File indexing completed on 2024-03-24 04:02:25

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