Files
qt-creator/src/plugins/vcsbase/vcsbasesubmiteditor.cpp

827 lines
28 KiB
C++
Raw Normal View History

/**************************************************************************
2008-12-02 12:01:29 +01:00
**
** This file is part of Qt Creator
**
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
2008-12-02 12:01:29 +01:00
**
** Contact: Nokia Corporation (qt-info@nokia.com)
2008-12-02 12:01:29 +01:00
**
**
** GNU Lesser General Public License Usage
**
2011-04-13 08:42:33 +02:00
** 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.
**
2010-12-17 16:01:08 +01:00
** In addition, as a special exception, Nokia gives you certain additional
2011-04-13 08:42:33 +02:00
** rights. These rights are described in the Nokia Qt LGPL Exception
2010-12-17 16:01:08 +01:00
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
2011-04-13 08:42:33 +02:00
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
2010-12-17 16:01:08 +01:00
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
2008-12-02 12:01:29 +01:00
**
**************************************************************************/
2008-12-02 16:19:05 +01:00
2008-12-02 12:01:29 +01:00
#include "vcsbasesubmiteditor.h"
#include "commonvcssettings.h"
#include "vcsbaseoutputwindow.h"
#include "vcsplugin.h"
#include "nicknamedialog.h"
2008-12-02 12:01:29 +01:00
#include "submiteditorfile.h"
#include <aggregation/aggregate.h>
#include <cplusplus/Control.h>
#include <cplusplus/CoreTypes.h>
#include <cplusplus/FullySpecifiedType.h>
#include <cplusplus/Literals.h>
#include <cpptools/ModelManagerInterface.h>
#include <cplusplus/Symbol.h>
#include <cplusplus/Symbols.h>
#include <cplusplus/TranslationUnit.h>
#include <coreplugin/idocument.h>
#include <coreplugin/icore.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/id.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <utils/completingtextedit.h>
2008-12-02 12:01:29 +01:00
#include <utils/submiteditorwidget.h>
#include <utils/checkablemessagebox.h>
#include <utils/synchronousprocess.h>
#include <utils/submitfieldwidget.h>
#include <utils/fileutils.h>
2008-12-02 12:01:29 +01:00
#include <find/basetextfind.h>
#include <texteditor/fontsettings.h>
#include <texteditor/texteditorsettings.h>
2008-12-02 12:01:29 +01:00
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/session.h>
#include <projectexplorer/project.h>
#include <QDebug>
#include <QDir>
#include <QTemporaryFile>
#include <QProcess>
#include <QFile>
#include <QFileInfo>
#include <QPointer>
#include <QStringListModel>
#include <QTextStream>
#include <QStyle>
#include <QToolBar>
#include <QAction>
#include <QApplication>
#include <QMessageBox>
#include <QMainWindow>
#include <QCompleter>
#include <QLineEdit>
#include <QTextEdit>
#include <cstring>
2008-12-02 12:01:29 +01:00
enum { debug = 0 };
enum { wantToolBar = 0 };
2008-12-02 12:01:29 +01:00
// Return true if word is meaningful and can be added to a completion model
static bool acceptsWordForCompletion(const char *word)
{
if (word == 0)
return false;
static const std::size_t minWordLength = 7;
return std::strlen(word) >= minWordLength;
}
// Return the class name which function belongs to
static const char *belongingClassName(const CPlusPlus::Function *function)
{
if (function == 0)
return 0;
const CPlusPlus::Name *funcName = function->name();
if (funcName != 0 && funcName->asQualifiedNameId() != 0) {
const CPlusPlus::Name *funcBaseName = funcName->asQualifiedNameId()->base();
if (funcBaseName != 0 && funcBaseName->identifier() != 0)
return funcBaseName->identifier()->chars();
}
return 0;
}
2011-03-28 14:19:17 +02:00
/*!
\struct VcsBase::VcsBaseSubmitEditorParameters
2011-03-28 14:19:17 +02:00
\brief Utility struct to parametrize a VcsBaseSubmitEditor.
2011-03-28 14:19:17 +02:00
*/
/*!
\class VcsBase::VcsBaseSubmitEditor
2011-03-28 14:19:17 +02:00
\brief Base class for a submit editor based on the Utils::SubmitEditorWidget.
Presents the commit message in a text editor and an
checkable list of modified files in a list window. The user can delete
files from the list by pressing unchecking them or diff the selection
by doubleclicking.
The action matching the the ids (unless 0) of the parameter struct will be
registered with the EditorWidget and submit/diff actions will be added to
a toolbar.
For the given context, there must be only one instance of the editor
active.
To start a submit, set the submit template on the editor and the output
of the VCS status command listing the modified files as fileList and open
it.
The submit process is started by listening on the editor close
signal and then asking the IDocument interface of the editor to save the file
within a DocumentManager::blockFileChange() section
2011-03-28 14:19:17 +02:00
and to launch the submit process. In addition, the action registered
for submit should be connected to a slot triggering the close of the
current editor in the editor manager.
*/
namespace VcsBase {
2008-12-02 12:01:29 +01:00
using namespace Internal;
using namespace Utils;
static inline QString submitMessageCheckScript()
{
return VcsPlugin::instance()->settings().submitMessageCheckScript;
}
struct VcsBaseSubmitEditorPrivate
{
VcsBaseSubmitEditorPrivate(const VcsBaseSubmitEditorParameters *parameters,
SubmitEditorWidget *editorWidget,
2008-12-02 12:01:29 +01:00
QObject *q);
SubmitEditorWidget *m_widget;
2008-12-02 12:01:29 +01:00
QToolBar *m_toolWidget;
const VcsBaseSubmitEditorParameters *m_parameters;
2008-12-02 12:01:29 +01:00
QString m_displayName;
QString m_checkScriptWorkingDirectory;
SubmitEditorFile *m_file;
2008-12-02 12:01:29 +01:00
QPointer<QAction> m_diffAction;
QPointer<QAction> m_submitAction;
NickNameDialog *m_nickNameDialog;
2008-12-02 12:01:29 +01:00
};
VcsBaseSubmitEditorPrivate::VcsBaseSubmitEditorPrivate(const VcsBaseSubmitEditorParameters *parameters,
SubmitEditorWidget *editorWidget,
2008-12-02 12:01:29 +01:00
QObject *q) :
m_widget(editorWidget),
m_toolWidget(0),
m_parameters(parameters),
m_file(new SubmitEditorFile(QLatin1String(parameters->mimeType), q)),
m_nickNameDialog(0)
2008-12-02 12:01:29 +01:00
{
QCompleter *completer = new QCompleter(q);
completer->setCaseSensitivity(Qt::CaseSensitive);
completer->setModelSorting(QCompleter::CaseSensitivelySortedModel);
m_widget->descriptionEdit()->setCompleter(completer);
m_widget->descriptionEdit()->setCompletionLengthThreshold(4);
2008-12-02 12:01:29 +01:00
}
VcsBaseSubmitEditor::VcsBaseSubmitEditor(const VcsBaseSubmitEditorParameters *parameters,
SubmitEditorWidget *editorWidget) :
d(new VcsBaseSubmitEditorPrivate(parameters, editorWidget, this))
2008-12-02 12:01:29 +01:00
{
setContext(Core::Context(parameters->context));
setWidget(d->m_widget);
// Message font according to settings
const TextEditor::FontSettings fs = TextEditor::TextEditorSettings::instance()->fontSettings();
QFont font = editorWidget->descriptionEdit()->font();
font.setFamily(fs.family());
font.setPointSize(fs.fontSize());
editorWidget->descriptionEdit()->setFont(font);
d->m_file->setModified(false);
2008-12-02 12:01:29 +01:00
// We are always clean to prevent the editor manager from asking to save.
connect(d->m_file, SIGNAL(saveMe(QString*,QString,bool)),
this, SLOT(save(QString*,QString,bool)));
2008-12-02 12:01:29 +01:00
connect(d->m_widget, SIGNAL(diffSelected(QStringList)), this, SLOT(slotDiffSelectedVcsFiles(QStringList)));
connect(d->m_widget->descriptionEdit(), SIGNAL(textChanged()), this, SLOT(slotDescriptionChanged()));
2008-12-02 12:01:29 +01:00
const CommonVcsSettings settings = VcsPlugin::instance()->settings();
// Add additional context menu settings
if (!settings.submitMessageCheckScript.isEmpty() || !settings.nickNameMailMap.isEmpty()) {
QAction *sep = new QAction(this);
sep->setSeparator(true);
d->m_widget->addDescriptionEditContextMenuAction(sep);
// Run check action
if (!settings.submitMessageCheckScript.isEmpty()) {
2010-09-21 17:17:54 +02:00
QAction *checkAction = new QAction(tr("Check Message"), this);
connect(checkAction, SIGNAL(triggered()), this, SLOT(slotCheckSubmitMessage()));
d->m_widget->addDescriptionEditContextMenuAction(checkAction);
}
// Insert nick
if (!settings.nickNameMailMap.isEmpty()) {
2010-09-21 17:17:54 +02:00
QAction *insertAction = new QAction(tr("Insert Name..."), this);
connect(insertAction, SIGNAL(triggered()), this, SLOT(slotInsertNickName()));
d->m_widget->addDescriptionEditContextMenuAction(insertAction);
}
}
// Do we have user fields?
if (!settings.nickNameFieldListFile.isEmpty())
createUserFields(settings.nickNameFieldListFile);
2009-03-20 14:22:20 +01:00
// wrapping. etc
slotUpdateEditorSettings(settings);
connect(VcsPlugin::instance(),
SIGNAL(settingsChanged(VcsBase::Internal::CommonVcsSettings)),
this, SLOT(slotUpdateEditorSettings(VcsBase::Internal::CommonVcsSettings)));
2009-03-20 14:22:20 +01:00
2008-12-02 12:01:29 +01:00
Aggregation::Aggregate *aggregate = new Aggregation::Aggregate;
aggregate->add(new Find::BaseTextFind(d->m_widget->descriptionEdit()));
2008-12-02 12:01:29 +01:00
aggregate->add(this);
}
VcsBaseSubmitEditor::~VcsBaseSubmitEditor()
2008-12-02 12:01:29 +01:00
{
delete d->m_toolWidget;
delete d->m_widget;
delete d;
2008-12-02 12:01:29 +01:00
}
void VcsBaseSubmitEditor::slotUpdateEditorSettings(const CommonVcsSettings &s)
2009-03-20 14:22:20 +01:00
{
setLineWrapWidth(s.lineWrapWidth);
setLineWrap(s.lineWrap);
}
// Return a trimmed list of non-empty field texts
static inline QStringList fieldTexts(const QString &fileContents)
{
QStringList rc;
const QStringList rawFields = fileContents.trimmed().split(QLatin1Char('\n'));
foreach (const QString &field, rawFields) {
const QString trimmedField = field.trimmed();
if (!trimmedField.isEmpty())
rc.push_back(trimmedField);
}
return rc;
}
void VcsBaseSubmitEditor::createUserFields(const QString &fieldConfigFile)
{
Utils::FileReader reader;
if (!reader.fetch(fieldConfigFile, QIODevice::Text, Core::ICore::mainWindow()))
return;
// Parse into fields
const QStringList fields = fieldTexts(QString::fromUtf8(reader.data()));
if (fields.empty())
return;
// Create a completer on user names
const QStandardItemModel *nickNameModel = VcsPlugin::instance()->nickNameModel();
QCompleter *completer = new QCompleter(NickNameDialog::nickNameList(nickNameModel), this);
SubmitFieldWidget *fieldWidget = new SubmitFieldWidget;
connect(fieldWidget, SIGNAL(browseButtonClicked(int,QString)),
this, SLOT(slotSetFieldNickName(int)));
fieldWidget->setCompleter(completer);
fieldWidget->setAllowDuplicateFields(true);
fieldWidget->setHasBrowseButton(true);
fieldWidget->setFields(fields);
d->m_widget->addSubmitFieldWidget(fieldWidget);
}
void VcsBaseSubmitEditor::registerActions(QAction *editorUndoAction, QAction *editorRedoAction,
QAction *submitAction, QAction *diffAction)
{
d->m_widget->registerActions(editorUndoAction, editorRedoAction, submitAction, diffAction);
d->m_diffAction = diffAction;
d->m_submitAction = submitAction;
}
void VcsBaseSubmitEditor::unregisterActions(QAction *editorUndoAction, QAction *editorRedoAction,
QAction *submitAction, QAction *diffAction)
{
d->m_widget->unregisterActions(editorUndoAction, editorRedoAction, submitAction, diffAction);
d->m_diffAction = d->m_submitAction = 0;
}
int VcsBaseSubmitEditor::fileNameColumn() const
{
return d->m_widget->fileNameColumn();
}
void VcsBaseSubmitEditor::setFileNameColumn(int c)
{
d->m_widget->setFileNameColumn(c);
}
QAbstractItemView::SelectionMode VcsBaseSubmitEditor::fileListSelectionMode() const
{
return d->m_widget->fileListSelectionMode();
}
void VcsBaseSubmitEditor::setFileListSelectionMode(QAbstractItemView::SelectionMode sm)
{
d->m_widget->setFileListSelectionMode(sm);
}
bool VcsBaseSubmitEditor::isEmptyFileListEnabled() const
{
return d->m_widget->isEmptyFileListEnabled();
}
void VcsBaseSubmitEditor::setEmptyFileListEnabled(bool e)
{
d->m_widget->setEmptyFileListEnabled(e);
}
bool VcsBaseSubmitEditor::lineWrap() const
2009-03-20 14:22:20 +01:00
{
return d->m_widget->lineWrap();
2009-03-20 14:22:20 +01:00
}
void VcsBaseSubmitEditor::setLineWrap(bool w)
2009-03-20 14:22:20 +01:00
{
d->m_widget->setLineWrap(w);
2009-03-20 14:22:20 +01:00
}
int VcsBaseSubmitEditor::lineWrapWidth() const
2009-03-20 14:22:20 +01:00
{
return d->m_widget->lineWrapWidth();
2009-03-20 14:22:20 +01:00
}
void VcsBaseSubmitEditor::setLineWrapWidth(int w)
2009-03-20 14:22:20 +01:00
{
d->m_widget->setLineWrapWidth(w);
2009-03-20 14:22:20 +01:00
}
void VcsBaseSubmitEditor::slotDescriptionChanged()
2008-12-02 12:01:29 +01:00
{
}
bool VcsBaseSubmitEditor::createNew(const QString &contents)
2008-12-02 12:01:29 +01:00
{
setFileContents(contents);
return true;
}
bool VcsBaseSubmitEditor::open(QString *errorString, const QString &fileName, const QString &realFileName)
2008-12-02 12:01:29 +01:00
{
if (fileName.isEmpty())
return false;
Utils::FileReader reader;
if (!reader.fetch(realFileName, QIODevice::Text, errorString))
2008-12-02 12:01:29 +01:00
return false;
const QString text = QString::fromLocal8Bit(reader.data());
2008-12-02 12:01:29 +01:00
if (!createNew(text))
return false;
d->m_file->setFileName(QFileInfo(fileName).absoluteFilePath());
d->m_file->setModified(fileName != realFileName);
2008-12-02 12:01:29 +01:00
return true;
}
Core::IDocument *VcsBaseSubmitEditor::document()
2008-12-02 12:01:29 +01:00
{
return d->m_file;
2008-12-02 12:01:29 +01:00
}
QString VcsBaseSubmitEditor::displayName() const
2008-12-02 12:01:29 +01:00
{
if (d->m_displayName.isEmpty())
d->m_displayName = QCoreApplication::translate("VCS", d->m_parameters->displayName);
return d->m_displayName;
2008-12-02 12:01:29 +01:00
}
void VcsBaseSubmitEditor::setDisplayName(const QString &title)
2008-12-02 12:01:29 +01:00
{
d->m_displayName = title;
emit changed();
2008-12-02 12:01:29 +01:00
}
QString VcsBaseSubmitEditor::checkScriptWorkingDirectory() const
{
return d->m_checkScriptWorkingDirectory;
}
void VcsBaseSubmitEditor::setCheckScriptWorkingDirectory(const QString &s)
{
d->m_checkScriptWorkingDirectory = s;
}
bool VcsBaseSubmitEditor::duplicateSupported() const
2008-12-02 12:01:29 +01:00
{
return false;
}
Core::IEditor *VcsBaseSubmitEditor::duplicate(QWidget * /*parent*/)
2008-12-02 12:01:29 +01:00
{
return 0;
}
Core::Id VcsBaseSubmitEditor::id() const
2008-12-02 12:01:29 +01:00
{
return d->m_parameters->id;
2008-12-02 12:01:29 +01:00
}
static QToolBar *createToolBar(const QWidget *someWidget, QAction *submitAction, QAction *diffAction)
{
// Create
QToolBar *toolBar = new QToolBar;
toolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
const int size = someWidget->style()->pixelMetric(QStyle::PM_SmallIconSize);
toolBar->setIconSize(QSize(size, size));
toolBar->addSeparator();
if (submitAction)
toolBar->addAction(submitAction);
if (diffAction)
toolBar->addAction(diffAction);
return toolBar;
}
QWidget *VcsBaseSubmitEditor::toolBar()
2008-12-02 12:01:29 +01:00
{
if (!wantToolBar)
return 0;
if (d->m_toolWidget)
return d->m_toolWidget;
2008-12-02 12:01:29 +01:00
if (!d->m_diffAction && !d->m_submitAction)
2008-12-02 12:01:29 +01:00
return 0;
// Create
d->m_toolWidget = createToolBar(d->m_widget, d->m_submitAction, d->m_diffAction);
return d->m_toolWidget;
2008-12-02 12:01:29 +01:00
}
QByteArray VcsBaseSubmitEditor::saveState() const
2008-12-02 12:01:29 +01:00
{
return QByteArray();
}
bool VcsBaseSubmitEditor::restoreState(const QByteArray &/*state*/)
2008-12-02 12:01:29 +01:00
{
return true;
}
QStringList VcsBaseSubmitEditor::checkedFiles() const
2008-12-02 12:01:29 +01:00
{
return d->m_widget->checkedFiles();
2008-12-02 12:01:29 +01:00
}
void VcsBaseSubmitEditor::setFileModel(QAbstractItemModel *m, const QString &repositoryDirectory)
2008-12-02 12:01:29 +01:00
{
d->m_widget->setFileModel(m);
QSet<QString> uniqueSymbols;
const CPlusPlus::Snapshot cppSnapShot = CPlusPlus::CppModelManagerInterface::instance()->snapshot();
// Iterate over the files and get interesting symbols
for (int row = 0; row < m->rowCount(); ++row) {
const QString fileName = m->data(m->index(row, d->m_widget->fileNameColumn())).toString();
const QFileInfo fileInfo(repositoryDirectory, fileName);
// Add file name
uniqueSymbols.insert(fileInfo.fileName());
const QString filePath = fileInfo.absoluteFilePath();
// Add symbols from the C++ code model
const CPlusPlus::Document::Ptr doc = cppSnapShot.document(filePath);
if (!doc.isNull() && doc->control() != 0) {
const CPlusPlus::Control *ctrl = doc->control();
CPlusPlus::Symbol **symPtr = ctrl->firstSymbol(); // Read-only
while (symPtr != ctrl->lastSymbol()) {
const CPlusPlus::Symbol *sym = *symPtr;
const CPlusPlus::Identifier *symId = sym->identifier();
// Add any class, function or namespace identifiers
if ((sym->isClass() || sym->isFunction() || sym->isNamespace())
&& (symId != 0 && acceptsWordForCompletion(symId->chars())))
{
uniqueSymbols.insert(QString::fromUtf8(symId->chars()));
}
// Handle specific case : get "Foo" in "void Foo::function() {}"
if (sym->isFunction() && !sym->asFunction()->isDeclaration()) {
const char *className = belongingClassName(sym->asFunction());
if (acceptsWordForCompletion(className))
uniqueSymbols.insert(QString::fromUtf8(className));
}
++symPtr;
}
}
}
// Populate completer with symbols
if (!uniqueSymbols.isEmpty()) {
QCompleter *completer = d->m_widget->descriptionEdit()->completer();
QStringList symbolsList = uniqueSymbols.toList();
symbolsList.sort();
completer->setModel(new QStringListModel(symbolsList, completer));
}
2008-12-02 12:01:29 +01:00
}
QAbstractItemModel *VcsBaseSubmitEditor::fileModel() const
2008-12-02 12:01:29 +01:00
{
return d->m_widget->fileModel();
2008-12-02 12:01:29 +01:00
}
void VcsBaseSubmitEditor::slotDiffSelectedVcsFiles(const QStringList &rawList)
2008-12-02 12:01:29 +01:00
{
emit diffSelectedFiles(rawList);
2008-12-02 12:01:29 +01:00
}
bool VcsBaseSubmitEditor::save(QString *errorString, const QString &fileName, bool autoSave)
2008-12-02 12:01:29 +01:00
{
const QString fName = fileName.isEmpty() ? d->m_file->fileName() : fileName;
Utils::FileSaver saver(fName, QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text);
saver.write(fileContents());
if (!saver.finalize(errorString))
2008-12-02 12:01:29 +01:00
return false;
if (autoSave)
return true;
2008-12-02 12:01:29 +01:00
const QFileInfo fi(fName);
d->m_file->setFileName(fi.absoluteFilePath());
d->m_file->setModified(false);
2008-12-02 12:01:29 +01:00
return true;
}
QByteArray VcsBaseSubmitEditor::fileContents() const
2008-12-02 12:01:29 +01:00
{
return d->m_widget->descriptionText().toLocal8Bit();
2008-12-02 12:01:29 +01:00
}
bool VcsBaseSubmitEditor::setFileContents(const QString &contents)
2008-12-02 12:01:29 +01:00
{
d->m_widget->setDescriptionText(contents);
2008-12-02 12:01:29 +01:00
return true;
}
enum { checkDialogMinimumWidth = 500 };
VcsBaseSubmitEditor::PromptSubmitResult
VcsBaseSubmitEditor::promptSubmit(const QString &title,
const QString &question,
const QString &checkFailureQuestion,
bool *promptSetting,
bool forcePrompt,
bool canCommitOnFailure) const
{
SubmitEditorWidget *submitWidget =
static_cast<SubmitEditorWidget *>(const_cast<VcsBaseSubmitEditor *>(this)->widget());
raiseSubmitEditor();
QString errorMessage;
QMessageBox::StandardButton answer = QMessageBox::Yes;
const bool prompt = forcePrompt || *promptSetting;
QWidget *parent = Core::ICore::mainWindow();
// Pop up a message depending on whether the check succeeded and the
// user wants to be prompted
bool canCommit = checkSubmitMessage(&errorMessage) && submitWidget->canSubmit();
if (canCommit) {
// Check ok, do prompt?
if (prompt) {
// Provide check box to turn off prompt ONLY if it was not forced
if (*promptSetting && !forcePrompt) {
const QDialogButtonBox::StandardButton danswer =
Utils::CheckableMessageBox::question(parent, title, question,
tr("Prompt to submit"), promptSetting,
QDialogButtonBox::Yes|QDialogButtonBox::No|
QDialogButtonBox::Cancel,
QDialogButtonBox::Yes);
answer = Utils::CheckableMessageBox::dialogButtonBoxToMessageBoxButton(danswer);
} else {
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());
}
if (!canCommit && !canCommitOnFailure) {
switch (answer) {
case QMessageBox::No:
return SubmitDiscarded;
case QMessageBox::Yes:
return SubmitCanceled;
default:
break;
}
} else {
switch (answer) {
case QMessageBox::No:
return SubmitDiscarded;
case QMessageBox::Yes:
return SubmitConfirmed;
default:
break;
}
}
return SubmitCanceled;
}
QString VcsBaseSubmitEditor::promptForNickName()
{
if (!d->m_nickNameDialog)
d->m_nickNameDialog = new NickNameDialog(VcsPlugin::instance()->nickNameModel(), d->m_widget);
if (d->m_nickNameDialog->exec() == QDialog::Accepted)
return d->m_nickNameDialog->nickName();
return QString();
}
void VcsBaseSubmitEditor::slotInsertNickName()
{
const QString nick = promptForNickName();
if (!nick.isEmpty())
d->m_widget->descriptionEdit()->textCursor().insertText(nick);
}
void VcsBaseSubmitEditor::slotSetFieldNickName(int i)
{
if (SubmitFieldWidget *sfw = d->m_widget->submitFieldWidgets().front()) {
const QString nick = promptForNickName();
if (!nick.isEmpty())
sfw->setFieldValue(i, nick);
}
}
void VcsBaseSubmitEditor::slotCheckSubmitMessage()
{
QString errorMessage;
if (!checkSubmitMessage(&errorMessage)) {
2010-09-21 17:17:54 +02:00
QMessageBox msgBox(QMessageBox::Warning, tr("Submit Message Check Failed"),
errorMessage, QMessageBox::Ok, d->m_widget);
msgBox.setMinimumWidth(checkDialogMinimumWidth);
msgBox.exec();
}
}
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;
}
static inline QString msgCheckScript(const QString &workingDir, const QString &cmd)
{
const QString nativeCmd = QDir::toNativeSeparators(cmd);
return workingDir.isEmpty() ?
VcsBaseSubmitEditor::tr("Executing %1").arg(nativeCmd) :
VcsBaseSubmitEditor::tr("Executing [%1] %2").
arg(QDir::toNativeSeparators(workingDir), nativeCmd);
}
bool VcsBaseSubmitEditor::runSubmitMessageCheckScript(const QString &checkScript, QString *errorMessage) const
{
// Write out message
QString tempFilePattern = QDir::tempPath();
if (!tempFilePattern.endsWith(QDir::separator()))
tempFilePattern += QDir::separator();
tempFilePattern += QLatin1String("msgXXXXXX.txt");
TempFileSaver saver(tempFilePattern);
saver.write(fileContents());
if (!saver.finalize(errorMessage))
return false;
// Run check process
VcsBaseOutputWindow *outputWindow = VcsBaseOutputWindow::instance();
outputWindow->appendCommand(msgCheckScript(d->m_checkScriptWorkingDirectory, checkScript));
QProcess checkProcess;
if (!d->m_checkScriptWorkingDirectory.isEmpty())
checkProcess.setWorkingDirectory(d->m_checkScriptWorkingDirectory);
checkProcess.start(checkScript, QStringList(saver.fileName()));
checkProcess.closeWriteChannel();
if (!checkProcess.waitForStarted()) {
*errorMessage = tr("The check script '%1' could not be started: %2").arg(checkScript, checkProcess.errorString());
return false;
}
QByteArray stdOutData;
QByteArray stdErrData;
if (!SynchronousProcess::readDataFromProcess(checkProcess, 30000, &stdOutData, &stdErrData, false)) {
SynchronousProcess::stopProcess(checkProcess);
*errorMessage = tr("The check script '%1' timed out.").
arg(QDir::toNativeSeparators(checkScript));
return false;
}
if (checkProcess.exitStatus() != QProcess::NormalExit) {
*errorMessage = tr("The check script '%1' crashed.").
arg(QDir::toNativeSeparators(checkScript));
return false;
}
if (!stdOutData.isEmpty())
outputWindow->appendSilently(QString::fromLocal8Bit(stdOutData));
const QString stdErr = QString::fromLocal8Bit(stdErrData);
if (!stdErr.isEmpty())
outputWindow->appendSilently(stdErr);
const int exitCode = checkProcess.exitCode();
if (exitCode != 0) {
const QString exMessage = tr("The check script returned exit code %1.").
arg(exitCode);
outputWindow->appendError(exMessage);
*errorMessage = stdErr;
if (errorMessage->isEmpty())
*errorMessage = exMessage;
return false;
}
return true;
}
QIcon VcsBaseSubmitEditor::diffIcon()
{
return QIcon(QLatin1String(":/vcsbase/images/diff.png"));
}
QIcon VcsBaseSubmitEditor::submitIcon()
{
return QIcon(QLatin1String(":/vcsbase/images/submit.png"));
}
// Compile a list if files in the current projects. TODO: Recurse down qrc files?
QStringList VcsBaseSubmitEditor::currentProjectFiles(bool nativeSeparators, QString *name)
{
if (name)
name->clear();
if (const ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectExplorerPlugin::currentProject()) {
QStringList files = currentProject->files(ProjectExplorer::Project::ExcludeGeneratedFiles);
if (name)
*name = currentProject->displayName();
if (nativeSeparators && !files.empty()) {
const QStringList::iterator end = files.end();
for (QStringList::iterator it = files.begin(); it != end; ++it)
*it = QDir::toNativeSeparators(*it);
}
return files;
}
return QStringList();
}
// Reduce a list of untracked files reported by a VCS down to the files
// that are actually part of the current project(s).
void VcsBaseSubmitEditor::filterUntrackedFilesOfProject(const QString &repositoryDirectory, QStringList *untrackedFiles)
{
if (untrackedFiles->empty())
return;
const QStringList nativeProjectFiles = VcsBaseSubmitEditor::currentProjectFiles(true);
if (nativeProjectFiles.empty())
return;
const QDir repoDir(repositoryDirectory);
for (QStringList::iterator it = untrackedFiles->begin(); it != untrackedFiles->end(); ) {
const QString path = QDir::toNativeSeparators(repoDir.absoluteFilePath(*it));
if (nativeProjectFiles.contains(path)) {
++it;
} else {
it = untrackedFiles->erase(it);
}
}
}
// Helper to raise an already open submit editor to prevent opening twice.
bool VcsBaseSubmitEditor::raiseSubmitEditor()
{
// Nothing to do?
if (Core::IEditor *ce = Core::EditorManager::currentEditor())
if (qobject_cast<VcsBaseSubmitEditor*>(ce))
return true;
// Try to activate a hidden one
Core::EditorManager *em = Core::EditorManager::instance();
foreach (Core::IEditor *e, em->openedEditors()) {
if (qobject_cast<VcsBaseSubmitEditor*>(e)) {
Core::EditorManager::activateEditor(e,
Core::EditorManager::IgnoreNavigationHistory | Core::EditorManager::ModeSwitch);
return true;
}
}
return false;
}
} // namespace VcsBase