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 
77 using namespace Kontact;
78 
80 {
81  // This bit is duplicated from TDEUniqueApplication::newInstance()
82  if ( kapp->mainWidget() ) {
83  kapp->mainWidget()->show();
84  KWin::forceActiveWindow( kapp->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 
93 bool 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  kapp->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 
132 QCStringList UniqueAppHandler::interfaces()
133 {
134  QCStringList ifaces = DCOPObject::interfaces();
135  ifaces += "Kontact::UniqueAppHandler";
136  return ifaces;
137 }
138 
139 QCStringList 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 = kapp->dcopClient()->isApplicationRegistered( plugin->name() );
152 
153  // and 2) it's not registered by kontact (e.g. in another plugin)
154  if ( mRunningStandalone && kapp->dcopClient()->findLocalClient( plugin->name() ) )
155  mRunningStandalone = false;
156 
157  if ( mRunningStandalone ) {
158  kapp->dcopClient()->setNotifications( true );
159  connect( kapp->dcopClient(), TQ_SIGNAL( applicationRemoved( const TQCString& ) ),
160  this, TQ_SLOT( unregisteredFromDCOP( const TQCString& ) ) );
161  } else {
162  mFactory->createHandler( mPlugin );
163  }
164 }
165 
166 UniqueAppWatcher::~UniqueAppWatcher()
167 {
168  if ( mRunningStandalone )
169  kapp->dcopClient()->setNotifications( false );
170 
171  delete mFactory;
172 }
173 
174 void UniqueAppWatcher::unregisteredFromDCOP( const TQCString& appId )
175 {
176  if ( appId == mPlugin->name() && mRunningStandalone ) {
177  disconnect( kapp->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  kapp->dcopClient()->setNotifications( false );
182  mRunningStandalone = false;
183  }
184 }
185 
186 static 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" ...