Optimization
[kdepim.git] / kontact / src / mainwindow.cpp
blob4cdad1e497cdd0977239978a6e3a16a889a516b3
1 /*
2 This file is part of KDE Kontact.
4 Copyright (c) 2001 Matthias Hoelzer-Kluepfel <mhk@kde.org>
5 Copyright (c) 2002-2005 Daniel Molkentin <molkentin@kde.org>
6 Copyright (c) 2003-2005 Cornelius Schumacher <schumacher@kde.org>
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License along
19 with this program; if not, write to the Free Software Foundation, Inc.,
20 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 #include "mainwindow.h"
24 #include "aboutdialog.h"
25 #include "prefs.h"
26 #include "iconsidepane.h"
28 #include "webengine/introductionwebengineview.h"
29 #include "webengine/introductionwebenginepage.h"
31 #include "kontactconfiguredialog.h"
32 using namespace Kontact;
34 #include <unistd.h>
36 #include <Libkdepim/BroadcastStatus>
37 #include <Libkdepim/ProgressStatusBarWidget>
38 #include <Libkdepim/StatusbarProgressWidget>
40 #include <KontactInterface/Core>
42 #include <QStatusBar>
43 #include <KWindowConfig>
44 #include <KXMLGUIFactory>
45 #include <KActionCollection>
46 #include <KActionMenu>
47 #include <KConfigGroup>
48 #include <KDBusServiceStarter>
49 #include "kontact_debug.h"
50 #include <KEditToolBar>
51 #include <KHelpMenu>
52 #include <KMessageBox>
53 #include <KPluginInfo>
54 #include <KRun>
55 #include <KServiceTypeTrader>
56 #include <KShortcutsDialog>
57 #include <KSqueezedTextLabel>
58 #include <KStandardAction>
59 #include <KToolBar>
60 #include <KParts/PartManager>
61 #include <ksettings/Dispatcher>
62 #include <KSycoca>
63 #include <KLocalizedString>
64 #include <QDBusConnection>
65 #include <QSplitter>
66 #include <QStackedWidget>
67 #include <QTimer>
68 #include <QVBoxLayout>
69 #include <QShortcut>
70 #include <QIcon>
71 #include <KHelpClient>
72 #include <KSharedConfig>
73 #include <QStandardPaths>
74 #include <QFile>
75 #include <QHBoxLayout>
76 #include <QApplication>
77 #include <KAboutData>
78 #include <QFontDatabase>
80 #include <grantleetheme/grantleethememanager.h>
81 #include <grantleetheme/grantleetheme.h>
83 // Define the maximum time Kontact waits for KSycoca to become available.
84 static const int KSYCOCA_WAIT_TIMEOUT = 10;
86 // This class extends the normal KDBusServiceStarter.
88 // When a service start is requested, it asks all plugins
89 // to create their dbus interfaces in addition to the normal
90 // way of starting a service.
91 class ServiceStarter : public KDBusServiceStarter
93 public:
95 virtual int startServiceFor(const QString &serviceType,
96 const QString &constraint = QString(),
97 QString *error = Q_NULLPTR, QString *dbusService = Q_NULLPTR,
98 int flags = 0) Q_DECL_OVERRIDE;
100 // We need to keep track of the plugins which are loaded, so pass a pointer
101 // to the plugin list here. Be sure to reset it back to 0 with
102 // setPluginList() as soon as the list gets destroyed.
103 ServiceStarter(PluginList *pluginList)
105 mPlugins = pluginList;
108 static void setPluginList(PluginList *pluginList)
110 mPlugins = pluginList;
113 protected:
115 virtual ~ServiceStarter() {}
116 static PluginList *mPlugins;
119 PluginList *ServiceStarter::mPlugins = Q_NULLPTR;
121 int ServiceStarter::startServiceFor(const QString &serviceType,
122 const QString &constraint,
123 QString *error, QString *dbusService,
124 int flags)
126 if (mPlugins) {
127 PluginList::ConstIterator end = mPlugins->constEnd();
128 for (PluginList::ConstIterator it = mPlugins->constBegin(); it != end; ++it) {
129 if ((*it)->createDBUSInterface(serviceType)) {
130 qCDebug(KONTACT_LOG) << "found interface for" << serviceType;
131 if (dbusService) {
132 *dbusService = (*it)->registerClient();
134 return 0;
139 qCDebug(KONTACT_LOG) << "Didn't find dbus interface, falling back to external process";
140 return KDBusServiceStarter::startServiceFor(serviceType, constraint,
141 error, dbusService, flags);
144 MainWindow::MainWindow()
145 : KontactInterface::Core(), mSplitter(Q_NULLPTR), mCurrentPlugin(Q_NULLPTR), mAboutDialog(Q_NULLPTR),
146 mReallyClose(false)
148 // The ServiceStarter created here will be deleted by the KDbusServiceStarter
149 // base class, which is a global static.
150 new ServiceStarter(&mPlugins);
152 QDBusConnection::sessionBus().registerObject(
153 QStringLiteral("/KontactInterface"), this, QDBusConnection::ExportScriptableSlots);
155 // Set this to be the group leader for all subdialogs - this means
156 // modal subdialogs will only affect this dialog, not the other windows
157 setAttribute(Qt::WA_GroupLeader);
159 initGUI();
160 initObject();
162 mSidePane->setMaximumWidth(mSidePane->sizeHint().width());
163 mSidePane->setMinimumWidth(mSidePane->sizeHint().width());
165 factory()->plugActionList(this, QStringLiteral("navigator_actionlist"), mActionPlugins);
167 KConfigGroup grp(KSharedConfig::openConfig(), "MainWindow");
168 KWindowConfig::restoreWindowSize(windowHandle(), grp);
169 setAutoSaveSettings();
172 void MainWindow::initGUI()
174 initWidgets();
175 setupActions();
176 setHelpMenuEnabled(false);
177 KHelpMenu *helpMenu = new KHelpMenu(this, QString(), true/*, actionCollection() QT5*/);
178 connect(helpMenu, &KHelpMenu::showAboutApplication, this, &MainWindow::showAboutDialog);
180 KStandardAction::keyBindings(this, SLOT(configureShortcuts()), actionCollection());
181 KStandardAction::configureToolbars(this, SLOT(configureToolbars()), actionCollection());
182 setXMLFile(QStringLiteral("kontactui.rc"));
184 setStandardToolBarMenuEnabled(true);
186 createGUI(Q_NULLPTR);
188 KToolBar *navigatorToolBar = findToolBar("navigatorToolBar");
189 if (navigatorToolBar) {
190 if (layoutDirection() == Qt::LeftToRight) {
191 navigatorToolBar->setLayoutDirection(Qt::RightToLeft);
192 } else {
193 navigatorToolBar->setLayoutDirection(Qt::LeftToRight);
195 Q_ASSERT(navigatorToolBar->sizeHint().isValid());
196 navigatorToolBar->setMinimumWidth(navigatorToolBar->sizeHint().width());
197 } else {
198 qCritical() << "Unable to find navigatorToolBar, probably kontactui.rc is missing";
202 void MainWindow::waitForKSycoca()
204 int i = 0;
205 while (i < KSYCOCA_WAIT_TIMEOUT) {
206 if (KSycoca::isAvailable()) {
207 return;
209 // When KSycoca is not availabe that usually means Kontact
210 // was started before kded is done with it's first run
211 // we want to block Kontact execution to
212 // give Kded time to initialize and create the
213 // System Configuration database necessary for further
214 // Kontact startup
215 qCDebug(KONTACT_LOG) << "Waiting for KSycoca";
216 sleep(1);
217 ++i;
219 // This should only happen if the distribution is broken
220 qFatal("KSycoca unavailable. Kontact will be unable to find plugins.");
223 void MainWindow::initObject()
225 if (!KSycoca::isAvailable()) {
226 waitForKSycoca();
228 KService::List offers = KServiceTypeTrader::self()->query(
229 QStringLiteral("Kontact/Plugin"),
230 QStringLiteral("[X-KDE-KontactPluginVersion] == %1").arg(KONTACT_PLUGIN_VERSION));
231 mPluginInfos = KPluginInfo::fromServices(
232 offers, KConfigGroup(Prefs::self()->config(), "Plugins"));
233 KPluginInfo::List::Iterator it;
234 KPluginInfo::List::Iterator end(mPluginInfos.end());
236 for (it = mPluginInfos.begin(); it != end; ++it) {
237 it->load();
240 // prepare the part manager
241 mPartManager = new KParts::PartManager(this);
242 connect(mPartManager, &KParts::PartManager::activePartChanged, this, &MainWindow::slotActivePartChanged);
244 loadPlugins();
246 if (mSidePane) {
247 mSidePane->updatePlugins();
249 KSettings::Dispatcher::registerComponent(QStringLiteral("kontact"), this, "updateConfig");
251 loadSettings();
253 statusBar()->show();
255 // done initializing
256 slotShowStatusMsg(QString());
258 connect(KPIM::BroadcastStatus::instance(), SIGNAL(statusMsg(QString)), this, SLOT(slotShowStatusMsg(QString)));
260 // launch commandline specified module if any
261 activateInitialPluginModule();
263 if (Prefs::lastVersionSeen() == KAboutData::applicationData().version()) {
264 selectPlugin(mCurrentPlugin);
267 paintAboutScreen(QStringLiteral("introduction_kontact.html"), introductionData());
268 Prefs::setLastVersionSeen(KAboutData::applicationData().version());
271 MainWindow::~MainWindow()
273 if (mCurrentPlugin) {
274 KConfigGroup grp(
275 KSharedConfig::openConfig()->group(
276 QStringLiteral("MainWindow%1").arg(mCurrentPlugin->identifier())));
277 saveMainWindowSettings(grp);
280 createGUI(Q_NULLPTR);
281 ServiceStarter::setPluginList(Q_NULLPTR);
282 saveSettings();
284 //QList<KParts::Part*> parts = mPartManager->parts();
286 // Q_FOREACH( KParts::Part *p, parts ) {
287 // delete p;
288 // p = 0;
289 // }
291 Prefs::self()->save();
293 // During deletion of plugins, we should not access the plugin list (bug #182176)
294 delete mSidePane;
296 PluginList::ConstIterator end = mPlugins.constEnd();
297 for (PluginList::ConstIterator it = mPlugins.constBegin(); it != end; ++it) {
298 delete *it;
302 // Called by main().
303 void MainWindow::setInitialActivePluginModule(const QString &module)
305 if (mInitialActiveModule != module) {
306 mInitialActiveModule = module;
307 activateInitialPluginModule();
311 bool MainWindow::pluginActionWeightLessThan(const QAction *left, const QAction *right)
313 // Since this lessThan method is used only for the toolbar (which is on
314 // the inverse layout direction than the rest of the system), we add the
315 // elements on the exactly inverse order. (ereslibre)
316 return !pluginWeightLessThan(left->data().value<KontactInterface::Plugin *>(),
317 right->data().value<KontactInterface::Plugin *>());
320 bool MainWindow::pluginWeightLessThan(const KontactInterface::Plugin *left,
321 const KontactInterface::Plugin *right)
323 return left->weight() < right->weight();
326 void MainWindow::activateInitialPluginModule()
328 if (!mInitialActiveModule.isEmpty() && !mPlugins.isEmpty()) {
329 PluginList::ConstIterator end = mPlugins.constEnd();
330 for (PluginList::ConstIterator it = mPlugins.constBegin(); it != end; ++it) {
331 if (!(*it)->identifier().isEmpty() &&
332 (*it)->identifier().contains(mInitialActiveModule)) {
333 selectPlugin(*it);
334 return;
340 void MainWindow::initWidgets()
342 QWidget *mTopWidget = new QWidget(this);
343 QVBoxLayout *layout = new QVBoxLayout;
344 layout->setMargin(0);
345 layout->setSpacing(0);
346 mTopWidget->setLayout(layout);
347 setCentralWidget(mTopWidget);
349 mSplitter = new QSplitter(mTopWidget);
350 layout->addWidget(mSplitter);
351 mSidePane = new IconSidePane(this, mSplitter);
353 // don't occupy screen estate on load
355 QList<int> sizes;
356 sizes << 0;
357 mSplitter->setSizes(sizes);
359 connect(mSidePane, SIGNAL(pluginSelected(KontactInterface::Plugin*)), SLOT(selectPlugin(KontactInterface::Plugin*)));
361 mPartsStack = new QStackedWidget(mSplitter);
362 mPartsStack->layout()->setSpacing(0);
364 initAboutScreen();
366 paintAboutScreen(QStringLiteral("loading_kontact.html"), QVariantHash());
368 KPIM::ProgressStatusBarWidget *progressStatusBarWidget = new KPIM::ProgressStatusBarWidget(statusBar(), this);
370 mStatusMsgLabel =
371 new KSqueezedTextLabel(i18nc("@info:status", " Initializing..."), statusBar());
372 mStatusMsgLabel->setTextElideMode(Qt::ElideRight);
373 mStatusMsgLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
375 statusBar()->addWidget(mStatusMsgLabel, 10);
376 statusBar()->addPermanentWidget(progressStatusBarWidget->littleProgress(), 0);
378 mSplitter->setCollapsible(1, false);
381 void MainWindow::paintAboutScreen(const QString &templateName, const QVariantHash &data)
383 GrantleeTheme::ThemeManager manager(QStringLiteral("splashPage"),
384 QStringLiteral("splash.theme"),
385 Q_NULLPTR,
386 QStringLiteral("messageviewer/about/"));
387 GrantleeTheme::Theme theme = manager.theme(QStringLiteral("default"));
388 if (!theme.isValid()) {
389 qCDebug(KONTACT_LOG) << "Theme error: failed to find splash theme";
390 } else {
391 mIntroPart->setHtml(theme.render(templateName, data),
392 QUrl::fromLocalFile(theme.absolutePath() + QLatin1Char('/')));
397 void MainWindow::initAboutScreen()
399 QWidget *introbox = new QWidget(mPartsStack);
400 QHBoxLayout *introboxHBoxLayout = new QHBoxLayout(introbox);
401 introboxHBoxLayout->setMargin(0);
402 mPartsStack->addWidget(introbox);
403 mPartsStack->setCurrentWidget(introbox);
404 mIntroPart = new IntroductionWebEngineView(introbox);
405 connect(mIntroPart, &IntroductionWebEngineView::openUrl, this, &MainWindow::slotOpenUrl, Qt::QueuedConnection);
406 introboxHBoxLayout->addWidget(mIntroPart);
409 void MainWindow::setupActions()
411 actionCollection()->addAction(KStandardAction::Quit, this, SLOT(slotQuit()));
413 mNewActions = new KActionMenu(
414 i18nc("@title:menu create new pim items (message,calendar,to-do,etc.)", "New"), this);
415 actionCollection()->addAction(QStringLiteral("action_new"), mNewActions);
416 actionCollection()->setDefaultShortcuts(mNewActions, KStandardShortcut::openNew());
417 connect(mNewActions, &KActionMenu::triggered, this, &MainWindow::slotNewClicked);
419 QAction *action =
420 new QAction(QIcon::fromTheme(QStringLiteral("configure")),
421 i18nc("@action:inmenu", "Configure Kontact..."), this);
422 setHelpText(action, i18nc("@info:status", "Configure Kontact"));
423 action->setWhatsThis(
424 i18nc("@info:whatsthis",
425 "You will be presented with a dialog where you can configure Kontact."));
426 actionCollection()->addAction(QStringLiteral("settings_configure_kontact"), action);
427 connect(action, &QAction::triggered, this, &MainWindow::slotPreferences);
429 action =
430 new QAction(QIcon::fromTheme(QStringLiteral("kontact")),
431 i18nc("@action:inmenu", "&Kontact Introduction"), this);
432 setHelpText(action, i18nc("@info:status", "Show the Kontact Introduction page"));
433 action->setWhatsThis(
434 i18nc("@info:whatsthis",
435 "Choose this option to see the Kontact Introduction page."));
436 actionCollection()->addAction(QStringLiteral("help_introduction"), action);
437 connect(action, &QAction::triggered, this, &MainWindow::slotShowIntroduction);
439 //TODO 4.12: add description
440 QShortcut *shortcut = new QShortcut(QKeySequence(Qt::Key_F9), this);
441 connect(shortcut, &QShortcut::activated, this, &MainWindow::slotShowHideSideBar);
444 bool MainWindow::isPluginLoaded(const KPluginInfo &info)
446 return (pluginFromInfo(info) != Q_NULLPTR);
449 KontactInterface::Plugin *MainWindow::pluginFromInfo(const KPluginInfo &info)
451 PluginList::ConstIterator end = mPlugins.constEnd();
452 for (PluginList::ConstIterator it = mPlugins.constBegin(); it != end; ++it) {
453 if ((*it)->identifier() == info.pluginName()) {
454 return *it;
457 return Q_NULLPTR;
460 void MainWindow::loadPlugins()
462 QList<KontactInterface::Plugin *> plugins;
464 int i;
465 KPluginInfo::List::ConstIterator it;
466 KPluginInfo::List::ConstIterator end(mPluginInfos.constEnd());
467 for (it = mPluginInfos.constBegin(); it != end; ++it) {
468 if (!it->isPluginEnabled()) {
469 continue;
472 KontactInterface::Plugin *plugin = Q_NULLPTR;
473 if (isPluginLoaded(*it)) {
474 plugin = pluginFromInfo(*it);
475 if (plugin) {
476 plugin->configUpdated();
478 continue;
481 qCDebug(KONTACT_LOG) << "Loading Plugin:" << it->name();
482 QString error;
483 plugin =
484 it->service()->createInstance<KontactInterface::Plugin>(this, QVariantList(), &error);
486 if (!plugin) {
487 qCDebug(KONTACT_LOG) << "Unable to create plugin for" << it->name() << error;
488 continue;
491 plugin->setIdentifier(it->pluginName());
492 plugin->setTitle(it->name());
493 plugin->setIcon(it->icon());
495 QVariant libNameProp = it->property(QStringLiteral("X-KDE-KontactPartLibraryName"));
496 QVariant exeNameProp = it->property(QStringLiteral("X-KDE-KontactPartExecutableName"));
497 QVariant loadOnStart = it->property(QStringLiteral("X-KDE-KontactPartLoadOnStart"));
498 QVariant hasPartProp = it->property(QStringLiteral("X-KDE-KontactPluginHasPart"));
500 if (!loadOnStart.isNull() && loadOnStart.toBool()) {
501 mDelayedPreload.append(plugin);
504 qCDebug(KONTACT_LOG) << "LIBNAMEPART:" << libNameProp.toString();
506 plugin->setPartLibraryName(libNameProp.toString().toUtf8());
507 plugin->setExecutableName(exeNameProp.toString());
508 if (hasPartProp.isValid()) {
509 plugin->setShowInSideBar(hasPartProp.toBool());
512 for (i = 0; i < plugins.count(); ++i) {
513 KontactInterface::Plugin *p = plugins.at(i);
514 if (plugin->weight() < p->weight()) {
515 break;
518 plugins.insert(i, plugin);
522 const int numberOfPlugins(plugins.count());
523 for (i = 0; i < numberOfPlugins; ++i) {
524 KontactInterface::Plugin *plugin = plugins.at(i);
526 const QList<QAction *> actionList = plugin->newActions();
527 QList<QAction *>::const_iterator listIt;
528 QList<QAction *>::const_iterator end(actionList.end());
530 for (listIt = actionList.begin(); listIt != end; ++listIt) {
531 qCDebug(KONTACT_LOG) << QStringLiteral("Plugging New actions") << (*listIt)->objectName();
532 mNewActions->addAction((*listIt));
535 addPlugin(plugin);
538 const bool state = (!mPlugins.isEmpty());
539 mNewActions->setEnabled(state);
542 void MainWindow::unloadPlugins()
544 KPluginInfo::List::ConstIterator end = mPluginInfos.constEnd();
545 KPluginInfo::List::ConstIterator it;
546 for (it = mPluginInfos.constBegin(); it != end; ++it) {
547 if (!it->isPluginEnabled()) {
548 removePlugin(*it);
553 void MainWindow::updateShortcuts()
555 ActionPluginList::ConstIterator end = mActionPlugins.constEnd();
556 ActionPluginList::ConstIterator it;
557 int i = 0;
558 for (it = mActionPlugins.constBegin(); it != end; ++it) {
559 QAction *action = static_cast<QAction *>(*it);
560 const QString shortcut = QStringLiteral("Ctrl+%1").arg(mActionPlugins.count() - i);
561 actionCollection()->setDefaultShortcut(action, QKeySequence(shortcut));
562 ++i;
564 factory()->plugActionList(this, QStringLiteral("navigator_actionlist"), mActionPlugins);
567 bool MainWindow::removePlugin(const KPluginInfo &info)
569 PluginList::Iterator end = mPlugins.end();
570 for (PluginList::Iterator it = mPlugins.begin(); it != end; ++it) {
571 KontactInterface::Plugin *plugin = *it;
572 if ((*it)->identifier() == info.pluginName()) {
573 QList<QAction *> actionList = plugin->newActions();
574 QList<QAction *>::const_iterator listIt;
575 QList<QAction *>::const_iterator listEnd(actionList.constEnd());
576 for (listIt = actionList.constBegin(); listIt != listEnd; ++listIt) {
577 qCDebug(KONTACT_LOG) << QStringLiteral("Unplugging New actions") << (*listIt)->objectName();
578 mNewActions->removeAction(*listIt);
581 removeChildClient(plugin);
583 if (mCurrentPlugin == plugin) {
584 mCurrentPlugin = Q_NULLPTR;
585 createGUI(Q_NULLPTR);
588 plugin->deleteLater(); // removes the part automatically
589 mPlugins.erase(it);
590 if (plugin->showInSideBar()) {
591 QAction *q = mPluginAction[plugin]; // remove QAction, to free the shortcut for later use
592 mActionPlugins.removeAll(q);
593 mPluginAction.remove(plugin);
594 delete q;
597 if (mCurrentPlugin == Q_NULLPTR) {
598 PluginList::Iterator it;
599 PluginList::Iterator pluginEnd(mPlugins.end());
600 for (it = mPlugins.begin(); it != pluginEnd; ++it) {
601 if ((*it)->showInSideBar()) {
602 selectPlugin(*it);
603 return true;
607 return true;
612 return false;
615 void MainWindow::addPlugin(KontactInterface::Plugin *plugin)
617 qCDebug(KONTACT_LOG);
619 mPlugins.append(plugin);
621 if (plugin->showInSideBar()) {
622 QAction *action = new QAction(QIcon::fromTheme(plugin->icon()), plugin->title(), this);
623 //action->setHelpText(
624 // i18nc( "@info:status", "Plugin %1", plugin->title() ) );
625 action->setWhatsThis(
626 i18nc("@info:whatsthis",
627 "Switch to plugin %1", plugin->title()));
628 action->setCheckable(true);
629 action->setData(QVariant::fromValue(plugin)); // on the slot we can decode
630 // which action was triggered
631 connect(action, &QAction::triggered, this, &MainWindow::slotActionTriggered);
632 actionCollection()->addAction(plugin->title(), action);
633 mActionPlugins.append(action);
634 mPluginAction.insert(plugin, action);
637 // merge the plugins GUI into the main window
638 insertChildClient(plugin);
640 // sort the action plugins again and reset shortcuts. If we removed and then readded some plugins
641 // we need to take in count their weights for setting shortcuts again
642 std::sort(mActionPlugins.begin(), mActionPlugins.end(), pluginActionWeightLessThan);
643 std::sort(mPlugins.begin(), mPlugins.end(), pluginWeightLessThan);
644 int i = 0;
645 foreach (QAction *qaction, mActionPlugins) {
646 QAction *action = static_cast<QAction *>(qaction);
647 QString shortcut = QStringLiteral("Ctrl+%1").arg(mActionPlugins.count() - i);
648 actionCollection()->setDefaultShortcut(action, QKeySequence(shortcut));
649 ++i;
653 void MainWindow::partLoaded(KontactInterface::Plugin *plugin, KParts::ReadOnlyPart *part)
655 Q_UNUSED(plugin);
657 // See if we have this part already (e.g. due to two plugins sharing it)
658 if (mPartsStack->indexOf(part->widget()) != -1) {
659 return;
662 mPartsStack->addWidget(part->widget());
664 mPartManager->addPart(part, false);
665 // Workaround for KParts misbehavior: addPart calls show!
666 part->widget()->hide();
669 void MainWindow::slotActivePartChanged(KParts::Part *part)
671 if (!part) {
672 createGUI(Q_NULLPTR);
673 return;
676 qCDebug(KONTACT_LOG) << QStringLiteral("Part activated:") << part
677 << QStringLiteral("with stack id.") << mPartsStack->indexOf(part->widget());
679 statusBar()->clearMessage();
682 void MainWindow::slotNewClicked()
684 if (!mCurrentPlugin->newActions().isEmpty()) {
685 mCurrentPlugin->newActions().at(0)->trigger();
686 } else {
687 PluginList::Iterator it;
688 PluginList::Iterator end(mPlugins.end());
689 for (it = mPlugins.begin(); it != end; ++it) {
690 if (!(*it)->newActions().isEmpty()) {
691 (*it)->newActions().first()->trigger();
692 return;
698 KToolBar *MainWindow::findToolBar(const char *name)
700 // like KMainWindow::toolBar, but which doesn't create the toolbar if not found
701 return findChild<KToolBar *>(QLatin1String(name));
704 void MainWindow::selectPlugin(KontactInterface::Plugin *plugin)
706 if (!plugin) {
707 return;
710 if (plugin->isRunningStandalone()) {
711 statusBar()->showMessage(
712 i18nc("@info:status",
713 "Application is running standalone. Foregrounding..."), 1000);
714 plugin->bringToForeground();
715 return;
718 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
720 if (mCurrentPlugin) {
721 KConfigGroup grp = KSharedConfig::openConfig()->group(
722 QStringLiteral("MainWindow%1").arg(mCurrentPlugin->identifier()));
723 saveMainWindowSettings(grp);
726 KParts::Part *part = plugin->part();
728 if (!part) {
729 QApplication::restoreOverrideCursor();
730 KMessageBox::error(
731 this,
732 i18nc("@info",
733 "Cannot load part for %1.",
734 plugin->title()) + QLatin1Char('\n') + lastErrorMessage());
735 plugin->setDisabled(true);
736 mSidePane->updatePlugins();
737 return;
740 if (mCurrentPlugin) {
741 QAction *action = mPluginAction[ mCurrentPlugin ];
742 if (action) {
743 action->setChecked(false);
746 QAction *selectedPluginAction = mPluginAction[ plugin ];
747 if (selectedPluginAction) {
748 selectedPluginAction->setChecked(true);
751 // store old focus widget
752 QWidget *focusWidget = qApp->focusWidget();
753 if (mCurrentPlugin && focusWidget) {
754 // save the focus widget only when it belongs to the activated part
755 QWidget *parent = focusWidget->parentWidget();
756 while (parent) {
757 if (parent == mCurrentPlugin->part()->widget()) {
758 mFocusWidgets.insert(mCurrentPlugin->identifier(), QPointer<QWidget>(focusWidget));
760 parent = parent->parentWidget();
764 if (mSidePane) {
765 mSidePane->setCurrentPlugin(plugin->identifier());
768 plugin->aboutToSelect();
770 mPartManager->setActivePart(part);
771 QWidget *view = part->widget();
772 Q_ASSERT(view);
774 if (view) {
775 mPartsStack->setCurrentWidget(view);
776 view->show();
778 if (!mFocusWidgets.isEmpty() && mFocusWidgets.contains(plugin->identifier())) {
779 focusWidget = mFocusWidgets[ plugin->identifier() ];
780 if (focusWidget) {
781 focusWidget->setFocus();
783 } else {
784 view->setFocus();
787 mCurrentPlugin = plugin;
789 QAction *newAction = Q_NULLPTR;
790 if (!plugin->newActions().isEmpty()) {
791 newAction = plugin->newActions().at(0);
794 createGUI(plugin->part());
795 plugin->shortcutChanged();
797 setCaption(i18nc("@title:window Plugin dependent window title",
798 "%1 - Kontact", plugin->title()));
800 if (newAction) {
801 mNewActions->setIcon(newAction->icon());
802 static_cast<QAction *>(mNewActions)->setText(newAction->text());
803 mNewActions->setWhatsThis(newAction->whatsThis());
804 } else { // we'll use the action of the first plugin which offers one
805 PluginList::Iterator it;
806 PluginList::Iterator end(mPlugins.end());
807 for (it = mPlugins.begin(); it != end; ++it) {
808 if (!(*it)->newActions().isEmpty()) {
809 newAction = (*it)->newActions().first();
811 if (newAction) {
812 static_cast<QAction *>(mNewActions)->setIcon(newAction->icon());
813 mNewActions->setText(newAction->text());
814 mNewActions->setWhatsThis(newAction->whatsThis());
815 break;
821 KToolBar *navigatorToolBar = findToolBar("navigatorToolBar");
822 if (navigatorToolBar && !navigatorToolBar->isHidden() &&
823 (toolBarArea(navigatorToolBar) == Qt::TopToolBarArea ||
824 toolBarArea(navigatorToolBar) == Qt::BottomToolBarArea)) {
825 addToolBar(toolBarArea(navigatorToolBar), navigatorToolBar);
828 applyMainWindowSettings(KSharedConfig::openConfig()->group(
829 QStringLiteral("MainWindow%1").arg(plugin->identifier())));
831 QApplication::restoreOverrideCursor();
834 void MainWindow::slotActionTriggered()
836 QAction *actionSender = static_cast<QAction *>(sender());
837 actionSender->setChecked(true);
838 KontactInterface::Plugin *plugin = actionSender->data().value<KontactInterface::Plugin *>();
839 if (!plugin) {
840 return;
842 mSidePane->setCurrentPlugin(plugin->identifier());
845 void MainWindow::selectPlugin(const QString &pluginName)
847 PluginList::ConstIterator end = mPlugins.constEnd();
848 for (PluginList::ConstIterator it = mPlugins.constBegin(); it != end; ++it) {
849 if ((*it)->identifier() == pluginName) {
850 selectPlugin(*it);
851 return;
856 void MainWindow::loadSettings()
858 if (mSplitter) {
859 // if the preferences do not contain useful values, the side pane part of the splitter
860 // takes up the full width of the window, so leave the splitter sizing at the widget defaults
861 QList<int> sizes = Prefs::self()->sidePaneSplitter();
862 if (sizes.count() == mSplitter->count()) {
863 mSplitter->setSizes(sizes);
867 // Preload Plugins. This _must_ happen before the default part is loaded
868 PluginList::ConstIterator it;
869 PluginList::ConstIterator end(mDelayedPreload.constEnd());
870 for (it = mDelayedPreload.constBegin(); it != end; ++it) {
871 selectPlugin(*it);
873 selectPlugin(Prefs::self()->mActivePlugin);
876 void MainWindow::saveSettings()
878 if (mSplitter) {
879 Prefs::self()->mSidePaneSplitter = mSplitter->sizes();
882 if (mCurrentPlugin) {
883 Prefs::self()->mActivePlugin = mCurrentPlugin->identifier();
887 void MainWindow::slotShowIntroduction()
889 mPartsStack->setCurrentIndex(0);
892 void MainWindow::slotQuit()
894 mReallyClose = true;
895 close();
898 void MainWindow::slotPreferences()
900 static Kontact::KontactConfigureDialog *dlg = Q_NULLPTR;
901 if (!dlg) {
902 dlg = new Kontact::KontactConfigureDialog(this);
903 dlg->setAllowComponentSelection(true);
905 // do not show settings of components running standalone
906 KPluginInfo::List filteredPlugins = mPluginInfos;
907 PluginList::ConstIterator it;
908 PluginList::ConstIterator end(mPlugins.constEnd());
909 for (it = mPlugins.constBegin(); it != end; ++it) {
910 if ((*it)->isRunningStandalone()) {
911 KPluginInfo::List::ConstIterator infoIt;
912 KPluginInfo::List::ConstIterator infoEnd(filteredPlugins.constEnd());
913 for (infoIt = filteredPlugins.constBegin();
914 infoIt != infoEnd; ++infoIt) {
915 if (infoIt->pluginName() == (*it)->identifier()) {
916 filteredPlugins.removeAll(*infoIt);
917 break;
922 dlg->addPluginInfos(filteredPlugins);
923 connect(dlg, &Kontact::KontactConfigureDialog::pluginSelectionChanged, this, &MainWindow::pluginsChanged);
926 dlg->show();
929 void MainWindow::pluginsChanged()
931 unplugActionList(QStringLiteral("navigator_actionlist"));
932 unloadPlugins();
933 loadPlugins();
934 mSidePane->updatePlugins();
935 updateShortcuts();
938 void MainWindow::updateConfig()
940 qCDebug(KONTACT_LOG);
942 saveSettings();
943 loadSettings();
946 void MainWindow::showAboutDialog()
948 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
950 if (!mAboutDialog) {
951 mAboutDialog = new AboutDialog(this);
954 mAboutDialog->show();
955 mAboutDialog->raise();
956 QApplication::restoreOverrideCursor();
959 void MainWindow::configureShortcuts()
961 KShortcutsDialog dialog(
962 KShortcutsEditor::AllActions, KShortcutsEditor::LetterShortcutsAllowed, this);
963 dialog.addCollection(actionCollection());
965 if (mCurrentPlugin && mCurrentPlugin->part()) {
966 dialog.addCollection(mCurrentPlugin->part()->actionCollection());
969 dialog.configure();
970 if (mCurrentPlugin && mCurrentPlugin->part()) {
971 mCurrentPlugin->shortcutChanged();
975 void MainWindow::configureToolbars()
977 if (mCurrentPlugin) {
978 KConfigGroup grp(KSharedConfig::openConfig()->group(
979 QStringLiteral("MainWindow%1").arg(mCurrentPlugin->identifier())));
980 saveMainWindowSettings(grp);
982 QPointer<KEditToolBar> edit = new KEditToolBar(factory());
983 connect(edit.data(), &KEditToolBar::newToolBarConfig, this, &MainWindow::slotNewToolbarConfig);
984 edit->exec();
985 delete edit;
988 void MainWindow::slotNewToolbarConfig()
990 if (mCurrentPlugin && mCurrentPlugin->part()) {
991 createGUI(mCurrentPlugin->part());
993 if (mCurrentPlugin) {
994 applyMainWindowSettings(
995 KSharedConfig::openConfig()->group(
996 QStringLiteral("MainWindow%1").arg(mCurrentPlugin->identifier())));
998 updateShortcuts(); // for the plugActionList call
1001 void MainWindow::slotOpenUrl(const QUrl &url)
1003 if (url.scheme() == QLatin1String("exec")) {
1004 const QString path(url.path());
1005 if (path == QLatin1String("/switch")) {
1006 if (mCurrentPlugin) {
1007 mPartsStack->setCurrentIndex(mPartsStack->indexOf(mCurrentPlugin->part()->widget()));
1009 } else if (path == QLatin1String("/accountwizard")) {
1010 KRun::runCommand(QStringLiteral("accountwizard"), this);
1011 slotQuit();
1012 } else if (path.startsWith(QStringLiteral("/help"))) {
1013 QString app(QStringLiteral("kontact"));
1014 if (!url.query().isEmpty()) {
1015 app = url.query().mid(1);
1017 KHelpClient::invokeHelp(QString(), app);
1019 } else {
1020 new KRun(url, this);
1024 void MainWindow::readProperties(const KConfigGroup &config)
1026 Core::readProperties(config);
1028 QSet<QString> activePlugins =
1029 QSet<QString>::fromList(config.readEntry("ActivePlugins", QStringList()));
1031 if (!activePlugins.isEmpty()) {
1032 foreach (KontactInterface::Plugin *plugin, mPlugins) {
1033 if (!plugin->isRunningStandalone() && activePlugins.contains(plugin->identifier())) {
1034 plugin->readProperties(config);
1040 void MainWindow::saveProperties(KConfigGroup &config)
1042 Core::saveProperties(config);
1044 QStringList activePlugins;
1046 foreach (const KPluginInfo &pluginInfo, mPluginInfos) {
1047 if (pluginInfo.isPluginEnabled()) {
1048 KontactInterface::Plugin *plugin = pluginFromInfo(pluginInfo);
1049 if (plugin) {
1050 activePlugins.append(plugin->identifier());
1051 plugin->saveProperties(config);
1056 config.writeEntry("ActivePlugins", activePlugins);
1059 bool MainWindow::queryClose()
1061 if (qApp->isSavingSession() || mReallyClose) {
1062 return true;
1065 foreach (KontactInterface::Plugin *plugin, mPlugins) {
1066 if (!plugin->isRunningStandalone()) {
1067 if (!plugin->queryClose()) {
1068 return false;
1072 return true;
1075 void MainWindow::slotShowStatusMsg(const QString &msg)
1077 if (!statusBar() || !mStatusMsgLabel) {
1078 return;
1081 mStatusMsgLabel->setText(msg);
1084 QVariantHash MainWindow::introductionData()
1086 QVariantHash data;
1087 data[QStringLiteral("icon")] = QStringLiteral("kontact");
1088 data[QStringLiteral("name")] = i18n("Kontact");
1089 data[QStringLiteral("subtitle")] = i18n("The KDE Personal Information Management Suite.");
1090 data[QStringLiteral("version")] = KAboutData::applicationData().version();
1092 QVariantList links = {
1093 QVariantHash{ { QStringLiteral("url"), QStringLiteral("exec:/help?kontact") },
1094 { QStringLiteral("icon"), QStringLiteral("help-contents") },
1095 { QStringLiteral("title"), i18n("Read Manual") },
1096 { QStringLiteral("subtext"), i18n("Learn more about Kontact and its components") } },
1097 QVariantHash{ { QStringLiteral("url"), QStringLiteral("http://kontact.org") },
1098 { QStringLiteral("icon"), QStringLiteral("kontact") },
1099 { QStringLiteral("title"), i18n("Visit Kontact Website") },
1100 { QStringLiteral("subtext"), i18n("Access online resources and tutorials") } },
1101 QVariantHash{ { QStringLiteral("url"), QStringLiteral("exec:/accountwizard") },
1102 { QStringLiteral("icon"), QStringLiteral("tools-wizard") },
1103 { QStringLiteral("title"), i18n("Setup your Accounts") },
1104 { QStringLiteral("subtext"), i18n("Prepare Kontact for use") } }
1106 data[QStringLiteral("links")] = links;
1108 return data;
1111 void MainWindow::slotShowHideSideBar()
1113 QList<int> sizes = mSplitter->sizes();
1114 if (!sizes.isEmpty()) {
1115 if (sizes.at(0) != 0) {
1116 sizes[0] = 0;
1117 } else {
1118 sizes[0] = 10;
1120 mSplitter->setSizes(sizes);
1124 void MainWindow::setHelpText(QAction *action, const QString &text)
1126 action->setStatusTip(text);
1127 action->setToolTip(text);
1128 if (action->whatsThis().isEmpty()) {
1129 action->setWhatsThis(text);