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

Allen Winter's avatar
Allen Winter committed
4 5 6
  Copyright (c) 2001 Matthias Hoelzer-Kluepfel <mhk@kde.org>
  Copyright (c) 2002-2005 Daniel Molkentin <molkentin@kde.org>
  Copyright (c) 2003-2005 Cornelius Schumacher <schumacher@kde.org>
7

Allen Winter's avatar
Allen Winter committed
8 9 10 11
  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.
12

Allen Winter's avatar
Allen Winter committed
13 14 15 16
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU General Public License for more details.
17

Allen Winter's avatar
Allen Winter committed
18 19 20
  You should have received a copy of the GNU General Public License along
  with this program; if not, write to the Free Software Foundation, Inc.,
  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 22
*/

Allen Winter's avatar
Allen Winter committed
23 24 25
#include "mainwindow.h"
#include "aboutdialog.h"
#include "prefs.h"
Allen Winter's avatar
Allen Winter committed
26
#include "iconsidepane.h"
27 28 29 30

#include "webengine/introductionwebengineview.h"
#include "webengine/introductionwebenginepage.h"

31
#include "kontactconfiguredialog.h"
Allen Winter's avatar
Allen Winter committed
32
using namespace Kontact;
Laurent Montel's avatar
Laurent Montel committed
33 34 35
#ifdef WIN32
#include <windows.h>
#else
36
#include <unistd.h>
Laurent Montel's avatar
Laurent Montel committed
37
#endif
Laurent Montel's avatar
Laurent Montel committed
38 39 40
#include <Libkdepim/BroadcastStatus>
#include <Libkdepim/ProgressStatusBarWidget>
#include <Libkdepim/StatusbarProgressWidget>
Allen Winter's avatar
Allen Winter committed
41

Laurent Montel's avatar
Laurent Montel committed
42
#include <KontactInterface/Core>
Allen Winter's avatar
Allen Winter committed
43

Laurent Montel's avatar
Laurent Montel committed
44
#include <QStatusBar>
Laurent Montel's avatar
Laurent Montel committed
45
#include <KWindowConfig>
Laurent Montel's avatar
Laurent Montel committed
46
#include <KXMLGUIFactory>
Allen Winter's avatar
Allen Winter committed
47 48 49 50
#include <KActionCollection>
#include <KActionMenu>
#include <KConfigGroup>
#include <KDBusServiceStarter>
Laurent Montel's avatar
Laurent Montel committed
51
#include "kontact_debug.h"
Allen Winter's avatar
Allen Winter committed
52 53 54 55 56 57 58 59 60 61 62
#include <KEditToolBar>
#include <KHelpMenu>
#include <KMessageBox>
#include <KPluginInfo>
#include <KRun>
#include <KServiceTypeTrader>
#include <KShortcutsDialog>
#include <KSqueezedTextLabel>
#include <KStandardAction>
#include <KToolBar>
#include <KParts/PartManager>
63
#include <ksettings/Dispatcher>
64
#include <KSycoca>
65
#include <KLocalizedString>
66
#include <QDBusConnection>
Allen Winter's avatar
Allen Winter committed
67
#include <QSplitter>
68
#include <QStackedWidget>
Allen Winter's avatar
Allen Winter committed
69
#include <QTimer>
Allen Winter's avatar
Allen Winter committed
70
#include <QVBoxLayout>
71
#include <QShortcut>
Laurent Montel's avatar
Laurent Montel committed
72
#include <QIcon>
73
#include <KHelpClient>
74
#include <KSharedConfig>
Laurent Montel's avatar
Laurent Montel committed
75
#include <QStandardPaths>
76
#include <QFile>
Laurent Montel's avatar
Laurent Montel committed
77
#include <QHBoxLayout>
78 79
#include <QApplication>
#include <KAboutData>
80 81 82 83
#include <QFontDatabase>

#include <grantleetheme/grantleethememanager.h>
#include <grantleetheme/grantleetheme.h>
Daniel Molkentin's avatar
Daniel Molkentin committed
84

85 86
// Define the maximum time Kontact waits for KSycoca to become available.
static const int KSYCOCA_WAIT_TIMEOUT = 10;
87

88 89 90 91 92 93 94
// This class extends the normal KDBusServiceStarter.
//
// When a service start is requested, it asks all plugins
// to create their dbus interfaces in addition to the normal
// way of starting a service.
class ServiceStarter : public KDBusServiceStarter
{
Laurent Montel's avatar
Laurent Montel committed
95
public:
96

Laurent Montel's avatar
Laurent Montel committed
97 98
    virtual int startServiceFor(const QString &serviceType,
                                const QString &constraint = QString(),
Laurent Montel's avatar
Laurent Montel committed
99
                                QString *error = nullptr, QString *dbusService = nullptr,
100
                                int flags = 0) override;
101 102

    // We need to keep track of the plugins which are loaded, so pass a pointer
Allen Winter's avatar
Allen Winter committed
103
    // to the plugin list here. Be sure to reset it back to 0 with
104
    // setPluginList() as soon as the list gets destroyed.
Laurent Montel's avatar
Laurent Montel committed
105 106
    ServiceStarter(PluginList *pluginList)
    {
Laurent Montel's avatar
Laurent Montel committed
107
        mPlugins = pluginList;
108 109
    }

Laurent Montel's avatar
Laurent Montel committed
110 111
    static void setPluginList(PluginList *pluginList)
    {
Laurent Montel's avatar
Laurent Montel committed
112
        mPlugins = pluginList;
113 114
    }

Laurent Montel's avatar
Laurent Montel committed
115
protected:
116 117 118 119 120

    virtual ~ServiceStarter() {}
    static PluginList *mPlugins;
};

