Commit 9bc9fe73 authored by dt's avatar dt

File renaming

Reviewed-By: con
Reviewed-By: Friedemann Kleint

We now support renaming files. The version control system tries first to
rename, if that doesn't support or can't rename the file we do a normal
rename. (Note: git, hg, perforce > 2009.02 support renaming, cvs not.
(perforce untested)). We correctly notify all editors of the renamed
file and tell the project manager to rename the file in the project.

Note: Only the qt4projectmanager knows how to rename files.

Note: renaming folders, moving files to different folders, renaming
.pro/.pri files is not supported. Those things can be later added after
this has proven to work correctly in the simple case.

Also we don't do any actions based on the renaming like renaming
classes, changing include guards or #include lines.
parent e65e0117
......@@ -196,6 +196,12 @@ public:
}
}
void rename(const QString &newName) {
m_fileName = newName;
m_editor->editorInterface()->setDisplayName(QFileInfo(fileName()).fileName());
emit changed();
}
bool open(const QString &fileName) {
QFile file(fileName);
if (file.open(QIODevice::ReadOnly)) {
......@@ -305,6 +311,7 @@ public:
m_toolBar->addWidget(w);
connect(m_editor, SIGNAL(cursorPositionChanged(int)), this, SLOT(updateCursorPosition(int)));
connect(m_file, SIGNAL(changed()), this, SIGNAL(changed()));
}
~BinEditorInterface() {
delete m_editor;
......
......@@ -155,6 +155,9 @@ ImageViewer::ImageViewer(QObject *parent)
layout->setMargin(0);
m_imageView->setLayout(layout);
layout->addWidget(m_label, 0, 0, 1, 1);
connect(m_file, SIGNAL(changed()),
this, SIGNAL(changed()));
}
ImageViewer::~ImageViewer()
......
......@@ -70,6 +70,7 @@ public:
explicit ImageViewerFile(ImageViewer *parent = 0);
bool save(const QString &fileName = QString()) { Q_UNUSED(fileName); return false; }
void rename(const QString &newName) { m_fileName = newName; }
QString fileName() const { return m_fileName; }
QString defaultPath() const { return QString(); }
......
......@@ -595,6 +595,13 @@ bool CMakeFile::isSaveAsAllowed() const
return false;
}
void CMakeFile::rename(const QString &newName)
{
Q_ASSERT(false);
Q_UNUSED(newName);
// Can't happen....
}
Core::IFile::ReloadBehavior CMakeFile::reloadBehavior(ChangeTrigger state, ChangeType type) const
{
Q_UNUSED(state)
......
......@@ -196,6 +196,8 @@ public:
ReloadBehavior reloadBehavior(ChangeTrigger state, ChangeType type) const;
void reload(ReloadFlag flag, ChangeType type);
void rename(const QString &newName);
private:
CMakeProject *m_project;
QString m_fileName;
......
......@@ -43,8 +43,9 @@ bool CMakeProjectNode::hasBuildTargets() const
return true;
}
QList<ProjectExplorer::ProjectNode::ProjectAction> CMakeProjectNode::supportedActions() const
QList<ProjectExplorer::ProjectNode::ProjectAction> CMakeProjectNode::supportedActions(Node *node) const
{
Q_UNUSED(node);
return QList<ProjectAction>();
}
......
......@@ -42,7 +42,7 @@ class CMakeProjectNode : public ProjectExplorer::ProjectNode
public:
CMakeProjectNode(const QString &fileName);
virtual bool hasBuildTargets() const;
virtual QList<ProjectExplorer::ProjectNode::ProjectAction> supportedActions() const;
virtual QList<ProjectExplorer::ProjectNode::ProjectAction> supportedActions(Node *node) const;
virtual bool addSubProjects(const QStringList &proFilePaths);
virtual bool removeSubProjects(const QStringList &proFilePaths);
virtual bool addFiles(const ProjectExplorer::FileType fileType,
......
......@@ -123,6 +123,11 @@ struct FileManagerPrivate {
QString m_lastVisitedDirectory;
QString m_projectsDirectory;
bool m_useProjectsDirectory;
// When we are callling into a IFile
// we don't want to receive a changed()
// signal
// That makes the code easier
IFile *m_blockedIFile;
};
FileManagerPrivate::FileManagerPrivate(QObject *q, QMainWindow *mw) :
......@@ -131,10 +136,11 @@ FileManagerPrivate::FileManagerPrivate(QObject *q, QMainWindow *mw) :
m_blockActivated(false),
m_lastVisitedDirectory(QDir::currentPath()),
#ifdef Q_OS_MAC // Creator is in bizarre places when launched via finder.
m_useProjectsDirectory(true)
m_useProjectsDirectory(true),
#else
m_useProjectsDirectory(false)
m_useProjectsDirectory(false),
#endif
m_blockedIFile(0)
{
}
......@@ -252,6 +258,55 @@ void FileManager::updateFileInfo(IFile *file)
d->m_states[fixedname].lastUpdatedState.insert(file, item);
}
/// Dumps the state of the file manager's map
/// For debugging purposes
void FileManager::dump()
{
QMap<QString, Internal::FileState>::const_iterator it, end;
it = d->m_states.constBegin();
end = d->m_states.constEnd();
for (; it != end; ++it) {
qDebug()<<" ";
qDebug() << it.key();
qDebug() << it.value().expected.modified;
QMap<IFile *, Internal::FileStateItem>::const_iterator jt, jend;
jt = it.value().lastUpdatedState.constBegin();
jend = it.value().lastUpdatedState.constEnd();
for (; jt != jend; ++jt) {
qDebug() << jt.key() << jt.value().modified;
}
}
}
void FileManager::renamedFile(const QString &from, QString &to)
{
QString fixedFrom = fixFileName(from);
QString fixedTo = fixFileName(to);
if (d->m_states.contains(fixedFrom)) {
QTC_ASSERT(!d->m_states.contains(to), return);
d->m_states.insert(fixedTo, d->m_states.value(fixedFrom));
d->m_states.remove(fixedFrom);
QFileInfo fi(to);
d->m_states[fixedTo].expected.modified = fi.lastModified();
d->m_states[fixedTo].expected.permissions = fi.permissions();
d->m_fileWatcher->removePath(fixedFrom);
d->m_fileWatcher->addPath(fixedTo);
QMap<IFile *, Internal::FileStateItem>::iterator it, end;
it = d->m_states[fixedTo].lastUpdatedState.begin();
end = d->m_states[fixedTo].lastUpdatedState.end();
for ( ; it != end; ++it) {
d->m_blockedIFile = it.key();
it.key()->rename(to);
d->m_blockedIFile = it.key();
it.value().modified = fi.lastModified();
}
}
}
///
/// Does not use file->fileName, as such is save to use
/// with renamed files and deleted files
......@@ -336,6 +391,10 @@ bool FileManager::removeFile(IFile *file)
void FileManager::checkForNewFileName()
{
IFile *file = qobject_cast<IFile *>(sender());
// We modified the IFile
// Trust the other code to also update the m_states map
if (file == d->m_blockedIFile)
return;
QTC_ASSERT(file, return);
const QString &fileName = fixFileName(file->fileName());
......@@ -751,6 +810,7 @@ void FileManager::checkForReload()
end = lastUpdated.constEnd();
for ( ; it != end; ++it) {
IFile *file = it.key();
d->m_blockedIFile = file;
// Compare
if (it.value().modified == fi.lastModified()
&& it.value().permissions == fi.permissions()) {
......@@ -832,6 +892,7 @@ void FileManager::checkForReload()
}
updateFileInfo(file);
d->m_blockedIFile = 0;
}
}
......
......@@ -63,6 +63,8 @@ public:
bool isFileManaged(const QString &fileName) const;
QList<IFile *> modifiedFiles() const;
void renamedFile(const QString &from, QString &to);
void blockFileChange(IFile *file);
void unblockFileChange(IFile *file);
......@@ -74,6 +76,7 @@ public:
QStringList recentFiles() const;
void saveRecentFiles();
// current file
void setCurrentFile(const QString &filePath);
QString currentFile() const;
......@@ -128,6 +131,7 @@ private slots:
void syncWithEditor(Core::IContext *context);
private:
void dump();
void addFileInfo(IFile *file);
void removeFileInfo(IFile *file);
void removeFileInfo(const QString &fileName, IFile *file);
......
......@@ -87,6 +87,7 @@ public:
virtual ReloadBehavior reloadBehavior(ChangeTrigger state, ChangeType type) const = 0;
virtual void reload(ReloadFlag flag, ChangeType type) = 0;
virtual void rename(const QString &newName) = 0;
virtual void checkPermissions() {}
......
......@@ -42,7 +42,7 @@ class CORE_EXPORT IVersionControl : public QObject
Q_OBJECT
public:
enum Operation {
AddOperation, DeleteOperation, OpenOperation,
AddOperation, DeleteOperation, OpenOperation, MoveOperation,
CreateRepositoryOperation,
SnapshotOperations,
AnnotateOperation
......@@ -101,6 +101,12 @@ public:
*/
virtual bool vcsDelete(const QString &filename) = 0;
/*!
* Called to rename a file, should do the actual on disk renaming
* (e.g. git mv, svn move, p4 move)
*/
virtual bool vcsMove(const QString &from, const QString &to) = 0;
/*!
* Called to initialize the version control system in a directory.
*/
......
......@@ -54,6 +54,7 @@ bool CVSControl::supportsOperation(Operation operation) const
case DeleteOperation:
case AnnotateOperation:
break;
case MoveOperation:
case OpenOperation:
case CreateRepositoryOperation:
case SnapshotOperations:
......@@ -81,6 +82,11 @@ bool CVSControl::vcsDelete(const QString &fileName)
return m_plugin->vcsDelete(fi.absolutePath(), fi.fileName());
}
bool CVSControl::vcsMove(const QString &from, const QString &to)
{
return false;
}
bool CVSControl::vcsCreateRepository(const QString &)
{
return false;
......
......@@ -52,6 +52,7 @@ public:
virtual bool vcsOpen(const QString &fileName);
virtual bool vcsAdd(const QString &fileName);
virtual bool vcsDelete(const QString &filename);
virtual bool vcsMove(const QString &from, const QString &to);
virtual bool vcsCreateRepository(const QString &directory);
virtual QString vcsCreateSnapshot(const QString &topLevel);
virtual QStringList vcsSnapshots(const QString &topLevel);
......
......@@ -81,6 +81,7 @@ FormWindowEditor::FormWindowEditor(Internal::DesignerXmlEditor *editor,
connect(&(d->m_file), SIGNAL(reload(QString)), this, SLOT(slotOpen(QString)));
// Force update of open editors model.
connect(&(d->m_file), SIGNAL(saved()), this, SIGNAL(changed()));
connect(&(d->m_file), SIGNAL(changed()), this, SIGNAL(changed()));
}
FormWindowEditor::~FormWindowEditor()
......
......@@ -91,6 +91,15 @@ bool FormWindowFile::save(const QString &name /*= QString()*/)
return true;
}
void FormWindowFile::rename(const QString &newName)
{
m_formWindow->setFileName(newName);
QFileInfo fi(newName);
m_fileName = fi.absoluteFilePath();
emit setDisplayName(fi.fileName());
emit changed();
}
QString FormWindowFile::fileName() const
{
return m_fileName;
......
......@@ -60,6 +60,7 @@ public:
virtual QString defaultPath() const;
virtual QString suggestedFileName() const;
virtual QString mimeType() const;
virtual void rename(const QString &newName);
// Internal
void setSuggestedFileName(const QString &fileName);
......
......@@ -591,6 +591,13 @@ bool GenericProjectFile::isSaveAsAllowed() const
return false;
}
void GenericProjectFile::rename(const QString &newName)
{
// Can't happen
Q_UNUSED(newName);
Q_ASSERT(false);
}
Core::IFile::ReloadBehavior GenericProjectFile::reloadBehavior(ChangeTrigger state, ChangeType type) const
{
Q_UNUSED(state)
......
......@@ -161,6 +161,7 @@ public:
virtual bool isModified() const;
virtual bool isReadOnly() const;
virtual bool isSaveAsAllowed() const;
virtual void rename(const QString &newName);
ReloadBehavior reloadBehavior(ChangeTrigger state, ChangeType type) const;
void reload(ReloadFlag flag, ChangeType type);
......
......@@ -170,8 +170,9 @@ bool GenericProjectNode::hasBuildTargets() const
return true;
}
QList<ProjectExplorer::ProjectNode::ProjectAction> GenericProjectNode::supportedActions() const
QList<ProjectExplorer::ProjectNode::ProjectAction> GenericProjectNode::supportedActions(Node *node) const
{
Q_UNUSED(node);
return QList<ProjectAction>()
<< AddFile
<< RemoveFile;
......
......@@ -55,7 +55,7 @@ public:
virtual bool hasBuildTargets() const;
virtual QList<ProjectExplorer::ProjectNode::ProjectAction> supportedActions() const;
virtual QList<ProjectExplorer::ProjectNode::ProjectAction> supportedActions(Node *node) const;
virtual bool addSubProjects(const QStringList &proFilePaths);
virtual bool removeSubProjects(const QStringList &proFilePaths);
......
......@@ -525,6 +525,27 @@ bool GitClient::synchronousDelete(const QString &workingDirectory,
return rc;
}
bool GitClient::synchronousMove(const QString &workingDirectory,
const QString &from,
const QString &to)
{
if (Git::Constants::debug)
qDebug() << Q_FUNC_INFO << workingDirectory << from << to;
QByteArray outputText;
QByteArray errorText;
QStringList arguments;
arguments << QLatin1String("mv");
arguments << (from);
arguments << (to);
const bool rc = synchronousGit(workingDirectory, arguments, &outputText, &errorText);
if (!rc) {
const QString errorMessage = tr("Unable to move from %1 to %2: %3").
arg(from, to, commandOutputFromLocal8Bit(errorText));
outputWindow()->appendError(errorMessage);
}
return rc;
}
bool GitClient::synchronousReset(const QString &workingDirectory,
const QStringList &files,
QString *errorMessage)
......
......@@ -101,6 +101,9 @@ public:
bool synchronousDelete(const QString &workingDirectory,
bool force,
const QStringList &files);
bool synchronousMove(const QString &workingDirectory,
const QString &from,
const QString &to);
bool synchronousReset(const QString &workingDirectory,
const QStringList &files = QStringList(),
QString *errorMessage = 0);
......
......@@ -75,6 +75,9 @@ bool GitVersionControl::supportsOperation(Operation operation) const
case DeleteOperation:
rc = true;
break;
case MoveOperation:
rc = true;
break;
case OpenOperation:
break;
case CreateRepositoryOperation:
......@@ -107,6 +110,13 @@ bool GitVersionControl::vcsDelete(const QString & fileName)
return gitClient()->synchronousDelete(fi.absolutePath(), true, QStringList(fi.fileName()));
}
bool GitVersionControl::vcsMove(const QString &from, const QString &to)
{
const QFileInfo fromInfo(from);
const QFileInfo toInfo(to);
return gitClient()->synchronousMove(fromInfo.absolutePath(), fromInfo.absoluteFilePath(), toInfo.absoluteFilePath());
}
bool GitVersionControl::vcsCreateRepository(const QString &directory)
{
return gitClient()->synchronousInit(directory);
......
......@@ -53,6 +53,7 @@ public:
virtual bool vcsOpen(const QString &fileName);
virtual bool vcsAdd(const QString &fileName);
virtual bool vcsDelete(const QString &filename);
virtual bool vcsMove(const QString &from, const QString &to);
virtual bool vcsCreateRepository(const QString &directory);
virtual QString vcsCreateSnapshot(const QString &topLevel);
virtual QStringList vcsSnapshots(const QString &topLevel);
......
......@@ -95,6 +95,14 @@ bool MercurialClient::remove(const QString &workingDir, const QString &filename)
return executeHgSynchronously(workingDir, args, &stdOut);
}
bool MercurialClient::move(const QString &workingDir, const QString &from, const QString &to)
{
QStringList args;
args << QLatin1String("rename") << from << to;
QByteArray stdOut;
return executeHgSynchronously(workingDir, args, &stdOut);
}
bool MercurialClient::manifestSync(const QString &repository, const QString &relativeFilename)
{
// This only works when called from the repo and outputs paths relative to it.
......
......@@ -62,6 +62,7 @@ public:
~MercurialClient();
bool add(const QString &workingDir, const QString &fileName);
bool remove(const QString &workingDir, const QString &fileName);
bool move(const QString &workingDir, const QString &from, const QString &to);
bool manifestSync(const QString &repository, const QString &filename);
QString branchQuerySync(const QString &repositoryRoot);
bool parentRevisionsSync(const QString &workingDirectory,
......
......@@ -65,6 +65,7 @@ bool MercurialControl::supportsOperation(Operation operation) const
switch (operation) {
case Core::IVersionControl::AddOperation:
case Core::IVersionControl::DeleteOperation:
case Core::IVersionControl::MoveOperation:
case Core::IVersionControl::CreateRepositoryOperation:
case Core::IVersionControl::AnnotateOperation:
break;
......@@ -94,6 +95,13 @@ bool MercurialControl::vcsDelete(const QString &filename)
return mercurialClient->remove(fi.absolutePath(), fi.fileName());
}
bool MercurialControl::vcsMove(const QString &from, const QString &to)
{
const QFileInfo fromInfo(from);
const QFileInfo toInfo(to);
return mercurialClient->move(fromInfo.absolutePath(), fromInfo.absoluteFilePath(), toInfo.absoluteFilePath());
}
bool MercurialControl::vcsCreateRepository(const QString &directory)
{
return mercurialClient->createRepositorySync(directory);
......
......@@ -56,6 +56,7 @@ public:
bool vcsOpen(const QString &fileName);
bool vcsAdd(const QString &filename);
bool vcsDelete(const QString &filename);
bool vcsMove(const QString &from, const QString &to);
bool vcsCreateRepository(const QString &directory);
QString vcsCreateSnapshot(const QString &topLevel);
QStringList vcsSnapshots(const QString &topLevel);
......
......@@ -925,6 +925,22 @@ bool PerforcePlugin::vcsDelete(const QString &workingDir, const QString &fileNam
return !deleteResult.error;
}
bool PerforcePlugin::vcsMove(const QString &workingDir, const QString &from, const QString &to)
{
// TODO verify this works
QStringList args;
args << QLatin1String("edit") << from;
const PerforceResponse editResult = runP4Cmd(workingDir, args,
CommandToWindow|StdOutToWindow|StdErrToWindow|ErrorToWindow);
if (editResult.error)
return false;
args.clear();
args << QLatin1String("move") << from << to;
const PerforceResponse moveResult = runP4Cmd(workingDir, args,
CommandToWindow|StdOutToWindow|StdErrToWindow|ErrorToWindow);
return !moveResult.error;
}
static QString formatCommand(const QString &cmd, const QStringList &args)
{
const QChar blank = QLatin1Char(' ');
......
......@@ -89,6 +89,7 @@ public:
bool vcsOpen(const QString &workingDir, const QString &fileName);
bool vcsAdd(const QString &workingDir, const QString &fileName);
bool vcsDelete(const QString &workingDir, const QString &filename);
bool vcsMove(const QString &workingDir, const QString &from, const QString &to);
void p4Diff(const QString &workingDir, const QStringList &files);
......
......@@ -53,6 +53,7 @@ bool PerforceVersionControl::supportsOperation(Operation operation) const
switch (operation) {
case AddOperation:
case DeleteOperation:
case MoveOperation:
case OpenOperation:
case AnnotateOperation:
return true;
......@@ -81,6 +82,13 @@ bool PerforceVersionControl::vcsDelete(const QString &fileName)
return m_plugin->vcsDelete(fi.absolutePath(), fi.fileName());
}
bool PerforceVersionControl::vcsMove(const QString &from, const QString &to)
{
const QFileInfo fromInfo(from);
const QFileInfo toInfo(to);
return m_plugin->vcsMove(fromInfo.absolutePath(), fromInfo.absoluteFilePath(), toInfo.absoluteFilePath());
}
bool PerforceVersionControl::vcsCreateRepository(const QString &)
{
return false;
......
......@@ -52,6 +52,7 @@ public:
virtual bool vcsOpen(const QString &fileName);
virtual bool vcsAdd(const QString &fileName);
virtual bool vcsDelete(const QString &filename);
virtual bool vcsMove(const QString &from, const QString &to);
virtual bool vcsCreateRepository(const QString &directory);
virtual QString vcsCreateSnapshot(const QString &topLevel);
virtual QStringList vcsSnapshots(const QString &topLevel);
......
......@@ -690,13 +690,15 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
globalcontext);
mfilec->addAction(cmd, Constants::G_FILE_OTHER);
// renamefile action (TODO: Not supported yet)
// renamefile action
d->m_renameFileAction = new QAction(tr("Rename"), this);
cmd = am->registerAction(d->m_renameFileAction, ProjectExplorer::Constants::RENAMEFILE,
globalcontext);
mfilec->addAction(cmd, Constants::G_FILE_OTHER);
d->m_renameFileAction->setEnabled(false);
d->m_renameFileAction->setVisible(false);
// Not yet used by anyone, so hide for now
// mfolder->addAction(cmd, Constants::G_FOLDER_FILES);
// msubProject->addAction(cmd, Constants::G_FOLDER_FILES);
// mproject->addAction(cmd, Constants::G_FOLDER_FILES);
// target selector
d->m_projectSelectorAction = new QAction(this);
......@@ -1232,7 +1234,7 @@ void ProjectExplorerPlugin::showContextMenu(const QPoint &globalPos, Node *node)
contextMenu = d->m_sessionContextMenu;
}
updateContextMenuActions();
updateContextMenuActions(d->m_currentNode);
if (contextMenu && contextMenu->actions().count() > 0) {
contextMenu->popup(globalPos);
}
......@@ -1915,19 +1917,24 @@ void ProjectExplorerPlugin::goToTaskWindow()
d->m_buildManager->gotoTaskWindow();
}
void ProjectExplorerPlugin::updateContextMenuActions()
void ProjectExplorerPlugin::updateContextMenuActions(Node *node)
{
d->m_addExistingFilesAction->setEnabled(false);
d->m_addNewFileAction->setEnabled(false);
d->m_removeFileAction->setEnabled(false);
if (FolderNode *folderNode = qobject_cast<FolderNode*>(d->m_currentNode)) {
const bool addFilesEnabled = folderNode->projectNode()->supportedActions().contains(ProjectNode::AddFile);
QList<ProjectNode::ProjectAction> actions =
d->m_currentNode->projectNode()->supportedActions(node);
if (qobject_cast<FolderNode*>(d->m_currentNode)) {
bool addFilesEnabled = actions.contains(ProjectNode::AddFile);
d->m_addExistingFilesAction->setEnabled(addFilesEnabled);
d->m_addNewFileAction->setEnabled(addFilesEnabled);
} else if (FileNode *fileNode = qobject_cast<FileNode*>(d->m_currentNode)) {
const bool removeFileEnabled = fileNode->projectNode()->supportedActions().contains(ProjectNode::RemoveFile);
d->m_renameFileAction->setEnabled(actions.contains(ProjectNode::Rename));
} else if (qobject_cast<FileNode*>(d->m_currentNode)) {
bool removeFileEnabled = actions.contains(ProjectNode::RemoveFile);
d->m_removeFileAction->setEnabled(removeFileEnabled);
d->m_renameFileAction->setEnabled(actions.contains(ProjectNode::Rename));