File indexing completed on 2024-03-24 15:40:50

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 }