cvsplugin.cpp 56.8 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
2
**
3
** Copyright (C) 2013 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
34
35

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

#include <vcsbase/basevcseditorfactory.h>
#include <vcsbase/vcsbaseeditor.h>
#include <vcsbase/basevcssubmiteditorfactory.h>
41
#include <vcsbase/vcsbaseoutputwindow.h>
42
#include <vcsbase/vcsbaseeditorparameterwidget.h>
Friedemann Kleint's avatar
Friedemann Kleint committed
43
#include <locator/commandlocator.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
58
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/vcsmanager.h>
59
#include <utils/stringutils.h>
60
#include <utils/fileutils.h>
61

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

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

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

namespace Cvs {
81
namespace Internal {
82
83
84

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

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

94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
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";
116

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

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

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

153
154
155
156
157
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;
}

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

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

hjk's avatar
hjk committed
193
CvsPlugin::~CvsPlugin()
194
{
195
    cleanCommitMessageFile();
196
197
}

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

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

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

    using namespace Core::Constants;
    using namespace ExtensionSystem;
228
    using Core::Command;
229

hjk's avatar
hjk committed
230
    initializeVcs(new CvsControl(this));
231

232
233
    m_cvsPluginInstance = this;

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

hjk's avatar
hjk committed
237
    m_settings.fromSettings(ICore::settings());
238
239
240
241
242
243

    addAutoReleasedObject(new SettingsPage);

    addAutoReleasedObject(new CVSSubmitEditorFactory(&submitParameters));

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

Friedemann Kleint's avatar
Friedemann Kleint committed
248
249
    addAutoReleasedObject(new CheckoutWizard);

Friedemann Kleint's avatar
Friedemann Kleint committed
250
    const QString prefix = QLatin1String("cvs");
251
    m_commandLocator = new Locator::CommandLocator("CVS", prefix, prefix);
Friedemann Kleint's avatar
Friedemann Kleint committed
252
253
    addAutoReleasedObject(m_commandLocator);

hjk's avatar
hjk committed
254
    // Register actions
Eike Ziller's avatar
Eike Ziller committed
255
    ActionContainer *toolsContainer = Core::ActionManager::actionContainer(M_TOOLS);
256

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

hjk's avatar
hjk committed
262
    Context globalcontext(C_GLOBAL);
263

hjk's avatar
hjk committed
264
    Command *command;
265
266

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

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

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

293
    cvsMenu->addSeparator(globalcontext);
294

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

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

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

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

329
    cvsMenu->addSeparator(globalcontext);
330

331
    m_editCurrentAction = new Utils::ParameterAction(tr("Edit"), tr("Edit \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
Eike Ziller's avatar
Eike Ziller committed
332
    command = Core::ActionManager::registerAction(m_editCurrentAction, CMD_ID_EDIT_FILE, globalcontext);
hjk's avatar
hjk committed
333
    command->setAttribute(Command::CA_UpdateText);
334
335
336
337
338
    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
339
    command = Core::ActionManager::registerAction(m_uneditCurrentAction, CMD_ID_UNEDIT_FILE, globalcontext);
hjk's avatar
hjk committed
340
    command->setAttribute(Command::CA_UpdateText);
341
342
343
344
345
    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
346
    command = Core::ActionManager::registerAction(m_uneditRepositoryAction, CMD_ID_UNEDIT_REPOSITORY, globalcontext);
347
348
349
350
    connect(m_uneditRepositoryAction, SIGNAL(triggered()), this, SLOT(uneditCurrentRepository()));
    cvsMenu->addAction(command);
    m_commandLocator->appendCommand(command);

351
    cvsMenu->addSeparator(globalcontext);
352

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

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

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

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

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

390
    cvsMenu->addSeparator(globalcontext);
391
392

    m_diffRepositoryAction = new QAction(tr("Diff Repository"), this);
Eike Ziller's avatar
Eike Ziller committed
393
    command = Core::ActionManager::registerAction(m_diffRepositoryAction, CMD_ID_REPOSITORYDIFF, globalcontext);
394
395
396
397
398
    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
399
    command = Core::ActionManager::registerAction(m_statusRepositoryAction, CMD_ID_REPOSITORYSTATUS, globalcontext);
400
401
402
    connect(m_statusRepositoryAction, SIGNAL(triggered()), this, SLOT(statusRepository()));
    cvsMenu->addAction(command);
    m_commandLocator->appendCommand(command);
403

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

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

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

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

430
    // Actions of the submit editor
hjk's avatar
hjk committed
431
    Context cvscommitcontext(Constants::CVSCOMMITEDITOR);
432

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

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

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

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

449
bool CvsPlugin::submitEditorAboutToClose()
450
{
451
    if (!isCommitEditorOpen())
452
453
        return true;

454
455
456
457
    CvsSubmitEditor *editor = qobject_cast<CvsSubmitEditor *>(submitEditor());
    QTC_ASSERT(editor, return true);
    IDocument *editorDocument = editor->document();
    QTC_ASSERT(editorDocument, return true);
458
459
460

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

hjk's avatar
hjk committed
498
void CvsPlugin::diffCommitFiles(const QStringList &files)
499
{
500
    cvsDiff(m_commitRepository, files);
501
502
}

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

509
510
511
512
513
514
515
516
517
518
519
// Collect all parameters required for a diff to be able to associate them
// with a diff editor and re-run the diff with parameters.
struct CvsDiffParameters
{
    QString workingDir;
    QStringList arguments;
    QStringList files;
};

// Parameter widget controlling whitespace diff mode, associated with a parameter
// struct.
hjk's avatar
hjk committed
520
class CvsDiffParameterWidget : public VcsBaseEditorParameterWidget
521
522
{
    Q_OBJECT
hjk's avatar
hjk committed
523

524
525
526
527
public:
    explicit CvsDiffParameterWidget(const CvsDiffParameters &p, QWidget *parent = 0);

signals:
hjk's avatar
hjk committed
528
    void reRunDiff(const Cvs::Internal::CvsDiffParameters &);
529
530
531
532
533
534
535
536
537

public slots:
    void triggerReRun();

private:
    const CvsDiffParameters m_parameters;
};

CvsDiffParameterWidget::CvsDiffParameterWidget(const CvsDiffParameters &p, QWidget *parent) :
hjk's avatar
hjk committed
538
    VcsBaseEditorParameterWidget(parent), m_parameters(p)
539
540
{
    setBaseArguments(p.arguments);
541
542
    addToggleButton(QLatin1String("-w"), tr("Ignore Whitespace"));
    addToggleButton(QLatin1String("-B"), tr("Ignore Blank Lines"));
543
544
545
546
547
548
549
550
551
552
553
    connect(this, SIGNAL(argumentsChanged()),
            this, SLOT(triggerReRun()));
}

void CvsDiffParameterWidget::triggerReRun()
{
    CvsDiffParameters effectiveParameters = m_parameters;
    effectiveParameters.arguments = arguments();
    emit reRunDiff(effectiveParameters);
}

hjk's avatar
hjk committed
554
void CvsPlugin::cvsDiff(const QString &workingDir, const QStringList &files)
555
556
557
558
559
560
561
562
{
    CvsDiffParameters p;
    p.workingDir = workingDir;
    p.files = files;
    p.arguments = m_settings.cvsDiffOptions.split(QLatin1Char(' '), QString::SkipEmptyParts);
    cvsDiff(p);
}

563
void CvsPlugin::cvsDiff(const Cvs::Internal::CvsDiffParameters &p)
564
{
565
    if (Constants::debug)
566
        qDebug() << Q_FUNC_INFO << p.files;
hjk's avatar
hjk committed
567
568
569
    const QString source = VcsBaseEditorWidget::getSource(p.workingDir, p.files);
    QTextCodec *codec = VcsBaseEditorWidget::getCodec(p.workingDir, p.files);
    const QString id = VcsBaseEditorWidget::getTitleId(p.workingDir, p.files);
570
571

    QStringList args(QLatin1String("diff"));
572
573
    args.append(p.arguments);
    args.append(p.files);
574
575
576

    // CVS returns the diff exit code (1 if files differ), which is
    // undistinguishable from a "file not found" error, unfortunately.
hjk's avatar
hjk committed
577
578
    const CvsResponse response =
            runCvs(p.workingDir, args, m_settings.timeOutMS(), 0, codec);
579
    switch (response.result) {
hjk's avatar
hjk committed
580
581
    case CvsResponse::NonNullExitCode:
    case CvsResponse::Ok:
582
        break;
hjk's avatar
hjk committed
583
    case CvsResponse::OtherError:
584
585
586
587
588
589
590
591
        return;
    }

    QString output = fixDiffOutput(response.stdOut);
    if (output.isEmpty())
        output = tr("The files do not differ.");
    // diff of a single file? re-use an existing view if possible to support
    // the common usage pattern of continuously changing and diffing a file
592
    // Show in the same editor if diff has been executed before
hjk's avatar
hjk committed
593
594
    const QString tag = VcsBaseEditorWidget::editorTag(DiffOutput, p.workingDir, p.files);
    if (IEditor *existingEditor = VcsBaseEditorWidget::locateEditorByTag(tag)) {
595
        existingEditor->document()->setContents(output.toUtf8());
Eike Ziller's avatar
Eike Ziller committed
596
        EditorManager::activateEditor(existingEditor);
597
598
        setDiffBaseDirectory(existingEditor, p.workingDir);
        return;
599
    }
600
    const QString title = QString::fromLatin1("cvs diff %1").arg(id);
hjk's avatar
hjk committed
601
602
    IEditor *editor = showOutputInEditor(title, output, DiffOutput, source, codec);
    VcsBaseEditorWidget::tagEditor(editor, tag);
603
    setDiffBaseDirectory(editor, p.workingDir);
hjk's avatar
hjk committed
604
    CvsEditor *diffEditorWidget = qobject_cast<CvsEditor*>(editor->widget());
605
    QTC_ASSERT(diffEditorWidget, return);
606
607
608
609

    // Wire up the parameter widget to trigger a re-run on
    // parameter change and 'revert' from inside the diff editor.
    CvsDiffParameterWidget *pw = new CvsDiffParameterWidget(p);
hjk's avatar
hjk committed
610
611
    connect(pw, SIGNAL(reRunDiff(Cvs::Internal::CvsDiffParameters)),
            this, SLOT(cvsDiff(Cvs::Internal::CvsDiffParameters)));
hjk's avatar
hjk committed
612
    connect(diffEditorWidget, SIGNAL(diffChunkReverted(VcsBase::DiffChunk)),
613
614
            pw, SLOT(triggerReRun()));
    diffEditorWidget->setConfigurationWidget(pw);
615
616
}

hjk's avatar
hjk committed
617
CvsSubmitEditor *CvsPlugin::openCVSSubmitEditor(const QString &fileName)
618
{
Eike Ziller's avatar
Eike Ziller committed
619
    IEditor *editor = EditorManager::openEditor(fileName, Constants::CVSCOMMITEDITOR_ID);
hjk's avatar
hjk committed
620
    CvsSubmitEditor *submitEditor = qobject_cast<CvsSubmitEditor*>(editor);
621
    QTC_CHECK(submitEditor);
622
    submitEditor->registerActions(m_submitUndoAction, m_submitRedoAction, m_submitCurrentLogAction, m_submitDiffAction);
623
    connect(submitEditor, SIGNAL(diffSelectedFiles(QStringList)), this, SLOT(diffCommitFiles(QStringList)));
624
625
626
627

    return submitEditor;
}

hjk's avatar
hjk committed
628
void CvsPlugin::updateActions(VcsBasePlugin::ActionState as)
629
{
Friedemann Kleint's avatar
Friedemann Kleint committed
630
631
    if (!enableMenuAction(as, m_menuAction)) {
        m_commandLocator->setEnabled(false);
632
        return;
Friedemann Kleint's avatar
Friedemann Kleint committed
633
    }
634

Friedemann Kleint's avatar
Friedemann Kleint committed
635
636
    const bool hasTopLevel = currentState().hasTopLevel();
    m_commandLocator->setEnabled(hasTopLevel);
637

638
639
640
641
642
643
644
645
    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);
646
647
    m_editCurrentAction->setParameter(currentFileName);
    m_uneditCurrentAction->setParameter(currentFileName);
648
649
650
651
652

    const QString currentProjectName = currentState().currentProjectName();
    m_diffProjectAction->setParameter(currentProjectName);
    m_statusProjectAction->setParameter(currentProjectName);
    m_updateProjectAction->setParameter(currentProjectName);
653
    m_logProjectAction->setParameter(currentProjectName);
654
    m_commitProjectAction->setParameter(currentProjectName);
655

656
657
658
    m_diffRepositoryAction->setEnabled(hasTopLevel);
    m_statusRepositoryAction->setEnabled(hasTopLevel);
    m_updateRepositoryAction->setEnabled(hasTopLevel);
Friedemann Kleint's avatar
Friedemann Kleint committed
659
    m_commitAllAction->setEnabled(hasTopLevel);
660
661
    m_logRepositoryAction->setEnabled(hasTopLevel);
    m_uneditRepositoryAction->setEnabled(hasTopLevel);
662
663
}

hjk's avatar
hjk committed
664
void CvsPlugin::addCurrentFile()
665
{
hjk's avatar
hjk committed
666
    const VcsBasePluginState state = currentState();
667
    QTC_ASSERT(state.hasFile(), return);
668
    vcsAdd(state.currentFileTopLevel(), state.relativeCurrentFile());
669
670
}

hjk's avatar
hjk committed
671
void CvsPlugin::revertAll()
672
{
hjk's avatar
hjk committed
673
    const VcsBasePluginState state = currentState();
674
    QTC_ASSERT(state.hasTopLevel(), return);
675
    const QString title = tr("Revert repository");
Tobias Hunger's avatar
Tobias Hunger committed
676
    if (!messageBoxQuestion(title, tr("Revert all pending changes to the repository?")))
677
678
679
        return;
    QStringList args;
    args << QLatin1String("update") << QLatin1String("-C") << state.topLevel();
hjk's avatar
hjk committed
680
681
    const CvsResponse revertResponse =
            runCvs(state.topLevel(), args, m_settings.timeOutMS(),
682
                   SshPasswordPrompt|ShowStdOutInLogWindow);
683
    if (revertResponse.result == CvsResponse::Ok)
684
        cvsVersionControl()->emitRepositoryChanged(state.topLevel());
685
    else
686
687
688
        QMessageBox::warning(0, title, tr("Revert failed: %1").arg(revertResponse.message), QMessageBox::Ok);
}

hjk's avatar
hjk committed
689
void CvsPlugin::revertCurrentFile()
690
{
hjk's avatar
hjk committed
691
    const VcsBasePluginState state = currentState();
692
    QTC_ASSERT(state.hasFile(), return);
693
694
    QStringList args;
    args << QLatin1String("diff") << state.relativeCurrentFile();
hjk's avatar
hjk committed
695
696
    const CvsResponse diffResponse =
            runCvs(state.currentFileTopLevel(), args, m_settings.timeOutMS(), 0);
697
    switch (diffResponse.result) {
hjk's avatar
hjk committed
698
    case CvsResponse::Ok:
699
        return; // Not modified, diff exit code 0
hjk's avatar
hjk committed
700
    case CvsResponse::NonNullExitCode: // Diff exit code != 0
701
702
703
        if (diffResponse.stdOut.isEmpty()) // Paranoia: Something else failed?
            return;
        break;
hjk's avatar
hjk committed
704
    case CvsResponse::OtherError:
705
706
707
        return;
    }

708
709
    if (!messageBoxQuestion(QLatin1String("CVS Revert"),
                            tr("The file has been changed. Do you want to revert it?")))
710
711
        return;

hjk's avatar
hjk committed
712
    FileChangeBlocker fcb(state.currentFile());
713
714

    // revert
715
716
    args.clear();
    args << QLatin1String("update") << QLatin1String("-C") << state.relativeCurrentFile();
hjk's avatar
hjk committed
717
718
    const CvsResponse revertResponse =
            runCvs(state.currentFileTopLevel(), args, m_settings.timeOutMS(),
719
                   SshPasswordPrompt|ShowStdOutInLogWindow);
720
    if (revertResponse.result == CvsResponse::Ok)
721
        cvsVersionControl()->emitFilesChanged(QStringList(state.currentFile()));
722
723
}

hjk's avatar
hjk committed
724
void CvsPlugin::diffProject()
725
{
hjk's avatar
hjk committed
726
    const VcsBasePluginState state = currentState();
727
    QTC_ASSERT(state.hasProject(), return);
728
729
730
    const QString relativeProject = state.relativeCurrentProject();
    cvsDiff(state.currentProjectTopLevel(),
            relativeProject.isEmpty() ? QStringList() : QStringList(relativeProject));
731
732
}

hjk's avatar
hjk committed
733
void CvsPlugin::diffCurrentFile()
734
{
hjk's avatar
hjk committed
735
    const VcsBasePluginState state = currentState();
736
    QTC_ASSERT(state.hasFile(), return);
737
    cvsDiff(state.currentFileTopLevel(), QStringList(state.relativeCurrentFile()));
738
739
}

hjk's avatar
hjk committed
740
void CvsPlugin::startCommitCurrentFile()
741
{
hjk's avatar
hjk committed
742
    const VcsBasePluginState state =