File indexing completed on 2024-04-28 05:49:04
0001 /* 0002 SPDX-FileCopyrightText: 2021 Waqar Ahmed <waqar.17a@gmail.com> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "comparebranchesview.h" 0008 #include "diffparams.h" 0009 #include "hostprocess.h" 0010 #include "kateprojectitem.h" 0011 #include "kateprojectpluginview.h" 0012 #include "kateprojectworker.h" 0013 #include "ktexteditor_utils.h" 0014 #include <gitprocess.h> 0015 0016 #include <QDir> 0017 #include <QPainter> 0018 #include <QProcess> 0019 #include <QStyledItemDelegate> 0020 #include <QVBoxLayout> 0021 0022 #include <KColorScheme> 0023 #include <KLocalizedString> 0024 0025 // TODO: this is duplicated in libkateprivate as DiffStyleDelegate 0026 class CompareBranchesDiffStyleDelegate : public QStyledItemDelegate 0027 { 0028 public: 0029 CompareBranchesDiffStyleDelegate(QObject *parent) 0030 : QStyledItemDelegate(parent) 0031 { 0032 } 0033 0034 void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override 0035 { 0036 if (index.data(KateProjectItem::TypeRole).toInt() == KateProjectItem::Directory) { 0037 QStyledItemDelegate::paint(painter, option, index); 0038 return; 0039 } 0040 0041 QStyleOptionViewItem options = option; 0042 initStyleOption(&options, index); 0043 0044 painter->save(); 0045 0046 // paint background 0047 if (option.state & QStyle::State_Selected) { 0048 painter->fillRect(option.rect, option.palette.highlight()); 0049 } else { 0050 painter->fillRect(option.rect, option.palette.base()); 0051 } 0052 0053 int add = index.data(Qt::UserRole + 2).toInt(); 0054 int sub = index.data(Qt::UserRole + 3).toInt(); 0055 QString adds = QString(QStringLiteral("+") + QString::number(add)); 0056 QString subs = QString(QStringLiteral(" -") + QString::number(sub)); 0057 QString file = options.text; 0058 0059 options.text = QString(); // clear old text 0060 options.widget->style()->drawControl(QStyle::CE_ItemViewItem, &options, painter, options.widget); 0061 0062 QRect r = options.rect; 0063 0064 // don't draw over icon 0065 r.setX(r.x() + option.decorationSize.width() + 5); 0066 0067 const QFontMetrics &fm = options.fontMetrics; 0068 0069 // adds width 0070 int aw = fm.horizontalAdvance(adds); 0071 // subs width 0072 int sw = fm.horizontalAdvance(subs); 0073 0074 // subtract this from total width of rect 0075 int totalw = r.width(); 0076 totalw = totalw - (aw + sw); 0077 0078 // get file name, elide if necessary 0079 QString filename = fm.elidedText(file, Qt::ElideRight, totalw); 0080 0081 painter->drawText(r, Qt::AlignVCenter, filename); 0082 0083 KColorScheme c; 0084 const auto red = c.shade(c.foreground(KColorScheme::NegativeText).color(), KColorScheme::MidlightShade, 1); 0085 const auto green = c.shade(c.foreground(KColorScheme::PositiveText).color(), KColorScheme::MidlightShade, 1); 0086 0087 r.setX(r.x() + totalw); 0088 painter->setPen(green); 0089 painter->drawText(r, Qt::AlignVCenter, adds); 0090 0091 painter->setPen(red); 0092 r.setX(r.x() + aw); 0093 painter->drawText(r, Qt::AlignVCenter, subs); 0094 0095 painter->restore(); 0096 } 0097 }; 0098 0099 static void createFileTree(QStandardItem *parent, const QString &basePath, const QList<GitUtils::StatusItem> &files) 0100 { 0101 QDir dir(basePath); 0102 const QString dirPath = dir.path() + QLatin1Char('/'); 0103 QHash<QString, QStandardItem *> dir2Item; 0104 dir2Item[QString()] = parent; 0105 for (const auto &file : qAsConst(files)) { 0106 const QString filePath = QString::fromUtf8(file.file); 0107 /** 0108 * cheap file name computation 0109 * we do this A LOT, QFileInfo is very expensive just for this operation 0110 */ 0111 const int slashIndex = filePath.lastIndexOf(QLatin1Char('/')); 0112 const QString fileName = (slashIndex < 0) ? filePath : filePath.mid(slashIndex + 1); 0113 const QString filePathName = (slashIndex < 0) ? QString() : filePath.left(slashIndex); 0114 const QString fullFilePath = dirPath + filePath; 0115 0116 /** 0117 * construct the item with right directory prefix 0118 * already hang in directories in tree 0119 */ 0120 KateProjectItem *fileItem = new KateProjectItem(KateProjectItem::File, fileName); 0121 fileItem->setData(fullFilePath, Qt::UserRole); 0122 fileItem->setData(file.statusChar, Qt::UserRole + 1); 0123 fileItem->setData(file.linesAdded, Qt::UserRole + 2); 0124 fileItem->setData(file.linesRemoved, Qt::UserRole + 3); 0125 0126 // put in our item to the right directory parent 0127 KateProjectWorker::directoryParent(dir, dir2Item, filePathName)->appendRow(fileItem); 0128 } 0129 } 0130 0131 CompareBranchesView::CompareBranchesView(QWidget *parent, const QString &gitPath, const QString fromB, const QString &toBr, QList<GitUtils::StatusItem> items) 0132 : QWidget(parent) 0133 , m_gitDir(gitPath) 0134 , m_fromBr(fromB) 0135 , m_toBr(toBr) 0136 { 0137 setLayout(new QVBoxLayout); 0138 0139 QStandardItem root; 0140 createFileTree(&root, m_gitDir, items); 0141 0142 m_model.clear(); 0143 m_model.invisibleRootItem()->appendColumn(root.takeColumn(0)); 0144 0145 m_backBtn.setText(i18n("Back")); 0146 m_backBtn.setIcon(QIcon::fromTheme(QStringLiteral("go-previous"))); 0147 connect(&m_backBtn, &QPushButton::clicked, this, &CompareBranchesView::backClicked); 0148 layout()->addWidget(&m_backBtn); 0149 0150 m_tree.setModel(&m_model); 0151 layout()->addWidget(&m_tree); 0152 0153 m_tree.setHeaderHidden(true); 0154 m_tree.setEditTriggers(QTreeView::NoEditTriggers); 0155 m_tree.setItemDelegate(new CompareBranchesDiffStyleDelegate(this)); 0156 m_tree.expandAll(); 0157 0158 connect(&m_tree, &QTreeView::clicked, this, &CompareBranchesView::showDiff); 0159 } 0160 0161 void CompareBranchesView::showDiff(const QModelIndex &idx) 0162 { 0163 auto file = idx.data(Qt::UserRole).toString().remove(m_gitDir + QLatin1Char('/')); 0164 QProcess git; 0165 if (!setupGitProcess(git, m_gitDir, {QStringLiteral("diff"), QStringLiteral("%1...%2").arg(m_fromBr).arg(m_toBr), QStringLiteral("--"), file})) { 0166 return; 0167 } 0168 startHostProcess(git, QProcess::ReadOnly); 0169 0170 if (git.waitForStarted() && git.waitForFinished(-1)) { 0171 if (git.exitStatus() != QProcess::NormalExit || git.exitCode() != 0) { 0172 return; 0173 } 0174 } 0175 0176 DiffParams d; 0177 d.tabTitle = QStringLiteral("Diff %1[%2 .. %3]").arg(Utils::fileNameFromPath(file)).arg(m_fromBr).arg(m_toBr); 0178 d.workingDir = m_gitDir; 0179 d.arguments = git.arguments(); 0180 Utils::showDiff(git.readAllStandardOutput(), d, m_pluginView->mainWindow()); 0181 } 0182 0183 #include "moc_comparebranchesview.cpp"