subversionplugin.cpp 57.5 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2
3
4
**
** This file is part of Qt Creator
**
con's avatar
con committed
5
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6
**
hjk's avatar
hjk committed
7
** Contact: Nokia Corporation (info@qt.nokia.com)
con's avatar
con committed
8
**
9
**
10
** GNU Lesser General Public License Usage
11
**
hjk's avatar
hjk committed
12
13
14
15
16
17
** 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.
18
**
con's avatar
con committed
19
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
con's avatar
con committed
21
22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23
24
25
26
27
** 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.
**
con's avatar
con committed
28
29
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
con's avatar
con committed
30
**
31
**************************************************************************/
hjk's avatar
hjk committed
32

con's avatar
con committed
33
34
35
36
37
38
39
40
#include "subversionplugin.h"

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

#include "subversionsubmiteditor.h"
#include "subversionconstants.h"
#include "subversioncontrol.h"
41
#include "checkoutwizard.h"
con's avatar
con committed
42
43
44
45

#include <vcsbase/basevcseditorfactory.h>
#include <vcsbase/vcsbaseeditor.h>
#include <vcsbase/basevcssubmiteditorfactory.h>
46
#include <vcsbase/vcsbaseoutputwindow.h>
47
#include <vcsbase/vcsbaseeditorparameterwidget.h>
con's avatar
con committed
48
#include <utils/synchronousprocess.h>
49
#include <utils/parameteraction.h>
con's avatar
con committed
50
51
52
53
54
55

#include <coreplugin/icore.h>
#include <coreplugin/coreconstants.h>
#include <coreplugin/filemanager.h>
#include <coreplugin/messagemanager.h>
#include <coreplugin/mimedatabase.h>
56
#include <coreplugin/actionmanager/actionmanager.h>
57
58
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/command.h>
59
#include <coreplugin/uniqueidmanager.h>
con's avatar
con committed
60
#include <coreplugin/editormanager/editormanager.h>
Friedemann Kleint's avatar
Friedemann Kleint committed
61
62
63

#include <locator/commandlocator.h>

hjk's avatar
hjk committed
64
#include <utils/qtcassert.h>
con's avatar
con committed
65
66

#include <QtCore/QDebug>
67
#include <QtCore/QDir>
con's avatar
con committed
68
69
#include <QtCore/QFileInfo>
#include <QtCore/QTemporaryFile>
70
71
#include <QtCore/QTextCodec>
#include <QtCore/QtPlugin>
72
#include <QtCore/QProcessEnvironment>
73
#include <QtCore/QUrl>
con's avatar
con committed
74
#include <QtGui/QAction>
75
76
#include <QtGui/QFileDialog>
#include <QtGui/QMainWindow>
con's avatar
con committed
77
78
#include <QtGui/QMenu>
#include <QtGui/QMessageBox>
79
#include <QtGui/QInputDialog>
80
#include <QtXml/QXmlStreamReader>
81
#include <limits.h>
con's avatar
con committed
82

83
84
namespace Subversion {
namespace Internal {
con's avatar
con committed
85

86
87
88
89
90
91
92
93
94
static const char * const CMD_ID_SUBVERSION_MENU    = "Subversion.Menu";
static const char * const CMD_ID_ADD                = "Subversion.Add";
static const char * const CMD_ID_DELETE_FILE        = "Subversion.Delete";
static const char * const CMD_ID_REVERT             = "Subversion.Revert";
static const char * const CMD_ID_SEPARATOR0         = "Subversion.Separator0";
static const char * const CMD_ID_DIFF_PROJECT       = "Subversion.DiffAll";
static const char * const CMD_ID_DIFF_CURRENT       = "Subversion.DiffCurrent";
static const char * const CMD_ID_SEPARATOR1         = "Subversion.Separator1";
static const char * const CMD_ID_COMMIT_ALL         = "Subversion.CommitAll";
95
static const char * const CMD_ID_REVERT_ALL         = "Subversion.RevertAll";
96
97
98
99
100
static const char * const CMD_ID_COMMIT_CURRENT     = "Subversion.CommitCurrent";
static const char * const CMD_ID_SEPARATOR2         = "Subversion.Separator2";
static const char * const CMD_ID_FILELOG_CURRENT    = "Subversion.FilelogCurrent";
static const char * const CMD_ID_ANNOTATE_CURRENT   = "Subversion.AnnotateCurrent";
static const char * const CMD_ID_SEPARATOR3         = "Subversion.Separator3";
101
static const char * const CMD_ID_SEPARATOR4         = "Subversion.Separator4";
102
static const char * const CMD_ID_STATUS             = "Subversion.Status";
103
104
static const char * const CMD_ID_PROJECTLOG         = "Subversion.ProjectLog";
static const char * const CMD_ID_REPOSITORYLOG      = "Subversion.RepositoryLog";
105
106
107
static const char * const CMD_ID_REPOSITORYUPDATE   = "Subversion.RepositoryUpdate";
static const char * const CMD_ID_REPOSITORYDIFF     = "Subversion.RepositoryDiff";
static const char * const CMD_ID_REPOSITORYSTATUS   = "Subversion.RepositoryStatus";
108
static const char * const CMD_ID_UPDATE             = "Subversion.Update";
109
static const char * const CMD_ID_COMMIT_PROJECT     = "Subversion.CommitProject";
110
static const char * const CMD_ID_DESCRIBE           = "Subversion.Describe";
con's avatar
con committed
111

112
113
static const char *nonInteractiveOptionC = "--non-interactive";

114
115


con's avatar
con committed
116
117
118
static const VCSBase::VCSBaseEditorParameters editorParameters[] = {
{
    VCSBase::RegularCommandOutput,
119
120
    "Subversion Command Log Editor", // id
    QT_TRANSLATE_NOOP("VCS", "Subversion Command Log Editor"), // display name
121
    "Subversion Command Log Editor", // context
con's avatar
con committed
122
123
124
    "application/vnd.nokia.text.scs_svn_commandlog",
    "scslog"},
{   VCSBase::LogOutput,
125
126
    "Subversion File Log Editor",   // id
    QT_TRANSLATE_NOOP("VCS", "Subversion File Log Editor"),   // display_name
127
    "Subversion File Log Editor",   // context
con's avatar
con committed
128
129
130
    "application/vnd.nokia.text.scs_svn_filelog",
    "scsfilelog"},
{    VCSBase::AnnotateOutput,
131
132
    "Subversion Annotation Editor",  // id
    QT_TRANSLATE_NOOP("VCS", "Subversion Annotation Editor"),   // display_name
133
    "Subversion Annotation Editor",  // context
con's avatar
con committed
134
135
136
    "application/vnd.nokia.text.scs_svn_annotation",
    "scsannotate"},
{   VCSBase::DiffOutput,
137
138
    "Subversion Diff Editor",  // id
    QT_TRANSLATE_NOOP("VCS", "Subversion Diff Editor"),   // display_name
139
    "Subversion Diff Editor",  // context
con's avatar
con committed
140
141
142
143
144
145
146
    "text/x-patch","diff"}
};

// Utility to find a parameter set by type
static inline const VCSBase::VCSBaseEditorParameters *findType(int ie)
{
    const VCSBase::EditorContentType et = static_cast<VCSBase::EditorContentType>(ie);
147
    return  VCSBase::VCSBaseEditorWidget::findType(editorParameters, sizeof(editorParameters)/sizeof(VCSBase::VCSBaseEditorParameters), et);
con's avatar
con committed
148
149
150
151
152
153
154
}

static inline QString debugCodec(const QTextCodec *c)
{
    return c ? QString::fromAscii(c->name()) : QString::fromAscii("Null codec");
}

155
156
157
158
159
160
161
162
163
164
165
166
167
168
// 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')) {
169
170
                const QString fileName = line.mid(7); // Column 8 starting from svn 1.6
                changeSet.push_back(SubversionSubmitEditor::StatusFilePair(QString(state), fileName.trimmed()));
171
172
173
174
175
176
177
            }

        }
    }
    return changeSet;
}