Laurent Montel's avatar
Laurent Montel committed
121
PluginList *ServiceStarter::mPlugins = nullptr;
Allen Winter's avatar
Allen Winter committed
122

Laurent Montel's avatar
Laurent Montel committed
123 124 125 126
int ServiceStarter::startServiceFor(const QString &serviceType,
                                    const QString &constraint,
                                    QString *error, QString *dbusService,
                                    int flags)
127
{
Laurent Montel's avatar
Laurent Montel committed
128
    if (mPlugins) {
Laurent Montel's avatar
Laurent Montel committed
129
        const PluginList::ConstIterator end = mPlugins->constEnd();
Laurent Montel's avatar
Laurent Montel committed
130 131
        for (PluginList::ConstIterator it = mPlugins->constBegin(); it != end; ++it) {
            if ((*it)->createDBUSInterface(serviceType)) {
Laurent Montel's avatar
Laurent Montel committed
132
                qCDebug(KONTACT_LOG) << "found interface for" << serviceType;
Laurent Montel's avatar
Laurent Montel committed
133
                if (dbusService) {
Laurent Montel's avatar
Laurent Montel committed
134 135 136 137
                    *dbusService = (*it)->registerClient();
                }
                return 0;
            }
Allen Winter's avatar
Allen Winter committed
138
        }
139
    }
Thomas McGuire's avatar
Thomas McGuire committed
140

Laurent Montel's avatar
Laurent Montel committed
141
    qCDebug(KONTACT_LOG) << "Didn't find dbus interface, falling back to external process";
Laurent Montel's avatar
Laurent Montel committed
142 143
    return KDBusServiceStarter::startServiceFor(serviceType, constraint,
            error, dbusService, flags);
144 145
}

146
MainWindow::MainWindow()
Laurent Montel's avatar
Laurent Montel committed
147
    : KontactInterface::Core(), mSplitter(nullptr), mCurrentPlugin(nullptr), mAboutDialog(nullptr),
148
    mReallyClose(false), mSaveSideBarWidth(10)
149
{
Laurent Montel's avatar
Laurent Montel committed
150 151
    // The ServiceStarter created here will be deleted by the KDbusServiceStarter
    // base class, which is a global static.
Laurent Montel's avatar
Laurent Montel committed
152
    new ServiceStarter(&mPlugins);
153

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

Laurent Montel's avatar
Laurent Montel committed
157 158
    // 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
159
    setAttribute(Qt::WA_GroupLeader);
160

Laurent Montel's avatar
Laurent Montel committed
161 162
    initGUI();
    initObject();
163

Laurent Montel's avatar
Laurent Montel committed
164 165
    mSidePane->setMaximumWidth(mSidePane->sizeHint().width());
    mSidePane->setMinimumWidth(mSidePane->sizeHint().width());
166

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

Laurent Montel's avatar
Laurent Montel committed
169 170
    KConfigGroup grp(KSharedConfig::openConfig(), "MainWindow");
    KWindowConfig::restoreWindowSize(windowHandle(), grp);
Laurent Montel's avatar
Laurent Montel committed
171
    setAutoSaveSettings();
172
}
173

174 175
void MainWindow::initGUI()
{
Laurent Montel's avatar
Laurent Montel committed
176 177
    initWidgets();
    setupActions();
Laurent Montel's avatar
Laurent Montel committed
178
    setHelpMenuEnabled(false);
Laurent Montel's avatar
Laurent Montel committed
179
    KHelpMenu *helpMenu = new KHelpMenu(this, QString(), true);
Laurent Montel's avatar
Laurent Montel committed
180
    connect(helpMenu, &KHelpMenu::showAboutApplication, this, &MainWindow::showAboutDialog);
181

Laurent Montel's avatar
Laurent Montel committed
182 183
    KStandardAction::keyBindings(this, &MainWindow::configureShortcuts, actionCollection());
    KStandardAction::configureToolbars(this, &MainWindow::configureToolbars, actionCollection());
184
    setXMLFile(QStringLiteral("kontactui.rc"));
185

Laurent Montel's avatar
Laurent Montel committed
186
    setStandardToolBarMenuEnabled(true);
187

Laurent Montel's avatar
Laurent Montel committed
188
    createGUI(nullptr);
189

Laurent Montel's avatar
Laurent Montel committed
190 191 192 193
    KToolBar *navigatorToolBar = findToolBar("navigatorToolBar");
    if (navigatorToolBar) {
        if (layoutDirection() == Qt::LeftToRight) {
            navigatorToolBar->setLayoutDirection(Qt::RightToLeft);
Laurent Montel's avatar
Laurent Montel committed
194
        } else {
Laurent Montel's avatar
Laurent Montel committed
195
            navigatorToolBar->setLayoutDirection(Qt::LeftToRight);
Laurent Montel's avatar
Laurent Montel committed
196
        }
Laurent Montel's avatar
Laurent Montel committed
197 198
        Q_ASSERT(navigatorToolBar->sizeHint().isValid());
        navigatorToolBar->setMinimumWidth(navigatorToolBar->sizeHint().width());
Allen Winter's avatar
Allen Winter committed
199
    } else {
Laurent Montel's avatar
Laurent Montel committed
200
        qCritical() << "Unable to find navigatorToolBar, probably kontactui.rc is missing";
Allen Winter's avatar
Allen Winter committed
201
    }
202 203
}

