File indexing completed on 2024-04-28 05:49:05

0001 /*
0002     SPDX-FileCopyrightText: 2021 Waqar Ahmed <waqar.17a@gmail.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 #include "gitstatusmodel.h"
0007 
0008 #include <KColorScheme>
0009 #include <QFileInfo>
0010 #include <QFont>
0011 #include <QIcon>
0012 #include <QMimeDatabase>
0013 
0014 #include <KLocalizedString>
0015 
0016 static constexpr int Staged = 0;
0017 static constexpr int Changed = 1;
0018 static constexpr int Conflict = 2;
0019 static constexpr int Untrack = 3;
0020 static constexpr quintptr Root = 0xFFFFFFFF;
0021 
0022 GitStatusModel::GitStatusModel(QObject *parent)
0023     : QAbstractItemModel(parent)
0024 {
0025     // setup root rows
0026     beginInsertRows(QModelIndex(), 0, 3);
0027     endInsertRows();
0028 }
0029 
0030 QModelIndex GitStatusModel::index(int row, int column, const QModelIndex &parent) const
0031 {
0032     auto rootIndex = Root;
0033     if (parent.isValid()) {
0034         if (parent.internalId() == Root) {
0035             rootIndex = parent.row();
0036         }
0037     }
0038     return createIndex(row, column, rootIndex);
0039 }
0040 
0041 QModelIndex GitStatusModel::parent(const QModelIndex &child) const
0042 {
0043     if (!child.isValid()) {
0044         return QModelIndex();
0045     }
0046 
0047     return createIndex(child.internalId(), 0, Root);
0048 }
0049 
0050 int GitStatusModel::rowCount(const QModelIndex &parent) const
0051 {
0052     if (!parent.isValid()) {
0053         return 4;
0054     }
0055 
0056     if (parent.internalId() == Root) {
0057         if (parent.row() < 0 || parent.row() > 3) {
0058             return 0;
0059         }
0060 
0061         return m_nodes[parent.row()].size();
0062     }
0063     return 0;
0064 }
0065 
0066 int GitStatusModel::columnCount(const QModelIndex &) const
0067 {
0068     return 2;
0069 }
0070 
0071 QVariant GitStatusModel::data(const QModelIndex &index, int role) const
0072 {
0073     if (!index.isValid()) {
0074         return {};
0075     }
0076 
0077     const int row = index.row();
0078 
0079     if (index.internalId() == Root) {
0080         if (role == Qt::DisplayRole) {
0081             if (index.column() == 1) {
0082                 return QString::number(m_nodes[row].count());
0083             } else {
0084                 if (row == Staged) {
0085                     return i18n("Staged");
0086                 } else if (row == Untrack) {
0087                     return i18n("Untracked");
0088                 } else if (row == Conflict) {
0089                     return i18n("Conflict");
0090                 } else if (row == Changed) {
0091                     return i18n("Modified");
0092                 } else {
0093                     Q_UNREACHABLE();
0094                 }
0095             }
0096         } else if (role == Qt::FontRole) {
0097             QFont bold;
0098             bold.setBold(true);
0099             return bold;
0100         } else if (role == Role::TreeItemType) {
0101             return NodeStage + row;
0102         } else if (role == Qt::TextAlignmentRole) {
0103             if (index.column() == 0) {
0104                 return (int)(Qt::AlignLeft | Qt::AlignVCenter);
0105             } else {
0106                 return (int)(Qt::AlignRight | Qt::AlignVCenter);
0107             }
0108         }
0109     } else {
0110         int rootIndex = index.internalId();
0111         if (rootIndex < 0 || rootIndex > 3) {
0112             return QVariant();
0113         }
0114 
0115         if (role == Qt::DisplayRole) {
0116             if (index.column() == 0) {
0117                 auto fileStr = QString::fromUtf8(m_nodes[rootIndex].at(row).file);
0118                 QFileInfo fi(fileStr);
0119                 const auto filename = fi.fileName();
0120                 if (filename.isEmpty()) {
0121                     return m_nodes[rootIndex].at(row).file;
0122                 }
0123                 if (rootIndex < Untrack && m_nonUniqueFileNames.contains(filename)) {
0124                     const auto path = fi.path();
0125                     const auto i = path.lastIndexOf(QLatin1Char('/'));
0126                     if (i != -1) {
0127                         return path.mid(i + 1).append(QLatin1Char('/')).append(filename);
0128                     }
0129                     return fileStr;
0130                 }
0131                 return filename;
0132             } else {
0133                 int a = m_nodes[rootIndex].at(row).linesAdded;
0134                 int r = m_nodes[rootIndex].at(row).linesRemoved;
0135                 auto add = QString::number(a);
0136                 auto sub = QString::number(r);
0137                 QString statusChar(QLatin1Char(m_nodes[rootIndex].at(row).statusChar));
0138                 QString ret = QStringLiteral("+") + add + QStringLiteral(" -") + sub + QStringLiteral(" ") + statusChar;
0139                 return ret;
0140             }
0141         } else if (role == FileNameRole) {
0142             return m_nodes[rootIndex].at(row).file;
0143         } else if (role == Qt::DecorationRole) {
0144             if (index.column() == 0) {
0145                 const QString file = QString::fromUtf8(m_nodes[rootIndex].at(row).file);
0146                 return QIcon::fromTheme(QMimeDatabase().mimeTypeForFile(file, QMimeDatabase::MatchExtension).iconName());
0147             }
0148         } else if (role == Role::TreeItemType) {
0149             return ItemType::NodeFile;
0150         } else if (role == Qt::ToolTipRole) {
0151             return QString(QString::fromUtf8(m_nodes[rootIndex].at(row).file) + GitUtils::statusString(m_nodes[rootIndex].at(row).status));
0152         } else if (role == Qt::TextAlignmentRole) {
0153             if (index.column() == 0) {
0154                 return (int)(Qt::AlignLeft | Qt::AlignVCenter);
0155             } else {
0156                 return (int)(Qt::AlignRight | Qt::AlignVCenter);
0157             }
0158         } else if (role == Qt::ForegroundRole) {
0159             if (index.column() == 1 && rootIndex > 0) {
0160                 return KColorScheme().foreground(KColorScheme::NegativeText).color();
0161             } else if (index.column() == 1 && rootIndex == 0) {
0162                 return KColorScheme().foreground(KColorScheme::PositiveText).color();
0163             }
0164         } else if (role == Role::GitItemType) {
0165             return (ItemType)rootIndex;
0166         }
0167     }
0168 
0169     return {};
0170 }
0171 
0172 QModelIndex GitStatusModel::indexForFilename(const QString &file)
0173 {
0174     const auto ba = file.toUtf8();
0175     bool checkUntracked = m_nodes[Untrack].size() < 500;
0176     for (int i = 0; i < Untrack + int(checkUntracked); ++i) {
0177         const auto &items = m_nodes[i];
0178         int r = 0;
0179         for (const auto &item : items) {
0180             if (ba.endsWith(item.file)) {
0181                 // match
0182                 return index(r, 0, getModelIndex(static_cast<ItemType>(i)));
0183             }
0184             r++;
0185         }
0186     }
0187     return {};
0188 }
0189 
0190 void GitStatusModel::setStatusItems(GitUtils::GitParsedStatus status)
0191 {
0192     beginResetModel();
0193     m_nodes[Staged] = std::move(status.staged);
0194     m_nodes[Changed] = std::move(status.changed);
0195     m_nodes[Conflict] = std::move(status.unmerge);
0196     m_nodes[Untrack] = std::move(status.untracked);
0197     m_nonUniqueFileNames = std::move(status.nonUniqueFileNames);
0198     endResetModel();
0199 }