1/*
2 * Copyright (C) 2006 Aaron Seigo <aseigo@kde.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Library General Public License version 2 as
6 * published by the Free Software Foundation
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details
12 *
13 * You should have received a copy of the GNU Library General Public
14 * License along with this program; if not, write to the
15 * Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19#include "krunnerapp.h"
20
21#include <stdio.h>
22#include <stdlib.h>
23#include <unistd.h>
24
25#include <QClipboard>
26#include <QObject>
27#include <QtDBus/QtDBus>
28
29#include <KAction>
30#include <KActionCollection>
31#include <KCrash>
32#include <KAuthorized>
33#include <KLocale>
34#include <KWindowSystem>
35
36#include <Plasma/RunnerManager>
37#include <Plasma/AbstractRunner>
38
39#include "kworkspace/kdisplaymanager.h"
40
41#include "appadaptor.h"
42#include "ksystemactivitydialog.h"
43#include "interfaces/default/interface.h"
44#include "interfaces/quicksand/qs_dialog.h"
45#ifdef Q_WS_X11
46#include "startupid.h"
47#endif
48#include "klaunchsettings.h"
49#include "krunnersettings.h"
50
51#ifdef Q_WS_X11
52#include <X11/extensions/Xrender.h>
53#endif
54
55KRunnerApp* KRunnerApp::self()
56{
57 if (!kapp) {
58 return new KRunnerApp();
59 }
60
61 return qobject_cast<KRunnerApp*>(kapp);
62}
63
64KRunnerApp::KRunnerApp()
65 : KUniqueApplication(),
66 m_interface(0),
67 m_tasks(0),
68 m_startupId(NULL),
69 m_firstTime(true)
70{
71 initialize();
72 connect(this, SIGNAL(aboutToQuit()), this, SLOT(cleanUp()));
73}
74
75KRunnerApp::~KRunnerApp()
76{
77}
78
79void KRunnerApp::cleanUp()
80{
81 disconnect(KRunnerSettings::self(), SIGNAL(configChanged()), this, SLOT(reloadConfig()));
82 kDebug() << "deleting interface";
83 delete m_interface;
84 m_interface = 0;
85 delete m_runnerManager;
86 m_runnerManager = 0;
87#ifndef Q_WS_WIN
88 delete m_tasks;
89 m_tasks = 0;
90#endif
91 KGlobal::config()->sync();
92}
93
94KActionCollection* KRunnerApp::actionCollection()
95{
96 return m_actionCollection;
97}
98
99void KRunnerApp::initialize()
100{
101 setWindowIcon(KIcon(QLatin1String("system-run")));
102
103 setQuitOnLastWindowClosed(false);
104 KCrash::setFlags(KCrash::AutoRestart);
105 initializeStartupNotification();
106
107 connect(KRunnerSettings::self(), SIGNAL(configChanged()), this, SLOT(reloadConfig()));
108
109 m_runnerManager = new Plasma::RunnerManager;
110
111 new AppAdaptor(this);
112 QDBusConnection::sessionBus().registerObject(QLatin1String("/App"), this);
113
114 // Global keys
115 m_actionCollection = new KActionCollection(this);
116 KAction* a = 0;
117
118 if (KAuthorized::authorize(QLatin1String("run_command"))) {
119 a = m_actionCollection->addAction(QLatin1String("Run Command"));
120 a->setText(i18n("Run Command"));
121 a->setGlobalShortcut(KShortcut(Qt::ALT+Qt::Key_F2));
122 connect(a, SIGNAL(triggered(bool)), SLOT(displayOrHide()));
123
124 a = m_actionCollection->addAction(QLatin1String("Run Command on clipboard contents"));
125 a->setText(i18n("Run Command on clipboard contents"));
126 a->setGlobalShortcut(KShortcut(Qt::ALT+Qt::SHIFT+Qt::Key_F2));
127 connect(a, SIGNAL(triggered(bool)), SLOT(displayWithClipboardContents()));
128 }
129
130 a = m_actionCollection->addAction(QLatin1String("Show System Activity"));
131 a->setText(i18n("Show System Activity"));
132 a->setGlobalShortcut(KShortcut(Qt::CTRL+Qt::Key_Escape));
133 connect(a, SIGNAL(triggered(bool)), SLOT(showTaskManager()));
134
135 if (KAuthorized::authorize(QLatin1String("switch_user"))) {
136 a = m_actionCollection->addAction(QLatin1String("Switch User"));
137 a->setText(i18n("Switch User"));
138 a->setGlobalShortcut(KShortcut(Qt::ALT+Qt::CTRL+Qt::Key_Insert));
139 connect(a, SIGNAL(triggered(bool)), SLOT(switchUser()));
140 }
141
142 //Setup the interface after we have set up the actions
143 //TODO: if !KAuthorized::authorize("run_comand") (and !"switch_user" i suppose?)
144 // then we probably don't need the interface at all. would be another place
145 // for some small improvements in footprint in that case
146 switch (KRunnerSettings::interface()) {
147 default:
148 case KRunnerSettings::EnumInterface::CommandOriented:
149 m_interface = new Interface(m_runnerManager);
150 break;
151 case KRunnerSettings::EnumInterface::TaskOriented:
152 m_interface = new QsDialog(m_runnerManager);
153 break;
154 }
155
156#ifdef Q_WS_X11
157 //FIXME: if argb visuals enabled Qt will always set WM_CLASS as "qt-subapplication" no matter what
158 //the application name is we set the proper XClassHint here, hopefully won't be necessary anymore when
159 //qapplication will manage apps with argvisuals in a better way
160 XClassHint classHint;
161 classHint.res_name = const_cast<char*>("krunner");
162 classHint.res_class = const_cast<char*>("krunner");
163 XSetClassHint(QX11Info::display(), m_interface->winId(), &classHint);
164#endif
165
166
167 m_actionCollection->readSettings();
168 if (KAuthorized::authorize(QLatin1String("run_command"))) {
169 //m_runnerManager->setAllowedRunners(QStringList() << "shell");
170 m_runnerManager->reloadConfiguration(); // pre-load the runners
171
172 // Single runner mode actions shortcuts
173
174 foreach (const QString &runnerId, m_runnerManager->singleModeAdvertisedRunnerIds()) {
175 a = m_actionCollection->addAction(runnerId);
176 a->setText(i18nc("Run krunner restricting the search only to runner %1", "Run Command (runner \"%1\" only)",
177 m_runnerManager->runnerName(runnerId)));
178 a->setGlobalShortcut(KShortcut());
179 connect(a, SIGNAL(triggered(bool)), SLOT(singleRunnerModeActionTriggered()));
180 }
181 }
182}
183
184void KRunnerApp::singleRunnerModeActionTriggered()
185{
186 KAction * action = qobject_cast<KAction*>(sender());
187 if (action) {
188 displaySingleRunner(action->objectName());
189 }
190}
191
192void KRunnerApp::querySingleRunner(const QString& runnerId, const QString &term)
193{
194 if (!KAuthorized::authorize(QLatin1String("run_command"))) {
195 return;
196 }
197
198 m_runnerManager->setSingleModeRunnerId(runnerId);
199 m_runnerManager->setSingleMode(!runnerId.isEmpty());
200
201 if (m_runnerManager->singleMode()) {
202 m_interface->display(term);
203 }
204}
205
206QStringList KRunnerApp::singleModeAdvertisedRunnerIds() const
207{
208 return m_runnerManager->singleModeAdvertisedRunnerIds();
209}
210
211void KRunnerApp::initializeStartupNotification()
212{
213 // Startup notification
214 KLaunchSettings::self()->readConfig();
215#ifdef Q_WS_X11
216 if (!KLaunchSettings::busyCursor()) {
217 delete m_startupId;
218 m_startupId = NULL;
219 } else {
220 if (m_startupId == NULL ) {
221 m_startupId = new StartupId;
222 }
223
224 m_startupId->configure();
225 }
226#endif
227}
228
229void KRunnerApp::showTaskManager()
230{
231 showTaskManagerWithFilter(QString());
232}
233
234void KRunnerApp::showTaskManagerWithFilter(const QString &filterText)
235{
236#ifndef Q_WS_WIN
237 //kDebug(1204) << "Launching KSysGuard...";
238 if (!m_tasks) {
239 m_tasks = new KSystemActivityDialog;
240 connect(m_tasks, SIGNAL(finished()),
241 this, SLOT(taskDialogFinished()));
242 } else if ((filterText.isEmpty() || m_tasks->filterText() == filterText) &&
243 KWindowSystem::activeWindow() == m_tasks->winId()) {
244 m_tasks->hide();
245 return;
246 }
247
248 m_tasks->run();
249 m_tasks->setFilterText(filterText);
250#endif
251}
252
253void KRunnerApp::display()
254{
255 if (!KAuthorized::authorize(QLatin1String("run_command"))) {
256 return;
257 }
258
259 m_runnerManager->setSingleMode(false);
260 m_interface->display();
261}
262
263void KRunnerApp::displaySingleRunner(const QString &runnerId)
264{
265 if (!KAuthorized::authorize(QLatin1String("run_command"))) {
266 return;
267 }
268
269 m_runnerManager->setSingleModeRunnerId(runnerId);
270 m_runnerManager->setSingleMode(!runnerId.isEmpty());
271 m_interface->display();
272}
273
274void KRunnerApp::displayOrHide()
275{
276 if (!KAuthorized::authorize(QLatin1String("run_command"))) {
277 m_interface->hide();
278 return;
279 }
280
281 if (!m_interface->isVisible()) {
282 m_runnerManager->setSingleMode(false);
283 }
284
285 if (m_interface->freeFloating()) {
286 if (m_interface->isVisible()) {
287 m_interface->hide();
288 } else {
289 m_interface->display();
290 }
291 } else if (m_interface->isActiveWindow()) {
292 m_interface->hide();
293 } else {
294 m_interface->display();
295 }
296}
297
298void KRunnerApp::query(const QString &term)
299{
300 if (!KAuthorized::authorize(QLatin1String("run_command"))) {
301 return;
302 }
303
304 m_interface->display(term);
305}
306
307void KRunnerApp::displayWithClipboardContents()
308{
309 if (!KAuthorized::authorize(QLatin1String("run_command"))) {
310 return;
311 }
312
313 QString clipboardData = QApplication::clipboard()->text(QClipboard::Selection);
314 m_interface->display(clipboardData);
315}
316
317void KRunnerApp::switchUser()
318{
319 const KService::Ptr service = KService::serviceByStorageId(QLatin1String("plasma-runner-sessions.desktop"));
320 KPluginInfo info(service);
321
322 if (info.isValid()) {
323 SessList sessions;
324 KDisplayManager dm;
325 dm.localSessions(sessions);
326
327 if (sessions.isEmpty()) {
328 // no sessions to switch between, let's just start up another session directly
329 Plasma::AbstractRunner *sessionRunner = m_runnerManager->runner(info.pluginName());
330 if (sessionRunner) {
331 Plasma::QueryMatch switcher(sessionRunner);
332 sessionRunner->run(*m_runnerManager->searchContext(), switcher);
333 }
334 } else {
335 m_runnerManager->setSingleModeRunnerId(info.pluginName());
336 m_runnerManager->setSingleMode(true);
337 m_interface->display();
338 //TODO: ugh, magic strings. See sessions/sessionrunner.cpp
339 m_runnerManager->launchQuery(QLatin1String("SESSIONS"), info.pluginName());
340 }
341 }
342}
343
344void KRunnerApp::clearHistory()
345{
346 m_interface->clearHistory();
347}
348
349void KRunnerApp::taskDialogFinished()
350{
351#ifndef Q_WS_WIN
352 m_tasks->deleteLater();
353 m_tasks = 0;
354#endif
355}
356
357int KRunnerApp::newInstance()
358{
359 if (m_firstTime) {
360 m_firstTime = false;
361 } else {
362 display();
363 }
364
365 return KUniqueApplication::newInstance();
366 //return 0;
367}
368
369void KRunnerApp::reloadConfig()
370{
371 //Prevent Interface destructor from triggering this method
372 disconnect(KRunnerSettings::self(), SIGNAL(configChanged()), this, SLOT(reloadConfig()));
373
374 const int interface = KRunnerSettings::interface();
375 if (!qobject_cast<QsDialog*>(m_interface) &&
376 interface == KRunnerSettings::EnumInterface::TaskOriented) {
377 m_interface->deleteLater();
378 m_interface = new QsDialog(m_runnerManager);
379 } else if (!qobject_cast<Interface*>(m_interface) &&
380 interface == KRunnerSettings::EnumInterface::CommandOriented) {
381 m_interface->deleteLater();
382 m_interface = new Interface(m_runnerManager);
383 }
384
385 m_interface->setFreeFloating(KRunnerSettings::freeFloating());
386 connect(KRunnerSettings::self(), SIGNAL(configChanged()), this, SLOT(reloadConfig()));
387 display();
388}
389
390#include "krunnerapp.moc"
391