clearcaseplugin.cpp 94.2 KB
Newer Older
Orgad Shaneh's avatar
Orgad Shaneh committed
1 2
/**************************************************************************
**
3
** Copyright (c) 2014 AudioCodes Ltd.
Orgad Shaneh's avatar
Orgad Shaneh committed
4
** Author: Orgad Shaneh <orgad.shaneh@audiocodes.com>
hjk's avatar
hjk committed
5
** Contact: http://www.qt-project.org/legal
Orgad Shaneh's avatar
Orgad Shaneh committed
6
**
hjk's avatar
hjk committed
7
** This file is part of Qt Creator.
Orgad Shaneh's avatar
Orgad Shaneh committed
8
**
hjk's avatar
hjk committed
9 10 11 12 13 14 15
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
Orgad Shaneh's avatar
Orgad Shaneh committed
16 17
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
18 19 20 21 22 23 24 25 26
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
Orgad Shaneh's avatar
Orgad Shaneh committed
27 28
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
29
****************************************************************************/
Orgad Shaneh's avatar
Orgad Shaneh committed
30

31
#include "clearcaseplugin.h"
Orgad Shaneh's avatar
Orgad Shaneh committed
32 33 34 35 36 37 38
#include "activityselector.h"
#include "checkoutdialog.h"
#include "clearcaseconstants.h"
#include "clearcasecontrol.h"
#include "clearcaseeditor.h"
#include "clearcasesubmiteditor.h"
#include "clearcasesubmiteditorwidget.h"
Orgad Shaneh's avatar
Orgad Shaneh committed
39
#include "clearcasesync.h"
Orgad Shaneh's avatar
Orgad Shaneh committed
40 41 42 43 44 45 46 47 48 49 50
#include "settingspage.h"
#include "versionselector.h"
#include "ui_undocheckout.h"

#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/command.h>
#include <coreplugin/coreconstants.h>
#include <coreplugin/documentmanager.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>
51
#include <coreplugin/infobar.h>
Orgad Shaneh's avatar
Orgad Shaneh committed
52 53 54
#include <coreplugin/messagemanager.h>
#include <coreplugin/mimedatabase.h>
#include <coreplugin/progressmanager/progressmanager.h>
55
#include <coreplugin/locator/commandlocator.h>
Orgad Shaneh's avatar
Orgad Shaneh committed
56 57 58
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/project.h>
#include <projectexplorer/iprojectmanager.h>
59
#include <utils/algorithm.h>
Orgad Shaneh's avatar
Orgad Shaneh committed
60 61 62
#include <utils/synchronousprocess.h>
#include <utils/parameteraction.h>
#include <utils/fileutils.h>
63
#include <utils/hostosinfo.h>
Orgad Shaneh's avatar
Orgad Shaneh committed
64 65 66 67 68 69
#include <utils/qtcassert.h>
#include <utils/runextensions.h>
#include <vcsbase/basevcseditorfactory.h>
#include <vcsbase/basevcssubmiteditorfactory.h>
#include <vcsbase/vcsbaseeditor.h>
#include <vcsbase/vcsbaseeditorparameterwidget.h>
70
#include <vcsbase/vcsoutputwindow.h>
Orgad Shaneh's avatar
Orgad Shaneh committed
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
#include <vcsbase/vcsbasesubmiteditor.h>

#include <QAction>
#include <QDebug>
#include <QDialog>
#include <QDialogButtonBox>
#include <QDir>
#include <QFileInfo>
#include <QFuture>
#include <QFutureInterface>
#include <QInputDialog>
#include <QList>
#include <QMainWindow>
#include <QMenu>
#include <QMessageBox>
Orgad Shaneh's avatar
Orgad Shaneh committed
86
#include <QMetaObject>
Orgad Shaneh's avatar
Orgad Shaneh committed
87 88
#include <QMutex>
#include <QProcess>
89
#include <QRegExp>
Orgad Shaneh's avatar
Orgad Shaneh committed
90
#include <QSharedPointer>
91 92 93 94
#include <QtConcurrentRun>
#include <QTemporaryFile>
#include <QTextCodec>
#include <QtPlugin>
Orgad Shaneh's avatar
Orgad Shaneh committed
95 96 97 98 99
#include <QUrl>
#include <QUuid>
#include <QVariant>
#include <QVBoxLayout>
#include <QXmlStreamReader>
100 101
#ifdef WITH_TESTS
#include <QTest>
102
#include <coreplugin/vcsmanager.h>
103
#endif
Orgad Shaneh's avatar
Orgad Shaneh committed
104

105 106
using namespace Core;
using namespace ProjectExplorer;
107
using namespace VcsBase;
108
using namespace Utils;
109

