mirror of
https://bitbucket.org/smil3y/kde-extraapps.git
synced 2025-02-26 11:52:54 +00:00
999 lines
34 KiB
C++
999 lines
34 KiB
C++
/* This file is part of KDevelop
|
|
Copyright 2007-2008 Hamish Rodda <rodda@kde.org>
|
|
Copyright 2008 Aleix Pol <aleixpol@gmail.com>
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "runcontroller.h"
|
|
|
|
#include <QApplication>
|
|
#include <QStandardItemModel>
|
|
#include <QPalette>
|
|
|
|
#include <KDE/KSelectAction>
|
|
#include <KDE/KActionMenu>
|
|
#include <KDE/KActionCollection>
|
|
#include <KDE/KMessageBox>
|
|
#include <KDE/KLocale>
|
|
#include <KDE/KDebug>
|
|
#include <KDE/KColorScheme>
|
|
#include <kdialogjobuidelegate.h>
|
|
|
|
#include <interfaces/iproject.h>
|
|
#include <interfaces/idocumentcontroller.h>
|
|
#include <interfaces/ilauncher.h>
|
|
#include <interfaces/ilaunchmode.h>
|
|
#include <interfaces/launchconfigurationtype.h>
|
|
#include <outputview/ioutputview.h>
|
|
#include <outputview/outputjob.h>
|
|
#include <project/projectmodel.h>
|
|
#include <project/interfaces/iprojectbuilder.h>
|
|
#include <project/interfaces/ibuildsystemmanager.h>
|
|
|
|
#include "core.h"
|
|
#include "plugincontroller.h"
|
|
#include "uicontroller.h"
|
|
#include "projectcontroller.h"
|
|
#include "mainwindow.h"
|
|
#include "launchconfiguration.h"
|
|
#include "launchconfigurationdialog.h"
|
|
#include <interfaces/isession.h>
|
|
#include <QSignalMapper>
|
|
#include <interfaces/contextmenuextension.h>
|
|
#include <interfaces/context.h>
|
|
#include <sublime/area.h>
|
|
#include <kmenu.h>
|
|
|
|
using namespace KDevelop;
|
|
|
|
QString RunController::LaunchConfigurationsGroup = "Launch";
|
|
QString RunController::LaunchConfigurationsListEntry = "Launch Configurations";
|
|
static QString CurrentLaunchConfigProjectEntry = "Current Launch Config Project";
|
|
static QString CurrentLaunchConfigNameEntry = "Current Launch Config GroupName";
|
|
static QString ConfiguredFromProjectItemEntry = "Configured from ProjectItem";
|
|
|
|
typedef QPair<QString, IProject*> Target;
|
|
Q_DECLARE_METATYPE(Target)
|
|
|
|
|
|
//TODO: Doesn't handle add/remove of launch configs in the dialog or renaming of configs
|
|
//TODO: Doesn't auto-select launch configs opened from projects
|
|
|
|
class DebugMode : public ILaunchMode
|
|
{
|
|
public:
|
|
DebugMode() {}
|
|
virtual KIcon icon() const { return KIcon("tools-report-bug"); }
|
|
virtual QString id() const { return "debug"; }
|
|
virtual QString name() const { return i18n("Debug"); }
|
|
};
|
|
|
|
class ProfileMode : public ILaunchMode
|
|
{
|
|
public:
|
|
ProfileMode() {}
|
|
virtual KIcon icon() const { return KIcon("office-chart-area"); }
|
|
virtual QString id() const { return "profile"; }
|
|
virtual QString name() const { return i18n("Profile"); }
|
|
};
|
|
|
|
class ExecuteMode : public ILaunchMode
|
|
{
|
|
public:
|
|
ExecuteMode() {}
|
|
virtual KIcon icon() const { return KIcon("system-run"); }
|
|
virtual QString id() const { return "execute"; }
|
|
virtual QString name() const { return i18n("Execute"); }
|
|
};
|
|
|
|
class RunController::RunControllerPrivate
|
|
{
|
|
public:
|
|
QItemDelegate* delegate;
|
|
|
|
IRunController::State state;
|
|
|
|
RunController* q;
|
|
|
|
QHash<KJob*, KAction*> jobs;
|
|
KAction* stopAction;
|
|
KActionMenu* stopJobsMenu;
|
|
KAction* runAction;
|
|
KAction* dbgAction;
|
|
KSelectAction* currentTargetAction;
|
|
QMap<QString,LaunchConfigurationType*> launchConfigurationTypes;
|
|
QList<LaunchConfiguration*> launchConfigurations;
|
|
QMap<QString,ILaunchMode*> launchModes;
|
|
QSignalMapper* launchChangeMapper;
|
|
QSignalMapper* launchAsMapper;
|
|
QMap<int,QPair<QString,QString> > launchAsInfo;
|
|
KDevelop::ProjectBaseItem* contextItem;
|
|
DebugMode* debugMode;
|
|
ExecuteMode* executeMode;
|
|
ProfileMode* profileMode;
|
|
bool hasLaunchConfigType( const QString& typeId )
|
|
{
|
|
return launchConfigurationTypes.contains( typeId );
|
|
}
|
|
void saveCurrentLaunchAction()
|
|
{
|
|
if (!currentTargetAction) return;
|
|
|
|
if( currentTargetAction->currentAction() )
|
|
{
|
|
KConfigGroup grp = Core::self()->activeSession()->config()->group( RunController::LaunchConfigurationsGroup );
|
|
LaunchConfiguration* l = static_cast<LaunchConfiguration*>( qVariantValue<void*>( currentTargetAction->currentAction()->data() ) );
|
|
grp.writeEntry( CurrentLaunchConfigProjectEntry, l->project() ? l->project()->name() : "" );
|
|
grp.writeEntry( CurrentLaunchConfigNameEntry, l->configGroupName() );
|
|
grp.sync();
|
|
}
|
|
}
|
|
|
|
void configureLaunches()
|
|
{
|
|
LaunchConfigurationDialog dlg;
|
|
dlg.exec();
|
|
}
|
|
|
|
QString launchActionText( LaunchConfiguration* l )
|
|
{
|
|
QString label;
|
|
if( l->project() )
|
|
{
|
|
label = QString("%1 : %2").arg( l->project()->name()).arg(l->name());
|
|
} else
|
|
{
|
|
label = QString("%1" ).arg(l->name());
|
|
}
|
|
return label;
|
|
}
|
|
|
|
void launchAs( int id )
|
|
{
|
|
//kDebug() << "Launching id:" << id;
|
|
QPair<QString,QString> info = launchAsInfo[id];
|
|
//kDebug() << "fetching type and mode:" << info.first << info.second;
|
|
LaunchConfigurationType* type = launchConfigurationTypeForId( info.first );
|
|
ILaunchMode* mode = q->launchModeForId( info.second );
|
|
|
|
//kDebug() << "got mode and type:" << type << type->id() << mode << mode->id();
|
|
if( type && mode )
|
|
{
|
|
ILauncher* launcher = 0;
|
|
foreach (ILauncher *l, type->launchers())
|
|
{
|
|
//kDebug() << "available launcher" << l << l->id() << l->supportedModes();
|
|
if (l->supportedModes().contains(mode->id())) {
|
|
launcher = l;
|
|
break;
|
|
}
|
|
}
|
|
if (launcher)
|
|
{
|
|
QStringList itemPath = Core::self()->projectController()->projectModel()->pathFromIndex(contextItem->index());
|
|
ILaunchConfiguration* ilaunch = 0;
|
|
foreach (LaunchConfiguration *l, launchConfigurations) {
|
|
QStringList path = l->config().readEntry(ConfiguredFromProjectItemEntry, QStringList());
|
|
if (l->type() == type && path == itemPath) {
|
|
kDebug() << "already generated ilaunch" << path;
|
|
ilaunch = l;
|
|
break;
|
|
}
|
|
}
|
|
if (!ilaunch) {
|
|
ilaunch = q->createLaunchConfiguration( type,
|
|
qMakePair( mode->id(), launcher->id() ),
|
|
contextItem->project(),
|
|
contextItem->text() );
|
|
LaunchConfiguration* launch = dynamic_cast<LaunchConfiguration*>( ilaunch );
|
|
type->configureLaunchFromItem( launch->config(), contextItem );
|
|
launch->config().writeEntry(ConfiguredFromProjectItemEntry, itemPath);
|
|
//kDebug() << "created config, launching";
|
|
} else {
|
|
//kDebug() << "reusing generated config, launching";
|
|
}
|
|
q->setDefaultLaunch(ilaunch);
|
|
q->execute( mode->id(), ilaunch );
|
|
}
|
|
}
|
|
}
|
|
|
|
void updateCurrentLaunchAction()
|
|
{
|
|
if (!currentTargetAction) return;
|
|
|
|
KConfigGroup launchGrp = Core::self()->activeSession()->config()->group( RunController::LaunchConfigurationsGroup );
|
|
QString currentLaunchProject = launchGrp.readEntry( CurrentLaunchConfigProjectEntry, "" );
|
|
QString currentLaunchName = launchGrp.readEntry( CurrentLaunchConfigNameEntry, "" );
|
|
|
|
LaunchConfiguration* l = 0;
|
|
if( currentTargetAction->currentAction() )
|
|
{
|
|
l = static_cast<LaunchConfiguration*>( qVariantValue<void*>( currentTargetAction->currentAction()->data() ) );
|
|
} else if( !launchConfigurations.isEmpty() )
|
|
{
|
|
l = launchConfigurations.at( 0 );
|
|
}
|
|
|
|
if( l && ( ( !currentLaunchProject.isEmpty() && ( !l->project() || l->project()->name() != currentLaunchProject ) ) || l->configGroupName() != currentLaunchName ) )
|
|
{
|
|
foreach( QAction* a, currentTargetAction->actions() )
|
|
{
|
|
LaunchConfiguration* l = static_cast<LaunchConfiguration*>( qvariant_cast<void*>( a->data() ) );
|
|
if( currentLaunchName == l->configGroupName()
|
|
&& ( ( currentLaunchProject.isEmpty() && !l->project() )
|
|
|| ( l->project() && l->project()->name() == currentLaunchProject ) ) )
|
|
{
|
|
a->setChecked( true );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if( !currentTargetAction->currentAction() )
|
|
{
|
|
kDebug() << "oops no current action, using first if list is non-empty";
|
|
if( !currentTargetAction->actions().isEmpty() )
|
|
{
|
|
currentTargetAction->actions().first()->setChecked( true );
|
|
}
|
|
}
|
|
}
|
|
|
|
void addLaunchAction( LaunchConfiguration* l )
|
|
{
|
|
if (!currentTargetAction) return;
|
|
|
|
KAction* action = currentTargetAction->addAction(launchActionText( l ));
|
|
action->setData(qVariantFromValue<void*>(l));
|
|
}
|
|
void readLaunchConfigs( KSharedConfigPtr cfg, IProject* prj )
|
|
{
|
|
KConfigGroup group(cfg, RunController::LaunchConfigurationsGroup);
|
|
QStringList configs = group.readEntry( RunController::LaunchConfigurationsListEntry, QStringList() );
|
|
|
|
foreach( const QString& cfg, configs )
|
|
{
|
|
KConfigGroup grp = group.group( cfg );
|
|
if( launchConfigurationTypeForId( grp.readEntry( LaunchConfiguration::LaunchConfigurationTypeEntry, "" ) ) )
|
|
{
|
|
q->addLaunchConfiguration( new LaunchConfiguration( grp, prj ) );
|
|
}
|
|
}
|
|
}
|
|
LaunchConfigurationType* launchConfigurationTypeForId( const QString& id )
|
|
{
|
|
QMap<QString, LaunchConfigurationType*>::iterator it = launchConfigurationTypes.find( id );
|
|
if( it != launchConfigurationTypes.end() )
|
|
{
|
|
return it.value();
|
|
} else
|
|
{
|
|
kWarning() << "couldn't find type for id:" << id << ". Known types:" << launchConfigurationTypes.keys();
|
|
}
|
|
return 0;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
RunController::RunController(QObject *parent)
|
|
: IRunController(parent)
|
|
, d(new RunControllerPrivate)
|
|
{
|
|
setObjectName("RunController");
|
|
// TODO: need to implement compile only if needed before execute
|
|
// TODO: need to implement abort all running programs when project closed
|
|
|
|
d->currentTargetAction = 0;
|
|
d->state = Idle;
|
|
d->q = this;
|
|
d->delegate = new RunDelegate(this);
|
|
d->launchChangeMapper = new QSignalMapper( this );
|
|
d->launchAsMapper = 0;
|
|
d->contextItem = 0;
|
|
d->executeMode = 0;
|
|
d->debugMode = 0;
|
|
d->profileMode = 0;
|
|
|
|
if(!(Core::self()->setupFlags() & Core::NoUi)) {
|
|
// Note that things like registerJob() do not work without the actions, it'll simply crash.
|
|
setupActions();
|
|
}
|
|
}
|
|
|
|
RunController::~RunController()
|
|
{
|
|
delete d;
|
|
}
|
|
|
|
void KDevelop::RunController::launchChanged( LaunchConfiguration* l )
|
|
{
|
|
foreach( QAction* a, d->currentTargetAction->actions() )
|
|
{
|
|
if( static_cast<LaunchConfiguration*>( qVariantValue<void*>( a->data() ) ) == l )
|
|
{
|
|
a->setText( d->launchActionText( l ) );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void RunController::cleanup()
|
|
{
|
|
delete d->executeMode;
|
|
d->executeMode = 0;
|
|
delete d->profileMode;
|
|
d->profileMode = 0;
|
|
delete d->debugMode;
|
|
d->debugMode = 0;
|
|
|
|
stopAllProcesses();
|
|
d->saveCurrentLaunchAction();
|
|
}
|
|
|
|
void RunController::initialize()
|
|
{
|
|
d->executeMode = new ExecuteMode();
|
|
addLaunchMode( d->executeMode );
|
|
d->profileMode = new ProfileMode();
|
|
addLaunchMode( d->profileMode );
|
|
d->debugMode = new DebugMode;
|
|
addLaunchMode( d->debugMode );
|
|
d->readLaunchConfigs( Core::self()->activeSession()->config(), 0 );
|
|
|
|
foreach (IProject* project, Core::self()->projectController()->projects()) {
|
|
slotProjectOpened(project);
|
|
}
|
|
connect(Core::self()->projectController(), SIGNAL(projectOpened(KDevelop::IProject*)),
|
|
this, SLOT(slotProjectOpened(KDevelop::IProject*)));
|
|
connect(Core::self()->projectController(), SIGNAL(projectClosing(KDevelop::IProject*)),
|
|
this, SLOT(slotProjectClosing(KDevelop::IProject*)));
|
|
connect(Core::self()->projectController(), SIGNAL(projectConfigurationChanged(KDevelop::IProject*)),
|
|
this, SLOT(slotRefreshProject(KDevelop::IProject*)));
|
|
|
|
if( (Core::self()->setupFlags() & Core::NoUi) == 0 )
|
|
{
|
|
// Only do this in GUI mode
|
|
d->updateCurrentLaunchAction();
|
|
}
|
|
}
|
|
|
|
KJob* RunController::execute(const QString& runMode, ILaunchConfiguration* launch)
|
|
{
|
|
if( !launch )
|
|
{
|
|
kDebug() << "execute called without launch config!";
|
|
return 0;
|
|
}
|
|
LaunchConfiguration *run = dynamic_cast<LaunchConfiguration*>(launch);
|
|
//TODO: Port to launch framework, probably needs to be part of the launcher
|
|
//if(!run.dependencies().isEmpty())
|
|
// ICore::self()->documentController()->saveAllDocuments(IDocument::Silent);
|
|
|
|
//foreach(KJob* job, run.dependencies())
|
|
//{
|
|
// jobs.append(job);
|
|
//}
|
|
|
|
kDebug() << "mode:" << runMode;
|
|
QString launcherId = run->launcherForMode( runMode );
|
|
kDebug() << "launcher id:" << launcherId;
|
|
|
|
ILauncher* launcher = run->type()->launcherForId( launcherId );
|
|
|
|
if( !launcher ) {
|
|
KMessageBox::error(
|
|
qApp->activeWindow(),
|
|
i18n("The current launch configuration does not support the '%1' mode.", runMode),
|
|
"");
|
|
return 0;
|
|
}
|
|
|
|
KJob* launchJob = launcher->start(runMode, run);
|
|
registerJob(launchJob);
|
|
return launchJob;
|
|
}
|
|
|
|
void RunController::setupActions()
|
|
{
|
|
KAction *action;
|
|
|
|
// TODO not multi-window friendly, FIXME
|
|
KActionCollection* ac = Core::self()->uiControllerInternal()->defaultMainWindow()->actionCollection();
|
|
|
|
action = new KAction (i18n("Configure Launches..."), this);
|
|
ac->addAction("configure_launches", action);
|
|
action->setStatusTip(i18n("Open Launch Configuration Dialog"));
|
|
action->setToolTip(i18nc("@info:tooltip", "Open Launch Configuration Dialog"));
|
|
action->setWhatsThis(i18nc("@info:whatsthis", "Opens a dialog to setup new launch configurations, or to change the existing ones."));
|
|
connect(action, SIGNAL(triggered(bool)), SLOT(configureLaunches()));
|
|
|
|
d->runAction = new KAction( KIcon("system-run"), i18n("Execute Launch"), this);
|
|
d->runAction->setIconText( i18nc("Short text for 'Execute launch' used in the toolbar", "Execute") );
|
|
d->runAction->setShortcut(Qt::SHIFT + Qt::Key_F9);
|
|
d->runAction->setToolTip(i18nc("@info:tooltip", "Execute current launch"));
|
|
d->runAction->setStatusTip(i18n("Execute current launch"));
|
|
d->runAction->setWhatsThis(i18nc("@info:whatsthis", "Executes the target or the program specified in currently active launch configuration."));
|
|
ac->addAction("run_execute", d->runAction);
|
|
connect(d->runAction, SIGNAL(triggered(bool)), this, SLOT(slotExecute()));
|
|
|
|
d->dbgAction = new KAction( KIcon("debug-run"), i18n("Debug Launch"), this);
|
|
d->dbgAction->setShortcut(Qt::Key_F9);
|
|
d->dbgAction->setIconText( i18nc("Short text for 'Debug launch' used in the toolbar", "Debug") );
|
|
d->dbgAction->setToolTip(i18nc("@info:tooltip", "Debug current launch"));
|
|
d->dbgAction->setStatusTip(i18n("Debug current launch"));
|
|
d->dbgAction->setWhatsThis(i18nc("@info:whatsthis", "Executes the target or the program specified in currently active launch configuration inside a Debugger."));
|
|
ac->addAction("run_debug", d->dbgAction);
|
|
connect(d->dbgAction, SIGNAL(triggered(bool)), this, SLOT(slotDebug()));
|
|
Core::self()->uiControllerInternal()->area(0, "code")->addAction(d->dbgAction);
|
|
|
|
// TODO: at least get a profile target, it's sad to have the menu entry without a profiler
|
|
// KAction* profileAction = new KAction( KIcon(""), i18n("Profile Launch"), this);
|
|
// profileAction->setToolTip(i18nc("@info:tooltip", "Profile current launch"));
|
|
// profileAction->setStatusTip(i18n("Profile current launch"));
|
|
// profileAction->setWhatsThis(i18nc("@info:whatsthis", "Executes the target or the program specified in currently active launch configuration inside a Profiler."));
|
|
// ac->addAction("run_profile", profileAction);
|
|
// connect(profileAction, SIGNAL(triggered(bool)), this, SLOT(slotProfile()));
|
|
|
|
action = d->stopAction = new KAction( KIcon("process-stop"), i18n("Stop All Jobs"), this);
|
|
action->setIconText(i18nc("Short text for 'Stop All Jobs' used in the toolbar", "Stop All"));
|
|
// Ctrl+Escape would be nicer, but thats taken by the ksysguard desktop shortcut
|
|
action->setShortcut(QKeySequence("Ctrl+Shift+Escape"));
|
|
action->setToolTip(i18nc("@info:tooltip", "Stop all currently running jobs"));
|
|
action->setWhatsThis(i18nc("@info:whatsthis", "Requests that all running jobs are stopped."));
|
|
action->setEnabled(false);
|
|
ac->addAction("run_stop_all", action);
|
|
connect(action, SIGNAL(triggered(bool)), this, SLOT(stopAllProcesses()));
|
|
Core::self()->uiControllerInternal()->area(0, "debug")->addAction(action);
|
|
|
|
action = d->stopJobsMenu = new KActionMenu( KIcon("process-stop"), i18n("Stop"), this);
|
|
action->setIconText(i18nc("Short text for 'Stop' used in the toolbar", "Stop"));
|
|
action->setToolTip(i18nc("@info:tooltip", "Menu allowing to stop individual jobs"));
|
|
action->setWhatsThis(i18nc("@info:whatsthis", "List of jobs that can be stopped individually."));
|
|
action->setEnabled(false);
|
|
ac->addAction("run_stop_menu", action);
|
|
|
|
d->currentTargetAction = new KSelectAction( i18n("Current Launch Configuration"), this);
|
|
d->currentTargetAction->setToolTip(i18nc("@info:tooltip", "Current launch configuration"));
|
|
d->currentTargetAction->setStatusTip(i18n("Current launch Configuration"));
|
|
d->currentTargetAction->setWhatsThis(i18nc("@info:whatsthis", "Select which launch configuration to run when run is invoked."));
|
|
ac->addAction("run_default_target", d->currentTargetAction);
|
|
}
|
|
|
|
LaunchConfigurationType* RunController::launchConfigurationTypeForId( const QString& id )
|
|
{
|
|
return d->launchConfigurationTypeForId( id );
|
|
}
|
|
|
|
void KDevelop::RunController::slotProjectOpened(KDevelop::IProject * project)
|
|
{
|
|
d->readLaunchConfigs( project->projectConfiguration(), project );
|
|
d->updateCurrentLaunchAction();
|
|
}
|
|
|
|
void KDevelop::RunController::slotProjectClosing(KDevelop::IProject * project)
|
|
{
|
|
if (!d->currentTargetAction) return;
|
|
|
|
foreach (QAction* action, d->currentTargetAction->actions()) {
|
|
LaunchConfiguration* l = static_cast<LaunchConfiguration*>(qvariant_cast<void*>(action->data()));
|
|
if ( project == l->project() ) {
|
|
l->save();
|
|
d->launchConfigurations.removeAll(l);
|
|
delete l;
|
|
bool wasSelected = action->isChecked();
|
|
delete action;
|
|
if (wasSelected && !d->currentTargetAction->actions().isEmpty())
|
|
d->currentTargetAction->actions().first()->setChecked(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
void KDevelop::RunController::slotRefreshProject(KDevelop::IProject* project)
|
|
{
|
|
slotProjectClosing(project);
|
|
slotProjectOpened(project);
|
|
}
|
|
|
|
void RunController::slotDebug()
|
|
{
|
|
if(d->launchConfigurations.isEmpty()) {
|
|
LaunchConfigurationDialog d;
|
|
d.exec();
|
|
}
|
|
|
|
if(!d->launchConfigurations.isEmpty())
|
|
executeDefaultLaunch( "debug" );
|
|
}
|
|
|
|
void RunController::slotProfile()
|
|
{
|
|
if(d->launchConfigurations.isEmpty()) {
|
|
LaunchConfigurationDialog d;
|
|
d.exec();
|
|
}
|
|
|
|
if(!d->launchConfigurations.isEmpty())
|
|
executeDefaultLaunch( "profile" );
|
|
}
|
|
|
|
void RunController::slotExecute()
|
|
{
|
|
|
|
if(d->launchConfigurations.isEmpty()) {
|
|
LaunchConfigurationDialog d;
|
|
d.exec();
|
|
}
|
|
|
|
if(!d->launchConfigurations.isEmpty())
|
|
executeDefaultLaunch( "execute" );
|
|
}
|
|
|
|
LaunchConfiguration* KDevelop::RunController::defaultLaunch() const
|
|
{
|
|
QAction* projectAction = d->currentTargetAction->currentAction();
|
|
if( projectAction )
|
|
return static_cast<LaunchConfiguration*>(qvariant_cast<void*>(projectAction->data()));
|
|
return 0;
|
|
}
|
|
|
|
void KDevelop::RunController::registerJob(KJob * job)
|
|
{
|
|
if (!job)
|
|
return;
|
|
|
|
if (!(job->capabilities() & KJob::Killable)) {
|
|
// see e.g. https://bugs.kde.org/show_bug.cgi?id=314187
|
|
kWarning() << "non-killable job" << job << "registered - this might lead to crashes on shutdown.";
|
|
}
|
|
|
|
if (!d->jobs.contains(job)) {
|
|
KAction* stopJobAction = 0;
|
|
if (Core::self()->setupFlags() != Core::NoUi) {
|
|
stopJobAction = new KAction(job->objectName().isEmpty() ? i18n("<%1> Unnamed job", job->staticMetaObject.className()) : job->objectName(), this);
|
|
stopJobAction->setData(QVariant::fromValue(static_cast<void*>(job)));
|
|
d->stopJobsMenu->addAction(stopJobAction);
|
|
connect (stopJobAction, SIGNAL(triggered(bool)), SLOT(slotKillJob()));
|
|
|
|
job->setUiDelegate( new KDialogJobUiDelegate() );
|
|
}
|
|
|
|
d->jobs.insert(job, stopJobAction);
|
|
|
|
connect( job, SIGNAL(finished(KJob*)), SLOT(finished(KJob*)) );
|
|
connect( job, SIGNAL(destroyed(QObject*)), SLOT(jobDestroyed(QObject*)) );
|
|
|
|
IRunController::registerJob(job);
|
|
|
|
emit jobRegistered(job);
|
|
}
|
|
|
|
job->start();
|
|
|
|
checkState();
|
|
}
|
|
|
|
void KDevelop::RunController::unregisterJob(KJob * job)
|
|
{
|
|
IRunController::unregisterJob(job);
|
|
|
|
Q_ASSERT(d->jobs.contains(job));
|
|
|
|
// Delete the stop job action
|
|
QAction *action = d->jobs.take(job);
|
|
if (action)
|
|
action->deleteLater();
|
|
|
|
checkState();
|
|
|
|
emit jobUnregistered(job);
|
|
}
|
|
|
|
void KDevelop::RunController::checkState()
|
|
{
|
|
bool running = false;
|
|
|
|
foreach (KJob* job, d->jobs.keys()) {
|
|
if (!job->isSuspended()) {
|
|
running = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( ( d->state != Running ? false : true ) == running ) {
|
|
d->state = running ? Running : Idle;
|
|
emit runStateChanged(d->state);
|
|
}
|
|
|
|
if (Core::self()->setupFlags() != Core::NoUi) {
|
|
d->stopAction->setEnabled(running);
|
|
d->stopJobsMenu->setEnabled(running);
|
|
}
|
|
}
|
|
|
|
void KDevelop::RunController::stopAllProcesses()
|
|
{
|
|
// composite jobs might remove child jobs, see also:
|
|
// https://bugs.kde.org/show_bug.cgi?id=258904
|
|
// foreach already iterates over a copy
|
|
foreach (KJob* job, d->jobs.keys()) {
|
|
// now we check the real list whether it was deleted
|
|
if (!d->jobs.contains(job))
|
|
continue;
|
|
if (job->capabilities() & KJob::Killable) {
|
|
job->kill(KJob::EmitResult);
|
|
} else {
|
|
kWarning() << "cannot stop non-killable job: " << job;
|
|
}
|
|
}
|
|
}
|
|
|
|
void KDevelop::RunController::slotKillJob()
|
|
{
|
|
KAction* action = dynamic_cast<KAction*>(sender());
|
|
Q_ASSERT(action);
|
|
|
|
KJob* job = static_cast<KJob*>(qvariant_cast<void*>(action->data()));
|
|
if (job->capabilities() & KJob::Killable)
|
|
job->kill();
|
|
}
|
|
|
|
void KDevelop::RunController::finished(KJob * job)
|
|
{
|
|
unregisterJob(job);
|
|
|
|
switch (job->error()) {
|
|
case KJob::NoError:
|
|
case KJob::KilledJobError:
|
|
case OutputJob::FailedShownError:
|
|
break;
|
|
|
|
default:
|
|
{
|
|
///WARNING: do *not* use a nested event loop here, it might cause
|
|
/// random crashes later on, see e.g.:
|
|
/// https://bugs.kde.org/show_bug.cgi?id=309811
|
|
KDialog* dialog = new KDialog(qApp->activeWindow());
|
|
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
|
dialog->setWindowTitle(i18n("Process Error"));
|
|
dialog->setButtons(KDialog::Close);
|
|
KMessageBox::createKMessageBox(dialog, QMessageBox::Warning,
|
|
job->errorString(), QStringList(),
|
|
QString(), 0, KMessageBox::NoExec
|
|
);
|
|
dialog->show();
|
|
}
|
|
}
|
|
}
|
|
|
|
void RunController::jobDestroyed(QObject* job)
|
|
{
|
|
KJob* kjob = static_cast<KJob*>(job);
|
|
if (d->jobs.contains(kjob)) {
|
|
kWarning() << "job destroyed without emitting finished signal!";
|
|
unregisterJob(kjob);
|
|
}
|
|
}
|
|
|
|
void KDevelop::RunController::suspended(KJob * job)
|
|
{
|
|
Q_UNUSED(job);
|
|
|
|
checkState();
|
|
}
|
|
|
|
void KDevelop::RunController::resumed(KJob * job)
|
|
{
|
|
Q_UNUSED(job);
|
|
|
|
checkState();
|
|
}
|
|
|
|
QList< KJob * > KDevelop::RunController::currentJobs() const
|
|
{
|
|
return d->jobs.keys();
|
|
}
|
|
|
|
QList<ILaunchConfiguration*> RunController::launchConfigurations() const
|
|
{
|
|
QList<ILaunchConfiguration*> configs;
|
|
foreach (LaunchConfiguration *config, launchConfigurationsInternal())
|
|
configs << config;
|
|
return configs;
|
|
}
|
|
|
|
QList<LaunchConfiguration*> RunController::launchConfigurationsInternal() const
|
|
{
|
|
return d->launchConfigurations;
|
|
}
|
|
|
|
QList<LaunchConfigurationType*> RunController::launchConfigurationTypes() const
|
|
{
|
|
return d->launchConfigurationTypes.values();
|
|
}
|
|
|
|
void RunController::addConfigurationType( LaunchConfigurationType* type )
|
|
{
|
|
if( !d->launchConfigurationTypes.contains( type->id() ) )
|
|
{
|
|
d->launchConfigurationTypes.insert( type->id(), type );
|
|
}
|
|
}
|
|
|
|
void RunController::removeConfigurationType( LaunchConfigurationType* type )
|
|
{
|
|
foreach( LaunchConfiguration* l, d->launchConfigurations )
|
|
{
|
|
if( l->type() == type )
|
|
{
|
|
d->launchConfigurations.removeAll( l );
|
|
delete l;
|
|
}
|
|
}
|
|
d->launchConfigurationTypes.remove( type->id() );
|
|
}
|
|
|
|
void KDevelop::RunController::addLaunchMode(KDevelop::ILaunchMode* mode)
|
|
{
|
|
if( !d->launchModes.contains( mode->id() ) )
|
|
{
|
|
d->launchModes.insert( mode->id(), mode );
|
|
}
|
|
}
|
|
|
|
QList< KDevelop::ILaunchMode* > KDevelop::RunController::launchModes() const
|
|
{
|
|
return d->launchModes.values();
|
|
}
|
|
|
|
void KDevelop::RunController::removeLaunchMode(KDevelop::ILaunchMode* mode)
|
|
{
|
|
d->launchModes.remove( mode->id() );
|
|
}
|
|
|
|
KDevelop::ILaunchMode* KDevelop::RunController::launchModeForId(const QString& id) const
|
|
{
|
|
QMap<QString,ILaunchMode*>::iterator it = d->launchModes.find( id );
|
|
if( it != d->launchModes.end() )
|
|
{
|
|
return it.value();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void KDevelop::RunController::addLaunchConfiguration(KDevelop::LaunchConfiguration* l)
|
|
{
|
|
if( !d->launchConfigurations.contains( l ) )
|
|
{
|
|
d->addLaunchAction( l );
|
|
d->launchConfigurations << l;
|
|
if( !d->currentTargetAction->currentAction() )
|
|
{
|
|
if( !d->currentTargetAction->actions().isEmpty() )
|
|
{
|
|
d->currentTargetAction->actions().first()->setChecked( true );
|
|
}
|
|
}
|
|
connect( l, SIGNAL(nameChanged(LaunchConfiguration*)), SLOT(launchChanged(LaunchConfiguration*)) );
|
|
}
|
|
}
|
|
|
|
void KDevelop::RunController::removeLaunchConfiguration(KDevelop::LaunchConfiguration* l)
|
|
{
|
|
KConfigGroup launcherGroup;
|
|
if( l->project() ) {
|
|
launcherGroup = l->project()->projectConfiguration()->group( LaunchConfigurationsGroup );
|
|
} else {
|
|
launcherGroup = Core::self()->activeSession()->config()->group( LaunchConfigurationsGroup );
|
|
}
|
|
QStringList configs = launcherGroup.readEntry( RunController::LaunchConfigurationsListEntry, QStringList() );
|
|
configs.removeAll( l->configGroupName() );
|
|
launcherGroup.deleteGroup( l->configGroupName() );
|
|
launcherGroup.writeEntry( RunController::LaunchConfigurationsListEntry, configs );
|
|
launcherGroup.sync();
|
|
|
|
foreach( QAction* a, d->currentTargetAction->actions() )
|
|
{
|
|
if( static_cast<LaunchConfiguration*>( qVariantValue<void*>( a->data() ) ) == l )
|
|
{
|
|
bool wasSelected = a->isChecked();
|
|
d->currentTargetAction->removeAction( a );
|
|
if( wasSelected && !d->currentTargetAction->actions().isEmpty() )
|
|
{
|
|
d->currentTargetAction->actions().first()->setChecked( true );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
d->launchConfigurations.removeAll( l );
|
|
|
|
delete l;
|
|
}
|
|
|
|
void KDevelop::RunController::executeDefaultLaunch(const QString& runMode)
|
|
{
|
|
if( !defaultLaunch() )
|
|
{
|
|
kWarning() << "no default launch!";
|
|
return;
|
|
}
|
|
execute( runMode, defaultLaunch() );
|
|
}
|
|
|
|
void RunController::setDefaultLaunch(ILaunchConfiguration* l)
|
|
{
|
|
foreach( QAction* a, d->currentTargetAction->actions() )
|
|
{
|
|
if( static_cast<ILaunchConfiguration*>( qVariantValue<void*>( a->data() ) ) == l )
|
|
{
|
|
a->setChecked(true);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool launcherNameExists(const QString& name)
|
|
{
|
|
foreach(ILaunchConfiguration* config, Core::self()->runControllerInternal()->launchConfigurations()) {
|
|
if(config->name()==name)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
QString makeUnique(const QString& name)
|
|
{
|
|
if(launcherNameExists(name)) {
|
|
for(int i=2; ; i++) {
|
|
QString proposed = QString("%1 (%2)").arg(name).arg(i);
|
|
if(!launcherNameExists(proposed)) {
|
|
return proposed;
|
|
}
|
|
}
|
|
}
|
|
return name;
|
|
}
|
|
|
|
ILaunchConfiguration* RunController::createLaunchConfiguration ( LaunchConfigurationType* type,
|
|
const QPair<QString,QString>& launcher,
|
|
IProject* project, const QString& name )
|
|
{
|
|
KConfigGroup launchGroup;
|
|
if( project )
|
|
{
|
|
launchGroup = project->projectConfiguration()->group( RunController::LaunchConfigurationsGroup );
|
|
|
|
} else
|
|
{
|
|
launchGroup = Core::self()->activeSession()->config()->group( RunController::LaunchConfigurationsGroup );
|
|
|
|
}
|
|
QStringList configs = launchGroup.readEntry( RunController::LaunchConfigurationsListEntry, QStringList() );
|
|
uint num = 0;
|
|
QString baseName = "Launch Configuration";
|
|
while( configs.contains( QString( "%1 %2" ).arg( baseName ).arg( num ) ) )
|
|
{
|
|
num++;
|
|
}
|
|
QString groupName = QString( "%1 %2" ).arg( baseName ).arg( num );
|
|
KConfigGroup launchConfigGroup = launchGroup.group( groupName );
|
|
QString cfgName = name;
|
|
if( name.isEmpty() )
|
|
{
|
|
cfgName = i18n("New %1 Launcher", type->name() );
|
|
cfgName = makeUnique(cfgName);
|
|
}
|
|
|
|
launchConfigGroup.writeEntry(LaunchConfiguration::LaunchConfigurationNameEntry, cfgName );
|
|
launchConfigGroup.writeEntry(LaunchConfiguration::LaunchConfigurationTypeEntry, type->id() );
|
|
launchConfigGroup.sync();
|
|
configs << groupName;
|
|
launchGroup.writeEntry( RunController::LaunchConfigurationsListEntry, configs );
|
|
launchGroup.sync();
|
|
LaunchConfiguration* l = new LaunchConfiguration( launchConfigGroup, project );
|
|
l->setLauncherForMode( launcher.first, launcher.second );
|
|
Core::self()->runControllerInternal()->addLaunchConfiguration( l );
|
|
return l;
|
|
}
|
|
|
|
QItemDelegate * KDevelop::RunController::delegate() const
|
|
{
|
|
return d->delegate;
|
|
}
|
|
|
|
ContextMenuExtension RunController::contextMenuExtension ( Context* ctx )
|
|
{
|
|
delete d->launchAsMapper;
|
|
d->launchAsMapper = new QSignalMapper( this );
|
|
connect( d->launchAsMapper, SIGNAL(mapped(int)), SLOT(launchAs(int)) );
|
|
d->launchAsInfo.clear();
|
|
d->contextItem = 0;
|
|
ContextMenuExtension ext;
|
|
if( ctx->type() == Context::ProjectItemContext ) {
|
|
KDevelop::ProjectItemContext* prjctx = dynamic_cast<KDevelop::ProjectItemContext*>( ctx );
|
|
if( prjctx->items().count() == 1 )
|
|
{
|
|
ProjectBaseItem* itm = prjctx->items().at( 0 );
|
|
int i = 0;
|
|
foreach( ILaunchMode* mode, d->launchModes.values() )
|
|
{
|
|
KActionMenu* menu = new KActionMenu( i18n("%1 As...", mode->name() ), this );
|
|
foreach( LaunchConfigurationType* type, launchConfigurationTypes() )
|
|
{
|
|
bool hasLauncher = false;
|
|
foreach( ILauncher* launcher, type->launchers() )
|
|
{
|
|
if( launcher->supportedModes().contains( mode->id() ) )
|
|
{
|
|
hasLauncher = true;
|
|
}
|
|
}
|
|
if( hasLauncher && type->canLaunch(itm) )
|
|
{
|
|
d->launchAsInfo[i] = qMakePair( type->id(), mode->id() );
|
|
KAction* act = new KAction( d->launchAsMapper );
|
|
act->setText( type->name() );
|
|
kDebug() << "Setting up mapping for:" << i << "for action" << act->text() << "in mode" << mode->name();
|
|
d->launchAsMapper->setMapping( act, i );
|
|
connect( act, SIGNAL(triggered()), d->launchAsMapper, SLOT(map()) );
|
|
menu->addAction(act);
|
|
i++;
|
|
}
|
|
}
|
|
if( menu->menu()->actions().count() > 0 )
|
|
{
|
|
ext.addAction( ContextMenuExtension::RunGroup, menu);
|
|
}
|
|
}
|
|
if( ext.actions( ContextMenuExtension::RunGroup ).count() > 0 )
|
|
{
|
|
d->contextItem = itm;
|
|
}
|
|
}
|
|
}
|
|
return ext;
|
|
}
|
|
|
|
|
|
|
|
RunDelegate::RunDelegate( QObject* parent )
|
|
: QItemDelegate(parent), runProviderBrush( KColorScheme::View, KColorScheme::PositiveText ),
|
|
errorBrush( KColorScheme::View, KColorScheme::NegativeText )
|
|
{
|
|
}
|
|
|
|
void RunDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const
|
|
{
|
|
QStyleOptionViewItem opt = option;
|
|
QVariant status = index.data(Qt::UserRole+1);
|
|
// if( status.isValid() && status.canConvert<KDevelop::IRunProvider::OutputTypes>() )
|
|
// {
|
|
// IRunProvider::OutputTypes type = status.value<KDevelop::IRunProvider::OutputTypes>();
|
|
// if( type == IRunProvider::RunProvider )
|
|
// {
|
|
// opt.palette.setBrush( QPalette::Text, runProviderBrush.brush( option.palette ) );
|
|
// } else if( type == IRunProvider::StandardError )
|
|
// {
|
|
// opt.palette.setBrush( QPalette::Text, errorBrush.brush( option.palette ) );
|
|
// }
|
|
// }
|
|
QItemDelegate::paint(painter, opt, index);
|
|
}
|
|
|
|
|
|
#include "moc_runcontroller.cpp"
|