File indexing completed on 2024-04-28 04:37:18

0001 /*
0002     SPDX-FileCopyrightText: 2009 Andreas Pakulat <apaku@gmx.de>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "launchconfigurationdialog.h"
0008 
0009 #include <QDialogButtonBox>
0010 #include <QLabel>
0011 #include <QMenu>
0012 #include <QPushButton>
0013 #include <QTabWidget>
0014 #include <QTreeView>
0015 #include <QVBoxLayout>
0016 
0017 #include <KComboBox>
0018 #include <KLocalizedString>
0019 #include <KMessageBox>
0020 #include <KMessageBox_KDevCompat>
0021 
0022 #include <interfaces/launchconfigurationpage.h>
0023 #include <interfaces/iproject.h>
0024 #include <interfaces/iprojectcontroller.h>
0025 #include <interfaces/isession.h>
0026 
0027 #include "core.h"
0028 #include "runcontroller.h"
0029 #include "launchconfiguration.h"
0030 #include "debug.h"
0031 
0032 #include <interfaces/ilauncher.h>
0033 #include <interfaces/ilaunchmode.h>
0034 #include <interfaces/launchconfigurationtype.h>
0035 
0036 namespace KDevelop
0037 {
0038 
0039 bool launchConfigGreaterThan(KDevelop::LaunchConfigurationType* a, KDevelop::LaunchConfigurationType* b)
0040 {
0041     return a->name()>b->name();
0042 }
0043 
0044 //TODO: Maybe use KPageDialog instead, might make the model stuff easier and the default-size stuff as well
0045 LaunchConfigurationDialog::LaunchConfigurationDialog(QWidget* parent)
0046     : QDialog(parent)
0047 {
0048     setWindowTitle( i18nc("@title:window", "Launch Configurations" ) );
0049 
0050     auto* mainWidget = new QWidget(this);
0051     auto *mainLayout = new QVBoxLayout(this);
0052     mainLayout->addWidget(mainWidget);
0053 
0054     setupUi(mainWidget);
0055     splitter->setSizes(QList<int>{260, 620});
0056     splitter->setCollapsible(0, false);
0057 
0058     addConfig->setToolTip(i18nc("@info:tooltip", "Add a new launch configuration."));
0059     deleteConfig->setEnabled( false );
0060     deleteConfig->setToolTip(i18nc("@info:tooltip", "Delete selected launch configuration."));
0061 
0062     model = new LaunchConfigurationsModel( tree );
0063     tree->setModel( model );
0064     tree->setExpandsOnDoubleClick( true );
0065     tree->setSelectionBehavior( QAbstractItemView::SelectRows );
0066     tree->setSelectionMode( QAbstractItemView::SingleSelection );
0067     tree->setUniformRowHeights( true );
0068     tree->setItemDelegate( new LaunchConfigurationModelDelegate(this) );
0069     tree->setColumnHidden(1, true);
0070     for(int row=0; row<model->rowCount(); row++) {
0071         tree->setExpanded(model->index(row, 0), true);
0072     }
0073 
0074     tree->setContextMenuPolicy(Qt::CustomContextMenu);
0075     connect( tree, &QTreeView::customContextMenuRequested, this, &LaunchConfigurationDialog::doTreeContextMenu );
0076     connect( deleteConfig, &QPushButton::clicked, this, &LaunchConfigurationDialog::deleteConfiguration);
0077     connect( model, &LaunchConfigurationsModel::dataChanged, this, &LaunchConfigurationDialog::modelChanged );
0078     connect( tree->selectionModel(), &QItemSelectionModel::selectionChanged, this, &LaunchConfigurationDialog::selectionChanged);
0079     QModelIndex idx = model->indexForConfig( Core::self()->runControllerInternal()->defaultLaunch() );
0080     qCDebug(SHELL) << "selecting index:" << idx;
0081     if( !idx.isValid() )
0082     {
0083         for( int i = 0; i < model->rowCount(); i++ )
0084         {
0085             if( model->rowCount( model->index( i, 0, QModelIndex() ) ) > 0 )
0086             {
0087                 idx = model->index( 1, 0, model->index( i, 0, QModelIndex() ) );
0088                 break;
0089             }
0090         }
0091         if( !idx.isValid() )
0092         {
0093             idx = model->index( 0, 0, QModelIndex() );
0094         }
0095     }
0096     tree->selectionModel()->select( QItemSelection( idx, idx ), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
0097     tree->selectionModel()->setCurrentIndex( idx, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
0098 
0099     // Unfortunately tree->resizeColumnToContents() only looks at the top-level
0100     // items, instead of all open ones. Hence we're calculating it ourselves like
0101     // this:
0102     // Take the selected index, check if it has childs, if so take the first child
0103     // Then count the level by going up, then let the tree calculate the width
0104     // for the selected or its first child index and add indentation*level
0105     //
0106     // If Qt Software ever fixes resizeColumnToContents, the following line
0107     // can be enabled and the rest be removed
0108     // tree->resizeColumnToContents( 0 );
0109     int level = 0;
0110     QModelIndex widthidx = idx;
0111     if( model->rowCount( idx ) > 0 )
0112     {
0113         widthidx = model->index( 0, 0, idx );
0114     }
0115     QModelIndex parentidx = widthidx.parent();
0116     while( parentidx.isValid() )
0117     {
0118         level++;
0119         parentidx = parentidx.parent();
0120     }
0121     // make sure the base column width is honored, e.g. when no launch configs exist
0122     tree->resizeColumnToContents(0);
0123     int width = tree->columnWidth( 0 );
0124     while ( widthidx.isValid() )
0125     {
0126         width = qMax( width, level*tree->indentation() + tree->indentation() + tree->sizeHintForIndex( widthidx ).width() );
0127         widthidx = widthidx.parent();
0128     }
0129     tree->setColumnWidth( 0, width );
0130 
0131     auto* m = new QMenu(this);
0132     QList<LaunchConfigurationType*> types = Core::self()->runController()->launchConfigurationTypes();
0133     std::sort(types.begin(), types.end(), launchConfigGreaterThan); //we want it in reverse order
0134     for (LaunchConfigurationType* type : qAsConst(types)) {
0135         connect(type, &LaunchConfigurationType::signalAddLaunchConfiguration, this, &LaunchConfigurationDialog::addConfiguration);
0136         QMenu* suggestionsMenu = type->launcherSuggestions();
0137 
0138         if(suggestionsMenu) {
0139             // take ownership
0140             suggestionsMenu->setParent(m, suggestionsMenu->windowFlags());
0141             m->addMenu(suggestionsMenu);
0142         }
0143     }
0144     // Simplify menu structure to get rid of 1-entry levels
0145     while (m->actions().count() == 1) {
0146         QMenu* subMenu = m->actions().at(0)->menu();
0147         if (subMenu && subMenu->isEnabled() && subMenu->actions().count()<5) {
0148             m = subMenu;
0149         } else {
0150             break;
0151         }
0152     }
0153     if(!m->isEmpty()) {
0154         auto* separator = new QAction(m);
0155         separator->setSeparator(true);
0156         m->insertAction(m->actions().at(0), separator);
0157     }
0158 
0159     for (LaunchConfigurationType* type : qAsConst(types)) {
0160         auto* action = new QAction(type->icon(), type->name(), m);
0161         action->setProperty("configtype", QVariant::fromValue<QObject*>(type));
0162         connect(action, &QAction::triggered, this, &LaunchConfigurationDialog::createEmptyLauncher);
0163 
0164         if(!m->actions().isEmpty())
0165             m->insertAction(m->actions().at(0), action);
0166         else
0167             m->addAction(action);
0168     }
0169     addConfig->setMenu(m);
0170     addConfig->setEnabled( !m->isEmpty() );
0171 
0172     messageWidget->setCloseButtonVisible( false );
0173     messageWidget->setMessageType( KMessageWidget::Warning );
0174     messageWidget->setText( i18n("No launch configurations available. (Is any of the Execute plugins loaded?)") );
0175     messageWidget->setVisible( m->isEmpty() );
0176 
0177     connect(debugger, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &LaunchConfigurationDialog::launchModeChanged);
0178 
0179     connect(buttonBox, &QDialogButtonBox::accepted, this, &LaunchConfigurationDialog::accept);
0180     connect(buttonBox, &QDialogButtonBox::rejected, this, &LaunchConfigurationDialog::reject);
0181     connect(buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked,
0182             this, QOverload<>::of(&LaunchConfigurationDialog::saveConfig));
0183     connect(buttonBox->button(QDialogButtonBox::Apply), &QPushButton::clicked,
0184             this, QOverload<>::of(&LaunchConfigurationDialog::saveConfig));
0185     mainLayout->addWidget(buttonBox);
0186 
0187     resize( QSize(qMax(1200, sizeHint().width()), qMax(500, sizeHint().height())) );
0188 }
0189 
0190 void LaunchConfigurationDialog::doTreeContextMenu(const QPoint& point)
0191 {
0192     if ( ! tree->selectionModel()->selectedRows().isEmpty() ) {
0193         QModelIndex selected = tree->selectionModel()->selectedRows().first();
0194         if ( selected.parent().isValid() && ! selected.parent().parent().isValid() ) {
0195             // only display the menu if a launch config is clicked
0196             QMenu menu(tree);
0197             auto* rename = new QAction(QIcon::fromTheme(QStringLiteral("edit-rename")), i18nc("@action:inmenu", "Rename Configuration"), &menu);
0198             auto* delete_ = new QAction(QIcon::fromTheme(QStringLiteral("edit-delete")), i18nc("@action:inmenu", "Delete Configuration"), &menu);
0199             connect(rename, &QAction::triggered, this, &LaunchConfigurationDialog::renameSelected);
0200             connect(delete_, &QAction::triggered, this, &LaunchConfigurationDialog::deleteConfiguration);
0201             menu.addAction(rename);
0202             menu.addAction(delete_);
0203             menu.exec(tree->viewport()->mapToGlobal(point));
0204         }
0205     }
0206 }
0207 
0208 void LaunchConfigurationDialog::renameSelected()
0209 {
0210     if( !tree->selectionModel()->selectedRows().isEmpty() )
0211     {
0212         QModelIndex parent = tree->selectionModel()->selectedRows().first();
0213         if( parent.parent().isValid() )
0214         {
0215             parent = parent.parent();
0216         }
0217         QModelIndex index = model->index(tree->selectionModel()->selectedRows().first().row(), 0, parent);
0218         tree->edit( index );
0219     }
0220 }
0221 
0222 QSize LaunchConfigurationDialog::sizeHint() const
0223 {
0224     QSize s = QDialog::sizeHint();
0225     return s.expandedTo(QSize(880, 520));
0226 }
0227 
0228 void LaunchConfigurationDialog::createEmptyLauncher()
0229 {
0230     auto* action = qobject_cast<QAction*>(sender());
0231     Q_ASSERT(action);
0232 
0233     auto* type = qobject_cast<LaunchConfigurationType*>(action->property("configtype").value<QObject*>());
0234     Q_ASSERT(type);
0235 
0236     IProject* p = model->projectForIndex(tree->currentIndex());
0237     QPair< QString, QString > launcher( type->launchers().at( 0 )->supportedModes().at(0), type->launchers().at( 0 )->id() );
0238     ILaunchConfiguration* l = ICore::self()->runController()->createLaunchConfiguration(type, launcher, p);
0239     addConfiguration(l);
0240 }
0241 
0242 void LaunchConfigurationDialog::selectionChanged(const QItemSelection& selected, const QItemSelection& deselected)
0243 {
0244     if( !deselected.indexes().isEmpty() )
0245     {
0246         LaunchConfiguration* l = model->configForIndex( deselected.indexes().first() );
0247         if( l )
0248         {
0249             disconnect(l, &LaunchConfiguration::nameChanged, this,  &LaunchConfigurationDialog::updateNameLabel);
0250             if( currentPageChanged )
0251             {
0252                 if (KMessageBox::questionTwoActions(
0253                         this, i18n("Selected Launch Configuration has unsaved changes. Do you want to save it?"),
0254                         i18nc("@title:window", "Unsaved Changes"), KStandardGuiItem::save(),
0255                         KStandardGuiItem::discard())
0256                     == KMessageBox::PrimaryAction) {
0257                     saveConfig( deselected.indexes().first() );
0258                 } else {
0259                     auto* tab = qobject_cast<LaunchConfigPagesContainer*>( stack->currentWidget() );
0260                     tab->setLaunchConfiguration( l );
0261                     buttonBox->button(QDialogButtonBox::Apply)->setEnabled( false );
0262                     currentPageChanged = false;
0263                 }
0264             }
0265         }
0266     }
0267     updateNameLabel(nullptr);
0268 
0269     for( int i = 1; i < stack->count(); i++ )
0270     {
0271         QWidget* w = stack->widget(i);
0272         stack->removeWidget(w);
0273         delete w;
0274     }
0275 
0276     if( !selected.indexes().isEmpty() )
0277     {
0278         QModelIndex idx = selected.indexes().first();
0279         LaunchConfiguration* l = model->configForIndex( idx );
0280         ILaunchMode* lm = model->modeForIndex( idx );
0281 
0282         if( l )
0283         {
0284             updateNameLabel( l );
0285             tree->expand( model->indexForConfig( l ) );
0286             connect( l, &LaunchConfiguration::nameChanged, this, &LaunchConfigurationDialog::updateNameLabel );
0287             if( lm )
0288             {
0289                 QVariant currentLaunchMode = idx.sibling(idx.row(), 1).data(Qt::EditRole);
0290                 {
0291                     QSignalBlocker blocker(debugger);
0292                     const QList<ILauncher*> launchers = l->type()->launchers();
0293 
0294                     debugger->clear();
0295                     for (ILauncher* launcher : launchers) {
0296                         if (launcher->supportedModes().contains(lm->id())) {
0297                             debugger->addItem(launcher->name(), launcher->id());
0298                         }
0299                     }
0300 
0301                     debugger->setCurrentIndex(debugger->findData(currentLaunchMode));
0302                 }
0303 
0304                 debugger->setVisible(debugger->count()>0);
0305                 debugLabel->setVisible(debugger->count()>0);
0306 
0307                 ILauncher* launcher = l->type()->launcherForId( currentLaunchMode.toString() );
0308                 if( launcher )
0309                 {
0310                     LaunchConfigPagesContainer* tab = launcherWidgets.value( launcher );
0311                     if(!tab)
0312                     {
0313                         QList<KDevelop::LaunchConfigurationPageFactory*> pages = launcher->configPages();
0314                         if(!pages.isEmpty()) {
0315                             tab = new LaunchConfigPagesContainer( launcher->configPages(), stack );
0316                             connect( tab, &LaunchConfigPagesContainer::changed, this, &LaunchConfigurationDialog::pageChanged );
0317                             stack->addWidget( tab );
0318                         }
0319                     }
0320 
0321                     if(tab) {
0322                         tab->setLaunchConfiguration( l );
0323                         stack->setCurrentWidget( tab );
0324                     } else {
0325                         auto* label = new QLabel(i18nc("%1 is a launcher name",
0326                                                        "No configuration is needed for '%1'",
0327                                                        launcher->name()), stack);
0328                         label->setAlignment(Qt::AlignCenter);
0329                         QFont font = label->font();
0330                         font.setItalic(true);
0331                         label->setFont(font);
0332                         stack->addWidget(label);
0333                         stack->setCurrentWidget(label);
0334                     }
0335 
0336                     updateNameLabel( l );
0337                     addConfig->setEnabled( false );
0338                     deleteConfig->setEnabled( false );
0339                 } else
0340                 {
0341                     addConfig->setEnabled( false );
0342                     deleteConfig->setEnabled( false );
0343                     stack->setCurrentIndex( 0 );
0344                 }
0345             } else
0346             {
0347                 //TODO: enable removal button
0348                 LaunchConfigurationType* type = l->type();
0349                 LaunchConfigPagesContainer* tab = typeWidgets.value( type );
0350                 if( !tab )
0351                 {
0352                     tab = new LaunchConfigPagesContainer( type->configPages(), stack );
0353                     connect( tab, &LaunchConfigPagesContainer::changed, this, &LaunchConfigurationDialog::pageChanged );
0354                     stack->addWidget( tab );
0355                 }
0356                 qCDebug(SHELL) << "created pages, setting config up";
0357                 tab->setLaunchConfiguration( l );
0358                 stack->setCurrentWidget( tab );
0359 
0360                 addConfig->setEnabled( addConfig->menu() && !addConfig->menu()->isEmpty() );
0361                 deleteConfig->setEnabled( true );
0362                 debugger->setVisible( false );
0363                 debugLabel->setVisible( false );
0364             }
0365         } else
0366         {
0367             addConfig->setEnabled( addConfig->menu() && !addConfig->menu()->isEmpty() );
0368             deleteConfig->setEnabled( false );
0369             stack->setCurrentIndex( 0 );
0370             auto* l = new QLabel(i18n("<i>Select a configuration to edit from the left,<br>"
0371                                       "or click the \"Add\" button to add a new one.</i>"), stack);
0372             l->setAlignment(Qt::AlignCenter);
0373             stack->addWidget(l);
0374             stack->setCurrentWidget(l);
0375             debugger->setVisible( false );
0376             debugLabel->setVisible( false );
0377         }
0378     } else
0379     {
0380         debugger->setVisible( false );
0381         debugLabel->setVisible( false );
0382         addConfig->setEnabled( false );
0383         deleteConfig->setEnabled( false );
0384         stack->setCurrentIndex( 0 );
0385     }
0386 }
0387 
0388 void LaunchConfigurationDialog::saveConfig( const QModelIndex& idx )
0389 {
0390     Q_UNUSED( idx );
0391     auto* tab = qobject_cast<LaunchConfigPagesContainer*>( stack->currentWidget() );
0392     if( tab )
0393     {
0394         tab->save();
0395         buttonBox->button(QDialogButtonBox::Apply)->setEnabled( false );
0396         currentPageChanged = false;
0397     }
0398 }
0399 
0400 void LaunchConfigurationDialog::saveConfig()
0401 {
0402     if( !tree->selectionModel()->selectedRows().isEmpty() )
0403     {
0404         saveConfig( tree->selectionModel()->selectedRows().first() );
0405     }
0406 }
0407 
0408 
0409 void LaunchConfigurationDialog::pageChanged()
0410 {
0411     currentPageChanged = true;
0412     buttonBox->button(QDialogButtonBox::Apply)->setEnabled( true );
0413 }
0414 
0415 void LaunchConfigurationDialog::modelChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight)
0416 {
0417     if (tree->selectionModel())
0418     {
0419         QModelIndex index = tree->selectionModel()->selectedRows().first();
0420         if (index.row() >= topLeft.row() && index.row() <= bottomRight.row() && bottomRight.column() == 1)
0421             selectionChanged(tree->selectionModel()->selection(), tree->selectionModel()->selection());
0422     }
0423 }
0424 
0425 void LaunchConfigurationDialog::deleteConfiguration()
0426 {
0427     if( !tree->selectionModel()->selectedRows().isEmpty() )
0428     {
0429         model->deleteConfiguration( tree->selectionModel()->selectedRows().first() );
0430         tree->resizeColumnToContents( 0 );
0431     }
0432 }
0433 
0434 void LaunchConfigurationDialog::updateNameLabel( LaunchConfiguration* l )
0435 {
0436     if( l )
0437     {
0438         configName->setText( i18n("Editing %2: <b>%1</b>", l->name(), l->type()->name() ) );
0439     } else
0440     {
0441         configName->clear();
0442     }
0443 }
0444 
0445 void LaunchConfigurationDialog::createConfiguration()
0446 {
0447     if( !tree->selectionModel()->selectedRows().isEmpty() )
0448     {
0449         QModelIndex idx = tree->selectionModel()->selectedRows().first();
0450         if( idx.parent().isValid() )
0451         {
0452             idx = idx.parent();
0453         }
0454         model->createConfiguration( idx );
0455         QModelIndex newindex = model->index( model->rowCount( idx ) - 1, 0, idx );
0456         tree->selectionModel()->select( newindex, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
0457         tree->selectionModel()->setCurrentIndex( newindex, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
0458         tree->edit( newindex );
0459         tree->resizeColumnToContents( 0 );
0460     }
0461 }
0462 
0463 void LaunchConfigurationDialog::addConfiguration(ILaunchConfiguration* _launch)
0464 {
0465     auto* launch = dynamic_cast<LaunchConfiguration*>(_launch);
0466     Q_ASSERT(launch);
0467     int row = launch->project() ? model->findItemForProject(launch->project())->row : 0;
0468     QModelIndex idx  = model->index(row, 0);
0469 
0470     model->addConfiguration(launch, idx);
0471 
0472     QModelIndex newindex = model->index( model->rowCount( idx ) - 1, 0, idx );
0473     tree->selectionModel()->select( newindex, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
0474     tree->selectionModel()->setCurrentIndex( newindex, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
0475     tree->edit( newindex );
0476     tree->resizeColumnToContents( 0 );
0477 }
0478 
0479 LaunchConfigurationsModel::LaunchConfigurationsModel(QObject* parent): QAbstractItemModel(parent)
0480 {
0481     auto* global = new GenericPageItem;
0482     global->text = i18n("Global");
0483     global->row = 0;
0484     const auto projects = Core::self()->projectController()->projects();
0485     topItems.reserve(1 + projects.size());
0486     topItems << global;
0487     for (IProject* p :  projects) {
0488         auto* t = new ProjectItem;
0489         t->project = p;
0490         t->row = topItems.count();
0491         topItems << t;
0492     }
0493     const auto launchConfigurations = Core::self()->runControllerInternal()->launchConfigurationsInternal();
0494     for (LaunchConfiguration* l : launchConfigurations) {
0495         addItemForLaunchConfig( l );
0496     }
0497 }
0498 
0499 void LaunchConfigurationsModel::addItemForLaunchConfig( LaunchConfiguration* l )
0500 {
0501     auto* t = new LaunchItem;
0502     t->launch = l;
0503     TreeItem* parent;
0504     if( l->project() ) {
0505         parent = findItemForProject( l->project() );
0506     } else {
0507         parent = topItems.at(0);
0508     }
0509     t->parent = parent;
0510     t->row = parent->children.count();
0511     parent->children.append( t );
0512     addLaunchModeItemsForLaunchConfig ( t );
0513 }
0514 
0515 void LaunchConfigurationsModel::addLaunchModeItemsForLaunchConfig ( LaunchItem* t )
0516 {
0517     QList<TreeItem*> items;
0518     QSet<QString> modes;
0519     const auto launchers = t->launch->type()->launchers();
0520     for (ILauncher* launcher : launchers) {
0521         const auto supportedModes = launcher->supportedModes();
0522         for (const QString& mode : supportedModes) {
0523             if( !modes.contains( mode ) && launcher->configPages().count() > 0 )
0524             {
0525                 modes.insert( mode );
0526                 auto* lmi = new LaunchModeItem;
0527                 lmi->mode = Core::self()->runController()->launchModeForId( mode );
0528                 lmi->parent = t;
0529                 lmi->row = t->children.count();
0530                 items.append( lmi );
0531             }
0532         }
0533     }
0534     if( !items.isEmpty() )
0535     {
0536         QModelIndex p = indexForConfig( t->launch );
0537         beginInsertRows( p, t->children.count(), t->children.count() + items.count() - 1  );
0538         t->children.append( items );
0539         endInsertRows();
0540     }
0541 }
0542 
0543 LaunchConfigurationsModel::ProjectItem* LaunchConfigurationsModel::findItemForProject(IProject* p) const
0544 {
0545     for (TreeItem* t : topItems) {
0546         auto* pi = dynamic_cast<ProjectItem*>( t );
0547         if( pi && pi->project == p )
0548         {
0549             return pi;
0550         }
0551     }
0552     Q_ASSERT(false);
0553     return nullptr;
0554 }
0555 
0556 int LaunchConfigurationsModel::columnCount(const QModelIndex& parent) const
0557 {
0558     Q_UNUSED( parent );
0559     return 2;
0560 }
0561 
0562 QVariant LaunchConfigurationsModel::data(const QModelIndex& index, int role) const
0563 {
0564     if( index.isValid() && index.column() >= 0 && index.column() < 2 )
0565     {
0566         auto* t = static_cast<TreeItem*>( index.internalPointer() );
0567         switch( role )
0568         {
0569             case Qt::DisplayRole:
0570             {
0571                 auto* li = dynamic_cast<LaunchItem*>( t );
0572                 if( li )
0573                 {
0574                     if( index.column() == 0 )
0575                     {
0576                         return li->launch->name();
0577                     } else if( index.column() == 1 )
0578                     {
0579                         return li->launch->type()->name();
0580                     }
0581                 }
0582                 auto* pi = dynamic_cast<ProjectItem*>( t );
0583                 if( pi && index.column() == 0 )
0584                 {
0585                     return pi->project->name();
0586                 }
0587                 auto* gpi = dynamic_cast<GenericPageItem*>( t );
0588                 if( gpi && index.column() == 0 )
0589                 {
0590                     return gpi->text;
0591                 }
0592                 auto* lmi = dynamic_cast<LaunchModeItem*>( t );
0593                 if( lmi )
0594                 {
0595                     if( index.column() == 0 )
0596                     {
0597                         return lmi->mode->name();
0598                     } else if( index.column() == 1 )
0599                     {
0600                         LaunchConfiguration* l = configForIndex( index );
0601                         return l->type()->launcherForId( l->launcherForMode( lmi->mode->id() ) )->name();
0602                     }
0603                 }
0604                 break;
0605             }
0606             case Qt::DecorationRole:
0607             {
0608                 auto* li = dynamic_cast<LaunchItem*>( t );
0609                 if( index.column() == 0 && li )
0610                 {
0611                     return li->launch->type()->icon();
0612                 }
0613                 auto* lmi = dynamic_cast<LaunchModeItem*>( t );
0614                 if( lmi && index.column() == 0 )
0615                 {
0616                     return lmi->mode->icon();
0617                 }
0618                 if ( index.column() == 0 && !index.parent().isValid() ) {
0619                     if (index.row() == 0) {
0620                         // global item
0621                         return QIcon::fromTheme(QStringLiteral("folder"));
0622                     } else {
0623                         // project item
0624                         return QIcon::fromTheme(QStringLiteral("folder-development"));
0625                     }
0626                 }
0627                 break;
0628             }
0629             case Qt::EditRole:
0630             {
0631                 auto* li = dynamic_cast<LaunchItem*>( t );
0632                 if( li )
0633                 {
0634                     if( index.column() == 0 )
0635                     {
0636                         return li->launch->name();
0637                     } else if ( index.column() == 1 )
0638                     {
0639                         return li->launch->type()->id();
0640                     }
0641                 }
0642                 auto* lmi = dynamic_cast<LaunchModeItem*>( t );
0643                 if( lmi && index.column() == 1  )
0644                 {
0645                     return configForIndex( index )->launcherForMode( lmi->mode->id() );
0646                 }
0647                 break;
0648             }
0649             default:
0650                 break;
0651         }
0652     }
0653     return QVariant();
0654 }
0655 
0656 QModelIndex LaunchConfigurationsModel::index(int row, int column, const QModelIndex& parent) const
0657 {
0658     if( !hasIndex( row, column, parent ) )
0659         return QModelIndex();
0660     TreeItem* tree;
0661 
0662     if( !parent.isValid() )
0663     {
0664         tree = topItems.at( row );
0665     } else
0666     {
0667         auto* t = static_cast<TreeItem*>( parent.internalPointer() );
0668         tree = t->children.at( row );
0669     }
0670     if( tree )
0671     {
0672         return createIndex( row, column, tree );
0673     }
0674     return QModelIndex();
0675 }
0676 
0677 QModelIndex LaunchConfigurationsModel::parent(const QModelIndex& child) const
0678 {
0679     if( child.isValid()  )
0680     {
0681         auto* t = static_cast<TreeItem*>( child.internalPointer() );
0682         if( t->parent )
0683         {
0684             return createIndex( t->parent->row, 0, t->parent );
0685         }
0686     }
0687     return QModelIndex();
0688 }
0689 
0690 int LaunchConfigurationsModel::rowCount(const QModelIndex& parent) const
0691 {
0692     if( parent.column() > 0 )
0693         return 0;
0694     if( parent.isValid() )
0695     {
0696         auto* t = static_cast<TreeItem*>( parent.internalPointer() );
0697         return t->children.count();
0698     } else
0699     {
0700         return topItems.count();
0701     }
0702     return 0;
0703 }
0704 
0705 QVariant LaunchConfigurationsModel::headerData(int section, Qt::Orientation orientation, int role) const
0706 {
0707     if( orientation == Qt::Horizontal && role == Qt::DisplayRole )
0708     {
0709         if( section == 0 )
0710         {
0711             return i18nc("@title:column Name of the Launch Configurations", "Name");
0712         } else if( section == 1 )
0713         {
0714             return i18nc("@title:column Type of the Launch Configurations (i.e. Python Application, C++ Application)", "Type");
0715         }
0716     }
0717     return QVariant();
0718 }
0719 
0720 Qt::ItemFlags LaunchConfigurationsModel::flags(const QModelIndex& index) const
0721 {
0722     if( index.isValid() && index.column() >= 0
0723         && index.column() < columnCount( QModelIndex() ) )
0724     {
0725         auto* t = static_cast<TreeItem*>( index.internalPointer() );
0726         if( t && ( dynamic_cast<LaunchItem*>( t ) || ( dynamic_cast<LaunchModeItem*>( t ) && index.column() == 1 ) ) )
0727         {
0728             return Qt::ItemFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable );
0729         } else if( t )
0730         {
0731             return Qt::ItemFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable );
0732         }
0733     }
0734     return Qt::NoItemFlags;
0735 }
0736 
0737 bool LaunchConfigurationsModel::setData(const QModelIndex& index, const QVariant& value, int role)
0738 {
0739     if( index.isValid() && index.parent().isValid() && role == Qt::EditRole )
0740     {
0741         if( index.row() >= 0 && index.row() < rowCount( index.parent() ) )
0742         {
0743             auto* t = dynamic_cast<LaunchItem*>( static_cast<TreeItem*>( index.internalPointer() ) );
0744             if( t )
0745             {
0746                 if( index.column() == 0 )
0747                 {
0748                     t->launch->setName( value.toString() );
0749                 } else if( index.column() == 1 )
0750                 {
0751                     if (t->launch->type()->id() != value.toString()) {
0752                         t->launch->setType( value.toString() );
0753                         QModelIndex p = indexForConfig(t->launch);
0754                         qCDebug(SHELL) << data(p);
0755                         beginRemoveRows( p, 0, t->children.count() );
0756                         qDeleteAll( t->children );
0757                         t->children.clear();
0758                         endRemoveRows();
0759                         addLaunchModeItemsForLaunchConfig( t );
0760                     }
0761                 }
0762                 emit dataChanged(index, index);
0763                 return true;
0764             }
0765             auto* lmi = dynamic_cast<LaunchModeItem*>( static_cast<TreeItem*>( index.internalPointer() ) );
0766             if( lmi )
0767             {
0768                 if( index.column() == 1 && index.data(Qt::EditRole)!=value)
0769                 {
0770                     LaunchConfiguration* l = configForIndex( index );
0771                     l->setLauncherForMode( lmi->mode->id(), value.toString() );
0772                     emit dataChanged(index, index);
0773                     return true;
0774                 }
0775             }
0776         }
0777     }
0778     return false;
0779 }
0780 
0781 ILaunchMode* LaunchConfigurationsModel::modeForIndex( const QModelIndex& idx ) const
0782 {
0783     if( idx.isValid() )
0784     {
0785         auto* item = dynamic_cast<LaunchModeItem*>( static_cast<TreeItem*>( idx.internalPointer() ) );
0786         if( item )
0787         {
0788             return item->mode;
0789         }
0790     }
0791     return nullptr;
0792 }
0793 
0794 LaunchConfiguration* LaunchConfigurationsModel::configForIndex(const QModelIndex& idx ) const
0795 {
0796     if( idx.isValid() )
0797     {
0798         auto* item = dynamic_cast<LaunchItem*>( static_cast<TreeItem*>( idx.internalPointer() ) );
0799         if( item )
0800         {
0801             return item->launch;
0802         }
0803         auto* lmitem = dynamic_cast<LaunchModeItem*>( static_cast<TreeItem*>( idx.internalPointer() ) );
0804         if( lmitem )
0805         {
0806             return dynamic_cast<LaunchItem*>( lmitem->parent )->launch;
0807         }
0808     }
0809     return nullptr;
0810 }
0811 
0812 QModelIndex LaunchConfigurationsModel::indexForConfig( LaunchConfiguration* l ) const
0813 {
0814     if( l )
0815     {
0816         TreeItem* tparent = topItems.at( 0 );
0817         if( l->project() )
0818         {
0819             for (TreeItem* t : topItems) {
0820                 auto* pi = dynamic_cast<ProjectItem*>( t );
0821                 if( pi && pi->project == l->project() )
0822                 {
0823                     tparent = t;
0824                     break;
0825                 }
0826             }
0827         }
0828 
0829         if( tparent )
0830         {
0831             for (TreeItem* c : qAsConst(tparent->children)) {
0832                 auto* li = dynamic_cast<LaunchItem*>( c );
0833                 if( li->launch && li->launch == l )
0834                 {
0835                     return index( c->row, 0, index( tparent->row, 0, QModelIndex() ) );
0836                 }
0837             }
0838         }
0839     }
0840     return QModelIndex();
0841 }
0842 
0843 
0844 void LaunchConfigurationsModel::deleteConfiguration( const QModelIndex& index )
0845 {
0846     auto* t = dynamic_cast<LaunchItem*>( static_cast<TreeItem*>( index.internalPointer() ) );
0847     if( !t )
0848         return;
0849     beginRemoveRows( parent( index ), index.row(), index.row() );
0850     t->parent->children.removeAll( t );
0851     Core::self()->runControllerInternal()->removeLaunchConfiguration( t->launch );
0852     endRemoveRows();
0853 }
0854 
0855 void LaunchConfigurationsModel::createConfiguration(const QModelIndex& parent )
0856 {
0857     if(!Core::self()->runController()->launchConfigurationTypes().isEmpty())
0858     {
0859         auto* t = static_cast<TreeItem*>( parent.internalPointer() );
0860         auto* ti = dynamic_cast<ProjectItem*>( t );
0861 
0862         LaunchConfigurationType* type = Core::self()->runController()->launchConfigurationTypes().at(0);
0863         QPair<QString,QString> launcher = qMakePair( type->launchers().at( 0 )->supportedModes().at(0), type->launchers().at( 0 )->id() );
0864         IProject* p = ( ti ? ti->project : nullptr );
0865         ILaunchConfiguration* l = Core::self()->runController()->createLaunchConfiguration( type, launcher, p );
0866 
0867         addConfiguration(l, parent);
0868     }
0869 }
0870 
0871 void LaunchConfigurationsModel::addConfiguration(ILaunchConfiguration* l, const QModelIndex& parent)
0872 {
0873     if( parent.isValid() )
0874     {
0875         beginInsertRows( parent, rowCount( parent ), rowCount( parent ) );
0876         addItemForLaunchConfig( dynamic_cast<LaunchConfiguration*>( l ) );
0877         endInsertRows();
0878     }
0879     else
0880     {
0881         delete l;
0882         Q_ASSERT(false && "could not add the configuration");
0883     }
0884 }
0885 
0886 IProject* LaunchConfigurationsModel::projectForIndex(const QModelIndex& idx)
0887 {
0888     if(idx.parent().isValid()) {
0889         return projectForIndex(idx.parent());
0890     } else {
0891         const auto* item = dynamic_cast<const ProjectItem*>(topItems[idx.row()]);
0892         return item ? item->project : nullptr;
0893     }
0894 }
0895 
0896 LaunchConfigPagesContainer::LaunchConfigPagesContainer( const QList<LaunchConfigurationPageFactory*>& factories, QWidget* parent )
0897     : QWidget(parent)
0898 {
0899     setLayout( new QVBoxLayout( this ) );
0900     layout()->setContentsMargins( 0, 0, 0, 0 );
0901     QWidget* parentwidget = this;
0902     QTabWidget* tab = nullptr;
0903     if( factories.count() > 1 )
0904     {
0905         tab = new QTabWidget( this );
0906         parentwidget = tab;
0907         layout()->addWidget( tab );
0908     }
0909     for (LaunchConfigurationPageFactory* fac : factories) {
0910         LaunchConfigurationPage* page = fac->createWidget( parentwidget );
0911         if ( page->layout() ) {
0912             // remove margins for single page, reset margins for tabbed display
0913             const int pageMargin = tab ? -1 : 0;
0914             page->layout()->setContentsMargins(pageMargin, pageMargin, pageMargin, pageMargin);
0915         }
0916         pages.append( page );
0917         connect( page, &LaunchConfigurationPage::changed, this, &LaunchConfigPagesContainer::changed );
0918         if( tab ) {
0919             tab->addTab( page, page->icon(), page->title() );
0920         } else
0921         {
0922             layout()->addWidget( page );
0923         }
0924     }
0925 }
0926 
0927 void LaunchConfigPagesContainer::setLaunchConfiguration( KDevelop::LaunchConfiguration* l )
0928 {
0929     config = l;
0930     for (LaunchConfigurationPage* p : qAsConst(pages)) {
0931         p->loadFromConfiguration( config->config(), config->project() );
0932     }
0933 }
0934 
0935 void LaunchConfigPagesContainer::save()
0936 {
0937     for (LaunchConfigurationPage* p : qAsConst(pages)) {
0938         p->saveToConfiguration( config->config() );
0939     }
0940     config->config().sync();
0941 }
0942 
0943 
0944 QWidget* LaunchConfigurationModelDelegate::createEditor ( QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index ) const
0945 {
0946     const auto* model = static_cast<const LaunchConfigurationsModel*>(index.model());
0947     ILaunchMode* mode = model->modeForIndex( index );
0948     LaunchConfiguration* config = model->configForIndex( index );
0949     if( index.column() == 1 && mode && config )
0950     {
0951         auto* box = new KComboBox( parent );
0952         const QList<ILauncher*> launchers = config->type()->launchers();
0953         for (auto* launcher : launchers) {
0954             if (launcher->supportedModes().contains(mode->id())) {
0955                 box->addItem(launcher->name(), launcher->id());
0956             }
0957         }
0958         return box;
0959     } else if( !mode && config && index.column() == 1 )
0960     {
0961         auto* box = new KComboBox( parent );
0962         const QList<LaunchConfigurationType*> types = Core::self()->runController()->launchConfigurationTypes();
0963         for (auto* type : types) {
0964             box->addItem(type->name(), type->id());
0965         }
0966         return box;
0967     }
0968     return QStyledItemDelegate::createEditor ( parent, option, index );
0969 }
0970 
0971 void LaunchConfigurationModelDelegate::setEditorData ( QWidget* editor, const QModelIndex& index ) const
0972 {
0973     const auto* model = static_cast<const LaunchConfigurationsModel*>(index.model());
0974     LaunchConfiguration* config = model->configForIndex( index );
0975     if( index.column() == 1 && config )
0976     {
0977         auto* box = qobject_cast<KComboBox*>( editor );
0978         box->setCurrentIndex( box->findData( index.data( Qt::EditRole ) ) );
0979     }
0980     else
0981     {
0982         QStyledItemDelegate::setEditorData ( editor, index );
0983     }
0984 }
0985 
0986 void LaunchConfigurationModelDelegate::setModelData ( QWidget* editor, QAbstractItemModel* model, const QModelIndex& index ) const
0987 {
0988     auto* lmodel = static_cast<LaunchConfigurationsModel*>(model);
0989     LaunchConfiguration* config = lmodel->configForIndex( index );
0990     if( index.column() == 1 && config )
0991     {
0992         auto* box = qobject_cast<KComboBox*>( editor );
0993         lmodel->setData( index, box->itemData( box->currentIndex() ) );
0994     }
0995     else
0996     {
0997         QStyledItemDelegate::setModelData ( editor, model, index );
0998     }
0999 }
1000 
1001 void LaunchConfigurationDialog::launchModeChanged(int item)
1002 {
1003     QModelIndex index = tree->currentIndex();
1004     if(debugger->isVisible() && item>=0)
1005         tree->model()->setData(index.sibling(index.row(), 1), debugger->itemData(item), Qt::EditRole);
1006 }
1007 
1008 }
1009 
1010 #include "moc_launchconfigurationdialog.cpp"