forked from qt-creator/qt-creator
		
	
		
			
				
	
	
		
			804 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			804 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/**************************************************************************
 | 
						|
**
 | 
						|
** This file is part of Qt Creator
 | 
						|
**
 | 
						|
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
 | 
						|
**
 | 
						|
** Contact: Nokia Corporation (qt-info@nokia.com)
 | 
						|
**
 | 
						|
** Commercial Usage
 | 
						|
**
 | 
						|
** Licensees holding valid Qt Commercial licenses may use this file in
 | 
						|
** accordance with the Qt Commercial License Agreement provided with the
 | 
						|
** Software or, alternatively, in accordance with the terms contained in
 | 
						|
** a written agreement between you and Nokia.
 | 
						|
**
 | 
						|
** GNU Lesser General Public License Usage
 | 
						|
**
 | 
						|
** 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.
 | 
						|
**
 | 
						|
** If you are unsure which license is appropriate for your use, please
 | 
						|
** contact the sales department at http://qt.nokia.com/contact.
 | 
						|
**
 | 
						|
**************************************************************************/
 | 
						|
 | 
						|
#include "vcsbaseplugin.h"
 | 
						|
#include "vcsbasesubmiteditor.h"
 | 
						|
#include "vcsplugin.h"
 | 
						|
#include "commonvcssettings.h"
 | 
						|
#include "vcsbaseoutputwindow.h"
 | 
						|
#include "corelistener.h"
 | 
						|
 | 
						|
#include <coreplugin/icore.h>
 | 
						|
#include <coreplugin/ifile.h>
 | 
						|
#include <coreplugin/iversioncontrol.h>
 | 
						|
#include <coreplugin/filemanager.h>
 | 
						|
#include <coreplugin/editormanager/editormanager.h>
 | 
						|
#include <coreplugin/editormanager/ieditor.h>
 | 
						|
#include <coreplugin/vcsmanager.h>
 | 
						|
#include <projectexplorer/projectexplorer.h>
 | 
						|
#include <projectexplorer/project.h>
 | 
						|
#include <utils/qtcassert.h>
 | 
						|
#include <utils/synchronousprocess.h>
 | 
						|
 | 
						|
#include <QtCore/QDebug>
 | 
						|
#include <QtCore/QDir>
 | 
						|
#include <QtCore/QSharedData>
 | 
						|
#include <QtCore/QScopedPointer>
 | 
						|
#include <QtCore/QProcessEnvironment>
 | 
						|
#include <QtCore/QTextStream>
 | 
						|
#include <QtCore/QTextCodec>
 | 
						|
 | 
						|
#include <QtGui/QAction>
 | 
						|
#include <QtGui/QMessageBox>
 | 
						|
#include <QtGui/QFileDialog>
 | 
						|
#include <QtGui/QMainWindow>
 | 
						|
 | 
						|
enum { debug = 0, debugRepositorySearch = 0, debugExecution = 0 };
 | 
						|
 | 
						|
