Commit 9310b652 authored by Daniel Teske's avatar Daniel Teske
Browse files

Resource file in project tree



Task-number: QTCREATORBUG-1346
Change-Id: I0cbb5633ef06a4762c48af46d9da551c67259d70
Reviewed-by: default avatarDaniel Teske <daniel.teske@digia.com>
parent 1e998432
......@@ -1734,6 +1734,11 @@ QList<RunControl *> ProjectExplorerPlugin::runControls() const
return d->m_outputPane->runControls();
}
void ProjectExplorerPlugin::initiateInlineRenaming()
{
renameFile();
}
void ProjectExplorerPlugin::buildQueueFinished(bool success)
{
if (debug)
......@@ -2758,6 +2763,12 @@ QString pathOrDirectoryFor(Node *node, bool dir)
}
} else {
QFileInfo fi(path);
// remove any /suffixes, which e.g. ResourceNode uses
// Note this should be removed again by making node->path() a true path again
// That requires changes in both the VirtualFolderNode and ResourceNode
while (!fi.exists() && !fi.isRoot())
fi.setFile(fi.absolutePath());
if (dir)
location = fi.isDir() ? fi.absoluteFilePath() : fi.absolutePath();
else
......@@ -2833,8 +2844,7 @@ void ProjectExplorerPlugin::addExistingDirectory()
{
QTC_ASSERT(d->m_currentNode, return);
const QString path = QFileInfo(d->m_currentNode->path()).absolutePath();
SelectableFilesDialogAddDirectory dialog(path, QStringList(), Core::ICore::mainWindow());
SelectableFilesDialogAddDirectory dialog(directoryFor(d->m_currentNode), QStringList(), Core::ICore::mainWindow());
if (dialog.exec() == QDialog::Accepted)
addExistingFiles(dialog.selectedFiles());
......@@ -2981,16 +2991,13 @@ void ProjectExplorerPlugin::renameFile()
void ProjectExplorerPlugin::renameFile(Node *node, const QString &to)
{
FileNode *fileNode = qobject_cast<FileNode *>(node);
if (!fileNode)
return;
QString orgFilePath = QFileInfo(node->path()).absoluteFilePath();
QString dir = QFileInfo(orgFilePath).absolutePath();
QString newFilePath = dir + QLatin1Char('/') + to;
if (Core::FileUtils::renameFile(orgFilePath, newFilePath)) {
// Tell the project plugin about rename
FolderNode *folderNode = fileNode->parentFolderNode();
FolderNode *folderNode = node->parentFolderNode();
if (!folderNode->renameFile(orgFilePath, newFilePath)) {
QMessageBox::warning(ICore::mainWindow(), tr("Project Editing Failed"),
tr("The file %1 was renamed to %2, but the project file %3 could not be automatically changed.")
......
......@@ -122,8 +122,13 @@ public:
QList<RunControl *> runControls() const;
void initiateInlineRenaming();
static QString displayNameForStepId(Core::Id stepId);
static QString directoryFor(Node *node);
static QString pathFor(Node *node);
signals:
void runControlStarted(ProjectExplorer::RunControl *rc);
void runControlFinished(ProjectExplorer::RunControl *rc);
......@@ -270,8 +275,6 @@ private slots:
#endif
private:
QString directoryFor(Node *node);
QString pathFor(Node *node);
void deploy(QList<Project *>);
int queue(QList<Project *>, QList<Core::Id> stepIds);
void updateContextMenuActions();
......
......@@ -51,6 +51,8 @@
#include <qtsupport/qtkitinformation.h>
#include <qtsupport/uicodemodelsupport.h>
#include <resourceeditor/resourcenode.h>
#include <cpptools/cppmodelmanagerinterface.h>
#include <cpptools/cpptoolsconstants.h>
......@@ -394,12 +396,15 @@ struct InternalNode
// Makes the projectNode's subtree below the given folder match this internal node's subtree
void updateSubFolders(ProjectExplorer::FolderNode *folder)
{
updateFiles(folder, type);
if (type == ProjectExplorer::ResourceType)
updateResourceFiles(folder);
else
updateFiles(folder, type);
// updateFolders
QMultiMap<QString, FolderNode *> existingFolderNodes;
foreach (FolderNode *node, folder->subFolderNodes())
if (node->nodeType() != ProjectNodeType)
if (node->nodeType() != ProjectNodeType && !qobject_cast<ResourceEditor::ResourceTopLevelNode *>(node))
existingFolderNodes.insert(node->path(), node);
QList<FolderNode *> foldersToRemove;
......@@ -507,6 +512,37 @@ struct InternalNode
folder->removeFileNodes(filesToRemove);
folder->addFileNodes(nodesToAdd);
}
// Makes the folder's files match this internal node's file list
void updateResourceFiles(FolderNode *folder)
{
QList<ProjectExplorer::FolderNode *> existingResourceNodes; // for resource special handling
foreach (FolderNode *folderNode, folder->subFolderNodes()) {
if (ResourceEditor::ResourceTopLevelNode *rn = qobject_cast<ResourceEditor::ResourceTopLevelNode *>(folderNode))
existingResourceNodes << rn;
}
QList<ProjectExplorer::FolderNode *> resourcesToRemove;
QStringList resourcesToAdd;
SortByPath sortByPath;
qSort(files.begin(), files.end(), sortByPath);
qSort(existingResourceNodes.begin(), existingResourceNodes.end(), sortByPath);
ProjectExplorer::compareSortedLists(existingResourceNodes, files, resourcesToRemove, resourcesToAdd, sortByPath);
QList<FolderNode *> nodesToAdd;
nodesToAdd.reserve(resourcesToAdd.size());
foreach (const QString &file, resourcesToAdd)
nodesToAdd.append(new ResourceEditor::ResourceTopLevelNode(file, folder));
folder->removeFolderNodes(resourcesToRemove);
folder->addFolderNodes(nodesToAdd);
foreach (FolderNode *fn, nodesToAdd)
qobject_cast<ResourceEditor::ResourceTopLevelNode *>(fn)->update();
}
};
}
......@@ -866,7 +902,8 @@ QList<ProjectExplorer::ProjectAction> QmakePriFileNode::supportedActions(Node *n
}
ProjectExplorer::FileNode *fileNode = qobject_cast<FileNode *>(node);
if (fileNode && fileNode->fileType() != ProjectExplorer::ProjectFileType)
if ((fileNode && fileNode->fileType() != ProjectExplorer::ProjectFileType)
|| qobject_cast<ResourceEditor::ResourceTopLevelNode *>(node))
actions << ProjectExplorer::Rename;
......
......@@ -10,6 +10,7 @@ QTC_PLUGIN_DEPENDS += \
qtsupport \
texteditor \
cpptools \
qmljstools
qmljstools \
resourceeditor
QTC_PLUGIN_RECOMMENDS += \
designer
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#ifndef RESOUCE_GLOBAL_H
#define RESOUCE_GLOBAL_H
#include <qglobal.h>
#if defined(RESOURCE_LIBRARY)
# define RESOURCE_EXPORT Q_DECL_EXPORT
#else
# define RESOURCE_EXPORT Q_DECL_IMPORT
#endif
#endif // RESOUCE_GLOBAL_H
......@@ -5,11 +5,16 @@ HEADERS += resourceeditorfactory.h \
resourceeditorplugin.h \
resourcewizard.h \
resourceeditorw.h \
resourceeditorconstants.h
resourceeditorconstants.h \
resource_global.h \
resourcenode.h
SOURCES +=resourceeditorfactory.cpp \
resourceeditorplugin.cpp \
resourcewizard.cpp \
resourceeditorw.cpp
resourceeditorw.cpp \
resourcenode.cpp
RESOURCES += resourceeditor.qrc
DEFINES += RESOURCE_LIBRARY
......@@ -20,6 +20,7 @@ QtcPlugin {
"resourceeditorplugin.cpp", "resourceeditorplugin.h",
"resourceeditorw.cpp", "resourceeditorw.h",
"resourcewizard.cpp", "resourcewizard.h",
"resource_global.h", "resourcenode.cpp", "resourcenode.h"
]
}
......
......@@ -5,3 +5,4 @@ QTC_LIB_DEPENDS += \
utils
QTC_PLUGIN_DEPENDS += \
coreplugin \
projectexplorer
......@@ -41,6 +41,17 @@ const char REFRESH[] = "ResourceEditor.Refresh";
const char C_RESOURCE_MIMETYPE[] = "application/vnd.qt.xml.resource";
const char C_ADD_PREFIX[] = "ResourceEditor.AddPrefix";
const char C_REMOVE_PREFIX[] = "ResourceEditor.RemovePrefix";
const char C_RENAME_PREFIX[] = "ResourceEditor.RenamePrefix";
const char C_REMOVE_FILE[] = "ResourceEditor.RemoveFile";
const char C_RENAME_FILE[] = "ResourceEditor.RenameFile";
const char C_OPEN_EDITOR[] = "ResourceEditor.OpenEditor";
const char C_OPEN_TEXT_EDITOR[] = "ResourceEditor.OpenTextEditor";
} // namespace Constants
} // namespace ResourceEditor
......
......@@ -33,13 +33,19 @@
#include "resourceeditorconstants.h"
#include "resourcewizard.h"
#include "resourceeditorfactory.h"
#include "resourcenode.h"
#include <coreplugin/icore.h>
#include <coreplugin/mimedatabase.h>
#include <coreplugin/coreconstants.h>
#include <coreplugin/id.h>
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/editormanager/editormanager.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/projectnodes.h>
#include <extensionsystem/pluginmanager.h>
#include <utils/qtcassert.h>
......@@ -47,9 +53,55 @@
#include <QtPlugin>
#include <QCoreApplication>
#include <QAction>
#include <QDebug>
#include <QInputDialog>
#include <QMessageBox>
#include <QFormLayout>
#include <QDialogButtonBox>
using namespace ResourceEditor::Internal;
class PrefixLangDialog : public QDialog
{
public:
PrefixLangDialog(const QString &title, const QString &prefix, const QString &lang, QWidget *parent)
: QDialog(parent)
{
setWindowTitle(title);
QFormLayout *layout = new QFormLayout(this);
m_prefixLineEdit = new QLineEdit(this);
m_prefixLineEdit->setText(prefix);
layout->addRow(tr("Prefix:"), m_prefixLineEdit);
m_langLineEdit = new QLineEdit(this);
m_langLineEdit->setText(lang);
layout->addRow(tr("Language:"), m_langLineEdit);
QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok
| QDialogButtonBox::Cancel,
this);
layout->addWidget(buttons);
connect(buttons, SIGNAL(accepted()),
this, SLOT(accept()));
connect(buttons, SIGNAL(rejected()),
this, SLOT(reject()));
}
QString prefix() const
{
return m_prefixLineEdit->text();
}
QString lang() const
{
return m_langLineEdit->text();
}
private:
QLineEdit *m_prefixLineEdit;
QLineEdit *m_langLineEdit;
};
ResourceEditorPlugin::ResourceEditorPlugin() :
m_redoAction(0),
m_undoAction(0)
......@@ -88,6 +140,56 @@ bool ResourceEditorPlugin::initialize(const QStringList &arguments, QString *err
connect(m_redoAction, SIGNAL(triggered()), this, SLOT(onRedo()));
connect(m_refreshAction, SIGNAL(triggered()), this, SLOT(onRefresh()));
Core::Context projectTreeContext(ProjectExplorer::Constants::C_PROJECT_TREE);
Core::ActionContainer *folderContextMenu =
Core::ActionManager::actionContainer(ProjectExplorer::Constants::M_FOLDERCONTEXT);
Core::Command *command = 0;
m_addPrefix = new QAction(tr("Add Prefix..."), this);
command = Core::ActionManager::registerAction(m_addPrefix, Constants::C_ADD_PREFIX, projectTreeContext);
folderContextMenu->addAction(command, ProjectExplorer::Constants::G_FOLDER_FILES);
connect(m_addPrefix, SIGNAL(triggered()), this, SLOT(addPrefixContextMenu()));
m_renamePrefix = new QAction(tr("Change Prefix..."), this);
command = Core::ActionManager::registerAction(m_renamePrefix, Constants::C_RENAME_PREFIX, projectTreeContext);
folderContextMenu->addAction(command, ProjectExplorer::Constants::G_FOLDER_FILES);
connect(m_renamePrefix, SIGNAL(triggered()), this, SLOT(renamePrefixContextMenu()));
m_removePrefix = new QAction(tr("Remove Prefix..."), this);
command = Core::ActionManager::registerAction(m_removePrefix, Constants::C_REMOVE_PREFIX, projectTreeContext);
folderContextMenu->addAction(command, ProjectExplorer::Constants::G_FOLDER_FILES);
connect(m_removePrefix, SIGNAL(triggered()), this, SLOT(removePrefixContextMenu()));
m_renameResourceFile = new QAction(tr("Rename File"), this);
command = Core::ActionManager::registerAction(m_renameResourceFile, Constants::C_RENAME_FILE, projectTreeContext);
folderContextMenu->addAction(command, ProjectExplorer::Constants::G_FOLDER_FILES);
connect(m_renameResourceFile, SIGNAL(triggered()), this, SLOT(renameFileContextMenu()));
m_removeResourceFile = new QAction(tr("Remove File"), this);
command = Core::ActionManager::registerAction(m_removeResourceFile, Constants::C_REMOVE_FILE, projectTreeContext);
folderContextMenu->addAction(command, ProjectExplorer::Constants::G_FOLDER_FILES);
connect(m_removeResourceFile, SIGNAL(triggered()), this, SLOT(removeFileContextMenu()));
m_openInEditor = new QAction(tr("Open in Editor"), this);
command = Core::ActionManager::registerAction(m_openInEditor, Constants::C_OPEN_EDITOR, projectTreeContext);
folderContextMenu->addAction(command, ProjectExplorer::Constants::G_FOLDER_FILES);
connect(m_openInEditor, SIGNAL(triggered()), this, SLOT(openEditorContextMenu()));
m_openInTextEditor = new QAction(tr("Open in Text Editor"), this);
command = Core::ActionManager::registerAction(m_openInTextEditor, Constants::C_OPEN_TEXT_EDITOR, projectTreeContext);
folderContextMenu->addAction(command, ProjectExplorer::Constants::G_FOLDER_FILES);
connect(m_openInTextEditor, SIGNAL(triggered()), this, SLOT(openTextEditorContextMenu()));
m_addPrefix->setEnabled(false);
m_removePrefix->setEnabled(false);
m_renamePrefix->setEnabled(false);
m_renameResourceFile->setEnabled(false);
m_removeResourceFile->setEnabled(false);
connect(ProjectExplorer::ProjectExplorerPlugin::instance(), SIGNAL(currentNodeChanged(ProjectExplorer::Node*,ProjectExplorer::Project*)),
this, SLOT(updateContextActions(ProjectExplorer::Node*,ProjectExplorer::Project*)));
return true;
}
......@@ -110,6 +212,108 @@ void ResourceEditorPlugin::onRefresh()
currentEditor()->onRefresh();
}
void ResourceEditorPlugin::addPrefixContextMenu()
{
PrefixLangDialog dialog(tr("Add Prefix"), QString(), QString(), Core::ICore::mainWindow());
if (dialog.exec() != QDialog::Accepted)
return;
QString prefix = dialog.prefix();
if (prefix.isEmpty())
return;
ResourceTopLevelNode *topLevel = static_cast<ResourceTopLevelNode *>(ProjectExplorer::ProjectExplorerPlugin::instance()->currentNode());
topLevel->addPrefix(prefix, dialog.lang());
}
void ResourceEditorPlugin::removePrefixContextMenu()
{
ResourceFolderNode *rfn = static_cast<ResourceFolderNode *>(ProjectExplorer::ProjectExplorerPlugin::instance()->currentNode());
if (QMessageBox::question(Core::ICore::mainWindow(),
tr("Remove Prefix"),
tr("Remove prefix %1 and all its files?").arg(rfn->displayName()))
== QMessageBox::Yes) {
ResourceTopLevelNode *rn = rfn->resourceNode();
rn->removePrefix(rfn->prefix(), rfn->lang());
}
}
void ResourceEditorPlugin::renameFileContextMenu()
{
ProjectExplorer::ProjectExplorerPlugin::instance()->initiateInlineRenaming();
}
void ResourceEditorPlugin::removeFileContextMenu()
{
ResourceFolderNode *rfn = static_cast<ResourceFolderNode *>(ProjectExplorer::ProjectExplorerPlugin::instance()->currentNode());
QString path = rfn->path();
ProjectExplorer::FolderNode *parent = rfn->parentFolderNode();
if (!parent->removeFiles(QStringList() << path))
QMessageBox::warning(Core::ICore::mainWindow(),
tr("File Removal failed"),
tr("Removing file %1 from the project failed.").arg(path));
}
void ResourceEditorPlugin::openEditorContextMenu()
{
ResourceTopLevelNode *topLevel = static_cast<ResourceTopLevelNode *>(ProjectExplorer::ProjectExplorerPlugin::instance()->currentNode());
QString path = topLevel->path();
Core::EditorManager::openEditor(path);
}
void ResourceEditorPlugin::openTextEditorContextMenu()
{
ResourceTopLevelNode *topLevel = static_cast<ResourceTopLevelNode *>(ProjectExplorer::ProjectExplorerPlugin::instance()->currentNode());
QString path = topLevel->path();
Core::EditorManager::openEditor(path, Core::Constants::K_DEFAULT_TEXT_EDITOR_ID);
}
void ResourceEditorPlugin::renamePrefixContextMenu()
{
ResourceFolderNode *rfn = static_cast<ResourceFolderNode *>(ProjectExplorer::ProjectExplorerPlugin::instance()->currentNode());
PrefixLangDialog dialog(tr("Rename Prefix"), rfn->prefix(), rfn->lang(), Core::ICore::mainWindow());
if (dialog.exec() != QDialog::Accepted)
return;
QString prefix = dialog.prefix();
if (prefix.isEmpty())
return;
rfn->renamePrefix(prefix, dialog.lang());
}
void ResourceEditorPlugin::updateContextActions(ProjectExplorer::Node *node, ProjectExplorer::Project *)
{
bool isResourceNode = qobject_cast<ResourceTopLevelNode *>(node);
m_addPrefix->setEnabled(isResourceNode);
m_addPrefix->setVisible(isResourceNode);
bool enableRename = false;
bool enableRemove = false;
if (isResourceNode) {
ProjectExplorer::FolderNode *parent = node ? node->parentFolderNode() : 0;
enableRename = parent && parent->supportedActions(node).contains(ProjectExplorer::Rename);
enableRemove = parent && parent->supportedActions(node).contains(ProjectExplorer::RemoveFile);
}
m_renameResourceFile->setEnabled(isResourceNode && enableRename);
m_renameResourceFile->setVisible(isResourceNode && enableRename);
m_removeResourceFile->setEnabled(isResourceNode && enableRemove);
m_removeResourceFile->setVisible(isResourceNode && enableRemove);
m_openInEditor->setEnabled(isResourceNode);
m_openInEditor->setVisible(isResourceNode);
m_openInTextEditor->setEnabled(isResourceNode);
m_openInTextEditor->setVisible(isResourceNode);
bool isResourceFolder = qobject_cast<ResourceFolderNode *>(node);
m_removePrefix->setEnabled(isResourceFolder);
m_removePrefix->setVisible(isResourceFolder);
m_renamePrefix->setEnabled(isResourceFolder);
m_renamePrefix->setVisible(isResourceFolder);
}
void ResourceEditorPlugin::onUndoStackChanged(ResourceEditorW const *editor,
bool canUndo, bool canRedo)
{
......
......@@ -36,6 +36,11 @@ QT_BEGIN_NAMESPACE
class QAction;
QT_END_NAMESPACE
namespace ProjectExplorer {
class Node;
class Project;
}
namespace ResourceEditor {
namespace Internal {
......@@ -60,6 +65,17 @@ private slots:
void onRedo();
void onRefresh();
void addPrefixContextMenu();
void renamePrefixContextMenu();
void removePrefixContextMenu();
void renameFileContextMenu();
void removeFileContextMenu();
void openEditorContextMenu();
void openTextEditorContextMenu();
void updateContextActions(ProjectExplorer::Node*,ProjectExplorer::Project*);
public:
void onUndoStackChanged(ResourceEditorW const *editor, bool canUndo, bool canRedo);
......@@ -70,6 +86,17 @@ private:
QAction *m_redoAction;
QAction *m_undoAction;
QAction *m_refreshAction;
// project tree's folder context menu
QAction *m_addPrefix;
QAction *m_removePrefix;
QAction *m_renamePrefix;
QAction *m_renameResourceFile;
QAction *m_removeResourceFile;
QAction *m_openInEditor;
QAction *m_openInTextEditor;
};
} // namespace Internal
......
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "resourcenode.h"
#include "resourcefile_p.h"
#include "resourceeditorconstants.h"
#include <utils/fileutils.h>
#include <coreplugin/documentmanager.h>
#include <coreplugin/fileiconprovider.h>
#include <coreplugin/mimedatabase.h>
#include <qmljstools/qmljstoolsconstants.h>
#include <QDir>
#include <QDebug>
using namespace ResourceEditor;
using namespace ResourceEditor::Internal;
static int priority(const QStringList &files)
{
if (files.isEmpty())