kmainwindow.cpp 30 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
/*
    This file is part of the KDE libraries
    SPDX-FileCopyrightText: 2000 Reginald Stadlbauer <reggie@kde.org>
    SPDX-FileCopyrightText: 1997 Stephan Kulow <coolo@kde.org>
    SPDX-FileCopyrightText: 1997-2000 Sven Radej <radej@kde.org>
    SPDX-FileCopyrightText: 1997-2000 Matthias Ettrich <ettrich@kde.org>
    SPDX-FileCopyrightText: 1999 Chris Schlaeger <cs@kde.org>
    SPDX-FileCopyrightText: 2002 Joseph Wenninger <jowenn@kde.org>
    SPDX-FileCopyrightText: 2005-2006 Hamish Rodda <rodda@kde.org>
    SPDX-FileCopyrightText: 2000-2008 David Faure <faure@kde.org>
11

12
    SPDX-License-Identifier: LGPL-2.0-only
13
*/
14 15 16 17

#include "kmainwindow.h"

#include "kmainwindow_p.h"
Volker Krause's avatar
Volker Krause committed
18
#ifdef QT_DBUS_LIB
19
#include "kmainwindowiface_p.h"
Volker Krause's avatar
Volker Krause committed
20
#endif
21 22 23 24 25
#include "ktoolbarhandler_p.h"
#include "khelpmenu.h"
#include "ktoolbar.h"

#include <QApplication>
26 27 28
#include <QList>
#include <QObject>
#include <QTimer>
29 30 31 32 33 34 35
#include <QCloseEvent>
#include <QDockWidget>
#include <QMenuBar>
#include <QSessionManager>
#include <QStatusBar>
#include <QStyle>
#include <QWidget>
36
#include <QWindow>
Volker Krause's avatar
Volker Krause committed
37
#ifdef QT_DBUS_LIB
38
#include <QDBusConnection>
Volker Krause's avatar
Volker Krause committed
39
#endif
40 41 42 43 44 45 46 47 48 49 50 51

#include <ktoggleaction.h>
#include <kaboutdata.h>
#include <kconfig.h>
#include <ksharedconfig.h>
#include <klocalizedstring.h>
#include <kconfiggroup.h>
#include <kwindowconfig.h>
#include <kconfiggui.h>

//#include <ctype.h>

52 53
static const char WINDOW_PROPERTIES[]="WindowProperties";

54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
static QMenuBar *internalMenuBar(KMainWindow *mw)
{
    return mw->findChild<QMenuBar *>(QString(), Qt::FindDirectChildrenOnly);
}

static QStatusBar *internalStatusBar(KMainWindow *mw)
{
    return mw->findChild<QStatusBar *>(QString(), Qt::FindDirectChildrenOnly);
}

/**

 * Listens to resize events from QDockWidgets. The KMainWindow
 * settings are set as dirty, as soon as at least one resize
 * event occurred. The listener is attached to the dock widgets
 * by dock->installEventFilter(dockResizeListener) inside
 * KMainWindow::event().
 */
class DockResizeListener : public QObject
{
74
    Q_OBJECT
75 76
public:
    DockResizeListener(KMainWindow *win);
Laurent Montel's avatar
Laurent Montel committed
77 78
    ~DockResizeListener() override;
    bool eventFilter(QObject *watched, QEvent *event) override;
79 80

private:
81
    KMainWindow *const m_win;
82 83 84 85 86 87 88 89 90 91 92 93 94 95
};

DockResizeListener::DockResizeListener(KMainWindow *win) :
    QObject(win),
    m_win(win)
{
}

DockResizeListener::~DockResizeListener()
{
}

bool DockResizeListener::eventFilter(QObject *watched, QEvent *event)
{
96
    switch (event->type()) {
97 98
    case QEvent::Resize:
    case QEvent::Move:
99
    case QEvent::Show:
100 101 102 103 104 105 106 107 108 109 110 111 112
    case QEvent::Hide:
        m_win->k_ptr->setSettingsDirty(KMainWindowPrivate::CompressCalls);
        break;

    default:
        break;
    }

    return QObject::eventFilter(watched, event);
}

KMWSessionManager::KMWSessionManager()
{
113 114 115 116
    connect(qApp, &QGuiApplication::saveStateRequest,
            this, &KMWSessionManager::saveState);
    connect(qApp, &QGuiApplication::commitDataRequest,
            this, &KMWSessionManager::commitData);
117 118 119 120 121 122
}

KMWSessionManager::~KMWSessionManager()
{
}

