KXmlGui

kmainwindow.cpp
1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2000 Reginald Stadlbauer <reggie@kde.org>
4 SPDX-FileCopyrightText: 1997 Stephan Kulow <coolo@kde.org>
5 SPDX-FileCopyrightText: 1997-2000 Sven Radej <radej@kde.org>
6 SPDX-FileCopyrightText: 1997-2000 Matthias Ettrich <ettrich@kde.org>
7 SPDX-FileCopyrightText: 1999 Chris Schlaeger <cs@kde.org>
8 SPDX-FileCopyrightText: 2002 Joseph Wenninger <jowenn@kde.org>
9 SPDX-FileCopyrightText: 2005-2006 Hamish Rodda <rodda@kde.org>
10 SPDX-FileCopyrightText: 2000-2008 David Faure <faure@kde.org>
11
12 SPDX-License-Identifier: LGPL-2.0-only
13*/
14
15#include "kmainwindow.h"
16
17#include "kactionconflictdetector_p.h"
18#include "kcheckaccelerators.h"
19#include "kmainwindow_p.h"
20#ifdef WITH_QTDBUS
21#include "kmainwindowiface_p.h"
22#endif
23#include "khelpmenu.h"
24#include "ktoolbar.h"
25#include "ktoolbarhandler_p.h"
26#include "ktooltiphelper.h"
27
28#include <QApplication>
29#include <QCloseEvent>
30#include <QDockWidget>
31#include <QFile>
32#include <QList>
33#include <QMenuBar>
34#include <QObject>
35#include <QRandomGenerator>
36#ifndef QT_NO_SESSIONMANAGER
37#include <QSessionManager>
38#endif
39#include <QStatusBar>
40#include <QStyle>
41#include <QTimer>
42#include <QWidget>
43#include <QWindow>
44#ifdef WITH_QTDBUS
45#include <QDBusConnection>
46#endif
47
48#include <KAboutData>
49#include <KConfig>
50#include <KConfigGroup>
51#include <KConfigGui>
52#include <KLocalizedString>
53#include <KSharedConfig>
54#include <KStandardShortcut>
55#include <KWindowConfig>
56
57static QMenuBar *internalMenuBar(KMainWindow *mw)
58{
60}
61
62static QStatusBar *internalStatusBar(KMainWindow *mw)
63{
65}
66
67/**
68
69 * Listens to resize events from QDockWidgets. The KMainWindow
70 * settings are set as dirty, as soon as at least one resize
71 * event occurred. The listener is attached to the dock widgets
72 * by dock->installEventFilter(dockResizeListener) inside
73 * KMainWindow::event().
74 */
75class DockResizeListener : public QObject
76{
78public:
79 DockResizeListener(KMainWindow *win);
80 ~DockResizeListener() override;
81 bool eventFilter(QObject *watched, QEvent *event) override;
82
83private:
84 KMainWindow *const m_win;
85};
86
87DockResizeListener::DockResizeListener(KMainWindow *win)
88 : QObject(win)
89 , m_win(win)
90{
91}
92
93DockResizeListener::~DockResizeListener()
94{
95}
96
97bool DockResizeListener::eventFilter(QObject *watched, QEvent *event)
98{
99 switch (event->type()) {
100 case QEvent::Resize:
101 case QEvent::Move:
102 case QEvent::Show:
103 case QEvent::Hide:
104 m_win->d_ptr->setSettingsDirty(KMainWindowPrivate::CompressCalls);
105 break;
106
107 default:
108 break;
109 }
110
111 return QObject::eventFilter(watched, event);
112}
113
114KMWSessionManager::KMWSessionManager()
115{
116#ifndef QT_NO_SESSIONMANAGER
117 connect(qApp, &QGuiApplication::saveStateRequest, this, &KMWSessionManager::saveState);
118 connect(qApp, &QGuiApplication::commitDataRequest, this, &KMWSessionManager::commitData);
119#endif
120}
121
122KMWSessionManager::~KMWSessionManager()
123{
124}
125
126void KMWSessionManager::saveState(QSessionManager &sm)
127{
128#ifndef QT_NO_SESSIONMANAGER
130
132 const auto windows = KMainWindow::memberList();
133 if (!windows.isEmpty()) {
134 // According to Jochen Wilhelmy <digisnap@cs.tu-berlin.de>, this
135 // hook is useful for better document orientation
136 windows.at(0)->saveGlobalProperties(config);
137 }
138
139 int n = 0;
140 for (KMainWindow *mw : windows) {
141 n++;
142 mw->savePropertiesInternal(config, n);
143 }
144
145 KConfigGroup group(config, QStringLiteral("Number"));
146 group.writeEntry("NumberOfWindows", n);
147
148 // store new status to disk
149 config->sync();
150
151 // generate discard command for new file
153 if (QFile::exists(localFilePath)) {
155 discard << QStringLiteral("rm");
156 discard << localFilePath;
157 sm.setDiscardCommand(discard);
158 }
159#else
160 Q_UNUSED(sm)
161#endif // QT_NO_SESSIONMANAGER
162}
163
164void KMWSessionManager::commitData(QSessionManager &sm)
165{
166#ifndef QT_NO_SESSIONMANAGER
167 if (!sm.allowsInteraction()) {
168 return;
169 }
170
171 /*
172 Purpose of this exercise: invoke queryClose() without actually closing the
173 windows, because
174 - queryClose() may contain session management code, so it must be invoked
175 - actually closing windows may quit the application - cf.
176 QGuiApplication::quitOnLastWindowClosed()
177 - quitting the application and thus closing the session manager connection
178 violates the X11 XSMP protocol.
179 The exact requirement of XSMP that would be broken is,
180 in the description of the client's state machine:
181
182 save-yourself-done: (changing state is forbidden)
183
184 Closing the session manager connection causes a state change.
185 Worst of all, that is a real problem with ksmserver - it will not save
186 applications that quit on their own in state save-yourself-done.
187 */
188 const auto windows = KMainWindow::memberList();
189 for (KMainWindow *window : windows) {
191 continue;
192 }
193 QCloseEvent e;
194 QApplication::sendEvent(window, &e);
195 if (!e.isAccepted()) {
196 sm.cancel();
197 return;
198 }
199 }
200#else
201 Q_UNUSED(sm)
202#endif // QT_NO_SESSIONMANAGER
203}
204
205#ifndef QT_NO_SESSIONMANAGER
206Q_GLOBAL_STATIC(KMWSessionManager, ksm)
207#endif
208Q_GLOBAL_STATIC(QList<KMainWindow *>, sMemberList)
209
211 : QMainWindow(parent, flags)
212 , d_ptr(new KMainWindowPrivate)
213{
215
216 d->init(this);
217}
218
219KMainWindow::KMainWindow(KMainWindowPrivate &dd, QWidget *parent, Qt::WindowFlags f)
220 : QMainWindow(parent, f)
221 , d_ptr(&dd)
222{
224
225 d->init(this);
226}
227
228void KMainWindowPrivate::init(KMainWindow *_q)
229{
230 q = _q;
231
232 q->setAnimated(q->style()->styleHint(QStyle::SH_Widget_Animate, nullptr, q));
233
234 q->setAttribute(Qt::WA_DeleteOnClose);
235
236 helpMenu = nullptr;
237
238 // actionCollection()->setWidget( this );
239#if 0
240 QObject::connect(KGlobalSettings::self(), SIGNAL(settingsChanged(int)),
241 q, SLOT(_k_slotSettingsChanged(int)));
242#endif
243
244#ifndef QT_NO_SESSIONMANAGER
245 // force KMWSessionManager creation
246 ksm();
247#endif
248
249 sMemberList()->append(q);
250
251 // If application is translated, load translator information for use in
252 // KAboutApplicationDialog or other getters. The context and messages below
253 // both must be exactly as listed, and are forced to be loaded from the
254 // application's own message catalog instead of kxmlgui's.
255 KAboutData aboutData(KAboutData::applicationData());
256 if (aboutData.translators().isEmpty()) {
257 aboutData.setTranslator(i18ndc(nullptr, "NAME OF TRANSLATORS", "Your names"), //
258 i18ndc(nullptr, "EMAIL OF TRANSLATORS", "Your emails"));
259
261 }
262
263 settingsDirty = false;
264 autoSaveSettings = false;
265 autoSaveWindowSize = true; // for compatibility
266 // d->kaccel = actionCollection()->kaccel();
267 settingsTimer = nullptr;
268 sizeTimer = nullptr;
269
270 dockResizeListener = new DockResizeListener(_q);
271 letDirtySettings = true;
272
273 sizeApplied = false;
274 suppressCloseEvent = false;
275
276 qApp->installEventFilter(KToolTipHelper::instance());
277
278 // create the conflict detector only if some main window got created
279 // before we did that on library load, that might mess with plain Qt applications
280 // see bug 467130
281 static QPointer<KActionConflictDetector> conflictDetector;
282 if (!conflictDetector) {
283 conflictDetector = new KActionConflictDetector(QCoreApplication::instance());
285 }
286
287 // same for accelerator checking
288 KCheckAccelerators::initiateIfNeeded();
289}
290
291static bool endsWithHashNumber(const QString &s)
292{
293 for (int i = s.length() - 1; i > 0; --i) {
294 if (s[i] == QLatin1Char('#') && i != s.length() - 1) {
295 return true; // ok
296 }
297 if (!s[i].isDigit()) {
298 break;
299 }
300 }
301 return false;
302}
303
304static inline bool isValidDBusObjectPathCharacter(const QChar &c)
305{
306 ushort u = c.unicode();
307 /* clang-format off */
308 return (u >= QLatin1Char('a') && u <= QLatin1Char('z'))
309 || (u >= QLatin1Char('A') && u <= QLatin1Char('Z'))
310 || (u >= QLatin1Char('0') && u <= QLatin1Char('9'))
311 || (u == QLatin1Char('_')) || (u == QLatin1Char('/'));
312 /* clang-format off */
313}
314
315void KMainWindowPrivate::polish(KMainWindow *q)
316{
317 // Set a unique object name. Required by session management, window management, and for the dbus interface.
318 QString objname;
319 QString s;
320 int unusedNumber = 1;
321 const QString name = q->objectName();
322 bool startNumberingImmediately = true;
323 bool tryReuse = false;
324 if (name.isEmpty()) {
325 // no name given
326 objname = QStringLiteral("MainWindow#");
327 } else if (name.endsWith(QLatin1Char('#'))) {
328 // trailing # - always add a number - KWin uses this for better grouping
329 objname = name;
330 } else if (endsWithHashNumber(name)) {
331 // trailing # with a number - like above, try to use the given number first
332 objname = name;
333 tryReuse = true;
334 startNumberingImmediately = false;
335 } else {
336 objname = name;
337 startNumberingImmediately = false;
338 }
339
340 s = objname;
341 if (startNumberingImmediately) {
342 s += QLatin1Char('1');
343 }
344
345 for (;;) {
346 const QList<QWidget *> list = qApp->topLevelWidgets();
347 bool found = false;
348 for (QWidget *w : list) {
349 if (w != q && w->objectName() == s) {
350 found = true;
351 break;
352 }
353 }
354 if (!found) {
355 break;
356 }
357 if (tryReuse) {
358 objname = name.left(name.length() - 1); // lose the hash
359 unusedNumber = 0; // start from 1 below
360 tryReuse = false;
361 }
362 s.setNum(++unusedNumber);
363 s = objname + s;
364 }
365 q->setObjectName(s);
366 if (!q->window() || q->window() == q) {
367 q->winId(); // workaround for setWindowRole() crashing, and set also window role, just in case TT
368 q->setWindowRole(s); // will keep insisting that object name suddenly should not be used for window role
369 }
370
371 dbusName = QLatin1Char('/') + QCoreApplication::applicationName() + QLatin1Char('/');
372 dbusName += q->objectName().replace(QLatin1Char('/'), QLatin1Char('_'));
373 // Clean up for dbus usage: any non-alphanumeric char should be turned into '_'
374 for (QChar &c : dbusName) {
375 if (!isValidDBusObjectPathCharacter(c)) {
376 c = QLatin1Char('_');
377 }
378 }
379
380#ifdef WITH_QTDBUS
381 /* clang-format off */
382 constexpr auto opts = QDBusConnection::ExportScriptableSlots
387 /* clang-format on */
388 QDBusConnection::sessionBus().registerObject(dbusName, q, opts);
389#endif
390}
391
392void KMainWindowPrivate::setSettingsDirty(CallCompression callCompression)
393{
394 if (!letDirtySettings) {
395 return;
396 }
397
398 settingsDirty = true;
399 if (autoSaveSettings) {
400 if (callCompression == CompressCalls) {
401 if (!settingsTimer) {
402 settingsTimer = new QTimer(q);
403 // don't trigger sync of config always at same time to avoid clashes if x instances are running
404 settingsTimer->setInterval(QRandomGenerator::global()->bounded(500, 1500));
405 settingsTimer->setSingleShot(true);
407 }
408 settingsTimer->start();
409 } else {
411 }
412 }
413}
414
415void KMainWindowPrivate::setSizeDirty()
416{
417 if (autoSaveWindowSize) {
418 if (!sizeTimer) {
419 sizeTimer = new QTimer(q);
420 // don't trigger sync of config always at same time to avoid clashes if x instances are running
421 sizeTimer->setInterval(QRandomGenerator::global()->bounded(500, 1500));
422 sizeTimer->setSingleShot(true);
423 QObject::connect(sizeTimer, &QTimer::timeout, q, [this]() {
424 _k_slotSaveAutoSaveSize();
425 });
426 }
427 sizeTimer->start();
428 }
429}
430
432{
433 sMemberList()->removeAll(this);
434 delete static_cast<QObject *>(d_ptr->dockResizeListener); // so we don't get anymore events after d_ptr is destroyed
435}
436
437bool KMainWindow::canBeRestored(int numberOfInstances)
438{
440 if (!config) {
441 return false;
442 }
443
444 KConfigGroup group(config, QStringLiteral("Number"));
445 // TODO KF6: we should use 0 as the default value, not 1
446 // See also https://bugs.kde.org/show_bug.cgi?id=427552
447 const int n = group.readEntry("NumberOfWindows", 1);
448 return numberOfInstances >= 1 && numberOfInstances <= n;
449}
450
452{
454 if (!config) {
455 return QString();
456 }
457
458 KConfigGroup group(config, QStringLiteral("WindowProperties%1").arg(instanceNumber));
459 if (!group.hasKey("ClassName")) {
460 return QString();
461 } else {
462 return group.readEntry("ClassName");
463 }
464}
465
466bool KMainWindow::restore(int numberOfInstances, bool show)
467{
468 if (!canBeRestored(numberOfInstances)) {
469 return false;
470 }
472 if (readPropertiesInternal(config, numberOfInstances)) {
473 if (show) {
475 }
476 return false;
477 }
478 return false;
479}
480
482{
483 setPlainCaption(caption);
484}
485
486void KMainWindow::setCaption(const QString &caption, bool modified)
487{
488 QString title = caption;
489 if (!title.contains(QLatin1String("[*]")) && !title.isEmpty()) { // append the placeholder so that the modified mechanism works
490 title.append(QLatin1String(" [*]"));
491 }
492 setPlainCaption(title);
493 setWindowModified(modified);
494}
495
497{
498 setWindowTitle(caption);
499}
500
502{
504 if (!d->helpMenu) {
505 d->helpMenu = new KHelpMenu(this);
506 if (!d->helpMenu) {
507 return;
508 }
509 }
510 d->helpMenu->appHelpActivated();
511}
512
514{
516 if (d->suppressCloseEvent) {
517 e->accept();
518 return;
519 }
520
521 // Save settings if auto-save is enabled, and settings have changed
522 if (d->settingsTimer && d->settingsTimer->isActive()) {
523 d->settingsTimer->stop();
525 }
526 if (d->sizeTimer && d->sizeTimer->isActive()) {
527 d->sizeTimer->stop();
528 d->_k_slotSaveAutoSaveSize();
529 }
530 // Delete the marker that says we don't want to restore the position of the
531 // next-opened instance; now that a window is closing, we do want to do this
532 if (d->getStateConfig().isValid()) {
533 d->getStateConfig().deleteEntry("RestorePositionForNextInstance");
534 }
535 d->_k_slotSaveAutoSavePosition();
536
537 if (queryClose()) {
538 // widgets will start destroying themselves at this point and we don't
539 // want to save state anymore after this as it might be incorrect
540 d->autoSaveSettings = false;
541 d->letDirtySettings = false;
542 e->accept();
543 } else {
544 e->ignore(); // if the window should not be closed, don't close it
545 }
546
547#ifndef QT_NO_SESSIONMANAGER
548 // If saving session, we are processing a fake close event, and might get the real one later.
549 if (e->isAccepted() && qApp->isSavingSession()) {
550 d->suppressCloseEvent = true;
551 }
552#endif
553}
554
556{
557 return true;
558}
559
563
567
568void KMainWindow::savePropertiesInternal(KConfig *config, int number)
569{
571 const bool oldASWS = d->autoSaveWindowSize;
572 d->autoSaveWindowSize = true; // make saveMainWindowSettings save the window size
573
574 KConfigGroup cg(config, QStringLiteral("WindowProperties%1").arg(number));
575
576 // store objectName, className, Width and Height for later restoring
577 // (Only useful for session management)
578 cg.writeEntry("ObjectName", objectName());
579 cg.writeEntry("ClassName", metaObject()->className());
580
581 saveMainWindowSettings(cg); // Menubar, statusbar and Toolbar settings.
582
583 cg = KConfigGroup(config, QString::number(number));
584 saveProperties(cg);
585
586 d->autoSaveWindowSize = oldASWS;
587}
588
590{
592 // qDebug(200) << "KMainWindow::saveMainWindowSettings " << cg.name();
593
594 // Called by session management - or if we want to save the window size anyway
595 if (d->autoSaveWindowSize) {
596 KWindowConfig::saveWindowSize(windowHandle(), d->getStateConfig());
597 KWindowConfig::saveWindowPosition(windowHandle(), d->getStateConfig());
598 }
599
600 // One day will need to save the version number, but for now, assume 0
601 // Utilise the QMainWindow::saveState() functionality.
602 const QByteArray state = saveState();
603 d->getStateConfig().writeEntry("State", state.toBase64());
604
605 QStatusBar *sb = internalStatusBar(this);
606 if (sb) {
607 if (!cg.hasDefault("StatusBar") && !sb->isHidden()) {
608 cg.revertToDefault("StatusBar");
609 } else {
610 cg.writeEntry("StatusBar", sb->isHidden() ? "Disabled" : "Enabled");
611 }
612 }
613
614 QMenuBar *mb = internalMenuBar(this);
615
616 if (mb && !mb->isNativeMenuBar()) {
617 if (!cg.hasDefault("MenuBar") && !mb->isHidden()) {
618 cg.revertToDefault("MenuBar");
619 } else {
620 cg.writeEntry("MenuBar", mb->isHidden() ? "Disabled" : "Enabled");
621 }
622 }
623
624 if (!autoSaveSettings() || cg.name() == autoSaveGroup()) {
625 // TODO should be cg == d->autoSaveGroup, to compare both kconfig and group name
626 if (!cg.hasDefault("ToolBarsMovable") && KToolBar::toolBarsLocked()) {
627 cg.revertToDefault("ToolBarsMovable");
628 } else {
629 cg.writeEntry("ToolBarsMovable", KToolBar::toolBarsLocked() ? "Disabled" : "Enabled");
630 }
631 }
632
633 int n = 1; // Toolbar counter. toolbars are counted from 1,
634 const auto toolBars = this->toolBars();
635 for (KToolBar *toolbar : toolBars) {
636 // Give a number to the toolbar, but prefer a name if there is one,
637 // because there's no real guarantee on the ordering of toolbars
638 const QString groupName = toolbar->objectName().isEmpty() ? QStringLiteral("Toolbar%1").arg(n) : (QStringLiteral("Toolbar ") + toolbar->objectName());
639
640 KConfigGroup toolbarGroup(&cg, groupName);
641 toolbar->saveSettings(toolbarGroup);
642 n++;
643 }
644}
645
646bool KMainWindow::readPropertiesInternal(KConfig *config, int number)
647{
649
650 const bool oldLetDirtySettings = d->letDirtySettings;
651 d->letDirtySettings = false;
652
653 if (number == 1) {
654 readGlobalProperties(config);
655 }
656
657 // in order they are in toolbar list
658 KConfigGroup cg(config, QStringLiteral("WindowProperties%1").arg(number));
659
660 // restore the object name (window role)
661 if (cg.hasKey("ObjectName")) {
662 setObjectName(cg.readEntry("ObjectName"));
663 }
664
665 d->sizeApplied = false; // since we are changing config file, reload the size of the window
666 // if necessary. Do it before the call to applyMainWindowSettings.
667 applyMainWindowSettings(cg); // Menubar, statusbar and toolbar settings.
668
669 KConfigGroup grp(config, QString::number(number));
670 readProperties(grp);
671
672 d->letDirtySettings = oldLetDirtySettings;
673
674 return true;
675}
676
678{
680 // qDebug(200) << "KMainWindow::applyMainWindowSettings " << cg.name();
681
682 KConfigGroup cg = _cg;
683 d->migrateStateDataIfNeeded(cg);
684
685 QWidget *focusedWidget = QApplication::focusWidget();
686
687 const bool oldLetDirtySettings = d->letDirtySettings;
688 d->letDirtySettings = false;
689
690 KConfigGroup stateConfig = d->getStateConfig();
691
692 if (!d->sizeApplied && (!window() || window() == this)) {
693 winId(); // ensure there's a window created
694 // Set the window's size from the existing widget geometry to respect the
695 // implicit size when there is no saved geometry in the config file for
696 // KWindowConfig::restoreWindowSize() to restore
697 // TODO: remove once QTBUG-40584 is fixed; see below
701 // NOTICE: QWindow::setGeometry() does NOT impact the backing QWidget geometry even if the platform
702 // window was created -> QTBUG-40584. We therefore copy the size here.
703 // TODO: remove once this was resolved in QWidget QPA
705 d->sizeApplied = true;
706
707 // Let the user opt out of KDE apps remembering window sizes if they
708 // find it annoying or it doesn't work for them due to other bugs.
709 KSharedConfigPtr config = KSharedConfig::openConfig();
710 KConfigGroup group(config, QStringLiteral("General"));
711 if (group.readEntry("AllowKDEAppsToRememberWindowPositions", true)) {
712 if (stateConfig.readEntry("RestorePositionForNextInstance", true)) {
714 // Save the fact that we now don't want to restore position
715 // anymore; if we did, the next instance would completely cover
716 // the existing one
717 stateConfig.writeEntry("RestorePositionForNextInstance", false);
718 }
719 }
720 }
721
722 QStatusBar *sb = internalStatusBar(this);
723 if (sb) {
724 QString entry = cg.readEntry("StatusBar", "Enabled");
725 sb->setVisible(entry != QLatin1String("Disabled"));
726 }
727
728 QMenuBar *mb = internalMenuBar(this);
729 if (mb && !mb->isNativeMenuBar()) {
730 QString entry = cg.readEntry("MenuBar", "Enabled");
731 mb->setVisible(entry != QLatin1String("Disabled"));
732 }
733
734 if (!autoSaveSettings() || cg.name() == autoSaveGroup()) { // TODO should be cg == d->autoSaveGroup, to compare both kconfig and group name
735 QString entry = cg.readEntry("ToolBarsMovable", "Disabled");
736 KToolBar::setToolBarsLocked(entry == QLatin1String("Disabled"));
737 }
738
739 int n = 1; // Toolbar counter. toolbars are counted from 1,
740 const auto toolBars = this->toolBars();
741 for (KToolBar *toolbar : toolBars) {
742 // Give a number to the toolbar, but prefer a name if there is one,
743 // because there's no real guarantee on the ordering of toolbars
744 const QString groupName = toolbar->objectName().isEmpty() ? QStringLiteral("Toolbar%1").arg(n) : (QStringLiteral("Toolbar ") + toolbar->objectName());
745
746 KConfigGroup toolbarGroup(&cg, groupName);
747 toolbar->applySettings(toolbarGroup);
748 n++;
749 }
750
751 if (stateConfig.hasKey("State")) {
752 QByteArray state;
753 state = stateConfig.readEntry("State", state);
754 state = QByteArray::fromBase64(state);
755 // One day will need to load the version number, but for now, assume 0
756 restoreState(state);
757 }
758
759 if (focusedWidget) {
760 focusedWidget->setFocus();
761 }
762
763 d->settingsDirty = false;
764 d->letDirtySettings = oldLetDirtySettings;
765}
766
768{
770 d->setSettingsDirty();
771}
772
774{
775 Q_D(const KMainWindow);
776 return d->settingsDirty;
777}
778
779void KMainWindow::setAutoSaveSettings(const QString &groupName, bool saveWindowSize)
780{
781 setAutoSaveSettings(KConfigGroup(KSharedConfig::openConfig(), groupName), saveWindowSize);
782}
783
784void KMainWindow::setAutoSaveSettings(const KConfigGroup &group, bool saveWindowSize)
785{
786 // We re making a little assumption that if you want to save the window
787 // size, you probably also want to save the window position too
788 // This avoids having to re-implement a new version of
789 // KMainWindow::setAutoSaveSettings that handles these cases independently
791 d->autoSaveSettings = true;
792 d->autoSaveGroup = group;
793 d->autoSaveWindowSize = saveWindowSize;
794
795 if (!saveWindowSize && d->sizeTimer) {
796 d->sizeTimer->stop();
797 }
798
799 // Now read the previously saved settings
800 applyMainWindowSettings(d->autoSaveGroup);
801}
802
804{
806 d->autoSaveSettings = false;
807 if (d->settingsTimer) {
808 d->settingsTimer->stop();
809 }
810}
811
812bool KMainWindow::autoSaveSettings() const
813{
814 Q_D(const KMainWindow);
815 return d->autoSaveSettings;
816}
817
818QString KMainWindow::autoSaveGroup() const
819{
820 Q_D(const KMainWindow);
821 return d->autoSaveSettings ? d->autoSaveGroup.name() : QString();
822}
823
825{
826 Q_D(const KMainWindow);
827 return d->autoSaveSettings ? d->autoSaveGroup : KConfigGroup();
828}
829
831{
833 d->m_stateConfigGroup = KSharedConfig::openStateConfig()->group(configGroup);
834}
835
837{
838 Q_D(const KMainWindow);
839 return d->getStateConfig();
840}
841
843{
845 Q_ASSERT(d->autoSaveSettings);
846 // qDebug(200) << "KMainWindow::saveAutoSaveSettings -> saving settings";
847 saveMainWindowSettings(d->autoSaveGroup);
848 d->autoSaveGroup.sync();
849 d->m_stateConfigGroup.sync();
850 d->settingsDirty = false;
851}
852
854{
856 switch (ev->type()) {
857#if defined(Q_OS_WIN) || defined(Q_OS_OSX)
858 case QEvent::Move:
859#endif
860 case QEvent::Resize:
861 d->setSizeDirty();
862 break;
863 case QEvent::Polish:
864 d->polish(this);
865 break;
867 QChildEvent *event = static_cast<QChildEvent *>(ev);
869 KToolBar *toolbar = qobject_cast<KToolBar *>(event->child());
870 QMenuBar *menubar = qobject_cast<QMenuBar *>(event->child());
871 if (dock) {
874
875 // there is no signal emitted if the size of the dock changes,
876 // hence install an event filter instead
877 dock->installEventFilter(d->dockResizeListener);
878 } else if (toolbar) {
879 // there is no signal emitted if the size of the toolbar changes,
880 // hence install an event filter instead
881 toolbar->installEventFilter(d->dockResizeListener);
882 } else if (menubar) {
883 // there is no signal emitted if the size of the menubar changes,
884 // hence install an event filter instead
885 menubar->installEventFilter(d->dockResizeListener);
886 }
887 break;
888 }
890 QChildEvent *event = static_cast<QChildEvent *>(ev);
892 KToolBar *toolbar = qobject_cast<KToolBar *>(event->child());
893 QMenuBar *menubar = qobject_cast<QMenuBar *>(event->child());
894 if (dock) {
897 dock->removeEventFilter(d->dockResizeListener);
898 } else if (toolbar) {
899 toolbar->removeEventFilter(d->dockResizeListener);
900 } else if (menubar) {
901 menubar->removeEventFilter(d->dockResizeListener);
902 }
903 break;
904 }
905 default:
906 break;
907 }
908 return QMainWindow::event(ev);
909}
910
912{
913 if (KStandardShortcut::openContextMenu().contains(QKeySequence(keyEvent->key() | keyEvent->modifiers()))) {
914 if (QWidget *widgetWithKeyboardFocus = qApp->focusWidget()) {
915 const QPoint centerOfWidget(widgetWithKeyboardFocus->width() / 2, widgetWithKeyboardFocus->height() / 2);
916 qApp->postEvent(widgetWithKeyboardFocus,
917 new QContextMenuEvent(QContextMenuEvent::Keyboard, centerOfWidget, widgetWithKeyboardFocus->mapToGlobal(centerOfWidget)));
918 return;
919 }
920 if (qApp->focusObject()) {
921 qApp->postEvent(qApp->focusObject(), new QContextMenuEvent(QContextMenuEvent::Keyboard, mapFromGlobal(QCursor::pos()), QCursor::pos()));
922 return;
923 }
924 }
926}
927
928bool KMainWindow::hasMenuBar()
929{
930 return internalMenuBar(this);
931}
932
933void KMainWindowPrivate::_k_slotSettingsChanged(int category)
934{
935 Q_UNUSED(category);
936
937 // This slot will be called when the style KCM changes settings that need
938 // to be set on the already running applications.
939
940 // At this level (KMainWindow) the only thing we need to restore is the
941 // animations setting (whether the user wants builtin animations or not).
942
944}
945
946void KMainWindowPrivate::_k_slotSaveAutoSaveSize()
947{
948 if (autoSaveGroup.isValid()) {
949 KWindowConfig::saveWindowSize(q->windowHandle(), getStateConfig());
950 }
951}
952
953void KMainWindowPrivate::_k_slotSaveAutoSavePosition()
954{
955 if (autoSaveGroup.isValid()) {
956 KWindowConfig::saveWindowPosition(q->windowHandle(), getStateConfig());
957 }
958}
959
961{
962 QString childName = name;
963 if (childName.isEmpty()) {
964 childName = QStringLiteral("mainToolBar");
965 }
966
967 KToolBar *tb = findChild<KToolBar *>(childName);
968 if (tb) {
969 return tb;
970 }
971
972 KToolBar *toolbar = new KToolBar(childName, this); // non-XMLGUI toolbar
973 return toolbar;
974}
975
977{
979
980 const auto theChildren = children();
981 for (QObject *child : theChildren) {
983 ret.append(toolBar);
984 }
985 }
986
987 return ret;
988}
989
991{
992 return *sMemberList();
993}
994
996{
997 Q_D(const KMainWindow);
998
999 return d->dbusName;
1000}
1001
1002#include "kmainwindow.moc"
1003#include "moc_kmainwindow.cpp"
1004#include "moc_kmainwindow_p.cpp"
static void setApplicationData(const KAboutData &aboutData)
static KAboutData applicationData()
QString name() const
void revertToDefault(const char *key, WriteConfigFlags pFlag=WriteConfigFlags())
bool hasDefault(const char *key) const
bool hasKey(const char *key) const
void writeEntry(const char *key, const char *value, WriteConfigFlags pFlags=Normal)
QString readEntry(const char *key, const char *aDefault=nullptr) const
bool sync() override
QString name() const
Standard KDE help menu with dialog boxes.
Definition khelpmenu.h:109
KMainWindow represents a top-level main window.
Definition kmainwindow.h:60
void appHelpActivated()
Opens the help page for the application.
virtual void applyMainWindowSettings(const KConfigGroup &config)
Read settings for statusbar, menubar and toolbar from their respective groups in the config file and ...
KConfigGroup stateConfigGroup() const
bool restore(int numberOfInstances, bool show=true)
Attempt to restore the top-level widget as defined by numberOfInstances (1..X).
KToolBar * toolBar(const QString &name=QString())
This is useful to both call specific toolbars that have been created or to generate a default one upo...
virtual void readGlobalProperties(KConfig *sessionConfig)
Reads your application-wide properties.
bool event(QEvent *event) override
Reimplemented to catch QEvent::Polish in order to adjust the object name if needed,...
void saveAutoSaveSettings()
This slot should only be called in case you reimplement closeEvent() and if you are using the autosav...
void setAutoSaveSettings(const QString &groupName=QStringLiteral("MainWindow"), bool saveWindowSize=true)
This enables autosave of toolbar/menubar/statusbar settings (and optionally window size).
void closeEvent(QCloseEvent *) override
Reimplemented to autosave settings and call queryClose().
virtual void saveProperties(KConfigGroup &)
Saves your instance-specific properties.
void resetAutoSaveSettings()
Disables the autosave settings feature.
void setSettingsDirty()
Tell the main window that it should save its settings when being closed.
KConfigGroup autoSaveConfigGroup() const
QList< KToolBar * > toolBars() const
void saveMainWindowSettings(KConfigGroup &config)
Manually save the settings for statusbar, menubar and toolbar to their respective groups in the KConf...
static const QString classNameOfToplevel(int instanceNumber)
Useful if your application uses different kinds of top-level windows.
virtual void setCaption(const QString &caption)
Assigns a KDE compliant caption (window title).
static QList< KMainWindow * > memberList()
~KMainWindow() override
Destructor.
virtual void setPlainCaption(const QString &caption)
Make a plain caption without any modifications.
void setStateConfigGroup(const QString &configGroup)
Assigns the config group name for the KConfigGroup returned by stateConfigGroup.
KMainWindow(QWidget *parent=nullptr, Qt::WindowFlags flags=Qt::WindowFlags())
Constructs a main window.
virtual void readProperties(const KConfigGroup &)
Reads your instance-specific properties.
virtual void saveGlobalProperties(KConfig *sessionConfig)
Saves your application-wide properties.
bool settingsDirty() const
For inherited classes.
static bool canBeRestored(int numberOfInstances)
void keyPressEvent(QKeyEvent *keyEvent) override
Reimplemented to open context menus on Shift+F10.
QString dbusName() const
virtual bool queryClose()
This function is called before the window is closed, either by the user or indirectly by the session ...
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
static KSharedConfig::Ptr openStateConfig(const QString &fileName=QString())
Floatable toolbar with auto resize.
Definition ktoolbar.h:68
static bool toolBarsLocked()
Returns whether the toolbars are locked (i.e., moving of the toobars disallowed).
static void setToolBarsLocked(bool locked)
Allows you to lock and unlock all toolbars (i.e., disallow/allow moving of the toobars).
QString i18ndc(const char *domain, const char *context, const char *text, const TYPE &arg...)
KCONFIGGUI_EXPORT void setSessionConfig(const QString &id, const QString &key)
KCONFIGGUI_EXPORT KConfig * sessionConfig()
QString name(const QVariant &location)
QWidget * window(QObject *job)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
KGuiItem discard()
const QList< QKeySequence > & openContextMenu()
KCONFIGGUI_EXPORT void saveWindowSize(const QWindow *window, KConfigGroup &config, KConfigGroup::WriteConfigFlags options=KConfigGroup::Normal)
KCONFIGGUI_EXPORT void saveWindowPosition(const QWindow *window, KConfigGroup &config, KConfigGroup::WriteConfigFlags options=KConfigGroup::Normal)
KCONFIGGUI_EXPORT void restoreWindowSize(QWindow *window, const KConfigGroup &config)
KCONFIGGUI_EXPORT void restoreWindowPosition(QWindow *window, const KConfigGroup &config)
QWidget * focusWidget()
QByteArray fromBase64(const QByteArray &base64, Base64Options options)
QByteArray toBase64(Base64Options options) const const
char16_t & unicode()
QCoreApplication * instance()
bool sendEvent(QObject *receiver, QEvent *event)
QPoint pos()
bool registerObject(const QString &path, QObject *object, RegisterOptions options)
QDBusConnection sessionBus()
void dockLocationChanged(Qt::DockWidgetArea area)
void topLevelChanged(bool topLevel)
void accept()
bool isAccepted() const const
void ignore()
Type type() const const
bool exists(const QString &fileName)
void commitDataRequest(QSessionManager &manager)
void saveStateRequest(QSessionManager &manager)
void append(QList< T > &&value)
QMainWindow(QWidget *parent, Qt::WindowFlags flags)
void setAnimated(bool enabled)
virtual bool event(QEvent *event) override
bool restoreState(const QByteArray &state, int version)
QByteArray saveState(int version) const const
bool isNativeMenuBar() const const
virtual void setVisible(bool visible) override
QObject(QObject *parent)
Q_OBJECTQ_OBJECT
const QObjectList & children() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
virtual bool event(QEvent *e)
virtual bool eventFilter(QObject *watched, QEvent *event)
T findChild(const QString &name, Qt::FindChildOptions options) const const
void installEventFilter(QObject *filterObj)
virtual const QMetaObject * metaObject() const const
QObject * parent() const const
T qobject_cast(QObject *object)
void removeEventFilter(QObject *obj)
void setObjectName(QAnyStringView name)
QRandomGenerator * global()
bool allowsInteraction()
QString sessionId() const const
QString sessionKey() const const
void setDiscardCommand(const QStringList &command)
QString writableLocation(StandardLocation type)
QString & append(QChar ch)
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
QString left(qsizetype n) const const
qsizetype length() const const
QString number(double n, char format, int precision)
QString & setNum(double n, char format, int precision)
SH_Widget_Animate
virtual int styleHint(StyleHint hint, const QStyleOption *option, const QWidget *widget, QStyleHintReturn *returnData) const const=0
FindDirectChildrenOnly
WA_WState_Hidden
typedef WindowFlags
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void timeout()
QWidget(QWidget *parent, Qt::WindowFlags f)
bool isHidden() const const
virtual void keyPressEvent(QKeyEvent *event)
QPoint mapFromGlobal(const QPoint &pos) const const
void setFocus()
void setWindowRole(const QString &role)
void show()
void resize(const QSize &)
QStyle * style() const const
bool testAttribute(Qt::WidgetAttribute attribute) const const
virtual void setVisible(bool visible)
WId winId() const const
QWidget * window() const const
QWindow * windowHandle() const const
void setWindowModified(bool)
void setWindowTitle(const QString &)
void setHeight(int arg)
void setWidth(int arg)
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Feb 21 2025 11:48:21 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.