diff --git a/src/plugins/git/gitplugin.cpp b/src/plugins/git/gitplugin.cpp index cb80541e0f30f1309c0851eb1b1a42f60dbc546a..2c043404019ae1203628a3ebfe8b1855b895fef4 100644 --- a/src/plugins/git/gitplugin.cpp +++ b/src/plugins/git/gitplugin.cpp @@ -142,7 +142,8 @@ GitPlugin::GitPlugin() : m_coreListener(0), m_submitEditorFactory(0), m_versionControl(0), - m_changeTmpFile(0) + m_changeTmpFile(0), + m_submitActionTriggered(false) { m_instance = this; } @@ -651,6 +652,7 @@ Core::IEditor *GitPlugin::openSubmitEditor(const QString &fileName, const Commit void GitPlugin::submitCurrentLog() { // Close the submit editor + m_submitActionTriggered = true; QList<Core::IEditor*> editors; editors.push_back(m_core->editorManager()->currentEditor()); m_core->editorManager()->closeEditors(editors); @@ -672,11 +674,14 @@ bool GitPlugin::editorAboutToClose(Core::IEditor *iEditor) // Paranoia! if (editorFile.absoluteFilePath() != changeFile.absoluteFilePath()) return true; - // Prompt user. + // Prompt user. Force a prompt unless submit was actually invoked (that + // is, the editor was closed or shutdown). 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?")); + tr("The commit message check failed. Do you want to commit the change?"), + !m_submitActionTriggered); + m_submitActionTriggered = false; switch (answer) { case VCSBase::VCSBaseSubmitEditor::SubmitCanceled: return false; // Keep editing and change file diff --git a/src/plugins/git/gitplugin.h b/src/plugins/git/gitplugin.h index 6337a60d96916cd711e02b1cf7db03e79790dc7e..8f7f0bfeeddab0b62e234b1361cd07bf63484510 100644 --- a/src/plugins/git/gitplugin.h +++ b/src/plugins/git/gitplugin.h @@ -171,6 +171,7 @@ private: QString m_submitRepository; QStringList m_submitOrigCommitFiles; QTemporaryFile *m_changeTmpFile; + bool m_submitActionTriggered; }; } // namespace Git diff --git a/src/plugins/git/gitsubmitpanel.ui b/src/plugins/git/gitsubmitpanel.ui index aba46b7c52542d09f7ba027fc21571f5bfa5c9a0..7dfa9b3ba0fbc4973fe988caedd157a5cd22363c 100644 --- a/src/plugins/git/gitsubmitpanel.ui +++ b/src/plugins/git/gitsubmitpanel.ui @@ -6,8 +6,8 @@ <rect> <x>0</x> <y>0</y> - <width>201</width> - <height>189</height> + <width>374</width> + <height>229</height> </rect> </property> <layout class="QVBoxLayout" name="verticalLayout"> @@ -59,26 +59,43 @@ <property name="title"> <string>Commit Information</string> </property> - <layout class="QFormLayout" name="editFormLayout"> - <item row="0" column="0"> - <widget class="QLabel" name="authorLabel"> - <property name="text"> - <string>Author:</string> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="QLineEdit" name="authorLineEdit"/> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <layout class="QFormLayout" name="formLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="authorLabel"> + <property name="text"> + <string>Author:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="authorLineEdit"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="emailLabel"> + <property name="text"> + <string>Email:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="emailLineEdit"/> + </item> + </layout> </item> - <item row="1" column="0"> - <widget class="QLabel" name="emailLabel"> - <property name="text"> - <string>Email:</string> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> </property> - </widget> - </item> - <item row="1" column="1"> - <widget class="QLineEdit" name="emailLineEdit"/> + <property name="sizeHint" stdset="0"> + <size> + <width>161</width> + <height>20</height> + </size> + </property> + </spacer> </item> </layout> </widget> diff --git a/src/plugins/perforce/perforceplugin.cpp b/src/plugins/perforce/perforceplugin.cpp index 2b068e2982bfdecba6fb94ee590a643ba921d5a6..25ea4524a6c74c6880f7b331983bd1cb24c5eb45 100644 --- a/src/plugins/perforce/perforceplugin.cpp +++ b/src/plugins/perforce/perforceplugin.cpp @@ -175,13 +175,18 @@ PerforcePlugin::PerforcePlugin() : m_diffProjectAction(0), m_diffAllAction(0), m_resolveAction(0), - m_submitAction(0), + m_submitAction(0), m_pendingAction(0), m_describeAction(0), m_annotateCurrentAction(0), m_annotateAction(0), m_filelogCurrentAction(0), m_filelogAction(0), + m_submitCurrentLogAction(0), + m_submitActionTriggered(false), + m_diffSelectedFiles(0), + m_undoAction(0), + m_redoAction(0), m_changeTmpFile(0), #ifdef USE_P4_API m_workbenchClientUser(0), @@ -949,6 +954,7 @@ void PerforcePlugin::describe(const QString & source, const QString &n) void PerforcePlugin::submitCurrentLog() { + m_submitActionTriggered = true; Core::EditorManager *em = Core::EditorManager::instance(); em->closeEditors(QList<Core::IEditor*>() << em->currentEditor()); } @@ -966,11 +972,15 @@ bool PerforcePlugin::editorAboutToClose(Core::IEditor *editor) return true; QFileInfo editorFile(fileIFace->fileName()); QFileInfo changeFile(m_changeTmpFile->fileName()); - if (editorFile.absoluteFilePath() == changeFile.absoluteFilePath()) { + if (editorFile.absoluteFilePath() == changeFile.absoluteFilePath()) { + // Prompt the user. Force a prompt unless submit was actually invoked (that + // is, the editor was closed or shutdown). 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")); + tr("The commit message check failed. Do you want to submit this change list"), + !m_submitActionTriggered); + m_submitActionTriggered = false; if (answer == VCSBase::VCSBaseSubmitEditor::SubmitCanceled) return false; @@ -1010,10 +1020,10 @@ bool PerforcePlugin::editorAboutToClose(Core::IEditor *editor) m_changeTmpFile = 0; return false; } - QString output = QString::fromUtf8(proc.readAll()); + const QString output = QString::fromUtf8(proc.readAll()); showOutput(output); - if (output.contains("Out of date files must be resolved or reverted"), true) { - QMessageBox::warning(editor->widget(), "Pending change", "Could not submit the change, because your workspace was out of date. Created a pending submit instead."); + if (output.contains(QLatin1String("Out of date files must be resolved or reverted)"))) { + QMessageBox::warning(editor->widget(), tr("Pending change"), tr("Could not submit the change, because your workspace was out of date. Created a pending submit instead.")); } QApplication::restoreOverrideCursor(); } diff --git a/src/plugins/perforce/perforceplugin.h b/src/plugins/perforce/perforceplugin.h index 919ab0c6d0b68c4a9c3a965f90f3b2a4ba1188e2..8ac7188994ec5857a5b7d116b608210e9b6a3a54 100644 --- a/src/plugins/perforce/perforceplugin.h +++ b/src/plugins/perforce/perforceplugin.h @@ -190,6 +190,7 @@ private: QAction *m_filelogCurrentAction; QAction *m_filelogAction; QAction *m_submitCurrentLogAction; + bool m_submitActionTriggered; QAction *m_diffSelectedFiles; QAction *m_undoAction; @@ -232,7 +233,7 @@ private: PerforceVersionControl *m_versionControl; PerforceSettings m_settings; - friend class PerforceOutputWindow; + friend class PerforceOutputWindow; // needs openFiles() }; } // namespace Perforce diff --git a/src/plugins/subversion/subversionplugin.cpp b/src/plugins/subversion/subversionplugin.cpp index 189b83e0e30aab4c7a57154ad8ee07395ab4a060..064bb086938adfd48dac153f30ee0232900c14d3 100644 --- a/src/plugins/subversion/subversionplugin.cpp +++ b/src/plugins/subversion/subversionplugin.cpp @@ -188,7 +188,8 @@ SubversionPlugin::SubversionPlugin() : m_submitCurrentLogAction(0), m_submitDiffAction(0), m_submitUndoAction(0), - m_submitRedoAction(0) + m_submitRedoAction(0), + m_submitActionTriggered(false) { } @@ -463,11 +464,14 @@ bool SubversionPlugin::editorAboutToClose(Core::IEditor *iEditor) if (editorFile.absoluteFilePath() != changeFile.absoluteFilePath()) return true; // Oops?! - // Prompt user. + // Prompt user. Force a prompt unless submit was actually invoked (that + // is, the editor was closed or shutdown). 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?")); + tr("The commit message check failed. Do you want to commit the change?"), + !m_submitActionTriggered); + m_submitActionTriggered = false; switch (answer) { case VCSBase::VCSBaseSubmitEditor::SubmitCanceled: return false; // Keep editing and change file @@ -922,6 +926,7 @@ void SubversionPlugin::slotDescribe() void SubversionPlugin::submitCurrentLog() { + m_submitActionTriggered = true; Core::EditorManager::instance()->closeEditors(QList<Core::IEditor*>() << Core::EditorManager::instance()->currentEditor()); } diff --git a/src/plugins/subversion/subversionplugin.h b/src/plugins/subversion/subversionplugin.h index a9dbd5ca658cc65b5092d7f7b36fce7dab42edaa..1611db8f2be33054e5ab95dbd166bc01d50e9075 100644 --- a/src/plugins/subversion/subversionplugin.h +++ b/src/plugins/subversion/subversionplugin.h @@ -168,6 +168,7 @@ private: QAction *m_submitDiffAction; QAction *m_submitUndoAction; QAction *m_submitRedoAction; + bool m_submitActionTriggered; static const char * const SUBVERSION_MENU; static const char * const ADD; diff --git a/src/plugins/vcsbase/nicknamedialog.cpp b/src/plugins/vcsbase/nicknamedialog.cpp index d8578aa7e498e94cd490b62cd0f9002012b8ee14..d6a10ad30297d98830f0d4543c074d35bc74779c 100644 --- a/src/plugins/vcsbase/nicknamedialog.cpp +++ b/src/plugins/vcsbase/nicknamedialog.cpp @@ -28,6 +28,7 @@ **************************************************************************/ #include "nicknamedialog.h" +#include "vcsbaseplugin.h" #include "ui_nicknamedialog.h" #include <QtCore/QDebug> @@ -36,13 +37,19 @@ #include <QtGui/QStandardItemModel> #include <QtGui/QSortFilterProxyModel> +enum { NickNameRole = Qt::UserRole + 1 }; + namespace VCSBase { namespace Internal { -struct NickEntry { +// For code clarity, a struct representing the entries of a mail map file +// with parse and model functions. +struct NickNameEntry { void clear(); bool parse(const QString &); QString nickName() const; + QList<QStandardItem *> toModelRow() const; + static QString nickNameOf(const QStandardItem *item); QString name; QString email; @@ -50,7 +57,7 @@ struct NickEntry { QString aliasEmail; }; -void NickEntry::clear() +void NickNameEntry::clear() { name.clear(); email.clear(); @@ -60,7 +67,7 @@ void NickEntry::clear() // Parse "Hans Mustermann <HM@acme.de> [Alias [<alias@acme.de>]]" -bool NickEntry::parse(const QString &l) +bool NickNameEntry::parse(const QString &l) { clear(); const QChar lessThan = QLatin1Char('<'); @@ -105,50 +112,59 @@ static inline QString formatNick(const QString &name, const QString &email) return rc; } -QString NickEntry::nickName() const +QString NickNameEntry::nickName() const { return aliasName.isEmpty() ? formatNick(name, email) : formatNick(aliasName, aliasEmail); } -// Sort by name -bool operator<(const NickEntry &n1, const NickEntry &n2) +QList<QStandardItem *> NickNameEntry::toModelRow() const { - return n1.name < n2.name; + const QVariant nickNameData = nickName(); + QStandardItem *i1 = new QStandardItem(name); + i1->setData(nickNameData, NickNameRole); + QStandardItem *i2 = new QStandardItem(email); + i2->setData(nickNameData, NickNameRole); + QStandardItem *i3 = new QStandardItem(aliasName); + i3->setData(nickNameData, NickNameRole); + QStandardItem *i4 = new QStandardItem(aliasEmail); + i4->setData(nickNameData, NickNameRole); + QList<QStandardItem *> row; + row << i1 << i2 << i3 << i4; + return row; } -QDebug operator<<(QDebug d, const NickEntry &e) +QString NickNameEntry::nickNameOf(const QStandardItem *item) +{ + return item->data(NickNameRole).toString(); +} + +QDebug operator<<(QDebug d, const NickNameEntry &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() +// Globally cached model tacked onto the plugin. +static QStandardItemModel *nickModel() { - static QList<NickEntry> rc; - return rc; + static QStandardItemModel *model = 0; + if (!model) { + model = new QStandardItemModel(VCSBasePlugin::instance()); + QStringList headers; + headers << NickNameDialog::tr("Name") + << NickNameDialog::tr("E-mail") + << NickNameDialog::tr("Alias") + << NickNameDialog::tr("Alias e-mail"); + model->setHorizontalHeaderLabels(headers); + } + return model; } -// 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; +static inline void clearModel(QStandardItemModel *model) +{ + if (const int rowCount = model->rowCount()) + model->removeRows(0, rowCount); } NickNameDialog::NickNameDialog(QWidget *parent) : @@ -161,7 +177,7 @@ NickNameDialog::NickNameDialog(QWidget *parent) : okButton()->setEnabled(false); // Populate model and grow tree to accommodate it - m_filterModel->setSourceModel(createModel(this)); + m_filterModel->setSourceModel(nickModel()); m_filterModel->setFilterCaseSensitivity(Qt::CaseInsensitive); m_ui->filterTreeView->setModel(m_filterModel); const int columnCount = m_filterModel->columnCount(); @@ -205,14 +221,15 @@ 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(); + if (const QStandardItem *item = nickModel()->itemFromIndex(sourceIndex)) + return NickNameEntry::nickNameOf(item); } return QString(); } void NickNameDialog::clearNickNames() { - nickList().clear(); + clearModel(nickModel()); } bool NickNameDialog::readNickNamesFromMailCapFile(const QString &fileName, QString *errorMessage) @@ -222,28 +239,30 @@ bool NickNameDialog::readNickNamesFromMailCapFile(const QString &fileName, QStri *errorMessage = tr("Cannot open '%1': %2").arg(fileName, file.errorString()); return false; } + QStandardItemModel *model = nickModel(); + clearModel(model); // Split into lines and read - QList<NickEntry> &nl = nickList(); - nl.clear(); - NickEntry entry; + NickNameEntry 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); + model->appendRow(entry.toModelRow()); } else { qWarning("%s: Invalid mail cap entry at line %d: '%s'\n", qPrintable(fileName), i + 1, qPrintable(lines.at(i))); } } - qStableSort(nl); + model->sort(0); return true; } QStringList NickNameDialog::nickNameList() { QStringList rc; - foreach(const NickEntry &ne, nickList()) - rc.push_back(ne.nickName()); + const QStandardItemModel *model = nickModel(); + const int rowCount = model->rowCount(); + for (int r = 0; r < rowCount; r++) + rc.push_back(NickNameEntry::nickNameOf(model->item(r, 0))); return rc; } diff --git a/src/plugins/vcsbase/nicknamedialog.h b/src/plugins/vcsbase/nicknamedialog.h index f8d32b7ee4c05057311d10ce482efa72f35d8086..7c644105777a916951b58a4c16ec3acc1f5fdb09 100644 --- a/src/plugins/vcsbase/nicknamedialog.h +++ b/src/plugins/vcsbase/nicknamedialog.h @@ -48,7 +48,9 @@ namespace Internal { * 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. */ + * be preferred. The static functions to read/clear the mail map + * files access a global model which is shared by all instances of the + * dialog to achieve updating. */ class NickNameDialog : public QDialog { Q_OBJECT diff --git a/src/plugins/vcsbase/vcsbasesettingspage.ui b/src/plugins/vcsbase/vcsbasesettingspage.ui index 5636d995ceb868b7967297e95bd173dee98dd00e..01e478f8176983f6434ace1814a772c40d6352ae 100644 --- a/src/plugins/vcsbase/vcsbasesettingspage.ui +++ b/src/plugins/vcsbase/vcsbasesettingspage.ui @@ -47,11 +47,11 @@ <item row="2" column="0"> <widget class="QLabel" name="nickNameMailMapLabel"> <property name="toolTip"> - <string>A file listing user names in 2-column mailmap format: + <string>A file listing user names and email addresses in a 4-column mailmap format: name <email> alias <email></string> </property> <property name="text"> - <string>User name file:</string> + <string>User/alias configuration file:</string> </property> </widget> </item> diff --git a/src/plugins/vcsbase/vcsbasesubmiteditor.cpp b/src/plugins/vcsbase/vcsbasesubmiteditor.cpp index 7f7c8365edfa65347709577f97fc8698774810bf..46ae4374cf71a9c339c8d9b7e97e2319e86a8c07 100644 --- a/src/plugins/vcsbase/vcsbasesubmiteditor.cpp +++ b/src/plugins/vcsbase/vcsbasesubmiteditor.cpp @@ -115,7 +115,7 @@ VCSBaseSubmitEditor::VCSBaseSubmitEditor(const VCSBaseSubmitEditorParameters *pa const Internal::VCSBaseSettings settings = Internal::VCSBasePlugin::instance()->settings(); // Add additional context menu settings - if (!settings.submitMessageCheckScript.isEmpty() || !settings.nickNameFieldListFile.isEmpty()) { + if (!settings.submitMessageCheckScript.isEmpty() || !settings.nickNameMailMap.isEmpty()) { QAction *sep = new QAction(this); sep->setSeparator(true); m_d->m_widget->addDescriptionEditContextMenuAction(sep); @@ -126,7 +126,7 @@ VCSBaseSubmitEditor::VCSBaseSubmitEditor(const VCSBaseSubmitEditorParameters *pa m_d->m_widget->addDescriptionEditContextMenuAction(checkAction); } // Insert nick - if (!settings.nickNameFieldListFile.isEmpty()) { + if (!settings.nickNameMailMap.isEmpty()) { QAction *insertAction = new QAction(tr("Insert name..."), this); connect(insertAction, SIGNAL(triggered()), this, SLOT(slotInsertNickName())); m_d->m_widget->addDescriptionEditContextMenuAction(insertAction); @@ -369,17 +369,22 @@ bool VCSBaseSubmitEditor::setFileContents(const QString &contents) enum { checkDialogMinimumWidth = 500 }; VCSBaseSubmitEditor::PromptSubmitResult - VCSBaseSubmitEditor::promptSubmit(const QString &title, const QString &question, const QString &checkFailureQuestion) const + VCSBaseSubmitEditor::promptSubmit(const QString &title, + const QString &question, + const QString &checkFailureQuestion, + bool forcePrompt) const { QString errorMessage; QMessageBox::StandardButton answer = QMessageBox::Yes; + const bool prompt = forcePrompt || Internal::VCSBasePlugin::instance()->settings().promptForSubmit; + 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) { + if (prompt) { answer = QMessageBox::question(parent, title, question, QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel, QMessageBox::Yes); @@ -443,6 +448,14 @@ bool VCSBaseSubmitEditor::checkSubmitMessage(QString *errorMessage) const const QString checkScript = submitMessageCheckScript(); if (checkScript.isEmpty()) return true; + QApplication::setOverrideCursor(Qt::WaitCursor); + const bool rc = runSubmitMessageCheckScript(checkScript, errorMessage); + QApplication::restoreOverrideCursor(); + return rc; +} + +bool VCSBaseSubmitEditor::runSubmitMessageCheckScript(const QString &checkScript, QString *errorMessage) const +{ // Write out message QString tempFilePattern = QDir::tempPath(); if (!tempFilePattern.endsWith(QDir::separator())) diff --git a/src/plugins/vcsbase/vcsbasesubmiteditor.h b/src/plugins/vcsbase/vcsbasesubmiteditor.h index 7b3b3fa358334ee6ef1c84fd88e2a68cf777c69c..4a02d262a29f8fd5311d347b0a1ec6e3be9cc0b7 100644 --- a/src/plugins/vcsbase/vcsbasesubmiteditor.h +++ b/src/plugins/vcsbase/vcsbasesubmiteditor.h @@ -106,11 +106,14 @@ public: virtual ~VCSBaseSubmitEditor(); - // A utility routine to be called when clsing a submit editor. + // A utility routine to be called when closing a submit editor. // Runs checks on the message and prompts according to configuration. + // Force prompt should be true if it is invoked by closing an editor + // as opposed to invoking the "Submit" button. enum PromptSubmitResult { SubmitConfirmed, SubmitCanceled, SubmitDiscarded }; PromptSubmitResult promptSubmit(const QString &title, const QString &question, - const QString &checkFailureQuestion) const; + const QString &checkFailureQuestion, + bool forcePrompt = false) const; int fileNameColumn() const; void setFileNameColumn(int c); @@ -169,6 +172,7 @@ protected: private: void createUserFields(const QString &fieldConfigFile); bool checkSubmitMessage(QString *errorMessage) const; + bool runSubmitMessageCheckScript(const QString &script, QString *errorMessage) const; QString promptForNickName(); VCSBaseSubmitEditorPrivate *m_d;