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