uniqueapphandler.cpp
1/*
2 This file is part of KDE Kontact.
3
4 Copyright (c) 2003 David Faure <faure@kde.org>
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301, USA.
20*/
21
22#include "uniqueapphandler.h"
23#include <tdestartupinfo.h>
24#include <tdeapplication.h>
25#include <tdecmdlineargs.h>
26#include "core.h"
27#include <twin.h>
28#include <dcopclient.h>
29#include <kdebug.h>
30#include <tdelocale.h>
31#include <tdeuniqueapplication.h>
32
33/*
34 Test plan for the various cases of interaction between standalone apps and kontact:
35
36 1) start kontact, select "Mail".
37 1a) type "korganizer" -> it switches to korganizer
38 1b) type "kmail" -> it switches to kmail
39 1c) type "kaddressbook" -> it switches to kaddressbook
40 1d) type "kmail foo@kde.org" -> it opens a kmail composer, without switching
41 1e) type "knode" -> it switches to knode
42 1f) type "kaddressbook --new-contact" -> it opens a kaddressbook contact window
43 1g) type "knode news://foobar/group" -> it pops up "can't resolve hostname"
44
45 2) close kontact. Launch kmail. Launch kontact again.
46 2a) click "Mail" icon -> kontact doesn't load a part, but activates the kmail window
47 2b) type "kmail foo@kde.org" -> standalone kmail opens composer.
48 2c) close kmail, click "Mail" icon -> kontact loads the kmail part.
49 2d) type "kmail" -> kontact is brought to front
50
51 3) close kontact. Launch korganizer, then kontact.
52 3a) both Todo and Calendar activate the running korganizer.
53 3b) type "korganizer" -> standalone korganizer is brought to front
54 3c) close korganizer, click Calendar or Todo -> kontact loads part.
55 3d) type "korganizer" -> kontact is brought to front
56
57 4) close kontact. Launch kaddressbook, then kontact.
58 4a) "Contacts" icon activate the running kaddressbook.
59 4b) type "kaddressbook" -> standalone kaddressbook is brought to front
60 4c) close kaddressbook, type "kaddressbook -a foo@kde.org" -> kontact loads part and opens editor
61 4d) type "kaddressbook" -> kontact is brought to front
62
63 5) close kontact. Launch knode, then kontact.
64 5a) "News" icon activate the running knode.
65 5b) type "knode" -> standalone knode is brought to front
66 5c) close knode, type "knode news://foobar/group" -> kontact loads knode and pops up msgbox
67 5d) type "knode" -> kontact is brought to front
68
69 6) start "kontact --module summaryplugin"
70 6a) type "dcop kmail kmail newInstance" -> kontact switches to kmail (#103775)
71 6b) type "kmail" -> kontact is brought to front
72 6c) type "kontact" -> kontact is brought to front
73 6d) type "kontact --module summaryplugin" -> kontact switches to summary
74
75*/
76
77using namespace Kontact;
78
79int UniqueAppHandler::newInstance()
80{
81 // This bit is duplicated from TDEUniqueApplication::newInstance()
82 if ( tdeApp->mainWidget() ) {
83 tdeApp->mainWidget()->show();
84 KWin::forceActiveWindow( tdeApp->mainWidget()->winId() );
85 TDEStartupInfo::appStarted();
86 }
87
88 // Then ensure the part appears in kontact
89 mPlugin->core()->selectPlugin( mPlugin );
90 return 0;
91}
92
93bool UniqueAppHandler::process( const TQCString &fun, const TQByteArray &data,
94 TQCString& replyType, TQByteArray &replyData )
95{
96 if ( fun == "newInstance()" ) {
97 replyType = "int";
98
99 TDECmdLineArgs::reset(); // forget options defined by other "applications"
100 loadCommandLineOptions(); // implemented by plugin
101
102 // This bit is duplicated from TDEUniqueApplication::processDelayed()
103 TQDataStream ds( data, IO_ReadOnly );
104 TDECmdLineArgs::loadAppArgs( ds );
105 if ( !ds.atEnd() ) { // backwards compatibility
106 TQCString asn_id;
107 ds >> asn_id;
108 tdeApp->setStartupId( asn_id );
109 }
110
111 TQDataStream _replyStream( replyData, IO_WriteOnly );
112 _replyStream << newInstance();
113
114 // OK, we're done, reload the initial kontact command line options,
115 // so that "kontact --module foo" keeps working (#103775).
116
117 TDECmdLineArgs::reset(); // forget options defined above
119
120 } else if ( fun == "load()" ) {
121 replyType = "bool";
122 (void)mPlugin->part(); // load the part without bringing it to front
123
124 TQDataStream _replyStream( replyData, IO_WriteOnly );
125 _replyStream << true;
126 } else {
127 return DCOPObject::process( fun, data, replyType, replyData );
128 }
129 return true;
130}
131
132QCStringList UniqueAppHandler::interfaces()
133{
134 QCStringList ifaces = DCOPObject::interfaces();
135 ifaces += "Kontact::UniqueAppHandler";
136 return ifaces;
137}
138
139QCStringList UniqueAppHandler::functions()
140{
141 QCStringList funcs = DCOPObject::functions();
142 funcs << "int newInstance()";
143 funcs << "bool load()";
144 return funcs;
145}
146
148 : TQObject( plugin ), mFactory( factory ), mPlugin( plugin )
149{
150 // The app is running standalone if 1) that name is known to DCOP
151 mRunningStandalone = tdeApp->dcopClient()->isApplicationRegistered( plugin->name() );
152
153 // and 2) it's not registered by kontact (e.g. in another plugin)
154 if ( mRunningStandalone && tdeApp->dcopClient()->findLocalClient( plugin->name() ) )
155 mRunningStandalone = false;
156
157 if ( mRunningStandalone ) {
158 tdeApp->dcopClient()->setNotifications( true );
159 connect( tdeApp->dcopClient(), TQ_SIGNAL( applicationRemoved( const TQCString& ) ),
160 this, TQ_SLOT( unregisteredFromDCOP( const TQCString& ) ) );
161 } else {
162 mFactory->createHandler( mPlugin );
163 }
164}
165
166UniqueAppWatcher::~UniqueAppWatcher()
167{
168 if ( mRunningStandalone )
169 tdeApp->dcopClient()->setNotifications( false );
170
171 delete mFactory;
172}
173
174void UniqueAppWatcher::unregisteredFromDCOP( const TQCString& appId )
175{
176 if ( appId == mPlugin->name() && mRunningStandalone ) {
177 disconnect( tdeApp->dcopClient(), TQ_SIGNAL( applicationRemoved( const TQCString& ) ),
178 this, TQ_SLOT( unregisteredFromDCOP( const TQCString& ) ) );
179 kdDebug(5601) << k_funcinfo << appId << endl;
180 mFactory->createHandler( mPlugin );
181 tdeApp->dcopClient()->setNotifications( false );
182 mRunningStandalone = false;
183 }
184}
185
186static TDECmdLineOptions options[] =
187{
188 { "module <module>", I18N_NOOP( "Start with a specific Kontact module" ), 0 },
189 { "iconify", I18N_NOOP( "Start in iconified (minimized) mode" ), 0 },
190 { "list", I18N_NOOP( "List all possible modules and exit" ), 0 },
191 { "listprofiles", I18N_NOOP( "List all possible profiles and exit" ), 0 },
192 { "profile <profile>", I18N_NOOP( "Start with a specific Kontact profile" ), 0 },
193 TDECmdLineLastOption
194};
195
197{
198 TDECmdLineArgs::addCmdLineOptions( options );
199 TDEUniqueApplication::addCmdLineOptions();
200 TDEApplication::addCmdLineOptions();
201}
202
203#include "uniqueapphandler.moc"
virtual void selectPlugin(Kontact::Plugin *plugin)=0
Selects the given plugin.
Base class for all Plugins in Kontact.
Definition: plugin.h:59
KParts::ReadOnlyPart * part()
You can use this method if you need to access the current part.
Definition: plugin.cpp:145
Base class for UniqueAppHandler.
virtual void loadCommandLineOptions()=0
This must be reimplemented so that app-specific command line options can be parsed.
static void loadKontactCommandLineOptions()
Load the kontact command line options.
virtual int newInstance()
We can't use k_dcop and dcopidl here, because the data passed to newInstance can't be expressed in te...
UniqueAppWatcher(UniqueAppHandlerFactoryBase *factory, Plugin *plugin)
Create an instance of UniqueAppWatcher, which does everything necessary for the "unique application" ...