File indexing completed on 2024-12-01 12:40:38
0001 /* 0002 This file is part of the KDE project 0003 SPDX-FileCopyrightText: 2006 Olivier Goffart <ogoffart@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "kmenumenuhandler_p.h" 0009 0010 #include "debug.h" 0011 #include "kactioncollection.h" 0012 #include "kmainwindow.h" 0013 #include "kshortcutwidget.h" 0014 #include "ktoolbar.h" 0015 #include "kxmlguibuilder.h" 0016 #include "kxmlguiclient.h" 0017 #include "kxmlguifactory.h" 0018 0019 #include <QAction> 0020 #include <QContextMenuEvent> 0021 #include <QDialog> 0022 #include <QDialogButtonBox> 0023 #include <QDomDocument> 0024 #include <QDomNode> 0025 #include <QMenu> 0026 #include <QVBoxLayout> 0027 0028 #include <KLocalizedString> 0029 #include <KSelectAction> 0030 0031 namespace KDEPrivate 0032 { 0033 KMenuMenuHandler::KMenuMenuHandler(KXMLGUIBuilder *builder) 0034 : QObject() 0035 , m_builder(builder) 0036 { 0037 m_toolbarAction = new KSelectAction(i18n("Add to Toolbar"), this); 0038 connect(m_toolbarAction, &KSelectAction::indexTriggered, this, &KMenuMenuHandler::slotAddToToolBar); 0039 } 0040 0041 void KMenuMenuHandler::insertMenu(QMenu *popup) 0042 { 0043 popup->installEventFilter(this); 0044 } 0045 0046 bool KMenuMenuHandler::eventFilter(QObject *watched, QEvent *event) 0047 { 0048 switch (event->type()) { 0049 case QEvent::MouseButtonPress: 0050 if (m_contextMenu && m_contextMenu->isVisible()) { 0051 m_contextMenu->hide(); 0052 return true; 0053 } 0054 break; 0055 0056 case QEvent::MouseButtonRelease: 0057 if (m_contextMenu && m_contextMenu->isVisible()) { 0058 return true; 0059 } 0060 break; 0061 0062 case QEvent::ContextMenu: { 0063 QContextMenuEvent *e = static_cast<QContextMenuEvent *>(event); 0064 QMenu *menu = static_cast<QMenu *>(watched); 0065 if (e->reason() == QContextMenuEvent::Mouse) { 0066 showContextMenu(menu, e->pos()); 0067 } else if (menu->activeAction()) { 0068 showContextMenu(menu, menu->actionGeometry(menu->activeAction()).center()); 0069 } 0070 } 0071 event->accept(); 0072 return true; 0073 0074 default: 0075 break; 0076 } 0077 0078 return false; 0079 } 0080 0081 void KMenuMenuHandler::buildToolbarAction() 0082 { 0083 KMainWindow *window = qobject_cast<KMainWindow *>(m_builder->widget()); 0084 if (!window) { 0085 return; 0086 } 0087 QStringList toolbarlist; 0088 const auto toolbars = window->toolBars(); 0089 toolbarlist.reserve(toolbars.size()); 0090 for (KToolBar *b : toolbars) { 0091 toolbarlist << (b->windowTitle().isEmpty() ? b->objectName() : b->windowTitle()); 0092 } 0093 m_toolbarAction->setItems(toolbarlist); 0094 } 0095 0096 static KActionCollection *findParentCollection(KXMLGUIFactory *factory, QAction *action) 0097 { 0098 const auto clients = factory->clients(); 0099 for (KXMLGUIClient *client : clients) { 0100 KActionCollection *collection = client->actionCollection(); 0101 // if the call to actions() is too slow, add KActionCollection::contains(QAction*). 0102 if (collection->actions().contains(action)) { 0103 return collection; 0104 } 0105 } 0106 return nullptr; 0107 } 0108 0109 void KMenuMenuHandler::slotSetShortcut() 0110 { 0111 if (!m_popupMenu || !m_popupAction) { 0112 return; 0113 } 0114 0115 QDialog dialog(m_builder->widget()); 0116 auto *layout = new QVBoxLayout(&dialog); 0117 0118 KShortcutWidget swidget(&dialog); 0119 swidget.setShortcut(m_popupAction->shortcuts()); 0120 layout->addWidget(&swidget); 0121 0122 QDialogButtonBox box(&dialog); 0123 box.setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); 0124 connect(&box, &QDialogButtonBox::accepted, &dialog, &QDialog::accept); 0125 connect(&box, &QDialogButtonBox::rejected, &dialog, &QDialog::reject); 0126 layout->addWidget(&box); 0127 0128 KActionCollection *parentCollection = nullptr; 0129 if (dynamic_cast<KXMLGUIClient *>(m_builder)) { 0130 QList<KActionCollection *> checkCollections; 0131 KXMLGUIFactory *factory = dynamic_cast<KXMLGUIClient *>(m_builder)->factory(); 0132 parentCollection = findParentCollection(factory, m_popupAction); 0133 const auto clients = factory->clients(); 0134 checkCollections.reserve(clients.size()); 0135 for (KXMLGUIClient *client : clients) { 0136 checkCollections += client->actionCollection(); 0137 } 0138 swidget.setCheckActionCollections(checkCollections); 0139 } 0140 0141 if (dialog.exec()) { 0142 m_popupAction->setShortcuts(swidget.shortcut()); 0143 swidget.applyStealShortcut(); 0144 if (parentCollection) { 0145 parentCollection->writeSettings(); 0146 } 0147 } 0148 } 0149 0150 void KMenuMenuHandler::slotAddToToolBar(int tb) 0151 { 0152 KMainWindow *window = qobject_cast<KMainWindow *>(m_builder->widget()); 0153 if (!window) { 0154 return; 0155 } 0156 0157 if (!m_popupMenu || !m_popupAction) { 0158 return; 0159 } 0160 0161 KXMLGUIFactory *factory = dynamic_cast<KXMLGUIClient *>(m_builder)->factory(); 0162 QString actionName = m_popupAction->objectName(); // set by KActionCollection::addAction 0163 KActionCollection *collection = nullptr; 0164 if (factory) { 0165 collection = findParentCollection(factory, m_popupAction); 0166 } 0167 if (!collection) { 0168 qCWarning(DEBUG_KXMLGUI) << "Cannot find the action collection for action " << actionName; 0169 return; 0170 } 0171 0172 KToolBar *toolbar = window->toolBars().at(tb); 0173 toolbar->addAction(m_popupAction); 0174 0175 const KXMLGUIClient *client = collection->parentGUIClient(); 0176 QString xmlFile = client->localXMLFile(); 0177 QDomDocument document; 0178 document.setContent(KXMLGUIFactory::readConfigFile(client->xmlFile(), client->componentName())); 0179 QDomElement elem = document.documentElement().toElement(); 0180 0181 const QLatin1String tagToolBar("ToolBar"); 0182 const QLatin1String attrNoEdit("noEdit"); 0183 const QLatin1String attrName("name"); 0184 0185 QDomElement toolbarElem; 0186 QDomNode n = elem.firstChild(); 0187 for (; !n.isNull(); n = n.nextSibling()) { 0188 QDomElement elem = n.toElement(); 0189 if (!elem.isNull() && elem.tagName() == tagToolBar && elem.attribute(attrName) == toolbar->objectName()) { 0190 if (elem.attribute(attrNoEdit) == QLatin1String("true")) { 0191 qCWarning(DEBUG_KXMLGUI) << "The toolbar is not editable"; 0192 return; 0193 } 0194 toolbarElem = elem; 0195 break; 0196 } 0197 } 0198 if (toolbarElem.isNull()) { 0199 toolbarElem = document.createElement(tagToolBar); 0200 toolbarElem.setAttribute(attrName, toolbar->objectName()); 0201 elem.appendChild(toolbarElem); 0202 } 0203 0204 KXMLGUIFactory::findActionByName(toolbarElem, actionName, true); 0205 KXMLGUIFactory::saveConfigFile(document, xmlFile); 0206 } 0207 0208 void KMenuMenuHandler::showContextMenu(QMenu *menu, const QPoint &pos) 0209 { 0210 Q_ASSERT(!m_popupMenu); 0211 Q_ASSERT(!m_popupAction); 0212 Q_ASSERT(!m_contextMenu); 0213 0214 auto *action = menu->actionAt(pos); 0215 if (!action || action->isSeparator()) { 0216 return; 0217 } 0218 0219 m_popupMenu = menu; 0220 m_popupAction = action; 0221 0222 m_contextMenu = new QMenu; 0223 m_contextMenu->addAction(i18nc("@action:inmenu", "Configure Shortcut..."), this, &KMenuMenuHandler::slotSetShortcut); 0224 0225 KMainWindow *window = qobject_cast<KMainWindow *>(m_builder->widget()); 0226 if (window) { 0227 m_contextMenu->addAction(m_toolbarAction); 0228 buildToolbarAction(); 0229 } 0230 0231 m_contextMenu->exec(menu->mapToGlobal(pos)); 0232 delete m_contextMenu; 0233 m_contextMenu = nullptr; 0234 0235 m_popupAction = nullptr; 0236 m_popupMenu = nullptr; 0237 } 0238 0239 } // END namespace KDEPrivate 0240 0241 #include "moc_kmenumenuhandler_p.cpp"