File indexing completed on 2024-09-15 12:04:29
0001 /* 0002 This file is part of the KDE project 0003 SPDX-FileCopyrightText: 2000 Simon Hausmann <hausmann@kde.org> 0004 SPDX-FileCopyrightText: 2000 David Faure <faure@kde.org> 0005 0006 SPDX-License-Identifier: LGPL-2.0-or-later 0007 */ 0008 0009 #include "kxmlguibuilder.h" 0010 0011 #include "debug.h" 0012 #include "kmainwindow.h" 0013 #include "kmenumenuhandler_p.h" 0014 #include "ktoolbar.h" 0015 #include "kxmlguiclient.h" 0016 #include "kxmlguiwindow.h" 0017 0018 #include <KAuthorized> 0019 #include <KLocalizedString> 0020 0021 #include <QAction> 0022 #include <QDomElement> 0023 #include <QMenu> 0024 #include <QMenuBar> 0025 #include <QObject> 0026 #include <QStatusBar> 0027 0028 using namespace KDEPrivate; 0029 0030 class KXMLGUIBuilderPrivate 0031 { 0032 public: 0033 KXMLGUIBuilderPrivate() 0034 { 0035 } 0036 ~KXMLGUIBuilderPrivate() 0037 { 0038 } 0039 0040 QWidget *m_widget = nullptr; 0041 0042 QString tagMainWindow; 0043 QString tagMenuBar; 0044 QString tagMenu; 0045 QString tagToolBar; 0046 QString tagStatusBar; 0047 0048 QString tagSeparator; 0049 QString tagSpacer; 0050 QString tagTearOffHandle; 0051 QString tagMenuTitle; 0052 0053 QString attrName; 0054 QString attrLineSeparator; 0055 0056 QString attrDomain; 0057 QString attrText1; 0058 QString attrText2; 0059 QString attrContext; 0060 0061 QString attrIcon; 0062 0063 KXMLGUIClient *m_client = nullptr; 0064 0065 KMenuMenuHandler *m_menumenuhandler = nullptr; 0066 }; 0067 0068 KXMLGUIBuilder::KXMLGUIBuilder(QWidget *widget) 0069 : d(new KXMLGUIBuilderPrivate) 0070 { 0071 d->m_widget = widget; 0072 0073 d->tagMainWindow = QStringLiteral("mainwindow"); 0074 d->tagMenuBar = QStringLiteral("menubar"); 0075 d->tagMenu = QStringLiteral("menu"); 0076 d->tagToolBar = QStringLiteral("toolbar"); 0077 d->tagStatusBar = QStringLiteral("statusbar"); 0078 0079 d->tagSeparator = QStringLiteral("separator"); 0080 d->tagSpacer = QStringLiteral("spacer"); 0081 d->tagTearOffHandle = QStringLiteral("tearoffhandle"); 0082 d->tagMenuTitle = QStringLiteral("title"); 0083 0084 d->attrName = QStringLiteral("name"); 0085 d->attrLineSeparator = QStringLiteral("lineseparator"); 0086 0087 d->attrDomain = QStringLiteral("translationDomain"); 0088 d->attrText1 = QStringLiteral("text"); 0089 d->attrText2 = QStringLiteral("Text"); 0090 d->attrContext = QStringLiteral("context"); 0091 0092 d->attrIcon = QStringLiteral("icon"); 0093 0094 d->m_menumenuhandler = new KMenuMenuHandler(this); 0095 } 0096 0097 KXMLGUIBuilder::~KXMLGUIBuilder() 0098 { 0099 delete d->m_menumenuhandler; 0100 } 0101 0102 QWidget *KXMLGUIBuilder::widget() 0103 { 0104 return d->m_widget; 0105 } 0106 0107 QStringList KXMLGUIBuilder::containerTags() const 0108 { 0109 QStringList res; 0110 res << d->tagMenu << d->tagToolBar << d->tagMainWindow << d->tagMenuBar << d->tagStatusBar; 0111 0112 return res; 0113 } 0114 0115 QWidget *KXMLGUIBuilder::createContainer(QWidget *parent, int index, const QDomElement &element, QAction *&containerAction) 0116 { 0117 containerAction = nullptr; 0118 0119 if (element.attribute(QStringLiteral("deleted")).toLower() == QLatin1String("true")) { 0120 return nullptr; 0121 } 0122 0123 const QString tagName = element.tagName().toLower(); 0124 if (tagName == d->tagMainWindow) { 0125 KMainWindow *mainwindow = qobject_cast<KMainWindow *>(d->m_widget); // could be 0 0126 return mainwindow; 0127 } 0128 0129 if (tagName == d->tagMenuBar) { 0130 KMainWindow *mainWin = qobject_cast<KMainWindow *>(d->m_widget); 0131 QMenuBar *bar = nullptr; 0132 if (mainWin) { 0133 bar = mainWin->menuBar(); 0134 } 0135 if (!bar) { 0136 bar = new QMenuBar(d->m_widget); 0137 } 0138 bar->show(); 0139 return bar; 0140 } 0141 0142 if (tagName == d->tagMenu) { 0143 // Look up to see if we are inside a mainwindow. If yes, then 0144 // use it as parent widget (to get kaction to plug itself into the 0145 // mainwindow). Don't use a popupmenu as parent widget, otherwise 0146 // the popup won't be hidden if it is used as a standalone menu as well. 0147 // Note: menus with a parent of 0, coming from child clients, can be 0148 // leaked if the child client is deleted without a proper removeClient call, though. 0149 QWidget *p = parent; 0150 0151 if (!p && qobject_cast<QMainWindow *>(d->m_widget)) { 0152 p = d->m_widget; 0153 } 0154 0155 while (p && !qobject_cast<QMainWindow *>(p)) { 0156 p = p->parentWidget(); 0157 } 0158 0159 QString name = element.attribute(d->attrName); 0160 0161 if (!KAuthorized::authorizeAction(name)) { 0162 return nullptr; 0163 } 0164 0165 QMenu *popup = new QMenu(p); 0166 popup->setObjectName(name); 0167 0168 d->m_menumenuhandler->insertMenu(popup); 0169 0170 QString i18nText; 0171 QDomElement textElem = element.namedItem(d->attrText1).toElement(); 0172 if (textElem.isNull()) { // try with capital T 0173 textElem = element.namedItem(d->attrText2).toElement(); 0174 } 0175 const QString text = textElem.text(); 0176 const QString context = textElem.attribute(d->attrContext); 0177 0178 // qCDebug(DEBUG_KXMLGUI) << "DOMAIN" << KLocalizedString::applicationDomain(); 0179 // qCDebug(DEBUG_KXMLGUI) << "ELEMENT TEXT:" << text; 0180 0181 if (text.isEmpty()) { // still no luck 0182 i18nText = i18n("No text"); 0183 } else { 0184 QByteArray domain = textElem.attribute(d->attrDomain).toUtf8(); 0185 if (domain.isEmpty()) { 0186 domain = element.ownerDocument().documentElement().attribute(d->attrDomain).toUtf8(); 0187 if (domain.isEmpty()) { 0188 domain = KLocalizedString::applicationDomain(); 0189 } 0190 } 0191 if (context.isEmpty()) { 0192 i18nText = i18nd(domain.constData(), text.toUtf8().constData()); 0193 } else { 0194 i18nText = i18ndc(domain.constData(), context.toUtf8().constData(), text.toUtf8().constData()); 0195 } 0196 } 0197 0198 // qCDebug(DEBUG_KXMLGUI) << "ELEMENT i18n TEXT:" << i18nText; 0199 0200 const QString icon = element.attribute(d->attrIcon); 0201 QIcon pix; 0202 if (!icon.isEmpty()) { 0203 pix = QIcon::fromTheme(icon); 0204 } 0205 0206 if (parent) { 0207 QAction *act = popup->menuAction(); 0208 if (!icon.isEmpty()) { 0209 act->setIcon(pix); 0210 } 0211 act->setText(i18nText); 0212 if (index == -1 || index >= parent->actions().count()) { 0213 parent->addAction(act); 0214 } else { 0215 parent->insertAction(parent->actions().value(index), act); 0216 } 0217 containerAction = act; 0218 containerAction->setObjectName(name); 0219 } 0220 0221 return popup; 0222 } 0223 0224 if (tagName == d->tagToolBar) { 0225 QString name = element.attribute(d->attrName); 0226 0227 KToolBar *bar = static_cast<KToolBar *>(d->m_widget->findChild<KToolBar *>(name)); 0228 if (!bar) { 0229 bar = new KToolBar(name, d->m_widget, false); 0230 } 0231 0232 if (qobject_cast<KMainWindow *>(d->m_widget)) { 0233 if (d->m_client && !d->m_client->xmlFile().isEmpty()) { 0234 bar->addXMLGUIClient(d->m_client); 0235 } 0236 } 0237 if (!bar->mainWindow()) { 0238 bar->show(); 0239 } 0240 0241 bar->loadState(element); 0242 0243 return bar; 0244 } 0245 0246 if (tagName == d->tagStatusBar) { 0247 KMainWindow *mainWin = qobject_cast<KMainWindow *>(d->m_widget); 0248 if (mainWin) { 0249 mainWin->statusBar()->show(); 0250 return mainWin->statusBar(); 0251 } 0252 QStatusBar *bar = new QStatusBar(d->m_widget); 0253 return bar; 0254 } 0255 0256 return nullptr; 0257 } 0258 0259 void KXMLGUIBuilder::removeContainer(QWidget *container, QWidget *parent, QDomElement &element, QAction *containerAction) 0260 { 0261 // Warning parent can be 0L 0262 0263 if (qobject_cast<QMenu *>(container)) { 0264 if (parent) { 0265 parent->removeAction(containerAction); 0266 } 0267 0268 delete container; 0269 } else if (qobject_cast<KToolBar *>(container)) { 0270 KToolBar *tb = static_cast<KToolBar *>(container); 0271 0272 tb->saveState(element); 0273 if (tb->mainWindow()) { 0274 delete tb; 0275 } else { 0276 tb->clear(); 0277 tb->hide(); 0278 } 0279 } else if (qobject_cast<QMenuBar *>(container)) { 0280 QMenuBar *mb = static_cast<QMenuBar *>(container); 0281 mb->hide(); 0282 // Don't delete menubar - it can be reused by createContainer. 0283 // If you decide that you do need to delete the menubar, make 0284 // sure that QMainWindow::d->mb does not point to a deleted 0285 // menubar object. 0286 } else if (qobject_cast<QStatusBar *>(container)) { 0287 if (qobject_cast<KMainWindow *>(d->m_widget)) { 0288 container->hide(); 0289 } else { 0290 delete static_cast<QStatusBar *>(container); 0291 } 0292 } else { 0293 qCWarning(DEBUG_KXMLGUI) << "Unhandled container to remove : " << container->metaObject()->className(); 0294 } 0295 } 0296 0297 QStringList KXMLGUIBuilder::customTags() const 0298 { 0299 QStringList res; 0300 res << d->tagSeparator << d->tagSpacer << d->tagTearOffHandle << d->tagMenuTitle; 0301 return res; 0302 } 0303 0304 QAction *KXMLGUIBuilder::createCustomElement(QWidget *parent, int index, const QDomElement &element) 0305 { 0306 QAction *before = nullptr; 0307 if (index > 0 && index < parent->actions().count()) { 0308 before = parent->actions().at(index); 0309 } 0310 0311 const QString tagName = element.tagName().toLower(); 0312 if (tagName == d->tagSeparator) { 0313 if (QMenu *menu = qobject_cast<QMenu *>(parent)) { 0314 // QMenu already cares for leading/trailing/repeated separators 0315 // no need to check anything 0316 return menu->insertSeparator(before); 0317 } else if (QMenuBar *bar = qobject_cast<QMenuBar *>(parent)) { 0318 QAction *separatorAction = new QAction(bar); 0319 separatorAction->setSeparator(true); 0320 bar->insertAction(before, separatorAction); 0321 return separatorAction; 0322 } else if (KToolBar *bar = qobject_cast<KToolBar *>(parent)) { 0323 /* FIXME KAction port - any need to provide a replacement for lineSeparator/normal separator? 0324 bool isLineSep = true; 0325 0326 QDomNamedNodeMap attributes = element.attributes(); 0327 unsigned int i = 0; 0328 for (; i < attributes.length(); i++ ) 0329 { 0330 QDomAttr attr = attributes.item( i ).toAttr(); 0331 0332 if ( attr.name().toLower() == d->attrLineSeparator && 0333 attr.value().toLower() == QLatin1String("false") ) 0334 { 0335 isLineSep = false; 0336 break; 0337 } 0338 } 0339 0340 if ( isLineSep ) 0341 return bar->insertSeparator( index ? bar->actions()[index - 1] : 0L ); 0342 else*/ 0343 0344 return bar->insertSeparator(before); 0345 } 0346 } else if (tagName == d->tagSpacer) { 0347 if (QToolBar *bar = qobject_cast<QToolBar *>(parent)) { 0348 // Create the simple spacer widget 0349 QWidget *spacer = new QWidget(parent); 0350 spacer->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); 0351 return bar->insertWidget(before, spacer); 0352 } 0353 } else if (tagName == d->tagTearOffHandle) { 0354 static_cast<QMenu *>(parent)->setTearOffEnabled(true); 0355 } else if (tagName == d->tagMenuTitle) { 0356 if (QMenu *m = qobject_cast<QMenu *>(parent)) { 0357 QString i18nText; 0358 const QString text = element.text(); 0359 0360 if (text.isEmpty()) { 0361 i18nText = i18n("No text"); 0362 } else { 0363 QByteArray domain = element.attribute(d->attrDomain).toUtf8(); 0364 if (domain.isEmpty()) { 0365 domain = element.ownerDocument().documentElement().attribute(d->attrDomain).toUtf8(); 0366 if (domain.isEmpty()) { 0367 domain = KLocalizedString::applicationDomain(); 0368 } 0369 } 0370 i18nText = i18nd(domain.constData(), qPrintable(text)); 0371 } 0372 0373 QString icon = element.attribute(d->attrIcon); 0374 QIcon pix; 0375 0376 if (!icon.isEmpty()) { 0377 pix = QIcon::fromTheme(icon); 0378 } 0379 0380 if (!icon.isEmpty()) { 0381 return m->insertSection(before, pix, i18nText); 0382 } else { 0383 return m->insertSection(before, i18nText); 0384 } 0385 } 0386 } 0387 0388 QAction *blank = new QAction(parent); 0389 blank->setVisible(false); 0390 parent->insertAction(before, blank); 0391 return blank; 0392 } 0393 0394 #if KXMLGUI_BUILD_DEPRECATED_SINCE(5, 0) 0395 void KXMLGUIBuilder::removeCustomElement(QWidget *parent, QAction *action) 0396 { 0397 parent->removeAction(action); 0398 } 0399 #endif 0400 0401 KXMLGUIClient *KXMLGUIBuilder::builderClient() const 0402 { 0403 return d->m_client; 0404 } 0405 0406 void KXMLGUIBuilder::setBuilderClient(KXMLGUIClient *client) 0407 { 0408 d->m_client = client; 0409 } 0410 0411 void KXMLGUIBuilder::finalizeGUI(KXMLGUIClient *) 0412 { 0413 KXmlGuiWindow *window = qobject_cast<KXmlGuiWindow *>(d->m_widget); 0414 if (window) { 0415 window->finalizeGUI(false); 0416 } 0417 } 0418 0419 void KXMLGUIBuilder::virtual_hook(int, void *) 0420 { 0421 /*BASE::virtual_hook( id, data );*/ 0422 }