VCS: Introduce Base class for VCS plugins, use in git.

Fixes:
- Cannot diff a file that does not belong to a project
- Cannot commit when a temporary diff/log view is open
  due to the current file pointing to a temporary directory
- git's project-related actions not passing the correct
  relative path.

Implementation:
- Centralize code to listen for Qt Creator's relevant state changes
  in VCSBasePlugin, dispatching the changes to the instances affected.
  (avoiding multiple invocations of searches/QFileInfo on current).
- Do the same for the corelistener catching closing SubmitEditors.
- Introduce VCSBasePluginState representing the relevant state
  (current file/project).
- Call git with working directory set and relative arguments
- Remove setEnabled/isEnabled() logic of IVersionControl
- Pass toplevel from VCSManager to avoid duplicate searches.
This commit is contained in:
Friedemann Kleint
2009-12-08 14:26:41 +01:00
parent 8d6b4e51ab
commit 8097879d6d
31 changed files with 891 additions and 699 deletions

View File

@@ -48,13 +48,6 @@ public:
virtual QString name() const = 0;
virtual bool isEnabled() const = 0;
/*!
* Enable the VCS, that is, make its menu actions visible.
*/
virtual void setEnabled(bool enabled) = 0;
/*!
* Returns whether files in this directory should be managed with this
* version control.

View File

@@ -46,15 +46,18 @@ enum { debug = 0 };
namespace Core {
typedef QList<IVersionControl *> VersionControlList;
typedef QMap<QString, IVersionControl *> VersionControlCache;
static inline VersionControlList allVersionControls()
{
return ExtensionSystem::PluginManager::instance()->getObjects<IVersionControl>();
}
// ---- VCSManagerPrivate
// ---- VCSManagerPrivate:
// Maintains a cache of top-level directory->version control.
struct VCSManagerPrivate {
QMap<QString, IVersionControl *> m_cachedMatches;
VersionControlCache m_cachedMatches;
};
VCSManager::VCSManager(QObject *parent) :
@@ -79,57 +82,48 @@ void VCSManager::extensionsInitialized()
}
}
void VCSManager::setVCSEnabled(const QString &directory)
IVersionControl* VCSManager::findVersionControlForDirectory(const QString &directory,
QString *topLevelDirectory)
{
if (debug)
qDebug() << Q_FUNC_INFO << directory;
IVersionControl* managingVCS = findVersionControlForDirectory(directory);
const VersionControlList versionControls = allVersionControls();
foreach (IVersionControl *versionControl, versionControls) {
const bool newEnabled = versionControl == managingVCS;
if (newEnabled != versionControl->isEnabled())
versionControl->setEnabled(newEnabled);
}
}
void VCSManager::setAllVCSEnabled()
{
if (debug)
qDebug() << Q_FUNC_INFO;
const VersionControlList versionControls = allVersionControls();
foreach (IVersionControl *versionControl, versionControls)
if (!versionControl->isEnabled())
versionControl->setEnabled(true);
}
IVersionControl* VCSManager::findVersionControlForDirectory(const QString &directory)
{
// first look into the cache, check the whole name
{
const QMap<QString, IVersionControl *>::const_iterator it = m_d->m_cachedMatches.constFind(directory);
if (it != m_d->m_cachedMatches.constEnd())
return it.value();
typedef VersionControlCache::const_iterator VersionControlCacheConstIterator;
const VersionControlCacheConstIterator cacheEnd = m_d->m_cachedMatches.constEnd();
if (topLevelDirectory)
topLevelDirectory->clear();
// First check if the directory has an entry, meaning it is a top level
const VersionControlCacheConstIterator fullPathIt = m_d->m_cachedMatches.constFind(directory);
if (fullPathIt != cacheEnd) {
if (topLevelDirectory)
*topLevelDirectory = directory;
return fullPathIt.value();
}
// Split the path, starting from top, try to find the matching repository
int pos = 0;
const QChar slash = QLatin1Char('/');
while (true) {
int index = directory.indexOf(slash, pos);
const int index = directory.indexOf(slash, pos);
if (index == -1)
break;
const QString directoryPart = directory.left(index);
QMap<QString, IVersionControl *>::const_iterator it = m_d->m_cachedMatches.constFind(directoryPart);
if (it != m_d->m_cachedMatches.constEnd())
const VersionControlCacheConstIterator it = m_d->m_cachedMatches.constFind(directoryPart);
if (it != cacheEnd) {
if (topLevelDirectory)
*topLevelDirectory = it.key();
return it.value();
}
pos = index + 1;
}
// ah nothing so ask the IVersionControls directly
// Nothing: ask the IVersionControls directly, insert the toplevel into the cache.
const VersionControlList versionControls = allVersionControls();
foreach (IVersionControl * versionControl, versionControls) {
if (versionControl->managesDirectory(directory)) {
m_d->m_cachedMatches.insert(versionControl->findTopLevelForDirectory(directory), versionControl);
const QString topLevel = versionControl->findTopLevelForDirectory(directory);
m_d->m_cachedMatches.insert(topLevel, versionControl);
if (topLevelDirectory)
*topLevelDirectory = topLevel;
return versionControl;
}
}
@@ -152,4 +146,14 @@ bool VCSManager::showDeleteDialog(const QString &fileName)
return vc->vcsDelete(fileName);
}
CORE_EXPORT QDebug operator<<(QDebug in, const VCSManager &v)
{
QDebug nospace = in.nospace();
const VersionControlCache::const_iterator cend = v.m_d->m_cachedMatches.constEnd();
for (VersionControlCache::const_iterator it = v.m_d->m_cachedMatches.constBegin(); it != cend; ++it)
nospace << "Directory: " << it.key() << ' ' << it.value()->name() << '\n';
nospace << '\n';
return in;
}
} // namespace Core

View File

@@ -35,20 +35,26 @@
#include <QtCore/QString>
#include <QtCore/QObject>
QT_BEGIN_NAMESPACE
class QDebug;
QT_END_NAMESPACE
namespace Core {
struct VCSManagerPrivate;
class IVersionControl;
// The VCSManager has only one notable function:
// findVersionControlFor(), which returns the IVersionControl * for a given
// filename. Note that the VCSManager assumes that if a IVersionControl *
// manages a directory, then it also manages all the files and all the
// subdirectories.
//
// It works by asking all IVersionControl * if they manage the file, and ask
// for the topmost directory it manages. This information is cached and
// VCSManager thus knows pretty fast which IVersionControl * is responsible.
/* VCSManager:
* 1) Provides functionality for finding the IVersionControl * for a given
* filename (findVersionControlForDirectory). Note that the VCSManager assumes
* that if a IVersionControl * manages a directory, then it also manages
* all the files and all the subdirectories.
* It works by asking all IVersionControl * if they manage the file, and ask
* for the topmost directory it manages. This information is cached and
* VCSManager thus knows pretty fast which IVersionControl * is responsible.
* 2) Passes on the changes from the version controls caused by updating or
* branching repositories and routes them to its signals (repositoryChanged,
* filesChanged). */
class CORE_EXPORT VCSManager : public QObject
{
@@ -60,19 +66,16 @@ public:
void extensionsInitialized();
IVersionControl *findVersionControlForDirectory(const QString &directory);
// Enable the VCS managing a certain directory only. This should
// be used by project manager classes.
void setVCSEnabled(const QString &directory);
// Enable all VCS.
void setAllVCSEnabled();
IVersionControl *findVersionControlForDirectory(const QString &directory,
QString *topLevelDirectory = 0);
// Shows a confirmation dialog, whether the file should also be deleted
// from revision control Calls sccDelete on the file. Returns false
// if a failure occurs
bool showDeleteDialog(const QString &fileName);
friend CORE_EXPORT QDebug operator<<(QDebug in, const VCSManager &);
signals:
void repositoryChanged(const QString &repository);
void filesChanged(const QStringList &files);
@@ -81,6 +84,8 @@ private:
VCSManagerPrivate *m_d;
};
CORE_EXPORT QDebug operator<<(QDebug in, const VCSManager &);
} // namespace Core
#endif // VCSMANAGER_H

View File

