cvsplugin.cpp 52.9 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
using namespace Core;
79
80
using namespace VcsBase;
using namespace Utils;
hjk's avatar
hjk committed
81
82

namespace Cvs {
83
namespace Internal {
84
85
86

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

static inline QString msgLogParsingFailed()
{
hjk's avatar
hjk committed
93
    return CvsPlugin::tr("Parsing of the log output failed");
94
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
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[] = {
127
{
128
    OtherContent,
129
130
    "CVS Command Log Editor", // id
    QT_TRANSLATE_NOOP("VCS", "CVS Command Log Editor"), // display name
131
    "CVS Command Log Editor", // context
Orgad Shaneh's avatar
Orgad Shaneh committed
132
    "text/vnd.qtcreator.cvs.commandlog"},
hjk's avatar
hjk committed
133
{   LogOutput,
134
135
    "CVS File Log Editor",   // id
    QT_TRANSLATE_NOOP("VCS", "CVS File Log Editor"),   // display name
136
    "CVS File Log Editor",   // context
Orgad Shaneh's avatar
Orgad Shaneh committed
137
    "text/vnd.qtcreator.cvs.log"},
hjk's avatar
hjk committed
138
{    AnnotateOutput,
139
140
    "CVS Annotation Editor",  // id
    QT_TRANSLATE_NOOP("VCS", "CVS Annotation Editor"),  // display name
141
    "CVS Annotation Editor",  // context
Orgad Shaneh's avatar
Orgad Shaneh committed
142
    "text/vnd.qtcreator.cvs.annotation"},
hjk's avatar
hjk committed
143
{   DiffOutput,
144
145
    "CVS Diff Editor",  // id
    QT_TRANSLATE_NOOP("VCS", "CVS Diff Editor"),  // display name
146
    "CVS Diff Editor",  // context
147
    "text/x-patch"}
148
149
150
};

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

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

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

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

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

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

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

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

hjk's avatar
hjk committed
229
bool CvsPlugin::initialize(const QStringList &arguments, QString *errorMessage)
230
{
hjk's avatar
hjk committed
231
232
233
    Q_UNUSED(arguments);
    typedef VcsSubmitEditorFactory<CvsSubmitEditor> CVSSubmitEditorFactory;
    typedef VcsEditorFactory<CvsEditor> CVSEditorFactory;
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
257
258
    for (int i = 0; i < editorCount; i++)
        addAutoReleasedObject(new CVSEditorFactory(editorParameters + i, this, describeSlotC));

259
    addAutoReleasedObject(new CheckoutWizardFactory);
Friedemann Kleint's avatar
Friedemann Kleint committed
260

Friedemann Kleint's avatar
Friedemann Kleint committed
261
    const QString prefix = QLatin1String("cvs");
262
    m_commandLocator = new CommandLocator("CVS", prefix, prefix);
Friedemann Kleint's avatar
Friedemann Kleint committed
263
264
    addAutoReleasedObject(m_commandLocator);

hjk's avatar
hjk committed
265
    // Register actions
266
    ActionContainer *toolsContainer = ActionManager::actionContainer(M_TOOLS);
267

268
    ActionContainer *cvsMenu = ActionManager::createMenu(Id(CMD_ID_CVS_MENU));
269
270
    cvsMenu->menu()->setTitle(tr("&CVS"));
    toolsContainer->addMenu(cvsMenu);
271
    m_menuAction = cvsMenu->menu()->menuAction();
272

hjk's avatar
hjk committed
273
    Context globalcontext(C_GLOBAL);
274

hjk's avatar
hjk committed
275
    Command *command;
276

277
278
    m_diffCurrentAction = new ParameterAction(tr("Diff Current File"), tr("Diff \"%1\""), ParameterAction::EnabledWithParameter, this);
    command = ActionManager::registerAction(m_diffCurrentAction,
279
        CMD_ID_DIFF_CURRENT, globalcontext);
hjk's avatar
hjk committed
280
    command->setAttribute(Command::CA_UpdateText);
281
    command->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+C,Meta+D") : tr("Alt+C,Alt+D")));
282
    connect(m_diffCurrentAction, SIGNAL(triggered()), this, SLOT(diffCurrentFile()));
283
    cvsMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
284
    m_commandLocator->appendCommand(command);
285

286
287
    m_filelogCurrentAction = new ParameterAction(tr("Filelog Current File"), tr("Filelog \"%1\""), ParameterAction::EnabledWithParameter, this);
    command = ActionManager::registerAction(m_filelogCurrentAction,
288
        CMD_ID_FILELOG_CURRENT, globalcontext);
hjk's avatar
hjk committed
289
    command->setAttribute(Command::CA_UpdateText);
290
291
    connect(m_filelogCurrentAction, SIGNAL(triggered()), this,
        SLOT(filelogCurrentFile()));
292
    cvsMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
293
    m_commandLocator->appendCommand(command);
294

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

304
    cvsMenu->addSeparator(globalcontext);
305

306
307
    m_addAction = new ParameterAction(tr("Add"), tr("Add \"%1\""), ParameterAction::EnabledWithParameter, this);
    command = ActionManager::registerAction(m_addAction, CMD_ID_ADD,
308
        globalcontext);
hjk's avatar
hjk committed
309
    command->setAttribute(Command::CA_UpdateText);
310
    command->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+C,Meta+A") : tr("Alt+C,Alt+A")));
311
    connect(m_addAction, SIGNAL(triggered()), this, SLOT(addCurrentFile()));
312
    cvsMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
313
    m_commandLocator->appendCommand(command);
314

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

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

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

340
    cvsMenu->addSeparator(globalcontext);
341

342
343
    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
344
    command->setAttribute(Command::CA_UpdateText);
345
346
347
348
    connect(m_editCurrentAction, SIGNAL(triggered()), this, SLOT(editCurrentFile()));
    cvsMenu->addAction(command);
    m_commandLocator->appendCommand(command);

349
350
    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
351
    command->setAttribute(Command::CA_UpdateText);
352
353
354
355
356
    connect(m_uneditCurrentAction, SIGNAL(triggered()), this, SLOT(uneditCurrentFile()));
    cvsMenu->addAction(command);
    m_commandLocator->appendCommand(command);

    m_uneditRepositoryAction = new QAction(tr("Unedit Repository"), this);
357
    command = ActionManager::registerAction(m_uneditRepositoryAction, CMD_ID_UNEDIT_REPOSITORY, globalcontext);
358
359
360
361
    connect(m_uneditRepositoryAction, SIGNAL(triggered()), this, SLOT(uneditCurrentRepository()));
    cvsMenu->addAction(command);
    m_commandLocator->appendCommand(command);

362
    cvsMenu->addSeparator(globalcontext);
363

364
365
    m_diffProjectAction = new ParameterAction(tr("Diff Project"), tr("Diff Project \"%1\""), ParameterAction::EnabledWithParameter, this);
    command = ActionManager::registerAction(m_diffProjectAction, CMD_ID_DIFF_PROJECT,
366
        globalcontext);
hjk's avatar
hjk committed
367
    command->setAttribute(Command::CA_UpdateText);
368
369
    connect(m_diffProjectAction, SIGNAL(triggered()), this, SLOT(diffProject()));
    cvsMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
370
    m_commandLocator->appendCommand(command);
371

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

380
381
    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
382
    command->setAttribute(Command::CA_UpdateText);
383
384
    connect(m_logProjectAction, SIGNAL(triggered()), this, SLOT(logProject()));
    cvsMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
385
    m_commandLocator->appendCommand(command);
386

387
388
    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
389
    command->setAttribute(Command::CA_UpdateText);
390
391
    connect(m_updateProjectAction, SIGNAL(triggered()), this, SLOT(updateProject()));
    cvsMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
392
    m_commandLocator->appendCommand(command);
393

394
395
    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
396
    command->setAttribute(Command::CA_UpdateText);
397
398
399
400
    connect(m_commitProjectAction, SIGNAL(triggered()), this, SLOT(commitProject()));
    cvsMenu->addAction(command);
    m_commandLocator->appendCommand(command);

401
    cvsMenu->addSeparator(globalcontext);
402
403

    m_diffRepositoryAction = new QAction(tr("Diff Repository"), this);
404
    command = ActionManager::registerAction(m_diffRepositoryAction, CMD_ID_REPOSITORYDIFF, globalcontext);
405
406
407
408
409
    connect(m_diffRepositoryAction, SIGNAL(triggered()), this, SLOT(diffRepository()));
    cvsMenu->addAction(command);
    m_commandLocator->appendCommand(command);

    m_statusRepositoryAction = new QAction(tr("Repository Status"), this);
410
    command = ActionManager::registerAction(m_statusRepositoryAction, CMD_ID_REPOSITORYSTATUS, globalcontext);
411
412
413
    connect(m_statusRepositoryAction, SIGNAL(triggered()), this, SLOT(statusRepository()));
    cvsMenu->addAction(command);
    m_commandLocator->appendCommand(command);
414

415
    m_logRepositoryAction = new QAction(tr("Repository Log"), this);
416
    command = ActionManager::registerAction(m_logRepositoryAction, CMD_ID_REPOSITORYLOG, globalcontext);
417
418
    connect(m_logRepositoryAction, SIGNAL(triggered()), this, SLOT(logRepository()));
    cvsMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
419
    m_commandLocator->appendCommand(command);
420

421
    m_updateRepositoryAction = new QAction(tr("Update Repository"), this);
422
    command = ActionManager::registerAction(m_updateRepositoryAction, CMD_ID_REPOSITORYUPDATE, globalcontext);
423
424
425
426
    connect(m_updateRepositoryAction, SIGNAL(triggered()), this, SLOT(updateRepository()));
    cvsMenu->addAction(command);
    m_commandLocator->appendCommand(command);

427
    m_commitAllAction = new QAction(tr("Commit All Files"), this);
428
    command = ActionManager::registerAction(m_commitAllAction, CMD_ID_COMMIT_ALL,
429
430
431
        globalcontext);
    connect(m_commitAllAction, SIGNAL(triggered()), this, SLOT(startCommitAll()));
    cvsMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
432
    m_commandLocator->appendCommand(command);
433
434

    m_revertRepositoryAction = new QAction(tr("Revert Repository..."), this);
435
436
    command = ActionManager::registerAction(m_revertRepositoryAction, CMD_ID_REVERT_ALL,
                             globalcontext);
437
438
    connect(m_revertRepositoryAction, SIGNAL(triggered()), this, SLOT(revertAll()));
    cvsMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
439
    m_commandLocator->appendCommand(command);
440

441
    // Actions of the submit editor
442
    Context cvscommitcontext(CVSCOMMITEDITOR);
443

hjk's avatar
hjk committed
444
    m_submitCurrentLogAction = new QAction(VcsBaseSubmitEditor::submitIcon(), tr("Commit"), this);
445
    command = ActionManager::registerAction(m_submitCurrentLogAction, SUBMIT_CURRENT, cvscommitcontext);
hjk's avatar
hjk committed
446
    command->setAttribute(Command::CA_UpdateText);
447
448
    connect(m_submitCurrentLogAction, SIGNAL(triggered()), this, SLOT(submitCurrentLog()));

hjk's avatar
hjk committed
449
    m_submitDiffAction = new QAction(VcsBaseSubmitEditor::diffIcon(), tr("Diff &Selected Files"), this);
450
    command = ActionManager::registerAction(m_submitDiffAction , DIFF_SELECTED, cvscommitcontext);
451
452

    m_submitUndoAction = new QAction(tr("&Undo"), this);
453
    command = ActionManager::registerAction(m_submitUndoAction, Core::Constants::UNDO, cvscommitcontext);
454
455

    m_submitRedoAction = new QAction(tr("&Redo"), this);
456
    command = ActionManager::registerAction(m_submitRedoAction, Core::Constants::REDO, cvscommitcontext);
457
458
459
    return true;
}

460
bool CvsPlugin::submitEditorAboutToClose()
461
{
462
    if (!isCommitEditorOpen())
463
464
        return true;

465
466
467
468
    CvsSubmitEditor *editor = qobject_cast<CvsSubmitEditor *>(submitEditor());
    QTC_ASSERT(editor, return true);
    IDocument *editorDocument = editor->document();
    QTC_ASSERT(editorDocument, return true);
469
470
471

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

hjk's avatar
hjk committed
510
void CvsPlugin::diffCommitFiles(const QStringList &files)
511
{
512
    m_client->diff(m_commitRepository, files);
513
514
}

hjk's avatar
hjk committed
515
static void setDiffBaseDirectory(IEditor *editor, const QString &db)
516
{
hjk's avatar
hjk committed
517
    if (VcsBaseEditorWidget *ve = qobject_cast<VcsBaseEditorWidget*>(editor->widget()))
518
        ve->setWorkingDirectory(db);
519
}
520

hjk's avatar
hjk committed
521
CvsSubmitEditor *CvsPlugin::openCVSSubmitEditor(const QString &fileName)
522
{
523
    IEditor *editor = EditorManager::openEditor(fileName, CVSCOMMITEDITOR_ID);
hjk's avatar
hjk committed
524
    CvsSubmitEditor *submitEditor = qobject_cast<CvsSubmitEditor*>(editor);
525
    QTC_CHECK(submitEditor);
526
    submitEditor->registerActions(m_submitUndoAction, m_submitRedoAction, m_submitCurrentLogAction, m_submitDiffAction);
527
    connect(submitEditor, SIGNAL(diffSelectedFiles(QStringList)), this, SLOT(diffCommitFiles(QStringList)));
528
529
530
531

    return submitEditor;
}

hjk's avatar
hjk committed
532
void CvsPlugin::updateActions(VcsBasePlugin::ActionState as)
533
{
Friedemann Kleint's avatar
Friedemann Kleint committed
534
535
    if (!enableMenuAction(as, m_menuAction)) {
        m_commandLocator->setEnabled(false);
536
        return;
Friedemann Kleint's avatar
Friedemann Kleint committed
537
    }
538

Friedemann Kleint's avatar
Friedemann Kleint committed
539
540
    const bool hasTopLevel = currentState().hasTopLevel();
    m_commandLocator->setEnabled(hasTopLevel);
541

542
543
544
545
546
547
548
549
    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);
550
551
    m_editCurrentAction->setParameter(currentFileName);
    m_uneditCurrentAction->setParameter(currentFileName);
552
553
554
555
556

    const QString currentProjectName = currentState().currentProjectName();
    m_diffProjectAction->setParameter(currentProjectName);
    m_statusProjectAction->setParameter(currentProjectName);
    m_updateProjectAction->setParameter(currentProjectName);
557
    m_logProjectAction->setParameter(currentProjectName);
558
    m_commitProjectAction->setParameter(currentProjectName);
559

560
561
562
    m_diffRepositoryAction->setEnabled(hasTopLevel);
    m_statusRepositoryAction->setEnabled(hasTopLevel);
    m_updateRepositoryAction->setEnabled(hasTopLevel);
Friedemann Kleint's avatar
Friedemann Kleint committed
563
    m_commitAllAction->setEnabled(hasTopLevel);
564
565
    m_logRepositoryAction->setEnabled(hasTopLevel);
    m_uneditRepositoryAction->setEnabled(hasTopLevel);
566
567
}

hjk's avatar
hjk committed
568
void CvsPlugin::addCurrentFile()
569
{
hjk's avatar
hjk committed
570
    const VcsBasePluginState state = currentState();
571
    QTC_ASSERT(state.hasFile(), return);
572
    vcsAdd(state.currentFileTopLevel(), state.relativeCurrentFile());
573
574
}

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

hjk's avatar
hjk committed
594
void CvsPlugin::revertCurrentFile()
595
{
hjk's avatar
hjk committed
596
    const VcsBasePluginState state = currentState();
597
    QTC_ASSERT(state.hasFile(), return);
598
599
    QStringList args;
    args << QLatin1String("diff") << state.relativeCurrentFile();
hjk's avatar
hjk committed
600
    const CvsResponse diffResponse =
601
            runCvs(state.currentFileTopLevel(), args, m_settings.timeOutMs(), 0);
602
    switch (diffResponse.result) {
hjk's avatar
hjk committed
603
    case CvsResponse::Ok:
604
        return; // Not modified, diff exit code 0
hjk's avatar
hjk committed
605
    case CvsResponse::NonNullExitCode: // Diff exit code != 0
606
607
608
        if (diffResponse.stdOut.isEmpty()) // Paranoia: Something else failed?
            return;
        break;
hjk's avatar
hjk committed
609
    case CvsResponse::OtherError:
610
611
612
        return;
    }

613
614
    if (!messageBoxQuestion(QLatin1String("CVS Revert"),
                            tr("The file has been changed. Do you want to revert it?")))
615
616
        return;

hjk's avatar
hjk committed
617
    FileChangeBlocker fcb(state.currentFile());
618
619

    // revert
620
621
    args.clear();
    args << QLatin1String("update") << QLatin1String("-C") << state.relativeCurrentFile();
hjk's avatar
hjk committed
622
    const CvsResponse revertResponse =
623
            runCvs(state.currentFileTopLevel(), args, m_settings.timeOutMs(),
624
                   SshPasswordPrompt|ShowStdOutInLogWindow);
625
    if (revertResponse.result == CvsResponse::Ok)
626
        cvsVersionControl()->emitFilesChanged(QStringList(state.currentFile()));
627
628
}

hjk's avatar
hjk committed
629
void CvsPlugin::diffProject()
630
{
hjk's avatar
hjk committed
631
    const VcsBasePluginState state = currentState();
632
    QTC_ASSERT(state.hasProject(), return);
633
    const QString relativeProject = state.relativeCurrentProject();
634
    m_client->diff(state.currentProjectTopLevel(),
635
            relativeProject.isEmpty() ? QStringList() : QStringList(relativeProject));
636
637
}

hjk's avatar
hjk committed
638
void CvsPlugin::diffCurrentFile()
639
{
hjk's avatar
hjk committed
640
    const VcsBasePluginState state = currentState();
641
    QTC_ASSERT(state.hasFile(), return);
642
    m_client->diff(state.currentFileTopLevel(), QStringList(state.relativeCurrentFile()));
643
644
}

hjk's avatar
hjk committed
645
void CvsPlugin::startCommitCurrentFile()
646
{
hjk's avatar
hjk committed
647
    const VcsBasePluginState state = currentState();
648
    QTC_ASSERT(state.hasFile(), return);
649
    startCommit(state.currentFileTopLevel(), state.relativeCurrentFile());
650
651
}

hjk's avatar
hjk committed
652
void CvsPlugin::startCommitAll()
653
{
hjk's avatar
hjk committed
654
    const VcsBasePluginState state = currentState();
655
    QTC_ASSERT(state.hasTopLevel(), return);
656
    startCommit(state.topLevel());
657
658
659
660
661
}

/* Start commit of files of a single repository by displaying
 * template and files in a submit editor. On closing, the real
 * commit will start. */
662
void CvsPlugin::startCommit(const QString &workingDir, const QString &file)
663
{
664
    if (raiseSubmitEditor())
665
        return;
666
    if (isCommitEditorOpen()) {
hjk's avatar
hjk committed
667
        VcsBaseOutputWindow::instance()->appendWarning(tr("Another commit is currently being executed."));
668
669
        return;
    }
670

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

    // Create a new submit change file containing the submit template
696
    TempFileSaver saver;
697
    saver.setAutoRemove(false);
698
699
700
    // TODO: Retrieve submit template from
    const QString submitTemplate;
    // Create a submit
701
702
    saver.write(submitTemplate.toUtf8());
    if (!saver.finalize()) {
hjk's avatar
hjk committed
703
        VcsBaseOutputWindow::instance()->appendError(saver.errorString());
704
705
706
        return;
    }
    m_commitMessageFileName = saver.fileName();
707
    // Create a submit editor and set file list
hjk's avatar
hjk committed
708
    CvsSubmitEditor *editor = openCVSSubmitEditor(m_commitMessageFileName);
709
    setSubmitEditor(editor);
710
    editor->setCheckScriptWorkingDirectory(m_commitRepository);
711
712
713
    editor->setStateList(statusOutput);
}