forked from qt-creator/qt-creator
		
	
		
			
				
	
	
		
			305 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			305 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /**************************************************************************
 | |
| **
 | |
| ** This file is part of Qt Creator
 | |
| **
 | |
| ** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
 | |
| **
 | |
| ** Contact: Nokia Corporation (qt-info@nokia.com)
 | |
| **
 | |
| ** Commercial Usage
 | |
| **
 | |
| ** Licensees holding valid Qt Commercial licenses may use this file in
 | |
| ** accordance with the Qt Commercial License Agreement provided with the
 | |
| ** Software or, alternatively, in accordance with the terms contained in
 | |
| ** a written agreement between you and Nokia.
 | |
| **
 | |
| ** GNU Lesser General Public License Usage
 | |
| **
 | |
| ** Alternatively, this file may be used under the terms of the GNU Lesser
 | |
| ** General Public License version 2.1 as published by the Free Software
 | |
| ** Foundation and appearing in the file LICENSE.LGPL included in the
 | |
| ** packaging of this file.  Please review the following information to
 | |
| ** ensure the GNU Lesser General Public License version 2.1 requirements
 | |
| ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
 | |
| **
 | |
| ** If you are unsure which license is appropriate for your use, please
 | |
| ** contact the sales department at http://qt.nokia.com/contact.
 | |
| **
 | |
| **************************************************************************/
 | |
| 
 | |
| #include "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 ¤t, 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
 |