1#include "DiffWidget.h"
2
3#include <CommitInfoPanel.h>
4#include <FileDiffWidget.h>
5#include <FileListWidget.h>
6#include <FullDiffWidget.h>
7#include <GitCache.h>
8#include <GitHistory.h>
9#include <GitQlientSettings.h>
10
11#include <QLogger.h>
12#include <QPinnableTabWidget.h>
13
14#include <QHBoxLayout>
15#include <QMessageBox>
16
17using namespace QLogger;
18
19DiffWidget::DiffWidget(const QSharedPointer<GitBase> git, QSharedPointer<GitCache> cache, QWidget *parent)
20 : QFrame(parent)
21 , mGit(git)
22 , mCache(cache)
23 , mInfoPanelBase(new CommitInfoPanel())
24 , mInfoPanelParent(new CommitInfoPanel())
25 , mCenterStackedWidget(new QPinnableTabWidget())
26 , fileListWidget(new FileListWidget(mGit, cache))
27{
28 setAttribute(Qt::WA_DeleteOnClose);
29
30 mInfoPanelParent->setObjectName("InfoPanel");
31 mInfoPanelParent->setFixedWidth(350);
32
33 mCenterStackedWidget->setCurrentIndex(0);
34 mCenterStackedWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
35 connect(mCenterStackedWidget, &QTabWidget::currentChanged, this, &DiffWidget::changeSelection);
36 connect(mCenterStackedWidget, &QTabWidget::tabCloseRequested, this, &DiffWidget::onTabClosed);
37
38 const auto wipSeparator = new QFrame();
39 wipSeparator->setObjectName("separator");
40
41 const auto infoPanel = new QFrame();
42 infoPanel->setFixedWidth(350);
43 infoPanel->setObjectName("InfoPanel");
44 const auto diffsLayout = new QVBoxLayout(infoPanel);
45 diffsLayout->setContentsMargins(QMargins());
46 diffsLayout->setSpacing(0);
47 diffsLayout->addWidget(mInfoPanelBase);
48 diffsLayout->addWidget(wipSeparator);
49 diffsLayout->addWidget(fileListWidget);
50
51 const auto panelLayout = new QVBoxLayout();
52 panelLayout->setContentsMargins(QMargins());
53 panelLayout->setSpacing(0);
54 panelLayout->addWidget(infoPanel);
55 panelLayout->addStretch();
56 panelLayout->addWidget(mInfoPanelParent);
57
58 const auto layout = new QHBoxLayout();
59 layout->setContentsMargins(QMargins());
60 layout->addLayout(panelLayout);
61 layout->setSpacing(10);
62 layout->addWidget(mCenterStackedWidget);
63
64 setLayout(layout);
65
66 connect(fileListWidget, &FileListWidget::itemDoubleClicked, this, &DiffWidget::onDoubleClick);
67 connect(fileListWidget, &FileListWidget::signalShowFileHistory, this, &DiffWidget::signalShowFileHistory);
68
69 fileListWidget->setVisible(false);
70}
71
72DiffWidget::~DiffWidget()
73{
74 mDiffWidgets.clear();
75 blockSignals(true);
76}
77
78void DiffWidget::reload()
79{
80 if (mCenterStackedWidget->count() > 0)
81 {
82 if (const auto fileDiff = dynamic_cast<FileDiffWidget *>(mCenterStackedWidget->currentWidget()))
83 fileDiff->reload();
84 else if (const auto fullDiff = dynamic_cast<FullDiffWidget *>(mCenterStackedWidget->currentWidget()))
85 fullDiff->reload();
86 }
87}
88
89void DiffWidget::clear() const
90{
91 mCenterStackedWidget->setCurrentIndex(0);
92}
93
94bool DiffWidget::loadFileDiff(const QString &currentSha, const QString &previousSha, const QString &file, bool isCached)
95{
96 const auto id = QString("%1 (%2 \u2194 %3)").arg(file.split("/").last(), currentSha.left(6), previousSha.left(6));
97
98 mCurrentSha = currentSha;
99 mParentSha = previousSha;
100
101 if (!mDiffWidgets.contains(id))
102 {
103 QLog_Info(
104 "UI",
105 QString("Requested diff for file {%1} on between commits {%2} and {%3}").arg(file, currentSha, previousSha));
106
107 const auto fileDiffWidget = new FileDiffWidget(mGit, mCache);
108 const auto fileWithModifications = fileDiffWidget->configure(currentSha, previousSha, file, isCached);
109
110 if (fileWithModifications)
111 {
112 mInfoPanelBase->configure(mCache->commitInfo(currentSha));
113 mInfoPanelParent->configure(mCache->commitInfo(previousSha));
114
115 mDiffWidgets.insert(id, fileDiffWidget);
116
117 const auto index = mCenterStackedWidget->addTab(fileDiffWidget, file.split("/").last());
118 mCenterStackedWidget->setCurrentIndex(index);
119
120 fileListWidget->insertFiles(currentSha, previousSha);
121 fileListWidget->setVisible(true);
122
123 return true;
124 }
125 else
126 {
127 QMessageBox::information(this, tr("No modifications"), tr("There are no content modifications for this file"));
128 delete fileDiffWidget;
129
130 return false;
131 }
132 }
133 else
134 {
135 const auto diffWidget = mDiffWidgets.value(id);
136 const auto diff = dynamic_cast<FileDiffWidget *>(diffWidget);
137 diff->reload();
138
139 mCenterStackedWidget->setCurrentWidget(diff);
140
141 return true;
142 }
143}
144
145bool DiffWidget::loadCommitDiff(const QString &sha, const QString &parentSha)
146{
147 const auto id = QString("Commit diff (%1 \u2194 %2)").arg(sha.left(6), parentSha.left(6));
148
149 mCurrentSha = sha;
150 mParentSha = parentSha;
151
152 if (!mDiffWidgets.contains(id))
153 {
154 QScopedPointer<GitHistory> git(new GitHistory(mGit));
155 const auto ret = git->getCommitDiff(sha, parentSha);
156
157 if (ret.success && !ret.output.isEmpty())
158 {
159 const auto fullDiffWidget = new FullDiffWidget(mGit, mCache);
160 fullDiffWidget->loadDiff(sha, parentSha, ret.output);
161
162 mInfoPanelBase->configure(mCache->commitInfo(sha));
163 mInfoPanelParent->configure(mCache->commitInfo(parentSha));
164
165 mDiffWidgets.insert(id, fullDiffWidget);
166
167 const auto index = mCenterStackedWidget->addTab(fullDiffWidget,
168 QString("(%1 \u2194 %2)").arg(sha.left(6), parentSha.left(6)));
169 mCenterStackedWidget->setCurrentIndex(index);
170
171 fileListWidget->insertFiles(sha, parentSha);
172 fileListWidget->setVisible(true);
173
174 return true;
175 }
176 else
177 QMessageBox::information(this, tr("No diff to show!"),
178 tr("There is no diff to show between commit SHAs {%1} and {%2}").arg(sha, parentSha));
179
180 return false;
181 }
182 else
183 {
184 const auto diffWidget = mDiffWidgets.value(id);
185 const auto diff = dynamic_cast<FullDiffWidget *>(diffWidget);
186 diff->reload();
187 mCenterStackedWidget->setCurrentWidget(diff);
188 }
189
190 return true;
191}
192
193void DiffWidget::changeSelection(int index)
194{
195 const auto widget = qobject_cast<IDiffWidget *>(mCenterStackedWidget->widget(index));
196
197 if (widget)
198 {
199 mInfoPanelBase->configure(mCache->commitInfo(widget->getCurrentSha()));
200 mInfoPanelParent->configure(mCache->commitInfo(widget->getPreviousSha()));
201 }
202 else
203 emit signalDiffEmpty();
204}
205
206void DiffWidget::onTabClosed(int index)
207{
208 const auto widget = qobject_cast<IDiffWidget *>(mCenterStackedWidget->widget(index));
209
210 if (widget)
211 {
212 const auto key = mDiffWidgets.key(widget);
213 mDiffWidgets.remove(key);
214 }
215}
216
217void DiffWidget::onDoubleClick(QListWidgetItem *item)
218{
219 loadFileDiff(mCurrentSha, mParentSha, item->text(), false);
220}
221