Fixes: Add common configuration to the VCSA base plugin; submit message check script and user name configuration. Details: Extend submit editor widget by configureable fields. Use them in the VCS base submit editor to specify users, provide completion and selection dialog for them.

This commit is contained in:
Friedemann Kleint
2009-03-19 17:40:01 +01:00
parent 40eab8980b
commit 27cb595bbb
19 changed files with 1303 additions and 27 deletions

View File

@@ -0,0 +1,251 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Qt Software Information (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 qt-sales@nokia.com.
**
**************************************************************************/
#include "nicknamedialog.h"
#include "ui_nicknamedialog.h"
#include <QtCore/QDebug>
#include <QtCore/QFile>
#include <QtGui/QPushButton>
#include <QtGui/QStandardItemModel>
#include <QtGui/QSortFilterProxyModel>
namespace VCSBase {
namespace Internal {
struct NickEntry {
void clear();
bool parse(const QString &);
QString nickName() const;
QString name;
QString email;
QString aliasName;
QString aliasEmail;
};
void NickEntry::clear()
{
name.clear();
email.clear();
aliasName.clear();
aliasEmail.clear();
}
// Parse "Hans Mustermann <HM@acme.de> [Alias [<alias@acme.de>]]"
bool NickEntry::parse(const QString &l)
{
clear();
const QChar lessThan = QLatin1Char('<');
const QChar greaterThan = QLatin1Char('>');
// Get first name/mail pair
int mailPos = l.indexOf(lessThan);
if (mailPos == -1)
return false;
name = l.mid(0, mailPos).trimmed();
mailPos++;
const int mailEndPos = l.indexOf(greaterThan, mailPos);
if (mailEndPos == -1)
return false;
email = l.mid(mailPos, mailEndPos - mailPos);
// get optional 2nd name/mail pair
const int aliasNameStart = mailEndPos + 1;
if (aliasNameStart >= l.size())
return true;
int aliasMailPos = l.indexOf(lessThan, aliasNameStart);
if (aliasMailPos == -1) {
aliasName =l.mid(aliasNameStart, l.size() - aliasNameStart).trimmed();
return true;
}
aliasName = l.mid(aliasNameStart, aliasMailPos - aliasNameStart).trimmed();
aliasMailPos++;
const int aliasMailEndPos = l.indexOf(greaterThan, aliasMailPos);
if (aliasMailEndPos == -1)
return true;
aliasEmail = l.mid(aliasMailPos, aliasMailEndPos - aliasMailPos);
return true;
}
// Format "Hans Mustermann <HM@acme.de>"
static inline QString formatNick(const QString &name, const QString &email)
{
QString rc = name;
if (!email.isEmpty()) {
rc += QLatin1String(" <");
rc += email;
rc += QLatin1Char('>');
}
return rc;
}
QString NickEntry::nickName() const
{
return aliasName.isEmpty() ? formatNick(name, email) : formatNick(aliasName, aliasEmail);
}
// Sort by name
bool operator<(const NickEntry &n1, const NickEntry &n2)
{
return n1.name < n2.name;
}
QDebug operator<<(QDebug d, const NickEntry &e)
{
d.nospace() << "Name='" << e.name << "' Mail='" << e.email
<< " Alias='" << e.aliasName << " AliasEmail='" << e.aliasEmail << "'\n";
return d;
}
// Globally cached list
static QList<NickEntry> &nickList()
{
static QList<NickEntry> rc;
return rc;
}
// Create a model populated with the names
static QStandardItemModel *createModel(QObject *parent)
{
QStandardItemModel *rc = new QStandardItemModel(parent);
QStringList headers;
headers << NickNameDialog::tr("Name")
<< NickNameDialog::tr("E-mail")
<< NickNameDialog::tr("Alias")
<< NickNameDialog::tr("Alias e-mail");
rc->setHorizontalHeaderLabels(headers);
foreach(const NickEntry &ne, nickList()) {
QList<QStandardItem *> row;
row.push_back(new QStandardItem(ne.name));
row.push_back(new QStandardItem(ne.email));
row.push_back(new QStandardItem(ne.aliasName));
row.push_back(new QStandardItem(ne.aliasEmail));
rc->appendRow(row);
}
return rc;
}
NickNameDialog::NickNameDialog(QWidget *parent) :
QDialog(parent),
m_ui(new Ui::NickNameDialog),
m_filterModel(new QSortFilterProxyModel(this))
{
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
m_ui->setupUi(this);
okButton()->setEnabled(false);
// Populate model and grow tree to accommodate it
m_filterModel->setSourceModel(createModel(this));
m_filterModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
m_ui->filterTreeView->setModel(m_filterModel);
const int columnCount = m_filterModel->columnCount();
int treeWidth = 0;
for (int c = 0; c < columnCount; c++) {
m_ui->filterTreeView->resizeColumnToContents(c);
treeWidth += m_ui->filterTreeView->columnWidth(c);
}
m_ui->filterTreeView->setMinimumWidth(treeWidth + 20);
connect(m_ui->filterTreeView, SIGNAL(doubleClicked(QModelIndex)), this,
SLOT(slotDoubleClicked(QModelIndex)));
connect(m_ui->filterTreeView->selectionModel(), SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),
this, SLOT(slotCurrentItemChanged(QModelIndex)));
connect(m_ui->filterLineEdit, SIGNAL(textChanged(QString)),
m_filterModel, SLOT(setFilterFixedString(QString)));
}
NickNameDialog::~NickNameDialog()
{
delete m_ui;
}
QPushButton *NickNameDialog::okButton() const
{
return m_ui->buttonBox->button(QDialogButtonBox::Ok);
}
void NickNameDialog::slotCurrentItemChanged(const QModelIndex &index)
{
okButton()->setEnabled(index.isValid());
}
void NickNameDialog::slotDoubleClicked(const QModelIndex &)
{
if (okButton()->isEnabled())
okButton()->animateClick();
}
QString NickNameDialog::nickName() const
{
const QModelIndex index = m_ui->filterTreeView->selectionModel()->currentIndex();
if (index.isValid()) {
const QModelIndex sourceIndex = m_filterModel->mapToSource(index);
return nickList().at(sourceIndex.row()).nickName();
}
return QString();
}
void NickNameDialog::clearNickNames()
{
nickList().clear();
}
bool NickNameDialog::readNickNamesFromMailCapFile(const QString &fileName, QString *errorMessage)
{
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly|QIODevice::Text)) {
*errorMessage = tr("Cannot open '%1': %2").arg(fileName, file.errorString());
return false;
}
// Split into lines and read
QList<NickEntry> &nl = nickList();
nl.clear();
NickEntry entry;
const QStringList lines = QString::fromUtf8(file.readAll()).trimmed().split(QLatin1Char('\n'));
const int count = lines.size();
for (int i = 0; i < count; i++) {
if (entry.parse(lines.at(i))) {
nl.push_back(entry);
} else {
qWarning("%s: Invalid mail cap entry at line %d: '%s'\n", qPrintable(fileName), i + 1, qPrintable(lines.at(i)));
}
}
qStableSort(nl);
return true;
}
QStringList NickNameDialog::nickNameList()
{
QStringList rc;
foreach(const NickEntry &ne, nickList())
rc.push_back(ne.nickName());
return rc;
}
}
}