123
void KMWSessionManager::saveState(QSessionManager &sm)
124
{
125 126
    KConfigGui::setSessionConfig(sm.sessionId(), sm.sessionKey());

127
    KConfig *config = KConfigGui::sessionConfig();
128 129
    const auto windows = KMainWindow::memberList();
    if (!windows.isEmpty()) {
130 131
        // According to Jochen Wilhelmy <digisnap@cs.tu-berlin.de>, this
        // hook is useful for better document orientation
132
        windows.at(0)->saveGlobalProperties(config);
133 134 135
    }

    int n = 0;
136
    for (KMainWindow *mw : windows) {
137 138 139 140
        n++;
        mw->savePropertiesInternal(config, n);
    }

141 142
    KConfigGroup group(config, "Number");
    group.writeEntry("NumberOfWindows", n);
143 144 145 146 147 148 149 150

    // store new status to disk
    config->sync();

    // generate discard command for new file
    QString localFilePath =  QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1Char('/') + config->name();
    if (QFile::exists(localFilePath)) {
        QStringList discard;
Laurent Montel's avatar
Laurent Montel committed
151
        discard << QStringLiteral("rm");
152 153 154
        discard << localFilePath;
        sm.setDiscardCommand(discard);
    }
155 156 157 158 159 160 161
}

void KMWSessionManager::commitData(QSessionManager &sm)
{
    if (!sm.allowsInteraction()) {
        return;
    }
162

163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
    /*
       Purpose of this exercise: invoke queryClose() without actually closing the
       windows, because
       - queryClose() may contain session management code, so it must be invoked
       - actually closing windows may quit the application - cf.
         QGuiApplication::quitOnLastWindowClosed()
       - quitting the application and thus closing the session manager connection
         violates the X11 XSMP protocol.
         The exact requirement of XSMP that would be broken is,
         in the description of the client's state machine:

           save-yourself-done: (changing state is forbidden)

         Closing the session manager connection causes a state change.
         Worst of all, that is a real problem with ksmserver - it will not save
         applications that quit on their own in state save-yourself-done.
     */
180 181
    const auto windows = KMainWindow::memberList();
    for (KMainWindow *window : windows) {
182 183 184 185 186 187 188 189 190 191
        if (window->testAttribute(Qt::WA_WState_Hidden)) {
            continue;
        }
        QCloseEvent e;
        QApplication::sendEvent(window, &e);
        if (!e.isAccepted()) {
            sm.cancel();
            return;
        }
    }
192 193 194
}

Q_GLOBAL_STATIC(KMWSessionManager, ksm)
195
Q_GLOBAL_STATIC(QList<KMainWindow *>, sMemberList)
196

197
KMainWindow::KMainWindow(QWidget *parent, Qt::WindowFlags f)
198 199 200 201 202 203 204 205 206 207 208 209 210 211
    : QMainWindow(parent, f), k_ptr(new KMainWindowPrivate)
{
    k_ptr->init(this);
}

KMainWindow::KMainWindow(KMainWindowPrivate &dd, QWidget *parent, Qt::WindowFlags f)
    : QMainWindow(parent, f), k_ptr(&dd)
{
    k_ptr->init(this);
}

void KMainWindowPrivate::init(KMainWindow *_q)
{
    q = _q;
212
    QGuiApplication::setFallbackSessionManagementEnabled(false);
Kevin Funk's avatar
Kevin Funk committed
213
    q->setAnimated(q->style()->styleHint(QStyle::SH_Widget_Animate, nullptr, q));
214

215
    q->setAttribute(Qt::WA_DeleteOnClose);
216

Kevin Funk's avatar
Kevin Funk committed
217
    helpMenu = nullptr;
218 219 220 221 222 223 224 225 226 227

    //actionCollection()->setWidget( this );
#if 0
    QObject::connect(KGlobalSettings::self(), SIGNAL(settingsChanged(int)),
                     q, SLOT(_k_slotSettingsChanged(int)));
#endif

    // force KMWSessionManager creation
    ksm();

228
    sMemberList()->append(q);
229

230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
    // Set the icon theme fallback to breeze (if not already set)
    // Most of our apps use "lots" of icons that most of the times
    // are only available with breeze, we still honour the user icon
    // theme but if the icon is not found there, we go to breeze
    // since it's almost sure it'll be there.
    // This should be done as soon as possible (preferably via
    // Q_COREAPP_STARTUP_FUNCTION), but as for now it cannot be done too soon
    // as at that point QPlatformTheme is not instantiated yet and breaks the
    // internal status of QIconLoader (see QTBUG-74252).
    // See also discussion at https://phabricator.kde.org/D22488
    // TODO: remove this once we depend on Qt 5.15.1, where this is fixed
#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
    if (QIcon::fallbackThemeName().isEmpty()) {
        QIcon::setFallbackThemeName(QStringLiteral("breeze"));
    }
#endif

247 248 249 250 251 252 253
    // If application is translated, load translator information for use in
    // KAboutApplicationDialog or other getters. The context and messages below
    // both must be exactly as listed, and are forced to be loaded from the
    // application's own message catalog instead of kxmlgui's.
    KAboutData aboutData(KAboutData::applicationData());
    if (aboutData.translators().isEmpty()) {
        aboutData.setTranslator(
Kevin Funk's avatar
Kevin Funk committed
254 255
                i18ndc(nullptr, "NAME OF TRANSLATORS", "Your names"),
                i18ndc(nullptr, "EMAIL OF TRANSLATORS", "Your emails"));
256 257 258 259

        KAboutData::setApplicationData(aboutData);
    }

260 261 262 263
    settingsDirty = false;
    autoSaveSettings = false;
    autoSaveWindowSize = true; // for compatibility
    //d->kaccel = actionCollection()->kaccel();
Kevin Funk's avatar
Kevin Funk committed
264 265
    settingsTimer = nullptr;
    sizeTimer = nullptr;
266 267 268 269 270

    dockResizeListener = new DockResizeListener(_q);
    letDirtySettings = true;

    sizeApplied = false;
271
    suppressCloseEvent = false;
272 273
}

274
static bool endsWithHashNumber(const QString &s)
275
{
276 277 278 279 280 281 282
    for (int i = s.length() - 1;
            i > 0;
            --i) {
        if (s[ i ] == QLatin1Char('#') && i != s.length() - 1) {
            return true;    // ok
        }
        if (!s[ i ].isDigit()) {
283
            break;
284
        }
285 286 287 288 289 290
    }
    return false;
}

static inline bool isValidDBusObjectPathCharacter(const QChar &c)
{
Laurent Montel's avatar
Laurent Montel committed
291
    ushort u = c.unicode();
292
    return (u >= QLatin1Char('a') && u <= QLatin1Char('z'))
293 294 295
           || (u >= QLatin1Char('A') && u <= QLatin1Char('Z'))
           || (u >= QLatin1Char('0') && u <= QLatin1Char('9'))
           || (u == QLatin1Char('_')) || (u == QLatin1Char('/'));
296 297 298 299 300 301 302 303 304 305 306
}

void KMainWindowPrivate::polish(KMainWindow *q)
{
    // Set a unique object name. Required by session management, window management, and for the dbus interface.
    QString objname;
    QString s;
    int unusedNumber = 1;
    const QString name = q->objectName();
    bool startNumberingImmediately = true;
    bool tryReuse = false;
307 308
    if (name.isEmpty()) {
        // no name given
309
        objname = QStringLiteral("MainWindow#");
310 311
    } else if (name.endsWith(QLatin1Char('#'))) {
        // trailing # - always add a number  - KWin uses this for better grouping
312
        objname = name;
313 314
    } else if (endsWithHashNumber(name)) {
        // trailing # with a number - like above, try to use the given number first
315 316 317
        objname = name;
        tryReuse = true;
        startNumberingImmediately = false;
318
    } else {
319 320 321 322 323
        objname = name;
        startNumberingImmediately = false;
    }

    s = objname;
324
    if (startNumberingImmediately) {
325
        s += QLatin1Char('1');
326
    }
327

328 329
    for (;;) {
        const QList<QWidget *> list = qApp->topLevelWidgets();
330
        bool found = false;
331
        for (QWidget *w : list) {
332
            if (w != q && w->objectName() == s) {
333 334 335 336
                found = true;
                break;
            }
        }
337
        if (!found) {
338
            break;
339 340 341
        }
        if (tryReuse) {
            objname = name.left(name.length() - 1);   // lose the hash
342 343 344
            unusedNumber = 0; // start from 1 below
            tryReuse = false;
        }
345
        s.setNum(++unusedNumber);
346 347
        s = objname + s;
    }
348
    q->setObjectName(s);
349
    if (!q->window() || q->window() == q) {
350 351 352
         q->winId(); // workaround for setWindowRole() crashing, and set also window role, just in case TT
         q->setWindowRole(s);   // will keep insisting that object name suddenly should not be used for window role
    }
353 354 355 356

    dbusName = QLatin1Char('/') + QCoreApplication::applicationName() + QLatin1Char('/');
    dbusName += q->objectName().replace(QLatin1Char('/'), QLatin1Char('_'));
    // Clean up for dbus usage: any non-alphanumeric char should be turned into '_'
357 358 359
    for (QChar &c : dbusName) {
        if (!isValidDBusObjectPathCharacter(c)) {
            c = QLatin1Char('_');
360
        }
361 362
    }

Volker Krause's avatar
Volker Krause committed
363
#ifdef QT_DBUS_LIB
364
    QDBusConnection::sessionBus().registerObject(dbusName, q, QDBusConnection::ExportScriptableSlots |
365 366 367 368
            QDBusConnection::ExportScriptableProperties |
            QDBusConnection::ExportNonScriptableSlots |
            QDBusConnection::ExportNonScriptableProperties |
            QDBusConnection::ExportAdaptors);
Volker Krause's avatar
Volker Krause committed
369
#endif
370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
}

void KMainWindowPrivate::setSettingsDirty(CallCompression callCompression)
{
    if (!letDirtySettings) {
        return;
    }

    settingsDirty = true;
    if (autoSaveSettings) {
        if (callCompression == CompressCalls) {
            if (!settingsTimer) {
                settingsTimer = new QTimer(q);
                settingsTimer->setInterval(500);
                settingsTimer->setSingleShot(true);
385 386
                QObject::connect(settingsTimer, &QTimer::timeout,
                                 q, &KMainWindow::saveAutoSaveSettings);
387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409
            }
            settingsTimer->start();
        } else {
            q->saveAutoSaveSettings();
        }
    }
}

void KMainWindowPrivate::setSizeDirty()
{
    if (autoSaveWindowSize) {
        if (!sizeTimer) {
            sizeTimer = new QTimer(q);
            sizeTimer->setInterval(500);
            sizeTimer->setSingleShot(true);
            QObject::connect(sizeTimer, SIGNAL(timeout()), q, SLOT(_k_slotSaveAutoSaveSize()));
        }
        sizeTimer->start();
    }
}

KMainWindow::~KMainWindow()
{
410
    sMemberList()->removeAll(this);
411 412 413 414
    delete static_cast<QObject *>(k_ptr->dockResizeListener);  //so we don't get anymore events after k_ptr is destroyed
    delete k_ptr;
}

415
#if KXMLGUI_BUILD_DEPRECATED_SINCE(5, 0)
416
QMenu *KMainWindow::helpMenu(const QString &aboutAppText, bool showWhatsThis)
417 418
{
    K_D(KMainWindow);
419 420 421 422 423 424
    if (!d->helpMenu) {
        if (aboutAppText.isEmpty()) {
            d->helpMenu = new KHelpMenu(this, KAboutData::applicationData(), showWhatsThis);
        } else {
            d->helpMenu = new KHelpMenu(this, aboutAppText, showWhatsThis);
        }
425

426
        if (!d->helpMenu) {
Kevin Funk's avatar
Kevin Funk committed
427
            return nullptr;
428
        }
429 430 431 432 433
    }

    return d->helpMenu->menu();
}

434
QMenu *KMainWindow::customHelpMenu(bool showWhatsThis)
435 436 437
{
    K_D(KMainWindow);
    if (!d->helpMenu) {
438
        d->helpMenu = new KHelpMenu(this, QString(), showWhatsThis);
439 440
        connect(d->helpMenu, &KHelpMenu::showAboutApplication,
                this, &KMainWindow::showAboutApplication);
441 442 443 444 445 446
    }

    return d->helpMenu->menu();
}
#endif

447
bool KMainWindow::canBeRestored(int number)
448 449
{
    KConfig *config = KConfigGui::sessionConfig();
450
    if (!config) {
451
        return false;
452
    }
453

454
    KConfigGroup group(config, "Number");
455
    const int n = group.readEntry("NumberOfWindows", 0);
456 457 458
    return number >= 1 && number <= n;
}

459
const QString KMainWindow::classNameOfToplevel(int number)
460 461
{
    KConfig *config = KConfigGui::sessionConfig();
462
    if (!config) {
463
        return QString();
464
    }
465

466 467
    KConfigGroup group(config, QByteArray(WINDOW_PROPERTIES).append(QByteArray::number(number)).constData());
    if (!group.hasKey("ClassName")) {
468
        return QString();
469
    } else {
470
        return group.readEntry("ClassName");
471
    }
472 473
}

474
bool KMainWindow::restore(int number, bool show)
475
{
476
    if (!canBeRestored(number)) {
477
        return false;
478
    }
479
    KConfig *config = KConfigGui::sessionConfig();
480 481
    if (readPropertiesInternal(config, number)) {
        if (show) {
482
            KMainWindow::show();
483
        }
484 485 486 487 488
        return false;
    }
    return false;
}

489
void KMainWindow::setCaption(const QString &caption)
490
{
491
    setPlainCaption(caption);
492 493
}

494
void KMainWindow::setCaption(const QString &caption, bool modified)
495
{
496
    QString title = caption;
497 498
    if (!title.contains(QLatin1String("[*]")) && !title.isEmpty()) { // append the placeholder so that the modified mechanism works
        title.append(QLatin1String(" [*]"));
499 500
    }
    setPlainCaption(title);
501
    setWindowModified(modified);
502 503
}

504
void KMainWindow::setPlainCaption(const QString &caption)
505
{
506
    setWindowTitle(caption);
507 508
}

509
void KMainWindow::appHelpActivated()
510 511
{
    K_D(KMainWindow);
512 513 514
    if (!d->helpMenu) {
        d->helpMenu = new KHelpMenu(this);
        if (!d->helpMenu) {
515
            return;
516
        }
517 518 519 520
    }
    d->helpMenu->appHelpActivated();
}

521
void KMainWindow::closeEvent(QCloseEvent *e)
522 523
{
    K_D(KMainWindow);
524
    if (d->suppressCloseEvent) {
525 526 527
        e->accept();
        return;
    }
528 529 530 531 532 533 534 535 536 537

    // Save settings if auto-save is enabled, and settings have changed
    if (d->settingsTimer && d->settingsTimer->isActive()) {
        d->settingsTimer->stop();
        saveAutoSaveSettings();
    }
    if (d->sizeTimer && d->sizeTimer->isActive()) {
        d->sizeTimer->stop();
        d->_k_slotSaveAutoSaveSize();
    }
538 539
    // Delete the marker that says we don't want to restore the position of the
    // next-opened instance; now that a window is closing, we do want to do this
540 541 542
    if (d->autoSaveGroup.isValid()) {
        d->autoSaveGroup.deleteEntry("RestorePositionForNextInstance");
    }
543
    d->_k_slotSaveAutoSavePosition();
544 545

    if (queryClose()) {
546 547 548 549
        // widgets will start destroying themselves at this point and we don't
        // want to save state anymore after this as it might be incorrect
        d->autoSaveSettings = false;
        d->letDirtySettings = false;
550
        e->accept();
551 552 553
    } else {
        e->ignore();    //if the window should not be closed, don't close it
    }
554 555
    // If saving session, we are processing a fake close event, and might get the real one later.
    if (e->isAccepted() && qApp->isSavingSession())
556
        d->suppressCloseEvent = true;
557 558 559 560 561 562 563
}

bool KMainWindow::queryClose()
{
    return true;
}

564
void KMainWindow::saveGlobalProperties(KConfig *)
565 566 567
{
}

568
void KMainWindow::readGlobalProperties(KConfig *)
569 570 571
{
}

572
void KMainWindow::savePropertiesInternal(KConfig *config, int number)
573 574 575 576 577
{
    K_D(KMainWindow);
    const bool oldASWS = d->autoSaveWindowSize;
    d->autoSaveWindowSize = true; // make saveMainWindowSettings save the window size

578
    KConfigGroup cg(config, QByteArray(WINDOW_PROPERTIES).append(QByteArray::number(number)).constData());
579 580 581 582 583 584 585 586

    // store objectName, className, Width and Height  for later restoring
    // (Only useful for session management)
    cg.writeEntry("ObjectName", objectName());
    cg.writeEntry("ClassName", metaObject()->className());

    saveMainWindowSettings(cg); // Menubar, statusbar and Toolbar settings.

587
    cg = KConfigGroup(config, QByteArray::number(number).constData());
588 589 590 591 592 593 594 595 596 597 598
    saveProperties(cg);

    d->autoSaveWindowSize = oldASWS;
}

void KMainWindow::saveMainWindowSettings(KConfigGroup &cg)
{
    K_D(KMainWindow);
    //qDebug(200) << "KMainWindow::saveMainWindowSettings " << cg.name();

    // Called by session management - or if we want to save the window size anyway
599 600
    if (d->autoSaveWindowSize) {
        KWindowConfig::saveWindowSize(windowHandle(), cg);
601
        KWindowConfig::saveWindowPosition(windowHandle(), cg);
602
    }
603 604 605 606 607 608

    // One day will need to save the version number, but for now, assume 0
    // Utilise the QMainWindow::saveState() functionality.
    const QByteArray state = saveState();
    cg.writeEntry("State", state.toBase64());

609
    QStatusBar *sb = internalStatusBar(this);
610
    if (sb) {
611 612 613 614 615
        if (!cg.hasDefault("StatusBar") && !sb->isHidden()) {
            cg.revertToDefault("StatusBar");
        } else {
            cg.writeEntry("StatusBar", sb->isHidden() ? "Disabled" : "Enabled");
        }
616 617
    }

618
    QMenuBar *mb = internalMenuBar(this);
619
    if (mb) {
620 621 622 623 624
        if (!cg.hasDefault("MenuBar") && !mb->isHidden()) {
            cg.revertToDefault("MenuBar");
        } else {
            cg.writeEntry("MenuBar", mb->isHidden() ? "Disabled" : "Enabled");
        }
625 626
    }

627
    if (!autoSaveSettings() || cg.name() == autoSaveGroup()) {
628
        // TODO should be cg == d->autoSaveGroup, to compare both kconfig and group name
629
        if (!cg.hasDefault("ToolBarsMovable") && !KToolBar::toolBarsLocked()) {
630
            cg.revertToDefault("ToolBarsMovable");
631
        } else {
632
            cg.writeEntry("ToolBarsMovable", KToolBar::toolBarsLocked() ? "Disabled" : "Enabled");
633
        }
634 635 636
    }

    int n = 1; // Toolbar counter. toolbars are counted from 1,
637 638
    const auto toolBars = this->toolBars();
    for (KToolBar *toolbar : toolBars) {
639
        QByteArray groupName("Toolbar");
640 641
        // Give a number to the toolbar, but prefer a name if there is one,
        // because there's no real guarantee on the ordering of toolbars
642
        groupName += (toolbar->objectName().isEmpty() ? QByteArray::number(n) : QByteArray(" ").append(toolbar->objectName().toUtf8()));
643

644
        KConfigGroup toolbarGroup(&cg, groupName.constData());
645 646 647 648 649
        toolbar->saveSettings(toolbarGroup);
        n++;
    }
}

650
bool KMainWindow::readPropertiesInternal(KConfig *config, int number)
651 652 653 654 655 656
{
    K_D(KMainWindow);

    const bool oldLetDirtySettings = d->letDirtySettings;
    d->letDirtySettings = false;

657 658 659
    if (number == 1) {
        readGlobalProperties(config);
    }
660 661

    // in order they are in toolbar list
662
    KConfigGroup cg(config, QByteArray(WINDOW_PROPERTIES).append(QByteArray::number(number)).constData());
663 664

    // restore the object name (window role)
665
    if (cg.hasKey("ObjectName")) {
666
        setObjectName(cg.readEntry("ObjectName"));
667
    }
668 669

    d->sizeApplied = false; // since we are changing config file, reload the size of the window
670
    // if necessary. Do it before the call to applyMainWindowSettings.
671 672
    applyMainWindowSettings(cg); // Menubar, statusbar and toolbar settings.

673
    KConfigGroup grp(config, QByteArray::number(number).constData());
674 675 676 677 678 679 680
    readProperties(grp);

    d->letDirtySettings = oldLetDirtySettings;

    return true;
}

681
void KMainWindow::applyMainWindowSettings(const KConfigGroup &cg)
682 683 684 685 686 687 688 689 690
{
    K_D(KMainWindow);
    //qDebug(200) << "KMainWindow::applyMainWindowSettings " << cg.name();

    QWidget *focusedWidget = QApplication::focusWidget();

    const bool oldLetDirtySettings = d->letDirtySettings;
    d->letDirtySettings = false;

691
    if (!d->sizeApplied && (!window() || window() == this)) {
692
        winId(); // ensure there's a window created
693
        KWindowConfig::restoreWindowSize(windowHandle(), cg);
694 695 696 697
        // NOTICE: QWindow::setGeometry() does NOT impact the backing QWidget geometry even if the platform
        // window was created -> QTBUG-40584. We therefore copy the size here.
        // TODO: remove once this was resolved in QWidget QPA
        resize(windowHandle()->size());
698
        d->sizeApplied = true;
699 700 701 702 703 704

        // Let the user opt out of KDE apps remembering window sizes if they
        // find it annoying or it doesn't work for them due to other bugs.
        KSharedConfigPtr config = KSharedConfig::openConfig();
        KConfigGroup group(config, "General");
        if (group.readEntry("AllowKDEAppsToRememberWindowPositions", true)) {
705 706 707 708 709 710 711 712
            if (cg.readEntry("RestorePositionForNextInstance", true)) {
                KWindowConfig::restoreWindowPosition(windowHandle(), cg);
                // Save the fact that we now don't want to restore position
                // anymore; if we did, the next instance would completely cover
                // the existing one
                KConfigGroup cgWritable = cg; // because cg is const
                cgWritable.writeEntry("RestorePositionForNextInstance", false);
            }
713
        }
714 715
    }

716
    QStatusBar *sb = internalStatusBar(this);
717 718
    if (sb) {
        QString entry = cg.readEntry("StatusBar", "Enabled");
719
        sb->setVisible( entry != QLatin1String("Disabled") );
720 721
    }

722
    QMenuBar *mb = internalMenuBar(this);
723
    if (mb) {
724
        QString entry = cg.readEntry("MenuBar", "Enabled");
725
        mb->setVisible( entry != QLatin1String("Disabled") );
726 727
    }

728 729
    if (!autoSaveSettings() || cg.name() == autoSaveGroup()) {   // TODO should be cg == d->autoSaveGroup, to compare both kconfig and group name
        QString entry = cg.readEntry("ToolBarsMovable", "Disabled");
730
        KToolBar::setToolBarsLocked(entry == QLatin1String("Disabled"));
731 732 733
    }

    int n = 1; // Toolbar counter. toolbars are counted from 1,
734 735
    const auto toolBars = this->toolBars();
    for (KToolBar *toolbar : toolBars) {
736
        QByteArray groupName("Toolbar");
737 738
        // Give a number to the toolbar, but prefer a name if there is one,
        // because there's no real guarantee on the ordering of toolbars
739
        groupName += (toolbar->objectName().isEmpty() ? QByteArray::number(n) : QByteArray(" ").append(toolbar->objectName().toUtf8()));
740

741
        KConfigGroup toolbarGroup(&cg, groupName.constData());
742
        toolbar->applySettings(toolbarGroup);
743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761
        n++;
    }

    QByteArray state;
    if (cg.hasKey("State")) {
        state = cg.readEntry("State", state);
        state = QByteArray::fromBase64(state);
        // One day will need to load the version number, but for now, assume 0
        restoreState(state);
    }

    if (focusedWidget) {
        focusedWidget->setFocus();
    }

    d->settingsDirty = false;
    d->letDirtySettings = oldLetDirtySettings;
}

762
#if KXMLGUI_BUILD_DEPRECATED_SINCE(5, 0)
763
void KMainWindow::restoreWindowSize(const KConfigGroup &cg)
764 765 766 767 768
{
    KWindowConfig::restoreWindowSize(windowHandle(), cg);
}
#endif

769
#if KXMLGUI_BUILD_DEPRECATED_SINCE(5, 0)
770
void KMainWindow::saveWindowSize(KConfigGroup &cg) const
771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787
{
    KWindowConfig::saveWindowSize(windowHandle(), cg);
}
#endif

void KMainWindow::setSettingsDirty()
{
    K_D(KMainWindow);
    d->setSettingsDirty();
}

bool KMainWindow::settingsDirty() const
{
    K_D(const KMainWindow);
    return d->settingsDirty;
}

788
void KMainWindow::setAutoSaveSettings(const QString &groupName, bool saveWindowSize)
789 790 791 792
{
    setAutoSaveSettings(KConfigGroup(KSharedConfig::openConfig(), groupName), saveWindowSize);
}

793 794
void KMainWindow::setAutoSaveSettings(const KConfigGroup &group,
                                      bool saveWindowSize)
795
{
796 797 798 799
    // We re making a little assumption that if you want to save the window
    // size, you probably also want to save the window position too
    // This avoids having to re-implement a new version of
    // KMainWindow::setAutoSaveSettings that handles these cases independently
800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842
    K_D(KMainWindow);
    d->autoSaveSettings = true;
    d->autoSaveGroup = group;
    d->autoSaveWindowSize = saveWindowSize;

    if (!saveWindowSize && d->sizeTimer) {
        d->sizeTimer->stop();
    }

    // Now read the previously saved settings
    applyMainWindowSettings(d->autoSaveGroup);
}

void KMainWindow::resetAutoSaveSettings()
{
    K_D(KMainWindow);
    d->autoSaveSettings = false;
    if (d->settingsTimer) {
        d->settingsTimer->stop();
    }
}

bool KMainWindow::autoSaveSettings() const
{
    K_D(const KMainWindow);
    return d->autoSaveSettings;
}

QString KMainWindow::autoSaveGroup() const
{
    K_D(const KMainWindow);
    return d->autoSaveSettings ? d->autoSaveGroup.name() : QString();
}

KConfigGroup KMainWindow::autoSaveConfigGroup() const
{
    K_D(const KMainWindow);
    return d->autoSaveSettings ? d->autoSaveGroup : KConfigGroup();
}

void KMainWindow::saveAutoSaveSettings()
{
    K_D(KMainWindow);
843
    Q_ASSERT(d->autoSaveSettings);
844 845 846 847 848 849
    //qDebug(200) << "KMainWindow::saveAutoSaveSettings -> saving settings";
    saveMainWindowSettings(d->autoSaveGroup);
    d->autoSaveGroup.sync();
    d->settingsDirty = false;
}

850
bool KMainWindow::event(QEvent *ev)
851 852
{
    K_D(KMainWindow);
853
    switch (ev->type()) {
854
#if defined(Q_OS_WIN) || defined(Q_OS_OSX)
855
    case QEvent::Move:
856
#endif
857 858 859 860 861 862
    case QEvent::Resize:
        d->setSizeDirty();
        break;
    case QEvent::Polish:
        d->polish(this);
        break;
863 864 865 866 867 868
    case QEvent::ChildPolished: {
        QChildEvent *event = static_cast<QChildEvent *>(ev);
        QDockWidget *dock = qobject_cast<QDockWidget *>(event->child());
        KToolBar *toolbar = qobject_cast<KToolBar *>(event->child());
        QMenuBar *menubar = qobject_cast<QMenuBar *>(event->child());
        if (dock) {
869 870 871 872
            connect(dock, &QDockWidget::dockLocationChanged,
                    this, &KMainWindow::setSettingsDirty);
            connect(dock, &QDockWidget::topLevelChanged,
                    this, &KMainWindow::setSettingsDirty);
873 874 875 876 877 878 879 880 881 882 883 884

            // there is no signal emitted if the size of the dock changes,
            // hence install an event filter instead
            dock->installEventFilter(k_ptr->dockResizeListener);
        } else if (toolbar) {
            // there is no signal emitted if the size of the toolbar changes,
            // hence install an event filter instead
            toolbar->installEventFilter(k_ptr->dockResizeListener);
        } else if (menubar) {
            // there is no signal emitted if the size of the menubar changes,
            // hence install an event filter instead
            menubar->installEventFilter(k_ptr->dockResizeListener);
885
        }
886 887 888 889 890 891 892 893
    }
    break;
    case QEvent::ChildRemoved: {
        QChildEvent *event = static_cast<QChildEvent *>(ev);
        QDockWidget *dock = qobject_cast<QDockWidget *>(event->child());
        KToolBar *toolbar = qobject_cast<KToolBar *>(event->child());
        QMenuBar *menubar = qobject_cast<QMenuBar *>(event->child());
        if (dock) {
894 895 896 897
            disconnect(dock, &QDockWidget::dockLocationChanged,
                       this, &KMainWindow::setSettingsDirty);
            disconnect(dock, &QDockWidget::topLevelChanged,
                       this, &KMainWindow::setSettingsDirty);
898 899 900 901 902
            dock->removeEventFilter(k_ptr->dockResizeListener);
        } else if (toolbar) {
            toolbar->removeEventFilter(k_ptr->dockResizeListener);
        } else if (menubar) {
            menubar->removeEventFilter(k_ptr->dockResizeListener);
903
        }
904 905
    }
    break;
906 907 908
    default:
        break;
    }
909
    return QMainWindow::event(ev);
910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926
}

bool KMainWindow::hasMenuBar()
{
    return internalMenuBar(this);
}

void KMainWindowPrivate::_k_slotSettingsChanged(int category)
{
    Q_UNUSED(category);

    // This slot will be called when the style KCM changes settings that need
    // to be set on the already running applications.

    // At this level (KMainWindow) the only thing we need to restore is the
    // animations setting (whether the user wants builtin animations or not).

Kevin Funk's avatar
Kevin Funk committed
927
    q->setAnimated(q->style()->styleHint(QStyle::SH_Widget_Animate, nullptr, q));
928 929 930 931 932 933 934 935 936
}

void KMainWindowPrivate::_k_slotSaveAutoSaveSize()
{
    if (autoSaveGroup.isValid()) {
        KWindowConfig::saveWindowSize(q->windowHandle(), autoSaveGroup);
    }
}

937 938 939 940 941 942 943
void KMainWindowPrivate::_k_slotSaveAutoSavePosition()
{
    if (autoSaveGroup.isValid()) {
        KWindowConfig::saveWindowPosition(q->windowHandle(), autoSaveGroup);
    }
}

944
KToolBar *KMainWindow::toolBar(const QString &name)
945 946
{
    QString childName = name;
947 948 949
    if (childName.isEmpty()) {
        childName = QStringLiteral("mainToolBar");
    }
950

951 952
    KToolBar *tb = findChild<KToolBar *>(childName);
    if (tb) {
953
        return tb;
954
    }
955

956
    KToolBar *toolbar = new KToolBar(childName, this); // non-XMLGUI toolbar
957 958 959
    return toolbar;
}

960
QList<KToolBar *> KMainWindow::toolBars() const
961
{
962
    QList<KToolBar *> ret;
963

Nicolas Fella's avatar
Nicolas Fella committed
964 965
    const auto theChildren = children();
    for (QObject *child : theChildren)
966
        if (KToolBar *toolBar = qobject_cast<KToolBar *>(child)) {
967
            ret.append(toolBar);
968
        }
969 970 971 972

    return ret;
}

973 974 975 976
QList<KMainWindow *> KMainWindow::memberList()
{
    return *sMemberList();
}
977 978 979 980 981 982 983

QString KMainWindow::dbusName() const
{
    return k_func()->dbusName;
}

#include "moc_kmainwindow.cpp"
984
#include "kmainwindow.moc"