bump product version to 7.6.3.2-android
[LibreOffice.git] / vcl / source / window / toolbox2.cxx
blob699080f9a530500a3c19b055ee79462e6cf2329a
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
21 #include <utility>
22 #include <vcl/uitest/logger.hxx>
23 #include <sal/log.hxx>
25 #include <comphelper/base64.hxx>
26 #include <comphelper/processfactory.hxx>
27 #include <boost/property_tree/ptree.hpp>
29 #include <vcl/cvtgrf.hxx>
30 #include <vcl/svapp.hxx>
31 #include <vcl/idle.hxx>
32 #include <vcl/bitmap.hxx>
33 #include <vcl/toolkit/floatwin.hxx>
34 #include <vcl/toolbox.hxx>
35 #include <vcl/mnemonic.hxx>
36 #include <vcl/menu.hxx>
37 #include <vcl/settings.hxx>
38 #include <vcl/IconThemeInfo.hxx>
39 #include <vcl/commandinfoprovider.hxx>
41 #include <svdata.hxx>
42 #include <brdwin.hxx>
43 #include <toolbox.h>
45 #include <unotools/confignode.hxx>
46 #include <tools/json_writer.hxx>
48 #include <vcl/uitest/uiobject.hxx>
50 #include "impldockingwrapper.hxx"
52 using namespace vcl;
54 #define TB_SEP_SIZE 8 // Separator size
57 ImplToolBoxPrivateData::ImplToolBoxPrivateData()
59 meButtonSize = ToolBoxButtonSize::DontCare;
60 mpMenu = VclPtr<PopupMenu>::Create();
62 maMenuType = ToolBoxMenuType::NONE;
63 maMenubuttonItem.maItemSize = Size( TB_MENUBUTTON_SIZE+TB_MENUBUTTON_OFFSET, TB_MENUBUTTON_SIZE+TB_MENUBUTTON_OFFSET );
64 maMenubuttonItem.meState = TRISTATE_FALSE;
65 mnMenuButtonWidth = TB_MENUBUTTON_SIZE;
67 mbIsLocked = false;
68 mbNativeButtons = false;
69 mbIsPaintLocked = false;
70 mbAssumeDocked = false;
71 mbAssumePopupMode = false;
72 mbAssumeFloating = false;
73 mbKeyInputDisabled = false;
74 mbMenubuttonSelected = false;
75 mbMenubuttonWasLastSelected = false;
76 mbWillUsePopupMode = false;
77 mbDropDownByKeyboard = false;
80 ImplToolBoxPrivateData::~ImplToolBoxPrivateData()
82 m_pLayoutData.reset();
83 mpMenu.disposeAndClear();
86 void ImplToolItem::init(ToolBoxItemId nItemId, ToolBoxItemBits nItemBits,
87 bool bEmptyBtn)
89 mnId = nItemId;
90 mpWindow = nullptr;
91 mbNonInteractiveWindow = false;
92 mpUserData = nullptr;
93 meType = ToolBoxItemType::BUTTON;
94 mnBits = nItemBits;
95 meState = TRISTATE_FALSE;
96 mbEnabled = true;
97 mbVisible = true;
98 mbEmptyBtn = bEmptyBtn;
99 mbShowWindow = false;
100 mbBreak = false;
101 mnSepSize = TB_SEP_SIZE;
102 mnDropDownArrowWidth = TB_DROPDOWNARROWWIDTH;
103 mnImageAngle = 0_deg10;
104 mbMirrorMode = false;
105 mbVisibleText = false;
106 mbExpand = false;
109 ImplToolItem::ImplToolItem()
111 init(ToolBoxItemId(0), ToolBoxItemBits::NONE, true);
114 ImplToolItem::ImplToolItem( ToolBoxItemId nItemId, Image aImage,
115 ToolBoxItemBits nItemBits ) :
116 maImage(std::move( aImage ))
118 init(nItemId, nItemBits, false);
121 ImplToolItem::ImplToolItem( ToolBoxItemId nItemId, OUString aText,
122 OUString aCommand, ToolBoxItemBits nItemBits ) :
123 maText(std::move( aText )),
124 maCommandStr(std::move( aCommand ))
126 init(nItemId, nItemBits, false);
129 ImplToolItem::ImplToolItem( ToolBoxItemId nItemId, Image aImage,
130 OUString aText, ToolBoxItemBits nItemBits ) :
131 maImage(std::move( aImage )),
132 maText(std::move( aText ))
134 init(nItemId, nItemBits, false);
137 Size ImplToolItem::GetSize( bool bHorz, bool bCheckMaxWidth, tools::Long maxWidth, const Size& rDefaultSize )
139 Size aSize( rDefaultSize ); // the size of 'standard' toolbox items
140 // non-standard items are eg windows or buttons with text
142 if ( (meType == ToolBoxItemType::BUTTON) || (meType == ToolBoxItemType::SPACE) )
144 aSize = maItemSize;
146 if ( mpWindow && bHorz )
148 // get size of item window and check if it fits
149 // no windows in vertical toolbars (the default is mbShowWindow=false)
150 Size aWinSize = mpWindow->GetSizePixel();
152 if (mpWindow->GetStyle() & WB_NOLABEL)
153 // Window wants no label? Then don't check width, it'll be just
154 // clipped.
155 bCheckMaxWidth = false;
157 if ( !bCheckMaxWidth || (aWinSize.Width() <= maxWidth) )
159 aSize.setWidth( aWinSize.Width() );
160 aSize.setHeight( aWinSize.Height() );
161 mbShowWindow = true;
163 else
165 if ( mbEmptyBtn )
167 aSize.setWidth( 0 );
168 aSize.setHeight( 0 );
173 else if ( meType == ToolBoxItemType::SEPARATOR )
175 if ( bHorz )
177 aSize.setWidth( mnSepSize );
178 aSize.setHeight( rDefaultSize.Height() );
180 else
182 aSize.setWidth( rDefaultSize.Width() );
183 aSize.setHeight( mnSepSize );
186 else if ( meType == ToolBoxItemType::BREAK )
188 aSize.setWidth( 0 );
189 aSize.setHeight( 0 );
192 return aSize;
195 void ImplToolItem::DetermineButtonDrawStyle( ButtonType eButtonType, bool& rbImage, bool& rbText ) const
197 if ( meType != ToolBoxItemType::BUTTON )
199 // no button -> draw nothing
200 rbImage = rbText = false;
201 return;
204 bool bHasImage;
205 bool bHasText;
207 // check for image and/or text
208 bHasImage = !!maImage;
209 bHasText = !maText.isEmpty();
211 // prefer images if symbolonly buttons are drawn
212 // prefer texts if textonly buttons are drawn
214 if ( eButtonType == ButtonType::SYMBOLONLY ) // drawing icons only
216 if( bHasImage || !bHasText )
218 rbImage = true;
219 rbText = false;
221 else
223 rbImage = false;
224 rbText = true;
227 else if ( eButtonType == ButtonType::TEXT ) // drawing text only
229 if( bHasText || !bHasImage )
231 rbImage = false;
232 rbText = true;
234 else
236 rbImage = true;
237 rbText = false;
240 else // drawing icons and text both
242 rbImage = true;
243 rbText = true;
247 tools::Rectangle ImplToolItem::GetDropDownRect( bool bHorz ) const
249 tools::Rectangle aRect;
250 if( (mnBits & ToolBoxItemBits::DROPDOWN) && !maRect.IsEmpty() )
252 aRect = maRect;
253 if( mbVisibleText && !bHorz )
254 // item will be rotated -> place dropdown to the bottom
255 aRect.SetTop( aRect.Bottom() - mnDropDownArrowWidth );
256 else
257 // place dropdown to the right
258 aRect.SetLeft( aRect.Right() - mnDropDownArrowWidth );
260 return aRect;
263 bool ImplToolItem::IsClipped() const
265 return ( meType == ToolBoxItemType::BUTTON && mbVisible && maRect.IsEmpty() );
268 bool ImplToolItem::IsItemHidden() const
270 return ( meType == ToolBoxItemType::BUTTON && !mbVisible );
273 void ToolBox::ImplInvalidate( bool bNewCalc, bool bFullPaint )
275 ImplUpdateInputEnable();
277 if ( bNewCalc )
278 mbCalc = true;
280 if ( bFullPaint )
282 mbFormat = true;
284 // do we need to redraw?
285 if ( IsReallyVisible() && IsUpdateMode() )
287 Invalidate( tools::Rectangle( mnLeftBorder, mnTopBorder,
288 mnDX-mnRightBorder-1, mnDY-mnBottomBorder-1 ) );
289 mpIdle->Stop();
292 else
294 if ( !mbFormat )
296 mbFormat = true;
298 // do we need to redraw?
299 if ( IsReallyVisible() && IsUpdateMode() )
300 mpIdle->Start();
304 // request new layout by layoutmanager
305 CallEventListeners( VclEventId::ToolboxFormatChanged );
308 void ToolBox::ImplUpdateItem( ImplToolItems::size_type nIndex )
310 // do we need to redraw?
311 if ( !(IsReallyVisible() && IsUpdateMode()) )
312 return;
314 if ( nIndex == ITEM_NOTFOUND )
316 // #i52217# no immediate draw as this might lead to paint problems
317 Invalidate( tools::Rectangle( mnLeftBorder, mnTopBorder, mnDX-mnRightBorder-1, mnDY-mnBottomBorder-1 ) );
319 else
321 if ( !mbFormat )
323 // #i52217# no immediate draw as this might lead to paint problems
324 Invalidate( mpData->m_aItems[nIndex].maRect );
326 else
327 maPaintRect.Union( mpData->m_aItems[nIndex].maRect );
331 void ToolBox::Click()
333 CallEventListeners( VclEventId::ToolboxClick );
334 maClickHdl.Call( this );
335 UITestLogger::getInstance().logAction( this, VclEventId::ToolboxClick);
338 void ToolBox::DoubleClick()
340 CallEventListeners( VclEventId::ToolboxDoubleClick );
341 maDoubleClickHdl.Call( this );
344 void ToolBox::Activate()
346 mnActivateCount++;
347 CallEventListeners( VclEventId::ToolboxActivate );
348 maActivateHdl.Call( this );
351 void ToolBox::Deactivate()
353 mnActivateCount--;
354 CallEventListeners( VclEventId::ToolboxDeactivate );
355 maDeactivateHdl.Call( this );
358 void ToolBox::Highlight()
360 CallEventListeners( VclEventId::ToolboxHighlight );
363 FactoryFunction ToolBox::GetUITestFactory() const
365 return ToolBoxUIObject::create;
368 void ToolBox::Select()
370 VclPtr<vcl::Window> xWindow = this;
372 CallEventListeners( VclEventId::ToolboxSelect );
373 maSelectHdl.Call( this );
375 if ( xWindow->isDisposed() )
376 return;
378 // TODO: GetFloatingWindow in DockingWindow is currently inline, change it to check dockingwrapper
379 ImplDockingWindowWrapper *pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper( this );
380 if( pWrapper && pWrapper->GetFloatingWindow() && static_cast<FloatingWindow*>(pWrapper->GetFloatingWindow())->IsInPopupMode() )
381 static_cast<FloatingWindow*>(pWrapper->GetFloatingWindow())->EndPopupMode();
384 void ToolBox::InsertItem( ToolBoxItemId nItemId, const Image& rImage, ToolBoxItemBits nBits, ImplToolItems::size_type nPos )
386 SAL_WARN_IF( !nItemId, "vcl", "ToolBox::InsertItem(): ItemId == 0" );
387 SAL_WARN_IF( GetItemPos( nItemId ) != ITEM_NOTFOUND, "vcl",
388 "ToolBox::InsertItem(): ItemId already exists" );
390 // create item and add to list
391 mpData->m_aItems.insert( (nPos < mpData->m_aItems.size()) ? mpData->m_aItems.begin()+nPos : mpData->m_aItems.end(),
392 ImplToolItem( nItemId, rImage, nBits ) );
393 mpData->ImplClearLayoutData();
395 ImplInvalidate( true );
397 // Notify
398 ImplToolItems::size_type nNewPos = ( nPos == APPEND ) ? ( mpData->m_aItems.size() - 1 ) : nPos;
399 CallEventListeners( VclEventId::ToolboxItemAdded, reinterpret_cast< void* >(nNewPos ) );
402 void ToolBox::InsertItem( ToolBoxItemId nItemId, const Image& rImage, const OUString& rText, ToolBoxItemBits nBits,
403 ImplToolItems::size_type nPos )
405 SAL_WARN_IF( !nItemId, "vcl", "ToolBox::InsertItem(): ItemId == 0" );
406 SAL_WARN_IF( GetItemPos( nItemId ) != ITEM_NOTFOUND, "vcl",
407 "ToolBox::InsertItem(): ItemId already exists" );
409 // create item and add to list
410 mpData->m_aItems.insert( (nPos < mpData->m_aItems.size()) ? mpData->m_aItems.begin()+nPos : mpData->m_aItems.end(),
411 ImplToolItem( nItemId, rImage, MnemonicGenerator::EraseAllMnemonicChars(rText), nBits ) );
412 mpData->ImplClearLayoutData();
414 ImplInvalidate( true );
416 // Notify
417 ImplToolItems::size_type nNewPos = ( nPos == APPEND ) ? ( mpData->m_aItems.size() - 1 ) : nPos;
418 CallEventListeners( VclEventId::ToolboxItemAdded, reinterpret_cast< void* >( nNewPos ) );
421 void ToolBox::InsertItem( ToolBoxItemId nItemId, const OUString& rText, const OUString& rCommand, ToolBoxItemBits nBits,
422 ImplToolItems::size_type nPos )
424 SAL_WARN_IF( !nItemId, "vcl", "ToolBox::InsertItem(): ItemId == 0" );
425 SAL_WARN_IF( GetItemPos( nItemId ) != ITEM_NOTFOUND, "vcl",
426 "ToolBox::InsertItem(): ItemId already exists" );
428 // create item and add to list
429 mpData->m_aItems.insert( (nPos < mpData->m_aItems.size()) ? mpData->m_aItems.begin()+nPos : mpData->m_aItems.end(),
430 ImplToolItem( nItemId, MnemonicGenerator::EraseAllMnemonicChars(rText), rCommand, nBits ) );
431 mpData->ImplClearLayoutData();
433 ImplInvalidate( true );
435 // Notify
436 ImplToolItems::size_type nNewPos = ( nPos == APPEND ) ? ( mpData->m_aItems.size() - 1 ) : nPos;
437 CallEventListeners( VclEventId::ToolboxItemAdded, reinterpret_cast< void* >( nNewPos ) );
440 void ToolBox::InsertItem(const OUString& rCommand, const css::uno::Reference<css::frame::XFrame>& rFrame, ToolBoxItemBits nBits,
441 const Size& rRequestedSize, ImplToolItems::size_type nPos)
443 OUString aModuleName(vcl::CommandInfoProvider::GetModuleIdentifier(rFrame));
444 auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(rCommand, aModuleName);
445 OUString aLabel(vcl::CommandInfoProvider::GetLabelForCommand(aProperties));
446 OUString aTooltip(vcl::CommandInfoProvider::GetTooltipForCommand(rCommand, aProperties, rFrame));
447 Image aImage(CommandInfoProvider::GetImageForCommand(rCommand, rFrame, GetImageSize()));
449 ToolBoxItemId nItemId(GetItemCount() + 1);
450 //TODO: ImplToolItems::size_type -> sal_uInt16!
451 InsertItem(nItemId, aLabel, rCommand, nBits, nPos);
452 SetItemImage(nItemId, aImage);
453 SetQuickHelpText(nItemId, aTooltip);
455 // set the minimal size
456 ImplToolItem* pItem = ImplGetItem( nItemId );
457 if ( pItem )
458 pItem->maMinimalItemSize = rRequestedSize;
461 void ToolBox::InsertWindow( ToolBoxItemId nItemId, vcl::Window* pWindow,
462 ToolBoxItemBits nBits, ImplToolItems::size_type nPos )
464 SAL_WARN_IF( !nItemId, "vcl", "ToolBox::InsertWindow(): ItemId == 0" );
465 SAL_WARN_IF( GetItemPos( nItemId ) != ITEM_NOTFOUND, "vcl",
466 "ToolBox::InsertWindow(): ItemId already exists" );
468 // create item and add to list
469 ImplToolItem aItem;
470 aItem.mnId = nItemId;
471 aItem.meType = ToolBoxItemType::BUTTON;
472 aItem.mnBits = nBits;
473 aItem.mpWindow = pWindow;
474 mpData->m_aItems.insert( (nPos < mpData->m_aItems.size()) ? mpData->m_aItems.begin()+nPos : mpData->m_aItems.end(), aItem );
475 mpData->ImplClearLayoutData();
477 if ( pWindow )
478 pWindow->Hide();
480 ImplInvalidate( true );
482 // Notify
483 ImplToolItems::size_type nNewPos = ( nPos == APPEND ) ? ( mpData->m_aItems.size() - 1 ) : nPos;
484 CallEventListeners( VclEventId::ToolboxItemAdded, reinterpret_cast< void* >( nNewPos ) );
487 void ToolBox::InsertSpace()
489 // create item and add to list
490 ImplToolItem aItem;
491 aItem.meType = ToolBoxItemType::SPACE;
492 aItem.mbEnabled = false;
493 mpData->m_aItems.push_back( aItem );
494 mpData->ImplClearLayoutData();
496 ImplInvalidate();
498 // Notify
499 ImplToolItems::size_type nNewPos = mpData->m_aItems.size() - 1;
500 CallEventListeners( VclEventId::ToolboxItemAdded, reinterpret_cast< void* >( nNewPos ) );
503 void ToolBox::InsertSeparator( ImplToolItems::size_type nPos, sal_uInt16 nPixSize )
505 // create item and add to list
506 ImplToolItem aItem;
507 aItem.meType = ToolBoxItemType::SEPARATOR;
508 aItem.mbEnabled = false;
509 if ( nPixSize )
510 aItem.mnSepSize = nPixSize;
511 mpData->m_aItems.insert( (nPos < mpData->m_aItems.size()) ? mpData->m_aItems.begin()+nPos : mpData->m_aItems.end(), aItem );
512 mpData->ImplClearLayoutData();
514 ImplInvalidate();
516 // Notify
517 ImplToolItems::size_type nNewPos = ( nPos == APPEND ) ? ( mpData->m_aItems.size() - 1 ) : nPos;
518 CallEventListeners( VclEventId::ToolboxItemAdded, reinterpret_cast< void* >( nNewPos ) );
521 void ToolBox::InsertBreak( ImplToolItems::size_type nPos )
523 // create item and add to list
524 ImplToolItem aItem;
525 aItem.meType = ToolBoxItemType::BREAK;
526 aItem.mbEnabled = false;
527 mpData->m_aItems.insert( (nPos < mpData->m_aItems.size()) ? mpData->m_aItems.begin()+nPos : mpData->m_aItems.end(), aItem );
528 mpData->ImplClearLayoutData();
530 ImplInvalidate();
532 // Notify
533 ImplToolItems::size_type nNewPos = ( nPos == APPEND ) ? ( mpData->m_aItems.size() - 1 ) : nPos;
534 CallEventListeners( VclEventId::ToolboxItemAdded, reinterpret_cast< void* >( nNewPos ) );
537 void ToolBox::RemoveItem( ImplToolItems::size_type nPos )
539 if( nPos >= mpData->m_aItems.size() )
540 return;
542 bool bMustCalc;
543 bMustCalc = mpData->m_aItems[nPos].meType == ToolBoxItemType::BUTTON;
545 if ( mpData->m_aItems[nPos].mpWindow )
546 mpData->m_aItems[nPos].mpWindow->Hide();
548 // add the removed item to PaintRect
549 maPaintRect.Union( mpData->m_aItems[nPos].maRect );
551 // ensure not to delete in the Select-Handler
552 if ( mpData->m_aItems[nPos].mnId == mnCurItemId )
553 mnCurItemId = ToolBoxItemId(0);
554 if ( mpData->m_aItems[nPos].mnId == mnHighItemId )
555 mnHighItemId = ToolBoxItemId(0);
557 ImplInvalidate( bMustCalc );
559 mpData->m_aItems.erase( mpData->m_aItems.begin()+nPos );
560 mpData->ImplClearLayoutData();
562 // Notify
563 CallEventListeners( VclEventId::ToolboxItemRemoved, reinterpret_cast< void* >( nPos ) );
566 void ToolBox::CopyItem( const ToolBox& rToolBox, ToolBoxItemId nItemId )
568 SAL_WARN_IF( GetItemPos( nItemId ) != ITEM_NOTFOUND, "vcl",
569 "ToolBox::CopyItem(): ItemId already exists" );
571 ImplToolItems::size_type nPos = rToolBox.GetItemPos( nItemId );
573 // found item
574 if ( nPos == ITEM_NOTFOUND )
575 return;
577 // push ToolBox item onto the list
578 ImplToolItem aNewItem = rToolBox.mpData->m_aItems[nPos];
579 // reset state
580 aNewItem.mpWindow = nullptr;
581 aNewItem.mbShowWindow = false;
583 mpData->m_aItems.push_back( aNewItem );
584 mpData->ImplClearLayoutData();
585 // redraw ToolBox
586 ImplInvalidate();
588 // Notify
589 ImplToolItems::size_type nNewPos2 = mpData->m_aItems.size() - 1;
590 CallEventListeners( VclEventId::ToolboxItemAdded, reinterpret_cast< void* >( nNewPos2 ) );
593 void ToolBox::Clear()
595 mpData->m_aItems.clear();
596 mpData->ImplClearLayoutData();
598 // ensure not to delete in the Select-Handler
599 mnCurItemId = ToolBoxItemId(0);
600 mnHighItemId = ToolBoxItemId(0);
602 ImplInvalidate( true, true );
604 // Notify
605 CallEventListeners( VclEventId::ToolboxAllItemsChanged );
608 void ToolBox::SetButtonType( ButtonType eNewType )
610 if ( meButtonType != eNewType )
612 meButtonType = eNewType;
614 // better redraw everything, as otherwise there might be problems
615 // with regions that were copied with CopyBits
616 ImplInvalidate( true );
620 void ToolBox::SetToolboxButtonSize( ToolBoxButtonSize eSize )
622 if( mpData->meButtonSize != eSize )
624 mpData->meButtonSize = eSize;
625 mbCalc = true;
626 mbFormat = true;
630 ToolBoxButtonSize ToolBox::GetToolboxButtonSize() const
632 return mpData->meButtonSize;
635 ImageType ToolBox::GetImageSize() const
637 ImageType eImageType = ImageType::Size16;
638 if (mpData->meButtonSize == ToolBoxButtonSize::Large)
639 eImageType = ImageType::Size26;
640 else if (mpData->meButtonSize == ToolBoxButtonSize::Size32)
641 eImageType = ImageType::Size32;
643 return eImageType;
646 /*static*/ Size ToolBox::GetDefaultImageSize(ToolBoxButtonSize eToolBoxButtonSize)
648 OutputDevice *pDefault = Application::GetDefaultDevice();
649 float fScaleFactor = pDefault ? pDefault->GetDPIScaleFactor() : 1.0;
651 Size aUnscaledSize(16, 16);
653 if (eToolBoxButtonSize == ToolBoxButtonSize::Large)
655 OUString iconTheme = Application::GetSettings().GetStyleSettings().DetermineIconTheme();
656 aUnscaledSize = vcl::IconThemeInfo::SizeByThemeName(iconTheme);
658 else if (eToolBoxButtonSize == ToolBoxButtonSize::Size32)
660 aUnscaledSize = Size(32, 32);
662 return Size(aUnscaledSize.Width() * fScaleFactor,
663 aUnscaledSize.Height() * fScaleFactor);
666 Size ToolBox::GetDefaultImageSize() const
668 return GetDefaultImageSize(GetToolboxButtonSize());
671 void ToolBox::SetAlign( WindowAlign eNewAlign )
673 if ( meAlign == eNewAlign )
674 return;
676 meAlign = eNewAlign;
678 if ( ImplIsFloatingMode() )
679 return;
681 // set horizontal/vertical alignment
682 if ( (eNewAlign == WindowAlign::Left) || (eNewAlign == WindowAlign::Right) )
683 mbHorz = false;
684 else
685 mbHorz = true;
687 // Update the background according to Persona if necessary
688 ImplInitSettings( false, false, true );
690 // redraw everything, as the border has changed
691 mbCalc = true;
692 mbFormat = true;
693 if ( IsReallyVisible() && IsUpdateMode() )
694 Invalidate();
697 void ToolBox::SetLineCount( ImplToolItems::size_type nNewLines )
699 if ( !nNewLines )
700 nNewLines = 1;
702 if ( mnLines != nNewLines )
704 mnLines = nNewLines;
706 // better redraw everything, as otherwise there might be problems
707 // with regions that were copied with CopyBits
708 Invalidate();
712 ToolBox::ImplToolItems::size_type ToolBox::GetItemCount() const
714 return mpData ? mpData->m_aItems.size() : 0;
717 ToolBoxItemType ToolBox::GetItemType( ImplToolItems::size_type nPos ) const
719 return (nPos < mpData->m_aItems.size()) ? mpData->m_aItems[nPos].meType : ToolBoxItemType::DONTKNOW;
722 ToolBox::ImplToolItems::size_type ToolBox::GetItemPos( ToolBoxItemId nItemId ) const
724 if (mpData)
726 ImplToolItems::size_type nCount = mpData->m_aItems.size();
727 for( ImplToolItems::size_type nPos = 0; nPos < nCount; nPos++ )
728 if( mpData->m_aItems[nPos].mnId == nItemId )
729 return nPos;
731 return ITEM_NOTFOUND;
734 ToolBox::ImplToolItems::size_type ToolBox::GetItemPos( const Point& rPos ) const
736 // search the item position on the given point
737 auto it = std::find_if(mpData->m_aItems.begin(), mpData->m_aItems.end(),
738 [&rPos](const ImplToolItem& rItem) { return rItem.maRect.Contains( rPos ); });
740 if( it != mpData->m_aItems.end() )
741 return std::distance(mpData->m_aItems.begin(), it);
743 return ITEM_NOTFOUND;
746 ToolBoxItemId ToolBox::GetItemId( ImplToolItems::size_type nPos ) const
748 return (nPos < mpData->m_aItems.size()) ? mpData->m_aItems[nPos].mnId : ToolBoxItemId(0);
751 ToolBoxItemId ToolBox::GetItemId( const Point& rPos ) const
753 // find item that was clicked
754 auto it = std::find_if(mpData->m_aItems.begin(), mpData->m_aItems.end(),
755 [&rPos](const ImplToolItem& rItem) { return rItem.maRect.Contains( rPos ); });
757 if( (it != mpData->m_aItems.end()) && (it->meType == ToolBoxItemType::BUTTON) )
758 return it->mnId;
760 return ToolBoxItemId(0);
763 Size ToolBox::GetItemContentSize( ToolBoxItemId nItemId )
765 if ( mbCalc || mbFormat )
766 ImplFormat();
768 ImplToolItems::size_type nPos = GetItemPos( nItemId );
769 if ( nPos < mpData->m_aItems.size() )
770 return mpData->m_aItems[nPos].maContentSize;
771 else
772 return Size();
775 ToolBoxItemId ToolBox::GetItemId(const OUString &rCommand) const
777 if (!mpData)
778 return ToolBoxItemId(0);
780 auto it = std::find_if(mpData->m_aItems.begin(), mpData->m_aItems.end(),
781 [&rCommand](const ImplToolItem& rItem) { return rItem.maCommandStr == rCommand; });
782 if (it != mpData->m_aItems.end())
783 return it->mnId;
785 return ToolBoxItemId(0);
788 Point ToolBox::ImplGetPopupPosition( const tools::Rectangle& rRect ) const
790 Point aPos;
791 if( !rRect.IsEmpty() )
793 tools::Rectangle aScreen = GetDesktopRectPixel();
795 // the popup should be positioned so that it will not cover
796 // the item rect and that it fits the desktop
797 // the preferred direction is always towards the center of
798 // the application window
800 Point devPos; // the position in device coordinates for screen comparison
801 switch( meAlign )
803 case WindowAlign::Top:
804 aPos = rRect.BottomLeft();
805 aPos.AdjustY( 1 );
806 devPos = OutputToAbsoluteScreenPixel( aPos );
807 if( devPos.Y() >= aScreen.Bottom() )
808 aPos.setY( rRect.Top() );
809 break;
810 case WindowAlign::Bottom:
811 aPos = rRect.TopLeft();
812 aPos.AdjustY( -1 );
813 devPos = OutputToAbsoluteScreenPixel( aPos );
814 if( devPos.Y() <= aScreen.Top() )
815 aPos.setY( rRect.Bottom() );
816 break;
817 case WindowAlign::Left:
818 aPos = rRect.TopRight();
819 aPos.AdjustX( 1 );
820 devPos = OutputToAbsoluteScreenPixel( aPos );
821 if( devPos.X() >= aScreen.Right() )
822 aPos.setX( rRect.Left() );
823 break;
824 case WindowAlign::Right:
825 aPos = rRect.TopLeft();
826 aPos.AdjustX( -1 );
827 devPos = OutputToAbsoluteScreenPixel( aPos );
828 if( devPos.X() <= aScreen.Left() )
829 aPos.setX( rRect.Right() );
830 break;
831 default:
832 break;
835 return aPos;
838 tools::Rectangle ToolBox::GetItemRect( ToolBoxItemId nItemId )
840 if ( mbCalc || mbFormat )
841 ImplFormat();
843 ImplToolItems::size_type nPos = GetItemPos( nItemId );
844 return GetItemPosRect( nPos );
847 tools::Rectangle ToolBox::GetItemPosRect( ImplToolItems::size_type nPos )
849 if ( mbCalc || mbFormat )
850 ImplFormat();
852 if ( nPos < mpData->m_aItems.size() )
853 return mpData->m_aItems[nPos].maRect;
854 else
855 return tools::Rectangle();
858 tools::Rectangle const & ToolBox::GetOverflowRect() const
860 return mpData->maMenubuttonItem.maRect;
863 bool ToolBox::ImplHasExternalMenubutton() const
865 // check if the borderwindow (i.e. the decoration) provides the menu button
866 bool bRet = false;
867 if( ImplIsFloatingMode() )
869 // custom menu is placed in the decoration
870 ImplBorderWindow *pBorderWin = dynamic_cast<ImplBorderWindow*>( GetWindow( GetWindowType::Border ) );
871 if( pBorderWin && !pBorderWin->GetMenuRect().IsEmpty() )
872 bRet = true;
874 return bRet;
877 void ToolBox::SetItemBits( ToolBoxItemId nItemId, ToolBoxItemBits nBits )
879 ImplToolItems::size_type nPos = GetItemPos( nItemId );
881 if ( nPos < GetItemCount() )
883 ToolBoxItemBits nOldBits = mpData->m_aItems[nPos].mnBits;
884 mpData->m_aItems[nPos].mnBits = nBits;
885 nBits &= ToolBoxItemBits::LEFT | ToolBoxItemBits::AUTOSIZE | ToolBoxItemBits::DROPDOWN;
886 nOldBits &= ToolBoxItemBits::LEFT | ToolBoxItemBits::AUTOSIZE | ToolBoxItemBits::DROPDOWN;
887 // trigger reformat when the item width has changed (dropdown arrow)
888 bool bFormat = ToolBoxItemBits(nBits & ToolBoxItemBits::DROPDOWN) != ToolBoxItemBits(nOldBits & ToolBoxItemBits::DROPDOWN);
889 if ( nBits != nOldBits )
890 ImplInvalidate( true, bFormat );
894 void ToolBox::SetItemWindowNonInteractive(ToolBoxItemId nItemId, bool bNonInteractive)
896 ImplToolItems::size_type nPos = GetItemPos( nItemId );
898 if ( nPos < GetItemCount() )
900 mpData->m_aItems[nPos].mbNonInteractiveWindow = bNonInteractive;
904 ToolBoxItemBits ToolBox::GetItemBits( ToolBoxItemId nItemId ) const
906 ImplToolItem* pItem = ImplGetItem( nItemId );
908 if ( pItem )
909 return pItem->mnBits;
910 else
911 return ToolBoxItemBits::NONE;
914 void ToolBox::SetItemExpand( ToolBoxItemId nItemId, bool bExpand )
916 ImplToolItem* pItem = ImplGetItem( nItemId );
917 if (!pItem)
918 return;
920 if (pItem->mbExpand != bExpand)
922 pItem->mbExpand = bExpand;
923 ImplInvalidate(true, true);
927 void ToolBox::SetItemData( ToolBoxItemId nItemId, void* pNewData )
929 ImplToolItems::size_type nPos = GetItemPos( nItemId );
931 if ( nPos < mpData->m_aItems.size() )
933 mpData->m_aItems[nPos].mpUserData = pNewData;
934 ImplUpdateItem( nPos );
938 void* ToolBox::GetItemData( ToolBoxItemId nItemId ) const
940 ImplToolItem* pItem = ImplGetItem( nItemId );
942 if ( pItem )
943 return pItem->mpUserData;
944 else
945 return nullptr;
948 static Image ImplMirrorImage( const Image& rImage )
950 BitmapEx aMirrBitmapEx( rImage.GetBitmapEx() );
952 aMirrBitmapEx.Mirror( BmpMirrorFlags::Horizontal );
954 return Image( aMirrBitmapEx );
957 static Image ImplRotImage( const Image& rImage, Degree10 nAngle10 )
959 BitmapEx aRotBitmapEx( rImage.GetBitmapEx() );
961 aRotBitmapEx.Rotate( nAngle10, COL_WHITE );
963 return Image( aRotBitmapEx );
966 void ToolBox::SetItemImage( ToolBoxItemId nItemId, const Image& rImage )
968 ImplToolItems::size_type nPos = GetItemPos( nItemId );
970 if ( nPos == ITEM_NOTFOUND )
971 return;
973 ImplToolItem* pItem = &mpData->m_aItems[nPos];
974 Size aOldSize = pItem->maImage.GetSizePixel();
976 pItem->maImage = pItem->mbMirrorMode ? ImplMirrorImage(rImage) : rImage;
977 if (pItem->mnImageAngle != 0_deg10)
978 pItem->maImage = ImplRotImage(pItem->maImage, pItem->mnImageAngle);
980 // only once all is calculated, do extra work
981 if (!mbCalc)
983 if (aOldSize != pItem->maImage.GetSizePixel())
984 ImplInvalidate( true );
985 else
986 ImplUpdateItem( nPos );
990 void ToolBox::SetItemImageAngle( ToolBoxItemId nItemId, Degree10 nAngle10 )
992 ImplToolItems::size_type nPos = GetItemPos( nItemId );
994 if ( nPos == ITEM_NOTFOUND )
995 return;
997 ImplToolItem* pItem = &mpData->m_aItems[nPos];
998 pItem->mnImageAngle = nAngle10;
1001 void ToolBox::SetItemImageMirrorMode( ToolBoxItemId nItemId, bool bMirror )
1003 ImplToolItems::size_type nPos = GetItemPos( nItemId );
1005 if ( nPos == ITEM_NOTFOUND )
1006 return;
1008 ImplToolItem* pItem = &mpData->m_aItems[nPos];
1009 pItem->mbMirrorMode = bMirror;
1012 Image ToolBox::GetItemImage(ToolBoxItemId nItemId) const
1014 ImplToolItem* pItem = ImplGetItem(nItemId);
1015 return pItem ? pItem->maImage : Image();
1018 void ToolBox::SetItemText( ToolBoxItemId nItemId, const OUString& rText )
1020 ImplToolItems::size_type nPos = GetItemPos( nItemId );
1022 if ( nPos == ITEM_NOTFOUND )
1023 return;
1025 ImplToolItem* pItem = &mpData->m_aItems[nPos];
1026 // only once all is calculated, do extra work
1027 if ( !mbCalc &&
1028 ((meButtonType != ButtonType::SYMBOLONLY) || !pItem->maImage) )
1030 tools::Long nOldWidth = GetOutDev()->GetCtrlTextWidth( pItem->maText );
1031 pItem->maText = MnemonicGenerator::EraseAllMnemonicChars(rText);
1032 mpData->ImplClearLayoutData();
1033 if ( nOldWidth != GetOutDev()->GetCtrlTextWidth( pItem->maText ) )
1034 ImplInvalidate( true );
1035 else
1036 ImplUpdateItem( nPos );
1038 else
1039 pItem->maText = MnemonicGenerator::EraseAllMnemonicChars(rText);
1041 // Notify button changed event to prepare accessibility bridge
1042 CallEventListeners( VclEventId::ToolboxButtonStateChanged, reinterpret_cast< void* >( nPos ) );
1044 // Notify
1045 CallEventListeners( VclEventId::ToolboxItemTextChanged, reinterpret_cast< void* >( nPos ) );
1048 const OUString& ToolBox::GetItemText( ToolBoxItemId nItemId ) const
1051 ImplToolItem* pItem = ImplGetItem( nItemId );
1053 assert( pItem );
1055 return pItem->maText;
1058 void ToolBox::SetItemWindow( ToolBoxItemId nItemId, vcl::Window* pNewWindow )
1060 ImplToolItems::size_type nPos = GetItemPos( nItemId );
1062 if ( nPos != ITEM_NOTFOUND )
1064 ImplToolItem* pItem = &mpData->m_aItems[nPos];
1065 pItem->mpWindow = pNewWindow;
1066 if ( pNewWindow )
1067 pNewWindow->Hide();
1068 ImplInvalidate( true );
1069 CallEventListeners( VclEventId::ToolboxItemWindowChanged, reinterpret_cast< void* >( nPos ) );
1073 vcl::Window* ToolBox::GetItemWindow( ToolBoxItemId nItemId ) const
1075 ImplToolItem* pItem = ImplGetItem( nItemId );
1077 if ( pItem )
1078 return pItem->mpWindow;
1079 else
1080 return nullptr;
1083 void ToolBox::EndSelection()
1085 if ( mbDrag )
1087 // reset
1088 mbDrag = false;
1089 if (mnCurPos != ITEM_NOTFOUND)
1090 InvalidateItem(mnCurPos);
1091 EndTracking();
1092 if (IsMouseCaptured())
1093 ReleaseMouse();
1094 Deactivate();
1097 mnCurPos = ITEM_NOTFOUND;
1098 mnCurItemId = ToolBoxItemId(0);
1099 mnDownItemId = ToolBoxItemId(0);
1100 mnMouseModifier = 0;
1103 void ToolBox::SetItemDown( ToolBoxItemId nItemId, bool bDown )
1105 ImplToolItems::size_type nPos = GetItemPos( nItemId );
1107 if ( nPos == ITEM_NOTFOUND )
1108 return;
1110 if ( bDown )
1112 if ( nPos != mnCurPos )
1114 mnCurPos = nPos;
1115 InvalidateItem(mnCurPos);
1116 GetOutDev()->Flush();
1119 else
1121 if ( nPos == mnCurPos )
1123 InvalidateItem(mnCurPos);
1124 GetOutDev()->Flush();
1125 mnCurPos = ITEM_NOTFOUND;
1129 if ( mbDrag )
1131 mbDrag = false;
1132 EndTracking();
1133 if (IsMouseCaptured())
1134 ReleaseMouse();
1135 Deactivate();
1138 mnCurItemId = ToolBoxItemId(0);
1139 mnDownItemId = ToolBoxItemId(0);
1140 mnMouseModifier = 0;
1143 void ToolBox::SetItemState( ToolBoxItemId nItemId, TriState eState )
1145 ImplToolItems::size_type nPos = GetItemPos( nItemId );
1147 if ( nPos == ITEM_NOTFOUND )
1148 return;
1150 ImplToolItem* pItem = &mpData->m_aItems[nPos];
1152 // the state has changed
1153 if ( pItem->meState == eState )
1154 return;
1156 // if RadioCheck, un-check the previous
1157 if ( (eState == TRISTATE_TRUE) && (pItem->mnBits & ToolBoxItemBits::AUTOCHECK) &&
1158 (pItem->mnBits & ToolBoxItemBits::RADIOCHECK) )
1160 ImplToolItem* pGroupItem;
1161 ImplToolItems::size_type nGroupPos;
1162 ImplToolItems::size_type nItemCount = GetItemCount();
1164 nGroupPos = nPos;
1165 while ( nGroupPos )
1167 pGroupItem = &mpData->m_aItems[nGroupPos-1];
1168 if ( pGroupItem->mnBits & ToolBoxItemBits::RADIOCHECK )
1170 if ( pGroupItem->meState != TRISTATE_FALSE )
1171 SetItemState( pGroupItem->mnId, TRISTATE_FALSE );
1173 else
1174 break;
1175 nGroupPos--;
1178 nGroupPos = nPos+1;
1179 while ( nGroupPos < nItemCount )
1181 pGroupItem = &mpData->m_aItems[nGroupPos];
1182 if ( pGroupItem->mnBits & ToolBoxItemBits::RADIOCHECK )
1184 if ( pGroupItem->meState != TRISTATE_FALSE )
1185 SetItemState( pGroupItem->mnId, TRISTATE_FALSE );
1187 else
1188 break;
1189 nGroupPos++;
1193 pItem->meState = eState;
1194 ImplUpdateItem( nPos );
1196 // Notify button changed event to prepare accessibility bridge
1197 CallEventListeners( VclEventId::ToolboxButtonStateChanged, reinterpret_cast< void* >( nPos ) );
1199 // Call accessible listener to notify state_changed event
1200 CallEventListeners( VclEventId::ToolboxItemUpdated, reinterpret_cast< void* >(nPos) );
1203 TriState ToolBox::GetItemState( ToolBoxItemId nItemId ) const
1205 ImplToolItem* pItem = ImplGetItem( nItemId );
1207 if ( pItem )
1208 return pItem->meState;
1209 else
1210 return TRISTATE_FALSE;
1213 void ToolBox::EnableItem( ToolBoxItemId nItemId, bool bEnable )
1215 ImplToolItems::size_type nPos = GetItemPos( nItemId );
1217 if ( nPos == ITEM_NOTFOUND )
1218 return;
1220 ImplToolItem* pItem = &mpData->m_aItems[nPos];
1221 if ( pItem->mbEnabled == bEnable )
1222 return;
1224 pItem->mbEnabled = bEnable;
1226 // if existing, also redraw the window
1227 if ( pItem->mpWindow )
1228 pItem->mpWindow->Enable( pItem->mbEnabled );
1230 // update item
1231 ImplUpdateItem( nPos );
1233 ImplUpdateInputEnable();
1235 // Notify button changed event to prepare accessibility bridge
1236 CallEventListeners( VclEventId::ToolboxButtonStateChanged, reinterpret_cast< void* >( nPos ) );
1238 CallEventListeners( bEnable ? VclEventId::ToolboxItemEnabled : VclEventId::ToolboxItemDisabled, reinterpret_cast< void* >( nPos ) );
1241 bool ToolBox::IsItemEnabled( ToolBoxItemId nItemId ) const
1243 ImplToolItem* pItem = ImplGetItem( nItemId );
1245 if ( pItem )
1246 return pItem->mbEnabled;
1247 else
1248 return false;
1251 void ToolBox::ShowItem( ToolBoxItemId nItemId, bool bVisible )
1253 ImplToolItems::size_type nPos = GetItemPos( nItemId );
1254 mpData->ImplClearLayoutData();
1256 if ( nPos != ITEM_NOTFOUND )
1258 ImplToolItem* pItem = &mpData->m_aItems[nPos];
1259 if ( pItem->mbVisible != bVisible )
1261 pItem->mbVisible = bVisible;
1262 ImplInvalidate();
1267 bool ToolBox::IsItemClipped( ToolBoxItemId nItemId ) const
1269 ImplToolItem* pItem = ImplGetItem( nItemId );
1271 if ( pItem )
1272 return pItem->IsClipped();
1273 else
1274 return false;
1277 bool ToolBox::IsItemVisible( ToolBoxItemId nItemId ) const
1279 ImplToolItem* pItem = ImplGetItem( nItemId );
1281 if ( pItem )
1282 return pItem->mbVisible;
1283 else
1284 return false;
1287 bool ToolBox::IsItemReallyVisible( ToolBoxItemId nItemId ) const
1289 // is the item on the visible area of the toolbox?
1290 bool bRet = false;
1291 tools::Rectangle aRect( mnLeftBorder, mnTopBorder, mnDX-mnRightBorder, mnDY-mnBottomBorder );
1292 ImplToolItem* pItem = ImplGetItem( nItemId );
1294 if ( pItem && pItem->mbVisible &&
1295 !pItem->maRect.IsEmpty() && aRect.Overlaps( pItem->maRect ) )
1297 bRet = true;
1300 return bRet;
1303 void ToolBox::SetItemCommand(ToolBoxItemId nItemId, const OUString& rCommand)
1305 ImplToolItem* pItem = ImplGetItem( nItemId );
1307 if (pItem)
1308 pItem->maCommandStr = rCommand;
1311 OUString ToolBox::GetItemCommand( ToolBoxItemId nItemId ) const
1313 ImplToolItem* pItem = ImplGetItem( nItemId );
1315 if (pItem)
1316 return pItem->maCommandStr;
1318 return OUString();
1321 void ToolBox::SetQuickHelpText( ToolBoxItemId nItemId, const OUString& rText )
1323 ImplToolItem* pItem = ImplGetItem( nItemId );
1325 if ( pItem )
1326 pItem->maQuickHelpText = rText;
1329 OUString ToolBox::GetQuickHelpText( ToolBoxItemId nItemId ) const
1331 ImplToolItem* pItem = ImplGetItem( nItemId );
1333 if ( pItem )
1334 return pItem->maQuickHelpText;
1335 else
1336 return OUString();
1339 void ToolBox::SetHelpText( ToolBoxItemId nItemId, const OUString& rText )
1341 ImplToolItem* pItem = ImplGetItem( nItemId );
1343 if ( pItem )
1344 pItem->maHelpText = rText;
1347 const OUString& ToolBox::GetHelpText( ToolBoxItemId nItemId ) const
1349 return ImplGetHelpText( nItemId );
1352 void ToolBox::SetHelpId( ToolBoxItemId nItemId, const OUString& rHelpId )
1354 ImplToolItem* pItem = ImplGetItem( nItemId );
1356 if ( pItem )
1357 pItem->maHelpId = rHelpId;
1360 // disable key input if all items are disabled
1361 void ToolBox::ImplUpdateInputEnable()
1363 mpData->mbKeyInputDisabled = std::none_of(mpData->m_aItems.begin(), mpData->m_aItems.end(),
1364 [](const ImplToolItem& rItem) {
1365 // at least one useful entry
1366 return rItem.mbEnabled;
1370 void ToolBox::ImplFillLayoutData()
1372 mpData->m_pLayoutData.emplace();
1374 ImplToolItems::size_type nCount = mpData->m_aItems.size();
1375 for( ImplToolItems::size_type i = 0; i < nCount; i++ )
1377 ImplToolItem* pItem = &mpData->m_aItems[i];
1379 // only draw, if the rectangle is within PaintRectangle
1380 if (!pItem->maRect.IsEmpty())
1381 InvalidateItem(i);
1385 OUString ToolBox::GetDisplayText() const
1387 if( ! mpData->m_pLayoutData )
1388 const_cast<ToolBox *>(this)->ImplFillLayoutData();
1389 return mpData->m_pLayoutData ? mpData->m_pLayoutData->m_aDisplayText : OUString();
1392 tools::Rectangle ToolBox::GetCharacterBounds( ToolBoxItemId nItemID, tools::Long nIndex )
1394 tools::Long nItemIndex = -1;
1395 if( ! mpData->m_pLayoutData )
1396 ImplFillLayoutData();
1397 if( mpData->m_pLayoutData )
1399 for( size_t i = 0; i < mpData->m_pLayoutData->m_aLineItemIds.size(); i++ )
1401 if( mpData->m_pLayoutData->m_aLineItemIds[i] == nItemID )
1403 nItemIndex = mpData->m_pLayoutData->m_aLineIndices[i];
1404 break;
1408 return (mpData->m_pLayoutData && nItemIndex != -1) ? mpData->m_pLayoutData->GetCharacterBounds( nItemIndex+nIndex ) : tools::Rectangle();
1411 tools::Long ToolBox::GetIndexForPoint( const Point& rPoint, ToolBoxItemId& rItemID )
1413 tools::Long nIndex = -1;
1414 rItemID = ToolBoxItemId(0);
1415 if( ! mpData->m_pLayoutData )
1416 ImplFillLayoutData();
1417 if( mpData->m_pLayoutData )
1419 nIndex = mpData->m_pLayoutData->GetIndexForPoint( rPoint );
1420 for( size_t i = 0; i < mpData->m_pLayoutData->m_aLineIndices.size(); i++ )
1422 if( mpData->m_pLayoutData->m_aLineIndices[i] <= nIndex &&
1423 (i == mpData->m_pLayoutData->m_aLineIndices.size()-1 || mpData->m_pLayoutData->m_aLineIndices[i+1] > nIndex) )
1425 rItemID = mpData->m_pLayoutData->m_aLineItemIds[i];
1426 break;
1430 return nIndex;
1433 void ToolBox::SetDropdownClickHdl( const Link<ToolBox *, void>& rLink )
1435 if (mpData != nullptr) {
1436 mpData->maDropdownClickHdl = rLink;
1440 void ToolBox::SetMenuType( ToolBoxMenuType aType )
1442 if( aType == mpData->maMenuType )
1443 return;
1445 mpData->maMenuType = aType;
1446 if( IsFloatingMode() )
1448 // the menu button may have to be moved into the decoration which changes the layout
1449 ImplDockingWindowWrapper *pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper( this );
1450 if( pWrapper )
1451 pWrapper->ShowMenuTitleButton( bool( aType & ToolBoxMenuType::Customize) );
1453 mbFormat = true;
1454 ImplFormat();
1455 ImplSetMinMaxFloatSize();
1457 else
1459 // trigger redraw of menu button
1460 if( !mpData->maMenubuttonItem.maRect.IsEmpty() )
1461 Invalidate(mpData->maMenubuttonItem.maRect);
1465 ToolBoxMenuType ToolBox::GetMenuType() const
1467 return mpData->maMenuType;
1470 bool ToolBox::IsMenuEnabled() const
1472 return mpData->maMenuType != ToolBoxMenuType::NONE;
1475 PopupMenu* ToolBox::GetMenu() const
1477 return mpData == nullptr ? nullptr : mpData->mpMenu;
1480 void ToolBox::SetMenuExecuteHdl( const Link<ToolBox *, void>& rLink )
1482 mpData->maMenuButtonHdl = rLink;
1485 bool ToolBox::ImplHasClippedItems()
1487 // are any items currently clipped ?
1488 ImplFormat();
1489 return std::any_of(mpData->m_aItems.begin(), mpData->m_aItems.end(),
1490 [](const ImplToolItem& rItem) { return rItem.IsClipped(); });
1493 namespace
1495 MenuItemBits ConvertBitsFromToolBoxToMenu(ToolBoxItemBits nToolItemBits)
1497 MenuItemBits nMenuItemBits = MenuItemBits::NONE;
1498 if ((nToolItemBits & ToolBoxItemBits::CHECKABLE) ||
1499 (nToolItemBits & ToolBoxItemBits::DROPDOWN))
1501 nMenuItemBits |= MenuItemBits::CHECKABLE;
1503 return nMenuItemBits;
1507 void ToolBox::UpdateCustomMenu()
1509 // fill clipped items into menu
1510 PopupMenu *pMenu = GetMenu();
1511 pMenu->Clear();
1513 // add menu items: first the overflow items, then hidden items, both in the
1514 // order they would usually appear in the toolbar. Separators that would be
1515 // in the toolbar are ignored as they would introduce too much clutter,
1516 // instead we have a single separator to help distinguish between overflow
1517 // and hidden items.
1518 if ( mpData->m_aItems.empty() )
1519 return;
1521 // nStartPos will hold the number of clipped items appended from first loop
1522 for ( const auto& rItem : mpData->m_aItems )
1524 if( rItem.IsClipped() )
1526 sal_uInt16 id = sal_uInt16(rItem.mnId) + TOOLBOX_MENUITEM_START;
1527 MenuItemBits nMenuItemBits = ConvertBitsFromToolBoxToMenu(rItem.mnBits);
1528 pMenu->InsertItem( id, rItem.maText, rItem.maImage, nMenuItemBits);
1529 pMenu->SetItemCommand( id, rItem.maCommandStr );
1530 pMenu->EnableItem( id, rItem.mbEnabled );
1531 pMenu->CheckItem ( id, rItem.meState == TRISTATE_TRUE );
1535 // add a separator below the inserted clipped-items
1536 pMenu->InsertSeparator();
1538 // now append the items that are explicitly disabled
1539 for ( const auto& rItem : mpData->m_aItems )
1541 if( rItem.IsItemHidden() )
1543 sal_uInt16 id = sal_uInt16(rItem.mnId) + TOOLBOX_MENUITEM_START;
1544 MenuItemBits nMenuItemBits = ConvertBitsFromToolBoxToMenu(rItem.mnBits);
1545 pMenu->InsertItem( id, rItem.maText, rItem.maImage, nMenuItemBits );
1546 pMenu->SetItemCommand( id, rItem.maCommandStr );
1547 pMenu->EnableItem( id, rItem.mbEnabled );
1548 pMenu->CheckItem( id, rItem.meState == TRISTATE_TRUE );
1553 IMPL_LINK( ToolBox, ImplCustomMenuListener, VclMenuEvent&, rEvent, void )
1555 if( rEvent.GetMenu() == GetMenu() && rEvent.GetId() == VclEventId::MenuSelect )
1557 sal_uInt16 id = GetMenu()->GetItemId( rEvent.GetItemPos() );
1558 if( id >= TOOLBOX_MENUITEM_START )
1559 TriggerItem( ToolBoxItemId(id - TOOLBOX_MENUITEM_START) );
1563 void ToolBox::ExecuteCustomMenu( const tools::Rectangle& rRect )
1565 if ( !IsMenuEnabled() || ImplIsInPopupMode() )
1566 return;
1568 UpdateCustomMenu();
1570 if( GetMenuType() & ToolBoxMenuType::Customize )
1571 // call button handler to allow for menu customization
1572 mpData->maMenuButtonHdl.Call( this );
1574 GetMenu()->AddEventListener( LINK( this, ToolBox, ImplCustomMenuListener ) );
1576 // make sure all disabled entries will be shown
1577 GetMenu()->SetMenuFlags(
1578 GetMenu()->GetMenuFlags() | MenuFlags::AlwaysShowDisabledEntries );
1580 // toolbox might be destroyed during execute
1581 bool bBorderDel = false;
1583 VclPtr<vcl::Window> pWin = this;
1584 tools::Rectangle aMenuRect = rRect;
1585 VclPtr<ImplBorderWindow> pBorderWin;
1586 if( aMenuRect.IsEmpty() && IsFloatingMode() )
1588 // custom menu is placed in the decoration
1589 pBorderWin = dynamic_cast<ImplBorderWindow*>( GetWindow( GetWindowType::Border ) );
1590 if( pBorderWin && !pBorderWin->GetMenuRect().IsEmpty() )
1592 pWin = pBorderWin;
1593 aMenuRect = pBorderWin->GetMenuRect();
1594 bBorderDel = true;
1598 sal_uInt16 uId = GetMenu()->Execute( pWin, tools::Rectangle( ImplGetPopupPosition( aMenuRect ), Size() ),
1599 PopupMenuFlags::ExecuteDown | PopupMenuFlags::NoMouseUpClose );
1601 if ( pWin->isDisposed() )
1602 return;
1604 if( GetMenu() )
1605 GetMenu()->RemoveEventListener( LINK( this, ToolBox, ImplCustomMenuListener ) );
1606 if( bBorderDel )
1608 if( pBorderWin->isDisposed() )
1609 return;
1612 pWin->Invalidate( aMenuRect );
1614 if( uId )
1615 GrabFocusToDocument();
1618 // checks override first, useful during calculation of sizes
1619 bool ToolBox::ImplIsFloatingMode() const
1621 SAL_WARN_IF( mpData->mbAssumeDocked && mpData->mbAssumeFloating, "vcl",
1622 "cannot assume docked and floating" );
1624 if( mpData->mbAssumeDocked )
1625 return false;
1626 else if( mpData->mbAssumeFloating )
1627 return true;
1628 else
1629 return IsFloatingMode();
1632 // checks override first, useful during calculation of sizes
1633 bool ToolBox::ImplIsInPopupMode() const
1635 if( mpData->mbAssumePopupMode )
1636 return true;
1637 else
1639 ImplDockingWindowWrapper *pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper( this );
1640 return ( pWrapper && pWrapper->GetFloatingWindow() && static_cast<FloatingWindow*>(pWrapper->GetFloatingWindow())->IsInPopupMode() );
1644 void ToolBox::Lock( bool bLock )
1646 ImplDockingWindowWrapper *pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper( this );
1647 if( !pWrapper )
1648 return;
1649 if( mpData->mbIsLocked != bLock )
1651 mpData->mbIsLocked = bLock;
1652 if( !ImplIsFloatingMode() )
1654 mbCalc = true;
1655 mbFormat = true;
1656 SetSizePixel( CalcWindowSizePixel(1) );
1657 Invalidate();
1662 bool ToolBox::AlwaysLocked()
1664 // read config item to determine toolbox behaviour, used for subtoolbars
1666 static int nAlwaysLocked = -1;
1668 if( nAlwaysLocked == -1 )
1670 nAlwaysLocked = 0; // ask configuration only once
1672 utl::OConfigurationNode aNode = utl::OConfigurationTreeRoot::tryCreateWithComponentContext(
1673 comphelper::getProcessComponentContext(),
1674 "/org.openoffice.Office.UI.GlobalSettings/Toolbars" ); // note: case sensitive !
1675 if ( aNode.isValid() )
1677 // feature enabled ?
1678 bool bStatesEnabled = bool();
1679 css::uno::Any aValue = aNode.getNodeValue( "StatesEnabled" );
1680 if( aValue >>= bStatesEnabled )
1682 if( bStatesEnabled )
1684 // now read the locking state
1685 utl::OConfigurationNode aNode2 = utl::OConfigurationTreeRoot::tryCreateWithComponentContext(
1686 comphelper::getProcessComponentContext(),
1687 "/org.openoffice.Office.UI.GlobalSettings/Toolbars/States" ); // note: case sensitive !
1689 bool bLocked = bool();
1690 css::uno::Any aValue2 = aNode2.getNodeValue( "Locked" );
1691 if( aValue2 >>= bLocked )
1692 nAlwaysLocked = bLocked ? 1 : 0;
1698 return nAlwaysLocked == 1;
1701 bool ToolBox::WillUsePopupMode() const
1703 return mpData->mbWillUsePopupMode;
1706 void ToolBox::WillUsePopupMode( bool b )
1708 mpData->mbWillUsePopupMode = b;
1711 void ToolBox::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
1713 DockingWindow::DumpAsPropertyTree(rJsonWriter);
1715 auto childrenNode = rJsonWriter.startArray("children");
1716 for (ToolBox::ImplToolItems::size_type i = 0; i < GetItemCount(); ++i)
1718 auto childNode = rJsonWriter.startStruct();
1719 ToolBoxItemId nId = GetItemId(i);
1721 vcl::Window* pWindow = GetItemWindow(nId);
1722 if (pWindow)
1724 pWindow->DumpAsPropertyTree(rJsonWriter);
1726 else
1728 OUString sCommand = GetItemCommand(nId);
1729 rJsonWriter.put("type", "toolitem");
1730 rJsonWriter.put("text", GetItemText(nId));
1731 rJsonWriter.put("command", sCommand);
1732 if (IsItemChecked(nId))
1733 rJsonWriter.put("selected", true);
1734 if (!IsItemVisible(nId))
1735 rJsonWriter.put("visible", false);
1736 if (GetItemBits(nId) & ToolBoxItemBits::DROPDOWN)
1737 rJsonWriter.put("dropdown", true);
1738 if (!IsItemEnabled(nId))
1739 rJsonWriter.put("enabled", false);
1741 Image aImage = GetItemImage(nId);
1742 if (!sCommand.startsWith(".uno:") && !!aImage)
1744 SvMemoryStream aOStm(6535, 6535);
1745 if(GraphicConverter::Export(aOStm, aImage.GetBitmapEx(), ConvertDataFormat::PNG) == ERRCODE_NONE)
1747 css::uno::Sequence<sal_Int8> aSeq( static_cast<sal_Int8 const *>(aOStm.GetData()), aOStm.Tell());
1748 OStringBuffer aBuffer("data:image/png;base64,");
1749 ::comphelper::Base64::encode(aBuffer, aSeq);
1750 rJsonWriter.put("image", aBuffer);
1757 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */