mainwindow.cpp 33.6 KB
Newer Older
1
/*
Allen Winter's avatar
Allen Winter committed
2
  This file is part of KDE Kontact.
3

4
5
6
  SPDX-FileCopyrightText: 2001 Matthias Hoelzer-Kluepfel <mhk@kde.org>
  SPDX-FileCopyrightText: 2002-2005 Daniel Molkentin <molkentin@kde.org>
  SPDX-FileCopyrightText: 2003-2005 Cornelius Schumacher <schumacher@kde.org>
7

8
  SPDX-License-Identifier: GPL-2.0-or-later
9
10
*/

Allen Winter's avatar
Allen Winter committed
11
12
#include "mainwindow.h"
#include "aboutdialog.h"
Allen Winter's avatar
Allen Winter committed
13
#include "iconsidepane.h"
Laurent Montel's avatar
Laurent Montel committed
14
#include "prefs.h"
15
16

#include "webengine/introductionwebenginepage.h"
Laurent Montel's avatar
Laurent Montel committed
17
#include "webengine/introductionwebengineview.h"
18

19
#include "kontactconfiguredialog.h"
Allen Winter's avatar
Allen Winter committed
20
using namespace Kontact;
Laurent Montel's avatar
Laurent Montel committed
21
22
23
#ifdef WIN32
#include <windows.h>
#else
24
#include <unistd.h>
Laurent Montel's avatar
Laurent Montel committed
25
#endif
Laurent Montel's avatar
Laurent Montel committed
26
27
#include <Libkdepim/ProgressStatusBarWidget>
#include <Libkdepim/StatusbarProgressWidget>
Laurent Montel's avatar
Laurent Montel committed
28
#include <PimCommon/BroadcastStatus>
Allen Winter's avatar
Allen Winter committed
29

Laurent Montel's avatar
Laurent Montel committed
30
31
#include "kontact_debug.h"
#include <KAboutData>
Allen Winter's avatar
Allen Winter committed
32
33
34
#include <KActionCollection>
#include <KActionMenu>
#include <KConfigGroup>
35
#include <KDialogJobUiDelegate>
Allen Winter's avatar
Allen Winter committed
36
#include <KEditToolBar>
Laurent Montel's avatar
Laurent Montel committed
37
#include <KHelpClient>
Allen Winter's avatar
Allen Winter committed
38
#include <KHelpMenu>
39
#include <KIO/CommandLauncherJob>
Laurent Montel's avatar
Laurent Montel committed
40
41
#include <KIO/OpenUrlJob>
#include <KLocalizedString>
Allen Winter's avatar
Allen Winter committed
42
#include <KMessageBox>
Laurent Montel's avatar
Laurent Montel committed
43
#include <KParts/PartManager>
Allen Winter's avatar
Allen Winter committed
44
#include <KPluginInfo>
45
#include <KPluginMetaData>
Laurent Montel's avatar
Laurent Montel committed
46
#include <KSharedConfig>
Allen Winter's avatar
Allen Winter committed
47
48
49
#include <KShortcutsDialog>
#include <KSqueezedTextLabel>
#include <KStandardAction>
50
#include <KSycoca>
Laurent Montel's avatar
Laurent Montel committed
51
52
53
#include <KToolBar>
#include <KWindowConfig>
#include <KXMLGUIFactory>
Laurent Montel's avatar
Laurent Montel committed
54
#include <QAction>
Laurent Montel's avatar
Laurent Montel committed
55
#include <QApplication>
56
#include <QDBusConnection>
Laurent Montel's avatar
Laurent Montel committed
57
58
59
#include <QHBoxLayout>
#include <QIcon>
#include <QShortcut>
Allen Winter's avatar
Allen Winter committed
60
#include <QSplitter>
61
#include <QStackedWidget>
Laurent Montel's avatar
Laurent Montel committed
62
#include <QStandardPaths>
Laurent Montel's avatar
Laurent Montel committed
63
64
65
#include <QStatusBar>
#include <QVBoxLayout>
#include <QWebEngineUrlScheme>
Laurent Montel's avatar
Laurent Montel committed
66

Laurent Montel's avatar
Laurent Montel committed
67
#include <ksettings/Dispatcher>
68

69
#include <GrantleeTheme/GrantleeTheme>
Laurent Montel's avatar
Laurent Montel committed
70
#include <GrantleeTheme/GrantleeThemeManager>
Daniel Molkentin's avatar
Daniel Molkentin committed
71

72
MainWindow::MainWindow()
Laurent Montel's avatar
Laurent Montel committed
73
    : KontactInterface::Core()
74
{
Laurent Montel's avatar
Laurent Montel committed
75
    // Necessary for "cid" support in kmail.
76
77
78
79
    QWebEngineUrlScheme cidScheme("cid");
    cidScheme.setFlags(QWebEngineUrlScheme::SecureScheme | QWebEngineUrlScheme::ContentSecurityPolicyIgnored);
    cidScheme.setSyntax(QWebEngineUrlScheme::Syntax::Path);
    QWebEngineUrlScheme::registerScheme(cidScheme);
Laurent Montel's avatar
Laurent Montel committed
80

Laurent Montel's avatar
Laurent Montel committed
81
    QDBusConnection::sessionBus().registerObject(QStringLiteral("/KontactInterface"), this, QDBusConnection::ExportScriptableSlots);
82

Laurent Montel's avatar
Laurent Montel committed
83
84
    // Set this to be the group leader for all subdialogs - this means
    // modal subdialogs will only affect this dialog, not the other windows
Laurent Montel's avatar
Laurent Montel committed
85
    setAttribute(Qt::WA_GroupLeader);
86

Laurent Montel's avatar
Laurent Montel committed
87
88
    initGUI();
    initObject();
89

Laurent Montel's avatar
Laurent Montel committed
90
91
    mSidePane->setMaximumWidth(mSidePane->sizeHint().width());
    mSidePane->setMinimumWidth(mSidePane->sizeHint().width());
92

Laurent Montel's avatar
Laurent Montel committed
93
    factory()->plugActionList(this, QStringLiteral("navigator_actionlist"), mActionPlugins);
94

Laurent Montel's avatar
Laurent Montel committed
95
96
    KConfigGroup grp(KSharedConfig::openConfig(), "MainWindow");
    KWindowConfig::restoreWindowSize(windowHandle(), grp);
Laurent Montel's avatar
Laurent Montel committed
97
    setAutoSaveSettings();
98
}
99

100
101
void MainWindow::initGUI()
{
Laurent Montel's avatar
Laurent Montel committed
102
103
    initWidgets();
    setupActions();
104

Laurent Montel's avatar
Laurent Montel committed
105
106
    KStandardAction::keyBindings(this, &MainWindow::configureShortcuts, actionCollection());
    KStandardAction::configureToolbars(this, &MainWindow::configureToolbars, actionCollection());
107
    setXMLFile(QStringLiteral("kontactui.rc"));
108

Laurent Montel's avatar
Laurent Montel committed
109
    setStandardToolBarMenuEnabled(true);
110

Laurent Montel's avatar
Laurent Montel committed
111
    createGUI(nullptr);
112

Laurent Montel's avatar
Laurent Montel committed
113
114
115
116
    KToolBar *navigatorToolBar = findToolBar("navigatorToolBar");
    if (navigatorToolBar) {
        if (layoutDirection() == Qt::LeftToRight) {
            navigatorToolBar->setLayoutDirection(Qt::RightToLeft);
Laurent Montel's avatar
Laurent Montel committed
117
        } else {
Laurent Montel's avatar
Laurent Montel committed
118
            navigatorToolBar->setLayoutDirection(Qt::LeftToRight);
Laurent Montel's avatar
Laurent Montel committed
119
        }
Laurent Montel's avatar
Laurent Montel committed
120
121
        Q_ASSERT(navigatorToolBar->sizeHint().isValid());
        navigatorToolBar->setMinimumWidth(navigatorToolBar->sizeHint().width());
Allen Winter's avatar
Allen Winter committed
122
    } else {
Laurent Montel's avatar
Laurent Montel committed
123
        qCritical() << "Unable to find navigatorToolBar, probably kontactui.rc is missing";
Allen Winter's avatar
Allen Winter committed
124
    }
125
126
127
128
}

void MainWindow::initObject()
{
Laurent Montel's avatar
Laurent Montel committed
129
    if (!KSycoca::isAvailable()) {
130
131
132
133
134
135
        qDebug() << "Trying to create ksycoca...";
        KSycoca::self()->ensureCacheValid();
        if (!KSycoca::isAvailable()) {
            // This should only happen if the distribution is broken, or the disk full
            qFatal("KSycoca unavailable. Kontact will be unable to find plugins.");
        }
Laurent Montel's avatar
Laurent Montel committed
136
    }
137

Laurent Montel's avatar
Laurent Montel committed
138
139
140
    const QVector<KPluginMetaData> pluginMetaDatas = KPluginLoader::findPlugins(QStringLiteral("kontact5"), [](const KPluginMetaData &data) {
        return data.rawData().value(QStringLiteral("X-KDE-KontactPluginVersion")).toInt() == KONTACT_PLUGIN_VERSION;
    });
141
142
143
    mPluginInfos = KPluginInfo::fromMetaData(pluginMetaDatas);

    KConfigGroup configGroup(Prefs::self()->config(), "Plugins");
144
    for (KPluginInfo &pluginInfo : mPluginInfos) {
145
        pluginInfo.setConfig(configGroup);
146
        pluginInfo.load();
Laurent Montel's avatar
Laurent Montel committed
147
148
149
    }

    // prepare the part manager
Laurent Montel's avatar
Laurent Montel committed
150
    mPartManager = new KParts::PartManager(this);
Laurent Montel's avatar
Laurent Montel committed
151
    connect(mPartManager, &KParts::PartManager::activePartChanged, this, &MainWindow::slotActivePartChanged);
Laurent Montel's avatar
Laurent Montel committed
152
153
154

    loadPlugins();

Laurent Montel's avatar
Laurent Montel committed
155
    if (mSidePane) {
Laurent Montel's avatar
Laurent Montel committed
156
157
        mSidePane->updatePlugins();
    }
158

Laurent Montel's avatar
Laurent Montel committed
159
    loadSettings();
Daniel Molkentin's avatar
Daniel Molkentin committed
160

Laurent Montel's avatar
Laurent Montel committed
161
    statusBar()->show();
162

Laurent Montel's avatar
Laurent Montel committed
163
    // done initializing
Laurent Montel's avatar
Laurent Montel committed
164
    slotShowStatusMsg(QString());
165

166
    connect(PimCommon::BroadcastStatus::instance(), &PimCommon::BroadcastStatus::statusMsg, this, &MainWindow::slotShowStatusMsg);
Daniel Molkentin's avatar
Daniel Molkentin committed
167

Laurent Montel's avatar
Laurent Montel committed
168
169
170
    // launch commandline specified module if any
    activateInitialPluginModule();

Laurent Montel's avatar
Laurent Montel committed
171
    if (Prefs::lastVersionSeen() == KAboutData::applicationData().version()) {
Laurent Montel's avatar
Laurent Montel committed
172
        selectPlugin(mCurrentPlugin);
Laurent Montel's avatar
Laurent Montel committed
173
174
    }

175
    paintAboutScreen(QStringLiteral("introduction_kontact.html"), introductionData());
Laurent Montel's avatar
Laurent Montel committed
176
    Prefs::setLastVersionSeen(KAboutData::applicationData().version());
177
178
}

Daniel Molkentin's avatar
Daniel Molkentin committed
179
MainWindow::~MainWindow()
180
{
Laurent Montel's avatar
Laurent Montel committed
181
    if (mCurrentPlugin) {
Laurent Montel's avatar
Laurent Montel committed
182
        KConfigGroup grp(KSharedConfig::openConfig()->group(QStringLiteral("MainWindow%1").arg(mCurrentPlugin->identifier())));
Laurent Montel's avatar
Laurent Montel committed
183
        saveMainWindowSettings(grp);
Laurent Montel's avatar
Laurent Montel committed
184
    }
185

Laurent Montel's avatar
Laurent Montel committed
186
    createGUI(nullptr);
Laurent Montel's avatar
Laurent Montel committed
187
    saveSettings();
188

Laurent Montel's avatar
Laurent Montel committed
189
    Prefs::self()->save();
190

Laurent Montel's avatar
Laurent Montel committed
191
192
    // During deletion of plugins, we should not access the plugin list (bug #182176)
    delete mSidePane;
193

194
    qDeleteAll(mPlugins);
195
196
}

197
// Called by main().
Laurent Montel's avatar
Laurent Montel committed
198
void MainWindow::setInitialActivePluginModule(const QString &module)
199
{
Laurent Montel's avatar
Laurent Montel committed
200
201
202
203
    if (mInitialActiveModule != module) {
        mInitialActiveModule = module;
        activateInitialPluginModule();
    }
204
205
}

Laurent Montel's avatar
Laurent Montel committed
206
bool MainWindow::pluginActionWeightLessThan(const QAction *left, const QAction *right)
207
{
Laurent Montel's avatar
Laurent Montel committed
208
209
210
    // Since this lessThan method is used only for the toolbar (which is on
    // the inverse layout direction than the rest of the system), we add the
    // elements on the exactly inverse order. (ereslibre)
Laurent Montel's avatar
Laurent Montel committed
211
    return !pluginWeightLessThan(left->data().value<KontactInterface::Plugin *>(), right->data().value<KontactInterface::Plugin *>());
212
213
}

Laurent Montel's avatar
Laurent Montel committed
214
bool MainWindow::pluginWeightLessThan(const KontactInterface::Plugin *left, const KontactInterface::Plugin *right)
215
{
Laurent Montel's avatar
Laurent Montel committed
216
    return left->weight() < right->weight();
217
218
}

219
void MainWindow::activateInitialPluginModule()
220
{
Laurent Montel's avatar
Laurent Montel committed
221
    if (!mInitialActiveModule.isEmpty() && !mPlugins.isEmpty()) {
222
        for (KontactInterface::Plugin *plugin : qAsConst(mPlugins)) {
Laurent Montel's avatar
Laurent Montel committed
223
            if (!plugin->identifier().isEmpty() && plugin->identifier().contains(mInitialActiveModule)) {
224
                selectPlugin(plugin);
Laurent Montel's avatar
Laurent Montel committed
225
226
227
                return;
            }
        }
Allen Winter's avatar
Allen Winter committed
228
    }
229
}
230

231
232
void MainWindow::initWidgets()
{
Laurent Montel's avatar
Laurent Montel committed
233
    auto mTopWidget = new QWidget(this);
234
    auto layout = new QVBoxLayout;
235
    layout->setContentsMargins(0, 0, 0, 0);
Laurent Montel's avatar
Laurent Montel committed
236
    layout->setSpacing(0);
Laurent Montel's avatar
Laurent Montel committed
237
238
    mTopWidget->setLayout(layout);
    setCentralWidget(mTopWidget);
Laurent Montel's avatar
Laurent Montel committed
239

Laurent Montel's avatar
Laurent Montel committed
240
    mSplitter = new QSplitter(mTopWidget);
241
    connect(mSplitter, &QSplitter::splitterMoved, this, &MainWindow::slotSplitterMoved);
Laurent Montel's avatar
Laurent Montel committed
242
243
    layout->addWidget(mSplitter);
    mSidePane = new IconSidePane(this, mSplitter);
244

Laurent Montel's avatar
Laurent Montel committed
245
    connect(mSidePane, SIGNAL(pluginSelected(KontactInterface::Plugin *)), SLOT(selectPlugin(KontactInterface::Plugin *)));
246

Laurent Montel's avatar
Laurent Montel committed
247
248
    mPartsStack = new QStackedWidget(mSplitter);
    mPartsStack->layout()->setSpacing(0);
249

Laurent Montel's avatar
Laurent Montel committed
250
    initAboutScreen();
Tobias Koenig's avatar
Tobias Koenig committed
251

252
    paintAboutScreen(QStringLiteral("loading_kontact.html"), QVariantHash());
253

254
    auto progressStatusBarWidget = new KPIM::ProgressStatusBarWidget(statusBar(), this);
255

Laurent Montel's avatar
Laurent Montel committed
256
    mStatusMsgLabel = new KSqueezedTextLabel(i18nc("@info:status", " Initializing..."), statusBar());
Laurent Montel's avatar
Laurent Montel committed
257
258
    mStatusMsgLabel->setTextElideMode(Qt::ElideRight);
    mStatusMsgLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
259

Laurent Montel's avatar
Laurent Montel committed
260
261
    statusBar()->addWidget(mStatusMsgLabel, 10);
    statusBar()->addPermanentWidget(progressStatusBarWidget->littleProgress(), 0);
262

Laurent Montel's avatar
Laurent Montel committed
263
    mSplitter->setCollapsible(1, false);
264
265
}

266
void MainWindow::paintAboutScreen(const QString &templateName, const QVariantHash &data)
267
{
Laurent Montel's avatar
Laurent Montel committed
268
    GrantleeTheme::ThemeManager manager(QStringLiteral("splashPage"), QStringLiteral("splash.theme"), nullptr, QStringLiteral("messageviewer/about/"));
269
270
271
    GrantleeTheme::Theme theme = manager.theme(QStringLiteral("default"));
    if (!theme.isValid()) {
        qCDebug(KONTACT_LOG) << "Theme error: failed to find splash theme";
Laurent Montel's avatar
Laurent Montel committed
272
    } else {
Laurent Montel's avatar
Laurent Montel committed
273
        mIntroPart->setHtml(theme.render(templateName, data, QByteArrayLiteral("kontact")), QUrl::fromLocalFile(theme.absolutePath() + QLatin1Char('/')));
Laurent Montel's avatar
Laurent Montel committed
274
    }
275
}
276

277
278
void MainWindow::initAboutScreen()
{
Laurent Montel's avatar
Laurent Montel committed
279
    auto introbox = new QWidget(mPartsStack);
280
    auto introboxHBoxLayout = new QHBoxLayout(introbox);
281
    introboxHBoxLayout->setContentsMargins(0, 0, 0, 0);
Laurent Montel's avatar
Laurent Montel committed
282
283
    mPartsStack->addWidget(introbox);
    mPartsStack->setCurrentWidget(introbox);
284
285
286
    mIntroPart = new IntroductionWebEngineView(introbox);
    connect(mIntroPart, &IntroductionWebEngineView::openUrl, this, &MainWindow::slotOpenUrl, Qt::QueuedConnection);
    introboxHBoxLayout->addWidget(mIntroPart);
287
288
}

Daniel Molkentin's avatar
Daniel Molkentin committed
289
void MainWindow::setupActions()
290
{
Laurent Montel's avatar
Laurent Montel committed
291
    actionCollection()->addAction(KStandardAction::Quit, this, SLOT(slotQuit()));
Laurent Montel's avatar
Laurent Montel committed
292

Laurent Montel's avatar
Laurent Montel committed
293
    mNewActions = new KActionMenu(i18nc("@title:menu create new pim items (message,calendar,to-do,etc.)", "New"), this);
Laurent Montel's avatar
Laurent Montel committed
294
    actionCollection()->addAction(QStringLiteral("action_new"), mNewActions);
Laurent Montel's avatar
Laurent Montel committed
295
    actionCollection()->setDefaultShortcuts(mNewActions, KStandardShortcut::openNew());
Laurent Montel's avatar
Laurent Montel committed
296
    connect(mNewActions, &KActionMenu::triggered, this, &MainWindow::slotNewClicked);
Laurent Montel's avatar
Laurent Montel committed
297

Laurent Montel's avatar
Laurent Montel committed
298
    auto action = new QAction(QIcon::fromTheme(QStringLiteral("configure")), i18nc("@action:inmenu", "Configure Kontact..."), this);
Laurent Montel's avatar
Laurent Montel committed
299
    setHelpText(action, i18nc("@info:status", "Configure Kontact"));
Laurent Montel's avatar
Laurent Montel committed
300
    action->setWhatsThis(i18nc("@info:whatsthis", "You will be presented with a dialog where you can configure Kontact."));
Laurent Montel's avatar
Laurent Montel committed
301
    actionCollection()->addAction(QStringLiteral("settings_configure_kontact"), action);
Laurent Montel's avatar
Laurent Montel committed
302
    connect(action, &QAction::triggered, this, &MainWindow::slotPreferences);
Laurent Montel's avatar
Laurent Montel committed
303

Laurent Montel's avatar
Laurent Montel committed
304
    action = new QAction(QIcon::fromTheme(QStringLiteral("kontact")), i18nc("@action:inmenu", "&Kontact Introduction"), this);
Laurent Montel's avatar
Laurent Montel committed
305
    setHelpText(action, i18nc("@info:status", "Show the Kontact Introduction page"));
Laurent Montel's avatar
Laurent Montel committed
306
    action->setWhatsThis(i18nc("@info:whatsthis", "Choose this option to see the Kontact Introduction page."));
Laurent Montel's avatar
Laurent Montel committed
307
    actionCollection()->addAction(QStringLiteral("help_introduction"), action);
Laurent Montel's avatar
Laurent Montel committed
308
    connect(action, &QAction::triggered, this, &MainWindow::slotShowIntroduction);
Laurent Montel's avatar
Laurent Montel committed
309

Laurent Montel's avatar
Laurent Montel committed
310
    mShowHideAction = new QAction(QIcon::fromTheme(QStringLiteral("zoom-fit-width")), i18nc("@action:inmenu", "Hide/Show the component sidebar"), this);
311
    setHelpText(mShowHideAction, i18nc("@info:status", "Hide/Show the component sidebar"));
312
313
    mShowHideAction->setCheckable(true);
    mShowHideAction->setChecked(Prefs::self()->sideBarOpen());
Laurent Montel's avatar
Laurent Montel committed
314
    mShowHideAction->setWhatsThis(i18nc("@info:whatsthis", "Allows you to show or hide the component sidebar as desired."));
315
    actionCollection()->addAction(QStringLiteral("hide_show_sidebar"), mShowHideAction);
316
    actionCollection()->setDefaultShortcut(mShowHideAction, QKeySequence(Qt::Key_F9));
317
    connect(mShowHideAction, &QAction::triggered, this, &MainWindow::slotShowHideSideBar);
318
319
}

320
KontactInterface::Plugin *MainWindow::pluginFromName(const QString &identifier) const
321
{
Laurent Montel's avatar
Laurent Montel committed
322
    auto hasIdentifier = [&](KontactInterface::Plugin *plugin) {
323
324
325
326
        return plugin->identifier() == identifier;
    };
    const auto it = std::find_if(mPlugins.constBegin(), mPlugins.constEnd(), hasIdentifier);
    return it == mPlugins.constEnd() ? nullptr : *it;
327
328
}

Daniel Molkentin's avatar
Daniel Molkentin committed
329
void MainWindow::loadPlugins()
330
{
Laurent Montel's avatar
Laurent Montel committed
331
    QList<KontactInterface::Plugin *> plugins;
332
333
    for (const KPluginInfo &pluginInfo : qAsConst(mPluginInfos)) {
        if (!pluginInfo.isPluginEnabled()) {
Laurent Montel's avatar
Laurent Montel committed
334
335
            continue;
        }
336

Laurent Montel's avatar
Laurent Montel committed
337
        const QString pluginPath = pluginInfo.pluginName();
338
        KontactInterface::Plugin *plugin = pluginFromName(pluginPath);
339
340
        if (plugin) { // already loaded
            plugin->configUpdated();
Laurent Montel's avatar
Laurent Montel committed
341
342
            continue;
        }
343

344
        qCDebug(KONTACT_LOG) << "Loading Plugin:" << pluginInfo.name();
345
        KPluginLoader loader(pluginInfo.libraryPath());
Laurent Montel's avatar
Laurent Montel committed
346
        KPluginFactory *factory = loader.factory();
347
348
        if (!factory) {
            qWarning() << "Error loading plugin" << pluginInfo.libraryPath() << loader.errorString();
Laurent Montel's avatar
Laurent Montel committed
349
            continue;
350
351
352
353
354
355
        } else {
            plugin = factory->create<KontactInterface::Plugin>(this);
            if (!plugin) {
                qCWarning(KONTACT_LOG) << "Unable to create plugin for" << pluginInfo.libraryPath();
                continue;
            }
Laurent Montel's avatar
Laurent Montel committed
356
        }
357

358
        plugin->setIdentifier(pluginInfo.pluginName());
359
360
        plugin->setTitle(pluginInfo.name());
        plugin->setIcon(pluginInfo.icon());
361

Laurent Montel's avatar
Laurent Montel committed
362
363
364
365
        const QVariant libNameProp = pluginInfo.property(QStringLiteral("X-KDE-KontactPartLibraryName"));
        const QVariant exeNameProp = pluginInfo.property(QStringLiteral("X-KDE-KontactPartExecutableName"));
        const QVariant loadOnStart = pluginInfo.property(QStringLiteral("X-KDE-KontactPartLoadOnStart"));
        const QVariant hasPartProp = pluginInfo.property(QStringLiteral("X-KDE-KontactPluginHasPart"));
366

Laurent Montel's avatar
Laurent Montel committed
367
368
        if (!loadOnStart.isNull() && loadOnStart.toBool()) {
            mDelayedPreload.append(plugin);
Laurent Montel's avatar
Laurent Montel committed
369
        }
370

Laurent Montel's avatar
Laurent Montel committed
371
        qCDebug(KONTACT_LOG) << "LIBNAMEPART:" << libNameProp.toString();
372

Laurent Montel's avatar
Laurent Montel committed
373
374
375
376
        plugin->setPartLibraryName(libNameProp.toString().toUtf8());
        plugin->setExecutableName(exeNameProp.toString());
        if (hasPartProp.isValid()) {
            plugin->setShowInSideBar(hasPartProp.toBool());
Laurent Montel's avatar
Laurent Montel committed
377
        }
378
        plugins.append(plugin);
379
    }
380
    std::sort(plugins.begin(), plugins.end(), pluginWeightLessThan); // new plugins
381

382
    for (KontactInterface::Plugin *plugin : qAsConst(plugins)) {
Laurent Montel's avatar
Laurent Montel committed
383
        const QList<QAction *> actionList = plugin->newActions();
384
385
386
        for (QAction *action : actionList) {
            qCDebug(KONTACT_LOG) << "Plugging new action" << action->objectName();
            mNewActions->addAction(action);
Laurent Montel's avatar
Laurent Montel committed
387
        }
Laurent Montel's avatar
Laurent Montel committed
388
        addPlugin(plugin);
389
    }
Tobias Koenig's avatar
Tobias Koenig committed
390

391
392
393
394
395
396
397
398
    std::sort(mPlugins.begin(), mPlugins.end(), pluginWeightLessThan); // all plugins

    // sort the action plugins again and reset shortcuts. If we removed and then readded some plugins
    // we need to take in count their weights for setting shortcuts again
    std::sort(mActionPlugins.begin(), mActionPlugins.end(), pluginActionWeightLessThan);

    updateShortcuts();

Laurent Montel's avatar
Laurent Montel committed
399
400
    const bool state = (!mPlugins.isEmpty());
    mNewActions->setEnabled(state);
401
402
}

403
void MainWindow::unloadDisabledPlugins()
404
{
405
406
    // Only remove the now-disabled plugins.
    // Keep the other ones. loadPlugins() will skip those that are already loaded.
407
408
    for (const KPluginInfo &pluginInfo : qAsConst(mPluginInfos)) {
        if (!pluginInfo.isPluginEnabled()) {
409
            removePlugin(pluginInfo.pluginName());
Laurent Montel's avatar
Laurent Montel committed
410
        }
Allen Winter's avatar
Allen Winter committed
411
    }
412
}
413

414
415
void MainWindow::updateShortcuts()
{
416
417
418
419
420
421
    for (int i = 0; i < mActionPlugins.count(); ++i) {
        QAction *action = mActionPlugins.at(i);
        const QKeySequence shortcut(QStringLiteral("Ctrl+%1").arg(mActionPlugins.count() - i));
        actionCollection()->setDefaultShortcut(action, shortcut);
        // Prevent plugActionList from restoring some old saved shortcuts
        action->setProperty("_k_DefaultShortcut", QVariant::fromValue(QList<QKeySequence>{shortcut}));
Laurent Montel's avatar
Laurent Montel committed
422
    }
423
424
}

425
bool MainWindow::removePlugin(const QString &pluginName)
426
{
Laurent Montel's avatar
Laurent Montel committed
427
    auto hasIdentifier = [&](KontactInterface::Plugin *plugin) {
428
429
430
431
432
433
434
435
436
437
438
439
440
        return plugin->identifier() == pluginName;
    };
    const auto it = std::find_if(mPlugins.begin(), mPlugins.end(), hasIdentifier);
    if (it == mPlugins.end()) {
        qCDebug(KONTACT_LOG) << "Plugin not found" << pluginName;
        return false;
    }
    KontactInterface::Plugin *plugin = *it;
    const QList<QAction *> actionList = plugin->newActions();
    for (QAction *action : actionList) {
        qCDebug(KONTACT_LOG) << QStringLiteral("Unplugging New actions") << action->objectName();
        mNewActions->removeAction(action);
    }
Laurent Montel's avatar
Laurent Montel committed
441

442
    removeChildClient(plugin);
Laurent Montel's avatar
Laurent Montel committed
443

444
445
446
447
    if (mCurrentPlugin == plugin) {
        mCurrentPlugin = nullptr;
        createGUI(nullptr);
    }
Laurent Montel's avatar
Laurent Montel committed
448

449
450
451
452
453
454
455
456
    plugin->deleteLater(); // removes the part automatically
    mPlugins.erase(it);
    if (plugin->showInSideBar()) {
        QAction *q = mPluginAction[plugin]; // remove QAction, to free the shortcut for later use
        mActionPlugins.removeAll(q);
        mPluginAction.remove(plugin);
        actionCollection()->removeAction(q); // deletes q
    }
Laurent Montel's avatar
Laurent Montel committed
457

458
459
460
461
462
    if (mCurrentPlugin == nullptr) {
        for (KontactInterface::Plugin *plugin : qAsConst(mPlugins)) {
            if (plugin->showInSideBar()) {
                selectPlugin(plugin);
                return true;
Laurent Montel's avatar
Laurent Montel committed
463
            }
464
        }
Laurent Montel's avatar
Laurent Montel committed
465
    }
466
    return true;
467
468
}

Laurent Montel's avatar
Laurent Montel committed
469
void MainWindow::addPlugin(KontactInterface::Plugin *plugin)
470
{
Laurent Montel's avatar
Laurent Montel committed
471
    qCDebug(KONTACT_LOG);
Laurent Montel's avatar
Laurent Montel committed
472

Laurent Montel's avatar
Laurent Montel committed
473
    mPlugins.append(plugin);
Laurent Montel's avatar
Laurent Montel committed
474

Laurent Montel's avatar
Laurent Montel committed
475
    if (plugin->showInSideBar()) {
Laurent Montel's avatar
Laurent Montel committed
476
        auto action = new QAction(QIcon::fromTheme(plugin->icon()), plugin->title(), this);
Laurent Montel's avatar
Laurent Montel committed
477
        // action->setHelpText(
Laurent Montel's avatar
Laurent Montel committed
478
        //            i18nc( "@info:status", "Plugin %1", plugin->title() ) );
Laurent Montel's avatar
Laurent Montel committed
479
        action->setWhatsThis(i18nc("@info:whatsthis", "Switch to plugin %1", plugin->title()));
Laurent Montel's avatar
Laurent Montel committed
480
        action->setCheckable(true);
481
482
483
484
485
        action->setData(QVariant::fromValue(plugin)); // used by pluginActionWeightLessThan
        connect(action, &QAction::triggered, this, [=]() {
            slotActionTriggered(action, plugin->identifier());
        });
        actionCollection()->addAction(plugin->identifier(), action);
Laurent Montel's avatar
Laurent Montel committed
486
487
        mActionPlugins.append(action);
        mPluginAction.insert(plugin, action);
Laurent Montel's avatar
Laurent Montel committed
488
    }
489

Laurent Montel's avatar
Laurent Montel committed
490
    // merge the plugins GUI into the main window
Laurent Montel's avatar
Laurent Montel committed
491
    insertChildClient(plugin);
492
493
}

494
void MainWindow::partLoaded(KontactInterface::Plugin *plugin, KParts::Part *part)
495
{
Laurent Montel's avatar
Laurent Montel committed
496
    Q_UNUSED(plugin)
Allen Winter's avatar
Allen Winter committed
497

Laurent Montel's avatar
Laurent Montel committed
498
    // See if we have this part already (e.g. due to two plugins sharing it)
Laurent Montel's avatar
Laurent Montel committed
499
    if (mPartsStack->indexOf(part->widget()) != -1) {
Laurent Montel's avatar
Laurent Montel committed
500
501
        return;
    }
502

Laurent Montel's avatar
Laurent Montel committed
503
    mPartsStack->addWidget(part->widget());
504

Laurent Montel's avatar
Laurent Montel committed
505
    mPartManager->addPart(part, false);
Laurent Montel's avatar
Laurent Montel committed
506
507
    // Workaround for KParts misbehavior: addPart calls show!
    part->widget()->hide();
508
509
}

Laurent Montel's avatar
Laurent Montel committed
510
void MainWindow::slotActivePartChanged(KParts::Part *part)
511
{
Laurent Montel's avatar
Laurent Montel committed
512
    if (!part) {
Laurent Montel's avatar
Laurent Montel committed
513
        createGUI(nullptr);
Laurent Montel's avatar
Laurent Montel committed
514
515
        return;
    }
516

Laurent Montel's avatar
Laurent Montel committed
517
    qCDebug(KONTACT_LOG) << QStringLiteral("Part activated:") << part << QStringLiteral("with stack id.") << mPartsStack->indexOf(part->widget());
518

Laurent Montel's avatar
Laurent Montel committed
519
    statusBar()->clearMessage();
520
521
}

522
523
void MainWindow::slotNewClicked()
{
Laurent Montel's avatar
Laurent Montel committed
524
    if (!mCurrentPlugin->newActions().isEmpty()) {
Laurent Montel's avatar
Laurent Montel committed
525
        mCurrentPlugin->newActions().at(0)->trigger();
Laurent Montel's avatar
Laurent Montel committed
526
    } else {
527
528
        for (KontactInterface::Plugin *plugin : qAsConst(mPlugins)) {
            if (!plugin->newActions().isEmpty()) {
Laurent Montel's avatar
Laurent Montel committed
529
                plugin->newActions().constFirst()->trigger();
Laurent Montel's avatar
Laurent Montel committed
530
531
532
                return;
            }
        }
533
    }
534
535
}

Laurent Montel's avatar
Laurent Montel committed
536
KToolBar *MainWindow::findToolBar(const char *name)
537
{
Laurent Montel's avatar
Laurent Montel committed
538
    // like KMainWindow::toolBar, but which doesn't create the toolbar if not found
Laurent Montel's avatar
Laurent Montel committed
539
    return findChild<KToolBar *>(QLatin1String(name));
540
541
}