namespace VCSBase {
 | 
						|
 | 
						|
namespace Internal {
 | 
						|
 | 
						|
// Internal state created by the state listener and
 | 
						|
// VCSBasePluginState.
 | 
						|
 | 
						|
struct State {
 | 
						|
    void clearFile();
 | 
						|
    void clearPatchFile();
 | 
						|
    void clearProject();
 | 
						|
    inline void clear();
 | 
						|
 | 
						|
    bool equals(const State &rhs) const;
 | 
						|
 | 
						|
    inline bool hasFile() const     { return !currentFile.isEmpty(); }
 | 
						|
    inline bool hasProject() const  { return !currentProjectPath.isEmpty(); }
 | 
						|
    inline bool isEmpty() const     { return !hasFile() && !hasProject(); }
 | 
						|
 | 
						|
    QString currentFile;
 | 
						|
    QString currentFileName;
 | 
						|
    QString currentPatchFile;
 | 
						|
    QString currentPatchFileDisplayName;
 | 
						|
 | 
						|
    QString currentFileDirectory;
 | 
						|
    QString currentFileTopLevel;
 | 
						|
 | 
						|
    QString currentProjectPath;
 | 
						|
    QString currentProjectName;
 | 
						|
    QString currentProjectTopLevel;
 | 
						|
};
 | 
						|
 | 
						|
void State::clearFile()
 | 
						|
{
 | 
						|
    currentFile.clear();
 | 
						|
    currentFileName.clear();
 | 
						|
    currentFileDirectory.clear();
 | 
						|
    currentFileTopLevel.clear();
 | 
						|
}
 | 
						|
 | 
						|
void State::clearPatchFile()
 | 
						|
{
 | 
						|
    currentPatchFile.clear();
 | 
						|
    currentPatchFileDisplayName.clear();
 | 
						|
}
 | 
						|
 | 
						|
void State::clearProject()
 | 
						|
{
 | 
						|
    currentProjectPath.clear();
 | 
						|
    currentProjectName.clear();
 | 
						|
    currentProjectTopLevel.clear();
 | 
						|
}
 | 
						|
 | 
						|
void State::clear()
 | 
						|
{
 | 
						|
    clearFile();
 | 
						|
    clearPatchFile();
 | 
						|
    clearProject();
 | 
						|
}
 | 
						|
 | 
						|
bool State::equals(const State &rhs) const
 | 
						|
{
 | 
						|
    return currentFile == rhs.currentFile
 | 
						|
            && currentFileName == rhs.currentFileName
 | 
						|
            && currentPatchFile == rhs.currentPatchFile
 | 
						|
            && currentFileTopLevel == rhs.currentFileTopLevel
 | 
						|
            && currentProjectPath == rhs.currentProjectPath
 | 
						|
            && currentProjectName == rhs.currentProjectName
 | 
						|
            && currentProjectTopLevel == rhs.currentProjectTopLevel;
 | 
						|
}
 | 
						|
 | 
						|
QDebug operator<<(QDebug in, const State &state)
 | 
						|
{
 | 
						|
    QDebug nospace = in.nospace();
 | 
						|
    nospace << "State: ";
 | 
						|
    if (state.isEmpty()) {
 | 
						|
        nospace << "<empty>";
 | 
						|
    } else {
 | 
						|
        if (state.hasFile()) {
 | 
						|
            nospace << "File=" << state.currentFile
 | 
						|
                    << ',' << state.currentFileTopLevel;
 | 
						|
        } else {
 | 
						|
            nospace << "<no file>";
 | 
						|
        }
 | 
						|
        nospace << '\n';
 | 
						|
        if (state.hasProject()) {
 | 
						|
            nospace << "       Project=" << state.currentProjectName
 | 
						|
            << ',' << state.currentProjectPath
 | 
						|
            << ',' << state.currentProjectTopLevel;
 | 
						|
 | 
						|
        } else {
 | 
						|
            nospace << "<no project>";
 | 
						|
        }
 | 
						|
        nospace << '\n';
 | 
						|
    }
 | 
						|
    return in;
 | 
						|
}
 | 
						|
 | 
						|
// StateListener: Connects to the relevant signals, tries to find version
 | 
						|
// controls and emits signals to the plugin instances.
 | 
						|
 | 
						|
class StateListener : public QObject {
 | 
						|
    Q_OBJECT
 | 
						|
public:
 | 
						|
    explicit StateListener(QObject *parent);
 | 
						|
 | 
						|
signals:
 | 
						|
    void stateChanged(const VCSBase::Internal::State &s, Core::IVersionControl *vc);
 | 
						|
 | 
						|
public slots:
 | 
						|
    void slotStateChanged();
 | 
						|
};
 | 
						|
 | 
						|
StateListener::StateListener(QObject *parent) :
 | 
						|
        QObject(parent)
 | 
						|
{
 | 
						|
    Core::ICore *core = Core::ICore::instance();
 | 
						|
    connect(core->fileManager(), SIGNAL(currentFileChanged(QString)),
 | 
						|
            this, SLOT(slotStateChanged()));
 | 
						|
 | 
						|
    if (ProjectExplorer::ProjectExplorerPlugin *pe = ProjectExplorer::ProjectExplorerPlugin::instance())
 | 
						|
        connect(pe, SIGNAL(currentProjectChanged(ProjectExplorer::Project*)),
 | 
						|
                this, SLOT(slotStateChanged()));
 | 
						|
}
 | 
						|
 | 
						|
static inline QString displayNameOfEditor(const QString &fileName)
 | 
						|
{
 | 
						|
    const QList<Core::IEditor*> editors = Core::EditorManager::instance()->editorsForFileName(fileName);
 | 
						|
    if (!editors.isEmpty())
 | 
						|
        return editors.front()->displayName();
 | 
						|
    return QString();
 | 
						|
}
 | 
						|
 | 
						|
void StateListener::slotStateChanged()
 | 
						|
{
 | 
						|
    const ProjectExplorer::ProjectExplorerPlugin *pe = ProjectExplorer::ProjectExplorerPlugin::instance();
 | 
						|
    const Core::ICore *core = Core::ICore::instance();
 | 
						|
    Core::VCSManager *vcsManager = core->vcsManager();
 | 
						|
 | 
						|
    // Get the current file. Are we on a temporary submit editor indicated by
 | 
						|
    // temporary path prefix or does the file contains a hash, indicating a project
 | 
						|
    // folder?
 | 
						|
    State state;
 | 
						|
    state.currentFile = core->fileManager()->currentFile();
 | 
						|
    QScopedPointer<QFileInfo> currentFileInfo; // Instantiate QFileInfo only once if required.
 | 
						|
    if (!state.currentFile.isEmpty()) {
 | 
						|
        const bool isTempFile = state.currentFile.startsWith(QDir::tempPath());
 | 
						|
        // Quick check: Does it look like a patch?
 | 
						|
        const bool isPatch = state.currentFile.endsWith(QLatin1String(".patch"))
 | 
						|
                             || state.currentFile.endsWith(QLatin1String(".diff"));
 | 
						|
        if (isPatch) {
 | 
						|
            // Patch: Figure out a name to display. If it is a temp file, it could be
 | 
						|
            // Codepaster. Use the display name of the editor.
 | 
						|
            state.currentPatchFile = state.currentFile;
 | 
						|
            if (isTempFile)
 | 
						|
                state.currentPatchFileDisplayName = displayNameOfEditor(state.currentPatchFile);
 | 
						|
            if (state.currentPatchFileDisplayName.isEmpty()) {
 | 
						|
                currentFileInfo.reset(new QFileInfo(state.currentFile));
 | 
						|
                state.currentPatchFileDisplayName = currentFileInfo->fileName();
 | 
						|
            }
 | 
						|
        }
 | 
						|
        // For actual version control operations on it:
 | 
						|
        // Do not show temporary files and project folders ('#')
 | 
						|
        if (isTempFile || state.currentFile.contains(QLatin1Char('#')))
 | 
						|
            state.currentFile.clear();
 | 
						|
    }
 | 
						|
    // Get the file and its control. Do not use the file unless we find one
 | 
						|
    Core::IVersionControl *fileControl = 0;
 | 
						|
    if (!state.currentFile.isEmpty()) {
 | 
						|
        if (currentFileInfo.isNull())
 | 
						|
            currentFileInfo.reset(new QFileInfo(state.currentFile));
 | 
						|
        state.currentFileDirectory = currentFileInfo->absolutePath();
 | 
						|
        state.currentFileName = currentFileInfo->fileName();
 | 
						|
        fileControl = vcsManager->findVersionControlForDirectory(state.currentFileDirectory,
 | 
						|
                                                                 &state.currentFileTopLevel);
 | 
						|
        if (!fileControl)
 | 
						|
            state.clearFile();
 | 
						|
    }
 | 
						|
    // Check for project, find the control
 | 
						|
    Core::IVersionControl *projectControl = 0;
 | 
						|
    if (const ProjectExplorer::Project *currentProject = pe->currentProject()) {
 | 
						|
        state.currentProjectPath = currentProject->projectDirectory();
 | 
						|
        state.currentProjectName = currentProject->displayName();
 | 
						|
        projectControl = vcsManager->findVersionControlForDirectory(state.currentProjectPath,
 | 
						|
                                                                    &state.currentProjectTopLevel);
 | 
						|
        if (projectControl) {
 | 
						|
            // If we have both, let the file's one take preference
 | 
						|
            if (fileControl && projectControl != fileControl) {
 | 
						|
                state.clearProject();
 | 
						|
            }
 | 
						|
        } else {
 | 
						|
            state.clearProject(); // No control found
 | 
						|
        }
 | 
						|
    }
 | 
						|
    // Assemble state and emit signal.
 | 
						|
    Core::IVersionControl *vc = state.currentFile.isEmpty() ? projectControl : fileControl;
 | 
						|
    if (!vc) // Need a repository to patch
 | 
						|
        state.clearPatchFile();
 | 
						|
    if (debug)
 | 
						|
        qDebug() << state << (vc ? vc->displayName() : QString(QLatin1String("No version control")));
 | 
						|
    emit stateChanged(state, vc);
 | 
						|
}
 | 
						|
 | 
						|
} // namespace Internal
 | 
						|
 | 
						|
class VCSBasePluginStateData : public QSharedData {
 | 
						|
public:
 | 
						|
    Internal::State m_state;
 | 
						|
};
 | 
						|
 | 
						|
VCSBasePluginState::VCSBasePluginState() : data(new VCSBasePluginStateData)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
VCSBasePluginState::VCSBasePluginState(const VCSBasePluginState &rhs) : data(rhs.data)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
VCSBasePluginState &VCSBasePluginState::operator=(const VCSBasePluginState &rhs)
 | 
						|
{
 | 
						|
    if (this != &rhs)
 | 
						|
        data.operator=(rhs.data);
 | 
						|
    return *this;
 | 
						|
}
 | 
						|
 | 
						|
VCSBasePluginState::~VCSBasePluginState()
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
QString VCSBasePluginState::currentFile() const
 | 
						|
{
 | 
						|
    return data->m_state.currentFile;
 | 
						|
}
 | 
						|
 | 
						|
QString VCSBasePluginState::currentFileName() const
 | 
						|
{
 | 
						|
    return data->m_state.currentFileName;
 | 
						|
}
 | 
						|
 | 
						|
QString VCSBasePluginState::currentFileTopLevel() const
 | 
						|
{
 | 
						|
    return data->m_state.currentFileTopLevel;
 | 
						|
}
 | 
						|
 | 
						|
QString VCSBasePluginState::currentFileDirectory() const
 | 
						|
{
 | 
						|
    return data->m_state.currentFileDirectory;
 | 
						|
}
 | 
						|
 | 
						|
QString VCSBasePluginState::relativeCurrentFile() const
 | 
						|
{
 | 
						|
    QTC_ASSERT(hasFile(), return QString())
 | 
						|
    return QDir(data->m_state.currentFileTopLevel).relativeFilePath(data->m_state.currentFile);
 | 
						|
}
 | 
						|
 | 
						|
QString VCSBasePluginState::currentPatchFile() const
 | 
						|
{
 | 
						|
    return data->m_state.currentPatchFile;
 | 
						|
}
 | 
						|
 | 
						|
QString VCSBasePluginState::currentPatchFileDisplayName() const
 | 
						|
{
 | 
						|
    return data->m_state.currentPatchFileDisplayName;
 | 
						|
}
 | 
						|
 | 
						|
QString VCSBasePluginState::currentProjectPath() const
 | 
						|
{
 | 
						|
    return data->m_state.currentProjectPath;
 | 
						|
}
 | 
						|
 | 
						|
QString VCSBasePluginState::currentProjectName() const
 | 
						|
{
 | 
						|
    return data->m_state.currentProjectName;
 | 
						|
}
 | 
						|
 | 
						|
QString VCSBasePluginState::currentProjectTopLevel() const
 | 
						|
{
 | 
						|
    return data->m_state.currentProjectTopLevel;
 | 
						|
}
 | 
						|
 | 
						|
QStringList VCSBasePluginState::relativeCurrentProject() const
 | 
						|
{
 | 
						|
    QStringList rc;
 | 
						|
    QTC_ASSERT(hasProject(), return rc)
 | 
						|
    if (data->m_state.currentProjectTopLevel != data->m_state.currentProjectPath)
 | 
						|
        rc.append(QDir(data->m_state.currentProjectTopLevel).relativeFilePath(data->m_state.currentProjectPath));
 | 
						|
    return rc;
 | 
						|
}
 | 
						|
 | 
						|
bool VCSBasePluginState::hasTopLevel() const
 | 
						|
{
 | 
						|
    return data->m_state.hasFile() || data->m_state.hasProject();
 | 
						|
}
 | 
						|
 | 
						|
QString VCSBasePluginState::topLevel() const
 | 
						|
{
 | 
						|
    return hasFile() ? data->m_state.currentFileTopLevel : data->m_state.currentProjectTopLevel;
 | 
						|
}
 | 
						|
 | 
						|
bool VCSBasePluginState::equals(const Internal::State &rhs) const
 | 
						|
{
 | 
						|
    return data->m_state.equals(rhs);
 | 
						|
}
 | 
						|
 | 
						|
bool VCSBasePluginState::equals(const VCSBasePluginState &rhs) const
 | 
						|
{
 | 
						|
    return equals(rhs.data->m_state);
 | 
						|
}
 | 
						|
 | 
						|
void VCSBasePluginState::clear()
 | 
						|
{
 | 
						|
    data->m_state.clear();
 | 
						|
}
 | 
						|
 | 
						|
void VCSBasePluginState::setState(const Internal::State &s)
 | 
						|
{
 | 
						|
    data->m_state = s;
 | 
						|
}
 | 
						|
 | 
						|
bool VCSBasePluginState::isEmpty() const
 | 
						|
{
 | 
						|
    return data->m_state.isEmpty();
 | 
						|
}
 | 
						|
 | 
						|
bool VCSBasePluginState::hasFile() const
 | 
						|
{
 | 
						|
    return data->m_state.hasFile();
 | 
						|
}
 | 
						|
 | 
						|
bool VCSBasePluginState::hasPatchFile() const
 | 
						|
{
 | 
						|
    return !data->m_state.currentPatchFile.isEmpty();
 | 
						|
}
 | 
						|
 | 
						|
bool VCSBasePluginState::hasProject() const
 | 
						|
{
 | 
						|
    return data->m_state.hasProject();
 | 
						|
}
 | 
						|
 | 
						|
VCSBASE_EXPORT QDebug operator<<(QDebug in, const VCSBasePluginState &state)
 | 
						|
{
 | 
						|
    in << state.data->m_state;
 | 
						|
    return in;
 | 
						|
}
 | 
						|
 | 
						|
//  VCSBasePlugin
 | 
						|
struct VCSBasePluginPrivate {
 | 
						|
    explicit VCSBasePluginPrivate(const QString &submitEditorId);
 | 
						|
 | 
						|
    inline bool supportsRepositoryCreation() const;
 | 
						|
 | 
						|
    const QString m_submitEditorId;
 | 
						|
    Core::IVersionControl *m_versionControl;
 | 
						|
    VCSBasePluginState m_state;
 | 
						|
    int m_actionState;
 | 
						|
    QAction *m_testSnapshotAction;
 | 
						|
    QAction *m_testListSnapshotsAction;
 | 
						|
    QAction *m_testRestoreSnapshotAction;
 | 
						|
    QAction *m_testRemoveSnapshotAction;
 | 
						|
    QString m_testLastSnapshot;
 | 
						|
 | 
						|
    static Internal::StateListener *m_listener;
 | 
						|
};
 | 
						|
 | 
						|
VCSBasePluginPrivate::VCSBasePluginPrivate(const QString &submitEditorId) :
 | 
						|
    m_submitEditorId(submitEditorId),
 | 
						|
    m_versionControl(0),
 | 
						|
    m_actionState(-1),
 | 
						|
    m_testSnapshotAction(0),
 | 
						|
    m_testListSnapshotsAction(0),
 | 
						|
    m_testRestoreSnapshotAction(0),
 | 
						|
    m_testRemoveSnapshotAction(0)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
bool VCSBasePluginPrivate::supportsRepositoryCreation() const
 | 
						|
{
 | 
						|
    return m_versionControl && m_versionControl->supportsOperation(Core::IVersionControl::CreateRepositoryOperation);
 | 
						|
}
 | 
						|
 | 
						|
Internal::StateListener *VCSBasePluginPrivate::m_listener = 0;
 | 
						|
 | 
						|
VCSBasePlugin::VCSBasePlugin(const QString &submitEditorId) :
 | 
						|
    d(new VCSBasePluginPrivate(submitEditorId))
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
VCSBasePlugin::~VCSBasePlugin()
 | 
						|
{
 | 
						|
    delete d;
 | 
						|
}
 | 
						|
 | 
						|
void VCSBasePlugin::initialize(Core::IVersionControl *vc)
 | 
						|
{
 | 
						|
    d->m_versionControl = vc;
 | 
						|
    addAutoReleasedObject(vc);
 | 
						|
 | 
						|
    Internal::VCSPlugin *plugin = Internal::VCSPlugin::instance();
 | 
						|
    connect(plugin->coreListener(), SIGNAL(submitEditorAboutToClose(VCSBaseSubmitEditor*,bool*)),
 | 
						|
            this, SLOT(slotSubmitEditorAboutToClose(VCSBaseSubmitEditor*,bool*)));
 | 
						|
    // First time: create new listener
 | 
						|
    if (!VCSBasePluginPrivate::m_listener)
 | 
						|
        VCSBasePluginPrivate::m_listener = new Internal::StateListener(plugin);
 | 
						|
    connect(VCSBasePluginPrivate::m_listener,
 | 
						|
            SIGNAL(stateChanged(VCSBase::Internal::State, Core::IVersionControl*)),
 | 
						|
            this,
 | 
						|
            SLOT(slotStateChanged(VCSBase::Internal::State,Core::IVersionControl*)));
 | 
						|
}
 | 
						|
 | 
						|
void VCSBasePlugin::extensionsInitialized()
 | 
						|
{
 | 
						|
    // Initialize enable menus.
 | 
						|
    VCSBasePluginPrivate::m_listener->slotStateChanged();
 | 
						|
}
 | 
						|
 | 
						|
void VCSBasePlugin::slotSubmitEditorAboutToClose(VCSBaseSubmitEditor *submitEditor, bool *result)
 | 
						|
{
 | 
						|
    if (debug)
 | 
						|
        qDebug() << this << d->m_submitEditorId << "Closing submit editor" << submitEditor << submitEditor->id();
 | 
						|
    if (submitEditor->id() == d->m_submitEditorId)
 | 
						|
        *result = submitEditorAboutToClose(submitEditor);
 | 
						|
}
 | 
						|
 | 
						|
Core::IVersionControl *VCSBasePlugin::versionControl() const
 | 
						|
{
 | 
						|
    return d->m_versionControl;
 | 
						|
}
 | 
						|
 | 
						|
void VCSBasePlugin::slotStateChanged(const VCSBase::Internal::State &newInternalState, Core::IVersionControl *vc)
 | 
						|
{
 | 
						|
    if (vc == d->m_versionControl) {
 | 
						|
        // We are directly affected: Change state
 | 
						|
        if (!d->m_state.equals(newInternalState)) {
 | 
						|
            d->m_state.setState(newInternalState);
 | 
						|
            updateActions(VCSEnabled);
 | 
						|
        }
 | 
						|
    } else {
 | 
						|
        // Some other VCS plugin or state changed: Reset us to empty state.
 | 
						|
        const ActionState newActionState = vc ? OtherVCSEnabled : NoVCSEnabled;
 | 
						|
        if (d->m_actionState != newActionState || !d->m_state.isEmpty()) {
 | 
						|
            d->m_actionState = newActionState;
 | 
						|
            const VCSBasePluginState emptyState;
 | 
						|
            d->m_state = emptyState;
 | 
						|
            updateActions(newActionState);
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
const VCSBasePluginState &VCSBasePlugin::currentState() const
 | 
						|
{
 | 
						|
    return d->m_state;
 | 
						|
}
 | 
						|
 | 
						|
bool VCSBasePlugin::enableMenuAction(ActionState as, QAction *menuAction) const
 | 
						|
{
 | 
						|
    if (debug)
 | 
						|
        qDebug() << "enableMenuAction" << menuAction->text() << as;
 | 
						|
    switch (as) {
 | 
						|
    case VCSBase::VCSBasePlugin::NoVCSEnabled: {
 | 
						|
        const bool supportsCreation = d->supportsRepositoryCreation();
 | 
						|
        menuAction->setVisible(supportsCreation);
 | 
						|
        menuAction->setEnabled(supportsCreation);
 | 
						|
        return supportsCreation;
 | 
						|
    }
 | 
						|
    case VCSBase::VCSBasePlugin::OtherVCSEnabled:
 | 
						|
        menuAction->setVisible(false);
 | 
						|
        return false;
 | 
						|
    case VCSBase::VCSBasePlugin::VCSEnabled:
 | 
						|
        menuAction->setVisible(true);
 | 
						|
        menuAction->setEnabled(true);
 | 
						|
        break;
 | 
						|
    }
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
void VCSBasePlugin::promptToDeleteCurrentFile()
 | 
						|
{
 | 
						|
    const VCSBasePluginState state = currentState();
 | 
						|
    QTC_ASSERT(state.hasFile(), return)
 | 
						|
    const bool rc = Core::ICore::instance()->vcsManager()->promptToDelete(versionControl(), state.currentFile());
 | 
						|
    if (!rc)
 | 
						|
        QMessageBox::warning(0, tr("Version Control"),
 | 
						|
                             tr("The file '%1' could not be deleted.").
 | 
						|
                             arg(QDir::toNativeSeparators(state.currentFile())),
 | 
						|
                             QMessageBox::Ok);
 | 
						|
}
 | 
						|
 | 
						|
static inline bool ask(QWidget *parent, const QString &title, const QString &question, bool defaultValue = true)
 | 
						|
 | 
						|
{
 | 
						|
    const QMessageBox::StandardButton defaultButton = defaultValue ? QMessageBox::Yes : QMessageBox::No;
 | 
						|
    return QMessageBox::question(parent, title, question, QMessageBox::Yes|QMessageBox::No, defaultButton) == QMessageBox::Yes;
 | 
						|
}
 | 
						|
 | 
						|
void VCSBasePlugin::createRepository()
 | 
						|
{
 | 
						|
    QTC_ASSERT(d->m_versionControl->supportsOperation(Core::IVersionControl::CreateRepositoryOperation), return);
 | 
						|
    // Find current starting directory
 | 
						|
    QString directory;
 | 
						|
    if (const ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectExplorerPlugin::instance()->currentProject())
 | 
						|
        directory = QFileInfo(currentProject->file()->fileName()).absolutePath();
 | 
						|
    // Prompt for a directory that is not under version control yet
 | 
						|
    QMainWindow *mw = Core::ICore::instance()->mainWindow();
 | 
						|
    do {
 | 
						|
        directory = QFileDialog::getExistingDirectory(mw, tr("Choose Repository Directory"), directory);
 | 
						|
        if (directory.isEmpty())
 | 
						|
            return;
 | 
						|
        const Core::IVersionControl *managingControl = Core::ICore::instance()->vcsManager()->findVersionControlForDirectory(directory);
 | 
						|
        if (managingControl == 0)
 | 
						|
            break;
 | 
						|
        const QString question = tr("The directory '%1' is already managed by a version control system (%2)."
 | 
						|
                                    " Would you like to specify another directory?").arg(directory, managingControl->displayName());
 | 
						|
 | 
						|
        if (!ask(mw, tr("Repository already under version control"), question))
 | 
						|
            return;
 | 
						|
    } while (true);
 | 
						|
    // Create
 | 
						|
    const bool rc = d->m_versionControl->vcsCreateRepository(directory);
 | 
						|
    const QString nativeDir = QDir::toNativeSeparators(directory);
 | 
						|
    if (rc) {
 | 
						|
        QMessageBox::information(mw, tr("Repository created"),
 | 
						|
                                 tr("A version control repository has been created in %1.").
 | 
						|
                                 arg(nativeDir));
 | 
						|
    } else {
 | 
						|
        QMessageBox::warning(mw, tr("Repository creation failed"),
 | 
						|
                                 tr("A version control repository could not be created in %1.").
 | 
						|
                                 arg(nativeDir));
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// For internal tests: Create actions driving IVersionControl's snapshot interface.
 | 
						|
QList<QAction*> VCSBasePlugin::createSnapShotTestActions()
 | 
						|
{
 | 
						|
    if (!d->m_testSnapshotAction) {
 | 
						|
        d->m_testSnapshotAction = new QAction(QLatin1String("Take snapshot"), this);
 | 
						|
        connect(d->m_testSnapshotAction, SIGNAL(triggered()), this, SLOT(slotTestSnapshot()));
 | 
						|
        d->m_testListSnapshotsAction = new QAction(QLatin1String("List snapshots"), this);
 | 
						|
        connect(d->m_testListSnapshotsAction, SIGNAL(triggered()), this, SLOT(slotTestListSnapshots()));
 | 
						|
        d->m_testRestoreSnapshotAction = new QAction(QLatin1String("Restore snapshot"), this);
 | 
						|
        connect(d->m_testRestoreSnapshotAction, SIGNAL(triggered()), this, SLOT(slotTestRestoreSnapshot()));
 | 
						|
        d->m_testRemoveSnapshotAction = new QAction(QLatin1String("Remove snapshot"), this);
 | 
						|
        connect(d->m_testRemoveSnapshotAction, SIGNAL(triggered()), this, SLOT(slotTestRemoveSnapshot()));
 | 
						|
    }
 | 
						|
    QList<QAction*> rc;
 | 
						|
    rc << d->m_testSnapshotAction << d->m_testListSnapshotsAction << d->m_testRestoreSnapshotAction
 | 
						|
       << d->m_testRemoveSnapshotAction;
 | 
						|
    return rc;
 | 
						|
}
 | 
						|
 | 
						|
void VCSBasePlugin::slotTestSnapshot()
 | 
						|
{
 | 
						|
    QTC_ASSERT(currentState().hasTopLevel(), return)
 | 
						|
    d->m_testLastSnapshot = versionControl()->vcsCreateSnapshot(currentState().topLevel());
 | 
						|
    qDebug() << "Snapshot " << d->m_testLastSnapshot;
 | 
						|
    VCSBaseOutputWindow::instance()->append(QLatin1String("Snapshot: ") + d->m_testLastSnapshot);
 | 
						|
    if (!d->m_testLastSnapshot.isEmpty())
 | 
						|
        d->m_testRestoreSnapshotAction->setText(QLatin1String("Restore snapshot ") + d->m_testLastSnapshot);
 | 
						|
}
 | 
						|
 | 
						|
void VCSBasePlugin::slotTestListSnapshots()
 | 
						|
{
 | 
						|
    QTC_ASSERT(currentState().hasTopLevel(), return)
 | 
						|
    const QStringList snapshots = versionControl()->vcsSnapshots(currentState().topLevel());
 | 
						|
    qDebug() << "Snapshots " << snapshots;
 | 
						|
    VCSBaseOutputWindow::instance()->append(QLatin1String("Snapshots: ") + snapshots.join(QLatin1String(", ")));
 | 
						|
}
 | 
						|
 | 
						|
void VCSBasePlugin::slotTestRestoreSnapshot()
 | 
						|
{
 | 
						|
    QTC_ASSERT(currentState().hasTopLevel() && !d->m_testLastSnapshot.isEmpty(), return)
 | 
						|
    const bool ok = versionControl()->vcsRestoreSnapshot(currentState().topLevel(), d->m_testLastSnapshot);
 | 
						|
    const QString msg = d->m_testLastSnapshot+ (ok ? QLatin1String(" restored") : QLatin1String(" failed"));
 | 
						|
    qDebug() << msg;
 | 
						|
    VCSBaseOutputWindow::instance()->append(msg);
 | 
						|
}
 | 
						|
 | 
						|
void VCSBasePlugin::slotTestRemoveSnapshot()
 | 
						|
{
 | 
						|
    QTC_ASSERT(currentState().hasTopLevel() && !d->m_testLastSnapshot.isEmpty(), return)
 | 
						|
    const bool ok = versionControl()->vcsRemoveSnapshot(currentState().topLevel(), d->m_testLastSnapshot);
 | 
						|
    const QString msg = d->m_testLastSnapshot+ (ok ? QLatin1String(" removed") : QLatin1String(" failed"));
 | 
						|
    qDebug() << msg;
 | 
						|
    VCSBaseOutputWindow::instance()->append(msg);
 | 
						|
    d->m_testLastSnapshot.clear();
 | 
						|
}
 | 
						|
 | 
						|
// Find top level for version controls like git/Mercurial that have
 | 
						|
// a directory at the top of the repository.
 | 
						|
// Note that checking for the existence of files is preferred over directories
 | 
						|
// since checking for directories can cause them to be created when
 | 
						|
// AutoFS is used (due its automatically creating mountpoints when querying
 | 
						|
// a directory). In addition, bail out when reaching the home directory
 | 
						|
// of the user or root (generally avoid '/', where mountpoints are created).
 | 
						|
QString VCSBasePlugin::findRepositoryForDirectory(const QString &dirS,
 | 
						|
                                                  const QString &checkFile)
 | 
						|
{
 | 
						|
    if (debugRepositorySearch)
 | 
						|
        qDebug() << ">VCSBasePlugin::findRepositoryForDirectory" << dirS << checkFile;
 | 
						|
    QTC_ASSERT(!dirS.isEmpty() && !checkFile.isEmpty(), return QString());
 | 
						|
 | 
						|
    const QString root = QDir::rootPath();
 | 
						|
    const QString home = QDir::homePath();
 | 
						|
 | 
						|
    QDir directory(dirS);
 | 
						|
    do {
 | 
						|
        const QString absDirPath = directory.absolutePath();
 | 
						|
        if (absDirPath == root || absDirPath == home)
 | 
						|
            break;
 | 
						|
 | 
						|
        if (QFileInfo(directory, checkFile).isFile()) {
 | 
						|
            if (debugRepositorySearch)
 | 
						|
                qDebug() << "<VCSBasePlugin::findRepositoryForDirectory> " << absDirPath;
 | 
						|
            return absDirPath;
 | 
						|
        }
 | 
						|
    } while (directory.cdUp());
 | 
						|
    if (debugRepositorySearch)
 | 
						|
        qDebug() << "<VCSBasePlugin::findRepositoryForDirectory bailing out at " << directory.absolutePath();
 | 
						|
    return QString();
 | 
						|
}
 | 
						|
 | 
						|
// Is SSH prompt configured?
 | 
						|
static inline QString sshPrompt()
 | 
						|
{
 | 
						|
    return VCSBase::Internal::VCSPlugin::instance()->settings().sshPasswordPrompt;
 | 
						|
}
 | 
						|
 | 
						|
bool VCSBasePlugin::isSshPromptConfigured()
 | 
						|
{
 | 
						|
    return !sshPrompt().isEmpty();
 | 
						|
}
 | 
						|
 | 
						|
void VCSBasePlugin::setProcessEnvironment(QProcessEnvironment *e, bool forceCLocale)
 | 
						|
{
 | 
						|
    if (forceCLocale)
 | 
						|
        e->insert(QLatin1String("LANG"), QString(QLatin1Char('C')));
 | 
						|
    const QString sshPromptBinary = sshPrompt();
 | 
						|
    if (!sshPromptBinary.isEmpty())
 | 
						|
        e->insert(QLatin1String("SSH_ASKPASS"), sshPromptBinary);
 | 
						|
}
 | 
						|
 | 
						|
Utils::SynchronousProcessResponse
 | 
						|
        VCSBasePlugin::runVCS(const QString &workingDir,
 | 
						|
                              const QString &binary,
 | 
						|
                              const QStringList &arguments,
 | 
						|
                              int timeOutMS,
 | 
						|
                              unsigned flags,
 | 
						|
                              QTextCodec *outputCodec /* = 0 */)
 | 
						|
{
 | 
						|
    const QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
 | 
						|
    return runVCS(workingDir, binary, arguments, timeOutMS, env,
 | 
						|
                  flags, outputCodec);
 | 
						|
}
 | 
						|
 | 
						|
Utils::SynchronousProcessResponse
 | 
						|
        VCSBasePlugin::runVCS(const QString &workingDir,
 | 
						|
                              const QString &binary,
 | 
						|
                              const QStringList &arguments,
 | 
						|
                              int timeOutMS,
 | 
						|
                              QProcessEnvironment env,
 | 
						|
                              unsigned flags,
 | 
						|
                              QTextCodec *outputCodec /* = 0 */)
 | 
						|
{
 | 
						|
    VCSBase::VCSBaseOutputWindow *outputWindow = VCSBase::VCSBaseOutputWindow::instance();
 | 
						|
 | 
						|
    if (!(flags & SuppressCommandLogging))
 | 
						|
        outputWindow->appendCommand(workingDir, binary, arguments);
 | 
						|
 | 
						|
    const bool sshPromptConfigured = VCSBasePlugin::isSshPromptConfigured();
 | 
						|
    if (debugExecution) {
 | 
						|
        QDebug nsp = qDebug().nospace();
 | 
						|
        nsp << "VCSBasePlugin::runVCS" << workingDir << binary << arguments
 | 
						|
                << timeOutMS;
 | 
						|
        if (flags & ShowStdOutInLogWindow)
 | 
						|
            nsp << "stdout";
 | 
						|
        if (flags & SuppressStdErrInLogWindow)
 | 
						|
            nsp << "suppress_stderr";
 | 
						|
        if (flags & SuppressFailMessageInLogWindow)
 | 
						|
            nsp << "suppress_fail_msg";
 | 
						|
        if (flags & MergeOutputChannels)
 | 
						|
            nsp << "merge_channels";
 | 
						|
        if (flags & SshPasswordPrompt)
 | 
						|
            nsp << "ssh (" << sshPromptConfigured << ')';
 | 
						|
        if (flags & SuppressCommandLogging)
 | 
						|
            nsp << "suppress_log";
 | 
						|
        if (flags & ForceCLocale)
 | 
						|
            nsp << "c_locale";
 | 
						|
        if (outputCodec)
 | 
						|
            nsp << " Codec: " << outputCodec->name();
 | 
						|
    }
 | 
						|
 | 
						|
    // Run, connect stderr to the output window
 | 
						|
    Utils::SynchronousProcess process;
 | 
						|
    if (!workingDir.isEmpty())
 | 
						|
        process.setWorkingDirectory(workingDir);
 | 
						|
 | 
						|
    VCSBase::VCSBasePlugin::setProcessEnvironment(&env, (flags & ForceCLocale));
 | 
						|
    process.setProcessEnvironment(env);
 | 
						|
    process.setTimeout(timeOutMS);
 | 
						|
    if (outputCodec)
 | 
						|
        process.setStdOutCodec(outputCodec);
 | 
						|
 | 
						|
    // Suppress terminal on UNIX for ssh prompts if it is configured.
 | 
						|
    if (sshPromptConfigured && (flags & SshPasswordPrompt))
 | 
						|
        process.setFlags(Utils::SynchronousProcess::UnixTerminalDisabled);
 | 
						|
 | 
						|
    // connect stderr to the output window if desired
 | 
						|
    if (flags & MergeOutputChannels) {
 | 
						|
        process.setProcessChannelMode(QProcess::MergedChannels);
 | 
						|
    } else {
 | 
						|
        if (!(flags & SuppressStdErrInLogWindow)) {
 | 
						|
            process.setStdErrBufferedSignalsEnabled(true);
 | 
						|
            connect(&process, SIGNAL(stdErrBuffered(QString,bool)), outputWindow, SLOT(append(QString)));
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // connect stdout to the output window if desired
 | 
						|
    if (flags & ShowStdOutInLogWindow) {
 | 
						|
        process.setStdOutBufferedSignalsEnabled(true);
 | 
						|
        connect(&process, SIGNAL(stdOutBuffered(QString,bool)), outputWindow, SLOT(append(QString)));
 | 
						|
    }
 | 
						|
 | 
						|
    process.setTimeOutMessageBoxEnabled(true);
 | 
						|
 | 
						|
    // Run!
 | 
						|
    const Utils::SynchronousProcessResponse sp_resp = process.run(binary, arguments);
 | 
						|
 | 
						|
    // Success/Fail message in appropriate window?
 | 
						|
    if (sp_resp.result == Utils::SynchronousProcessResponse::Finished) {
 | 
						|
        if (flags & ShowSuccessMessage)
 | 
						|
            outputWindow->append(sp_resp.exitMessage(binary, timeOutMS));
 | 
						|
    } else {
 | 
						|
        if (!(flags & SuppressFailMessageInLogWindow))
 | 
						|
            outputWindow->appendError(sp_resp.exitMessage(binary, timeOutMS));
 | 
						|
    }
 | 
						|
 | 
						|
    return sp_resp;
 | 
						|
}
 | 
						|
} // namespace VCSBase
 | 
						|
 | 
						|
#include "vcsbaseplugin.moc"
 |