diff --git a/src/libs/utils/submiteditorwidget.cpp b/src/libs/utils/submiteditorwidget.cpp
index a047682b81502b4fd8d98d434487768b05657cb0..b9c8259020b99a993a195de2c45c76fbfb8a0015 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 74dd56d006ed2f2c99aa3146dcab6b64bb3b34b6..e77da4379e56d2c351f811be1f8741a60cb4173b 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 29af54298879fad17e6eaf8f0f2d26e3dd0971a1..cb80541e0f30f1309c0851eb1b1a42f60dbc546a 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 65efbc977f3fb7a5e05516eaced425ac3a5ea638..2b068e2982bfdecba6fb94ee590a643ba921d5a6 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 67e56eacf65d1c65996c40408e8f39cf01a40401..189b83e0e30aab4c7a57154ad8ee07395ab4a060 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 0000000000000000000000000000000000000000..d8578aa7e498e94cd490b62cd0f9002012b8ee14
--- /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 0000000000000000000000000000000000000000..f8d32b7ee4c05057311d10ce482efa72f35d8086
--- /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 0000000000000000000000000000000000000000..6d7217d7005273ad03e92ef9ed560e7caf6f9a30
--- /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 d34fe84e7c05476658180ae46779d68540842f5b..58bbc0dd4e8d5292cf6e657172bfe7b6a48e1e0d 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 6ce3141cb64ac9cd175bcd4c4132d1f18a16909c..bb180213fb24a7a4f2ba2b66f05cce694499c16e 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 1aa9a32ff6134197de8c2933e6763e3a82df0703..0de678e80e341a718584427d0860953c26ec3e65 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 0adbd5b8a611abfd785ac51293e05d9b7497f8c2..7b845a3055da180bf6cdfe2ad1065e118a5cdb13 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 0000000000000000000000000000000000000000..d2f6951bbd7eed52d4b5e1503b31eb0bf3b0faec
--- /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 0000000000000000000000000000000000000000..b4efd59d33bd607e2ada85f83c3a352797b7b1ac
--- /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 0000000000000000000000000000000000000000..0cc528a2e7b2c8ce22d0b367ef1ef3f22724e615
--- /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 0000000000000000000000000000000000000000..4492f665444af240ea6933b399262c0da4278541
--- /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 0000000000000000000000000000000000000000..5636d995ceb868b7967297e95bd173dee98dd00e
--- /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 473abaf1757cee2554f3825250296a32465314e1..7f7c8365edfa65347709577f97fc8698774810bf 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 3dcc4e4d299ea52dee4089e253efff0ec8889553..7b3b3fa358334ee6ef1c84fd88e2a68cf777c69c 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;
 };