From c98115e2f6f59b807382c41256d7f1a86009aa40 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Wed, 19 Jun 2019 17:24:23 -0400 Subject: in the process of moving stuff to modinfodialogconflicts.h/cpp --- src/modinfodialogconflicts.cpp | 530 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 530 insertions(+) create mode 100644 src/modinfodialogconflicts.cpp (limited to 'src/modinfodialogconflicts.cpp') diff --git a/src/modinfodialogconflicts.cpp b/src/modinfodialogconflicts.cpp new file mode 100644 index 00000000..17313436 --- /dev/null +++ b/src/modinfodialogconflicts.cpp @@ -0,0 +1,530 @@ +#include "modinfodialogconflicts.h" +#include "ui_modinfodialog.h" +#include "utility.h" + +using namespace MOShared; +using namespace MOBase; + +int naturalCompare(const QString& a, const QString& b) +{ + static QCollator c = []{ + QCollator c; + c.setNumericMode(true); + c.setCaseSensitivity(Qt::CaseInsensitive); + return c; + }(); + + return c.compare(a, b); +} + + +class ConflictItem : public QTreeWidgetItem +{ +public: + static const int FILENAME_USERROLE = Qt::UserRole + 1; + static const int ALT_ORIGIN_USERROLE = Qt::UserRole + 2; + static const int ARCHIVE_USERROLE = Qt::UserRole + 3; + static const int INDEX_USERROLE = Qt::UserRole + 4; + static const int HAS_ALTS_USERROLE = Qt::UserRole + 5; + + ConflictItem( + QStringList columns, FileEntry::Index index, const QString& fileName, + bool hasAltOrigins, const QString& altOrigin, bool archive) + : QTreeWidgetItem(columns) + { + setData(0, FILENAME_USERROLE, fileName); + setData(0, ALT_ORIGIN_USERROLE, altOrigin); + setData(0, ARCHIVE_USERROLE, archive); + setData(0, INDEX_USERROLE, index); + setData(0, HAS_ALTS_USERROLE, hasAltOrigins); + + if (archive) { + QFont f = font(0); + f.setItalic(true); + + for (int i=0; i); + return data(0, INDEX_USERROLE).toUInt(); + } + + bool canHide() const + { + return canHideFile(isArchive(), fileName()); + } + + bool canUnhide() const + { + return canUnhideFile(isArchive(), fileName()); + } + + bool canOpen() const + { + return canOpenFile(isArchive(), fileName()); + } + + bool canPreview(PluginContainer* pluginContainer) const + { + return canPreviewFile(pluginContainer, isArchive(), fileName()); + } + + bool operator<(const QTreeWidgetItem& other) const + { + const int column = treeWidget()->sortColumn(); + + if (column >= columnCount() || column >= other.columnCount()) { + // shouldn't happen + qWarning().nospace() << "ConflictItem::operator<() mistmatch in column count"; + return false; + } + + return (naturalCompare(text(column), other.text(column)) < 0); + } +}; + + +ConflictsTab::ConflictsTab(Ui::ModInfoDialog* ui, OrganizerCore& oc) + : ui(ui), m_general(ui, oc), m_advanced(ui, oc) +{ +} + +void ConflictsTab::saveState(Settings& s) +{ + s.directInterface().setValue( + "mod_info_conflicts_tab", ui->tabConflictsTabs1->currentIndex()); + + m_general.saveState(s); + m_advanced.saveState(s); +} + +void ConflictsTab::restoreState(const Settings& s) +{ + ui->tabConflictsTabs1->setCurrentIndex( + s.directInterface().value("mod_info_conflicts_tab", 0).toInt()); + + m_general.restoreState(s); + m_advanced.restoreState(s); +} + + +GeneralConflictsTab::GeneralConflictsTab(Ui::ModInfoDialog* ui, OrganizerCore& oc) + : ui(ui), m_core(oc), m_origin(nullptr) +{ + m_expanders.overwrite.set(ui->overwriteExpander1, ui->overwriteTree1, true); + m_expanders.overwritten.set(ui->overwrittenExpander1, ui->overwrittenTree1, true); + m_expanders.nonconflict.set(ui->noConflictExpander1, ui->noConflictTree1); + + QObject::connect( + ui->overwriteTree1, &QTreeWidget::itemDoubleClicked, + [&](auto* item, int col){ onOverwriteActivated(item, col); }); +} + +void GeneralConflictsTab::saveState(Settings& s) +{ + QByteArray result; + QDataStream stream(&result, QIODevice::WriteOnly); + + stream + << m_expanders.overwrite.opened() + << m_expanders.overwritten.opened() + << m_expanders.nonconflict.opened(); + + s.directInterface().setValue( + "mod_info_conflicts_general_expanders", result); + + s.directInterface().setValue( + "mod_info_conflicts_general_overwrite", + ui->overwriteTree1->header()->saveState()); + + s.directInterface().setValue( + "mod_info_conflicts_general_noconflict", + ui->noConflictTree1->header()->saveState()); + + s.directInterface().setValue( + "mod_info_conflicts_general_overwritten", + ui->overwrittenTree1->header()->saveState()); +} + +void GeneralConflictsTab::restoreState(const Settings& s) +{ + QDataStream stream(s.directInterface() + .value("mod_info_conflicts_general_expanders").toByteArray()); + + bool overwriteExpanded = false; + bool overwrittenExpanded = false; + bool noConflictExpanded = false; + + stream >> overwriteExpanded >> overwrittenExpanded >> noConflictExpanded; + + if (stream.status() == QDataStream::Ok) { + m_expanders.overwrite.toggle(overwriteExpanded); + m_expanders.overwritten.toggle(overwrittenExpanded); + m_expanders.nonconflict.toggle(noConflictExpanded); + } + + ui->overwriteTree1->header()->restoreState(s.directInterface() + .value("mod_info_conflicts_general_overwrite").toByteArray()); + + ui->noConflictTree1->header()->restoreState(s.directInterface() + .value("mod_info_conflicts_general_noconflict").toByteArray()); + + ui->overwrittenTree1->header()->restoreState(s.directInterface() + .value("mod_info_conflicts_general_overwritten").toByteArray()); +} + +void GeneralConflictsTab::rebuild(ModInfo::Ptr mod, FilesOrigin* origin) +{ + m_mod = mod; + m_origin = origin; + + update(); +} + +void GeneralConflictsTab::update() +{ + int numNonConflicting = 0; + int numOverwrite = 0; + int numOverwritten = 0; + + ui->overwriteTree1->clear(); + ui->overwrittenTree1->clear(); + ui->noConflictTree1->clear(); + + if (m_origin != nullptr) { + const auto rootPath = m_mod->absolutePath(); + + for (const auto& file : m_origin->getFiles()) { + const QString relativeName = QDir::fromNativeSeparators(ToQString(file->getRelativePath())); + const QString fileName = relativeName.mid(0).prepend(rootPath); + + bool archive = false; + const int fileOrigin = file->getOrigin(archive); + const auto& alternatives = file->getAlternatives(); + + if (fileOrigin == m_origin->getID()) { + if (!alternatives.empty()) { + ui->overwriteTree1->addTopLevelItem(createOverwriteItem( + file->getIndex(), archive, fileName, relativeName, alternatives)); + + ++numOverwrite; + } else { + // otherwise, put the file in the noconflict tree + ui->noConflictTree1->addTopLevelItem(createNoConflictItem( + file->getIndex(), archive, fileName, relativeName)); + + ++numNonConflicting; + } + } else { + ui->overwrittenTree1->addTopLevelItem(createOverwrittenItem( + file->getIndex(), fileOrigin, archive, fileName, relativeName)); + + ++numOverwritten; + } + } + } + + ui->overwriteCount1->display(numOverwrite); + ui->overwrittenCount1->display(numOverwritten); + ui->noConflictCount1->display(numNonConflicting); +} + +QTreeWidgetItem* GeneralConflictsTab::createOverwriteItem( + FileEntry::Index index, bool archive, + const QString& fileName, const QString& relativeName, + const FileEntry::AlternativesVector& alternatives) +{ + const auto& ds = *m_core.directoryStructure(); + + QString altString; + + for (const auto& alt : alternatives) { + if (!altString.isEmpty()) { + altString += ", "; + } + + altString += ToQString(ds.getOriginByID(alt.first).getName()); + } + + QStringList fields(relativeName); + fields.append(altString); + + const auto origin = ToQString(ds.getOriginByID(alternatives.back().first).getName()); + + return new ConflictItem(fields, index, fileName, true, origin, archive); +} + +QTreeWidgetItem* GeneralConflictsTab::createNoConflictItem( + FileEntry::Index index, bool archive, + const QString& fileName, const QString& relativeName) +{ + return new ConflictItem({relativeName}, index, fileName, false, "", archive); +} + +QTreeWidgetItem* GeneralConflictsTab::createOverwrittenItem( + FileEntry::Index index, int fileOrigin, bool archive, + const QString& fileName, const QString& relativeName) +{ + const auto& ds = *m_core.directoryStructure(); + + const FilesOrigin &realOrigin = ds.getOriginByID(fileOrigin); + + QStringList fields(relativeName); + fields.append(ToQString(realOrigin.getName())); + + return new ConflictItem( + fields, index, fileName, true, ToQString(realOrigin.getName()), archive); +} + +void GeneralConflictsTab::onOverwriteActivated(QTreeWidgetItem *item, int) +{ + if (auto* ci=dynamic_cast(item)) { + const auto origin = ci->altOrigin(); + + if (!origin.isEmpty()) { + close(); + emit modOpen(origin, TAB_CONFLICTS); + } + } +} + + +AdvancedConflictsTab::AdvancedConflictsTab(Ui::ModInfoDialog* ui, OrganizerCore& oc) + : ui(ui), m_core(oc), m_origin(nullptr) +{ + // left-elide the overwrites column so that the nearest are visible + ui->conflictsAdvancedList1->setItemDelegateForColumn( + 0, new ElideLeftDelegate(ui->conflictsAdvancedList1)); + + // left-elide the file column to see filenames + ui->conflictsAdvancedList1->setItemDelegateForColumn( + 1, new ElideLeftDelegate(ui->conflictsAdvancedList1)); + + // don't elide the overwritten by column so that the nearest are visible + + QObject::connect(ui->conflictsAdvancedShowNoConflict1, &QCheckBox::clicked, [&] { + update(); + }); + + QObject::connect(ui->conflictsAdvancedShowAll1, &QRadioButton::clicked, [&] { + update(); + }); + + QObject::connect(ui->conflictsAdvancedShowNearest1, &QRadioButton::clicked, [&] { + update(); + }); + + m_filter.set(ui->conflictsAdvancedFilter1); + m_filter.changed = [&]{ update(); }; +} + +void AdvancedConflictsTab::saveState(Settings& s) +{ + s.directInterface().setValue( + "mod_info_conflicts_advanced_list", + ui->conflictsAdvancedList1->header()->saveState()); + + QByteArray result; + QDataStream stream(&result, QIODevice::WriteOnly); + + stream + << ui->conflictsAdvancedShowNoConflict1->isChecked() + << ui->conflictsAdvancedShowAll1->isChecked() + << ui->conflictsAdvancedShowNearest1->isChecked(); + + s.directInterface().setValue( + "mod_info_conflicts_advanced_options", + ui->conflictsAdvancedList1->header()->saveState()); +} + +void AdvancedConflictsTab::restoreState(const Settings& s) +{ + ui->conflictsAdvancedList1->header()->restoreState( + s.directInterface().value("mod_info_conflicts_advanced_list").toByteArray()); + + QDataStream stream(s.directInterface() + .value("mod_info_conflicts_advanced_options").toByteArray()); + + bool noConflictChecked = false; + bool showAllChecked = false; + bool showNearestChecked = false; + + stream >> noConflictChecked >> showAllChecked >> showNearestChecked; + + if (stream.status() == QDataStream::Ok) { + ui->conflictsAdvancedShowNoConflict1->setChecked(noConflictChecked); + ui->conflictsAdvancedShowAll1->setChecked(showAllChecked); + ui->conflictsAdvancedShowNearest1->setChecked(showNearestChecked); + } +} + +void AdvancedConflictsTab::rebuild(ModInfo::Ptr mod, MOShared::FilesOrigin* origin) +{ + m_mod = mod; + m_origin = origin; + + update(); +} + +void AdvancedConflictsTab::update() +{ + ui->conflictsAdvancedList1->clear(); + + if (m_origin != nullptr) { + const auto rootPath = m_mod->absolutePath(); + + for (const auto& file : m_origin->getFiles()) { + const QString relativeName = QDir::fromNativeSeparators(ToQString(file->getRelativePath())); + const QString fileName = relativeName.mid(0).prepend(rootPath); + + bool archive = false; + const int fileOrigin = file->getOrigin(archive); + const auto& alternatives = file->getAlternatives(); + + auto* advancedItem = createItem( + file->getIndex(), fileOrigin, archive, + fileName, relativeName, alternatives); + + if (advancedItem) { + ui->conflictsAdvancedList1->addTopLevelItem(advancedItem); + } + } + } +} + +QTreeWidgetItem* AdvancedConflictsTab::createItem( + FileEntry::Index index, int fileOrigin, bool archive, + const QString& fileName, const QString& relativeName, + const MOShared::FileEntry::AlternativesVector& alternatives) +{ + const auto& ds = *m_core.directoryStructure(); + + QString before, after; + + if (!alternatives.empty()) { + int beforePrio = 0; + int afterPrio = std::numeric_limits::max(); + + for (const auto& alt : alternatives) + { + const auto altOrigin = ds.getOriginByID(alt.first); + + if (ui->conflictsAdvancedShowAll1->isChecked()) { + // fills 'before' and 'after' with all the alternatives that come + // before and after this mod in terms of priority + + if (altOrigin.getPriority() < m_origin->getPriority()) { + // add all the mods having a lower priority than this one + if (!before.isEmpty()) { + before += ", "; + } + + before += ToQString(altOrigin.getName()); + } else if (altOrigin.getPriority() > m_origin->getPriority()) { + // add all the mods having a higher priority than this one + if (!after.isEmpty()) { + after += ", "; + } + + after += ToQString(altOrigin.getName()); + } + } else { + // keep track of the nearest mods that come before and after this one + // in terms of priority + + if (altOrigin.getPriority() < m_origin->getPriority()) { + // the alternative has a lower priority than this mod + + if (altOrigin.getPriority() > beforePrio) { + // the alternative has a higher priority and therefore is closer + // to this mod, use it + before = ToQString(altOrigin.getName()); + beforePrio = altOrigin.getPriority(); + } + } + + if (altOrigin.getPriority() > m_origin->getPriority()) { + // the alternative has a higher priority than this mod + + if (altOrigin.getPriority() < afterPrio) { + // the alternative has a lower priority and there is closer + // to this mod, use it + after = ToQString(altOrigin.getName()); + afterPrio = altOrigin.getPriority(); + } + } + } + } + + // the primary origin is never in the list of alternatives, so it has to + // be handled separately + // + // if 'after' is not empty, it means at least one alternative with a higher + // priority than this mod was found; if the user only wants to see the + // nearest mods, it's not worth checking for the primary origin because it + // will always have a higher priority than the alternatives (or it wouldn't + // be the primary) + if (after.isEmpty() || ui->conflictsAdvancedShowAll1->isChecked()) { + FilesOrigin &realOrigin = ds.getOriginByID(fileOrigin); + + // if no mods overwrite this file, the primary origin is the same as this + // mod, so ignore that + if (realOrigin.getID() != m_origin->getID()) { + if (!after.isEmpty()) { + after += ", "; + } + + after += ToQString(realOrigin.getName()); + } + } + } + + bool hasAlts = !before.isEmpty() || !after.isEmpty(); + + if (!ui->conflictsAdvancedShowNoConflict1->isChecked()) { + // if both before and after are empty, it means this file has no conflicts + // at all, only display it if the user wants it + if (!hasAlts) { + return nullptr; + } + } + + bool matched = m_filter.matches([&](auto&& what) { + return + before.contains(what, Qt::CaseInsensitive) || + relativeName.contains(what, Qt::CaseInsensitive) || + after.contains(what, Qt::CaseInsensitive); + }); + + if (!matched) { + return nullptr; + } + + return new ConflictItem( + {before, relativeName, after}, index, fileName, hasAlts, "", archive); +} -- cgit v1.3.1 From b5c47eb161f6e20c9275909d32202e52bf3ba4a3 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Fri, 21 Jun 2019 21:24:44 -0400 Subject: pass parent widget to tabs for dialogs finished moving over conflict tab to its own set of files --- src/modinfodialog.cpp | 444 +++++++---------------------------------- src/modinfodialog.h | 62 ++---- src/modinfodialogconflicts.cpp | 441 +++++++++++++++++++++++++++++++++++++--- src/modinfodialogconflicts.h | 80 +++++++- src/modinfodialogesps.cpp | 30 +-- src/modinfodialogesps.h | 5 +- src/modinfodialogimages.cpp | 2 +- src/modinfodialogimages.h | 4 +- src/modinfodialogtextfiles.cpp | 17 +- src/modinfodialogtextfiles.h | 13 +- 10 files changed, 616 insertions(+), 482 deletions(-) (limited to 'src/modinfodialogconflicts.cpp') diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index c24176b3..d52103b9 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -63,20 +63,8 @@ along with Mod Organizer. If not, see . using namespace MOBase; using namespace MOShared; +const int max_scan_for_context_menu = 50; -std::vector> ModInfoDialogTab::createTabs( - Ui::ModInfoDialog* ui) -{ - std::vector> v; - - v.push_back(std::make_unique(ui)); - v.push_back(std::make_unique(ui)); - v.push_back(std::make_unique(ui)); - v.push_back(std::make_unique(ui)); - v.push_back(std::make_unique(ui)); - - return v; -} bool ModInfoDialogTab::canClose() { @@ -93,6 +81,26 @@ void ModInfoDialogTab::restoreState(const Settings& s) // no-op } +void ModInfoDialogTab::setMod(ModInfo::Ptr, MOShared::FilesOrigin*) +{ + // no-op +} + +void ModInfoDialogTab::update() +{ + // no-op +} + +void ModInfoDialogTab::emitOriginModified(int originID) +{ + emit originModified(originID); +} + +void ModInfoDialogTab::emitModOpen(QString name) +{ + emit modOpen(name); +} + class ModFileListWidget : public QListWidgetItem { friend bool operator<(const ModFileListWidget &LHS, const ModFileListWidget &RHS); @@ -109,20 +117,16 @@ static bool operator<(const ModFileListWidget &LHS, const ModFileListWidget &RHS return LHS.m_SortValue < RHS.m_SortValue; } -// if there are more than 50 selected items in the conflict tree or filetree, -// don't bother checking whether menu items apply to them, just show all of them -const int max_scan_for_context_menu = 50; - bool canPreviewFile( - PluginContainer* pluginContainer, bool isArchive, const QString& filename) + PluginContainer& pluginContainer, bool isArchive, const QString& filename) { if (isArchive) { return false; } const auto ext = QFileInfo(filename).suffix(); - return pluginContainer->previewGenerator().previewSupported(ext); + return pluginContainer.previewGenerator().previewSupported(ext); } bool canOpenFile(bool isArchive, const QString&) @@ -161,6 +165,18 @@ bool canUnhideFile(bool isArchive, const QString& filename) return true; } +FileRenamer::RenameResults hideFile(FileRenamer& renamer, const QString &oldName) +{ + const QString newName = oldName + ModInfo::s_HiddenExt; + return renamer.rename(oldName, newName); +} + +FileRenamer::RenameResults unhideFile(FileRenamer& renamer, const QString &oldName) +{ + QString newName = oldName.left(oldName.length() - ModInfo::s_HiddenExt.length()); + return renamer.rename(oldName, newName); +} + ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directory, bool unmanaged, OrganizerCore *organizerCore, PluginContainer *pluginContainer, QWidget *parent) : TutorableDialog("ModInfoDialog", parent), ui(new Ui::ModInfoDialog), m_ModInfo(modInfo), @@ -172,7 +188,20 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo { ui->setupUi(this); - m_tabs = ModInfoDialogTab::createTabs(ui); + m_tabs = createTabs(); + + for (std::size_t i=0; i(i)); + }); + } this->setWindowTitle(modInfo->name()); this->setWindowModality(Qt::WindowModal); @@ -223,12 +252,6 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo } } - // refresh everything but the conflict lists, which are done in exec() because - // they depend on restoring the state to some widgets; this refresh has to be - // done here because some of the checks below depend on the ui to decide which - // tabs to enable - refreshFiles(); - if (modInfo->hasFlag(ModInfo::FLAG_SEPARATOR)) { ui->tabWidget->setTabEnabled(TAB_TEXTFILES, false); @@ -283,8 +306,11 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo if (ui->tabWidget->currentIndex() == TAB_NEXUS) { activateNexusTab(); } -} + for (auto& tab : m_tabs) { + tab->setMod(m_ModInfo, m_Origin); + } +} ModInfoDialog::~ModInfoDialog() { @@ -301,11 +327,23 @@ ModInfoDialog::~ModInfoDialog() delete m_Settings; } +std::vector> ModInfoDialog::createTabs() +{ + std::vector> v; + + v.push_back(std::make_unique(this, ui)); + v.push_back(std::make_unique(this, ui)); + v.push_back(std::make_unique(this, ui)); + v.push_back(std::make_unique(this, ui)); + v.push_back(std::make_unique( + this, ui, *m_OrganizerCore, *m_PluginContainer)); + + return v; +} int ModInfoDialog::exec() { - // no need to refresh the other stuff, that was done in the constructor - refreshConflictLists(true, true); + refreshLists(); return TutorableDialog::exec(); } @@ -412,17 +450,15 @@ QByteArray ModInfoDialog::saveTabState() const void ModInfoDialog::refreshLists() { - refreshConflictLists(true, true); + for (auto& tab : m_tabs) { + tab->update(); + } + refreshFiles(); } void ModInfoDialog::refreshFiles() { - // clearing - for (auto& tab : m_tabs) { - tab->clear(); - } - if (m_RootPath.length() > 0) { QDirIterator dirIterator(m_RootPath, QDir::Files, QDirIterator::Subdirectories); while (dirIterator.hasNext()) { @@ -936,7 +972,7 @@ void ModInfoDialog::on_fileTree_customContextMenuRequested(const QPoint &pos) const QString fileName = m_FileSystemModel->fileName(m_FileSelection.at(0)); - if (!canPreviewFile(m_PluginContainer, false, fileName)) { + if (!canPreviewFile(*m_PluginContainer, false, fileName)) { enablePreview = false; } @@ -1054,342 +1090,6 @@ void ModInfoDialog::on_primaryCategoryBox_currentIndexChanged(int index) } } -FileRenamer::RenameResults ModInfoDialog::hideFile(FileRenamer& renamer, const QString &oldName) -{ - const QString newName = oldName + ModInfo::s_HiddenExt; - return renamer.rename(oldName, newName); -} - -FileRenamer::RenameResults ModInfoDialog::unhideFile(FileRenamer& renamer, const QString &oldName) -{ - QString newName = oldName.left(oldName.length() - ModInfo::s_HiddenExt.length()); - return renamer.rename(oldName, newName); -} - -void ModInfoDialog::changeConflictItemsVisibility( - const QList& items, bool visible) -{ - bool changed = false; - bool stop = false; - - qDebug().nospace() - << (visible ? "unhiding" : "hiding") << " " - << items.size() << " conflict files"; - - QFlags flags = - (visible ? FileRenamer::UNHIDE : FileRenamer::HIDE); - - if (items.size() > 1) { - flags |= FileRenamer::MULTIPLE; - } - - FileRenamer renamer(this, flags); - - for (const auto* item : items) { - if (stop) { - break; - } - - const auto* ci = dynamic_cast(item); - if (!ci) { - continue; - } - - auto result = FileRenamer::RESULT_CANCEL; - - if (visible) { - if (!ci->canUnhide()) { - qDebug().nospace() << "cannot unhide " << item->text(0) << ", skipping"; - continue; - } - result = unhideFile(renamer, ci->fileName()); - - } else { - if (!ci->canHide()) { - qDebug().nospace() << "cannot hide " << item->text(0) << ", skipping"; - continue; - } - result = hideFile(renamer, ci->fileName()); - } - - switch (result) { - case FileRenamer::RESULT_OK: { - // will trigger a refresh at the end - changed = true; - break; - } - - case FileRenamer::RESULT_SKIP: { - // nop - break; - } - - case FileRenamer::RESULT_CANCEL: { - // stop right now, but make sure to refresh if needed - stop = true; - break; - } - } - } - - qDebug().nospace() << (visible ? "unhiding" : "hiding") << " conflict files done"; - - if (changed) { - qDebug().nospace() << "triggering refresh"; - if (m_Origin) { - emit originModified(m_Origin->getID()); - } - refreshLists(); - } -} - -void ModInfoDialog::openConflictItems(const QList& items) -{ - // the menu item is only shown for a single selection, but handle all of them - // in case this changes - for (auto* item : items) { - if (auto* ci=dynamic_cast(item)) { - m_OrganizerCore->executeFileVirtualized(this, ci->fileName()); - } - } -} - -void ModInfoDialog::previewConflictItems(const QList& items) -{ - // the menu item is only shown for a single selection, but handle all of them - // in case this changes - for (auto* item : items) { - if (auto* ci=dynamic_cast(item)) { - m_OrganizerCore->previewFileWithAlternatives(this, ci->fileName()); - } - } -} - -void ModInfoDialog::on_overwriteTree_customContextMenuRequested(const QPoint &pos) -{ - showConflictMenu(pos, ui->overwriteTree); -} - -void ModInfoDialog::on_overwrittenTree_customContextMenuRequested(const QPoint &pos) -{ - showConflictMenu(pos, ui->overwrittenTree); -} - -void ModInfoDialog::on_noConflictTree_customContextMenuRequested(const QPoint &pos) -{ - showConflictMenu(pos, ui->noConflictTree); -} - -void ModInfoDialog::on_conflictsAdvancedList_customContextMenuRequested(const QPoint &pos) -{ - showConflictMenu(pos, ui->conflictsAdvancedList); -} - -void ModInfoDialog::showConflictMenu(const QPoint &pos, QTreeWidget* tree) -{ - auto actions = createConflictMenuActions(tree->selectedItems()); - - QMenu menu; - - // open - if (actions.open) { - connect(actions.open, &QAction::triggered, [&]{ - openConflictItems(tree->selectedItems()); - }); - - menu.addAction(actions.open); - } - - // preview - if (actions.preview) { - connect(actions.preview, &QAction::triggered, [&]{ - previewConflictItems(tree->selectedItems()); - }); - - menu.addAction(actions.preview); - } - - // hide - if (actions.hide) { - connect(actions.hide, &QAction::triggered, [&]{ - changeConflictItemsVisibility(tree->selectedItems(), false); - }); - - menu.addAction(actions.hide); - } - - // unhide - if (actions.unhide) { - connect(actions.unhide, &QAction::triggered, [&]{ - changeConflictItemsVisibility(tree->selectedItems(), true); - }); - - menu.addAction(actions.unhide); - } - - // goto - if (actions.gotoMenu) { - menu.addMenu(actions.gotoMenu); - - for (auto* a : actions.gotoActions) { - connect(a, &QAction::triggered, [&, name=a->text()]{ - close(); - emit modOpen(name, TAB_CONFLICTS); - }); - - actions.gotoMenu->addAction(a); - } - } - - if (!menu.isEmpty()) { - menu.exec(tree->viewport()->mapToGlobal(pos)); - } -} - -ModInfoDialog::ConflictActions ModInfoDialog::createConflictMenuActions( - const QList& selection) -{ - if (selection.empty()) { - return {}; - } - - bool enableHide = true; - bool enableUnhide = true; - bool enableOpen = true; - bool enablePreview = true; - bool enableGoto = true; - - if (selection.size() == 1) { - // this is a single selection - const auto* ci = dynamic_cast(selection[0]); - if (!ci) { - return {}; - } - - enableHide = ci->canHide(); - enableUnhide = ci->canUnhide(); - enableOpen = ci->canOpen(); - enablePreview = ci->canPreview(m_PluginContainer); - enableGoto = ci->hasAlts(); - } - else { - // this is a multiple selection, don't show open/preview so users don't open - // a thousand files - enableOpen = false; - enablePreview = false; - - // don't bother with this on multiple selection, at least for now - enableGoto = false; - - if (selection.size() < max_scan_for_context_menu) { - // if the number of selected items is low, checking them to accurately - // show the menu items is worth it - enableHide = false; - enableUnhide = false; - - for (const auto* item : selection) { - if (const auto* ci=dynamic_cast(item)) { - if (ci->canHide()) { - enableHide = true; - } - - if (ci->canUnhide()) { - enableUnhide = true; - } - - if (enableHide && enableUnhide && enableGoto) { - // found all, no need to check more - break; - } - } - } - } - } - - ConflictActions actions; - - actions.hide = new QAction(tr("Hide"), this); - actions.hide->setEnabled(enableHide); - - // note that it is possible for hidden files to appear if they override other - // hidden files from another mod - actions.unhide = new QAction(tr("Unhide"), this); - actions.unhide->setEnabled(enableUnhide); - - actions.open = new QAction(tr("Open/Execute"), this); - actions.open->setEnabled(enableOpen); - - actions.preview = new QAction(tr("Preview"), this); - actions.preview->setEnabled(enablePreview); - - actions.gotoMenu = new QMenu(tr("Go to..."), this); - actions.gotoMenu->setEnabled(enableGoto); - - if (enableGoto) { - actions.gotoActions = createGotoActions(selection); - } - - return actions; -} - -std::vector ModInfoDialog::createGotoActions(const QList& selection) -{ - if (!m_Origin || selection.size() != 1) { - return {}; - } - - const auto* item = dynamic_cast(selection[0]); - if (!item) { - return {}; - } - - auto file = m_Origin->findFile(item->fileIndex()); - if (!file) { - return {}; - } - - - std::vector mods; - - // add all alternatives - for (const auto& alt : file->getAlternatives()) { - const auto& o = m_Directory->getOriginByID(alt.first); - if (o.getID() != m_Origin->getID()) { - mods.push_back(ToQString(o.getName())); - } - } - - // add the real origin if different from this mod - const FilesOrigin& realOrigin = m_Directory->getOriginByID(file->getOrigin()); - if (realOrigin.getID() != m_Origin->getID()) { - mods.push_back(ToQString(realOrigin.getName())); - } - - std::sort(mods.begin(), mods.end(), [](const auto& a, const auto& b) { - return (QString::localeAwareCompare(a, b) < 0); - }); - - std::vector actions; - - for (const auto& name : mods) { - actions.push_back(new QAction(name, this)); - } - - return actions; -} - -void ModInfoDialog::on_overwrittenTree_itemDoubleClicked(QTreeWidgetItem *item, int) -{ - if (const auto* ci=dynamic_cast(item)) { - const auto origin = ci->altOrigin(); - - if (!origin.isEmpty()) { - close(); - emit modOpen(origin, TAB_CONFLICTS); - } - } -} - void ModInfoDialog::on_refreshButton_clicked() { if (m_ModInfo->getNexusID() > 0) { diff --git a/src/modinfodialog.h b/src/modinfodialog.h index 53b5d94a..2a3ee2d8 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -54,12 +54,11 @@ class CategoryFactory; class TextEditor; -class ModInfoDialogTab +class ModInfoDialogTab : public QObject { -public: - static std::vector> createTabs( - Ui::ModInfoDialog* ui); + Q_OBJECT; +public: ModInfoDialogTab() = default; ModInfoDialogTab(const ModInfoDialogTab&) = delete; ModInfoDialogTab& operator=(const ModInfoDialogTab&) = delete; @@ -72,6 +71,17 @@ public: virtual bool canClose(); virtual void saveState(Settings& s); virtual void restoreState(const Settings& s); + + virtual void setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin); + virtual void update(); + +signals: + void originModified(int originID); + void modOpen(QString name); + +protected: + void emitOriginModified(int originID); + void emitModOpen(QString name); }; @@ -89,11 +99,14 @@ protected: }; -bool canPreviewFile(PluginContainer* pluginContainer, bool isArchive, const QString& filename); +bool canPreviewFile(PluginContainer& pluginContainer, bool isArchive, const QString& filename); bool canOpenFile(bool isArchive, const QString& filename); bool canHideFile(bool isArchive, const QString& filename); bool canUnhideFile(bool isArchive, const QString& filename); +FileRenamer::RenameResults hideFile(FileRenamer& renamer, const QString &oldName); +FileRenamer::RenameResults unhideFile(FileRenamer& renamer, const QString &oldName); + /** * this is a larger dialog used to visualise information about the mod. @@ -185,8 +198,6 @@ private: bool recursiveDelete(const QModelIndex &index); void deleteFile(const QModelIndex &index); void saveCategories(QTreeWidgetItem *currentNode); - FileRenamer::RenameResults hideFile(FileRenamer& renamer, const QString &oldName); - FileRenamer::RenameResults unhideFile(FileRenamer& renamer, const QString &oldName); void addCheckedCategories(QTreeWidgetItem *tree); void refreshPrimaryCategoriesBox(); @@ -216,12 +227,6 @@ private slots: void on_tabWidget_currentChanged(int index); void on_primaryCategoryBox_currentIndexChanged(int index); void on_categoriesTree_itemChanged(QTreeWidgetItem *item, int column); - void on_overwriteTree_itemDoubleClicked(QTreeWidgetItem *item, int column); - void on_overwrittenTree_itemDoubleClicked(QTreeWidgetItem *item, int column); - void on_overwriteTree_customContextMenuRequested(const QPoint &pos); - void on_overwrittenTree_customContextMenuRequested(const QPoint &pos); - void on_noConflictTree_customContextMenuRequested(const QPoint &pos); - void on_conflictsAdvancedList_customContextMenuRequested(const QPoint &pos); void on_fileTree_customContextMenuRequested(const QPoint &pos); void on_refreshButton_clicked(); @@ -235,22 +240,6 @@ private slots: private: using FileEntry = MOShared::FileEntry; - struct ConflictActions - { - QAction* hide; - QAction* unhide; - QAction* open; - QAction* preview; - QMenu* gotoMenu; - std::vector gotoActions; - - ConflictActions() : - hide(nullptr), unhide(nullptr), open(nullptr), preview(nullptr), - gotoMenu(nullptr) - { - } - }; - Ui::ModInfoDialog *ui; ModInfo::Ptr m_ModInfo; @@ -285,27 +274,14 @@ private: std::map m_RealTabPos; + std::vector> createTabs(); - void refreshConflictLists(bool refreshGeneral, bool refreshAdvanced); void refreshFiles(); void restoreTabState(const QByteArray &state); QByteArray saveTabState() const; void changeFiletreeVisibility(bool visible); - - void openConflictItems(const QList& items); - void previewConflictItems(const QList& items); - void changeConflictItemsVisibility( - const QList& items, bool visible); - - void showConflictMenu(const QPoint &pos, QTreeWidget* tree); - - ConflictActions createConflictMenuActions( - const QList& selection); - - std::vector createGotoActions( - const QList& selection); }; #endif // MODINFODIALOG_H diff --git a/src/modinfodialogconflicts.cpp b/src/modinfodialogconflicts.cpp index 17313436..22306d0c 100644 --- a/src/modinfodialogconflicts.cpp +++ b/src/modinfodialogconflicts.cpp @@ -5,6 +5,11 @@ using namespace MOShared; using namespace MOBase; +// if there are more than 50 selected items in the conflict tree or filetree, +// don't bother checking whether menu items apply to them, just show all of them +const int max_scan_for_context_menu = 50; + + int naturalCompare(const QString& a, const QString& b) { static QCollator c = []{ @@ -89,7 +94,7 @@ public: return canOpenFile(isArchive(), fileName()); } - bool canPreview(PluginContainer* pluginContainer) const + bool canPreview(PluginContainer& pluginContainer) const { return canPreviewFile(pluginContainer, isArchive(), fileName()); } @@ -109,9 +114,45 @@ public: }; -ConflictsTab::ConflictsTab(Ui::ModInfoDialog* ui, OrganizerCore& oc) - : ui(ui), m_general(ui, oc), m_advanced(ui, oc) +ConflictsTab::ConflictsTab( + QWidget* parent, Ui::ModInfoDialog* ui, + OrganizerCore& oc, PluginContainer& plugin) : + m_parent(parent), ui(ui), m_core(oc), m_plugin(plugin), + m_origin(nullptr), m_general(this, ui, oc), m_advanced(this, ui, oc) +{ + connect( + &m_general, &GeneralConflictsTab::modOpen, + [&](const QString& name){ emitModOpen(name); }); + + connect( + &m_advanced, &AdvancedConflictsTab::modOpen, + [&](const QString& name){ emitModOpen(name); }); +} + +void ConflictsTab::setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin) +{ + m_mod = mod; + m_origin = origin; + + m_general.setMod(mod, origin); + m_advanced.setMod(mod, origin); +} + +void ConflictsTab::update() { + m_general.update(); + m_advanced.update(); +} + +void ConflictsTab::clear() +{ + m_general.clear(); + m_advanced.clear(); +} + +bool ConflictsTab::feedFile(const QString&, const QString&) +{ + return false; } void ConflictsTab::saveState(Settings& s) @@ -132,9 +173,307 @@ void ConflictsTab::restoreState(const Settings& s) m_advanced.restoreState(s); } +void ConflictsTab::changeItemsVisibility( + const QList& items, bool visible) +{ + bool changed = false; + bool stop = false; + + qDebug().nospace() + << (visible ? "unhiding" : "hiding") << " " + << items.size() << " conflict files"; + + QFlags flags = + (visible ? FileRenamer::UNHIDE : FileRenamer::HIDE); + + if (items.size() > 1) { + flags |= FileRenamer::MULTIPLE; + } + + FileRenamer renamer(m_parent, flags); + + for (const auto* item : items) { + if (stop) { + break; + } + + const auto* ci = dynamic_cast(item); + if (!ci) { + continue; + } + + auto result = FileRenamer::RESULT_CANCEL; + + if (visible) { + if (!ci->canUnhide()) { + qDebug().nospace() << "cannot unhide " << item->text(0) << ", skipping"; + continue; + } + + result = unhideFile(renamer, ci->fileName()); + + } else { + if (!ci->canHide()) { + qDebug().nospace() << "cannot hide " << item->text(0) << ", skipping"; + continue; + } + + result = hideFile(renamer, ci->fileName()); + } + + switch (result) { + case FileRenamer::RESULT_OK: { + // will trigger a refresh at the end + changed = true; + break; + } + + case FileRenamer::RESULT_SKIP: { + // nop + break; + } + + case FileRenamer::RESULT_CANCEL: { + // stop right now, but make sure to refresh if needed + stop = true; + break; + } + } + } + + qDebug().nospace() << (visible ? "unhiding" : "hiding") << " conflict files done"; + + if (changed) { + qDebug().nospace() << "triggering refresh"; + + if (m_origin) { + emit originModified(m_origin->getID()); + } + + update(); + } +} + +void ConflictsTab::openItems(const QList& items) +{ + // the menu item is only shown for a single selection, but handle all of them + // in case this changes + for (auto* item : items) { + if (auto* ci=dynamic_cast(item)) { + m_core.executeFileVirtualized(m_parent, ci->fileName()); + } + } +} + +void ConflictsTab::previewItems(const QList& items) +{ + // the menu item is only shown for a single selection, but handle all of them + // in case this changes + for (auto* item : items) { + if (auto* ci=dynamic_cast(item)) { + m_core.previewFileWithAlternatives(m_parent, ci->fileName()); + } + } +} + +void ConflictsTab::showContextMenu(const QPoint &pos, QTreeWidget* tree) +{ + auto actions = createMenuActions(tree->selectedItems()); + + QMenu menu; + + // open + if (actions.open) { + connect(actions.open, &QAction::triggered, [&]{ + openItems(tree->selectedItems()); + }); + + menu.addAction(actions.open); + } + + // preview + if (actions.preview) { + connect(actions.preview, &QAction::triggered, [&]{ + previewItems(tree->selectedItems()); + }); + + menu.addAction(actions.preview); + } + + // hide + if (actions.hide) { + connect(actions.hide, &QAction::triggered, [&]{ + changeItemsVisibility(tree->selectedItems(), false); + }); + + menu.addAction(actions.hide); + } + + // unhide + if (actions.unhide) { + connect(actions.unhide, &QAction::triggered, [&]{ + changeItemsVisibility(tree->selectedItems(), true); + }); + + menu.addAction(actions.unhide); + } + + // goto + if (actions.gotoMenu) { + menu.addMenu(actions.gotoMenu); + + for (auto* a : actions.gotoActions) { + connect(a, &QAction::triggered, [&, name=a->text()]{ + emitModOpen(name); + }); + + actions.gotoMenu->addAction(a); + } + } + + if (!menu.isEmpty()) { + menu.exec(tree->viewport()->mapToGlobal(pos)); + } +} + +ConflictsTab::Actions ConflictsTab::createMenuActions( + const QList& selection) +{ + if (selection.empty()) { + return {}; + } + + bool enableHide = true; + bool enableUnhide = true; + bool enableOpen = true; + bool enablePreview = true; + bool enableGoto = true; + + if (selection.size() == 1) { + // this is a single selection + const auto* ci = dynamic_cast(selection[0]); + if (!ci) { + return {}; + } + + enableHide = ci->canHide(); + enableUnhide = ci->canUnhide(); + enableOpen = ci->canOpen(); + enablePreview = ci->canPreview(m_plugin); + enableGoto = ci->hasAlts(); + } + else { + // this is a multiple selection, don't show open/preview so users don't open + // a thousand files + enableOpen = false; + enablePreview = false; + + // don't bother with this on multiple selection, at least for now + enableGoto = false; + + if (selection.size() < max_scan_for_context_menu) { + // if the number of selected items is low, checking them to accurately + // show the menu items is worth it + enableHide = false; + enableUnhide = false; + + for (const auto* item : selection) { + if (const auto* ci=dynamic_cast(item)) { + if (ci->canHide()) { + enableHide = true; + } + + if (ci->canUnhide()) { + enableUnhide = true; + } + + if (enableHide && enableUnhide && enableGoto) { + // found all, no need to check more + break; + } + } + } + } + } + + Actions actions; + + actions.hide = new QAction(tr("Hide"), m_parent); + actions.hide->setEnabled(enableHide); + + // note that it is possible for hidden files to appear if they override other + // hidden files from another mod + actions.unhide = new QAction(tr("Unhide"), m_parent); + actions.unhide->setEnabled(enableUnhide); + + actions.open = new QAction(tr("Open/Execute"), m_parent); + actions.open->setEnabled(enableOpen); + + actions.preview = new QAction(tr("Preview"), m_parent); + actions.preview->setEnabled(enablePreview); + + actions.gotoMenu = new QMenu(tr("Go to..."), m_parent); + actions.gotoMenu->setEnabled(enableGoto); + + if (enableGoto) { + actions.gotoActions = createGotoActions(selection); + } + + return actions; +} + +std::vector ConflictsTab::createGotoActions( + const QList& selection) +{ + if (!m_origin || selection.size() != 1) { + return {}; + } + + const auto* item = dynamic_cast(selection[0]); + if (!item) { + return {}; + } + + auto file = m_origin->findFile(item->fileIndex()); + if (!file) { + return {}; + } + + + std::vector mods; + const auto& ds = *m_core.directoryStructure(); + + // add all alternatives + for (const auto& alt : file->getAlternatives()) { + const auto& o = ds.getOriginByID(alt.first); + if (o.getID() != m_origin->getID()) { + mods.push_back(ToQString(o.getName())); + } + } + + // add the real origin if different from this mod + const FilesOrigin& realOrigin = ds.getOriginByID(file->getOrigin()); + if (realOrigin.getID() != m_origin->getID()) { + mods.push_back(ToQString(realOrigin.getName())); + } + + std::sort(mods.begin(), mods.end(), [](const auto& a, const auto& b) { + return (QString::localeAwareCompare(a, b) < 0); + }); + + std::vector actions; + + for (const auto& name : mods) { + actions.push_back(new QAction(name, m_parent)); + } + + return actions; +} -GeneralConflictsTab::GeneralConflictsTab(Ui::ModInfoDialog* ui, OrganizerCore& oc) - : ui(ui), m_core(oc), m_origin(nullptr) + +GeneralConflictsTab::GeneralConflictsTab( + ConflictsTab* tab, Ui::ModInfoDialog* ui, OrganizerCore& oc) + : m_tab(tab), ui(ui), m_core(oc), m_origin(nullptr) { m_expanders.overwrite.set(ui->overwriteExpander1, ui->overwriteTree1, true); m_expanders.overwritten.set(ui->overwrittenExpander1, ui->overwrittenTree1, true); @@ -143,6 +482,33 @@ GeneralConflictsTab::GeneralConflictsTab(Ui::ModInfoDialog* ui, OrganizerCore& o QObject::connect( ui->overwriteTree1, &QTreeWidget::itemDoubleClicked, [&](auto* item, int col){ onOverwriteActivated(item, col); }); + + QObject::connect( + ui->overwrittenTree1, &QTreeWidget::itemDoubleClicked, + [&](auto* item, int col){ onOverwrittenActivated(item, col); }); + + QObject::connect( + ui->overwriteTree1, &QTreeWidget::customContextMenuRequested, + [&](const QPoint& p){ m_tab->showContextMenu(p, ui->overwriteTree1); }); + + QObject::connect( + ui->overwrittenTree1, &QTreeWidget::customContextMenuRequested, + [&](const QPoint& p){ m_tab->showContextMenu(p, ui->overwrittenTree1); }); + + QObject::connect( + ui->noConflictTree1, &QTreeWidget::customContextMenuRequested, + [&](const QPoint& p){ m_tab->showContextMenu(p, ui->noConflictTree1); }); +} + +void GeneralConflictsTab::clear() +{ + ui->overwriteTree1->clear(); + ui->overwrittenTree1->clear(); + ui->noConflictTree1->clear(); + + ui->overwriteCount1->display(0); + ui->overwrittenCount1->display(0); + ui->noConflictCount1->display(0); } void GeneralConflictsTab::saveState(Settings& s) @@ -198,24 +564,20 @@ void GeneralConflictsTab::restoreState(const Settings& s) .value("mod_info_conflicts_general_overwritten").toByteArray()); } -void GeneralConflictsTab::rebuild(ModInfo::Ptr mod, FilesOrigin* origin) +void GeneralConflictsTab::setMod(ModInfo::Ptr mod, FilesOrigin* origin) { m_mod = mod; m_origin = origin; - - update(); } void GeneralConflictsTab::update() { + clear(); + int numNonConflicting = 0; int numOverwrite = 0; int numOverwritten = 0; - ui->overwriteTree1->clear(); - ui->overwrittenTree1->clear(); - ui->noConflictTree1->clear(); - if (m_origin != nullptr) { const auto rootPath = m_mod->absolutePath(); @@ -307,15 +669,26 @@ void GeneralConflictsTab::onOverwriteActivated(QTreeWidgetItem *item, int) const auto origin = ci->altOrigin(); if (!origin.isEmpty()) { - close(); - emit modOpen(origin, TAB_CONFLICTS); + emit modOpen(origin); } } } +void GeneralConflictsTab::onOverwrittenActivated(QTreeWidgetItem *item, int) +{ + if (const auto* ci=dynamic_cast(item)) { + const auto origin = ci->altOrigin(); + + if (!origin.isEmpty()) { + emit modOpen(origin); + } + } +} -AdvancedConflictsTab::AdvancedConflictsTab(Ui::ModInfoDialog* ui, OrganizerCore& oc) - : ui(ui), m_core(oc), m_origin(nullptr) + +AdvancedConflictsTab::AdvancedConflictsTab( + ConflictsTab* tab, Ui::ModInfoDialog* ui, OrganizerCore& oc) + : m_tab(tab), ui(ui), m_core(oc), m_origin(nullptr) { // left-elide the overwrites column so that the nearest are visible ui->conflictsAdvancedList1->setItemDelegateForColumn( @@ -327,22 +700,31 @@ AdvancedConflictsTab::AdvancedConflictsTab(Ui::ModInfoDialog* ui, OrganizerCore& // don't elide the overwritten by column so that the nearest are visible - QObject::connect(ui->conflictsAdvancedShowNoConflict1, &QCheckBox::clicked, [&] { - update(); - }); + QObject::connect( + ui->conflictsAdvancedShowNoConflict1, &QCheckBox::clicked, + [&]{ update(); }); - QObject::connect(ui->conflictsAdvancedShowAll1, &QRadioButton::clicked, [&] { - update(); - }); + QObject::connect( + ui->conflictsAdvancedShowAll1, &QRadioButton::clicked, + [&]{ update(); }); - QObject::connect(ui->conflictsAdvancedShowNearest1, &QRadioButton::clicked, [&] { - update(); - }); + QObject::connect( + ui->conflictsAdvancedShowNearest1, &QRadioButton::clicked, + [&]{ update(); }); + + QObject::connect( + ui->conflictsAdvancedList1, &QTreeWidget::customContextMenuRequested, + [&](const QPoint& p){ m_tab->showContextMenu(p, ui->conflictsAdvancedList1); }); m_filter.set(ui->conflictsAdvancedFilter1); m_filter.changed = [&]{ update(); }; } +void AdvancedConflictsTab::clear() +{ + ui->conflictsAdvancedList1->clear(); +} + void AdvancedConflictsTab::saveState(Settings& s) { s.directInterface().setValue( @@ -358,8 +740,7 @@ void AdvancedConflictsTab::saveState(Settings& s) << ui->conflictsAdvancedShowNearest1->isChecked(); s.directInterface().setValue( - "mod_info_conflicts_advanced_options", - ui->conflictsAdvancedList1->header()->saveState()); + "mod_info_conflicts_advanced_options", result); } void AdvancedConflictsTab::restoreState(const Settings& s) @@ -383,17 +764,15 @@ void AdvancedConflictsTab::restoreState(const Settings& s) } } -void AdvancedConflictsTab::rebuild(ModInfo::Ptr mod, MOShared::FilesOrigin* origin) +void AdvancedConflictsTab::setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin) { m_mod = mod; m_origin = origin; - - update(); } void AdvancedConflictsTab::update() { - ui->conflictsAdvancedList1->clear(); + clear(); if (m_origin != nullptr) { const auto rootPath = m_mod->absolutePath(); diff --git a/src/modinfodialogconflicts.h b/src/modinfodialogconflicts.h index 894f9dd4..5843884a 100644 --- a/src/modinfodialogconflicts.h +++ b/src/modinfodialogconflicts.h @@ -4,23 +4,33 @@ #include "modinfodialog.h" #include "expanderwidget.h" -class GeneralConflictsTab +class ConflictsTab; + +class GeneralConflictsTab : public QObject { + Q_OBJECT; + public: - GeneralConflictsTab(Ui::ModInfoDialog* ui, OrganizerCore& oc); + GeneralConflictsTab( + ConflictsTab* tab, Ui::ModInfoDialog* ui, OrganizerCore& oc); + void clear(); void saveState(Settings& s); void restoreState(const Settings& s); - void rebuild(ModInfo::Ptr mod, MOShared::FilesOrigin* origin); + void setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin); void update(); +signals: + void modOpen(QString name); + private: struct Expanders { ExpanderWidget overwrite, overwritten, nonconflict; }; + ConflictsTab* m_tab; Ui::ModInfoDialog* ui; OrganizerCore& m_core; ModInfo::Ptr m_mod; @@ -41,21 +51,34 @@ private: const QString& fileName, const QString& relativeName); void onOverwriteActivated(QTreeWidgetItem* item, int); + void onOverwrittenActivated(QTreeWidgetItem *item, int); + + void onOverwriteTreeContext(const QPoint &pos); + void onOverwrittenTreeContext(const QPoint &pos); + void onNoConflictTreeContext(const QPoint &pos); }; -class AdvancedConflictsTab +class AdvancedConflictsTab : public QObject { + Q_OBJECT; + public: - AdvancedConflictsTab(Ui::ModInfoDialog* ui, OrganizerCore& oc); + AdvancedConflictsTab( + ConflictsTab* tab, Ui::ModInfoDialog* ui, OrganizerCore& oc); + void clear(); void saveState(Settings& s); void restoreState(const Settings& s); - void rebuild(ModInfo::Ptr mod, MOShared::FilesOrigin* origin); + void setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin); void update(); +signals: + void modOpen(QString name); + private: + ConflictsTab* m_tab; Ui::ModInfoDialog* ui; OrganizerCore& m_core; ModInfo::Ptr m_mod; @@ -71,16 +94,59 @@ private: class ConflictsTab : public ModInfoDialogTab { + Q_OBJECT; + public: - ConflictsTab(Ui::ModInfoDialog* ui, OrganizerCore& oc); + ConflictsTab( + QWidget* parent, Ui::ModInfoDialog* ui, + OrganizerCore& oc, PluginContainer& plugin); + + void setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin) override; + void update() override; + void clear() override; + bool feedFile(const QString& rootPath, const QString& filename) override; void saveState(Settings& s) override; void restoreState(const Settings& s) override; + void openItems(const QList& items); + void previewItems(const QList& items); + + void changeItemsVisibility( + const QList& items, bool visible); + + void showContextMenu(const QPoint &pos, QTreeWidget* tree); + private: + struct Actions + { + QAction* hide; + QAction* unhide; + QAction* open; + QAction* preview; + QMenu* gotoMenu; + std::vector gotoActions; + + Actions() : + hide(nullptr), unhide(nullptr), open(nullptr), preview(nullptr), + gotoMenu(nullptr) + { + } + }; + + QWidget* m_parent; Ui::ModInfoDialog* ui; + OrganizerCore& m_core; + PluginContainer& m_plugin; + ModInfo::Ptr m_mod; + MOShared::FilesOrigin* m_origin; GeneralConflictsTab m_general; AdvancedConflictsTab m_advanced; + + Actions createMenuActions(const QList& selection); + + std::vector createGotoActions( + const QList& selection); }; #endif // MODINFODIALOGCONFLICTS_H diff --git a/src/modinfodialogesps.cpp b/src/modinfodialogesps.cpp index 32dcdb35..6c87b10d 100644 --- a/src/modinfodialogesps.cpp +++ b/src/modinfodialogesps.cpp @@ -123,20 +123,20 @@ private: }; -ESPsTab::ESPsTab(Ui::ModInfoDialog* ui) - : ui(ui) +ESPsTab::ESPsTab(QWidget* parent, Ui::ModInfoDialog* ui) + : m_parent(parent), ui(ui) { QObject::connect( - ui->activateESP1, &QToolButton::clicked, [&]{ onActivate(); }); + ui->activateESP, &QToolButton::clicked, [&]{ onActivate(); }); QObject::connect( - ui->deactivateESP1, &QToolButton::clicked, [&]{ onDeactivate(); }); + ui->deactivateESP, &QToolButton::clicked, [&]{ onDeactivate(); }); } void ESPsTab::clear() { - ui->inactiveESPList1->clear(); - ui->activeESPList1->clear(); + ui->inactiveESPList->clear(); + ui->activeESPList->clear(); } bool ESPsTab::feedFile(const QString& rootPath, const QString& fullPath) @@ -152,9 +152,9 @@ bool ESPsTab::feedFile(const QString& rootPath, const QString& fullPath) auto* item = new ESPItem({rootPath, relativePath}); if (item->esp().isActive()) { - ui->activeESPList1->addItem(item); + ui->activeESPList->addItem(item); } else { - ui->inactiveESPList1->addItem(item); + ui->inactiveESPList->addItem(item); } return true; @@ -188,7 +188,7 @@ void ESPsTab::onActivate() bool okClicked = false; newName = QInputDialog::getText( - ui->tabESPs, + m_parent, QObject::tr("File Exists"), QObject::tr("A file with that name exists, please enter a new one"), QLineEdit::Normal, file.fileName(), &okClicked); @@ -203,8 +203,8 @@ void ESPsTab::onActivate() } if (esp.activate(newName)) { - ui->inactiveESPList1->takeItem(ui->inactiveESPList1->row(item)); - ui->activeESPList1->addItem(item); + ui->inactiveESPList->takeItem(ui->inactiveESPList->row(item)); + ui->activeESPList->addItem(item); item->setESP(esp); } else { reportError(QObject::tr("Failed to move file")); @@ -246,8 +246,8 @@ void ESPsTab::onDeactivate() } if (esp.deactivate(newName)) { - ui->activeESPList1->takeItem(ui->activeESPList1->row(item)); - ui->inactiveESPList1->addItem(item); + ui->activeESPList->takeItem(ui->activeESPList->row(item)); + ui->inactiveESPList->addItem(item); item->setESP(esp); } else { reportError(QObject::tr("Failed to move file")); @@ -256,7 +256,7 @@ void ESPsTab::onDeactivate() ESPItem* ESPsTab::selectedInactive() { - auto* item = ui->inactiveESPList1->currentItem(); + auto* item = ui->inactiveESPList->currentItem(); if (!item) { return nullptr; } @@ -272,7 +272,7 @@ ESPItem* ESPsTab::selectedInactive() ESPItem* ESPsTab::selectedActive() { - auto* item = ui->activeESPList1->currentItem(); + auto* item = ui->activeESPList->currentItem(); if (!item) { return nullptr; } diff --git a/src/modinfodialogesps.h b/src/modinfodialogesps.h index a46677cb..8da70377 100644 --- a/src/modinfodialogesps.h +++ b/src/modinfodialogesps.h @@ -7,13 +7,16 @@ class ESPItem; class ESPsTab : public ModInfoDialogTab { + Q_OBJECT; + public: - ESPsTab(Ui::ModInfoDialog* ui); + ESPsTab(QWidget* parent, Ui::ModInfoDialog* ui); void clear() override; bool feedFile(const QString& rootPath, const QString& fullPath) override; private: + QWidget* m_parent; Ui::ModInfoDialog* ui; void onActivate(); diff --git a/src/modinfodialogimages.cpp b/src/modinfodialogimages.cpp index 1659bed3..752c8d0c 100644 --- a/src/modinfodialogimages.cpp +++ b/src/modinfodialogimages.cpp @@ -80,7 +80,7 @@ void ScalableImage::mousePressEvent(QMouseEvent* e) } -ImagesTab::ImagesTab(Ui::ModInfoDialog* ui) +ImagesTab::ImagesTab(QWidget*, Ui::ModInfoDialog* ui) : ui(ui), m_image(new ScalableImage) { ui->imagesImage->layout()->addWidget(m_image); diff --git a/src/modinfodialogimages.h b/src/modinfodialogimages.h index 73fb5936..a40904f7 100644 --- a/src/modinfodialogimages.h +++ b/src/modinfodialogimages.h @@ -31,8 +31,10 @@ private: class ImagesTab : public ModInfoDialogTab { + Q_OBJECT; + public: - ImagesTab(Ui::ModInfoDialog* ui); + ImagesTab(QWidget* parent, Ui::ModInfoDialog* ui); void clear() override; bool feedFile(const QString& rootPath, const QString& fullPath) override; diff --git a/src/modinfodialogtextfiles.cpp b/src/modinfodialogtextfiles.cpp index cf800aa6..0dd6f06e 100644 --- a/src/modinfodialogtextfiles.cpp +++ b/src/modinfodialogtextfiles.cpp @@ -21,8 +21,9 @@ private: }; -GenericFilesTab::GenericFilesTab(QListWidget* list, QSplitter* sp, TextEditor* e) - : m_list(list), m_editor(e) +GenericFilesTab::GenericFilesTab( + QWidget* parent, QListWidget* list, QSplitter* sp, TextEditor* e) + : m_parent(parent), m_list(list), m_editor(e) { m_editor->setupToolbar(); @@ -48,7 +49,7 @@ bool GenericFilesTab::canClose() } const int res = QMessageBox::question( - m_list, + m_parent, QObject::tr("Save changes?"), QObject::tr("Save changes to \"%1\"?").arg(m_editor->filename()), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); @@ -108,8 +109,9 @@ void GenericFilesTab::select(FileListItem* item) } -TextFilesTab::TextFilesTab(Ui::ModInfoDialog* ui) - : GenericFilesTab(ui->textFileList, ui->tabTextSplitter, ui->textFileEditor) +TextFilesTab::TextFilesTab(QWidget* parent, Ui::ModInfoDialog* ui) + : GenericFilesTab( + parent, ui->textFileList, ui->tabTextSplitter, ui->textFileEditor) { } @@ -128,8 +130,9 @@ bool TextFilesTab::wantsFile(const QString& rootPath, const QString& fullPath) c return false; } -IniFilesTab::IniFilesTab(Ui::ModInfoDialog* ui) - : GenericFilesTab(ui->iniFileList, ui->tabIniSplitter, ui->iniFileEditor) +IniFilesTab::IniFilesTab(QWidget* parent, Ui::ModInfoDialog* ui) + : GenericFilesTab( + parent, ui->iniFileList, ui->tabIniSplitter, ui->iniFileEditor) { } diff --git a/src/modinfodialogtextfiles.h b/src/modinfodialogtextfiles.h index 8725ea09..d30e20d7 100644 --- a/src/modinfodialogtextfiles.h +++ b/src/modinfodialogtextfiles.h @@ -10,17 +10,22 @@ class TextEditor; class GenericFilesTab : public ModInfoDialogTab { -public: - GenericFilesTab(QListWidget* list, QSplitter* splitter, TextEditor* editor); + Q_OBJECT; +public: void clear() override; bool canClose() override; bool feedFile(const QString& rootPath, const QString& fullPath) override; protected: + QWidget* m_parent; QListWidget* m_list; TextEditor* m_editor; + GenericFilesTab( + QWidget* parent, + QListWidget* list, QSplitter* splitter, TextEditor* editor); + virtual bool wantsFile(const QString& rootPath, const QString& fullPath) const = 0; private: @@ -32,7 +37,7 @@ private: class TextFilesTab : public GenericFilesTab { public: - TextFilesTab(Ui::ModInfoDialog* ui); + TextFilesTab(QWidget* parent, Ui::ModInfoDialog* ui); protected: bool wantsFile(const QString& rootPath, const QString& fullPath) const override; @@ -42,7 +47,7 @@ protected: class IniFilesTab : public GenericFilesTab { public: - IniFilesTab(Ui::ModInfoDialog* ui); + IniFilesTab(QWidget* parent, Ui::ModInfoDialog* ui); protected: bool wantsFile(const QString& rootPath, const QString& fullPath) const override; -- cgit v1.3.1 From 90280399aaa1abb571c32d4ff063b4f8d5efc39b Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Fri, 21 Jun 2019 21:32:37 -0400 Subject: renamed widgets to their proper name --- src/modinfodialog.ui | 50 +++++++++---------- src/modinfodialogconflicts.cpp | 108 ++++++++++++++++++++--------------------- 2 files changed, 79 insertions(+), 79 deletions(-) (limited to 'src/modinfodialogconflicts.cpp') diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui index a26d15ab..c8ffd470 100644 --- a/src/modinfodialog.ui +++ b/src/modinfodialog.ui @@ -128,7 +128,7 @@ - + @@ -386,23 +386,23 @@ Most mods do not have optional esps, so chances are good you are looking at an e - + Conflicts - + 1 - + General - + 0 @@ -423,11 +423,11 @@ Most mods do not have optional esps, so chances are good you are looking at an e 0 - + - + - + 0 @@ -444,7 +444,7 @@ text-align: left; - + QFrame::Sunken @@ -461,7 +461,7 @@ text-align: left; - + Qt::CustomContextMenu @@ -502,11 +502,11 @@ text-align: left; - + - + - + 0 @@ -523,7 +523,7 @@ text-align: left; - + QFrame::Sunken @@ -537,7 +537,7 @@ text-align: left; - + Qt::CustomContextMenu @@ -578,11 +578,11 @@ text-align: left; - + - + - + 0 @@ -599,7 +599,7 @@ text-align: left; - + QFrame::Sunken @@ -613,7 +613,7 @@ text-align: left; - + Qt::CustomContextMenu @@ -696,7 +696,7 @@ text-align: left; 0 - + Qt::CustomContextMenu @@ -751,7 +751,7 @@ text-align: left; 0 - + Whether files that have no conflicts should be visible in the list @@ -764,7 +764,7 @@ text-align: left; - + Shows all mods overwriting or being overwritten by this mod @@ -777,7 +777,7 @@ text-align: left; - + Shows only the nearest conflicting mods, in order of priority @@ -802,7 +802,7 @@ text-align: left; 0 - + Filter diff --git a/src/modinfodialogconflicts.cpp b/src/modinfodialogconflicts.cpp index 22306d0c..0107b8a1 100644 --- a/src/modinfodialogconflicts.cpp +++ b/src/modinfodialogconflicts.cpp @@ -158,7 +158,7 @@ bool ConflictsTab::feedFile(const QString&, const QString&) void ConflictsTab::saveState(Settings& s) { s.directInterface().setValue( - "mod_info_conflicts_tab", ui->tabConflictsTabs1->currentIndex()); + "mod_info_conflicts_tab", ui->tabConflictsTabs->currentIndex()); m_general.saveState(s); m_advanced.saveState(s); @@ -166,7 +166,7 @@ void ConflictsTab::saveState(Settings& s) void ConflictsTab::restoreState(const Settings& s) { - ui->tabConflictsTabs1->setCurrentIndex( + ui->tabConflictsTabs->setCurrentIndex( s.directInterface().value("mod_info_conflicts_tab", 0).toInt()); m_general.restoreState(s); @@ -475,40 +475,40 @@ GeneralConflictsTab::GeneralConflictsTab( ConflictsTab* tab, Ui::ModInfoDialog* ui, OrganizerCore& oc) : m_tab(tab), ui(ui), m_core(oc), m_origin(nullptr) { - m_expanders.overwrite.set(ui->overwriteExpander1, ui->overwriteTree1, true); - m_expanders.overwritten.set(ui->overwrittenExpander1, ui->overwrittenTree1, true); - m_expanders.nonconflict.set(ui->noConflictExpander1, ui->noConflictTree1); + m_expanders.overwrite.set(ui->overwriteExpander, ui->overwriteTree, true); + m_expanders.overwritten.set(ui->overwrittenExpander, ui->overwrittenTree, true); + m_expanders.nonconflict.set(ui->noConflictExpander, ui->noConflictTree); QObject::connect( - ui->overwriteTree1, &QTreeWidget::itemDoubleClicked, + ui->overwriteTree, &QTreeWidget::itemDoubleClicked, [&](auto* item, int col){ onOverwriteActivated(item, col); }); QObject::connect( - ui->overwrittenTree1, &QTreeWidget::itemDoubleClicked, + ui->overwrittenTree, &QTreeWidget::itemDoubleClicked, [&](auto* item, int col){ onOverwrittenActivated(item, col); }); QObject::connect( - ui->overwriteTree1, &QTreeWidget::customContextMenuRequested, - [&](const QPoint& p){ m_tab->showContextMenu(p, ui->overwriteTree1); }); + ui->overwriteTree, &QTreeWidget::customContextMenuRequested, + [&](const QPoint& p){ m_tab->showContextMenu(p, ui->overwriteTree); }); QObject::connect( - ui->overwrittenTree1, &QTreeWidget::customContextMenuRequested, - [&](const QPoint& p){ m_tab->showContextMenu(p, ui->overwrittenTree1); }); + ui->overwrittenTree, &QTreeWidget::customContextMenuRequested, + [&](const QPoint& p){ m_tab->showContextMenu(p, ui->overwrittenTree); }); QObject::connect( - ui->noConflictTree1, &QTreeWidget::customContextMenuRequested, - [&](const QPoint& p){ m_tab->showContextMenu(p, ui->noConflictTree1); }); + ui->noConflictTree, &QTreeWidget::customContextMenuRequested, + [&](const QPoint& p){ m_tab->showContextMenu(p, ui->noConflictTree); }); } void GeneralConflictsTab::clear() { - ui->overwriteTree1->clear(); - ui->overwrittenTree1->clear(); - ui->noConflictTree1->clear(); + ui->overwriteTree->clear(); + ui->overwrittenTree->clear(); + ui->noConflictTree->clear(); - ui->overwriteCount1->display(0); - ui->overwrittenCount1->display(0); - ui->noConflictCount1->display(0); + ui->overwriteCount->display(0); + ui->overwrittenCount->display(0); + ui->noConflictCount->display(0); } void GeneralConflictsTab::saveState(Settings& s) @@ -526,15 +526,15 @@ void GeneralConflictsTab::saveState(Settings& s) s.directInterface().setValue( "mod_info_conflicts_general_overwrite", - ui->overwriteTree1->header()->saveState()); + ui->overwriteTree->header()->saveState()); s.directInterface().setValue( "mod_info_conflicts_general_noconflict", - ui->noConflictTree1->header()->saveState()); + ui->noConflictTree->header()->saveState()); s.directInterface().setValue( "mod_info_conflicts_general_overwritten", - ui->overwrittenTree1->header()->saveState()); + ui->overwrittenTree->header()->saveState()); } void GeneralConflictsTab::restoreState(const Settings& s) @@ -554,13 +554,13 @@ void GeneralConflictsTab::restoreState(const Settings& s) m_expanders.nonconflict.toggle(noConflictExpanded); } - ui->overwriteTree1->header()->restoreState(s.directInterface() + ui->overwriteTree->header()->restoreState(s.directInterface() .value("mod_info_conflicts_general_overwrite").toByteArray()); - ui->noConflictTree1->header()->restoreState(s.directInterface() + ui->noConflictTree->header()->restoreState(s.directInterface() .value("mod_info_conflicts_general_noconflict").toByteArray()); - ui->overwrittenTree1->header()->restoreState(s.directInterface() + ui->overwrittenTree->header()->restoreState(s.directInterface() .value("mod_info_conflicts_general_overwritten").toByteArray()); } @@ -591,19 +591,19 @@ void GeneralConflictsTab::update() if (fileOrigin == m_origin->getID()) { if (!alternatives.empty()) { - ui->overwriteTree1->addTopLevelItem(createOverwriteItem( + ui->overwriteTree->addTopLevelItem(createOverwriteItem( file->getIndex(), archive, fileName, relativeName, alternatives)); ++numOverwrite; } else { // otherwise, put the file in the noconflict tree - ui->noConflictTree1->addTopLevelItem(createNoConflictItem( + ui->noConflictTree->addTopLevelItem(createNoConflictItem( file->getIndex(), archive, fileName, relativeName)); ++numNonConflicting; } } else { - ui->overwrittenTree1->addTopLevelItem(createOverwrittenItem( + ui->overwrittenTree->addTopLevelItem(createOverwrittenItem( file->getIndex(), fileOrigin, archive, fileName, relativeName)); ++numOverwritten; @@ -611,9 +611,9 @@ void GeneralConflictsTab::update() } } - ui->overwriteCount1->display(numOverwrite); - ui->overwrittenCount1->display(numOverwritten); - ui->noConflictCount1->display(numNonConflicting); + ui->overwriteCount->display(numOverwrite); + ui->overwrittenCount->display(numOverwritten); + ui->noConflictCount->display(numNonConflicting); } QTreeWidgetItem* GeneralConflictsTab::createOverwriteItem( @@ -691,53 +691,53 @@ AdvancedConflictsTab::AdvancedConflictsTab( : m_tab(tab), ui(ui), m_core(oc), m_origin(nullptr) { // left-elide the overwrites column so that the nearest are visible - ui->conflictsAdvancedList1->setItemDelegateForColumn( - 0, new ElideLeftDelegate(ui->conflictsAdvancedList1)); + ui->conflictsAdvancedList->setItemDelegateForColumn( + 0, new ElideLeftDelegate(ui->conflictsAdvancedList)); // left-elide the file column to see filenames - ui->conflictsAdvancedList1->setItemDelegateForColumn( - 1, new ElideLeftDelegate(ui->conflictsAdvancedList1)); + ui->conflictsAdvancedList->setItemDelegateForColumn( + 1, new ElideLeftDelegate(ui->conflictsAdvancedList)); // don't elide the overwritten by column so that the nearest are visible QObject::connect( - ui->conflictsAdvancedShowNoConflict1, &QCheckBox::clicked, + ui->conflictsAdvancedShowNoConflict, &QCheckBox::clicked, [&]{ update(); }); QObject::connect( - ui->conflictsAdvancedShowAll1, &QRadioButton::clicked, + ui->conflictsAdvancedShowAll, &QRadioButton::clicked, [&]{ update(); }); QObject::connect( - ui->conflictsAdvancedShowNearest1, &QRadioButton::clicked, + ui->conflictsAdvancedShowNearest, &QRadioButton::clicked, [&]{ update(); }); QObject::connect( - ui->conflictsAdvancedList1, &QTreeWidget::customContextMenuRequested, - [&](const QPoint& p){ m_tab->showContextMenu(p, ui->conflictsAdvancedList1); }); + ui->conflictsAdvancedList, &QTreeWidget::customContextMenuRequested, + [&](const QPoint& p){ m_tab->showContextMenu(p, ui->conflictsAdvancedList); }); - m_filter.set(ui->conflictsAdvancedFilter1); + m_filter.set(ui->conflictsAdvancedFilter); m_filter.changed = [&]{ update(); }; } void AdvancedConflictsTab::clear() { - ui->conflictsAdvancedList1->clear(); + ui->conflictsAdvancedList->clear(); } void AdvancedConflictsTab::saveState(Settings& s) { s.directInterface().setValue( "mod_info_conflicts_advanced_list", - ui->conflictsAdvancedList1->header()->saveState()); + ui->conflictsAdvancedList->header()->saveState()); QByteArray result; QDataStream stream(&result, QIODevice::WriteOnly); stream - << ui->conflictsAdvancedShowNoConflict1->isChecked() - << ui->conflictsAdvancedShowAll1->isChecked() - << ui->conflictsAdvancedShowNearest1->isChecked(); + << ui->conflictsAdvancedShowNoConflict->isChecked() + << ui->conflictsAdvancedShowAll->isChecked() + << ui->conflictsAdvancedShowNearest->isChecked(); s.directInterface().setValue( "mod_info_conflicts_advanced_options", result); @@ -745,7 +745,7 @@ void AdvancedConflictsTab::saveState(Settings& s) void AdvancedConflictsTab::restoreState(const Settings& s) { - ui->conflictsAdvancedList1->header()->restoreState( + ui->conflictsAdvancedList->header()->restoreState( s.directInterface().value("mod_info_conflicts_advanced_list").toByteArray()); QDataStream stream(s.directInterface() @@ -758,9 +758,9 @@ void AdvancedConflictsTab::restoreState(const Settings& s) stream >> noConflictChecked >> showAllChecked >> showNearestChecked; if (stream.status() == QDataStream::Ok) { - ui->conflictsAdvancedShowNoConflict1->setChecked(noConflictChecked); - ui->conflictsAdvancedShowAll1->setChecked(showAllChecked); - ui->conflictsAdvancedShowNearest1->setChecked(showNearestChecked); + ui->conflictsAdvancedShowNoConflict->setChecked(noConflictChecked); + ui->conflictsAdvancedShowAll->setChecked(showAllChecked); + ui->conflictsAdvancedShowNearest->setChecked(showNearestChecked); } } @@ -790,7 +790,7 @@ void AdvancedConflictsTab::update() fileName, relativeName, alternatives); if (advancedItem) { - ui->conflictsAdvancedList1->addTopLevelItem(advancedItem); + ui->conflictsAdvancedList->addTopLevelItem(advancedItem); } } } @@ -813,7 +813,7 @@ QTreeWidgetItem* AdvancedConflictsTab::createItem( { const auto altOrigin = ds.getOriginByID(alt.first); - if (ui->conflictsAdvancedShowAll1->isChecked()) { + if (ui->conflictsAdvancedShowAll->isChecked()) { // fills 'before' and 'after' with all the alternatives that come // before and after this mod in terms of priority @@ -868,7 +868,7 @@ QTreeWidgetItem* AdvancedConflictsTab::createItem( // nearest mods, it's not worth checking for the primary origin because it // will always have a higher priority than the alternatives (or it wouldn't // be the primary) - if (after.isEmpty() || ui->conflictsAdvancedShowAll1->isChecked()) { + if (after.isEmpty() || ui->conflictsAdvancedShowAll->isChecked()) { FilesOrigin &realOrigin = ds.getOriginByID(fileOrigin); // if no mods overwrite this file, the primary origin is the same as this @@ -885,7 +885,7 @@ QTreeWidgetItem* AdvancedConflictsTab::createItem( bool hasAlts = !before.isEmpty() || !after.isEmpty(); - if (!ui->conflictsAdvancedShowNoConflict1->isChecked()) { + if (!ui->conflictsAdvancedShowNoConflict->isChecked()) { // if both before and after are empty, it means this file has no conflicts // at all, only display it if the user wants it if (!hasAlts) { -- cgit v1.3.1 From a2e5f7d38214c9872a5c3a360065b5790bb29e2f Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Fri, 21 Jun 2019 21:43:16 -0400 Subject: split ModInfoDialogTab --- src/CMakeLists.txt | 3 +++ src/modinfodialog.cpp | 36 ------------------------------------ src/modinfodialog.h | 33 +-------------------------------- src/modinfodialogconflicts.cpp | 1 + src/modinfodialogconflicts.h | 5 ++++- src/modinfodialogesps.h | 2 +- src/modinfodialogimages.h | 2 +- src/modinfodialogtab.cpp | 36 ++++++++++++++++++++++++++++++++++++ src/modinfodialogtab.h | 42 ++++++++++++++++++++++++++++++++++++++++++ src/modinfodialogtextfiles.h | 2 +- 10 files changed, 90 insertions(+), 72 deletions(-) create mode 100644 src/modinfodialogtab.cpp create mode 100644 src/modinfodialogtab.h (limited to 'src/modinfodialogconflicts.cpp') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index eead6541..adaddb77 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -57,6 +57,7 @@ SET(organizer_SRCS modinfodialogconflicts.cpp modinfodialogesps.cpp modinfodialogimages.cpp + modinfodialogtab.cpp modinfodialogtextfiles.cpp modinfo.cpp modinfobackup.cpp @@ -161,6 +162,7 @@ SET(organizer_HDRS modinfodialogconflicts.h modinfodialogesps.h modinfodialogimages.h + modinfodialogtab.h modinfodialogtextfiles.h modinfo.h modinfobackup.h @@ -363,6 +365,7 @@ set(modinfo modinfodialogconflicts modinfodialogesps modinfodialogimages + modinfodialogtab modinfodialogtextfiles modinfoforeign modinfooverwrite diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index d52103b9..8231d366 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -66,42 +66,6 @@ using namespace MOShared; const int max_scan_for_context_menu = 50; -bool ModInfoDialogTab::canClose() -{ - return true; -} - -void ModInfoDialogTab::saveState(Settings&) -{ - // no-op -} - -void ModInfoDialogTab::restoreState(const Settings& s) -{ - // no-op -} - -void ModInfoDialogTab::setMod(ModInfo::Ptr, MOShared::FilesOrigin*) -{ - // no-op -} - -void ModInfoDialogTab::update() -{ - // no-op -} - -void ModInfoDialogTab::emitOriginModified(int originID) -{ - emit originModified(originID); -} - -void ModInfoDialogTab::emitModOpen(QString name) -{ - emit modOpen(name); -} - - class ModFileListWidget : public QListWidgetItem { friend bool operator<(const ModFileListWidget &LHS, const ModFileListWidget &RHS); public: diff --git a/src/modinfodialog.h b/src/modinfodialog.h index 2a3ee2d8..8e7fed17 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -43,7 +43,6 @@ along with Mod Organizer. If not, see . #include #include - namespace Ui { class ModInfoDialog; } @@ -52,37 +51,7 @@ class QFileSystemModel; class QTreeView; class CategoryFactory; class TextEditor; - - -class ModInfoDialogTab : public QObject -{ - Q_OBJECT; - -public: - ModInfoDialogTab() = default; - ModInfoDialogTab(const ModInfoDialogTab&) = delete; - ModInfoDialogTab& operator=(const ModInfoDialogTab&) = delete; - ModInfoDialogTab(ModInfoDialogTab&&) = default; - ModInfoDialogTab& operator=(ModInfoDialogTab&&) = default; - virtual ~ModInfoDialogTab() = default; - - virtual void clear() = 0; - virtual bool feedFile(const QString& rootPath, const QString& filename) = 0; - virtual bool canClose(); - virtual void saveState(Settings& s); - virtual void restoreState(const Settings& s); - - virtual void setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin); - virtual void update(); - -signals: - void originModified(int originID); - void modOpen(QString name); - -protected: - void emitOriginModified(int originID); - void emitModOpen(QString name); -}; +class ModInfoDialogTab; class ElideLeftDelegate : public QStyledItemDelegate diff --git a/src/modinfodialogconflicts.cpp b/src/modinfodialogconflicts.cpp index 0107b8a1..1734f4f9 100644 --- a/src/modinfodialogconflicts.cpp +++ b/src/modinfodialogconflicts.cpp @@ -1,5 +1,6 @@ #include "modinfodialogconflicts.h" #include "ui_modinfodialog.h" +#include "modinfodialog.h" #include "utility.h" using namespace MOShared; diff --git a/src/modinfodialogconflicts.h b/src/modinfodialogconflicts.h index 5843884a..4d4f279c 100644 --- a/src/modinfodialogconflicts.h +++ b/src/modinfodialogconflicts.h @@ -1,10 +1,13 @@ #ifndef MODINFODIALOGCONFLICTS_H #define MODINFODIALOGCONFLICTS_H -#include "modinfodialog.h" +#include "modinfodialogtab.h" #include "expanderwidget.h" +#include "filterwidget.h" +#include "directoryentry.h" class ConflictsTab; +class OrganizerCore; class GeneralConflictsTab : public QObject { diff --git a/src/modinfodialogesps.h b/src/modinfodialogesps.h index 8da70377..d71ad3ff 100644 --- a/src/modinfodialogesps.h +++ b/src/modinfodialogesps.h @@ -1,7 +1,7 @@ #ifndef MODINFODIALOGESPS_H #define MODINFODIALOGESPS_H -#include "modinfodialog.h" +#include "modinfodialogtab.h" class ESPItem; diff --git a/src/modinfodialogimages.h b/src/modinfodialogimages.h index a40904f7..708045c8 100644 --- a/src/modinfodialogimages.h +++ b/src/modinfodialogimages.h @@ -1,7 +1,7 @@ #ifndef MODINFODIALOGIMAGES_H #define MODINFODIALOGIMAGES_H -#include "modinfodialog.h" +#include "modinfodialogtab.h" class ScalableImage : public QWidget { diff --git a/src/modinfodialogtab.cpp b/src/modinfodialogtab.cpp new file mode 100644 index 00000000..b4734526 --- /dev/null +++ b/src/modinfodialogtab.cpp @@ -0,0 +1,36 @@ +#include "modinfodialogtab.h" + +bool ModInfoDialogTab::canClose() +{ + return true; +} + +void ModInfoDialogTab::saveState(Settings&) +{ + // no-op +} + +void ModInfoDialogTab::restoreState(const Settings& s) +{ + // no-op +} + +void ModInfoDialogTab::setMod(ModInfo::Ptr, MOShared::FilesOrigin*) +{ + // no-op +} + +void ModInfoDialogTab::update() +{ + // no-op +} + +void ModInfoDialogTab::emitOriginModified(int originID) +{ + emit originModified(originID); +} + +void ModInfoDialogTab::emitModOpen(QString name) +{ + emit modOpen(name); +} diff --git a/src/modinfodialogtab.h b/src/modinfodialogtab.h new file mode 100644 index 00000000..93a495f4 --- /dev/null +++ b/src/modinfodialogtab.h @@ -0,0 +1,42 @@ +#ifndef MODINFODIALOGTAB_H +#define MODINFODIALOGTAB_H + +#include "modinfo.h" +#include + +namespace MOShared { class FilesOrigin; } +namespace Ui { class ModInfoDialog; } + +class Settings; + +class ModInfoDialogTab : public QObject +{ + Q_OBJECT; + +public: + ModInfoDialogTab() = default; + ModInfoDialogTab(const ModInfoDialogTab&) = delete; + ModInfoDialogTab& operator=(const ModInfoDialogTab&) = delete; + ModInfoDialogTab(ModInfoDialogTab&&) = default; + ModInfoDialogTab& operator=(ModInfoDialogTab&&) = default; + virtual ~ModInfoDialogTab() = default; + + virtual void clear() = 0; + virtual bool feedFile(const QString& rootPath, const QString& filename) = 0; + virtual bool canClose(); + virtual void saveState(Settings& s); + virtual void restoreState(const Settings& s); + + virtual void setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin); + virtual void update(); + +signals: + void originModified(int originID); + void modOpen(QString name); + +protected: + void emitOriginModified(int originID); + void emitModOpen(QString name); +}; + +#endif // MODINFODIALOGTAB_H diff --git a/src/modinfodialogtextfiles.h b/src/modinfodialogtextfiles.h index d30e20d7..11691d64 100644 --- a/src/modinfodialogtextfiles.h +++ b/src/modinfodialogtextfiles.h @@ -1,7 +1,7 @@ #ifndef MODINFODIALOGTEXTFILES_H #define MODINFODIALOGTEXTFILES_H -#include "modinfodialog.h" +#include "modinfodialogtab.h" #include #include -- cgit v1.3.1 From a72573b385a941adf7d662b8df5c8e35309fdd45 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Fri, 21 Jun 2019 22:42:14 -0400 Subject: split categories tab --- src/CMakeLists.txt | 3 + src/modinfodialog.cpp | 88 ++------------------------- src/modinfodialog.h | 7 --- src/modinfodialogcategories.cpp | 129 ++++++++++++++++++++++++++++++++++++++++ src/modinfodialogcategories.h | 29 +++++++++ src/modinfodialogconflicts.cpp | 5 -- src/modinfodialogconflicts.h | 1 - src/modinfodialogesps.cpp | 1 - src/modinfodialogtab.cpp | 6 ++ src/modinfodialogtab.h | 2 +- 10 files changed, 173 insertions(+), 98 deletions(-) create mode 100644 src/modinfodialogcategories.cpp create mode 100644 src/modinfodialogcategories.h (limited to 'src/modinfodialogconflicts.cpp') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index adaddb77..6ebfaddd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -54,6 +54,7 @@ SET(organizer_SRCS modlist.cpp modidlineedit.cpp modinfodialog.cpp + modinfodialogcategories.cpp modinfodialogconflicts.cpp modinfodialogesps.cpp modinfodialogimages.cpp @@ -159,6 +160,7 @@ SET(organizer_HDRS modlist.h modidlineedit.h modinfodialog.h + modinfodialogcategories.h modinfodialogconflicts.h modinfodialogesps.h modinfodialogimages.h @@ -362,6 +364,7 @@ set(modinfo modinfo modinfobackup modinfodialog + modinfodialogcategories modinfodialogconflicts modinfodialogesps modinfodialogimages diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 8231d366..3263a511 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -42,6 +42,7 @@ along with Mod Organizer. If not, see . #include "modinfodialogimages.h" #include "modinfodialogesps.h" #include "modinfodialogconflicts.h" +#include "modinfodialogcategories.h" #include #include @@ -223,9 +224,7 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo ui->tabWidget->setTabEnabled(TAB_IMAGES, false); ui->tabWidget->setTabEnabled(TAB_ESPS, false); ui->tabWidget->setTabEnabled(TAB_CONFLICTS, false); - //ui->tabWidget->setTabEnabled(TAB_CATEGORIES, false); - addCategories(CategoryFactory::instance(), modInfo->getCategories(), ui->categoriesTree->invisibleRootItem(), 0); - refreshPrimaryCategoriesBox(); + ui->tabWidget->setTabEnabled(TAB_CATEGORIES, true); ui->tabWidget->setTabEnabled(TAB_NEXUS, false); //ui->tabWidget->setTabEnabled(TAB_NOTES, false); ui->tabWidget->setTabEnabled(TAB_FILETREE, false); @@ -249,12 +248,8 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo ui->tabWidget->setTabEnabled(TAB_CONFLICTS, true); initFiletree(modInfo); - addCategories(CategoryFactory::instance(), modInfo->getCategories(), ui->categoriesTree->invisibleRootItem(), 0); - refreshPrimaryCategoriesBox(); } - - ui->endorseBtn->setVisible(Settings::instance().endorsementIntegration()); ui->endorseBtn->setEnabled((m_ModInfo->endorsedState() == ModInfo::ENDORSED_FALSE) || (m_ModInfo->endorsedState() == ModInfo::ENDORSED_NEVER)); @@ -279,12 +274,13 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo ModInfoDialog::~ModInfoDialog() { m_ModInfo->setComments(ui->commentsEdit->text()); + //Avoid saving html stump if notes field is empty. if (ui->notesEdit->toPlainText().isEmpty()) m_ModInfo->setNotes(ui->notesEdit->toPlainText()); else m_ModInfo->setNotes(ui->notesEdit->toHtml()); - saveCategories(ui->categoriesTree->invisibleRootItem()); + delete ui->descriptionView->page(); delete ui->descriptionView; delete ui; @@ -301,6 +297,7 @@ std::vector> ModInfoDialog::createTabs() v.push_back(std::make_unique(this, ui)); v.push_back(std::make_unique( this, ui, *m_OrganizerCore, *m_PluginContainer)); + v.push_back(std::make_unique(this, ui)); return v; } @@ -437,38 +434,6 @@ void ModInfoDialog::refreshFiles() } } -void ModInfoDialog::addCategories(const CategoryFactory &factory, const std::set &enabledCategories, QTreeWidgetItem *root, int rootLevel) -{ - for (int i = 0; i < static_cast(factory.numCategories()); ++i) { - if (factory.getParentID(i) != rootLevel) { - continue; - } - int categoryID = factory.getCategoryID(i); - QTreeWidgetItem *newItem - = new QTreeWidgetItem(QStringList(factory.getCategoryName(i))); - newItem->setFlags(newItem->flags() | Qt::ItemIsUserCheckable); - newItem->setCheckState(0, enabledCategories.find(categoryID) - != enabledCategories.end() - ? Qt::Checked - : Qt::Unchecked); - newItem->setData(0, Qt::UserRole, categoryID); - if (factory.hasChildren(i)) { - addCategories(factory, enabledCategories, newItem, categoryID); - } - root->addChild(newItem); - } -} - - -void ModInfoDialog::saveCategories(QTreeWidgetItem *currentNode) -{ - for (int i = 0; i < currentNode->childCount(); ++i) { - QTreeWidgetItem *childNode = currentNode->child(i); - m_ModInfo->setCategory(childNode->data(0, Qt::UserRole).toInt(), childNode->checkState(0)); - saveCategories(childNode); - } -} - void ModInfoDialog::on_closeButton_clicked() { @@ -1011,49 +976,6 @@ void ModInfoDialog::on_fileTree_customContextMenuRequested(const QPoint &pos) } -void ModInfoDialog::on_categoriesTree_itemChanged(QTreeWidgetItem *item, int) -{ - QTreeWidgetItem *parent = item->parent(); - while ((parent != nullptr) && ((parent->flags() & Qt::ItemIsUserCheckable) != 0) && (parent->checkState(0) == Qt::Unchecked)) { - parent->setCheckState(0, Qt::Checked); - parent = parent->parent(); - } - refreshPrimaryCategoriesBox(); -} - - -void ModInfoDialog::addCheckedCategories(QTreeWidgetItem *tree) -{ - for (int i = 0; i < tree->childCount(); ++i) { - QTreeWidgetItem *child = tree->child(i); - if (child->checkState(0) == Qt::Checked) { - ui->primaryCategoryBox->addItem(child->text(0), child->data(0, Qt::UserRole)); - addCheckedCategories(child); - } - } -} - - -void ModInfoDialog::refreshPrimaryCategoriesBox() -{ - ui->primaryCategoryBox->clear(); - int primaryCategory = m_ModInfo->getPrimaryCategory(); - addCheckedCategories(ui->categoriesTree->invisibleRootItem()); - for (int i = 0; i < ui->primaryCategoryBox->count(); ++i) { - if (ui->primaryCategoryBox->itemData(i).toInt() == primaryCategory) { - ui->primaryCategoryBox->setCurrentIndex(i); - break; - } - } -} - -void ModInfoDialog::on_primaryCategoryBox_currentIndexChanged(int index) -{ - if (index != -1) { - m_ModInfo->setPrimaryCategory(ui->primaryCategoryBox->itemData(index).toInt()); - } -} - void ModInfoDialog::on_refreshButton_clicked() { if (m_ModInfo->getNexusID() > 0) { diff --git a/src/modinfodialog.h b/src/modinfodialog.h index 8e7fed17..384aafce 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -157,8 +157,6 @@ private: void refreshLists(); - void addCategories(const CategoryFactory &factory, const std::set &enabledCategories, QTreeWidgetItem *root, int rootLevel); - void updateVersionColor(); void refreshNexusData(int modID); @@ -166,9 +164,6 @@ private: QString getFileCategory(int categoryID); bool recursiveDelete(const QModelIndex &index); void deleteFile(const QModelIndex &index); - void saveCategories(QTreeWidgetItem *currentNode); - void addCheckedCategories(QTreeWidgetItem *tree); - void refreshPrimaryCategoriesBox(); int tabIndex(const QString &tabId); @@ -194,8 +189,6 @@ private slots: void on_versionEdit_editingFinished(); void on_customUrlLineEdit_editingFinished(); void on_tabWidget_currentChanged(int index); - void on_primaryCategoryBox_currentIndexChanged(int index); - void on_categoriesTree_itemChanged(QTreeWidgetItem *item, int column); void on_fileTree_customContextMenuRequested(const QPoint &pos); void on_refreshButton_clicked(); diff --git a/src/modinfodialogcategories.cpp b/src/modinfodialogcategories.cpp new file mode 100644 index 00000000..7df13aea --- /dev/null +++ b/src/modinfodialogcategories.cpp @@ -0,0 +1,129 @@ +#include "modinfodialogcategories.h" +#include "ui_modinfodialog.h" +#include "categories.h" +#include "modinfo.h" + +CategoriesTab::CategoriesTab(QWidget*, Ui::ModInfoDialog* ui) + : ui(ui) +{ + connect( + ui->categoriesTree, &QTreeWidget::itemChanged, + [&](auto* item, int col){ onCategoryChanged(item, col); }); + + connect( + ui->primaryCategoryBox, + static_cast(&QComboBox::currentIndexChanged), + [&](int index){ onPrimaryChanged(index); }); +} + +void CategoriesTab::setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin) +{ + m_mod = mod; +} + +void CategoriesTab::clear() +{ + ui->categoriesTree->clear(); + ui->primaryCategoryBox->clear(); +} + +void CategoriesTab::update() +{ + clear(); + + add( + CategoryFactory::instance(), m_mod->getCategories(), + ui->categoriesTree->invisibleRootItem(), 0); + + updatePrimary(); +} + +void CategoriesTab::add( + const CategoryFactory &factory, const std::set& enabledCategories, + QTreeWidgetItem* root, int rootLevel) +{ + for (int i=0; i(factory.numCategories()); ++i) { + if (factory.getParentID(i) != rootLevel) { + continue; + } + + int categoryID = factory.getCategoryID(i); + + QTreeWidgetItem *newItem + = new QTreeWidgetItem(QStringList(factory.getCategoryName(i))); + + newItem->setFlags(newItem->flags() | Qt::ItemIsUserCheckable); + + newItem->setCheckState(0, enabledCategories.find(categoryID) + != enabledCategories.end() + ? Qt::Checked + : Qt::Unchecked); + + newItem->setData(0, Qt::UserRole, categoryID); + + if (factory.hasChildren(i)) { + add(factory, enabledCategories, newItem, categoryID); + } + + root->addChild(newItem); + } +} + +void CategoriesTab::updatePrimary() +{ + ui->primaryCategoryBox->clear(); + + int primaryCategory = m_mod->getPrimaryCategory(); + + addChecked(ui->categoriesTree->invisibleRootItem()); + + for (int i = 0; i < ui->primaryCategoryBox->count(); ++i) { + if (ui->primaryCategoryBox->itemData(i).toInt() == primaryCategory) { + ui->primaryCategoryBox->setCurrentIndex(i); + break; + } + } +} + +void CategoriesTab::addChecked(QTreeWidgetItem* tree) +{ + for (int i = 0; i < tree->childCount(); ++i) { + QTreeWidgetItem *child = tree->child(i); + if (child->checkState(0) == Qt::Checked) { + ui->primaryCategoryBox->addItem(child->text(0), child->data(0, Qt::UserRole)); + addChecked(child); + } + } +} + +void CategoriesTab::save(QTreeWidgetItem* currentNode) +{ + for (int i = 0; i < currentNode->childCount(); ++i) { + QTreeWidgetItem *childNode = currentNode->child(i); + + m_mod->setCategory( + childNode->data(0, Qt::UserRole).toInt(), childNode->checkState(0)); + + save(childNode); + } +} + +void CategoriesTab::onCategoryChanged(QTreeWidgetItem* item, int) +{ + QTreeWidgetItem *parent = item->parent(); + + while ((parent != nullptr) && ((parent->flags() & Qt::ItemIsUserCheckable) != 0) && (parent->checkState(0) == Qt::Unchecked)) { + parent->setCheckState(0, Qt::Checked); + parent = parent->parent(); + } + + updatePrimary(); + save(ui->categoriesTree->invisibleRootItem()); +} + +void CategoriesTab::onPrimaryChanged(int index) +{ + if (index != -1) { + m_mod->setPrimaryCategory(ui->primaryCategoryBox->itemData(index).toInt()); + } +} diff --git a/src/modinfodialogcategories.h b/src/modinfodialogcategories.h new file mode 100644 index 00000000..c0644c7f --- /dev/null +++ b/src/modinfodialogcategories.h @@ -0,0 +1,29 @@ +#include "modinfodialogtab.h" + +class CategoryFactory; + +class CategoriesTab : public ModInfoDialogTab +{ +public: + CategoriesTab(QWidget* parent, Ui::ModInfoDialog* ui); + + void clear() override; + void setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin) override; + void update() override; + +private: + Ui::ModInfoDialog* ui; + ModInfo::Ptr m_mod; + + void add( + const CategoryFactory& factory, const std::set& enabledCategories, + QTreeWidgetItem* root, int rootLevel); + + void updatePrimary(); + void addChecked(QTreeWidgetItem* tree); + + void save(QTreeWidgetItem* currentNode); + + void onCategoryChanged(QTreeWidgetItem* item, int col); + void onPrimaryChanged(int index); +}; diff --git a/src/modinfodialogconflicts.cpp b/src/modinfodialogconflicts.cpp index 1734f4f9..d2dca492 100644 --- a/src/modinfodialogconflicts.cpp +++ b/src/modinfodialogconflicts.cpp @@ -151,11 +151,6 @@ void ConflictsTab::clear() m_advanced.clear(); } -bool ConflictsTab::feedFile(const QString&, const QString&) -{ - return false; -} - void ConflictsTab::saveState(Settings& s) { s.directInterface().setValue( diff --git a/src/modinfodialogconflicts.h b/src/modinfodialogconflicts.h index 4d4f279c..212a196d 100644 --- a/src/modinfodialogconflicts.h +++ b/src/modinfodialogconflicts.h @@ -108,7 +108,6 @@ public: void update() override; void clear() override; - bool feedFile(const QString& rootPath, const QString& filename) override; void saveState(Settings& s) override; void restoreState(const Settings& s) override; diff --git a/src/modinfodialogesps.cpp b/src/modinfodialogesps.cpp index 6c87b10d..cb3dcc84 100644 --- a/src/modinfodialogesps.cpp +++ b/src/modinfodialogesps.cpp @@ -4,7 +4,6 @@ using MOBase::reportError; - class ESP { public: diff --git a/src/modinfodialogtab.cpp b/src/modinfodialogtab.cpp index b4734526..6dcff0ac 100644 --- a/src/modinfodialogtab.cpp +++ b/src/modinfodialogtab.cpp @@ -1,5 +1,11 @@ #include "modinfodialogtab.h" +bool ModInfoDialogTab::feedFile(const QString&, const QString&) +{ + // no-op + return false; +} + bool ModInfoDialogTab::canClose() { return true; diff --git a/src/modinfodialogtab.h b/src/modinfodialogtab.h index 93a495f4..ead29d10 100644 --- a/src/modinfodialogtab.h +++ b/src/modinfodialogtab.h @@ -22,7 +22,7 @@ public: virtual ~ModInfoDialogTab() = default; virtual void clear() = 0; - virtual bool feedFile(const QString& rootPath, const QString& filename) = 0; + virtual bool feedFile(const QString& rootPath, const QString& filename); virtual bool canClose(); virtual void saveState(Settings& s); virtual void restoreState(const Settings& s); -- cgit v1.3.1 From 8d1c121f648f2f6a8e0a5e2ad76cd245e318290d Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sun, 23 Jun 2019 02:24:34 -0400 Subject: split nexus tab added OrganizerCore::loggedInAction() to execute a function only when logged in, replaces a bunch of copy/pasted stuff in mainwindow moved common variables in ModInfoDialogTab moved DescriptionPage to modinfodialognexus.h, renamed to NexusTabWebpage, deleted now empty descriptionpage.h --- src/CMakeLists.txt | 5 +- src/descriptionpage.h | 28 ----- src/mainwindow.cpp | 167 ++++++------------------ src/modinfodialog.cpp | 233 +++------------------------------- src/modinfodialog.h | 29 ----- src/modinfodialog.ui | 222 +++++++++++++++----------------- src/modinfodialogcategories.cpp | 43 +++---- src/modinfodialogcategories.h | 8 +- src/modinfodialogconflicts.cpp | 91 ++++++-------- src/modinfodialogconflicts.h | 17 +-- src/modinfodialogesps.cpp | 8 +- src/modinfodialogesps.h | 7 +- src/modinfodialogimages.cpp | 6 +- src/modinfodialogimages.h | 5 +- src/modinfodialognexus.cpp | 273 ++++++++++++++++++++++++++++++++++++++++ src/modinfodialognexus.h | 67 ++++++++++ src/modinfodialogtab.cpp | 39 +++++- src/modinfodialogtab.h | 25 +++- src/modinfodialogtextfiles.cpp | 26 ++-- src/modinfodialogtextfiles.h | 12 +- src/modinforegular.cpp | 20 ++- src/modinforegular.h | 1 + src/organizercore.cpp | 15 +++ src/organizercore.h | 1 + 24 files changed, 690 insertions(+), 658 deletions(-) delete mode 100644 src/descriptionpage.h create mode 100644 src/modinfodialognexus.cpp create mode 100644 src/modinfodialognexus.h (limited to 'src/modinfodialogconflicts.cpp') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6ebfaddd..7dc39fd9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -58,6 +58,7 @@ SET(organizer_SRCS modinfodialogconflicts.cpp modinfodialogesps.cpp modinfodialogimages.cpp + modinfodialognexus.cpp modinfodialogtab.cpp modinfodialogtextfiles.cpp modinfo.cpp @@ -164,6 +165,7 @@ SET(organizer_HDRS modinfodialogconflicts.h modinfodialogesps.h modinfodialogimages.h + modinfodialognexus.h modinfodialogtab.h modinfodialogtextfiles.h modinfo.h @@ -223,7 +225,6 @@ SET(organizer_HDRS instancemanager.h usvfsconnector.h eventfilter.h - descriptionpage.h moshortcut.h listdialog.h lcdnumber.h @@ -368,6 +369,7 @@ set(modinfo modinfodialogconflicts modinfodialogesps modinfodialogimages + modinfodialognexus modinfodialogtab modinfodialogtextfiles modinfoforeign @@ -424,7 +426,6 @@ set(utilities ) set(widgets - descriptionpage expanderwidget genericicondelegate filerenamer diff --git a/src/descriptionpage.h b/src/descriptionpage.h deleted file mode 100644 index f6158ee0..00000000 --- a/src/descriptionpage.h +++ /dev/null @@ -1,28 +0,0 @@ -#include - -#ifndef DESCRIPTIONPAGE_H -#define DESCRIPTIONPAGE_H - -class DescriptionPage : public QWebEnginePage -{ - Q_OBJECT - -public: - DescriptionPage(QObject* parent = 0) : QWebEnginePage(parent){} - - bool acceptNavigationRequest(const QUrl & url, QWebEnginePage::NavigationType type, bool isMainFrame) - { - if (type == QWebEnginePage::NavigationTypeLinkClicked) - { - emit linkClicked(url); - return false; - } - return true; - } - -signals: - void linkClicked(const QUrl&); - -}; - -#endif //DESCRIPTIONPAGE_H diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index da7c721d..8eb0838e 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3073,66 +3073,34 @@ void MainWindow::backupMod_clicked() void MainWindow::resumeDownload(int downloadIndex) { - if (NexusInterface::instance(&m_PluginContainer)->getAccessManager()->validated()) { + m_OrganizerCore.loggedInAction(this, [this, downloadIndex] { m_OrganizerCore.downloadManager()->resumeDownload(downloadIndex); - } else { - QString apiKey; - if (m_OrganizerCore.settings().getNexusApiKey(apiKey)) { - m_OrganizerCore.doAfterLogin([this, downloadIndex] () { - this->resumeDownload(downloadIndex); - }); - NexusInterface::instance(&m_PluginContainer)->getAccessManager()->apiCheck(apiKey); - } else { - MessageDialog::showMessage(tr("You need to be logged in with Nexus to resume a download"), this); - } - } + }); } void MainWindow::endorseMod(ModInfo::Ptr mod) { - if (NexusInterface::instance(&m_PluginContainer)->getAccessManager()->validated()) { + m_OrganizerCore.loggedInAction(this, [this, mod] { mod->endorse(true); - } else { - QString apiKey; - if (m_OrganizerCore.settings().getNexusApiKey(apiKey)) { - m_OrganizerCore.doAfterLogin([&]() { this->endorseMod(mod); }); - NexusInterface::instance(&m_PluginContainer)->getAccessManager()->apiCheck(apiKey); - } else { - MessageDialog::showMessage(tr("You need to be logged in with Nexus to endorse"), this); - } - } + }); } void MainWindow::endorse_clicked() { QItemSelectionModel *selection = ui->modList->selectionModel(); - if (selection->hasSelection() && selection->selectedRows().count() > 1) { - if (NexusInterface::instance(&m_PluginContainer)->getAccessManager()->validated()) { + + m_OrganizerCore.loggedInAction(this, [this] { + QItemSelectionModel *selection = ui->modList->selectionModel(); + if (selection->hasSelection() && selection->selectedRows().count() > 1) { MessageDialog::showMessage(tr("Endorsing multiple mods will take a while. Please wait..."), this); - for (QModelIndex idx : selection->selectedRows()) { - ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt())->endorse(true); - } } - else { - QString apiKey; - if (m_OrganizerCore.settings().getNexusApiKey(apiKey)) { - MessageDialog::showMessage(tr("Endorsing multiple mods will take a while. Please wait..."), this); - for (QModelIndex idx : selection->selectedRows()) { - ModInfo::Ptr modInfo = ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt()); - m_OrganizerCore.doAfterLogin([&]() { this->endorseMod(modInfo); }); - } - NexusInterface::instance(&m_PluginContainer)->getAccessManager()->apiCheck(apiKey); - } else { - MessageDialog::showMessage(tr("You need to be logged in with Nexus to endorse"), this); - return; - } + + for (QModelIndex idx : selection->selectedRows()) { + ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt())->endorse(true); } - } - else { - endorseMod(ModInfo::getByIndex(m_ContextRow)); - } + }); } void MainWindow::dontendorse_clicked() @@ -3151,114 +3119,53 @@ void MainWindow::dontendorse_clicked() void MainWindow::unendorseMod(ModInfo::Ptr mod) { - if (NexusInterface::instance(&m_PluginContainer)->getAccessManager()->validated()) { - ModInfo::getByIndex(m_ContextRow)->endorse(false); - } else { - QString apiKey; - if (m_OrganizerCore.settings().getNexusApiKey(apiKey)) { - m_OrganizerCore.doAfterLogin([&]() { this->unendorseMod(mod); }); - NexusInterface::instance(&m_PluginContainer)->getAccessManager()->apiCheck(apiKey); - } else { - MessageDialog::showMessage(tr("You need to be logged in with Nexus to endorse"), this); - } - } + m_OrganizerCore.loggedInAction(this, [mod] { + mod->endorse(false); + }); } void MainWindow::unendorse_clicked() { - QItemSelectionModel *selection = ui->modList->selectionModel(); - if (selection->hasSelection() && selection->selectedRows().count() > 1) { - if (NexusInterface::instance(&m_PluginContainer)->getAccessManager()->validated()) { + m_OrganizerCore.loggedInAction(this, [this] { + QItemSelectionModel *selection = ui->modList->selectionModel(); + if (selection->hasSelection() && selection->selectedRows().count() > 1) { MessageDialog::showMessage(tr("Unendorsing multiple mods will take a while. Please wait..."), this); - for (QModelIndex idx : selection->selectedRows()) { - ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt())->endorse(false); - } - } else { - QString apiKey; - if (m_OrganizerCore.settings().getNexusApiKey(apiKey)) { - MessageDialog::showMessage(tr("Unendorsing multiple mods will take a while. Please wait..."), this); - for (QModelIndex idx : selection->selectedRows()) { - ModInfo::Ptr modInfo = ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt()); - m_OrganizerCore.doAfterLogin([&]() { this->unendorseMod(modInfo); }); - } - NexusInterface::instance(&m_PluginContainer)->getAccessManager()->apiCheck(apiKey); - } else { - MessageDialog::showMessage(tr("You need to be logged in with Nexus to endorse"), this); - return; - } } - } - else { - unendorseMod(ModInfo::getByIndex(m_ContextRow)); - } + + for (QModelIndex idx : selection->selectedRows()) { + ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt())->endorse(false); + } + }); } void MainWindow::trackMod(ModInfo::Ptr mod, bool doTrack) { - if (NexusInterface::instance(&m_PluginContainer)->getAccessManager()->validated()) { - ModInfo::getByIndex(m_ContextRow)->track(doTrack); - } else { - QString apiKey; - if (m_OrganizerCore.settings().getNexusApiKey(apiKey)) { - m_OrganizerCore.doAfterLogin([&]() { this->trackMod(mod, doTrack); }); - NexusInterface::instance(&m_PluginContainer)->getAccessManager()->apiCheck(apiKey); - } else { - MessageDialog::showMessage(tr("You need to be logged in with Nexus to track"), this); - } - } + m_OrganizerCore.loggedInAction(this, [mod, doTrack] { + mod->track(doTrack); + }); } void MainWindow::track_clicked() { - QItemSelectionModel *selection = ui->modList->selectionModel(); - if (selection->hasSelection() && selection->selectedRows().count() > 1) { - if (NexusInterface::instance(&m_PluginContainer)->getAccessManager()->validated()) { - for (auto idx : selection->selectedRows()) { - ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt())->track(true); - } - } else { - QString apiKey; - if (m_OrganizerCore.settings().getNexusApiKey(apiKey)) { - for (auto idx : selection->selectedRows()) { - auto modInfo = ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt()); - m_OrganizerCore.doAfterLogin([&]() { this->trackMod(modInfo, true); }); - } - NexusInterface::instance(&m_PluginContainer)->getAccessManager()->apiCheck(apiKey); - } else { - MessageDialog::showMessage(tr("You need to be logged in with Nexus to track"), this); - } + m_OrganizerCore.loggedInAction(this, [this] { + QItemSelectionModel *selection = ui->modList->selectionModel(); + for (auto idx : selection->selectedRows()) { + ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt())->track(true); } - } else { - trackMod(ModInfo::getByIndex(m_ContextRow), true); - } + }); } void MainWindow::untrack_clicked() { - QItemSelectionModel *selection = ui->modList->selectionModel(); - if (selection->hasSelection() && selection->selectedRows().count() > 1) { - if (NexusInterface::instance(&m_PluginContainer)->getAccessManager()->validated()) { - for (auto idx : selection->selectedRows()) { - ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt())->track(false); - } - } else { - QString apiKey; - if (m_OrganizerCore.settings().getNexusApiKey(apiKey)) { - for (auto idx : selection->selectedRows()) { - auto modInfo = ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt()); - m_OrganizerCore.doAfterLogin([&]() { this->trackMod(modInfo, false); }); - } - NexusInterface::instance(&m_PluginContainer)->getAccessManager()->apiCheck(apiKey); - } else { - MessageDialog::showMessage(tr("You need to be logged in with Nexus to track"), this); - } + m_OrganizerCore.loggedInAction(this, [this] { + QItemSelectionModel *selection = ui->modList->selectionModel(); + for (auto idx : selection->selectedRows()) { + ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt())->track(false); } - } else { - trackMod(ModInfo::getByIndex(m_ContextRow), false); - } + }); } void MainWindow::validationFailed(const QString &error) @@ -3316,13 +3223,11 @@ void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index, } else { modInfo->saveMeta(); ModInfoDialog dialog(modInfo, m_OrganizerCore.directoryStructure(), modInfo->hasFlag(ModInfo::FLAG_FOREIGN), &m_OrganizerCore, &m_PluginContainer, this); - connect(&dialog, SIGNAL(linkActivated(QString)), this, SLOT(linkClicked(QString))); connect(&dialog, SIGNAL(downloadRequest(QString)), &m_OrganizerCore, SLOT(downloadRequestedNXM(QString))); connect(&dialog, SIGNAL(modOpen(QString, int)), this, SLOT(displayModInformation(QString, int)), Qt::QueuedConnection); connect(&dialog, SIGNAL(modOpenNext(int)), this, SLOT(modOpenNext(int)), Qt::QueuedConnection); connect(&dialog, SIGNAL(modOpenPrev(int)), this, SLOT(modOpenPrev(int)), Qt::QueuedConnection); connect(&dialog, SIGNAL(originModified(int)), this, SLOT(originModified(int))); - connect(&dialog, SIGNAL(endorseMod(ModInfo::Ptr)), this, SLOT(endorseMod(ModInfo::Ptr))); //Open the tab first if we want to use the standard indexes of the tabs. if (tab != -1) { diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 3263a511..30110d14 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -43,6 +43,7 @@ along with Mod Organizer. If not, see . #include "modinfodialogesps.h" #include "modinfodialogconflicts.h" #include "modinfodialogcategories.h" +#include "modinfodialognexus.h" #include #include @@ -145,7 +146,6 @@ FileRenamer::RenameResults unhideFile(FileRenamer& renamer, const QString &oldNa ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directory, bool unmanaged, OrganizerCore *organizerCore, PluginContainer *pluginContainer, QWidget *parent) : TutorableDialog("ModInfoDialog", parent), ui(new Ui::ModInfoDialog), m_ModInfo(modInfo), - m_RequestStarted(false), m_NewFolderAction(nullptr), m_OpenAction(nullptr), m_PreviewAction(nullptr), m_RenameAction(nullptr), m_DeleteAction(nullptr), m_HideAction(nullptr), m_UnhideAction(nullptr), m_Directory(directory), m_Origin(nullptr), @@ -173,38 +173,9 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo m_RootPath = modInfo->absolutePath(); - QString metaFileName = m_RootPath.mid(0).append("/meta.ini"); - m_Settings = new QSettings(metaFileName, QSettings::IniFormat); - - QLineEdit *modIDEdit = findChild("modIDEdit"); - ui->modIDEdit->setValidator(new QIntValidator(modIDEdit)); - ui->modIDEdit->setText(QString("%1").arg(modInfo->getNexusID())); - - connect(ui->modIDEdit, SIGNAL(linkClicked(QString)), this, SLOT(linkClicked(QString))); - - QString gameName = modInfo->getGameName(); - ui->sourceGameEdit->addItem(organizerCore->managedGame()->gameName(), organizerCore->managedGame()->gameShortName()); - if (organizerCore->managedGame()->validShortNames().size() == 0) { - ui->sourceGameEdit->setDisabled(true); - } else { - for (auto game : pluginContainer->plugins()) { - for (QString gameName : organizerCore->managedGame()->validShortNames()) { - if (game->gameShortName().compare(gameName, Qt::CaseInsensitive) == 0) { - ui->sourceGameEdit->addItem(game->gameName(), game->gameShortName()); - break; - } - } - } - } - ui->sourceGameEdit->setCurrentIndex(ui->sourceGameEdit->findData(gameName)); - ui->commentsEdit->setText(modInfo->comments()); ui->notesEdit->setText(modInfo->notes()); - ui->descriptionView->setPage(new DescriptionPage()); - - connect(m_ModInfo.data(), SIGNAL(modDetailsUpdated(bool)), this, SLOT(modDetailsUpdated(bool))); - connect(ui->descriptionView->page(), SIGNAL(linkClicked(QUrl)), this, SLOT(linkClicked(QUrl))); //TODO: No easy way to delegate links //ui->descriptionView->page()->acceptNavigationRequest(QWebEnginePage::DelegateAllLinks); @@ -246,14 +217,12 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo ui->tabWidget->setTabEnabled(TAB_IMAGES, true); ui->tabWidget->setTabEnabled(TAB_ESPS, true); ui->tabWidget->setTabEnabled(TAB_CONFLICTS, true); + ui->tabWidget->setTabEnabled(TAB_CATEGORIES, true); + ui->tabWidget->setTabEnabled(TAB_NEXUS, true); initFiletree(modInfo); } - ui->endorseBtn->setVisible(Settings::instance().endorsementIntegration()); - ui->endorseBtn->setEnabled((m_ModInfo->endorsedState() == ModInfo::ENDORSED_FALSE) || - (m_ModInfo->endorsedState() == ModInfo::ENDORSED_NEVER)); - // activate first enabled tab for (int i = 0; i < ui->tabWidget->count(); ++i) { if (ui->tabWidget->isTabEnabled(i)) { @@ -262,10 +231,6 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo } } - if (ui->tabWidget->currentIndex() == TAB_NEXUS) { - activateNexusTab(); - } - for (auto& tab : m_tabs) { tab->setMod(m_ModInfo, m_Origin); } @@ -281,27 +246,28 @@ ModInfoDialog::~ModInfoDialog() else m_ModInfo->setNotes(ui->notesEdit->toHtml()); - delete ui->descriptionView->page(); - delete ui->descriptionView; delete ui; - delete m_Settings; } -std::vector> ModInfoDialog::createTabs() +template +std::vector> createTabsImpl( + OrganizerCore& oc, PluginContainer& plugin, + ModInfoDialog* self, Ui::ModInfoDialog* ui) { std::vector> v; - - v.push_back(std::make_unique(this, ui)); - v.push_back(std::make_unique(this, ui)); - v.push_back(std::make_unique(this, ui)); - v.push_back(std::make_unique(this, ui)); - v.push_back(std::make_unique( - this, ui, *m_OrganizerCore, *m_PluginContainer)); - v.push_back(std::make_unique(this, ui)); + (v.push_back(std::make_unique(oc, plugin, self, ui)), ...); return v; } +std::vector> ModInfoDialog::createTabs() +{ + return createTabsImpl< + TextFilesTab, IniFilesTab, ImagesTab, ESPsTab, + ConflictsTab, CategoriesTab, NexusTab>( + *m_OrganizerCore, *m_PluginContainer, this, ui); +} + int ModInfoDialog::exec() { refreshLists(); @@ -434,7 +400,6 @@ void ModInfoDialog::refreshFiles() } } - void ModInfoDialog::on_closeButton_clicked() { for (auto& tab : m_tabs) { @@ -446,19 +411,6 @@ void ModInfoDialog::on_closeButton_clicked() close(); } - - -QString ModInfoDialog::getModVersion() const -{ - return m_Settings->value("version", "").toString(); -} - - -const int ModInfoDialog::getModID() const -{ - return m_Settings->value("modid", 0).toInt(); -} - void ModInfoDialog::openTab(int tab) { QTabWidget *tabWidget = findChild("tabWidget"); @@ -467,40 +419,6 @@ void ModInfoDialog::openTab(int tab) } } -void ModInfoDialog::on_visitNexusLabel_linkActivated(const QString &link) -{ - emit linkActivated(link); -} - -void ModInfoDialog::linkClicked(const QUrl &url) -{ - //Ideally we'd ask the mod for the game and the web service then pass the game - //and URL to the web service - if (NexusInterface::instance(m_PluginContainer)->isURLGameRelated(url)) { - emit linkActivated(url.toString()); - } else { - shell::OpenLink(url); - } -} - -void ModInfoDialog::linkClicked(QString url) -{ - emit linkActivated(url); -} - - -void ModInfoDialog::refreshNexusData(int modID) -{ - if ((!m_RequestStarted) && (modID > 0)) { - m_RequestStarted = true; - - m_ModInfo->updateNXMInfo(); - - MessageDialog::showMessage(tr("Info requested, please wait"), this); - } -} - - QString ModInfoDialog::getFileCategory(int categoryID) { switch (categoryID) { @@ -514,111 +432,8 @@ QString ModInfoDialog::getFileCategory(int categoryID) } } - -void ModInfoDialog::updateVersionColor() -{ -// QPalette versionColor; - if (m_ModInfo->getVersion() != m_ModInfo->getNewestVersion()) { - ui->versionEdit->setStyleSheet("color: red"); -// versionColor.setColor(QPalette::Text, Qt::red); - ui->versionEdit->setToolTip(tr("Current Version: %1").arg(m_ModInfo->getNewestVersion().canonicalString())); - } else { - ui->versionEdit->setStyleSheet("color: green"); -// versionColor.setColor(QPalette::Text, Qt::green); - ui->versionEdit->setToolTip(tr("No update available")); - } -// ui->versionEdit->setPalette(versionColor); -} - - -void ModInfoDialog::modDetailsUpdated(bool success) -{ - QString nexusDescription = m_ModInfo->getNexusDescription(); - QString descriptionAsHTML = "" - "" - "%1" - ""; - - if (!nexusDescription.isEmpty()) { - descriptionAsHTML = descriptionAsHTML.arg(BBCode::convertToHTML(nexusDescription)); - } else { - descriptionAsHTML = descriptionAsHTML.arg(tr("

Uh oh!

Sorry, there is no description available for this mod. :(

")); - } - - ui->descriptionView->page()->setHtml(descriptionAsHTML); - - updateVersionColor(); -} - - -void ModInfoDialog::activateNexusTab() -{ - QLineEdit *modIDEdit = findChild("modIDEdit"); - int modID = modIDEdit->text().toInt(); - if (modID > 0) { - QString nexusLink = NexusInterface::instance(m_PluginContainer)->getModURL(modID, m_ModInfo->getGameName()); - QLabel *visitNexusLabel = findChild("visitNexusLabel"); - visitNexusLabel->setText(tr("Visit on Nexus").arg(nexusLink)); - visitNexusLabel->setToolTip(nexusLink); - m_ModInfo->setURL(nexusLink); - - if (m_ModInfo->getNexusDescription().isEmpty() || QDateTime::currentDateTimeUtc() >= m_ModInfo->getLastNexusQuery().addDays(1)) { - refreshNexusData(modID); - } else { - modDetailsUpdated(true); - } - } else - modDetailsUpdated(true); - QLineEdit *versionEdit = findChild("versionEdit"); - QString currentVersion = m_Settings->value("version", "???").toString(); - versionEdit->setText(currentVersion); - ui->customUrlLineEdit->setText(m_ModInfo->getURL()); -} - - void ModInfoDialog::on_tabWidget_currentChanged(int index) { - if (index == TAB_NEXUS || m_RealTabPos[index] == TAB_NEXUS) { - activateNexusTab(); - } -} - - -void ModInfoDialog::on_modIDEdit_editingFinished() -{ - int oldID = m_Settings->value("modid", 0).toInt(); - int modID = ui->modIDEdit->text().toInt(); - if (oldID != modID){ - m_ModInfo->setNexusID(modID); - - ui->descriptionView->page()->setHtml(""); - if (modID != 0) { - m_RequestStarted = false; - refreshNexusData(modID); - } - } -} - -void ModInfoDialog::on_sourceGameEdit_currentIndexChanged(int) -{ - for (auto game : m_PluginContainer->plugins()) { - if (game->gameName() == ui->sourceGameEdit->currentText()) { - m_ModInfo->setGameName(game->gameShortName()); - return; - } - } -} - -void ModInfoDialog::on_versionEdit_editingFinished() -{ - VersionInfo version(ui->versionEdit->text()); - m_ModInfo->setVersion(version); - updateVersionColor(); -} - -void ModInfoDialog::on_customUrlLineEdit_editingFinished() -{ - m_ModInfo->setURL(ui->customUrlLineEdit->text()); } bool ModInfoDialog::recursiveDelete(const QModelIndex &index) @@ -975,22 +790,6 @@ void ModInfoDialog::on_fileTree_customContextMenuRequested(const QPoint &pos) menu.exec(ui->fileTree->viewport()->mapToGlobal(pos)); } - -void ModInfoDialog::on_refreshButton_clicked() -{ - if (m_ModInfo->getNexusID() > 0) { - QLineEdit *modIDEdit = findChild("modIDEdit"); - int modID = modIDEdit->text().toInt(); - refreshNexusData(modID); - } else - qInfo("Mod has no valid Nexus ID, info can't be updated."); -} - -void ModInfoDialog::on_endorseBtn_clicked() -{ - emit endorseMod(m_ModInfo); -} - void ModInfoDialog::on_nextButton_clicked() { int currentTab = ui->tabWidget->currentIndex(); diff --git a/src/modinfodialog.h b/src/modinfodialog.h index 384aafce..7b96d21a 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -138,18 +138,11 @@ public: void restoreState(const Settings& s); signals: - - void linkActivated(const QString &link); void downloadRequest(const QString &link); void modOpen(const QString &modName, int tab); void modOpenNext(int tab=-1); void modOpenPrev(int tab=-1); void originModified(int originID); - void endorseMod(ModInfo::Ptr nexusID); - -public slots: - - void modDetailsUpdated(bool success); private: @@ -157,10 +150,6 @@ private: void refreshLists(); - void updateVersionColor(); - - void refreshNexusData(int modID); - void activateNexusTab(); QString getFileCategory(int categoryID); bool recursiveDelete(const QModelIndex &index); void deleteFile(const QModelIndex &index); @@ -168,9 +157,6 @@ private: int tabIndex(const QString &tabId); private slots: - void linkClicked(const QUrl &url); - void linkClicked(QString url); - void delete_activated(); void createDirectoryTriggered(); @@ -183,20 +169,10 @@ private slots: void on_openInExplorerButton_clicked(); void on_closeButton_clicked(); - void on_visitNexusLabel_linkActivated(const QString &link); - void on_modIDEdit_editingFinished(); - void on_sourceGameEdit_currentIndexChanged(int); - void on_versionEdit_editingFinished(); - void on_customUrlLineEdit_editingFinished(); void on_tabWidget_currentChanged(int index); void on_fileTree_customContextMenuRequested(const QPoint &pos); - void on_refreshButton_clicked(); - - void on_endorseBtn_clicked(); - void on_nextButton_clicked(); - void on_prevButton_clicked(); private: @@ -217,11 +193,6 @@ private: QTreeView *m_FileTree; QModelIndexList m_FileSelection; - QSettings *m_Settings; - - std::set m_RequestIDs; - bool m_RequestStarted; - QAction *m_NewFolderAction; QAction *m_OpenAction; QAction *m_PreviewAction; diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui index c8ffd470..29a7400f 100644 --- a/src/modinfodialog.ui +++ b/src/modinfodialog.ui @@ -829,7 +829,7 @@ text-align: left; - + true @@ -853,13 +853,13 @@ text-align: left; - +
- + :/MO/gui/resources/internet-web-browser.png:/MO/gui/resources/internet-web-browser.png @@ -870,6 +870,60 @@ text-align: left; + + + + + 0 + 0 + + + + Refresh + + + Refresh all information from Nexus. + + + Refresh + + + + :/MO/gui/refresh:/MO/gui/refresh + + + Qt::ToolButtonTextBesideIcon + + + + + + + Open in Browser + + + + :/MO/gui/resources/internet-web-browser.png:/MO/gui/resources/internet-web-browser.png + + + Qt::ToolButtonTextBesideIcon + + + + + + + Endorse + + + + :/MO/gui/icon_favorite:/MO/gui/icon_favorite + + + Qt::ToolButtonTextBesideIcon + + + @@ -878,7 +932,7 @@ text-align: left; - + Mod ID for this mod on Nexus. @@ -891,22 +945,6 @@ p, li { white-space: pre-wrap; } - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 10 - 20 - - - - @@ -915,7 +953,7 @@ p, li { white-space: pre-wrap; } - + Source game for this mod. @@ -925,20 +963,7 @@ p, li { white-space: pre-wrap; } - - - Qt::Horizontal - - - - 40 - 20 - - - - - - + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> @@ -952,7 +977,7 @@ p, li { white-space: pre-wrap; } - + 150 @@ -967,102 +992,61 @@ p, li { white-space: pre-wrap; } - - - - - - 0 - 0 - - - - Refresh - - - Refresh all information from Nexus. - - - - - - - :/MO/gui/refresh:/MO/gui/refresh - - - - - - - Description - - - - - - - - - - 0 - 0 - - - - - 0 - 200 - + + + QFrame::StyledPanel - - - about:blank - + + QFrame::Sunken + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 0 + 200 + + + + + about:blank + + + + + - - - - - - - 1 - 0 - - - - - - - false - - - - - - - Endorse - - - - :/MO/gui/icon_favorite:/MO/gui/icon_favorite - - - - - - Web page URL (only used if invalid Nexus ID) : + URL - + diff --git a/src/modinfodialogcategories.cpp b/src/modinfodialogcategories.cpp index 7df13aea..69c7c6b5 100644 --- a/src/modinfodialogcategories.cpp +++ b/src/modinfodialogcategories.cpp @@ -3,28 +3,25 @@ #include "categories.h" #include "modinfo.h" -CategoriesTab::CategoriesTab(QWidget*, Ui::ModInfoDialog* ui) - : ui(ui) +CategoriesTab::CategoriesTab( + OrganizerCore& oc, PluginContainer& plugin, + QWidget* parent, Ui::ModInfoDialog* ui) + : ModInfoDialogTab(oc, plugin, parent, ui) { connect( - ui->categoriesTree, &QTreeWidget::itemChanged, + ui->categories, &QTreeWidget::itemChanged, [&](auto* item, int col){ onCategoryChanged(item, col); }); connect( - ui->primaryCategoryBox, + ui->primaryCategories, static_cast(&QComboBox::currentIndexChanged), [&](int index){ onPrimaryChanged(index); }); } -void CategoriesTab::setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin) -{ - m_mod = mod; -} - void CategoriesTab::clear() { - ui->categoriesTree->clear(); - ui->primaryCategoryBox->clear(); + ui->categories->clear(); + ui->primaryCategories->clear(); } void CategoriesTab::update() @@ -32,8 +29,8 @@ void CategoriesTab::update() clear(); add( - CategoryFactory::instance(), m_mod->getCategories(), - ui->categoriesTree->invisibleRootItem(), 0); + CategoryFactory::instance(), mod()->getCategories(), + ui->categories->invisibleRootItem(), 0); updatePrimary(); } @@ -71,15 +68,15 @@ void CategoriesTab::add( void CategoriesTab::updatePrimary() { - ui->primaryCategoryBox->clear(); + ui->primaryCategories->clear(); - int primaryCategory = m_mod->getPrimaryCategory(); + int primaryCategory = mod()->getPrimaryCategory(); - addChecked(ui->categoriesTree->invisibleRootItem()); + addChecked(ui->categories->invisibleRootItem()); - for (int i = 0; i < ui->primaryCategoryBox->count(); ++i) { - if (ui->primaryCategoryBox->itemData(i).toInt() == primaryCategory) { - ui->primaryCategoryBox->setCurrentIndex(i); + for (int i = 0; i < ui->primaryCategories->count(); ++i) { + if (ui->primaryCategories->itemData(i).toInt() == primaryCategory) { + ui->primaryCategories->setCurrentIndex(i); break; } } @@ -90,7 +87,7 @@ void CategoriesTab::addChecked(QTreeWidgetItem* tree) for (int i = 0; i < tree->childCount(); ++i) { QTreeWidgetItem *child = tree->child(i); if (child->checkState(0) == Qt::Checked) { - ui->primaryCategoryBox->addItem(child->text(0), child->data(0, Qt::UserRole)); + ui->primaryCategories->addItem(child->text(0), child->data(0, Qt::UserRole)); addChecked(child); } } @@ -101,7 +98,7 @@ void CategoriesTab::save(QTreeWidgetItem* currentNode) for (int i = 0; i < currentNode->childCount(); ++i) { QTreeWidgetItem *childNode = currentNode->child(i); - m_mod->setCategory( + mod()->setCategory( childNode->data(0, Qt::UserRole).toInt(), childNode->checkState(0)); save(childNode); @@ -118,12 +115,12 @@ void CategoriesTab::onCategoryChanged(QTreeWidgetItem* item, int) } updatePrimary(); - save(ui->categoriesTree->invisibleRootItem()); + save(ui->categories->invisibleRootItem()); } void CategoriesTab::onPrimaryChanged(int index) { if (index != -1) { - m_mod->setPrimaryCategory(ui->primaryCategoryBox->itemData(index).toInt()); + mod()->setPrimaryCategory(ui->primaryCategories->itemData(index).toInt()); } } diff --git a/src/modinfodialogcategories.h b/src/modinfodialogcategories.h index c0644c7f..76426a5d 100644 --- a/src/modinfodialogcategories.h +++ b/src/modinfodialogcategories.h @@ -5,16 +5,14 @@ class CategoryFactory; class CategoriesTab : public ModInfoDialogTab { public: - CategoriesTab(QWidget* parent, Ui::ModInfoDialog* ui); + CategoriesTab( + OrganizerCore& oc, PluginContainer& plugin, + QWidget* parent, Ui::ModInfoDialog* ui); void clear() override; - void setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin) override; void update() override; private: - Ui::ModInfoDialog* ui; - ModInfo::Ptr m_mod; - void add( const CategoryFactory& factory, const std::set& enabledCategories, QTreeWidgetItem* root, int rootLevel); diff --git a/src/modinfodialogconflicts.cpp b/src/modinfodialogconflicts.cpp index d2dca492..4176ac3e 100644 --- a/src/modinfodialogconflicts.cpp +++ b/src/modinfodialogconflicts.cpp @@ -116,10 +116,10 @@ public: ConflictsTab::ConflictsTab( - QWidget* parent, Ui::ModInfoDialog* ui, - OrganizerCore& oc, PluginContainer& plugin) : - m_parent(parent), ui(ui), m_core(oc), m_plugin(plugin), - m_origin(nullptr), m_general(this, ui, oc), m_advanced(this, ui, oc) + OrganizerCore& oc, PluginContainer& plugin, + QWidget* parent, Ui::ModInfoDialog* ui) : + ModInfoDialogTab(oc, plugin, parent, ui), + m_general(this, ui, oc), m_advanced(this, ui, oc) { connect( &m_general, &GeneralConflictsTab::modOpen, @@ -130,15 +130,6 @@ ConflictsTab::ConflictsTab( [&](const QString& name){ emitModOpen(name); }); } -void ConflictsTab::setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin) -{ - m_mod = mod; - m_origin = origin; - - m_general.setMod(mod, origin); - m_advanced.setMod(mod, origin); -} - void ConflictsTab::update() { m_general.update(); @@ -186,7 +177,7 @@ void ConflictsTab::changeItemsVisibility( flags |= FileRenamer::MULTIPLE; } - FileRenamer renamer(m_parent, flags); + FileRenamer renamer(parentWidget(), flags); for (const auto* item : items) { if (stop) { @@ -242,8 +233,8 @@ void ConflictsTab::changeItemsVisibility( if (changed) { qDebug().nospace() << "triggering refresh"; - if (m_origin) { - emit originModified(m_origin->getID()); + if (origin()) { + emit originModified(origin()->getID()); } update(); @@ -256,7 +247,7 @@ void ConflictsTab::openItems(const QList& items) // in case this changes for (auto* item : items) { if (auto* ci=dynamic_cast(item)) { - m_core.executeFileVirtualized(m_parent, ci->fileName()); + core().executeFileVirtualized(parentWidget(), ci->fileName()); } } } @@ -267,7 +258,7 @@ void ConflictsTab::previewItems(const QList& items) // in case this changes for (auto* item : items) { if (auto* ci=dynamic_cast(item)) { - m_core.previewFileWithAlternatives(m_parent, ci->fileName()); + core().previewFileWithAlternatives(parentWidget(), ci->fileName()); } } } @@ -355,7 +346,7 @@ ConflictsTab::Actions ConflictsTab::createMenuActions( enableHide = ci->canHide(); enableUnhide = ci->canUnhide(); enableOpen = ci->canOpen(); - enablePreview = ci->canPreview(m_plugin); + enablePreview = ci->canPreview(plugin()); enableGoto = ci->hasAlts(); } else { @@ -394,21 +385,21 @@ ConflictsTab::Actions ConflictsTab::createMenuActions( Actions actions; - actions.hide = new QAction(tr("Hide"), m_parent); + actions.hide = new QAction(tr("Hide"), parentWidget()); actions.hide->setEnabled(enableHide); // note that it is possible for hidden files to appear if they override other // hidden files from another mod - actions.unhide = new QAction(tr("Unhide"), m_parent); + actions.unhide = new QAction(tr("Unhide"), parentWidget()); actions.unhide->setEnabled(enableUnhide); - actions.open = new QAction(tr("Open/Execute"), m_parent); + actions.open = new QAction(tr("Open/Execute"), parentWidget()); actions.open->setEnabled(enableOpen); - actions.preview = new QAction(tr("Preview"), m_parent); + actions.preview = new QAction(tr("Preview"), parentWidget()); actions.preview->setEnabled(enablePreview); - actions.gotoMenu = new QMenu(tr("Go to..."), m_parent); + actions.gotoMenu = new QMenu(tr("Go to..."), parentWidget()); actions.gotoMenu->setEnabled(enableGoto); if (enableGoto) { @@ -421,7 +412,7 @@ ConflictsTab::Actions ConflictsTab::createMenuActions( std::vector ConflictsTab::createGotoActions( const QList& selection) { - if (!m_origin || selection.size() != 1) { + if (!origin() || selection.size() != 1) { return {}; } @@ -430,26 +421,26 @@ std::vector ConflictsTab::createGotoActions( return {}; } - auto file = m_origin->findFile(item->fileIndex()); + auto file = origin()->findFile(item->fileIndex()); if (!file) { return {}; } std::vector mods; - const auto& ds = *m_core.directoryStructure(); + const auto& ds = *core().directoryStructure(); // add all alternatives for (const auto& alt : file->getAlternatives()) { const auto& o = ds.getOriginByID(alt.first); - if (o.getID() != m_origin->getID()) { + if (o.getID() != origin()->getID()) { mods.push_back(ToQString(o.getName())); } } // add the real origin if different from this mod const FilesOrigin& realOrigin = ds.getOriginByID(file->getOrigin()); - if (realOrigin.getID() != m_origin->getID()) { + if (realOrigin.getID() != origin()->getID()) { mods.push_back(ToQString(realOrigin.getName())); } @@ -460,7 +451,7 @@ std::vector ConflictsTab::createGotoActions( std::vector actions; for (const auto& name : mods) { - actions.push_back(new QAction(name, m_parent)); + actions.push_back(new QAction(name, parentWidget())); } return actions; @@ -469,7 +460,7 @@ std::vector ConflictsTab::createGotoActions( GeneralConflictsTab::GeneralConflictsTab( ConflictsTab* tab, Ui::ModInfoDialog* ui, OrganizerCore& oc) - : m_tab(tab), ui(ui), m_core(oc), m_origin(nullptr) + : m_tab(tab), ui(ui), m_core(oc) { m_expanders.overwrite.set(ui->overwriteExpander, ui->overwriteTree, true); m_expanders.overwritten.set(ui->overwrittenExpander, ui->overwrittenTree, true); @@ -560,12 +551,6 @@ void GeneralConflictsTab::restoreState(const Settings& s) .value("mod_info_conflicts_general_overwritten").toByteArray()); } -void GeneralConflictsTab::setMod(ModInfo::Ptr mod, FilesOrigin* origin) -{ - m_mod = mod; - m_origin = origin; -} - void GeneralConflictsTab::update() { clear(); @@ -574,10 +559,10 @@ void GeneralConflictsTab::update() int numOverwrite = 0; int numOverwritten = 0; - if (m_origin != nullptr) { - const auto rootPath = m_mod->absolutePath(); + if (m_tab->origin() != nullptr) { + const auto rootPath = m_tab->mod()->absolutePath(); - for (const auto& file : m_origin->getFiles()) { + for (const auto& file : m_tab->origin()->getFiles()) { const QString relativeName = QDir::fromNativeSeparators(ToQString(file->getRelativePath())); const QString fileName = relativeName.mid(0).prepend(rootPath); @@ -585,7 +570,7 @@ void GeneralConflictsTab::update() const int fileOrigin = file->getOrigin(archive); const auto& alternatives = file->getAlternatives(); - if (fileOrigin == m_origin->getID()) { + if (fileOrigin == m_tab->origin()->getID()) { if (!alternatives.empty()) { ui->overwriteTree->addTopLevelItem(createOverwriteItem( file->getIndex(), archive, fileName, relativeName, alternatives)); @@ -684,7 +669,7 @@ void GeneralConflictsTab::onOverwrittenActivated(QTreeWidgetItem *item, int) AdvancedConflictsTab::AdvancedConflictsTab( ConflictsTab* tab, Ui::ModInfoDialog* ui, OrganizerCore& oc) - : m_tab(tab), ui(ui), m_core(oc), m_origin(nullptr) + : m_tab(tab), ui(ui), m_core(oc) { // left-elide the overwrites column so that the nearest are visible ui->conflictsAdvancedList->setItemDelegateForColumn( @@ -760,20 +745,14 @@ void AdvancedConflictsTab::restoreState(const Settings& s) } } -void AdvancedConflictsTab::setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin) -{ - m_mod = mod; - m_origin = origin; -} - void AdvancedConflictsTab::update() { clear(); - if (m_origin != nullptr) { - const auto rootPath = m_mod->absolutePath(); + if (m_tab->origin() != nullptr) { + const auto rootPath = m_tab->mod()->absolutePath(); - for (const auto& file : m_origin->getFiles()) { + for (const auto& file : m_tab->origin()->getFiles()) { const QString relativeName = QDir::fromNativeSeparators(ToQString(file->getRelativePath())); const QString fileName = relativeName.mid(0).prepend(rootPath); @@ -813,14 +792,14 @@ QTreeWidgetItem* AdvancedConflictsTab::createItem( // fills 'before' and 'after' with all the alternatives that come // before and after this mod in terms of priority - if (altOrigin.getPriority() < m_origin->getPriority()) { + if (altOrigin.getPriority() < m_tab->origin()->getPriority()) { // add all the mods having a lower priority than this one if (!before.isEmpty()) { before += ", "; } before += ToQString(altOrigin.getName()); - } else if (altOrigin.getPriority() > m_origin->getPriority()) { + } else if (altOrigin.getPriority() > m_tab->origin()->getPriority()) { // add all the mods having a higher priority than this one if (!after.isEmpty()) { after += ", "; @@ -832,7 +811,7 @@ QTreeWidgetItem* AdvancedConflictsTab::createItem( // keep track of the nearest mods that come before and after this one // in terms of priority - if (altOrigin.getPriority() < m_origin->getPriority()) { + if (altOrigin.getPriority() < m_tab->origin()->getPriority()) { // the alternative has a lower priority than this mod if (altOrigin.getPriority() > beforePrio) { @@ -843,7 +822,7 @@ QTreeWidgetItem* AdvancedConflictsTab::createItem( } } - if (altOrigin.getPriority() > m_origin->getPriority()) { + if (altOrigin.getPriority() > m_tab->origin()->getPriority()) { // the alternative has a higher priority than this mod if (altOrigin.getPriority() < afterPrio) { @@ -869,7 +848,7 @@ QTreeWidgetItem* AdvancedConflictsTab::createItem( // if no mods overwrite this file, the primary origin is the same as this // mod, so ignore that - if (realOrigin.getID() != m_origin->getID()) { + if (realOrigin.getID() != m_tab->origin()->getID()) { if (!after.isEmpty()) { after += ", "; } diff --git a/src/modinfodialogconflicts.h b/src/modinfodialogconflicts.h index 212a196d..20362575 100644 --- a/src/modinfodialogconflicts.h +++ b/src/modinfodialogconflicts.h @@ -21,7 +21,6 @@ public: void saveState(Settings& s); void restoreState(const Settings& s); - void setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin); void update(); signals: @@ -36,8 +35,6 @@ private: ConflictsTab* m_tab; Ui::ModInfoDialog* ui; OrganizerCore& m_core; - ModInfo::Ptr m_mod; - MOShared::FilesOrigin* m_origin; Expanders m_expanders; QTreeWidgetItem* createOverwriteItem( @@ -74,7 +71,6 @@ public: void saveState(Settings& s); void restoreState(const Settings& s); - void setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin); void update(); signals: @@ -84,8 +80,6 @@ private: ConflictsTab* m_tab; Ui::ModInfoDialog* ui; OrganizerCore& m_core; - ModInfo::Ptr m_mod; - MOShared::FilesOrigin* m_origin; FilterWidget m_filter; QTreeWidgetItem* createItem( @@ -101,10 +95,9 @@ class ConflictsTab : public ModInfoDialogTab public: ConflictsTab( - QWidget* parent, Ui::ModInfoDialog* ui, - OrganizerCore& oc, PluginContainer& plugin); + OrganizerCore& oc, PluginContainer& plugin, + QWidget* parent, Ui::ModInfoDialog* ui); - void setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin) override; void update() override; void clear() override; @@ -136,12 +129,6 @@ private: } }; - QWidget* m_parent; - Ui::ModInfoDialog* ui; - OrganizerCore& m_core; - PluginContainer& m_plugin; - ModInfo::Ptr m_mod; - MOShared::FilesOrigin* m_origin; GeneralConflictsTab m_general; AdvancedConflictsTab m_advanced; diff --git a/src/modinfodialogesps.cpp b/src/modinfodialogesps.cpp index cb3dcc84..ea7eb3b0 100644 --- a/src/modinfodialogesps.cpp +++ b/src/modinfodialogesps.cpp @@ -122,8 +122,10 @@ private: }; -ESPsTab::ESPsTab(QWidget* parent, Ui::ModInfoDialog* ui) - : m_parent(parent), ui(ui) +ESPsTab::ESPsTab( + OrganizerCore& oc, PluginContainer& plugin, + QWidget* parent, Ui::ModInfoDialog* ui) + : ModInfoDialogTab(oc, plugin, parent, ui) { QObject::connect( ui->activateESP, &QToolButton::clicked, [&]{ onActivate(); }); @@ -187,7 +189,7 @@ void ESPsTab::onActivate() bool okClicked = false; newName = QInputDialog::getText( - m_parent, + parentWidget(), QObject::tr("File Exists"), QObject::tr("A file with that name exists, please enter a new one"), QLineEdit::Normal, file.fileName(), &okClicked); diff --git a/src/modinfodialogesps.h b/src/modinfodialogesps.h index d71ad3ff..e1a7a4f7 100644 --- a/src/modinfodialogesps.h +++ b/src/modinfodialogesps.h @@ -10,15 +10,14 @@ class ESPsTab : public ModInfoDialogTab Q_OBJECT; public: - ESPsTab(QWidget* parent, Ui::ModInfoDialog* ui); + ESPsTab( + OrganizerCore& oc, PluginContainer& plugin, + QWidget* parent, Ui::ModInfoDialog* ui); void clear() override; bool feedFile(const QString& rootPath, const QString& fullPath) override; private: - QWidget* m_parent; - Ui::ModInfoDialog* ui; - void onActivate(); void onDeactivate(); diff --git a/src/modinfodialogimages.cpp b/src/modinfodialogimages.cpp index 752c8d0c..1c7dcc1f 100644 --- a/src/modinfodialogimages.cpp +++ b/src/modinfodialogimages.cpp @@ -80,8 +80,10 @@ void ScalableImage::mousePressEvent(QMouseEvent* e) } -ImagesTab::ImagesTab(QWidget*, Ui::ModInfoDialog* ui) - : ui(ui), m_image(new ScalableImage) +ImagesTab::ImagesTab( + OrganizerCore& oc, PluginContainer& plugin, + QWidget* parent, Ui::ModInfoDialog* ui) + : ModInfoDialogTab(oc, plugin, parent, ui), m_image(new ScalableImage) { ui->imagesImage->layout()->addWidget(m_image); ui->imagesThumbnails->setLayout(new QVBoxLayout); diff --git a/src/modinfodialogimages.h b/src/modinfodialogimages.h index 708045c8..7853935a 100644 --- a/src/modinfodialogimages.h +++ b/src/modinfodialogimages.h @@ -34,13 +34,14 @@ class ImagesTab : public ModInfoDialogTab Q_OBJECT; public: - ImagesTab(QWidget* parent, Ui::ModInfoDialog* ui); + ImagesTab( + OrganizerCore& oc, PluginContainer& plugin, + QWidget* parent, Ui::ModInfoDialog* ui); void clear() override; bool feedFile(const QString& rootPath, const QString& fullPath) override; private: - Ui::ModInfoDialog* ui; ScalableImage* m_image; void add(const QString& fullPath); diff --git a/src/modinfodialognexus.cpp b/src/modinfodialognexus.cpp new file mode 100644 index 00000000..753d43de --- /dev/null +++ b/src/modinfodialognexus.cpp @@ -0,0 +1,273 @@ +#include "modinfodialognexus.h" +#include "ui_modinfodialog.h" +#include "settings.h" +#include "organizercore.h" +#include "iplugingame.h" +#include "bbcode.h" +#include +#include + +NexusTab::NexusTab( + OrganizerCore& oc, PluginContainer& plugin, + QWidget* parent, Ui::ModInfoDialog* ui) + : ModInfoDialogTab(oc, plugin, parent, ui), m_requestStarted(false) +{ + ui->modID->setValidator(new QIntValidator(ui->modID)); + ui->endorse->setVisible(core().settings().endorsementIntegration()); + + connect(ui->modID, &QLineEdit::editingFinished, [&]{ onModIDChanged(); }); + connect(ui->version, &QLineEdit::editingFinished, [&]{ onVersionChanged(); }); + connect(ui->openInBrowser, &QToolButton::clicked, [&]{ onOpenLink(); }); + connect(ui->url, &QLineEdit::editingFinished, [&]{ onUrlChanged(); }); + connect(ui->endorse, &QToolButton::clicked, [&]{ onEndorse(); }); + connect(ui->refresh, &QToolButton::clicked, [&]{ updateWebpage(); }); + + connect( + ui->sourceGame, + static_cast(&QComboBox::currentIndexChanged), + [&]{ onSourceGameChanged(); }); +} + +NexusTab::~NexusTab() +{ + cleanup(); +} + +void NexusTab::cleanup() +{ + if (m_modConnection) { + disconnect(m_modConnection); + m_modConnection = {}; + } +} + +void NexusTab::clear() +{ + ui->modID->clear(); + ui->sourceGame->clear(); + ui->version->clear(); + ui->browser->setPage(new NexusTabWebpage(ui->browser)); + ui->url->clear(); +} + +void NexusTab::update() +{ + clear(); + + ui->modID->setText(QString("%1").arg(mod()->getNexusID())); + + QString gameName = mod()->getGameName(); + ui->sourceGame->addItem( + core().managedGame()->gameName(), + core().managedGame()->gameShortName()); + + if (core().managedGame()->validShortNames().size() == 0) { + ui->sourceGame->setDisabled(true); + } else { + for (auto game : plugin().plugins()) { + for (QString gameName : core().managedGame()->validShortNames()) { + if (game->gameShortName().compare(gameName, Qt::CaseInsensitive) == 0) { + ui->sourceGame->addItem(game->gameName(), game->gameShortName()); + break; + } + } + } + } + + ui->sourceGame->setCurrentIndex(ui->sourceGame->findData(gameName)); + + auto* page = new NexusTabWebpage(ui->browser); + ui->browser->setPage(page); + + connect( + page, &NexusTabWebpage::linkClicked, + [&](const QUrl& url){ MOBase::shell::OpenLink(url); }); + + ui->endorse->setEnabled( + (mod()->endorsedState() == ModInfo::ENDORSED_FALSE) || + (mod()->endorsedState() == ModInfo::ENDORSED_NEVER)); + + updateWebpage(); +} + +void NexusTab::setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin) +{ + cleanup(); + + ModInfoDialogTab::setMod(mod, origin); + + m_modConnection = connect( + mod.data(), &ModInfo::modDetailsUpdated, [&]{ onModChanged(); }); +} + +void NexusTab::updateVersionColor() +{ + if (mod()->getVersion() != mod()->getNewestVersion()) { + ui->version->setStyleSheet("color: red"); + ui->version->setToolTip(tr("Current Version: %1").arg( + mod()->getNewestVersion().canonicalString())); + } else { + ui->version->setStyleSheet("color: green"); + ui->version->setToolTip(tr("No update available")); + } +} + +void NexusTab::updateWebpage() +{ + const int modID = mod()->getNexusID(); + + if (modID > 0) { + const QString nexusLink = NexusInterface::instance(&plugin()) + ->getModURL(modID, mod()->getGameName()); + + ui->openInBrowser->setToolTip(nexusLink); + mod()->setURL(nexusLink); + refreshData(modID); + } else { + onModChanged(); + } + + ui->version->setText(mod()->getVersion().displayString()); + ui->url->setText(mod()->getURL()); +} + +void NexusTab::onModChanged() +{ + m_requestStarted = false; + + const QString nexusDescription = mod()->getNexusDescription(); + + QString descriptionAsHTML = R"( + + + + + %1 +)"; + + if (nexusDescription.isEmpty()) { + descriptionAsHTML = descriptionAsHTML.arg(tr( + "
" + "

Uh oh!

" + "

Sorry, there is no description available for this mod. :(

" + "
")); + + } else { + descriptionAsHTML = descriptionAsHTML.arg( + BBCode::convertToHTML(nexusDescription)); + } + + ui->browser->page()->setHtml(descriptionAsHTML); + updateVersionColor(); +} + +void NexusTab::onModIDChanged() +{ + const int oldID = mod()->getNexusID(); + const int newID = ui->modID->text().toInt(); + + if (oldID != newID){ + mod()->setNexusID(newID); + mod()->setLastNexusQuery(QDateTime::fromSecsSinceEpoch(0)); + + ui->browser->page()->setHtml(""); + + if (newID != 0) { + refreshData(newID); + } + } +} + +void NexusTab::onSourceGameChanged() +{ + for (auto game : plugin().plugins()) { + if (game->gameName() == ui->sourceGame->currentText()) { + mod()->setGameName(game->gameShortName()); + mod()->setLastNexusQuery(QDateTime::fromSecsSinceEpoch(0)); + refreshData(mod()->getNexusID()); + return; + } + } +} + +void NexusTab::onVersionChanged() +{ + MOBase::VersionInfo version(ui->version->text()); + mod()->setVersion(version); + updateVersionColor(); +} + +void NexusTab::onUrlChanged() +{ + mod()->setURL(ui->url->text()); + mod()->setLastNexusQuery(QDateTime::fromSecsSinceEpoch(0)); +} + +void NexusTab::onOpenLink() +{ + const int modID = mod()->getNexusID(); + + if (modID > 0) { + const QString nexusLink = NexusInterface::instance(&plugin()) + ->getModURL(modID, mod()->getGameName()); + + MOBase::shell::OpenLink(QUrl(nexusLink)); + } +} + +void NexusTab::onRefreshBrowser() +{ + const auto modID = mod()->getNexusID(); + + if (modID > 0) { + refreshData(modID); + } else + qInfo("Mod has no valid Nexus ID, info can't be updated."); +} + +void NexusTab::onEndorse() +{ + core().loggedInAction(parentWidget(), [m=mod()]{ m->endorse(true); }); +} + +void NexusTab::refreshData(int modID) +{ + if (tryRefreshData(modID)) { + m_requestStarted = true; + } else { + onModChanged(); + } + + //MessageDialog::showMessage(tr("Info requested, please wait"), this); +} + +bool NexusTab::tryRefreshData(int modID) +{ + if (modID <= 0) { + qDebug() << "NexusTab: can't refresh, no mod id"; + return false; + } + + if (m_requestStarted) { + qDebug() << "NexusTab: a refresh request is already running"; + return false; + } + + if (!mod()->updateNXMInfo()) { + qDebug() << "NexusTab: nexus description does not need an update"; + return false; + } + + return true; +} diff --git a/src/modinfodialognexus.h b/src/modinfodialognexus.h new file mode 100644 index 00000000..2e328c6d --- /dev/null +++ b/src/modinfodialognexus.h @@ -0,0 +1,67 @@ +#ifndef MODINFODIALOGNEXUS_H +#define MODINFODIALOGNEXUS_H + +#include "modinfodialogtab.h" + +class NexusTabWebpage : public QWebEnginePage +{ + Q_OBJECT + +public: + NexusTabWebpage(QObject* parent = 0) + : QWebEnginePage(parent) + { + } + + bool acceptNavigationRequest( + const QUrl & url, QWebEnginePage::NavigationType type, bool) override + { + if (type == QWebEnginePage::NavigationTypeLinkClicked) + { + emit linkClicked(url); + return false; + } + + return true; + } + +signals: + void linkClicked(const QUrl&); +}; + + +class NexusTab : public ModInfoDialogTab +{ +public: + NexusTab( + OrganizerCore& oc, PluginContainer& plugin, + QWidget* parent, Ui::ModInfoDialog* ui); + + ~NexusTab(); + + void clear() override; + void update() override; + void setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin) override; + +private: + QMetaObject::Connection m_modConnection; + bool m_requestStarted; + + void cleanup(); + void updateVersionColor(); + void updateWebpage(); + + void refreshData(int modID); + bool tryRefreshData(int modID); + + void onModChanged(); + void onOpenLink(); + void onModIDChanged(); + void onSourceGameChanged(); + void onVersionChanged(); + void onRefreshBrowser(); + void onEndorse(); + void onUrlChanged(); +}; + +#endif // MODINFODIALOGNEXUS_H diff --git a/src/modinfodialogtab.cpp b/src/modinfodialogtab.cpp index 6dcff0ac..ae0de5e8 100644 --- a/src/modinfodialogtab.cpp +++ b/src/modinfodialogtab.cpp @@ -1,5 +1,12 @@ #include "modinfodialogtab.h" +ModInfoDialogTab::ModInfoDialogTab( + OrganizerCore& oc, PluginContainer& plugin, + QWidget* parent, Ui::ModInfoDialog* ui) + : ui(ui), m_core(oc), m_plugin(plugin), m_parent(parent), m_origin(nullptr) +{ +} + bool ModInfoDialogTab::feedFile(const QString&, const QString&) { // no-op @@ -21,14 +28,40 @@ void ModInfoDialogTab::restoreState(const Settings& s) // no-op } -void ModInfoDialogTab::setMod(ModInfo::Ptr, MOShared::FilesOrigin*) +void ModInfoDialogTab::update() { // no-op } -void ModInfoDialogTab::update() +void ModInfoDialogTab::setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin) { - // no-op + m_mod = mod; + m_origin = origin; +} + +ModInfo::Ptr ModInfoDialogTab::mod() const +{ + return m_mod; +} + +MOShared::FilesOrigin* ModInfoDialogTab::origin() const +{ + return m_origin; +} + +OrganizerCore& ModInfoDialogTab::core() +{ + return m_core; +} + +PluginContainer& ModInfoDialogTab::plugin() +{ + return m_plugin; +} + +QWidget* ModInfoDialogTab::parentWidget() +{ + return m_parent; } void ModInfoDialogTab::emitOriginModified(int originID) diff --git a/src/modinfodialogtab.h b/src/modinfodialogtab.h index ead29d10..dd851b31 100644 --- a/src/modinfodialogtab.h +++ b/src/modinfodialogtab.h @@ -8,13 +8,13 @@ namespace MOShared { class FilesOrigin; } namespace Ui { class ModInfoDialog; } class Settings; +class OrganizerCore; class ModInfoDialogTab : public QObject { Q_OBJECT; public: - ModInfoDialogTab() = default; ModInfoDialogTab(const ModInfoDialogTab&) = delete; ModInfoDialogTab& operator=(const ModInfoDialogTab&) = delete; ModInfoDialogTab(ModInfoDialogTab&&) = default; @@ -22,21 +22,42 @@ public: virtual ~ModInfoDialogTab() = default; virtual void clear() = 0; + virtual void update(); virtual bool feedFile(const QString& rootPath, const QString& filename); virtual bool canClose(); virtual void saveState(Settings& s); virtual void restoreState(const Settings& s); virtual void setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin); - virtual void update(); + + ModInfo::Ptr mod() const; + MOShared::FilesOrigin* origin() const; signals: void originModified(int originID); void modOpen(QString name); protected: + Ui::ModInfoDialog* ui; + + ModInfoDialogTab( + OrganizerCore& oc, PluginContainer& plugin, + QWidget* parent, Ui::ModInfoDialog* ui); + + OrganizerCore& core(); + PluginContainer& plugin(); + + QWidget* parentWidget(); + void emitOriginModified(int originID); void emitModOpen(QString name); + +private: + OrganizerCore& m_core; + PluginContainer& m_plugin; + QWidget* m_parent; + ModInfo::Ptr m_mod; + MOShared::FilesOrigin* m_origin; }; #endif // MODINFODIALOGTAB_H diff --git a/src/modinfodialogtextfiles.cpp b/src/modinfodialogtextfiles.cpp index 0dd6f06e..f48557b0 100644 --- a/src/modinfodialogtextfiles.cpp +++ b/src/modinfodialogtextfiles.cpp @@ -22,8 +22,10 @@ private: GenericFilesTab::GenericFilesTab( - QWidget* parent, QListWidget* list, QSplitter* sp, TextEditor* e) - : m_parent(parent), m_list(list), m_editor(e) + OrganizerCore& oc, PluginContainer& plugin, + QWidget* parent, Ui::ModInfoDialog* ui, + QListWidget* list, QSplitter* sp, TextEditor* e) + : ModInfoDialogTab(oc, plugin, parent, ui), m_list(list), m_editor(e) { m_editor->setupToolbar(); @@ -49,7 +51,7 @@ bool GenericFilesTab::canClose() } const int res = QMessageBox::question( - m_parent, + parentWidget(), QObject::tr("Save changes?"), QObject::tr("Save changes to \"%1\"?").arg(m_editor->filename()), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); @@ -109,9 +111,12 @@ void GenericFilesTab::select(FileListItem* item) } -TextFilesTab::TextFilesTab(QWidget* parent, Ui::ModInfoDialog* ui) - : GenericFilesTab( - parent, ui->textFileList, ui->tabTextSplitter, ui->textFileEditor) +TextFilesTab::TextFilesTab( + OrganizerCore& oc, PluginContainer& plugin, + QWidget* parent, Ui::ModInfoDialog* ui) + : GenericFilesTab( + oc, plugin, parent, ui, + ui->textFileList, ui->tabTextSplitter, ui->textFileEditor) { } @@ -130,9 +135,12 @@ bool TextFilesTab::wantsFile(const QString& rootPath, const QString& fullPath) c return false; } -IniFilesTab::IniFilesTab(QWidget* parent, Ui::ModInfoDialog* ui) - : GenericFilesTab( - parent, ui->iniFileList, ui->tabIniSplitter, ui->iniFileEditor) +IniFilesTab::IniFilesTab( + OrganizerCore& oc, PluginContainer& plugin, + QWidget* parent, Ui::ModInfoDialog* ui) + : GenericFilesTab( + oc, plugin, parent, ui, + ui->iniFileList, ui->tabIniSplitter, ui->iniFileEditor) { } diff --git a/src/modinfodialogtextfiles.h b/src/modinfodialogtextfiles.h index 11691d64..0dc5ec89 100644 --- a/src/modinfodialogtextfiles.h +++ b/src/modinfodialogtextfiles.h @@ -18,12 +18,12 @@ public: bool feedFile(const QString& rootPath, const QString& fullPath) override; protected: - QWidget* m_parent; QListWidget* m_list; TextEditor* m_editor; GenericFilesTab( - QWidget* parent, + OrganizerCore& oc, PluginContainer& plugin, + QWidget* parent, Ui::ModInfoDialog* ui, QListWidget* list, QSplitter* splitter, TextEditor* editor); virtual bool wantsFile(const QString& rootPath, const QString& fullPath) const = 0; @@ -37,7 +37,9 @@ private: class TextFilesTab : public GenericFilesTab { public: - TextFilesTab(QWidget* parent, Ui::ModInfoDialog* ui); + TextFilesTab( + OrganizerCore& oc, PluginContainer& plugin, + QWidget* parent, Ui::ModInfoDialog* ui); protected: bool wantsFile(const QString& rootPath, const QString& fullPath) const override; @@ -47,7 +49,9 @@ protected: class IniFilesTab : public GenericFilesTab { public: - IniFilesTab(QWidget* parent, Ui::ModInfoDialog* ui); + IniFilesTab( + OrganizerCore& oc, PluginContainer& plugin, + QWidget* parent, Ui::ModInfoDialog* ui); protected: bool wantsFile(const QString& rootPath, const QString& fullPath) const override; diff --git a/src/modinforegular.cpp b/src/modinforegular.cpp index a271c4e8..babbd665 100644 --- a/src/modinforegular.cpp +++ b/src/modinforegular.cpp @@ -118,7 +118,7 @@ void ModInfoRegular::readMeta() m_EndorsedState = metaFile.value("endorsed", false).toBool() ? ENDORSED_TRUE : ENDORSED_FALSE; } } - + QString categoriesString = metaFile.value("category", "").toString(); QStringList categories = categoriesString.split(',', QString::SkipEmptyParts); @@ -291,15 +291,27 @@ void ModInfoRegular::nxmRequestFailed(QString, int, int, QVariant userData, QNet bool ModInfoRegular::updateNXMInfo() { - QDateTime time = QDateTime::currentDateTimeUtc(); - QDateTime target = m_LastNexusQuery.addDays(1); - if (m_NexusID > 0 && time >= target) { + if (needsDescriptionUpdate()) { m_NexusBridge.requestDescription(m_GameName, m_NexusID, QVariant()); return true; } + return false; } +bool ModInfoRegular::needsDescriptionUpdate() const +{ + if (m_NexusID > 0) { + QDateTime time = QDateTime::currentDateTimeUtc(); + QDateTime target = m_LastNexusQuery.addDays(1); + + if (time >= target) { + return true; + } + } + + return false; +} void ModInfoRegular::setCategory(int categoryID, bool active) { diff --git a/src/modinforegular.h b/src/modinforegular.h index f70487a2..cfe713ca 100644 --- a/src/modinforegular.h +++ b/src/modinforegular.h @@ -464,6 +464,7 @@ private: mutable std::vector m_CachedContent; mutable QTime m_LastContentCheck; + bool needsDescriptionUpdate() const; }; diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 2cad9ce8..e073924b 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -2204,6 +2204,21 @@ void OrganizerCore::updateModsInDirectoryStructure(QMap f) +{ + if (NexusInterface::instance(m_PluginContainer)->getAccessManager()->validated()) { + f(); + } else { + QString apiKey; + if (settings().getNexusApiKey(apiKey)) { + doAfterLogin([f]{ f(); }); + NexusInterface::instance(m_PluginContainer)->getAccessManager()->apiCheck(apiKey); + } else { + MessageDialog::showMessage(tr("You need to be logged in with Nexus"), parent); + } + } +} + void OrganizerCore::requestDownload(const QUrl &url, QNetworkReply *reply) { if (m_PluginContainer != nullptr) { diff --git a/src/organizercore.h b/src/organizercore.h index a4a57496..4dd11831 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -146,6 +146,7 @@ public: void updateModsInDirectoryStructure(QMap modInfos); void doAfterLogin(const std::function &function) { m_PostLoginTasks.append(function); } + void loggedInAction(QWidget* parent, std::function f); static QString findJavaInstallation(const QString& jarFile={}); -- cgit v1.3.1 From 895883571b2b71c891dbaad4662adc7b39256286 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sun, 23 Jun 2019 04:04:54 -0400 Subject: splitting filetree tab moved mod info dialog classes to a sub filter --- src/CMakeLists.txt | 19 +- src/modinfodialog.cpp | 409 +-------------------------------------- src/modinfodialog.h | 28 +-- src/modinfodialog.ui | 8 +- src/modinfodialogconflicts.cpp | 14 ++ src/modinfodialogconflicts.h | 16 +- src/modinfodialogfiletree.cpp | 420 +++++++++++++++++++++++++++++++++++++++++ src/modinfodialogfiletree.h | 39 ++++ 8 files changed, 502 insertions(+), 451 deletions(-) create mode 100644 src/modinfodialogfiletree.cpp create mode 100644 src/modinfodialogfiletree.h (limited to 'src/modinfodialogconflicts.cpp') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7dc39fd9..1d27f444 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -57,6 +57,7 @@ SET(organizer_SRCS modinfodialogcategories.cpp modinfodialogconflicts.cpp modinfodialogesps.cpp + modinfodialogfiletree.cpp modinfodialogimages.cpp modinfodialognexus.cpp modinfodialogtab.cpp @@ -164,6 +165,7 @@ SET(organizer_HDRS modinfodialogcategories.h modinfodialogconflicts.h modinfodialogesps.h + modinfodialogfiletree.h modinfodialogimages.h modinfodialognexus.h modinfodialogtab.h @@ -364,21 +366,24 @@ set(locking set(modinfo modinfo modinfobackup + modinfoforeign + modinfooverwrite + modinforegular + modinfoseparator + modinfowithconflictinfo +) + +set(modinfo\\dialog modinfodialog modinfodialogcategories modinfodialogconflicts modinfodialogesps + modinfodialogfiletree modinfodialogimages modinfodialognexus modinfodialogtab modinfodialogtextfiles - modinfoforeign - modinfooverwrite - modinforegular - modinfoseparator - modinfowithconflictinfo ) - set(modlist modlist modlistsortproxy @@ -443,7 +448,7 @@ set(widgets ) set(src_filters - application core browser dialogs downloads executables locking modinfo + application core browser dialogs downloads executables locking modinfo modinfo\\dialog modlist plugins previews profiles settings utilities widgets ) diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index b78f4515..6463a5a6 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -43,6 +43,7 @@ along with Mod Organizer. If not, see . #include "modinfodialogconflicts.h" #include "modinfodialogcategories.h" #include "modinfodialognexus.h" +#include "modinfodialogfiletree.h" #include #include @@ -193,7 +194,7 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo ui->tabWidget->setTabEnabled(TAB_CONFLICTS, false); ui->tabWidget->setTabEnabled(TAB_CATEGORIES, true); ui->tabWidget->setTabEnabled(TAB_NEXUS, false); - //ui->tabWidget->setTabEnabled(TAB_NOTES, false); + ui->tabWidget->setTabEnabled(TAB_NOTES, true); ui->tabWidget->setTabEnabled(TAB_FILETREE, false); } else if (unmanaged) @@ -205,8 +206,8 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo ui->tabWidget->setTabEnabled(TAB_CONFLICTS, true); ui->tabWidget->setTabEnabled(TAB_CATEGORIES, false); ui->tabWidget->setTabEnabled(TAB_NEXUS, false); - ui->tabWidget->setTabEnabled(TAB_FILETREE, false); ui->tabWidget->setTabEnabled(TAB_NOTES, false); + ui->tabWidget->setTabEnabled(TAB_FILETREE, false); } else { ui->tabWidget->setTabEnabled(TAB_TEXTFILES, true); ui->tabWidget->setTabEnabled(TAB_INIFILES, true); @@ -215,8 +216,8 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo ui->tabWidget->setTabEnabled(TAB_CONFLICTS, true); ui->tabWidget->setTabEnabled(TAB_CATEGORIES, true); ui->tabWidget->setTabEnabled(TAB_NEXUS, true); - - initFiletree(modInfo); + ui->tabWidget->setTabEnabled(TAB_NOTES, true); + ui->tabWidget->setTabEnabled(TAB_FILETREE, true); } // activate first enabled tab @@ -252,7 +253,7 @@ std::vector> ModInfoDialog::createTabs() { return createTabsImpl< TextFilesTab, IniFilesTab, ImagesTab, ESPsTab, - ConflictsTab, CategoriesTab, NexusTab, NotesTab>( + ConflictsTab, CategoriesTab, NexusTab, NotesTab, FileTreeTab>( *m_OrganizerCore, *m_PluginContainer, this, ui); } @@ -262,35 +263,6 @@ int ModInfoDialog::exec() return TutorableDialog::exec(); } -void ModInfoDialog::initFiletree(ModInfo::Ptr modInfo) -{ - ui->fileTree = findChild("fileTree"); - - m_FileSystemModel = new QFileSystemModel(this); - m_FileSystemModel->setReadOnly(false); - m_FileSystemModel->setRootPath(m_RootPath); - ui->fileTree->setModel(m_FileSystemModel); - ui->fileTree->setRootIndex(m_FileSystemModel->index(m_RootPath)); - ui->fileTree->setColumnWidth(0, 300); - - m_NewFolderAction = new QAction(tr("&New Folder"), ui->fileTree); - m_OpenAction = new QAction(tr("&Open"), ui->fileTree); - m_PreviewAction = new QAction(tr("&Preview"), ui->fileTree); - m_RenameAction = new QAction(tr("&Rename"), ui->fileTree); - m_DeleteAction = new QAction(tr("&Delete"), ui->fileTree); - m_HideAction = new QAction(tr("&Hide"), ui->fileTree); - m_UnhideAction = new QAction(tr("&Unhide"), ui->fileTree); - - connect(m_NewFolderAction, SIGNAL(triggered()), this, SLOT(createDirectoryTriggered())); - connect(m_OpenAction, SIGNAL(triggered()), this, SLOT(openTriggered())); - connect(m_PreviewAction, SIGNAL(triggered()), this, SLOT(previewTriggered())); - connect(m_RenameAction, SIGNAL(triggered()), this, SLOT(renameTriggered())); - connect(m_DeleteAction, SIGNAL(triggered()), this, SLOT(deleteTriggered())); - connect(m_HideAction, SIGNAL(triggered()), this, SLOT(hideTriggered())); - connect(m_UnhideAction, SIGNAL(triggered()), this, SLOT(unhideTriggered())); -} - - int ModInfoDialog::tabIndex(const QString &tabId) { for (int i = 0; i < ui->tabWidget->count(); ++i) { @@ -301,7 +273,6 @@ int ModInfoDialog::tabIndex(const QString &tabId) return -1; } - void ModInfoDialog::saveState(Settings& s) const { s.directInterface().setValue("mod_info_tabs", saveTabState()); @@ -340,6 +311,7 @@ void ModInfoDialog::restoreTabState(const QByteArray &state) m_RealTabPos[newPos] = newPos; } } + // then actually move the tabs QTabBar *tabBar = ui->tabWidget->findChild("qt_tabwidget_tabbar"); // magic name = bad ui->tabWidget->blockSignals(true); @@ -407,377 +379,10 @@ void ModInfoDialog::openTab(int tab) } } -QString ModInfoDialog::getFileCategory(int categoryID) -{ - switch (categoryID) { - case 1: return tr("Main"); - case 2: return tr("Update"); - case 3: return tr("Optional"); - case 4: return tr("Old"); - case 5: return tr("Miscellaneous"); - case 6: return tr("Deleted"); - default: return tr("Unknown"); - } -} - void ModInfoDialog::on_tabWidget_currentChanged(int index) { } -bool ModInfoDialog::recursiveDelete(const QModelIndex &index) -{ - for (int childRow = 0; childRow < m_FileSystemModel->rowCount(index); ++childRow) { - QModelIndex childIndex = m_FileSystemModel->index(childRow, 0, index); - if (m_FileSystemModel->isDir(childIndex)) { - if (!recursiveDelete(childIndex)) { - qCritical("failed to delete %s", m_FileSystemModel->fileName(childIndex).toUtf8().constData()); - return false; - } - } else { - if (!m_FileSystemModel->remove(childIndex)) { - qCritical("failed to delete %s", m_FileSystemModel->fileName(childIndex).toUtf8().constData()); - return false; - } - } - } - if (!m_FileSystemModel->remove(index)) { - qCritical("failed to delete %s", m_FileSystemModel->fileName(index).toUtf8().constData()); - return false; - } - return true; -} - - -void ModInfoDialog::on_openInExplorerButton_clicked() -{ - shell::ExploreFile(m_ModInfo->absolutePath()); -} - -void ModInfoDialog::deleteFile(const QModelIndex &index) -{ - bool res = m_FileSystemModel->isDir(index) ? recursiveDelete(index) - : m_FileSystemModel->remove(index); - if (!res) { - QString fileName = m_FileSystemModel->fileName(index); - reportError(tr("Failed to delete %1").arg(fileName)); - } -} - -void ModInfoDialog::delete_activated() -{ - if (ui->fileTree->hasFocus()) { - QItemSelectionModel *selection = ui->fileTree->selectionModel(); - - if (selection->hasSelection() && selection->selectedRows().count() >= 1) { - - if (selection->selectedRows().count() == 0) { - return; - } - else if (selection->selectedRows().count() == 1) { - QString fileName = m_FileSystemModel->fileName(selection->selectedRows().at(0)); - if (QMessageBox::question(this, tr("Confirm"), tr("Are you sure you want to delete \"%1\"?").arg(fileName), - QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) { - return; - } - } - else { - if (QMessageBox::question(this, tr("Confirm"), tr("Are you sure you want to delete the selected files?"), - QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) { - return; - } - } - - foreach(QModelIndex index, selection->selectedRows()) { - deleteFile(index); - } - } - } -} - -void ModInfoDialog::deleteTriggered() -{ - if (m_FileSelection.count() == 0) { - return; - } else if (m_FileSelection.count() == 1) { - QString fileName = m_FileSystemModel->fileName(m_FileSelection.at(0)); - if (QMessageBox::question(this, tr("Confirm"), tr("Are you sure you want to delete \"%1\"?").arg(fileName), - QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) { - return; - } - } else { - if (QMessageBox::question(this, tr("Confirm"), tr("Are you sure you want to delete the selected files?"), - QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) { - return; - } - } - - foreach(QModelIndex index, m_FileSelection) { - deleteFile(index); - } -} - - -void ModInfoDialog::renameTriggered() -{ - QModelIndex selection = m_FileSelection.at(0); - QModelIndex index = selection.sibling(selection.row(), 0); - if (!index.isValid() || m_FileSystemModel->isReadOnly()) { - return; - } - - ui->fileTree->edit(index); -} - - -void ModInfoDialog::hideTriggered() -{ - changeFiletreeVisibility(false); -} - - -void ModInfoDialog::unhideTriggered() -{ - changeFiletreeVisibility(true); -} - -void ModInfoDialog::changeFiletreeVisibility(bool visible) -{ - bool changed = false; - bool stop = false; - - qDebug().nospace() - << (visible ? "unhiding" : "hiding") << " " - << m_FileSelection.size() << " filetree files"; - - QFlags flags = - (visible ? FileRenamer::UNHIDE : FileRenamer::HIDE); - - if (m_FileSelection.size() > 1) { - flags |= FileRenamer::MULTIPLE; - } - - FileRenamer renamer(this, flags); - - for (const auto& index : m_FileSelection) { - if (stop) { - break; - } - - const QString path = m_FileSystemModel->filePath(index); - auto result = FileRenamer::RESULT_CANCEL; - - if (visible) { - if (!canUnhideFile(false, path)) { - qDebug().nospace() << "cannot unhide " << path << ", skipping"; - continue; - } - result = unhideFile(renamer, path); - } else { - if (!canHideFile(false, path)) { - qDebug().nospace() << "cannot hide " << path << ", skipping"; - continue; - } - result = hideFile(renamer, path); - } - - switch (result) { - case FileRenamer::RESULT_OK: { - // will trigger a refresh at the end - changed = true; - break; - } - - case FileRenamer::RESULT_SKIP: { - // nop - break; - } - - case FileRenamer::RESULT_CANCEL: { - // stop right now, but make sure to refresh if needed - stop = true; - break; - } - } - } - - qDebug().nospace() << (visible ? "unhiding" : "hiding") << " filetree files done"; - - if (changed) { - qDebug().nospace() << "triggering refresh"; - if (m_Origin) { - emit originModified(m_Origin->getID()); - } - refreshLists(); - } -} - - -void ModInfoDialog::openTriggered() -{ - if (m_FileSelection.size() == 1) { - const auto index = m_FileSelection.at(0); - if (!index.isValid()) { - return; - } - - QString fileName = m_FileSystemModel->filePath(index); - shell::OpenFile(fileName); - } -} - -void ModInfoDialog::previewTriggered() -{ - if (m_FileSelection.size() == 1) { - const auto index = m_FileSelection.at(0); - if (!index.isValid()) { - return; - } - - QString fileName = m_FileSystemModel->filePath(index); - m_OrganizerCore->previewFile(this, m_ModInfo->name(), fileName); - } -} - -void ModInfoDialog::createDirectoryTriggered() -{ - QModelIndex selection = m_FileSelection.at(0); - - QModelIndex index = m_FileSystemModel->isDir(selection) ? selection - : selection.parent(); - index = index.sibling(index.row(), 0); - - QString name = tr("New Folder"); - QString path = m_FileSystemModel->filePath(index).append("/"); - - QModelIndex existingIndex = m_FileSystemModel->index(path + name); - int suffix = 1; - while (existingIndex.isValid()) { - name = tr("New Folder") + QString::number(suffix++); - existingIndex = m_FileSystemModel->index(path + name); - } - - QModelIndex newIndex = m_FileSystemModel->mkdir(index, name); - if (!newIndex.isValid()) { - reportError(tr("Failed to create \"%1\"").arg(name)); - return; - } - - ui->fileTree->setCurrentIndex(newIndex); - ui->fileTree->edit(newIndex); -} - - -void ModInfoDialog::on_fileTree_customContextMenuRequested(const QPoint &pos) -{ - QItemSelectionModel *selectionModel = ui->fileTree->selectionModel(); - m_FileSelection = selectionModel->selectedRows(0); - - QMenu menu(ui->fileTree); - - menu.addAction(m_NewFolderAction); - - if (selectionModel->hasSelection()) { - bool enableOpen = true; - bool enablePreview = true; - bool enableRename = true; - bool enableDelete = true; - bool enableHide = true; - bool enableUnhide = true; - - if (m_FileSelection.size() == 1) { - // single selection - - // only enable open action if a file is selected - bool hasFiles = false; - - foreach(QModelIndex idx, m_FileSelection) { - if (m_FileSystemModel->fileInfo(idx).isFile()) { - hasFiles = true; - break; - } - } - - if (!hasFiles) { - enableOpen = false; - enablePreview = false; - } - - const QString fileName = m_FileSystemModel->fileName(m_FileSelection.at(0)); - - if (!canPreviewFile(*m_PluginContainer, false, fileName)) { - enablePreview = false; - } - - if (!canHideFile(false, fileName)) { - enableHide = false; - } - - if (!canUnhideFile(false, fileName)) { - enableUnhide = false; - } - } else { - // this is a multiple selection, don't show open action so users don't open - // a thousand files - enableOpen = false; - enablePreview = false; - enableRename = false; - - if (m_FileSelection.size() < max_scan_for_context_menu) { - // if the number of selected items is low, checking them to accurately - // show the menu items is worth it - enableHide = false; - enableUnhide = false; - - for (const auto& index : m_FileSelection) { - const QString fileName = m_FileSystemModel->fileName(index); - - if (canHideFile(false, fileName)) { - enableHide = true; - } - - if (canUnhideFile(false, fileName)) { - enableUnhide = true; - } - - if (enableHide && enableUnhide) { - // found both, no need to check more - break; - } - } - } - } - - if (enableOpen) { - menu.addAction(m_OpenAction); - } - - if (enablePreview) { - menu.addAction(m_PreviewAction); - } - - if (enableRename) { - menu.addAction(m_RenameAction); - } - - if (enableDelete) { - menu.addAction(m_DeleteAction); - } - - if (enableHide) { - menu.addAction(m_HideAction); - } - - if (enableUnhide) { - menu.addAction(m_UnhideAction); - } - } else { - m_FileSelection.clear(); - m_FileSelection.append(m_FileSystemModel->index(m_FileSystemModel->rootPath(), 0)); - } - - menu.exec(ui->fileTree->viewport()->mapToGlobal(pos)); -} - void ModInfoDialog::on_nextButton_clicked() { int currentTab = ui->tabWidget->currentIndex(); diff --git a/src/modinfodialog.h b/src/modinfodialog.h index 7b96d21a..b367f647 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -54,19 +54,6 @@ class TextEditor; class ModInfoDialogTab; -class ElideLeftDelegate : public QStyledItemDelegate -{ -public: - using QStyledItemDelegate::QStyledItemDelegate; - -protected: - void initStyleOption(QStyleOptionViewItem* o, const QModelIndex& i) const - { - QStyledItemDelegate::initStyleOption(o, i); - o->textElideMode = Qt::ElideLeft; - } -}; - bool canPreviewFile(PluginContainer& pluginContainer, bool isArchive, const QString& filename); bool canOpenFile(bool isArchive, const QString& filename); @@ -145,12 +132,6 @@ signals: void originModified(int originID); private: - - void initFiletree(ModInfo::Ptr modInfo); - - void refreshLists(); - - QString getFileCategory(int categoryID); bool recursiveDelete(const QModelIndex &index); void deleteFile(const QModelIndex &index); @@ -189,17 +170,9 @@ private: OrganizerCore *m_OrganizerCore; PluginContainer *m_PluginContainer; - QFileSystemModel *m_FileSystemModel; QTreeView *m_FileTree; QModelIndexList m_FileSelection; - QAction *m_NewFolderAction; - QAction *m_OpenAction; - QAction *m_PreviewAction; - QAction *m_RenameAction; - QAction *m_DeleteAction; - QAction *m_HideAction; - QAction *m_UnhideAction; const MOShared::DirectoryEntry *m_Directory; MOShared::FilesOrigin *m_Origin; @@ -209,6 +182,7 @@ private: std::vector> createTabs(); + void refreshLists(); void refreshFiles(); void restoreTabState(const QByteArray &state); diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui index 360ecc79..04282e81 100644 --- a/src/modinfodialog.ui +++ b/src/modinfodialog.ui @@ -20,7 +20,7 @@ QTabWidget::Rounded - 7 + 8 true @@ -1088,7 +1088,7 @@ p, li { white-space: pre-wrap; }
- + Filetree @@ -1096,7 +1096,7 @@ p, li { white-space: pre-wrap; } - + 0 @@ -1124,7 +1124,7 @@ p, li { white-space: pre-wrap; } - + Qt::CustomContextMenu diff --git a/src/modinfodialogconflicts.cpp b/src/modinfodialogconflicts.cpp index 4176ac3e..e7f189c2 100644 --- a/src/modinfodialogconflicts.cpp +++ b/src/modinfodialogconflicts.cpp @@ -24,6 +24,20 @@ int naturalCompare(const QString& a, const QString& b) } +class ElideLeftDelegate : public QStyledItemDelegate +{ +public: + using QStyledItemDelegate::QStyledItemDelegate; + +protected: + void initStyleOption(QStyleOptionViewItem* o, const QModelIndex& i) const + { + QStyledItemDelegate::initStyleOption(o, i); + o->textElideMode = Qt::ElideLeft; + } +}; + + class ConflictItem : public QTreeWidgetItem { public: diff --git a/src/modinfodialogconflicts.h b/src/modinfodialogconflicts.h index 20362575..9c011163 100644 --- a/src/modinfodialogconflicts.h +++ b/src/modinfodialogconflicts.h @@ -115,18 +115,12 @@ public: private: struct Actions { - QAction* hide; - QAction* unhide; - QAction* open; - QAction* preview; - QMenu* gotoMenu; + QAction* hide = nullptr; + QAction* unhide = nullptr; + QAction* open = nullptr; + QAction* preview = nullptr; + QMenu* gotoMenu = nullptr; std::vector gotoActions; - - Actions() : - hide(nullptr), unhide(nullptr), open(nullptr), preview(nullptr), - gotoMenu(nullptr) - { - } }; GeneralConflictsTab m_general; diff --git a/src/modinfodialogfiletree.cpp b/src/modinfodialogfiletree.cpp new file mode 100644 index 00000000..e277b04b --- /dev/null +++ b/src/modinfodialogfiletree.cpp @@ -0,0 +1,420 @@ +#include "modinfodialogfiletree.h" +#include "ui_modinfodialog.h" +#include "organizercore.h" +#include +#include + +using MOBase::reportError; +namespace shell = MOBase::shell; + +FileTreeTab::FileTreeTab( + OrganizerCore& oc, PluginContainer& plugin, + QWidget* parent, Ui::ModInfoDialog* ui) + : ModInfoDialogTab(oc, plugin, parent, ui), m_fs(nullptr) +{ + m_fs = new QFileSystemModel(this); + m_fs->setReadOnly(false); + ui->fileTree1->setModel(m_fs); + ui->fileTree1->setColumnWidth(0, 300); + + m_actions.newFolder = new QAction(tr("&New Folder"), ui->fileTree1); + m_actions.open = new QAction(tr("&Open"), ui->fileTree1); + m_actions.preview = new QAction(tr("&Preview"), ui->fileTree1); + m_actions.rename = new QAction(tr("&Rename"), ui->fileTree1); + m_actions.del = new QAction(tr("&Delete"), ui->fileTree1); + m_actions.hide = new QAction(tr("&Hide"), ui->fileTree1); + m_actions.unhide = new QAction(tr("&Unhide"), ui->fileTree1); + + connect(m_actions.newFolder, &QAction::triggered, [&]{ onCreateDirectory(); }); + connect(m_actions.open, &QAction::triggered, [&]{ onOpen(); }); + connect(m_actions.preview, &QAction::triggered, [&]{ onPreview(); }); + connect(m_actions.rename, &QAction::triggered, [&]{ onRename(); }); + connect(m_actions.del, &QAction::triggered, [&]{ onDelete(); }); + connect(m_actions.hide, &QAction::triggered, [&]{ onHide(); }); + connect(m_actions.unhide, &QAction::triggered, [&]{ onUnhide(); }); +} + +void FileTreeTab::clear() +{ + m_fs->setRootPath({}); + //ui->fileTree1-> +} + +void FileTreeTab::update() +{ + const auto rootPath = mod()->absolutePath(); + + m_fs->setRootPath(rootPath); + ui->fileTree1->setRootIndex(m_fs->index(rootPath)); +} + +QModelIndex FileTreeTab::singleSelection() const +{ + const auto rows = ui->fileTree1->selectionModel()->selectedRows(); + if (rows.size() != 1) { + return {}; + } + + return rows[0]; +} + +void FileTreeTab::onCreateDirectory() +{ + auto selection = singleSelection(); + if (!selection.isValid()) { + return; + } + + QModelIndex index = m_fs->isDir(selection) ? selection : selection.parent(); + index = index.sibling(index.row(), 0); + + QString name = tr("New Folder"); + QString path = m_fs->filePath(index).append("/"); + + QModelIndex existingIndex = m_fs->index(path + name); + int suffix = 1; + while (existingIndex.isValid()) { + name = tr("New Folder") + QString::number(suffix++); + existingIndex = m_fs->index(path + name); + } + + QModelIndex newIndex = m_fs->mkdir(index, name); + if (!newIndex.isValid()) { + reportError(tr("Failed to create \"%1\"").arg(name)); + return; + } + + ui->fileTree1->setCurrentIndex(newIndex); + ui->fileTree1->edit(newIndex); +} + +void FileTreeTab::onOpen() +{ + auto selection = singleSelection(); + if (!selection.isValid()) { + return; + } + + shell::OpenFile(m_fs->filePath(selection)); +} + +void FileTreeTab::onPreview() +{ + auto selection = singleSelection(); + if (!selection.isValid()) { + return; + } + + core().previewFile(parentWidget(), mod()->name(), m_fs->filePath(selection)); +} + +void FileTreeTab::onRename() +{ + auto selection = singleSelection(); + if (!selection.isValid()) { + return; + } + + QModelIndex index = selection.sibling(selection.row(), 0); + if (!index.isValid() || m_fs->isReadOnly()) { + return; + } + + ui->fileTree1->edit(index); +} + +void FileTreeTab::onDelete() +{ + const auto rows = ui->fileTree1->selectionModel()->selectedRows(); + if (rows.count() == 0) { + return; + } + + QString message; + + if (rows.count() == 1) { + QString fileName = m_fs->fileName(rows[0]); + message = tr("Are sure you want to delete \"%1\"?").arg(fileName); + } else { + message = tr("Are sure you want to delete the selected files?"); + } + + if (QMessageBox::question(parentWidget(), tr("Confirm"), message) != QMessageBox::Yes) { + return; + } + + foreach(QModelIndex index, m_FileSelection) { + deleteFile(index); + } +} + + +bool FileTreeTab::recursiveDelete(const QModelIndex &index) +{ + for (int childRow = 0; childRow < m_FileSystemModel->rowCount(index); ++childRow) { + QModelIndex childIndex = m_FileSystemModel->index(childRow, 0, index); + if (m_FileSystemModel->isDir(childIndex)) { + if (!recursiveDelete(childIndex)) { + qCritical("failed to delete %s", m_FileSystemModel->fileName(childIndex).toUtf8().constData()); + return false; + } + } else { + if (!m_FileSystemModel->remove(childIndex)) { + qCritical("failed to delete %s", m_FileSystemModel->fileName(childIndex).toUtf8().constData()); + return false; + } + } + } + if (!m_FileSystemModel->remove(index)) { + qCritical("failed to delete %s", m_FileSystemModel->fileName(index).toUtf8().constData()); + return false; + } + return true; +} + + +void ModInfoDialog::on_openInExplorerButton_clicked() +{ + shell::ExploreFile(m_ModInfo->absolutePath()); +} + +void ModInfoDialog::deleteFile(const QModelIndex &index) +{ + bool res = m_FileSystemModel->isDir(index) ? recursiveDelete(index) + : m_FileSystemModel->remove(index); + if (!res) { + QString fileName = m_FileSystemModel->fileName(index); + reportError(tr("Failed to delete %1").arg(fileName)); + } +} + +void ModInfoDialog::delete_activated() +{ + if (ui->fileTree->hasFocus()) { + QItemSelectionModel *selection = ui->fileTree->selectionModel(); + + if (selection->hasSelection() && selection->selectedRows().count() >= 1) { + + if (selection->selectedRows().count() == 0) { + return; + } + else if (selection->selectedRows().count() == 1) { + QString fileName = m_FileSystemModel->fileName(selection->selectedRows().at(0)); + if (QMessageBox::question(this, tr("Confirm"), tr("Are sure you want to delete \"%1\"?").arg(fileName), + QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) { + return; + } + } + else { + if (QMessageBox::question(this, tr("Confirm"), tr("Are sure you want to delete the selected files?"), + QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) { + return; + } + } + + foreach(QModelIndex index, selection->selectedRows()) { + deleteFile(index); + } + } + } +} + + + + + +void ModInfoDialog::hideTriggered() +{ + changeFiletreeVisibility(false); +} + + +void ModInfoDialog::unhideTriggered() +{ + changeFiletreeVisibility(true); +} + +void ModInfoDialog::changeFiletreeVisibility(bool visible) +{ + bool changed = false; + bool stop = false; + + qDebug().nospace() + << (visible ? "unhiding" : "hiding") << " " + << m_FileSelection.size() << " filetree files"; + + QFlags flags = + (visible ? FileRenamer::UNHIDE : FileRenamer::HIDE); + + if (m_FileSelection.size() > 1) { + flags |= FileRenamer::MULTIPLE; + } + + FileRenamer renamer(this, flags); + + for (const auto& index : m_FileSelection) { + if (stop) { + break; + } + + const QString path = m_FileSystemModel->filePath(index); + auto result = FileRenamer::RESULT_CANCEL; + + if (visible) { + if (!canUnhideFile(false, path)) { + qDebug().nospace() << "cannot unhide " << path << ", skipping"; + continue; + } + result = unhideFile(renamer, path); + } else { + if (!canHideFile(false, path)) { + qDebug().nospace() << "cannot hide " << path << ", skipping"; + continue; + } + result = hideFile(renamer, path); + } + + switch (result) { + case FileRenamer::RESULT_OK: { + // will trigger a refresh at the end + changed = true; + break; + } + + case FileRenamer::RESULT_SKIP: { + // nop + break; + } + + case FileRenamer::RESULT_CANCEL: { + // stop right now, but make sure to refresh if needed + stop = true; + break; + } + } + } + + qDebug().nospace() << (visible ? "unhiding" : "hiding") << " filetree files done"; + + if (changed) { + qDebug().nospace() << "triggering refresh"; + if (m_Origin) { + emit originModified(m_Origin->getID()); + } + refreshLists(); + } +} + + + + +void ModInfoDialog::on_fileTree_customContextMenuRequested(const QPoint &pos) +{ + QItemSelectionModel *selectionModel = ui->fileTree->selectionModel(); + m_FileSelection = selectionModel->selectedRows(0); + + QMenu menu(ui->fileTree); + + menu.addAction(m_NewFolderAction); + + if (selectionModel->hasSelection()) { + bool enableOpen = true; + bool enablePreview = true; + bool enableRename = true; + bool enableDelete = true; + bool enableHide = true; + bool enableUnhide = true; + + if (m_FileSelection.size() == 1) { + // single selection + + // only enable open action if a file is selected + bool hasFiles = false; + + foreach(QModelIndex idx, m_FileSelection) { + if (m_FileSystemModel->fileInfo(idx).isFile()) { + hasFiles = true; + break; + } + } + + if (!hasFiles) { + enableOpen = false; + enablePreview = false; + } + + const QString fileName = m_FileSystemModel->fileName(m_FileSelection.at(0)); + + if (!canPreviewFile(*m_PluginContainer, false, fileName)) { + enablePreview = false; + } + + if (!canHideFile(false, fileName)) { + enableHide = false; + } + + if (!canUnhideFile(false, fileName)) { + enableUnhide = false; + } + } else { + // this is a multiple selection, don't show open action so users don't open + // a thousand files + enableOpen = false; + enablePreview = false; + enableRename = false; + + if (m_FileSelection.size() < max_scan_for_context_menu) { + // if the number of selected items is low, checking them to accurately + // show the menu items is worth it + enableHide = false; + enableUnhide = false; + + for (const auto& index : m_FileSelection) { + const QString fileName = m_FileSystemModel->fileName(index); + + if (canHideFile(false, fileName)) { + enableHide = true; + } + + if (canUnhideFile(false, fileName)) { + enableUnhide = true; + } + + if (enableHide && enableUnhide) { + // found both, no need to check more + break; + } + } + } + } + + if (enableOpen) { + menu.addAction(m_OpenAction); + } + + if (enablePreview) { + menu.addAction(m_PreviewAction); + } + + if (enableRename) { + menu.addAction(m_RenameAction); + } + + if (enableDelete) { + menu.addAction(m_DeleteAction); + } + + if (enableHide) { + menu.addAction(m_HideAction); + } + + if (enableUnhide) { + menu.addAction(m_UnhideAction); + } + } else { + m_FileSelection.clear(); + m_FileSelection.append(m_FileSystemModel->index(m_FileSystemModel->rootPath(), 0)); + } + + menu.exec(ui->fileTree->viewport()->mapToGlobal(pos)); +} diff --git a/src/modinfodialogfiletree.h b/src/modinfodialogfiletree.h new file mode 100644 index 00000000..e7708ab8 --- /dev/null +++ b/src/modinfodialogfiletree.h @@ -0,0 +1,39 @@ +#ifndef MODINFODIALOGFILETREE_H +#define MODINFODIALOGFILETREE_H + +#include "modinfodialogtab.h" + +class FileTreeTab : public ModInfoDialogTab +{ +public: + FileTreeTab( + OrganizerCore& oc, PluginContainer& plugin, + QWidget* parent, Ui::ModInfoDialog* ui); + + void clear() override; + void update() override; + +private: + struct Actions + { + QAction *newFolder = nullptr; + QAction *open = nullptr; + QAction *preview = nullptr; + QAction *rename = nullptr; + QAction *del = nullptr; + QAction *hide = nullptr; + QAction *unhide = nullptr; + }; + + QFileSystemModel* m_fs; + Actions m_actions; + + QModelIndex singleSelection() const; + void onCreateDirectory(); + void onOpen(); + void onPreview(); + void onRename(); + void onDelete(); +}; + +#endif // MODINFODIALOGFILETREE_H -- cgit v1.3.1 From 3edad124635291b5d07794ad088ff8840391257f Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sun, 23 Jun 2019 06:00:17 -0400 Subject: finished splitting filetree tab forward delete shortcut to tabs --- src/mainwindow.cpp | 45 ++++---- src/modinfodialog.cpp | 32 +++--- src/modinfodialog.h | 42 +------- src/modinfodialog.ui | 13 ++- src/modinfodialogconflicts.cpp | 6 +- src/modinfodialogfiletree.cpp | 232 ++++++++++++++++++++--------------------- src/modinfodialogfiletree.h | 11 +- src/modinfodialogtab.cpp | 15 ++- src/modinfodialogtab.h | 3 +- 9 files changed, 197 insertions(+), 202 deletions(-) (limited to 'src/modinfodialogconflicts.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 8eb0838e..f9bfaafb 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3222,34 +3222,37 @@ void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index, } } else { modInfo->saveMeta(); - ModInfoDialog dialog(modInfo, m_OrganizerCore.directoryStructure(), modInfo->hasFlag(ModInfo::FLAG_FOREIGN), &m_OrganizerCore, &m_PluginContainer, this); - connect(&dialog, SIGNAL(downloadRequest(QString)), &m_OrganizerCore, SLOT(downloadRequestedNXM(QString))); + + ModInfoDialog dialog( + modInfo, modInfo->hasFlag(ModInfo::FLAG_FOREIGN), + &m_OrganizerCore, &m_PluginContainer, this); + connect(&dialog, SIGNAL(modOpen(QString, int)), this, SLOT(displayModInformation(QString, int)), Qt::QueuedConnection); connect(&dialog, SIGNAL(modOpenNext(int)), this, SLOT(modOpenNext(int)), Qt::QueuedConnection); connect(&dialog, SIGNAL(modOpenPrev(int)), this, SLOT(modOpenPrev(int)), Qt::QueuedConnection); connect(&dialog, SIGNAL(originModified(int)), this, SLOT(originModified(int))); - //Open the tab first if we want to use the standard indexes of the tabs. - if (tab != -1) { - dialog.openTab(tab); - } + //Open the tab first if we want to use the standard indexes of the tabs. + if (tab != -1) { + dialog.openTab(tab); + } - dialog.restoreState(m_OrganizerCore.settings()); - QSettings &settings = m_OrganizerCore.settings().directInterface(); - QString key = QString("geometry/%1").arg(dialog.objectName()); - if (settings.contains(key)) { - dialog.restoreGeometry(settings.value(key).toByteArray()); - } + dialog.restoreState(m_OrganizerCore.settings()); + QSettings &settings = m_OrganizerCore.settings().directInterface(); + QString key = QString("geometry/%1").arg(dialog.objectName()); + if (settings.contains(key)) { + dialog.restoreGeometry(settings.value(key).toByteArray()); + } - //If no tab was specified use the first tab from the left based on the user order. - if (tab == -1) { - for (int i = 0; i < dialog.findChild("tabWidget")->count(); ++i) { - if (dialog.findChild("tabWidget")->isTabEnabled(i)) { - dialog.findChild("tabWidget")->setCurrentIndex(i); - break; - } - } - } + //If no tab was specified use the first tab from the left based on the user order. + if (tab == -1) { + for (int i = 0; i < dialog.findChild("tabWidget")->count(); ++i) { + if (dialog.findChild("tabWidget")->isTabEnabled(i)) { + dialog.findChild("tabWidget")->setCurrentIndex(i); + break; + } + } + } dialog.exec(); dialog.saveState(m_OrganizerCore.settings()); diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 6463a5a6..71e514b2 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -144,12 +144,9 @@ FileRenamer::RenameResults unhideFile(FileRenamer& renamer, const QString &oldNa } -ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directory, bool unmanaged, OrganizerCore *organizerCore, PluginContainer *pluginContainer, QWidget *parent) +ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, bool unmanaged, OrganizerCore *organizerCore, PluginContainer *pluginContainer, QWidget *parent) : TutorableDialog("ModInfoDialog", parent), ui(new Ui::ModInfoDialog), m_ModInfo(modInfo), - m_NewFolderAction(nullptr), m_OpenAction(nullptr), m_PreviewAction(nullptr), - m_RenameAction(nullptr), m_DeleteAction(nullptr), m_HideAction(nullptr), - m_UnhideAction(nullptr), m_Directory(directory), m_Origin(nullptr), - m_OrganizerCore(organizerCore), m_PluginContainer(pluginContainer) + m_Origin(nullptr), m_OrganizerCore(organizerCore), m_PluginContainer(pluginContainer) { ui->setupUi(this); @@ -173,13 +170,12 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo m_RootPath = modInfo->absolutePath(); - //TODO: No easy way to delegate links - //ui->descriptionView->page()->acceptNavigationRequest(QWebEnginePage::DelegateAllLinks); + auto* sc = new QShortcut(QKeySequence::Delete, this); + connect(sc, &QShortcut::activated, [&]{ onDeleteShortcut(); }); - new QShortcut(QKeySequence::Delete, this, SLOT(delete_activated())); - - if (directory->originExists(ToWString(modInfo->name()))) { - m_Origin = &directory->getOriginByName(ToWString(modInfo->name())); + auto* ds = m_OrganizerCore->directoryStructure(); + if (ds->originExists(ToWString(modInfo->name()))) { + m_Origin = &ds->getOriginByName(ToWString(modInfo->name())); if (m_Origin->isDisabled()) { m_Origin = nullptr; } @@ -335,17 +331,21 @@ QByteArray ModInfoDialog::saveTabState() const return result; } +void ModInfoDialog::onDeleteShortcut() +{ + for (auto& t : m_tabs) { + if (t->deleteRequested()) { + break; + } + } +} + void ModInfoDialog::refreshLists() { for (auto& tab : m_tabs) { tab->update(); } - refreshFiles(); -} - -void ModInfoDialog::refreshFiles() -{ if (m_RootPath.length() > 0) { QDirIterator dirIterator(m_RootPath, QDir::Files, QDirIterator::Subdirectories); while (dirIterator.hasNext()) { diff --git a/src/modinfodialog.h b/src/modinfodialog.h index b367f647..49007c87 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -73,7 +73,6 @@ class ModInfoDialog : public MOBase::TutorableDialog Q_OBJECT public: - enum ETabs { TAB_TEXTFILES, TAB_INIFILES, @@ -87,14 +86,16 @@ public: }; public: - /** * @brief constructor * * @param modInfo info structure about the mod to display * @param parent parend widget **/ - explicit ModInfoDialog(ModInfo::Ptr modInfo, const MOShared::DirectoryEntry *directory, bool unmanaged, OrganizerCore *organizerCore, PluginContainer *pluginContainer, QWidget *parent = 0); + explicit ModInfoDialog( + ModInfo::Ptr modInfo, + bool unmanaged, OrganizerCore *organizerCore, PluginContainer *pluginContainer, + QWidget *parent = 0); ~ModInfoDialog(); @@ -125,34 +126,17 @@ public: void restoreState(const Settings& s); signals: - void downloadRequest(const QString &link); void modOpen(const QString &modName, int tab); void modOpenNext(int tab=-1); void modOpenPrev(int tab=-1); void originModified(int originID); private: - bool recursiveDelete(const QModelIndex &index); - void deleteFile(const QModelIndex &index); - int tabIndex(const QString &tabId); private slots: - void delete_activated(); - - void createDirectoryTriggered(); - void openTriggered(); - void previewTriggered(); - void renameTriggered(); - void deleteTriggered(); - void hideTriggered(); - void unhideTriggered(); - - void on_openInExplorerButton_clicked(); void on_closeButton_clicked(); void on_tabWidget_currentChanged(int index); - void on_fileTree_customContextMenuRequested(const QPoint &pos); - void on_nextButton_clicked(); void on_prevButton_clicked(); @@ -160,35 +144,19 @@ private: using FileEntry = MOShared::FileEntry; Ui::ModInfoDialog *ui; - ModInfo::Ptr m_ModInfo; - std::vector> m_tabs; - QString m_RootPath; - OrganizerCore *m_OrganizerCore; PluginContainer *m_PluginContainer; - - QTreeView *m_FileTree; - QModelIndexList m_FileSelection; - - - const MOShared::DirectoryEntry *m_Directory; MOShared::FilesOrigin *m_Origin; - std::map m_RealTabPos; - std::vector> createTabs(); - void refreshLists(); - void refreshFiles(); - void restoreTabState(const QByteArray &state); QByteArray saveTabState() const; - - void changeFiletreeVisibility(bool visible); + void onDeleteShortcut(); }; #endif // MODINFODIALOG_H diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui index 04282e81..65b89621 100644 --- a/src/modinfodialog.ui +++ b/src/modinfodialog.ui @@ -1096,7 +1096,7 @@ p, li { white-space: pre-wrap; } - + 0 @@ -1124,7 +1124,7 @@ p, li { white-space: pre-wrap; } - + Qt::CustomContextMenu @@ -1161,6 +1161,9 @@ p, li { white-space: pre-wrap; } Previous + + false + @@ -1168,6 +1171,9 @@ p, li { white-space: pre-wrap; } Next + + false + @@ -1188,6 +1194,9 @@ p, li { white-space: pre-wrap; } Close + + false +
diff --git a/src/modinfodialogconflicts.cpp b/src/modinfodialogconflicts.cpp index e7f189c2..adde27ca 100644 --- a/src/modinfodialogconflicts.cpp +++ b/src/modinfodialogconflicts.cpp @@ -6,8 +6,8 @@ using namespace MOShared; using namespace MOBase; -// if there are more than 50 selected items in the conflict tree or filetree, -// don't bother checking whether menu items apply to them, just show all of them +// if there are more than 50 selected items in the conflict tree, don't bother +// checking whether menu items apply to them, just show all of them const int max_scan_for_context_menu = 50; @@ -248,7 +248,7 @@ void ConflictsTab::changeItemsVisibility( qDebug().nospace() << "triggering refresh"; if (origin()) { - emit originModified(origin()->getID()); + emitOriginModified(); } update(); diff --git a/src/modinfodialogfiletree.cpp b/src/modinfodialogfiletree.cpp index e277b04b..b73a9e24 100644 --- a/src/modinfodialogfiletree.cpp +++ b/src/modinfodialogfiletree.cpp @@ -1,12 +1,18 @@ #include "modinfodialogfiletree.h" #include "ui_modinfodialog.h" +#include "modinfodialog.h" #include "organizercore.h" +#include "filerenamer.h" #include #include using MOBase::reportError; namespace shell = MOBase::shell; +// if there are more than 50 selected items in the filetree, don't bother +// checking whether menu items apply to them, just show all of them +const int max_scan_for_context_menu = 50; + FileTreeTab::FileTreeTab( OrganizerCore& oc, PluginContainer& plugin, QWidget* parent, Ui::ModInfoDialog* ui) @@ -14,16 +20,16 @@ FileTreeTab::FileTreeTab( { m_fs = new QFileSystemModel(this); m_fs->setReadOnly(false); - ui->fileTree1->setModel(m_fs); - ui->fileTree1->setColumnWidth(0, 300); + ui->filetree->setModel(m_fs); + ui->filetree->setColumnWidth(0, 300); - m_actions.newFolder = new QAction(tr("&New Folder"), ui->fileTree1); - m_actions.open = new QAction(tr("&Open"), ui->fileTree1); - m_actions.preview = new QAction(tr("&Preview"), ui->fileTree1); - m_actions.rename = new QAction(tr("&Rename"), ui->fileTree1); - m_actions.del = new QAction(tr("&Delete"), ui->fileTree1); - m_actions.hide = new QAction(tr("&Hide"), ui->fileTree1); - m_actions.unhide = new QAction(tr("&Unhide"), ui->fileTree1); + m_actions.newFolder = new QAction(tr("&New Folder"), ui->filetree); + m_actions.open = new QAction(tr("&Open"), ui->filetree); + m_actions.preview = new QAction(tr("&Preview"), ui->filetree); + m_actions.rename = new QAction(tr("&Rename"), ui->filetree); + m_actions.del = new QAction(tr("&Delete"), ui->filetree); + m_actions.hide = new QAction(tr("&Hide"), ui->filetree); + m_actions.unhide = new QAction(tr("&Unhide"), ui->filetree); connect(m_actions.newFolder, &QAction::triggered, [&]{ onCreateDirectory(); }); connect(m_actions.open, &QAction::triggered, [&]{ onOpen(); }); @@ -32,12 +38,18 @@ FileTreeTab::FileTreeTab( connect(m_actions.del, &QAction::triggered, [&]{ onDelete(); }); connect(m_actions.hide, &QAction::triggered, [&]{ onHide(); }); connect(m_actions.unhide, &QAction::triggered, [&]{ onUnhide(); }); + + connect(ui->openInExplorer, &QToolButton::clicked, [&]{ onOpenInExplorer(); }); + + connect( + ui->filetree, &QTreeView::customContextMenuRequested, + [&](const QPoint& pos){ onContextMenu(pos); }); } void FileTreeTab::clear() { m_fs->setRootPath({}); - //ui->fileTree1-> + //ui->filetree-> } void FileTreeTab::update() @@ -45,12 +57,22 @@ void FileTreeTab::update() const auto rootPath = mod()->absolutePath(); m_fs->setRootPath(rootPath); - ui->fileTree1->setRootIndex(m_fs->index(rootPath)); + ui->filetree->setRootIndex(m_fs->index(rootPath)); +} + +bool FileTreeTab::deleteRequested() +{ + if (!ui->filetree->hasFocus()) { + return false; + } + + onDelete(); + return true; } QModelIndex FileTreeTab::singleSelection() const { - const auto rows = ui->fileTree1->selectionModel()->selectedRows(); + const auto rows = ui->filetree->selectionModel()->selectedRows(); if (rows.size() != 1) { return {}; } @@ -60,11 +82,19 @@ QModelIndex FileTreeTab::singleSelection() const void FileTreeTab::onCreateDirectory() { - auto selection = singleSelection(); - if (!selection.isValid()) { + const auto selectedRows = ui->filetree->selectionModel()->selectedRows(); + if (selectedRows.size() > 1) { return; } + QModelIndex selection; + + if (selectedRows.size() == 0) { + selection = m_fs->index(m_fs->rootPath(), 0); + } else { + selection = selectedRows[0]; + } + QModelIndex index = m_fs->isDir(selection) ? selection : selection.parent(); index = index.sibling(index.row(), 0); @@ -84,8 +114,8 @@ void FileTreeTab::onCreateDirectory() return; } - ui->fileTree1->setCurrentIndex(newIndex); - ui->fileTree1->edit(newIndex); + ui->filetree->setCurrentIndex(newIndex); + ui->filetree->edit(newIndex); } void FileTreeTab::onOpen() @@ -120,12 +150,12 @@ void FileTreeTab::onRename() return; } - ui->fileTree1->edit(index); + ui->filetree->edit(index); } void FileTreeTab::onDelete() { - const auto rows = ui->fileTree1->selectionModel()->selectedRows(); + const auto rows = ui->filetree->selectionModel()->selectedRows(); if (rows.count() == 0) { return; } @@ -143,121 +173,95 @@ void FileTreeTab::onDelete() return; } - foreach(QModelIndex index, m_FileSelection) { + for (const auto& index : rows) { deleteFile(index); } } - -bool FileTreeTab::recursiveDelete(const QModelIndex &index) +void FileTreeTab::onHide() { - for (int childRow = 0; childRow < m_FileSystemModel->rowCount(index); ++childRow) { - QModelIndex childIndex = m_FileSystemModel->index(childRow, 0, index); - if (m_FileSystemModel->isDir(childIndex)) { - if (!recursiveDelete(childIndex)) { - qCritical("failed to delete %s", m_FileSystemModel->fileName(childIndex).toUtf8().constData()); - return false; - } - } else { - if (!m_FileSystemModel->remove(childIndex)) { - qCritical("failed to delete %s", m_FileSystemModel->fileName(childIndex).toUtf8().constData()); - return false; - } - } - } - if (!m_FileSystemModel->remove(index)) { - qCritical("failed to delete %s", m_FileSystemModel->fileName(index).toUtf8().constData()); - return false; - } - return true; + changeVisibility(false); } +void FileTreeTab::onUnhide() +{ + changeVisibility(true); +} -void ModInfoDialog::on_openInExplorerButton_clicked() +void FileTreeTab::onOpenInExplorer() { - shell::ExploreFile(m_ModInfo->absolutePath()); + shell::ExploreFile(mod()->absolutePath()); } -void ModInfoDialog::deleteFile(const QModelIndex &index) +bool FileTreeTab::deleteFile(const QModelIndex& index) { - bool res = m_FileSystemModel->isDir(index) ? recursiveDelete(index) - : m_FileSystemModel->remove(index); + bool res = false; + + if (m_fs->isDir(index)) { + res = deleteFileRecursive(index); + } else { + res = m_fs->remove(index); + } + if (!res) { - QString fileName = m_FileSystemModel->fileName(index); - reportError(tr("Failed to delete %1").arg(fileName)); + reportError(tr("Failed to delete %1").arg(m_fs->fileName(index))); } + + return res; } -void ModInfoDialog::delete_activated() +bool FileTreeTab::deleteFileRecursive(const QModelIndex& parent) { - if (ui->fileTree->hasFocus()) { - QItemSelectionModel *selection = ui->fileTree->selectionModel(); - - if (selection->hasSelection() && selection->selectedRows().count() >= 1) { + for (int row = 0; rowrowCount(parent); ++row) { + QModelIndex index = m_fs->index(row, 0, parent); - if (selection->selectedRows().count() == 0) { - return; - } - else if (selection->selectedRows().count() == 1) { - QString fileName = m_FileSystemModel->fileName(selection->selectedRows().at(0)); - if (QMessageBox::question(this, tr("Confirm"), tr("Are sure you want to delete \"%1\"?").arg(fileName), - QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) { - return; - } - } - else { - if (QMessageBox::question(this, tr("Confirm"), tr("Are sure you want to delete the selected files?"), - QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) { - return; - } + if (m_fs->isDir(index)) { + if (!deleteFileRecursive(index)) { + qCritical() << "failed to delete" << m_fs->fileName(index); + return false; } - - foreach(QModelIndex index, selection->selectedRows()) { - deleteFile(index); + } else { + if (!m_fs->remove(index)) { + qCritical() << "failed to delete", m_fs->fileName(index); + return false; } } } -} - - - + if (!m_fs->remove(parent)) { + qCritical() << "failed to delete" << m_fs->fileName(parent); + return false; + } -void ModInfoDialog::hideTriggered() -{ - changeFiletreeVisibility(false); + return true; } - -void ModInfoDialog::unhideTriggered() +void FileTreeTab::changeVisibility(bool visible) { - changeFiletreeVisibility(true); -} + const auto selection = ui->filetree->selectionModel()->selectedRows(); -void ModInfoDialog::changeFiletreeVisibility(bool visible) -{ bool changed = false; bool stop = false; qDebug().nospace() << (visible ? "unhiding" : "hiding") << " " - << m_FileSelection.size() << " filetree files"; + << selection.size() << " filetree files"; QFlags flags = (visible ? FileRenamer::UNHIDE : FileRenamer::HIDE); - if (m_FileSelection.size() > 1) { + if (selection.size() > 1) { flags |= FileRenamer::MULTIPLE; } - FileRenamer renamer(this, flags); + FileRenamer renamer(parentWidget(), flags); - for (const auto& index : m_FileSelection) { + for (const auto& index : selection) { if (stop) { break; } - const QString path = m_FileSystemModel->filePath(index); + const QString path = m_fs->filePath(index); auto result = FileRenamer::RESULT_CANCEL; if (visible) { @@ -298,26 +302,21 @@ void ModInfoDialog::changeFiletreeVisibility(bool visible) if (changed) { qDebug().nospace() << "triggering refresh"; - if (m_Origin) { - emit originModified(m_Origin->getID()); + if (origin()) { + emitOriginModified(); } - refreshLists(); } } - - - -void ModInfoDialog::on_fileTree_customContextMenuRequested(const QPoint &pos) +void FileTreeTab::onContextMenu(const QPoint &pos) { - QItemSelectionModel *selectionModel = ui->fileTree->selectionModel(); - m_FileSelection = selectionModel->selectedRows(0); + const auto selection = ui->filetree->selectionModel()->selectedRows(); - QMenu menu(ui->fileTree); + QMenu menu(ui->filetree); - menu.addAction(m_NewFolderAction); + menu.addAction(m_actions.newFolder); - if (selectionModel->hasSelection()) { + if (selection.size() > 0) { bool enableOpen = true; bool enablePreview = true; bool enableRename = true; @@ -325,14 +324,14 @@ void ModInfoDialog::on_fileTree_customContextMenuRequested(const QPoint &pos) bool enableHide = true; bool enableUnhide = true; - if (m_FileSelection.size() == 1) { + if (selection.size() == 1) { // single selection // only enable open action if a file is selected bool hasFiles = false; - foreach(QModelIndex idx, m_FileSelection) { - if (m_FileSystemModel->fileInfo(idx).isFile()) { + for (auto index : selection) { + if (m_fs->fileInfo(index).isFile()) { hasFiles = true; break; } @@ -343,9 +342,9 @@ void ModInfoDialog::on_fileTree_customContextMenuRequested(const QPoint &pos) enablePreview = false; } - const QString fileName = m_FileSystemModel->fileName(m_FileSelection.at(0)); + const QString fileName = m_fs->fileName(selection[0]); - if (!canPreviewFile(*m_PluginContainer, false, fileName)) { + if (!canPreviewFile(plugin(), false, fileName)) { enablePreview = false; } @@ -363,14 +362,14 @@ void ModInfoDialog::on_fileTree_customContextMenuRequested(const QPoint &pos) enablePreview = false; enableRename = false; - if (m_FileSelection.size() < max_scan_for_context_menu) { + if (selection.size() < max_scan_for_context_menu) { // if the number of selected items is low, checking them to accurately // show the menu items is worth it enableHide = false; enableUnhide = false; - for (const auto& index : m_FileSelection) { - const QString fileName = m_FileSystemModel->fileName(index); + for (const auto& index : selection) { + const QString fileName = m_fs->fileName(index); if (canHideFile(false, fileName)) { enableHide = true; @@ -389,32 +388,29 @@ void ModInfoDialog::on_fileTree_customContextMenuRequested(const QPoint &pos) } if (enableOpen) { - menu.addAction(m_OpenAction); + menu.addAction(m_actions.open); } if (enablePreview) { - menu.addAction(m_PreviewAction); + menu.addAction(m_actions.preview); } if (enableRename) { - menu.addAction(m_RenameAction); + menu.addAction(m_actions.rename); } if (enableDelete) { - menu.addAction(m_DeleteAction); + menu.addAction(m_actions.del); } if (enableHide) { - menu.addAction(m_HideAction); + menu.addAction(m_actions.hide); } if (enableUnhide) { - menu.addAction(m_UnhideAction); + menu.addAction(m_actions.unhide); } - } else { - m_FileSelection.clear(); - m_FileSelection.append(m_FileSystemModel->index(m_FileSystemModel->rootPath(), 0)); } - menu.exec(ui->fileTree->viewport()->mapToGlobal(pos)); + menu.exec(ui->filetree->viewport()->mapToGlobal(pos)); } diff --git a/src/modinfodialogfiletree.h b/src/modinfodialogfiletree.h index e7708ab8..dcc096fe 100644 --- a/src/modinfodialogfiletree.h +++ b/src/modinfodialogfiletree.h @@ -12,6 +12,7 @@ public: void clear() override; void update() override; + bool deleteRequested() override; private: struct Actions @@ -28,12 +29,20 @@ private: QFileSystemModel* m_fs; Actions m_actions; - QModelIndex singleSelection() const; void onCreateDirectory(); void onOpen(); void onPreview(); void onRename(); void onDelete(); + void onHide(); + void onUnhide(); + void onOpenInExplorer(); + void onContextMenu(const QPoint &pos); + + QModelIndex singleSelection() const; + bool deleteFile(const QModelIndex& index); + bool deleteFileRecursive(const QModelIndex& index); + void changeVisibility(bool visible); }; #endif // MODINFODIALOGFILETREE_H diff --git a/src/modinfodialogtab.cpp b/src/modinfodialogtab.cpp index b59f4dcc..58745220 100644 --- a/src/modinfodialogtab.cpp +++ b/src/modinfodialogtab.cpp @@ -1,6 +1,7 @@ #include "modinfodialogtab.h" #include "ui_modinfodialog.h" #include "texteditor.h" +#include "directoryentry.h" ModInfoDialogTab::ModInfoDialogTab( OrganizerCore& oc, PluginContainer& plugin, @@ -9,6 +10,11 @@ ModInfoDialogTab::ModInfoDialogTab( { } +void ModInfoDialogTab::update() +{ + // no-op +} + bool ModInfoDialogTab::feedFile(const QString&, const QString&) { // no-op @@ -30,9 +36,10 @@ void ModInfoDialogTab::restoreState(const Settings& s) // no-op } -void ModInfoDialogTab::update() +bool ModInfoDialogTab::deleteRequested() { // no-op + return false; } void ModInfoDialogTab::setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin) @@ -66,9 +73,11 @@ QWidget* ModInfoDialogTab::parentWidget() return m_parent; } -void ModInfoDialogTab::emitOriginModified(int originID) +void ModInfoDialogTab::emitOriginModified() { - emit originModified(originID); + if (m_origin) { + emit originModified(m_origin->getID()); + } } void ModInfoDialogTab::emitModOpen(QString name) diff --git a/src/modinfodialogtab.h b/src/modinfodialogtab.h index 60371954..0dc977a8 100644 --- a/src/modinfodialogtab.h +++ b/src/modinfodialogtab.h @@ -27,6 +27,7 @@ public: virtual bool canClose(); virtual void saveState(Settings& s); virtual void restoreState(const Settings& s); + virtual bool deleteRequested(); virtual void setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin); @@ -49,7 +50,7 @@ protected: QWidget* parentWidget(); - void emitOriginModified(int originID); + void emitOriginModified(); void emitModOpen(QString name); private: -- cgit v1.3.1 From 949e451379d63fe4c6bff82a7a059c6792fbebb5 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sun, 23 Jun 2019 06:48:35 -0400 Subject: added missing icons now passing tab index to allow enabling/disabling depending on mod type modinfodialog cleanup --- src/modinfodialog.cpp | 173 +++++++++++---------------------- src/modinfodialog.h | 48 +++------ src/modinfodialog.ui | 5 +- src/modinfodialogcategories.cpp | 9 +- src/modinfodialogcategories.h | 3 +- src/modinfodialogconflicts.cpp | 11 ++- src/modinfodialogconflicts.h | 4 +- src/modinfodialogesps.cpp | 4 +- src/modinfodialogesps.h | 2 +- src/modinfodialogfiletree.cpp | 4 +- src/modinfodialogfiletree.h | 2 +- src/modinfodialogimages.cpp | 5 +- src/modinfodialogimages.h | 2 +- src/modinfodialognexus.cpp | 4 +- src/modinfodialognexus.h | 2 +- src/modinfodialogtab.cpp | 29 +++++- src/modinfodialogtab.h | 11 ++- src/modinfodialogtextfiles.cpp | 12 +-- src/modinfodialogtextfiles.h | 6 +- src/resources.qrc | 210 ++++++++++++++++++++-------------------- 20 files changed, 258 insertions(+), 288 deletions(-) (limited to 'src/modinfodialogconflicts.cpp') diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 71e514b2..be7d4aa4 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -19,24 +19,8 @@ along with Mod Organizer. If not, see . #include "modinfodialog.h" #include "ui_modinfodialog.h" -#include "mainwindow.h" - -#include "modidlineedit.h" -#include "iplugingame.h" -#include "nexusinterface.h" -#include "report.h" -#include "utility.h" -#include "messagedialog.h" -#include "bbcode.h" -#include "questionboxmemory.h" -#include "settings.h" -#include "categories.h" +#include "plugincontainer.h" #include "organizercore.h" -#include "pluginlistsortproxy.h" -#include "previewgenerator.h" -#include "previewdialog.h" -#include "texteditor.h" - #include "modinfodialogtextfiles.h" #include "modinfodialogimages.h" #include "modinfodialogesps.h" @@ -45,23 +29,6 @@ along with Mod Organizer. If not, see . #include "modinfodialognexus.h" #include "modinfodialogfiletree.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - - using namespace MOBase; using namespace MOShared; @@ -144,13 +111,32 @@ FileRenamer::RenameResults unhideFile(FileRenamer& renamer, const QString &oldNa } -ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, bool unmanaged, OrganizerCore *organizerCore, PluginContainer *pluginContainer, QWidget *parent) - : TutorableDialog("ModInfoDialog", parent), ui(new Ui::ModInfoDialog), m_ModInfo(modInfo), - m_Origin(nullptr), m_OrganizerCore(organizerCore), m_PluginContainer(pluginContainer) +ModInfoDialog::ModInfoDialog( + ModInfo::Ptr modInfo, bool unmanaged, OrganizerCore *organizerCore, + PluginContainer *pluginContainer, QWidget *parent) : + TutorableDialog("ModInfoDialog", parent), ui(new Ui::ModInfoDialog), + m_ModInfo(modInfo), m_RootPath(modInfo->absolutePath()), + m_OrganizerCore(organizerCore), m_PluginContainer(pluginContainer), + m_Origin(nullptr) { ui->setupUi(this); + auto* ds = m_OrganizerCore->directoryStructure(); + if (ds->originExists(ToWString(m_ModInfo->name()))) { + m_Origin = &ds->getOriginByName(ToWString(m_ModInfo->name())); + if (m_Origin->isDisabled()) { + m_Origin = nullptr; + } + } + + this->setWindowTitle(m_ModInfo->name()); + this->setWindowModality(Qt::WindowModal); + + auto* sc = new QShortcut(QKeySequence::Delete, this); + connect(sc, &QShortcut::activated, [&]{ onDeleteShortcut(); }); + m_tabs = createTabs(); + bool tabSelected = false; for (std::size_t i=0; i(i)); }); - } - this->setWindowTitle(modInfo->name()); - this->setWindowModality(Qt::WindowModal); + bool enabled = true; - m_RootPath = modInfo->absolutePath(); - - auto* sc = new QShortcut(QKeySequence::Delete, this); - connect(sc, &QShortcut::activated, [&]{ onDeleteShortcut(); }); - - auto* ds = m_OrganizerCore->directoryStructure(); - if (ds->originExists(ToWString(modInfo->name()))) { - m_Origin = &ds->getOriginByName(ToWString(modInfo->name())); - if (m_Origin->isDisabled()) { - m_Origin = nullptr; + if (unmanaged) { + enabled = m_tabs[i]->canHandleUnmanaged(); + } else if (m_ModInfo->hasFlag(ModInfo::FLAG_SEPARATOR)) { + enabled = m_tabs[i]->canHandleSeparators(); } - } - if (modInfo->hasFlag(ModInfo::FLAG_SEPARATOR)) - { - ui->tabWidget->setTabEnabled(TAB_TEXTFILES, false); - ui->tabWidget->setTabEnabled(TAB_INIFILES, false); - ui->tabWidget->setTabEnabled(TAB_IMAGES, false); - ui->tabWidget->setTabEnabled(TAB_ESPS, false); - ui->tabWidget->setTabEnabled(TAB_CONFLICTS, false); - ui->tabWidget->setTabEnabled(TAB_CATEGORIES, true); - ui->tabWidget->setTabEnabled(TAB_NEXUS, false); - ui->tabWidget->setTabEnabled(TAB_NOTES, true); - ui->tabWidget->setTabEnabled(TAB_FILETREE, false); - } - else if (unmanaged) - { - ui->tabWidget->setTabEnabled(TAB_TEXTFILES, false); - ui->tabWidget->setTabEnabled(TAB_INIFILES, false); - ui->tabWidget->setTabEnabled(TAB_IMAGES, false); - ui->tabWidget->setTabEnabled(TAB_ESPS, false); - ui->tabWidget->setTabEnabled(TAB_CONFLICTS, true); - ui->tabWidget->setTabEnabled(TAB_CATEGORIES, false); - ui->tabWidget->setTabEnabled(TAB_NEXUS, false); - ui->tabWidget->setTabEnabled(TAB_NOTES, false); - ui->tabWidget->setTabEnabled(TAB_FILETREE, false); - } else { - ui->tabWidget->setTabEnabled(TAB_TEXTFILES, true); - ui->tabWidget->setTabEnabled(TAB_INIFILES, true); - ui->tabWidget->setTabEnabled(TAB_IMAGES, true); - ui->tabWidget->setTabEnabled(TAB_ESPS, true); - ui->tabWidget->setTabEnabled(TAB_CONFLICTS, true); - ui->tabWidget->setTabEnabled(TAB_CATEGORIES, true); - ui->tabWidget->setTabEnabled(TAB_NEXUS, true); - ui->tabWidget->setTabEnabled(TAB_NOTES, true); - ui->tabWidget->setTabEnabled(TAB_FILETREE, true); - } + ui->tabWidget->setTabEnabled(static_cast(i), enabled); - // activate first enabled tab - for (int i = 0; i < ui->tabWidget->count(); ++i) { - if (ui->tabWidget->isTabEnabled(i)) { - ui->tabWidget->setCurrentIndex(i); - break; + if (!tabSelected && enabled) { + ui->tabWidget->setCurrentIndex(static_cast(i)); + tabSelected = true; } } @@ -234,23 +176,21 @@ ModInfoDialog::~ModInfoDialog() delete ui; } -template -std::vector> createTabsImpl( - OrganizerCore& oc, PluginContainer& plugin, - ModInfoDialog* self, Ui::ModInfoDialog* ui) +std::vector> ModInfoDialog::createTabs() { std::vector> v; - (v.push_back(std::make_unique(oc, plugin, self, ui)), ...); - return v; -} + v.push_back(createTab(TAB_TEXTFILES)); + v.push_back(createTab(TAB_INIFILES)); + v.push_back(createTab(TAB_IMAGES)); + v.push_back(createTab(TAB_ESPS)); + v.push_back(createTab(TAB_CONFLICTS)); + v.push_back(createTab(TAB_CATEGORIES)); + v.push_back(createTab(TAB_NEXUS)); + v.push_back(createTab(TAB_NOTES)); + v.push_back(createTab(TAB_FILETREE)); -std::vector> ModInfoDialog::createTabs() -{ - return createTabsImpl< - TextFilesTab, IniFilesTab, ImagesTab, ESPsTab, - ConflictsTab, CategoriesTab, NexusTab, NotesTab, FileTreeTab>( - *m_OrganizerCore, *m_PluginContainer, this, ui); + return v; } int ModInfoDialog::exec() @@ -259,16 +199,6 @@ int ModInfoDialog::exec() return TutorableDialog::exec(); } -int ModInfoDialog::tabIndex(const QString &tabId) -{ - for (int i = 0; i < ui->tabWidget->count(); ++i) { - if (ui->tabWidget->widget(i)->objectName() == tabId) { - return i; - } - } - return -1; -} - void ModInfoDialog::saveState(Settings& s) const { s.directInterface().setValue("mod_info_tabs", saveTabState()); @@ -309,7 +239,7 @@ void ModInfoDialog::restoreTabState(const QByteArray &state) } // then actually move the tabs - QTabBar *tabBar = ui->tabWidget->findChild("qt_tabwidget_tabbar"); // magic name = bad + QTabBar *tabBar = ui->tabWidget->tabBar(); ui->tabWidget->blockSignals(true); for (int newPos = 0; newPos < count; ++newPos) { QString tabId = tabIds.at(newPos); @@ -331,6 +261,16 @@ QByteArray ModInfoDialog::saveTabState() const return result; } +int ModInfoDialog::tabIndex(const QString& tabId) +{ + for (int i = 0; i < ui->tabWidget->count(); ++i) { + if (ui->tabWidget->widget(i)->objectName() == tabId) { + return i; + } + } + return -1; +} + void ModInfoDialog::onDeleteShortcut() { for (auto& t : m_tabs) { @@ -373,9 +313,8 @@ void ModInfoDialog::on_closeButton_clicked() void ModInfoDialog::openTab(int tab) { - QTabWidget *tabWidget = findChild("tabWidget"); - if (tabWidget->isTabEnabled(tab)) { - tabWidget->setCurrentIndex(tab); + if (ui->tabWidget->isTabEnabled(tab)) { + ui->tabWidget->setCurrentIndex(tab); } } diff --git a/src/modinfodialog.h b/src/modinfodialog.h index 49007c87..020e7958 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -23,36 +23,15 @@ along with Mod Organizer. If not, see . #include "modinfo.h" #include "tutorabledialog.h" -#include "plugincontainer.h" -#include "organizercore.h" -#include "filterwidget.h" #include "filerenamer.h" -#include "expanderwidget.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Ui { - class ModInfoDialog; -} - -class QFileSystemModel; -class QTreeView; -class CategoryFactory; -class TextEditor; -class ModInfoDialogTab; +namespace Ui { class ModInfoDialog; } +namespace MOShared { class FilesOrigin; } + +class PluginContainer; +class OrganizerCore; +class Settings; +class ModInfoDialogTab; bool canPreviewFile(PluginContainer& pluginContainer, bool isArchive, const QString& filename); @@ -131,9 +110,6 @@ signals: void modOpenPrev(int tab=-1); void originModified(int originID); -private: - int tabIndex(const QString &tabId); - private slots: void on_closeButton_clicked(); void on_tabWidget_currentChanged(int index); @@ -141,8 +117,6 @@ private slots: void on_prevButton_clicked(); private: - using FileEntry = MOShared::FileEntry; - Ui::ModInfoDialog *ui; ModInfo::Ptr m_ModInfo; std::vector> m_tabs; @@ -157,6 +131,14 @@ private: void restoreTabState(const QByteArray &state); QByteArray saveTabState() const; void onDeleteShortcut(); + int tabIndex(const QString &tabId); + + template + std::unique_ptr createTab(int index) + { + return std::make_unique( + *m_OrganizerCore, *m_PluginContainer, this, ui, index); + } }; #endif // MODINFODIALOG_H diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui index 65b89621..93550de3 100644 --- a/src/modinfodialog.ui +++ b/src/modinfodialog.ui @@ -20,7 +20,7 @@ QTabWidget::Rounded - 8 + 0 true @@ -1148,6 +1148,9 @@ p, li { white-space: pre-wrap; } QAbstractItemView::ExtendedSelection + + true +
diff --git a/src/modinfodialogcategories.cpp b/src/modinfodialogcategories.cpp index 69c7c6b5..321c22b8 100644 --- a/src/modinfodialogcategories.cpp +++ b/src/modinfodialogcategories.cpp @@ -5,8 +5,8 @@ CategoriesTab::CategoriesTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui) - : ModInfoDialogTab(oc, plugin, parent, ui) + QWidget* parent, Ui::ModInfoDialog* ui, int index) + : ModInfoDialogTab(oc, plugin, parent, ui, index) { connect( ui->categories, &QTreeWidget::itemChanged, @@ -35,6 +35,11 @@ void CategoriesTab::update() updatePrimary(); } +bool CategoriesTab::canHandleSeparators() const +{ + return true; +} + void CategoriesTab::add( const CategoryFactory &factory, const std::set& enabledCategories, QTreeWidgetItem* root, int rootLevel) diff --git a/src/modinfodialogcategories.h b/src/modinfodialogcategories.h index 76426a5d..29d0b2a5 100644 --- a/src/modinfodialogcategories.h +++ b/src/modinfodialogcategories.h @@ -7,10 +7,11 @@ class CategoriesTab : public ModInfoDialogTab public: CategoriesTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui); + QWidget* parent, Ui::ModInfoDialog* ui, int index); void clear() override; void update() override; + bool canHandleSeparators() const override; private: void add( diff --git a/src/modinfodialogconflicts.cpp b/src/modinfodialogconflicts.cpp index adde27ca..15bb7ed4 100644 --- a/src/modinfodialogconflicts.cpp +++ b/src/modinfodialogconflicts.cpp @@ -2,6 +2,8 @@ #include "ui_modinfodialog.h" #include "modinfodialog.h" #include "utility.h" +#include "settings.h" +#include "organizercore.h" using namespace MOShared; using namespace MOBase; @@ -131,8 +133,8 @@ public: ConflictsTab::ConflictsTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui) : - ModInfoDialogTab(oc, plugin, parent, ui), + QWidget* parent, Ui::ModInfoDialog* ui, int index) : + ModInfoDialogTab(oc, plugin, parent, ui, index), m_general(this, ui, oc), m_advanced(this, ui, oc) { connect( @@ -174,6 +176,11 @@ void ConflictsTab::restoreState(const Settings& s) m_advanced.restoreState(s); } +bool ConflictsTab::canHandleUnmanaged() const +{ + return true; +} + void ConflictsTab::changeItemsVisibility( const QList& items, bool visible) { diff --git a/src/modinfodialogconflicts.h b/src/modinfodialogconflicts.h index 9c011163..a05682ba 100644 --- a/src/modinfodialogconflicts.h +++ b/src/modinfodialogconflicts.h @@ -5,6 +5,7 @@ #include "expanderwidget.h" #include "filterwidget.h" #include "directoryentry.h" +#include class ConflictsTab; class OrganizerCore; @@ -96,13 +97,14 @@ class ConflictsTab : public ModInfoDialogTab public: ConflictsTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui); + QWidget* parent, Ui::ModInfoDialog* ui, int index); void update() override; void clear() override; void saveState(Settings& s) override; void restoreState(const Settings& s) override; + bool canHandleUnmanaged() const override; void openItems(const QList& items); void previewItems(const QList& items); diff --git a/src/modinfodialogesps.cpp b/src/modinfodialogesps.cpp index ea7eb3b0..dd4fff0b 100644 --- a/src/modinfodialogesps.cpp +++ b/src/modinfodialogesps.cpp @@ -124,8 +124,8 @@ private: ESPsTab::ESPsTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui) - : ModInfoDialogTab(oc, plugin, parent, ui) + QWidget* parent, Ui::ModInfoDialog* ui, int index) + : ModInfoDialogTab(oc, plugin, parent, ui, index) { QObject::connect( ui->activateESP, &QToolButton::clicked, [&]{ onActivate(); }); diff --git a/src/modinfodialogesps.h b/src/modinfodialogesps.h index e1a7a4f7..d8c8997e 100644 --- a/src/modinfodialogesps.h +++ b/src/modinfodialogesps.h @@ -12,7 +12,7 @@ class ESPsTab : public ModInfoDialogTab public: ESPsTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui); + QWidget* parent, Ui::ModInfoDialog* ui, int index); void clear() override; bool feedFile(const QString& rootPath, const QString& fullPath) override; diff --git a/src/modinfodialogfiletree.cpp b/src/modinfodialogfiletree.cpp index b73a9e24..3e233ccc 100644 --- a/src/modinfodialogfiletree.cpp +++ b/src/modinfodialogfiletree.cpp @@ -15,8 +15,8 @@ const int max_scan_for_context_menu = 50; FileTreeTab::FileTreeTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui) - : ModInfoDialogTab(oc, plugin, parent, ui), m_fs(nullptr) + QWidget* parent, Ui::ModInfoDialog* ui, int index) + : ModInfoDialogTab(oc, plugin, parent, ui, index), m_fs(nullptr) { m_fs = new QFileSystemModel(this); m_fs->setReadOnly(false); diff --git a/src/modinfodialogfiletree.h b/src/modinfodialogfiletree.h index dcc096fe..d0c36edc 100644 --- a/src/modinfodialogfiletree.h +++ b/src/modinfodialogfiletree.h @@ -8,7 +8,7 @@ class FileTreeTab : public ModInfoDialogTab public: FileTreeTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui); + QWidget* parent, Ui::ModInfoDialog* ui, int index); void clear() override; void update() override; diff --git a/src/modinfodialogimages.cpp b/src/modinfodialogimages.cpp index 1c7dcc1f..332a0984 100644 --- a/src/modinfodialogimages.cpp +++ b/src/modinfodialogimages.cpp @@ -82,8 +82,9 @@ void ScalableImage::mousePressEvent(QMouseEvent* e) ImagesTab::ImagesTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui) - : ModInfoDialogTab(oc, plugin, parent, ui), m_image(new ScalableImage) + QWidget* parent, Ui::ModInfoDialog* ui, int index) : + ModInfoDialogTab(oc, plugin, parent, ui, index), + m_image(new ScalableImage) { ui->imagesImage->layout()->addWidget(m_image); ui->imagesThumbnails->setLayout(new QVBoxLayout); diff --git a/src/modinfodialogimages.h b/src/modinfodialogimages.h index 7853935a..689b8e93 100644 --- a/src/modinfodialogimages.h +++ b/src/modinfodialogimages.h @@ -36,7 +36,7 @@ class ImagesTab : public ModInfoDialogTab public: ImagesTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui); + QWidget* parent, Ui::ModInfoDialog* ui, int index); void clear() override; bool feedFile(const QString& rootPath, const QString& fullPath) override; diff --git a/src/modinfodialognexus.cpp b/src/modinfodialognexus.cpp index 55b55439..9d51871c 100644 --- a/src/modinfodialognexus.cpp +++ b/src/modinfodialognexus.cpp @@ -9,8 +9,8 @@ NexusTab::NexusTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui) - : ModInfoDialogTab(oc, plugin, parent, ui), m_requestStarted(false) + QWidget* parent, Ui::ModInfoDialog* ui, int index) + : ModInfoDialogTab(oc, plugin, parent, ui, index), m_requestStarted(false) { ui->modID->setValidator(new QIntValidator(ui->modID)); ui->endorse->setVisible(core().settings().endorsementIntegration()); diff --git a/src/modinfodialognexus.h b/src/modinfodialognexus.h index 2e328c6d..7fe10171 100644 --- a/src/modinfodialognexus.h +++ b/src/modinfodialognexus.h @@ -35,7 +35,7 @@ class NexusTab : public ModInfoDialogTab public: NexusTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui); + QWidget* parent, Ui::ModInfoDialog* ui, int index); ~NexusTab(); diff --git a/src/modinfodialogtab.cpp b/src/modinfodialogtab.cpp index 58745220..1b7fadbb 100644 --- a/src/modinfodialogtab.cpp +++ b/src/modinfodialogtab.cpp @@ -5,8 +5,9 @@ ModInfoDialogTab::ModInfoDialogTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui) - : ui(ui), m_core(oc), m_plugin(plugin), m_parent(parent), m_origin(nullptr) + QWidget* parent, Ui::ModInfoDialog* ui, int index) : + ui(ui), m_core(oc), m_plugin(plugin), m_parent(parent), + m_origin(nullptr), m_tabIndex(index) { } @@ -42,6 +43,16 @@ bool ModInfoDialogTab::deleteRequested() return false; } +bool ModInfoDialogTab::canHandleSeparators() const +{ + return false; +} + +bool ModInfoDialogTab::canHandleUnmanaged() const +{ + return false; +} + void ModInfoDialogTab::setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin) { m_mod = mod; @@ -58,6 +69,11 @@ MOShared::FilesOrigin* ModInfoDialogTab::origin() const return m_origin; } +int ModInfoDialogTab::tabIndex() const +{ + return m_tabIndex; +} + OrganizerCore& ModInfoDialogTab::core() { return m_core; @@ -88,8 +104,8 @@ void ModInfoDialogTab::emitModOpen(QString name) NotesTab::NotesTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui) - : ModInfoDialogTab(oc, plugin, parent, ui) + QWidget* parent, Ui::ModInfoDialog* ui, int index) + : ModInfoDialogTab(oc, plugin, parent, ui, index) { connect(ui->commentsEdit, &QLineEdit::editingFinished, [&]{ onComments(); }); connect(ui->notesEdit, &HTMLEditor::editingFinished, [&]{ onNotes(); }); @@ -107,6 +123,11 @@ void NotesTab::update() ui->notesEdit->setText(mod()->notes()); } +bool NotesTab::canHandleSeparators() const +{ + return true; +} + void NotesTab::onComments() { mod()->setComments(ui->commentsEdit->text()); diff --git a/src/modinfodialogtab.h b/src/modinfodialogtab.h index 0dc977a8..1f99344f 100644 --- a/src/modinfodialogtab.h +++ b/src/modinfodialogtab.h @@ -29,11 +29,16 @@ public: virtual void restoreState(const Settings& s); virtual bool deleteRequested(); + virtual bool canHandleSeparators() const; + virtual bool canHandleUnmanaged() const; + virtual void setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin); ModInfo::Ptr mod() const; MOShared::FilesOrigin* origin() const; + int tabIndex() const; + signals: void originModified(int originID); void modOpen(QString name); @@ -43,7 +48,7 @@ protected: ModInfoDialogTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui); + QWidget* parent, Ui::ModInfoDialog* ui, int index); OrganizerCore& core(); PluginContainer& plugin(); @@ -59,6 +64,7 @@ private: QWidget* m_parent; ModInfo::Ptr m_mod; MOShared::FilesOrigin* m_origin; + int m_tabIndex; }; @@ -67,10 +73,11 @@ class NotesTab : public ModInfoDialogTab public: NotesTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui); + QWidget* parent, Ui::ModInfoDialog* ui, int index); void clear() override; void update() override; + bool canHandleSeparators() const override; private: void onComments(); diff --git a/src/modinfodialogtextfiles.cpp b/src/modinfodialogtextfiles.cpp index f48557b0..fddfafba 100644 --- a/src/modinfodialogtextfiles.cpp +++ b/src/modinfodialogtextfiles.cpp @@ -23,9 +23,9 @@ private: GenericFilesTab::GenericFilesTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, + QWidget* parent, Ui::ModInfoDialog* ui, int index, QListWidget* list, QSplitter* sp, TextEditor* e) - : ModInfoDialogTab(oc, plugin, parent, ui), m_list(list), m_editor(e) + : ModInfoDialogTab(oc, plugin, parent, ui, index), m_list(list), m_editor(e) { m_editor->setupToolbar(); @@ -113,9 +113,9 @@ void GenericFilesTab::select(FileListItem* item) TextFilesTab::TextFilesTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui) + QWidget* parent, Ui::ModInfoDialog* ui, int index) : GenericFilesTab( - oc, plugin, parent, ui, + oc, plugin, parent, ui, index, ui->textFileList, ui->tabTextSplitter, ui->textFileEditor) { } @@ -137,9 +137,9 @@ bool TextFilesTab::wantsFile(const QString& rootPath, const QString& fullPath) c IniFilesTab::IniFilesTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui) + QWidget* parent, Ui::ModInfoDialog* ui, int index) : GenericFilesTab( - oc, plugin, parent, ui, + oc, plugin, parent, ui, index, ui->iniFileList, ui->tabIniSplitter, ui->iniFileEditor) { } diff --git a/src/modinfodialogtextfiles.h b/src/modinfodialogtextfiles.h index 0dc5ec89..f618a6bb 100644 --- a/src/modinfodialogtextfiles.h +++ b/src/modinfodialogtextfiles.h @@ -23,7 +23,7 @@ protected: GenericFilesTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, + QWidget* parent, Ui::ModInfoDialog* ui, int index, QListWidget* list, QSplitter* splitter, TextEditor* editor); virtual bool wantsFile(const QString& rootPath, const QString& fullPath) const = 0; @@ -39,7 +39,7 @@ class TextFilesTab : public GenericFilesTab public: TextFilesTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui); + QWidget* parent, Ui::ModInfoDialog* ui, int index); protected: bool wantsFile(const QString& rootPath, const QString& fullPath) const override; @@ -51,7 +51,7 @@ class IniFilesTab : public GenericFilesTab public: IniFilesTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui); + QWidget* parent, Ui::ModInfoDialog* ui, int index); protected: bool wantsFile(const QString& rootPath, const QString& fullPath) const override; diff --git a/src/resources.qrc b/src/resources.qrc index 8645b27e..6fc33293 100644 --- a/src/resources.qrc +++ b/src/resources.qrc @@ -1,106 +1,108 @@ - - resources/help-browser.png - resources/list-add.png - resources/document-save.png - resources/edit-find-replace.png - resources/go-jump.png - resources/media-playback-start.png - resources/process-stop.png - resources/system-search.png - resources/view-refresh.png - resources/system-installer.png - resources/start-here.png - resources/list-remove.png - resources/document-properties.png - resources/go-up.png - resources/go-down.png - resources/switch-instance-icon.png - resources/contact-new.png - resources/preferences-system.png - resources/application-x-executable.png - resources/dialog-information.png - resources/emblem-readonly.png - resources/go-next_16.png - resources/go-previous_16.png - resources/view-refresh_16.png - resources/software-update-available.png - resources/emblem-important.png - resources/check.png - resources/dialog-warning.png - resources/symbol-backup.png - resources/applications-accessories.png - resources/emblem-unreadable.png - resources/internet-web-browser.png - resources/system-software-update.png - resources/help-browser_32.png - resources/system-installer.png - resources/function.png - resources/plugins.png - resources/edit-clear.png - resources/dynamic-blue-right.png - resources/icon-favorite.png - resources/emblem-favorite.png - resources/error.png - resources/show.png - splash.png - resources/conflict-mixed.png - resources/conflict-overwrite.png - resources/conflict-overwritten.png - resources/conflict-redundant.png - resources/conflict-mixed-blue.png - resources/conflict-overwrite-blue.png - resources/red-archive-conflict-loser.png - resources/accessories-text-editor.png - resources/x-office-calendar.png - resources/dialog-warning_16.png - resources/mail-attachment.png - resources/document-save_32.png - resources/edit-undo.png - resources/arrange-boxes.png - resources/badge_1.png - resources/badge_2.png - resources/badge_3.png - resources/badge_4.png - resources/badge_5.png - resources/badge_6.png - resources/badge_7.png - resources/badge_8.png - resources/badge_9.png - resources/badge_more.png - resources/status_active.png - resources/status_awaiting.png - resources/status_inactive.png - resources/mo_icon.png - resources/package.png - resources/switch-instance-icon.png - resources/open-Folder-Icon.png - resources/multiply-red.png - resources/archive-conflict-loser.png - resources/archive-conflict-mixed.png - resources/archive-conflict-neutral.png - resources/archive-conflict-winner.png - resources/game-warning.png - resources/game-warning-16.png - resources/tracked.png - - - resources/contents/jigsaw-piece.png - resources/contents/hand-of-god.png - resources/contents/empty-chessboard.png - resources/contents/double-quaver.png - resources/contents/lyre.png - resources/contents/usable.png - resources/contents/checkbox-tree.png - resources/contents/tinker.png - resources/contents/breastplate.png - resources/contents/conversation.png - resources/contents/locked-chest.png - resources/contents/config.png - resources/contents/feather-and-scroll.png - resources/contents/xedit.png - - - qt.conf - + + resources/save.svg + resources/word-wrap.svg + resources/help-browser.png + resources/list-add.png + resources/document-save.png + resources/edit-find-replace.png + resources/go-jump.png + resources/media-playback-start.png + resources/process-stop.png + resources/system-search.png + resources/view-refresh.png + resources/system-installer.png + resources/start-here.png + resources/list-remove.png + resources/document-properties.png + resources/go-up.png + resources/go-down.png + resources/switch-instance-icon.png + resources/contact-new.png + resources/preferences-system.png + resources/application-x-executable.png + resources/dialog-information.png + resources/emblem-readonly.png + resources/go-next_16.png + resources/go-previous_16.png + resources/view-refresh_16.png + resources/software-update-available.png + resources/emblem-important.png + resources/check.png + resources/dialog-warning.png + resources/symbol-backup.png + resources/applications-accessories.png + resources/emblem-unreadable.png + resources/internet-web-browser.png + resources/system-software-update.png + resources/help-browser_32.png + resources/system-installer.png + resources/function.png + resources/plugins.png + resources/edit-clear.png + resources/dynamic-blue-right.png + resources/icon-favorite.png + resources/emblem-favorite.png + resources/error.png + resources/show.png + splash.png + resources/conflict-mixed.png + resources/conflict-overwrite.png + resources/conflict-overwritten.png + resources/conflict-redundant.png + resources/conflict-mixed-blue.png + resources/conflict-overwrite-blue.png + resources/red-archive-conflict-loser.png + resources/accessories-text-editor.png + resources/x-office-calendar.png + resources/dialog-warning_16.png + resources/mail-attachment.png + resources/document-save_32.png + resources/edit-undo.png + resources/arrange-boxes.png + resources/badge_1.png + resources/badge_2.png + resources/badge_3.png + resources/badge_4.png + resources/badge_5.png + resources/badge_6.png + resources/badge_7.png + resources/badge_8.png + resources/badge_9.png + resources/badge_more.png + resources/status_active.png + resources/status_awaiting.png + resources/status_inactive.png + resources/mo_icon.png + resources/package.png + resources/switch-instance-icon.png + resources/open-Folder-Icon.png + resources/multiply-red.png + resources/archive-conflict-loser.png + resources/archive-conflict-mixed.png + resources/archive-conflict-neutral.png + resources/archive-conflict-winner.png + resources/game-warning.png + resources/game-warning-16.png + resources/tracked.png + + + resources/contents/jigsaw-piece.png + resources/contents/hand-of-god.png + resources/contents/empty-chessboard.png + resources/contents/double-quaver.png + resources/contents/lyre.png + resources/contents/usable.png + resources/contents/checkbox-tree.png + resources/contents/tinker.png + resources/contents/breastplate.png + resources/contents/conversation.png + resources/contents/locked-chest.png + resources/contents/config.png + resources/contents/feather-and-scroll.png + resources/contents/xedit.png + + + qt.conf + -- cgit v1.3.1 From eb8140afadc5aa4e6d1d2611f69dc6e38f469978 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Mon, 24 Jun 2019 07:28:02 -0400 Subject: grey out tab names when they have no data remove tabs if they're can't handle the selected mod next/previous now load mods in place without reopening the dialog tab reordering is broken --- src/mainwindow.cpp | 104 +++++++----- src/mainwindow.h | 5 +- src/modinfodialog.cpp | 358 ++++++++++++++++++++++++++-------------- src/modinfodialog.h | 60 ++++--- src/modinfodialogcategories.cpp | 2 + src/modinfodialogconflicts.cpp | 7 +- src/modinfodialogconflicts.h | 3 +- src/modinfodialogesps.cpp | 2 + src/modinfodialogfiletree.cpp | 4 +- src/modinfodialogimages.cpp | 12 +- src/modinfodialogimages.h | 2 +- src/modinfodialognexus.cpp | 2 + src/modinfodialogtab.cpp | 22 ++- src/modinfodialogtab.h | 3 + src/modinfodialogtextfiles.cpp | 2 + 15 files changed, 375 insertions(+), 213 deletions(-) (limited to 'src/modinfodialogconflicts.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index f9bfaafb..67dc8418 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3223,18 +3223,14 @@ void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index, } else { modInfo->saveMeta(); - ModInfoDialog dialog( - modInfo, modInfo->hasFlag(ModInfo::FLAG_FOREIGN), - &m_OrganizerCore, &m_PluginContainer, this); - - connect(&dialog, SIGNAL(modOpen(QString, int)), this, SLOT(displayModInformation(QString, int)), Qt::QueuedConnection); - connect(&dialog, SIGNAL(modOpenNext(int)), this, SLOT(modOpenNext(int)), Qt::QueuedConnection); - connect(&dialog, SIGNAL(modOpenPrev(int)), this, SLOT(modOpenPrev(int)), Qt::QueuedConnection); + ModInfoDialog dialog(this, &m_OrganizerCore, &m_PluginContainer); connect(&dialog, SIGNAL(originModified(int)), this, SLOT(originModified(int))); + dialog.setMod(modInfo); + //Open the tab first if we want to use the standard indexes of the tabs. if (tab != -1) { - dialog.openTab(tab); + dialog.setTab(tab); } dialog.restoreState(m_OrganizerCore.settings()); @@ -3244,16 +3240,6 @@ void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index, dialog.restoreGeometry(settings.value(key).toByteArray()); } - //If no tab was specified use the first tab from the left based on the user order. - if (tab == -1) { - for (int i = 0; i < dialog.findChild("tabWidget")->count(); ++i) { - if (dialog.findChild("tabWidget")->isTabEnabled(i)) { - dialog.findChild("tabWidget")->setCurrentIndex(i); - break; - } - } - } - dialog.exec(); dialog.saveState(m_OrganizerCore.settings()); settings.setValue(key, dialog.saveGeometry()); @@ -3296,43 +3282,71 @@ void MainWindow::setWindowEnabled(bool enabled) } -void MainWindow::modOpenNext(int tab) +ModInfo::Ptr MainWindow::nextModInList() { - QModelIndex index = m_ModListSortProxy->mapFromSource(m_OrganizerCore.modList()->index(m_ContextRow, 0)); - index = m_ModListSortProxy->index((index.row() + 1) % m_ModListSortProxy->rowCount(), 0); + const QModelIndex start = m_ModListSortProxy->mapFromSource( + m_OrganizerCore.modList()->index(m_ContextRow, 0)); + + auto index = start; + + for (;;) { + index = m_ModListSortProxy->index((index.row() + 1) % m_ModListSortProxy->rowCount(), 0); + m_ContextRow = m_ModListSortProxy->mapToSource(index).row(); + + if (index == start || !index.isValid()) { + // wrapped around, give up + break; + } + + ModInfo::Ptr mod = ModInfo::getByIndex(m_ContextRow); - m_ContextRow = m_ModListSortProxy->mapToSource(index).row(); - ModInfo::Ptr mod = ModInfo::getByIndex(m_ContextRow); - std::vector flags = mod->getFlags(); - if ((std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end()) || - (std::find(flags.begin(), flags.end(), ModInfo::FLAG_BACKUP) != flags.end()) || - (std::find(flags.begin(), flags.end(), ModInfo::FLAG_SEPARATOR) != flags.end())) { // skip overwrite and backups and separators - modOpenNext(tab); - } else { - displayModInformation(m_ContextRow,tab); + if (mod->hasFlag(ModInfo::FLAG_OVERWRITE) || + mod->hasFlag(ModInfo::FLAG_BACKUP) || + mod->hasFlag(ModInfo::FLAG_SEPARATOR)) { + continue; + } + + return mod; } + + return {}; } -void MainWindow::modOpenPrev(int tab) +ModInfo::Ptr MainWindow::previousModInList() { - QModelIndex index = m_ModListSortProxy->mapFromSource(m_OrganizerCore.modList()->index(m_ContextRow, 0)); - int row = index.row() - 1; - if (row == -1) { - row = m_ModListSortProxy->rowCount() - 1; - } + const QModelIndex start = m_ModListSortProxy->mapFromSource( + m_OrganizerCore.modList()->index(m_ContextRow, 0)); + + auto index = start; + + for (;;) { + int row = index.row() - 1; + if (row == -1) { + row = m_ModListSortProxy->rowCount() - 1; + } + + index = m_ModListSortProxy->index(row, 0); + m_ContextRow = m_ModListSortProxy->mapToSource(index).row(); + + if (index == start || !index.isValid()) { + // wrapped around, give up + break; + } - m_ContextRow = m_ModListSortProxy->mapToSource(m_ModListSortProxy->index(row, 0)).row(); - ModInfo::Ptr mod = ModInfo::getByIndex(m_ContextRow); - std::vector flags = mod->getFlags(); - if ((std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end()) || - (std::find(flags.begin(), flags.end(), ModInfo::FLAG_BACKUP) != flags.end()) || - (std::find(flags.begin(), flags.end(), ModInfo::FLAG_SEPARATOR) != flags.end())) { // skip overwrite and backups and separators - modOpenPrev(tab); - } else { - displayModInformation(m_ContextRow,tab); + ModInfo::Ptr mod = ModInfo::getByIndex(m_ContextRow); + + if (mod->hasFlag(ModInfo::FLAG_OVERWRITE) || + mod->hasFlag(ModInfo::FLAG_BACKUP) || + mod->hasFlag(ModInfo::FLAG_SEPARATOR)) { + continue; + } + + return mod; } + + return {}; } void MainWindow::displayModInformation(const QString &modName, int tab) diff --git a/src/mainwindow.h b/src/mainwindow.h index b6283a26..f204211e 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -160,6 +160,9 @@ public: virtual MOBase::DelayedFileWriterBase &archivesWriter() override { return m_ArchiveListWriter; } + ModInfo::Ptr nextModInList(); + ModInfo::Ptr previousModInList(); + public slots: void displayColumnSelection(const QPoint &pos); @@ -549,8 +552,6 @@ private slots: void deselectFilters(); void displayModInformation(const QString &modName, int tab); - void modOpenNext(int tab=-1); - void modOpenPrev(int tab=-1); void modRenamed(const QString &oldName, const QString &newName); void modRemoved(const QString &fileName); diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index be7d4aa4..ad704ce8 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -21,6 +21,7 @@ along with Mod Organizer. If not, see . #include "ui_modinfodialog.h" #include "plugincontainer.h" #include "organizercore.h" +#include "mainwindow.h" #include "modinfodialogtextfiles.h" #include "modinfodialogimages.h" #include "modinfodialogesps.h" @@ -34,23 +35,6 @@ using namespace MOShared; const int max_scan_for_context_menu = 50; - -class ModFileListWidget : public QListWidgetItem { - friend bool operator<(const ModFileListWidget &LHS, const ModFileListWidget &RHS); -public: - ModFileListWidget(const QString &text, int sortValue, QListWidget *parent = 0) - : QListWidgetItem(text, parent, QListWidgetItem::UserType + 1), m_SortValue(sortValue) {} -private: - int m_SortValue; -}; - - -static bool operator<(const ModFileListWidget &LHS, const ModFileListWidget &RHS) -{ - return LHS.m_SortValue < RHS.m_SortValue; -} - - bool canPreviewFile( PluginContainer& pluginContainer, bool isArchive, const QString& filename) { @@ -111,74 +95,54 @@ FileRenamer::RenameResults unhideFile(FileRenamer& renamer, const QString &oldNa } +ModInfoDialog::TabInfo::TabInfo(std::unique_ptr tab) + : tab(std::move(tab)), realPos(-1), widget(nullptr) +{ +} + ModInfoDialog::ModInfoDialog( - ModInfo::Ptr modInfo, bool unmanaged, OrganizerCore *organizerCore, - PluginContainer *pluginContainer, QWidget *parent) : - TutorableDialog("ModInfoDialog", parent), ui(new Ui::ModInfoDialog), - m_ModInfo(modInfo), m_RootPath(modInfo->absolutePath()), - m_OrganizerCore(organizerCore), m_PluginContainer(pluginContainer), - m_Origin(nullptr) + MainWindow* mw, OrganizerCore* core, PluginContainer* plugin) : + TutorableDialog("ModInfoDialog", mw), + ui(new Ui::ModInfoDialog), m_mainWindow(mw), + m_core(core), m_plugin(plugin), m_initialTab(-1) { ui->setupUi(this); - auto* ds = m_OrganizerCore->directoryStructure(); - if (ds->originExists(ToWString(m_ModInfo->name()))) { - m_Origin = &ds->getOriginByName(ToWString(m_ModInfo->name())); - if (m_Origin->isDisabled()) { - m_Origin = nullptr; - } - } - - this->setWindowTitle(m_ModInfo->name()); - this->setWindowModality(Qt::WindowModal); - auto* sc = new QShortcut(QKeySequence::Delete, this); connect(sc, &QShortcut::activated, [&]{ onDeleteShortcut(); }); m_tabs = createTabs(); - bool tabSelected = false; - for (std::size_t i=0; itabWidget->count(); ++i) { + if (static_cast(i) >= m_tabs.size()) { + qCritical() << "mod info dialog has more tabs than expected"; + break; + } + + auto& tabInfo = m_tabs[static_cast(i)]; + tabInfo.widget = ui->tabWidget->widget(i); + tabInfo.caption = ui->tabWidget->tabText(i); + tabInfo.icon = ui->tabWidget->tabIcon(i); + tabInfo.realPos = i; + connect( - m_tabs[i].get(), &ModInfoDialogTab::originModified, + tabInfo.tab.get(), &ModInfoDialogTab::originModified, [&](int originID){ emit originModified(originID); }); connect( - m_tabs[i].get(), &ModInfoDialogTab::modOpen, + tabInfo.tab.get(), &ModInfoDialogTab::modOpen, [&](const QString& name){ - close(); - emit modOpen(name, static_cast(i)); + setMod(name); + update(); }); - - bool enabled = true; - - if (unmanaged) { - enabled = m_tabs[i]->canHandleUnmanaged(); - } else if (m_ModInfo->hasFlag(ModInfo::FLAG_SEPARATOR)) { - enabled = m_tabs[i]->canHandleSeparators(); - } - - ui->tabWidget->setTabEnabled(static_cast(i), enabled); - - if (!tabSelected && enabled) { - ui->tabWidget->setCurrentIndex(static_cast(i)); - tabSelected = true; - } - } - - for (auto& tab : m_tabs) { - tab->setMod(m_ModInfo, m_Origin); } } -ModInfoDialog::~ModInfoDialog() -{ - delete ui; -} +ModInfoDialog::~ModInfoDialog() = default; -std::vector> ModInfoDialog::createTabs() +std::vector ModInfoDialog::createTabs() { - std::vector> v; + std::vector v; v.push_back(createTab(TAB_TEXTFILES)); v.push_back(createTab(TAB_INIFILES)); @@ -195,31 +159,206 @@ std::vector> ModInfoDialog::createTabs() int ModInfoDialog::exec() { - refreshLists(); + update(); return TutorableDialog::exec(); } +void ModInfoDialog::setMod(ModInfo::Ptr mod) +{ + m_mod = mod; +} + +void ModInfoDialog::setMod(const QString& name) +{ + unsigned int index = ModInfo::getIndex(name); + if (index == UINT_MAX) { + qCritical() << "failed to resolve mod name " << name; + return; + } + + auto mod = ModInfo::getByIndex(index); + if (!mod) { + qCritical() << "mod by index " << index << " is null"; + return; + } + + setMod(mod); +} + +void ModInfoDialog::setTab(int index) +{ + if (!isVisible()) { + m_initialTab = index; + return; + } + + switchToTab(index); +} + +void ModInfoDialog::update() +{ + setWindowTitle(m_mod->name()); + setTabsVisibility(); + updateTabs(); + feedFiles(); + setTabsColors(); + + if (m_initialTab >= 0) { + switchToTab(m_initialTab); + m_initialTab = -1; + } +} + +void ModInfoDialog::setTabsVisibility() +{ + std::vector visibility(m_tabs.size()); + bool changed = false; + + for (std::size_t i=0; ihasFlag(ModInfo::FLAG_FOREIGN)) { + visible = tabInfo.tab->canHandleUnmanaged(); + } else if (m_mod->hasFlag(ModInfo::FLAG_SEPARATOR)) { + visible = tabInfo.tab->canHandleSeparators(); + } + + const auto currentlyVisible = (ui->tabWidget->indexOf(tabInfo.widget) != -1); + + if (visible != currentlyVisible) { + changed = true; + } + + visibility[i] = visible; + } + + if (!changed) { + return; + } + + // remember selection + const int sel = ui->tabWidget->currentIndex(); + + // remove all tabs + ui->tabWidget->clear(); + + // add visible tabs + for (std::size_t i=0; itabWidget->addTab(m_tabs[i].widget, m_tabs[i].icon, m_tabs[i].caption); + + if (static_cast(i) == sel) { + ui->tabWidget->setCurrentIndex(static_cast(i)); + } + } + } +} + +void ModInfoDialog::updateTabs() +{ + auto* origin = getOrigin(); + + for (auto& tabInfo : m_tabs) { + tabInfo.tab->setMod(m_mod, origin); + tabInfo.tab->clear(); + tabInfo.tab->update(); + } +} + +void ModInfoDialog::feedFiles() +{ + const auto rootPath = m_mod->absolutePath(); + + if (rootPath.length() > 0) { + QDirIterator dirIterator(rootPath, QDir::Files, QDirIterator::Subdirectories); + while (dirIterator.hasNext()) { + QString fileName = dirIterator.next(); + + for (auto& tabInfo : m_tabs) { + if (tabInfo.tab->feedFile(rootPath, fileName)) { + break; + } + } + } + } +} + +void ModInfoDialog::setTabsColors() +{ + for (const auto& tabInfo : m_tabs) { + const auto c = tabInfo.tab->hasData() ? + QColor::Invalid : + ui->tabWidget->palette().color(QPalette::Disabled, QPalette::WindowText); + + ui->tabWidget->tabBar()->setTabTextColor(tabInfo.realPos, c); + } +} + +void ModInfoDialog::switchToTab(std::size_t index) +{ + if (index >= m_tabs.size()) { + qCritical() << "tab index " << index << "out of range"; + return; + } + + if (ui->tabWidget->indexOf(m_tabs[index].widget) == -1) { + qCritical() << "can't switch to tab " << index << ", not available"; + return; + } + + ui->tabWidget->setCurrentIndex(m_tabs[index].realPos); +} + +MOShared::FilesOrigin* ModInfoDialog::getOrigin() +{ + MOShared::FilesOrigin* origin = nullptr; + + auto* ds = m_core->directoryStructure(); + if (ds->originExists(ToWString(m_mod->name()))) { + auto* origin = &ds->getOriginByName(ToWString(m_mod->name())); + if (!origin->isDisabled()) { + return origin; + } + } + + return nullptr; +} + void ModInfoDialog::saveState(Settings& s) const { - s.directInterface().setValue("mod_info_tabs", saveTabState()); + //s.directInterface().setValue("mod_info_tabs", saveTabState()); - for (const auto& tab : m_tabs) { - tab->saveState(s); + for (const auto& tabInfo : m_tabs) { + tabInfo.tab->saveState(s); } } void ModInfoDialog::restoreState(const Settings& s) { - restoreTabState(s.directInterface().value("mod_info_tabs").toByteArray()); + //restoreTabState(s.directInterface().value("mod_info_tabs").toByteArray()); - for (const auto& tab : m_tabs) { - tab->restoreState(s); + for (const auto& tabInfo : m_tabs) { + tabInfo.tab->restoreState(s); } } +QByteArray ModInfoDialog::saveTabState() const +{ + QByteArray result; + /*QDataStream stream(&result, QIODevice::WriteOnly); + stream << ui->tabWidget->count(); + for (int i = 0; i < ui->tabWidget->count(); ++i) { + stream << ui->tabWidget->widget(i)->objectName(); + }*/ + + return result; +} + void ModInfoDialog::restoreTabState(const QByteArray &state) { - QDataStream stream(state); + /*QDataStream stream(state); int count = 0; stream >> count; @@ -232,9 +371,9 @@ void ModInfoDialog::restoreTabState(const QByteArray &state) tabIds.append(tabId); int oldPos = tabIndex(tabId); if (oldPos != -1) { - m_RealTabPos[newPos] = oldPos; + m_realTabPos[newPos] = oldPos; } else { - m_RealTabPos[newPos] = newPos; + m_realTabPos[newPos] = newPos; } } @@ -246,19 +385,7 @@ void ModInfoDialog::restoreTabState(const QByteArray &state) int oldPos = tabIndex(tabId); tabBar->moveTab(oldPos, newPos); } - ui->tabWidget->blockSignals(false); -} - -QByteArray ModInfoDialog::saveTabState() const -{ - QByteArray result; - QDataStream stream(&result, QIODevice::WriteOnly); - stream << ui->tabWidget->count(); - for (int i = 0; i < ui->tabWidget->count(); ++i) { - stream << ui->tabWidget->widget(i)->objectName(); - } - - return result; + ui->tabWidget->blockSignals(false);*/ } int ModInfoDialog::tabIndex(const QString& tabId) @@ -273,37 +400,17 @@ int ModInfoDialog::tabIndex(const QString& tabId) void ModInfoDialog::onDeleteShortcut() { - for (auto& t : m_tabs) { - if (t->deleteRequested()) { + for (auto& tabInfo : m_tabs) { + if (tabInfo.tab->deleteRequested()) { break; } } } -void ModInfoDialog::refreshLists() -{ - for (auto& tab : m_tabs) { - tab->update(); - } - - if (m_RootPath.length() > 0) { - QDirIterator dirIterator(m_RootPath, QDir::Files, QDirIterator::Subdirectories); - while (dirIterator.hasNext()) { - QString fileName = dirIterator.next(); - - for (auto& tab : m_tabs) { - if (tab->feedFile(m_RootPath, fileName)) { - break; - } - } - } - } -} - void ModInfoDialog::on_closeButton_clicked() { - for (auto& tab : m_tabs) { - if (!tab->canClose()) { + for (auto& tabInfo : m_tabs) { + if (!tabInfo.tab->canClose()) { return; } } @@ -311,31 +418,28 @@ void ModInfoDialog::on_closeButton_clicked() close(); } -void ModInfoDialog::openTab(int tab) -{ - if (ui->tabWidget->isTabEnabled(tab)) { - ui->tabWidget->setCurrentIndex(tab); - } -} - void ModInfoDialog::on_tabWidget_currentChanged(int index) { } void ModInfoDialog::on_nextButton_clicked() { - int currentTab = ui->tabWidget->currentIndex(); - int tab = m_RealTabPos[currentTab]; + auto mod = m_mainWindow->nextModInList(); + if (mod == m_mod) { + return; + } - emit modOpenNext(tab); - this->accept(); + setMod(mod); + update(); } void ModInfoDialog::on_prevButton_clicked() { - int currentTab = ui->tabWidget->currentIndex(); - int tab = m_RealTabPos[currentTab]; + auto mod = m_mainWindow->previousModInList(); + if (mod == m_mod) { + return; + } - emit modOpenPrev(tab); - this->accept(); + setMod(mod); + update(); } diff --git a/src/modinfodialog.h b/src/modinfodialog.h index 020e7958..1cefc71a 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -32,7 +32,7 @@ class PluginContainer; class OrganizerCore; class Settings; class ModInfoDialogTab; - +class MainWindow; bool canPreviewFile(PluginContainer& pluginContainer, bool isArchive, const QString& filename); bool canOpenFile(bool isArchive, const QString& filename); @@ -71,10 +71,7 @@ public: * @param modInfo info structure about the mod to display * @param parent parend widget **/ - explicit ModInfoDialog( - ModInfo::Ptr modInfo, - bool unmanaged, OrganizerCore *organizerCore, PluginContainer *pluginContainer, - QWidget *parent = 0); + ModInfoDialog(MainWindow* mw, OrganizerCore* core, PluginContainer* plugin); ~ModInfoDialog(); @@ -92,12 +89,9 @@ public: **/ const int getModID() const; - /** - * @brief open the specified tab in the dialog if it's enabled - * - * @param tab the tab to activate - **/ - void openTab(int tab); + void setMod(ModInfo::Ptr mod); + void setMod(const QString& name); + void setTab(int index); int exec() override; @@ -105,9 +99,6 @@ public: void restoreState(const Settings& s); signals: - void modOpen(const QString &modName, int tab); - void modOpenNext(int tab=-1); - void modOpenPrev(int tab=-1); void originModified(int originID); private slots: @@ -117,27 +108,42 @@ private slots: void on_prevButton_clicked(); private: - Ui::ModInfoDialog *ui; - ModInfo::Ptr m_ModInfo; - std::vector> m_tabs; - QString m_RootPath; - OrganizerCore *m_OrganizerCore; - PluginContainer *m_PluginContainer; - MOShared::FilesOrigin *m_Origin; - std::map m_RealTabPos; - - std::vector> createTabs(); - void refreshLists(); + struct TabInfo + { + std::unique_ptr tab; + int realPos; + QWidget* widget; + QString caption; + QIcon icon; + + TabInfo(std::unique_ptr tab); + }; + + std::unique_ptr ui; + MainWindow* m_mainWindow; + ModInfo::Ptr m_mod; + OrganizerCore* m_core; + PluginContainer* m_plugin; + std::vector m_tabs; + int m_initialTab; + + std::vector createTabs(); void restoreTabState(const QByteArray &state); QByteArray saveTabState() const; + void update(); void onDeleteShortcut(); int tabIndex(const QString &tabId); + MOShared::FilesOrigin* getOrigin(); + void setTabsVisibility(); + void updateTabs(); + void feedFiles(); + void setTabsColors(); + void switchToTab(std::size_t index); template std::unique_ptr createTab(int index) { - return std::make_unique( - *m_OrganizerCore, *m_PluginContainer, this, ui, index); + return std::make_unique(*m_core, *m_plugin, this, ui.get(), index); } }; diff --git a/src/modinfodialogcategories.cpp b/src/modinfodialogcategories.cpp index 321c22b8..bce1162b 100644 --- a/src/modinfodialogcategories.cpp +++ b/src/modinfodialogcategories.cpp @@ -22,6 +22,7 @@ void CategoriesTab::clear() { ui->categories->clear(); ui->primaryCategories->clear(); + setHasData(false); } void CategoriesTab::update() @@ -33,6 +34,7 @@ void CategoriesTab::update() ui->categories->invisibleRootItem(), 0); updatePrimary(); + setHasData(ui->primaryCategories->count() > 0); } bool CategoriesTab::canHandleSeparators() const diff --git a/src/modinfodialogconflicts.cpp b/src/modinfodialogconflicts.cpp index 15bb7ed4..dde00354 100644 --- a/src/modinfodialogconflicts.cpp +++ b/src/modinfodialogconflicts.cpp @@ -148,7 +148,7 @@ ConflictsTab::ConflictsTab( void ConflictsTab::update() { - m_general.update(); + setHasData(m_general.update()); m_advanced.update(); } @@ -156,6 +156,7 @@ void ConflictsTab::clear() { m_general.clear(); m_advanced.clear(); + setHasData(false); } void ConflictsTab::saveState(Settings& s) @@ -572,7 +573,7 @@ void GeneralConflictsTab::restoreState(const Settings& s) .value("mod_info_conflicts_general_overwritten").toByteArray()); } -void GeneralConflictsTab::update() +bool GeneralConflictsTab::update() { clear(); @@ -616,6 +617,8 @@ void GeneralConflictsTab::update() ui->overwriteCount->display(numOverwrite); ui->overwrittenCount->display(numOverwritten); ui->noConflictCount->display(numNonConflicting); + + return (numOverwrite > 0 || numOverwritten > 0); } QTreeWidgetItem* GeneralConflictsTab::createOverwriteItem( diff --git a/src/modinfodialogconflicts.h b/src/modinfodialogconflicts.h index a05682ba..38fa6a74 100644 --- a/src/modinfodialogconflicts.h +++ b/src/modinfodialogconflicts.h @@ -22,7 +22,7 @@ public: void saveState(Settings& s); void restoreState(const Settings& s); - void update(); + bool update(); signals: void modOpen(QString name); @@ -100,7 +100,6 @@ public: QWidget* parent, Ui::ModInfoDialog* ui, int index); void update() override; - void clear() override; void saveState(Settings& s) override; void restoreState(const Settings& s) override; diff --git a/src/modinfodialogesps.cpp b/src/modinfodialogesps.cpp index dd4fff0b..d0dcaf2b 100644 --- a/src/modinfodialogesps.cpp +++ b/src/modinfodialogesps.cpp @@ -138,6 +138,7 @@ void ESPsTab::clear() { ui->inactiveESPList->clear(); ui->activeESPList->clear(); + setHasData(false); } bool ESPsTab::feedFile(const QString& rootPath, const QString& fullPath) @@ -158,6 +159,7 @@ bool ESPsTab::feedFile(const QString& rootPath, const QString& fullPath) ui->inactiveESPList->addItem(item); } + setHasData(true); return true; } } diff --git a/src/modinfodialogfiletree.cpp b/src/modinfodialogfiletree.cpp index 3e233ccc..dae37f25 100644 --- a/src/modinfodialogfiletree.cpp +++ b/src/modinfodialogfiletree.cpp @@ -49,7 +49,9 @@ FileTreeTab::FileTreeTab( void FileTreeTab::clear() { m_fs->setRootPath({}); - //ui->filetree-> + + // always has data; even if the mod is empty, it still has a meta.ini + setHasData(true); } void FileTreeTab::update() diff --git a/src/modinfodialogimages.cpp b/src/modinfodialogimages.cpp index 332a0984..9a60fc8e 100644 --- a/src/modinfodialogimages.cpp +++ b/src/modinfodialogimages.cpp @@ -105,6 +105,7 @@ void ImagesTab::clear() } static_cast(ui->imagesThumbnails->layout())->addStretch(1); + setHasData(false); } bool ImagesTab::feedFile(const QString& rootPath, const QString& fullPath) @@ -115,7 +116,10 @@ bool ImagesTab::feedFile(const QString& rootPath, const QString& fullPath) for (const auto* e : extensions) { if (fullPath.endsWith(e, Qt::CaseInsensitive)) { - add(fullPath); + if (add(fullPath)) { + setHasData(true); + } + return true; } } @@ -123,13 +127,13 @@ bool ImagesTab::feedFile(const QString& rootPath, const QString& fullPath) return false; } -void ImagesTab::add(const QString& fullPath) +bool ImagesTab::add(const QString& fullPath) { QImage image = QImage(fullPath); if (image.isNull()) { qWarning() << "ImagesTab: '" << fullPath << "' is not a valid image"; - return; + return false; } auto* thumbnail = new ScalableImage(std::move(image)); @@ -140,6 +144,8 @@ void ImagesTab::add(const QString& fullPath) static_cast(ui->imagesThumbnails->layout())->insertWidget( ui->imagesThumbnails->layout()->count() - 1, thumbnail); + + return true; } void ImagesTab::onClicked(const QImage& original) diff --git a/src/modinfodialogimages.h b/src/modinfodialogimages.h index 689b8e93..60271da0 100644 --- a/src/modinfodialogimages.h +++ b/src/modinfodialogimages.h @@ -44,7 +44,7 @@ public: private: ScalableImage* m_image; - void add(const QString& fullPath); + bool add(const QString& fullPath); void onClicked(const QImage& image); }; diff --git a/src/modinfodialognexus.cpp b/src/modinfodialognexus.cpp index 9d51871c..61b868d1 100644 --- a/src/modinfodialognexus.cpp +++ b/src/modinfodialognexus.cpp @@ -48,6 +48,7 @@ void NexusTab::clear() ui->version->clear(); ui->browser->setPage(new NexusTabWebpage(ui->browser)); ui->url->clear(); + setHasData(false); } void NexusTab::update() @@ -88,6 +89,7 @@ void NexusTab::update() (mod()->endorsedState() == ModInfo::ENDORSED_NEVER)); updateWebpage(); + setHasData(mod()->getNexusID() >= 0); } void NexusTab::setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin) diff --git a/src/modinfodialogtab.cpp b/src/modinfodialogtab.cpp index 1b7fadbb..e50aec29 100644 --- a/src/modinfodialogtab.cpp +++ b/src/modinfodialogtab.cpp @@ -7,7 +7,7 @@ ModInfoDialogTab::ModInfoDialogTab( OrganizerCore& oc, PluginContainer& plugin, QWidget* parent, Ui::ModInfoDialog* ui, int index) : ui(ui), m_core(oc), m_plugin(plugin), m_parent(parent), - m_origin(nullptr), m_tabIndex(index) + m_origin(nullptr), m_tabIndex(index), m_hasData(false) { } @@ -74,6 +74,11 @@ int ModInfoDialogTab::tabIndex() const return m_tabIndex; } +bool ModInfoDialogTab::hasData() const +{ + return m_hasData; +} + OrganizerCore& ModInfoDialogTab::core() { return m_core; @@ -101,6 +106,11 @@ void ModInfoDialogTab::emitModOpen(QString name) emit modOpen(name); } +void ModInfoDialogTab::setHasData(bool b) +{ + m_hasData = b; +} + NotesTab::NotesTab( OrganizerCore& oc, PluginContainer& plugin, @@ -115,12 +125,18 @@ void NotesTab::clear() { ui->commentsEdit->clear(); ui->notesEdit->clear(); + setHasData(false); } void NotesTab::update() { - ui->commentsEdit->setText(mod()->comments()); - ui->notesEdit->setText(mod()->notes()); + const auto comments = mod()->comments(); + const auto notes = mod()->notes(); + + ui->commentsEdit->setText(comments); + ui->notesEdit->setText(notes); + + setHasData(!comments.isEmpty() || !notes.isEmpty()); } bool NotesTab::canHandleSeparators() const diff --git a/src/modinfodialogtab.h b/src/modinfodialogtab.h index 1f99344f..8fe7d2d4 100644 --- a/src/modinfodialogtab.h +++ b/src/modinfodialogtab.h @@ -38,6 +38,7 @@ public: MOShared::FilesOrigin* origin() const; int tabIndex() const; + bool hasData() const; signals: void originModified(int originID); @@ -57,6 +58,7 @@ protected: void emitOriginModified(); void emitModOpen(QString name); + void setHasData(bool b); private: OrganizerCore& m_core; @@ -65,6 +67,7 @@ private: ModInfo::Ptr m_mod; MOShared::FilesOrigin* m_origin; int m_tabIndex; + bool m_hasData; }; diff --git a/src/modinfodialogtextfiles.cpp b/src/modinfodialogtextfiles.cpp index fddfafba..bd175c24 100644 --- a/src/modinfodialogtextfiles.cpp +++ b/src/modinfodialogtextfiles.cpp @@ -42,6 +42,7 @@ void GenericFilesTab::clear() { m_list->clear(); select(nullptr); + setHasData(false); } bool GenericFilesTab::canClose() @@ -76,6 +77,7 @@ bool GenericFilesTab::feedFile(const QString& rootPath, const QString& fullPath) for (const auto* e : extensions) { if (wantsFile(rootPath, fullPath)) { m_list->addItem(new FileListItem(rootPath, fullPath)); + setHasData(true); return true; } } -- cgit v1.3.1 From ad4a90692b73bd73fa7a88690a95daa348839add Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Mon, 24 Jun 2019 07:37:26 -0400 Subject: fixed crash in conflict tabs --- src/modinfodialogconflicts.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/modinfodialogconflicts.cpp') diff --git a/src/modinfodialogconflicts.cpp b/src/modinfodialogconflicts.cpp index dde00354..ad3b5e5f 100644 --- a/src/modinfodialogconflicts.cpp +++ b/src/modinfodialogconflicts.cpp @@ -481,8 +481,8 @@ std::vector ConflictsTab::createGotoActions( GeneralConflictsTab::GeneralConflictsTab( - ConflictsTab* tab, Ui::ModInfoDialog* ui, OrganizerCore& oc) - : m_tab(tab), ui(ui), m_core(oc) + ConflictsTab* tab, Ui::ModInfoDialog* pui, OrganizerCore& oc) + : m_tab(tab), ui(pui), m_core(oc) { m_expanders.overwrite.set(ui->overwriteExpander, ui->overwriteTree, true); m_expanders.overwritten.set(ui->overwrittenExpander, ui->overwrittenTree, true); @@ -692,8 +692,8 @@ void GeneralConflictsTab::onOverwrittenActivated(QTreeWidgetItem *item, int) AdvancedConflictsTab::AdvancedConflictsTab( - ConflictsTab* tab, Ui::ModInfoDialog* ui, OrganizerCore& oc) - : m_tab(tab), ui(ui), m_core(oc) + ConflictsTab* tab, Ui::ModInfoDialog* pui, OrganizerCore& oc) + : m_tab(tab), ui(pui), m_core(oc) { // left-elide the overwrites column so that the nearest are visible ui->conflictsAdvancedList->setItemDelegateForColumn( -- cgit v1.3.1 From 581cfacbbdee17f2b4df8195487e5934702a430e Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Mon, 24 Jun 2019 09:29:41 -0400 Subject: changed "tab index" to "tab id", this was confusing the order in the widget and the id from the enum fixed reordering --- src/mainwindow.cpp | 2 +- src/modinfodialog.cpp | 208 +++++++++++++++++++++++++++------------- src/modinfodialog.h | 17 ++-- src/modinfodialog.ui | 2 +- src/modinfodialogcategories.cpp | 4 +- src/modinfodialogcategories.h | 2 +- src/modinfodialogconflicts.cpp | 4 +- src/modinfodialogconflicts.h | 2 +- src/modinfodialogesps.cpp | 4 +- src/modinfodialogesps.h | 2 +- src/modinfodialogfiletree.cpp | 4 +- src/modinfodialogfiletree.h | 2 +- src/modinfodialogimages.cpp | 4 +- src/modinfodialogimages.h | 2 +- src/modinfodialognexus.cpp | 4 +- src/modinfodialognexus.h | 2 +- src/modinfodialogtab.cpp | 8 +- src/modinfodialogtab.h | 6 +- src/modinfodialogtextfiles.cpp | 12 +-- src/modinfodialogtextfiles.h | 6 +- 20 files changed, 184 insertions(+), 113 deletions(-) (limited to 'src/modinfodialogconflicts.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 67dc8418..d75e8d9d 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3230,7 +3230,7 @@ void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index, //Open the tab first if we want to use the standard indexes of the tabs. if (tab != -1) { - dialog.setTab(tab); + dialog.setTab(ModInfoDialog::ETabs(tab)); } dialog.restoreState(m_OrganizerCore.settings()); diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index ad704ce8..c03739ca 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -104,7 +104,7 @@ ModInfoDialog::ModInfoDialog( MainWindow* mw, OrganizerCore* core, PluginContainer* plugin) : TutorableDialog("ModInfoDialog", mw), ui(new Ui::ModInfoDialog), m_mainWindow(mw), - m_core(core), m_plugin(plugin), m_initialTab(-1) + m_core(core), m_plugin(plugin), m_initialTab(ETabs(-1)) { ui->setupUi(this); @@ -123,7 +123,6 @@ ModInfoDialog::ModInfoDialog( tabInfo.widget = ui->tabWidget->widget(i); tabInfo.caption = ui->tabWidget->tabText(i); tabInfo.icon = ui->tabWidget->tabIcon(i); - tabInfo.realPos = i; connect( tabInfo.tab.get(), &ModInfoDialogTab::originModified, @@ -159,7 +158,16 @@ std::vector ModInfoDialog::createTabs() int ModInfoDialog::exec() { - update(); + const auto selectFirst = (m_initialTab == -1); + + update(true); + + if (selectFirst) { + if (ui->tabWidget->count() > 0) { + ui->tabWidget->setCurrentIndex(0); + } + } + return TutorableDialog::exec(); } @@ -185,33 +193,34 @@ void ModInfoDialog::setMod(const QString& name) setMod(mod); } -void ModInfoDialog::setTab(int index) +void ModInfoDialog::setTab(ETabs id) { if (!isVisible()) { - m_initialTab = index; + m_initialTab = id; return; } - switchToTab(index); + switchToTab(id); } -void ModInfoDialog::update() +void ModInfoDialog::update(bool firstTime) { setWindowTitle(m_mod->name()); - setTabsVisibility(); + setTabsVisibility(firstTime); updateTabs(); feedFiles(); setTabsColors(); if (m_initialTab >= 0) { switchToTab(m_initialTab); - m_initialTab = -1; + m_initialTab = ETabs(-1); } } -void ModInfoDialog::setTabsVisibility() +void ModInfoDialog::setTabsVisibility(bool firstTime) { std::vector visibility(m_tabs.size()); + bool changed = false; for (std::size_t i=0; itabWidget->currentIndex(); - - // remove all tabs - ui->tabWidget->clear(); - - // add visible tabs - for (std::size_t i=0; itabWidget->addTab(m_tabs[i].widget, m_tabs[i].icon, m_tabs[i].caption); + const int selIndex = ui->tabWidget->currentIndex(); + ETabs sel = ETabs(-1); - if (static_cast(i) == sel) { - ui->tabWidget->setCurrentIndex(static_cast(i)); - } + for (const auto& tabInfo : m_tabs) { + if (tabInfo.realPos == selIndex) { + sel = ETabs(tabInfo.tab->tabID()); + break; } } + + reAddTabs(visibility, sel); } void ModInfoDialog::updateTabs() @@ -296,19 +301,16 @@ void ModInfoDialog::setTabsColors() } } -void ModInfoDialog::switchToTab(std::size_t index) +void ModInfoDialog::switchToTab(ETabs id) { - if (index >= m_tabs.size()) { - qCritical() << "tab index " << index << "out of range"; - return; - } - - if (ui->tabWidget->indexOf(m_tabs[index].widget) == -1) { - qCritical() << "can't switch to tab " << index << ", not available"; - return; + for (const auto& tabInfo : m_tabs) { + if (tabInfo.tab->tabID() == id) { + ui->tabWidget->setCurrentIndex(tabInfo.realPos); + return; + } } - ui->tabWidget->setCurrentIndex(m_tabs[index].realPos); + qDebug() << "can't switch to tab " << id << ", not available"; } MOShared::FilesOrigin* ModInfoDialog::getOrigin() @@ -328,7 +330,10 @@ MOShared::FilesOrigin* ModInfoDialog::getOrigin() void ModInfoDialog::saveState(Settings& s) const { - //s.directInterface().setValue("mod_info_tabs", saveTabState()); + const auto tabState = saveTabState(); + if (!tabState.isEmpty()) { + s.directInterface().setValue("mod_info_tabs", tabState); + } for (const auto& tabInfo : m_tabs) { tabInfo.tab->saveState(s); @@ -337,55 +342,120 @@ void ModInfoDialog::saveState(Settings& s) const void ModInfoDialog::restoreState(const Settings& s) { - //restoreTabState(s.directInterface().value("mod_info_tabs").toByteArray()); - for (const auto& tabInfo : m_tabs) { tabInfo.tab->restoreState(s); } } -QByteArray ModInfoDialog::saveTabState() const +QString ModInfoDialog::saveTabState() const { - QByteArray result; - /*QDataStream stream(&result, QIODevice::WriteOnly); - stream << ui->tabWidget->count(); - for (int i = 0; i < ui->tabWidget->count(); ++i) { - stream << ui->tabWidget->widget(i)->objectName(); - }*/ + if (static_cast(m_tabs.size()) != ui->tabWidget->count()) { + // only save tab state when all tabs are visible + return {}; + } + + QString result; + QTextStream stream(&result); + + for (int i=0; itabWidget->count(); ++i) { + stream << ui->tabWidget->widget(i)->objectName() << " "; + } + + return result.trimmed(); +} + +std::vector ModInfoDialog::getOrderedTabNames() const +{ + const auto value = Settings::instance() + .directInterface().value("mod_info_tabs"); + + std::vector v; + + if (value.type() == QVariant::ByteArray) { + // old byte array + QDataStream stream(value.toByteArray()); + + int count = 0; + stream >> count; + + for (int i=0; i> s; + v.emplace_back(std::move(s)); + } + } else { + // string list + QString string = value.toString(); + QTextStream stream(&string); + + while (!stream.atEnd()) { + QString s; + stream >> s; + v.emplace_back(std::move(s)); + } + } - return result; + return v; } -void ModInfoDialog::restoreTabState(const QByteArray &state) +void ModInfoDialog::reAddTabs(const std::vector& visibility, ETabs sel) { - /*QDataStream stream(state); - int count = 0; - stream >> count; - - QStringList tabIds; - - // first, only determine the new mapping - for (int newPos = 0; newPos < count; ++newPos) { - QString tabId; - stream >> tabId; - tabIds.append(tabId); - int oldPos = tabIndex(tabId); - if (oldPos != -1) { - m_realTabPos[newPos] = oldPos; - } else { - m_realTabPos[newPos] = newPos; + Q_ASSERT(visibility.size() == m_tabs.size()); + + // ordered tab names from settings + const auto orderedNames = getOrderedTabNames(); + + bool canSort = true; + + // gathering visible tabs + std::vector visibleTabs; + for (std::size_t i=0; iobjectName(); + auto itor = std::find(orderedNames.begin(), orderedNames.end(), objectName); + if (itor == orderedNames.end()) { + qCritical() << "can't sort tabs, '" << objectName << "' not found"; + canSort = false; + } + } } } - // then actually move the tabs - QTabBar *tabBar = ui->tabWidget->tabBar(); - ui->tabWidget->blockSignals(true); - for (int newPos = 0; newPos < count; ++newPos) { - QString tabId = tabIds.at(newPos); - int oldPos = tabIndex(tabId); - tabBar->moveTab(oldPos, newPos); + // sorting tabs + if (canSort) { + std::sort(visibleTabs.begin(), visibleTabs.end(), [&](auto&& a, auto&& b){ + auto aItor = std::find(orderedNames.begin(), orderedNames.end(), a->widget->objectName()); + auto bItor = std::find(orderedNames.begin(), orderedNames.end(), b->widget->objectName()); + + // this was checked above + Q_ASSERT(aItor != orderedNames.end() && bItor != orderedNames.end()); + + return (aItor < bItor); + }); + } + + + ui->tabWidget->clear(); + + // reset real positions + for (auto& tabInfo : m_tabs) { + tabInfo.realPos = -1; + } + + // add visible tabs + for (std::size_t i=0; i(i); + ui->tabWidget->addTab(tabInfo.widget, tabInfo.icon, tabInfo.caption); + + if (tabInfo.tab->tabID() == sel) { + ui->tabWidget->setCurrentIndex(static_cast(i)); + } } - ui->tabWidget->blockSignals(false);*/ } int ModInfoDialog::tabIndex(const QString& tabId) diff --git a/src/modinfodialog.h b/src/modinfodialog.h index 1cefc71a..54e056b8 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -64,7 +64,6 @@ public: TAB_FILETREE }; -public: /** * @brief constructor * @@ -91,7 +90,7 @@ public: void setMod(ModInfo::Ptr mod); void setMod(const QString& name); - void setTab(int index); + void setTab(ETabs id); int exec() override; @@ -125,20 +124,22 @@ private: OrganizerCore* m_core; PluginContainer* m_plugin; std::vector m_tabs; - int m_initialTab; + ETabs m_initialTab; std::vector createTabs(); - void restoreTabState(const QByteArray &state); - QByteArray saveTabState() const; - void update(); + void restoreTabState(const QString& state); + QString saveTabState() const; + void update(bool firstTime=false); void onDeleteShortcut(); int tabIndex(const QString &tabId); MOShared::FilesOrigin* getOrigin(); - void setTabsVisibility(); + void setTabsVisibility(bool firstTime); void updateTabs(); void feedFiles(); void setTabsColors(); - void switchToTab(std::size_t index); + void switchToTab(ETabs id); + void reAddTabs(const std::vector& visibility, ETabs sel); + std::vector getOrderedTabNames() const; template std::unique_ptr createTab(int index) diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui index 93550de3..40b3c7b4 100644 --- a/src/modinfodialog.ui +++ b/src/modinfodialog.ui @@ -1088,7 +1088,7 @@ p, li { white-space: pre-wrap; }
- + Filetree diff --git a/src/modinfodialogcategories.cpp b/src/modinfodialogcategories.cpp index bce1162b..4bd10028 100644 --- a/src/modinfodialogcategories.cpp +++ b/src/modinfodialogcategories.cpp @@ -5,8 +5,8 @@ CategoriesTab::CategoriesTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int index) - : ModInfoDialogTab(oc, plugin, parent, ui, index) + QWidget* parent, Ui::ModInfoDialog* ui, int id) + : ModInfoDialogTab(oc, plugin, parent, ui, id) { connect( ui->categories, &QTreeWidget::itemChanged, diff --git a/src/modinfodialogcategories.h b/src/modinfodialogcategories.h index 29d0b2a5..738b4e4d 100644 --- a/src/modinfodialogcategories.h +++ b/src/modinfodialogcategories.h @@ -7,7 +7,7 @@ class CategoriesTab : public ModInfoDialogTab public: CategoriesTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int index); + QWidget* parent, Ui::ModInfoDialog* ui, int id); void clear() override; void update() override; diff --git a/src/modinfodialogconflicts.cpp b/src/modinfodialogconflicts.cpp index ad3b5e5f..7f297ec3 100644 --- a/src/modinfodialogconflicts.cpp +++ b/src/modinfodialogconflicts.cpp @@ -133,8 +133,8 @@ public: ConflictsTab::ConflictsTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int index) : - ModInfoDialogTab(oc, plugin, parent, ui, index), + QWidget* parent, Ui::ModInfoDialog* ui, int id) : + ModInfoDialogTab(oc, plugin, parent, ui, id), m_general(this, ui, oc), m_advanced(this, ui, oc) { connect( diff --git a/src/modinfodialogconflicts.h b/src/modinfodialogconflicts.h index 38fa6a74..1f82a7c0 100644 --- a/src/modinfodialogconflicts.h +++ b/src/modinfodialogconflicts.h @@ -97,7 +97,7 @@ class ConflictsTab : public ModInfoDialogTab public: ConflictsTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int index); + QWidget* parent, Ui::ModInfoDialog* ui, int id); void update() override; void clear() override; diff --git a/src/modinfodialogesps.cpp b/src/modinfodialogesps.cpp index d0dcaf2b..6c4fc4dd 100644 --- a/src/modinfodialogesps.cpp +++ b/src/modinfodialogesps.cpp @@ -124,8 +124,8 @@ private: ESPsTab::ESPsTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int index) - : ModInfoDialogTab(oc, plugin, parent, ui, index) + QWidget* parent, Ui::ModInfoDialog* ui, int id) + : ModInfoDialogTab(oc, plugin, parent, ui, id) { QObject::connect( ui->activateESP, &QToolButton::clicked, [&]{ onActivate(); }); diff --git a/src/modinfodialogesps.h b/src/modinfodialogesps.h index d8c8997e..e82ed368 100644 --- a/src/modinfodialogesps.h +++ b/src/modinfodialogesps.h @@ -12,7 +12,7 @@ class ESPsTab : public ModInfoDialogTab public: ESPsTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int index); + QWidget* parent, Ui::ModInfoDialog* ui, int id); void clear() override; bool feedFile(const QString& rootPath, const QString& fullPath) override; diff --git a/src/modinfodialogfiletree.cpp b/src/modinfodialogfiletree.cpp index dae37f25..b57f6b5d 100644 --- a/src/modinfodialogfiletree.cpp +++ b/src/modinfodialogfiletree.cpp @@ -15,8 +15,8 @@ const int max_scan_for_context_menu = 50; FileTreeTab::FileTreeTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int index) - : ModInfoDialogTab(oc, plugin, parent, ui, index), m_fs(nullptr) + QWidget* parent, Ui::ModInfoDialog* ui, int id) + : ModInfoDialogTab(oc, plugin, parent, ui, id), m_fs(nullptr) { m_fs = new QFileSystemModel(this); m_fs->setReadOnly(false); diff --git a/src/modinfodialogfiletree.h b/src/modinfodialogfiletree.h index d0c36edc..2145f298 100644 --- a/src/modinfodialogfiletree.h +++ b/src/modinfodialogfiletree.h @@ -8,7 +8,7 @@ class FileTreeTab : public ModInfoDialogTab public: FileTreeTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int index); + QWidget* parent, Ui::ModInfoDialog* ui, int id); void clear() override; void update() override; diff --git a/src/modinfodialogimages.cpp b/src/modinfodialogimages.cpp index 9a60fc8e..f4cdade8 100644 --- a/src/modinfodialogimages.cpp +++ b/src/modinfodialogimages.cpp @@ -82,8 +82,8 @@ void ScalableImage::mousePressEvent(QMouseEvent* e) ImagesTab::ImagesTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int index) : - ModInfoDialogTab(oc, plugin, parent, ui, index), + QWidget* parent, Ui::ModInfoDialog* ui, int id) : + ModInfoDialogTab(oc, plugin, parent, ui, id), m_image(new ScalableImage) { ui->imagesImage->layout()->addWidget(m_image); diff --git a/src/modinfodialogimages.h b/src/modinfodialogimages.h index 60271da0..6603660a 100644 --- a/src/modinfodialogimages.h +++ b/src/modinfodialogimages.h @@ -36,7 +36,7 @@ class ImagesTab : public ModInfoDialogTab public: ImagesTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int index); + QWidget* parent, Ui::ModInfoDialog* ui, int id); void clear() override; bool feedFile(const QString& rootPath, const QString& fullPath) override; diff --git a/src/modinfodialognexus.cpp b/src/modinfodialognexus.cpp index 61b868d1..172968ab 100644 --- a/src/modinfodialognexus.cpp +++ b/src/modinfodialognexus.cpp @@ -9,8 +9,8 @@ NexusTab::NexusTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int index) - : ModInfoDialogTab(oc, plugin, parent, ui, index), m_requestStarted(false) + QWidget* parent, Ui::ModInfoDialog* ui, int id) + : ModInfoDialogTab(oc, plugin, parent, ui, id), m_requestStarted(false) { ui->modID->setValidator(new QIntValidator(ui->modID)); ui->endorse->setVisible(core().settings().endorsementIntegration()); diff --git a/src/modinfodialognexus.h b/src/modinfodialognexus.h index 7fe10171..ce1ef426 100644 --- a/src/modinfodialognexus.h +++ b/src/modinfodialognexus.h @@ -35,7 +35,7 @@ class NexusTab : public ModInfoDialogTab public: NexusTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int index); + QWidget* parent, Ui::ModInfoDialog* ui, int id); ~NexusTab(); diff --git a/src/modinfodialogtab.cpp b/src/modinfodialogtab.cpp index e50aec29..2f5fbdb8 100644 --- a/src/modinfodialogtab.cpp +++ b/src/modinfodialogtab.cpp @@ -5,9 +5,9 @@ ModInfoDialogTab::ModInfoDialogTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int index) : + QWidget* parent, Ui::ModInfoDialog* ui, int id) : ui(ui), m_core(oc), m_plugin(plugin), m_parent(parent), - m_origin(nullptr), m_tabIndex(index), m_hasData(false) + m_origin(nullptr), m_tabID(id), m_hasData(false) { } @@ -69,9 +69,9 @@ MOShared::FilesOrigin* ModInfoDialogTab::origin() const return m_origin; } -int ModInfoDialogTab::tabIndex() const +int ModInfoDialogTab::tabID() const { - return m_tabIndex; + return m_tabID; } bool ModInfoDialogTab::hasData() const diff --git a/src/modinfodialogtab.h b/src/modinfodialogtab.h index 8fe7d2d4..fae5bc41 100644 --- a/src/modinfodialogtab.h +++ b/src/modinfodialogtab.h @@ -37,7 +37,7 @@ public: ModInfo::Ptr mod() const; MOShared::FilesOrigin* origin() const; - int tabIndex() const; + int tabID() const; bool hasData() const; signals: @@ -49,7 +49,7 @@ protected: ModInfoDialogTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int index); + QWidget* parent, Ui::ModInfoDialog* ui, int id); OrganizerCore& core(); PluginContainer& plugin(); @@ -66,7 +66,7 @@ private: QWidget* m_parent; ModInfo::Ptr m_mod; MOShared::FilesOrigin* m_origin; - int m_tabIndex; + int m_tabID; bool m_hasData; }; diff --git a/src/modinfodialogtextfiles.cpp b/src/modinfodialogtextfiles.cpp index bd175c24..7c8e84c7 100644 --- a/src/modinfodialogtextfiles.cpp +++ b/src/modinfodialogtextfiles.cpp @@ -23,9 +23,9 @@ private: GenericFilesTab::GenericFilesTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int index, + QWidget* parent, Ui::ModInfoDialog* ui, int id, QListWidget* list, QSplitter* sp, TextEditor* e) - : ModInfoDialogTab(oc, plugin, parent, ui, index), m_list(list), m_editor(e) + : ModInfoDialogTab(oc, plugin, parent, ui, id), m_list(list), m_editor(e) { m_editor->setupToolbar(); @@ -115,9 +115,9 @@ void GenericFilesTab::select(FileListItem* item) TextFilesTab::TextFilesTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int index) + QWidget* parent, Ui::ModInfoDialog* ui, int id) : GenericFilesTab( - oc, plugin, parent, ui, index, + oc, plugin, parent, ui, id, ui->textFileList, ui->tabTextSplitter, ui->textFileEditor) { } @@ -139,9 +139,9 @@ bool TextFilesTab::wantsFile(const QString& rootPath, const QString& fullPath) c IniFilesTab::IniFilesTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int index) + QWidget* parent, Ui::ModInfoDialog* ui, int id) : GenericFilesTab( - oc, plugin, parent, ui, index, + oc, plugin, parent, ui, id, ui->iniFileList, ui->tabIniSplitter, ui->iniFileEditor) { } diff --git a/src/modinfodialogtextfiles.h b/src/modinfodialogtextfiles.h index f618a6bb..75f31d88 100644 --- a/src/modinfodialogtextfiles.h +++ b/src/modinfodialogtextfiles.h @@ -23,7 +23,7 @@ protected: GenericFilesTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int index, + QWidget* parent, Ui::ModInfoDialog* ui, int id, QListWidget* list, QSplitter* splitter, TextEditor* editor); virtual bool wantsFile(const QString& rootPath, const QString& fullPath) const = 0; @@ -39,7 +39,7 @@ class TextFilesTab : public GenericFilesTab public: TextFilesTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int index); + QWidget* parent, Ui::ModInfoDialog* ui, int id); protected: bool wantsFile(const QString& rootPath, const QString& fullPath) const override; @@ -51,7 +51,7 @@ class IniFilesTab : public GenericFilesTab public: IniFilesTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int index); + QWidget* parent, Ui::ModInfoDialog* ui, int id); protected: bool wantsFile(const QString& rootPath, const QString& fullPath) const override; -- cgit v1.3.1 From 4ee9f29cdcb3df2a085e02fa29c8ae3ddbd27f1c Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Mon, 24 Jun 2019 12:49:08 -0400 Subject: performance optimizations: text/ini tabs and advanced conflict list use views and custom models call update() after feedFiles() to allow some tabs to have post-processing after feedFiles() --- src/modinfodialog.cpp | 20 +++- src/modinfodialog.h | 2 + src/modinfodialog.ui | 48 +++------ src/modinfodialogconflicts.cpp | 216 +++++++++++++++++++++++++++++++++++------ src/modinfodialogconflicts.h | 6 +- src/modinfodialogtextfiles.cpp | 133 +++++++++++++++++++------ src/modinfodialogtextfiles.h | 13 ++- 7 files changed, 338 insertions(+), 100 deletions(-) (limited to 'src/modinfodialogconflicts.cpp') diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 4c169cc4..a407e9b7 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -35,6 +35,19 @@ using namespace MOShared; const int max_scan_for_context_menu = 50; + +int naturalCompare(const QString& a, const QString& b) +{ + static QCollator c = []{ + QCollator c; + c.setNumericMode(true); + c.setCaseSensitivity(Qt::CaseInsensitive); + return c; + }(); + + return c.compare(a, b); +} + bool canPreviewFile( PluginContainer& pluginContainer, bool isArchive, const QString& filename) { @@ -283,10 +296,15 @@ void ModInfoDialog::updateTabs(bool becauseOriginChanged) tabInfo.tab->setMod(m_mod, origin); tabInfo.tab->clear(); - tabInfo.tab->update(); } + feedFiles(becauseOriginChanged); + + for (auto& tabInfo : m_tabs) { + tabInfo.tab->update(); + } + setTabsColors(); } diff --git a/src/modinfodialog.h b/src/modinfodialog.h index 31ea5536..e814dcf4 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -42,6 +42,8 @@ bool canUnhideFile(bool isArchive, const QString& filename); FileRenamer::RenameResults hideFile(FileRenamer& renamer, const QString &oldName); FileRenamer::RenameResults unhideFile(FileRenamer& renamer, const QString &oldName); +int naturalCompare(const QString& a, const QString& b); + /** * this is a larger dialog used to visualise information about the mod. diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui index 40b3c7b4..5defcd57 100644 --- a/src/modinfodialog.ui +++ b/src/modinfodialog.ui @@ -60,13 +60,16 @@ - + A list of text-files in the mod directory. A list of text-files in the mod directory like readmes. + + true + @@ -117,13 +120,16 @@ - + This is a list of .ini files in the mod. This is a list of .ini files in the mod. These are usually used to configure the behaviour of mods if there are configurable parameters. + + true + @@ -251,6 +257,9 @@ They usually contain optional functionality, see the readme. Most mods do not have optional esps, so chances are good you are looking at an empty list. + + true +
@@ -375,6 +384,9 @@ Most mods do not have optional esps, so chances are good you are looking at an e These are the mod files that are in the (virtual) data directory of your game and will thus be selectable in the esp list in the main window. + + true + @@ -477,9 +489,6 @@ text-align: left; true - - true - 2 @@ -553,9 +562,6 @@ text-align: left; true - - true - 2 @@ -629,9 +635,6 @@ text-align: left; true - - true - 1 @@ -696,7 +699,7 @@ text-align: left; 0 - + Qt::CustomContextMenu @@ -709,27 +712,6 @@ text-align: left; true - - 3 - - - 243 - - - - Overwrites - - - - - File - - - - - Overwritten by - - diff --git a/src/modinfodialogconflicts.cpp b/src/modinfodialogconflicts.cpp index 7f297ec3..55fbf2e5 100644 --- a/src/modinfodialogconflicts.cpp +++ b/src/modinfodialogconflicts.cpp @@ -13,19 +13,6 @@ using namespace MOBase; const int max_scan_for_context_menu = 50; -int naturalCompare(const QString& a, const QString& b) -{ - static QCollator c = []{ - QCollator c; - c.setNumericMode(true); - c.setCaseSensitivity(Qt::CaseInsensitive); - return c; - }(); - - return c.compare(a, b); -} - - class ElideLeftDelegate : public QStyledItemDelegate { public: @@ -581,6 +568,10 @@ bool GeneralConflictsTab::update() int numOverwrite = 0; int numOverwritten = 0; + QList overwriteItems; + QList overwrittenItems; + QList noConflictItems; + if (m_tab->origin() != nullptr) { const auto rootPath = m_tab->mod()->absolutePath(); @@ -594,19 +585,19 @@ bool GeneralConflictsTab::update() if (fileOrigin == m_tab->origin()->getID()) { if (!alternatives.empty()) { - ui->overwriteTree->addTopLevelItem(createOverwriteItem( + overwriteItems.append(createOverwriteItem( file->getIndex(), archive, fileName, relativeName, alternatives)); ++numOverwrite; } else { // otherwise, put the file in the noconflict tree - ui->noConflictTree->addTopLevelItem(createNoConflictItem( + noConflictItems.append(createNoConflictItem( file->getIndex(), archive, fileName, relativeName)); ++numNonConflicting; } } else { - ui->overwrittenTree->addTopLevelItem(createOverwrittenItem( + overwrittenItems.append(createOverwrittenItem( file->getIndex(), fileOrigin, archive, fileName, relativeName)); ++numOverwritten; @@ -614,6 +605,10 @@ bool GeneralConflictsTab::update() } } + ui->overwriteTree->addTopLevelItems(overwriteItems); + ui->overwrittenTree->addTopLevelItems(overwrittenItems); + ui->noConflictTree->addTopLevelItems(noConflictItems); + ui->overwriteCount->display(numOverwrite); ui->overwrittenCount->display(numOverwritten); ui->noConflictCount->display(numNonConflicting); @@ -691,10 +686,166 @@ void GeneralConflictsTab::onOverwrittenActivated(QTreeWidgetItem *item, int) } +class AdvancedListModel : public QAbstractItemModel +{ +public: + struct Item + { + QString before, relativeName, after; + FileEntry::Index index; + QString fileName; + bool hasAltOrigins; + QString altOrigin; + bool archive; + }; + + void clear() + { + m_items.clear(); + } + + void reserve(std::size_t s) + { + m_items.reserve(s); + } + + QModelIndex index(int row, int col, const QModelIndex& ={}) const override + { + return createIndex(row, col); + } + + QModelIndex parent(const QModelIndex&) const override + { + return {}; + } + + int rowCount(const QModelIndex& parent={}) const override + { + if (parent.isValid()) { + return 0; + } + + return static_cast(m_items.size()); + } + + int columnCount(const QModelIndex& ={}) const override + { + return 3; + } + + QVariant data(const QModelIndex& index, int role) const override + { + if (role == Qt::DisplayRole) { + const auto row = index.row(); + if (row < 0) { + return {}; + } + + const auto i = static_cast(row); + if (i >= m_items.size()) { + return {}; + } + + const auto& item = m_items[i]; + + if (index.column() == 0) { + return item.before; + } else if (index.column() == 1) { + return item.relativeName; + } else if (index.column() == 2) { + return item.after; + } + } + + return {}; + } + + QVariant headerData(int col, Qt::Orientation, int role) const + { + if (role == Qt::DisplayRole) { + if (col == 0) { + return tr("Overwrites"); + } else if (col == 1) { + return tr("File"); + } else if (col == 2) { + return tr("Overwritten by"); + } + } + + return {}; + } + + void sort(int col, Qt::SortOrder order=Qt::AscendingOrder) + { + // avoids branching on column/sort order while sorting + auto sortBeforeAsc = [](const auto& a, const auto& b) { + return (naturalCompare(a.before, b.before) < 0); + }; + + auto sortBeforeDesc = [](const auto& a, const auto& b) { + return (naturalCompare(a.before, b.before) > 0); + }; + + auto sortFileAsc = [](const auto& a, const auto& b) { + return (naturalCompare(a.relativeName, b.relativeName) < 0); + }; + + auto sortFileDesc = [](const auto& a, const auto& b) { + return (naturalCompare(a.relativeName, b.relativeName) > 0); + }; + + auto sortAfterAsc = [](const auto& a, const auto& b) { + return (naturalCompare(a.after, b.after) < 0); + }; + + auto sortAfterDesc = [](const auto& a, const auto& b) { + return (naturalCompare(a.after, b.after) > 0); + }; + + if (col == 0) { + if (order == Qt::AscendingOrder) { + std::sort(m_items.begin(), m_items.end(), sortBeforeAsc); + } else { + std::sort(m_items.begin(), m_items.end(), sortBeforeDesc); + } + } else if (col == 1) { + if (order == Qt::AscendingOrder) { + std::sort(m_items.begin(), m_items.end(), sortFileAsc); + } else { + std::sort(m_items.begin(), m_items.end(), sortFileDesc); + } + } else if (col == 2) { + if (order == Qt::AscendingOrder) { + std::sort(m_items.begin(), m_items.end(), sortAfterAsc); + } else { + std::sort(m_items.begin(), m_items.end(), sortAfterDesc); + } + } + + emit layoutChanged({}, QAbstractItemModel::VerticalSortHint); + } + + void add(Item item) + { + m_items.emplace_back(std::move(item)); + } + + void finished() + { + emit dataChanged(index(0, 0), index(0, rowCount())); + } + +private: + std::vector m_items; +}; + + AdvancedConflictsTab::AdvancedConflictsTab( ConflictsTab* tab, Ui::ModInfoDialog* pui, OrganizerCore& oc) - : m_tab(tab), ui(pui), m_core(oc) + : m_tab(tab), ui(pui), m_core(oc), m_model(new AdvancedListModel) { + ui->conflictsAdvancedList->setModel(m_model); + // left-elide the overwrites column so that the nearest are visible ui->conflictsAdvancedList->setItemDelegateForColumn( 0, new ElideLeftDelegate(ui->conflictsAdvancedList)); @@ -717,9 +868,9 @@ AdvancedConflictsTab::AdvancedConflictsTab( ui->conflictsAdvancedShowNearest, &QRadioButton::clicked, [&]{ update(); }); - QObject::connect( - ui->conflictsAdvancedList, &QTreeWidget::customContextMenuRequested, - [&](const QPoint& p){ m_tab->showContextMenu(p, ui->conflictsAdvancedList); }); + //QObject::connect( + // ui->conflictsAdvancedList, &QTreeView::customContextMenuRequested, + // [&](const QPoint& p){ m_tab->showContextMenu(p, ui->conflictsAdvancedList); }); m_filter.set(ui->conflictsAdvancedFilter); m_filter.changed = [&]{ update(); }; @@ -727,7 +878,7 @@ AdvancedConflictsTab::AdvancedConflictsTab( void AdvancedConflictsTab::clear() { - ui->conflictsAdvancedList->clear(); + m_model->clear(); } void AdvancedConflictsTab::saveState(Settings& s) @@ -776,7 +927,10 @@ void AdvancedConflictsTab::update() if (m_tab->origin() != nullptr) { const auto rootPath = m_tab->mod()->absolutePath(); - for (const auto& file : m_tab->origin()->getFiles()) { + const auto& files = m_tab->origin()->getFiles(); + m_model->reserve(files.size()); + + for (const auto& file : files) { const QString relativeName = QDir::fromNativeSeparators(ToQString(file->getRelativePath())); const QString fileName = relativeName.mid(0).prepend(rootPath); @@ -784,18 +938,16 @@ void AdvancedConflictsTab::update() const int fileOrigin = file->getOrigin(archive); const auto& alternatives = file->getAlternatives(); - auto* advancedItem = createItem( + addItem( file->getIndex(), fileOrigin, archive, fileName, relativeName, alternatives); - - if (advancedItem) { - ui->conflictsAdvancedList->addTopLevelItem(advancedItem); - } } + + m_model->finished(); } } -QTreeWidgetItem* AdvancedConflictsTab::createItem( +void AdvancedConflictsTab::addItem( FileEntry::Index index, int fileOrigin, bool archive, const QString& fileName, const QString& relativeName, const MOShared::FileEntry::AlternativesVector& alternatives) @@ -888,7 +1040,7 @@ QTreeWidgetItem* AdvancedConflictsTab::createItem( // if both before and after are empty, it means this file has no conflicts // at all, only display it if the user wants it if (!hasAlts) { - return nullptr; + return; } } @@ -900,9 +1052,9 @@ QTreeWidgetItem* AdvancedConflictsTab::createItem( }); if (!matched) { - return nullptr; + return; } - return new ConflictItem( - {before, relativeName, after}, index, fileName, hasAlts, "", archive); + m_model->add( + {before, relativeName, after, index, fileName, hasAlts, "", archive}); } diff --git a/src/modinfodialogconflicts.h b/src/modinfodialogconflicts.h index 1f82a7c0..f499d1a4 100644 --- a/src/modinfodialogconflicts.h +++ b/src/modinfodialogconflicts.h @@ -9,6 +9,7 @@ class ConflictsTab; class OrganizerCore; +class ConflictItem; class GeneralConflictsTab : public QObject { @@ -60,6 +61,8 @@ private: }; +class AdvancedListModel; + class AdvancedConflictsTab : public QObject { Q_OBJECT; @@ -82,8 +85,9 @@ private: Ui::ModInfoDialog* ui; OrganizerCore& m_core; FilterWidget m_filter; + AdvancedListModel* m_model; - QTreeWidgetItem* createItem( + void addItem( MOShared::FileEntry::Index index, int fileOrigin, bool archive, const QString& fileName, const QString& relativeName, const MOShared::FileEntry::AlternativesVector& alternatives); diff --git a/src/modinfodialogtextfiles.cpp b/src/modinfodialogtextfiles.cpp index 7c8e84c7..08b8c988 100644 --- a/src/modinfodialogtextfiles.cpp +++ b/src/modinfodialogtextfiles.cpp @@ -1,32 +1,109 @@ #include "modinfodialogtextfiles.h" #include "ui_modinfodialog.h" +#include "modinfodialog.h" #include -class FileListItem : public QListWidgetItem +class FileListModel : public QAbstractItemModel { public: - FileListItem(const QString& rootPath, QString fullPath) - : m_fullPath(std::move(fullPath)) + void clear() { - setText(m_fullPath.mid(rootPath.length() + 1)); + m_files.clear(); } - const QString& fullPath() const + QModelIndex index(int row, int col, const QModelIndex& ={}) const override { - return m_fullPath; + return createIndex(row, col); + } + + QModelIndex parent(const QModelIndex&) const override + { + return {}; + } + + int rowCount(const QModelIndex& ={}) const override + { + return static_cast(m_files.size()); + } + + int columnCount(const QModelIndex& ={}) const override + { + return 1; + } + + QVariant data(const QModelIndex& index, int role) const override + { + if (role == Qt::DisplayRole) { + const auto row = index.row(); + if (row < 0) { + return {}; + } + + const auto i = static_cast(row); + if (i >= m_files.size()) { + return {}; + } + + return m_files[i].text; + } + + return {}; + } + + void add(const QString& rootPath, QString fullPath) + { + QString text = fullPath.mid(rootPath.length() + 1); + m_files.emplace_back(std::move(fullPath), std::move(text)); + } + + void finished() + { + std::sort(m_files.begin(), m_files.end(), [](const auto& a, const auto& b) { + return (naturalCompare(a.text, b.text) < 0); + }); + + emit dataChanged(index(0, 0), index(0, rowCount())); + } + + QString fullPath(const QModelIndex& index) const + { + const auto row = index.row(); + if (row < 0) { + return {}; + } + + const auto i = static_cast(row); + if (i >= m_files.size()) { + return {}; + } + + return m_files[i].fullPath; } private: - QString m_fullPath; + struct File + { + QString fullPath; + QString text; + + File(QString fp, QString t) + : fullPath(std::move(fp)), text(std::move(t)) + { + } + }; + + std::deque m_files; }; GenericFilesTab::GenericFilesTab( OrganizerCore& oc, PluginContainer& plugin, QWidget* parent, Ui::ModInfoDialog* ui, int id, - QListWidget* list, QSplitter* sp, TextEditor* e) - : ModInfoDialogTab(oc, plugin, parent, ui, id), m_list(list), m_editor(e) + QListView* list, QSplitter* sp, TextEditor* e) : + ModInfoDialogTab(oc, plugin, parent, ui, id), + m_list(list), m_editor(e), m_model(new FileListModel) { + m_list->setModel(m_model); m_editor->setupToolbar(); sp->setSizes({200, 1}); @@ -34,14 +111,14 @@ GenericFilesTab::GenericFilesTab( sp->setStretchFactor(1, 1); QObject::connect( - m_list, &QListWidget::currentItemChanged, - [&](auto* current, auto* previous){ onSelection(current, previous); }); + m_list->selectionModel(), &QItemSelectionModel::currentRowChanged, + [&](auto current, auto previous){ onSelection(current, previous); }); } void GenericFilesTab::clear() { - m_list->clear(); - select(nullptr); + m_model->clear(); + select({}); setHasData(false); } @@ -76,7 +153,7 @@ bool GenericFilesTab::feedFile(const QString& rootPath, const QString& fullPath) for (const auto* e : extensions) { if (wantsFile(rootPath, fullPath)) { - m_list->addItem(new FileListItem(rootPath, fullPath)); + m_model->add(rootPath, fullPath); setHasData(true); return true; } @@ -85,31 +162,31 @@ bool GenericFilesTab::feedFile(const QString& rootPath, const QString& fullPath) return false; } -void GenericFilesTab::onSelection( - QListWidgetItem* current, QListWidgetItem* previous) +void GenericFilesTab::update() { - auto* item = dynamic_cast(current); - if (!item) { - qCritical("TextFilesTab: item is not a FileListItem"); - return; - } + m_model->finished(); +} +void GenericFilesTab::onSelection( + const QModelIndex& current, const QModelIndex& previous) +{ if (!canClose()) { - m_list->setCurrentItem(previous, QItemSelectionModel::Current); + m_list->selectionModel()->select(previous, QItemSelectionModel::Current); return; } - select(item); + select(current); } -void GenericFilesTab::select(FileListItem* item) +void GenericFilesTab::select(const QModelIndex& index) { - if (item) { - m_editor->setEnabled(true); - m_editor->load(item->fullPath()); - } else { + if (!index.isValid()) { m_editor->setEnabled(false); + return; } + + m_editor->setEnabled(true); + m_editor->load(m_model->fullPath(index)); } diff --git a/src/modinfodialogtextfiles.h b/src/modinfodialogtextfiles.h index 75f31d88..d879c2bd 100644 --- a/src/modinfodialogtextfiles.h +++ b/src/modinfodialogtextfiles.h @@ -3,9 +3,10 @@ #include "modinfodialogtab.h" #include -#include +#include class FileListItem; +class FileListModel; class TextEditor; class GenericFilesTab : public ModInfoDialogTab @@ -16,21 +17,23 @@ public: void clear() override; bool canClose() override; bool feedFile(const QString& rootPath, const QString& fullPath) override; + void update() override; protected: - QListWidget* m_list; + QListView* m_list; TextEditor* m_editor; + FileListModel* m_model; GenericFilesTab( OrganizerCore& oc, PluginContainer& plugin, QWidget* parent, Ui::ModInfoDialog* ui, int id, - QListWidget* list, QSplitter* splitter, TextEditor* editor); + QListView* list, QSplitter* splitter, TextEditor* editor); virtual bool wantsFile(const QString& rootPath, const QString& fullPath) const = 0; private: - void onSelection(QListWidgetItem* current, QListWidgetItem* previous); - void select(FileListItem* item); + void onSelection(const QModelIndex& current, const QModelIndex& previous); + void select(const QModelIndex& index); }; -- cgit v1.3.1 From 1e54b4b57682b544bca0bb9d563fdb3d747073f2 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Mon, 24 Jun 2019 14:50:55 -0400 Subject: switched all conflict lists to QTreeView with custom model --- src/modinfodialog.ui | 56 +-- src/modinfodialogconflicts.cpp | 766 ++++++++++++++++++++++++----------------- src/modinfodialogconflicts.h | 37 +- 3 files changed, 471 insertions(+), 388 deletions(-) (limited to 'src/modinfodialogconflicts.cpp') diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui index 5defcd57..cdf4c80f 100644 --- a/src/modinfodialog.ui +++ b/src/modinfodialog.ui @@ -406,7 +406,7 @@ Most mods do not have optional esps, so chances are good you are looking at an e - 1 + 0 @@ -421,7 +421,7 @@ Most mods do not have optional esps, so chances are good you are looking at an e 100 - + 0 @@ -473,7 +473,7 @@ text-align: left; - + Qt::CustomContextMenu @@ -489,25 +489,6 @@ text-align: left; true - - 2 - - - 200 - - - 365 - - - - File - - - - - Overwritten Mods - - @@ -546,7 +527,7 @@ text-align: left; - + Qt::CustomContextMenu @@ -562,25 +543,6 @@ text-align: left; true - - 2 - - - 200 - - - 365 - - - - File - - - - - Providing Mod - - @@ -619,7 +581,7 @@ text-align: left; - + Qt::CustomContextMenu @@ -635,14 +597,6 @@ text-align: left; true - - 1 - - - - File - - diff --git a/src/modinfodialogconflicts.cpp b/src/modinfodialogconflicts.cpp index 55fbf2e5..d1384d7a 100644 --- a/src/modinfodialogconflicts.cpp +++ b/src/modinfodialogconflicts.cpp @@ -10,7 +10,7 @@ using namespace MOBase; // if there are more than 50 selected items in the conflict tree, don't bother // checking whether menu items apply to them, just show all of them -const int max_scan_for_context_menu = 50; +const std::size_t max_small_selection = 50; class ElideLeftDelegate : public QStyledItemDelegate @@ -27,60 +27,62 @@ protected: }; -class ConflictItem : public QTreeWidgetItem +class ConflictItem { public: - static const int FILENAME_USERROLE = Qt::UserRole + 1; - static const int ALT_ORIGIN_USERROLE = Qt::UserRole + 2; - static const int ARCHIVE_USERROLE = Qt::UserRole + 3; - static const int INDEX_USERROLE = Qt::UserRole + 4; - static const int HAS_ALTS_USERROLE = Qt::UserRole + 5; - ConflictItem( - QStringList columns, FileEntry::Index index, const QString& fileName, - bool hasAltOrigins, const QString& altOrigin, bool archive) - : QTreeWidgetItem(columns) + QString before, QString relativeName, QString after, + FileEntry::Index index, QString fileName, + bool hasAltOrigins, QString altOrigin, bool archive) : + m_before(std::move(before)), + m_relativeName(std::move(relativeName)), + m_after(std::move(after)), + m_index(index), + m_fileName(std::move(fileName)), + m_hasAltOrigins(hasAltOrigins), + m_altOrigin(std::move(altOrigin)), + m_isArchive(archive) { - setData(0, FILENAME_USERROLE, fileName); - setData(0, ALT_ORIGIN_USERROLE, altOrigin); - setData(0, ARCHIVE_USERROLE, archive); - setData(0, INDEX_USERROLE, index); - setData(0, HAS_ALTS_USERROLE, hasAltOrigins); - - if (archive) { - QFont f = font(0); - f.setItalic(true); - - for (int i=0; i); - return data(0, INDEX_USERROLE).toUInt(); + return m_index; } bool canHide() const @@ -103,21 +105,276 @@ public: return canPreviewFile(pluginContainer, isArchive(), fileName()); } - bool operator<(const QTreeWidgetItem& other) const +private: + QString m_before; + QString m_relativeName; + QString m_after; + FileEntry::Index m_index; + QString m_fileName; + bool m_hasAltOrigins; + QString m_altOrigin; + bool m_isArchive; +}; + + +class ConflictListModel : public QAbstractItemModel +{ +public: + struct Column { - const int column = treeWidget()->sortColumn(); + QString caption; + const QString& (ConflictItem::*getText)() const; + }; - if (column >= columnCount() || column >= other.columnCount()) { - // shouldn't happen - qWarning().nospace() << "ConflictItem::operator<() mistmatch in column count"; - return false; + ConflictListModel(QTreeView* tree, std::vector columns) + : m_tree(tree), m_columns(std::move(columns)) + { + m_tree->setModel(this); + } + + void clear() + { + m_items.clear(); + } + + void reserve(std::size_t s) + { + m_items.reserve(s); + } + + QModelIndex index(int row, int col, const QModelIndex& ={}) const override + { + return createIndex(row, col); + } + + QModelIndex parent(const QModelIndex&) const override + { + return {}; + } + + int rowCount(const QModelIndex& parent={}) const override + { + if (parent.isValid()) { + return 0; + } + + return static_cast(m_items.size()); + } + + int columnCount(const QModelIndex& ={}) const override + { + return static_cast(m_columns.size()); + } + + QVariant data(const QModelIndex& index, int role) const override + { + if (role == Qt::DisplayRole || role == Qt::FontRole) { + const auto row = index.row(); + if (row < 0) { + return {}; + } + + const auto i = static_cast(row); + if (i >= m_items.size()) { + return {}; + } + + const auto col = index.column(); + if (col < 0) { + return {}; + } + + const auto c = static_cast(col); + if (c >= m_columns.size()) { + return {}; + } + + const auto& item = m_items[i]; + + if (role == Qt::DisplayRole) { + return (item.*m_columns[c].getText)(); + } else if (role == Qt::FontRole) { + if (item.isArchive()) { + QFont f = m_tree->font(); + f.setItalic(true); + return f; + } + } + } + + return {}; + } + + QVariant headerData(int col, Qt::Orientation, int role) const + { + if (role == Qt::DisplayRole) { + if (col < 0) { + return {}; + } + + const auto i = static_cast(col); + if (i >= m_columns.size()) { + return {}; + } + + return m_columns[i].caption; } - return (naturalCompare(text(column), other.text(column)) < 0); + return {}; + } + + void sort(int colIndex, Qt::SortOrder order=Qt::AscendingOrder) + { + if (colIndex < 0) { + return; + } + + const auto c = static_cast(colIndex); + if (c >= m_columns.size()) { + return; + } + + const auto& col = m_columns[c]; + + // avoids branching on sort order while sorting + auto sortAsc = [&](const auto& a, const auto& b) { + return (naturalCompare((a.*col.getText)(), (b.*col.getText)()) < 0); + }; + + auto sortDesc = [&](const auto& a, const auto& b) { + return (naturalCompare((a.*col.getText)(), (b.*col.getText)()) > 0); + }; + + if (order == Qt::AscendingOrder) { + std::sort(m_items.begin(), m_items.end(), sortAsc); + } else { + std::sort(m_items.begin(), m_items.end(), sortDesc); + } + + emit layoutChanged({}, QAbstractItemModel::VerticalSortHint); + } + + void add(ConflictItem item) + { + m_items.emplace_back(std::move(item)); + } + + void finished() + { + beginResetModel(); + endResetModel(); + } + + const ConflictItem* getItem(std::size_t row) const + { + if (row >= m_items.size()) { + return nullptr; + } + + return &m_items[row]; + } + +private: + QTreeView* m_tree; + std::vector m_columns; + std::vector m_items; +}; + + +class OverwriteConflictListModel : public ConflictListModel +{ +public: + OverwriteConflictListModel(QTreeView* tree) + : ConflictListModel(tree, { + {tr("File"), &ConflictItem::relativeName}, + {tr("Overwritten Mods"), &ConflictItem::before} + }) + { + } +}; + + +class OverwrittenConflictListModel : public ConflictListModel +{ +public: + OverwrittenConflictListModel(QTreeView* tree) + : ConflictListModel(tree, { + {tr("File"), &ConflictItem::relativeName}, + {tr("Providing Mod"), &ConflictItem::after} + }) + { + } +}; + + +class NoConflictListModel : public ConflictListModel +{ +public: + NoConflictListModel(QTreeView* tree) + : ConflictListModel(tree, { + {tr("File"), &ConflictItem::relativeName} + }) + { + } +}; + + +class AdvancedConflictListModel : public ConflictListModel +{ +public: + AdvancedConflictListModel(QTreeView* tree) + : ConflictListModel(tree, { + {tr("Overwrites"), &ConflictItem::before}, + {tr("File"), &ConflictItem::relativeName}, + {tr("Overwritten By"), &ConflictItem::after} + }) + { } }; +std::size_t smallSelectionSize(const QTreeView* tree) +{ + const std::size_t too_many = std::numeric_limits::max(); + + std::size_t n = 0; + const auto* sel = tree->selectionModel(); + + for (const auto& range : sel->selection()) { + n += range.height(); + + if (n >= max_small_selection) { + return too_many; + } + } + + return n; +} + +template +void for_each_in_selection(QTreeView* tree, F&& f) +{ + const auto* sel = tree->selectionModel(); + const auto* model = dynamic_cast(tree->model()); + + if (!model) { + qCritical() << "tree doesn't have a ConflictListModel"; + return; + } + + for (const auto& range : sel->selection()) { + // ranges are inclusive + for (int row=range.top(); row<=range.bottom(); ++row) { + if (auto* item=model->getItem(static_cast(row))) { + if (!f(item)) { + return; + } + } + } + } +} + + ConflictsTab::ConflictsTab( OrganizerCore& oc, PluginContainer& plugin, QWidget* parent, Ui::ModInfoDialog* ui, int id) : @@ -169,52 +426,55 @@ bool ConflictsTab::canHandleUnmanaged() const return true; } -void ConflictsTab::changeItemsVisibility( - const QList& items, bool visible) +void ConflictsTab::changeItemsVisibility(QTreeView* tree, bool visible) { bool changed = false; bool stop = false; - qDebug().nospace() + const auto n = smallSelectionSize(tree); + + qDebug().nospace().noquote() << (visible ? "unhiding" : "hiding") << " " - << items.size() << " conflict files"; + << (n > max_small_selection ? "a lot of" : QString("%1").arg(n)) + << " conflict files"; QFlags flags = (visible ? FileRenamer::UNHIDE : FileRenamer::HIDE); - if (items.size() > 1) { + if (n > 1) { flags |= FileRenamer::MULTIPLE; } FileRenamer renamer(parentWidget(), flags); - for (const auto* item : items) { - if (stop) { - break; - } + auto* model = dynamic_cast(tree->model()); + if (!model) { + qCritical() << "list doesn't have a ConflictListModel"; + return; + } - const auto* ci = dynamic_cast(item); - if (!ci) { - continue; + for_each_in_selection(tree, [&](const ConflictItem* item) { + if (stop) { + return false; } auto result = FileRenamer::RESULT_CANCEL; if (visible) { - if (!ci->canUnhide()) { - qDebug().nospace() << "cannot unhide " << item->text(0) << ", skipping"; - continue; + if (!item->canUnhide()) { + qDebug().nospace() << "cannot unhide " << item->relativeName() << ", skipping"; + return true; } - result = unhideFile(renamer, ci->fileName()); + result = unhideFile(renamer, item->fileName()); } else { - if (!ci->canHide()) { - qDebug().nospace() << "cannot hide " << item->text(0) << ", skipping"; - continue; + if (!item->canHide()) { + qDebug().nospace() << "cannot hide " << item->relativeName() << ", skipping"; + return true; } - result = hideFile(renamer, ci->fileName()); + result = hideFile(renamer, item->fileName()); } switch (result) { @@ -235,7 +495,9 @@ void ConflictsTab::changeItemsVisibility( break; } } - } + + return true; + }); qDebug().nospace() << (visible ? "unhiding" : "hiding") << " conflict files done"; @@ -250,38 +512,36 @@ void ConflictsTab::changeItemsVisibility( } } -void ConflictsTab::openItems(const QList& items) +void ConflictsTab::openItems(QTreeView* tree) { // the menu item is only shown for a single selection, but handle all of them // in case this changes - for (auto* item : items) { - if (auto* ci=dynamic_cast(item)) { - core().executeFileVirtualized(parentWidget(), ci->fileName()); - } - } + for_each_in_selection(tree, [&](const ConflictItem* item) { + core().executeFileVirtualized(parentWidget(), item->fileName()); + return true; + }); } -void ConflictsTab::previewItems(const QList& items) +void ConflictsTab::previewItems(QTreeView* tree) { // the menu item is only shown for a single selection, but handle all of them // in case this changes - for (auto* item : items) { - if (auto* ci=dynamic_cast(item)) { - core().previewFileWithAlternatives(parentWidget(), ci->fileName()); - } - } + for_each_in_selection(tree, [&](const ConflictItem* item) { + core().previewFileWithAlternatives(parentWidget(), item->fileName()); + return true; + }); } -void ConflictsTab::showContextMenu(const QPoint &pos, QTreeWidget* tree) +void ConflictsTab::showContextMenu(const QPoint &pos, QTreeView* tree) { - auto actions = createMenuActions(tree->selectedItems()); + auto actions = createMenuActions(tree); QMenu menu; // open if (actions.open) { connect(actions.open, &QAction::triggered, [&]{ - openItems(tree->selectedItems()); + openItems(tree); }); menu.addAction(actions.open); @@ -290,7 +550,7 @@ void ConflictsTab::showContextMenu(const QPoint &pos, QTreeWidget* tree) // preview if (actions.preview) { connect(actions.preview, &QAction::triggered, [&]{ - previewItems(tree->selectedItems()); + previewItems(tree); }); menu.addAction(actions.preview); @@ -299,7 +559,7 @@ void ConflictsTab::showContextMenu(const QPoint &pos, QTreeWidget* tree) // hide if (actions.hide) { connect(actions.hide, &QAction::triggered, [&]{ - changeItemsVisibility(tree->selectedItems(), false); + changeItemsVisibility(tree, false); }); menu.addAction(actions.hide); @@ -308,7 +568,7 @@ void ConflictsTab::showContextMenu(const QPoint &pos, QTreeWidget* tree) // unhide if (actions.unhide) { connect(actions.unhide, &QAction::triggered, [&]{ - changeItemsVisibility(tree->selectedItems(), true); + changeItemsVisibility(tree, true); }); menu.addAction(actions.unhide); @@ -332,10 +592,9 @@ void ConflictsTab::showContextMenu(const QPoint &pos, QTreeWidget* tree) } } -ConflictsTab::Actions ConflictsTab::createMenuActions( - const QList& selection) +ConflictsTab::Actions ConflictsTab::createMenuActions(QTreeView* tree) { - if (selection.empty()) { + if (tree->selectionModel()->selection().isEmpty()) { return {}; } @@ -345,18 +604,28 @@ ConflictsTab::Actions ConflictsTab::createMenuActions( bool enablePreview = true; bool enableGoto = true; - if (selection.size() == 1) { + const auto n = smallSelectionSize(tree); + + const auto* model = dynamic_cast(tree->model()); + if (!model) { + qCritical() << "tree doesn't have a ConflictListModel"; + return {}; + } + + if (n == 1) { // this is a single selection - const auto* ci = dynamic_cast(selection[0]); - if (!ci) { + const auto* item = model->getItem(static_cast( + tree->selectionModel()->selectedRows()[0].row())); + + if (!item) { return {}; } - enableHide = ci->canHide(); - enableUnhide = ci->canUnhide(); - enableOpen = ci->canOpen(); - enablePreview = ci->canPreview(plugin()); - enableGoto = ci->hasAlts(); + enableHide = item->canHide(); + enableUnhide = item->canUnhide(); + enableOpen = item->canOpen(); + enablePreview = item->canPreview(plugin()); + enableGoto = item->hasAlts(); } else { // this is a multiple selection, don't show open/preview so users don't open @@ -367,28 +636,28 @@ ConflictsTab::Actions ConflictsTab::createMenuActions( // don't bother with this on multiple selection, at least for now enableGoto = false; - if (selection.size() < max_scan_for_context_menu) { + if (n <= max_small_selection) { // if the number of selected items is low, checking them to accurately // show the menu items is worth it enableHide = false; enableUnhide = false; - for (const auto* item : selection) { - if (const auto* ci=dynamic_cast(item)) { - if (ci->canHide()) { - enableHide = true; - } + for_each_in_selection(tree, [&](const ConflictItem* item) { + if (item->canHide()) { + enableHide = true; + } - if (ci->canUnhide()) { - enableUnhide = true; - } + if (item->canUnhide()) { + enableUnhide = true; + } - if (enableHide && enableUnhide && enableGoto) { - // found all, no need to check more - break; - } + if (enableHide && enableUnhide && enableGoto) { + // found all, no need to check more + return false; } - } + + return true; + }); } } @@ -411,22 +680,19 @@ ConflictsTab::Actions ConflictsTab::createMenuActions( actions.gotoMenu = new QMenu(tr("Go to..."), parentWidget()); actions.gotoMenu->setEnabled(enableGoto); - if (enableGoto) { - actions.gotoActions = createGotoActions(selection); + if (enableGoto && n == 1) { + const auto* item = model->getItem(static_cast( + tree->selectionModel()->selectedRows()[0].row())); + + actions.gotoActions = createGotoActions(item); } return actions; } -std::vector ConflictsTab::createGotoActions( - const QList& selection) +std::vector ConflictsTab::createGotoActions(const ConflictItem* item) { - if (!origin() || selection.size() != 1) { - return {}; - } - - const auto* item = dynamic_cast(selection[0]); - if (!item) { + if (!origin()) { return {}; } @@ -468,39 +734,42 @@ std::vector ConflictsTab::createGotoActions( GeneralConflictsTab::GeneralConflictsTab( - ConflictsTab* tab, Ui::ModInfoDialog* pui, OrganizerCore& oc) - : m_tab(tab), ui(pui), m_core(oc) + ConflictsTab* tab, Ui::ModInfoDialog* pui, OrganizerCore& oc) : + m_tab(tab), ui(pui), m_core(oc), + m_overwriteModel(new OverwriteConflictListModel(ui->overwriteTree)), + m_overwrittenModel(new OverwrittenConflictListModel(ui->overwrittenTree)), + m_noConflictModel(new NoConflictListModel(ui->noConflictTree)) { m_expanders.overwrite.set(ui->overwriteExpander, ui->overwriteTree, true); m_expanders.overwritten.set(ui->overwrittenExpander, ui->overwrittenTree, true); m_expanders.nonconflict.set(ui->noConflictExpander, ui->noConflictTree); QObject::connect( - ui->overwriteTree, &QTreeWidget::itemDoubleClicked, - [&](auto* item, int col){ onOverwriteActivated(item, col); }); + ui->overwriteTree, &QTreeView::doubleClicked, + [&](auto&& item){ onOverwriteActivated(item); }); QObject::connect( - ui->overwrittenTree, &QTreeWidget::itemDoubleClicked, - [&](auto* item, int col){ onOverwrittenActivated(item, col); }); + ui->overwrittenTree, &QTreeView::doubleClicked, + [&](auto&& item){ onOverwrittenActivated(item); }); QObject::connect( - ui->overwriteTree, &QTreeWidget::customContextMenuRequested, + ui->overwriteTree, &QTreeView::customContextMenuRequested, [&](const QPoint& p){ m_tab->showContextMenu(p, ui->overwriteTree); }); QObject::connect( - ui->overwrittenTree, &QTreeWidget::customContextMenuRequested, + ui->overwrittenTree, &QTreeView::customContextMenuRequested, [&](const QPoint& p){ m_tab->showContextMenu(p, ui->overwrittenTree); }); QObject::connect( - ui->noConflictTree, &QTreeWidget::customContextMenuRequested, + ui->noConflictTree, &QTreeView::customContextMenuRequested, [&](const QPoint& p){ m_tab->showContextMenu(p, ui->noConflictTree); }); } void GeneralConflictsTab::clear() { - ui->overwriteTree->clear(); - ui->overwrittenTree->clear(); - ui->noConflictTree->clear(); + m_overwriteModel->clear(); + m_overwrittenModel->clear(); + m_noConflictModel->clear(); ui->overwriteCount->display(0); ui->overwrittenCount->display(0); @@ -568,10 +837,6 @@ bool GeneralConflictsTab::update() int numOverwrite = 0; int numOverwritten = 0; - QList overwriteItems; - QList overwrittenItems; - QList noConflictItems; - if (m_tab->origin() != nullptr) { const auto rootPath = m_tab->mod()->absolutePath(); @@ -585,19 +850,19 @@ bool GeneralConflictsTab::update() if (fileOrigin == m_tab->origin()->getID()) { if (!alternatives.empty()) { - overwriteItems.append(createOverwriteItem( - file->getIndex(), archive, fileName, relativeName, alternatives)); + m_overwriteModel->add(createOverwriteItem( + file->getIndex(), archive, fileName, relativeName, alternatives)); ++numOverwrite; } else { // otherwise, put the file in the noconflict tree - noConflictItems.append(createNoConflictItem( + m_noConflictModel->add(createNoConflictItem( file->getIndex(), archive, fileName, relativeName)); ++numNonConflicting; } } else { - overwrittenItems.append(createOverwrittenItem( + m_overwrittenModel->add(createOverwrittenItem( file->getIndex(), fileOrigin, archive, fileName, relativeName)); ++numOverwritten; @@ -605,9 +870,9 @@ bool GeneralConflictsTab::update() } } - ui->overwriteTree->addTopLevelItems(overwriteItems); - ui->overwrittenTree->addTopLevelItems(overwrittenItems); - ui->noConflictTree->addTopLevelItems(noConflictItems); + m_overwriteModel->finished(); + m_overwrittenModel->finished(); + m_noConflictModel->finished(); ui->overwriteCount->display(numOverwrite); ui->overwrittenCount->display(numOverwritten); @@ -616,7 +881,7 @@ bool GeneralConflictsTab::update() return (numOverwrite > 0 || numOverwritten > 0); } -QTreeWidgetItem* GeneralConflictsTab::createOverwriteItem( +ConflictItem GeneralConflictsTab::createOverwriteItem( FileEntry::Index index, bool archive, const QString& fileName, const QString& relativeName, const FileEntry::AlternativesVector& alternatives) @@ -638,17 +903,17 @@ QTreeWidgetItem* GeneralConflictsTab::createOverwriteItem( const auto origin = ToQString(ds.getOriginByID(alternatives.back().first).getName()); - return new ConflictItem(fields, index, fileName, true, origin, archive); + return {altString, relativeName, "", index, fileName, true, origin, archive}; } -QTreeWidgetItem* GeneralConflictsTab::createNoConflictItem( +ConflictItem GeneralConflictsTab::createNoConflictItem( FileEntry::Index index, bool archive, const QString& fileName, const QString& relativeName) { - return new ConflictItem({relativeName}, index, fileName, false, "", archive); + return {"", relativeName, "", index, fileName, false, "", archive}; } -QTreeWidgetItem* GeneralConflictsTab::createOverwrittenItem( +ConflictItem GeneralConflictsTab::createOverwrittenItem( FileEntry::Index index, int fileOrigin, bool archive, const QString& fileName, const QString& relativeName) { @@ -659,193 +924,53 @@ QTreeWidgetItem* GeneralConflictsTab::createOverwrittenItem( QStringList fields(relativeName); fields.append(ToQString(realOrigin.getName())); - return new ConflictItem( - fields, index, fileName, true, ToQString(realOrigin.getName()), archive); + return { + "", relativeName, ToQString(realOrigin.getName()), + index, fileName, true, ToQString(realOrigin.getName()), archive}; } -void GeneralConflictsTab::onOverwriteActivated(QTreeWidgetItem *item, int) +void GeneralConflictsTab::onOverwriteActivated(const QModelIndex& index) { - if (auto* ci=dynamic_cast(item)) { - const auto origin = ci->altOrigin(); - - if (!origin.isEmpty()) { - emit modOpen(origin); - } + auto* model = dynamic_cast(ui->overwriteTree->model()); + if (!model) { + return; } -} -void GeneralConflictsTab::onOverwrittenActivated(QTreeWidgetItem *item, int) -{ - if (const auto* ci=dynamic_cast(item)) { - const auto origin = ci->altOrigin(); + auto* item = model->getItem(static_cast(index.row())); + if (!item) { + return; + } - if (!origin.isEmpty()) { - emit modOpen(origin); - } + const auto origin = item->altOrigin(); + if (!origin.isEmpty()) { + emit modOpen(origin); } } - -class AdvancedListModel : public QAbstractItemModel +void GeneralConflictsTab::onOverwrittenActivated(const QModelIndex& index) { -public: - struct Item - { - QString before, relativeName, after; - FileEntry::Index index; - QString fileName; - bool hasAltOrigins; - QString altOrigin; - bool archive; - }; - - void clear() - { - m_items.clear(); - } - - void reserve(std::size_t s) - { - m_items.reserve(s); - } - - QModelIndex index(int row, int col, const QModelIndex& ={}) const override - { - return createIndex(row, col); - } - - QModelIndex parent(const QModelIndex&) const override - { - return {}; - } - - int rowCount(const QModelIndex& parent={}) const override - { - if (parent.isValid()) { - return 0; - } - - return static_cast(m_items.size()); - } - - int columnCount(const QModelIndex& ={}) const override - { - return 3; - } - - QVariant data(const QModelIndex& index, int role) const override - { - if (role == Qt::DisplayRole) { - const auto row = index.row(); - if (row < 0) { - return {}; - } - - const auto i = static_cast(row); - if (i >= m_items.size()) { - return {}; - } - - const auto& item = m_items[i]; - - if (index.column() == 0) { - return item.before; - } else if (index.column() == 1) { - return item.relativeName; - } else if (index.column() == 2) { - return item.after; - } - } - - return {}; - } - - QVariant headerData(int col, Qt::Orientation, int role) const - { - if (role == Qt::DisplayRole) { - if (col == 0) { - return tr("Overwrites"); - } else if (col == 1) { - return tr("File"); - } else if (col == 2) { - return tr("Overwritten by"); - } - } - - return {}; - } - - void sort(int col, Qt::SortOrder order=Qt::AscendingOrder) - { - // avoids branching on column/sort order while sorting - auto sortBeforeAsc = [](const auto& a, const auto& b) { - return (naturalCompare(a.before, b.before) < 0); - }; - - auto sortBeforeDesc = [](const auto& a, const auto& b) { - return (naturalCompare(a.before, b.before) > 0); - }; - - auto sortFileAsc = [](const auto& a, const auto& b) { - return (naturalCompare(a.relativeName, b.relativeName) < 0); - }; - - auto sortFileDesc = [](const auto& a, const auto& b) { - return (naturalCompare(a.relativeName, b.relativeName) > 0); - }; - - auto sortAfterAsc = [](const auto& a, const auto& b) { - return (naturalCompare(a.after, b.after) < 0); - }; - - auto sortAfterDesc = [](const auto& a, const auto& b) { - return (naturalCompare(a.after, b.after) > 0); - }; - - if (col == 0) { - if (order == Qt::AscendingOrder) { - std::sort(m_items.begin(), m_items.end(), sortBeforeAsc); - } else { - std::sort(m_items.begin(), m_items.end(), sortBeforeDesc); - } - } else if (col == 1) { - if (order == Qt::AscendingOrder) { - std::sort(m_items.begin(), m_items.end(), sortFileAsc); - } else { - std::sort(m_items.begin(), m_items.end(), sortFileDesc); - } - } else if (col == 2) { - if (order == Qt::AscendingOrder) { - std::sort(m_items.begin(), m_items.end(), sortAfterAsc); - } else { - std::sort(m_items.begin(), m_items.end(), sortAfterDesc); - } - } - - emit layoutChanged({}, QAbstractItemModel::VerticalSortHint); + auto* model = dynamic_cast(ui->overwrittenTree->model()); + if (!model) { + return; } - void add(Item item) - { - m_items.emplace_back(std::move(item)); + auto* item = model->getItem(static_cast(index.row())); + if (!item) { + return; } - void finished() - { - emit dataChanged(index(0, 0), index(0, rowCount())); + const auto origin = item->altOrigin(); + if (!origin.isEmpty()) { + emit modOpen(origin); } - -private: - std::vector m_items; -}; +} AdvancedConflictsTab::AdvancedConflictsTab( - ConflictsTab* tab, Ui::ModInfoDialog* pui, OrganizerCore& oc) - : m_tab(tab), ui(pui), m_core(oc), m_model(new AdvancedListModel) + ConflictsTab* tab, Ui::ModInfoDialog* pui, OrganizerCore& oc) : + m_tab(tab), ui(pui), m_core(oc), + m_model(new AdvancedConflictListModel(ui->conflictsAdvancedList)) { - ui->conflictsAdvancedList->setModel(m_model); - // left-elide the overwrites column so that the nearest are visible ui->conflictsAdvancedList->setItemDelegateForColumn( 0, new ElideLeftDelegate(ui->conflictsAdvancedList)); @@ -868,9 +993,9 @@ AdvancedConflictsTab::AdvancedConflictsTab( ui->conflictsAdvancedShowNearest, &QRadioButton::clicked, [&]{ update(); }); - //QObject::connect( - // ui->conflictsAdvancedList, &QTreeView::customContextMenuRequested, - // [&](const QPoint& p){ m_tab->showContextMenu(p, ui->conflictsAdvancedList); }); + QObject::connect( + ui->conflictsAdvancedList, &QTreeView::customContextMenuRequested, + [&](const QPoint& p){ m_tab->showContextMenu(p, ui->conflictsAdvancedList); }); m_filter.set(ui->conflictsAdvancedFilter); m_filter.changed = [&]{ update(); }; @@ -938,16 +1063,20 @@ void AdvancedConflictsTab::update() const int fileOrigin = file->getOrigin(archive); const auto& alternatives = file->getAlternatives(); - addItem( + auto item = createItem( file->getIndex(), fileOrigin, archive, fileName, relativeName, alternatives); + + if (item) { + m_model->add(std::move(*item)); + } } m_model->finished(); } } -void AdvancedConflictsTab::addItem( +std::optional AdvancedConflictsTab::createItem( FileEntry::Index index, int fileOrigin, bool archive, const QString& fileName, const QString& relativeName, const MOShared::FileEntry::AlternativesVector& alternatives) @@ -1040,7 +1169,7 @@ void AdvancedConflictsTab::addItem( // if both before and after are empty, it means this file has no conflicts // at all, only display it if the user wants it if (!hasAlts) { - return; + return {}; } } @@ -1052,9 +1181,8 @@ void AdvancedConflictsTab::addItem( }); if (!matched) { - return; + return {}; } - m_model->add( - {before, relativeName, after, index, fileName, hasAlts, "", archive}); + return ConflictItem{before, relativeName, after, index, fileName, hasAlts, "", archive}; } diff --git a/src/modinfodialogconflicts.h b/src/modinfodialogconflicts.h index f499d1a4..f0c0c589 100644 --- a/src/modinfodialogconflicts.h +++ b/src/modinfodialogconflicts.h @@ -6,10 +6,12 @@ #include "filterwidget.h" #include "directoryentry.h" #include +#include class ConflictsTab; class OrganizerCore; class ConflictItem; +class ConflictListModel; class GeneralConflictsTab : public QObject { @@ -39,21 +41,25 @@ private: OrganizerCore& m_core; Expanders m_expanders; - QTreeWidgetItem* createOverwriteItem( + ConflictListModel* m_overwriteModel; + ConflictListModel* m_overwrittenModel; + ConflictListModel* m_noConflictModel; + + ConflictItem createOverwriteItem( MOShared::FileEntry::Index index, bool archive, const QString& fileName, const QString& relativeName, const MOShared::FileEntry::AlternativesVector& alternatives); - QTreeWidgetItem* createNoConflictItem( + ConflictItem createNoConflictItem( MOShared::FileEntry::Index index, bool archive, const QString& fileName, const QString& relativeName); - QTreeWidgetItem* createOverwrittenItem( + ConflictItem createOverwrittenItem( MOShared::FileEntry::Index index, int fileOrigin, bool archive, const QString& fileName, const QString& relativeName); - void onOverwriteActivated(QTreeWidgetItem* item, int); - void onOverwrittenActivated(QTreeWidgetItem *item, int); + void onOverwriteActivated(const QModelIndex& index); + void onOverwrittenActivated(const QModelIndex& index); void onOverwriteTreeContext(const QPoint &pos); void onOverwrittenTreeContext(const QPoint &pos); @@ -61,8 +67,6 @@ private: }; -class AdvancedListModel; - class AdvancedConflictsTab : public QObject { Q_OBJECT; @@ -85,9 +89,9 @@ private: Ui::ModInfoDialog* ui; OrganizerCore& m_core; FilterWidget m_filter; - AdvancedListModel* m_model; + ConflictListModel* m_model; - void addItem( + std::optional createItem( MOShared::FileEntry::Index index, int fileOrigin, bool archive, const QString& fileName, const QString& relativeName, const MOShared::FileEntry::AlternativesVector& alternatives); @@ -109,13 +113,12 @@ public: void restoreState(const Settings& s) override; bool canHandleUnmanaged() const override; - void openItems(const QList& items); - void previewItems(const QList& items); + void openItems(QTreeView* tree); + void previewItems(QTreeView* tree); - void changeItemsVisibility( - const QList& items, bool visible); + void changeItemsVisibility(QTreeView* tree, bool visible); - void showContextMenu(const QPoint &pos, QTreeWidget* tree); + void showContextMenu(const QPoint &pos, QTreeView* tree); private: struct Actions @@ -131,10 +134,8 @@ private: GeneralConflictsTab m_general; AdvancedConflictsTab m_advanced; - Actions createMenuActions(const QList& selection); - - std::vector createGotoActions( - const QList& selection); + Actions createMenuActions(QTreeView* tree); + std::vector createGotoActions(const ConflictItem* item); }; #endif // MODINFODIALOGCONFLICTS_H -- cgit v1.3.1 From 1ef0620f6e2d8a4e7d48b3b2b8eb96c3a8d3cd0d Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Tue, 25 Jun 2019 08:10:17 -0400 Subject: rework of the images tab to avoid using widgets at all, painting is done manually --- src/modinfodialog.ui | 19 +++- src/modinfodialogconflicts.cpp | 8 -- src/modinfodialogimages.cpp | 237 +++++++++++++++++++++++++++++++++-------- src/modinfodialogimages.h | 71 ++++++++++-- 4 files changed, 268 insertions(+), 67 deletions(-) (limited to 'src/modinfodialogconflicts.cpp') diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui index cdf4c80f..bcdf1007 100644 --- a/src/modinfodialog.ui +++ b/src/modinfodialog.ui @@ -177,11 +177,8 @@ 0 - - - true - - + + 0 @@ -1163,6 +1160,18 @@ p, li { white-space: pre-wrap; } QTextEdit
texteditor.h
+ + ImagesScrollArea + QScrollArea +
modinfodialogimages.h
+ 1 +
+ + ImagesThumbnails + QWidget +
modinfodialogimages.h
+ 1 +
diff --git a/src/modinfodialogconflicts.cpp b/src/modinfodialogconflicts.cpp index d1384d7a..05b69f05 100644 --- a/src/modinfodialogconflicts.cpp +++ b/src/modinfodialogconflicts.cpp @@ -887,7 +887,6 @@ ConflictItem GeneralConflictsTab::createOverwriteItem( const FileEntry::AlternativesVector& alternatives) { const auto& ds = *m_core.directoryStructure(); - QString altString; for (const auto& alt : alternatives) { @@ -898,9 +897,6 @@ ConflictItem GeneralConflictsTab::createOverwriteItem( altString += ToQString(ds.getOriginByID(alt.first).getName()); } - QStringList fields(relativeName); - fields.append(altString); - const auto origin = ToQString(ds.getOriginByID(alternatives.back().first).getName()); return {altString, relativeName, "", index, fileName, true, origin, archive}; @@ -918,12 +914,8 @@ ConflictItem GeneralConflictsTab::createOverwrittenItem( const QString& fileName, const QString& relativeName) { const auto& ds = *m_core.directoryStructure(); - const FilesOrigin &realOrigin = ds.getOriginByID(fileOrigin); - QStringList fields(relativeName); - fields.append(ToQString(realOrigin.getName())); - return { "", relativeName, ToQString(realOrigin.getName()), index, fileName, true, ToQString(realOrigin.getName()), archive}; diff --git a/src/modinfodialogimages.cpp b/src/modinfodialogimages.cpp index f4cdade8..1f5b258c 100644 --- a/src/modinfodialogimages.cpp +++ b/src/modinfodialogimages.cpp @@ -1,25 +1,68 @@ #include "modinfodialogimages.h" #include "ui_modinfodialog.h" -ScalableImage::ScalableImage(QImage original) - : m_original(std::move(original)), m_border(1) +void ImagesScrollArea::setTab(ImagesTab* tab) +{ + m_tab = tab; +} + +void ImagesScrollArea::resizeEvent(QResizeEvent* e) +{ + if (m_tab) { + m_tab->scrollAreaResized(e->size()); + } +} + + +void ImagesThumbnails::setTab(ImagesTab* tab) +{ + m_tab = tab; +} + +void ImagesThumbnails::paintEvent(QPaintEvent* e) +{ + if (m_tab) { + m_tab->paintThumbnails(e); + } +} + +void ImagesThumbnails::mousePressEvent(QMouseEvent* e) +{ + if (m_tab) { + m_tab->thumbnailsMouseEvent(e); + } +} + + +ScalableImage::ScalableImage(QString path) + : m_path(std::move(path)), m_border(1) { auto sp = sizePolicy(); sp.setHeightForWidth(true); setSizePolicy(sp); } +void ScalableImage::setImage(const QString& path) +{ + m_path = path; + m_original = {}; + m_scaled = {}; + + update(); +} + void ScalableImage::setImage(QImage image) { + m_path.clear(); m_original = std::move(image); m_scaled = {}; update(); } -const QImage& ScalableImage::image() const +void ScalableImage::clear() { - return m_original; + setImage(QImage()); } bool ScalableImage::hasHeightForWidth() const @@ -35,7 +78,15 @@ int ScalableImage::heightForWidth(int w) const void ScalableImage::paintEvent(QPaintEvent* e) { if (m_original.isNull()) { - return; + if (m_path.isNull()) { + return; + } + + m_original.load(m_path); + + if (m_original.isNull()) { + return; + } } const QRect widgetRect = rect(); @@ -72,83 +123,177 @@ void ScalableImage::paintEvent(QPaintEvent* e) painter.drawImage(drawImageRect, m_scaled); } -void ScalableImage::mousePressEvent(QMouseEvent* e) -{ - if (e->button() == Qt::LeftButton) { - emit clicked(m_original); - } -} - ImagesTab::ImagesTab( OrganizerCore& oc, PluginContainer& plugin, QWidget* parent, Ui::ModInfoDialog* ui, int id) : ModInfoDialogTab(oc, plugin, parent, ui, id), - m_image(new ScalableImage) + m_image(new ScalableImage), m_margins(3), m_padding(5), m_border(1) { ui->imagesImage->layout()->addWidget(m_image); - ui->imagesThumbnails->setLayout(new QVBoxLayout); + delete ui->imagesThumbnails->layout(); ui->tabImagesSplitter->setSizes({128, 1}); ui->tabImagesSplitter->setStretchFactor(0, 0); ui->tabImagesSplitter->setStretchFactor(1, 1); + + ui->imagesScrollArea->setWidgetResizable(false); + ui->imagesScrollArea->setTab(this); + ui->imagesThumbnails->setTab(this); } void ImagesTab::clear() { - m_image->setImage({}); + m_image->clear(); + m_files.clear(); + setHasData(false); +} - while (ui->imagesThumbnails->layout()->count() > 0) { - auto* item = ui->imagesThumbnails->layout()->takeAt(0); - delete item->widget(); - delete item; +bool ImagesTab::feedFile(const QString& rootPath, const QString& fullPath) +{ + QImageReader reader(fullPath); + if (reader.canRead()) { + m_files.push_back({fullPath}); + return true; } - static_cast(ui->imagesThumbnails->layout())->addStretch(1); - setHasData(false); + return false; } -bool ImagesTab::feedFile(const QString& rootPath, const QString& fullPath) +void ImagesTab::update() +{ + setHasData(!m_files.empty()); +} + +void ImagesTab::scrollAreaResized(const QSize& s) { - static constexpr const char* extensions[] = { - ".png", ".jpg" - }; + const int availableWidth = s.width(); - for (const auto* e : extensions) { - if (fullPath.endsWith(e, Qt::CaseInsensitive)) { - if (add(fullPath)) { - setHasData(true); + const int thumbSize = availableWidth - (m_margins * 2); + + int height = 0; + if (!m_files.empty()) { + height = thumbSize + static_cast((m_padding + thumbSize) * (m_files.size() - 1)); + } + + height += (m_margins * 2); + + qDebug() << "new size: " << availableWidth << "x" << height; + + ui->imagesThumbnails->setGeometry(QRect(0, 0, availableWidth, height)); +} + +void ImagesTab::paintThumbnails(QPaintEvent* e) +{ + const auto availableRect = ui->imagesThumbnails->rect(); + const int thumbSize = availableRect.width() - (m_margins * 2); + + const QRect topRect( + availableRect.left() + m_margins, + availableRect.top() + m_margins, + thumbSize, thumbSize); + + const std::size_t begin = e->rect().top() / (thumbSize + m_padding); + const std::size_t end = e->rect().bottom() / (thumbSize + m_padding) + 1; + + QPainter painter(ui->imagesThumbnails); + + for (std::size_t i=begin; i(topRect.top() + (i * (thumbSize + m_padding))), + thumbSize, thumbSize); + + painter.setPen(QColor(Qt::black)); + painter.drawRect(borderRect); + + auto& file = m_files[i]; + if (file.failed) { + continue; + } + + const QRect thumbRect = borderRect.adjusted( + m_border, m_border, -m_border, -m_border); + + if (needsReload(file, thumbRect.size())) { + if (!file.original.load(file.path)) { + qCritical() << "failed to load image from " << file.path; + file.failed = true; + continue; } - return true; + file.thumbnail = file.original.scaled( + scaledImageSize(file.original.size(), thumbRect.size()), + Qt::IgnoreAspectRatio, Qt::SmoothTransformation); } - } - return false; + if (file.thumbnail.isNull()) { + continue; + } + + const QRect scaledThumbRect( + (thumbRect.left()+thumbRect.width()/2) - file.thumbnail.width()/2, + (thumbRect.top()+thumbRect.height()/2) - file.thumbnail.height()/2, + file.thumbnail.width(), file.thumbnail.height()); + + painter.drawImage(scaledThumbRect, file.thumbnail); + } } -bool ImagesTab::add(const QString& fullPath) +void ImagesTab::thumbnailsMouseEvent(QMouseEvent* e) { - QImage image = QImage(fullPath); + if (e->button() != Qt::LeftButton) { + return; + } - if (image.isNull()) { - qWarning() << "ImagesTab: '" << fullPath << "' is not a valid image"; - return false; + const auto availableRect = ui->imagesThumbnails->rect(); + const int thumbSize = availableRect.width() - (m_margins * 2); + + const QRect topRect( + availableRect.left() + m_margins, + availableRect.top() + m_margins, + thumbSize, thumbSize); + + const std::size_t i = e->y() / (thumbSize + m_padding); + if (i >= m_files.size()) { + return; } - auto* thumbnail = new ScalableImage(std::move(image)); + if (e->x() < topRect.left() || e->x() > (topRect.right() + 1)) { + return; + } - QObject::connect( - thumbnail, &ScalableImage::clicked, - [&](const QImage& image){ onClicked(image); }); + m_image->setImage(m_files[i].original); +} - static_cast(ui->imagesThumbnails->layout())->insertWidget( - ui->imagesThumbnails->layout()->count() - 1, thumbnail); +bool ImagesTab::needsReload(const File& file, const QSize& thumbSize) const +{ + if (file.failed) { + return false; + } - return true; + if (file.original.isNull() || file.thumbnail.isNull()) { + return true; + } + + if (file.thumbnail.size() != scaledImageSize(file.original.size(), thumbSize)) { + return true; + } + + return false; } -void ImagesTab::onClicked(const QImage& original) +QSize ImagesTab::scaledImageSize( + const QSize& originalSize, const QSize& thumbSize) const { - m_image->setImage(original); + const auto ratio = std::min({ + 1.0, + static_cast(thumbSize.width()) / originalSize.width(), + static_cast(thumbSize.height()) / originalSize.height()}); + + const QSize scaledSize( + static_cast(std::round(originalSize.width() * ratio)), + static_cast(std::round(originalSize.height() * ratio))); + + return scaledSize; } diff --git a/src/modinfodialogimages.h b/src/modinfodialogimages.h index 6603660a..1c65d8bd 100644 --- a/src/modinfodialogimages.h +++ b/src/modinfodialogimages.h @@ -2,28 +2,62 @@ #define MODINFODIALOGIMAGES_H #include "modinfodialogtab.h" +#include + +class ImagesTab; + +class ImagesScrollArea : public QScrollArea +{ + Q_OBJECT; + +public: + using QScrollArea::QScrollArea; + void setTab(ImagesTab* tab); + +protected: + void resizeEvent(QResizeEvent* e) override; + +private: + ImagesTab* m_tab = nullptr; +}; + + +class ImagesThumbnails : public QWidget +{ + Q_OBJECT; + +public: + using QWidget::QWidget; + void setTab(ImagesTab* tab); + +protected: + void paintEvent(QPaintEvent* e) override; + void mousePressEvent(QMouseEvent* e) override; + +private: + ImagesTab* m_tab = nullptr; +}; + class ScalableImage : public QWidget { Q_OBJECT; public: - ScalableImage(QImage image={}); + ScalableImage(QString path={}); + void setImage(const QString& path); void setImage(QImage image); - const QImage& image() const; + void clear(); bool hasHeightForWidth() const; int heightForWidth(int w) const; -signals: - void clicked(const QImage& image); - protected: void paintEvent(QPaintEvent* e) override; - void mousePressEvent(QMouseEvent* e) override; private: + QString m_path; QImage m_original, m_scaled; int m_border; }; @@ -32,6 +66,8 @@ private: class ImagesTab : public ModInfoDialogTab { Q_OBJECT; + friend class ImagesScrollArea; + friend class ImagesThumbnails; public: ImagesTab( @@ -40,12 +76,31 @@ public: void clear() override; bool feedFile(const QString& rootPath, const QString& fullPath) override; + void update() override; private: + struct File + { + QString path; + QImage original, thumbnail; + bool failed = false; + + File(QString path) + : path(std::move(path)) + { + } + }; + ScalableImage* m_image; + std::vector m_files; + int m_margins, m_padding, m_border; + + void scrollAreaResized(const QSize& s); + void paintThumbnails(QPaintEvent* e); + void thumbnailsMouseEvent(QMouseEvent* e); - bool add(const QString& fullPath); - void onClicked(const QImage& image); + bool needsReload(const File& file, const QSize& thumbSize) const; + QSize scaledImageSize(const QSize& originalSize, const QSize& thumbSize) const; }; #endif // MODINFODIALOGIMAGES_H -- cgit v1.3.1 From b6d8f4e479e42e401be60f522a0496c1f5624089 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Tue, 25 Jun 2019 10:16:12 -0400 Subject: custom models for esp lists --- src/modinfodialog.ui | 4 +- src/modinfodialogconflicts.cpp | 2 +- src/modinfodialogesps.cpp | 245 +++++++++++++++++++++++++++++------------ src/modinfodialogesps.h | 9 +- src/modinfodialogtextfiles.cpp | 3 +- 5 files changed, 184 insertions(+), 79 deletions(-) (limited to 'src/modinfodialogconflicts.cpp') diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui index bcdf1007..a152c292 100644 --- a/src/modinfodialog.ui +++ b/src/modinfodialog.ui @@ -244,7 +244,7 @@
- + List of esps, esms, and esls that can not be loaded by the game. @@ -374,7 +374,7 @@ Most mods do not have optional esps, so chances are good you are looking at an e - + ESPs in the data directory and thus visible to the game. diff --git a/src/modinfodialogconflicts.cpp b/src/modinfodialogconflicts.cpp index 05b69f05..03589030 100644 --- a/src/modinfodialogconflicts.cpp +++ b/src/modinfodialogconflicts.cpp @@ -135,6 +135,7 @@ public: void clear() { m_items.clear(); + endResetModel(); } void reserve(std::size_t s) @@ -261,7 +262,6 @@ public: void finished() { - beginResetModel(); endResetModel(); } diff --git a/src/modinfodialogesps.cpp b/src/modinfodialogesps.cpp index 6c4fc4dd..3f742bae 100644 --- a/src/modinfodialogesps.cpp +++ b/src/modinfodialogesps.cpp @@ -1,13 +1,14 @@ #include "modinfodialogesps.h" #include "ui_modinfodialog.h" +#include "modinfodialog.h" #include using MOBase::reportError; -class ESP +class ESPItem { public: - ESP(QString rootPath, QString relativePath) + ESPItem(QString rootPath, QString relativePath) : m_rootPath(std::move(rootPath)), m_active(false) { if (relativePath.contains('/')) { @@ -16,6 +17,8 @@ public: m_activePath = relativePath; m_active = true; } + + updateFilename(); } const QString& rootPath() const @@ -32,6 +35,11 @@ public: } } + const QString& filename() const + { + return m_filename; + } + const QString& activePath() const { return m_activePath; @@ -65,6 +73,8 @@ public: m_inactivePath = QFileInfo(m_inactivePath).path() + QDir::separator() + newName; } + updateFilename(); + return true; } @@ -78,6 +88,7 @@ public: if (root.rename(m_activePath, newName)) { m_active = false; m_inactivePath = newName; + updateFilename(); return true; } @@ -88,45 +99,137 @@ private: QString m_rootPath; QString m_activePath; QString m_inactivePath; + QString m_filename; bool m_active; + + void updateFilename() + { + m_filename = fileInfo().fileName(); + } }; -class ESPItem : public QListWidgetItem +class ESPListModel : public QAbstractItemModel { public: - ESPItem(ESP esp) - : m_esp(std::move(esp)) + void clear() { - updateText(); + m_esps.clear(); + endResetModel(); } - const ESP& esp() const + QModelIndex index(int row, int col, const QModelIndex& ={}) const override { - return m_esp; + return createIndex(row, col); } - void setESP(ESP esp) + QModelIndex parent(const QModelIndex&) const override { - m_esp = esp; - updateText(); + return {}; } -private: - ESP m_esp; + int rowCount(const QModelIndex& ={}) const override + { + return static_cast(m_esps.size()); + } + + int columnCount(const QModelIndex& ={}) const override + { + return 1; + } + + QVariant data(const QModelIndex& index, int role) const override + { + if (role == Qt::DisplayRole) { + if (auto* esp=getESP(index)) { + return esp->filename(); + } + } + + return {}; + } + + + void add(ESPItem esp) + { + m_esps.emplace_back(std::move(esp)); + } + + void addOne(ESPItem esp) + { + const auto i = m_esps.size(); + + beginInsertRows({}, static_cast(i), static_cast(i)); + add(std::move(esp)); + endInsertRows(); + } + + bool removeRows(int row, int count, const QModelIndex& = {}) override + { + if (row < 0) { + return false; + } + + const auto start = static_cast(row); + if (start >= m_esps.size()) { + return false; + } + + const auto end = std::min( + start + static_cast(count), + m_esps.size()); + + beginRemoveRows({}, static_cast(start), static_cast(end)); + m_esps.erase(m_esps.begin() + start, m_esps.begin() + end); + endRemoveRows(); + + return true; + } - void updateText() + void finished() { - setText(m_esp.fileInfo().fileName()); + std::sort(m_esps.begin(), m_esps.end(), [](const auto& a, const auto& b) { + return (naturalCompare(a.filename(), b.filename()) < 0); + }); + + endResetModel(); + } + + const ESPItem* getESP(const QModelIndex& index) const + { + const auto row = index.row(); + if (row < 0) { + return nullptr; + } + + const auto i = static_cast(row); + if (i >= m_esps.size()) { + return nullptr; + } + + return &m_esps[i]; } + + ESPItem* getESP(const QModelIndex& index) + { + return const_cast(std::as_const(*this).getESP(index)); + } + +private: + std::deque m_esps; }; + ESPsTab::ESPsTab( OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int id) - : ModInfoDialogTab(oc, plugin, parent, ui, id) + QWidget* parent, Ui::ModInfoDialog* ui, int id) : + ModInfoDialogTab(oc, plugin, parent, ui, id), + m_inactiveModel(new ESPListModel), m_activeModel(new ESPListModel) { + ui->inactiveESPList->setModel(m_inactiveModel); + ui->activeESPList->setModel(m_activeModel); + QObject::connect( ui->activateESP, &QToolButton::clicked, [&]{ onActivate(); }); @@ -136,8 +239,8 @@ ESPsTab::ESPsTab( void ESPsTab::clear() { - ui->inactiveESPList->clear(); - ui->activeESPList->clear(); + m_inactiveModel->clear(); + m_activeModel->clear(); setHasData(false); } @@ -151,12 +254,12 @@ bool ESPsTab::feedFile(const QString& rootPath, const QString& fullPath) if (fullPath.endsWith(e, Qt::CaseInsensitive)) { QString relativePath = fullPath.mid(rootPath.length() + 1); - auto* item = new ESPItem({rootPath, relativePath}); + ESPItem esp(rootPath, relativePath); - if (item->esp().isActive()) { - ui->activeESPList->addItem(item); + if (esp.isActive()) { + m_activeModel->add(std::move(esp)); } else { - ui->inactiveESPList->addItem(item); + m_inactiveModel->add(std::move(esp)); } setHasData(true); @@ -167,23 +270,31 @@ bool ESPsTab::feedFile(const QString& rootPath, const QString& fullPath) return false; } +void ESPsTab::update() +{ + m_inactiveModel->finished(); + m_activeModel->finished(); +} + void ESPsTab::onActivate() { - auto* item = selectedInactive(); - if (!item) { - qWarning("ESPsTab::onActivate(): no selection"); + const auto index = ui->inactiveESPList->currentIndex(); + if (!index.isValid()) { return; } - ESP esp = item->esp(); + auto* esp = m_inactiveModel->getESP(index); + if (!esp) { + return; + } - if (esp.isActive()) { + if (esp->isActive()) { qWarning("ESPsTab::onActive(): item is already active"); return; } - QDir root(esp.rootPath()); - const QFileInfo file(esp.fileInfo()); + QDir root(esp->rootPath()); + const QFileInfo file(esp->fileInfo()); QString newName = file.fileName(); @@ -205,10 +316,12 @@ void ESPsTab::onActivate() } } - if (esp.activate(newName)) { - ui->inactiveESPList->takeItem(ui->inactiveESPList->row(item)); - ui->activeESPList->addItem(item); - item->setESP(esp); + if (esp->activate(newName)) { + // copy esp, original will be destroyed + auto copy = *esp; + m_inactiveModel->removeRow(index.row()); + m_activeModel->addOne(std::move(copy)); + selectRow(ui->inactiveESPList, index.row()); } else { reportError(QObject::tr("Failed to move file")); } @@ -216,26 +329,28 @@ void ESPsTab::onActivate() void ESPsTab::onDeactivate() { - auto* item = selectedActive(); - if (!item) { - qWarning("ESPsTab::onDeactivate(): no selection"); + const auto index = ui->activeESPList->currentIndex(); + if (!index.isValid()) { return; } - ESP esp = item->esp(); + auto* esp = m_activeModel->getESP(index); + if (!esp) { + return; + } - if (!esp.isActive()) { + if (!esp->isActive()) { qWarning("ESPsTab::onDeactivate(): item is already inactive"); return; } - QDir root(esp.rootPath()); + QDir root(esp->rootPath()); // if we moved the file from optional to active in this session, we move the // file back to where it came from. Otherwise, it is moved to the new folder // "optional" - QString newName = esp.inactivePath(); + QString newName = esp->inactivePath(); if (newName.isEmpty()) { if (!root.exists("optional")) { @@ -245,46 +360,32 @@ void ESPsTab::onDeactivate() } } - newName = QString("optional") + QDir::separator() + esp.fileInfo().fileName(); + newName = QString("optional") + QDir::separator() + esp->fileInfo().fileName(); } - if (esp.deactivate(newName)) { - ui->activeESPList->takeItem(ui->activeESPList->row(item)); - ui->inactiveESPList->addItem(item); - item->setESP(esp); + if (esp->deactivate(newName)) { + // copy esp, original will be destroyed + auto copy = *esp; + + m_activeModel->removeRow(index.row()); + m_inactiveModel->addOne(std::move(copy)); + selectRow(ui->activeESPList, index.row()); } else { reportError(QObject::tr("Failed to move file")); } } -ESPItem* ESPsTab::selectedInactive() -{ - auto* item = ui->inactiveESPList->currentItem(); - if (!item) { - return nullptr; - } - - auto* esp = dynamic_cast(item); - if (!esp) { - qCritical("ESPsTab: inactive item is not an ESPItem"); - return nullptr; - } - - return esp; -} - -ESPItem* ESPsTab::selectedActive() +void ESPsTab::selectRow(QListView* list, int row) { - auto* item = ui->activeESPList->currentItem(); - if (!item) { - return nullptr; + const auto* model = list->model(); + const auto count = model->rowCount(); + if (count == 0) { + return; } - auto* esp = dynamic_cast(item); - if (!esp) { - qCritical("ESPsTab: active item is not an ESPItem"); - return nullptr; + if (row >= count) { + list->setCurrentIndex(model->index(count - 1, 0)); + } else { + list->setCurrentIndex(model->index(row, 0)); } - - return esp; } diff --git a/src/modinfodialogesps.h b/src/modinfodialogesps.h index e82ed368..c972074a 100644 --- a/src/modinfodialogesps.h +++ b/src/modinfodialogesps.h @@ -4,6 +4,7 @@ #include "modinfodialogtab.h" class ESPItem; +class ESPListModel; class ESPsTab : public ModInfoDialogTab { @@ -16,13 +17,15 @@ public: void clear() override; bool feedFile(const QString& rootPath, const QString& fullPath) override; + void update(); private: + ESPListModel* m_inactiveModel; + ESPListModel* m_activeModel; + void onActivate(); void onDeactivate(); - - ESPItem* selectedInactive(); - ESPItem* selectedActive(); + void selectRow(QListView* list, int row); }; #endif // MODINFODIALOGESPS_H diff --git a/src/modinfodialogtextfiles.cpp b/src/modinfodialogtextfiles.cpp index 08b8c988..25d799b1 100644 --- a/src/modinfodialogtextfiles.cpp +++ b/src/modinfodialogtextfiles.cpp @@ -9,6 +9,7 @@ public: void clear() { m_files.clear(); + endResetModel(); } QModelIndex index(int row, int col, const QModelIndex& ={}) const override @@ -62,7 +63,7 @@ public: return (naturalCompare(a.text, b.text) < 0); }); - emit dataChanged(index(0, 0), index(0, rowCount())); + endResetModel(); } QString fullPath(const QModelIndex& index) const -- cgit v1.3.1 From 139c33ccc4f529083b0288907caad2946a3f5a8f Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Tue, 25 Jun 2019 11:41:00 -0400 Subject: various optimizations and caching fixed conflict list not sorting when changing parameters switched from QDirIterator to std::filesystem, much faster FilterWidget precompiles the list --- src/filterwidget.cpp | 19 +++-- src/filterwidget.h | 3 + src/modinfodialog.cpp | 12 ++- src/modinfodialogconflicts.cpp | 186 ++++++++++++++++++++++++----------------- src/modinfodialogconflicts.h | 8 +- src/modinfodialogesps.cpp | 26 +++--- src/modinfodialogtextfiles.cpp | 15 ++-- 7 files changed, 155 insertions(+), 114 deletions(-) (limited to 'src/modinfodialogconflicts.cpp') diff --git a/src/filterwidget.cpp b/src/filterwidget.cpp index 16a46b0e..82644658 100644 --- a/src/filterwidget.cpp +++ b/src/filterwidget.cpp @@ -30,21 +30,29 @@ void FilterWidget::clear() m_edit->clear(); } -bool FilterWidget::matches(std::function pred) const +void FilterWidget::compile() { + m_compiled.clear(); + const QStringList ORList = [&] { QString filterCopy = QString(m_text); filterCopy.replace("||", ";").replace("OR", ";").replace("|", ";"); return filterCopy.split(";", QString::SkipEmptyParts); }(); - if (ORList.isEmpty() || !pred) { + // split in ORSegments that internally use AND logic + for (auto& ORSegment : ORList) { + m_compiled.push_back(ORSegment.split(" ", QString::SkipEmptyParts)); + } +} + +bool FilterWidget::matches(std::function pred) const +{ + if (m_compiled.isEmpty() || !pred) { return true; } - // split in ORSegments that internally use AND logic - for (auto& ORSegment : ORList) { - QStringList ANDKeywords = ORSegment.split(" ", QString::SkipEmptyParts); + for (auto& ANDKeywords : m_compiled) { bool segmentGood = true; // check each word in the segment for match, each word needs to be matched @@ -115,6 +123,7 @@ void FilterWidget::onTextChanged() if (text != m_text) { m_text = text; + compile(); if (changed) { changed(); diff --git a/src/filterwidget.h b/src/filterwidget.h index 4fee5983..762d9b15 100644 --- a/src/filterwidget.h +++ b/src/filterwidget.h @@ -20,6 +20,7 @@ private: EventFilter* m_eventFilter; QToolButton* m_clear; QString m_text; + QList> m_compiled; void unhook(); void createClear(); @@ -28,6 +29,8 @@ private: void onTextChanged(); void onResized(); + + void compile(); }; #endif // FILTERWIDGET_H diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index a407e9b7..c6cdb961 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -29,6 +29,7 @@ along with Mod Organizer. If not, see . #include "modinfodialogcategories.h" #include "modinfodialognexus.h" #include "modinfodialogfiletree.h" +#include using namespace MOBase; using namespace MOShared; @@ -310,12 +311,17 @@ void ModInfoDialog::updateTabs(bool becauseOriginChanged) void ModInfoDialog::feedFiles(bool becauseOriginChanged) { + namespace fs = std::filesystem; const auto rootPath = m_mod->absolutePath(); if (rootPath.length() > 0) { - QDirIterator dirIterator(rootPath, QDir::Files, QDirIterator::Subdirectories); - while (dirIterator.hasNext()) { - QString fileName = dirIterator.next(); + + for (const auto& entry : fs::recursive_directory_iterator(rootPath.toStdString())) { + if (!entry.is_regular_file()) { + continue; + } + + const auto fileName = QString::fromWCharArray(entry.path().c_str()); for (auto& tabInfo : m_tabs) { if (!tabInfo.isVisible()) { diff --git a/src/modinfodialogconflicts.cpp b/src/modinfodialogconflicts.cpp index 03589030..52d25954 100644 --- a/src/modinfodialogconflicts.cpp +++ b/src/modinfodialogconflicts.cpp @@ -126,8 +126,9 @@ public: const QString& (ConflictItem::*getText)() const; }; - ConflictListModel(QTreeView* tree, std::vector columns) - : m_tree(tree), m_columns(std::move(columns)) + ConflictListModel(QTreeView* tree, std::vector columns) : + m_tree(tree), m_columns(std::move(columns)), + m_sortColumn(-1), m_sortOrder(Qt::AscendingOrder) { m_tree->setModel(this); } @@ -226,32 +227,10 @@ public: void sort(int colIndex, Qt::SortOrder order=Qt::AscendingOrder) { - if (colIndex < 0) { - return; - } - - const auto c = static_cast(colIndex); - if (c >= m_columns.size()) { - return; - } - - const auto& col = m_columns[c]; - - // avoids branching on sort order while sorting - auto sortAsc = [&](const auto& a, const auto& b) { - return (naturalCompare((a.*col.getText)(), (b.*col.getText)()) < 0); - }; - - auto sortDesc = [&](const auto& a, const auto& b) { - return (naturalCompare((a.*col.getText)(), (b.*col.getText)()) > 0); - }; - - if (order == Qt::AscendingOrder) { - std::sort(m_items.begin(), m_items.end(), sortAsc); - } else { - std::sort(m_items.begin(), m_items.end(), sortDesc); - } + m_sortColumn = colIndex; + m_sortOrder = order; + doSort(); emit layoutChanged({}, QAbstractItemModel::VerticalSortHint); } @@ -263,6 +242,7 @@ public: void finished() { endResetModel(); + doSort(); } const ConflictItem* getItem(std::size_t row) const @@ -278,6 +258,37 @@ private: QTreeView* m_tree; std::vector m_columns; std::vector m_items; + int m_sortColumn; + Qt::SortOrder m_sortOrder; + + void doSort() + { + if (m_sortColumn < 0) { + return; + } + + const auto c = static_cast(m_sortColumn); + if (c >= m_columns.size()) { + return; + } + + const auto& col = m_columns[c]; + + // avoids branching on sort order while sorting + auto sortAsc = [&](const auto& a, const auto& b) { + return (naturalCompare((a.*col.getText)(), (b.*col.getText)()) < 0); + }; + + auto sortDesc = [&](const auto& a, const auto& b) { + return (naturalCompare((a.*col.getText)(), (b.*col.getText)()) > 0); + }; + + if (m_sortOrder == Qt::AscendingOrder) { + std::sort(m_items.begin(), m_items.end(), sortAsc); + } else { + std::sort(m_items.begin(), m_items.end(), sortDesc); + } + } }; @@ -841,8 +852,9 @@ bool GeneralConflictsTab::update() const auto rootPath = m_tab->mod()->absolutePath(); for (const auto& file : m_tab->origin()->getFiles()) { - const QString relativeName = QDir::fromNativeSeparators(ToQString(file->getRelativePath())); - const QString fileName = relativeName.mid(0).prepend(rootPath); + // careful: these two strings are moved into createXItem() below + QString relativeName = QDir::fromNativeSeparators(ToQString(file->getRelativePath())); + QString fileName = rootPath + relativeName; bool archive = false; const int fileOrigin = file->getOrigin(archive); @@ -851,28 +863,31 @@ bool GeneralConflictsTab::update() if (fileOrigin == m_tab->origin()->getID()) { if (!alternatives.empty()) { m_overwriteModel->add(createOverwriteItem( - file->getIndex(), archive, fileName, relativeName, alternatives)); + file->getIndex(), archive, + std::move(fileName), std::move(relativeName), alternatives)); ++numOverwrite; } else { // otherwise, put the file in the noconflict tree m_noConflictModel->add(createNoConflictItem( - file->getIndex(), archive, fileName, relativeName)); + file->getIndex(), archive, + std::move(fileName), std::move(relativeName))); ++numNonConflicting; } } else { m_overwrittenModel->add(createOverwrittenItem( - file->getIndex(), fileOrigin, archive, fileName, relativeName)); + file->getIndex(), fileOrigin, archive, + std::move(fileName), std::move(relativeName))); ++numOverwritten; } } - } - m_overwriteModel->finished(); - m_overwrittenModel->finished(); - m_noConflictModel->finished(); + m_overwriteModel->finished(); + m_overwrittenModel->finished(); + m_noConflictModel->finished(); + } ui->overwriteCount->display(numOverwrite); ui->overwrittenCount->display(numOverwritten); @@ -882,43 +897,48 @@ bool GeneralConflictsTab::update() } ConflictItem GeneralConflictsTab::createOverwriteItem( - FileEntry::Index index, bool archive, - const QString& fileName, const QString& relativeName, + FileEntry::Index index, bool archive, QString fileName, QString relativeName, const FileEntry::AlternativesVector& alternatives) { const auto& ds = *m_core.directoryStructure(); - QString altString; + std::wstring altString; for (const auto& alt : alternatives) { - if (!altString.isEmpty()) { - altString += ", "; + if (!altString.empty()) { + altString += L", "; } - altString += ToQString(ds.getOriginByID(alt.first).getName()); + altString += ds.getOriginByID(alt.first).getName(); } - const auto origin = ToQString(ds.getOriginByID(alternatives.back().first).getName()); + auto origin = ToQString(ds.getOriginByID(alternatives.back().first).getName()); - return {altString, relativeName, "", index, fileName, true, origin, archive}; + return ConflictItem( + ToQString(altString), std::move(relativeName), QString::null, index, + std::move(fileName), true, std::move(origin), archive); } ConflictItem GeneralConflictsTab::createNoConflictItem( - FileEntry::Index index, bool archive, - const QString& fileName, const QString& relativeName) + FileEntry::Index index, bool archive, QString fileName, QString relativeName) { - return {"", relativeName, "", index, fileName, false, "", archive}; + return ConflictItem( + QString::null, std::move(relativeName), QString::null, index, + std::move(fileName), false, QString::null, archive); } ConflictItem GeneralConflictsTab::createOverwrittenItem( FileEntry::Index index, int fileOrigin, bool archive, - const QString& fileName, const QString& relativeName) + QString fileName, QString relativeName) { const auto& ds = *m_core.directoryStructure(); - const FilesOrigin &realOrigin = ds.getOriginByID(fileOrigin); + const FilesOrigin& realOrigin = ds.getOriginByID(fileOrigin); - return { - "", relativeName, ToQString(realOrigin.getName()), - index, fileName, true, ToQString(realOrigin.getName()), archive}; + QString after = ToQString(realOrigin.getName()); + QString altOrigin = after; + + return ConflictItem( + QString::null, std::move(relativeName), std::move(after), + index, std::move(fileName), true, std::move(altOrigin), archive); } void GeneralConflictsTab::onOverwriteActivated(const QModelIndex& index) @@ -1048,8 +1068,9 @@ void AdvancedConflictsTab::update() m_model->reserve(files.size()); for (const auto& file : files) { - const QString relativeName = QDir::fromNativeSeparators(ToQString(file->getRelativePath())); - const QString fileName = relativeName.mid(0).prepend(rootPath); + // careful: these two strings are moved into createItem() below + QString relativeName = QDir::fromNativeSeparators(ToQString(file->getRelativePath())); + QString fileName = rootPath + relativeName; bool archive = false; const int fileOrigin = file->getOrigin(archive); @@ -1057,7 +1078,7 @@ void AdvancedConflictsTab::update() auto item = createItem( file->getIndex(), fileOrigin, archive, - fileName, relativeName, alternatives); + std::move(fileName), std::move(relativeName), alternatives); if (item) { m_model->add(std::move(*item)); @@ -1070,39 +1091,41 @@ void AdvancedConflictsTab::update() std::optional AdvancedConflictsTab::createItem( FileEntry::Index index, int fileOrigin, bool archive, - const QString& fileName, const QString& relativeName, + QString fileName, QString relativeName, const MOShared::FileEntry::AlternativesVector& alternatives) { const auto& ds = *m_core.directoryStructure(); - QString before, after; + std::wstring before, after; if (!alternatives.empty()) { + const bool showAllAlts = ui->conflictsAdvancedShowAll->isChecked(); + int beforePrio = 0; int afterPrio = std::numeric_limits::max(); for (const auto& alt : alternatives) { - const auto altOrigin = ds.getOriginByID(alt.first); + const auto& altOrigin = ds.getOriginByID(alt.first); - if (ui->conflictsAdvancedShowAll->isChecked()) { + if (showAllAlts) { // fills 'before' and 'after' with all the alternatives that come // before and after this mod in terms of priority if (altOrigin.getPriority() < m_tab->origin()->getPriority()) { // add all the mods having a lower priority than this one - if (!before.isEmpty()) { - before += ", "; + if (!before.empty()) { + before += L", "; } - before += ToQString(altOrigin.getName()); + before += altOrigin.getName(); } else if (altOrigin.getPriority() > m_tab->origin()->getPriority()) { // add all the mods having a higher priority than this one - if (!after.isEmpty()) { - after += ", "; + if (!after.empty()) { + after += L", "; } - after += ToQString(altOrigin.getName()); + after += altOrigin.getName(); } } else { // keep track of the nearest mods that come before and after this one @@ -1114,7 +1137,7 @@ std::optional AdvancedConflictsTab::createItem( if (altOrigin.getPriority() > beforePrio) { // the alternative has a higher priority and therefore is closer // to this mod, use it - before = ToQString(altOrigin.getName()); + before = altOrigin.getName(); beforePrio = altOrigin.getPriority(); } } @@ -1125,7 +1148,7 @@ std::optional AdvancedConflictsTab::createItem( if (altOrigin.getPriority() < afterPrio) { // the alternative has a lower priority and there is closer // to this mod, use it - after = ToQString(altOrigin.getName()); + after = altOrigin.getName(); afterPrio = altOrigin.getPriority(); } } @@ -1140,41 +1163,46 @@ std::optional AdvancedConflictsTab::createItem( // nearest mods, it's not worth checking for the primary origin because it // will always have a higher priority than the alternatives (or it wouldn't // be the primary) - if (after.isEmpty() || ui->conflictsAdvancedShowAll->isChecked()) { - FilesOrigin &realOrigin = ds.getOriginByID(fileOrigin); + if (after.empty() || showAllAlts) { + const FilesOrigin& realOrigin = ds.getOriginByID(fileOrigin); // if no mods overwrite this file, the primary origin is the same as this // mod, so ignore that if (realOrigin.getID() != m_tab->origin()->getID()) { - if (!after.isEmpty()) { - after += ", "; + if (!after.empty()) { + after += L", "; } - after += ToQString(realOrigin.getName()); + after += realOrigin.getName(); } } } - bool hasAlts = !before.isEmpty() || !after.isEmpty(); + const bool hasAlts = !before.empty() || !after.empty(); - if (!ui->conflictsAdvancedShowNoConflict->isChecked()) { + if (!hasAlts) { // if both before and after are empty, it means this file has no conflicts // at all, only display it if the user wants it - if (!hasAlts) { + if (!ui->conflictsAdvancedShowNoConflict->isChecked()) { return {}; } } - bool matched = m_filter.matches([&](auto&& what) { + auto beforeQS = QString::fromStdWString(before); + auto afterQS = QString::fromStdWString(after); + + const bool matched = m_filter.matches([&](auto&& what) { return - before.contains(what, Qt::CaseInsensitive) || + beforeQS.contains(what, Qt::CaseInsensitive) || relativeName.contains(what, Qt::CaseInsensitive) || - after.contains(what, Qt::CaseInsensitive); + afterQS.contains(what, Qt::CaseInsensitive); }); if (!matched) { return {}; } - return ConflictItem{before, relativeName, after, index, fileName, hasAlts, "", archive}; + return ConflictItem( + std::move(beforeQS), std::move(relativeName), std::move(afterQS), + index, std::move(fileName), hasAlts, QString::null, archive); } diff --git a/src/modinfodialogconflicts.h b/src/modinfodialogconflicts.h index f0c0c589..fc7dd32a 100644 --- a/src/modinfodialogconflicts.h +++ b/src/modinfodialogconflicts.h @@ -47,16 +47,16 @@ private: ConflictItem createOverwriteItem( MOShared::FileEntry::Index index, bool archive, - const QString& fileName, const QString& relativeName, + QString fileName, QString relativeName, const MOShared::FileEntry::AlternativesVector& alternatives); ConflictItem createNoConflictItem( MOShared::FileEntry::Index index, bool archive, - const QString& fileName, const QString& relativeName); + QString fileName, QString relativeName); ConflictItem createOverwrittenItem( MOShared::FileEntry::Index index, int fileOrigin, bool archive, - const QString& fileName, const QString& relativeName); + QString fileName, QString relativeName); void onOverwriteActivated(const QModelIndex& index); void onOverwrittenActivated(const QModelIndex& index); @@ -93,7 +93,7 @@ private: std::optional createItem( MOShared::FileEntry::Index index, int fileOrigin, bool archive, - const QString& fileName, const QString& relativeName, + QString fileName, QString relativeName, const MOShared::FileEntry::AlternativesVector& alternatives); }; diff --git a/src/modinfodialogesps.cpp b/src/modinfodialogesps.cpp index 3f742bae..ecb341e8 100644 --- a/src/modinfodialogesps.cpp +++ b/src/modinfodialogesps.cpp @@ -18,7 +18,7 @@ public: m_active = true; } - updateFilename(); + pathChanged(); } const QString& rootPath() const @@ -50,9 +50,9 @@ public: return m_inactivePath; } - QFileInfo fileInfo() const + const QFileInfo& fileInfo() const { - return m_rootPath + QDir::separator() + relativePath(); + return m_fileInfo; } bool isActive() const @@ -73,7 +73,7 @@ public: m_inactivePath = QFileInfo(m_inactivePath).path() + QDir::separator() + newName; } - updateFilename(); + pathChanged(); return true; } @@ -88,7 +88,7 @@ public: if (root.rename(m_activePath, newName)) { m_active = false; m_inactivePath = newName; - updateFilename(); + pathChanged(); return true; } @@ -100,11 +100,13 @@ private: QString m_activePath; QString m_inactivePath; QString m_filename; + QFileInfo m_fileInfo; bool m_active; - void updateFilename() + void pathChanged() { - m_filename = fileInfo().fileName(); + m_fileInfo.setFile(m_rootPath + QDir::separator() + relativePath()); + m_filename = m_fileInfo.fileName(); } }; @@ -246,15 +248,11 @@ void ESPsTab::clear() bool ESPsTab::feedFile(const QString& rootPath, const QString& fullPath) { - static constexpr const char* extensions[] = { - ".esp", ".esm", ".esl" - }; + static const QString extensions[] = {".esp", ".esm", ".esl"}; - for (const auto* e : extensions) { + for (const auto& e : extensions) { if (fullPath.endsWith(e, Qt::CaseInsensitive)) { - QString relativePath = fullPath.mid(rootPath.length() + 1); - - ESPItem esp(rootPath, relativePath); + ESPItem esp(rootPath, fullPath.mid(rootPath.length() + 1)); if (esp.isActive()) { m_activeModel->add(std::move(esp)); diff --git a/src/modinfodialogtextfiles.cpp b/src/modinfodialogtextfiles.cpp index 25d799b1..675a4c79 100644 --- a/src/modinfodialogtextfiles.cpp +++ b/src/modinfodialogtextfiles.cpp @@ -202,11 +202,9 @@ TextFilesTab::TextFilesTab( bool TextFilesTab::wantsFile(const QString& rootPath, const QString& fullPath) const { - static constexpr const char* extensions[] = { - ".txt" - }; + static const QString extensions[] = {".txt"}; - for (const auto* e : extensions) { + for (const auto& e : extensions) { if (fullPath.endsWith(e, Qt::CaseInsensitive)) { return true; } @@ -226,13 +224,12 @@ IniFilesTab::IniFilesTab( bool IniFilesTab::wantsFile(const QString& rootPath, const QString& fullPath) const { - static constexpr const char* extensions[] = { - ".ini", ".cfg" - }; + static const QString extensions[] = {".ini", ".cfg"}; + static const QString meta("meta.ini"); - for (const auto* e : extensions) { + for (const auto& e : extensions) { if (fullPath.endsWith(e, Qt::CaseInsensitive)) { - if (!fullPath.endsWith("meta.ini")) { + if (!fullPath.endsWith(meta)) { return true; } } -- cgit v1.3.1 From 2b57172eb8ab23f2fab580be73c570f44500ed8d Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Tue, 25 Jun 2019 12:26:25 -0400 Subject: added path and explore button to images tab --- src/modinfodialog.h | 14 ++++++++++++++ src/modinfodialog.ui | 44 +++++++++++++++++++++++++++++++++++++++--- src/modinfodialogconflicts.cpp | 18 ++++------------- src/modinfodialogimages.cpp | 39 +++++++++++++++++++++++++++++++++---- src/modinfodialogimages.h | 3 +++ 5 files changed, 97 insertions(+), 21 deletions(-) (limited to 'src/modinfodialogconflicts.cpp') diff --git a/src/modinfodialog.h b/src/modinfodialog.h index e814dcf4..8ddaf86c 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -45,6 +45,20 @@ FileRenamer::RenameResults unhideFile(FileRenamer& renamer, const QString &oldNa int naturalCompare(const QString& a, const QString& b); +class ElideLeftDelegate : public QStyledItemDelegate +{ +public: + using QStyledItemDelegate::QStyledItemDelegate; + +protected: + void initStyleOption(QStyleOptionViewItem* o, const QModelIndex& i) const + { + QStyledItemDelegate::initStyleOption(o, i); + o->textElideMode = Qt::ElideLeft; + } +}; + + /** * this is a larger dialog used to visualise information about the mod. * @todo this would probably a good place for a plugin-system diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui index a152c292..9f4ae873 100644 --- a/src/modinfodialog.ui +++ b/src/modinfodialog.ui @@ -192,8 +192,11 @@ - - + + + + 3 + 0 @@ -206,6 +209,41 @@ 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Open in Explorer + + + + + + + true + + + + + + + + + @@ -403,7 +441,7 @@ Most mods do not have optional esps, so chances are good you are looking at an e - 0 + 1 diff --git a/src/modinfodialogconflicts.cpp b/src/modinfodialogconflicts.cpp index 52d25954..c053e64c 100644 --- a/src/modinfodialogconflicts.cpp +++ b/src/modinfodialogconflicts.cpp @@ -13,20 +13,6 @@ using namespace MOBase; const std::size_t max_small_selection = 50; -class ElideLeftDelegate : public QStyledItemDelegate -{ -public: - using QStyledItemDelegate::QStyledItemDelegate; - -protected: - void initStyleOption(QStyleOptionViewItem* o, const QModelIndex& i) const - { - QStyledItemDelegate::initStyleOption(o, i); - o->textElideMode = Qt::ElideLeft; - } -}; - - class ConflictItem { public: @@ -263,6 +249,10 @@ private: void doSort() { + if (m_items.empty()) { + return; + } + if (m_sortColumn < 0) { return; } diff --git a/src/modinfodialogimages.cpp b/src/modinfodialogimages.cpp index 5b35a9b1..22c004e6 100644 --- a/src/modinfodialogimages.cpp +++ b/src/modinfodialogimages.cpp @@ -1,13 +1,18 @@ #include "modinfodialogimages.h" #include "ui_modinfodialog.h" +#include "utility.h" ImagesTab::ImagesTab( OrganizerCore& oc, PluginContainer& plugin, QWidget* parent, Ui::ModInfoDialog* ui, int id) : ModInfoDialogTab(oc, plugin, parent, ui, id), - m_image(new ScalableImage), m_margins(3), m_padding(5), m_border(1) + m_image(new ScalableImage), m_margins(3), m_padding(5), m_border(1), + m_selection(nullptr) { - ui->imagesImage->layout()->addWidget(m_image); + auto* ly = new QVBoxLayout(ui->imagesImage); + ly->setContentsMargins({0, 0, 0, 0}); + ly->addWidget(m_image); + delete ui->imagesThumbnails->layout(); ui->tabImagesSplitter->setSizes({128, 1}); @@ -18,13 +23,15 @@ ImagesTab::ImagesTab( ui->imagesScrollArea->setTab(this); ui->imagesThumbnails->setTab(this); + connect(ui->imagesExplore, &QAbstractButton::clicked, [&]{ onExplore(); }); + getSupportedFormats(); } void ImagesTab::clear() { - m_image->clear(); m_files.clear(); + select(nullptr); setHasData(false); } @@ -64,6 +71,21 @@ void ImagesTab::getSupportedFormats() } } +void ImagesTab::select(const File* f) +{ + if (f) { + ui->imagesPath->setText(f->path); + ui->imagesExplore->setEnabled(true); + m_image->setImage(f->original); + } else { + ui->imagesPath->clear(); + ui->imagesExplore->setEnabled(false); + m_image->clear(); + } + + m_selection = f; +} + int ImagesTab::calcThumbSize(int availableWidth) const { return availableWidth - (m_margins * 2); @@ -227,10 +249,19 @@ void ImagesTab::thumbnailsMouseEvent(QMouseEvent* e) } if (const auto* file=fileAtPos(e->pos())) { - m_image->setImage(file->original); + select(file); } } +void ImagesTab::onExplore() +{ + if (!m_selection) { + return; + } + + MOBase::shell::ExploreFile(m_selection->path); +} + bool ImagesTab::needsReload(const File& file, const QSize& imageSize) const { if (file.failed) { diff --git a/src/modinfodialogimages.h b/src/modinfodialogimages.h index 9c3e5746..cb9a2d67 100644 --- a/src/modinfodialogimages.h +++ b/src/modinfodialogimages.h @@ -107,12 +107,15 @@ private: std::vector m_files; std::vector m_supportedFormats; int m_margins, m_padding, m_border; + const File* m_selection; void getSupportedFormats(); + void select(const File* file); void scrollAreaResized(const QSize& s); void paintThumbnails(QPaintEvent* e); void thumbnailsMouseEvent(QMouseEvent* e); + void onExplore(); int calcThumbSize(int availableWidth) const; int calcWidgetHeight(int availableWidth) const; -- cgit v1.3.1 From 19a19ead4059c600680d6e7ac193490e32e03245 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Tue, 25 Jun 2019 16:41:32 -0400 Subject: filterwidget now support automatically installing a proxy on lists filters on txt and ini tabs --- src/filterwidget.cpp | 70 +++++++++++++++++++++++++++++++++++++++--- src/filterwidget.h | 41 ++++++++++++++++++++++--- src/modinfodialog.ui | 8 ++++- src/modinfodialogconflicts.cpp | 4 +-- src/modinfodialogtextfiles.cpp | 13 +++++--- src/modinfodialogtextfiles.h | 4 ++- 6 files changed, 123 insertions(+), 17 deletions(-) (limited to 'src/modinfodialogconflicts.cpp') diff --git a/src/filterwidget.cpp b/src/filterwidget.cpp index 82644658..1b9efd7c 100644 --- a/src/filterwidget.cpp +++ b/src/filterwidget.cpp @@ -1,12 +1,41 @@ #include "filterwidget.h" #include "eventfilter.h" -FilterWidget::FilterWidget() - : m_edit(nullptr), m_eventFilter(nullptr), m_clear(nullptr) +FilterWidgetProxyModel::FilterWidgetProxyModel(FilterWidget& fw, QWidget* parent) + : QSortFilterProxyModel(parent), m_filter(fw) { + connect(&fw, &FilterWidget::changed, [&]{ invalidateFilter(); }); } -void FilterWidget::set(QLineEdit* edit) +bool FilterWidgetProxyModel::filterAcceptsRow( + int sourceRow, const QModelIndex& sourceParent) const +{ + const auto cols = sourceModel()->columnCount(); + + const auto m = m_filter.matches([&](auto&& what) { + for (int c=0; cindex(sourceRow, c, sourceParent); + const auto text = sourceModel()->data(index, Qt::DisplayRole).toString(); + + if (text.contains(what, Qt::CaseInsensitive)) { + return true; + } + } + + return false; + }); + + return m; +} + + +FilterWidget::FilterWidget() : + m_edit(nullptr), m_list(nullptr), m_proxy(nullptr), + m_eventFilter(nullptr), m_clear(nullptr) +{ +} + +void FilterWidget::setEdit(QLineEdit* edit) { unhook(); @@ -16,11 +45,22 @@ void FilterWidget::set(QLineEdit* edit) return; } + m_edit->setPlaceholderText(QObject::tr("Filter")); + createClear(); hookEvents(); clear(); } +void FilterWidget::setList(QAbstractItemView* list) +{ + m_list = list; + + m_proxy = new FilterWidgetProxyModel(*this); + m_proxy->setSourceModel(m_list->model()); + m_list->setModel(m_proxy); +} + void FilterWidget::clear() { if (!m_edit) { @@ -30,6 +70,16 @@ void FilterWidget::clear() m_edit->clear(); } +QModelIndex FilterWidget::map(const QModelIndex& index) +{ + if (m_proxy) { + return m_proxy->mapToSource(index); + } else { + qCritical() << "FilterWidget::map() called, but proxy isn't set up"; + return index; + } +} + void FilterWidget::compile() { m_compiled.clear(); @@ -83,6 +133,14 @@ void FilterWidget::unhook() if (m_edit) { m_edit->removeEventFilter(m_eventFilter); } + + if (m_proxy && m_list) { + auto* model = m_proxy->sourceModel(); + m_proxy->setSourceModel(nullptr); + delete m_proxy; + + m_list->setModel(model); + } } void FilterWidget::createClear() @@ -125,9 +183,11 @@ void FilterWidget::onTextChanged() m_text = text; compile(); - if (changed) { - changed(); + if (m_proxy) { + m_proxy->invalidateFilter(); } + + emit changed(); } } diff --git a/src/filterwidget.h b/src/filterwidget.h index 762d9b15..5b08c3ae 100644 --- a/src/filterwidget.h +++ b/src/filterwidget.h @@ -1,22 +1,55 @@ #ifndef FILTERWIDGET_H #define FILTERWIDGET_H +#include +#include +#include +#include +#include + class EventFilter; +class FilterWidget; -class FilterWidget +class FilterWidgetProxyModel : public QSortFilterProxyModel { + Q_OBJECT; + public: - std::function changed; + FilterWidgetProxyModel(FilterWidget& fw, QWidget* parent=nullptr); + using QSortFilterProxyModel::invalidateFilter; + +protected: + bool filterAcceptsRow(int row, const QModelIndex& parent) const override; + +private: + FilterWidget& m_filter; +}; + + +class FilterWidget : public QObject +{ + Q_OBJECT; + +public: + using predFun = std::function; FilterWidget(); - void set(QLineEdit* edit); + void setEdit(QLineEdit* edit); + void setList(QAbstractItemView* list); void clear(); - bool matches(std::function pred) const; + QModelIndex map(const QModelIndex& index); + + bool matches(predFun pred) const; + +signals: + void changed(); private: QLineEdit* m_edit; + QAbstractItemView* m_list; + FilterWidgetProxyModel* m_proxy; EventFilter* m_eventFilter; QToolButton* m_clear; QString m_text; diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui index 9f4ae873..69e73da4 100644 --- a/src/modinfodialog.ui +++ b/src/modinfodialog.ui @@ -72,6 +72,9 @@ + + + @@ -108,7 +111,7 @@ Qt::Horizontal - + 6 @@ -132,6 +135,9 @@
+ + + diff --git a/src/modinfodialogconflicts.cpp b/src/modinfodialogconflicts.cpp index c053e64c..e631db30 100644 --- a/src/modinfodialogconflicts.cpp +++ b/src/modinfodialogconflicts.cpp @@ -999,8 +999,8 @@ AdvancedConflictsTab::AdvancedConflictsTab( ui->conflictsAdvancedList, &QTreeView::customContextMenuRequested, [&](const QPoint& p){ m_tab->showContextMenu(p, ui->conflictsAdvancedList); }); - m_filter.set(ui->conflictsAdvancedFilter); - m_filter.changed = [&]{ update(); }; + m_filter.setEdit(ui->conflictsAdvancedFilter); + QObject::connect(&m_filter, &FilterWidget::changed, [&]{ update(); }); } void AdvancedConflictsTab::clear() diff --git a/src/modinfodialogtextfiles.cpp b/src/modinfodialogtextfiles.cpp index 675a4c79..2b5ab489 100644 --- a/src/modinfodialogtextfiles.cpp +++ b/src/modinfodialogtextfiles.cpp @@ -100,7 +100,7 @@ private: GenericFilesTab::GenericFilesTab( OrganizerCore& oc, PluginContainer& plugin, QWidget* parent, Ui::ModInfoDialog* ui, int id, - QListView* list, QSplitter* sp, TextEditor* e) : + QListView* list, QSplitter* sp, TextEditor* e, QLineEdit* filter) : ModInfoDialogTab(oc, plugin, parent, ui, id), m_list(list), m_editor(e), m_model(new FileListModel) { @@ -111,6 +111,9 @@ GenericFilesTab::GenericFilesTab( sp->setStretchFactor(0, 0); sp->setStretchFactor(1, 1); + m_filter.setEdit(filter); + m_filter.setList(m_list); + QObject::connect( m_list->selectionModel(), &QItemSelectionModel::currentRowChanged, [&](auto current, auto previous){ onSelection(current, previous); }); @@ -187,7 +190,7 @@ void GenericFilesTab::select(const QModelIndex& index) } m_editor->setEnabled(true); - m_editor->load(m_model->fullPath(index)); + m_editor->load(m_model->fullPath(m_filter.map(index))); } @@ -196,7 +199,8 @@ TextFilesTab::TextFilesTab( QWidget* parent, Ui::ModInfoDialog* ui, int id) : GenericFilesTab( oc, plugin, parent, ui, id, - ui->textFileList, ui->tabTextSplitter, ui->textFileEditor) + ui->textFileList, ui->tabTextSplitter, + ui->textFileEditor, ui->textFileFilter) { } @@ -218,7 +222,8 @@ IniFilesTab::IniFilesTab( QWidget* parent, Ui::ModInfoDialog* ui, int id) : GenericFilesTab( oc, plugin, parent, ui, id, - ui->iniFileList, ui->tabIniSplitter, ui->iniFileEditor) + ui->iniFileList, ui->tabIniSplitter, + ui->iniFileEditor, ui->iniFileFilter) { } diff --git a/src/modinfodialogtextfiles.h b/src/modinfodialogtextfiles.h index d879c2bd..037c6bb3 100644 --- a/src/modinfodialogtextfiles.h +++ b/src/modinfodialogtextfiles.h @@ -2,6 +2,7 @@ #define MODINFODIALOGTEXTFILES_H #include "modinfodialogtab.h" +#include "filterwidget.h" #include #include @@ -23,11 +24,12 @@ protected: QListView* m_list; TextEditor* m_editor; FileListModel* m_model; + FilterWidget m_filter; GenericFilesTab( OrganizerCore& oc, PluginContainer& plugin, QWidget* parent, Ui::ModInfoDialog* ui, int id, - QListView* list, QSplitter* splitter, TextEditor* editor); + QListView* list, QSplitter* splitter, TextEditor* editor, QLineEdit* filter); virtual bool wantsFile(const QString& rootPath, const QString& fullPath) const = 0; -- cgit v1.3.1 From a818954ee5d02b8723f5b48f458a0c76f9ba1935 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Thu, 27 Jun 2019 11:30:46 -0400 Subject: added explore menu item to conflict lists and filetree changed filetree context menu to always show all items added mnemonics to conflicts context menu --- src/modinfodialog.cpp | 6 ++ src/modinfodialog.h | 1 + src/modinfodialogconflicts.cpp | 43 ++++++++-- src/modinfodialogconflicts.h | 3 + src/modinfodialogfiletree.cpp | 180 ++++++++++++++++++++++++----------------- src/modinfodialogfiletree.h | 2 + 6 files changed, 154 insertions(+), 81 deletions(-) (limited to 'src/modinfodialogconflicts.cpp') diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 7696023f..2772994c 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -66,6 +66,12 @@ bool canOpenFile(bool isArchive, const QString&) return !isArchive; } +bool canExploreFile(bool isArchive, const QString&) +{ + // can explore anything as long as it's not in an archive + return !isArchive; +} + bool canHideFile(bool isArchive, const QString& filename) { if (isArchive) { diff --git a/src/modinfodialog.h b/src/modinfodialog.h index 6dc66003..273af8c7 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -36,6 +36,7 @@ class MainWindow; bool canPreviewFile(PluginContainer& pluginContainer, bool isArchive, const QString& filename); bool canOpenFile(bool isArchive, const QString& filename); +bool canExploreFile(bool isArchive, const QString& filename); bool canHideFile(bool isArchive, const QString& filename); bool canUnhideFile(bool isArchive, const QString& filename); diff --git a/src/modinfodialogconflicts.cpp b/src/modinfodialogconflicts.cpp index e631db30..a3383a50 100644 --- a/src/modinfodialogconflicts.cpp +++ b/src/modinfodialogconflicts.cpp @@ -7,6 +7,7 @@ using namespace MOShared; using namespace MOBase; +namespace shell = MOBase::shell; // if there are more than 50 selected items in the conflict tree, don't bother // checking whether menu items apply to them, just show all of them @@ -91,6 +92,11 @@ public: return canPreviewFile(pluginContainer, isArchive(), fileName()); } + bool canExplore() const + { + return canExploreFile(isArchive(), fileName()); + } + private: QString m_before; QString m_relativeName; @@ -533,6 +539,16 @@ void ConflictsTab::previewItems(QTreeView* tree) }); } +void ConflictsTab::exploreItems(QTreeView* tree) +{ + // the menu item is only shown for a single selection, but handle all of them + // in case this changes + for_each_in_selection(tree, [&](const ConflictItem* item) { + shell::ExploreFile(item->fileName()); + return true; + }); +} + void ConflictsTab::showContextMenu(const QPoint &pos, QTreeView* tree) { auto actions = createMenuActions(tree); @@ -557,6 +573,15 @@ void ConflictsTab::showContextMenu(const QPoint &pos, QTreeView* tree) menu.addAction(actions.preview); } + // explore + if (actions.explore) { + connect(actions.explore, &QAction::triggered, [&]{ + exploreItems(tree); + }); + + menu.addAction(actions.explore); + } + // hide if (actions.hide) { connect(actions.hide, &QAction::triggered, [&]{ @@ -603,6 +628,7 @@ ConflictsTab::Actions ConflictsTab::createMenuActions(QTreeView* tree) bool enableUnhide = true; bool enableOpen = true; bool enablePreview = true; + bool enableExplore = true; bool enableGoto = true; const auto n = smallSelectionSize(tree); @@ -626,6 +652,7 @@ ConflictsTab::Actions ConflictsTab::createMenuActions(QTreeView* tree) enableUnhide = item->canUnhide(); enableOpen = item->canOpen(); enablePreview = item->canPreview(plugin()); + enableExplore = item->canExplore(); enableGoto = item->hasAlts(); } else { @@ -634,6 +661,9 @@ ConflictsTab::Actions ConflictsTab::createMenuActions(QTreeView* tree) enableOpen = false; enablePreview = false; + // can't explore multiple files + enableExplore = false; + // don't bother with this on multiple selection, at least for now enableGoto = false; @@ -664,21 +694,24 @@ ConflictsTab::Actions ConflictsTab::createMenuActions(QTreeView* tree) Actions actions; - actions.hide = new QAction(tr("Hide"), parentWidget()); + actions.hide = new QAction(tr("&Hide"), parentWidget()); actions.hide->setEnabled(enableHide); // note that it is possible for hidden files to appear if they override other // hidden files from another mod - actions.unhide = new QAction(tr("Unhide"), parentWidget()); + actions.unhide = new QAction(tr("&Unhide"), parentWidget()); actions.unhide->setEnabled(enableUnhide); - actions.open = new QAction(tr("Open/Execute"), parentWidget()); + actions.open = new QAction(tr("&Open/Execute"), parentWidget()); actions.open->setEnabled(enableOpen); - actions.preview = new QAction(tr("Preview"), parentWidget()); + actions.preview = new QAction(tr("&Preview"), parentWidget()); actions.preview->setEnabled(enablePreview); - actions.gotoMenu = new QMenu(tr("Go to..."), parentWidget()); + actions.explore = new QAction(tr("Open in &Explorer"), parentWidget()); + actions.explore->setEnabled(enableExplore); + + actions.gotoMenu = new QMenu(tr("&Go to..."), parentWidget()); actions.gotoMenu->setEnabled(enableGoto); if (enableGoto && n == 1) { diff --git a/src/modinfodialogconflicts.h b/src/modinfodialogconflicts.h index fc7dd32a..3fa12231 100644 --- a/src/modinfodialogconflicts.h +++ b/src/modinfodialogconflicts.h @@ -115,6 +115,7 @@ public: void openItems(QTreeView* tree); void previewItems(QTreeView* tree); + void exploreItems(QTreeView* tree); void changeItemsVisibility(QTreeView* tree, bool visible); @@ -127,7 +128,9 @@ private: QAction* unhide = nullptr; QAction* open = nullptr; QAction* preview = nullptr; + QAction* explore = nullptr; QMenu* gotoMenu = nullptr; + std::vector gotoActions; }; diff --git a/src/modinfodialogfiletree.cpp b/src/modinfodialogfiletree.cpp index 24faec5e..6690dd2f 100644 --- a/src/modinfodialogfiletree.cpp +++ b/src/modinfodialogfiletree.cpp @@ -24,8 +24,9 @@ FileTreeTab::FileTreeTab( ui->filetree->setColumnWidth(0, 300); m_actions.newFolder = new QAction(tr("&New Folder"), ui->filetree); - m_actions.open = new QAction(tr("&Open"), ui->filetree); + m_actions.open = new QAction(tr("&Open/Execute"), ui->filetree); m_actions.preview = new QAction(tr("&Preview"), ui->filetree); + m_actions.explore = new QAction(tr("Open in &Explorer"), ui->filetree); m_actions.rename = new QAction(tr("&Rename"), ui->filetree); m_actions.del = new QAction(tr("&Delete"), ui->filetree); m_actions.hide = new QAction(tr("&Hide"), ui->filetree); @@ -34,6 +35,7 @@ FileTreeTab::FileTreeTab( connect(m_actions.newFolder, &QAction::triggered, [&]{ onCreateDirectory(); }); connect(m_actions.open, &QAction::triggered, [&]{ onOpen(); }); connect(m_actions.preview, &QAction::triggered, [&]{ onPreview(); }); + connect(m_actions.explore, &QAction::triggered, [&]{ onExplore(); }); connect(m_actions.rename, &QAction::triggered, [&]{ onRename(); }); connect(m_actions.del, &QAction::triggered, [&]{ onDelete(); }); connect(m_actions.hide, &QAction::triggered, [&]{ onHide(); }); @@ -140,6 +142,17 @@ void FileTreeTab::onPreview() core().previewFile(parentWidget(), mod()->name(), m_fs->filePath(selection)); } +void FileTreeTab::onExplore() +{ + auto selection = singleSelection(); + + if (selection.isValid()) { + shell::ExploreFile(m_fs->filePath(selection)); + } else { + shell::ExploreFile(mod()->absolutePath()); + } +} + void FileTreeTab::onRename() { auto selection = singleSelection(); @@ -316,103 +329,118 @@ void FileTreeTab::onContextMenu(const QPoint &pos) QMenu menu(ui->filetree); - menu.addAction(m_actions.newFolder); - - if (selection.size() > 0) { - bool enableOpen = true; - bool enablePreview = true; - bool enableRename = true; - bool enableDelete = true; - bool enableHide = true; - bool enableUnhide = true; + bool enableNewFolder = true; + bool enableOpen = true; + bool enablePreview = true; + bool enableExplore = true; + bool enableRename = true; + bool enableDelete = true; + bool enableHide = true; + bool enableUnhide = true; + + if (selection.size() == 0) { + // no selection, only new folder and explore + enableOpen = false; + enablePreview = false; + enableRename = false; + enableDelete = false; + enableHide = false; + enableUnhide = false; + } else if (selection.size() == 1) { + // single selection + + // only enable open action if a file is selected + bool hasFiles = false; + + for (auto index : selection) { + if (m_fs->fileInfo(index).isFile()) { + hasFiles = true; + break; + } + } - if (selection.size() == 1) { - // single selection + if (!hasFiles) { + enableOpen = false; + enablePreview = false; + } - // only enable open action if a file is selected - bool hasFiles = false; + const QString fileName = m_fs->fileName(selection[0]); - for (auto index : selection) { - if (m_fs->fileInfo(index).isFile()) { - hasFiles = true; - break; - } - } + if (!canPreviewFile(plugin(), false, fileName)) { + enablePreview = false; + } - if (!hasFiles) { - enableOpen = false; - enablePreview = false; - } + if (!canExploreFile(false, fileName)) { + enableExplore = false; + } - const QString fileName = m_fs->fileName(selection[0]); + if (!canHideFile(false, fileName)) { + enableHide = false; + } - if (!canPreviewFile(plugin(), false, fileName)) { - enablePreview = false; - } + if (!canUnhideFile(false, fileName)) { + enableUnhide = false; + } + } else { + // this is a multiple selection, don't show open action so users don't open + // a thousand files + enableOpen = false; + enablePreview = false; - if (!canHideFile(false, fileName)) { - enableHide = false; - } + // can't explore multiple files + enableExplore = false; - if (!canUnhideFile(false, fileName)) { - enableUnhide = false; - } - } else { - // this is a multiple selection, don't show open action so users don't open - // a thousand files - enableOpen = false; - enablePreview = false; - enableRename = false; + // can't rename multiple files + enableRename = false; - if (selection.size() < max_scan_for_context_menu) { - // if the number of selected items is low, checking them to accurately - // show the menu items is worth it - enableHide = false; - enableUnhide = false; + if (selection.size() < max_scan_for_context_menu) { + // if the number of selected items is low, checking them to accurately + // show the menu items is worth it + enableHide = false; + enableUnhide = false; - for (const auto& index : selection) { - const QString fileName = m_fs->fileName(index); + for (const auto& index : selection) { + const QString fileName = m_fs->fileName(index); - if (canHideFile(false, fileName)) { - enableHide = true; - } + if (canHideFile(false, fileName)) { + enableHide = true; + } - if (canUnhideFile(false, fileName)) { - enableUnhide = true; - } + if (canUnhideFile(false, fileName)) { + enableUnhide = true; + } - if (enableHide && enableUnhide) { - // found both, no need to check more - break; - } + if (enableHide && enableUnhide) { + // found both, no need to check more + break; } } } + } - if (enableOpen) { - menu.addAction(m_actions.open); - } + menu.addAction(m_actions.newFolder); + m_actions.newFolder->setEnabled(enableNewFolder); - if (enablePreview) { - menu.addAction(m_actions.preview); - } + menu.addAction(m_actions.open); + m_actions.open->setEnabled(enableOpen); - if (enableRename) { - menu.addAction(m_actions.rename); - } + menu.addAction(m_actions.preview); + m_actions.preview->setEnabled(enablePreview); - if (enableDelete) { - menu.addAction(m_actions.del); - } + menu.addAction(m_actions.explore); + m_actions.explore->setEnabled(enableExplore); - if (enableHide) { - menu.addAction(m_actions.hide); - } + menu.addAction(m_actions.rename); + m_actions.rename->setEnabled(enableRename); - if (enableUnhide) { - menu.addAction(m_actions.unhide); - } - } + menu.addAction(m_actions.del); + m_actions.del->setEnabled(enableDelete); + + menu.addAction(m_actions.hide); + m_actions.hide->setEnabled(enableHide); + + menu.addAction(m_actions.unhide); + m_actions.unhide->setEnabled(enableUnhide); menu.exec(ui->filetree->viewport()->mapToGlobal(pos)); } diff --git a/src/modinfodialogfiletree.h b/src/modinfodialogfiletree.h index 2145f298..9f5206b9 100644 --- a/src/modinfodialogfiletree.h +++ b/src/modinfodialogfiletree.h @@ -20,6 +20,7 @@ private: QAction *newFolder = nullptr; QAction *open = nullptr; QAction *preview = nullptr; + QAction *explore = nullptr; QAction *rename = nullptr; QAction *del = nullptr; QAction *hide = nullptr; @@ -32,6 +33,7 @@ private: void onCreateDirectory(); void onOpen(); void onPreview(); + void onExplore(); void onRename(); void onDelete(); void onHide(); -- cgit v1.3.1 From 82d985064e5105ded4b20d357eaf7cd1b97fe9da Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Tue, 2 Jul 2019 12:02:07 -0400 Subject: added a ModInfoDialogTabContext to avoid passing too many things to tab constructors mod is passed to ctors to make sure they can never be empty only call deleteRequest() to selected mod comments --- src/mainwindow.cpp | 4 +- src/modinfodialog.cpp | 40 +++++++++++------- src/modinfodialog.h | 36 ++++------------ src/modinfodialogcategories.cpp | 14 +++--- src/modinfodialogcategories.h | 4 +- src/modinfodialogconflicts.cpp | 12 +++--- src/modinfodialogconflicts.h | 4 +- src/modinfodialogesps.cpp | 8 ++-- src/modinfodialogesps.h | 4 +- src/modinfodialogfiletree.cpp | 14 +++--- src/modinfodialogfiletree.h | 4 +- src/modinfodialogimages.cpp | 9 ++-- src/modinfodialogimages.h | 4 +- src/modinfodialognexus.cpp | 85 +++++++++++++++++++------------------ src/modinfodialognexus.h | 4 +- src/modinfodialogtab.cpp | 33 ++++++++------- src/modinfodialogtab.h | 94 +++++++++++++++++++++++++++++++++++++---- src/modinfodialogtextfiles.cpp | 30 ++++++------- src/modinfodialogtextfiles.h | 14 +++--- 19 files changed, 226 insertions(+), 191 deletions(-) (limited to 'src/modinfodialogconflicts.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index d48fae4f..7ab555fa 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3223,11 +3223,9 @@ void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index, } else { modInfo->saveMeta(); - ModInfoDialog dialog(this, &m_OrganizerCore, &m_PluginContainer); + ModInfoDialog dialog(this, &m_OrganizerCore, &m_PluginContainer, modInfo); connect(&dialog, SIGNAL(originModified(int)), this, SLOT(originModified(int))); - dialog.setMod(modInfo); - //Open the tab first if we want to use the standard indexes of the tabs. if (tab != -1) { dialog.setTab(ModInfoDialog::ETabs(tab)); diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 4af479c4..4ef010e4 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -127,7 +127,8 @@ bool ModInfoDialog::TabInfo::isVisible() const ModInfoDialog::ModInfoDialog( - MainWindow* mw, OrganizerCore* core, PluginContainer* plugin) : + MainWindow* mw, OrganizerCore* core, PluginContainer* plugin, + ModInfo::Ptr mod) : TutorableDialog("ModInfoDialog", mw), ui(new Ui::ModInfoDialog), m_mainWindow(mw), m_core(core), m_plugin(plugin), m_initialTab(ETabs(-1)), @@ -138,6 +139,7 @@ ModInfoDialog::ModInfoDialog( auto* sc = new QShortcut(QKeySequence::Delete, this); connect(sc, &QShortcut::activated, [&]{ onDeleteShortcut(); }); + setMod(mod); m_tabs = createTabs(); for (int i=0; itabWidget->count(); ++i) { @@ -184,19 +186,26 @@ ModInfoDialog::ModInfoDialog( ModInfoDialog::~ModInfoDialog() = default; +template +std::unique_ptr createTab(ModInfoDialog& d, int index) +{ + return std::make_unique(ModInfoDialogTabContext( + *d.m_core, *d.m_plugin, &d, d.ui.get(), index, d.m_mod, d.getOrigin())); +} + std::vector ModInfoDialog::createTabs() { std::vector v; - v.push_back(createTab(TAB_TEXTFILES)); - v.push_back(createTab(TAB_INIFILES)); - v.push_back(createTab(TAB_IMAGES)); - v.push_back(createTab(TAB_ESPS)); - v.push_back(createTab(TAB_CONFLICTS)); - v.push_back(createTab(TAB_CATEGORIES)); - v.push_back(createTab(TAB_NEXUS)); - v.push_back(createTab(TAB_NOTES)); - v.push_back(createTab(TAB_FILETREE)); + v.push_back(createTab(*this, TAB_TEXTFILES)); + v.push_back(createTab(*this, TAB_INIFILES)); + v.push_back(createTab(*this, TAB_IMAGES)); + v.push_back(createTab(*this, TAB_ESPS)); + v.push_back(createTab(*this, TAB_CONFLICTS)); + v.push_back(createTab(*this, TAB_CATEGORIES)); + v.push_back(createTab(*this, TAB_NEXUS)); + v.push_back(createTab(*this, TAB_NOTES)); + v.push_back(createTab(*this, TAB_FILETREE)); return v; } @@ -218,6 +227,7 @@ int ModInfoDialog::exec() void ModInfoDialog::setMod(ModInfo::Ptr mod) { + Q_ASSERT(mod); m_mod = mod; for (auto& tabInfo : m_tabs) { @@ -584,10 +594,8 @@ void ModInfoDialog::onOriginModified(int originID) void ModInfoDialog::onDeleteShortcut() { - for (auto& tabInfo : m_tabs) { - if (tabInfo.tab->deleteRequested()) { - break; - } + if (auto* tabInfo=currentTab()) { + tabInfo->tab->deleteRequested(); } } @@ -663,7 +671,7 @@ void ModInfoDialog::onTabMoved() void ModInfoDialog::on_nextButton_clicked() { auto mod = m_mainWindow->nextModInList(); - if (mod == m_mod) { + if (!mod || mod == m_mod) { return; } @@ -674,7 +682,7 @@ void ModInfoDialog::on_nextButton_clicked() void ModInfoDialog::on_prevButton_clicked() { auto mod = m_mainWindow->previousModInList(); - if (mod == m_mod) { + if (!mod || mod == m_mod) { return; } diff --git a/src/modinfodialog.h b/src/modinfodialog.h index 899a3eab..9eb00a3b 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -66,7 +66,11 @@ protected: **/ class ModInfoDialog : public MOBase::TutorableDialog { - Q_OBJECT + Q_OBJECT; + + template + friend std::unique_ptr createTab( + ModInfoDialog& d, int index); public: enum ETabs { @@ -81,30 +85,12 @@ public: TAB_FILETREE }; - /** - * @brief constructor - * - * @param modInfo info structure about the mod to display - * @param parent parend widget - **/ - ModInfoDialog(MainWindow* mw, OrganizerCore* core, PluginContainer* plugin); + ModInfoDialog( + MainWindow* mw, OrganizerCore* core, PluginContainer* plugin, + ModInfo::Ptr mod); ~ModInfoDialog(); - /** - * @brief retrieve the (user-modified) version of the mod - * - * @return the (user-modified) version of the mod - **/ - QString getModVersion() const; - - /** - * @brief retrieve the (user-modified) mod id - * - * @return the (user-modified) id of the mod - **/ - const int getModID() const; - void setMod(ModInfo::Ptr mod); void setMod(const QString& name); void setTab(ETabs id); @@ -166,12 +152,6 @@ private: void onOriginModified(int originID); void onTabChanged(); void onTabMoved(); - - template - std::unique_ptr createTab(int index) - { - return std::make_unique(*m_core, *m_plugin, this, ui.get(), index); - } }; #endif // MODINFODIALOG_H diff --git a/src/modinfodialogcategories.cpp b/src/modinfodialogcategories.cpp index 8ffded59..c61e248e 100644 --- a/src/modinfodialogcategories.cpp +++ b/src/modinfodialogcategories.cpp @@ -3,10 +3,8 @@ #include "categories.h" #include "modinfo.h" -CategoriesTab::CategoriesTab( - OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int id) - : ModInfoDialogTab(oc, plugin, parent, ui, id) +CategoriesTab::CategoriesTab(ModInfoDialogTabContext cx) + : ModInfoDialogTab(std::move(cx)) { connect( ui->categories, &QTreeWidget::itemChanged, @@ -30,7 +28,7 @@ void CategoriesTab::update() clear(); add( - CategoryFactory::instance(), mod()->getCategories(), + CategoryFactory::instance(), mod().getCategories(), ui->categories->invisibleRootItem(), 0); updatePrimary(); @@ -81,7 +79,7 @@ void CategoriesTab::updatePrimary() { ui->primaryCategories->clear(); - int primaryCategory = mod()->getPrimaryCategory(); + int primaryCategory = mod().getPrimaryCategory(); addChecked(ui->categories->invisibleRootItem()); @@ -111,7 +109,7 @@ void CategoriesTab::save(QTreeWidgetItem* currentNode) for (int i = 0; i < currentNode->childCount(); ++i) { QTreeWidgetItem *childNode = currentNode->child(i); - mod()->setCategory( + mod().setCategory( childNode->data(0, Qt::UserRole).toInt(), childNode->checkState(0)); save(childNode); @@ -134,6 +132,6 @@ void CategoriesTab::onCategoryChanged(QTreeWidgetItem* item, int) void CategoriesTab::onPrimaryChanged(int index) { if (index != -1) { - mod()->setPrimaryCategory(ui->primaryCategories->itemData(index).toInt()); + mod().setPrimaryCategory(ui->primaryCategories->itemData(index).toInt()); } } diff --git a/src/modinfodialogcategories.h b/src/modinfodialogcategories.h index 392023e7..c8b52fec 100644 --- a/src/modinfodialogcategories.h +++ b/src/modinfodialogcategories.h @@ -5,9 +5,7 @@ class CategoryFactory; class CategoriesTab : public ModInfoDialogTab { public: - CategoriesTab( - OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int id); + CategoriesTab(ModInfoDialogTabContext cx); void clear() override; void update() override; diff --git a/src/modinfodialogconflicts.cpp b/src/modinfodialogconflicts.cpp index a3383a50..511d48ad 100644 --- a/src/modinfodialogconflicts.cpp +++ b/src/modinfodialogconflicts.cpp @@ -382,11 +382,9 @@ void for_each_in_selection(QTreeView* tree, F&& f) } -ConflictsTab::ConflictsTab( - OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int id) : - ModInfoDialogTab(oc, plugin, parent, ui, id), - m_general(this, ui, oc), m_advanced(this, ui, oc) +ConflictsTab::ConflictsTab(ModInfoDialogTabContext cx) : + ModInfoDialogTab(cx), // don't move, cx is used again + m_general(this, cx.ui, cx.core), m_advanced(this, cx.ui, cx.core) { connect( &m_general, &GeneralConflictsTab::modOpen, @@ -872,7 +870,7 @@ bool GeneralConflictsTab::update() int numOverwritten = 0; if (m_tab->origin() != nullptr) { - const auto rootPath = m_tab->mod()->absolutePath(); + const auto rootPath = m_tab->mod().absolutePath(); for (const auto& file : m_tab->origin()->getFiles()) { // careful: these two strings are moved into createXItem() below @@ -1085,7 +1083,7 @@ void AdvancedConflictsTab::update() clear(); if (m_tab->origin() != nullptr) { - const auto rootPath = m_tab->mod()->absolutePath(); + const auto rootPath = m_tab->mod().absolutePath(); const auto& files = m_tab->origin()->getFiles(); m_model->reserve(files.size()); diff --git a/src/modinfodialogconflicts.h b/src/modinfodialogconflicts.h index 3fa12231..a77c2ac9 100644 --- a/src/modinfodialogconflicts.h +++ b/src/modinfodialogconflicts.h @@ -103,9 +103,7 @@ class ConflictsTab : public ModInfoDialogTab Q_OBJECT; public: - ConflictsTab( - OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int id); + ConflictsTab(ModInfoDialogTabContext cx); void update() override; void clear() override; diff --git a/src/modinfodialogesps.cpp b/src/modinfodialogesps.cpp index 8dceaa31..fba5d39a 100644 --- a/src/modinfodialogesps.cpp +++ b/src/modinfodialogesps.cpp @@ -224,11 +224,9 @@ private: -ESPsTab::ESPsTab( - OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int id) : - ModInfoDialogTab(oc, plugin, parent, ui, id), - m_inactiveModel(new ESPListModel), m_activeModel(new ESPListModel) +ESPsTab::ESPsTab(ModInfoDialogTabContext cx) : + ModInfoDialogTab(std::move(cx)), + m_inactiveModel(new ESPListModel), m_activeModel(new ESPListModel) { ui->inactiveESPList->setModel(m_inactiveModel); ui->activeESPList->setModel(m_activeModel); diff --git a/src/modinfodialogesps.h b/src/modinfodialogesps.h index 217863c6..b128f279 100644 --- a/src/modinfodialogesps.h +++ b/src/modinfodialogesps.h @@ -11,9 +11,7 @@ class ESPsTab : public ModInfoDialogTab Q_OBJECT; public: - ESPsTab( - OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int id); + ESPsTab(ModInfoDialogTabContext cx); void clear() override; bool feedFile(const QString& rootPath, const QString& fullPath) override; diff --git a/src/modinfodialogfiletree.cpp b/src/modinfodialogfiletree.cpp index 6690dd2f..35480e2c 100644 --- a/src/modinfodialogfiletree.cpp +++ b/src/modinfodialogfiletree.cpp @@ -13,10 +13,8 @@ namespace shell = MOBase::shell; // checking whether menu items apply to them, just show all of them const int max_scan_for_context_menu = 50; -FileTreeTab::FileTreeTab( - OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int id) - : ModInfoDialogTab(oc, plugin, parent, ui, id), m_fs(nullptr) +FileTreeTab::FileTreeTab(ModInfoDialogTabContext cx) + : ModInfoDialogTab(std::move(cx)), m_fs(nullptr) { m_fs = new QFileSystemModel(this); m_fs->setReadOnly(false); @@ -58,7 +56,7 @@ void FileTreeTab::clear() void FileTreeTab::update() { - const auto rootPath = mod()->absolutePath(); + const auto rootPath = mod().absolutePath(); m_fs->setRootPath(rootPath); ui->filetree->setRootIndex(m_fs->index(rootPath)); @@ -139,7 +137,7 @@ void FileTreeTab::onPreview() return; } - core().previewFile(parentWidget(), mod()->name(), m_fs->filePath(selection)); + core().previewFile(parentWidget(), mod().name(), m_fs->filePath(selection)); } void FileTreeTab::onExplore() @@ -149,7 +147,7 @@ void FileTreeTab::onExplore() if (selection.isValid()) { shell::ExploreFile(m_fs->filePath(selection)); } else { - shell::ExploreFile(mod()->absolutePath()); + shell::ExploreFile(mod().absolutePath()); } } @@ -205,7 +203,7 @@ void FileTreeTab::onUnhide() void FileTreeTab::onOpenInExplorer() { - shell::ExploreFile(mod()->absolutePath()); + shell::ExploreFile(mod().absolutePath()); } bool FileTreeTab::deleteFile(const QModelIndex& index) diff --git a/src/modinfodialogfiletree.h b/src/modinfodialogfiletree.h index 9f5206b9..f9fa62d4 100644 --- a/src/modinfodialogfiletree.h +++ b/src/modinfodialogfiletree.h @@ -6,9 +6,7 @@ class FileTreeTab : public ModInfoDialogTab { public: - FileTreeTab( - OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int id); + FileTreeTab(ModInfoDialogTabContext cx); void clear() override; void update() override; diff --git a/src/modinfodialogimages.cpp b/src/modinfodialogimages.cpp index 307fa8e8..cf33f9f5 100644 --- a/src/modinfodialogimages.cpp +++ b/src/modinfodialogimages.cpp @@ -35,12 +35,9 @@ QString dimensionString(const QSize& s) } -ImagesTab::ImagesTab( - OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int id) : - ModInfoDialogTab(oc, plugin, parent, ui, id), - m_image(new ScalableImage), - m_ddsAvailable(false), m_ddsEnabled(false) +ImagesTab::ImagesTab(ModInfoDialogTabContext cx) : + ModInfoDialogTab(std::move(cx)), m_image(new ScalableImage), + m_ddsAvailable(false), m_ddsEnabled(false) { getSupportedFormats(); diff --git a/src/modinfodialogimages.h b/src/modinfodialogimages.h index d22a6ab2..8d9b965b 100644 --- a/src/modinfodialogimages.h +++ b/src/modinfodialogimages.h @@ -314,9 +314,7 @@ class ImagesTab : public ModInfoDialogTab friend class ImagesTabHelpers::ThumbnailsWidget; public: - ImagesTab( - OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int id); + ImagesTab(ModInfoDialogTabContext cx); void clear() override; bool feedFile(const QString& rootPath, const QString& fullPath) override; diff --git a/src/modinfodialognexus.cpp b/src/modinfodialognexus.cpp index 8c8ce55a..81546f58 100644 --- a/src/modinfodialognexus.cpp +++ b/src/modinfodialognexus.cpp @@ -14,11 +14,8 @@ bool isValidModID(int id) return (id > 0); } -NexusTab::NexusTab( - OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int id) : - ModInfoDialogTab(oc, plugin, parent, ui, id), m_requestStarted(false), - m_loading(false) +NexusTab::NexusTab(ModInfoDialogTabContext cx) : + ModInfoDialogTab(std::move(cx)), m_requestStarted(false), m_loading(false) { ui->modID->setValidator(new QIntValidator(ui->modID)); ui->endorse->setVisible(core().settings().endorsementIntegration()); @@ -70,9 +67,9 @@ void NexusTab::update() clear(); - ui->modID->setText(QString("%1").arg(mod()->getNexusID())); + ui->modID->setText(QString("%1").arg(mod().getNexusID())); - QString gameName = mod()->getGameName(); + QString gameName = mod().getGameName(); ui->sourceGame->addItem( core().managedGame()->gameName(), core().managedGame()->gameShortName()); @@ -100,10 +97,10 @@ void NexusTab::update() [&](const QUrl& url){ shell::OpenLink(url); }); ui->endorse->setEnabled( - (mod()->endorsedState() == ModInfo::ENDORSED_FALSE) || - (mod()->endorsedState() == ModInfo::ENDORSED_NEVER)); + (mod().endorsedState() == ModInfo::ENDORSED_FALSE) || + (mod().endorsedState() == ModInfo::ENDORSED_NEVER)); - setHasData(mod()->getNexusID() >= 0); + setHasData(mod().getNexusID() >= 0); } void NexusTab::firstActivation() @@ -128,10 +125,10 @@ bool NexusTab::usesOriginFiles() const void NexusTab::updateVersionColor() { - if (mod()->getVersion() != mod()->getNewestVersion()) { + if (mod().getVersion() != mod().getNewestVersion()) { ui->version->setStyleSheet("color: red"); ui->version->setToolTip(tr("Current Version: %1").arg( - mod()->getNewestVersion().canonicalString())); + mod().getNewestVersion().canonicalString())); } else { ui->version->setStyleSheet("color: green"); ui->version->setToolTip(tr("No update available")); @@ -140,11 +137,11 @@ void NexusTab::updateVersionColor() void NexusTab::updateWebpage() { - const int modID = mod()->getNexusID(); + const int modID = mod().getNexusID(); if (isValidModID(modID)) { const QString nexusLink = NexusInterface::instance(&plugin()) - ->getModURL(modID, mod()->getGameName()); + ->getModURL(modID, mod().getGameName()); ui->visitNexus->setToolTip(nexusLink); refreshData(modID); @@ -152,19 +149,19 @@ void NexusTab::updateWebpage() onModChanged(); } - ui->version->setText(mod()->getVersion().displayString()); - ui->hasCustomURL->setChecked(mod()->hasCustomURL()); - ui->customURL->setText(mod()->getCustomURL()); - ui->customURL->setEnabled(mod()->hasCustomURL()); - ui->visitCustomURL->setEnabled(mod()->hasCustomURL()); - ui->visitCustomURL->setToolTip(mod()->parseCustomURL().toString()); + ui->version->setText(mod().getVersion().displayString()); + ui->hasCustomURL->setChecked(mod().hasCustomURL()); + ui->customURL->setText(mod().getCustomURL()); + ui->customURL->setEnabled(mod().hasCustomURL()); + ui->visitCustomURL->setEnabled(mod().hasCustomURL()); + ui->visitCustomURL->setToolTip(mod().parseCustomURL().toString()); updateTracking(); } void NexusTab::updateTracking() { - if (mod()->trackedState() == ModInfo::TRACKED_TRUE) { + if (mod().trackedState() == ModInfo::TRACKED_TRUE) { ui->track->setChecked(true); ui->track->setText(tr("Tracked")); } else { @@ -185,7 +182,7 @@ void NexusTab::refreshData(int modID) bool NexusTab::tryRefreshData(int modID) { if (isValidModID(modID) && !m_requestStarted) { - if (mod()->updateNXMInfo()) { + if (mod().updateNXMInfo()) { ui->browser->setHtml(""); return true; } @@ -198,7 +195,7 @@ void NexusTab::onModChanged() { m_requestStarted = false; - const QString nexusDescription = mod()->getNexusDescription(); + const QString nexusDescription = mod().getNexusDescription(); QString descriptionAsHTML = R"( @@ -247,12 +244,12 @@ void NexusTab::onModIDChanged() return; } - const int oldID = mod()->getNexusID(); + const int oldID = mod().getNexusID(); const int newID = ui->modID->text().toInt(); if (oldID != newID){ - mod()->setNexusID(newID); - mod()->setLastNexusQuery(QDateTime::fromSecsSinceEpoch(0)); + mod().setNexusID(newID); + mod().setLastNexusQuery(QDateTime::fromSecsSinceEpoch(0)); ui->browser->page()->setHtml(""); @@ -270,9 +267,9 @@ void NexusTab::onSourceGameChanged() for (auto game : plugin().plugins()) { if (game->gameName() == ui->sourceGame->currentText()) { - mod()->setGameName(game->gameShortName()); - mod()->setLastNexusQuery(QDateTime::fromSecsSinceEpoch(0)); - refreshData(mod()->getNexusID()); + mod().setGameName(game->gameShortName()); + mod().setLastNexusQuery(QDateTime::fromSecsSinceEpoch(0)); + refreshData(mod().getNexusID()); return; } } @@ -285,16 +282,16 @@ void NexusTab::onVersionChanged() } MOBase::VersionInfo version(ui->version->text()); - mod()->setVersion(version); + mod().setVersion(version); updateVersionColor(); } void NexusTab::onRefreshBrowser() { - const auto modID = mod()->getNexusID(); + const auto modID = mod().getNexusID(); if (isValidModID(modID)) { - mod()->setLastNexusQuery(QDateTime::fromSecsSinceEpoch(0)); + mod().setLastNexusQuery(QDateTime::fromSecsSinceEpoch(0)); updateWebpage(); } else { qInfo("Mod has no valid Nexus ID, info can't be updated."); @@ -303,11 +300,11 @@ void NexusTab::onRefreshBrowser() void NexusTab::onVisitNexus() { - const int modID = mod()->getNexusID(); + const int modID = mod().getNexusID(); if (isValidModID(modID)) { const QString nexusLink = NexusInterface::instance(&plugin()) - ->getModURL(modID, mod()->getGameName()); + ->getModURL(modID, mod().getGameName()); shell::OpenLink(QUrl(nexusLink)); } @@ -315,12 +312,16 @@ void NexusTab::onVisitNexus() void NexusTab::onEndorse() { - core().loggedInAction(parentWidget(), [m=mod()]{ m->endorse(true); }); + // use modPtr() instead of mod() or this because the callback may be + // executed after the dialog is closed + core().loggedInAction(parentWidget(), [m=modPtr()]{ m->endorse(true); }); } void NexusTab::onTrack() { - core().loggedInAction(parentWidget(), [m=mod()] { + // use modPtr() instead of mod() or this because the callback may be + // executed after the dialog is closed + core().loggedInAction(parentWidget(), [m=modPtr()] { if (m->trackedState() == ModInfo::TRACKED_TRUE) { m->track(false); } else { @@ -335,9 +336,9 @@ void NexusTab::onCustomURLToggled() return; } - mod()->setHasCustomURL(ui->hasCustomURL->isChecked()); - ui->customURL->setEnabled(mod()->hasCustomURL()); - ui->visitCustomURL->setEnabled(mod()->hasCustomURL()); + mod().setHasCustomURL(ui->hasCustomURL->isChecked()); + ui->customURL->setEnabled(mod().hasCustomURL()); + ui->visitCustomURL->setEnabled(mod().hasCustomURL()); } void NexusTab::onCustomURLChanged() @@ -346,13 +347,13 @@ void NexusTab::onCustomURLChanged() return; } - mod()->setCustomURL(ui->customURL->text()); - ui->visitCustomURL->setToolTip(mod()->parseCustomURL().toString()); + mod().setCustomURL(ui->customURL->text()); + ui->visitCustomURL->setToolTip(mod().parseCustomURL().toString()); } void NexusTab::onVisitCustomURL() { - const auto url = mod()->parseCustomURL(); + const auto url = mod().parseCustomURL(); if (url.isValid()) { shell::OpenLink(url); } diff --git a/src/modinfodialognexus.h b/src/modinfodialognexus.h index 930c1ffc..6478375b 100644 --- a/src/modinfodialognexus.h +++ b/src/modinfodialognexus.h @@ -33,9 +33,7 @@ signals: class NexusTab : public ModInfoDialogTab { public: - NexusTab( - OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int id); + NexusTab(ModInfoDialogTabContext cx); ~NexusTab(); diff --git a/src/modinfodialogtab.cpp b/src/modinfodialogtab.cpp index 0468b405..554df6df 100644 --- a/src/modinfodialogtab.cpp +++ b/src/modinfodialogtab.cpp @@ -3,11 +3,9 @@ #include "texteditor.h" #include "directoryentry.h" -ModInfoDialogTab::ModInfoDialogTab( - OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int id) : - ui(ui), m_core(oc), m_plugin(plugin), m_parent(parent), - m_origin(nullptr), m_tabID(id), m_hasData(false), m_firstActivation(true) +ModInfoDialogTab::ModInfoDialogTab(ModInfoDialogTabContext cx) : + ui(cx.ui), m_core(cx.core), m_plugin(cx.plugin), m_parent(cx.parent), + m_origin(cx.origin), m_tabID(cx.id), m_hasData(false), m_firstActivation(true) { } @@ -82,8 +80,15 @@ void ModInfoDialogTab::setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin) m_origin = origin; } -ModInfo::Ptr ModInfoDialogTab::mod() const +ModInfo& ModInfoDialogTab::mod() const { + Q_ASSERT(m_mod); + return *m_mod; +} + +ModInfo::Ptr ModInfoDialogTab::modPtr() const +{ + Q_ASSERT(m_mod); return m_mod; } @@ -143,10 +148,8 @@ void ModInfoDialogTab::setFocus() } -NotesTab::NotesTab( - OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int index) - : ModInfoDialogTab(oc, plugin, parent, ui, index) +NotesTab::NotesTab(ModInfoDialogTabContext cx) + : ModInfoDialogTab(std::move(cx)) { connect(ui->comments, &QLineEdit::editingFinished, [&]{ onComments(); }); connect(ui->notes, &HTMLEditor::editingFinished, [&]{ onNotes(); }); @@ -161,8 +164,8 @@ void NotesTab::clear() void NotesTab::update() { - const auto comments = mod()->comments(); - const auto notes = mod()->notes(); + const auto comments = mod().comments(); + const auto notes = mod().notes(); ui->comments->setText(comments); ui->notes->setText(notes); @@ -176,7 +179,7 @@ bool NotesTab::canHandleSeparators() const void NotesTab::onComments() { - mod()->setComments(ui->comments->text()); + mod().setComments(ui->comments->text()); checkHasData(); } @@ -184,9 +187,9 @@ void NotesTab::onNotes() { // Avoid saving html stub if notes field is empty. if (ui->notes->toPlainText().isEmpty()) { - mod()->setNotes({}); + mod().setNotes({}); } else { - mod()->setNotes(ui->notes->toHtml()); + mod().setNotes(ui->notes->toHtml()); } checkHasData(); diff --git a/src/modinfodialogtab.h b/src/modinfodialogtab.h index 3f98314a..eb574de0 100644 --- a/src/modinfodialogtab.h +++ b/src/modinfodialogtab.h @@ -10,6 +10,33 @@ namespace Ui { class ModInfoDialog; } class Settings; class OrganizerCore; +// helper struct to avoid passing too much stuff to tab constructors +// +struct ModInfoDialogTabContext +{ + OrganizerCore& core; + PluginContainer& plugin; + QWidget* parent; + Ui::ModInfoDialog* ui; + int id; + ModInfo::Ptr mod; + MOShared::FilesOrigin* origin; + + ModInfoDialogTabContext( + OrganizerCore& core, + PluginContainer& plugin, + QWidget* parent, + Ui::ModInfoDialog* ui, + int id, + ModInfo::Ptr mod, + MOShared::FilesOrigin* origin) : + core(core), plugin(plugin), parent(parent), ui(ui), id(id), + mod(mod), origin(origin) + { + } +}; + + // base class for all tabs in the mod info dialog // // when the dialog is opened or when next/previous is clicked, the sequence is: @@ -38,7 +65,7 @@ class OrganizerCore; // tabs can call emitModOpen() to request showing a different mod // // hasDataChanged() should be called when a tab goes from having data to being -// empty or vice versa; this will update the tab text color +// empty or vice versa; this will update the tab text colour // class ModInfoDialogTab : public QObject { @@ -107,20 +134,75 @@ public: // virtual void firstActivation(); + // called when closing the dialog, can return false to stop the dialog from + // closing + // + // this is typically used by tabs that require manual saving, like text files; + // tabs that refuse to close should focus themselves before showing whatever + // confirmation they have // virtual bool canClose(); + + + // called after the dialog is closed, tabs should save whatever UI state they + // want + // virtual void saveState(Settings& s); + + // called before the is shown, tabs should restore whatever UI state they + // saved in saveState() + // virtual void restoreState(const Settings& s); + + // called on the selected tab when the Delete key is pressed on the keyboard; + // tabs _must_ check which widget currently has focus to decide whether this + // should be handled or not; do not blindly delete stuff when this is called + // + // if the delete request was handled, this should return true + // virtual bool deleteRequested(); + + // return true if this tab can handle a separator mod, defaults to false; + // when this returns false, the tab is removed from the widget entirely + // + // if a tab can show meaningful information about a separator (like + // categories or notes), it should return true + // virtual bool canHandleSeparators() const; + + // return true if this tab can handle unmanaged mods, defaults to false; + // when this returns false, the tab is removed from the widget entirely + // virtual bool canHandleUnmanaged() const; + + // return true if this tab uses the files from the mod's origin, defaults to + // false + // + // tabs that do not care about the files inside a mod should return false, + // such as the notes or categories tab + // + // mods that return true will be updated anytime a tab calls + // emitOriginModifed() + // virtual bool usesOriginFiles() const; - ModInfo::Ptr mod() const; + + // returns the currently selected mod + // + ModInfo& mod() const; + + // returns the currently selected mod, can never be empty + // + ModInfo::Ptr modPtr() const; + + // returns the origin of the selected mod; this can be null for mods that + // don't have an origin, like deactivated mods + // MOShared::FilesOrigin* origin() const; + int tabID() const; bool hasData() const; @@ -133,9 +215,7 @@ signals: protected: Ui::ModInfoDialog* ui; - ModInfoDialogTab( - OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int id); + ModInfoDialogTab(ModInfoDialogTabContext cx); OrganizerCore& core(); PluginContainer& plugin(); @@ -186,9 +266,7 @@ private: class NotesTab : public ModInfoDialogTab { public: - NotesTab( - OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int index); + NotesTab(ModInfoDialogTabContext cx); void clear() override; void update() override; diff --git a/src/modinfodialogtextfiles.cpp b/src/modinfodialogtextfiles.cpp index bc44ee3e..7a09fa4e 100644 --- a/src/modinfodialogtextfiles.cpp +++ b/src/modinfodialogtextfiles.cpp @@ -99,10 +99,10 @@ private: GenericFilesTab::GenericFilesTab( - OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int id, - QListView* list, QSplitter* sp, TextEditor* e, QLineEdit* filter) : - ModInfoDialogTab(oc, plugin, parent, ui, id), + ModInfoDialogTabContext cx, + QListView* list, QSplitter* sp, + TextEditor* e, QLineEdit* filter) : + ModInfoDialogTab(std::move(cx)), m_list(list), m_editor(e), m_splitter(sp), m_model(new FileListModel) { m_list->setModel(m_model); @@ -208,13 +208,10 @@ void GenericFilesTab::select(const QModelIndex& index) } -TextFilesTab::TextFilesTab( - OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int id) - : GenericFilesTab( - oc, plugin, parent, ui, id, - ui->textFileList, ui->tabTextSplitter, - ui->textFileEditor, ui->textFileFilter) +TextFilesTab::TextFilesTab(ModInfoDialogTabContext cx) + : GenericFilesTab(cx, + cx.ui->textFileList, cx.ui->tabTextSplitter, + cx.ui->textFileEditor, cx.ui->textFileFilter) { } @@ -231,13 +228,10 @@ bool TextFilesTab::wantsFile(const QString& rootPath, const QString& fullPath) c return false; } -IniFilesTab::IniFilesTab( - OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int id) - : GenericFilesTab( - oc, plugin, parent, ui, id, - ui->iniFileList, ui->tabIniSplitter, - ui->iniFileEditor, ui->iniFileFilter) +IniFilesTab::IniFilesTab(ModInfoDialogTabContext cx) + : GenericFilesTab(cx, + cx.ui->iniFileList, cx.ui->tabIniSplitter, + cx.ui->iniFileEditor, cx.ui->iniFileFilter) { } diff --git a/src/modinfodialogtextfiles.h b/src/modinfodialogtextfiles.h index ffe49904..725ac999 100644 --- a/src/modinfodialogtextfiles.h +++ b/src/modinfodialogtextfiles.h @@ -30,9 +30,9 @@ protected: FilterWidget m_filter; GenericFilesTab( - OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int id, - QListView* list, QSplitter* splitter, TextEditor* editor, QLineEdit* filter); + ModInfoDialogTabContext cx, + QListView* list, QSplitter* splitter, + TextEditor* editor, QLineEdit* filter); virtual bool wantsFile(const QString& rootPath, const QString& fullPath) const = 0; @@ -45,9 +45,7 @@ private: class TextFilesTab : public GenericFilesTab { public: - TextFilesTab( - OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int id); + TextFilesTab(ModInfoDialogTabContext cx); protected: bool wantsFile(const QString& rootPath, const QString& fullPath) const override; @@ -57,9 +55,7 @@ protected: class IniFilesTab : public GenericFilesTab { public: - IniFilesTab( - OrganizerCore& oc, PluginContainer& plugin, - QWidget* parent, Ui::ModInfoDialog* ui, int id); + IniFilesTab(ModInfoDialogTabContext cx); protected: bool wantsFile(const QString& rootPath, const QString& fullPath) const override; -- cgit v1.3.1