cvsplugin.cpp 53.1 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
40
41

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

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

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

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

hjk's avatar
hjk committed
78
79
80
81
using namespace VcsBase;
using namespace Core;

namespace Cvs {
82
namespace Internal {
83
84
85

static inline QString msgCannotFindTopLevel(const QString &f)
{
hjk's avatar
hjk committed
86
    return CvsPlugin::tr("Cannot find repository for '%1'").
87
            arg(QDir::toNativeSeparators(f));
88
89
90
91
}

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

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

hjk's avatar
hjk committed
118
static const VcsBaseEditorParameters editorParameters[] = {
119
{
120
    OtherContent,
121
122
    "CVS Command Log Editor", // id
    QT_TRANSLATE_NOOP("VCS", "CVS Command Log Editor"), // display name
123
    "CVS Command Log Editor", // context
Orgad Shaneh's avatar
Orgad Shaneh committed
124
    "text/vnd.qtcreator.cvs.commandlog"},
hjk's avatar
hjk committed
125
{   LogOutput,
126
127
    "CVS File Log Editor",   // id
    QT_TRANSLATE_NOOP("VCS", "CVS File Log Editor"),   // display name
128
    "CVS File Log Editor",   // context
Orgad Shaneh's avatar
Orgad Shaneh committed
129
    "text/vnd.qtcreator.cvs.log"},
hjk's avatar
hjk committed
130
{    AnnotateOutput,
131
132
    "CVS Annotation Editor",  // id
    QT_TRANSLATE_NOOP("VCS", "CVS Annotation Editor"),  // display name
133
    "CVS Annotation Editor",  // context
Orgad Shaneh's avatar
Orgad Shaneh committed
134
    "text/vnd.qtcreator.cvs.annotation"},
hjk's avatar
hjk committed
135
{   DiffOutput,
136
137
    "CVS Diff Editor",  // id
    QT_TRANSLATE_NOOP("VCS", "CVS Diff Editor"),  // display name
138
    "CVS Diff Editor",  // context
139
    "text/x-patch"}
140
141
142
};

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

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

154
155
156
157
158
static inline bool messageBoxQuestion(const QString &title, const QString &question, QWidget *parent = 0)
{
    return QMessageBox::question(parent, title, question, QMessageBox::Yes|QMessageBox::No) == QMessageBox::Yes;
}

159
// ------------- CVSPlugin
hjk's avatar
hjk committed
160
CvsPlugin *CvsPlugin::m_cvsPluginInstance = 0;
161

hjk's avatar
hjk committed
162
CvsPlugin::CvsPlugin() :
Friedemann Kleint's avatar
Friedemann Kleint committed
163
    m_commandLocator(0),
164
165
166
    m_addAction(0),
    m_deleteAction(0),
    m_revertAction(0),
167
168
169
    m_editCurrentAction(0),
    m_uneditCurrentAction(0),
    m_uneditRepositoryAction(0),
170
171
    m_diffProjectAction(0),
    m_diffCurrentAction(0),
172
173
    m_logProjectAction(0),
    m_logRepositoryAction(0),
174
    m_commitAllAction(0),
175
    m_revertRepositoryAction(0),
176
177
178
    m_commitCurrentAction(0),
    m_filelogCurrentAction(0),
    m_annotateCurrentAction(0),
179
    m_statusProjectAction(0),
180
    m_updateProjectAction(0),
181
182
183
184
    m_commitProjectAction(0),
    m_diffRepositoryAction(0),
    m_updateRepositoryAction(0),
    m_statusRepositoryAction(0),
185
186
187
188
    m_submitCurrentLogAction(0),
    m_submitDiffAction(0),
    m_submitUndoAction(0),
    m_submitRedoAction(0),
189
    m_menuAction(0),
190
191
192
193
    m_submitActionTriggered(false)
{
}

hjk's avatar
hjk committed
194
CvsPlugin::~CvsPlugin()
195
{
196
    delete m_client;
197
    cleanCommitMessageFile();
198
199
}

hjk's avatar
hjk committed
200
void CvsPlugin::cleanCommitMessageFile()
201
{
202
203
204
    if (!m_commitMessageFileName.isEmpty()) {
        QFile::remove(m_commitMessageFileName);
        m_commitMessageFileName.clear();
205
        m_commitRepository.clear();
206
207
    }
}
hjk's avatar
hjk committed
208
bool CvsPlugin::isCommitEditorOpen() const
209
210
211
{
    return !m_commitMessageFileName.isEmpty();
}
212

hjk's avatar
hjk committed
213
214
215
216
static const VcsBaseSubmitEditorParameters submitParameters = {
    Constants::CVS_SUBMIT_MIMETYPE,
    Constants::CVSCOMMITEDITOR_ID,
    Constants::CVSCOMMITEDITOR_DISPLAY_NAME,
217
218
    Constants::CVSCOMMITEDITOR,
    VcsBase::VcsBaseSubmitEditorParameters::DiffFiles
219
220
};

hjk's avatar
hjk committed
221
bool CvsPlugin::initialize(const QStringList &arguments, QString *errorMessage)
222
{
hjk's avatar
hjk committed
223
224
225
    Q_UNUSED(arguments);
    typedef VcsSubmitEditorFactory<CvsSubmitEditor> CVSSubmitEditorFactory;
    typedef VcsEditorFactory<CvsEditor> CVSEditorFactory;
226
227
228
229
    using namespace Constants;

    using namespace Core::Constants;
    using namespace ExtensionSystem;
230
    using Core::Command;
231

hjk's avatar
hjk committed
232
    initializeVcs(new CvsControl(this));
233

234
235
    m_cvsPluginInstance = this;

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

239
240
    m_settings.readSettings(ICore::settings());
    m_client = new CvsClient(&m_settings);
241
242
243
244
245
246

    addAutoReleasedObject(new SettingsPage);

    addAutoReleasedObject(new CVSSubmitEditorFactory(&submitParameters));

    static const char *describeSlotC = SLOT(slotDescribe(QString,QString));
hjk's avatar
hjk committed
247
    const int editorCount = sizeof(editorParameters) / sizeof(editorParameters[0]);
248
249
250
    for (int i = 0; i < editorCount; i++)
        addAutoReleasedObject(new CVSEditorFactory(editorParameters + i, this, describeSlotC));

Friedemann Kleint's avatar
Friedemann Kleint committed
251
252
    addAutoReleasedObject(new CheckoutWizard);

Friedemann Kleint's avatar
Friedemann Kleint committed
253
    const QString prefix = QLatin1String("cvs");
254
    m_commandLocator = new Core::CommandLocator("CVS", prefix, prefix);
Friedemann Kleint's avatar
Friedemann Kleint committed
255
256
    addAutoReleasedObject(m_commandLocator);

hjk's avatar
hjk committed
257
    // Register actions
Eike Ziller's avatar
Eike Ziller committed
258
    ActionContainer *toolsContainer = Core::ActionManager::actionContainer(M_TOOLS);
259

Eike Ziller's avatar
Eike Ziller committed
260
    ActionContainer *cvsMenu = Core::ActionManager::createMenu(Id(CMD_ID_CVS_MENU));
261
262
    cvsMenu->menu()->setTitle(tr("&CVS"));
    toolsContainer->addMenu(cvsMenu);
263
    m_menuAction = cvsMenu->menu()->menuAction();
264

hjk's avatar
hjk committed
265
    Context globalcontext(C_GLOBAL);
266

hjk's avatar
hjk committed
267
    Command *command;
268
269

    m_diffCurrentAction = new Utils::ParameterAction(tr("Diff Current File"), tr("Diff \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
Eike Ziller's avatar
Eike Ziller committed
270
    command = Core::ActionManager::registerAction(m_diffCurrentAction,
271
        CMD_ID_DIFF_CURRENT, globalcontext);
hjk's avatar
hjk committed
272
    command->setAttribute(Command::CA_UpdateText);
273
    command->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+C,Meta+D") : tr("Alt+C,Alt+D")));
274
    connect(m_diffCurrentAction, SIGNAL(triggered()), this, SLOT(diffCurrentFile()));
275
    cvsMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
276
    m_commandLocator->appendCommand(command);
277

278
    m_filelogCurrentAction = new Utils::ParameterAction(tr("Filelog Current File"), tr("Filelog \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
Eike Ziller's avatar
Eike Ziller committed
279
    command = Core::ActionManager::registerAction(m_filelogCurrentAction,
280
        CMD_ID_FILELOG_CURRENT, globalcontext);
hjk's avatar
hjk committed
281
    command->setAttribute(Command::CA_UpdateText);
282
283
    connect(m_filelogCurrentAction, SIGNAL(triggered()), this,
        SLOT(filelogCurrentFile()));
284
    cvsMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
285
    m_commandLocator->appendCommand(command);
286

287
    m_annotateCurrentAction = new Utils::ParameterAction(tr("Annotate Current File"), tr("Annotate \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
Eike Ziller's avatar
Eike Ziller committed
288
    command = Core::ActionManager::registerAction(m_annotateCurrentAction,
289
        CMD_ID_ANNOTATE_CURRENT, globalcontext);
hjk's avatar
hjk committed
290
    command->setAttribute(Command::CA_UpdateText);
291
292
    connect(m_annotateCurrentAction, SIGNAL(triggered()), this,
        SLOT(annotateCurrentFile()));
293
    cvsMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
294
    m_commandLocator->appendCommand(command);
295

296
    cvsMenu->addSeparator(globalcontext);
297

298
    m_addAction = new Utils::ParameterAction(tr("Add"), tr("Add \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
Eike Ziller's avatar
Eike Ziller committed
299
    command = Core::ActionManager::registerAction(m_addAction, CMD_ID_ADD,
300
        globalcontext);
hjk's avatar
hjk committed
301
    command->setAttribute(Command::CA_UpdateText);
302
    command->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+C,Meta+A") : tr("Alt+C,Alt+A")));
303
    connect(m_addAction, SIGNAL(triggered()), this, SLOT(addCurrentFile()));
304
    cvsMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
305
    m_commandLocator->appendCommand(command);
306

307
    m_commitCurrentAction = new Utils::ParameterAction(tr("Commit Current File"), tr("Commit \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
Eike Ziller's avatar
Eike Ziller committed
308
    command = Core::ActionManager::registerAction(m_commitCurrentAction,
309
        CMD_ID_COMMIT_CURRENT, globalcontext);
hjk's avatar
hjk committed
310
    command->setAttribute(Command::CA_UpdateText);
311
    command->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+C,Meta+C") : tr("Alt+C,Alt+C")));
312
313
    connect(m_commitCurrentAction, SIGNAL(triggered()), this, SLOT(startCommitCurrentFile()));
    cvsMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
314
    m_commandLocator->appendCommand(command);
315

316
    m_deleteAction = new Utils::ParameterAction(tr("Delete..."), tr("Delete \"%1\"..."), Utils::ParameterAction::EnabledWithParameter, this);
Eike Ziller's avatar
Eike Ziller committed
317
    command = Core::ActionManager::registerAction(m_deleteAction, CMD_ID_DELETE_FILE,
318
        globalcontext);
hjk's avatar
hjk committed
319
    command->setAttribute(Command::CA_UpdateText);
320
    connect(m_deleteAction, SIGNAL(triggered()), this, SLOT(promptToDeleteCurrentFile()));
321
    cvsMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
322
    m_commandLocator->appendCommand(command);
323

324
    m_revertAction = new Utils::ParameterAction(tr("Revert..."), tr("Revert \"%1\"..."), Utils::ParameterAction::EnabledWithParameter, this);
Eike Ziller's avatar
Eike Ziller committed
325
    command = Core::ActionManager::registerAction(m_revertAction, CMD_ID_REVERT,
326
        globalcontext);
hjk's avatar
hjk committed
327
    command->setAttribute(Command::CA_UpdateText);
328
    connect(m_revertAction, SIGNAL(triggered()), this, SLOT(revertCurrentFile()));
329
    cvsMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
330
    m_commandLocator->appendCommand(command);
331

332
    cvsMenu->addSeparator(globalcontext);
333

334
    m_editCurrentAction = new Utils::ParameterAction(tr("Edit"), tr("Edit \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
Eike Ziller's avatar
Eike Ziller committed
335
    command = Core::ActionManager::registerAction(m_editCurrentAction, CMD_ID_EDIT_FILE, globalcontext);
hjk's avatar
hjk committed
336
    command->setAttribute(Command::CA_UpdateText);
337
338
339
340
341
    connect(m_editCurrentAction, SIGNAL(triggered()), this, SLOT(editCurrentFile()));
    cvsMenu->addAction(command);
    m_commandLocator->appendCommand(command);

    m_uneditCurrentAction = new Utils::ParameterAction(tr("Unedit"), tr("Unedit \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
Eike Ziller's avatar
Eike Ziller committed
342
    command = Core::ActionManager::registerAction(m_uneditCurrentAction, CMD_ID_UNEDIT_FILE, globalcontext);
hjk's avatar
hjk committed
343
    command->setAttribute(Command::CA_UpdateText);
344
345
346
347
348
    connect(m_uneditCurrentAction, SIGNAL(triggered()), this, SLOT(uneditCurrentFile()));
    cvsMenu->addAction(command);
    m_commandLocator->appendCommand(command);

    m_uneditRepositoryAction = new QAction(tr("Unedit Repository"), this);
Eike Ziller's avatar
Eike Ziller committed
349
    command = Core::ActionManager::registerAction(m_uneditRepositoryAction, CMD_ID_UNEDIT_REPOSITORY, globalcontext);
350
351
352
353
    connect(m_uneditRepositoryAction, SIGNAL(triggered()), this, SLOT(uneditCurrentRepository()));
    cvsMenu->addAction(command);
    m_commandLocator->appendCommand(command);

354
    cvsMenu->addSeparator(globalcontext);
355

356
    m_diffProjectAction = new Utils::ParameterAction(tr("Diff Project"), tr("Diff Project \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
Eike Ziller's avatar
Eike Ziller committed
357
    command = Core::ActionManager::registerAction(m_diffProjectAction, CMD_ID_DIFF_PROJECT,
358
        globalcontext);
hjk's avatar
hjk committed
359
    command->setAttribute(Command::CA_UpdateText);
360
361
    connect(m_diffProjectAction, SIGNAL(triggered()), this, SLOT(diffProject()));
    cvsMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
362
    m_commandLocator->appendCommand(command);
363

364
    m_statusProjectAction = new Utils::ParameterAction(tr("Project Status"), tr("Status of Project \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
Eike Ziller's avatar
Eike Ziller committed
365
    command = Core::ActionManager::registerAction(m_statusProjectAction, CMD_ID_STATUS,
366
        globalcontext);
hjk's avatar
hjk committed
367
    command->setAttribute(Command::CA_UpdateText);
368
    connect(m_statusProjectAction, SIGNAL(triggered()), this, SLOT(projectStatus()));
369
    cvsMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
370
    m_commandLocator->appendCommand(command);
371

372
    m_logProjectAction = new Utils::ParameterAction(tr("Log Project"), tr("Log Project \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
Eike Ziller's avatar
Eike Ziller committed
373
    command = Core::ActionManager::registerAction(m_logProjectAction, CMD_ID_PROJECTLOG, globalcontext);
hjk's avatar
hjk committed
374
    command->setAttribute(Command::CA_UpdateText);
375
376
    connect(m_logProjectAction, SIGNAL(triggered()), this, SLOT(logProject()));
    cvsMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
377
    m_commandLocator->appendCommand(command);
378

379
    m_updateProjectAction = new Utils::ParameterAction(tr("Update Project"), tr("Update Project \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
Eike Ziller's avatar
Eike Ziller committed
380
    command = Core::ActionManager::registerAction(m_updateProjectAction, CMD_ID_UPDATE, globalcontext);
hjk's avatar
hjk committed
381
    command->setAttribute(Command::CA_UpdateText);
382
383
    connect(m_updateProjectAction, SIGNAL(triggered()), this, SLOT(updateProject()));
    cvsMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
384
    m_commandLocator->appendCommand(command);
385

386
    m_commitProjectAction = new Utils::ParameterAction(tr("Commit Project"), tr("Commit Project \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
Eike Ziller's avatar
Eike Ziller committed
387
    command = Core::ActionManager::registerAction(m_commitProjectAction, CMD_ID_PROJECTCOMMIT, globalcontext);
hjk's avatar
hjk committed
388
    command->setAttribute(Command::CA_UpdateText);
389
390
391
392
    connect(m_commitProjectAction, SIGNAL(triggered()), this, SLOT(commitProject()));
    cvsMenu->addAction(command);
    m_commandLocator->appendCommand(command);

393
    cvsMenu->addSeparator(globalcontext);
394
395

    m_diffRepositoryAction = new QAction(tr("Diff Repository"), this);
Eike Ziller's avatar
Eike Ziller committed
396
    command = Core::ActionManager::registerAction(m_diffRepositoryAction, CMD_ID_REPOSITORYDIFF, globalcontext);
397
398
399
400
401
    connect(m_diffRepositoryAction, SIGNAL(triggered()), this, SLOT(diffRepository()));
    cvsMenu->addAction(command);
    m_commandLocator->appendCommand(command);

    m_statusRepositoryAction = new QAction(tr("Repository Status"), this);
Eike Ziller's avatar
Eike Ziller committed
402
    command = Core::ActionManager::registerAction(m_statusRepositoryAction, CMD_ID_REPOSITORYSTATUS, globalcontext);
403
404
405
    connect(m_statusRepositoryAction, SIGNAL(triggered()), this, SLOT(statusRepository()));
    cvsMenu->addAction(command);
    m_commandLocator->appendCommand(command);
406

407
    m_logRepositoryAction = new QAction(tr("Repository Log"), this);
Eike Ziller's avatar
Eike Ziller committed
408
    command = Core::ActionManager::registerAction(m_logRepositoryAction, CMD_ID_REPOSITORYLOG, globalcontext);
409
410
    connect(m_logRepositoryAction, SIGNAL(triggered()), this, SLOT(logRepository()));
    cvsMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
411
    m_commandLocator->appendCommand(command);
412

413
    m_updateRepositoryAction = new QAction(tr("Update Repository"), this);
Eike Ziller's avatar
Eike Ziller committed
414
    command = Core::ActionManager::registerAction(m_updateRepositoryAction, CMD_ID_REPOSITORYUPDATE, globalcontext);
415
416
417
418
    connect(m_updateRepositoryAction, SIGNAL(triggered()), this, SLOT(updateRepository()));
    cvsMenu->addAction(command);
    m_commandLocator->appendCommand(command);

419
    m_commitAllAction = new QAction(tr("Commit All Files"), this);
Eike Ziller's avatar
Eike Ziller committed
420
    command = Core::ActionManager::registerAction(m_commitAllAction, CMD_ID_COMMIT_ALL,
421
422
423
        globalcontext);
    connect(m_commitAllAction, SIGNAL(triggered()), this, SLOT(startCommitAll()));
    cvsMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
424
    m_commandLocator->appendCommand(command);
425
426

    m_revertRepositoryAction = new QAction(tr("Revert Repository..."), this);
Eike Ziller's avatar
Eike Ziller committed
427
    command = Core::ActionManager::registerAction(m_revertRepositoryAction, CMD_ID_REVERT_ALL,
428
429
430
                                  globalcontext);
    connect(m_revertRepositoryAction, SIGNAL(triggered()), this, SLOT(revertAll()));
    cvsMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
431
    m_commandLocator->appendCommand(command);
432

433
    // Actions of the submit editor
hjk's avatar
hjk committed
434
    Context cvscommitcontext(Constants::CVSCOMMITEDITOR);
435

hjk's avatar
hjk committed
436
    m_submitCurrentLogAction = new QAction(VcsBaseSubmitEditor::submitIcon(), tr("Commit"), this);
Eike Ziller's avatar
Eike Ziller committed
437
    command = Core::ActionManager::registerAction(m_submitCurrentLogAction, Constants::SUBMIT_CURRENT, cvscommitcontext);
hjk's avatar
hjk committed
438
    command->setAttribute(Command::CA_UpdateText);
439
440
    connect(m_submitCurrentLogAction, SIGNAL(triggered()), this, SLOT(submitCurrentLog()));

hjk's avatar
hjk committed
441
    m_submitDiffAction = new QAction(VcsBaseSubmitEditor::diffIcon(), tr("Diff &Selected Files"), this);
Eike Ziller's avatar
Eike Ziller committed
442
    command = Core::ActionManager::registerAction(m_submitDiffAction , Constants::DIFF_SELECTED, cvscommitcontext);
443
444

    m_submitUndoAction = new QAction(tr("&Undo"), this);
Eike Ziller's avatar
Eike Ziller committed
445
    command = Core::ActionManager::registerAction(m_submitUndoAction, Core::Constants::UNDO, cvscommitcontext);
446
447

    m_submitRedoAction = new QAction(tr("&Redo"), this);
Eike Ziller's avatar
Eike Ziller committed
448
    command = Core::ActionManager::registerAction(m_submitRedoAction, Core::Constants::REDO, cvscommitcontext);
449
450
451
    return true;
}

452
bool CvsPlugin::submitEditorAboutToClose()
453
{
454
    if (!isCommitEditorOpen())
455
456
        return true;

457
458
459
460
    CvsSubmitEditor *editor = qobject_cast<CvsSubmitEditor *>(submitEditor());
    QTC_ASSERT(editor, return true);
    IDocument *editorDocument = editor->document();
    QTC_ASSERT(editorDocument, return true);
461
462
463

    // Submit editor closing. Make it write out the commit message
    // and retrieve files
464
    const QFileInfo editorFile(editorDocument->filePath());
465
    const QFileInfo changeFile(m_commitMessageFileName);
466
467
468
469
470
    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
471
472
    CvsSettings newSettings = m_settings;
    const VcsBaseSubmitEditor::PromptSubmitResult answer =
473
474
475
            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?"),
476
477
                                 newSettings.boolPointer(CvsSettings::promptOnSubmitKey),
                                 !m_submitActionTriggered);
478
479
    m_submitActionTriggered = false;
    switch (answer) {
hjk's avatar
hjk committed
480
    case VcsBaseSubmitEditor::SubmitCanceled:
481
        return false; // Keep editing and change file
hjk's avatar
hjk committed
482
    case VcsBaseSubmitEditor::SubmitDiscarded:
483
        cleanCommitMessageFile();
484
485
486
487
488
489
490
491
492
        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
493
        closeEditor = DocumentManager::saveDocument(editorDocument);
494
495
        if (closeEditor)
            closeEditor = commit(m_commitMessageFileName, fileList);
496
497
    }
    if (closeEditor)
498
        cleanCommitMessageFile();
499
500
501
    return closeEditor;
}

hjk's avatar
hjk committed
502
void CvsPlugin::diffCommitFiles(const QStringList &files)
503
{
504
    m_client->diff(m_commitRepository, files);
505
506
}

hjk's avatar
hjk committed
507
static void setDiffBaseDirectory(IEditor *editor, const QString &db)
508
{
hjk's avatar
hjk committed
509
    if (VcsBaseEditorWidget *ve = qobject_cast<VcsBaseEditorWidget*>(editor->widget()))
510
        ve->setWorkingDirectory(db);
511
}
512

hjk's avatar
hjk committed
513
CvsSubmitEditor *CvsPlugin::openCVSSubmitEditor(const QString &fileName)
514
{
Eike Ziller's avatar
Eike Ziller committed
515
    IEditor *editor = EditorManager::openEditor(fileName, Constants::CVSCOMMITEDITOR_ID);
hjk's avatar
hjk committed
516
    CvsSubmitEditor *submitEditor = qobject_cast<CvsSubmitEditor*>(editor);
517
    QTC_CHECK(submitEditor);
518
    submitEditor->registerActions(m_submitUndoAction, m_submitRedoAction, m_submitCurrentLogAction, m_submitDiffAction);
519
    connect(submitEditor, SIGNAL(diffSelectedFiles(QStringList)), this, SLOT(diffCommitFiles(QStringList)));
520
521
522
523

    return submitEditor;
}

hjk's avatar
hjk committed
524
void CvsPlugin::updateActions(VcsBasePlugin::ActionState as)
525
{
Friedemann Kleint's avatar
Friedemann Kleint committed
526
527
    if (!enableMenuAction(as, m_menuAction)) {
        m_commandLocator->setEnabled(false);
528
        return;
Friedemann Kleint's avatar
Friedemann Kleint committed
529
    }
530

Friedemann Kleint's avatar
Friedemann Kleint committed
531
532
    const bool hasTopLevel = currentState().hasTopLevel();
    m_commandLocator->setEnabled(hasTopLevel);
533

534
535
536
537
538
539
540
541
    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);
542
543
    m_editCurrentAction->setParameter(currentFileName);
    m_uneditCurrentAction->setParameter(currentFileName);
544
545
546
547
548

    const QString currentProjectName = currentState().currentProjectName();
    m_diffProjectAction->setParameter(currentProjectName);
    m_statusProjectAction->setParameter(currentProjectName);
    m_updateProjectAction->setParameter(currentProjectName);
549
    m_logProjectAction->setParameter(currentProjectName);
550
    m_commitProjectAction->setParameter(currentProjectName);
551

552
553
554
    m_diffRepositoryAction->setEnabled(hasTopLevel);
    m_statusRepositoryAction->setEnabled(hasTopLevel);
    m_updateRepositoryAction->setEnabled(hasTopLevel);
Friedemann Kleint's avatar
Friedemann Kleint committed
555
    m_commitAllAction->setEnabled(hasTopLevel);
556
557
    m_logRepositoryAction->setEnabled(hasTopLevel);
    m_uneditRepositoryAction->setEnabled(hasTopLevel);
558
559
}

hjk's avatar
hjk committed
560
void CvsPlugin::addCurrentFile()
561
{
hjk's avatar
hjk committed
562
    const VcsBasePluginState state = currentState();
563
    QTC_ASSERT(state.hasFile(), return);
564
    vcsAdd(state.currentFileTopLevel(), state.relativeCurrentFile());
565
566
}

hjk's avatar
hjk committed
567
void CvsPlugin::revertAll()
568
{
hjk's avatar
hjk committed
569
    const VcsBasePluginState state = currentState();
570
    QTC_ASSERT(state.hasTopLevel(), return);
571
    const QString title = tr("Revert repository");
Tobias Hunger's avatar
Tobias Hunger committed
572
    if (!messageBoxQuestion(title, tr("Revert all pending changes to the repository?")))
573
574
575
        return;
    QStringList args;
    args << QLatin1String("update") << QLatin1String("-C") << state.topLevel();
hjk's avatar
hjk committed
576
    const CvsResponse revertResponse =
577
            runCvs(state.topLevel(), args, m_settings.timeOutMs(),
578
                   SshPasswordPrompt|ShowStdOutInLogWindow);
579
    if (revertResponse.result == CvsResponse::Ok)
580
        cvsVersionControl()->emitRepositoryChanged(state.topLevel());
581
    else
582
583
584
        QMessageBox::warning(0, title, tr("Revert failed: %1").arg(revertResponse.message), QMessageBox::Ok);
}

hjk's avatar
hjk committed
585
void CvsPlugin::revertCurrentFile()
586
{
hjk's avatar
hjk committed
587
    const VcsBasePluginState state = currentState();
588
    QTC_ASSERT(state.hasFile(), return);
589
590
    QStringList args;
    args << QLatin1String("diff") << state.relativeCurrentFile();
hjk's avatar
hjk committed
591
    const CvsResponse diffResponse =
592
            runCvs(state.currentFileTopLevel(), args, m_settings.timeOutMs(), 0);
593
    switch (diffResponse.result) {
hjk's avatar
hjk committed
594
    case CvsResponse::Ok:
595
        return; // Not modified, diff exit code 0
hjk's avatar
hjk committed
596
    case CvsResponse::NonNullExitCode: // Diff exit code != 0
597
598
599
        if (diffResponse.stdOut.isEmpty()) // Paranoia: Something else failed?
            return;
        break;
hjk's avatar
hjk committed
600
    case CvsResponse::OtherError:
601
602
603
        return;
    }

604
605
    if (!messageBoxQuestion(QLatin1String("CVS Revert"),
                            tr("The file has been changed. Do you want to revert it?")))
606
607
        return;

hjk's avatar
hjk committed
608
    FileChangeBlocker fcb(state.currentFile());
609
610

    // revert
611
612
    args.clear();
    args << QLatin1String("update") << QLatin1String("-C") << state.relativeCurrentFile();
hjk's avatar
hjk committed
613
    const CvsResponse revertResponse =
614
            runCvs(state.currentFileTopLevel(), args, m_settings.timeOutMs(),
615
                   SshPasswordPrompt|ShowStdOutInLogWindow);
616
    if (revertResponse.result == CvsResponse::Ok)
617
        cvsVersionControl()->emitFilesChanged(QStringList(state.currentFile()));
618
619
}

hjk's avatar
hjk committed
620
void CvsPlugin::diffProject()
621
{
hjk's avatar
hjk committed
622
    const VcsBasePluginState state = currentState();
623
    QTC_ASSERT(state.hasProject(), return);
624
    const QString relativeProject = state.relativeCurrentProject();
625
    m_client->diff(state.currentProjectTopLevel(),
626
            relativeProject.isEmpty() ? QStringList() : QStringList(relativeProject));
627
628
}

hjk's avatar
hjk committed
629
void CvsPlugin::diffCurrentFile()
630
{
hjk's avatar
hjk committed
631
    const VcsBasePluginState state = currentState();
632
    QTC_ASSERT(state.hasFile(), return);
633
    m_client->diff(state.currentFileTopLevel(), QStringList(state.relativeCurrentFile()));
634
635
}

hjk's avatar
hjk committed
636
void CvsPlugin::startCommitCurrentFile()
637
{
hjk's avatar
hjk committed
638
    const VcsBasePluginState state = currentState();
639
    QTC_ASSERT(state.hasFile(), return);
640
    startCommit(state.currentFileTopLevel(), state.relativeCurrentFile());
641
642
}

hjk's avatar
hjk committed
643
void CvsPlugin::startCommitAll()
644
{
hjk's avatar
hjk committed
645
    const VcsBasePluginState state = currentState();
646
    QTC_ASSERT(state.hasTopLevel(), return);
647
    startCommit(state.topLevel());
648
649
650
651
652
}

/* Start commit of files of a single repository by displaying
 * template and files in a submit editor. On closing, the real
 * commit will start. */
653
void CvsPlugin::startCommit(const QString &workingDir, const QString &file)
654
{
655
    if (raiseSubmitEditor())
656
        return;
657
    if (isCommitEditorOpen()) {
hjk's avatar
hjk committed
658
        VcsBaseOutputWindow::instance()->appendWarning(tr("Another commit is currently being executed."));
659
660
        return;
    }
661

662
663
    // We need the "Examining <subdir>" stderr output to tell
    // where we are, so, have stdout/stderr channels merged.
664
    QStringList args = QStringList(QLatin1String("status"));
hjk's avatar
hjk committed
665
    const CvsResponse response =
666
            runCvs(workingDir, args, m_settings.timeOutMs(), MergeOutputChannels);
hjk's avatar
hjk committed
667
    if (response.result != CvsResponse::Ok)
668
        return;
669
670
671
    // 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);
672
    if (!file.isEmpty()) {
673
        for (StateList::iterator it = statusOutput.begin(); it != statusOutput.end() ; ) {
674
            if (file == it->second)
675
                ++it;
676
            else
677
678
679
                it = statusOutput.erase(it);
        }
    }
680
    if (statusOutput.empty()) {
Orgad Shaneh's avatar
Orgad Shaneh committed
681
        VcsBaseOutputWindow::instance()->appendWarning(tr("There are no modified files."));
682
683
        return;
    }
684
    m_commitRepository = workingDir;
685
686

    // Create a new submit change file containing the submit template
687
688
    Utils::TempFileSaver saver;
    saver.setAutoRemove(false);
689
690
691
    // TODO: Retrieve submit template from
    const QString submitTemplate;
    // Create a submit
692
693
    saver.write(submitTemplate.toUtf8());
    if (!saver.finalize()) {
hjk's avatar
hjk committed
694
        VcsBaseOutputWindow::instance()->appendError(saver.errorString());
695
696
697
        return;
    }
    m_commitMessageFileName = saver.fileName();
698
    // Create a submit editor and set file list
hjk's avatar
hjk committed
699