/************************************************************************** ** ** This file is part of Qt Creator ** ** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** Commercial Usage ** ** Licensees holding valid Qt Commercial licenses may use this file in ** accordance with the Qt Commercial License Agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Nokia. ** ** GNU Lesser General Public License Usage ** ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** If you are unsure which license is appropriate for your use, please ** contact the sales department at http://qt.nokia.com/contact. ** **************************************************************************/ #include "branchdialog.h" #include "branchmodel.h" #include "gitclient.h" #include "gitplugin.h" #include "ui_branchdialog.h" #include "stashdialog.h" // Label helpers #include #include #include #include #include enum { debug = 0 }; // Single selection helper static inline int selectedRow(const QAbstractItemView *listView) { const QModelIndexList indexList = listView->selectionModel()->selectedIndexes(); if (indexList.size() == 1) return indexList.front().row(); return -1; } // Helper to select a row. No sooner said then done static inline void selectListRow(QAbstractItemView *iv, int row) { const QModelIndex index = iv->model()->index(row, 0); iv->selectionModel()->select(index, QItemSelectionModel::Select); } namespace Git { namespace Internal { static inline GitClient *gitClient() { return GitPlugin::instance()->gitClient(); } BranchDialog::BranchDialog(QWidget *parent) : QDialog(parent), m_ui(new Ui::BranchDialog), m_checkoutButton(0), m_diffButton(0), m_logButton(0), m_refreshButton(0), m_deleteButton(0), m_localModel(new LocalBranchModel(gitClient(), this)), m_remoteModel(new RemoteBranchModel(gitClient(), this)) { setModal(false); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); setAttribute(Qt::WA_DeleteOnClose, true); // Do not update unnecessarily m_ui->setupUi(this); m_checkoutButton = m_ui->buttonBox->addButton(tr("Checkout"), QDialogButtonBox::ActionRole); connect(m_checkoutButton, SIGNAL(clicked()), this, SLOT(slotCheckoutSelectedBranch())); m_diffButton = m_ui->buttonBox->addButton(tr("Diff"), QDialogButtonBox::ActionRole); connect(m_diffButton, SIGNAL(clicked()), this, SLOT(slotDiffSelected())); m_logButton = m_ui->buttonBox->addButton(tr("Log"), QDialogButtonBox::ActionRole); connect(m_logButton, SIGNAL(clicked()), this, SLOT(slotLog())); m_refreshButton = m_ui->buttonBox->addButton(tr("Refresh"), QDialogButtonBox::ActionRole); connect(m_refreshButton, SIGNAL(clicked()), this, SLOT(slotRefresh())); m_deleteButton = m_ui->buttonBox->addButton(tr("Delete..."), QDialogButtonBox::ActionRole); connect(m_deleteButton, SIGNAL(clicked()), this, SLOT(slotDeleteSelectedBranch())); connect(m_ui->localBranchListView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(slotLocalBranchActivated())); connect(m_ui->remoteBranchListView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(slotRemoteBranchActivated(QModelIndex))); connect(m_localModel, SIGNAL(newBranchEntered(QString)), this, SLOT(slotCreateLocalBranch(QString))); m_ui->localBranchListView->setModel(m_localModel); m_ui->remoteBranchListView->setModel(m_remoteModel); connect(m_ui->localBranchListView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(slotEnableButtons(QItemSelection))); connect(m_ui->remoteBranchListView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(slotEnableButtons(QItemSelection))); slotEnableButtons(); } BranchDialog::~BranchDialog() { delete m_ui; } void BranchDialog::refresh(const QString &repository, bool force) { if (m_repository == repository && !force) return; // Refresh m_repository = repository; m_ui->repositoryLabel->setText(StashDialog::msgRepositoryLabel(m_repository)); if (m_repository.isEmpty()) { m_localModel->clear(); m_remoteModel->clear(); } else { QString errorMessage; const bool success = m_localModel->refresh(m_repository, &errorMessage) && m_remoteModel->refresh(m_repository, &errorMessage); if (!success) VCSBase::VCSBaseOutputWindow::instance()->appendError(errorMessage); } slotEnableButtons(); } int BranchDialog::selectedLocalBranchIndex() const { return selectedRow(m_ui->localBranchListView); } int BranchDialog::selectedRemoteBranchIndex() const { return selectedRow(m_ui->remoteBranchListView); } void BranchDialog::slotEnableButtons(const QItemSelection &selected) { if (!selected.indexes().isEmpty()) { if (selected.indexes().at(0).model() == m_localModel) m_ui->remoteBranchListView->clearSelection(); else m_ui->localBranchListView->clearSelection(); } // We can switch to or delete branches that are not current. const int selectedLocalRow = selectedLocalBranchIndex(); const bool hasRepository = !m_repository.isEmpty(); const bool hasLocalSelection = selectedLocalRow != -1 && !m_localModel->isNewBranchRow(selectedLocalRow); const bool otherLocalSelected = hasLocalSelection && selectedLocalRow != m_localModel->currentBranch(); const bool branchSelected = hasLocalSelection || selectedRemoteBranchIndex() != -1; m_checkoutButton->setEnabled(otherLocalSelected); m_diffButton->setEnabled(branchSelected); m_logButton->setEnabled(branchSelected); m_deleteButton->setEnabled(otherLocalSelected); m_refreshButton->setEnabled(hasRepository); // Also disable entry of list view m_ui->localBranchListView->setEnabled(hasRepository); m_ui->remoteBranchListView->setEnabled(hasRepository); } void BranchDialog::slotRefresh() { refresh(m_repository, true); } void BranchDialog::selectLocalBranch(const QString &b) { // Select the newly created branch const int row = m_localModel->findBranchByName(b); if (row != -1) selectListRow(m_ui->localBranchListView, row); } bool BranchDialog::ask(const QString &title, const QString &what, bool defaultButton) { return QMessageBox::question(this, title, what, QMessageBox::Yes|QMessageBox::No, defaultButton ? QMessageBox::Yes : QMessageBox::No) == QMessageBox::Yes; } /* Prompt to delete a local branch and do so. */ void BranchDialog::slotDeleteSelectedBranch() { const int idx = selectedLocalBranchIndex(); if (idx == -1) return; const QString name = m_localModel->branchName(idx); if (!ask(tr("Delete Branch"), tr("Would you like to delete the branch '%1'?").arg(name), true)) return; QString errorMessage; bool ok = false; do { QString output; QStringList args(QLatin1String("-D")); args << name; if (!gitClient()->synchronousBranchCmd(m_repository, args, &output, &errorMessage)) break; if (!m_localModel->refresh(m_repository, &errorMessage)) break; ok = true; } while (false); slotEnableButtons(); if (!ok) QMessageBox::warning(this, tr("Failed to delete branch"), errorMessage); } void BranchDialog::slotCreateLocalBranch(const QString &branchName) { // Create QString output; QString errorMessage; bool ok = false; do { if (!gitClient()->synchronousBranchCmd(m_repository, QStringList(branchName), &output, &errorMessage)) break; if (!m_localModel->refresh(m_repository, &errorMessage)) break; ok = true; } while (false); if (!ok) { QMessageBox::warning(this, tr("Failed to create branch"), errorMessage); return; } selectLocalBranch(branchName); } void BranchDialog::slotLocalBranchActivated() { if (m_checkoutButton->isEnabled()) m_checkoutButton->animateClick(); } void BranchDialog::slotDiffSelected() { int idx = selectedLocalBranchIndex(); if (idx != -1) { gitClient()->diffBranch(m_repository, QStringList(), m_localModel->branchName(idx)); return; } idx = selectedRemoteBranchIndex(); if (idx != -1) gitClient()->diffBranch(m_repository, QStringList(), m_remoteModel->branchName(idx)); } void BranchDialog::slotLog() { int idx = selectedLocalBranchIndex(); if (idx != -1) { gitClient()->graphLog(m_repository, m_localModel->branchName(idx)); return; } idx = selectedRemoteBranchIndex(); if (idx != -1) gitClient()->graphLog(m_repository, m_remoteModel->branchName(idx)); } /* Ask to stash away changes and then close dialog and do an asynchronous * checkout. */ void BranchDialog::slotCheckoutSelectedBranch() { const int idx = selectedLocalBranchIndex(); if (idx == -1) return; const QString name = m_localModel->branchName(idx); QString errorMessage; switch (gitClient()->ensureStash(m_repository, &errorMessage)) { case GitClient::StashUnchanged: case GitClient::Stashed: case GitClient::NotStashed: break; case GitClient::StashCanceled: return; case GitClient::StashFailed: QMessageBox::warning(this, tr("Failed to stash"), errorMessage); return; } if (gitClient()->synchronousCheckoutBranch(m_repository, name, &errorMessage)) { refresh(m_repository, true); } else { QMessageBox::warning(this, tr("Checkout failed"), errorMessage); } } void BranchDialog::slotRemoteBranchActivated(const QModelIndex &i) { // Double click on a remote branch (origin/foo): Switch to matching // local (foo) one or offer to create a tracking branch. const QString remoteName = m_remoteModel->branchName(i.row()); // build the name of the corresponding local branch // and look for it in the local model. const int slashPos = remoteName.indexOf(QLatin1Char('/')); if (slashPos == -1) return; const QString localBranch = remoteName.mid(slashPos + 1); if (localBranch == QLatin1String("HEAD") || localBranch == QLatin1String("master")) return; const int localIndex = m_localModel->findBranchByName(localBranch); if (debug) qDebug() << Q_FUNC_INFO << remoteName << localBranch << localIndex; // There is a matching a local one! if (localIndex != -1) { // Is it the current one? Just close. if (m_localModel->currentBranch() == localIndex) { accept(); return; } // Nope, select and trigger checkout selectListRow(m_ui->localBranchListView, localIndex); slotLocalBranchActivated(); return; } // Does not exist yet. Ask to create. const QString msg = tr("Would you like to create a local branch '%1' tracking the remote branch '%2'?").arg(localBranch, remoteName); if (!ask(tr("Create branch"), msg, true)) return; QStringList args(QLatin1String("--track")); args << localBranch << remoteName; QString errorMessage; bool ok = false; do { QString output; if (!gitClient()->synchronousBranchCmd(m_repository, args, &output, &errorMessage)) break; if (!m_localModel->refresh(m_repository, &errorMessage)) break; ok = true; } while (false); if (!ok) { QMessageBox::warning(this, tr("Failed to create a tracking branch"), errorMessage); return; } // Select it selectLocalBranch(localBranch); } void BranchDialog::changeEvent(QEvent *e) { QDialog::changeEvent(e); switch (e->type()) { case QEvent::LanguageChange: m_ui->retranslateUi(this); break; default: break; } } } // namespace Internal } // namespace Git