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"
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>
44 #include <kconfiggroup.h>
46 #include <kdbusservicestarter.h>
47 #include <kedittoolbar.h>
49 #include <khelpmenu.h>
50 #include <kiconloader.h>
51 #include <kshortcutsdialog.h>
52 #include <klibloader.h>
55 #include <kmessagebox.h>
56 #include <kplugininfo.h>
58 #include <kshortcut.h>
59 #include <kstandarddirs.h>
60 #include <kstatusbar.h>
61 #include <kstandardaction.h>
64 #include <kservicetypetrader.h>
65 #include <kstringhandler.h>
66 #include <ksqueezedtextlabel.h>
67 #include <khtml_part.h>
68 #include <khtmlview.h>
70 #include <kaboutdata.h>
72 #include <kstandardshortcut.h>
73 #include <ktoolinvocation.h>
74 #include <kactionmenu.h>
78 #include <kxmlguifactory.h>
82 #include <QDBusConnection>
87 #include <QPushButton>
89 #include <QStackedWidget>
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
103 virtual int startServiceFor( const QString
&serviceType
,
104 const QString
&constraint
= QString(),
105 QString
*error
= 0, QString
*dbusService
= 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
;
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
,
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
;
138 *dbusService
= (*it
)->registerClient();
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
);
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()
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 );
193 KToolBar
*navigatorToolBar
= findToolBar( "navigatorToolBar" );
194 Q_ASSERT( navigatorToolBar
);
195 if ( layoutDirection() == Qt::LeftToRight
) {
196 navigatorToolBar
->setLayoutDirection( Qt::RightToLeft
);
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
) {
217 // prepare the part manager
218 mPartManager
= new KParts::PartManager( this );
219 connect( mPartManager
, SIGNAL(activePartChanged(KParts::Part
*)),
220 this, SLOT(slotActivePartChanged(KParts::Part
*)) );
225 mSidePane
->updatePlugins();
228 KSettings::Dispatcher::registerComponent( componentData(), this, "updateConfig" );
234 QTimer::singleShot( 200, this, SLOT( slotShowTipOnStart() ) );
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() ) ) );
261 ServiceStarter::setPluginList( 0 );
264 QList
<KParts::Part
*> parts
= mPartManager
->parts();
266 // Q_FOREACH( KParts::Part *p, parts ) {
271 Prefs::self()->writeConfig();
273 // During deletion of plugins, we should not access the plugin list (bug #182176)
276 PluginList::ConstIterator end
= mPlugins
.constEnd();
277 for ( PluginList::ConstIterator it
= mPlugins
.constBegin(); it
!= end
; ++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
) ) {
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
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 );
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" ) );
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" ) );
385 content
.arg( QFont().pointSize() + 2 ).
386 arg( appName
).arg( catchPhrase
).arg( quickDescription
).arg( msg
) );
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()) );
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()));
426 mNewActions
= new KActionMenu(
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 );
441 mSyncActionsEnabled
= cfg
.readEntry( "GroupwareMailFoldersEnabled", false );
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() ) {
481 void MainWindow::loadPlugins()
483 QList
<Plugin
*> plugins
;
486 KPluginInfo::List::ConstIterator it
;
487 for ( it
= mPluginInfos
.constBegin(); it
!= mPluginInfos
.constEnd(); ++it
) {
488 if ( !it
->isPluginEnabled() ) {
493 if ( isPluginLoaded( *it
) ) {
494 plugin
= pluginFromInfo( *it
);
496 plugin
->configUpdated();
501 kDebug() << "Loading Plugin:" << it
->name();
502 plugin
= KService::createInstance
<KontactInterface::Plugin
>( it
->service(), this );
505 kDebug() << "Unable to create plugin for " << it
->name();
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() ) {
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
);
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() ) {
577 void MainWindow::updateShortcuts()
579 ActionPluginList::ConstIterator end
= mActionPlugins
.constEnd();
580 ActionPluginList::ConstIterator it
;
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
) );
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
) {
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
);
628 if ( mCurrentPlugin
== 0 ) {
629 PluginList::Iterator it
;
630 for ( it
= mPlugins
.begin(); it
!= mPlugins
.end(); ++it
) {
631 if ( (*it
)->showInSideBar() ) {
645 void MainWindow::addPlugin( KontactInterface::Plugin
*plugin
)
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
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
);
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
) );
678 void MainWindow::partLoaded( KontactInterface::Plugin
*plugin
, KParts::ReadOnlyPart
*part
)
682 // See if we have this part already (e.g. due to two plugins sharing it)
683 if ( mPartsStack
->indexOf( part
->widget() ) != -1 ) {
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
)
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();
712 PluginList::Iterator it
;
713 for ( it
= mPlugins
.begin(); it
!= mPlugins
.end(); ++it
) {
714 if ( !(*it
)->newActions().isEmpty() ) {
715 (*it
)->newActions().first()->trigger();
722 void MainWindow::slotSyncClicked()
724 if ( !mCurrentPlugin
->syncActions().isEmpty() ) {
725 mCurrentPlugin
->syncActions().first()->trigger();
727 PluginList::Iterator it
;
728 for ( it
= mPlugins
.begin(); it
!= mPlugins
.end(); ++it
) {
729 if ( !(*it
)->syncActions().isEmpty() ) {
730 (*it
)->syncActions().first()->trigger();
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
)
749 if ( plugin
->isRunningStandalone() ) {
750 statusBar()->showMessage( i18n( "Application is running standalone. Foregrounding..." ), 1000 );
751 plugin
->bringToForeground();
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();
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();
773 if ( mCurrentPlugin
) {
774 KAction
*action
= mPluginAction
[ mCurrentPlugin
];
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();
790 if ( parent
== mCurrentPlugin
->part()->widget() ) {
791 mFocusWidgets
.insert( mCurrentPlugin
->identifier(), QPointer
<QWidget
>( focusWidget
) );
793 parent
= parent
->parentWidget();
798 mSidePane
->setCurrentPlugin( plugin
->identifier() );
803 mPartManager
->setActivePart( part
);
804 QWidget
*view
= part
->widget();
808 mPartsStack
->setCurrentWidget( view
);
811 if ( mFocusWidgets
.contains( plugin
->identifier() ) ) {
812 focusWidget
= mFocusWidgets
[ plugin
->identifier() ];
814 focusWidget
->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() ) );
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();
849 static_cast<QAction
*>( mNewActions
)->setIcon( newAction
->icon() );
850 mNewActions
->setText( newAction
->text() );
851 mNewActions
->setWhatsThis( newAction
->whatsThis() );
857 if ( mSyncActionsEnabled
) {
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();
868 static_cast<QAction
*>( mSyncActions
)->setIcon( syncAction
->icon() );
869 mSyncActions
->setText( syncAction
->text() );
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
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
*>();
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
) {
956 void MainWindow::loadSettings()
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
) {
972 selectPlugin( Prefs::self()->mActivePlugin
);
975 void MainWindow::saveSettings()
978 Prefs::self()->mSidePaneSplitter
= mSplitter
->sizes();
981 if ( mCurrentPlugin
) {
982 Prefs::self()->mActivePlugin
= mCurrentPlugin
->identifier();
986 void MainWindow::slotShowTip()
991 void MainWindow::slotShowTipOnStart()
996 void MainWindow::slotShowIntroduction()
998 mPartsStack
->setCurrentIndex( 0 );
1001 void MainWindow::showTip( bool force
)
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;
1021 void MainWindow::slotPreferences()
1023 static KSettings::Dialog
*dlg
= 0;
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
);
1044 dlg
->setHelp( "main-config", "kontact" );
1045 dlg
->addPluginInfos( filteredPlugins
);
1046 connect( dlg
, SIGNAL(pluginSelectionChanged()), SLOT(pluginsChanged()) );
1052 void MainWindow::pluginsChanged()
1054 unplugActionList( "navigator_actionlist" );
1057 mSidePane
->updatePlugins();
1061 void MainWindow::updateConfig()
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() );
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()) );
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 );
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
);
1159 activePlugins
.append( plugin
->identifier() );
1160 plugin
->saveProperties( config
);
1165 config
.writeEntry( "ActivePlugins", activePlugins
);
1168 bool MainWindow::queryClose()
1170 if ( kapp
->sessionSaving() || mReallyClose
) {
1174 foreach ( Plugin
*plugin
, mPlugins
) {
1175 if ( !plugin
->isRunningStandalone() ) {
1176 if ( !plugin
->queryClose() ) {
1184 void MainWindow::slotShowStatusMsg( const QString
&msg
)
1186 if ( !statusBar() || !mStatusMsgLabel
) {
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>"
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" )
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" )
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" )
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" )
1241 #include "mainwindow.moc"