@@ -44,19 +44,6 @@ QString CVSControl::name() const
return QLatin1String("cvs");
}
bool CVSControl::isEnabled() const
{
return m_enabled;
}
void CVSControl::setEnabled(bool enabled)
{
if (m_enabled != enabled) {
m_enabled = enabled;
emit enabledChanged(m_enabled);
}
}
bool CVSControl::supportsOperation(Operation operation) const
{
bool rc = true;

View File

@@ -45,9 +45,6 @@ public:
explicit CVSControl(CVSPlugin *plugin);
virtual QString name() const;
virtual bool isEnabled() const;
virtual void setEnabled(bool enabled);
virtual bool managesDirectory(const QString &directory) const;
virtual QString findTopLevelForDirectory(const QString &directory) const;
@@ -59,9 +56,6 @@ public:
void emitRepositoryChanged(const QString &s);
void emitFilesChanged(const QStringList &l);
signals:
void enabledChanged(bool);
private:
bool m_enabled;
CVSPlugin *m_plugin;

View File

@@ -151,7 +151,7 @@ Core::IEditor* locateEditor(const char *property, const QString &entry)
CVSPlugin *CVSPlugin::m_cvsPluginInstance = 0;
CVSPlugin::CVSPlugin() :
m_versionControl(0),
VCSBase::VCSBasePlugin(QLatin1String(CVS::Constants::CVSCOMMITEDITOR_KIND)),
m_projectExplorer(0),
m_addAction(0),
m_deleteAction(0),
@@ -168,6 +168,7 @@ CVSPlugin::CVSPlugin() :
m_submitDiffAction(0),
m_submitUndoAction(0),
m_submitRedoAction(0),
m_menuAction(0),
m_submitActionTriggered(false)
{
}
@@ -205,10 +206,8 @@ static inline Core::Command *createSeparator(QObject *parent,
return ami->registerAction(tmpaction, id, globalcontext);
}
bool CVSPlugin::initialize(const QStringList &arguments, QString *errorMessage)
bool CVSPlugin::initialize(const QStringList & /*arguments */, QString *errorMessage)
{
Q_UNUSED(arguments);
typedef VCSBase::VCSSubmitEditorFactory<CVSSubmitEditor> CVSSubmitEditorFactory;
typedef VCSBase::VCSEditorFactory<CVSEditor> CVSEditorFactory;
using namespace Constants;
@@ -216,21 +215,17 @@ bool CVSPlugin::initialize(const QStringList &arguments, QString *errorMessage)
using namespace Core::Constants;
using namespace ExtensionSystem;
VCSBase::VCSBasePlugin::initialize(new CVSControl(this));
m_cvsPluginInstance = this;
Core::ICore *core = Core::ICore::instance();
if (!core->mimeDatabase()->addMimeTypes(QLatin1String(":/trolltech.cvs/CVS.mimetypes.xml"), errorMessage))
return false;
m_versionControl = new CVSControl(this);
addAutoReleasedObject(m_versionControl);
if (QSettings *settings = core->settings())
m_settings.fromSettings(settings);
addAutoReleasedObject(new CoreListener(this));
addAutoReleasedObject(new SettingsPage);
addAutoReleasedObject(new CVSSubmitEditorFactory(&submitParameters));
@@ -250,10 +245,7 @@ bool CVSPlugin::initialize(const QStringList &arguments, QString *errorMessage)
ami->createMenu(QLatin1String(CMD_ID_CVS_MENU));
cvsMenu->menu()->setTitle(tr("&CVS"));
toolsContainer->addMenu(cvsMenu);
if (QAction *ma = cvsMenu->menu()->menuAction()) {
ma->setEnabled(m_versionControl->isEnabled());
connect(m_versionControl, SIGNAL(enabledChanged(bool)), ma, SLOT(setVisible(bool)));
}
m_menuAction = cvsMenu->menu()->menuAction();
QList<int> globalcontext;
globalcontext << core->uniqueIDManager()->uniqueIdentifier(C_GLOBAL);
@@ -360,30 +352,21 @@ bool CVSPlugin::initialize(const QStringList &arguments, QString *errorMessage)
m_submitRedoAction = new QAction(tr("&Redo"), this);
command = ami->registerAction(m_submitRedoAction, Core::Constants::REDO, cvscommitcontext);
connect(Core::ICore::instance(), SIGNAL(contextChanged(Core::IContext *)), this, SLOT(updateActions()));
return true;
}
void CVSPlugin::extensionsInitialized()
{
m_projectExplorer = ProjectExplorer::ProjectExplorerPlugin::instance();
if (m_projectExplorer) {
connect(m_projectExplorer,
SIGNAL(currentProjectChanged(ProjectExplorer::Project*)),
m_cvsPluginInstance, SLOT(updateActions()));
}
updateActions();
}
bool CVSPlugin::editorAboutToClose(Core::IEditor *iEditor)
bool CVSPlugin::submitEditorAboutToClose(VCSBase::VCSBaseSubmitEditor *submitEditor)
{
if (!iEditor || !isCommitEditorOpen() || qstrcmp(Constants::CVSCOMMITEDITOR, iEditor->kind()))
if (!isCommitEditorOpen())
return true;
Core::IFile *fileIFace = iEditor->file();
const CVSSubmitEditor *editor = qobject_cast<CVSSubmitEditor *>(iEditor);
Core::IFile *fileIFace = submitEditor->file();
const CVSSubmitEditor *editor = qobject_cast<CVSSubmitEditor *>(submitEditor);
if (!fileIFace || !editor)
return true;
@@ -488,8 +471,11 @@ CVSSubmitEditor *CVSPlugin::openCVSSubmitEditor(const QString &fileName)
return submitEditor;
}
void CVSPlugin::updateActions()
void CVSPlugin::updateActions(VCSBase::VCSBasePlugin::ActionState as)
{
if (!VCSBase::VCSBasePlugin::enableMenuAction(as, m_menuAction))
return;
m_diffProjectAction->setEnabled(true);
m_commitAllAction->setEnabled(true);
m_statusAction->setEnabled(true);
@@ -554,7 +540,7 @@ void CVSPlugin::revertCurrentFile()
const CVSResponse revertResponse = runCVS(args, files, cvsShortTimeOut, true);
if (revertResponse.result == CVSResponse::Ok) {
fcb.setModifiedReload(true);
m_versionControl->emitFilesChanged(files);
cvsVersionControl()->emitFilesChanged(files);
}
}
@@ -738,7 +724,7 @@ void CVSPlugin::updateProject()
const CVSResponse response = runCVS(args, topLevels, cvsLongTimeOut, true);
if (response.result == CVSResponse::Ok)
foreach(const QString &topLevel, topLevels)
m_versionControl->emitRepositoryChanged(topLevel);
cvsVersionControl()->emitRepositoryChanged(topLevel);
}
}
@@ -1193,6 +1179,11 @@ QString CVSPlugin::findTopLevelForDirectoryI(const QString &directory) const
return QString();
}
CVSControl *CVSPlugin::cvsVersionControl() const
{
return static_cast<CVSControl *>(versionControl());
}
}
}
Q_EXPORT_PLUGIN(CVS::Internal::CVSPlugin)

View File

@@ -33,8 +33,7 @@
#include "cvssettings.h"
#include "cvsutils.h"
#include <coreplugin/icorelistener.h>
#include <extensionsystem/iplugin.h>
#include <vcsbase/vcsbaseplugin.h>
QT_BEGIN_NAMESPACE
class QDir;
@@ -55,6 +54,10 @@ namespace ProjectExplorer {
class ProjectExplorerPlugin;
}
namespace VCSBase {
class VCSBaseSubmitEditor;
}
namespace CVS {
namespace Internal {
@@ -80,7 +83,7 @@ struct CVSResponse
* the diff editor has an additional property specifying the
* base directory for its interaction to work. */
class CVSPlugin : public ExtensionSystem::IPlugin
class CVSPlugin : public VCSBase::VCSBasePlugin
{
Q_OBJECT
@@ -90,7 +93,6 @@ public:
virtual bool initialize(const QStringList &arguments, QString *error_message);
virtual void extensionsInitialized();
virtual bool editorAboutToClose(Core::IEditor *editor);
void cvsDiff(const QStringList &files, QString diffname = QString());
@@ -108,7 +110,6 @@ public:
static CVSPlugin *cvsPluginInstance();
private slots:
void updateActions();
void addCurrentFile();
void deleteCurrentFile();
void revertCurrentFile();
@@ -124,6 +125,10 @@ private slots:
void submitCurrentLog();
void diffFiles(const QStringList &);
protected:
virtual void updateActions(VCSBase::VCSBasePlugin::ActionState);
virtual bool submitEditorAboutToClose(VCSBase::VCSBaseSubmitEditor *submitEditor);
private:
bool isCommitEditorOpen() const;
QString currentFileName() const;
@@ -152,9 +157,9 @@ private:
void startCommit(const QString &file);
bool commit(const QString &messageFile, const QStringList &subVersionFileList);
void cleanCommitMessageFile();
inline CVSControl *cvsVersionControl() const;
CVSSettings m_settings;
CVSControl *m_versionControl;
QString m_commitMessageFileName;
ProjectExplorer::ProjectExplorerPlugin *m_projectExplorer;
@@ -175,32 +180,12 @@ private:
QAction *m_submitDiffAction;
QAction *m_submitUndoAction;
QAction *m_submitRedoAction;
QAction *m_menuAction;
bool m_submitActionTriggered;
static CVSPlugin *m_cvsPluginInstance;
};
// Just a proxy for CVSPlugin
class CoreListener : public Core::ICoreListener
{
Q_OBJECT
public:
CoreListener(CVSPlugin *plugin) : m_plugin(plugin) { }
// Start commit when submit editor closes
bool editorAboutToClose(Core::IEditor *editor) {
return m_plugin->editorAboutToClose(editor);
}
// TODO: how to handle that ???
bool coreAboutToClose() {
return true;
}
private:
CVSPlugin *m_plugin;
};
} // namespace CVS
} // namespace Internal

View File

@@ -32,7 +32,8 @@
#include <QtGui/QFileDialog>
#include <QtGui/QMessageBox>
using namespace Git::Internal;
namespace Git {
namespace Internal {
ChangeSelectionDialog::ChangeSelectionDialog(QWidget *parent)
: QDialog(parent)
@@ -42,6 +43,21 @@ ChangeSelectionDialog::ChangeSelectionDialog(QWidget *parent)
setWindowTitle(tr("Select a Git commit"));
}
QString ChangeSelectionDialog::change() const
{
return m_ui.changeNumberEdit->text();
}
QString ChangeSelectionDialog::repository() const
{
return m_ui.repositoryEdit->text();
}
void ChangeSelectionDialog::setRepository(const QString &s)
{
m_ui.repositoryEdit->setText(s);
}
void ChangeSelectionDialog::selectWorkingDirectory()
{
static QString location = QString();
@@ -67,3 +83,6 @@ void ChangeSelectionDialog::selectWorkingDirectory()
tr("Selected directory is not a Git repository"));
}
}
}

View File