204 205
void MainWindow::waitForKSycoca()
{
Laurent Montel's avatar
Laurent Montel committed
206
    int i = 0;
Laurent Montel's avatar
Laurent Montel committed
207 208
    while (i < KSYCOCA_WAIT_TIMEOUT) {
        if (KSycoca::isAvailable()) {
Laurent Montel's avatar
Laurent Montel committed
209 210 211 212 213 214 215 216
            return;
        }
        // When KSycoca is not availabe that usually means Kontact
        // was started before kded is done with it's first run
        // we want to block Kontact execution to
        // give Kded time to initialize and create the
        // System Configuration database necessary for further
        // Kontact startup
Laurent Montel's avatar
Laurent Montel committed
217
        qCDebug(KONTACT_LOG) << "Waiting for KSycoca";
Laurent Montel's avatar
Laurent Montel committed
218 219 220
#ifdef WIN32
        Sleep(1000);
#else
Laurent Montel's avatar
Laurent Montel committed
221
        sleep(1);
Laurent Montel's avatar
Laurent Montel committed
222 223
#endif

Laurent Montel's avatar
Laurent Montel committed
224
        ++i;
Allen Winter's avatar
Allen Winter committed
225
    }
Laurent Montel's avatar
Laurent Montel committed
226
    // This should only happen if the distribution is broken
Laurent Montel's avatar
Laurent Montel committed
227
    qFatal("KSycoca unavailable. Kontact will be unable to find plugins.");
228 229
}

230 231
void MainWindow::initObject()
{
Laurent Montel's avatar
Laurent Montel committed
232
    if (!KSycoca::isAvailable()) {
Laurent Montel's avatar
Laurent Montel committed
233 234 235
        waitForKSycoca();
    }
    KService::List offers = KServiceTypeTrader::self()->query(
236 237
                                QStringLiteral("Kontact/Plugin"),
                                QStringLiteral("[X-KDE-KontactPluginVersion] == %1").arg(KONTACT_PLUGIN_VERSION));
Laurent Montel's avatar
Laurent Montel committed
238
    mPluginInfos = KPluginInfo::fromServices(
Laurent Montel's avatar
Laurent Montel committed
239
                       offers, KConfigGroup(Prefs::self()->config(), "Plugins"));
240

Laurent Montel's avatar
Laurent Montel committed
241 242
    const KPluginInfo::List::Iterator end(mPluginInfos.end());
    for (KPluginInfo::List::Iterator it = mPluginInfos.begin(); it != end; ++it) {
Laurent Montel's avatar
Laurent Montel committed
243 244 245 246
        it->load();
    }

    // prepare the part manager
Laurent Montel's avatar
Laurent Montel committed
247
    mPartManager = new KParts::PartManager(this);
Laurent Montel's avatar
Laurent Montel committed
248
    connect(mPartManager, &KParts::PartManager::activePartChanged, this, &MainWindow::slotActivePartChanged);
Laurent Montel's avatar
Laurent Montel committed
249 250 251

    loadPlugins();

Laurent Montel's avatar
Laurent Montel committed
252
    if (mSidePane) {
Laurent Montel's avatar
Laurent Montel committed
253 254
        mSidePane->updatePlugins();
    }
Laurent Montel's avatar
Laurent Montel committed
255
    KSettings::Dispatcher::registerComponent(QStringLiteral("kontact"), this, "updateConfig");
256

Laurent Montel's avatar
Laurent Montel committed
257
    loadSettings();
Daniel Molkentin's avatar
Daniel Molkentin committed
258

Laurent Montel's avatar
Laurent Montel committed
259
    statusBar()->show();
260

Laurent Montel's avatar
Laurent Montel committed
261
    // done initializing
Laurent Montel's avatar
Laurent Montel committed
262
    slotShowStatusMsg(QString());
263

Laurent Montel's avatar
Laurent Montel committed
264
    connect(KPIM::BroadcastStatus::instance(), SIGNAL(statusMsg(QString)), this, SLOT(slotShowStatusMsg(QString)));
Daniel Molkentin's avatar
Daniel Molkentin committed
265

Laurent Montel's avatar
Laurent Montel committed
266 267 268
    // launch commandline specified module if any
    activateInitialPluginModule();

Laurent Montel's avatar
Laurent Montel committed
269
    if (Prefs::lastVersionSeen() == KAboutData::applicationData().version()) {
Laurent Montel's avatar
Laurent Montel committed
270
        selectPlugin(mCurrentPlugin);
Laurent Montel's avatar
Laurent Montel committed
271 272
    }

273
    paintAboutScreen(QStringLiteral("introduction_kontact.html"), introductionData());
Laurent Montel's avatar
Laurent Montel committed
274
    Prefs::setLastVersionSeen(KAboutData::applicationData().version());
275 276
}

Daniel Molkentin's avatar
Daniel Molkentin committed
277
MainWindow::~MainWindow()
278
{
Laurent Montel's avatar
Laurent Montel committed
279
    if (mCurrentPlugin) {
Laurent Montel's avatar
Laurent Montel committed
280
        KConfigGroup grp(
Laurent Montel's avatar
Laurent Montel committed
281
            KSharedConfig::openConfig()->group(
282
                QStringLiteral("MainWindow%1").arg(mCurrentPlugin->identifier())));
Laurent Montel's avatar
Laurent Montel committed
283
        saveMainWindowSettings(grp);
Laurent Montel's avatar
Laurent Montel committed
284
    }
285

Laurent Montel's avatar
Laurent Montel committed
286 287
    createGUI(nullptr);
    ServiceStarter::setPluginList(nullptr);
Laurent Montel's avatar
Laurent Montel committed
288
    saveSettings();
289

Laurent Montel's avatar
Laurent Montel committed
290
    Prefs::self()->save();
291

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

Laurent Montel's avatar
Laurent Montel committed
295
    const PluginList::ConstIterator end = mPlugins.constEnd();
Laurent Montel's avatar
Laurent Montel committed
296
    for (PluginList::ConstIterator it = mPlugins.constBegin(); it != end; ++it) {
Laurent Montel's avatar
Laurent Montel committed
297 298
        delete *it;
    }
299 300
}

