#include "gitstatusmodel.h" #include #include GitStatusModel::GitStatusModel(QObject *parent) : QAbstractItemModel(parent) { m_nodeNames[NodeStaged] = QStringLiteral("Staged"); m_nodeNames[NodeChanged] = QStringLiteral("Changed"); m_nodeNames[NodeUntracked] = QStringLiteral("Untracked"); m_nodeIcons[NodeStaged] = QIcon::fromTheme(QStringLiteral("vcs-added")); m_nodeIcons[NodeChanged] = QIcon::fromTheme(QStringLiteral("vcs-locally-modified")); m_nodeIcons[NodeUntracked] = QIcon::fromTheme(QStringLiteral("vcs-added")); } QModelIndex GitStatusModel::index(int row, int column, const QModelIndex &parent) const { if (column != 0) return {}; if (!parent.isValid()) { // Top-level nodes if (row >= 0 && row < NodeCount) return createIndex(row, 0, TopLevelId); return {}; } // Child items — parent's internalId is TopLevelId, parent's row gives the node type if (parent.internalId() == TopLevelId) { int nodeType = parent.row(); if (nodeType >= 0 && nodeType < NodeCount && row >= 0 && row < m_entries[nodeType].size()) return createIndex(row, 0, quintptr(nodeType)); } return {}; } QModelIndex GitStatusModel::parent(const QModelIndex &child) const { if (!child.isValid()) return {}; if (child.internalId() == TopLevelId) return {}; // top-level has no parent // internalId is the node type int nodeType = int(child.internalId()); if (nodeType >= 0 && nodeType < NodeCount) return createIndex(nodeType, 0, TopLevelId); return {}; } int GitStatusModel::rowCount(const QModelIndex &parent) const { if (!parent.isValid()) return NodeCount; if (parent.internalId() == TopLevelId) { int nodeType = parent.row(); if (nodeType >= 0 && nodeType < NodeCount) return m_entries[nodeType].size(); } return 0; } int GitStatusModel::columnCount(const QModelIndex &) const { return 1; } QVariant GitStatusModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return {}; // Top-level nodes if (index.internalId() == TopLevelId) { int nodeType = index.row(); if (nodeType < 0 || nodeType >= NodeCount) return {}; switch (role) { case Qt::DisplayRole: return QStringLiteral("%1 (%2)").arg(m_nodeNames[nodeType]).arg(m_entries[nodeType].size()); case Qt::DecorationRole: return m_nodeIcons[nodeType]; case Qt::FontRole: { QFont f; f.setBold(true); return f; } case NodeTypeRole: return nodeType; default: return {}; } } // File entries int nodeType = int(index.internalId()); if (nodeType < 0 || nodeType >= NodeCount) return {}; int row = index.row(); if (row < 0 || row >= m_entries[nodeType].size()) return {}; const auto &entry = m_entries[nodeType][row]; switch (role) { case Qt::DisplayRole: return entry.file; case Qt::DecorationRole: { // Status char icon QChar sc = (nodeType == NodeStaged) ? entry.indexStatus : entry.workStatus; if (sc == QLatin1Char('M')) return QIcon::fromTheme(QStringLiteral("vcs-locally-modified")); if (sc == QLatin1Char('A') || sc == QLatin1Char('?')) return QIcon::fromTheme(QStringLiteral("vcs-added")); if (sc == QLatin1Char('D')) return QIcon::fromTheme(QStringLiteral("vcs-removed")); if (sc == QLatin1Char('R')) return QIcon::fromTheme(QStringLiteral("vcs-locally-modified")); return QIcon::fromTheme(QStringLiteral("text-plain")); } case Qt::ToolTipRole: { QChar sc = (nodeType == NodeStaged) ? entry.indexStatus : entry.workStatus; return QStringLiteral("[%1] %2").arg(sc).arg(entry.file); } case FilePathRole: return entry.file; case NodeTypeRole: return nodeType; default: return {}; } } Qt::ItemFlags GitStatusModel::flags(const QModelIndex &index) const { if (!index.isValid()) return Qt::NoItemFlags; if (index.internalId() == TopLevelId) return Qt::ItemIsEnabled; // top-level nodes not selectable return Qt::ItemIsEnabled | Qt::ItemIsSelectable; } void GitStatusModel::parseStatus(const QByteArray &raw) { if (raw == m_lastRaw) return; m_lastRaw = raw; beginResetModel(); for (int i = 0; i < NodeCount; ++i) m_entries[i].clear(); const auto lines = raw.split('\n'); for (const auto &line : lines) { if (line.size() < 4) continue; // Format: XY filename // or XY old -> new (for renames) QChar x = QLatin1Char(line.at(0)); QChar y = QLatin1Char(line.at(1)); // line[2] is space QString file = QString::fromUtf8(line.mid(3)); // Handle renames: "R old -> new" int arrowIdx = file.indexOf(QStringLiteral(" -> ")); if (arrowIdx >= 0) file = file.mid(arrowIdx + 4); GitStatusEntry entry{file, x, y}; // Staged: X is not ' ', '?' or '!' if (x != QLatin1Char(' ') && x != QLatin1Char('?') && x != QLatin1Char('!')) { m_entries[NodeStaged].append(entry); } // Changed (working tree): Y is not ' ', '?' or '!' if (y == QLatin1Char('?')) { m_entries[NodeUntracked].append(entry); } else if (y != QLatin1Char(' ') && y != QLatin1Char('!')) { m_entries[NodeChanged].append(entry); } } endResetModel(); }