File indexing completed on 2024-05-12 05:52:07

0001 /*
0002     SPDX-FileCopyrightText: 2018 Tomaz Canabrava <tcanabrava@kde.org>
0003     SPDX-FileCopyrightText: 2021 Waqar Ahmed <waqar.17a@gmail.com>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "katequickopenmodel.h"
0009 
0010 #include "kateapp.h"
0011 #include "katemainwindow.h"
0012 
0013 #include <KTextEditor/Document>
0014 #include <KTextEditor/View>
0015 
0016 #include <QFileInfo>
0017 #include <QIcon>
0018 #include <QMimeDatabase>
0019 
0020 #include <unordered_set>
0021 
0022 KateQuickOpenModel::KateQuickOpenModel(QObject *parent)
0023     : QAbstractTableModel(parent)
0024 {
0025 }
0026 
0027 int KateQuickOpenModel::rowCount(const QModelIndex &parent) const
0028 {
0029     if (parent.isValid()) {
0030         return 0;
0031     }
0032     return (int)m_modelEntries.size();
0033 }
0034 
0035 int KateQuickOpenModel::columnCount(const QModelIndex &parent) const
0036 {
0037     Q_UNUSED(parent);
0038     return 1;
0039 }
0040 
0041 QVariant KateQuickOpenModel::data(const QModelIndex &idx, int role) const
0042 {
0043     if (!idx.isValid()) {
0044         return {};
0045     }
0046 
0047     const ModelEntry &entry = m_modelEntries.at(idx.row());
0048     switch (role) {
0049     case Qt::DisplayRole:
0050     case Role::FileName:
0051         return entry.fileName;
0052     case Role::FilePath: {
0053         // no .remove since that might remove all occurrence in rare cases
0054         const auto &path = entry.filePath;
0055         return path.startsWith(m_projectBase) ? path.mid(m_projectBase.size()) : path;
0056     }
0057     case Qt::FontRole: {
0058         if (entry.document) {
0059             QFont font;
0060             font.setBold(true);
0061             return font;
0062         }
0063         return {};
0064     }
0065     case Qt::DecorationRole:
0066         return QIcon::fromTheme(QMimeDatabase().mimeTypeForFile(entry.fileName, QMimeDatabase::MatchExtension).iconName());
0067     case Qt::UserRole:
0068         return !entry.document ? QUrl::fromLocalFile(entry.filePath) : entry.document->url();
0069     case Role::Score:
0070         return entry.score;
0071     case Role::Document:
0072         return QVariant::fromValue(entry.document);
0073     default:
0074         return {};
0075     }
0076 
0077     return {};
0078 }
0079 
0080 void KateQuickOpenModel::refresh(KateMainWindow *mainWindow)
0081 {
0082     QObject *projectView = mainWindow->pluginView(QStringLiteral("kateprojectplugin"));
0083     const auto sortedViews = mainWindow->viewManager()->views();
0084     const QList<KTextEditor::Document *> openDocs = KateApp::self()->documentManager()->documentList();
0085     const QStringList projectDocs = projectView
0086         ? (m_listMode == CurrentProject ? projectView->property("projectFiles") : projectView->property("allProjectsFiles")).toStringList()
0087         : QStringList();
0088     const QString projectBase = [projectView]() -> QString {
0089         if (!projectView) {
0090             return QString();
0091         }
0092         QString ret;
0093         // open files are always included in the listing, even if list mode == CurrentProject
0094         // those open files may belong to another project than the current one
0095         // so we should always consistently strip the common base
0096         // otherwise it will be confusing and the opened files of anther project
0097         // end up with an unstripped complete file path
0098         ret = projectView->property("allProjectsCommonBaseDir").toString();
0099         if (!ret.endsWith(QLatin1Char('/'))) {
0100             ret.append(QLatin1Char('/'));
0101         }
0102         // avoid strip of a single leading /
0103         if (ret == QStringLiteral("/")) {
0104             ret.clear();
0105         }
0106         return ret;
0107     }();
0108 
0109     m_projectBase = projectBase;
0110 
0111     std::vector<ModelEntry> allDocuments;
0112     allDocuments.reserve(sortedViews.size() + projectDocs.size());
0113 
0114     std::unordered_set<QString> openedDocUrls;
0115     std::unordered_set<KTextEditor::Document *> seenDocuments;
0116     openedDocUrls.reserve(sortedViews.size());
0117 
0118     const auto collectDoc = [&openedDocUrls, &seenDocuments, &allDocuments](KTextEditor::Document *doc) {
0119         // We don't want any duplicates, beside for untitled documents
0120         if (!seenDocuments.insert(doc).second) {
0121             return;
0122         }
0123 
0124         // document with set url => use the url for displaying
0125         if (!doc->url().isEmpty()) {
0126             auto path = doc->url().toString(QUrl::NormalizePathSegments | QUrl::PreferLocalFile);
0127             openedDocUrls.insert(path);
0128             allDocuments.push_back({QFileInfo(path).fileName(), path, doc, -1});
0129             return;
0130         }
0131 
0132         // untitled document
0133         allDocuments.push_back({doc->documentName(), QString(), doc, -1});
0134     };
0135 
0136     for (auto *view : sortedViews) {
0137         collectDoc(view->document());
0138     }
0139 
0140     for (auto *doc : openDocs) {
0141         collectDoc(doc);
0142     }
0143 
0144     for (const auto &filePath : projectDocs) {
0145         // No duplicates
0146         if (openedDocUrls.count(filePath) != 0) {
0147             continue;
0148         }
0149 
0150         // QFileInfo is too expensive just for fileName computation
0151         const int slashIndex = filePath.lastIndexOf(QLatin1Char('/'));
0152         QString fileName = filePath.mid(slashIndex + 1);
0153         allDocuments.push_back({std::move(fileName), filePath, nullptr, -1});
0154     }
0155 
0156     beginResetModel();
0157     m_modelEntries = std::move(allDocuments);
0158     endResetModel();
0159 }
0160 
0161 #include "moc_katequickopenmodel.cpp"