Fix no newlines warnings. Patch by Peter Oberndorfer
[kdevelopdvcssupport.git] / shell / runcontroller.cpp
blob47ceb1df79e2f71f27b9f23031e99499782611cf
1 /* This file is part of KDevelop
2 Copyright 2007-2008 Hamish Rodda <rodda@kde.org>
3 Copyright 2008 Aleix Pol <aleixpol@gmail.com>
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
21 #include "runcontroller.h"
23 #include <QApplication>
24 #include <QStandardItemModel>
25 #include <QPalette>
27 #include <KSelectAction>
28 #include <KActionMenu>
29 #include <KActionCollection>
30 #include <KMessageBox>
31 #include <KLocale>
32 #include <KDebug>
33 #include <KColorScheme>
34 #include <KCompositeJob>
35 #include <interfaces/iproject.h>
36 #include <interfaces/idocumentcontroller.h>
37 #include <outputview/ioutputview.h>
38 #include <project/projectmodel.h>
39 #include <project/interfaces/iprojectbuilder.h>
40 #include <project/interfaces/ibuildsystemmanager.h>
42 #include "core.h"
43 #include "plugincontroller.h"
44 #include "uicontroller.h"
45 #include "projectcontroller.h"
46 #include "mainwindow.h"
48 using namespace KDevelop;
50 typedef QPair<QString, IProject*> Target;
51 Q_DECLARE_METATYPE(Target)
53 class RunController::RunControllerPrivate
55 public:
56 QItemDelegate* delegate;
58 IRunController::State state;
60 QHash<KJob*, KAction*> jobs;
61 KActionMenu* stopAction;
62 KSelectAction* currentTargetAction;
65 RunController::RunController(QObject *parent)
66 : IRunController(parent)
67 , d(new RunControllerPrivate)
69 // TODO: need to implement compile only if needed before execute
70 // TODO: need to implement abort all running programs when project closed
72 d->state = Idle;
73 d->delegate = new RunDelegate(this);
75 if(!(Core::self()->setupFlags() & Core::NoUi)) setupActions();
78 class ExecuteCompositeJob : public KCompositeJob
80 public:
81 ExecuteCompositeJob(QObject* parent, const QList<KJob*>& jobs) : KCompositeJob(parent)
83 qDebug() << "execute composite" << jobs;
84 foreach(KJob* job, jobs) {
85 addSubjob(job);
89 public slots:
90 virtual void start()
92 if(hasSubjobs())
93 subjobs().first()->start();
94 else
95 emitResult();
98 void slotResult(KJob* job)
100 kDebug() << "finished: "<< job << job->error() << error();
101 KCompositeJob::slotResult(job);
103 if(hasSubjobs() && !error())
105 kDebug() << "remaining: " << subjobs().count() << subjobs();
106 KJob* nextJob=subjobs().first();
107 nextJob->start();
108 } else {
109 emitResult();
114 KJob* RunController::execute(const IRun & run)
116 if(!run.dependencies().isEmpty())
117 ICore::self()->documentController()->saveAllDocuments(IDocument::Silent);
119 QList<KJob*> jobs;
120 foreach(KJob* job, run.dependencies())
122 jobs.append(job);
125 jobs.append(new RunJob(this, run));
126 ExecuteCompositeJob* ecj=new ExecuteCompositeJob(this, jobs);
127 ecj->setObjectName(jobs.last()->objectName());
128 registerJob(ecj);
129 return ecj;
132 RunController::~ RunController()
134 delete d;
137 void RunController::setupActions()
139 KAction *action;
141 // TODO not multi-window friendly, FIXME
142 KActionCollection* ac = Core::self()->uiControllerInternal()->defaultMainWindow()->actionCollection();
144 action = new KAction (i18n("Configure Launches"), this);
145 ac->addAction("configure_launches", action);
147 action = new KAction( KIcon("system-run"), i18n("Execute Program"), this);
148 action->setShortcut(Qt::SHIFT + Qt::Key_F9);
149 action->setToolTip(i18n("Execute program"));
150 action->setWhatsThis(i18n("<b>Execute program</b><p>Executes the currently active target or the main program specified in project settings, <b>Run Options</b> tab."));
151 ac->addAction("run_execute", action);
152 connect(action, SIGNAL(triggered(bool)), this, SLOT(slotExecute()));
154 action = d->stopAction = new KActionMenu( KIcon("dialog-close"), i18n("Stop Jobs"), this);
155 action->setShortcut(Qt::Key_Escape);
156 action->setToolTip(i18n("Stop all currently running jobs"));
157 action->setWhatsThis(i18n("<b>Stop Jobs</b><p>Requests that all running jobs are stopped."));
158 action->setEnabled(false);
159 ac->addAction("run_stop", action);
160 connect(action, SIGNAL(triggered(bool)), this, SLOT(stopAllProcesses()));
162 d->currentTargetAction = new KSelectAction( i18n("Current Run Target"), this);
163 d->currentTargetAction->setToolTip(i18n("Current Run Target"));
164 d->currentTargetAction->setWhatsThis(i18n("<b>Run Target</b><p>Select which target to run when run is invoked."));
165 ac->addAction("run_default_target", d->currentTargetAction);
167 foreach (IProject* project, Core::self()->projectController()->projects()) {
168 slotProjectOpened(project);
171 if(!d->currentTargetAction->actions().isEmpty())
172 d->currentTargetAction->actions().first()->setChecked(true);
173 connect(Core::self()->projectController(), SIGNAL(projectOpened( KDevelop::IProject* )),
174 this, SLOT(slotProjectOpened(KDevelop::IProject*)));
175 connect(Core::self()->projectController(), SIGNAL(projectClosing( KDevelop::IProject* )),
176 this, SLOT(slotProjectClosing(KDevelop::IProject*)));
179 QAction* KDevelop::RunController::addTarget(KDevelop::IProject * project, const QString& targetName)
181 QAction* action = d->currentTargetAction->addAction(i18n("%1 : %2", project->name(), targetName));
182 action->setData(qVariantFromValue<Target>(Target(targetName, project)));
183 return action;
186 void KDevelop::RunController::slotProjectOpened(KDevelop::IProject * project)
188 KConfigGroup group(project->projectConfiguration(), "Run Options");
189 QStringList runTargets = group.readEntry("Run Targets", QStringList());
191 QAction* a=0;
192 foreach(const QString& target, runTargets) {
193 a=addTarget(project, target);
196 if(a)
197 a->setChecked(true);
200 void KDevelop::RunController::slotProjectClosing(KDevelop::IProject * project)
202 foreach (QAction* action, d->currentTargetAction->actions()) {
203 if (project == qvariant_cast<Target>(action->data()).second) {
204 bool wasSelected = action->isChecked();
205 delete action;
206 if (wasSelected)
207 if (!d->currentTargetAction->actions().isEmpty())
208 d->currentTargetAction->actions().first()->setChecked(true);
213 void KDevelop::RunController::slotConfigurationChanged()
215 kDebug() << "updating runcontroller configuration";
216 //if we could check what project changed we wouldn't need to regenerate everything
217 foreach (QAction* action, d->currentTargetAction->actions()) {
218 delete action;
221 foreach (IProject* project, Core::self()->projectController()->projects()) {
222 slotProjectOpened(project);
226 void RunController::slotExecute()
228 execute(defaultRun());
231 QStringList splitArguments(const QString& args)
233 QStringList ret;
234 bool inQuotes=false, scaping=false;
235 for(int i=0; i<args.size(); i++)
237 if(i==0) ret += QString();
239 if(scaping)
241 ret.last() += args[i];
242 scaping=false;
244 else switch(args[i].toAscii())
246 case '\\':
247 scaping=true;
248 break;
249 case '\"':
250 inQuotes=!inQuotes;
251 break;
252 case ' ':
253 if(inQuotes)
254 ret.last() += args[i];
255 else
256 ret += QString();
258 break;
259 default:
260 ret.last() += args[i];
261 break;
265 return ret;
268 //Copied from projectitemlineedit.cpp
269 QModelIndex pathToIndex(const QAbstractItemModel* model, const QStringList& tofetch)
271 if(tofetch.isEmpty())
272 return QModelIndex();
274 QModelIndex current=model->index(0,0, QModelIndex());
276 foreach(const QString& currentName, tofetch)
278 QModelIndexList l = model->match(current, Qt::EditRole, currentName, 1, Qt::MatchExactly);
280 if(l.count()>0)
281 current = model->index(0,0, l.first());
282 else
283 current = QModelIndex();
285 return current;
288 IRun KDevelop::RunController::defaultRun() const
290 IProject* project = 0;
291 IRun run;
293 QAction* projectAction = d->currentTargetAction->currentAction();
295 Target data;
296 if (projectAction) {
297 data=qvariant_cast<Target>(projectAction->data());
298 project = data.second;
301 if (!project)
302 return run;
304 QString targetName=data.first;
306 KConfigGroup group(project->projectConfiguration(), targetName+"-Run Options" );
308 QString exec=group.readEntry("Executable", QString());
309 ProjectModel *model=ICore::self()->projectController()->projectModel();
310 if(exec.isEmpty())
312 QString target=group.readEntry("Run Item", QString());
313 QModelIndex idx=pathToIndex(model, target.split('/'));
314 ProjectBaseItem *it=model->item(idx);
315 if(it->executable())
316 exec=it->executable()->builtUrl().toLocalFile();
318 //FIXME: throw error
319 run.setExecutable(exec);
320 run.setWorkingDirectory(group.readEntry("Working Directory", QString()));
321 run.setArguments(splitArguments(group.readEntry("Arguments", QString())));
322 if (group.readEntry("Start In Terminal", false))
323 // TODO: start in terminal rather than output view
324 #warning Implement a Konsole instrumentor
325 run.setInstrumentor("konsole");
326 else
327 run.setInstrumentor("default");
329 QStringList compileItems=group.readEntry("Compile Items", QStringList());
330 int actionDeps=group.readEntry("BeforeExecute", 1);
332 QList<KJob*> comp;
333 if(actionDeps!=0)
335 foreach(const QString& it, compileItems)
337 QModelIndex idx=pathToIndex(model, it.split('/'));
338 ProjectBaseItem *it=model->item(idx);
340 IProject* project = it->project();
341 if (!project)
342 continue;
344 IPlugin* fmgr = project->managerPlugin();
345 IBuildSystemManager* mgr = fmgr->extension<IBuildSystemManager>();
346 IProjectBuilder* builder;
347 if( mgr )
349 builder=mgr->builder( project->projectItem() );
350 KJob* buildJob;
351 switch(actionDeps)
353 case 1:
354 buildJob=builder->build(it);
355 break;
356 case 2:
357 buildJob=builder->install(it);
358 break;
359 case 3:
360 #warning make it install as superuser.
361 break;
363 comp+=buildJob;
365 else
367 kDebug() << "Failed to compile";
370 run.setDependencies(comp);
373 return run;
376 IRunProvider * KDevelop::RunController::findProvider(const QString & instrumentor)
378 foreach (IPlugin* i, Core::self()->pluginController()->allPluginsForExtension("org.kdevelop.IRunProvider", QStringList())) {
379 KDevelop::IRunProvider* provider = i->extension<KDevelop::IRunProvider>();
380 if (provider && provider->instrumentorsProvided().contains(instrumentor))
381 return provider;
384 return 0;
387 void KDevelop::RunController::registerJob(KJob * job)
389 if (!job)
390 return;
392 if (!d->jobs.contains(job)) {
393 KAction* stopJobAction = new KAction(job->objectName().isEmpty() ? i18n("Unnamed job") : job->objectName(), this);
394 stopJobAction->setData(QVariant::fromValue(static_cast<void*>(job)));
395 d->stopAction->addAction(stopJobAction);
396 connect (stopJobAction, SIGNAL(triggered(bool)), SLOT(slotKillJob()));
398 d->jobs.insert(job, stopJobAction);
400 IRunController::registerJob(job);
402 emit jobRegistered(job);
405 job->start();
407 checkState();
410 void KDevelop::RunController::unregisterJob(KJob * job)
412 IRunController::unregisterJob(job);
414 Q_ASSERT(d->jobs.contains(job));
416 // Delete the stop job action
417 delete d->jobs.take(job);
419 checkState();
421 emit jobUnregistered(job);
424 void KDevelop::RunController::checkState()
426 bool running = false;
428 foreach (KJob* job, d->jobs.keys()) {
429 if (!job->isSuspended()) {
430 running = true;
431 break;
435 if (d->state != running ? Running : Idle) {
436 d->state = running ? Running : Idle;
437 emit runStateChanged(d->state);
440 d->stopAction->setEnabled(running);
443 void KDevelop::RunController::stopAllProcesses()
445 foreach (KJob* job, d->jobs.keys()) {
446 if (job->capabilities() & KJob::Killable)
447 job->kill(KJob::EmitResult);
451 void KDevelop::RunController::slotKillJob()
453 KAction* action = dynamic_cast<KAction*>(sender());
454 Q_ASSERT(action);
456 KJob* job = static_cast<KJob*>(qvariant_cast<void*>(action->data()));
457 if (job->capabilities() & KJob::Killable)
458 job->kill();
461 void KDevelop::RunController::finished(KJob * job)
463 unregisterJob(job);
465 switch (job->error()) {
466 case KJob::NoError:
467 case KJob::KilledJobError:
468 break;
470 default:
471 KMessageBox::error(qApp->activeWindow(), job->errorString(), i18n("Process Error"));
475 void KDevelop::RunController::suspended(KJob * job)
477 Q_UNUSED(job);
479 checkState();
482 void KDevelop::RunController::resumed(KJob * job)
484 Q_UNUSED(job);
486 checkState();
489 KDevelop::RunJob::RunJob(RunController* controller, const IRun & run)
490 : m_controller(controller)
491 , m_provider(0)
492 , m_run(run)
494 setCapabilities(Killable);
496 QString instrumentorName = i18n("Run");
497 if (!m_run.instrumentor().isEmpty()) {
498 m_provider = m_controller->findProvider(m_run.instrumentor());
499 if (m_provider) {
500 instrumentorName = m_provider->translatedInstrumentor(run.instrumentor());
503 setObjectName(i18n("%1: %2", instrumentorName, run.executable().path()));
506 void KDevelop::RunJob::start()
508 if (m_run.instrumentor().isEmpty()) {
509 setErrorText(i18n("No run target was selected. Please select a run target in the Run menu."));
510 setError(ErrorInvalidTarget);
511 emitResult();
512 return;
515 if (!m_provider) {
516 setErrorText(i18n("Execution failed: no plugin found for requested instrumentor \"%1\"", m_run.instrumentor()));
517 setError(ErrorNoProvider);
518 emitResult();
519 return;
522 QObject* m_providerObject = dynamic_cast<QObject*>(m_provider);
523 Q_ASSERT(m_providerObject);
525 connect(m_providerObject, SIGNAL(finished(KJob*)), this, SLOT(slotFinished(KJob*)));
526 connect(m_providerObject, SIGNAL(output(KJob*, const QString&, KDevelop::IRunProvider::OutputTypes)), this, SLOT(slotOutput(KJob*, const QString&, KDevelop::IRunProvider::OutputTypes)));
528 m_provider->execute(m_run, this);
530 setStandardToolView(IOutputView::RunView);
531 setDelegate(m_controller->delegate());
532 setTitle(m_run.executable().path());
533 setBehaviours( KDevelop::IOutputView::AllowUserClose | KDevelop::IOutputView::AutoScroll );
534 startOutput();
537 QList< KJob * > KDevelop::RunController::currentJobs() const
539 return d->jobs.keys();
542 void RunJob::slotOutput(KJob * job, const QString & line, KDevelop::IRunProvider::OutputTypes type)
544 if (job != this)
545 return;
547 if (!model())
548 return;
550 if( model()->columnCount() == 0 )
552 model()->insertColumns( 0, 1 );
555 int rowCount = model()->rowCount();
556 model()->insertRows( rowCount, 1 );
557 QModelIndex row_idx = model()->index( rowCount, 0 );
558 model()->setData( row_idx, QVariant( line ) );
559 model()->setData( row_idx, QVariant::fromValue(type), Qt::UserRole+1 );
562 void KDevelop::RunJob::slotFinished(KJob * job)
564 if (job == this)
565 emitResult();
568 bool KDevelop::RunJob::doKill()
570 m_provider->abort(this);
572 return true;
575 QItemDelegate * KDevelop::RunController::delegate() const
577 return d->delegate;
580 RunDelegate::RunDelegate( QObject* parent )
581 : QItemDelegate(parent), runProviderBrush( KColorScheme::View, KColorScheme::PositiveText ),
582 errorBrush( KColorScheme::View, KColorScheme::NegativeText )
586 void RunDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const
588 QStyleOptionViewItem opt = option;
589 QVariant status = index.data(Qt::UserRole+1);
590 if( status.isValid() && status.canConvert<KDevelop::IRunProvider::OutputTypes>() )
592 IRunProvider::OutputTypes type = status.value<KDevelop::IRunProvider::OutputTypes>();
593 if( type == IRunProvider::RunProvider )
595 opt.palette.setBrush( QPalette::Text, runProviderBrush.brush( option.palette ) );
596 } else if( type == IRunProvider::StandardError )
598 opt.palette.setBrush( QPalette::Text, errorBrush.brush( option.palette ) );
601 QItemDelegate::paint(painter, opt, index);
605 #include "runcontroller.moc"