Laurent Montel's avatar
Laurent Montel committed
542
void MainWindow::selectPlugin(KontactInterface::Plugin *plugin)
543
{
Laurent Montel's avatar
Laurent Montel committed
544
    if (!plugin) {
Laurent Montel's avatar
Laurent Montel committed
545
546
        return;
    }
547

Laurent Montel's avatar
Laurent Montel committed
548
    if (plugin->isRunningStandalone()) {
Laurent Montel's avatar
Laurent Montel committed
549
        statusBar()->showMessage(i18nc("@info:status", "Application is running standalone. Foregrounding..."), 1000);
Laurent Montel's avatar
Laurent Montel committed
550
551
552
        plugin->bringToForeground();
        return;
    }
553

Laurent Montel's avatar
Laurent Montel committed
554
    QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
555

Laurent Montel's avatar
Laurent Montel committed
556
    if (mCurrentPlugin) {
Laurent Montel's avatar
Laurent Montel committed
557
        KConfigGroup grp = KSharedConfig::openConfig()->group(QStringLiteral("MainWindow%1").arg(mCurrentPlugin->identifier()));
Laurent Montel's avatar
Laurent Montel committed
558
        saveMainWindowSettings(grp);
Laurent Montel's avatar
Laurent Montel committed
559
    }
560

Laurent Montel's avatar
Laurent Montel committed
561
562
    KParts::Part *part = plugin->part();

Laurent Montel's avatar
Laurent Montel committed
563
    if (!part) {
Laurent Montel's avatar
Laurent Montel committed
564
        QApplication::restoreOverrideCursor();
Laurent Montel's avatar
Laurent Montel committed
565
        KMessageBox::error(this, i18nc("@info", "Cannot load part for %1.", plugin->title()) + QLatin1Char('\n') + lastErrorMessage());
Laurent Montel's avatar
Laurent Montel committed
566
        plugin->setDisabled(true);
Laurent Montel's avatar
Laurent Montel committed
567
568
569
        mSidePane->updatePlugins();
        return;
    }
570

Laurent Montel's avatar
Laurent Montel committed
571
    if (mCurrentPlugin) {
Laurent Montel's avatar
Laurent Montel committed
572
        QAction *action = mPluginAction.value(mCurrentPlugin);
Laurent Montel's avatar
Laurent Montel committed
573
574
        if (action) {
            action->setChecked(false);
Laurent Montel's avatar
Laurent Montel committed
575
        }
576
    }
Laurent Montel's avatar
Laurent Montel committed
577
    QAction *selectedPluginAction = mPluginAction.value(mCurrentPlugin);
Laurent Montel's avatar
Laurent Montel committed
578
579
    if (selectedPluginAction) {
        selectedPluginAction->setChecked(true);
Tobias Koenig's avatar
Tobias Koenig committed
580
581
    }

Laurent Montel's avatar
Laurent Montel committed
582
    // store old focus widget
583
    QWidget *focusWidget = qApp->focusWidget();
Laurent Montel's avatar
Laurent Montel committed
584
    if (mCurrentPlugin && focusWidget) {
Laurent Montel's avatar
Laurent Montel committed
585
586
        // save the focus widget only when it belongs to the activated part
        QWidget *parent = focusWidget->parentWidget();
Laurent Montel's avatar
Laurent Montel committed
587
588
589
        while (parent) {
            if (parent == mCurrentPlugin->part()->widget()) {
                mFocusWidgets.insert(mCurrentPlugin->identifier(), QPointer<QWidget>(focusWidget));
Laurent Montel's avatar
Laurent Montel committed
590
591
592
593
            }
            parent = parent->parentWidget();
        }
    }
594

Laurent Montel's avatar
Laurent Montel committed
595
596
    if (mSidePane) {
        mSidePane->setCurrentPlugin(plugin->identifier());
Laurent Montel's avatar
Laurent Montel committed
597
    }
598

Laurent Montel's avatar
Laurent Montel committed
599
    plugin->aboutToSelect();
600

Laurent Montel's avatar
Laurent Montel committed
601
    mPartManager->setActivePart(part);
Laurent Montel's avatar
Laurent Montel committed
602
    QWidget *view = part->widget();
Laurent Montel's avatar
Laurent Montel committed
603
    Q_ASSERT(view);
604

Laurent Montel's avatar
Laurent Montel committed
605
606
    if (view) {
        mPartsStack->setCurrentWidget(view);
Laurent Montel's avatar
Laurent Montel committed
607
        view->show();
608

Laurent Montel's avatar
Laurent Montel committed
609
        if (!mFocusWidgets.isEmpty() && mFocusWidgets.contains(plugin->identifier())) {
Laurent Montel's avatar
Laurent Montel committed
610
            focusWidget = mFocusWidgets.value(plugin->identifier());
Laurent Montel's avatar
Laurent Montel committed
611
            if (focusWidget) {
Laurent Montel's avatar
Laurent Montel committed
612
613
614
615
616
                focusWidget->setFocus();
            }
        } else {
            view->setFocus();
        }
617

Laurent Montel's avatar
Laurent Montel committed
618
        mCurrentPlugin = plugin;
619

Laurent Montel's avatar
Laurent Montel committed
620
        QAction *newAction = nullptr;
Laurent Montel's avatar
Laurent Montel committed
621
        if (!plugin->newActions().isEmpty()) {
Laurent Montel's avatar
Laurent Montel committed
622
            newAction = plugin->newActions().at(0);
Laurent Montel's avatar
Laurent Montel committed
623
        }
624

625
626
        const QString oldWindowTitle = windowTitle();

Laurent Montel's avatar
Laurent Montel committed
627
        createGUI(plugin->part());
Laurent Montel's avatar
Laurent Montel committed
628
        plugin->shortcutChanged();
629

630
        // Only some parts (like kmail) set a caption in guiActivateEvent when being activated.
631
        // For others, this is the default caption:
632
        if (windowTitle() == oldWindowTitle) {
Laurent Montel's avatar
Laurent Montel committed
633
            setCaption(i18nc("@title:window Plugin dependent window title", "%1 - Kontact", plugin->title()));
634
        }
635

Laurent Montel's avatar
Laurent Montel committed
636
637
        if (newAction) {
            mNewActions->setIcon(newAction->icon());
638
            mNewActions->setText(newAction->text());
Laurent Montel's avatar
Laurent Montel committed
639
            mNewActions->setWhatsThis(newAction->whatsThis());
Laurent Montel's avatar
Laurent Montel committed
640
        } else { // we'll use the action of the first plugin which offers one
641
642
            for (KontactInterface::Plugin *plugin : qAsConst(mPlugins)) {
                if (!plugin->newActions().isEmpty()) {
Laurent Montel's avatar
Laurent Montel committed
643
                    newAction = plugin->newActions().constFirst();
Laurent Montel's avatar
Laurent Montel committed
644
                }
Laurent Montel's avatar
Laurent Montel committed
645
                if (newAction) {
646
                    mNewActions->setIcon(newAction->icon());
Laurent Montel's avatar
Laurent Montel committed
647
648
                    mNewActions->setText(newAction->text());
                    mNewActions->setWhatsThis(newAction->whatsThis());
Laurent Montel's avatar
Laurent Montel committed
649
650
651
                    break;
                }
            }
652
        }
653
    }
654

Laurent Montel's avatar
Laurent Montel committed
655
    KToolBar *navigatorToolBar = findToolBar("navigatorToolBar");
Laurent Montel's avatar
Laurent Montel committed
656
    if (navigatorToolBar && !navigatorToolBar->isHidden()
Laurent Montel's avatar
Laurent Montel committed
657
        && (toolBarArea(navigatorToolBar) == Qt::TopToolBarArea || toolBarArea(navigatorToolBar) == Qt::BottomToolBarArea)) {
Laurent Montel's avatar
Laurent Montel committed
658
        addToolBar(toolBarArea(navigatorToolBar), navigatorToolBar);
Laurent Montel's avatar
Laurent Montel committed
659
    }
660

Laurent Montel's avatar
Laurent Montel committed
661
    applyMainWindowSettings(KSharedConfig::openConfig()->group(QStringLiteral("MainWindow%1").arg(plugin->identifier())));
662

Laurent Montel's avatar
Laurent Montel committed
663
    QApplication::restoreOverrideCursor();
664
665
}

