diff --git a/kate/CMakeLists.txt b/kate/CMakeLists.txt --- a/kate/CMakeLists.txt +++ b/kate/CMakeLists.txt @@ -27,7 +27,7 @@ katesavemodifieddialog.cpp katemwmodonhddialog.cpp katecolorschemechooser.cpp - + katequickopenmodel.cpp katetabbutton.cpp katetabbar.cpp diff --git a/kate/katequickopen.h b/kate/katequickopen.h --- a/kate/katequickopen.h +++ b/kate/katequickopen.h @@ -29,6 +29,7 @@ class QStandardItemModel; class QSortFilterProxyModel; class QTreeView; +class KateQuickOpenModel; class KateQuickOpen : public QWidget { @@ -61,7 +62,7 @@ /** * our model we search in */ - QStandardItemModel *m_base_model; + KateQuickOpenModel *m_base_model; /** * filtered model we search in diff --git a/kate/katequickopen.cpp b/kate/katequickopen.cpp --- a/kate/katequickopen.cpp +++ b/kate/katequickopen.cpp @@ -18,14 +18,17 @@ */ #include "katequickopen.h" +#include "katequickopenmodel.h" #include "katemainwindow.h" #include "kateviewmanager.h" #include "kateapp.h" #include #include +#include + #include #include #include @@ -46,10 +49,6 @@ Q_DECLARE_METATYPE(QPointer) -static const int DocumentRole = Qt::UserRole + 1; -static const int UrlRole = Qt::UserRole + 2; -static const int SortFilterRole = Qt::UserRole + 3; - KateQuickOpen::KateQuickOpen(QWidget *parent, KateMainWindow *mainWindow) : QWidget(parent) , m_mainWindow(mainWindow) @@ -69,11 +68,11 @@ layout->addWidget(m_listView, 1); m_listView->setTextElideMode(Qt::ElideLeft); - m_base_model = new QStandardItemModel(0, 2, this); + m_base_model = new KateQuickOpenModel(m_mainWindow, this); m_model = new QSortFilterProxyModel(this); - m_model->setFilterRole(SortFilterRole); - m_model->setSortRole(SortFilterRole); + m_model->setFilterRole(Qt::DisplayRole); + m_model->setSortRole(Qt::DisplayRole); m_model->setFilterCaseSensitivity(Qt::CaseInsensitive); m_model->setSortCaseSensitivity(Qt::CaseInsensitive); @@ -144,161 +143,15 @@ void KateQuickOpen::update() { - /** - * new base mode creation - * remove from proxy model before populating to avoid wasting time - * with repeatedly sorting it - */ - m_base_model->clear(); - m_model->setSourceModel(nullptr); - - /** - * remember local file names to avoid dupes with project files - */ - QSet alreadySeenFiles; - QSet alreadySeenDocs; - - /** - * get views in lru order - */ - const QList sortedViews(m_mainWindow->viewManager()->sortedViews()); - - /** - * now insert them in order - */ - QModelIndex idxToSelect; - int linecount = 0; - foreach (KTextEditor::View *view, sortedViews) { - KTextEditor::Document *doc = view->document(); - - if (alreadySeenDocs.contains(doc)) { - continue; - } - - alreadySeenDocs.insert(doc); - - //QStandardItem *item=new QStandardItem(i18n("%1: %2",doc->documentName(),doc->url().toString())); - QStandardItem *itemName = new QStandardItem(doc->documentName()); - - itemName->setData(qVariantFromValue(QPointer (doc)), DocumentRole); - itemName->setData(QString::fromLatin1("%1: %2").arg(doc->documentName()).arg(doc->url().toString()), SortFilterRole); - itemName->setEditable(false); - QFont font = itemName->font(); - font.setBold(true); - itemName->setFont(font); - - QStandardItem *itemUrl = new QStandardItem(doc->url().toString()); - itemUrl->setEditable(false); - m_base_model->setItem(linecount, 0, itemName); - m_base_model->setItem(linecount, 1, itemUrl); - linecount++; - - if (!doc->url().isEmpty() && doc->url().isLocalFile()) { - alreadySeenFiles.insert(doc->url().toLocalFile()); - } - - // select second document, that is the last used (beside the active one) - if (linecount == 2) { - idxToSelect = itemName->index(); - } - } - - /** - * get all open documents - */ - QList docs = KateApp::self()->documentManager()->documentList(); - foreach(KTextEditor::Document * doc, docs) { - /** - * skip docs already open - */ - if (alreadySeenDocs.contains(doc)) { - continue; - } - - //QStandardItem *item=new QStandardItem(i18n("%1: %2",doc->documentName(),doc->url().toString())); - QStandardItem *itemName = new QStandardItem(doc->documentName()); - - itemName->setData(qVariantFromValue(QPointer (doc)), DocumentRole); - itemName->setData(QString::fromLatin1("%1: %2").arg(doc->documentName()).arg(doc->url().toString()), SortFilterRole); - itemName->setEditable(false); - QFont font = itemName->font(); - font.setBold(true); - itemName->setFont(font); - - QStandardItem *itemUrl = new QStandardItem(doc->url().toString()); - itemUrl->setEditable(false); - m_base_model->setItem(linecount, 0, itemName); - m_base_model->setItem(linecount, 1, itemUrl); - linecount++; - - if (!doc->url().isEmpty() && doc->url().isLocalFile()) { - alreadySeenFiles.insert(doc->url().toLocalFile()); - } - } - - /** - * insert all project files, if any project around - */ - if (QObject *projectView = m_mainWindow->pluginView(QStringLiteral("kateprojectplugin"))) { - QStringList projectFiles = projectView->property("projectFiles").toStringList(); - foreach(const QString & file, projectFiles) { - /** - * skip files already open - */ - if (alreadySeenFiles.contains(file)) { - continue; - } - - QFileInfo fi(file); - QStandardItem *itemName = new QStandardItem(fi.fileName()); - - itemName->setData(qVariantFromValue(QUrl::fromLocalFile(file)), UrlRole); - itemName->setData(QString::fromLatin1("%1: %2").arg(fi.fileName()).arg(file), SortFilterRole); - itemName->setEditable(false); - - QStandardItem *itemUrl = new QStandardItem(file); - itemUrl->setEditable(false); - m_base_model->setItem(linecount, 0, itemName); - m_base_model->setItem(linecount, 1, itemUrl); - linecount++; - } - } - - if (idxToSelect.isValid()) { - m_listView->setCurrentIndex(m_model->mapFromSource(idxToSelect)); - } else { - reselectFirst(); - } - - m_model->setSourceModel(m_base_model); - - /** - * adjust view - */ + m_base_model->refresh(); m_listView->resizeColumnToContents(0); } void KateQuickOpen::slotReturnPressed() { - /** - * open document for first element, if possible - * prefer to use the document pointer - */ - // our data is in column 0 (clicking on column 1 results in no data, therefore, create new index) - const QModelIndex index = m_listView->model()->index(m_listView->currentIndex().row(), 0); - KTextEditor::Document *doc = index.data(DocumentRole).value >(); - if (doc) { - m_mainWindow->wrapper()->activateView(doc); - } else { - QUrl url = index.data(UrlRole).value(); - if (!url.isEmpty()) { - m_mainWindow->wrapper()->openUrl(url); - } - } - - /** - * in any case, switch back to view manager - */ + const auto index = m_listView->model()->index(m_listView->currentIndex().row(), KateQuickOpenModel::Columns::FilePath); + auto url = QUrl(index.data(Qt::DisplayRole).toString()); + m_mainWindow->wrapper()->openUrl(url); m_mainWindow->slotWindowActivated(); m_inputLine->clear(); } diff --git a/kate/katequickopenmodel.h b/kate/katequickopenmodel.h new file mode 100644 --- /dev/null +++ b/kate/katequickopenmodel.h @@ -0,0 +1,32 @@ +#ifndef KATEQUICKOPENMODEL_H +#define KATEQUICKOPENMODEL_H + +#include +#include +#include +#include + +#include "katemainwindow.h" + +class KateQuickOpenModel : public QAbstractTableModel { + Q_OBJECT +public: + enum Columns : int { FileName, FilePath, Bold }; + explicit KateQuickOpenModel(KateMainWindow *mainWindow, QObject *parent=nullptr); + int rowCount(const QModelIndex& parent) const override; + int columnCount(const QModelIndex& parent) const override; + QVariant data(const QModelIndex& idx, int role) const override; + void refresh(); + +private: + QVector> m_modelEntries; + + /* TODO: don't rely in a pointer to the main window. + * this is bad enginering, but current code is too tigth + * on this and it's hard to untangle without breaking existing + * code. + */ + KateMainWindow *m_mainWindow; +}; + +#endif diff --git a/kate/katequickopenmodel.cpp b/kate/katequickopenmodel.cpp new file mode 100644 --- /dev/null +++ b/kate/katequickopenmodel.cpp @@ -0,0 +1,114 @@ +#include "katequickopenmodel.h" + +#include "katemainwindow.h" +#include "kateviewmanager.h" +#include "kateapp.h" + +#include +#include + +KateQuickOpenModel::KateQuickOpenModel(KateMainWindow *mainWindow, QObject *parent) : + QAbstractTableModel (parent), m_mainWindow(mainWindow) +{ +} + +int KateQuickOpenModel::rowCount(const QModelIndex& parent) const +{ + if (parent.isValid()) { + return 0; + } + return m_modelEntries.size(); +} + +int KateQuickOpenModel::columnCount(const QModelIndex& parent) const +{ + Q_UNUSED(parent); + return 2; +} + +QVariant KateQuickOpenModel::data(const QModelIndex& idx, int role) const +{ + if(! idx.isValid()) { + return {}; + } + + if (role != Qt::DisplayRole && role != Qt::FontRole) { + return {}; + } + + if (role == Qt::DisplayRole) { + switch(idx.column()) { + case Columns::FileName: return std::get(m_modelEntries.at(idx.row())); + case Columns::FilePath: return std::get(m_modelEntries.at(idx.row())); + } + } else if (role == Qt::FontRole) { + if (std::get(m_modelEntries.at(idx.row()))) { + QFont font; + font.setBold(true); + return font; + } + } + + return {}; +} + +void KateQuickOpenModel::refresh() +{ + QObject *projectView = m_mainWindow->pluginView(QStringLiteral("kateprojectplugin")); + const QList sortedViews = m_mainWindow->viewManager()->sortedViews(); + const QList openDocs = KateApp::self()->documentManager()->documentList(); + const QStringList projectDocs = projectView ? projectView->property("projectFiles").toStringList() : QStringList(); + + using DocumentTuple = std::tuple; + + QVector allDocuments; + allDocuments.resize(sortedViews.size() + openDocs.size() + projectDocs.size()); + + for (auto *view : qAsConst(sortedViews)) { + auto doc = view->document(); + allDocuments.push_back({ doc->documentName(), doc->url().toString(QUrl::NormalizePathSegments), false }); + } + + QStringList openedUrls; + openedUrls.reserve(openDocs.size()); + for (auto *doc : qAsConst(openDocs)) { + const auto normalizedUrl = doc->url().toString(QUrl::NormalizePathSegments); + allDocuments.push_back({ doc->documentName(), normalizedUrl, false }); + openedUrls.push_back(normalizedUrl); + } + + for (const auto& file : qAsConst(projectDocs)) { + QFileInfo fi(file); + allDocuments.push_back({ fi.fileName(), QUrl::fromLocalFile(file).toString(QUrl::NormalizePathSegments), false }); + } + + /** Sort the arrays via Url. */ + std::sort(std::begin(allDocuments), std::end(allDocuments), + [](const DocumentTuple& a, const DocumentTuple& b) { + return std::get(a) < std::get(b); + }); + + /** remove Duplicates. */ + allDocuments.erase( + std::unique(allDocuments.begin(), allDocuments.end(), + [](const DocumentTuple& a, const DocumentTuple& b) { + return std::get(a) == std::get(b); + }), + std::end(allDocuments)); + + for(auto& doc : allDocuments) { + if (Q_UNLIKELY(openedUrls.indexOf(std::get(doc)) != -1)) { + std::get(doc) = true; + } + } + + /** sort the arrays via boldness (open or not */ + std::sort(std::begin(allDocuments), std::end(allDocuments), + [](const DocumentTuple& a, const DocumentTuple& b) { + return std::get(a) > std::get(b); + }); + + beginResetModel(); + m_modelEntries = allDocuments; + endResetModel(); +}