File indexing completed on 2025-04-27 03:46:27
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"