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 <utility>
22 #include <vcl/uitest/logger.hxx>
23 #include <sal/log.hxx>
24
25 #include <comphelper/base64.hxx>
26 #include <comphelper/processfactory.hxx>
27 #include <boost/property_tree/ptree.hpp>
28
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>
40
41 #include <svdata.hxx>
42 #include <brdwin.hxx>
43 #include <toolbox.h>
44
45 #include <unotools/confignode.hxx>
46 #include <tools/json_writer.hxx>
47
48 #include <vcl/uitest/uiobject.hxx>
49
50 #include "impldockingwrapper.hxx"
51
52 using namespace vcl;
53
54 #define TB_SEP_SIZE 8 // Separator size
55
56
ImplToolBoxPrivateData()57 ImplToolBoxPrivateData::ImplToolBoxPrivateData()
58 {
59 meButtonSize = ToolBoxButtonSize::DontCare;
60 mpMenu = VclPtr<PopupMenu>::Create();
61
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;
66
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;
78 }
79
~ImplToolBoxPrivateData()80 ImplToolBoxPrivateData::~ImplToolBoxPrivateData()
81 {
82 m_pLayoutData.reset();
83 mpMenu.disposeAndClear();
84 }
85
init(ToolBoxItemId nItemId,ToolBoxItemBits nItemBits,bool bEmptyBtn)86 void ImplToolItem::init(ToolBoxItemId nItemId, ToolBoxItemBits nItemBits,
87 bool bEmptyBtn)
88 {
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;
107 }
108
ImplToolItem()109 ImplToolItem::ImplToolItem()
110 {
111 init(ToolBoxItemId(0), ToolBoxItemBits::NONE, true);
112 }
113
ImplToolItem(ToolBoxItemId nItemId,Image aImage,ToolBoxItemBits nItemBits)114 ImplToolItem::ImplToolItem( ToolBoxItemId nItemId, Image aImage,
115 ToolBoxItemBits nItemBits ) :
116 maImage(std::move( aImage ))
117 {
118 init(nItemId, nItemBits, false);
119 }
120
ImplToolItem(ToolBoxItemId nItemId,OUString aText,OUString aCommand,ToolBoxItemBits nItemBits)121 ImplToolItem::ImplToolItem( ToolBoxItemId nItemId, OUString aText,
122 OUString aCommand, ToolBoxItemBits nItemBits ) :
123 maText(std::move( aText )),
124 maCommandStr(std::move( aCommand ))
125 {
126 init(nItemId, nItemBits, false);
127 }
128
ImplToolItem(ToolBoxItemId nItemId,Image aImage,OUString aText,ToolBoxItemBits nItemBits)129 ImplToolItem::ImplToolItem( ToolBoxItemId nItemId, Image aImage,
130 OUString aText, ToolBoxItemBits nItemBits ) :
131 maImage(std::move( aImage )),
132 maText(std::move( aText ))
133 {
134 init(nItemId, nItemBits, false);
135 }
136
GetSize(bool bHorz,bool bCheckMaxWidth,tools::Long maxWidth,const Size & rDefaultSize)137 Size ImplToolItem::GetSize( bool bHorz, bool bCheckMaxWidth, tools::Long maxWidth, const Size& rDefaultSize )
138 {
139 Size aSize( rDefaultSize ); // the size of 'standard' toolbox items
140 // non-standard items are eg windows or buttons with text
141
142 if ( (meType == ToolBoxItemType::BUTTON) || (meType == ToolBoxItemType::SPACE) )
143 {
144 aSize = maItemSize;
145
146 if ( mpWindow && bHorz )
147 {
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();
151
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;
156
157 if ( !bCheckMaxWidth || (aWinSize.Width() <= maxWidth) )
158 {
159 aSize.setWidth( aWinSize.Width() );
160 aSize.setHeight( aWinSize.Height() );
161 mbShowWindow = true;
162 }
163 else
164 {
165 if ( mbEmptyBtn )
166 {
167 aSize.setWidth( 0 );
168 aSize.setHeight( 0 );
169 }
170 }
171 }
172 }
173 else if ( meType == ToolBoxItemType::SEPARATOR )
174 {
175 if ( bHorz )
176 {
177 aSize.setWidth( mnSepSize );
178 aSize.setHeight( rDefaultSize.Height() );
179 }
180 else
181 {
182 aSize.setWidth( rDefaultSize.Width() );
183 aSize.setHeight( mnSepSize );
184 }
185 }
186 else if ( meType == ToolBoxItemType::BREAK )
187 {
188 aSize.setWidth( 0 );
189 aSize.setHeight( 0 );
190 }
191
192 return aSize;
193 }
194
DetermineButtonDrawStyle(ButtonType eButtonType,bool & rbImage,bool & rbText) const195 void ImplToolItem::DetermineButtonDrawStyle( ButtonType eButtonType, bool& rbImage, bool& rbText ) const
196 {
197 if ( meType != ToolBoxItemType::BUTTON )
198 {
199 // no button -> draw nothing
200 rbImage = rbText = false;
201 return;
202 }
203
204 bool bHasImage;
205 bool bHasText;
206
207 // check for image and/or text
208 bHasImage = !!maImage;
209 bHasText = !maText.isEmpty();
210
211 // prefer images if symbolonly buttons are drawn
212 // prefer texts if textonly buttons are drawn
213
214 if ( eButtonType == ButtonType::SYMBOLONLY ) // drawing icons only
215 {
216 if( bHasImage || !bHasText )
217 {
218 rbImage = true;
219 rbText = false;
220 }
221 else
222 {
223 rbImage = false;
224 rbText = true;
225 }
226 }
227 else if ( eButtonType == ButtonType::TEXT ) // drawing text only
228 {
229 if( bHasText || !bHasImage )
230 {
231 rbImage = false;
232 rbText = true;
233 }
234 else
235 {
236 rbImage = true;
237 rbText = false;
238 }
239 }
240 else // drawing icons and text both
241 {
242 rbImage = true;
243 rbText = true;
244 }
245 }
246
GetDropDownRect(bool bHorz) const247 tools::Rectangle ImplToolItem::GetDropDownRect( bool bHorz ) const
248 {
249 tools::Rectangle aRect;
250 if( (mnBits & ToolBoxItemBits::DROPDOWN) && !maRect.IsEmpty() )
251 {
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 );
259 }
260 return aRect;
261 }
262
IsClipped() const263 bool ImplToolItem::IsClipped() const
264 {
265 return ( meType == ToolBoxItemType::BUTTON && mbVisible && maRect.IsEmpty() );
266 }
267
IsItemHidden() const268 bool ImplToolItem::IsItemHidden() const
269 {
270 return ( meType == ToolBoxItemType::BUTTON && !mbVisible );
271 }
272
ImplInvalidate(bool bNewCalc,bool bFullPaint)273 void ToolBox::ImplInvalidate( bool bNewCalc, bool bFullPaint )
274 {
275 ImplUpdateInputEnable();
276
277 if ( bNewCalc )
278 mbCalc = true;
279
280 if ( bFullPaint )
281 {
282 mbFormat = true;
283
284 // do we need to redraw?
285 if ( IsReallyVisible() && IsUpdateMode() )
286 {
287 Invalidate( tools::Rectangle( mnLeftBorder, mnTopBorder,
288 mnDX-mnRightBorder-1, mnDY-mnBottomBorder-1 ) );
289 mpIdle->Stop();
290 }
291 }
292 else
293 {
294 if ( !mbFormat )
295 {
296 mbFormat = true;
297
298 // do we need to redraw?
299 if ( IsReallyVisible() && IsUpdateMode() )
300 mpIdle->Start();
301 }
302 }
303
304 // request new layout by layoutmanager
305 CallEventListeners( VclEventId::ToolboxFormatChanged );
306 }
307
ImplUpdateItem(ImplToolItems::size_type nIndex)308 void ToolBox::ImplUpdateItem( ImplToolItems::size_type nIndex )
309 {
310 // do we need to redraw?
311 if ( !(IsReallyVisible() && IsUpdateMode()) )
312 return;
313
314 if ( nIndex == ITEM_NOTFOUND )
315 {
316 // #i52217# no immediate draw as this might lead to paint problems
317 Invalidate( tools::Rectangle( mnLeftBorder, mnTopBorder, mnDX-mnRightBorder-1, mnDY-mnBottomBorder-1 ) );
318 }
319 else
320 {
321 if ( !mbFormat )
322 {
323 // #i52217# no immediate draw as this might lead to paint problems
324 Invalidate( mpData->m_aItems[nIndex].maRect );
325 }
326 else
327 maPaintRect.Union( mpData->m_aItems[nIndex].maRect );
328 }
329 }
330
Click()331 void ToolBox::Click()
332 {
333 CallEventListeners( VclEventId::ToolboxClick );
334 maClickHdl.Call( this );
335 UITestLogger::getInstance().logAction( this, VclEventId::ToolboxClick);
336 }
337
DoubleClick()338 void ToolBox::DoubleClick()
339 {
340 CallEventListeners( VclEventId::ToolboxDoubleClick );
341 maDoubleClickHdl.Call( this );
342 }
343
Activate()344 void ToolBox::Activate()
345 {
346 mnActivateCount++;
347 CallEventListeners( VclEventId::ToolboxActivate );
348 maActivateHdl.Call( this );
349 }
350
Deactivate()351 void ToolBox::Deactivate()
352 {
353 mnActivateCount--;
354 CallEventListeners( VclEventId::ToolboxDeactivate );
355 maDeactivateHdl.Call( this );
356 }
357
Highlight()358 void ToolBox::Highlight()
359 {
360 CallEventListeners( VclEventId::ToolboxHighlight );
361 }
362
GetUITestFactory() const363 FactoryFunction ToolBox::GetUITestFactory() const
364 {
365 return ToolBoxUIObject::create;
366 }
367
Select()368 void ToolBox::Select()
369 {
370 VclPtr<vcl::Window> xWindow = this;
371
372 CallEventListeners( VclEventId::ToolboxSelect );
373 maSelectHdl.Call( this );
374
375 if ( xWindow->isDisposed() )
376 return;
377
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();
382 }
383
InsertItem(ToolBoxItemId nItemId,const Image & rImage,ToolBoxItemBits nBits,ImplToolItems::size_type nPos)384 void ToolBox::InsertItem( ToolBoxItemId nItemId, const Image& rImage, ToolBoxItemBits nBits, ImplToolItems::size_type nPos )
385 {
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" );
389
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();
394
395 ImplInvalidate( true );
396
397 // Notify
398 ImplToolItems::size_type nNewPos = ( nPos == APPEND ) ? ( mpData->m_aItems.size() - 1 ) : nPos;
399 CallEventListeners( VclEventId::ToolboxItemAdded, reinterpret_cast< void* >(nNewPos ) );
400 }
401
InsertItem(ToolBoxItemId nItemId,const Image & rImage,const OUString & rText,ToolBoxItemBits nBits,ImplToolItems::size_type nPos)402 void ToolBox::InsertItem( ToolBoxItemId nItemId, const Image& rImage, const OUString& rText, ToolBoxItemBits nBits,
403 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, rImage, 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(ToolBoxItemId nItemId,const OUString & rText,const OUString & rCommand,ToolBoxItemBits nBits,ImplToolItems::size_type nPos)421 void ToolBox::InsertItem( ToolBoxItemId nItemId, const OUString& rText, const OUString& rCommand, ToolBoxItemBits nBits,
422 ImplToolItems::size_type nPos )
423 {
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" );
427
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();
432
433 ImplInvalidate( true );
434
435 // Notify
436 ImplToolItems::size_type nNewPos = ( nPos == APPEND ) ? ( mpData->m_aItems.size() - 1 ) : nPos;
437 CallEventListeners( VclEventId::ToolboxItemAdded, reinterpret_cast< void* >( nNewPos ) );
438 }
439
InsertItem(const OUString & rCommand,const css::uno::Reference<css::frame::XFrame> & rFrame,ToolBoxItemBits nBits,const Size & rRequestedSize,ImplToolItems::size_type nPos)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)
442 {
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()));
448
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);
454
455 // set the minimal size
456 ImplToolItem* pItem = ImplGetItem( nItemId );
457 if ( pItem )
458 pItem->maMinimalItemSize = rRequestedSize;
459 }
460
InsertWindow(ToolBoxItemId nItemId,vcl::Window * pWindow,ToolBoxItemBits nBits,ImplToolItems::size_type nPos)461 void ToolBox::InsertWindow( ToolBoxItemId nItemId, vcl::Window* pWindow,
462 ToolBoxItemBits nBits, ImplToolItems::size_type nPos )
463 {
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" );
467
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();
476
477 if ( pWindow )
478 pWindow->Hide();
479
480 ImplInvalidate( true );
481
482 // Notify
483 ImplToolItems::size_type nNewPos = ( nPos == APPEND ) ? ( mpData->m_aItems.size() - 1 ) : nPos;
484 CallEventListeners( VclEventId::ToolboxItemAdded, reinterpret_cast< void* >( nNewPos ) );
485 }
486
InsertSpace()487 void ToolBox::InsertSpace()
488 {
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();
495
496 ImplInvalidate();
497
498 // Notify
499 ImplToolItems::size_type nNewPos = mpData->m_aItems.size() - 1;
500 CallEventListeners( VclEventId::ToolboxItemAdded, reinterpret_cast< void* >( nNewPos ) );
501 }
502
InsertSeparator(ImplToolItems::size_type nPos,sal_uInt16 nPixSize)503 void ToolBox::InsertSeparator( ImplToolItems::size_type nPos, sal_uInt16 nPixSize )
504 {
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();
513
514 ImplInvalidate();
515
516 // Notify
517 ImplToolItems::size_type nNewPos = ( nPos == APPEND ) ? ( mpData->m_aItems.size() - 1 ) : nPos;
518 CallEventListeners( VclEventId::ToolboxItemAdded, reinterpret_cast< void* >( nNewPos ) );
519 }
520
InsertBreak(ImplToolItems::size_type nPos)521 void ToolBox::InsertBreak( ImplToolItems::size_type nPos )
522 {
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();
529
530 ImplInvalidate();
531
532 // Notify
533 ImplToolItems::size_type nNewPos = ( nPos == APPEND ) ? ( mpData->m_aItems.size() - 1 ) : nPos;
534 CallEventListeners( VclEventId::ToolboxItemAdded, reinterpret_cast< void* >( nNewPos ) );
535 }
536
RemoveItem(ImplToolItems::size_type nPos)537 void ToolBox::RemoveItem( ImplToolItems::size_type nPos )
538 {
539 if( nPos >= mpData->m_aItems.size() )
540 return;
541
542 bool bMustCalc;
543 bMustCalc = mpData->m_aItems[nPos].meType == ToolBoxItemType::BUTTON;
544
545 if ( mpData->m_aItems[nPos].mpWindow )
546 mpData->m_aItems[nPos].mpWindow->Hide();
547
548 // add the removed item to PaintRect
549 maPaintRect.Union( mpData->m_aItems[nPos].maRect );
550
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);
556
557 ImplInvalidate( bMustCalc );
558
559 mpData->m_aItems.erase( mpData->m_aItems.begin()+nPos );
560 mpData->ImplClearLayoutData();
561
562 // Notify
563 CallEventListeners( VclEventId::ToolboxItemRemoved, reinterpret_cast< void* >( nPos ) );
564 }
565
CopyItem(const ToolBox & rToolBox,ToolBoxItemId nItemId)566 void ToolBox::CopyItem( const ToolBox& rToolBox, ToolBoxItemId nItemId )
567 {
568 SAL_WARN_IF( GetItemPos( nItemId ) != ITEM_NOTFOUND, "vcl",
569 "ToolBox::CopyItem(): ItemId already exists" );
570
571 ImplToolItems::size_type nPos = rToolBox.GetItemPos( nItemId );
572
573 // found item
574 if ( nPos == ITEM_NOTFOUND )
575 return;
576
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;
582
583 mpData->m_aItems.push_back( aNewItem );
584 mpData->ImplClearLayoutData();
585 // redraw ToolBox
586 ImplInvalidate();
587
588 // Notify
589 ImplToolItems::size_type nNewPos2 = mpData->m_aItems.size() - 1;
590 CallEventListeners( VclEventId::ToolboxItemAdded, reinterpret_cast< void* >( nNewPos2 ) );
591 }
592
Clear()593 void ToolBox::Clear()
594 {
595 mpData->m_aItems.clear();
596 mpData->ImplClearLayoutData();
597
598 // ensure not to delete in the Select-Handler
599 mnCurItemId = ToolBoxItemId(0);
600 mnHighItemId = ToolBoxItemId(0);
601
602 ImplInvalidate( true, true );
603
604 // Notify
605 CallEventListeners( VclEventId::ToolboxAllItemsChanged );
606 }
607
SetButtonType(ButtonType eNewType)608 void ToolBox::SetButtonType( ButtonType eNewType )
609 {
610 if ( meButtonType != eNewType )
611 {
612 meButtonType = eNewType;
613
614 // better redraw everything, as otherwise there might be problems
615 // with regions that were copied with CopyBits
616 ImplInvalidate( true );
617 }
618 }
619
SetToolboxButtonSize(ToolBoxButtonSize eSize)620 void ToolBox::SetToolboxButtonSize( ToolBoxButtonSize eSize )
621 {
622 if( mpData->meButtonSize != eSize )
623 {
624 mpData->meButtonSize = eSize;
625 mbCalc = true;
626 mbFormat = true;
627 }
628 }
629
GetToolboxButtonSize() const630 ToolBoxButtonSize ToolBox::GetToolboxButtonSize() const
631 {
632 return mpData->meButtonSize;
633 }
634
GetImageSize() const635 ImageType ToolBox::GetImageSize() const
636 {
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;
642
643 return eImageType;
644 }
645
GetDefaultImageSize(ToolBoxButtonSize eToolBoxButtonSize)646 /*static*/ Size ToolBox::GetDefaultImageSize(ToolBoxButtonSize eToolBoxButtonSize)
647 {
648 OutputDevice *pDefault = Application::GetDefaultDevice();
649 float fScaleFactor = pDefault ? pDefault->GetDPIScaleFactor() : 1.0;
650
651 Size aUnscaledSize(16, 16);
652
653 if (eToolBoxButtonSize == ToolBoxButtonSize::Large)
654 {
655 OUString iconTheme = Application::GetSettings().GetStyleSettings().DetermineIconTheme();
656 aUnscaledSize = vcl::IconThemeInfo::SizeByThemeName(iconTheme);
657 }
658 else if (eToolBoxButtonSize == ToolBoxButtonSize::Size32)
659 {
660 aUnscaledSize = Size(32, 32);
661 }
662 return Size(aUnscaledSize.Width() * fScaleFactor,
663 aUnscaledSize.Height() * fScaleFactor);
664 }
665
GetDefaultImageSize() const666 Size ToolBox::GetDefaultImageSize() const
667 {
668 return GetDefaultImageSize(GetToolboxButtonSize());
669 }
670
SetAlign(WindowAlign eNewAlign)671 void ToolBox::SetAlign( WindowAlign eNewAlign )
672 {
673 if ( meAlign == eNewAlign )
674 return;
675
676 meAlign = eNewAlign;
677
678 if ( ImplIsFloatingMode() )
679 return;
680
681 // set horizontal/vertical alignment
682 if ( (eNewAlign == WindowAlign::Left) || (eNewAlign == WindowAlign::Right) )
683 mbHorz = false;
684 else
685 mbHorz = true;
686
687 // Update the background according to Persona if necessary
688 ImplInitSettings( false, false, true );
689
690 // redraw everything, as the border has changed
691 mbCalc = true;
692 mbFormat = true;
693 if ( IsReallyVisible() && IsUpdateMode() )
694 Invalidate();
695 }
696
SetLineCount(ImplToolItems::size_type nNewLines)697 void ToolBox::SetLineCount( ImplToolItems::size_type nNewLines )
698 {
699 if ( !nNewLines )
700 nNewLines = 1;
701
702 if ( mnLines != nNewLines )
703 {
704 mnLines = nNewLines;
705
706 // better redraw everything, as otherwise there might be problems
707 // with regions that were copied with CopyBits
708 Invalidate();
709 }
710 }
711
GetItemCount() const712 ToolBox::ImplToolItems::size_type ToolBox::GetItemCount() const
713 {
714 return mpData ? mpData->m_aItems.size() : 0;
715 }
716
GetItemType(ImplToolItems::size_type nPos) const717 ToolBoxItemType ToolBox::GetItemType( ImplToolItems::size_type nPos ) const
718 {
719 return (nPos < mpData->m_aItems.size()) ? mpData->m_aItems[nPos].meType : ToolBoxItemType::DONTKNOW;
720 }
721
GetItemPos(ToolBoxItemId nItemId) const722 ToolBox::ImplToolItems::size_type ToolBox::GetItemPos( ToolBoxItemId nItemId ) const
723 {
724 if (mpData)
725 {
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;
730 }
731 return ITEM_NOTFOUND;
732 }
733
GetItemPos(const Point & rPos) const734 ToolBox::ImplToolItems::size_type ToolBox::GetItemPos( const Point& rPos ) const
735 {
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 ); });
739
740 if( it != mpData->m_aItems.end() )
741 return std::distance(mpData->m_aItems.begin(), it);
742
743 return ITEM_NOTFOUND;
744 }
745
GetItemId(ImplToolItems::size_type nPos) const746 ToolBoxItemId ToolBox::GetItemId( ImplToolItems::size_type nPos ) const
747 {
748 return (nPos < mpData->m_aItems.size()) ? mpData->m_aItems[nPos].mnId : ToolBoxItemId(0);
749 }
750
GetItemId(const Point & rPos) const751 ToolBoxItemId ToolBox::GetItemId( const Point& rPos ) const
752 {
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 ); });
756
757 if( (it != mpData->m_aItems.end()) && (it->meType == ToolBoxItemType::BUTTON) )
758 return it->mnId;
759
760 return ToolBoxItemId(0);
761 }
762
GetItemContentSize(ToolBoxItemId nItemId)763 Size ToolBox::GetItemContentSize( ToolBoxItemId nItemId )
764 {
765 if ( mbCalc || mbFormat )
766 ImplFormat();
767
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();
773 }
774
GetItemId(const OUString & rCommand) const775 ToolBoxItemId ToolBox::GetItemId(const OUString &rCommand) const
776 {
777 if (!mpData)
778 return ToolBoxItemId(0);
779
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;
784
785 return ToolBoxItemId(0);
786 }
787
ImplGetPopupPosition(const tools::Rectangle & rRect) const788 Point ToolBox::ImplGetPopupPosition( const tools::Rectangle& rRect ) const
789 {
790 Point aPos;
791 if( !rRect.IsEmpty() )
792 {
793 AbsoluteScreenPixelRectangle aScreen = GetDesktopRectPixel();
794
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
799
800 AbsoluteScreenPixelPoint devPos; // the position in device coordinates for screen comparison
801 switch( meAlign )
802 {
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;
833 }
834 }
835 return aPos;
836 }
837
GetItemRect(ToolBoxItemId nItemId)838 tools::Rectangle ToolBox::GetItemRect( ToolBoxItemId nItemId )
839 {
840 if ( mbCalc || mbFormat )
841 ImplFormat();
842
843 ImplToolItems::size_type nPos = GetItemPos( nItemId );
844 return GetItemPosRect( nPos );
845 }
846
GetItemPosRect(ImplToolItems::size_type nPos)847 tools::Rectangle ToolBox::GetItemPosRect( ImplToolItems::size_type nPos )
848 {
849 if ( mbCalc || mbFormat )
850 ImplFormat();
851
852 if ( nPos < mpData->m_aItems.size() )
853 return mpData->m_aItems[nPos].maRect;
854 else
855 return tools::Rectangle();
856 }
857
GetOverflowRect() const858 tools::Rectangle const & ToolBox::GetOverflowRect() const
859 {
860 return mpData->maMenubuttonItem.maRect;
861 }
862
ImplHasExternalMenubutton() const863 bool ToolBox::ImplHasExternalMenubutton() const
864 {
865 // check if the borderwindow (i.e. the decoration) provides the menu button
866 bool bRet = false;
867 if( ImplIsFloatingMode() )
868 {
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;
873 }
874 return bRet;
875 }
876
SetItemBits(ToolBoxItemId nItemId,ToolBoxItemBits nBits)877 void ToolBox::SetItemBits( ToolBoxItemId nItemId, ToolBoxItemBits nBits )
878 {
879 ImplToolItems::size_type nPos = GetItemPos( nItemId );
880
881 if ( nPos < GetItemCount() )
882 {
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 );
891 }
892 }
893
SetItemWindowNonInteractive(ToolBoxItemId nItemId,bool bNonInteractive)894 void ToolBox::SetItemWindowNonInteractive(ToolBoxItemId nItemId, bool bNonInteractive)
895 {
896 ImplToolItems::size_type nPos = GetItemPos( nItemId );
897
898 if ( nPos < GetItemCount() )
899 {
900 mpData->m_aItems[nPos].mbNonInteractiveWindow = bNonInteractive;
901 }
902 }
903
GetItemBits(ToolBoxItemId nItemId) const904 ToolBoxItemBits ToolBox::GetItemBits( ToolBoxItemId nItemId ) const
905 {
906 ImplToolItem* pItem = ImplGetItem( nItemId );
907
908 if ( pItem )
909 return pItem->mnBits;
910 else
911 return ToolBoxItemBits::NONE;
912 }
913
SetItemExpand(ToolBoxItemId nItemId,bool bExpand)914 void ToolBox::SetItemExpand( ToolBoxItemId nItemId, bool bExpand )
915 {
916 ImplToolItem* pItem = ImplGetItem( nItemId );
917 if (!pItem)
918 return;
919
920 if (pItem->mbExpand != bExpand)
921 {
922 pItem->mbExpand = bExpand;
923 ImplInvalidate(true, true);
924 }
925 }
926
SetItemData(ToolBoxItemId nItemId,void * pNewData)927 void ToolBox::SetItemData( ToolBoxItemId nItemId, void* pNewData )
928 {
929 ImplToolItems::size_type nPos = GetItemPos( nItemId );
930
931 if ( nPos < mpData->m_aItems.size() )
932 {
933 mpData->m_aItems[nPos].mpUserData = pNewData;
934 ImplUpdateItem( nPos );
935 }
936 }
937
GetItemData(ToolBoxItemId nItemId) const938 void* ToolBox::GetItemData( ToolBoxItemId nItemId ) const
939 {
940 ImplToolItem* pItem = ImplGetItem( nItemId );
941
942 if ( pItem )
943 return pItem->mpUserData;
944 else
945 return nullptr;
946 }
947
ImplMirrorImage(const Image & rImage)948 static Image ImplMirrorImage( const Image& rImage )
949 {
950 BitmapEx aMirrBitmapEx( rImage.GetBitmapEx() );
951
952 aMirrBitmapEx.Mirror( BmpMirrorFlags::Horizontal );
953
954 return Image( aMirrBitmapEx );
955 }
956
ImplRotImage(const Image & rImage,Degree10 nAngle10)957 static Image ImplRotImage( const Image& rImage, Degree10 nAngle10 )
958 {
959 BitmapEx aRotBitmapEx( rImage.GetBitmapEx() );
960
961 aRotBitmapEx.Rotate( nAngle10, COL_WHITE );
962
963 return Image( aRotBitmapEx );
964 }
965
SetItemImage(ToolBoxItemId nItemId,const Image & rImage)966 void ToolBox::SetItemImage( ToolBoxItemId nItemId, const Image& rImage )
967 {
968 ImplToolItems::size_type nPos = GetItemPos( nItemId );
969
970 if ( nPos == ITEM_NOTFOUND )
971 return;
972
973 ImplToolItem* pItem = &mpData->m_aItems[nPos];
974 Size aOldSize = pItem->maImage.GetSizePixel();
975
976 pItem->maImage = pItem->mbMirrorMode ? ImplMirrorImage(rImage) : rImage;
977 if (pItem->mnImageAngle != 0_deg10)
978 pItem->maImage = ImplRotImage(pItem->maImage, pItem->mnImageAngle);
979
980 // only once all is calculated, do extra work
981 if (!mbCalc)
982 {
983 if (aOldSize != pItem->maImage.GetSizePixel())
984 ImplInvalidate( true );
985 else
986 ImplUpdateItem( nPos );
987 }
988 }
989
SetItemImageAngle(ToolBoxItemId nItemId,Degree10 nAngle10)990 void ToolBox::SetItemImageAngle( ToolBoxItemId nItemId, Degree10 nAngle10 )
991 {
992 ImplToolItems::size_type nPos = GetItemPos( nItemId );
993
994 if ( nPos == ITEM_NOTFOUND )
995 return;
996
997 ImplToolItem* pItem = &mpData->m_aItems[nPos];
998 pItem->mnImageAngle = nAngle10;
999 }
1000
SetItemImageMirrorMode(ToolBoxItemId nItemId,bool bMirror)1001 void ToolBox::SetItemImageMirrorMode( ToolBoxItemId nItemId, bool bMirror )
1002 {
1003 ImplToolItems::size_type nPos = GetItemPos( nItemId );
1004
1005 if ( nPos == ITEM_NOTFOUND )
1006 return;
1007
1008 ImplToolItem* pItem = &mpData->m_aItems[nPos];
1009 pItem->mbMirrorMode = bMirror;
1010 }
1011
GetItemImage(ToolBoxItemId nItemId) const1012 Image ToolBox::GetItemImage(ToolBoxItemId nItemId) const
1013 {
1014 ImplToolItem* pItem = ImplGetItem(nItemId);
1015 return pItem ? pItem->maImage : Image();
1016 }
1017
SetItemText(ToolBoxItemId nItemId,const OUString & rText)1018 void ToolBox::SetItemText( ToolBoxItemId nItemId, const OUString& rText )
1019 {
1020 ImplToolItems::size_type nPos = GetItemPos( nItemId );
1021
1022 if ( nPos == ITEM_NOTFOUND )
1023 return;
1024
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) )
1029 {
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 );
1037 }
1038 else
1039 pItem->maText = MnemonicGenerator::EraseAllMnemonicChars(rText);
1040
1041 // Notify
1042 CallEventListeners( VclEventId::ToolboxItemTextChanged, reinterpret_cast< void* >( nPos ) );
1043 }
1044
GetItemText(ToolBoxItemId nItemId) const1045 const OUString& ToolBox::GetItemText( ToolBoxItemId nItemId ) const
1046 {
1047
1048 ImplToolItem* pItem = ImplGetItem( nItemId );
1049
1050 assert( pItem );
1051
1052 return pItem->maText;
1053 }
1054
SetItemWindow(ToolBoxItemId nItemId,vcl::Window * pNewWindow)1055 void ToolBox::SetItemWindow( ToolBoxItemId nItemId, vcl::Window* pNewWindow )
1056 {
1057 ImplToolItems::size_type nPos = GetItemPos( nItemId );
1058
1059 if ( nPos != ITEM_NOTFOUND )
1060 {
1061 ImplToolItem* pItem = &mpData->m_aItems[nPos];
1062 pItem->mpWindow = pNewWindow;
1063 if ( pNewWindow )
1064 pNewWindow->Hide();
1065 ImplInvalidate( true );
1066 CallEventListeners( VclEventId::ToolboxItemWindowChanged, reinterpret_cast< void* >( nPos ) );
1067 }
1068 }
1069
GetItemWindow(ToolBoxItemId nItemId) const1070 vcl::Window* ToolBox::GetItemWindow( ToolBoxItemId nItemId ) const
1071 {
1072 ImplToolItem* pItem = ImplGetItem( nItemId );
1073
1074 if ( pItem )
1075 return pItem->mpWindow;
1076 else
1077 return nullptr;
1078 }
1079
EndSelection()1080 void ToolBox::EndSelection()
1081 {
1082 if ( mbDrag )
1083 {
1084 // reset
1085 mbDrag = false;
1086 if (mnCurPos != ITEM_NOTFOUND)
1087 InvalidateItem(mnCurPos);
1088 EndTracking();
1089 if (IsMouseCaptured())
1090 ReleaseMouse();
1091 Deactivate();
1092 }
1093
1094 mnCurPos = ITEM_NOTFOUND;
1095 mnCurItemId = ToolBoxItemId(0);
1096 mnDownItemId = ToolBoxItemId(0);
1097 mnMouseModifier = 0;
1098 }
1099
SetItemDown(ToolBoxItemId nItemId,bool bDown)1100 void ToolBox::SetItemDown( ToolBoxItemId nItemId, bool bDown )
1101 {
1102 ImplToolItems::size_type nPos = GetItemPos( nItemId );
1103
1104 if ( nPos == ITEM_NOTFOUND )
1105 return;
1106
1107 if ( bDown )
1108 {
1109 if ( nPos != mnCurPos )
1110 {
1111 mnCurPos = nPos;
1112 InvalidateItem(mnCurPos);
1113 GetOutDev()->Flush();
1114 }
1115 }
1116 else
1117 {
1118 if ( nPos == mnCurPos )
1119 {
1120 InvalidateItem(mnCurPos);
1121 GetOutDev()->Flush();
1122 mnCurPos = ITEM_NOTFOUND;
1123 }
1124 }
1125
1126 if ( mbDrag )
1127 {
1128 mbDrag = false;
1129 EndTracking();
1130 if (IsMouseCaptured())
1131 ReleaseMouse();
1132 Deactivate();
1133 }
1134
1135 mnCurItemId = ToolBoxItemId(0);
1136 mnDownItemId = ToolBoxItemId(0);
1137 mnMouseModifier = 0;
1138 }
1139
SetItemState(ToolBoxItemId nItemId,TriState eState)1140 void ToolBox::SetItemState( ToolBoxItemId nItemId, TriState eState )
1141 {
1142 ImplToolItems::size_type nPos = GetItemPos( nItemId );
1143
1144 if ( nPos == ITEM_NOTFOUND )
1145 return;
1146
1147 ImplToolItem* pItem = &mpData->m_aItems[nPos];
1148
1149 // the state has changed
1150 if ( pItem->meState == eState )
1151 return;
1152
1153 // if RadioCheck, un-check the previous
1154 if ( (eState == TRISTATE_TRUE) && (pItem->mnBits & ToolBoxItemBits::AUTOCHECK) &&
1155 (pItem->mnBits & ToolBoxItemBits::RADIOCHECK) )
1156 {
1157 ImplToolItem* pGroupItem;
1158 ImplToolItems::size_type nGroupPos;
1159 ImplToolItems::size_type nItemCount = GetItemCount();
1160
1161 nGroupPos = nPos;
1162 while ( nGroupPos )
1163 {
1164 pGroupItem = &mpData->m_aItems[nGroupPos-1];
1165 if ( pGroupItem->mnBits & ToolBoxItemBits::RADIOCHECK )
1166 {
1167 if ( pGroupItem->meState != TRISTATE_FALSE )
1168 SetItemState( pGroupItem->mnId, TRISTATE_FALSE );
1169 }
1170 else
1171 break;
1172 nGroupPos--;
1173 }
1174
1175 nGroupPos = nPos+1;
1176 while ( nGroupPos < nItemCount )
1177 {
1178 pGroupItem = &mpData->m_aItems[nGroupPos];
1179 if ( pGroupItem->mnBits & ToolBoxItemBits::RADIOCHECK )
1180 {
1181 if ( pGroupItem->meState != TRISTATE_FALSE )
1182 SetItemState( pGroupItem->mnId, TRISTATE_FALSE );
1183 }
1184 else
1185 break;
1186 nGroupPos++;
1187 }
1188 }
1189
1190 pItem->meState = eState;
1191 ImplUpdateItem( nPos );
1192
1193 // Call accessible listener to notify state_changed event
1194 CallEventListeners( VclEventId::ToolboxItemUpdated, reinterpret_cast< void* >(nPos) );
1195 }
1196
GetItemState(ToolBoxItemId nItemId) const1197 TriState ToolBox::GetItemState( ToolBoxItemId nItemId ) const
1198 {
1199 ImplToolItem* pItem = ImplGetItem( nItemId );
1200
1201 if ( pItem )
1202 return pItem->meState;
1203 else
1204 return TRISTATE_FALSE;
1205 }
1206
EnableItem(ToolBoxItemId nItemId,bool bEnable)1207 void ToolBox::EnableItem( ToolBoxItemId nItemId, bool bEnable )
1208 {
1209 ImplToolItems::size_type nPos = GetItemPos( nItemId );
1210
1211 if ( nPos == ITEM_NOTFOUND )
1212 return;
1213
1214 ImplToolItem* pItem = &mpData->m_aItems[nPos];
1215 if ( pItem->mbEnabled == bEnable )
1216 return;
1217
1218 pItem->mbEnabled = bEnable;
1219
1220 // if existing, also redraw the window
1221 if ( pItem->mpWindow )
1222 pItem->mpWindow->Enable( pItem->mbEnabled );
1223
1224 // update item
1225 ImplUpdateItem( nPos );
1226
1227 ImplUpdateInputEnable();
1228
1229 CallEventListeners( bEnable ? VclEventId::ToolboxItemEnabled : VclEventId::ToolboxItemDisabled, reinterpret_cast< void* >( nPos ) );
1230 }
1231
IsItemEnabled(ToolBoxItemId nItemId) const1232 bool ToolBox::IsItemEnabled( ToolBoxItemId nItemId ) const
1233 {
1234 ImplToolItem* pItem = ImplGetItem( nItemId );
1235
1236 if ( pItem )
1237 return pItem->mbEnabled;
1238 else
1239 return false;
1240 }
1241
ShowItem(ToolBoxItemId nItemId,bool bVisible)1242 void ToolBox::ShowItem( ToolBoxItemId nItemId, bool bVisible )
1243 {
1244 ImplToolItems::size_type nPos = GetItemPos( nItemId );
1245 mpData->ImplClearLayoutData();
1246
1247 if ( nPos != ITEM_NOTFOUND )
1248 {
1249 ImplToolItem* pItem = &mpData->m_aItems[nPos];
1250 if ( pItem->mbVisible != bVisible )
1251 {
1252 pItem->mbVisible = bVisible;
1253 ImplInvalidate();
1254 }
1255 }
1256 }
1257
IsItemClipped(ToolBoxItemId nItemId) const1258 bool ToolBox::IsItemClipped( ToolBoxItemId nItemId ) const
1259 {
1260 ImplToolItem* pItem = ImplGetItem( nItemId );
1261
1262 if ( pItem )
1263 return pItem->IsClipped();
1264 else
1265 return false;
1266 }
1267
IsItemVisible(ToolBoxItemId nItemId) const1268 bool ToolBox::IsItemVisible( ToolBoxItemId nItemId ) const
1269 {
1270 ImplToolItem* pItem = ImplGetItem( nItemId );
1271
1272 if ( pItem )
1273 return pItem->mbVisible;
1274 else
1275 return false;
1276 }
1277
IsItemReallyVisible(ToolBoxItemId nItemId) const1278 bool ToolBox::IsItemReallyVisible( ToolBoxItemId nItemId ) const
1279 {
1280 // is the item on the visible area of the toolbox?
1281 bool bRet = false;
1282 tools::Rectangle aRect( mnLeftBorder, mnTopBorder, mnDX-mnRightBorder, mnDY-mnBottomBorder );
1283 ImplToolItem* pItem = ImplGetItem( nItemId );
1284
1285 if ( pItem && pItem->mbVisible &&
1286 !pItem->maRect.IsEmpty() && aRect.Overlaps( pItem->maRect ) )
1287 {
1288 bRet = true;
1289 }
1290
1291 return bRet;
1292 }
1293
SetItemCommand(ToolBoxItemId nItemId,const OUString & rCommand)1294 void ToolBox::SetItemCommand(ToolBoxItemId nItemId, const OUString& rCommand)
1295 {
1296 ImplToolItem* pItem = ImplGetItem( nItemId );
1297
1298 if (pItem)
1299 pItem->maCommandStr = rCommand;
1300 }
1301
GetItemCommand(ToolBoxItemId nItemId) const1302 OUString ToolBox::GetItemCommand( ToolBoxItemId nItemId ) const
1303 {
1304 ImplToolItem* pItem = ImplGetItem( nItemId );
1305
1306 if (pItem)
1307 return pItem->maCommandStr;
1308
1309 return OUString();
1310 }
1311
SetQuickHelpText(ToolBoxItemId nItemId,const OUString & rText)1312 void ToolBox::SetQuickHelpText( ToolBoxItemId nItemId, const OUString& rText )
1313 {
1314 ImplToolItem* pItem = ImplGetItem( nItemId );
1315
1316 if ( pItem )
1317 pItem->maQuickHelpText = rText;
1318 }
1319
GetQuickHelpText(ToolBoxItemId nItemId) const1320 OUString ToolBox::GetQuickHelpText( ToolBoxItemId nItemId ) const
1321 {
1322 ImplToolItem* pItem = ImplGetItem( nItemId );
1323
1324 if ( pItem )
1325 return pItem->maQuickHelpText;
1326 else
1327 return OUString();
1328 }
1329
SetHelpText(ToolBoxItemId nItemId,const OUString & rText)1330 void ToolBox::SetHelpText( ToolBoxItemId nItemId, const OUString& rText )
1331 {
1332 ImplToolItem* pItem = ImplGetItem( nItemId );
1333
1334 if ( pItem )
1335 pItem->maHelpText = rText;
1336 }
1337
GetHelpText(ToolBoxItemId nItemId) const1338 const OUString& ToolBox::GetHelpText( ToolBoxItemId nItemId ) const
1339 {
1340 return ImplGetHelpText( nItemId );
1341 }
1342
SetAccessibleName(ToolBoxItemId nItemId,const OUString & rText)1343 void ToolBox::SetAccessibleName(ToolBoxItemId nItemId, const OUString& rText)
1344 {
1345 ImplToolItem* pItem = ImplGetItem(nItemId);
1346
1347 if (pItem)
1348 pItem->maAccessibleName = rText;
1349 }
1350
GetAccessibleName(ToolBoxItemId nItemId) const1351 OUString ToolBox::GetAccessibleName(ToolBoxItemId nItemId) const
1352 {
1353 ImplToolItem* pItem = ImplGetItem(nItemId);
1354 if (pItem)
1355 return pItem->maAccessibleName;
1356
1357 return OUString();
1358 }
1359
SetHelpId(ToolBoxItemId nItemId,const OUString & rHelpId)1360 void ToolBox::SetHelpId( ToolBoxItemId nItemId, const OUString& rHelpId )
1361 {
1362 ImplToolItem* pItem = ImplGetItem( nItemId );
1363
1364 if ( pItem )
1365 pItem->maHelpId = rHelpId;
1366 }
1367
1368 // disable key input if all items are disabled
ImplUpdateInputEnable()1369 void ToolBox::ImplUpdateInputEnable()
1370 {
1371 mpData->mbKeyInputDisabled = std::none_of(mpData->m_aItems.begin(), mpData->m_aItems.end(),
1372 [](const ImplToolItem& rItem) {
1373 // at least one useful entry
1374 return rItem.mbEnabled;
1375 });
1376 }
1377
ImplFillLayoutData()1378 void ToolBox::ImplFillLayoutData()
1379 {
1380 mpData->m_pLayoutData.emplace();
1381
1382 ImplToolItems::size_type nCount = mpData->m_aItems.size();
1383 for( ImplToolItems::size_type i = 0; i < nCount; i++ )
1384 {
1385 ImplToolItem* pItem = &mpData->m_aItems[i];
1386
1387 // only draw, if the rectangle is within PaintRectangle
1388 if (!pItem->maRect.IsEmpty())
1389 InvalidateItem(i);
1390 }
1391 }
1392
GetDisplayText() const1393 OUString ToolBox::GetDisplayText() const
1394 {
1395 if( ! mpData->m_pLayoutData )
1396 const_cast<ToolBox *>(this)->ImplFillLayoutData();
1397 return mpData->m_pLayoutData ? mpData->m_pLayoutData->m_aDisplayText : OUString();
1398 }
1399
GetCharacterBounds(ToolBoxItemId nItemID,tools::Long nIndex)1400 tools::Rectangle ToolBox::GetCharacterBounds( ToolBoxItemId nItemID, tools::Long nIndex )
1401 {
1402 tools::Long nItemIndex = -1;
1403 if( ! mpData->m_pLayoutData )
1404 ImplFillLayoutData();
1405 if( mpData->m_pLayoutData )
1406 {
1407 for( size_t i = 0; i < mpData->m_pLayoutData->m_aLineItemIds.size(); i++ )
1408 {
1409 if( mpData->m_pLayoutData->m_aLineItemIds[i] == nItemID )
1410 {
1411 nItemIndex = mpData->m_pLayoutData->m_aLineIndices[i];
1412 break;
1413 }
1414 }
1415 }
1416 return (mpData->m_pLayoutData && nItemIndex != -1) ? mpData->m_pLayoutData->GetCharacterBounds( nItemIndex+nIndex ) : tools::Rectangle();
1417 }
1418
GetIndexForPoint(const Point & rPoint,ToolBoxItemId & rItemID)1419 tools::Long ToolBox::GetIndexForPoint( const Point& rPoint, ToolBoxItemId& rItemID )
1420 {
1421 tools::Long nIndex = -1;
1422 rItemID = ToolBoxItemId(0);
1423 if( ! mpData->m_pLayoutData )
1424 ImplFillLayoutData();
1425 if( mpData->m_pLayoutData )
1426 {
1427 nIndex = mpData->m_pLayoutData->GetIndexForPoint( rPoint );
1428 for( size_t i = 0; i < mpData->m_pLayoutData->m_aLineIndices.size(); i++ )
1429 {
1430 if( mpData->m_pLayoutData->m_aLineIndices[i] <= nIndex &&
1431 (i == mpData->m_pLayoutData->m_aLineIndices.size()-1 || mpData->m_pLayoutData->m_aLineIndices[i+1] > nIndex) )
1432 {
1433 rItemID = mpData->m_pLayoutData->m_aLineItemIds[i];
1434 break;
1435 }
1436 }
1437 }
1438 return nIndex;
1439 }
1440
SetDropdownClickHdl(const Link<ToolBox *,void> & rLink)1441 void ToolBox::SetDropdownClickHdl( const Link<ToolBox *, void>& rLink )
1442 {
1443 if (mpData != nullptr) {
1444 mpData->maDropdownClickHdl = rLink;
1445 }
1446 }
1447
SetMenuType(ToolBoxMenuType aType)1448 void ToolBox::SetMenuType( ToolBoxMenuType aType )
1449 {
1450 if( aType == mpData->maMenuType )
1451 return;
1452
1453 mpData->maMenuType = aType;
1454 if( IsFloatingMode() )
1455 {
1456 // the menu button may have to be moved into the decoration which changes the layout
1457 ImplDockingWindowWrapper *pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper( this );
1458 if( pWrapper )
1459 pWrapper->ShowMenuTitleButton( bool( aType & ToolBoxMenuType::Customize) );
1460
1461 mbFormat = true;
1462 ImplFormat();
1463 ImplSetMinMaxFloatSize();
1464 }
1465 else
1466 {
1467 // trigger redraw of menu button
1468 if( !mpData->maMenubuttonItem.maRect.IsEmpty() )
1469 Invalidate(mpData->maMenubuttonItem.maRect);
1470 }
1471 }
1472
GetMenuType() const1473 ToolBoxMenuType ToolBox::GetMenuType() const
1474 {
1475 return mpData->maMenuType;
1476 }
1477
IsMenuEnabled() const1478 bool ToolBox::IsMenuEnabled() const
1479 {
1480 return mpData->maMenuType != ToolBoxMenuType::NONE;
1481 }
1482
GetMenu() const1483 PopupMenu* ToolBox::GetMenu() const
1484 {
1485 return mpData == nullptr ? nullptr : mpData->mpMenu;
1486 }
1487
SetMenuExecuteHdl(const Link<ToolBox *,void> & rLink)1488 void ToolBox::SetMenuExecuteHdl( const Link<ToolBox *, void>& rLink )
1489 {
1490 mpData->maMenuButtonHdl = rLink;
1491 }
1492
ImplHasClippedItems()1493 bool ToolBox::ImplHasClippedItems()
1494 {
1495 // are any items currently clipped ?
1496 ImplFormat();
1497 return std::any_of(mpData->m_aItems.begin(), mpData->m_aItems.end(),
1498 [](const ImplToolItem& rItem) { return rItem.IsClipped(); });
1499 }
1500
1501 namespace
1502 {
ConvertBitsFromToolBoxToMenu(ToolBoxItemBits nToolItemBits)1503 MenuItemBits ConvertBitsFromToolBoxToMenu(ToolBoxItemBits nToolItemBits)
1504 {
1505 MenuItemBits nMenuItemBits = MenuItemBits::NONE;
1506 if ((nToolItemBits & ToolBoxItemBits::CHECKABLE) ||
1507 (nToolItemBits & ToolBoxItemBits::DROPDOWN))
1508 {
1509 nMenuItemBits |= MenuItemBits::CHECKABLE;
1510 }
1511 return nMenuItemBits;
1512 }
1513 }
1514
UpdateCustomMenu()1515 void ToolBox::UpdateCustomMenu()
1516 {
1517 // fill clipped items into menu
1518 PopupMenu *pMenu = GetMenu();
1519 pMenu->Clear();
1520
1521 // add menu items: first the overflow items, then hidden items, both in the
1522 // order they would usually appear in the toolbar. Separators that would be
1523 // in the toolbar are ignored as they would introduce too much clutter,
1524 // instead we have a single separator to help distinguish between overflow
1525 // and hidden items.
1526 if ( mpData->m_aItems.empty() )
1527 return;
1528
1529 // nStartPos will hold the number of clipped items appended from first loop
1530 for ( const auto& rItem : mpData->m_aItems )
1531 {
1532 if( rItem.IsClipped() )
1533 {
1534 sal_uInt16 id = sal_uInt16(rItem.mnId) + TOOLBOX_MENUITEM_START;
1535 MenuItemBits nMenuItemBits = ConvertBitsFromToolBoxToMenu(rItem.mnBits);
1536 pMenu->InsertItem( id, rItem.maText, rItem.maImage, nMenuItemBits);
1537 pMenu->SetItemCommand( id, rItem.maCommandStr );
1538 pMenu->EnableItem( id, rItem.mbEnabled );
1539 pMenu->CheckItem ( id, rItem.meState == TRISTATE_TRUE );
1540 }
1541 }
1542
1543 // add a separator below the inserted clipped-items
1544 pMenu->InsertSeparator();
1545
1546 // now append the items that are explicitly disabled
1547 for ( const auto& rItem : mpData->m_aItems )
1548 {
1549 if( rItem.IsItemHidden() )
1550 {
1551 sal_uInt16 id = sal_uInt16(rItem.mnId) + TOOLBOX_MENUITEM_START;
1552 MenuItemBits nMenuItemBits = ConvertBitsFromToolBoxToMenu(rItem.mnBits);
1553 pMenu->InsertItem( id, rItem.maText, rItem.maImage, nMenuItemBits );
1554 pMenu->SetItemCommand( id, rItem.maCommandStr );
1555 pMenu->EnableItem( id, rItem.mbEnabled );
1556 pMenu->CheckItem( id, rItem.meState == TRISTATE_TRUE );
1557 }
1558 }
1559 }
1560
IMPL_LINK(ToolBox,ImplCustomMenuListener,VclMenuEvent &,rEvent,void)1561 IMPL_LINK( ToolBox, ImplCustomMenuListener, VclMenuEvent&, rEvent, void )
1562 {
1563 if( rEvent.GetMenu() == GetMenu() && rEvent.GetId() == VclEventId::MenuSelect )
1564 {
1565 sal_uInt16 id = GetMenu()->GetItemId( rEvent.GetItemPos() );
1566 if( id >= TOOLBOX_MENUITEM_START )
1567 TriggerItem( ToolBoxItemId(id - TOOLBOX_MENUITEM_START) );
1568 }
1569 }
1570
ExecuteCustomMenu(const tools::Rectangle & rRect)1571 void ToolBox::ExecuteCustomMenu( const tools::Rectangle& rRect )
1572 {
1573 if ( !IsMenuEnabled() || ImplIsInPopupMode() )
1574 return;
1575
1576 UpdateCustomMenu();
1577
1578 if( GetMenuType() & ToolBoxMenuType::Customize )
1579 // call button handler to allow for menu customization
1580 mpData->maMenuButtonHdl.Call( this );
1581
1582 GetMenu()->AddEventListener( LINK( this, ToolBox, ImplCustomMenuListener ) );
1583
1584 // make sure all disabled entries will be shown
1585 GetMenu()->SetMenuFlags(
1586 GetMenu()->GetMenuFlags() | MenuFlags::AlwaysShowDisabledEntries );
1587
1588 // toolbox might be destroyed during execute
1589 bool bBorderDel = false;
1590
1591 VclPtr<vcl::Window> pWin = this;
1592 tools::Rectangle aMenuRect = rRect;
1593 VclPtr<ImplBorderWindow> pBorderWin;
1594 if( aMenuRect.IsEmpty() && IsFloatingMode() )
1595 {
1596 // custom menu is placed in the decoration
1597 pBorderWin = dynamic_cast<ImplBorderWindow*>( GetWindow( GetWindowType::Border ) );
1598 if( pBorderWin && !pBorderWin->GetMenuRect().IsEmpty() )
1599 {
1600 pWin = pBorderWin;
1601 aMenuRect = pBorderWin->GetMenuRect();
1602 bBorderDel = true;
1603 }
1604 }
1605
1606 sal_uInt16 uId = GetMenu()->Execute( pWin, tools::Rectangle( ImplGetPopupPosition( aMenuRect ), Size() ),
1607 PopupMenuFlags::ExecuteDown | PopupMenuFlags::NoMouseUpClose );
1608
1609 if ( pWin->isDisposed() )
1610 return;
1611
1612 if( GetMenu() )
1613 GetMenu()->RemoveEventListener( LINK( this, ToolBox, ImplCustomMenuListener ) );
1614 if( bBorderDel )
1615 {
1616 if( pBorderWin->isDisposed() )
1617 return;
1618 }
1619
1620 pWin->Invalidate( aMenuRect );
1621
1622 if( uId )
1623 GrabFocusToDocument();
1624 }
1625
1626 // checks override first, useful during calculation of sizes
ImplIsFloatingMode() const1627 bool ToolBox::ImplIsFloatingMode() const
1628 {
1629 SAL_WARN_IF( mpData->mbAssumeDocked && mpData->mbAssumeFloating, "vcl",
1630 "cannot assume docked and floating" );
1631
1632 if( mpData->mbAssumeDocked )
1633 return false;
1634 else if( mpData->mbAssumeFloating )
1635 return true;
1636 else
1637 return IsFloatingMode();
1638 }
1639
1640 // checks override first, useful during calculation of sizes
ImplIsInPopupMode() const1641 bool ToolBox::ImplIsInPopupMode() const
1642 {
1643 if( mpData->mbAssumePopupMode )
1644 return true;
1645 else
1646 {
1647 ImplDockingWindowWrapper *pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper( this );
1648 return ( pWrapper && pWrapper->GetFloatingWindow() && static_cast<FloatingWindow*>(pWrapper->GetFloatingWindow())->IsInPopupMode() );
1649 }
1650 }
1651
Lock(bool bLock)1652 void ToolBox::Lock( bool bLock )
1653 {
1654 ImplDockingWindowWrapper *pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper( this );
1655 if( !pWrapper )
1656 return;
1657 if( mpData->mbIsLocked != bLock )
1658 {
1659 mpData->mbIsLocked = bLock;
1660 if( !ImplIsFloatingMode() )
1661 {
1662 mbCalc = true;
1663 mbFormat = true;
1664 SetSizePixel( CalcWindowSizePixel(1) );
1665 Invalidate();
1666 }
1667 }
1668 }
1669
AlwaysLocked()1670 bool ToolBox::AlwaysLocked()
1671 {
1672 // read config item to determine toolbox behaviour, used for subtoolbars
1673
1674 static int nAlwaysLocked = -1;
1675
1676 if( nAlwaysLocked == -1 )
1677 {
1678 nAlwaysLocked = 0; // ask configuration only once
1679
1680 utl::OConfigurationNode aNode = utl::OConfigurationTreeRoot::tryCreateWithComponentContext(
1681 comphelper::getProcessComponentContext(),
1682 u"/org.openoffice.Office.UI.GlobalSettings/Toolbars"_ustr ); // note: case sensitive !
1683 if ( aNode.isValid() )
1684 {
1685 // feature enabled ?
1686 bool bStatesEnabled = bool();
1687 css::uno::Any aValue = aNode.getNodeValue( u"StatesEnabled"_ustr );
1688 if( aValue >>= bStatesEnabled )
1689 {
1690 if( bStatesEnabled )
1691 {
1692 // now read the locking state
1693 utl::OConfigurationNode aNode2 = utl::OConfigurationTreeRoot::tryCreateWithComponentContext(
1694 comphelper::getProcessComponentContext(),
1695 u"/org.openoffice.Office.UI.GlobalSettings/Toolbars/States"_ustr ); // note: case sensitive !
1696
1697 bool bLocked = bool();
1698 css::uno::Any aValue2 = aNode2.getNodeValue( u"Locked"_ustr );
1699 if( aValue2 >>= bLocked )
1700 nAlwaysLocked = bLocked ? 1 : 0;
1701 }
1702 }
1703 }
1704 }
1705
1706 return nAlwaysLocked == 1;
1707 }
1708
WillUsePopupMode() const1709 bool ToolBox::WillUsePopupMode() const
1710 {
1711 return mpData->mbWillUsePopupMode;
1712 }
1713
WillUsePopupMode(bool b)1714 void ToolBox::WillUsePopupMode( bool b )
1715 {
1716 mpData->mbWillUsePopupMode = b;
1717 }
1718
DumpAsPropertyTree(tools::JsonWriter & rJsonWriter)1719 void ToolBox::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
1720 {
1721 DockingWindow::DumpAsPropertyTree(rJsonWriter);
1722
1723 auto childrenNode = rJsonWriter.startArray("children");
1724 for (ToolBox::ImplToolItems::size_type i = 0; i < GetItemCount(); ++i)
1725 {
1726 auto childNode = rJsonWriter.startStruct();
1727 ToolBoxItemId nId = GetItemId(i);
1728
1729 vcl::Window* pWindow = GetItemWindow(nId);
1730 if (pWindow)
1731 {
1732 pWindow->DumpAsPropertyTree(rJsonWriter);
1733 }
1734 else
1735 {
1736 OUString sCommand = GetItemCommand(nId);
1737 rJsonWriter.put("type", "toolitem");
1738 rJsonWriter.put("text", GetItemText(nId));
1739 rJsonWriter.put("command", sCommand);
1740 if (IsItemChecked(nId))
1741 rJsonWriter.put("selected", true);
1742 if (!IsItemVisible(nId))
1743 rJsonWriter.put("visible", false);
1744 if (GetItemBits(nId) & ToolBoxItemBits::DROPDOWN)
1745 rJsonWriter.put("dropdown", true);
1746 if (!IsItemEnabled(nId))
1747 rJsonWriter.put("enabled", false);
1748
1749 Image aImage = GetItemImage(nId);
1750 if (!sCommand.startsWith(".uno:") && !!aImage)
1751 {
1752 SvMemoryStream aOStm(6535, 6535);
1753 if(GraphicConverter::Export(aOStm, aImage.GetBitmapEx(), ConvertDataFormat::PNG) == ERRCODE_NONE)
1754 {
1755 css::uno::Sequence<sal_Int8> aSeq( static_cast<sal_Int8 const *>(aOStm.GetData()), aOStm.Tell());
1756 OStringBuffer aBuffer("data:image/png;base64,");
1757 ::comphelper::Base64::encode(aBuffer, aSeq);
1758 rJsonWriter.put("image", aBuffer);
1759 }
1760 }
1761 }
1762 }
1763 }
1764
1765 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1766