cvsplugin.cpp 53.3 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
2
**
3
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
7
**
hjk's avatar
hjk committed
8
9
10
11
12
13
14
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
15
16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
18
19
20
21
22
23
24
25
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
con's avatar
con committed
26
27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
29
30
31
32
33

#include "cvsplugin.h"
#include "settingspage.h"
#include "cvseditor.h"
#include "cvssubmiteditor.h"
34
#include "cvsclient.h"
35
36
#include "cvsconstants.h"
#include "cvscontrol.h"
Friedemann Kleint's avatar
Friedemann Kleint committed
37
#include "checkoutwizard.h"
38
39

#include <vcsbase/basevcseditorfactory.h>
40
#include <vcsbase/vcsbaseconstants.h>
41
42
#include <vcsbase/vcsbaseeditor.h>
#include <vcsbase/basevcssubmiteditorfactory.h>
43
#include <vcsbase/vcsoutputwindow.h>
44
#include <vcsbase/vcsbaseeditorparameterwidget.h>
45
46
#include <utils/synchronousprocess.h>
#include <utils/parameteraction.h>
47
#include <utils/qtcassert.h>
48
49
50

#include <coreplugin/icore.h>
#include <coreplugin/coreconstants.h>
51
#include <coreplugin/documentmanager.h>
52
53
54
#include <coreplugin/messagemanager.h>
#include <coreplugin/mimedatabase.h>
#include <coreplugin/actionmanager/actionmanager.h>
55
56
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/command.h>
57
#include <coreplugin/id.h>
58
#include <coreplugin/editormanager/editormanager.h>
59
#include <coreplugin/locator/commandlocator.h>
60
#include <coreplugin/vcsmanager.h>
61
#include <utils/stringutils.h>
62
#include <utils/fileutils.h>
63

64
65
66
67
68
69
70
71
72
73
#include <QDebug>
#include <QDate>
#include <QDir>
#include <QFileInfo>
#include <QTextCodec>
#include <QtPlugin>
#include <QAction>
#include <QMainWindow>
#include <QMenu>
#include <QMessageBox>
74

75
76
77
78
#ifdef WITH_TESTS
#include <QTest>
#endif

hjk's avatar
hjk committed
79
using namespace Core;
80
81
using namespace VcsBase;
using namespace Utils;
hjk's avatar
hjk committed
82
83

