diff --git a/plugins/execute/nativeappconfig.cpp b/plugins/execute/nativeappconfig.cpp index a1070715cc..0cd40f41b2 100644 --- a/plugins/execute/nativeappconfig.cpp +++ b/plugins/execute/nativeappconfig.cpp @@ -1,524 +1,522 @@ /* This file is part of KDevelop Copyright 2009 Andreas Pakulat Copyright 2010 Aleix Pol Gonzalez 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 "nativeappconfig.h" #include #include #include #include #include #include #include "nativeappjob.h" #include #include #include #include #include #include #include #include #include #include #include "executeplugin.h" #include "debug.h" #include #include #include #include "projecttargetscombobox.h" #include #include -#include #include #include #include -#include using namespace KDevelop; QIcon NativeAppConfigPage::icon() const { return QIcon::fromTheme("system-run"); } static KDevelop::ProjectBaseItem* itemForPath(const QStringList& path, KDevelop::ProjectModel* model) { return model->itemFromIndex(model->pathToIndex(path)); } //TODO: Make sure to auto-add the executable target to the dependencies when its used. void NativeAppConfigPage::loadFromConfiguration(const KConfigGroup& cfg, KDevelop::IProject* project ) { bool b = blockSignals( true ); projectTarget->setBaseItem( project ? project->projectItem() : 0, true); projectTarget->setCurrentItemPath( cfg.readEntry( ExecutePlugin::projectTargetEntry, QStringList() ) ); QUrl exe = cfg.readEntry( ExecutePlugin::executableEntry, QUrl()); if( !exe.isEmpty() || project ){ executablePath->setUrl( !exe.isEmpty() ? exe : project->folder() ); }else{ KDevelop::IProjectController* pc = KDevelop::ICore::self()->projectController(); if( pc ){ executablePath->setUrl( pc->projects().count() ? pc->projects().first()->folder() : QUrl() ); } } //executablePath->setFilter("application/x-executable"); executableRadio->setChecked( true ); if ( !cfg.readEntry( ExecutePlugin::isExecutableEntry, false ) && projectTarget->count() ){ projectTargetRadio->setChecked( true ); } arguments->setClearButtonEnabled( true ); arguments->setText( cfg.readEntry( ExecutePlugin::argumentsEntry, "" ) ); workingDirectory->setUrl( cfg.readEntry( ExecutePlugin::workingDirEntry, QUrl() ) ); environment->setCurrentProfile( cfg.readEntry( ExecutePlugin::environmentGroupEntry, QString() ) ); runInTerminal->setChecked( cfg.readEntry( ExecutePlugin::useTerminalEntry, false ) ); terminal->setEditText( cfg.readEntry( ExecutePlugin::terminalEntry, terminal->itemText(0) ) ); QVariantList deps = KDevelop::stringToQVariant( cfg.readEntry( ExecutePlugin::dependencyEntry, QString() ) ).toList(); QStringList strDeps; foreach( const QVariant& dep, deps ) { QStringList deplist = dep.toStringList(); KDevelop::ProjectModel* model = KDevelop::ICore::self()->projectController()->projectModel(); KDevelop::ProjectBaseItem* pitem=itemForPath(deplist, model); QIcon icon; if(pitem) icon=QIcon::fromTheme(pitem->iconName()); QListWidgetItem* item = new QListWidgetItem(icon, KDevelop::joinWithEscaping( deplist, '/', '\\' ), dependencies ); item->setData( Qt::UserRole, dep ); } dependencyAction->setCurrentIndex( dependencyAction->findData( cfg.readEntry( ExecutePlugin::dependencyActionEntry, "Nothing" ) ) ); blockSignals( b ); } NativeAppConfigPage::NativeAppConfigPage( QWidget* parent ) : LaunchConfigurationPage( parent ) { setupUi(this); //Setup data info for combobox dependencyAction->setItemData(0, "Nothing" ); dependencyAction->setItemData(1, "Build" ); dependencyAction->setItemData(2, "Install" ); dependencyAction->setItemData(3, "SudoInstall" ); addDependency->setIcon( QIcon::fromTheme("list-add") ); removeDependency->setIcon( QIcon::fromTheme("list-remove") ); moveDepUp->setIcon( QIcon::fromTheme("go-up") ); moveDepDown->setIcon( QIcon::fromTheme("go-down") ); browseProject->setIcon(QIcon::fromTheme("folder-document")); //Set workingdirectory widget to ask for directories rather than files workingDirectory->setMode(KFile::Directory | KFile::ExistingOnly | KFile::LocalOnly); configureEnvironment->setSelectionWidget(environment); //connect signals to changed signal connect( projectTarget, static_cast(&ProjectTargetsComboBox::currentIndexChanged), this, &NativeAppConfigPage::changed ); connect( projectTargetRadio, &QRadioButton::toggled, this, &NativeAppConfigPage::changed ); connect( executableRadio, &QRadioButton::toggled, this, &NativeAppConfigPage::changed ); connect( executablePath->lineEdit(), &KLineEdit::textEdited, this, &NativeAppConfigPage::changed ); connect( executablePath, &KUrlRequester::urlSelected, this, &NativeAppConfigPage::changed ); connect( arguments, &QLineEdit::textEdited, this, &NativeAppConfigPage::changed ); connect( workingDirectory, &KUrlRequester::urlSelected, this, &NativeAppConfigPage::changed ); connect( workingDirectory->lineEdit(), &KLineEdit::textEdited, this, &NativeAppConfigPage::changed ); connect( environment, &EnvironmentSelectionWidget::currentProfileChanged, this, &NativeAppConfigPage::changed ); connect( addDependency, &QPushButton::clicked, this, &NativeAppConfigPage::addDep ); connect( addDependency, &QPushButton::clicked, this, &NativeAppConfigPage::changed ); connect( removeDependency, &QPushButton::clicked, this, &NativeAppConfigPage::changed ); connect( removeDependency, &QPushButton::clicked, this, &NativeAppConfigPage::removeDep ); connect( moveDepDown, &QPushButton::clicked, this, &NativeAppConfigPage::changed ); connect( moveDepUp, &QPushButton::clicked, this, &NativeAppConfigPage::changed ); connect( moveDepDown, &QPushButton::clicked, this, &NativeAppConfigPage::moveDependencyDown ); connect( moveDepUp, &QPushButton::clicked, this, &NativeAppConfigPage::moveDependencyUp ); connect( dependencies->selectionModel(), &QItemSelectionModel::selectionChanged, this, &NativeAppConfigPage::checkActions ); connect( dependencyAction, static_cast(&KComboBox::currentIndexChanged), this, &NativeAppConfigPage::changed ); connect( runInTerminal, &QCheckBox::toggled, this, &NativeAppConfigPage::changed ); connect( terminal, &KComboBox::editTextChanged, this, &NativeAppConfigPage::changed ); connect( terminal, static_cast(&KComboBox::currentIndexChanged), this, &NativeAppConfigPage::changed ); connect( dependencyAction, static_cast(&KComboBox::currentIndexChanged), this, &NativeAppConfigPage::activateDeps ); connect( targetDependency, &ProjectItemLineEdit::textChanged, this, &NativeAppConfigPage::depEdited); connect( browseProject, &QPushButton::clicked, this, &NativeAppConfigPage::selectItemDialog); } void NativeAppConfigPage::depEdited( const QString& str ) { int pos; QString tmp = str; addDependency->setEnabled( !str.isEmpty() && ( !targetDependency->validator() || targetDependency->validator()->validate( tmp, pos ) == QValidator::Acceptable ) ); } void NativeAppConfigPage::activateDeps( int idx ) { browseProject->setEnabled( dependencyAction->itemData( idx ).toString() != "Nothing" ); dependencies->setEnabled( dependencyAction->itemData( idx ).toString() != "Nothing" ); targetDependency->setEnabled( dependencyAction->itemData( idx ).toString() != "Nothing" ); } void NativeAppConfigPage::checkActions( const QItemSelection& selected, const QItemSelection& unselected ) { Q_UNUSED( unselected ); qCDebug(PLUGIN_EXECUTE) << "checkActions"; if( !selected.indexes().isEmpty() ) { qCDebug(PLUGIN_EXECUTE) << "have selection"; Q_ASSERT( selected.indexes().count() == 1 ); QModelIndex idx = selected.indexes().at( 0 ); qCDebug(PLUGIN_EXECUTE) << "index" << idx; moveDepUp->setEnabled( idx.row() > 0 ); moveDepDown->setEnabled( idx.row() < dependencies->count() - 1 ); removeDependency->setEnabled( true ); } else { removeDependency->setEnabled( false ); moveDepUp->setEnabled( false ); moveDepDown->setEnabled( false ); } } void NativeAppConfigPage::moveDependencyDown() { QList list = dependencies->selectedItems(); if( !list.isEmpty() ) { Q_ASSERT( list.count() == 1 ); QListWidgetItem* item = list.at( 0 ); int row = dependencies->row( item ); dependencies->takeItem( row ); dependencies->insertItem( row+1, item ); dependencies->selectionModel()->select( dependencies->model()->index( row+1, 0, QModelIndex() ), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::SelectCurrent ); } } void NativeAppConfigPage::moveDependencyUp() { QList list = dependencies->selectedItems(); if( !list.isEmpty() ) { Q_ASSERT( list.count() == 1 ); QListWidgetItem* item = list.at( 0 ); int row = dependencies->row( item ); dependencies->takeItem( row ); dependencies->insertItem( row-1, item ); dependencies->selectionModel()->select( dependencies->model()->index( row-1, 0, QModelIndex() ), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::SelectCurrent ); } } void NativeAppConfigPage::addDep() { QIcon icon; KDevelop::ProjectBaseItem* pitem = targetDependency->currentItem(); if(pitem) icon = QIcon::fromTheme(pitem->iconName()); QListWidgetItem* item = new QListWidgetItem(icon, targetDependency->text(), dependencies); item->setData( Qt::UserRole, targetDependency->itemPath() ); targetDependency->setText(""); addDependency->setEnabled( false ); dependencies->selectionModel()->clearSelection(); item->setSelected(true); // dependencies->selectionModel()->select( dependencies->model()->index( dependencies->model()->rowCount() - 1, 0, QModelIndex() ), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::SelectCurrent ); } void NativeAppConfigPage::selectItemDialog() { if(targetDependency->selectItemDialog()) { addDep(); } } void NativeAppConfigPage::removeDep() { QList list = dependencies->selectedItems(); if( !list.isEmpty() ) { Q_ASSERT( list.count() == 1 ); int row = dependencies->row( list.at(0) ); delete dependencies->takeItem( row ); dependencies->selectionModel()->select( dependencies->model()->index( row - 1, 0, QModelIndex() ), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::SelectCurrent ); } } void NativeAppConfigPage::saveToConfiguration( KConfigGroup cfg, KDevelop::IProject* project ) const { Q_UNUSED( project ); cfg.writeEntry( ExecutePlugin::isExecutableEntry, executableRadio->isChecked() ); cfg.writeEntry( ExecutePlugin::executableEntry, executablePath->url() ); cfg.writeEntry( ExecutePlugin::projectTargetEntry, projectTarget->currentItemPath() ); cfg.writeEntry( ExecutePlugin::argumentsEntry, arguments->text() ); cfg.writeEntry( ExecutePlugin::workingDirEntry, workingDirectory->url() ); cfg.writeEntry( ExecutePlugin::environmentGroupEntry, environment->currentProfile() ); cfg.writeEntry( ExecutePlugin::useTerminalEntry, runInTerminal->isChecked() ); cfg.writeEntry( ExecutePlugin::terminalEntry, terminal->currentText() ); cfg.writeEntry( ExecutePlugin::dependencyActionEntry, dependencyAction->itemData( dependencyAction->currentIndex() ).toString() ); QVariantList deps; for( int i = 0; i < dependencies->count(); i++ ) { deps << dependencies->item( i )->data( Qt::UserRole ); } cfg.writeEntry( ExecutePlugin::dependencyEntry, KDevelop::qvariantToString( QVariant( deps ) ) ); } QString NativeAppConfigPage::title() const { return i18n("Configure Native Application"); } QList< KDevelop::LaunchConfigurationPageFactory* > NativeAppLauncher::configPages() const { return QList(); } QString NativeAppLauncher::description() const { return "Executes Native Applications"; } QString NativeAppLauncher::id() { return "nativeAppLauncher"; } QString NativeAppLauncher::name() const { return i18n("Native Application"); } NativeAppLauncher::NativeAppLauncher() { } KJob* NativeAppLauncher::start(const QString& launchMode, KDevelop::ILaunchConfiguration* cfg) { Q_ASSERT(cfg); if( !cfg ) { return 0; } if( launchMode == "execute" ) { IExecutePlugin* iface = KDevelop::ICore::self()->pluginController()->pluginForExtension("org.kdevelop.IExecutePlugin", "kdevexecute")->extension(); Q_ASSERT(iface); KJob* depjob = iface->dependecyJob( cfg ); QList l; if( depjob ) { l << depjob; } l << new NativeAppJob( KDevelop::ICore::self()->runController(), cfg ); return new KDevelop::ExecuteCompositeJob( KDevelop::ICore::self()->runController(), l ); } qWarning() << "Unknown launch mode " << launchMode << "for config:" << cfg->name(); return 0; } QStringList NativeAppLauncher::supportedModes() const { return QStringList() << "execute"; } KDevelop::LaunchConfigurationPage* NativeAppPageFactory::createWidget(QWidget* parent) { return new NativeAppConfigPage( parent ); } NativeAppPageFactory::NativeAppPageFactory() { } NativeAppConfigType::NativeAppConfigType() { factoryList.append( new NativeAppPageFactory() ); } NativeAppConfigType::~NativeAppConfigType() { qDeleteAll(factoryList); factoryList.clear(); } QString NativeAppConfigType::name() const { return i18n("Compiled Binary"); } QList NativeAppConfigType::configPages() const { return factoryList; } QString NativeAppConfigType::id() const { return ExecutePlugin::_nativeAppConfigTypeId; } QIcon NativeAppConfigType::icon() const { return QIcon::fromTheme("application-x-executable"); } bool NativeAppConfigType::canLaunch ( KDevelop::ProjectBaseItem* item ) const { if( item->target() && item->target()->executable() ) { return canLaunch( item->target()->executable()->builtUrl() ); } return false; } bool NativeAppConfigType::canLaunch ( const QUrl& file ) const { return ( file.isLocalFile() && QFileInfo( file.toLocalFile() ).isExecutable() ); } void NativeAppConfigType::configureLaunchFromItem ( KConfigGroup cfg, KDevelop::ProjectBaseItem* item ) const { cfg.writeEntry( ExecutePlugin::isExecutableEntry, false ); KDevelop::ProjectModel* model = KDevelop::ICore::self()->projectController()->projectModel(); cfg.writeEntry( ExecutePlugin::projectTargetEntry, model->pathFromIndex( model->indexFromItem( item ) ) ); cfg.writeEntry( ExecutePlugin::workingDirEntry, item->executable()->builtUrl().adjusted(QUrl::RemoveFilename) ); cfg.sync(); } void NativeAppConfigType::configureLaunchFromCmdLineArguments ( KConfigGroup cfg, const QStringList& args ) const { cfg.writeEntry( ExecutePlugin::isExecutableEntry, true ); Q_ASSERT(QFile::exists(args.first())); // TODO: we probably want to flexibilize, but at least we won't be accepting wrong values anymore cfg.writeEntry( ExecutePlugin::executableEntry, QUrl::fromLocalFile(args.first()) ); QStringList a(args); a.removeFirst(); cfg.writeEntry( ExecutePlugin::argumentsEntry, KShell::joinArgs(a) ); cfg.sync(); } QList targetsInFolder(KDevelop::ProjectFolderItem* folder) { QList ret; foreach(KDevelop::ProjectFolderItem* f, folder->folderList()) ret += targetsInFolder(f); ret += folder->targetList(); return ret; } bool actionLess(QAction* a, QAction* b) { return a->text() < b->text(); } bool menuLess(QMenu* a, QMenu* b) { return a->title() < b->title(); } QMenu* NativeAppConfigType::launcherSuggestions() { QMenu* ret = new QMenu(i18n("Project Executables")); KDevelop::ProjectModel* model = KDevelop::ICore::self()->projectController()->projectModel(); QList projects = KDevelop::ICore::self()->projectController()->projects(); foreach(KDevelop::IProject* project, projects) { if(project->projectFileManager()->features() & KDevelop::IProjectFileManager::Targets) { QList targets=targetsInFolder(project->projectItem()); QHash > targetsContainer; QMenu* projectMenu = ret->addMenu(QIcon::fromTheme("project-development"), project->name()); foreach(KDevelop::ProjectTargetItem* target, targets) { if(target->executable()) { QStringList path = model->pathFromIndex(target->index()); if(!path.isEmpty()){ QAction* act = new QAction(projectMenu); act->setData(KDevelop::joinWithEscaping(path, '/','\\')); act->setProperty("name", target->text()); path.removeFirst(); act->setText(path.join("/")); act->setIcon(QIcon::fromTheme("system-run")); connect(act, &QAction::triggered, this, &NativeAppConfigType::suggestionTriggered); targetsContainer[target->parent()].append(act); } } } QList separateActions; QList submenus; foreach(KDevelop::ProjectBaseItem* folder, targetsContainer.keys()) { QList actions = targetsContainer.value(folder); if(actions.size()==1 || !folder->parent()) { separateActions += actions.first(); } else { foreach(QAction* a, actions) { a->setText(a->property("name").toString()); } QStringList path = model->pathFromIndex(folder->index()); path.removeFirst(); QMenu* submenu = new QMenu(path.join("/")); submenu->addActions(actions); submenus += submenu; } } std::sort(separateActions.begin(), separateActions.end(), actionLess); std::sort(submenus.begin(), submenus.end(), menuLess); foreach(QMenu* m, submenus) projectMenu->addMenu(m); projectMenu->addActions(separateActions); projectMenu->setEnabled(!projectMenu->isEmpty()); } } return ret; } void NativeAppConfigType::suggestionTriggered() { QAction* action = qobject_cast(sender()); KDevelop::ProjectModel* model = KDevelop::ICore::self()->projectController()->projectModel(); KDevelop::ProjectTargetItem* pitem = dynamic_cast(itemForPath(KDevelop::splitWithEscaping(action->data().toString(),'/', '\\'), model)); if(pitem) { QPair launcher = qMakePair( launchers().at( 0 )->supportedModes().at(0), launchers().at( 0 )->id() ); KDevelop::IProject* p = pitem->project(); KDevelop::ILaunchConfiguration* config = KDevelop::ICore::self()->runController()->createLaunchConfiguration(this, launcher, p, pitem->text()); KConfigGroup cfg = config->config(); QStringList splitPath = model->pathFromIndex(pitem->index()); // QString path = KDevelop::joinWithEscaping(splitPath,'/','\\'); cfg.writeEntry( ExecutePlugin::projectTargetEntry, splitPath ); cfg.writeEntry( ExecutePlugin::dependencyEntry, KDevelop::qvariantToString( QVariantList() << splitPath ) ); cfg.writeEntry( ExecutePlugin::dependencyActionEntry, "Build" ); cfg.sync(); emit signalAddLaunchConfiguration(config); } }