File indexing completed on 2024-06-09 04:26:06

0001 /* This file is part of the KDE libraries
0002     Copyright
0003     (C) 2000 Reginald Stadlbauer (reggie@kde.org)
0004     (C) 1997 Stephan Kulow (coolo@kde.org)
0005     (C) 1997-2000 Sven Radej (radej@kde.org)
0006     (C) 1997-2000 Matthias Ettrich (ettrich@kde.org)
0007     (C) 1999 Chris Schlaeger (cs@kde.org)
0008     (C) 2002 Joseph Wenninger (jowenn@kde.org)
0009     (C) 2005-2006 Hamish Rodda (rodda@kde.org)
0010 
0011     SPDX-License-Identifier: LGPL-2.0-only
0012 */
0013 
0014 #include "kxmlguiwindow.h"
0015 
0016 #include "config-xmlgui.h"
0017 
0018 #include "kmainwindow_p.h"
0019 #include "kactioncollection.h"
0020 #ifdef HAVE_DBUS
0021 #include "kmainwindowiface_p.h"
0022 #endif
0023 #include "ktoolbarhandler_p.h"
0024 #include "kxmlguifactory.h"
0025 #include "kedittoolbar.h"
0026 #include "khelpmenu.h"
0027 #include "ktoolbar.h"
0028 
0029 #include <QCloseEvent>
0030 #ifdef HAVE_DBUS
0031 #include <QDBusConnection>
0032 #endif
0033 #include <QDomDocument>
0034 #include <QLayout>
0035 #include <QDebug>
0036 #include <QMenuBar>
0037 #include <QObject>
0038 #include <QStatusBar>
0039 #include <QStyle>
0040 #include <QWidget>
0041 #include <QList>
0042 
0043 #include <ktoggleaction.h>
0044 #include <kstandardaction.h>
0045 #include <kconfig.h>
0046 #include <klocalizedstring.h>
0047 #include <kaboutdata.h>
0048 #include <ksharedconfig.h>
0049 #include <kconfiggroup.h>
0050 
0051 #include <stdlib.h>
0052 #include <ctype.h>
0053 
0054 class KXmlGuiWindowPrivate : public KisKMainWindowPrivate
0055 {
0056 public:
0057     void _k_slotFactoryMakingChanges(bool b)
0058     {
0059         // While the GUI factory is adding/removing clients,
0060         // don't let KisKMainWindow think those are changes made by the user
0061         // #105525
0062         letDirtySettings = !b;
0063     }
0064 
0065     bool showHelpMenu: 1;
0066     QSize defaultSize;
0067 
0068     KDEPrivate::ToolBarHandler *toolBarHandler;
0069     KToggleAction *showStatusBarAction;
0070     QPointer<KisKEditToolBar> toolBarEditor;
0071     KisKXMLGUIFactory *factory;
0072 };
0073 
0074 KXmlGuiWindow::KXmlGuiWindow(QWidget *parent, Qt::WindowFlags f)
0075     : KisKMainWindow(*new KXmlGuiWindowPrivate, parent, f), KisKXMLGUIBuilder(this)
0076 {
0077     K_D(KXmlGuiWindow);
0078     d->showHelpMenu = true;
0079     d->toolBarHandler = 0;
0080     d->showStatusBarAction = 0;
0081     d->factory = 0;
0082 #ifdef HAVE_DBUS
0083     new KisKMainWindowInterface(this);
0084 #endif
0085 }
0086 
0087 QAction *KXmlGuiWindow::toolBarMenuAction()
0088 {
0089     K_D(KXmlGuiWindow);
0090     if (!d->toolBarHandler) {
0091         return 0;
0092     }
0093 
0094     return d->toolBarHandler->toolBarMenuAction();
0095 }
0096 
0097 void KXmlGuiWindow::setupToolbarMenuActions()
0098 {
0099     K_D(KXmlGuiWindow);
0100     if (d->toolBarHandler) {
0101         d->toolBarHandler->setupActions();
0102     }
0103 }
0104 
0105 KXmlGuiWindow::~KXmlGuiWindow()
0106 {
0107     K_D(KXmlGuiWindow);
0108     delete d->factory;
0109 }
0110 
0111 bool KXmlGuiWindow::event(QEvent *ev)
0112 {
0113     bool ret = KisKMainWindow::event(ev);
0114 #ifdef HAVE_DBUS
0115     if (ev->type() == QEvent::Polish) {
0116         QDBusConnection::sessionBus().registerObject(dbusName() + QStringLiteral("/actions"), actionCollection(),
0117                 QDBusConnection::ExportScriptableSlots |
0118                 QDBusConnection::ExportScriptableProperties |
0119                 QDBusConnection::ExportNonScriptableSlots |
0120                 QDBusConnection::ExportNonScriptableProperties |
0121                 QDBusConnection::ExportChildObjects);
0122     }
0123 #endif
0124     return ret;
0125 }
0126 
0127 void KXmlGuiWindow::setHelpMenuEnabled(bool showHelpMenu)
0128 {
0129     K_D(KXmlGuiWindow);
0130     d->showHelpMenu = showHelpMenu;
0131 }
0132 
0133 bool KXmlGuiWindow::isHelpMenuEnabled() const
0134 {
0135     K_D(const KXmlGuiWindow);
0136     return d->showHelpMenu;
0137 }
0138 
0139 KisKXMLGUIFactory *KXmlGuiWindow::guiFactory()
0140 {
0141     K_D(KXmlGuiWindow);
0142     if (!d->factory) {
0143         d->factory = new KisKXMLGUIFactory(this, this);
0144         connect(d->factory, SIGNAL(makingChanges(bool)),
0145                 this, SLOT(_k_slotFactoryMakingChanges(bool)));
0146     }
0147     return d->factory;
0148 }
0149 
0150 void KXmlGuiWindow::configureToolbars()
0151 {
0152     K_D(KXmlGuiWindow);
0153     KConfigGroup cg(KSharedConfig::openConfig(), "");
0154     saveMainWindowSettings(cg);
0155     if (!d->toolBarEditor) {
0156         d->toolBarEditor = new KisKEditToolBar(guiFactory(), this);
0157         d->toolBarEditor->setAttribute(Qt::WA_DeleteOnClose);
0158         connect(d->toolBarEditor, SIGNAL(newToolBarConfig()), SLOT(saveNewToolbarConfig()));
0159     }
0160     d->toolBarEditor->show();
0161 }
0162 
0163 void KXmlGuiWindow::saveNewToolbarConfig()
0164 {
0165     // createGUI(xmlFile()); // this loses any plugged-in guiclients, so we use remove+add instead.
0166 
0167     guiFactory()->removeClient(this);
0168     guiFactory()->addClient(this);
0169 
0170     KConfigGroup cg(KSharedConfig::openConfig(), "");
0171     applyMainWindowSettings(cg);
0172 }
0173 
0174 void KXmlGuiWindow::setupGUI(StandardWindowOptions options, const QString &xmlfile)
0175 {
0176     setupGUI(QSize(), options, xmlfile);
0177 }
0178 
0179 void KXmlGuiWindow::setupGUI(const QSize &defaultSize, StandardWindowOptions options, const QString &xmlfile)
0180 {
0181     K_D(KXmlGuiWindow);
0182     if (options & Keys) {
0183         KStandardAction::keyBindings(guiFactory(),
0184                                      SLOT(configureShortcuts()), actionCollection());
0185     }
0186     if ((options & StatusBar) && statusBar()) {
0187         createStandardStatusBarAction();
0188     }
0189 
0190     if (options & ToolBar) {
0191         setStandardToolBarMenuEnabled(true);
0192         KStandardAction::configureToolbars(this,
0193                                            SLOT(configureToolbars()), actionCollection());
0194     }
0195 
0196     d->defaultSize = defaultSize;
0197 
0198     if (options & Create) {
0199         createGUI(xmlfile);
0200     }
0201 
0202     if (d->defaultSize.isValid()) {
0203         resize(d->defaultSize);
0204     } else if (isHidden()) {
0205         adjustSize();
0206     }
0207 
0208     if (options & Save) {
0209         const KConfigGroup cg(autoSaveConfigGroup());
0210         if (cg.isValid()) {
0211             setAutoSaveSettings(cg);
0212         } else {
0213             setAutoSaveSettings();
0214         }
0215     }
0216 }
0217 void KXmlGuiWindow::createGUI(const QString &xmlfile)
0218 {
0219     K_D(KXmlGuiWindow);
0220     // disabling the updates prevents unnecessary redraws
0221     //setUpdatesEnabled( false );
0222 
0223     // just in case we are rebuilding, let's remove our old client
0224     guiFactory()->removeClient(this);
0225 
0226     // make sure to have an empty GUI
0227     QMenuBar *mb = menuBar();
0228     if (mb) {
0229         mb->clear();
0230     }
0231 
0232     qDeleteAll(toolBars());   // delete all toolbars
0233 
0234     // don't build a help menu unless the user ask for it
0235     if (d->showHelpMenu) {
0236         delete d->helpMenu;
0237         // we always want a help menu
0238         d->helpMenu = new KisKHelpMenu(this, KAboutData::applicationData(), true);
0239 
0240         KisKActionCollection *actions = actionCollection();
0241         QAction *helpContentsAction = d->helpMenu->action(KisKHelpMenu::menuHelpContents);
0242         QAction *whatsThisAction = d->helpMenu->action(KisKHelpMenu::menuWhatsThis);
0243         QAction *reportBugAction = d->helpMenu->action(KisKHelpMenu::menuReportBug);
0244         QAction *switchLanguageAction = d->helpMenu->action(KisKHelpMenu::menuSwitchLanguage);
0245         QAction *aboutAppAction = d->helpMenu->action(KisKHelpMenu::menuAboutApp);
0246         QAction *aboutKdeAction = d->helpMenu->action(KisKHelpMenu::menuAboutKDE);
0247 
0248         if (helpContentsAction) {
0249             actions->addAction(helpContentsAction->objectName(), helpContentsAction);
0250         }
0251         if (whatsThisAction) {
0252             actions->addAction(whatsThisAction->objectName(), whatsThisAction);
0253         }
0254         if (reportBugAction) {
0255             actions->addAction(reportBugAction->objectName(), reportBugAction);
0256         }
0257         if (switchLanguageAction) {
0258             actions->addAction(switchLanguageAction->objectName(), switchLanguageAction);
0259         }
0260         if (aboutAppAction) {
0261             actions->addAction(aboutAppAction->objectName(), aboutAppAction);
0262         }
0263         if (aboutKdeAction) {
0264             actions->addAction(aboutKdeAction->objectName(), aboutKdeAction);
0265         }
0266     }
0267 
0268     const QString windowXmlFile = xmlfile.isNull() ? componentName() + QStringLiteral("ui.xmlgui") : xmlfile;
0269 
0270     // Help beginners who call setXMLFile and then setupGUI...
0271     if (!xmlFile().isEmpty() && xmlFile() != windowXmlFile) {
0272         qWarning() << "You called setXMLFile(" << xmlFile() << ") and then createGUI or setupGUI,"
0273                    << "which also calls setXMLFile and will overwrite the file you have previously set.\n"
0274                    << "You should call createGUI(" << xmlFile() << ") or setupGUI(<options>," << xmlFile() << ") instead.";
0275     }
0276 
0277     // we always want to load in our global standards file
0278     loadStandardsXmlFile();
0279 
0280     // now, merge in our local xml file.
0281     setXMLFile(windowXmlFile, true);
0282 
0283     // make sure we don't have any state saved already
0284     setXMLGUIBuildDocument(QDomDocument());
0285 
0286     // do the actual GUI building
0287     guiFactory()->reset();
0288     guiFactory()->addClient(this);
0289 
0290     //  setUpdatesEnabled( true );
0291 }
0292 
0293 void KXmlGuiWindow::slotStateChanged(const QString &newstate)
0294 {
0295     stateChanged(newstate, KisKXMLGUIClient::StateNoReverse);
0296 }
0297 
0298 void KXmlGuiWindow::slotStateChanged(const QString &newstate,
0299                                      bool reverse)
0300 {
0301     stateChanged(newstate,
0302                  reverse ? KisKXMLGUIClient::StateReverse : KisKXMLGUIClient::StateNoReverse);
0303 }
0304 
0305 void KXmlGuiWindow::setStandardToolBarMenuEnabled(bool enable)
0306 {
0307     K_D(KXmlGuiWindow);
0308     if (enable) {
0309         if (d->toolBarHandler) {
0310             return;
0311         }
0312 
0313         d->toolBarHandler = new KDEPrivate::ToolBarHandler(this);
0314 
0315         if (factory()) {
0316             factory()->addClient(d->toolBarHandler);
0317         }
0318     } else {
0319         if (!d->toolBarHandler) {
0320             return;
0321         }
0322 
0323         if (factory()) {
0324             factory()->removeClient(d->toolBarHandler);
0325         }
0326 
0327         delete d->toolBarHandler;
0328         d->toolBarHandler = 0;
0329     }
0330 }
0331 
0332 bool KXmlGuiWindow::isStandardToolBarMenuEnabled() const
0333 {
0334     K_D(const KXmlGuiWindow);
0335     return (d->toolBarHandler);
0336 }
0337 
0338 void KXmlGuiWindow::createStandardStatusBarAction()
0339 {
0340     K_D(KXmlGuiWindow);
0341     if (!d->showStatusBarAction) {
0342         d->showStatusBarAction = KStandardAction::showStatusbar(this, SLOT(setSettingsDirty()), actionCollection());
0343         QStatusBar *sb = statusBar(); // Creates statusbar if it doesn't exist already.
0344         connect(d->showStatusBarAction, SIGNAL(toggled(bool)), sb, SLOT(setVisible(bool)));
0345         d->showStatusBarAction->setChecked(sb->isHidden());
0346     } else {
0347         // If the language has changed, we'll need to grab the new text and whatsThis
0348         QAction *tmpStatusBar = KStandardAction::showStatusbar(0, 0, 0);
0349         d->showStatusBarAction->setText(tmpStatusBar->text());
0350         d->showStatusBarAction->setWhatsThis(tmpStatusBar->whatsThis());
0351         delete tmpStatusBar;
0352     }
0353 }
0354 
0355 void KXmlGuiWindow::finalizeGUI(bool /*force*/)
0356 {
0357     // FIXME: this really needs to be removed with a code more like the one we had on KDE3.
0358     //        what we need to do here is to position correctly toolbars so they don't overlap.
0359     //        Also, take in count plugins could provide their own toolbars and those also need to
0360     //        be restored.
0361     if (autoSaveSettings() && autoSaveConfigGroup().isValid()) {
0362         applyMainWindowSettings(autoSaveConfigGroup());
0363     }
0364 }
0365 
0366 void KXmlGuiWindow::applyMainWindowSettings(const KConfigGroup &config)
0367 {
0368     K_D(KXmlGuiWindow);
0369     KisKMainWindow::applyMainWindowSettings(config);
0370     QStatusBar *sb = findChild<QStatusBar *>();
0371     if (sb && d->showStatusBarAction) {
0372         d->showStatusBarAction->setChecked(!sb->isHidden());
0373     }
0374 }
0375 
0376 // KDE5 TODO: change it to "using KisKXMLGUIBuilder::finalizeGUI;" in the header
0377 // and remove the reimplementation
0378 void KXmlGuiWindow::finalizeGUI(KisKXMLGUIClient *client)
0379 {
0380     KisKXMLGUIBuilder::finalizeGUI(client);
0381 }
0382 
0383 #include "moc_kxmlguiwindow.cpp"
0384