namespace Cvs {
84
namespace Internal {
85
86
87

static inline QString msgCannotFindTopLevel(const QString &f)
{
88
    return CvsPlugin::tr("Cannot find repository for \"%1\"").
89
            arg(QDir::toNativeSeparators(f));
90
91
92
93
}

static inline QString msgLogParsingFailed()
{
hjk's avatar
hjk committed
94
    return CvsPlugin::tr("Parsing of the log output failed");
95
96
}

97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
const char CMD_ID_CVS_MENU[]           = "CVS.Menu";
const char CMD_ID_ADD[]                = "CVS.Add";
const char CMD_ID_DELETE_FILE[]        = "CVS.Delete";
const char CMD_ID_EDIT_FILE[]          = "CVS.EditFile";
const char CMD_ID_UNEDIT_FILE[]        = "CVS.UneditFile";
const char CMD_ID_UNEDIT_REPOSITORY[]  = "CVS.UneditRepository";
const char CMD_ID_REVERT[]             = "CVS.Revert";
const char CMD_ID_DIFF_PROJECT[]       = "CVS.DiffAll";
const char CMD_ID_DIFF_CURRENT[]       = "CVS.DiffCurrent";
const char CMD_ID_COMMIT_ALL[]         = "CVS.CommitAll";
const char CMD_ID_REVERT_ALL[]         = "CVS.RevertAll";
const char CMD_ID_COMMIT_CURRENT[]     = "CVS.CommitCurrent";
const char CMD_ID_FILELOG_CURRENT[]    = "CVS.FilelogCurrent";
const char CMD_ID_ANNOTATE_CURRENT[]   = "CVS.AnnotateCurrent";
const char CMD_ID_STATUS[]             = "CVS.Status";
const char CMD_ID_UPDATE[]             = "CVS.Update";
const char CMD_ID_PROJECTLOG[]         = "CVS.ProjectLog";
const char CMD_ID_PROJECTCOMMIT[]      = "CVS.ProjectCommit";
const char CMD_ID_REPOSITORYLOG[]      = "CVS.RepositoryLog";
const char CMD_ID_REPOSITORYDIFF[]     = "CVS.RepositoryDiff";
const char CMD_ID_REPOSITORYSTATUS[]   = "CVS.RepositoryStatus";
const char CMD_ID_REPOSITORYUPDATE[]   = "CVS.RepositoryUpdate";

const char CVS_SUBMIT_MIMETYPE[] = "text/vnd.qtcreator.cvs.submit";
const char CVSCOMMITEDITOR[]  = "CVS Commit Editor";
const char CVSCOMMITEDITOR_ID[]  = "CVS Commit Editor";
const char CVSCOMMITEDITOR_DISPLAY_NAME[]  = QT_TRANSLATE_NOOP("VCS", "CVS Commit Editor");
const char SUBMIT_CURRENT[] = "CVS.SubmitCurrentLog";
const char DIFF_SELECTED[] = "CVS.DiffSelectedFilesInLog";

const VcsBaseEditorParameters editorParameters[] = {
128
{
129
    OtherContent,
130
131
    "CVS Command Log Editor", // id
    QT_TRANSLATE_NOOP("VCS", "CVS Command Log Editor"), // display name
132
    "CVS Command Log Editor", // context
Orgad Shaneh's avatar
Orgad Shaneh committed
133
    "text/vnd.qtcreator.cvs.commandlog"},
hjk's avatar
hjk committed
134
{   LogOutput,
135
136
    "CVS File Log Editor",   // id
    QT_TRANSLATE_NOOP("VCS", "CVS File Log Editor"),   // display name
137
    "CVS File Log Editor",   // context
Orgad Shaneh's avatar
Orgad Shaneh committed
138
    "text/vnd.qtcreator.cvs.log"},
hjk's avatar
hjk committed
139
{    AnnotateOutput,
140
141
    "CVS Annotation Editor",  // id
    QT_TRANSLATE_NOOP("VCS", "CVS Annotation Editor"),  // display name
142
    "CVS Annotation Editor",  // context
Orgad Shaneh's avatar
Orgad Shaneh committed
143
    "text/vnd.qtcreator.cvs.annotation"},
hjk's avatar
hjk committed
144
{   DiffOutput,
145
146
    "CVS Diff Editor",  // id
    QT_TRANSLATE_NOOP("VCS", "CVS Diff Editor"),  // display name
147
    "CVS Diff Editor",  // context
148
    "text/x-patch"}
149
150
151
};

// Utility to find a parameter set by type
hjk's avatar
hjk committed
152
static inline const VcsBaseEditorParameters *findType(int ie)
153
{
hjk's avatar
hjk committed
154
    const EditorContentType et = static_cast<EditorContentType>(ie);
155
    return VcsBaseEditor::findType(editorParameters, sizeof(editorParameters) / sizeof(editorParameters[0]), et);
156
157
158
159
}

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

163
static inline bool messageBoxQuestion(const QString &title, const QString &question)
164
{
165
    return QMessageBox::question(ICore::dialogParent(), title, question, QMessageBox::Yes|QMessageBox::No) == QMessageBox::Yes;
166
167
}

168
// ------------- CVSPlugin
hjk's avatar
hjk committed
169
CvsPlugin *CvsPlugin::m_cvsPluginInstance = 0;
170

hjk's avatar
hjk committed
171
CvsPlugin::CvsPlugin() :
Friedemann Kleint's avatar
Friedemann Kleint committed
172
    m_commandLocator(0),
173
174
175
    m_addAction(0),
    m_deleteAction(0),
    m_revertAction(0),
176
177
178
    m_editCurrentAction(0),
    m_uneditCurrentAction(0),
    m_uneditRepositoryAction(0),
179
180
    m_diffProjectAction(0),
    m_diffCurrentAction(0),
181
182
    m_logProjectAction(0),
    m_logRepositoryAction(0),
183
    m_commitAllAction(0),
184
    m_revertRepositoryAction(0),
185
186
187
    m_commitCurrentAction(0),
    m_filelogCurrentAction(0),
    m_annotateCurrentAction(0),
188
    m_statusProjectAction(0),
189
    m_updateProjectAction(0),
190
191
192
193
    m_commitProjectAction(0),
    m_diffRepositoryAction(0),
    m_updateRepositoryAction(0),
    m_statusRepositoryAction(0),
194
195
196
197
    m_submitCurrentLogAction(0),
    m_submitDiffAction(0),
    m_submitUndoAction(0),
    m_submitRedoAction(0),
198
    m_menuAction(0),
199
200
201
202
    m_submitActionTriggered(false)
{
}

hjk's avatar
hjk committed
203
CvsPlugin::~CvsPlugin()
204
{
205
    delete m_client;
206
    cleanCommitMessageFile();
207
208
}

hjk's avatar
hjk committed
209
void CvsPlugin::cleanCommitMessageFile()
210
{
211
212
213
    if (!m_commitMessageFileName.isEmpty()) {
        QFile::remove(m_commitMessageFileName);
        m_commitMessageFileName.clear();
214
        m_commitRepository.clear();
215
216
    }
}
hjk's avatar
hjk committed
217
bool CvsPlugin::isCommitEditorOpen() const
218
219
220
{
    return !m_commitMessageFileName.isEmpty();
}
221

hjk's avatar
hjk committed
222
static const VcsBaseSubmitEditorParameters submitParameters = {
223
224
225
226
227
    CVS_SUBMIT_MIMETYPE,
    CVSCOMMITEDITOR_ID,
    CVSCOMMITEDITOR_DISPLAY_NAME,
    CVSCOMMITEDITOR,
    VcsBaseSubmitEditorParameters::DiffFiles
228
229
};

hjk's avatar
hjk committed
230
bool CvsPlugin::initialize(const QStringList &arguments, QString *errorMessage)
231
{
hjk's avatar
hjk committed
232
233
    Q_UNUSED(arguments);
    typedef VcsSubmitEditorFactory<CvsSubmitEditor> CVSSubmitEditorFactory;
234
235
236
237
    using namespace Constants;

    using namespace Core::Constants;
    using namespace ExtensionSystem;
238
    using Core::Command;
239

hjk's avatar
hjk committed
240
    initializeVcs(new CvsControl(this));
241

242
243
    m_cvsPluginInstance = this;

hjk's avatar
hjk committed
244
    if (!MimeDatabase::addMimeTypes(QLatin1String(":/trolltech.cvs/CVS.mimetypes.xml"), errorMessage))
245
246
        return false;

247
248
    m_settings.readSettings(ICore::settings());
    m_client = new CvsClient(&m_settings);
249
250
251
252
253
254

    addAutoReleasedObject(new SettingsPage);

    addAutoReleasedObject(new CVSSubmitEditorFactory(&submitParameters));

    static const char *describeSlotC = SLOT(slotDescribe(QString,QString));
hjk's avatar
hjk committed
255
    const int editorCount = sizeof(editorParameters) / sizeof(editorParameters[0]);
256
    const auto widgetCreator = []() { return new CvsEditorWidget; };
257
    for (int i = 0; i < editorCount; i++)
258
        addAutoReleasedObject(new VcsEditorFactory(editorParameters + i, widgetCreator, this, describeSlotC));
259

260
261
262
263
264
265
266
267
268
    auto checkoutWizardFactory = new BaseCheckoutWizardFactory;
    checkoutWizardFactory->setId(QLatin1String(VcsBase::Constants::VCS_ID_CVS));
    checkoutWizardFactory->setIcon(QIcon(QLatin1String(":/cvs/images/cvs.png")));
    checkoutWizardFactory->setDescription(tr("Checks out a CVS repository and tries to load the contained project."));
    checkoutWizardFactory->setDisplayName(tr("CVS Checkout"));
    checkoutWizardFactory->setWizardCreator([this] (const FileName &path, QWidget *parent) {
        return new CheckoutWizard(path, parent);
    });
    addAutoReleasedObject(checkoutWizardFactory);
Friedemann Kleint's avatar
Friedemann Kleint committed
269

Friedemann Kleint's avatar
Friedemann Kleint committed
270
    const QString prefix = QLatin1String("cvs");
271
    m_commandLocator = new CommandLocator("CVS", prefix, prefix);
Friedemann Kleint's avatar
Friedemann Kleint committed
272
273
    addAutoReleasedObject(m_commandLocator);

hjk's avatar
hjk committed
274
    // Register actions
275
    ActionContainer *toolsContainer = ActionManager::actionContainer(M_TOOLS);
276

277
    ActionContainer *cvsMenu = ActionManager::createMenu(Id(CMD_ID_CVS_MENU));
278
279
    cvsMenu->menu()->setTitle(tr("&CVS"));
    toolsContainer->addMenu(cvsMenu);
280
    m_menuAction = cvsMenu->menu()->menuAction();
281

hjk's avatar
hjk committed
282
    Context globalcontext(C_GLOBAL);
283

hjk's avatar
hjk committed
284
    Command *command;
285

286
287
    m_diffCurrentAction = new ParameterAction(tr("Diff Current File"), tr("Diff \"%1\""), ParameterAction::EnabledWithParameter, this);
    command = ActionManager::registerAction(m_diffCurrentAction,
288
        CMD_ID_DIFF_CURRENT, globalcontext);
hjk's avatar
hjk committed
289
    command->setAttribute(Command::CA_UpdateText);
290
    command->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+C,Meta+D") : tr("Alt+C,Alt+D")));
291
    connect(m_diffCurrentAction, SIGNAL(triggered()), this, SLOT(diffCurrentFile()));
292
    cvsMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
293
    m_commandLocator->appendCommand(command);
294

295
296
    m_filelogCurrentAction = new ParameterAction(tr("Filelog Current File"), tr("Filelog \"%1\""), ParameterAction::EnabledWithParameter, this);
    command = ActionManager::registerAction(m_filelogCurrentAction,
297
        CMD_ID_FILELOG_CURRENT, globalcontext);
hjk's avatar
hjk committed
298
    command->setAttribute(Command::CA_UpdateText);
299
300
    connect(m_filelogCurrentAction, SIGNAL(triggered()), this,
        SLOT(filelogCurrentFile()));
301
    cvsMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
302
    m_commandLocator->appendCommand(command);
303

304
305
    m_annotateCurrentAction = new ParameterAction(tr("Annotate Current File"), tr("Annotate \"%1\""), ParameterAction::EnabledWithParameter, this);
    command = ActionManager::registerAction(m_annotateCurrentAction,
306
        CMD_ID_ANNOTATE_CURRENT, globalcontext);
hjk's avatar
hjk committed
307
    command->setAttribute(Command::CA_UpdateText);
308
309
    connect(m_annotateCurrentAction, SIGNAL(triggered()), this,
        SLOT(annotateCurrentFile()));
310
    cvsMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
311
    m_commandLocator->appendCommand(command);
312

313
    cvsMenu->addSeparator(globalcontext);
314

315
316
    m_addAction = new ParameterAction(tr("Add"), tr("Add \"%1\""), ParameterAction::EnabledWithParameter, this);
    command = ActionManager::registerAction(m_addAction, CMD_ID_ADD,
317
        globalcontext);
hjk's avatar
hjk committed
318
    command->setAttribute(Command::CA_UpdateText);
319
    command->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+C,Meta+A") : tr("Alt+C,Alt+A")));
320
    connect(m_addAction, SIGNAL(triggered()), this, SLOT(addCurrentFile()));
321
    cvsMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
322
    m_commandLocator->appendCommand(command);
323

324
325
    m_commitCurrentAction = new ParameterAction(tr("Commit Current File"), tr("Commit \"%1\""), ParameterAction::EnabledWithParameter, this);
    command = ActionManager::registerAction(m_commitCurrentAction,
326
        CMD_ID_COMMIT_CURRENT, globalcontext);
hjk's avatar
hjk committed
327
    command->setAttribute(Command::CA_UpdateText);
328
    command->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+C,Meta+C") : tr("Alt+C,Alt+C")));
329
330
    connect(m_commitCurrentAction, SIGNAL(triggered()), this, SLOT(startCommitCurrentFile()));
    cvsMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
331
    m_commandLocator->appendCommand(command);
332

333
334
    m_deleteAction = new ParameterAction(tr("Delete..."), tr("Delete \"%1\"..."), ParameterAction::EnabledWithParameter, this);
    command = ActionManager::registerAction(m_deleteAction, CMD_ID_DELETE_FILE,
335
        globalcontext);
hjk's avatar
hjk committed
336
    command->setAttribute(Command::CA_UpdateText);
337
    connect(m_deleteAction, SIGNAL(triggered()), this, SLOT(promptToDeleteCurrentFile()));
338
    cvsMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
339
    m_commandLocator->appendCommand(command);
340

341
342
    m_revertAction = new ParameterAction(tr("Revert..."), tr("Revert \"%1\"..."), ParameterAction::EnabledWithParameter, this);
    command = ActionManager::registerAction(m_revertAction, CMD_ID_REVERT,
343
        globalcontext);
hjk's avatar
hjk committed
344
    command->setAttribute(Command::CA_UpdateText);
345
    connect(m_revertAction, SIGNAL(triggered()), this, SLOT(revertCurrentFile()));
346
    cvsMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
347
    m_commandLocator->appendCommand(command);
348

349
    cvsMenu->addSeparator(globalcontext);
350

351
352
    m_editCurrentAction = new ParameterAction(tr("Edit"), tr("Edit \"%1\""), ParameterAction::EnabledWithParameter, this);
    command = ActionManager::registerAction(m_editCurrentAction, CMD_ID_EDIT_FILE, globalcontext);
hjk's avatar
hjk committed
353
    command->setAttribute(Command::CA_UpdateText);
354
355
356
357
    connect(m_editCurrentAction, SIGNAL(triggered()), this, SLOT(editCurrentFile()));
    cvsMenu->addAction(command);
    m_commandLocator->appendCommand(command);

358
359
    m_uneditCurrentAction = new ParameterAction(tr("Unedit"), tr("Unedit \"%1\""), ParameterAction::EnabledWithParameter, this);
    command = ActionManager::registerAction(m_uneditCurrentAction, CMD_ID_UNEDIT_FILE, globalcontext);
hjk's avatar
hjk committed
360
    command->setAttribute(Command::CA_UpdateText);
361
362
363
364
365
    connect(m_uneditCurrentAction, SIGNAL(triggered()), this, SLOT(uneditCurrentFile()));
    cvsMenu->addAction(command);
    m_commandLocator->appendCommand(command);

    m_uneditRepositoryAction = new QAction(tr("Unedit Repository"), this);
366
    command = ActionManager::registerAction(m_uneditRepositoryAction, CMD_ID_UNEDIT_REPOSITORY, globalcontext);
367
368
369
370
    connect(m_uneditRepositoryAction, SIGNAL(triggered()), this, SLOT(uneditCurrentRepository()));
    cvsMenu->addAction(command);
    m_commandLocator->appendCommand(command);

371
    cvsMenu->addSeparator(globalcontext);
372

373
374
    m_diffProjectAction = new ParameterAction(tr("Diff Project"), tr("Diff Project \"%1\""), ParameterAction::EnabledWithParameter, this);
    command = ActionManager::registerAction(m_diffProjectAction, CMD_ID_DIFF_PROJECT,
375
        globalcontext);
hjk's avatar
hjk committed
376
    command->setAttribute(Command::CA_UpdateText);
377
378
    connect(m_diffProjectAction, SIGNAL(triggered()), this, SLOT(diffProject()));
    cvsMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
379
    m_commandLocator->appendCommand(command);
380

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

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

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

403
404
    m_commitProjectAction = new ParameterAction(tr("Commit Project"), tr("Commit Project \"%1\""), ParameterAction::EnabledWithParameter, this);
    command = ActionManager::registerAction(m_commitProjectAction, CMD_ID_PROJECTCOMMIT, globalcontext);
hjk's avatar
hjk committed
405
    command->setAttribute(Command::CA_UpdateText);
406
407
408
409
    connect(m_commitProjectAction, SIGNAL(triggered()), this, SLOT(commitProject()));
    cvsMenu->addAction(command);
    m_commandLocator->appendCommand(command);

410
    cvsMenu->addSeparator(globalcontext);
411
412

    m_diffRepositoryAction = new QAction(tr("Diff Repository"), this);
413
    command = ActionManager::registerAction(m_diffRepositoryAction, CMD_ID_REPOSITORYDIFF, globalcontext);
414
415
416
417
418
    connect(m_diffRepositoryAction, SIGNAL(triggered()), this, SLOT(diffRepository()));
    cvsMenu->addAction(command);
    m_commandLocator->appendCommand(command);

    m_statusRepositoryAction = new QAction(tr("Repository Status"), this);
419
    command = ActionManager::registerAction(m_statusRepositoryAction, CMD_ID_REPOSITORYSTATUS, globalcontext);
420
421
422
    connect(m_statusRepositoryAction, SIGNAL(triggered()), this, SLOT(statusRepository()));
    cvsMenu->addAction(command);
    m_commandLocator->appendCommand(command);
423

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

430
    m_updateRepositoryAction = new QAction(tr("Update Repository"), this);
431
    command = ActionManager::registerAction(m_updateRepositoryAction, CMD_ID_REPOSITORYUPDATE, globalcontext);
432
433
434
435
    connect(m_updateRepositoryAction, SIGNAL(triggered()), this, SLOT(updateRepository()));
    cvsMenu->addAction(command);
    m_commandLocator->appendCommand(command);

436
    m_commitAllAction = new QAction(tr("Commit All Files"), this);
437
    command = ActionManager::registerAction(m_commitAllAction, CMD_ID_COMMIT_ALL,
438
439
440
        globalcontext);
    connect(m_commitAllAction, SIGNAL(triggered()), this, SLOT(startCommitAll()));
    cvsMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
441
    m_commandLocator->appendCommand(command);
442
443

    m_revertRepositoryAction = new QAction(tr("Revert Repository..."), this);
444
445
    command = ActionManager::registerAction(m_revertRepositoryAction, CMD_ID_REVERT_ALL,
                             globalcontext);
446
447
    connect(m_revertRepositoryAction, SIGNAL(triggered()), this, SLOT(revertAll()));
    cvsMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
448
    m_commandLocator->appendCommand(command);
449

450
    // Actions of the submit editor
451
    Context cvscommitcontext(CVSCOMMITEDITOR);
452

hjk's avatar
hjk committed
453
    m_submitCurrentLogAction = new QAction(VcsBaseSubmitEditor::submitIcon(), tr("Commit"), this);
454
    command = ActionManager::registerAction(m_submitCurrentLogAction, SUBMIT_CURRENT, cvscommitcontext);
hjk's avatar
hjk committed
455
    command->setAttribute(Command::CA_UpdateText);
456
457
    connect(m_submitCurrentLogAction, SIGNAL(triggered()), this, SLOT(submitCurrentLog()));

hjk's avatar
hjk committed
458
    m_submitDiffAction = new QAction(VcsBaseSubmitEditor::diffIcon(), tr("Diff &Selected Files"), this);
459
    command = ActionManager::registerAction(m_submitDiffAction , DIFF_SELECTED, cvscommitcontext);
460
461

    m_submitUndoAction = new QAction(tr("&Undo"), this);
462
    command = ActionManager::registerAction(m_submitUndoAction, Core::Constants::UNDO, cvscommitcontext);
463
464

    m_submitRedoAction = new QAction(tr("&Redo"), this);
465
    command = ActionManager::registerAction(m_submitRedoAction, Core::Constants::REDO, cvscommitcontext);
466
467
468
    return true;
}

469
bool CvsPlugin::submitEditorAboutToClose()
470
{
471
    if (!isCommitEditorOpen())
472
473
        return true;

474
475
476
477
    CvsSubmitEditor *editor = qobject_cast<CvsSubmitEditor *>(submitEditor());
    QTC_ASSERT(editor, return true);
    IDocument *editorDocument = editor->document();
    QTC_ASSERT(editorDocument, return true);
478
479
480

    // Submit editor closing. Make it write out the commit message
    // and retrieve files
481
    const QFileInfo editorFile(editorDocument->filePath());
482
    const QFileInfo changeFile(m_commitMessageFileName);
483
484
485
486
487
    if (editorFile.absoluteFilePath() != changeFile.absoluteFilePath())
        return true; // Oops?!

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

hjk's avatar
hjk committed
519
void CvsPlugin::diffCommitFiles(const QStringList &files)
520
{
521
    m_client->diff(m_commitRepository, files);
522
523
}

hjk's avatar
hjk committed
524
static void setDiffBaseDirectory(IEditor *editor, const QString &db)
525
{
hjk's avatar
hjk committed
526
    if (VcsBaseEditorWidget *ve = qobject_cast<VcsBaseEditorWidget*>(editor->widget()))
527
        ve->setWorkingDirectory(db);
528
}
529

hjk's avatar
hjk committed
530
CvsSubmitEditor *CvsPlugin::openCVSSubmitEditor(const QString &fileName)
531
{
532
    IEditor *editor = EditorManager::openEditor(fileName, CVSCOMMITEDITOR_ID);
hjk's avatar
hjk committed
533
    CvsSubmitEditor *submitEditor = qobject_cast<CvsSubmitEditor*>(editor);
534
    QTC_CHECK(submitEditor);
535
    submitEditor->registerActions(m_submitUndoAction, m_submitRedoAction, m_submitCurrentLogAction, m_submitDiffAction);
536
    connect(submitEditor, SIGNAL(diffSelectedFiles(QStringList)), this, SLOT(diffCommitFiles(QStringList)));
537
538
539
540

    return submitEditor;
}

hjk's avatar
hjk committed
541
void CvsPlugin::updateActions(VcsBasePlugin::ActionState as)
542
{
Friedemann Kleint's avatar
Friedemann Kleint committed
543
544
    if (!enableMenuAction(as, m_menuAction)) {
        m_commandLocator->setEnabled(false);
545
        return;
Friedemann Kleint's avatar
Friedemann Kleint committed
546
    }
547

Friedemann Kleint's avatar
Friedemann Kleint committed
548
549
    const bool hasTopLevel = currentState().hasTopLevel();
    m_commandLocator->setEnabled(hasTopLevel);
550

551
552
553
554
555
556
557
558
    const QString currentFileName = currentState().currentFileName();
    m_addAction->setParameter(currentFileName);
    m_deleteAction->setParameter(currentFileName);
    m_revertAction->setParameter(currentFileName);
    m_diffCurrentAction->setParameter(currentFileName);
    m_commitCurrentAction->setParameter(currentFileName);
    m_filelogCurrentAction->setParameter(currentFileName);
    m_annotateCurrentAction->setParameter(currentFileName);
559
560
    m_editCurrentAction->setParameter(currentFileName);
    m_uneditCurrentAction->setParameter(currentFileName);
561
562
563
564
565

    const QString currentProjectName = currentState().currentProjectName();
    m_diffProjectAction->setParameter(currentProjectName);
    m_statusProjectAction->setParameter(currentProjectName);
    m_updateProjectAction->setParameter(currentProjectName);
566
    m_logProjectAction->setParameter(currentProjectName);
567
    m_commitProjectAction->setParameter(currentProjectName);
568

569
570
571
    m_diffRepositoryAction->setEnabled(hasTopLevel);
    m_statusRepositoryAction->setEnabled(hasTopLevel);
    m_updateRepositoryAction->setEnabled(hasTopLevel);
Friedemann Kleint's avatar
Friedemann Kleint committed
572
    m_commitAllAction->setEnabled(hasTopLevel);
573
574
    m_logRepositoryAction->setEnabled(hasTopLevel);
    m_uneditRepositoryAction->setEnabled(hasTopLevel);
575
576
}

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

hjk's avatar
hjk committed
584
void CvsPlugin::revertAll()
585
{
hjk's avatar
hjk committed
586
    const VcsBasePluginState state = currentState();
587
    QTC_ASSERT(state.hasTopLevel(), return);
588
    const QString title = tr("Revert repository");
Tobias Hunger's avatar
Tobias Hunger committed
589
    if (!messageBoxQuestion(title, tr("Revert all pending changes to the repository?")))
590
591
592
        return;
    QStringList args;
    args << QLatin1String("update") << QLatin1String("-C") << state.topLevel();
hjk's avatar
hjk committed
593
    const CvsResponse revertResponse =
594
            runCvs(state.topLevel(), args, m_settings.timeOutMs(),
595
                   SshPasswordPrompt|ShowStdOutInLogWindow);
596
    if (revertResponse.result == CvsResponse::Ok)
597
        cvsVersionControl()->emitRepositoryChanged(state.topLevel());
598
    else
599
600
        QMessageBox::warning(ICore::dialogParent(), title,
                             tr("Revert failed: %1").arg(revertResponse.message), QMessageBox::Ok);
601
602
}

hjk's avatar
hjk committed
603
void CvsPlugin::revertCurrentFile()
604
{
hjk's avatar
hjk committed
605
    const VcsBasePluginState state = currentState();
606
    QTC_ASSERT(state.hasFile(), return);
607
608
    QStringList args;
    args << QLatin1String("diff") << state.relativeCurrentFile();
hjk's avatar
hjk committed
609
    const CvsResponse diffResponse =
610
            runCvs(state.currentFileTopLevel(), args, m_settings.timeOutMs(), 0);
611
    switch (diffResponse.result) {
hjk's avatar
hjk committed
612
    case CvsResponse::Ok:
613
        return; // Not modified, diff exit code 0
hjk's avatar
hjk committed
614
    case CvsResponse::NonNullExitCode: // Diff exit code != 0
615
616
617
        if (diffResponse.stdOut.isEmpty()) // Paranoia: Something else failed?
            return;
        break;
hjk's avatar
hjk committed
618
    case CvsResponse::OtherError:
619
620
621
        return;
    }

622
623
    if (!messageBoxQuestion(QLatin1String("CVS Revert"),
                            tr("The file has been changed. Do you want to revert it?")))
624
625
        return;

hjk's avatar
hjk committed
626
    FileChangeBlocker fcb(state.currentFile());
627
628

    // revert
629
630
    args.clear();
    args << QLatin1String("update") << QLatin1String("-C") << state.relativeCurrentFile();
hjk's avatar
hjk committed
631
    const CvsResponse revertResponse =
632
            runCvs(state.currentFileTopLevel(), args, m_settings.timeOutMs(),
633
                   SshPasswordPrompt|ShowStdOutInLogWindow);
634
    if (revertResponse.result == CvsResponse::Ok)
635
        cvsVersionControl()->emitFilesChanged(QStringList(state.currentFile()));
636
637
}

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

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

hjk's avatar
hjk committed
654
void CvsPlugin::startCommitCurrentFile()
655
{
hjk's avatar
hjk committed
656
    const VcsBasePluginState state = currentState();
657
    QTC_ASSERT(state.hasFile(), return);
658
    startCommit(state.currentFileTopLevel(), state.relativeCurrentFile());
659
660
}

hjk's avatar
hjk committed
661
void CvsPlugin::startCommitAll()
662
{
hjk's avatar
hjk committed
663
    const VcsBasePluginState state = currentState();
664
    QTC_ASSERT(state.hasTopLevel(), return);
665
    startCommit(state.topLevel());
666
667
668
669
670
}

/* Start commit of files of a single repository by displaying
 * template and files in a submit editor. On closing, the real
 * commit will start. */
671
void CvsPlugin::startCommit(const QString &workingDir, const QString &file)
672
{
673
    if (raiseSubmitEditor())
674
        return;
675
    if (isCommitEditorOpen()) {
676
        VcsOutputWindow::appendWarning(tr("Another commit is currently being executed."));
677
678
        return;
    }
679

680
681
    // We need the "Examining <subdir>" stderr output to tell
    // where we are, so, have stdout/stderr channels merged.
682
    QStringList args = QStringList(QLatin1String("status"));
hjk's avatar
hjk committed
683
    const CvsResponse response =
684
            runCvs(workingDir, args, m_settings.timeOutMs(), MergeOutputChannels);
hjk's avatar
hjk committed
685
    if (response.result != CvsResponse::Ok)
686
        return;
687
688
689
    // Get list of added/modified/deleted files and purge out undesired ones
    // (do not run status with relative arguments as it will omit the directories)
    StateList statusOutput = parseStatusOutput(QString(), response.stdOut);
690
    if (!file.isEmpty()) {
691
        for (StateList::iterator it = statusOutput.begin(); it != statusOutput.end() ; ) {
692
            if (file == it->second)
693
                ++it;
694
            else
695
696
697
                it = statusOutput.erase(it);
        }
    }
698
    if (statusOutput.empty()) {
699
        VcsOutputWindow::appendWarning(tr("There are no modified files."));
700
701
        return;
    }
702
    m_commitRepository = workingDir;
703
704

    // Create a new submit change file containing the submit template
705
    TempFileSaver saver;
706
    saver.setAutoRemove(false);
707
708
709
    // TODO: Retrieve submit template from
    const QString submitTemplate;
    // Create a submit
710
711
    saver.write(submitTemplate.toUtf8());
    if (!saver.finalize()) {
712
        VcsOutputWindow::appendError(saver.errorString());
713
714