OSDN Git Service

347dfd6afafce8d5eb2d4332c62a79fdce640c3b
[qt-creator-jp/qt-creator-jp.git] / src / plugins / git / branchdialog.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** No Commercial Usage
10 **
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
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 **
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.
24 **
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.
28 **
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
31 **
32 **************************************************************************/
33
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
40
41 #include <vcsbase/vcsbaseoutputwindow.h>
42
43 #include <QtGui/QItemSelectionModel>
44 #include <QtGui/QPushButton>
45 #include <QtGui/QMessageBox>
46
47 #include <QtCore/QDebug>
48
49 enum { debug = 0 };
50
51 // Single selection helper
52 static inline int selectedRow(const QAbstractItemView *listView)
53 {
54     const QModelIndexList indexList = listView->selectionModel()->selectedIndexes();
55     if (indexList.size() == 1)
56         return indexList.front().row();
57     return -1;
58 }
59
60 // Helper to select a row. No sooner said then done
61 static inline void selectListRow(QAbstractItemView *iv, int row)
62 {
63     const QModelIndex index = iv->model()->index(row, 0);
64     iv->selectionModel()->select(index, QItemSelectionModel::Select);
65 }
66
67 namespace Git {
68     namespace Internal {
69
70 static inline GitClient *gitClient()
71 {
72     return GitPlugin::instance()->gitClient();
73 }
74
75 BranchDialog::BranchDialog(QWidget *parent) :
76     QDialog(parent),
77     m_ui(new Ui::BranchDialog),
78     m_checkoutButton(0),
79     m_diffButton(0),
80     m_logButton(0),
81     m_refreshButton(0),
82     m_deleteButton(0),
83     m_localModel(new LocalBranchModel(gitClient(), this)),
84     m_remoteModel(new RemoteBranchModel(gitClient(), this))
85 {
86     setModal(false);
87     setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
88     setAttribute(Qt::WA_DeleteOnClose, true); // Do not update unnecessarily
89
90     m_ui->setupUi(this);
91
92     m_checkoutButton = m_ui->buttonBox->addButton(tr("Checkout"), QDialogButtonBox::ActionRole);
93     connect(m_checkoutButton, SIGNAL(clicked()), this, SLOT(slotCheckoutSelectedBranch()));
94
95     m_diffButton = m_ui->buttonBox->addButton(tr("Diff"), QDialogButtonBox::ActionRole);
96     connect(m_diffButton, SIGNAL(clicked()), this, SLOT(slotDiffSelected()));
97
98     m_logButton = m_ui->buttonBox->addButton(tr("Log"), QDialogButtonBox::ActionRole);
99     connect(m_logButton, SIGNAL(clicked()), this, SLOT(slotLog()));
100
101     m_refreshButton = m_ui->buttonBox->addButton(tr("Refresh"), QDialogButtonBox::ActionRole);
102     connect(m_refreshButton, SIGNAL(clicked()), this, SLOT(slotRefresh()));
103
104     m_deleteButton = m_ui->buttonBox->addButton(tr("Delete..."), QDialogButtonBox::ActionRole);
105     connect(m_deleteButton, SIGNAL(clicked()), this, SLOT(slotDeleteSelectedBranch()));
106
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)));
111
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);
115
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)));
122
123     slotEnableButtons();
124 }
125
126 BranchDialog::~BranchDialog()
127 {
128     delete m_ui;
129 }
130
131 void BranchDialog::refresh(const QString &repository, bool force)
132 {
133     if (m_repository == repository && !force)
134             return;
135         // Refresh
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();
141     } else {
142         QString errorMessage;
143         const bool success = m_localModel->refresh(m_repository, &errorMessage)
144                              && m_remoteModel->refresh(m_repository, &errorMessage);
145         if (!success)
146             VCSBase::VCSBaseOutputWindow::instance()->appendError(errorMessage);
147     }
148     slotEnableButtons();
149 }
150
151 int BranchDialog::selectedLocalBranchIndex() const
152 {
153     return selectedRow(m_ui->localBranchListView);
154 }
155
156 int BranchDialog::selectedRemoteBranchIndex() const
157 {
158     return selectedRow(m_ui->remoteBranchListView);
159 }
160
161 void BranchDialog::slotEnableButtons(const QItemSelection &selected)
162 {
163     if (!selected.indexes().isEmpty()) {
164         if (selected.indexes().at(0).model() == m_localModel)
165             m_ui->remoteBranchListView->clearSelection();
166         else
167             m_ui->localBranchListView->clearSelection();
168     }
169
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;
176
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);
185 }
186
187 void BranchDialog::slotRefresh()
188 {
189     refresh(m_repository, true);
190 }
191
192 void BranchDialog::selectLocalBranch(const QString &b)
193 {
194     // Select the newly created branch
195     const int row = m_localModel->findBranchByName(b);
196     if (row != -1)
197         selectListRow(m_ui->localBranchListView, row);
198 }
199
200 bool BranchDialog::ask(const QString &title, const QString &what, bool defaultButton)
201 {
202     return QMessageBox::question(this, title, what, QMessageBox::Yes|QMessageBox::No,
203                                  defaultButton ? QMessageBox::Yes : QMessageBox::No) == QMessageBox::Yes;
204 }
205
206 /* Prompt to delete a local branch and do so. */
207 void BranchDialog::slotDeleteSelectedBranch()
208 {
209     const int idx = selectedLocalBranchIndex();
210     if (idx == -1)
211         return;
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))
214         return;
215     QString errorMessage;
216     bool ok = false;
217     do {
218         QString output;
219         QStringList args(QLatin1String("-D"));
220         args << name;
221         if (!gitClient()->synchronousBranchCmd(m_repository, args, &output, &errorMessage))
222             break;
223         if (!m_localModel->refresh(m_repository, &errorMessage))
224             break;
225         ok = true;
226     } while (false);
227     slotEnableButtons();
228     if (!ok)
229         QMessageBox::warning(this, tr("Failed to delete branch"), errorMessage);
230 }
231
232 void BranchDialog::slotCreateLocalBranch(const QString &branchName)
233 {
234     // Create
235     QString output;
236     QString errorMessage;
237     bool ok = false;
238     do {
239         if (!gitClient()->synchronousBranchCmd(m_repository, QStringList(branchName), &output, &errorMessage))
240             break;
241         if (!m_localModel->refresh(m_repository, &errorMessage))
242             break;
243         ok = true;
244     } while (false);
245     if (!ok) {
246         QMessageBox::warning(this, tr("Failed to create branch"), errorMessage);
247         return;
248     }
249     selectLocalBranch(branchName);
250 }
251
252 void BranchDialog::slotLocalBranchActivated()
253 {
254     if (m_checkoutButton->isEnabled())
255         m_checkoutButton->animateClick();
256 }
257
258 void BranchDialog::slotDiffSelected()
259 {
260     int idx = selectedLocalBranchIndex();
261     if (idx != -1) {
262         gitClient()->diffBranch(m_repository, QStringList(), m_localModel->branchName(idx));
263         return;
264     }
265     idx = selectedRemoteBranchIndex();
266     if (idx != -1)
267         gitClient()->diffBranch(m_repository, QStringList(), m_remoteModel->branchName(idx));
268 }
269
270 void BranchDialog::slotLog()
271 {
272     int idx = selectedLocalBranchIndex();
273     if (idx != -1) {
274         gitClient()->graphLog(m_repository, m_localModel->branchName(idx));
275         return;
276     }
277     idx = selectedRemoteBranchIndex();
278     if (idx != -1)
279         gitClient()->graphLog(m_repository, m_remoteModel->branchName(idx));
280 }
281
282 /* Ask to stash away changes and then close dialog and do an asynchronous
283  * checkout. */
284 void BranchDialog::slotCheckoutSelectedBranch()
285 {
286     const int idx = selectedLocalBranchIndex();
287     if (idx == -1)
288         return;
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:
295         break;
296         case GitClient::StashCanceled:
297         return;
298         case GitClient::StashFailed:
299         QMessageBox::warning(this, tr("Failed to stash"), errorMessage);
300         return;
301     }
302     if (gitClient()->synchronousCheckoutBranch(m_repository, name, &errorMessage)) {
303         refresh(m_repository, true);
304     } else {
305         QMessageBox::warning(this, tr("Checkout failed"), errorMessage);
306     }
307 }
308
309 void BranchDialog::slotRemoteBranchActivated(const QModelIndex &i)
310 {
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('/'));
317     if (slashPos == -1)
318         return;
319     const QString localBranch = remoteName.mid(slashPos + 1);
320     if (localBranch == QLatin1String("HEAD") || localBranch == QLatin1String("master"))
321         return;
322    const int localIndex = m_localModel->findBranchByName(localBranch);
323    if (debug)
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) {
329            accept();
330            return;
331        }
332        // Nope, select and trigger checkout
333        selectListRow(m_ui->localBranchListView, localIndex);
334        slotLocalBranchActivated();
335        return;
336     }
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))
340         return;
341     QStringList args(QLatin1String("--track"));
342     args << localBranch << remoteName;
343     QString errorMessage;
344     bool ok = false;
345     do {
346         QString output;
347         if (!gitClient()->synchronousBranchCmd(m_repository, args, &output, &errorMessage))
348             break;
349         if (!m_localModel->refresh(m_repository, &errorMessage))
350             break;
351         ok = true;
352     } while (false);
353     if (!ok) {
354         QMessageBox::warning(this, tr("Failed to create a tracking branch"), errorMessage);
355         return;
356     }
357     // Select it
358     selectLocalBranch(localBranch);
359 }
360
361 void BranchDialog::changeEvent(QEvent *e)
362 {
363     QDialog::changeEvent(e);
364     switch (e->type()) {
365     case QEvent::LanguageChange:
366         m_ui->retranslateUi(this);
367         break;
368     default:
369         break;
370     }
371 }
372
373 } // namespace Internal
374 } // namespace Git