1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
32 **************************************************************************/
34 #include "perforceplugin.h"
36 #include "changenumberdialog.h"
37 #include "pendingchangesdialog.h"
38 #include "perforceconstants.h"
39 #include "perforceeditor.h"
40 #include "perforcesubmiteditor.h"
41 #include "perforceversioncontrol.h"
42 #include "perforcechecker.h"
43 #include "settingspage.h"
45 #include <coreplugin/actionmanager/actionmanager.h>
46 #include <coreplugin/actionmanager/actioncontainer.h>
47 #include <coreplugin/actionmanager/command.h>
48 #include <coreplugin/uniqueidmanager.h>
49 #include <coreplugin/coreconstants.h>
50 #include <coreplugin/editormanager/editormanager.h>
51 #include <coreplugin/filemanager.h>
52 #include <coreplugin/icore.h>
53 #include <coreplugin/messagemanager.h>
54 #include <coreplugin/mimedatabase.h>
55 #include <locator/commandlocator.h>
56 #include <utils/qtcassert.h>
57 #include <utils/synchronousprocess.h>
58 #include <utils/parameteraction.h>
59 #include <vcsbase/basevcseditorfactory.h>
60 #include <vcsbase/basevcssubmiteditorfactory.h>
61 #include <vcsbase/vcsbaseeditor.h>
62 #include <vcsbase/vcsbaseoutputwindow.h>
64 #include <QtCore/QtPlugin>
65 #include <QtCore/QDebug>
66 #include <QtCore/QDir>
67 #include <QtCore/QFileInfo>
68 #include <QtCore/QSettings>
69 #include <QtCore/QTemporaryFile>
70 #include <QtCore/QTextCodec>
72 #include <QtGui/QAction>
73 #include <QtGui/QFileDialog>
74 #include <QtGui/QMainWindow>
75 #include <QtGui/QMenu>
76 #include <QtGui/QMessageBox>
78 static const VCSBase::VCSBaseEditorParameters editorParameters[] = {
80 VCSBase::RegularCommandOutput,
81 Perforce::Constants::PERFORCE_COMMANDLOG_EDITOR_ID,
82 Perforce::Constants::PERFORCE_COMMANDLOG_EDITOR_DISPLAY_NAME,
83 Perforce::Constants::PERFORCE_COMMANDLOG_EDITOR_CONTEXT,
84 "application/vnd.nokia.text.scs_commandlog",
87 Perforce::Constants::PERFORCE_LOG_EDITOR_ID,
88 Perforce::Constants::PERFORCE_LOG_EDITOR_DISPLAY_NAME,
89 Perforce::Constants::PERFORCE_LOG_EDITOR_CONTEXT,
90 "application/vnd.nokia.text.scs_filelog",
92 { VCSBase::AnnotateOutput,
93 Perforce::Constants::PERFORCE_ANNOTATION_EDITOR_ID,
94 Perforce::Constants::PERFORCE_ANNOTATION_EDITOR_DISPLAY_NAME,
95 Perforce::Constants::PERFORCE_ANNOTATION_EDITOR_CONTEXT,
96 "application/vnd.nokia.text.scs_annotation",
98 { VCSBase::DiffOutput,
99 Perforce::Constants::PERFORCE_DIFF_EDITOR_ID,
100 Perforce::Constants::PERFORCE_DIFF_EDITOR_DISPLAY_NAME,
101 Perforce::Constants::PERFORCE_DIFF_EDITOR_CONTEXT,
102 "text/x-patch","diff"}
105 // Utility to find a parameter set by type
106 static inline const VCSBase::VCSBaseEditorParameters *findType(int ie)
108 const VCSBase::EditorContentType et = static_cast<VCSBase::EditorContentType>(ie);
109 return VCSBase::VCSBaseEditorWidget::findType(editorParameters, sizeof(editorParameters)/sizeof(VCSBase::VCSBaseEditorParameters), et);
112 static inline QString debugCodec(const QTextCodec *c)
114 return c ? QString::fromAscii(c->name()) : QString::fromAscii("Null codec");
117 // Ensure adding "..." to relative paths which is p4's convention
118 // for the current directory
119 static inline QStringList perforceRelativeFileArguments(const QStringList &args)
122 return QStringList(QLatin1String("..."));
123 QTC_ASSERT(args.size() == 1, return QStringList())
124 QStringList p4Args = args;
125 p4Args.front() += QLatin1String("/...");
129 static inline QStringList perforceRelativeProjectDirectory(const VCSBase::VCSBasePluginState &s)
131 return perforceRelativeFileArguments(s.relativeCurrentProject());
134 // Clean user setting off diff-binary for 'p4 resolve' and 'p4 diff'.
135 static inline QProcessEnvironment overrideDiffEnvironmentVariable()
137 QProcessEnvironment rc = QProcessEnvironment::systemEnvironment();
138 rc.remove(QLatin1String("P4DIFF"));
142 static const char * const CMD_ID_PERFORCE_MENU = "Perforce.Menu";
143 static const char * const CMD_ID_EDIT = "Perforce.Edit";
144 static const char * const CMD_ID_ADD = "Perforce.Add";
145 static const char * const CMD_ID_DELETE_FILE = "Perforce.Delete";
146 static const char * const CMD_ID_OPENED = "Perforce.Opened";
147 static const char * const CMD_ID_PROJECTLOG = "Perforce.ProjectLog";
148 static const char * const CMD_ID_REPOSITORYLOG = "Perforce.RepositoryLog";
149 static const char * const CMD_ID_REVERT = "Perforce.Revert";
150 static const char * const CMD_ID_DIFF_CURRENT = "Perforce.DiffCurrent";
151 static const char * const CMD_ID_DIFF_PROJECT = "Perforce.DiffProject";
152 static const char * const CMD_ID_UPDATE_PROJECT = "Perforce.UpdateProject";
153 static const char * const CMD_ID_REVERT_PROJECT = "Perforce.RevertProject";
154 static const char * const CMD_ID_REVERT_UNCHANGED_PROJECT = "Perforce.RevertUnchangedProject";
155 static const char * const CMD_ID_DIFF_ALL = "Perforce.DiffAll";
156 static const char * const CMD_ID_RESOLVE = "Perforce.Resolve";
157 static const char * const CMD_ID_SUBMIT = "Perforce.Submit";
158 static const char * const CMD_ID_PENDING_CHANGES = "Perforce.PendingChanges";
159 static const char * const CMD_ID_DESCRIBE = "Perforce.Describe";
160 static const char * const CMD_ID_ANNOTATE_CURRENT = "Perforce.AnnotateCurrent";
161 static const char * const CMD_ID_ANNOTATE = "Perforce.Annotate";
162 static const char * const CMD_ID_FILELOG_CURRENT = "Perforce.FilelogCurrent";
163 static const char * const CMD_ID_FILELOG = "Perforce.Filelog";
164 static const char * const CMD_ID_UPDATEALL = "Perforce.UpdateAll";
165 static const char * const CMD_ID_SEPARATOR1 = "Perforce.Separator1";
166 static const char * const CMD_ID_SEPARATOR2 = "Perforce.Separator2";
167 static const char * const CMD_ID_SEPARATOR3 = "Perforce.Separator3";
176 PerforceResponse::PerforceResponse() :
182 PerforcePlugin *PerforcePlugin::m_perforcePluginInstance = NULL;
184 PerforcePlugin::PerforcePlugin() :
185 VCSBase::VCSBasePlugin(QLatin1String(Constants::PERFORCE_SUBMIT_EDITOR_ID)),
191 m_revertFileAction(0),
193 m_diffProjectAction(0),
194 m_updateProjectAction(0),
195 m_revertProjectAction(0),
196 m_revertUnchangedAction(0),
198 m_submitProjectAction(0),
201 m_annotateCurrentAction(0),
203 m_filelogCurrentAction(0),
205 m_logProjectAction(0),
206 m_logRepositoryAction(0),
207 m_submitCurrentLogAction(0),
208 m_updateAllAction(0),
209 m_submitActionTriggered(false),
210 m_diffSelectedFiles(0),
216 static const VCSBase::VCSBaseSubmitEditorParameters submitParameters = {
217 Perforce::Constants::SUBMIT_MIMETYPE,
218 Perforce::Constants::PERFORCE_SUBMIT_EDITOR_ID,
219 Perforce::Constants::PERFORCE_SUBMIT_EDITOR_DISPLAY_NAME,
220 Perforce::Constants::PERFORCESUBMITEDITOR_CONTEXT
223 static inline Core::Command *createSeparator(QObject *parent,
224 Core::ActionManager *ami,
226 const Core::Context &globalcontext)
228 QAction *tmpaction = new QAction(parent);
229 tmpaction->setSeparator(true);
230 return ami->registerAction(tmpaction, id, globalcontext);
233 bool PerforcePlugin::initialize(const QStringList & /* arguments */, QString *errorMessage)
235 typedef VCSBase::VCSEditorFactory<PerforceEditor> PerforceEditorFactory;
236 typedef VCSBase::VCSSubmitEditorFactory<PerforceSubmitEditor> PerforceSubmitEditorFactory;
238 VCSBase::VCSBasePlugin::initialize(new PerforceVersionControl(this));
240 Core::ICore *core = Core::ICore::instance();
241 if (!core->mimeDatabase()->addMimeTypes(QLatin1String(":/trolltech.perforce/Perforce.mimetypes.xml"), errorMessage))
243 m_perforcePluginInstance = this;
245 if (QSettings *settings = core->settings())
246 m_settings.fromSettings(settings);
248 addAutoReleasedObject(new SettingsPage);
251 addAutoReleasedObject(new PerforceSubmitEditorFactory(&submitParameters));
253 static const char *describeSlot = SLOT(describe(QString,QString));
254 const int editorCount = sizeof(editorParameters)/sizeof(VCSBase::VCSBaseEditorParameters);
255 for (int i = 0; i < editorCount; i++)
256 addAutoReleasedObject(new PerforceEditorFactory(editorParameters + i, this, describeSlot));
258 const QString prefix = QLatin1String("p4");
259 m_commandLocator = new Locator::CommandLocator(QLatin1String("Perforce"), prefix, prefix);
260 addAutoReleasedObject(m_commandLocator);
263 Core::ActionManager *am = Core::ICore::instance()->actionManager();
265 Core::ActionContainer *mtools =
266 am->actionContainer(Core::Constants::M_TOOLS);
268 Core::ActionContainer *mperforce =
269 am->createMenu(Core::Id(CMD_ID_PERFORCE_MENU));
270 mperforce->menu()->setTitle(tr("&Perforce"));
271 mtools->addMenu(mperforce);
272 m_menuAction = mperforce->menu()->menuAction();
274 Core::Context globalcontext(Core::Constants::C_GLOBAL);
275 Core::Context perforcesubmitcontext(Constants::PERFORCESUBMITEDITOR_CONTEXT);
277 Core::Command *command;
279 m_diffFileAction = new Utils::ParameterAction(tr("Diff Current File"), tr("Diff \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
280 command = am->registerAction(m_diffFileAction, CMD_ID_DIFF_CURRENT, globalcontext);
281 command->setAttribute(Core::Command::CA_UpdateText);
282 command->setDefaultText(tr("Diff Current File"));
283 connect(m_diffFileAction, SIGNAL(triggered()), this, SLOT(diffCurrentFile()));
284 mperforce->addAction(command);
285 m_commandLocator->appendCommand(command);
287 m_annotateCurrentAction = new Utils::ParameterAction(tr("Annotate Current File"), tr("Annotate \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
288 command = am->registerAction(m_annotateCurrentAction, CMD_ID_ANNOTATE_CURRENT, globalcontext);
289 command->setAttribute(Core::Command::CA_UpdateText);
290 command->setDefaultText(tr("Annotate Current File"));
291 connect(m_annotateCurrentAction, SIGNAL(triggered()), this, SLOT(annotateCurrentFile()));
292 mperforce->addAction(command);
293 m_commandLocator->appendCommand(command);
295 m_filelogCurrentAction = new Utils::ParameterAction(tr("Filelog Current File"), tr("Filelog \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
296 command = am->registerAction(m_filelogCurrentAction, CMD_ID_FILELOG_CURRENT, globalcontext);
297 command->setAttribute(Core::Command::CA_UpdateText);
298 command->setDefaultKeySequence(QKeySequence(tr("Alt+P,Alt+F")));
299 command->setDefaultText(tr("Filelog Current File"));
300 connect(m_filelogCurrentAction, SIGNAL(triggered()), this, SLOT(filelogCurrentFile()));
301 mperforce->addAction(command);
302 m_commandLocator->appendCommand(command);
304 mperforce->addAction(createSeparator(this, am, "Perforce.Sep.Edit", globalcontext));
306 m_editAction = new Utils::ParameterAction(tr("Edit"), tr("Edit \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
307 command = am->registerAction(m_editAction, CMD_ID_EDIT, globalcontext);
308 command->setAttribute(Core::Command::CA_UpdateText);
309 command->setDefaultKeySequence(QKeySequence(tr("Alt+P,Alt+E")));
310 command->setDefaultText(tr("Edit File"));
311 connect(m_editAction, SIGNAL(triggered()), this, SLOT(openCurrentFile()));
312 mperforce->addAction(command);
313 m_commandLocator->appendCommand(command);
315 m_addAction = new Utils::ParameterAction(tr("Add"), tr("Add \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
316 command = am->registerAction(m_addAction, CMD_ID_ADD, globalcontext);
317 command->setAttribute(Core::Command::CA_UpdateText);
318 command->setDefaultKeySequence(QKeySequence(tr("Alt+P,Alt+A")));
319 command->setDefaultText(tr("Add File"));
320 connect(m_addAction, SIGNAL(triggered()), this, SLOT(addCurrentFile()));
321 mperforce->addAction(command);
322 m_commandLocator->appendCommand(command);
324 m_deleteAction = new Utils::ParameterAction(tr("Delete..."), tr("Delete \"%1\"..."), Utils::ParameterAction::EnabledWithParameter, this);
325 command = am->registerAction(m_deleteAction, CMD_ID_DELETE_FILE, globalcontext);
326 command->setAttribute(Core::Command::CA_UpdateText);
327 command->setDefaultText(tr("Delete File"));
328 connect(m_deleteAction, SIGNAL(triggered()), this, SLOT(promptToDeleteCurrentFile()));
329 mperforce->addAction(command);
330 m_commandLocator->appendCommand(command);
332 m_revertFileAction = new Utils::ParameterAction(tr("Revert"), tr("Revert \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
333 command = am->registerAction(m_revertFileAction, CMD_ID_REVERT, globalcontext);
334 command->setAttribute(Core::Command::CA_UpdateText);
335 command->setDefaultKeySequence(QKeySequence(tr("Alt+P,Alt+R")));
336 command->setDefaultText(tr("Revert File"));
337 connect(m_revertFileAction, SIGNAL(triggered()), this, SLOT(revertCurrentFile()));
338 mperforce->addAction(command);
339 m_commandLocator->appendCommand(command);
341 mperforce->addAction(createSeparator(this, am, "Perforce.Sep.Project", globalcontext));
343 const QString diffProjectDefaultText = tr("Diff Current Project/Session");
344 m_diffProjectAction = new Utils::ParameterAction(diffProjectDefaultText, tr("Diff Project \"%1\""), Utils::ParameterAction::AlwaysEnabled, this);
345 command = am->registerAction(m_diffProjectAction, CMD_ID_DIFF_PROJECT, globalcontext);
346 command->setAttribute(Core::Command::CA_UpdateText);
347 command->setDefaultKeySequence(QKeySequence(tr("Alt+P,Alt+D")));
348 command->setDefaultText(diffProjectDefaultText);
349 connect(m_diffProjectAction, SIGNAL(triggered()), this, SLOT(diffCurrentProject()));
350 mperforce->addAction(command);
351 m_commandLocator->appendCommand(command);
353 m_logProjectAction = new Utils::ParameterAction(tr("Log Project"), tr("Log Project \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
354 command = am->registerAction(m_logProjectAction, CMD_ID_PROJECTLOG, globalcontext);
355 command->setAttribute(Core::Command::CA_UpdateText);
356 connect(m_logProjectAction, SIGNAL(triggered()), this, SLOT(logProject()));
357 mperforce->addAction(command);
358 m_commandLocator->appendCommand(command);
360 m_submitProjectAction = new Utils::ParameterAction(tr("Submit Project"), tr("Submit Project \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
361 command = am->registerAction(m_submitProjectAction, CMD_ID_SUBMIT, globalcontext);
362 command->setAttribute(Core::Command::CA_UpdateText);
363 command->setDefaultKeySequence(QKeySequence(tr("Alt+P,Alt+S")));
364 connect(m_submitProjectAction, SIGNAL(triggered()), this, SLOT(startSubmitProject()));
365 mperforce->addAction(command);
366 m_commandLocator->appendCommand(command);
368 const QString updateProjectDefaultText = tr("Update Current Project");
369 m_updateProjectAction = new Utils::ParameterAction(updateProjectDefaultText, tr("Update Project \"%1\""), Utils::ParameterAction::AlwaysEnabled, this);
370 command = am->registerAction(m_updateProjectAction, CMD_ID_UPDATE_PROJECT, globalcontext);
371 command->setDefaultText(updateProjectDefaultText);
372 command->setAttribute(Core::Command::CA_UpdateText);
373 connect(m_updateProjectAction, SIGNAL(triggered()), this, SLOT(updateCurrentProject()));
374 mperforce->addAction(command);
375 m_commandLocator->appendCommand(command);
377 m_revertUnchangedAction = new Utils::ParameterAction(tr("Revert Unchanged"), tr("Revert Unchanged Files of Project \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
378 command = am->registerAction(m_revertUnchangedAction, CMD_ID_REVERT_UNCHANGED_PROJECT, globalcontext);
379 command->setAttribute(Core::Command::CA_UpdateText);
380 connect(m_revertUnchangedAction, SIGNAL(triggered()), this, SLOT(revertUnchangedCurrentProject()));
381 mperforce->addAction(command);
382 m_commandLocator->appendCommand(command);
384 m_revertProjectAction = new Utils::ParameterAction(tr("Revert Project"), tr("Revert Project \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
385 command = am->registerAction(m_revertProjectAction, CMD_ID_REVERT_PROJECT, globalcontext);
386 command->setAttribute(Core::Command::CA_UpdateText);
387 connect(m_revertProjectAction, SIGNAL(triggered()), this, SLOT(revertCurrentProject()));
388 mperforce->addAction(command);
389 m_commandLocator->appendCommand(command);
391 mperforce->addAction(createSeparator(this, am, "Perforce.Sep.Repository", globalcontext));
393 m_diffAllAction = new QAction(tr("Diff Opened Files"), this);
394 command = am->registerAction(m_diffAllAction, CMD_ID_DIFF_ALL, globalcontext);
395 connect(m_diffAllAction, SIGNAL(triggered()), this, SLOT(diffAllOpened()));
396 mperforce->addAction(command);
397 m_commandLocator->appendCommand(command);
399 m_openedAction = new QAction(tr("Opened"), this);
400 command = am->registerAction(m_openedAction, CMD_ID_OPENED, globalcontext);
401 command->setDefaultKeySequence(QKeySequence(tr("Alt+P,Alt+O")));
402 connect(m_openedAction, SIGNAL(triggered()), this, SLOT(printOpenedFileList()));
403 mperforce->addAction(command);
404 m_commandLocator->appendCommand(command);
406 m_logRepositoryAction = new QAction(tr("Repository Log"), this);
407 command = am->registerAction(m_logRepositoryAction, CMD_ID_REPOSITORYLOG, globalcontext);
408 connect(m_logRepositoryAction, SIGNAL(triggered()), this, SLOT(logRepository()));
409 mperforce->addAction(command);
410 m_commandLocator->appendCommand(command);
412 m_pendingAction = new QAction(tr("Pending Changes..."), this);
413 command = am->registerAction(m_pendingAction, CMD_ID_PENDING_CHANGES, globalcontext);
414 connect(m_pendingAction, SIGNAL(triggered()), this, SLOT(printPendingChanges()));
415 mperforce->addAction(command);
416 m_commandLocator->appendCommand(command);
418 m_updateAllAction = new QAction(tr("Update All"), this);
419 command = am->registerAction(m_updateAllAction, CMD_ID_UPDATEALL, globalcontext);
420 connect(m_updateAllAction, SIGNAL(triggered()), this, SLOT(updateAll()));
421 mperforce->addAction(command);
422 m_commandLocator->appendCommand(command);
424 mperforce->addAction(createSeparator(this, am, "Perforce.Sep.Dialogs", globalcontext));
426 m_describeAction = new QAction(tr("Describe..."), this);
427 command = am->registerAction(m_describeAction, CMD_ID_DESCRIBE, globalcontext);
428 connect(m_describeAction, SIGNAL(triggered()), this, SLOT(describeChange()));
429 mperforce->addAction(command);
431 m_annotateAction = new QAction(tr("Annotate..."), this);
432 command = am->registerAction(m_annotateAction, CMD_ID_ANNOTATE, globalcontext);
433 connect(m_annotateAction, SIGNAL(triggered()), this, SLOT(annotate()));
434 mperforce->addAction(command);
436 m_filelogAction = new QAction(tr("Filelog..."), this);
437 command = am->registerAction(m_filelogAction, CMD_ID_FILELOG, globalcontext);
438 connect(m_filelogAction, SIGNAL(triggered()), this, SLOT(filelog()));
439 mperforce->addAction(command);
441 m_submitCurrentLogAction = new QAction(VCSBase::VCSBaseSubmitEditor::submitIcon(), tr("Submit"), this);
442 command = am->registerAction(m_submitCurrentLogAction, Constants::SUBMIT_CURRENT, perforcesubmitcontext);
443 command->setAttribute(Core::Command::CA_UpdateText);
444 connect(m_submitCurrentLogAction, SIGNAL(triggered()), this, SLOT(submitCurrentLog()));
446 m_diffSelectedFiles = new QAction(VCSBase::VCSBaseSubmitEditor::diffIcon(), tr("Diff Selected Files"), this);
447 command = am->registerAction(m_diffSelectedFiles, Constants::DIFF_SELECTED, perforcesubmitcontext);
449 m_undoAction = new QAction(tr("&Undo"), this);
450 command = am->registerAction(m_undoAction, Core::Constants::UNDO, perforcesubmitcontext);
452 m_redoAction = new QAction(tr("&Redo"), this);
453 command = am->registerAction(m_redoAction, Core::Constants::REDO, perforcesubmitcontext);
458 void PerforcePlugin::extensionsInitialized()
460 VCSBase::VCSBasePlugin::extensionsInitialized();
464 void PerforcePlugin::openCurrentFile()
466 const VCSBase::VCSBasePluginState state = currentState();
467 QTC_ASSERT(state.hasFile(), return)
468 vcsOpen(state.currentFileTopLevel(), state.relativeCurrentFile());
471 void PerforcePlugin::addCurrentFile()
473 const VCSBase::VCSBasePluginState state = currentState();
474 QTC_ASSERT(state.hasFile(), return)
475 vcsAdd(state.currentFileTopLevel(), state.relativeCurrentFile());
478 void PerforcePlugin::revertCurrentFile()
480 const VCSBase::VCSBasePluginState state = currentState();
481 QTC_ASSERT(state.hasFile(), return)
483 QTextCodec *codec = VCSBase::VCSBaseEditorWidget::getCodec(state.currentFile());
485 args << QLatin1String("diff") << QLatin1String("-sa") << state.relativeCurrentFile();
486 PerforceResponse result = runP4Cmd(state.currentFileTopLevel(), args,
487 RunFullySynchronous|CommandToWindow|StdErrToWindow|ErrorToWindow,
488 QStringList(), QByteArray(), codec);
491 // "foo.cpp - file(s) not opened on this client."
492 if (result.stdOut.isEmpty() || result.stdOut.contains(QLatin1String(" - ")))
495 const bool doNotRevert = QMessageBox::warning(0, tr("p4 revert"),
496 tr("The file has been changed. Do you want to revert it?"),
497 QMessageBox::Yes, QMessageBox::No) == QMessageBox::No;
501 Core::FileChangeBlocker fcb(state.currentFile());
503 args << QLatin1String("revert") << state.relativeCurrentFile();
504 PerforceResponse result2 = runP4Cmd(state.currentFileTopLevel(), args,
505 CommandToWindow|StdOutToWindow|StdErrToWindow|ErrorToWindow);
507 perforceVersionControl()->emitFilesChanged(QStringList(state.currentFile()));
510 void PerforcePlugin::diffCurrentFile()
512 const VCSBase::VCSBasePluginState state = currentState();
513 QTC_ASSERT(state.hasFile(), return)
514 p4Diff(state.currentFileTopLevel(), QStringList(state.relativeCurrentFile()));
517 void PerforcePlugin::diffCurrentProject()
519 const VCSBase::VCSBasePluginState state = currentState();
520 QTC_ASSERT(state.hasProject(), return)
521 p4Diff(state.currentProjectTopLevel(), perforceRelativeProjectDirectory(state));
524 void PerforcePlugin::diffAllOpened()
526 p4Diff(m_settings.topLevel(), QStringList());
529 void PerforcePlugin::updateCurrentProject()
531 const VCSBase::VCSBasePluginState state = currentState();
532 QTC_ASSERT(state.hasProject(), return)
533 updateCheckout(state.currentProjectTopLevel(), perforceRelativeProjectDirectory(state));
536 void PerforcePlugin::updateAll()
538 updateCheckout(m_settings.topLevel());
541 void PerforcePlugin::revertCurrentProject()
543 const VCSBase::VCSBasePluginState state = currentState();
544 QTC_ASSERT(state.hasProject(), return)
546 const QString msg = tr("Do you want to revert all changes to the project \"%1\"?").arg(state.currentProjectName());
547 if (QMessageBox::warning(0, tr("p4 revert"), msg, QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
549 revertProject(state.currentProjectTopLevel(), perforceRelativeProjectDirectory(state), false);
552 void PerforcePlugin::revertUnchangedCurrentProject()
555 const VCSBase::VCSBasePluginState state = currentState();
556 QTC_ASSERT(state.hasProject(), return)
557 revertProject(state.currentProjectTopLevel(), perforceRelativeProjectDirectory(state), true);
560 bool PerforcePlugin::revertProject(const QString &workingDir, const QStringList &pathArgs, bool unchangedOnly)
562 QStringList args(QLatin1String("revert"));
564 args.push_back(QLatin1String("-a"));
565 args.append(pathArgs);
566 const PerforceResponse resp = runP4Cmd(workingDir, args,
567 RunFullySynchronous|CommandToWindow|StdOutToWindow|StdErrToWindow|ErrorToWindow);
571 void PerforcePlugin::updateCheckout(const QString &workingDir, const QStringList &dirs)
573 QStringList args(QLatin1String("sync"));
575 const PerforceResponse resp = runP4Cmd(workingDir, args,
576 CommandToWindow|StdOutToWindow|StdErrToWindow|ErrorToWindow);
578 if (!workingDir.isEmpty())
579 perforceVersionControl()->emitRepositoryChanged(workingDir);
581 const QChar slash = QLatin1Char('/');
582 foreach(const QString &dir, dirs)
583 perforceVersionControl()->emitRepositoryChanged(workingDir + slash + dir);
587 void PerforcePlugin::printOpenedFileList()
589 const PerforceResponse perforceResponse
590 = runP4Cmd(m_settings.topLevel(), QStringList(QLatin1String("opened")),
591 CommandToWindow|StdErrToWindow|ErrorToWindow);
592 if (perforceResponse.error || perforceResponse.stdOut.isEmpty())
594 // reformat "//depot/file.cpp#1 - description" into "file.cpp # - description"
595 // for context menu opening to work. This produces absolute paths, then.
596 VCSBase::VCSBaseOutputWindow *outWin = VCSBase::VCSBaseOutputWindow::instance();
597 QString errorMessage;
599 const QChar delimiter = QLatin1Char('#');
600 foreach (const QString &line, perforceResponse.stdOut.split(QLatin1Char('\n'))) {
602 const int delimiterPos = line.indexOf(delimiter);
603 if (delimiterPos > 0)
604 mapped = fileNameFromPerforceName(line.left(delimiterPos), true, &errorMessage);
605 if (mapped.isEmpty()) {
606 outWin->appendSilently(line);
608 outWin->appendSilently(mapped + QLatin1Char(' ') + line.mid(delimiterPos));
614 void PerforcePlugin::startSubmitProject()
617 if (VCSBase::VCSBaseSubmitEditor::raiseSubmitEditor())
620 if (isCommitEditorOpen()) {
621 VCSBase::VCSBaseOutputWindow::instance()->appendWarning(tr("Another submit is currently executed."));
625 const VCSBase::VCSBasePluginState state = currentState();
626 QTC_ASSERT(state.hasProject(), return)
628 QTemporaryFile changeTmpFile;
629 changeTmpFile.setAutoRemove(false);
630 if (!changeTmpFile.open()) {
631 VCSBase::VCSBaseOutputWindow::instance()->appendError(tr("Cannot create temporary file."));
632 cleanCommitMessageFile();
635 // Revert all unchanged files.
636 if (!revertProject(state.currentProjectTopLevel(), perforceRelativeProjectDirectory(state), true))
641 args << QLatin1String("change") << QLatin1String("-o");
642 PerforceResponse result = runP4Cmd(state.currentProjectTopLevel(), args,
643 RunFullySynchronous|CommandToWindow|StdErrToWindow|ErrorToWindow);
645 cleanCommitMessageFile();
649 m_commitMessageFileName = changeTmpFile.fileName();
650 changeTmpFile.write(result.stdOut.toAscii());
651 changeTmpFile.close();
654 args << QLatin1String("fstat");
655 args.append(perforceRelativeProjectDirectory(state));
656 PerforceResponse fstatResult = runP4Cmd(state.currentProjectTopLevel(), args,
657 RunFullySynchronous|CommandToWindow|StdErrToWindow|ErrorToWindow);
658 if (fstatResult.error) {
659 cleanCommitMessageFile();
663 QStringList fstatLines = fstatResult.stdOut.split(QLatin1Char('\n'));
664 QStringList depotFileNames;
665 foreach (const QString &line, fstatLines) {
666 if (line.startsWith(QLatin1String("... depotFile")))
667 depotFileNames.append(line.mid(14));
669 if (depotFileNames.isEmpty()) {
670 VCSBase::VCSBaseOutputWindow::instance()->appendWarning(tr("Project has no files"));
671 cleanCommitMessageFile();
675 openPerforceSubmitEditor(m_commitMessageFileName, depotFileNames);
678 Core::IEditor *PerforcePlugin::openPerforceSubmitEditor(const QString &fileName, const QStringList &depotFileNames)
680 Core::EditorManager *editorManager = Core::EditorManager::instance();
681 Core::IEditor *editor = editorManager->openEditor(fileName, Constants::PERFORCE_SUBMIT_EDITOR_ID,
682 Core::EditorManager::ModeSwitch);
683 PerforceSubmitEditor *submitEditor = static_cast<PerforceSubmitEditor*>(editor);
684 submitEditor->restrictToProjectFiles(depotFileNames);
685 submitEditor->registerActions(m_undoAction, m_redoAction, m_submitCurrentLogAction, m_diffSelectedFiles);
686 connect(submitEditor, SIGNAL(diffSelectedFiles(QStringList)), this, SLOT(slotSubmitDiff(QStringList)));
687 submitEditor->setCheckScriptWorkingDirectory(m_commitWorkingDirectory);
691 void PerforcePlugin::printPendingChanges()
693 qApp->setOverrideCursor(Qt::WaitCursor);
694 PendingChangesDialog dia(pendingChangesData(), Core::ICore::instance()->mainWindow());
695 qApp->restoreOverrideCursor();
696 if (dia.exec() == QDialog::Accepted) {
697 const int i = dia.changeNumber();
698 QStringList args(QLatin1String("submit"));
699 args << QLatin1String("-c") << QString::number(i);
700 runP4Cmd(m_settings.topLevel(), args,
701 CommandToWindow|StdOutToWindow|StdErrToWindow|ErrorToWindow);
705 void PerforcePlugin::describeChange()
707 ChangeNumberDialog dia;
708 if (dia.exec() == QDialog::Accepted && dia.number() > 0)
709 describe(QString(), QString::number(dia.number()));
712 void PerforcePlugin::annotateCurrentFile()
714 const VCSBase::VCSBasePluginState state = currentState();
715 QTC_ASSERT(state.hasFile(), return)
716 annotate(state.currentFileTopLevel(), state.relativeCurrentFile());
719 void PerforcePlugin::annotate()
721 const QString file = QFileDialog::getOpenFileName(0, tr("p4 annotate"));
722 if (!file.isEmpty()) {
723 const QFileInfo fi(file);
724 annotate(fi.absolutePath(), fi.fileName());
728 void PerforcePlugin::vcsAnnotate(const QString &file, const QString &revision, int lineNumber)
730 const QFileInfo fi(file);
731 annotate(fi.absolutePath(), fi.fileName(), revision, lineNumber);
734 void PerforcePlugin::annotate(const QString &workingDir,
735 const QString &fileName,
736 const QString &changeList /* = QString() */,
737 int lineNumber /* = -1 */)
739 const QStringList files = QStringList(fileName);
740 QTextCodec *codec = VCSBase::VCSBaseEditorWidget::getCodec(workingDir, files);
741 const QString id = VCSBase::VCSBaseEditorWidget::getTitleId(workingDir, files, changeList);
742 const QString source = VCSBase::VCSBaseEditorWidget::getSource(workingDir, files);
744 args << QLatin1String("annotate") << QLatin1String("-cqi");
745 if (changeList.isEmpty()) {
748 args << (fileName + QLatin1Char('@') + changeList);
750 const PerforceResponse result = runP4Cmd(workingDir, args,
751 CommandToWindow|StdErrToWindow|ErrorToWindow,
752 QStringList(), QByteArray(), codec);
755 lineNumber = VCSBase::VCSBaseEditorWidget::lineNumberOfCurrentEditor();
756 const QFileInfo fi(fileName);
757 Core::IEditor *ed = showOutputInEditor(tr("p4 annotate %1").arg(id),
758 result.stdOut, VCSBase::AnnotateOutput,
760 VCSBase::VCSBaseEditorWidget::gotoLineOfEditor(ed, lineNumber);
764 void PerforcePlugin::filelogCurrentFile()
766 const VCSBase::VCSBasePluginState state = currentState();
767 QTC_ASSERT(state.hasFile(), return)
768 filelog(state.currentFileTopLevel(), QStringList(state.relativeCurrentFile()), true);
771 void PerforcePlugin::filelog()
773 const QString file = QFileDialog::getOpenFileName(0, tr("p4 filelog"));
774 if (!file.isEmpty()) {
775 const QFileInfo fi(file);
776 filelog(fi.absolutePath(), QStringList(fi.fileName()));
780 void PerforcePlugin::logProject()
782 const VCSBase::VCSBasePluginState state = currentState();
783 QTC_ASSERT(state.hasProject(), return)
784 filelog(state.currentProjectTopLevel(), perforceRelativeFileArguments(state.relativeCurrentProject()));
787 void PerforcePlugin::logRepository()
789 const VCSBase::VCSBasePluginState state = currentState();
790 QTC_ASSERT(state.hasTopLevel(), return)
791 filelog(state.topLevel(), perforceRelativeFileArguments(QStringList()));
794 void PerforcePlugin::filelog(const QString &workingDir, const QStringList &fileNames,
795 bool enableAnnotationContextMenu)
797 const QString id = VCSBase::VCSBaseEditorWidget::getTitleId(workingDir, fileNames);
798 QTextCodec *codec = VCSBase::VCSBaseEditorWidget::getCodec(workingDir, fileNames);
800 args << QLatin1String("filelog") << QLatin1String("-li");
801 if (m_settings.logCount() > 0)
802 args << QLatin1String("-m") << QString::number(m_settings.logCount());
803 args.append(fileNames);
804 const PerforceResponse result = runP4Cmd(workingDir, args,
805 CommandToWindow|StdErrToWindow|ErrorToWindow,
806 QStringList(), QByteArray(), codec);
808 const QString source = VCSBase::VCSBaseEditorWidget::getSource(workingDir, fileNames);
809 Core::IEditor *editor = showOutputInEditor(tr("p4 filelog %1").arg(id), result.stdOut,
810 VCSBase::LogOutput, source, codec);
811 if (enableAnnotationContextMenu)
812 VCSBase::VCSBaseEditorWidget::getVcsBaseEditor(editor)->setFileLogAnnotateEnabled(true);
816 void PerforcePlugin::updateActions(VCSBase::VCSBasePlugin::ActionState as)
818 if (!enableMenuAction(as, m_menuAction)) {
819 m_commandLocator->setEnabled(false);
822 const bool hasTopLevel = currentState().hasTopLevel();
823 m_commandLocator->setEnabled(hasTopLevel);
824 m_logRepositoryAction->setEnabled(hasTopLevel);
826 const QString fileName = currentState().currentFileName();
827 m_editAction->setParameter(fileName);
828 m_addAction->setParameter(fileName);
829 m_deleteAction->setParameter(fileName);
830 m_revertFileAction->setParameter(fileName);
831 m_diffFileAction->setParameter(fileName);
832 m_annotateCurrentAction->setParameter(fileName);
833 m_filelogCurrentAction->setParameter(fileName);
835 const QString projectName = currentState().currentProjectName();
836 m_logProjectAction->setParameter(projectName);
837 m_updateProjectAction->setParameter(projectName);
838 m_diffProjectAction->setParameter(projectName);
839 m_submitProjectAction->setParameter(projectName);
840 m_revertProjectAction->setParameter(projectName);
841 m_revertUnchangedAction->setParameter(projectName);
843 m_diffAllAction->setEnabled(true);
844 m_openedAction->setEnabled(true);
845 m_describeAction->setEnabled(true);
846 m_annotateAction->setEnabled(true);
847 m_filelogAction->setEnabled(true);
848 m_pendingAction->setEnabled(true);
849 m_updateAllAction->setEnabled(true);
852 bool PerforcePlugin::managesDirectory(const QString &directory, QString *topLevel /* = 0 */)
854 const bool rc = managesDirectoryFstat(directory);
857 *topLevel = m_settings.topLevelSymLinkTarget();
865 bool PerforcePlugin::managesDirectoryFstat(const QString &directory)
867 if (!m_settings.isValid())
870 const ManagedDirectoryCache::const_iterator cit = m_managedDirectoryCache.constFind(directory);
871 if (cit != m_managedDirectoryCache.constEnd())
873 // Determine value and insert into cache
874 bool managed = false;
876 // Quick check: Must be at or below top level and not "../../other_path"
877 const QStringList relativeDirArgs = m_settings.relativeToTopLevelArguments(directory);
878 if (!relativeDirArgs.empty() && relativeDirArgs.front().startsWith(QLatin1String("..")))
880 // Is it actually managed by perforce?
882 args << QLatin1String("fstat") << QLatin1String("-m1") << perforceRelativeFileArguments(relativeDirArgs);
883 const PerforceResponse result = runP4Cmd(m_settings.topLevel(), args,
884 RunFullySynchronous);
885 managed = result.stdOut.contains("depotFile") || result.stdErr.contains("... - no such file(s)");
888 m_managedDirectoryCache.insert(directory, managed);
892 bool PerforcePlugin::vcsOpen(const QString &workingDir, const QString &fileName)
894 if (Perforce::Constants::debug)
895 qDebug() << "PerforcePlugin::vcsOpen" << workingDir << fileName;
897 args << QLatin1String("edit") << QDir::toNativeSeparators(fileName);
898 const PerforceResponse result = runP4Cmd(workingDir, args,
899 CommandToWindow|StdOutToWindow|StdErrToWindow|ErrorToWindow);
900 return !result.error;
903 bool PerforcePlugin::vcsAdd(const QString &workingDir, const QString &fileName)
906 args << QLatin1String("add") << fileName;
907 const PerforceResponse result = runP4Cmd(workingDir, args,
908 CommandToWindow|StdOutToWindow|StdErrToWindow|ErrorToWindow);
909 return !result.error;
912 bool PerforcePlugin::vcsDelete(const QString &workingDir, const QString &fileName)
916 args << QLatin1String("revert") << fileName;
917 const PerforceResponse revertResult = runP4Cmd(workingDir, args,
918 CommandToWindow|StdOutToWindow|StdErrToWindow|ErrorToWindow);
919 if (revertResult.error)
922 args << QLatin1String("delete") << fileName;
923 const PerforceResponse deleteResult = runP4Cmd(workingDir, args,
924 CommandToWindow|StdOutToWindow|StdErrToWindow|ErrorToWindow);
925 // TODO need to carefully parse the actual messages from perforce
926 // or do a fstat before to decide what to do
928 // Different states are:
929 // File is in depot and unopened => p4 delete %
930 // File is in depot and opened => p4 revert %, p4 delete %
931 // File is not in depot => p4 revert %
932 return !deleteResult.error;
935 bool PerforcePlugin::vcsMove(const QString &workingDir, const QString &from, const QString &to)
937 // TODO verify this works
939 args << QLatin1String("edit") << from;
940 const PerforceResponse editResult = runP4Cmd(workingDir, args,
941 RunFullySynchronous|CommandToWindow|StdOutToWindow|StdErrToWindow|ErrorToWindow);
942 if (editResult.error)
945 args << QLatin1String("move") << from << to;
946 const PerforceResponse moveResult = runP4Cmd(workingDir, args,
947 RunFullySynchronous|CommandToWindow|StdOutToWindow|StdErrToWindow|ErrorToWindow);
948 return !moveResult.error;
951 // Write extra args to temporary file
952 QSharedPointer<QTemporaryFile>
953 PerforcePlugin::createTemporaryArgumentFile(const QStringList &extraArgs) const
955 if (extraArgs.isEmpty())
956 return QSharedPointer<QTemporaryFile>();
958 if (m_tempFilePattern.isEmpty()) {
959 m_tempFilePattern = QDir::tempPath();
960 if (!m_tempFilePattern.endsWith(QDir::separator()))
961 m_tempFilePattern += QDir::separator();
962 m_tempFilePattern += QLatin1String("qtc_p4_XXXXXX.args");
964 QSharedPointer<QTemporaryFile> rc(new QTemporaryFile(m_tempFilePattern));
965 rc->setAutoRemove(true);
967 qWarning("Could not create temporary file: %s. Appending all file names to command line.",
968 qPrintable(rc->errorString()));
969 return QSharedPointer<QTemporaryFile>();
971 const int last = extraArgs.size() - 1;
972 for (int i = 0; i <= last; i++) {
973 rc->write(extraArgs.at(i).toLocal8Bit());
983 static inline QString msgNotStarted(const QString &cmd)
985 return PerforcePlugin::tr("Could not start perforce '%1'. Please check your settings in the preferences.").arg(cmd);
988 static inline QString msgTimeout(int timeOut)
990 return PerforcePlugin::tr("Perforce did not respond within timeout limit (%1 ms).").arg(timeOut );
993 static inline QString msgCrash()
995 return PerforcePlugin::tr("The process terminated abnormally.");
998 static inline QString msgExitCode(int ex)
1000 return PerforcePlugin::tr("The process terminated with exit code %1.").arg(ex);
1003 // Run using a SynchronousProcess, emitting signals to the message window
1004 PerforceResponse PerforcePlugin::synchronousProcess(const QString &workingDir,
1005 const QStringList &args,
1007 const QByteArray &stdInput,
1008 QTextCodec *outputCodec) const
1010 QTC_ASSERT(stdInput.isEmpty(), return PerforceResponse()) // Not supported here
1012 VCSBase::VCSBaseOutputWindow *outputWindow = VCSBase::VCSBaseOutputWindow::instance();
1013 // Run, connect stderr to the output window
1014 Utils::SynchronousProcess process;
1015 const int timeOut = (flags & LongTimeOut) ? m_settings.longTimeOutMS() : m_settings.timeOutMS();
1016 process.setTimeout(timeOut);
1017 process.setStdOutCodec(outputCodec);
1018 if (flags & OverrideDiffEnvironment)
1019 process.setProcessEnvironment(overrideDiffEnvironmentVariable());
1020 if (!workingDir.isEmpty())
1021 process.setWorkingDirectory(workingDir);
1023 // connect stderr to the output window if desired
1024 if (flags & StdErrToWindow) {
1025 process.setStdErrBufferedSignalsEnabled(true);
1026 connect(&process, SIGNAL(stdErrBuffered(QString,bool)), outputWindow, SLOT(append(QString)));
1029 // connect stdout to the output window if desired
1030 if (flags & StdOutToWindow) {
1031 process.setStdOutBufferedSignalsEnabled(true);
1032 connect(&process, SIGNAL(stdOutBuffered(QString,bool)), outputWindow, SLOT(append(QString)));
1034 if (Perforce::Constants::debug)
1035 qDebug() << "PerforcePlugin::run syncp actual args [" << process.workingDirectory() << ']' << args;
1036 process.setTimeOutMessageBoxEnabled(true);
1037 const Utils::SynchronousProcessResponse sp_resp = process.run(m_settings.p4Command(), args);
1038 if (Perforce::Constants::debug)
1039 qDebug() << sp_resp;
1041 PerforceResponse response;
1042 response.error = true;
1043 response.exitCode = sp_resp.exitCode;
1044 response.stdErr = sp_resp.stdErr;
1045 response.stdOut = sp_resp.stdOut;
1046 switch (sp_resp.result) {
1047 case Utils::SynchronousProcessResponse::Finished:
1048 response.error = false;
1050 case Utils::SynchronousProcessResponse::FinishedError:
1051 response.message = msgExitCode(sp_resp.exitCode);
1052 response.error = !(flags & IgnoreExitCode);
1054 case Utils::SynchronousProcessResponse::TerminatedAbnormally:
1055 response.message = msgCrash();
1057 case Utils::SynchronousProcessResponse::StartFailed:
1058 response.message = msgNotStarted(m_settings.p4Command());
1060 case Utils::SynchronousProcessResponse::Hang:
1061 response.message = msgCrash();
1067 // Run using a QProcess, for short queries
1068 PerforceResponse PerforcePlugin::fullySynchronousProcess(const QString &workingDir,
1069 const QStringList &args,
1071 const QByteArray &stdInput,
1072 QTextCodec *outputCodec) const
1076 if (flags & OverrideDiffEnvironment)
1077 process.setProcessEnvironment(overrideDiffEnvironmentVariable());
1078 if (!workingDir.isEmpty())
1079 process.setWorkingDirectory(workingDir);
1081 if (Perforce::Constants::debug)
1082 qDebug() << "PerforcePlugin::run fully syncp actual args [" << process.workingDirectory() << ']' << args;
1084 PerforceResponse response;
1085 process.start(m_settings.p4Command(), args);
1086 if (stdInput.isEmpty())
1087 process.closeWriteChannel();
1089 if (!process.waitForStarted(3000)) {
1090 response.error = true;
1091 response.message = msgNotStarted(m_settings.p4Command());
1094 if (!stdInput.isEmpty()) {
1095 if (process.write(stdInput) == -1) {
1096 Utils::SynchronousProcess::stopProcess(process);
1097 response.error = true;
1098 response.message = tr("Unable to write input data to process %1: %2").
1099 arg(QDir::toNativeSeparators(m_settings.p4Command()),
1100 process.errorString());
1103 process.closeWriteChannel();
1108 const int timeOut = (flags & LongTimeOut) ? m_settings.longTimeOutMS() : m_settings.timeOutMS();
1109 if (!Utils::SynchronousProcess::readDataFromProcess(process, timeOut, &stdOut, &stdErr, true)) {
1110 Utils::SynchronousProcess::stopProcess(process);
1111 response.error = true;
1112 response.message = msgTimeout(timeOut);
1115 if (process.exitStatus() != QProcess::NormalExit) {
1116 response.error = true;
1117 response.message = msgCrash();
1120 response.exitCode = process.exitCode();
1121 response.error = response.exitCode ? !(flags & IgnoreExitCode) : false;
1122 response.stdErr = QString::fromLocal8Bit(stdErr);
1123 response.stdOut = outputCodec ? outputCodec->toUnicode(stdOut.constData(), stdOut.size()) :
1124 QString::fromLocal8Bit(stdOut);
1125 const QChar cr = QLatin1Char('\r');
1126 response.stdErr.remove(cr);
1127 response.stdOut.remove(cr);
1129 VCSBase::VCSBaseOutputWindow *outputWindow = VCSBase::VCSBaseOutputWindow::instance();
1130 if ((flags & StdErrToWindow) && !response.stdErr.isEmpty())
1131 outputWindow->append(response.stdErr);
1132 if ((flags & StdOutToWindow) && !response.stdOut.isEmpty())
1133 outputWindow->append(response.stdOut);
1137 PerforceResponse PerforcePlugin::runP4Cmd(const QString &workingDir,
1138 const QStringList &args,
1140 const QStringList &extraArgs,
1141 const QByteArray &stdInput,
1142 QTextCodec *outputCodec) const
1144 if (Perforce::Constants::debug)
1145 qDebug() << "PerforcePlugin::runP4Cmd [" << workingDir << ']' << args << extraArgs << stdInput << debugCodec(outputCodec);
1147 VCSBase::VCSBaseOutputWindow *outputWindow = VCSBase::VCSBaseOutputWindow::instance();
1148 if (!m_settings.isValid()) {
1149 PerforceResponse invalidConfigResponse;
1150 invalidConfigResponse.error = true;
1151 invalidConfigResponse.message = tr("Perforce is not correctly configured.");
1152 outputWindow->appendError(invalidConfigResponse.message);
1153 return invalidConfigResponse;
1155 QStringList actualArgs = m_settings.commonP4Arguments(workingDir);
1156 QSharedPointer<QTemporaryFile> tempFile = createTemporaryArgumentFile(extraArgs);
1157 if (!tempFile.isNull())
1158 actualArgs << QLatin1String("-x") << tempFile->fileName();
1159 actualArgs.append(args);
1161 if (flags & CommandToWindow)
1162 outputWindow->appendCommand(workingDir, m_settings.p4Command(), actualArgs);
1164 if (flags & ShowBusyCursor)
1165 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1167 const PerforceResponse response = (flags & RunFullySynchronous) ?
1168 fullySynchronousProcess(workingDir, actualArgs, flags, stdInput, outputCodec) :
1169 synchronousProcess(workingDir, actualArgs, flags, stdInput, outputCodec);
1171 if (flags & ShowBusyCursor)
1172 QApplication::restoreOverrideCursor();
1174 if (response.error) {
1175 if (Perforce::Constants::debug)
1176 qDebug() << response.message;
1177 if (flags & ErrorToWindow)
1178 outputWindow->appendError(response.message);
1183 Core::IEditor * PerforcePlugin::showOutputInEditor(const QString& title, const QString output,
1185 const QString &source,
1188 const VCSBase::VCSBaseEditorParameters *params = findType(editorType);
1189 QTC_ASSERT(params, return 0);
1190 const QString id = params->id;
1191 if (Perforce::Constants::debug)
1192 qDebug() << "PerforcePlugin::showOutputInEditor" << title << id << "Size= " << output.size() << " Type=" << editorType << debugCodec(codec);
1194 Core::IEditor *editor = Core::EditorManager::instance()->openEditorWithContents(id, &s, output);
1195 connect(editor, SIGNAL(annotateRevisionRequested(QString,QString,int)),
1196 this, SLOT(vcsAnnotate(QString,QString,int)));
1197 PerforceEditor *e = qobject_cast<PerforceEditor*>(editor->widget());
1200 e->setForceReadOnly(true);
1201 e->setSource(source);
1202 s.replace(QLatin1Char(' '), QLatin1Char('_'));
1203 e->setSuggestedFileName(s);
1206 Core::IEditor *ie = e->editor();
1207 Core::EditorManager::instance()->activateEditor(ie, Core::EditorManager::ModeSwitch);
1211 void PerforcePlugin::slotSubmitDiff(const QStringList &files)
1213 p4Diff(m_commitWorkingDirectory, files);
1216 void PerforcePlugin::p4Diff(const QString &workingDir, const QStringList &files)
1218 Core::IEditor *existingEditor = 0;
1220 QTextCodec *codec = VCSBase::VCSBaseEditorWidget::getCodec(workingDir, files);
1221 const QString id = VCSBase::VCSBaseEditorWidget::getTitleId(workingDir, files);
1222 const QString source = VCSBase::VCSBaseEditorWidget::getSource(workingDir, files);
1224 // Reuse existing editors for that id
1225 foreach (Core::IEditor *ed, Core::EditorManager::instance()->openedEditors()) {
1226 if (ed->file()->property("originalFileName").toString() == id) {
1227 existingEditor = ed;
1231 // Split arguments according to size
1233 args << QLatin1String("diff") << QLatin1String("-du");
1234 QStringList extraArgs;
1235 if (files.size() > 1) {
1240 const unsigned flags = CommandToWindow|StdErrToWindow|ErrorToWindow|OverrideDiffEnvironment;
1241 const PerforceResponse result = runP4Cmd(workingDir, args, flags,
1242 extraArgs, QByteArray(), codec);
1246 if (existingEditor) {
1247 existingEditor->createNew(result.stdOut);
1248 Core::EditorManager::instance()->activateEditor(existingEditor, Core::EditorManager::ModeSwitch);
1250 Core::IEditor *editor = showOutputInEditor(tr("p4 diff %1").arg(id), result.stdOut, VCSBase::DiffOutput,
1251 VCSBase::VCSBaseEditorWidget::getSource(workingDir, files),
1253 editor->file()->setProperty("originalFileName", id);
1257 void PerforcePlugin::describe(const QString & source, const QString &n)
1259 QTextCodec *codec = source.isEmpty() ? static_cast<QTextCodec *>(0) : VCSBase::VCSBaseEditorWidget::getCodec(source);
1261 args << QLatin1String("describe") << QLatin1String("-du") << n;
1262 const PerforceResponse result = runP4Cmd(m_settings.topLevel(), args, CommandToWindow|StdErrToWindow|ErrorToWindow,
1263 QStringList(), QByteArray(), codec);
1265 showOutputInEditor(tr("p4 describe %1").arg(n), result.stdOut, VCSBase::DiffOutput, source, codec);
1268 void PerforcePlugin::submitCurrentLog()
1270 m_submitActionTriggered = true;
1271 Core::EditorManager *em = Core::EditorManager::instance();
1272 em->closeEditors(QList<Core::IEditor*>() << em->currentEditor());
1275 void PerforcePlugin::cleanCommitMessageFile()
1277 if (!m_commitMessageFileName.isEmpty()) {
1278 QFile::remove(m_commitMessageFileName);
1279 m_commitMessageFileName.clear();
1280 m_commitWorkingDirectory.clear();
1284 bool PerforcePlugin::isCommitEditorOpen() const
1286 return !m_commitMessageFileName.isEmpty();
1289 bool PerforcePlugin::submitEditorAboutToClose(VCSBase::VCSBaseSubmitEditor *submitEditor)
1291 if (!isCommitEditorOpen())
1293 Core::IFile *fileIFace = submitEditor->file();
1294 const PerforceSubmitEditor *perforceEditor = qobject_cast<PerforceSubmitEditor *>(submitEditor);
1295 if (!fileIFace || !perforceEditor)
1297 // Prompt the user. Force a prompt unless submit was actually invoked (that
1298 // is, the editor was closed or shutdown).
1299 bool wantsPrompt = m_settings.promptToSubmit();
1300 const VCSBase::VCSBaseSubmitEditor::PromptSubmitResult answer =
1301 perforceEditor->promptSubmit(tr("Closing p4 Editor"),
1302 tr("Do you want to submit this change list?"),
1303 tr("The commit message check failed. Do you want to submit this change list"),
1304 &wantsPrompt, !m_submitActionTriggered);
1305 m_submitActionTriggered = false;
1307 if (answer == VCSBase::VCSBaseSubmitEditor::SubmitCanceled)
1310 // Set without triggering the checking mechanism
1311 if (wantsPrompt != m_settings.promptToSubmit()) {
1312 m_settings.setPromptToSubmit(wantsPrompt);
1313 m_settings.toSettings(Core::ICore::instance()->settings());
1315 Core::FileManager *fileManager = Core::ICore::instance()->fileManager();
1316 fileManager->blockFileChange(fileIFace);
1318 fileManager->unblockFileChange(fileIFace);
1319 if (answer == VCSBase::VCSBaseSubmitEditor::SubmitDiscarded) {
1320 cleanCommitMessageFile();
1323 // Pipe file into p4 submit -i
1324 QFile commitMessageFile(m_commitMessageFileName);
1325 if (!commitMessageFile.open(QIODevice::ReadOnly|QIODevice::Text)) {
1326 VCSBase::VCSBaseOutputWindow::instance()->appendError(tr("Cannot open temporary file."));
1330 const QByteArray changeDescription = commitMessageFile.readAll();
1331 commitMessageFile.close();
1332 QStringList submitArgs;
1333 submitArgs << QLatin1String("submit") << QLatin1String("-i");
1334 const PerforceResponse submitResponse = runP4Cmd(m_settings.topLevelSymLinkTarget(), submitArgs,
1335 LongTimeOut|RunFullySynchronous|CommandToWindow|StdErrToWindow|ErrorToWindow|ShowBusyCursor,
1336 QStringList(), changeDescription);
1337 if (submitResponse.error) {
1338 VCSBase::VCSBaseOutputWindow::instance()->appendError(tr("p4 submit failed: %1").arg(submitResponse.message));
1341 VCSBase::VCSBaseOutputWindow::instance()->append(submitResponse.stdOut);
1342 if (submitResponse.stdOut.contains(QLatin1String("Out of date files must be resolved or reverted)")))
1343 QMessageBox::warning(submitEditor->widget(), tr("Pending change"), tr("Could not submit the change, because your workspace was out of date. Created a pending submit instead."));
1345 cleanCommitMessageFile();
1349 QString PerforcePlugin::clientFilePath(const QString &serverFilePath)
1351 QTC_ASSERT(m_settings.isValid(), return QString())
1354 args << QLatin1String("fstat") << serverFilePath;
1355 const PerforceResponse response = runP4Cmd(m_settings.topLevelSymLinkTarget(), args,
1356 ShowBusyCursor|RunFullySynchronous|CommandToWindow|StdErrToWindow|ErrorToWindow);
1360 QRegExp r(QLatin1String("\\.\\.\\.\\sclientFile\\s(.+)\n"));
1362 const QString path = r.indexIn(response.stdOut) != -1 ? r.cap(1).trimmed() : QString();
1363 if (Perforce::Constants::debug)
1364 qDebug() << "clientFilePath" << serverFilePath << path;
1368 QString PerforcePlugin::pendingChangesData()
1370 QTC_ASSERT(m_settings.isValid(), return QString())
1372 QStringList args = QStringList(QLatin1String("info"));
1373 const PerforceResponse userResponse = runP4Cmd(m_settings.topLevelSymLinkTarget(), args,
1374 RunFullySynchronous|CommandToWindow|StdErrToWindow|ErrorToWindow);
1375 if (userResponse.error)
1378 QRegExp r(QLatin1String("User\\sname:\\s(\\S+)\\s*\n"));
1379 QTC_ASSERT(r.isValid(), return QString())
1381 const QString user = r.indexIn(userResponse.stdOut) != -1 ? r.cap(1).trimmed() : QString();
1385 args << QLatin1String("changes") << QLatin1String("-s") << QLatin1String("pending") << QLatin1String("-u") << user;
1386 const PerforceResponse dataResponse = runP4Cmd(m_settings.topLevelSymLinkTarget(), args,
1387 RunFullySynchronous|CommandToWindow|StdErrToWindow|ErrorToWindow);
1388 return dataResponse.error ? QString() : dataResponse.stdOut;
1391 PerforcePlugin::~PerforcePlugin()
1395 const PerforceSettings& PerforcePlugin::settings() const
1400 void PerforcePlugin::setSettings(const Settings &newSettings)
1402 if (newSettings != m_settings.settings()) {
1403 m_settings.setSettings(newSettings);
1404 m_managedDirectoryCache.clear();
1405 m_settings.toSettings(Core::ICore::instance()->settings());
1410 static inline QString msgWhereFailed(const QString & file, const QString &why)
1412 //: Failed to run p4 "where" to resolve a Perforce file name to a local
1413 //: file system name.
1414 return PerforcePlugin::tr("Error running \"where\" on %1: %2").
1415 arg(QDir::toNativeSeparators(file), why);
1418 // Map a perforce name "//xx" to its real name in the file system
1419 QString PerforcePlugin::fileNameFromPerforceName(const QString& perforceName,
1421 QString *errorMessage) const
1423 // All happy, already mapped
1424 if (!perforceName.startsWith(QLatin1String("//")))
1425 return perforceName;
1426 // "where" remaps the file to client file tree
1428 args << QLatin1String("where") << perforceName;
1429 unsigned flags = RunFullySynchronous;
1431 flags |= CommandToWindow|StdErrToWindow|ErrorToWindow;
1432 const PerforceResponse response = runP4Cmd(m_settings.topLevelSymLinkTarget(), args, flags);
1433 if (response.error) {
1434 *errorMessage = msgWhereFailed(perforceName, response.message);
1438 QString output = response.stdOut;
1439 if (output.endsWith(QLatin1Char('\r')))
1441 if (output.endsWith(QLatin1Char('\n')))
1444 if (output.isEmpty()) {
1445 //: File is not managed by Perforce
1446 *errorMessage = msgWhereFailed(perforceName, tr("The file is not mapped"));
1449 const QString p4fileSpec = output.mid(output.lastIndexOf(QLatin1Char(' ')) + 1);
1450 const QString rc = m_settings.mapToFileSystem(p4fileSpec);
1451 if (Perforce::Constants::debug)
1452 qDebug() << "fileNameFromPerforceName" << perforceName << p4fileSpec << rc;
1456 PerforcePlugin *PerforcePlugin::perforcePluginInstance()
1458 QTC_ASSERT(m_perforcePluginInstance, return 0);
1459 return m_perforcePluginInstance;
1462 PerforceVersionControl *PerforcePlugin::perforceVersionControl() const
1464 return static_cast<PerforceVersionControl *>(versionControl());
1467 void PerforcePlugin::slotTopLevelFound(const QString &t)
1469 m_settings.setTopLevel(t);
1470 const QString msg = tr("Perforce repository: %1").
1471 arg(QDir::toNativeSeparators(t));
1472 VCSBase::VCSBaseOutputWindow::instance()->appendSilently(msg);
1473 if (Perforce::Constants::debug)
1474 qDebug() << "P4: " << t;
1477 void PerforcePlugin::slotTopLevelFailed(const QString &errorMessage)
1479 VCSBase::VCSBaseOutputWindow::instance()->appendSilently(tr("Perforce: Unable to determine the repository: %1").arg(errorMessage));
1480 if (Perforce::Constants::debug)
1481 qDebug() << errorMessage;
1484 void PerforcePlugin::getTopLevel()
1486 // Run a new checker
1487 if (m_settings.p4Command().isEmpty())
1489 PerforceChecker *checker = new PerforceChecker(this);
1490 connect(checker, SIGNAL(failed(QString)), this, SLOT(slotTopLevelFailed(QString)));
1491 connect(checker, SIGNAL(failed(QString)), checker, SLOT(deleteLater()));
1492 connect(checker, SIGNAL(succeeded(QString)), this, SLOT(slotTopLevelFound(QString)));
1493 connect(checker, SIGNAL(succeeded(QString)),checker, SLOT(deleteLater()));
1494 checker->start(m_settings.p4Command(), m_settings.commonP4Arguments(QString()), 30000);
1500 Q_EXPORT_PLUGIN(Perforce::Internal::PerforcePlugin)