View File

@@ -0,0 +1,81 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Qt Software Information (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 qt-sales@nokia.com.
**
**************************************************************************/
#ifndef NICKNAMEDIALOG_H
#define NICKNAMEDIALOG_H
#include <QtGui/QDialog>
QT_BEGIN_NAMESPACE
namespace Ui {
class NickNameDialog;
}
class QSortFilterProxyModel;
class QModelIndex;
class QPushButton;
QT_END_NAMESPACE
namespace VCSBase {
namespace Internal {
/* Nick name dialog: Manages a list of users read from an extended
* mail cap file, consisting of 4 columns:
* "Name Mail [AliasName [AliasMail]]".
* The names can be used for insertion into "RevBy:" fields; aliases will
* be preferred. */
class NickNameDialog : public QDialog {
Q_OBJECT
public:
explicit NickNameDialog(QWidget *parent = 0);
virtual ~NickNameDialog();
QString nickName() const;
// Fill/clear the global nick name cache
static bool readNickNamesFromMailCapFile(const QString &file, QString *errorMessage);
static void clearNickNames();
// Return a list for a completer on the field line edits
static QStringList nickNameList();
private slots:
void slotCurrentItemChanged(const QModelIndex &);
void slotDoubleClicked(const QModelIndex &);
private:
QPushButton *okButton() const;
Ui::NickNameDialog *m_ui;
QSortFilterProxyModel *m_filterModel;
};
} // namespace Internal
} // namespace VCSBase
#endif // NICKNAMEDIALOG_H

View File

@@ -0,0 +1,117 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>NickNameDialog</class>
<widget class="QDialog" name="NickNameDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>618</width>
<height>414</height>
</rect>
</property>
<property name="windowTitle">
<string>Nick Names</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<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="filterClearToolButton">
<property name="text">
<string>Clear</string>
</property>
</widget>
</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>
</item>
<item>
<widget class="QTreeView" name="filterTreeView"/>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>NickNameDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>252</x>
<y>405</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>NickNameDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>293</x>
<y>405</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>filterClearToolButton</sender>
<signal>clicked()</signal>
<receiver>filterLineEdit</receiver>
<slot>clear()</slot>
<hints>
<hint type="sourcelabel">
<x>263</x>
<y>14</y>
</hint>
<hint type="destinationlabel">
<x>198</x>
<y>19</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@@ -14,7 +14,11 @@ HEADERS += vcsbase_global.h \
basevcseditorfactory.h \
submiteditorfile.h \
basevcssubmiteditorfactory.h \
submitfilemodel.h
submitfilemodel.h \
vcsbasesettings.h \
vcsbasesettingspage.h \
nicknamedialog.h
SOURCES += vcsbaseplugin.cpp \
baseannotationhighlighter.cpp \
diffhighlighter.cpp \
@@ -24,5 +28,12 @@ SOURCES += vcsbaseplugin.cpp \
basevcseditorfactory.cpp \
submiteditorfile.cpp \
basevcssubmiteditorfactory.cpp \
submitfilemodel.cpp
RESOURCES = vcsbase.qrc
submitfilemodel.cpp \
vcsbasesettings.cpp \
vcsbasesettingspage.cpp \
nicknamedialog.cpp
RESOURCES += vcsbase.qrc
FORMS += vcsbasesettingspage.ui \
nicknamedialog.ui

View File

@@ -36,6 +36,7 @@ namespace VCSBase {
namespace Constants {
const char * const VCS_SETTINGS_CATEGORY = QT_TRANSLATE_NOOP("VCSBase", "Version Control System");
const char * const VCS_COMMON_SETTINGS_ID = QT_TRANSLATE_NOOP("VCSBase", "Common");
namespace Internal {
enum { debug = 0 };

View File

@@ -29,6 +29,7 @@
#include "vcsbaseplugin.h"
#include "diffhighlighter.h"
#include "vcsbasesettingspage.h"
#include <coreplugin/icore.h>
#include <coreplugin/coreconstants.h>
@@ -42,7 +43,8 @@ namespace Internal {
VCSBasePlugin *VCSBasePlugin::m_instance = 0;
VCSBasePlugin::VCSBasePlugin()
VCSBasePlugin::VCSBasePlugin() :
m_settingsPage(0)
{
m_instance = this;
}
@@ -61,6 +63,8 @@ bool VCSBasePlugin::initialize(const QStringList &arguments, QString *errorMessa
if (!core->mimeDatabase()->addMimeTypes(QLatin1String(":/vcsbase/VCSBase.mimetypes.xml"), errorMessage))
return false;
m_settingsPage = new VCSBaseSettingsPage;
addAutoReleasedObject(m_settingsPage);
return true;
}
@@ -73,6 +77,11 @@ VCSBasePlugin *VCSBasePlugin::instance()
return m_instance;
}
VCSBaseSettings VCSBasePlugin::settings() const
{
return m_settingsPage->settings();
}
} // namespace Internal
} // namespace VCSBase

View File

@@ -37,6 +37,9 @@
namespace VCSBase {
namespace Internal {
struct VCSBaseSettings;
class VCSBaseSettingsPage;
class VCSBasePlugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
@@ -51,8 +54,11 @@ public:
static VCSBasePlugin *instance();
VCSBaseSettings settings() const;
private:
static VCSBasePlugin *m_instance;
VCSBaseSettingsPage *m_settingsPage;
};
} // namespace Internal

View File

@@ -0,0 +1,78 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Qt Software Information (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 qt-sales@nokia.com.
**
**************************************************************************/
#include "vcsbasesettings.h"
#include <QtCore/QSettings>
#include <QtCore/QDebug>
static const char *settingsGroupC = "VCS";
static const char *nickNameMailMapKeyC = "NickNameMailMap";
static const char *nickNameFieldListFileKeyC = "NickNameFieldListFile";
static const char *promptForSubmitKeyC = "PromptForSubmit";
static const char *submitMessageCheckScriptKeyC = "SubmitMessageCheckScript";
namespace VCSBase {
namespace Internal {
VCSBaseSettings::VCSBaseSettings() :
promptForSubmit(true)
{
}
void VCSBaseSettings::toSettings(QSettings *s) const
{
s->beginGroup(QLatin1String(settingsGroupC));
s->setValue(QLatin1String(nickNameMailMapKeyC), nickNameMailMap);
s->setValue(QLatin1String(nickNameFieldListFileKeyC), nickNameFieldListFile);
s->setValue(QLatin1String(submitMessageCheckScriptKeyC), submitMessageCheckScript);
s->setValue(QLatin1String(promptForSubmitKeyC), promptForSubmit);
s->endGroup();
}
void VCSBaseSettings::fromSettings(QSettings *s)
{
s->beginGroup(QLatin1String(settingsGroupC));
nickNameMailMap = s->value(QLatin1String(nickNameMailMapKeyC), QString()).toString();
nickNameFieldListFile = s->value(QLatin1String(nickNameFieldListFileKeyC), QString()).toString();
submitMessageCheckScript = s->value(QLatin1String(submitMessageCheckScriptKeyC), QString()).toString();
promptForSubmit = s->value(QLatin1String(promptForSubmitKeyC), QVariant(true)).toBool();
s->endGroup();
}
bool VCSBaseSettings::equals(const VCSBaseSettings &rhs) const
{
return promptForSubmit == rhs.promptForSubmit
&& nickNameMailMap == rhs.nickNameMailMap
&& nickNameFieldListFile == rhs.nickNameFieldListFile
&& submitMessageCheckScript == rhs.submitMessageCheckScript;
}
}
}

View File

@@ -0,0 +1,66 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Qt Software Information (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 qt-sales@nokia.com.
**
**************************************************************************/
#ifndef VCSBASESETTINGS_H
#define VCSBASESETTINGS_H
#include <QtCore/QString>
#include <QtGui/QWidget>
QT_BEGIN_NAMESPACE
class QSettings;
QT_END_NAMESPACE
namespace VCSBase {
namespace Internal {
// Common VCS settings, message check script and user nick names.
struct VCSBaseSettings {
VCSBaseSettings();
bool promptForSubmit;
QString nickNameMailMap;
QString nickNameFieldListFile;
QString submitMessageCheckScript;
void toSettings(QSettings *) const;
void fromSettings(QSettings *);
bool equals(const VCSBaseSettings &rhs) const;
};
inline bool operator==(const VCSBaseSettings &s1, const VCSBaseSettings &s2) { return s1.equals(s2); }
inline bool operator!=(const VCSBaseSettings &s1, const VCSBaseSettings &s2) { return !s1.equals(s2); }
} // namespace Internal
} // namespace VCSBase
#endif // VCSBASESETTINGS_H

View File

@@ -0,0 +1,145 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Qt Software Information (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 qt-sales@nokia.com.
**
**************************************************************************/
#include "vcsbasesettingspage.h"
#include "vcsbaseconstants.h"
#include "nicknamedialog.h"
#include "ui_vcsbasesettingspage.h"
#include <coreplugin/icore.h>
#include <extensionsystem/pluginmanager.h>
#include <QtCore/QDebug>
#include <QtCore/QCoreApplication>
#include <QtGui/QMessageBox>
namespace VCSBase {
namespace Internal {
// ------------------ VCSBaseSettingsWidget
VCSBaseSettingsWidget::VCSBaseSettingsWidget(QWidget *parent) :
QWidget(parent),
m_ui(new Ui::VCSBaseSettingsPage)
{
m_ui->setupUi(this);
m_ui->submitMessageCheckScriptChooser->setExpectedKind(Core::Utils::PathChooser::Command);
m_ui->nickNameFieldsFileChooser->setExpectedKind(Core::Utils::PathChooser::File);
m_ui->nickNameMailMapChooser->setExpectedKind(Core::Utils::PathChooser::File);
}
VCSBaseSettingsWidget::~VCSBaseSettingsWidget()
{
delete m_ui;
}
VCSBaseSettings VCSBaseSettingsWidget::settings() const
{
VCSBaseSettings rc;
rc.nickNameMailMap = m_ui->nickNameMailMapChooser->path();
rc.nickNameFieldListFile = m_ui->nickNameFieldsFileChooser->path();
rc.submitMessageCheckScript = m_ui->submitMessageCheckScriptChooser->path();
rc.promptForSubmit = m_ui->promptForSubmitCheckBox->isChecked();
return rc;
}
void VCSBaseSettingsWidget::setSettings(const VCSBaseSettings &s)
{
m_ui->nickNameMailMapChooser->setPath(s.nickNameMailMap);
m_ui->nickNameFieldsFileChooser->setPath(s.nickNameFieldListFile);
m_ui->submitMessageCheckScriptChooser->setPath(s.submitMessageCheckScript);
m_ui->promptForSubmitCheckBox->setChecked(s.promptForSubmit);
}
// --------------- VCSBaseSettingsPage
VCSBaseSettingsPage::VCSBaseSettingsPage(QObject *parent) :
Core::IOptionsPage(parent)
{
m_settings.fromSettings(Core::ICore::instance()->settings());
updateNickNames();
}
void VCSBaseSettingsPage::updateNickNames()
{
if (m_settings.nickNameMailMap.isEmpty()) {
NickNameDialog::clearNickNames();
} else {
QString errorMessage;
if (!NickNameDialog::readNickNamesFromMailCapFile(m_settings.nickNameMailMap, &errorMessage))
qWarning("%s", qPrintable(errorMessage));
}
}
VCSBaseSettingsPage::~VCSBaseSettingsPage()
{
}
QString VCSBaseSettingsPage::id() const
{
return QLatin1String(Constants::VCS_COMMON_SETTINGS_ID);
}
QString VCSBaseSettingsPage::trName() const
{
return QCoreApplication::translate("VCSBase", Constants::VCS_COMMON_SETTINGS_ID);
}
QString VCSBaseSettingsPage::category() const
{
return QLatin1String(Constants::VCS_SETTINGS_CATEGORY);
}
QString VCSBaseSettingsPage::trCategory() const
{
return QCoreApplication::translate("VCSBase", Constants::VCS_SETTINGS_CATEGORY);
}
QWidget *VCSBaseSettingsPage::createPage(QWidget *parent)
{
m_widget = new VCSBaseSettingsWidget(parent);
m_widget->setSettings(m_settings);
return m_widget;
}
void VCSBaseSettingsPage::apply()
{
if (m_widget) {
const VCSBaseSettings newSettings = m_widget->settings();
if (newSettings != m_settings) {
m_settings = newSettings;
m_settings.toSettings(Core::ICore::instance()->settings());
updateNickNames();
}
}
}
}
}

View File

@@ -0,0 +1,86 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Qt Software Information (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 qt-sales@nokia.com.
**
**************************************************************************/
#ifndef VCSBASESETTINGSPAGE_H
#define VCSBASESETTINGSPAGE_H
#include "vcsbasesettings.h"
#include <coreplugin/dialogs/ioptionspage.h>
#include <QtCore/QPointer>
#include <QtGui/QWidget>
QT_BEGIN_NAMESPACE
namespace Ui {
class VCSBaseSettingsPage;
}
QT_END_NAMESPACE
namespace VCSBase {
namespace Internal {
class VCSBaseSettingsWidget : public QWidget {
Q_OBJECT
public:
explicit VCSBaseSettingsWidget(QWidget *parent = 0);
virtual ~VCSBaseSettingsWidget();
VCSBaseSettings settings() const;
void setSettings(const VCSBaseSettings &s);
private:
Ui::VCSBaseSettingsPage *m_ui;
};
class VCSBaseSettingsPage : public Core::IOptionsPage
{
public:
explicit VCSBaseSettingsPage(QObject *parent = 0);
virtual ~VCSBaseSettingsPage();
virtual QString id() const;
virtual QString trName() const;
virtual QString category() const;
virtual QString trCategory() const;
virtual QWidget *createPage(QWidget *parent);
virtual void apply();
virtual void finish() { }
VCSBaseSettings settings() const { return m_settings; }
private:
void updateNickNames();
QPointer<VCSBaseSettingsWidget> m_widget;
VCSBaseSettings m_settings;
};
} // namespace Internal
} // namespace VCSBase
#endif // VCSBASESETTINGSPAGE_H

View File

@@ -0,0 +1,116 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>VCSBaseSettingsPage</class>
<widget class="QWidget" name="VCSBaseSettingsPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>536</width>
<height>407</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::ExpandingFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="promptForSubmitLabel">
<property name="text">
<string>Prompt for submit:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="promptForSubmitCheckBox"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="submitMessageCheckScriptLabel">
<property name="toolTip">
<string>An executable which is called with the submit message in a temporary file as first argument. It should return with an exit != 0 and a message on standard error to indicate failure.</string>
</property>
<property name="text">
<string>Submit message check script:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="Core::Utils::PathChooser" name="submitMessageCheckScriptChooser" native="true"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="nickNameMailMapLabel">
<property name="toolTip">
<string>A file listing user names in 2-column mailmap format:
name &lt;email&gt; alias &lt;email&gt;</string>
</property>
<property name="text">
<string>User name file:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="Core::Utils::PathChooser" name="nickNameMailMapChooser" native="true"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="nickNameFieldsFileLabel">
<property name="toolTip">
<string>A simple file containing lines with field names like &quot;Reviewed-By:&quot; which will be added below the submit editor.</string>
</property>
<property name="text">
<string>User fields configuration file:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="Core::Utils::PathChooser" name="nickNameFieldsFileChooser" native="true"/>
</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>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>307</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>Core::Utils::PathChooser</class>
<extends>QWidget</extends>
<header location="global">utils/pathchooser.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@@ -28,10 +28,14 @@
**************************************************************************/
#include "vcsbasesubmiteditor.h"
#include "vcsbasesettings.h"
#include "vcsbaseplugin.h"
#include "nicknamedialog.h"
#include "submiteditorfile.h"
#include <aggregation/aggregate.h>
#include <coreplugin/ifile.h>
#include <coreplugin/icore.h>
#include <coreplugin/uniqueidmanager.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <utils/submiteditorwidget.h>
@@ -42,18 +46,31 @@
#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtCore/QTemporaryFile>
#include <QtCore/QProcess>
#include <QtCore/QFile>
#include <QtCore/QFileInfo>
#include <QtCore/QPointer>
#include <QtCore/QTextStream>
#include <QtGui/QStyle>
#include <QtGui/QToolBar>
#include <QtGui/QAction>
#include <QtGui/QApplication>
#include <QtGui/QMessageBox>
#include <QtGui/QMainWindow>
#include <QtGui/QCompleter>
#include <QtGui/QLineEdit>
enum { debug = 0 };
enum { wantToolBar = 0 };
namespace VCSBase {
static inline QString submitMessageCheckScript()
{
return Internal::VCSBasePlugin::instance()->settings().submitMessageCheckScript;
}
struct VCSBaseSubmitEditorPrivate
{
VCSBaseSubmitEditorPrivate(const VCSBaseSubmitEditorParameters *parameters,
@@ -69,6 +86,8 @@ struct VCSBaseSubmitEditorPrivate
QPointer<QAction> m_diffAction;
QPointer<QAction> m_submitAction;
Internal::NickNameDialog *m_nickNameDialog;
};
VCSBaseSubmitEditorPrivate::VCSBaseSubmitEditorPrivate(const VCSBaseSubmitEditorParameters *parameters,
@@ -77,7 +96,8 @@ VCSBaseSubmitEditorPrivate::VCSBaseSubmitEditorPrivate(const VCSBaseSubmitEditor
m_widget(editorWidget),
m_toolWidget(0),
m_parameters(parameters),
m_file(new VCSBase::Internal::SubmitEditorFile(QLatin1String(m_parameters->mimeType), q))
m_file(new VCSBase::Internal::SubmitEditorFile(QLatin1String(m_parameters->mimeType), q)),
m_nickNameDialog(0)
{
m_contexts << Core::UniqueIDManager::instance()->uniqueIdentifier(m_parameters->context);
}
@@ -93,6 +113,29 @@ VCSBaseSubmitEditor::VCSBaseSubmitEditor(const VCSBaseSubmitEditorParameters *pa
connect(m_d->m_widget, SIGNAL(diffSelected(QStringList)), this, SLOT(slotDiffSelectedVCSFiles(QStringList)));
connect(m_d->m_widget->descriptionEdit(), SIGNAL(textChanged()), this, SLOT(slotDescriptionChanged()));
const Internal::VCSBaseSettings settings = Internal::VCSBasePlugin::instance()->settings();
// Add additional context menu settings
if (!settings.submitMessageCheckScript.isEmpty() || !settings.nickNameFieldListFile.isEmpty()) {
QAction *sep = new QAction(this);
sep->setSeparator(true);
m_d->m_widget->addDescriptionEditContextMenuAction(sep);
// Run check action
if (!settings.submitMessageCheckScript.isEmpty()) {
QAction *checkAction = new QAction(tr("Check message"), this);
connect(checkAction, SIGNAL(triggered()), this, SLOT(slotCheckSubmitMessage()));
m_d->m_widget->addDescriptionEditContextMenuAction(checkAction);
}
// Insert nick
if (!settings.nickNameFieldListFile.isEmpty()) {
QAction *insertAction = new QAction(tr("Insert name..."), this);
connect(insertAction, SIGNAL(triggered()), this, SLOT(slotInsertNickName()));
m_d->m_widget->addDescriptionEditContextMenuAction(insertAction);
}
}
// Do we have user fields?
if (!settings.nickNameFieldListFile.isEmpty())
createUserFields(settings.nickNameFieldListFile);
connect(m_d->m_widget, SIGNAL(fieldDialogRequested(int)), this, SLOT(slotSetFieldNickName(int)));
Aggregation::Aggregate *aggregate = new Aggregation::Aggregate;
aggregate->add(new Find::BaseTextFind(m_d->m_widget->descriptionEdit()));
aggregate->add(this);
@@ -105,6 +148,26 @@ VCSBaseSubmitEditor::~VCSBaseSubmitEditor()
delete m_d;
}
void VCSBaseSubmitEditor::createUserFields(const QString &fieldConfigFile)
{
QFile fieldFile(fieldConfigFile);
if (!fieldFile.open(QIODevice::ReadOnly|QIODevice::Text)) {
qWarning("%s: Unable to open %s: %s", Q_FUNC_INFO, qPrintable(fieldConfigFile), qPrintable(fieldFile.errorString()));
return;
}
// Parse into fields
const QStringList fields = QString::fromUtf8(fieldFile.readAll()).trimmed().split(QLatin1Char('\n'));
if (fields.empty())
return;
// Create a completer on user names
QCompleter *completer = new QCompleter(Internal::NickNameDialog::nickNameList(), this);
foreach(const QString &field, fields) {
const QString trimmedField = field.trimmed();
if (!trimmedField.isEmpty())
m_d->m_widget->addField(trimmedField, true)->setCompleter(completer);
}
}
void VCSBaseSubmitEditor::registerActions(QAction *editorUndoAction, QAction *editorRedoAction,
QAction *submitAction, QAction *diffAction)\
{
@@ -139,7 +202,6 @@ void VCSBaseSubmitEditor::setFileListSelectionMode(QAbstractItemView::SelectionM
m_d->m_widget->setFileListSelectionMode(sm);
}
void VCSBaseSubmitEditor::slotDescriptionChanged()
{
}
@@ -304,6 +366,118 @@ bool VCSBaseSubmitEditor::setFileContents(const QString &contents)
return true;
}
enum { checkDialogMinimumWidth = 500 };
VCSBaseSubmitEditor::PromptSubmitResult
VCSBaseSubmitEditor::promptSubmit(const QString &title, const QString &question, const QString &checkFailureQuestion) const
{
QString errorMessage;
QMessageBox::StandardButton answer = QMessageBox::Yes;
QWidget *parent = Core::ICore::instance()->mainWindow();
// Pop up a message depending on whether the check succeeded and the
// user wants to be prompted
if (checkSubmitMessage(&errorMessage)) {
// Check ok, do prompt?
if (Internal::VCSBasePlugin::instance()->settings().promptForSubmit) {
answer = QMessageBox::question(parent, title, question,
QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel,
QMessageBox::Yes);
}
} else {
// Check failed.
QMessageBox msgBox(QMessageBox::Question, title, checkFailureQuestion,
QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel, parent);
msgBox.setDefaultButton(QMessageBox::Cancel);
msgBox.setInformativeText(errorMessage);
msgBox.setMinimumWidth(checkDialogMinimumWidth);
answer = static_cast<QMessageBox::StandardButton>(msgBox.exec());
}
switch (answer) {
case QMessageBox::Cancel:
return SubmitCanceled;
case QMessageBox::No:
return SubmitDiscarded;
default:
break;
}
return SubmitConfirmed;
}
QString VCSBaseSubmitEditor::promptForNickName()
{
if (!m_d->m_nickNameDialog)
m_d->m_nickNameDialog = new Internal::NickNameDialog(m_d->m_widget);
if (m_d->m_nickNameDialog->exec() == QDialog::Accepted)
return m_d->m_nickNameDialog->nickName();
return QString();
}
void VCSBaseSubmitEditor::slotInsertNickName()
{
const QString nick = promptForNickName();
if (!nick.isEmpty())
m_d->m_widget->descriptionEdit()->textCursor().insertText(nick);
}
void VCSBaseSubmitEditor::slotSetFieldNickName(int i)
{
const QString nick = promptForNickName();
if (!nick.isEmpty())
m_d->m_widget->fieldLineEdit(i)->setText(nick);
}
void VCSBaseSubmitEditor::slotCheckSubmitMessage()
{
QString errorMessage;
if (!checkSubmitMessage(&errorMessage)) {
QMessageBox msgBox(QMessageBox::Warning, tr("Submit Message Check failed"),
errorMessage, QMessageBox::Ok, m_d->m_widget);
msgBox.setMinimumWidth(checkDialogMinimumWidth);
msgBox.exec();
}
}
bool VCSBaseSubmitEditor::checkSubmitMessage(QString *errorMessage) const
{
const QString checkScript = submitMessageCheckScript();
if (checkScript.isEmpty())
return true;
// Write out message
QString tempFilePattern = QDir::tempPath();
if (!tempFilePattern.endsWith(QDir::separator()))
tempFilePattern += QDir::separator();
tempFilePattern += QLatin1String("msgXXXXXX.txt");
QTemporaryFile messageFile(tempFilePattern);
messageFile.setAutoRemove(true);
if (!messageFile.open()) {
*errorMessage = tr("Unable to open '%1': %2").arg(messageFile.fileName(), messageFile.errorString());
return false;
}
const QString messageFileName = messageFile.fileName();
messageFile.write(fileContents().toUtf8());
messageFile.close();
// Run check process
QProcess checkProcess;
checkProcess.start(checkScript, QStringList(messageFileName));
if (!checkProcess.waitForStarted()) {
*errorMessage = tr("The check script '%1' could not be started: %2").arg(checkScript, checkProcess.errorString());
return false;
}
if (!checkProcess.waitForFinished()) {
*errorMessage = tr("The check script '%1' could not be run: %2").arg(checkScript, checkProcess.errorString());
return false;
}
const int exitCode = checkProcess.exitCode();
if (exitCode != 0) {
*errorMessage = QString::fromLocal8Bit(checkProcess.readAllStandardError());
if (errorMessage->isEmpty())
*errorMessage = tr("The check script returned exit code %1.").arg(exitCode);
return false;
}
return true;
}
QIcon VCSBaseSubmitEditor::diffIcon()
{
return QIcon(QLatin1String(":/vcsbase/images/diff.png"));

View File

@@ -50,7 +50,9 @@ namespace Core {
}
namespace VCSBase {
namespace Internal {
struct VCSBaseSettings;
}
struct VCSBaseSubmitEditorPrivate;
/* Utility struct to parametrize a VCSBaseSubmitEditor. */
@@ -104,6 +106,12 @@ public:
virtual ~VCSBaseSubmitEditor();
// A utility routine to be called when clsing a submit editor.
// Runs checks on the message and prompts according to configuration.
enum PromptSubmitResult { SubmitConfirmed, SubmitCanceled, SubmitDiscarded };
PromptSubmitResult promptSubmit(const QString &title, const QString &question,
const QString &checkFailureQuestion) const;
int fileNameColumn() const;
void setFileNameColumn(int c);
@@ -147,6 +155,9 @@ private slots:
void slotDiffSelectedVCSFiles(const QStringList &rawList);
bool save(const QString &fileName);
void slotDescriptionChanged();
void slotCheckSubmitMessage();
void slotInsertNickName();
void slotSetFieldNickName(int);
protected:
/* These hooks allow for modifying the contents that goes to
@@ -156,6 +167,10 @@ protected:
virtual bool setFileContents(const QString &contents);
private:
void createUserFields(const QString &fieldConfigFile);
bool checkSubmitMessage(QString *errorMessage) const;
QString promptForNickName();
VCSBaseSubmitEditorPrivate *m_d;
};