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 <accessibility/accessiblemenubasecomponent.hxx>
21 #include <accessibility/vclxaccessiblemenubar.hxx>
22 #include <accessibility/vclxaccessiblepopupmenu.hxx>
23
24 #include <comphelper/diagnose_ex.hxx>
25 #include <sal/log.hxx>
26
27 #include <comphelper/types.hxx>
28 #include <comphelper/lok.hxx>
29 #include <vcl/dialoghelper.hxx>
30 #include <vcl/svapp.hxx>
31 #include <vcl/mnemonic.hxx>
32 #include <vcl/image.hxx>
33 #include <vcl/event.hxx>
34 #include <vcl/help.hxx>
35 #include <vcl/toolkit/floatwin.hxx>
36 #include <vcl/decoview.hxx>
37 #include <vcl/menu.hxx>
38 #include <vcl/taskpanelist.hxx>
39 #include <vcl/settings.hxx>
40 #include <vcl/commandinfoprovider.hxx>
41
42 #include <salinst.hxx>
43 #include <svdata.hxx>
44 #include <strings.hrc>
45 #include <window.h>
46 #include <salmenu.hxx>
47 #include <salframe.hxx>
48
49 #include "menubarwindow.hxx"
50 #include "menufloatingwindow.hxx"
51 #include "menuitemlist.hxx"
52
53 #include <com/sun/star/uno/Reference.h>
54 #include <com/sun/star/lang/XComponent.hpp>
55 #include <com/sun/star/accessibility/XAccessible.hpp>
56 #include <vcl/toolkit/unowrap.hxx>
57 #include <rtl/ustrbuf.hxx>
58
59 #include <map>
60 #include <string_view>
61 #include <vector>
62
63 #include <officecfg/Office/Common.hxx>
64 #include <officecfg/VCL.hxx>
65
66 namespace vcl
67 {
68
69 struct MenuLayoutData : public ControlLayoutData
70 {
71 std::vector< sal_uInt16 > m_aLineItemIds;
72 std::map< sal_uInt16, tools::Rectangle > m_aVisibleItemBoundRects;
73 };
74
75 }
76
77 using namespace vcl;
78
79 constexpr auto EXTRAITEMHEIGHT = 4;
80 constexpr auto SPACE_AROUND_TITLE = 4;
81
ImplSetMenuItemData(MenuItemData * pData)82 static void ImplSetMenuItemData( MenuItemData* pData )
83 {
84 // convert data
85 if ( !pData->aImage )
86 pData->eType = MenuItemType::STRING;
87 else if ( pData->aText.isEmpty() )
88 pData->eType = MenuItemType::IMAGE;
89 else
90 pData->eType = MenuItemType::STRINGIMAGE;
91 }
92
93 namespace {
94
ImplClosePopupToolBox(const VclPtr<vcl::Window> & pWin)95 void ImplClosePopupToolBox( const VclPtr<vcl::Window>& pWin )
96 {
97 if ( pWin->GetType() == WindowType::TOOLBOX && ImplGetDockingManager()->IsInPopupMode( pWin ) )
98 {
99 SystemWindow* pFloatingWindow = ImplGetDockingManager()->GetFloatingWindow(pWin);
100 if (pFloatingWindow)
101 static_cast<FloatingWindow*>(pFloatingWindow)->EndPopupMode( FloatWinPopupEndFlags::CloseAll );
102 }
103 }
104
105 // TODO: Move to common code with the same function in toolbox
106 // Draw the ">>" - more indicator at the coordinates
lclDrawMoreIndicator(vcl::RenderContext & rRenderContext,const tools::Rectangle & rRect)107 void lclDrawMoreIndicator(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
108 {
109 auto popIt = rRenderContext.ScopedPush(PushFlags::FILLCOLOR | PushFlags::LINECOLOR);
110 rRenderContext.SetLineColor();
111
112 if (rRenderContext.GetSettings().GetStyleSettings().GetFaceColor().IsDark())
113 rRenderContext.SetFillColor(COL_WHITE);
114 else
115 rRenderContext.SetFillColor(COL_BLACK);
116 float fScaleFactor = rRenderContext.GetDPIScaleFactor();
117
118 int linewidth = 1 * fScaleFactor;
119 int space = 4 * fScaleFactor;
120
121 tools::Long width = 8 * fScaleFactor;
122 tools::Long height = 5 * fScaleFactor;
123
124 //Keep odd b/c drawing code works better
125 if ( height % 2 == 0 )
126 height--;
127
128 tools::Long heightOrig = height;
129
130 tools::Long x = rRect.Left() + (rRect.getOpenWidth() - width)/2 + 1;
131 tools::Long y = rRect.Top() + (rRect.getOpenHeight() - height)/2 + 1;
132 while( height >= 1)
133 {
134 rRenderContext.DrawRect( tools::Rectangle( x, y, x + linewidth, y ) );
135 x += space;
136 rRenderContext.DrawRect( tools::Rectangle( x, y, x + linewidth, y ) );
137 x -= space;
138 y++;
139 if( height <= heightOrig / 2 + 1) x--;
140 else x++;
141 height--;
142 }
143 }
144
145 } // end anonymous namespace
146
147
Menu()148 Menu::Menu()
149 : mpFirstDel(nullptr),
150 pItemList(new MenuItemList),
151 pStartedFrom(nullptr),
152 m_pWindow(nullptr),
153 nTitleHeight(0),
154 nEventId(nullptr),
155 mnHighlightedItemPos(ITEMPOS_INVALID),
156 nMenuFlags(MenuFlags::NONE),
157 nSelectedId(0),
158 nImgOrChkPos(0),
159 nTextPos(0),
160 bCanceled(false),
161 bInCallback(false),
162 bKilled(false)
163 {
164 }
165
~Menu()166 Menu::~Menu()
167 {
168 disposeOnce();
169 }
170
dispose()171 void Menu::dispose()
172 {
173 ImplCallEventListeners( VclEventId::ObjectDying, ITEMPOS_INVALID );
174
175 m_pWindow.disposeAndClear();
176
177 if (mpAccessible.is())
178 mpAccessible->dispose();
179 mpAccessible.clear();
180
181 if ( nEventId )
182 Application::RemoveUserEvent( nEventId );
183
184 // Notify deletion of this menu
185 ImplMenuDelData* pDelData = mpFirstDel;
186 while ( pDelData )
187 {
188 pDelData->mpMenu = nullptr;
189 pDelData = pDelData->mpNext;
190 }
191
192 bKilled = true;
193
194 // tdf#140225 when clearing pItemList, keep SalMenu in sync with
195 // their removal during menu teardown
196 for (size_t n = pItemList->size(); n;)
197 {
198 --n;
199 if (mpSalMenu)
200 mpSalMenu->RemoveItem(n);
201 pItemList->Remove(n);
202 }
203
204 assert(!pItemList->size());
205
206 mpLayoutData.reset();
207
208 // Native-support: destroy SalMenu
209 mpSalMenu.reset();
210
211 pStartedFrom.reset();
212 m_pWindow.reset();
213 VclReferenceBase::dispose();
214 }
215
CreateAutoMnemonics()216 void Menu::CreateAutoMnemonics()
217 {
218 MnemonicGenerator aMnemonicGenerator;
219 size_t n;
220 for ( n = 0; n < pItemList->size(); n++ )
221 {
222 MenuItemData* pData = pItemList->GetDataFromPos( n );
223 if ( ! (pData->nBits & MenuItemBits::NOSELECT ) )
224 aMnemonicGenerator.RegisterMnemonic( pData->aText );
225 }
226 for ( n = 0; n < pItemList->size(); n++ )
227 {
228 MenuItemData* pData = pItemList->GetDataFromPos( n );
229 if ( ! (pData->nBits & MenuItemBits::NOSELECT ) )
230 pData->aText = aMnemonicGenerator.CreateMnemonic( pData->aText );
231 }
232 }
233
Activate()234 void Menu::Activate()
235 {
236 bInCallback = true;
237
238 ImplMenuDelData aDelData( this );
239
240 ImplCallEventListeners( VclEventId::MenuActivate, ITEMPOS_INVALID );
241
242 if( !aDelData.isDeleted() )
243 {
244 if ( !aActivateHdl.Call( this ) )
245 {
246 if( !aDelData.isDeleted() )
247 {
248 Menu* pStartMenu = ImplGetStartMenu();
249 if ( pStartMenu && ( pStartMenu != this ) )
250 {
251 pStartMenu->bInCallback = true;
252 // MT 11/01: Call EventListener here? I don't know...
253 pStartMenu->aActivateHdl.Call( this );
254 pStartMenu->bInCallback = false;
255 }
256 }
257 }
258 bInCallback = false;
259 }
260
261 if (!aDelData.isDeleted() && !(nMenuFlags & MenuFlags::NoAutoMnemonics))
262 CreateAutoMnemonics();
263 }
264
Deactivate()265 void Menu::Deactivate()
266 {
267 for ( size_t n = pItemList->size(); n; )
268 {
269 MenuItemData* pData = pItemList->GetDataFromPos( --n );
270 if ( pData->bIsTemporary )
271 {
272 if ( ImplGetSalMenu() )
273 ImplGetSalMenu()->RemoveItem( n );
274
275 pItemList->Remove( n );
276 }
277 }
278
279 bInCallback = true;
280
281 ImplMenuDelData aDelData( this );
282
283 Menu* pStartMenu = ImplGetStartMenu();
284 ImplCallEventListeners( VclEventId::MenuDeactivate, ITEMPOS_INVALID );
285
286 if( !aDelData.isDeleted() )
287 {
288 if ( !aDeactivateHdl.Call( this ) )
289 {
290 if( !aDelData.isDeleted() )
291 {
292 if ( pStartMenu && ( pStartMenu != this ) )
293 {
294 pStartMenu->bInCallback = true;
295 pStartMenu->aDeactivateHdl.Call( this );
296 pStartMenu->bInCallback = false;
297 }
298 }
299 }
300 }
301
302 if( !aDelData.isDeleted() )
303 {
304 bInCallback = false;
305 }
306 }
307
ImplSelect()308 void Menu::ImplSelect()
309 {
310 MenuItemData* pData = GetItemList()->GetData( nSelectedId );
311 if ( pData && (pData->nBits & MenuItemBits::AUTOCHECK) )
312 {
313 bool bChecked = IsItemChecked( nSelectedId );
314 if ( pData->nBits & MenuItemBits::RADIOCHECK )
315 {
316 if ( !bChecked )
317 CheckItem( nSelectedId );
318 }
319 else
320 CheckItem( nSelectedId, !bChecked );
321 }
322
323 // call select
324 ImplSVData* pSVData = ImplGetSVData();
325 pSVData->maAppData.mpActivePopupMenu = nullptr; // if new execute in select()
326 nEventId = Application::PostUserEvent( LINK( this, Menu, ImplCallSelect ) );
327 }
328
Select()329 void Menu::Select()
330 {
331 ImplMenuDelData aDelData( this );
332
333 ImplCallEventListeners( VclEventId::MenuSelect, GetItemPos( GetCurItemId() ) );
334 if (aDelData.isDeleted())
335 return;
336 if (aSelectHdl.Call(this))
337 return;
338 if (aDelData.isDeleted())
339 return;
340 Menu* pStartMenu = ImplGetStartMenu();
341 if (!pStartMenu || (pStartMenu == this))
342 return;
343 pStartMenu->nSelectedId = nSelectedId;
344 pStartMenu->sSelectedIdent = sSelectedIdent;
345 pStartMenu->aSelectHdl.Call( this );
346 }
347
348 #if defined(MACOSX)
ImplSelectWithStart(Menu * pSMenu)349 void Menu::ImplSelectWithStart( Menu* pSMenu )
350 {
351 auto pOldStartedFrom = pStartedFrom;
352 pStartedFrom = pSMenu;
353 auto pOldStartedStarted = pOldStartedFrom ? pOldStartedFrom->pStartedFrom : VclPtr<Menu>();
354 Select();
355 if( pOldStartedFrom )
356 pOldStartedFrom->pStartedFrom = pOldStartedStarted;
357 pStartedFrom = pOldStartedFrom;
358 }
359 #endif
360
ImplCallEventListeners(VclEventId nEvent,sal_uInt16 nPos)361 void Menu::ImplCallEventListeners( VclEventId nEvent, sal_uInt16 nPos )
362 {
363 ImplMenuDelData aDelData( this );
364
365 VclMenuEvent aEvent( this, nEvent, nPos );
366
367 // This is needed by atk accessibility bridge
368 if ( nEvent == VclEventId::MenuHighlight )
369 {
370 Application::ImplCallEventListeners( aEvent );
371 }
372
373 if ( !aDelData.isDeleted() )
374 {
375 // Copy the list, because this can be destroyed when calling a Link...
376 std::list<Link<VclMenuEvent&,void>> aCopy( maEventListeners );
377 for ( const auto& rLink : aCopy )
378 {
379 if( std::find(maEventListeners.begin(), maEventListeners.end(), rLink) != maEventListeners.end() )
380 rLink.Call( aEvent );
381 }
382 }
383 }
384
AddEventListener(const Link<VclMenuEvent &,void> & rEventListener)385 void Menu::AddEventListener( const Link<VclMenuEvent&,void>& rEventListener )
386 {
387 maEventListeners.push_back( rEventListener );
388 }
389
RemoveEventListener(const Link<VclMenuEvent &,void> & rEventListener)390 void Menu::RemoveEventListener( const Link<VclMenuEvent&,void>& rEventListener )
391 {
392 maEventListeners.remove( rEventListener );
393 }
394
NbcInsertItem(sal_uInt16 nId,MenuItemBits nBits,const OUString & rStr,Menu * pMenu,size_t nPos,const OUString & rIdent)395 MenuItemData* Menu::NbcInsertItem(sal_uInt16 nId, MenuItemBits nBits,
396 const OUString& rStr, Menu* pMenu,
397 size_t nPos, const OUString &rIdent)
398 {
399 // put Item in MenuItemList
400 MenuItemData* pData = pItemList->Insert(nId, MenuItemType::STRING,
401 nBits, rStr, pMenu, nPos, rIdent);
402
403 // update native menu
404 if (ImplGetSalMenu() && pData->pSalMenuItem)
405 ImplGetSalMenu()->InsertItem(pData->pSalMenuItem.get(), nPos);
406
407 return pData;
408 }
409
InsertItem(sal_uInt16 nItemId,const OUString & rStr,MenuItemBits nItemBits,const OUString & rIdent,sal_uInt16 nPos)410 void Menu::InsertItem(sal_uInt16 nItemId, const OUString& rStr, MenuItemBits nItemBits,
411 const OUString &rIdent, sal_uInt16 nPos)
412 {
413 SAL_WARN_IF( !nItemId, "vcl", "Menu::InsertItem(): ItemId == 0" );
414 SAL_WARN_IF( GetItemPos( nItemId ) != MENU_ITEM_NOTFOUND, "vcl",
415 "Menu::InsertItem(): ItemId already exists" );
416
417 // if Position > ItemCount, append
418 if ( nPos >= pItemList->size() )
419 nPos = MENU_APPEND;
420
421 // put Item in MenuItemList
422 NbcInsertItem(nItemId, nItemBits, rStr, this, nPos, rIdent);
423
424 vcl::Window* pWin = GetWindow();
425 mpLayoutData.reset();
426 if ( pWin )
427 {
428 ImplCalcSize( pWin );
429 if ( pWin->IsVisible() )
430 pWin->Invalidate();
431 }
432 ImplCallEventListeners( VclEventId::MenuInsertItem, nPos );
433 }
434
InsertItem(sal_uInt16 nItemId,const Image & rImage,MenuItemBits nItemBits,const OUString & rIdent,sal_uInt16 nPos)435 void Menu::InsertItem(sal_uInt16 nItemId, const Image& rImage,
436 MenuItemBits nItemBits, const OUString &rIdent, sal_uInt16 nPos)
437 {
438 InsertItem(nItemId, OUString(), nItemBits, rIdent, nPos);
439 SetItemImage( nItemId, rImage );
440 }
441
InsertItem(sal_uInt16 nItemId,const OUString & rStr,const Image & rImage,MenuItemBits nItemBits,const OUString & rIdent,sal_uInt16 nPos)442 void Menu::InsertItem(sal_uInt16 nItemId, const OUString& rStr,
443 const Image& rImage, MenuItemBits nItemBits,
444 const OUString &rIdent, sal_uInt16 nPos)
445 {
446 InsertItem(nItemId, rStr, nItemBits, rIdent, nPos);
447 SetItemImage( nItemId, rImage );
448 }
449
InsertSeparator(const OUString & rIdent,sal_uInt16 nPos)450 void Menu::InsertSeparator(const OUString &rIdent, sal_uInt16 nPos)
451 {
452 // do nothing if it's a menu bar
453 if (IsMenuBar())
454 return;
455
456 // if position > ItemCount, append
457 if ( nPos >= pItemList->size() )
458 nPos = MENU_APPEND;
459
460 // put separator in item list
461 pItemList->InsertSeparator(rIdent, nPos);
462
463 // update native menu
464 size_t itemPos = ( nPos != MENU_APPEND ) ? nPos : pItemList->size() - 1;
465 MenuItemData *pData = pItemList->GetDataFromPos( itemPos );
466 if( ImplGetSalMenu() && pData && pData->pSalMenuItem )
467 ImplGetSalMenu()->InsertItem( pData->pSalMenuItem.get(), nPos );
468
469 mpLayoutData.reset();
470
471 ImplCallEventListeners( VclEventId::MenuInsertItem, nPos );
472 }
473
RemoveItem(sal_uInt16 nPos)474 void Menu::RemoveItem( sal_uInt16 nPos )
475 {
476 bool bRemove = false;
477
478 if ( nPos < GetItemCount() )
479 {
480 // update native menu
481 if( ImplGetSalMenu() )
482 ImplGetSalMenu()->RemoveItem( nPos );
483
484 pItemList->Remove( nPos );
485 bRemove = true;
486 }
487
488 vcl::Window* pWin = GetWindow();
489 if ( pWin )
490 {
491 ImplCalcSize( pWin );
492 if ( pWin->IsVisible() )
493 pWin->Invalidate();
494 }
495 mpLayoutData.reset();
496
497 if ( bRemove )
498 ImplCallEventListeners( VclEventId::MenuRemoveItem, nPos );
499 }
500
ImplCopyItem(Menu * pThis,const Menu & rMenu,sal_uInt16 nPos,sal_uInt16 nNewPos)501 static void ImplCopyItem( Menu* pThis, const Menu& rMenu, sal_uInt16 nPos, sal_uInt16 nNewPos )
502 {
503 MenuItemType eType = rMenu.GetItemType( nPos );
504
505 if ( eType == MenuItemType::DONTKNOW )
506 return;
507
508 if ( eType == MenuItemType::SEPARATOR )
509 pThis->InsertSeparator( {}, nNewPos );
510 else
511 {
512 sal_uInt16 nId = rMenu.GetItemId( nPos );
513
514 SAL_WARN_IF( pThis->GetItemPos( nId ) != MENU_ITEM_NOTFOUND, "vcl",
515 "Menu::CopyItem(): ItemId already exists" );
516
517 MenuItemData* pData = rMenu.GetItemList()->GetData( nId );
518
519 if (!pData)
520 return;
521
522 if ( eType == MenuItemType::STRINGIMAGE )
523 pThis->InsertItem( nId, pData->aText, pData->aImage, pData->nBits, pData->sIdent, nNewPos );
524 else if ( eType == MenuItemType::STRING )
525 pThis->InsertItem( nId, pData->aText, pData->nBits, pData->sIdent, nNewPos );
526 else
527 pThis->InsertItem( nId, pData->aImage, pData->nBits, pData->sIdent, nNewPos );
528
529 if ( rMenu.IsItemChecked( nId ) )
530 pThis->CheckItem( nId );
531 if ( !rMenu.IsItemEnabled( nId ) )
532 pThis->EnableItem( nId, false );
533 pThis->SetHelpId( nId, pData->aHelpId );
534 pThis->SetHelpText( nId, pData->aHelpText );
535 pThis->SetAccelKey( nId, pData->aAccelKey );
536 pThis->SetItemCommand( nId, pData->aCommandStr );
537 pThis->SetHelpCommand( nId, pData->aHelpCommandStr );
538
539 PopupMenu* pSubMenu = rMenu.GetPopupMenu( nId );
540 if ( pSubMenu )
541 {
542 // create auto-copy
543 VclPtr<PopupMenu> pNewMenu = VclPtr<PopupMenu>::Create( *pSubMenu );
544 pThis->SetPopupMenu( nId, pNewMenu );
545 }
546 }
547 }
548
Clear()549 void Menu::Clear()
550 {
551 for ( sal_uInt16 i = GetItemCount(); i; i-- )
552 RemoveItem( 0 );
553 }
554
GetItemCount() const555 sal_uInt16 Menu::GetItemCount() const
556 {
557 return static_cast<sal_uInt16>(pItemList->size());
558 }
559
HasValidEntries(bool bCheckPopups) const560 bool Menu::HasValidEntries(bool bCheckPopups) const
561 {
562 bool bValidEntries = false;
563 sal_uInt16 nCount = GetItemCount();
564 for (sal_uInt16 n = 0; !bValidEntries && (n < nCount); n++)
565 {
566 MenuItemData* pItem = pItemList->GetDataFromPos(n);
567 if (pItem->bEnabled && (pItem->eType != MenuItemType::SEPARATOR))
568 {
569 if (bCheckPopups && pItem->pSubMenu)
570 bValidEntries = pItem->pSubMenu->HasValidEntries(true);
571 else
572 bValidEntries = true;
573 }
574 }
575 return bValidEntries;
576 }
577
ImplGetVisibleItemCount() const578 sal_uInt16 Menu::ImplGetVisibleItemCount() const
579 {
580 sal_uInt16 nItems = 0;
581 for ( size_t n = pItemList->size(); n; )
582 {
583 if ( ImplIsVisible( --n ) )
584 nItems++;
585 }
586 return nItems;
587 }
588
ImplGetFirstVisible() const589 sal_uInt16 Menu::ImplGetFirstVisible() const
590 {
591 for ( size_t n = 0; n < pItemList->size(); n++ )
592 {
593 if ( ImplIsVisible( n ) )
594 return n;
595 }
596 return ITEMPOS_INVALID;
597 }
598
ImplGetPrevVisible(sal_uInt16 nPos) const599 sal_uInt16 Menu::ImplGetPrevVisible( sal_uInt16 nPos ) const
600 {
601 for ( size_t n = nPos; n; )
602 {
603 if (ImplIsVisible(--n))
604 return n;
605 }
606 return ITEMPOS_INVALID;
607 }
608
ImplGetNextVisible(sal_uInt16 nPos) const609 sal_uInt16 Menu::ImplGetNextVisible( sal_uInt16 nPos ) const
610 {
611 for ( size_t n = nPos+1; n < pItemList->size(); n++ )
612 {
613 if ( ImplIsVisible( n ) )
614 return n;
615 }
616 return ITEMPOS_INVALID;
617 }
618
GetItemId(sal_uInt16 nPos) const619 sal_uInt16 Menu::GetItemId(sal_uInt16 nPos) const
620 {
621 MenuItemData* pData = pItemList->GetDataFromPos( nPos );
622
623 if ( pData )
624 return pData->nId;
625 else
626 return 0;
627 }
628
GetItemId(std::u16string_view rIdent) const629 sal_uInt16 Menu::GetItemId(std::u16string_view rIdent) const
630 {
631 for (size_t n = 0; n < pItemList->size(); ++n)
632 {
633 MenuItemData* pData = pItemList->GetDataFromPos(n);
634 if (pData && pData->sIdent == rIdent)
635 return pData->nId;
636 }
637 return MENU_ITEM_NOTFOUND;
638 }
639
GetItemPos(sal_uInt16 nItemId) const640 sal_uInt16 Menu::GetItemPos( sal_uInt16 nItemId ) const
641 {
642 size_t nPos;
643 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
644
645 if ( pData )
646 return static_cast<sal_uInt16>(nPos);
647 else
648 return MENU_ITEM_NOTFOUND;
649 }
650
GetItemType(sal_uInt16 nPos) const651 MenuItemType Menu::GetItemType( sal_uInt16 nPos ) const
652 {
653 MenuItemData* pData = pItemList->GetDataFromPos( nPos );
654
655 if ( pData )
656 return pData->eType;
657 else
658 return MenuItemType::DONTKNOW;
659 }
660
GetItemIdent(sal_uInt16 nId) const661 OUString Menu::GetItemIdent(sal_uInt16 nId) const
662 {
663 const MenuItemData* pData = pItemList->GetData(nId);
664 return pData ? pData->sIdent : OUString();
665 }
666
SetItemBits(sal_uInt16 nItemId,MenuItemBits nBits)667 void Menu::SetItemBits( sal_uInt16 nItemId, MenuItemBits nBits )
668 {
669 size_t nPos;
670 MenuItemData* pData = pItemList->GetData(nItemId, nPos);
671
672 if (pData && (pData->nBits != nBits))
673 {
674 // these two menu item bits are relevant for (accessible) role
675 const MenuItemBits nRoleMask = MenuItemBits::CHECKABLE | MenuItemBits::RADIOCHECK;
676 const bool bRoleBitsChanged = (pData->nBits & nRoleMask) != (nBits & nRoleMask);
677
678 pData->nBits = nBits;
679
680 // update native menu
681 if (ImplGetSalMenu())
682 ImplGetSalMenu()->SetItemBits(nPos, nBits);
683
684 if (bRoleBitsChanged)
685 ImplCallEventListeners(VclEventId::MenuItemRoleChanged, nPos);
686 }
687 }
688
GetItemBits(sal_uInt16 nItemId) const689 MenuItemBits Menu::GetItemBits( sal_uInt16 nItemId ) const
690 {
691 MenuItemBits nBits = MenuItemBits::NONE;
692 MenuItemData* pData = pItemList->GetData( nItemId );
693 if ( pData )
694 nBits = pData->nBits;
695 return nBits;
696 }
697
SetUserValue(sal_uInt16 nItemId,void * nUserValue,MenuUserDataReleaseFunction aFunc)698 void Menu::SetUserValue(sal_uInt16 nItemId, void* nUserValue, MenuUserDataReleaseFunction aFunc)
699 {
700 MenuItemData* pData = pItemList->GetData(nItemId);
701 if (pData)
702 {
703 if (pData->aUserValueReleaseFunc)
704 pData->aUserValueReleaseFunc(pData->nUserValue);
705 pData->aUserValueReleaseFunc = aFunc;
706 pData->nUserValue = nUserValue;
707 }
708 }
709
GetUserValue(sal_uInt16 nItemId) const710 void* Menu::GetUserValue( sal_uInt16 nItemId ) const
711 {
712 MenuItemData* pData = pItemList->GetData( nItemId );
713 return pData ? pData->nUserValue : nullptr;
714 }
715
SetPopupMenu(sal_uInt16 nItemId,PopupMenu * pMenu)716 void Menu::SetPopupMenu( sal_uInt16 nItemId, PopupMenu* pMenu )
717 {
718 size_t nPos;
719 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
720
721 // Item does not exist -> return NULL
722 if ( !pData )
723 return;
724
725 // same menu, nothing to do
726 if ( pData->pSubMenu.get() == pMenu )
727 return;
728
729 // remove old menu
730 auto oldSubMenu = pData->pSubMenu;
731
732 // data exchange
733 pData->pSubMenu = pMenu;
734
735 // #112023# Make sure pStartedFrom does not point to invalid (old) data
736 if ( pData->pSubMenu )
737 pData->pSubMenu->pStartedFrom = nullptr;
738
739 // set native submenu
740 if( ImplGetSalMenu() && pData->pSalMenuItem )
741 {
742 if( pMenu )
743 ImplGetSalMenu()->SetSubMenu( pData->pSalMenuItem.get(), pMenu->ImplGetSalMenu(), nPos );
744 else
745 ImplGetSalMenu()->SetSubMenu( pData->pSalMenuItem.get(), nullptr, nPos );
746 }
747
748 oldSubMenu.disposeAndClear();
749
750 ImplCallEventListeners( VclEventId::MenuSubmenuChanged, nPos );
751 }
752
GetPopupMenu(sal_uInt16 nItemId) const753 PopupMenu* Menu::GetPopupMenu( sal_uInt16 nItemId ) const
754 {
755 MenuItemData* pData = pItemList->GetData( nItemId );
756
757 if ( pData )
758 return pData->pSubMenu.get();
759 else
760 return nullptr;
761 }
762
SetAccelKey(sal_uInt16 nItemId,const KeyCode & rKeyCode)763 void Menu::SetAccelKey( sal_uInt16 nItemId, const KeyCode& rKeyCode )
764 {
765 size_t nPos;
766 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
767
768 if ( !pData )
769 return;
770
771 if ( pData->aAccelKey == rKeyCode )
772 return;
773
774 pData->aAccelKey = rKeyCode;
775
776 // update native menu
777 if( ImplGetSalMenu() && pData->pSalMenuItem )
778 ImplGetSalMenu()->SetAccelerator( nPos, pData->pSalMenuItem.get(), rKeyCode, rKeyCode.GetName() );
779 }
780
GetAccelKey(sal_uInt16 nItemId) const781 KeyCode Menu::GetAccelKey( sal_uInt16 nItemId ) const
782 {
783 MenuItemData* pData = pItemList->GetData( nItemId );
784
785 if ( pData )
786 return pData->aAccelKey;
787 else
788 return KeyCode();
789 }
790
GetActivationKey(sal_uInt16 nItemId) const791 KeyEvent Menu::GetActivationKey( sal_uInt16 nItemId ) const
792 {
793 KeyEvent aRet;
794 MenuItemData* pData = pItemList->GetData( nItemId );
795 if( pData )
796 {
797 sal_Int32 nPos = pData->aText.indexOf( '~' );
798 if( nPos != -1 && nPos < pData->aText.getLength()-1 )
799 {
800 sal_uInt16 nCode = 0;
801 sal_Unicode cAccel = pData->aText[nPos+1];
802 if( cAccel >= 'a' && cAccel <= 'z' )
803 nCode = KEY_A + (cAccel-'a');
804 else if( cAccel >= 'A' && cAccel <= 'Z' )
805 nCode = KEY_A + (cAccel-'A');
806 else if( cAccel >= '0' && cAccel <= '9' )
807 nCode = KEY_0 + (cAccel-'0');
808
809 aRet = KeyEvent( cAccel, KeyCode( nCode, KEY_MOD2 ) );
810 }
811
812 }
813 return aRet;
814 }
815
CheckItem(sal_uInt16 nItemId,bool bCheck)816 void Menu::CheckItem( sal_uInt16 nItemId, bool bCheck )
817 {
818 size_t nPos;
819 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
820
821 if ( !pData || pData->bChecked == bCheck )
822 return;
823
824 // if radio-check, then uncheck previous
825 if ( bCheck && (pData->nBits & MenuItemBits::AUTOCHECK) &&
826 (pData->nBits & MenuItemBits::RADIOCHECK) )
827 {
828 MenuItemData* pGroupData;
829 sal_uInt16 nGroupPos;
830 sal_uInt16 nItemCount = GetItemCount();
831 bool bFound = false;
832
833 nGroupPos = nPos;
834 while ( nGroupPos )
835 {
836 pGroupData = pItemList->GetDataFromPos( nGroupPos-1 );
837 if ( pGroupData->nBits & MenuItemBits::RADIOCHECK )
838 {
839 if ( IsItemChecked( pGroupData->nId ) )
840 {
841 CheckItem( pGroupData->nId, false );
842 bFound = true;
843 break;
844 }
845 }
846 else
847 break;
848 nGroupPos--;
849 }
850
851 if ( !bFound )
852 {
853 nGroupPos = nPos+1;
854 while ( nGroupPos < nItemCount )
855 {
856 pGroupData = pItemList->GetDataFromPos( nGroupPos );
857 if ( pGroupData->nBits & MenuItemBits::RADIOCHECK )
858 {
859 if ( IsItemChecked( pGroupData->nId ) )
860 {
861 CheckItem( pGroupData->nId, false );
862 break;
863 }
864 }
865 else
866 break;
867 nGroupPos++;
868 }
869 }
870 }
871
872 pData->bChecked = bCheck;
873
874 // update native menu
875 if( ImplGetSalMenu() )
876 ImplGetSalMenu()->CheckItem( nPos, bCheck );
877
878 ImplCallEventListeners( bCheck ? VclEventId::MenuItemChecked : VclEventId::MenuItemUnchecked, nPos );
879 }
880
CheckItem(std::u16string_view rIdent,bool bCheck)881 void Menu::CheckItem( std::u16string_view rIdent , bool bCheck )
882 {
883 CheckItem( GetItemId( rIdent ), bCheck );
884 }
885
IsItemCheckable(sal_uInt16 nItemId) const886 bool Menu::IsItemCheckable(sal_uInt16 nItemId) const
887 {
888 size_t nPos;
889 MenuItemData* pData = pItemList->GetData(nItemId, nPos);
890
891 if (!pData)
892 return false;
893
894 return pData->HasCheck();
895 }
896
IsItemChecked(sal_uInt16 nItemId) const897 bool Menu::IsItemChecked( sal_uInt16 nItemId ) const
898 {
899 size_t nPos;
900 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
901
902 if ( !pData )
903 return false;
904
905 return pData->bChecked;
906 }
907
EnableItem(sal_uInt16 nItemId,bool bEnable)908 void Menu::EnableItem( sal_uInt16 nItemId, bool bEnable )
909 {
910 size_t nPos;
911 MenuItemData* pItemData = pItemList->GetData( nItemId, nPos );
912
913 if ( !(pItemData && ( pItemData->bEnabled != bEnable )) )
914 return;
915
916 pItemData->bEnabled = bEnable;
917
918 vcl::Window* pWin = GetWindow();
919 if ( pWin && pWin->IsVisible() )
920 {
921 SAL_WARN_IF(!IsMenuBar(), "vcl", "Menu::EnableItem - Popup visible!" );
922 tools::Long nX = 0;
923 size_t nCount = pItemList->size();
924 for ( size_t n = 0; n < nCount; n++ )
925 {
926 MenuItemData* pData = pItemList->GetDataFromPos( n );
927 if ( n == nPos )
928 {
929 pWin->Invalidate( tools::Rectangle( Point( nX, 0 ), Size( pData->aSz.Width(), pData->aSz.Height() ) ) );
930 break;
931 }
932 nX += pData->aSz.Width();
933 }
934 }
935 // update native menu
936 if( ImplGetSalMenu() )
937 ImplGetSalMenu()->EnableItem( nPos, bEnable );
938
939 ImplCallEventListeners( bEnable ? VclEventId::MenuEnable : VclEventId::MenuDisable, nPos );
940 }
941
IsItemEnabled(sal_uInt16 nItemId) const942 bool Menu::IsItemEnabled( sal_uInt16 nItemId ) const
943 {
944 size_t nPos;
945 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
946
947 if ( !pData )
948 return false;
949
950 return pData->bEnabled;
951 }
952
ShowItem(sal_uInt16 nItemId,bool bVisible)953 void Menu::ShowItem( sal_uInt16 nItemId, bool bVisible )
954 {
955 size_t nPos;
956 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
957
958 SAL_WARN_IF(IsMenuBar() && !bVisible , "vcl", "Menu::ShowItem - ignored for menu bar entries!");
959 if (IsMenuBar() || !pData || (pData->bVisible == bVisible))
960 return;
961
962 vcl::Window* pWin = GetWindow();
963 if ( pWin && pWin->IsVisible() )
964 {
965 SAL_WARN( "vcl", "Menu::ShowItem - ignored for visible popups!" );
966 return;
967 }
968 pData->bVisible = bVisible;
969
970 // update native menu
971 if( ImplGetSalMenu() )
972 ImplGetSalMenu()->ShowItem( nPos, bVisible );
973 }
974
SetItemText(sal_uInt16 nItemId,const OUString & rStr)975 void Menu::SetItemText( sal_uInt16 nItemId, const OUString& rStr )
976 {
977 size_t nPos;
978 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
979
980 if ( !pData )
981 return;
982
983 if ( rStr == pData->aText )
984 return;
985
986 pData->aText = rStr;
987 // Clear layout for aText.
988 pData->aTextGlyphs.Invalidate();
989 ImplSetMenuItemData( pData );
990 // update native menu
991 if( ImplGetSalMenu() && pData->pSalMenuItem )
992 ImplGetSalMenu()->SetItemText( nPos, pData->pSalMenuItem.get(), rStr );
993
994 vcl::Window* pWin = GetWindow();
995 mpLayoutData.reset();
996 if (pWin && IsMenuBar())
997 {
998 ImplCalcSize( pWin );
999 if ( pWin->IsVisible() )
1000 pWin->Invalidate();
1001 }
1002
1003 ImplCallEventListeners( VclEventId::MenuItemTextChanged, nPos );
1004 }
1005
GetItemText(sal_uInt16 nItemId) const1006 OUString Menu::GetItemText( sal_uInt16 nItemId ) const
1007 {
1008 size_t nPos;
1009 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
1010
1011 if ( pData )
1012 return pData->aText;
1013
1014 return OUString();
1015 }
1016
SetItemImage(sal_uInt16 nItemId,const Image & rImage)1017 void Menu::SetItemImage( sal_uInt16 nItemId, const Image& rImage )
1018 {
1019 size_t nPos;
1020 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
1021
1022 if ( !pData )
1023 return;
1024
1025 pData->aImage = rImage;
1026 ImplSetMenuItemData( pData );
1027
1028 // update native menu
1029 if( ImplGetSalMenu() && pData->pSalMenuItem )
1030 ImplGetSalMenu()->SetItemImage( nPos, pData->pSalMenuItem.get(), rImage );
1031 }
1032
GetItemImage(sal_uInt16 nItemId) const1033 Image Menu::GetItemImage( sal_uInt16 nItemId ) const
1034 {
1035 MenuItemData* pData = pItemList->GetData( nItemId );
1036
1037 if ( pData )
1038 return pData->aImage;
1039 else
1040 return Image();
1041 }
1042
SetItemCommand(sal_uInt16 nItemId,const OUString & rCommand)1043 void Menu::SetItemCommand( sal_uInt16 nItemId, const OUString& rCommand )
1044 {
1045 size_t nPos;
1046 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
1047
1048 if ( pData )
1049 pData->aCommandStr = rCommand;
1050 }
1051
GetItemCommand(sal_uInt16 nItemId) const1052 OUString Menu::GetItemCommand( sal_uInt16 nItemId ) const
1053 {
1054 MenuItemData* pData = pItemList->GetData( nItemId );
1055
1056 if (pData)
1057 return pData->aCommandStr;
1058
1059 return OUString();
1060 }
1061
SetHelpCommand(sal_uInt16 nItemId,const OUString & rStr)1062 void Menu::SetHelpCommand( sal_uInt16 nItemId, const OUString& rStr )
1063 {
1064 MenuItemData* pData = pItemList->GetData( nItemId );
1065
1066 if ( pData )
1067 pData->aHelpCommandStr = rStr;
1068 }
1069
GetHelpCommand(sal_uInt16 nItemId) const1070 OUString Menu::GetHelpCommand( sal_uInt16 nItemId ) const
1071 {
1072 MenuItemData* pData = pItemList->GetData( nItemId );
1073
1074 if ( pData )
1075 return pData->aHelpCommandStr;
1076
1077 return OUString();
1078 }
1079
SetHelpText(sal_uInt16 nItemId,const OUString & rStr)1080 void Menu::SetHelpText( sal_uInt16 nItemId, const OUString& rStr )
1081 {
1082 MenuItemData* pData = pItemList->GetData( nItemId );
1083
1084 if ( pData )
1085 pData->aHelpText = rStr;
1086 }
1087
ImplGetHelpText(sal_uInt16 nItemId) const1088 OUString Menu::ImplGetHelpText( sal_uInt16 nItemId ) const
1089 {
1090 MenuItemData* pData = pItemList->GetData( nItemId );
1091
1092 if (!pData)
1093 return OUString();
1094
1095 if ( pData->aHelpText.isEmpty() &&
1096 (( !pData->aHelpId.isEmpty() ) || ( !pData->aCommandStr.isEmpty() )))
1097 {
1098 Help* pHelp = Application::GetHelp();
1099 if ( pHelp )
1100 {
1101 if (!pData->aCommandStr.isEmpty())
1102 pData->aHelpText = pHelp->GetHelpText( pData->aCommandStr );
1103 if (pData->aHelpText.isEmpty() && !pData->aHelpId.isEmpty())
1104 pData->aHelpText = pHelp->GetHelpText( pData->aHelpId );
1105 }
1106 }
1107
1108 //Fallback to Menu::GetAccessibleDescription without reentry to GetHelpText()
1109 if (pData->aHelpText.isEmpty())
1110 return pData->aAccessibleDescription;
1111 return pData->aHelpText;
1112 }
1113
GetHelpText(sal_uInt16 nItemId) const1114 OUString Menu::GetHelpText( sal_uInt16 nItemId ) const
1115 {
1116 return ImplGetHelpText( nItemId );
1117 }
1118
SetTipHelpText(sal_uInt16 nItemId,const OUString & rStr)1119 void Menu::SetTipHelpText( sal_uInt16 nItemId, const OUString& rStr )
1120 {
1121 MenuItemData* pData = pItemList->GetData( nItemId );
1122
1123 if ( pData )
1124 {
1125 pData->aTipHelpText = rStr;
1126
1127 if (ImplGetSalMenu() && pData->pSalMenuItem)
1128 ImplGetSalMenu()->SetItemTooltip(pData->pSalMenuItem.get(), rStr);
1129 }
1130 }
1131
GetTipHelpText(sal_uInt16 nItemId) const1132 OUString Menu::GetTipHelpText( sal_uInt16 nItemId ) const
1133 {
1134 MenuItemData* pData = pItemList->GetData( nItemId );
1135
1136 if ( pData )
1137 return pData->aTipHelpText;
1138
1139 return OUString();
1140 }
1141
SetHelpId(sal_uInt16 nItemId,const OUString & rHelpId)1142 void Menu::SetHelpId( sal_uInt16 nItemId, const OUString& rHelpId )
1143 {
1144 MenuItemData* pData = pItemList->GetData( nItemId );
1145
1146 if ( pData )
1147 pData->aHelpId = rHelpId;
1148 }
1149
GetHelpId(sal_uInt16 nItemId) const1150 OUString Menu::GetHelpId( sal_uInt16 nItemId ) const
1151 {
1152 OUString aRet;
1153
1154 MenuItemData* pData = pItemList->GetData( nItemId );
1155
1156 if ( pData )
1157 {
1158 if ( !pData->aHelpId.isEmpty() )
1159 aRet = pData->aHelpId;
1160 else
1161 aRet = pData->aCommandStr;
1162 }
1163
1164 return aRet;
1165 }
1166
operator =(const Menu & rMenu)1167 Menu& Menu::operator=( const Menu& rMenu )
1168 {
1169 if(this == &rMenu)
1170 return *this;
1171
1172 // clean up
1173 Clear();
1174
1175 // copy items
1176 sal_uInt16 nCount = rMenu.GetItemCount();
1177 for ( sal_uInt16 i = 0; i < nCount; i++ )
1178 ImplCopyItem( this, rMenu, i, MENU_APPEND );
1179
1180 aActivateHdl = rMenu.aActivateHdl;
1181 aDeactivateHdl = rMenu.aDeactivateHdl;
1182 aSelectHdl = rMenu.aSelectHdl;
1183 aTitleText = rMenu.aTitleText;
1184 nTitleHeight = rMenu.nTitleHeight;
1185
1186 return *this;
1187 }
1188
1189 // Returns true if the item is completely hidden on the GUI and shouldn't
1190 // be possible to interact with
ImplCurrentlyHiddenOnGUI(sal_uInt16 nPos) const1191 bool Menu::ImplCurrentlyHiddenOnGUI(sal_uInt16 nPos) const
1192 {
1193 MenuItemData* pData = pItemList->GetDataFromPos(nPos);
1194 if (pData)
1195 {
1196 MenuItemData* pPreviousData = pItemList->GetDataFromPos( nPos - 1 );
1197 if (pPreviousData && pPreviousData->bHiddenOnGUI)
1198 {
1199 return true;
1200 }
1201 }
1202 return false;
1203 }
1204
ImplIsVisible(sal_uInt16 nPos) const1205 bool Menu::ImplIsVisible( sal_uInt16 nPos ) const
1206 {
1207 bool bVisible = true;
1208
1209 MenuItemData* pData = pItemList->GetDataFromPos( nPos );
1210 // check general visibility first
1211 if( pData && !pData->bVisible )
1212 bVisible = false;
1213
1214 if ( bVisible && pData && pData->eType == MenuItemType::SEPARATOR )
1215 {
1216 if( nPos == 0 ) // no separator should be shown at the very beginning
1217 bVisible = false;
1218 else
1219 {
1220 // always avoid adjacent separators
1221 size_t nCount = pItemList->size();
1222 size_t n;
1223 MenuItemData* pNextData = nullptr;
1224 // search next visible item
1225 for( n = nPos + 1; n < nCount; n++ )
1226 {
1227 pNextData = pItemList->GetDataFromPos( n );
1228 if( pNextData && pNextData->bVisible )
1229 {
1230 if( pNextData->eType == MenuItemType::SEPARATOR || ImplIsVisible(n) )
1231 break;
1232 }
1233 }
1234 if( n == nCount ) // no next visible item
1235 bVisible = false;
1236 // check for separator
1237 if( pNextData && pNextData->bVisible && pNextData->eType == MenuItemType::SEPARATOR )
1238 bVisible = false;
1239
1240 if( bVisible )
1241 {
1242 for( n = nPos; n > 0; n-- )
1243 {
1244 pNextData = pItemList->GetDataFromPos( n-1 );
1245 if( pNextData && pNextData->bVisible )
1246 {
1247 if( pNextData->eType != MenuItemType::SEPARATOR && ImplIsVisible(n-1) )
1248 break;
1249 }
1250 }
1251 if( n == 0 ) // no previous visible item
1252 bVisible = false;
1253 }
1254 }
1255 }
1256
1257 // not allowed for menubar, as I do not know
1258 // whether a menu-entry will disappear or will appear
1259 if (bVisible && !IsMenuBar() && (nMenuFlags & MenuFlags::HideDisabledEntries) &&
1260 !(nMenuFlags & MenuFlags::AlwaysShowDisabledEntries))
1261 {
1262 if( !pData ) // e.g. nPos == ITEMPOS_INVALID
1263 bVisible = false;
1264 else if ( pData->eType != MenuItemType::SEPARATOR ) // separators handled above
1265 {
1266 // tdf#86850 Always display clipboard functions
1267 if ( pData->aCommandStr == ".uno:Cut" || pData->aCommandStr == ".uno:Copy" || pData->aCommandStr == ".uno:Paste" ||
1268 pData->sIdent == ".uno:Cut" || pData->sIdent == ".uno:Copy" || pData->sIdent == ".uno:Paste" )
1269 bVisible = true;
1270 else
1271 // bVisible = pData->bEnabled && ( !pData->pSubMenu || pData->pSubMenu->HasValidEntries( true ) );
1272 bVisible = pData->bEnabled; // do not check submenus as they might be filled at Activate().
1273 }
1274 }
1275
1276 return bVisible;
1277 }
1278
IsItemPosVisible(sal_uInt16 nItemPos) const1279 bool Menu::IsItemPosVisible( sal_uInt16 nItemPos ) const
1280 {
1281 return IsMenuVisible() && ImplIsVisible( nItemPos );
1282 }
1283
IsMenuVisible() const1284 bool Menu::IsMenuVisible() const
1285 {
1286 return m_pWindow && m_pWindow->IsReallyVisible();
1287 }
1288
ImplIsSelectable(sal_uInt16 nPos) const1289 bool Menu::ImplIsSelectable( sal_uInt16 nPos ) const
1290 {
1291 bool bSelectable = true;
1292
1293 MenuItemData* pData = pItemList->GetDataFromPos( nPos );
1294 // check general visibility first
1295 if ( pData && ( pData->nBits & MenuItemBits::NOSELECT ) )
1296 bSelectable = false;
1297
1298 return bSelectable;
1299 }
1300
CreateAccessible()1301 rtl::Reference<comphelper::OAccessible> Menu::CreateAccessible()
1302 {
1303 rtl::Reference<OAccessibleMenuBaseComponent> xAccessible;
1304 if (IsMenuBar())
1305 xAccessible = new VCLXAccessibleMenuBar(this);
1306 else
1307 xAccessible = new VCLXAccessiblePopupMenu(this);
1308 xAccessible->SetStates();
1309 return xAccessible;
1310 }
1311
GetAccessible()1312 rtl::Reference<comphelper::OAccessible> Menu::GetAccessible()
1313 {
1314 // Since PopupMenu are sometimes shared by different instances of MenuBar, the mpAccessible member gets
1315 // overwritten and may contain a disposed object when the initial menubar gets set again. So use the
1316 // mpAccessible member only for sub menus.
1317 if (pStartedFrom && pStartedFrom != this)
1318 {
1319 for ( sal_uInt16 i = 0, nCount = pStartedFrom->GetItemCount(); i < nCount; ++i )
1320 {
1321 sal_uInt16 nItemId = pStartedFrom->GetItemId( i );
1322 if ( static_cast< Menu* >( pStartedFrom->GetPopupMenu( nItemId ) ) == this )
1323 {
1324 rtl::Reference<comphelper::OAccessible> pParent = pStartedFrom->GetAccessible();
1325 if (pParent.is())
1326 {
1327 css::uno::Reference<css::accessibility::XAccessible> xAcc = pParent->getAccessibleChild(i);
1328 if (!xAcc)
1329 return {};
1330
1331 rtl::Reference<comphelper::OAccessible> pAccessible = dynamic_cast<comphelper::OAccessible*>(xAcc.get());
1332 assert(pAccessible.is() && "Accessible is not an OAccessible");
1333 return pAccessible;
1334 }
1335 }
1336 }
1337 }
1338 else if (!mpAccessible.is())
1339 mpAccessible = CreateAccessible();
1340
1341 return mpAccessible;
1342 }
1343
SetAccessible(const rtl::Reference<comphelper::OAccessible> & rAccessible)1344 void Menu::SetAccessible(const rtl::Reference<comphelper::OAccessible>& rAccessible)
1345 {
1346 mpAccessible = rAccessible;
1347 }
1348
ImplGetNativeCheckAndRadioSize(vcl::RenderContext const & rRenderContext,tools::Long & rCheckHeight,tools::Long & rRadioHeight) const1349 Size Menu::ImplGetNativeCheckAndRadioSize(vcl::RenderContext const & rRenderContext, tools::Long& rCheckHeight, tools::Long& rRadioHeight ) const
1350 {
1351 tools::Long nCheckWidth = 0, nRadioWidth = 0;
1352 rCheckHeight = rRadioHeight = 0;
1353
1354 if (!IsMenuBar())
1355 {
1356 ImplControlValue aVal;
1357 tools::Rectangle aNativeBounds;
1358 tools::Rectangle aNativeContent;
1359
1360 tools::Rectangle aCtrlRegion(tools::Rectangle(Point(), Size(100, 15)));
1361 if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::MenuItemCheckMark))
1362 {
1363 if (rRenderContext.GetNativeControlRegion(ControlType::MenuPopup, ControlPart::MenuItemCheckMark,
1364 aCtrlRegion, ControlState::ENABLED, aVal,
1365 aNativeBounds, aNativeContent))
1366 {
1367 rCheckHeight = aNativeBounds.GetHeight() - 1;
1368 nCheckWidth = aNativeContent.GetWidth() - 1;
1369 }
1370 }
1371 if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::MenuItemRadioMark))
1372 {
1373 if (rRenderContext.GetNativeControlRegion(ControlType::MenuPopup, ControlPart::MenuItemRadioMark,
1374 aCtrlRegion, ControlState::ENABLED, aVal,
1375 aNativeBounds, aNativeContent))
1376 {
1377 rRadioHeight = aNativeBounds.GetHeight() - 1;
1378 nRadioWidth = aNativeContent.GetWidth() - 1;
1379 }
1380 }
1381 }
1382 return Size(std::max(nCheckWidth, nRadioWidth), std::max(rCheckHeight, rRadioHeight));
1383 }
1384
ImplGetNativeSubmenuArrowSize(vcl::RenderContext const & rRenderContext,Size & rArrowSize,tools::Long & rArrowSpacing)1385 bool Menu::ImplGetNativeSubmenuArrowSize(vcl::RenderContext const & rRenderContext, Size& rArrowSize, tools::Long& rArrowSpacing)
1386 {
1387 ImplControlValue aVal;
1388 tools::Rectangle aCtrlRegion(tools::Rectangle(Point(), Size(100, 15)));
1389 if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::SubmenuArrow))
1390 {
1391 tools::Rectangle aNativeContent;
1392 tools::Rectangle aNativeBounds;
1393 if (rRenderContext.GetNativeControlRegion(ControlType::MenuPopup, ControlPart::SubmenuArrow,
1394 aCtrlRegion, ControlState::ENABLED,
1395 aVal, aNativeBounds, aNativeContent))
1396 {
1397 Size aSize(aNativeContent.GetWidth(), aNativeContent.GetHeight());
1398 rArrowSize = aSize;
1399 rArrowSpacing = aNativeBounds.GetWidth() - aNativeContent.GetWidth();
1400 return true;
1401 }
1402 }
1403 return false;
1404 }
1405
ImplAddDel(ImplMenuDelData & rDel)1406 void Menu::ImplAddDel( ImplMenuDelData& rDel )
1407 {
1408 SAL_WARN_IF( rDel.mpMenu, "vcl", "Menu::ImplAddDel(): cannot add ImplMenuDelData twice !" );
1409 if( !rDel.mpMenu )
1410 {
1411 rDel.mpMenu = this;
1412 rDel.mpNext = mpFirstDel;
1413 mpFirstDel = &rDel;
1414 }
1415 }
1416
ImplRemoveDel(ImplMenuDelData & rDel)1417 void Menu::ImplRemoveDel( ImplMenuDelData& rDel )
1418 {
1419 rDel.mpMenu = nullptr;
1420 if ( mpFirstDel == &rDel )
1421 {
1422 mpFirstDel = rDel.mpNext;
1423 }
1424 else
1425 {
1426 ImplMenuDelData* pData = mpFirstDel;
1427 while ( pData && (pData->mpNext != &rDel) )
1428 pData = pData->mpNext;
1429
1430 SAL_WARN_IF( !pData, "vcl", "Menu::ImplRemoveDel(): ImplMenuDelData not registered !" );
1431 if( pData )
1432 pData->mpNext = rDel.mpNext;
1433 }
1434 }
1435
ImplCalcSize(vcl::Window * pWin)1436 Size Menu::ImplCalcSize( vcl::Window* pWin )
1437 {
1438 // | Check/Radio/Image| Text| Accel/Popup|
1439
1440 // for symbols: nFontHeight x nFontHeight
1441 tools::Long nFontHeight = pWin->GetTextHeight();
1442 tools::Long nExtra = nFontHeight/4;
1443
1444 tools::Long nMinMenuItemHeight = nFontHeight;
1445 tools::Long nCheckHeight = 0, nRadioHeight = 0;
1446 Size aMarkSize = ImplGetNativeCheckAndRadioSize(*pWin->GetOutDev(), nCheckHeight, nRadioHeight);
1447 if( aMarkSize.Height() > nMinMenuItemHeight )
1448 nMinMenuItemHeight = aMarkSize.Height();
1449
1450 tools::Long aMaxImgWidth = 0;
1451
1452 const StyleSettings& rSettings = pWin->GetSettings().GetStyleSettings();
1453 if ( rSettings.GetUseImagesInMenus() )
1454 {
1455 if ( 16 > nMinMenuItemHeight )
1456 nMinMenuItemHeight = 16;
1457 for ( size_t i = pItemList->size(); i; )
1458 {
1459 MenuItemData* pData = pItemList->GetDataFromPos( --i );
1460 if ( ImplIsVisible( i )
1461 && ( ( pData->eType == MenuItemType::IMAGE )
1462 || ( pData->eType == MenuItemType::STRINGIMAGE )
1463 )
1464 )
1465 {
1466 Size aImgSz = pData->aImage.GetSizePixel();
1467 if ( aImgSz.Width() > aMaxImgWidth )
1468 aMaxImgWidth = aImgSz.Width();
1469 if ( aImgSz.Height() > nMinMenuItemHeight )
1470 nMinMenuItemHeight = aImgSz.Height();
1471 break;
1472 }
1473 }
1474 }
1475
1476 Size aSz;
1477 tools::Long nMaxWidth = 0;
1478
1479 for ( size_t n = pItemList->size(); n; )
1480 {
1481 MenuItemData* pData = pItemList->GetDataFromPos( --n );
1482
1483 pData->aSz.setHeight( 0 );
1484 pData->aSz.setWidth( 0 );
1485
1486 if ( ImplIsVisible( n ) )
1487 {
1488 tools::Long nWidth = 0;
1489
1490 // Separator
1491 if (!IsMenuBar()&& (pData->eType == MenuItemType::SEPARATOR))
1492 {
1493 pData->aSz.setHeight( 4 );
1494 }
1495
1496 // Image:
1497 if (!IsMenuBar() && ((pData->eType == MenuItemType::IMAGE) || (pData->eType == MenuItemType::STRINGIMAGE)))
1498 {
1499 tools::Long aImgHeight = pData->aImage.GetSizePixel().Height();
1500
1501 aImgHeight += 4; // add a border for native marks
1502 if (aImgHeight > pData->aSz.Height())
1503 pData->aSz.setHeight(aImgHeight);
1504 }
1505
1506 // Check Buttons:
1507 if (!IsMenuBar() && pData->HasCheck())
1508 {
1509 // checks / images take the same place
1510 if( ( pData->eType != MenuItemType::IMAGE ) && ( pData->eType != MenuItemType::STRINGIMAGE ) )
1511 {
1512 nWidth += aMarkSize.Width() + nExtra * 2;
1513 if (aMarkSize.Height() > pData->aSz.Height())
1514 pData->aSz.setHeight(aMarkSize.Height());
1515 }
1516 }
1517
1518 // Text:
1519 if ( (pData->eType == MenuItemType::STRING) || (pData->eType == MenuItemType::STRINGIMAGE) )
1520 {
1521 const SalLayoutGlyphs* pGlyphs = pData->GetTextGlyphs(pWin->GetOutDev());
1522 tools::Long nTextWidth = pWin->GetOutDev()->GetCtrlTextWidth(pData->aText, pGlyphs);
1523 tools::Long nTextHeight = pWin->GetTextHeight() + EXTRAITEMHEIGHT;
1524
1525 if (IsMenuBar())
1526 {
1527 if ( nTextHeight > pData->aSz.Height() )
1528 pData->aSz.setHeight( nTextHeight );
1529
1530 pData->aSz.setWidth( nTextWidth + 4*nExtra );
1531 aSz.AdjustWidth(pData->aSz.Width() );
1532 }
1533 else
1534 pData->aSz.setHeight( std::max( std::max( nTextHeight, pData->aSz.Height() ), nMinMenuItemHeight ) );
1535
1536 nWidth += nTextWidth;
1537 }
1538
1539 // Accel
1540 if (!IsMenuBar()&& pData->aAccelKey.GetCode() && !officecfg::VCL::VCLSettings::Menu::SuppressAccelerators::get())
1541 {
1542 OUString aName = pData->aAccelKey.GetName();
1543 tools::Long nAccWidth = pWin->GetTextWidth( aName );
1544 nAccWidth += nExtra;
1545 nWidth += nAccWidth;
1546 }
1547
1548 // SubMenu?
1549 if (!IsMenuBar() && pData->pSubMenu)
1550 {
1551 if ( nFontHeight > nWidth )
1552 nWidth += nFontHeight;
1553
1554 pData->aSz.setHeight( std::max( std::max( nFontHeight, pData->aSz.Height() ), nMinMenuItemHeight ) );
1555 }
1556
1557 if (!IsMenuBar())
1558 aSz.AdjustHeight(pData->aSz.Height() );
1559
1560 if ( nWidth > nMaxWidth )
1561 nMaxWidth = nWidth;
1562
1563 }
1564 }
1565
1566 // Additional space for title
1567 nTitleHeight = 0;
1568 if (!IsMenuBar() && aTitleText.getLength() > 0) {
1569 // Set expected font
1570 auto popIt = pWin->GetOutDev()->ScopedPush(PushFlags::FONT);
1571 vcl::Font aFont = pWin->GetFont();
1572 aFont.SetWeight(WEIGHT_BOLD);
1573 pWin->SetFont(aFont);
1574
1575 // Compute text bounding box
1576 tools::Rectangle aTextBoundRect;
1577 pWin->GetOutDev()->GetTextBoundRect(aTextBoundRect, aTitleText);
1578
1579 // Vertically, one height of char + extra space for decoration
1580 nTitleHeight = aTextBoundRect.GetSize().Height() + 4 * SPACE_AROUND_TITLE ;
1581 aSz.AdjustHeight(nTitleHeight );
1582
1583 tools::Long nWidth = aTextBoundRect.GetSize().Width() + 4 * SPACE_AROUND_TITLE;
1584 if ( nWidth > nMaxWidth )
1585 nMaxWidth = nWidth;
1586 }
1587
1588 if (!IsMenuBar())
1589 {
1590 // popup menus should not be wider than half the screen
1591 // except on rather small screens
1592 // TODO: move GetScreenNumber from SystemWindow to Window ?
1593 // currently we rely on internal privileges
1594 unsigned int nDisplayScreen = pWin->ImplGetWindowImpl()->mpFrame->GetUnmirroredGeometry().screen();
1595 tools::Rectangle aDispRect( Application::GetScreenPosSizePixel( nDisplayScreen ) );
1596 tools::Long nScreenWidth = aDispRect.GetWidth() >= 800 ? aDispRect.GetWidth() : 800;
1597 if( nMaxWidth > nScreenWidth/2 )
1598 nMaxWidth = nScreenWidth/2;
1599
1600 sal_uInt16 gfxExtra = static_cast<sal_uInt16>(std::max( nExtra, tools::Long(7) )); // #107710# increase space between checkmarks/images/text
1601 nImgOrChkPos = static_cast<sal_uInt16>(nExtra);
1602 tools::Long nImgOrChkWidth = 0;
1603 if( aMarkSize.Height() > 0 ) // NWF case
1604 nImgOrChkWidth = aMarkSize.Height() + nExtra;
1605 else // non NWF case
1606 nImgOrChkWidth = nFontHeight/2 + gfxExtra;
1607 nImgOrChkWidth = std::max( nImgOrChkWidth, aMaxImgWidth + gfxExtra );
1608 nTextPos = static_cast<sal_uInt16>(nImgOrChkPos + nImgOrChkWidth);
1609 nTextPos = nTextPos + gfxExtra;
1610
1611 aSz.setWidth( nTextPos + nMaxWidth + nExtra );
1612 aSz.AdjustWidth(4*nExtra ); // a _little_ more ...
1613
1614 aSz.AdjustWidth(2*ImplGetSVData()->maNWFData.mnMenuFormatBorderX );
1615 aSz.AdjustHeight(2*ImplGetSVData()->maNWFData.mnMenuFormatBorderY );
1616 }
1617 else
1618 {
1619 nTextPos = static_cast<sal_uInt16>(2*nExtra);
1620 aSz.setHeight( nFontHeight+6 );
1621
1622 // get menubar height from native methods if supported
1623 if (m_pWindow->IsNativeControlSupported(ControlType::Menubar, ControlPart::Entire))
1624 {
1625 ImplControlValue aVal;
1626 tools::Rectangle aNativeBounds;
1627 tools::Rectangle aNativeContent;
1628 Point tmp( 0, 0 );
1629 tools::Rectangle aCtrlRegion( tmp, Size( 100, 15 ) );
1630 if (m_pWindow->GetNativeControlRegion(ControlType::Menubar,
1631 ControlPart::Entire,
1632 aCtrlRegion,
1633 ControlState::ENABLED,
1634 aVal,
1635 aNativeBounds,
1636 aNativeContent)
1637 )
1638 {
1639 int nNativeHeight = aNativeBounds.GetHeight();
1640 if( nNativeHeight > aSz.Height() )
1641 aSz.setHeight( nNativeHeight );
1642 }
1643 }
1644
1645 // account for the size of the close button, which actually is a toolbox
1646 // due to NWF this is variable
1647 tools::Long nCloseButtonHeight = static_cast<MenuBarWindow*>(m_pWindow.get())->MinCloseButtonSize().Height();
1648 if (aSz.Height() < nCloseButtonHeight)
1649 aSz.setHeight( nCloseButtonHeight );
1650 }
1651
1652 return aSz;
1653 }
1654
ImplPaintCheckBackground(vcl::RenderContext & rRenderContext,vcl::Window const & rWindow,const tools::Rectangle & i_rRect,bool i_bHighlight)1655 static void ImplPaintCheckBackground(vcl::RenderContext & rRenderContext, vcl::Window const & rWindow, const tools::Rectangle& i_rRect, bool i_bHighlight)
1656 {
1657 bool bNativeOk = false;
1658 if (rRenderContext.IsNativeControlSupported(ControlType::Toolbar, ControlPart::Button))
1659 {
1660 ImplControlValue aControlValue;
1661 aControlValue.setTristateVal(ButtonValue::On);
1662 tools::Rectangle r = i_rRect;
1663 r.AdjustBottom(1);
1664
1665 bNativeOk = rRenderContext.DrawNativeControl(ControlType::Toolbar, ControlPart::Button,
1666 r,
1667 ControlState::PRESSED | ControlState::ENABLED,
1668 aControlValue,
1669 OUString());
1670 }
1671
1672 if (!bNativeOk)
1673 {
1674 const StyleSettings& rSettings = rRenderContext.GetSettings().GetStyleSettings();
1675 Color aColor( i_bHighlight ? rSettings.GetMenuHighlightTextColor() : rSettings.GetHighlightColor() );
1676 RenderTools::DrawSelectionBackground(rRenderContext, rWindow, i_rRect, 0, i_bHighlight, true, false, nullptr, 2, &aColor);
1677 }
1678 }
1679
getShortenedString(const OUString & i_rLong,vcl::RenderContext const & rRenderContext,tools::Long i_nMaxWidth)1680 static OUString getShortenedString( const OUString& i_rLong, vcl::RenderContext const & rRenderContext, tools::Long i_nMaxWidth )
1681 {
1682 sal_Int32 nPos = -1;
1683 OUString aNonMnem(removeMnemonicFromString(i_rLong, nPos));
1684 aNonMnem = rRenderContext.GetEllipsisString( aNonMnem, i_nMaxWidth, DrawTextFlags::CenterEllipsis);
1685 // re-insert mnemonic
1686 if (nPos != -1)
1687 {
1688 if (nPos < aNonMnem.getLength() && i_rLong[nPos+1] == aNonMnem[nPos])
1689 aNonMnem = OUString::Concat(aNonMnem.subView(0, nPos)) + "~" + aNonMnem.subView(nPos);
1690 }
1691 return aNonMnem;
1692 }
1693
ImplPaintMenuTitle(vcl::RenderContext & rRenderContext,const tools::Rectangle & rRect) const1694 void Menu::ImplPaintMenuTitle(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect ) const
1695 {
1696 // Save previous graphical settings, set new one
1697 rRenderContext.Push(PushFlags::FONT | PushFlags::FILLCOLOR);
1698 Wallpaper aOldBackground = rRenderContext.GetBackground();
1699
1700 Color aBackgroundColor = rRenderContext.GetSettings().GetStyleSettings().GetMenuBarColor();
1701 rRenderContext.SetBackground(Wallpaper(aBackgroundColor));
1702 rRenderContext.SetFillColor(aBackgroundColor);
1703 vcl::Font aFont = rRenderContext.GetFont();
1704 aFont.SetWeight(WEIGHT_BOLD);
1705 rRenderContext.SetFont(aFont);
1706
1707 // Draw background rectangle
1708 tools::Rectangle aBgRect(rRect);
1709 int nOuterSpaceX = ImplGetSVData()->maNWFData.mnMenuFormatBorderX;
1710 aBgRect.Move(SPACE_AROUND_TITLE, SPACE_AROUND_TITLE);
1711 aBgRect.setWidth(aBgRect.getOpenWidth() - 2 * SPACE_AROUND_TITLE - 2 * nOuterSpaceX);
1712 aBgRect.setHeight(nTitleHeight - 2 * SPACE_AROUND_TITLE);
1713 rRenderContext.DrawRect(aBgRect);
1714
1715 // Draw the text centered
1716 Point aTextTopLeft(aBgRect.TopLeft());
1717 tools::Rectangle aTextBoundRect;
1718 rRenderContext.GetTextBoundRect( aTextBoundRect, aTitleText );
1719 aTextTopLeft.AdjustX((aBgRect.getOpenWidth() - aTextBoundRect.GetSize().Width()) / 2 );
1720 aTextTopLeft.AdjustY((aBgRect.GetHeight() - aTextBoundRect.GetSize().Height()) / 2
1721 - aTextBoundRect.Top() );
1722 rRenderContext.DrawText(aTextTopLeft, aTitleText, 0, aTitleText.getLength());
1723
1724 // Restore
1725 rRenderContext.Pop();
1726 rRenderContext.SetBackground(aOldBackground);
1727 }
1728
ImplPaint(vcl::RenderContext & rRenderContext,Size const & rSize,sal_uInt16 nBorder,tools::Long nStartY,MenuItemData const * pThisItemOnly,bool bHighlighted,bool bLayout,bool bRollover) const1729 void Menu::ImplPaint(vcl::RenderContext& rRenderContext, Size const & rSize,
1730 sal_uInt16 nBorder, tools::Long nStartY, MenuItemData const * pThisItemOnly,
1731 bool bHighlighted, bool bLayout, bool bRollover) const
1732 {
1733 // for symbols: nFontHeight x nFontHeight
1734 tools::Long nFontHeight = rRenderContext.GetTextHeight();
1735 tools::Long nExtra = nFontHeight / 4;
1736
1737 tools::Long nCheckHeight = 0, nRadioHeight = 0;
1738 ImplGetNativeCheckAndRadioSize(rRenderContext, nCheckHeight, nRadioHeight);
1739
1740 DecorationView aDecoView(&rRenderContext);
1741 const StyleSettings& rSettings = rRenderContext.GetSettings().GetStyleSettings();
1742
1743 Point aTopLeft, aTmpPos;
1744
1745 int nOuterSpaceX = 0;
1746 if (!IsMenuBar())
1747 {
1748 nOuterSpaceX = ImplGetSVData()->maNWFData.mnMenuFormatBorderX;
1749 aTopLeft.AdjustX(nOuterSpaceX );
1750 aTopLeft.AdjustY(ImplGetSVData()->maNWFData.mnMenuFormatBorderY );
1751 }
1752
1753 // for the computations, use size of the underlying window, not of RenderContext
1754 Size aOutSz(rSize);
1755
1756 size_t nCount = pItemList->size();
1757 if (bLayout)
1758 mpLayoutData->m_aVisibleItemBoundRects.clear();
1759
1760 // Paint title
1761 if (!pThisItemOnly && !IsMenuBar() && nTitleHeight > 0)
1762 ImplPaintMenuTitle(rRenderContext, tools::Rectangle(aTopLeft, aOutSz));
1763
1764 bool bHiddenItems = false; // are any items on the GUI hidden
1765
1766 for (size_t n = 0; n < nCount; n++)
1767 {
1768 MenuItemData* pData = pItemList->GetDataFromPos( n );
1769 if (ImplIsVisible(n) && (!pThisItemOnly || (pData == pThisItemOnly)))
1770 {
1771 if (pThisItemOnly)
1772 {
1773 if (IsMenuBar())
1774 {
1775 if (!ImplGetSVData()->maNWFData.mbRolloverMenubar)
1776 {
1777 if (bRollover)
1778 rRenderContext.SetTextColor(rSettings.GetMenuBarRolloverTextColor());
1779 else if (bHighlighted)
1780 rRenderContext.SetTextColor(rSettings.GetMenuBarHighlightTextColor());
1781 }
1782 else
1783 {
1784 if (bHighlighted)
1785 rRenderContext.SetTextColor(rSettings.GetMenuBarHighlightTextColor());
1786 else if (bRollover)
1787 rRenderContext.SetTextColor(rSettings.GetMenuBarRolloverTextColor());
1788 }
1789 if (!bRollover && !bHighlighted)
1790 rRenderContext.SetTextColor(rSettings.GetMenuBarTextColor());
1791 }
1792 else if (bHighlighted)
1793 rRenderContext.SetTextColor(rSettings.GetMenuHighlightTextColor());
1794 }
1795
1796 Point aPos(aTopLeft);
1797 aPos.AdjustY(nBorder );
1798 aPos.AdjustY(nStartY );
1799
1800 if (aPos.Y() >= 0)
1801 {
1802 tools::Long nTextOffsetY = (pData->aSz.Height() - nFontHeight) / 2;
1803 if (IsMenuBar())
1804 nTextOffsetY += (aOutSz.Height()-pData->aSz.Height()) / 2;
1805 DrawTextFlags nTextStyle = DrawTextFlags::NONE;
1806 DrawSymbolFlags nSymbolStyle = DrawSymbolFlags::NONE;
1807 DrawImageFlags nImageStyle = DrawImageFlags::NONE;
1808
1809 // submenus without items are not disabled when no items are
1810 // contained. The application itself should check for this!
1811 // Otherwise it could happen entries are disabled due to
1812 // asynchronous loading
1813 if (!pData->bEnabled || !m_pWindow->IsEnabled())
1814 {
1815 nTextStyle |= DrawTextFlags::Disable;
1816 nSymbolStyle |= DrawSymbolFlags::Disable;
1817 nImageStyle |= DrawImageFlags::Disable;
1818 }
1819
1820 // Separator
1821 if (!bLayout && !IsMenuBar() && (pData->eType == MenuItemType::SEPARATOR))
1822 {
1823 bool bNativeOk = false;
1824 if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::Separator))
1825 {
1826 ControlState nState = ControlState::NONE;
1827 if (pData->bEnabled && m_pWindow->IsEnabled())
1828 nState |= ControlState::ENABLED;
1829 if (bHighlighted)
1830 nState |= ControlState::SELECTED;
1831 Size aSz(pData->aSz);
1832 aSz.setWidth( aOutSz.Width() - 2*nOuterSpaceX );
1833 tools::Rectangle aItemRect(aPos, aSz);
1834 MenupopupValue aVal(nTextPos - GUTTERBORDER, aItemRect);
1835 bNativeOk = rRenderContext.DrawNativeControl(ControlType::MenuPopup, ControlPart::Separator,
1836 aItemRect, nState, aVal, OUString());
1837 }
1838 if (!bNativeOk)
1839 {
1840 aTmpPos.setY( aPos.Y() + ((pData->aSz.Height() - 2) / 2) );
1841 aTmpPos.setX( aPos.X() + 2 + nOuterSpaceX );
1842 rRenderContext.SetLineColor(rSettings.GetShadowColor());
1843 rRenderContext.DrawLine(aTmpPos, Point(aOutSz.Width() - 3 - 2 * nOuterSpaceX, aTmpPos.Y()));
1844 aTmpPos.AdjustY( 1 );
1845 rRenderContext.SetLineColor(rSettings.GetLightColor());
1846 rRenderContext.DrawLine(aTmpPos, Point(aOutSz.Width() - 3 - 2 * nOuterSpaceX, aTmpPos.Y()));
1847 rRenderContext.SetLineColor();
1848 }
1849 }
1850
1851 tools::Rectangle aOuterCheckRect(Point(aPos.X()+nImgOrChkPos, aPos.Y()),
1852 Size(pData->aSz.Height(), pData->aSz.Height()));
1853
1854 // CheckMark
1855 if (!bLayout && !IsMenuBar() && pData->HasCheck())
1856 {
1857 // draw selection transparent marker if checked
1858 // onto that either a checkmark or the item image
1859 // will be painted
1860 // however do not do this if native checks will be painted since
1861 // the selection color too often does not fit the theme's check and/or radio
1862
1863 if( (pData->eType != MenuItemType::IMAGE) && (pData->eType != MenuItemType::STRINGIMAGE))
1864 {
1865 if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup,
1866 (pData->nBits & MenuItemBits::RADIOCHECK)
1867 ? ControlPart::MenuItemCheckMark
1868 : ControlPart::MenuItemRadioMark))
1869 {
1870 ControlPart nPart = ((pData->nBits & MenuItemBits::RADIOCHECK)
1871 ? ControlPart::MenuItemRadioMark
1872 : ControlPart::MenuItemCheckMark);
1873
1874 ControlState nState = ControlState::NONE;
1875
1876 if (pData->bChecked)
1877 nState |= ControlState::PRESSED;
1878
1879 if (pData->bEnabled && m_pWindow->IsEnabled())
1880 nState |= ControlState::ENABLED;
1881
1882 if (bHighlighted)
1883 nState |= ControlState::SELECTED;
1884
1885 tools::Long nCtrlHeight = (pData->nBits & MenuItemBits::RADIOCHECK) ? nCheckHeight : nRadioHeight;
1886 aTmpPos.setX( aOuterCheckRect.Left() + (aOuterCheckRect.GetWidth() - nCtrlHeight) / 2 );
1887 aTmpPos.setY( aOuterCheckRect.Top() + (aOuterCheckRect.GetHeight() - nCtrlHeight) / 2 );
1888
1889 tools::Rectangle aCheckRect(aTmpPos, Size(nCtrlHeight, nCtrlHeight));
1890 Size aSz(pData->aSz);
1891 aSz.setWidth( aOutSz.Width() - 2 * nOuterSpaceX );
1892 tools::Rectangle aItemRect(aPos, aSz);
1893 MenupopupValue aVal(nTextPos - GUTTERBORDER, aItemRect);
1894 rRenderContext.DrawNativeControl(ControlType::MenuPopup, nPart, aCheckRect,
1895 nState, aVal, OUString());
1896 }
1897 else if (pData->bChecked) // by default do nothing for unchecked items
1898 {
1899 ImplPaintCheckBackground(rRenderContext, *m_pWindow, aOuterCheckRect, pThisItemOnly && bHighlighted);
1900
1901 SymbolType eSymbol;
1902 Size aSymbolSize;
1903 if (pData->nBits & MenuItemBits::RADIOCHECK)
1904 {
1905 eSymbol = SymbolType::RADIOCHECKMARK;
1906 aSymbolSize = Size(nFontHeight / 2, nFontHeight / 2);
1907 }
1908 else
1909 {
1910 eSymbol = SymbolType::CHECKMARK;
1911 aSymbolSize = Size((nFontHeight * 25) / 40, nFontHeight / 2);
1912 }
1913 aTmpPos.setX( aOuterCheckRect.Left() + (aOuterCheckRect.GetWidth() - aSymbolSize.Width()) / 2 );
1914 aTmpPos.setY( aOuterCheckRect.Top() + (aOuterCheckRect.GetHeight() - aSymbolSize.Height()) / 2 );
1915 tools::Rectangle aRect(aTmpPos, aSymbolSize);
1916 aDecoView.DrawSymbol(aRect, eSymbol, rRenderContext.GetTextColor(), nSymbolStyle);
1917 }
1918 }
1919 }
1920
1921 // Image:
1922 if (!bLayout && !IsMenuBar() && ((pData->eType == MenuItemType::IMAGE) || (pData->eType == MenuItemType::STRINGIMAGE)))
1923 {
1924 // Don't render an image for a check thing
1925 if (pData->bChecked)
1926 ImplPaintCheckBackground(rRenderContext, *m_pWindow, aOuterCheckRect, pThisItemOnly && bHighlighted);
1927
1928 Image aImage = pData->aImage;
1929
1930 aTmpPos = aOuterCheckRect.TopLeft();
1931 aTmpPos.AdjustX((aOuterCheckRect.GetWidth() - aImage.GetSizePixel().Width()) / 2 );
1932 aTmpPos.AdjustY((aOuterCheckRect.GetHeight() - aImage.GetSizePixel().Height()) / 2 );
1933 rRenderContext.DrawImage(aTmpPos, aImage, nImageStyle);
1934 }
1935
1936 // Text:
1937 if ((pData->eType == MenuItemType::STRING ) || (pData->eType == MenuItemType::STRINGIMAGE))
1938 {
1939 aTmpPos.setX( aPos.X() + nTextPos );
1940 aTmpPos.setY( aPos.Y() );
1941 aTmpPos.AdjustY(nTextOffsetY );
1942 DrawTextFlags nStyle = nTextStyle;
1943
1944 const Menu *pMenu = this;
1945 while (!pMenu->IsMenuBar() && pMenu->pStartedFrom)
1946 pMenu = pMenu->pStartedFrom;
1947 if (!pMenu->IsMenuBar() || !static_cast<MenuBarWindow*>(pMenu->m_pWindow.get())->GetMBWHideAccel())
1948 nStyle |= DrawTextFlags::Mnemonic;
1949
1950 if (pData->bIsTemporary)
1951 nStyle |= DrawTextFlags::Disable;
1952 std::vector< tools::Rectangle >* pVector = bLayout ? &mpLayoutData->m_aUnicodeBoundRects : nullptr;
1953 OUString* pDisplayText = bLayout ? &mpLayoutData->m_aDisplayText : nullptr;
1954 if (bLayout)
1955 {
1956 mpLayoutData->m_aLineIndices.push_back(mpLayoutData->m_aDisplayText.getLength());
1957 mpLayoutData->m_aLineItemIds.push_back(pData->nId);
1958 }
1959 // #i47946# with NWF painted menus the background is transparent
1960 // since DrawCtrlText can depend on the background (e.g. for
1961 // DrawTextFlags::Disable), temporarily set a background which
1962 // hopefully matches the NWF background since it is read
1963 // from the system style settings
1964 bool bSetTmpBackground = !rRenderContext.IsBackground()
1965 && rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::Entire);
1966 if (bSetTmpBackground)
1967 {
1968 Color aBg = IsMenuBar() ? rRenderContext.GetSettings().GetStyleSettings().GetMenuBarColor()
1969 : rRenderContext.GetSettings().GetStyleSettings().GetMenuColor();
1970 rRenderContext.SetBackground(Wallpaper(aBg));
1971 }
1972 // how much space is there for the text?
1973 tools::Long nMaxItemTextWidth = aOutSz.Width() - aTmpPos.X() - nExtra - nOuterSpaceX;
1974 if (!IsMenuBar() && pData->aAccelKey.GetCode() && !officecfg::VCL::VCLSettings::Menu::SuppressAccelerators::get())
1975 {
1976 OUString aAccText = pData->aAccelKey.GetName();
1977 nMaxItemTextWidth -= rRenderContext.GetTextWidth(aAccText) + 3 * nExtra;
1978 }
1979 if (!IsMenuBar() && pData->pSubMenu)
1980 {
1981 nMaxItemTextWidth -= nFontHeight - nExtra;
1982 }
1983
1984 OUString aItemText(pData->aText);
1985 pData->bHiddenOnGUI = false;
1986
1987 if (IsMenuBar()) // In case of menubar if we are out of bounds we shouldn't paint the item
1988 {
1989 if (nMaxItemTextWidth < rRenderContext.GetTextWidth(aItemText))
1990 {
1991 aItemText = "";
1992 pData->bHiddenOnGUI = true;
1993 bHiddenItems = true;
1994 }
1995 }
1996 else
1997 {
1998 aItemText = getShortenedString(aItemText, rRenderContext, nMaxItemTextWidth);
1999 pData->bHiddenOnGUI = false;
2000 }
2001
2002 const SalLayoutGlyphs* pGlyphs = pData->GetTextGlyphs(&rRenderContext);
2003 if (aItemText != pData->aText)
2004 // Can't use pre-computed glyphs, item text was
2005 // changed.
2006 pGlyphs = nullptr;
2007 rRenderContext.DrawCtrlText(aTmpPos, aItemText, 0, aItemText.getLength(),
2008 nStyle, pVector, pDisplayText, pGlyphs);
2009 if (bSetTmpBackground)
2010 rRenderContext.SetBackground();
2011 }
2012
2013 // Accel
2014 if (!bLayout && !IsMenuBar() && pData->aAccelKey.GetCode() && !officecfg::VCL::VCLSettings::Menu::SuppressAccelerators::get())
2015 {
2016 OUString aAccText = pData->aAccelKey.GetName();
2017 aTmpPos.setX( aOutSz.Width() - rRenderContext.GetTextWidth(aAccText) );
2018 aTmpPos.AdjustX( -(4 * nExtra) );
2019
2020 aTmpPos.AdjustX( -nOuterSpaceX );
2021 aTmpPos.setY( aPos.Y() );
2022 aTmpPos.AdjustY(nTextOffsetY );
2023 rRenderContext.DrawCtrlText(aTmpPos, aAccText, 0, aAccText.getLength(), nTextStyle);
2024 }
2025
2026 // SubMenu?
2027 if (!bLayout && !IsMenuBar() && pData->pSubMenu)
2028 {
2029 bool bNativeOk = false;
2030 if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::SubmenuArrow))
2031 {
2032 ControlState nState = ControlState::NONE;
2033 Size aTmpSz(0, 0);
2034 tools::Long aSpacing = 0;
2035
2036 if (!ImplGetNativeSubmenuArrowSize(rRenderContext, aTmpSz, aSpacing))
2037 {
2038 aTmpSz = Size(nFontHeight, nFontHeight);
2039 aSpacing = nOuterSpaceX;
2040 }
2041
2042 if (pData->bEnabled && m_pWindow->IsEnabled())
2043 nState |= ControlState::ENABLED;
2044 if (bHighlighted)
2045 nState |= ControlState::SELECTED;
2046
2047 aTmpPos.setX( aOutSz.Width() - aTmpSz.Width() - aSpacing - nOuterSpaceX );
2048 aTmpPos.setY( aPos.Y() + ( pData->aSz.Height() - aTmpSz.Height() ) / 2 );
2049 aTmpPos.AdjustY(nExtra / 2 );
2050
2051 tools::Rectangle aItemRect(aTmpPos, aTmpSz);
2052 MenupopupValue aVal(nTextPos - GUTTERBORDER, aItemRect);
2053 bNativeOk = rRenderContext.DrawNativeControl(ControlType::MenuPopup, ControlPart::SubmenuArrow,
2054 aItemRect, nState, aVal, OUString());
2055 }
2056 if (!bNativeOk)
2057 {
2058 aTmpPos.setX( aOutSz.Width() - nFontHeight + nExtra - nOuterSpaceX );
2059 aTmpPos.setY( aPos.Y() );
2060 aTmpPos.AdjustY(nExtra/2 );
2061 aTmpPos.AdjustY((pData->aSz.Height() / 2) - (nFontHeight / 4) );
2062 if (pData->nBits & MenuItemBits::POPUPSELECT)
2063 {
2064 rRenderContext.SetTextColor(rSettings.GetMenuTextColor());
2065 Point aTmpPos2(aPos);
2066 aTmpPos2.setX( aOutSz.Width() - nFontHeight - nFontHeight/4 );
2067 aDecoView.DrawFrame(tools::Rectangle(aTmpPos2, Size(nFontHeight + nFontHeight / 4,
2068 pData->aSz.Height())),
2069 DrawFrameStyle::Group);
2070 }
2071 aDecoView.DrawSymbol(tools::Rectangle(aTmpPos, Size(nFontHeight / 2, nFontHeight / 2)),
2072 SymbolType::SPIN_RIGHT, rRenderContext.GetTextColor(), nSymbolStyle);
2073 }
2074 }
2075
2076 if (pThisItemOnly && bHighlighted)
2077 {
2078 // This restores the normal menu or menu bar text
2079 // color for when it is no longer highlighted.
2080 if (IsMenuBar())
2081 rRenderContext.SetTextColor(rSettings.GetMenuBarTextColor());
2082 else
2083 rRenderContext.SetTextColor(rSettings.GetMenuTextColor());
2084 }
2085 }
2086 if( bLayout )
2087 {
2088 if (!IsMenuBar())
2089 mpLayoutData->m_aVisibleItemBoundRects[ n ] = tools::Rectangle(aTopLeft, Size(aOutSz.Width(), pData->aSz.Height()));
2090 else
2091 mpLayoutData->m_aVisibleItemBoundRects[ n ] = tools::Rectangle(aTopLeft, pData->aSz);
2092 }
2093 }
2094
2095 if (!IsMenuBar())
2096 aTopLeft.AdjustY(pData->aSz.Height() );
2097 else
2098 aTopLeft.AdjustX(pData->aSz.Width() );
2099 }
2100
2101 // draw "more" (">>") indicator if some items have been hidden as they go out of visible area
2102 if (bHiddenItems)
2103 {
2104 sal_Int32 nSize = nFontHeight;
2105 tools::Rectangle aRectangle(Point(aOutSz.Width() - nSize, (aOutSz.Height() / 2) - (nSize / 2)), Size(nSize, nSize));
2106 lclDrawMoreIndicator(rRenderContext, aRectangle);
2107 }
2108 }
2109
ImplGetStartMenu()2110 Menu* Menu::ImplGetStartMenu()
2111 {
2112 Menu* pStart = this;
2113 while ( pStart && pStart->pStartedFrom && ( pStart->pStartedFrom != pStart ) )
2114 pStart = pStart->pStartedFrom;
2115 return pStart;
2116 }
2117
ImplCallHighlight(sal_uInt16 nItem)2118 void Menu::ImplCallHighlight(sal_uInt16 nItem)
2119 {
2120 ImplMenuDelData aDelData( this );
2121
2122 nSelectedId = 0;
2123 sSelectedIdent.clear();
2124 MenuItemData* pData = pItemList->GetDataFromPos(nItem);
2125 if (pData)
2126 {
2127 nSelectedId = pData->nId;
2128 sSelectedIdent = pData->sIdent;
2129 }
2130 ImplCallEventListeners( VclEventId::MenuHighlight, GetItemPos( GetCurItemId() ) );
2131
2132 if( !aDelData.isDeleted() )
2133 {
2134 nSelectedId = 0;
2135 sSelectedIdent.clear();
2136 }
2137 }
2138
IMPL_LINK_NOARG(Menu,ImplCallSelect,void *,void)2139 IMPL_LINK_NOARG(Menu, ImplCallSelect, void*, void)
2140 {
2141 nEventId = nullptr;
2142 Select();
2143 }
2144
ImplFindSelectMenu()2145 Menu* Menu::ImplFindSelectMenu()
2146 {
2147 Menu* pSelMenu = nEventId ? this : nullptr;
2148
2149 for ( size_t n = GetItemList()->size(); n && !pSelMenu; )
2150 {
2151 MenuItemData* pData = GetItemList()->GetDataFromPos( --n );
2152
2153 if ( pData->pSubMenu )
2154 pSelMenu = pData->pSubMenu->ImplFindSelectMenu();
2155 }
2156
2157 return pSelMenu;
2158 }
2159
ImplFindMenu(sal_uInt16 nItemId)2160 Menu* Menu::ImplFindMenu( sal_uInt16 nItemId )
2161 {
2162 Menu* pSelMenu = nullptr;
2163
2164 for ( size_t n = GetItemList()->size(); n && !pSelMenu; )
2165 {
2166 MenuItemData* pData = GetItemList()->GetDataFromPos( --n );
2167
2168 if( pData->nId == nItemId )
2169 pSelMenu = this;
2170 else if ( pData->pSubMenu )
2171 pSelMenu = pData->pSubMenu->ImplFindMenu( nItemId );
2172 }
2173
2174 return pSelMenu;
2175 }
2176
RemoveDisabledEntries(bool bRemoveEmptyPopups)2177 void Menu::RemoveDisabledEntries( bool bRemoveEmptyPopups )
2178 {
2179 sal_uInt16 n = 0;
2180 while (n < GetItemCount())
2181 {
2182 bool bRemove = false;
2183 MenuItemData* pItem = pItemList->GetDataFromPos( n );
2184 if ( pItem->eType == MenuItemType::SEPARATOR )
2185 {
2186 if ( !n || ( GetItemType( n-1 ) == MenuItemType::SEPARATOR ) )
2187 bRemove = true;
2188 }
2189 else
2190 bRemove = !pItem->bEnabled;
2191
2192 if ( pItem->pSubMenu )
2193 {
2194 pItem->pSubMenu->RemoveDisabledEntries();
2195 if ( bRemoveEmptyPopups && !pItem->pSubMenu->GetItemCount() )
2196 bRemove = true;
2197 }
2198
2199 if (bRemove)
2200 RemoveItem(n);
2201 else
2202 ++n;
2203 }
2204
2205 if ( GetItemCount() )
2206 {
2207 sal_uInt16 nLast = GetItemCount() - 1;
2208 MenuItemData* pItem = pItemList->GetDataFromPos( nLast );
2209 if ( pItem->eType == MenuItemType::SEPARATOR )
2210 RemoveItem( nLast );
2211 }
2212 mpLayoutData.reset();
2213 }
2214
UpdateNativeMenu()2215 void Menu::UpdateNativeMenu()
2216 {
2217 if ( ImplGetSalMenu() )
2218 ImplGetSalMenu()->Update();
2219 }
2220
MenuBarKeyInput(const KeyEvent &)2221 void Menu::MenuBarKeyInput(const KeyEvent&)
2222 {
2223 }
2224
ImplKillLayoutData() const2225 void Menu::ImplKillLayoutData() const
2226 {
2227 mpLayoutData.reset();
2228 }
2229
ImplFillLayoutData() const2230 void Menu::ImplFillLayoutData() const
2231 {
2232 if (!(m_pWindow && m_pWindow->IsReallyVisible()))
2233 return;
2234
2235 mpLayoutData.reset(new MenuLayoutData);
2236 if (IsMenuBar())
2237 {
2238 ImplPaint(*m_pWindow->GetOutDev(), m_pWindow->GetOutputSizePixel(), 0, 0, nullptr, false, true); // FIXME
2239 }
2240 else
2241 {
2242 MenuFloatingWindow* pFloat = static_cast<MenuFloatingWindow*>(m_pWindow.get());
2243 ImplPaint(*m_pWindow->GetOutDev(), m_pWindow->GetOutputSizePixel(), pFloat->nScrollerHeight, pFloat->ImplGetStartY(),
2244 nullptr, false, true); //FIXME
2245 }
2246 }
2247
GetCharacterBounds(sal_uInt16 nItemID,tools::Long nIndex) const2248 tools::Rectangle Menu::GetCharacterBounds( sal_uInt16 nItemID, tools::Long nIndex ) const
2249 {
2250 tools::Long nItemIndex = -1;
2251 if( ! mpLayoutData )
2252 ImplFillLayoutData();
2253 if( mpLayoutData )
2254 {
2255 auto it = std::find(mpLayoutData->m_aLineItemIds.begin(), mpLayoutData->m_aLineItemIds.end(), nItemID);
2256 if (it != mpLayoutData->m_aLineItemIds.end())
2257 {
2258 nItemIndex = mpLayoutData->m_aLineIndices[std::distance(mpLayoutData->m_aLineItemIds.begin(), it)];
2259 }
2260 }
2261 return (mpLayoutData && nItemIndex != -1) ? mpLayoutData->GetCharacterBounds( nItemIndex+nIndex ) : tools::Rectangle();
2262 }
2263
GetIndexForPoint(const Point & rPoint,sal_uInt16 & rItemID) const2264 tools::Long Menu::GetIndexForPoint( const Point& rPoint, sal_uInt16& rItemID ) const
2265 {
2266 rItemID = 0;
2267 if( ! mpLayoutData )
2268 ImplFillLayoutData();
2269 if( mpLayoutData )
2270 {
2271 tools::Long nIndex = mpLayoutData->GetIndexForPoint( rPoint );
2272 for( size_t i = 0; i < mpLayoutData->m_aLineIndices.size(); i++ )
2273 {
2274 if( mpLayoutData->m_aLineIndices[i] <= nIndex &&
2275 (i == mpLayoutData->m_aLineIndices.size()-1 || mpLayoutData->m_aLineIndices[i+1] > nIndex) )
2276 {
2277 rItemID = mpLayoutData->m_aLineItemIds[i];
2278 // return index relative to item
2279 return nIndex - mpLayoutData->m_aLineIndices[i];
2280 }
2281 }
2282 }
2283 return -1;
2284 }
2285
GetBoundingRectangle(sal_uInt16 nPos) const2286 tools::Rectangle Menu::GetBoundingRectangle( sal_uInt16 nPos ) const
2287 {
2288 tools::Rectangle aRet;
2289
2290 if (!mpLayoutData )
2291 ImplFillLayoutData();
2292 if (mpLayoutData)
2293 {
2294 std::map< sal_uInt16, tools::Rectangle >::const_iterator it = mpLayoutData->m_aVisibleItemBoundRects.find( nPos );
2295 if( it != mpLayoutData->m_aVisibleItemBoundRects.end() )
2296 aRet = it->second;
2297 }
2298 return aRet;
2299 }
2300
SetAccessibleName(sal_uInt16 nItemId,const OUString & rStr)2301 void Menu::SetAccessibleName( sal_uInt16 nItemId, const OUString& rStr )
2302 {
2303 size_t nPos;
2304 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
2305
2306 if (pData && !rStr.equals(pData->aAccessibleName))
2307 {
2308 pData->aAccessibleName = rStr;
2309 ImplCallEventListeners(VclEventId::MenuAccessibleNameChanged, nPos);
2310 }
2311 }
2312
GetAccessibleName(sal_uInt16 nItemId) const2313 OUString Menu::GetAccessibleName( sal_uInt16 nItemId ) const
2314 {
2315 MenuItemData* pData = pItemList->GetData( nItemId );
2316
2317 if ( pData )
2318 return pData->aAccessibleName;
2319
2320 return OUString();
2321 }
2322
SetAccessibleDescription(sal_uInt16 nItemId,const OUString & rStr)2323 void Menu::SetAccessibleDescription( sal_uInt16 nItemId, const OUString& rStr )
2324 {
2325 MenuItemData* pData = pItemList->GetData( nItemId );
2326
2327 if ( pData )
2328 pData->aAccessibleDescription = rStr;
2329 }
2330
GetAccessibleDescription(sal_uInt16 nItemId) const2331 OUString Menu::GetAccessibleDescription( sal_uInt16 nItemId ) const
2332 {
2333 MenuItemData* pData = pItemList->GetData( nItemId );
2334
2335 if (pData && !pData->aAccessibleDescription.isEmpty())
2336 return pData->aAccessibleDescription;
2337
2338 return GetHelpText(nItemId);
2339 }
2340
GetSystemMenuData(SystemMenuData & rData) const2341 void Menu::GetSystemMenuData(SystemMenuData& rData) const
2342 {
2343 if (ImplGetSalMenu())
2344 {
2345 ImplGetSalMenu()->GetSystemMenuData(rData);
2346 }
2347 }
2348
IsHighlighted(sal_uInt16 nItemPos) const2349 bool Menu::IsHighlighted( sal_uInt16 nItemPos ) const
2350 {
2351 bool bRet = false;
2352
2353 if (m_pWindow)
2354 {
2355 if (IsMenuBar())
2356 bRet = ( nItemPos == static_cast< MenuBarWindow * > (m_pWindow.get())->GetHighlightedItem() );
2357 else
2358 bRet = ( nItemPos == static_cast< MenuFloatingWindow * > (m_pWindow.get())->GetHighlightedItem() );
2359 }
2360
2361 return bRet;
2362 }
2363
HighlightItem(sal_uInt16 nItemPos)2364 void Menu::HighlightItem( sal_uInt16 nItemPos )
2365 {
2366 if ( !m_pWindow )
2367 return;
2368
2369 if (IsMenuBar())
2370 {
2371 MenuBarWindow* pMenuWin = static_cast< MenuBarWindow* >( m_pWindow.get() );
2372 pMenuWin->SetAutoPopup( false );
2373 pMenuWin->ChangeHighlightItem( nItemPos, false );
2374 }
2375 else
2376 {
2377 static_cast< MenuFloatingWindow* >( m_pWindow.get() )->ChangeHighlightItem( nItemPos, false );
2378 }
2379 }
2380
DumpAsPropertyTree(tools::JsonWriter & rJsonWriter) const2381 void Menu::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter) const
2382 {
2383 rJsonWriter.put("id", "__MENU__"); // we have single instance of menu at the time per session
2384 rJsonWriter.put("type", "menu");
2385 rJsonWriter.put("count", GetItemCount());
2386 {
2387 auto aEntries = rJsonWriter.startArray("entries");
2388 for (size_t i = 0; i < GetItemCount(); i++)
2389 {
2390 auto aEntry = rJsonWriter.startStruct();
2391 sal_uInt16 nId = GetItemId(i);
2392 rJsonWriter.put("row", GetItemIdent(nId));
2393 {
2394 auto aColumns = rJsonWriter.startArray("columns");
2395 auto aColumn = rJsonWriter.startStruct();
2396 rJsonWriter.put("text", GetItemText(nId));
2397 }
2398 }
2399 }
2400 }
2401
getMenuBarWindow()2402 MenuBarWindow* MenuBar::getMenuBarWindow()
2403 {
2404 // so far just a dynamic_cast, hopefully to be turned into something saner
2405 // at some stage
2406 MenuBarWindow *pWin = dynamic_cast<MenuBarWindow*>(m_pWindow.get());
2407 //either there is no window (fdo#87663) or it is a MenuBarWindow
2408 assert(!m_pWindow || pWin);
2409 return pWin;
2410 }
2411
MenuBar()2412 MenuBar::MenuBar()
2413 : mbCloseBtnVisible(false),
2414 mbFloatBtnVisible(false),
2415 mbHideBtnVisible(false),
2416 mbDisplayable(true)
2417 {
2418 mpSalMenu = ImplGetSVData()->mpDefInst->CreateMenu(true, this);
2419 }
2420
MenuBar(const MenuBar & rMenu)2421 MenuBar::MenuBar( const MenuBar& rMenu )
2422 : mbCloseBtnVisible(false),
2423 mbFloatBtnVisible(false),
2424 mbHideBtnVisible(false),
2425 mbDisplayable(true)
2426 {
2427 mpSalMenu = ImplGetSVData()->mpDefInst->CreateMenu(true, this);
2428 *this = rMenu;
2429 }
2430
~MenuBar()2431 MenuBar::~MenuBar()
2432 {
2433 disposeOnce();
2434 }
2435
dispose()2436 void MenuBar::dispose()
2437 {
2438 ImplDestroy(true);
2439 Menu::dispose();
2440 }
2441
ClosePopup(PopupMenu * pPopupMenu)2442 void MenuBar::ClosePopup(PopupMenu *pPopupMenu)
2443 {
2444 MenuBarWindow* pMenuWin = getMenuBarWindow();
2445 if (!pMenuWin)
2446 return;
2447 pMenuWin->PopupClosed(pPopupMenu);
2448 }
2449
MenuBarKeyInput(const KeyEvent & rEvent)2450 void MenuBar::MenuBarKeyInput(const KeyEvent& rEvent)
2451 {
2452 m_pWindow->KeyInput(rEvent);
2453 }
2454
ShowCloseButton(bool bShow)2455 void MenuBar::ShowCloseButton(bool bShow)
2456 {
2457 ShowButtons( bShow, mbFloatBtnVisible, mbHideBtnVisible );
2458 }
2459
ShowButtons(bool bClose,bool bFloat,bool bHide)2460 void MenuBar::ShowButtons( bool bClose, bool bFloat, bool bHide )
2461 {
2462 if ((bClose != mbCloseBtnVisible) ||
2463 (bFloat != mbFloatBtnVisible) ||
2464 (bHide != mbHideBtnVisible))
2465 {
2466 mbCloseBtnVisible = bClose;
2467 mbFloatBtnVisible = bFloat;
2468 mbHideBtnVisible = bHide;
2469 MenuBarWindow* pMenuWin = getMenuBarWindow();
2470 if (pMenuWin)
2471 pMenuWin->ShowButtons(bClose, bFloat, bHide);
2472 }
2473 }
2474
LayoutChanged()2475 void MenuBar::LayoutChanged()
2476 {
2477 MenuBarWindow* pMenuWin = getMenuBarWindow();
2478 if (pMenuWin)
2479 pMenuWin->LayoutChanged();
2480 }
2481
SetDisplayable(bool bDisplayable)2482 void MenuBar::SetDisplayable( bool bDisplayable )
2483 {
2484 if( bDisplayable != mbDisplayable )
2485 {
2486 if ( ImplGetSalMenu() )
2487 ImplGetSalMenu()->ShowMenuBar( bDisplayable );
2488
2489 mbDisplayable = bDisplayable;
2490 LayoutChanged();
2491 }
2492 }
2493
ImplCreate(vcl::Window * pParent,MenuBarWindow * pWindow)2494 VclPtr<MenuBarWindow> MenuBar::ImplCreate(vcl::Window* pParent, MenuBarWindow* pWindow)
2495 {
2496 VclPtr<MenuBarWindow> pMenuBarWindow = pWindow;
2497 if (!pMenuBarWindow)
2498 {
2499 pMenuBarWindow = VclPtr<MenuBarWindow>::Create(pParent);
2500 }
2501
2502 pStartedFrom = nullptr;
2503 m_pWindow = pMenuBarWindow;
2504 pMenuBarWindow->SetMenu(this);
2505 tools::Long nHeight = ImplCalcSize(pMenuBarWindow).Height();
2506
2507 // depending on the native implementation or the displayable flag
2508 // the menubar windows is suppressed (ie, height=0)
2509 if (!IsDisplayable() || (ImplGetSalMenu() && ImplGetSalMenu()->VisibleMenuBar()))
2510 {
2511 nHeight = 0;
2512 }
2513
2514 pMenuBarWindow->SetHeight(nHeight);
2515 return pMenuBarWindow;
2516 }
2517
ImplDestroy(bool bDelete)2518 void MenuBar::ImplDestroy(bool bDelete)
2519 {
2520 MenuBarWindow* pMenuWin = getMenuBarWindow();
2521 if (pMenuWin && bDelete)
2522 {
2523 pMenuWin->KillActivePopup();
2524 pMenuWin->disposeOnce();
2525 }
2526 m_pWindow = nullptr;
2527 if (mpSalMenu)
2528 {
2529 mpSalMenu->ShowMenuBar(false);
2530 }
2531 }
2532
ImplHandleKeyEvent(const KeyEvent & rKEvent)2533 bool MenuBar::ImplHandleKeyEvent( const KeyEvent& rKEvent )
2534 {
2535 // No keyboard processing when our menubar is invisible
2536 if (!IsDisplayable())
2537 return false;
2538
2539 // No keyboard processing when system handles the menu.
2540 SalMenu *pNativeMenu = ImplGetSalMenu();
2541 if (pNativeMenu && pNativeMenu->VisibleMenuBar())
2542 {
2543 // Except when the event is the F6 cycle pane event and we can put our
2544 // focus into it (i.e. the gtk3 menubar case but not the mac/unity case
2545 // where it's not part of the application window)
2546 if (!TaskPaneList::IsCycleKey(rKEvent.GetKeyCode()))
2547 return false;
2548 if (!pNativeMenu->CanGetFocus())
2549 return false;
2550 }
2551
2552 bool bDone = false;
2553 // check for enabled, if this method is called from another window...
2554 vcl::Window* pWin = GetWindow();
2555 if (pWin && pWin->IsEnabled() && pWin->IsInputEnabled() && !pWin->IsInModalMode())
2556 {
2557 MenuBarWindow* pMenuWin = getMenuBarWindow();
2558 bDone = pMenuWin && pMenuWin->HandleKeyEvent(rKEvent, false/*bFromMenu*/);
2559 }
2560 return bDone;
2561 }
2562
ImplHandleCmdEvent(const CommandEvent & rCEvent)2563 bool MenuBar::ImplHandleCmdEvent( const CommandEvent& rCEvent )
2564 {
2565 // No keyboard processing when system handles the menu or our menubar is invisible
2566 if( !IsDisplayable() ||
2567 ( ImplGetSalMenu() && ImplGetSalMenu()->VisibleMenuBar() ) )
2568 return false;
2569
2570 // check for enabled, if this method is called from another window...
2571 MenuBarWindow* pWin = static_cast<MenuBarWindow*>(GetWindow());
2572 if ( pWin && pWin->IsEnabled() && pWin->IsInputEnabled() && ! pWin->IsInModalMode() )
2573 {
2574 if (rCEvent.GetCommand() == CommandEventId::ModKeyChange && ImplGetSVData()->maNWFData.mbAutoAccel)
2575 {
2576 const CommandModKeyData* pCData = rCEvent.GetModKeyData ();
2577 if (pWin->m_nHighlightedItem == ITEMPOS_INVALID)
2578 {
2579 if (pCData && pCData->IsMod2() && pCData->IsDown())
2580 pWin->SetMBWHideAccel(false);
2581 pWin->Invalidate(InvalidateFlags::Update);
2582 }
2583 return true;
2584 }
2585 }
2586 return false;
2587 }
2588
SelectItem(sal_uInt16 nId)2589 void MenuBar::SelectItem(sal_uInt16 nId)
2590 {
2591 if (!m_pWindow)
2592 return;
2593
2594 m_pWindow->GrabFocus();
2595 nId = GetItemPos( nId );
2596
2597 MenuBarWindow* pMenuWin = getMenuBarWindow();
2598 if (pMenuWin)
2599 {
2600 // #99705# popup the selected menu
2601 pMenuWin->SetAutoPopup( true );
2602 if (ITEMPOS_INVALID != pMenuWin->GetHighlightedItem())
2603 {
2604 pMenuWin->KillActivePopup();
2605 pMenuWin->ChangeHighlightItem( ITEMPOS_INVALID, false );
2606 }
2607 if (nId != ITEMPOS_INVALID)
2608 pMenuWin->ChangeHighlightItem( nId, false );
2609 }
2610 }
2611
2612 // handler for native menu selection and command events
HandleMenuActivateEvent(Menu * pMenu)2613 bool Menu::HandleMenuActivateEvent(Menu* pMenu)
2614 {
2615 if( pMenu )
2616 {
2617 ImplMenuDelData aDelData( this );
2618
2619 pMenu->pStartedFrom = this;
2620 pMenu->bInCallback = true;
2621 pMenu->Activate();
2622
2623 if( !aDelData.isDeleted() )
2624 pMenu->bInCallback = false;
2625 }
2626 return true;
2627 }
2628
HandleMenuDeActivateEvent(Menu * pMenu)2629 bool Menu::HandleMenuDeActivateEvent(Menu* pMenu)
2630 {
2631 if( pMenu )
2632 {
2633 ImplMenuDelData aDelData( this );
2634
2635 pMenu->pStartedFrom = this;
2636 pMenu->bInCallback = true;
2637 pMenu->Deactivate();
2638 if( !aDelData.isDeleted() )
2639 pMenu->bInCallback = false;
2640 }
2641 return true;
2642 }
2643
HandleMenuHighlightEvent(Menu * pMenu,sal_uInt16 nHighlightEventId)2644 bool MenuBar::HandleMenuHighlightEvent(Menu* pMenu, sal_uInt16 nHighlightEventId)
2645 {
2646 if( !pMenu )
2647 pMenu = ImplFindMenu(nHighlightEventId);
2648 if( pMenu )
2649 {
2650 ImplMenuDelData aDelData( pMenu );
2651
2652 if( mnHighlightedItemPos != ITEMPOS_INVALID )
2653 pMenu->ImplCallEventListeners( VclEventId::MenuDehighlight, mnHighlightedItemPos );
2654
2655 if( !aDelData.isDeleted() )
2656 {
2657 pMenu->mnHighlightedItemPos = pMenu->GetItemPos( nHighlightEventId );
2658 pMenu->nSelectedId = nHighlightEventId;
2659 pMenu->sSelectedIdent = pMenu->GetItemIdent( nHighlightEventId );
2660 pMenu->pStartedFrom = this;
2661 pMenu->ImplCallHighlight( pMenu->mnHighlightedItemPos );
2662 }
2663 return true;
2664 }
2665 else
2666 return false;
2667 }
2668
HandleMenuCommandEvent(Menu * pMenu,sal_uInt16 nCommandEventId)2669 bool Menu::HandleMenuCommandEvent(Menu* pMenu, sal_uInt16 nCommandEventId)
2670 {
2671 if( !pMenu )
2672 pMenu = ImplFindMenu(nCommandEventId);
2673 if( pMenu )
2674 {
2675 pMenu->nSelectedId = nCommandEventId;
2676 pMenu->sSelectedIdent = pMenu->GetItemIdent(nCommandEventId);
2677 pMenu->pStartedFrom = this;
2678 pMenu->ImplSelect();
2679 return true;
2680 }
2681 else
2682 return false;
2683 }
2684
AddMenuBarButton(const Image & i_rImage,const Link<MenuBarButtonCallbackArg &,bool> & i_rLink,const OUString & i_rToolTip)2685 sal_uInt16 MenuBar::AddMenuBarButton( const Image& i_rImage, const Link<MenuBarButtonCallbackArg&,bool>& i_rLink, const OUString& i_rToolTip )
2686 {
2687 MenuBarWindow* pMenuWin = getMenuBarWindow();
2688 return pMenuWin ? pMenuWin->AddMenuBarButton(i_rImage, i_rLink, i_rToolTip) : 0;
2689 }
2690
SetMenuBarButtonHighlightHdl(sal_uInt16 nId,const Link<MenuBarButtonCallbackArg &,bool> & rLink)2691 void MenuBar::SetMenuBarButtonHighlightHdl( sal_uInt16 nId, const Link<MenuBarButtonCallbackArg&,bool>& rLink )
2692 {
2693 MenuBarWindow* pMenuWin = getMenuBarWindow();
2694 if (!pMenuWin)
2695 return;
2696 pMenuWin->SetMenuBarButtonHighlightHdl(nId, rLink);
2697 }
2698
RemoveMenuBarButton(sal_uInt16 nId)2699 void MenuBar::RemoveMenuBarButton( sal_uInt16 nId )
2700 {
2701 MenuBarWindow* pMenuWin = getMenuBarWindow();
2702 if (!pMenuWin)
2703 return;
2704 pMenuWin->RemoveMenuBarButton(nId);
2705 }
2706
GetMenuBarButtonRectPixel(sal_uInt16 nId)2707 tools::Rectangle MenuBar::GetMenuBarButtonRectPixel( sal_uInt16 nId )
2708 {
2709 MenuBarWindow* pMenuWin = getMenuBarWindow();
2710 return pMenuWin ? pMenuWin->GetMenuBarButtonRectPixel(nId) : tools::Rectangle();
2711 }
2712
HandleMenuButtonEvent(sal_uInt16 i_nButtonId)2713 bool MenuBar::HandleMenuButtonEvent( sal_uInt16 i_nButtonId )
2714 {
2715 MenuBarWindow* pMenuWin = getMenuBarWindow();
2716 return pMenuWin && pMenuWin->HandleMenuButtonEvent(i_nButtonId);
2717 }
2718
GetMenuBarHeight() const2719 int MenuBar::GetMenuBarHeight() const
2720 {
2721 const SalMenu *pNativeMenu = ImplGetSalMenu();
2722 int nMenubarHeight;
2723 if (pNativeMenu)
2724 nMenubarHeight = pNativeMenu->GetMenuBarHeight();
2725 else
2726 {
2727 vcl::Window* pMenubarWin = GetWindow();
2728 nMenubarHeight = pMenubarWin ? pMenubarWin->GetOutputSizePixel().Height() : 0;
2729 }
2730 return nMenubarHeight;
2731 }
2732
ImplGetFloatingWindow() const2733 MenuFloatingWindow * PopupMenu::ImplGetFloatingWindow() const {
2734 return static_cast<MenuFloatingWindow*>(GetWindow());
2735 }
2736
PopupMenu()2737 PopupMenu::PopupMenu()
2738 {
2739 mpSalMenu = ImplGetSVData()->mpDefInst->CreateMenu(false, this);
2740 }
2741
PopupMenu(const PopupMenu & rMenu)2742 PopupMenu::PopupMenu( const PopupMenu& rMenu )
2743 {
2744 mpSalMenu = ImplGetSVData()->mpDefInst->CreateMenu(false, this);
2745 *this = rMenu;
2746 }
2747
~PopupMenu()2748 PopupMenu::~PopupMenu()
2749 {
2750 disposeOnce();
2751 }
2752
ClosePopup(PopupMenu * pPopupMenu)2753 void PopupMenu::ClosePopup(PopupMenu* pPopupMenu)
2754 {
2755 MenuFloatingWindow* p = ImplGetFloatingWindow();
2756 if (p && pPopupMenu && p->GetActivePopup() == pPopupMenu)
2757 p->KillActivePopup();
2758 }
2759
2760 namespace vcl
2761 {
IsInPopupMenuExecute()2762 bool IsInPopupMenuExecute()
2763 {
2764 return PopupMenu::GetActivePopupMenu() != nullptr;
2765 }
2766 }
2767
GetActivePopupMenu()2768 PopupMenu* PopupMenu::GetActivePopupMenu()
2769 {
2770 ImplSVData* pSVData = ImplGetSVData();
2771 return pSVData->maAppData.mpActivePopupMenu;
2772 }
2773
EndExecute()2774 void PopupMenu::EndExecute()
2775 {
2776 if (GetWindow())
2777 ImplGetFloatingWindow()->EndExecute( 0 );
2778 }
2779
SelectItem(sal_uInt16 nId)2780 void PopupMenu::SelectItem(sal_uInt16 nId)
2781 {
2782 if (!GetWindow())
2783 return;
2784
2785 if( nId != ITEMPOS_INVALID )
2786 {
2787 size_t nPos = 0;
2788 MenuItemData* pData = GetItemList()->GetData( nId, nPos );
2789 if (pData && pData->pSubMenu)
2790 ImplGetFloatingWindow()->ChangeHighlightItem( nPos, true );
2791 else
2792 ImplGetFloatingWindow()->EndExecute( nId );
2793 }
2794 else
2795 {
2796 MenuFloatingWindow* pFloat = ImplGetFloatingWindow();
2797 pFloat->GrabFocus();
2798
2799 for( size_t nPos = 0; nPos < GetItemList()->size(); nPos++ )
2800 {
2801 MenuItemData* pData = GetItemList()->GetDataFromPos( nPos );
2802 if( pData->pSubMenu )
2803 {
2804 pFloat->KillActivePopup();
2805 }
2806 }
2807 pFloat->ChangeHighlightItem( ITEMPOS_INVALID, false );
2808 }
2809 }
2810
SetSelectedEntry(sal_uInt16 nId)2811 void PopupMenu::SetSelectedEntry( sal_uInt16 nId )
2812 {
2813 nSelectedId = nId;
2814 sSelectedIdent = GetItemIdent(nId);
2815 }
2816
Execute(vcl::Window * pExecWindow,const Point & rPopupPos)2817 sal_uInt16 PopupMenu::Execute( vcl::Window* pExecWindow, const Point& rPopupPos )
2818 {
2819 return Execute( pExecWindow, tools::Rectangle( rPopupPos, rPopupPos ), PopupMenuFlags::ExecuteDown );
2820 }
2821
lcl_TranslateFlags(PopupMenuFlags nFlags)2822 static FloatWinPopupFlags lcl_TranslateFlags(PopupMenuFlags nFlags)
2823 {
2824 FloatWinPopupFlags nPopupModeFlags = FloatWinPopupFlags::NONE;
2825 if ( nFlags & PopupMenuFlags::ExecuteDown )
2826 nPopupModeFlags = FloatWinPopupFlags::Down;
2827 else if ( nFlags & PopupMenuFlags::ExecuteUp )
2828 nPopupModeFlags = FloatWinPopupFlags::Up;
2829 else if ( nFlags & PopupMenuFlags::ExecuteRight )
2830 nPopupModeFlags = FloatWinPopupFlags::Right;
2831 else
2832 nPopupModeFlags = FloatWinPopupFlags::Down;
2833
2834 if (nFlags & PopupMenuFlags::NoMouseUpClose ) // allow popup menus to stay open on mouse button up
2835 nPopupModeFlags |= FloatWinPopupFlags::NoMouseUpClose; // useful if the menu was opened on mousebutton down (eg toolbox configuration)
2836
2837 return nPopupModeFlags;
2838 }
2839
Execute(vcl::Window * pExecWindow,const tools::Rectangle & rRect,PopupMenuFlags nFlags)2840 sal_uInt16 PopupMenu::Execute( vcl::Window* pExecWindow, const tools::Rectangle& rRect, PopupMenuFlags nFlags )
2841 {
2842 ENSURE_OR_RETURN( pExecWindow, "PopupMenu::Execute: need a non-NULL window!", 0 );
2843 return ImplExecute( pExecWindow, rRect, lcl_TranslateFlags(nFlags), nullptr, false );
2844 }
2845
ImplFlushPendingSelect()2846 void PopupMenu::ImplFlushPendingSelect()
2847 {
2848 // is there still Select?
2849 Menu* pSelect = ImplFindSelectMenu();
2850 if (pSelect)
2851 {
2852 // Select should be called prior to leaving execute in a popup menu!
2853 Application::RemoveUserEvent( pSelect->nEventId );
2854 pSelect->nEventId = nullptr;
2855 pSelect->Select();
2856 }
2857 }
2858
ImplExecute(const VclPtr<vcl::Window> & pParentWin,const tools::Rectangle & rRect,FloatWinPopupFlags nPopupModeFlags,Menu * pSFrom,bool bPreSelectFirst)2859 sal_uInt16 PopupMenu::ImplExecute(const VclPtr<vcl::Window>& pParentWin, const tools::Rectangle& rRect,
2860 FloatWinPopupFlags nPopupModeFlags, Menu* pSFrom, bool bPreSelectFirst)
2861 {
2862 // tdf#126054 hold this until after function completes
2863 VclPtr<PopupMenu> xThis(this);
2864
2865 bool bRealExecute = false;
2866 const sal_uInt16 nItemCount = GetItemCount();
2867 if (!pSFrom && (vcl::IsInPopupMenuExecute() || !nItemCount))
2868 return 0;
2869
2870 mpLayoutData.reset();
2871
2872 ImplSVData* pSVData = ImplGetSVData();
2873
2874 pStartedFrom = pSFrom;
2875 nSelectedId = 0;
2876 sSelectedIdent.clear();
2877 bCanceled = false;
2878
2879 VclPtr<vcl::Window> xFocusId;
2880 if ( !pStartedFrom )
2881 {
2882 pSVData->mpWinData->mbNoDeactivate = true;
2883 xFocusId = Window::SaveFocus();
2884 bRealExecute = true;
2885 }
2886 else
2887 {
2888 // assure that only one menu is open at a time
2889 if (pStartedFrom->IsMenuBar() && pSVData->mpWinData->mpFirstFloat)
2890 pSVData->mpWinData->mpFirstFloat->EndPopupMode(FloatWinPopupEndFlags::Cancel
2891 | FloatWinPopupEndFlags::CloseAll);
2892 }
2893
2894 tools::Rectangle aRect(rRect);
2895 aRect.SetPos(pParentWin->OutputToScreenPixel(aRect.TopLeft()));
2896
2897 nPopupModeFlags |= FloatWinPopupFlags::NoKeyClose | FloatWinPopupFlags::AllMouseButtonClose | FloatWinPopupFlags::GrabFocus;
2898 if (bRealExecute)
2899 nPopupModeFlags |= FloatWinPopupFlags::NewLevel;
2900
2901 // MenuFlags get clobbered in the Activate function. Restore them after calling.
2902 MenuFlags nMenuFlagsSaved = GetMenuFlags();
2903 bInCallback = true; // set it here, if Activate overridden
2904 Activate();
2905 bInCallback = false;
2906 SetMenuFlags(nMenuFlagsSaved);
2907
2908 if (pParentWin->isDisposed())
2909 return 0;
2910
2911 if ( bCanceled || bKilled )
2912 return 0;
2913
2914 if (!nItemCount)
2915 return 0;
2916
2917 // The flag MenuFlags::HideDisabledEntries is inherited.
2918 if ( pSFrom )
2919 {
2920 if ( pSFrom->nMenuFlags & MenuFlags::HideDisabledEntries )
2921 nMenuFlags |= MenuFlags::HideDisabledEntries;
2922 else
2923 nMenuFlags &= ~MenuFlags::HideDisabledEntries;
2924 }
2925
2926 sal_uInt16 nVisibleEntries = ImplGetVisibleItemCount();
2927 if ( !nVisibleEntries )
2928 {
2929 OUString aTmpEntryText(VclResId(SV_RESID_STRING_NOSELECTIONPOSSIBLE));
2930
2931 MenuItemData* pData = NbcInsertItem(0xFFFF, MenuItemBits::NONE, aTmpEntryText, nullptr, 0xFFFF, {});
2932 size_t nPos = 0;
2933 pData = pItemList->GetData( pData->nId, nPos );
2934 assert(pData);
2935 if (pData)
2936 {
2937 pData->bIsTemporary = true;
2938 }
2939 ImplCallEventListeners(VclEventId::MenuSubmenuChanged, nPos);
2940 }
2941
2942 VclPtr<MenuFloatingWindow> pWin = VclPtrInstance<MenuFloatingWindow>(this, pParentWin, WB_BORDER | WB_SYSTEMWINDOW);
2943 if (comphelper::LibreOfficeKit::isActive() && get_id() == "editviewspellmenu")
2944 {
2945 VclPtr<vcl::Window> xNotifierParent = pParentWin->GetParentWithLOKNotifier();
2946 assert(xNotifierParent && xNotifierParent->GetLOKNotifier() && "editview menu without LOKNotifier");
2947 pWin->SetLOKNotifier(xNotifierParent->GetLOKNotifier());
2948 }
2949
2950 if( pSVData->maNWFData.mbFlatMenu )
2951 pWin->SetBorderStyle( WindowBorderStyle::NOBORDER );
2952 else
2953 pWin->SetBorderStyle( pWin->GetBorderStyle() | WindowBorderStyle::MENU );
2954
2955 m_pWindow.disposeAndClear();
2956 m_pWindow = pWin;
2957
2958 Size aSz = ImplCalcSize( pWin );
2959
2960 AbsoluteScreenPixelRectangle aDesktopRect(pWin->GetDesktopRectPixel());
2961 if( Application::GetScreenCount() > 1 )
2962 {
2963 vcl::Window* pDeskW = m_pWindow->GetWindow( GetWindowType::RealParent );
2964 if( ! pDeskW )
2965 pDeskW = m_pWindow;
2966 AbsoluteScreenPixelPoint aDesktopTL(pDeskW->OutputToAbsoluteScreenPixel(aRect.TopLeft()));
2967 aDesktopRect = Application::GetScreenPosSizePixel(
2968 Application::GetBestScreen(AbsoluteScreenPixelRectangle(aDesktopTL, aRect.GetSize())));
2969 }
2970
2971 tools::Long nMaxHeight = aDesktopRect.GetHeight();
2972
2973 //rhbz#1021915. If a menu won't fit in the desired location the default
2974 //mode is to place it somewhere it will fit. e.g. above, left, right. For
2975 //some cases, e.g. menubars, it's desirable to limit the options to
2976 //above/below and force the menu to scroll if it won't fit
2977 if (nPopupModeFlags & FloatWinPopupFlags::NoHorzPlacement)
2978 {
2979 vcl::Window* pRef = pWin;
2980 if ( pRef->GetParent() )
2981 pRef = pRef->GetParent();
2982
2983 AbsoluteScreenPixelRectangle devRect(pRef->OutputToAbsoluteScreenPixel(aRect.TopLeft()),
2984 pRef->OutputToAbsoluteScreenPixel(aRect.BottomRight()));
2985
2986 tools::Long nHeightAbove = devRect.Top() - aDesktopRect.Top();
2987 tools::Long nHeightBelow = aDesktopRect.Bottom() - devRect.Bottom();
2988 nMaxHeight = std::min(nMaxHeight, std::max(nHeightAbove, nHeightBelow));
2989 }
2990
2991 // In certain cases this might be misdetected with a height of 0, leading to menus not being displayed.
2992 // So assume that the available screen size matches at least the system requirements. With menu origin
2993 // in the middle, nMaxHeight will be at least half of screen height.
2994 SAL_WARN_IF(nMaxHeight < 768 / 2, "vcl",
2995 "Available height misdetected as " << nMaxHeight
2996 << "px. Setting to 384px instead.");
2997 nMaxHeight = std::max(nMaxHeight, tools::Long(768 / 2));
2998
2999 if (pStartedFrom && pStartedFrom->IsMenuBar())
3000 nMaxHeight -= pParentWin->GetSizePixel().Height();
3001 sal_Int32 nLeft, nTop, nRight, nBottom;
3002 m_pWindow->GetBorder( nLeft, nTop, nRight, nBottom );
3003 nMaxHeight -= nTop+nBottom;
3004 if ( aSz.Height() > nMaxHeight )
3005 {
3006 pWin->EnableScrollMenu( true );
3007 sal_uInt16 nStart = ImplGetFirstVisible();
3008 sal_uInt16 nEntries = ImplCalcVisEntries( nMaxHeight, nStart );
3009 aSz.setHeight( ImplCalcHeight( nEntries ) );
3010 }
3011
3012 pWin->SetFocusId( xFocusId );
3013 pWin->SetOutputSizePixel( aSz );
3014
3015 const bool bNative = Run(pWin, bRealExecute, bPreSelectFirst, nPopupModeFlags, pSFrom, aRect);
3016 FinishRun(pWin, pParentWin, bRealExecute, bNative);
3017 return nSelectedId;
3018 }
3019
Run(const VclPtr<MenuFloatingWindow> & pWin,const bool bRealExecute,const bool bPreSelectFirst,const FloatWinPopupFlags nPopupModeFlags,Menu * pSFrom,const tools::Rectangle & rRect)3020 bool PopupMenu::Run(const VclPtr<MenuFloatingWindow>& pWin, const bool bRealExecute, const bool bPreSelectFirst,
3021 const FloatWinPopupFlags nPopupModeFlags, Menu* pSFrom, const tools::Rectangle& rRect)
3022 {
3023 SalMenu* pMenu = ImplGetSalMenu();
3024 if (pMenu && bRealExecute && pMenu->ShowNativePopupMenu(pWin, rRect, nPopupModeFlags))
3025 return true;
3026
3027 pWin->StartPopupMode(rRect, nPopupModeFlags);
3028 if (pSFrom)
3029 {
3030 sal_uInt16 aPos;
3031 if (pSFrom->IsMenuBar())
3032 aPos = static_cast<MenuBarWindow *>(pSFrom->m_pWindow.get())->GetHighlightedItem();
3033 else
3034 aPos = static_cast<MenuFloatingWindow *>(pSFrom->m_pWindow.get())->GetHighlightedItem();
3035
3036 pWin->SetPosInParent(aPos); // store position to be sent in SUBMENUDEACTIVATE
3037 pSFrom->ImplCallEventListeners(VclEventId::MenuSubmenuActivate, aPos);
3038 }
3039
3040 if ( bPreSelectFirst )
3041 {
3042 for (size_t n = 0; n < static_cast<size_t>(GetItemCount()); n++)
3043 {
3044 MenuItemData* pData = pItemList->GetDataFromPos( n );
3045 if ( ( pData->bEnabled
3046 || !Application::GetSettings().GetStyleSettings().GetSkipDisabledInMenus()
3047 )
3048 && ( pData->eType != MenuItemType::SEPARATOR )
3049 && ImplIsVisible( n )
3050 && ImplIsSelectable( n )
3051 )
3052 {
3053 pWin->ChangeHighlightItem(n, false);
3054 break;
3055 }
3056 }
3057 }
3058
3059 if (bRealExecute)
3060 pWin->Execute();
3061
3062 return false;
3063 }
3064
FinishRun(const VclPtr<MenuFloatingWindow> & pWin,const VclPtr<vcl::Window> & pParentWin,const bool bRealExecute,const bool bIsNativeMenu)3065 void PopupMenu::FinishRun(const VclPtr<MenuFloatingWindow>& pWin, const VclPtr<vcl::Window>& pParentWin, const bool bRealExecute, const bool bIsNativeMenu)
3066 {
3067 if (!bRealExecute || pWin->isDisposed())
3068 return;
3069
3070 if (!bIsNativeMenu)
3071 {
3072 VclPtr<vcl::Window> xFocusId = pWin->GetFocusId();
3073 assert(xFocusId == nullptr && "Focus should already be restored by MenuFloatingWindow::End");
3074 pWin->ImplEndPopupMode(FloatWinPopupEndFlags::NONE, xFocusId);
3075
3076 if (nSelectedId) // then clean up .. ( otherwise done by TH )
3077 {
3078 PopupMenu* pSub = pWin->GetActivePopup();
3079 while ( pSub )
3080 {
3081 pSub->ImplGetFloatingWindow()->EndPopupMode();
3082 pSub = pSub->ImplGetFloatingWindow()->GetActivePopup();
3083 }
3084 }
3085 }
3086 else
3087 pWin->StopExecute();
3088
3089 pWin->doShutdown();
3090 ImplClosePopupToolBox(pParentWin);
3091 ImplFlushPendingSelect();
3092 }
3093
ImplCalcVisEntries(tools::Long nMaxHeight,sal_uInt16 nStartEntry,sal_uInt16 * pLastVisible) const3094 sal_uInt16 PopupMenu::ImplCalcVisEntries( tools::Long nMaxHeight, sal_uInt16 nStartEntry, sal_uInt16* pLastVisible ) const
3095 {
3096 nMaxHeight -= 2 * ImplGetFloatingWindow()->GetScrollerHeight();
3097
3098 tools::Long nHeight = 0;
3099 size_t nEntries = pItemList->size();
3100 sal_uInt16 nVisEntries = 0;
3101
3102 if ( pLastVisible )
3103 *pLastVisible = 0;
3104
3105 for ( size_t n = nStartEntry; n < nEntries; n++ )
3106 {
3107 if ( ImplIsVisible( n ) )
3108 {
3109 MenuItemData* pData = pItemList->GetDataFromPos( n );
3110 nHeight += pData->aSz.Height();
3111 if ( nHeight > nMaxHeight )
3112 break;
3113
3114 if ( pLastVisible )
3115 *pLastVisible = n;
3116 nVisEntries++;
3117 }
3118 }
3119 return nVisEntries;
3120 }
3121
ImplCalcHeight(sal_uInt16 nEntries) const3122 tools::Long PopupMenu::ImplCalcHeight( sal_uInt16 nEntries ) const
3123 {
3124 tools::Long nHeight = 0;
3125
3126 sal_uInt16 nFound = 0;
3127 for ( size_t n = 0; ( nFound < nEntries ) && ( n < pItemList->size() ); n++ )
3128 {
3129 if ( ImplIsVisible( static_cast<sal_uInt16>(n) ) )
3130 {
3131 MenuItemData* pData = pItemList->GetDataFromPos( n );
3132 nHeight += pData->aSz.Height();
3133 nFound++;
3134 }
3135 }
3136
3137 nHeight += 2*ImplGetFloatingWindow()->GetScrollerHeight();
3138
3139 return nHeight;
3140 }
3141
CreateMenuInterface()3142 css::uno::Reference<css::awt::XPopupMenu> PopupMenu::CreateMenuInterface()
3143 {
3144 UnoWrapperBase* pWrapper = UnoWrapperBase::GetUnoWrapper();
3145 if ( pWrapper )
3146 return pWrapper->CreateMenuInterface(this);
3147 return nullptr;
3148 }
3149
ImplMenuDelData(const Menu * pMenu)3150 ImplMenuDelData::ImplMenuDelData( const Menu* pMenu )
3151 : mpNext( nullptr )
3152 , mpMenu( nullptr )
3153 {
3154 if( pMenu )
3155 const_cast< Menu* >( pMenu )->ImplAddDel( *this );
3156 }
3157
~ImplMenuDelData()3158 ImplMenuDelData::~ImplMenuDelData()
3159 {
3160 if( mpMenu )
3161 const_cast< Menu* >( mpMenu.get() )->ImplRemoveDel( *this );
3162 }
3163
3164 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
3165