Orgad Shaneh's avatar
Orgad Shaneh committed
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
namespace ClearCase {
namespace Internal {

static const char CMD_ID_CLEARCASE_MENU[]     = "ClearCase.Menu";
static const char CMD_ID_CHECKOUT[]           = "ClearCase.CheckOut";
static const char CMD_ID_CHECKIN[]            = "ClearCase.CheckInCurrent";
static const char CMD_ID_UNDOCHECKOUT[]       = "ClearCase.UndoCheckOut";
static const char CMD_ID_UNDOHIJACK[]         = "ClearCase.UndoHijack";
static const char CMD_ID_DIFF_CURRENT[]       = "ClearCase.DiffCurrent";
static const char CMD_ID_HISTORY_CURRENT[]    = "ClearCase.HistoryCurrent";
static const char CMD_ID_ANNOTATE[]           = "ClearCase.Annotate";
static const char CMD_ID_ADD_FILE[]           = "ClearCase.AddFile";
static const char CMD_ID_DIFF_ACTIVITY[]      = "ClearCase.DiffActivity";
static const char CMD_ID_CHECKIN_ACTIVITY[]   = "ClearCase.CheckInActivity";
static const char CMD_ID_UPDATEINDEX[]        = "ClearCase.UpdateIndex";
static const char CMD_ID_UPDATE_VIEW[]        = "ClearCase.UpdateView";
static const char CMD_ID_CHECKIN_ALL[]        = "ClearCase.CheckInAll";
static const char CMD_ID_STATUS[]             = "ClearCase.Status";

129
static const VcsBaseEditorParameters editorParameters[] = {
Orgad Shaneh's avatar
Orgad Shaneh committed
130
{
Orgad Shaneh's avatar
Orgad Shaneh committed
131
    VcsBase::LogOutput,
Orgad Shaneh's avatar
Orgad Shaneh committed
132 133
    "ClearCase File Log Editor",   // id
    QT_TRANSLATE_NOOP("VCS", "ClearCase File Log Editor"),   // display_name
Orgad Shaneh's avatar
Orgad Shaneh committed
134
    "text/vnd.qtcreator.clearcase.log"},
Orgad Shaneh's avatar
Orgad Shaneh committed
135 136 137
{    VcsBase::AnnotateOutput,
    "ClearCase Annotation Editor",  // id
    QT_TRANSLATE_NOOP("VCS", "ClearCase Annotation Editor"),   // display_name
Orgad Shaneh's avatar
Orgad Shaneh committed
138
    "text/vnd.qtcreator.clearcase.annotation"},
Orgad Shaneh's avatar
Orgad Shaneh committed
139 140 141
{   VcsBase::DiffOutput,
    "ClearCase Diff Editor",  // id
    QT_TRANSLATE_NOOP("VCS", "ClearCase Diff Editor"),   // display_name
142
    "text/x-patch"}
Orgad Shaneh's avatar
Orgad Shaneh committed
143 144 145
};

// Utility to find a parameter set by type
146
static const VcsBaseEditorParameters *findType(int ie)
Orgad Shaneh's avatar
Orgad Shaneh committed
147
{
148 149
    const EditorContentType et = static_cast<EditorContentType>(ie);
    return VcsBaseEditor::findType(editorParameters, sizeof(editorParameters)/sizeof(VcsBaseEditorParameters), et);
Orgad Shaneh's avatar
Orgad Shaneh committed
150 151
}

152
static QString debugCodec(const QTextCodec *c)
Orgad Shaneh's avatar
Orgad Shaneh committed
153
{
154
    return c ? QString::fromLatin1(c->name()) : QString::fromLatin1("Null codec");
Orgad Shaneh's avatar
Orgad Shaneh committed
155 156 157 158 159
}

// ------------- ClearCasePlugin
ClearCasePlugin *ClearCasePlugin::m_clearcasePluginInstance = 0;

Orgad Shaneh's avatar
Orgad Shaneh committed
160 161 162 163 164 165
ViewData::ViewData() :
    isDynamic(false),
    isUcm(false)
{
}

Orgad Shaneh's avatar
Orgad Shaneh committed
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
ClearCasePlugin::ClearCasePlugin() :
    m_commandLocator(0),
    m_checkOutAction(0),
    m_checkInCurrentAction(0),
    m_undoCheckOutAction(0),
    m_undoHijackAction(0),
    m_diffCurrentAction(0),
    m_historyCurrentAction(0),
    m_annotateCurrentAction(0),
    m_addFileAction(0),
    m_diffActivityAction(0),
    m_updateIndexAction(0),
    m_updateViewAction(0),
    m_checkInActivityAction(0),
    m_checkInAllAction(0),
    m_statusAction(0),
    m_checkInSelectedAction(0),
    m_checkInDiffAction(0),
    m_submitUndoAction(0),
    m_submitRedoAction(0),
    m_menuAction(0),
187
    m_submitActionTriggered(false),
Orgad Shaneh's avatar
Orgad Shaneh committed
188 189
    m_activityMutex(new QMutex),
    m_statusMap(new StatusMap)
190 191 192
  #ifdef WITH_TESTS
   ,m_fakeClearTool(false)
  #endif
Orgad Shaneh's avatar
Orgad Shaneh committed
193
{
Orgad Shaneh's avatar
Orgad Shaneh committed
194
    qRegisterMetaType<ClearCase::Internal::FileStatus::Status>("ClearCase::Internal::FileStatus::Status");
Orgad Shaneh's avatar
Orgad Shaneh committed
195 196 197 198 199 200
}

ClearCasePlugin::~ClearCasePlugin()
{
    cleanCheckInMessageFile();
    // wait for sync thread to finish reading activities
201 202 203
    m_activityMutex->lock();
    m_activityMutex->unlock();
    delete m_activityMutex;
Orgad Shaneh's avatar
Orgad Shaneh committed
204 205 206 207 208 209 210
}

void ClearCasePlugin::cleanCheckInMessageFile()
{
    if (!m_checkInMessageFileName.isEmpty()) {
        QFile::remove(m_checkInMessageFileName);
        m_checkInMessageFileName.clear();
211
        m_checkInView.clear();
Orgad Shaneh's avatar
Orgad Shaneh committed
212 213 214 215 216 217 218 219
    }
}

bool ClearCasePlugin::isCheckInEditorOpen() const
{
    return !m_checkInMessageFileName.isEmpty();
}

220 221
/// Files in this directories are under ClearCase control
QStringList ClearCasePlugin::getVobList() const
222
{
223 224 225 226 227 228 229 230 231 232 233 234 235 236
    QStringList args(QLatin1String("lsvob"));
    args << QLatin1String("-s");
    const ClearCaseResponse response =
            runCleartool(currentState().topLevel(), args, m_settings.timeOutMS(), SilentRun);

    return response.stdOut.split(QLatin1Char('\n'), QString::SkipEmptyParts);
}

/// Get the drive letter of a path
/// Necessary since QDir(directory).rootPath() returns C:/ in all cases
QString ClearCasePlugin::getDriveLetterOfPath(const QString &directory)
{
    // cdUp until we get just the drive letter
    QDir dir(directory);
Orgad Shaneh's avatar
Orgad Shaneh committed
237
    while (!dir.isRoot() && dir.cdUp())
238 239 240 241 242
    { }

    return dir.path();
}

243 244 245 246 247
void ClearCasePlugin::updateStatusForFile(const QString &absFile)
{
    setStatus(absFile, getFileStatus(absFile), false);
}

248 249 250 251 252 253 254
/// Give warning if a derived object is edited
void ClearCasePlugin::updateEditDerivedObjectWarning(const QString &fileName,
                                                     const FileStatus::Status status)
{
    if (!isDynamic())
        return;

255
    IDocument *curDocument = EditorManager::currentDocument();
256 257 258
    if (!curDocument)
        return;

259 260
    InfoBar *infoBar = curDocument->infoBar();
    const Id derivedObjectWarning("ClearCase.DerivedObjectWarning");
261 262 263 264 265

    if (status == FileStatus::Derived) {
        if (!infoBar->canInfoBeAdded(derivedObjectWarning))
            return;

266 267
        infoBar->addInfo(InfoBarEntry(derivedObjectWarning,
                                      tr("Editing Derived Object: %1").arg(fileName)));
268 269 270 271 272
    } else {
        infoBar->removeInfo(derivedObjectWarning);
    }
}

273 274 275 276 277 278 279 280 281 282 283 284
FileStatus::Status ClearCasePlugin::getFileStatus(const QString &fileName) const
{
    QTC_CHECK(!fileName.isEmpty());

    const QDir viewRootDir = QFileInfo(fileName).dir();
    const QString viewRoot = viewRootDir.path();

    QStringList args(QLatin1String("ls"));
    args << fileName;
    QString buffer = runCleartoolSync(viewRoot, args);

    const int atatpos = buffer.indexOf(QLatin1String("@@"));
285
    if (atatpos != -1) { // probably a managed file
286 287 288 289 290 291
        const QString absFile =
                viewRootDir.absoluteFilePath(
                    QDir::fromNativeSeparators(buffer.left(atatpos)));
        QTC_CHECK(QFile(absFile).exists());
        QTC_CHECK(!absFile.isEmpty());

292 293 294 295 296 297 298 299 300
        // "cleartool ls" of a derived object looks like this:
        // /path/to/file/export/MyFile.h@@--11-13T19:52.266580
        const QChar c = buffer.at(atatpos + 2);
        const bool isDerivedObject = c != QLatin1Char('/') && c != QLatin1Char('\\');
        if (isDerivedObject)
            return FileStatus::Derived;

        // find first whitespace. anything before that is not interesting
        const int wspos = buffer.indexOf(QRegExp(QLatin1String("\\s")));
301 302 303 304 305 306 307 308 309 310 311
        if (buffer.lastIndexOf(QLatin1String("CHECKEDOUT"), wspos) != -1)
            return FileStatus::CheckedOut;
        else
            return FileStatus::CheckedIn;
    } else {
        QTC_CHECK(QFile(fileName).exists());
        QTC_CHECK(!fileName.isEmpty());
        return FileStatus::NotManaged;
    }
}

312 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 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379
///
/// Check if the directory is managed by ClearCase.
///
/// There are 6 cases to consider for accessing ClearCase views:
///
/// 1) Windows: dynamic view under M:\<view_tag> (working dir view)
/// 2) Windows: dynamic view under Z:\ (similar to unix "set view" by using "subst" or "net use")
/// 3) Windows: snapshot view
/// 4) Unix: dynamic view under /view/<view_tag> (working dir view)
/// 5) Unix: dynamic view which are set view (transparent access in a shell process)
/// 6) Unix: snapshot view
///
/// Note: the drive letters M: and Z: can be chosen by the user. /view is the "view-root"
///       directory and is not configurable, while VOB names and mount points are configurable
///       by the ClearCase admin.
///
/// Note: All cases except #5 have a root directory, i.e., all files reside under a directory.
///       For #5 files are "mounted" and access is transparent (e.g., under /vobs).
///
/// For a view named "myview" and a VOB named "vobA" topLevels would be:
/// 1) M:/myview/vobA
/// 2) Z:/vobA
/// 3) c:/snapshots/myview/vobA
/// 4) /view/myview/vobs/vobA
/// 5) /vobs/vobA/
/// 6) /home/<username>/snapshots/myview/vobs/vobA
///
/// Note: The VOB directory is used as toplevel although the directory one up could have been
///       used on cases execpt 5. For case 5 it would have been /, which we don't want.
///
/// "cleartool pwv" returns the values for "set view" and "working directory view", also for
/// snapshot views.
///
/// \returns The ClearCase topLevel/VOB directory for this directory
QString ClearCasePlugin::ccManagesDirectory(const QString &directory) const
{
    QStringList args(QLatin1String("pwv"));
    const ClearCaseResponse response =
            runCleartool(directory, args, m_settings.timeOutMS(), SilentRun);

    if (response.error)
        return QString();

    const QStringList result = response.stdOut.split(QLatin1Char('\n'), QString::SkipEmptyParts);
    if (result.size() != 2)
        return QString();

    const QByteArray workingDirPattern("Working directory view: ");
    if (!result[0].startsWith(QLatin1String(workingDirPattern)))
        return QString();
    const QString workingDirectoryView = result[0].mid(workingDirPattern.size());

    const QByteArray setViewDirPattern("Set view: ");
    if (!result[1].startsWith(QLatin1String(setViewDirPattern)))
        return QString();
    const QString setView = result[1].mid(setViewDirPattern.size());

    const QString none(QLatin1String("** NONE **"));
    QString rootDir;
    if (setView != none || workingDirectoryView != none)
        rootDir = ccViewRoot(directory);
    else
        return QString();

    // Check if the directory is inside one of the known VOBs.
    static QStringList vobs;
    if (vobs.empty())
        vobs = getVobList();
380

381 382
    foreach (const QString &relativeVobDir, vobs) {
        const QString vobPath = QDir::cleanPath(rootDir + QDir::fromNativeSeparators(relativeVobDir));
383
        const bool isManaged = (vobPath == directory)
384
                || FileName::fromString(directory).isChildOf(FileName::fromString(vobPath));
385 386 387
        if (isManaged)
            return vobPath;
    }
388 389 390 391

    return QString();
}

392 393 394 395 396 397 398 399 400 401 402
/// Find the root path of a clearcase view. Precondition: This is a clearcase managed dir
QString ClearCasePlugin::ccViewRoot(const QString &directory) const
{
    QStringList args(QLatin1String("pwv"));
    args << QLatin1String("-root");
    const ClearCaseResponse response =
            runCleartool(directory, args, m_settings.timeOutMS(), SilentRun);

    QString root = response.stdOut.trimmed();

    if (root.isEmpty()) {
403
        if (HostOsInfo::isWindowsHost())
404 405 406 407 408 409 410 411
            root = getDriveLetterOfPath(directory);
        else
            root = QLatin1String("/");
    }

    return QDir::fromNativeSeparators(root);
}

412 413
/*! Find top level for view that contains \a directory
 *
414
 * Handles both dynamic views and snapshot views.
415
 */
Orgad Shaneh's avatar
Orgad Shaneh committed
416 417
QString ClearCasePlugin::findTopLevel(const QString &directory) const
{
418 419
    // Do not check again if we've already tested that the dir is managed,
    // or if it is a child of a managed dir (top level).
420
    if ((directory == m_topLevel) ||
421
           FileName::fromString(directory).isChildOf(FileName::fromString(m_topLevel)))
422 423
        return m_topLevel;

424
    return ccManagesDirectory(directory);
Orgad Shaneh's avatar
Orgad Shaneh committed
425 426
}

427
static const VcsBaseSubmitEditorParameters submitParameters = {
428 429 430
    Constants::CLEARCASE_SUBMIT_MIMETYPE,
    Constants::CLEARCASECHECKINEDITOR_ID,
    Constants::CLEARCASECHECKINEDITOR_DISPLAY_NAME,
431
    VcsBaseSubmitEditorParameters::DiffFiles
Orgad Shaneh's avatar
Orgad Shaneh committed
432 433 434 435 436 437 438 439 440 441
};

bool ClearCasePlugin::initialize(const QStringList & /*arguments */, QString *errorMessage)
{
    using namespace Constants;
    using namespace Core::Constants;

    initializeVcs(new ClearCaseControl(this));

    m_clearcasePluginInstance = this;
442
    connect(ICore::instance(), SIGNAL(coreAboutToClose()), this, SLOT(closing()));
443 444
    connect(ProgressManager::instance(), SIGNAL(allTasksFinished(Core::Id)),
            this, SLOT(tasksFinished(Core::Id)));
Orgad Shaneh's avatar
Orgad Shaneh committed
445

hjk's avatar
hjk committed
446
    if (!MimeDatabase::addMimeTypes(QLatin1String(":/clearcase/ClearCase.mimetypes.xml"), errorMessage))
Orgad Shaneh's avatar
Orgad Shaneh committed
447 448
        return false;

449
    m_settings.fromSettings(ICore::settings());
Orgad Shaneh's avatar
Orgad Shaneh committed
450 451

    // update view name when changing active project
452 453
    connect(ProjectExplorerPlugin::instance(), SIGNAL(currentProjectChanged(ProjectExplorer::Project*)),
            this, SLOT(projectChanged(ProjectExplorer::Project*)));
Orgad Shaneh's avatar
Orgad Shaneh committed
454 455 456

    addAutoReleasedObject(new SettingsPage);

457 458
    addAutoReleasedObject(new VcsSubmitEditorFactory(&submitParameters,
        []() { return new ClearCaseSubmitEditor(&submitParameters); }));
Orgad Shaneh's avatar
Orgad Shaneh committed
459 460 461

    // any editor responds to describe (when clicking a version)
    static const char *describeSlot = SLOT(describe(QString,QString));
462
    const int editorCount = sizeof(editorParameters)/sizeof(VcsBaseEditorParameters);
463
    const auto widgetCreator = []() { return new ClearCaseEditorWidget; };
Orgad Shaneh's avatar
Orgad Shaneh committed
464
    for (int i = 0; i < editorCount; i++)
465
        addAutoReleasedObject(new VcsEditorFactory(editorParameters + i, widgetCreator, this, describeSlot));
Orgad Shaneh's avatar
Orgad Shaneh committed
466 467 468 469

    const QString description = QLatin1String("ClearCase");
    const QString prefix = QLatin1String("cc");
    // register cc prefix in Locator
470
    m_commandLocator = new CommandLocator("cc", description, prefix);
Orgad Shaneh's avatar
Orgad Shaneh committed
471 472 473
    addAutoReleasedObject(m_commandLocator);

    //register actions
474
    ActionContainer *toolsContainer = ActionManager::actionContainer(M_TOOLS);
Orgad Shaneh's avatar
Orgad Shaneh committed
475

476
    ActionContainer *clearcaseMenu = ActionManager::createMenu(CMD_ID_CLEARCASE_MENU);
Orgad Shaneh's avatar
Orgad Shaneh committed
477 478 479
    clearcaseMenu->menu()->setTitle(tr("C&learCase"));
    toolsContainer->addMenu(clearcaseMenu);
    m_menuAction = clearcaseMenu->menu()->menuAction();
480 481
    Context globalcontext(C_GLOBAL);
    Command *command;
Orgad Shaneh's avatar
Orgad Shaneh committed
482

483
    m_checkOutAction = new ParameterAction(tr("Check Out..."), tr("Check &Out \"%1\"..."), ParameterAction::AlwaysEnabled, this);
484
    command = ActionManager::registerAction(m_checkOutAction, CMD_ID_CHECKOUT,
Orgad Shaneh's avatar
Orgad Shaneh committed
485
        globalcontext);
486 487
    command->setAttribute(Command::CA_UpdateText);
    command->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+L,Meta+O") : tr("Alt+L,Alt+O")));
Orgad Shaneh's avatar
Orgad Shaneh committed
488 489 490 491
    connect(m_checkOutAction, SIGNAL(triggered()), this, SLOT(checkOutCurrentFile()));
    clearcaseMenu->addAction(command);
    m_commandLocator->appendCommand(command);

492
    m_checkInCurrentAction = new ParameterAction(tr("Check &In..."), tr("Check &In \"%1\"..."), ParameterAction::AlwaysEnabled, this);
493 494 495
    command = ActionManager::registerAction(m_checkInCurrentAction, CMD_ID_CHECKIN, globalcontext);
    command->setAttribute(Command::CA_UpdateText);
    command->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+L,Meta+I") : tr("Alt+L,Alt+I")));
Orgad Shaneh's avatar
Orgad Shaneh committed
496 497 498 499
    connect(m_checkInCurrentAction, SIGNAL(triggered()), this, SLOT(startCheckInCurrentFile()));
    clearcaseMenu->addAction(command);
    m_commandLocator->appendCommand(command);

500
    m_undoCheckOutAction = new ParameterAction(tr("Undo Check Out"), tr("&Undo Check Out \"%1\""), ParameterAction::AlwaysEnabled, this);
501 502 503
    command = ActionManager::registerAction(m_undoCheckOutAction, CMD_ID_UNDOCHECKOUT, globalcontext);
    command->setAttribute(Command::CA_UpdateText);
    command->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+L,Meta+U") : tr("Alt+L,Alt+U")));
Orgad Shaneh's avatar
Orgad Shaneh committed
504 505 506 507
    connect(m_undoCheckOutAction, SIGNAL(triggered()), this, SLOT(undoCheckOutCurrent()));
    clearcaseMenu->addAction(command);
    m_commandLocator->appendCommand(command);

508
    m_undoHijackAction = new ParameterAction(tr("Undo Hijack"), tr("Undo Hi&jack \"%1\""), ParameterAction::AlwaysEnabled, this);
509 510 511
    command = ActionManager::registerAction(m_undoHijackAction, CMD_ID_UNDOHIJACK, globalcontext);
    command->setAttribute(Command::CA_UpdateText);
    command->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+L,Meta+R") : tr("Alt+L,Alt+R")));
Orgad Shaneh's avatar
Orgad Shaneh committed
512 513 514 515 516 517
    connect(m_undoHijackAction, SIGNAL(triggered()), this, SLOT(undoHijackCurrent()));
    clearcaseMenu->addAction(command);
    m_commandLocator->appendCommand(command);

    clearcaseMenu->addSeparator(globalcontext);

518
    m_diffCurrentAction = new ParameterAction(tr("Diff Current File"), tr("&Diff \"%1\""), ParameterAction::EnabledWithParameter, this);
519
    command = ActionManager::registerAction(m_diffCurrentAction,
Orgad Shaneh's avatar
Orgad Shaneh committed
520
        CMD_ID_DIFF_CURRENT, globalcontext);
521 522
    command->setAttribute(Command::CA_UpdateText);
    command->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+L,Meta+D") : tr("Alt+L,Alt+D")));
Orgad Shaneh's avatar
Orgad Shaneh committed
523 524 525 526
    connect(m_diffCurrentAction, SIGNAL(triggered()), this, SLOT(diffCurrentFile()));
    clearcaseMenu->addAction(command);
    m_commandLocator->appendCommand(command);

527
    m_historyCurrentAction = new ParameterAction(tr("History Current File"), tr("&History \"%1\""), ParameterAction::EnabledWithParameter, this);
528
    command = ActionManager::registerAction(m_historyCurrentAction,
Orgad Shaneh's avatar
Orgad Shaneh committed
529
        CMD_ID_HISTORY_CURRENT, globalcontext);
530 531
    command->setAttribute(Command::CA_UpdateText);
    command->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+L,Meta+H") : tr("Alt+L,Alt+H")));
Orgad Shaneh's avatar
Orgad Shaneh committed
532 533 534 535 536
    connect(m_historyCurrentAction, SIGNAL(triggered()), this,
        SLOT(historyCurrentFile()));
    clearcaseMenu->addAction(command);
    m_commandLocator->appendCommand(command);

537
    m_annotateCurrentAction = new ParameterAction(tr("Annotate Current File"), tr("&Annotate \"%1\""), ParameterAction::EnabledWithParameter, this);
538
    command = ActionManager::registerAction(m_annotateCurrentAction,
Orgad Shaneh's avatar
Orgad Shaneh committed
539
        CMD_ID_ANNOTATE, globalcontext);
540 541
    command->setAttribute(Command::CA_UpdateText);
    command->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+L,Meta+A") : tr("Alt+L,Alt+A")));
Orgad Shaneh's avatar
Orgad Shaneh committed
542 543 544 545 546
    connect(m_annotateCurrentAction, SIGNAL(triggered()), this,
        SLOT(annotateCurrentFile()));
    clearcaseMenu->addAction(command);
    m_commandLocator->appendCommand(command);

547
    m_addFileAction = new ParameterAction(tr("Add File..."), tr("Add File \"%1\""), ParameterAction::EnabledWithParameter, this);
548 549
    command = ActionManager::registerAction(m_addFileAction, CMD_ID_ADD_FILE, globalcontext);
    command->setAttribute(Command::CA_UpdateText);
Orgad Shaneh's avatar
Orgad Shaneh committed
550 551 552 553 554 555
    connect(m_addFileAction, SIGNAL(triggered()), this, SLOT(addCurrentFile()));
    clearcaseMenu->addAction(command);

    clearcaseMenu->addSeparator(globalcontext);

    m_diffActivityAction = new QAction(tr("Diff A&ctivity..."), this);
556
    m_diffActivityAction->setEnabled(false);
557
    command = ActionManager::registerAction(m_diffActivityAction, CMD_ID_DIFF_ACTIVITY, globalcontext);
Orgad Shaneh's avatar
Orgad Shaneh committed
558 559 560 561
    connect(m_diffActivityAction, SIGNAL(triggered()), this, SLOT(diffActivity()));
    clearcaseMenu->addAction(command);
    m_commandLocator->appendCommand(command);

562
    m_checkInActivityAction = new ParameterAction(tr("Ch&eck In Activity"), tr("Chec&k In Activity \"%1\"..."), ParameterAction::EnabledWithParameter, this);
563
    m_checkInActivityAction->setEnabled(false);
564
    command = ActionManager::registerAction(m_checkInActivityAction, CMD_ID_CHECKIN_ACTIVITY, globalcontext);
Orgad Shaneh's avatar
Orgad Shaneh committed
565
    connect(m_checkInActivityAction, SIGNAL(triggered()), this, SLOT(startCheckInActivity()));
566
    command->setAttribute(Command::CA_UpdateText);
Orgad Shaneh's avatar
Orgad Shaneh committed
567 568 569 570 571 572
    clearcaseMenu->addAction(command);
    m_commandLocator->appendCommand(command);

    clearcaseMenu->addSeparator(globalcontext);

    m_updateIndexAction = new QAction(tr("Update Index"), this);
573
    command = ActionManager::registerAction(m_updateIndexAction, CMD_ID_UPDATEINDEX, globalcontext);
Orgad Shaneh's avatar
Orgad Shaneh committed
574 575 576
    connect(m_updateIndexAction, SIGNAL(triggered()), this, SLOT(updateIndex()));
    clearcaseMenu->addAction(command);

577
    m_updateViewAction = new ParameterAction(tr("Update View"), tr("U&pdate View \"%1\""), ParameterAction::EnabledWithParameter, this);
578
    command = ActionManager::registerAction(m_updateViewAction, CMD_ID_UPDATE_VIEW, globalcontext);
Orgad Shaneh's avatar
Orgad Shaneh committed
579
    connect(m_updateViewAction, SIGNAL(triggered()), this, SLOT(updateView()));
580
    command->setAttribute(Command::CA_UpdateText);
Orgad Shaneh's avatar
Orgad Shaneh committed
581 582 583 584 585
    clearcaseMenu->addAction(command);

    clearcaseMenu->addSeparator(globalcontext);

    m_checkInAllAction = new QAction(tr("Check In All &Files..."), this);
586 587
    command = ActionManager::registerAction(m_checkInAllAction, CMD_ID_CHECKIN_ALL, globalcontext);
    command->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+L,Meta+F") : tr("Alt+L,Alt+F")));
Orgad Shaneh's avatar
Orgad Shaneh committed
588 589 590 591 592
    connect(m_checkInAllAction, SIGNAL(triggered()), this, SLOT(startCheckInAll()));
    clearcaseMenu->addAction(command);
    m_commandLocator->appendCommand(command);

    m_statusAction = new QAction(tr("View &Status"), this);
593 594
    command = ActionManager::registerAction(m_statusAction, CMD_ID_STATUS, globalcontext);
    command->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+L,Meta+S") : tr("Alt+L,Alt+S")));
Orgad Shaneh's avatar
Orgad Shaneh committed
595 596 597 598 599
    connect(m_statusAction, SIGNAL(triggered()), this, SLOT(viewStatus()));
    clearcaseMenu->addAction(command);
    m_commandLocator->appendCommand(command);

    // Actions of the submit editor
600
    Context clearcasecheckincontext(Constants::CLEARCASECHECKINEDITOR_ID);
Orgad Shaneh's avatar
Orgad Shaneh committed
601

602
    m_checkInSelectedAction = new QAction(VcsBaseSubmitEditor::submitIcon(), tr("Check In"), this);
603 604
    command = ActionManager::registerAction(m_checkInSelectedAction, Constants::CHECKIN_SELECTED, clearcasecheckincontext);
    command->setAttribute(Command::CA_UpdateText);
Orgad Shaneh's avatar
Orgad Shaneh committed
605 606
    connect(m_checkInSelectedAction, SIGNAL(triggered()), this, SLOT(checkInSelected()));

607
    m_checkInDiffAction = new QAction(VcsBaseSubmitEditor::diffIcon(), tr("Diff Selected Files"), this);
608
    command = ActionManager::registerAction(m_checkInDiffAction , Constants::DIFF_SELECTED, clearcasecheckincontext);
Orgad Shaneh's avatar
Orgad Shaneh committed
609 610

    m_submitUndoAction = new QAction(tr("&Undo"), this);
611
    command = ActionManager::registerAction(m_submitUndoAction, Core::Constants::UNDO, clearcasecheckincontext);
Orgad Shaneh's avatar
Orgad Shaneh committed
612 613

    m_submitRedoAction = new QAction(tr("&Redo"), this);
614
    command = ActionManager::registerAction(m_submitRedoAction, Core::Constants::REDO, clearcasecheckincontext);
Orgad Shaneh's avatar
Orgad Shaneh committed
615 616 617 618 619

    return true;
}

// called before closing the submit editor
620
bool ClearCasePlugin::submitEditorAboutToClose()
Orgad Shaneh's avatar
Orgad Shaneh committed
621 622 623 624
{
    if (!isCheckInEditorOpen())
        return true;

625 626
    ClearCaseSubmitEditor *editor = qobject_cast<ClearCaseSubmitEditor *>(submitEditor());
    QTC_ASSERT(editor, return true);
627
    IDocument *editorDocument = editor->document();
628
    QTC_ASSERT(editorDocument, return true);
Orgad Shaneh's avatar
Orgad Shaneh committed
629 630 631

    // Submit editor closing. Make it write out the check in message
    // and retrieve files
632
    const QFileInfo editorFile(editorDocument->filePath());
Orgad Shaneh's avatar
Orgad Shaneh committed
633 634 635 636 637 638 639
    const QFileInfo changeFile(m_checkInMessageFileName);
    if (editorFile.absoluteFilePath() != changeFile.absoluteFilePath())
        return true; // Oops?!

    // Prompt user. Force a prompt unless submit was actually invoked (that
    // is, the editor was closed or shutdown).
    bool prompt = m_settings.promptToCheckIn;
640
    const VcsBaseSubmitEditor::PromptSubmitResult answer =
Orgad Shaneh's avatar
Orgad Shaneh committed
641 642 643 644 645 646
            editor->promptSubmit(tr("Closing ClearCase Editor"),
                                 tr("Do you want to check in the files?"),
                                 tr("The comment check failed. Do you want to check in the files?"),
                                 &prompt, !m_submitActionTriggered);
    m_submitActionTriggered = false;
    switch (answer) {
647
    case VcsBaseSubmitEditor::SubmitCanceled:
Orgad Shaneh's avatar
Orgad Shaneh committed
648
        return false; // Keep editing and change file
649
    case VcsBaseSubmitEditor::SubmitDiscarded:
Orgad Shaneh's avatar
Orgad Shaneh committed
650 651 652 653 654 655 656 657
        cleanCheckInMessageFile();
        return true; // Cancel all
    default:
        break;
    }
    // If user changed
    if (prompt != m_settings.promptToCheckIn) {
        m_settings.promptToCheckIn = prompt;
658
        m_settings.toSettings(ICore::settings());
Orgad Shaneh's avatar
Orgad Shaneh committed
659 660 661 662 663 664
    }

    const QStringList fileList = editor->checkedFiles();
    bool closeEditor = true;
    if (!fileList.empty()) {
        // get message & check in
665
        closeEditor = DocumentManager::saveDocument(editorDocument);
Orgad Shaneh's avatar
Orgad Shaneh committed
666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683
        if (closeEditor) {
            ClearCaseSubmitEditorWidget *widget = editor->submitEditorWidget();
            closeEditor = vcsCheckIn(m_checkInMessageFileName, fileList, widget->activity(),
                                   widget->isIdentical(), widget->isPreserve(),
                                   widget->activityChanged());
        }
    }
    // vcsCheckIn might fail if some of the files failed to check-in (though it does check-in
    // those who didn't fail). Therefore, if more than one file was sent, consider it as success
    // anyway (sync will be called from vcsCheckIn for next attempt)
    closeEditor |= (fileList.count() > 1);
    if (closeEditor)
        cleanCheckInMessageFile();
    return closeEditor;
}

void ClearCasePlugin::diffCheckInFiles(const QStringList &files)
{
684
    ccDiffWithPred(m_checkInView, files);
Orgad Shaneh's avatar
Orgad Shaneh committed
685 686
}

687
static void setWorkingDirectory(IEditor *editor, const QString &wd)
Orgad Shaneh's avatar
Orgad Shaneh committed
688
{
689
    if (VcsBaseEditorWidget *ve = qobject_cast<VcsBaseEditorWidget*>(editor->widget()))
690
        ve->setWorkingDirectory(wd);
Orgad Shaneh's avatar
Orgad Shaneh committed
691 692 693 694 695 696 697 698 699 700 701 702 703 704 705
}

//! retrieve full location of predecessor of \a version
QString ClearCasePlugin::ccGetPredecessor(const QString &version) const
{
    QStringList args(QLatin1String("describe"));
    args << QLatin1String("-fmt") << QLatin1String("%En@@%PSn") << version;
    const ClearCaseResponse response =
            runCleartool(currentState().topLevel(), args, m_settings.timeOutMS(), SilentRun);
    if (response.error || response.stdOut.endsWith(QLatin1Char('@'))) // <name-unknown>@@
        return QString();
    else
        return response.stdOut;
}

706
//! Get a list of paths to active VOBs.
707
//! Paths are relative to viewRoot
Orgad Shaneh's avatar
Orgad Shaneh committed
708 709 710 711
QStringList ClearCasePlugin::ccGetActiveVobs() const
{
    QStringList res;
    QStringList args(QLatin1String("lsvob"));
712 713
    const QString theViewRoot = viewRoot();

Orgad Shaneh's avatar
Orgad Shaneh committed
714
    const ClearCaseResponse response =
715
            runCleartool(theViewRoot, args, m_settings.timeOutMS(), SilentRun);
Orgad Shaneh's avatar
Orgad Shaneh committed
716 717
    if (response.error)
        return res;
718 719 720 721 722

    // format of output unix:
    // * /path/to/vob   /path/to/vob/storage.vbs <and some text omitted here>
    // format of output windows:
    // * \vob     \\share\path\to\vob\storage.vbs <and some text omitted here>
723
    QString prefix = theViewRoot;
724 725 726
    if (!prefix.endsWith(QLatin1Char('/')))
        prefix += QLatin1Char('/');

727
    const QDir theViewRootDir(theViewRoot);
728 729 730 731 732 733 734
    foreach (const QString &line, response.stdOut.split(QLatin1Char('\n'), QString::SkipEmptyParts)) {
        const bool isActive = line.at(0) == QLatin1Char('*');
        if (!isActive)
            continue;

        const QString dir =
                QDir::fromNativeSeparators(line.mid(3, line.indexOf(QLatin1Char(' '), 3) - 3));
735
        const QString relativeDir = theViewRootDir.relativeFilePath(dir);
736 737 738 739 740 741

        // Snapshot views does not necessarily have all active VOBs loaded, so we'll have to
        // check if the dirs exists as well. Else the command will work, but the output will
        // complain about the element not being loaded.
        if (QFile::exists(prefix + relativeDir))
            res.append(relativeDir);
Orgad Shaneh's avatar
Orgad Shaneh committed
742 743 744 745
    }
    return res;
}

746 747 748 749 750 751 752 753 754 755
void ClearCasePlugin::checkAndReIndexUnknownFile(const QString &file)
{
    if (isDynamic()) {
        // reindex unknown files
        if (m_statusMap->value(file, FileStatus(FileStatus::Unknown)).status == FileStatus::Unknown)
            updateStatusForFile(file);
    }
}

// file must be absolute, and using '/' path separator
Orgad Shaneh's avatar
Orgad Shaneh committed
756 757
FileStatus ClearCasePlugin::vcsStatus(const QString &file) const
{
Orgad Shaneh's avatar
Orgad Shaneh committed
758
    return m_statusMap->value(file, FileStatus(FileStatus::Unknown));
Orgad Shaneh's avatar
Orgad Shaneh committed
759 760 761 762 763 764 765 766 767 768 769 770
}

QString ClearCasePlugin::ccGetFileActivity(const QString &workingDir, const QString &file)
{
    QStringList args(QLatin1String("lscheckout"));
    args << QLatin1String("-fmt") << QLatin1String("%[activity]p");
    args << file;
    const ClearCaseResponse response =
            runCleartool(workingDir, args, m_settings.timeOutMS(), SilentRun);
    return response.stdOut;
}

771
ClearCaseSubmitEditor *ClearCasePlugin::openClearCaseSubmitEditor(const QString &fileName, bool isUcm)
Orgad Shaneh's avatar
Orgad Shaneh committed
772
{
773 774
    IEditor *editor =
            EditorManager::openEditor(fileName, Constants::CLEARCASECHECKINEDITOR_ID);
Orgad Shaneh's avatar
Orgad Shaneh committed
775 776 777 778
    ClearCaseSubmitEditor *submitEditor = qobject_cast<ClearCaseSubmitEditor*>(editor);
    QTC_CHECK(submitEditor);
    submitEditor->registerActions(m_submitUndoAction, m_submitRedoAction, m_checkInSelectedAction, m_checkInDiffAction);
    connect(submitEditor, SIGNAL(diffSelectedFiles(QStringList)), this, SLOT(diffCheckInFiles(QStringList)));
779
    submitEditor->setCheckScriptWorkingDirectory(m_checkInView);
780
    submitEditor->setIsUcm(isUcm);
Orgad Shaneh's avatar
Orgad Shaneh committed
781 782 783
    return submitEditor;
}

784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804
QString fileStatusToText(FileStatus fileStatus)
{
    switch (fileStatus.status)
    {
    case FileStatus::CheckedIn:
        return QLatin1String("CheckedIn");
    case FileStatus::CheckedOut:
        return QLatin1String("CheckedOut");
    case FileStatus::Hijacked:
        return QLatin1String("Hijacked");
    case FileStatus::Missing:
        return QLatin1String("Missing");
    case FileStatus::NotManaged:
        return QLatin1String("ViewPrivate");
    case FileStatus::Unknown:
        return QLatin1String("Unknown");
    default:
        return QLatin1String("default");
    }
}

Orgad Shaneh's avatar
Orgad Shaneh committed
805 806
void ClearCasePlugin::updateStatusActions()
{
807
    FileStatus fileStatus = FileStatus::Unknown;
Orgad Shaneh's avatar
Orgad Shaneh committed
808
    bool hasFile = currentState().hasFile();
809
    if (hasFile) {
810
        QString absoluteFileName = currentState().currentFile();
811 812
        checkAndReIndexUnknownFile(absoluteFileName);
        fileStatus = vcsStatus(absoluteFileName);
813

814 815
        updateEditDerivedObjectWarning(absoluteFileName, fileStatus.status);

816
        if (Constants::debug)
817 818
            qDebug() << Q_FUNC_INFO << absoluteFileName << ", status = "
                     << fileStatusToText(fileStatus.status) << "(" << fileStatus.status << ")";
819
    }
820

Orgad Shaneh's avatar
Orgad Shaneh committed
821 822
    m_checkOutAction->setEnabled(hasFile && (fileStatus.status & (FileStatus::CheckedIn | FileStatus::Hijacked)));
    m_undoCheckOutAction->setEnabled(hasFile && (fileStatus.status & FileStatus::CheckedOut));
Orgad Shaneh's avatar
Orgad Shaneh committed
823
    m_undoHijackAction->setEnabled(!m_viewData.isDynamic && hasFile && (fileStatus.status & FileStatus::Hijacked));
Orgad Shaneh's avatar
Orgad Shaneh committed
824 825
    m_checkInCurrentAction->setEnabled(hasFile && (fileStatus.status & FileStatus::CheckedOut));
    m_addFileAction->setEnabled(hasFile && (fileStatus.status & FileStatus::NotManaged));
826 827 828
    m_diffCurrentAction->setEnabled(hasFile && (fileStatus.status != FileStatus::NotManaged));
    m_historyCurrentAction->setEnabled(hasFile && (fileStatus.status != FileStatus::NotManaged));
    m_annotateCurrentAction->setEnabled(hasFile && (fileStatus.status != FileStatus::NotManaged));
829

Orgad Shaneh's avatar
Orgad Shaneh committed
830 831
    m_checkInActivityAction->setEnabled(m_viewData.isUcm);
    m_diffActivityAction->setEnabled(m_viewData.isUcm);
Orgad Shaneh's avatar
Orgad Shaneh committed
832 833
}

834
void ClearCasePlugin::updateActions(VcsBasePlugin::ActionState as)
Orgad Shaneh's avatar
Orgad Shaneh committed
835 836 837 838 839
{
    if (!enableMenuAction(as, m_menuAction)) {
        m_commandLocator->setEnabled(false);
        return;
    }
840
    const VcsBasePluginState state = currentState();
Orgad Shaneh's avatar
Orgad Shaneh committed
841 842
    const bool hasTopLevel = state.hasTopLevel();
    m_commandLocator->setEnabled(hasTopLevel);
843 844 845 846 847 848 849
    if (hasTopLevel) {
        const QString topLevel = state.topLevel();
        if (m_topLevel != topLevel) {
            m_topLevel = topLevel;
            m_viewData = ccGetView(topLevel);
        }
    }
Orgad Shaneh's avatar
Orgad Shaneh committed
850

Orgad Shaneh's avatar
Orgad Shaneh committed
851
    m_updateViewAction->setParameter(m_viewData.isDynamic ? QString() : m_viewData.name);
Orgad Shaneh's avatar
Orgad Shaneh committed
852 853 854 855 856 857 858 859 860 861 862

    const QString fileName = state.currentFileName();
    m_checkOutAction->setParameter(fileName);
    m_undoCheckOutAction->setParameter(fileName);
    m_undoHijackAction->setParameter(fileName);
    m_diffCurrentAction->setParameter(fileName);
    m_checkInCurrentAction->setParameter(fileName);
    m_historyCurrentAction->setParameter(fileName);
    m_annotateCurrentAction->setParameter(fileName);
    m_addFileAction->setParameter(fileName);
    m_updateIndexAction->setEnabled(!m_settings.disableIndexer);
863

Orgad Shaneh's avatar
Orgad Shaneh committed
864 865 866 867 868
    updateStatusActions();
}

void ClearCasePlugin::checkOutCurrentFile()
{
869
    const VcsBasePluginState state = currentState();
Orgad Shaneh's avatar
Orgad Shaneh committed
870 871 872 873 874 875
    QTC_ASSERT(state.hasFile(), return);
    vcsOpen(state.currentFileTopLevel(), state.relativeCurrentFile());
}

void ClearCasePlugin::addCurrentFile()
{
876
    const VcsBasePluginState state = currentState();
Orgad Shaneh's avatar
Orgad Shaneh committed
877 878 879 880
    QTC_ASSERT(state.hasFile(), return);
    vcsAdd(state.currentFileTopLevel(), state.relativeCurrentFile());
}

881
// Set the FileStatus of file given in absolute path
Orgad Shaneh's avatar
Orgad Shaneh committed
882
void ClearCasePlugin::setStatus(const QString &file, FileStatus::Status status, bool update)
Orgad Shaneh's avatar
Orgad Shaneh committed
883
{
884
    QTC_CHECK(!file.isEmpty());
885 886 887
    m_statusMap->insert(file, FileStatus(status, QFileInfo(file).permissions()));

    if (update && currentState().currentFile() == file)
Orgad Shaneh's avatar
Orgad Shaneh committed
888
        QMetaObject::invokeMethod(this, "updateStatusActions");
Orgad Shaneh's avatar
Orgad Shaneh committed
889 890 891 892
}

void ClearCasePlugin::undoCheckOutCurrent()
{
893
    const VcsBasePluginState state = currentState();
Orgad Shaneh's avatar
Orgad Shaneh committed
894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910
    QTC_ASSERT(state.hasFile(), return);
    QString file = state.relativeCurrentFile();
    const QString fileName = QDir::toNativeSeparators(file);

    QStringList args(QLatin1String("diff"));
    args << QLatin1String("-diff_format") << QLatin1String("-predecessor");
    args << fileName;

    const ClearCaseResponse diffResponse =
            runCleartool(state.currentFileTopLevel(), args, m_settings.timeOutMS(), 0);

    bool different = diffResponse.error; // return value is 1 if there is any difference
    bool keep = false;
    if (different) {
        Ui::UndoCheckOut uncoUi;
        QDialog uncoDlg;
        uncoUi.setupUi(&uncoDlg);
911
        uncoUi.lblMessage->setText(tr("Do you want to undo the check out of \"%1\"?").arg(fileName));
Orgad Shaneh's avatar
Orgad Shaneh committed
912 913 914 915 916 917 918 919 920
        if (uncoDlg.exec() != QDialog::Accepted)
            return;
        keep = uncoUi.chkKeep->isChecked();
    }
    vcsUndoCheckOut(state.topLevel(), file, keep);
}

bool ClearCasePlugin::vcsUndoCheckOut(const QString &workingDir, const QString &fileName, bool keep)
{
921
    if (Constants::debug)
Orgad Shaneh's avatar
Orgad Shaneh committed
922 923
        qDebug() << Q_FUNC_INFO << workingDir << fileName << keep;

924
    FileChangeBlocker fcb(fileName);
Orgad Shaneh's avatar
Orgad Shaneh committed
925 926 927 928 929 930 931 932 933 934 935

    // revert
    QStringList args(QLatin1String("uncheckout"));
    args << QLatin1String(keep ? "-keep" : "-rm");
    args << QDir::toNativeSeparators(fileName);

    const ClearCaseResponse response =
            runCleartool(workingDir, args, m_settings.timeOutMS(),
                         ShowStdOutInLogWindow | FullySynchronously);

    if (!response.error) {
936 937
        const QString absPath = workingDir + QLatin1Char('/') + fileName;

Orgad Shaneh's avatar
Orgad Shaneh committed
938
        if (!m_settings.disableIndexer)
939 940
            setStatus(absPath, FileStatus::CheckedIn);
        clearCaseControl()->emitFilesChanged(QStringList(absPath));
Orgad Shaneh's avatar
Orgad Shaneh committed
941 942 943 944
    }
    return !response.error;
}

945 946 947 948 949 950

/*! Undo a hijacked file in a snapshot view
 *
 * Runs cleartool update -overwrite \a fileName in \a workingDir
 * if \a keep is true, renames hijacked files to <filename>.keep. Otherwise it is overwritten
 */
Orgad Shaneh's avatar
Orgad Shaneh committed
951 952
bool ClearCasePlugin::vcsUndoHijack(const QString &workingDir, const QString &fileName, bool keep)
{
953
    if (Constants::debug)
Orgad Shaneh's avatar
Orgad Shaneh committed
954 955 956 957
        qDebug() << Q_FUNC_INFO << workingDir << fileName << keep;
    QStringList args(QLatin1String("update"));
    args << QLatin1String(keep ? "-rename" : "-overwrite");
    args << QLatin1String("-log");
958
    if (HostOsInfo::isWindowsHost())
959 960
        args << QLatin1String("NUL");
    else
Orgad Shaneh's avatar
Orgad Shaneh committed
961 962 963 964 965 966
    args << QLatin1String("/dev/null");
    args << QDir::toNativeSeparators(fileName);

    const ClearCaseResponse response =
            runCleartool(workingDir, args, m_settings.timeOutMS(),
                   ShowStdOutInLogWindow | FullySynchronously);
967 968 969 970
    if (!response.error && !m_settings.disableIndexer) {
        const QString absPath = workingDir + QLatin1Char('/') + fileName;
        setStatus(absPath, FileStatus::CheckedIn);
    }
Orgad Shaneh's avatar
Orgad Shaneh committed
971 972 973 974 975
    return !response.error;
}

void ClearCasePlugin::undoHijackCurrent()
{
976
    const VcsBasePluginState state = currentState();
Orgad Shaneh's avatar
Orgad Shaneh committed
977 978 979 980
    QTC_ASSERT(state.hasFile(), return);
    const QString fileName = state.relativeCurrentFile();

    bool keep = false;
981 982 983 984 985 986 987
    bool askKeep = true;
    if (m_settings.extDiffAvailable) {
        QString diffres = diffExternal(ccGetFileVersion(state.topLevel(), fileName), fileName);
        if (diffres.at(0) == QLatin1Char('F')) // Files are identical
            askKeep = false;
    }
    if (askKeep) {
Orgad Shaneh's avatar
Orgad Shaneh committed
988 989 990
        Ui::UndoCheckOut unhijackUi;
        QDialog unhijackDlg;
        unhijackUi.setupUi(&unhijackDlg);
991
        unhijackDlg.setWindowTitle(tr("Undo Hijack File"));
992
        unhijackUi.lblMessage->setText(tr("Do you want to undo hijack of \"%1\"?")
Orgad Shaneh's avatar
Orgad Shaneh committed
993 994 995 996 997 998
                                       .arg(QDir::toNativeSeparators(fileName)));
        if (unhijackDlg.exec() != QDialog::Accepted)
            return;
        keep = unhijackUi.chkKeep->isChecked();
    }

999
    FileChangeBlocker fcb(state.currentFile());
Orgad Shaneh's avatar
Orgad Shaneh committed
1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012

    // revert
    if (vcsUndoHijack(state.currentFileTopLevel(), fileName, keep))
        clearCaseControl()->emitFilesChanged(QStringList(state.currentFile()));
}

QString ClearCasePlugin::ccGetFileVersion(const QString &workingDir, const QString &file) const
{
    QStringList args(QLatin1String("ls"));
    args << QLatin1String("-short") << file;
    return runCleartoolSync(workingDir, args).trimmed();
}

1013
void ClearCasePlugin::ccDiffWithPred(const QString &workingDir, const QStringList &files)
Orgad Shaneh's avatar
Orgad Shaneh committed
1014
{
1015
    if (Constants::debug)
Orgad Shaneh's avatar
Orgad Shaneh committed
1016
        qDebug() << Q_FUNC_INFO << files;
1017 1018
    const QString source = VcsBaseEditor::getSource(workingDir, files);
    QTextCodec *codec = source.isEmpty() ? static_cast<QTextCodec *>(0) : VcsBaseEditor::getCodec(source);
Orgad Shaneh's avatar
Orgad Shaneh committed
1019 1020

    if ((m_settings.diffType == GraphicalDiff) && (files.count() == 1)) {
1021 1022
        const QString file = files.first();
        const QString absFilePath = workingDir + QLatin1Char('/') + file;
1023
        if (vcsStatus(absFilePath).status == FileStatus::Hijacked)
1024
            diffGraphical(ccGetFileVersion(workingDir, file), file);
Orgad Shaneh's avatar
Orgad Shaneh committed
1025 1026 1027 1028
        else
            diffGraphical(file);
        return; // done here, diff is opened in a new window
    }
1029
    if (!m_settings.extDiffAvailable) {
1030
        VcsOutputWindow::appendError(tr("External diff is required to compare multiple files."));
Orgad Shaneh's avatar