/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt VS Tools. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** 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 The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "resourceview.h" #include "undocommands_p.h" #include #include #include #include #include #include #include #include #include namespace SharedTools { /*! \class FileEntryBackup Backups a file node. */ class FileEntryBackup : public EntryBackup { private: int m_fileIndex; QString m_alias; public: FileEntryBackup(ResourceModel &model, int prefixIndex, int fileIndex, const QString &fileName, const QString &alias) : EntryBackup(model, prefixIndex, fileName), m_fileIndex(fileIndex), m_alias(alias) { } void restore() const; }; void FileEntryBackup::restore() const { m_model->insertFile(m_prefixIndex, m_fileIndex, m_name, m_alias); } /*! \class PrefixEntryBackup Backups a prefix node including children. */ class PrefixEntryBackup : public EntryBackup { private: QString m_language; QList m_files; public: PrefixEntryBackup(ResourceModel &model, int prefixIndex, const QString &prefix, const QString &language, const QList &files) : EntryBackup(model, prefixIndex, prefix), m_language(language), m_files(files) { } void restore() const; }; void PrefixEntryBackup::restore() const { m_model->insertPrefix(m_prefixIndex, m_name, m_language); foreach (const FileEntryBackup &entry, m_files) { entry.restore(); } } namespace Internal { class RelativeResourceModel : public ResourceModel { public: RelativeResourceModel(const ResourceFile &resource_file, QObject *parent = 0); QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const { if (!index.isValid()) return QVariant(); /* void const * const internalPointer = index.internalPointer(); if ((role == Qt::DisplayRole) && (internalPointer != NULL)) return ResourceModel::data(index, Qt::ToolTipRole); */ return ResourceModel::data(index, role); } void setResourceDragEnabled(bool e) { m_resourceDragEnabled = e; } bool resourceDragEnabled() const { return m_resourceDragEnabled; } virtual Qt::ItemFlags flags(const QModelIndex &index) const; EntryBackup * removeEntry(const QModelIndex &index); private: bool m_resourceDragEnabled; }; RelativeResourceModel::RelativeResourceModel(const ResourceFile &resource_file, QObject *parent) : ResourceModel(resource_file, parent), m_resourceDragEnabled(false) { } Qt::ItemFlags RelativeResourceModel::flags(const QModelIndex &index) const { Qt::ItemFlags rc = ResourceModel::flags(index); if ((rc & Qt::ItemIsEnabled) && m_resourceDragEnabled) rc |= Qt::ItemIsDragEnabled; return rc; } EntryBackup * RelativeResourceModel::removeEntry(const QModelIndex &index) { const QModelIndex prefixIndex = this->prefixIndex(index); const bool isPrefixNode = (prefixIndex == index); // Create backup, remove, return backup if (isPrefixNode) { QString dummy; QString prefixBackup; getItem(index, prefixBackup, dummy); const QString languageBackup = lang(index); const int childCount = rowCount(index); QList filesBackup; for (int i = 0; i < childCount; i++) { const QModelIndex childIndex = this->index(i, 0, index); const QString fileNameBackup = file(childIndex); const QString aliasBackup = alias(childIndex); FileEntryBackup entry(*this, index.row(), i, fileNameBackup, aliasBackup); filesBackup << entry; } deleteItem(index); return new PrefixEntryBackup(*this, index.row(), prefixBackup, languageBackup, filesBackup); } else { const QString fileNameBackup = file(index); const QString aliasBackup = alias(index); deleteItem(index); return new FileEntryBackup(*this, prefixIndex.row(), index.row(), fileNameBackup, aliasBackup); } } } // namespace Internal ResourceView::ResourceView(QUndoStack *history, QWidget *parent) : QTreeView(parent), m_qrcModel(new Internal::RelativeResourceModel(m_qrcFile, this)), m_addFile(0), m_editAlias(0), m_removeItem(0), m_addPrefix(0), m_editPrefix(0), m_editLang(0), m_viewMenu(0), m_defaultAddFile(false), m_history(history), m_mergeId(-1) { advanceMergeId(); setModel(m_qrcModel); header()->hide(); connect(m_qrcModel, SIGNAL(dirtyChanged(bool)), this, SIGNAL(dirtyChanged(bool))); setupMenu(); setDefaultAddFileEnabled(true); enableContextMenu(true); } ResourceView::~ResourceView() { } void ResourceView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) { Q_UNUSED(current) Q_UNUSED(previous) emit currentIndexChanged(); } bool ResourceView::isDirty() const { return m_qrcModel->dirty(); } void ResourceView::setDirty(bool dirty) { m_qrcModel->setDirty(dirty); } void ResourceView::setDefaultAddFileEnabled(bool enable) { m_defaultAddFile = enable; } bool ResourceView::defaultAddFileEnabled() const { return m_defaultAddFile; } 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); } 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::enableContextMenu(bool enable) { if (enable) { connect(this, SIGNAL(clicked(const QModelIndex &)), this, SLOT(popupMenu(const QModelIndex &))); } else { disconnect(this, SIGNAL(clicked(const QModelIndex &)), this, SLOT(popupMenu(const QModelIndex &))); } } void ResourceView::setupMenu() { m_viewMenu = new QMenu(this); /* m_addFile = m_viewMenu->addAction(tr("Add Files..."), this, SIGNAL(addFiles())); m_editAlias = m_viewMenu->addAction(tr("Change Alias..."), this, SLOT(onEditAlias())); m_addPrefix = m_viewMenu->addAction(tr("Add Prefix..."), this, SLOT(addPrefix())); m_editPrefix = m_viewMenu->addAction(tr("Change Prefix..."), this, SLOT(onEditPrefix())); m_editLang = m_viewMenu->addAction(tr("Change Language..."), this, SLOT(onEditLang())); m_viewMenu->addSeparator(); m_removeItem = m_viewMenu->addAction(tr("Remove Item"), this, SLOT(removeItem())); */ m_addFile = m_viewMenu->addAction(tr("Add Files..."), this, SLOT(onAddFiles())); m_editAlias = m_viewMenu->addAction(tr("Change Alias..."), this, SLOT(onEditAlias())); m_addPrefix = m_viewMenu->addAction(tr("Add Prefix..."), this, SIGNAL(addPrefixTriggered())); m_editPrefix = m_viewMenu->addAction(tr("Change Prefix..."), this, SLOT(onEditPrefix())); m_editLang = m_viewMenu->addAction(tr("Change Language..."), this, SLOT(onEditLang())); m_viewMenu->addSeparator(); m_removeItem = m_viewMenu->addAction(tr("Remove Item"), this, SIGNAL(removeItem())); } void ResourceView::mouseReleaseEvent(QMouseEvent *e) { m_releasePos = e->globalPos(); if (e->button() != Qt::RightButton) m_releasePos = QPoint(); QTreeView::mouseReleaseEvent(e); } void ResourceView::keyPressEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Delete) removeItem(); else QTreeView::keyPressEvent(e); } void ResourceView::popupMenu(const QModelIndex &index) { if (!m_releasePos.isNull()) { m_addFile->setEnabled(index.isValid()); m_editPrefix->setEnabled(index.isValid()); m_editLang->setEnabled(index.isValid()); m_removeItem->setEnabled(index.isValid()); m_viewMenu->popup(m_releasePos); } } QModelIndex ResourceView::addPrefix() { const QModelIndex idx = m_qrcModel->addNewPrefix(); selectionModel()->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect); return idx; } QStringList ResourceView::fileNamesToAdd() { return QFileDialog::getOpenFileNames(this, tr("Open File"), m_qrcModel->absolutePath(QString()), tr("All files (*)")); } void ResourceView::onAddFiles() { emit addFilesTriggered(currentPrefix()); } void ResourceView::addFiles(QStringList fileList, const QModelIndex &index) { if (fileList.isEmpty()) return; QModelIndex idx = index; if (!m_qrcModel->hasChildren(QModelIndex())) { idx = addPrefix(); expand(idx); } idx = m_qrcModel->addFiles(idx, fileList); if (idx.isValid()) { const QModelIndex preindex = m_qrcModel->prefixIndex(index); setExpanded(preindex, true); selectionModel()->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect); QString prefix, file; m_qrcModel->getItem(preindex, prefix, file); // XXX emit filesAdded(prefix, fileList); } } void ResourceView::addFile(const QString &prefix, const QString &file) { const QModelIndex preindex = m_qrcModel->getIndex(prefix, QString()); addFiles(QStringList(file), preindex); } /* void ResourceView::removeItem() { const QModelIndex index = currentIndex(); m_qrcModel->deleteItem(index); } void ResourceView::removeFile(const QString &prefix, const QString &file) { const QModelIndex index = m_qrcModel->getIndex(prefix, file); if (index.isValid()) m_qrcModel->deleteItem(index); } */ void ResourceView::onEditPrefix() { QModelIndex index = currentIndex(); changePrefix(index); } void ResourceView::onEditLang() { const QModelIndex index = currentIndex(); changeLang(index); } void ResourceView::onEditAlias() { const QModelIndex index = currentIndex(); changeAlias(index); } bool ResourceView::load(const QString &fileName) { const QFileInfo fi(fileName); m_qrcModel->setFileName(fi.absoluteFilePath()); if (!fi.exists()) return false; return m_qrcModel->reload(); } bool ResourceView::save() { return m_qrcModel->save(); } void ResourceView::changePrefix(const QModelIndex &index) { bool ok = false; const QModelIndex preindex = m_qrcModel->prefixIndex(index); QString prefixBefore; QString dummy; m_qrcModel->getItem(preindex, prefixBefore, dummy); QString const prefixAfter = QInputDialog::getText(this, tr("Change Prefix"), tr("Input prefix:"), QLineEdit::Normal, prefixBefore, &ok); if (ok) addUndoCommand(preindex, PrefixProperty, prefixBefore, prefixAfter); } void ResourceView::changeLang(const QModelIndex &index) { bool ok = false; const QModelIndex preindex = m_qrcModel->prefixIndex(index); QString const langBefore = m_qrcModel->lang(preindex); QString const langAfter = QInputDialog::getText(this, tr("Change Language"), tr("Language:"), QLineEdit::Normal, langBefore, &ok); if (ok) { addUndoCommand(preindex, LanguageProperty, langBefore, langAfter); } } void ResourceView::changeAlias(const QModelIndex &index) { if (!index.parent().isValid()) return; bool ok = false; QString const aliasBefore = m_qrcModel->alias(index); QString const aliasAfter = QInputDialog::getText(this, tr("Change File Alias"), tr("Alias:"), QLineEdit::Normal, aliasBefore, &ok); if (ok) addUndoCommand(index, AliasProperty, aliasBefore, aliasAfter); } 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::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::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; } QString ResourceView::fileName() const { return m_qrcModel->fileName(); } void ResourceView::setFileName(const QString &fileName) { m_qrcModel->setFileName(fileName); } void ResourceView::setResourceDragEnabled(bool e) { setDragEnabled(e); m_qrcModel->setResourceDragEnabled(e); } bool ResourceView::resourceDragEnabled() const { return m_qrcModel->resourceDragEnabled(); } } // namespace SharedTools