301
// Called by main().
Laurent Montel's avatar
Laurent Montel committed
302
void MainWindow::setInitialActivePluginModule(const QString &module)
303
{
Laurent Montel's avatar
Laurent Montel committed
304 305 306 307
    if (mInitialActiveModule != module) {
        mInitialActiveModule = module;
        activateInitialPluginModule();
    }
308 309
}

Laurent Montel's avatar
Laurent Montel committed
310
bool MainWindow::pluginActionWeightLessThan(const QAction *left, const QAction *right)
311
{
Laurent Montel's avatar
Laurent Montel committed
312 313 314
    // 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
315 316
    return !pluginWeightLessThan(left->data().value<KontactInterface::Plugin *>(),
                                 right->data().value<KontactInterface::Plugin *>());
317 318
}

Laurent Montel's avatar
Laurent Montel committed
319 320
bool MainWindow::pluginWeightLessThan(const KontactInterface::Plugin *left,
                                      const KontactInterface::Plugin *right)
321
{
Laurent Montel's avatar
Laurent Montel committed
322
    return left->weight() < right->weight();
323 324
}

325
void MainWindow::activateInitialPluginModule()
326
{
Laurent Montel's avatar
Laurent Montel committed
327
    if (!mInitialActiveModule.isEmpty() && !mPlugins.isEmpty()) {
Laurent Montel's avatar
Laurent Montel committed
328
        const PluginList::ConstIterator end = mPlugins.constEnd();
Laurent Montel's avatar
Laurent Montel committed
329 330 331 332
        for (PluginList::ConstIterator it = mPlugins.constBegin(); it != end; ++it) {
            if (!(*it)->identifier().isEmpty() &&
                    (*it)->identifier().contains(mInitialActiveModule)) {
                selectPlugin(*it);
Laurent Montel's avatar
Laurent Montel committed
333 334 335
                return;
            }
        }
Allen Winter's avatar
Allen Winter committed
336
    }
337
}
338

339 340
void MainWindow::initWidgets()
{
Laurent Montel's avatar
Laurent Montel committed
341
    QWidget *mTopWidget = new QWidget(this);
Laurent Montel's avatar
Laurent Montel committed
342 343 344
    QVBoxLayout *layout = new QVBoxLayout;
    layout->setMargin(0);
    layout->setSpacing(0);
Laurent Montel's avatar
Laurent Montel committed
345 346
    mTopWidget->setLayout(layout);
    setCentralWidget(mTopWidget);
Laurent Montel's avatar
Laurent Montel committed
347

Laurent Montel's avatar
Laurent Montel committed
348
    mSplitter = new QSplitter(mTopWidget);
349
    connect(mSplitter, &QSplitter::splitterMoved, this, &MainWindow::slotSplitterMoved);
Laurent Montel's avatar
Laurent Montel committed
350 351
    layout->addWidget(mSplitter);
    mSidePane = new IconSidePane(this, mSplitter);
352

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

Laurent Montel's avatar
Laurent Montel committed
355 356
    mPartsStack = new QStackedWidget(mSplitter);
    mPartsStack->layout()->setSpacing(0);
357

Laurent Montel's avatar
Laurent Montel committed
358
    initAboutScreen();
Tobias Koenig's avatar
Tobias Koenig committed
359

360
    paintAboutScreen(QStringLiteral("loading_kontact.html"), QVariantHash());
361

Laurent Montel's avatar
Laurent Montel committed
362
    KPIM::ProgressStatusBarWidget *progressStatusBarWidget = new KPIM::ProgressStatusBarWidget(statusBar(), this);
363

Laurent Montel's avatar
Laurent Montel committed
364
    mStatusMsgLabel =
Laurent Montel's avatar
Laurent Montel committed
365 366 367
        new KSqueezedTextLabel(i18nc("@info:status", " Initializing..."), statusBar());
    mStatusMsgLabel->setTextElideMode(Qt::ElideRight);
    mStatusMsgLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
368

Laurent Montel's avatar
Laurent Montel committed
369 370
    statusBar()->addWidget(mStatusMsgLabel, 10);
    statusBar()->addPermanentWidget(progressStatusBarWidget->littleProgress(), 0);
371

Laurent Montel's avatar
Laurent Montel committed
372
    mSplitter->setCollapsible(1, false);
373 374
}

375
void MainWindow::paintAboutScreen(const QString &templateName, const QVariantHash &data)
376
{
377 378
    GrantleeTheme::ThemeManager manager(QStringLiteral("splashPage"),
                                        QStringLiteral("splash.theme"),
Laurent Montel's avatar
Laurent Montel committed
379
                                        nullptr,
380 381 382 383
                                        QStringLiteral("messageviewer/about/"));
    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
384
    } else {
Laurent Montel's avatar
Laurent Montel committed
385
        mIntroPart->setHtml(theme.render(templateName, data, QByteArrayLiteral("kontact")),
386
                            QUrl::fromLocalFile(theme.absolutePath() + QLatin1Char('/')));
Laurent Montel's avatar
Laurent Montel committed
387 388
    }

389
}
390

