summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/plugins/git/clonewizard.cpp15
-rw-r--r--src/plugins/git/clonewizardpage.cpp58
-rw-r--r--src/plugins/git/clonewizardpage.h21
-rw-r--r--src/plugins/git/git.pro1
-rw-r--r--src/plugins/git/gitorious/gitorious.cpp600
-rw-r--r--src/plugins/git/gitorious/gitorious.h177
-rw-r--r--src/plugins/git/gitorious/gitorious.pri22
-rw-r--r--src/plugins/git/gitorious/gitoriousclonewizard.cpp111
-rw-r--r--src/plugins/git/gitorious/gitoriousclonewizard.h60
-rw-r--r--src/plugins/git/gitorious/gitorioushostwidget.cpp319
-rw-r--r--src/plugins/git/gitorious/gitorioushostwidget.h103
-rw-r--r--src/plugins/git/gitorious/gitorioushostwidget.ui63
-rw-r--r--src/plugins/git/gitorious/gitorioushostwizardpage.cpp100
-rw-r--r--src/plugins/git/gitorious/gitorioushostwizardpage.h58
-rw-r--r--src/plugins/git/gitorious/gitoriousprojectswizardwidget.cpp0
-rw-r--r--src/plugins/git/gitorious/gitoriousprojectwidget.cpp304
-rw-r--r--src/plugins/git/gitorious/gitoriousprojectwidget.h113
-rw-r--r--src/plugins/git/gitorious/gitoriousprojectwidget.ui76
-rw-r--r--src/plugins/git/gitorious/gitoriousprojectwizardpage.cpp133
-rw-r--r--src/plugins/git/gitorious/gitoriousprojectwizardpage.h87
-rw-r--r--src/plugins/git/gitorious/gitoriousrepositorywizardpage.cpp211
-rw-r--r--src/plugins/git/gitorious/gitoriousrepositorywizardpage.h80
-rw-r--r--src/plugins/git/gitorious/gitoriousrepositorywizardpage.ui24
-rw-r--r--src/plugins/git/gitplugin.cpp2
-rw-r--r--src/plugins/vcsbase/basecheckoutwizardpage.cpp10
-rw-r--r--src/plugins/vcsbase/basecheckoutwizardpage.h3
-rw-r--r--src/plugins/vcsbase/basecheckoutwizardpage.ui13
27 files changed, 2719 insertions, 45 deletions
diff --git a/src/plugins/git/clonewizard.cpp b/src/plugins/git/clonewizard.cpp
index 03903aa4ff..9b379d18fb 100644
--- a/src/plugins/git/clonewizard.cpp
+++ b/src/plugins/git/clonewizard.cpp
@@ -29,8 +29,6 @@
#include "clonewizard.h"
#include "clonewizardpage.h"
-#include "gitplugin.h"
-#include "gitclient.h"
#include <vcsbase/checkoutjobs.h>
#include <utils/qtcassert.h>
@@ -75,18 +73,7 @@ QSharedPointer<VCSBase::AbstractCheckoutJob> CloneWizard::createJob(const QList<
// Collect parameters for the clone command.
const CloneWizardPage *cwp = qobject_cast<const CloneWizardPage *>(parameterPages.front());
QTC_ASSERT(cwp, return QSharedPointer<VCSBase::AbstractCheckoutJob>())
- const GitClient *client = GitPlugin::instance()->gitClient();
- QStringList args = client->binary();
- const QString workingDirectory = cwp->path();
- const QString directory = cwp->directory();
- *checkoutPath = workingDirectory + QLatin1Char('/') + directory;
- args << QLatin1String("clone") << cwp->repository() << directory;
- const QString binary = args.front();
- args.pop_front();
-
- VCSBase::AbstractCheckoutJob *job = new VCSBase::ProcessCheckoutJob(binary, args, workingDirectory,
- client->processEnvironment());
- return QSharedPointer<VCSBase::AbstractCheckoutJob>(job);
+ return cwp->createCheckoutJob(checkoutPath);
}
} // namespace Internal
diff --git a/src/plugins/git/clonewizardpage.cpp b/src/plugins/git/clonewizardpage.cpp
index 6f78ab278f..aef415a382 100644
--- a/src/plugins/git/clonewizardpage.cpp
+++ b/src/plugins/git/clonewizardpage.cpp
@@ -28,20 +28,42 @@
**************************************************************************/
#include "clonewizardpage.h"
+#include "gitplugin.h"
+#include "gitclient.h"
+
+#include <vcsbase/checkoutjobs.h>
+#include <utils/qtcassert.h>
namespace Git {
-namespace Internal {
+
+struct CloneWizardPagePrivate {
+ CloneWizardPagePrivate();
+
+ const QString mainLinePostfix;
+ const QString gitPostFix;
+ const QString protocolDelimiter;
+};
+
+CloneWizardPagePrivate::CloneWizardPagePrivate() :
+ mainLinePostfix(QLatin1String("/mainline.git")),
+ gitPostFix(QLatin1String(".git")),
+ protocolDelimiter(QLatin1String("://"))
+{
+}
CloneWizardPage::CloneWizardPage(QWidget *parent) :
VCSBase::BaseCheckoutWizardPage(parent),
- m_mainLinePostfix(QLatin1String("/mainline.git")),
- m_gitPostFix(QLatin1String(".git")),
- m_protocolDelimiter(QLatin1String("://"))
+ d(new CloneWizardPagePrivate)
{
setSubTitle(tr("Specify repository URL, checkout directory and path."));
setRepositoryLabel(tr("Clone URL:"));
}
+CloneWizardPage::~CloneWizardPage()
+{
+ delete d;
+}
+
QString CloneWizardPage::directoryFromRepository(const QString &urlIn) const
{
/* Try to figure out a good directory name from something like:
@@ -51,19 +73,19 @@ QString CloneWizardPage::directoryFromRepository(const QString &urlIn) const
QString url = urlIn.trimmed();
const QChar slash = QLatin1Char('/');
// remove host
- const int protocolDelimiterPos = url.indexOf(m_protocolDelimiter); // "://"
- const int startRepoSearchPos = protocolDelimiterPos == -1 ? 0 : protocolDelimiterPos + m_protocolDelimiter.size();
+ const int protocolDelimiterPos = url.indexOf(d->protocolDelimiter); // "://"
+ const int startRepoSearchPos = protocolDelimiterPos == -1 ? 0 : protocolDelimiterPos + d->protocolDelimiter.size();
int repoPos = url.indexOf(QLatin1Char(':'), startRepoSearchPos);
if (repoPos == -1)
repoPos = url.indexOf(slash, startRepoSearchPos);
if (repoPos != -1)
url.remove(0, repoPos + 1);
// Remove postfixes
- if (url.endsWith(m_mainLinePostfix)) {
- url.truncate(url.size() - m_mainLinePostfix.size());
+ if (url.endsWith(d->mainLinePostfix)) {
+ url.truncate(url.size() - d->mainLinePostfix.size());
} else {
- if (url.endsWith(m_gitPostFix)) {
- url.truncate(url.size() - m_gitPostFix.size());
+ if (url.endsWith(d->gitPostFix)) {
+ url.truncate(url.size() - d->gitPostFix.size());
}
}
// Check for equal parts, something like "qt/qt" -> "qt"
@@ -79,5 +101,19 @@ QString CloneWizardPage::directoryFromRepository(const QString &urlIn) const
return url;
}
-} // namespace Internal
+QSharedPointer<VCSBase::AbstractCheckoutJob> CloneWizardPage::createCheckoutJob(QString *checkoutPath) const
+{
+ const Internal::GitClient *client = Internal::GitPlugin::instance()->gitClient();
+ QStringList args = client->binary();
+ const QString workingDirectory = path();
+ const QString checkoutDir = directory();
+ *checkoutPath = workingDirectory + QLatin1Char('/') + checkoutDir;
+ args << QLatin1String("clone") << repository() << checkoutDir;
+ const QString binary = args.front();
+ args.pop_front();
+ VCSBase::AbstractCheckoutJob *job = new VCSBase::ProcessCheckoutJob(binary, args, workingDirectory,
+ client->processEnvironment());
+ return QSharedPointer<VCSBase::AbstractCheckoutJob>(job);
+}
+
} // namespace Git
diff --git a/src/plugins/git/clonewizardpage.h b/src/plugins/git/clonewizardpage.h
index 4d8797e6f0..2ad6b99d84 100644
--- a/src/plugins/git/clonewizardpage.h
+++ b/src/plugins/git/clonewizardpage.h
@@ -32,25 +32,32 @@
#include <vcsbase/basecheckoutwizardpage.h>
+#include <QtCore/QSharedPointer>
+
+namespace VCSBase {
+ class AbstractCheckoutJob;
+}
+
namespace Git {
-namespace Internal {
+struct CloneWizardPagePrivate;
+
+// Used by gitorious as well.
class CloneWizardPage : public VCSBase::BaseCheckoutWizardPage
{
Q_OBJECT
public:
- CloneWizardPage(QWidget *parent = 0);
+ explicit CloneWizardPage(QWidget *parent = 0);
+ virtual ~CloneWizardPage();
+
+ QSharedPointer<VCSBase::AbstractCheckoutJob> createCheckoutJob(QString *checkoutPath) const;
protected:
virtual QString directoryFromRepository(const QString &r) const;
private:
- const QString m_mainLinePostfix;
- const QString m_gitPostFix;
- const QString m_protocolDelimiter;
+ CloneWizardPagePrivate *d;
};
-} // namespace Internal
} // namespace Git
#endif // CLONEWIZARDPAGE_H
-
diff --git a/src/plugins/git/git.pro b/src/plugins/git/git.pro
index 618b7810b4..86556815eb 100644
--- a/src/plugins/git/git.pro
+++ b/src/plugins/git/git.pro
@@ -47,3 +47,4 @@ FORMS += changeselectiondialog.ui \
branchdialog.ui
OTHER_FILES += ScmGit.pluginspec
+include(gitorious/gitorious.pri)
diff --git a/src/plugins/git/gitorious/gitorious.cpp b/src/plugins/git/gitorious/gitorious.cpp
new file mode 100644
index 0000000000..c2a1787d91
--- /dev/null
+++ b/src/plugins/git/gitorious/gitorious.cpp
@@ -0,0 +1,600 @@
+/**************************************************************************
+**
+** 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://www.qtsoftware.com/contact.
+**
+**************************************************************************/
+
+#include "gitorious.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QCoreApplication>
+#include <QtCore/QXmlStreamReader>
+#include <QtCore/QSettings>
+
+#include <QtNetwork/QNetworkAccessManager>
+#include <QtNetwork/QNetworkReply>
+
+enum { debug = 0 };
+
+enum Protocol { ListCategoriesProtocol, ListProjectsProtocol };
+
+static const char *protocolPropertyC = "gitoriousProtocol";
+static const char *hostNamePropertyC = "gitoriousHost";
+static const char *pagePropertyC = "requestPage";
+
+static const char *settingsKeyC = "GitoriousHosts";
+
+// Gitorious paginates projects as 20 per page. It starts with page 1.
+enum { ProjectsPageSize = 20 };
+
+// Format an URL for a XML request
+static inline QUrl xmlRequest(const QString &host, const QString &request, int page = -1)
+{
+ QUrl url;
+ url.setScheme(QLatin1String("http"));
+ url.setHost(host);
+ url.setPath(QLatin1Char('/') + request);
+ url.addQueryItem(QLatin1String("format"), QLatin1String("xml"));
+ if (page >= 0)
+ url.addQueryItem(QLatin1String("page"), QString::number(page));
+ return url;
+}
+
+namespace Gitorious {
+namespace Internal {
+
+GitoriousRepository::GitoriousRepository() :
+ type(BaselineRepository),
+ id(0)
+{
+}
+
+static inline GitoriousRepository::Type repositoryType(const QString &nspace)
+{
+ if (nspace == QLatin1String("Repository::Namespace::BASELINE"))
+ return GitoriousRepository::BaselineRepository;
+ if (nspace == QLatin1String("Repository::Namespace::SHARED"))
+ return GitoriousRepository::SharedRepository;
+ if (nspace == QLatin1String("Repository::Namespace::PERSONAL"))
+ return GitoriousRepository::PersonalRepository;
+ return GitoriousRepository::BaselineRepository;
+}
+
+GitoriousCategory::GitoriousCategory(const QString &n) :
+ name(n)
+{
+}
+
+GitoriousHost::GitoriousHost(const QString &h, const QString &d) :
+ hostName(h),
+ description(d),
+ state(ProjectsQueryRunning)
+{
+}
+
+int GitoriousHost::findCategory(const QString &n) const
+{
+ const int count = categories.size();
+ for (int i = 0; i < count; i++)
+ if (categories.at(i)->name == n)
+ return i;
+ return -1;
+}
+
+QDebug operator<<(QDebug d, const GitoriousRepository &r)
+{
+ QDebug nospace = d.nospace();
+ nospace << "name=" << r.name << '/' << r.id << '/' << r.type << r.owner
+ <<" push=" << r.pushUrl << " clone=" << r.cloneUrl << " descr=" << r.description;
+ return d;
+}
+
+QDebug operator<<(QDebug d, const GitoriousProject &p)
+{
+ QDebug nospace = d.nospace();
+ nospace << " project=" << p.name << " description=" << p.description << '\n';
+ foreach(const GitoriousRepository &r, p.repositories)
+ nospace << " " << r << '\n';
+ return d;
+}
+
+QDebug operator<<(QDebug d, const GitoriousCategory &c)
+{
+ d.nospace() << " category=" << c.name << '\n';
+ return d;
+}
+
+QDebug operator<<(QDebug d, const GitoriousHost &h)
+{
+ QDebug nospace = d.nospace();
+ nospace << " Host=" << h.hostName << " description=" << h.description << '\n';
+ foreach(const QSharedPointer<GitoriousCategory> &c, h.categories)
+ nospace << *c;
+ foreach(const QSharedPointer<GitoriousProject> &p, h.projects)
+ nospace << *p;
+ return d;
+}
+
+/* GitoriousProjectReader: Helper class for parsing project list output
+ * \code
+projects...>
+ <project>
+ <bugtracker-url>
+ <created-at>
+ <description>... </description>
+ <home-url> (rarely set)
+ <license>
+ <mailinglist-url>
+ <slug> (name)
+ <title>MuleFTW</title>
+ <owner>
+ <repositories>
+ <mainlines> // Optional
+ <repository>
+ <id>
+ <name>
+ <owner>
+ <clone_url>
+ </repository>
+ </mainlines>
+ <clones> // Optional
+ </clones>
+ </repositories>
+ </project>
+
+ * \endcode */
+
+class GitoriousProjectReader
+{
+ Q_DISABLE_COPY(GitoriousProjectReader)
+public:
+ typedef GitoriousCategory::ProjectList ProjectList;
+
+ GitoriousProjectReader();
+ ProjectList read(const QByteArray &a, QString *errorMessage);
+
+private:
+ void readProjects(QXmlStreamReader &r);
+ QSharedPointer<GitoriousProject> readProject(QXmlStreamReader &r);
+ QList<GitoriousRepository> readRepositories(QXmlStreamReader &r);
+ GitoriousRepository readRepository(QXmlStreamReader &r, int defaultType = -1);
+ void readUnknownElement(QXmlStreamReader &r);
+
+ const QString m_mainLinesElement;
+ const QString m_clonesElement;
+ ProjectList m_projects;
+};
+
+GitoriousProjectReader::GitoriousProjectReader() :
+ m_mainLinesElement(QLatin1String("mainlines")),
+ m_clonesElement(QLatin1String("clones"))
+{
+}
+
+GitoriousProjectReader::ProjectList GitoriousProjectReader::read(const QByteArray &a, QString *errorMessage)
+{
+ m_projects.clear();
+ QXmlStreamReader reader(a);
+
+ while (!reader.atEnd()) {
+ reader.readNext();
+ if (reader.isStartElement()) {
+ if (reader.name() == QLatin1String("projects")) {
+ readProjects(reader);
+ } else {
+ readUnknownElement(reader);
+ }
+ }
+ }
+
+ if (reader.hasError()) {
+ *errorMessage = QString::fromLatin1("Error at %1:%2: %3").arg(reader.lineNumber()).arg(reader.columnNumber()).arg(reader.errorString());
+ m_projects.clear();
+ }
+
+ return m_projects;
+}
+
+bool gitoriousProjectLessThan(const QSharedPointer<GitoriousProject> &p1, const QSharedPointer<GitoriousProject> &p2)
+{
+ return p1->name.compare(p2->name, Qt::CaseInsensitive) < 0;
+}
+
+void GitoriousProjectReader::readProjects(QXmlStreamReader &reader)
+{
+ while (!reader.atEnd()) {
+ reader.readNext();
+
+ if (reader.isEndElement())
+ break;
+
+ if (reader.isStartElement()) {
+ if (reader.name() == "project") {
+ const QSharedPointer<GitoriousProject> p = readProject(reader);
+ if (!p->name.isEmpty())
+ m_projects.push_back(p);
+ } else {
+ readUnknownElement(reader);
+ }
+ }
+ }
+}
+
+QSharedPointer<GitoriousProject> GitoriousProjectReader::readProject(QXmlStreamReader &reader)
+{
+ QSharedPointer<GitoriousProject> project(new GitoriousProject);
+
+ while (!reader.atEnd()) {
+ reader.readNext();
+ if (reader.isEndElement())
+ break;
+
+ if (reader.isStartElement()) {
+ const QStringRef name = reader.name();
+ if (name == QLatin1String("description")) {
+ project->description = reader.readElementText();
+ } else if (name == QLatin1String("title")) {
+ project->name = reader.readElementText();
+ } else if (name == QLatin1String("slug") && project->name.isEmpty()) {
+ project->name = reader.readElementText();
+ } else if (name == QLatin1String("repositories")) {
+ project->repositories = readRepositories(reader);
+ } else {
+ readUnknownElement(reader);
+ }
+ }
+ }
+ return project;
+}
+
+QList<GitoriousRepository> GitoriousProjectReader::readRepositories(QXmlStreamReader &reader)
+{
+ QList<GitoriousRepository> repositories;
+ int defaultType = -1;
+
+ // The "mainlines"/"clones" elements are not used in the
+ // Nokia setup, handle them optionally.
+ while (!reader.atEnd()) {
+ reader.readNext();
+
+ if (reader.isEndElement()) {
+ const QStringRef name = reader.name();
+ if (name == m_mainLinesElement || name == m_clonesElement) {
+ defaultType = -1;
+ } else {
+ break;
+ }
+ }
+
+ if (reader.isStartElement()) {
+ const QStringRef name = reader.name();
+ if (reader.name() == QLatin1String("repository")) {
+ repositories.push_back(readRepository(reader, defaultType));
+ } else if (name == m_mainLinesElement) {
+ defaultType = GitoriousRepository::MainLineRepository;
+ } else if (name == m_clonesElement) {
+ defaultType = GitoriousRepository::CloneRepository;
+ } else {
+ readUnknownElement(reader);
+ }
+ }
+ }
+ return repositories;
+}
+
+GitoriousRepository GitoriousProjectReader::readRepository(QXmlStreamReader &reader, int defaultType)
+{
+ GitoriousRepository repository;
+ if (defaultType >= 0)
+ repository.type = static_cast<GitoriousRepository::Type>(defaultType);
+
+ while (!reader.atEnd()) {
+ reader.readNext();
+
+ if (reader.isEndElement())
+ break;
+
+ if (reader.isStartElement()) {
+ const QStringRef name = reader.name();
+ if (name == QLatin1String("name")) {
+ repository.name = reader.readElementText();
+ } else if (name == QLatin1String("owner")) {
+ repository.owner = reader.readElementText();
+ } else if (name == QLatin1String("id")) {
+ repository.id = reader.readElementText().toInt();
+ } else if (name == QLatin1String("description")) {
+ repository.description = reader.readElementText();
+ } else if (name == QLatin1String("push_url")) {
+ repository.pushUrl = reader.readElementText();
+ } else if (name == QLatin1String("clone_url")) {
+ repository.cloneUrl = reader.readElementText();
+ } else if (name == QLatin1String("namespace")) {
+ repository.type = repositoryType(reader.readElementText());
+ } else {
+ readUnknownElement(reader);
+ }
+ }
+ }
+ return repository;
+}
+
+void GitoriousProjectReader::readUnknownElement(QXmlStreamReader &reader)
+{
+ Q_ASSERT(reader.isStartElement());
+
+ while (!reader.atEnd()) {
+ reader.readNext();
+
+ if (reader.isEndElement())
+ break;
+
+ if (reader.isStartElement())
+ readUnknownElement(reader);
+ }
+}
+
+// --- Gitorious
+
+Gitorious::Gitorious() :
+ m_networkManager(0)
+{
+}
+
+Gitorious &Gitorious::instance()
+{
+ static Gitorious gitorious;
+ return gitorious;
+}
+
+void Gitorious::emitError(const QString &e)
+{
+ qWarning("%s\n", qPrintable(e));
+ emit error(e);
+}
+
+void Gitorious::addHost(const QString &addr, const QString &description)
+{
+ addHost(GitoriousHost(addr, description));
+}
+
+void Gitorious::addHost(const GitoriousHost &host)
+{
+ if (debug)
+ qDebug() << host;
+ const int index = m_hosts.size();
+ m_hosts.push_back(host);
+ if (host.categories.empty()) {
+ updateCategories(index);
+ m_hosts.back().state = GitoriousHost::ProjectsQueryRunning;
+ } else {
+ m_hosts.back().state = GitoriousHost::ProjectsComplete;
+ }
+ if (host.projects.empty())
+ updateProjectList(index);
+ emit hostAdded(index);
+}
+
+void Gitorious::removeAt(int index)
+{
+ m_hosts.removeAt(index);
+ emit hostRemoved(index);
+}
+
+int Gitorious::findByHostName(const QString &hostName) const
+{
+ const int size = m_hosts.size();
+ for (int i = 0; i < size; i++)
+ if (m_hosts.at(i).hostName == hostName)
+ return i;
+ return -1;
+}
+
+void Gitorious::setHostDescription(int index, const QString &s)
+{
+ m_hosts[index].description = s;
+}
+
+QString Gitorious::hostDescription(int index) const
+{
+ return m_hosts.at(index).description;
+}
+
+void Gitorious::listCategoriesReply(int index, QByteArray dataB)
+{
+ /* For now, parse the HTML of the projects site for "Popular Categories":
+ * \code
+ * <h4>Popular Categories:</h4>
+ * <ul class="...">
+ * <li class="..."><a href="..."><category></a> </li>
+ * \endcode */
+ do {
+ const int catIndex = dataB.indexOf("Popular Categories:");
+ const int endIndex = catIndex != -1 ? dataB.indexOf("</ul>", catIndex) : -1;
+ if (debug)
+ qDebug() << "listCategoriesReply cat pos=" << catIndex << endIndex;
+ if (endIndex == -1)
+ break;
+ dataB.truncate(endIndex);
+ dataB.remove(0, catIndex);
+ const QString data = QString::fromUtf8(dataB);
+ // Cut out the contents of the anchors
+ QRegExp pattern = QRegExp(QLatin1String("<a href=[^>]+>([^<]+)</a>"));
+ Q_ASSERT(pattern.isValid());
+ GitoriousHost::CategoryList &categories = m_hosts[index].categories;
+ for (int pos = pattern.indexIn(data) ; pos != -1; ) {
+ const QString cat = pattern.cap(1);
+ categories.push_back(QSharedPointer<GitoriousCategory>(new GitoriousCategory(cat)));
+ pos = pattern.indexIn(data, pos + pattern.matchedLength());
+ }
+ } while (false);
+
+ emit categoryListReceived(index);
+}
+
+void Gitorious::listProjectsReply(int hostIndex, int page, const QByteArray &data)
+{
+ // Receive projects.
+ QString errorMessage;
+ GitoriousCategory::ProjectList projects = GitoriousProjectReader().read(data, &errorMessage);
+
+ if (debug) {
+ qDebug() << "listProjectsReply" << hostName(hostIndex)
+ << "page=" << page << " got" << projects.size();
+ if (debug > 1)
+ qDebug() << '\n' <<data;
+ }
+
+ if (!errorMessage.isEmpty()) {
+ emitError(tr("Error parsing reply from '%1': %2").arg(hostName(hostIndex), errorMessage));
+ if (projects.empty())
+ m_hosts[hostIndex].state = GitoriousHost::Error;
+ }
+
+ // Add the projects and start next request if 20 projects received
+ GitoriousCategory::ProjectList &hostProjects = m_hosts[hostIndex].projects;
+ if (!projects.empty())
+ hostProjects.append(projects);
+
+ if (projects.size() == ProjectsPageSize) {
+ startProjectsRequest(hostIndex, page + 1);
+ emit projectListPageReceived(hostIndex, page);
+ } else {
+ // We are done
+ m_hosts[hostIndex].state = GitoriousHost::ProjectsComplete;
+ emit projectListReceived(hostIndex);
+ }
+}
+
+static inline int replyPage(const QNetworkReply *reply)
+{ return reply->property(pagePropertyC).toInt(); }
+
+void Gitorious::slotReplyFinished()
+{
+ // Dispatch the answers via dynamic properties
+ if (QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender())) {
+ const int protocol = reply->property(protocolPropertyC).toInt();
+ // Locate host by name (in case one was deleted in the meantime)
+ const QString hostName = reply->property(hostNamePropertyC).toString();
+ const int hostIndex = findByHostName(hostName);
+ if (hostIndex == -1) // Entry deleted in-between?
+ return;
+ if (reply->error() == QNetworkReply::NoError) {
+ const QByteArray data = reply->readAll();
+ switch (protocol) {
+ case ListProjectsProtocol:
+ listProjectsReply(hostIndex, replyPage(reply), data);
+ break;
+ case ListCategoriesProtocol:
+ listCategoriesReply(hostIndex, data);
+ break;
+
+ } // switch protocol
+ } else {
+ const QString msg = tr("Request failed for '%1': %2").arg(m_hosts.at(hostIndex).hostName, reply->errorString());
+ emitError(msg);
+ }
+ reply->deleteLater();
+ }
+}
+
+// Create a network request. Set dynamic properties on it to be able to
+// dispatch. Use host name in case an entry is removed in-between
+QNetworkReply *Gitorious::createRequest(const QUrl &url, int protocol, int hostIndex, int page)
+{
+ if (!m_networkManager)
+ m_networkManager = new QNetworkAccessManager(this);
+ QNetworkReply *reply = m_networkManager->get(QNetworkRequest(url));
+ connect(reply, SIGNAL(finished()), this, SLOT(slotReplyFinished()));
+ reply->setProperty(protocolPropertyC, QVariant(protocol));
+ reply->setProperty(hostNamePropertyC, QVariant(hostName(hostIndex)));
+ if (page >= 0)
+ reply->setProperty(pagePropertyC, QVariant(page));
+ if (debug)
+ qDebug() << "createRequest" << url;
+ return reply;
+}
+
+void Gitorious::updateCategories(int index)
+{
+ // For now, parse the HTML of the projects site for "Popular Categories":
+ QUrl url;
+ url.setScheme(QLatin1String("http"));
+ url.setHost(hostName(index));
+ url.setPath(QLatin1String("/projects"));
+ createRequest(url, ListCategoriesProtocol, index);
+}
+
+void Gitorious::updateProjectList(int hostIndex)
+{
+ startProjectsRequest(hostIndex);
+}
+
+void Gitorious::startProjectsRequest(int hostIndex, int page)
+{
+ const QUrl url = xmlRequest(hostName(hostIndex), QLatin1String("projects"), page);
+ createRequest(url, ListProjectsProtocol, hostIndex, page);
+}
+
+// Serialize hosts/descriptions as a list of "<host>|descr".
+void Gitorious::saveSettings(const QString &group, QSettings *s)
+{
+ const QChar separator = QLatin1Char('|');
+ QStringList hosts;
+ foreach(const GitoriousHost &h, m_hosts) {
+ QString entry = h.hostName;
+ if (!h.description.isEmpty()) {
+ entry += separator;
+ entry += h.description;
+ }
+ hosts.push_back(entry);
+ }
+ s->beginGroup(group);
+ s->setValue(QLatin1String(settingsKeyC), hosts);
+ s->endGroup();
+}
+
+void Gitorious::restoreSettings(const QString &group, const QSettings *s)
+{
+ m_hosts.clear();
+ const QChar separator = QLatin1Char('|');
+ const QStringList hosts = s->value(group + QLatin1Char('/') + QLatin1String(settingsKeyC), QStringList()).toStringList();
+ foreach (const QString &h, hosts) {
+ const int sepPos = h.indexOf(separator);
+ if (sepPos == -1) {
+ addHost(GitoriousHost(h));
+ } else {
+ addHost(GitoriousHost(h.mid(0, sepPos), h.mid(sepPos + 1)));
+ }
+ }
+}
+
+GitoriousHost Gitorious::gitoriousOrg()
+{
+ return GitoriousHost(QLatin1String("gitorious.org"), tr("Open source projects that use Git."));
+}
+
+} // namespace Internal
+} // namespace Gitorious
diff --git a/src/plugins/git/gitorious/gitorious.h b/src/plugins/git/gitorious/gitorious.h
new file mode 100644
index 0000000000..a296aba02d
--- /dev/null
+++ b/src/plugins/git/gitorious/gitorious.h
@@ -0,0 +1,177 @@
+/**************************************************************************
+**
+** 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://www.qtsoftware.com/contact.
+**
+**************************************************************************/
+
+#ifndef GITORIOUS_H
+#define GITORIOUS_H
+
+#include <QtCore/QStringList>
+#include <QtCore/QSharedPointer>
+#include <QtCore/QUrl>
+#include <QtCore/QObject>
+
+QT_BEGIN_NAMESPACE
+class QNetworkAccessManager;
+class QNetworkReply;
+class QDebug;
+class QUrl;
+class QSettings;
+QT_END_NAMESPACE
+
+namespace Gitorious {
+namespace Internal {
+
+struct GitoriousRepository
+{
+ enum Type {
+ MainLineRepository,
+ CloneRepository,
+ BaselineRepository, // Nokia extension
+ SharedRepository, // Nokia extension
+ PersonalRepository, // Nokia extension
+ };
+
+ GitoriousRepository();
+
+ QString name;
+ QString owner;
+ QUrl pushUrl;
+ QUrl cloneUrl;
+ QString description;
+ Type type;
+ int id;
+};
+
+struct GitoriousProject
+{
+ QString name;
+ QString description;
+ QList<GitoriousRepository> repositories;
+};
+
+struct GitoriousCategory
+{
+ typedef QList<QSharedPointer<GitoriousProject > > ProjectList;
+
+ GitoriousCategory(const QString &name = QString());
+
+ QString name;
+};
+
+struct GitoriousHost
+{
+ enum State { ProjectsQueryRunning, ProjectsComplete, Error };
+ typedef QList<QSharedPointer<GitoriousCategory> > CategoryList;
+ typedef QList<QSharedPointer<GitoriousProject > > ProjectList;
+
+ GitoriousHost(const QString &hostName = QString(), const QString &description = QString());
+ int findCategory(const QString &) const;
+
+ QString hostName;
+ QString description;
+ CategoryList categories;
+ ProjectList projects;
+ State state;
+};
+
+QDebug operator<<(QDebug d, const GitoriousRepository &r);
+QDebug operator<<(QDebug d, const GitoriousProject &p);
+QDebug operator<<(QDebug d, const GitoriousCategory &p);
+QDebug operator<<(QDebug d, const GitoriousHost &p);
+
+/* Singleton that manages a list of gitorious hosts, running network queries
+ * in the background. It models hosts with a flat list of projects (Gitorious
+ * has a concept of categories, but this is not enforced, and there is no
+ * way to query them).
+ * As 24.07.2009, the only supported XML request of the host is a paginated
+ * "list-all-projects". */
+
+class Gitorious : public QObject
+{
+ Q_DISABLE_COPY(Gitorious)
+ Q_OBJECT
+
+public:
+ static Gitorious &instance();
+
+ const QList<GitoriousHost> &hosts() const { return m_hosts; }
+ int hostCount() const { return m_hosts.size(); }
+ int categoryCount(int hostIndex) const { return m_hosts.at(hostIndex).categories.size(); }
+ int projectCount(int hostIndex) const { return m_hosts.at(hostIndex).projects.size(); }
+ GitoriousHost::State hostState(int hostIndex) const { return m_hosts.at(hostIndex).state; }
+
+ // If no projects are set, start an asynchronous request querying
+ // the projects/categories of the host.
+ void addHost(const QString &addr, const QString &description = QString());
+ void addHost(const GitoriousHost &host);
+ void removeAt(int index);
+
+ int findByHostName(const QString &hostName) const;
+ QString hostName(int i) const { return m_hosts.at(i).hostName; }
+ QString categoryName(int hostIndex, int categoryIndex) const { return m_hosts.at(hostIndex).categories.at(categoryIndex)->name; }
+
+ QString hostDescription(int index) const;
+ void setHostDescription(int index, const QString &s);
+
+ void saveSettings(const QString &group, QSettings *s);
+ void restoreSettings(const QString &group, const QSettings *s);
+
+ // Return predefined entry for "gitorious.org".
+ static GitoriousHost gitoriousOrg();
+
+signals:
+ void error(const QString &);
+ void projectListReceived(int hostIndex);
+ void projectListPageReceived(int hostIndex, int page);
+ void categoryListReceived(int index);
+ void hostAdded(int index);
+ void hostRemoved(int index);
+
+public slots:
+ void updateProjectList(int hostIndex);
+ void updateCategories(int index);
+
+private slots:
+ void slotReplyFinished();
+
+private:
+ Gitorious();
+ void listProjectsReply(int hostIndex, int page, const QByteArray &data);
+ void listCategoriesReply(int index, QByteArray data);
+ void emitError(const QString &e);
+ QNetworkReply *createRequest(const QUrl &url, int protocol, int hostIndex, int page = -1);
+ void startProjectsRequest(int index, int page = 1);
+
+ QList<GitoriousHost> m_hosts;
+ QNetworkAccessManager *m_networkManager;
+};
+
+} // namespace Internal
+} // namespace Gitorious
+
+#endif // GITORIOUS_H
diff --git a/src/plugins/git/gitorious/gitorious.pri b/src/plugins/git/gitorious/gitorious.pri
new file mode 100644
index 0000000000..8678042a26
--- /dev/null
+++ b/src/plugins/git/gitorious/gitorious.pri
@@ -0,0 +1,22 @@
+QT += network
+INCLUDEPATH+=$$PWD
+
+HEADERS += $$PWD/gitoriousclonewizard.h \
+ $$PWD/gitorioushostwizardpage.h \
+ $$PWD/gitoriousrepositorywizardpage.h \
+ $$PWD/gitoriousprojectwizardpage.h \
+ $$PWD/gitoriousprojectwidget.h \
+ $$PWD/gitorioushostwidget.h \
+ $$PWD/gitorious.h
+
+SOURCES += $$PWD/gitoriousclonewizard.cpp \
+ $$PWD/gitorioushostwizardpage.cpp \
+ $$PWD/gitoriousrepositorywizardpage.cpp \
+ $$PWD/gitoriousprojectwizardpage.cpp \
+ $$PWD/gitoriousprojectwidget.cpp \
+ $$PWD/gitorioushostwidget.cpp \
+ $$PWD/gitorious.cpp
+
+FORMS += $$PWD/gitorioushostwidget.ui \
+ $$PWD/gitoriousrepositorywizardpage.ui \
+ $$PWD/gitoriousprojectwidget.ui
diff --git a/src/plugins/git/gitorious/gitoriousclonewizard.cpp b/src/plugins/git/gitorious/gitoriousclonewizard.cpp
new file mode 100644
index 0000000000..3a4c309ca0
--- /dev/null
+++ b/src/plugins/git/gitorious/gitoriousclonewizard.cpp
@@ -0,0 +1,111 @@
+/**************************************************************************
+**
+** 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://www.qtsoftware.com/contact.
+**
+**************************************************************************/
+
+#include "gitoriousclonewizard.h"
+#include "gitorioushostwizardpage.h"
+#include "gitoriousprojectwizardpage.h"
+#include "gitoriousrepositorywizardpage.h"
+#include "clonewizardpage.h"
+
+#include <vcsbase/checkoutjobs.h>
+#include <utils/qtcassert.h>
+
+#include <QtCore/QUrl>
+#include <QtGui/QIcon>
+
+namespace Gitorious {
+namespace Internal {
+
+// GitoriousCloneWizardPage: A git clone page taking its URL from the
+// projects page.
+
+class GitoriousCloneWizardPage : public Git::CloneWizardPage {
+public:
+ explicit GitoriousCloneWizardPage(const GitoriousRepositoryWizardPage *rp, QWidget *parent = 0);
+ virtual void initializePage();
+
+private:
+ const GitoriousRepositoryWizardPage *m_repositoryPage;
+};
+
+GitoriousCloneWizardPage::GitoriousCloneWizardPage(const GitoriousRepositoryWizardPage *rp, QWidget *parent) :
+ Git::CloneWizardPage(parent),
+ m_repositoryPage(rp)
+{
+}
+
+void GitoriousCloneWizardPage::initializePage()
+{
+ setRepository(m_repositoryPage->repositoryURL().toString());
+}
+
+// -------- GitoriousCloneWizard
+GitoriousCloneWizard::GitoriousCloneWizard(QObject *parent) :
+ VCSBase::BaseCheckoutWizard(parent)
+{
+}
+
+QIcon GitoriousCloneWizard::icon() const
+{
+ return QIcon();
+}
+
+QString GitoriousCloneWizard::description() const
+{
+ return tr("Clones a project from a Gitorious repository.");
+}
+
+QString GitoriousCloneWizard::name() const
+{
+ return tr("Gitorious Repository Clone");
+}
+
+QList<QWizardPage*> GitoriousCloneWizard::createParameterPages(const QString &path)
+{
+ GitoriousHostWizardPage *hostPage = new GitoriousHostWizardPage;
+ GitoriousProjectWizardPage *projectPage = new GitoriousProjectWizardPage(hostPage);
+ GitoriousRepositoryWizardPage *repoPage = new GitoriousRepositoryWizardPage(projectPage);
+ GitoriousCloneWizardPage *clonePage = new GitoriousCloneWizardPage(repoPage);
+ clonePage->setPath(path);
+
+ QList<QWizardPage*> rc;
+ rc << hostPage << projectPage << repoPage << clonePage;
+ return rc;
+}
+
+QSharedPointer<VCSBase::AbstractCheckoutJob> GitoriousCloneWizard::createJob(const QList<QWizardPage*> &parameterPages,
+ QString *checkoutPath)
+{
+ const Git::CloneWizardPage *cwp = qobject_cast<const Git::CloneWizardPage *>(parameterPages.back());
+ QTC_ASSERT(cwp, return QSharedPointer<VCSBase::AbstractCheckoutJob>())
+ return cwp->createCheckoutJob(checkoutPath);
+}
+
+} // namespace Internal
+} // namespace Gitorius
diff --git a/src/plugins/git/gitorious/gitoriousclonewizard.h b/src/plugins/git/gitorious/gitoriousclonewizard.h
new file mode 100644
index 0000000000..0004463b96
--- /dev/null
+++ b/src/plugins/git/gitorious/gitoriousclonewizard.h
@@ -0,0 +1,60 @@
+/**************************************************************************
+**
+** 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://www.qtsoftware.com/contact.
+**
+**************************************************************************/
+
+#ifndef GITORIOUSCLONEWIZARD_H
+#define GITORIOUSCLONEWIZARD_H
+
+#include <vcsbase/basecheckoutwizard.h>
+
+namespace Gitorious {
+namespace Internal {
+
+// GitoriousCloneWizard: A wizard allowing for browsing
+// Gitorious-hosted projects.
+class GitoriousCloneWizard : public VCSBase::BaseCheckoutWizard
+{
+public:
+ explicit GitoriousCloneWizard(QObject *parent = 0);
+
+ // IWizard
+ virtual QIcon icon() const;
+ virtual QString description() const;
+ virtual QString name() const;
+
+protected:
+ // BaseCheckoutWizard
+ virtual QList<QWizardPage*> createParameterPages(const QString &path);
+ virtual QSharedPointer<VCSBase::AbstractCheckoutJob> createJob(const QList<QWizardPage*> &parameterPages,
+ QString *checkoutPath);
+};
+
+} // namespace Internal
+} // namespace Gitorious
+
+#endif // GITORIOUSCLONEWIZARD_H
diff --git a/src/plugins/git/gitorious/gitorioushostwidget.cpp b/src/plugins/git/gitorious/gitorioushostwidget.cpp
new file mode 100644
index 0000000000..c01cd4c94c
--- /dev/null
+++ b/src/plugins/git/gitorious/gitorioushostwidget.cpp
@@ -0,0 +1,319 @@
+/**************************************************************************
+**
+** 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://www.qtsoftware.com/contact.
+**
+**************************************************************************/
+
+#include "gitorious.h"
+#include "gitorioushostwidget.h"
+#include "ui_gitorioushostwidget.h"
+
+#include <coreplugin/coreconstants.h>
+
+#include <QtCore/QUrl>
+#include <QtCore/QDebug>
+#include <QtCore/QTimer>
+
+#include <QtGui/QStandardItem>
+#include <QtGui/QStandardItemModel>
+#include <QtGui/QItemSelectionModel>
+#include <QtGui/QDesktopServices>
+#include <QtGui/QIcon>
+#include <QtGui/QStyle>
+
+enum { debug = 0 };
+
+enum { NewDummyEntryRole = Qt::UserRole + 1 };
+
+namespace Gitorious {
+namespace Internal {
+
+enum { HostNameColumn, ProjectCountColumn, DescriptionColumn, ColumnCount };
+
+// Create a model row for a host. Make the host name editable as specified by
+// flag.
+static QList<QStandardItem *> hostEntry(const QString &host,
+ int projectCount,
+ const QString &description, bool isDummyEntry)
+{
+ const Qt::ItemFlags nonEditableFlags = (Qt::ItemIsSelectable|Qt::ItemIsEnabled);
+ const Qt::ItemFlags editableFlags = nonEditableFlags|Qt::ItemIsEditable;
+ QStandardItem *hostItem = new QStandardItem(host);
+ hostItem->setFlags(isDummyEntry ? editableFlags : nonEditableFlags);
+ // Empty for dummy, else "..." or count
+ QStandardItem *projectCountItem = 0;
+ QString countItemText;
+ if (!isDummyEntry) {
+ countItemText = projectCount ? QString::number(projectCount) : QString(QLatin1String("..."));
+ }
+ projectCountItem = new QStandardItem(countItemText);
+ projectCountItem->setFlags(nonEditableFlags);
+ QStandardItem *descriptionItem = new QStandardItem(description);
+ descriptionItem->setFlags(editableFlags);
+ QList<QStandardItem *> rc;
+ rc << hostItem << projectCountItem << descriptionItem;
+ return rc;
+}
+
+static inline QList<QStandardItem *> hostEntry(const GitoriousHost &h)
+{
+ return hostEntry(h.hostName, h.projects.size(), h.description, false);
+}
+
+GitoriousHostWidget::GitoriousHostWidget(QWidget *parent) :
+ QWidget(parent),
+ m_newHost(tr("<New Host>")),
+ ui(new Ui::GitoriousHostWidget),
+ m_model(new QStandardItemModel(0, ColumnCount)),
+ m_errorClearTimer(0),
+ m_isValid(false),
+ m_isHostListDirty(false)
+{
+ ui->setupUi(this);
+ ui->errorLabel->setVisible(false);
+ ui->browseToolButton->setIcon(style()->standardIcon(QStyle::SP_MessageBoxInformation));
+ connect(ui->browseToolButton, SIGNAL(clicked()), this, SLOT(slotBrowse()));
+ ui->browseToolButton->setEnabled(false);
+ ui->deleteToolButton->setIcon(QIcon(Core::Constants::ICON_MINUS));
+ connect(ui->deleteToolButton, SIGNAL(clicked()), this, SLOT(slotDelete()));
+ ui->deleteToolButton->setEnabled(false);
+
+ // Model
+ QStringList headers;
+ headers << tr("Host") << tr("Projects") << tr("Description");
+ m_model->setHorizontalHeaderLabels(headers);
+
+ Gitorious &gitorious = Gitorious::instance();
+ foreach( const GitoriousHost &gh, gitorious.hosts())
+ m_model->appendRow(hostEntry(gh));
+ appendNewDummyEntry();
+ connect(m_model, SIGNAL(itemChanged(QStandardItem*)), this, SLOT(slotItemEdited(QStandardItem*)));
+ ui->hostView->setModel(m_model);
+
+ // View
+ ui->hostView->setRootIsDecorated(false);
+ ui->hostView->setUniformRowHeights(true);
+ connect(ui->hostView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
+ this, SLOT(slotCurrentChanged(QModelIndex,QModelIndex)));
+
+ ui->hostView->setSelectionMode(QAbstractItemView::SingleSelection);
+ if (m_model->rowCount())
+ selectRow(0);
+
+ connect(&gitorious, SIGNAL(projectListPageReceived(int,int)),
+ this, SLOT(slotProjectListPageReceived(int)));
+ connect(&gitorious, SIGNAL(projectListReceived(int)),
+ this, SLOT(slotProjectListPageReceived(int)));
+
+ connect(&gitorious, SIGNAL(error(QString)), this, SLOT(slotError(QString)));
+
+ setMinimumWidth(700);
+}
+
+GitoriousHostWidget::~GitoriousHostWidget()
+{
+ // Prevent crash?
+ Gitorious *gitorious = &Gitorious::instance();
+ disconnect(gitorious, SIGNAL(projectListPageReceived(int,int)),
+ this, SLOT(slotProjectListPageReceived(int)));
+ disconnect(gitorious, SIGNAL(projectListReceived(int)),
+ this, SLOT(slotProjectListPageReceived(int)));
+ disconnect(gitorious, SIGNAL(error(QString)), this, SLOT(slotError(QString)));
+ delete ui;
+}
+
+int GitoriousHostWidget::selectedRow() const
+{
+ const QModelIndex idx = ui->hostView->selectionModel()->currentIndex();
+ if (idx.isValid())
+ return idx.row();
+ return -1;
+}
+
+void GitoriousHostWidget::selectRow(int r)
+{
+ if (r >= 0 && r != selectedRow()) {
+ const QModelIndex index = m_model->index(r, 0);
+ ui->hostView->selectionModel()->setCurrentIndex(index, QItemSelectionModel::Select|QItemSelectionModel::Current|QItemSelectionModel::Rows);
+ }
+}
+
+void GitoriousHostWidget::appendNewDummyEntry()
+{
+ // Append a new entry where a host name is editable
+ const QList<QStandardItem *> dummyRow = hostEntry(m_newHost, 0, QString(), true);
+ dummyRow.front()->setData(QVariant(true), NewDummyEntryRole);
+ m_model->appendRow(dummyRow);
+}
+
+void GitoriousHostWidget::slotItemEdited(QStandardItem *item)
+{
+ // Synchronize with Gitorious singleton.
+ // Did someone enter a valid host name into the dummy item?
+ // -> Create a new one.
+ const int row = item->row();
+ const bool isDummyEntry = row >= Gitorious::instance().hostCount();
+ switch (item->column()) {
+ case HostNameColumn:
+ if (isDummyEntry) {
+ Gitorious::instance().addHost(item->text(), m_model->item(row, DescriptionColumn)->text());
+ item->setData(QVariant(false), NewDummyEntryRole);
+ m_isHostListDirty = true;
+ appendNewDummyEntry();
+ selectRow(row);
+ }
+ break;
+ case ProjectCountColumn:
+ break;
+ case DescriptionColumn:
+ if (!isDummyEntry) {
+ const QString description = item->text();
+ if (description != Gitorious::instance().hostDescription(row)) {
+ Gitorious::instance().setHostDescription(row, item->text());
+ m_isHostListDirty = true;
+ }
+ }
+ break;
+ }
+}
+
+void GitoriousHostWidget::slotProjectListPageReceived(int row)
+{
+ if (debug)
+ qDebug() << Q_FUNC_INFO << row;
+ // Update column
+ const int projectCount = Gitorious::instance().projectCount(row);
+ m_model->item(row, ProjectCountColumn)->setText(QString::number(projectCount));
+ // If it is the currently selected host, re-check validity if not enabled
+ if (!m_isValid) {
+ const QModelIndex current = ui->hostView->selectionModel()->currentIndex();
+ if (current.isValid() && current.row() == row)
+ checkValid(current);
+ }
+}
+
+QStandardItem *GitoriousHostWidget::currentItem() const
+{
+ const QModelIndex idx = ui->hostView->selectionModel()->currentIndex();
+ if (idx.isValid())
+ return m_model->itemFromIndex(idx.column() != 0 ? idx.sibling(idx.row(), 0) : idx);
+ return 0;
+}
+
+void GitoriousHostWidget::slotBrowse()
+{
+ if (const QStandardItem *item = currentItem()) {
+ const QUrl url(QLatin1String("http://") + item->text() + QLatin1Char('/'));
+ if (url.isValid())
+ QDesktopServices::openUrl(url);
+ }
+}
+
+void GitoriousHostWidget::slotDelete()
+{
+ const QModelIndex index = ui->hostView->selectionModel()->currentIndex();
+ ui->hostView->selectionModel()->clear();
+ if (index.isValid()) {
+ const int row = index.row();
+ qDeleteAll(m_model->takeRow(row));
+ Gitorious::instance().removeAt(row);
+ m_isHostListDirty = true;
+ }
+}
+
+void GitoriousHostWidget::slotCurrentChanged(const QModelIndex &current, const QModelIndex & /* previous */)
+{
+ checkValid(current);
+}
+
+void GitoriousHostWidget::checkValid(const QModelIndex &index)
+{
+ if (debug)
+ qDebug() << Q_FUNC_INFO << index;
+ bool hasSelectedHost = false;
+ bool hasProjects = false;
+ if (index.isValid()) {
+ // Are we on the new dummy item?
+ Gitorious &gitorious = Gitorious::instance();
+ const int row = index.row();
+ hasSelectedHost = row < gitorious.hostCount();
+ hasProjects = hasSelectedHost && gitorious.projectCount(row) > 0;
+ }
+ ui->deleteToolButton->setEnabled(hasSelectedHost);
+ ui->browseToolButton->setEnabled(hasSelectedHost);
+
+ const bool valid = hasSelectedHost && hasProjects;
+ if (valid != m_isValid) {
+ m_isValid = valid;
+ emit validChanged();
+ }
+}
+
+bool GitoriousHostWidget::isValid() const
+{
+ return m_isValid;
+}
+
+bool GitoriousHostWidget::isHostListDirty() const
+{
+ return m_isHostListDirty;
+}
+
+void GitoriousHostWidget::slotClearError()
+{
+ ui->errorLabel->setVisible(false);
+ ui->errorLabel->clear();
+}
+
+void GitoriousHostWidget::slotError(const QString &e)
+{
+ // Display error for a while
+ ui->errorLabel->setText(e);
+ ui->errorLabel->setVisible(true);
+ if (!m_errorClearTimer) {
+ m_errorClearTimer = new QTimer(this);
+ m_errorClearTimer->setSingleShot(true);
+ m_errorClearTimer->setInterval(5000);
+ connect(m_errorClearTimer, SIGNAL(timeout()), this, SLOT(slotClearError()));
+ }
+ if (!m_errorClearTimer->isActive())
+ m_errorClearTimer->start();
+}
+
+void GitoriousHostWidget::changeEvent(QEvent *e)
+{
+ QWidget::changeEvent(e);
+ switch (e->type()) {
+ case QEvent::LanguageChange:
+ ui->retranslateUi(this);
+ break;
+ default:
+ break;
+ }
+}
+
+} // namespace Internal
+} // namespace Gitorious
diff --git a/src/plugins/git/gitorious/gitorioushostwidget.h b/src/plugins/git/gitorious/gitorioushostwidget.h
new file mode 100644
index 0000000000..961ae68bbe
--- /dev/null
+++ b/src/plugins/git/gitorious/gitorioushostwidget.h
@@ -0,0 +1,103 @@
+/**************************************************************************
+**
+** 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://www.qtsoftware.com/contact.
+**
+**************************************************************************/
+
+#ifndef GITORIOUSHOSTWIDGET_H
+#define GITORIOUSHOSTWIDGET_H
+
+#include <QtGui/QWizardPage>
+#include <QtGui/QStandardItemModel>
+
+QT_BEGIN_NAMESPACE
+class QStandardItemModel;
+class QStandardItem;
+class QModelIndex;
+class QTimer;
+
+QT_END_NAMESPACE
+
+namespace Gitorious {
+namespace Internal {
+
+namespace Ui {
+ class GitoriousHostWidget;
+}
+
+/* A page listing gitorious hosts with browse/add options. isValid() and the
+ * related change signals are provided for use within a QWizardPage.
+ * Connects to the signals of Gitorious and updates the project count as the
+ * it receives the projects. As soon as there are projects, isValid() becomes
+ * true. */
+
+class GitoriousHostWidget : public QWidget {
+ Q_OBJECT
+public:
+ GitoriousHostWidget(QWidget *parent = 0);
+ ~GitoriousHostWidget();
+
+ // Has a host selected that has projects.
+ bool isValid() const;
+ int selectedRow() const;
+ // hosts modified?
+ bool isHostListDirty() const;
+
+signals:
+ void validChanged();
+
+public slots:
+ void selectRow(int);
+
+protected:
+ void changeEvent(QEvent *e);
+
+private slots:
+ void slotBrowse();
+ void slotDelete();
+ void slotCurrentChanged(const QModelIndex &current, const QModelIndex &previous);
+ void slotItemEdited(QStandardItem *item);
+ void slotProjectListPageReceived(int row);
+ void slotClearError();
+ void slotError(const QString &e);
+
+private:
+ void appendNewDummyEntry();
+ void checkValid(const QModelIndex &current);
+ QStandardItem *currentItem() const;
+
+ const QString m_newHost;
+
+ Ui::GitoriousHostWidget *ui;
+ QStandardItemModel *m_model;
+ QTimer *m_errorClearTimer;
+ bool m_isValid;
+ bool m_isHostListDirty;
+};
+
+} // namespace Internal
+} // namespace Gitorious
+#endif // GITORIOUSHOSTWIDGET_H
diff --git a/src/plugins/git/gitorious/gitorioushostwidget.ui b/src/plugins/git/gitorious/gitorioushostwidget.ui
new file mode 100644
index 0000000000..e9896977ec
--- /dev/null
+++ b/src/plugins/git/gitorious/gitorioushostwidget.ui
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Gitorious::Internal::GitoriousHostWidget</class>
+ <widget class="QWidget" name="Gitorious::Internal::GitoriousHostWidget">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>356</width>
+ <height>265</height>
+ </rect>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QTreeView" name="hostView"/>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="buttonLayout">
+ <item>
+ <widget class="QToolButton" name="browseToolButton">
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="deleteToolButton">
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="buttonSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QLabel" name="errorLabel">
+ <property name="styleSheet">
+ <string notr="true">background-color: red;</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/plugins/git/gitorious/gitorioushostwizardpage.cpp b/src/plugins/git/gitorious/gitorioushostwizardpage.cpp
new file mode 100644
index 0000000000..82a1c61a7d
--- /dev/null
+++ b/src/plugins/git/gitorious/gitorioushostwizardpage.cpp
@@ -0,0 +1,100 @@
+/**************************************************************************
+**
+** 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://www.qtsoftware.com/contact.
+**
+**************************************************************************/
+
+
+#include "gitorioushostwizardpage.h"
+#include "gitorioushostwidget.h"
+#include "gitorious.h"
+
+#include <coreplugin/icore.h>
+
+#include <QtCore/QSettings>
+#include <QtGui/QVBoxLayout>
+
+static const char *settingsGroupC = "Gitorious";
+static const char *selectionKeyC = "/SelectedHost";
+
+namespace Gitorious {
+namespace Internal {
+
+// Ensure Gitorious is populated and create widget in right order.
+static GitoriousHostWidget *createHostWidget()
+{
+ // First time? Populate gitorious from settings.
+ // If there is still no host, add "gitorious.org"
+ Gitorious &gitorious = Gitorious::instance();
+ const QSettings *settings = Core::ICore::instance()->settings();
+ const QString group = QLatin1String(settingsGroupC);
+ if (!gitorious.hostCount()) {
+ gitorious.restoreSettings(group, settings);
+ if (!gitorious.hostCount())
+ gitorious.addHost(Gitorious::gitoriousOrg());
+ }
+ // Now create widget
+ GitoriousHostWidget *rc = new GitoriousHostWidget;
+ // Restore selection
+ const int selectedRow = settings->value(group + QLatin1String(selectionKeyC)).toInt();
+ if (selectedRow >= 0 && selectedRow < gitorious.hostCount())
+ rc->selectRow(selectedRow);
+ return rc;
+}
+
+GitoriousHostWizardPage::GitoriousHostWizardPage(QWidget *parent) :
+ QWizardPage(parent),
+ m_widget(createHostWidget())
+{
+ connect(m_widget, SIGNAL(validChanged()), this, SIGNAL(completeChanged()));
+ QVBoxLayout *lt = new QVBoxLayout;
+ lt->addWidget(m_widget);
+ setLayout(lt);
+ setSubTitle(tr("Select a host."));
+}
+
+GitoriousHostWizardPage::~GitoriousHostWizardPage()
+{
+ // Write out settings + selected row.
+ QSettings *settings = Core::ICore::instance()->settings();
+ if (m_widget->isHostListDirty())
+ Gitorious::instance().saveSettings(QLatin1String(settingsGroupC), settings);
+ if (m_widget->isValid())
+ settings->setValue(QLatin1String(settingsGroupC) + QLatin1String(selectionKeyC), m_widget->selectedRow());
+}
+
+bool GitoriousHostWizardPage::isComplete() const
+{
+ return m_widget->isValid();
+}
+
+int GitoriousHostWizardPage::selectedHostIndex() const
+{
+ return m_widget->selectedRow();
+}
+
+} // namespace Internal
+} // namespace Gitorious
diff --git a/src/plugins/git/gitorious/gitorioushostwizardpage.h b/src/plugins/git/gitorious/gitorioushostwizardpage.h
new file mode 100644
index 0000000000..025c69f772
--- /dev/null
+++ b/src/plugins/git/gitorious/gitorioushostwizardpage.h
@@ -0,0 +1,58 @@
+/**************************************************************************
+**
+** 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://www.qtsoftware.com/contact.
+**
+**************************************************************************/
+
+#ifndef GITORIOUSHOSTWIZARDPAGE_H
+#define GITORIOUSHOSTWIZARDPAGE_H
+
+#include <QtGui/QWizardPage>
+
+namespace Gitorious {
+namespace Internal {
+
+class GitoriousHostWidget;
+
+/* A page listing gitorious hosts with browse/add options. */
+
+class GitoriousHostWizardPage : public QWizardPage {
+ Q_OBJECT
+public:
+ GitoriousHostWizardPage(QWidget *parent = 0);
+ virtual ~GitoriousHostWizardPage();
+
+ virtual bool isComplete() const;
+
+ int selectedHostIndex() const;
+
+private:
+ GitoriousHostWidget *m_widget;
+};
+
+} // namespace Internal
+} // namespace Gitorious
+#endif // GITORIOUSHOSTWIZARDPAGE_H
diff --git a/src/plugins/git/gitorious/gitoriousprojectswizardwidget.cpp b/src/plugins/git/gitorious/gitoriousprojectswizardwidget.cpp
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/src/plugins/git/gitorious/gitoriousprojectswizardwidget.cpp
diff --git a/src/plugins/git/gitorious/gitoriousprojectwidget.cpp b/src/plugins/git/gitorious/gitoriousprojectwidget.cpp
new file mode 100644
index 0000000000..6894214cb3
--- /dev/null
+++ b/src/plugins/git/gitorious/gitoriousprojectwidget.cpp
@@ -0,0 +1,304 @@
+/**************************************************************************
+**
+** 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://www.qtsoftware.com/contact.
+**
+**************************************************************************/
+
+#include "gitoriousprojectwidget.h"
+#include "gitorioushostwizardpage.h"
+#include "gitorious.h"
+#include "ui_gitoriousprojectwidget.h"
+
+#include <coreplugin/coreconstants.h>
+#include <utils/qtcassert.h>
+
+#include <QtCore/QRegExp>
+#include <QtCore/QDebug>
+
+#include <QtGui/QStandardItemModel>
+#include <QtGui/QSortFilterProxyModel>
+#include <QtGui/QStandardItem>
+#include <QtGui/QItemSelectionModel>
+#include <QtGui/QDesktopServices>
+#include <QtGui/QIcon>
+#include <QtGui/QStyle>
+
+enum {
+ urlRole = Qt::UserRole + 1 // Project has a URL in the description
+};
+
+enum { debug = 1 };
+
+namespace Gitorious {
+namespace Internal {
+
+enum { ProjectColumn, DescriptionColumn, ColumnCount };
+
+GitoriousProjectWidget::GitoriousProjectWidget(int hostIndex,
+ QWidget *parent) :
+ QWidget(parent),
+ m_hostName(Gitorious::instance().hostName(hostIndex)),
+ ui(new Ui::GitoriousProjectWidget),
+ m_model(new QStandardItemModel(0, ColumnCount, this)),
+ m_filterModel(new QSortFilterProxyModel),
+ m_valid(false)
+{
+ ui->setupUi(this);
+ ui->infoToolButton->setIcon(style()->standardIcon(QStyle::SP_MessageBoxInformation));
+ ui->infoToolButton->setEnabled(false);
+ connect(ui->infoToolButton, SIGNAL(clicked()), this, SLOT(slotInfo()));
+ // Filter
+ connect(ui->filterLineEdit, SIGNAL(textChanged(QString)), m_filterModel, SLOT(setFilterFixedString(QString)));
+ ui->filterClearButton->setIcon(QIcon(Core::Constants::ICON_RESET));
+ connect(ui->filterClearButton, SIGNAL(clicked()), ui->filterLineEdit, SLOT(clear()));
+ // Updater
+ ui->updateCheckBox->setChecked(true);
+ if (Gitorious::instance().hostState(hostIndex) != GitoriousHost::ProjectsQueryRunning)
+ ui->updateCheckBox->setVisible(false);
+ connect(ui->updateCheckBox, SIGNAL(stateChanged(int)), this, SLOT(slotUpdateCheckBoxChanged(int)));
+ // Model
+ QStringList headers;
+ headers << tr("Project") << tr("Description");
+ m_model->setHorizontalHeaderLabels(headers);
+ // Populate the model
+ slotUpdateProjects(hostIndex);
+ // Filter on all columns
+ m_filterModel->setSourceModel(m_model);
+ m_filterModel->setFilterKeyColumn(-1);
+ m_filterModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
+ m_filterModel->setSortCaseSensitivity(Qt::CaseInsensitive);
+ ui->projectTreeView->setModel(m_filterModel);
+ // View
+ ui->projectTreeView->setAlternatingRowColors(true);
+ ui->projectTreeView->setRootIsDecorated(false);
+ ui->projectTreeView->setUniformRowHeights(true);
+ ui->projectTreeView->setSortingEnabled(true);
+ connect(ui->projectTreeView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
+ this, SLOT(slotCurrentChanged(QModelIndex,QModelIndex)));
+ ui->projectTreeView->setSelectionMode(QAbstractItemView::SingleSelection);
+ // Select first, resize columns
+ if (Gitorious::instance().projectCount(hostIndex)) {
+ for (int r = 0; r < ColumnCount; r++)
+ ui->projectTreeView->resizeColumnToContents(r);
+ // Select first
+ const QModelIndex index = m_filterModel->index(0, 0);
+ ui->projectTreeView->selectionModel()->setCurrentIndex(index, QItemSelectionModel::Select|QItemSelectionModel::Current|QItemSelectionModel::Rows);
+ }
+
+ // Continuous update
+ Gitorious *gitorious = &Gitorious::instance();
+ connect(gitorious, SIGNAL(projectListPageReceived(int,int)), this, SLOT(slotUpdateProjects(int)));
+ connect(gitorious, SIGNAL(projectListReceived(int)), this, SLOT(slotUpdateProjects(int)));
+}
+
+GitoriousProjectWidget::~GitoriousProjectWidget()
+{
+ Gitorious *gitorious = &Gitorious::instance();
+ disconnect(gitorious, SIGNAL(projectListPageReceived(int,int)), this, SLOT(slotUpdateProjects(int)));
+ disconnect(gitorious, SIGNAL(projectListReceived(int)), this, SLOT(slotUpdateProjects(int)));
+ delete ui;
+}
+
+// Map indexes back via filter
+QStandardItem *GitoriousProjectWidget::itemFromIndex(const QModelIndex &index) const
+{
+ if (index.isValid())
+ return m_model->itemFromIndex(m_filterModel->mapToSource(index));
+ return 0;
+}
+
+QStandardItem *GitoriousProjectWidget::currentItem() const
+{
+ return itemFromIndex(ui->projectTreeView->selectionModel()->currentIndex());
+}
+
+void GitoriousProjectWidget::slotCurrentChanged(const QModelIndex &current, const QModelIndex & /* previous */)
+{
+ // Any info URL to show?
+ QString url;
+ if (current.isValid())
+ if (QStandardItem *item = itemFromIndex(current)) {
+ // Project: URL in description?
+ const QVariant urlV = item->data(urlRole);
+ if (urlV.isValid())
+ url = urlV.toString();
+ }
+
+ ui->infoToolButton->setEnabled(!url.isEmpty());
+ ui->infoToolButton->setToolTip(url);
+
+ const bool isValid = current.isValid();
+ if (isValid != m_valid) {
+ m_valid = isValid;
+ emit validChanged();
+ }
+}
+
+void GitoriousProjectWidget::slotInfo()
+{
+ if (const QStandardItem *item = currentItem()) {
+ const QVariant url = item->data(urlRole);
+ if (url.isValid())
+ QDesktopServices::openUrl(QUrl(url.toString()));
+ }
+}
+
+// Create a model row for a project
+static inline QList<QStandardItem *> projectEntry(const GitoriousProject &p)
+{
+ enum { maxNameLength = 30 };
+ // Truncate names with colons
+ QString name = p.name;
+ const int colonPos = name.indexOf(QLatin1Char(':'));
+ if (colonPos != -1)
+ name.truncate(colonPos);
+ if (name.size() > maxNameLength) {
+ name.truncate(maxNameLength);
+ name += QLatin1String("...");
+ }
+ QStandardItem *nameItem = new QStandardItem(name);
+ nameItem->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled);
+ // Description
+ QStandardItem *descriptionItem = new QStandardItem;
+ descriptionItem->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled);
+ QList<QStandardItem *> rc;
+ rc << nameItem << descriptionItem;
+ // Should the text contain an URL, store it under 'urlRole' for the info button
+ QString url;
+ GitoriousProjectWidget::setDescription(p.description, DescriptionColumn, &rc, &url);
+ if (!url.isEmpty()) {
+ const QVariant urlV = QVariant(url);
+ nameItem->setData(urlV, urlRole);
+ descriptionItem->setData(urlV, urlRole);
+ }
+ return rc;
+}
+
+// Utility to set description column and tooltip for a row from a free
+// format/HTMLish gitorious description. Make sure the description is just one
+// row for the item and set a tooltip with full contents. If desired, extract
+// an URL.
+
+void GitoriousProjectWidget::setDescription(const QString &description,
+ int descriptionColumn,
+ QList<QStandardItem *> *items,
+ QString *url /* =0 */)
+{
+ enum { MaxDescriptionLineLength = 70 };
+ // Trim description to 1 sensibly long line for the item view
+ QString descLine = description;
+ const int newLinePos = descLine.indexOf(QLatin1Char('\n'));
+ if (newLinePos != -1)
+ descLine.truncate(newLinePos);
+ if (descLine.size() > MaxDescriptionLineLength) {
+ const int dotPos = descLine.lastIndexOf(QLatin1Char('.'), MaxDescriptionLineLength);
+ if (dotPos != -1) {
+ descLine.truncate(dotPos);
+ } else {
+ descLine.truncate(MaxDescriptionLineLength);
+ }
+ descLine += QLatin1String("...");
+ }
+ items->at(descriptionColumn)->setText(descLine);
+ // Set a HTML tooltip to make lines wrap and the markup sprinkled within work
+ const QString htmlTip = QLatin1String("<html><body>") + description + QLatin1String("</body></html>");
+ const int size = items->size();
+ for (int i = 0; i < size; i++)
+ items->at(i)->setToolTip(htmlTip);
+ if (url) {
+ // Should the text contain an URL, extract
+ // Do not fall for "(http://XX)", strip special characters
+ static const QRegExp urlRegExp(QLatin1String("(http://[\\w\\.-]+/[a-zA-Z0-9/\\-&]*)"));
+ Q_ASSERT(urlRegExp.isValid());
+ if (urlRegExp.indexIn(description) != -1) {
+ *url= urlRegExp.cap(1);
+ } else {
+ url->clear();
+ }
+ }
+}
+
+void GitoriousProjectWidget::grabFocus()
+{
+ ui->projectTreeView->setFocus();
+}
+
+void GitoriousProjectWidget::slotUpdateCheckBoxChanged(int state)
+{
+ if (state == Qt::Checked)
+ slotUpdateProjects(Gitorious::instance().findByHostName(m_hostName));
+}
+
+void GitoriousProjectWidget::slotUpdateProjects(int hostIndex)
+{
+ if (!ui->updateCheckBox->isChecked())
+ return;
+ const Gitorious &gitorious = Gitorious::instance();
+ // Complete list of projects
+ if (m_hostName != gitorious.hostName(hostIndex))
+ return;
+ // Fill in missing projects
+ const GitoriousHost::ProjectList &projects = gitorious.hosts().at(hostIndex).projects;
+ const int size = projects.size();
+ for (int i = m_model->rowCount(); i < size; i++)
+ m_model->appendRow(projectEntry(*projects.at(i)));
+ if (gitorious.hostState(hostIndex) == GitoriousHost::ProjectsComplete)
+ ui->updateCheckBox->setVisible(false);
+}
+
+bool GitoriousProjectWidget::isValid() const
+{
+ return m_valid;
+}
+
+int GitoriousProjectWidget::hostIndex() const
+{
+ return Gitorious::instance().findByHostName(m_hostName);
+}
+
+QSharedPointer<GitoriousProject> GitoriousProjectWidget::project() const
+{
+ if (const QStandardItem *item = currentItem()) {
+ const int projectIndex = item->row();
+ return Gitorious::instance().hosts().at(hostIndex()).projects.at(projectIndex);
+ }
+ return QSharedPointer<GitoriousProject>(new GitoriousProject);
+}
+
+void GitoriousProjectWidget::changeEvent(QEvent *e)
+{
+ QWidget::changeEvent(e);
+ switch (e->type()) {
+ case QEvent::LanguageChange:
+ ui->retranslateUi(this);
+ break;
+ default:
+ break;
+ }
+}
+
+} // namespace Internal
+} // namespace Gitorious
diff --git a/src/plugins/git/gitorious/gitoriousprojectwidget.h b/src/plugins/git/gitorious/gitoriousprojectwidget.h
new file mode 100644
index 0000000000..6151b381e1
--- /dev/null
+++ b/src/plugins/git/gitorious/gitoriousprojectwidget.h
@@ -0,0 +1,113 @@
+/**************************************************************************
+**
+** 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://www.qtsoftware.com/contact.
+**
+**************************************************************************/
+
+#ifndef GITORIOUSPROJECTWIDGET_H
+#define GITORIOUSPROJECTWIDGET_H
+
+#include <QtCore/QSharedPointer>
+#include <QtGui/QWidget>
+
+QT_BEGIN_NAMESPACE
+class QStandardItemModel;
+class QStandardItem;
+class QModelIndex;
+class QSortFilterProxyModel;
+QT_END_NAMESPACE
+
+namespace Gitorious {
+namespace Internal {
+
+class GitoriousHostWizardPage;
+class GitoriousProject;
+
+namespace Ui {
+ class GitoriousProjectWidget;
+}
+
+/* Let the user select a project from a host. Displays name and description
+ * with tooltip and info button that opens URLs contained in the description.
+ * Connects to the signals of Gitorious and updates the project list as the
+ * it receives the projects. isValid() and signal validChanged are
+ * provided for use in a QWizardPage. Host matching happens via name as the
+ * hostIndex might change due to deleting hosts. */
+class GitoriousProjectWidget : public QWidget {
+ Q_OBJECT
+public:
+ explicit GitoriousProjectWidget(int hostIndex,
+ QWidget *parent = 0);
+ ~GitoriousProjectWidget();
+
+ virtual bool isValid() const;
+
+ QSharedPointer<GitoriousProject> project() const;
+
+ QString hostName() const { return m_hostName; }
+ int hostIndex() const;
+
+ // Utility to set description column and tooltip for a row from a free
+ // format/HTMLish gitorious description. Make sure the description is
+ // just one row for the item and set a tooltip with full contents.
+ // If desired, extract an URL.
+ static void setDescription(const QString &description,
+ int descriptionColumn,
+ QList<QStandardItem *> *items,
+ QString *url = 0);
+
+signals:
+ void validChanged();
+
+public slots:
+ void grabFocus();
+
+private slots:
+ void slotCurrentChanged(const QModelIndex &current, const QModelIndex &previous);
+ void slotInfo();
+ void slotUpdateProjects(int hostIndex);
+ void slotUpdateCheckBoxChanged(int);
+
+protected:
+ void changeEvent(QEvent *e);
+
+private:
+ QStandardItem *itemFromIndex(const QModelIndex &idx) const;
+ QStandardItem *currentItem() const;
+
+ const QString m_hostName;
+
+ Ui::GitoriousProjectWidget *ui;
+ const GitoriousHostWizardPage *m_hostPage;
+ QStandardItemModel *m_model;
+ QSortFilterProxyModel *m_filterModel;
+ bool m_valid;
+};
+
+
+} // namespace Internal
+} // namespace Gitorious
+#endif // GITORIOUSPROJECTWIDGET_H
diff --git a/src/plugins/git/gitorious/gitoriousprojectwidget.ui b/src/plugins/git/gitorious/gitoriousprojectwidget.ui
new file mode 100644
index 0000000000..f478c125b6
--- /dev/null
+++ b/src/plugins/git/gitorious/gitoriousprojectwidget.ui
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Gitorious::Internal::GitoriousProjectWidget</class>
+ <widget class="QWidget" name="Gitorious::Internal::GitoriousProjectWidget">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>300</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>WizardPage</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLabel" name="filterLabel">
+ <property name="text">
+ <string>Filter:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="filterLineEdit"/>
+ </item>
+ <item>
+ <widget class="QToolButton" name="filterClearButton">
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="0">
+ <widget class="QTreeView" name="projectTreeView"/>
+ </item>
+ <item row="1" column="1">
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QToolButton" name="infoToolButton">
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="buttonVerticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item row="2" column="0">
+ <widget class="QCheckBox" name="updateCheckBox">
+ <property name="text">
+ <string>Keep updating</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/plugins/git/gitorious/gitoriousprojectwizardpage.cpp b/src/plugins/git/gitorious/gitoriousprojectwizardpage.cpp
new file mode 100644
index 0000000000..c36915d4fe
--- /dev/null
+++ b/src/plugins/git/gitorious/gitoriousprojectwizardpage.cpp
@@ -0,0 +1,133 @@
+/**************************************************************************
+**
+** 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://www.qtsoftware.com/contact.
+**
+**************************************************************************/
+
+#include "gitoriousprojectwizardpage.h"
+#include "gitoriousprojectwidget.h"
+#include "gitorioushostwizardpage.h"
+#include "gitorious.h"
+
+#include <utils/qtcassert.h>
+
+#include <QtGui/QStackedWidget>
+#include <QtGui/QVBoxLayout>
+
+namespace Gitorious {
+namespace Internal {
+
+GitoriousProjectWizardPage::GitoriousProjectWizardPage(const GitoriousHostWizardPage *hostPage,
+ QWidget *parent) :
+ QWizardPage(parent),
+ m_hostPage(hostPage),
+ m_stackedWidget(new QStackedWidget),
+ m_isValid(false)
+{
+ QVBoxLayout *lt = new QVBoxLayout;
+ lt->addWidget(m_stackedWidget);
+ setLayout(lt);
+}
+
+static inline QString msgChooseProject(const QString &h)
+{
+ return GitoriousProjectWizardPage::tr("Choose a project from '%1'").arg((h));
+}
+
+QString GitoriousProjectWizardPage::selectedHostName() const
+{
+ if (const GitoriousProjectWidget *w = currentProjectWidget())
+ return w->hostName();
+ return QString();
+}
+
+void GitoriousProjectWizardPage::initializePage()
+{
+ // Try to find the page by hostindex
+ const int hostIndex = m_hostPage->selectedHostIndex();
+ const int existingStackIndex = hostIndexToStackIndex(hostIndex);
+ // Found? - pop up that page
+ if (existingStackIndex != -1) {
+ m_stackedWidget->setCurrentIndex(existingStackIndex);
+ setSubTitle(msgChooseProject(selectedHostName()));
+ return;
+ }
+ // Add a new page
+ GitoriousProjectWidget *widget = new GitoriousProjectWidget(hostIndex);
+ connect(widget, SIGNAL(validChanged()), this, SLOT(slotCheckValid()));
+ m_stackedWidget->addWidget(widget);
+ m_stackedWidget->setCurrentIndex(m_stackedWidget->count() - 1);
+ setSubTitle(msgChooseProject(widget->hostName()));
+ slotCheckValid();
+}
+
+bool GitoriousProjectWizardPage::isComplete() const
+{
+ return m_isValid;
+}
+
+void GitoriousProjectWizardPage::slotCheckValid()
+{
+ const GitoriousProjectWidget *w = currentProjectWidget();
+ const bool isValid = w ? w->isValid() : false;
+ if (isValid != m_isValid) {
+ m_isValid = isValid;
+ emit completeChanged();
+ }
+}
+
+QSharedPointer<GitoriousProject> GitoriousProjectWizardPage::project() const
+{
+ if (const GitoriousProjectWidget *w = currentProjectWidget())
+ return w->project();
+ return QSharedPointer<GitoriousProject>();
+}
+
+GitoriousProjectWidget *GitoriousProjectWizardPage::projectWidgetAt(int index) const
+{
+ return qobject_cast<GitoriousProjectWidget *>(m_stackedWidget->widget(index));
+}
+
+GitoriousProjectWidget *GitoriousProjectWizardPage::currentProjectWidget() const
+{
+ const int index = m_stackedWidget->currentIndex();
+ if (index < 0)
+ return 0;
+ return projectWidgetAt(index);
+}
+
+// Convert a host index to a stack index.
+int GitoriousProjectWizardPage::hostIndexToStackIndex(int hostIndex) const
+{
+ const int count = m_stackedWidget->count();
+ for(int i = 0; i < count; i++)
+ if (projectWidgetAt(i)->hostIndex() == hostIndex)
+ return i;
+ return -1;
+}
+
+} // namespace Internal
+} // namespace Gitorious
diff --git a/src/plugins/git/gitorious/gitoriousprojectwizardpage.h b/src/plugins/git/gitorious/gitoriousprojectwizardpage.h
new file mode 100644
index 0000000000..abe4b3f2d2
--- /dev/null
+++ b/src/plugins/git/gitorious/gitoriousprojectwizardpage.h
@@ -0,0 +1,87 @@
+/**************************************************************************
+**
+** 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://www.qtsoftware.com/contact.
+**
+**************************************************************************/
+
+#ifndef GITORIOUSPROJECTWIZARDPAGE_H
+#define GITORIOUSPROJECTWIZARDPAGE_H
+
+#include <QtCore/QSharedPointer>
+#include <QtGui/QWizardPage>
+
+QT_BEGIN_NAMESPACE
+class QStackedWidget;
+QT_END_NAMESPACE
+
+namespace Gitorious {
+namespace Internal {
+
+class GitoriousHostWizardPage;
+class GitoriousProject;
+class GitoriousProjectWidget;
+
+namespace Ui {
+ class GitoriousProjectWizardPage;
+}
+
+/* GitoriousProjectWizardPage: Let the user select a project via
+ * GitoriousProjectWidget. As switching back and forth hosts (repopulating
+ * the sorting projects model/treeviews) might get slow when the host has
+ * lots of projects, it manages a stack of project widgets and activates
+ * the one selected in the host page (or creates a new one) in
+ * initializePage. */
+
+class GitoriousProjectWizardPage : public QWizardPage {
+ Q_OBJECT
+public:
+ explicit GitoriousProjectWizardPage(const GitoriousHostWizardPage *hostPage,
+ QWidget *parent = 0);
+
+ virtual void initializePage();
+ virtual bool isComplete() const;
+
+ QSharedPointer<GitoriousProject> project() const;
+ int selectedHostIndex() const;
+ QString selectedHostName() const;
+
+private slots:
+ void slotCheckValid();
+
+private:
+ GitoriousProjectWidget *projectWidgetAt(int index) const;
+ GitoriousProjectWidget *currentProjectWidget() const;
+ int hostIndexToStackIndex(int hostIndex) const;
+
+ const GitoriousHostWizardPage *m_hostPage;
+ QStackedWidget *m_stackedWidget;
+ bool m_isValid;
+
+};
+
+} // namespace Internal
+} // namespace Gitorious
+#endif // GITORIOUSPROJECTWIZARDPAGE_H
diff --git a/src/plugins/git/gitorious/gitoriousrepositorywizardpage.cpp b/src/plugins/git/gitorious/gitoriousrepositorywizardpage.cpp
new file mode 100644
index 0000000000..bbe11e5b3e
--- /dev/null
+++ b/src/plugins/git/gitorious/gitoriousrepositorywizardpage.cpp
@@ -0,0 +1,211 @@
+/**************************************************************************
+**
+** 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://www.qtsoftware.com/contact.
+**
+**************************************************************************/
+
+#include "gitoriousrepositorywizardpage.h"
+#include "gitoriousprojectwizardpage.h"
+#include "gitoriousprojectwidget.h"
+#include "gitorious.h"
+#include "ui_gitoriousrepositorywizardpage.h"
+
+#include <utils/qtcassert.h>
+
+#include <QtCore/QDebug>
+
+#include <QtGui/QStandardItemModel>
+#include <QtGui/QStandardItem>
+#include <QtGui/QItemSelectionModel>
+
+enum { TypeRole = Qt::UserRole + 1};
+enum { HeaderType, RepositoryType };
+
+enum { debug = 0 };
+
+namespace Gitorious {
+namespace Internal {
+
+enum { RepositoryColumn, OwnerColumn, DescriptionColumn, ColumnCount };
+
+GitoriousRepositoryWizardPage::GitoriousRepositoryWizardPage(const GitoriousProjectWizardPage *projectPage,
+ QWidget *parent) :
+ QWizardPage(parent),
+ ui(new Ui::GitoriousRepositoryWizardPage),
+ m_projectPage(projectPage),
+ m_model(new QStandardItemModel(0, ColumnCount)),
+ m_valid(false)
+{
+ QStringList headers;
+ headers << tr("Name") << tr("Owner") << tr("Description");
+ m_model->setHorizontalHeaderLabels(headers);
+
+ ui->setupUi(this);
+ ui->repositoryTreeView->setModel(m_model);
+ ui->repositoryTreeView->setUniformRowHeights(true);
+ ui->repositoryTreeView->setAlternatingRowColors(true);
+ ui->repositoryTreeView->setSelectionMode(QAbstractItemView::SingleSelection);
+ connect(ui->repositoryTreeView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
+ this, SLOT(slotCurrentChanged(QModelIndex,QModelIndex)));
+}
+
+GitoriousRepositoryWizardPage::~GitoriousRepositoryWizardPage()
+{
+ delete ui;
+}
+
+bool gitRepoLessThanByType(const GitoriousRepository &r1, const GitoriousRepository &r2)
+{
+ return r1.type < r2.type;
+}
+
+static inline QList<QStandardItem *> headerEntry(const QString &h)
+{
+ QStandardItem *nameItem = new QStandardItem(h);
+ nameItem->setFlags(Qt::ItemIsEnabled);
+ nameItem->setData(QVariant(HeaderType), TypeRole);
+ QStandardItem *ownerItem = new QStandardItem;
+ ownerItem->setFlags(Qt::ItemIsEnabled);
+ ownerItem->setData(QVariant(HeaderType), TypeRole);
+ QStandardItem *descriptionItem = new QStandardItem;
+ descriptionItem->setFlags(Qt::ItemIsEnabled);
+ descriptionItem->setData(QVariant(HeaderType), TypeRole);
+ QList<QStandardItem *> rc;
+ rc << nameItem << ownerItem << descriptionItem;
+ return rc;
+}
+
+static inline QList<QStandardItem *> repositoryEntry(const GitoriousRepository &r)
+{
+ QStandardItem *nameItem = new QStandardItem(r.name);
+ nameItem->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled);
+ nameItem->setData(QVariant(RepositoryType), TypeRole);
+ QStandardItem *ownerItem = new QStandardItem(r.owner);
+ ownerItem->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled);
+ ownerItem->setData(QVariant(RepositoryType), TypeRole);
+ QStandardItem *descriptionItem = new QStandardItem;
+ descriptionItem->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled);
+ descriptionItem->setData(QVariant(RepositoryType), TypeRole);
+ QList<QStandardItem *> rc;
+ rc << nameItem << ownerItem << descriptionItem;
+ GitoriousProjectWidget::setDescription(r.description, DescriptionColumn, &rc);
+ return rc;
+}
+
+void GitoriousRepositoryWizardPage::initializePage()
+{
+ // Populate the model
+ ui->repositoryTreeView->selectionModel()->clearSelection();
+ if (const int oldRowCount = m_model->rowCount())
+ m_model->removeRows(0, oldRowCount);
+ // fill model
+ const QSharedPointer<GitoriousProject> proj = m_projectPage->project();
+ setSubTitle(tr("Choose a repository of the project '%1'.").arg(proj->name));
+ // Create a hierarchical list by repository type, sort by type
+ QList<GitoriousRepository> repositories = proj->repositories;
+ QStandardItem *firstEntry = 0;
+ if (!repositories.empty()) {
+ int lastRepoType = -1;
+ QStandardItem *header = 0;
+ qStableSort(repositories.begin(), repositories.end(), gitRepoLessThanByType);
+ const QString types[GitoriousRepository::PersonalRepository + 1] =
+ { tr("Mainline Repositories"), tr("Clones"), tr("Baseline Repositories"), tr("Shared Project Repositories"), tr("Personal Repositories") };
+ foreach(const GitoriousRepository &r, repositories) {
+ // New Header?
+ if (r.type != lastRepoType || !header) {
+ lastRepoType = r.type;
+ const QList<QStandardItem *> headerRow = headerEntry(types[r.type]);
+ m_model->appendRow(headerRow);
+ header = headerRow.front();
+ }
+ // Repository row
+ const QList<QStandardItem *> row = repositoryEntry(r);
+ header->appendRow(row);
+ if (!firstEntry)
+ firstEntry = row.front();
+ }
+ }
+ ui->repositoryTreeView->expandAll();
+ for (int r = 0; r < ColumnCount; r++)
+ ui->repositoryTreeView->resizeColumnToContents(r);
+ // Select first
+ if (firstEntry) {
+ const QModelIndex idx = m_model->indexFromItem(firstEntry);
+ ui->repositoryTreeView->selectionModel()->setCurrentIndex(idx, QItemSelectionModel::Select|QItemSelectionModel::Current|QItemSelectionModel::Rows);
+ }
+}
+
+void GitoriousRepositoryWizardPage::slotCurrentChanged(const QModelIndex &current, const QModelIndex & /*previous */)
+{
+ const QStandardItem *item = current.isValid() ? m_model->itemFromIndex(current) : static_cast<const QStandardItem *>(0);
+ const bool isValid = item && item->data(TypeRole).toInt() == RepositoryType;
+ if (isValid != m_valid) {
+ m_valid = isValid;
+ emit completeChanged();
+ }
+}
+
+QString GitoriousRepositoryWizardPage::repositoryName() const
+{
+ const QModelIndex idx = ui->repositoryTreeView->selectionModel()->currentIndex();
+ if (idx.isValid()) {
+ const QModelIndex sibling0 = idx.column() ? idx.sibling(idx.row(), 0) : idx;
+ if (const QStandardItem *item = m_model->itemFromIndex(sibling0))
+ if (item->data(TypeRole).toInt() == RepositoryType)
+ return item->text();
+ }
+ return QString();
+}
+
+QUrl GitoriousRepositoryWizardPage::repositoryURL() const
+{
+ // Find by name (as we sorted the the repositories)
+ const QString repoName = repositoryName();
+ foreach (const GitoriousRepository &r, m_projectPage->project()->repositories)
+ if (r.name == repoName)
+ return r.cloneUrl;
+ return QUrl();
+}
+
+bool GitoriousRepositoryWizardPage::isComplete() const
+{
+ return m_valid;
+}
+
+void GitoriousRepositoryWizardPage::changeEvent(QEvent *e)
+{
+ QWizardPage::changeEvent(e);
+ switch (e->type()) {
+ case QEvent::LanguageChange:
+ ui->retranslateUi(this);
+ break;
+ default:
+ break;
+ }
+}
+
+} // namespace Internal
+} // namespace Gitorious
diff --git a/src/plugins/git/gitorious/gitoriousrepositorywizardpage.h b/src/plugins/git/gitorious/gitoriousrepositorywizardpage.h
new file mode 100644
index 0000000000..f90f415d02
--- /dev/null
+++ b/src/plugins/git/gitorious/gitoriousrepositorywizardpage.h
@@ -0,0 +1,80 @@
+/**************************************************************************
+**
+** 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://www.qtsoftware.com/contact.
+**
+**************************************************************************/
+
+#ifndef GITORIOUSREPOSITORYWIZARDPAGE_H
+#define GITORIOUSREPOSITORYWIZARDPAGE_H
+
+#include <QtGui/QWizardPage>
+
+QT_BEGIN_NAMESPACE
+class QStandardItemModel;
+class QStandardItem;
+class QModelIndex;
+class QUrl;
+QT_END_NAMESPACE
+
+namespace Gitorious {
+namespace Internal {
+
+class GitoriousProjectWizardPage;
+
+namespace Ui {
+ class GitoriousRepositoryWizardPage;
+}
+
+// A wizard page listing Gitorious repositories in a tree, by repository type.
+
+class GitoriousRepositoryWizardPage : public QWizardPage {
+ Q_OBJECT
+public:
+ explicit GitoriousRepositoryWizardPage(const GitoriousProjectWizardPage *projectPage,
+ QWidget *parent = 0);
+ ~GitoriousRepositoryWizardPage();
+
+ virtual void initializePage();
+ virtual bool isComplete() const;
+
+ QString repositoryName() const;
+ QUrl repositoryURL() const;
+
+public slots:
+ void slotCurrentChanged(const QModelIndex &current, const QModelIndex &previous);
+
+protected:
+ void changeEvent(QEvent *e);
+
+ Ui::GitoriousRepositoryWizardPage *ui;
+ const GitoriousProjectWizardPage *m_projectPage;
+ QStandardItemModel *m_model;
+ bool m_valid;
+};
+
+} // namespace Internal
+} // namespace Gitorious
+#endif // GITORIOUSREPOSITORYWIZARDPAGE_H
diff --git a/src/plugins/git/gitorious/gitoriousrepositorywizardpage.ui b/src/plugins/git/gitorious/gitoriousrepositorywizardpage.ui
new file mode 100644
index 0000000000..e85f590389
--- /dev/null
+++ b/src/plugins/git/gitorious/gitoriousrepositorywizardpage.ui
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Gitorious::Internal::GitoriousRepositoryWizardPage</class>
+ <widget class="QWizardPage" name="Gitorious::Internal::GitoriousRepositoryWizardPage">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>300</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>WizardPage</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QTreeView" name="repositoryTreeView"/>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/plugins/git/gitplugin.cpp b/src/plugins/git/gitplugin.cpp
index 8359329819..14e36fcb16 100644
--- a/src/plugins/git/gitplugin.cpp
+++ b/src/plugins/git/gitplugin.cpp
@@ -38,6 +38,7 @@
#include "gitversioncontrol.h"
#include "branchdialog.h"
#include "clonewizard.h"
+#include "gitoriousclonewizard.h"
#include <coreplugin/icore.h>
#include <coreplugin/coreconstants.h>
@@ -216,6 +217,7 @@ bool GitPlugin::initialize(const QStringList &arguments, QString *errorMessage)
addAutoReleasedObject(versionControl);
addAutoReleasedObject(new CloneWizard);
+ addAutoReleasedObject(new Gitorious::Internal::GitoriousCloneWizard);
//register actions
Core::ActionManager *actionManager = m_core->actionManager();
diff --git a/src/plugins/vcsbase/basecheckoutwizardpage.cpp b/src/plugins/vcsbase/basecheckoutwizardpage.cpp
index f9fdb1a697..c058c42555 100644
--- a/src/plugins/vcsbase/basecheckoutwizardpage.cpp
+++ b/src/plugins/vcsbase/basecheckoutwizardpage.cpp
@@ -63,6 +63,16 @@ void BaseCheckoutWizardPage::setRepositoryLabel(const QString &l)
d->ui.repositoryLabel->setText(l);
}
+bool BaseCheckoutWizardPage::isRepositoryReadOnly() const
+{
+ return d->ui.repositoryLineEdit->isReadOnly();
+}
+
+void BaseCheckoutWizardPage::setRepositoryReadOnly(bool v)
+{
+ d->ui.repositoryLineEdit->setReadOnly(v);
+}
+
QString BaseCheckoutWizardPage::path() const
{
return d->ui.pathChooser->path();
diff --git a/src/plugins/vcsbase/basecheckoutwizardpage.h b/src/plugins/vcsbase/basecheckoutwizardpage.h
index adf5d60dcc..b8aab5b090 100644
--- a/src/plugins/vcsbase/basecheckoutwizardpage.h
+++ b/src/plugins/vcsbase/basecheckoutwizardpage.h
@@ -62,6 +62,9 @@ public:
QString repository() const;
void setRepository(const QString &r);
+ bool isRepositoryReadOnly() const;
+ void setRepositoryReadOnly(bool v);
+
virtual bool isComplete() const;
protected:
diff --git a/src/plugins/vcsbase/basecheckoutwizardpage.ui b/src/plugins/vcsbase/basecheckoutwizardpage.ui
index 46b1edfaf7..75ac881df2 100644
--- a/src/plugins/vcsbase/basecheckoutwizardpage.ui
+++ b/src/plugins/vcsbase/basecheckoutwizardpage.ui
@@ -44,19 +44,6 @@
</item>
</layout>
</item>
- <item>
- <spacer name="horizontalSpacer">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
</layout>
</widget>
<customwidgets>