ResourceEditor: Compactify qrceditor implementation a bit

Combine files; remove pointers Q_ASSERT that effectively don't check anything
as they are dereferenced immediately; using namespace Utils, ...

Change-Id: Iefbc4c9e9de3ac6e499bf6caea04503b8c796e3d
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
hjk
2024-07-12 17:34:43 +02:00
parent b9c2f197ff
commit 6f6004a425
10 changed files with 623 additions and 801 deletions

View File

@@ -2,10 +2,10 @@ add_qtc_plugin(ResourceEditor
DEPENDS Qt::Xml
PLUGIN_DEPENDS Core ProjectExplorer
SOURCES
qrceditor/qrceditor.cpp qrceditor/qrceditor.h
qrceditor/resourcefile.cpp qrceditor/resourcefile_p.h
qrceditor/resourceview.cpp qrceditor/resourceview.h
qrceditor/undocommands.cpp qrceditor/undocommands_p.h
qrceditor/qrceditor.cpp
qrceditor/qrceditor.h
qrceditor/resourcefile.cpp
qrceditor/resourcefile_p.h
resource_global.h
resourceeditor.cpp
resourceeditor.h

View File

@@ -4,23 +4,623 @@
#include "qrceditor.h"
#include "../resourceeditortr.h"
#include "undocommands_p.h"
#include "resourcefile_p.h"
#include <aggregation/aggregate.h>
#include <coreplugin/find/itemviewfind.h>
#include <utils/itemviews.h>
#include <utils/layoutbuilder.h>
#include <QDebug>
#include <QFileDialog>
#include <QHeaderView>
#include <QLabel>
#include <QLineEdit>
#include <QMenu>
#include <QMessageBox>
#include <QPoint>
#include <QPushButton>
#include <QScopedPointer>
#include <QString>
#include <QUndoCommand>
using namespace Utils;
namespace ResourceEditor::Internal {
class ResourceView : public Utils::TreeView
{
Q_OBJECT
public:
enum NodeProperty {
AliasProperty,
PrefixProperty,
LanguageProperty
};
explicit ResourceView(RelativeResourceModel *model, QUndoStack *history, QWidget *parent = nullptr);
FilePath filePath() const;
bool isPrefix(const QModelIndex &index) const;
QString currentAlias() const;
QString currentPrefix() const;
QString currentLanguage() const;
QString currentResourcePath() const;
void setResourceDragEnabled(bool e);
bool resourceDragEnabled() const;
void findSamePlacePostDeletionModelIndex(int &row, QModelIndex &parent) const;
EntryBackup *removeEntry(const QModelIndex &index);
QStringList existingFilesSubtracted(int prefixIndex, const QStringList &fileNames) const;
void addFiles(int prefixIndex, const QStringList &fileNames, int cursorFile,
int &firstFile, int &lastFile);
void removeFiles(int prefixIndex, int firstFileIndex, int lastFileIndex);
QStringList fileNamesToAdd();
QModelIndex addPrefix();
QList<QModelIndex> nonExistingFiles();
void refresh();
void setCurrentAlias(const QString &before, const QString &after);
void setCurrentPrefix(const QString &before, const QString &after);
void setCurrentLanguage(const QString &before, const QString &after);
void advanceMergeId();
QString getCurrentValue(NodeProperty property) const;
void changeValue(const QModelIndex &nodeIndex, NodeProperty property, const QString &value);
signals:
void removeItem();
void itemActivated(const QString &fileName);
void contextMenuShown(const QPoint &globalPos, const QString &fileName);
protected:
void keyPressEvent(QKeyEvent *e) override;
private:
void onItemActivated(const QModelIndex &index);
void showContextMenu(const QPoint &pos);
void addUndoCommand(const QModelIndex &nodeIndex, NodeProperty property,
const QString &before, const QString &after);
RelativeResourceModel *m_qrcModel;
QUndoStack *m_history;
int m_mergeId;
};
class ViewCommand : public QUndoCommand
{
protected:
ViewCommand(ResourceView *view) : m_view(view) {}
ResourceView *m_view;
};
class ModelIndexViewCommand : public ViewCommand
{
protected:
ModelIndexViewCommand(ResourceView *view) : ViewCommand(view) {}
void storeIndex(const QModelIndex &index)
{
if (m_view->isPrefix(index)) {
m_prefixArrayIndex = index.row();
m_fileArrayIndex = -1;
} else {
m_fileArrayIndex = index.row();
m_prefixArrayIndex = m_view->model()->parent(index).row();
}
}
QModelIndex makeIndex() const
{
const QModelIndex prefixModelIndex
= m_view->model()->index(m_prefixArrayIndex, 0, QModelIndex());
if (m_fileArrayIndex != -1) {
// File node
const QModelIndex fileModelIndex
= m_view->model()->index(m_fileArrayIndex, 0, prefixModelIndex);
return fileModelIndex;
} else {
// Prefix node
return prefixModelIndex;
}
}
private:
int m_prefixArrayIndex;
int m_fileArrayIndex;
};
class ModifyPropertyCommand : public ModelIndexViewCommand
{
public:
ModifyPropertyCommand(ResourceView *view, const QModelIndex &nodeIndex,
ResourceView::NodeProperty property, const int mergeId,
const QString &before, const QString &after = {})
: ModelIndexViewCommand(view), m_property(property), m_before(before), m_after(after),
m_mergeId(mergeId)
{
storeIndex(nodeIndex);
}
private:
int id() const override { return m_mergeId; }
bool mergeWith(const QUndoCommand * command) override
{
if (command->id() != id() || m_property != static_cast<const ModifyPropertyCommand *>(command)->m_property)
return false;
// Choose older command (this) and forgot the other
return true;
}
void undo() override
{
// Save current text in m_after for redo()
m_after = m_view->getCurrentValue(m_property);
// Reset text to m_before
m_view->changeValue(makeIndex(), m_property, m_before);
}
void redo() override
{
// Prevent execution from within QUndoStack::push
if (m_after.isNull())
return;
// Bring back text before undo
Q_ASSERT(m_view);
m_view->changeValue(makeIndex(), m_property, m_after);
}
ResourceView::NodeProperty m_property;
QString m_before;
QString m_after;
int m_mergeId;
};
class RemoveEntryCommand : public ModelIndexViewCommand
{
public:
RemoveEntryCommand(ResourceView *view, const QModelIndex &index)
: ModelIndexViewCommand(view)
{
storeIndex(index);
}
~RemoveEntryCommand() override { freeEntry(); }
private:
void redo() override
{
freeEntry();
const QModelIndex index = makeIndex();
m_isExpanded = m_view->isExpanded(index);
m_entry = m_view->removeEntry(index);
}
void undo() override
{
if (m_entry != nullptr) {
m_entry->restore();
Q_ASSERT(m_view != nullptr);
const QModelIndex index = makeIndex();
m_view->setExpanded(index, m_isExpanded);
m_view->setCurrentIndex(index);
freeEntry();
}
}
void freeEntry() { delete m_entry; m_entry = nullptr; }
EntryBackup *m_entry = nullptr;
bool m_isExpanded = true;
};
class RemoveMultipleEntryCommand : public QUndoCommand
{
public:
// list must be in view order
RemoveMultipleEntryCommand(ResourceView *view, const QList<QModelIndex> &list)
{
m_subCommands.reserve(list.size());
for (const QModelIndex &index : list)
m_subCommands.push_back(new RemoveEntryCommand(view, index));
}
~RemoveMultipleEntryCommand() override { qDeleteAll(m_subCommands); }
private:
void redo() override
{
auto it = m_subCommands.rbegin();
auto end = m_subCommands.rend();
for (; it != end; ++it)
(*it)->redo();
}
void undo() override
{
auto it = m_subCommands.begin();
auto end = m_subCommands.end();
for (; it != end; ++it)
(*it)->undo();
}
std::vector<QUndoCommand *> m_subCommands;
};
class AddFilesCommand : public ViewCommand
{
public:
AddFilesCommand(ResourceView *view, int prefixIndex, int cursorFileIndex,
const QStringList &fileNames)
: ViewCommand(view), m_prefixIndex(prefixIndex), m_cursorFileIndex(cursorFileIndex),
m_fileNames(fileNames)
{}
private:
void redo() override
{
m_view->addFiles(m_prefixIndex, m_fileNames, m_cursorFileIndex, m_firstFile, m_lastFile);
}
void undo() override
{
m_view->removeFiles(m_prefixIndex, m_firstFile, m_lastFile);
}
int m_prefixIndex;
int m_cursorFileIndex;
int m_firstFile;
int m_lastFile;
const QStringList m_fileNames;
};
class AddEmptyPrefixCommand : public ViewCommand
{
public:
AddEmptyPrefixCommand(ResourceView *view) : ViewCommand(view) {}
private:
void redo() override
{
m_prefixArrayIndex = m_view->addPrefix().row();
}
void undo() override
{
const QModelIndex prefixModelIndex = m_view->model()->index(
m_prefixArrayIndex, 0, QModelIndex());
delete m_view->removeEntry(prefixModelIndex);
}
int m_prefixArrayIndex;
};
ResourceView::ResourceView(RelativeResourceModel *model, QUndoStack *history, QWidget *parent) :
Utils::TreeView(parent),
m_qrcModel(model),
m_history(history),
m_mergeId(-1)
{
advanceMergeId();
setModel(m_qrcModel);
setContextMenuPolicy(Qt::CustomContextMenu);
setEditTriggers(EditKeyPressed);
setFrameStyle(QFrame::NoFrame);
setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
header()->hide();
connect(this, &QWidget::customContextMenuRequested, this, &ResourceView::showContextMenu);
connect(this, &QAbstractItemView::activated, this, &ResourceView::onItemActivated);
}
void ResourceView::findSamePlacePostDeletionModelIndex(int &row, QModelIndex &parent) const
{
// Concept:
// - Make selection stay on same Y level
// - Enable user to hit delete several times in row
const bool hasLowerBrother = m_qrcModel->hasIndex(row + 1, 0, parent);
if (hasLowerBrother) {
// First or mid child -> lower brother
// o
// +--o
// +-[o] <-- deleted
// +--o <-- chosen
// o
// --> return unmodified
} else {
if (parent == QModelIndex()) {
// Last prefix node
if (row == 0) {
// Last and only prefix node
// [o] <-- deleted
// +--o
// +--o
row = -1;
parent = QModelIndex();
} else {
const QModelIndex upperBrother = m_qrcModel->index(row - 1,
0, parent);
if (m_qrcModel->hasChildren(upperBrother)) {
// o
// +--o <-- selected
// [o] <-- deleted
row = m_qrcModel->rowCount(upperBrother) - 1;
parent = upperBrother;
} else {
// o
// o <-- selected
// [o] <-- deleted
row--;
}
}
} else {
// Last file node
const bool hasPrefixBelow = m_qrcModel->hasIndex(parent.row() + 1,
parent.column(), QModelIndex());
if (hasPrefixBelow) {
// Last child or parent with lower brother -> lower brother of parent
// o
// +--o
// +-[o] <-- deleted
// o <-- chosen
row = parent.row() + 1;
parent = QModelIndex();
} else {
const bool onlyChild = row == 0;
if (onlyChild) {
// Last and only child of last parent -> parent
// o <-- chosen
// +-[o] <-- deleted
row = parent.row();
parent = m_qrcModel->parent(parent);
} else {
// Last child of last parent -> upper brother
// o
// +--o <-- chosen
// +-[o] <-- deleted
row--;
}
}
}
}
}
EntryBackup * ResourceView::removeEntry(const QModelIndex &index)
{
return m_qrcModel->removeEntry(index);
}
QStringList ResourceView::existingFilesSubtracted(int prefixIndex, const QStringList &fileNames) const
{
return m_qrcModel->existingFilesSubtracted(prefixIndex, fileNames);
}
void ResourceView::addFiles(int prefixIndex, const QStringList &fileNames, int cursorFile,
int &firstFile, int &lastFile)
{
m_qrcModel->addFiles(prefixIndex, fileNames, cursorFile, firstFile, lastFile);
// Expand prefix node
const QModelIndex prefixModelIndex = m_qrcModel->index(prefixIndex, 0, QModelIndex());
if (prefixModelIndex.isValid())
this->setExpanded(prefixModelIndex, true);
}
void ResourceView::removeFiles(int prefixIndex, int firstFileIndex, int lastFileIndex)
{
Q_ASSERT(prefixIndex >= 0 && prefixIndex < m_qrcModel->rowCount(QModelIndex()));
const QModelIndex prefixModelIndex = m_qrcModel->index(prefixIndex, 0, QModelIndex());
Q_ASSERT(prefixModelIndex != QModelIndex());
Q_ASSERT(firstFileIndex >= 0 && firstFileIndex < m_qrcModel->rowCount(prefixModelIndex));
Q_ASSERT(lastFileIndex >= 0 && lastFileIndex < m_qrcModel->rowCount(prefixModelIndex));
for (int i = lastFileIndex; i >= firstFileIndex; i--) {
const QModelIndex index = m_qrcModel->index(i, 0, prefixModelIndex);
delete removeEntry(index);
}
}
void ResourceView::keyPressEvent(QKeyEvent *e)
{
if (e->key() == Qt::Key_Delete || e->key() == Qt::Key_Backspace)
emit removeItem();
else
Utils::TreeView::keyPressEvent(e);
}
QModelIndex ResourceView::addPrefix()
{
const QModelIndex idx = m_qrcModel->addNewPrefix();
selectionModel()->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect);
return idx;
}
QList<QModelIndex> ResourceView::nonExistingFiles()
{
return m_qrcModel->nonExistingFiles();
}
void ResourceView::refresh()
{
m_qrcModel->refresh();
QModelIndex idx = currentIndex();
setModel(nullptr);
setModel(m_qrcModel);
setCurrentIndex(idx);
expandAll();
}
QStringList ResourceView::fileNamesToAdd()
{
return QFileDialog::getOpenFileNames(this, Tr::tr("Open File"),
m_qrcModel->absolutePath(QString()),
Tr::tr("All files (*)"));
}
QString ResourceView::currentAlias() const
{
const QModelIndex current = currentIndex();
if (!current.isValid())
return QString();
return m_qrcModel->alias(current);
}
QString ResourceView::currentPrefix() const
{
const QModelIndex current = currentIndex();
if (!current.isValid())
return QString();
const QModelIndex preindex = m_qrcModel->prefixIndex(current);
QString prefix, file;
m_qrcModel->getItem(preindex, prefix, file);
return prefix;
}
QString ResourceView::currentLanguage() const
{
const QModelIndex current = currentIndex();
if (!current.isValid())
return QString();
const QModelIndex preindex = m_qrcModel->prefixIndex(current);
return m_qrcModel->lang(preindex);
}
QString ResourceView::currentResourcePath() const
{
const QModelIndex current = currentIndex();
if (!current.isValid())
return QString();
const QString alias = m_qrcModel->alias(current);
if (!alias.isEmpty())
return QLatin1Char(':') + currentPrefix() + QLatin1Char('/') + alias;
return QLatin1Char(':') + currentPrefix() + QLatin1Char('/') + m_qrcModel->relativePath(m_qrcModel->file(current));
}
QString ResourceView::getCurrentValue(NodeProperty property) const
{
switch (property) {
case AliasProperty: return currentAlias();
case PrefixProperty: return currentPrefix();
case LanguageProperty: return currentLanguage();
default: Q_ASSERT(false); return QString(); // Kill warning
}
}
void ResourceView::changeValue(const QModelIndex &nodeIndex, NodeProperty property,
const QString &value)
{
switch (property) {
case AliasProperty: m_qrcModel->changeAlias(nodeIndex, value); return;
case PrefixProperty: m_qrcModel->changePrefix(nodeIndex, value); return;
case LanguageProperty: m_qrcModel->changeLang(nodeIndex, value); return;
default: Q_ASSERT(false);
}
}
void ResourceView::onItemActivated(const QModelIndex &index)
{
const QString fileName = m_qrcModel->file(index);
if (fileName.isEmpty())
return;
emit itemActivated(fileName);
}
void ResourceView::showContextMenu(const QPoint &pos)
{
const QModelIndex index = indexAt(pos);
const QString fileName = m_qrcModel->file(index);
if (fileName.isEmpty())
return;
emit contextMenuShown(mapToGlobal(pos), fileName);
}
void ResourceView::advanceMergeId()
{
m_mergeId++;
if (m_mergeId < 0)
m_mergeId = 0;
}
void ResourceView::addUndoCommand(const QModelIndex &nodeIndex, NodeProperty property,
const QString &before, const QString &after)
{
QUndoCommand * const command = new ModifyPropertyCommand(this, nodeIndex, property,
m_mergeId, before, after);
m_history->push(command);
}
void ResourceView::setCurrentAlias(const QString &before, const QString &after)
{
const QModelIndex current = currentIndex();
if (!current.isValid())
return;
addUndoCommand(current, AliasProperty, before, after);
}
void ResourceView::setCurrentPrefix(const QString &before, const QString &after)
{
const QModelIndex current = currentIndex();
if (!current.isValid())
return;
const QModelIndex preindex = m_qrcModel->prefixIndex(current);
addUndoCommand(preindex, PrefixProperty, before, after);
}
void ResourceView::setCurrentLanguage(const QString &before, const QString &after)
{
const QModelIndex current = currentIndex();
if (!current.isValid())
return;
const QModelIndex preindex = m_qrcModel->prefixIndex(current);
addUndoCommand(preindex, LanguageProperty, before, after);
}
bool ResourceView::isPrefix(const QModelIndex &index) const
{
if (!index.isValid())
return false;
const QModelIndex preindex = m_qrcModel->prefixIndex(index);
if (preindex == index)
return true;
return false;
}
FilePath ResourceView::filePath() const
{
return m_qrcModel->filePath();
}
void ResourceView::setResourceDragEnabled(bool e)
{
setDragEnabled(e);
m_qrcModel->setResourceDragEnabled(e);
}
bool ResourceView::resourceDragEnabled() const
{
return m_qrcModel->resourceDragEnabled();
}
QrcEditor::QrcEditor(RelativeResourceModel *model, QWidget *parent)
: Core::MiniSplitter(Qt::Vertical, parent),
m_treeview(new ResourceView(model, &m_history))
@@ -428,3 +1028,5 @@ void QrcEditor::onRedo()
}
} // ResourceEditor::Internal
#include "qrceditor.moc"

