File indexing completed on 2024-09-08 12:23:22
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"