Better solution for hiding specific actions in kontact: tagging them in the xml
[kdepim.git] / kontact / src / mainwindow.cpp
blobeae2bca640263ec3886f65fd1021369f50c4242a
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 <config-enterprise.h>
25 #include "mainwindow.h"
26 #include "aboutdialog.h"
27 #include "iconsidepane.h"
28 #include "prefs.h"
29 #include "progressdialog.h"
30 #include "statusbarprogresswidget.h"
31 #include "broadcaststatus.h"
33 #include <kontactinterface/plugin.h>
34 using namespace KontactInterface;
36 #include <kpimutils/kfileio.h>
38 #include <kparts/componentfactory.h>
39 #include <ksettings/dialog.h>
40 #include <ksettings/dispatcher.h>
41 #include <kactioncollection.h>
42 #include <kapplication.h>
43 #include <kconfig.h>
44 #include <kconfiggroup.h>
45 #include <kdebug.h>
46 #include <kdbusservicestarter.h>
47 #include <kedittoolbar.h>
48 #include <kguiitem.h>
49 #include <khelpmenu.h>
50 #include <kiconloader.h>
51 #include <kshortcutsdialog.h>
52 #include <klibloader.h>
53 #include <ktoolbar.h>
54 #include <klocale.h>
55 #include <kmessagebox.h>
56 #include <kplugininfo.h>
57 #include <kmenu.h>
58 #include <kshortcut.h>
59 #include <kstandarddirs.h>
60 #include <kstatusbar.h>
61 #include <kstandardaction.h>
62 #include <ktip.h>
63 #include <kservice.h>
64 #include <kservicetypetrader.h>
65 #include <kstringhandler.h>
66 #include <ksqueezedtextlabel.h>
67 #include <khtml_part.h>
68 #include <khtmlview.h>
69 #include <krun.h>
70 #include <kaboutdata.h>
71 #include <kmenubar.h>
72 #include <kstandardshortcut.h>
73 #include <ktoolinvocation.h>
74 #include <kactionmenu.h>
75 #include <khbox.h>
76 #include <kvbox.h>
77 #include <kicon.h>
78 #include <kxmlguifactory.h>
80 #include <QComboBox>
81 #include <QCursor>
82 #include <QDBusConnection>
83 #include <QImage>
84 #include <QLayout>
85 #include <QList>
86 #include <QObject>
87 #include <QPushButton>
88 #include <QSplitter>
89 #include <QStackedWidget>
90 #include <QTimer>
92 using namespace Kontact;
94 // This class extends the normal KDBusServiceStarter.
96 // When a service start is requested, it asks all plugins
97 // to create their dbus interfaces in addition to the normal
98 // way of starting a service.
99 class ServiceStarter : public KDBusServiceStarter
101 public:
103 virtual int startServiceFor( const QString &serviceType,
104 const QString &constraint = QString(),
105 QString *error = 0, QString *dbusService = 0,
106 int flags = 0 );
108 // We need to keep track of the plugins which are loaded, so pass a pointer
109 // to the plugin list here. Be sure to reset it back to 0 with
110 // setPluginList() as soon as the list gets destroyed.
111 ServiceStarter( PluginList *pluginList ) {
112 mPlugins = pluginList;
115 static void setPluginList( PluginList *pluginList ) {
116 mPlugins = pluginList;
119 protected:
121 virtual ~ServiceStarter() {}
122 static PluginList *mPlugins;
125 PluginList *ServiceStarter::mPlugins = 0;
127 int ServiceStarter::startServiceFor( const QString &serviceType,
128 const QString &constraint,
129 QString *error, QString *dbusService,
130 int flags )
132 if ( mPlugins ) {
133 PluginList::ConstIterator end = mPlugins->constEnd();
134 for ( PluginList::ConstIterator it = mPlugins->constBegin(); it != end; ++it ) {
135 if ( (*it)->createDBUSInterface( serviceType ) ) {
136 kDebug() << "found interface for" << serviceType;
137 if ( dbusService ) {
138 *dbusService = (*it)->registerClient();
140 return 0;
145 kDebug() << "Didn't find dbus interface, falling back to external process";
146 return KDBusServiceStarter::startServiceFor( serviceType, constraint,
147 error, dbusService, flags );
150 MainWindow::MainWindow()
151 : KontactInterface::Core(), mSplitter( 0 ), mCurrentPlugin( 0 ), mAboutDialog( 0 ),
152 mReallyClose( false ), mSyncActionsEnabled( true )
154 // The ServiceStarter created here will be deleted by the KDbusServiceStarter
155 // base class, which is a global static.
156 new ServiceStarter( &mPlugins );
158 QDBusConnection::sessionBus().registerObject(
159 "/KontactInterface", this, QDBusConnection::ExportScriptableSlots );
161 // Set this to be the group leader for all subdialogs - this means
162 // modal subdialogs will only affect this dialog, not the other windows
163 setAttribute( Qt::WA_GroupLeader );
165 initGUI();
166 initObject();
168 mSidePane->setMaximumWidth( mSidePane->sizeHint().width() );
169 mSidePane->setMinimumWidth( mSidePane->sizeHint().width() );
171 factory()->plugActionList( this, QString( "navigator_actionlist" ), mActionPlugins );
173 restoreWindowSize( KConfigGroup( KGlobal::config(), "MainWindow" ) );
174 setAutoSaveSettings();
177 void MainWindow::initGUI()
179 initWidgets();
180 setupActions();
181 setHelpMenuEnabled( false );
182 KHelpMenu *helpMenu = new KHelpMenu( this, 0, true, actionCollection() );
183 connect( helpMenu, SIGNAL(showAboutApplication()), SLOT(showAboutDialog()) );
185 KStandardAction::keyBindings( this, SLOT(configureShortcuts()), actionCollection() );
186 KStandardAction::configureToolbars( this, SLOT(configureToolbars()), actionCollection() );
187 setXMLFile( "kontactui.rc" );
189 setStandardToolBarMenuEnabled( true );
191 createGUI( 0 );
193 KToolBar *navigatorToolBar = findToolBar( "navigatorToolBar" );
194 Q_ASSERT( navigatorToolBar );
195 if ( layoutDirection() == Qt::LeftToRight ) {
196 navigatorToolBar->setLayoutDirection( Qt::RightToLeft );
197 } else {
198 navigatorToolBar->setLayoutDirection( Qt::LeftToRight );
200 Q_ASSERT( navigatorToolBar->sizeHint().isValid() );
201 navigatorToolBar->setMinimumWidth( navigatorToolBar->sizeHint().width() );
204 void MainWindow::initObject()
206 KService::List offers = KServiceTypeTrader::self()->query(
207 QString::fromLatin1( "Kontact/Plugin" ),
208 QString( "[X-KDE-KontactPluginVersion] == %1" ).arg( KONTACT_PLUGIN_VERSION ) );
209 mPluginInfos = KPluginInfo::fromServices(
210 offers, KConfigGroup( Prefs::self()->config(), "Plugins" ) );
212 KPluginInfo::List::Iterator it;
213 for ( it = mPluginInfos.begin(); it != mPluginInfos.end(); ++it ) {
214 it->load();
217 // prepare the part manager
218 mPartManager = new KParts::PartManager( this );
219 connect( mPartManager, SIGNAL(activePartChanged(KParts::Part *)),
220 this, SLOT(slotActivePartChanged(KParts::Part *)) );
222 loadPlugins();
224 if ( mSidePane ) {
225 mSidePane->updatePlugins();
228 KSettings::Dispatcher::registerComponent( componentData(), this, "updateConfig" );
230 loadSettings();
232 statusBar()->show();
234 QTimer::singleShot( 200, this, SLOT( slotShowTipOnStart() ) );
236 // done initializing
237 slotShowStatusMsg( QString::null ); //krazy:exclude=nullstrassign for old broken gcc
239 connect( KPIM::BroadcastStatus::instance(), SIGNAL(statusMsg(const QString &)),
240 this, SLOT(slotShowStatusMsg(const QString &)) );
242 // launch commandline specified module if any
243 activatePluginModule();
245 if ( Prefs::lastVersionSeen() == KGlobal::mainComponent().aboutData()->version() ) {
246 selectPlugin( mCurrentPlugin );
249 paintAboutScreen( introductionString() );
250 Prefs::setLastVersionSeen( KGlobal::mainComponent().aboutData()->version() );
253 MainWindow::~MainWindow()
255 if ( mCurrentPlugin ) {
256 saveMainWindowSettings( KGlobal::config()->group(
257 QString( "MainWindow%1" ).arg( mCurrentPlugin->identifier() ) ) );
260 createGUI( 0 );
261 ServiceStarter::setPluginList( 0 );
262 saveSettings();
264 QList<KParts::Part*> parts = mPartManager->parts();
266 // Q_FOREACH( KParts::Part *p, parts ) {
267 // delete p;
268 // p = 0;
269 // }
271 Prefs::self()->writeConfig();
273 // During deletion of plugins, we should not access the plugin list (bug #182176)
274 delete mSidePane;
276 PluginList::ConstIterator end = mPlugins.constEnd();
277 for ( PluginList::ConstIterator it = mPlugins.constBegin(); it != end; ++it ) {
278 delete *it;
282 void MainWindow::setActivePluginModule( const QString &module )
284 mActiveModule = module;
285 activatePluginModule();
288 bool MainWindow::pluginActionWeightLessThan( const QAction *left, const QAction *right )
290 // Since this lessThan method is used only for the toolbar (which is on
291 // the inverse layout direction than the rest of the system), we add the
292 // elements on the exactly inverse order. (ereslibre)
293 return !pluginWeightLessThan( left->data().value<KontactInterface::Plugin*>(),
294 right->data().value<KontactInterface::Plugin*>() );
297 bool MainWindow::pluginWeightLessThan( const KontactInterface::Plugin *left,
298 const KontactInterface::Plugin *right )
300 return left->weight() < right->weight();
303 void MainWindow::activatePluginModule()
305 if ( !mActiveModule.isEmpty() ) {
306 PluginList::ConstIterator end = mPlugins.constEnd();
307 for ( PluginList::ConstIterator it = mPlugins.constBegin(); it != end; ++it ) {
308 if ( ( *it )->identifier().contains( mActiveModule ) ) {
309 selectPlugin( *it );
310 return;
316 void MainWindow::initWidgets()
318 QWidget *mTopWidget = new QWidget( this );
319 QVBoxLayout *layout = new QVBoxLayout;
320 layout->setMargin(0);
321 layout->setSpacing(0);
322 mTopWidget->setLayout( layout );
323 setCentralWidget( mTopWidget );
325 mSplitter = new QSplitter( mTopWidget );
326 layout->addWidget( mSplitter );
327 mSidePane = new IconSidePane( this, mSplitter );
329 // don't occupy screen estate on load
331 QList<int> sizes;
332 sizes << 0;
333 mSplitter->setSizes(sizes);
335 connect( mSidePane, SIGNAL(pluginSelected(KontactInterface::Plugin *)),
336 SLOT(selectPlugin(KontactInterface::Plugin *)) );
338 mPartsStack = new QStackedWidget( mSplitter );
339 mPartsStack->layout()->setSpacing( 0 );
341 initAboutScreen();
343 QString loading =
344 i18n( "<h2 style='text-align:center; margin-top: 0px; margin-bottom: 0px'>%1</h2>",
345 i18n( "Loading Kontact..." ) );
347 paintAboutScreen( loading );
349 /* Create a progress dialog and hide it. */
350 KPIM::ProgressDialog *progressDialog = new KPIM::ProgressDialog( statusBar(), this );
351 progressDialog->hide();
353 mLittleProgress = new KPIM::StatusbarProgressWidget( progressDialog, statusBar() );
355 mStatusMsgLabel = new KSqueezedTextLabel( i18n( " Initializing..." ), statusBar() );
356 mStatusMsgLabel->setTextElideMode( Qt::ElideRight );
357 mStatusMsgLabel->setAlignment( Qt::AlignLeft | Qt::AlignVCenter );
359 statusBar()->addWidget( mStatusMsgLabel, 10 );
360 statusBar()->addPermanentWidget( mLittleProgress, 0 );
361 mLittleProgress->show();
363 mSplitter->setCollapsible( 1, false );
366 void MainWindow::paintAboutScreen( const QString &msg )
368 QString location = KStandardDirs::locate( "data", "kontact/about/main.html" );
369 QString content = KPIMUtils::kFileToByteArray( location );
370 content = content.arg( KStandardDirs::locate( "data", "kdeui/about/kde_infopage.css" ) );
371 if ( QApplication::isRightToLeft() ) {
372 content = content.arg( "@import \"%1\";" ).
373 arg( KStandardDirs::locate( "data", "kdeui/about/kde_infopage_rtl.css" ) );
374 } else {
375 content = content.arg( "" );
378 mIntroPart->begin( KUrl( location ) );
380 QString appName( i18n( "KDE Kontact" ) );
381 QString catchPhrase( i18n( "Get Organized!" ) );
382 QString quickDescription( i18n( "The KDE Personal Information Management Suite" ) );
384 mIntroPart->write(
385 content.arg( QFont().pointSize() + 2 ).
386 arg( appName ).arg( catchPhrase ).arg( quickDescription ).arg( msg ) );
387 mIntroPart->end();
390 void MainWindow::initAboutScreen()
392 KHBox *introbox = new KHBox( mPartsStack );
393 mPartsStack->addWidget( introbox );
394 mPartsStack->setCurrentWidget( introbox );
395 mIntroPart = new KHTMLPart( introbox );
396 mIntroPart->widget()->setFocusPolicy( Qt::WheelFocus );
397 // Let's better be paranoid and disable plugins (it defaults to enabled):
398 mIntroPart->setPluginsEnabled( false );
399 mIntroPart->setJScriptEnabled( false ); // just make this explicit
400 mIntroPart->setJavaEnabled( false ); // just make this explicit
401 mIntroPart->setMetaRefreshEnabled( false );
402 mIntroPart->setURLCursor( QCursor( Qt::PointingHandCursor ) );
403 mIntroPart->view()->setLineWidth( 0 );
405 connect( mIntroPart->browserExtension(),
406 SIGNAL(openUrlRequest(const KUrl &,const KParts::OpenUrlArguments &,
407 const KParts::BrowserArguments &)),
408 SLOT(slotOpenUrl(const KUrl &)) );
410 connect( mIntroPart->browserExtension(),
411 SIGNAL(createNewWindow(const KUrl &,const KParts::OpenUrlArguments &,
412 const KParts::BrowserArguments &)),
413 SLOT(slotOpenUrl(const KUrl&)) );
416 void MainWindow::setupActions()
418 actionCollection()->addAction( KStandardAction::Quit, this, SLOT(slotQuit()) );
420 #ifdef Q_WS_MACX
421 // ### This quits the application prematurely, for example when the composer window
422 // ### is closed while the main application is minimized to the systray
423 connect( qApp, SIGNAL(lastWindowClosed()), qApp, SLOT(quit()));
424 #endif
426 mNewActions = new KActionMenu(
427 KIcon( "" ),
428 i18nc( "@title:menu create new pim items (message,calendar,to-do,etc.)", "New" ), this );
429 actionCollection()->addAction( "action_new", mNewActions );
430 mNewActions->setShortcut( KStandardShortcut::openNew() );
431 connect( mNewActions, SIGNAL(triggered(bool)), this, SLOT(slotNewClicked()) );
433 // If the user is using disconnected imap mail folders as groupware, we add
434 // plugins' Synchronize actions to the toolbar which trigger an imap sync.
435 // Otherwise it's redundant and misleading.
436 KConfig *_cfg = Prefs::self()->config();
437 KConfigGroup cfg( _cfg, "Kontact Groupware Settings" );
438 #if defined(KDEPIM_ENTERPRISE_BUILD)
439 mSyncActionsEnabled = cfg.readEntry( "GroupwareMailFoldersEnabled", true );
440 #else
441 mSyncActionsEnabled = cfg.readEntry( "GroupwareMailFoldersEnabled", false );
442 #endif
444 if ( mSyncActionsEnabled ) {
445 mSyncActions = new KActionMenu(
446 KIcon( "view-refresh" ),
447 i18nc( "@title:menu synchronize pim items (message,calendar,to-do,etc.)", "Sync" ), this );
448 actionCollection()->addAction( "action_sync", mSyncActions );
449 mSyncActions->setShortcut( KStandardShortcut::reload() );
450 connect( mSyncActions, SIGNAL(triggered(bool)), this, SLOT(slotSyncClicked()) );
453 KAction *action = new KAction( KIcon( "configure" ), i18n( "Configure Kontact..." ), this );
454 actionCollection()->addAction( "settings_configure_kontact", action );
455 connect( action, SIGNAL(triggered(bool)), SLOT(slotPreferences()) );
457 action = new KAction( KIcon( "kontact" ), i18n( "&Kontact Introduction" ), this );
458 actionCollection()->addAction( "help_introduction", action );
459 connect( action, SIGNAL(triggered(bool)), SLOT(slotShowIntroduction()) );
460 action = new KAction( KIcon( "ktip" ), i18n( "&Tip of the Day" ), this );
461 actionCollection()->addAction( "help_tipofday", action );
462 connect( action, SIGNAL(triggered(bool)), SLOT(slotShowTip()) );
465 bool MainWindow::isPluginLoaded( const KPluginInfo &info )
467 return ( pluginFromInfo( info ) != 0 );
470 Plugin *MainWindow::pluginFromInfo( const KPluginInfo &info )
472 PluginList::ConstIterator end = mPlugins.constEnd();
473 for ( PluginList::ConstIterator it = mPlugins.constBegin(); it != end; ++it ) {
474 if ( (*it)->identifier() == info.pluginName() ) {
475 return *it;
478 return 0;
481 void MainWindow::loadPlugins()
483 QList<Plugin*> plugins;
485 int i;
486 KPluginInfo::List::ConstIterator it;
487 for ( it = mPluginInfos.constBegin(); it != mPluginInfos.constEnd(); ++it ) {
488 if ( !it->isPluginEnabled() ) {
489 continue;
492 Plugin *plugin = 0;
493 if ( isPluginLoaded( *it ) ) {
494 plugin = pluginFromInfo( *it );
495 if ( plugin ) {
496 plugin->configUpdated();
498 continue;
501 kDebug() << "Loading Plugin:" << it->name();
502 plugin = KService::createInstance<KontactInterface::Plugin>( it->service(), this );
504 if ( !plugin ) {
505 kDebug() << "Unable to create plugin for " << it->name();
506 continue;
509 plugin->setIdentifier( it->pluginName() );
510 plugin->setTitle( it->name() );
511 plugin->setIcon( it->icon() );
513 QVariant libNameProp = it->property( "X-KDE-KontactPartLibraryName" );
514 QVariant exeNameProp = it->property( "X-KDE-KontactPartExecutableName" );
515 QVariant loadOnStart = it->property( "X-KDE-KontactPartLoadOnStart" );
516 QVariant hasPartProp = it->property( "X-KDE-KontactPluginHasPart" );
518 if ( !loadOnStart.isNull() && loadOnStart.toBool() ) {
519 mDelayedPreload.append( plugin );
522 kDebug() << "LIBNAMEPART:" << libNameProp.toString();
524 plugin->setPartLibraryName( libNameProp.toString().toUtf8() );
525 plugin->setExecutableName( exeNameProp.toString() );
526 if ( hasPartProp.isValid() ) {
527 plugin->setShowInSideBar( hasPartProp.toBool() );
530 for ( i = 0; i < plugins.count(); ++i ) {
531 Plugin *p = plugins.at( i );
532 if ( plugin->weight() < p->weight() ) {
533 break;
536 plugins.insert( i, plugin );
540 for ( i = 0; i < plugins.count(); ++ i ) {
541 Plugin *plugin = plugins.at( i );
543 const QList<KAction*> actionList = plugin->newActions();
544 QList<KAction*>::const_iterator listIt;
546 for ( listIt = actionList.begin(); listIt != actionList.end(); ++listIt ) {
547 kDebug() << "Plugging New actions" << (*listIt)->objectName();
548 mNewActions->addAction( (*listIt) );
551 if ( mSyncActionsEnabled ) {
552 Q_FOREACH( KAction *listIt, plugin->syncActions() ) {
553 kDebug() << "Plugging Sync actions" << listIt->objectName();
554 mSyncActions->addAction( listIt );
557 addPlugin( plugin );
560 mNewActions->setEnabled( mPlugins.size() != 0 );
561 if ( mSyncActionsEnabled ) {
562 mSyncActions->setEnabled( mPlugins.size() != 0 );
566 void MainWindow::unloadPlugins()
568 KPluginInfo::List::ConstIterator end = mPluginInfos.constEnd();
569 KPluginInfo::List::ConstIterator it;
570 for ( it = mPluginInfos.constBegin(); it != end; ++it ) {
571 if ( !it->isPluginEnabled() ) {
572 removePlugin( *it );
577 void MainWindow::updateShortcuts()
579 ActionPluginList::ConstIterator end = mActionPlugins.constEnd();
580 ActionPluginList::ConstIterator it;
581 int i = 0;
582 for ( it = mActionPlugins.constBegin(); it != end; ++it ) {
583 KAction *action = static_cast<KAction*>( *it );
584 QString shortcut = QString( "Ctrl+%1" ).arg( mActionPlugins.count() - i );
585 action->setShortcut( KShortcut( shortcut ) );
586 i++;
588 factory()->plugActionList( this, QString( "navigator_actionlist" ), mActionPlugins );
591 bool MainWindow::removePlugin( const KPluginInfo &info )
593 PluginList::Iterator end = mPlugins.end();
594 for ( PluginList::Iterator it = mPlugins.begin(); it != end; ++it ) {
595 Plugin *plugin = *it;
596 if ( ( *it )->identifier() == info.pluginName() ) {
597 QList<KAction*> actionList = plugin->newActions();
598 QList<KAction*>::const_iterator listIt;
600 for ( listIt = actionList.constBegin(); listIt != actionList.constEnd(); ++listIt ) {
601 kDebug() << "Unplugging New actions" << (*listIt)->objectName();
602 mNewActions->removeAction( *listIt );
605 if ( mSyncActionsEnabled ) {
606 actionList = plugin->syncActions();
607 for ( listIt = actionList.constBegin(); listIt != actionList.constEnd(); ++listIt ) {
608 kDebug() << "Unplugging Sync actions" << (*listIt)->objectName();
609 mSyncActions->removeAction( *listIt );
612 removeChildClient( plugin );
614 if ( mCurrentPlugin == plugin ) {
615 mCurrentPlugin = 0;
616 createGUI( 0 );
619 plugin->deleteLater(); // removes the part automatically
620 mPlugins.erase( it );
621 if ( plugin->showInSideBar() ) {
622 QAction *q = mPluginAction[plugin]; // remove KAction, to free the shortcut for later use
623 mActionPlugins.removeAll( q );
624 mPluginAction.remove(plugin);
625 delete q;
628 if ( mCurrentPlugin == 0 ) {
629 PluginList::Iterator it;
630 for ( it = mPlugins.begin(); it != mPlugins.end(); ++it ) {
631 if ( (*it)->showInSideBar() ) {
632 selectPlugin( *it );
633 return true;
637 return true;
642 return false;
645 void MainWindow::addPlugin( KontactInterface::Plugin *plugin )
647 kDebug();
649 mPlugins.append( plugin );
651 if ( plugin->showInSideBar() ) {
652 KAction *action = new KAction( KIcon( plugin->icon() ), plugin->title(), this );
653 action->setCheckable( true );
654 action->setData( QVariant::fromValue( plugin ) ); // on the slot we can decode which action was
655 // triggered
656 connect( action, SIGNAL(triggered(bool)), SLOT(slotActionTriggered()) );
657 actionCollection()->addAction( plugin->title(), action );
658 mActionPlugins.append( action );
659 mPluginAction.insert( plugin, action );
662 // merge the plugins GUI into the main window
663 insertChildClient( plugin );
665 // sort the action plugins again and reset shortcuts. If we removed and then readded some plugins
666 // we need to take in count their weights for setting shortcuts again
667 qSort( mActionPlugins.begin(), mActionPlugins.end(), pluginActionWeightLessThan );
668 qSort( mPlugins.begin(), mPlugins.end(), pluginWeightLessThan );
669 int i = 0;
670 foreach ( QAction *qaction, mActionPlugins ) {
671 KAction *action = static_cast<KAction*>( qaction );
672 QString shortcut = QString( "Ctrl+%1" ).arg( mActionPlugins.count() - i );
673 action->setShortcut( KShortcut( shortcut ) );
674 i++;
678 void MainWindow::partLoaded( KontactInterface::Plugin *plugin, KParts::ReadOnlyPart *part )
680 Q_UNUSED( plugin );
682 // See if we have this part already (e.g. due to two plugins sharing it)
683 if ( mPartsStack->indexOf( part->widget() ) != -1 ) {
684 return;
687 mPartsStack->addWidget( part->widget() );
689 mPartManager->addPart( part, false );
690 // Workaround for KParts misbehavior: addPart calls show!
691 part->widget()->hide();
694 void MainWindow::slotActivePartChanged( KParts::Part *part )
696 if ( !part ) {
697 createGUI( 0 );
698 return;
701 kDebug() << "Part activated:" << part
702 << "with stack id."<< mPartsStack->indexOf( part->widget() );
704 statusBar()->clearMessage();
707 void MainWindow::slotNewClicked()
709 if ( !mCurrentPlugin->newActions().isEmpty() ) {
710 mCurrentPlugin->newActions().first()->trigger();
711 } else {
712 PluginList::Iterator it;
713 for ( it = mPlugins.begin(); it != mPlugins.end(); ++it ) {
714 if ( !(*it)->newActions().isEmpty() ) {
715 (*it)->newActions().first()->trigger();
716 return;
722 void MainWindow::slotSyncClicked()
724 if ( !mCurrentPlugin->syncActions().isEmpty() ) {
725 mCurrentPlugin->syncActions().first()->trigger();
726 } else {
727 PluginList::Iterator it;
728 for ( it = mPlugins.begin(); it != mPlugins.end(); ++it ) {
729 if ( !(*it)->syncActions().isEmpty() ) {
730 (*it)->syncActions().first()->trigger();
731 return;
737 KToolBar *KontactInterface::MainWindow::findToolBar( const char *name )
739 // like KMainWindow::toolBar, but which doesn't create the toolbar if not found
740 return findChild<KToolBar *>( name );
743 void MainWindow::selectPlugin( KontactInterface::Plugin *plugin )
745 if ( !plugin ) {
746 return;
749 if ( plugin->isRunningStandalone() ) {
750 statusBar()->showMessage( i18n( "Application is running standalone. Foregrounding..." ), 1000 );
751 plugin->bringToForeground();
752 return;
755 QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
757 if ( mCurrentPlugin ) {
758 saveMainWindowSettings( KGlobal::config()->group(
759 QString( "MainWindow%1" ).arg( mCurrentPlugin->identifier() ) ) );
762 KParts::Part *part = plugin->part();
764 if ( !part ) {
765 QApplication::restoreOverrideCursor();
766 KMessageBox::error( this, i18n( "Cannot load part for %1.",
767 plugin->title() ) + '\n' + lastErrorMessage() );
768 plugin->setDisabled( true );
769 mSidePane->updatePlugins();
770 return;
773 if ( mCurrentPlugin ) {
774 KAction *action = mPluginAction[ mCurrentPlugin ];
775 if ( action ) {
776 action->setChecked( false );
779 KAction *selectedPluginAction = mPluginAction[ plugin ];
780 if ( selectedPluginAction ) {
781 selectedPluginAction->setChecked( true );
784 // store old focus widget
785 QWidget *focusWidget = kapp->focusWidget();
786 if ( mCurrentPlugin && focusWidget ) {
787 // save the focus widget only when it belongs to the activated part
788 QWidget *parent = focusWidget->parentWidget();
789 while ( parent ) {
790 if ( parent == mCurrentPlugin->part()->widget() ) {
791 mFocusWidgets.insert( mCurrentPlugin->identifier(), QPointer<QWidget>( focusWidget ) );
793 parent = parent->parentWidget();
797 if ( mSidePane ) {
798 mSidePane->setCurrentPlugin( plugin->identifier() );
801 plugin->select();
803 mPartManager->setActivePart( part );
804 QWidget *view = part->widget();
805 Q_ASSERT( view );
807 if ( view ) {
808 mPartsStack->setCurrentWidget( view );
809 view->show();
811 if ( mFocusWidgets.contains( plugin->identifier() ) ) {
812 focusWidget = mFocusWidgets[ plugin->identifier() ];
813 if ( focusWidget ) {
814 focusWidget->setFocus();
816 } else {
817 view->setFocus();
820 mCurrentPlugin = plugin;
822 QAction *newAction = 0;
823 if ( !plugin->newActions().isEmpty() ) {
824 newAction = plugin->newActions().first();
827 QAction *syncAction = 0;
828 if ( !plugin->syncActions().isEmpty() ) {
829 syncAction = plugin->syncActions().first();
832 removeInvisibleToolbarActions(plugin);
834 createGUI( plugin->part() );
836 setCaption( i18nc( "Plugin dependent window title", "%1 - Kontact", plugin->title() ) );
838 if ( newAction ) {
839 mNewActions->setIcon( newAction->icon() );
840 static_cast<QAction*>( mNewActions )->setText( newAction->text() );
841 mNewActions->setWhatsThis( newAction->whatsThis() );
842 } else { // we'll use the action of the first plugin which offers one
843 PluginList::Iterator it;
844 for ( it = mPlugins.begin(); it != mPlugins.end(); ++it ) {
845 if ( (*it)->newActions().count() > 0 ) {
846 newAction = (*it)->newActions().first();
848 if ( newAction ) {
849 static_cast<QAction*>( mNewActions )->setIcon( newAction->icon() );
850 mNewActions->setText( newAction->text() );
851 mNewActions->setWhatsThis( newAction->whatsThis() );
852 break;
857 if ( mSyncActionsEnabled ) {
858 if ( syncAction ) {
859 mSyncActions->setIcon( syncAction->icon() );
860 static_cast<QAction*>( mSyncActions )->setText( syncAction->text() );
861 } else { // we'll use the action of the first plugin which offers one
862 PluginList::Iterator it;
863 for ( it = mPlugins.begin(); it != mPlugins.end(); ++it ) {
864 if ( (*it)->syncActions().count() > 0 ) {
865 syncAction = (*it)->syncActions().first();
867 if ( syncAction ) {
868 static_cast<QAction*>( mSyncActions )->setIcon( syncAction->icon() );
869 mSyncActions->setText( syncAction->text() );
870 break;
877 KToolBar *navigatorToolBar = findToolBar( "navigatorToolBar" );
878 if ( navigatorToolBar && !navigatorToolBar->isHidden() &&
879 ( toolBarArea( navigatorToolBar ) == Qt::TopToolBarArea ||
880 toolBarArea( navigatorToolBar ) == Qt::BottomToolBarArea ) ) {
881 addToolBar( toolBarArea( navigatorToolBar ), navigatorToolBar );
884 applyMainWindowSettings( KGlobal::config()->group(
885 QString( "MainWindow%1" ).arg( plugin->identifier() ) ) );
887 QApplication::restoreOverrideCursor();
890 // Hack to access KXMLGUIClient::setDOMDocument which should be public...
891 class ReadOnlyPartAccess : public KParts::ReadOnlyPart
893 public:
894 using KParts::ReadOnlyPart::setDOMDocument;
897 void MainWindow::removeInvisibleToolbarActions(Plugin* plugin)
899 // Hide unwanted toolbar action by modifying the XML before createGUI, rather than
900 // doing it by calling removeAction on the toolbar after createGUI. Both solutions
901 // work visually, but only modifying the XML ensures that the actions don't appear
902 // in "edit toolbars". #207296
903 KParts::Part *part = plugin->part();
904 const QStringList hideActions = plugin->invisibleToolbarActions();
905 //kDebug() << "Hiding actions" << hideActions;
906 QDomDocument doc = part->domDocument();
907 QDomElement docElem = doc.documentElement();
908 // 1. Iterate over containers
909 for (QDomElement containerElem = docElem.firstChildElement();
910 !containerElem.isNull(); containerElem = containerElem.nextSiblingElement()) {
911 if (QString::compare(containerElem.tagName(), "ToolBar", Qt::CaseInsensitive) == 0) {
912 // 2. Iterate over actions in toolbars
913 QDomElement actionElem = containerElem.firstChildElement();
914 while (!actionElem.isNull()) {
915 QDomElement nextActionElem = actionElem.nextSiblingElement();
916 if (actionElem.attribute("remove_in_kontact") == "true") {
917 containerElem.removeChild(actionElem);
919 if (QString::compare(actionElem.tagName(), "Action", Qt::CaseInsensitive) == 0) {
920 //kDebug() << "Looking at action" << actionElem.attribute("name");
921 if (hideActions.contains(actionElem.attribute("name"))) {
922 //kDebug() << "REMOVING";
923 containerElem.removeChild(actionElem);
926 actionElem = nextActionElem;
930 //kDebug() << doc.toString();
931 static_cast<ReadOnlyPartAccess *>(part)->setDOMDocument(doc);
934 void MainWindow::slotActionTriggered()
936 KAction *actionSender = static_cast<KAction*>( sender() );
937 actionSender->setChecked( true );
938 KontactInterface::Plugin *plugin = actionSender->data().value<KontactInterface::Plugin*>();
939 if ( !plugin ) {
940 return;
942 mSidePane->setCurrentPlugin( plugin->identifier() );
945 void MainWindow::selectPlugin( const QString &pluginName )
947 PluginList::ConstIterator end = mPlugins.constEnd();
948 for ( PluginList::ConstIterator it = mPlugins.constBegin(); it != end; ++it ) {
949 if ( ( *it )->identifier() == pluginName ) {
950 selectPlugin( *it );
951 return;
956 void MainWindow::loadSettings()
958 if ( mSplitter ) {
959 // if the preferences do not contain useful values, the side pane part of the splitter
960 // takes up the full width of the window, so leave the splitter sizing at the widget defaults
961 QList<int> sizes = Prefs::self()->sidePaneSplitter();
962 if ( sizes.count() == mSplitter->count() ) {
963 mSplitter->setSizes( sizes );
967 // Preload Plugins. This _must_ happen before the default part is loaded
968 PluginList::ConstIterator it;
969 for ( it = mDelayedPreload.constBegin(); it != mDelayedPreload.constEnd(); ++it ) {
970 selectPlugin( *it );
972 selectPlugin( Prefs::self()->mActivePlugin );
975 void MainWindow::saveSettings()
977 if ( mSplitter ) {
978 Prefs::self()->mSidePaneSplitter = mSplitter->sizes();
981 if ( mCurrentPlugin ) {
982 Prefs::self()->mActivePlugin = mCurrentPlugin->identifier();
986 void MainWindow::slotShowTip()
988 showTip( true );
991 void MainWindow::slotShowTipOnStart()
993 showTip( false );
996 void MainWindow::slotShowIntroduction()
998 mPartsStack->setCurrentIndex( 0 );
1001 void MainWindow::showTip( bool force )
1003 QStringList tips;
1004 PluginList::ConstIterator end = mPlugins.constEnd();
1005 for ( PluginList::ConstIterator it = mPlugins.constBegin(); it != end; ++it ) {
1006 QString file = (*it)->tipFile();
1007 if ( !file.isEmpty() ) {
1008 tips.append( file );
1012 KTipDialog::showMultiTip( this, tips, force );
1015 void MainWindow::slotQuit()
1017 mReallyClose = true;
1018 close();
1021 void MainWindow::slotPreferences()
1023 static KSettings::Dialog *dlg = 0;
1024 if ( !dlg ) {
1025 dlg = new KSettings::Dialog( this );
1026 dlg->setAllowComponentSelection( true );
1028 // do not show settings of components running standalone
1029 KPluginInfo::List filteredPlugins = mPluginInfos;
1030 PluginList::ConstIterator it;
1031 for ( it = mPlugins.constBegin(); it != mPlugins.constEnd(); ++it ) {
1032 if ( (*it)->isRunningStandalone() ) {
1033 KPluginInfo::List::ConstIterator infoIt;
1034 for ( infoIt = filteredPlugins.constBegin();
1035 infoIt != filteredPlugins.constEnd(); ++infoIt ) {
1036 if ( infoIt->pluginName() == (*it)->identifier() ) {
1037 filteredPlugins.removeAll( *infoIt );
1038 break;
1044 dlg->setHelp( "main-config", "kontact" );
1045 dlg->addPluginInfos( filteredPlugins );
1046 connect( dlg, SIGNAL(pluginSelectionChanged()), SLOT(pluginsChanged()) );
1049 dlg->show();
1052 void MainWindow::pluginsChanged()
1054 unplugActionList( "navigator_actionlist" );
1055 unloadPlugins();
1056 loadPlugins();
1057 mSidePane->updatePlugins();
1058 updateShortcuts();
1061 void MainWindow::updateConfig()
1063 kDebug();
1065 saveSettings();
1066 loadSettings();
1069 void MainWindow::showAboutDialog()
1071 QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
1073 if ( !mAboutDialog ) {
1074 mAboutDialog = new AboutDialog( this );
1077 mAboutDialog->show();
1078 mAboutDialog->raise();
1079 QApplication::restoreOverrideCursor();
1082 void MainWindow::configureShortcuts()
1084 KShortcutsDialog dialog(
1085 KShortcutsEditor::AllActions, KShortcutsEditor::LetterShortcutsAllowed, this );
1086 dialog.addCollection( actionCollection() );
1088 if ( mCurrentPlugin && mCurrentPlugin->part() ) {
1089 dialog.addCollection( mCurrentPlugin->part()->actionCollection() );
1092 dialog.configure();
1095 void MainWindow::configureToolbars()
1097 if ( mCurrentPlugin ) {
1098 saveMainWindowSettings( KGlobal::config()->group(
1099 QString( "MainWindow%1" ).arg( mCurrentPlugin->identifier() ) ) );
1101 KEditToolBar edit( factory() );
1102 connect( &edit, SIGNAL(newToolbarConfig()), this, SLOT(slotNewToolbarConfig()) );
1103 edit.exec();
1106 void MainWindow::slotNewToolbarConfig()
1108 if ( mCurrentPlugin && mCurrentPlugin->part() ) {
1109 removeInvisibleToolbarActions(mCurrentPlugin);
1110 createGUI( mCurrentPlugin->part() );
1112 if ( mCurrentPlugin ) {
1113 applyMainWindowSettings( KGlobal::config()->group(
1114 QString( "MainWindow%1" ).arg( mCurrentPlugin->identifier() ) ) );
1116 updateShortcuts(); // for the plugActionList call
1119 void MainWindow::slotOpenUrl( const KUrl &url )
1121 if ( url.protocol() == "exec" ) {
1122 if ( url.path() == "/switch" ) {
1123 if ( mCurrentPlugin ) {
1124 mPartsStack->setCurrentIndex( mPartsStack->indexOf( mCurrentPlugin->part()->widget() ) );
1127 if ( url.path() == "/gwwizard" ) {
1128 KRun::runCommand( "groupwarewizard", this );
1129 slotQuit();
1131 } else {
1132 new KRun( url, this );
1136 void MainWindow::readProperties( const KConfigGroup &config )
1138 Core::readProperties( config );
1140 QSet<QString> activePlugins =
1141 QSet<QString>::fromList( config.readEntry( "ActivePlugins", QStringList() ) );
1142 foreach ( Plugin *plugin, mPlugins ) {
1143 if ( !plugin->isRunningStandalone() && activePlugins.contains( plugin->identifier() ) ) {
1144 plugin->readProperties( config );
1149 void MainWindow::saveProperties( KConfigGroup &config )
1151 Core::saveProperties( config );
1153 QStringList activePlugins;
1155 foreach ( const KPluginInfo &pluginInfo, mPluginInfos ) {
1156 if ( pluginInfo.isPluginEnabled() ) {
1157 Plugin *plugin = pluginFromInfo( pluginInfo );
1158 if ( plugin ) {
1159 activePlugins.append( plugin->identifier() );
1160 plugin->saveProperties( config );
1165 config.writeEntry( "ActivePlugins", activePlugins );
1168 bool MainWindow::queryClose()
1170 if ( kapp->sessionSaving() || mReallyClose ) {
1171 return true;
1174 foreach ( Plugin *plugin, mPlugins ) {
1175 if ( !plugin->isRunningStandalone() ) {
1176 if ( !plugin->queryClose() ) {
1177 return false;
1181 return true;
1184 void MainWindow::slotShowStatusMsg( const QString &msg )
1186 if ( !statusBar() || !mStatusMsgLabel ) {
1187 return;
1190 mStatusMsgLabel->setText( msg );
1193 QString MainWindow::introductionString()
1195 KIconLoader *iconloader = KIconLoader::global();
1196 int iconSize = iconloader->currentSize( KIconLoader::Desktop );
1198 QString handbook_icon_path = iconloader->iconPath( "help-contents", KIconLoader::Desktop );
1199 QString html_icon_path = iconloader->iconPath( "text-html", KIconLoader::Desktop );
1200 QString wizard_icon_path = iconloader->iconPath( "tools-wizard", KIconLoader::Desktop );
1202 QString info = ki18n( "<h2 style='text-align:center; margin-top: 0px;'>Welcome to Kontact %1</h2>"
1203 "<p align=\"center\">%2</p>"
1204 "<table align=\"center\">"
1205 "<tr><td><a href=\"%3\"><img width=\"%4\" height=\"%5\" src=\"%6\" /></a></td>"
1206 "<td><a href=\"%7\">%8</a><br /><span id=\"subtext\"><nobr>%9</nobr></span></td></tr>"
1207 "<tr><td><a href=\"%10\"><img width=\"%11\" height=\"%12\" src=\"%13\" /></a></td>"
1208 "<td><a href=\"%14\">%15</a><br /><span id=\"subtext\"><nobr>%16</nobr></span></td></tr>"
1209 "<tr><td><a href=\"%17\"><img width=\"%18\" height=\"%19\" src=\"%20\" /></a></td>"
1210 "<td><a href=\"%21\">%22</a><br /><span id=\"subtext\"><nobr>%23</nobr></span></td></tr>"
1211 "</table>"
1212 "<p style=\"margin-bottom: 0px\"> <a href=\"%24\">Skip this introduction</a></p>" )
1213 .subs( KGlobal::mainComponent().aboutData()->version() )
1214 .subs( i18n( "Kontact handles your e-mail, address book, calendar, to-do list and more." ) )
1215 .subs( "help:/kontact" )
1216 .subs( iconSize )
1217 .subs( iconSize )
1218 .subs( handbook_icon_path )
1219 .subs( "help:/kontact" )
1220 .subs( i18n( "Read Manual" ) )
1221 .subs( i18n( "Learn more about Kontact and its components" ) )
1222 .subs( "http://kontact.org" )
1223 .subs( iconSize )
1224 .subs( iconSize )
1225 .subs( html_icon_path )
1226 .subs( "http://kontact.org" )
1227 .subs( i18n( "Visit Kontact Website" ) )
1228 .subs( i18n( "Access online resources and tutorials" ) )
1229 .subs( "exec:/gwwizard" )
1230 .subs( iconSize )
1231 .subs( iconSize )
1232 .subs( wizard_icon_path )
1233 .subs( "exec:/gwwizard" )
1234 .subs( i18n( "Configure Kontact as Groupware Client" ) )
1235 .subs( i18n( "Prepare Kontact for use in corporate networks" ) )
1236 .subs( "exec:/switch" )
1237 .toString();
1238 return info;
1241 #include "mainwindow.moc"