aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/resourceeditor
diff options
context:
space:
mode:
authorDaniel Teske <daniel.teske@digia.com>2014-02-18 21:38:03 +0100
committerDaniel Teske <daniel.teske@digia.com>2014-02-19 17:44:09 +0100
commit9310b652ebbfca345c33ec554d73be8d10bdec19 (patch)
treef5230fe19a6c78da6b92c75682ace4ada44eb0cd /src/plugins/resourceeditor
parent1e9984322b65d20e3ddeb05784d4fcc771045f45 (diff)
Resource file in project tree
Task-number: QTCREATORBUG-1346 Change-Id: I0cbb5633ef06a4762c48af46d9da551c67259d70 Reviewed-by: Daniel Teske <daniel.teske@digia.com>
Diffstat (limited to 'src/plugins/resourceeditor')
-rw-r--r--src/plugins/resourceeditor/resource_global.h40
-rw-r--r--src/plugins/resourceeditor/resourceeditor.pro9
-rw-r--r--src/plugins/resourceeditor/resourceeditor.qbs1
-rw-r--r--src/plugins/resourceeditor/resourceeditor_dependencies.pri1
-rw-r--r--src/plugins/resourceeditor/resourceeditorconstants.h11
-rw-r--r--src/plugins/resourceeditor/resourceeditorplugin.cpp204
-rw-r--r--src/plugins/resourceeditor/resourceeditorplugin.h27
-rw-r--r--src/plugins/resourceeditor/resourcenode.cpp484
-rw-r--r--src/plugins/resourceeditor/resourcenode.h134
9 files changed, 909 insertions, 2 deletions
diff --git a/src/plugins/resourceeditor/resource_global.h b/src/plugins/resourceeditor/resource_global.h
new file mode 100644
index 00000000000..93c66ce985c
--- /dev/null
+++ b/src/plugins/resourceeditor/resource_global.h
@@ -0,0 +1,40 @@
+/****************************************************************************
+**
+** 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
diff --git a/src/plugins/resourceeditor/resourceeditor.pro b/src/plugins/resourceeditor/resourceeditor.pro
index b71135955d2..2e66742a1c1 100644
--- a/src/plugins/resourceeditor/resourceeditor.pro
+++ b/src/plugins/resourceeditor/resourceeditor.pro
@@ -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
diff --git a/src/plugins/resourceeditor/resourceeditor.qbs b/src/plugins/resourceeditor/resourceeditor.qbs
index 3bc41153890..a1199be38ac 100644
--- a/src/plugins/resourceeditor/resourceeditor.qbs
+++ b/src/plugins/resourceeditor/resourceeditor.qbs
@@ -20,6 +20,7 @@ QtcPlugin {
"resourceeditorplugin.cpp", "resourceeditorplugin.h",
"resourceeditorw.cpp", "resourceeditorw.h",
"resourcewizard.cpp", "resourcewizard.h",
+ "resource_global.h", "resourcenode.cpp", "resourcenode.h"
]
}
diff --git a/src/plugins/resourceeditor/resourceeditor_dependencies.pri b/src/plugins/resourceeditor/resourceeditor_dependencies.pri
index 83c79618066..4b8c19dfe09 100644
--- a/src/plugins/resourceeditor/resourceeditor_dependencies.pri
+++ b/src/plugins/resourceeditor/resourceeditor_dependencies.pri
@@ -5,3 +5,4 @@ QTC_LIB_DEPENDS += \
utils
QTC_PLUGIN_DEPENDS += \
coreplugin \
+ projectexplorer
diff --git a/src/plugins/resourceeditor/resourceeditorconstants.h b/src/plugins/resourceeditor/resourceeditorconstants.h
index 4c27ea44145..284c7f049c2 100644
--- a/src/plugins/resourceeditor/resourceeditorconstants.h
+++ b/src/plugins/resourceeditor/resourceeditorconstants.h
@@ -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
diff --git a/src/plugins/resourceeditor/resourceeditorplugin.cpp b/src/plugins/resourceeditor/resourceeditorplugin.cpp
index 3d6b622f732..f88aeec47a9 100644
--- a/src/plugins/resourceeditor/resourceeditorplugin.cpp
+++ b/src/plugins/resourceeditor/resourceeditorplugin.cpp
@@ -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)
{
diff --git a/src/plugins/resourceeditor/resourceeditorplugin.h b/src/plugins/resourceeditor/resourceeditorplugin.h
index d488ec0b0ec..e773b6d40fb 100644
--- a/src/plugins/resourceeditor/resourceeditorplugin.h
+++ b/src/plugins/resourceeditor/resourceeditorplugin.h
@@ -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
diff --git a/src/plugins/resourceeditor/resourcenode.cpp b/src/plugins/resourceeditor/resourcenode.cpp
new file mode 100644
index 00000000000..e8b798f3de5
--- /dev/null
+++ b/src/plugins/resourceeditor/resourcenode.cpp
@@ -0,0 +1,484 @@
+/****************************************************************************
+**
+** 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())
+ return -1;
+ Core::MimeType mt = Core::MimeDatabase::findByFile(files.at(0));
+ QString type = mt.type();
+ if (type.startsWith(QLatin1String("image/"))
+ || type == QLatin1String(QmlJSTools::Constants::QML_MIMETYPE)
+ || type == QLatin1String(QmlJSTools::Constants::JS_MIMETYPE))
+ return 120;
+ return 80;
+}
+
+static bool addFilesToResource(const QString &resourceFile, const QStringList &filePaths, QStringList *notAdded,
+ const QString &prefix, const QString &lang)
+{
+ if (notAdded)
+ *notAdded = filePaths;
+
+ ResourceFile file(resourceFile);
+ if (!file.load())
+ return false;
+
+ int index = file.indexOfPrefix(prefix, lang);
+ if (index == -1)
+ index = file.addPrefix(prefix, lang);
+
+ if (notAdded)
+ notAdded->clear();
+ foreach (const QString &path, filePaths) {
+ if (file.contains(index, path))
+ *notAdded << path;
+ else
+ file.addFile(index, path);
+ }
+
+ Core::DocumentManager::expectFileChange(resourceFile);
+ file.save();
+ Core::DocumentManager::unexpectFileChange(resourceFile);
+
+ return true;
+}
+
+static bool sortByPrefixAndLang(ProjectExplorer::FolderNode *a, ProjectExplorer::FolderNode *b)
+{
+ ResourceFolderNode *aa = static_cast<ResourceFolderNode *>(a);
+ ResourceFolderNode *bb = static_cast<ResourceFolderNode *>(b);
+
+ if (aa->prefix() < bb->prefix())
+ return true;
+ if (bb->prefix() < aa->prefix())
+ return false;
+ return aa->lang() < bb->lang();
+}
+
+static bool equalByPrefixAndLang(ProjectExplorer::FolderNode *a, ProjectExplorer::FolderNode *b)
+{
+ ResourceFolderNode *aa = static_cast<ResourceFolderNode *>(a);
+ ResourceFolderNode *bb = static_cast<ResourceFolderNode *>(b);
+
+ return aa->prefix() == bb->prefix()
+ && aa->lang() == bb->lang();
+}
+
+static bool sortNodesByPath(ProjectExplorer::Node *a, ProjectExplorer::Node *b)
+{
+ return a->path() < b->path();
+}
+
+ResourceTopLevelNode::ResourceTopLevelNode(const QString &filePath, FolderNode *parent)
+ : ProjectExplorer::FolderNode(filePath)
+{
+ setIcon(Core::FileIconProvider::icon(filePath));
+ m_document = new ResourceFileWatcher(this);
+ Core::DocumentManager::addDocument(m_document);
+
+ Utils::FileName base = Utils::FileName::fromString(parent->path());
+ Utils::FileName file = Utils::FileName::fromString(filePath);
+ if (file.isChildOf(base))
+ setDisplayName(file.relativeChildPath(base).toString());
+ else
+ setDisplayName(file.toString());
+}
+
+ResourceTopLevelNode::~ResourceTopLevelNode()
+{
+ Core::DocumentManager::removeDocument(m_document);
+}
+
+void ResourceTopLevelNode::update()
+{
+ QList<ProjectExplorer::FolderNode *> newFolderList;
+ QMap<QPair<QString, QString>, QList<ProjectExplorer::FileNode *> > filesToAdd;
+
+ ResourceFile file(path());
+ if (file.load()) {
+ QSet<QPair<QString, QString > > prefixes;
+
+ int prfxcount = file.prefixCount();
+ for (int i = 0; i < prfxcount; ++i) {
+ const QString &prefix = file.prefix(i);
+ const QString &lang = file.lang(i);
+ // ensure that we don't duplicate prefixes
+ if (!prefixes.contains(qMakePair(prefix, lang))) {
+ ProjectExplorer::FolderNode *fn = new ResourceFolderNode(file.prefix(i), file.lang(i), this);
+ newFolderList << fn;
+
+ prefixes.insert(qMakePair(prefix, lang));
+ }
+
+ QSet<QString> fileNames;
+ int filecount = file.fileCount(i);
+ for (int j = 0; j < filecount; ++j) {
+ const QString &fileName = file.file(i, j);
+ if (fileNames.contains(fileName)) {
+ // The file name is duplicated, skip it
+ // Note: this is wrong, but the qrceditor doesn't allow it either
+ // only aliases need to be unique
+ } else {
+ fileNames.insert(fileName);
+ filesToAdd[qMakePair(prefix, lang)]
+ << new ResourceFileNode(fileName, this);
+ }
+
+ }
+ }
+ }
+
+ QList<ProjectExplorer::FolderNode *> oldFolderList = subFolderNodes();
+ QList<ProjectExplorer::FolderNode *> foldersToAdd;
+ QList<ProjectExplorer::FolderNode *> foldersToRemove;
+
+ std::sort(oldFolderList.begin(), oldFolderList.end(), sortByPrefixAndLang);
+ std::sort(newFolderList.begin(), newFolderList.end(), sortByPrefixAndLang);
+
+ ProjectExplorer::compareSortedLists(oldFolderList, newFolderList, foldersToRemove, foldersToAdd, sortByPrefixAndLang);
+
+ removeFolderNodes(foldersToRemove);
+ addFolderNodes(foldersToAdd);
+
+ // delete nodes that weren't added
+ qDeleteAll(ProjectExplorer::subtractSortedList(newFolderList, foldersToAdd, sortByPrefixAndLang));
+
+ foreach (FolderNode *fn, subFolderNodes()) {
+ ResourceFolderNode *rn = static_cast<ResourceFolderNode *>(fn);
+ rn->updateFiles(filesToAdd.value(qMakePair(rn->prefix(), rn->lang())));
+ }
+}
+
+QList<ProjectExplorer::ProjectAction> ResourceTopLevelNode::supportedActions(ProjectExplorer::Node *node) const
+{
+ if (node != this)
+ return QList<ProjectExplorer::ProjectAction>();
+ return QList<ProjectExplorer::ProjectAction>()
+ << ProjectExplorer::AddNewFile
+ << ProjectExplorer::AddExistingFile
+ << ProjectExplorer::AddExistingDirectory
+ << ProjectExplorer::HidePathActions
+ << ProjectExplorer::Rename;
+}
+
+bool ResourceTopLevelNode::addFiles(const QStringList &filePaths, QStringList *notAdded)
+{
+ return addFilesToResource(path(), filePaths, notAdded, QLatin1String("/"), QString());
+}
+
+bool ResourceTopLevelNode::removeFiles(const QStringList &filePaths, QStringList *notRemoved)
+{
+ return parentFolderNode()->removeFiles(filePaths, notRemoved);
+}
+
+bool ResourceTopLevelNode::addPrefix(const QString &prefix, const QString &lang)
+{
+ ResourceFile file(path());
+ if (!file.load())
+ return false;
+ int index = file.addPrefix(prefix, lang);
+ if (index == -1)
+ return false;
+ Core::DocumentManager::expectFileChange(path());
+ file.save();
+ Core::DocumentManager::unexpectFileChange(path());
+
+ update();
+
+ return true;
+}
+
+bool ResourceTopLevelNode::removePrefix(const QString &prefix, const QString &lang)
+{
+ ResourceFile file(path());
+ if (!file.load())
+ return false;
+ for (int i = 0; i < file.prefixCount(); ++i) {
+ if (file.prefix(i) == prefix
+ && file.lang(i) == lang) {
+ file.removePrefix(i);
+ Core::DocumentManager::expectFileChange(path());
+ file.save();
+ Core::DocumentManager::unexpectFileChange(path());
+
+ update();
+ return true;
+ }
+ }
+ return false;
+}
+
+ProjectExplorer::FolderNode::AddNewInformation ResourceTopLevelNode::addNewInformation(const QStringList &files) const
+{
+ QString name = tr("%1 Prefix: %2")
+ .arg(QFileInfo(path()).fileName())
+ .arg(QLatin1String("/"));
+ return AddNewInformation(name, priority(files) + 1);
+}
+
+ResourceFolderNode::ResourceFolderNode(const QString &prefix, const QString &lang, ResourceTopLevelNode *parent)
+ : ProjectExplorer::FolderNode(parent->path() + QLatin1Char('/') + prefix),
+ // TOOD Why add existing directory doesn't work
+ m_topLevelNode(parent),
+ m_prefix(prefix),
+ m_lang(lang)
+{
+
+}
+
+ResourceFolderNode::~ResourceFolderNode()
+{
+
+}
+
+QList<ProjectExplorer::ProjectAction> ResourceFolderNode::supportedActions(ProjectExplorer::Node *node) const
+{
+ Q_UNUSED(node)
+ QList<ProjectExplorer::ProjectAction> actions;
+ actions << ProjectExplorer::AddNewFile
+ << ProjectExplorer::AddExistingFile
+ << ProjectExplorer::AddExistingDirectory
+ << ProjectExplorer::RemoveFile
+ << ProjectExplorer::Rename // Note: only works for the filename, works akwardly for relative file paths
+ << ProjectExplorer::HidePathActions; // hides open terminal etc.
+
+ // if the prefix is '/' (without lang) hide this node in add new dialog,
+ // as the ResouceTopLevelNode is always shown for the '/' prefix
+ if (m_prefix == QLatin1String("/") && m_lang.isEmpty())
+ actions << ProjectExplorer::InheritedFromParent;
+
+ return actions;
+}
+
+bool ResourceFolderNode::addFiles(const QStringList &filePaths, QStringList *notAdded)
+{
+ return addFilesToResource(m_topLevelNode->path(), filePaths, notAdded, m_prefix, m_lang);
+}
+
+bool ResourceFolderNode::removeFiles(const QStringList &filePaths, QStringList *notRemoved)
+{
+ if (notRemoved)
+ *notRemoved = filePaths;
+ ResourceFile file(m_topLevelNode->path());
+ if (!file.load())
+ return false;
+ int index = file.indexOfPrefix(m_prefix, m_lang);
+ if (index == -1)
+ return false;
+ for (int j = 0; j < file.fileCount(index); ++j) {
+ QString fileName = file.file(index, j);
+ if (!filePaths.contains(fileName))
+ continue;
+ if (notRemoved)
+ notRemoved->removeOne(fileName);
+ file.removeFile(index, j);
+ --j;
+ }
+ Core::DocumentManager::expectFileChange(m_topLevelNode->path());
+ file.save();
+ Core::DocumentManager::unexpectFileChange(m_topLevelNode->path());
+
+ return true;
+}
+
+bool ResourceFolderNode::renameFile(const QString &filePath, const QString &newFilePath)
+{
+ ResourceFile file(m_topLevelNode->path());
+ if (!file.load())
+ return false;
+ int index = file.indexOfPrefix(m_prefix, m_lang);
+ if (index == -1)
+ return false;
+
+ for (int j = 0; j < file.fileCount(index); ++j) {
+ if (file.file(index, j) == filePath) {
+ file.replaceFile(index, j, newFilePath);
+ Core::DocumentManager::expectFileChange(m_topLevelNode->path());
+ file.save();
+ Core::DocumentManager::unexpectFileChange(m_topLevelNode->path());
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool ResourceFolderNode::renamePrefix(const QString &prefix, const QString &lang)
+{
+ ResourceFile file(m_topLevelNode->path());
+ if (!file.load())
+ return false;
+ int index = file.indexOfPrefix(prefix, lang);
+ if (index == -1)
+ return false;
+
+ if (!file.replacePrefixAndLang(index, prefix, lang))
+ return false;
+
+ Core::DocumentManager::expectFileChange(m_topLevelNode->path());
+ file.save();
+ Core::DocumentManager::unexpectFileChange(m_topLevelNode->path());
+ return true;
+}
+
+ProjectExplorer::FolderNode::AddNewInformation ResourceFolderNode::addNewInformation(const QStringList &files) const
+{
+ QString name = tr("%1 Prefix: %2")
+ .arg(QFileInfo(m_topLevelNode->path()).fileName())
+ .arg(displayName());
+ return AddNewInformation(name, priority(files) + 1);
+}
+
+QString ResourceFolderNode::displayName() const
+{
+ if (m_lang.isEmpty())
+ return m_prefix;
+ return m_prefix + QLatin1String(" (") + m_lang + QLatin1Char(')');
+}
+
+QString ResourceFolderNode::prefix() const
+{
+ return m_prefix;
+}
+
+QString ResourceFolderNode::lang() const
+{
+ return m_lang;
+}
+
+ResourceTopLevelNode *ResourceFolderNode::resourceNode() const
+{
+ return m_topLevelNode;
+}
+
+void ResourceFolderNode::updateFiles(QList<ProjectExplorer::FileNode *> newList)
+{
+ QList<ProjectExplorer::FileNode *> oldList = fileNodes();
+ QList<ProjectExplorer::FileNode *> filesToAdd;
+ QList<ProjectExplorer::FileNode *> filesToRemove;
+
+ std::sort(oldList.begin(), oldList.end(), sortNodesByPath);
+ std::sort(newList.begin(), newList.end(), sortNodesByPath);
+
+ ProjectExplorer::compareSortedLists(oldList, newList, filesToRemove, filesToAdd, sortNodesByPath);
+
+ removeFileNodes(filesToRemove);
+ addFileNodes(filesToAdd);
+
+ qDeleteAll(ProjectExplorer::subtractSortedList(newList, filesToAdd, sortNodesByPath));
+}
+
+ResourceFileWatcher::ResourceFileWatcher(ResourceTopLevelNode *node)
+ : IDocument(node), m_node(node)
+{
+ setFilePath(node->path());
+}
+
+bool ResourceFileWatcher::save(QString *errorString, const QString &fileName, bool autoSave)
+{
+ Q_UNUSED(errorString);
+ Q_UNUSED(fileName);
+ Q_UNUSED(autoSave);
+ return false;
+}
+
+QString ResourceFileWatcher::defaultPath() const
+{
+ return QString();
+}
+
+QString ResourceFileWatcher::suggestedFileName() const
+{
+ return QString();
+}
+
+QString ResourceFileWatcher::mimeType() const
+{
+ return QLatin1String(ResourceEditor::Constants::C_RESOURCE_MIMETYPE);
+}
+
+bool ResourceFileWatcher::isModified() const
+{
+ return false;
+}
+
+bool ResourceFileWatcher::isSaveAsAllowed() const
+{
+ return false;
+}
+
+Core::IDocument::ReloadBehavior ResourceFileWatcher::reloadBehavior(ChangeTrigger state, ChangeType type) const
+{
+ Q_UNUSED(state)
+ Q_UNUSED(type)
+ return BehaviorSilent;
+}
+
+bool ResourceFileWatcher::reload(QString *errorString, ReloadFlag flag, ChangeType type)
+{
+ Q_UNUSED(errorString)
+ Q_UNUSED(flag)
+ if (type == TypePermissions)
+ return true;
+ m_node->update();
+ return true;
+}
+
+ResourceFileNode::ResourceFileNode(const QString &filePath, ResourceTopLevelNode *topLevel)
+ : ProjectExplorer::FileNode(filePath, ProjectExplorer::UnknownFileType, false),
+ m_topLevel(topLevel)
+
+{
+ QString baseDir = QFileInfo(topLevel->path()).absolutePath();
+ m_displayName = QDir(baseDir).relativeFilePath(filePath);
+}
+
+QString ResourceFileNode::displayName() const
+{
+ return m_displayName;
+}
diff --git a/src/plugins/resourceeditor/resourcenode.h b/src/plugins/resourceeditor/resourcenode.h
new file mode 100644
index 00000000000..74c01eb227b
--- /dev/null
+++ b/src/plugins/resourceeditor/resourcenode.h
@@ -0,0 +1,134 @@
+/****************************************************************************
+**
+** 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 RESOURCENODE_H
+#define RESOURCENODE_H
+
+#include "resource_global.h"
+#include <projectexplorer/projectnodes.h>
+#include <coreplugin/idocument.h>
+
+namespace ProjectExplorer {
+class RunConfiguration;
+}
+
+namespace ResourceEditor {
+namespace Internal { class ResourceFileWatcher; }
+
+class RESOURCE_EXPORT ResourceTopLevelNode : public ProjectExplorer::FolderNode
+{
+ Q_OBJECT
+public:
+ ResourceTopLevelNode(const QString &filePath, FolderNode *parent);
+ ~ResourceTopLevelNode();
+ void update();
+
+ QList<ProjectExplorer::ProjectAction> supportedActions(Node *node) const;
+ bool addFiles(const QStringList &filePaths, QStringList *notAdded);
+ bool removeFiles(const QStringList &filePaths, QStringList *notRemoved);
+
+ bool addFiles(const QString &prefix, const QString &lang, const QStringList &filePaths, QStringList *notAdded);
+ bool removeFiles(const QString &prefix, const QString &lang,const QStringList &filePaths, QStringList *notRemoved);
+
+ bool addPrefix(const QString &prefix, const QString &lang);
+ bool removePrefix(const QString &prefix, const QString &lang);
+
+ AddNewInformation addNewInformation(const QStringList &files) const;
+
+private:
+ Internal::ResourceFileWatcher *m_document;
+};
+
+namespace Internal {
+class ResourceFolderNode : public ProjectExplorer::FolderNode
+{
+ friend class ResourceEditor::ResourceTopLevelNode; // for updateFiles
+ Q_OBJECT
+public:
+ ResourceFolderNode(const QString &prefix, const QString &lang, ResourceTopLevelNode *parent);
+ ~ResourceFolderNode();
+
+ QList<ProjectExplorer::ProjectAction> supportedActions(Node *node) const;
+
+ QString displayName() const;
+
+ bool addFiles(const QStringList &filePaths, QStringList *notAdded);
+ bool removeFiles(const QStringList &filePaths, QStringList *notRemoved);
+ bool renameFile(const QString &filePath, const QString &newFilePath);
+
+ bool renamePrefix(const QString &prefix, const QString &lang);
+
+ AddNewInformation addNewInformation(const QStringList &files) const;
+
+ QString prefix() const;
+ QString lang() const;
+ ResourceTopLevelNode *resourceNode() const;
+private:
+ void updateFiles(QList<ProjectExplorer::FileNode *> newList);
+ ResourceTopLevelNode *m_topLevelNode;
+ QString m_prefix;
+ QString m_lang;
+};
+
+class ResourceFileNode : public ProjectExplorer::FileNode
+{
+ Q_OBJECT
+public:
+ ResourceFileNode(const QString &filePath, ResourceTopLevelNode *topLevel);
+
+ QString displayName() const;
+
+private:
+ QString m_displayName;
+ ResourceTopLevelNode *m_topLevel;
+};
+
+class ResourceFileWatcher : public Core::IDocument
+{
+ Q_OBJECT
+public:
+ ResourceFileWatcher(ResourceTopLevelNode *node);
+ virtual bool save(QString *errorString, const QString &fileName, bool autoSave);
+
+ virtual QString defaultPath() const;
+ virtual QString suggestedFileName() const;
+ virtual QString mimeType() const;
+
+ virtual bool isModified() const;
+ virtual bool isSaveAsAllowed() const;
+
+ ReloadBehavior reloadBehavior(ChangeTrigger state, ChangeType type) const;
+ bool reload(QString *errorString, ReloadFlag flag, ChangeType type);
+private:
+ ResourceTopLevelNode *m_node;
+};
+} // namespace Internal
+} // namespace ResourceEditor
+
+#endif // RESOUCENODE_H