subversionplugin.cpp 53.1 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
3
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
con's avatar
con committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
con's avatar
con committed
7
**
hjk's avatar
hjk committed
8 9 10 11 12 13 14
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
15
**
16
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17 18 19 20 21 22 23 24 25
** 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.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
con's avatar
con committed
26 27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
hjk's avatar
hjk committed
29

con's avatar
con committed
30 31 32 33 34 35
#include "subversionplugin.h"

#include "settingspage.h"
#include "subversioneditor.h"

#include "subversionsubmiteditor.h"
36
#include "subversionclient.h"
con's avatar
con committed
37 38
#include "subversionconstants.h"
#include "subversioncontrol.h"
39
#include "checkoutwizard.h"
con's avatar
con committed
40 41

#include <vcsbase/basevcseditorfactory.h>
42
#include <vcsbase/command.h>
con's avatar
con committed
43 44
#include <vcsbase/vcsbaseeditor.h>
#include <vcsbase/basevcssubmiteditorfactory.h>
45
#include <vcsbase/vcsbaseconstants.h>
46
#include <vcsbase/vcsbaseoutputwindow.h>
47
#include <vcsbase/vcsbaseeditorparameterwidget.h>
con's avatar
con committed
48

49
#include <coreplugin/actionmanager/actioncontainer.h>
50
#include <coreplugin/actionmanager/actionmanager.h>
51
#include <coreplugin/actionmanager/command.h>
52 53
#include <coreplugin/coreconstants.h>
#include <coreplugin/documentmanager.h>
con's avatar
con committed
54
#include <coreplugin/editormanager/editormanager.h>
55 56
#include <coreplugin/icore.h>
#include <coreplugin/id.h>
57
#include <coreplugin/locator/commandlocator.h>
58 59
#include <coreplugin/messagemanager.h>
#include <coreplugin/mimedatabase.h>
Friedemann Kleint's avatar
Friedemann Kleint committed
60

61 62 63
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
#include <utils/parameteraction.h>
hjk's avatar
hjk committed
64
#include <utils/qtcassert.h>
65
#include <utils/synchronousprocess.h>
con's avatar
con committed
66

67 68 69 70 71 72 73 74 75 76 77 78 79
#include <QDebug>
#include <QDir>
#include <QFileInfo>
#include <QTextCodec>
#include <QtPlugin>
#include <QProcessEnvironment>
#include <QUrl>
#include <QXmlStreamReader>
#include <QAction>
#include <QFileDialog>
#include <QMenu>
#include <QMessageBox>
#include <QInputDialog>
80

81
#include <limits.h>
con's avatar
con committed
82

83 84 85 86
#ifdef WITH_TESTS
#include <QTest>
#endif

hjk's avatar
hjk committed
87 88 89 90
using namespace Core;
using namespace Utils;
using namespace VcsBase;

91 92
namespace Subversion {
namespace Internal {
con's avatar
con committed
93

94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
const char CMD_ID_SUBVERSION_MENU[]    = "Subversion.Menu";
const char CMD_ID_ADD[]                = "Subversion.Add";
const char CMD_ID_DELETE_FILE[]        = "Subversion.Delete";
const char CMD_ID_REVERT[]             = "Subversion.Revert";
const char CMD_ID_DIFF_PROJECT[]       = "Subversion.DiffAll";
const char CMD_ID_DIFF_CURRENT[]       = "Subversion.DiffCurrent";
const char CMD_ID_COMMIT_ALL[]         = "Subversion.CommitAll";
const char CMD_ID_REVERT_ALL[]         = "Subversion.RevertAll";
const char CMD_ID_COMMIT_CURRENT[]     = "Subversion.CommitCurrent";
const char CMD_ID_FILELOG_CURRENT[]    = "Subversion.FilelogCurrent";
const char CMD_ID_ANNOTATE_CURRENT[]   = "Subversion.AnnotateCurrent";
const char CMD_ID_STATUS[]             = "Subversion.Status";
const char CMD_ID_PROJECTLOG[]         = "Subversion.ProjectLog";
const char CMD_ID_REPOSITORYLOG[]      = "Subversion.RepositoryLog";
const char CMD_ID_REPOSITORYUPDATE[]   = "Subversion.RepositoryUpdate";
const char CMD_ID_REPOSITORYDIFF[]     = "Subversion.RepositoryDiff";
const char CMD_ID_REPOSITORYSTATUS[]   = "Subversion.RepositoryStatus";
const char CMD_ID_UPDATE[]             = "Subversion.Update";
const char CMD_ID_COMMIT_PROJECT[]     = "Subversion.CommitProject";
const char CMD_ID_DESCRIBE[]           = "Subversion.Describe";

const char SUBVERSION_SUBMIT_MIMETYPE[] = "text/vnd.qtcreator.svn.submit";
const char SUBVERSIONCOMMITEDITOR[]     = "Subversion Commit Editor";
const char SUBVERSIONCOMMITEDITOR_ID[]  = "Subversion Commit Editor";
const char SUBVERSIONCOMMITEDITOR_DISPLAY_NAME[]  = QT_TRANSLATE_NOOP("VCS", "Subversion Commit Editor");
const char SUBMIT_CURRENT[]             = "Subversion.SubmitCurrentLog";
const char DIFF_SELECTED[]              = "Subversion.DiffSelectedFilesInLog";

const VcsBaseEditorParameters editorParameters[] = {
con's avatar
con committed
123
{
hjk's avatar
hjk committed
124
    LogOutput,
125 126
    "Subversion File Log Editor",   // id
    QT_TRANSLATE_NOOP("VCS", "Subversion File Log Editor"),   // display_name
127
    "Subversion File Log Editor",   // context
Orgad Shaneh's avatar
Orgad Shaneh committed
128
    "text/vnd.qtcreator.svn.log"},
hjk's avatar
hjk committed
129
{    AnnotateOutput,
130 131
    "Subversion Annotation Editor",  // id
    QT_TRANSLATE_NOOP("VCS", "Subversion Annotation Editor"),   // display_name
132
    "Subversion Annotation Editor",  // context
Orgad Shaneh's avatar
Orgad Shaneh committed
133
    "text/vnd.qtcreator.svn.annotation"},
hjk's avatar
hjk committed
134
{   DiffOutput,
135 136
    "Subversion Diff Editor",  // id
    QT_TRANSLATE_NOOP("VCS", "Subversion Diff Editor"),   // display_name
137
    "Subversion Diff Editor",  // context
138
    "text/x-patch"}
con's avatar
con committed
139 140 141
};

// Utility to find a parameter set by type
hjk's avatar
hjk committed
142
static const VcsBaseEditorParameters *findType(int ie)
con's avatar
con committed
143
{
hjk's avatar
hjk committed
144 145
    const EditorContentType et = static_cast<EditorContentType>(ie);
    return VcsBaseEditorWidget::findType(editorParameters, sizeof(editorParameters)/sizeof(editorParameters[0]), et);
con's avatar
con committed
146 147 148 149
}

static inline QString debugCodec(const QTextCodec *c)
{
150
    return c ? QString::fromLatin1(c->name()) : QString::fromLatin1("Null codec");
con's avatar
con committed
151 152
}

153 154 155 156 157 158 159 160 161 162 163 164 165 166
// Parse "svn status" output for added/modified/deleted files
// "M<7blanks>file"
typedef QList<SubversionSubmitEditor::StatusFilePair> StatusList;

StatusList parseStatusOutput(const QString &output)
{
    StatusList changeSet;
    const QString newLine = QString(QLatin1Char('\n'));
    const QStringList list = output.split(newLine, QString::SkipEmptyParts);
    foreach (const QString &l, list) {
        const QString line =l.trimmed();
        if (line.size() > 8) {
            const QChar state = line.at(0);
            if (state == QLatin1Char('A') || state == QLatin1Char('D') || state == QLatin1Char('M')) {
167 168
                const QString fileName = line.mid(7); // Column 8 starting from svn 1.6
                changeSet.push_back(SubversionSubmitEditor::StatusFilePair(QString(state), fileName.trimmed()));
169 170 171 172 173 174 175
            }

        }
    }
    return changeSet;
}

176 177 178 179
// Return a list of names for the internal svn directories
static inline QStringList svnDirectories()
{
    QStringList rc(QLatin1String(".svn"));
hjk's avatar
hjk committed
180
    if (HostOsInfo::isWindowsHost())
181 182
        // Option on Windows systems to avoid hassle with some IDEs
        rc.push_back(QLatin1String("_svn"));
183 184 185
    return rc;
}

con's avatar
con committed
186 187 188 189
// ------------- SubversionPlugin
SubversionPlugin *SubversionPlugin::m_subversionPluginInstance = 0;

SubversionPlugin::SubversionPlugin() :
190
    m_svnDirectories(svnDirectories()),
Friedemann Kleint's avatar
Friedemann Kleint committed
191
    m_commandLocator(0),
con's avatar
con committed
192 193 194 195 196
    m_addAction(0),
    m_deleteAction(0),
    m_revertAction(0),
    m_diffProjectAction(0),
    m_diffCurrentAction(0),
197 198
    m_logProjectAction(0),
    m_logRepositoryAction(0),
con's avatar
con committed
199
    m_commitAllAction(0),
200
    m_revertRepositoryAction(0),
201 202 203
    m_diffRepositoryAction(0),
    m_statusRepositoryAction(0),
    m_updateRepositoryAction(0),
con's avatar
con committed
204 205 206
    m_commitCurrentAction(0),
    m_filelogCurrentAction(0),
    m_annotateCurrentAction(0),
207
    m_statusProjectAction(0),
con's avatar
con committed
208
    m_updateProjectAction(0),
209
    m_commitProjectAction(0),
210
    m_describeAction(0),
con's avatar
con committed
211 212 213
    m_submitCurrentLogAction(0),
    m_submitDiffAction(0),
    m_submitUndoAction(0),
214
    m_submitRedoAction(0),
215
    m_menuAction(0),
216
    m_submitActionTriggered(false)
con's avatar
con committed
217 218 219 220 221
{
}

SubversionPlugin::~SubversionPlugin()
{
222
    delete m_client;
223
    cleanCommitMessageFile();
con's avatar
con committed
224 225
}

226
void SubversionPlugin::cleanCommitMessageFile()
con's avatar
con committed
227
{
228 229 230
    if (!m_commitMessageFileName.isEmpty()) {
        QFile::remove(m_commitMessageFileName);
        m_commitMessageFileName.clear();
231
        m_commitRepository.clear();
con's avatar
con committed
232 233 234
    }
}

235 236 237 238 239
bool SubversionPlugin::isCommitEditorOpen() const
{
    return !m_commitMessageFileName.isEmpty();
}

hjk's avatar
hjk committed
240
const VcsBaseSubmitEditorParameters submitParameters = {
241 242 243 244
    SUBVERSION_SUBMIT_MIMETYPE,
    SUBVERSIONCOMMITEDITOR_ID,
    SUBVERSIONCOMMITEDITOR_DISPLAY_NAME,
    SUBVERSIONCOMMITEDITOR,
hjk's avatar
hjk committed
245
    VcsBaseSubmitEditorParameters::DiffFiles
con's avatar
con committed
246 247
};

248
bool SubversionPlugin::initialize(const QStringList & /*arguments */, QString *errorMessage)
con's avatar
con committed
249
{
hjk's avatar
hjk committed
250
    typedef VcsSubmitEditorFactory<SubversionSubmitEditor> SubversionSubmitEditorFactory;
con's avatar
con committed
251 252 253 254 255
    using namespace Constants;

    using namespace Core::Constants;
    using namespace ExtensionSystem;

256
    initializeVcs(new SubversionControl(this));
257

con's avatar
con committed
258 259
    m_subversionPluginInstance = this;

hjk's avatar
hjk committed
260
    if (!MimeDatabase::addMimeTypes(QLatin1String(":/trolltech.subversion/Subversion.mimetypes.xml"), errorMessage))
con's avatar
con committed
261 262
        return false;

hjk's avatar
hjk committed
263
    m_settings.readSettings(ICore::settings());
264
    m_client = new SubversionClient(&m_settings);
con's avatar
con committed
265

266
    addAutoReleasedObject(new SettingsPage);
con's avatar
con committed
267

268
    addAutoReleasedObject(new SubversionSubmitEditorFactory(&submitParameters));
con's avatar
con committed
269 270

    static const char *describeSlot = SLOT(describe(QString,QString));
hjk's avatar
hjk committed
271
    const int editorCount = sizeof(editorParameters) / sizeof(editorParameters[0]);
272
    const auto widgetCreator = []() { return new SubversionEditor; };
273
    for (int i = 0; i < editorCount; i++)
274
        addAutoReleasedObject(new VcsEditorFactory(editorParameters + i, widgetCreator, this, describeSlot));
con's avatar
con committed
275

276 277 278 279 280 281 282 283 284
    auto checkoutWizardFactory = new BaseCheckoutWizardFactory;
    checkoutWizardFactory->setId(QLatin1String(VcsBase::Constants::VCS_ID_SUBVERSION));
    checkoutWizardFactory->setIcon(QIcon(QLatin1String(":/subversion/images/subversion.png")));
    checkoutWizardFactory->setDescription(tr("Checks out a Subversion repository and tries to load the contained project."));
    checkoutWizardFactory->setDisplayName(tr("Subversion Checkout"));
    checkoutWizardFactory->setWizardCreator([this] (const FileName &path, QWidget *parent) {
        return new CheckoutWizard(path, parent);
    });
    addAutoReleasedObject(checkoutWizardFactory);
285

Friedemann Kleint's avatar
Friedemann Kleint committed
286
    const QString prefix = QLatin1String("svn");
hjk's avatar
hjk committed
287
    m_commandLocator = new CommandLocator("Subversion", prefix, prefix);
Friedemann Kleint's avatar
Friedemann Kleint committed
288 289
    addAutoReleasedObject(m_commandLocator);

hjk's avatar
hjk committed
290 291
    // Register actions
    ActionContainer *toolsContainer = ActionManager::actionContainer(M_TOOLS);
con's avatar
con committed
292

hjk's avatar
hjk committed
293
    ActionContainer *subversionMenu = ActionManager::createMenu(Id(CMD_ID_SUBVERSION_MENU));
con's avatar
con committed
294 295
    subversionMenu->menu()->setTitle(tr("&Subversion"));
    toolsContainer->addMenu(subversionMenu);
296
    m_menuAction = subversionMenu->menu()->menuAction();
hjk's avatar
hjk committed
297
    Context globalcontext(C_GLOBAL);
con's avatar
con committed
298
    Core::Command *command;
299

hjk's avatar
hjk committed
300 301
    m_diffCurrentAction = new ParameterAction(tr("Diff Current File"), tr("Diff \"%1\""), ParameterAction::EnabledWithParameter, this);
    command = ActionManager::registerAction(m_diffCurrentAction,
302
        CMD_ID_DIFF_CURRENT, globalcontext);
con's avatar
con committed
303
    command->setAttribute(Core::Command::CA_UpdateText);
hjk's avatar
hjk committed
304
    command->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+S,Meta+D") : tr("Alt+S,Alt+D")));
305
    connect(m_diffCurrentAction, SIGNAL(triggered()), this, SLOT(diffCurrentFile()));
con's avatar
con committed
306
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
307
    m_commandLocator->appendCommand(command);
con's avatar
con committed
308

hjk's avatar
hjk committed
309 310
    m_filelogCurrentAction = new ParameterAction(tr("Filelog Current File"), tr("Filelog \"%1\""), ParameterAction::EnabledWithParameter, this);
    command = ActionManager::registerAction(m_filelogCurrentAction,
311
        CMD_ID_FILELOG_CURRENT, globalcontext);
con's avatar
con committed
312
    command->setAttribute(Core::Command::CA_UpdateText);
313 314
    connect(m_filelogCurrentAction, SIGNAL(triggered()), this,
        SLOT(filelogCurrentFile()));
con's avatar
con committed
315
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
316
    m_commandLocator->appendCommand(command);
con's avatar
con committed
317

hjk's avatar
hjk committed
318 319
    m_annotateCurrentAction = new ParameterAction(tr("Annotate Current File"), tr("Annotate \"%1\""), ParameterAction::EnabledWithParameter, this);
    command = ActionManager::registerAction(m_annotateCurrentAction,
320
        CMD_ID_ANNOTATE_CURRENT, globalcontext);
con's avatar
con committed
321
    command->setAttribute(Core::Command::CA_UpdateText);
322 323
    connect(m_annotateCurrentAction, SIGNAL(triggered()), this,
        SLOT(annotateCurrentFile()));
con's avatar
con committed
324
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
325
    m_commandLocator->appendCommand(command);
con's avatar
con committed
326

327
    subversionMenu->addSeparator(globalcontext);
con's avatar
con committed
328

hjk's avatar
hjk committed
329 330
    m_addAction = new ParameterAction(tr("Add"), tr("Add \"%1\""), ParameterAction::EnabledWithParameter, this);
    command = ActionManager::registerAction(m_addAction, CMD_ID_ADD,
con's avatar
con committed
331
        globalcontext);
332
    command->setAttribute(Core::Command::CA_UpdateText);
hjk's avatar
hjk committed
333
    command->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+S,Meta+A") : tr("Alt+S,Alt+A")));
334
    connect(m_addAction, SIGNAL(triggered()), this, SLOT(addCurrentFile()));
con's avatar
con committed
335
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
336
    m_commandLocator->appendCommand(command);
con's avatar
con committed
337

hjk's avatar
hjk committed
338 339
    m_commitCurrentAction = new ParameterAction(tr("Commit Current File"), tr("Commit \"%1\""), ParameterAction::EnabledWithParameter, this);
    command = ActionManager::registerAction(m_commitCurrentAction,
340
        CMD_ID_COMMIT_CURRENT, globalcontext);
con's avatar
con committed
341
    command->setAttribute(Core::Command::CA_UpdateText);
hjk's avatar
hjk committed
342
    command->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+S,Meta+C") : tr("Alt+S,Alt+C")));
con's avatar
con committed
343 344
    connect(m_commitCurrentAction, SIGNAL(triggered()), this, SLOT(startCommitCurrentFile()));
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
345
    m_commandLocator->appendCommand(command);
con's avatar
con committed
346

hjk's avatar
hjk committed
347 348
    m_deleteAction = new ParameterAction(tr("Delete..."), tr("Delete \"%1\"..."), ParameterAction::EnabledWithParameter, this);
    command = ActionManager::registerAction(m_deleteAction, CMD_ID_DELETE_FILE,
349
        globalcontext);
con's avatar
con committed
350
    command->setAttribute(Core::Command::CA_UpdateText);
351
    connect(m_deleteAction, SIGNAL(triggered()), this, SLOT(promptToDeleteCurrentFile()));
con's avatar
con committed
352
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
353
    m_commandLocator->appendCommand(command);
con's avatar
con committed
354

hjk's avatar
hjk committed
355 356
    m_revertAction = new ParameterAction(tr("Revert..."), tr("Revert \"%1\"..."), ParameterAction::EnabledWithParameter, this);
    command = ActionManager::registerAction(m_revertAction, CMD_ID_REVERT,
357
        globalcontext);
con's avatar
con committed
358
    command->setAttribute(Core::Command::CA_UpdateText);
359
    connect(m_revertAction, SIGNAL(triggered()), this, SLOT(revertCurrentFile()));
con's avatar
con committed
360
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
361
    m_commandLocator->appendCommand(command);
con's avatar
con committed
362

363
    subversionMenu->addSeparator(globalcontext);
364

hjk's avatar
hjk committed
365 366
    m_diffProjectAction = new ParameterAction(tr("Diff Project"), tr("Diff Project \"%1\""), ParameterAction::EnabledWithParameter, this);
    command = ActionManager::registerAction(m_diffProjectAction, CMD_ID_DIFF_PROJECT,
367 368 369 370
        globalcontext);
    command->setAttribute(Core::Command::CA_UpdateText);
    connect(m_diffProjectAction, SIGNAL(triggered()), this, SLOT(diffProject()));
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
371
    m_commandLocator->appendCommand(command);
con's avatar
con committed
372

hjk's avatar
hjk committed
373 374
    m_statusProjectAction = new ParameterAction(tr("Project Status"), tr("Status of Project \"%1\""), ParameterAction::EnabledWithParameter, this);
    command = ActionManager::registerAction(m_statusProjectAction, CMD_ID_STATUS,
con's avatar
con committed
375
        globalcontext);
376 377
    command->setAttribute(Core::Command::CA_UpdateText);
    connect(m_statusProjectAction, SIGNAL(triggered()), this, SLOT(projectStatus()));
con's avatar
con committed
378
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
379
    m_commandLocator->appendCommand(command);
con's avatar
con committed
380

hjk's avatar
hjk committed
381 382
    m_logProjectAction = new ParameterAction(tr("Log Project"), tr("Log Project \"%1\""), ParameterAction::EnabledWithParameter, this);
    command = ActionManager::registerAction(m_logProjectAction, CMD_ID_PROJECTLOG, globalcontext);
383 384 385
    command->setAttribute(Core::Command::CA_UpdateText);
    connect(m_logProjectAction, SIGNAL(triggered()), this, SLOT(logProject()));
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
386
    m_commandLocator->appendCommand(command);
387

hjk's avatar
hjk committed
388 389
    m_updateProjectAction = new ParameterAction(tr("Update Project"), tr("Update Project \"%1\""), ParameterAction::EnabledWithParameter, this);
    command = ActionManager::registerAction(m_updateProjectAction, CMD_ID_UPDATE, globalcontext);
con's avatar
con committed
390
    connect(m_updateProjectAction, SIGNAL(triggered()), this, SLOT(updateProject()));
391
    command->setAttribute(Core::Command::CA_UpdateText);
con's avatar
con committed
392
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
393
    m_commandLocator->appendCommand(command);
con's avatar
con committed
394

hjk's avatar
hjk committed
395 396
    m_commitProjectAction = new ParameterAction(tr("Commit Project"), tr("Commit Project \"%1\""), ParameterAction::EnabledWithParameter, this);
    command = ActionManager::registerAction(m_commitProjectAction, CMD_ID_COMMIT_PROJECT, globalcontext);
397 398 399 400 401
    connect(m_commitProjectAction, SIGNAL(triggered()), this, SLOT(startCommitProject()));
    command->setAttribute(Core::Command::CA_UpdateText);
    subversionMenu->addAction(command);
    m_commandLocator->appendCommand(command);

402
    subversionMenu->addSeparator(globalcontext);
403

404
    m_diffRepositoryAction = new QAction(tr("Diff Repository"), this);
hjk's avatar
hjk committed
405
    command = ActionManager::registerAction(m_diffRepositoryAction, CMD_ID_REPOSITORYDIFF, globalcontext);
406 407 408 409 410
    connect(m_diffRepositoryAction, SIGNAL(triggered()), this, SLOT(diffRepository()));
    subversionMenu->addAction(command);
    m_commandLocator->appendCommand(command);

    m_statusRepositoryAction = new QAction(tr("Repository Status"), this);
hjk's avatar
hjk committed
411
    command = ActionManager::registerAction(m_statusRepositoryAction, CMD_ID_REPOSITORYSTATUS, globalcontext);
412 413 414 415 416
    connect(m_statusRepositoryAction, SIGNAL(triggered()), this, SLOT(statusRepository()));
    subversionMenu->addAction(command);
    m_commandLocator->appendCommand(command);

    m_logRepositoryAction = new QAction(tr("Log Repository"), this);
hjk's avatar
hjk committed
417
    command = ActionManager::registerAction(m_logRepositoryAction, CMD_ID_REPOSITORYLOG, globalcontext);
418 419
    connect(m_logRepositoryAction, SIGNAL(triggered()), this, SLOT(logRepository()));
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
420
    m_commandLocator->appendCommand(command);
421

422
    m_updateRepositoryAction = new QAction(tr("Update Repository"), this);
hjk's avatar
hjk committed
423
    command = ActionManager::registerAction(m_updateRepositoryAction, CMD_ID_REPOSITORYUPDATE, globalcontext);
424
    connect(m_updateRepositoryAction, SIGNAL(triggered()), this, SLOT(updateRepository()));
425
    subversionMenu->addAction(command);
426
    m_commandLocator->appendCommand(command);
427 428

    m_commitAllAction = new QAction(tr("Commit All Files"), this);
hjk's avatar
hjk committed
429
    command = ActionManager::registerAction(m_commitAllAction, CMD_ID_COMMIT_ALL,
430 431 432
        globalcontext);
    connect(m_commitAllAction, SIGNAL(triggered()), this, SLOT(startCommitAll()));
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
433
    m_commandLocator->appendCommand(command);
434

435
    m_describeAction = new QAction(tr("Describe..."), this);
hjk's avatar
hjk committed
436
    command = ActionManager::registerAction(m_describeAction, CMD_ID_DESCRIBE, globalcontext);
437 438 439
    connect(m_describeAction, SIGNAL(triggered()), this, SLOT(slotDescribe()));
    subversionMenu->addAction(command);

440
    m_revertRepositoryAction = new QAction(tr("Revert Repository..."), this);
hjk's avatar
hjk committed
441
    command = ActionManager::registerAction(m_revertRepositoryAction, CMD_ID_REVERT_ALL,
442 443 444
        globalcontext);
    connect(m_revertRepositoryAction, SIGNAL(triggered()), this, SLOT(revertAll()));
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
445
    m_commandLocator->appendCommand(command);
446

con's avatar
con committed
447
    // Actions of the submit editor
448
    Context svncommitcontext(SUBVERSIONCOMMITEDITOR);
con's avatar
con committed
449

hjk's avatar
hjk committed
450
    m_submitCurrentLogAction = new QAction(VcsBaseSubmitEditor::submitIcon(), tr("Commit"), this);
451
    command = ActionManager::registerAction(m_submitCurrentLogAction, SUBMIT_CURRENT, svncommitcontext);
452
    command->setAttribute(Core::Command::CA_UpdateText);
con's avatar
con committed
453 454
    connect(m_submitCurrentLogAction, SIGNAL(triggered()), this, SLOT(submitCurrentLog()));

hjk's avatar
hjk committed
455
    m_submitDiffAction = new QAction(VcsBaseSubmitEditor::diffIcon(), tr("Diff &Selected Files"), this);
456
    command = ActionManager::registerAction(m_submitDiffAction , DIFF_SELECTED, svncommitcontext);
con's avatar
con committed
457 458

    m_submitUndoAction = new QAction(tr("&Undo"), this);
hjk's avatar
hjk committed
459
    command = ActionManager::registerAction(m_submitUndoAction, Core::Constants::UNDO, svncommitcontext);
con's avatar
con committed
460 461

    m_submitRedoAction = new QAction(tr("&Redo"), this);
hjk's avatar
hjk committed
462
    command = ActionManager::registerAction(m_submitRedoAction, Core::Constants::REDO, svncommitcontext);
con's avatar
con committed
463 464 465 466

    return true;
}

467
bool SubversionPlugin::submitEditorAboutToClose()
con's avatar
con committed
468
{
469
    if (!isCommitEditorOpen())
con's avatar
con committed
470 471
        return true;

472 473
    SubversionSubmitEditor *editor = qobject_cast<SubversionSubmitEditor *>(submitEditor());
    QTC_ASSERT(editor, return true);
hjk's avatar
hjk committed
474
    IDocument *editorDocument = editor->document();
475
    QTC_ASSERT(editorDocument, return true);
con's avatar
con committed
476 477 478

    // Submit editor closing. Make it write out the commit message
    // and retrieve files
479
    const QFileInfo editorFile(editorDocument->filePath());
480
    const QFileInfo changeFile(m_commitMessageFileName);
con's avatar
con committed
481 482 483
    if (editorFile.absoluteFilePath() != changeFile.absoluteFilePath())
        return true; // Oops?!

484 485
    // Prompt user. Force a prompt unless submit was actually invoked (that
    // is, the editor was closed or shutdown).
486
    SubversionSettings newSettings = m_settings;
hjk's avatar
hjk committed
487
    const VcsBaseSubmitEditor::PromptSubmitResult answer =
488 489
            editor->promptSubmit(tr("Closing Subversion Editor"),
                                 tr("Do you want to commit the change?"),
490
                                 tr("The commit message check failed. Do you want to commit the change?"),
491 492
                                 newSettings.boolPointer(SubversionSettings::promptOnSubmitKey),
                                 !m_submitActionTriggered);
493
    m_submitActionTriggered = false;
con's avatar
con committed
494
    switch (answer) {
hjk's avatar
hjk committed
495
    case VcsBaseSubmitEditor::SubmitCanceled:
con's avatar
con committed
496
        return false; // Keep editing and change file
hjk's avatar
hjk committed
497
    case VcsBaseSubmitEditor::SubmitDiscarded:
498
        cleanCommitMessageFile();
con's avatar
con committed
499 500 501 502
        return true; // Cancel all
    default:
        break;
    }
503
    setSettings(newSettings); // in case someone turned prompting off
con's avatar
con committed
504
    const QStringList fileList = editor->checkedFiles();
505
    bool closeEditor = true;
con's avatar
con committed
506 507
    if (!fileList.empty()) {
        // get message & commit
hjk's avatar
hjk committed
508
        closeEditor = DocumentManager::saveDocument(editorDocument);
509 510 511 512 513 514 515 516
        if (closeEditor) {
            VcsBase::Command *commitCmd = m_client->createCommitCmd(m_commitRepository,
                                                                    fileList,
                                                                    m_commitMessageFileName);
            QObject::connect(commitCmd, SIGNAL(success(QVariant)),
                             this, SLOT(cleanCommitMessageFile()));
            commitCmd->execute();
        }
con's avatar
con committed
517
    }
518
    return closeEditor;
con's avatar
con committed
519 520
}

521
void SubversionPlugin::diffCommitFiles(const QStringList &files)
con's avatar
con committed
522
{
523
    m_client->diff(m_commitRepository, files);
524 525
}

con's avatar
con committed
526 527
SubversionSubmitEditor *SubversionPlugin::openSubversionSubmitEditor(const QString &fileName)
{
528
    IEditor *editor = EditorManager::openEditor(fileName, SUBVERSIONCOMMITEDITOR_ID);
con's avatar
con committed
529
    SubversionSubmitEditor *submitEditor = qobject_cast<SubversionSubmitEditor*>(editor);
530
    QTC_ASSERT(submitEditor, return 0);
531
    setSubmitEditor(submitEditor);
532
    submitEditor->registerActions(m_submitUndoAction, m_submitRedoAction, m_submitCurrentLogAction, m_submitDiffAction);
533
    connect(submitEditor, SIGNAL(diffSelectedFiles(QStringList)), this, SLOT(diffCommitFiles(QStringList)));
534
    submitEditor->setCheckScriptWorkingDirectory(m_commitRepository);
con's avatar
con committed
535 536 537
    return submitEditor;
}

hjk's avatar
hjk committed
538
void SubversionPlugin::updateActions(VcsBasePlugin::ActionState as)
con's avatar
con committed
539
{
Friedemann Kleint's avatar
Friedemann Kleint committed
540 541
    if (!enableMenuAction(as, m_menuAction)) {
        m_commandLocator->setEnabled(false);
542
        return;
Friedemann Kleint's avatar
Friedemann Kleint committed
543 544 545 546
    }
    const bool hasTopLevel = currentState().hasTopLevel();
    m_commandLocator->setEnabled(hasTopLevel);
    m_logRepositoryAction->setEnabled(hasTopLevel);
547

548 549 550 551
    const QString projectName = currentState().currentProjectName();
    m_diffProjectAction->setParameter(projectName);
    m_statusProjectAction->setParameter(projectName);
    m_updateProjectAction->setParameter(projectName);
552
    m_logProjectAction->setParameter(projectName);
553
    m_commitProjectAction->setParameter(projectName);
554 555 556 557

    const bool repoEnabled = currentState().hasTopLevel();
    m_commitAllAction->setEnabled(repoEnabled);
    m_describeAction->setEnabled(repoEnabled);
558
    m_revertRepositoryAction->setEnabled(repoEnabled);
559 560 561
    m_diffRepositoryAction->setEnabled(repoEnabled);
    m_statusRepositoryAction->setEnabled(repoEnabled);
    m_updateRepositoryAction->setEnabled(repoEnabled);
562 563 564 565 566 567 568 569 570 571

    const QString fileName = currentState().currentFileName();

    m_addAction->setParameter(fileName);
    m_deleteAction->setParameter(fileName);
    m_revertAction->setParameter(fileName);
    m_diffCurrentAction->setParameter(fileName);
    m_commitCurrentAction->setParameter(fileName);
    m_filelogCurrentAction->setParameter(fileName);
    m_annotateCurrentAction->setParameter(fileName);
con's avatar
con committed
572 573 574 575
}

void SubversionPlugin::addCurrentFile()
{
hjk's avatar
hjk committed
576
    const VcsBasePluginState state = currentState();
577
    QTC_ASSERT(state.hasFile(), return);
578
    vcsAdd(state.currentFileTopLevel(), state.relativeCurrentFile());
con's avatar
con committed
579 580
}

581 582
void SubversionPlugin::revertAll()
{
hjk's avatar
hjk committed
583
    const VcsBasePluginState state = currentState();
584
    QTC_ASSERT(state.hasTopLevel(), return);
585
    const QString title = tr("Revert repository");
hjk's avatar
hjk committed
586
    if (QMessageBox::warning(ICore::dialogParent(), title,
587
                             tr("Revert all pending changes to the repository?"),
588 589 590 591 592
                             QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
        return;
    // NoteL: Svn "revert ." doesn not work.
    QStringList args;
    args << QLatin1String("revert") << QLatin1String("--recursive") << state.topLevel();
593
    const SubversionResponse revertResponse =
594
            runSvn(state.topLevel(), args, m_settings.timeOutMs(),
595
                   SshPasswordPrompt|ShowStdOutInLogWindow);
596
    if (revertResponse.error)
hjk's avatar
hjk committed
597
        QMessageBox::warning(ICore::dialogParent(), title,
598
                             tr("Revert failed: %1").arg(revertResponse.message), QMessageBox::Ok);
599
    else
600 601 602
        subVersionControl()->emitRepositoryChanged(state.topLevel());
}

con's avatar
con committed
603 604
void SubversionPlugin::revertCurrentFile()
{
hjk's avatar
hjk committed
605
    const VcsBasePluginState state = currentState();
606
    QTC_ASSERT(state.hasFile(), return);
con's avatar
con committed
607 608

    QStringList args(QLatin1String("diff"));
609
    args.push_back(state.relativeCurrentFile());
con's avatar
con committed
610

611
    const SubversionResponse diffResponse =
612
            runSvn(state.currentFileTopLevel(), args, m_settings.timeOutMs(), 0);
con's avatar
con committed
613 614 615 616 617
    if (diffResponse.error)
        return;

    if (diffResponse.stdOut.isEmpty())
        return;
hjk's avatar
hjk committed
618
    if (QMessageBox::warning(ICore::dialogParent(), QLatin1String("svn revert"),
619
                             tr("The file has been changed. Do you want to revert it?"),
con's avatar
con committed
620 621 622
                             QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
        return;

623

hjk's avatar
hjk committed
624
    FileChangeBlocker fcb(state.currentFile());
con's avatar
con committed
625 626 627

    // revert
    args.clear();
628
    args << QLatin1String("revert") << state.relativeCurrentFile();
con's avatar
con committed
629

630
    const SubversionResponse revertResponse =
631
            runSvn(state.currentFileTopLevel(), args, m_settings.timeOutMs(),
632 633
                   SshPasswordPrompt|ShowStdOutInLogWindow);

634
    if (!revertResponse.error)
635
        subVersionControl()->emitFilesChanged(QStringList(state.currentFile()));
con's avatar
con committed
636 637 638 639
}

void SubversionPlugin::diffProject()
{
hjk's avatar
hjk committed
640
    const VcsBasePluginState state = currentState();
641
    QTC_ASSERT(state.hasProject(), return);
642 643 644
    const QString relativeProject = state.relativeCurrentProject();
    m_client->diff(state.currentProjectTopLevel(),
                      relativeProject.isEmpty() ? QStringList() : QStringList(relativeProject));
con's avatar
con committed
645 646 647 648
}

void SubversionPlugin::diffCurrentFile()
{
hjk's avatar
hjk committed
649
    const VcsBasePluginState state = currentState();
650
    QTC_ASSERT(state.hasFile(), return);
651
    m_client->diff(state.currentFileTopLevel(), QStringList(state.relativeCurrentFile()));
con's avatar
con committed
652 653 654 655
}

void SubversionPlugin::startCommitCurrentFile()
{
hjk's avatar
hjk committed
656
    const VcsBasePluginState state = currentState();
657
    QTC_ASSERT(state.hasFile(), return);
658
    startCommit(state.currentFileTopLevel(), QStringList(state.relativeCurrentFile()));
con's avatar
con committed
659 660 661 662
}

void SubversionPlugin::startCommitAll()
{
hjk's avatar
hjk committed
663
    const VcsBasePluginState state = currentState();
664 665
    QTC_ASSERT(state.hasTopLevel(), return);
    startCommit(state.topLevel());
con's avatar
con committed
666 667
}

668 669
void SubversionPlugin::startCommitProject()
{
hjk's avatar
hjk committed
670
    const VcsBasePluginState state = currentState();
671 672 673 674
    QTC_ASSERT(state.hasProject(), return);
    startCommit(state.currentProjectPath());
}

con's avatar
con committed
675 676 677
/* Start commit of files of a single repository by displaying
 * template and files in a submit editor. On closing, the real
 * commit will start. */
678
void SubversionPlugin::startCommit(const QString &workingDir, const QStringList &files)
con's avatar
con committed
679
{
680
    if (raiseSubmitEditor())
681
        return;
682
    if (isCommitEditorOpen()) {
hjk's avatar
hjk committed
683
        VcsBaseOutputWindow::instance()->appendWarning(tr("Another commit is currently being executed."));
con's avatar
con committed
684 685 686 687 688 689
        return;
    }

    QStringList args(QLatin1String("status"));
    args += files;

690
    const SubversionResponse response =
691
            runSvn(workingDir, args, m_settings.timeOutMs(), 0);
con's avatar
con committed
692 693
    if (response.error)
        return;
694

con's avatar
con committed
695
    // Get list of added/modified/deleted files
696
    const StatusList statusOutput = parseStatusOutput(response.stdOut);
con's avatar
con committed
697
    if (statusOutput.empty()) {
hjk's avatar
hjk committed
698
        VcsBaseOutputWindow::instance()->appendWarning(tr("There are no modified files."));
con's avatar
con committed
699 700
        return;
    }
701
    m_commitRepository = workingDir;
con's avatar
con committed
702
    // Create a new submit change file containing the submit template
hjk's avatar
hjk committed
703
    TempFileSaver saver;
704 705
    saver.setAutoRemove(false);
    // TODO: Retrieve submit template from
con's avatar
con committed
706 707
    const QString submitTemplate;
    // Create a submit
708 709
    saver.write(submitTemplate.toUtf8());
    if (!saver.finalize()) {
hjk's avatar
hjk committed
710
        VcsBaseOutputWindow::instance()->appendError(saver.errorString());
711 712 713
        return;
    }
    m_commitMessageFileName = saver.fileName();
con's avatar
con committed
714
    // Create a submit editor and set file list
715
    SubversionSubmitEditor *editor = openSubversionSubmitEditor(m_commitMessageFileName);
716
    QTC_ASSERT(editor, return);
717
    editor->setStatusList(statusOutput);
con's avatar
con committed
718 719 720 721 722 723 724 725 726 727 728
}

bool SubversionPlugin::commit(const QString &messageFile,
                              const QStringList &subVersionFileList)
{
    if (Subversion::Constants::debug)
        qDebug() << Q_FUNC_INFO << messageFile << subVersionFileList;
    // Transform the status list which is sth
    // "[ADM]<blanks>file" into an args list. The files of the status log
    // can be relative or absolute depending on where the command was run.
    QStringList args = QStringList(QLatin1String("commit"));
729
    args << QLatin1String(Constants::NON_INTERACTIVE_OPTION) << QLatin1String("--file") << messageFile;
con's avatar
con committed
730
    args.append(subVersionFileList);
731
    const SubversionResponse response =
732
            runSvn(m_commitRepository, args, 10 * m_settings.timeOutMs(),
733
                   SshPasswordPrompt|ShowStdOutInLogWindow);
con's avatar
con committed
734 735 736 737 738
    return !response.error ;
}

void SubversionPlugin::filelogCurrentFile()
{
hjk's avatar
hjk committed
739
    const VcsBasePluginState state = currentState();
740
    QTC_ASSERT(state.hasFile(), return);
741
    filelog(state.currentFileTopLevel(), state.relativeCurrentFile(), true);
742 743 744 745
}

void SubversionPlugin::logProject()
{
hjk's avatar
hjk committed
746
    const VcsBasePluginState state = currentState();
747
    QTC_ASSERT(state.hasProject(), return);
748 749 750 751 752
    filelog(state.currentProjectTopLevel(), state.relativeCurrentProject());
}

void SubversionPlugin::logRepository()
{
hjk's avatar
hjk committed
753
    const VcsBasePluginState state = currentState();
754
    QTC_ASSERT(state.hasTopLevel(), return);
755
    filelog(state.topLevel());
con's avatar
con committed
756 757
}

758 759
void SubversionPlugin::diffRepository()
{
hjk's avatar
hjk committed
760
    const VcsBasePluginState state = currentState();
761
    QTC_ASSERT(state.hasTopLevel(), return);
762
    m_client->diff(state.topLevel(), QStringList());
763 764 765 766
}

void SubversionPlugin::statusRepository()
{
hjk's avatar
hjk committed
767
    const VcsBasePluginState state = currentState();
768
    QTC_ASSERT(state.hasTopLevel(), return);
769 770 771 772 773
    svnStatus(