From 963ff4381d5a3b2edb27c1d538299e23d15d8fa1 Mon Sep 17 00:00:00 2001 From: hjk Date: Fri, 22 Sep 2023 18:05:03 +0200 Subject: Bookmarks: Merge plugin into TextEditor Change-Id: I4c9438f3596daff2c18680a731764bf5010e1e25 Reviewed-by: David Schulz --- src/plugins/CMakeLists.txt | 1 - src/plugins/bookmarks/Bookmarks.json.in | 19 - src/plugins/bookmarks/CMakeLists.txt | 10 - src/plugins/bookmarks/bookmark.cpp | 99 ---- src/plugins/bookmarks/bookmark.h | 37 -- src/plugins/bookmarks/bookmarkfilter.cpp | 115 ---- src/plugins/bookmarks/bookmarkfilter.h | 24 - src/plugins/bookmarks/bookmarkmanager.cpp | 830 --------------------------- src/plugins/bookmarks/bookmarkmanager.h | 113 ---- src/plugins/bookmarks/bookmarks.qbs | 25 - src/plugins/bookmarks/bookmarks_global.h | 26 - src/plugins/bookmarks/bookmarksplugin.cpp | 223 ------- src/plugins/bookmarks/bookmarksplugin.h | 22 - src/plugins/bookmarks/bookmarkstr.h | 15 - src/plugins/plugins.qbs | 1 - src/plugins/texteditor/CMakeLists.txt | 3 + src/plugins/texteditor/basefilefind.cpp | 2 +- src/plugins/texteditor/bookmark.cpp | 101 ++++ src/plugins/texteditor/bookmark.h | 37 ++ src/plugins/texteditor/bookmarkfilter.cpp | 115 ++++ src/plugins/texteditor/bookmarkfilter.h | 24 + src/plugins/texteditor/bookmarkmanager.cpp | 830 +++++++++++++++++++++++++++ src/plugins/texteditor/bookmarkmanager.h | 113 ++++ src/plugins/texteditor/texteditor.qbs | 6 + src/plugins/texteditor/texteditorconstants.h | 3 + src/plugins/texteditor/texteditorplugin.cpp | 202 ++++++- 26 files changed, 1422 insertions(+), 1574 deletions(-) delete mode 100644 src/plugins/bookmarks/Bookmarks.json.in delete mode 100644 src/plugins/bookmarks/CMakeLists.txt delete mode 100644 src/plugins/bookmarks/bookmark.cpp delete mode 100644 src/plugins/bookmarks/bookmark.h delete mode 100644 src/plugins/bookmarks/bookmarkfilter.cpp delete mode 100644 src/plugins/bookmarks/bookmarkfilter.h delete mode 100644 src/plugins/bookmarks/bookmarkmanager.cpp delete mode 100644 src/plugins/bookmarks/bookmarkmanager.h delete mode 100644 src/plugins/bookmarks/bookmarks.qbs delete mode 100644 src/plugins/bookmarks/bookmarks_global.h delete mode 100644 src/plugins/bookmarks/bookmarksplugin.cpp delete mode 100644 src/plugins/bookmarks/bookmarksplugin.h delete mode 100644 src/plugins/bookmarks/bookmarkstr.h create mode 100644 src/plugins/texteditor/bookmark.cpp create mode 100644 src/plugins/texteditor/bookmark.h create mode 100644 src/plugins/texteditor/bookmarkfilter.cpp create mode 100644 src/plugins/texteditor/bookmarkfilter.h create mode 100644 src/plugins/texteditor/bookmarkmanager.cpp create mode 100644 src/plugins/texteditor/bookmarkmanager.h diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt index 8fd10b68374..d10f434eb10 100644 --- a/src/plugins/CMakeLists.txt +++ b/src/plugins/CMakeLists.txt @@ -22,7 +22,6 @@ add_subdirectory(silversearcher) # Level 3: (only depends on Level 2 and below) add_subdirectory(axivion) -add_subdirectory(bookmarks) add_subdirectory(cppeditor) add_subdirectory(haskell) add_subdirectory(help) diff --git a/src/plugins/bookmarks/Bookmarks.json.in b/src/plugins/bookmarks/Bookmarks.json.in deleted file mode 100644 index 46a307f517a..00000000000 --- a/src/plugins/bookmarks/Bookmarks.json.in +++ /dev/null @@ -1,19 +0,0 @@ -{ - "Name" : "Bookmarks", - "Version" : "${IDE_VERSION}", - "CompatVersion" : "${IDE_VERSION_COMPAT}", - "Vendor" : "The Qt Company Ltd", - "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", - "License" : [ "Commercial Usage", - "", - "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt Commercial License Agreement provided with the Software or, alternatively, in accordance with the terms contained in a written agreement between you and The Qt Company.", - "", - "GNU General Public License Usage", - "", - "Alternatively, this plugin may be used under the terms of the GNU General Public License version 3 as published by the Free Software Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT included in the packaging of this plugin. Please review the following information to ensure the GNU General Public License requirements will be met: https://www.gnu.org/licenses/gpl-3.0.html." - ], - "Category" : "Core", - "Description" : "Bookmarks in text editors.", - "Url" : "http://www.qt.io", - ${IDE_PLUGIN_DEPENDENCIES} -} diff --git a/src/plugins/bookmarks/CMakeLists.txt b/src/plugins/bookmarks/CMakeLists.txt deleted file mode 100644 index 62a83b5adb1..00000000000 --- a/src/plugins/bookmarks/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -add_qtc_plugin(Bookmarks - PLUGIN_DEPENDS Core ProjectExplorer TextEditor - SOURCES - bookmark.cpp bookmark.h - bookmarkfilter.cpp bookmarkfilter.h - bookmarkmanager.cpp bookmarkmanager.h - bookmarks_global.h - bookmarkstr.h - bookmarksplugin.cpp bookmarksplugin.h -) diff --git a/src/plugins/bookmarks/bookmark.cpp b/src/plugins/bookmarks/bookmark.cpp deleted file mode 100644 index 27c8365e19e..00000000000 --- a/src/plugins/bookmarks/bookmark.cpp +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "bookmark.h" - -#include "bookmarkmanager.h" -#include "bookmarks_global.h" -#include "bookmarkstr.h" - -#include - -#include - -using namespace Utils; - -namespace Bookmarks::Internal { - -Bookmark::Bookmark(int lineNumber, BookmarkManager *manager) : - TextMark(FilePath(), lineNumber, {Tr::tr("Bookmark"), Constants::BOOKMARKS_TEXT_MARK_CATEGORY}), - m_manager(manager) -{ - setColor(Theme::Bookmarks_TextMarkColor); - setIcon(Icons::BOOKMARK_TEXTEDITOR.icon()); - setDefaultToolTip(Tr::tr("Bookmark")); - setPriority(TextEditor::TextMark::NormalPriority); -} - -void Bookmark::removedFromEditor() -{ - m_manager->deleteBookmark(this); -} - -bool Bookmark::isDraggable() const -{ - return true; -} - -void Bookmark::dragToLine(int lineNumber) -{ - move(lineNumber); -} - -void Bookmark::updateLineNumber(int line) -{ - if (line != lineNumber()) { - TextMark::updateLineNumber(line); - m_manager->updateBookmark(this); - } -} - -void Bookmark::move(int line) -{ - if (line != lineNumber()) { - TextMark::move(line); - m_manager->updateBookmark(this); - updateMarker(); - } -} - -void Bookmark::updateBlock(const QTextBlock &block) -{ - const QString &lineText = block.text().trimmed(); - if (m_lineText != lineText) { - m_lineText = lineText; - m_manager->updateBookmark(this); - } -} - -void Bookmark::updateFilePath(const FilePath &filePath) -{ - const FilePath oldFilePath = this->filePath(); - TextMark::updateFilePath(filePath); - m_manager->updateBookmarkFileName(this, oldFilePath); -} - -void Bookmark::setNote(const QString ¬e) -{ - setToolTip(note); - setLineAnnotation(note); - updateMarker(); -} - -void Bookmark::updateNote(const QString ¬e) -{ - setNote(note); - m_manager->updateBookmark(this); -} - -QString Bookmark::lineText() const -{ - return m_lineText; -} - -QString Bookmark::note() const -{ - return toolTip(); -} - -} // Bookmarks::Internal diff --git a/src/plugins/bookmarks/bookmark.h b/src/plugins/bookmarks/bookmark.h deleted file mode 100644 index 44277d19c60..00000000000 --- a/src/plugins/bookmarks/bookmark.h +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include - -namespace Bookmarks::Internal { - -class BookmarkManager; - -class Bookmark : public TextEditor::TextMark -{ -public: - Bookmark(int lineNumber, BookmarkManager *manager); - - void updateLineNumber(int lineNumber) override; - void move(int line) override; - void updateBlock(const QTextBlock &block) override; - void updateFilePath(const Utils::FilePath &filePath) override; - void removedFromEditor() override; - - bool isDraggable() const override; - void dragToLine(int lineNumber) override; - - void setNote(const QString ¬e); - void updateNote(const QString ¬e); - - QString lineText() const; - QString note() const; - -private: - BookmarkManager *m_manager; - QString m_lineText; -}; - -} // Bookmarks::Internal diff --git a/src/plugins/bookmarks/bookmarkfilter.cpp b/src/plugins/bookmarks/bookmarkfilter.cpp deleted file mode 100644 index 1cedbade481..00000000000 --- a/src/plugins/bookmarks/bookmarkfilter.cpp +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "bookmarkfilter.h" - -#include "bookmark.h" -#include "bookmarkmanager.h" -#include "bookmarkstr.h" - -#include - -using namespace Core; -using namespace Utils; - -namespace Bookmarks::Internal { - -BookmarkFilter::BookmarkFilter(BookmarkManager *manager) - : m_manager(manager) -{ - setId("Bookmarks"); - setDisplayName(Tr::tr("Bookmarks")); - setDescription(Tr::tr("Locates bookmarks. Filter by file name, by the text on the line of the " - "bookmark, or by the bookmark's note text.")); - setPriority(Medium); - setDefaultShortcutString("b"); -} - -LocatorMatcherTasks BookmarkFilter::matchers() -{ - using namespace Tasking; - - TreeStorage storage; - - const auto onSetup = [=] { storage->reportOutput(match(storage->input())); }; - return {{Sync(onSetup), storage}}; -} - -LocatorFilterEntries BookmarkFilter::match(const QString &input) const -{ - if (m_manager->rowCount() == 0) - return {}; - const auto match = [this](const QString &name, BookmarkManager::Roles role) { - return m_manager->match(m_manager->index(0, 0), role, name, -1, - Qt::MatchContains | Qt::MatchWrap); - }; - - const int colonIndex = input.lastIndexOf(':'); - QModelIndexList fileNameLineNumberMatches; - if (colonIndex >= 0) { - // Filter by "fileName:lineNumber" pattern - const QString fileName = input.left(colonIndex); - const QString lineNumber = input.mid(colonIndex + 1); - fileNameLineNumberMatches = match(fileName, BookmarkManager::Filename); - fileNameLineNumberMatches = Utils::filtered( - fileNameLineNumberMatches, [lineNumber](const QModelIndex &index) { - return index.data(BookmarkManager::LineNumber).toString().contains(lineNumber); - }); - } - const QModelIndexList matches = filteredUnique(fileNameLineNumberMatches - + match(input, BookmarkManager::Filename) - + match(input, BookmarkManager::LineNumber) - + match(input, BookmarkManager::Note) - + match(input, BookmarkManager::LineText)); - LocatorFilterEntries entries; - for (const QModelIndex &idx : matches) { - const Bookmark *bookmark = m_manager->bookmarkForIndex(idx); - const QString filename = bookmark->filePath().fileName(); - LocatorFilterEntry entry; - entry.displayName = QString("%1:%2").arg(filename).arg(bookmark->lineNumber()); - // TODO: according to QModelIndex docs, we shouldn't store model indexes: - // Model indexes should be used immediately and then discarded. - // You should not rely on indexes to remain valid after calling model functions - // that change the structure of the model or delete items. - entry.acceptor = [manager = m_manager, idx] { - if (Bookmark *bookmark = manager->bookmarkForIndex(idx)) - manager->gotoBookmark(bookmark); - return AcceptResult(); - }; - if (!bookmark->note().isEmpty()) - entry.extraInfo = bookmark->note(); - else if (!bookmark->lineText().isEmpty()) - entry.extraInfo = bookmark->lineText(); - else - entry.extraInfo = bookmark->filePath().toString(); - int highlightIndex = entry.displayName.indexOf(input, 0, Qt::CaseInsensitive); - if (highlightIndex >= 0) { - entry.highlightInfo = {highlightIndex, int(input.length())}; - } else { - highlightIndex = entry.extraInfo.indexOf(input, 0, Qt::CaseInsensitive); - if (highlightIndex >= 0) { - entry.highlightInfo = {highlightIndex, int(input.length()), - LocatorFilterEntry::HighlightInfo::ExtraInfo}; - } else if (colonIndex >= 0) { - const QString fileName = input.left(colonIndex); - const QString lineNumber = input.mid(colonIndex + 1); - highlightIndex = entry.displayName.indexOf(fileName, 0, - Qt::CaseInsensitive); - if (highlightIndex >= 0) { - entry.highlightInfo = {highlightIndex, int(fileName.length())}; - highlightIndex = entry.displayName.indexOf( - lineNumber, highlightIndex, Qt::CaseInsensitive); - if (highlightIndex >= 0) { - entry.highlightInfo.startsDisplay += highlightIndex; - entry.highlightInfo.lengthsDisplay += lineNumber.length(); - } - } - } - } - entry.displayIcon = bookmark->icon(); - entries.append(entry); - } - return entries; -} - -} // Bookmarks::Internal diff --git a/src/plugins/bookmarks/bookmarkfilter.h b/src/plugins/bookmarks/bookmarkfilter.h deleted file mode 100644 index f85eff9b7d7..00000000000 --- a/src/plugins/bookmarks/bookmarkfilter.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include - -namespace Bookmarks::Internal { - -class BookmarkManager; - -class BookmarkFilter : public Core::ILocatorFilter -{ -public: - BookmarkFilter(BookmarkManager *manager); - -private: - Core::LocatorMatcherTasks matchers() final; - Core::LocatorFilterEntries match(const QString &input) const; - - BookmarkManager *m_manager = nullptr; // not owned -}; - -} // Bookmarks::Internal diff --git a/src/plugins/bookmarks/bookmarkmanager.cpp b/src/plugins/bookmarks/bookmarkmanager.cpp deleted file mode 100644 index 18a4206afd3..00000000000 --- a/src/plugins/bookmarks/bookmarkmanager.cpp +++ /dev/null @@ -1,830 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "bookmarkmanager.h" - -#include "bookmark.h" -#include "bookmarks_global.h" -#include "bookmarkstr.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 - -Q_DECLARE_METATYPE(Bookmarks::Internal::Bookmark*) - -using namespace Core; -using namespace Utils; - -namespace Bookmarks::Internal { - -class BookmarkDelegate : public QStyledItemDelegate -{ -public: - BookmarkDelegate(QObject *parent) - : QStyledItemDelegate(parent) - {} - -private: - void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const final; - QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const final; - void generateGradientPixmap(int width, int height, const QColor &color, bool selected) const; - - mutable QPixmap m_normalPixmap; - mutable QPixmap m_selectedPixmap; -}; - -QSize BookmarkDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const -{ - QStyleOptionViewItem opt = option; - initStyleOption(&opt, index); - - QFontMetrics fm(option.font); - QSize s; - s.setWidth(option.rect.width()); - s.setHeight(fm.height() * 2 + 10); - return s; -} - -void BookmarkDelegate::generateGradientPixmap(int width, int height, const QColor &color, bool selected) const -{ - QColor c = color; - c.setAlpha(0); - - QPixmap pixmap(width+1, height); - pixmap.fill(c); - - QPainter painter(&pixmap); - painter.setPen(Qt::NoPen); - - QLinearGradient lg; - lg.setCoordinateMode(QGradient::ObjectBoundingMode); - lg.setFinalStop(1,0); - - lg.setColorAt(0, c); - lg.setColorAt(0.4, color); - - painter.setBrush(lg); - painter.drawRect(0, 0, width+1, height); - - if (selected) - m_selectedPixmap = pixmap; - else - m_normalPixmap = pixmap; -} - -void BookmarkDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const -{ - QStyleOptionViewItem opt = option; - initStyleOption(&opt, index); - painter->save(); - - QFontMetrics fm(opt.font); - static int lwidth = fm.horizontalAdvance(QLatin1String("8888")) + 18; - - QColor backgroundColor; - QColor textColor; - - bool selected = opt.state & QStyle::State_Selected; - - if (selected) { - painter->setBrush(opt.palette.highlight().color()); - backgroundColor = opt.palette.highlight().color(); - if (!m_selectedPixmap) - generateGradientPixmap(lwidth, fm.height()+1, backgroundColor, selected); - } else { - backgroundColor = opt.palette.window().color(); - painter->setBrush(backgroundColor); - if (!m_normalPixmap) - generateGradientPixmap(lwidth, fm.height(), backgroundColor, selected); - } - painter->setPen(Qt::NoPen); - painter->drawRect(opt.rect); - - // Set Text Color - if (opt.state & QStyle::State_Selected) - textColor = opt.palette.highlightedText().color(); - else - textColor = opt.palette.text().color(); - - painter->setPen(textColor); - - - // TopLeft - QString topLeft = index.data(BookmarkManager::Filename).toString(); - painter->drawText(6, 2 + opt.rect.top() + fm.ascent(), topLeft); - - QString topRight = index.data(BookmarkManager::LineNumber).toString(); - // Check whether we need to be fancy and paint some background - int fwidth = fm.horizontalAdvance(topLeft); - if (fwidth + lwidth > opt.rect.width()) { - int left = opt.rect.right() - lwidth; - painter->drawPixmap(left, opt.rect.top(), selected ? m_selectedPixmap : m_normalPixmap); - } - // topRight - painter->drawText(opt.rect.right() - fm.horizontalAdvance(topRight) - 6, - 2 + opt.rect.top() + fm.ascent(), topRight); - - // Directory - QColor mix; - mix.setRgbF(0.7 * textColor.redF() + 0.3 * backgroundColor.redF(), - 0.7 * textColor.greenF() + 0.3 * backgroundColor.greenF(), - 0.7 * textColor.blueF() + 0.3 * backgroundColor.blueF()); - painter->setPen(mix); - - QString lineText = index.data(BookmarkManager::Note).toString().trimmed(); - if (lineText.isEmpty()) - lineText = index.data(BookmarkManager::LineText).toString(); - - painter->drawText(6, opt.rect.top() + fm.ascent() + fm.height() + 6, lineText); - - // Separator lines - const QRectF innerRect = QRectF(opt.rect).adjusted(0.5, 0.5, -0.5, -0.5); - painter->setPen(QColor::fromRgb(150,150,150)); - painter->drawLine(innerRect.bottomLeft(), innerRect.bottomRight()); - painter->restore(); -} - -// BookmarkView - -class BookmarkView final : public Utils::ListView -{ -public: - explicit BookmarkView(BookmarkManager *manager); - - QList createToolBarWidgets(); - - void gotoBookmark(const QModelIndex &index); - - void removeFromContextMenu(); - void removeAll(); - -protected: - void contextMenuEvent(QContextMenuEvent *event) final; - void removeBookmark(const QModelIndex &index); - void keyPressEvent(QKeyEvent *event) final; - -private: - Core::IContext *m_bookmarkContext; - QModelIndex m_contextMenuIndex; - BookmarkManager *m_manager; -}; - -BookmarkView::BookmarkView(BookmarkManager *manager) : - m_bookmarkContext(new IContext(this)), - m_manager(manager) -{ - setWindowTitle(Tr::tr("Bookmarks")); - - m_bookmarkContext->setWidget(this); - m_bookmarkContext->setContext(Context(Constants::BOOKMARKS_CONTEXT)); - - ICore::addContextObject(m_bookmarkContext); - - ListView::setModel(manager); - - setItemDelegate(new BookmarkDelegate(this)); - setFrameStyle(QFrame::NoFrame); - setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - setAttribute(Qt::WA_MacShowFocusRect, false); - setSelectionModel(manager->selectionModel()); - setSelectionMode(QAbstractItemView::SingleSelection); - setSelectionBehavior(QAbstractItemView::SelectRows); - setDragEnabled(true); - setDragDropMode(QAbstractItemView::DragOnly); - - connect(this, &QAbstractItemView::clicked, this, &BookmarkView::gotoBookmark); - connect(this, &QAbstractItemView::activated, this, &BookmarkView::gotoBookmark); -} - -QList BookmarkView::createToolBarWidgets() -{ - Command *prevCmd = ActionManager::command(Constants::BOOKMARKS_PREV_ACTION); - Command *nextCmd = ActionManager::command(Constants::BOOKMARKS_NEXT_ACTION); - QTC_ASSERT(prevCmd && nextCmd, return {}); - auto prevButton = new QToolButton(this); - prevButton->setToolButtonStyle(Qt::ToolButtonIconOnly); - prevButton->setDefaultAction(prevCmd->action()); - auto nextButton = new QToolButton(this); - nextButton->setToolButtonStyle(Qt::ToolButtonIconOnly); - nextButton->setDefaultAction(nextCmd->action()); - return {prevButton, nextButton}; -} - -void BookmarkView::contextMenuEvent(QContextMenuEvent *event) -{ - QMenu menu; - QAction *moveUp = menu.addAction(Tr::tr("Move Up")); - QAction *moveDown = menu.addAction(Tr::tr("Move Down")); - QAction *edit = menu.addAction(Tr::tr("&Edit")); - menu.addSeparator(); - QAction *remove = menu.addAction(Tr::tr("&Remove")); - menu.addSeparator(); - QAction *removeAll = menu.addAction(Tr::tr("Remove All")); - - m_contextMenuIndex = indexAt(event->pos()); - if (!m_contextMenuIndex.isValid()) { - moveUp->setEnabled(false); - moveDown->setEnabled(false); - remove->setEnabled(false); - edit->setEnabled(false); - } - - if (model()->rowCount() == 0) - removeAll->setEnabled(false); - - connect(moveUp, &QAction::triggered, m_manager, &BookmarkManager::moveUp); - connect(moveDown, &QAction::triggered, m_manager, &BookmarkManager::moveDown); - connect(remove, &QAction::triggered, this, &BookmarkView::removeFromContextMenu); - connect(removeAll, &QAction::triggered, this, &BookmarkView::removeAll); - connect(edit, &QAction::triggered, m_manager, &BookmarkManager::edit); - - menu.exec(mapToGlobal(event->pos())); -} - -void BookmarkView::removeFromContextMenu() -{ - removeBookmark(m_contextMenuIndex); -} - -void BookmarkView::removeBookmark(const QModelIndex& index) -{ - Bookmark *bm = m_manager->bookmarkForIndex(index); - m_manager->deleteBookmark(bm); -} - -void BookmarkView::keyPressEvent(QKeyEvent *event) -{ - if (event->key() == Qt::Key_Delete || event->key() == Qt::Key_Backspace) { - removeBookmark(currentIndex()); - event->accept(); - return; - } - ListView::keyPressEvent(event); -} - -void BookmarkView::removeAll() -{ - if (CheckableMessageBox::question(this, - Tr::tr("Remove All Bookmarks"), - Tr::tr("Are you sure you want to remove all bookmarks from " - "all files in the current session?"), - QString("RemoveAllBookmarks")) - != QMessageBox::Yes) - return; - - // The performance of this function could be greatly improved. - while (m_manager->rowCount()) { - QModelIndex index = m_manager->index(0, 0); - removeBookmark(index); - } -} - -void BookmarkView::gotoBookmark(const QModelIndex &index) -{ - Bookmark *bk = m_manager->bookmarkForIndex(index); - if (!m_manager->gotoBookmark(bk)) - m_manager->deleteBookmark(bk); -} - -//// -// BookmarkManager -//// - -BookmarkManager::BookmarkManager() : - m_selectionModel(new QItemSelectionModel(this, this)) -{ - connect(ICore::instance(), &ICore::contextChanged, - this, &BookmarkManager::updateActionStatus); - - connect(SessionManager::instance(), &SessionManager::sessionLoaded, - this, &BookmarkManager::loadBookmarks); - - updateActionStatus(); -} - -BookmarkManager::~BookmarkManager() -{ - qDeleteAll(m_bookmarksList); -} - -QItemSelectionModel *BookmarkManager::selectionModel() const -{ - return m_selectionModel; -} - -bool BookmarkManager::hasBookmarkInPosition(const Utils::FilePath &fileName, int lineNumber) -{ - return findBookmark(fileName, lineNumber); -} - -QModelIndex BookmarkManager::index(int row, int column, const QModelIndex &parent) const -{ - if (parent.isValid()) - return QModelIndex(); - else - return createIndex(row, column); -} - -QModelIndex BookmarkManager::parent(const QModelIndex &) const -{ - return QModelIndex(); -} - -int BookmarkManager::rowCount(const QModelIndex &parent) const -{ - if (parent.isValid()) - return 0; - else - return m_bookmarksList.count(); -} - -int BookmarkManager::columnCount(const QModelIndex &parent) const -{ - if (parent.isValid()) - return 0; - return 3; -} - -QVariant BookmarkManager::data(const QModelIndex &index, int role) const -{ - if (!index.isValid() || index.column() !=0 || index.row() < 0 || index.row() >= m_bookmarksList.count()) - return QVariant(); - - Bookmark *bookMark = m_bookmarksList.at(index.row()); - if (role == BookmarkManager::Filename) - return bookMark->filePath().fileName(); - if (role == BookmarkManager::LineNumber) - return bookMark->lineNumber(); - if (role == BookmarkManager::Directory) - return bookMark->filePath().toFileInfo().path(); - if (role == BookmarkManager::LineText) - return bookMark->lineText(); - if (role == BookmarkManager::Note) - return bookMark->note(); - if (role == Qt::ToolTipRole) - return bookMark->filePath().toUserOutput(); - return QVariant(); -} - -Qt::ItemFlags BookmarkManager::flags(const QModelIndex &index) const -{ - if (!index.isValid() || index.column() !=0 || index.row() < 0 || index.row() >= m_bookmarksList.count()) - return Qt::NoItemFlags; - return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled; -} - -Qt::DropActions BookmarkManager::supportedDragActions() const -{ - return Qt::MoveAction; -} - -QStringList BookmarkManager::mimeTypes() const -{ - return DropSupport::mimeTypesForFilePaths(); -} - -QMimeData *BookmarkManager::mimeData(const QModelIndexList &indexes) const -{ - auto data = new DropMimeData; - for (const QModelIndex &index : indexes) { - if (!index.isValid() || index.column() != 0 || index.row() < 0 || index.row() >= m_bookmarksList.count()) - continue; - Bookmark *bookMark = m_bookmarksList.at(index.row()); - data->addFile(bookMark->filePath(), bookMark->lineNumber()); - } - return data; -} - -void BookmarkManager::toggleBookmark(const FilePath &fileName, int lineNumber) -{ - if (lineNumber <= 0 || fileName.isEmpty()) - return; - - // Remove any existing bookmark on this line - if (Bookmark *mark = findBookmark(fileName, lineNumber)) { - // TODO check if the bookmark is really on the same markable Interface - deleteBookmark(mark); - return; - } - - // Add a new bookmark if no bookmark existed on this line - auto mark = new Bookmark(lineNumber, this); - mark->updateFilePath(fileName); - const QModelIndex currentIndex = selectionModel()->currentIndex(); - const int insertionIndex = currentIndex.isValid() ? currentIndex.row() + 1 - : m_bookmarksList.size(); - insertBookmark(insertionIndex, mark); -} - -void BookmarkManager::updateBookmark(Bookmark *bookmark) -{ - const int idx = m_bookmarksList.indexOf(bookmark); - if (idx == -1) - return; - - emit dataChanged(index(idx, 0, QModelIndex()), index(idx, 2, QModelIndex())); - saveBookmarks(); -} - -void BookmarkManager::updateBookmarkFileName(Bookmark *bookmark, const FilePath &oldFilePath) -{ - if (oldFilePath == bookmark->filePath()) - return; - - m_bookmarksMap[oldFilePath].removeAll(bookmark); - m_bookmarksMap[bookmark->filePath()].append(bookmark); - updateBookmark(bookmark); -} - -void BookmarkManager::removeAllBookmarks() -{ - if (m_bookmarksList.isEmpty()) - return; - beginRemoveRows(QModelIndex(), 0, m_bookmarksList.size() - 1); - - qDeleteAll(m_bookmarksList); - m_bookmarksMap.clear(); - m_bookmarksList.clear(); - endRemoveRows(); -} - -void BookmarkManager::deleteBookmark(Bookmark *bookmark) -{ - int idx = m_bookmarksList.indexOf(bookmark); - beginRemoveRows(QModelIndex(), idx, idx); - - m_bookmarksMap[bookmark->filePath()].removeAll(bookmark); - delete bookmark; - - m_bookmarksList.removeAt(idx); - endRemoveRows(); - - if (selectionModel()->currentIndex().isValid()) - selectionModel()->setCurrentIndex(selectionModel()->currentIndex(), QItemSelectionModel::Select | QItemSelectionModel::Clear); - - updateActionStatus(); - saveBookmarks(); -} - -Bookmark *BookmarkManager::bookmarkForIndex(const QModelIndex &index) const -{ - if (!index.isValid() || index.row() >= m_bookmarksList.size()) - return nullptr; - return m_bookmarksList.at(index.row()); -} - -bool BookmarkManager::gotoBookmark(const Bookmark *bookmark) const -{ - if (IEditor *editor = EditorManager::openEditorAt( - Utils::Link(bookmark->filePath(), bookmark->lineNumber()))) { - return editor->currentLine() == bookmark->lineNumber(); - } - return false; -} - -void BookmarkManager::nextInDocument() -{ - documentPrevNext(true); -} - -void BookmarkManager::prevInDocument() -{ - documentPrevNext(false); -} - -void BookmarkManager::documentPrevNext(bool next) -{ - IEditor *editor = EditorManager::currentEditor(); - const int editorLine = editor->currentLine(); - if (editorLine <= 0) - return; - - const FilePath filePath = editor->document()->filePath(); - if (!m_bookmarksMap.contains(filePath)) - return; - - int firstLine = -1; - int lastLine = -1; - int prevLine = -1; - int nextLine = -1; - const QVector marks = m_bookmarksMap[filePath]; - for (int i = 0; i < marks.count(); ++i) { - int markLine = marks.at(i)->lineNumber(); - if (firstLine == -1 || firstLine > markLine) - firstLine = markLine; - if (lastLine < markLine) - lastLine = markLine; - if (markLine < editorLine && prevLine < markLine) - prevLine = markLine; - if (markLine > editorLine && - (nextLine == -1 || nextLine > markLine)) - nextLine = markLine; - } - - EditorManager::addCurrentPositionToNavigationHistory(); - if (next) { - if (nextLine == -1) - editor->gotoLine(firstLine); - else - editor->gotoLine(nextLine); - } else { - if (prevLine == -1) - editor->gotoLine(lastLine); - else - editor->gotoLine(prevLine); - } -} - -void BookmarkManager::next() -{ - QModelIndex current = selectionModel()->currentIndex(); - if (!current.isValid()) - return; - int row = current.row(); - ++row; - while (true) { - if (row == m_bookmarksList.size()) - row = 0; - - Bookmark *bk = m_bookmarksList.at(row); - if (gotoBookmark(bk)) { - QModelIndex newIndex = current.sibling(row, current.column()); - selectionModel()->setCurrentIndex(newIndex, QItemSelectionModel::Select | QItemSelectionModel::Clear); - return; - } - deleteBookmark(bk); - if (m_bookmarksList.isEmpty()) // No bookmarks anymore ... - return; - } -} - -void BookmarkManager::prev() -{ - QModelIndex current = selectionModel()->currentIndex(); - if (!current.isValid()) - return; - if (!isAtCurrentBookmark() && gotoBookmark(bookmarkForIndex(current))) - return; - int row = current.row(); - while (true) { - if (row == 0) - row = m_bookmarksList.size(); - --row; - Bookmark *bk = m_bookmarksList.at(row); - if (gotoBookmark(bk)) { - QModelIndex newIndex = current.sibling(row, current.column()); - selectionModel()->setCurrentIndex(newIndex, QItemSelectionModel::Select | QItemSelectionModel::Clear); - return; - } - deleteBookmark(bk); - if (m_bookmarksList.isEmpty()) - return; - } -} - -BookmarkManager::State BookmarkManager::state() const -{ - if (m_bookmarksList.empty()) - return NoBookMarks; - - IEditor *editor = EditorManager::currentEditor(); - if (!editor) - return HasBookMarks; - - return m_bookmarksMap.value(editor->document()->filePath()).isEmpty() ? HasBookMarks - : HasBookmarksInDocument; -} - -void BookmarkManager::updateActionStatus() -{ - IEditor *editor = EditorManager::currentEditor(); - const bool enableToggle = editor && !editor->document()->isTemporary(); - - emit updateActions(enableToggle, state()); -} - -void BookmarkManager::moveUp() -{ - QModelIndex current = selectionModel()->currentIndex(); - int row = current.row(); - if (row == 0) - row = m_bookmarksList.size(); - --row; - - // swap current.row() and row - - Bookmark *b = m_bookmarksList.at(row); - m_bookmarksList[row] = m_bookmarksList.at(current.row()); - m_bookmarksList[current.row()] = b; - - QModelIndex topLeft = current.sibling(row, 0); - QModelIndex bottomRight = current.sibling(current.row(), 2); - emit dataChanged(topLeft, bottomRight); - selectionModel()->setCurrentIndex(current.sibling(row, 0), QItemSelectionModel::Select | QItemSelectionModel::Clear); - - saveBookmarks(); -} - -void BookmarkManager::moveDown() -{ - QModelIndex current = selectionModel()->currentIndex(); - int row = current.row(); - ++row; - if (row == m_bookmarksList.size()) - row = 0; - - // swap current.row() and row - Bookmark *b = m_bookmarksList.at(row); - m_bookmarksList[row] = m_bookmarksList.at(current.row()); - m_bookmarksList[current.row()] = b; - - QModelIndex topLeft = current.sibling(current.row(), 0); - QModelIndex bottomRight = current.sibling(row, 2); - emit dataChanged(topLeft, bottomRight); - selectionModel()->setCurrentIndex(current.sibling(row, 0), QItemSelectionModel::Select | QItemSelectionModel::Clear); - - saveBookmarks(); -} - -void BookmarkManager::editByFileAndLine(const FilePath &fileName, int lineNumber) -{ - Bookmark *b = findBookmark(fileName, lineNumber); - QModelIndex current = selectionModel()->currentIndex(); - selectionModel()->setCurrentIndex(current.sibling(m_bookmarksList.indexOf(b), 0), - QItemSelectionModel::Select | QItemSelectionModel::Clear); - - edit(); -} - -void BookmarkManager::edit() -{ - QModelIndex current = selectionModel()->currentIndex(); - Bookmark *b = m_bookmarksList.at(current.row()); - - QDialog dlg; - dlg.setWindowTitle(Tr::tr("Edit Bookmark")); - auto layout = new QFormLayout(&dlg); - auto noteEdit = new QLineEdit(b->note()); - noteEdit->setMinimumWidth(300); - auto lineNumberSpinbox = new QSpinBox; - lineNumberSpinbox->setRange(1, INT_MAX); - lineNumberSpinbox->setValue(b->lineNumber()); - lineNumberSpinbox->setMaximumWidth(100); - auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); - connect(buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept); - connect(buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject); - layout->addRow(Tr::tr("Note text:"), noteEdit); - layout->addRow(Tr::tr("Line number:"), lineNumberSpinbox); - layout->addWidget(buttonBox); - if (dlg.exec() == QDialog::Accepted) { - b->move(lineNumberSpinbox->value()); - b->updateNote(noteEdit->text().replace(QLatin1Char('\t'), QLatin1Char(' '))); - emit dataChanged(current, current); - saveBookmarks(); - } -} - -/* Returns the bookmark at the given file and line number, or 0 if no such bookmark exists. */ -Bookmark *BookmarkManager::findBookmark(const FilePath &filePath, int lineNumber) -{ - return Utils::findOrDefault(m_bookmarksMap.value(filePath), - Utils::equal(&Bookmark::lineNumber, lineNumber)); -} - -void BookmarkManager::insertBookmark(int idx, Bookmark *bookmark, bool userset) -{ - idx = qBound(0, idx, m_bookmarksList.size()); - beginInsertRows(QModelIndex(), idx, idx); - - m_bookmarksMap[bookmark->filePath()].append(bookmark); - m_bookmarksList.insert(idx, bookmark); - - endInsertRows(); - if (userset) { - updateActionStatus(); - saveBookmarks(); - } - selectionModel()->setCurrentIndex(index(idx, 0, QModelIndex()), - QItemSelectionModel::Select | QItemSelectionModel::Clear); -} - -/* Adds a bookmark to the internal data structures. The 'userset' parameter - * determines whether action status should be updated and whether the bookmarks - * should be saved to the session settings. - */ -void BookmarkManager::addBookmark(Bookmark *bookmark, bool userset) -{ - insertBookmark(m_bookmarksList.size(), bookmark, userset); -} - -/* Adds a new bookmark based on information parsed from the string. */ -void BookmarkManager::addBookmark(const QString &s) -{ - // index3 is a frontier beetween note text and other bookmarks data - int index3 = s.lastIndexOf(QLatin1Char('\t')); - if (index3 < 0) - index3 = s.size(); - int index2 = s.lastIndexOf(QLatin1Char(':'), index3 - 1); - int index1 = s.indexOf(QLatin1Char(':')); - - if (index3 != -1 || index2 != -1 || index1 != -1) { - const QString &filePath = s.mid(index1+1, index2-index1-1); - const QString ¬e = s.mid(index3 + 1); - const int lineNumber = s.mid(index2 + 1, index3 - index2 - 1).toInt(); - if (!filePath.isEmpty() && !findBookmark(FilePath::fromString(filePath), lineNumber)) { - auto b = new Bookmark(lineNumber, this); - b->updateFilePath(FilePath::fromString(filePath)); - b->setNote(note); - addBookmark(b, false); - } - } else { - qDebug() << "BookmarkManager::addBookmark() Invalid bookmark string:" << s; - } -} - -/* Puts the bookmark in a string for storing it in the settings. */ -QString BookmarkManager::bookmarkToString(const Bookmark *b) -{ - const QLatin1Char colon(':'); - // Using \t as delimiter because any another symbol can be a part of note. - const QLatin1Char noteDelimiter('\t'); - return colon + b->filePath().toString() + - colon + QString::number(b->lineNumber()) + - noteDelimiter + b->note(); -} - -/* Saves the bookmarks to the session settings. */ -void BookmarkManager::saveBookmarks() -{ - QStringList list; - for (const Bookmark *bookmark : std::as_const(m_bookmarksList)) - list << bookmarkToString(bookmark); - - SessionManager::setValue("Bookmarks", list); -} - -/* Loads the bookmarks from the session settings. */ -void BookmarkManager::loadBookmarks() -{ - removeAllBookmarks(); - const QStringList &list = SessionManager::value("Bookmarks").toStringList(); - for (const QString &bookmarkString : list) - addBookmark(bookmarkString); - - updateActionStatus(); -} - -bool BookmarkManager::isAtCurrentBookmark() const -{ - Bookmark *bk = bookmarkForIndex(selectionModel()->currentIndex()); - if (!bk) - return true; - IEditor *currentEditor = EditorManager::currentEditor(); - return currentEditor - && currentEditor->document()->filePath() == bk->filePath() - && currentEditor->currentLine() == bk->lineNumber(); -} - -// BookmarkViewFactory - -BookmarkViewFactory::BookmarkViewFactory(BookmarkManager *bm) - : m_manager(bm) -{ - setDisplayName(Tr::tr("Bookmarks")); - setPriority(300); - setId("Bookmarks"); - setActivationSequence(QKeySequence(useMacShortcuts ? Tr::tr("Alt+Meta+M") : Tr::tr("Alt+M"))); -} - -NavigationView BookmarkViewFactory::createWidget() -{ - auto view = new BookmarkView(m_manager); - return {view, view->createToolBarWidgets()}; -} - -} // Bookmarks::Internal diff --git a/src/plugins/bookmarks/bookmarkmanager.h b/src/plugins/bookmarks/bookmarkmanager.h deleted file mode 100644 index 0455d316375..00000000000 --- a/src/plugins/bookmarks/bookmarkmanager.h +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -namespace Core { class IContext; } - -namespace Bookmarks::Internal { - -class Bookmark; -class BookmarksPlugin; -class BookmarkContext; - -class BookmarkManager final : public QAbstractItemModel -{ - Q_OBJECT - -public: - BookmarkManager(); - ~BookmarkManager() final; - - void updateBookmark(Bookmark *bookmark); - void updateBookmarkFileName(Bookmark *bookmark, const Utils::FilePath &oldFilePath); - void deleteBookmark(Bookmark *bookmark); // Does not remove the mark - void removeAllBookmarks(); - Bookmark *bookmarkForIndex(const QModelIndex &index) const; - - enum State { NoBookMarks, HasBookMarks, HasBookmarksInDocument }; - State state() const; - - // Model stuff - QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const final; - QModelIndex parent(const QModelIndex &child) const final; - int rowCount(const QModelIndex &parent = QModelIndex()) const final; - int columnCount(const QModelIndex &parent = QModelIndex()) const final; - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const final; - Qt::ItemFlags flags(const QModelIndex &index) const final; - - Qt::DropActions supportedDragActions() const final; - QStringList mimeTypes() const final; - QMimeData *mimeData(const QModelIndexList &indexes) const final; - - // this QItemSelectionModel is shared by all views - QItemSelectionModel *selectionModel() const; - - bool hasBookmarkInPosition(const Utils::FilePath &fileName, int lineNumber); - - enum Roles { - Filename = Qt::UserRole, - LineNumber = Qt::UserRole + 1, - Directory = Qt::UserRole + 2, - LineText = Qt::UserRole + 3, - Note = Qt::UserRole + 4 - }; - - void toggleBookmark(const Utils::FilePath &fileName, int lineNumber); - void nextInDocument(); - void prevInDocument(); - void next(); - void prev(); - void moveUp(); - void moveDown(); - void edit(); - void editByFileAndLine(const Utils::FilePath &fileName, int lineNumber); - bool gotoBookmark(const Bookmark *bookmark) const; - -signals: - void updateActions(bool enableToggle, int state); - void currentIndexChanged(const QModelIndex &); - -private: - void updateActionStatus(); - void loadBookmarks(); - bool isAtCurrentBookmark() const; - - void documentPrevNext(bool next); - - Bookmark *findBookmark(const Utils::FilePath &filePath, int lineNumber); - void insertBookmark(int index, Bookmark *bookmark, bool userset = true); - void addBookmark(Bookmark *bookmark, bool userset = true); - void addBookmark(const QString &s); - static QString bookmarkToString(const Bookmark *b); - void saveBookmarks(); - - QMap> m_bookmarksMap; - - QList m_bookmarksList; - QItemSelectionModel *m_selectionModel; -}; - -class BookmarkViewFactory : public Core::INavigationWidgetFactory -{ -public: - BookmarkViewFactory(BookmarkManager *bm); - -private: - Core::NavigationView createWidget() override; - - BookmarkManager *m_manager; -}; - -} // Bookmarks::Internal diff --git a/src/plugins/bookmarks/bookmarks.qbs b/src/plugins/bookmarks/bookmarks.qbs deleted file mode 100644 index d42fa43cdfd..00000000000 --- a/src/plugins/bookmarks/bookmarks.qbs +++ /dev/null @@ -1,25 +0,0 @@ -import qbs 1.0 - -QtcPlugin { - name: "Bookmarks" - - Depends { name: "Qt.widgets" } - Depends { name: "Utils" } - - Depends { name: "Core" } - Depends { name: "ProjectExplorer" } - Depends { name: "TextEditor" } - - files: [ - "bookmark.cpp", - "bookmark.h", - "bookmarkfilter.cpp", - "bookmarkfilter.h", - "bookmarkmanager.cpp", - "bookmarkmanager.h", - "bookmarks_global.h", "bookmarkstr.h", - "bookmarksplugin.cpp", - "bookmarksplugin.h", - ] -} - diff --git a/src/plugins/bookmarks/bookmarks_global.h b/src/plugins/bookmarks/bookmarks_global.h deleted file mode 100644 index 2b9727d1c6e..00000000000 --- a/src/plugins/bookmarks/bookmarks_global.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -namespace Bookmarks { -namespace Constants { - -const char BOOKMARKS_TOGGLE_ACTION[] = "Bookmarks.Toggle"; -const char BOOKMARKS_EDIT_ACTION[] = "Bookmarks.Edit"; -const char BOOKMARKS_MOVEUP_ACTION[] = "Bookmarks.MoveUp"; -const char BOOKMARKS_MOVEDOWN_ACTION[] = "Bookmarks.MoveDown"; -const char BOOKMARKS_EDITNOTE_ACTION[] = "Bookmarks.EditNote"; -const char BOOKMARKS_PREV_ACTION[] = "Bookmarks.Previous"; -const char BOOKMARKS_NEXT_ACTION[] = "Bookmarks.Next"; -const char BOOKMARKS_PREVDIR_ACTION[] = "Bookmarks.PreviousDirectory"; -const char BOOKMARKS_NEXTDIR_ACTION[] = "Bookmarks.NextDirectory"; -const char BOOKMARKS_PREVDOC_ACTION[] = "Bookmarks.PreviousDocument"; -const char BOOKMARKS_NEXTDOC_ACTION[] = "Bookmarks.NextDocument"; -const char BOOKMARKS_TEXT_MARK_CATEGORY[] = "Bookmarks.TextMarkCategory"; - -const char BOOKMARKS_MENU[] = "Bookmarks.Menu"; -const char BOOKMARKS_CONTEXT[] = "Bookmarks"; - -} // namespace Constants -} // namespace Bookmarks diff --git a/src/plugins/bookmarks/bookmarksplugin.cpp b/src/plugins/bookmarks/bookmarksplugin.cpp deleted file mode 100644 index fc8f1ba7503..00000000000 --- a/src/plugins/bookmarks/bookmarksplugin.cpp +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "bookmarksplugin.h" - -#include "bookmarkfilter.h" -#include "bookmarkmanager.h" -#include "bookmarks_global.h" -#include "bookmarkstr.h" - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include - -using namespace Core; -using namespace TextEditor; -using namespace Utils; - -using namespace Bookmarks::Constants; - -namespace Bookmarks::Internal { - -class BookmarksPluginPrivate : public QObject -{ -public: - BookmarksPluginPrivate(); - - void updateActions(bool enableToggle, int stateMask); - void editorOpened(Core::IEditor *editor); - void editorAboutToClose(Core::IEditor *editor); - - void requestContextMenu(TextEditor::TextEditorWidget *widget, - int lineNumber, QMenu *menu); - - BookmarkManager m_bookmarkManager; - BookmarkFilter m_bookmarkFilter; - BookmarkViewFactory m_bookmarkViewFactory; - - QAction m_toggleAction{Tr::tr("Toggle Bookmark"), nullptr}; - QAction m_editAction{Tr::tr("Edit Bookmark"), nullptr}; - QAction m_prevAction{Tr::tr("Previous Bookmark"), nullptr}; - QAction m_nextAction{Tr::tr("Next Bookmark"), nullptr}; - QAction m_docPrevAction{Tr::tr("Previous Bookmark in Document"), nullptr}; - QAction m_docNextAction{Tr::tr("Next Bookmark in Document"), nullptr}; - QAction m_editBookmarkAction{Tr::tr("Edit Bookmark"), nullptr}; - QAction m_bookmarkMarginAction{Tr::tr("Toggle Bookmark"), nullptr}; - - int m_marginActionLineNumber = 0; - Utils::FilePath m_marginActionFileName; -}; - -BookmarksPlugin::~BookmarksPlugin() -{ - delete d; -} - -void BookmarksPlugin::initialize() -{ - d = new BookmarksPluginPrivate; -} - -BookmarksPluginPrivate::BookmarksPluginPrivate() - : m_bookmarkFilter(&m_bookmarkManager) - , m_bookmarkViewFactory(&m_bookmarkManager) -{ - ActionContainer *mtools = ActionManager::actionContainer(Core::Constants::M_TOOLS); - ActionContainer *touchBar = ActionManager::actionContainer(Core::Constants::TOUCH_BAR); - ActionContainer *mbm = ActionManager::createMenu(Id(BOOKMARKS_MENU)); - mbm->menu()->setTitle(Tr::tr("&Bookmarks")); - mtools->addMenu(mbm); - - const Context editorManagerContext(Core::Constants::C_EDITORMANAGER); - - // Toggle - Command *cmd = ActionManager::registerAction(&m_toggleAction, BOOKMARKS_TOGGLE_ACTION, - editorManagerContext); - cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? Tr::tr("Meta+M") : Tr::tr("Ctrl+M"))); - cmd->setTouchBarIcon(Utils::Icons::MACOS_TOUCHBAR_BOOKMARK.icon()); - mbm->addAction(cmd); - touchBar->addAction(cmd, Core::Constants::G_TOUCHBAR_EDITOR); - - cmd = ActionManager::registerAction(&m_editAction, BOOKMARKS_EDIT_ACTION, editorManagerContext); - cmd->setDefaultKeySequence( - QKeySequence(useMacShortcuts ? Tr::tr("Meta+Shift+M") : Tr::tr("Ctrl+Shift+M"))); - mbm->addAction(cmd); - - mbm->addSeparator(); - - // Previous - m_prevAction.setIcon(Utils::Icons::PREV_TOOLBAR.icon()); - m_prevAction.setIconVisibleInMenu(false); - cmd = ActionManager::registerAction(&m_prevAction, BOOKMARKS_PREV_ACTION, editorManagerContext); - cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? Tr::tr("Meta+,") - : Tr::tr("Ctrl+,"))); - mbm->addAction(cmd); - - // Next - m_nextAction.setIcon(Utils::Icons::NEXT_TOOLBAR.icon()); - m_nextAction.setIconVisibleInMenu(false); - cmd = ActionManager::registerAction(&m_nextAction, BOOKMARKS_NEXT_ACTION, editorManagerContext); - cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? Tr::tr("Meta+.") - : Tr::tr("Ctrl+."))); - mbm->addAction(cmd); - - mbm->addSeparator(); - - // Previous Doc - cmd = ActionManager::registerAction(&m_docPrevAction, BOOKMARKS_PREVDOC_ACTION, - editorManagerContext); - mbm->addAction(cmd); - - // Next Doc - cmd = ActionManager::registerAction(&m_docNextAction, BOOKMARKS_NEXTDOC_ACTION, - editorManagerContext); - mbm->addAction(cmd); - - connect(&m_toggleAction, &QAction::triggered, this, [this] { - IEditor *editor = EditorManager::currentEditor(); - auto widget = TextEditorWidget::fromEditor(editor); - if (widget && editor && !editor->document()->isTemporary()) - m_bookmarkManager.toggleBookmark(editor->document()->filePath(), editor->currentLine()); - }); - - connect(&m_editAction, &QAction::triggered, this, [this] { - IEditor *editor = EditorManager::currentEditor(); - auto widget = TextEditorWidget::fromEditor(editor); - if (widget && editor && !editor->document()->isTemporary()) { - const FilePath filePath = editor->document()->filePath(); - const int line = editor->currentLine(); - if (!m_bookmarkManager.hasBookmarkInPosition(filePath, line)) - m_bookmarkManager.toggleBookmark(filePath, line); - m_bookmarkManager.editByFileAndLine(filePath, line); - } - }); - - connect(&m_prevAction, &QAction::triggered, &m_bookmarkManager, &BookmarkManager::prev); - connect(&m_nextAction, &QAction::triggered, &m_bookmarkManager, &BookmarkManager::next); - connect(&m_docPrevAction, &QAction::triggered, - &m_bookmarkManager, &BookmarkManager::prevInDocument); - connect(&m_docNextAction, &QAction::triggered, - &m_bookmarkManager, &BookmarkManager::nextInDocument); - - connect(&m_editBookmarkAction, &QAction::triggered, this, [this] { - m_bookmarkManager.editByFileAndLine(m_marginActionFileName, m_marginActionLineNumber); - }); - - connect(&m_bookmarkManager, &BookmarkManager::updateActions, - this, &BookmarksPluginPrivate::updateActions); - updateActions(false, m_bookmarkManager.state()); - - connect(&m_bookmarkMarginAction, &QAction::triggered, this, [this] { - m_bookmarkManager.toggleBookmark(m_marginActionFileName, m_marginActionLineNumber); - }); - - // EditorManager - connect(EditorManager::instance(), &EditorManager::editorAboutToClose, - this, &BookmarksPluginPrivate::editorAboutToClose); - connect(EditorManager::instance(), &EditorManager::editorOpened, - this, &BookmarksPluginPrivate::editorOpened); -} - -void BookmarksPluginPrivate::updateActions(bool enableToggle, int state) -{ - const bool hasbm = state >= BookmarkManager::HasBookMarks; - const bool hasdocbm = state == BookmarkManager::HasBookmarksInDocument; - - m_toggleAction.setEnabled(enableToggle); - m_editAction.setEnabled(enableToggle); - m_prevAction.setEnabled(hasbm); - m_nextAction.setEnabled(hasbm); - m_docPrevAction.setEnabled(hasdocbm); - m_docNextAction.setEnabled(hasdocbm); -} - -void BookmarksPluginPrivate::editorOpened(IEditor *editor) -{ - if (auto widget = TextEditorWidget::fromEditor(editor)) { - connect(widget, &TextEditorWidget::markRequested, - this, [this, editor](TextEditorWidget *, int line, TextMarkRequestKind kind) { - if (kind == BookmarkRequest && !editor->document()->isTemporary()) - m_bookmarkManager.toggleBookmark(editor->document()->filePath(), line); - }); - - connect(widget, &TextEditorWidget::markContextMenuRequested, - this, &BookmarksPluginPrivate::requestContextMenu); - } -} - -void BookmarksPluginPrivate::editorAboutToClose(IEditor *editor) -{ - if (auto widget = TextEditorWidget::fromEditor(editor)) { - disconnect(widget, &TextEditorWidget::markContextMenuRequested, - this, &BookmarksPluginPrivate::requestContextMenu); - } -} - -void BookmarksPluginPrivate::requestContextMenu(TextEditorWidget *widget, - int lineNumber, QMenu *menu) -{ - if (widget->textDocument()->isTemporary()) - return; - - m_marginActionLineNumber = lineNumber; - m_marginActionFileName = widget->textDocument()->filePath(); - - menu->addAction(&m_bookmarkMarginAction); - if (m_bookmarkManager.hasBookmarkInPosition(m_marginActionFileName, m_marginActionLineNumber)) - menu->addAction(&m_editBookmarkAction); -} - -} // Bookmarks::Internal diff --git a/src/plugins/bookmarks/bookmarksplugin.h b/src/plugins/bookmarks/bookmarksplugin.h deleted file mode 100644 index 06764043c7b..00000000000 --- a/src/plugins/bookmarks/bookmarksplugin.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include - -namespace Bookmarks::Internal { - -class BookmarksPlugin final : public ExtensionSystem::IPlugin -{ - Q_OBJECT - Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Bookmarks.json") - - ~BookmarksPlugin() final; - - void initialize() final; - - class BookmarksPluginPrivate *d = nullptr; -}; - -} // Bookmarks::Internal diff --git a/src/plugins/bookmarks/bookmarkstr.h b/src/plugins/bookmarks/bookmarkstr.h deleted file mode 100644 index c48879d3470..00000000000 --- a/src/plugins/bookmarks/bookmarkstr.h +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include - -namespace Bookmarks { - -struct Tr -{ - Q_DECLARE_TR_FUNCTIONS(QtC::Bookmarks) -}; - -} // namespace Bookmarks diff --git a/src/plugins/plugins.qbs b/src/plugins/plugins.qbs index 0ea388ec705..6adaa25ae7e 100644 --- a/src/plugins/plugins.qbs +++ b/src/plugins/plugins.qbs @@ -12,7 +12,6 @@ Project { "bazaar/bazaar.qbs", "beautifier/beautifier.qbs", "bineditor/bineditor.qbs", - "bookmarks/bookmarks.qbs", "boot2qt/boot2qt.qbs", "clangcodemodel/clangcodemodel.qbs", "clangformat/clangformat.qbs", diff --git a/src/plugins/texteditor/CMakeLists.txt b/src/plugins/texteditor/CMakeLists.txt index 81d562778d5..03efc189224 100644 --- a/src/plugins/texteditor/CMakeLists.txt +++ b/src/plugins/texteditor/CMakeLists.txt @@ -16,6 +16,9 @@ add_qtc_plugin(TextEditor behaviorsettingspage.cpp behaviorsettingspage.h behaviorsettingswidget.cpp behaviorsettingswidget.h blockrange.h + bookmark.cpp bookmark.h + bookmarkfilter.cpp bookmarkfilter.h + bookmarkmanager.cpp bookmarkmanager.h circularclipboard.cpp circularclipboard.h circularclipboardassist.cpp circularclipboardassist.h codeassist/assistenums.h diff --git a/src/plugins/texteditor/basefilefind.cpp b/src/plugins/texteditor/basefilefind.cpp index 91c0b8e259a..3b9d68d3c37 100644 --- a/src/plugins/texteditor/basefilefind.cpp +++ b/src/plugins/texteditor/basefilefind.cpp @@ -357,7 +357,7 @@ void BaseFileFind::runSearch(SearchResult *search) d->m_futureSynchronizer.addFuture(future); FutureProgress *progress = ProgressManager::addTask(future, Tr::tr("Searching"), - Constants::TASK_SEARCH); + Core::Constants::TASK_SEARCH); connect(search, &SearchResult::countChanged, progress, [progress](int c) { progress->setSubtitle(Tr::tr("%n found.", nullptr, c)); }); diff --git a/src/plugins/texteditor/bookmark.cpp b/src/plugins/texteditor/bookmark.cpp new file mode 100644 index 00000000000..9d13d5a6b23 --- /dev/null +++ b/src/plugins/texteditor/bookmark.cpp @@ -0,0 +1,101 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "bookmark.h" + +#include "bookmarkmanager.h" +#include "texteditor_global.h" +#include "texteditortr.h" + +#include + +#include + +using namespace Utils; + +namespace TextEditor::Internal { + +const char BOOKMARKS_TEXT_MARK_CATEGORY[] = "Bookmarks.TextMarkCategory"; + +Bookmark::Bookmark(int lineNumber, BookmarkManager *manager) : + TextMark(FilePath(), lineNumber, {Tr::tr("Bookmark"), BOOKMARKS_TEXT_MARK_CATEGORY}), + m_manager(manager) +{ + setColor(Theme::Bookmarks_TextMarkColor); + setIcon(Icons::BOOKMARK_TEXTEDITOR.icon()); + setDefaultToolTip(Tr::tr("Bookmark")); + setPriority(TextMark::NormalPriority); +} + +void Bookmark::removedFromEditor() +{ + m_manager->deleteBookmark(this); +} + +bool Bookmark::isDraggable() const +{ + return true; +} + +void Bookmark::dragToLine(int lineNumber) +{ + move(lineNumber); +} + +void Bookmark::updateLineNumber(int line) +{ + if (line != lineNumber()) { + TextMark::updateLineNumber(line); + m_manager->updateBookmark(this); + } +} + +void Bookmark::move(int line) +{ + if (line != lineNumber()) { + TextMark::move(line); + m_manager->updateBookmark(this); + updateMarker(); + } +} + +void Bookmark::updateBlock(const QTextBlock &block) +{ + const QString &lineText = block.text().trimmed(); + if (m_lineText != lineText) { + m_lineText = lineText; + m_manager->updateBookmark(this); + } +} + +void Bookmark::updateFilePath(const FilePath &filePath) +{ + const FilePath oldFilePath = this->filePath(); + TextMark::updateFilePath(filePath); + m_manager->updateBookmarkFileName(this, oldFilePath); +} + +void Bookmark::setNote(const QString ¬e) +{ + setToolTip(note); + setLineAnnotation(note); + updateMarker(); +} + +void Bookmark::updateNote(const QString ¬e) +{ + setNote(note); + m_manager->updateBookmark(this); +} + +QString Bookmark::lineText() const +{ + return m_lineText; +} + +QString Bookmark::note() const +{ + return toolTip(); +} + +} // Bookmarks::Internal diff --git a/src/plugins/texteditor/bookmark.h b/src/plugins/texteditor/bookmark.h new file mode 100644 index 00000000000..1c8c522363e --- /dev/null +++ b/src/plugins/texteditor/bookmark.h @@ -0,0 +1,37 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include + +namespace TextEditor::Internal { + +class BookmarkManager; + +class Bookmark : public TextMark +{ +public: + Bookmark(int lineNumber, BookmarkManager *manager); + + void updateLineNumber(int lineNumber) override; + void move(int line) override; + void updateBlock(const QTextBlock &block) override; + void updateFilePath(const Utils::FilePath &filePath) override; + void removedFromEditor() override; + + bool isDraggable() const override; + void dragToLine(int lineNumber) override; + + void setNote(const QString ¬e); + void updateNote(const QString ¬e); + + QString lineText() const; + QString note() const; + +private: + BookmarkManager *m_manager; + QString m_lineText; +}; + +} // TextEditor::Internal diff --git a/src/plugins/texteditor/bookmarkfilter.cpp b/src/plugins/texteditor/bookmarkfilter.cpp new file mode 100644 index 00000000000..2756521cd89 --- /dev/null +++ b/src/plugins/texteditor/bookmarkfilter.cpp @@ -0,0 +1,115 @@ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "bookmarkfilter.h" + +#include "bookmark.h" +#include "bookmarkmanager.h" +#include "texteditortr.h" + +#include + +using namespace Core; +using namespace Utils; + +namespace TextEditor::Internal { + +BookmarkFilter::BookmarkFilter(BookmarkManager *manager) + : m_manager(manager) +{ + setId("Bookmarks"); + setDisplayName(Tr::tr("Bookmarks")); + setDescription(Tr::tr("Locates bookmarks. Filter by file name, by the text on the line of the " + "bookmark, or by the bookmark's note text.")); + setPriority(Medium); + setDefaultShortcutString("b"); +} + +LocatorMatcherTasks BookmarkFilter::matchers() +{ + using namespace Tasking; + + TreeStorage storage; + + const auto onSetup = [=] { storage->reportOutput(match(storage->input())); }; + return {{Sync(onSetup), storage}}; +} + +LocatorFilterEntries BookmarkFilter::match(const QString &input) const +{ + if (m_manager->rowCount() == 0) + return {}; + const auto match = [this](const QString &name, BookmarkManager::Roles role) { + return m_manager->match(m_manager->index(0, 0), role, name, -1, + Qt::MatchContains | Qt::MatchWrap); + }; + + const int colonIndex = input.lastIndexOf(':'); + QModelIndexList fileNameLineNumberMatches; + if (colonIndex >= 0) { + // Filter by "fileName:lineNumber" pattern + const QString fileName = input.left(colonIndex); + const QString lineNumber = input.mid(colonIndex + 1); + fileNameLineNumberMatches = match(fileName, BookmarkManager::Filename); + fileNameLineNumberMatches = Utils::filtered( + fileNameLineNumberMatches, [lineNumber](const QModelIndex &index) { + return index.data(BookmarkManager::LineNumber).toString().contains(lineNumber); + }); + } + const QModelIndexList matches = filteredUnique(fileNameLineNumberMatches + + match(input, BookmarkManager::Filename) + + match(input, BookmarkManager::LineNumber) + + match(input, BookmarkManager::Note) + + match(input, BookmarkManager::LineText)); + LocatorFilterEntries entries; + for (const QModelIndex &idx : matches) { + const Bookmark *bookmark = m_manager->bookmarkForIndex(idx); + const QString filename = bookmark->filePath().fileName(); + LocatorFilterEntry entry; + entry.displayName = QString("%1:%2").arg(filename).arg(bookmark->lineNumber()); + // TODO: according to QModelIndex docs, we shouldn't store model indexes: + // Model indexes should be used immediately and then discarded. + // You should not rely on indexes to remain valid after calling model functions + // that change the structure of the model or delete items. + entry.acceptor = [manager = m_manager, idx] { + if (Bookmark *bookmark = manager->bookmarkForIndex(idx)) + manager->gotoBookmark(bookmark); + return AcceptResult(); + }; + if (!bookmark->note().isEmpty()) + entry.extraInfo = bookmark->note(); + else if (!bookmark->lineText().isEmpty()) + entry.extraInfo = bookmark->lineText(); + else + entry.extraInfo = bookmark->filePath().toString(); + int highlightIndex = entry.displayName.indexOf(input, 0, Qt::CaseInsensitive); + if (highlightIndex >= 0) { + entry.highlightInfo = {highlightIndex, int(input.length())}; + } else { + highlightIndex = entry.extraInfo.indexOf(input, 0, Qt::CaseInsensitive); + if (highlightIndex >= 0) { + entry.highlightInfo = {highlightIndex, int(input.length()), + LocatorFilterEntry::HighlightInfo::ExtraInfo}; + } else if (colonIndex >= 0) { + const QString fileName = input.left(colonIndex); + const QString lineNumber = input.mid(colonIndex + 1); + highlightIndex = entry.displayName.indexOf(fileName, 0, + Qt::CaseInsensitive); + if (highlightIndex >= 0) { + entry.highlightInfo = {highlightIndex, int(fileName.length())}; + highlightIndex = entry.displayName.indexOf( + lineNumber, highlightIndex, Qt::CaseInsensitive); + if (highlightIndex >= 0) { + entry.highlightInfo.startsDisplay += highlightIndex; + entry.highlightInfo.lengthsDisplay += lineNumber.length(); + } + } + } + } + entry.displayIcon = bookmark->icon(); + entries.append(entry); + } + return entries; +} + +} // TextEditor::Internal diff --git a/src/plugins/texteditor/bookmarkfilter.h b/src/plugins/texteditor/bookmarkfilter.h new file mode 100644 index 00000000000..fca42dd679d --- /dev/null +++ b/src/plugins/texteditor/bookmarkfilter.h @@ -0,0 +1,24 @@ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include + +namespace TextEditor::Internal { + +class BookmarkManager; + +class BookmarkFilter : public Core::ILocatorFilter +{ +public: + BookmarkFilter(BookmarkManager *manager); + +private: + Core::LocatorMatcherTasks matchers() final; + Core::LocatorFilterEntries match(const QString &input) const; + + BookmarkManager *m_manager = nullptr; // not owned +}; + +} // TextEditor::Internal diff --git a/src/plugins/texteditor/bookmarkmanager.cpp b/src/plugins/texteditor/bookmarkmanager.cpp new file mode 100644 index 00000000000..1ab12cfb88d --- /dev/null +++ b/src/plugins/texteditor/bookmarkmanager.cpp @@ -0,0 +1,830 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "bookmarkmanager.h" + +#include "bookmark.h" +#include "texteditorconstants.h" +#include "texteditortr.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +Q_DECLARE_METATYPE(TextEditor::Internal::Bookmark*) + +using namespace Core; +using namespace Utils; + +namespace TextEditor::Internal { + +const char BOOKMARKS_CONTEXT[] = "Bookmarks"; + +class BookmarkDelegate : public QStyledItemDelegate +{ +public: + BookmarkDelegate(QObject *parent) + : QStyledItemDelegate(parent) + {} + +private: + void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const final; + QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const final; + void generateGradientPixmap(int width, int height, const QColor &color, bool selected) const; + + mutable QPixmap m_normalPixmap; + mutable QPixmap m_selectedPixmap; +}; + +QSize BookmarkDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QStyleOptionViewItem opt = option; + initStyleOption(&opt, index); + + QFontMetrics fm(option.font); + QSize s; + s.setWidth(option.rect.width()); + s.setHeight(fm.height() * 2 + 10); + return s; +} + +void BookmarkDelegate::generateGradientPixmap(int width, int height, const QColor &color, bool selected) const +{ + QColor c = color; + c.setAlpha(0); + + QPixmap pixmap(width+1, height); + pixmap.fill(c); + + QPainter painter(&pixmap); + painter.setPen(Qt::NoPen); + + QLinearGradient lg; + lg.setCoordinateMode(QGradient::ObjectBoundingMode); + lg.setFinalStop(1,0); + + lg.setColorAt(0, c); + lg.setColorAt(0.4, color); + + painter.setBrush(lg); + painter.drawRect(0, 0, width+1, height); + + if (selected) + m_selectedPixmap = pixmap; + else + m_normalPixmap = pixmap; +} + +void BookmarkDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QStyleOptionViewItem opt = option; + initStyleOption(&opt, index); + painter->save(); + + QFontMetrics fm(opt.font); + static int lwidth = fm.horizontalAdvance(QLatin1String("8888")) + 18; + + QColor backgroundColor; + QColor textColor; + + bool selected = opt.state & QStyle::State_Selected; + + if (selected) { + painter->setBrush(opt.palette.highlight().color()); + backgroundColor = opt.palette.highlight().color(); + if (!m_selectedPixmap) + generateGradientPixmap(lwidth, fm.height()+1, backgroundColor, selected); + } else { + backgroundColor = opt.palette.window().color(); + painter->setBrush(backgroundColor); + if (!m_normalPixmap) + generateGradientPixmap(lwidth, fm.height(), backgroundColor, selected); + } + painter->setPen(Qt::NoPen); + painter->drawRect(opt.rect); + + // Set Text Color + if (opt.state & QStyle::State_Selected) + textColor = opt.palette.highlightedText().color(); + else + textColor = opt.palette.text().color(); + + painter->setPen(textColor); + + + // TopLeft + QString topLeft = index.data(BookmarkManager::Filename).toString(); + painter->drawText(6, 2 + opt.rect.top() + fm.ascent(), topLeft); + + QString topRight = index.data(BookmarkManager::LineNumber).toString(); + // Check whether we need to be fancy and paint some background + int fwidth = fm.horizontalAdvance(topLeft); + if (fwidth + lwidth > opt.rect.width()) { + int left = opt.rect.right() - lwidth; + painter->drawPixmap(left, opt.rect.top(), selected ? m_selectedPixmap : m_normalPixmap); + } + // topRight + painter->drawText(opt.rect.right() - fm.horizontalAdvance(topRight) - 6, + 2 + opt.rect.top() + fm.ascent(), topRight); + + // Directory + QColor mix; + mix.setRgbF(0.7 * textColor.redF() + 0.3 * backgroundColor.redF(), + 0.7 * textColor.greenF() + 0.3 * backgroundColor.greenF(), + 0.7 * textColor.blueF() + 0.3 * backgroundColor.blueF()); + painter->setPen(mix); + + QString lineText = index.data(BookmarkManager::Note).toString().trimmed(); + if (lineText.isEmpty()) + lineText = index.data(BookmarkManager::LineText).toString(); + + painter->drawText(6, opt.rect.top() + fm.ascent() + fm.height() + 6, lineText); + + // Separator lines + const QRectF innerRect = QRectF(opt.rect).adjusted(0.5, 0.5, -0.5, -0.5); + painter->setPen(QColor::fromRgb(150,150,150)); + painter->drawLine(innerRect.bottomLeft(), innerRect.bottomRight()); + painter->restore(); +} + +// BookmarkView + +class BookmarkView final : public Utils::ListView +{ +public: + explicit BookmarkView(BookmarkManager *manager); + + QList createToolBarWidgets(); + + void gotoBookmark(const QModelIndex &index); + + void removeFromContextMenu(); + void removeAll(); + +protected: + void contextMenuEvent(QContextMenuEvent *event) final; + void removeBookmark(const QModelIndex &index); + void keyPressEvent(QKeyEvent *event) final; + +private: + Core::IContext *m_bookmarkContext; + QModelIndex m_contextMenuIndex; + BookmarkManager *m_manager; +}; + +BookmarkView::BookmarkView(BookmarkManager *manager) : + m_bookmarkContext(new IContext(this)), + m_manager(manager) +{ + setWindowTitle(Tr::tr("Bookmarks")); + + m_bookmarkContext->setWidget(this); + m_bookmarkContext->setContext(Context(BOOKMARKS_CONTEXT)); + + ICore::addContextObject(m_bookmarkContext); + + ListView::setModel(manager); + + setItemDelegate(new BookmarkDelegate(this)); + setFrameStyle(QFrame::NoFrame); + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setAttribute(Qt::WA_MacShowFocusRect, false); + setSelectionModel(manager->selectionModel()); + setSelectionMode(QAbstractItemView::SingleSelection); + setSelectionBehavior(QAbstractItemView::SelectRows); + setDragEnabled(true); + setDragDropMode(QAbstractItemView::DragOnly); + + connect(this, &QAbstractItemView::clicked, this, &BookmarkView::gotoBookmark); + connect(this, &QAbstractItemView::activated, this, &BookmarkView::gotoBookmark); +} + +QList BookmarkView::createToolBarWidgets() +{ + Command *prevCmd = ActionManager::command(TextEditor::Constants::BOOKMARKS_PREV_ACTION); + Command *nextCmd = ActionManager::command(TextEditor::Constants::BOOKMARKS_NEXT_ACTION); + QTC_ASSERT(prevCmd && nextCmd, return {}); + auto prevButton = new QToolButton(this); + prevButton->setToolButtonStyle(Qt::ToolButtonIconOnly); + prevButton->setDefaultAction(prevCmd->action()); + auto nextButton = new QToolButton(this); + nextButton->setToolButtonStyle(Qt::ToolButtonIconOnly); + nextButton->setDefaultAction(nextCmd->action()); + return {prevButton, nextButton}; +} + +void BookmarkView::contextMenuEvent(QContextMenuEvent *event) +{ + QMenu menu; + QAction *moveUp = menu.addAction(Tr::tr("Move Up")); + QAction *moveDown = menu.addAction(Tr::tr("Move Down")); + QAction *edit = menu.addAction(Tr::tr("&Edit")); + menu.addSeparator(); + QAction *remove = menu.addAction(Tr::tr("&Remove")); + menu.addSeparator(); + QAction *removeAll = menu.addAction(Tr::tr("Remove All")); + + m_contextMenuIndex = indexAt(event->pos()); + if (!m_contextMenuIndex.isValid()) { + moveUp->setEnabled(false); + moveDown->setEnabled(false); + remove->setEnabled(false); + edit->setEnabled(false); + } + + if (model()->rowCount() == 0) + removeAll->setEnabled(false); + + connect(moveUp, &QAction::triggered, m_manager, &BookmarkManager::moveUp); + connect(moveDown, &QAction::triggered, m_manager, &BookmarkManager::moveDown); + connect(remove, &QAction::triggered, this, &BookmarkView::removeFromContextMenu); + connect(removeAll, &QAction::triggered, this, &BookmarkView::removeAll); + connect(edit, &QAction::triggered, m_manager, &BookmarkManager::edit); + + menu.exec(mapToGlobal(event->pos())); +} + +void BookmarkView::removeFromContextMenu() +{ + removeBookmark(m_contextMenuIndex); +} + +void BookmarkView::removeBookmark(const QModelIndex& index) +{ + Bookmark *bm = m_manager->bookmarkForIndex(index); + m_manager->deleteBookmark(bm); +} + +void BookmarkView::keyPressEvent(QKeyEvent *event) +{ + if (event->key() == Qt::Key_Delete || event->key() == Qt::Key_Backspace) { + removeBookmark(currentIndex()); + event->accept(); + return; + } + ListView::keyPressEvent(event); +} + +void BookmarkView::removeAll() +{ + if (CheckableMessageBox::question(this, + Tr::tr("Remove All Bookmarks"), + Tr::tr("Are you sure you want to remove all bookmarks from " + "all files in the current session?"), + QString("RemoveAllBookmarks")) + != QMessageBox::Yes) + return; + + // The performance of this function could be greatly improved. + while (m_manager->rowCount()) { + QModelIndex index = m_manager->index(0, 0); + removeBookmark(index); + } +} + +void BookmarkView::gotoBookmark(const QModelIndex &index) +{ + Bookmark *bk = m_manager->bookmarkForIndex(index); + if (!m_manager->gotoBookmark(bk)) + m_manager->deleteBookmark(bk); +} + +//// +// BookmarkManager +//// + +BookmarkManager::BookmarkManager() : + m_selectionModel(new QItemSelectionModel(this, this)) +{ + connect(ICore::instance(), &ICore::contextChanged, + this, &BookmarkManager::updateActionStatus); + + connect(SessionManager::instance(), &SessionManager::sessionLoaded, + this, &BookmarkManager::loadBookmarks); + + updateActionStatus(); +} + +BookmarkManager::~BookmarkManager() +{ + qDeleteAll(m_bookmarksList); +} + +QItemSelectionModel *BookmarkManager::selectionModel() const +{ + return m_selectionModel; +} + +bool BookmarkManager::hasBookmarkInPosition(const Utils::FilePath &fileName, int lineNumber) +{ + return findBookmark(fileName, lineNumber); +} + +QModelIndex BookmarkManager::index(int row, int column, const QModelIndex &parent) const +{ + if (parent.isValid()) + return QModelIndex(); + else + return createIndex(row, column); +} + +QModelIndex BookmarkManager::parent(const QModelIndex &) const +{ + return QModelIndex(); +} + +int BookmarkManager::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + else + return m_bookmarksList.count(); +} + +int BookmarkManager::columnCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + return 3; +} + +QVariant BookmarkManager::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.column() !=0 || index.row() < 0 || index.row() >= m_bookmarksList.count()) + return QVariant(); + + Bookmark *bookMark = m_bookmarksList.at(index.row()); + if (role == BookmarkManager::Filename) + return bookMark->filePath().fileName(); + if (role == BookmarkManager::LineNumber) + return bookMark->lineNumber(); + if (role == BookmarkManager::Directory) + return bookMark->filePath().toFileInfo().path(); + if (role == BookmarkManager::LineText) + return bookMark->lineText(); + if (role == BookmarkManager::Note) + return bookMark->note(); + if (role == Qt::ToolTipRole) + return bookMark->filePath().toUserOutput(); + return QVariant(); +} + +Qt::ItemFlags BookmarkManager::flags(const QModelIndex &index) const +{ + if (!index.isValid() || index.column() !=0 || index.row() < 0 || index.row() >= m_bookmarksList.count()) + return Qt::NoItemFlags; + return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled; +} + +Qt::DropActions BookmarkManager::supportedDragActions() const +{ + return Qt::MoveAction; +} + +QStringList BookmarkManager::mimeTypes() const +{ + return DropSupport::mimeTypesForFilePaths(); +} + +QMimeData *BookmarkManager::mimeData(const QModelIndexList &indexes) const +{ + auto data = new DropMimeData; + for (const QModelIndex &index : indexes) { + if (!index.isValid() || index.column() != 0 || index.row() < 0 || index.row() >= m_bookmarksList.count()) + continue; + Bookmark *bookMark = m_bookmarksList.at(index.row()); + data->addFile(bookMark->filePath(), bookMark->lineNumber()); + } + return data; +} + +void BookmarkManager::toggleBookmark(const FilePath &fileName, int lineNumber) +{ + if (lineNumber <= 0 || fileName.isEmpty()) + return; + + // Remove any existing bookmark on this line + if (Bookmark *mark = findBookmark(fileName, lineNumber)) { + // TODO check if the bookmark is really on the same markable Interface + deleteBookmark(mark); + return; + } + + // Add a new bookmark if no bookmark existed on this line + auto mark = new Bookmark(lineNumber, this); + mark->updateFilePath(fileName); + const QModelIndex currentIndex = selectionModel()->currentIndex(); + const int insertionIndex = currentIndex.isValid() ? currentIndex.row() + 1 + : m_bookmarksList.size(); + insertBookmark(insertionIndex, mark); +} + +void BookmarkManager::updateBookmark(Bookmark *bookmark) +{ + const int idx = m_bookmarksList.indexOf(bookmark); + if (idx == -1) + return; + + emit dataChanged(index(idx, 0, QModelIndex()), index(idx, 2, QModelIndex())); + saveBookmarks(); +} + +void BookmarkManager::updateBookmarkFileName(Bookmark *bookmark, const FilePath &oldFilePath) +{ + if (oldFilePath == bookmark->filePath()) + return; + + m_bookmarksMap[oldFilePath].removeAll(bookmark); + m_bookmarksMap[bookmark->filePath()].append(bookmark); + updateBookmark(bookmark); +} + +void BookmarkManager::removeAllBookmarks() +{ + if (m_bookmarksList.isEmpty()) + return; + beginRemoveRows(QModelIndex(), 0, m_bookmarksList.size() - 1); + + qDeleteAll(m_bookmarksList); + m_bookmarksMap.clear(); + m_bookmarksList.clear(); + endRemoveRows(); +} + +void BookmarkManager::deleteBookmark(Bookmark *bookmark) +{ + int idx = m_bookmarksList.indexOf(bookmark); + beginRemoveRows(QModelIndex(), idx, idx); + + m_bookmarksMap[bookmark->filePath()].removeAll(bookmark); + delete bookmark; + + m_bookmarksList.removeAt(idx); + endRemoveRows(); + + if (selectionModel()->currentIndex().isValid()) + selectionModel()->setCurrentIndex(selectionModel()->currentIndex(), QItemSelectionModel::Select | QItemSelectionModel::Clear); + + updateActionStatus(); + saveBookmarks(); +} + +Bookmark *BookmarkManager::bookmarkForIndex(const QModelIndex &index) const +{ + if (!index.isValid() || index.row() >= m_bookmarksList.size()) + return nullptr; + return m_bookmarksList.at(index.row()); +} + +bool BookmarkManager::gotoBookmark(const Bookmark *bookmark) const +{ + if (IEditor *editor = EditorManager::openEditorAt( + Utils::Link(bookmark->filePath(), bookmark->lineNumber()))) { + return editor->currentLine() == bookmark->lineNumber(); + } + return false; +} + +void BookmarkManager::nextInDocument() +{ + documentPrevNext(true); +} + +void BookmarkManager::prevInDocument() +{ + documentPrevNext(false); +} + +void BookmarkManager::documentPrevNext(bool next) +{ + IEditor *editor = EditorManager::currentEditor(); + const int editorLine = editor->currentLine(); + if (editorLine <= 0) + return; + + const FilePath filePath = editor->document()->filePath(); + if (!m_bookmarksMap.contains(filePath)) + return; + + int firstLine = -1; + int lastLine = -1; + int prevLine = -1; + int nextLine = -1; + const QVector marks = m_bookmarksMap[filePath]; + for (int i = 0; i < marks.count(); ++i) { + int markLine = marks.at(i)->lineNumber(); + if (firstLine == -1 || firstLine > markLine) + firstLine = markLine; + if (lastLine < markLine) + lastLine = markLine; + if (markLine < editorLine && prevLine < markLine) + prevLine = markLine; + if (markLine > editorLine && + (nextLine == -1 || nextLine > markLine)) + nextLine = markLine; + } + + EditorManager::addCurrentPositionToNavigationHistory(); + if (next) { + if (nextLine == -1) + editor->gotoLine(firstLine); + else + editor->gotoLine(nextLine); + } else { + if (prevLine == -1) + editor->gotoLine(lastLine); + else + editor->gotoLine(prevLine); + } +} + +void BookmarkManager::next() +{ + QModelIndex current = selectionModel()->currentIndex(); + if (!current.isValid()) + return; + int row = current.row(); + ++row; + while (true) { + if (row == m_bookmarksList.size()) + row = 0; + + Bookmark *bk = m_bookmarksList.at(row); + if (gotoBookmark(bk)) { + QModelIndex newIndex = current.sibling(row, current.column()); + selectionModel()->setCurrentIndex(newIndex, QItemSelectionModel::Select | QItemSelectionModel::Clear); + return; + } + deleteBookmark(bk); + if (m_bookmarksList.isEmpty()) // No bookmarks anymore ... + return; + } +} + +void BookmarkManager::prev() +{ + QModelIndex current = selectionModel()->currentIndex(); + if (!current.isValid()) + return; + if (!isAtCurrentBookmark() && gotoBookmark(bookmarkForIndex(current))) + return; + int row = current.row(); + while (true) { + if (row == 0) + row = m_bookmarksList.size(); + --row; + Bookmark *bk = m_bookmarksList.at(row); + if (gotoBookmark(bk)) { + QModelIndex newIndex = current.sibling(row, current.column()); + selectionModel()->setCurrentIndex(newIndex, QItemSelectionModel::Select | QItemSelectionModel::Clear); + return; + } + deleteBookmark(bk); + if (m_bookmarksList.isEmpty()) + return; + } +} + +BookmarkManager::State BookmarkManager::state() const +{ + if (m_bookmarksList.empty()) + return NoBookMarks; + + IEditor *editor = EditorManager::currentEditor(); + if (!editor) + return HasBookMarks; + + return m_bookmarksMap.value(editor->document()->filePath()).isEmpty() ? HasBookMarks + : HasBookmarksInDocument; +} + +void BookmarkManager::updateActionStatus() +{ + IEditor *editor = EditorManager::currentEditor(); + const bool enableToggle = editor && !editor->document()->isTemporary(); + + emit updateActions(enableToggle, state()); +} + +void BookmarkManager::moveUp() +{ + QModelIndex current = selectionModel()->currentIndex(); + int row = current.row(); + if (row == 0) + row = m_bookmarksList.size(); + --row; + + // swap current.row() and row + + Bookmark *b = m_bookmarksList.at(row); + m_bookmarksList[row] = m_bookmarksList.at(current.row()); + m_bookmarksList[current.row()] = b; + + QModelIndex topLeft = current.sibling(row, 0); + QModelIndex bottomRight = current.sibling(current.row(), 2); + emit dataChanged(topLeft, bottomRight); + selectionModel()->setCurrentIndex(current.sibling(row, 0), QItemSelectionModel::Select | QItemSelectionModel::Clear); + + saveBookmarks(); +} + +void BookmarkManager::moveDown() +{ + QModelIndex current = selectionModel()->currentIndex(); + int row = current.row(); + ++row; + if (row == m_bookmarksList.size()) + row = 0; + + // swap current.row() and row + Bookmark *b = m_bookmarksList.at(row); + m_bookmarksList[row] = m_bookmarksList.at(current.row()); + m_bookmarksList[current.row()] = b; + + QModelIndex topLeft = current.sibling(current.row(), 0); + QModelIndex bottomRight = current.sibling(row, 2); + emit dataChanged(topLeft, bottomRight); + selectionModel()->setCurrentIndex(current.sibling(row, 0), QItemSelectionModel::Select | QItemSelectionModel::Clear); + + saveBookmarks(); +} + +void BookmarkManager::editByFileAndLine(const FilePath &fileName, int lineNumber) +{ + Bookmark *b = findBookmark(fileName, lineNumber); + QModelIndex current = selectionModel()->currentIndex(); + selectionModel()->setCurrentIndex(current.sibling(m_bookmarksList.indexOf(b), 0), + QItemSelectionModel::Select | QItemSelectionModel::Clear); + + edit(); +} + +void BookmarkManager::edit() +{ + QModelIndex current = selectionModel()->currentIndex(); + Bookmark *b = m_bookmarksList.at(current.row()); + + QDialog dlg; + dlg.setWindowTitle(Tr::tr("Edit Bookmark")); + auto layout = new QFormLayout(&dlg); + auto noteEdit = new QLineEdit(b->note()); + noteEdit->setMinimumWidth(300); + auto lineNumberSpinbox = new QSpinBox; + lineNumberSpinbox->setRange(1, INT_MAX); + lineNumberSpinbox->setValue(b->lineNumber()); + lineNumberSpinbox->setMaximumWidth(100); + auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + connect(buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept); + connect(buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject); + layout->addRow(Tr::tr("Note text:"), noteEdit); + layout->addRow(Tr::tr("Line number:"), lineNumberSpinbox); + layout->addWidget(buttonBox); + if (dlg.exec() == QDialog::Accepted) { + b->move(lineNumberSpinbox->value()); + b->updateNote(noteEdit->text().replace(QLatin1Char('\t'), QLatin1Char(' '))); + emit dataChanged(current, current); + saveBookmarks(); + } +} + +/* Returns the bookmark at the given file and line number, or 0 if no such bookmark exists. */ +Bookmark *BookmarkManager::findBookmark(const FilePath &filePath, int lineNumber) +{ + return Utils::findOrDefault(m_bookmarksMap.value(filePath), + Utils::equal(&Bookmark::lineNumber, lineNumber)); +} + +void BookmarkManager::insertBookmark(int idx, Bookmark *bookmark, bool userset) +{ + idx = qBound(0, idx, m_bookmarksList.size()); + beginInsertRows(QModelIndex(), idx, idx); + + m_bookmarksMap[bookmark->filePath()].append(bookmark); + m_bookmarksList.insert(idx, bookmark); + + endInsertRows(); + if (userset) { + updateActionStatus(); + saveBookmarks(); + } + selectionModel()->setCurrentIndex(index(idx, 0, QModelIndex()), + QItemSelectionModel::Select | QItemSelectionModel::Clear); +} + +/* Adds a bookmark to the internal data structures. The 'userset' parameter + * determines whether action status should be updated and whether the bookmarks + * should be saved to the session settings. + */ +void BookmarkManager::addBookmark(Bookmark *bookmark, bool userset) +{ + insertBookmark(m_bookmarksList.size(), bookmark, userset); +} + +/* Adds a new bookmark based on information parsed from the string. */ +void BookmarkManager::addBookmark(const QString &s) +{ + // index3 is a frontier beetween note text and other bookmarks data + int index3 = s.lastIndexOf(QLatin1Char('\t')); + if (index3 < 0) + index3 = s.size(); + int index2 = s.lastIndexOf(QLatin1Char(':'), index3 - 1); + int index1 = s.indexOf(QLatin1Char(':')); + + if (index3 != -1 || index2 != -1 || index1 != -1) { + const QString &filePath = s.mid(index1+1, index2-index1-1); + const QString ¬e = s.mid(index3 + 1); + const int lineNumber = s.mid(index2 + 1, index3 - index2 - 1).toInt(); + if (!filePath.isEmpty() && !findBookmark(FilePath::fromString(filePath), lineNumber)) { + auto b = new Bookmark(lineNumber, this); + b->updateFilePath(FilePath::fromString(filePath)); + b->setNote(note); + addBookmark(b, false); + } + } else { + qDebug() << "BookmarkManager::addBookmark() Invalid bookmark string:" << s; + } +} + +/* Puts the bookmark in a string for storing it in the settings. */ +QString BookmarkManager::bookmarkToString(const Bookmark *b) +{ + const QLatin1Char colon(':'); + // Using \t as delimiter because any another symbol can be a part of note. + const QLatin1Char noteDelimiter('\t'); + return colon + b->filePath().toString() + + colon + QString::number(b->lineNumber()) + + noteDelimiter + b->note(); +} + +/* Saves the bookmarks to the session settings. */ +void BookmarkManager::saveBookmarks() +{ + QStringList list; + for (const Bookmark *bookmark : std::as_const(m_bookmarksList)) + list << bookmarkToString(bookmark); + + SessionManager::setValue("Bookmarks", list); +} + +/* Loads the bookmarks from the session settings. */ +void BookmarkManager::loadBookmarks() +{ + removeAllBookmarks(); + const QStringList &list = SessionManager::value("Bookmarks").toStringList(); + for (const QString &bookmarkString : list) + addBookmark(bookmarkString); + + updateActionStatus(); +} + +bool BookmarkManager::isAtCurrentBookmark() const +{ + Bookmark *bk = bookmarkForIndex(selectionModel()->currentIndex()); + if (!bk) + return true; + IEditor *currentEditor = EditorManager::currentEditor(); + return currentEditor + && currentEditor->document()->filePath() == bk->filePath() + && currentEditor->currentLine() == bk->lineNumber(); +} + +// BookmarkViewFactory + +BookmarkViewFactory::BookmarkViewFactory(BookmarkManager *bm) + : m_manager(bm) +{ + setDisplayName(Tr::tr("Bookmarks")); + setPriority(300); + setId("Bookmarks"); + setActivationSequence(QKeySequence(useMacShortcuts ? Tr::tr("Alt+Meta+M") : Tr::tr("Alt+M"))); +} + +NavigationView BookmarkViewFactory::createWidget() +{ + auto view = new BookmarkView(m_manager); + return {view, view->createToolBarWidgets()}; +} + +} // TextEditor::Internal diff --git a/src/plugins/texteditor/bookmarkmanager.h b/src/plugins/texteditor/bookmarkmanager.h new file mode 100644 index 00000000000..4df91094e3d --- /dev/null +++ b/src/plugins/texteditor/bookmarkmanager.h @@ -0,0 +1,113 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace Core { class IContext; } + +namespace TextEditor::Internal { + +class Bookmark; +class BookmarksPlugin; +class BookmarkContext; + +class BookmarkManager final : public QAbstractItemModel +{ + Q_OBJECT + +public: + BookmarkManager(); + ~BookmarkManager() final; + + void updateBookmark(Bookmark *bookmark); + void updateBookmarkFileName(Bookmark *bookmark, const Utils::FilePath &oldFilePath); + void deleteBookmark(Bookmark *bookmark); // Does not remove the mark + void removeAllBookmarks(); + Bookmark *bookmarkForIndex(const QModelIndex &index) const; + + enum State { NoBookMarks, HasBookMarks, HasBookmarksInDocument }; + State state() const; + + // Model stuff + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const final; + QModelIndex parent(const QModelIndex &child) const final; + int rowCount(const QModelIndex &parent = QModelIndex()) const final; + int columnCount(const QModelIndex &parent = QModelIndex()) const final; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const final; + Qt::ItemFlags flags(const QModelIndex &index) const final; + + Qt::DropActions supportedDragActions() const final; + QStringList mimeTypes() const final; + QMimeData *mimeData(const QModelIndexList &indexes) const final; + + // this QItemSelectionModel is shared by all views + QItemSelectionModel *selectionModel() const; + + bool hasBookmarkInPosition(const Utils::FilePath &fileName, int lineNumber); + + enum Roles { + Filename = Qt::UserRole, + LineNumber = Qt::UserRole + 1, + Directory = Qt::UserRole + 2, + LineText = Qt::UserRole + 3, + Note = Qt::UserRole + 4 + }; + + void toggleBookmark(const Utils::FilePath &fileName, int lineNumber); + void nextInDocument(); + void prevInDocument(); + void next(); + void prev(); + void moveUp(); + void moveDown(); + void edit(); + void editByFileAndLine(const Utils::FilePath &fileName, int lineNumber); + bool gotoBookmark(const Bookmark *bookmark) const; + +signals: + void updateActions(bool enableToggle, int state); + void currentIndexChanged(const QModelIndex &); + +private: + void updateActionStatus(); + void loadBookmarks(); + bool isAtCurrentBookmark() const; + + void documentPrevNext(bool next); + + Bookmark *findBookmark(const Utils::FilePath &filePath, int lineNumber); + void insertBookmark(int index, Bookmark *bookmark, bool userset = true); + void addBookmark(Bookmark *bookmark, bool userset = true); + void addBookmark(const QString &s); + static QString bookmarkToString(const Bookmark *b); + void saveBookmarks(); + + QMap> m_bookmarksMap; + + QList m_bookmarksList; + QItemSelectionModel *m_selectionModel; +}; + +class BookmarkViewFactory : public Core::INavigationWidgetFactory +{ +public: + BookmarkViewFactory(BookmarkManager *bm); + +private: + Core::NavigationView createWidget() override; + + BookmarkManager *m_manager; +}; + +} // Bookmarks::Internal diff --git a/src/plugins/texteditor/texteditor.qbs b/src/plugins/texteditor/texteditor.qbs index 76644a13570..a041fbcaa66 100644 --- a/src/plugins/texteditor/texteditor.qbs +++ b/src/plugins/texteditor/texteditor.qbs @@ -31,6 +31,12 @@ QtcPlugin { "behaviorsettingswidget.cpp", "behaviorsettingswidget.h", "blockrange.h", + "bookmark.cpp", + "bookmark.h", + "bookmarkfilter.cpp", + "bookmarkfilter.h", + "bookmarkmanager.cpp", + "bookmarkmanager.h", "circularclipboard.cpp", "circularclipboard.h", "circularclipboardassist.cpp", diff --git a/src/plugins/texteditor/texteditorconstants.h b/src/plugins/texteditor/texteditorconstants.h index 9da6b86eb37..03433fd0e8c 100644 --- a/src/plugins/texteditor/texteditorconstants.h +++ b/src/plugins/texteditor/texteditorconstants.h @@ -236,6 +236,9 @@ const char TEXT_SNIPPET_GROUP_ID[] = "Text"; const char GLOBAL_SETTINGS_ID[] = "Global"; const char GENERIC_PROPOSAL_ID[] = "TextEditor.GenericProposalId"; +const char BOOKMARKS_PREV_ACTION[] = "Bookmarks.Previous"; +const char BOOKMARKS_NEXT_ACTION[] = "Bookmarks.Next"; + /** * Delay before tooltip will be shown near completion assistant proposal */ diff --git a/src/plugins/texteditor/texteditorplugin.cpp b/src/plugins/texteditor/texteditorplugin.cpp index d0df98fb8af..f8de8503937 100644 --- a/src/plugins/texteditor/texteditorplugin.cpp +++ b/src/plugins/texteditor/texteditorplugin.cpp @@ -3,7 +3,8 @@ #include "texteditorplugin.h" -#include "commentssettings.h" +#include "bookmarkfilter.h" +#include "bookmarkmanager.h" #include "findincurrentfile.h" #include "findinfiles.h" #include "findinopenfiles.h" @@ -18,7 +19,9 @@ #include "snippets/snippetprovider.h" #include "tabsettings.h" #include "textdocument.h" +#include "textdocument.h" #include "texteditor.h" +#include "texteditorconstants.h" #include "texteditorsettings.h" #include "texteditortr.h" @@ -30,26 +33,28 @@ #include #include #include +#include #include +#include +#include #include #include #include #include -#include - #include #include #include +#include #include using namespace Core; using namespace Utils; +using namespace TextEditor::Constants; -namespace TextEditor { -namespace Internal { +namespace TextEditor::Internal { const char kCurrentDocumentSelection[] = "CurrentDocument:Selection"; const char kCurrentDocumentRow[] = "CurrentDocument:Row"; @@ -62,13 +67,37 @@ const char kCurrentDocumentWordUnderCursor[] = "CurrentDocument:WordUnderCursor" class TextEditorPluginPrivate : public QObject { public: + TextEditorPluginPrivate(); + + void updateActions(bool enableToggle, int stateMask); + void editorOpened(Core::IEditor *editor); + void editorAboutToClose(Core::IEditor *editor); + + void requestContextMenu(TextEditorWidget *widget, int lineNumber, QMenu *menu); + void extensionsInitialized(); - void updateSearchResultsFont(const TextEditor::FontSettings &); - void updateSearchResultsTabWidth(const TextEditor::TabSettings &tabSettings); + void updateSearchResultsFont(const FontSettings &); + void updateSearchResultsTabWidth(const TabSettings &tabSettings); void updateCurrentSelection(const QString &text); void createStandardContextMenu(); + BookmarkManager m_bookmarkManager; + BookmarkFilter m_bookmarkFilter{&m_bookmarkManager}; + BookmarkViewFactory m_bookmarkViewFactory{&m_bookmarkManager}; + + QAction m_toggleAction{Tr::tr("Toggle Bookmark")}; + QAction m_editAction{Tr::tr("Edit Bookmark")}; + QAction m_prevAction{Tr::tr("Previous Bookmark")}; + QAction m_nextAction{Tr::tr("Next Bookmark")}; + QAction m_docPrevAction{Tr::tr("Previous Bookmark in Document")}; + QAction m_docNextAction{Tr::tr("Next Bookmark in Document")}; + QAction m_editBookmarkAction{Tr::tr("Edit Bookmark")}; + QAction m_bookmarkMarginAction{Tr::tr("Toggle Bookmark")}; + + int m_marginActionLineNumber = 0; + FilePath m_marginActionFileName; + TextEditorSettings settings; LineNumberFilter lineNumberFilter; // Goto line functionality for quick open OutlineFactory outlineFactory; @@ -82,6 +111,154 @@ public: JsonEditorFactory jsonEditorFactory; }; +TextEditorPluginPrivate::TextEditorPluginPrivate() +{ + ActionContainer *mtools = ActionManager::actionContainer(Core::Constants::M_TOOLS); + ActionContainer *touchBar = ActionManager::actionContainer(Core::Constants::TOUCH_BAR); + ActionContainer *mbm = ActionManager::createMenu(Id("Bookmarks.Menu")); + + mbm->menu()->setTitle(Tr::tr("&Bookmarks")); + mtools->addMenu(mbm); + + const Context editorManagerContext(Core::Constants::C_EDITORMANAGER); + + // Toggle + Command *cmd = ActionManager::registerAction(&m_toggleAction, "Bookmarks.Toggle", + editorManagerContext); + cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? Tr::tr("Meta+M") : Tr::tr("Ctrl+M"))); + cmd->setTouchBarIcon(Icons::MACOS_TOUCHBAR_BOOKMARK.icon()); + mbm->addAction(cmd); + touchBar->addAction(cmd, Core::Constants::G_TOUCHBAR_EDITOR); + + cmd = ActionManager::registerAction(&m_editAction, "Bookmarks.Edit", editorManagerContext); + cmd->setDefaultKeySequence( + QKeySequence(useMacShortcuts ? Tr::tr("Meta+Shift+M") : Tr::tr("Ctrl+Shift+M"))); + mbm->addAction(cmd); + + mbm->addSeparator(); + + // Previous + m_prevAction.setIcon(Icons::PREV_TOOLBAR.icon()); + m_prevAction.setIconVisibleInMenu(false); + cmd = ActionManager::registerAction(&m_prevAction, BOOKMARKS_PREV_ACTION, editorManagerContext); + cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? Tr::tr("Meta+,") + : Tr::tr("Ctrl+,"))); + mbm->addAction(cmd); + + // Next + m_nextAction.setIcon(Icons::NEXT_TOOLBAR.icon()); + m_nextAction.setIconVisibleInMenu(false); + cmd = ActionManager::registerAction(&m_nextAction, BOOKMARKS_NEXT_ACTION, editorManagerContext); + cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? Tr::tr("Meta+.") + : Tr::tr("Ctrl+."))); + mbm->addAction(cmd); + + mbm->addSeparator(); + + // Previous Doc + cmd = ActionManager::registerAction(&m_docPrevAction, "Bookmarks.PreviousDocument", + editorManagerContext); + mbm->addAction(cmd); + + // Next Doc + cmd = ActionManager::registerAction(&m_docNextAction, "Bookmarks.NextDocument", + editorManagerContext); + mbm->addAction(cmd); + + connect(&m_toggleAction, &QAction::triggered, this, [this] { + IEditor *editor = EditorManager::currentEditor(); + auto widget = TextEditorWidget::fromEditor(editor); + if (widget && editor && !editor->document()->isTemporary()) + m_bookmarkManager.toggleBookmark(editor->document()->filePath(), editor->currentLine()); + }); + + connect(&m_editAction, &QAction::triggered, this, [this] { + IEditor *editor = EditorManager::currentEditor(); + auto widget = TextEditorWidget::fromEditor(editor); + if (widget && editor && !editor->document()->isTemporary()) { + const FilePath filePath = editor->document()->filePath(); + const int line = editor->currentLine(); + if (!m_bookmarkManager.hasBookmarkInPosition(filePath, line)) + m_bookmarkManager.toggleBookmark(filePath, line); + m_bookmarkManager.editByFileAndLine(filePath, line); + } + }); + + connect(&m_prevAction, &QAction::triggered, &m_bookmarkManager, &BookmarkManager::prev); + connect(&m_nextAction, &QAction::triggered, &m_bookmarkManager, &BookmarkManager::next); + connect(&m_docPrevAction, &QAction::triggered, + &m_bookmarkManager, &BookmarkManager::prevInDocument); + connect(&m_docNextAction, &QAction::triggered, + &m_bookmarkManager, &BookmarkManager::nextInDocument); + + connect(&m_editBookmarkAction, &QAction::triggered, this, [this] { + m_bookmarkManager.editByFileAndLine(m_marginActionFileName, m_marginActionLineNumber); + }); + + connect(&m_bookmarkManager, &BookmarkManager::updateActions, + this, &TextEditorPluginPrivate::updateActions); + updateActions(false, m_bookmarkManager.state()); + + connect(&m_bookmarkMarginAction, &QAction::triggered, this, [this] { + m_bookmarkManager.toggleBookmark(m_marginActionFileName, m_marginActionLineNumber); + }); + + // EditorManager + connect(EditorManager::instance(), &EditorManager::editorAboutToClose, + this, &TextEditorPluginPrivate::editorAboutToClose); + connect(EditorManager::instance(), &EditorManager::editorOpened, + this, &TextEditorPluginPrivate::editorOpened); +} + +void TextEditorPluginPrivate::updateActions(bool enableToggle, int state) +{ + const bool hasbm = state >= BookmarkManager::HasBookMarks; + const bool hasdocbm = state == BookmarkManager::HasBookmarksInDocument; + + m_toggleAction.setEnabled(enableToggle); + m_editAction.setEnabled(enableToggle); + m_prevAction.setEnabled(hasbm); + m_nextAction.setEnabled(hasbm); + m_docPrevAction.setEnabled(hasdocbm); + m_docNextAction.setEnabled(hasdocbm); +} + +void TextEditorPluginPrivate::editorOpened(IEditor *editor) +{ + if (auto widget = TextEditorWidget::fromEditor(editor)) { + connect(widget, &TextEditorWidget::markRequested, + this, [this, editor](TextEditorWidget *, int line, TextMarkRequestKind kind) { + if (kind == BookmarkRequest && !editor->document()->isTemporary()) + m_bookmarkManager.toggleBookmark(editor->document()->filePath(), line); + }); + + connect(widget, &TextEditorWidget::markContextMenuRequested, + this, &TextEditorPluginPrivate::requestContextMenu); + } +} + +void TextEditorPluginPrivate::editorAboutToClose(IEditor *editor) +{ + if (auto widget = TextEditorWidget::fromEditor(editor)) { + disconnect(widget, &TextEditorWidget::markContextMenuRequested, + this, &TextEditorPluginPrivate::requestContextMenu); + } +} + +void TextEditorPluginPrivate::requestContextMenu(TextEditorWidget *widget, + int lineNumber, QMenu *menu) +{ + if (widget->textDocument()->isTemporary()) + return; + + m_marginActionLineNumber = lineNumber; + m_marginActionFileName = widget->textDocument()->filePath(); + + menu->addAction(&m_bookmarkMarginAction); + if (m_bookmarkManager.hasBookmarkInPosition(m_marginActionFileName, m_marginActionLineNumber)) + menu->addAction(&m_editBookmarkAction); +} + static TextEditorPlugin *m_instance = nullptr; TextEditorPlugin::TextEditorPlugin() @@ -117,9 +294,9 @@ void TextEditorPlugin::initialize() editor->editorWidget()->invokeAssist(Completion); }); connect(command, &Command::keySequenceChanged, [command] { - Utils::FancyLineEdit::setCompletionShortcut(command->keySequence()); + FancyLineEdit::setCompletionShortcut(command->keySequence()); }); - Utils::FancyLineEdit::setCompletionShortcut(command->keySequence()); + FancyLineEdit::setCompletionShortcut(command->keySequence()); // Add shortcut for invoking function hint completion QAction *functionHintAction = new QAction(Tr::tr("Display Function Hint"), this); @@ -167,7 +344,7 @@ void TextEditorPluginPrivate::extensionsInitialized() &FolderNavigationWidgetFactory::aboutToShowContextMenu, this, [](QMenu *menu, const FilePath &filePath, bool isDir) { if (!isDir && Core::DiffService::instance()) { - menu->addAction(TextEditor::TextDocument::createDiffAgainstCurrentFileAction( + menu->addAction(TextDocument::createDiffAgainstCurrentFileAction( menu, [filePath] { return filePath; })); } }); @@ -192,7 +369,7 @@ void TextEditorPlugin::extensionsInitialized() { d->extensionsInitialized(); - Utils::MacroExpander *expander = Utils::globalMacroExpander(); + MacroExpander *expander = Utils::globalMacroExpander(); expander->registerVariable(kCurrentDocumentSelection, Tr::tr("Selected text within the current document."), @@ -341,5 +518,4 @@ void TextEditorPluginPrivate::createStandardContextMenu() add(Constants::SWITCH_UTF8BOM, Constants::G_BOM); } -} // namespace Internal -} // namespace TextEditor +} // namespace TextEditor::Internal -- cgit v1.2.3