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"