1 | #include "Controls.h" |
2 | |
3 | #include <BranchDlg.h> |
4 | #include <GitBase.h> |
5 | #include <GitCache.h> |
6 | #include <GitConfig.h> |
7 | #include <GitQlientSettings.h> |
8 | #include <GitQlientStyles.h> |
9 | #include <GitQlientUpdater.h> |
10 | #include <GitRemote.h> |
11 | #include <GitStashes.h> |
12 | #include <GitTags.h> |
13 | #include <PomodoroButton.h> |
14 | #include <QLogger.h> |
15 | |
16 | #include <QApplication> |
17 | #include <QButtonGroup> |
18 | #include <QHBoxLayout> |
19 | #include <QMenu> |
20 | #include <QMessageBox> |
21 | #include <QProgressBar> |
22 | #include <QPushButton> |
23 | #include <QTimer> |
24 | #include <QToolButton> |
25 | |
26 | using namespace QLogger; |
27 | |
28 | Controls::Controls(const QSharedPointer<GitCache> &cache, const QSharedPointer<GitBase> &git, QWidget *parent) |
29 | : QFrame(parent) |
30 | , mCache(cache) |
31 | , mGit(git) |
32 | , mGitTags(new GitTags(mGit, mCache)) |
33 | , mHistory(new QToolButton()) |
34 | , mDiff(new QToolButton()) |
35 | , mBlame(new QToolButton()) |
36 | , mPullBtn(new QToolButton()) |
37 | , mPullOptions(new QToolButton()) |
38 | , mPushBtn(new QToolButton()) |
39 | , mRefreshBtn(new QToolButton()) |
40 | , mConfigBtn(new QToolButton()) |
41 | , mGitPlatform(new QToolButton()) |
42 | , mBuildSystem(new QToolButton()) |
43 | , mPomodoro(new PomodoroButton(mGit)) |
44 | , mVersionCheck(new QToolButton()) |
45 | , mMergeWarning(new QPushButton(tr("WARNING: There is a merge pending to be committed! Click here to solve it." ))) |
46 | , mUpdater(new GitQlientUpdater(this)) |
47 | , mBtnGroup(new QButtonGroup()) |
48 | { |
49 | setAttribute(Qt::WA_DeleteOnClose); |
50 | |
51 | connect(mUpdater, &GitQlientUpdater::newVersionAvailable, this, [this]() { mVersionCheck->setVisible(true); }); |
52 | |
53 | mHistory->setCheckable(true); |
54 | mHistory->setIcon(QIcon(":/icons/git_orange" )); |
55 | mHistory->setIconSize(QSize(22, 22)); |
56 | mHistory->setToolTip(tr("View" )); |
57 | mHistory->setToolButtonStyle(Qt::ToolButtonIconOnly); |
58 | mBtnGroup->addButton(mHistory, static_cast<int>(ControlsMainViews::History)); |
59 | |
60 | mDiff->setCheckable(true); |
61 | mDiff->setIcon(QIcon(":/icons/diff" )); |
62 | mDiff->setIconSize(QSize(22, 22)); |
63 | mDiff->setToolTip(tr("Diff" )); |
64 | mDiff->setToolButtonStyle(Qt::ToolButtonIconOnly); |
65 | mDiff->setEnabled(false); |
66 | mBtnGroup->addButton(mDiff, static_cast<int>(ControlsMainViews::Diff)); |
67 | |
68 | mBlame->setCheckable(true); |
69 | mBlame->setIcon(QIcon(":/icons/blame" )); |
70 | mBlame->setIconSize(QSize(22, 22)); |
71 | mBlame->setToolTip(tr("Blame" )); |
72 | mBlame->setToolButtonStyle(Qt::ToolButtonIconOnly); |
73 | mBtnGroup->addButton(mBlame, static_cast<int>(ControlsMainViews::Blame)); |
74 | |
75 | const auto = new QMenu(mPullOptions); |
76 | menu->installEventFilter(this); |
77 | |
78 | auto action = menu->addAction(tr("Fetch all" )); |
79 | connect(action, &QAction::triggered, this, &Controls::fetchAll); |
80 | |
81 | action = menu->addAction(tr("Prune" )); |
82 | connect(action, &QAction::triggered, this, &Controls::pruneBranches); |
83 | menu->addSeparator(); |
84 | |
85 | mPullBtn->setIconSize(QSize(22, 22)); |
86 | mPullBtn->setToolTip(tr("Pull" )); |
87 | mPullBtn->setToolButtonStyle(Qt::ToolButtonIconOnly); |
88 | mPullBtn->setPopupMode(QToolButton::InstantPopup); |
89 | mPullBtn->setIcon(QIcon(":/icons/git_pull" )); |
90 | mPullBtn->setObjectName("ToolButtonAboveMenu" ); |
91 | |
92 | mPullOptions->setMenu(menu); |
93 | mPullOptions->setIcon(QIcon(":/icons/arrow_down" )); |
94 | mPullOptions->setIconSize(QSize(22, 22)); |
95 | mPullOptions->setToolButtonStyle(Qt::ToolButtonIconOnly); |
96 | mPullOptions->setPopupMode(QToolButton::InstantPopup); |
97 | mPullOptions->setToolTip("Remote actions" ); |
98 | mPullOptions->setObjectName("ToolButtonWithMenu" ); |
99 | |
100 | const auto pullLayout = new QVBoxLayout(); |
101 | pullLayout->setContentsMargins(QMargins()); |
102 | pullLayout->setSpacing(0); |
103 | pullLayout->addWidget(mPullBtn); |
104 | pullLayout->addWidget(mPullOptions); |
105 | |
106 | mPushBtn->setIcon(QIcon(":/icons/git_push" )); |
107 | mPushBtn->setIconSize(QSize(22, 22)); |
108 | mPushBtn->setToolTip(tr("Push" )); |
109 | mPushBtn->setToolButtonStyle(Qt::ToolButtonIconOnly); |
110 | |
111 | mRefreshBtn->setIcon(QIcon(":/icons/refresh" )); |
112 | mRefreshBtn->setIconSize(QSize(22, 22)); |
113 | mRefreshBtn->setToolTip(tr("Refresh" )); |
114 | mRefreshBtn->setToolButtonStyle(Qt::ToolButtonIconOnly); |
115 | |
116 | mConfigBtn->setCheckable(true); |
117 | mConfigBtn->setIcon(QIcon(":/icons/config" )); |
118 | mConfigBtn->setIconSize(QSize(22, 22)); |
119 | mConfigBtn->setToolTip(tr("Config" )); |
120 | mConfigBtn->setToolButtonStyle(Qt::ToolButtonIconOnly); |
121 | mBtnGroup->addButton(mConfigBtn, static_cast<int>(ControlsMainViews::Config)); |
122 | |
123 | const auto separator = new QFrame(); |
124 | separator->setObjectName("orangeSeparator" ); |
125 | separator->setFixedHeight(20); |
126 | |
127 | const auto separator2 = new QFrame(); |
128 | separator2->setObjectName("orangeSeparator" ); |
129 | separator2->setFixedHeight(20); |
130 | |
131 | const auto hLayout = new QHBoxLayout(); |
132 | hLayout->setContentsMargins(QMargins()); |
133 | hLayout->addStretch(); |
134 | hLayout->setSpacing(5); |
135 | hLayout->addWidget(mHistory); |
136 | hLayout->addWidget(mDiff); |
137 | hLayout->addWidget(mBlame); |
138 | hLayout->addWidget(separator); |
139 | hLayout->addLayout(pullLayout); |
140 | hLayout->addWidget(mPushBtn); |
141 | hLayout->addWidget(separator2); |
142 | |
143 | createGitPlatformButton(hLayout); |
144 | |
145 | GitQlientSettings settings(mGit->getGitDir()); |
146 | mBuildSystem->setVisible(settings.localValue("BuildSystemEnabled" , false).toBool()); |
147 | mBuildSystem->setCheckable(true); |
148 | mBuildSystem->setIcon(QIcon(":/icons/build_system" )); |
149 | mBuildSystem->setIconSize(QSize(22, 22)); |
150 | mBuildSystem->setToolTip("Jenkins" ); |
151 | mBuildSystem->setToolButtonStyle(Qt::ToolButtonIconOnly); |
152 | mBuildSystem->setPopupMode(QToolButton::InstantPopup); |
153 | mBtnGroup->addButton(mBuildSystem, static_cast<int>(ControlsMainViews::BuildSystem)); |
154 | |
155 | connect(mBuildSystem, &QToolButton::clicked, this, &Controls::signalGoBuildSystem); |
156 | |
157 | hLayout->addWidget(mBuildSystem); |
158 | |
159 | configBuildSystemButton(); |
160 | |
161 | const auto separator3 = new QFrame(); |
162 | separator3->setObjectName("orangeSeparator" ); |
163 | separator3->setFixedHeight(20); |
164 | hLayout->addWidget(separator3); |
165 | |
166 | mVersionCheck->setIcon(QIcon(":/icons/get_gitqlient" )); |
167 | mVersionCheck->setIconSize(QSize(22, 22)); |
168 | mVersionCheck->setText(tr("New version" )); |
169 | mVersionCheck->setObjectName("longToolButton" ); |
170 | mVersionCheck->setToolButtonStyle(Qt::ToolButtonIconOnly); |
171 | mVersionCheck->setVisible(false); |
172 | |
173 | mUpdater->checkNewGitQlientVersion(); |
174 | |
175 | hLayout->addWidget(mRefreshBtn); |
176 | hLayout->addWidget(mConfigBtn); |
177 | hLayout->addWidget(mPomodoro); |
178 | hLayout->addWidget(mVersionCheck); |
179 | hLayout->addStretch(); |
180 | |
181 | mMergeWarning->setObjectName("WarningButton" ); |
182 | mMergeWarning->setVisible(false); |
183 | mBtnGroup->addButton(mMergeWarning, static_cast<int>(ControlsMainViews::Merge)); |
184 | |
185 | const auto vLayout = new QVBoxLayout(this); |
186 | vLayout->setContentsMargins(0, 5, 0, 0); |
187 | vLayout->setSpacing(10); |
188 | vLayout->addLayout(hLayout); |
189 | vLayout->addWidget(mMergeWarning); |
190 | |
191 | connect(mHistory, &QToolButton::clicked, this, &Controls::signalGoRepo); |
192 | connect(mDiff, &QToolButton::clicked, this, &Controls::signalGoDiff); |
193 | connect(mBlame, &QToolButton::clicked, this, &Controls::signalGoBlame); |
194 | connect(mPullBtn, &QToolButton::clicked, this, &Controls::pullCurrentBranch); |
195 | connect(mPushBtn, &QToolButton::clicked, this, &Controls::pushCurrentBranch); |
196 | connect(mRefreshBtn, &QToolButton::clicked, this, &Controls::requestFullReload); |
197 | connect(mMergeWarning, &QPushButton::clicked, this, &Controls::signalGoMerge); |
198 | connect(mVersionCheck, &QToolButton::clicked, mUpdater, &GitQlientUpdater::showInfoMessage); |
199 | connect(mConfigBtn, &QToolButton::clicked, this, &Controls::goConfig); |
200 | |
201 | enableButtons(false); |
202 | } |
203 | |
204 | Controls::~Controls() |
205 | { |
206 | delete mBtnGroup; |
207 | } |
208 | |
209 | void Controls::toggleButton(ControlsMainViews view) |
210 | { |
211 | mBtnGroup->button(static_cast<int>(view))->setChecked(true); |
212 | } |
213 | |
214 | void Controls::enableButtons(bool enabled) |
215 | { |
216 | mHistory->setEnabled(enabled); |
217 | mBlame->setEnabled(enabled); |
218 | mPullBtn->setEnabled(enabled); |
219 | mPullOptions->setEnabled(enabled); |
220 | mPushBtn->setEnabled(enabled); |
221 | mRefreshBtn->setEnabled(enabled); |
222 | mGitPlatform->setEnabled(enabled); |
223 | mConfigBtn->setEnabled(enabled); |
224 | |
225 | if (enabled) |
226 | { |
227 | GitQlientSettings settings(mGit->getGitDir()); |
228 | const auto isConfigured = settings.localValue("BuildSystemEnabled" , false).toBool(); |
229 | |
230 | mBuildSystem->setEnabled(isConfigured); |
231 | } |
232 | else |
233 | mBuildSystem->setEnabled(false); |
234 | } |
235 | |
236 | void Controls::pullCurrentBranch() |
237 | { |
238 | QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); |
239 | QScopedPointer<GitRemote> git(new GitRemote(mGit)); |
240 | const auto ret = git->pull(); |
241 | QApplication::restoreOverrideCursor(); |
242 | |
243 | if (ret.success) |
244 | { |
245 | if (ret.output.contains("merge conflict" , Qt::CaseInsensitive)) |
246 | emit signalPullConflict(); |
247 | else |
248 | emit requestFullReload(); |
249 | } |
250 | else |
251 | { |
252 | if (ret.output.contains("error: could not apply" , Qt::CaseInsensitive) |
253 | && ret.output.contains("causing a conflict" , Qt::CaseInsensitive)) |
254 | { |
255 | emit signalPullConflict(); |
256 | } |
257 | else |
258 | { |
259 | QMessageBox msgBox(QMessageBox::Critical, tr("Error while pulling" ), |
260 | QString(tr("There were problems during the pull operation. Please, see the detailed " |
261 | "description for more information." )), |
262 | QMessageBox::Ok, this); |
263 | msgBox.setDetailedText(ret.output); |
264 | msgBox.setStyleSheet(GitQlientStyles::getStyles()); |
265 | msgBox.exec(); |
266 | } |
267 | } |
268 | } |
269 | |
270 | void Controls::fetchAll() |
271 | { |
272 | QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); |
273 | QScopedPointer<GitRemote> git(new GitRemote(mGit)); |
274 | const auto ret = git->fetch(); |
275 | QApplication::restoreOverrideCursor(); |
276 | |
277 | if (ret) |
278 | { |
279 | mGitTags->getRemoteTags(); |
280 | emit requestFullReload(); |
281 | } |
282 | } |
283 | |
284 | void Controls::activateMergeWarning() |
285 | { |
286 | mMergeWarning->setVisible(true); |
287 | } |
288 | |
289 | void Controls::disableMergeWarning() |
290 | { |
291 | mMergeWarning->setVisible(false); |
292 | } |
293 | |
294 | void Controls::disableDiff() |
295 | { |
296 | mDiff->setDisabled(true); |
297 | } |
298 | |
299 | void Controls::enableDiff() |
300 | { |
301 | mDiff->setEnabled(true); |
302 | } |
303 | |
304 | ControlsMainViews Controls::getCurrentSelectedButton() const |
305 | { |
306 | return mBlame->isChecked() ? ControlsMainViews::Blame : ControlsMainViews::History; |
307 | } |
308 | |
309 | void Controls::pushCurrentBranch() |
310 | { |
311 | QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); |
312 | QScopedPointer<GitRemote> git(new GitRemote(mGit)); |
313 | const auto ret = git->push(); |
314 | QApplication::restoreOverrideCursor(); |
315 | |
316 | if (ret.output.contains("has no upstream branch" )) |
317 | { |
318 | const auto currentBranch = mGit->getCurrentBranch(); |
319 | BranchDlg dlg({ currentBranch, BranchDlgMode::PUSH_UPSTREAM, mCache, mGit }); |
320 | const auto dlgRet = dlg.exec(); |
321 | |
322 | if (dlgRet == QDialog::Accepted) |
323 | emit signalRefreshPRsCache(); |
324 | } |
325 | else if (ret.success) |
326 | { |
327 | const auto currentBranch = mGit->getCurrentBranch(); |
328 | QScopedPointer<GitConfig> git(new GitConfig(mGit)); |
329 | const auto remote = git->getRemoteForBranch(currentBranch); |
330 | |
331 | if (remote.success) |
332 | { |
333 | const auto oldSha = mCache->getShaOfReference(QString("%1/%2" ).arg(remote.output, currentBranch), |
334 | References::Type::RemoteBranches); |
335 | const auto sha = mCache->getShaOfReference(currentBranch, References::Type::LocalBranch); |
336 | mCache->deleteReference(oldSha, References::Type::RemoteBranches, |
337 | QString("%1/%2" ).arg(remote.output, currentBranch)); |
338 | mCache->insertReference(sha, References::Type::RemoteBranches, |
339 | QString("%1/%2" ).arg(remote.output, currentBranch)); |
340 | emit mCache->signalCacheUpdated(); |
341 | emit signalRefreshPRsCache(); |
342 | } |
343 | } |
344 | else |
345 | { |
346 | QMessageBox msgBox( |
347 | QMessageBox::Critical, tr("Error while pushing" ), |
348 | QString(tr("There were problems during the push operation. Please, see the detailed description " |
349 | "for more information." )), |
350 | QMessageBox::Ok, this); |
351 | msgBox.setDetailedText(ret.output); |
352 | msgBox.setStyleSheet(GitQlientStyles::getStyles()); |
353 | msgBox.exec(); |
354 | } |
355 | } |
356 | |
357 | void Controls::pruneBranches() |
358 | { |
359 | QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); |
360 | QScopedPointer<GitRemote> git(new GitRemote(mGit)); |
361 | const auto ret = git->prune(); |
362 | QApplication::restoreOverrideCursor(); |
363 | |
364 | if (ret.success) |
365 | emit requestReferencesReload(); |
366 | } |
367 | |
368 | void Controls::createGitPlatformButton(QHBoxLayout *layout) |
369 | { |
370 | QScopedPointer<GitConfig> gitConfig(new GitConfig(mGit)); |
371 | const auto remoteUrl = gitConfig->getServerHost(); |
372 | QIcon gitPlatformIcon; |
373 | QString name; |
374 | QString prName; |
375 | auto add = false; |
376 | |
377 | if (remoteUrl.contains("github" , Qt::CaseInsensitive)) |
378 | { |
379 | add = true; |
380 | |
381 | gitPlatformIcon = QIcon(":/icons/github" ); |
382 | name = "GitHub" ; |
383 | prName = tr("Pull Request" ); |
384 | } |
385 | else if (remoteUrl.contains("gitlab" , Qt::CaseInsensitive)) |
386 | { |
387 | add = true; |
388 | |
389 | gitPlatformIcon = QIcon(":/icons/gitlab" ); |
390 | name = "GitLab" ; |
391 | prName = tr("Merge Request" ); |
392 | } |
393 | |
394 | if (add) |
395 | { |
396 | mGitPlatform->setCheckable(true); |
397 | mGitPlatform->setIcon(gitPlatformIcon); |
398 | mGitPlatform->setIconSize(QSize(22, 22)); |
399 | mGitPlatform->setToolTip(name); |
400 | mGitPlatform->setToolButtonStyle(Qt::ToolButtonIconOnly); |
401 | mGitPlatform->setPopupMode(QToolButton::InstantPopup); |
402 | mBtnGroup->addButton(mGitPlatform, static_cast<int>(ControlsMainViews::GitServer)); |
403 | |
404 | layout->addWidget(mGitPlatform); |
405 | |
406 | connect(mGitPlatform, &QToolButton::clicked, this, &Controls::signalGoServer); |
407 | } |
408 | else |
409 | mGitPlatform->setVisible(false); |
410 | } |
411 | |
412 | void Controls::configBuildSystemButton() |
413 | { |
414 | GitQlientSettings settings(mGit->getGitDir()); |
415 | const auto isConfigured = settings.localValue("BuildSystemEnabled" , false).toBool(); |
416 | mBuildSystem->setEnabled(isConfigured); |
417 | |
418 | if (!isConfigured) |
419 | emit signalGoRepo(); |
420 | } |
421 | |
422 | bool Controls::eventFilter(QObject *obj, QEvent *event) |
423 | { |
424 | if (const auto = qobject_cast<QMenu *>(obj); menu && event->type() == QEvent::Show) |
425 | { |
426 | auto localPos = menu->parentWidget()->pos(); |
427 | localPos.setX(localPos.x()); |
428 | auto pos = mapToGlobal(localPos); |
429 | menu->show(); |
430 | pos.setY(pos.y() + menu->parentWidget()->height()); |
431 | menu->move(pos); |
432 | return true; |
433 | } |
434 | |
435 | return false; |
436 | } |
437 | |