forked from qt-creator/qt-creator
Give dialogs a consistent look, set WA_DeleteOnClose on them and improve updating. Add a Refresh/Diff buttons to branch dialog.
283 lines
7.9 KiB
C++
283 lines
7.9 KiB
C++
/**************************************************************************
|
|
**
|
|
** This file is part of Qt Creator
|
|
**
|
|
** Copyright (c) 2009 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 "branchmodel.h"
|
|
#include "gitclient.h"
|
|
|
|
#include <QtCore/QDebug>
|
|
#include <QtCore/QRegExp>
|
|
#include <QtCore/QTimer>
|
|
|
|
enum { debug = 0 };
|
|
|
|
namespace Git {
|
|
namespace Internal {
|
|
|
|
// Parse a branch line: " *name sha description". Return true if it is
|
|
// the current one
|
|
bool RemoteBranchModel::Branch::parse(const QString &lineIn, bool *isCurrent)
|
|
{
|
|
if (debug)
|
|
qDebug() << Q_FUNC_INFO << lineIn;
|
|
|
|
*isCurrent = lineIn.startsWith(QLatin1String("* "));
|
|
if (lineIn.size() < 3)
|
|
return false;
|
|
|
|
const QStringList tokens =lineIn.mid(2).split(QLatin1Char(' '), QString::SkipEmptyParts);
|
|
if (tokens.size() < 2)
|
|
return false;
|
|
name = tokens.at(0);
|
|
currentSHA= tokens.at(1);
|
|
toolTip.clear();
|
|
return true;
|
|
}
|
|
|
|
// ------ RemoteBranchModel
|
|
RemoteBranchModel::RemoteBranchModel(GitClient *client, QObject *parent) :
|
|
QAbstractListModel(parent),
|
|
m_flags(Qt::ItemIsSelectable|Qt::ItemIsEnabled),
|
|
m_client(client)
|
|
{
|
|
}
|
|
|
|
bool RemoteBranchModel::refresh(const QString &workingDirectory, QString *errorMessage)
|
|
{
|
|
int currentBranch;
|
|
return refreshBranches(workingDirectory, true, ¤tBranch, errorMessage);
|
|
}
|
|
|
|
QString RemoteBranchModel::branchName(int row) const
|
|
{
|
|
return m_branches.at(row).name;
|
|
}
|
|
|
|
QString RemoteBranchModel::workingDirectory() const
|
|
{
|
|
return m_workingDirectory;
|
|
}
|
|
|
|
int RemoteBranchModel::branchCount() const
|
|
{
|
|
return m_branches.size();
|
|
}
|
|
|
|
int RemoteBranchModel::rowCount(const QModelIndex & /* parent */) const
|
|
{
|
|
return branchCount();
|
|
}
|
|
|
|
QVariant RemoteBranchModel::data(const QModelIndex &index, int role) const
|
|
{
|
|
const int row = index.row();
|
|
switch (role) {
|
|
case Qt::DisplayRole:
|
|
return branchName(row);
|
|
case Qt::ToolTipRole:
|
|
if (m_branches.at(row).toolTip.isEmpty())
|
|
m_branches.at(row).toolTip = toolTip(m_branches.at(row).currentSHA);
|
|
return m_branches.at(row).toolTip;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return QVariant();
|
|
}
|
|
|
|
Qt::ItemFlags RemoteBranchModel::flags(const QModelIndex & /* index */) const
|
|
{
|
|
return m_flags;
|
|
}
|
|
|
|
QString RemoteBranchModel::toolTip(const QString &sha) const
|
|
{
|
|
// Show the sha description excluding diff as toolTip
|
|
QString output;
|
|
QString errorMessage;
|
|
if (!m_client->synchronousShow(m_workingDirectory, sha, &output, &errorMessage))
|
|
return errorMessage;
|
|
// Remove 'diff' output
|
|
const int diffPos = output.indexOf(QLatin1String("\ndiff --"));
|
|
if (diffPos != -1)
|
|
output.remove(diffPos, output.size() - diffPos);
|
|
return output;
|
|
}
|
|
|
|
bool RemoteBranchModel::runGitBranchCommand(const QString &workingDirectory, const QStringList &additionalArgs, QString *output, QString *errorMessage)
|
|
{
|
|
return m_client->synchronousBranchCmd(workingDirectory, additionalArgs, output, errorMessage);
|
|
}
|
|
|
|
void RemoteBranchModel::clear()
|
|
{
|
|
if (!m_branches.isEmpty()) {
|
|
m_branches.clear();
|
|
reset();
|
|
}
|
|
}
|
|
|
|
bool RemoteBranchModel::refreshBranches(const QString &workingDirectory, bool remoteBranches,
|
|
int *currentBranch, QString *errorMessage)
|
|
{
|
|
// Run branch command with verbose.
|
|
QStringList branchArgs;
|
|
branchArgs << QLatin1String(GitClient::noColorOption) << QLatin1String("-v");
|
|
QString output;
|
|
*currentBranch = -1;
|
|
if (remoteBranches)
|
|
branchArgs.push_back(QLatin1String("-r"));
|
|
if (!runGitBranchCommand(workingDirectory, branchArgs, &output, errorMessage))
|
|
return false;
|
|
if (debug)
|
|
qDebug() << Q_FUNC_INFO << workingDirectory << output;
|
|
// Parse output
|
|
m_workingDirectory = workingDirectory;
|
|
m_branches.clear();
|
|
const QStringList branches = output.split(QLatin1Char('\n'));
|
|
const int branchCount = branches.size();
|
|
bool isCurrent;
|
|
for (int b = 0; b < branchCount; b++) {
|
|
Branch newBranch;
|
|
if (newBranch.parse(branches.at(b), &isCurrent)) {
|
|
m_branches.push_back(newBranch);
|
|
if (isCurrent)
|
|
*currentBranch = b;
|
|
}
|
|
}
|
|
reset();
|
|
return true;
|
|
}
|
|
|
|
int RemoteBranchModel::findBranchByName(const QString &name) const
|
|
{
|
|
const int count = branchCount();
|
|
for (int i = 0; i < count; i++)
|
|
if (branchName(i) == name)
|
|
return i;
|
|
return -1;
|
|
}
|
|
|
|
// --- LocalBranchModel
|
|
LocalBranchModel::LocalBranchModel(GitClient *client, QObject *parent) :
|
|
RemoteBranchModel(client, parent),
|
|
m_typeHere(tr("<New branch>")),
|
|
m_typeHereToolTip(tr("Type to create a new branch")),
|
|
m_currentBranch(-1)
|
|
{
|
|
}
|
|
|
|
int LocalBranchModel::currentBranch() const
|
|
{
|
|
return m_currentBranch;
|
|
}
|
|
|
|
bool LocalBranchModel::isNewBranchRow(int row) const
|
|
{
|
|
return row >= branchCount();
|
|
}
|
|
|
|
Qt::ItemFlags LocalBranchModel::flags(const QModelIndex & index) const
|
|
{
|
|
if (isNewBranchRow(index))
|
|
return Qt::ItemIsEditable|Qt::ItemIsSelectable|Qt::ItemIsEnabled| Qt::ItemIsUserCheckable;
|
|
return RemoteBranchModel::flags(index) | Qt::ItemIsUserCheckable;
|
|
}
|
|
|
|
int LocalBranchModel::rowCount(const QModelIndex & /* parent */) const
|
|
{
|
|
return branchCount() + 1;
|
|
}
|
|
|
|
QVariant LocalBranchModel::data(const QModelIndex &index, int role) const
|
|
{
|
|
if (isNewBranchRow(index)) {
|
|
switch (role) {
|
|
case Qt::DisplayRole:
|
|
return m_typeHere;
|
|
case Qt::ToolTipRole:
|
|
return m_typeHereToolTip;
|
|
case Qt::CheckStateRole:
|
|
return QVariant(false);
|
|
}
|
|
return QVariant();
|
|
}
|
|
|
|
if (role == Qt::CheckStateRole)
|
|
return index.row() == m_currentBranch ? Qt::Checked : Qt::Unchecked;
|
|
return RemoteBranchModel::data(index, role);
|
|
}
|
|
|
|
void LocalBranchModel::clear()
|
|
{
|
|
m_currentBranch = -1;
|
|
m_newBranch.clear();
|
|
RemoteBranchModel::clear();
|
|
}
|
|
|
|
bool LocalBranchModel::refresh(const QString &workingDirectory, QString *errorMessage)
|
|
{
|
|
return refreshBranches(workingDirectory, false, &m_currentBranch, errorMessage);
|
|
}
|
|
|
|
bool LocalBranchModel::checkNewBranchName(const QString &name) const
|
|
{
|
|
// Syntax
|
|
const QRegExp pattern(QLatin1String("[a-zA-Z0-9-_]+"));
|
|
if (!pattern.exactMatch(name))
|
|
return false;
|
|
// existing
|
|
if (findBranchByName(name) != -1)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
bool LocalBranchModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
|
{
|
|
// Verify
|
|
if (role != Qt::EditRole || index.row() < branchCount())
|
|
return false;
|
|
const QString branchName = value.toString();
|
|
// Delay the signal as we don't want ourselves to be reset while
|
|
// in setData().
|
|
if (checkNewBranchName(branchName)) {
|
|
m_newBranch = branchName;
|
|
QTimer::singleShot(0, this, SLOT(slotNewBranchDelayedRefresh()));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void LocalBranchModel::slotNewBranchDelayedRefresh()
|
|
{
|
|
emit newBranchEntered(m_newBranch);
|
|
}
|
|
|
|
}
|
|
}
|
|
|