diff --git a/debugger/breakpoint/breakpointmodel.cpp b/debugger/breakpoint/breakpointmodel.cpp index d2ac79e83d..e62efce6d2 100644 --- a/debugger/breakpoint/breakpointmodel.cpp +++ b/debugger/breakpoint/breakpointmodel.cpp @@ -1,635 +1,635 @@ /* This file is part of the KDE project Copyright (C) 2002 Matthias Hoelzer-Kluepfel Copyright (C) 2002 John Firebaugh Copyright (C) 2006, 2008 Vladimir Prus Copyright (C) 2007 Hamish Rodda Copyright (C) 2009 Niko Sams 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, see . */ #include "breakpointmodel.h" #include #include #include #include #include #include #include "../interfaces/icore.h" #include "../interfaces/idebugcontroller.h" #include "../interfaces/idocumentcontroller.h" #include "../interfaces/idocument.h" #include "../interfaces/ipartcontroller.h" #include #include #include #include #include "breakpoint.h" #include #include #include #include #define IF_DEBUG(x) using namespace KDevelop; using namespace KTextEditor; namespace { IBreakpointController* breakpointController() { KDevelop::ICore* core = KDevelop::ICore::self(); if (!core) { return nullptr; } IDebugController* controller = core->debugController(); if (!controller) { return nullptr; } IDebugSession* session = controller->currentSession(); return session ? session->breakpointController() : nullptr; } } // anonymous namespace BreakpointModel::BreakpointModel(QObject* parent) : QAbstractTableModel(parent), m_dontUpdateMarks(false) { connect(this, &BreakpointModel::dataChanged, this, &BreakpointModel::updateMarks); if (KDevelop::ICore::self()->partController()) { //TODO remove if foreach(KParts::Part* p, KDevelop::ICore::self()->partController()->parts()) slotPartAdded(p); connect(KDevelop::ICore::self()->partController(), &IPartController::partAdded, this, &BreakpointModel::slotPartAdded); } connect (KDevelop::ICore::self()->documentController(), &IDocumentController::textDocumentCreated, this, &BreakpointModel::textDocumentCreated); connect (KDevelop::ICore::self()->documentController(), &IDocumentController::documentSaved, this, &BreakpointModel::documentSaved); } BreakpointModel::~BreakpointModel() { qDeleteAll(m_breakpoints); } void BreakpointModel::slotPartAdded(KParts::Part* part) { if (auto doc = qobject_cast(part)) { MarkInterface *iface = dynamic_cast(doc); if( !iface ) return; iface->setMarkDescription((MarkInterface::MarkTypes)BreakpointMark, i18n("Breakpoint")); iface->setMarkPixmap((MarkInterface::MarkTypes)BreakpointMark, *breakpointPixmap()); iface->setMarkPixmap((MarkInterface::MarkTypes)PendingBreakpointMark, *pendingBreakpointPixmap()); iface->setMarkPixmap((MarkInterface::MarkTypes)ReachedBreakpointMark, *reachedBreakpointPixmap()); iface->setMarkPixmap((MarkInterface::MarkTypes)DisabledBreakpointMark, *disabledBreakpointPixmap()); iface->setEditableMarks( MarkInterface::Bookmark | BreakpointMark ); updateMarks(); } } void BreakpointModel::textDocumentCreated(KDevelop::IDocument* doc) { KTextEditor::MarkInterface *iface = qobject_cast(doc->textDocument()); if (iface) { // can't use new signal slot syntax here, MarkInterface is not a QObject connect(doc->textDocument(), SIGNAL(markChanged(KTextEditor::Document*,KTextEditor::Mark,KTextEditor::MarkInterface::MarkChangeAction)), this, SLOT(markChanged(KTextEditor::Document*,KTextEditor::Mark,KTextEditor::MarkInterface::MarkChangeAction))); connect(doc->textDocument(), SIGNAL(markContextMenuRequested(KTextEditor::Document*,KTextEditor::Mark,QPoint,bool&)), SLOT(markContextMenuRequested(KTextEditor::Document*,KTextEditor::Mark,QPoint,bool&))); } } void BreakpointModel::markContextMenuRequested(Document* document, Mark mark, const QPoint &pos, bool& handled) { int type = mark.type; qCDebug(DEBUGGER) << type; /* Is this a breakpoint mark, to begin with? */ if (!(type & AllBreakpointMarks)) return; Breakpoint *b = breakpoint(document->url(), mark.line); if (!b) { QMessageBox::critical(nullptr, i18n("Breakpoint not found"), i18n("Couldn't find breakpoint at %1:%2", document->url().toString(), mark.line)); return; } QMenu menu; QAction deleteAction(QIcon::fromTheme(QStringLiteral("edit-delete")), i18n("&Delete Breakpoint"), nullptr); QAction disableAction(QIcon::fromTheme(QStringLiteral("dialog-cancel")), i18n("&Disable Breakpoint"), nullptr); QAction enableAction(QIcon::fromTheme(QStringLiteral("dialog-ok-apply")), i18n("&Enable Breakpoint"), nullptr); menu.addAction(&deleteAction); if (b->enabled()) { menu.addAction(&disableAction); } else { menu.addAction(&enableAction); } QAction *a = menu.exec(pos); if (a == &deleteAction) { b->setDeleted(); } else if (a == &disableAction) { b->setData(Breakpoint::EnableColumn, Qt::Unchecked); } else if (a == &enableAction) { b->setData(Breakpoint::EnableColumn, Qt::Checked); } handled = true; } QVariant BreakpointModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Vertical) return QVariant(); if (role == Qt::DecorationRole ) { if (section == 0) return QIcon::fromTheme(QStringLiteral("dialog-ok-apply")); else if (section == 1) return QIcon::fromTheme(QStringLiteral("system-switch-user")); } if (role == Qt::DisplayRole) { if (section == 0 || section == 1) return ""; if (section == 2) return i18n("Type"); if (section == 3) return i18n("Location"); if (section == 4) return i18n("Condition"); } if (role == Qt::ToolTipRole) { if (section == 0) return i18n("Active status"); if (section == 1) return i18n("State"); return headerData(section, orientation, Qt::DisplayRole); } return QVariant(); } Qt::ItemFlags BreakpointModel::flags(const QModelIndex &index) const { /* FIXME: all this logic must be in item */ if (!index.isValid()) - return nullptr; + return Qt::NoItemFlags; if (index.column() == 0) return static_cast( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsUserCheckable); if (index.column() == Breakpoint::LocationColumn || index.column() == Breakpoint::ConditionColumn) return static_cast( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable); return static_cast(Qt::ItemIsEnabled | Qt::ItemIsSelectable); } QModelIndex BreakpointModel::breakpointIndex(KDevelop::Breakpoint* b, int column) { int row = m_breakpoints.indexOf(b); if (row == -1) return QModelIndex(); return index(row, column); } bool KDevelop::BreakpointModel::removeRows(int row, int count, const QModelIndex& parent) { if (count < 1 || (row < 0) || (row + count) > rowCount(parent)) return false; IBreakpointController* controller = breakpointController(); beginRemoveRows(parent, row, row+count-1); for (int i=0; i < count; ++i) { Breakpoint* b = m_breakpoints.at(row); b->m_deleted = true; if (controller) controller->breakpointAboutToBeDeleted(row); m_breakpoints.removeAt(row); b->m_model = nullptr; // To be changed: the controller is currently still responsible for deleting the breakpoint // object } endRemoveRows(); updateMarks(); scheduleSave(); return true; } int KDevelop::BreakpointModel::rowCount(const QModelIndex& parent) const { if (!parent.isValid()) { return m_breakpoints.count(); } return 0; } int KDevelop::BreakpointModel::columnCount(const QModelIndex& parent) const { Q_UNUSED(parent); return 5; } QVariant BreakpointModel::data(const QModelIndex& index, int role) const { if (!index.parent().isValid() && index.row() < m_breakpoints.count()) { return m_breakpoints.at(index.row())->data(index.column(), role); } return QVariant(); } bool KDevelop::BreakpointModel::setData(const QModelIndex& index, const QVariant& value, int role) { if (!index.parent().isValid() && index.row() < m_breakpoints.count() && (role == Qt::EditRole || role == Qt::CheckStateRole)) { return m_breakpoints.at(index.row())->setData(index.column(), value); } return false; } void BreakpointModel::updateState(int row, Breakpoint::BreakpointState state) { Breakpoint* breakpoint = m_breakpoints.at(row); if (state != breakpoint->m_state) { breakpoint->m_state = state; reportChange(breakpoint, Breakpoint::StateColumn); } } void BreakpointModel::updateHitCount(int row, int hitCount) { Breakpoint* breakpoint = m_breakpoints.at(row); if (hitCount != breakpoint->m_hitCount) { breakpoint->m_hitCount = hitCount; reportChange(breakpoint, Breakpoint::HitCountColumn); } } void BreakpointModel::updateErrorText(int row, const QString& errorText) { Breakpoint* breakpoint = m_breakpoints.at(row); if (breakpoint->m_errorText != errorText) { breakpoint->m_errorText = errorText; reportChange(breakpoint, Breakpoint::StateColumn); } if (!errorText.isEmpty()) { emit error(row, errorText); } } void BreakpointModel::notifyHit(int row) { emit hit(row); } void BreakpointModel::markChanged( KTextEditor::Document *document, KTextEditor::Mark mark, KTextEditor::MarkInterface::MarkChangeAction action) { int type = mark.type; /* Is this a breakpoint mark, to begin with? */ if (!(type & AllBreakpointMarks)) return; if (action == KTextEditor::MarkInterface::MarkAdded) { Breakpoint *b = breakpoint(document->url(), mark.line); if (b) { //there was already a breakpoint, so delete instead of adding b->setDeleted(); return; } Breakpoint *breakpoint = addCodeBreakpoint(document->url(), mark.line); KTextEditor::MovingInterface *moving = qobject_cast(document); if (moving) { KTextEditor::MovingCursor* cursor = moving->newMovingCursor(KTextEditor::Cursor(mark.line, 0)); // can't use new signal/slot syntax here, MovingInterface is not a QObject connect(document, SIGNAL(aboutToDeleteMovingInterfaceContent(KTextEditor::Document*)), this, SLOT(aboutToDeleteMovingInterfaceContent(KTextEditor::Document*)), Qt::UniqueConnection); breakpoint->setMovingCursor(cursor); } } else { // Find this breakpoint and delete it Breakpoint *b = breakpoint(document->url(), mark.line); if (b) { b->setDeleted(); } } #if 0 if ( KDevelop::ICore::self()->documentController()->activeDocument() && KDevelop::ICore::self()->documentController()->activeDocument()->textDocument() == document ) { //bring focus back to the editor // TODO probably want a different command here KDevelop::ICore::self()->documentController()->activateDocument(KDevelop::ICore::self()->documentController()->activeDocument()); } #endif } const QPixmap* BreakpointModel::breakpointPixmap() { static QPixmap pixmap=QIcon::fromTheme(QStringLiteral("breakpoint")).pixmap(QSize(16,16), QIcon::Active, QIcon::Off); return &pixmap; } const QPixmap* BreakpointModel::pendingBreakpointPixmap() { static QPixmap pixmap=QIcon::fromTheme(QStringLiteral("breakpoint")).pixmap(QSize(16,16), QIcon::Normal, QIcon::Off); return &pixmap; } const QPixmap* BreakpointModel::reachedBreakpointPixmap() { static QPixmap pixmap=QIcon::fromTheme(QStringLiteral("breakpoint")).pixmap(QSize(16,16), QIcon::Selected, QIcon::Off); return &pixmap; } const QPixmap* BreakpointModel::disabledBreakpointPixmap() { static QPixmap pixmap=QIcon::fromTheme(QStringLiteral("breakpoint")).pixmap(QSize(16,16), QIcon::Disabled, QIcon::Off); return &pixmap; } void BreakpointModel::toggleBreakpoint(const QUrl& url, const KTextEditor::Cursor& cursor) { Breakpoint *b = breakpoint(url, cursor.line()); if (b) { b->setDeleted(); } else { addCodeBreakpoint(url, cursor.line()); } } void BreakpointModel::reportChange(Breakpoint* breakpoint, Breakpoint::Column column) { // note: just a portion of Breakpoint::Column is displayed in this model! if (column >= 0 && column < columnCount()) { QModelIndex idx = breakpointIndex(breakpoint, column); Q_ASSERT(idx.isValid()); // make sure we don't pass invalid indices to dataChanged() emit dataChanged(idx, idx); } if (IBreakpointController* controller = breakpointController()) { int row = m_breakpoints.indexOf(breakpoint); Q_ASSERT(row != -1); controller->breakpointModelChanged(row, ColumnFlags(1 << column)); } scheduleSave(); } uint BreakpointModel::breakpointType(Breakpoint *breakpoint) { uint type = BreakpointMark; if (!breakpoint->enabled()) { type = DisabledBreakpointMark; } else if (breakpoint->hitCount() > 0) { type = ReachedBreakpointMark; } else if (breakpoint->state() == Breakpoint::PendingState) { type = PendingBreakpointMark; } return type; } void KDevelop::BreakpointModel::updateMarks() { if (m_dontUpdateMarks) return; //add marks foreach (Breakpoint *breakpoint, m_breakpoints) { if (breakpoint->kind() != Breakpoint::CodeBreakpoint) continue; if (breakpoint->line() == -1) continue; IDocument *doc = ICore::self()->documentController()->documentForUrl(breakpoint->url()); if (!doc) continue; KTextEditor::MarkInterface *mark = qobject_cast(doc->textDocument()); if (!mark) continue; uint type = breakpointType(breakpoint); IF_DEBUG( qCDebug(DEBUGGER) << type << breakpoint->url() << mark->mark(breakpoint->line()); ) { QSignalBlocker blocker(doc->textDocument()); if (mark->mark(breakpoint->line()) & AllBreakpointMarks) { if (!(mark->mark(breakpoint->line()) & type)) { mark->removeMark(breakpoint->line(), AllBreakpointMarks); mark->addMark(breakpoint->line(), type); } } else { mark->addMark(breakpoint->line(), type); } } } //remove marks foreach (IDocument *doc, ICore::self()->documentController()->openDocuments()) { KTextEditor::MarkInterface *mark = qobject_cast(doc->textDocument()); if (!mark) continue; { QSignalBlocker blocker(doc->textDocument()); foreach (KTextEditor::Mark *m, mark->marks()) { if (!(m->type & AllBreakpointMarks)) continue; IF_DEBUG( qCDebug(DEBUGGER) << m->line << m->type; ) foreach (Breakpoint *breakpoint, m_breakpoints) { if (breakpoint->kind() != Breakpoint::CodeBreakpoint) continue; if (doc->url() == breakpoint->url() && m->line == breakpoint->line()) { goto continueNextMark; } } mark->removeMark(m->line, AllBreakpointMarks); continueNextMark:; } } } } void BreakpointModel::documentSaved(KDevelop::IDocument* doc) { IF_DEBUG( qCDebug(DEBUGGER); ) foreach (Breakpoint *breakpoint, m_breakpoints) { if (breakpoint->movingCursor()) { if (breakpoint->movingCursor()->document() != doc->textDocument()) continue; if (breakpoint->movingCursor()->line() == breakpoint->line()) continue; m_dontUpdateMarks = true; breakpoint->setLine(breakpoint->movingCursor()->line()); m_dontUpdateMarks = false; } } } void BreakpointModel::aboutToDeleteMovingInterfaceContent(KTextEditor::Document* document) { foreach (Breakpoint *breakpoint, m_breakpoints) { if (breakpoint->movingCursor() && breakpoint->movingCursor()->document() == document) { breakpoint->setMovingCursor(nullptr); } } } void BreakpointModel::load() { KConfigGroup breakpoints = ICore::self()->activeSession()->config()->group("Breakpoints"); int count = breakpoints.readEntry("number", 0); if (count == 0) return; beginInsertRows(QModelIndex(), 0, count - 1); for (int i = 0; i < count; ++i) { if (!breakpoints.group(QString::number(i)).readEntry("kind", "").isEmpty()) { new Breakpoint(this, breakpoints.group(QString::number(i))); } } endInsertRows(); } void BreakpointModel::save() { m_dirty = false; KConfigGroup breakpoints = ICore::self()->activeSession()->config()->group("Breakpoints"); breakpoints.writeEntry("number", m_breakpoints.count()); int i = 0; foreach (Breakpoint *b, m_breakpoints) { KConfigGroup g = breakpoints.group(QString::number(i)); b->save(g); ++i; } breakpoints.sync(); } void BreakpointModel::scheduleSave() { if (m_dirty) return; m_dirty = true; QTimer::singleShot(0, this, &BreakpointModel::save); } QList KDevelop::BreakpointModel::breakpoints() const { return m_breakpoints; } Breakpoint* BreakpointModel::breakpoint(int row) { if (row >= m_breakpoints.count()) return nullptr; return m_breakpoints.at(row); } Breakpoint* BreakpointModel::addCodeBreakpoint() { beginInsertRows(QModelIndex(), m_breakpoints.count(), m_breakpoints.count()); Breakpoint* n = new Breakpoint(this, Breakpoint::CodeBreakpoint); endInsertRows(); return n; } Breakpoint* BreakpointModel::addCodeBreakpoint(const QUrl& url, int line) { Breakpoint* n = addCodeBreakpoint(); n->setLocation(url, line); return n; } Breakpoint* BreakpointModel::addCodeBreakpoint(const QString& expression) { Breakpoint* n = addCodeBreakpoint(); n->setExpression(expression); return n; } Breakpoint* BreakpointModel::addWatchpoint() { beginInsertRows(QModelIndex(), m_breakpoints.count(), m_breakpoints.count()); Breakpoint* n = new Breakpoint(this, Breakpoint::WriteBreakpoint); endInsertRows(); return n; } Breakpoint* BreakpointModel::addWatchpoint(const QString& expression) { Breakpoint* n = addWatchpoint(); n->setExpression(expression); return n; } Breakpoint* BreakpointModel::addReadWatchpoint() { beginInsertRows(QModelIndex(), m_breakpoints.count(), m_breakpoints.count()); Breakpoint* n = new Breakpoint(this, Breakpoint::ReadBreakpoint); endInsertRows(); return n; } Breakpoint* BreakpointModel::addReadWatchpoint(const QString& expression) { Breakpoint* n = addReadWatchpoint(); n->setExpression(expression); return n; } Breakpoint* BreakpointModel::addAccessWatchpoint() { beginInsertRows(QModelIndex(), m_breakpoints.count(), m_breakpoints.count()); Breakpoint* n = new Breakpoint(this, Breakpoint::AccessBreakpoint); endInsertRows(); return n; } Breakpoint* BreakpointModel::addAccessWatchpoint(const QString& expression) { Breakpoint* n = addAccessWatchpoint(); n->setExpression(expression); return n; } void BreakpointModel::registerBreakpoint(Breakpoint* breakpoint) { Q_ASSERT(!m_breakpoints.contains(breakpoint)); int row = m_breakpoints.size(); m_breakpoints << breakpoint; if (IBreakpointController* controller = breakpointController()) { controller->breakpointAdded(row); } scheduleSave(); } Breakpoint* BreakpointModel::breakpoint(const QUrl& url, int line) { foreach (Breakpoint *b, m_breakpoints) { if (b->url() == url && b->line() == line) { return b; } } return nullptr; } diff --git a/debugger/util/treemodel.cpp b/debugger/util/treemodel.cpp index 1300b488b7..665efec163 100644 --- a/debugger/util/treemodel.cpp +++ b/debugger/util/treemodel.cpp @@ -1,204 +1,204 @@ /* * This file is part of KDevelop * * Copyright 2008 Vladimir Prus * * 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. * * 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; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "treemodel.h" #include #include "treeitem.h" using namespace KDevelop; TreeModel::TreeModel(const QVector& headers, QObject *parent) : QAbstractItemModel(parent), headers_(headers), root_(nullptr) { } void TreeModel::setRootItem(TreeItem *item) { root_ = item; root_->fetchMoreChildren(); } TreeModel::~TreeModel() { delete root_; } int TreeModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return headers_.size(); } QVariant TreeModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); TreeItem *item = static_cast(index.internalPointer()); if (role == ItemRole) return QVariant::fromValue(item); return item->data(index.column(), role); } Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const { if (!index.isValid()) - return nullptr; + return Qt::NoItemFlags; return Qt::ItemIsEnabled | Qt::ItemIsSelectable; } QVariant TreeModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) return headers_[section]; return QVariant(); } QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const { if (!hasIndex(row, column, parent)) return QModelIndex(); TreeItem *parentItem; if (!parent.isValid()) parentItem = root_; else parentItem = static_cast(parent.internalPointer()); TreeItem *childItem = parentItem->child(row); if (childItem) return createIndex(row, column, childItem); else return createIndex(row, column, nullptr); } QModelIndex TreeModel::parent(const QModelIndex &index) const { if (!index.isValid()) return QModelIndex(); TreeItem *childItem = static_cast(index.internalPointer()); TreeItem *parentItem = childItem->parent(); if (parentItem == root_) return QModelIndex(); return createIndex(parentItem->row(), 0, parentItem); } int TreeModel::rowCount(const QModelIndex &parent) const { TreeItem *parentItem; if (parent.column() > 0) return 0; if (!parent.isValid()) parentItem = root_; else parentItem = static_cast(parent.internalPointer()); if(parentItem) return parentItem->childCount(); else return 0; } TreeItem* TreeModel::itemForIndex(const QModelIndex& index) const { if (!index.isValid()) return root_; else return static_cast(index.internalPointer()); } QModelIndex TreeModel::indexForItem(TreeItem *item, int column) const { if (item->parent() == nullptr) return QModelIndex(); if (TreeItem* parent = item->parent()) { /* FIXME: we might store row directly in item. */ int row = parent->childItems.indexOf(item); Q_ASSERT(row != -1); return createIndex(row, column, item); } else { return QModelIndex(); } } void TreeModel::expanded(const QModelIndex &index) { TreeItem* item = itemForIndex(index); QObject::connect(item, &TreeItem::allChildrenFetched, this, &TreeModel::itemChildrenReady); if (item->hasMore() && item->childCount() == 1) item->fetchMoreChildren(); else emit itemChildrenReady(); item->setExpanded(true); } void TreeModel::collapsed(const QModelIndex &index) { TreeItem* item = itemForIndex(index); item->setExpanded(false); } void TreeModel::clicked(const QModelIndex &index) { TreeItem* item = itemForIndex(index); item->clicked(); } bool TreeModel::setData(const QModelIndex& index, const QVariant& value, int role) { /* FIXME: CheckStateRole is dirty. Should we pass the role to the item? */ if (index.isValid() && (role == Qt::EditRole || role == Qt::CheckStateRole)) { TreeItem *item = static_cast(index.internalPointer()); item->setColumn(index.column(), value); return true; } return false; } KDevelop::TreeItem* KDevelop::TreeModel::root() const { return root_; } diff --git a/interfaces/idocumentcontroller.h b/interfaces/idocumentcontroller.h index 2be5de32b6..6ee1ed829f 100644 --- a/interfaces/idocumentcontroller.h +++ b/interfaces/idocumentcontroller.h @@ -1,234 +1,234 @@ /*************************************************************************** * Copyright 2007 Alexander Dymo * * * * 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. * ***************************************************************************/ #ifndef KDEVPLATFORM_IDOCUMENTCONTROLLER_H #define KDEVPLATFORM_IDOCUMENTCONTROLLER_H #include #include #include #include #include #include "interfacesexport.h" #include "idocument.h" namespace KTextEditor { class View; } namespace KDevelop { class ICore; class KDEVPLATFORMINTERFACES_EXPORT IDocumentFactory { public: virtual ~IDocumentFactory() {} virtual IDocument* create(const QUrl&, ICore* ) = 0; }; /** * * Allows to access the open documents and also open new ones * * @class IDocumentController */ class KDEVPLATFORMINTERFACES_EXPORT IDocumentController: public QObject { Q_OBJECT public: enum DocumentActivation { DefaultMode = 0, /**Activate document and create a view if no other flags passed.*/ DoNotActivate = 1, /**Don't activate the Document.*/ DoNotCreateView = 2, /**Don't create and show the view for the Document.*/ DoNotFocus = 4, /**Don't change the keyboard focus.*/ DoNotAddToRecentOpen = 8, /**Don't add the document to the File/Open Recent menu.*/ }; Q_DECLARE_FLAGS(DocumentActivationParams, DocumentActivation) explicit IDocumentController(QObject *parent); /** * Finds the first document object corresponding to a given url. * * @param url The Url of the document. * @return The corresponding document, or null if not found. */ virtual KDevelop::IDocument* documentForUrl( const QUrl & url ) const = 0; /// @return The list of all open documents virtual QList openDocuments() const = 0; /** * Returns the curently active or focused document. * * @return The active document. */ virtual KDevelop::IDocument* activeDocument() const = 0; virtual void activateDocument( KDevelop::IDocument * document, const KTextEditor::Range& range = KTextEditor::Range::invalid() ) = 0; virtual void registerDocumentForMimetype( const QString&, KDevelop::IDocumentFactory* ) = 0; virtual bool saveAllDocuments(KDevelop::IDocument::DocumentSaveMode mode = KDevelop::IDocument::Default) = 0; virtual bool saveSomeDocuments(const QList& list, KDevelop::IDocument::DocumentSaveMode mode = KDevelop::IDocument::Default) = 0; virtual bool saveAllDocumentsForWindow(KParts::MainWindow* mw, IDocument::DocumentSaveMode mode, bool currentAreaOnly = false) = 0; /// Opens a text document containing the @p data text. virtual KDevelop::IDocument* openDocumentFromText( const QString& data ) = 0; virtual IDocumentFactory* factory(const QString& mime) const = 0; virtual KTextEditor::Document* globalTextEditorInstance()=0; /** * @returns the KTextEditor::View of the current document, in case it is a text document */ virtual KTextEditor::View* activeTextDocumentView() const = 0; public Q_SLOTS: /** * Opens a new or existing document. * * @param url The full Url of the document to open. * @param cursor The location information, if applicable. * @param activationParams Indicates whether to fully activate the document. */ KDevelop::IDocument* openDocument( const QUrl &url, const KTextEditor::Cursor& cursor, - DocumentActivationParams activationParams = nullptr, + DocumentActivationParams activationParams = {}, const QString& encoding = {}); /** * Opens a new or existing document. * * @param url The full Url of the document to open. * @param range The range of text to select, if applicable. * @param activationParams Indicates whether to fully activate the document * @param buddy Optional buddy document. If 0, the registered IBuddyDocumentFinder * for the URL's mimetype will be queried to find a fitting buddy. * If a buddy was found (or passed) @p url will be opened next * to its buddy. * * @return The opened document */ virtual KDevelop::IDocument* openDocument( const QUrl &url, const KTextEditor::Range& range = KTextEditor::Range::invalid(), - DocumentActivationParams activationParams = nullptr, + DocumentActivationParams activationParams = {}, const QString& encoding = {}, IDocument* buddy = nullptr) = 0; /** * Opens a document from the IDocument instance. * * @param doc The IDocument to add * @param range The location information, if applicable. * @param activationParams Indicates whether to fully activate the document. * @param buddy Optional buddy document. If 0, the registered IBuddyDocumentFinder * for the Documents mimetype will be queried to find a fitting buddy. * If a buddy was found (or passed) @p url will be opened next * to its buddy. */ virtual bool openDocument(IDocument* doc, const KTextEditor::Range& range = KTextEditor::Range::invalid(), - DocumentActivationParams activationParams = nullptr, + DocumentActivationParams activationParams = {}, IDocument* buddy = nullptr) = 0; /** * Opens a new or existing document. * * @param url The full Url of the document to open. * @param prefName The name of the preferred KPart to open that document */ virtual KDevelop::IDocument* openDocument( const QUrl &url, const QString& prefName ) = 0; virtual bool closeAllDocuments() = 0; Q_SIGNALS: /// Emitted when the document has been activated. void documentActivated( KDevelop::IDocument* document ); /** * Emitted whenever the active cursor jumps from one document+cursor to another, * as e.g. caused by a call to openDocument(..). * * This is also emitted when a document is only activated. Then previousDocument is zero. */ void documentJumpPerformed( KDevelop::IDocument* newDocument, KTextEditor::Cursor newCursor, KDevelop::IDocument* previousDocument, KTextEditor::Cursor previousCursor); /// Emitted when a document has been saved. void documentSaved( KDevelop::IDocument* document ); /** * Emitted when a document has been opened. * * NOTE: The document may not be loaded from disk/network at this point. * NOTE: No views exist for the document at the time this signal is emitted. */ void documentOpened( KDevelop::IDocument* document ); /** * Emitted when a document has been loaded. * * NOTE: No views exist for the document at the time this signal is emitted. */ void documentLoaded( KDevelop::IDocument* document ); /** * Emitted when a text document has been loaded, and the text document created. * * NOTE: no views exist for the document at the time this signal is emitted. */ void textDocumentCreated( KDevelop::IDocument* document ); /// Emitted when a document has been closed. void documentClosed( KDevelop::IDocument* document ); /** * This is emitted when the document state(the relationship * between the file in the editor and the file stored on disk) changes. */ void documentStateChanged( KDevelop::IDocument* document ); /// This is emitted when the document content changed. void documentContentChanged( KDevelop::IDocument* document ); /** * Emitted when a document has been loaded, but before documentLoaded(..) is emitted. * * This allows parts of kdevplatform to prepare data-structures that can be used by other parts * during documentLoaded(..). */ void documentLoadedPrepare( KDevelop::IDocument* document ); /// Emitted when a document url has changed. void documentUrlChanged( KDevelop::IDocument* document ); friend class IDocument; }; } // namespace KDevelop Q_DECLARE_OPERATORS_FOR_FLAGS(KDevelop::IDocumentController::DocumentActivationParams) #endif diff --git a/project/projectmodel.cpp b/project/projectmodel.cpp index ed9e82418a..e504983589 100644 --- a/project/projectmodel.cpp +++ b/project/projectmodel.cpp @@ -1,1170 +1,1170 @@ /* This file is part of KDevelop Copyright 2005 Roberto Raggi Copyright 2007 Andreas Pakulat Copyright 2007 Aleix Pol 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 "projectmodel.h" #include #include #include #include #include #include #include #include #include "interfaces/iprojectfilemanager.h" #include #include "debug.h" #include "path.h" namespace KDevelop { QStringList removeProjectBasePath( const QStringList& fullpath, KDevelop::ProjectBaseItem* item ) { QStringList result = fullpath; if( item ) { KDevelop::ProjectModel* model = KDevelop::ICore::self()->projectController()->projectModel(); QStringList basePath = model->pathFromIndex( model->indexFromItem( item ) ); if( basePath.count() >= fullpath.count() ) { return QStringList(); } for( int i = 0; i < basePath.count(); i++ ) { result.takeFirst(); } } return result; } QStringList joinProjectBasePath( const QStringList& partialpath, KDevelop::ProjectBaseItem* item ) { QStringList basePath; if( item ) { KDevelop::ProjectModel* model = KDevelop::ICore::self()->projectController()->projectModel(); basePath = model->pathFromIndex( model->indexFromItem( item ) ); } return basePath + partialpath; } inline uint indexForPath( const Path& path ) { return IndexedString::indexForString(path.pathOrUrl()); } class ProjectModelPrivate { public: explicit ProjectModelPrivate( ProjectModel* model ): model( model ) { } ProjectBaseItem* rootItem; ProjectModel* model; ProjectBaseItem* itemFromIndex( const QModelIndex& idx ) { if( !idx.isValid() ) { return rootItem; } if( idx.model() != model ) { return nullptr; } return model->itemFromIndex( idx ); } // a hash of IndexedString::indexForString(path) <-> ProjectBaseItem for fast lookup QMultiHash pathLookupTable; }; class ProjectBaseItemPrivate { public: ProjectBaseItemPrivate() : project(nullptr), parent(nullptr), row(-1), model(nullptr), m_pathIndex(0) {} IProject* project; ProjectBaseItem* parent; int row; QList children; QString text; ProjectBaseItem::ProjectItemType type; Qt::ItemFlags flags; ProjectModel* model; Path m_path; uint m_pathIndex; QString iconName; ProjectBaseItem::RenameStatus renameBaseItem(ProjectBaseItem* item, const QString& newName) { if (item->parent()) { foreach(ProjectBaseItem* sibling, item->parent()->children()) { if (sibling->text() == newName) { return ProjectBaseItem::ExistingItemSameName; } } } item->setText( newName ); return ProjectBaseItem::RenameOk; } ProjectBaseItem::RenameStatus renameFileOrFolder(ProjectBaseItem* item, const QString& newName) { Q_ASSERT(item->file() || item->folder()); if (newName.contains('/')) { return ProjectBaseItem::InvalidNewName; } if (item->text() == newName) { return ProjectBaseItem::RenameOk; } Path newPath = item->path(); newPath.setLastPathSegment(newName); auto job = KIO::stat(newPath.toUrl(), KIO::StatJob::SourceSide, 0, KIO::HideProgressInfo); if (job->exec()) { // file/folder exists already return ProjectBaseItem::ExistingItemSameName; } if( !item->project() || !item->project()->projectFileManager() ) { return renameBaseItem(item, newName); } else if( item->folder() && item->project()->projectFileManager()->renameFolder(item->folder(), newPath) ) { return ProjectBaseItem::RenameOk; } else if ( item->file() && item->project()->projectFileManager()->renameFile(item->file(), newPath) ) { return ProjectBaseItem::RenameOk; } else { return ProjectBaseItem::ProjectManagerRenameFailed; } } }; ProjectBaseItem::ProjectBaseItem( IProject* project, const QString &name, ProjectBaseItem *parent ) : d_ptr(new ProjectBaseItemPrivate) { Q_ASSERT(!name.isEmpty() || !parent); Q_D(ProjectBaseItem); d->project = project; d->text = name; d->flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled; if( parent ) { parent->appendRow( this ); } } ProjectBaseItem::~ProjectBaseItem() { Q_D(ProjectBaseItem); if (model() && d->m_pathIndex) { model()->d->pathLookupTable.remove(d->m_pathIndex, this); } if( parent() ) { parent()->takeRow( d->row ); } else if( model() ) { model()->takeRow( d->row ); } removeRows(0, d->children.size()); delete d; } ProjectBaseItem* ProjectBaseItem::child( int row ) const { Q_D(const ProjectBaseItem); if( row < 0 || row >= d->children.length() ) { return nullptr; } return d->children.at( row ); } QList< ProjectBaseItem* > ProjectBaseItem::children() const { Q_D(const ProjectBaseItem); return d->children; } ProjectBaseItem* ProjectBaseItem::takeRow(int row) { Q_D(ProjectBaseItem); Q_ASSERT(row >= 0 && row < d->children.size()); if( model() ) { model()->beginRemoveRows(index(), row, row); } ProjectBaseItem* olditem = d->children.takeAt( row ); olditem->d_func()->parent = nullptr; olditem->d_func()->row = -1; olditem->setModel( nullptr ); for(int i=row; id_func()->row--; Q_ASSERT(child(i)->d_func()->row==i); } if( model() ) { model()->endRemoveRows(); } return olditem; } void ProjectBaseItem::removeRow( int row ) { delete takeRow( row ); } void ProjectBaseItem::removeRows(int row, int count) { if (!count) { return; } Q_D(ProjectBaseItem); Q_ASSERT(row >= 0 && row + count <= d->children.size()); if( model() ) { model()->beginRemoveRows(index(), row, row + count - 1); } //NOTE: we unset parent, row and model manually to speed up the deletion if (row == 0 && count == d->children.size()) { // optimize if we want to delete all foreach(ProjectBaseItem* item, d->children) { item->d_func()->parent = nullptr; item->d_func()->row = -1; item->setModel( nullptr ); delete item; } d->children.clear(); } else { for (int i = row; i < count; ++i) { ProjectBaseItem* item = d->children.at(i); item->d_func()->parent = nullptr; item->d_func()->row = -1; item->setModel( nullptr ); delete d->children.takeAt( row ); } for(int i = row; i < d->children.size(); ++i) { d->children.at(i)->d_func()->row--; Q_ASSERT(child(i)->d_func()->row==i); } } if( model() ) { model()->endRemoveRows(); } } QModelIndex ProjectBaseItem::index() const { if( model() ) { return model()->indexFromItem( this ); } return QModelIndex(); } int ProjectBaseItem::rowCount() const { Q_D(const ProjectBaseItem); return d->children.count(); } int ProjectBaseItem::type() const { return ProjectBaseItem::BaseItem; } ProjectModel* ProjectBaseItem::model() const { Q_D(const ProjectBaseItem); return d->model; } ProjectBaseItem* ProjectBaseItem::parent() const { Q_D(const ProjectBaseItem); if( model() && model()->d->rootItem == d->parent ) { return nullptr; } return d->parent; } int ProjectBaseItem::row() const { Q_D(const ProjectBaseItem); return d->row; } QString ProjectBaseItem::text() const { Q_D(const ProjectBaseItem); if( project() && !parent() ) { return project()->name(); } else { return d->text; } } void ProjectBaseItem::setModel( ProjectModel* model ) { Q_D(ProjectBaseItem); if (model == d->model) { return; } if (d->model && d->m_pathIndex) { d->model->d->pathLookupTable.remove(d->m_pathIndex, this); } d->model = model; if (model && d->m_pathIndex) { model->d->pathLookupTable.insert(d->m_pathIndex, this); } foreach( ProjectBaseItem* item, d->children ) { item->setModel( model ); } } void ProjectBaseItem::setRow( int row ) { Q_D(ProjectBaseItem); d->row = row; } void ProjectBaseItem::setText( const QString& text ) { Q_ASSERT(!text.isEmpty() || !parent()); Q_D(ProjectBaseItem); d->text = text; if( d->model ) { QModelIndex idx = index(); emit d->model->dataChanged(idx, idx); } } ProjectBaseItem::RenameStatus ProjectBaseItem::rename(const QString& newName) { Q_D(ProjectBaseItem); return d->renameBaseItem(this, newName); } KDevelop::ProjectBaseItem::ProjectItemType baseType( int type ) { if( type == KDevelop::ProjectBaseItem::Folder || type == KDevelop::ProjectBaseItem::BuildFolder ) return KDevelop::ProjectBaseItem::Folder; if( type == KDevelop::ProjectBaseItem::Target || type == KDevelop::ProjectBaseItem::ExecutableTarget || type == KDevelop::ProjectBaseItem::LibraryTarget) return KDevelop::ProjectBaseItem::Target; return static_cast( type ); } bool ProjectBaseItem::lessThan( const KDevelop::ProjectBaseItem* item ) const { if(item->type() >= KDevelop::ProjectBaseItem::CustomProjectItemType ) { // For custom types we want to make sure that if they override lessThan, then we // prefer their lessThan implementation return !item->lessThan( this ); } KDevelop::ProjectBaseItem::ProjectItemType leftType=baseType(type()), rightType=baseType(item->type()); if(leftType==rightType) { if(leftType==KDevelop::ProjectBaseItem::File) { return file()->fileName().compare(item->file()->fileName(), Qt::CaseInsensitive) < 0; } return this->text()text(); } else { return leftTypepath() < item2->path(); } IProject* ProjectBaseItem::project() const { Q_D(const ProjectBaseItem); return d->project; } void ProjectBaseItem::appendRow( ProjectBaseItem* item ) { Q_D(ProjectBaseItem); if( !item ) { return; } if( item->parent() ) { // Proper way is to first removeRow() on the original parent, then appendRow on this one qCWarning(PROJECT) << "Ignoring double insertion of item" << item; return; } // this is too slow... O(n) and thankfully not a problem anyways // Q_ASSERT(!d->children.contains(item)); int startrow,endrow; if( model() ) { startrow = endrow = d->children.count(); model()->beginInsertRows(index(), startrow, endrow); } d->children.append( item ); item->setRow( d->children.count() - 1 ); item->d_func()->parent = this; item->setModel( model() ); if( model() ) { model()->endInsertRows(); } } Path ProjectBaseItem::path() const { Q_D(const ProjectBaseItem); return d->m_path; } QString ProjectBaseItem::baseName() const { return text(); } void ProjectBaseItem::setPath( const Path& path) { Q_D(ProjectBaseItem); if (model() && d->m_pathIndex) { model()->d->pathLookupTable.remove(d->m_pathIndex, this); } d->m_path = path; d->m_pathIndex = indexForPath(path); setText( path.lastPathSegment() ); if (model() && d->m_pathIndex) { model()->d->pathLookupTable.insert(d->m_pathIndex, this); } } Qt::ItemFlags ProjectBaseItem::flags() { Q_D(ProjectBaseItem); return d->flags; } Qt::DropActions ProjectModel::supportedDropActions() const { return (Qt::DropActions)(Qt::MoveAction); } void ProjectBaseItem::setFlags(Qt::ItemFlags flags) { Q_D(ProjectBaseItem); d->flags = flags; if(d->model) emit d->model->dataChanged(index(), index()); } QString ProjectBaseItem::iconName() const { return QString(); } ProjectFolderItem *ProjectBaseItem::folder() const { return nullptr; } ProjectTargetItem *ProjectBaseItem::target() const { return nullptr; } ProjectExecutableTargetItem *ProjectBaseItem::executable() const { return nullptr; } ProjectFileItem *ProjectBaseItem::file() const { return nullptr; } QList ProjectBaseItem::folderList() const { QList lst; for ( int i = 0; i < rowCount(); ++i ) { ProjectBaseItem* item = child( i ); if ( item->type() == Folder || item->type() == BuildFolder ) { ProjectFolderItem *kdevitem = dynamic_cast( item ); if ( kdevitem ) lst.append( kdevitem ); } } return lst; } QList ProjectBaseItem::targetList() const { QList lst; for ( int i = 0; i < rowCount(); ++i ) { ProjectBaseItem* item = child( i ); if ( item->type() == Target || item->type() == LibraryTarget || item->type() == ExecutableTarget ) { ProjectTargetItem *kdevitem = dynamic_cast( item ); if ( kdevitem ) lst.append( kdevitem ); } } return lst; } QList ProjectBaseItem::fileList() const { QList lst; for ( int i = 0; i < rowCount(); ++i ) { ProjectBaseItem* item = child( i ); Q_ASSERT(item); if ( item && item->type() == File ) { ProjectFileItem *kdevitem = dynamic_cast( item ); if ( kdevitem ) lst.append( kdevitem ); } } return lst; } void ProjectModel::clear() { d->rootItem->removeRows(0, d->rootItem->rowCount()); } ProjectFolderItem::ProjectFolderItem(IProject* project, const Path& path, ProjectBaseItem* parent) : ProjectBaseItem( project, path.lastPathSegment(), parent ) { setPath( path ); setFlags(flags() | Qt::ItemIsDropEnabled); if (project && project->path() != path) setFlags(flags() | Qt::ItemIsDragEnabled); } ProjectFolderItem::ProjectFolderItem( const QString & name, ProjectBaseItem * parent ) : ProjectBaseItem( parent->project(), name, parent ) { setPath( Path(parent->path(), name) ); setFlags(flags() | Qt::ItemIsDropEnabled); if (project() && project()->path() != path()) setFlags(flags() | Qt::ItemIsDragEnabled); } ProjectFolderItem::~ProjectFolderItem() { } void ProjectFolderItem::setPath( const Path& path ) { ProjectBaseItem::setPath(path); propagateRename(path); } ProjectFolderItem *ProjectFolderItem::folder() const { return const_cast(this); } int ProjectFolderItem::type() const { return ProjectBaseItem::Folder; } QString ProjectFolderItem::folderName() const { return baseName(); } void ProjectFolderItem::propagateRename( const Path& newBase ) const { Path path = newBase; path.addPath(QStringLiteral("dummy")); foreach( KDevelop::ProjectBaseItem* child, children() ) { path.setLastPathSegment( child->text() ); child->setPath( path ); const ProjectFolderItem* folder = child->folder(); if ( folder ) { folder->propagateRename( path ); } } } ProjectBaseItem::RenameStatus ProjectFolderItem::rename(const QString& newName) { return d_ptr->renameFileOrFolder(this, newName); } bool ProjectFolderItem::hasFileOrFolder(const QString& name) const { foreach ( ProjectBaseItem* item, children() ) { if ( (item->type() == Folder || item->type() == File || item->type() == BuildFolder) && name == item->baseName() ) { return true; } } return false; } bool ProjectBaseItem::isProjectRoot() const { return parent()==nullptr; } ProjectBuildFolderItem::ProjectBuildFolderItem(IProject* project, const Path& path, ProjectBaseItem *parent) : ProjectFolderItem( project, path, parent ) { } ProjectBuildFolderItem::ProjectBuildFolderItem( const QString& name, ProjectBaseItem* parent ) : ProjectFolderItem( name, parent ) { } QString ProjectFolderItem::iconName() const { return QStringLiteral("folder"); } int ProjectBuildFolderItem::type() const { return ProjectBaseItem::BuildFolder; } QString ProjectBuildFolderItem::iconName() const { return QStringLiteral("folder-development"); } ProjectFileItem::ProjectFileItem( IProject* project, const Path& path, ProjectBaseItem* parent ) : ProjectBaseItem( project, path.lastPathSegment(), parent ) { setFlags(flags() | Qt::ItemIsDragEnabled); setPath( path ); } ProjectFileItem::ProjectFileItem( const QString& name, ProjectBaseItem* parent ) : ProjectBaseItem( parent->project(), name, parent ) { setFlags(flags() | Qt::ItemIsDragEnabled); setPath( Path(parent->path(), name) ); } ProjectFileItem::~ProjectFileItem() { if( project() && d_ptr->m_pathIndex ) { project()->removeFromFileSet( this ); } } IndexedString ProjectFileItem::indexedPath() const { return IndexedString::fromIndex( d_ptr->m_pathIndex ); } ProjectBaseItem::RenameStatus ProjectFileItem::rename(const QString& newName) { return d_ptr->renameFileOrFolder(this, newName); } QString ProjectFileItem::fileName() const { return baseName(); } // Maximum length of a string to still consider it as a file extension which we cache // This has to be a slow value, so that we don't fill our file extension cache with crap static const int maximumCacheExtensionLength = 3; bool isNumeric(const QStringRef& str) { int len = str.length(); if(len == 0) return false; for(int a = 0; a < len; ++a) if(!str.at(a).isNumber()) return false; return true; } class IconNameCache { public: QString iconNameForPath(const Path& path, const QString& fileName) { // find icon name based on file extension, if possible QString extension; int extensionStart = fileName.lastIndexOf(QLatin1Char('.')); if( extensionStart != -1 && fileName.length() - extensionStart - 1 <= maximumCacheExtensionLength ) { QStringRef extRef = fileName.midRef(extensionStart + 1); if( isNumeric(extRef) ) { // don't cache numeric extensions extRef.clear(); } if( !extRef.isEmpty() ) { extension = extRef.toString(); QMutexLocker lock(&mutex); QHash< QString, QString >::const_iterator it = fileExtensionToIcon.constFind( extension ); if( it != fileExtensionToIcon.constEnd() ) { return *it; } } } QMimeType mime = QMimeDatabase().mimeTypeForFile(path.lastPathSegment(), QMimeDatabase::MatchExtension); // no I/O QMutexLocker lock(&mutex); QHash< QString, QString >::const_iterator it = mimeToIcon.constFind(mime.name()); QString iconName; if ( it == mimeToIcon.constEnd() ) { iconName = mime.iconName(); if (iconName.isEmpty()) { iconName = QStringLiteral("none"); } mimeToIcon.insert(mime.name(), iconName); } else { iconName = *it; } if ( !extension.isEmpty() ) { fileExtensionToIcon.insert(extension, iconName); } return iconName; } QMutex mutex; QHash mimeToIcon; QHash fileExtensionToIcon; }; Q_GLOBAL_STATIC(IconNameCache, s_cache); QString ProjectFileItem::iconName() const { // think of d_ptr->iconName as mutable, possible since d_ptr is not const if (d_ptr->iconName.isEmpty()) { // lazy load implementation of icon lookup d_ptr->iconName = s_cache->iconNameForPath( d_ptr->m_path, d_ptr->text ); // we should always get *some* icon name back Q_ASSERT(!d_ptr->iconName.isEmpty()); } return d_ptr->iconName; } void ProjectFileItem::setPath( const Path& path ) { if (path == d_ptr->m_path) { return; } if( project() && d_ptr->m_pathIndex ) { // remove from fileset if we are in there project()->removeFromFileSet( this ); } ProjectBaseItem::setPath( path ); if( project() && d_ptr->m_pathIndex ) { // add to fileset with new path project()->addToFileSet( this ); } // invalidate icon name for future lazy-loaded updated d_ptr->iconName.clear(); } int ProjectFileItem::type() const { return ProjectBaseItem::File; } ProjectFileItem *ProjectFileItem::file() const { return const_cast( this ); } ProjectTargetItem::ProjectTargetItem( IProject* project, const QString &name, ProjectBaseItem *parent ) : ProjectBaseItem( project, name, parent ) { setFlags(flags() | Qt::ItemIsDropEnabled); } QString ProjectTargetItem::iconName() const { return QStringLiteral("system-run"); } void ProjectTargetItem::setPath( const Path& path ) { // don't call base class, it calls setText with the new path's filename // which we do not want for target items d_ptr->m_path = path; } int ProjectTargetItem::type() const { return ProjectBaseItem::Target; } ProjectTargetItem *ProjectTargetItem::target() const { return const_cast( this ); } ProjectExecutableTargetItem::ProjectExecutableTargetItem( IProject* project, const QString &name, ProjectBaseItem *parent ) : ProjectTargetItem(project, name, parent) { } ProjectExecutableTargetItem *ProjectExecutableTargetItem::executable() const { return const_cast( this ); } int ProjectExecutableTargetItem::type() const { return ProjectBaseItem::ExecutableTarget; } ProjectLibraryTargetItem::ProjectLibraryTargetItem( IProject* project, const QString &name, ProjectBaseItem *parent ) : ProjectTargetItem(project, name, parent) {} int ProjectLibraryTargetItem::type() const { return ProjectBaseItem::LibraryTarget; } QModelIndex ProjectModel::pathToIndex(const QStringList& tofetch_) const { if(tofetch_.isEmpty()) return QModelIndex(); QStringList tofetch(tofetch_); if(tofetch.last().isEmpty()) tofetch.takeLast(); QModelIndex current=index(0,0, QModelIndex()); QModelIndex ret; for(int a = 0; a < tofetch.size(); ++a) { const QString& currentName = tofetch[a]; bool matched = false; QModelIndexList l = match(current, Qt::DisplayRole, currentName, -1, Qt::MatchExactly); foreach(const QModelIndex& idx, l) { //If this is not the last item, only match folders, as there may be targets and folders with the same name if(a == tofetch.size()-1 || itemFromIndex(idx)->folder()) { ret = idx; current = index(0,0, ret); matched = true; break; } } if(!matched) { ret = QModelIndex(); break; } } Q_ASSERT(!ret.isValid() || data(ret).toString()==tofetch.last()); return ret; } QStringList ProjectModel::pathFromIndex(const QModelIndex& index) const { if (!index.isValid()) return QStringList(); QModelIndex idx = index; QStringList list; do { QString t = data(idx, Qt::DisplayRole).toString(); list.prepend(t); QModelIndex parent = idx.parent(); idx = parent.sibling(parent.row(), index.column()); } while (idx.isValid()); return list; } int ProjectModel::columnCount( const QModelIndex& ) const { return 1; } int ProjectModel::rowCount( const QModelIndex& parent ) const { ProjectBaseItem* item = d->itemFromIndex( parent ); return item ? item->rowCount() : 0; } QModelIndex ProjectModel::parent( const QModelIndex& child ) const { if( child.isValid() ) { ProjectBaseItem* item = static_cast( child.internalPointer() ); return indexFromItem( item ); } return QModelIndex(); } QModelIndex ProjectModel::indexFromItem( const ProjectBaseItem* item ) const { if( item && item->d_func()->parent ) { return createIndex( item->row(), 0, item->d_func()->parent ); } return QModelIndex(); } ProjectBaseItem* ProjectModel::itemFromIndex( const QModelIndex& index ) const { if( index.row() >= 0 && index.column() == 0 && index.model() == this ) { ProjectBaseItem* parent = static_cast( index.internalPointer() ); if( parent ) { return parent->child( index.row() ); } } return nullptr; } QVariant ProjectModel::data( const QModelIndex& index, int role ) const { static const QSet allowedRoles = { Qt::DisplayRole, Qt::ToolTipRole, Qt::DecorationRole, ProjectItemRole, ProjectRole, UrlRole }; if( allowedRoles.contains(role) && index.isValid() ) { ProjectBaseItem* item = itemFromIndex( index ); if( item ) { switch(role) { case Qt::DecorationRole: return QIcon::fromTheme(item->iconName()); case Qt::ToolTipRole: return item->path().pathOrUrl(); case Qt::DisplayRole: return item->text(); case ProjectItemRole: return QVariant::fromValue(item); case UrlRole: return item->path().toUrl(); case ProjectRole: return QVariant::fromValue(item->project()); } } } return QVariant(); } ProjectModel::ProjectModel( QObject *parent ) : QAbstractItemModel( parent ), d( new ProjectModelPrivate( this ) ) { d->rootItem = new ProjectBaseItem( nullptr, QString(), nullptr ); d->rootItem->setModel( this ); } ProjectModel::~ProjectModel() { d->rootItem->setModel(nullptr); delete d->rootItem; delete d; } ProjectVisitor::ProjectVisitor() { } QModelIndex ProjectModel::index( int row, int column, const QModelIndex& parent ) const { ProjectBaseItem* parentItem = d->itemFromIndex( parent ); if( parentItem && row >= 0 && row < parentItem->rowCount() && column == 0 ) { return createIndex( row, column, parentItem ); } return QModelIndex(); } void ProjectModel::appendRow( ProjectBaseItem* item ) { d->rootItem->appendRow( item ); } void ProjectModel::removeRow( int row ) { d->rootItem->removeRow( row ); } ProjectBaseItem* ProjectModel::takeRow( int row ) { return d->rootItem->takeRow( row ); } ProjectBaseItem* ProjectModel::itemAt(int row) const { return d->rootItem->child(row); } QList< ProjectBaseItem* > ProjectModel::topItems() const { return d->rootItem->children(); } Qt::ItemFlags ProjectModel::flags(const QModelIndex& index) const { ProjectBaseItem* item = itemFromIndex( index ); if(item) return item->flags(); - else - return nullptr; + + return Qt::NoItemFlags; } bool ProjectModel::insertColumns(int, int, const QModelIndex&) { // Not supported return false; } bool ProjectModel::insertRows(int, int, const QModelIndex&) { // Not supported return false; } bool ProjectModel::setData(const QModelIndex&, const QVariant&, int) { // Not supported return false; } QList ProjectModel::itemsForPath(const IndexedString& path) const { return d->pathLookupTable.values(path.index()); } ProjectBaseItem* ProjectModel::itemForPath(const IndexedString& path) const { return d->pathLookupTable.value(path.index()); } void ProjectVisitor::visit( ProjectModel* model ) { foreach( ProjectBaseItem* item, model->topItems() ) { visit( item->project() ); } } void ProjectVisitor::visit ( IProject* prj ) { visit( prj->projectItem() ); } void ProjectVisitor::visit ( ProjectBuildFolderItem* folder ) { visit(static_cast(folder)); } void ProjectVisitor::visit ( ProjectExecutableTargetItem* exec ) { foreach( ProjectFileItem* item, exec->fileList() ) { visit( item ); } } void ProjectVisitor::visit ( ProjectFolderItem* folder ) { foreach( ProjectFileItem* item, folder->fileList() ) { visit( item ); } foreach( ProjectTargetItem* item, folder->targetList() ) { if( item->type() == ProjectBaseItem::LibraryTarget ) { visit( dynamic_cast( item ) ); } else if( item->type() == ProjectBaseItem::ExecutableTarget ) { visit( dynamic_cast( item ) ); } } foreach( ProjectFolderItem* item, folder->folderList() ) { if( item->type() == ProjectBaseItem::BuildFolder ) { visit( dynamic_cast( item ) ); } else if( item->type() == ProjectBaseItem::Folder ) { visit( dynamic_cast( item ) ); } } } void ProjectVisitor::visit ( ProjectFileItem* ) { } void ProjectVisitor::visit ( ProjectLibraryTargetItem* lib ) { foreach( ProjectFileItem* item, lib->fileList() ) { visit( item ); } } ProjectVisitor::~ProjectVisitor() { } } diff --git a/shell/documentcontroller.h b/shell/documentcontroller.h index 8c16fb0770..de4af768c0 100644 --- a/shell/documentcontroller.h +++ b/shell/documentcontroller.h @@ -1,180 +1,180 @@ /* This document is part of the KDE project Copyright 2002 Matthias Hoelzer-Kluepfel Copyright 2002 Bernd Gehrmann Copyright 2003 Roberto Raggi Copyright 2003 Hamish Rodda Copyright 2003 Harald Fernengel Copyright 2003 Jens Dagerbo Copyright 2005 Adam Treat Copyright 2004-2007 Alexander Dymo Copyright 2007 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 document COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KDEVPLATFORM_DOCUMENTCONTROLLER_H #define KDEVPLATFORM_DOCUMENTCONTROLLER_H #include #include #include "shellexport.h" namespace KTextEditor { class View; } namespace Sublime { class Document; class Area; class AreaIndex; } namespace KDevelop { class MainWindow; /** * \short Interface to control open documents. * The document controller manages open documents in the IDE. * Open documents are usually editors, GUI designers, html documentation etc. * * Please note that this interface gives access to documents and not to their views. * It is possible that more than 1 view is shown in KDevelop for a document. */ class KDEVPLATFORMSHELL_EXPORT DocumentController: public IDocumentController { Q_OBJECT Q_CLASSINFO( "D-Bus Interface", "org.kdevelop.DocumentController" ) public: /**Constructor. @param parent The parent object.*/ explicit DocumentController( QObject *parent = nullptr ); ~DocumentController() override; /**Finds the first document object corresponding to a given url. @param url The Url of the document. @return The corresponding document, or null if not found.*/ IDocument* documentForUrl( const QUrl & url ) const override; /**@return The list of open documents*/ QList openDocuments() const override; /**Refers to the document currently active or focused. @return The active document.*/ IDocument* activeDocument() const override; KTextEditor::View* activeTextDocumentView() const override; /// Activate the given \a document. This convenience function does not add the document /// to the File/Recent Open menu. Use DocumentController::openDocument if that is desired. void activateDocument( IDocument * document, const KTextEditor::Range& range = KTextEditor::Range::invalid() ) override; void registerDocumentForMimetype( const QString&, KDevelop::IDocumentFactory* ) override; /// Request the document controller to save all documents. /// If the \a mode is not IDocument::Silent, ask the user which documents to save. /// Returns false if the user cancels the save dialog. bool saveAllDocuments(IDocument::DocumentSaveMode mode) override; bool saveAllDocumentsForWindow(KParts::MainWindow* mw, IDocument::DocumentSaveMode mode, bool currentAreaOnly = false) override; void initialize(); void cleanup(); virtual QStringList documentTypes() const; QString documentType(Sublime::Document* document) const; using IDocumentController::openDocument; /**checks that url is an url of empty document*/ static bool isEmptyDocumentUrl(const QUrl &url); static QUrl nextEmptyDocumentUrl(); IDocumentFactory* factory(const QString& mime) const override; bool openDocument(IDocument* doc, const KTextEditor::Range& range = KTextEditor::Range::invalid(), - DocumentActivationParams activationParams = nullptr, + DocumentActivationParams activationParams = {}, IDocument* buddy = nullptr) override; KTextEditor::Document* globalTextEditorInstance() override; public Q_SLOTS: /**Opens a new or existing document. @param url The full Url of the document to open. If it is empty, a dialog to choose the document will be opened. @param range The location information, if applicable. @param activationParams Indicates whether to fully activate the document. @param buddy The buddy document @return The opened document */ IDocument* openDocument( const QUrl &url, const KTextEditor::Range& range = KTextEditor::Range::invalid(), - DocumentActivationParams activationParams = nullptr, + DocumentActivationParams activationParams = {}, const QString& encoding = {}, IDocument* buddy = nullptr ) override; IDocument* openDocumentFromText( const QString& data ) override; KDevelop::IDocument* openDocument( const QUrl &url, const QString& prefName ) override; virtual bool closeDocument( const QUrl &url ); void fileClose(); void slotSaveAllDocuments(); bool closeAllDocuments() override; void closeAllOtherDocuments(); void reloadAllDocuments(); // DBUS-compatible versions of openDocument virtual Q_SCRIPTABLE bool openDocumentSimple( QString url, int line = -1, int column = 0 ); // Opens a list of documents, with optional split-view separators, like: "file1 / [ file2 - fil3 ]" (see kdevplatform_shell_environment.sh) virtual Q_SCRIPTABLE bool openDocumentsSimple( QStringList urls ); virtual Q_SCRIPTABLE bool openDocumentFromTextSimple( QString text ); // If 'target' is empty, returns the currently active document, or // the currently selected project-item if no document is active. // If 'target' is "[selection]", returns the path of the currently active selection. // If 'target' is the name of a project, returns the root-path of that project. // Whenever the returned path corresponds to a directory, a '/.' suffix is appended. Q_SCRIPTABLE QString activeDocumentPath(QString target = {}) const; // Returns all open documents in the current area Q_SCRIPTABLE QStringList activeDocumentPaths() const; void vcsAnnotateCurrentDocument(); private Q_SLOTS: virtual void slotOpenDocument(const QUrl &url); void notifyDocumentClosed(Sublime::Document* doc); private: bool openDocumentsWithSplitSeparators( Sublime::AreaIndex* index, QStringList urlsWithSeparators, bool& isFirstView ); QList visibleDocumentsInWindow(MainWindow* mw) const; QList documentsExclusivelyInWindow(MainWindow* mw, bool currentAreaOnly = false) const; QList modifiedDocuments(const QList& list) const; bool saveSomeDocuments(const QList& list, IDocument::DocumentSaveMode mode) override; void setupActions(); friend struct DocumentControllerPrivate; struct DocumentControllerPrivate *d; }; } #endif diff --git a/sublime/aggregatemodel.cpp b/sublime/aggregatemodel.cpp index d1ceea0ff1..0c3e993355 100644 --- a/sublime/aggregatemodel.cpp +++ b/sublime/aggregatemodel.cpp @@ -1,216 +1,216 @@ /*************************************************************************** * Copyright 2007 Alexander Dymo * * * * 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 "aggregatemodel.h" #include #include namespace Sublime { struct AggregateModelPrivate { /*Instance of this class is used as an internal pointer to the aggregator items in the model to differentiate between aggregators and non-aggregators.*/ class AggregateInternalData { }; AggregateModelPrivate() { internal = new AggregateInternalData(); } ~AggregateModelPrivate() { delete internal; } QList modelList; QMap modelNames; AggregateInternalData *internal; }; AggregateModel::AggregateModel(QObject *parent) :QAbstractItemModel(parent) { d = new AggregateModelPrivate(); } AggregateModel::~AggregateModel() { delete d; } void AggregateModel::addModel(const QString &name, QStandardItemModel *model) { beginResetModel(); d->modelList << model; d->modelNames[model] = name; endResetModel(); } void AggregateModel::removeModel(QStandardItemModel *model) { beginResetModel(); d->modelList.removeAll(model); d->modelNames.remove(model); endResetModel(); } // reimplemented methods from QAbstractItemModel Qt::ItemFlags AggregateModel::flags(const QModelIndex &index) const { if (!index.isValid()) - return nullptr; + return Qt::NoItemFlags; return Qt::ItemIsEnabled | Qt::ItemIsSelectable; } QVariant AggregateModel::headerData(int section, Qt::Orientation orientation, int role) const { Q_UNUSED(section); Q_UNUSED(orientation); Q_UNUSED(role); //there's nothing to return here because aggregated models will have different headers //so we just use empty headers for aggregate model. return ""; } int AggregateModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); //only 1 column is supported atm return 1; } int AggregateModel::rowCount(const QModelIndex &parent) const { if (!parent.isValid()) { //toplevel items represent aggregated models return d->modelList.count(); } else { //Qt model guideline - only 1st column has children if (parent.column() != 0) return 0; //find out if the parent is an aggregator if (parent.internalPointer() == d->internal) { //return the number of toplevel rows in the source model return d->modelList[parent.row()]->rowCount(QModelIndex()); } else { //we have a standard item in the source model - just map it into our model QStandardItem *item = static_cast(parent.internalPointer()); return item->rowCount(); } } } QVariant AggregateModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || (role != Qt::DisplayRole)) return QVariant(); if (!index.parent().isValid()) { //aggregator item return d->modelNames[d->modelList[index.row()]]; } else { //we have a standard item in the source model - just map it into our model QStandardItem *item = static_cast(index.internalPointer()); return item->data(role); } } QModelIndex AggregateModel::parent(const QModelIndex &index) const { if (!index.isValid()) return QModelIndex(); if (index.internalPointer() == d->internal) { //this is aggregator item, it has no parents return QModelIndex(); } //this is just an item from the model QStandardItem *item = static_cast(index.internalPointer()); QModelIndex parent; if (!item->parent()) { //we need to find the aggregator item that owns this index //first find the model for this index QStandardItemModel *model = item->model(); //next find the row number of the aggregator item int row = d->modelList.indexOf(model); parent = createIndex(row, 0, d->internal); } else { //we have a standard item in the source model - just map it into our model parent = createIndex(item->parent()->row(), 0, item->parent()); } return parent; } QModelIndex AggregateModel::index(int row, int column, const QModelIndex &parent) const { if (row < 0 || column < 0) return QModelIndex(); if (!parent.isValid()) { if (column > 1 || row >= d->modelList.count()) return QModelIndex(); //this is an aggregator item return createIndex(row, column, d->internal); } else if (parent.internalPointer() == d->internal) { //the parent is an aggregator //find the model that holds the items QStandardItemModel *model = d->modelList[parent.row()]; //this is the first level of items QStandardItem *item = model->item(row, column); if (item) return createIndex(row, column, item); else return QModelIndex(); } else { //we have a standard item in the source model - just map it into our model QStandardItem *parentItem = static_cast(parent.internalPointer()); return createIndex(row, column, parentItem->child(row, column)); } } }