diff options
author | Daniel Teske <daniel.teske@digia.com> | 2014-02-18 21:38:03 +0100 |
---|---|---|
committer | Daniel Teske <daniel.teske@digia.com> | 2014-02-19 17:44:09 +0100 |
commit | 9310b652ebbfca345c33ec554d73be8d10bdec19 (patch) | |
tree | f5230fe19a6c78da6b92c75682ace4ada44eb0cd /src/plugins/resourceeditor | |
parent | 1e9984322b65d20e3ddeb05784d4fcc771045f45 (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.h | 40 | ||||
-rw-r--r-- | src/plugins/resourceeditor/resourceeditor.pro | 9 | ||||
-rw-r--r-- | src/plugins/resourceeditor/resourceeditor.qbs | 1 | ||||
-rw-r--r-- | src/plugins/resourceeditor/resourceeditor_dependencies.pri | 1 | ||||
-rw-r--r-- | src/plugins/resourceeditor/resourceeditorconstants.h | 11 | ||||
-rw-r--r-- | src/plugins/resourceeditor/resourceeditorplugin.cpp | 204 | ||||
-rw-r--r-- | src/plugins/resourceeditor/resourceeditorplugin.h | 27 | ||||
-rw-r--r-- | src/plugins/resourceeditor/resourcenode.cpp | 484 | ||||
-rw-r--r-- | src/plugins/resourceeditor/resourcenode.h | 134 |
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 |