1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <sal/config.h>
21 #include <sal/log.hxx>
22 
23 #include <comphelper/processfactory.hxx>
24 #include <boost/property_tree/ptree.hpp>
25 
26 #include <vcl/svapp.hxx>
27 #include <vcl/idle.hxx>
28 #include <vcl/bitmap.hxx>
29 #include <vcl/toolbox.hxx>
30 #include <vcl/mnemonic.hxx>
31 #include <vcl/menu.hxx>
32 #include <vcl/settings.hxx>
33 #include <vcl/IconThemeInfo.hxx>
34 #include <vcl/commandinfoprovider.hxx>
35 
36 #include <svdata.hxx>
37 #include <brdwin.hxx>
38 #include <toolbox.h>
39 
40 #include <unotools/confignode.hxx>
41 
42 using namespace vcl;
43 
44 #define TB_SEP_SIZE     8  // Separator size
45 
46 
ImplToolBoxPrivateData()47 ImplToolBoxPrivateData::ImplToolBoxPrivateData()
48 {
49     meButtonSize = ToolBoxButtonSize::DontCare;
50     mpMenu = VclPtr<PopupMenu>::Create();
51 
52     maMenuType = ToolBoxMenuType::NONE;
53     maMenubuttonItem.maItemSize = Size( TB_MENUBUTTON_SIZE+TB_MENUBUTTON_OFFSET, TB_MENUBUTTON_SIZE+TB_MENUBUTTON_OFFSET );
54     maMenubuttonItem.meState = TRISTATE_FALSE;
55     mnMenuButtonWidth = TB_MENUBUTTON_SIZE;
56 
57     mbIsLocked = false;
58     mbNativeButtons = false;
59     mbIsPaintLocked = false;
60     mbAssumeDocked = false;
61     mbAssumePopupMode = false;
62     mbAssumeFloating = false;
63     mbKeyInputDisabled = false;
64     mbMenubuttonSelected = false;
65     mbMenubuttonWasLastSelected = false;
66     mbWillUsePopupMode = false;
67     mbDropDownByKeyboard = false;
68 }
69 
~ImplToolBoxPrivateData()70 ImplToolBoxPrivateData::~ImplToolBoxPrivateData()
71 {
72     m_pLayoutData.reset();
73     mpMenu.disposeAndClear();
74 }
75 
init(sal_uInt16 nItemId,ToolBoxItemBits nItemBits,bool bEmptyBtn)76 void ImplToolItem::init(sal_uInt16 nItemId, ToolBoxItemBits nItemBits,
77                         bool bEmptyBtn)
78 {
79     mnId            = nItemId;
80     mpWindow        = nullptr;
81     mpUserData      = nullptr;
82     meType          = ToolBoxItemType::BUTTON;
83     mnBits          = nItemBits;
84     meState         = TRISTATE_FALSE;
85     mbEnabled       = true;
86     mbVisible       = true;
87     mbEmptyBtn      = bEmptyBtn;
88     mbShowWindow    = false;
89     mbBreak         = false;
90     mnSepSize       = TB_SEP_SIZE;
91     mnDropDownArrowWidth = TB_DROPDOWNARROWWIDTH;
92     mnImageAngle    = 0;
93     mbMirrorMode    = false;
94     mbVisibleText   = false;
95     mbExpand        = false;
96 }
97 
ImplToolItem()98 ImplToolItem::ImplToolItem()
99 {
100     init(0, ToolBoxItemBits::NONE, true);
101 }
102 
ImplToolItem(sal_uInt16 nItemId,const Image & rImage,ToolBoxItemBits nItemBits)103 ImplToolItem::ImplToolItem( sal_uInt16 nItemId, const Image& rImage,
104                             ToolBoxItemBits nItemBits ) :
105     maImage( rImage )
106 {
107     init(nItemId, nItemBits, false);
108 }
109 
ImplToolItem(sal_uInt16 nItemId,const OUString & rText,ToolBoxItemBits nItemBits)110 ImplToolItem::ImplToolItem( sal_uInt16 nItemId, const OUString& rText,
111                             ToolBoxItemBits nItemBits ) :
112     maText( rText )
113 {
114     init(nItemId, nItemBits, false);
115 }
116 
ImplToolItem(sal_uInt16 nItemId,const Image & rImage,const OUString & rText,ToolBoxItemBits nItemBits)117 ImplToolItem::ImplToolItem( sal_uInt16 nItemId, const Image& rImage,
118                             const OUString& rText, ToolBoxItemBits nItemBits ) :
119     maImage( rImage ),
120     maText( rText )
121 {
122     init(nItemId, nItemBits, false);
123 }
124 
GetSize(bool bHorz,bool bCheckMaxWidth,long maxWidth,const Size & rDefaultSize)125 Size ImplToolItem::GetSize( bool bHorz, bool bCheckMaxWidth, long maxWidth, const Size& rDefaultSize )
126 {
127     Size aSize( rDefaultSize ); // the size of 'standard' toolbox items
128                                 // non-standard items are eg windows or buttons with text
129 
130     if ( (meType == ToolBoxItemType::BUTTON) || (meType == ToolBoxItemType::SPACE) )
131     {
132         aSize = maItemSize;
133 
134         if ( mpWindow && bHorz )
135         {
136             // get size of item window and check if it fits
137             // no windows in vertical toolbars (the default is mbShowWindow=false)
138             Size aWinSize = mpWindow->GetSizePixel();
139 
140             if (mpWindow->GetStyle() & WB_NOLABEL)
141                 // Window wants no label? Then don't check width, it'll be just
142                 // clipped.
143                 bCheckMaxWidth = false;
144 
145             if ( !bCheckMaxWidth || (aWinSize.Width() <= maxWidth) )
146             {
147                 aSize.setWidth( aWinSize.Width() );
148                 aSize.setHeight( aWinSize.Height() );
149                 mbShowWindow = true;
150             }
151             else
152             {
153                 if ( mbEmptyBtn )
154                 {
155                     aSize.setWidth( 0 );
156                     aSize.setHeight( 0 );
157                 }
158             }
159         }
160     }
161     else if ( meType == ToolBoxItemType::SEPARATOR )
162     {
163         if ( bHorz )
164         {
165             aSize.setWidth( mnSepSize );
166             aSize.setHeight( rDefaultSize.Height() );
167         }
168         else
169         {
170             aSize.setWidth( rDefaultSize.Width() );
171             aSize.setHeight( mnSepSize );
172         }
173     }
174     else if ( meType == ToolBoxItemType::BREAK )
175     {
176         aSize.setWidth( 0 );
177         aSize.setHeight( 0 );
178     }
179 
180     return aSize;
181 }
182 
DetermineButtonDrawStyle(ButtonType eButtonType,bool & rbImage,bool & rbText) const183 void ImplToolItem::DetermineButtonDrawStyle( ButtonType eButtonType, bool& rbImage, bool& rbText ) const
184 {
185     if ( meType != ToolBoxItemType::BUTTON )
186     {
187         // no button -> draw nothing
188         rbImage = rbText = false;
189         return;
190     }
191 
192     bool bHasImage;
193     bool bHasText;
194 
195     // check for image and/or text
196     bHasImage = !!maImage;
197     bHasText = !maText.isEmpty();
198 
199     // prefer images if symbolonly buttons are drawn
200     // prefer texts if textonly buttons are drawn
201 
202     if ( eButtonType == ButtonType::SYMBOLONLY )         // drawing icons only
203     {
204         if( bHasImage || !bHasText )
205         {
206             rbImage = true;
207             rbText  = false;
208         }
209         else
210         {
211             rbImage = false;
212             rbText  = true;
213         }
214     }
215     else if ( eButtonType == ButtonType::TEXT )      // drawing text only
216     {
217         if( bHasText || !bHasImage )
218         {
219             rbImage = false;
220             rbText  = true;
221         }
222         else
223         {
224             rbImage = true;
225             rbText  = false;
226         }
227     }
228     else                                        // drawing icons and text both
229     {
230         rbImage = true;
231         rbText  = true;
232     }
233 }
234 
GetDropDownRect(bool bHorz) const235 tools::Rectangle ImplToolItem::GetDropDownRect( bool bHorz ) const
236 {
237     tools::Rectangle aRect;
238     if( (mnBits & ToolBoxItemBits::DROPDOWN) && !maRect.IsEmpty() )
239     {
240         aRect = maRect;
241         if( mbVisibleText && !bHorz )
242             // item will be rotated -> place dropdown to the bottom
243             aRect.SetTop( aRect.Bottom() - mnDropDownArrowWidth );
244         else
245             // place dropdown to the right
246             aRect.SetLeft( aRect.Right() - mnDropDownArrowWidth );
247     }
248     return aRect;
249 }
250 
IsClipped() const251 bool ImplToolItem::IsClipped() const
252 {
253     return ( meType == ToolBoxItemType::BUTTON && mbVisible && maRect.IsEmpty() );
254 }
255 
IsItemHidden() const256 bool ImplToolItem::IsItemHidden() const
257 {
258     return ( meType == ToolBoxItemType::BUTTON && !mbVisible );
259 }
260 
ImplInvalidate(bool bNewCalc,bool bFullPaint)261 void ToolBox::ImplInvalidate( bool bNewCalc, bool bFullPaint )
262 {
263     ImplUpdateInputEnable();
264 
265     if ( bNewCalc )
266         mbCalc = true;
267 
268     if ( bFullPaint )
269     {
270         mbFormat = true;
271 
272         // do we need to redraw?
273         if ( IsReallyVisible() && IsUpdateMode() )
274         {
275             Invalidate( tools::Rectangle( mnLeftBorder, mnTopBorder,
276                                    mnDX-mnRightBorder-1, mnDY-mnBottomBorder-1 ) );
277             mpIdle->Stop();
278         }
279     }
280     else
281     {
282         if ( !mbFormat )
283         {
284             mbFormat = true;
285 
286             // do we need to redraw?
287             if ( IsReallyVisible() && IsUpdateMode() )
288                 mpIdle->Start();
289         }
290     }
291 
292     // request new layout by layoutmanager
293     CallEventListeners( VclEventId::ToolboxFormatChanged );
294 }
295 
ImplUpdateItem(ImplToolItems::size_type nIndex)296 void ToolBox::ImplUpdateItem( ImplToolItems::size_type nIndex )
297 {
298     // do we need to redraw?
299     if ( IsReallyVisible() && IsUpdateMode() )
300     {
301         if ( nIndex == ITEM_NOTFOUND )
302         {
303             // #i52217# no immediate draw as this might lead to paint problems
304             Invalidate( tools::Rectangle( mnLeftBorder, mnTopBorder, mnDX-mnRightBorder-1, mnDY-mnBottomBorder-1 ) );
305         }
306         else
307         {
308             if ( !mbFormat )
309             {
310                 // #i52217# no immediate draw as this might lead to paint problems
311                 Invalidate( mpData->m_aItems[nIndex].maRect );
312             }
313             else
314                 maPaintRect.Union( mpData->m_aItems[nIndex].maRect );
315         }
316     }
317 }
318 
Click()319 void ToolBox::Click()
320 {
321     CallEventListeners( VclEventId::ToolboxClick );
322     maClickHdl.Call( this );
323 }
324 
DoubleClick()325 void ToolBox::DoubleClick()
326 {
327     CallEventListeners( VclEventId::ToolboxDoubleClick );
328     maDoubleClickHdl.Call( this );
329 }
330 
Activate()331 void ToolBox::Activate()
332 {
333     mnActivateCount++;
334     CallEventListeners( VclEventId::ToolboxActivate );
335     maActivateHdl.Call( this );
336 }
337 
Deactivate()338 void ToolBox::Deactivate()
339 {
340     mnActivateCount--;
341     CallEventListeners( VclEventId::ToolboxDeactivate );
342     maDeactivateHdl.Call( this );
343 }
344 
Highlight()345 void ToolBox::Highlight()
346 {
347     CallEventListeners( VclEventId::ToolboxHighlight );
348 }
349 
Select()350 void ToolBox::Select()
351 {
352     VclPtr<vcl::Window> xWindow = this;
353 
354     CallEventListeners( VclEventId::ToolboxSelect );
355     maSelectHdl.Call( this );
356 
357     if ( xWindow->IsDisposed() )
358         return;
359 
360     // TODO: GetFloatingWindow in DockingWindow is currently inline, change it to check dockingwrapper
361     ImplDockingWindowWrapper *pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper( this );
362     if( pWrapper && pWrapper->GetFloatingWindow() && pWrapper->GetFloatingWindow()->IsInPopupMode() )
363         pWrapper->GetFloatingWindow()->EndPopupMode();
364 }
365 
InsertItem(sal_uInt16 nItemId,const Image & rImage,ToolBoxItemBits nBits,ImplToolItems::size_type nPos)366 void ToolBox::InsertItem( sal_uInt16 nItemId, const Image& rImage, ToolBoxItemBits nBits, ImplToolItems::size_type nPos )
367 {
368     SAL_WARN_IF( !nItemId, "vcl", "ToolBox::InsertItem(): ItemId == 0" );
369     SAL_WARN_IF( GetItemPos( nItemId ) != ITEM_NOTFOUND, "vcl",
370                 "ToolBox::InsertItem(): ItemId already exists" );
371 
372     // create item and add to list
373     mpData->m_aItems.insert( (nPos < mpData->m_aItems.size()) ? mpData->m_aItems.begin()+nPos : mpData->m_aItems.end(),
374                              ImplToolItem( nItemId, rImage, nBits ) );
375     mpData->ImplClearLayoutData();
376 
377     ImplInvalidate( true );
378 
379     // Notify
380     ImplToolItems::size_type nNewPos = ( nPos == APPEND ) ? ( mpData->m_aItems.size() - 1 ) : nPos;
381     CallEventListeners( VclEventId::ToolboxItemAdded, reinterpret_cast< void* >(nNewPos ) );
382 }
383 
InsertItem(sal_uInt16 nItemId,const Image & rImage,const OUString & rText,ToolBoxItemBits nBits,ImplToolItems::size_type nPos)384 void ToolBox::InsertItem( sal_uInt16 nItemId, const Image& rImage, const OUString& rText, ToolBoxItemBits nBits,
385                           ImplToolItems::size_type nPos )
386 {
387     SAL_WARN_IF( !nItemId, "vcl", "ToolBox::InsertItem(): ItemId == 0" );
388     SAL_WARN_IF( GetItemPos( nItemId ) != ITEM_NOTFOUND, "vcl",
389                 "ToolBox::InsertItem(): ItemId already exists" );
390 
391     // create item and add to list
392     mpData->m_aItems.insert( (nPos < mpData->m_aItems.size()) ? mpData->m_aItems.begin()+nPos : mpData->m_aItems.end(),
393                              ImplToolItem( nItemId, rImage, MnemonicGenerator::EraseAllMnemonicChars(rText), nBits ) );
394     mpData->ImplClearLayoutData();
395 
396     ImplInvalidate( true );
397 
398     // Notify
399     ImplToolItems::size_type nNewPos = ( nPos == APPEND ) ? ( mpData->m_aItems.size() - 1 ) : nPos;
400     CallEventListeners( VclEventId::ToolboxItemAdded, reinterpret_cast< void* >( nNewPos ) );
401 }
402 
InsertItem(sal_uInt16 nItemId,const OUString & rText,ToolBoxItemBits nBits,ImplToolItems::size_type nPos)403 void ToolBox::InsertItem( sal_uInt16 nItemId, const OUString& rText, ToolBoxItemBits nBits, ImplToolItems::size_type nPos )
404 {
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" );
408 
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, MnemonicGenerator::EraseAllMnemonicChars(rText), nBits ) );
412     mpData->ImplClearLayoutData();
413 
414     ImplInvalidate( true );
415 
416     // Notify
417     ImplToolItems::size_type nNewPos = ( nPos == APPEND ) ? ( mpData->m_aItems.size() - 1 ) : nPos;
418     CallEventListeners( VclEventId::ToolboxItemAdded, reinterpret_cast< void* >( nNewPos ) );
419 }
420 
InsertItem(const OUString & rCommand,const css::uno::Reference<css::frame::XFrame> & rFrame,ToolBoxItemBits nBits,const Size & rRequestedSize,ImplToolItems::size_type nPos)421 void ToolBox::InsertItem(const OUString& rCommand, const css::uno::Reference<css::frame::XFrame>& rFrame, ToolBoxItemBits nBits,
422                          const Size& rRequestedSize, ImplToolItems::size_type nPos)
423 {
424     OUString aModuleName(vcl::CommandInfoProvider::GetModuleIdentifier(rFrame));
425     auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(rCommand, aModuleName);
426     OUString aLabel(vcl::CommandInfoProvider::GetLabelForCommand(aProperties));
427     OUString aTooltip(vcl::CommandInfoProvider::GetTooltipForCommand(rCommand, aProperties, rFrame));
428     Image aImage(CommandInfoProvider::GetImageForCommand(rCommand, rFrame, GetImageSize()));
429 
430     sal_uInt16 nItemId = GetItemCount() + 1;
431         //TODO: ImplToolItems::size_type -> sal_uInt16!
432     InsertItem(nItemId, aImage, aLabel, nBits, nPos);
433     SetItemCommand(nItemId, rCommand);
434     SetQuickHelpText(nItemId, aTooltip);
435 
436     // set the minimal size
437     ImplToolItem* pItem = ImplGetItem( nItemId );
438     if ( pItem )
439         pItem->maMinimalItemSize = rRequestedSize;
440 }
441 
InsertWindow(sal_uInt16 nItemId,vcl::Window * pWindow,ToolBoxItemBits nBits,ImplToolItems::size_type nPos)442 void ToolBox::InsertWindow( sal_uInt16 nItemId, vcl::Window* pWindow,
443                             ToolBoxItemBits nBits, ImplToolItems::size_type nPos )
444 {
445     SAL_WARN_IF( !nItemId, "vcl", "ToolBox::InsertWindow(): ItemId == 0" );
446     SAL_WARN_IF( GetItemPos( nItemId ) != ITEM_NOTFOUND, "vcl",
447                 "ToolBox::InsertWindow(): ItemId already exists" );
448 
449     // create item and add to list
450     ImplToolItem aItem;
451     aItem.mnId       = nItemId;
452     aItem.meType     = ToolBoxItemType::BUTTON;
453     aItem.mnBits     = nBits;
454     aItem.mpWindow   = pWindow;
455     mpData->m_aItems.insert( (nPos < mpData->m_aItems.size()) ? mpData->m_aItems.begin()+nPos : mpData->m_aItems.end(), aItem );
456     mpData->ImplClearLayoutData();
457 
458     if ( pWindow )
459         pWindow->Hide();
460 
461     ImplInvalidate( true );
462 
463     // Notify
464     ImplToolItems::size_type nNewPos = ( nPos == APPEND ) ? ( mpData->m_aItems.size() - 1 ) : nPos;
465     CallEventListeners( VclEventId::ToolboxItemAdded, reinterpret_cast< void* >( nNewPos ) );
466 }
467 
InsertSpace()468 void ToolBox::InsertSpace()
469 {
470     // create item and add to list
471     ImplToolItem aItem;
472     aItem.meType     = ToolBoxItemType::SPACE;
473     aItem.mbEnabled  = false;
474     mpData->m_aItems.push_back( aItem );
475     mpData->ImplClearLayoutData();
476 
477     ImplInvalidate();
478 
479     // Notify
480     ImplToolItems::size_type nNewPos = mpData->m_aItems.size() - 1;
481     CallEventListeners( VclEventId::ToolboxItemAdded, reinterpret_cast< void* >( nNewPos ) );
482 }
483 
InsertSeparator(ImplToolItems::size_type nPos,sal_uInt16 nPixSize)484 void ToolBox::InsertSeparator( ImplToolItems::size_type nPos, sal_uInt16 nPixSize )
485 {
486     // create item and add to list
487     ImplToolItem aItem;
488     aItem.meType     = ToolBoxItemType::SEPARATOR;
489     aItem.mbEnabled  = false;
490     if ( nPixSize )
491         aItem.mnSepSize = nPixSize;
492     mpData->m_aItems.insert( (nPos < mpData->m_aItems.size()) ? mpData->m_aItems.begin()+nPos : mpData->m_aItems.end(), aItem );
493     mpData->ImplClearLayoutData();
494 
495     ImplInvalidate();
496 
497     // Notify
498     ImplToolItems::size_type nNewPos = ( nPos == APPEND ) ? ( mpData->m_aItems.size() - 1 ) : nPos;
499     CallEventListeners( VclEventId::ToolboxItemAdded, reinterpret_cast< void* >( nNewPos ) );
500 }
501 
InsertBreak(ImplToolItems::size_type nPos)502 void ToolBox::InsertBreak( ImplToolItems::size_type nPos )
503 {
504     // create item and add to list
505     ImplToolItem aItem;
506     aItem.meType     = ToolBoxItemType::BREAK;
507     aItem.mbEnabled  = false;
508     mpData->m_aItems.insert( (nPos < mpData->m_aItems.size()) ? mpData->m_aItems.begin()+nPos : mpData->m_aItems.end(), aItem );
509     mpData->ImplClearLayoutData();
510 
511     ImplInvalidate();
512 
513     // Notify
514     ImplToolItems::size_type nNewPos = ( nPos == APPEND ) ? ( mpData->m_aItems.size() - 1 ) : nPos;
515     CallEventListeners( VclEventId::ToolboxItemAdded, reinterpret_cast< void* >( nNewPos ) );
516 }
517 
RemoveItem(ImplToolItems::size_type nPos)518 void ToolBox::RemoveItem( ImplToolItems::size_type nPos )
519 {
520     if( nPos < mpData->m_aItems.size() )
521     {
522         bool bMustCalc;
523         bMustCalc = mpData->m_aItems[nPos].meType == ToolBoxItemType::BUTTON;
524 
525         if ( mpData->m_aItems[nPos].mpWindow )
526             mpData->m_aItems[nPos].mpWindow->Hide();
527 
528         // add the removed item to PaintRect
529         maPaintRect.Union( mpData->m_aItems[nPos].maRect );
530 
531         // ensure not to delete in the Select-Handler
532         if ( mpData->m_aItems[nPos].mnId == mnCurItemId )
533             mnCurItemId = 0;
534         if ( mpData->m_aItems[nPos].mnId == mnHighItemId )
535             mnHighItemId = 0;
536 
537         ImplInvalidate( bMustCalc );
538 
539         mpData->m_aItems.erase( mpData->m_aItems.begin()+nPos );
540         mpData->ImplClearLayoutData();
541 
542         // Notify
543         CallEventListeners( VclEventId::ToolboxItemRemoved, reinterpret_cast< void* >( nPos ) );
544     }
545 }
546 
CopyItem(const ToolBox & rToolBox,sal_uInt16 nItemId)547 void ToolBox::CopyItem( const ToolBox& rToolBox, sal_uInt16 nItemId )
548 {
549     SAL_WARN_IF( GetItemPos( nItemId ) != ITEM_NOTFOUND, "vcl",
550                 "ToolBox::CopyItem(): ItemId already exists" );
551 
552     ImplToolItems::size_type nPos = rToolBox.GetItemPos( nItemId );
553 
554     // found item
555     if ( nPos != ITEM_NOTFOUND )
556     {
557         // push ToolBox item onto the list
558         ImplToolItem aNewItem = rToolBox.mpData->m_aItems[nPos];
559         // reset state
560         aNewItem.mpWindow      = nullptr;
561         aNewItem.mbShowWindow = false;
562 
563         mpData->m_aItems.push_back( aNewItem );
564         mpData->ImplClearLayoutData();
565         // redraw ToolBox
566         ImplInvalidate();
567 
568         // Notify
569         ImplToolItems::size_type nNewPos2 = mpData->m_aItems.size() - 1;
570         CallEventListeners( VclEventId::ToolboxItemAdded, reinterpret_cast< void* >( nNewPos2 ) );
571     }
572 }
573 
Clear()574 void ToolBox::Clear()
575 {
576     mpData->m_aItems.clear();
577     mpData->ImplClearLayoutData();
578 
579     // ensure not to delete in the Select-Handler
580     mnCurItemId = 0;
581     mnHighItemId = 0;
582 
583     ImplInvalidate( true, true );
584 
585     // Notify
586     CallEventListeners( VclEventId::ToolboxAllItemsChanged );
587 }
588 
SetButtonType(ButtonType eNewType)589 void ToolBox::SetButtonType( ButtonType eNewType )
590 {
591     if ( meButtonType != eNewType )
592     {
593         meButtonType = eNewType;
594 
595         // better redraw everything, as otherwise there might be problems
596         // with regions that were copied with CopyBits
597         ImplInvalidate( true );
598     }
599 }
600 
SetToolboxButtonSize(ToolBoxButtonSize eSize)601 void ToolBox::SetToolboxButtonSize( ToolBoxButtonSize eSize )
602 {
603     if( mpData->meButtonSize != eSize )
604     {
605         mpData->meButtonSize = eSize;
606         mbCalc = true;
607         mbFormat = true;
608     }
609 }
610 
GetToolboxButtonSize() const611 ToolBoxButtonSize ToolBox::GetToolboxButtonSize() const
612 {
613     return mpData->meButtonSize;
614 }
615 
GetImageSize() const616 ImageType ToolBox::GetImageSize() const
617 {
618     ImageType eImageType = ImageType::Size16;
619     if (mpData->meButtonSize == ToolBoxButtonSize::Large)
620         eImageType = ImageType::Size26;
621     else if (mpData->meButtonSize == ToolBoxButtonSize::Size32)
622         eImageType = ImageType::Size32;
623 
624     return eImageType;
625 }
626 
GetDefaultImageSize(ToolBoxButtonSize eToolBoxButtonSize)627 /*static*/ Size ToolBox::GetDefaultImageSize(ToolBoxButtonSize eToolBoxButtonSize)
628 {
629     OutputDevice *pDefault = Application::GetDefaultDevice();
630     float fScaleFactor = pDefault ? pDefault->GetDPIScaleFactor() : 1.0;
631 
632     Size aUnscaledSize(16, 16);
633 
634     if (eToolBoxButtonSize == ToolBoxButtonSize::Large)
635     {
636         OUString iconTheme = Application::GetSettings().GetStyleSettings().DetermineIconTheme();
637         aUnscaledSize = vcl::IconThemeInfo::SizeByThemeName(iconTheme);
638     }
639     else if (eToolBoxButtonSize == ToolBoxButtonSize::Size32)
640     {
641         aUnscaledSize = Size(32, 32);
642     }
643     return Size(aUnscaledSize.Width()  * fScaleFactor,
644                 aUnscaledSize.Height() * fScaleFactor);
645 }
646 
GetDefaultImageSize() const647 Size ToolBox::GetDefaultImageSize() const
648 {
649     return GetDefaultImageSize(GetToolboxButtonSize());
650 }
651 
SetAlign(WindowAlign eNewAlign)652 void ToolBox::SetAlign( WindowAlign eNewAlign )
653 {
654     if ( meAlign != eNewAlign )
655     {
656         meAlign = eNewAlign;
657 
658         if ( !ImplIsFloatingMode() )
659         {
660             // set horizontal/vertical alignment
661             if ( (eNewAlign == WindowAlign::Left) || (eNewAlign == WindowAlign::Right) )
662                 mbHorz = false;
663             else
664                 mbHorz = true;
665 
666             // Update the background according to Persona if necessary
667             ImplInitSettings( false, false, true );
668 
669             // redraw everything, as the border has changed
670             mbCalc = true;
671             mbFormat = true;
672             if ( IsReallyVisible() && IsUpdateMode() )
673                 Invalidate();
674         }
675     }
676 }
677 
SetLineCount(ImplToolItems::size_type nNewLines)678 void ToolBox::SetLineCount( ImplToolItems::size_type nNewLines )
679 {
680     if ( !nNewLines )
681         nNewLines = 1;
682 
683     if ( mnLines != nNewLines )
684     {
685         mnLines = nNewLines;
686 
687         // better redraw everything, as otherwise there might be problems
688         // with regions that were copied with CopyBits
689         Invalidate();
690     }
691 }
692 
GetItemCount() const693 ToolBox::ImplToolItems::size_type ToolBox::GetItemCount() const
694 {
695     return mpData ? mpData->m_aItems.size() : 0;
696 }
697 
GetItemType(ImplToolItems::size_type nPos) const698 ToolBoxItemType ToolBox::GetItemType( ImplToolItems::size_type nPos ) const
699 {
700     return (nPos < mpData->m_aItems.size()) ? mpData->m_aItems[nPos].meType : ToolBoxItemType::DONTKNOW;
701 }
702 
GetItemPos(sal_uInt16 nItemId) const703 ToolBox::ImplToolItems::size_type ToolBox::GetItemPos( sal_uInt16 nItemId ) const
704 {
705     if (mpData)
706     {
707         ImplToolItems::size_type nCount = mpData->m_aItems.size();
708         for( ImplToolItems::size_type nPos = 0; nPos < nCount; nPos++ )
709             if( mpData->m_aItems[nPos].mnId == nItemId )
710                 return nPos;
711     }
712     return ITEM_NOTFOUND;
713 }
714 
GetItemPos(const Point & rPos) const715 ToolBox::ImplToolItems::size_type ToolBox::GetItemPos( const Point& rPos ) const
716 {
717     // search the item position on the given point
718     auto it = std::find_if(mpData->m_aItems.begin(), mpData->m_aItems.end(),
719         [&rPos](const ImplToolItem& rItem) { return rItem.maRect.IsInside( rPos ); });
720 
721     if( it != mpData->m_aItems.end() )
722         return std::distance(mpData->m_aItems.begin(), it);
723 
724     return ITEM_NOTFOUND;
725 }
726 
GetItemId(ImplToolItems::size_type nPos) const727 sal_uInt16 ToolBox::GetItemId( ImplToolItems::size_type nPos ) const
728 {
729     return (nPos < mpData->m_aItems.size()) ? mpData->m_aItems[nPos].mnId : 0;
730 }
731 
GetItemId(const Point & rPos) const732 sal_uInt16 ToolBox::GetItemId( const Point& rPos ) const
733 {
734     // find item that was clicked
735     auto it = std::find_if(mpData->m_aItems.begin(), mpData->m_aItems.end(),
736         [&rPos](const ImplToolItem& rItem) { return rItem.maRect.IsInside( rPos ); });
737 
738     if( (it != mpData->m_aItems.end()) && (it->meType == ToolBoxItemType::BUTTON) )
739         return it->mnId;
740 
741     return 0;
742 }
743 
GetItemContentSize(sal_uInt16 nItemId)744 Size ToolBox::GetItemContentSize( sal_uInt16 nItemId )
745 {
746     if ( mbCalc || mbFormat )
747         ImplFormat();
748 
749     ImplToolItems::size_type nPos = GetItemPos( nItemId );
750     if ( nPos < mpData->m_aItems.size() )
751         return mpData->m_aItems[nPos].maContentSize;
752     else
753         return Size();
754 }
755 
GetItemId(const OUString & rCommand) const756 sal_uInt16 ToolBox::GetItemId(const OUString &rCommand) const
757 {
758     if (!mpData)
759         return 0;
760 
761     auto it = std::find_if(mpData->m_aItems.begin(), mpData->m_aItems.end(),
762         [&rCommand](const ImplToolItem& rItem) { return rItem.maCommandStr == rCommand; });
763     if (it != mpData->m_aItems.end())
764         return it->mnId;
765 
766     return 0;
767 }
768 
ImplGetPopupPosition(const tools::Rectangle & rRect) const769 Point ToolBox::ImplGetPopupPosition( const tools::Rectangle& rRect ) const
770 {
771     Point aPos;
772     if( !rRect.IsEmpty() )
773     {
774         tools::Rectangle aScreen = GetDesktopRectPixel();
775 
776         // the popup should be positioned so that it will not cover
777         // the item rect and that it fits the desktop
778         // the preferred direction is always towards the center of
779         // the application window
780 
781         Point devPos;           // the position in device coordinates for screen comparison
782         switch( meAlign )
783         {
784             case WindowAlign::Top:
785                 aPos = rRect.BottomLeft();
786                 aPos.AdjustY( 1 );
787                 devPos = OutputToAbsoluteScreenPixel( aPos );
788                 if( devPos.Y() >= aScreen.Bottom() )
789                     aPos.setY( rRect.Top() );
790                 break;
791             case WindowAlign::Bottom:
792                 aPos = rRect.TopLeft();
793                 aPos.AdjustY( -1 );
794                 devPos = OutputToAbsoluteScreenPixel( aPos );
795                 if( devPos.Y() <= aScreen.Top() )
796                     aPos.setY( rRect.Bottom() );
797                 break;
798             case WindowAlign::Left:
799                 aPos = rRect.TopRight();
800                 aPos.AdjustX( 1 );
801                 devPos = OutputToAbsoluteScreenPixel( aPos );
802                 if( devPos.X() >= aScreen.Right() )
803                     aPos.setX( rRect.Left() );
804                 break;
805             case WindowAlign::Right:
806                 aPos = rRect.TopLeft();
807                 aPos.AdjustX( -1 );
808                 devPos = OutputToAbsoluteScreenPixel( aPos );
809                 if( devPos.X() <= aScreen.Left() )
810                     aPos.setX( rRect.Right() );
811                 break;
812             default:
813                 break;
814         }
815     }
816     return aPos;
817 }
818 
GetItemRect(sal_uInt16 nItemId)819 tools::Rectangle ToolBox::GetItemRect( sal_uInt16 nItemId )
820 {
821     if ( mbCalc || mbFormat )
822         ImplFormat();
823 
824     ImplToolItems::size_type nPos = GetItemPos( nItemId );
825     return GetItemPosRect( nPos );
826 }
827 
GetItemPosRect(ImplToolItems::size_type nPos)828 tools::Rectangle ToolBox::GetItemPosRect( ImplToolItems::size_type nPos )
829 {
830     if ( mbCalc || mbFormat )
831         ImplFormat();
832 
833     if ( nPos < mpData->m_aItems.size() )
834         return mpData->m_aItems[nPos].maRect;
835     else
836         return tools::Rectangle();
837 }
838 
GetOverflowRect() const839 tools::Rectangle const & ToolBox::GetOverflowRect() const
840 {
841     return mpData->maMenubuttonItem.maRect;
842 }
843 
ImplHasExternalMenubutton()844 bool ToolBox::ImplHasExternalMenubutton()
845 {
846     // check if the borderwindow (i.e. the decoration) provides the menu button
847     bool bRet = false;
848     if( ImplIsFloatingMode() )
849     {
850         // custom menu is placed in the decoration
851         ImplBorderWindow *pBorderWin = dynamic_cast<ImplBorderWindow*>( GetWindow( GetWindowType::Border ) );
852         if( pBorderWin && !pBorderWin->GetMenuRect().IsEmpty() )
853             bRet = true;
854     }
855     return bRet;
856 }
857 
SetItemBits(sal_uInt16 nItemId,ToolBoxItemBits nBits)858 void ToolBox::SetItemBits( sal_uInt16 nItemId, ToolBoxItemBits nBits )
859 {
860     ImplToolItems::size_type nPos = GetItemPos( nItemId );
861 
862     if ( nPos < GetItemCount() )
863     {
864         ToolBoxItemBits nOldBits = mpData->m_aItems[nPos].mnBits;
865         mpData->m_aItems[nPos].mnBits = nBits;
866         nBits &= ToolBoxItemBits::LEFT | ToolBoxItemBits::AUTOSIZE | ToolBoxItemBits::DROPDOWN;
867         nOldBits &= ToolBoxItemBits::LEFT | ToolBoxItemBits::AUTOSIZE | ToolBoxItemBits::DROPDOWN;
868         // trigger reformat when the item width has changed (dropdown arrow)
869         bool bFormat = ToolBoxItemBits(nBits & ToolBoxItemBits::DROPDOWN) != ToolBoxItemBits(nOldBits & ToolBoxItemBits::DROPDOWN);
870         if ( nBits != nOldBits )
871             ImplInvalidate( true, bFormat );
872     }
873 }
874 
GetItemBits(sal_uInt16 nItemId) const875 ToolBoxItemBits ToolBox::GetItemBits( sal_uInt16 nItemId ) const
876 {
877     ImplToolItem* pItem = ImplGetItem( nItemId );
878 
879     if ( pItem )
880         return pItem->mnBits;
881     else
882         return ToolBoxItemBits::NONE;
883 }
884 
SetItemExpand(sal_uInt16 nItemId,bool bExpand)885 void ToolBox::SetItemExpand( sal_uInt16 nItemId, bool bExpand )
886 {
887     ImplToolItem* pItem = ImplGetItem( nItemId );
888     if (!pItem)
889         return;
890 
891     if (pItem->mbExpand != bExpand)
892     {
893         pItem->mbExpand = bExpand;
894         ImplInvalidate(true, true);
895     }
896 }
897 
SetItemData(sal_uInt16 nItemId,void * pNewData)898 void ToolBox::SetItemData( sal_uInt16 nItemId, void* pNewData )
899 {
900     ImplToolItems::size_type nPos = GetItemPos( nItemId );
901 
902     if ( nPos < mpData->m_aItems.size() )
903     {
904         mpData->m_aItems[nPos].mpUserData = pNewData;
905         ImplUpdateItem( nPos );
906     }
907 }
908 
GetItemData(sal_uInt16 nItemId) const909 void* ToolBox::GetItemData( sal_uInt16 nItemId ) const
910 {
911     ImplToolItem* pItem = ImplGetItem( nItemId );
912 
913     if ( pItem )
914         return pItem->mpUserData;
915     else
916         return nullptr;
917 }
918 
SetItemImage(sal_uInt16 nItemId,const Image & rImage)919 void ToolBox::SetItemImage( sal_uInt16 nItemId, const Image& rImage )
920 {
921     ImplToolItems::size_type nPos = GetItemPos( nItemId );
922 
923     if ( nPos != ITEM_NOTFOUND )
924     {
925         ImplToolItem* pItem = &mpData->m_aItems[nPos];
926         Size aOldSize = pItem->maImage.GetSizePixel();
927 
928         pItem->maImage = rImage;
929 
930         // only once all is calculated, do extra work
931         if (!mbCalc)
932         {
933             if (aOldSize != pItem->maImage.GetSizePixel())
934                 ImplInvalidate( true );
935             else
936                 ImplUpdateItem( nPos );
937         }
938     }
939 }
940 
SetItemOverlayImage(sal_uInt16 nItemId,const Image & rImage)941 void ToolBox::SetItemOverlayImage( sal_uInt16 nItemId, const Image& rImage )
942 {
943     ImplToolItems::size_type nPos = GetItemPos( nItemId );
944 
945     if ( nPos != ITEM_NOTFOUND )
946     {
947         ImplToolItem* pItem = &mpData->m_aItems[nPos];
948         Size aOldSize = pItem->maOverlayImage.GetSizePixel();
949 
950         pItem->maOverlayImage = rImage;
951 
952         // only once all is calculated, do extra work
953         if (!mbCalc)
954         {
955             if (aOldSize != pItem->maOverlayImage.GetSizePixel())
956                 ImplInvalidate( true );
957             else
958                 ImplUpdateItem( nPos );
959         }
960     }
961 }
962 
ImplRotImage(const Image & rImage,long nAngle10)963 static Image ImplRotImage( const Image& rImage, long nAngle10 )
964 {
965     BitmapEx    aRotBitmapEx( rImage.GetBitmapEx() );
966 
967     aRotBitmapEx.Rotate( nAngle10, COL_WHITE );
968 
969     return Image( aRotBitmapEx );
970 }
971 
SetItemImageAngle(sal_uInt16 nItemId,long nAngle10)972 void ToolBox::SetItemImageAngle( sal_uInt16 nItemId, long nAngle10 )
973 {
974     ImplToolItems::size_type nPos = GetItemPos( nItemId );
975 
976     if ( nPos != ITEM_NOTFOUND )
977     {
978         ImplToolItem* pItem = &mpData->m_aItems[nPos];
979         Size aOldSize = pItem->maImage.GetSizePixel();
980 
981         long nDeltaAngle = (nAngle10 - pItem->mnImageAngle) % 3600;
982         while( nDeltaAngle < 0 )
983             nDeltaAngle += 3600;
984 
985         pItem->mnImageAngle = nAngle10;
986         if( nDeltaAngle && !!pItem->maImage )
987         {
988             pItem->maImage = ImplRotImage( pItem->maImage, nDeltaAngle );
989         }
990 
991         if (!mbCalc)
992         {
993             if (aOldSize != pItem->maImage.GetSizePixel())
994                 ImplInvalidate(true);
995             else
996                 ImplUpdateItem(nPos);
997         }
998     }
999 }
1000 
ImplMirrorImage(const Image & rImage)1001 static Image ImplMirrorImage( const Image& rImage )
1002 {
1003     BitmapEx    aMirrBitmapEx( rImage.GetBitmapEx() );
1004 
1005     aMirrBitmapEx.Mirror( BmpMirrorFlags::Horizontal );
1006 
1007     return Image( aMirrBitmapEx );
1008 }
1009 
SetItemImageMirrorMode(sal_uInt16 nItemId,bool bMirror)1010 void ToolBox::SetItemImageMirrorMode( sal_uInt16 nItemId, bool bMirror )
1011 {
1012     ImplToolItems::size_type nPos = GetItemPos( nItemId );
1013 
1014     if ( nPos != ITEM_NOTFOUND )
1015     {
1016         ImplToolItem* pItem = &mpData->m_aItems[nPos];
1017 
1018         if (pItem->mbMirrorMode != bMirror)
1019         {
1020             pItem->mbMirrorMode = bMirror;
1021             if (!!pItem->maImage)
1022             {
1023                 pItem->maImage = ImplMirrorImage(pItem->maImage);
1024             }
1025 
1026             if (!mbCalc)
1027                 ImplUpdateItem(nPos);
1028         }
1029     }
1030 }
1031 
GetItemImage(sal_uInt16 nItemId) const1032 Image ToolBox::GetItemImage(sal_uInt16 nItemId) const
1033 {
1034     ImplToolItem* pItem = ImplGetItem(nItemId);
1035     return pItem ? pItem->maImage : Image();
1036 }
1037 
SetItemText(sal_uInt16 nItemId,const OUString & rText)1038 void ToolBox::SetItemText( sal_uInt16 nItemId, const OUString& rText )
1039 {
1040     ImplToolItems::size_type nPos = GetItemPos( nItemId );
1041 
1042     if ( nPos != ITEM_NOTFOUND )
1043     {
1044         ImplToolItem* pItem = &mpData->m_aItems[nPos];
1045         // only once all is calculated, do extra work
1046         if ( !mbCalc &&
1047              ((meButtonType != ButtonType::SYMBOLONLY) || !pItem->maImage) )
1048         {
1049             long nOldWidth = GetCtrlTextWidth( pItem->maText );
1050             pItem->maText = MnemonicGenerator::EraseAllMnemonicChars(rText);
1051             mpData->ImplClearLayoutData();
1052             if ( nOldWidth != GetCtrlTextWidth( pItem->maText ) )
1053                 ImplInvalidate( true );
1054             else
1055                 ImplUpdateItem( nPos );
1056         }
1057         else
1058             pItem->maText = MnemonicGenerator::EraseAllMnemonicChars(rText);
1059 
1060         // Notify button changed event to prepare accessibility bridge
1061         CallEventListeners( VclEventId::ToolboxButtonStateChanged, reinterpret_cast< void* >( nPos ) );
1062 
1063         // Notify
1064         CallEventListeners( VclEventId::ToolboxItemTextChanged, reinterpret_cast< void* >( nPos ) );
1065     }
1066 }
1067 
GetItemText(sal_uInt16 nItemId) const1068 const OUString& ToolBox::GetItemText( sal_uInt16 nItemId ) const
1069 {
1070 
1071     ImplToolItem* pItem = ImplGetItem( nItemId );
1072 
1073     assert( pItem );
1074 
1075     return pItem->maText;
1076 }
1077 
SetItemWindow(sal_uInt16 nItemId,vcl::Window * pNewWindow)1078 void ToolBox::SetItemWindow( sal_uInt16 nItemId, vcl::Window* pNewWindow )
1079 {
1080     ImplToolItems::size_type nPos = GetItemPos( nItemId );
1081 
1082     if ( nPos != ITEM_NOTFOUND )
1083     {
1084         ImplToolItem* pItem = &mpData->m_aItems[nPos];
1085         pItem->mpWindow = pNewWindow;
1086         if ( pNewWindow )
1087             pNewWindow->Hide();
1088         ImplInvalidate( true );
1089         CallEventListeners( VclEventId::ToolboxItemWindowChanged, reinterpret_cast< void* >( nPos ) );
1090     }
1091 }
1092 
GetItemWindow(sal_uInt16 nItemId) const1093 vcl::Window* ToolBox::GetItemWindow( sal_uInt16 nItemId ) const
1094 {
1095     ImplToolItem* pItem = ImplGetItem( nItemId );
1096 
1097     if ( pItem )
1098         return pItem->mpWindow;
1099     else
1100         return nullptr;
1101 }
1102 
StartSelection()1103 void ToolBox::StartSelection()
1104 {
1105     if ( mbDrag )
1106         EndSelection();
1107 
1108     if ( !mbSelection )
1109     {
1110         mbSelection  = true;
1111         mnCurPos     = ITEM_NOTFOUND;
1112         mnCurItemId  = 0;
1113         Activate();
1114     }
1115 }
1116 
EndSelection()1117 void ToolBox::EndSelection()
1118 {
1119     if ( mbDrag || mbSelection )
1120     {
1121         // reset
1122         mbDrag = false;
1123         mbSelection = false;
1124         if (mnCurPos != ITEM_NOTFOUND)
1125             InvalidateItem(mnCurPos);
1126         EndTracking();
1127         if (IsMouseCaptured())
1128             ReleaseMouse();
1129         Deactivate();
1130     }
1131 
1132     mnCurPos        = ITEM_NOTFOUND;
1133     mnCurItemId     = 0;
1134     mnDownItemId    = 0;
1135     mnMouseModifier = 0;
1136 }
1137 
SetItemDown(sal_uInt16 nItemId,bool bDown)1138 void ToolBox::SetItemDown( sal_uInt16 nItemId, bool bDown )
1139 {
1140     ImplToolItems::size_type nPos = GetItemPos( nItemId );
1141 
1142     if ( nPos != ITEM_NOTFOUND )
1143     {
1144         if ( bDown )
1145         {
1146             if ( nPos != mnCurPos )
1147             {
1148                 mnCurPos = nPos;
1149                 InvalidateItem(mnCurPos);
1150                 Flush();
1151             }
1152         }
1153         else
1154         {
1155             if ( nPos == mnCurPos )
1156             {
1157                 InvalidateItem(mnCurPos);
1158                 Flush();
1159                 mnCurPos = ITEM_NOTFOUND;
1160             }
1161         }
1162 
1163         if ( mbDrag || mbSelection )
1164         {
1165             mbDrag = false;
1166             mbSelection = false;
1167             EndTracking();
1168             if (IsMouseCaptured())
1169                 ReleaseMouse();
1170             Deactivate();
1171         }
1172 
1173         mnCurItemId     = 0;
1174         mnDownItemId    = 0;
1175         mnMouseModifier = 0;
1176     }
1177 }
1178 
SetItemState(sal_uInt16 nItemId,TriState eState)1179 void ToolBox::SetItemState( sal_uInt16 nItemId, TriState eState )
1180 {
1181     ImplToolItems::size_type nPos = GetItemPos( nItemId );
1182 
1183     if ( nPos != ITEM_NOTFOUND )
1184     {
1185         ImplToolItem* pItem = &mpData->m_aItems[nPos];
1186 
1187         // the state has changed
1188         if ( pItem->meState != eState )
1189         {
1190             // if RadioCheck, un-check the previous
1191             if ( (eState == TRISTATE_TRUE) && (pItem->mnBits & ToolBoxItemBits::AUTOCHECK) &&
1192                  (pItem->mnBits & ToolBoxItemBits::RADIOCHECK) )
1193             {
1194                 ImplToolItem*    pGroupItem;
1195                 ImplToolItems::size_type nGroupPos;
1196                 ImplToolItems::size_type nItemCount = GetItemCount();
1197 
1198                 nGroupPos = nPos;
1199                 while ( nGroupPos )
1200                 {
1201                     pGroupItem = &mpData->m_aItems[nGroupPos-1];
1202                     if ( pGroupItem->mnBits & ToolBoxItemBits::RADIOCHECK )
1203                     {
1204                         if ( pGroupItem->meState != TRISTATE_FALSE )
1205                             SetItemState( pGroupItem->mnId, TRISTATE_FALSE );
1206                     }
1207                     else
1208                         break;
1209                     nGroupPos--;
1210                 }
1211 
1212                 nGroupPos = nPos+1;
1213                 while ( nGroupPos < nItemCount )
1214                 {
1215                     pGroupItem = &mpData->m_aItems[nGroupPos];
1216                     if ( pGroupItem->mnBits & ToolBoxItemBits::RADIOCHECK )
1217                     {
1218                         if ( pGroupItem->meState != TRISTATE_FALSE )
1219                             SetItemState( pGroupItem->mnId, TRISTATE_FALSE );
1220                     }
1221                     else
1222                         break;
1223                     nGroupPos++;
1224                 }
1225             }
1226 
1227             pItem->meState = eState;
1228             ImplUpdateItem( nPos );
1229 
1230             // Notify button changed event to prepare accessibility bridge
1231             CallEventListeners( VclEventId::ToolboxButtonStateChanged, reinterpret_cast< void* >( nPos ) );
1232 
1233             // Call accessible listener to notify state_changed event
1234             CallEventListeners( VclEventId::ToolboxItemUpdated, reinterpret_cast< void* >(nPos) );
1235         }
1236     }
1237 }
1238 
GetItemState(sal_uInt16 nItemId) const1239 TriState ToolBox::GetItemState( sal_uInt16 nItemId ) const
1240 {
1241     ImplToolItem* pItem = ImplGetItem( nItemId );
1242 
1243     if ( pItem )
1244         return pItem->meState;
1245     else
1246         return TRISTATE_FALSE;
1247 }
1248 
EnableItem(sal_uInt16 nItemId,bool bEnable)1249 void ToolBox::EnableItem( sal_uInt16 nItemId, bool bEnable )
1250 {
1251     ImplToolItems::size_type nPos = GetItemPos( nItemId );
1252 
1253     if ( nPos != ITEM_NOTFOUND )
1254     {
1255         ImplToolItem* pItem = &mpData->m_aItems[nPos];
1256         if ( pItem->mbEnabled != bEnable )
1257         {
1258             pItem->mbEnabled = bEnable;
1259 
1260             // if existing, also redraw the window
1261             if ( pItem->mpWindow )
1262                 pItem->mpWindow->Enable( pItem->mbEnabled );
1263 
1264             // update item
1265             ImplUpdateItem( nPos );
1266 
1267             ImplUpdateInputEnable();
1268 
1269             // Notify button changed event to prepare accessibility bridge
1270             CallEventListeners( VclEventId::ToolboxButtonStateChanged, reinterpret_cast< void* >( nPos ) );
1271 
1272             CallEventListeners( bEnable ? VclEventId::ToolboxItemEnabled : VclEventId::ToolboxItemDisabled, reinterpret_cast< void* >( nPos ) );
1273         }
1274     }
1275 }
1276 
IsItemEnabled(sal_uInt16 nItemId) const1277 bool ToolBox::IsItemEnabled( sal_uInt16 nItemId ) const
1278 {
1279     ImplToolItem* pItem = ImplGetItem( nItemId );
1280 
1281     if ( pItem )
1282         return pItem->mbEnabled;
1283     else
1284         return false;
1285 }
1286 
ShowItem(sal_uInt16 nItemId,bool bVisible)1287 void ToolBox::ShowItem( sal_uInt16 nItemId, bool bVisible )
1288 {
1289     ImplToolItems::size_type nPos = GetItemPos( nItemId );
1290     mpData->ImplClearLayoutData();
1291 
1292     if ( nPos != ITEM_NOTFOUND )
1293     {
1294         ImplToolItem* pItem = &mpData->m_aItems[nPos];
1295         if ( pItem->mbVisible != bVisible )
1296         {
1297             pItem->mbVisible = bVisible;
1298             ImplInvalidate();
1299         }
1300     }
1301 }
1302 
IsItemClipped(sal_uInt16 nItemId) const1303 bool ToolBox::IsItemClipped( sal_uInt16 nItemId ) const
1304 {
1305     ImplToolItem* pItem = ImplGetItem( nItemId );
1306 
1307     if ( pItem )
1308         return pItem->IsClipped();
1309     else
1310         return false;
1311 }
1312 
IsItemVisible(sal_uInt16 nItemId) const1313 bool ToolBox::IsItemVisible( sal_uInt16 nItemId ) const
1314 {
1315     ImplToolItem* pItem = ImplGetItem( nItemId );
1316 
1317     if ( pItem )
1318         return pItem->mbVisible;
1319     else
1320         return false;
1321 }
1322 
IsItemReallyVisible(sal_uInt16 nItemId) const1323 bool ToolBox::IsItemReallyVisible( sal_uInt16 nItemId ) const
1324 {
1325     // is the item on the visible area of the toolbox?
1326     bool bRet = false;
1327     tools::Rectangle aRect( mnLeftBorder, mnTopBorder, mnDX-mnRightBorder, mnDY-mnBottomBorder );
1328     ImplToolItem* pItem = ImplGetItem( nItemId );
1329 
1330     if ( pItem && pItem->mbVisible &&
1331          !pItem->maRect.IsEmpty() && aRect.IsOver( pItem->maRect ) )
1332     {
1333         bRet = true;
1334     }
1335 
1336     return bRet;
1337 }
1338 
SetItemCommand(sal_uInt16 nItemId,const OUString & rCommand)1339 void ToolBox::SetItemCommand(sal_uInt16 nItemId, const OUString& rCommand)
1340 {
1341     ImplToolItem* pItem = ImplGetItem( nItemId );
1342 
1343     if (pItem)
1344         pItem->maCommandStr = rCommand;
1345 }
1346 
GetItemCommand(sal_uInt16 nItemId) const1347 OUString ToolBox::GetItemCommand( sal_uInt16 nItemId ) const
1348 {
1349     ImplToolItem* pItem = ImplGetItem( nItemId );
1350 
1351     if (pItem)
1352         return pItem->maCommandStr;
1353 
1354     return OUString();
1355 }
1356 
SetQuickHelpText(sal_uInt16 nItemId,const OUString & rText)1357 void ToolBox::SetQuickHelpText( sal_uInt16 nItemId, const OUString& rText )
1358 {
1359     ImplToolItem* pItem = ImplGetItem( nItemId );
1360 
1361     if ( pItem )
1362         pItem->maQuickHelpText = rText;
1363 }
1364 
GetQuickHelpText(sal_uInt16 nItemId) const1365 OUString ToolBox::GetQuickHelpText( sal_uInt16 nItemId ) const
1366 {
1367     ImplToolItem* pItem = ImplGetItem( nItemId );
1368 
1369     if ( pItem )
1370         return pItem->maQuickHelpText;
1371     else
1372         return OUString();
1373 }
1374 
SetHelpText(sal_uInt16 nItemId,const OUString & rText)1375 void ToolBox::SetHelpText( sal_uInt16 nItemId, const OUString& rText )
1376 {
1377     ImplToolItem* pItem = ImplGetItem( nItemId );
1378 
1379     if ( pItem )
1380         pItem->maHelpText = rText;
1381 }
1382 
GetHelpText(sal_uInt16 nItemId) const1383 const OUString& ToolBox::GetHelpText( sal_uInt16 nItemId ) const
1384 {
1385     return ImplGetHelpText( nItemId );
1386 }
1387 
SetHelpId(sal_uInt16 nItemId,const OString & rHelpId)1388 void ToolBox::SetHelpId( sal_uInt16 nItemId, const OString& rHelpId )
1389 {
1390     ImplToolItem* pItem = ImplGetItem( nItemId );
1391 
1392     if ( pItem )
1393         pItem->maHelpId = rHelpId;
1394 }
1395 
SetOutStyle(sal_uInt16 nNewStyle)1396 void ToolBox::SetOutStyle( sal_uInt16 nNewStyle )
1397 {
1398     // always force flat looking toolbars since NWF
1399     nNewStyle |= TOOLBOX_STYLE_FLAT;
1400 
1401     if ( mnOutStyle != nNewStyle )
1402     {
1403         mnOutStyle = nNewStyle;
1404         ImplDisableFlatButtons();
1405 
1406         // so as to redo the ButtonDevice
1407         if ( !(mnOutStyle & TOOLBOX_STYLE_FLAT) )
1408         {
1409             mnMaxItemWidth  = 1;
1410             mnMaxItemHeight = 1;
1411         }
1412 
1413         ImplInvalidate( true, true );
1414     }
1415 }
1416 
1417 // disable key input if all items are disabled
ImplUpdateInputEnable()1418 void ToolBox::ImplUpdateInputEnable()
1419 {
1420     mpData->mbKeyInputDisabled = std::none_of(mpData->m_aItems.begin(), mpData->m_aItems.end(),
1421         [](const ImplToolItem& rItem) {
1422             // at least one useful entry
1423             return rItem.mbEnabled;
1424         });
1425 }
1426 
ImplFillLayoutData()1427 void ToolBox::ImplFillLayoutData()
1428 {
1429     mpData->m_pLayoutData.reset(new ToolBoxLayoutData);
1430 
1431     ImplToolItems::size_type nCount = mpData->m_aItems.size();
1432     for( ImplToolItems::size_type i = 0; i < nCount; i++ )
1433     {
1434         ImplToolItem* pItem = &mpData->m_aItems[i];
1435 
1436         // only draw, if the rectangle is within PaintRectangle
1437         if (!pItem->maRect.IsEmpty())
1438             InvalidateItem(i);
1439     }
1440 }
1441 
GetDisplayText() const1442 OUString ToolBox::GetDisplayText() const
1443 {
1444     if( ! mpData->m_pLayoutData )
1445         const_cast<ToolBox *>(this)->ImplFillLayoutData();
1446     return mpData->m_pLayoutData ? mpData->m_pLayoutData->m_aDisplayText : OUString();
1447 }
1448 
GetCharacterBounds(sal_uInt16 nItemID,long nIndex)1449 tools::Rectangle ToolBox::GetCharacterBounds( sal_uInt16 nItemID, long nIndex )
1450 {
1451     long nItemIndex = -1;
1452     if( ! mpData->m_pLayoutData )
1453         ImplFillLayoutData();
1454     if( mpData->m_pLayoutData )
1455     {
1456         for( sal_uLong i = 0; i < mpData->m_pLayoutData->m_aLineItemIds.size(); i++ )
1457         {
1458             if( mpData->m_pLayoutData->m_aLineItemIds[i] == nItemID )
1459             {
1460                 nItemIndex = mpData->m_pLayoutData->m_aLineIndices[i];
1461                 break;
1462             }
1463         }
1464     }
1465     return (mpData->m_pLayoutData && nItemIndex != -1) ? mpData->m_pLayoutData->GetCharacterBounds( nItemIndex+nIndex ) : tools::Rectangle();
1466 }
1467 
GetIndexForPoint(const Point & rPoint,sal_uInt16 & rItemID)1468 long ToolBox::GetIndexForPoint( const Point& rPoint, sal_uInt16& rItemID )
1469 {
1470     long nIndex = -1;
1471     rItemID = 0;
1472     if( ! mpData->m_pLayoutData )
1473         ImplFillLayoutData();
1474     if( mpData->m_pLayoutData )
1475     {
1476         nIndex = mpData->m_pLayoutData->GetIndexForPoint( rPoint );
1477         for( sal_uLong i = 0; i < mpData->m_pLayoutData->m_aLineIndices.size(); i++ )
1478         {
1479             if( mpData->m_pLayoutData->m_aLineIndices[i] <= nIndex &&
1480                 (i == mpData->m_pLayoutData->m_aLineIndices.size()-1 || mpData->m_pLayoutData->m_aLineIndices[i+1] > nIndex) )
1481             {
1482                 rItemID = mpData->m_pLayoutData->m_aLineItemIds[i];
1483                 break;
1484             }
1485         }
1486     }
1487     return nIndex;
1488 }
1489 
SetDropdownClickHdl(const Link<ToolBox *,void> & rLink)1490 void ToolBox::SetDropdownClickHdl( const Link<ToolBox *, void>& rLink )
1491 {
1492     if (mpData != nullptr) {
1493         mpData->maDropdownClickHdl = rLink;
1494     }
1495 }
1496 
SetMenuType(ToolBoxMenuType aType)1497 void ToolBox::SetMenuType( ToolBoxMenuType aType )
1498 {
1499     if( aType != mpData->maMenuType )
1500     {
1501         mpData->maMenuType = aType;
1502         if( IsFloatingMode() )
1503         {
1504             // the menu button may have to be moved into the decoration which changes the layout
1505             ImplDockingWindowWrapper *pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper( this );
1506             if( pWrapper )
1507                 pWrapper->ShowTitleButton( TitleButton::Menu, bool( aType & ToolBoxMenuType::Customize) );
1508 
1509             mbFormat = true;
1510             ImplFormat();
1511             ImplSetMinMaxFloatSize();
1512         }
1513         else
1514         {
1515             // trigger redraw of menu button
1516             if( !mpData->maMenubuttonItem.maRect.IsEmpty() )
1517                 Invalidate(mpData->maMenubuttonItem.maRect);
1518         }
1519     }
1520 }
1521 
GetMenuType() const1522 ToolBoxMenuType ToolBox::GetMenuType() const
1523 {
1524     return mpData->maMenuType;
1525 }
1526 
IsMenuEnabled() const1527 bool ToolBox::IsMenuEnabled() const
1528 {
1529     return mpData->maMenuType != ToolBoxMenuType::NONE;
1530 }
1531 
GetMenu() const1532 PopupMenu* ToolBox::GetMenu() const
1533 {
1534     return mpData == nullptr ? nullptr : mpData->mpMenu;
1535 }
1536 
SetMenuExecuteHdl(const Link<ToolBox *,void> & rLink)1537 void ToolBox::SetMenuExecuteHdl( const Link<ToolBox *, void>& rLink )
1538 {
1539     mpData->maMenuButtonHdl = rLink;
1540 }
1541 
ImplHasClippedItems()1542 bool ToolBox::ImplHasClippedItems()
1543 {
1544     // are any items currently clipped ?
1545     ImplFormat();
1546     return std::any_of(mpData->m_aItems.begin(), mpData->m_aItems.end(),
1547         [](const ImplToolItem& rItem) { return rItem.IsClipped(); });
1548 }
1549 
1550 namespace
1551 {
ConvertBitsFromToolBoxToMenu(ToolBoxItemBits nToolItemBits)1552     MenuItemBits ConvertBitsFromToolBoxToMenu(ToolBoxItemBits nToolItemBits)
1553     {
1554         MenuItemBits nMenuItemBits = MenuItemBits::NONE;
1555         if ((nToolItemBits & ToolBoxItemBits::CHECKABLE) ||
1556             (nToolItemBits & ToolBoxItemBits::DROPDOWN))
1557         {
1558             nMenuItemBits |= MenuItemBits::CHECKABLE;
1559         }
1560         return nMenuItemBits;
1561     }
1562 }
1563 
UpdateCustomMenu()1564 void ToolBox::UpdateCustomMenu()
1565 {
1566     // fill clipped items into menu
1567     PopupMenu *pMenu = GetMenu();
1568     pMenu->Clear();
1569 
1570     // add menu items: first the overflow items, then hidden items, both in the
1571     // order they would usually appear in the toolbar. Separators that would be
1572     // in the toolbar are ignored as they would introduce too much clutter,
1573     // instead we have a single separator to help distinguish between overflow
1574     // and hidden items.
1575     if ( !mpData->m_aItems.empty() )
1576     {
1577         // nStartPos will hold the number of clipped items appended from first loop
1578         for ( const auto& rItem : mpData->m_aItems )
1579         {
1580             if( rItem.IsClipped() )
1581             {
1582                 sal_uInt16 id = rItem.mnId + TOOLBOX_MENUITEM_START;
1583                 MenuItemBits nMenuItemBits = ConvertBitsFromToolBoxToMenu(rItem.mnBits);
1584                 pMenu->InsertItem( id, rItem.maText, rItem.maImage, nMenuItemBits);
1585                 pMenu->SetItemCommand( id, rItem.maCommandStr );
1586                 pMenu->EnableItem( id, rItem.mbEnabled );
1587                 pMenu->CheckItem ( id, rItem.meState == TRISTATE_TRUE );
1588             }
1589         }
1590 
1591         // add a separator below the inserted clipped-items
1592         pMenu->InsertSeparator();
1593 
1594         // now append the items that are explicitly disabled
1595         for ( const auto& rItem : mpData->m_aItems )
1596         {
1597             if( rItem.IsItemHidden() )
1598             {
1599                 sal_uInt16 id = rItem.mnId + TOOLBOX_MENUITEM_START;
1600                 MenuItemBits nMenuItemBits = ConvertBitsFromToolBoxToMenu(rItem.mnBits);
1601                 pMenu->InsertItem( id, rItem.maText, rItem.maImage, nMenuItemBits );
1602                 pMenu->SetItemCommand( id, rItem.maCommandStr );
1603                 pMenu->EnableItem( id, rItem.mbEnabled );
1604                 pMenu->CheckItem( id, rItem.meState == TRISTATE_TRUE );
1605             }
1606         }
1607 
1608     }
1609 }
1610 
IMPL_LINK(ToolBox,ImplCustomMenuListener,VclMenuEvent &,rEvent,void)1611 IMPL_LINK( ToolBox, ImplCustomMenuListener, VclMenuEvent&, rEvent, void )
1612 {
1613     if( rEvent.GetMenu() == GetMenu() && rEvent.GetId() == VclEventId::MenuSelect )
1614     {
1615         sal_uInt16 id = GetMenu()->GetItemId( rEvent.GetItemPos() );
1616         if( id >= TOOLBOX_MENUITEM_START )
1617             TriggerItem( id - TOOLBOX_MENUITEM_START );
1618     }
1619 }
1620 
ExecuteCustomMenu(const tools::Rectangle & rRect)1621 void ToolBox::ExecuteCustomMenu( const tools::Rectangle& rRect )
1622 {
1623     if ( !IsMenuEnabled() || ImplIsInPopupMode() )
1624         return;
1625 
1626     UpdateCustomMenu();
1627 
1628     if( GetMenuType() & ToolBoxMenuType::Customize )
1629         // call button handler to allow for menu customization
1630         mpData->maMenuButtonHdl.Call( this );
1631 
1632     GetMenu()->AddEventListener( LINK( this, ToolBox, ImplCustomMenuListener ) );
1633 
1634     // make sure all disabled entries will be shown
1635     GetMenu()->SetMenuFlags(
1636         GetMenu()->GetMenuFlags() | MenuFlags::AlwaysShowDisabledEntries );
1637 
1638     // toolbox might be destroyed during execute
1639     bool bBorderDel = false;
1640 
1641     VclPtr<vcl::Window> pWin = this;
1642     tools::Rectangle aMenuRect = rRect;
1643     VclPtr<ImplBorderWindow> pBorderWin;
1644     if( aMenuRect.IsEmpty() && IsFloatingMode() )
1645     {
1646         // custom menu is placed in the decoration
1647         pBorderWin = dynamic_cast<ImplBorderWindow*>( GetWindow( GetWindowType::Border ) );
1648         if( pBorderWin && !pBorderWin->GetMenuRect().IsEmpty() )
1649         {
1650             pWin = pBorderWin;
1651             aMenuRect = pBorderWin->GetMenuRect();
1652             bBorderDel = true;
1653         }
1654     }
1655 
1656     sal_uInt16 uId = GetMenu()->Execute( pWin, tools::Rectangle( ImplGetPopupPosition( aMenuRect ), Size() ),
1657                             PopupMenuFlags::ExecuteDown | PopupMenuFlags::NoMouseUpClose );
1658 
1659     if ( pWin->IsDisposed() )
1660         return;
1661 
1662     if( GetMenu() )
1663         GetMenu()->RemoveEventListener( LINK( this, ToolBox, ImplCustomMenuListener ) );
1664     if( bBorderDel )
1665     {
1666         if( pBorderWin->IsDisposed() )
1667             return;
1668     }
1669 
1670     pWin->Invalidate( aMenuRect );
1671 
1672     if( uId )
1673         GrabFocusToDocument();
1674 }
1675 
1676 // checks override first, useful during calculation of sizes
ImplIsFloatingMode() const1677 bool ToolBox::ImplIsFloatingMode() const
1678 {
1679     SAL_WARN_IF( mpData->mbAssumeDocked && mpData->mbAssumeFloating, "vcl",
1680         "cannot assume docked and floating" );
1681 
1682     if( mpData->mbAssumeDocked )
1683         return false;
1684     else if( mpData->mbAssumeFloating )
1685         return true;
1686     else
1687         return IsFloatingMode();
1688 }
1689 
1690 // checks override first, useful during calculation of sizes
ImplIsInPopupMode() const1691 bool ToolBox::ImplIsInPopupMode() const
1692 {
1693     if( mpData->mbAssumePopupMode )
1694         return true;
1695     else
1696     {
1697         ImplDockingWindowWrapper *pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper( this );
1698         return ( pWrapper && pWrapper->GetFloatingWindow() && pWrapper->GetFloatingWindow()->IsInPopupMode() );
1699     }
1700 }
1701 
Lock(bool bLock)1702 void ToolBox::Lock( bool bLock )
1703 {
1704     ImplDockingWindowWrapper *pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper( this );
1705     if( !pWrapper )
1706         return;
1707     if( mpData->mbIsLocked != bLock )
1708     {
1709         mpData->mbIsLocked = bLock;
1710         if( !ImplIsFloatingMode() )
1711         {
1712             mbCalc = true;
1713             mbFormat = true;
1714             SetSizePixel( CalcWindowSizePixel(1) );
1715             Invalidate();
1716         }
1717     }
1718 }
1719 
AlwaysLocked()1720 bool ToolBox::AlwaysLocked()
1721 {
1722     // read config item to determine toolbox behaviour, used for subtoolbars
1723 
1724     static int nAlwaysLocked = -1;
1725 
1726     if( nAlwaysLocked == -1 )
1727     {
1728         nAlwaysLocked = 0; // ask configuration only once
1729 
1730         utl::OConfigurationNode aNode = utl::OConfigurationTreeRoot::tryCreateWithComponentContext(
1731             comphelper::getProcessComponentContext(),
1732             "/org.openoffice.Office.UI.GlobalSettings/Toolbars" );    // note: case sensitive !
1733         if ( aNode.isValid() )
1734         {
1735             // feature enabled ?
1736             bool bStatesEnabled = bool();
1737             css::uno::Any aValue = aNode.getNodeValue( "StatesEnabled" );
1738             if( aValue >>= bStatesEnabled )
1739             {
1740                 if( bStatesEnabled )
1741                 {
1742                     // now read the locking state
1743                     utl::OConfigurationNode aNode2 = utl::OConfigurationTreeRoot::tryCreateWithComponentContext(
1744                         comphelper::getProcessComponentContext(),
1745                         "/org.openoffice.Office.UI.GlobalSettings/Toolbars/States" );    // note: case sensitive !
1746 
1747                     bool bLocked = bool();
1748                     css::uno::Any aValue2 = aNode2.getNodeValue( "Locked" );
1749                     if( aValue2 >>= bLocked )
1750                         nAlwaysLocked = bLocked ? 1 : 0;
1751                 }
1752             }
1753         }
1754     }
1755 
1756     return nAlwaysLocked == 1;
1757 }
1758 
WillUsePopupMode() const1759 bool ToolBox::WillUsePopupMode() const
1760 {
1761     return mpData->mbWillUsePopupMode;
1762 }
1763 
WillUsePopupMode(bool b)1764 void ToolBox::WillUsePopupMode( bool b )
1765 {
1766     mpData->mbWillUsePopupMode = b;
1767 }
1768 
DumpAsPropertyTree()1769 boost::property_tree::ptree ToolBox::DumpAsPropertyTree()
1770 {
1771     boost::property_tree::ptree aTree(DockingWindow::DumpAsPropertyTree());
1772     boost::property_tree::ptree aChildren;
1773 
1774     boost::property_tree::ptree::const_assoc_iterator found = aTree.find("children");
1775     if (found == aTree.not_found())
1776     {
1777         for (ToolBox::ImplToolItems::size_type i = 0; i < GetItemCount(); ++i)
1778         {
1779             ToolBoxItemType type = GetItemType(i);
1780             if (type == ToolBoxItemType::BUTTON)
1781             {
1782                 boost::property_tree::ptree aEntry;
1783                 int nId = GetItemId(i);
1784                 aEntry.put("type", "toolitem");
1785                 aEntry.put("text", GetItemText(nId));
1786                 aEntry.put("command", GetItemCommand(nId));
1787                 aChildren.push_back(std::make_pair("", aEntry));
1788             }
1789         }
1790 
1791         aTree.add_child("children", aChildren);
1792     }
1793 
1794     return aTree;
1795 }
1796 
1797 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1798