OSDN Git Service

e1c61438affd893482f1bcbe0fa5a1fa87ad6eea
[qt-creator-jp/qt-creator-jp.git] / src / plugins / vcsbase / vcsbasesubmiteditor.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** No Commercial Usage
10 **
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 **
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights.  These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
31 **
32 **************************************************************************/
33
34 #include "vcsbasesubmiteditor.h"
35
36 #include "commonvcssettings.h"
37 #include "vcsbaseoutputwindow.h"
38 #include "vcsplugin.h"
39 #include "nicknamedialog.h"
40 #include "submiteditorfile.h"
41
42 #include <aggregation/aggregate.h>
43 #include <coreplugin/ifile.h>
44 #include <coreplugin/icore.h>
45 #include <coreplugin/editormanager/editormanager.h>
46 #include <coreplugin/uniqueidmanager.h>
47 #include <coreplugin/actionmanager/actionmanager.h>
48 #include <utils/submiteditorwidget.h>
49 #include <utils/checkablemessagebox.h>
50 #include <utils/synchronousprocess.h>
51 #include <utils/submitfieldwidget.h>
52 #include <find/basetextfind.h>
53 #include <texteditor/fontsettings.h>
54 #include <texteditor/texteditorsettings.h>
55
56 #include <projectexplorer/projectexplorer.h>
57 #include <projectexplorer/session.h>
58 #include <projectexplorer/project.h>
59
60 #include <QtCore/QDebug>
61 #include <QtCore/QDir>
62 #include <QtCore/QTemporaryFile>
63 #include <QtCore/QProcess>
64 #include <QtCore/QFile>
65 #include <QtCore/QFileInfo>
66 #include <QtCore/QPointer>
67 #include <QtCore/QTextStream>
68 #include <QtGui/QStyle>
69 #include <QtGui/QToolBar>
70 #include <QtGui/QAction>
71 #include <QtGui/QApplication>
72 #include <QtGui/QMessageBox>
73 #include <QtGui/QMainWindow>
74 #include <QtGui/QCompleter>
75 #include <QtGui/QLineEdit>
76 #include <QtGui/QTextEdit>
77
78 enum { debug = 0 };
79 enum { wantToolBar = 0 };
80
81 namespace VCSBase {
82
83 static inline QString submitMessageCheckScript()
84 {
85     return Internal::VCSPlugin::instance()->settings().submitMessageCheckScript;
86 }
87
88 struct VCSBaseSubmitEditorPrivate
89 {
90     VCSBaseSubmitEditorPrivate(const VCSBaseSubmitEditorParameters *parameters,
91                                Utils::SubmitEditorWidget *editorWidget,
92                                QObject *q);
93
94     Utils::SubmitEditorWidget *m_widget;
95     QToolBar *m_toolWidget;
96     const VCSBaseSubmitEditorParameters *m_parameters;
97     QString m_displayName;
98     QString m_checkScriptWorkingDirectory;
99     VCSBase::Internal::SubmitEditorFile *m_file;
100
101     QPointer<QAction> m_diffAction;
102     QPointer<QAction> m_submitAction;
103
104     Internal::NickNameDialog *m_nickNameDialog;
105     Core::Context m_contexts;
106 };
107
108 VCSBaseSubmitEditorPrivate::VCSBaseSubmitEditorPrivate(const VCSBaseSubmitEditorParameters *parameters,
109                                                        Utils::SubmitEditorWidget *editorWidget,
110                                                        QObject *q) :
111     m_widget(editorWidget),
112     m_toolWidget(0),
113     m_parameters(parameters),
114     m_file(new VCSBase::Internal::SubmitEditorFile(QLatin1String(parameters->mimeType), q)),
115     m_nickNameDialog(0),
116     m_contexts(parameters->context)
117 {
118 }
119
120 VCSBaseSubmitEditor::VCSBaseSubmitEditor(const VCSBaseSubmitEditorParameters *parameters,
121                                          Utils::SubmitEditorWidget *editorWidget) :
122     m_d(new VCSBaseSubmitEditorPrivate(parameters, editorWidget, this))
123 {
124     // Message font according to settings
125     const TextEditor::FontSettings fs = TextEditor::TextEditorSettings::instance()->fontSettings();
126     QFont font = editorWidget->descriptionEdit()->font();
127     font.setFamily(fs.family());
128     font.setPointSize(fs.fontSize());
129     editorWidget->descriptionEdit()->setFont(font);
130
131     m_d->m_file->setModified(false);
132     // We are always clean to prevent the editor manager from asking to save.
133     connect(m_d->m_file, SIGNAL(saveMe(QString)), this, SLOT(save(QString)));
134
135     connect(m_d->m_widget, SIGNAL(diffSelected(QStringList)), this, SLOT(slotDiffSelectedVCSFiles(QStringList)));
136     connect(m_d->m_widget->descriptionEdit(), SIGNAL(textChanged()), this, SLOT(slotDescriptionChanged()));
137
138     const Internal::CommonVcsSettings settings = Internal::VCSPlugin::instance()->settings();
139     // Add additional context menu settings
140     if (!settings.submitMessageCheckScript.isEmpty() || !settings.nickNameMailMap.isEmpty()) {
141         QAction *sep = new QAction(this);
142         sep->setSeparator(true);
143         m_d->m_widget->addDescriptionEditContextMenuAction(sep);
144         // Run check action
145         if (!settings.submitMessageCheckScript.isEmpty()) {
146             QAction *checkAction = new QAction(tr("Check Message"), this);
147             connect(checkAction, SIGNAL(triggered()), this, SLOT(slotCheckSubmitMessage()));
148             m_d->m_widget->addDescriptionEditContextMenuAction(checkAction);
149         }
150         // Insert nick
151         if (!settings.nickNameMailMap.isEmpty()) {
152             QAction *insertAction = new QAction(tr("Insert Name..."), this);
153             connect(insertAction, SIGNAL(triggered()), this, SLOT(slotInsertNickName()));
154             m_d->m_widget->addDescriptionEditContextMenuAction(insertAction);
155         }
156     }
157     // Do we have user fields?
158     if (!settings.nickNameFieldListFile.isEmpty())
159         createUserFields(settings.nickNameFieldListFile);
160
161     // wrapping. etc
162     slotUpdateEditorSettings(settings);
163     connect(Internal::VCSPlugin::instance(),
164             SIGNAL(settingsChanged(VCSBase::Internal::CommonVcsSettings)),
165             this, SLOT(slotUpdateEditorSettings(VCSBase::Internal::CommonVcsSettings)));
166
167     Aggregation::Aggregate *aggregate = new Aggregation::Aggregate;
168     aggregate->add(new Find::BaseTextFind(m_d->m_widget->descriptionEdit()));
169     aggregate->add(this);
170 }
171
172 VCSBaseSubmitEditor::~VCSBaseSubmitEditor()
173 {
174     delete m_d->m_toolWidget;
175     delete m_d->m_widget;
176     delete m_d;
177 }
178
179 void VCSBaseSubmitEditor::slotUpdateEditorSettings(const Internal::CommonVcsSettings &s)
180 {
181     setLineWrapWidth(s.lineWrapWidth);
182     setLineWrap(s.lineWrap);
183 }
184
185 // Return a trimmed list of non-empty field texts
186 static inline QStringList fieldTexts(const QString &fileContents)
187 {
188     QStringList rc;
189     const QStringList rawFields = fileContents.trimmed().split(QLatin1Char('\n'));
190     foreach(const QString &field, rawFields) {
191         const QString trimmedField = field.trimmed();
192         if (!trimmedField.isEmpty())
193             rc.push_back(trimmedField);
194     }
195     return rc;
196 }
197
198 void VCSBaseSubmitEditor::createUserFields(const QString &fieldConfigFile)
199 {
200     QFile fieldFile(fieldConfigFile);
201     if (!fieldFile.open(QIODevice::ReadOnly|QIODevice::Text)) {
202         qWarning("%s: Unable to open %s: %s", Q_FUNC_INFO, qPrintable(fieldConfigFile), qPrintable(fieldFile.errorString()));
203         return;
204     }
205     // Parse into fields
206     const QStringList fields = fieldTexts(QString::fromUtf8(fieldFile.readAll()));
207     if (fields.empty())
208         return;
209     // Create a completer on user names
210     const QStandardItemModel *nickNameModel = Internal::VCSPlugin::instance()->nickNameModel();
211     QCompleter *completer = new QCompleter(Internal::NickNameDialog::nickNameList(nickNameModel), this);
212
213     Utils::SubmitFieldWidget *fieldWidget = new Utils::SubmitFieldWidget;
214     connect(fieldWidget, SIGNAL(browseButtonClicked(int,QString)),
215             this, SLOT(slotSetFieldNickName(int)));
216     fieldWidget->setCompleter(completer);
217     fieldWidget->setAllowDuplicateFields(true);
218     fieldWidget->setHasBrowseButton(true);
219     fieldWidget->setFields(fields);
220     m_d->m_widget->addSubmitFieldWidget(fieldWidget);
221 }
222
223 void VCSBaseSubmitEditor::registerActions(QAction *editorUndoAction,  QAction *editorRedoAction,
224                                           QAction *submitAction, QAction *diffAction)\
225 {
226     m_d->m_widget->registerActions(editorUndoAction, editorRedoAction, submitAction, diffAction);
227     m_d->m_diffAction = diffAction;
228     m_d->m_submitAction = submitAction;
229 }
230
231 void VCSBaseSubmitEditor::unregisterActions(QAction *editorUndoAction,  QAction *editorRedoAction,
232                            QAction *submitAction, QAction *diffAction)
233 {
234     m_d->m_widget->unregisterActions(editorUndoAction, editorRedoAction, submitAction, diffAction);
235     m_d->m_diffAction = m_d->m_submitAction = 0;
236 }
237
238 int VCSBaseSubmitEditor::fileNameColumn() const
239 {
240     return m_d->m_widget->fileNameColumn();
241 }
242
243 void VCSBaseSubmitEditor::setFileNameColumn(int c)
244 {
245     m_d->m_widget->setFileNameColumn(c);
246 }
247
248 QAbstractItemView::SelectionMode VCSBaseSubmitEditor::fileListSelectionMode() const
249 {
250     return m_d->m_widget->fileListSelectionMode();
251 }
252
253 void VCSBaseSubmitEditor::setFileListSelectionMode(QAbstractItemView::SelectionMode sm)
254 {
255     m_d->m_widget->setFileListSelectionMode(sm);
256 }
257
258 bool VCSBaseSubmitEditor::isEmptyFileListEnabled() const
259 {
260     return m_d->m_widget->isEmptyFileListEnabled();
261 }
262
263 void VCSBaseSubmitEditor::setEmptyFileListEnabled(bool e)
264 {
265     m_d->m_widget->setEmptyFileListEnabled(e);
266 }
267
268 bool VCSBaseSubmitEditor::lineWrap() const
269 {
270     return m_d->m_widget->lineWrap();
271 }
272
273 void VCSBaseSubmitEditor::setLineWrap(bool w)
274 {
275     m_d->m_widget->setLineWrap(w);
276 }
277
278 int VCSBaseSubmitEditor::lineWrapWidth() const
279 {
280     return m_d->m_widget->lineWrapWidth();
281 }
282
283 void VCSBaseSubmitEditor::setLineWrapWidth(int w)
284 {
285     m_d->m_widget->setLineWrapWidth(w);
286 }
287
288 void VCSBaseSubmitEditor::slotDescriptionChanged()
289 {
290 }
291
292 bool VCSBaseSubmitEditor::createNew(const QString &contents)
293 {
294     setFileContents(contents);
295     return true;
296 }
297
298 bool VCSBaseSubmitEditor::open(const QString &fileName)
299 {
300     if (fileName.isEmpty())
301         return false;
302
303     const QFileInfo fi(fileName);
304     if (!fi.isFile() || !fi.isReadable())
305         return false;
306
307     QFile file(fileName);
308     if (!file.open(QIODevice::ReadOnly|QIODevice::Text)) {
309         qWarning("Unable to open %s: %s", qPrintable(fileName), qPrintable(file.errorString()));
310         return false;
311     }
312
313     const QString text = QString::fromLocal8Bit(file.readAll());
314     if (!createNew(text))
315         return false;
316
317     m_d->m_file->setFileName(fi.absoluteFilePath());
318     return true;
319 }
320
321 Core::IFile *VCSBaseSubmitEditor::file()
322 {
323     return m_d->m_file;
324 }
325
326 QString VCSBaseSubmitEditor::displayName() const
327 {
328     if (m_d->m_displayName.isEmpty())
329         m_d->m_displayName = QCoreApplication::translate("VCS", m_d->m_parameters->displayName);
330     return m_d->m_displayName;
331 }
332
333 void VCSBaseSubmitEditor::setDisplayName(const QString &title)
334 {
335     m_d->m_displayName = title;
336     emit changed();
337 }
338
339 QString VCSBaseSubmitEditor::checkScriptWorkingDirectory() const
340 {
341     return m_d->m_checkScriptWorkingDirectory;
342 }
343
344 void VCSBaseSubmitEditor::setCheckScriptWorkingDirectory(const QString &s)
345 {
346     m_d->m_checkScriptWorkingDirectory = s;
347 }
348
349 bool VCSBaseSubmitEditor::duplicateSupported() const
350 {
351     return false;
352 }
353
354 Core::IEditor *VCSBaseSubmitEditor::duplicate(QWidget * /*parent*/)
355 {
356     return 0;
357 }
358
359 QString VCSBaseSubmitEditor::id() const
360 {
361     return m_d->m_parameters->id;
362 }
363
364 static QToolBar *createToolBar(const QWidget *someWidget, QAction *submitAction, QAction *diffAction)
365 {
366     // Create
367     QToolBar *toolBar = new QToolBar;
368     toolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
369     const int size = someWidget->style()->pixelMetric(QStyle::PM_SmallIconSize);
370     toolBar->setIconSize(QSize(size, size));
371     toolBar->addSeparator();
372
373     if (submitAction)
374         toolBar->addAction(submitAction);
375     if (diffAction)
376         toolBar->addAction(diffAction);
377     return toolBar;
378 }
379
380 QWidget *VCSBaseSubmitEditor::toolBar()
381 {
382     if (!wantToolBar)
383         return 0;
384
385     if (m_d->m_toolWidget)
386         return m_d->m_toolWidget;
387
388     if (!m_d->m_diffAction && !m_d->m_submitAction)
389         return 0;
390
391     // Create
392     m_d->m_toolWidget = createToolBar(m_d->m_widget, m_d->m_submitAction, m_d->m_diffAction);
393     return m_d->m_toolWidget;
394 }
395
396 Core::Context VCSBaseSubmitEditor::context() const
397 {
398     return m_d->m_contexts;
399 }
400
401 QWidget *VCSBaseSubmitEditor::widget()
402 {
403     return m_d->m_widget;
404 }
405
406 QByteArray VCSBaseSubmitEditor::saveState() const
407 {
408     return QByteArray();
409 }
410
411 bool VCSBaseSubmitEditor::restoreState(const QByteArray &/*state*/)
412 {
413     return true;
414 }
415
416 QStringList VCSBaseSubmitEditor::checkedFiles() const
417 {
418     return m_d->m_widget->checkedFiles();
419 }
420
421 void VCSBaseSubmitEditor::setFileModel(QAbstractItemModel *m)
422 {
423     m_d->m_widget->setFileModel(m);
424 }
425
426 QAbstractItemModel *VCSBaseSubmitEditor::fileModel() const
427 {
428     return m_d->m_widget->fileModel();
429 }
430
431 void VCSBaseSubmitEditor::slotDiffSelectedVCSFiles(const QStringList &rawList)
432 {
433      emit diffSelectedFiles(rawList);
434 }
435
436 bool VCSBaseSubmitEditor::save(const QString &fileName)
437 {
438     const QString fName = fileName.isEmpty() ? m_d->m_file->fileName() : fileName;
439     QFile file(fName);
440     if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) {
441         qWarning("Unable to open %s: %s", qPrintable(fName), qPrintable(file.errorString()));
442         return false;
443     }
444     file.write(fileContents().toLocal8Bit());
445     if (!file.flush())
446         return false;
447     file.close();
448     const QFileInfo fi(fName);
449     m_d->m_file->setFileName(fi.absoluteFilePath());
450     m_d->m_file->setModified(false);
451     return true;
452 }
453
454 QString VCSBaseSubmitEditor::fileContents() const
455 {
456     return m_d->m_widget->descriptionText();
457 }
458
459 bool VCSBaseSubmitEditor::setFileContents(const QString &contents)
460 {
461     m_d->m_widget->setDescriptionText(contents);
462     return true;
463 }
464
465 enum { checkDialogMinimumWidth = 500 };
466
467 VCSBaseSubmitEditor::PromptSubmitResult
468         VCSBaseSubmitEditor::promptSubmit(const QString &title,
469                                           const QString &question,
470                                           const QString &checkFailureQuestion,
471                                           bool *promptSetting,
472                                           bool forcePrompt,
473                                           bool canCommitOnFailure) const
474 {
475     Utils::SubmitEditorWidget *submitWidget =
476             static_cast<Utils::SubmitEditorWidget *>(const_cast<VCSBaseSubmitEditor *>(this)->widget());
477
478     raiseSubmitEditor();
479
480     QString errorMessage;
481     QMessageBox::StandardButton answer = QMessageBox::Yes;
482
483     const bool prompt = forcePrompt || *promptSetting;
484
485     QWidget *parent = Core::ICore::instance()->mainWindow();
486     // Pop up a message depending on whether the check succeeded and the
487     // user wants to be prompted
488     bool canCommit = checkSubmitMessage(&errorMessage) && submitWidget->canSubmit();
489     if (canCommit) {
490         // Check ok, do prompt?
491         if (prompt) {
492             // Provide check box to turn off prompt ONLY if it was not forced
493             if (*promptSetting && !forcePrompt) {
494                 const QDialogButtonBox::StandardButton danswer =
495                         Utils::CheckableMessageBox::question(parent, title, question,
496                                                                    tr("Prompt to submit"), promptSetting,
497                                                                    QDialogButtonBox::Yes|QDialogButtonBox::No|
498                                                                    QDialogButtonBox::Cancel,
499                                                                    QDialogButtonBox::Yes);
500                 answer = Utils::CheckableMessageBox::dialogButtonBoxToMessageBoxButton(danswer);
501             } else {
502                 answer = QMessageBox::question(parent, title, question,
503                                                QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel,
504                                                QMessageBox::Yes);
505             }
506         }
507     } else {
508         // Check failed.
509         QMessageBox msgBox(QMessageBox::Question, title, checkFailureQuestion,
510                            QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel, parent);
511         msgBox.setDefaultButton(QMessageBox::Cancel);
512         msgBox.setInformativeText(errorMessage);
513         msgBox.setMinimumWidth(checkDialogMinimumWidth);
514         answer = static_cast<QMessageBox::StandardButton>(msgBox.exec());
515     }
516     if (!canCommit && !canCommitOnFailure) {
517         switch (answer) {
518         case QMessageBox::No:
519                 return SubmitDiscarded;
520         case QMessageBox::Yes:
521                 return SubmitCanceled;
522         default:
523             break;
524         }
525     } else {
526         switch (answer) {
527         case QMessageBox::No:
528             return SubmitDiscarded;
529         case QMessageBox::Yes:
530             return SubmitConfirmed;
531         default:
532             break;
533         }
534     }
535
536     return SubmitCanceled;
537 }
538
539 QString VCSBaseSubmitEditor::promptForNickName()
540 {
541     if (!m_d->m_nickNameDialog)
542         m_d->m_nickNameDialog = new Internal::NickNameDialog(Internal::VCSPlugin::instance()->nickNameModel(), m_d->m_widget);
543     if (m_d->m_nickNameDialog->exec() == QDialog::Accepted)
544        return m_d->m_nickNameDialog->nickName();
545     return QString();
546 }
547
548 void VCSBaseSubmitEditor::slotInsertNickName()
549 {
550     const QString nick = promptForNickName();
551     if (!nick.isEmpty())
552         m_d->m_widget->descriptionEdit()->textCursor().insertText(nick);
553 }
554
555 void VCSBaseSubmitEditor::slotSetFieldNickName(int i)
556 {
557     if (Utils::SubmitFieldWidget *sfw  =m_d->m_widget->submitFieldWidgets().front()) {
558         const QString nick = promptForNickName();
559         if (!nick.isEmpty())
560             sfw->setFieldValue(i, nick);
561     }
562 }
563
564 void VCSBaseSubmitEditor::slotCheckSubmitMessage()
565 {
566     QString errorMessage;
567     if (!checkSubmitMessage(&errorMessage)) {
568         QMessageBox msgBox(QMessageBox::Warning, tr("Submit Message Check Failed"),
569                            errorMessage, QMessageBox::Ok, m_d->m_widget);
570         msgBox.setMinimumWidth(checkDialogMinimumWidth);
571         msgBox.exec();
572     }
573 }
574
575 bool VCSBaseSubmitEditor::checkSubmitMessage(QString *errorMessage) const
576 {
577     const QString checkScript = submitMessageCheckScript();
578     if (checkScript.isEmpty())
579         return true;
580     QApplication::setOverrideCursor(Qt::WaitCursor);
581     const bool rc = runSubmitMessageCheckScript(checkScript, errorMessage);
582     QApplication::restoreOverrideCursor();
583     return rc;
584 }
585
586 static inline QString msgCheckScript(const QString &workingDir, const QString &cmd)
587 {
588     const QString nativeCmd = QDir::toNativeSeparators(cmd);
589     return workingDir.isEmpty() ?
590            VCSBaseSubmitEditor::tr("Executing %1").arg(nativeCmd) :
591            VCSBaseSubmitEditor::tr("Executing [%1] %2").
592            arg(QDir::toNativeSeparators(workingDir), nativeCmd);
593 }
594
595 bool VCSBaseSubmitEditor::runSubmitMessageCheckScript(const QString &checkScript, QString *errorMessage) const
596 {
597     // Write out message
598     QString tempFilePattern = QDir::tempPath();
599     if (!tempFilePattern.endsWith(QDir::separator()))
600         tempFilePattern += QDir::separator();
601     tempFilePattern += QLatin1String("msgXXXXXX.txt");
602     QTemporaryFile messageFile(tempFilePattern);
603     messageFile.setAutoRemove(true);
604     if (!messageFile.open()) {
605         *errorMessage = tr("Unable to open '%1': %2").
606                         arg(QDir::toNativeSeparators(messageFile.fileName()),
607                             messageFile.errorString());
608         return false;
609     }
610     const QString messageFileName = messageFile.fileName();
611     messageFile.write(fileContents().toUtf8());
612     messageFile.close();
613     // Run check process
614     VCSBaseOutputWindow *outputWindow = VCSBaseOutputWindow::instance();
615     outputWindow->appendCommand(msgCheckScript(m_d->m_checkScriptWorkingDirectory, checkScript));
616     QProcess checkProcess;
617     if (!m_d->m_checkScriptWorkingDirectory.isEmpty())
618         checkProcess.setWorkingDirectory(m_d->m_checkScriptWorkingDirectory);
619     checkProcess.start(checkScript, QStringList(messageFileName));
620     checkProcess.closeWriteChannel();
621     if (!checkProcess.waitForStarted()) {
622         *errorMessage = tr("The check script '%1' could not be started: %2").arg(checkScript, checkProcess.errorString());
623         return false;
624     }
625     QByteArray stdOutData;
626     QByteArray stdErrData;
627     if (!Utils::SynchronousProcess::readDataFromProcess(checkProcess, 30000, &stdOutData, &stdErrData, false)) {
628         Utils::SynchronousProcess::stopProcess(checkProcess);
629         *errorMessage = tr("The check script '%1' timed out.").
630                         arg(QDir::toNativeSeparators(checkScript));
631         return false;
632     }
633     if (checkProcess.exitStatus() != QProcess::NormalExit) {
634         *errorMessage = tr("The check script '%1' crashed").
635                         arg(QDir::toNativeSeparators(checkScript));
636         return false;
637     }
638     if (!stdOutData.isEmpty())
639         outputWindow->appendSilently(QString::fromLocal8Bit(stdOutData));
640     const QString stdErr = QString::fromLocal8Bit(stdErrData);
641     if (!stdErr.isEmpty())
642         outputWindow->appendSilently(stdErr);
643     const int exitCode = checkProcess.exitCode();
644     if (exitCode != 0) {
645         const QString exMessage = tr("The check script returned exit code %1.").
646                                   arg(exitCode);
647         outputWindow->appendError(exMessage);
648         *errorMessage = stdErr;
649         if (errorMessage->isEmpty())
650             *errorMessage = exMessage;
651         return false;
652     }
653     return true;
654 }
655
656 QIcon VCSBaseSubmitEditor::diffIcon()
657 {
658     return QIcon(QLatin1String(":/vcsbase/images/diff.png"));
659 }
660
661 QIcon VCSBaseSubmitEditor::submitIcon()
662 {
663     return QIcon(QLatin1String(":/vcsbase/images/submit.png"));
664 }
665
666 // Compile a list if files in the current projects. TODO: Recurse down qrc files?
667 QStringList VCSBaseSubmitEditor::currentProjectFiles(bool nativeSeparators, QString *name)
668 {
669     if (name)
670         name->clear();
671
672     if (ProjectExplorer::ProjectExplorerPlugin *pe = ProjectExplorer::ProjectExplorerPlugin::instance()) {
673         if (const ProjectExplorer::Project *currentProject = pe->currentProject()) {
674             QStringList files = currentProject->files(ProjectExplorer::Project::ExcludeGeneratedFiles);
675             if (name)
676                 *name = currentProject->displayName();
677             if (nativeSeparators && !files.empty()) {
678                 const QStringList::iterator end = files.end();
679                 for (QStringList::iterator it = files.begin(); it != end; ++it)
680                     *it = QDir::toNativeSeparators(*it);
681             }
682             return files;
683         }
684     }
685     return QStringList();
686 }
687
688 // Reduce a list of untracked files reported by a VCS down to the files
689 // that are actually part of the current project(s).
690 void VCSBaseSubmitEditor::filterUntrackedFilesOfProject(const QString &repositoryDirectory, QStringList *untrackedFiles)
691 {
692     if (untrackedFiles->empty())
693         return;
694     const QStringList nativeProjectFiles = VCSBase::VCSBaseSubmitEditor::currentProjectFiles(true);
695     if (nativeProjectFiles.empty())
696         return;
697     const QDir repoDir(repositoryDirectory);
698     for (QStringList::iterator it = untrackedFiles->begin(); it != untrackedFiles->end(); ) {
699         const QString path = QDir::toNativeSeparators(repoDir.absoluteFilePath(*it));
700         if (nativeProjectFiles.contains(path)) {
701             ++it;
702         } else {
703             it = untrackedFiles->erase(it);
704         }
705     }
706 }
707
708 // Helper to raise an already open submit editor to prevent opening twice.
709 bool VCSBaseSubmitEditor::raiseSubmitEditor()
710 {
711     Core::EditorManager *em = Core::EditorManager::instance();
712     // Nothing to do?
713     if (Core::IEditor *ce = em->currentEditor())
714         if (qobject_cast<VCSBaseSubmitEditor*>(ce))
715             return true;
716     // Try to activate a hidden one
717     foreach (Core::IEditor *e, em->openedEditors()) {
718         if (qobject_cast<VCSBaseSubmitEditor*>(e)) {
719             em->activateEditor(e, Core::EditorManager::IgnoreNavigationHistory | Core::EditorManager::ModeSwitch);
720             return true;
721         }
722     }
723     return false;
724 }
725
726 } // namespace VCSBase