@@ -37,21 +37,22 @@
namespace Git {
namespace Internal {
class GitPlugin;
class ChangeSelectionDialog : public QDialog
{
Q_OBJECT
public:
ChangeSelectionDialog(QWidget *parent = 0);
QString change() const;
QString repository() const;
void setRepository(const QString &s);
public slots:
void selectWorkingDirectory();
private:
friend class GitPlugin;
Ui_ChangeSelectionDialog m_ui;
};
} // namespace Internal

View File

@@ -147,19 +147,6 @@ QString GitClient::findRepositoryForDirectory(const QString &dir)
return QString();
}
// Return source file or directory string depending on parameters
// ('git diff XX' -> 'XX' , 'git diff XX file' -> 'XX/file').
static QString source(const QString &workingDirectory, const QString &fileName)
{
if (fileName.isEmpty())
return workingDirectory;
QString rc = workingDirectory;
if (!rc.isEmpty() && !rc.endsWith(QDir::separator()))
rc += QDir::separator();
rc += fileName;
return rc;
}
/* Create an editor associated to VCS output of a source file/directory
* (using the file's codec). Makes use of a dynamic property to find an
* existing instance and to reuse it (in case, say, 'git diff foo' is
@@ -253,8 +240,7 @@ void GitClient::diff(const QString &workingDirectory,
const QString kind = QLatin1String(Git::Constants::GIT_DIFF_EDITOR_KIND);
const QString title = tr("Git Diff %1").arg(fileName);
const QString sourceFile = source(workingDirectory, fileName);
const QString sourceFile = VCSBase::VCSBaseEditor::getSource(workingDirectory, fileName);
VCSBase::VCSBaseEditor *editor = createVCSEditor(kind, title, sourceFile, true, "originalFileName", sourceFile);
executeGit(workingDirectory, arguments, editor);
}
@@ -267,10 +253,10 @@ void GitClient::status(const QString &workingDirectory)
executeGit(workingDirectory, statusArgs, 0, true);
}
void GitClient::log(const QString &workingDirectory, const QString &fileName)
void GitClient::log(const QString &workingDirectory, const QStringList &fileNames)
{
if (Git::Constants::debug)
qDebug() << "log" << workingDirectory << fileName;
qDebug() << "log" << workingDirectory << fileNames;
QStringList arguments;
arguments << QLatin1String("log") << QLatin1String(noColorOption);
@@ -278,12 +264,14 @@ void GitClient::log(const QString &workingDirectory, const QString &fileName)
if (m_settings.logCount > 0)
arguments << QLatin1String("-n") << QString::number(m_settings.logCount);
if (!fileName.isEmpty())
arguments << fileName;
if (!fileNames.isEmpty())
arguments.append(fileNames);
const QString title = tr("Git Log %1").arg(fileName);
const QString msgArg = fileNames.empty() ? workingDirectory :
fileNames.join(QString(", "));
const QString title = tr("Git Log %1").arg(msgArg);
const QString kind = QLatin1String(Git::Constants::GIT_LOG_EDITOR_KIND);
const QString sourceFile = source(workingDirectory, fileName);
const QString sourceFile = VCSBase::VCSBaseEditor::getSource(workingDirectory, fileNames);
VCSBase::VCSBaseEditor *editor = createVCSEditor(kind, title, sourceFile, false, "logFileName", sourceFile);
executeGit(workingDirectory, arguments, editor);
}
@@ -313,7 +301,7 @@ void GitClient::blame(const QString &workingDirectory, const QString &fileName,
const QString kind = QLatin1String(Git::Constants::GIT_BLAME_EDITOR_KIND);
const QString title = tr("Git Blame %1").arg(fileName);
const QString sourceFile = source(workingDirectory, fileName);
const QString sourceFile = VCSBase::VCSBaseEditor::getSource(workingDirectory, fileName);
VCSBase::VCSBaseEditor *editor = createVCSEditor(kind, title, sourceFile, true, "blameFileName", sourceFile);
executeGit(workingDirectory, arguments, editor, false, GitCommand::NoReport, lineNumber);
@@ -912,7 +900,7 @@ void GitClient::revert(const QStringList &files)
QString errorMessage;
switch (revertI(files, &isDirectory, &errorMessage)) {
case RevertOk:
m_plugin->versionControl()->emitFilesChanged(files);
m_plugin->gitVersionControl()->emitFilesChanged(files);
break;
case RevertCanceled:
break;
@@ -1020,7 +1008,7 @@ void GitClient::connectRepositoryChanged(const QString & repository, GitCommand
if (!m_repositoryChangedSignalMapper) {
m_repositoryChangedSignalMapper = new QSignalMapper(this);
connect(m_repositoryChangedSignalMapper, SIGNAL(mapped(QString)),
m_plugin->versionControl(), SIGNAL(repositoryChanged(QString)));
m_plugin->gitVersionControl(), SIGNAL(repositoryChanged(QString)));
}
m_repositoryChangedSignalMapper->setMapping(cmd, repository);
connect(cmd, SIGNAL(success()), m_repositoryChangedSignalMapper, SLOT(map()),

View File

@@ -79,7 +79,7 @@ public:
const QStringList &unstagedFileNames, const QStringList &stagedFileNames= QStringList());
void status(const QString &workingDirectory);
void log(const QString &workingDirectory, const QString &fileName);
void log(const QString &workingDirectory, const QStringList &fileNames);
void blame(const QString &workingDirectory, const QString &fileName, int lineNumber = -1);
void showCommit(const QString &workingDirectory, const QString &commit);
void checkout(const QString &workingDirectory, const QString &file);

View File

@@ -56,10 +56,6 @@
#include <vcsbase/basevcssubmiteditorfactory.h>
#include <vcsbase/vcsbaseoutputwindow.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectnodes.h>
#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
@@ -105,23 +101,16 @@ static inline const VCSBase::VCSBaseEditorParameters *findType(int ie)
using namespace Git;
using namespace Git::Internal;
// CoreListener
bool CoreListener::editorAboutToClose(Core::IEditor *editor)
{
return m_plugin->editorAboutToClose(editor);
}
// GitPlugin
GitPlugin *GitPlugin::m_instance = 0;
GitPlugin::GitPlugin() :
VCSBase::VCSBasePlugin(QLatin1String(Git::Constants::GITSUBMITEDITOR_KIND)),
m_core(0),
m_diffAction(0),
m_diffProjectAction(0),
m_statusAction(0),
m_statusProjectAction(0),
m_statusRepositoryAction(0),
m_logAction(0),
m_blameAction(0),
m_logProjectAction(0),
@@ -141,8 +130,8 @@ GitPlugin::GitPlugin() :
m_stashPopAction(0),
m_stashListAction(0),
m_branchListAction(0),
m_menuAction(0),
m_gitClient(0),
m_versionControl(0),
m_changeSelectionDialog(0),
m_submitActionTriggered(false)
{
@@ -192,15 +181,17 @@ static Core::Command *createSeparator(Core::ActionManager *am,
bool GitPlugin::initialize(const QStringList &arguments, QString *errorMessage)
{
typedef VCSBase::VCSEditorFactory<GitEditor> GitEditorFactory;
typedef VCSBase::VCSSubmitEditorFactory<GitSubmitEditor> GitSubmitEditorFactory;
Q_UNUSED(arguments)
Q_UNUSED(errorMessage)
typedef VCSBase::VCSEditorFactory<GitEditor> GitEditorFactory;
typedef VCSBase::VCSSubmitEditorFactory<GitSubmitEditor> GitSubmitEditorFactory;
VCSBase::VCSBasePlugin::initialize(new GitVersionControl(m_gitClient));
m_core = Core::ICore::instance();
m_gitClient = new GitClient(this);
// Create the globalco6664324b12a3339d18251df1cd69a1da06d1e2dcntext list to register actions accordingly
// Create the globalcontext list to register actions accordingly
QList<int> globalcontext;
globalcontext << m_core->uniqueIDManager()->uniqueIdentifier(Core::Constants::C_GLOBAL);
@@ -212,13 +203,7 @@ bool GitPlugin::initialize(const QStringList &arguments, QString *errorMessage)
for (int i = 0; i < editorCount; i++)
addAutoReleasedObject(new GitEditorFactory(editorParameters + i, m_gitClient, describeSlot));
addAutoReleasedObject(new CoreListener(this));
addAutoReleasedObject(new GitSubmitEditorFactory(&submitParameters));
m_versionControl = new GitVersionControl(m_gitClient);
addAutoReleasedObject(m_versionControl);
addAutoReleasedObject(new CloneWizard);
addAutoReleasedObject(new Gitorious::Internal::GitoriousCloneWizard);
@@ -232,11 +217,7 @@ bool GitPlugin::initialize(const QStringList &arguments, QString *errorMessage)
actionManager->createMenu(QLatin1String("Git"));
gitContainer->menu()->setTitle(tr("&Git"));
toolsContainer->addMenu(gitContainer);
if (QAction *ma = gitContainer->menu()->menuAction()) {
ma->setEnabled(m_versionControl->isEnabled());
connect(m_versionControl, SIGNAL(enabledChanged(bool)), ma, SLOT(setVisible(bool)));
}
m_menuAction = gitContainer->menu()->menuAction();
Core::Command *command;
m_diffAction = new Utils::ParameterAction(tr("Diff Current File"), tr("Diff \"%1\""), Utils::ParameterAction::AlwaysEnabled, this);
@@ -246,13 +227,6 @@ bool GitPlugin::initialize(const QStringList &arguments, QString *errorMessage)
connect(m_diffAction, SIGNAL(triggered()), this, SLOT(diffCurrentFile()));
gitContainer->addAction(command);
m_statusAction = new Utils::ParameterAction(tr("File Status"), tr("Status Related to \"%1\""), Utils::ParameterAction::AlwaysEnabled, this);
command = actionManager->registerAction(m_statusAction, "Git.Status", globalcontext);
command->setDefaultKeySequence(QKeySequence(tr("Alt+G,Alt+S")));
command->setAttribute(Core::Command::CA_UpdateText);
connect(m_statusAction, SIGNAL(triggered()), this, SLOT(statusFile()));
gitContainer->addAction(command);
m_logAction = new Utils::ParameterAction(tr("Log File"), tr("Log of \"%1\""), Utils::ParameterAction::AlwaysEnabled, this);
command = actionManager->registerAction(m_logAction, "Git.Log", globalcontext);
command->setDefaultKeySequence(QKeySequence(tr("Alt+G,Alt+L")));
@@ -296,10 +270,9 @@ bool GitPlugin::initialize(const QStringList &arguments, QString *errorMessage)
connect(m_diffProjectAction, SIGNAL(triggered()), this, SLOT(diffCurrentProject()));
gitContainer->addAction(command);
m_statusProjectAction = new Utils::ParameterAction(tr("Project Status"), tr("Status Project \"%1\""), Utils::ParameterAction::AlwaysEnabled, this);
command = actionManager->registerAction(m_statusProjectAction, "Git.StatusProject", globalcontext);
command->setAttribute(Core::Command::CA_UpdateText);
connect(m_statusProjectAction, SIGNAL(triggered()), this, SLOT(statusProject()));
m_statusRepositoryAction = new QAction(tr("Repository Status"), this);
command = actionManager->registerAction(m_statusRepositoryAction, "Git.StatusRepository", globalcontext);
connect(m_statusRepositoryAction, SIGNAL(triggered()), this, SLOT(statusRepository()));
gitContainer->addAction(command);
m_logProjectAction = new Utils::ParameterAction(tr("Log Project"), tr("Log Project \"%1\""), Utils::ParameterAction::AlwaysEnabled, this);
@@ -386,12 +359,6 @@ bool GitPlugin::initialize(const QStringList &arguments, QString *errorMessage)
m_redoAction = new QAction(tr("&Redo"), this);
command = actionManager->registerAction(m_redoAction, Core::Constants::REDO, submitContext);
// Ask for updates of our actions, in case context switches
connect(m_core, SIGNAL(contextChanged(Core::IContext *)),
this, SLOT(updateActions()));
connect(m_core->fileManager(), SIGNAL(currentFileChanged(const QString &)),
this, SLOT(updateActions()));
return true;
}
@@ -399,9 +366,9 @@ void GitPlugin::extensionsInitialized()
{
}
GitVersionControl *GitPlugin::versionControl() const
GitVersionControl *GitPlugin::gitVersionControl() const
{
return m_versionControl;
return static_cast<GitVersionControl *>(versionControl());
}
void GitPlugin::submitEditorDiff(const QStringList &unstaged, const QStringList &staged)
@@ -411,127 +378,83 @@ void GitPlugin::submitEditorDiff(const QStringList &unstaged, const QStringList
void GitPlugin::diffCurrentFile()
{
const QFileInfo fileInfo = currentFile();
const QString fileName = fileInfo.fileName();
const QString workingDirectory = fileInfo.absolutePath();
m_gitClient->diff(workingDirectory, QStringList(), fileName);
const VCSBase::VCSBasePluginState state = currentState();
QTC_ASSERT(state.hasFile(), return)
m_gitClient->diff(state.currentFileTopLevel(), QStringList(), state.relativeCurrentFile());
}
void GitPlugin::diffCurrentProject()
{
QString workingDirectory = getWorkingDirectory();
if (workingDirectory.isEmpty())
return;
m_gitClient->diff(workingDirectory, QStringList(), QString());
const VCSBase::VCSBasePluginState state = currentState();
QTC_ASSERT(state.hasProject(), return)
m_gitClient->diff(state.currentProjectTopLevel(), QStringList(), state.relativeCurrentProject());
}
QFileInfo GitPlugin::currentFile() const
void GitPlugin::statusRepository()
{
QString fileName = m_core->fileManager()->currentFile();
QFileInfo fileInfo(fileName);
return fileInfo;
}
QString GitPlugin::getWorkingDirectory()
{
QString workingDirectory;
if (const ProjectExplorer::ProjectExplorerPlugin *p = ProjectExplorer::ProjectExplorerPlugin::instance())
if (p && p->currentNode())
workingDirectory = QFileInfo(p->currentNode()->path()).absolutePath();
if (Git::Constants::debug > 1)
qDebug() << Q_FUNC_INFO << "Project" << workingDirectory;
if (workingDirectory.isEmpty())
workingDirectory = QFileInfo(m_core->fileManager()->currentFile()).absolutePath();
if (Git::Constants::debug > 1)
qDebug() << Q_FUNC_INFO << "file" << workingDirectory;
if (workingDirectory.isEmpty()) {
VCSBase::VCSBaseOutputWindow::instance()->appendError(tr("Could not find working directory"));
return QString();
}
return workingDirectory;
}
void GitPlugin::statusProject()
{
QString workingDirectory = getWorkingDirectory();
if (workingDirectory.isEmpty())
return;
m_gitClient->status(workingDirectory);
}
void GitPlugin::statusFile()
{
m_gitClient->status(currentFile().absolutePath());
const VCSBase::VCSBasePluginState state = currentState();
QTC_ASSERT(state.hasTopLevel(), return)
m_gitClient->status(state.topLevel());
}
void GitPlugin::logFile()
{
const QFileInfo fileInfo = currentFile();
const QString fileName = fileInfo.fileName();
const QString workingDirectory = fileInfo.absolutePath();
m_gitClient->log(workingDirectory, fileName);
const VCSBase::VCSBasePluginState state = currentState();
QTC_ASSERT(state.hasFile(), return)
m_gitClient->log(state.currentFileTopLevel(), QStringList(state.relativeCurrentFile()));
}
void GitPlugin::blameFile()
{
const QFileInfo fileInfo = currentFile();
const QString fileName = fileInfo.fileName();
const QString workingDirectory = fileInfo.absolutePath();
m_gitClient->blame(workingDirectory, fileName,
VCSBase::VCSBaseEditor::lineNumberOfCurrentEditor(fileInfo.absoluteFilePath()));
const VCSBase::VCSBasePluginState state = currentState();
QTC_ASSERT(state.hasFile(), return)
const int lineNumber = VCSBase::VCSBaseEditor::lineNumberOfCurrentEditor(state.currentFile());
m_gitClient->blame(state.currentFileTopLevel(), state.relativeCurrentFile(), lineNumber);
}
void GitPlugin::logProject()
{
QString workingDirectory = getWorkingDirectory();
if (workingDirectory.isEmpty())
return;
m_gitClient->log(workingDirectory, QString());
const VCSBase::VCSBasePluginState state = currentState();
QTC_ASSERT(state.hasProject(), return)
m_gitClient->log(state.currentProjectTopLevel(), state.relativeCurrentProject());
}
void GitPlugin::undoFileChanges()
{
const QFileInfo fileInfo = currentFile();
Core::FileChangeBlocker fcb(fileInfo.filePath());
const VCSBase::VCSBasePluginState state = currentState();
QTC_ASSERT(state.hasFile(), return)
Core::FileChangeBlocker fcb(state.currentFile());
fcb.setModifiedReload(true);
m_gitClient->revert(QStringList(fileInfo.absoluteFilePath()));
m_gitClient->revert(QStringList(state.currentFile()));
}
void GitPlugin::undoProjectChanges()
{
const QString workingDirectory = getWorkingDirectory();
if (workingDirectory.isEmpty())
return;
const VCSBase::VCSBasePluginState state = currentState();
QTC_ASSERT(state.hasTopLevel(), return)
const QString msg = tr("Would you like to revert all pending changes to the repository\n%1?").arg(state.topLevel());
const QMessageBox::StandardButton answer
= QMessageBox::question(m_core->mainWindow(),
tr("Revert"),
tr("Would you like to revert all pending changes to the project?"),
tr("Revert"), msg,
QMessageBox::Yes|QMessageBox::No,
QMessageBox::No);
if (answer == QMessageBox::No)
return;
m_gitClient->hardReset(workingDirectory, QString());
m_gitClient->hardReset(state.topLevel(), QString());
}
void GitPlugin::stageFile()
{
const QFileInfo fileInfo = currentFile();
const QString fileName = fileInfo.fileName();
const QString workingDirectory = fileInfo.absolutePath();
m_gitClient->addFile(workingDirectory, fileName);
const VCSBase::VCSBasePluginState state = currentState();
QTC_ASSERT(state.hasFile(), return)
m_gitClient->addFile(state.currentFileTopLevel(), state.relativeCurrentFile());
}
void GitPlugin::unstageFile()
{
const QFileInfo fileInfo = currentFile();
const QString fileName = fileInfo.fileName();
const QString workingDirectory = fileInfo.absolutePath();
m_gitClient->synchronousReset(workingDirectory, QStringList(fileName));
const VCSBase::VCSBasePluginState state = currentState();
QTC_ASSERT(state.hasFile(), return)
m_gitClient->synchronousReset(state.currentFileTopLevel(), QStringList(state.relativeCurrentFile()));
}
void GitPlugin::startCommit()
@@ -543,15 +466,12 @@ void GitPlugin::startCommit()
return;
}
// Find repository and get commit data
const QFileInfo currentFileInfo = currentFile();
if (!currentFileInfo.exists())
return;
const VCSBase::VCSBasePluginState state = currentState();
QTC_ASSERT(state.hasTopLevel(), return)
const QString workingDirectory = currentFileInfo.absolutePath();
QString errorMessage, commitTemplate;
CommitData data;
if (!m_gitClient->getCommitData(workingDirectory, &commitTemplate, &data, &errorMessage)) {
if (!m_gitClient->getCommitData(state.topLevel(), &commitTemplate, &data, &errorMessage)) {
VCSBase::VCSBaseOutputWindow::instance()->append(errorMessage);
return;
}
@@ -606,13 +526,12 @@ void GitPlugin::submitCurrentLog()
m_core->editorManager()->closeEditors(editors);
}
bool GitPlugin::editorAboutToClose(Core::IEditor *iEditor)
bool GitPlugin::submitEditorAboutToClose(VCSBase::VCSBaseSubmitEditor *submitEditor)
{
// Closing a submit editor?
if (!iEditor || !isCommitEditorOpen() || qstrcmp(iEditor->kind(), Constants::GITSUBMITEDITOR_KIND))
return true;
Core::IFile *fileIFace = iEditor->file();
const GitSubmitEditor *editor = qobject_cast<GitSubmitEditor *>(iEditor);
if (!isCommitEditorOpen())
return false;
Core::IFile *fileIFace = submitEditor->file();
const GitSubmitEditor *editor = qobject_cast<GitSubmitEditor *>(submitEditor);
if (!fileIFace || !editor)
return true;
// Submit editor closing. Make it write out the commit message
@@ -668,15 +587,14 @@ bool GitPlugin::editorAboutToClose(Core::IEditor *iEditor)
void GitPlugin::pull()
{
const QString workingDirectory = getWorkingDirectory();
if (workingDirectory.isEmpty())
return;
const VCSBase::VCSBasePluginState state = currentState();
QTC_ASSERT(state.hasTopLevel(), return)
switch (m_gitClient->ensureStash(workingDirectory)) {
switch (m_gitClient->ensureStash(state.topLevel())) {
case GitClient::StashUnchanged:
case GitClient::Stashed:
case GitClient::NotStashed:
m_gitClient->pull(workingDirectory);
m_gitClient->pull(state.topLevel());
default:
break;
}
@@ -684,34 +602,33 @@ void GitPlugin::pull()
void GitPlugin::push()
{
const QString workingDirectory = getWorkingDirectory();
if (!workingDirectory.isEmpty())
m_gitClient->push(workingDirectory);
const VCSBase::VCSBasePluginState state = currentState();
QTC_ASSERT(state.hasTopLevel(), return)
m_gitClient->push(state.topLevel());
}
void GitPlugin::stash()
{
const QString workingDirectory = getWorkingDirectory();
if (!workingDirectory.isEmpty())
m_gitClient->stash(workingDirectory);
const VCSBase::VCSBasePluginState state = currentState();
QTC_ASSERT(state.hasTopLevel(), return)
m_gitClient->stash(state.topLevel());
}
void GitPlugin::stashPop()
{
const QString workingDirectory = getWorkingDirectory();
if (!workingDirectory.isEmpty())
m_gitClient->stashPop(workingDirectory);
const VCSBase::VCSBasePluginState state = currentState();
QTC_ASSERT(state.hasTopLevel(), return)
m_gitClient->stashPop(state.topLevel());
}
void GitPlugin::branchList()
{
const QString workingDirectory = getWorkingDirectory();
if (workingDirectory.isEmpty())
return;
const VCSBase::VCSBasePluginState state = currentState();
QTC_ASSERT(state.hasTopLevel(), return)
QString errorMessage;
BranchDialog dialog(m_core->mainWindow());
if (!dialog.init(m_gitClient, workingDirectory, &errorMessage)) {
if (!dialog.init(m_gitClient, state.topLevel(), &errorMessage)) {
VCSBase::VCSBaseOutputWindow::instance()->appendError(errorMessage);
return;
}
@@ -720,85 +637,67 @@ void GitPlugin::branchList()
void GitPlugin::stashList()
{
const QString workingDirectory = getWorkingDirectory();
if (!workingDirectory.isEmpty())
m_gitClient->stashList(workingDirectory);
const VCSBase::VCSBasePluginState state = currentState();
QTC_ASSERT(state.hasTopLevel(), return)
m_gitClient->stashList(state.topLevel());
}
void GitPlugin::updateActions()
void GitPlugin::updateActions(VCSBase::VCSBasePlugin::ActionState as)
{
const QFileInfo current = currentFile();
const QString fileName = current.fileName();
const QString currentDirectory = getWorkingDirectory();
const QString repository = m_gitClient->findRepositoryForFile(current.absoluteFilePath());
// First check for file commands and if the current file is inside
// a Git-repository
if (!VCSBase::VCSBasePlugin::enableMenuAction(as, m_menuAction))
return;
const QString fileName = currentState().currentFileName();
m_diffAction->setParameter(fileName);
m_statusAction->setParameter(fileName);
m_logAction->setParameter(fileName);
m_blameAction->setParameter(fileName);
m_undoFileAction->setParameter(fileName);
m_stageAction->setParameter(fileName);
m_unstageAction->setParameter(fileName);
bool enabled = !fileName.isEmpty() && !repository.isEmpty();
m_diffAction->setEnabled(enabled);
m_statusAction->setEnabled(enabled);
m_logAction->setEnabled(enabled);
m_blameAction->setEnabled(enabled);
m_undoFileAction->setEnabled(enabled);
m_stageAction->setEnabled(enabled);
m_unstageAction->setEnabled(enabled);
bool fileEnabled = !fileName.isEmpty();
m_diffAction->setEnabled(fileEnabled);
m_logAction->setEnabled(fileEnabled);
m_blameAction->setEnabled(fileEnabled);
m_undoFileAction->setEnabled(fileEnabled);
m_stageAction->setEnabled(fileEnabled);
m_unstageAction->setEnabled(fileEnabled);
if (repository.isEmpty()) {
// If the file is not in a repository, the corresponding project will
// be neither and we can disable everything and return
m_diffProjectAction->setEnabled(false);
m_diffProjectAction->setParameter(repository);
m_statusProjectAction->setParameter(repository);
m_statusProjectAction->setEnabled(false);
m_logProjectAction->setParameter(repository);
m_logProjectAction->setEnabled(false);
m_undoProjectAction->setEnabled(false);
return;
}
const QString projectName = currentState().currentProjectName();
const bool projectEnabled = !projectName.isEmpty();
m_diffProjectAction->setEnabled(projectEnabled);
m_diffProjectAction->setParameter(projectName);
m_logProjectAction->setEnabled(projectEnabled);
m_logProjectAction->setParameter(projectName);
m_undoProjectAction->setEnabled(projectEnabled);
// We only know the file is in some repository, we do not know
// anything about any project so far.
QString project;
if (const ProjectExplorer::ProjectExplorerPlugin *p = ProjectExplorer::ProjectExplorerPlugin::instance()) {
if (const ProjectExplorer::Node *node = p->currentNode())
if (const ProjectExplorer::Node *projectNode = node->projectNode())
project = QFileInfo(projectNode->path()).completeBaseName();
}
const bool repositoryEnabled = currentState().hasTopLevel();
m_statusRepositoryAction->setEnabled(repositoryEnabled);
m_branchListAction->setEnabled(repositoryEnabled);
m_stashListAction->setEnabled(repositoryEnabled);
m_stashPopAction->setEnabled(repositoryEnabled);
enabled = !project.isEmpty();
m_diffProjectAction->setEnabled(enabled);
m_diffProjectAction->setParameter(project);
m_statusProjectAction->setEnabled(enabled);
m_statusProjectAction->setParameter(project);
m_logProjectAction->setEnabled(enabled);
m_logProjectAction->setParameter(project);
m_undoProjectAction->setEnabled(enabled);
// Prompts for repo.
m_showAction->setEnabled(true);
}
void GitPlugin::showCommit()
{
const VCSBase::VCSBasePluginState state = currentState();
if (!m_changeSelectionDialog)
m_changeSelectionDialog = new ChangeSelectionDialog();
const QFileInfo currentInfo = currentFile();
QString repositoryLocation = m_gitClient->findRepositoryForFile(currentInfo.absoluteFilePath());
if (!repositoryLocation.isEmpty())
m_changeSelectionDialog->m_ui.repositoryEdit->setText(repositoryLocation);
if (state.hasTopLevel())
m_changeSelectionDialog->setRepository(state.topLevel());
if (m_changeSelectionDialog->exec() != QDialog::Accepted)
return;
const QString change = m_changeSelectionDialog->m_ui.changeNumberEdit->text();
const QString change = m_changeSelectionDialog->change();
if (change.isEmpty())
return;
m_gitClient->show(m_changeSelectionDialog->m_ui.repositoryEdit->text(), change);
m_gitClient->show(m_changeSelectionDialog->repository(), change);
}
GitSettings GitPlugin::settings() const

View File

@@ -31,9 +31,9 @@
#define GITPLUGIN_H
#include "settingspage.h"
#include "vcsbase/vcsbaseplugin.h"
#include <coreplugin/editormanager/ieditorfactory.h>
#include <coreplugin/icorelistener.h>
#include <extensionsystem/iplugin.h>
#include <QtCore/QObject>
@@ -49,7 +49,6 @@ QT_END_NAMESPACE
namespace Core {
class IEditorFactory;
class ICore;
class IVersionControl;
}
namespace Utils {
class ParameterAction;
@@ -66,19 +65,7 @@ class GitSubmitEditor;
struct CommitData;
struct GitSettings;
// Just a proxy for GitPlugin
class CoreListener : public Core::ICoreListener
{
Q_OBJECT
public:
CoreListener(GitPlugin *plugin) : m_plugin(plugin) { }
bool editorAboutToClose(Core::IEditor *editor);
bool coreAboutToClose() { return true; }
private:
GitPlugin *m_plugin;
};
class GitPlugin : public ExtensionSystem::IPlugin
class GitPlugin : public VCSBase::VCSBasePlugin
{
Q_OBJECT
@@ -91,25 +78,19 @@ public:
virtual bool initialize(const QStringList &arguments, QString *error_message);
virtual void extensionsInitialized();
QString getWorkingDirectory();
GitVersionControl *gitVersionControl() const;
GitSettings settings() const;
void setSettings(const GitSettings &s);
GitClient *gitClient() const;
GitVersionControl *versionControl() const;
public slots:
void updateActions();
bool editorAboutToClose(Core::IEditor *editor);
private slots:
void diffCurrentFile();
void diffCurrentProject();
void submitEditorDiff(const QStringList &unstaged, const QStringList &staged);
void submitCurrentLog();
void statusFile();
void statusProject();
void statusRepository();
void logFile();
void blameFile();
void logProject();
@@ -127,9 +108,12 @@ private slots:
void pull();
void push();
protected:
virtual void updateActions(VCSBase::VCSBasePlugin::ActionState);
virtual bool submitEditorAboutToClose(VCSBase::VCSBaseSubmitEditor *submitEditor);
private:
bool isCommitEditorOpen() const;
QFileInfo currentFile() const;
Core::IEditor *openSubmitEditor(const QString &fileName, const CommitData &cd);
void cleanCommitMessageFile();
@@ -137,8 +121,7 @@ private:
Core::ICore *m_core;
Utils::ParameterAction *m_diffAction;
Utils::ParameterAction *m_diffProjectAction;
Utils::ParameterAction *m_statusAction;
Utils::ParameterAction *m_statusProjectAction;
QAction *m_statusRepositoryAction;
Utils::ParameterAction *m_logAction;
Utils::ParameterAction *m_blameAction;
Utils::ParameterAction *m_logProjectAction;
@@ -159,9 +142,9 @@ private:
QAction *m_stashPopAction;
QAction *m_stashListAction;
QAction *m_branchListAction;
QAction *m_menuAction;
GitClient *m_gitClient;
GitVersionControl *m_versionControl;
ChangeSelectionDialog *m_changeSelectionDialog;
QString m_submitRepository;
QStringList m_submitOrigCommitFiles;

View File

@@ -44,19 +44,6 @@ QString GitVersionControl::name() const
return QLatin1String("git");
}
bool GitVersionControl::isEnabled() const
{
return m_enabled;
}
void GitVersionControl::setEnabled(bool enabled)
{
if (m_enabled != enabled) {
m_enabled = enabled;
emit enabledChanged(m_enabled);
}
}
bool GitVersionControl::supportsOperation(Operation operation) const
{
bool rc = false;

View File

@@ -46,9 +46,6 @@ public:
virtual QString name() const;
virtual bool isEnabled() const;
virtual void setEnabled(bool enabled);
bool managesDirectory(const QString &directory) const;
virtual QString findTopLevelForDirectory(const QString &directory) const;

View File

@@ -152,15 +152,6 @@ static const char * const CMD_ID_SEPARATOR1 = "Perforce.Separator1";
static const char * const CMD_ID_SEPARATOR2 = "Perforce.Separator2";
static const char * const CMD_ID_SEPARATOR3 = "Perforce.Separator3";
////
// CoreListener
////
bool CoreListener::editorAboutToClose(Core::IEditor *editor)
{
return m_plugin->editorAboutToClose(editor);
}
////
// PerforcePlugin
////
@@ -168,6 +159,7 @@ bool CoreListener::editorAboutToClose(Core::IEditor *editor)
PerforcePlugin *PerforcePlugin::m_perforcePluginInstance = NULL;
PerforcePlugin::PerforcePlugin() :
VCSBase::VCSBasePlugin(QLatin1String(Constants::PERFORCESUBMITEDITOR_KIND)),
m_editAction(0),
m_addAction(0),
m_deleteAction(0),
@@ -190,8 +182,7 @@ PerforcePlugin::PerforcePlugin() :
m_submitActionTriggered(false),
m_diffSelectedFiles(0),
m_undoAction(0),
m_redoAction(0),
m_versionControl(0)
m_redoAction(0)
{
}
@@ -201,14 +192,13 @@ static const VCSBase::VCSBaseSubmitEditorParameters submitParameters = {
Perforce::Constants::C_PERFORCESUBMITEDITOR
};
bool PerforcePlugin::initialize(const QStringList &arguments, QString *errorMessage)
bool PerforcePlugin::initialize(const QStringList & /* arguments */, QString * errorMessage)
{
Q_UNUSED(arguments)
Q_UNUSED(errorMessage)
typedef VCSBase::VCSEditorFactory<PerforceEditor> PerforceEditorFactory;
typedef VCSBase::VCSSubmitEditorFactory<PerforceSubmitEditor> PerforceSubmitEditorFactory;
VCSBase::VCSBasePlugin::initialize(new PerforceVersionControl(this));
Core::ICore *core = Core::ICore::instance();
if (!core->mimeDatabase()->addMimeTypes(QLatin1String(":/trolltech.perforce/Perforce.mimetypes.xml"), errorMessage))
return false;
@@ -227,11 +217,6 @@ bool PerforcePlugin::initialize(const QStringList &arguments, QString *errorMess
for (int i = 0; i < editorCount; i++)
addAutoReleasedObject(new PerforceEditorFactory(editorParameters + i, this, describeSlot));
m_versionControl = new PerforceVersionControl(this);
addAutoReleasedObject(m_versionControl);
addAutoReleasedObject(new CoreListener(this));
//register actions
Core::ActionManager *am = Core::ICore::instance()->actionManager();
@@ -242,10 +227,7 @@ bool PerforcePlugin::initialize(const QStringList &arguments, QString *errorMess
am->createMenu(QLatin1String(CMD_ID_PERFORCE_MENU));
mperforce->menu()->setTitle(tr("&Perforce"));
mtools->addMenu(mperforce);
if (QAction *ma = mperforce->menu()->menuAction()) {
ma->setEnabled(m_versionControl->isEnabled());
connect(m_versionControl, SIGNAL(enabledChanged(bool)), ma, SLOT(setVisible(bool)));
}
m_menuAction = mperforce->menu()->menuAction();
QList<int> globalcontext;
globalcontext << Core::Constants::C_GLOBAL_ID;
@@ -397,24 +379,12 @@ bool PerforcePlugin::initialize(const QStringList &arguments, QString *errorMess
m_redoAction = new QAction(tr("&Redo"), this);
command = am->registerAction(m_redoAction, Core::Constants::REDO, perforcesubmitcontext);
connect(core, SIGNAL(contextChanged(Core::IContext *)),
this, SLOT(updateActions()));
connect(core->fileManager(), SIGNAL(currentFileChanged(const QString &)),
this, SLOT(updateActions()));
return true;
}
void PerforcePlugin::extensionsInitialized()
{
m_projectExplorer = ProjectExplorer::ProjectExplorerPlugin::instance();
if (m_projectExplorer) {
connect(m_projectExplorer,
SIGNAL(currentProjectChanged(ProjectExplorer::Project*)),
this, SLOT(updateActions()));
}
updateActions();
}
void PerforcePlugin::openCurrentFile()
@@ -455,7 +425,7 @@ void PerforcePlugin::revertCurrentFile()
fcb.setModifiedReload(true);
PerforceResponse result2 = runP4Cmd(QStringList() << QLatin1String("revert") << fileName, QStringList(), CommandToWindow|StdOutToWindow|StdErrToWindow|ErrorToWindow);
if (!result2.error)
m_versionControl->emitFilesChanged(QStringList(fileName));
perforceVersionControl()->emitFilesChanged(QStringList(fileName));
}
void PerforcePlugin::diffCurrentFile()
@@ -519,7 +489,7 @@ void PerforcePlugin::updateCheckout(const QStringList &dirs)
const PerforceResponse resp = runP4Cmd(args, QStringList(), CommandToWindow|StdOutToWindow|StdErrToWindow|ErrorToWindow);
if (!dirs.empty())
foreach(const QString &dir, dirs)
m_versionControl->emitRepositoryChanged(dir);
perforceVersionControl()->emitRepositoryChanged(dir);
}
void PerforcePlugin::printOpenedFileList()
@@ -680,8 +650,11 @@ void PerforcePlugin::filelog(const QString &fileName)
}
}
void PerforcePlugin::updateActions()
void PerforcePlugin::updateActions(VCSBase::VCSBasePlugin::ActionState as)
{
if (!VCSBase::VCSBasePlugin::enableMenuAction(as, m_menuAction))
return;
const QString fileName = currentFileName();
const QString baseName = fileName.isEmpty() ? fileName : QFileInfo(fileName).fileName();
@@ -993,19 +966,16 @@ bool PerforcePlugin::isCommitEditorOpen() const
return !m_commitMessageFileName.isEmpty();
}
bool PerforcePlugin::editorAboutToClose(Core::IEditor *editor)
bool PerforcePlugin::submitEditorAboutToClose(VCSBase::VCSBaseSubmitEditor *submitEditor)
{
if (!editor || !isCommitEditorOpen())
if (!isCommitEditorOpen())
return true;
Core::ICore *core = Core::ICore::instance();
Core::IFile *fileIFace = editor->file();
if (!fileIFace)
Core::IFile *fileIFace = submitEditor->file();
const PerforceSubmitEditor *perforceEditor = qobject_cast<PerforceSubmitEditor *>(submitEditor);
if (!fileIFace || !perforceEditor)
return true;
const PerforceSubmitEditor *perforceEditor = qobject_cast<PerforceSubmitEditor *>(editor);
if (!perforceEditor)
return true;
QFileInfo editorFile(fileIFace->fileName());
QFileInfo changeFile(m_commitMessageFileName);
const QFileInfo editorFile(fileIFace->fileName());
const QFileInfo changeFile(m_commitMessageFileName);
if (editorFile.absoluteFilePath() == changeFile.absoluteFilePath()) {
// Prompt the user. Force a prompt unless submit was actually invoked (that
// is, the editor was closed or shutdown).
@@ -1025,9 +995,10 @@ bool PerforcePlugin::editorAboutToClose(Core::IEditor *editor)
m_settings.setPromptToSubmit(wantsPrompt);
m_settings.toSettings(Core::ICore::instance()->settings());
}
core->fileManager()->blockFileChange(fileIFace);
Core::FileManager *fileManager = Core::ICore::instance()->fileManager();
fileManager->blockFileChange(fileIFace);
fileIFace->save();
core->fileManager()->unblockFileChange(fileIFace);
fileManager->unblockFileChange(fileIFace);
if (answer == VCSBase::VCSBaseSubmitEditor::SubmitConfirmed) {
QFile commitMessageFile(m_commitMessageFileName);
if (!commitMessageFile.open(QIODevice::ReadOnly|QIODevice::Text)) {
@@ -1071,7 +1042,7 @@ bool PerforcePlugin::editorAboutToClose(Core::IEditor *editor)
const QString output = QString::fromLocal8Bit(proc.readAll());
VCSBase::VCSBaseOutputWindow::instance()->append(output);
if (output.contains(QLatin1String("Out of date files must be resolved or reverted)"))) {
QMessageBox::warning(editor->widget(), tr("Pending change"), tr("Could not submit the change, because your workspace was out of date. Created a pending submit instead."));
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."));
}
QApplication::restoreOverrideCursor();
}
@@ -1219,5 +1190,10 @@ PerforcePlugin *PerforcePlugin::perforcePluginInstance()
return m_perforcePluginInstance;
}
PerforceVersionControl *PerforcePlugin::perforceVersionControl() const
{
return static_cast<PerforceVersionControl *>(versionControl());
}
Q_EXPORT_PLUGIN(PerforcePlugin)

View File

@@ -34,8 +34,7 @@
#include <coreplugin/editormanager/ieditorfactory.h>
#include <coreplugin/iversioncontrol.h>
#include <coreplugin/icorelistener.h>
#include <extensionsystem/iplugin.h>
#include <vcsbase/vcsbaseplugin.h>
#include <projectexplorer/projectexplorer.h>
#include <QtCore/QObject>
@@ -56,19 +55,6 @@ namespace Perforce {
namespace Internal {
class PerforceVersionControl;
class PerforcePlugin;
// Just a proxy for PerforcePlugin
class CoreListener : public Core::ICoreListener
{
Q_OBJECT
public:
CoreListener(PerforcePlugin *plugin) : m_plugin(plugin) { }
bool editorAboutToClose(Core::IEditor *editor);
bool coreAboutToClose() { return true; }
private:
PerforcePlugin *m_plugin;
};
struct PerforceResponse
{
@@ -78,7 +64,7 @@ struct PerforceResponse
QString message;
};
class PerforcePlugin : public ExtensionSystem::IPlugin
class PerforcePlugin : public VCSBase::VCSBasePlugin
{
Q_OBJECT
@@ -94,8 +80,6 @@ public:
bool vcsOpen(const QString &fileName);
bool vcsAdd(const QString &fileName);
bool vcsDelete(const QString &filename);
// Displays the message for the submit mesage
bool editorAboutToClose(Core::IEditor *editor);
void p4Diff(const QStringList &files, QString diffname = QString());
@@ -130,11 +114,14 @@ private slots:
void filelogCurrentFile();
void filelog();
void updateActions();
void submitCurrentLog();
void printPendingChanges();
void slotDiff(const QStringList &files);
protected:
virtual void updateActions(VCSBase::VCSBasePlugin::ActionState);
virtual bool submitEditorAboutToClose(VCSBase::VCSBaseSubmitEditor *submitEditor);
private:
QStringList environment() const;
@@ -163,6 +150,7 @@ private:
bool isCommitEditorOpen() const;
void updateCheckout(const QStringList &dirs = QStringList());
inline PerforceVersionControl *perforceVersionControl() const;
ProjectExplorer::ProjectExplorerPlugin *m_projectExplorer;
@@ -191,12 +179,11 @@ private:
QAction *m_undoAction;
QAction *m_redoAction;
QAction *m_menuAction;
static PerforcePlugin *m_perforcePluginInstance;
QString pendingChangesData();
PerforceVersionControl *m_versionControl;
PerforceSettings m_settings;
};

View File

@@ -44,19 +44,6 @@ QString PerforceVersionControl::name() const
return QLatin1String("perforce");
}
bool PerforceVersionControl::isEnabled() const
{
return m_enabled;
}
void PerforceVersionControl::setEnabled(bool enabled)
{
if (m_enabled != enabled) {
m_enabled = enabled;
emit enabledChanged(m_enabled);
}
}
bool PerforceVersionControl::supportsOperation(Operation operation) const
{
bool rc = true;

View File

@@ -45,9 +45,6 @@ public:
virtual QString name() const;
virtual bool isEnabled() const;
virtual void setEnabled(bool enabled);
bool managesDirectory(const QString &directory) const;
virtual QString findTopLevelForDirectory(const QString &directory) const;
@@ -59,9 +56,6 @@ public:
void emitRepositoryChanged(const QString &s);
void emitFilesChanged(const QStringList &l);
signals:
void enabledChanged(bool);
private:
bool m_enabled;
PerforcePlugin *m_plugin;

View File

@@ -1323,13 +1323,6 @@ void ProjectExplorerPlugin::setCurrent(Project *project, QString filePath, Node
if (projectChanged) {
if (debug)
qDebug() << "ProjectExplorer - currentProjectChanged(" << (project ? project->name() : "0") << ")";
// Enable the right VCS
if (const Core::IFile *projectFile = project ? project->file() : static_cast<const Core::IFile *>(0)) {
core->vcsManager()->setVCSEnabled(QFileInfo(projectFile->fileName()).absolutePath());
} else {
core->vcsManager()->setAllVCSEnabled();
}
emit currentProjectChanged(project);
updateActions();
}

View File

@@ -44,19 +44,6 @@ QString SubversionControl::name() const
return QLatin1String("subversion");
}
bool SubversionControl::isEnabled() const
{
return m_enabled;
}
void SubversionControl::setEnabled(bool enabled)
{
if (m_enabled != enabled) {
m_enabled = enabled;
emit enabledChanged(m_enabled);
}
}
bool SubversionControl::supportsOperation(Operation operation) const
{
bool rc = true;

View File

@@ -45,9 +45,6 @@ public:
explicit SubversionControl(SubversionPlugin *plugin);
virtual QString name() const;
virtual bool isEnabled() const;
virtual void setEnabled(bool enabled);
virtual bool managesDirectory(const QString &directory) const;
virtual QString findTopLevelForDirectory(const QString &directory) const;
@@ -59,9 +56,6 @@ public:
void emitRepositoryChanged(const QString &);
void emitFilesChanged(const QStringList &);
signals:
void enabledChanged(bool);
private:
bool m_enabled;
SubversionPlugin *m_plugin;

View File

@@ -180,8 +180,8 @@ static inline QStringList svnDirectories()
SubversionPlugin *SubversionPlugin::m_subversionPluginInstance = 0;
SubversionPlugin::SubversionPlugin() :
VCSBase::VCSBasePlugin(QLatin1String(Subversion::Constants::SUBVERSIONCOMMITEDITOR_KIND)),
m_svnDirectories(svnDirectories()),
m_versionControl(0),
m_projectExplorer(0),
m_addAction(0),
m_deleteAction(0),
@@ -199,6 +199,7 @@ SubversionPlugin::SubversionPlugin() :
m_submitDiffAction(0),
m_submitUndoAction(0),
m_submitRedoAction(0),
m_menuAction(0),
m_submitActionTriggered(false)
{
}
@@ -237,10 +238,8 @@ static inline Core::Command *createSeparator(QObject *parent,
return ami->registerAction(tmpaction, id, globalcontext);
}
bool SubversionPlugin::initialize(const QStringList &arguments, QString *errorMessage)
bool SubversionPlugin::initialize(const QStringList & /*arguments */, QString *errorMessage)
{
Q_UNUSED(arguments)
typedef VCSBase::VCSSubmitEditorFactory<SubversionSubmitEditor> SubversionSubmitEditorFactory;
typedef VCSBase::VCSEditorFactory<SubversionEditor> SubversionEditorFactory;
using namespace Constants;
@@ -248,19 +247,17 @@ bool SubversionPlugin::initialize(const QStringList &arguments, QString *errorMe
using namespace Core::Constants;
using namespace ExtensionSystem;
VCSBase::VCSBasePlugin::initialize(new SubversionControl(this));
m_subversionPluginInstance = this;
Core::ICore *core = Core::ICore::instance();
if (!core->mimeDatabase()->addMimeTypes(QLatin1String(":/trolltech.subversion/Subversion.mimetypes.xml"), errorMessage))
return false;
m_versionControl = new SubversionControl(this);
addAutoReleasedObject(m_versionControl);
if (QSettings *settings = core->settings())
m_settings.fromSettings(settings);
addAutoReleasedObject(new CoreListener(this));
addAutoReleasedObject(new SettingsPage);
addAutoReleasedObject(new SubversionSubmitEditorFactory(&submitParameters));
@@ -280,11 +277,7 @@ bool SubversionPlugin::initialize(const QStringList &arguments, QString *errorMe
ami->createMenu(QLatin1String(CMD_ID_SUBVERSION_MENU));
subversionMenu->menu()->setTitle(tr("&Subversion"));
toolsContainer->addMenu(subversionMenu);
if (QAction *ma = subversionMenu->menu()->menuAction()) {
ma->setEnabled(m_versionControl->isEnabled());
connect(m_versionControl, SIGNAL(enabledChanged(bool)), ma, SLOT(setVisible(bool)));
}
m_menuAction = subversionMenu->menu()->menuAction();
QList<int> globalcontext;
globalcontext << core->uniqueIDManager()->uniqueIdentifier(C_GLOBAL);
@@ -396,29 +389,21 @@ bool SubversionPlugin::initialize(const QStringList &arguments, QString *errorMe
m_submitRedoAction = new QAction(tr("&Redo"), this);
command = ami->registerAction(m_submitRedoAction, Core::Constants::REDO, svncommitcontext);
connect(Core::ICore::instance(), SIGNAL(contextChanged(Core::IContext *)), this, SLOT(updateActions()));
return true;
}
void SubversionPlugin::extensionsInitialized()
{
m_projectExplorer = ProjectExplorer::ProjectExplorerPlugin::instance();
if (m_projectExplorer) {
connect(m_projectExplorer,
SIGNAL(currentProjectChanged(ProjectExplorer::Project*)),
m_subversionPluginInstance, SLOT(updateActions()));
}
updateActions();
}
bool SubversionPlugin::editorAboutToClose(Core::IEditor *iEditor)
bool SubversionPlugin::submitEditorAboutToClose(VCSBase::VCSBaseSubmitEditor *submitEditor)
{
if ( !iEditor || !isCommitEditorOpen() || qstrcmp(Constants::SUBVERSIONCOMMITEDITOR, iEditor->kind()))
if (!isCommitEditorOpen())
return true;
Core::IFile *fileIFace = iEditor->file();
const SubversionSubmitEditor *editor = qobject_cast<SubversionSubmitEditor *>(iEditor);
Core::IFile *fileIFace = submitEditor->file();
const SubversionSubmitEditor *editor = qobject_cast<SubversionSubmitEditor *>(submitEditor);
if (!fileIFace || !editor)
return true;
@@ -511,8 +496,11 @@ SubversionSubmitEditor *SubversionPlugin::openSubversionSubmitEditor(const QStri
return submitEditor;
}
void SubversionPlugin::updateActions()
void SubversionPlugin::updateActions(VCSBase::VCSBasePlugin::ActionState as)
{
if (!VCSBase::VCSBasePlugin::enableMenuAction(as, m_menuAction))
return;
m_diffProjectAction->setEnabled(true);
m_commitAllAction->setEnabled(true);
m_statusAction->setEnabled(true);
@@ -574,7 +562,7 @@ void SubversionPlugin::revertCurrentFile()
const SubversionResponse revertResponse = runSvn(args, subversionShortTimeOut, true);
if (!revertResponse.error) {
fcb.setModifiedReload(true);
m_versionControl->emitFilesChanged(QStringList(file));
subVersionControl()->emitFilesChanged(QStringList(file));
}
}
@@ -752,9 +740,10 @@ void SubversionPlugin::updateProject()
args.push_back(QLatin1String(nonInteractiveOptionC));
args.append(topLevels);
const SubversionResponse response = runSvn(args, subversionLongTimeOut, true);
if (!response.error)
if (!response.error) {
foreach(const QString &repo, topLevels)
m_versionControl->emitRepositoryChanged(repo);
subVersionControl()->emitRepositoryChanged(repo);
}
}
void SubversionPlugin::annotateCurrentFile()
@@ -1086,4 +1075,9 @@ QString SubversionPlugin::findTopLevelForDirectoryI(const QString &directory) co
return QString();
}
SubversionControl *SubversionPlugin::subVersionControl() const
{
return static_cast<SubversionControl *>(versionControl());
}
Q_EXPORT_PLUGIN(SubversionPlugin)

View File

@@ -32,8 +32,7 @@
#include "subversionsettings.h"
#include <coreplugin/icorelistener.h>
#include <extensionsystem/iplugin.h>
#include <vcsbase/vcsbaseplugin.h>
QT_BEGIN_NAMESPACE
class QDir;
@@ -43,6 +42,7 @@ QT_END_NAMESPACE
namespace Core {
class IVersionControl;
class IEditor;
}
namespace Utils {
class ParameterAction;
@@ -52,6 +52,10 @@ namespace ProjectExplorer {
class ProjectExplorerPlugin;
}
namespace VCSBase {
class VCSBaseSubmitEditor;
}
namespace Subversion {
namespace Internal {
@@ -67,7 +71,7 @@ struct SubversionResponse
QString message;
};
class SubversionPlugin : public ExtensionSystem::IPlugin
class SubversionPlugin : public VCSBase::VCSBasePlugin
{
Q_OBJECT
@@ -77,7 +81,6 @@ public:
bool initialize(const QStringList &arguments, QString *error_message);
void extensionsInitialized();
bool editorAboutToClose(Core::IEditor *editor);
void svnDiff(const QStringList &files, QString diffname = QString());
@@ -95,7 +98,6 @@ public:
static SubversionPlugin *subversionPluginInstance();
private slots:
void updateActions();
void addCurrentFile();
void deleteCurrentFile();
void revertCurrentFile();
@@ -112,6 +114,10 @@ private slots:
void submitCurrentLog();
void diffFiles(const QStringList &);
protected:
virtual void updateActions(VCSBase::VCSBasePlugin::ActionState);
virtual bool submitEditorAboutToClose(VCSBase::VCSBaseSubmitEditor *submitEditor);
private:
inline bool isCommitEditorOpen() const;
QString currentFileName() const;
@@ -128,11 +134,11 @@ private:
void startCommit(const QStringList &files);
bool commit(const QString &messageFile, const QStringList &subVersionFileList);
void cleanCommitMessageFile();
inline SubversionControl *subVersionControl() const;
const QStringList m_svnDirectories;
SubversionSettings m_settings;
SubversionControl *m_versionControl;
QString m_commitMessageFileName;
ProjectExplorer::ProjectExplorerPlugin *m_projectExplorer;
@@ -154,32 +160,12 @@ private:
QAction *m_submitDiffAction;
QAction *m_submitUndoAction;
QAction *m_submitRedoAction;
QAction *m_menuAction;
bool m_submitActionTriggered;
static SubversionPlugin *m_subversionPluginInstance;
};
// Just a proxy for SubversionPlugin
class CoreListener : public Core::ICoreListener
{
Q_OBJECT
public:
CoreListener(SubversionPlugin *plugin) : m_plugin(plugin) { }
// Start commit when submit editor closes
bool editorAboutToClose(Core::IEditor *editor) {
return m_plugin->editorAboutToClose(editor);
}
// TODO: how to handle that ???
bool coreAboutToClose() {
return true;
}
private:
SubversionPlugin *m_plugin;
};
} // namespace Subversion
} // namespace Internal

View File

@@ -28,7 +28,6 @@
**************************************************************************/
#include "basevcseditorfactory.h"
#include "vcsbaseplugin.h"
#include "vcsbaseeditor.h"
#include <coreplugin/editormanager/editormanager.h>

View File

@@ -5,6 +5,8 @@ include(../../qtcreatorplugin.pri)
include(vcsbase_dependencies.pri)
HEADERS += vcsbase_global.h \
vcsbaseconstants.h \
vcsplugin.h \
corelistener.h \
vcsbaseplugin.h \
baseannotationhighlighter.h \
diffhighlighter.h \
@@ -25,7 +27,9 @@ HEADERS += vcsbase_global.h \
basecheckoutwizardpage.h \
vcsbaseoutputwindow.h
SOURCES += vcsbaseplugin.cpp \
SOURCES += vcsplugin.cpp \
vcsbaseplugin.cpp \
corelistener.cpp \
baseannotationhighlighter.cpp \
diffhighlighter.cpp \
vcsbasetextdocument.cpp \

View File

@@ -630,5 +630,41 @@ bool VCSBaseEditor::gotoLineOfEditor(Core::IEditor *e, int lineNumber)
return false;
}
// Return source file or directory string depending on parameters
// ('git diff XX' -> 'XX' , 'git diff XX file' -> 'XX/file').
QString VCSBaseEditor::getSource(const QString &workingDirectory,
const QString &fileName)
{
if (fileName.isEmpty())
return workingDirectory;
QString rc = workingDirectory;
const QChar slash = QLatin1Char('/');
if (!rc.isEmpty() && !(rc.endsWith(slash) || rc.endsWith(QLatin1Char('\\'))))
rc += slash;
rc += fileName;
return rc;
}
QString VCSBaseEditor::getSource(const QString &workingDirectory,
const QStringList &fileNames)
{
return fileNames.size() == 1 ?
getSource(workingDirectory, fileNames.front()) :
workingDirectory;
}
QString VCSBaseEditor::getTitleId(const QString &workingDirectory, const QStringList &fileNames)
{
switch (fileNames.size()) {
case 0:
return workingDirectory;
case 1:
return fileNames.front();
default:
break;
}
return fileNames.join(QLatin1String(", "));
}
} // namespace VCSBase

View File

@@ -128,6 +128,15 @@ public:
//Helper to go to line of editor if it is a text editor
static bool gotoLineOfEditor(Core::IEditor *e, int lineNumber);
// Convenience functions to determine the source to pass on to a diff
// editor if one has a call consisting of working directory and file arguments.
// ('git diff XX' -> 'XX' , 'git diff XX file' -> 'XX/file').
static QString getSource(const QString &workingDirectory, const QString &fileName);
static QString getSource(const QString &workingDirectory, const QStringList &fileNames);
// Convenience functions to determine an title/id to identify the editor
// from the arguments (','-joined arguments or directory).
static QString getTitleId(const QString &workingDirectory, const QStringList &fileNames);
signals:
void describeRequested(const QString &source, const QString &change);

View File

@@ -28,97 +28,420 @@
**************************************************************************/
#include "vcsbaseplugin.h"
#include "diffhighlighter.h"
#include "vcsbasesettingspage.h"
#include "nicknamedialog.h"
#include "vcsbaseoutputwindow.h"
#include "vcsbasesubmiteditor.h"
#include "vcsplugin.h"
#include "corelistener.h"
#include <coreplugin/icore.h>
#include <coreplugin/coreconstants.h>
#include <coreplugin/uniqueidmanager.h>
#include <coreplugin/mimedatabase.h>
#include <coreplugin/ifile.h>
#include <coreplugin/iversioncontrol.h>
#include <coreplugin/filemanager.h>
#include <coreplugin/vcsmanager.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/project.h>
#include <utils/qtcassert.h>
#include <QtCore/QtPlugin>
#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtCore/QSharedData>
#include <QtGui/QAction>
enum { debug = 0 };
namespace VCSBase {
namespace Internal {
VCSBasePlugin *VCSBasePlugin::m_instance = 0;
// Internal state created by the state listener and
// VCSBasePluginState.
VCSBasePlugin::VCSBasePlugin() :
m_settingsPage(0),
m_nickNameModel(0)
struct State {
void clearFile();
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 currentFileDirectory;
QString currentFileTopLevel;
QString currentProjectPath;
QString currentProjectName;
QString currentProjectTopLevel;
};
void State::clearFile()
{
currentFile.clear();
currentFileName.clear();
currentFileDirectory.clear();
currentFileTopLevel.clear();
}
void State::clearProject()
{
currentProjectPath.clear();
currentProjectName.clear();
currentProjectTopLevel.clear();
}
void State::clear()
{
clearFile();
clearProject();
}
bool State::equals(const State &rhs) const
{
return currentFile == rhs.currentFile
&& currentFileName == rhs.currentFileName
&& 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);
private slots:
void slotStateChanged();
};
StateListener::StateListener(QObject *parent) :
QObject(parent)
{
Core::ICore *core = Core::ICore::instance();
connect(core, SIGNAL(contextChanged(Core::IContext *)),
this, SLOT(slotStateChanged()));
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()));
}
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 or something? Ignore.
State state;
state.currentFile = core->fileManager()->currentFile();
if (!state.currentFile.isEmpty() && state.currentFile.startsWith(QDir::tempPath()))
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()) {
const QFileInfo fi(state.currentFile);
state.currentFileDirectory = fi.absolutePath();
state.currentFileName = fi.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 = QFileInfo(currentProject->file()->fileName()).absolutePath();
state.currentProjectName = currentProject->name();
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 (debug)
qDebug() << state << (vc ? vc->name() : 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::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::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 &submitEditorKind);
const QString m_submitEditorKind;
Core::IVersionControl *m_versionControl;
VCSBasePluginState m_state;
int m_actionState;
static Internal::StateListener *m_listener;
};
VCSBasePluginPrivate::VCSBasePluginPrivate(const QString &submitEditorKind) :
m_submitEditorKind(submitEditorKind),
m_versionControl(0),
m_actionState(-1)
{
}
Internal::StateListener *VCSBasePluginPrivate::m_listener = 0;
VCSBasePlugin::VCSBasePlugin(const QString &submitEditorKind) :
d(new VCSBasePluginPrivate(submitEditorKind))
{
m_instance = this;
}
VCSBasePlugin::~VCSBasePlugin()
{
m_instance = 0;
delete d;
}
bool VCSBasePlugin::initialize(const QStringList &arguments, QString *errorMessage)
void VCSBasePlugin::initialize(Core::IVersionControl *vc)
{
Q_UNUSED(arguments)
Q_UNUSED(errorMessage)
d->m_versionControl = vc;
addAutoReleasedObject(vc);
Core::ICore *core = Core::ICore::instance();
if (!core->mimeDatabase()->addMimeTypes(QLatin1String(":/vcsbase/VCSBase.mimetypes.xml"), errorMessage))
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::slotSubmitEditorAboutToClose(VCSBaseSubmitEditor *submitEditor, bool *result)
{
if (debug)
qDebug() << this << d->m_submitEditorKind << "Closing submit editor" << submitEditor << submitEditor->kind();
if (submitEditor->kind() == d->m_submitEditorKind)
*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)
{
if (debug)
qDebug() << "enableMenuAction" << menuAction->text() << as;
switch (as) {
case VCSBase::VCSBasePlugin::NoVCSEnabled:
menuAction->setVisible(true);
menuAction->setEnabled(false);
return false;
m_settingsPage = new VCSBaseSettingsPage;
addAutoReleasedObject(m_settingsPage);
addAutoReleasedObject(VCSBaseOutputWindow::instance());
connect(m_settingsPage, SIGNAL(settingsChanged(VCSBase::Internal::VCSBaseSettings)),
this, SIGNAL(settingsChanged(VCSBase::Internal::VCSBaseSettings)));
connect(m_settingsPage, SIGNAL(settingsChanged(VCSBase::Internal::VCSBaseSettings)),
this, SLOT(slotSettingsChanged()));
slotSettingsChanged();
case VCSBase::VCSBasePlugin::OtherVCSEnabled:
menuAction->setVisible(false);
return false;
case VCSBase::VCSBasePlugin::VCSEnabled:
menuAction->setVisible(true);
menuAction->setEnabled(true);
break;
}
return true;
}
void VCSBasePlugin::extensionsInitialized()
{
}
VCSBasePlugin *VCSBasePlugin::instance()
{
return m_instance;
}
VCSBaseSettings VCSBasePlugin::settings() const
{
return m_settingsPage->settings();
}
/* Delayed creation/update of the nick name model. */
QStandardItemModel *VCSBasePlugin::nickNameModel()
{
if (!m_nickNameModel) {
m_nickNameModel = NickNameDialog::createModel(this);
populateNickNameModel();
}
return m_nickNameModel;
}
void VCSBasePlugin::populateNickNameModel()
{
QString errorMessage;
if (!NickNameDialog::populateModelFromMailCapFile(settings().nickNameMailMap,
m_nickNameModel,
&errorMessage)) {
qWarning("%s", qPrintable(errorMessage));
}
}
void VCSBasePlugin::slotSettingsChanged()
{
if (m_nickNameModel)
populateNickNameModel();
}
} // namespace Internal
} // namespace VCSBase
Q_EXPORT_PLUGIN(VCSBase::Internal::VCSBasePlugin)
#include "vcsbaseplugin.moc"

View File

@@ -30,56 +30,146 @@
#ifndef VCSBASEPLUGIN_H
#define VCSBASEPLUGIN_H
#include "vcsbase_global.h"
#include <extensionsystem/iplugin.h>
#include <QtCore/QObject>
#include <QSharedDataPointer>
QT_BEGIN_NAMESPACE
class QStandardItemModel;
class QAction;
QT_END_NAMESPACE
namespace Core {
class IVersionControl;
}
namespace VCSBase {
namespace Internal {
struct State;
}
struct VCSBaseSettings;
class VCSBaseSettingsPage;
class VCSBaseSubmitEditor;
struct VCSBasePluginPrivate;
class VCSBasePluginStateData;
class VCSBasePlugin;
class VCSBasePlugin : public ExtensionSystem::IPlugin
/* VCSBasePlugin and VCSBasePluginState: Provide a base class for
* VCS plugins. It mainly takes care of maintaining the
* VCS-relevant state of Qt Creator which is a tuple of
*
* 1) Current file and it's version system control/top level
* 2) Current project and it's version system control/top level
*
* (reflected in VCSBasePluginState). The plugin connects to the
* relevant change signals in Qt Creator and calls the virtual
* updateActions() for the plugins to update their menu actions
* according to the new state. This is done centrally to avoid
* single plugins repeatedly invoking searches/QFileInfo on files,
* etc.
*
* If current file/project are managed
* by different version controls, the project is discarded and only
* the current file is taken into account, allowing to do a diff
* also when the project of a file is not opened.
*
* When triggering an action, a copy of the state should be made to
* keep it, as it may rapidly change due to context changes, etc.
*
* The class also detects the VCS plugin submit editor closing and calls
* the virtual submitEditorAboutToClose() to trigger the submit process. */
class VCSBASE_EXPORT VCSBasePluginState
{
public:
VCSBasePluginState();
VCSBasePluginState(const VCSBasePluginState &);
VCSBasePluginState &operator=(const VCSBasePluginState &);
~VCSBasePluginState();
void clear();
bool isEmpty() const;
bool hasFile() const;
bool hasProject() const;
bool hasTopLevel() const;
// Current file.
QString currentFile() const;
QString currentFileName() const;
QString currentFileDirectory() const;
QString currentFileTopLevel() const;
// Convenience: Returns file relative to top level.
QString relativeCurrentFile() const;
// Current project.
QString currentProjectPath() const;
QString currentProjectName() const;
QString currentProjectTopLevel() const;
/* Convenience: Returns project path relative to top level if it
* differs from top level (else empty()) as an argument list to do
* eg a 'vcs diff <args>' */
QStringList relativeCurrentProject() const;
// Top level directory for actions on the top level. Preferably
// the file one.
QString topLevel() const;
bool equals(const VCSBasePluginState &rhs) const;
friend VCSBASE_EXPORT QDebug operator<<(QDebug in, const VCSBasePluginState &state);
private:
friend class VCSBasePlugin;
bool equals(const Internal::State &s) const;
void setState(const Internal::State &s);
QSharedDataPointer<VCSBasePluginStateData> data;
};
VCSBASE_EXPORT QDebug operator<<(QDebug in, const VCSBasePluginState &state);
inline bool operator==(const VCSBasePluginState &s1, const VCSBasePluginState &s2)
{ return s1.equals(s2); }
inline bool operator!=(const VCSBasePluginState &s1, const VCSBasePluginState &s2)
{ return !s1.equals(s2); }
class VCSBASE_EXPORT VCSBasePlugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
protected:
explicit VCSBasePlugin(const QString &submitEditorKind);
void initialize(Core::IVersionControl *vc);
Core::IVersionControl *versionControl() const;
public:
VCSBasePlugin();
~VCSBasePlugin();
virtual ~VCSBasePlugin();
bool initialize(const QStringList &arguments, QString *error_message);
const VCSBasePluginState &currentState() const;
void extensionsInitialized();
protected:
enum ActionState { NoVCSEnabled, OtherVCSEnabled, VCSEnabled };
static VCSBasePlugin *instance();
// Implement to enable the plugin menu actions according to state.
virtual void updateActions(ActionState as) = 0;
// Implement to start the submit process.
virtual bool submitEditorAboutToClose(VCSBaseSubmitEditor *submitEditor) = 0;
VCSBaseSettings settings() const;
// Model of user nick names used for the submit
// editor. Stored centrally here to achieve delayed
// initialization and updating on settings change.
QStandardItemModel *nickNameModel();
signals:
void settingsChanged(const VCSBase::Internal::VCSBaseSettings& s);
// A helper to enable the VCS menu action according to state:
// NoVCSEnabled->(visible,disabled), OtherVCSEnabled->(invisible), else
// enabled. Returns whether actions should be set up further.
static bool enableMenuAction(ActionState as, QAction *in);
private slots:
void slotSettingsChanged();
void slotSubmitEditorAboutToClose(VCSBaseSubmitEditor *submitEditor, bool *result);
void slotStateChanged(const VCSBase::Internal::State &s, Core::IVersionControl *vc);
private:
void populateNickNameModel();
static VCSBasePlugin *m_instance;
VCSBaseSettingsPage *m_settingsPage;
QStandardItemModel *m_nickNameModel;
VCSBasePluginPrivate *d;
};
} // namespace Internal
} // namespace VCSBase
#endif // VCSBASEPLUGIN_H

