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>
27 #include <KSelectAction>
28 #include <KActionMenu>
29 #include <KActionCollection>
30 #include <KMessageBox>
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>
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
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
73 d
->delegate
= new RunDelegate(this);
75 if(!(Core::self()->setupFlags() & Core::NoUi
)) setupActions();
78 class ExecuteCompositeJob
: public KCompositeJob
81 ExecuteCompositeJob(QObject
* parent
, const QList
<KJob
*>& jobs
) : KCompositeJob(parent
)
83 qDebug() << "execute composite" << jobs
;
84 foreach(KJob
* job
, jobs
) {
93 subjobs().first()->start();
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();
114 KJob
* RunController::execute(const IRun
& run
)
116 if(!run
.dependencies().isEmpty())
117 ICore::self()->documentController()->saveAllDocuments(IDocument::Silent
);
120 foreach(KJob
* job
, run
.dependencies())
125 jobs
.append(new RunJob(this, run
));
126 ExecuteCompositeJob
* ecj
=new ExecuteCompositeJob(this, jobs
);
127 ecj
->setObjectName(jobs
.last()->objectName());
132 RunController::~ RunController()
137 void RunController::setupActions()
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
)));
186 void KDevelop::RunController::slotProjectOpened(KDevelop::IProject
* project
)
188 KConfigGroup
group(project
->projectConfiguration(), "Run Options");
189 QStringList runTargets
= group
.readEntry("Run Targets", QStringList());
192 foreach(const QString
& target
, runTargets
) {
193 a
=addTarget(project
, target
);
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();
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()) {
221 foreach (IProject
* project
, Core::self()->projectController()->projects()) {
222 slotProjectOpened(project
);
226 void RunController::slotExecute()
228 execute(defaultRun());
231 QStringList
splitArguments(const QString
& args
)
234 bool inQuotes
=false, scaping
=false;
235 for(int i
=0; i
<args
.size(); i
++)
237 if(i
==0) ret
+= QString();
241 ret
.last() += args
[i
];
244 else switch(args
[i
].toAscii())
254 ret
.last() += args
[i
];
260 ret
.last() += args
[i
];
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
);
281 current
= model
->index(0,0, l
.first());
283 current
= QModelIndex();
288 IRun
KDevelop::RunController::defaultRun() const
290 IProject
* project
= 0;
293 QAction
* projectAction
= d
->currentTargetAction
->currentAction();
297 data
=qvariant_cast
<Target
>(projectAction
->data());
298 project
= data
.second
;
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();
312 QString target
=group
.readEntry("Run Item", QString());
313 QModelIndex idx
=pathToIndex(model
, target
.split('/'));
314 ProjectBaseItem
*it
=model
->item(idx
);
316 exec
=it
->executable()->builtUrl().toLocalFile();
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");
327 run
.setInstrumentor("default");
329 QStringList compileItems
=group
.readEntry("Compile Items", QStringList());
330 int actionDeps
=group
.readEntry("BeforeExecute", 1);
335 foreach(const QString
& it
, compileItems
)
337 QModelIndex idx
=pathToIndex(model
, it
.split('/'));
338 ProjectBaseItem
*it
=model
->item(idx
);
340 IProject
* project
= it
->project();
344 IPlugin
* fmgr
= project
->managerPlugin();
345 IBuildSystemManager
* mgr
= fmgr
->extension
<IBuildSystemManager
>();
346 IProjectBuilder
* builder
;
349 builder
=mgr
->builder( project
->projectItem() );
354 buildJob
=builder
->build(it
);
357 buildJob
=builder
->install(it
);
360 #warning make it install as superuser.
367 kDebug() << "Failed to compile";
370 run
.setDependencies(comp
);
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
))
387 void KDevelop::RunController::registerJob(KJob
* job
)
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
);
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
);
421 emit
jobUnregistered(job
);
424 void KDevelop::RunController::checkState()
426 bool running
= false;
428 foreach (KJob
* job
, d
->jobs
.keys()) {
429 if (!job
->isSuspended()) {
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());
456 KJob
* job
= static_cast<KJob
*>(qvariant_cast
<void*>(action
->data()));
457 if (job
->capabilities() & KJob::Killable
)
461 void KDevelop::RunController::finished(KJob
* job
)
465 switch (job
->error()) {
467 case KJob::KilledJobError
:
471 KMessageBox::error(qApp
->activeWindow(), job
->errorString(), i18n("Process Error"));
475 void KDevelop::RunController::suspended(KJob
* job
)
482 void KDevelop::RunController::resumed(KJob
* job
)
489 KDevelop::RunJob::RunJob(RunController
* controller
, const IRun
& run
)
490 : m_controller(controller
)
494 setCapabilities(Killable
);
496 QString instrumentorName
= i18n("Run");
497 if (!m_run
.instrumentor().isEmpty()) {
498 m_provider
= m_controller
->findProvider(m_run
.instrumentor());
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
);
516 setErrorText(i18n("Execution failed: no plugin found for requested instrumentor \"%1\"", m_run
.instrumentor()));
517 setError(ErrorNoProvider
);
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
);
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
)
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
)
568 bool KDevelop::RunJob::doKill()
570 m_provider
->abort(this);
575 QItemDelegate
* KDevelop::RunController::delegate() const
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"