summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/libs/utils/submiteditorwidget.cpp107
-rw-r--r--src/libs/utils/submiteditorwidget.h12
-rw-r--r--src/plugins/git/gitplugin.cpp10
-rw-r--r--src/plugins/perforce/perforceplugin.cpp19
-rw-r--r--src/plugins/subversion/subversionplugin.cpp12
-rw-r--r--src/plugins/vcsbase/nicknamedialog.cpp251
-rw-r--r--src/plugins/vcsbase/nicknamedialog.h81
-rw-r--r--src/plugins/vcsbase/nicknamedialog.ui117
-rw-r--r--src/plugins/vcsbase/vcsbase.pro17
-rw-r--r--src/plugins/vcsbase/vcsbaseconstants.h1
-rw-r--r--src/plugins/vcsbase/vcsbaseplugin.cpp11
-rw-r--r--src/plugins/vcsbase/vcsbaseplugin.h6
-rw-r--r--src/plugins/vcsbase/vcsbasesettings.cpp78
-rw-r--r--src/plugins/vcsbase/vcsbasesettings.h66
-rw-r--r--src/plugins/vcsbase/vcsbasesettingspage.cpp145
-rw-r--r--src/plugins/vcsbase/vcsbasesettingspage.h86
-rw-r--r--src/plugins/vcsbase/vcsbasesettingspage.ui116
-rw-r--r--src/plugins/vcsbase/vcsbasesubmiteditor.cpp178
-rw-r--r--src/plugins/vcsbase/vcsbasesubmiteditor.h17
19 files changed, 1303 insertions, 27 deletions
diff --git a/src/libs/utils/submiteditorwidget.cpp b/src/libs/utils/submiteditorwidget.cpp
index a047682b81..b9c8259020 100644
--- a/src/libs/utils/submiteditorwidget.cpp
+++ b/src/libs/utils/submiteditorwidget.cpp
@@ -33,8 +33,15 @@
#include <QtCore/QDebug>
#include <QtCore/QPointer>
#include <QtCore/QTimer>
+#include <QtCore/QSignalMapper>
#include <QtGui/QPushButton>
+#include <QtGui/QMenu>
+#include <QtGui/QLineEdit>
+#include <QtGui/QFormLayout>
+#include <QtGui/QHBoxLayout>
+#include <QtGui/QToolButton>
+#include <QtGui/QSpacerItem>
enum { debug = 0 };
@@ -104,8 +111,12 @@ QList<int> selectedRows(const QAbstractItemView *view)
}
// ----------- SubmitEditorWidgetPrivate
+
struct SubmitEditorWidgetPrivate
{
+ // A pair of position/action to extend context menus
+ typedef QPair<int, QPointer<QAction> > AdditionalContextMenuAction;
+
SubmitEditorWidgetPrivate();
Ui::SubmitEditorWidget m_ui;
@@ -113,13 +124,22 @@ struct SubmitEditorWidgetPrivate
bool m_filesChecked;
int m_fileNameColumn;
int m_activatedRow;
+
+ QList<AdditionalContextMenuAction> descriptionEditContextMenuActions;
+ QFormLayout *m_fieldLayout;
+ // Field entries (label, line edits)
+ typedef QPair<QString, QLineEdit*> FieldEntry;
+ QList<FieldEntry> m_fieldEntries;
+ QSignalMapper *m_fieldSignalMapper;
};
SubmitEditorWidgetPrivate::SubmitEditorWidgetPrivate() :
m_filesSelected(false),
m_filesChecked(false),
m_fileNameColumn(1),
- m_activatedRow(-1)
+ m_activatedRow(-1),
+ m_fieldLayout(0),
+ m_fieldSignalMapper(0)
{
}
@@ -128,6 +148,10 @@ SubmitEditorWidget::SubmitEditorWidget(QWidget *parent) :
m_d(new SubmitEditorWidgetPrivate)
{
m_d->m_ui.setupUi(this);
+ m_d->m_ui.description->setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(m_d->m_ui.description, SIGNAL(customContextMenuRequested(QPoint)),
+ this, SLOT(editorCustomContextMenuRequested(QPoint)));
+
// File List
m_d->m_ui.fileView->setSelectionMode(QAbstractItemView::ExtendedSelection);
m_d->m_ui.fileView->setRootIsDecorated(false);
@@ -212,7 +236,18 @@ QString SubmitEditorWidget::trimmedDescriptionText() const
QString SubmitEditorWidget::descriptionText() const
{
- return m_d->m_ui.description->toPlainText();
+ QString rc = m_d->m_ui.description->toPlainText();
+ // append field entries
+ foreach(const SubmitEditorWidgetPrivate::FieldEntry &fe, m_d->m_fieldEntries) {
+ const QString fieldText = fe.second->text().trimmed();
+ if (!fieldText.isEmpty()) {
+ rc += fe.first;
+ rc += QLatin1Char(' ');
+ rc += fieldText;
+ rc += QLatin1Char('\n');
+ }
+ }
+ return rc;
}
void SubmitEditorWidget::setDescriptionText(const QString &text)
@@ -381,6 +416,74 @@ void SubmitEditorWidget::insertTopWidget(QWidget *w)
m_d->m_ui.vboxLayout->insertWidget(0, w);
}
+void SubmitEditorWidget::addDescriptionEditContextMenuAction(QAction *a)
+{
+ m_d->descriptionEditContextMenuActions.push_back(SubmitEditorWidgetPrivate::AdditionalContextMenuAction(-1, a));
+}
+
+void SubmitEditorWidget::insertDescriptionEditContextMenuAction(int pos, QAction *a)
+{
+ m_d->descriptionEditContextMenuActions.push_back(SubmitEditorWidgetPrivate::AdditionalContextMenuAction(pos, a));
+}
+
+void SubmitEditorWidget::editorCustomContextMenuRequested(const QPoint &pos)
+{
+ QMenu *menu = m_d->m_ui.description->createStandardContextMenu();
+ // Extend
+ foreach (const SubmitEditorWidgetPrivate::AdditionalContextMenuAction &a, m_d->descriptionEditContextMenuActions) {
+ if (a.second) {
+ if (a.first >= 0) {
+ menu->insertAction(menu->actions().at(a.first), a.second);
+ } else {
+ menu->addAction(a.second);
+ }
+ }
+ }
+ menu->exec(m_d->m_ui.description->mapToGlobal(pos));
+ delete menu;
+}
+
+QLineEdit *SubmitEditorWidget::addField(const QString &label, bool hasDialogButton)
+{
+ // Insert the form layout below the editor
+ if (!m_d->m_fieldLayout) {
+ QHBoxLayout *outerLayout = new QHBoxLayout;
+ m_d->m_fieldLayout = new QFormLayout;
+ outerLayout->addLayout(m_d->m_fieldLayout);
+ outerLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Ignored));
+ QBoxLayout *descrLayout = qobject_cast<QBoxLayout*>(m_d->m_ui.descriptionBox->layout());
+ Q_ASSERT(descrLayout);
+ descrLayout->addLayout(outerLayout);
+ }
+ if (hasDialogButton && !m_d->m_fieldSignalMapper) {
+ m_d->m_fieldSignalMapper = new QSignalMapper;
+ connect(m_d->m_fieldSignalMapper, SIGNAL(mapped(int)), this, SIGNAL(fieldDialogRequested(int)));
+ }
+ // Add a field row consisting of label and line edit
+ QLineEdit *lineEdit = new QLineEdit;
+ QHBoxLayout *fieldLayout = new QHBoxLayout;
+ fieldLayout->addWidget(lineEdit);
+ if (hasDialogButton) {
+ QToolButton *dialogButton = new QToolButton;
+ dialogButton->setText(tr("..."));
+ connect(dialogButton, SIGNAL(clicked()), m_d->m_fieldSignalMapper, SLOT(map()));
+ m_d->m_fieldSignalMapper->setMapping(dialogButton, m_d->m_fieldEntries.size());
+ fieldLayout->addWidget(dialogButton);
+ }
+ QToolButton *clearButton = new QToolButton;
+ clearButton->setText(tr("Clear"));
+ connect(clearButton, SIGNAL(clicked()), lineEdit, SLOT(clear()));
+ fieldLayout->addWidget(clearButton);
+ m_d->m_fieldLayout->addRow(label, fieldLayout);
+ m_d->m_fieldEntries.push_back(SubmitEditorWidgetPrivate::FieldEntry(label, lineEdit));
+ return lineEdit;
+}
+
+QLineEdit *SubmitEditorWidget::fieldLineEdit(int i) const
+{
+ return m_d->m_fieldEntries.at(i).second;
+}
+
} // namespace Utils
} // namespace Core
diff --git a/src/libs/utils/submiteditorwidget.h b/src/libs/utils/submiteditorwidget.h
index 74dd56d006..e77da4379e 100644
--- a/src/libs/utils/submiteditorwidget.h
+++ b/src/libs/utils/submiteditorwidget.h
@@ -41,6 +41,7 @@ class QListWidgetItem;
class QAction;
class QAbstractItemModel;
class QModelIndex;
+class QLineEdit;
QT_END_NAMESPACE
namespace Core {
@@ -104,10 +105,20 @@ public:
QPlainTextEdit *descriptionEdit() const;
+ void addDescriptionEditContextMenuAction(QAction *a);
+ void insertDescriptionEditContextMenuAction(int pos, QAction *a);
+
+ // Fields are additional fields consisting of a Label and a Line Edit.
+ // A field dialog is wired to a button labeled "..." that pops up a chooser
+ // resulting in text being set
+ QLineEdit *addField(const QString &label, bool hasDialogButton);
+ QLineEdit *fieldLineEdit(int i) const;
+
signals:
void diffSelected(const QStringList &);
void fileSelectionChanged(bool someFileSelected);
void fileCheckStateChanged(bool someFileChecked);
+ void fieldDialogRequested(int);
protected:
virtual void changeEvent(QEvent *e);
@@ -120,6 +131,7 @@ private slots:
void updateActions();
void updateSubmitAction();
void updateDiffAction();
+ void editorCustomContextMenuRequested(const QPoint &);
private:
bool hasSelection() const;
diff --git a/src/plugins/git/gitplugin.cpp b/src/plugins/git/gitplugin.cpp
index 29af542988..cb80541e0f 100644
--- a/src/plugins/git/gitplugin.cpp
+++ b/src/plugins/git/gitplugin.cpp
@@ -673,12 +673,14 @@ bool GitPlugin::editorAboutToClose(Core::IEditor *iEditor)
if (editorFile.absoluteFilePath() != changeFile.absoluteFilePath())
return true;
// Prompt user.
- const QMessageBox::StandardButton answer = QMessageBox::question(m_core->mainWindow(), tr("Closing git editor"), tr("Do you want to commit the change?"),
- QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel, QMessageBox::Yes);
+ const VCSBase::VCSBaseSubmitEditor::PromptSubmitResult answer =
+ editor->promptSubmit(tr("Closing git editor"),
+ tr("Do you want to commit the change?"),
+ tr("The commit message check failed. Do you want to commit the change?"));
switch (answer) {
- case QMessageBox::Cancel:
+ case VCSBase::VCSBaseSubmitEditor::SubmitCanceled:
return false; // Keep editing and change file
- case QMessageBox::No:
+ case VCSBase::VCSBaseSubmitEditor::SubmitDiscarded:
cleanChangeTmpFile();
return true; // Cancel all
default:
diff --git a/src/plugins/perforce/perforceplugin.cpp b/src/plugins/perforce/perforceplugin.cpp
index 65efbc977f..2b068e2982 100644
--- a/src/plugins/perforce/perforceplugin.cpp
+++ b/src/plugins/perforce/perforceplugin.cpp
@@ -961,21 +961,24 @@ bool PerforcePlugin::editorAboutToClose(Core::IEditor *editor)
Core::IFile *fileIFace = editor->file();
if (!fileIFace)
return true;
+ const PerforceSubmitEditor *perforceEditor = qobject_cast<PerforceSubmitEditor *>(editor);
+ if (!perforceEditor)
+ return true;
QFileInfo editorFile(fileIFace->fileName());
QFileInfo changeFile(m_changeTmpFile->fileName());
- if (editorFile.absoluteFilePath() == changeFile.absoluteFilePath()) {
- const QMessageBox::StandardButton answer =
- QMessageBox::question(core->mainWindow(),
- tr("Closing p4 Editor"),
- tr("Do you want to submit this change list?"),
- QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel, QMessageBox::Yes);
- if (answer == QMessageBox::Cancel)
+ if (editorFile.absoluteFilePath() == changeFile.absoluteFilePath()) {
+ const VCSBase::VCSBaseSubmitEditor::PromptSubmitResult answer =
+ perforceEditor->promptSubmit(tr("Closing p4 Editor"),
+ tr("Do you want to submit this change list?"),
+ tr("The commit message check failed. Do you want to submit this change list"));
+
+ if (answer == VCSBase::VCSBaseSubmitEditor::SubmitCanceled)
return false;
core->fileManager()->blockFileChange(fileIFace);
fileIFace->save();
core->fileManager()->unblockFileChange(fileIFace);
- if (answer == QMessageBox::Yes) {
+ if (answer == VCSBase::VCSBaseSubmitEditor::SubmitConfirmed) {
QByteArray change = m_changeTmpFile->readAll();
m_changeTmpFile->close();
if (!checkP4Command()) {
diff --git a/src/plugins/subversion/subversionplugin.cpp b/src/plugins/subversion/subversionplugin.cpp
index 67e56eacf6..189b83e0e3 100644
--- a/src/plugins/subversion/subversionplugin.cpp
+++ b/src/plugins/subversion/subversionplugin.cpp
@@ -464,14 +464,14 @@ bool SubversionPlugin::editorAboutToClose(Core::IEditor *iEditor)
return true; // Oops?!
// Prompt user.
- const QMessageBox::StandardButton answer = QMessageBox::question(
- Core::ICore::instance()->mainWindow(), tr("Closing Subversion Editor"),
- tr("Do you want to commit the change?"),
- QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel, QMessageBox::Yes);
+ const VCSBase::VCSBaseSubmitEditor::PromptSubmitResult answer =
+ editor->promptSubmit(tr("Closing Subversion Editor"),
+ tr("Do you want to commit the change?"),
+ tr("The commit message check failed. Do you want to commit the change?"));
switch (answer) {
- case QMessageBox::Cancel:
+ case VCSBase::VCSBaseSubmitEditor::SubmitCanceled:
return false; // Keep editing and change file
- case QMessageBox::No:
+ case VCSBase::VCSBaseSubmitEditor::SubmitDiscarded:
cleanChangeTmpFile();
return true; // Cancel all
default:
diff --git a/src/plugins/vcsbase/nicknamedialog.cpp b/src/plugins/vcsbase/nicknamedialog.cpp
new file mode 100644
index 0000000000..d8578aa7e4
--- /dev/null
+++ b/src/plugins/vcsbase/nicknamedialog.cpp
@@ -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;
+}
+
+}
+}
diff --git a/src/plugins/vcsbase/nicknamedialog.h b/src/plugins/vcsbase/nicknamedialog.h
new file mode 100644
index 0000000000..f8d32b7ee4
--- /dev/null
+++ b/src/plugins/vcsbase/nicknamedialog.h
@@ -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
diff --git a/src/plugins/vcsbase/nicknamedialog.ui b/src/plugins/vcsbase/nicknamedialog.ui
new file mode 100644
index 0000000000..6d7217d700
--- /dev/null
+++ b/src/plugins/vcsbase/nicknamedialog.ui
@@ -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>
diff --git a/src/plugins/vcsbase/vcsbase.pro b/src/plugins/vcsbase/vcsbase.pro
index d34fe84e7c..58bbc0dd4e 100644
--- a/src/plugins/vcsbase/vcsbase.pro
+++ b/src/plugins/vcsbase/vcsbase.pro
@@ -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
diff --git a/src/plugins/vcsbase/vcsbaseconstants.h b/src/plugins/vcsbase/vcsbaseconstants.h
index 6ce3141cb6..bb180213fb 100644
--- a/src/plugins/vcsbase/vcsbaseconstants.h
+++ b/src/plugins/vcsbase/vcsbaseconstants.h
@@ -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 };
diff --git a/src/plugins/vcsbase/vcsbaseplugin.cpp b/src/plugins/vcsbase/vcsbaseplugin.cpp
index 1aa9a32ff6..0de678e80e 100644
--- a/src/plugins/vcsbase/vcsbaseplugin.cpp
+++ b/src/plugins/vcsbase/vcsbaseplugin.cpp
@@ -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
diff --git a/src/plugins/vcsbase/vcsbaseplugin.h b/src/plugins/vcsbase/vcsbaseplugin.h
index 0adbd5b8a6..7b845a3055 100644
--- a/src/plugins/vcsbase/vcsbaseplugin.h
+++ b/src/plugins/vcsbase/vcsbaseplugin.h
@@ -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
diff --git a/src/plugins/vcsbase/vcsbasesettings.cpp b/src/plugins/vcsbase/vcsbasesettings.cpp
new file mode 100644
index 0000000000..d2f6951bbd
--- /dev/null
+++ b/src/plugins/vcsbase/vcsbasesettings.cpp
@@ -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;
+}
+
+}
+}
diff --git a/src/plugins/vcsbase/vcsbasesettings.h b/src/plugins/vcsbase/vcsbasesettings.h
new file mode 100644
index 0000000000..b4efd59d33
--- /dev/null
+++ b/src/plugins/vcsbase/vcsbasesettings.h
@@ -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
diff --git a/src/plugins/vcsbase/vcsbasesettingspage.cpp b/src/plugins/vcsbase/vcsbasesettingspage.cpp
new file mode 100644
index 0000000000..0cc528a2e7
--- /dev/null
+++ b/src/plugins/vcsbase/vcsbasesettingspage.cpp
@@ -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();
+ }
+ }
+}
+
+}
+}
diff --git a/src/plugins/vcsbase/vcsbasesettingspage.h b/src/plugins/vcsbase/vcsbasesettingspage.h
new file mode 100644
index 0000000000..4492f66544
--- /dev/null
+++ b/src/plugins/vcsbase/vcsbasesettingspage.h
@@ -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
diff --git a/src/plugins/vcsbase/vcsbasesettingspage.ui b/src/plugins/vcsbase/vcsbasesettingspage.ui
new file mode 100644
index 0000000000..5636d995ce
--- /dev/null
+++ b/src/plugins/vcsbase/vcsbasesettingspage.ui
@@ -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>
diff --git a/src/plugins/vcsbase/vcsbasesubmiteditor.cpp b/src/plugins/vcsbase/vcsbasesubmiteditor.cpp
index 473abaf175..7f7c8365ed 100644
--- a/src/plugins/vcsbase/vcsbasesubmiteditor.cpp
+++ b/src/plugins/vcsbase/vcsbasesubmiteditor.cpp
@@ -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"));
diff --git a/src/plugins/vcsbase/vcsbasesubmiteditor.h b/src/plugins/vcsbase/vcsbasesubmiteditor.h
index 3dcc4e4d29..7b3b3fa358 100644
--- a/src/plugins/vcsbase/vcsbasesubmiteditor.h
+++ b/src/plugins/vcsbase/vcsbasesubmiteditor.h
@@ -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;
};