forked from qt-creator/qt-creator
Start on a gerrit plugin as a sub-plugin of git.
- Add a gerrit window that shows a list of changes and buttons to display, apply or check out a change. - Uses the new Qt 5 Json API or the utils/json classes for Qt 4. Tested-by: Tobias Hunger <tobias.hunger@nokia.com> Tested-by: Orgad Shaneh <orgads@gmail.com> Change-Id: I14c6c2c2de8f95fb785752c7319be8638b386a1e Reviewed-by: Eike Ziller <eike.ziller@nokia.com> Reviewed-by: Tobias Hunger <tobias.hunger@nokia.com>
This commit is contained in:
13
src/plugins/git/gerrit/gerrit.pri
Normal file
13
src/plugins/git/gerrit/gerrit.pri
Normal file
@@ -0,0 +1,13 @@
|
||||
INCLUDEPATH += $$PWD
|
||||
|
||||
SOURCES += $$PWD/gerritdialog.cpp \
|
||||
$$PWD/gerritmodel.cpp \
|
||||
$$PWD/gerritparameters.cpp \
|
||||
$$PWD/gerritplugin.cpp \
|
||||
$$PWD/gerritoptionspage.cpp
|
||||
|
||||
HEADERS += $$PWD/gerritdialog.h \
|
||||
$$PWD/gerritmodel.h \
|
||||
$$PWD/gerritparameters.h \
|
||||
$$PWD/gerritplugin.h \
|
||||
$$PWD/gerritoptionspage.h
|
||||
205
src/plugins/git/gerrit/gerritdialog.cpp
Normal file
205
src/plugins/git/gerrit/gerritdialog.cpp
Normal file
@@ -0,0 +1,205 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** Other Usage
|
||||
**
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#include "gerritdialog.h"
|
||||
#include "gerritmodel.h"
|
||||
#include "gerritparameters.h"
|
||||
|
||||
#include <utils/filterlineedit.h>
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QSpacerItem>
|
||||
#include <QTreeView>
|
||||
#include <QHeaderView>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QPushButton>
|
||||
#include <QDesktopServices>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QUrl>
|
||||
#include <QClipboard>
|
||||
#include <QApplication>
|
||||
#include <QProcess>
|
||||
|
||||
namespace Gerrit {
|
||||
namespace Internal {
|
||||
|
||||
GerritDialog::GerritDialog(const QSharedPointer<GerritParameters> &p,
|
||||
QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, m_parameters(p)
|
||||
, m_filterModel(new QSortFilterProxyModel(this))
|
||||
, m_model(new GerritModel(p, this))
|
||||
, m_treeView(new QTreeView)
|
||||
, m_filterLineEdit(new Utils::FilterLineEdit)
|
||||
, m_buttonBox(new QDialogButtonBox(QDialogButtonBox::Close))
|
||||
{
|
||||
setWindowTitle(tr("Gerrit %1@%2").arg(p->user, p->host));
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
QVBoxLayout *l = new QVBoxLayout(this);
|
||||
QHBoxLayout *hl = new QHBoxLayout;
|
||||
hl->addItem(new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding, QSizePolicy::Ignored));
|
||||
m_filterLineEdit->setFixedWidth(300);
|
||||
hl->addWidget(m_filterLineEdit);
|
||||
connect(m_filterLineEdit, SIGNAL(filterChanged(QString)),
|
||||
m_filterModel, SLOT(setFilterFixedString(QString)));
|
||||
m_filterModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
|
||||
l->addLayout(hl);
|
||||
l->addWidget(m_treeView);
|
||||
m_filterModel->setSourceModel(m_model);
|
||||
m_filterModel->setFilterRole(GerritModel::FilterRole);
|
||||
m_treeView->setModel(m_filterModel);
|
||||
m_treeView->setMinimumWidth(600);
|
||||
m_treeView->setUniformRowHeights(true);
|
||||
m_treeView->setRootIsDecorated(false);
|
||||
m_treeView->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
|
||||
QItemSelectionModel *selectionModel = m_treeView->selectionModel();
|
||||
connect(selectionModel, SIGNAL(currentChanged(QModelIndex,QModelIndex)),
|
||||
this, SLOT(slotEnableButtons()));
|
||||
connect(m_treeView, SIGNAL(doubleClicked(QModelIndex)),
|
||||
this, SLOT(slotDoubleClicked(QModelIndex)));
|
||||
m_openButton = addActionButton(tr("Open"), SLOT(slotOpenBrowser()));
|
||||
m_copyUrlButton = addActionButton(tr("Copy URL"), SLOT(slotCopyUrl()));
|
||||
m_displayButton = addActionButton(tr("Diff..."), SLOT(slotFetchDisplay()));
|
||||
m_applyButton = addActionButton(tr("Apply..."), SLOT(slotFetchApply()));
|
||||
m_checkoutButton = addActionButton(tr("Checkout..."), SLOT(slotFetchCheckout()));
|
||||
m_refreshButton = addActionButton(tr("Refresh"), SLOT(slotRefresh()));
|
||||
|
||||
connect(m_model, SIGNAL(refreshStateChanged(bool)),
|
||||
m_refreshButton, SLOT(setDisabled(bool)));
|
||||
connect(m_model, SIGNAL(refreshStateChanged(bool)),
|
||||
this, SLOT(slotRefreshStateChanged(bool)));
|
||||
l->addWidget(m_buttonBox);
|
||||
connect(m_buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
|
||||
connect(m_buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
|
||||
|
||||
slotEnableButtons();
|
||||
m_model->refresh();
|
||||
|
||||
resize(QSize(1200, 400));
|
||||
}
|
||||
|
||||
QPushButton *GerritDialog::addActionButton(const QString &text, const char *buttonSlot)
|
||||
{
|
||||
QPushButton *button = m_buttonBox->addButton(text, QDialogButtonBox::ActionRole);
|
||||
connect(button, SIGNAL(clicked()), this, buttonSlot);
|
||||
return button;
|
||||
}
|
||||
|
||||
GerritDialog::~GerritDialog()
|
||||
{
|
||||
}
|
||||
|
||||
void GerritDialog::slotDoubleClicked(const QModelIndex &i)
|
||||
{
|
||||
if (const QStandardItem *item = itemAt(i))
|
||||
openUrl(item->row());
|
||||
}
|
||||
|
||||
void GerritDialog::slotRefreshStateChanged(bool v)
|
||||
{
|
||||
if (!v && m_model->rowCount())
|
||||
for (int c = 0; c < GerritModel::ColumnCount; ++c)
|
||||
m_treeView->resizeColumnToContents(c);
|
||||
}
|
||||
|
||||
void GerritDialog::slotOpenBrowser()
|
||||
{
|
||||
if (const QStandardItem *item = currentItem())
|
||||
openUrl(item->row());
|
||||
}
|
||||
|
||||
void GerritDialog::slotFetchDisplay()
|
||||
{
|
||||
if (const QStandardItem *item = currentItem())
|
||||
emit fetchDisplay(m_model->change(item->row()));
|
||||
}
|
||||
|
||||
void GerritDialog::slotFetchApply()
|
||||
{
|
||||
if (const QStandardItem *item = currentItem())
|
||||
emit fetchApply(m_model->change(item->row()));
|
||||
}
|
||||
|
||||
void GerritDialog::slotFetchCheckout()
|
||||
{
|
||||
if (const QStandardItem *item = currentItem())
|
||||
emit fetchCheckout(m_model->change(item->row()));
|
||||
}
|
||||
|
||||
void GerritDialog::slotCopyUrl()
|
||||
{
|
||||
if (const QStandardItem *item = currentItem())
|
||||
QApplication::clipboard()->setText(m_model->change(item->row())->url);
|
||||
}
|
||||
|
||||
void GerritDialog::slotRefresh()
|
||||
{
|
||||
m_model->refresh();
|
||||
}
|
||||
|
||||
void GerritDialog::openUrl(int row) const
|
||||
{
|
||||
QDesktopServices::openUrl(QUrl(m_model->change(row)->url));
|
||||
}
|
||||
|
||||
const QStandardItem *GerritDialog::itemAt(const QModelIndex &i, int column) const
|
||||
{
|
||||
if (i.isValid()) {
|
||||
const QModelIndex source = m_filterModel->mapToSource(i);
|
||||
if (source.isValid())
|
||||
return m_model->item(source.row(), column);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const QStandardItem *GerritDialog::currentItem(int column) const
|
||||
{
|
||||
const QModelIndex index = m_treeView->selectionModel()->currentIndex();
|
||||
if (index.isValid())
|
||||
return itemAt(index, column);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GerritDialog::slotEnableButtons()
|
||||
{
|
||||
const bool valid = m_treeView->selectionModel()->currentIndex().isValid();
|
||||
m_openButton->setEnabled(valid);
|
||||
m_displayButton->setEnabled(valid);
|
||||
m_copyUrlButton->setEnabled(valid);
|
||||
m_applyButton->setEnabled(valid);
|
||||
m_checkoutButton->setEnabled(valid);
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Gerrit
|
||||
104
src/plugins/git/gerrit/gerritdialog.h
Normal file
104
src/plugins/git/gerrit/gerritdialog.h
Normal file
@@ -0,0 +1,104 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** Other Usage
|
||||
**
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef GERRIT_INTERNAL_GERRITDIALOG_H
|
||||
#define GERRIT_INTERNAL_GERRITDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QSharedPointer>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QTreeView;
|
||||
class QModelIndex;
|
||||
class QSortFilterProxyModel;
|
||||
class QStandardItem;
|
||||
class QPushButton;
|
||||
class QDialogButtonBox;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Utils {
|
||||
class FilterLineEdit;
|
||||
}
|
||||
namespace Gerrit {
|
||||
namespace Internal {
|
||||
class GerritParameters;
|
||||
class GerritModel;
|
||||
class GerritChange;
|
||||
|
||||
class GerritDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit GerritDialog(const QSharedPointer<GerritParameters> &p,
|
||||
QWidget *parent = 0);
|
||||
~GerritDialog();
|
||||
|
||||
signals:
|
||||
void fetchDisplay(const QSharedPointer<Gerrit::Internal::GerritChange> &);
|
||||
void fetchApply(const QSharedPointer<Gerrit::Internal::GerritChange> &);
|
||||
void fetchCheckout(const QSharedPointer<Gerrit::Internal::GerritChange> &);
|
||||
|
||||
private slots:
|
||||
void slotEnableButtons();
|
||||
void slotDoubleClicked(const QModelIndex &);
|
||||
void slotRefreshStateChanged(bool);
|
||||
void slotOpenBrowser();
|
||||
void slotFetchDisplay();
|
||||
void slotFetchApply();
|
||||
void slotFetchCheckout();
|
||||
void slotCopyUrl();
|
||||
void slotRefresh();
|
||||
|
||||
private:
|
||||
inline void openUrl(int row) const;
|
||||
const QStandardItem *itemAt(const QModelIndex &i, int column = 0) const;
|
||||
const QStandardItem *currentItem(int column = 0) const;
|
||||
QPushButton *addActionButton(const QString &text, const char *buttonSlot);
|
||||
|
||||
const QSharedPointer<GerritParameters> m_parameters;
|
||||
QSortFilterProxyModel *m_filterModel;
|
||||
GerritModel *m_model;
|
||||
QTreeView *m_treeView;
|
||||
Utils::FilterLineEdit *m_filterLineEdit;
|
||||
QDialogButtonBox *m_buttonBox;
|
||||
QPushButton *m_openButton;
|
||||
QPushButton *m_displayButton;
|
||||
QPushButton *m_applyButton;
|
||||
QPushButton *m_checkoutButton;
|
||||
QPushButton *m_copyUrlButton;
|
||||
QPushButton *m_refreshButton;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Gerrit
|
||||
|
||||
#endif // GERRIT_INTERNAL_GERRITDIALOG_H
|
||||
635
src/plugins/git/gerrit/gerritmodel.cpp
Normal file
635
src/plugins/git/gerrit/gerritmodel.cpp
Normal file
@@ -0,0 +1,635 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** Other Usage
|
||||
**
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#include "gerritmodel.h"
|
||||
#include "gerritparameters.h"
|
||||
|
||||
#include <coreplugin/vcsmanager.h>
|
||||
#include <coreplugin/icore.h>
|
||||
#include <coreplugin/progressmanager/progressmanager.h>
|
||||
#include <coreplugin/progressmanager/futureprogress.h>
|
||||
#include <vcsbase/vcsbaseoutputwindow.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
|
||||
#include <QStringList>
|
||||
#include <QProcess>
|
||||
#include <QVariant>
|
||||
#include <QUrl>
|
||||
#include <QTextStream>
|
||||
#include <QDesktopServices>
|
||||
#include <QDebug>
|
||||
#include <QScopedPointer>
|
||||
#if QT_VERSION >= 0x050000
|
||||
# include <QJsonDocument>
|
||||
# include <QJsonValue>
|
||||
# include <QJsonArray>
|
||||
# include <QJsonObject>
|
||||
#else
|
||||
# include <utils/json.h>
|
||||
# include <utils/qtcassert.h>
|
||||
#endif
|
||||
|
||||
enum { debug = 0 };
|
||||
|
||||
namespace Gerrit {
|
||||
namespace Internal {
|
||||
|
||||
// Format default Url for a change
|
||||
static inline QString defaultUrl(const QSharedPointer<GerritParameters> &p, int gerritNumber)
|
||||
{
|
||||
QString result = p->https ? QLatin1String("https://") : QLatin1String("http://");
|
||||
result += p->host;
|
||||
result += QLatin1Char('/');
|
||||
result += QString::number(gerritNumber);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Format approvals as "John Doe: -1, ...".
|
||||
QString GerritPatchSet::approvalsToolTip() const
|
||||
{
|
||||
QString result;
|
||||
if (const int approvalsCount = approvals.size()) {
|
||||
const QString sepComma = QLatin1String(", ");
|
||||
const QString sepColon = QLatin1String(": ");
|
||||
for (int i = 0; i < approvalsCount; ++ i) {
|
||||
if (i)
|
||||
result += sepComma;
|
||||
result += approvals.at(i).first;
|
||||
result += sepColon;
|
||||
result += QString::number(approvals.at(i).second);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QString GerritChange::toolTip() const
|
||||
{
|
||||
static const QString format = GerritModel::tr(
|
||||
"Subject: %1\nNumber: %2 Id: %3\nOwner: %4 <%5>\n"
|
||||
"Project: %6 Branch: %7\nStatus: %8, %9\n"
|
||||
"URL: %10\nApprovals: %11");
|
||||
return format.arg(title).arg(number).arg(id, owner, email, branch)
|
||||
.arg(currentPatchSet.patchSetNumber)
|
||||
.arg(status, lastUpdated.toString(Qt::DefaultLocaleShortDate),
|
||||
url, currentPatchSet.approvalsToolTip());
|
||||
}
|
||||
|
||||
QString GerritChange::filterString() const
|
||||
{
|
||||
const QChar blank = QLatin1Char(' ');
|
||||
QString result = QString::number(number) + blank + title + blank
|
||||
+ owner + blank + project + blank
|
||||
+ branch + blank + status;
|
||||
foreach (const GerritPatchSet::Approval &a, currentPatchSet.approvals) {
|
||||
result += blank;
|
||||
result += a.first;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QStringList GerritChange::gitFetchArguments(const QSharedPointer<GerritParameters> &p) const
|
||||
{
|
||||
QStringList arguments;
|
||||
const QString url = QLatin1String("ssh://") + p->sshHostArgument()
|
||||
+ QLatin1Char(':') + QString::number(p->port) + QLatin1Char('/')
|
||||
+ project;
|
||||
arguments << QLatin1String("fetch") << url << currentPatchSet.ref;
|
||||
return arguments;
|
||||
}
|
||||
|
||||
// Helper class that runs ssh gerrit queries from a list of query argument
|
||||
// string lists,
|
||||
// see http://gerrit.googlecode.com/svn/documentation/2.1.5/cmd-query.html
|
||||
// In theory, querying uses a continuation/limit protocol, but we assume
|
||||
// we will never reach a limit with those queries.
|
||||
|
||||
class QueryContext : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
typedef QList<QStringList> StringListList;
|
||||
|
||||
QueryContext(const StringListList &queries,
|
||||
const QSharedPointer<GerritParameters> &p,
|
||||
QObject *parent = 0);
|
||||
|
||||
~QueryContext();
|
||||
|
||||
public slots:
|
||||
void start();
|
||||
|
||||
signals:
|
||||
void queryFinished(const QByteArray &);
|
||||
void finished();
|
||||
|
||||
private slots:
|
||||
void processError(QProcess::ProcessError);
|
||||
void processFinished(int exitCode, QProcess::ExitStatus);
|
||||
void readyReadStandardError();
|
||||
void readyReadStandardOutput();
|
||||
|
||||
private:
|
||||
void startQuery(const QStringList &queryArguments);
|
||||
void errorTermination(const QString &msg);
|
||||
|
||||
const QSharedPointer<GerritParameters> m_parameters;
|
||||
const StringListList m_queries;
|
||||
QProcess m_process;
|
||||
QString m_binary;
|
||||
QByteArray m_output;
|
||||
int m_currentQuery;
|
||||
QFutureInterface<void> m_progress;
|
||||
QStringList m_baseArguments;
|
||||
};
|
||||
|
||||
QueryContext::QueryContext(const StringListList &queries,
|
||||
const QSharedPointer<GerritParameters> &p,
|
||||
QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_parameters(p)
|
||||
, m_queries(queries)
|
||||
, m_currentQuery(0)
|
||||
, m_baseArguments(p->baseCommandArguments())
|
||||
{
|
||||
connect(&m_process, SIGNAL(readyReadStandardError()),
|
||||
this, SLOT(readyReadStandardError()));
|
||||
connect(&m_process, SIGNAL(readyReadStandardOutput()),
|
||||
this, SLOT(readyReadStandardOutput()));
|
||||
connect(&m_process, SIGNAL(finished(int,QProcess::ExitStatus)),
|
||||
this, SLOT(processFinished(int,QProcess::ExitStatus)));
|
||||
connect(&m_process, SIGNAL(error(QProcess::ProcessError)),
|
||||
this, SLOT(processError(QProcess::ProcessError)));
|
||||
m_progress.setProgressRange(0, m_queries.size());
|
||||
|
||||
// Determine binary and common command line arguments.
|
||||
m_baseArguments << QLatin1String("query") << QLatin1String("--current-patch-set")
|
||||
<< QLatin1String("--format=JSON");
|
||||
m_binary = m_baseArguments.front();
|
||||
m_baseArguments.pop_front();
|
||||
}
|
||||
|
||||
QueryContext::~QueryContext()
|
||||
{
|
||||
if (m_progress.isRunning())
|
||||
m_progress.reportFinished();
|
||||
m_process.disconnect(this);
|
||||
Utils::SynchronousProcess::stopProcess(m_process);
|
||||
}
|
||||
|
||||
void QueryContext::start()
|
||||
{
|
||||
Core::ProgressManager *pm = Core::ICore::instance()->progressManager();
|
||||
Core::FutureProgress *fp = pm->addTask(m_progress.future(), tr("Gerrit"),
|
||||
QLatin1String("gerrit-query"));
|
||||
fp->setKeepOnFinish(Core::FutureProgress::HideOnFinish);
|
||||
m_progress.reportStarted();
|
||||
startQuery(m_queries.front()); // Order: synchronous call to error handling if something goes wrong.
|
||||
}
|
||||
|
||||
void QueryContext::startQuery(const QStringList &queryArguments)
|
||||
{
|
||||
const QStringList arguments = m_baseArguments + queryArguments;
|
||||
VcsBase::VcsBaseOutputWindow::instance()
|
||||
->appendCommand(m_process.workingDirectory(), m_binary, arguments);
|
||||
m_process.start(m_binary, arguments);
|
||||
m_process.closeWriteChannel();
|
||||
}
|
||||
|
||||
void QueryContext::errorTermination(const QString &msg)
|
||||
{
|
||||
m_progress.reportCanceled();
|
||||
m_progress.reportFinished();
|
||||
VcsBase::VcsBaseOutputWindow::instance()->appendError(msg);
|
||||
emit finished();
|
||||
}
|
||||
|
||||
void QueryContext::processError(QProcess::ProcessError e)
|
||||
{
|
||||
const QString msg = tr("Error running %1: %2").arg(m_binary, m_process.errorString());
|
||||
if (e == QProcess::FailedToStart) {
|
||||
errorTermination(msg);
|
||||
} else {
|
||||
VcsBase::VcsBaseOutputWindow::instance()->appendError(msg);
|
||||
}
|
||||
}
|
||||
|
||||
void QueryContext::processFinished(int exitCode, QProcess::ExitStatus es)
|
||||
{
|
||||
if (es != QProcess::NormalExit) {
|
||||
errorTermination(tr("%1 crashed.").arg(m_binary));
|
||||
return;
|
||||
} else if (exitCode) {
|
||||
errorTermination(tr("%1 returned %2.").arg(m_binary).arg(exitCode));
|
||||
return;
|
||||
}
|
||||
emit queryFinished(m_output);
|
||||
m_output.clear();
|
||||
|
||||
if (++m_currentQuery >= m_queries.size()) {
|
||||
m_progress.reportFinished();
|
||||
emit finished();
|
||||
} else {
|
||||
m_progress.setProgressValue(m_currentQuery);
|
||||
startQuery(m_queries.at(m_currentQuery));
|
||||
}
|
||||
}
|
||||
|
||||
void QueryContext::readyReadStandardError()
|
||||
{
|
||||
VcsBase::VcsBaseOutputWindow::instance()->appendError(QString::fromLocal8Bit(m_process.readAllStandardError()));
|
||||
}
|
||||
|
||||
void QueryContext::readyReadStandardOutput()
|
||||
{
|
||||
m_output.append(m_process.readAllStandardOutput());
|
||||
}
|
||||
|
||||
GerritModel::GerritModel(const QSharedPointer<GerritParameters> &p, QObject *parent)
|
||||
: QStandardItemModel(0, ColumnCount, parent)
|
||||
, m_parameters(p)
|
||||
, m_query(0)
|
||||
{
|
||||
QStringList headers;
|
||||
headers << QLatin1String("#") << tr("Title") << tr("Owner")
|
||||
<< tr("Date") << tr("Project")
|
||||
<< tr("Approvals") << tr("Status");
|
||||
setHorizontalHeaderLabels(headers);
|
||||
}
|
||||
|
||||
GerritModel::~GerritModel()
|
||||
{
|
||||
}
|
||||
|
||||
GerritChangePtr GerritModel::change(int row) const
|
||||
{
|
||||
if (row >= 0 && row < rowCount())
|
||||
return qvariant_cast<GerritChangePtr>(item(row, 0)->data(GerritChangeRole));
|
||||
return GerritChangePtr(new GerritChange);
|
||||
}
|
||||
|
||||
int GerritModel::indexOf(int gerritNumber) const
|
||||
{
|
||||
const int numRows = rowCount();
|
||||
for (int r = 0; r < numRows; ++r)
|
||||
if (change(r)->number == gerritNumber)
|
||||
return r;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void GerritModel::refresh()
|
||||
{
|
||||
typedef QueryContext::StringListList StringListList;
|
||||
|
||||
if (m_query) {
|
||||
qWarning("%s: Another query is still running", Q_FUNC_INFO);
|
||||
return;
|
||||
}
|
||||
clearData();
|
||||
|
||||
// Assemble list of queries
|
||||
const QString statusOpenQuery = QLatin1String("status:open");
|
||||
|
||||
StringListList queries;
|
||||
QStringList queryArguments;
|
||||
if (m_parameters->user.isEmpty()) {
|
||||
queryArguments << statusOpenQuery;
|
||||
queries.push_back(queryArguments);
|
||||
queryArguments.clear();
|
||||
} else {
|
||||
// Owned by:
|
||||
queryArguments << (QLatin1String("owner:") + m_parameters->user) << statusOpenQuery;
|
||||
queries.push_back(queryArguments);
|
||||
queryArguments.clear();
|
||||
// For Review by:
|
||||
queryArguments << (QLatin1String("reviewer:") + m_parameters->user) << statusOpenQuery;
|
||||
queries.push_back(queryArguments);
|
||||
queryArguments.clear();
|
||||
}
|
||||
// Any custom queries?
|
||||
if (!m_parameters->additionalQueries.isEmpty()) {
|
||||
foreach (const QString &customQuery, m_parameters->additionalQueries.split(QString::SkipEmptyParts)) {
|
||||
queryArguments = customQuery.split(QLatin1Char(' '), QString::SkipEmptyParts);
|
||||
if (!queryArguments.isEmpty()) {
|
||||
queries.push_back(queryArguments);
|
||||
queryArguments.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_query = new QueryContext(queries, m_parameters, this);
|
||||
connect(m_query, SIGNAL(queryFinished(QByteArray)),
|
||||
this, SLOT(queryFinished(QByteArray)));
|
||||
connect(m_query, SIGNAL(finished()),
|
||||
this, SLOT(queriesFinished()));
|
||||
emit refreshStateChanged(true);
|
||||
m_query->start();
|
||||
}
|
||||
|
||||
void GerritModel::clearData()
|
||||
{
|
||||
if (const int rows = rowCount())
|
||||
removeRows(0, rows);
|
||||
}
|
||||
|
||||
/* Parse gerrit query Json output.
|
||||
* See http://gerrit.googlecode.com/svn/documentation/2.1.5/cmd-query.html
|
||||
* Note: The url will be present only if "canonicalWebUrl" is configured
|
||||
* in gerrit.config.
|
||||
\code
|
||||
{"project":"qt/qtbase","branch":"master","id":"I6601ca68c427b909680423ae81802f1ed5cd178a",
|
||||
"number":"24143","subject":"bla","owner":{"name":"Hans Mustermann","email":"hm@acme.com"},
|
||||
"url":"https://...","lastUpdated":1335127888,"sortKey":"001c8fc300005e4f",
|
||||
"open":true,"status":"NEW","currentPatchSet":
|
||||
{"number":"1","revision":"0a1e40c78ef16f7652472f4b4bb4c0addeafbf82",
|
||||
"ref":"refs/changes/43/24143/1",
|
||||
"uploader":{"name":"Hans Mustermann","email":"hm@acme.com"},
|
||||
"approvals":[{"type":"SRVW","description":"Sanity Review","value":"1",
|
||||
"grantedOn":1335127888,"by":{
|
||||
"name":"Qt Sanity Bot","email":"qt_sanity_bot@ovi.com"}}]}}
|
||||
\endcode
|
||||
*/
|
||||
|
||||
#if QT_VERSION >= 0x050000
|
||||
// Qt 5 using the QJsonDocument classes.
|
||||
static QList<GerritChangePtr> parseOutput(const QSharedPointer<GerritParameters> ¶meters,
|
||||
const QByteArray &output)
|
||||
{
|
||||
// The output consists of separate lines containing a document each
|
||||
const QString typeKey = QLatin1String("type");
|
||||
const QString idKey = QLatin1String("id");
|
||||
const QString branchKey = QLatin1String("branch");
|
||||
const QString numberKey = QLatin1String("number");
|
||||
const QString ownerKey = QLatin1String("owner");
|
||||
const QString ownerNameKey = QLatin1String("name");
|
||||
const QString ownerEmailKey = QLatin1String("email");
|
||||
const QString statusKey = QLatin1String("status");
|
||||
const QString projectKey = QLatin1String("project");
|
||||
const QString titleKey = QLatin1String("subject");
|
||||
const QString urlKey = QLatin1String("url");
|
||||
const QString patchSetKey = QLatin1String("currentPatchSet");
|
||||
const QString refKey = QLatin1String("ref");
|
||||
const QString approvalsKey = QLatin1String("approvals");
|
||||
const QString approvalsValueKey = QLatin1String("value");
|
||||
const QString approvalsByKey = QLatin1String("by");
|
||||
const QString lastUpdatedKey = QLatin1String("lastUpdated");
|
||||
const QList<QByteArray> lines = output.split('\n');
|
||||
QList<GerritChangePtr> result;
|
||||
result.reserve(lines.size());
|
||||
|
||||
foreach (const QByteArray &line, lines) {
|
||||
if (line.isEmpty())
|
||||
continue;
|
||||
QJsonParseError error;
|
||||
const QJsonDocument doc = QJsonDocument::fromJson(line, &error);
|
||||
if (doc.isNull()) {
|
||||
qWarning("Parse error: '%s' -> %s", line.constData(),
|
||||
qPrintable(error.errorString()));
|
||||
continue;
|
||||
}
|
||||
GerritChangePtr change(new GerritChange);
|
||||
const QJsonObject object = doc.object();
|
||||
// Skip stats line: {"type":"stats","rowCount":9,"runTimeMilliseconds":13}
|
||||
if (!object.value(typeKey).toString().isEmpty())
|
||||
continue;
|
||||
// Read current patch set.
|
||||
const QJsonObject patchSet = object.value(patchSetKey).toObject();
|
||||
change->currentPatchSet.patchSetNumber = qMax(1, patchSet.value(numberKey).toString().toInt());
|
||||
change->currentPatchSet.ref = patchSet.value(refKey).toString();
|
||||
const QJsonArray approvalsJ = patchSet.value(approvalsKey).toArray();
|
||||
const int ac = approvalsJ.size();
|
||||
for (int a = 0; a < ac; ++a) {
|
||||
const QJsonObject ao = approvalsJ.at(a).toObject();
|
||||
const int value = ao.value(approvalsValueKey).toString().toInt();
|
||||
const QString by = ao.value(approvalsByKey).toObject().value(ownerNameKey).toString();
|
||||
change->currentPatchSet.approvals.push_back(GerritChange::Approval(by, value));
|
||||
}
|
||||
// Remaining
|
||||
change->number = object.value(numberKey).toString().toInt();
|
||||
change->url = object.value(urlKey).toString();
|
||||
if (change->url.isEmpty()) // No "canonicalWebUrl" is in gerrit.config.
|
||||
change->url = defaultUrl(parameters, change->number);
|
||||
change->id = object.value(idKey).toString();
|
||||
change->title = object.value(titleKey).toString();
|
||||
const QJsonObject ownerJ = object.value(ownerKey).toObject();
|
||||
change->owner = ownerJ.value(ownerNameKey).toString();
|
||||
change->email = ownerJ.value(ownerEmailKey).toString();
|
||||
change->project = object.value(projectKey).toString();
|
||||
change->branch = object.value(branchKey).toString();
|
||||
change->status = object.value(statusKey).toString();
|
||||
if (const int timeT = qRound(object.value(lastUpdatedKey).toDouble()))
|
||||
change->lastUpdated = QDateTime::fromTime_t(timeT);
|
||||
if (change->isValid()) {
|
||||
result.push_back(change);
|
||||
} else {
|
||||
qWarning("%s: Parse error in line '%s'.", Q_FUNC_INFO, line.constData());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#else // QT_VERSION >= 0x050000
|
||||
// Qt 4.8 using the Json classes from utils.
|
||||
|
||||
static inline int jsonIntMember(Utils::JsonObjectValue *object,
|
||||
const QString &key,
|
||||
int defaultValue = 0)
|
||||
{
|
||||
if (Utils::JsonValue *v = object->member(key)) {
|
||||
if (Utils::JsonIntValue *iv = v->toInt())
|
||||
return iv->value();
|
||||
if (Utils::JsonStringValue *sv = v->toString()) {
|
||||
bool ok;
|
||||
const int result = sv->value().toInt(&ok);
|
||||
if (ok)
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
static inline QString jsonStringMember(Utils::JsonObjectValue *object,
|
||||
const QString &key)
|
||||
{
|
||||
if (Utils::JsonValue *v = object->member(key))
|
||||
if (Utils::JsonStringValue *sv = v->toString())
|
||||
return sv->value();
|
||||
return QString();
|
||||
}
|
||||
|
||||
static QList<GerritChangePtr> parseOutput(const QSharedPointer<GerritParameters> ¶meters,
|
||||
const QByteArray &output)
|
||||
{
|
||||
// The output consists of separate lines containing a document each
|
||||
const QList<QByteArray> lines = output.split('\n');
|
||||
const QString typeKey = QLatin1String("type");
|
||||
const QString idKey = QLatin1String("id");
|
||||
const QString branchKey = QLatin1String("branch");
|
||||
const QString numberKey = QLatin1String("number");
|
||||
const QString ownerKey = QLatin1String("owner");
|
||||
const QString ownerNameKey = QLatin1String("name");
|
||||
const QString ownerEmailKey = QLatin1String("email");
|
||||
const QString statusKey = QLatin1String("status");
|
||||
const QString projectKey = QLatin1String("project");
|
||||
const QString titleKey = QLatin1String("subject");
|
||||
const QString urlKey = QLatin1String("url");
|
||||
const QString patchSetKey = QLatin1String("currentPatchSet");
|
||||
const QString refKey = QLatin1String("ref");
|
||||
const QString approvalsKey = QLatin1String("approvals");
|
||||
const QString approvalsValueKey = QLatin1String("value");
|
||||
const QString approvalsByKey = QLatin1String("by");
|
||||
const QString lastUpdatedKey = QLatin1String("lastUpdated");
|
||||
QList<GerritChangePtr> result;
|
||||
result.reserve(lines.size());
|
||||
|
||||
foreach (const QByteArray &line, lines) {
|
||||
if (line.isEmpty())
|
||||
continue;
|
||||
QScopedPointer<Utils::JsonValue> objectValue(Utils::JsonValue::create(QString::fromAscii(line)));
|
||||
if (objectValue.isNull()) {
|
||||
qWarning("Parse error: '%s'", line.constData());
|
||||
continue;
|
||||
}
|
||||
Utils::JsonObjectValue *object = objectValue->toObject();
|
||||
QTC_ASSERT(object, continue );
|
||||
GerritChangePtr change(new GerritChange);
|
||||
// Skip stats line: {"type":"stats","rowCount":9,"runTimeMilliseconds":13}
|
||||
if (jsonStringMember(object, typeKey) == QLatin1String("stats"))
|
||||
continue;
|
||||
// Read current patch set.
|
||||
if (Utils::JsonValue *patchSetValue = object->member(patchSetKey)) {
|
||||
Utils::JsonObjectValue *patchSet = patchSetValue->toObject();
|
||||
QTC_ASSERT(patchSet, continue );
|
||||
const int n = jsonIntMember(patchSet, numberKey);
|
||||
change->currentPatchSet.patchSetNumber = qMax(1, n);
|
||||
change->currentPatchSet.ref = jsonStringMember(patchSet, refKey);
|
||||
if (Utils::JsonValue *approvalsV = patchSet->member(approvalsKey)) {
|
||||
Utils::JsonArrayValue *approvals = approvalsV->toArray();
|
||||
QTC_ASSERT(approvals, continue );
|
||||
foreach (Utils::JsonValue *c, approvals->elements()) {
|
||||
Utils::JsonObjectValue *oc = c->toObject();
|
||||
QTC_ASSERT(oc, break );
|
||||
const int value = jsonIntMember(oc, approvalsValueKey);
|
||||
QString by;
|
||||
if (Utils::JsonValue *byV = oc->member(approvalsByKey)) {
|
||||
Utils::JsonObjectValue *byO = byV->toObject();
|
||||
QTC_ASSERT(byO, break );
|
||||
by = jsonStringMember(byO, ownerNameKey);
|
||||
}
|
||||
change->currentPatchSet.approvals.push_back(GerritChange::Approval(by, value));
|
||||
}
|
||||
}
|
||||
} // patch set
|
||||
// Remaining
|
||||
|
||||
change->number = jsonIntMember(object, numberKey);
|
||||
change->url = jsonStringMember(object, urlKey);
|
||||
if (change->url.isEmpty()) // No "canonicalWebUrl" is in gerrit.config.
|
||||
change->url = defaultUrl(parameters, change->number);
|
||||
change->id = jsonStringMember(object, idKey);
|
||||
change->title = jsonStringMember(object, titleKey);
|
||||
if (Utils::JsonValue *ownerV = object->member(ownerKey)) {
|
||||
Utils::JsonObjectValue *ownerO = ownerV->toObject();
|
||||
QTC_ASSERT(ownerO, continue );
|
||||
change->owner = jsonStringMember(ownerO, ownerNameKey);
|
||||
change->email = jsonStringMember(ownerO, ownerEmailKey);
|
||||
}
|
||||
change->project = jsonStringMember(object, projectKey);
|
||||
change->branch = jsonStringMember(object, branchKey);
|
||||
change->status = jsonStringMember(object, statusKey);
|
||||
|
||||
if (const int timeT = jsonIntMember(object, lastUpdatedKey))
|
||||
change->lastUpdated = QDateTime::fromTime_t(timeT);
|
||||
if (change->isValid()) {
|
||||
result.push_back(change);
|
||||
} else {
|
||||
qWarning("%s: Parse error in line '%s'.", Q_FUNC_INFO, line.constData());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#endif // QT_VERSION < 0x050000
|
||||
|
||||
bool changeDateLessThan(const GerritChangePtr &c1, const GerritChangePtr &c2)
|
||||
{
|
||||
return c1->lastUpdated < c2->lastUpdated;
|
||||
}
|
||||
|
||||
void GerritModel::queryFinished(const QByteArray &output)
|
||||
{
|
||||
QList<GerritChangePtr> changes = parseOutput(m_parameters, output);
|
||||
qStableSort(changes.begin(), changes.end(), changeDateLessThan);
|
||||
foreach (const GerritChangePtr &c, changes) {
|
||||
// Avoid duplicate entries for example in the (unlikely)
|
||||
// case people do self-reviews.
|
||||
if (indexOf(c->number) == -1) {
|
||||
const QVariant filterV = QVariant(c->filterString());
|
||||
const QString toolTip = c->toolTip();
|
||||
const QVariant changeV = qVariantFromValue(c);
|
||||
QList<QStandardItem *> row;
|
||||
for (int i = 0; i < GerritModel::ColumnCount; ++i) {
|
||||
QStandardItem *item = new QStandardItem;
|
||||
item->setData(changeV, GerritModel::GerritChangeRole);
|
||||
item->setData(filterV, GerritModel::FilterRole);
|
||||
item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
||||
item->setToolTip(toolTip);
|
||||
row.append(item);
|
||||
}
|
||||
row[NumberColumn]->setText(QString::number(c->number));
|
||||
row[TitleColumn]->setText(c->title);
|
||||
row[OwnerColumn]->setText(c->owner);
|
||||
row[DateColumn]->setText(c->lastUpdated.toString(Qt::SystemLocaleShortDate));
|
||||
QString project = c->project;
|
||||
if (c->branch != QLatin1String("master"))
|
||||
project += QLatin1String(" (") + c->branch + QLatin1Char(')');
|
||||
row[ProjectColumn]->setText(project);
|
||||
row[StatusColumn]->setText(c->status);
|
||||
QString approvals;
|
||||
foreach (const GerritChange::Approval &a, c->currentPatchSet.approvals) {
|
||||
if (!approvals.isEmpty())
|
||||
approvals.append(QLatin1String(", "));
|
||||
approvals.append(QString::number(a.second));
|
||||
}
|
||||
row[ApprovalsColumn]->setText(approvals);
|
||||
appendRow(row);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GerritModel::queriesFinished()
|
||||
{
|
||||
m_query->deleteLater();
|
||||
m_query = 0;
|
||||
emit refreshStateChanged(false);
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Gerrit
|
||||
|
||||
#include "gerritmodel.moc"
|
||||
139
src/plugins/git/gerrit/gerritmodel.h
Normal file
139
src/plugins/git/gerrit/gerritmodel.h
Normal file
@@ -0,0 +1,139 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** Other Usage
|
||||
**
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef GERRIT_INTERNAL_GERRITMODEL_H
|
||||
#define GERRIT_INTERNAL_GERRITMODEL_H
|
||||
|
||||
#include <QStandardItemModel>
|
||||
#include <QSharedPointer>
|
||||
#include <QMetaType>
|
||||
#include <QPair>
|
||||
#include <QDateTime>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QUrl;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Gerrit {
|
||||
namespace Internal {
|
||||
class GerritParameters;
|
||||
class QueryContext;
|
||||
|
||||
class GerritPatchSet {
|
||||
public:
|
||||
typedef QPair<QString, int> Approval;
|
||||
|
||||
GerritPatchSet() : patchSetNumber(1) {}
|
||||
QString approvalsToolTip() const;
|
||||
|
||||
QString ref;
|
||||
int patchSetNumber;
|
||||
QList<Approval> approvals;
|
||||
};
|
||||
|
||||
class GerritChange
|
||||
{
|
||||
public:
|
||||
typedef QPair<QString, int> Approval;
|
||||
|
||||
GerritChange() : number(0) {}
|
||||
|
||||
bool isValid() const { return number && !url.isEmpty() && !project.isEmpty(); }
|
||||
QString toolTip() const;
|
||||
QString filterString() const;
|
||||
QStringList gitFetchArguments(const QSharedPointer<GerritParameters> &p) const;
|
||||
|
||||
QString url;
|
||||
int number;
|
||||
QString id;
|
||||
QString title;
|
||||
QString owner;
|
||||
QString email;
|
||||
QString project;
|
||||
QString branch;
|
||||
QString status;
|
||||
QDateTime lastUpdated;
|
||||
GerritPatchSet currentPatchSet;
|
||||
};
|
||||
|
||||
typedef QSharedPointer<GerritChange> GerritChangePtr;
|
||||
|
||||
class GerritModel : public QStandardItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum Columns {
|
||||
NumberColumn,
|
||||
TitleColumn,
|
||||
OwnerColumn,
|
||||
DateColumn,
|
||||
ProjectColumn,
|
||||
ApprovalsColumn,
|
||||
StatusColumn,
|
||||
ColumnCount
|
||||
};
|
||||
|
||||
enum CustomModelRoles {
|
||||
FilterRole = Qt::UserRole + 1,
|
||||
GerritChangeRole = Qt::UserRole + 2
|
||||
};
|
||||
GerritModel(const QSharedPointer<GerritParameters> &, QObject *parent = 0);
|
||||
~GerritModel();
|
||||
|
||||
GerritChangePtr change(int row) const;
|
||||
|
||||
int indexOf(int gerritNumber) const;
|
||||
|
||||
public slots:
|
||||
void refresh();
|
||||
|
||||
signals:
|
||||
void refreshStateChanged(bool isRefreshing); // For disabling the "Refresh" button.
|
||||
|
||||
private slots:
|
||||
void queryFinished(const QByteArray &);
|
||||
void queriesFinished();
|
||||
void clearData();
|
||||
|
||||
private:
|
||||
inline bool evaluateQuery(QString *errorMessage);
|
||||
|
||||
const QSharedPointer<GerritParameters> m_parameters;
|
||||
QueryContext *m_query;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Gerrit
|
||||
|
||||
Q_DECLARE_METATYPE(Gerrit::Internal::GerritChangePtr)
|
||||
|
||||
#endif // GERRIT_INTERNAL_GERRITMODEL_H
|
||||
139
src/plugins/git/gerrit/gerritoptionspage.cpp
Normal file
139
src/plugins/git/gerrit/gerritoptionspage.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** Other Usage
|
||||
**
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#include "gerritoptionspage.h"
|
||||
#include "gerritparameters.h"
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
#include <utils/pathchooser.h>
|
||||
|
||||
#include <QLineEdit>
|
||||
#include <QSpinBox>
|
||||
#include <QCheckBox>
|
||||
#include <QFormLayout>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
namespace Gerrit {
|
||||
namespace Internal {
|
||||
|
||||
GerritOptionsPage::GerritOptionsPage(const QSharedPointer<GerritParameters> &p,
|
||||
QObject *parent)
|
||||
: VcsBase::VcsBaseOptionsPage(parent)
|
||||
, m_parameters(p)
|
||||
{
|
||||
}
|
||||
|
||||
QString GerritOptionsPage::displayName() const
|
||||
{
|
||||
return tr("Gerrit");
|
||||
}
|
||||
|
||||
QWidget *GerritOptionsPage::createPage(QWidget *parent)
|
||||
{
|
||||
GerritOptionsWidget *gow = new GerritOptionsWidget(parent);
|
||||
gow->setParameters(*m_parameters);
|
||||
m_widget = gow;
|
||||
return gow;
|
||||
}
|
||||
|
||||
void GerritOptionsPage::apply()
|
||||
{
|
||||
if (GerritOptionsWidget *w = m_widget.data()) {
|
||||
const GerritParameters newParameters = w->parameters();
|
||||
if (newParameters != *m_parameters) {
|
||||
*m_parameters = newParameters;
|
||||
m_parameters->toSettings(Core::ICore::instance()->settings());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool GerritOptionsPage::matches(const QString &s) const
|
||||
{
|
||||
return s.contains(QLatin1String("gerrit"), Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
GerritOptionsWidget::GerritOptionsWidget(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, m_hostLineEdit(new QLineEdit(this))
|
||||
, m_userLineEdit(new QLineEdit(this))
|
||||
, m_sshChooser(new Utils::PathChooser)
|
||||
, m_portSpinBox(new QSpinBox(this))
|
||||
, m_additionalQueriesLineEdit(new QLineEdit(this))
|
||||
, m_httpsCheckBox(new QCheckBox(tr("HTTPS")))
|
||||
{
|
||||
QFormLayout *formLayout = new QFormLayout(this);
|
||||
formLayout->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow);
|
||||
formLayout->addRow(tr("&Host: "), m_hostLineEdit);
|
||||
formLayout->addRow(tr("&User: "), m_userLineEdit);
|
||||
m_sshChooser->setExpectedKind(Utils::PathChooser::ExistingCommand);
|
||||
m_sshChooser->setCommandVersionArguments(QStringList(QLatin1String("-V")));
|
||||
formLayout->addRow(tr("&ssh: "), m_sshChooser);
|
||||
m_portSpinBox->setMinimum(1);
|
||||
m_portSpinBox->setMaximum(65535);
|
||||
formLayout->addRow(tr("&Port: "), m_portSpinBox);
|
||||
formLayout->addRow(tr("&Additional queries: "), m_additionalQueriesLineEdit);
|
||||
m_additionalQueriesLineEdit->setToolTip(tr(
|
||||
"A comma-separated list of additional queries.\n"
|
||||
"For example \"status:staged,status:integrating\""
|
||||
" can be used to show the status of the Continuous Integration\n"
|
||||
"of the Qt project."));
|
||||
formLayout->addRow(tr("P&rotocol:"), m_httpsCheckBox);
|
||||
m_httpsCheckBox->setToolTip(tr(
|
||||
"Determines the protocol used to form a URL in case\n"
|
||||
"\"canonicalWebUrl\" is not configured in the file\n"
|
||||
"\"gerrit.config\"."));
|
||||
}
|
||||
|
||||
GerritParameters GerritOptionsWidget::parameters() const
|
||||
{
|
||||
GerritParameters result;
|
||||
result.host = m_hostLineEdit->text().trimmed();
|
||||
result.user = m_userLineEdit->text().trimmed();
|
||||
result.ssh = m_sshChooser->path();
|
||||
result.port = m_portSpinBox->value();
|
||||
result.additionalQueries = m_additionalQueriesLineEdit->text().trimmed();
|
||||
result.https = m_httpsCheckBox->isChecked();
|
||||
return result;
|
||||
}
|
||||
|
||||
void GerritOptionsWidget::setParameters(const GerritParameters &p)
|
||||
{
|
||||
m_hostLineEdit->setText(p.host);
|
||||
m_userLineEdit->setText(p.user);
|
||||
m_sshChooser->setPath(p.ssh);
|
||||
m_portSpinBox->setValue(p.port);
|
||||
m_additionalQueriesLineEdit->setText(p.additionalQueries);
|
||||
m_httpsCheckBox->setChecked(p.https);
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Gerrit
|
||||
99
src/plugins/git/gerrit/gerritoptionspage.h
Normal file
99
src/plugins/git/gerrit/gerritoptionspage.h
Normal file
@@ -0,0 +1,99 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** Other Usage
|
||||
**
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef GERRIT_INTERNAL_GERRITOPTIONSPAGE_H
|
||||
#define GERRIT_INTERNAL_GERRITOPTIONSPAGE_H
|
||||
|
||||
#include <vcsbase/vcsbaseoptionspage.h>
|
||||
|
||||
#include <QWidget>
|
||||
#include <QSharedPointer>
|
||||
#include <QWeakPointer>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QLineEdit;
|
||||
class QSpinBox;
|
||||
class QCheckBox;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Utils {
|
||||
class PathChooser;
|
||||
}
|
||||
namespace Gerrit {
|
||||
namespace Internal {
|
||||
|
||||
class GerritParameters;
|
||||
|
||||
class GerritOptionsWidget : public QWidget
|
||||
{
|
||||
public:
|
||||
explicit GerritOptionsWidget(QWidget *parent = 0);
|
||||
|
||||
GerritParameters parameters() const;
|
||||
void setParameters(const GerritParameters &);
|
||||
|
||||
private:
|
||||
QLineEdit *m_hostLineEdit;
|
||||
QLineEdit *m_userLineEdit;
|
||||
Utils::PathChooser *m_sshChooser;
|
||||
QSpinBox *m_portSpinBox;
|
||||
QLineEdit *m_additionalQueriesLineEdit;
|
||||
QCheckBox *m_httpsCheckBox;
|
||||
};
|
||||
|
||||
class GerritOptionsPage : public VcsBase::VcsBaseOptionsPage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
GerritOptionsPage(const QSharedPointer<GerritParameters> &p,
|
||||
QObject *parent = 0);
|
||||
|
||||
static QString optionsId() { return QLatin1String("Gerrit"); }
|
||||
|
||||
QString id() const { return optionsId(); }
|
||||
QString displayName() const;
|
||||
|
||||
QWidget *createPage(QWidget *parent);
|
||||
void apply();
|
||||
void finish() { }
|
||||
|
||||
virtual bool matches(const QString &) const;
|
||||
|
||||
private:
|
||||
const QSharedPointer<GerritParameters> &m_parameters;
|
||||
QWeakPointer<GerritOptionsWidget> m_widget;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Gerrit
|
||||
|
||||
#endif // GERRIT_INTERNAL_GERRITOPTIONSPAGE_H
|
||||
144
src/plugins/git/gerrit/gerritparameters.cpp
Normal file
144
src/plugins/git/gerrit/gerritparameters.cpp
Normal file
@@ -0,0 +1,144 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** Other Usage
|
||||
**
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#include "gerritparameters.h"
|
||||
#include "gerritplugin.h"
|
||||
|
||||
#if QT_VERSION >= 0x050000
|
||||
# include <QStandardPaths>
|
||||
#else
|
||||
# include <utils/environment.h>
|
||||
#endif
|
||||
#include <QDebug>
|
||||
#include <QFileInfo>
|
||||
#include <QSettings>
|
||||
|
||||
namespace Gerrit {
|
||||
namespace Internal {
|
||||
|
||||
static const char settingsGroupC[] = "Gerrit";
|
||||
static const char hostKeyC[] = "Host";
|
||||
static const char userKeyC[] = "User";
|
||||
static const char portKeyC[] = "Port";
|
||||
static const char sshKeyC[] = "Ssh";
|
||||
static const char httpsKeyC[] = "Https";
|
||||
static const char defaultHostC[] = "codereview.qt-project.org";
|
||||
static const char defaultSshC[] = "ssh";
|
||||
static const char additionalQueriesKeyC[] = "AdditionalQueries";
|
||||
|
||||
enum { defaultPort = 29418 };
|
||||
|
||||
static inline QString detectSsh()
|
||||
{
|
||||
const QByteArray gitSsh = qgetenv("GIT_SSH");
|
||||
if (!gitSsh.isEmpty())
|
||||
return QString::fromLocal8Bit(gitSsh);
|
||||
#if QT_VERSION >= 0x050000
|
||||
QString ssh = QStandardPaths::findExecutable(QLatin1String(defaultSshC));
|
||||
#else
|
||||
const Utils::Environment env = Utils::Environment::systemEnvironment();
|
||||
QString ssh = env.searchInPath(QLatin1String(defaultSshC));
|
||||
#endif
|
||||
if (!ssh.isEmpty())
|
||||
return ssh;
|
||||
#ifdef Q_OS_WIN // Windows: Use ssh.exe from git if it cannot be found.
|
||||
const QString git = GerritPlugin::gitBinary();
|
||||
if (!git.isEmpty()) {
|
||||
// Is 'git\cmd' in the path (folder containing .bats)?
|
||||
QString path = QFileInfo(git).absolutePath();
|
||||
if (path.endsWith(QLatin1String("cmd"), Qt::CaseInsensitive))
|
||||
path.replace(path.size() - 3, 3, QLatin1String("bin"));
|
||||
ssh = path + QLatin1Char('/') + QLatin1String(defaultSshC);
|
||||
}
|
||||
#endif
|
||||
return ssh;
|
||||
}
|
||||
|
||||
GerritParameters::GerritParameters()
|
||||
: host(QLatin1String(defaultHostC))
|
||||
, port(defaultPort)
|
||||
, https(true)
|
||||
{
|
||||
}
|
||||
|
||||
QStringList GerritParameters::baseCommandArguments() const
|
||||
{
|
||||
QStringList result;
|
||||
result << ssh << QLatin1String("-p") << QString::number(port)
|
||||
<< sshHostArgument() << QLatin1String("gerrit");
|
||||
return result;
|
||||
}
|
||||
|
||||
QString GerritParameters::sshHostArgument() const
|
||||
{
|
||||
return user.isEmpty() ? host : (user + QLatin1Char('@') + host);
|
||||
}
|
||||
|
||||
bool GerritParameters::equals(const GerritParameters &rhs) const
|
||||
{
|
||||
return port == rhs.port && host == rhs.host && user == rhs.user
|
||||
&& ssh == rhs.ssh && additionalQueries == rhs.additionalQueries
|
||||
&& https == rhs.https;
|
||||
}
|
||||
|
||||
void GerritParameters::toSettings(QSettings *s) const
|
||||
{
|
||||
s->beginGroup(QLatin1String(settingsGroupC));
|
||||
s->setValue(QLatin1String(hostKeyC), host);
|
||||
s->setValue(QLatin1String(userKeyC), user);
|
||||
s->setValue(QLatin1String(portKeyC), port);
|
||||
s->setValue(QLatin1String(sshKeyC), ssh);
|
||||
s->setValue(QLatin1String(additionalQueriesKeyC), additionalQueries);
|
||||
s->setValue(QLatin1String(httpsKeyC), https);
|
||||
s->endGroup();
|
||||
}
|
||||
|
||||
void GerritParameters::fromSettings(const QSettings *s)
|
||||
{
|
||||
const QString rootKey = QLatin1String(settingsGroupC) + QLatin1Char('/');
|
||||
host = s->value(rootKey + QLatin1String(hostKeyC), QLatin1String(defaultHostC)).toString();
|
||||
user = s->value(rootKey + QLatin1String(userKeyC), QString()).toString();
|
||||
ssh = s->value(rootKey + QLatin1String(sshKeyC), QString()).toString();
|
||||
port = s->value(rootKey + QLatin1String(portKeyC), QVariant(int(defaultPort))).toInt();
|
||||
additionalQueries = s->value(rootKey + QLatin1String(additionalQueriesKeyC), QString()).toString();
|
||||
https = s->value(rootKey + QLatin1String(httpsKeyC), QVariant(true)).toBool();
|
||||
if (ssh.isEmpty())
|
||||
ssh = detectSsh();
|
||||
}
|
||||
|
||||
bool GerritParameters::isValid() const
|
||||
{
|
||||
return !host.isEmpty() && !user.isEmpty() && !ssh.isEmpty();
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Gerrit
|
||||
71
src/plugins/git/gerrit/gerritparameters.h
Normal file
71
src/plugins/git/gerrit/gerritparameters.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** Other Usage
|
||||
**
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef GERRITPARAMETERS_H
|
||||
#define GERRITPARAMETERS_H
|
||||
|
||||
#include <QStringList>
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QSettings)
|
||||
|
||||
namespace Gerrit {
|
||||
namespace Internal {
|
||||
|
||||
class GerritParameters
|
||||
{
|
||||
public:
|
||||
GerritParameters();
|
||||
|
||||
QStringList baseCommandArguments() const;
|
||||
QString sshHostArgument() const;
|
||||
bool isValid() const;
|
||||
bool equals(const GerritParameters &rhs) const;
|
||||
void toSettings(QSettings *) const;
|
||||
void fromSettings(const QSettings *);
|
||||
|
||||
QString host;
|
||||
unsigned short port;
|
||||
QString user;
|
||||
QString ssh;
|
||||
QString additionalQueries;
|
||||
bool https;
|
||||
};
|
||||
|
||||
inline bool operator==(const GerritParameters &p1, const GerritParameters &p2)
|
||||
{ return p1.equals(p2); }
|
||||
inline bool operator!=(const GerritParameters &p1, const GerritParameters &p2)
|
||||
{ return !p1.equals(p2); }
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Gerrit
|
||||
|
||||
#endif // GERRITPARAMETERS_H
|
||||
488
src/plugins/git/gerrit/gerritplugin.cpp
Normal file
488
src/plugins/git/gerrit/gerritplugin.cpp
Normal file
@@ -0,0 +1,488 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** Other Usage
|
||||
**
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#include "gerritplugin.h"
|
||||
#include "gerritparameters.h"
|
||||
#include "gerritdialog.h"
|
||||
#include "gerritmodel.h"
|
||||
#include "gerritoptionspage.h"
|
||||
|
||||
#include <gitplugin.h>
|
||||
#include <gitclient.h>
|
||||
#include <gitversioncontrol.h>
|
||||
#include <vcsbase/vcsbaseconstants.h>
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
#include <coreplugin/coreconstants.h>
|
||||
#include <coreplugin/vcsmanager.h>
|
||||
#include <coreplugin/progressmanager/progressmanager.h>
|
||||
#include <coreplugin/progressmanager/futureprogress.h>
|
||||
#include <coreplugin/documentmanager.h>
|
||||
#include <coreplugin/actionmanager/actionmanager.h>
|
||||
#include <coreplugin/actionmanager/actioncontainer.h>
|
||||
#include <coreplugin/actionmanager/command.h>
|
||||
#include <coreplugin/editormanager/editormanager.h>
|
||||
|
||||
#include <vcsbase/vcsbaseoutputwindow.h>
|
||||
|
||||
#include <utils/synchronousprocess.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QProcess>
|
||||
#include <QRegExp>
|
||||
#include <QMainWindow>
|
||||
#include <QAction>
|
||||
#include <QFileDialog>
|
||||
#include <QTemporaryFile>
|
||||
#include <QDir>
|
||||
|
||||
enum { debug = 0 };
|
||||
|
||||
namespace Gerrit {
|
||||
namespace Constants {
|
||||
const char GERRIT_OPEN_VIEW[] = "Gerrit.OpenView";
|
||||
}
|
||||
namespace Internal {
|
||||
|
||||
enum FetchMode
|
||||
{
|
||||
FetchDisplay,
|
||||
FetchApply,
|
||||
FetchCheckout
|
||||
};
|
||||
|
||||
/* FetchContext: Retrieves the patch and displays
|
||||
* or applies it as desired. Does deleteLater() once it is done. */
|
||||
|
||||
class FetchContext : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
FetchContext(const QSharedPointer<GerritChange> &change,
|
||||
const QString &repository, const QString &git,
|
||||
const QSharedPointer<GerritParameters> &p,
|
||||
FetchMode fm, QObject *parent = 0);
|
||||
~FetchContext();
|
||||
|
||||
public slots:
|
||||
void start();
|
||||
|
||||
private slots:
|
||||
void processError(QProcess::ProcessError);
|
||||
void processFinished(int exitCode, QProcess::ExitStatus);
|
||||
void processReadyReadStandardError();
|
||||
void processReadyReadStandardOutput();
|
||||
|
||||
private:
|
||||
// State enumeration. It starts in 'FetchState' and then
|
||||
// branches to 'WritePatchFileState', 'CherryPickState'
|
||||
// or 'CheckoutState' depending on FetchMode.
|
||||
enum State
|
||||
{
|
||||
FetchState, // Fetch patch
|
||||
WritePatchFileState, // Write patch to a file
|
||||
CherryPickState, // Cherry-pick (apply) fetch-head
|
||||
CheckoutState, // Check out fetch-head
|
||||
DoneState,
|
||||
ErrorState
|
||||
};
|
||||
|
||||
void handleError(const QString &message);
|
||||
void startWritePatchFile();
|
||||
void startCherryPick();
|
||||
void startCheckout();
|
||||
|
||||
const QSharedPointer<GerritChange> m_change;
|
||||
const QString m_repository;
|
||||
const FetchMode m_fetchMode;
|
||||
const QString m_git;
|
||||
const QSharedPointer<GerritParameters> m_parameters;
|
||||
QScopedPointer<QTemporaryFile> m_patchFile;
|
||||
QString m_patchFileName;
|
||||
State m_state;
|
||||
QProcess m_process;
|
||||
QFutureInterface<void> m_progress;
|
||||
};
|
||||
|
||||
FetchContext::FetchContext(const QSharedPointer<GerritChange> &change,
|
||||
const QString &repository, const QString &git,
|
||||
const QSharedPointer<GerritParameters> &p,
|
||||
FetchMode fm, QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_change(change)
|
||||
, m_repository(repository)
|
||||
, m_fetchMode(fm)
|
||||
, m_git(git)
|
||||
, m_parameters(p)
|
||||
, m_state(FetchState)
|
||||
{
|
||||
connect(&m_process, SIGNAL(error(QProcess::ProcessError)),
|
||||
this, SLOT(processError(QProcess::ProcessError)));
|
||||
connect(&m_process, SIGNAL(finished(int,QProcess::ExitStatus)),
|
||||
this, SLOT(processFinished(int,QProcess::ExitStatus)));
|
||||
connect(&m_process, SIGNAL(readyReadStandardError()),
|
||||
this, SLOT(processReadyReadStandardError()));
|
||||
connect(&m_process, SIGNAL(readyReadStandardOutput()),
|
||||
this, SLOT(processReadyReadStandardOutput()));
|
||||
m_process.setWorkingDirectory(repository);
|
||||
}
|
||||
|
||||
FetchContext::~FetchContext()
|
||||
{
|
||||
if (m_progress.isRunning())
|
||||
m_progress.reportFinished();
|
||||
m_process.disconnect(this);
|
||||
Utils::SynchronousProcess::stopProcess(m_process);
|
||||
}
|
||||
|
||||
void FetchContext::start()
|
||||
{
|
||||
m_progress.setProgressRange(0, 2);
|
||||
Core::ProgressManager *pm = Core::ICore::instance()->progressManager();
|
||||
Core::FutureProgress *fp = pm->addTask(m_progress.future(), tr("Gerrit Fetch"),
|
||||
QLatin1String("gerrit-fetch"));
|
||||
fp->setKeepOnFinish(Core::FutureProgress::HideOnFinish);
|
||||
m_progress.reportStarted();
|
||||
// Order: initialize future before starting the process in case error handling is invoked.
|
||||
const QStringList args = m_change->gitFetchArguments(m_parameters);
|
||||
VcsBase::VcsBaseOutputWindow::instance()->appendCommand(m_repository, m_git, args);
|
||||
m_process.start(m_git, args);
|
||||
m_process.closeWriteChannel();
|
||||
}
|
||||
|
||||
void FetchContext::processFinished(int exitCode, QProcess::ExitStatus es)
|
||||
{
|
||||
if (es != QProcess::NormalExit) {
|
||||
handleError(tr("%1 crashed.").arg(m_git));
|
||||
return;
|
||||
}
|
||||
if (exitCode) {
|
||||
handleError(tr("%1 returned %2.").arg(m_git).arg(exitCode));
|
||||
return;
|
||||
}
|
||||
switch (m_state) {
|
||||
case DoneState:
|
||||
case ErrorState:
|
||||
break;
|
||||
case FetchState:
|
||||
m_progress.setProgressValue(m_progress.progressValue() + 1);
|
||||
switch (m_fetchMode) {
|
||||
case FetchDisplay:
|
||||
m_state = WritePatchFileState;
|
||||
startWritePatchFile();
|
||||
break;
|
||||
case FetchApply:
|
||||
m_state = CherryPickState;
|
||||
startCherryPick();
|
||||
break;
|
||||
case FetchCheckout:
|
||||
m_state = CheckoutState;
|
||||
startCheckout();
|
||||
break;
|
||||
} // switch (m_fetchMode)
|
||||
break;
|
||||
case WritePatchFileState:
|
||||
switch (m_fetchMode) {
|
||||
case FetchDisplay:
|
||||
m_patchFileName = m_patchFile->fileName();
|
||||
m_patchFile->close();
|
||||
m_patchFile.reset();
|
||||
m_state = DoneState;
|
||||
m_progress.reportFinished();
|
||||
Core::EditorManager::instance()->openEditor(m_patchFileName);
|
||||
deleteLater();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case CherryPickState:
|
||||
case CheckoutState:
|
||||
m_progress.reportFinished();
|
||||
m_state = DoneState;
|
||||
deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
void FetchContext::processReadyReadStandardError()
|
||||
{
|
||||
// Note: fetch displays progress on stderr.
|
||||
const QString errorOutput = QString::fromLocal8Bit(m_process.readAllStandardError());
|
||||
if (m_state == FetchState || m_state == CheckoutState) {
|
||||
VcsBase::VcsBaseOutputWindow::instance()->append(errorOutput);
|
||||
} else {
|
||||
VcsBase::VcsBaseOutputWindow::instance()->appendError(errorOutput);
|
||||
}
|
||||
}
|
||||
|
||||
void FetchContext::processReadyReadStandardOutput()
|
||||
{
|
||||
const QByteArray output = m_process.readAllStandardOutput();
|
||||
if (m_state == WritePatchFileState) {
|
||||
m_patchFile->write(output);
|
||||
} else {
|
||||
VcsBase::VcsBaseOutputWindow::instance()->append(QString::fromLocal8Bit(output));
|
||||
}
|
||||
}
|
||||
|
||||
void FetchContext::handleError(const QString &e)
|
||||
{
|
||||
m_state = ErrorState;
|
||||
VcsBase::VcsBaseOutputWindow::instance()->appendError(e);
|
||||
m_progress.reportCanceled();
|
||||
m_progress.reportFinished();
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
void FetchContext::processError(QProcess::ProcessError e)
|
||||
{
|
||||
const QString msg = tr("Error running %1: %2").arg(m_git, m_process.errorString());
|
||||
if (e == QProcess::FailedToStart) {
|
||||
handleError(msg);
|
||||
} else {
|
||||
VcsBase::VcsBaseOutputWindow::instance()->appendError(msg);
|
||||
}
|
||||
}
|
||||
|
||||
void FetchContext::startWritePatchFile()
|
||||
{
|
||||
// Fetch to file in temporary folder.
|
||||
QString tempPattern = QDir::tempPath();
|
||||
if (!tempPattern.endsWith(QLatin1Char('/')))
|
||||
tempPattern += QLatin1Char('/');
|
||||
tempPattern += QLatin1String("gerrit_") + QString::number(m_change->number)
|
||||
+ QLatin1Char('_')
|
||||
+ QString::number(m_change->currentPatchSet.patchSetNumber)
|
||||
+ QLatin1String("XXXXXX.patch");
|
||||
m_patchFile.reset(new QTemporaryFile(tempPattern));
|
||||
m_patchFile->setAutoRemove(false);
|
||||
if (!m_patchFile->open()) {
|
||||
handleError(tr("Error writing to temporary file."));
|
||||
return;
|
||||
}
|
||||
VcsBase::VcsBaseOutputWindow::instance()->append(tr("Writing %1...").arg(m_patchFile->fileName()));
|
||||
QStringList args;
|
||||
args << QLatin1String("format-patch") << QLatin1String("-1")
|
||||
<< QLatin1String("--stdout") << QLatin1String("FETCH_HEAD");
|
||||
VcsBase::VcsBaseOutputWindow::instance()->appendCommand(m_repository, m_git, args);
|
||||
if (debug)
|
||||
qDebug() << m_git << args;
|
||||
m_process.start(m_git, args);
|
||||
m_process.closeWriteChannel();
|
||||
}
|
||||
|
||||
void FetchContext::startCherryPick()
|
||||
{
|
||||
VcsBase::VcsBaseOutputWindow::instance()->popup(); // Point user to errors.
|
||||
VcsBase::VcsBaseOutputWindow::instance()->append(tr("Cherry-picking %1...").arg(m_patchFileName));
|
||||
QStringList args;
|
||||
args << QLatin1String("cherry-pick") << QLatin1String("FETCH_HEAD");
|
||||
VcsBase::VcsBaseOutputWindow::instance()->appendCommand(m_repository, m_git, args);
|
||||
if (debug)
|
||||
qDebug() << m_git << args;
|
||||
m_process.start(m_git, args);
|
||||
m_process.closeWriteChannel();
|
||||
}
|
||||
|
||||
void FetchContext::startCheckout()
|
||||
{
|
||||
QStringList args;
|
||||
args << QLatin1String("checkout") << QLatin1String("FETCH_HEAD");
|
||||
VcsBase::VcsBaseOutputWindow::instance()->appendCommand(m_repository, m_git, args);
|
||||
m_process.start(m_git, args);
|
||||
m_process.closeWriteChannel();
|
||||
}
|
||||
|
||||
GerritPlugin::GerritPlugin(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_parameters(new GerritParameters)
|
||||
{
|
||||
}
|
||||
|
||||
GerritPlugin::~GerritPlugin()
|
||||
{
|
||||
}
|
||||
|
||||
bool GerritPlugin::initialize(Core::ActionContainer *ac)
|
||||
{
|
||||
m_parameters->fromSettings(Core::ICore::instance()->settings());
|
||||
|
||||
Core::ActionManager *am = Core::ICore::instance()->actionManager();
|
||||
QAction *openViewAction = new QAction(tr("Gerrit..."), this);
|
||||
|
||||
Core::Command *command =
|
||||
am->registerAction(openViewAction, Constants::GERRIT_OPEN_VIEW,
|
||||
Core::Context(Core::Constants::C_GLOBAL));
|
||||
connect(openViewAction, SIGNAL(triggered()), this, SLOT(openView()));
|
||||
ac->addAction(command);
|
||||
|
||||
Git::Internal::GitPlugin::instance()->addAutoReleasedObject(new GerritOptionsPage(m_parameters));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Open or raise the Gerrit dialog window.
|
||||
void GerritPlugin::openView()
|
||||
{
|
||||
if (m_dialog.isNull()) {
|
||||
while (!m_parameters->isValid()) {
|
||||
const QString group = QLatin1String(VcsBase::Constants::VCS_SETTINGS_CATEGORY);
|
||||
if (!Core::ICore::instance()->showOptionsDialog(group, GerritOptionsPage::optionsId()))
|
||||
return;
|
||||
}
|
||||
GerritDialog *gd = new GerritDialog(m_parameters, Core::ICore::mainWindow());
|
||||
gd->setModal(false);
|
||||
connect(gd, SIGNAL(fetchDisplay(QSharedPointer<Gerrit::Internal::GerritChange>)),
|
||||
this, SLOT(fetchDisplay(QSharedPointer<Gerrit::Internal::GerritChange>)));
|
||||
connect(gd, SIGNAL(fetchApply(QSharedPointer<Gerrit::Internal::GerritChange>)),
|
||||
this, SLOT(fetchApply(QSharedPointer<Gerrit::Internal::GerritChange>)));
|
||||
connect(gd, SIGNAL(fetchCheckout(QSharedPointer<Gerrit::Internal::GerritChange>)),
|
||||
this, SLOT(fetchCheckout(QSharedPointer<Gerrit::Internal::GerritChange>)));
|
||||
m_dialog = gd;
|
||||
}
|
||||
const Qt::WindowStates state = m_dialog.data()->windowState();
|
||||
if (state & Qt::WindowMinimized)
|
||||
m_dialog.data()->setWindowState(state & ~Qt::WindowMinimized);
|
||||
m_dialog.data()->show();
|
||||
m_dialog.data()->raise();
|
||||
}
|
||||
|
||||
QString GerritPlugin::gitBinary()
|
||||
{
|
||||
bool ok;
|
||||
const QString git = Git::Internal::GitPlugin::instance()->gitClient()->gitBinaryPath(&ok);
|
||||
if (!ok) {
|
||||
VcsBase::VcsBaseOutputWindow::instance()->appendError(tr("git is not available."));
|
||||
return QString();
|
||||
}
|
||||
return git;
|
||||
}
|
||||
|
||||
// Find the branch of a repository.
|
||||
QString GerritPlugin::branch(const QString &repository)
|
||||
{
|
||||
Git::Internal::GitClient *client = Git::Internal::GitPlugin::instance()->gitClient();
|
||||
QString errorMessage;
|
||||
QString output;
|
||||
if (client->synchronousBranchCmd(repository, QStringList(), &output, &errorMessage)) {
|
||||
output.remove(QLatin1Char('\r'));
|
||||
foreach (const QString &line, output.split(QLatin1Char('\n')))
|
||||
if (line.startsWith(QLatin1String("* ")))
|
||||
return line.right(line.size() - 2);
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
void GerritPlugin::fetchDisplay(const QSharedPointer<Gerrit::Internal::GerritChange> &change)
|
||||
{
|
||||
fetch(change, FetchDisplay);
|
||||
}
|
||||
|
||||
void GerritPlugin::fetchApply(const QSharedPointer<Gerrit::Internal::GerritChange> &change)
|
||||
{
|
||||
fetch(change, FetchApply);
|
||||
}
|
||||
|
||||
void GerritPlugin::fetchCheckout(const QSharedPointer<Gerrit::Internal::GerritChange> &change)
|
||||
{
|
||||
fetch(change, FetchCheckout);
|
||||
}
|
||||
|
||||
void GerritPlugin::fetch(const QSharedPointer<Gerrit::Internal::GerritChange> &change, int mode)
|
||||
{
|
||||
// Locate git.
|
||||
const QString git = gitBinary();
|
||||
if (git.isEmpty())
|
||||
return;
|
||||
|
||||
// Ask the user for a repository to retrieve the change.
|
||||
const QString title =
|
||||
tr("Enter Local Repository for '%1' (%2)").arg(change->project, change->branch);
|
||||
const QString suggestedRespository =
|
||||
findLocalRepository(change->project, change->branch);
|
||||
const QString repository =
|
||||
QFileDialog::getExistingDirectory(m_dialog.data(),
|
||||
title, suggestedRespository);
|
||||
if (repository.isEmpty())
|
||||
return;
|
||||
|
||||
FetchContext *fc = new FetchContext(change, repository, git,
|
||||
m_parameters, FetchMode(mode), this);
|
||||
fc->start();
|
||||
}
|
||||
|
||||
// Try to find a matching repository for a project by asking the VcsManager.
|
||||
QString GerritPlugin::findLocalRepository(QString project, const QString &branch) const
|
||||
{
|
||||
const Core::VcsManager *vcsManager = Core::ICore::instance()->vcsManager();
|
||||
const QStringList gitRepositories = vcsManager->repositories(Git::Internal::GitPlugin::instance()->gitVersionControl());
|
||||
// Determine key (file name) to look for (qt/qtbase->'qtbase').
|
||||
const int slashPos = project.lastIndexOf(QLatin1Char('/'));
|
||||
if (slashPos != -1)
|
||||
project.remove(0, slashPos + 1);
|
||||
// When looking at branch 1.7, try to check folders
|
||||
// "qtbase_17", 'qtbase1.7' with a semi-smart regular expression.
|
||||
QScopedPointer<QRegExp> branchRegexp;
|
||||
if (!branch.isEmpty() && branch != QLatin1String("master")) {
|
||||
QString branchPattern = branch;
|
||||
branchPattern.replace(QLatin1String("."), QLatin1String("[\\.-_]?"));
|
||||
const QString pattern = QLatin1Char('^') + project
|
||||
+ QLatin1String("[-_]?")
|
||||
+ branchPattern + QLatin1Char('$');
|
||||
branchRegexp.reset(new QRegExp(pattern));
|
||||
if (!branchRegexp->isValid())
|
||||
branchRegexp.reset(); // Oops.
|
||||
}
|
||||
foreach (const QString &repository, gitRepositories) {
|
||||
const QString fileName = QFileInfo(repository).fileName();
|
||||
if ((!branchRegexp.isNull() && branchRegexp->exactMatch(fileName))
|
||||
|| fileName == project) {
|
||||
// Perform a check on the branch.
|
||||
if (branch.isEmpty()) {
|
||||
return repository;
|
||||
} else {
|
||||
const QString repositoryBranch = GerritPlugin::branch(repository);
|
||||
if (repositoryBranch.isEmpty() || repositoryBranch == branch)
|
||||
return repository;
|
||||
} // !branch.isEmpty()
|
||||
} // branchRegexp or file name match
|
||||
} // for repositories
|
||||
// No match, do we have a projects folder?
|
||||
if (Core::DocumentManager::useProjectsDirectory())
|
||||
return Core::DocumentManager::projectsDirectory();
|
||||
|
||||
return QDir::currentPath();
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Gerrit
|
||||
|
||||
#include "gerritplugin.moc"
|
||||
82
src/plugins/git/gerrit/gerritplugin.h
Normal file
82
src/plugins/git/gerrit/gerritplugin.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** Other Usage
|
||||
**
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef GERRIT_INTERNAL_GERRITPLUGIN_H
|
||||
#define GERRIT_INTERNAL_GERRITPLUGIN_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QSharedPointer>
|
||||
#include <QWeakPointer>
|
||||
|
||||
namespace Core {
|
||||
class ActionContainer;
|
||||
}
|
||||
|
||||
namespace Gerrit {
|
||||
namespace Internal {
|
||||
|
||||
class GerritChange;
|
||||
class GerritParameters;
|
||||
class GerritDialog;
|
||||
|
||||
class GerritPlugin : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit GerritPlugin(QObject *parent = 0);
|
||||
~GerritPlugin();
|
||||
|
||||
bool initialize(Core::ActionContainer *ac);
|
||||
|
||||
static QString gitBinary();
|
||||
static QString branch(const QString &repository);
|
||||
|
||||
public slots:
|
||||
void fetchDisplay(const QSharedPointer<Gerrit::Internal::GerritChange> &change);
|
||||
void fetchApply(const QSharedPointer<Gerrit::Internal::GerritChange> &change);
|
||||
void fetchCheckout(const QSharedPointer<Gerrit::Internal::GerritChange> &change);
|
||||
|
||||
private slots:
|
||||
void openView();
|
||||
|
||||
private:
|
||||
QString findLocalRepository(QString project, const QString &branch) const;
|
||||
void fetch(const QSharedPointer<Gerrit::Internal::GerritChange> &change, int mode);
|
||||
|
||||
QSharedPointer<GerritParameters> m_parameters;
|
||||
QWeakPointer<GerritDialog> m_dialog;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Gerrit
|
||||
|
||||
#endif // GERRIT_INTERNAL_GERRITPLUGIN_H
|
||||
@@ -64,5 +64,4 @@ include(gitorious/gitorious.pri)
|
||||
RESOURCES += \
|
||||
git.qrc
|
||||
|
||||
|
||||
|
||||
include(gerrit/gerrit.pri)
|
||||
|
||||
@@ -17,6 +17,7 @@ QtcPlugin {
|
||||
cpp.includePaths: [
|
||||
".",
|
||||
"gitorious",
|
||||
"gerrit",
|
||||
"..",
|
||||
"../../libs",
|
||||
buildDirectory
|
||||
@@ -96,5 +97,20 @@ QtcPlugin {
|
||||
"gitorious/gitoriousrepositorywizardpage.h"
|
||||
]
|
||||
}
|
||||
|
||||
Group {
|
||||
files: [
|
||||
"gerrit/gerritdialog.cpp",
|
||||
"gerrit/gerritdialog.h",
|
||||
"gerrit/gerritmodel.cpp",
|
||||
"gerrit/gerritmodel.h",
|
||||
"gerrit/gerritoptionspage.cpp",
|
||||
"gerrit/gerritoptionspage.h",
|
||||
"gerrit/gerritparameters.cpp",
|
||||
"gerrit/gerritparameters.h",
|
||||
"gerrit/gerritplugin.cpp",
|
||||
"gerrit/gerritplugin.h"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,8 @@
|
||||
#include "stashdialog.h"
|
||||
#include "settingspage.h"
|
||||
|
||||
#include <gerritplugin.h>
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
#include <coreplugin/coreconstants.h>
|
||||
#include <coreplugin/documentmanager.h>
|
||||
@@ -566,7 +568,9 @@ bool GitPlugin::initialize(const QStringList &arguments, QString *errorMessage)
|
||||
m_redoAction = new QAction(tr("&Redo"), this);
|
||||
command = actionManager->registerAction(m_redoAction, Core::Constants::REDO, submitContext);
|
||||
|
||||
return true;
|
||||
|
||||
Gerrit::Internal::GerritPlugin *gp = new Gerrit::Internal::GerritPlugin(this);
|
||||
return gp->initialize(gitContainer);
|
||||
}
|
||||
|
||||
GitVersionControl *GitPlugin::gitVersionControl() const
|
||||
|
||||
Reference in New Issue
Block a user