666
void MainWindow::slotActionTriggered(QAction *action, const QString &identifier)
667
{
668
669
670
    action->setChecked(true);
    if (!identifier.isEmpty()) {
        mSidePane->setCurrentPlugin(identifier);
Laurent Montel's avatar
Laurent Montel committed
671
    }
672
673
}

Laurent Montel's avatar
Laurent Montel committed
674
void MainWindow::selectPlugin(const QString &pluginName)
675
{
676
677
678
    KontactInterface::Plugin *plugin = pluginFromName(pluginName);
    if (plugin) {
        selectPlugin(plugin);
679
680
681
    }
}

Tobias Koenig's avatar
   
Tobias Koenig committed
682
683
void MainWindow::loadSettings()
{
Laurent Montel's avatar
Laurent Montel committed
684
    if (mSplitter) {
685
        showHideSideBar(Prefs::self()->sideBarOpen());
Laurent Montel's avatar
Laurent Montel committed
686
687
688
    }

    // Preload Plugins. This _must_ happen before the default part is loaded
689
690
    for (KontactInterface::Plugin *plugin : qAsConst(mDelayedPreload)) {
        selectPlugin(plugin);
691
    }
Laurent Montel's avatar
Laurent Montel committed
692
    selectPlugin(Prefs::self()->mActivePlugin);
Tobias Koenig's avatar
   
Tobias Koenig committed
693
694
695
696
}

void MainWindow::saveSettings()
{
Laurent Montel's avatar
Laurent Montel committed
697
    if (mSplitter) {
Laurent Montel's avatar
Laurent Montel committed
698
699
        Prefs::self()->mSidePaneSplitter = mSplitter->sizes();
    }
700

Laurent Montel's avatar
Laurent Montel committed
701
    if (mCurrentPlugin) {
Laurent Montel's avatar
Laurent Montel committed
702
703
        Prefs::self()->mActivePlugin = mCurrentPlugin->identifier();
    }
Tobias Koenig's avatar
   
Tobias Koenig committed
704
}
705

706
707
void MainWindow::slotShowIntroduction()
{
Laurent Montel's avatar
Laurent Montel committed
708
    mPartsStack->setCurrentIndex(0);
709
710
}

Daniel Molkentin's avatar
Daniel Molkentin committed
711
void MainWindow::slotQuit()
712
{
Laurent Montel's avatar
Laurent Montel committed
713
714
    mReallyClose = true;
    close();
715
716
}

Daniel Molkentin's avatar
Daniel Molkentin committed
717
void MainWindow::slotPreferences()
Daniel Molkentin's avatar
Daniel Molkentin committed
718
{
Laurent Montel's avatar
Laurent Montel committed
719
    static Kontact::KontactConfigureDialog *dlg = nullptr;
Laurent Montel's avatar
Laurent Montel committed
720
721
722
    if (!dlg) {
        dlg = new Kontact::KontactConfigureDialog(this);
        dlg->setAllowComponentSelection(true);
723
724
725
726
727
        connect(dlg, QOverload<const QByteArray &>::of(&KSettings::Dialog::configCommitted), this, [this](const QByteArray &componentName) {
            if (componentName == QByteArrayLiteral("kontact")) {
                MainWindow::updateConfig();
            }
        });
Laurent Montel's avatar
Laurent Montel committed
728
729
730
731

        // do not show settings of components running standalone
        KPluginInfo::List filteredPlugins = mPluginInfos;
        PluginList::ConstIterator end(mPlugins.constEnd());
Laurent Montel's avatar
Laurent Montel committed
732
        for (PluginList::ConstIterator it = mPlugins.constBegin(); it != end; ++it) {
Laurent Montel's avatar
Laurent Montel committed
733
            if ((*it)->isRunningStandalone()) {
Laurent Montel's avatar
Laurent Montel committed
734
735
                KPluginInfo::List::ConstIterator infoIt;
                KPluginInfo::List::ConstIterator infoEnd(filteredPlugins.constEnd());
Laurent Montel's avatar
Laurent Montel committed
736
                for (infoIt = filteredPlugins.constBegin(); infoIt != infoEnd; ++infoIt) {
Laurent Montel's avatar
Laurent Montel committed
737
738
                    if (infoIt->pluginName() == (*it)->identifier()) {
                        filteredPlugins.removeAll(*infoIt);
Laurent Montel's avatar
Laurent Montel committed
739
740
741
742
                        break;
                    }
                }
            }
743
        }
Laurent Montel's avatar
Laurent Montel committed
744
        dlg->addPluginInfos(filteredPlugins);
Laurent Montel's avatar
Laurent Montel committed
745
        connect(dlg, &Kontact::KontactConfigureDialog::pluginSelectionChanged, this, &MainWindow::pluginsChanged);
Laurent Montel's avatar
Laurent Montel committed
746
    }
747

Laurent Montel's avatar
Laurent Montel committed
748
    dlg->show();
Daniel Molkentin's avatar
Daniel Molkentin committed
749
}