cvsplugin.cpp 53.5 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/vcsbaseoutputwindow.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 VcsBaseEditorWidget::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 CvsEditor; };
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()) {
hjk's avatar
hjk committed
676
        VcsBaseOutputWindow::instance()->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;
Friedemann Kleint's avatar