perforceplugin.cpp 63.3 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
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
con's avatar
con committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
con's avatar
con committed
7
**
hjk's avatar
hjk committed
8
9
10
11
12
** 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
Eike Ziller's avatar
Eike Ziller committed
13
14
** conditions see http://www.qt.io/licensing.  For further information
** use the contact form at http://www.qt.io/contact-us.
15
**
16
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
** Alternatively, this file may be used under the terms of the GNU Lesser
Eike Ziller's avatar
Eike Ziller committed
18
19
20
21
22
23
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
hjk's avatar
hjk committed
24
25
26
**
** 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
27
28
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
29
****************************************************************************/
hjk's avatar
hjk committed
30

con's avatar
con committed
31
#include "perforceplugin.h"
hjk's avatar
hjk committed
32

con's avatar
con committed
33
#include "changenumberdialog.h"
hjk's avatar
hjk committed
34
#include "pendingchangesdialog.h"
con's avatar
con committed
35
36
#include "perforceconstants.h"
#include "perforceeditor.h"
hjk's avatar
hjk committed
37
38
#include "perforcesubmiteditor.h"
#include "perforceversioncontrol.h"
39
#include "perforcechecker.h"
hjk's avatar
hjk committed
40
#include "settingspage.h"
con's avatar
con committed
41

42
#include <coreplugin/actionmanager/actionmanager.h>
43
44
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/command.h>
45
#include <coreplugin/id.h>
con's avatar
con committed
46
#include <coreplugin/coreconstants.h>
hjk's avatar
hjk committed
47
#include <coreplugin/editormanager/editormanager.h>
48
#include <coreplugin/documentmanager.h>
hjk's avatar
hjk committed
49
#include <coreplugin/icore.h>
con's avatar
con committed
50
#include <coreplugin/messagemanager.h>
hjk's avatar
hjk committed
51
#include <coreplugin/mimedatabase.h>
52
#include <coreplugin/locator/commandlocator.h>
hjk's avatar
hjk committed
53
#include <utils/qtcassert.h>
con's avatar
con committed
54
#include <utils/synchronousprocess.h>
55
#include <utils/parameteraction.h>
56
#include <utils/fileutils.h>
con's avatar
con committed
57
58
59
#include <vcsbase/basevcseditorfactory.h>
#include <vcsbase/basevcssubmiteditorfactory.h>
#include <vcsbase/vcsbaseeditor.h>
60
#include <vcsbase/vcsoutputwindow.h>
61
#include <vcsbase/vcsbaseeditorparameterwidget.h>
con's avatar
con committed
62

63
#include <QAction>
64
65
66
#include <QDebug>
#include <QDir>
#include <QFileDialog>
67
#include <QFileInfo>
68
69
70
#include <QMainWindow>
#include <QMenu>
#include <QMessageBox>
71
72
73
74
75
76
77
#include <QSettings>
#include <QTextCodec>
#include <QtPlugin>

using namespace Core;
using namespace Utils;
using namespace VcsBase;
con's avatar
con committed
78

79
80
81
82
83
84
85
namespace Perforce {
namespace Internal {

const char SUBMIT_CURRENT[] = "Perforce.SubmitCurrentLog";
const char DIFF_SELECTED[] = "Perforce.DiffSelectedFilesInLog";
const char SUBMIT_MIMETYPE[] = "text/vnd.qtcreator.p4.submit";

86
const char PERFORCE_CONTEXT[] = "Perforce Context";
87
88
89
90
91
92
93
94
95
96
97
98
99
const char PERFORCE_SUBMIT_EDITOR_ID[] = "Perforce.SubmitEditor";
const char PERFORCE_SUBMIT_EDITOR_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("VCS", "Perforce.SubmitEditor");

const char PERFORCE_LOG_EDITOR_ID[] = "Perforce.LogEditor";
const char PERFORCE_LOG_EDITOR_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("VCS", "Perforce Log Editor");

const char PERFORCE_DIFF_EDITOR_ID[] = "Perforce.DiffEditor";
const char PERFORCE_DIFF_EDITOR_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("VCS", "Perforce Diff Editor");

const char PERFORCE_ANNOTATION_EDITOR_ID[] = "Perforce.AnnotationEditor";
const char PERFORCE_ANNOTATION_EDITOR_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("VCS", "Perforce Annotation Editor");

const VcsBaseEditorParameters editorParameters[] = {
con's avatar
con committed
100
{
Orgad Shaneh's avatar
Orgad Shaneh committed
101
    VcsBase::LogOutput,
102
103
    PERFORCE_LOG_EDITOR_ID,
    PERFORCE_LOG_EDITOR_DISPLAY_NAME,
Orgad Shaneh's avatar
Orgad Shaneh committed
104
    "text/vnd.qtcreator.p4.log"},
hjk's avatar
hjk committed
105
{    VcsBase::AnnotateOutput,
106
107
    PERFORCE_ANNOTATION_EDITOR_ID,
    PERFORCE_ANNOTATION_EDITOR_DISPLAY_NAME,
Orgad Shaneh's avatar
Orgad Shaneh committed
108
    "text/vnd.qtcreator.p4.annotation"},
hjk's avatar
hjk committed
109
{   VcsBase::DiffOutput,
110
111
    PERFORCE_DIFF_EDITOR_ID,
    PERFORCE_DIFF_EDITOR_DISPLAY_NAME,
112
    "text/x-patch"}
con's avatar
con committed
113
114
115
};

// Utility to find a parameter set by type
116
static inline const VcsBaseEditorParameters *findType(int ie)
con's avatar
con committed
117
{
118
    const EditorContentType et = static_cast<EditorContentType>(ie);
119
    return VcsBaseEditor::findType(editorParameters, sizeof(editorParameters)/sizeof(editorParameters[0]), et);
con's avatar
con committed
120
121
122
123
}

static inline QString debugCodec(const QTextCodec *c)
{
124
    return c ? QString::fromLatin1(c->name()) : QString::fromLatin1("Null codec");
con's avatar
con committed
125
126
}

127
128
// Ensure adding "..." to relative paths which is p4's convention
// for the current directory
129
static inline QString perforceRelativeFileArguments(const QString &args)
130
131
{
    if (args.isEmpty())
132
133
        return QLatin1String("...");
    return args + QLatin1String("/...");
134
135
}

136
static inline QStringList perforceRelativeProjectDirectory(const VcsBasePluginState &s)
137
{
138
    return QStringList(perforceRelativeFileArguments(s.relativeCurrentProject()));
139
140
141
142
143
144
145
146
}

// Clean user setting off diff-binary for 'p4 resolve' and 'p4 diff'.
static inline QProcessEnvironment overrideDiffEnvironmentVariable()
{
    QProcessEnvironment rc = QProcessEnvironment::systemEnvironment();
    rc.remove(QLatin1String("P4DIFF"));
    return rc;
147
148
}

149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
const char CMD_ID_PERFORCE_MENU[] = "Perforce.Menu";
const char CMD_ID_EDIT[] = "Perforce.Edit";
const char CMD_ID_ADD[] = "Perforce.Add";
const char CMD_ID_DELETE_FILE[] = "Perforce.Delete";
const char CMD_ID_OPENED[] = "Perforce.Opened";
const char CMD_ID_PROJECTLOG[] = "Perforce.ProjectLog";
const char CMD_ID_REPOSITORYLOG[] = "Perforce.RepositoryLog";
const char CMD_ID_REVERT[] = "Perforce.Revert";
const char CMD_ID_DIFF_CURRENT[] = "Perforce.DiffCurrent";
const char CMD_ID_DIFF_PROJECT[] = "Perforce.DiffProject";
const char CMD_ID_UPDATE_PROJECT[] = "Perforce.UpdateProject";
const char CMD_ID_REVERT_PROJECT[] = "Perforce.RevertProject";
const char CMD_ID_REVERT_UNCHANGED_PROJECT[] = "Perforce.RevertUnchangedProject";
const char CMD_ID_DIFF_ALL[] = "Perforce.DiffAll";
const char CMD_ID_SUBMIT[] = "Perforce.Submit";
const char CMD_ID_PENDING_CHANGES[] = "Perforce.PendingChanges";
const char CMD_ID_DESCRIBE[] = "Perforce.Describe";
const char CMD_ID_ANNOTATE_CURRENT[] = "Perforce.AnnotateCurrent";
const char CMD_ID_ANNOTATE[] = "Perforce.Annotate";
const char CMD_ID_FILELOG_CURRENT[] = "Perforce.FilelogCurrent";
const char CMD_ID_FILELOG[] = "Perforce.Filelog";
const char CMD_ID_UPDATEALL[] = "Perforce.UpdateAll";
con's avatar
con committed
171
172
173
174
175

////
// PerforcePlugin
////

176
177
178
179
180
181
PerforceResponse::PerforceResponse() :
    error(true),
    exitCode(-1)
{
}

Tobias Hunger's avatar
Tobias Hunger committed
182
PerforcePlugin *PerforcePlugin::m_instance = NULL;
con's avatar
con committed
183
184

PerforcePlugin::PerforcePlugin() :
Friedemann Kleint's avatar
Friedemann Kleint committed
185
    m_commandLocator(0),
con's avatar
con committed
186
187
188
189
    m_editAction(0),
    m_addAction(0),
    m_deleteAction(0),
    m_openedAction(0),
190
191
    m_revertFileAction(0),
    m_diffFileAction(0),
con's avatar
con committed
192
    m_diffProjectAction(0),
193
    m_updateProjectAction(0),
194
195
    m_revertProjectAction(0),
    m_revertUnchangedAction(0),
con's avatar
con committed
196
    m_diffAllAction(0),
197
    m_submitProjectAction(0),
con's avatar
con committed
198
199
200
201
202
203
    m_pendingAction(0),
    m_describeAction(0),
    m_annotateCurrentAction(0),
    m_annotateAction(0),
    m_filelogCurrentAction(0),
    m_filelogAction(0),
204
205
    m_logProjectAction(0),
    m_logRepositoryAction(0),
206
    m_submitCurrentLogAction(0),
207
    m_updateAllAction(0),
208
209
210
    m_submitActionTriggered(false),
    m_diffSelectedFiles(0),
    m_undoAction(0),
211
    m_redoAction(0)
con's avatar
con committed
212
213
214
{
}

215
216
217
218
219
static const VcsBaseSubmitEditorParameters submitParameters = {
    SUBMIT_MIMETYPE,
    PERFORCE_SUBMIT_EDITOR_ID,
    PERFORCE_SUBMIT_EDITOR_DISPLAY_NAME,
    VcsBaseSubmitEditorParameters::DiffFiles
con's avatar
con committed
220
221
};

222
bool PerforcePlugin::initialize(const QStringList & /* arguments */, QString *errorMessage)
con's avatar
con committed
223
{
224
225
226
    Context context(PERFORCE_CONTEXT);

    initializeVcs(new PerforceVersionControl(this), context);
227

228
    if (!MimeDatabase::addMimeTypes(QLatin1String(":/trolltech.perforce/Perforce.mimetypes.xml"), errorMessage))
con's avatar
con committed
229
        return false;
Tobias Hunger's avatar
Tobias Hunger committed
230
    m_instance = this;
con's avatar
con committed
231

232
    m_settings.fromSettings(ICore::settings());
con's avatar
con committed
233

234
    addAutoReleasedObject(new SettingsPage);
con's avatar
con committed
235
236

    // Editor factories
237
238
    addAutoReleasedObject(new VcsSubmitEditorFactory(&submitParameters,
        []() { return new PerforceSubmitEditor(&submitParameters); }));
con's avatar
con committed
239
240

    static const char *describeSlot = SLOT(describe(QString,QString));
241
    const int editorCount = sizeof(editorParameters) / sizeof(editorParameters[0]);
242
    const auto widgetCreator = []() { return new PerforceEditorWidget; };
243
    for (int i = 0; i < editorCount; i++)
244
        addAutoReleasedObject(new VcsEditorFactory(editorParameters + i, widgetCreator, this, describeSlot));
con's avatar
con committed
245

Friedemann Kleint's avatar
Friedemann Kleint committed
246
    const QString prefix = QLatin1String("p4");
247
    m_commandLocator = new CommandLocator("Perforce", prefix, prefix);
Friedemann Kleint's avatar
Friedemann Kleint committed
248
249
    addAutoReleasedObject(m_commandLocator);

250
    ActionContainer *mtools = ActionManager::actionContainer(Core::Constants::M_TOOLS);
con's avatar
con committed
251

252
253
254
255
    Core::ActionContainer *perforceContainer = ActionManager::createMenu(CMD_ID_PERFORCE_MENU);
    perforceContainer->menu()->setTitle(tr("&Perforce"));
    mtools->addMenu(perforceContainer);
    m_menuAction = perforceContainer->menu()->menuAction();
con's avatar
con committed
256

257
    Context perforcesubmitcontext(PERFORCE_SUBMIT_EDITOR_ID);
con's avatar
con committed
258

con's avatar
con committed
259
    Core::Command *command;
260

261
    m_diffFileAction = new ParameterAction(tr("Diff Current File"), tr("Diff \"%1\""), ParameterAction::EnabledWithParameter, this);
262
    command = ActionManager::registerAction(m_diffFileAction, CMD_ID_DIFF_CURRENT, context);
263
    command->setAttribute(Core::Command::CA_UpdateText);
264
    command->setDescription(tr("Diff Current File"));
265
    connect(m_diffFileAction, SIGNAL(triggered()), this, SLOT(diffCurrentFile()));
266
    perforceContainer->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
267
    m_commandLocator->appendCommand(command);
268

269
    m_annotateCurrentAction = new ParameterAction(tr("Annotate Current File"), tr("Annotate \"%1\""), ParameterAction::EnabledWithParameter, this);
270
    command = ActionManager::registerAction(m_annotateCurrentAction, CMD_ID_ANNOTATE_CURRENT, context);
271
    command->setAttribute(Core::Command::CA_UpdateText);
272
    command->setDescription(tr("Annotate Current File"));
273
    connect(m_annotateCurrentAction, SIGNAL(triggered()), this, SLOT(annotateCurrentFile()));
274
    perforceContainer->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
275
    m_commandLocator->appendCommand(command);
276

277
    m_filelogCurrentAction = new ParameterAction(tr("Filelog Current File"), tr("Filelog \"%1\""), ParameterAction::EnabledWithParameter, this);
278
    command = ActionManager::registerAction(m_filelogCurrentAction, CMD_ID_FILELOG_CURRENT, context);
279
    command->setAttribute(Core::Command::CA_UpdateText);
280
    command->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+P,Meta+F") : tr("Alt+P,Alt+F")));
281
    command->setDescription(tr("Filelog Current File"));
282
    connect(m_filelogCurrentAction, SIGNAL(triggered()), this, SLOT(filelogCurrentFile()));
283
    perforceContainer->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
284
    m_commandLocator->appendCommand(command);
285

286
    perforceContainer->addSeparator(context);
con's avatar
con committed
287

288
    m_editAction = new ParameterAction(tr("Edit"), tr("Edit \"%1\""), ParameterAction::EnabledWithParameter, this);
289
    command = ActionManager::registerAction(m_editAction, CMD_ID_EDIT, context);
con's avatar
con committed
290
    command->setAttribute(Core::Command::CA_UpdateText);
291
    command->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+P,Meta+E") : tr("Alt+P,Alt+E")));
292
    command->setDescription(tr("Edit File"));
con's avatar
con committed
293
    connect(m_editAction, SIGNAL(triggered()), this, SLOT(openCurrentFile()));
294
    perforceContainer->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
295
    m_commandLocator->appendCommand(command);
con's avatar
con committed
296

297
    m_addAction = new ParameterAction(tr("Add"), tr("Add \"%1\""), ParameterAction::EnabledWithParameter, this);
298
    command = ActionManager::registerAction(m_addAction, CMD_ID_ADD, context);
con's avatar
con committed
299
    command->setAttribute(Core::Command::CA_UpdateText);
300
    command->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+P,Meta+A") : tr("Alt+P,Alt+A")));
301
    command->setDescription(tr("Add File"));
con's avatar
con committed
302
    connect(m_addAction, SIGNAL(triggered()), this, SLOT(addCurrentFile()));
303
    perforceContainer->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
304
    m_commandLocator->appendCommand(command);
con's avatar
con committed
305

306
    m_deleteAction = new ParameterAction(tr("Delete..."), tr("Delete \"%1\"..."), ParameterAction::EnabledWithParameter, this);
307
    command = ActionManager::registerAction(m_deleteAction, CMD_ID_DELETE_FILE, context);
con's avatar
con committed
308
    command->setAttribute(Core::Command::CA_UpdateText);
309
    command->setDescription(tr("Delete File"));
310
    connect(m_deleteAction, SIGNAL(triggered()), this, SLOT(promptToDeleteCurrentFile()));
311
    perforceContainer->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
312
    m_commandLocator->appendCommand(command);
con's avatar
con committed
313

314
    m_revertFileAction = new ParameterAction(tr("Revert"), tr("Revert \"%1\""), ParameterAction::EnabledWithParameter, this);
315
    command = ActionManager::registerAction(m_revertFileAction, CMD_ID_REVERT, context);
con's avatar
con committed
316
    command->setAttribute(Core::Command::CA_UpdateText);
317
    command->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+P,Meta+R") : tr("Alt+P,Alt+R")));
318
    command->setDescription(tr("Revert File"));
319
    connect(m_revertFileAction, SIGNAL(triggered()), this, SLOT(revertCurrentFile()));
320
    perforceContainer->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
321
    m_commandLocator->appendCommand(command);
con's avatar
con committed
322

323
    perforceContainer->addSeparator(context);
con's avatar
con committed
324

325
    const QString diffProjectDefaultText = tr("Diff Current Project/Session");
326
    m_diffProjectAction = new ParameterAction(diffProjectDefaultText, tr("Diff Project \"%1\""), ParameterAction::AlwaysEnabled, this);
327
    command = ActionManager::registerAction(m_diffProjectAction, CMD_ID_DIFF_PROJECT, context);
con's avatar
con committed
328
    command->setAttribute(Core::Command::CA_UpdateText);
329
    command->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+P,Meta+D") : tr("Alt+P,Alt+D")));
330
    command->setDescription(diffProjectDefaultText);
con's avatar
con committed
331
    connect(m_diffProjectAction, SIGNAL(triggered()), this, SLOT(diffCurrentProject()));
332
    perforceContainer->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
333
    m_commandLocator->appendCommand(command);
con's avatar
con committed
334

335
    m_logProjectAction = new ParameterAction(tr("Log Project"), tr("Log Project \"%1\""), ParameterAction::EnabledWithParameter, this);
336
    command = ActionManager::registerAction(m_logProjectAction, CMD_ID_PROJECTLOG, context);
337
338
    command->setAttribute(Core::Command::CA_UpdateText);
    connect(m_logProjectAction, SIGNAL(triggered()), this, SLOT(logProject()));
339
    perforceContainer->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
340
    m_commandLocator->appendCommand(command);
341

342
    m_submitProjectAction = new ParameterAction(tr("Submit Project"), tr("Submit Project \"%1\""), ParameterAction::EnabledWithParameter, this);
343
    command = ActionManager::registerAction(m_submitProjectAction, CMD_ID_SUBMIT, context);
344
    command->setAttribute(Core::Command::CA_UpdateText);
345
    command->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+P,Meta+S") : tr("Alt+P,Alt+S")));
346
    connect(m_submitProjectAction, SIGNAL(triggered()), this, SLOT(startSubmitProject()));
347
    perforceContainer->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
348
    m_commandLocator->appendCommand(command);
con's avatar
con committed
349

350
    const QString updateProjectDefaultText = tr("Update Current Project");
351
    m_updateProjectAction = new ParameterAction(updateProjectDefaultText, tr("Update Project \"%1\""), ParameterAction::AlwaysEnabled, this);
352
    command = ActionManager::registerAction(m_updateProjectAction, CMD_ID_UPDATE_PROJECT, context);
353
    command->setDescription(updateProjectDefaultText);
354
355
    command->setAttribute(Core::Command::CA_UpdateText);
    connect(m_updateProjectAction, SIGNAL(triggered()), this, SLOT(updateCurrentProject()));
356
    perforceContainer->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
357
    m_commandLocator->appendCommand(command);
358

359
    m_revertUnchangedAction = new ParameterAction(tr("Revert Unchanged"), tr("Revert Unchanged Files of Project \"%1\""), ParameterAction::EnabledWithParameter, this);
360
    command = ActionManager::registerAction(m_revertUnchangedAction, CMD_ID_REVERT_UNCHANGED_PROJECT, context);
361
362
    command->setAttribute(Core::Command::CA_UpdateText);
    connect(m_revertUnchangedAction, SIGNAL(triggered()), this, SLOT(revertUnchangedCurrentProject()));
363
    perforceContainer->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
364
    m_commandLocator->appendCommand(command);
365

366
    m_revertProjectAction = new ParameterAction(tr("Revert Project"), tr("Revert Project \"%1\""), ParameterAction::EnabledWithParameter, this);
367
    command = ActionManager::registerAction(m_revertProjectAction, CMD_ID_REVERT_PROJECT, context);
368
369
    command->setAttribute(Core::Command::CA_UpdateText);
    connect(m_revertProjectAction, SIGNAL(triggered()), this, SLOT(revertCurrentProject()));
370
    perforceContainer->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
371
    m_commandLocator->appendCommand(command);
372

373
    perforceContainer->addSeparator(context);
374
375

    m_diffAllAction = new QAction(tr("Diff Opened Files"), this);
376
    command = ActionManager::registerAction(m_diffAllAction, CMD_ID_DIFF_ALL, context);
377
    connect(m_diffAllAction, SIGNAL(triggered()), this, SLOT(diffAllOpened()));
378
    perforceContainer->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
379
    m_commandLocator->appendCommand(command);
380

381
    m_openedAction = new QAction(tr("Opened"), this);
382
    command = ActionManager::registerAction(m_openedAction, CMD_ID_OPENED, context);
383
    command->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+P,Meta+O") : tr("Alt+P,Alt+O")));
384
    connect(m_openedAction, SIGNAL(triggered()), this, SLOT(printOpenedFileList()));
385
    perforceContainer->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
386
    m_commandLocator->appendCommand(command);
387
388

    m_logRepositoryAction = new QAction(tr("Repository Log"), this);
389
    command = ActionManager::registerAction(m_logRepositoryAction, CMD_ID_REPOSITORYLOG, context);
390
    connect(m_logRepositoryAction, SIGNAL(triggered()), this, SLOT(logRepository()));
391
    perforceContainer->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
392
    m_commandLocator->appendCommand(command);
393
394

    m_pendingAction = new QAction(tr("Pending Changes..."), this);
395
    command = ActionManager::registerAction(m_pendingAction, CMD_ID_PENDING_CHANGES, context);
396
    connect(m_pendingAction, SIGNAL(triggered()), this, SLOT(printPendingChanges()));
397
    perforceContainer->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
398
    m_commandLocator->appendCommand(command);
399
400

    m_updateAllAction = new QAction(tr("Update All"), this);
401
    command = ActionManager::registerAction(m_updateAllAction, CMD_ID_UPDATEALL, context);
402
    connect(m_updateAllAction, SIGNAL(triggered()), this, SLOT(updateAll()));
403
    perforceContainer->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
404
    m_commandLocator->appendCommand(command);
con's avatar
con committed
405

406
    perforceContainer->addSeparator(context);
407

con's avatar
con committed
408
    m_describeAction = new QAction(tr("Describe..."), this);
409
    command = ActionManager::registerAction(m_describeAction, CMD_ID_DESCRIBE, context);
con's avatar
con committed
410
    connect(m_describeAction, SIGNAL(triggered()), this, SLOT(describeChange()));
411
    perforceContainer->addAction(command);
con's avatar
con committed
412
413

    m_annotateAction = new QAction(tr("Annotate..."), this);
414
    command = ActionManager::registerAction(m_annotateAction, CMD_ID_ANNOTATE, context);
con's avatar
con committed
415
    connect(m_annotateAction, SIGNAL(triggered()), this, SLOT(annotate()));
416
    perforceContainer->addAction(command);
con's avatar
con committed
417
418

    m_filelogAction = new QAction(tr("Filelog..."), this);
419
    command = ActionManager::registerAction(m_filelogAction, CMD_ID_FILELOG, context);
con's avatar
con committed
420
    connect(m_filelogAction, SIGNAL(triggered()), this, SLOT(filelog()));
421
    perforceContainer->addAction(command);
con's avatar
con committed
422

423
424
    m_submitCurrentLogAction = new QAction(VcsBaseSubmitEditor::submitIcon(), tr("Submit"), this);
    command = ActionManager::registerAction(m_submitCurrentLogAction, SUBMIT_CURRENT, perforcesubmitcontext);
425
    command->setAttribute(Core::Command::CA_UpdateText);
con's avatar
con committed
426
427
    connect(m_submitCurrentLogAction, SIGNAL(triggered()), this, SLOT(submitCurrentLog()));

428
429
    m_diffSelectedFiles = new QAction(VcsBaseSubmitEditor::diffIcon(), tr("Diff &Selected Files"), this);
    command = ActionManager::registerAction(m_diffSelectedFiles, DIFF_SELECTED, perforcesubmitcontext);
con's avatar
con committed
430
431

    m_undoAction = new QAction(tr("&Undo"), this);
432
    command = ActionManager::registerAction(m_undoAction, Core::Constants::UNDO, perforcesubmitcontext);
con's avatar
con committed
433
434

    m_redoAction = new QAction(tr("&Redo"), this);
435
    command = ActionManager::registerAction(m_redoAction, Core::Constants::REDO, perforcesubmitcontext);
con's avatar
con committed
436
437
438
439
440
441

    return true;
}

void PerforcePlugin::extensionsInitialized()
{
442
    VcsBasePlugin::extensionsInitialized();
443
    getTopLevel();
con's avatar
con committed
444
445
446
447
}

void PerforcePlugin::openCurrentFile()
{
448
    const VcsBasePluginState state = currentState();
449
    QTC_ASSERT(state.hasFile(), return);
450
    vcsOpen(state.currentFileTopLevel(), state.relativeCurrentFile());
con's avatar
con committed
451
452
453
454
}

void PerforcePlugin::addCurrentFile()
{
455
    const VcsBasePluginState state = currentState();
456
    QTC_ASSERT(state.hasFile(), return);
457
    vcsAdd(state.currentFileTopLevel(), state.relativeCurrentFile());
con's avatar
con committed
458
459
460
461
}

void PerforcePlugin::revertCurrentFile()
{
462
    const VcsBasePluginState state = currentState();
463
    QTC_ASSERT(state.hasFile(), return);
464

465
    QTextCodec *codec = VcsBaseEditor::getCodec(state.currentFile());
con's avatar
con committed
466
    QStringList args;
467
468
469
470
    args << QLatin1String("diff") << QLatin1String("-sa") << state.relativeCurrentFile();
    PerforceResponse result = runP4Cmd(state.currentFileTopLevel(), args,
                                       RunFullySynchronous|CommandToWindow|StdErrToWindow|ErrorToWindow,
                                       QStringList(), QByteArray(), codec);
con's avatar
con committed
471
472
    if (result.error)
        return;
473
    // "foo.cpp - file(s) not opened on this client."
474
475
    // also revert when the output is empty: The file is unchanged but open then.
    if (result.stdOut.contains(QLatin1String(" - ")) || result.stdErr.contains(QLatin1String(" - ")))
476
        return;
con's avatar
con committed
477

478
479
    bool doNotRevert = false;
    if (!result.stdOut.isEmpty())
480
        doNotRevert = (QMessageBox::warning(ICore::dialogParent(), tr("p4 revert"),
481
482
                                            tr("The file has been changed. Do you want to revert it?"),
                                            QMessageBox::Yes, QMessageBox::No) == QMessageBox::No);
483
484
    if (doNotRevert)
        return;
con's avatar
con committed
485

486
    FileChangeBlocker fcb(state.currentFile());
487
488
489
490
    args.clear();
    args << QLatin1String("revert") << state.relativeCurrentFile();
    PerforceResponse result2 = runP4Cmd(state.currentFileTopLevel(), args,
                                        CommandToWindow|StdOutToWindow|StdErrToWindow|ErrorToWindow);
491
    if (!result2.error)
492
        perforceVersionControl()->emitFilesChanged(QStringList(state.currentFile()));
con's avatar
con committed
493
494
495
496
}

void PerforcePlugin::diffCurrentFile()
{
497
    const VcsBasePluginState state = currentState();
498
    QTC_ASSERT(state.hasFile(), return);
499
    p4Diff(state.currentFileTopLevel(), QStringList(state.relativeCurrentFile()));
con's avatar
con committed
500
501
502
503
}

void PerforcePlugin::diffCurrentProject()
{
504
    const VcsBasePluginState state = currentState();
505
    QTC_ASSERT(state.hasProject(), return);
506
    p4Diff(state.currentProjectTopLevel(), perforceRelativeProjectDirectory(state));
con's avatar
con committed
507
508
509
510
}

void PerforcePlugin::diffAllOpened()
{
511
    p4Diff(m_settings.topLevel(), QStringList());
con's avatar
con committed
512
513
}

514
void PerforcePlugin::updateCurrentProject()
515
{
516
    const VcsBasePluginState state = currentState();
517
    QTC_ASSERT(state.hasProject(), return);
518
    updateCheckout(state.currentProjectTopLevel(), perforceRelativeProjectDirectory(state));
519
520
}

521
522
523
524
525
526
void PerforcePlugin::updateAll()
{
    updateCheckout(m_settings.topLevel());
}

void PerforcePlugin::revertCurrentProject()
527
{
528
    const VcsBasePluginState state = currentState();
529
    QTC_ASSERT(state.hasProject(), return);
530
531

    const QString msg = tr("Do you want to revert all changes to the project \"%1\"?").arg(state.currentProjectName());
532
    if (QMessageBox::warning(ICore::dialogParent(), tr("p4 revert"), msg, QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
533
        return;
534
    revertProject(state.currentProjectTopLevel(), perforceRelativeProjectDirectory(state), false);
535
536
}

537
void PerforcePlugin::revertUnchangedCurrentProject()
538
{
539
    // revert -a.
540
    const VcsBasePluginState state = currentState();
541
    QTC_ASSERT(state.hasProject(), return);
542
    revertProject(state.currentProjectTopLevel(), perforceRelativeProjectDirectory(state), true);
543
544
}

545
546
547
548
549
550
551
552
553
554
555
556
bool PerforcePlugin::revertProject(const QString &workingDir, const QStringList &pathArgs, bool unchangedOnly)
{
    QStringList args(QLatin1String("revert"));
    if (unchangedOnly)
        args.push_back(QLatin1String("-a"));
    args.append(pathArgs);
    const PerforceResponse resp = runP4Cmd(workingDir, args,
                                           RunFullySynchronous|CommandToWindow|StdOutToWindow|StdErrToWindow|ErrorToWindow);
    return !resp.error;
}

void PerforcePlugin::updateCheckout(const QString &workingDir, const QStringList &dirs)
557
558
559
{
    QStringList args(QLatin1String("sync"));
    args.append(dirs);
560
561
562
563
564
565
566
    const PerforceResponse resp = runP4Cmd(workingDir, args,
                                           CommandToWindow|StdOutToWindow|StdErrToWindow|ErrorToWindow);
    if (dirs.empty()) {
        if (!workingDir.isEmpty())
            perforceVersionControl()->emitRepositoryChanged(workingDir);
    } else {
        const QChar slash = QLatin1Char('/');
567
        foreach (const QString &dir, dirs)
568
569
            perforceVersionControl()->emitRepositoryChanged(workingDir + slash + dir);
    }
570
571
}

con's avatar
con committed
572
573
void PerforcePlugin::printOpenedFileList()
{
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
    const PerforceResponse perforceResponse
            = runP4Cmd(m_settings.topLevel(), QStringList(QLatin1String("opened")),
                       CommandToWindow|StdErrToWindow|ErrorToWindow);
    if (perforceResponse.error || perforceResponse.stdOut.isEmpty())
        return;
    // reformat "//depot/file.cpp#1 - description" into "file.cpp # - description"
    // for context menu opening to work. This produces absolute paths, then.
    QString errorMessage;
    QString mapped;
    const QChar delimiter = QLatin1Char('#');
    foreach (const QString &line, perforceResponse.stdOut.split(QLatin1Char('\n'))) {
        mapped.clear();
        const int delimiterPos = line.indexOf(delimiter);
        if (delimiterPos > 0)
            mapped = fileNameFromPerforceName(line.left(delimiterPos), true, &errorMessage);
589
        if (mapped.isEmpty())
590
            VcsOutputWindow::appendSilently(line);
591
        else
592
            VcsOutputWindow::appendSilently(mapped + QLatin1Char(' ') + line.mid(delimiterPos));
593
    }
594
    VcsOutputWindow::instance()->popup(IOutputPane::ModeSwitch | IOutputPane::WithFocus);
con's avatar
con committed
595
596
}

597
void PerforcePlugin::startSubmitProject()
con's avatar
con committed
598
{
599

600
    if (raiseSubmitEditor())
con's avatar
con committed
601
602
        return;

603
    if (isCommitEditorOpen()) {
604
        VcsOutputWindow::appendWarning(tr("Another submit is currently executed."));
con's avatar
con committed
605
606
607
        return;
    }

608
    const VcsBasePluginState state = currentState();
609
    QTC_ASSERT(state.hasProject(), return);
610
611
612
613
614
615

    // Revert all unchanged files.
    if (!revertProject(state.currentProjectTopLevel(), perforceRelativeProjectDirectory(state), true))
        return;
    // Start a change
    QStringList args;
con's avatar
con committed
616

617
618
619
    args << QLatin1String("change") << QLatin1String("-o");
    PerforceResponse result = runP4Cmd(state.currentProjectTopLevel(), args,
                                       RunFullySynchronous|CommandToWindow|StdErrToWindow|ErrorToWindow);
con's avatar
con committed
620
    if (result.error) {
621
        cleanCommitMessageFile();
con's avatar
con committed
622
623
624
        return;
    }

625
    TempFileSaver saver;
626
    saver.setAutoRemove(false);
627
    saver.write(result.stdOut.toLatin1());
628
    if (!saver.finalize()) {
629
        VcsOutputWindow::appendError(saver.errorString());
630
631
632
633
        cleanCommitMessageFile();
        return;
    }
    m_commitMessageFileName = saver.fileName();
con's avatar
con committed
634

635
636
637
638
639
640
    args.clear();
    args << QLatin1String("fstat");
    args.append(perforceRelativeProjectDirectory(state));
    PerforceResponse fstatResult = runP4Cmd(state.currentProjectTopLevel(), args,
                                            RunFullySynchronous|CommandToWindow|StdErrToWindow|ErrorToWindow);
    if (fstatResult.error) {
641
        cleanCommitMessageFile();
con's avatar
con committed
642
643
644
        return;
    }

645
    QStringList fstatLines = fstatResult.stdOut.split(QLatin1Char('\n'));
con's avatar
con committed
646
    QStringList depotFileNames;
647
    foreach (const QString &line, fstatLines) {
648
        if (line.startsWith(QLatin1String("... depotFile")))
con's avatar
con committed
649
650
651
            depotFileNames.append(line.mid(14));
    }
    if (depotFileNames.isEmpty()) {
652
        VcsOutputWindow::appendWarning(tr("Project has no files"));
653
        cleanCommitMessageFile();
con's avatar
con committed
654
655
656
        return;
    }

657
    openPerforceSubmitEditor(m_commitMessageFileName, depotFileNames);
con's avatar
con committed
658
659
}

660
IEditor *PerforcePlugin::openPerforceSubmitEditor(const QString &fileName, const QStringList &depotFileNames)
con's avatar
con committed
661
{
662
    IEditor *editor = EditorManager::openEditor(fileName, PERFORCE_SUBMIT_EDITOR_ID);
dt's avatar
dt committed
663
    PerforceSubmitEditor *submitEditor = static_cast<PerforceSubmitEditor*>(editor);
664
    setSubmitEditor(submitEditor);
con's avatar
con committed
665
    submitEditor->restrictToProjectFiles(depotFileNames);
666
    submitEditor->registerActions(m_undoAction, m_redoAction, m_submitCurrentLogAction, m_diffSelectedFiles);
667
    connect(submitEditor, SIGNAL(diffSelectedFiles(QStringList)), this, SLOT(slotSubmitDiff(QStringList)));
668
    submitEditor->setCheckScriptWorkingDirectory(m_commitWorkingDirectory);
con's avatar
con committed
669
670
671
672
673
674
    return editor;
}

void PerforcePlugin::printPendingChanges()
{
    qApp->setOverrideCursor(Qt::WaitCursor);
675
    PendingChangesDialog dia(pendingChangesData(), ICore::mainWindow());
con's avatar
con committed
676
677
    qApp->restoreOverrideCursor();
    if (dia.exec() == QDialog::Accepted) {
678
679
680
        const int i = dia.changeNumber();
        QStringList args(QLatin1String("submit"));
        args << QLatin1String("-c") << QString::number(i);
681
682
        runP4Cmd(m_settings.topLevel(), args,
                 CommandToWindow|StdOutToWindow|StdErrToWindow|ErrorToWindow);
con's avatar
con committed
683
684
685
686
687
688
689
690
691
692
693
694
    }
}

void PerforcePlugin::describeChange()
{
    ChangeNumberDialog dia;
    if (dia.exec() == QDialog::Accepted && dia.number() > 0)
        describe(QString(), QString::number(dia.number()));
}

void PerforcePlugin::annotateCurrentFile()
{
695
    const VcsBasePluginState state = currentState();
696
    QTC_ASSERT(state.hasFile(), return);
697
    annotate(state.currentFileTopLevel(), state.relativeCurrentFile());
con's avatar
con committed
698
699
700
701
}

void PerforcePlugin::annotate()
{
702
    const QString file = QFileDialog::getOpenFileName(ICore::dialogParent(), tr("p4 annotate"));
703
704
705
706
    if (!file.isEmpty()) {
        const QFileInfo fi(file);
        annotate(fi.absolutePath(), fi.fileName());
    }
con's avatar
con committed
707
708
}

709
710
void PerforcePlugin::vcsAnnotate(const QString &workingDirectory, const QString &file,
                                 const QString &revision, int lineNumber)
711
{
712
    annotate(workingDirectory, file, revision, lineNumber);
713
714
715
716
717
718
}

void PerforcePlugin::annotate(const QString &workingDir,
                              const QString &fileName,
                              const QString &changeList /* = QString() */,
                              int lineNumber /* = -1 */)
con's avatar
con committed
719
{
720
    const QStringList files = QStringList(fileName);
721
722
723
    QTextCodec *codec = VcsBaseEditor::getCodec(workingDir, files);
    const QString id = VcsBaseEditor::getTitleId(workingDir, files, changeList);
    const QString source = VcsBaseEditor::getSource(workingDir, files);
con's avatar
con committed
724
    QStringList args;
725
    args << QLatin1String("annotate") << QLatin1String("-cqi");
726
    if (changeList.isEmpty())
727
        args << fileName;
728
    else
729
        args << (fileName + QLatin1Char('@') + changeList);
730
731
732
    const PerforceResponse result = runP4Cmd(workingDir, args,
                                             CommandToWindow|StdErrToWindow|ErrorToWindow,
                                             QStringList(), QByteArray(), codec);
con's avatar
con committed
733
    if (!result.error) {
734
        if (lineNumber < 1)
735
            lineNumber = VcsBaseEditor::lineNumberOfCurrentEditor();
736
737
738
        IEditor *ed = showOutputInEditor(tr("p4 annotate %1").arg(id),
                                         result.stdOut, VcsBase::AnnotateOutput,
                                         source, codec);
739
        VcsBaseEditor::gotoLineOfEditor(ed, lineNumber);
con's avatar
con committed
740
741
742
743
744
    }
}

void PerforcePlugin::filelogCurrentFile()
{
745
    const VcsBasePluginState state = currentState();
746
    QTC_ASSERT(state.hasFile(), return);
747
    filelog(state.currentFileTopLevel(), state.relativeCurrentFile(), true);
con's avatar
con committed
748
749
750
751
}

void PerforcePlugin::filelog()
{
752
    const QString file = QFileDialog::getOpenFileName(ICore::dialogParent(), tr("p4 filelog"));
753
754
    if (!file.isEmpty()) {
        const QFileInfo fi(file);
755
        filelog(fi.absolutePath(), fi.fileName());
756
    }
con's avatar
con committed
757
758
}

759
760
void PerforcePlugin::logProject()
{
761
    const VcsBasePluginState state = currentState();
762
    QTC_ASSERT(state.hasProject(), return);
763
764
765
766
767
    filelog(state.currentProjectTopLevel(), perforceRelativeFileArguments(state.relativeCurrentProject()));
}

void PerforcePlugin::logRepository()
{
768
    const VcsBasePluginState state = currentState();
769
    QTC_ASSERT(state.hasTopLevel(), return);
770
    filelog(state.topLevel(), perforceRelativeFileArguments(QString()));