/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include #include #include #include #include #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) #include #else #include #endif #include #include #include #include #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) #include #else #include #endif #include #include #include #include #include #include #include #include // LO SalMenuButtonItem::mnId is sal_uInt16, so we go with -2, as -1 has a special meaning as automatic id constexpr int CLOSE_BUTTON_ID = -2; const QString gButtonGroupKey("QtMenu::ButtonGroup"); static inline void lcl_force_menubar_layout_update(QMenuBar& rMenuBar) { // just exists as a function to not comment it everywhere: forces reposition of the // corner widget after its layout changes, which will otherwise just happen on resize. // it unfortunatly has additional side effects; see QtMenu::GetMenuBarButtonRectPixel. rMenuBar.adjustSize(); } OUString QtMenu::m_sCurrentHelpId = u""_ustr; QtMenu::QtMenu(bool bMenuBar) : mpVCLMenu(nullptr) , mpParentSalMenu(nullptr) , mpFrame(nullptr) , mbMenuBar(bMenuBar) , mpQMenuBar(nullptr) , mpQMenu(nullptr) , m_pButtonGroup(nullptr) { } bool QtMenu::VisibleMenuBar() { return true; } void QtMenu::InsertMenuItem(QtMenuItem* pSalMenuItem, unsigned nPos) { sal_uInt16 nId = pSalMenuItem->mnId; const QString aText = vclToQtStringWithAccelerator(mpVCLMenu->GetItemText(nId)); vcl::KeyCode nAccelKey = mpVCLMenu->GetAccelKey(nId); pSalMenuItem->mpAction.reset(); pSalMenuItem->mpMenu.reset(); if (mbMenuBar) { // top-level menu if (validateQMenuBar()) { QMenu* pQMenu = new QMenu(aText, nullptr); connectHelpSignalSlots(pQMenu, pSalMenuItem); pSalMenuItem->mpMenu.reset(pQMenu); if ((nPos != MENU_APPEND) && (static_cast(nPos) < o3tl::make_unsigned(mpQMenuBar->actions().size()))) { mpQMenuBar->insertMenu(mpQMenuBar->actions()[nPos], pQMenu); } else { mpQMenuBar->addMenu(pQMenu); } // correct parent menu for generated menu if (pSalMenuItem->mpSubMenu) { pSalMenuItem->mpSubMenu->mpQMenu = pQMenu; } connect(pQMenu, &QMenu::aboutToShow, this, [pSalMenuItem] { slotMenuAboutToShow(pSalMenuItem); }); connect(pQMenu, &QMenu::aboutToHide, this, [pSalMenuItem] { slotMenuAboutToHide(pSalMenuItem); }); } } else { if (!mpQMenu) { // no QMenu set, instantiate own one mpOwnedQMenu.reset(new QMenu); mpQMenu = mpOwnedQMenu.get(); connectHelpSignalSlots(mpQMenu, pSalMenuItem); } if (pSalMenuItem->mpSubMenu) { // submenu QMenu* pQMenu = new QMenu(aText, nullptr); connectHelpSignalSlots(pQMenu, pSalMenuItem); pSalMenuItem->mpMenu.reset(pQMenu); if ((nPos != MENU_APPEND) && (static_cast(nPos) < o3tl::make_unsigned(mpQMenu->actions().size()))) { mpQMenu->insertMenu(mpQMenu->actions()[nPos], pQMenu); } else { mpQMenu->addMenu(pQMenu); } // correct parent menu for generated menu pSalMenuItem->mpSubMenu->mpQMenu = pQMenu; ReinitializeActionGroup(nPos); // clear all action groups since menu is recreated pSalMenuItem->mpSubMenu->ResetAllActionGroups(); connect(pQMenu, &QMenu::aboutToShow, this, [pSalMenuItem] { slotMenuAboutToShow(pSalMenuItem); }); connect(pQMenu, &QMenu::aboutToHide, this, [pSalMenuItem] { slotMenuAboutToHide(pSalMenuItem); }); } else { if (pSalMenuItem->mnType == MenuItemType::SEPARATOR) { QAction* pAction = new QAction(nullptr); pSalMenuItem->mpAction.reset(pAction); pAction->setSeparator(true); if ((nPos != MENU_APPEND) && (static_cast(nPos) < o3tl::make_unsigned(mpQMenu->actions().size()))) { mpQMenu->insertAction(mpQMenu->actions()[nPos], pAction); } else { mpQMenu->addAction(pAction); } ReinitializeActionGroup(nPos); } else { // leaf menu QAction* pAction = new QAction(aText, nullptr); pSalMenuItem->mpAction.reset(pAction); if ((nPos != MENU_APPEND) && (static_cast(nPos) < o3tl::make_unsigned(mpQMenu->actions().size()))) { mpQMenu->insertAction(mpQMenu->actions()[nPos], pAction); } else { mpQMenu->addAction(pAction); } ReinitializeActionGroup(nPos); UpdateActionGroupItem(pSalMenuItem); pAction->setShortcut(toQString(nAccelKey.GetName())); connect(pAction, &QAction::triggered, this, [pSalMenuItem] { slotMenuTriggered(pSalMenuItem); }); connect(pAction, &QAction::hovered, this, [pSalMenuItem] { slotMenuHovered(pSalMenuItem); }); } } } QAction* pAction = pSalMenuItem->getAction(); if (pAction) { pAction->setEnabled(pSalMenuItem->mbEnabled); pAction->setVisible(pSalMenuItem->mbVisible); } } void QtMenu::ReinitializeActionGroup(unsigned nPos) { const unsigned nCount = GetItemCount(); if (nCount == 0) { return; } if (nPos == MENU_APPEND) { nPos = nCount - 1; } else if (nPos >= nCount) { return; } QtMenuItem* pPrevItem = (nPos > 0) ? GetItemAtPos(nPos - 1) : nullptr; QtMenuItem* pCurrentItem = GetItemAtPos(nPos); QtMenuItem* pNextItem = (nPos < nCount - 1) ? GetItemAtPos(nPos + 1) : nullptr; if (pCurrentItem->mnType == MenuItemType::SEPARATOR) { pCurrentItem->mpActionGroup.reset(); // if it's inserted into middle of existing group, split it into two groups: // first goes original group, after separator goes new group if (pPrevItem && pPrevItem->mpActionGroup && pNextItem && pNextItem->mpActionGroup && (pPrevItem->mpActionGroup == pNextItem->mpActionGroup)) { std::shared_ptr pFirstActionGroup = pPrevItem->mpActionGroup; auto pSecondActionGroup = std::make_shared(nullptr); pSecondActionGroup->setExclusive(true); auto actions = pFirstActionGroup->actions(); for (unsigned idx = nPos + 1; idx < nCount; ++idx) { QtMenuItem* pModifiedItem = GetItemAtPos(idx); if ((!pModifiedItem) || (!pModifiedItem->mpActionGroup)) { break; } pModifiedItem->mpActionGroup = pSecondActionGroup; auto action = pModifiedItem->getAction(); if (actions.contains(action)) { pFirstActionGroup->removeAction(action); pSecondActionGroup->addAction(action); } } } } else { if (!pCurrentItem->mpActionGroup) { // unless element is inserted between two separators, or a separator and an end of vector, use neighbouring group since it's shared if (pPrevItem && pPrevItem->mpActionGroup) { pCurrentItem->mpActionGroup = pPrevItem->mpActionGroup; } else if (pNextItem && pNextItem->mpActionGroup) { pCurrentItem->mpActionGroup = pNextItem->mpActionGroup; } else { pCurrentItem->mpActionGroup = std::make_shared(nullptr); pCurrentItem->mpActionGroup->setExclusive(true); } } // if there's also a different group after this element, merge it if (pNextItem && pNextItem->mpActionGroup && (pCurrentItem->mpActionGroup != pNextItem->mpActionGroup)) { auto pFirstCheckedAction = pCurrentItem->mpActionGroup->checkedAction(); auto pSecondCheckedAction = pNextItem->mpActionGroup->checkedAction(); auto actions = pNextItem->mpActionGroup->actions(); // first move all actions from second group to first one, and if first group already has checked action, // and second group also has a checked action, uncheck action from second group for (auto action : actions) { pNextItem->mpActionGroup->removeAction(action); if (pFirstCheckedAction && pSecondCheckedAction && (action == pSecondCheckedAction)) { action->setChecked(false); } pCurrentItem->mpActionGroup->addAction(action); } // now replace all pointers to second group with pointers to first group for (unsigned idx = nPos + 1; idx < nCount; ++idx) { QtMenuItem* pModifiedItem = GetItemAtPos(idx); if ((!pModifiedItem) || (!pModifiedItem->mpActionGroup)) { break; } pModifiedItem->mpActionGroup = pCurrentItem->mpActionGroup; } } } } void QtMenu::ResetAllActionGroups() { for (unsigned nItem = 0; nItem < GetItemCount(); ++nItem) { QtMenuItem* pSalMenuItem = GetItemAtPos(nItem); pSalMenuItem->mpActionGroup.reset(); } } void QtMenu::UpdateActionGroupItem(const QtMenuItem* pSalMenuItem) { QAction* pAction = pSalMenuItem->getAction(); if (!pAction) return; bool bChecked = mpVCLMenu->IsItemChecked(pSalMenuItem->mnId); MenuItemBits itemBits = mpVCLMenu->GetItemBits(pSalMenuItem->mnId); if (itemBits & MenuItemBits::RADIOCHECK) { pAction->setCheckable(true); if (pSalMenuItem->mpActionGroup) { pSalMenuItem->mpActionGroup->addAction(pAction); } pAction->setChecked(bChecked); } else { pAction->setActionGroup(nullptr); if (itemBits & MenuItemBits::CHECKABLE) { pAction->setCheckable(true); pAction->setChecked(bChecked); } else { pAction->setChecked(false); pAction->setCheckable(false); } } } void QtMenu::InsertItem(SalMenuItem* pSalMenuItem, unsigned nPos) { SolarMutexGuard aGuard; QtMenuItem* pItem = static_cast(pSalMenuItem); if (nPos == MENU_APPEND) maItems.push_back(pItem); else maItems.insert(maItems.begin() + nPos, pItem); pItem->mpParentMenu = this; InsertMenuItem(pItem, nPos); } void QtMenu::RemoveItem(unsigned nPos) { SolarMutexGuard aGuard; if (nPos >= maItems.size()) return; QtMenuItem* pItem = maItems[nPos]; pItem->mpAction.reset(); pItem->mpMenu.reset(); maItems.erase(maItems.begin() + nPos); // Recalculate action groups if necessary: // if separator between two QActionGroups was removed, // it may be needed to merge them if (nPos > 0) { ReinitializeActionGroup(nPos - 1); } } void QtMenu::SetSubMenu(SalMenuItem* pSalMenuItem, SalMenu* pSubMenu, unsigned nPos) { SolarMutexGuard aGuard; QtMenuItem* pItem = static_cast(pSalMenuItem); QtMenu* pQSubMenu = static_cast(pSubMenu); pItem->mpSubMenu = pQSubMenu; // at this point the pointer to parent menu may be outdated, update it too pItem->mpParentMenu = this; if (pQSubMenu != nullptr) { pQSubMenu->mpParentSalMenu = this; pQSubMenu->mpQMenu = pItem->mpMenu.get(); } // if it's not a menu bar item, then convert it to corresponding item if type if necessary. // If submenu is present and it's an action, convert it to menu. // If submenu is not present and it's a menu, convert it to action. // It may be fine to proceed in any case, but by skipping other cases // amount of unneeded actions taken should be reduced. if (pItem->mpParentMenu->mbMenuBar || (pQSubMenu && pItem->mpMenu) || ((!pQSubMenu) && pItem->mpAction)) { return; } InsertMenuItem(pItem, nPos); } void QtMenu::SetFrame(const SalFrame* pFrame) { auto* pSalInst(GetQtInstance()); assert(pSalInst); if (!pSalInst->IsMainThread()) { pSalInst->RunInMainThread([this, pFrame]() { SetFrame(pFrame); }); return; } SolarMutexGuard aGuard; assert(mbMenuBar); mpFrame = const_cast(static_cast(pFrame)); mpFrame->SetMenu(this); QtMainWindow* pMainWindow = mpFrame->GetTopLevelWindow(); if (!pMainWindow) return; mpQMenuBar = new QMenuBar(); pMainWindow->setMenuBar(mpQMenuBar); QWidget* pWidget = mpQMenuBar->cornerWidget(Qt::TopRightCorner); if (pWidget) { m_pButtonGroup = pWidget->findChild(gButtonGroupKey); assert(m_pButtonGroup); connect(m_pButtonGroup, QOverload::of(&QButtonGroup::buttonClicked), this, &QtMenu::slotMenuBarButtonClicked); QPushButton* pButton = static_cast(m_pButtonGroup->button(CLOSE_BUTTON_ID)); if (pButton) connect(pButton, &QPushButton::clicked, this, &QtMenu::slotCloseDocument); } else m_pButtonGroup = nullptr; mpQMenu = nullptr; DoFullMenuUpdate(mpVCLMenu); } void QtMenu::DoFullMenuUpdate(Menu* pMenuBar) { // clear action groups since menu is rebuilt ResetAllActionGroups(); ShowCloseButton(false); for (sal_Int32 nItem = 0; nItem < static_cast(GetItemCount()); nItem++) { QtMenuItem* pSalMenuItem = GetItemAtPos(nItem); InsertMenuItem(pSalMenuItem, nItem); SetItemImage(nItem, pSalMenuItem, pSalMenuItem->maImage); const bool bShowDisabled = bool(pMenuBar->GetMenuFlags() & MenuFlags::AlwaysShowDisabledEntries) || !bool(pMenuBar->GetMenuFlags() & MenuFlags::HideDisabledEntries); const bool bVisible = pSalMenuItem->mbVisible && (bShowDisabled || mpVCLMenu->IsItemEnabled(pSalMenuItem->mnId)); pSalMenuItem->getAction()->setVisible(bVisible); if (pSalMenuItem->mpSubMenu != nullptr) { pMenuBar->HandleMenuActivateEvent(pSalMenuItem->mpSubMenu->GetMenu()); pSalMenuItem->mpSubMenu->DoFullMenuUpdate(pMenuBar); pMenuBar->HandleMenuDeActivateEvent(pSalMenuItem->mpSubMenu->GetMenu()); } } } void QtMenu::ShowItem(unsigned nPos, bool bShow) { if (nPos < maItems.size()) { QtMenuItem* pSalMenuItem = GetItemAtPos(nPos); QAction* pAction = pSalMenuItem->getAction(); if (pAction) pAction->setVisible(bShow); pSalMenuItem->mbVisible = bShow; } } void QtMenu::SetItemBits(unsigned nPos, MenuItemBits) { if (nPos < maItems.size()) { QtMenuItem* pSalMenuItem = GetItemAtPos(nPos); UpdateActionGroupItem(pSalMenuItem); } } void QtMenu::CheckItem(unsigned nPos, bool bChecked) { if (nPos < maItems.size()) { QtMenuItem* pSalMenuItem = GetItemAtPos(nPos); QAction* pAction = pSalMenuItem->getAction(); if (pAction) { pAction->setCheckable(true); pAction->setChecked(bChecked); } } } void QtMenu::EnableItem(unsigned nPos, bool bEnable) { if (nPos < maItems.size()) { QtMenuItem* pSalMenuItem = GetItemAtPos(nPos); QAction* pAction = pSalMenuItem->getAction(); if (pAction) pAction->setEnabled(bEnable); pSalMenuItem->mbEnabled = bEnable; } } void QtMenu::SetItemText(unsigned, SalMenuItem* pItem, const OUString& rText) { QtMenuItem* pSalMenuItem = static_cast(pItem); QAction* pAction = pSalMenuItem->getAction(); if (pAction) { pAction->setText(vclToQtStringWithAccelerator(rText)); } } void QtMenu::SetItemImage(unsigned, SalMenuItem* pItem, const Image& rImage) { QtMenuItem* pSalMenuItem = static_cast(pItem); // Save new image to use it in DoFullMenuUpdate pSalMenuItem->maImage = rImage; QAction* pAction = pSalMenuItem->getAction(); if (!pAction) return; pAction->setIcon(QPixmap::fromImage(toQImage(rImage))); } void QtMenu::SetAccelerator(unsigned, SalMenuItem* pItem, const vcl::KeyCode&, const OUString& rText) { QtMenuItem* pSalMenuItem = static_cast(pItem); QAction* pAction = pSalMenuItem->getAction(); if (pAction) pAction->setShortcut(QKeySequence(toQString(rText), QKeySequence::PortableText)); } void QtMenu::GetSystemMenuData(SystemMenuData*) {} QtMenu* QtMenu::GetTopLevel() { QtMenu* pMenu = this; while (pMenu->mpParentSalMenu) pMenu = pMenu->mpParentSalMenu; return pMenu; } bool QtMenu::validateQMenuBar() const { if (!mpQMenuBar) return false; assert(mpFrame); QtMainWindow* pMainWindow = mpFrame->GetTopLevelWindow(); assert(pMainWindow); const bool bValid = mpQMenuBar == pMainWindow->menuBar(); if (!bValid) { QtMenu* thisPtr = const_cast(this); thisPtr->mpQMenuBar = nullptr; } return bValid; } void QtMenu::ShowMenuBar(bool bVisible) { if (!validateQMenuBar()) return; mpQMenuBar->setVisible(bVisible); if (bVisible) lcl_force_menubar_layout_update(*mpQMenuBar); } void QtMenu::slotMenuHovered(QtMenuItem* pItem) { const OUString sHelpId = pItem->mpParentMenu->GetMenu()->GetHelpId(pItem->mnId); m_sCurrentHelpId = sHelpId; } void QtMenu::slotShowHelp() { SolarMutexGuard aGuard; Help* pHelp = Application::GetHelp(); if (pHelp && !m_sCurrentHelpId.isEmpty()) { pHelp->Start(m_sCurrentHelpId); } } void QtMenu::slotMenuTriggered(QtMenuItem* pQItem) { if (!pQItem) return; QtMenu* pSalMenu = pQItem->mpParentMenu; QtMenu* pTopLevel = pSalMenu->GetTopLevel(); Menu* pMenu = pSalMenu->GetMenu(); auto mnId = pQItem->mnId; // HACK to allow HandleMenuCommandEvent to "not-set" the checked button // LO expects a signal before an item state change, so reset the check item if (pQItem->mpAction->isCheckable() && (!pQItem->mpActionGroup || pQItem->mpActionGroup->actions().size() <= 1)) pQItem->mpAction->setChecked(!pQItem->mpAction->isChecked()); pTopLevel->GetMenu()->HandleMenuCommandEvent(pMenu, mnId); } void QtMenu::slotMenuAboutToShow(QtMenuItem* pQItem) { if (pQItem) { QtMenu* pSalMenu = pQItem->mpSubMenu; QtMenu* pTopLevel = pSalMenu->GetTopLevel(); Menu* pMenu = pSalMenu->GetMenu(); // following function may update the menu pTopLevel->GetMenu()->HandleMenuActivateEvent(pMenu); } } void QtMenu::slotMenuAboutToHide(QtMenuItem* pQItem) { if (pQItem) { QtMenu* pSalMenu = pQItem->mpSubMenu; QtMenu* pTopLevel = pSalMenu->GetTopLevel(); Menu* pMenu = pSalMenu->GetMenu(); pTopLevel->GetMenu()->HandleMenuDeActivateEvent(pMenu); } } void QtMenu::slotCloseDocument() { MenuBar* pVclMenuBar = static_cast(mpVCLMenu.get()); if (pVclMenuBar) Application::PostUserEvent(pVclMenuBar->GetCloseButtonClickHdl()); } void QtMenu::slotMenuBarButtonClicked(QAbstractButton* pButton) { MenuBar* pVclMenuBar = static_cast(mpVCLMenu.get()); if (pVclMenuBar) { SolarMutexGuard aGuard; pVclMenuBar->HandleMenuButtonEvent(m_pButtonGroup->id(pButton)); } } QPushButton* QtMenu::ImplAddMenuBarButton(const QIcon& rIcon, const QString& rToolTip, int nId) { if (!validateQMenuBar()) return nullptr; QWidget* pWidget = mpQMenuBar->cornerWidget(Qt::TopRightCorner); QHBoxLayout* pLayout; if (!pWidget) { assert(!m_pButtonGroup); pWidget = new QWidget(mpQMenuBar); assert(!pWidget->layout()); pLayout = new QHBoxLayout(); pLayout->setContentsMargins(QMargins()); pLayout->setSpacing(0); pWidget->setLayout(pLayout); m_pButtonGroup = new QButtonGroup(pLayout); m_pButtonGroup->setObjectName(gButtonGroupKey); m_pButtonGroup->setExclusive(false); connect(m_pButtonGroup, QOverload::of(&QButtonGroup::buttonClicked), this, &QtMenu::slotMenuBarButtonClicked); pWidget->show(); mpQMenuBar->setCornerWidget(pWidget, Qt::TopRightCorner); } else pLayout = static_cast(pWidget->layout()); assert(m_pButtonGroup); assert(pLayout); QPushButton* pButton = static_cast(m_pButtonGroup->button(nId)); if (pButton) ImplRemoveMenuBarButton(nId); pButton = new QPushButton(); // we don't want the button to increase the QMenuBar height, so a fixed size square it is const int nFixedLength = mpQMenuBar->height() - 2 * mpQMenuBar->style()->pixelMetric(QStyle::PM_MenuBarVMargin); pButton->setFixedSize(nFixedLength, nFixedLength); pButton->setIcon(rIcon); pButton->setFlat(true); pButton->setFocusPolicy(Qt::NoFocus); pButton->setToolTip(rToolTip); m_pButtonGroup->addButton(pButton, nId); int nPos = pLayout->count(); if (m_pButtonGroup->button(CLOSE_BUTTON_ID)) nPos--; pLayout->insertWidget(nPos, pButton, 0, Qt::AlignCenter); // show must happen after adding the button to the layout, otherwise the button is // shown, but not correct in the layout, if at all! Some times the layout ignores it. pButton->show(); lcl_force_menubar_layout_update(*mpQMenuBar); return pButton; } bool QtMenu::AddMenuBarButton(const SalMenuButtonItem& rItem) { if (!validateQMenuBar()) return false; return !!ImplAddMenuBarButton(QIcon(QPixmap::fromImage(toQImage(rItem.maImage))), toQString(rItem.maToolTipText), rItem.mnId); } void QtMenu::ImplRemoveMenuBarButton(int nId) { if (!validateQMenuBar()) return; assert(m_pButtonGroup); auto* pButton = m_pButtonGroup->button(nId); assert(pButton); QWidget* pWidget = mpQMenuBar->cornerWidget(Qt::TopRightCorner); assert(pWidget); QLayout* pLayout = pWidget->layout(); m_pButtonGroup->removeButton(pButton); pLayout->removeWidget(pButton); delete pButton; lcl_force_menubar_layout_update(*mpQMenuBar); } void QtMenu::connectHelpShortcut(QMenu* pMenu) { assert(pMenu); QKeySequence sequence(QKeySequence::HelpContents); QShortcut* pQShortcut = new QShortcut(sequence, pMenu); connect(pQShortcut, &QShortcut::activated, this, QtMenu::slotShowHelp); connect(pQShortcut, &QShortcut::activatedAmbiguously, this, QtMenu::slotShowHelp); } void QtMenu::connectHelpSignalSlots(QMenu* pMenu, QtMenuItem* pSalMenuItem) { // connect hovered signal of the menu's own action QAction* pAction = pMenu->menuAction(); assert(pAction); connect(pAction, &QAction::hovered, this, [pSalMenuItem] { slotMenuHovered(pSalMenuItem); }); // connect slot to handle Help key (F1) connectHelpShortcut(pMenu); } void QtMenu::RemoveMenuBarButton(sal_uInt16 nId) { ImplRemoveMenuBarButton(nId); } tools::Rectangle QtMenu::GetMenuBarButtonRectPixel(sal_uInt16 nId, SalFrame* pFrame) { #ifdef NDEBUG Q_UNUSED(pFrame); #endif if (!validateQMenuBar()) return tools::Rectangle(); assert(mpFrame == static_cast(pFrame)); assert(m_pButtonGroup); auto* pButton = static_cast(m_pButtonGroup->button(nId)); assert(pButton); // unfortunatly, calling lcl_force_menubar_layout_update results in a temporary wrong menubar size, // but it's the correct minimal size AFAIK and the layout seems correct, so just adjust the width. QPoint aPos = pButton->mapTo(mpFrame->asChild(), QPoint()); aPos.rx() += (mpFrame->asChild()->width() - mpQMenuBar->width()); return tools::Rectangle(toPoint(aPos), toSize(pButton->size())); } void QtMenu::ShowCloseButton(bool bShow) { if (!validateQMenuBar()) return; if (!bShow && !m_pButtonGroup) return; QPushButton* pButton = nullptr; if (m_pButtonGroup) pButton = static_cast(m_pButtonGroup->button(CLOSE_BUTTON_ID)); if (!bShow && !pButton) return; if (!pButton) { QIcon aIcon; if (QIcon::hasThemeIcon("window-close-symbolic")) aIcon = QIcon::fromTheme("window-close-symbolic"); else aIcon = QIcon( QPixmap::fromImage(toQImage(Image(StockImage::Yes, SV_RESID_BITMAP_CLOSEDOC)))); pButton = ImplAddMenuBarButton(aIcon, toQString(VclResId(SV_HELPTEXT_CLOSEDOCUMENT)), CLOSE_BUTTON_ID); connect(pButton, &QPushButton::clicked, this, &QtMenu::slotCloseDocument); } if (bShow) pButton->show(); else pButton->hide(); lcl_force_menubar_layout_update(*mpQMenuBar); } bool QtMenu::ShowNativePopupMenu(FloatingWindow* pWin, const tools::Rectangle& rRect, FloatWinPopupFlags nFlags) { assert(mpQMenu); DoFullMenuUpdate(mpVCLMenu); mpQMenu->setTearOffEnabled(bool(nFlags & FloatWinPopupFlags::AllowTearOff)); const VclPtr xParent = pWin->ImplGetWindowImpl()->mpRealParent; AbsoluteScreenPixelRectangle aFloatRect = FloatingWindow::ImplConvertToAbsPos(xParent, rRect); // tdf#154447 Menu bar height has to be added QtFrame* pFrame = static_cast(pWin->ImplGetFrame()); assert(pFrame); aFloatRect.SetPosY(aFloatRect.getY() + pFrame->menuBarOffset()); const QRect aRect = toQRect(aFloatRect, 1 / pFrame->devicePixelRatioF()); mpQMenu->exec(aRect.bottomLeft()); return true; } int QtMenu::GetMenuBarHeight() const { if (!validateQMenuBar() || mpQMenuBar->isHidden()) return 0; return mpQMenuBar->height(); } QtMenuItem::QtMenuItem(const SalItemParams* pItemData) : mpParentMenu(nullptr) , mpSubMenu(nullptr) , mnId(pItemData->nId) , mnType(pItemData->eType) , mbVisible(true) , mbEnabled(true) , maImage(pItemData->aImage) { } QAction* QtMenuItem::getAction() const { if (mpMenu) return mpMenu->menuAction(); if (mpAction) return mpAction.get(); return nullptr; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */