perforceplugin.cpp 43.7 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2 3 4
**
** This file is part of Qt Creator
**
5
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6
**
7
** Contact: Nokia Corporation (qt-info@nokia.com)
con's avatar
con committed
8
**
9
** Commercial Usage
10
**
11 12 13 14
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
15
**
16
** GNU Lesser General Public License Usage
17
**
18 19 20 21 22 23
** 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.
24
**
25
** If you are unsure which license is appropriate for your use, please
26
** contact the sales department at http://www.qtsoftware.com/contact.
con's avatar
con committed
27
**
28
**************************************************************************/
hjk's avatar
hjk committed
29

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

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

41
#include <coreplugin/actionmanager/actionmanager.h>
con's avatar
con committed
42
#include <coreplugin/coreconstants.h>
hjk's avatar
hjk committed
43
#include <coreplugin/editormanager/editormanager.h>
con's avatar
con committed
44
#include <coreplugin/filemanager.h>
hjk's avatar
hjk committed
45
#include <coreplugin/icore.h>
con's avatar
con committed
46
#include <coreplugin/messagemanager.h>
hjk's avatar
hjk committed
47
#include <coreplugin/mimedatabase.h>
con's avatar
con committed
48
#include <coreplugin/uniqueidmanager.h>
hjk's avatar
hjk committed
49
#include <utils/qtcassert.h>
con's avatar
con committed
50
#include <utils/synchronousprocess.h>
51
#include <utils/parameteraction.h>
con's avatar
con committed
52 53 54 55
#include <vcsbase/basevcseditorfactory.h>
#include <vcsbase/basevcssubmiteditorfactory.h>
#include <vcsbase/vcsbaseeditor.h>

hjk's avatar
hjk committed
56
#include <QtCore/QtPlugin>
con's avatar
con committed
57 58
#include <QtCore/QDebug>
#include <QtCore/QDir>
hjk's avatar
hjk committed
59
#include <QtCore/QFileInfo>
con's avatar
con committed
60
#include <QtCore/QSettings>
hjk's avatar
hjk committed
61
#include <QtCore/QTemporaryFile>
con's avatar
con committed
62
#include <QtCore/QTextCodec>
hjk's avatar
hjk committed
63

con's avatar
con committed
64
#include <QtGui/QAction>
hjk's avatar
hjk committed
65 66
#include <QtGui/QFileDialog>
#include <QtGui/QMainWindow>
con's avatar
con committed
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
#include <QtGui/QMenu>
#include <QtGui/QMessageBox>

using namespace Perforce::Internal;

enum { p4Timeout = 20000 };

static const VCSBase::VCSBaseEditorParameters editorParameters[] = {
{
    VCSBase::RegularCommandOutput,
    "Perforce Command Log Editor",
    Perforce::Constants::C_PERFORCEEDITOR,
    "application/vnd.nokia.text.scs_commandlog",
    "scslog"},
{   VCSBase::LogOutput,
    "Perforce File Log Editor",
    Perforce::Constants::C_PERFORCEEDITOR,
    "application/vnd.nokia.text.scs_filelog",
    "scsfilelog"},
{    VCSBase::AnnotateOutput,
    "Perforce Annotation Editor",
    Perforce::Constants::C_PERFORCEEDITOR,
    "application/vnd.nokia.text.scs_annotation",
    "scsannotate"},
{   VCSBase::DiffOutput,
    "Perforce Diff Editor",
    Perforce::Constants::C_PERFORCEEDITOR,
    "text/x-patch","diff"}
};

// Utility to find a parameter set by type
static inline const VCSBase::VCSBaseEditorParameters *findType(int ie)
{
    const VCSBase::EditorContentType et = static_cast<VCSBase::EditorContentType>(ie);
    return  VCSBase::VCSBaseEditor::findType(editorParameters, sizeof(editorParameters)/sizeof(VCSBase::VCSBaseEditorParameters), et);
}

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

109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
// Return the project files relevant for VCS
static const QStringList currentProjectFiles(QString *name)
{
    QStringList files = VCSBase::VCSBaseSubmitEditor::currentProjectFiles(true, name);
    if (!files.empty()) {
        // Filter out mkspecs/qconfig.pri
        QString exclusion = QLatin1String("mkspecs");
        exclusion += QDir::separator();
        exclusion += QLatin1String("qconfig.pri");
        for (QStringList::iterator it = files.begin(); it != files.end(); ) {
            if (it->endsWith(exclusion)) {
                it = files.erase(it);
                break;
            } else {
                ++it;
            }
        }
    }
    return files;
}

con's avatar
con committed
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
const char * const PerforcePlugin::PERFORCE_MENU = "Perforce.Menu";
const char * const PerforcePlugin::EDIT = "Perforce.Edit";
const char * const PerforcePlugin::ADD = "Perforce.Add";
const char * const PerforcePlugin::DELETE_FILE = "Perforce.Delete";
const char * const PerforcePlugin::OPENED = "Perforce.Opened";
const char * const PerforcePlugin::REVERT = "Perforce.Revert";
const char * const PerforcePlugin::DIFF_CURRENT = "Perforce.DiffCurrent";
const char * const PerforcePlugin::DIFF_PROJECT = "Perforce.DiffProject";
const char * const PerforcePlugin::DIFF_ALL = "Perforce.DiffAll";
const char * const PerforcePlugin::RESOLVE = "Perforce.Resolve";
const char * const PerforcePlugin::SUBMIT = "Perforce.Submit";
const char * const PerforcePlugin::PENDING_CHANGES = "Perforce.PendingChanges";
const char * const PerforcePlugin::DESCRIBE = "Perforce.Describe";
const char * const PerforcePlugin::ANNOTATE_CURRENT = "Perforce.AnnotateCurrent";
const char * const PerforcePlugin::ANNOTATE = "Perforce.Annotate";
const char * const PerforcePlugin::FILELOG_CURRENT = "Perforce.FilelogCurrent";
const char * const PerforcePlugin::FILELOG = "Perforce.Filelog";
const char * const PerforcePlugin::SEPARATOR1 = "Perforce.Separator1";
const char * const PerforcePlugin::SEPARATOR2 = "Perforce.Separator2";
const char * const PerforcePlugin::SEPARATOR3 = "Perforce.Separator3";

////
// CoreListener
////

bool CoreListener::editorAboutToClose(Core::IEditor *editor)
{
    return m_plugin->editorAboutToClose(editor);
}

////
// PerforcePlugin
////

PerforcePlugin *PerforcePlugin::m_perforcePluginInstance = NULL;

PerforcePlugin::PerforcePlugin() :
    m_perforceOutputWindow(0),
    m_settingsPage(0),
    m_editAction(0),
    m_addAction(0),
    m_deleteAction(0),
    m_openedAction(0),
    m_revertAction(0),
    m_diffCurrentAction(0),
    m_diffProjectAction(0),
    m_diffAllAction(0),
    m_resolveAction(0),
178
    m_submitAction(0),    
con's avatar
con committed
179 180 181 182 183 184
    m_pendingAction(0),
    m_describeAction(0),
    m_annotateCurrentAction(0),
    m_annotateAction(0),
    m_filelogCurrentAction(0),
    m_filelogAction(0),
185 186 187 188 189
    m_submitCurrentLogAction(0),
    m_submitActionTriggered(false),
    m_diffSelectedFiles(0),
    m_undoAction(0),
    m_redoAction(0),
con's avatar
con committed
190 191 192 193 194 195 196 197 198 199
    m_changeTmpFile(0),
    m_coreListener(0),
    m_submitEditorFactory(0),
    m_versionControl(0)
{
}

static const VCSBase::VCSBaseSubmitEditorParameters submitParameters = {
    Perforce::Constants::SUBMIT_MIMETYPE,
    Perforce::Constants::PERFORCESUBMITEDITOR_KIND,
200
    Perforce::Constants::C_PERFORCESUBMITEDITOR
con's avatar
con committed
201 202
};

203
bool PerforcePlugin::initialize(const QStringList &arguments, QString *errorMessage)
con's avatar
con committed
204
{
205 206 207
    Q_UNUSED(arguments);
    Q_UNUSED(errorMessage);

con's avatar
con committed
208 209 210
    typedef VCSBase::VCSEditorFactory<PerforceEditor> PerforceEditorFactory;
    typedef VCSBase::VCSSubmitEditorFactory<PerforceSubmitEditor> PerforceSubmitEditorFactory;

211 212
    Core::ICore *core = Core::ICore::instance();
    if (!core->mimeDatabase()->addMimeTypes(QLatin1String(":/trolltech.perforce/Perforce.mimetypes.xml"), errorMessage))
con's avatar
con committed
213 214 215
        return false;
    m_perforcePluginInstance = this;

216
    if (QSettings *settings = core->settings())
con's avatar
con committed
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
        m_settings.fromSettings(settings);

    m_perforceOutputWindow = new PerforceOutputWindow(this);
    addObject(m_perforceOutputWindow);

    m_settingsPage = new SettingsPage;
    addObject(m_settingsPage);

    // Editor factories
    m_submitEditorFactory = new PerforceSubmitEditorFactory(&submitParameters);
    addObject(m_submitEditorFactory);

    static const char *describeSlot = SLOT(describe(QString,QString));
    const int editorCount = sizeof(editorParameters)/sizeof(VCSBase::VCSBaseEditorParameters);
    for (int i = 0; i < editorCount; i++) {
232
        m_editorFactories.push_back(new PerforceEditorFactory(editorParameters + i, this, describeSlot));
con's avatar
con committed
233 234 235 236 237 238 239 240 241 242
        addObject(m_editorFactories.back());
    }

    m_versionControl = new PerforceVersionControl(this);
    addObject(m_versionControl);

    m_coreListener = new CoreListener(this);
    addObject(m_coreListener);

    //register actions
243
    Core::ActionManager *am = Core::ICore::instance()->actionManager();
con's avatar
con committed
244

245
    Core::ActionContainer *mtools =
con's avatar
con committed
246 247
        am->actionContainer(Core::Constants::M_TOOLS);

248
    Core::ActionContainer *mperforce =
con's avatar
con committed
249 250 251
        am->createMenu(QLatin1String(PERFORCE_MENU));
    mperforce->menu()->setTitle(tr("&Perforce"));
    mtools->addMenu(mperforce);
252 253 254 255
    if (QAction *ma = mperforce->menu()->menuAction()) {
        ma->setEnabled(m_versionControl->isEnabled());
        connect(m_versionControl, SIGNAL(enabledChanged(bool)), ma, SLOT(setVisible(bool)));
    }
con's avatar
con committed
256 257 258 259 260

    QList<int> globalcontext;
    globalcontext << Core::Constants::C_GLOBAL_ID;

    QList<int> perforcesubmitcontext;
261 262
    perforcesubmitcontext << Core::UniqueIDManager::instance()->
            uniqueIdentifier(Constants::C_PERFORCESUBMITEDITOR);
con's avatar
con committed
263

con's avatar
con committed
264
    Core::Command *command;
con's avatar
con committed
265 266
    QAction *tmpaction;

267
    m_editAction = new Core::Utils::ParameterAction(tr("Edit"), tr("Edit \"%1\""), Core::Utils::ParameterAction::EnabledWithParameter, this);
con's avatar
con committed
268
    command = am->registerAction(m_editAction, PerforcePlugin::EDIT, globalcontext);
con's avatar
con committed
269
    command->setAttribute(Core::Command::CA_UpdateText);
con's avatar
con committed
270 271 272 273 274
    command->setDefaultKeySequence(QKeySequence(tr("Alt+P,Alt+E")));
    command->setDefaultText(tr("Edit File"));
    connect(m_editAction, SIGNAL(triggered()), this, SLOT(openCurrentFile()));
    mperforce->addAction(command);

275
    m_addAction = new Core::Utils::ParameterAction(tr("Add"), tr("Add \"%1\""), Core::Utils::ParameterAction::EnabledWithParameter, this);
con's avatar
con committed
276
    command = am->registerAction(m_addAction, PerforcePlugin::ADD, globalcontext);
con's avatar
con committed
277
    command->setAttribute(Core::Command::CA_UpdateText);
con's avatar
con committed
278 279 280 281 282
    command->setDefaultKeySequence(QKeySequence(tr("Alt+P,Alt+A")));
    command->setDefaultText(tr("Add File"));
    connect(m_addAction, SIGNAL(triggered()), this, SLOT(addCurrentFile()));
    mperforce->addAction(command);

283
    m_deleteAction = new Core::Utils::ParameterAction(tr("Delete"), tr("Delete \"%1\""), Core::Utils::ParameterAction::EnabledWithParameter, this);
con's avatar
con committed
284
    command = am->registerAction(m_deleteAction, PerforcePlugin::DELETE_FILE, globalcontext);
con's avatar
con committed
285
    command->setAttribute(Core::Command::CA_UpdateText);
con's avatar
con committed
286 287 288 289
    command->setDefaultText(tr("Delete File"));
    connect(m_deleteAction, SIGNAL(triggered()), this, SLOT(deleteCurrentFile()));
    mperforce->addAction(command);

290
    m_revertAction = new Core::Utils::ParameterAction(tr("Revert"), tr("Revert \"%1\""), Core::Utils::ParameterAction::EnabledWithParameter, this);
con's avatar
con committed
291
    command = am->registerAction(m_revertAction, PerforcePlugin::REVERT, globalcontext);
con's avatar
con committed
292
    command->setAttribute(Core::Command::CA_UpdateText);
con's avatar
con committed
293 294 295 296 297 298 299 300 301 302
    command->setDefaultKeySequence(QKeySequence(tr("Alt+P,Alt+R")));
    command->setDefaultText(tr("Revert File"));
    connect(m_revertAction, SIGNAL(triggered()), this, SLOT(revertCurrentFile()));
    mperforce->addAction(command);

    tmpaction = new QAction(this);
    tmpaction->setSeparator(true);
    command = am->registerAction(tmpaction, QLatin1String("Perforce.Sep.Edit"), globalcontext);
    mperforce->addAction(command);

303
    m_diffCurrentAction = new Core::Utils::ParameterAction(tr("Diff Current File"), tr("Diff \"%1\""), Core::Utils::ParameterAction::EnabledWithParameter, this);
con's avatar
con committed
304
    command = am->registerAction(m_diffCurrentAction, PerforcePlugin::DIFF_CURRENT, globalcontext);
con's avatar
con committed
305
    command->setAttribute(Core::Command::CA_UpdateText);
con's avatar
con committed
306 307 308 309
    command->setDefaultText(tr("Diff Current File"));
    connect(m_diffCurrentAction, SIGNAL(triggered()), this, SLOT(diffCurrentFile()));
    mperforce->addAction(command);

310
    m_diffProjectAction = new Core::Utils::ParameterAction(tr("Diff Current Project/Session"), tr("Diff Project \"%1\""), Core::Utils::ParameterAction::EnabledWithParameter, this);
con's avatar
con committed
311
    command = am->registerAction(m_diffProjectAction, PerforcePlugin::DIFF_PROJECT, globalcontext);
con's avatar
con committed
312
    command->setAttribute(Core::Command::CA_UpdateText);
con's avatar
con committed
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354
    command->setDefaultKeySequence(QKeySequence(tr("Alt+P,Alt+D")));
    command->setDefaultText(tr("Diff Current Project/Session"));
    connect(m_diffProjectAction, SIGNAL(triggered()), this, SLOT(diffCurrentProject()));
    mperforce->addAction(command);

    m_diffAllAction = new QAction(tr("Diff Opened Files"), this);
    command = am->registerAction(m_diffAllAction, PerforcePlugin::DIFF_ALL, globalcontext);
    connect(m_diffAllAction, SIGNAL(triggered()), this, SLOT(diffAllOpened()));
    mperforce->addAction(command);

    tmpaction = new QAction(this);
    tmpaction->setSeparator(true);
    command = am->registerAction(tmpaction, QLatin1String("Perforce.Sep.Diff"), globalcontext);
    mperforce->addAction(command);

    m_openedAction = new QAction(tr("Opened"), this);
    command = am->registerAction(m_openedAction, PerforcePlugin::OPENED, globalcontext);
    command->setDefaultKeySequence(QKeySequence(tr("Alt+P,Alt+O")));
    connect(m_openedAction, SIGNAL(triggered()), this, SLOT(printOpenedFileList()));
    mperforce->addAction(command);

    m_submitAction = new QAction(tr("Submit Project"), this);
    command = am->registerAction(m_submitAction, PerforcePlugin::SUBMIT, globalcontext);
    command->setDefaultKeySequence(QKeySequence(tr("Alt+P,Alt+S")));
    connect(m_submitAction, SIGNAL(triggered()), this, SLOT(submit()));
    mperforce->addAction(command);

    m_pendingAction = new QAction(tr("Pending Changes..."), this);
    command = am->registerAction(m_pendingAction, PerforcePlugin::PENDING_CHANGES, globalcontext);
    connect(m_pendingAction, SIGNAL(triggered()), this, SLOT(printPendingChanges()));
    mperforce->addAction(command);

    tmpaction = new QAction(this);
    tmpaction->setSeparator(true);
    command = am->registerAction(tmpaction, QLatin1String("Perforce.Sep.Changes"), globalcontext);
    mperforce->addAction(command);

    m_describeAction = new QAction(tr("Describe..."), this);
    command = am->registerAction(m_describeAction, PerforcePlugin::DESCRIBE, globalcontext);
    connect(m_describeAction, SIGNAL(triggered()), this, SLOT(describeChange()));
    mperforce->addAction(command);

355
    m_annotateCurrentAction = new Core::Utils::ParameterAction(tr("Annotate Current File"), tr("Annotate \"%1\""), Core::Utils::ParameterAction::EnabledWithParameter, this);
con's avatar
con committed
356
    command = am->registerAction(m_annotateCurrentAction, PerforcePlugin::ANNOTATE_CURRENT, globalcontext);
con's avatar
con committed
357
    command->setAttribute(Core::Command::CA_UpdateText);
con's avatar
con committed
358 359 360 361 362 363 364 365 366
    command->setDefaultText(tr("Annotate Current File"));
    connect(m_annotateCurrentAction, SIGNAL(triggered()), this, SLOT(annotateCurrentFile()));
    mperforce->addAction(command);

    m_annotateAction = new QAction(tr("Annotate..."), this);
    command = am->registerAction(m_annotateAction, PerforcePlugin::ANNOTATE, globalcontext);
    connect(m_annotateAction, SIGNAL(triggered()), this, SLOT(annotate()));
    mperforce->addAction(command);

367
    m_filelogCurrentAction = new Core::Utils::ParameterAction(tr("Filelog Current File"), tr("Filelog \"%1\""), Core::Utils::ParameterAction::EnabledWithParameter, this);
con's avatar
con committed
368
    command = am->registerAction(m_filelogCurrentAction, PerforcePlugin::FILELOG_CURRENT, globalcontext);
con's avatar
con committed
369
    command->setAttribute(Core::Command::CA_UpdateText);
con's avatar
con committed
370 371 372 373 374 375 376 377 378 379
    command->setDefaultKeySequence(QKeySequence(tr("Alt+P,Alt+F")));
    command->setDefaultText(tr("Filelog Current File"));
    connect(m_filelogCurrentAction, SIGNAL(triggered()), this, SLOT(filelogCurrentFile()));
    mperforce->addAction(command);

    m_filelogAction = new QAction(tr("Filelog..."), this);
    command = am->registerAction(m_filelogAction, PerforcePlugin::FILELOG, globalcontext);
    connect(m_filelogAction, SIGNAL(triggered()), this, SLOT(filelog()));
    mperforce->addAction(command);

380
    m_submitCurrentLogAction = new QAction(VCSBase::VCSBaseSubmitEditor::submitIcon(), tr("Submit"), this);
con's avatar
con committed
381 382 383
    command = am->registerAction(m_submitCurrentLogAction, Constants::SUBMIT_CURRENT, perforcesubmitcontext);
    connect(m_submitCurrentLogAction, SIGNAL(triggered()), this, SLOT(submitCurrentLog()));

384
    m_diffSelectedFiles = new QAction(VCSBase::VCSBaseSubmitEditor::diffIcon(), tr("Diff Selected Files"), this);
con's avatar
con committed
385 386 387 388 389 390 391 392
    command = am->registerAction(m_diffSelectedFiles, Constants::DIFF_SELECTED, perforcesubmitcontext);

    m_undoAction = new QAction(tr("&Undo"), this);
    command = am->registerAction(m_undoAction, Core::Constants::UNDO, perforcesubmitcontext);

    m_redoAction = new QAction(tr("&Redo"), this);
    command = am->registerAction(m_redoAction, Core::Constants::REDO, perforcesubmitcontext);

393
    connect(core, SIGNAL(contextChanged(Core::IContext *)),
con's avatar
con committed
394 395
        this, SLOT(updateActions()));

396
    connect(core->fileManager(), SIGNAL(currentFileChanged(const QString &)),
con's avatar
con committed
397 398 399 400 401 402 403
        this, SLOT(updateActions()));

    return true;
}

void PerforcePlugin::extensionsInitialized()
{
404
    m_projectExplorer = ProjectExplorer::ProjectExplorerPlugin::instance();
con's avatar
con committed
405 406 407 408 409 410 411 412 413 414
    if (m_projectExplorer) {
        connect(m_projectExplorer,
            SIGNAL(currentProjectChanged(ProjectExplorer::Project*)),
            this, SLOT(updateActions()));
    }
    updateActions();
}

void PerforcePlugin::openCurrentFile()
{
415
    vcsOpen(currentFileName());
con's avatar
con committed
416 417 418 419
}

void PerforcePlugin::addCurrentFile()
{
420
    vcsAdd(currentFileName());
con's avatar
con committed
421 422 423 424
}

void PerforcePlugin::deleteCurrentFile()
{
425
    vcsDelete(currentFileName());
con's avatar
con committed
426 427 428 429 430
}

void PerforcePlugin::revertCurrentFile()
{
    const QString fileName = currentFileName();
431
    QTextCodec *codec = VCSBase::VCSBaseEditor::getCodec(fileName);
con's avatar
con committed
432 433
    QStringList args;
    args << QLatin1String("diff") << QLatin1String("-sa");
434
    PerforceResponse result = runP4Cmd(args, QStringList(), CommandToWindow|StdErrToWindow|ErrorToWindow, codec);
con's avatar
con committed
435 436 437 438 439 440 441 442 443 444 445 446
    if (result.error)
        return;

    if (!result.stdOut.isEmpty()) {
        bool doNotRevert = QMessageBox::warning(0, tr("p4 revert"),
                                                tr("The file has been changed. Do you want to revert it?"),
                                                QMessageBox::Yes, QMessageBox::No)
                            == QMessageBox::No;
        if (doNotRevert)
            return;
    }

447 448
    Core::FileChangeBlocker fcb(fileName);
    fcb.setModifiedReload(true);
449
    PerforceResponse result2 = runP4Cmd(QStringList() << QLatin1String("revert") << fileName, QStringList(), CommandToWindow|StdOutToWindow|StdErrToWindow|ErrorToWindow);
con's avatar
con committed
450 451 452 453 454 455 456 457 458 459
}

void PerforcePlugin::diffCurrentFile()
{
    p4Diff(QStringList(currentFileName()));
}

void PerforcePlugin::diffCurrentProject()
{
    QString name;
460
    const QStringList nativeFiles = currentProjectFiles(&name);
con's avatar
con committed
461 462 463 464 465 466 467 468 469 470
    p4Diff(nativeFiles, name);
}

void PerforcePlugin::diffAllOpened()
{
    p4Diff(QStringList());
}

void PerforcePlugin::printOpenedFileList()
{
471
    Core::IEditor *e = Core::EditorManager::instance()->currentEditor();
con's avatar
con committed
472 473
    if (e)
        e->widget()->setFocus();
474
    PerforceResponse result = runP4Cmd(QStringList() << QLatin1String("opened"), QStringList(), CommandToWindow|StdOutToWindow|StdErrToWindow|ErrorToWindow);
con's avatar
con committed
475 476 477 478
}

void PerforcePlugin::submit()
{
479 480 481
    if (VCSBase::VCSBaseSubmitEditor::raiseSubmitEditor())
        return;

Friedemann Kleint's avatar
Friedemann Kleint committed
482 483 484
    QString errorMessage;
    if (!checkP4Configuration(&errorMessage)) {
        showOutput(errorMessage, true);
con's avatar
con committed
485 486 487 488
        return;
    }

    if (m_changeTmpFile) {
489
        showOutput(tr("Another submit is currently executed."), true);
con's avatar
con committed
490 491 492 493 494 495
        m_perforceOutputWindow->popup(false);
        return;
    }

    m_changeTmpFile = new QTemporaryFile(this);
    if (!m_changeTmpFile->open()) {
496
        showOutput(tr("Cannot create temporary file."), true);
497
        cleanChangeTmpFile();
con's avatar
con committed
498 499 500
        return;
    }

501 502
    PerforceResponse result = runP4Cmd(QStringList()<< QLatin1String("change") << QLatin1String("-o"), QStringList(),
                                       CommandToWindow|StdErrToWindow|ErrorToWindow);
con's avatar
con committed
503
    if (result.error) {
504
        cleanChangeTmpFile();
con's avatar
con committed
505 506 507 508 509 510 511 512
        return;
    }

    m_changeTmpFile->write(result.stdOut.toAscii());
    m_changeTmpFile->seek(0);

    // Assemble file list of project
    QString name;
513
    const QStringList nativeFiles = currentProjectFiles(&name);
514 515
    PerforceResponse result2 = runP4Cmd(QStringList(QLatin1String("fstat")), nativeFiles,
                                        CommandToWindow|StdErrToWindow|ErrorToWindow);
con's avatar
con committed
516
    if (result2.error) {
517
        cleanChangeTmpFile();
con's avatar
con committed
518 519 520 521 522
        return;
    }

    QStringList stdOutLines = result2.stdOut.split(QLatin1Char('\n'));
    QStringList depotFileNames;
hjk's avatar
hjk committed
523 524
    foreach (const QString &line, stdOutLines) {
        if (line.startsWith("... depotFile"))
con's avatar
con committed
525 526 527 528
            depotFileNames.append(line.mid(14));
    }
    if (depotFileNames.isEmpty()) {
        showOutput(tr("Project has no files"));
529
        cleanChangeTmpFile();
con's avatar
con committed
530 531 532 533 534 535 536 537
        return;
    }

    openPerforceSubmitEditor(m_changeTmpFile->fileName(), depotFileNames);
}

Core::IEditor *PerforcePlugin::openPerforceSubmitEditor(const QString &fileName, const QStringList &depotFileNames)
{
538 539 540
    Core::EditorManager *editorManager = Core::EditorManager::instance();
    Core::IEditor *editor = editorManager->openEditor(fileName, Constants::PERFORCESUBMITEDITOR_KIND);
    editorManager->ensureEditorManagerVisible();
con's avatar
con committed
541
    PerforceSubmitEditor *submitEditor = dynamic_cast<PerforceSubmitEditor*>(editor);
hjk's avatar
hjk committed
542
    QTC_ASSERT(submitEditor, return 0);
con's avatar
con committed
543
    submitEditor->restrictToProjectFiles(depotFileNames);
544
    submitEditor->registerActions(m_undoAction, m_redoAction, m_submitCurrentLogAction, m_diffSelectedFiles);
con's avatar
con committed
545 546 547 548 549 550 551
    connect(submitEditor, SIGNAL(diffSelectedFiles(QStringList)), this, SLOT(slotDiff(QStringList)));
    return editor;
}

void PerforcePlugin::printPendingChanges()
{
    qApp->setOverrideCursor(Qt::WaitCursor);
552
    PendingChangesDialog dia(pendingChangesData(), Core::ICore::instance()->mainWindow());
con's avatar
con committed
553 554
    qApp->restoreOverrideCursor();
    if (dia.exec() == QDialog::Accepted) {
555 556 557 558
        const int i = dia.changeNumber();
        QStringList args(QLatin1String("submit"));
        args << QLatin1String("-c") << QString::number(i);
        runP4Cmd(args, QStringList(), CommandToWindow|StdOutToWindow|StdErrToWindow|ErrorToWindow);
con's avatar
con committed
559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584
    }
}

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

void PerforcePlugin::annotateCurrentFile()
{
    const QString file = currentFileName();
    if (!file.isEmpty())
        annotate(file);
}

void PerforcePlugin::annotate()
{
    const QString file = QFileDialog::getOpenFileName(0, tr("p4 annotate"), currentFileName());
    if (!file.isEmpty())
        annotate(file);
}

void PerforcePlugin::annotate(const QString &fileName)
{
585
    QTextCodec *codec = VCSBase::VCSBaseEditor::getCodec(fileName);
con's avatar
con committed
586 587
    QStringList args;
    args << QLatin1String("annotate") << QLatin1String("-cqi") << fileName;
588 589
    const PerforceResponse result = runP4Cmd(args, QStringList(),
                                             CommandToWindow|StdErrToWindow|ErrorToWindow, codec);
con's avatar
con committed
590 591
    if (!result.error) {
        const QFileInfo fi(fileName);
592 593
        showOutputInEditor(tr("p4 annotate %1").arg(fi.fileName()),
            result.stdOut, VCSBase::AnnotateOutput, codec);
con's avatar
con committed
594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612
    }
}

void PerforcePlugin::filelogCurrentFile()
{
    const QString file = currentFileName();
    if (!file.isEmpty())
        filelog(file);
}

void PerforcePlugin::filelog()
{
    const QString file = QFileDialog::getOpenFileName(0, tr("p4 filelog"), currentFileName());
    if (!file.isEmpty())
        filelog(file);
}

void PerforcePlugin::filelog(const QString &fileName)
{
613
    QTextCodec *codec = VCSBase::VCSBaseEditor::getCodec(fileName);
con's avatar
con committed
614 615
    QStringList args;
    args << QLatin1String("filelog") << QLatin1String("-li") << fileName;
616 617
    const PerforceResponse result = runP4Cmd(args, QStringList(),
                                             CommandToWindow|StdErrToWindow|ErrorToWindow, codec);
con's avatar
con committed
618 619
    if (!result.error) {
        const QFileInfo fi(fileName);
620 621
        showOutputInEditor(tr("p4 filelog %1").arg(fi.fileName()),
            result.stdOut, VCSBase::LogOutput, codec);
con's avatar
con committed
622 623 624 625 626
    }
}

void PerforcePlugin::updateActions()
{
627 628 629 630 631 632 633 634 635 636 637
    const QString fileName = currentFileName();
    const QString baseName = fileName.isEmpty() ? fileName : QFileInfo(fileName).fileName();

    m_editAction->setParameter(baseName);
    m_addAction->setParameter(baseName);
    m_deleteAction->setParameter(baseName);
    m_revertAction->setParameter(baseName);
    m_diffCurrentAction->setParameter(baseName);
    m_annotateCurrentAction->setParameter(baseName);
    m_filelogCurrentAction->setParameter(baseName);
    
con's avatar
con committed
638
    if (m_projectExplorer && m_projectExplorer->currentProject()) {
639
        m_diffProjectAction->setParameter(m_projectExplorer->currentProject()->name());
con's avatar
con committed
640 641
        m_submitAction->setEnabled(true);
    } else {
642
        m_diffProjectAction->setParameter(QString());
con's avatar
con committed
643 644 645 646 647 648 649 650 651 652 653 654
        m_submitAction->setEnabled(false);
    }
    m_diffAllAction->setEnabled(true);
    m_openedAction->setEnabled(true);
    m_describeAction->setEnabled(true);
    m_annotateAction->setEnabled(true);
    m_filelogAction->setEnabled(true);
    m_pendingAction->setEnabled(true);
}

bool PerforcePlugin::managesDirectory(const QString &directory) const
{
Friedemann Kleint's avatar
Friedemann Kleint committed
655
    if (!checkP4Configuration())
656
        return false;
con's avatar
con committed
657 658 659 660
    const QString p4Path = directory + QLatin1String("/...");
    QStringList args;
    args << QLatin1String("fstat") << QLatin1String("-m1") << p4Path;

661
    const PerforceResponse result = runP4Cmd(args, QStringList(), 0u);
con's avatar
con committed
662 663 664 665 666 667
    return result.stdOut.contains("depotFile") || result.stdErr.contains("... - no such file(s)");
}

QString PerforcePlugin::findTopLevelForDirectory(const QString & /* dir */) const
{
    // First check with p4 client -o
668
    PerforceResponse result = runP4Cmd(QStringList() << QLatin1String("client") << QLatin1String("-o"), QStringList(), 0u);
con's avatar
con committed
669 670 671 672
    if (result.error)
        return QString::null;

    QRegExp regExp(QLatin1String("(\\n|\\r\\n|\\r)Root:\\s*(.*)(\\n|\\r\\n|\\r)"));
hjk's avatar
hjk committed
673
    QTC_ASSERT(regExp.isValid(), /**/);
con's avatar
con committed
674 675 676 677 678 679 680 681 682 683 684
    regExp.setMinimal(true);
    if (regExp.indexIn(result.stdOut) != -1) {
        QString file = regExp.cap(2).trimmed();
        if (QFileInfo(file).exists())
            return file;
    }
    return QString::null;
}

bool PerforcePlugin::vcsOpen(const QString &fileName)
{
685 686
    PerforceResponse result = runP4Cmd(QStringList() << QLatin1String("edit") << QDir::toNativeSeparators(fileName), QStringList(),
                                       CommandToWindow|StdOutToWindow|StdErrToWindow|ErrorToWindow);
con's avatar
con committed
687 688 689 690 691
    return !result.error;
}

bool PerforcePlugin::vcsAdd(const QString &fileName)
{
692 693
    PerforceResponse result = runP4Cmd(QStringList() << QLatin1String("add") << fileName, QStringList(),
                                       CommandToWindow|StdOutToWindow|StdErrToWindow|ErrorToWindow);
con's avatar
con committed
694 695 696 697 698
    return !result.error;
}

bool PerforcePlugin::vcsDelete(const QString &fileName)
{
699 700 701 702
    PerforceResponse result = runP4Cmd(QStringList() << QLatin1String("revert") << fileName, QStringList(),
                                       CommandToWindow|StdOutToWindow|StdErrToWindow|ErrorToWindow);
    PerforceResponse result2 = runP4Cmd(QStringList() << QLatin1String("delete") << fileName, QStringList(),
                                        CommandToWindow|StdOutToWindow|StdErrToWindow|ErrorToWindow);
con's avatar
con committed
703 704 705 706 707 708 709 710 711 712 713 714
    // TODO need to carefully parse the actual messages from perforce
    // or do a fstat before to decide what to do

    // Different states are:
    // File is in depot and unopened => p4 delete %
    // File is in depot and opened => p4 revert %, p4 delete %
    // File is not in depot => p4 revert %
    return !(result.error && result2.error);
}

PerforceResponse PerforcePlugin::runP4Cmd(const QStringList &args,
                                          const QStringList &extraArgs,
715
                                          unsigned logFlags,
con's avatar
con committed
716 717 718 719 720 721
                                          QTextCodec *outputCodec) const
{
    if (Perforce::Constants::debug)
        qDebug() << "PerforcePlugin::runP4Cmd" << args << extraArgs << debugCodec(outputCodec);
    PerforceResponse response;
    response.error = true;
Friedemann Kleint's avatar
Friedemann Kleint committed
722
    if (!checkP4Configuration(&response.message)) {
con's avatar
con committed
723 724 725 726 727 728 729 730 731
        m_perforceOutputWindow->append(response.message, true);
        return response;
    }

    // handle extra args
    QTemporaryFile tempfile;
    tempfile.setAutoRemove(true);
    const QChar newLine = QLatin1Char('\n');
    const QChar blank = QLatin1Char(' ');
732
    QStringList actualArgs = m_settings.basicP4Args();
con's avatar
con committed
733 734 735 736 737 738 739 740 741 742 743 744 745
    if (!extraArgs.isEmpty()) {
        if (tempfile.open()) {
            QTextStream stream(&tempfile);
            stream << extraArgs.join(QString(newLine));
            actualArgs << QLatin1String("-x") << tempfile.fileName();
            tempfile.close();
        } else {
            qWarning()<<"Could not create temporary file. Appending all file names to command line.";
            actualArgs << extraArgs;
        }
    }
    actualArgs << args;

746
    if (logFlags & CommandToWindow) {
747
        QString command = m_settings.p4Command();
748 749 750 751 752 753
        command += blank;
        command += actualArgs.join(QString(blank));
        const QString timeStamp = QTime::currentTime().toString(QLatin1String("HH:mm"));
        const QString outputText = tr("%1 Executing: %2\n").arg(timeStamp, command);
        showOutput(outputText, false);
    }
con's avatar
con committed
754 755 756 757 758 759 760 761

    // Run, connect stderr to the output window
    Core::Utils::SynchronousProcess process;
    process.setTimeout(p4Timeout);
    process.setStdOutCodec(outputCodec);
    process.setEnvironment(environment());

    // connect stderr to the output window if desired
762
    if (logFlags & StdErrToWindow) {
con's avatar
con committed
763 764 765 766 767
        process.setStdErrBufferedSignalsEnabled(true);
        connect(&process, SIGNAL(stdErrBuffered(QString,bool)), m_perforceOutputWindow, SLOT(append(QString,bool)));
    }

    // connect stdout to the output window if desired
768
    if (logFlags & StdOutToWindow) {
con's avatar
con committed
769 770 771 772
        process.setStdOutBufferedSignalsEnabled(true);
        connect(&process, SIGNAL(stdOutBuffered(QString,bool)), m_perforceOutputWindow, SLOT(append(QString,bool)));
    }

773
    const Core::Utils::SynchronousProcessResponse sp_resp = process.run(m_settings.p4Command(), actualArgs);
con's avatar
con committed
774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790
    if (Perforce::Constants::debug)
        qDebug() << sp_resp;

    response.error = true;
    response.stdErr = sp_resp.stdErr;
    response.stdOut = sp_resp.stdOut;
    switch (sp_resp.result) {
    case Core::Utils::SynchronousProcessResponse::Finished:
        response.error = false;
        break;
    case Core::Utils::SynchronousProcessResponse::FinishedError:
        response.message = tr("The process terminated with exit code %1.").arg(sp_resp.exitCode);
        break;
    case Core::Utils::SynchronousProcessResponse::TerminatedAbnormally:
        response.message = tr("The process terminated abnormally.");
        break;
    case Core::Utils::SynchronousProcessResponse::StartFailed:
791
        response.message = tr("Could not start perforce '%1'. Please check your settings in the preferences.").arg(m_settings.p4Command());
con's avatar
con committed
792 793
        break;
    case Core::Utils::SynchronousProcessResponse::Hang:
794
        response.message = tr("Perforce did not respond within timeout limit (%1 ms).").arg(p4Timeout );
con's avatar
con committed
795 796
        break;
    }
797 798 799 800 801 802
    if (response.error) {
        if (Perforce::Constants::debug)
            qDebug() << response.message;
        if (logFlags & ErrorToWindow)
            m_perforceOutputWindow->append(response.message, true);
    }
con's avatar
con committed
803 804 805 806 807 808 809
    return response;
}

Core::IEditor * PerforcePlugin::showOutputInEditor(const QString& title, const QString output,
                                                   int editorType, QTextCodec *codec)
{
    const VCSBase::VCSBaseEditorParameters *params = findType(editorType);
hjk's avatar
hjk committed
810
    QTC_ASSERT(params, return 0);
con's avatar
con committed
811 812 813 814
    const QString kind = QLatin1String(params->kind);
    if (Perforce::Constants::debug)
        qDebug() << "PerforcePlugin::showOutputInEditor" << title << kind <<  "Size= " << output.size() <<  " Type=" << editorType << debugCodec(codec);
    QString s = title;
815
    Core::IEditor *editor = Core::EditorManager::instance()->
con's avatar
con committed
816
        newFile(kind, &s, output.toLocal8Bit());
817
    PerforceEditor *e = qobject_cast<PerforceEditor*>(editor->widget());
con's avatar
con committed
818 819 820 821 822 823
    if (!e)
        return 0;
    s.replace(QLatin1Char(' '), QLatin1Char('_'));
    e->setSuggestedFileName(s);
    if (codec)
        e->setCodec(codec);
824 825 826
    Core::IEditor *ie = e->editableInterface();
    Core::EditorManager::instance()->activateEditor(ie);
    return ie;
con's avatar
con committed
827 828 829 830 831 832
}

QStringList PerforcePlugin::environment() const
{
    QStringList newEnv = QProcess::systemEnvironment();
    const QString name = "P4DIFF";
833
    for (int i = 0; i < newEnv.count(); ++i) {
con's avatar
con committed
834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851
        if (newEnv.at(i).startsWith(name)) {
            newEnv.removeAt(i);
            return newEnv;
        }
    }
    return newEnv;
}

void PerforcePlugin::slotDiff(const QStringList &files)
{
    p4Diff(files);
}

void PerforcePlugin::p4Diff(const QStringList &files, QString diffname)
{
    Core::IEditor *editor = 0;
    bool displayInEditor = true;
    Core::IEditor *existingEditor = 0;
852
    QTextCodec *codec = files.empty() ? static_cast<QTextCodec *>(0) : VCSBase::VCSBaseEditor::getCodec(files.front());
con's avatar
con committed
853 854 855 856 857 858 859 860 861 862 863 864
    if (Perforce::Constants::debug)
        qDebug() << Q_FUNC_INFO << files << debugCodec(codec);

    // 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
    if (files.count() == 1) {
        const QString fileName = files.at(0);
        if (diffname.isEmpty()) {
            const QFileInfo fi(fileName);
            diffname = fi.fileName();
        }

865
        foreach (Core::IEditor *ed, Core::EditorManager::instance()->openedEditors()) {
866
            if (ed->file()->property("originalFileName").toString() == fileName) {
con's avatar
con committed
867 868 869 870 871 872 873
                existingEditor = ed;
                displayInEditor = false;
                break;
            }
        }
    }

874
    const PerforceResponse result = runP4Cmd(QStringList() << QLatin1String("diff") << QLatin1String("-du"), files, CommandToWindow|StdErrToWindow|ErrorToWindow, codec);
con's avatar
con committed
875 876 877 878 879 880 881 882 883
    if (result.error)
        return;

    if (displayInEditor)
        editor = showOutputInEditor(tr("p4 diff %1").arg(diffname), result.stdOut, VCSBase::DiffOutput, codec);


    if (files.count() == 1) {
        if (displayInEditor && editor != 0) {
884
            editor->file()->setProperty("originalFileName", files.at(0));
con's avatar
con committed
885 886 887
        } else if (!displayInEditor && existingEditor) {
            if (existingEditor) {
                existingEditor->createNew(result.stdOut);
888
                Core::EditorManager::instance()->activateEditor(existingEditor);
con's avatar
con committed
889 890 891 892 893 894 895
            }
        }
    }
}

void PerforcePlugin::describe(const QString & source, const QString &n)
{
896
    QTextCodec *codec = source.isEmpty() ? static_cast<QTextCodec *>(0) : VCSBase::VCSBaseEditor::getCodec(source);
con's avatar
con committed
897 898
    QStringList args;
    args << QLatin1String("describe") << QLatin1String("-du") << n;
899
    const PerforceResponse result = runP4Cmd(args, QStringList(), CommandToWindow|StdErrToWindow|ErrorToWindow, codec);
con's avatar
con committed
900 901 902 903 904 905
    if (!result.error)
        showOutputInEditor(tr("p4 describe %1").arg(n), result.stdOut, VCSBase::DiffOutput, codec);
}

void PerforcePlugin::submitCurrentLog()
{
906
    m_submitActionTriggered = true;
907
    Core::EditorManager *em = Core::EditorManager::instance();
908
    em->closeEditors(QList<Core::IEditor*>() << em->currentEditor());
con's avatar
con committed
909 910
}

911 912 913 914 915 916 917 918 919 920
void PerforcePlugin::cleanChangeTmpFile()
{
    if (m_changeTmpFile) {
        if (m_changeTmpFile->isOpen())
            m_changeTmpFile->close();
        delete m_changeTmpFile;
        m_changeTmpFile = 0;
    }
}

con's avatar
con committed
921 922 923 924
bool PerforcePlugin::editorAboutToClose(Core::IEditor *editor)
{
    if (!m_changeTmpFile || !editor)
        return true;
925
    Core::ICore *core = Core::ICore::instance();
con's avatar
con committed
926 927 928
    Core::IFile *fileIFace = editor->file();
    if (!fileIFace)
        return true;
929 930 931
    const PerforceSubmitEditor *perforceEditor = qobject_cast<PerforceSubmitEditor *>(editor);
    if (!perforceEditor)
        return true;
con's avatar
con committed
932 933
    QFileInfo editorFile(fileIFace->fileName());
    QFileInfo changeFile(m_changeTmpFile->fileName());
934 935 936
    if (editorFile.absoluteFilePath() == changeFile.absoluteFilePath()) {
        // Prompt the user. Force a prompt unless submit was actually invoked (that
        // is, the editor was closed or shutdown).
937 938 939
         const VCSBase::VCSBaseSubmitEditor::PromptSubmitResult answer =
            perforceEditor->promptSubmit(tr("Closing p4 Editor"),
                                         tr("Do you want to submit this change list?"),
940 941 942
                                         tr("The commit message check failed. Do you want to submit this change list"),
                                         !m_submitActionTriggered);
         m_submitActionTriggered = false;
943 944

        if (answer == VCSBase::VCSBaseSubmitEditor::SubmitCanceled)
con's avatar
con committed
945 946
            return false;

947
        core->fileManager()->blockFileChange(fileIFace);
con's avatar
con committed
948
        fileIFace->save();
949
        core->fileManager()->unblockFileChange(fileIFace);
950
        if (answer == VCSBase::VCSBaseSubmitEditor::SubmitConfirmed) {
951
            m_changeTmpFile->seek(0);
con's avatar
con committed
952
            QByteArray change = m_changeTmpFile->readAll();
Friedemann Kleint's avatar
Friedemann Kleint committed
953 954 955
            QString errorMessage;
            if (!checkP4Configuration(&errorMessage)) {
                showOutput(errorMessage, true);
con's avatar
con committed
956 957
                return false;
            }
958

con's avatar
con committed
959 960 961 962
            QProcess proc;
            proc.setEnvironment(environment());

            QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
963 964
            proc.start(m_settings.p4Command(),
                m_settings.basicP4Args() << QLatin1String("submit") << QLatin1String("-i"));
965 966
            if (!proc.waitForStarted(p4Timeout)) {
                showOutput(tr("Cannot execute p4 submit."), true);
con's avatar
con committed
967 968 969 970 971 972 973
                QApplication::restoreOverrideCursor();
                return false;
            }
            proc.write(change);
            proc.closeWriteChannel();

            if (!proc.waitForFinished()) {
974
                showOutput(tr("Cannot execute p4 submit."), true);
con's avatar
con committed
975 976 977
                QApplication::restoreOverrideCursor();
                return false;
            }
978
            const QString output = QString::fromUtf8(proc.readAll());
con's avatar
con committed
979
            showOutput(output);
980 981
            if (output.contains(QLatin1String("Out of date files must be resolved or reverted)"))) {
                QMessageBox::warning(editor->widget(), tr("Pending change"), tr("Could not submit the change, because your workspace was out of date. Created a pending submit instead."));
con's avatar
con committed
982 983 984
            }
            QApplication::restoreOverrideCursor();
        }
985
        cleanChangeTmpFile();
con's avatar
con committed
986 987 988 989 990 991
    }
    return true;
}

void PerforcePlugin::openFiles(const QStringList &files)
{
992 993
    Core::EditorManager *em = Core::EditorManager::instance();
    foreach (const QString &s, files)
994 995
        em->openEditor(clientFilePath(s));
    em->ensureEditorManagerVisible();
con's avatar
con committed
996 997 998 999
}

QString PerforcePlugin::clientFilePath(const QString &serverFilePath)
{
Friedemann Kleint's avatar
Friedemann Kleint committed
1000
    if (!checkP4Configuration())
hjk's avatar
hjk committed
1001
        return QString();
con's avatar
con committed
1002 1003 1004 1005

    QApplication::setOverrideCursor(Qt::WaitCursor);
    QProcess proc;
    proc.setEnvironment(environment());
1006 1007
    proc.start(m_settings.p4Command(),
        m_settings.basicP4Args() << QLatin1String("fstat") << serverFilePath);
con's avatar
con committed
1008

hjk's avatar
hjk committed
1009
    QString path;
con's avatar
con committed
1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024
    if (proc.waitForFinished(3000)) {
        QString output = QString::fromUtf8(proc.readAllStandardOutput());
        if (!output.isEmpty()) {
            QRegExp r(QLatin1String("\\.\\.\\.\\sclientFile\\s(.+)\n"));
            r.setMinimal(true);
            if (r.indexIn(output) != -1)
                path = r.cap(1).trimmed();
        }
    }
    QApplication::restoreOverrideCursor();
    return path;
}

QString PerforcePlugin::currentFileName()
{
1025
    QString fileName = Core::ICore::instance()->fileManager()->currentFile();
con's avatar
con committed
1026 1027 1028 1029 1030 1031 1032 1033 1034

    // TODO: Use FileManager::fixPath
    const QFileInfo fileInfo(fileName);
    if (fileInfo.exists())
        fileName = fileInfo.absoluteFilePath();
    fileName = QDir::toNativeSeparators(fileName);
    return fileName;
}

Friedemann Kleint's avatar
Friedemann Kleint committed
1035
bool PerforcePlugin::checkP4Configuration(QString *errorMessage /* = 0 */) const
con's avatar
con committed
1036
{
Friedemann Kleint's avatar
Friedemann Kleint committed
1037 1038 1039 1040 1041
    if (m_settings.isValid())
        return true;
    if (errorMessage)
        *errorMessage = tr("Invalid configuration: %1").arg(m_settings.errorString());
    return false;
con's avatar
con committed
1042 1043 1044 1045 1046
}

QString PerforcePlugin::pendingChangesData()
{
    QString data;
Friedemann Kleint's avatar
Friedemann Kleint committed
1047
    if (!checkP4Configuration())
con's avatar
con committed
1048 1049 1050 1051 1052
        return data;

    QString user;
    QProcess proc;
    proc.setEnvironment(environment());
1053 1054
    proc.start(m_settings.p4Command(),
        m_settings.basicP4Args() << QLatin1String("info"));
con's avatar
con committed
1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065
    if (proc.waitForFinished(3000)) {
        QString output = QString::fromUtf8(proc.readAllStandardOutput());
        if (!output.isEmpty()) {
            QRegExp r(QLatin1String("User\\sname:\\s(\\S+)\\s*\n"));
            r.setMinimal(true);
            if (r.indexIn(output) != -1)
                user = r.cap(1).trimmed();
        }
    }
    if (user.isEmpty())
        return data;
1066 1067
    proc.start(m_settings.p4Command(),
        m_settings.basicP4Args() << QLatin1String("changes") << QLatin1String("-s") << QLatin1String("pending") << QLatin1String("-u") << user);
con's avatar
con committed
1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090
    if (proc.waitForFinished(3000))
        data = QString::fromUtf8(proc.readAllStandardOutput());
    return data;
}

void PerforcePlugin::showOutput(const QString &output, bool popup) const
{
    m_perforceOutputWindow->append(output, popup);
}

PerforcePlugin::~PerforcePlugin()
{
    if (m_settingsPage) {
        removeObject(m_settingsPage);
        delete m_settingsPage;
        m_settingsPage = 0;
    }

    if (m_perforceOutputWindow) {
        removeObject(m_perforceOutputWindow);
        delete  m_perforceOutputWindow;
        m_perforceOutputWindow = 0;
    }
1091

con's avatar
con committed
1092 1093 1094 1095 1096
    if (m_submitEditorFactory) {
        removeObject(m_submitEditorFactory);
        delete m_submitEditorFactory;
        m_submitEditorFactory = 0;
    }
1097

con's avatar
con committed
1098 1099 1100 1101 1102 1103 1104
    if (m_versionControl) {
        removeObject(m_versionControl);
        delete m_versionControl;
        m_versionControl = 0;
    }

    if (!m_editorFactories.empty()) {
hjk's avatar
hjk committed
1105
        foreach (Core::IEditorFactory *pf, m_editorFactories)
con's avatar
con committed
1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117
            removeObject(pf);
        qDeleteAll(m_editorFactories);
        m_editorFactories.clear();
    }

    if (m_coreListener) {
        removeObject(m_coreListener);
        delete m_coreListener;
        m_coreListener = 0;
    }
}

1118
const PerforceSettings& PerforcePlugin::settings() const
con's avatar
con committed
1119 1120 1121 1122
{
    return m_settings;
}

Friedemann Kleint's avatar
Friedemann Kleint committed
1123
void PerforcePlugin::setSettings(const Settings &newSettings)
con's avatar
con committed
1124
{
Friedemann Kleint's avatar
Friedemann Kleint committed
1125 1126
    if (newSettings != m_settings.settings()) {
        m_settings.setSettings(newSettings);
dt's avatar