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"
26 #include "iconsidepane.h"
28 #include "webengine/introductionwebengineview.h"
29 #include "webengine/introductionwebenginepage.h"
31 #include "kontactconfiguredialog.h"
32 using namespace Kontact
;
36 #include <Libkdepim/BroadcastStatus>
37 #include <Libkdepim/ProgressStatusBarWidget>
38 #include <Libkdepim/StatusbarProgressWidget>
40 #include <KontactInterface/Core>
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>
52 #include <KMessageBox>
53 #include <KPluginInfo>
55 #include <KServiceTypeTrader>
56 #include <KShortcutsDialog>
57 #include <KSqueezedTextLabel>
58 #include <KStandardAction>
60 #include <KParts/PartManager>
61 #include <ksettings/Dispatcher>
63 #include <KLocalizedString>
64 #include <QDBusConnection>
66 #include <QStackedWidget>
68 #include <QVBoxLayout>
71 #include <KHelpClient>
72 #include <KSharedConfig>
73 #include <QStandardPaths>
75 #include <QHBoxLayout>
76 #include <QApplication>
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
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
;
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
,
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
;
132 *dbusService
= (*it
)->registerClient();
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
),
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
);
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()
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
);
193 navigatorToolBar
->setLayoutDirection(Qt::LeftToRight
);
195 Q_ASSERT(navigatorToolBar
->sizeHint().isValid());
196 navigatorToolBar
->setMinimumWidth(navigatorToolBar
->sizeHint().width());
198 qCritical() << "Unable to find navigatorToolBar, probably kontactui.rc is missing";
202 void MainWindow::waitForKSycoca()
205 while (i
< KSYCOCA_WAIT_TIMEOUT
) {
206 if (KSycoca::isAvailable()) {
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
215 qCDebug(KONTACT_LOG
) << "Waiting for KSycoca";
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()) {
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
) {
240 // prepare the part manager
241 mPartManager
= new KParts::PartManager(this);
242 connect(mPartManager
, &KParts::PartManager::activePartChanged
, this, &MainWindow::slotActivePartChanged
);
247 mSidePane
->updatePlugins();
249 KSettings::Dispatcher::registerComponent(QStringLiteral("kontact"), this, "updateConfig");
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
) {
275 KSharedConfig::openConfig()->group(
276 QStringLiteral("MainWindow%1").arg(mCurrentPlugin
->identifier())));
277 saveMainWindowSettings(grp
);
280 createGUI(Q_NULLPTR
);
281 ServiceStarter::setPluginList(Q_NULLPTR
);
284 //QList<KParts::Part*> parts = mPartManager->parts();
286 // Q_FOREACH( KParts::Part *p, parts ) {
291 Prefs::self()->save();
293 // During deletion of plugins, we should not access the plugin list (bug #182176)
296 PluginList::ConstIterator end
= mPlugins
.constEnd();
297 for (PluginList::ConstIterator it
= mPlugins
.constBegin(); it
!= end
; ++it
) {
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
)) {
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
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);
366 paintAboutScreen(QStringLiteral("loading_kontact.html"), QVariantHash());
368 KPIM::ProgressStatusBarWidget
*progressStatusBarWidget
= new KPIM::ProgressStatusBarWidget(statusBar(), this);
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"),
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";
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
);
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
);
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()) {
460 void MainWindow::loadPlugins()
462 QList
<KontactInterface::Plugin
*> plugins
;
465 KPluginInfo::List::ConstIterator it
;
466 KPluginInfo::List::ConstIterator
end(mPluginInfos
.constEnd());
467 for (it
= mPluginInfos
.constBegin(); it
!= end
; ++it
) {
468 if (!it
->isPluginEnabled()) {
472 KontactInterface::Plugin
*plugin
= Q_NULLPTR
;
473 if (isPluginLoaded(*it
)) {
474 plugin
= pluginFromInfo(*it
);
476 plugin
->configUpdated();
481 qCDebug(KONTACT_LOG
) << "Loading Plugin:" << it
->name();
484 it
->service()->createInstance
<KontactInterface::Plugin
>(this, QVariantList(), &error
);
487 qCDebug(KONTACT_LOG
) << "Unable to create plugin for" << it
->name() << error
;
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()) {
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
));
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()) {
553 void MainWindow::updateShortcuts()
555 ActionPluginList::ConstIterator end
= mActionPlugins
.constEnd();
556 ActionPluginList::ConstIterator it
;
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
));
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
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
);
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()) {
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
);
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
));
653 void MainWindow::partLoaded(KontactInterface::Plugin
*plugin
, KParts::ReadOnlyPart
*part
)
657 // See if we have this part already (e.g. due to two plugins sharing it)
658 if (mPartsStack
->indexOf(part
->widget()) != -1) {
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
)
672 createGUI(Q_NULLPTR
);
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();
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();
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
)
710 if (plugin
->isRunningStandalone()) {
711 statusBar()->showMessage(
712 i18nc("@info:status",
713 "Application is running standalone. Foregrounding..."), 1000);
714 plugin
->bringToForeground();
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();
729 QApplication::restoreOverrideCursor();
733 "Cannot load part for %1.",
734 plugin
->title()) + QLatin1Char('\n') + lastErrorMessage());
735 plugin
->setDisabled(true);
736 mSidePane
->updatePlugins();
740 if (mCurrentPlugin
) {
741 QAction
*action
= mPluginAction
[ mCurrentPlugin
];
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();
757 if (parent
== mCurrentPlugin
->part()->widget()) {
758 mFocusWidgets
.insert(mCurrentPlugin
->identifier(), QPointer
<QWidget
>(focusWidget
));
760 parent
= parent
->parentWidget();
765 mSidePane
->setCurrentPlugin(plugin
->identifier());
768 plugin
->aboutToSelect();
770 mPartManager
->setActivePart(part
);
771 QWidget
*view
= part
->widget();
775 mPartsStack
->setCurrentWidget(view
);
778 if (!mFocusWidgets
.isEmpty() && mFocusWidgets
.contains(plugin
->identifier())) {
779 focusWidget
= mFocusWidgets
[ plugin
->identifier() ];
781 focusWidget
->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()));
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();
812 static_cast<QAction
*>(mNewActions
)->setIcon(newAction
->icon());
813 mNewActions
->setText(newAction
->text());
814 mNewActions
->setWhatsThis(newAction
->whatsThis());
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
*>();
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
) {
856 void MainWindow::loadSettings()
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
) {
873 selectPlugin(Prefs::self()->mActivePlugin
);
876 void MainWindow::saveSettings()
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()
898 void MainWindow::slotPreferences()
900 static Kontact::KontactConfigureDialog
*dlg
= Q_NULLPTR
;
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
);
922 dlg
->addPluginInfos(filteredPlugins
);
923 connect(dlg
, &Kontact::KontactConfigureDialog::pluginSelectionChanged
, this, &MainWindow::pluginsChanged
);
929 void MainWindow::pluginsChanged()
931 unplugActionList(QStringLiteral("navigator_actionlist"));
934 mSidePane
->updatePlugins();
938 void MainWindow::updateConfig()
940 qCDebug(KONTACT_LOG
);
946 void MainWindow::showAboutDialog()
948 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor
));
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());
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
);
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);
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
);
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
);
1050 activePlugins
.append(plugin
->identifier());
1051 plugin
->saveProperties(config
);
1056 config
.writeEntry("ActivePlugins", activePlugins
);
1059 bool MainWindow::queryClose()
1061 if (qApp
->isSavingSession() || mReallyClose
) {
1065 foreach (KontactInterface::Plugin
*plugin
, mPlugins
) {
1066 if (!plugin
->isRunningStandalone()) {
1067 if (!plugin
->queryClose()) {
1075 void MainWindow::slotShowStatusMsg(const QString
&msg
)
1077 if (!statusBar() || !mStatusMsgLabel
) {
1081 mStatusMsgLabel
->setText(msg
);
1084 QVariantHash
MainWindow::introductionData()
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
;
1111 void MainWindow::slotShowHideSideBar()
1113 QList
<int> sizes
= mSplitter
->sizes();
1114 if (!sizes
.isEmpty()) {
1115 if (sizes
.at(0) != 0) {
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
);