391 392
void MainWindow::initAboutScreen()
{
Laurent Montel's avatar
Laurent Montel committed
393
    QWidget *introbox = new QWidget(mPartsStack);
Laurent Montel's avatar
Laurent Montel committed
394 395
    QHBoxLayout *introboxHBoxLayout = new QHBoxLayout(introbox);
    introboxHBoxLayout->setMargin(0);
Laurent Montel's avatar
Laurent Montel committed
396 397
    mPartsStack->addWidget(introbox);
    mPartsStack->setCurrentWidget(introbox);
398 399 400
    mIntroPart = new IntroductionWebEngineView(introbox);
    connect(mIntroPart, &IntroductionWebEngineView::openUrl, this, &MainWindow::slotOpenUrl, Qt::QueuedConnection);
    introboxHBoxLayout->addWidget(mIntroPart);
401 402
}

Daniel Molkentin's avatar
Daniel Molkentin committed
403
void MainWindow::setupActions()
404
{
Laurent Montel's avatar
Laurent Montel committed
405
    actionCollection()->addAction(KStandardAction::Quit, this, SLOT(slotQuit()));
Laurent Montel's avatar
Laurent Montel committed
406 407

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

Laurent Montel's avatar
Laurent Montel committed
413
    QAction *action =
Laurent Montel's avatar
Laurent Montel committed
414
        new QAction(QIcon::fromTheme(QStringLiteral("configure")),
Laurent Montel's avatar
Laurent Montel committed
415
                    i18nc("@action:inmenu", "Configure Kontact..."), this);
Laurent Montel's avatar
Laurent Montel committed
416
    setHelpText(action, i18nc("@info:status", "Configure Kontact"));
Laurent Montel's avatar
Laurent Montel committed
417
    action->setWhatsThis(
Laurent Montel's avatar
Laurent Montel committed
418 419
        i18nc("@info:whatsthis",
              "You will be presented with a dialog where you can configure Kontact."));
Laurent Montel's avatar
Laurent Montel committed
420
    actionCollection()->addAction(QStringLiteral("settings_configure_kontact"), action);
Laurent Montel's avatar
Laurent Montel committed
421
    connect(action, &QAction::triggered, this, &MainWindow::slotPreferences);
Laurent Montel's avatar
Laurent Montel committed
422 423

    action =
Laurent Montel's avatar
Laurent Montel committed
424
        new QAction(QIcon::fromTheme(QStringLiteral("kontact")),
Laurent Montel's avatar
Laurent Montel committed
425
                    i18nc("@action:inmenu", "&Kontact Introduction"), this);
Laurent Montel's avatar
Laurent Montel committed
426
    setHelpText(action, i18nc("@info:status", "Show the Kontact Introduction page"));
Laurent Montel's avatar
Laurent Montel committed
427
    action->setWhatsThis(
Laurent Montel's avatar
Laurent Montel committed
428 429
        i18nc("@info:whatsthis",
              "Choose this option to see the Kontact Introduction page."));
Laurent Montel's avatar
Laurent Montel committed
430
    actionCollection()->addAction(QStringLiteral("help_introduction"), action);
Laurent Montel's avatar
Laurent Montel committed
431
    connect(action, &QAction::triggered, this, &MainWindow::slotShowIntroduction);
Laurent Montel's avatar
Laurent Montel committed
432

433 434 435 436 437 438 439
    mShowHideAction = new QAction(QIcon::fromTheme(QStringLiteral("zoom-fit-width")),
                                  i18nc("@action:inmenu", "Hide/Show the component sidebar"), this);
    setHelpText(mShowHideAction, i18nc("@info:status", "Hide/Show the component sidebar"));
    mShowHideAction->setWhatsThis(
        i18nc("@info:whatsthis",
              "Allows you to show or hide the component sidebar as desired."));
    actionCollection()->addAction(QStringLiteral("hide_show_sidebar"), mShowHideAction);
440
    actionCollection()->setDefaultShortcut(mShowHideAction, QKeySequence(Qt::Key_F9));
441
    connect(mShowHideAction, &QAction::triggered, this, &MainWindow::slotShowHideSideBar);
442 443
}

Laurent Montel's avatar
Laurent Montel committed
444
bool MainWindow::isPluginLoaded(const KPluginInfo &info)
445
{
Laurent Montel's avatar
Laurent Montel committed
446
    return (pluginFromInfo(info) != nullptr);
447 448
}

Laurent Montel's avatar
Laurent Montel committed
449
KontactInterface::Plugin *MainWindow::pluginFromInfo(const KPluginInfo &info)
450
{
Laurent Montel's avatar
Laurent Montel committed
451
    const PluginList::ConstIterator end = mPlugins.constEnd();
Laurent Montel's avatar
Laurent Montel committed
452 453
    for (PluginList::ConstIterator it = mPlugins.constBegin(); it != end; ++it) {
        if ((*it)->identifier() == info.pluginName()) {
Laurent Montel's avatar
Laurent Montel committed
454 455
            return *it;
        }
Allen Winter's avatar
Allen Winter committed
456
    }
Laurent Montel's avatar
Laurent Montel committed
457
    return nullptr;
458 459
}

