File indexing completed on 2024-04-14 14:30:01

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2000 Reginald Stadlbauer <reggie@kde.org>
0004     SPDX-FileCopyrightText: 1997 Stephan Kulow <coolo@kde.org>
0005     SPDX-FileCopyrightText: 1997-2000 Sven Radej <radej@kde.org>
0006     SPDX-FileCopyrightText: 1997-2000 Matthias Ettrich <ettrich@kde.org>
0007     SPDX-FileCopyrightText: 1999 Chris Schlaeger <cs@kde.org>
0008     SPDX-FileCopyrightText: 2002 Joseph Wenninger <jowenn@kde.org>
0009     SPDX-FileCopyrightText: 2005-2006 Hamish Rodda <rodda@kde.org>
0010     SPDX-FileCopyrightText: 2000-2008 David Faure <faure@kde.org>
0011 
0012     SPDX-License-Identifier: LGPL-2.0-only
0013 */
0014 
0015 #include "kmainwindow.h"
0016 
0017 #include "kmainwindow_p.h"
0018 #ifdef QT_DBUS_LIB
0019 #include "kmainwindowiface_p.h"
0020 #endif
0021 #include "khelpmenu.h"
0022 #include "ktoolbar.h"
0023 #include "ktoolbarhandler_p.h"
0024 #include "ktooltiphelper.h"
0025 
0026 #include <QApplication>
0027 #include <QCloseEvent>
0028 #include <QDockWidget>
0029 #include <QFile>
0030 #include <QList>
0031 #include <QMenuBar>
0032 #include <QObject>
0033 #ifndef QT_NO_SESSIONMANAGER
0034 #include <QSessionManager>
0035 #endif
0036 #include <QStatusBar>
0037 #include <QStyle>
0038 #include <QTimer>
0039 #include <QWidget>
0040 #include <QWindow>
0041 #ifdef QT_DBUS_LIB
0042 #include <QDBusConnection>
0043 #endif
0044 
0045 #include <KAboutData>
0046 #include <KConfig>
0047 #include <KConfigGroup>
0048 #include <KConfigGui>
0049 #include <KLocalizedString>
0050 #include <KSharedConfig>
0051 #include <KWindowConfig>
0052 
0053 //#include <cctype>
0054 
0055 static const char WINDOW_PROPERTIES[] = "WindowProperties";
0056 
0057 static QMenuBar *internalMenuBar(KMainWindow *mw)
0058 {
0059     return mw->findChild<QMenuBar *>(QString(), Qt::FindDirectChildrenOnly);
0060 }
0061 
0062 static QStatusBar *internalStatusBar(KMainWindow *mw)
0063 {
0064     return mw->findChild<QStatusBar *>(QString(), Qt::FindDirectChildrenOnly);
0065 }
0066 
0067 /**
0068 
0069  * Listens to resize events from QDockWidgets. The KMainWindow
0070  * settings are set as dirty, as soon as at least one resize
0071  * event occurred. The listener is attached to the dock widgets
0072  * by dock->installEventFilter(dockResizeListener) inside
0073  * KMainWindow::event().
0074  */
0075 class DockResizeListener : public QObject
0076 {
0077     Q_OBJECT
0078 public:
0079     DockResizeListener(KMainWindow *win);
0080     ~DockResizeListener() override;
0081     bool eventFilter(QObject *watched, QEvent *event) override;
0082 
0083 private:
0084     KMainWindow *const m_win;
0085 };
0086 
0087 DockResizeListener::DockResizeListener(KMainWindow *win)
0088     : QObject(win)
0089     , m_win(win)
0090 {
0091 }
0092 
0093 DockResizeListener::~DockResizeListener()
0094 {
0095 }
0096 
0097 bool DockResizeListener::eventFilter(QObject *watched, QEvent *event)
0098 {
0099     switch (event->type()) {
0100     case QEvent::Resize:
0101     case QEvent::Move:
0102     case QEvent::Show:
0103     case QEvent::Hide:
0104         m_win->k_ptr->setSettingsDirty(KMainWindowPrivate::CompressCalls);
0105         break;
0106 
0107     default:
0108         break;
0109     }
0110 
0111     return QObject::eventFilter(watched, event);
0112 }
0113 
0114 KMWSessionManager::KMWSessionManager()
0115 {
0116 #ifndef QT_NO_SESSIONMANAGER
0117     connect(qApp, &QGuiApplication::saveStateRequest, this, &KMWSessionManager::saveState);
0118     connect(qApp, &QGuiApplication::commitDataRequest, this, &KMWSessionManager::commitData);
0119 #endif
0120 }
0121 
0122 KMWSessionManager::~KMWSessionManager()
0123 {
0124 }
0125 
0126 void KMWSessionManager::saveState(QSessionManager &sm)
0127 {
0128 #ifndef QT_NO_SESSIONMANAGER
0129     KConfigGui::setSessionConfig(sm.sessionId(), sm.sessionKey());
0130 
0131     KConfig *config = KConfigGui::sessionConfig();
0132     const auto windows = KMainWindow::memberList();
0133     if (!windows.isEmpty()) {
0134         // According to Jochen Wilhelmy <digisnap@cs.tu-berlin.de>, this
0135         // hook is useful for better document orientation
0136         windows.at(0)->saveGlobalProperties(config);
0137     }
0138 
0139     int n = 0;
0140     for (KMainWindow *mw : windows) {
0141         n++;
0142         mw->savePropertiesInternal(config, n);
0143     }
0144 
0145     KConfigGroup group(config, "Number");
0146     group.writeEntry("NumberOfWindows", n);
0147 
0148     // store new status to disk
0149     config->sync();
0150 
0151     // generate discard command for new file
0152     QString localFilePath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1Char('/') + config->name();
0153     if (QFile::exists(localFilePath)) {
0154         QStringList discard;
0155         discard << QStringLiteral("rm");
0156         discard << localFilePath;
0157         sm.setDiscardCommand(discard);
0158     }
0159 #else
0160     Q_UNUSED(sm)
0161 #endif // QT_NO_SESSIONMANAGER
0162 }
0163 
0164 void KMWSessionManager::commitData(QSessionManager &sm)
0165 {
0166 #ifndef QT_NO_SESSIONMANAGER
0167     if (!sm.allowsInteraction()) {
0168         return;
0169     }
0170 
0171     /*
0172        Purpose of this exercise: invoke queryClose() without actually closing the
0173        windows, because
0174        - queryClose() may contain session management code, so it must be invoked
0175        - actually closing windows may quit the application - cf.
0176          QGuiApplication::quitOnLastWindowClosed()
0177        - quitting the application and thus closing the session manager connection
0178          violates the X11 XSMP protocol.
0179          The exact requirement of XSMP that would be broken is,
0180          in the description of the client's state machine:
0181 
0182            save-yourself-done: (changing state is forbidden)
0183 
0184          Closing the session manager connection causes a state change.
0185          Worst of all, that is a real problem with ksmserver - it will not save
0186          applications that quit on their own in state save-yourself-done.
0187      */
0188     const auto windows = KMainWindow::memberList();
0189     for (KMainWindow *window : windows) {
0190         if (window->testAttribute(Qt::WA_WState_Hidden)) {
0191             continue;
0192         }
0193         QCloseEvent e;
0194         QApplication::sendEvent(window, &e);
0195         if (!e.isAccepted()) {
0196             sm.cancel();
0197             return;
0198         }
0199     }
0200 #else
0201     Q_UNUSED(sm)
0202 #endif // QT_NO_SESSIONMANAGER
0203 }
0204 
0205 Q_GLOBAL_STATIC(KMWSessionManager, ksm)
0206 Q_GLOBAL_STATIC(QList<KMainWindow *>, sMemberList)
0207 
0208 KMainWindow::KMainWindow(QWidget *parent, Qt::WindowFlags flags)
0209     : QMainWindow(parent, flags)
0210     , k_ptr(new KMainWindowPrivate)
0211 {
0212     Q_D(KMainWindow);
0213 
0214     d->init(this);
0215 }
0216 
0217 KMainWindow::KMainWindow(KMainWindowPrivate &dd, QWidget *parent, Qt::WindowFlags f)
0218     : QMainWindow(parent, f)
0219     , k_ptr(&dd)
0220 {
0221     Q_D(KMainWindow);
0222 
0223     d->init(this);
0224 }
0225 
0226 void KMainWindowPrivate::init(KMainWindow *_q)
0227 {
0228     q = _q;
0229 
0230     // Not needed in Qt6 (and doesn't exist at all)
0231 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0232 #ifndef QT_NO_SESSIONMANAGER
0233     QGuiApplication::setFallbackSessionManagementEnabled(false);
0234 #endif
0235 #endif
0236 
0237     q->setAnimated(q->style()->styleHint(QStyle::SH_Widget_Animate, nullptr, q));
0238 
0239     q->setAttribute(Qt::WA_DeleteOnClose);
0240 
0241     helpMenu = nullptr;
0242 
0243     // actionCollection()->setWidget( this );
0244 #if 0
0245     QObject::connect(KGlobalSettings::self(), SIGNAL(settingsChanged(int)),
0246                      q, SLOT(_k_slotSettingsChanged(int)));
0247 #endif
0248 
0249 #ifndef QT_NO_SESSIONMANAGER
0250     // force KMWSessionManager creation
0251     ksm();
0252 #endif
0253 
0254     sMemberList()->append(q);
0255 
0256     // Set the icon theme fallback to breeze (if not already set)
0257     // Most of our apps use "lots" of icons that most of the times
0258     // are only available with breeze, we still honour the user icon
0259     // theme but if the icon is not found there, we go to breeze
0260     // since it's almost sure it'll be there.
0261     // This should be done as soon as possible (preferably via
0262     // Q_COREAPP_STARTUP_FUNCTION), but as for now it cannot be done too soon
0263     // as at that point QPlatformTheme is not instantiated yet and breaks the
0264     // internal status of QIconLoader (see QTBUG-74252).
0265     // See also discussion at https://phabricator.kde.org/D22488
0266     // TODO: remove this once we depend on Qt 5.15.1, where this is fixed
0267     if (QIcon::fallbackThemeName().isEmpty()) {
0268         QIcon::setFallbackThemeName(QStringLiteral("breeze"));
0269     }
0270 
0271     // If application is translated, load translator information for use in
0272     // KAboutApplicationDialog or other getters. The context and messages below
0273     // both must be exactly as listed, and are forced to be loaded from the
0274     // application's own message catalog instead of kxmlgui's.
0275     KAboutData aboutData(KAboutData::applicationData());
0276     if (aboutData.translators().isEmpty()) {
0277         aboutData.setTranslator(i18ndc(nullptr, "NAME OF TRANSLATORS", "Your names"), //
0278                                 i18ndc(nullptr, "EMAIL OF TRANSLATORS", "Your emails"));
0279 
0280         KAboutData::setApplicationData(aboutData);
0281     }
0282 
0283     settingsDirty = false;
0284     autoSaveSettings = false;
0285     autoSaveWindowSize = true; // for compatibility
0286     // d->kaccel = actionCollection()->kaccel();
0287     settingsTimer = nullptr;
0288     sizeTimer = nullptr;
0289 
0290     dockResizeListener = new DockResizeListener(_q);
0291     letDirtySettings = true;
0292 
0293     sizeApplied = false;
0294     suppressCloseEvent = false;
0295 
0296     qApp->installEventFilter(KToolTipHelper::instance());
0297 }
0298 
0299 static bool endsWithHashNumber(const QString &s)
0300 {
0301     for (int i = s.length() - 1; i > 0; --i) {
0302         if (s[i] == QLatin1Char('#') && i != s.length() - 1) {
0303             return true; // ok
0304         }
0305         if (!s[i].isDigit()) {
0306             break;
0307         }
0308     }
0309     return false;
0310 }
0311 
0312 static inline bool isValidDBusObjectPathCharacter(const QChar &c)
0313 {
0314     ushort u = c.unicode();
0315     /* clang-format off */
0316     return (u >= QLatin1Char('a') && u <= QLatin1Char('z'))
0317         || (u >= QLatin1Char('A') && u <= QLatin1Char('Z'))
0318         || (u >= QLatin1Char('0') && u <= QLatin1Char('9'))
0319         || (u == QLatin1Char('_')) || (u == QLatin1Char('/'));
0320     /* clang-format off */
0321 }
0322 
0323 void KMainWindowPrivate::polish(KMainWindow *q)
0324 {
0325     // Set a unique object name. Required by session management, window management, and for the dbus interface.
0326     QString objname;
0327     QString s;
0328     int unusedNumber = 1;
0329     const QString name = q->objectName();
0330     bool startNumberingImmediately = true;
0331     bool tryReuse = false;
0332     if (name.isEmpty()) {
0333         // no name given
0334         objname = QStringLiteral("MainWindow#");
0335     } else if (name.endsWith(QLatin1Char('#'))) {
0336         // trailing # - always add a number  - KWin uses this for better grouping
0337         objname = name;
0338     } else if (endsWithHashNumber(name)) {
0339         // trailing # with a number - like above, try to use the given number first
0340         objname = name;
0341         tryReuse = true;
0342         startNumberingImmediately = false;
0343     } else {
0344         objname = name;
0345         startNumberingImmediately = false;
0346     }
0347 
0348     s = objname;
0349     if (startNumberingImmediately) {
0350         s += QLatin1Char('1');
0351     }
0352 
0353     for (;;) {
0354         const QList<QWidget *> list = qApp->topLevelWidgets();
0355         bool found = false;
0356         for (QWidget *w : list) {
0357             if (w != q && w->objectName() == s) {
0358                 found = true;
0359                 break;
0360             }
0361         }
0362         if (!found) {
0363             break;
0364         }
0365         if (tryReuse) {
0366             objname = name.left(name.length() - 1); // lose the hash
0367             unusedNumber = 0; // start from 1 below
0368             tryReuse = false;
0369         }
0370         s.setNum(++unusedNumber);
0371         s = objname + s;
0372     }
0373     q->setObjectName(s);
0374     if (!q->window() || q->window() == q) {
0375         q->winId(); // workaround for setWindowRole() crashing, and set also window role, just in case TT
0376         q->setWindowRole(s); // will keep insisting that object name suddenly should not be used for window role
0377     }
0378 
0379     dbusName = QLatin1Char('/') + QCoreApplication::applicationName() + QLatin1Char('/');
0380     dbusName += q->objectName().replace(QLatin1Char('/'), QLatin1Char('_'));
0381     // Clean up for dbus usage: any non-alphanumeric char should be turned into '_'
0382     for (QChar &c : dbusName) {
0383         if (!isValidDBusObjectPathCharacter(c)) {
0384             c = QLatin1Char('_');
0385         }
0386     }
0387 
0388 #ifdef QT_DBUS_LIB
0389     /* clang-format off */
0390     constexpr auto opts = QDBusConnection::ExportScriptableSlots
0391                           | QDBusConnection::ExportScriptableProperties
0392                           | QDBusConnection::ExportNonScriptableSlots
0393                           | QDBusConnection::ExportNonScriptableProperties
0394                           | QDBusConnection::ExportAdaptors;
0395     /* clang-format on */
0396     QDBusConnection::sessionBus().registerObject(dbusName, q, opts);
0397 #endif
0398 }
0399 
0400 void KMainWindowPrivate::setSettingsDirty(CallCompression callCompression)
0401 {
0402     if (!letDirtySettings) {
0403         return;
0404     }
0405 
0406     settingsDirty = true;
0407     if (autoSaveSettings) {
0408         if (callCompression == CompressCalls) {
0409             if (!settingsTimer) {
0410                 settingsTimer = new QTimer(q);
0411                 settingsTimer->setInterval(500);
0412                 settingsTimer->setSingleShot(true);
0413                 QObject::connect(settingsTimer, &QTimer::timeout, q, &KMainWindow::saveAutoSaveSettings);
0414             }
0415             settingsTimer->start();
0416         } else {
0417             q->saveAutoSaveSettings();
0418         }
0419     }
0420 }
0421 
0422 void KMainWindowPrivate::setSizeDirty()
0423 {
0424     if (autoSaveWindowSize) {
0425         if (!sizeTimer) {
0426             sizeTimer = new QTimer(q);
0427             sizeTimer->setInterval(500);
0428             sizeTimer->setSingleShot(true);
0429             QObject::connect(sizeTimer, &QTimer::timeout, q, [this]() {
0430                 _k_slotSaveAutoSaveSize();
0431             });
0432         }
0433         sizeTimer->start();
0434     }
0435 }
0436 
0437 KMainWindow::~KMainWindow()
0438 {
0439     sMemberList()->removeAll(this);
0440     delete static_cast<QObject *>(k_ptr->dockResizeListener); // so we don't get anymore events after k_ptr is destroyed
0441 }
0442 
0443 #if KXMLGUI_BUILD_DEPRECATED_SINCE(5, 0)
0444 QMenu *KMainWindow::helpMenu(const QString &aboutAppText, bool showWhatsThis)
0445 {
0446     Q_D(KMainWindow);
0447     if (!d->helpMenu) {
0448         if (aboutAppText.isEmpty()) {
0449             d->helpMenu = new KHelpMenu(this, KAboutData::applicationData(), showWhatsThis);
0450         } else {
0451             d->helpMenu = new KHelpMenu(this, aboutAppText, showWhatsThis);
0452         }
0453 
0454         if (!d->helpMenu) {
0455             return nullptr;
0456         }
0457     }
0458 
0459     return d->helpMenu->menu();
0460 }
0461 
0462 QMenu *KMainWindow::customHelpMenu(bool showWhatsThis)
0463 {
0464     Q_D(KMainWindow);
0465     if (!d->helpMenu) {
0466         d->helpMenu = new KHelpMenu(this, QString(), showWhatsThis);
0467         connect(d->helpMenu, &KHelpMenu::showAboutApplication, this, &KMainWindow::showAboutApplication);
0468     }
0469 
0470     return d->helpMenu->menu();
0471 }
0472 #endif
0473 
0474 bool KMainWindow::canBeRestored(int numberOfInstances)
0475 {
0476     KConfig *config = KConfigGui::sessionConfig();
0477     if (!config) {
0478         return false;
0479     }
0480 
0481     KConfigGroup group(config, "Number");
0482     // TODO KF6: we should use 0 as the default value, not 1
0483     // See also https://bugs.kde.org/show_bug.cgi?id=427552
0484     const int n = group.readEntry("NumberOfWindows", 1);
0485     return numberOfInstances >= 1 && numberOfInstances <= n;
0486 }
0487 
0488 const QString KMainWindow::classNameOfToplevel(int instanceNumber)
0489 {
0490     KConfig *config = KConfigGui::sessionConfig();
0491     if (!config) {
0492         return QString();
0493     }
0494 
0495     KConfigGroup group(config, QByteArray(WINDOW_PROPERTIES).append(QByteArray::number(instanceNumber)).constData());
0496     if (!group.hasKey("ClassName")) {
0497         return QString();
0498     } else {
0499         return group.readEntry("ClassName");
0500     }
0501 }
0502 
0503 bool KMainWindow::restore(int numberOfInstances, bool show)
0504 {
0505     if (!canBeRestored(numberOfInstances)) {
0506         return false;
0507     }
0508     KConfig *config = KConfigGui::sessionConfig();
0509     if (readPropertiesInternal(config, numberOfInstances)) {
0510         if (show) {
0511             KMainWindow::show();
0512         }
0513         return false;
0514     }
0515     return false;
0516 }
0517 
0518 void KMainWindow::setCaption(const QString &caption)
0519 {
0520     setPlainCaption(caption);
0521 }
0522 
0523 void KMainWindow::setCaption(const QString &caption, bool modified)
0524 {
0525     QString title = caption;
0526     if (!title.contains(QLatin1String("[*]")) && !title.isEmpty()) { // append the placeholder so that the modified mechanism works
0527         title.append(QLatin1String(" [*]"));
0528     }
0529     setPlainCaption(title);
0530     setWindowModified(modified);
0531 }
0532 
0533 void KMainWindow::setPlainCaption(const QString &caption)
0534 {
0535     setWindowTitle(caption);
0536 }
0537 
0538 void KMainWindow::appHelpActivated()
0539 {
0540     Q_D(KMainWindow);
0541     if (!d->helpMenu) {
0542         d->helpMenu = new KHelpMenu(this);
0543         if (!d->helpMenu) {
0544             return;
0545         }
0546     }
0547     d->helpMenu->appHelpActivated();
0548 }
0549 
0550 void KMainWindow::closeEvent(QCloseEvent *e)
0551 {
0552     Q_D(KMainWindow);
0553     if (d->suppressCloseEvent) {
0554         e->accept();
0555         return;
0556     }
0557 
0558     // Save settings if auto-save is enabled, and settings have changed
0559     if (d->settingsTimer && d->settingsTimer->isActive()) {
0560         d->settingsTimer->stop();
0561         saveAutoSaveSettings();
0562     }
0563     if (d->sizeTimer && d->sizeTimer->isActive()) {
0564         d->sizeTimer->stop();
0565         d->_k_slotSaveAutoSaveSize();
0566     }
0567     // Delete the marker that says we don't want to restore the position of the
0568     // next-opened instance; now that a window is closing, we do want to do this
0569     if (d->autoSaveGroup.isValid()) {
0570         d->getValidStateConfig(d->autoSaveGroup).deleteEntry("RestorePositionForNextInstance");
0571     }
0572     d->_k_slotSaveAutoSavePosition();
0573 
0574     if (queryClose()) {
0575         // widgets will start destroying themselves at this point and we don't
0576         // want to save state anymore after this as it might be incorrect
0577         d->autoSaveSettings = false;
0578         d->letDirtySettings = false;
0579         e->accept();
0580     } else {
0581         e->ignore(); // if the window should not be closed, don't close it
0582     }
0583 
0584 #ifndef QT_NO_SESSIONMANAGER
0585     // If saving session, we are processing a fake close event, and might get the real one later.
0586     if (e->isAccepted() && qApp->isSavingSession()) {
0587         d->suppressCloseEvent = true;
0588     }
0589 #endif
0590 }
0591 
0592 bool KMainWindow::queryClose()
0593 {
0594     return true;
0595 }
0596 
0597 void KMainWindow::saveGlobalProperties(KConfig *)
0598 {
0599 }
0600 
0601 void KMainWindow::readGlobalProperties(KConfig *)
0602 {
0603 }
0604 
0605 void KMainWindow::savePropertiesInternal(KConfig *config, int number)
0606 {
0607     Q_D(KMainWindow);
0608     const bool oldASWS = d->autoSaveWindowSize;
0609     d->autoSaveWindowSize = true; // make saveMainWindowSettings save the window size
0610 
0611     KConfigGroup cg(config, QByteArray(WINDOW_PROPERTIES).append(QByteArray::number(number)).constData());
0612 
0613     // store objectName, className, Width and Height  for later restoring
0614     // (Only useful for session management)
0615     cg.writeEntry("ObjectName", objectName());
0616     cg.writeEntry("ClassName", metaObject()->className());
0617 
0618     saveMainWindowSettings(cg); // Menubar, statusbar and Toolbar settings.
0619 
0620     cg = KConfigGroup(config, QByteArray::number(number).constData());
0621     saveProperties(cg);
0622 
0623     d->autoSaveWindowSize = oldASWS;
0624 }
0625 
0626 void KMainWindow::saveMainWindowSettings(KConfigGroup &cg)
0627 {
0628     Q_D(KMainWindow);
0629     // qDebug(200) << "KMainWindow::saveMainWindowSettings " << cg.name();
0630 
0631     KConfigGroup stateConfig = d->getValidStateConfig(cg);
0632     // Called by session management - or if we want to save the window size anyway
0633     if (d->autoSaveWindowSize) {
0634         KWindowConfig::saveWindowSize(windowHandle(), stateConfig);
0635         KWindowConfig::saveWindowPosition(windowHandle(), stateConfig);
0636     }
0637 
0638     // One day will need to save the version number, but for now, assume 0
0639     // Utilise the QMainWindow::saveState() functionality.
0640     const QByteArray state = saveState();
0641     stateConfig.writeEntry("State", state.toBase64());
0642 
0643     QStatusBar *sb = internalStatusBar(this);
0644     if (sb) {
0645         if (!cg.hasDefault("StatusBar") && !sb->isHidden()) {
0646             cg.revertToDefault("StatusBar");
0647         } else {
0648             cg.writeEntry("StatusBar", sb->isHidden() ? "Disabled" : "Enabled");
0649         }
0650     }
0651 
0652     QMenuBar *mb = internalMenuBar(this);
0653     if (mb) {
0654         if (!cg.hasDefault("MenuBar") && !mb->isHidden()) {
0655             cg.revertToDefault("MenuBar");
0656         } else {
0657             cg.writeEntry("MenuBar", mb->isHidden() ? "Disabled" : "Enabled");
0658         }
0659     }
0660 
0661     if (!autoSaveSettings() || cg.name() == autoSaveGroup()) {
0662         // TODO should be cg == d->autoSaveGroup, to compare both kconfig and group name
0663         if (!cg.hasDefault("ToolBarsMovable") && !KToolBar::toolBarsLocked()) {
0664             cg.revertToDefault("ToolBarsMovable");
0665         } else {
0666             cg.writeEntry("ToolBarsMovable", KToolBar::toolBarsLocked() ? "Disabled" : "Enabled");
0667         }
0668     }
0669 
0670     int n = 1; // Toolbar counter. toolbars are counted from 1,
0671     const auto toolBars = this->toolBars();
0672     for (KToolBar *toolbar : toolBars) {
0673         QByteArray groupName("Toolbar");
0674         // Give a number to the toolbar, but prefer a name if there is one,
0675         // because there's no real guarantee on the ordering of toolbars
0676         groupName += (toolbar->objectName().isEmpty() ? QByteArray::number(n) : QByteArray(" ").append(toolbar->objectName().toUtf8()));
0677 
0678         KConfigGroup toolbarGroup(&cg, groupName.constData());
0679         toolbar->saveSettings(toolbarGroup);
0680         n++;
0681     }
0682 }
0683 
0684 bool KMainWindow::readPropertiesInternal(KConfig *config, int number)
0685 {
0686     Q_D(KMainWindow);
0687 
0688     const bool oldLetDirtySettings = d->letDirtySettings;
0689     d->letDirtySettings = false;
0690 
0691     if (number == 1) {
0692         readGlobalProperties(config);
0693     }
0694 
0695     // in order they are in toolbar list
0696     KConfigGroup cg(config, QByteArray(WINDOW_PROPERTIES).append(QByteArray::number(number)).constData());
0697 
0698     // restore the object name (window role)
0699     if (cg.hasKey("ObjectName")) {
0700         setObjectName(cg.readEntry("ObjectName"));
0701     }
0702 
0703     d->sizeApplied = false; // since we are changing config file, reload the size of the window
0704     // if necessary. Do it before the call to applyMainWindowSettings.
0705     applyMainWindowSettings(cg); // Menubar, statusbar and toolbar settings.
0706 
0707     KConfigGroup grp(config, QByteArray::number(number).constData());
0708     readProperties(grp);
0709 
0710     d->letDirtySettings = oldLetDirtySettings;
0711 
0712     return true;
0713 }
0714 
0715 void KMainWindow::applyMainWindowSettings(const KConfigGroup &_cg)
0716 {
0717     Q_D(KMainWindow);
0718     // qDebug(200) << "KMainWindow::applyMainWindowSettings " << cg.name();
0719 
0720     KConfigGroup cg = _cg;
0721     d->migrateStateDataIfNeeded(cg);
0722 
0723     QWidget *focusedWidget = QApplication::focusWidget();
0724 
0725     const bool oldLetDirtySettings = d->letDirtySettings;
0726     d->letDirtySettings = false;
0727 
0728     KConfigGroup stateConfig = d->getValidStateConfig(cg);
0729 
0730     if (!d->sizeApplied && (!window() || window() == this)) {
0731         winId(); // ensure there's a window created
0732         // Set the window's size from the existing widget geometry to respect the
0733         // implicit size when there is no saved geometry in the config file for
0734         // KWindowConfig::restoreWindowSize() to restore
0735         // TODO: remove once QTBUG-40584 is fixed; see below
0736         windowHandle()->setWidth(width());
0737         windowHandle()->setHeight(height());
0738         KWindowConfig::restoreWindowSize(windowHandle(), stateConfig);
0739         // NOTICE: QWindow::setGeometry() does NOT impact the backing QWidget geometry even if the platform
0740         // window was created -> QTBUG-40584. We therefore copy the size here.
0741         // TODO: remove once this was resolved in QWidget QPA
0742         resize(windowHandle()->size());
0743         d->sizeApplied = true;
0744 
0745         // Let the user opt out of KDE apps remembering window sizes if they
0746         // find it annoying or it doesn't work for them due to other bugs.
0747         KSharedConfigPtr config = KSharedConfig::openConfig();
0748         KConfigGroup group(config, "General");
0749         if (group.readEntry("AllowKDEAppsToRememberWindowPositions", true)) {
0750             if (stateConfig.readEntry("RestorePositionForNextInstance", true)) {
0751                 KWindowConfig::restoreWindowPosition(windowHandle(), stateConfig);
0752                 // Save the fact that we now don't want to restore position
0753                 // anymore; if we did, the next instance would completely cover
0754                 // the existing one
0755                 stateConfig.writeEntry("RestorePositionForNextInstance", false);
0756             }
0757         }
0758     }
0759 
0760     QStatusBar *sb = internalStatusBar(this);
0761     if (sb) {
0762         QString entry = cg.readEntry("StatusBar", "Enabled");
0763         sb->setVisible(entry != QLatin1String("Disabled"));
0764     }
0765 
0766     QMenuBar *mb = internalMenuBar(this);
0767     if (mb) {
0768         QString entry = cg.readEntry("MenuBar", "Enabled");
0769         mb->setVisible(entry != QLatin1String("Disabled"));
0770     }
0771 
0772     if (!autoSaveSettings() || cg.name() == autoSaveGroup()) { // TODO should be cg == d->autoSaveGroup, to compare both kconfig and group name
0773         QString entry = cg.readEntry("ToolBarsMovable", "Disabled");
0774         KToolBar::setToolBarsLocked(entry == QLatin1String("Disabled"));
0775     }
0776 
0777     int n = 1; // Toolbar counter. toolbars are counted from 1,
0778     const auto toolBars = this->toolBars();
0779     for (KToolBar *toolbar : toolBars) {
0780         QByteArray groupName("Toolbar");
0781         // Give a number to the toolbar, but prefer a name if there is one,
0782         // because there's no real guarantee on the ordering of toolbars
0783         groupName += (toolbar->objectName().isEmpty() ? QByteArray::number(n) : QByteArray(" ").append(toolbar->objectName().toUtf8()));
0784 
0785         KConfigGroup toolbarGroup(&cg, groupName.constData());
0786         toolbar->applySettings(toolbarGroup);
0787         n++;
0788     }
0789 
0790     if (stateConfig.hasKey("State")) {
0791         QByteArray state;
0792         state = stateConfig.readEntry("State", state);
0793         state = QByteArray::fromBase64(state);
0794         // One day will need to load the version number, but for now, assume 0
0795         restoreState(state);
0796     }
0797 
0798     if (focusedWidget) {
0799         focusedWidget->setFocus();
0800     }
0801 
0802     d->settingsDirty = false;
0803     d->letDirtySettings = oldLetDirtySettings;
0804 }
0805 
0806 #if KXMLGUI_BUILD_DEPRECATED_SINCE(5, 0)
0807 void KMainWindow::restoreWindowSize(const KConfigGroup &cg)
0808 {
0809     KWindowConfig::restoreWindowSize(windowHandle(), cg);
0810 }
0811 #endif
0812 
0813 #if KXMLGUI_BUILD_DEPRECATED_SINCE(5, 0)
0814 void KMainWindow::saveWindowSize(KConfigGroup &cg) const
0815 {
0816     KWindowConfig::saveWindowSize(windowHandle(), cg);
0817 }
0818 #endif
0819 
0820 void KMainWindow::setSettingsDirty()
0821 {
0822     Q_D(KMainWindow);
0823     d->setSettingsDirty();
0824 }
0825 
0826 bool KMainWindow::settingsDirty() const
0827 {
0828     Q_D(const KMainWindow);
0829     return d->settingsDirty;
0830 }
0831 
0832 void KMainWindow::setAutoSaveSettings(const QString &groupName, bool saveWindowSize)
0833 {
0834     setAutoSaveSettings(KConfigGroup(KSharedConfig::openConfig(), groupName), saveWindowSize);
0835 }
0836 
0837 void KMainWindow::setAutoSaveSettings(const KConfigGroup &group, bool saveWindowSize)
0838 {
0839     // We re making a little assumption that if you want to save the window
0840     // size, you probably also want to save the window position too
0841     // This avoids having to re-implement a new version of
0842     // KMainWindow::setAutoSaveSettings that handles these cases independently
0843     Q_D(KMainWindow);
0844     d->autoSaveSettings = true;
0845     d->autoSaveGroup = group;
0846     d->autoSaveWindowSize = saveWindowSize;
0847 
0848     if (!saveWindowSize && d->sizeTimer) {
0849         d->sizeTimer->stop();
0850     }
0851 
0852     // Now read the previously saved settings
0853     applyMainWindowSettings(d->autoSaveGroup);
0854 }
0855 
0856 void KMainWindow::resetAutoSaveSettings()
0857 {
0858     Q_D(KMainWindow);
0859     d->autoSaveSettings = false;
0860     if (d->settingsTimer) {
0861         d->settingsTimer->stop();
0862     }
0863 }
0864 
0865 bool KMainWindow::autoSaveSettings() const
0866 {
0867     Q_D(const KMainWindow);
0868     return d->autoSaveSettings;
0869 }
0870 
0871 QString KMainWindow::autoSaveGroup() const
0872 {
0873     Q_D(const KMainWindow);
0874     return d->autoSaveSettings ? d->autoSaveGroup.name() : QString();
0875 }
0876 
0877 KConfigGroup KMainWindow::autoSaveConfigGroup() const
0878 {
0879     Q_D(const KMainWindow);
0880     return d->autoSaveSettings ? d->autoSaveGroup : KConfigGroup();
0881 }
0882 
0883 void KMainWindow::setStateConfigGroup(const QString &configGroup)
0884 {
0885     Q_D(KMainWindow);
0886     d->m_stateConfigGroup = KSharedConfig::openStateConfig()->group(configGroup);
0887 }
0888 
0889 KConfigGroup KMainWindow::stateConfigGroup() const
0890 {
0891     Q_D(const KMainWindow);
0892     return d->m_stateConfigGroup;
0893 }
0894 
0895 void KMainWindow::saveAutoSaveSettings()
0896 {
0897     Q_D(KMainWindow);
0898     Q_ASSERT(d->autoSaveSettings);
0899     // qDebug(200) << "KMainWindow::saveAutoSaveSettings -> saving settings";
0900     saveMainWindowSettings(d->autoSaveGroup);
0901     d->autoSaveGroup.sync();
0902     d->autoSaveStateGroup().sync();
0903     d->settingsDirty = false;
0904 }
0905 
0906 bool KMainWindow::event(QEvent *ev)
0907 {
0908     Q_D(KMainWindow);
0909     switch (ev->type()) {
0910 #if defined(Q_OS_WIN) || defined(Q_OS_OSX)
0911     case QEvent::Move:
0912 #endif
0913     case QEvent::Resize:
0914         d->setSizeDirty();
0915         break;
0916     case QEvent::Polish:
0917         d->polish(this);
0918         break;
0919     case QEvent::ChildPolished: {
0920         QChildEvent *event = static_cast<QChildEvent *>(ev);
0921         QDockWidget *dock = qobject_cast<QDockWidget *>(event->child());
0922         KToolBar *toolbar = qobject_cast<KToolBar *>(event->child());
0923         QMenuBar *menubar = qobject_cast<QMenuBar *>(event->child());
0924         if (dock) {
0925             connect(dock, &QDockWidget::dockLocationChanged, this, &KMainWindow::setSettingsDirty);
0926             connect(dock, &QDockWidget::topLevelChanged, this, &KMainWindow::setSettingsDirty);
0927 
0928             // there is no signal emitted if the size of the dock changes,
0929             // hence install an event filter instead
0930             dock->installEventFilter(d->dockResizeListener);
0931         } else if (toolbar) {
0932             // there is no signal emitted if the size of the toolbar changes,
0933             // hence install an event filter instead
0934             toolbar->installEventFilter(d->dockResizeListener);
0935         } else if (menubar) {
0936             // there is no signal emitted if the size of the menubar changes,
0937             // hence install an event filter instead
0938             menubar->installEventFilter(d->dockResizeListener);
0939         }
0940         break;
0941     }
0942     case QEvent::ChildRemoved: {
0943         QChildEvent *event = static_cast<QChildEvent *>(ev);
0944         QDockWidget *dock = qobject_cast<QDockWidget *>(event->child());
0945         KToolBar *toolbar = qobject_cast<KToolBar *>(event->child());
0946         QMenuBar *menubar = qobject_cast<QMenuBar *>(event->child());
0947         if (dock) {
0948             disconnect(dock, &QDockWidget::dockLocationChanged, this, &KMainWindow::setSettingsDirty);
0949             disconnect(dock, &QDockWidget::topLevelChanged, this, &KMainWindow::setSettingsDirty);
0950             dock->removeEventFilter(d->dockResizeListener);
0951         } else if (toolbar) {
0952             toolbar->removeEventFilter(d->dockResizeListener);
0953         } else if (menubar) {
0954             menubar->removeEventFilter(d->dockResizeListener);
0955         }
0956         break;
0957     }
0958     default:
0959         break;
0960     }
0961     return QMainWindow::event(ev);
0962 }
0963 
0964 bool KMainWindow::hasMenuBar()
0965 {
0966     return internalMenuBar(this);
0967 }
0968 
0969 void KMainWindowPrivate::_k_slotSettingsChanged(int category)
0970 {
0971     Q_UNUSED(category);
0972 
0973     // This slot will be called when the style KCM changes settings that need
0974     // to be set on the already running applications.
0975 
0976     // At this level (KMainWindow) the only thing we need to restore is the
0977     // animations setting (whether the user wants builtin animations or not).
0978 
0979     q->setAnimated(q->style()->styleHint(QStyle::SH_Widget_Animate, nullptr, q));
0980 }
0981 
0982 void KMainWindowPrivate::_k_slotSaveAutoSaveSize()
0983 {
0984     if (autoSaveGroup.isValid()) {
0985         KWindowConfig::saveWindowSize(q->windowHandle(), autoSaveStateGroup());
0986     }
0987 }
0988 
0989 void KMainWindowPrivate::_k_slotSaveAutoSavePosition()
0990 {
0991     if (autoSaveGroup.isValid()) {
0992         KWindowConfig::saveWindowPosition(q->windowHandle(), autoSaveStateGroup());
0993     }
0994 }
0995 
0996 KToolBar *KMainWindow::toolBar(const QString &name)
0997 {
0998     QString childName = name;
0999     if (childName.isEmpty()) {
1000         childName = QStringLiteral("mainToolBar");
1001     }
1002 
1003     KToolBar *tb = findChild<KToolBar *>(childName);
1004     if (tb) {
1005         return tb;
1006     }
1007 
1008     KToolBar *toolbar = new KToolBar(childName, this); // non-XMLGUI toolbar
1009     return toolbar;
1010 }
1011 
1012 QList<KToolBar *> KMainWindow::toolBars() const
1013 {
1014     QList<KToolBar *> ret;
1015 
1016     const auto theChildren = children();
1017     for (QObject *child : theChildren) {
1018         if (KToolBar *toolBar = qobject_cast<KToolBar *>(child)) {
1019             ret.append(toolBar);
1020         }
1021     }
1022 
1023     return ret;
1024 }
1025 
1026 QList<KMainWindow *> KMainWindow::memberList()
1027 {
1028     return *sMemberList();
1029 }
1030 
1031 QString KMainWindow::dbusName() const
1032 {
1033     Q_D(const KMainWindow);
1034 
1035     return d->dbusName;
1036 }
1037 
1038 #include "kmainwindow.moc"
1039 #include "moc_kmainwindow.cpp"
1040 #include "moc_kmainwindow_p.cpp"