diff --git a/kate/katequickopen.cpp b/kate/katequickopen.cpp index aa639de8b..8209cd122 100644 --- a/kate/katequickopen.cpp +++ b/kate/katequickopen.cpp @@ -1,179 +1,183 @@ /* This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --- Copyright (C) 2007,2009 Joseph Wenninger */ #include "katequickopen.h" #include "katequickopenmodel.h" #include "katemainwindow.h" #include "kateviewmanager.h" #include "kateapp.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include Q_DECLARE_METATYPE(QPointer) KateQuickOpen::KateQuickOpen(QWidget *parent, KateMainWindow *mainWindow) : QWidget(parent) , m_mainWindow(mainWindow) { QVBoxLayout *layout = new QVBoxLayout(); layout->setSpacing(0); layout->setContentsMargins(0, 0, 0, 0); setLayout(layout); m_inputLine = new KLineEdit(); setFocusProxy(m_inputLine); m_inputLine->setPlaceholderText(i18n("Quick Open Search")); layout->addWidget(m_inputLine); m_listView = new QTreeView(); layout->addWidget(m_listView, 1); m_listView->setTextElideMode(Qt::ElideLeft); m_base_model = new KateQuickOpenModel(m_mainWindow, this); m_model = new QSortFilterProxyModel(this); m_model->setFilterRole(Qt::DisplayRole); m_model->setSortRole(Qt::DisplayRole); m_model->setFilterCaseSensitivity(Qt::CaseInsensitive); m_model->setSortCaseSensitivity(Qt::CaseInsensitive); m_model->setFilterKeyColumn(0); connect(m_inputLine, &KLineEdit::textChanged, m_model, &QSortFilterProxyModel::setFilterWildcard); connect(m_inputLine, &KLineEdit::returnPressed, this, &KateQuickOpen::slotReturnPressed); connect(m_model, &QSortFilterProxyModel::rowsInserted, this, &KateQuickOpen::reselectFirst); connect(m_model, &QSortFilterProxyModel::rowsRemoved, this, &KateQuickOpen::reselectFirst); connect(m_listView, &QTreeView::activated, this, &KateQuickOpen::slotReturnPressed); m_listView->setModel(m_model); m_model->setSourceModel(m_base_model); m_inputLine->installEventFilter(this); m_listView->installEventFilter(this); m_listView->setHeaderHidden(true); m_listView->setRootIsDecorated(false); } bool KateQuickOpen::eventFilter(QObject *obj, QEvent *event) { if (event->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast(event); if (obj == m_inputLine) { const bool forward2list = (keyEvent->key() == Qt::Key_Up) || (keyEvent->key() == Qt::Key_Down) || (keyEvent->key() == Qt::Key_PageUp) || (keyEvent->key() == Qt::Key_PageDown); if (forward2list) { QCoreApplication::sendEvent(m_listView, event); return true; } if (keyEvent->key() == Qt::Key_Escape) { m_mainWindow->slotWindowActivated(); m_inputLine->clear(); return true; } } else { const bool forward2input = (keyEvent->key() != Qt::Key_Up) && (keyEvent->key() != Qt::Key_Down) && (keyEvent->key() != Qt::Key_PageUp) && (keyEvent->key() != Qt::Key_PageDown) && (keyEvent->key() != Qt::Key_Tab) && (keyEvent->key() != Qt::Key_Backtab); if (forward2input) { QCoreApplication::sendEvent(m_inputLine, event); return true; } } } // hide on focus out, if neither input field nor list have focus! else if (event->type() == QEvent::FocusOut && !(m_inputLine->hasFocus() || m_listView->hasFocus())) { m_mainWindow->slotWindowActivated(); m_inputLine->clear(); return true; } return QWidget::eventFilter(obj, event); } void KateQuickOpen::reselectFirst() { - QModelIndex index = m_model->index(0, 0); + int first = 0; + if (m_mainWindow->viewManager()->sortedViews().size() > 1) + first = 1; + + QModelIndex index = m_model->index(first, 0); m_listView->setCurrentIndex(index); } void KateQuickOpen::update() { m_base_model->refresh(); m_listView->resizeColumnToContents(0); // If we have a very long file name we restrict the size of the first column // to take at most half of the space. Otherwise it would look odd. int colw0 = m_listView->header()->sectionSize(0); // file name int colw1 = m_listView->header()->sectionSize(1); // file path if (colw0 > colw1) { m_listView->setColumnWidth(0, (colw0 + colw1) / 2); } reselectFirst(); } void KateQuickOpen::slotReturnPressed() { const auto index = m_listView->model()->index(m_listView->currentIndex().row(), KateQuickOpenModel::Columns::FilePath); auto url = index.data(Qt::UserRole).toUrl(); m_mainWindow->wrapper()->openUrl(url); m_mainWindow->slotWindowActivated(); m_inputLine->clear(); } void KateQuickOpen::setMatchMode(int mode) { m_model->setFilterKeyColumn(mode); } int KateQuickOpen::matchMode() { return m_model->filterKeyColumn(); } diff --git a/kate/katequickopenmodel.cpp b/kate/katequickopenmodel.cpp index 6a596090f..4dd29bc38 100644 --- a/kate/katequickopenmodel.cpp +++ b/kate/katequickopenmodel.cpp @@ -1,138 +1,135 @@ /* This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --- Copyright (C) 2018 Tomaz Canabrava */ #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 && role != Qt::UserRole) { return {}; } auto entry = m_modelEntries.at(idx.row()); if (role == Qt::DisplayRole) { switch(idx.column()) { case Columns::FileName: return entry.fileName; case Columns::FilePath: return entry.filePath; } } else if (role == Qt::FontRole) { if (entry.bold) { QFont font; font.setBold(true); return font; } } else if (role == Qt::UserRole) { return entry.url; } 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(); QVector allDocuments; - allDocuments.resize(sortedViews.size() + openDocs.size() + projectDocs.size()); + allDocuments.reserve(sortedViews.size() + openDocs.size() + projectDocs.size()); + size_t sort_id = (size_t)-1; for (auto *view : qAsConst(sortedViews)) { auto doc = view->document(); - allDocuments.push_back({ doc->url(), doc->documentName(), doc->url().toDisplayString(QUrl::NormalizePathSegments | QUrl::PreferLocalFile), false }); + allDocuments.push_back({ doc->url(), doc->documentName(), doc->url().toDisplayString(QUrl::NormalizePathSegments | QUrl::PreferLocalFile), true, sort_id --}); } - QStringList openedUrls; - openedUrls.reserve(openDocs.size()); for (auto *doc : qAsConst(openDocs)) { const auto normalizedUrl = doc->url().toString(QUrl::NormalizePathSegments | QUrl::PreferLocalFile); - allDocuments.push_back({ doc->url(), doc->documentName(), normalizedUrl, false }); - openedUrls.push_back(normalizedUrl); + allDocuments.push_back({ doc->url(), doc->documentName(), normalizedUrl, true, 0 }); } for (const auto& file : qAsConst(projectDocs)) { QFileInfo fi(file); const auto localFile = QUrl::fromLocalFile(fi.absoluteFilePath()); allDocuments.push_back({ localFile, fi.fileName(), - localFile.toString(QUrl::NormalizePathSegments | QUrl::PreferLocalFile), false }); + localFile.toString(QUrl::NormalizePathSegments | QUrl::PreferLocalFile), false, 0 }); } /** Sort the arrays by filePath. */ - std::sort(std::begin(allDocuments), std::end(allDocuments), + std::stable_sort(std::begin(allDocuments), std::end(allDocuments), [](const ModelEntry& a, const ModelEntry& b) { return a.filePath < b.filePath; }); - /** remove Duplicates. */ + /** remove Duplicates. + * Note that the stable_sort above guarantees that the items that the + * bold/sort_id fields of the items added first are correctly preserved. + */ allDocuments.erase( std::unique(allDocuments.begin(), allDocuments.end(), [](const ModelEntry& a, const ModelEntry& b) { return a.filePath == b.filePath; }), std::end(allDocuments)); - for (auto& doc : allDocuments) { - if (Q_UNLIKELY(openedUrls.indexOf(doc.filePath) != -1)) { - doc.bold = true; - } - } - /** sort the arrays via boldness (open or not */ - std::sort(std::begin(allDocuments), std::end(allDocuments), + std::stable_sort(std::begin(allDocuments), std::end(allDocuments), [](const ModelEntry& a, const ModelEntry& b) { + if (a.bold == b.bold) + return a.sort_id > b.sort_id; return a.bold > b.bold; }); beginResetModel(); m_modelEntries = allDocuments; endResetModel(); } diff --git a/kate/katequickopenmodel.h b/kate/katequickopenmodel.h index 709918c5f..7d9959a08 100644 --- a/kate/katequickopenmodel.h +++ b/kate/katequickopenmodel.h @@ -1,58 +1,59 @@ /* This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --- Copyright (C) 2018 Tomaz Canabrava */ #ifndef KATEQUICKOPENMODEL_H #define KATEQUICKOPENMODEL_H #include #include #include #include #include "katemainwindow.h" struct ModelEntry { QUrl url; // used for actually opening a selected file (local or remote) QString fileName; // display string for left column QString filePath; // display string for right column bool bold; // format line in bold text or not + size_t sort_id; }; 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 engineering, but current code is too tight * on this and it's hard to untangle without breaking existing * code. */ KateMainWindow *m_mainWindow; }; #endif