178
179
180
181
182
183
184
185
186
187
188
// Return a list of names for the internal svn directories
static inline QStringList svnDirectories()
{
    QStringList rc(QLatin1String(".svn"));
#ifdef Q_OS_WIN
    // Option on Windows systems to avoid hassle with some IDEs
    rc.push_back(QLatin1String("_svn"));
#endif
    return rc;
}

con's avatar
con committed
189
190
191
192
// ------------- SubversionPlugin
SubversionPlugin *SubversionPlugin::m_subversionPluginInstance = 0;

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

SubversionPlugin::~SubversionPlugin()
{
226
    cleanCommitMessageFile();
con's avatar
con committed
227
228
}

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

238
239
240
241
242
bool SubversionPlugin::isCommitEditorOpen() const
{
    return !m_commitMessageFileName.isEmpty();
}

con's avatar
con committed
243
244
static const VCSBase::VCSBaseSubmitEditorParameters submitParameters = {
    Subversion::Constants::SUBVERSION_SUBMIT_MIMETYPE,
245
246
    Subversion::Constants::SUBVERSIONCOMMITEDITOR_ID,
    Subversion::Constants::SUBVERSIONCOMMITEDITOR_DISPLAY_NAME,
247
    Subversion::Constants::SUBVERSIONCOMMITEDITOR
con's avatar
con committed
248
249
};

250
251
252
static inline Core::Command *createSeparator(QObject *parent,
                                             Core::ActionManager *ami,
                                             const char*id,
253
                                             const Core::Context &globalcontext)
254
255
256
257
258
259
{
    QAction *tmpaction = new QAction(parent);
    tmpaction->setSeparator(true);
    return ami->registerAction(tmpaction, id, globalcontext);
}

260
bool SubversionPlugin::initialize(const QStringList & /*arguments */, QString *errorMessage)
con's avatar
con committed
261
262
263
264
265
266
267
268
{
    typedef VCSBase::VCSSubmitEditorFactory<SubversionSubmitEditor> SubversionSubmitEditorFactory;
    typedef VCSBase::VCSEditorFactory<SubversionEditor> SubversionEditorFactory;
    using namespace Constants;

    using namespace Core::Constants;
    using namespace ExtensionSystem;

269
270
    VCSBase::VCSBasePlugin::initialize(new SubversionControl(this));

con's avatar
con committed
271
    m_subversionPluginInstance = this;
hjk's avatar
hjk committed
272
    Core::ICore *core = Core::ICore::instance();
con's avatar
con committed
273

hjk's avatar
hjk committed
274
    if (!core->mimeDatabase()->addMimeTypes(QLatin1String(":/trolltech.subversion/Subversion.mimetypes.xml"), errorMessage))
con's avatar
con committed
275
276
        return false;

hjk's avatar
hjk committed
277
    if (QSettings *settings = core->settings())
con's avatar
con committed
278
279
        m_settings.fromSettings(settings);

280
    addAutoReleasedObject(new SettingsPage);
con's avatar
con committed
281

282
    addAutoReleasedObject(new SubversionSubmitEditorFactory(&submitParameters));
con's avatar
con committed
283
284
285

    static const char *describeSlot = SLOT(describe(QString,QString));
    const int editorCount = sizeof(editorParameters)/sizeof(VCSBase::VCSBaseEditorParameters);
286
287
    for (int i = 0; i < editorCount; i++)
        addAutoReleasedObject(new SubversionEditorFactory(editorParameters + i, this, describeSlot));
con's avatar
con committed
288

289
290
    addAutoReleasedObject(new CheckoutWizard);

Friedemann Kleint's avatar
Friedemann Kleint committed
291
292
293
294
295
    const QString description = QLatin1String("Subversion");
    const QString prefix = QLatin1String("svn");
    m_commandLocator = new Locator::CommandLocator(description, prefix, prefix);
    addAutoReleasedObject(m_commandLocator);

con's avatar
con committed
296
    //register actions
hjk's avatar
hjk committed
297
    Core::ActionManager *ami = core->actionManager();
298
    Core::ActionContainer *toolsContainer = ami->actionContainer(M_TOOLS);
con's avatar
con committed
299

300
    Core::ActionContainer *subversionMenu =
hjk's avatar
hjk committed
301
        ami->createMenu(Core::Id(CMD_ID_SUBVERSION_MENU));
con's avatar
con committed
302
303
    subversionMenu->menu()->setTitle(tr("&Subversion"));
    toolsContainer->addMenu(subversionMenu);
304
    m_menuAction = subversionMenu->menu()->menuAction();
305
    Core::Context globalcontext(C_GLOBAL);
con's avatar
con committed
306
    Core::Command *command;
307
308
309
310

    m_diffCurrentAction = new Utils::ParameterAction(tr("Diff Current File"), tr("Diff \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
    command = ami->registerAction(m_diffCurrentAction,
        CMD_ID_DIFF_CURRENT, globalcontext);
con's avatar
con committed
311
    command->setAttribute(Core::Command::CA_UpdateText);
312
313
    command->setDefaultKeySequence(QKeySequence(tr("Alt+S,Alt+D")));
    connect(m_diffCurrentAction, SIGNAL(triggered()), this, SLOT(diffCurrentFile()));
con's avatar
con committed
314
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
315
    m_commandLocator->appendCommand(command);
con's avatar
con committed
316

317
318
319
    m_filelogCurrentAction = new Utils::ParameterAction(tr("Filelog Current File"), tr("Filelog \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
    command = ami->registerAction(m_filelogCurrentAction,
        CMD_ID_FILELOG_CURRENT, globalcontext);
con's avatar
con committed
320
    command->setAttribute(Core::Command::CA_UpdateText);
321
322
    connect(m_filelogCurrentAction, SIGNAL(triggered()), this,
        SLOT(filelogCurrentFile()));
con's avatar
con committed
323
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
324
    m_commandLocator->appendCommand(command);
con's avatar
con committed
325

326
327
328
    m_annotateCurrentAction = new Utils::ParameterAction(tr("Annotate Current File"), tr("Annotate \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
    command = ami->registerAction(m_annotateCurrentAction,
        CMD_ID_ANNOTATE_CURRENT, globalcontext);
con's avatar
con committed
329
    command->setAttribute(Core::Command::CA_UpdateText);
330
331
    connect(m_annotateCurrentAction, SIGNAL(triggered()), this,
        SLOT(annotateCurrentFile()));
con's avatar
con committed
332
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
333
    m_commandLocator->appendCommand(command);
con's avatar
con committed
334

335
    subversionMenu->addAction(createSeparator(this, ami, CMD_ID_SEPARATOR0, globalcontext));
con's avatar
con committed
336

337
338
    m_addAction = new Utils::ParameterAction(tr("Add"), tr("Add \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
    command = ami->registerAction(m_addAction, CMD_ID_ADD,
con's avatar
con committed
339
        globalcontext);
340
    command->setAttribute(Core::Command::CA_UpdateText);
341
342
    command->setDefaultKeySequence(QKeySequence(tr("Alt+S,Alt+A")));
    connect(m_addAction, SIGNAL(triggered()), this, SLOT(addCurrentFile()));
con's avatar
con committed
343
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
344
    m_commandLocator->appendCommand(command);
con's avatar
con committed
345

346
    m_commitCurrentAction = new Utils::ParameterAction(tr("Commit Current File"), tr("Commit \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
con's avatar
con committed
347
    command = ami->registerAction(m_commitCurrentAction,
348
        CMD_ID_COMMIT_CURRENT, globalcontext);
con's avatar
con committed
349
    command->setAttribute(Core::Command::CA_UpdateText);
con's avatar
con committed
350
351
352
    command->setDefaultKeySequence(QKeySequence(tr("Alt+S,Alt+C")));
    connect(m_commitCurrentAction, SIGNAL(triggered()), this, SLOT(startCommitCurrentFile()));
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
353
    m_commandLocator->appendCommand(command);
con's avatar
con committed
354

355
356
357
    m_deleteAction = new Utils::ParameterAction(tr("Delete..."), tr("Delete \"%1\"..."), Utils::ParameterAction::EnabledWithParameter, this);
    command = ami->registerAction(m_deleteAction, CMD_ID_DELETE_FILE,
        globalcontext);
con's avatar
con committed
358
    command->setAttribute(Core::Command::CA_UpdateText);
359
    connect(m_deleteAction, SIGNAL(triggered()), this, SLOT(promptToDeleteCurrentFile()));
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
364
365
    m_revertAction = new Utils::ParameterAction(tr("Revert..."), tr("Revert \"%1\"..."), Utils::ParameterAction::EnabledWithParameter, this);
    command = ami->registerAction(m_revertAction, CMD_ID_REVERT,
        globalcontext);
con's avatar
con committed
366
    command->setAttribute(Core::Command::CA_UpdateText);
367
    connect(m_revertAction, SIGNAL(triggered()), this, SLOT(revertCurrentFile()));
con's avatar
con committed
368
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
369
    m_commandLocator->appendCommand(command);
con's avatar
con committed
370

371
    subversionMenu->addAction(createSeparator(this, ami, CMD_ID_SEPARATOR1, globalcontext));
372

373
374
375
376
377
378
    m_diffProjectAction = new Utils::ParameterAction(tr("Diff Project"), tr("Diff Project \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
    command = ami->registerAction(m_diffProjectAction, CMD_ID_DIFF_PROJECT,
        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
379
    m_commandLocator->appendCommand(command);
con's avatar
con committed
380

381
382
    m_statusProjectAction = new Utils::ParameterAction(tr("Project Status"), tr("Status of Project \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
    command = ami->registerAction(m_statusProjectAction, CMD_ID_STATUS,
con's avatar
con committed
383
        globalcontext);
384
385
    command->setAttribute(Core::Command::CA_UpdateText);
    connect(m_statusProjectAction, SIGNAL(triggered()), this, SLOT(projectStatus()));
con's avatar
con committed
386
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
387
    m_commandLocator->appendCommand(command);
con's avatar
con committed
388

Friedemann Kleint's avatar
Friedemann Kleint committed
389
    m_logProjectAction = new Utils::ParameterAction(tr("Log Project"), tr("Log Project \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
390
391
392
393
    command = ami->registerAction(m_logProjectAction, CMD_ID_PROJECTLOG, globalcontext);
    command->setAttribute(Core::Command::CA_UpdateText);
    connect(m_logProjectAction, SIGNAL(triggered()), this, SLOT(logProject()));
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
394
    m_commandLocator->appendCommand(command);
395

396
    m_updateProjectAction = new Utils::ParameterAction(tr("Update Project"), tr("Update Project \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
397
    command = ami->registerAction(m_updateProjectAction, CMD_ID_UPDATE, globalcontext);
con's avatar
con committed
398
    connect(m_updateProjectAction, SIGNAL(triggered()), this, SLOT(updateProject()));
399
    command->setAttribute(Core::Command::CA_UpdateText);
con's avatar
con committed
400
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
401
    m_commandLocator->appendCommand(command);
con's avatar
con committed
402

403
404
405
406
407
408
409
    m_commitProjectAction = new Utils::ParameterAction(tr("Commit Project"), tr("Commit Project \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
    command = ami->registerAction(m_commitProjectAction, CMD_ID_COMMIT_PROJECT, globalcontext);
    connect(m_commitProjectAction, SIGNAL(triggered()), this, SLOT(startCommitProject()));
    command->setAttribute(Core::Command::CA_UpdateText);
    subversionMenu->addAction(command);
    m_commandLocator->appendCommand(command);

410
411
    subversionMenu->addAction(createSeparator(this, ami, CMD_ID_SEPARATOR2, globalcontext));

412
413
414
415
416
417
418
419
420
421
422
423
424
    m_diffRepositoryAction = new QAction(tr("Diff Repository"), this);
    command = ami->registerAction(m_diffRepositoryAction, CMD_ID_REPOSITORYDIFF, globalcontext);
    connect(m_diffRepositoryAction, SIGNAL(triggered()), this, SLOT(diffRepository()));
    subversionMenu->addAction(command);
    m_commandLocator->appendCommand(command);

    m_statusRepositoryAction = new QAction(tr("Repository Status"), this);
    command = ami->registerAction(m_statusRepositoryAction, CMD_ID_REPOSITORYSTATUS, globalcontext);
    connect(m_statusRepositoryAction, SIGNAL(triggered()), this, SLOT(statusRepository()));
    subversionMenu->addAction(command);
    m_commandLocator->appendCommand(command);

    m_logRepositoryAction = new QAction(tr("Log Repository"), this);
425
426
427
    command = ami->registerAction(m_logRepositoryAction, CMD_ID_REPOSITORYLOG, globalcontext);
    connect(m_logRepositoryAction, SIGNAL(triggered()), this, SLOT(logRepository()));
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
428
    m_commandLocator->appendCommand(command);
429

430
431
432
    m_updateRepositoryAction = new QAction(tr("Update Repository"), this);
    command = ami->registerAction(m_updateRepositoryAction, CMD_ID_REPOSITORYUPDATE, globalcontext);
    connect(m_updateRepositoryAction, SIGNAL(triggered()), this, SLOT(updateRepository()));
433
    subversionMenu->addAction(command);
434
    m_commandLocator->appendCommand(command);
435
436
437
438
439
440

    m_commitAllAction = new QAction(tr("Commit All Files"), this);
    command = ami->registerAction(m_commitAllAction, CMD_ID_COMMIT_ALL,
        globalcontext);
    connect(m_commitAllAction, SIGNAL(triggered()), this, SLOT(startCommitAll()));
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
441
    m_commandLocator->appendCommand(command);
442

443
444
445
446
447
    m_describeAction = new QAction(tr("Describe..."), this);
    command = ami->registerAction(m_describeAction, CMD_ID_DESCRIBE, globalcontext);
    connect(m_describeAction, SIGNAL(triggered()), this, SLOT(slotDescribe()));
    subversionMenu->addAction(command);

448
449
450
451
452
    m_revertRepositoryAction = new QAction(tr("Revert Repository..."), this);
    command = ami->registerAction(m_revertRepositoryAction, CMD_ID_REVERT_ALL,
        globalcontext);
    connect(m_revertRepositoryAction, SIGNAL(triggered()), this, SLOT(revertAll()));
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
453
    m_commandLocator->appendCommand(command);
454

con's avatar
con committed
455
    // Actions of the submit editor
456
    Core::Context svncommitcontext(Constants::SUBVERSIONCOMMITEDITOR);
con's avatar
con committed
457

458
    m_submitCurrentLogAction = new QAction(VCSBase::VCSBaseSubmitEditor::submitIcon(), tr("Commit"), this);
con's avatar
con committed
459
    command = ami->registerAction(m_submitCurrentLogAction, Constants::SUBMIT_CURRENT, svncommitcontext);
460
    command->setAttribute(Core::Command::CA_UpdateText);
con's avatar
con committed
461
462
    connect(m_submitCurrentLogAction, SIGNAL(triggered()), this, SLOT(submitCurrentLog()));

463
    m_submitDiffAction = new QAction(VCSBase::VCSBaseSubmitEditor::diffIcon(), tr("Diff Selected Files"), this);
con's avatar
con committed
464
465
466
467
468
469
470
471
472
473
474
    command = ami->registerAction(m_submitDiffAction , Constants::DIFF_SELECTED, svncommitcontext);

    m_submitUndoAction = new QAction(tr("&Undo"), this);
    command = ami->registerAction(m_submitUndoAction, Core::Constants::UNDO, svncommitcontext);

    m_submitRedoAction = new QAction(tr("&Redo"), this);
    command = ami->registerAction(m_submitRedoAction, Core::Constants::REDO, svncommitcontext);

    return true;
}

475
bool SubversionPlugin::submitEditorAboutToClose(VCSBase::VCSBaseSubmitEditor *submitEditor)
con's avatar
con committed
476
{
477
    if (!isCommitEditorOpen())
con's avatar
con committed
478
479
        return true;

480
481
    Core::IFile *fileIFace = submitEditor->file();
    const SubversionSubmitEditor *editor = qobject_cast<SubversionSubmitEditor *>(submitEditor);
con's avatar
con committed
482
483
484
485
486
487
    if (!fileIFace || !editor)
        return true;

    // Submit editor closing. Make it write out the commit message
    // and retrieve files
    const QFileInfo editorFile(fileIFace->fileName());
488
    const QFileInfo changeFile(m_commitMessageFileName);
con's avatar
con committed
489
490
491
    if (editorFile.absoluteFilePath() != changeFile.absoluteFilePath())
        return true; // Oops?!

492
493
    // Prompt user. Force a prompt unless submit was actually invoked (that
    // is, the editor was closed or shutdown).
494
    SubversionSettings newSettings = m_settings;
495
496
497
    const VCSBase::VCSBaseSubmitEditor::PromptSubmitResult answer =
            editor->promptSubmit(tr("Closing Subversion Editor"),
                                 tr("Do you want to commit the change?"),
498
                                 tr("The commit message check failed. Do you want to commit the change?"),
499
                                 &newSettings.promptToSubmit, !m_submitActionTriggered);
500
    m_submitActionTriggered = false;
con's avatar
con committed
501
    switch (answer) {
502
    case VCSBase::VCSBaseSubmitEditor::SubmitCanceled:
con's avatar
con committed
503
        return false; // Keep editing and change file
504
    case VCSBase::VCSBaseSubmitEditor::SubmitDiscarded:
505
        cleanCommitMessageFile();
con's avatar
con committed
506
507
508
509
        return true; // Cancel all
    default:
        break;
    }
510
    setSettings(newSettings); // in case someone turned prompting off
con's avatar
con committed
511
    const QStringList fileList = editor->checkedFiles();
512
    bool closeEditor = true;
con's avatar
con committed
513
514
    if (!fileList.empty()) {
        // get message & commit
515
516
517
        closeEditor = Core::ICore::instance()->fileManager()->saveFile(fileIFace);
        if (closeEditor)
            closeEditor = commit(m_commitMessageFileName, fileList);
con's avatar
con committed
518
    }
519
    if (closeEditor)
520
        cleanCommitMessageFile();
521
    return closeEditor;
con's avatar
con committed
522
523
}

524
void SubversionPlugin::diffCommitFiles(const QStringList &files)
con's avatar
con committed
525
{
526
    svnDiff(m_commitRepository, files);
con's avatar
con committed
527
528
}

529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
// Collect all parameters required for a diff to be able to associate them
// with a diff editor and re-run the diff with parameters.
struct SubversionDiffParameters
{
    QString workingDir;
    QStringList arguments;
    QStringList files;
    QString diffName;
};

// Parameter widget controlling whitespace diff mode, associated with a parameter
class SubversionDiffParameterWidget : public VCSBase::VCSBaseEditorParameterWidget
{
    Q_OBJECT
public:
    explicit SubversionDiffParameterWidget(const SubversionDiffParameters &p, QWidget *parent = 0);

signals:
    void reRunDiff(const Subversion::Internal::SubversionDiffParameters &);

private slots:
    void triggerReRun();

private:
    const SubversionDiffParameters m_parameters;
};

SubversionDiffParameterWidget::SubversionDiffParameterWidget(const SubversionDiffParameters &p, QWidget *parent) :
    VCSBase::VCSBaseEditorParameterWidget(parent), m_parameters(p)
{
    setBaseArguments(p.arguments);
    addIgnoreWhiteSpaceButton(QString(QLatin1Char('w')));
    connect(this, SIGNAL(argumentsChanged()), this, SLOT(triggerReRun()));
}

void SubversionDiffParameterWidget::triggerReRun()
{
    SubversionDiffParameters effectiveParameters = m_parameters;
    // Subversion wants" -x -<ext-args>", default being -u
    const QStringList a = arguments();
    if (!a.isEmpty())
        effectiveParameters.arguments << QLatin1String("-x") << (QLatin1String("-u") + a.join(QString()));
    emit reRunDiff(effectiveParameters);
}

574
575
static inline void setDiffBaseDirectory(Core::IEditor *editor, const QString &db)
{
576
    if (VCSBase::VCSBaseEditorWidget *ve = qobject_cast<VCSBase::VCSBaseEditorWidget*>(editor->widget()))
577
578
579
        ve->setDiffBaseDirectory(db);
}

580
void SubversionPlugin::svnDiff(const QString &workingDir, const QStringList &files, QString diffname)
581
582
583
584
585
586
587
588
589
{
    SubversionDiffParameters p;
    p.workingDir = workingDir;
    p.files = files;
    p.diffName = diffname;
    svnDiff(p);
}

void SubversionPlugin::svnDiff(const Subversion::Internal::SubversionDiffParameters &p)
con's avatar
con committed
590
591
{
    if (Subversion::Constants::debug)
592
593
        qDebug() << Q_FUNC_INFO << p.files << p.diffName;
    const QString source = VCSBase::VCSBaseEditorWidget::getSource(p.workingDir, p.files);
594
    QTextCodec *codec = source.isEmpty() ? static_cast<QTextCodec *>(0) : VCSBase::VCSBaseEditorWidget::getCodec(source);
con's avatar
con committed
595

596
597
    const QString diffName = p.files.count() == 1 && p.diffName.isEmpty() ?
                             QFileInfo(p.files.front()).fileName() : p.diffName;
con's avatar
con committed
598
599

    QStringList args(QLatin1String("diff"));
600
601
    args.append(p.arguments);
    args << p.files;
con's avatar
con committed
602

603
    const SubversionResponse response =
604
            runSvn(p.workingDir, args, m_settings.timeOutMS(), 0, codec);
con's avatar
con committed
605
606
607
608
609
    if (response.error)
        return;

    // diff of a single file? re-use an existing view if possible to support
    // the common usage pattern of continuously changing and diffing a file
610
611
612
613
614
615
616
    const QString tag = VCSBase::VCSBaseEditorWidget::editorTag(VCSBase::DiffOutput, p.workingDir, p.files);
    // Show in the same editor if diff has been executed before
    if (Core::IEditor *existingEditor = VCSBase::VCSBaseEditorWidget::locateEditorByTag(tag)) {
        existingEditor->createNew(response.stdOut);
        Core::EditorManager::instance()->activateEditor(existingEditor, Core::EditorManager::ModeSwitch);
        setDiffBaseDirectory(existingEditor, p.workingDir);
        return;
con's avatar
con committed
617
    }
618
    const QString title = QString::fromLatin1("svn diff %1").arg(diffName);
con's avatar
con committed
619
    Core::IEditor *editor = showOutputInEditor(title, response.stdOut, VCSBase::DiffOutput, source, codec);
620
    setDiffBaseDirectory(editor, p.workingDir);
621
    VCSBase::VCSBaseEditorWidget::tagEditor(editor, tag);
622
623
624
625
626
627
628
629
630
631
632
633
    SubversionEditor *diffEditorWidget = qobject_cast<SubversionEditor *>(editor->widget());
    QTC_ASSERT(diffEditorWidget, return ; )

    // Wire up the parameter widget to trigger a re-run on
    // parameter change and 'revert' from inside the diff editor.
    diffEditorWidget->setRevertDiffChunkEnabled(true);
    SubversionDiffParameterWidget *pw = new SubversionDiffParameterWidget(p);
    connect(pw, SIGNAL(reRunDiff(Subversion::Internal::SubversionDiffParameters)),
            this, SLOT(svnDiff(Subversion::Internal::SubversionDiffParameters)));
    connect(diffEditorWidget, SIGNAL(diffChunkReverted(VCSBase::DiffChunk)),
            pw, SLOT(triggerReRun()));
    diffEditorWidget->setConfigurationWidget(pw);
con's avatar
con committed
634
635
636
637
}

SubversionSubmitEditor *SubversionPlugin::openSubversionSubmitEditor(const QString &fileName)
{
638
639
640
    Core::IEditor *editor = Core::EditorManager::instance()->openEditor(fileName,
                                                                        QLatin1String(Constants::SUBVERSIONCOMMITEDITOR_ID),
                                                                        Core::EditorManager::ModeSwitch);
con's avatar
con committed
641
    SubversionSubmitEditor *submitEditor = qobject_cast<SubversionSubmitEditor*>(editor);
hjk's avatar
hjk committed
642
    QTC_ASSERT(submitEditor, /**/);
643
    submitEditor->registerActions(m_submitUndoAction, m_submitRedoAction, m_submitCurrentLogAction, m_submitDiffAction);
644
    connect(submitEditor, SIGNAL(diffSelectedFiles(QStringList)), this, SLOT(diffCommitFiles(QStringList)));
645
    submitEditor->setCheckScriptWorkingDirectory(m_commitRepository);
con's avatar
con committed
646
647
648
    return submitEditor;
}

649
void SubversionPlugin::updateActions(VCSBase::VCSBasePlugin::ActionState as)
con's avatar
con committed
650
{
Friedemann Kleint's avatar
Friedemann Kleint committed
651
652
    if (!enableMenuAction(as, m_menuAction)) {
        m_commandLocator->setEnabled(false);
653
        return;
Friedemann Kleint's avatar
Friedemann Kleint committed
654
655
656
657
    }
    const bool hasTopLevel = currentState().hasTopLevel();
    m_commandLocator->setEnabled(hasTopLevel);
    m_logRepositoryAction->setEnabled(hasTopLevel);
658

659
660
661
662
    const QString projectName = currentState().currentProjectName();
    m_diffProjectAction->setParameter(projectName);
    m_statusProjectAction->setParameter(projectName);
    m_updateProjectAction->setParameter(projectName);
663
    m_logProjectAction->setParameter(projectName);
664
    m_commitProjectAction->setParameter(projectName);
665
666
667
668

    const bool repoEnabled = currentState().hasTopLevel();
    m_commitAllAction->setEnabled(repoEnabled);
    m_describeAction->setEnabled(repoEnabled);
669
    m_revertRepositoryAction->setEnabled(repoEnabled);
670
671
672
    m_diffRepositoryAction->setEnabled(repoEnabled);
    m_statusRepositoryAction->setEnabled(repoEnabled);
    m_updateRepositoryAction->setEnabled(repoEnabled);
673
674
675
676
677
678
679
680
681
682

    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
683
684
685
686
}

void SubversionPlugin::addCurrentFile()
{
687
688
689
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasFile(), return)
    vcsAdd(state.currentFileTopLevel(), state.relativeCurrentFile());
con's avatar
con committed
690
691
}

692
693
694
695
696
void SubversionPlugin::revertAll()
{
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasTopLevel(), return)
    const QString title = tr("Revert repository");
Tobias Hunger's avatar
Tobias Hunger committed
697
    if (QMessageBox::warning(0, title, tr("Revert all pending changes to the repository?"),
698
699
700
701
702
                             QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
        return;
    // NoteL: Svn "revert ." doesn not work.
    QStringList args;
    args << QLatin1String("revert") << QLatin1String("--recursive") << state.topLevel();
703
704
705
    const SubversionResponse revertResponse =
            runSvn(state.topLevel(), args, m_settings.timeOutMS(),
                   SshPasswordPrompt|ShowStdOutInLogWindow);
706
707
708
709
710
711
712
    if (revertResponse.error) {
        QMessageBox::warning(0, title, tr("Revert failed: %1").arg(revertResponse.message), QMessageBox::Ok);
    } else {
        subVersionControl()->emitRepositoryChanged(state.topLevel());
    }
}

con's avatar
con committed
713
714
void SubversionPlugin::revertCurrentFile()
{
715
716
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasFile(), return)
con's avatar
con committed
717
718

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

721
722
    const SubversionResponse diffResponse =
            runSvn(state.currentFileTopLevel(), args, m_settings.timeOutMS(), 0);
con's avatar
con committed
723
724
725
726
727
    if (diffResponse.error)
        return;

    if (diffResponse.stdOut.isEmpty())
        return;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
728
    if (QMessageBox::warning(0, QLatin1String("svn revert"), tr("The file has been changed. Do you want to revert it?"),
con's avatar
con committed
729
730
731
                             QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
        return;

732

733
    Core::FileChangeBlocker fcb(state.currentFile());
con's avatar
con committed
734
735
736

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

739
740
741
742
    const SubversionResponse revertResponse =
            runSvn(state.currentFileTopLevel(), args, m_settings.timeOutMS(),
                   SshPasswordPrompt|ShowStdOutInLogWindow);

743
    if (!revertResponse.error) {
744
        subVersionControl()->emitFilesChanged(QStringList(state.currentFile()));
con's avatar
con committed
745
746
747
748
749
    }
}

void SubversionPlugin::diffProject()
{
750
751
752
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasProject(), return)
    svnDiff(state.currentProjectTopLevel(), state.relativeCurrentProject(), state.currentProjectName());
con's avatar
con committed
753
754
755
756
}

void SubversionPlugin::diffCurrentFile()
{
757
758
759
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasFile(), return)
    svnDiff(state.currentFileTopLevel(), QStringList(state.relativeCurrentFile()));
con's avatar
con committed
760
761
762
763
}

void SubversionPlugin::startCommitCurrentFile()
{
764
765
766
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasFile(), return)
    startCommit(state.currentFileTopLevel(), QStringList(state.relativeCurrentFile()));
con's avatar
con committed
767
768
769
770
}

void SubversionPlugin::startCommitAll()
{
771
772
773
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasTopLevel(), return);
    startCommit(state.topLevel());
con's avatar
con committed
774
775
}

776
777
778
779
780
781
782
void SubversionPlugin::startCommitProject()
{
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasProject(), return);
    startCommit(state.currentProjectPath());
}

con's avatar
con committed
783
784
785
/* Start commit of files of a single repository by displaying
 * template and files in a submit editor. On closing, the real
 * commit will start. */
786
void SubversionPlugin::startCommit(const QString &workingDir, const QStringList &files)
con's avatar
con committed
787
{
788
789
    if (VCSBase::VCSBaseSubmitEditor::raiseSubmitEditor())
        return;
790
    if (isCommitEditorOpen()) {
791
        VCSBase::VCSBaseOutputWindow::instance()->appendWarning(tr("Another commit is currently being executed."));
con's avatar
con committed
792
793
794
795
796
797
        return;
    }

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

798
799
    const SubversionResponse response =
            runSvn(workingDir, args, m_settings.timeOutMS(), 0);
con's avatar
con committed
800
801
    if (response.error)
        return;
802

con's avatar
con committed
803
    // Get list of added/modified/deleted files
804
    const StatusList statusOutput = parseStatusOutput(response.stdOut);
con's avatar
con committed
805
    if (statusOutput.empty()) {
806
        VCSBase::VCSBaseOutputWindow::instance()->appendWarning(tr("There are no modified files."));
con's avatar
con committed
807
808
        return;
    }
809
    m_commitRepository = workingDir;
con's avatar
con committed
810
    // Create a new submit change file containing the submit template
811
812
813
814
    QTemporaryFile changeTmpFile;
    changeTmpFile.setAutoRemove(false);
    if (!changeTmpFile.open()) {
        VCSBase::VCSBaseOutputWindow::instance()->appendError(tr("Cannot create temporary file: %1").arg(changeTmpFile.errorString()));
con's avatar
con committed
815
816
        return;
    }
817
    m_commitMessageFileName = changeTmpFile.fileName();
818
    // TODO: Regitctrieve submit template from
con's avatar
con committed
819
820
    const QString submitTemplate;
    // Create a submit
821
822
823
    changeTmpFile.write(submitTemplate.toUtf8());
    changeTmpFile.flush();
    changeTmpFile.close();
con's avatar
con committed
824
    // Create a submit editor and set file list
825
    SubversionSubmitEditor *editor = openSubversionSubmitEditor(m_commitMessageFileName);
826
    editor->setStatusList(statusOutput);
con's avatar
con committed
827
828
829
830
831
832
833
834
835
836
837
}

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"));
838
    args << QLatin1String(nonInteractiveOptionC) << QLatin1String("--file") << messageFile;
con's avatar
con committed
839
    args.append(subVersionFileList);
840
841
842
    const SubversionResponse response =
            runSvn(m_commitRepository, args, m_settings.longTimeOutMS(),
                   SshPasswordPrompt|ShowStdOutInLogWindow);
con's avatar
con committed
843
844
845
846
847
    return !response.error ;
}

void SubversionPlugin::filelogCurrentFile()
{
848
849
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasFile(), return)
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
   filelog(state.currentFileTopLevel(), QStringList(state.relativeCurrentFile()), true);
}

void SubversionPlugin::logProject()
{
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasProject(), return)
    filelog(state.currentProjectTopLevel(), state.relativeCurrentProject());
}

void SubversionPlugin::logRepository()
{
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasTopLevel(), return)
    filelog(state.topLevel());
con's avatar
con committed
865
866
}

867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
void SubversionPlugin::diffRepository()
{
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasTopLevel(), return)
    svnDiff(state.topLevel(), QStringList());
}

void SubversionPlugin::statusRepository()
{
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasTopLevel(), return)
    svnStatus(state.topLevel());
}

void SubversionPlugin::updateRepository()
{
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasTopLevel(), return)
    svnUpdate(state.topLevel());
}

void SubversionPlugin::svnStatus(const QString &workingDir, const QStringList &relativePaths)
{
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasTopLevel(), return)
    QStringList args(QLatin1String("status"));
    if (!relativePaths.isEmpty())
        args.append(relativePaths);
    VCSBase::VCSBaseOutputWindow *outwin = VCSBase::VCSBaseOutputWindow::instance();
    outwin->setRepository(workingDir);
897
    runSvn(workingDir, args, m_settings.timeOutMS(),
898
           ShowStdOutInLogWindow|ShowSuccessMessage);
899
900
901
    outwin->clearRepository();
}

902
903
904
void SubversionPlugin::filelog(const QString &workingDir,
                               const QStringList &files,
                               bool enableAnnotationContextMenu)
con's avatar
con committed
905
906
907
{
    // no need for temp file
    QStringList args(QLatin1String("log"));
908
909
    if (m_settings.logCount > 0)
        args << QLatin1String("-l") << QString::number(m_settings.logCount);
910
911
    foreach(const QString &file, files)
        args.append(QDir::toNativeSeparators(file));
con's avatar
con committed
912

Roman Kovalev's avatar
Roman Kovalev committed
913
914
    // subversion stores log in UTF-8 and returns it back in user system locale.
    // So we do not need to encode it.
915
916
    const SubversionResponse response =
            runSvn(workingDir, args, m_settings.timeOutMS(),
Roman Kovalev's avatar
Roman Kovalev committed
917
                   SshPasswordPrompt, 0/*codec*/);
con's avatar
con committed
918
919
920
921
922
923
    if (response.error)
        return;

    // Re-use an existing view if possible to support
    // the common usage pattern of continuously changing and diffing a file

924
    const QString id = VCSBase::VCSBaseEditorWidget::getTitleId(workingDir, files);
925
926
    const QString tag = VCSBase::VCSBaseEditorWidget::editorTag(VCSBase::LogOutput, workingDir, files);
    if (Core::IEditor *editor = VCSBase::VCSBaseEditorWidget::locateEditorByTag(tag)) {
con's avatar
con committed
927
        editor->createNew(response.stdOut);
928
        Core::EditorManager::instance()->activateEditor(editor, Core::EditorManager::ModeSwitch);
con's avatar
con committed
929
    } else {
930
        const QString title = QString::fromLatin1("svn log %1").arg(id);
931
        const QString source = VCSBase::VCSBaseEditorWidget::getSource(workingDir, files);
Roman Kovalev's avatar
Roman Kovalev committed
932
        Core::IEditor *newEditor = showOutputInEditor(title, response.stdOut, VCSBase::LogOutput, source, /*codec*/0);
933
        VCSBase::VCSBaseEditorWidget::tagEditor(newEditor, tag);
934
        if (enableAnnotationContextMenu)
935
            VCSBase::VCSBaseEditorWidget::getVcsBaseEditor(newEditor)->setFileLogAnnotateEnabled(true);
con's avatar
con committed
936
937
938
939
940
    }
}

void SubversionPlugin::updateProject()
{
941
942
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasProject(), return);
943
944
    svnUpdate(state.currentProjectTopLevel(), state.relativeCurrentProject());
}
con's avatar
con committed
945

946
947
void SubversionPlugin::svnUpdate(const QString &workingDir, const QStringList &relativePaths)
{
con's avatar
con committed
948
    QStringList args(QLatin1String("update"));
949
    args.push_back(QLatin1String(nonInteractiveOptionC));
950
951
    if (!relativePaths.isEmpty())
        args.append(relativePaths);
952
953
954
        const SubversionResponse response =
                runSvn(workingDir, args, m_settings.longTimeOutMS(),
                       SshPasswordPrompt|ShowStdOutInLogWindow);
955
    if (!response.error)
956
        subVersionControl()->emitRepositoryChanged(workingDir);
con's avatar
con committed
957
958
959
960
}

void SubversionPlugin::annotateCurrentFile()
{
961
962
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasFile(), return);
963
    vcsAnnotate(state.currentFileTopLevel(), state.relativeCurrentFile());
con's avatar
con committed
964
965
}

966
967
968
969
970
void SubversionPlugin::annotateVersion(const QString &file,
                                       const QString &revision,
                                       int lineNr)
{
    const QFileInfo fi(file);
971
    vcsAnnotate(fi.absolutePath(), fi.fileName(), revision, lineNr);
972
973
}

974
void SubversionPlugin::vcsAnnotate(const QString &workingDir, const QString &file,
975
976
                                const QString &revision /* = QString() */,
                                int lineNumber /* = -1 */)
con's avatar
con committed
977
{
978
979
    const QString source = VCSBase::VCSBaseEditorWidget::getSource(workingDir, file);
    QTextCodec *codec = VCSBase::VCSBaseEditorWidget::getCodec(source);
con's avatar
con committed
980
981

    QStringList args(QLatin1String("annotate"));
982
983
    if (m_settings.spaceIgnorantAnnotation)
        args << QLatin1String("-x") << QLatin1String("-uw");
984
985
    if (!revision.isEmpty())
        args << QLatin1String("-r") << revision;
con's avatar
con committed
986
987
988
    args.push_back(QLatin1String("-v"));
    args.append(QDir::toNativeSeparators(file));

989
990
    const SubversionResponse response =
            runSvn(workingDir, args, m_settings.timeOutMS(),
Roman Kovalev's avatar
Roman Kovalev committed
991
                   SshPasswordPrompt|ForceCLocale, codec);
con's avatar
con committed
992
993
994
995
996
    if (response.error)
        return;

    // Re-use an existing view if possible to support
    // the common usage pattern of continuously changing and diffing a file
997
    if (lineNumber <= 0)
998
        lineNumber = VCSBase::VCSBaseEditorWidget::lineNumberOfCurrentEditor(source);
999
    // Determine id
1000
1001
1002
1003
    const QStringList files = QStringList(file);
    const QString id = VCSBase::VCSBaseEditorWidget::getTitleId(workingDir, files, revision);
    const QString tag = VCSBase::VCSBaseEditorWidget::editorTag(VCSBase::AnnotateOutput, workingDir, files);
    if (Core::IEditor *editor = VCSBase::VCSBaseEditorWidget::locateEditorByTag(tag)) {
con's avatar
con committed
1004
        editor->createNew(response.stdOut);
1005
        VCSBase::VCSBaseEditorWidget::gotoLineOfEditor(editor, lineNumber);