Daniel Molkentin's avatar
Daniel Molkentin committed
460
void MainWindow::loadPlugins()
461
{
Laurent Montel's avatar
Laurent Montel committed
462 463 464 465
    QList<KontactInterface::Plugin *> plugins;

    int i;
    KPluginInfo::List::ConstIterator it;
Laurent Montel's avatar
Laurent Montel committed
466
    const KPluginInfo::List::ConstIterator end(mPluginInfos.constEnd());
Laurent Montel's avatar
Laurent Montel committed
467 468
    for (it = mPluginInfos.constBegin(); it != end; ++it) {
        if (!it->isPluginEnabled()) {
Laurent Montel's avatar
Laurent Montel committed
469 470
            continue;
        }
471

Laurent Montel's avatar
Laurent Montel committed
472
        KontactInterface::Plugin *plugin = nullptr;
Laurent Montel's avatar
Laurent Montel committed
473 474 475
        if (isPluginLoaded(*it)) {
            plugin = pluginFromInfo(*it);
            if (plugin) {
Laurent Montel's avatar
Laurent Montel committed
476 477 478 479
                plugin->configUpdated();
            }
            continue;
        }
480

Laurent Montel's avatar
Laurent Montel committed
481
        qCDebug(KONTACT_LOG) << "Loading Plugin:" << it->name();
Laurent Montel's avatar
Laurent Montel committed
482 483
        QString error;
        plugin =
Laurent Montel's avatar
Laurent Montel committed
484
            it->service()->createInstance<KontactInterface::Plugin>(this, QVariantList(), &error);
485

Laurent Montel's avatar
Laurent Montel committed
486
        if (!plugin) {
Laurent Montel's avatar
Laurent Montel committed
487
            qCDebug(KONTACT_LOG) << "Unable to create plugin for" << it->name() << error;
Laurent Montel's avatar
Laurent Montel committed
488 489
            continue;
        }
490

Laurent Montel's avatar
Laurent Montel committed
491 492 493
        plugin->setIdentifier(it->pluginName());
        plugin->setTitle(it->name());
        plugin->setIcon(it->icon());
494

Laurent Montel's avatar
Laurent Montel committed
495 496 497 498
        QVariant libNameProp = it->property(QStringLiteral("X-KDE-KontactPartLibraryName"));
        QVariant exeNameProp = it->property(QStringLiteral("X-KDE-KontactPartExecutableName"));
        QVariant loadOnStart = it->property(QStringLiteral("X-KDE-KontactPartLoadOnStart"));
        QVariant hasPartProp = it->property(QStringLiteral("X-KDE-KontactPluginHasPart"));
499

Laurent Montel's avatar
Laurent Montel committed
500 501
        if (!loadOnStart.isNull() && loadOnStart.toBool()) {
            mDelayedPreload.append(plugin);
Laurent Montel's avatar
Laurent Montel committed
502
        }
503

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

Laurent Montel's avatar
Laurent Montel committed
506 507 508 509
        plugin->setPartLibraryName(libNameProp.toString().toUtf8());
        plugin->setExecutableName(exeNameProp.toString());
        if (hasPartProp.isValid()) {
            plugin->setShowInSideBar(hasPartProp.toBool());
Laurent Montel's avatar
Laurent Montel committed
510 511
        }

Laurent Montel's avatar
Laurent Montel committed
512 513 514
        for (i = 0; i < plugins.count(); ++i) {
            KontactInterface::Plugin *p = plugins.at(i);
            if (plugin->weight() < p->weight()) {
Laurent Montel's avatar
Laurent Montel committed
515 516 517
                break;
            }
        }
Laurent Montel's avatar
Laurent Montel committed
518
        plugins.insert(i, plugin);
519

520
    }
521

Laurent Montel's avatar
Laurent Montel committed
522 523 524
    const int numberOfPlugins(plugins.count());
    for (i = 0; i < numberOfPlugins; ++i) {
        KontactInterface::Plugin *plugin = plugins.at(i);
525

Laurent Montel's avatar
Laurent Montel committed
526
        const QList<QAction *> actionList = plugin->newActions();
Laurent Montel's avatar
Laurent Montel committed
527
        const QList<QAction *>::const_iterator end(actionList.end());
528

Laurent Montel's avatar
Laurent Montel committed
529
        for (QList<QAction *>::const_iterator  listIt = actionList.begin(); listIt != end; ++listIt) {
Laurent Montel's avatar
Laurent Montel committed
530
            qCDebug(KONTACT_LOG) << QStringLiteral("Plugging New actions") << (*listIt)->objectName();
Laurent Montel's avatar
Laurent Montel committed
531
            mNewActions->addAction((*listIt));
Laurent Montel's avatar
Laurent Montel committed
532
        }
533

Laurent Montel's avatar
Laurent Montel committed
534
        addPlugin(plugin);
535
    }
Tobias Koenig's avatar
Tobias Koenig committed
536

Laurent Montel's avatar
Laurent Montel committed
537 538
    const bool state = (!mPlugins.isEmpty());
    mNewActions->setEnabled(state);
539 540
}

541 542
void MainWindow::unloadPlugins()
{
Laurent Montel's avatar
Laurent Montel committed
543
    const KPluginInfo::List::ConstIterator end = mPluginInfos.constEnd();
Laurent Montel's avatar
Laurent Montel committed
544
    for (KPluginInfo::List::ConstIterator it = mPluginInfos.constBegin(); it != end; ++it) {
Laurent Montel's avatar
Laurent Montel committed
545 546
        if (!it->isPluginEnabled()) {
            removePlugin(*it);
Laurent Montel's avatar
Laurent Montel committed
547
        }
Allen Winter's avatar
Allen Winter committed
548
    }
549
}
550

551 552
void MainWindow::updateShortcuts()
{
Laurent Montel's avatar
Laurent Montel committed
553
    const ActionPluginList::ConstIterator end = mActionPlugins.constEnd();
Laurent Montel's avatar
Laurent Montel committed
554
    int i = 0;
Laurent Montel's avatar
Laurent Montel committed
555
    for (ActionPluginList::ConstIterator it = mActionPlugins.constBegin(); it != end; ++it) {
Laurent Montel's avatar
Laurent Montel committed
556
        QAction *action = static_cast<QAction *>(*it);
557
        const QString shortcut = QStringLiteral("Ctrl+%1").arg(mActionPlugins.count() - i);
Laurent Montel's avatar
Laurent Montel committed
558
        actionCollection()->setDefaultShortcut(action, QKeySequence(shortcut));
Laurent Montel's avatar
Laurent Montel committed
559 560
        ++i;
    }
Laurent Montel's avatar
Laurent Montel committed
561
    factory()->plugActionList(this, QStringLiteral("navigator_actionlist"), mActionPlugins);
562 563
}

Laurent Montel's avatar
Laurent Montel committed
564
bool MainWindow::removePlugin(const KPluginInfo &info)
565
{
Laurent Montel's avatar
Laurent Montel committed
566
    const PluginList::Iterator end = mPlugins.end();
Laurent Montel's avatar
Laurent Montel committed
567
    for (PluginList::Iterator it = mPlugins.begin(); it != end; ++it) {
Laurent Montel's avatar
Laurent Montel committed
568
        KontactInterface::Plugin *plugin = *it;
Laurent Montel's avatar
Laurent Montel committed
569 570 571 572 573
        if ((*it)->identifier() == info.pluginName()) {
            QList<QAction *> actionList = plugin->newActions();
            QList<QAction *>::const_iterator listIt;
            QList<QAction *>::const_iterator listEnd(actionList.constEnd());
            for (listIt = actionList.constBegin(); listIt != listEnd; ++listIt) {
Laurent Montel's avatar
Laurent Montel committed
574
                qCDebug(KONTACT_LOG) << QStringLiteral("Unplugging New actions") << (*listIt)->objectName();
Laurent Montel's avatar
Laurent Montel committed
575
                mNewActions->removeAction(*listIt);
Laurent Montel's avatar
Laurent Montel committed
576 577
            }

Laurent Montel's avatar
Laurent Montel committed
578
            removeChildClient(plugin);
Laurent Montel's avatar
Laurent Montel committed
579

Laurent Montel's avatar
Laurent Montel committed
580
            if (mCurrentPlugin == plugin) {
Laurent Montel's avatar
Laurent Montel committed
581 582
                mCurrentPlugin = nullptr;
                createGUI(nullptr);
Laurent Montel's avatar
Laurent Montel committed
583 584 585
            }

            plugin->deleteLater(); // removes the part automatically
Laurent Montel's avatar
Laurent Montel committed
586 587
            mPlugins.erase(it);
            if (plugin->showInSideBar()) {
Laurent Montel's avatar
Laurent Montel committed
588
                QAction *q = mPluginAction[plugin]; // remove QAction, to free the shortcut for later use
Laurent Montel's avatar
Laurent Montel committed
589
                mActionPlugins.removeAll(q);
Laurent Montel's avatar
Laurent Montel committed
590 591 592 593
                mPluginAction.remove(plugin);
                delete q;
            }

Laurent Montel's avatar
Laurent Montel committed
594
            if (mCurrentPlugin == nullptr) {
Laurent Montel's avatar
Laurent Montel committed
595
                PluginList::Iterator it;
Laurent Montel's avatar
Laurent Montel committed
596
                const PluginList::Iterator pluginEnd(mPlugins.end());
Laurent Montel's avatar
Laurent Montel committed
597 598 599
                for (it = mPlugins.begin(); it != pluginEnd; ++it) {
                    if ((*it)->showInSideBar()) {
                        selectPlugin(*it);
Laurent Montel's avatar
Laurent Montel committed
600 601 602 603
                        return true;
                    }
                }
            }
604 605
            return true;
        }
606

Laurent Montel's avatar
Laurent Montel committed
607
    }
Tobias Koenig's avatar
Tobias Koenig committed
608

Laurent Montel's avatar
Laurent Montel committed
609
    return false;
610 611
}

Laurent Montel's avatar
Laurent Montel committed
612
void MainWindow::addPlugin(KontactInterface::Plugin *plugin)
613
{
Laurent Montel's avatar
Laurent Montel committed
614
    qCDebug(KONTACT_LOG);
Laurent Montel's avatar
Laurent Montel committed
615

Laurent Montel's avatar
Laurent Montel committed
616
    mPlugins.append(plugin);
Laurent Montel's avatar
Laurent Montel committed
617

Laurent Montel's avatar
Laurent Montel committed
618 619
    if (plugin->showInSideBar()) {
        QAction *action = new QAction(QIcon::fromTheme(plugin->icon()), plugin->title(), this);
Laurent Montel's avatar
Laurent Montel committed
620 621
        //action->setHelpText(
        //            i18nc( "@info:status", "Plugin %1", plugin->title() ) );
Laurent Montel's avatar
Laurent Montel committed
622
        action->setWhatsThis(
Laurent Montel's avatar
Laurent Montel committed
623 624 625 626
            i18nc("@info:whatsthis",
                  "Switch to plugin %1", plugin->title()));
        action->setCheckable(true);
        action->setData(QVariant::fromValue(plugin));     // on the slot we can decode
Laurent Montel's avatar
Laurent Montel committed
627
        // which action was triggered
Laurent Montel's avatar
Laurent Montel committed
628
        connect(action, &QAction::triggered, this, &MainWindow::slotActionTriggered);
Laurent Montel's avatar
Laurent Montel committed
629 630 631
        actionCollection()->addAction(plugin->title(), action);
        mActionPlugins.append(action);
        mPluginAction.insert(plugin, action);
Laurent Montel's avatar
Laurent Montel committed
632
    }
633

Laurent Montel's avatar
Laurent Montel committed
634
    // merge the plugins GUI into the main window
Laurent Montel's avatar
Laurent Montel committed
635
    insertChildClient(plugin);
Laurent Montel's avatar
Laurent Montel committed
636 637 638

    // 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
Laurent Montel's avatar
Laurent Montel committed
639 640
    std::sort(mActionPlugins.begin(), mActionPlugins.end(), pluginActionWeightLessThan);
    std::sort(mPlugins.begin(), mPlugins.end(), pluginWeightLessThan);
Laurent Montel's avatar
Laurent Montel committed
641
    int i = 0;
Laurent Montel's avatar
Laurent Montel committed
642
    for (QAction *qaction : qAsConst(mActionPlugins)) {
Laurent Montel's avatar
Laurent Montel committed
643
        QAction *action = static_cast<QAction *>(qaction);
644
        QString shortcut = QStringLiteral("Ctrl+%1").arg(mActionPlugins.count() - i);
Laurent Montel's avatar
Laurent Montel committed
645
        actionCollection()->setDefaultShortcut(action, QKeySequence(shortcut));
Laurent Montel's avatar
Laurent Montel committed
646 647
        ++i;
    }
648 649
}

Laurent Montel's avatar
Laurent Montel committed
650
void MainWindow::partLoaded(KontactInterface::Plugin *plugin, KParts::ReadOnlyPart *part)
651
{
Laurent Montel's avatar
Laurent Montel committed
652
    Q_UNUSED(plugin);
Allen Winter's avatar
Allen Winter committed
653

Laurent Montel's avatar
Laurent Montel committed
654
    // See if we have this part already (e.g. due to two plugins sharing it)
Laurent Montel's avatar
Laurent Montel committed
655
    if (mPartsStack->indexOf(part->widget()) != -1) {
Laurent Montel's avatar
Laurent Montel committed
656 657
        return;
    }
658

Laurent Montel's avatar
Laurent Montel committed
659
    mPartsStack->addWidget(part->widget());
660

Laurent Montel's avatar
Laurent Montel committed
661
    mPartManager->addPart(part, false);
Laurent Montel's avatar
Laurent Montel committed
662 663
    // Workaround for KParts misbehavior: addPart calls show!
    part->widget()->hide();
664 665
}

Laurent Montel's avatar
Laurent Montel committed
666
void MainWindow::slotActivePartChanged(KParts::Part *part)
667
{
Laurent Montel's avatar
Laurent Montel committed
668
    if (!part) {
Laurent Montel's avatar
Laurent Montel committed
669
        createGUI(nullptr);
Laurent Montel's avatar
Laurent Montel committed
670 671
        return;
    }
672

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

Laurent Montel's avatar
Laurent Montel committed
676
    statusBar()->clearMessage();
677 678
}

679 680
void MainWindow::slotNewClicked()
{
Laurent Montel's avatar
Laurent Montel committed
681
    if (!mCurrentPlugin->newActions().isEmpty()) {
Laurent Montel's avatar
Laurent Montel committed
682
        mCurrentPlugin->newActions().at(0)->trigger();
Laurent Montel's avatar
Laurent Montel committed
683 684
    } else {
        PluginList::Iterator it;
Laurent Montel's avatar
Laurent Montel committed
685
        const PluginList::Iterator end(mPlugins.end());
Laurent Montel's avatar
Laurent Montel committed
686 687
        for (it = mPlugins.begin(); it != end; ++it) {
            if (!(*it)->newActions().isEmpty()) {
Laurent Montel's avatar
Laurent Montel committed
688 689 690 691
                (*it)->newActions().first()->trigger();
                return;
            }
        }
692
    }
693 694
}

Laurent Montel's avatar
Laurent Montel committed
695
KToolBar *MainWindow::findToolBar(const char *name)
696
{
Laurent Montel's avatar
Laurent Montel committed
697
    // like KMainWindow::toolBar, but which doesn't create the toolbar if not found
Laurent Montel's avatar
Laurent Montel committed
698
    return findChild<KToolBar *>(QLatin1String(name));
699 700
}

Laurent Montel's avatar
Laurent Montel committed
701
void MainWindow::selectPlugin(KontactInterface::Plugin *plugin)
702
{
Laurent Montel's avatar
Laurent Montel committed
703
    if (!plugin) {
Laurent Montel's avatar
Laurent Montel committed
704 705
        return;
    }
706

Laurent Montel's avatar
Laurent Montel committed
707
    if (plugin->isRunningStandalone()) {
Laurent Montel's avatar
Laurent Montel committed
708
        statusBar()->showMessage(
Laurent Montel's avatar
Laurent Montel committed
709 710
            i18nc("@info:status",
                  "Application is running standalone. Foregrounding..."), 1000);
Laurent Montel's avatar
Laurent Montel committed
711 712 713
        plugin->bringToForeground();
        return;
    }
714

Laurent Montel's avatar
Laurent Montel committed
715
    QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
716

Laurent Montel's avatar
Laurent Montel committed
717
    if (mCurrentPlugin) {
718
        KConfigGroup grp = KSharedConfig::openConfig()->group(
719
                               QStringLiteral("MainWindow%1").arg(mCurrentPlugin->identifier()));
Laurent Montel's avatar
Laurent Montel committed
720
        saveMainWindowSettings(grp);
Laurent Montel's avatar
Laurent Montel committed
721
    }
722

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

Laurent Montel's avatar
Laurent Montel committed
725
    if (!part) {
Laurent Montel's avatar
Laurent Montel committed
726 727
        QApplication::restoreOverrideCursor();
        KMessageBox::error(
Laurent Montel's avatar
Laurent Montel committed
728 729 730 731 732
            this,
            i18nc("@info",
                  "Cannot load part for %1.",
                  plugin->title()) + QLatin1Char('\n') + lastErrorMessage());
        plugin->setDisabled(true);
Laurent Montel's avatar
Laurent Montel committed
733 734 735
        mSidePane->updatePlugins();
        return;
    }
736

Laurent Montel's avatar
Laurent Montel committed