subversionplugin.cpp 57.4 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>
50
#include <utils/fileutils.h>
con's avatar
con committed
51
52
53
54
55
56

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

#include <locator/commandlocator.h>

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

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

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

87
88
89
90
91
92
93
94
95
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";
96
static const char * const CMD_ID_REVERT_ALL         = "Subversion.RevertAll";
97
98
99
100
101
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";
102
static const char * const CMD_ID_SEPARATOR4         = "Subversion.Separator4";
103
static const char * const CMD_ID_STATUS             = "Subversion.Status";
104
105
static const char * const CMD_ID_PROJECTLOG         = "Subversion.ProjectLog";
static const char * const CMD_ID_REPOSITORYLOG      = "Subversion.RepositoryLog";
106
107
108
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";
109
static const char * const CMD_ID_UPDATE             = "Subversion.Update";
110
static const char * const CMD_ID_COMMIT_PROJECT     = "Subversion.CommitProject";
111
static const char * const CMD_ID_DESCRIBE           = "Subversion.Describe";
con's avatar
con committed
112

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

115
116


con's avatar
con committed
117
118
119
static const VCSBase::VCSBaseEditorParameters editorParameters[] = {
{
    VCSBase::RegularCommandOutput,
120
121
    "Subversion Command Log Editor", // id
    QT_TRANSLATE_NOOP("VCS", "Subversion Command Log Editor"), // display name
122
    "Subversion Command Log Editor", // context
con's avatar
con committed
123
124
125
    "application/vnd.nokia.text.scs_svn_commandlog",
    "scslog"},
{   VCSBase::LogOutput,
126
127
    "Subversion File Log Editor",   // id
    QT_TRANSLATE_NOOP("VCS", "Subversion File Log Editor"),   // display_name
128
    "Subversion File Log Editor",   // context
con's avatar
con committed
129
130
131
    "application/vnd.nokia.text.scs_svn_filelog",
    "scsfilelog"},
{    VCSBase::AnnotateOutput,
132
133
    "Subversion Annotation Editor",  // id
    QT_TRANSLATE_NOOP("VCS", "Subversion Annotation Editor"),   // display_name
134
    "Subversion Annotation Editor",  // context
con's avatar
con committed
135
136
137
    "application/vnd.nokia.text.scs_svn_annotation",
    "scsannotate"},
{   VCSBase::DiffOutput,
138
139
    "Subversion Diff Editor",  // id
    QT_TRANSLATE_NOOP("VCS", "Subversion Diff Editor"),   // display_name
140
    "Subversion Diff Editor",  // context
con's avatar
con committed
141
142
143
144
145
146
147
    "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);
148
    return  VCSBase::VCSBaseEditorWidget::findType(editorParameters, sizeof(editorParameters)/sizeof(VCSBase::VCSBaseEditorParameters), et);
con's avatar
con committed
149
150
151
152
153
154
155
}

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

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

        }
    }
    return changeSet;
}

179
180
181
182
183
184
185
186
187
188
189
// 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
190
191
192
193
// ------------- SubversionPlugin
SubversionPlugin *SubversionPlugin::m_subversionPluginInstance = 0;

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

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

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

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

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

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

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

    using namespace Core::Constants;
    using namespace ExtensionSystem;

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

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

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

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

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

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

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

290
291
    addAutoReleasedObject(new CheckoutWizard);

Friedemann Kleint's avatar
Friedemann Kleint committed
292
293
294
295
296
    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
297
    //register actions
hjk's avatar
hjk committed
298
    Core::ActionManager *ami = core->actionManager();
299
    Core::ActionContainer *toolsContainer = ami->actionContainer(M_TOOLS);
con's avatar
con committed
300

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

    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
312
    command->setAttribute(Core::Command::CA_UpdateText);
313
314
    command->setDefaultKeySequence(QKeySequence(tr("Alt+S,Alt+D")));
    connect(m_diffCurrentAction, SIGNAL(triggered()), this, SLOT(diffCurrentFile()));
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

318
319
320
    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
321
    command->setAttribute(Core::Command::CA_UpdateText);
322
323
    connect(m_filelogCurrentAction, SIGNAL(triggered()), this,
        SLOT(filelogCurrentFile()));
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
328
329
    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
330
    command->setAttribute(Core::Command::CA_UpdateText);
331
332
    connect(m_annotateCurrentAction, SIGNAL(triggered()), this,
        SLOT(annotateCurrentFile()));
con's avatar
con committed
333
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
334
    m_commandLocator->appendCommand(command);
con's avatar
con committed
335

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

338
339
    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
340
        globalcontext);
341
    command->setAttribute(Core::Command::CA_UpdateText);
342
343
    command->setDefaultKeySequence(QKeySequence(tr("Alt+S,Alt+A")));
    connect(m_addAction, SIGNAL(triggered()), this, SLOT(addCurrentFile()));
con's avatar
con committed
344
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
345
    m_commandLocator->appendCommand(command);
con's avatar
con committed
346

