File indexing completed on 2024-12-22 04:14:10

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