1#include "CommitHistoryView.h"
2
3#include <CommitHistoryColumns.h>
4#include <CommitHistoryContextMenu.h>
5#include <CommitHistoryModel.h>
6#include <CommitInfo.h>
7#include <GitBase.h>
8#include <GitCache.h>
9#include <GitConfig.h>
10#include <GitQlientSettings.h>
11#include <ShaFilterProxyModel.h>
12
13#include <QDateTime>
14#include <QHeaderView>
15
16#include <QLogger.h>
17using namespace QLogger;
18
19CommitHistoryView::CommitHistoryView(const QSharedPointer<GitCache> &cache, const QSharedPointer<GitBase> &git,
20 const QSharedPointer<GitQlientSettings> &settings,
21 const QSharedPointer<GitServerCache> &gitServerCache, QWidget *parent)
22 : QTreeView(parent)
23 , mCache(cache)
24 , mGit(git)
25 , mSettings(settings)
26 , mGitServerCache(gitServerCache)
27{
28 setEnabled(false);
29 setContextMenuPolicy(Qt::CustomContextMenu);
30 setItemsExpandable(false);
31 setMouseTracking(true);
32 setSelectionMode(QAbstractItemView::ExtendedSelection);
33 setAttribute(Qt::WA_DeleteOnClose);
34
35 header()->setSortIndicatorShown(false);
36 header()->setContextMenuPolicy(Qt::CustomContextMenu);
37 connect(header(), &QHeaderView::customContextMenuRequested, this, &CommitHistoryView::onHeaderContextMenu);
38
39 connect(mCache.get(), &GitCache::signalCacheUpdated, this, &CommitHistoryView::refreshView);
40
41 connect(this, &CommitHistoryView::doubleClicked, this, [this](const QModelIndex &index) {
42 if (mCommitHistoryModel)
43 {
44 const auto sha = mCommitHistoryModel->sha(index.row());
45 emit signalOpenDiff(sha);
46 }
47 });
48}
49
50void CommitHistoryView::setModel(QAbstractItemModel *model)
51{
52 connect(this, &CommitHistoryView::customContextMenuRequested, this, &CommitHistoryView::showContextMenu,
53 Qt::UniqueConnection);
54
55 mCommitHistoryModel = dynamic_cast<CommitHistoryModel *>(model);
56 QTreeView::setModel(model);
57 setupGeometry();
58 connect(this->selectionModel(), &QItemSelectionModel::selectionChanged, this,
59 [this](const QItemSelection &selected, const QItemSelection &) {
60 const auto indexes = selected.indexes();
61 if (!indexes.isEmpty())
62 {
63 scrollTo(indexes.first());
64 emit clicked(indexes.first());
65 }
66 });
67}
68
69void CommitHistoryView::filterBySha(const QStringList &shaList)
70{
71 mIsFiltering = true;
72
73 if (mProxyModel)
74 {
75 mProxyModel->beginResetModel();
76 mProxyModel->setAcceptedSha(shaList);
77 mProxyModel->endResetModel();
78 }
79 else
80 {
81 mProxyModel = new ShaFilterProxyModel(this);
82 mProxyModel->setSourceModel(mCommitHistoryModel);
83 mProxyModel->setAcceptedSha(shaList);
84 setModel(mProxyModel);
85 }
86
87 setupGeometry();
88}
89
90CommitHistoryView::~CommitHistoryView()
91{
92 mSettings->setLocalValue(QString("%1").arg(objectName()), header()->saveState());
93}
94
95void CommitHistoryView::setupGeometry()
96{
97 const auto previousState = mSettings->localValue(QString("%1").arg(objectName()), QByteArray()).toByteArray();
98
99 if (previousState.isEmpty())
100 {
101 const auto hv = header();
102 hv->setMinimumSectionSize(75);
103 hv->resizeSection(static_cast<int>(CommitHistoryColumns::Sha), 75);
104 hv->resizeSection(static_cast<int>(CommitHistoryColumns::Graph), 120);
105 hv->resizeSection(static_cast<int>(CommitHistoryColumns::Author), 160);
106 hv->resizeSection(static_cast<int>(CommitHistoryColumns::Date), 125);
107 hv->resizeSection(static_cast<int>(CommitHistoryColumns::Sha), 75);
108 hv->setSectionResizeMode(static_cast<int>(CommitHistoryColumns::Author), QHeaderView::Fixed);
109 hv->setSectionResizeMode(static_cast<int>(CommitHistoryColumns::Date), QHeaderView::Fixed);
110 hv->setSectionResizeMode(static_cast<int>(CommitHistoryColumns::Sha), QHeaderView::Fixed);
111 hv->setSectionResizeMode(static_cast<int>(CommitHistoryColumns::Log), QHeaderView::Stretch);
112 hv->setStretchLastSection(false);
113
114 hideColumn(static_cast<int>(CommitHistoryColumns::TreeViewIcon));
115 }
116 else
117 {
118 header()->restoreState(previousState);
119 header()->setSectionResizeMode(static_cast<int>(CommitHistoryColumns::Log), QHeaderView::Stretch);
120 }
121}
122
123void CommitHistoryView::currentChanged(const QModelIndex &index, const QModelIndex &)
124{
125 mCurrentSha = model()->index(index.row(), static_cast<int>(CommitHistoryColumns::Sha)).data().toString();
126}
127
128void CommitHistoryView::refreshView()
129{
130 QModelIndex topLeft;
131 QModelIndex bottomRight;
132
133 if (mProxyModel)
134 {
135 topLeft = mProxyModel->index(0, 0);
136 bottomRight = mProxyModel->index(mProxyModel->rowCount() - 1, mProxyModel->columnCount() - 1);
137 mProxyModel->beginResetModel();
138 mProxyModel->endResetModel();
139 }
140 else
141 {
142 topLeft = mCommitHistoryModel->index(0, 0);
143 bottomRight
144 = mCommitHistoryModel->index(mCommitHistoryModel->rowCount() - 1, mCommitHistoryModel->columnCount() - 1);
145 mCommitHistoryModel->onNewRevisions(mCache->commitCount());
146 }
147
148 const auto auxTL = visualRect(topLeft);
149 const auto auxBR = visualRect(bottomRight);
150 viewport()->update(auxTL.x(), auxTL.y(), auxBR.x() + auxBR.width(), auxBR.y() + auxBR.height());
151}
152
153void CommitHistoryView::onHeaderContextMenu(const QPoint &pos)
154{
155 const auto menu = new QMenu(this);
156 const auto total = header()->count();
157
158 for (auto column = 3; column < total; ++column)
159 {
160 const auto columnName = model()->headerData(column, Qt::Horizontal, Qt::DisplayRole).toString();
161 const auto action = menu->addAction(columnName);
162 const auto isHidden = isColumnHidden(column);
163 action->setCheckable(true);
164 action->setChecked(!isHidden);
165 connect(action, &QAction::triggered, this, [column, this, action]() {
166 action->setChecked(!action->isChecked());
167 setColumnHidden(column, !isColumnHidden(column));
168 });
169 }
170
171 menu->exec(header()->mapToGlobal(pos));
172}
173
174void CommitHistoryView::clear()
175{
176 mCommitHistoryModel->clear();
177}
178
179void CommitHistoryView::focusOnCommit(const QString &goToSha)
180{
181 mCurrentSha = goToSha;
182
183 QLog_Info("UI", QString("Setting the focus on the commit {%1}").arg(mCurrentSha));
184
185 auto row = mCache->commitInfo(mCurrentSha).pos;
186
187 if (mIsFiltering)
188 {
189 const auto sourceIndex = mProxyModel->sourceModel()->index(row, 0);
190 row = mProxyModel->mapFromSource(sourceIndex).row();
191 }
192
193 clearSelection();
194
195 const auto index = model()->index(row, 0);
196
197 setCurrentIndex(index);
198 scrollTo(currentIndex());
199}
200
201QModelIndexList CommitHistoryView::selectedIndexes() const
202{
203 return QTreeView::selectedIndexes();
204}
205
206void CommitHistoryView::showContextMenu(const QPoint &pos)
207{
208 if (!mIsFiltering)
209 {
210 const auto shas = getSelectedShaList();
211
212 if (!shas.isEmpty())
213 {
214 const auto menu = new CommitHistoryContextMenu(mCache, mGit, mGitServerCache, shas, this);
215 connect(menu, &CommitHistoryContextMenu::fullReload, this, &CommitHistoryView::fullReload);
216 connect(menu, &CommitHistoryContextMenu::referencesReload, this, &CommitHistoryView::referencesReload);
217 connect(menu, &CommitHistoryContextMenu::logReload, this, &CommitHistoryView::logReload);
218 connect(menu, &CommitHistoryContextMenu::signalOpenDiff, this, &CommitHistoryView::signalOpenDiff);
219 connect(menu, &CommitHistoryContextMenu::signalOpenCompareDiff, this,
220 &CommitHistoryView::signalOpenCompareDiff);
221 connect(menu, &CommitHistoryContextMenu::signalAmendCommit, this, &CommitHistoryView::signalAmendCommit);
222 connect(menu, &CommitHistoryContextMenu::signalMergeRequired, this, &CommitHistoryView::signalMergeRequired);
223 connect(menu, &CommitHistoryContextMenu::mergeSqushRequested, this, &CommitHistoryView::mergeSqushRequested);
224 connect(menu, &CommitHistoryContextMenu::signalCherryPickConflict, this,
225 &CommitHistoryView::signalCherryPickConflict);
226 connect(menu, &CommitHistoryContextMenu::signalPullConflict, this, &CommitHistoryView::signalPullConflict);
227 connect(menu, &CommitHistoryContextMenu::showPrDetailedView, this, &CommitHistoryView::showPrDetailedView);
228 menu->exec(viewport()->mapToGlobal(pos));
229 }
230 else
231 QLog_Warning("UI", "SHAs selected belong to different branches. They need to share at least one branch.");
232 }
233}
234
235QStringList CommitHistoryView::getSelectedShaList() const
236{
237 const auto indexes = selectedIndexes();
238
239 if (indexes.count() > 0)
240 {
241 QMap<QDateTime, QString> shas;
242
243 for (auto index : indexes)
244 {
245 const auto sha = mCommitHistoryModel->sha(index.row());
246 const auto dtStr
247 = mCommitHistoryModel->index(index.row(), static_cast<int>(CommitHistoryColumns::Date)).data().toString();
248
249 shas.insert(QDateTime::fromString(dtStr, "dd MMM yyyy hh:mm"), sha);
250 }
251
252 return shas.values();
253 }
254
255 return QStringList();
256}
257