347
    m_commitCurrentAction = new Utils::ParameterAction(tr("Commit Current File"), tr("Commit \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
con's avatar
con committed
348
    command = ami->registerAction(m_commitCurrentAction,
349
        CMD_ID_COMMIT_CURRENT, globalcontext);
con's avatar
con committed
350
    command->setAttribute(Core::Command::CA_UpdateText);
con's avatar
con committed
351
352
353
    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
354
    m_commandLocator->appendCommand(command);
con's avatar
con committed
355

356
357
358
    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
359
    command->setAttribute(Core::Command::CA_UpdateText);
360
    connect(m_deleteAction, SIGNAL(triggered()), this, SLOT(promptToDeleteCurrentFile()));
con's avatar
con committed
361
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
362
    m_commandLocator->appendCommand(command);
con's avatar
con committed
363

364
365
366
    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
367
    command->setAttribute(Core::Command::CA_UpdateText);
368
    connect(m_revertAction, SIGNAL(triggered()), this, SLOT(revertCurrentFile()));
con's avatar
con committed
369
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
370
    m_commandLocator->appendCommand(command);
con's avatar
con committed
371

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

374
375
376
377
378
379
    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
380
    m_commandLocator->appendCommand(command);
con's avatar
con committed
381

382
383
    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
384
        globalcontext);
385
386
    command->setAttribute(Core::Command::CA_UpdateText);
    connect(m_statusProjectAction, SIGNAL(triggered()), this, SLOT(projectStatus()));
con's avatar
con committed
387
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
388
    m_commandLocator->appendCommand(command);
con's avatar
con committed
389

Friedemann Kleint's avatar
Friedemann Kleint committed
390
    m_logProjectAction = new Utils::ParameterAction(tr("Log Project"), tr("Log Project \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
391
392
393
394
    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
395
    m_commandLocator->appendCommand(command);
396

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

404
405
406
407
408
409
410
    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);

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

413
414
415
416
417
418
419
420
421
422
423
424
425
    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);
426
427
428
    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
429
    m_commandLocator->appendCommand(command);
430

431
432
433
    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()));
434
    subversionMenu->addAction(command);
435
    m_commandLocator->appendCommand(command);
436
437
438
439
440
441

    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
442
    m_commandLocator->appendCommand(command);
443

444
445
446
447
448
    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);

449
450
451
452
453
    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
454
    m_commandLocator->appendCommand(command);
455

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

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

464
    m_submitDiffAction = new QAction(VCSBase::VCSBaseSubmitEditor::diffIcon(), tr("Diff &Selected Files"), this);
con's avatar
con committed
465
466
467
468
469
470
471
472
473
474
475
    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;
}

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

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

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

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

525
void SubversionPlugin::diffCommitFiles(const QStringList &files)
con's avatar
con committed
526
{
527
    svnDiff(m_commitRepository, files);
con's avatar
con committed
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
574
// 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);
}

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

581
void SubversionPlugin::svnDiff(const QString &workingDir, const QStringList &files, QString diffname)
582
583
584
585
586
587
588
589
590
{
    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
591
592
{
    if (Subversion::Constants::debug)
593
594
        qDebug() << Q_FUNC_INFO << p.files << p.diffName;
    const QString source = VCSBase::VCSBaseEditorWidget::getSource(p.workingDir, p.files);
595
    QTextCodec *codec = source.isEmpty() ? static_cast<QTextCodec *>(0) : VCSBase::VCSBaseEditorWidget::getCodec(source);
con's avatar
con committed
596

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

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

604
    const SubversionResponse response =
605
            runSvn(p.workingDir, args, m_settings.timeOutMS(), 0, codec);
con's avatar
con committed
606
607
608
609
610
    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
611
612
613
614
615
616
617
    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
618
    }
619
    const QString title = QString::fromLatin1("svn diff %1").arg(diffName);
con's avatar
con committed
620
    Core::IEditor *editor = showOutputInEditor(title, response.stdOut, VCSBase::DiffOutput, source, codec);
621
    setDiffBaseDirectory(editor, p.workingDir);
622
    VCSBase::VCSBaseEditorWidget::tagEditor(editor, tag);
623
624
625
626
627
628
629
630
631
632
633
634
    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
635
636
637
638
}

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

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

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

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

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

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

693
694
695
696
697
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
698
    if (QMessageBox::warning(0, title, tr("Revert all pending changes to the repository?"),
699
700
701
702
703
                             QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
        return;
    // NoteL: Svn "revert ." doesn not work.
    QStringList args;
    args << QLatin1String("revert") << QLatin1String("--recursive") << state.topLevel();
704
705
706
    const SubversionResponse revertResponse =
            runSvn(state.topLevel(), args, m_settings.timeOutMS(),
                   SshPasswordPrompt|ShowStdOutInLogWindow);
707
708
709
710
711
712
713
    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
714
715
void SubversionPlugin::revertCurrentFile()
{
716
717
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasFile(), return)
con's avatar
con committed
718
719

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

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

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

733

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

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

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

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

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

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

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

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

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

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

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

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

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

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

void SubversionPlugin::filelogCurrentFile()
{
847
848
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasFile(), return)
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
   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
864
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
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);
896
    runSvn(workingDir, args, m_settings.timeOutMS(),
897
           ShowStdOutInLogWindow|ShowSuccessMessage);
898
899
900
    outwin->clearRepository();
}

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

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

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

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

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

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

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

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

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

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

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

    // Re-use an existing view if possible to support
    // the common usage pattern of continuously changing and diffing a file
996
    if (lineNumber <= 0)
997
        lineNumber = VCSBase::VCSBaseEditorWidget::lineNumberOfCurrentEditor(source);
998
    // Determine id
999
1000
1001
1002
    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
1003
        editor->createNew(response.stdOut);
1004
        VCSBase::VCSBaseEditorWidget::gotoLineOfEditor(editor, lineNumber);
1005
        Core::EditorManager::instance()->activateEditor(editor, Core::EditorManager::ModeSwitch);
con's avatar
con committed
1006
    } else {