1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
32 **************************************************************************/
34 #include "branchdialog.h"
35 #include "branchmodel.h"
36 #include "gitclient.h"
37 #include "gitplugin.h"
38 #include "ui_branchdialog.h"
39 #include "stashdialog.h" // Label helpers
41 #include <vcsbase/vcsbaseoutputwindow.h>
43 #include <QtGui/QItemSelectionModel>
44 #include <QtGui/QPushButton>
45 #include <QtGui/QMessageBox>
47 #include <QtCore/QDebug>
51 // Single selection helper
52 static inline int selectedRow(const QAbstractItemView *listView)
54 const QModelIndexList indexList = listView->selectionModel()->selectedIndexes();
55 if (indexList.size() == 1)
56 return indexList.front().row();
60 // Helper to select a row. No sooner said then done
61 static inline void selectListRow(QAbstractItemView *iv, int row)
63 const QModelIndex index = iv->model()->index(row, 0);
64 iv->selectionModel()->select(index, QItemSelectionModel::Select);
70 static inline GitClient *gitClient()
72 return GitPlugin::instance()->gitClient();
75 BranchDialog::BranchDialog(QWidget *parent) :
77 m_ui(new Ui::BranchDialog),
83 m_localModel(new LocalBranchModel(gitClient(), this)),
84 m_remoteModel(new RemoteBranchModel(gitClient(), this))
87 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
88 setAttribute(Qt::WA_DeleteOnClose, true); // Do not update unnecessarily
92 m_checkoutButton = m_ui->buttonBox->addButton(tr("Checkout"), QDialogButtonBox::ActionRole);
93 connect(m_checkoutButton, SIGNAL(clicked()), this, SLOT(slotCheckoutSelectedBranch()));
95 m_diffButton = m_ui->buttonBox->addButton(tr("Diff"), QDialogButtonBox::ActionRole);
96 connect(m_diffButton, SIGNAL(clicked()), this, SLOT(slotDiffSelected()));
98 m_logButton = m_ui->buttonBox->addButton(tr("Log"), QDialogButtonBox::ActionRole);
99 connect(m_logButton, SIGNAL(clicked()), this, SLOT(slotLog()));
101 m_refreshButton = m_ui->buttonBox->addButton(tr("Refresh"), QDialogButtonBox::ActionRole);
102 connect(m_refreshButton, SIGNAL(clicked()), this, SLOT(slotRefresh()));
104 m_deleteButton = m_ui->buttonBox->addButton(tr("Delete..."), QDialogButtonBox::ActionRole);
105 connect(m_deleteButton, SIGNAL(clicked()), this, SLOT(slotDeleteSelectedBranch()));
107 connect(m_ui->localBranchListView, SIGNAL(doubleClicked(QModelIndex)), this,
108 SLOT(slotLocalBranchActivated()));
109 connect(m_ui->remoteBranchListView, SIGNAL(doubleClicked(QModelIndex)), this,
110 SLOT(slotRemoteBranchActivated(QModelIndex)));
112 connect(m_localModel, SIGNAL(newBranchEntered(QString)), this, SLOT(slotCreateLocalBranch(QString)));
113 m_ui->localBranchListView->setModel(m_localModel);
114 m_ui->remoteBranchListView->setModel(m_remoteModel);
116 connect(m_ui->localBranchListView->selectionModel(),
117 SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
118 this, SLOT(slotEnableButtons(QItemSelection)));
119 connect(m_ui->remoteBranchListView->selectionModel(),
120 SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
121 this, SLOT(slotEnableButtons(QItemSelection)));
126 BranchDialog::~BranchDialog()
131 void BranchDialog::refresh(const QString &repository, bool force)
133 if (m_repository == repository && !force)
136 m_repository = repository;
137 m_ui->repositoryLabel->setText(StashDialog::msgRepositoryLabel(m_repository));
138 if (m_repository.isEmpty()) {
139 m_localModel->clear();
140 m_remoteModel->clear();
142 QString errorMessage;
143 const bool success = m_localModel->refresh(m_repository, &errorMessage)
144 && m_remoteModel->refresh(m_repository, &errorMessage);
146 VCSBase::VCSBaseOutputWindow::instance()->appendError(errorMessage);
151 int BranchDialog::selectedLocalBranchIndex() const
153 return selectedRow(m_ui->localBranchListView);
156 int BranchDialog::selectedRemoteBranchIndex() const
158 return selectedRow(m_ui->remoteBranchListView);
161 void BranchDialog::slotEnableButtons(const QItemSelection &selected)
163 if (!selected.indexes().isEmpty()) {
164 if (selected.indexes().at(0).model() == m_localModel)
165 m_ui->remoteBranchListView->clearSelection();
167 m_ui->localBranchListView->clearSelection();
170 // We can switch to or delete branches that are not current.
171 const int selectedLocalRow = selectedLocalBranchIndex();
172 const bool hasRepository = !m_repository.isEmpty();
173 const bool hasLocalSelection = selectedLocalRow != -1 && !m_localModel->isNewBranchRow(selectedLocalRow);
174 const bool otherLocalSelected = hasLocalSelection && selectedLocalRow != m_localModel->currentBranch();
175 const bool branchSelected = hasLocalSelection || selectedRemoteBranchIndex() != -1;
177 m_checkoutButton->setEnabled(otherLocalSelected);
178 m_diffButton->setEnabled(branchSelected);
179 m_logButton->setEnabled(branchSelected);
180 m_deleteButton->setEnabled(otherLocalSelected);
181 m_refreshButton->setEnabled(hasRepository);
182 // Also disable <New Branch> entry of list view
183 m_ui->localBranchListView->setEnabled(hasRepository);
184 m_ui->remoteBranchListView->setEnabled(hasRepository);
187 void BranchDialog::slotRefresh()
189 refresh(m_repository, true);
192 void BranchDialog::selectLocalBranch(const QString &b)
194 // Select the newly created branch
195 const int row = m_localModel->findBranchByName(b);
197 selectListRow(m_ui->localBranchListView, row);
200 bool BranchDialog::ask(const QString &title, const QString &what, bool defaultButton)
202 return QMessageBox::question(this, title, what, QMessageBox::Yes|QMessageBox::No,
203 defaultButton ? QMessageBox::Yes : QMessageBox::No) == QMessageBox::Yes;
206 /* Prompt to delete a local branch and do so. */
207 void BranchDialog::slotDeleteSelectedBranch()
209 const int idx = selectedLocalBranchIndex();
212 const QString name = m_localModel->branchName(idx);
213 if (!ask(tr("Delete Branch"), tr("Would you like to delete the branch '%1'?").arg(name), true))
215 QString errorMessage;
219 QStringList args(QLatin1String("-D"));
221 if (!gitClient()->synchronousBranchCmd(m_repository, args, &output, &errorMessage))
223 if (!m_localModel->refresh(m_repository, &errorMessage))
229 QMessageBox::warning(this, tr("Failed to delete branch"), errorMessage);
232 void BranchDialog::slotCreateLocalBranch(const QString &branchName)
236 QString errorMessage;
239 if (!gitClient()->synchronousBranchCmd(m_repository, QStringList(branchName), &output, &errorMessage))
241 if (!m_localModel->refresh(m_repository, &errorMessage))
246 QMessageBox::warning(this, tr("Failed to create branch"), errorMessage);
249 selectLocalBranch(branchName);
252 void BranchDialog::slotLocalBranchActivated()
254 if (m_checkoutButton->isEnabled())
255 m_checkoutButton->animateClick();
258 void BranchDialog::slotDiffSelected()
260 int idx = selectedLocalBranchIndex();
262 gitClient()->diffBranch(m_repository, QStringList(), m_localModel->branchName(idx));
265 idx = selectedRemoteBranchIndex();
267 gitClient()->diffBranch(m_repository, QStringList(), m_remoteModel->branchName(idx));
270 void BranchDialog::slotLog()
272 int idx = selectedLocalBranchIndex();
274 gitClient()->graphLog(m_repository, m_localModel->branchName(idx));
277 idx = selectedRemoteBranchIndex();
279 gitClient()->graphLog(m_repository, m_remoteModel->branchName(idx));
282 /* Ask to stash away changes and then close dialog and do an asynchronous
284 void BranchDialog::slotCheckoutSelectedBranch()
286 const int idx = selectedLocalBranchIndex();
289 const QString name = m_localModel->branchName(idx);
290 QString errorMessage;
291 switch (gitClient()->ensureStash(m_repository, &errorMessage)) {
292 case GitClient::StashUnchanged:
293 case GitClient::Stashed:
294 case GitClient::NotStashed:
296 case GitClient::StashCanceled:
298 case GitClient::StashFailed:
299 QMessageBox::warning(this, tr("Failed to stash"), errorMessage);
302 if (gitClient()->synchronousCheckoutBranch(m_repository, name, &errorMessage)) {
303 refresh(m_repository, true);
305 QMessageBox::warning(this, tr("Checkout failed"), errorMessage);
309 void BranchDialog::slotRemoteBranchActivated(const QModelIndex &i)
311 // Double click on a remote branch (origin/foo): Switch to matching
312 // local (foo) one or offer to create a tracking branch.
313 const QString remoteName = m_remoteModel->branchName(i.row());
314 // build the name of the corresponding local branch
315 // and look for it in the local model.
316 const int slashPos = remoteName.indexOf(QLatin1Char('/'));
319 const QString localBranch = remoteName.mid(slashPos + 1);
320 if (localBranch == QLatin1String("HEAD") || localBranch == QLatin1String("master"))
322 const int localIndex = m_localModel->findBranchByName(localBranch);
324 qDebug() << Q_FUNC_INFO << remoteName << localBranch << localIndex;
325 // There is a matching a local one!
326 if (localIndex != -1) {
327 // Is it the current one? Just close.
328 if (m_localModel->currentBranch() == localIndex) {
332 // Nope, select and trigger checkout
333 selectListRow(m_ui->localBranchListView, localIndex);
334 slotLocalBranchActivated();
337 // Does not exist yet. Ask to create.
338 const QString msg = tr("Would you like to create a local branch '%1' tracking the remote branch '%2'?").arg(localBranch, remoteName);
339 if (!ask(tr("Create branch"), msg, true))
341 QStringList args(QLatin1String("--track"));
342 args << localBranch << remoteName;
343 QString errorMessage;
347 if (!gitClient()->synchronousBranchCmd(m_repository, args, &output, &errorMessage))
349 if (!m_localModel->refresh(m_repository, &errorMessage))
354 QMessageBox::warning(this, tr("Failed to create a tracking branch"), errorMessage);
358 selectLocalBranch(localBranch);
361 void BranchDialog::changeEvent(QEvent *e)
363 QDialog::changeEvent(e);
365 case QEvent::LanguageChange:
366 m_ui->retranslateUi(this);
373 } // namespace Internal