View File

@@ -3,9 +3,8 @@
#pragma once
#include "resourceview.h"
#include <coreplugin/minisplitter.h>
#include <QUndoStack>
QT_BEGIN_NAMESPACE
@@ -16,6 +15,9 @@ QT_END_NAMESPACE
namespace ResourceEditor::Internal {
class RelativeResourceModel;
class ResourceView;
class QrcEditor : public Core::MiniSplitter
{
Q_OBJECT

View File

@@ -112,7 +112,7 @@ Core::IDocument::OpenResult ResourceFile::load()
}
QByteArray data = file.readAll();
// Detect line ending style
m_textFileFormat = Utils::TextFileFormat::detect(data);
m_textFileFormat = TextFileFormat::detect(data);
// we always write UTF-8 when saving
m_textFileFormat.codec = QTextCodec::codecForName("UTF-8");
file.close();
@@ -238,7 +238,6 @@ void ResourceFile::refresh()
int ResourceFile::addFile(int prefix_idx, const QString &file, int file_idx)
{
Prefix * const p = m_prefix_list[prefix_idx];
Q_ASSERT(p);
FileList &files = p->file_list;
Q_ASSERT(file_idx >= -1 && file_idx <= files.size());
if (file_idx == -1)
@@ -358,8 +357,8 @@ bool ResourceFile::renameFile(const QString &fileName, const QString &newFileNam
if (entries.at(0)->exists()) {
for (File *file : std::as_const(entries))
file->setExists(true);
success = Core::FileUtils::renameFile(Utils::FilePath::fromString(entries.at(0)->name),
Utils::FilePath::fromString(newFileName));
success = Core::FileUtils::renameFile(FilePath::fromString(entries.at(0)->name),
FilePath::fromString(newFileName));
}
if (success) {
@@ -557,7 +556,7 @@ void ResourceFile::clearPrefixList()
ResourceModel::ResourceModel()
{
static QIcon resourceFolderIcon = Utils::FileIconProvider::directoryIcon(QLatin1String(ProjectExplorer::Constants::FILEOVERLAY_QRC));
static QIcon resourceFolderIcon = FileIconProvider::directoryIcon(QLatin1String(ProjectExplorer::Constants::FILEOVERLAY_QRC));
m_prefixIcon = resourceFolderIcon;
}
@@ -755,7 +754,6 @@ QVariant ResourceModel::data(const QModelIndex &index, int role) const
appendParenthesized(lang, stringRes);
} else {
// File node
Q_ASSERT(file);
QString conv_file = m_resource_file.relativePath(file->name);
stringRes = QDir::fromNativeSeparators(conv_file);
const QString alias = file->alias;
@@ -768,13 +766,12 @@ QVariant ResourceModel::data(const QModelIndex &index, int role) const
case Qt::DecorationRole:
if (isFileNode) {
// File node
Q_ASSERT(file);
if (file->icon.isNull()) {
const QString path = m_resource_file.absolutePath(file->name);
if (iconFileExtension(path))
file->icon = QIcon(path);
else
file->icon = Utils::FileIconProvider::icon(Utils::FilePath::fromString(path));
file->icon = FileIconProvider::icon(FilePath::fromString(path));
}
if (!file->icon.isNull())
result = file->icon;
@@ -785,7 +782,6 @@ QVariant ResourceModel::data(const QModelIndex &index, int role) const
break;
case Qt::EditRole:
if (isFileNode) {
Q_ASSERT(file);
QString conv_file = m_resource_file.relativePath(file->name);
result = QDir::fromNativeSeparators(conv_file);
}
@@ -793,9 +789,8 @@ QVariant ResourceModel::data(const QModelIndex &index, int role) const
case Qt::ForegroundRole:
if (isFileNode) {
// File node
Q_ASSERT(file);
if (!file->exists())
result = Utils::creatorColor(Utils::Theme::TextColorError);
result = Utils::creatorColor(Theme::TextColorError);
}
break;
default:
@@ -812,8 +807,7 @@ bool ResourceModel::setData(const QModelIndex &index, const QVariant &value, int
return false;
const QDir baseDir = filePath().toFileInfo().absoluteDir();
Utils::FilePath newFileName = Utils::FilePath::fromUserInput(
baseDir.absoluteFilePath(value.toString()));
FilePath newFileName = FilePath::fromUserInput(baseDir.absoluteFilePath(value.toString()));
if (newFileName.isEmpty())
return false;
@@ -840,7 +834,6 @@ void ResourceModel::getItem(const QModelIndex &index, QString &prefix, QString &
if (isFileNode) {
const File *f = node->file();
Q_ASSERT(f);
if (!f->alias.isEmpty())
file = f->alias;
else
@@ -1229,11 +1222,11 @@ EntryBackup * RelativeResourceModel::removeEntry(const QModelIndex &index)
deleteItem(index);
return new FileEntryBackup(*this, prefixIndex.row(), index.row(), fileNameBackup, aliasBackup);
}
Utils::RemoveFileDialog removeFileDialog(Utils::FilePath::fromString(fileNameBackup),
Core::ICore::dialogParent());
RemoveFileDialog removeFileDialog(FilePath::fromString(fileNameBackup),
Core::ICore::dialogParent());
if (removeFileDialog.exec() == QDialog::Accepted) {
deleteItem(index);
Core::FileUtils::removeFiles({Utils::FilePath::fromString(fileNameBackup)},
Core::FileUtils::removeFiles({FilePath::fromString(fileNameBackup)},
removeFileDialog.isDeleteFileChecked());
return new FileEntryBackup(*this, prefixIndex.row(), index.row(), fileNameBackup, aliasBackup);
}

View File

@@ -1,339 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "resourceview.h"
#include "../resourceeditortr.h"
#include "undocommands_p.h"
#include <coreplugin/fileutils.h>
#include <coreplugin/icore.h>
#include <QAction>
#include <QApplication>
#include <QDebug>
#include <QFileDialog>
#include <QHeaderView>
#include <QInputDialog>
#include <QMenu>
#include <QMouseEvent>
#include <QUndoStack>
namespace ResourceEditor::Internal {
ResourceView::ResourceView(RelativeResourceModel *model, QUndoStack *history, QWidget *parent) :
Utils::TreeView(parent),
m_qrcModel(model),
m_history(history),
m_mergeId(-1)
{
advanceMergeId();
setModel(m_qrcModel);
setContextMenuPolicy(Qt::CustomContextMenu);
setEditTriggers(EditKeyPressed);
header()->hide();
connect(this, &QWidget::customContextMenuRequested, this, &ResourceView::showContextMenu);
connect(this, &QAbstractItemView::activated, this, &ResourceView::onItemActivated);
}
ResourceView::~ResourceView() = default;
void ResourceView::findSamePlacePostDeletionModelIndex(int &row, QModelIndex &parent) const
{
// Concept:
// - Make selection stay on same Y level
// - Enable user to hit delete several times in row
const bool hasLowerBrother = m_qrcModel->hasIndex(row + 1,
0, parent);
if (hasLowerBrother) {
// First or mid child -> lower brother
// o
// +--o
// +-[o] <-- deleted
// +--o <-- chosen
// o
// --> return unmodified
} else {
if (parent == QModelIndex()) {
// Last prefix node
if (row == 0) {
// Last and only prefix node
// [o] <-- deleted
// +--o
// +--o
row = -1;
parent = QModelIndex();
} else {
const QModelIndex upperBrother = m_qrcModel->index(row - 1,
0, parent);
if (m_qrcModel->hasChildren(upperBrother)) {
// o
// +--o <-- selected
// [o] <-- deleted
row = m_qrcModel->rowCount(upperBrother) - 1;
parent = upperBrother;
} else {
// o
// o <-- selected
// [o] <-- deleted
row--;
}
}
} else {
// Last file node
const bool hasPrefixBelow = m_qrcModel->hasIndex(parent.row() + 1,
parent.column(), QModelIndex());
if (hasPrefixBelow) {
// Last child or parent with lower brother -> lower brother of parent
// o
// +--o
// +-[o] <-- deleted
// o <-- chosen
row = parent.row() + 1;
parent = QModelIndex();
} else {
const bool onlyChild = row == 0;
if (onlyChild) {
// Last and only child of last parent -> parent
// o <-- chosen
// +-[o] <-- deleted
row = parent.row();
parent = m_qrcModel->parent(parent);
} else {
// Last child of last parent -> upper brother
// o
// +--o <-- chosen
// +-[o] <-- deleted
row--;
}
}
}
}
}
EntryBackup * ResourceView::removeEntry(const QModelIndex &index)
{
Q_ASSERT(m_qrcModel);
return m_qrcModel->removeEntry(index);
}
QStringList ResourceView::existingFilesSubtracted(int prefixIndex, const QStringList &fileNames) const
{
return m_qrcModel->existingFilesSubtracted(prefixIndex, fileNames);
}
void ResourceView::addFiles(int prefixIndex, const QStringList &fileNames, int cursorFile,
int &firstFile, int &lastFile)
{
Q_ASSERT(m_qrcModel);
m_qrcModel->addFiles(prefixIndex, fileNames, cursorFile, firstFile, lastFile);
// Expand prefix node
const QModelIndex prefixModelIndex = m_qrcModel->index(prefixIndex, 0, QModelIndex());
if (prefixModelIndex.isValid())
this->setExpanded(prefixModelIndex, true);
}
void ResourceView::removeFiles(int prefixIndex, int firstFileIndex, int lastFileIndex)
{
Q_ASSERT(prefixIndex >= 0 && prefixIndex < m_qrcModel->rowCount(QModelIndex()));
const QModelIndex prefixModelIndex = m_qrcModel->index(prefixIndex, 0, QModelIndex());
Q_ASSERT(prefixModelIndex != QModelIndex());
Q_ASSERT(firstFileIndex >= 0 && firstFileIndex < m_qrcModel->rowCount(prefixModelIndex));
Q_ASSERT(lastFileIndex >= 0 && lastFileIndex < m_qrcModel->rowCount(prefixModelIndex));
for (int i = lastFileIndex; i >= firstFileIndex; i--) {
const QModelIndex index = m_qrcModel->index(i, 0, prefixModelIndex);
delete removeEntry(index);
}
}
void ResourceView::keyPressEvent(QKeyEvent *e)
{
if (e->key() == Qt::Key_Delete || e->key() == Qt::Key_Backspace)
emit removeItem();
else
Utils::TreeView::keyPressEvent(e);
}
QModelIndex ResourceView::addPrefix()
{
const QModelIndex idx = m_qrcModel->addNewPrefix();
selectionModel()->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect);
return idx;
}
QList<QModelIndex> ResourceView::nonExistingFiles()
{
return m_qrcModel->nonExistingFiles();
}
void ResourceView::refresh()
{
m_qrcModel->refresh();
QModelIndex idx = currentIndex();
setModel(nullptr);
setModel(m_qrcModel);
setCurrentIndex(idx);
expandAll();
}
QStringList ResourceView::fileNamesToAdd()
{
return QFileDialog::getOpenFileNames(this, Tr::tr("Open File"),
m_qrcModel->absolutePath(QString()),
Tr::tr("All files (*)"));
}
QString ResourceView::currentAlias() const
{
const QModelIndex current = currentIndex();
if (!current.isValid())
return QString();
return m_qrcModel->alias(current);
}
QString ResourceView::currentPrefix() const
{
const QModelIndex current = currentIndex();
if (!current.isValid())
return QString();
const QModelIndex preindex = m_qrcModel->prefixIndex(current);
QString prefix, file;
m_qrcModel->getItem(preindex, prefix, file);
return prefix;
}
QString ResourceView::currentLanguage() const
{
const QModelIndex current = currentIndex();
if (!current.isValid())
return QString();
const QModelIndex preindex = m_qrcModel->prefixIndex(current);
return m_qrcModel->lang(preindex);
}
QString ResourceView::currentResourcePath() const
{
const QModelIndex current = currentIndex();
if (!current.isValid())
return QString();
const QString alias = m_qrcModel->alias(current);
if (!alias.isEmpty())
return QLatin1Char(':') + currentPrefix() + QLatin1Char('/') + alias;
return QLatin1Char(':') + currentPrefix() + QLatin1Char('/') + m_qrcModel->relativePath(m_qrcModel->file(current));
}
QString ResourceView::getCurrentValue(NodeProperty property) const
{
switch (property) {
case AliasProperty: return currentAlias();
case PrefixProperty: return currentPrefix();
case LanguageProperty: return currentLanguage();
default: Q_ASSERT(false); return QString(); // Kill warning
}
}
void ResourceView::changeValue(const QModelIndex &nodeIndex, NodeProperty property,
const QString &value)
{
switch (property) {
case AliasProperty: m_qrcModel->changeAlias(nodeIndex, value); return;
case PrefixProperty: m_qrcModel->changePrefix(nodeIndex, value); return;
case LanguageProperty: m_qrcModel->changeLang(nodeIndex, value); return;
default: Q_ASSERT(false);
}
}
void ResourceView::onItemActivated(const QModelIndex &index)
{
const QString fileName = m_qrcModel->file(index);
if (fileName.isEmpty())
return;
emit itemActivated(fileName);
}
void ResourceView::showContextMenu(const QPoint &pos)
{
const QModelIndex index = indexAt(pos);
const QString fileName = m_qrcModel->file(index);
if (fileName.isEmpty())
return;
emit contextMenuShown(mapToGlobal(pos), fileName);
}
void ResourceView::advanceMergeId()
{
m_mergeId++;
if (m_mergeId < 0)
m_mergeId = 0;
}
void ResourceView::addUndoCommand(const QModelIndex &nodeIndex, NodeProperty property,
const QString &before, const QString &after)
{
QUndoCommand * const command = new ModifyPropertyCommand(this, nodeIndex, property,
m_mergeId, before, after);
m_history->push(command);
}
void ResourceView::setCurrentAlias(const QString &before, const QString &after)
{
const QModelIndex current = currentIndex();
if (!current.isValid())
return;
addUndoCommand(current, AliasProperty, before, after);
}
void ResourceView::setCurrentPrefix(const QString &before, const QString &after)
{
const QModelIndex current = currentIndex();
if (!current.isValid())
return;
const QModelIndex preindex = m_qrcModel->prefixIndex(current);
addUndoCommand(preindex, PrefixProperty, before, after);
}
void ResourceView::setCurrentLanguage(const QString &before, const QString &after)
{
const QModelIndex current = currentIndex();
if (!current.isValid())
return;
const QModelIndex preindex = m_qrcModel->prefixIndex(current);
addUndoCommand(preindex, LanguageProperty, before, after);
}
bool ResourceView::isPrefix(const QModelIndex &index) const
{
if (!index.isValid())
return false;
const QModelIndex preindex = m_qrcModel->prefixIndex(index);
if (preindex == index)
return true;
return false;
}
Utils::FilePath ResourceView::filePath() const
{
return m_qrcModel->filePath();
}
void ResourceView::setResourceDragEnabled(bool e)
{
setDragEnabled(e);
m_qrcModel->setResourceDragEnabled(e);
}
bool ResourceView::resourceDragEnabled() const
{
return m_qrcModel->resourceDragEnabled();
}
} // ResourceEditor::Internal

View File

@@ -1,86 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include "resourcefile_p.h"
#include <utils/itemviews.h>
#include <QPoint>
QT_BEGIN_NAMESPACE
class QUndoStack;
QT_END_NAMESPACE
namespace ResourceEditor::Internal {
class RelativeResourceModel;
class ResourceView : public Utils::TreeView
{
Q_OBJECT
public:
enum NodeProperty {
AliasProperty,
PrefixProperty,
LanguageProperty
};
explicit ResourceView(RelativeResourceModel *model, QUndoStack *history, QWidget *parent = nullptr);
~ResourceView() override;
Utils::FilePath filePath() const;
bool isPrefix(const QModelIndex &index) const;
QString currentAlias() const;
QString currentPrefix() const;
QString currentLanguage() const;
QString currentResourcePath() const;
void setResourceDragEnabled(bool e);
bool resourceDragEnabled() const;
void findSamePlacePostDeletionModelIndex(int &row, QModelIndex &parent) const;
EntryBackup *removeEntry(const QModelIndex &index);
QStringList existingFilesSubtracted(int prefixIndex, const QStringList &fileNames) const;
void addFiles(int prefixIndex, const QStringList &fileNames, int cursorFile,
int &firstFile, int &lastFile);
void removeFiles(int prefixIndex, int firstFileIndex, int lastFileIndex);
QStringList fileNamesToAdd();
QModelIndex addPrefix();
QList<QModelIndex> nonExistingFiles();
void refresh();
void setCurrentAlias(const QString &before, const QString &after);
void setCurrentPrefix(const QString &before, const QString &after);
void setCurrentLanguage(const QString &before, const QString &after);
void advanceMergeId();
QString getCurrentValue(NodeProperty property) const;
void changeValue(const QModelIndex &nodeIndex, NodeProperty property, const QString &value);
signals:
void removeItem();
void itemActivated(const QString &fileName);
void contextMenuShown(const QPoint &globalPos, const QString &fileName);
protected:
void keyPressEvent(QKeyEvent *e) override;
private:
void onItemActivated(const QModelIndex &index);
void showContextMenu(const QPoint &pos);
void addUndoCommand(const QModelIndex &nodeIndex, NodeProperty property,
const QString &before, const QString &after);
RelativeResourceModel *m_qrcModel;
QUndoStack *m_history;
int m_mergeId;
};
} // ResourceEditor::Internal

View File

@@ -1,188 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "undocommands_p.h"
#include <QModelIndex>
namespace ResourceEditor::Internal {
ViewCommand::ViewCommand(ResourceView *view)
: m_view(view)
{ }
ViewCommand::~ViewCommand() = default;
ModelIndexViewCommand::ModelIndexViewCommand(ResourceView *view)
: ViewCommand(view)
{ }
ModelIndexViewCommand::~ModelIndexViewCommand() = default;
void ModelIndexViewCommand::storeIndex(const QModelIndex &index)
{
if (m_view->isPrefix(index)) {
m_prefixArrayIndex = index.row();
m_fileArrayIndex = -1;
} else {
m_fileArrayIndex = index.row();
m_prefixArrayIndex = m_view->model()->parent(index).row();
}
}
QModelIndex ModelIndexViewCommand::makeIndex() const
{
const QModelIndex prefixModelIndex
= m_view->model()->index(m_prefixArrayIndex, 0, QModelIndex());
if (m_fileArrayIndex != -1) {
// File node
const QModelIndex fileModelIndex
= m_view->model()->index(m_fileArrayIndex, 0, prefixModelIndex);
return fileModelIndex;
} else {
// Prefix node
return prefixModelIndex;
}
}
ModifyPropertyCommand::ModifyPropertyCommand(ResourceView *view, const QModelIndex &nodeIndex,
ResourceView::NodeProperty property, const int mergeId, const QString &before,
const QString &after)
: ModelIndexViewCommand(view), m_property(property), m_before(before), m_after(after),
m_mergeId(mergeId)
{
storeIndex(nodeIndex);
}
bool ModifyPropertyCommand::mergeWith(const QUndoCommand * command)
{
if (command->id() != id() || m_property != static_cast<const ModifyPropertyCommand *>(command)->m_property)
return false;
// Choose older command (this) and forgot the other
return true;
}
void ModifyPropertyCommand::undo()
{
Q_ASSERT(m_view);
// Save current text in m_after for redo()
m_after = m_view->getCurrentValue(m_property);
// Reset text to m_before
m_view->changeValue(makeIndex(), m_property, m_before);
}
void ModifyPropertyCommand::redo()
{
// Prevent execution from within QUndoStack::push
if (m_after.isNull())
return;
// Bring back text before undo
Q_ASSERT(m_view);
m_view->changeValue(makeIndex(), m_property, m_after);
}
RemoveEntryCommand::RemoveEntryCommand(ResourceView *view, const QModelIndex &index)
: ModelIndexViewCommand(view), m_entry(nullptr), m_isExpanded(true)
{
storeIndex(index);
}
RemoveEntryCommand::~RemoveEntryCommand()
{
freeEntry();
}
void RemoveEntryCommand::redo()
{
freeEntry();
const QModelIndex index = makeIndex();
m_isExpanded = m_view->isExpanded(index);
m_entry = m_view->removeEntry(index);
}
void RemoveEntryCommand::undo()
{
if (m_entry != nullptr) {
m_entry->restore();
Q_ASSERT(m_view != nullptr);
const QModelIndex index = makeIndex();
m_view->setExpanded(index, m_isExpanded);
m_view->setCurrentIndex(index);
freeEntry();
}
}
void RemoveEntryCommand::freeEntry()
{
delete m_entry;
m_entry = nullptr;
}
RemoveMultipleEntryCommand::RemoveMultipleEntryCommand(ResourceView *view, const QList<QModelIndex> &list)
{
m_subCommands.reserve(list.size());
for (const QModelIndex &index : list)
m_subCommands.push_back(new RemoveEntryCommand(view, index));
}
RemoveMultipleEntryCommand::~RemoveMultipleEntryCommand()
{
qDeleteAll(m_subCommands);
}
void RemoveMultipleEntryCommand::redo()
{
auto it = m_subCommands.rbegin();
auto end = m_subCommands.rend();
for (; it != end; ++it)
(*it)->redo();
}
void RemoveMultipleEntryCommand::undo()
{
auto it = m_subCommands.begin();
auto end = m_subCommands.end();
for (; it != end; ++it)
(*it)->undo();
}
AddFilesCommand::AddFilesCommand(ResourceView *view, int prefixIndex, int cursorFileIndex,
const QStringList &fileNames)
: ViewCommand(view), m_prefixIndex(prefixIndex), m_cursorFileIndex(cursorFileIndex),
m_fileNames(fileNames)
{ }
void AddFilesCommand::redo()
{
m_view->addFiles(m_prefixIndex, m_fileNames, m_cursorFileIndex, m_firstFile, m_lastFile);
}
void AddFilesCommand::undo()
{
m_view->removeFiles(m_prefixIndex, m_firstFile, m_lastFile);
}
AddEmptyPrefixCommand::AddEmptyPrefixCommand(ResourceView *view)
: ViewCommand(view)
{ }
void AddEmptyPrefixCommand::redo()
{
m_prefixArrayIndex = m_view->addPrefix().row();
}
void AddEmptyPrefixCommand::undo()
{
const QModelIndex prefixModelIndex = m_view->model()->index(
m_prefixArrayIndex, 0, QModelIndex());
delete m_view->removeEntry(prefixModelIndex);
}
} // ResourceEditor::Internal

View File

@@ -1,150 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include "resourceview.h"
#include <QString>
#include <QUndoCommand>
QT_BEGIN_NAMESPACE
class QModelIndex;
QT_END_NAMESPACE
namespace ResourceEditor::Internal {
/*!
\class ViewCommand
Provides a base for \l ResourceView-related commands.
*/
class ViewCommand : public QUndoCommand
{
protected:
ResourceView *m_view;
ViewCommand(ResourceView *view);
~ViewCommand() override;
};
/*!
\class ModelIndexViewCommand
Provides a mean to store/restore a \l QModelIndex as it cannot
be stored safely in most cases. This is an abstract class.
*/
class ModelIndexViewCommand : public ViewCommand
{
int m_prefixArrayIndex;
int m_fileArrayIndex;
protected:
ModelIndexViewCommand(ResourceView *view);
~ModelIndexViewCommand() override;
void storeIndex(const QModelIndex &index);
QModelIndex makeIndex() const;
};
/*!
\class ModifyPropertyCommand
Modifies the name/prefix/language property of a prefix/file node.
*/
class ModifyPropertyCommand : public ModelIndexViewCommand
{
ResourceView::NodeProperty m_property;
QString m_before;
QString m_after;
int m_mergeId;
public:
ModifyPropertyCommand(ResourceView *view, const QModelIndex &nodeIndex,
ResourceView::NodeProperty property, const int mergeId, const QString &before,
const QString &after = QString());
private:
int id() const override { return m_mergeId; }
bool mergeWith(const QUndoCommand * command) override;
void undo() override;
void redo() override;
};
/*!
\class RemoveEntryCommand
Removes a \l QModelIndex including all children from a \l ResourceView.
*/
class RemoveEntryCommand : public ModelIndexViewCommand
{
EntryBackup *m_entry;
bool m_isExpanded;
public:
RemoveEntryCommand(ResourceView *view, const QModelIndex &index);
~RemoveEntryCommand() override;
private:
void redo() override;
void undo() override;
void freeEntry();
};
/*!
\class RemoveMultipleEntryCommand
Removes multiple \l QModelIndex including all children from a \l ResourceView.
*/
class RemoveMultipleEntryCommand : public QUndoCommand
{
std::vector<QUndoCommand *> m_subCommands;
public:
// list must be in view order
RemoveMultipleEntryCommand(ResourceView *view, const QList<QModelIndex> &list);
~RemoveMultipleEntryCommand() override;
private:
void redo() override;
void undo() override;
};
/*!
\class AddFilesCommand
Adds a list of files to a given prefix node.
*/
class AddFilesCommand : public ViewCommand
{
int m_prefixIndex;
int m_cursorFileIndex;
int m_firstFile;
int m_lastFile;
const QStringList m_fileNames;
public:
AddFilesCommand(ResourceView *view, int prefixIndex, int cursorFileIndex,
const QStringList &fileNames);
private:
void redo() override;
void undo() override;
};
/*!
\class AddEmptyPrefixCommand
Adds a new, empty prefix node.
*/
class AddEmptyPrefixCommand : public ViewCommand
{
int m_prefixArrayIndex;
public:
AddEmptyPrefixCommand(ResourceView *view);
private:
void redo() override;
void undo() override;
};
} // ResourceEditor::Internal

View File

@@ -50,8 +50,8 @@ public:
OpenResult open(QString *errorString, const FilePath &filePath,
const FilePath &realFilePath) final;
QString plainText() const;
QByteArray contents() const final;
QString plainText() const { return m_model.contents(); }
QByteArray contents() const final { return m_model.contents().toUtf8(); }
bool setContents(const QByteArray &contents) final;
bool shouldAutoSave() const final { return m_shouldAutoSave; }
bool isModified() const final { return m_model.dirty(); }
@@ -240,16 +240,6 @@ bool ResourceEditorDocument::saveImpl(QString *errorString, const FilePath &file
return true;
}
QString ResourceEditorDocument::plainText() const
{
return m_model.contents();
}
QByteArray ResourceEditorDocument::contents() const
{
return m_model.contents().toUtf8();
}
bool ResourceEditorDocument::setContents(const QByteArray &contents)
{
TempFileSaver saver;

View File

@@ -31,8 +31,6 @@ QtcPlugin {
files: [
"qrceditor.cpp", "qrceditor.h",
"resourcefile.cpp", "resourcefile_p.h",
"resourceview.cpp", "resourceview.h",
"undocommands.cpp", "undocommands_p.h",
]
}
}