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 
10 #include <Qt5Menu.hxx>
11 #include <Qt5Menu.moc>
12 
13 #include <Qt5Frame.hxx>
14 #include <Qt5Instance.hxx>
15 #include <Qt5MainWindow.hxx>
16 
17 #include <QtWidgets/QMenuBar>
18 #include <QtWidgets/QPushButton>
19 
20 #include <vcl/svapp.hxx>
21 #include <sal/log.hxx>
22 
23 #include <strings.hrc>
24 #include <bitmaps.hlst>
25 
26 #include <vcl/floatwin.hxx>
27 #include <window.h>
28 
Qt5Menu(bool bMenuBar)29 Qt5Menu::Qt5Menu(bool bMenuBar)
30     : mpVCLMenu(nullptr)
31     , mpParentSalMenu(nullptr)
32     , mpFrame(nullptr)
33     , mbMenuBar(bMenuBar)
34     , mpQMenuBar(nullptr)
35     , mpQMenu(nullptr)
36     , mpCloseButton(nullptr)
37 {
38 }
39 
VisibleMenuBar()40 bool Qt5Menu::VisibleMenuBar() { return true; }
41 
InsertMenuItem(Qt5MenuItem * pSalMenuItem,unsigned nPos)42 void Qt5Menu::InsertMenuItem(Qt5MenuItem* pSalMenuItem, unsigned nPos)
43 {
44     sal_uInt16 nId = pSalMenuItem->mnId;
45     OUString aText = mpVCLMenu->GetItemText(nId);
46     NativeItemText(aText);
47     vcl::KeyCode nAccelKey = mpVCLMenu->GetAccelKey(nId);
48 
49     pSalMenuItem->mpAction.reset();
50     pSalMenuItem->mpMenu.reset();
51 
52     if (mbMenuBar)
53     {
54         // top-level menu
55         if (mpQMenuBar)
56         {
57             QMenu* pQMenu = new QMenu(toQString(aText), nullptr);
58             pSalMenuItem->mpMenu.reset(pQMenu);
59 
60             if ((nPos != MENU_APPEND)
61                 && (static_cast<size_t>(nPos) < static_cast<size_t>(mpQMenuBar->actions().size())))
62             {
63                 mpQMenuBar->insertMenu(mpQMenuBar->actions()[nPos], pQMenu);
64             }
65             else
66             {
67                 mpQMenuBar->addMenu(pQMenu);
68             }
69 
70             // correct parent menu for generated menu
71             if (pSalMenuItem->mpSubMenu)
72             {
73                 pSalMenuItem->mpSubMenu->mpQMenu = pQMenu;
74             }
75 
76             connect(pQMenu, &QMenu::aboutToShow, this,
77                     [pSalMenuItem] { slotMenuAboutToShow(pSalMenuItem); });
78             connect(pQMenu, &QMenu::aboutToHide, this,
79                     [pSalMenuItem] { slotMenuAboutToHide(pSalMenuItem); });
80         }
81     }
82     else
83     {
84         if (!mpQMenu)
85         {
86             // no QMenu set, instantiate own one
87             mpOwnedQMenu.reset(new QMenu);
88             mpQMenu = mpOwnedQMenu.get();
89         }
90 
91         if (pSalMenuItem->mpSubMenu)
92         {
93             // submenu
94             QMenu* pQMenu = new QMenu(toQString(aText), nullptr);
95             pSalMenuItem->mpMenu.reset(pQMenu);
96 
97             if ((nPos != MENU_APPEND)
98                 && (static_cast<size_t>(nPos) < static_cast<size_t>(mpQMenu->actions().size())))
99             {
100                 mpQMenu->insertMenu(mpQMenu->actions()[nPos], pQMenu);
101             }
102             else
103             {
104                 mpQMenu->addMenu(pQMenu);
105             }
106 
107             // correct parent menu for generated menu
108             pSalMenuItem->mpSubMenu->mpQMenu = pQMenu;
109 
110             ReinitializeActionGroup(nPos);
111 
112             // clear all action groups since menu is recreated
113             pSalMenuItem->mpSubMenu->ResetAllActionGroups();
114 
115             connect(pQMenu, &QMenu::aboutToShow, this,
116                     [pSalMenuItem] { slotMenuAboutToShow(pSalMenuItem); });
117             connect(pQMenu, &QMenu::aboutToHide, this,
118                     [pSalMenuItem] { slotMenuAboutToHide(pSalMenuItem); });
119         }
120         else
121         {
122             if (pSalMenuItem->mnType == MenuItemType::SEPARATOR)
123             {
124                 QAction* pAction = new QAction(nullptr);
125                 pSalMenuItem->mpAction.reset(pAction);
126                 pAction->setSeparator(true);
127 
128                 if ((nPos != MENU_APPEND)
129                     && (static_cast<size_t>(nPos) < static_cast<size_t>(mpQMenu->actions().size())))
130                 {
131                     mpQMenu->insertAction(mpQMenu->actions()[nPos], pAction);
132                 }
133                 else
134                 {
135                     mpQMenu->addAction(pAction);
136                 }
137 
138                 ReinitializeActionGroup(nPos);
139             }
140             else
141             {
142                 // leaf menu
143                 QAction* pAction = new QAction(toQString(aText), nullptr);
144                 pSalMenuItem->mpAction.reset(pAction);
145 
146                 if ((nPos != MENU_APPEND)
147                     && (static_cast<size_t>(nPos) < static_cast<size_t>(mpQMenu->actions().size())))
148                 {
149                     mpQMenu->insertAction(mpQMenu->actions()[nPos], pAction);
150                 }
151                 else
152                 {
153                     mpQMenu->addAction(pAction);
154                 }
155 
156                 ReinitializeActionGroup(nPos);
157 
158                 UpdateActionGroupItem(pSalMenuItem);
159 
160                 const Qt5Frame* pFrame = GetFrame();
161                 if (pFrame)
162                     pAction->setShortcut(toQString(nAccelKey.GetName(pFrame->GetWindow())));
163 
164                 connect(pAction, &QAction::triggered, this,
165                         [pSalMenuItem] { slotMenuTriggered(pSalMenuItem); });
166             }
167         }
168     }
169 
170     QAction* pAction = pSalMenuItem->getAction();
171     if (pAction)
172     {
173         pAction->setEnabled(pSalMenuItem->mbEnabled);
174         pAction->setVisible(pSalMenuItem->mbVisible);
175     }
176 }
177 
ReinitializeActionGroup(unsigned nPos)178 void Qt5Menu::ReinitializeActionGroup(unsigned nPos)
179 {
180     const unsigned nCount = GetItemCount();
181 
182     if (nCount == 0)
183     {
184         return;
185     }
186 
187     if (nPos == MENU_APPEND)
188     {
189         nPos = nCount - 1;
190     }
191     else if (nPos >= nCount)
192     {
193         return;
194     }
195 
196     Qt5MenuItem* pPrevItem = (nPos > 0) ? GetItemAtPos(nPos - 1) : nullptr;
197     Qt5MenuItem* pCurrentItem = GetItemAtPos(nPos);
198     Qt5MenuItem* pNextItem = (nPos < nCount - 1) ? GetItemAtPos(nPos + 1) : nullptr;
199 
200     if (pCurrentItem->mnType == MenuItemType::SEPARATOR)
201     {
202         pCurrentItem->mpActionGroup.reset();
203 
204         // if it's inserted into middle of existing group, split it into two groups:
205         // first goes original group, after separator goes new group
206         if (pPrevItem && pPrevItem->mpActionGroup && pNextItem && pNextItem->mpActionGroup
207             && (pPrevItem->mpActionGroup == pNextItem->mpActionGroup))
208         {
209             std::shared_ptr<QActionGroup> pFirstActionGroup = pPrevItem->mpActionGroup;
210             std::shared_ptr<QActionGroup> pSecondActionGroup(new QActionGroup(nullptr));
211             pSecondActionGroup->setExclusive(true);
212 
213             auto actions = pFirstActionGroup->actions();
214 
215             for (unsigned idx = nPos + 1; idx < nCount; ++idx)
216             {
217                 Qt5MenuItem* pModifiedItem = GetItemAtPos(idx);
218 
219                 if ((!pModifiedItem) || (!pModifiedItem->mpActionGroup))
220                 {
221                     break;
222                 }
223 
224                 pModifiedItem->mpActionGroup = pSecondActionGroup;
225                 auto action = pModifiedItem->getAction();
226 
227                 if (actions.contains(action))
228                 {
229                     pFirstActionGroup->removeAction(action);
230                     pSecondActionGroup->addAction(action);
231                 }
232             }
233         }
234     }
235     else
236     {
237         if (!pCurrentItem->mpActionGroup)
238         {
239             // unless element is inserted between two separators, or a separator and an end of vector, use neighbouring group since it's shared
240             if (pPrevItem && pPrevItem->mpActionGroup)
241             {
242                 pCurrentItem->mpActionGroup = pPrevItem->mpActionGroup;
243             }
244             else if (pNextItem && pNextItem->mpActionGroup)
245             {
246                 pCurrentItem->mpActionGroup = pNextItem->mpActionGroup;
247             }
248             else
249             {
250                 pCurrentItem->mpActionGroup.reset(new QActionGroup(nullptr));
251                 pCurrentItem->mpActionGroup->setExclusive(true);
252             }
253         }
254 
255         // if there's also a different group after this element, merge it
256         if (pNextItem && pNextItem->mpActionGroup
257             && (pCurrentItem->mpActionGroup != pNextItem->mpActionGroup))
258         {
259             auto pFirstCheckedAction = pCurrentItem->mpActionGroup->checkedAction();
260             auto pSecondCheckedAction = pNextItem->mpActionGroup->checkedAction();
261             auto actions = pNextItem->mpActionGroup->actions();
262 
263             // first move all actions from second group to first one, and if first group already has checked action,
264             // and second group also has a checked action, uncheck action from second group
265             for (auto action : actions)
266             {
267                 pNextItem->mpActionGroup->removeAction(action);
268 
269                 if (pFirstCheckedAction && pSecondCheckedAction && (action == pSecondCheckedAction))
270                 {
271                     action->setChecked(false);
272                 }
273 
274                 pCurrentItem->mpActionGroup->addAction(action);
275             }
276 
277             // now replace all pointers to second group with pointers to first group
278             for (unsigned idx = nPos + 1; idx < nCount; ++idx)
279             {
280                 Qt5MenuItem* pModifiedItem = GetItemAtPos(idx);
281 
282                 if ((!pModifiedItem) || (!pModifiedItem->mpActionGroup))
283                 {
284                     break;
285                 }
286 
287                 pModifiedItem->mpActionGroup = pCurrentItem->mpActionGroup;
288             }
289         }
290     }
291 }
292 
ResetAllActionGroups()293 void Qt5Menu::ResetAllActionGroups()
294 {
295     for (unsigned nItem = 0; nItem < GetItemCount(); ++nItem)
296     {
297         Qt5MenuItem* pSalMenuItem = GetItemAtPos(nItem);
298         pSalMenuItem->mpActionGroup.reset();
299     }
300 }
301 
UpdateActionGroupItem(const Qt5MenuItem * pSalMenuItem)302 void Qt5Menu::UpdateActionGroupItem(const Qt5MenuItem* pSalMenuItem)
303 {
304     QAction* pAction = pSalMenuItem->getAction();
305     if (!pAction)
306         return;
307 
308     bool bChecked = mpVCLMenu->IsItemChecked(pSalMenuItem->mnId);
309     MenuItemBits itemBits = mpVCLMenu->GetItemBits(pSalMenuItem->mnId);
310 
311     if (itemBits & MenuItemBits::RADIOCHECK)
312     {
313         pAction->setCheckable(true);
314 
315         if (pSalMenuItem->mpActionGroup)
316         {
317             pSalMenuItem->mpActionGroup->addAction(pAction);
318         }
319 
320         pAction->setChecked(bChecked);
321     }
322     else
323     {
324         pAction->setActionGroup(nullptr);
325 
326         if (itemBits & MenuItemBits::CHECKABLE)
327         {
328             pAction->setCheckable(true);
329             pAction->setChecked(bChecked);
330         }
331         else
332         {
333             pAction->setChecked(false);
334             pAction->setCheckable(false);
335         }
336     }
337 }
338 
InsertItem(SalMenuItem * pSalMenuItem,unsigned nPos)339 void Qt5Menu::InsertItem(SalMenuItem* pSalMenuItem, unsigned nPos)
340 {
341     SolarMutexGuard aGuard;
342     Qt5MenuItem* pItem = static_cast<Qt5MenuItem*>(pSalMenuItem);
343 
344     if (nPos == MENU_APPEND)
345         maItems.push_back(pItem);
346     else
347         maItems.insert(maItems.begin() + nPos, pItem);
348 
349     pItem->mpParentMenu = this;
350 
351     InsertMenuItem(pItem, nPos);
352 }
353 
RemoveItem(unsigned nPos)354 void Qt5Menu::RemoveItem(unsigned nPos)
355 {
356     SolarMutexGuard aGuard;
357 
358     if (nPos < maItems.size())
359     {
360         Qt5MenuItem* pItem = maItems[nPos];
361         pItem->mpAction.reset();
362         pItem->mpMenu.reset();
363 
364         maItems.erase(maItems.begin() + nPos);
365 
366         // Recalculate action groups if necessary:
367         // if separator between two QActionGroups was removed,
368         // it may be needed to merge them
369         if (nPos > 0)
370         {
371             ReinitializeActionGroup(nPos - 1);
372         }
373     }
374 }
375 
SetSubMenu(SalMenuItem * pSalMenuItem,SalMenu * pSubMenu,unsigned nPos)376 void Qt5Menu::SetSubMenu(SalMenuItem* pSalMenuItem, SalMenu* pSubMenu, unsigned nPos)
377 {
378     SolarMutexGuard aGuard;
379     Qt5MenuItem* pItem = static_cast<Qt5MenuItem*>(pSalMenuItem);
380     Qt5Menu* pQSubMenu = static_cast<Qt5Menu*>(pSubMenu);
381 
382     pItem->mpSubMenu = pQSubMenu;
383     // at this point the pointer to parent menu may be outdated, update it too
384     pItem->mpParentMenu = this;
385 
386     if (pQSubMenu != nullptr)
387     {
388         pQSubMenu->mpParentSalMenu = this;
389         pQSubMenu->mpQMenu = pItem->mpMenu.get();
390     }
391 
392     // if it's not a menu bar item, then convert it to corresponding item if type if necessary.
393     // If submenu is present and it's an action, convert it to menu.
394     // If submenu is not present and it's a menu, convert it to action.
395     // It may be fine to proceed in any case, but by skipping other cases
396     // amount of unneeded actions taken should be reduced.
397     if (pItem->mpParentMenu->mbMenuBar || (pQSubMenu && pItem->mpMenu)
398         || ((!pQSubMenu) && pItem->mpAction))
399     {
400         return;
401     }
402 
403     InsertMenuItem(pItem, nPos);
404 }
405 
SetFrame(const SalFrame * pFrame)406 void Qt5Menu::SetFrame(const SalFrame* pFrame)
407 {
408     auto* pSalInst(static_cast<Qt5Instance*>(GetSalData()->m_pInstance));
409     assert(pSalInst);
410     if (!pSalInst->IsMainThread())
411     {
412         pSalInst->RunInMainThread([this, pFrame]() { SetFrame(pFrame); });
413         return;
414     }
415 
416     SolarMutexGuard aGuard;
417     assert(mbMenuBar);
418     mpFrame = const_cast<Qt5Frame*>(static_cast<const Qt5Frame*>(pFrame));
419 
420     mpFrame->SetMenu(this);
421 
422     Qt5MainWindow* pMainWindow = mpFrame->GetTopLevelWindow();
423     if (pMainWindow)
424     {
425         mpQMenuBar = pMainWindow->menuBar();
426         if (mpQMenuBar)
427         {
428             mpQMenuBar->clear();
429             QPushButton* pButton
430                 = static_cast<QPushButton*>(mpQMenuBar->cornerWidget(Qt::TopRightCorner));
431             if (pButton && ((mpCloseButton != pButton) || !maCloseButtonConnection))
432             {
433                 maCloseButtonConnection
434                     = connect(pButton, &QPushButton::clicked, this, &Qt5Menu::slotCloseDocument);
435                 mpCloseButton = pButton;
436             }
437         }
438 
439         mpQMenu = nullptr;
440 
441         DoFullMenuUpdate(mpVCLMenu);
442     }
443 }
444 
DoFullMenuUpdate(Menu * pMenuBar)445 void Qt5Menu::DoFullMenuUpdate(Menu* pMenuBar)
446 {
447     // clear action groups since menu is rebuilt
448     ResetAllActionGroups();
449     ShowCloseButton(false);
450 
451     for (sal_Int32 nItem = 0; nItem < static_cast<sal_Int32>(GetItemCount()); nItem++)
452     {
453         Qt5MenuItem* pSalMenuItem = GetItemAtPos(nItem);
454         InsertMenuItem(pSalMenuItem, nItem);
455         SetItemImage(nItem, pSalMenuItem, pSalMenuItem->maImage);
456         const bool bShowDisabled
457             = bool(pMenuBar->GetMenuFlags() & MenuFlags::AlwaysShowDisabledEntries)
458               || !bool(pMenuBar->GetMenuFlags() & MenuFlags::HideDisabledEntries);
459         const bool bVisible = bShowDisabled || mpVCLMenu->IsItemEnabled(pSalMenuItem->mnId);
460         pSalMenuItem->getAction()->setVisible(bVisible);
461 
462         if (pSalMenuItem->mpSubMenu != nullptr)
463         {
464             pMenuBar->HandleMenuActivateEvent(pSalMenuItem->mpSubMenu->GetMenu());
465             pSalMenuItem->mpSubMenu->DoFullMenuUpdate(pMenuBar);
466             pMenuBar->HandleMenuDeActivateEvent(pSalMenuItem->mpSubMenu->GetMenu());
467         }
468     }
469 }
470 
ShowItem(unsigned nPos,bool bShow)471 void Qt5Menu::ShowItem(unsigned nPos, bool bShow)
472 {
473     if (nPos < maItems.size())
474     {
475         Qt5MenuItem* pSalMenuItem = GetItemAtPos(nPos);
476         QAction* pAction = pSalMenuItem->getAction();
477         if (pAction)
478             pAction->setVisible(bShow);
479         pSalMenuItem->mbVisible = bShow;
480     }
481 }
482 
SetItemBits(unsigned nPos,MenuItemBits)483 void Qt5Menu::SetItemBits(unsigned nPos, MenuItemBits)
484 {
485     if (nPos < maItems.size())
486     {
487         Qt5MenuItem* pSalMenuItem = GetItemAtPos(nPos);
488         UpdateActionGroupItem(pSalMenuItem);
489     }
490 }
491 
CheckItem(unsigned nPos,bool bChecked)492 void Qt5Menu::CheckItem(unsigned nPos, bool bChecked)
493 {
494     if (nPos < maItems.size())
495     {
496         Qt5MenuItem* pSalMenuItem = GetItemAtPos(nPos);
497         QAction* pAction = pSalMenuItem->getAction();
498         if (pAction)
499         {
500             pAction->setCheckable(true);
501             pAction->setChecked(bChecked);
502         }
503     }
504 }
505 
EnableItem(unsigned nPos,bool bEnable)506 void Qt5Menu::EnableItem(unsigned nPos, bool bEnable)
507 {
508     if (nPos < maItems.size())
509     {
510         Qt5MenuItem* pSalMenuItem = GetItemAtPos(nPos);
511         QAction* pAction = pSalMenuItem->getAction();
512         if (pAction)
513             pAction->setEnabled(bEnable);
514         pSalMenuItem->mbEnabled = bEnable;
515     }
516 }
517 
SetItemText(unsigned,SalMenuItem * pItem,const OUString & rText)518 void Qt5Menu::SetItemText(unsigned, SalMenuItem* pItem, const OUString& rText)
519 {
520     Qt5MenuItem* pSalMenuItem = static_cast<Qt5MenuItem*>(pItem);
521     QAction* pAction = pSalMenuItem->getAction();
522     if (pAction)
523     {
524         OUString aText(rText);
525         NativeItemText(aText);
526         pAction->setText(toQString(aText));
527     }
528 }
529 
SetItemImage(unsigned,SalMenuItem * pItem,const Image & rImage)530 void Qt5Menu::SetItemImage(unsigned, SalMenuItem* pItem, const Image& rImage)
531 {
532     Qt5MenuItem* pSalMenuItem = static_cast<Qt5MenuItem*>(pItem);
533 
534     // Save new image to use it in DoFullMenuUpdate
535     pSalMenuItem->maImage = rImage;
536 
537     QAction* pAction = pSalMenuItem->getAction();
538     if (!pAction)
539         return;
540 
541     pAction->setIcon(QPixmap::fromImage(toQImage(rImage)));
542 }
543 
SetAccelerator(unsigned,SalMenuItem * pItem,const vcl::KeyCode &,const OUString & rText)544 void Qt5Menu::SetAccelerator(unsigned, SalMenuItem* pItem, const vcl::KeyCode&,
545                              const OUString& rText)
546 {
547     Qt5MenuItem* pSalMenuItem = static_cast<Qt5MenuItem*>(pItem);
548     QAction* pAction = pSalMenuItem->getAction();
549     if (pAction)
550         pAction->setShortcut(QKeySequence(toQString(rText), QKeySequence::PortableText));
551 }
552 
GetSystemMenuData(SystemMenuData *)553 void Qt5Menu::GetSystemMenuData(SystemMenuData*) {}
554 
GetTopLevel()555 Qt5Menu* Qt5Menu::GetTopLevel()
556 {
557     Qt5Menu* pMenu = this;
558     while (pMenu->mpParentSalMenu)
559         pMenu = pMenu->mpParentSalMenu;
560     return pMenu;
561 }
562 
ShowMenuBar(bool bVisible)563 void Qt5Menu::ShowMenuBar(bool bVisible)
564 {
565     if (mpQMenuBar)
566         mpQMenuBar->setVisible(bVisible);
567 }
568 
GetFrame() const569 const Qt5Frame* Qt5Menu::GetFrame() const
570 {
571     SolarMutexGuard aGuard;
572     const Qt5Menu* pMenu = this;
573     while (pMenu && !pMenu->mpFrame)
574         pMenu = pMenu->mpParentSalMenu;
575     return pMenu ? pMenu->mpFrame : nullptr;
576 }
577 
slotMenuTriggered(Qt5MenuItem * pQItem)578 void Qt5Menu::slotMenuTriggered(Qt5MenuItem* pQItem)
579 {
580     if (pQItem)
581     {
582         Qt5Menu* pSalMenu = pQItem->mpParentMenu;
583         Qt5Menu* pTopLevel = pSalMenu->GetTopLevel();
584 
585         Menu* pMenu = pSalMenu->GetMenu();
586         auto mnId = pQItem->mnId;
587 
588         // HACK to allow HandleMenuCommandEvent to "not-set" the checked button
589         // LO expects a signal before an item state change, so reset the check item
590         if (pQItem->mpAction->isCheckable()
591             && (!pQItem->mpActionGroup || pQItem->mpActionGroup->actions().size() <= 1))
592             pQItem->mpAction->setChecked(!pQItem->mpAction->isChecked());
593         pTopLevel->GetMenu()->HandleMenuCommandEvent(pMenu, mnId);
594     }
595 }
596 
slotMenuAboutToShow(Qt5MenuItem * pQItem)597 void Qt5Menu::slotMenuAboutToShow(Qt5MenuItem* pQItem)
598 {
599     if (pQItem)
600     {
601         Qt5Menu* pSalMenu = pQItem->mpSubMenu;
602         Qt5Menu* pTopLevel = pSalMenu->GetTopLevel();
603 
604         Menu* pMenu = pSalMenu->GetMenu();
605 
606         // following function may update the menu
607         pTopLevel->GetMenu()->HandleMenuActivateEvent(pMenu);
608     }
609 }
610 
slotMenuAboutToHide(Qt5MenuItem * pQItem)611 void Qt5Menu::slotMenuAboutToHide(Qt5MenuItem* pQItem)
612 {
613     if (pQItem)
614     {
615         Qt5Menu* pSalMenu = pQItem->mpSubMenu;
616         Qt5Menu* pTopLevel = pSalMenu->GetTopLevel();
617 
618         Menu* pMenu = pSalMenu->GetMenu();
619 
620         pTopLevel->GetMenu()->HandleMenuDeActivateEvent(pMenu);
621     }
622 }
623 
NativeItemText(OUString & rItemText)624 void Qt5Menu::NativeItemText(OUString& rItemText)
625 {
626     // preserve literal '&'s in menu texts
627     rItemText = rItemText.replaceAll("&", "&&");
628 
629     rItemText = rItemText.replace('~', '&');
630 }
631 
slotCloseDocument()632 void Qt5Menu::slotCloseDocument()
633 {
634     MenuBar* pVclMenuBar = static_cast<MenuBar*>(mpVCLMenu.get());
635     if (pVclMenuBar)
636         Application::PostUserEvent(pVclMenuBar->GetCloseButtonClickHdl());
637 }
638 
ShowCloseButton(bool bShow)639 void Qt5Menu::ShowCloseButton(bool bShow)
640 {
641     if (!mpQMenuBar)
642         return;
643 
644     QPushButton* pButton = static_cast<QPushButton*>(mpQMenuBar->cornerWidget(Qt::TopRightCorner));
645     if (!pButton)
646     {
647         QIcon aIcon;
648         if (QIcon::hasThemeIcon("window-close-symbolic"))
649             aIcon = QIcon::fromTheme("window-close-symbolic");
650         else
651             aIcon = QIcon(
652                 QPixmap::fromImage((toQImage(Image(StockImage::Yes, SV_RESID_BITMAP_CLOSEDOC)))));
653         pButton = new QPushButton(mpQMenuBar);
654         pButton->setIcon(aIcon);
655         pButton->setFlat(true);
656         pButton->setFocusPolicy(Qt::NoFocus);
657         pButton->setToolTip(toQString(VclResId(SV_HELPTEXT_CLOSEDOCUMENT)));
658         mpQMenuBar->setCornerWidget(pButton, Qt::TopRightCorner);
659         maCloseButtonConnection
660             = connect(pButton, &QPushButton::clicked, this, &Qt5Menu::slotCloseDocument);
661         mpCloseButton = pButton;
662     }
663 
664     if (bShow)
665         pButton->show();
666     else
667         pButton->hide();
668 }
669 
ShowNativePopupMenu(FloatingWindow *,const tools::Rectangle &,FloatWinPopupFlags nFlags)670 bool Qt5Menu::ShowNativePopupMenu(FloatingWindow*, const tools::Rectangle&,
671                                   FloatWinPopupFlags nFlags)
672 {
673     assert(mpQMenu);
674     DoFullMenuUpdate(mpVCLMenu);
675     mpQMenu->setTearOffEnabled(bool(nFlags & FloatWinPopupFlags::AllowTearOff));
676 
677     const QPoint aPos = QCursor::pos();
678     mpQMenu->exec(aPos);
679 
680     return true;
681 }
682 
Qt5MenuItem(const SalItemParams * pItemData)683 Qt5MenuItem::Qt5MenuItem(const SalItemParams* pItemData)
684     : mpParentMenu(nullptr)
685     , mpSubMenu(nullptr)
686     , mnId(pItemData->nId)
687     , mnType(pItemData->eType)
688     , mbVisible(true)
689     , mbEnabled(true)
690     , maImage(pItemData->aImage)
691 {
692 }
693 
getAction() const694 QAction* Qt5MenuItem::getAction() const
695 {
696     if (mpMenu)
697         return mpMenu->menuAction();
698     if (mpAction)
699         return mpAction.get();
700     return nullptr;
701 }
702 
703 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
704