View File

@@ -29,7 +29,7 @@
#include "vcsbasesubmiteditor.h"
#include "vcsbasesettings.h"
#include "vcsbaseplugin.h"
#include "vcsplugin.h"
#include "nicknamedialog.h"
#include "submiteditorfile.h"
@@ -75,7 +75,7 @@ namespace VCSBase {
static inline QString submitMessageCheckScript()
{
return Internal::VCSBasePlugin::instance()->settings().submitMessageCheckScript;
return Internal::VCSPlugin::instance()->settings().submitMessageCheckScript;
}
struct VCSBaseSubmitEditorPrivate
@@ -127,7 +127,7 @@ VCSBaseSubmitEditor::VCSBaseSubmitEditor(const VCSBaseSubmitEditorParameters *pa
connect(m_d->m_widget, SIGNAL(diffSelected(QStringList)), this, SLOT(slotDiffSelectedVCSFiles(QStringList)));
connect(m_d->m_widget->descriptionEdit(), SIGNAL(textChanged()), this, SLOT(slotDescriptionChanged()));
const Internal::VCSBaseSettings settings = Internal::VCSBasePlugin::instance()->settings();
const Internal::VCSBaseSettings settings = Internal::VCSPlugin::instance()->settings();
// Add additional context menu settings
if (!settings.submitMessageCheckScript.isEmpty() || !settings.nickNameMailMap.isEmpty()) {
QAction *sep = new QAction(this);
@@ -152,7 +152,7 @@ VCSBaseSubmitEditor::VCSBaseSubmitEditor(const VCSBaseSubmitEditorParameters *pa
// wrapping. etc
slotUpdateEditorSettings(settings);
connect(Internal::VCSBasePlugin::instance(),
connect(Internal::VCSPlugin::instance(),
SIGNAL(settingsChanged(VCSBase::Internal::VCSBaseSettings)),
this, SLOT(slotUpdateEditorSettings(VCSBase::Internal::VCSBaseSettings)));
@@ -199,7 +199,7 @@ void VCSBaseSubmitEditor::createUserFields(const QString &fieldConfigFile)
if (fields.empty())
return;
// Create a completer on user names
const QStandardItemModel *nickNameModel = Internal::VCSBasePlugin::instance()->nickNameModel();
const QStandardItemModel *nickNameModel = Internal::VCSPlugin::instance()->nickNameModel();
QCompleter *completer = new QCompleter(Internal::NickNameDialog::nickNameList(nickNameModel), this);
Utils::SubmitFieldWidget *fieldWidget = new Utils::SubmitFieldWidget;
@@ -487,7 +487,7 @@ VCSBaseSubmitEditor::PromptSubmitResult
QString VCSBaseSubmitEditor::promptForNickName()
{
if (!m_d->m_nickNameDialog)
m_d->m_nickNameDialog = new Internal::NickNameDialog(Internal::VCSBasePlugin::instance()->nickNameModel(), m_d->m_widget);
m_d->m_nickNameDialog = new Internal::NickNameDialog(Internal::VCSPlugin::instance()->nickNameModel(), m_d->m_widget);
if (m_d->m_nickNameDialog->exec() == QDialog::Accepted)
return m_d->m_nickNameDialog->nickName();
return QString();