diff --git a/documentation/documentationview.cpp b/documentation/documentationview.cpp index 7928c7b9b..49247a970 100644 --- a/documentation/documentationview.cpp +++ b/documentation/documentationview.cpp @@ -1,354 +1,354 @@ /* Copyright 2009 Aleix Pol Gonzalez Copyright 2010 Benjamin Port This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. 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 "documentationview.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "documentationfindwidget.h" #include "debug.h" Q_LOGGING_CATEGORY(DOCUMENTATION, "kdevplatform.documentation") using namespace KDevelop; DocumentationView::DocumentationView(QWidget* parent, ProvidersModel* model) : QWidget(parent), mProvidersModel(model) { setWindowIcon(QIcon::fromTheme(QStringLiteral("documentation"), windowIcon())); setWindowTitle(i18n("Documentation")); setLayout(new QVBoxLayout(this)); layout()->setMargin(0); layout()->setSpacing(0); //TODO: clean this up, simply use addAction as that will create a toolbar automatically // use custom QAction's with createWidget for mProviders and mIdentifiers mActions = new KToolBar(this); // set window title so the QAction from QToolBar::toggleViewAction gets a proper name set mActions->setWindowTitle(i18n("Documentation Tool Bar")); mActions->setToolButtonStyle(Qt::ToolButtonIconOnly); int iconSize = style()->pixelMetric(QStyle::PM_SmallIconSize); mActions->setIconSize(QSize(iconSize, iconSize)); mFindDoc = new DocumentationFindWidget; mFindDoc->hide(); mBack = mActions->addAction(QIcon::fromTheme(QStringLiteral("go-previous")), i18n("Back")); mForward = mActions->addAction(QIcon::fromTheme(QStringLiteral("go-next")), i18n("Forward")); mFind = mActions->addAction(QIcon::fromTheme(QStringLiteral("edit-find")), i18n("Find"), mFindDoc, SLOT(startSearch())); mActions->addSeparator(); mActions->addAction(QIcon::fromTheme(QStringLiteral("go-home")), i18n("Home"), this, SLOT(showHome())); mProviders = new QComboBox(mActions); mIdentifiers = new QLineEdit(mActions); mIdentifiers->setClearButtonEnabled(true); mIdentifiers->setPlaceholderText(i18n("Search...")); mIdentifiers->setCompleter(new QCompleter(mIdentifiers)); // mIdentifiers->completer()->setCompletionMode(QCompleter::UnfilteredPopupCompletion); mIdentifiers->completer()->setCaseSensitivity(Qt::CaseInsensitive); /* vertical size policy should be left to the style. */ mIdentifiers->setSizePolicy(QSizePolicy::Expanding, mIdentifiers->sizePolicy().verticalPolicy()); connect(mIdentifiers->completer(), static_cast(&QCompleter::activated), this, &DocumentationView::changedSelection); QWidget::setTabOrder(mProviders, mIdentifiers); mActions->addWidget(mProviders); mActions->addWidget(mIdentifiers); mBack->setEnabled(false); mForward->setEnabled(false); connect(mBack, &QAction::triggered, this, &DocumentationView::browseBack); connect(mForward, &QAction::triggered, this, &DocumentationView::browseForward); mCurrent = mHistory.end(); layout()->addWidget(mActions); layout()->addWidget(new QWidget(this)); layout()->addWidget(mFindDoc); setFocusProxy(mIdentifiers); QMetaObject::invokeMethod(this, "initialize", Qt::QueuedConnection); } void DocumentationView::initialize() { mProviders->setModel(mProvidersModel); connect(mProviders, static_cast(&QComboBox::activated), this, &DocumentationView::changedProvider); foreach (IDocumentationProvider* p, mProvidersModel->providers()) { // can't use new signal/slot syntax here, IDocumentation is not a QObject connect(dynamic_cast(p), SIGNAL(addHistory(KDevelop::IDocumentation::Ptr)), this, SLOT(addHistory(KDevelop::IDocumentation::Ptr))); } connect(mProvidersModel, &ProvidersModel::providersChanged, this, &DocumentationView::emptyHistory); if (mProvidersModel->rowCount() > 0) { changedProvider(0); } } void DocumentationView::browseBack() { - mCurrent--; + --mCurrent; mBack->setEnabled(mCurrent != mHistory.begin()); mForward->setEnabled(true); updateView(); } void DocumentationView::browseForward() { - mCurrent++; + ++mCurrent; mForward->setEnabled(mCurrent+1 != mHistory.end()); mBack->setEnabled(true); updateView(); } void DocumentationView::showHome() { auto prov = mProvidersModel->provider(mProviders->currentIndex()); showDocumentation(prov->homePage()); } void DocumentationView::changedSelection(const QModelIndex& idx) { if (idx.isValid()) { IDocumentationProvider* prov = mProvidersModel->provider(mProviders->currentIndex()); auto doc = prov->documentationForIndex(idx); if (doc) { showDocumentation(doc); } } } void DocumentationView::showDocumentation(const IDocumentation::Ptr& doc) { qCDebug(DOCUMENTATION) << "showing" << doc->name(); addHistory(doc); updateView(); } void DocumentationView::addHistory(const IDocumentation::Ptr& doc) { mBack->setEnabled(!mHistory.isEmpty()); mForward->setEnabled(false); // clear all history following the current item, unless we're already // at the end (otherwise this code crashes when history is empty, which // happens when addHistory is first called on startup to add the // homepage) if (mCurrent+1 < mHistory.end()) { mHistory.erase(mCurrent+1, mHistory.end()); } mHistory.append(doc); mCurrent = mHistory.end()-1; // NOTE: we assume an existing widget was used to navigate somewhere // but this history entry actually contains the new info for the // title... this is ugly and should be refactored somehow if (mIdentifiers->completer()->model() == (*mCurrent)->provider()->indexModel()) { mIdentifiers->setText((*mCurrent)->name()); } } void DocumentationView::emptyHistory() { mHistory.clear(); mCurrent = mHistory.end(); mBack->setEnabled(false); mForward->setEnabled(false); if (mProviders->count() > 0) { mProviders->setCurrentIndex(0); changedProvider(0); } } void DocumentationView::updateView() { mProviders->setCurrentIndex(mProvidersModel->rowForProvider((*mCurrent)->provider())); mIdentifiers->completer()->setModel((*mCurrent)->provider()->indexModel()); mIdentifiers->setText((*mCurrent)->name()); QLayoutItem* lastview = layout()->takeAt(1); Q_ASSERT(lastview); if (lastview->widget()->parent() == this) { lastview->widget()->deleteLater(); } delete lastview; mFindDoc->setEnabled(false); QWidget* w = (*mCurrent)->documentationWidget(mFindDoc, this); Q_ASSERT(w); QWidget::setTabOrder(mIdentifiers, w); mFind->setEnabled(mFindDoc->isEnabled()); if (!mFindDoc->isEnabled()) { mFindDoc->hide(); } QLayoutItem* findWidget = layout()->takeAt(1); layout()->addWidget(w); layout()->addItem(findWidget); } void DocumentationView::changedProvider(int row) { mIdentifiers->completer()->setModel(mProvidersModel->provider(row)->indexModel()); mIdentifiers->clear(); showHome(); } ////////////// ProvidersModel ////////////////// ProvidersModel::ProvidersModel(QObject* parent) : QAbstractListModel(parent) , mProviders(ICore::self()->documentationController()->documentationProviders()) { connect(ICore::self()->pluginController(), &IPluginController::unloadingPlugin, this, &ProvidersModel::unloaded); connect(ICore::self()->pluginController(), &IPluginController::pluginLoaded, this, &ProvidersModel::loaded); connect(ICore::self()->documentationController(), &IDocumentationController::providersChanged, this, &ProvidersModel::reloadProviders); } void ProvidersModel::reloadProviders() { beginResetModel(); mProviders = ICore::self()->documentationController()->documentationProviders(); std::sort(mProviders.begin(), mProviders.end(), [](const KDevelop::IDocumentationProvider* a, const KDevelop::IDocumentationProvider* b) { return a->name() < b->name(); }); endResetModel(); emit providersChanged(); } QVariant ProvidersModel::data(const QModelIndex& index, int role) const { if (index.row() >= mProviders.count() || index.row() < 0) return QVariant(); QVariant ret; switch (role) { case Qt::DisplayRole: ret = provider(index.row())->name(); break; case Qt::DecorationRole: ret = provider(index.row())->icon(); break; } return ret; } void ProvidersModel::addProvider(IDocumentationProvider* provider) { if (!provider || mProviders.contains(provider)) return; int pos = 0; while (pos < mProviders.size() && mProviders[pos]->name() < provider->name()) ++pos; beginInsertRows(QModelIndex(), pos, pos); mProviders.insert(pos, provider); endInsertRows(); emit providersChanged(); } void ProvidersModel::removeProvider(IDocumentationProvider* provider) { int pos; if (!provider || (pos = mProviders.indexOf(provider)) < 0) return; beginRemoveRows(QModelIndex(), pos, pos); mProviders.removeAt(pos); endRemoveRows(); emit providersChanged(); } void ProvidersModel::unloaded(IPlugin* plugin) { removeProvider(plugin->extension()); IDocumentationProviderProvider* providerProvider = plugin->extension(); if (providerProvider) { foreach(IDocumentationProvider* provider, providerProvider->providers()) removeProvider(provider); } } void ProvidersModel::loaded(IPlugin* plugin) { addProvider(plugin->extension()); IDocumentationProviderProvider* providerProvider = plugin->extension(); if (providerProvider) { foreach(IDocumentationProvider* provider, providerProvider->providers()) addProvider(provider); } } int ProvidersModel::rowCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : mProviders.count(); } int ProvidersModel::rowForProvider(IDocumentationProvider* provider) { return mProviders.indexOf(provider); } IDocumentationProvider* ProvidersModel::provider(int pos) const { return mProviders[pos]; } QList ProvidersModel::providers() { return mProviders; } diff --git a/plugins/patchreview/patchreviewtoolview.cpp b/plugins/patchreview/patchreviewtoolview.cpp index eff2bc6c2..c088414ac 100644 --- a/plugins/patchreview/patchreviewtoolview.cpp +++ b/plugins/patchreview/patchreviewtoolview.cpp @@ -1,602 +1,602 @@ /*************************************************************************** Copyright 2006-2009 David Nolden ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "patchreviewtoolview.h" #include "localpatchsource.h" #include "patchreview.h" #include "debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef WITH_PURPOSE #include #include #endif using namespace KDevelop; class PatchFilesModel : public VcsFileChangesModel { Q_OBJECT public: PatchFilesModel( QObject *parent, bool allowSelection ) : VcsFileChangesModel( parent, allowSelection ) { }; enum ItemRoles { HunksNumberRole = LastItemRole+1 }; public slots: void updateState( const KDevelop::VcsStatusInfo &status, unsigned hunksNum ) { int row = VcsFileChangesModel::updateState( invisibleRootItem(), status ); if ( row == -1 ) return; QStandardItem *item = invisibleRootItem()->child( row, 0 ); setFileInfo( item, hunksNum ); item->setData( QVariant( hunksNum ), HunksNumberRole ); } void updateState( const KDevelop::VcsStatusInfo &status ) { int row = VcsFileChangesModel::updateState( invisibleRootItem(), status ); if ( row == -1 ) return; QStandardItem *item = invisibleRootItem()->child( row, 0 ); setFileInfo( invisibleRootItem()->child( row, 0 ), item->data( HunksNumberRole ).toUInt() ); } private: void setFileInfo( QStandardItem *item, unsigned int hunksNum ) { const auto url = item->index().data(VcsFileChangesModel::UrlRole).toUrl(); const QString path = ICore::self()->projectController()->prettyFileName(url, KDevelop::IProjectController::FormatPlain); const QString newText = i18ncp( "%1: number of changed hunks, %2: file name", "%2 (1 hunk)", "%2 (%1 hunks)", hunksNum, path); item->setText( newText ); } }; PatchReviewToolView::PatchReviewToolView( QWidget* parent, PatchReviewPlugin* plugin ) : QWidget( parent ), m_resetCheckedUrls( true ), m_plugin( plugin ) { connect( m_plugin->finishReviewAction(), &QAction::triggered, this, &PatchReviewToolView::finishReview ); connect( plugin, &PatchReviewPlugin::patchChanged, this, &PatchReviewToolView::patchChanged ); connect( plugin, &PatchReviewPlugin::startingNewReview, this, &PatchReviewToolView::startingNewReview ); connect( ICore::self()->documentController(), &IDocumentController::documentActivated, this, &PatchReviewToolView::documentActivated ); Sublime::MainWindow* w = dynamic_cast( ICore::self()->uiController()->activeMainWindow() ); connect(w, &Sublime::MainWindow::areaChanged, m_plugin, &PatchReviewPlugin::areaChanged); showEditDialog(); patchChanged(); } void PatchReviewToolView::resizeEvent(QResizeEvent* ev) { bool vertical = (width() < height()); m_editPatch.buttonsLayout->setDirection(vertical ? QBoxLayout::TopToBottom : QBoxLayout::LeftToRight); m_editPatch.contentLayout->setDirection(vertical ? QBoxLayout::TopToBottom : QBoxLayout::LeftToRight); m_editPatch.buttonsSpacer->changeSize(vertical ? 0 : 40, 0, QSizePolicy::Fixed, QSizePolicy::Fixed); QWidget::resizeEvent(ev); if(m_customWidget) { m_editPatch.contentLayout->removeWidget( m_customWidget ); m_editPatch.contentLayout->insertWidget(0, m_customWidget ); } } void PatchReviewToolView::startingNewReview() { m_resetCheckedUrls = true; } void PatchReviewToolView::patchChanged() { fillEditFromPatch(); kompareModelChanged(); #ifdef WITH_PURPOSE IPatchSource::Ptr p = m_plugin->patch(); if (p) { m_exportMenu->model()->setInputData(QJsonObject { { QStringLiteral("urls"), QJsonArray { p->file().toString() } }, { QStringLiteral("mimeType"), { QStringLiteral("text/x-patch") } }, { QStringLiteral("localBaseDir"), { p->baseDir().toString() } } }); } #endif } PatchReviewToolView::~PatchReviewToolView() { } LocalPatchSource* PatchReviewToolView::GetLocalPatchSource() { IPatchSource::Ptr ips = m_plugin->patch(); if ( !ips ) return nullptr; return dynamic_cast( ips.data() ); } void PatchReviewToolView::fillEditFromPatch() { IPatchSource::Ptr ipatch = m_plugin->patch(); if ( !ipatch ) return; m_editPatch.cancelReview->setVisible( ipatch->canCancel() ); m_fileModel->setIsCheckbable( m_plugin->patch()->canSelectFiles() ); if( m_customWidget ) { qCDebug(PLUGIN_PATCHREVIEW) << "removing custom widget"; m_customWidget->hide(); m_editPatch.contentLayout->removeWidget( m_customWidget ); } m_customWidget = ipatch->customWidget(); if( m_customWidget ) { m_editPatch.contentLayout->insertWidget( 0, m_customWidget ); m_customWidget->show(); qCDebug(PLUGIN_PATCHREVIEW) << "got custom widget"; } bool showTests = false; IProject* project = nullptr; QMap files = ipatch->additionalSelectableFiles(); QMap::const_iterator it = files.constBegin(); for (; it != files.constEnd(); ++it) { project = ICore::self()->projectController()->findProjectForUrl(it.key()); if (project && !ICore::self()->testController()->testSuitesForProject(project).isEmpty()) { showTests = true; break; } } m_editPatch.testsButton->setVisible(showTests); m_editPatch.testProgressBar->hide(); } void PatchReviewToolView::slotAppliedChanged( int newState ) { if ( LocalPatchSource* lpatch = GetLocalPatchSource() ) { lpatch->setAlreadyApplied( newState == Qt::Checked ); m_plugin->notifyPatchChanged(); } } void PatchReviewToolView::showEditDialog() { m_editPatch.setupUi( this ); bool allowSelection = m_plugin->patch() && m_plugin->patch()->canSelectFiles(); m_fileModel = new PatchFilesModel( this, allowSelection ); m_fileSortProxyModel = new VcsFileChangesSortProxyModel(this); m_fileSortProxyModel->setSourceModel(m_fileModel); m_fileSortProxyModel->sort(1); m_fileSortProxyModel->setDynamicSortFilter(true); m_editPatch.filesList->setModel( m_fileSortProxyModel ); m_editPatch.filesList->header()->hide(); m_editPatch.filesList->setRootIsDecorated( false ); m_editPatch.filesList->setContextMenuPolicy(Qt::CustomContextMenu); connect(m_editPatch.filesList, &QTreeView::customContextMenuRequested, this, &PatchReviewToolView::customContextMenuRequested); connect(m_fileModel, &PatchFilesModel::itemChanged, this, &PatchReviewToolView::fileItemChanged); m_editPatch.previousFile->setIcon( QIcon::fromTheme( QStringLiteral("arrow-left") ) ); m_editPatch.previousHunk->setIcon( QIcon::fromTheme( QStringLiteral("arrow-up") ) ); m_editPatch.nextHunk->setIcon( QIcon::fromTheme( QStringLiteral("arrow-down") ) ); m_editPatch.nextFile->setIcon( QIcon::fromTheme( QStringLiteral("arrow-right") ) ); m_editPatch.cancelReview->setIcon( QIcon::fromTheme( QStringLiteral("dialog-cancel") ) ); m_editPatch.updateButton->setIcon( QIcon::fromTheme( QStringLiteral("view-refresh") ) ); m_editPatch.testsButton->setIcon( QIcon::fromTheme( QStringLiteral("preflight-verifier") ) ); m_editPatch.finishReview->setDefaultAction(m_plugin->finishReviewAction()); #ifdef WITH_PURPOSE m_exportMenu = new Purpose::Menu(this); connect(m_exportMenu, &Purpose::Menu::finished, this, [](const QJsonObject &output, int error, const QString &message) { if (error==0) { KMessageBox::information(nullptr, i18n("You can find the new request at:
%1
", output["url"].toString()), QString(), QString(), KMessageBox::AllowLink); } else { QMessageBox::warning(nullptr, i18n("Error exporting"), i18n("Couldn't export the patch.\n%1", message)); } }); m_exportMenu->model()->setPluginType("Export"); m_editPatch.exportReview->setMenu( m_exportMenu ); #else m_editPatch.exportReview->setEnabled(false); #endif connect( m_editPatch.previousHunk, &QToolButton::clicked, this, &PatchReviewToolView::prevHunk ); connect( m_editPatch.nextHunk, &QToolButton::clicked, this, &PatchReviewToolView::nextHunk ); connect( m_editPatch.previousFile, &QToolButton::clicked, this, &PatchReviewToolView::prevFile ); connect( m_editPatch.nextFile, &QToolButton::clicked, this, &PatchReviewToolView::nextFile ); connect( m_editPatch.filesList, &QTreeView::activated , this, &PatchReviewToolView::fileDoubleClicked ); connect( m_editPatch.cancelReview, &QPushButton::clicked, m_plugin, &PatchReviewPlugin::cancelReview ); //connect( m_editPatch.cancelButton, SIGNAL(pressed()), this, SLOT(slotEditCancel()) ); //connect( this, SIGNAL(finished(int)), this, SLOT(slotEditDialogFinished(int)) ); connect( m_editPatch.updateButton, &QPushButton::clicked, m_plugin, &PatchReviewPlugin::forceUpdate ); connect( m_editPatch.testsButton, &QPushButton::clicked, this, &PatchReviewToolView::runTests ); m_selectAllAction = new QAction(QIcon::fromTheme(QStringLiteral("edit-select-all")), i18n("Select All"), this ); connect( m_selectAllAction, &QAction::triggered, this, &PatchReviewToolView::selectAll ); m_deselectAllAction = new QAction( i18n("Deselect All"), this ); connect( m_deselectAllAction, &QAction::triggered, this, &PatchReviewToolView::deselectAll ); } void PatchReviewToolView::customContextMenuRequested(const QPoint& ) { QList urls; QModelIndexList selectionIdxs = m_editPatch.filesList->selectionModel()->selectedIndexes(); foreach(const QModelIndex& idx, selectionIdxs) { urls += idx.data(KDevelop::VcsFileChangesModel::UrlRole).toUrl(); } QPointer menu = new QMenu(m_editPatch.filesList); QList extensions; if(!urls.isEmpty()) { KDevelop::FileContext context(urls); extensions = ICore::self()->pluginController()->queryPluginsForContextMenuExtensions( &context ); } QList vcsActions; foreach( const ContextMenuExtension& ext, extensions ) { vcsActions += ext.actions(ContextMenuExtension::VcsGroup); } menu->addAction(m_selectAllAction); menu->addAction(m_deselectAllAction); menu->addActions(vcsActions); if ( !menu->isEmpty() ) { menu->exec(QCursor::pos()); } delete menu; } void PatchReviewToolView::nextHunk() { IDocument* current = ICore::self()->documentController()->activeDocument(); if(current && current->textDocument()) m_plugin->seekHunk( true, current->textDocument()->url() ); } void PatchReviewToolView::prevHunk() { IDocument* current = ICore::self()->documentController()->activeDocument(); if(current && current->textDocument()) m_plugin->seekHunk( false, current->textDocument()->url() ); } void PatchReviewToolView::seekFile(bool forwards) { if(!m_plugin->patch()) return; QList checkedUrls = m_fileModel->checkedUrls(); QList allUrls = m_fileModel->urls(); IDocument* current = ICore::self()->documentController()->activeDocument(); if(!current || checkedUrls.empty()) return; qCDebug(PLUGIN_PATCHREVIEW) << "seeking direction" << forwards; int currentIndex = allUrls.indexOf(current->url()); QUrl newUrl; if((forwards && current->url() == checkedUrls.back()) || (!forwards && current->url() == checkedUrls[0])) { newUrl = m_plugin->patch()->file(); qCDebug(PLUGIN_PATCHREVIEW) << "jumping to patch"; } else if(current->url() == m_plugin->patch()->file() || currentIndex == -1) { if(forwards) newUrl = checkedUrls[0]; else newUrl = checkedUrls.back(); qCDebug(PLUGIN_PATCHREVIEW) << "jumping from patch"; } else { QSet checkedUrlsSet( checkedUrls.toSet() ); for(int offset = 1; offset < allUrls.size(); ++offset) { int pos; if(forwards) { pos = (currentIndex + offset) % allUrls.size(); }else{ pos = currentIndex - offset; if(pos < 0) pos += allUrls.size(); } if(checkedUrlsSet.contains(allUrls[pos])) { newUrl = allUrls[pos]; break; } } } if(newUrl.isValid()) { open( newUrl, true ); }else{ qCDebug(PLUGIN_PATCHREVIEW) << "found no valid target url"; } } void PatchReviewToolView::open( const QUrl& url, bool activate ) const { qCDebug(PLUGIN_PATCHREVIEW) << "activating url" << url; // If the document is already open in this area, just re-activate it if(KDevelop::IDocument* doc = ICore::self()->documentController()->documentForUrl(url)) { foreach(Sublime::View* view, ICore::self()->uiController()->activeArea()->views()) { if(view->document() == dynamic_cast(doc)) { if (activate) { // use openDocument() for the activation so that the document is added to File/Open Recent. ICore::self()->documentController()->openDocument(doc->url(), KTextEditor::Range::invalid()); } return; } } } QStandardItem* item = m_fileModel->itemForUrl( url ); IDocument* buddyDoc = nullptr; if (m_plugin->patch() && item) { for (int preRow = item->row() - 1; preRow >= 0; --preRow) { QStandardItem* preItem = m_fileModel->item(preRow); if (!m_fileModel->isCheckable() || preItem->checkState() == Qt::Checked) { // found valid predecessor, take it as buddy buddyDoc = ICore::self()->documentController()->documentForUrl(preItem->index().data(VcsFileChangesModel::UrlRole).toUrl()); if (buddyDoc) { break; } } } if (!buddyDoc) { buddyDoc = ICore::self()->documentController()->documentForUrl(m_plugin->patch()->file()); } } // we simplify and assume that documents to be opened without activating them also need not be // added to the Files/Open Recent menu. IDocument* newDoc = ICore::self()->documentController()->openDocument(url, KTextEditor::Range::invalid(), activate ? IDocumentController::DefaultMode : IDocumentController::DoNotActivate|IDocumentController::DoNotAddToRecentOpen, QString(), buddyDoc); KTextEditor::View* view = nullptr; if(newDoc) view = newDoc->activeTextView(); if(view && view->cursorPosition().line() == 0) m_plugin->seekHunk( true, url ); } void PatchReviewToolView::fileItemChanged( QStandardItem* item ) { if (item->column() != 0 || !m_plugin->patch()) return; QUrl url = item->index().data(VcsFileChangesModel::UrlRole).toUrl(); if (url.isEmpty()) return; KDevelop::IDocument* doc = ICore::self()->documentController()->documentForUrl(url); if(m_fileModel->isCheckable() && item->checkState() != Qt::Checked) { // The file was deselected, so eventually close it if(doc && doc->state() == IDocument::Clean) { foreach(Sublime::View* view, ICore::self()->uiController()->activeArea()->views()) { if(view->document() == dynamic_cast(doc)) { ICore::self()->uiController()->activeArea()->closeView(view); return; } } } } else if (!doc) { // Maybe the file was unchecked before, or it was just loaded. open( url, false ); } } void PatchReviewToolView::nextFile() { seekFile(true); } void PatchReviewToolView::prevFile() { seekFile(false); } void PatchReviewToolView::deselectAll() { m_fileModel->setAllChecked(false); } void PatchReviewToolView::selectAll() { m_fileModel->setAllChecked(true); } void PatchReviewToolView::finishReview() { QList selectedUrls = m_fileModel->checkedUrls(); qCDebug(PLUGIN_PATCHREVIEW) << "finishing review with" << selectedUrls; m_plugin->finishReview( selectedUrls ); } void PatchReviewToolView::fileDoubleClicked( const QModelIndex& idx ) { const QUrl file = idx.data(VcsFileChangesModel::UrlRole).toUrl(); open( file, true ); } void PatchReviewToolView::kompareModelChanged() { QList oldCheckedUrls = m_fileModel->checkedUrls(); m_fileModel->clear(); if ( !m_plugin->modelList() ) return; QMap additionalUrls = m_plugin->patch()->additionalSelectableFiles(); const Diff2::DiffModelList* models = m_plugin->modelList()->models(); if( models ) { Diff2::DiffModelList::const_iterator it = models->constBegin(); for(; it != models->constEnd(); ++it ) { Diff2::DifferenceList * diffs = ( *it )->differences(); int cnt = 0; if ( diffs ) cnt = diffs->count(); const QUrl file = m_plugin->urlForFileModel( *it ); if( file.isLocalFile() && !QFileInfo( file.toLocalFile() ).isReadable() ) continue; VcsStatusInfo status; status.setUrl( file ); status.setState( cnt>0 ? VcsStatusInfo::ItemModified : VcsStatusInfo::ItemUpToDate ); m_fileModel->updateState( status, cnt ); } } - for( QMap::const_iterator it = additionalUrls.constBegin(); it != additionalUrls.constEnd(); it++ ) { + for( QMap::const_iterator it = additionalUrls.constBegin(); it != additionalUrls.constEnd(); ++it ) { VcsStatusInfo status; status.setUrl( it.key() ); status.setState( it.value() ); m_fileModel->updateState( status ); } if(!m_resetCheckedUrls) m_fileModel->setCheckedUrls(oldCheckedUrls); else m_resetCheckedUrls = false; m_editPatch.filesList->resizeColumnToContents( 0 ); // Eventually select the active document documentActivated( ICore::self()->documentController()->activeDocument() ); } void PatchReviewToolView::documentActivated( IDocument* doc ) { if( !doc ) return; if ( !m_plugin->modelList() ) return; const auto matches = m_fileSortProxyModel->match( m_fileSortProxyModel->index(0, 0), VcsFileChangesModel::UrlRole, doc->url(), 1, Qt::MatchExactly); m_editPatch.filesList->setCurrentIndex(matches.value(0)); } void PatchReviewToolView::runTests() { IPatchSource::Ptr ipatch = m_plugin->patch(); if ( !ipatch ) { return; } IProject* project = nullptr; QMap files = ipatch->additionalSelectableFiles(); QMap::const_iterator it = files.constBegin(); for (; it != files.constEnd(); ++it) { project = ICore::self()->projectController()->findProjectForUrl(it.key()); if (project) { break; } } if (!project) { return; } m_editPatch.testProgressBar->setFormat(i18n("Running tests: %p%")); m_editPatch.testProgressBar->setValue(0); m_editPatch.testProgressBar->show(); ProjectTestJob* job = new ProjectTestJob(project, this); connect(job, &ProjectTestJob::finished, this, &PatchReviewToolView::testJobResult); connect(job, SIGNAL(percent(KJob*,ulong)), this, SLOT(testJobPercent(KJob*,ulong))); ICore::self()->runController()->registerJob(job); } void PatchReviewToolView::testJobPercent(KJob* job, ulong percent) { Q_UNUSED(job); m_editPatch.testProgressBar->setValue(percent); } void PatchReviewToolView::testJobResult(KJob* job) { ProjectTestJob* testJob = qobject_cast(job); if (!testJob) { return; } ProjectTestResult result = testJob->testResult(); QString format; if (result.passed > 0 && result.failed == 0 && result.error == 0) { format = i18np("Test passed", "All %1 tests passed", result.passed); } else { format = i18n("Test results: %1 passed, %2 failed, %3 errors", result.passed, result.failed, result.error); } m_editPatch.testProgressBar->setFormat(format); // Needed because some test jobs may raise their own output views ICore::self()->uiController()->raiseToolView(this); } #include "patchreviewtoolview.moc" diff --git a/plugins/subversion/kdevsvncpp/client_status.cpp b/plugins/subversion/kdevsvncpp/client_status.cpp index 538b61889..66c215b65 100644 --- a/plugins/subversion/kdevsvncpp/client_status.cpp +++ b/plugins/subversion/kdevsvncpp/client_status.cpp @@ -1,418 +1,418 @@ /* * ==================================================================== * Copyright (c) 2002-2009 The RapidSvn Group. All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program (in the file GPL.txt. * If not, see . * * This software consists of voluntary contributions made by many * individuals. For exact contribution history, see the revision * history and logs, available at http://rapidsvn.tigris.org/. * ==================================================================== */ #if defined( _MSC_VER) && _MSC_VER <= 1200 #pragma warning( disable: 4786 )// debug symbol truncated #endif // Stdlib (for strcmp) #include "string.h" // Subversion api #include "svn_client.h" #include "svn_sorts.h" //#include "svn_utf.h" // svncpp #include "kdevsvncpp/client.hpp" #include "kdevsvncpp/dirent.hpp" #include "kdevsvncpp/exception.hpp" #include "kdevsvncpp/info.hpp" #include "kdevsvncpp/pool.hpp" #include "kdevsvncpp/status.hpp" #include "kdevsvncpp/targets.hpp" #include "kdevsvncpp/url.hpp" namespace svn { static svn_error_t * logReceiver(void *baton, apr_hash_t * changedPaths, svn_revnum_t rev, const char *author, const char *date, const char *msg, apr_pool_t * pool) { LogEntries * entries = (LogEntries *) baton; entries->insert(entries->begin(), LogEntry(rev, author, date, msg)); if (changedPaths != nullptr) { LogEntry &entry = entries->front(); for (apr_hash_index_t *hi = apr_hash_first(pool, changedPaths); hi != nullptr; hi = apr_hash_next(hi)) { char *path; void *val; apr_hash_this(hi, (const void **)&path, nullptr, &val); svn_log_changed_path_t *log_item = reinterpret_cast(val); entry.changedPaths.push_back( LogChangePathEntry(path, log_item->action, log_item->copyfrom_path, log_item->copyfrom_rev)); } } return nullptr; } static void statusEntriesFunc(void *baton, const char *path, svn_wc_status2_t *status) { StatusEntries * entries = static_cast(baton); entries->push_back(Status(path, status)); } static StatusEntries localStatus(const char * path, const bool descend, const bool get_all, const bool update, const bool no_ignore, Context * context, const bool ignore_externals) { svn_error_t *error; StatusEntries entries; svn_revnum_t revnum; Revision rev(Revision::HEAD); Pool pool; error = svn_client_status2( &revnum, // revnum path, // path rev, // revision statusEntriesFunc, // status func &entries, // status baton descend, // recurse get_all, update, // need 'update' to be true to get repository lock info no_ignore, ignore_externals, // ignore_externals *context, // client ctx pool); if (error!=nullptr) throw ClientException(error); return entries; } static Status dirEntryToStatus(const char * path, const DirEntry & dirEntry) { Pool pool; svn_wc_entry_t * e = static_cast( apr_pcalloc(pool, sizeof(svn_wc_entry_t))); std::string url(path); url += '/'; url += dirEntry.name(); e->name = dirEntry.name(); e->revision = dirEntry.createdRev(); e->url = url.c_str(); e->kind = dirEntry.kind(); e->schedule = svn_wc_schedule_normal; e->text_time = dirEntry.time(); e->prop_time = dirEntry.time(); e->cmt_rev = dirEntry.createdRev(); e->cmt_date = dirEntry.time(); e->cmt_author = dirEntry.lastAuthor(); svn_wc_status2_t * s = static_cast( apr_pcalloc(pool, sizeof(svn_wc_status2_t))); s->entry = e; s->text_status = svn_wc_status_normal; s->prop_status = svn_wc_status_normal; s->locked = 0; s->switched = 0; s->repos_text_status = svn_wc_status_normal; s->repos_prop_status = svn_wc_status_normal; return Status(url.c_str(), s); } static svn_revnum_t remoteStatus(Client * client, const char * path, const bool descend, StatusEntries & entries, Context * /*context*/) { Revision rev(Revision::HEAD); DirEntries dirEntries = client->list(path, rev, descend); DirEntries::const_iterator it; svn_revnum_t revnum = 0; - for (it = dirEntries.begin(); it != dirEntries.end(); it++) + for (it = dirEntries.begin(); it != dirEntries.end(); ++it) { const DirEntry & dirEntry = *it; entries.push_back(dirEntryToStatus(path, dirEntry)); } if (dirEntries.size() > 0) revnum = dirEntries[0].createdRev(); return revnum; } StatusEntries Client::status(const char * path, const bool descend, const bool get_all, const bool update, const bool no_ignore, const bool ignore_externals) throw(ClientException) { if (Url::isValid(path)) { StatusEntries entries; remoteStatus(this, path, descend, entries, m_context); return entries; } else return localStatus(path, descend, get_all, update, no_ignore, m_context, ignore_externals); } struct StatusFilter; struct StatusBaton { public: const StatusFilter & filter; StatusEntries & entries; StatusBaton(const StatusFilter & filter_, StatusEntries & entries_) : filter(filter_), entries(entries_) { } }; static void filteredStatusFunc(void *baton_, const char *path, svn_wc_status2_t *status) { StatusBaton * baton = static_cast(baton_); // now we have to decide whether to return the entry or not if (nullptr == status) return; bool useStatus = false; bool isUnversioned = nullptr == status->entry; if (isUnversioned) { // unversioned if (baton->filter.showUnversioned) useStatus = true; } else { bool isUnmodified = ((svn_wc_status_normal == status->text_status) && (svn_wc_status_normal == status->prop_status)); if (isUnmodified) { if (baton->filter.showUnmodified) useStatus = true; } else { // so here we know its modified. // what are we interested in? if (baton->filter.showModified) useStatus = true; else if (baton->filter.showConflicted) { if (svn_wc_status_conflicted == status->text_status) useStatus = true; } } } if (useStatus) baton->entries.push_back(Status(path, status)); } static svn_revnum_t localFilteredStatus(const char * path, const StatusFilter & filter, const bool descend, const bool update, StatusEntries & entries, Context * context) { svn_error_t *error; svn_revnum_t revnum; Revision rev(Revision::HEAD); Pool pool; StatusBaton baton(filter, entries); error = svn_client_status2( &revnum, // revnum path, // path rev, // revision filteredStatusFunc, // status func &baton, // status baton descend, // recurse filter.showUnmodified, update, // need 'update' to be true to get repository lock info filter.showIgnored, // no_ignores !filter.showExternals, // ignore_externals *context, // client ctx pool); if (error!=nullptr) throw ClientException(error); return revnum; } svn_revnum_t Client::status(const char * path, const StatusFilter & filter, const bool descend, const bool update, StatusEntries & entries) throw(ClientException) { entries.clear(); if (Url::isValid(path)) return remoteStatus(this, path, descend, entries, m_context); else { // remote URLs only need a subset of the filters: // we dont expect any modified, conflicting, unknown, // ignored entries. And externals arent visible there anyhow return localFilteredStatus( path, filter, descend, update, entries, m_context); } } const LogEntries * Client::log(const char * path, const Revision & revisionStart, const Revision & revisionEnd, bool discoverChangedPaths, bool strictNodeHistory) throw(ClientException) { Pool pool; Targets target(path); LogEntries * entries = new LogEntries(); svn_error_t *error; int limit = 0; error = svn_client_log2( target.array(pool), revisionStart.revision(), revisionEnd.revision(), limit, discoverChangedPaths ? 1 : 0, strictNodeHistory ? 1 : 0, logReceiver, entries, *m_context, // client ctx pool); if (error != nullptr) { delete entries; throw ClientException(error); } return entries; } /** * callback function for Client::info, will be * called for every entry svn_client_info wants to * return */ static svn_error_t * infoReceiverFunc(void * baton, const char * path, const svn_info_t * info, apr_pool_t * /*pool*/) { InfoVector * infoVector = static_cast(baton); infoVector->push_back(Info(path, info)); return nullptr; } InfoVector Client::info(const Path & pathOrUrl, bool recurse, const Revision & revision, const Revision & pegRevision) throw(ClientException) { Pool pool; InfoVector infoVector; svn_error_t * error = svn_client_info(pathOrUrl.c_str(), pegRevision.revision(), revision.revision(), infoReceiverFunc, &infoVector, recurse, *m_context, pool); if (error != nullptr) throw ClientException(error); return infoVector; } } /* ----------------------------------------------------------------- * local variables: * eval: (load-file "../../rapidsvn-dev.el") * end: */ diff --git a/plugins/subversion/kdevsvncpp/status_selection.cpp b/plugins/subversion/kdevsvncpp/status_selection.cpp index 901fea9b0..e17539274 100644 --- a/plugins/subversion/kdevsvncpp/status_selection.cpp +++ b/plugins/subversion/kdevsvncpp/status_selection.cpp @@ -1,278 +1,278 @@ /* * ==================================================================== * Copyright (c) 2002-2009 The RapidSvn Group. All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program (in the file GPL.txt. * If not, see . * * This software consists of voluntary contributions made by many * individuals. For exact contribution history, see the revision * history and logs, available at http://rapidsvn.tigris.org/. * ==================================================================== */ // subversion api #include "svn_types.h" // apr api #include "apr_file_info.h" // svncpp #include "kdevsvncpp/exception.hpp" #include "kdevsvncpp/path.hpp" #include "kdevsvncpp/pool.hpp" #include "kdevsvncpp/status.hpp" #include "kdevsvncpp/status_selection.hpp" #include "kdevsvncpp/targets.hpp" #include "kdevsvncpp/url.hpp" namespace svn { struct StatusSel::Data { Targets targets; StatusVector status; bool hasDirs; bool hasFiles; bool hasVersioned; bool hasUnversioned; bool hasUrl; bool hasLocal; Path emptyTarget; /** default constructor */ Data() {} /** copy constructor */ Data(const Data & src) { if (this != &src) assign(src); } /** assign new values */ void assign(const Data & src) { // clear existing... clear(); // ... and set from source StatusVector::const_iterator it; - for (it = src.status.begin(); it != src.status.end(); it++) + for (it = src.status.begin(); it != src.status.end(); ++it) { push_back(*it); } } void clear() { targets.clear(); status.clear(); hasDirs = false; hasFiles = false; hasVersioned = false; hasUnversioned = false; hasLocal = false; hasUrl = false; } void push_back(const Status & status_) { // skip pseudo entries if (!status_.isSet()) return; if (!status_.isVersioned()) { // for an unversioned entry we do not know // whether it's a file or a directory so // we have to check using APR apr_finfo_t finfo; Pool pool; apr_status_t apr_status = apr_stat( &finfo, status_.path(), APR_FINFO_TYPE, pool); // if we get an error the file might // have been deleted in the meantime // anyhow: we dont want to display it if (apr_status != APR_SUCCESS) return; hasUnversioned = true; if (APR_DIR == finfo.filetype) hasDirs = true; else hasFiles = true; } else { hasVersioned = true; if (Url::isValid(status_.path())) hasUrl = true; else hasLocal = true; if (svn_node_dir == status_.entry().kind()) hasDirs = true; else hasFiles = true; } // add stuff only now (because of possible apr_error // which causes the function to exit) targets.push_back(status_.path()); status.push_back(status_); } }; StatusSel::StatusSel() : m(new Data) { } StatusSel::StatusSel(const StatusSel & src) : m(new Data) { // different instance? if (this != &src) m->assign(*src.m); } StatusSel & StatusSel::operator = (const StatusSel & src) { if (this != &src) { delete m; m = new Data(*src.m); } return *this; } StatusSel::~StatusSel() { delete m; } const apr_array_header_t * StatusSel::array(const Pool & pool) const { return m->targets.array(pool); } const StatusVector & StatusSel::statusVector() const { return m->status; } const Targets & StatusSel::targets() const { return m->targets; } size_t StatusSel::size() const { return m->targets.size(); } void StatusSel::push_back(const Status & status) { m->push_back(status); } void StatusSel::clear() { m->clear(); } void StatusSel::reserve(size_t size) { m->targets.reserve(size); m->status.reserve(size); } StatusSel::operator const PathVector & () const { return m->targets; } const Path & StatusSel::target() const { if (size() > 0) return m->targets.targets()[0]; else return m->emptyTarget; } bool StatusSel::hasDirs() const { return m->hasDirs; } bool StatusSel::hasFiles() const { return m->hasFiles; } bool StatusSel::hasVersioned() const { return m->hasVersioned; } bool StatusSel::hasUnversioned() const { return m->hasUnversioned; } bool StatusSel::hasLocal() const { return m->hasLocal; } bool StatusSel::hasUrl() const { return m->hasUrl; } } /* ----------------------------------------------------------------- * local variables: * eval: (load-file "../../rapidsvn-dev.el") * end: */ diff --git a/plugins/subversion/kdevsvncpp/targets.cpp b/plugins/subversion/kdevsvncpp/targets.cpp index 52c58c9a0..e6c8c3ada 100644 --- a/plugins/subversion/kdevsvncpp/targets.cpp +++ b/plugins/subversion/kdevsvncpp/targets.cpp @@ -1,148 +1,148 @@ /* * ==================================================================== * Copyright (c) 2002-2009 The RapidSvn Group. All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program (in the file GPL.txt. * If not, see . * * This software consists of voluntary contributions made by many * individuals. For exact contribution history, see the revision * history and logs, available at http://rapidsvn.tigris.org/. * ==================================================================== */ // subversion api #include "svn_types.h" // apr api #include "apr_pools.h" #include "apr_strings.h" // svncpp #include "kdevsvncpp/targets.hpp" #include "kdevsvncpp/path.hpp" #include "kdevsvncpp/pool.hpp" namespace svn { Targets::Targets(const PathVector & targets) { m_targets = targets; } Targets::Targets(const apr_array_header_t * apr_targets) { int i; m_targets.clear(); m_targets.reserve(apr_targets->nelts); for (i = 0; i < apr_targets->nelts; i++) { const char ** target = &APR_ARRAY_IDX(apr_targets, i, const char *); m_targets.push_back(Path(*target)); } } Targets::Targets(const Targets & targets) { m_targets = targets.targets(); } Targets::Targets(const char * target) { if (target != nullptr) { m_targets.push_back(target); } } Targets::~Targets() { } const apr_array_header_t * Targets::array(const Pool & pool) const { PathVector::const_iterator it; apr_pool_t *apr_pool = pool.pool(); apr_array_header_t *apr_targets = apr_array_make(apr_pool, m_targets.size(), sizeof(const char *)); - for (it = m_targets.begin(); it != m_targets.end(); it++) + for (it = m_targets.begin(); it != m_targets.end(); ++it) { const Path &path = *it; const char * target = apr_pstrdup(apr_pool, path.c_str()); (*((const char **) apr_array_push(apr_targets))) = target; } return apr_targets; } const PathVector & Targets::targets() const { return m_targets; } size_t Targets::size() const { return m_targets.size(); } const Path Targets::target() const { if (m_targets.size() > 0) { return m_targets[0]; } else { return ""; } } void Targets::push_back(const Path & path) { m_targets.push_back(path); } void Targets::clear() { m_targets.clear(); } void Targets::reserve(size_t size) { m_targets.reserve(size); } } /* ----------------------------------------------------------------- * local variables: * eval: (load-file "../../rapidsvn-dev.el") * end: */ diff --git a/plugins/subversion/svnblamejob.cpp b/plugins/subversion/svnblamejob.cpp index 1974f8db9..69ed9d1d2 100644 --- a/plugins/subversion/svnblamejob.cpp +++ b/plugins/subversion/svnblamejob.cpp @@ -1,191 +1,191 @@ /*************************************************************************** * This file is part of KDevelop * * Copyright 2007 Andreas Pakulat * * * * This program 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 program 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 General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "svnblamejob.h" #include "svnblamejob_p.h" #include #include #include "svnclient.h" #include SvnInternalBlameJob::SvnInternalBlameJob( SvnJobBase* parent ) : SvnInternalJobBase( parent ) { m_startRevision.setRevisionValue( qVariantFromValue( KDevelop::VcsRevision::Start ), KDevelop::VcsRevision::Special ); m_endRevision.setRevisionValue( qVariantFromValue( KDevelop::VcsRevision::Head ), KDevelop::VcsRevision::Special ); } void SvnInternalBlameJob::run(ThreadWeaver::JobPointer /*self*/, ThreadWeaver::Thread* /*thread*/) { initBeforeRun(); QByteArray ba = location().toString( QUrl::PreferLocalFile | QUrl::StripTrailingSlash ).toUtf8(); svn::Client cli(m_ctxt); svn::AnnotatedFile* file; try { file = cli.annotate( ba.data(), createSvnCppRevisionFromVcsRevision( startRevision() ), createSvnCppRevisionFromVcsRevision( endRevision() ) ); }catch( svn::ClientException ce ) { qCDebug(PLUGIN_SVN) << "Exception while blaming file: " << location() << QString::fromUtf8( ce.message() ); setErrorMessage( QString::fromUtf8( ce.message() ) ); m_success = false; return; } svn_revnum_t minrev = -1, maxrev = -1; - for( svn::AnnotatedFile::const_iterator it = file->begin(); it != file->end(); it++ ) + for( svn::AnnotatedFile::const_iterator it = file->begin(); it != file->end(); ++it ) { if( (*it).revision() < minrev || minrev == -1 ) { minrev = (*it).revision(); } if( (*it).revision() > maxrev || maxrev == -1 ) { maxrev = (*it).revision(); } } QHash commitMessages; try { const svn::LogEntries* entries = cli.log( ba.data(), svn::Revision(minrev), svn::Revision(maxrev), false, false ); - for( svn::LogEntries::const_iterator it = entries->begin(); it != entries->end(); it++ ) + for( svn::LogEntries::const_iterator it = entries->begin(); it != entries->end(); ++it ) { commitMessages[(*it).revision] = QString::fromUtf8( (*it).message.c_str() ); } }catch( svn::ClientException ce ) { qCDebug(PLUGIN_SVN) << "Exception while fetching log messages for blame: " << location() << QString::fromUtf8( ce.message() ); setErrorMessage( QString::fromUtf8( ce.message() ) ); m_success = false; } - for( svn::AnnotatedFile::const_iterator it = file->begin(); it != file->end(); it++ ) + for( svn::AnnotatedFile::const_iterator it = file->begin(); it != file->end(); ++it ) { KDevelop::VcsAnnotationLine line; line.setAuthor( QString::fromUtf8( it->author().c_str() ) ); line.setDate( QDateTime::fromString( QString::fromUtf8( it->date().c_str() ), Qt::ISODate ) ); line.setText( QString::fromUtf8( it->line().c_str() ) ); KDevelop::VcsRevision rev; rev.setRevisionValue( QVariant( qlonglong( it->revision() ) ), KDevelop::VcsRevision::GlobalNumber ); line.setRevision( rev ); line.setLineNumber( it->lineNumber() ); line.setCommitMessage( commitMessages[it->revision()] ); emit blameLine( line ); } } void SvnInternalBlameJob::setLocation( const QUrl &url ) { QMutexLocker l( &m_mutex ); m_location = url; } QUrl SvnInternalBlameJob::location() const { QMutexLocker l( &m_mutex ); return m_location; } KDevelop::VcsRevision SvnInternalBlameJob::startRevision() const { QMutexLocker l( &m_mutex ); return m_startRevision; } KDevelop::VcsRevision SvnInternalBlameJob::endRevision() const { QMutexLocker l( &m_mutex ); return m_endRevision; } void SvnInternalBlameJob::setStartRevision( const KDevelop::VcsRevision& rev ) { QMutexLocker l( &m_mutex ); m_startRevision = rev; } void SvnInternalBlameJob::setEndRevision( const KDevelop::VcsRevision& rev ) { QMutexLocker l( &m_mutex ); m_endRevision = rev; } SvnBlameJob::SvnBlameJob( KDevSvnPlugin* parent ) : SvnJobBaseImpl( parent, KDevelop::OutputJob::Silent ) { setType( KDevelop::VcsJob::Annotate ); connect(m_job, &SvnInternalBlameJob::blameLine, this, &SvnBlameJob::blameLineReceived); setObjectName(i18n("Subversion Annotate")); } QVariant SvnBlameJob::fetchResults() { QList results = m_annotations; m_annotations.clear(); return results; } void SvnBlameJob::start() { if ( !m_job->location().isValid() ) { internalJobFailed(); setErrorText( i18n( "Not enough information to blame location" ) ); } else { qCDebug(PLUGIN_SVN) << "blaming url:" << m_job->location(); startInternalJob(); } } void SvnBlameJob::setLocation( const QUrl &url ) { if( status() == KDevelop::VcsJob::JobNotStarted ) m_job->setLocation( url ); } void SvnBlameJob::setStartRevision( const KDevelop::VcsRevision& rev ) { if( status() == KDevelop::VcsJob::JobNotStarted ) m_job->setStartRevision( rev ); } void SvnBlameJob::setEndRevision( const KDevelop::VcsRevision& rev ) { if( status() == KDevelop::VcsJob::JobNotStarted ) m_job->setEndRevision( rev ); } void SvnBlameJob::blameLineReceived( const KDevelop::VcsAnnotationLine& line ) { m_annotations.append( qVariantFromValue( line ) ); emit resultsReady( this ); } diff --git a/shell/launchconfigurationdialog.cpp b/shell/launchconfigurationdialog.cpp index ceb146f36..0d03924fa 100644 --- a/shell/launchconfigurationdialog.cpp +++ b/shell/launchconfigurationdialog.cpp @@ -1,1025 +1,1025 @@ /* This file is part of KDevelop Copyright 2009 Andreas Pakulat 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 "launchconfigurationdialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core.h" #include "runcontroller.h" #include "launchconfiguration.h" #include "debug.h" #include #include #include namespace KDevelop { bool launchConfigGreaterThan(KDevelop::LaunchConfigurationType* a, KDevelop::LaunchConfigurationType* b) { return a->name()>b->name(); } //TODO: Maybe use KPageDialog instead, might make the model stuff easier and the default-size stuff as well LaunchConfigurationDialog::LaunchConfigurationDialog(QWidget* parent) : QDialog(parent) , currentPageChanged(false) { setWindowTitle( i18n( "Launch Configurations" ) ); QWidget *mainWidget = new QWidget(this); QVBoxLayout *mainLayout = new QVBoxLayout(this); mainLayout->addWidget(mainWidget); setupUi(mainWidget); mainLayout->setContentsMargins( 0, 0, 0, 0 ); splitter->setSizes(QList() << 260 << 620); splitter->setCollapsible(0, false); addConfig->setIcon( QIcon::fromTheme(QStringLiteral("list-add")) ); addConfig->setToolTip(i18nc("@info:tooltip", "Add a new launch configuration.")); deleteConfig->setIcon( QIcon::fromTheme(QStringLiteral("list-remove")) ); deleteConfig->setEnabled( false ); deleteConfig->setToolTip(i18nc("@info:tooltip", "Delete selected launch configuration.")); model = new LaunchConfigurationsModel( tree ); tree->setModel( model ); tree->setExpandsOnDoubleClick( true ); tree->setSelectionBehavior( QAbstractItemView::SelectRows ); tree->setSelectionMode( QAbstractItemView::SingleSelection ); tree->setUniformRowHeights( true ); tree->setItemDelegate( new LaunchConfigurationModelDelegate(this) ); tree->setColumnHidden(1, true); for(int row=0; rowrowCount(); row++) { tree->setExpanded(model->index(row, 0), true); } tree->setContextMenuPolicy(Qt::CustomContextMenu); connect( tree, &QTreeView::customContextMenuRequested, this, &LaunchConfigurationDialog::doTreeContextMenu ); connect( deleteConfig, &QToolButton::clicked, this, &LaunchConfigurationDialog::deleteConfiguration); connect( model, &LaunchConfigurationsModel::dataChanged, this, &LaunchConfigurationDialog::modelChanged ); connect( tree->selectionModel(), &QItemSelectionModel::selectionChanged, this, &LaunchConfigurationDialog::selectionChanged); QModelIndex idx = model->indexForConfig( Core::self()->runControllerInternal()->defaultLaunch() ); qCDebug(SHELL) << "selecting index:" << idx; if( !idx.isValid() ) { for( int i = 0; i < model->rowCount(); i++ ) { if( model->rowCount( model->index( i, 0, QModelIndex() ) ) > 0 ) { idx = model->index( 1, 0, model->index( i, 0, QModelIndex() ) ); break; } } if( !idx.isValid() ) { idx = model->index( 0, 0, QModelIndex() ); } } tree->selectionModel()->select( QItemSelection( idx, idx ), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows ); tree->selectionModel()->setCurrentIndex( idx, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows ); // Unfortunately tree->resizeColumnToContents() only looks at the top-level // items, instead of all open ones. Hence we're calculating it ourselves like // this: // Take the selected index, check if it has childs, if so take the first child // Then count the level by going up, then let the tree calculate the width // for the selected or its first child index and add indentation*level // // If Qt Software ever fixes resizeColumnToContents, the following line // can be enabled and the rest be removed // tree->resizeColumnToContents( 0 ); int level = 0; QModelIndex widthidx = idx; if( model->rowCount( idx ) > 0 ) { widthidx = idx.child( 0, 0 ); } QModelIndex parentidx = widthidx.parent(); while( parentidx.isValid() ) { level++; parentidx = parentidx.parent(); } // make sure the base column width is honored, e.g. when no launch configs exist tree->resizeColumnToContents(0); int width = tree->columnWidth( 0 ); while ( widthidx.isValid() ) { width = qMax( width, level*tree->indentation() + tree->indentation() + tree->sizeHintForIndex( widthidx ).width() ); widthidx = widthidx.parent(); } tree->setColumnWidth( 0, width ); QMenu* m = new QMenu(this); QList types = Core::self()->runController()->launchConfigurationTypes(); std::sort(types.begin(), types.end(), launchConfigGreaterThan); //we want it in reverse order foreach(LaunchConfigurationType* type, types) { connect(type, &LaunchConfigurationType::signalAddLaunchConfiguration, this, &LaunchConfigurationDialog::addConfiguration); QMenu* suggestionsMenu = type->launcherSuggestions(); if(suggestionsMenu) { m->addMenu(suggestionsMenu); } } // Simplify menu structure to get rid of 1-entry levels while (m->actions().count() == 1) { QMenu* subMenu = m->actions().at(0)->menu(); if (subMenu && subMenu->isEnabled() && subMenu->actions().count()<5) { m = subMenu; } else { break; } } if(!m->isEmpty()) { QAction* separator = new QAction(m); separator->setSeparator(true); m->insertAction(m->actions().at(0), separator); } foreach(LaunchConfigurationType* type, types) { QAction* action = new QAction(type->icon(), type->name(), m); action->setProperty("configtype", qVariantFromValue(type)); connect(action, &QAction::triggered, this, &LaunchConfigurationDialog::createEmptyLauncher); if(!m->actions().isEmpty()) m->insertAction(m->actions().at(0), action); else m->addAction(action); } addConfig->setMenu(m); addConfig->setEnabled( !m->isEmpty() ); messageWidget->setCloseButtonVisible( false ); messageWidget->setMessageType( KMessageWidget::Warning ); messageWidget->setText( i18n("No launch configurations available. (Is any of the Execute plugins loaded?)") ); messageWidget->setVisible( m->isEmpty() ); connect(debugger, static_cast(&QComboBox::currentIndexChanged), this, &LaunchConfigurationDialog::launchModeChanged); connect(buttonBox, &QDialogButtonBox::accepted, this, &LaunchConfigurationDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &LaunchConfigurationDialog::reject); connect(buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, this, static_cast(&LaunchConfigurationDialog::saveConfig) ); connect(buttonBox->button(QDialogButtonBox::Apply), &QPushButton::clicked, this, static_cast(&LaunchConfigurationDialog::saveConfig) ); mainLayout->addWidget(buttonBox); resize( QSize(qMax(700, sizeHint().width()), qMax(500, sizeHint().height())) ); } void LaunchConfigurationDialog::doTreeContextMenu(QPoint point) { if ( ! tree->selectionModel()->selectedRows().isEmpty() ) { QModelIndex selected = tree->selectionModel()->selectedRows().first(); if ( selected.parent().isValid() && ! selected.parent().parent().isValid() ) { // only display the menu if a launch config is clicked QMenu menu; QAction* rename = new QAction(QIcon::fromTheme(QStringLiteral("edit-rename")), i18n("Rename configuration"), &menu); QAction* delete_ = new QAction(QIcon::fromTheme(QStringLiteral("edit-delete")), i18n("Delete configuration"), &menu); connect(rename, &QAction::triggered, this, &LaunchConfigurationDialog::renameSelected); connect(delete_, &QAction::triggered, this, &LaunchConfigurationDialog::deleteConfiguration); menu.addAction(rename); menu.addAction(delete_); menu.exec(tree->mapToGlobal(point)); } } } void LaunchConfigurationDialog::renameSelected() { if( !tree->selectionModel()->selectedRows().isEmpty() ) { QModelIndex parent = tree->selectionModel()->selectedRows().first(); if( parent.parent().isValid() ) { parent = parent.parent(); } QModelIndex index = model->index(tree->selectionModel()->selectedRows().first().row(), 0, parent); tree->edit( index ); } } QSize LaunchConfigurationDialog::sizeHint() const { QSize s = QDialog::sizeHint(); return s.expandedTo(QSize(880, 520)); } void LaunchConfigurationDialog::createEmptyLauncher() { QAction* action = qobject_cast(sender()); Q_ASSERT(action); LaunchConfigurationType* type = qobject_cast(action->property("configtype").value()); Q_ASSERT(type); IProject* p = model->projectForIndex(tree->currentIndex()); QPair< QString, QString > launcher( type->launchers().at( 0 )->supportedModes().at(0), type->launchers().at( 0 )->id() ); ILaunchConfiguration* l = ICore::self()->runController()->createLaunchConfiguration(type, launcher, p); addConfiguration(l); } void LaunchConfigurationDialog::selectionChanged(QItemSelection selected, QItemSelection deselected ) { if( !deselected.indexes().isEmpty() ) { LaunchConfiguration* l = model->configForIndex( deselected.indexes().first() ); if( l ) { disconnect(l, &LaunchConfiguration::nameChanged, this, &LaunchConfigurationDialog::updateNameLabel); if( currentPageChanged ) { if( KMessageBox::questionYesNo( this, i18n("Selected Launch Configuration has unsaved changes. Do you want to save it?"), i18n("Unsaved Changes") ) == KMessageBox::Yes ) { saveConfig( deselected.indexes().first() ); } else { LaunchConfigPagesContainer* tab = qobject_cast( stack->currentWidget() ); tab->setLaunchConfiguration( l ); buttonBox->button(QDialogButtonBox::Apply)->setEnabled( false ); currentPageChanged = false; } } } } updateNameLabel(nullptr); for( int i = 1; i < stack->count(); i++ ) { QWidget* w = stack->widget(i); stack->removeWidget(w); delete w; } debugger->clear(); if( !selected.indexes().isEmpty() ) { QModelIndex idx = selected.indexes().first(); LaunchConfiguration* l = model->configForIndex( idx ); ILaunchMode* lm = model->modeForIndex( idx ); if( l ) { updateNameLabel( l ); tree->expand( model->indexForConfig( l ) ); connect( l, &LaunchConfiguration::nameChanged, this, &LaunchConfigurationDialog::updateNameLabel ); if( lm ) { { QSignalBlocker blocker(debugger); QList launchers = l->type()->launchers(); - for( QList::const_iterator it = launchers.constBegin(); it != launchers.constEnd(); it++ ) + for( QList::const_iterator it = launchers.constBegin(); it != launchers.constEnd(); ++it ) { if( ((*it)->supportedModes().contains( lm->id() ) ) ) { debugger->addItem( (*it)->name(), (*it)->id() ); } } } debugger->setVisible(debugger->count()>0); debugLabel->setVisible(debugger->count()>0); QVariant currentLaunchMode = idx.sibling(idx.row(), 1).data(Qt::EditRole); debugger->setCurrentIndex(debugger->findData(currentLaunchMode)); ILauncher* launcher = l->type()->launcherForId( currentLaunchMode.toString() ); if( launcher ) { LaunchConfigPagesContainer* tab = launcherWidgets.value( launcher ); if(!tab) { QList pages = launcher->configPages(); if(!pages.isEmpty()) { tab = new LaunchConfigPagesContainer( launcher->configPages(), stack ); connect( tab, &LaunchConfigPagesContainer::changed, this, &LaunchConfigurationDialog::pageChanged ); stack->addWidget( tab ); } } if(tab) { tab->setLaunchConfiguration( l ); stack->setCurrentWidget( tab ); } else { QLabel* label = new QLabel(i18nc("%1 is a launcher name", "No configuration is needed for '%1'", launcher->name()), stack); label->setAlignment(Qt::AlignCenter); QFont font = label->font(); font.setItalic(true); label->setFont(font); stack->addWidget(label); stack->setCurrentWidget(label); } updateNameLabel( l ); addConfig->setEnabled( false ); deleteConfig->setEnabled( false ); } else { addConfig->setEnabled( false ); deleteConfig->setEnabled( false ); stack->setCurrentIndex( 0 ); } } else { //TODO: enable removal button LaunchConfigurationType* type = l->type(); LaunchConfigPagesContainer* tab = typeWidgets.value( type ); if( !tab ) { tab = new LaunchConfigPagesContainer( type->configPages(), stack ); connect( tab, &LaunchConfigPagesContainer::changed, this, &LaunchConfigurationDialog::pageChanged ); stack->addWidget( tab ); } qCDebug(SHELL) << "created pages, setting config up"; tab->setLaunchConfiguration( l ); stack->setCurrentWidget( tab ); addConfig->setEnabled( addConfig->menu() && !addConfig->menu()->isEmpty() ); deleteConfig->setEnabled( true ); debugger->setVisible( false ); debugLabel->setVisible( false ); } } else { addConfig->setEnabled( addConfig->menu() && !addConfig->menu()->isEmpty() ); deleteConfig->setEnabled( false ); stack->setCurrentIndex( 0 ); QLabel* l = new QLabel(i18n("Select a configuration to edit from the left,
" "or click the \"Add New\" button to add a new one.
"), stack); l->setAlignment(Qt::AlignCenter); stack->addWidget(l); stack->setCurrentWidget(l); debugger->setVisible( false ); debugLabel->setVisible( false ); } } else { debugger->setVisible( false ); debugLabel->setVisible( false ); addConfig->setEnabled( false ); deleteConfig->setEnabled( false ); stack->setCurrentIndex( 0 ); } } void LaunchConfigurationDialog::saveConfig( const QModelIndex& idx ) { Q_UNUSED( idx ); LaunchConfigPagesContainer* tab = qobject_cast( stack->currentWidget() ); if( tab ) { tab->save(); buttonBox->button(QDialogButtonBox::Apply)->setEnabled( false ); currentPageChanged = false; } } void LaunchConfigurationDialog::saveConfig() { if( !tree->selectionModel()->selectedRows().isEmpty() ) { saveConfig( tree->selectionModel()->selectedRows().first() ); } } void LaunchConfigurationDialog::pageChanged() { currentPageChanged = true; buttonBox->button(QDialogButtonBox::Apply)->setEnabled( true ); } void LaunchConfigurationDialog::modelChanged(QModelIndex topLeft, QModelIndex bottomRight) { if (tree->selectionModel()) { QModelIndex index = tree->selectionModel()->selectedRows().first(); if (index.row() >= topLeft.row() && index.row() <= bottomRight.row() && bottomRight.column() == 1) selectionChanged(tree->selectionModel()->selection(), tree->selectionModel()->selection()); } } void LaunchConfigurationDialog::deleteConfiguration() { if( !tree->selectionModel()->selectedRows().isEmpty() ) { model->deleteConfiguration( tree->selectionModel()->selectedRows().first() ); tree->resizeColumnToContents( 0 ); } } void LaunchConfigurationDialog::updateNameLabel( LaunchConfiguration* l ) { if( l ) { configName->setText( i18n("Editing %2: %1", l->name(), l->type()->name() ) ); } else { configName->clear(); } } void LaunchConfigurationDialog::createConfiguration() { if( !tree->selectionModel()->selectedRows().isEmpty() ) { QModelIndex idx = tree->selectionModel()->selectedRows().first(); if( idx.parent().isValid() ) { idx = idx.parent(); } model->createConfiguration( idx ); QModelIndex newindex = model->index( model->rowCount( idx ) - 1, 0, idx ); tree->selectionModel()->select( newindex, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows ); tree->selectionModel()->setCurrentIndex( newindex, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows ); tree->edit( newindex ); tree->resizeColumnToContents( 0 ); } } void LaunchConfigurationDialog::addConfiguration(ILaunchConfiguration* _launch) { LaunchConfiguration* launch = dynamic_cast(_launch); Q_ASSERT(launch); int row = launch->project() ? model->findItemForProject(launch->project())->row : 0; QModelIndex idx = model->index(row, 0); model->addConfiguration(launch, idx); QModelIndex newindex = model->index( model->rowCount( idx ) - 1, 0, idx ); tree->selectionModel()->select( newindex, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows ); tree->selectionModel()->setCurrentIndex( newindex, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows ); tree->edit( newindex ); tree->resizeColumnToContents( 0 ); } LaunchConfigurationsModel::LaunchConfigurationsModel(QObject* parent): QAbstractItemModel(parent) { GenericPageItem* global = new GenericPageItem; global->text = i18n("Global"); global->row = 0; topItems << global; foreach( IProject* p, Core::self()->projectController()->projects() ) { ProjectItem* t = new ProjectItem; t->project = p; t->row = topItems.count(); topItems << t; } foreach( LaunchConfiguration* l, Core::self()->runControllerInternal()->launchConfigurationsInternal() ) { addItemForLaunchConfig( l ); } } void LaunchConfigurationsModel::addItemForLaunchConfig( LaunchConfiguration* l ) { LaunchItem* t = new LaunchItem; t->launch = l; TreeItem* parent; if( l->project() ) { parent = findItemForProject( l->project() ); } else { parent = topItems.at(0); } t->parent = parent; t->row = parent->children.count(); parent->children.append( t ); addLaunchModeItemsForLaunchConfig ( t ); } void LaunchConfigurationsModel::addLaunchModeItemsForLaunchConfig ( LaunchItem* t ) { QList items; QSet modes; foreach( ILauncher* launcher, t->launch->type()->launchers() ) { foreach( const QString& mode, launcher->supportedModes() ) { if( !modes.contains( mode ) && launcher->configPages().count() > 0 ) { modes.insert( mode ); LaunchModeItem* lmi = new LaunchModeItem; lmi->mode = Core::self()->runController()->launchModeForId( mode ); lmi->parent = t; lmi->row = t->children.count(); items.append( lmi ); } } } if( !items.isEmpty() ) { QModelIndex p = indexForConfig( t->launch ); beginInsertRows( p, t->children.count(), t->children.count() + items.count() - 1 ); t->children.append( items ); endInsertRows(); } } LaunchConfigurationsModel::ProjectItem* LaunchConfigurationsModel::findItemForProject( IProject* p ) { foreach( TreeItem* t, topItems ) { ProjectItem* pi = dynamic_cast( t ); if( pi && pi->project == p ) { return pi; } } Q_ASSERT(false); return nullptr; } int LaunchConfigurationsModel::columnCount(const QModelIndex& parent) const { Q_UNUSED( parent ); return 2; } QVariant LaunchConfigurationsModel::data(const QModelIndex& index, int role) const { if( index.isValid() && index.column() >= 0 && index.column() < 2 ) { TreeItem* t = static_cast( index.internalPointer() ); switch( role ) { case Qt::DisplayRole: { LaunchItem* li = dynamic_cast( t ); if( li ) { if( index.column() == 0 ) { return li->launch->name(); } else if( index.column() == 1 ) { return li->launch->type()->name(); } } ProjectItem* pi = dynamic_cast( t ); if( pi && index.column() == 0 ) { return pi->project->name(); } GenericPageItem* gpi = dynamic_cast( t ); if( gpi && index.column() == 0 ) { return gpi->text; } LaunchModeItem* lmi = dynamic_cast( t ); if( lmi ) { if( index.column() == 0 ) { return lmi->mode->name(); } else if( index.column() == 1 ) { LaunchConfiguration* l = configForIndex( index ); return l->type()->launcherForId( l->launcherForMode( lmi->mode->id() ) )->name(); } } break; } case Qt::DecorationRole: { LaunchItem* li = dynamic_cast( t ); if( index.column() == 0 && li ) { return li->launch->type()->icon(); } LaunchModeItem* lmi = dynamic_cast( t ); if( lmi && index.column() == 0 ) { return lmi->mode->icon(); } if ( index.column() == 0 && !index.parent().isValid() ) { if (index.row() == 0) { // global item return QIcon::fromTheme(QStringLiteral("folder")); } else { // project item return QIcon::fromTheme(QStringLiteral("folder-development")); } } } case Qt::EditRole: { LaunchItem* li = dynamic_cast( t ); if( li ) { if( index.column() == 0 ) { return li->launch->name(); } else if ( index.column() == 1 ) { return li->launch->type()->id(); } } LaunchModeItem* lmi = dynamic_cast( t ); if( lmi && index.column() == 1 ) { return configForIndex( index )->launcherForMode( lmi->mode->id() ); } break; } default: break; } } return QVariant(); } QModelIndex LaunchConfigurationsModel::index(int row, int column, const QModelIndex& parent) const { if( !hasIndex( row, column, parent ) ) return QModelIndex(); TreeItem* tree; if( !parent.isValid() ) { tree = topItems.at( row ); } else { TreeItem* t = static_cast( parent.internalPointer() ); tree = t->children.at( row ); } if( tree ) { return createIndex( row, column, tree ); } return QModelIndex(); } QModelIndex LaunchConfigurationsModel::parent(const QModelIndex& child) const { if( child.isValid() ) { TreeItem* t = static_cast( child.internalPointer() ); if( t->parent ) { return createIndex( t->parent->row, 0, t->parent ); } } return QModelIndex(); } int LaunchConfigurationsModel::rowCount(const QModelIndex& parent) const { if( parent.column() > 0 ) return 0; if( parent.isValid() ) { TreeItem* t = static_cast( parent.internalPointer() ); return t->children.count(); } else { return topItems.count(); } return 0; } QVariant LaunchConfigurationsModel::headerData(int section, Qt::Orientation orientation, int role) const { if( orientation == Qt::Horizontal && role == Qt::DisplayRole ) { if( section == 0 ) { return i18nc("Name of the Launch Configurations", "Name"); } else if( section == 1 ) { return i18nc("The type of the Launch Configurations (i.e. Python Application, C++ Application)", "Type"); } } return QVariant(); } Qt::ItemFlags LaunchConfigurationsModel::flags(const QModelIndex& index) const { if( index.isValid() && index.column() >= 0 && index.column() < columnCount( QModelIndex() ) ) { TreeItem* t = static_cast( index.internalPointer() ); if( t && ( dynamic_cast( t ) || ( dynamic_cast( t ) && index.column() == 1 ) ) ) { return Qt::ItemFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable ); } else if( t ) { return Qt::ItemFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable ); } } return Qt::NoItemFlags; } bool LaunchConfigurationsModel::setData(const QModelIndex& index, const QVariant& value, int role) { if( index.isValid() && index.parent().isValid() && role == Qt::EditRole ) { if( index.row() >= 0 && index.row() < rowCount( index.parent() ) ) { LaunchItem* t = dynamic_cast( static_cast( index.internalPointer() ) ); if( t ) { if( index.column() == 0 ) { t->launch->setName( value.toString() ); } else if( index.column() == 1 ) { if (t->launch->type()->id() != value.toString()) { t->launch->setType( value.toString() ); QModelIndex p = indexForConfig(t->launch); qCDebug(SHELL) << data(p); beginRemoveRows( p, 0, t->children.count() ); qDeleteAll( t->children ); t->children.clear(); endRemoveRows(); addLaunchModeItemsForLaunchConfig( t ); } } emit dataChanged(index, index); return true; } LaunchModeItem* lmi = dynamic_cast( static_cast( index.internalPointer() ) ); if( lmi ) { if( index.column() == 1 && index.data(Qt::EditRole)!=value) { LaunchConfiguration* l = configForIndex( index ); l->setLauncherForMode( lmi->mode->id(), value.toString() ); emit dataChanged(index, index); return true; } } } } return false; } ILaunchMode* LaunchConfigurationsModel::modeForIndex( const QModelIndex& idx ) const { if( idx.isValid() ) { LaunchModeItem* item = dynamic_cast( static_cast( idx.internalPointer() ) ); if( item ) { return item->mode; } } return nullptr; } LaunchConfiguration* LaunchConfigurationsModel::configForIndex(const QModelIndex& idx ) const { if( idx.isValid() ) { LaunchItem* item = dynamic_cast( static_cast( idx.internalPointer() ) ); if( item ) { return item->launch; } LaunchModeItem* lmitem = dynamic_cast( static_cast( idx.internalPointer() ) ); if( lmitem ) { return dynamic_cast( lmitem->parent )->launch; } } return nullptr; } QModelIndex LaunchConfigurationsModel::indexForConfig( LaunchConfiguration* l ) const { if( l ) { TreeItem* tparent = topItems.at( 0 ); if( l->project() ) { foreach( TreeItem* t, topItems ) { ProjectItem* pi = dynamic_cast( t ); if( pi && pi->project == l->project() ) { tparent = t; break; } } } if( tparent ) { foreach( TreeItem* c, tparent->children ) { LaunchItem* li = dynamic_cast( c ); if( li->launch && li->launch == l ) { return index( c->row, 0, index( tparent->row, 0, QModelIndex() ) ); } } } } return QModelIndex(); } void LaunchConfigurationsModel::deleteConfiguration( const QModelIndex& index ) { LaunchItem* t = dynamic_cast( static_cast( index.internalPointer() ) ); if( !t ) return; beginRemoveRows( parent( index ), index.row(), index.row() ); t->parent->children.removeAll( t ); Core::self()->runControllerInternal()->removeLaunchConfiguration( t->launch ); endRemoveRows(); } void LaunchConfigurationsModel::createConfiguration(const QModelIndex& parent ) { if(!Core::self()->runController()->launchConfigurationTypes().isEmpty()) { TreeItem* t = static_cast( parent.internalPointer() ); ProjectItem* ti = dynamic_cast( t ); LaunchConfigurationType* type = Core::self()->runController()->launchConfigurationTypes().at(0); QPair launcher = qMakePair( type->launchers().at( 0 )->supportedModes().at(0), type->launchers().at( 0 )->id() ); IProject* p = ( ti ? ti->project : nullptr ); ILaunchConfiguration* l = Core::self()->runController()->createLaunchConfiguration( type, launcher, p ); addConfiguration(l, parent); } } void LaunchConfigurationsModel::addConfiguration(ILaunchConfiguration* l, const QModelIndex& parent) { if( parent.isValid() ) { beginInsertRows( parent, rowCount( parent ), rowCount( parent ) ); addItemForLaunchConfig( dynamic_cast( l ) ); endInsertRows(); } else { delete l; Q_ASSERT(false && "could not add the configuration"); } } IProject* LaunchConfigurationsModel::projectForIndex(const QModelIndex& idx) { if(idx.parent().isValid()) { return projectForIndex(idx.parent()); } else { const ProjectItem* item = dynamic_cast(topItems[idx.row()]); return item ? item->project : nullptr; } } LaunchConfigPagesContainer::LaunchConfigPagesContainer( const QList& factories, QWidget* parent ) : QWidget(parent) { setLayout( new QVBoxLayout( this ) ); layout()->setContentsMargins( 0, 0, 0, 0 ); QWidget* parentwidget = this; QTabWidget* tab = nullptr; if( factories.count() > 1 ) { tab = new QTabWidget( this ); parentwidget = tab; layout()->addWidget( tab ); } foreach( LaunchConfigurationPageFactory* fac, factories ) { LaunchConfigurationPage* page = fac->createWidget( parentwidget ); if ( page->layout() ) { page->layout()->setContentsMargins( 0, 0, 0, 0 ); } pages.append( page ); connect( page, &LaunchConfigurationPage::changed, this, &LaunchConfigPagesContainer::changed ); if( tab ) { tab->addTab( page, page->icon(), page->title() ); } else { layout()->addWidget( page ); } } } void LaunchConfigPagesContainer::setLaunchConfiguration( KDevelop::LaunchConfiguration* l ) { config = l; foreach( LaunchConfigurationPage* p, pages ) { p->loadFromConfiguration( config->config(), config->project() ); } } void LaunchConfigPagesContainer::save() { foreach( LaunchConfigurationPage* p, pages ) { p->saveToConfiguration( config->config() ); } config->config().sync(); } QWidget* LaunchConfigurationModelDelegate::createEditor ( QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index ) const { const LaunchConfigurationsModel* model = dynamic_cast( index.model() ); ILaunchMode* mode = model->modeForIndex( index ); LaunchConfiguration* config = model->configForIndex( index ); if( index.column() == 1 && mode && config ) { KComboBox* box = new KComboBox( parent ); QList launchers = config->type()->launchers(); - for( QList::const_iterator it = launchers.constBegin(); it != launchers.constEnd(); it++ ) + for( QList::const_iterator it = launchers.constBegin(); it != launchers.constEnd(); ++it ) { if( ((*it)->supportedModes().contains( mode->id() ) ) ) { box->addItem( (*it)->name(), (*it)->id() ); } } return box; } else if( !mode && config && index.column() == 1 ) { KComboBox* box = new KComboBox( parent ); const QList types = Core::self()->runController()->launchConfigurationTypes(); - for( QList::const_iterator it = types.begin(); it != types.end(); it++ ) + for( QList::const_iterator it = types.begin(); it != types.end(); ++it ) { box->addItem( (*it)->name(), (*it)->id() ); } return box; } return QStyledItemDelegate::createEditor ( parent, option, index ); } void LaunchConfigurationModelDelegate::setEditorData ( QWidget* editor, const QModelIndex& index ) const { const LaunchConfigurationsModel* model = dynamic_cast( index.model() ); LaunchConfiguration* config = model->configForIndex( index ); if( index.column() == 1 && config ) { KComboBox* box = qobject_cast( editor ); box->setCurrentIndex( box->findData( index.data( Qt::EditRole ) ) ); } else { QStyledItemDelegate::setEditorData ( editor, index ); } } void LaunchConfigurationModelDelegate::setModelData ( QWidget* editor, QAbstractItemModel* model, const QModelIndex& index ) const { LaunchConfigurationsModel* lmodel = dynamic_cast( model ); LaunchConfiguration* config = lmodel->configForIndex( index ); if( index.column() == 1 && config ) { KComboBox* box = qobject_cast( editor ); lmodel->setData( index, box->itemData( box->currentIndex() ) ); } else { QStyledItemDelegate::setModelData ( editor, model, index ); } } void LaunchConfigurationDialog::launchModeChanged(int item) { QModelIndex index = tree->currentIndex(); if(debugger->isVisible() && item>=0) tree->model()->setData(index.sibling(index.row(), 1), debugger->itemData(item), Qt::EditRole); } }