From 67bc0d83277b36397766d97218d5ed9b4e25640a Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Fri, 14 Jun 2019 20:21:07 -0400 Subject: clean up the list widgets and thumnails as well as recheck the tabs when refreshing after a change --- src/modinfodialog.cpp | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) (limited to 'src/modinfodialog.cpp') diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 188ea956..44934544 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -579,9 +579,6 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo initFiletree(modInfo); addCategories(CategoryFactory::instance(), modInfo->getCategories(), ui->categoriesTree->invisibleRootItem(), 0); refreshPrimaryCategoriesBox(); - ui->tabWidget->setTabEnabled(TAB_TEXTFILES, ui->textFileList->count() != 0); - ui->tabWidget->setTabEnabled(TAB_IMAGES, ui->thumbnailArea->count() != 0); - ui->tabWidget->setTabEnabled(TAB_ESPS, (ui->inactiveESPList->count() != 0) || (ui->activeESPList->count() != 0)); } initINITweaks(); @@ -1074,6 +1071,21 @@ QTreeWidgetItem* ModInfoDialog::createAdvancedConflictItem( void ModInfoDialog::refreshFiles() { + // clearing + ui->textFileList->clear(); + ui->iniTweaksList->clear(); + ui->iniFileList->clear(); + ui->inactiveESPList->clear(); + ui->activeESPList->clear(); + ui->imageLabel->setPixmap({}); + + while (ui->thumbnailArea->count() > 0) { + auto* item = ui->thumbnailArea->takeAt(0); + delete item->widget(); + delete item; + } + + if (m_RootPath.length() > 0) { QDirIterator dirIterator(m_RootPath, QDir::Files, QDirIterator::Subdirectories); while (dirIterator.hasNext()) { @@ -1124,6 +1136,10 @@ void ModInfoDialog::refreshFiles() } } } + + ui->tabWidget->setTabEnabled(TAB_TEXTFILES, ui->textFileList->count() != 0); + ui->tabWidget->setTabEnabled(TAB_IMAGES, ui->thumbnailArea->count() != 0); + ui->tabWidget->setTabEnabled(TAB_ESPS, (ui->inactiveESPList->count() != 0) || (ui->activeESPList->count() != 0)); } void ModInfoDialog::addCategories(const CategoryFactory &factory, const std::set &enabledCategories, QTreeWidgetItem *root, int rootLevel) @@ -1189,15 +1205,14 @@ void ModInfoDialog::openTab(int tab) void ModInfoDialog::thumbnailClicked(const QString &fileName) { - QLabel *imageLabel = findChild("imageLabel"); - imageLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); + ui->imageLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); QImage image(fileName); if (static_cast(image.width()) / static_cast(image.height()) > 1.34) { - image = image.scaledToWidth(imageLabel->geometry().width()); + image = image.scaledToWidth(ui->imageLabel->geometry().width()); } else { - image = image.scaledToHeight(imageLabel->geometry().height()); + image = image.scaledToHeight(ui->imageLabel->geometry().height()); } - imageLabel->setPixmap(QPixmap::fromImage(image)); + ui->imageLabel->setPixmap(QPixmap::fromImage(image)); } bool ModInfoDialog::allowNavigateFromTXT() -- cgit v1.3.1 From 59e0c4aa34fec9048d064705863c3269aacd86b5 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sat, 15 Jun 2019 17:58:23 -0400 Subject: moved filerenamer to its own .cpp/.h pair --- src/CMakeLists.txt | 3 + src/filerenamer.cpp | 189 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/filerenamer.h | 140 +++++++++++++++++++++++++++++++++++++ src/modinfodialog.cpp | 187 ------------------------------------------------- src/modinfodialog.h | 135 +----------------------------------- 5 files changed, 333 insertions(+), 321 deletions(-) create mode 100644 src/filerenamer.cpp create mode 100644 src/filerenamer.h (limited to 'src/modinfodialog.cpp') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 645a5a73..f654f9b4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -118,6 +118,7 @@ SET(organizer_SRCS filterwidget.cpp statusbar.cpp apiuseraccount.cpp + filerenamer.cpp shared/windows_error.cpp shared/error_report.cpp @@ -217,6 +218,7 @@ SET(organizer_HDRS filterwidget.h statusbar.h apiuseraccount.h + filerenamer.h shared/windows_error.h shared/error_report.h @@ -402,6 +404,7 @@ set(utilities set(widgets descriptionpage genericicondelegate + filerenamer filterwidget icondelegate lcdnumber diff --git a/src/filerenamer.cpp b/src/filerenamer.cpp new file mode 100644 index 00000000..c5c6782b --- /dev/null +++ b/src/filerenamer.cpp @@ -0,0 +1,189 @@ +#include "filerenamer.h" +#include +#include + +FileRenamer::FileRenamer(QWidget* parent, QFlags flags) + : m_parent(parent), m_flags(flags) +{ + // sanity check for flags + if ((m_flags & (HIDE|UNHIDE)) == 0) { + qCritical("renameFile() missing hide flag"); + // doesn't really matter, it's just for text + m_flags = HIDE; + } +} + +FileRenamer::RenameResults FileRenamer::rename(const QString& oldName, const QString& newName) +{ + qDebug().nospace() << "renaming " << oldName << " to " << newName; + + if (QFileInfo(newName).exists()) { + qDebug().nospace() << newName << " already exists"; + + // target file already exists, confirm replacement + auto answer = confirmReplace(newName); + + switch (answer) { + case DECISION_SKIP: { + // user wants to skip this file + qDebug().nospace() << "skipping " << oldName; + return RESULT_SKIP; + } + + case DECISION_REPLACE: { + qDebug().nospace() << "removing " << newName; + // user wants to replace the file, so remove it + if (!QFile(newName).remove()) { + qWarning().nospace() << "failed to remove " << newName; + // removal failed, warn the user and allow canceling + if (!removeFailed(newName)) { + qDebug().nospace() << "canceling " << oldName; + // user wants to cancel + return RESULT_CANCEL; + } + + // ignore this file and continue on + qDebug().nospace() << "skipping " << oldName; + return RESULT_SKIP; + } + + break; + } + + case DECISION_CANCEL: // fall-through + default: { + // user wants to stop + qDebug().nospace() << "canceling"; + return RESULT_CANCEL; + } + } + } + + // target either didn't exist or was removed correctly + + if (!QFile::rename(oldName, newName)) { + qWarning().nospace() << "failed to rename " << oldName << " to " << newName; + + // renaming failed, warn the user and allow canceling + if (!renameFailed(oldName, newName)) { + // user wants to cancel + qDebug().nospace() << "canceling"; + return RESULT_CANCEL; + } + + // ignore this file and continue on + qDebug().nospace() << "skipping " << oldName; + return RESULT_SKIP; + } + + // everything worked + qDebug().nospace() << "successfully renamed " << oldName << " to " << newName; + return RESULT_OK; +} + +FileRenamer::RenameDecision FileRenamer::confirmReplace(const QString& newName) +{ + if (m_flags & REPLACE_ALL) { + // user wants to silently replace all + qDebug().nospace() << "user has selected replace all"; + return DECISION_REPLACE; + } + else if (m_flags & REPLACE_NONE) { + // user wants to silently skip all + qDebug().nospace() << "user has selected replace none"; + return DECISION_SKIP; + } + + QString text; + + if (m_flags & HIDE) { + text = QObject::tr("The hidden file \"%1\" already exists. Replace it?").arg(newName); + } + else if (m_flags & UNHIDE) { + text = QObject::tr("The visible file \"%1\" already exists. Replace it?").arg(newName); + } + + auto buttons = QMessageBox::Yes | QMessageBox::No; + if (m_flags & MULTIPLE) { + // only show these buttons when there are multiple files to replace + buttons |= QMessageBox::YesToAll | QMessageBox::NoToAll | QMessageBox::Cancel; + } + + const auto answer = QMessageBox::question( + m_parent, QObject::tr("Replace file?"), text, buttons); + + switch (answer) { + case QMessageBox::Yes: + qDebug().nospace() << "user wants to replace"; + return DECISION_REPLACE; + + case QMessageBox::No: + qDebug().nospace() << "user wants to skip"; + return DECISION_SKIP; + + case QMessageBox::YesToAll: + qDebug().nospace() << "user wants to replace all"; + // remember the answer + m_flags |= REPLACE_ALL; + return DECISION_REPLACE; + + case QMessageBox::NoToAll: + qDebug().nospace() << "user wants to replace none"; + // remember the answer + m_flags |= REPLACE_NONE; + return DECISION_SKIP; + + case QMessageBox::Cancel: // fall-through + default: + qDebug().nospace() << "user wants to cancel"; + return DECISION_CANCEL; + } +} + +bool FileRenamer::removeFailed(const QString& name) +{ + QMessageBox::StandardButtons buttons = QMessageBox::Ok; + if (m_flags & MULTIPLE) { + // only show cancel for multiple files + buttons |= QMessageBox::Cancel; + } + + const auto answer = QMessageBox::critical( + m_parent, QObject::tr("File operation failed"), + QObject::tr("Failed to remove \"%1\". Maybe you lack the required file permissions?").arg(name), + buttons); + + if (answer == QMessageBox::Cancel) { + // user wants to stop + qDebug().nospace() << "user wants to cancel"; + return false; + } + + // skip this one and continue + qDebug().nospace() << "user wants to skip"; + return true; +} + +bool FileRenamer::renameFailed(const QString& oldName, const QString& newName) +{ + QMessageBox::StandardButtons buttons = QMessageBox::Ok; + if (m_flags & MULTIPLE) { + // only show cancel for multiple files + buttons |= QMessageBox::Cancel; + } + + const auto answer = QMessageBox::critical( + m_parent, QObject::tr("File operation failed"), + QObject::tr("failed to rename %1 to %2").arg(oldName).arg(QDir::toNativeSeparators(newName)), + buttons); + + if (answer == QMessageBox::Cancel) { + // user wants to stop + qDebug().nospace() << "user wants to cancel"; + return false; + } + + // skip this one and continue + qDebug().nospace() << "user wants to skip"; + return true; +} diff --git a/src/filerenamer.h b/src/filerenamer.h new file mode 100644 index 00000000..cd57244c --- /dev/null +++ b/src/filerenamer.h @@ -0,0 +1,140 @@ +#ifndef FILERENAMER_H +#define FILERENAMER_H + +#include + +/** +* Renames individual files and handles dialog boxes to confirm replacements and +* failures with the user +**/ +class FileRenamer +{ +public: + /** + * controls appearance and replacement behaviour; if RENAME_REPLACE_ALL and + * RENAME_REPLACE_NONE are not provided, the user will have the option to + * choose on the first replacement + **/ + enum RenameFlags + { + /** + * this renamer will be used on multiple files, so display additional + * buttons to replace all and for canceling + **/ + MULTIPLE = 0x01, + + /** + * customizes some of the text shown on dialog to mention that files are + * being hidden + **/ + HIDE = 0x02, + + /** + * customizes some of the text shown on dialog to mention that files are + * being unhidden + **/ + UNHIDE = 0x04, + + /** + * silently replaces all existing files + **/ + REPLACE_ALL = 0x08, + + /** + * silently skips all existing files + **/ + REPLACE_NONE = 0x10, + }; + + + /** result of a single rename + * + **/ + enum RenameResults + { + /** + * the user skipped this file + */ + RESULT_SKIP, + + /** + * the file was successfully renamed + */ + RESULT_OK, + + /** + * the user wants to cancel + */ + RESULT_CANCEL + }; + + + /** + * @param parent Parent widget for dialog boxes + **/ + FileRenamer(QWidget* parent, QFlags flags); + + /** + * renames the given file + * @param oldName current filename + * @param newName new filename + * @return whether the file was renamed, skipped or the user wants to cancel + **/ + RenameResults rename(const QString& oldName, const QString& newName); + +private: + /** + *user's decision when replacing + **/ + enum RenameDecision + { + /** + * replace the file + **/ + DECISION_REPLACE, + + /** + * skip the file + **/ + DECISION_SKIP, + + /** + * cancel the whole thing + **/ + DECISION_CANCEL + }; + + /** + * parent widget for dialog boxes + **/ + QWidget* m_parent; + + /** + * flags + **/ + QFlags m_flags; + + /** + * asks the user to replace an existing file, may return early if the user + * has already selected to replace all/none + * @return whether to replace, skip or cancel + **/ + RenameDecision confirmReplace(const QString& newName); + + /** + * removal of a file failed, ask the user to continue or cancel + * @param name The name of the file that failed to be removed + * @return true to continue, false to stop + **/ + bool removeFailed(const QString& name); + + /** + * renaming a file failed, ask the user to continue or cancel + * @param oldName current filename + * @param newName new filename + * @return true to continue, false to stop + **/ + bool renameFailed(const QString& oldName, const QString& newName); +}; + +#endif // FILERENAMER_H diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 44934544..f553a7be 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -78,193 +78,6 @@ static bool operator<(const ModFileListWidget &LHS, const ModFileListWidget &RHS const int max_scan_for_context_menu = 50; -FileRenamer::FileRenamer(QWidget* parent, QFlags flags) - : m_parent(parent), m_flags(flags) -{ - // sanity check for flags - if ((m_flags & (HIDE|UNHIDE)) == 0) { - qCritical("renameFile() missing hide flag"); - // doesn't really matter, it's just for text - m_flags = HIDE; - } -} - -FileRenamer::RenameResults FileRenamer::rename(const QString& oldName, const QString& newName) -{ - qDebug().nospace() << "renaming " << oldName << " to " << newName; - - if (QFileInfo(newName).exists()) { - qDebug().nospace() << newName << " already exists"; - - // target file already exists, confirm replacement - auto answer = confirmReplace(newName); - - switch (answer) { - case DECISION_SKIP: { - // user wants to skip this file - qDebug().nospace() << "skipping " << oldName; - return RESULT_SKIP; - } - - case DECISION_REPLACE: { - qDebug().nospace() << "removing " << newName; - // user wants to replace the file, so remove it - if (!QFile(newName).remove()) { - qWarning().nospace() << "failed to remove " << newName; - // removal failed, warn the user and allow canceling - if (!removeFailed(newName)) { - qDebug().nospace() << "canceling " << oldName; - // user wants to cancel - return RESULT_CANCEL; - } - - // ignore this file and continue on - qDebug().nospace() << "skipping " << oldName; - return RESULT_SKIP; - } - - break; - } - - case DECISION_CANCEL: // fall-through - default: { - // user wants to stop - qDebug().nospace() << "canceling"; - return RESULT_CANCEL; - } - } - } - - // target either didn't exist or was removed correctly - - if (!QFile::rename(oldName, newName)) { - qWarning().nospace() << "failed to rename " << oldName << " to " << newName; - - // renaming failed, warn the user and allow canceling - if (!renameFailed(oldName, newName)) { - // user wants to cancel - qDebug().nospace() << "canceling"; - return RESULT_CANCEL; - } - - // ignore this file and continue on - qDebug().nospace() << "skipping " << oldName; - return RESULT_SKIP; - } - - // everything worked - qDebug().nospace() << "successfully renamed " << oldName << " to " << newName; - return RESULT_OK; -} - -FileRenamer::RenameDecision FileRenamer::confirmReplace(const QString& newName) -{ - if (m_flags & REPLACE_ALL) { - // user wants to silently replace all - qDebug().nospace() << "user has selected replace all"; - return DECISION_REPLACE; - } - else if (m_flags & REPLACE_NONE) { - // user wants to silently skip all - qDebug().nospace() << "user has selected replace none"; - return DECISION_SKIP; - } - - QString text; - - if (m_flags & HIDE) { - text = QObject::tr("The hidden file \"%1\" already exists. Replace it?").arg(newName); - } - else if (m_flags & UNHIDE) { - text = QObject::tr("The visible file \"%1\" already exists. Replace it?").arg(newName); - } - - auto buttons = QMessageBox::Yes | QMessageBox::No; - if (m_flags & MULTIPLE) { - // only show these buttons when there are multiple files to replace - buttons |= QMessageBox::YesToAll | QMessageBox::NoToAll | QMessageBox::Cancel; - } - - const auto answer = QMessageBox::question( - m_parent, QObject::tr("Replace file?"), text, buttons); - - switch (answer) { - case QMessageBox::Yes: - qDebug().nospace() << "user wants to replace"; - return DECISION_REPLACE; - - case QMessageBox::No: - qDebug().nospace() << "user wants to skip"; - return DECISION_SKIP; - - case QMessageBox::YesToAll: - qDebug().nospace() << "user wants to replace all"; - // remember the answer - m_flags |= REPLACE_ALL; - return DECISION_REPLACE; - - case QMessageBox::NoToAll: - qDebug().nospace() << "user wants to replace none"; - // remember the answer - m_flags |= REPLACE_NONE; - return DECISION_SKIP; - - case QMessageBox::Cancel: // fall-through - default: - qDebug().nospace() << "user wants to cancel"; - return DECISION_CANCEL; - } -} - -bool FileRenamer::removeFailed(const QString& name) -{ - QMessageBox::StandardButtons buttons = QMessageBox::Ok; - if (m_flags & MULTIPLE) { - // only show cancel for multiple files - buttons |= QMessageBox::Cancel; - } - - const auto answer = QMessageBox::critical( - m_parent, QObject::tr("File operation failed"), - QObject::tr("Failed to remove \"%1\". Maybe you lack the required file permissions?").arg(name), - buttons); - - if (answer == QMessageBox::Cancel) { - // user wants to stop - qDebug().nospace() << "user wants to cancel"; - return false; - } - - // skip this one and continue - qDebug().nospace() << "user wants to skip"; - return true; -} - -bool FileRenamer::renameFailed(const QString& oldName, const QString& newName) -{ - QMessageBox::StandardButtons buttons = QMessageBox::Ok; - if (m_flags & MULTIPLE) { - // only show cancel for multiple files - buttons |= QMessageBox::Cancel; - } - - const auto answer = QMessageBox::critical( - m_parent, QObject::tr("File operation failed"), - QObject::tr("failed to rename %1 to %2").arg(oldName).arg(QDir::toNativeSeparators(newName)), - buttons); - - if (answer == QMessageBox::Cancel) { - // user wants to stop - qDebug().nospace() << "user wants to cancel"; - return false; - } - - // skip this one and continue - qDebug().nospace() << "user wants to skip"; - return true; -} - - ExpanderWidget::ExpanderWidget() : m_button(nullptr), m_content(nullptr), opened_(false) { diff --git a/src/modinfodialog.h b/src/modinfodialog.h index c4f65d8a..b3ce3d07 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -26,6 +26,7 @@ along with Mod Organizer. If not, see . #include "plugincontainer.h" #include "organizercore.h" #include "filterwidget.h" +#include "filerenamer.h" #include #include @@ -49,140 +50,6 @@ class QFileSystemModel; class QTreeView; class CategoryFactory; -/** -* Renames individual files and handles dialog boxes to confirm replacements and -* failures with the user -**/ -class FileRenamer -{ -public: - /** - * controls appearance and replacement behaviour; if RENAME_REPLACE_ALL and - * RENAME_REPLACE_NONE are not provided, the user will have the option to - * choose on the first replacement - **/ - enum RenameFlags - { - /** - * this renamer will be used on multiple files, so display additional - * buttons to replace all and for canceling - **/ - MULTIPLE = 0x01, - - /** - * customizes some of the text shown on dialog to mention that files are - * being hidden - **/ - HIDE = 0x02, - - /** - * customizes some of the text shown on dialog to mention that files are - * being unhidden - **/ - UNHIDE = 0x04, - - /** - * silently replaces all existing files - **/ - REPLACE_ALL = 0x08, - - /** - * silently skips all existing files - **/ - REPLACE_NONE = 0x10, - }; - - - /** result of a single rename - * - **/ - enum RenameResults - { - /** - * the user skipped this file - */ - RESULT_SKIP, - - /** - * the file was successfully renamed - */ - RESULT_OK, - - /** - * the user wants to cancel - */ - RESULT_CANCEL - }; - - - /** - * @param parent Parent widget for dialog boxes - **/ - FileRenamer(QWidget* parent, QFlags flags); - - /** - * renames the given file - * @param oldName current filename - * @param newName new filename - * @return whether the file was renamed, skipped or the user wants to cancel - **/ - RenameResults rename(const QString& oldName, const QString& newName); - -private: - /** - *user's decision when replacing - **/ - enum RenameDecision - { - /** - * replace the file - **/ - DECISION_REPLACE, - - /** - * skip the file - **/ - DECISION_SKIP, - - /** - * cancel the whole thing - **/ - DECISION_CANCEL - }; - - /** - * parent widget for dialog boxes - **/ - QWidget* m_parent; - - /** - * flags - **/ - QFlags m_flags; - - /** - * asks the user to replace an existing file, may return early if the user - * has already selected to replace all/none - * @return whether to replace, skip or cancel - **/ - RenameDecision confirmReplace(const QString& newName); - - /** - * removal of a file failed, ask the user to continue or cancel - * @param name The name of the file that failed to be removed - * @return true to continue, false to stop - **/ - bool removeFailed(const QString& name); - - /** - * renaming a file failed, ask the user to continue or cancel - * @param oldName current filename - * @param newName new filename - * @return true to continue, false to stop - **/ - bool renameFailed(const QString& oldName, const QString& newName); -}; - /* Takes a QToolButton and a widget and creates an expandable widget. **/ -- cgit v1.3.1 From 6bd5bed29f3ebcc1155e615d7daa1c9cd1724efb Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sat, 15 Jun 2019 18:54:19 -0400 Subject: added initial toolbar, splitter moved most of the text editor stuff to a new TextEditor class --- src/CMakeLists.txt | 3 ++ src/modinfodialog.cpp | 77 ++++++++++++++++-------------- src/modinfodialog.h | 10 +++- src/modinfodialog.ui | 130 +++++++++++++++++++++++++++++++++----------------- src/texteditor.cpp | 76 +++++++++++++++++++++++++++++ src/texteditor.h | 36 ++++++++++++++ 6 files changed, 249 insertions(+), 83 deletions(-) create mode 100644 src/texteditor.cpp create mode 100644 src/texteditor.h (limited to 'src/modinfodialog.cpp') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f654f9b4..f75ab1e0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -119,6 +119,7 @@ SET(organizer_SRCS statusbar.cpp apiuseraccount.cpp filerenamer.cpp + texteditor.cpp shared/windows_error.cpp shared/error_report.cpp @@ -219,6 +220,7 @@ SET(organizer_HDRS statusbar.h apiuseraccount.h filerenamer.h + texteditor.h shared/windows_error.h shared/error_report.h @@ -414,6 +416,7 @@ set(widgets modidlineedit noeditdelegate qtgroupingproxy + texteditor viewmarkingscrollbar ) diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index f553a7be..db5b5fd0 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -36,6 +36,7 @@ along with Mod Organizer. If not, see . #include "pluginlistsortproxy.h" #include "previewgenerator.h" #include "previewdialog.h" +#include "texteditor.h" #include #include @@ -358,6 +359,17 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo } } + m_textFileEditor.reset(new TextEditor(ui->textFileView)); + + connect( + m_textFileEditor.get(), &TextEditor::changed, + [&](bool b){ onTextFileChanged(b); }); + + ui->tabTextSplitter->setSizes({200, 1}); + ui->tabTextSplitter->setStretchFactor(0, 0); + ui->tabTextSplitter->setStretchFactor(1, 1); + setTextFileWordWrap(true); + // 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 @@ -1030,9 +1042,12 @@ void ModInfoDialog::thumbnailClicked(const QString &fileName) bool ModInfoDialog::allowNavigateFromTXT() { - if (ui->saveTXTButton->isEnabled()) { - int res = QMessageBox::question(this, tr("Save changes?"), tr("Save changes to \"%1\"?").arg(ui->textFileView->property("currentFile").toString()), - QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); + if (m_textFileEditor->dirty()) { + const int res = QMessageBox::question( + this, tr("Save changes?"), + tr("Save changes to \"%1\"?").arg(m_textFileEditor->filename()), + QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); + if (res == QMessageBox::Cancel) { return false; } else if (res == QMessageBox::Yes) { @@ -1042,7 +1057,6 @@ bool ModInfoDialog::allowNavigateFromTXT() return true; } - bool ModInfoDialog::allowNavigateFromINI() { if (ui->saveButton->isEnabled()) { @@ -1057,14 +1071,14 @@ bool ModInfoDialog::allowNavigateFromINI() return true; } - -void ModInfoDialog::on_textFileList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous) +void ModInfoDialog::on_textFileList_currentItemChanged( + QListWidgetItem *current, QListWidgetItem *previous) { - QString fullPath = m_RootPath + "/" + current->text(); + const QString fullPath = m_RootPath + "/" + current->text(); - QVariant currentFile = ui->textFileView->property("currentFile"); - if (currentFile.isValid() && (currentFile.toString() == fullPath)) { - // the new file is the same as the currently displayed file. May be the result of a cancelation + if (fullPath == m_textFileEditor->filename()) { + // the new file is the same as the currently displayed file. May be the + // result of a cancellation return; } @@ -1075,17 +1089,12 @@ void ModInfoDialog::on_textFileList_currentItemChanged(QListWidgetItem *current, } } - void ModInfoDialog::openTextFile(const QString &fileName) { - QString encoding; - ui->textFileView->setText(MOBase::readFileText(fileName, &encoding)); - ui->textFileView->setProperty("currentFile", fileName); - ui->textFileView->setProperty("encoding", encoding); - ui->saveTXTButton->setEnabled(false); + m_textFileEditor->load(fileName); + ui->textFileSave->setEnabled(false); } - void ModInfoDialog::openIniFile(const QString &fileName) { QFile iniFile(fileName); @@ -1155,35 +1164,31 @@ void ModInfoDialog::on_iniTweaksList_currentItemChanged(QListWidgetItem *current } - void ModInfoDialog::on_saveButton_clicked() { saveCurrentIniFile(); } - -void ModInfoDialog::on_saveTXTButton_clicked() +void ModInfoDialog::on_textFileSave_clicked() { saveCurrentTextFile(); } +void ModInfoDialog::on_textFileWordWrap_clicked() +{ + setTextFileWordWrap(!m_textFileEditor->wordWrap()); +} + +void ModInfoDialog::setTextFileWordWrap(bool b) +{ + m_textFileEditor->wordWrap(b); + ui->textFileWordWrap->setChecked(b); +} void ModInfoDialog::saveCurrentTextFile() { - QVariant fileNameVar = ui->textFileView->property("currentFile"); - QVariant encodingVar = ui->textFileView->property("encoding"); - if (fileNameVar.isValid() && encodingVar.isValid()) { - QString fileName = fileNameVar.toString(); - QFile txtFile(fileName); - txtFile.open(QIODevice::WriteOnly); - txtFile.resize(0); - QTextCodec *codec = QTextCodec::codecForName(encodingVar.toString().toUtf8()); - QString data = ui->textFileView->toPlainText().replace("\n", "\r\n"); - txtFile.write(codec->fromUnicode(data)); - } else { - reportError("no file selected"); - } - ui->saveTXTButton->setEnabled(false); + m_textFileEditor->save(); + ui->textFileSave->setEnabled(false); } @@ -1214,9 +1219,9 @@ void ModInfoDialog::on_iniFileView_textChanged() } -void ModInfoDialog::on_textFileView_textChanged() +void ModInfoDialog::onTextFileChanged(bool b) { - ui->saveTXTButton->setEnabled(true); + ui->textFileSave->setEnabled(b); } diff --git a/src/modinfodialog.h b/src/modinfodialog.h index b3ce3d07..85226f45 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -35,6 +35,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include #include #include #include @@ -49,6 +50,7 @@ namespace Ui { class QFileSystemModel; class QTreeView; class CategoryFactory; +class TextEditor; /* Takes a QToolButton and a widget and creates an expandable widget. @@ -218,14 +220,15 @@ private slots: void on_saveButton_clicked(); void on_activateESP_clicked(); void on_deactivateESP_clicked(); - void on_saveTXTButton_clicked(); + void on_textFileSave_clicked(); + void on_textFileWordWrap_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_iniFileView_textChanged(); - void on_textFileView_textChanged(); + void onTextFileChanged(bool b); void on_tabWidget_currentChanged(int index); void on_primaryCategoryBox_currentIndexChanged(int index); void on_categoriesTree_itemChanged(QTreeWidgetItem *item, int column); @@ -304,6 +307,7 @@ private: ExpanderWidget m_overwriteExpander, m_overwrittenExpander, m_nonconflictExpander; FilterWidget m_advancedConflictFilter; + std::unique_ptr m_textFileEditor; void refreshConflictLists(bool refreshGeneral, bool refreshAdvanced); @@ -347,6 +351,8 @@ private: std::vector createGotoActions( const QList& selection); + + void setTextFileWordWrap(bool b); }; #endif // MODINFODIALOG_H diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui index eed4e31f..4a29f6e4 100644 --- a/src/modinfodialog.ui +++ b/src/modinfodialog.ui @@ -27,49 +27,89 @@ - Textfiles + Text Files - + - - - - 192 - 16777215 - - - - A list of text-files in the mod directory. + + + Qt::Horizontal - - A list of text-files in the mod directory like readmes. + + false + + + A list of text-files in the mod directory. + + + A list of text-files in the mod directory like readmes. + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Save + + + + + + + Word wrap + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - false - - - Save - - - - - @@ -493,12 +533,12 @@ text-align: left; 2 - - 365 - 200 + + 365 + File @@ -569,12 +609,12 @@ text-align: left; 2 - - 365 - 200 + + 365 + File @@ -1011,7 +1051,7 @@ p, li { white-space: pre-wrap; } - + 0 @@ -1024,7 +1064,7 @@ p, li { white-space: pre-wrap; } 200 - + about:blank diff --git a/src/texteditor.cpp b/src/texteditor.cpp new file mode 100644 index 00000000..eef74246 --- /dev/null +++ b/src/texteditor.cpp @@ -0,0 +1,76 @@ +#include "texteditor.h" +#include "utility.h" + +TextEditor::TextEditor(QPlainTextEdit* edit) + : m_edit(edit), m_dirty(false) +{ + m_edit->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); + wordWrap(true); + + QObject::connect( + m_edit->document(), &QTextDocument::modificationChanged, + [&](bool b){ onChanged(b); }); +} + +bool TextEditor::load(const QString& filename) +{ + m_filename = filename; + m_edit->setPlainText(MOBase::readFileText(filename, &m_encoding)); + m_edit->document()->setModified(false); + + return true; +} + +bool TextEditor::save() +{ + if (m_filename.isEmpty() || m_encoding.isEmpty()) { + return false; + } + + QFile file(m_filename); + file.open(QIODevice::WriteOnly); + file.resize(0); + + QTextCodec* codec = QTextCodec::codecForName(m_encoding.toUtf8()); + QString data = m_edit->toPlainText().replace("\n", "\r\n"); + + file.write(codec->fromUnicode(data)); + m_edit->document()->setModified(false); + + return true; +} + +const QString& TextEditor::filename() const +{ + return m_filename; +} + +void TextEditor::wordWrap(bool b) +{ + if (b) { + m_edit->setLineWrapMode(QPlainTextEdit::WidgetWidth); + } else { + m_edit->setLineWrapMode(QPlainTextEdit::NoWrap); + } +} + +bool TextEditor::wordWrap() const +{ + return (m_edit->lineWrapMode() == QPlainTextEdit::WidgetWidth); +} + +void TextEditor::dirty(bool b) +{ + m_dirty = b; +} + +bool TextEditor::dirty() const +{ + return m_dirty; +} + +void TextEditor::onChanged(bool b) +{ + dirty(b); + emit changed(b); +} diff --git a/src/texteditor.h b/src/texteditor.h new file mode 100644 index 00000000..25a15ad7 --- /dev/null +++ b/src/texteditor.h @@ -0,0 +1,36 @@ +#ifndef MO_TEXTEDITOR_H +#define MO_TEXTEDITOR_H + +#include + +class TextEditor : public QObject +{ + Q_OBJECT + +public: + TextEditor(QPlainTextEdit* edit); + + bool load(const QString& filename); + bool save(); + + const QString& filename() const; + + void wordWrap(bool b); + bool wordWrap() const; + + bool dirty() const; + +signals: + void changed(bool b); + +private: + QPlainTextEdit* m_edit; + QString m_filename; + QString m_encoding; + bool m_dirty; + + void onChanged(bool b); + void dirty(bool b); +}; + +#endif // MO_TEXTEDITOR_H -- cgit v1.3.1 From 9f509f9aa2a1066a223bfd23fb54862f9c988b78 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sat, 15 Jun 2019 19:36:55 -0400 Subject: TextEditor now has a dynamic toolbar --- src/modinfodialog.cpp | 31 --------------- src/modinfodialog.h | 5 --- src/modinfodialog.ui | 68 ++++----------------------------- src/texteditor.cpp | 102 ++++++++++++++++++++++++++++++++++++++++++++++++-- src/texteditor.h | 29 +++++++++++++- 5 files changed, 132 insertions(+), 103 deletions(-) (limited to 'src/modinfodialog.cpp') diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index db5b5fd0..169623ad 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -361,14 +361,9 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo m_textFileEditor.reset(new TextEditor(ui->textFileView)); - connect( - m_textFileEditor.get(), &TextEditor::changed, - [&](bool b){ onTextFileChanged(b); }); - ui->tabTextSplitter->setSizes({200, 1}); ui->tabTextSplitter->setStretchFactor(0, 0); ui->tabTextSplitter->setStretchFactor(1, 1); - setTextFileWordWrap(true); // 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 @@ -1092,7 +1087,6 @@ void ModInfoDialog::on_textFileList_currentItemChanged( void ModInfoDialog::openTextFile(const QString &fileName) { m_textFileEditor->load(fileName); - ui->textFileSave->setEnabled(false); } void ModInfoDialog::openIniFile(const QString &fileName) @@ -1169,26 +1163,9 @@ void ModInfoDialog::on_saveButton_clicked() saveCurrentIniFile(); } -void ModInfoDialog::on_textFileSave_clicked() -{ - saveCurrentTextFile(); -} - -void ModInfoDialog::on_textFileWordWrap_clicked() -{ - setTextFileWordWrap(!m_textFileEditor->wordWrap()); -} - -void ModInfoDialog::setTextFileWordWrap(bool b) -{ - m_textFileEditor->wordWrap(b); - ui->textFileWordWrap->setChecked(b); -} - void ModInfoDialog::saveCurrentTextFile() { m_textFileEditor->save(); - ui->textFileSave->setEnabled(false); } @@ -1211,20 +1188,12 @@ void ModInfoDialog::saveCurrentIniFile() ui->saveButton->setEnabled(false); } - void ModInfoDialog::on_iniFileView_textChanged() { QPushButton* saveButton = findChild("saveButton"); saveButton->setEnabled(true); } - -void ModInfoDialog::onTextFileChanged(bool b) -{ - ui->textFileSave->setEnabled(b); -} - - void ModInfoDialog::on_activateESP_clicked() { QListWidget *activeESPList = findChild("activeESPList"); diff --git a/src/modinfodialog.h b/src/modinfodialog.h index 85226f45..42ef990d 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -220,15 +220,12 @@ private slots: void on_saveButton_clicked(); void on_activateESP_clicked(); void on_deactivateESP_clicked(); - void on_textFileSave_clicked(); - void on_textFileWordWrap_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_iniFileView_textChanged(); - void onTextFileChanged(bool b); void on_tabWidget_currentChanged(int index); void on_primaryCategoryBox_currentIndexChanged(int index); void on_categoriesTree_itemChanged(QTreeWidgetItem *item, int column); @@ -351,8 +348,6 @@ private: std::vector createGotoActions( const QList& selection); - - void setTextFileWordWrap(bool b); }; #endif // MODINFODIALOG_H diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui index 4a29f6e4..513506a7 100644 --- a/src/modinfodialog.ui +++ b/src/modinfodialog.ui @@ -46,67 +46,13 @@ A list of text-files in the mod directory like readmes. - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Save - - - - - - - Word wrap - - - true - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - - - - - - + + + + + + + diff --git a/src/texteditor.cpp b/src/texteditor.cpp index eef74246..9de2b9c1 100644 --- a/src/texteditor.cpp +++ b/src/texteditor.cpp @@ -1,15 +1,18 @@ #include "texteditor.h" #include "utility.h" +#include TextEditor::TextEditor(QPlainTextEdit* edit) - : m_edit(edit), m_dirty(false) + : m_edit(edit), m_toolbar(*this), m_dirty(false) { + setupToolbar(); + m_edit->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); wordWrap(true); QObject::connect( m_edit->document(), &QTextDocument::modificationChanged, - [&](bool b){ onChanged(b); }); + [&](bool b){ onModified(b); }); } bool TextEditor::load(const QString& filename) @@ -69,8 +72,99 @@ bool TextEditor::dirty() const return m_dirty; } -void TextEditor::onChanged(bool b) +void TextEditor::onModified(bool b) { dirty(b); - emit changed(b); + emit modified(b); +} + +void TextEditor::setupToolbar() +{ + auto* widget = wrapEditWidget(); + if (!widget) { + return; + } + + auto* layout = new QVBoxLayout(widget); + + // adding toolbar and edit + layout->addWidget(m_toolbar.widget()); + layout->addWidget(m_edit); + + // make the edit stretch + layout->setStretch(0, 0); + layout->setStretch(1, 1); + + // visuals + layout->setContentsMargins(0, 0, 0, 0); + widget->show(); +} + +QWidget* TextEditor::wrapEditWidget() +{ + auto widget = std::make_unique(); + + // wrapping the QPlainTextEdit into a new widget so the toolbar can be + // displayed above it + + if (auto* parentLayout=m_edit->parentWidget()->layout()) { + // the edit's parent has a regular layout, replace the edit by the new + // widget and delete the QLayoutItem that's returned as it's not needed + delete parentLayout->replaceWidget(m_edit, widget.get()); + } else if (auto* splitter=qobject_cast(m_edit->parentWidget())) { + // the edit's parent is a QSplitter, which doesn't have a layout; replace + // the edit by using its index in the splitter + splitter->replaceWidget(splitter->indexOf(m_edit), widget.get()); + } else { + // unknown parent + qCritical("TextEditor: cannot wrap edit widget to display a toolbar"); + return nullptr; + } + + return widget.release(); +} + + +TextEditorToolbar::TextEditorToolbar(TextEditor& editor) : + m_editor(editor), + m_widget(new QWidget), + m_save(new QAction(QObject::tr("&Save"))), + m_wordWrap(new QAction(QObject::tr("&Word wrap"))) +{ + QObject::connect(m_save, &QAction::triggered, [&]{ onSave(); }); + QObject::connect(m_wordWrap, &QAction::triggered, [&]{ onWordWrap(); }); + + auto* layout = new QHBoxLayout(m_widget); + layout->setContentsMargins(0, 0, 0, 0); + layout->setAlignment(Qt::AlignLeft); + + auto* b = new QToolButton; + b->setDefaultAction(m_save); + layout->addWidget(b); + + b = new QToolButton; + b->setDefaultAction(m_wordWrap); + layout->addWidget(b); + + QObject::connect(&m_editor, &TextEditor::modified, [&](bool b){ onTextModified(b); }); +} + +QWidget* TextEditorToolbar::widget() +{ + return m_widget; +} + +void TextEditorToolbar::onTextModified(bool b) +{ + m_save->setEnabled(b); +} + +void TextEditorToolbar::onSave() +{ + m_editor.save(); +} + +void TextEditorToolbar::onWordWrap() +{ + m_editor.wordWrap(!m_editor.wordWrap()); } diff --git a/src/texteditor.h b/src/texteditor.h index 25a15ad7..10f6c4de 100644 --- a/src/texteditor.h +++ b/src/texteditor.h @@ -3,6 +3,27 @@ #include +class TextEditor; + +class TextEditorToolbar +{ +public: + TextEditorToolbar(TextEditor& editor); + + QWidget* widget(); + +private: + TextEditor& m_editor; + QWidget* m_widget; + QAction* m_save; + QAction* m_wordWrap; + + void onTextModified(bool b); + void onSave(); + void onWordWrap(); +}; + + class TextEditor : public QObject { Q_OBJECT @@ -21,16 +42,20 @@ public: bool dirty() const; signals: - void changed(bool b); + void modified(bool b); private: QPlainTextEdit* m_edit; + TextEditorToolbar m_toolbar; QString m_filename; QString m_encoding; bool m_dirty; - void onChanged(bool b); + void onModified(bool b); void dirty(bool b); + + void setupToolbar(); + QWidget* wrapEditWidget(); }; #endif // MO_TEXTEDITOR_H -- cgit v1.3.1 From c79cf72d2250631110e8605ced6f34dda0378dc0 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sat, 15 Jun 2019 21:00:04 -0400 Subject: line numbers, which required inheriting from QPlainTextEdit instead --- src/modinfodialog.cpp | 12 ++-- src/modinfodialog.h | 1 - src/modinfodialog.ui | 14 ++--- src/texteditor.cpp | 148 +++++++++++++++++++++++++++++++++++++++++++------- src/texteditor.h | 33 +++++++++-- 5 files changed, 170 insertions(+), 38 deletions(-) (limited to 'src/modinfodialog.cpp') diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 169623ad..743f76b0 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -359,7 +359,7 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo } } - m_textFileEditor.reset(new TextEditor(ui->textFileView)); + ui->textFileView->setupToolbar(); ui->tabTextSplitter->setSizes({200, 1}); ui->tabTextSplitter->setStretchFactor(0, 0); @@ -1037,10 +1037,10 @@ void ModInfoDialog::thumbnailClicked(const QString &fileName) bool ModInfoDialog::allowNavigateFromTXT() { - if (m_textFileEditor->dirty()) { + if (ui->textFileView->dirty()) { const int res = QMessageBox::question( this, tr("Save changes?"), - tr("Save changes to \"%1\"?").arg(m_textFileEditor->filename()), + tr("Save changes to \"%1\"?").arg(ui->textFileView->filename()), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); if (res == QMessageBox::Cancel) { @@ -1071,7 +1071,7 @@ void ModInfoDialog::on_textFileList_currentItemChanged( { const QString fullPath = m_RootPath + "/" + current->text(); - if (fullPath == m_textFileEditor->filename()) { + if (fullPath == ui->textFileView->filename()) { // the new file is the same as the currently displayed file. May be the // result of a cancellation return; @@ -1086,7 +1086,7 @@ void ModInfoDialog::on_textFileList_currentItemChanged( void ModInfoDialog::openTextFile(const QString &fileName) { - m_textFileEditor->load(fileName); + ui->textFileView->load(fileName); } void ModInfoDialog::openIniFile(const QString &fileName) @@ -1165,7 +1165,7 @@ void ModInfoDialog::on_saveButton_clicked() void ModInfoDialog::saveCurrentTextFile() { - m_textFileEditor->save(); + ui->textFileView->save(); } diff --git a/src/modinfodialog.h b/src/modinfodialog.h index 42ef990d..09924671 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -304,7 +304,6 @@ private: ExpanderWidget m_overwriteExpander, m_overwrittenExpander, m_nonconflictExpander; FilterWidget m_advancedConflictFilter; - std::unique_ptr m_textFileEditor; void refreshConflictLists(bool refreshGeneral, bool refreshAdvanced); diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui index 513506a7..785fdeb4 100644 --- a/src/modinfodialog.ui +++ b/src/modinfodialog.ui @@ -46,14 +46,7 @@ A list of text-files in the mod directory like readmes. - - - - - - - - + @@ -1217,6 +1210,11 @@ p, li { white-space: pre-wrap; } QLineEdit
modidlineedit.h
+ + TextEditor + QPlainTextEdit +
texteditor.h
+
diff --git a/src/texteditor.cpp b/src/texteditor.cpp index 56ccfd2c..ecb46547 100644 --- a/src/texteditor.cpp +++ b/src/texteditor.cpp @@ -2,26 +2,27 @@ #include "utility.h" #include -TextEditor::TextEditor(QPlainTextEdit* edit) - : m_edit(edit), m_toolbar(*this), m_dirty(false) +TextEditor::TextEditor(QWidget* parent) : + QPlainTextEdit(parent), + m_toolbar(*this), m_lineNumbers(nullptr), m_dirty(false) { - setupToolbar(); - - m_edit->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); + setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); wordWrap(true); + m_lineNumbers = new TextEditorLineNumbers(*this); + emit modified(false); QObject::connect( - m_edit->document(), &QTextDocument::modificationChanged, + document(), &QTextDocument::modificationChanged, [&](bool b){ onModified(b); }); } bool TextEditor::load(const QString& filename) { m_filename = filename; - m_edit->setPlainText(MOBase::readFileText(filename, &m_encoding)); - m_edit->document()->setModified(false); + setPlainText(MOBase::readFileText(filename, &m_encoding)); + document()->setModified(false); return true; } @@ -37,10 +38,10 @@ bool TextEditor::save() file.resize(0); QTextCodec* codec = QTextCodec::codecForName(m_encoding.toUtf8()); - QString data = m_edit->toPlainText().replace("\n", "\r\n"); + QString data = toPlainText().replace("\n", "\r\n"); file.write(codec->fromUnicode(data)); - m_edit->document()->setModified(false); + document()->setModified(false); return true; } @@ -53,9 +54,9 @@ const QString& TextEditor::filename() const void TextEditor::wordWrap(bool b) { if (b) { - m_edit->setLineWrapMode(QPlainTextEdit::WidgetWidth); + setLineWrapMode(QPlainTextEdit::WidgetWidth); } else { - m_edit->setLineWrapMode(QPlainTextEdit::NoWrap); + setLineWrapMode(QPlainTextEdit::NoWrap); } emit wordWrapChanged(b); @@ -68,7 +69,7 @@ void TextEditor::toggleWordWrap() bool TextEditor::wordWrap() const { - return (m_edit->lineWrapMode() == QPlainTextEdit::WidgetWidth); + return (lineWrapMode() == QPlainTextEdit::WidgetWidth); } void TextEditor::dirty(bool b) @@ -98,7 +99,7 @@ void TextEditor::setupToolbar() // adding toolbar and edit layout->addWidget(m_toolbar.widget()); - layout->addWidget(m_edit); + layout->addWidget(this); // make the edit stretch layout->setStretch(0, 0); @@ -116,23 +117,132 @@ QWidget* TextEditor::wrapEditWidget() // wrapping the QPlainTextEdit into a new widget so the toolbar can be // displayed above it - if (auto* parentLayout=m_edit->parentWidget()->layout()) { + if (auto* parentLayout=parentWidget()->layout()) { // the edit's parent has a regular layout, replace the edit by the new // widget and delete the QLayoutItem that's returned as it's not needed - delete parentLayout->replaceWidget(m_edit, widget.get()); - } else if (auto* splitter=qobject_cast(m_edit->parentWidget())) { + delete parentLayout->replaceWidget(this, widget.get()); + + } else if (auto* splitter=qobject_cast(parentWidget())) { // the edit's parent is a QSplitter, which doesn't have a layout; replace // the edit by using its index in the splitter - splitter->replaceWidget(splitter->indexOf(m_edit), widget.get()); + auto index = splitter->indexOf(this); + + if (index == -1) { + qCritical( + "TextEditor: cannot wrap edit widget to display a toolbar, " + "parent is a splitter, but widget isn't in it"); + + return nullptr; + } + + splitter->replaceWidget(index, widget.get()); + } else { // unknown parent - qCritical("TextEditor: cannot wrap edit widget to display a toolbar"); + qCritical( + "TextEditor: cannot wrap edit widget to display a toolbar, " + "no parent or parent has no layout"); + return nullptr; } return widget.release(); } +void TextEditor::resizeEvent(QResizeEvent* e) +{ + QPlainTextEdit::resizeEvent(e); + + QRect cr = contentsRect(); + m_lineNumbers->setGeometry(QRect(cr.left(), cr.top(), m_lineNumbers->areaWidth(), cr.height())); +} + +void TextEditor::paintLineNumbers(QPaintEvent* e) +{ + QPainter painter(m_lineNumbers); + painter.fillRect(e->rect(), Qt::lightGray); + + QTextBlock block = firstVisibleBlock(); + int blockNumber = block.blockNumber(); + int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top(); + int bottom = top + (int) blockBoundingRect(block).height(); + + while (block.isValid() && top <= e->rect().bottom()) { + if (block.isVisible() && bottom >= e->rect().top()) { + QString number = QString::number(blockNumber + 1); + painter.setPen(Qt::black); + + painter.drawText( + 0, top, m_lineNumbers->width(), fontMetrics().height(), + Qt::AlignRight, number); + } + + block = block.next(); + top = bottom; + bottom = top + (int) blockBoundingRect(block).height(); + ++blockNumber; + } +} + + +TextEditorLineNumbers::TextEditorLineNumbers(TextEditor& editor) + : QWidget(&editor), m_editor(editor) +{ + setFont(editor.font()); + + connect(&m_editor, &QPlainTextEdit::blockCountChanged, [&]{ updateAreaWidth(); }); + connect(&m_editor, &QPlainTextEdit::updateRequest, [&](auto&& rect, int dy){ updateArea(rect, dy); }); + //connect(e, SIGNAL(cursorPositionChanged()), this, SLOT(highlightCurrentLine())); + + updateAreaWidth(); + //highlightCurrentLine(); +} + +QSize TextEditorLineNumbers::sizeHint() const +{ + return QSize(areaWidth(), 0); +} + +void TextEditorLineNumbers::paintEvent(QPaintEvent* e) +{ + m_editor.paintLineNumbers(e); +} + +int TextEditorLineNumbers::areaWidth() const +{ + int digits = 1; + int max = qMax(1, m_editor.blockCount()); + + while (max >= 10) { + max /= 10; + ++digits; + } + + digits = std::max(3, digits); + + int space = 3 + fontMetrics().horizontalAdvance(QLatin1Char('9')) * digits; + + return space; +} + +void TextEditorLineNumbers::updateAreaWidth() +{ + m_editor.setViewportMargins(areaWidth(), 0, 0, 0); +} + +void TextEditorLineNumbers::updateArea(const QRect &rect, int dy) +{ + if (dy) { + scroll(0, dy); + } else { + update(0, rect.y(), width(), rect.height()); + } + + if (rect.contains(m_editor.viewport()->rect())) { + updateAreaWidth(); + } +} + TextEditorToolbar::TextEditorToolbar(TextEditor& editor) : m_editor(editor), diff --git a/src/texteditor.h b/src/texteditor.h index c196b7a4..af542341 100644 --- a/src/texteditor.h +++ b/src/texteditor.h @@ -23,12 +23,33 @@ private: }; -class TextEditor : public QObject +class TextEditorLineNumbers : public QWidget +{ +public: + TextEditorLineNumbers(TextEditor& editor); + QSize sizeHint() const override; + int areaWidth() const; + +protected: + void paintEvent(QPaintEvent *event) override; + +private: + TextEditor& m_editor; + + void updateAreaWidth(); + void updateArea(const QRect &rect, int dy); +}; + + +class TextEditor : public QPlainTextEdit { Q_OBJECT + friend class TextEditorLineNumbers; public: - TextEditor(QPlainTextEdit* edit); + TextEditor(QWidget* parent=nullptr); + + void setupToolbar(); bool load(const QString& filename); bool save(); @@ -45,9 +66,12 @@ signals: void modified(bool b); void wordWrapChanged(bool b); +protected: + void resizeEvent(QResizeEvent* e) override; + private: - QPlainTextEdit* m_edit; TextEditorToolbar m_toolbar; + TextEditorLineNumbers* m_lineNumbers; QString m_filename; QString m_encoding; bool m_dirty; @@ -55,8 +79,9 @@ private: void onModified(bool b); void dirty(bool b); - void setupToolbar(); QWidget* wrapEditWidget(); + + void paintLineNumbers(QPaintEvent* e); }; #endif // MO_TEXTEDITOR_H -- cgit v1.3.1 From ab7f42dd97e5e6f6076877469a774213bfc3bf76 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Mon, 17 Jun 2019 03:12:49 -0400 Subject: split ExpanderWidget into its own set of files --- src/CMakeLists.txt | 3 +++ src/expanderwidget.cpp | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/expanderwidget.h | 46 ++++++++++++++++++++++++++++++++++++++++++ src/modinfodialog.cpp | 54 -------------------------------------------------- src/modinfodialog.h | 42 +-------------------------------------- 5 files changed, 104 insertions(+), 95 deletions(-) create mode 100644 src/expanderwidget.cpp create mode 100644 src/expanderwidget.h (limited to 'src/modinfodialog.cpp') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f75ab1e0..d5aa8247 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -120,6 +120,7 @@ SET(organizer_SRCS apiuseraccount.cpp filerenamer.cpp texteditor.cpp + expanderwidget.cpp shared/windows_error.cpp shared/error_report.cpp @@ -221,6 +222,7 @@ SET(organizer_HDRS apiuseraccount.h filerenamer.h texteditor.h + expanderwidget.h shared/windows_error.h shared/error_report.h @@ -405,6 +407,7 @@ set(utilities set(widgets descriptionpage + expanderwidget genericicondelegate filerenamer filterwidget diff --git a/src/expanderwidget.cpp b/src/expanderwidget.cpp new file mode 100644 index 00000000..2f47da5b --- /dev/null +++ b/src/expanderwidget.cpp @@ -0,0 +1,54 @@ +#include "expanderwidget.h" + +ExpanderWidget::ExpanderWidget() + : m_button(nullptr), m_content(nullptr), opened_(false) +{ +} + +ExpanderWidget::ExpanderWidget(QToolButton* button, QWidget* content) + : ExpanderWidget() +{ + set(button, content); +} + +void ExpanderWidget::set(QToolButton* button, QWidget* content, bool o) +{ + m_button = button; + m_content = content; + + m_button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + QObject::connect(m_button, &QToolButton::clicked, [&]{ toggle(); }); + + toggle(o); +} + +void ExpanderWidget::toggle() +{ + if (opened()) { + toggle(false); + } + else { + toggle(true); + } +} + +void ExpanderWidget::toggle(bool b) +{ + if (b) { + m_button->setArrowType(Qt::DownArrow); + m_content->show(); + } else { + m_button->setArrowType(Qt::RightArrow); + m_content->hide(); + } + + // the state has to be remembered instead of using m_content's visibility + // because saving the state in saveConflictExpandersState() happens after the + // dialog is closed, which marks all the widgets hidden + opened_ = b; +} + +bool ExpanderWidget::opened() const +{ + return opened_; +} diff --git a/src/expanderwidget.h b/src/expanderwidget.h new file mode 100644 index 00000000..da3eb9d6 --- /dev/null +++ b/src/expanderwidget.h @@ -0,0 +1,46 @@ +#ifndef EXPANDERWIDGET_H +#define EXPANDERWIDGET_H + +#include + +/* Takes a QToolButton and a widget and creates an expandable widget. +**/ +class ExpanderWidget +{ +public: + /** empty expander, use set() + **/ + ExpanderWidget(); + + /** see set() + **/ + ExpanderWidget(QToolButton* button, QWidget* content); + + /** @brief sets the button and content widgets to use + * the button will be given an arrow icon, clicking it will toggle the + * visibility of the given widget + * @param button the button that toggles the content + * @param content the widget that will be shown or hidden + * @param opened initial state, defaults to closed + **/ + void set(QToolButton* button, QWidget* content, bool opened=false); + + /** either opens or closes the expander depending on the current state + **/ + void toggle(); + + /** sets the current state of the expander + **/ + void toggle(bool b); + + /** returns whether the expander is currently opened + **/ + bool opened() const; + +private: + QToolButton* m_button; + QWidget* m_content; + bool opened_; +}; + +#endif // EXPANDERWIDGET_H diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 743f76b0..065058fc 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -79,60 +79,6 @@ static bool operator<(const ModFileListWidget &LHS, const ModFileListWidget &RHS const int max_scan_for_context_menu = 50; -ExpanderWidget::ExpanderWidget() - : m_button(nullptr), m_content(nullptr), opened_(false) -{ -} - -ExpanderWidget::ExpanderWidget(QToolButton* button, QWidget* content) - : ExpanderWidget() -{ - set(button, content); -} - -void ExpanderWidget::set(QToolButton* button, QWidget* content, bool o) -{ - m_button = button; - m_content = content; - - m_button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - QObject::connect(m_button, &QToolButton::clicked, [&]{ toggle(); }); - - toggle(o); -} - -void ExpanderWidget::toggle() -{ - if (opened()) { - toggle(false); - } - else { - toggle(true); - } -} - -void ExpanderWidget::toggle(bool b) -{ - if (b) { - m_button->setArrowType(Qt::DownArrow); - m_content->show(); - } else { - m_button->setArrowType(Qt::RightArrow); - m_content->hide(); - } - - // the state has to be remembered instead of using m_content's visibility - // because saving the state in saveConflictExpandersState() happens after the - // dialog is closed, which marks all the widgets hidden - opened_ = b; -} - -bool ExpanderWidget::opened() const -{ - return opened_; -} - - class ElideLeftDelegate : public QStyledItemDelegate { public: diff --git a/src/modinfodialog.h b/src/modinfodialog.h index 09924671..df450ac0 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -27,6 +27,7 @@ along with Mod Organizer. If not, see . #include "organizercore.h" #include "filterwidget.h" #include "filerenamer.h" +#include "expanderwidget.h" #include #include @@ -53,47 +54,6 @@ class CategoryFactory; class TextEditor; -/* Takes a QToolButton and a widget and creates an expandable widget. - **/ -class ExpanderWidget -{ -public: - /** empty expander, use set() - **/ - ExpanderWidget(); - - /** see set() - **/ - ExpanderWidget(QToolButton* button, QWidget* content); - - /** @brief sets the button and content widgets to use - * the button will be given an arrow icon, clicking it will toggle the - * visibility of the given widget - * @param button the button that toggles the content - * @param content the widget that will be shown or hidden - * @param opened initial state, defaults to closed - **/ - void set(QToolButton* button, QWidget* content, bool opened=false); - - /** either opens or closes the expander depending on the current state - **/ - void toggle(); - - /** sets the current state of the expander - **/ - void toggle(bool b); - - /** returns whether the expander is currently opened - **/ - bool opened() const; - -private: - QToolButton* m_button; - QWidget* m_content; - bool opened_; -}; - - /** * this is a larger dialog used to visualise information abount the mod. * @todo this would probably a good place for a plugin-system -- cgit v1.3.1 From 6e2ae2444ca0bdea964cf0551edd338e6dafa2bc Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Mon, 17 Jun 2019 04:00:00 -0400 Subject: split text file tab into its own class --- src/CMakeLists.txt | 3 ++ src/modinfodialog.cpp | 100 +++++++++++++++++------------------------ src/modinfodialog.h | 29 +++++++++--- src/modinfodialogtextfiles.cpp | 91 +++++++++++++++++++++++++++++++++++++ src/modinfodialogtextfiles.h | 21 +++++++++ 5 files changed, 180 insertions(+), 64 deletions(-) create mode 100644 src/modinfodialogtextfiles.cpp create mode 100644 src/modinfodialogtextfiles.h (limited to 'src/modinfodialog.cpp') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d5aa8247..7e62f3e9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -54,6 +54,7 @@ SET(organizer_SRCS modlist.cpp modidlineedit.cpp modinfodialog.cpp + modinfodialogtextfiles.cpp modinfo.cpp modinfobackup.cpp modinfoforeign.cpp @@ -154,6 +155,7 @@ SET(organizer_HDRS modlist.h modidlineedit.h modinfodialog.h + modinfodialogtextfiles.h modinfo.h modinfobackup.h modinfoforeign.h @@ -352,6 +354,7 @@ set(modinfo modinfo modinfobackup modinfodialog + modinfodialogtextfiles modinfoforeign modinfooverwrite modinforegular diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 065058fc..7e72653f 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -38,6 +38,8 @@ along with Mod Organizer. If not, see . #include "previewdialog.h" #include "texteditor.h" +#include "modinfodialogtextfiles.h" + #include #include #include @@ -59,6 +61,18 @@ using namespace MOBase; using namespace MOShared; +std::vector> ModInfoDialogTab::createTabs( + Ui::ModInfoDialog* ui) +{ + std::vector> v; + + v.push_back(std::make_unique(ui)); + + return v; +} + + + class ModFileListWidget : public QListWidgetItem { friend bool operator<(const ModFileListWidget &LHS, const ModFileListWidget &RHS); public: @@ -254,6 +268,9 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo m_OrganizerCore(organizerCore), m_PluginContainer(pluginContainer) { ui->setupUi(this); + + m_tabs = ModInfoDialogTab::createTabs(ui); + this->setWindowTitle(modInfo->name()); this->setWindowModality(Qt::WindowModal); @@ -305,12 +322,6 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo } } - ui->textFileView->setupToolbar(); - - ui->tabTextSplitter->setSizes({200, 1}); - ui->tabTextSplitter->setStretchFactor(0, 0); - ui->tabTextSplitter->setStretchFactor(1, 1); - // 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 @@ -333,15 +344,17 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo } else if (unmanaged) { + ui->tabWidget->setTabEnabled(TAB_TEXTFILES, false); ui->tabWidget->setTabEnabled(TAB_INIFILES, false); 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_ESPS, false); - ui->tabWidget->setTabEnabled(TAB_TEXTFILES, false); ui->tabWidget->setTabEnabled(TAB_IMAGES, false); } else { + ui->tabWidget->setTabEnabled(TAB_TEXTFILES, true); + initFiletree(modInfo); addCategories(CategoryFactory::instance(), modInfo->getCategories(), ui->categoriesTree->invisibleRootItem(), 0); refreshPrimaryCategoriesBox(); @@ -838,7 +851,10 @@ QTreeWidgetItem* ModInfoDialog::createAdvancedConflictItem( void ModInfoDialog::refreshFiles() { // clearing - ui->textFileList->clear(); + for (auto& tab : m_tabs) { + tab->clear(); + } + ui->iniTweaksList->clear(); ui->iniFileList->clear(); ui->inactiveESPList->clear(); @@ -857,9 +873,13 @@ void ModInfoDialog::refreshFiles() while (dirIterator.hasNext()) { QString fileName = dirIterator.next(); - if (fileName.endsWith(".txt", Qt::CaseInsensitive)) { - ui->textFileList->addItem(fileName.mid(m_RootPath.length() + 1)); - } else if ((fileName.endsWith(".ini", Qt::CaseInsensitive) || fileName.endsWith(".cfg", Qt::CaseInsensitive)) && + for (auto& tab : m_tabs) { + if (tab->feedFile(m_RootPath, fileName)) { + break; + } + } + + if ((fileName.endsWith(".ini", Qt::CaseInsensitive) || fileName.endsWith(".cfg", Qt::CaseInsensitive)) && !fileName.endsWith("meta.ini")) { QString namePart = fileName.mid(m_RootPath.length() + 1); if (namePart.startsWith("INI Tweaks", Qt::CaseInsensitive)) { @@ -903,7 +923,6 @@ void ModInfoDialog::refreshFiles() } } - ui->tabWidget->setTabEnabled(TAB_TEXTFILES, ui->textFileList->count() != 0); ui->tabWidget->setTabEnabled(TAB_IMAGES, ui->thumbnailArea->count() != 0); ui->tabWidget->setTabEnabled(TAB_ESPS, (ui->inactiveESPList->count() != 0) || (ui->activeESPList->count() != 0)); } @@ -943,9 +962,17 @@ void ModInfoDialog::saveCategories(QTreeWidgetItem *currentNode) void ModInfoDialog::on_closeButton_clicked() { - if (allowNavigateFromTXT() && allowNavigateFromINI()) { - this->close(); + for (auto& tab : m_tabs) { + if (!tab->canClose()) { + return; + } + } + + if (!allowNavigateFromINI()) { + return; } + + close(); } @@ -981,23 +1008,6 @@ void ModInfoDialog::thumbnailClicked(const QString &fileName) ui->imageLabel->setPixmap(QPixmap::fromImage(image)); } -bool ModInfoDialog::allowNavigateFromTXT() -{ - if (ui->textFileView->dirty()) { - const int res = QMessageBox::question( - this, tr("Save changes?"), - tr("Save changes to \"%1\"?").arg(ui->textFileView->filename()), - QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); - - if (res == QMessageBox::Cancel) { - return false; - } else if (res == QMessageBox::Yes) { - saveCurrentTextFile(); - } - } - return true; -} - bool ModInfoDialog::allowNavigateFromINI() { if (ui->saveButton->isEnabled()) { @@ -1012,29 +1022,6 @@ bool ModInfoDialog::allowNavigateFromINI() return true; } -void ModInfoDialog::on_textFileList_currentItemChanged( - QListWidgetItem *current, QListWidgetItem *previous) -{ - const QString fullPath = m_RootPath + "/" + current->text(); - - if (fullPath == ui->textFileView->filename()) { - // the new file is the same as the currently displayed file. May be the - // result of a cancellation - return; - } - - if (allowNavigateFromTXT()) { - openTextFile(fullPath); - } else { - ui->textFileList->setCurrentItem(previous, QItemSelectionModel::Current); - } -} - -void ModInfoDialog::openTextFile(const QString &fileName) -{ - ui->textFileView->load(fileName); -} - void ModInfoDialog::openIniFile(const QString &fileName) { QFile iniFile(fileName); @@ -1109,11 +1096,6 @@ void ModInfoDialog::on_saveButton_clicked() saveCurrentIniFile(); } -void ModInfoDialog::saveCurrentTextFile() -{ - ui->textFileView->save(); -} - void ModInfoDialog::saveCurrentIniFile() { diff --git a/src/modinfodialog.h b/src/modinfodialog.h index df450ac0..97ab9a38 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -53,9 +53,30 @@ class QTreeView; class CategoryFactory; class TextEditor; +class TextFilesTab; + + +class ModInfoDialogTab +{ +public: + static std::vector> createTabs( + Ui::ModInfoDialog* ui); + + 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() = 0; +}; + /** - * this is a larger dialog used to visualise information abount the mod. + * this is a larger dialog used to visualise information about the mod. * @todo this would probably a good place for a plugin-system **/ class ModInfoDialog : public MOBase::TutorableDialog @@ -147,11 +168,8 @@ private: void deleteFile(const QModelIndex &index); void saveIniTweaks(); void saveCategories(QTreeWidgetItem *currentNode); - void saveCurrentTextFile(); void saveCurrentIniFile(); - void openTextFile(const QString &fileName); void openIniFile(const QString &fileName); - bool allowNavigateFromTXT(); bool allowNavigateFromINI(); FileRenamer::RenameResults hideFile(FileRenamer& renamer, const QString &oldName); FileRenamer::RenameResults unhideFile(FileRenamer& renamer, const QString &oldName); @@ -189,7 +207,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_textFileList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous); void on_iniFileList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous); void on_iniTweaksList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous); void on_overwriteTree_itemDoubleClicked(QTreeWidgetItem *item, int column); @@ -234,6 +251,8 @@ private: ModInfo::Ptr m_ModInfo; + std::vector> m_tabs; + QSignalMapper m_ThumbnailMapper; QString m_RootPath; diff --git a/src/modinfodialogtextfiles.cpp b/src/modinfodialogtextfiles.cpp new file mode 100644 index 00000000..98da8c4a --- /dev/null +++ b/src/modinfodialogtextfiles.cpp @@ -0,0 +1,91 @@ +#include "modinfodialogtextfiles.h" +#include "ui_modinfodialog.h" +#include + +class TextFileItem : public QListWidgetItem +{ +public: + TextFileItem(const QString& rootPath, QString fullPath) + : m_fullPath(std::move(fullPath)) + { + setText(m_fullPath.mid(rootPath.length() + 1)); + } + + const QString& fullPath() const + { + return m_fullPath; + } + +private: + QString m_fullPath; +}; + + +TextFilesTab::TextFilesTab(Ui::ModInfoDialog* ui) + : ui(ui) +{ + ui->textFileView->setupToolbar(); + + ui->tabTextSplitter->setSizes({200, 1}); + ui->tabTextSplitter->setStretchFactor(0, 0); + ui->tabTextSplitter->setStretchFactor(1, 1); + + QObject::connect( + ui->textFileList, &QListWidget::currentItemChanged, + [&](auto* current, auto* previous){ onSelection(current, previous); }); +} + +void TextFilesTab::clear() +{ + ui->textFileList->clear(); +} + +bool TextFilesTab::feedFile(const QString& rootPath, const QString& fullPath) +{ + if (fullPath.endsWith(".txt", Qt::CaseInsensitive)) { + ui->textFileList->addItem(new TextFileItem(rootPath, fullPath)); + return true; + } + + return false; +} + +bool TextFilesTab::canClose() +{ + if (!ui->textFileView->dirty()) { + return true; + } + + const int res = QMessageBox::question( + ui->tabText, + QObject::tr("Save changes?"), + QObject::tr("Save changes to \"%1\"?").arg(ui->textFileView->filename()), + QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); + + if (res == QMessageBox::Cancel) { + return false; + } + + if (res == QMessageBox::Yes) { + ui->textFileView->save(); + } + + return true; +} + +void TextFilesTab::onSelection( + QListWidgetItem* current, QListWidgetItem* previous) +{ + auto* item = dynamic_cast(current); + if (!item) { + qCritical("TextFilesTab: item is not a TextFileItem"); + return; + } + + if (!canClose()) { + ui->textFileList->setCurrentItem(previous, QItemSelectionModel::Current); + return; + } + + ui->textFileView->load(item->fullPath()); +} diff --git a/src/modinfodialogtextfiles.h b/src/modinfodialogtextfiles.h new file mode 100644 index 00000000..ab9e972c --- /dev/null +++ b/src/modinfodialogtextfiles.h @@ -0,0 +1,21 @@ +#ifndef MODINFODIALOGTEXTFILES_H +#define MODINFODIALOGTEXTFILES_H + +#include "modinfodialog.h" + +class TextFilesTab : public ModInfoDialogTab +{ +public: + TextFilesTab(Ui::ModInfoDialog* ui); + + void clear() override; + bool feedFile(const QString& rootPath, const QString& fullPath) override; + bool canClose() override; + +private: + Ui::ModInfoDialog* ui; + + void onSelection(QListWidgetItem* current, QListWidgetItem* previous); +}; + +#endif // MODINFODIALOGTEXTFILES_H -- cgit v1.3.1 From b96f154625a0a139e722731b055f022bc90c2058 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Mon, 17 Jun 2019 04:29:15 -0400 Subject: removed unused ini tweaks widgets splitter in ini tab --- src/modinfodialog.cpp | 90 +------------------ src/modinfodialog.h | 6 -- src/modinfodialog.ui | 233 ++++++++++++++++++++++++++------------------------ 3 files changed, 122 insertions(+), 207 deletions(-) (limited to 'src/modinfodialog.cpp') diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 7e72653f..23b8f4d8 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -359,7 +359,6 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo addCategories(CategoryFactory::instance(), modInfo->getCategories(), ui->categoriesTree->invisibleRootItem(), 0); refreshPrimaryCategoriesBox(); } - initINITweaks(); ui->tabWidget->setTabEnabled(TAB_CONFLICTS, m_Origin != nullptr); @@ -421,7 +420,6 @@ ModInfoDialog::~ModInfoDialog() else m_ModInfo->setNotes(ui->notesEdit->toHtml()); saveCategories(ui->categoriesTree->invisibleRootItem()); - saveIniTweaks(); // ini tweaks are written to the ini file directly. This is the only information not managed by ModInfo delete ui->descriptionView->page(); delete ui->descriptionView; delete ui; @@ -436,19 +434,6 @@ int ModInfoDialog::exec() return TutorableDialog::exec(); } -void ModInfoDialog::initINITweaks() -{ - int numTweaks = m_Settings->beginReadArray("INI Tweaks"); - for (int i = 0; i < numTweaks; ++i) { - m_Settings->setArrayIndex(i); - QList items = ui->iniTweaksList->findItems(m_Settings->value("name").toString(), Qt::MatchFixedString); - if (items.size() != 0) { - items.at(0)->setCheckState(Qt::Checked); - } - } - m_Settings->endArray(); -} - void ModInfoDialog::initFiletree(ModInfo::Ptr modInfo) { ui->fileTree = findChild("fileTree"); @@ -855,7 +840,6 @@ void ModInfoDialog::refreshFiles() tab->clear(); } - ui->iniTweaksList->clear(); ui->iniFileList->clear(); ui->inactiveESPList->clear(); ui->activeESPList->clear(); @@ -882,15 +866,7 @@ void ModInfoDialog::refreshFiles() if ((fileName.endsWith(".ini", Qt::CaseInsensitive) || fileName.endsWith(".cfg", Qt::CaseInsensitive)) && !fileName.endsWith("meta.ini")) { QString namePart = fileName.mid(m_RootPath.length() + 1); - if (namePart.startsWith("INI Tweaks", Qt::CaseInsensitive)) { - QListWidgetItem *newItem = new QListWidgetItem(namePart.mid(11), ui->iniTweaksList); - newItem->setData(Qt::UserRole, namePart); - newItem->setFlags(newItem->flags() | Qt::ItemIsUserCheckable); - newItem->setCheckState(Qt::Unchecked); - ui->iniTweaksList->addItem(newItem); - } else { - ui->iniFileList->addItem(namePart); - } + ui->iniFileList->addItem(namePart); } else if (fileName.endsWith(".esp", Qt::CaseInsensitive) || fileName.endsWith(".esm", Qt::CaseInsensitive) || fileName.endsWith(".esl", Qt::CaseInsensitive)) { @@ -1038,23 +1014,6 @@ void ModInfoDialog::openIniFile(const QString &fileName) ui->saveButton->setEnabled(false); } - -void ModInfoDialog::saveIniTweaks() -{ - m_Settings->remove("INI Tweaks"); - m_Settings->beginWriteArray("INI Tweaks"); - - int countEnabled = 0; - for (int i = 0; i < ui->iniTweaksList->count(); ++i) { - if (ui->iniTweaksList->item(i)->checkState() == Qt::Checked) { - m_Settings->setArrayIndex(countEnabled++); - m_Settings->setValue("name", ui->iniTweaksList->item(i)->text()); - } - } - m_Settings->endArray(); -} - - void ModInfoDialog::on_iniFileList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous) { QString fullPath = m_RootPath + "/" + current->text(); @@ -1072,25 +1031,6 @@ void ModInfoDialog::on_iniFileList_currentItemChanged(QListWidgetItem *current, } } - -void ModInfoDialog::on_iniTweaksList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous) -{ - QString fullPath = m_RootPath + "/" + current->data(Qt::UserRole).toString(); - - QVariant currentFile = ui->iniFileView->property("currentFile"); - if (currentFile.isValid() && (currentFile.toString() == fullPath)) { - // the new file is the same as the currently displayed file. May be the result of a cancelation - return; - } - - if (allowNavigateFromINI()) { - openIniFile(fullPath); - } else { - ui->iniFileList->setCurrentItem(previous, QItemSelectionModel::Current); - } - -} - void ModInfoDialog::on_saveButton_clicked() { saveCurrentIniFile(); @@ -2129,31 +2069,3 @@ void ModInfoDialog::on_prevButton_clicked() emit modOpenPrev(tab); this->accept(); } - - -void ModInfoDialog::createTweak() -{ - QString name = QInputDialog::getText(this, tr("Name"), tr("Please enter a name")); - if (name.isNull()) { - return; - } else if (!fixDirectoryName(name)) { - QMessageBox::critical(this, tr("Error"), tr("Invalid name. Must be a valid file name")); - return; - } else if (ui->iniTweaksList->findItems(name, Qt::MatchFixedString).count() != 0) { - QMessageBox::critical(this, tr("Error"), tr("A tweak by that name exists")); - return; - } - - QListWidgetItem *newTweak = new QListWidgetItem(name + ".ini"); - newTweak->setData(Qt::UserRole, "INI Tweaks/" + name + ".ini"); - newTweak->setFlags(newTweak->flags() | Qt::ItemIsUserCheckable); - newTweak->setCheckState(Qt::Unchecked); - ui->iniTweaksList->addItem(newTweak); -} - -void ModInfoDialog::on_iniTweaksList_customContextMenuRequested(const QPoint &pos) -{ - QMenu menu; - menu.addAction(tr("Create Tweak"), this, SLOT(createTweak())); - menu.exec(ui->iniTweaksList->mapToGlobal(pos)); -} diff --git a/src/modinfodialog.h b/src/modinfodialog.h index 97ab9a38..0d54177a 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -153,7 +153,6 @@ public slots: private: void initFiletree(ModInfo::Ptr modInfo); - void initINITweaks(); void refreshLists(); @@ -166,7 +165,6 @@ private: QString getFileCategory(int categoryID); bool recursiveDelete(const QModelIndex &index); void deleteFile(const QModelIndex &index); - void saveIniTweaks(); void saveCategories(QTreeWidgetItem *currentNode); void saveCurrentIniFile(); void openIniFile(const QString &fileName); @@ -208,7 +206,6 @@ private slots: void on_primaryCategoryBox_currentIndexChanged(int index); void on_categoriesTree_itemChanged(QTreeWidgetItem *item, int column); void on_iniFileList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous); - void on_iniTweaksList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous); void on_overwriteTree_itemDoubleClicked(QTreeWidgetItem *item, int column); void on_overwrittenTree_itemDoubleClicked(QTreeWidgetItem *item, int column); void on_overwriteTree_customContextMenuRequested(const QPoint &pos); @@ -225,9 +222,6 @@ private slots: void on_prevButton_clicked(); - void on_iniTweaksList_customContextMenuRequested(const QPoint &pos); - - void createTweak(); private: using FileEntry = MOShared::FileEntry; diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui index 785fdeb4..10ebbb33 100644 --- a/src/modinfodialog.ui +++ b/src/modinfodialog.ui @@ -38,15 +38,58 @@ false - - - A list of text-files in the mod directory. - - - A list of text-files in the mod directory like readmes. - + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Text Files + + + + + + + A list of text-files in the mod directory. + + + A list of text-files in the mod directory like readmes. + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + - @@ -55,113 +98,79 @@ INI-Files - + - - - 6 - - - QLayout::SetMinimumSize + + + Qt::Horizontal - - - - Ini Files - - - - - - - - 228 - 16777215 - - - - 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. - - - - - - - false - - - Ini Tweaks *This feature is non-functional* - - - - - - - false - - - - 228 - 16777215 - - - - Qt::CustomContextMenu - - - This is a list of ini tweaks (ini modifications that can be toggled). - - - This is a list of ini tweaks. Ini Tweaks are (usually small) fragments of ini files that are applied over existing settings in skyrim.ini/skyrimprefs.ini. Each tweak can be toggled individually. You should check the description of the mod whether the tweaks are really optional. - - - - - - - - - - - - - - - - - true - - - false - - - false - - - false - - - - - - - false - - - Save changes to the file. - - - Save changes to the file. This overwrites the original. There is no automatic backup! - - - Save + + + + 6 - - - + + + + Ini Files + + + + + + + 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 + + + false + + + false + + + false + + + + + + + false + + + Save changes to the file. + + + Save changes to the file. This overwrites the original. There is no automatic backup! + + + Save + + + + + + -- cgit v1.3.1 From 81815d202b4364847062ba248321474ef5a2d686 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Mon, 17 Jun 2019 04:54:41 -0400 Subject: split ini tab stuff to IniFilesTab refactored TextFilesTab into GenericFiles so it can be used by IniFilesTab --- src/modinfodialog.cpp | 90 +------------------------------ src/modinfodialog.h | 8 --- src/modinfodialog.ui | 38 ++----------- src/modinfodialogtextfiles.cpp | 117 +++++++++++++++++++++++++++++------------ src/modinfodialogtextfiles.h | 41 ++++++++++++--- 5 files changed, 124 insertions(+), 170 deletions(-) (limited to 'src/modinfodialog.cpp') diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 23b8f4d8..8f989d02 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -67,6 +67,7 @@ std::vector> ModInfoDialogTab::createTabs( std::vector> v; v.push_back(std::make_unique(ui)); + v.push_back(std::make_unique(ui)); return v; } @@ -840,7 +841,6 @@ void ModInfoDialog::refreshFiles() tab->clear(); } - ui->iniFileList->clear(); ui->inactiveESPList->clear(); ui->activeESPList->clear(); ui->imageLabel->setPixmap({}); @@ -863,11 +863,7 @@ void ModInfoDialog::refreshFiles() } } - if ((fileName.endsWith(".ini", Qt::CaseInsensitive) || fileName.endsWith(".cfg", Qt::CaseInsensitive)) && - !fileName.endsWith("meta.ini")) { - QString namePart = fileName.mid(m_RootPath.length() + 1); - ui->iniFileList->addItem(namePart); - } else if (fileName.endsWith(".esp", Qt::CaseInsensitive) || + if (fileName.endsWith(".esp", Qt::CaseInsensitive) || fileName.endsWith(".esm", Qt::CaseInsensitive) || fileName.endsWith(".esl", Qt::CaseInsensitive)) { QString relativePath = fileName.mid(m_RootPath.length() + 1); @@ -944,10 +940,6 @@ void ModInfoDialog::on_closeButton_clicked() } } - if (!allowNavigateFromINI()) { - return; - } - close(); } @@ -984,84 +976,6 @@ void ModInfoDialog::thumbnailClicked(const QString &fileName) ui->imageLabel->setPixmap(QPixmap::fromImage(image)); } -bool ModInfoDialog::allowNavigateFromINI() -{ - if (ui->saveButton->isEnabled()) { - int res = QMessageBox::question(this, tr("Save changes?"), tr("Save changes to \"%1\"?").arg(ui->iniFileView->property("currentFile").toString()), - QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); - if (res == QMessageBox::Cancel) { - return false; - } else if (res == QMessageBox::Yes) { - saveCurrentIniFile(); - } - } - return true; -} - -void ModInfoDialog::openIniFile(const QString &fileName) -{ - QFile iniFile(fileName); - iniFile.open(QIODevice::ReadOnly); - QByteArray buffer = iniFile.readAll(); - - QTextCodec *codec = QTextCodec::codecForUtfText(buffer, QTextCodec::codecForName("utf-8")); - QTextEdit *iniFileView = findChild("iniFileView"); - iniFileView->setText(codec->toUnicode(buffer)); - iniFileView->setProperty("currentFile", fileName); - iniFileView->setProperty("encoding", codec->name()); - iniFile.close(); - - ui->saveButton->setEnabled(false); -} - -void ModInfoDialog::on_iniFileList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous) -{ - QString fullPath = m_RootPath + "/" + current->text(); - - QVariant currentFile = ui->iniFileView->property("currentFile"); - if (currentFile.isValid() && (currentFile.toString() == fullPath)) { - // the new file is the same as the currently displayed file. May be the result of a cancelation - return; - } - - if (allowNavigateFromINI()) { - openIniFile(fullPath); - } else { - ui->iniFileList->setCurrentItem(previous, QItemSelectionModel::Current); - } -} - -void ModInfoDialog::on_saveButton_clicked() -{ - saveCurrentIniFile(); -} - - -void ModInfoDialog::saveCurrentIniFile() -{ - QVariant fileNameVar = ui->iniFileView->property("currentFile"); - QVariant encodingVar = ui->iniFileView->property("encoding"); - if (fileNameVar.isValid() && !fileNameVar.toString().isEmpty()) { - QString fileName = fileNameVar.toString(); - QDir().mkpath(QFileInfo(fileName).absolutePath()); - QFile txtFile(fileName); - txtFile.open(QIODevice::WriteOnly); - txtFile.resize(0); - QTextCodec *codec = QTextCodec::codecForName(encodingVar.toString().toUtf8()); - QString data = ui->iniFileView->toPlainText().replace("\n", "\r\n"); - txtFile.write(codec->fromUnicode(data)); - } else { - reportError("no file selected"); - } - ui->saveButton->setEnabled(false); -} - -void ModInfoDialog::on_iniFileView_textChanged() -{ - QPushButton* saveButton = findChild("saveButton"); - saveButton->setEnabled(true); -} - void ModInfoDialog::on_activateESP_clicked() { QListWidget *activeESPList = findChild("activeESPList"); diff --git a/src/modinfodialog.h b/src/modinfodialog.h index 0d54177a..70fd157c 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -53,8 +53,6 @@ class QTreeView; class CategoryFactory; class TextEditor; -class TextFilesTab; - class ModInfoDialogTab { @@ -166,9 +164,6 @@ private: bool recursiveDelete(const QModelIndex &index); void deleteFile(const QModelIndex &index); void saveCategories(QTreeWidgetItem *currentNode); - void saveCurrentIniFile(); - void openIniFile(const QString &fileName); - bool allowNavigateFromINI(); FileRenamer::RenameResults hideFile(FileRenamer& renamer, const QString &oldName); FileRenamer::RenameResults unhideFile(FileRenamer& renamer, const QString &oldName); void addCheckedCategories(QTreeWidgetItem *tree); @@ -193,7 +188,6 @@ private slots: void on_openInExplorerButton_clicked(); void on_closeButton_clicked(); - void on_saveButton_clicked(); void on_activateESP_clicked(); void on_deactivateESP_clicked(); void on_visitNexusLabel_linkActivated(const QString &link); @@ -201,11 +195,9 @@ private slots: void on_sourceGameEdit_currentIndexChanged(int); void on_versionEdit_editingFinished(); void on_customUrlLineEdit_editingFinished(); - void on_iniFileView_textChanged(); void on_tabWidget_currentChanged(int index); void on_primaryCategoryBox_currentIndexChanged(int index); void on_categoriesTree_itemChanged(QTreeWidgetItem *item, int column); - void on_iniFileList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous); void on_overwriteTree_itemDoubleClicked(QTreeWidgetItem *item, int column); void on_overwrittenTree_itemDoubleClicked(QTreeWidgetItem *item, int column); void on_overwriteTree_customContextMenuRequested(const QPoint &pos); diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui index 10ebbb33..c6ab111f 100644 --- a/src/modinfodialog.ui +++ b/src/modinfodialog.ui @@ -86,7 +86,7 @@ 0 - + @@ -96,11 +96,11 @@ - INI-Files + INI Files - + Qt::Horizontal @@ -129,43 +129,15 @@ - + - + - - true - - - false - - - false - - - false - - - - - - - false - - - Save changes to the file. - - - Save changes to the file. This overwrites the original. There is no automatic backup! - - - Save - diff --git a/src/modinfodialogtextfiles.cpp b/src/modinfodialogtextfiles.cpp index 50174ec5..cf800aa6 100644 --- a/src/modinfodialogtextfiles.cpp +++ b/src/modinfodialogtextfiles.cpp @@ -2,10 +2,10 @@ #include "ui_modinfodialog.h" #include -class TextFileItem : public QListWidgetItem +class FileListItem : public QListWidgetItem { public: - TextFileItem(const QString& rootPath, QString fullPath) + FileListItem(const QString& rootPath, QString fullPath) : m_fullPath(std::move(fullPath)) { setText(m_fullPath.mid(rootPath.length() + 1)); @@ -21,46 +21,36 @@ private: }; -TextFilesTab::TextFilesTab(Ui::ModInfoDialog* ui) - : ui(ui) +GenericFilesTab::GenericFilesTab(QListWidget* list, QSplitter* sp, TextEditor* e) + : m_list(list), m_editor(e) { - ui->textFileView->setupToolbar(); + m_editor->setupToolbar(); - ui->tabTextSplitter->setSizes({200, 1}); - ui->tabTextSplitter->setStretchFactor(0, 0); - ui->tabTextSplitter->setStretchFactor(1, 1); + sp->setSizes({200, 1}); + sp->setStretchFactor(0, 0); + sp->setStretchFactor(1, 1); QObject::connect( - ui->textFileList, &QListWidget::currentItemChanged, + m_list, &QListWidget::currentItemChanged, [&](auto* current, auto* previous){ onSelection(current, previous); }); } -void TextFilesTab::clear() +void GenericFilesTab::clear() { - ui->textFileList->clear(); + m_list->clear(); select(nullptr); } -bool TextFilesTab::feedFile(const QString& rootPath, const QString& fullPath) +bool GenericFilesTab::canClose() { - if (fullPath.endsWith(".txt", Qt::CaseInsensitive)) { - ui->textFileList->addItem(new TextFileItem(rootPath, fullPath)); - return true; - } - - return false; -} - -bool TextFilesTab::canClose() -{ - if (!ui->textFileView->dirty()) { + if (!m_editor->dirty()) { return true; } const int res = QMessageBox::question( - ui->tabText, + m_list, QObject::tr("Save changes?"), - QObject::tr("Save changes to \"%1\"?").arg(ui->textFileView->filename()), + QObject::tr("Save changes to \"%1\"?").arg(m_editor->filename()), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); if (res == QMessageBox::Cancel) { @@ -68,35 +58,94 @@ bool TextFilesTab::canClose() } if (res == QMessageBox::Yes) { - ui->textFileView->save(); + m_editor->save(); } return true; } -void TextFilesTab::onSelection( +bool GenericFilesTab::feedFile(const QString& rootPath, const QString& fullPath) +{ + static constexpr const char* extensions[] = { + ".txt" + }; + + for (const auto* e : extensions) { + if (wantsFile(rootPath, fullPath)) { + m_list->addItem(new FileListItem(rootPath, fullPath)); + return true; + } + } + + return false; +} + +void GenericFilesTab::onSelection( QListWidgetItem* current, QListWidgetItem* previous) { - auto* item = dynamic_cast(current); + auto* item = dynamic_cast(current); if (!item) { - qCritical("TextFilesTab: item is not a TextFileItem"); + qCritical("TextFilesTab: item is not a FileListItem"); return; } if (!canClose()) { - ui->textFileList->setCurrentItem(previous, QItemSelectionModel::Current); + m_list->setCurrentItem(previous, QItemSelectionModel::Current); return; } select(item); } -void TextFilesTab::select(TextFileItem* item) +void GenericFilesTab::select(FileListItem* item) { if (item) { - ui->textFileView->setEnabled(true); - ui->textFileView->load(item->fullPath()); + m_editor->setEnabled(true); + m_editor->load(item->fullPath()); } else { - ui->textFileView->setEnabled(false); + m_editor->setEnabled(false); + } +} + + +TextFilesTab::TextFilesTab(Ui::ModInfoDialog* ui) + : GenericFilesTab(ui->textFileList, ui->tabTextSplitter, ui->textFileEditor) +{ +} + +bool TextFilesTab::wantsFile(const QString& rootPath, const QString& fullPath) const +{ + static constexpr const char* extensions[] = { + ".txt" + }; + + for (const auto* e : extensions) { + if (fullPath.endsWith(e, Qt::CaseInsensitive)) { + return true; + } } + + return false; +} + +IniFilesTab::IniFilesTab(Ui::ModInfoDialog* ui) + : GenericFilesTab(ui->iniFileList, ui->tabIniSplitter, ui->iniFileEditor) +{ +} + +bool IniFilesTab::wantsFile(const QString& rootPath, const QString& fullPath) const +{ + static constexpr const char* extensions[] = { + ".ini", ".cfg" + }; + + for (const auto* e : extensions) { + if (fullPath.endsWith(e, Qt::CaseInsensitive)) { + if (!fullPath.endsWith("meta.ini")) { + return true; + } + } + } + + return false; } diff --git a/src/modinfodialogtextfiles.h b/src/modinfodialogtextfiles.h index 80eb9dc0..8725ea09 100644 --- a/src/modinfodialogtextfiles.h +++ b/src/modinfodialogtextfiles.h @@ -2,23 +2,50 @@ #define MODINFODIALOGTEXTFILES_H #include "modinfodialog.h" +#include +#include -class TextFileItem; +class FileListItem; +class TextEditor; -class TextFilesTab : public ModInfoDialogTab +class GenericFilesTab : public ModInfoDialogTab { public: - TextFilesTab(Ui::ModInfoDialog* ui); + GenericFilesTab(QListWidget* list, QSplitter* splitter, TextEditor* editor); void clear() override; - bool feedFile(const QString& rootPath, const QString& fullPath) override; bool canClose() override; + bool feedFile(const QString& rootPath, const QString& fullPath) override; -private: - Ui::ModInfoDialog* ui; +protected: + QListWidget* m_list; + TextEditor* m_editor; + + virtual bool wantsFile(const QString& rootPath, const QString& fullPath) const = 0; +private: void onSelection(QListWidgetItem* current, QListWidgetItem* previous); - void select(TextFileItem* item); + void select(FileListItem* item); +}; + + +class TextFilesTab : public GenericFilesTab +{ +public: + TextFilesTab(Ui::ModInfoDialog* ui); + +protected: + bool wantsFile(const QString& rootPath, const QString& fullPath) const override; +}; + + +class IniFilesTab : public GenericFilesTab +{ +public: + IniFilesTab(Ui::ModInfoDialog* ui); + +protected: + bool wantsFile(const QString& rootPath, const QString& fullPath) const override; }; #endif // MODINFODIALOGTEXTFILES_H -- cgit v1.3.1 From 9f1520c69e88113d9a1a06f91bbabe9cf5ddef9f Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Mon, 17 Jun 2019 05:50:45 -0400 Subject: split images tab stuff in ImagesTab text editor: fixed modified flag not being set to false when loading an empty file --- src/CMakeLists.txt | 3 ++ src/modinfodialog.cpp | 51 ++++++------------------- src/modinfodialog.h | 5 +-- src/modinfodialog.ui | 8 ++-- src/modinfodialogimages.cpp | 91 +++++++++++++++++++++++++++++++++++++++++++++ src/modinfodialogimages.h | 37 ++++++++++++++++++ src/texteditor.cpp | 12 +++++- 7 files changed, 158 insertions(+), 49 deletions(-) create mode 100644 src/modinfodialogimages.cpp create mode 100644 src/modinfodialogimages.h (limited to 'src/modinfodialog.cpp') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7e62f3e9..c5018baf 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -54,6 +54,7 @@ SET(organizer_SRCS modlist.cpp modidlineedit.cpp modinfodialog.cpp + modinfodialogimages.cpp modinfodialogtextfiles.cpp modinfo.cpp modinfobackup.cpp @@ -155,6 +156,7 @@ SET(organizer_HDRS modlist.h modidlineedit.h modinfodialog.h + modinfodialogimages.h modinfodialogtextfiles.h modinfo.h modinfobackup.h @@ -354,6 +356,7 @@ set(modinfo modinfo modinfobackup modinfodialog + modinfodialogimages modinfodialogtextfiles modinfoforeign modinfooverwrite diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 8f989d02..cc3140b6 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -39,6 +39,7 @@ along with Mod Organizer. If not, see . #include "texteditor.h" #include "modinfodialogtextfiles.h" +#include "modinfodialogimages.h" #include #include @@ -68,10 +69,16 @@ std::vector> ModInfoDialogTab::createTabs( 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() +{ + return true; +} + class ModFileListWidget : public QListWidgetItem { @@ -262,7 +269,7 @@ public: 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_ThumbnailMapper(this), m_RequestStarted(false), + 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), @@ -307,8 +314,6 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo ui->descriptionView->setPage(new DescriptionPage()); - connect(&m_ThumbnailMapper, SIGNAL(mapped(const QString&)), this, SIGNAL(thumbnailClickedSignal(const QString&))); - connect(this, SIGNAL(thumbnailClickedSignal(const QString&)), this, SLOT(thumbnailClicked(const QString&))); 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 @@ -347,14 +352,16 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo { ui->tabWidget->setTabEnabled(TAB_TEXTFILES, false); ui->tabWidget->setTabEnabled(TAB_INIFILES, false); + ui->tabWidget->setTabEnabled(TAB_IMAGES, false); 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_ESPS, false); - ui->tabWidget->setTabEnabled(TAB_IMAGES, false); } else { ui->tabWidget->setTabEnabled(TAB_TEXTFILES, true); + ui->tabWidget->setTabEnabled(TAB_INIFILES, true); + ui->tabWidget->setTabEnabled(TAB_IMAGES, true); initFiletree(modInfo); addCategories(CategoryFactory::instance(), modInfo->getCategories(), ui->categoriesTree->invisibleRootItem(), 0); @@ -843,13 +850,6 @@ void ModInfoDialog::refreshFiles() ui->inactiveESPList->clear(); ui->activeESPList->clear(); - ui->imageLabel->setPixmap({}); - - while (ui->thumbnailArea->count() > 0) { - auto* item = ui->thumbnailArea->takeAt(0); - delete item->widget(); - delete item; - } if (m_RootPath.length() > 0) { @@ -875,27 +875,10 @@ void ModInfoDialog::refreshFiles() } else { ui->activeESPList->addItem(relativePath); } - } else if ((fileName.endsWith(".png", Qt::CaseInsensitive)) || - (fileName.endsWith(".jpg", Qt::CaseInsensitive))) { - QImage image = QImage(fileName); - if (!image.isNull()) { - if (static_cast(image.width()) / static_cast(image.height()) > 1.34) { - image = image.scaledToWidth(128); - } else { - image = image.scaledToHeight(96); - } - - QPushButton *thumbnailButton = new QPushButton(QPixmap::fromImage(image), ""); - thumbnailButton->setIconSize(QSize(image.width(), image.height())); - connect(thumbnailButton, SIGNAL(clicked()), &m_ThumbnailMapper, SLOT(map())); - m_ThumbnailMapper.setMapping(thumbnailButton, fileName); - ui->thumbnailArea->addWidget(thumbnailButton); - } } } } - ui->tabWidget->setTabEnabled(TAB_IMAGES, ui->thumbnailArea->count() != 0); ui->tabWidget->setTabEnabled(TAB_ESPS, (ui->inactiveESPList->count() != 0) || (ui->activeESPList->count() != 0)); } @@ -964,18 +947,6 @@ void ModInfoDialog::openTab(int tab) } } -void ModInfoDialog::thumbnailClicked(const QString &fileName) -{ - ui->imageLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); - QImage image(fileName); - if (static_cast(image.width()) / static_cast(image.height()) > 1.34) { - image = image.scaledToWidth(ui->imageLabel->geometry().width()); - } else { - image = image.scaledToHeight(ui->imageLabel->geometry().height()); - } - ui->imageLabel->setPixmap(QPixmap::fromImage(image)); -} - void ModInfoDialog::on_activateESP_clicked() { QListWidget *activeESPList = findChild("activeESPList"); diff --git a/src/modinfodialog.h b/src/modinfodialog.h index 70fd157c..f3ef6e76 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -69,7 +69,7 @@ public: virtual void clear() = 0; virtual bool feedFile(const QString& rootPath, const QString& filename) = 0; - virtual bool canClose() = 0; + virtual bool canClose(); }; @@ -135,7 +135,6 @@ public: signals: - void thumbnailClickedSignal(const QString &filename); void linkActivated(const QString &link); void downloadRequest(const QString &link); void modOpen(const QString &modName, int tab); @@ -172,7 +171,6 @@ private: int tabIndex(const QString &tabId); private slots: - void thumbnailClicked(const QString &fileName); void linkClicked(const QUrl &url); void linkClicked(QString url); @@ -239,7 +237,6 @@ private: std::vector> m_tabs; - QSignalMapper m_ThumbnailMapper; QString m_RootPath; OrganizerCore *m_OrganizerCore; diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui index c6ab111f..ba670ef3 100644 --- a/src/modinfodialog.ui +++ b/src/modinfodialog.ui @@ -154,7 +154,7 @@ - + 0 0 @@ -183,7 +183,7 @@ - + 16777215 @@ -193,7 +193,7 @@ true - + 0 @@ -225,7 +225,7 @@ 0 - + 0 diff --git a/src/modinfodialogimages.cpp b/src/modinfodialogimages.cpp new file mode 100644 index 00000000..4f2ee78c --- /dev/null +++ b/src/modinfodialogimages.cpp @@ -0,0 +1,91 @@ +#include "modinfodialogimages.h" +#include "ui_modinfodialog.h" + +ThumbnailButton::ThumbnailButton(const QString& fullPath, QImage original) + : m_original(std::move(original)) +{ + const auto ratio = static_cast(m_original.width()) / m_original.height(); + + QImage thumbnail; + if (ratio > 1.34) { + thumbnail = m_original.scaledToWidth(128); + } else { + thumbnail = m_original.scaledToHeight(96); + } + + setIcon(QPixmap::fromImage(thumbnail)); + setIconSize(QSize(thumbnail.width(), thumbnail.height())); + + connect(this, &QPushButton::clicked, [&]{ emit open(m_original); }); +} + +const QImage& ThumbnailButton::image() const +{ + return m_original; +} + + +ImagesTab::ImagesTab(Ui::ModInfoDialog* ui) + : ui(ui) +{ +} + +void ImagesTab::clear() +{ + ui->imageLabel->setPixmap({}); + + while (ui->imageThumbnails->count() > 0) { + auto* item = ui->imageThumbnails->takeAt(0); + delete item->widget(); + delete item; + } +} + +bool ImagesTab::feedFile(const QString& rootPath, const QString& fullPath) +{ + static constexpr const char* extensions[] = { + ".png", ".jpg" + }; + + for (const auto* e : extensions) { + if (fullPath.endsWith(e, Qt::CaseInsensitive)) { + add(fullPath); + return true; + } + } + + return false; +} + +void ImagesTab::add(const QString& fullPath) +{ + QImage image = QImage(fullPath); + + if (image.isNull()) { + qWarning() << "ImagesTab: '" << fullPath << "' is not a valid image"; + return; + } + + auto* button = new ThumbnailButton(fullPath, std::move(image)); + + QObject::connect( + button, &ThumbnailButton::open, + [&](const QImage& image){ onOpen(image); }); + + ui->imageThumbnails->addWidget(button); +} + +void ImagesTab::onOpen(const QImage& original) +{ + QImage image; + + const auto ratio = static_cast(original.width()) / original.height(); + + if (ratio > 1.34) { + image = original.scaledToWidth(ui->imageLabel->geometry().width()); + } else { + image = original.scaledToHeight(ui->imageLabel->geometry().height()); + } + + ui->imageLabel->setPixmap(QPixmap::fromImage(image)); +} diff --git a/src/modinfodialogimages.h b/src/modinfodialogimages.h new file mode 100644 index 00000000..3c492e77 --- /dev/null +++ b/src/modinfodialogimages.h @@ -0,0 +1,37 @@ +#ifndef MODINFODIALOGIMAGES_H +#define MODINFODIALOGIMAGES_H + +#include "modinfodialog.h" + +class ThumbnailButton : public QPushButton +{ + Q_OBJECT; + +public: + ThumbnailButton(const QString& fullPath, QImage image); + const QImage& image() const; + +signals: + void open(const QImage& image); + +private: + const QImage m_original; +}; + + +class ImagesTab : public ModInfoDialogTab +{ +public: + ImagesTab(Ui::ModInfoDialog* ui); + + void clear() override; + bool feedFile(const QString& rootPath, const QString& fullPath) override; + +private: + Ui::ModInfoDialog* ui; + + void add(const QString& fullPath); + void onOpen(const QImage& image); +}; + +#endif // MODINFODIALOGIMAGES_H diff --git a/src/texteditor.cpp b/src/texteditor.cpp index 78ce1610..99490b22 100644 --- a/src/texteditor.cpp +++ b/src/texteditor.cpp @@ -60,9 +60,19 @@ void TextEditor::setDefaultStyle() bool TextEditor::load(const QString& filename) { m_filename = filename; - setPlainText(MOBase::readFileText(filename, &m_encoding)); + clear(); + + const QString s = MOBase::readFileText(filename, &m_encoding); + + setPlainText(s); document()->setModified(false); + if (s.isEmpty()) { + // the modificationChanged even is not fired by the setModified() call + // above when the text being set is empty + onModified(false); + } + return true; } -- cgit v1.3.1 From f8a037a409d1b6bbb2f34b237b8b66d454179f56 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Tue, 18 Jun 2019 09:29:06 -0400 Subject: changed layout of esps tab, moved to modinfodialogesps.cpp/h slightly offset next/previous images so they look better when shown next to each other vertically --- src/CMakeLists.txt | 3 + src/modinfodialog.cpp | 98 +------------ src/modinfodialog.h | 2 - src/modinfodialog.ui | 268 ++++++++++++++++++++++-------------- src/modinfodialogesps.cpp | 287 +++++++++++++++++++++++++++++++++++++++ src/modinfodialogesps.h | 26 ++++ src/modinfodialogimages.cpp | 2 - src/resources/go-next_16.png | Bin 676 -> 719 bytes src/resources/go-previous_16.png | Bin 655 -> 718 bytes 9 files changed, 483 insertions(+), 203 deletions(-) create mode 100644 src/modinfodialogesps.cpp create mode 100644 src/modinfodialogesps.h (limited to 'src/modinfodialog.cpp') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c5018baf..aba922f9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -54,6 +54,7 @@ SET(organizer_SRCS modlist.cpp modidlineedit.cpp modinfodialog.cpp + modinfodialogesps.cpp modinfodialogimages.cpp modinfodialogtextfiles.cpp modinfo.cpp @@ -156,6 +157,7 @@ SET(organizer_HDRS modlist.h modidlineedit.h modinfodialog.h + modinfodialogesps.h modinfodialogimages.h modinfodialogtextfiles.h modinfo.h @@ -356,6 +358,7 @@ set(modinfo modinfo modinfobackup modinfodialog + modinfodialogesps modinfodialogimages modinfodialogtextfiles modinfoforeign diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index cc3140b6..a52f2031 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -40,6 +40,7 @@ along with Mod Organizer. If not, see . #include "modinfodialogtextfiles.h" #include "modinfodialogimages.h" +#include "modinfodialogesps.h" #include #include @@ -70,6 +71,7 @@ std::vector> ModInfoDialogTab::createTabs( 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; } @@ -353,15 +355,16 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo 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_CATEGORIES, false); ui->tabWidget->setTabEnabled(TAB_NEXUS, false); ui->tabWidget->setTabEnabled(TAB_FILETREE, false); ui->tabWidget->setTabEnabled(TAB_NOTES, false); - ui->tabWidget->setTabEnabled(TAB_ESPS, 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); initFiletree(modInfo); addCategories(CategoryFactory::instance(), modInfo->getCategories(), ui->categoriesTree->invisibleRootItem(), 0); @@ -848,10 +851,6 @@ void ModInfoDialog::refreshFiles() tab->clear(); } - ui->inactiveESPList->clear(); - ui->activeESPList->clear(); - - if (m_RootPath.length() > 0) { QDirIterator dirIterator(m_RootPath, QDir::Files, QDirIterator::Subdirectories); while (dirIterator.hasNext()) { @@ -862,24 +861,8 @@ void ModInfoDialog::refreshFiles() break; } } - - if (fileName.endsWith(".esp", Qt::CaseInsensitive) || - fileName.endsWith(".esm", Qt::CaseInsensitive) || - fileName.endsWith(".esl", Qt::CaseInsensitive)) { - QString relativePath = fileName.mid(m_RootPath.length() + 1); - if (relativePath.contains('/')) { - QFileInfo fileInfo(fileName); - QListWidgetItem *newItem = new QListWidgetItem(fileInfo.fileName()); - newItem->setData(Qt::UserRole, relativePath); - ui->inactiveESPList->addItem(newItem); - } else { - ui->activeESPList->addItem(relativePath); - } - } } } - - ui->tabWidget->setTabEnabled(TAB_ESPS, (ui->inactiveESPList->count() != 0) || (ui->activeESPList->count() != 0)); } void ModInfoDialog::addCategories(const CategoryFactory &factory, const std::set &enabledCategories, QTreeWidgetItem *root, int rootLevel) @@ -947,79 +930,6 @@ void ModInfoDialog::openTab(int tab) } } -void ModInfoDialog::on_activateESP_clicked() -{ - QListWidget *activeESPList = findChild("activeESPList"); - QListWidget *inactiveESPList = findChild("inactiveESPList"); - - int selectedRow = inactiveESPList->currentRow(); - if (selectedRow < 0) { - return; - } - - QListWidgetItem *selectedItem = inactiveESPList->takeItem(selectedRow); - - QDir root(m_RootPath); - bool renamed = false; - - while (root.exists(selectedItem->text())) { - bool okClicked = false; - QString newName = QInputDialog::getText(this, tr("File Exists"), tr("A file with that name exists, please enter a new one"), QLineEdit::Normal, selectedItem->text(), &okClicked); - if (!okClicked) { - inactiveESPList->insertItem(selectedRow, selectedItem); - return; - } else if (newName.size() > 0) { - selectedItem->setText(newName); - renamed = true; - } - } - - if (root.rename(selectedItem->data(Qt::UserRole).toString(), selectedItem->text())) { - activeESPList->addItem(selectedItem); - if (renamed) { - selectedItem->setData(Qt::UserRole, QVariant()); - } - } else { - inactiveESPList->insertItem(selectedRow, selectedItem); - reportError(tr("failed to move file")); - } -} - - -void ModInfoDialog::on_deactivateESP_clicked() -{ - QListWidget *activeESPList = findChild("activeESPList"); - QListWidget *inactiveESPList = findChild("inactiveESPList"); - - int selectedRow = activeESPList->currentRow(); - if (selectedRow < 0) { - return; - } - - QDir root(m_RootPath); - - QListWidgetItem *selectedItem = activeESPList->takeItem(selectedRow); - - // 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" - if (selectedItem->data(Qt::UserRole).isNull()) { - selectedItem->setData(Qt::UserRole, QString("optional/") + selectedItem->text()); - if (!root.exists("optional")) { - if (!root.mkdir("optional")) { - reportError(tr("failed to create directory \"optional\"")); - activeESPList->insertItem(selectedRow, selectedItem); - return; - } - } - } - - if (root.rename(selectedItem->text(), selectedItem->data(Qt::UserRole).toString())) { - inactiveESPList->addItem(selectedItem); - } else { - activeESPList->insertItem(selectedRow, selectedItem); - } -} - void ModInfoDialog::on_visitNexusLabel_linkActivated(const QString &link) { emit linkActivated(link); diff --git a/src/modinfodialog.h b/src/modinfodialog.h index f3ef6e76..6ecf16bf 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -186,8 +186,6 @@ private slots: void on_openInExplorerButton_clicked(); void on_closeButton_clicked(); - void on_activateESP_clicked(); - void on_deactivateESP_clicked(); void on_visitNexusLabel_linkActivated(const QString &link); void on_modIDEdit_editingFinished(); void on_sourceGameEdit_currentIndexChanged(int); diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui index c1dba1c7..f1212634 100644 --- a/src/modinfodialog.ui +++ b/src/modinfodialog.ui @@ -104,7 +104,7 @@ Qt::Horizontal - + 6 @@ -128,7 +128,7 @@ - + @@ -180,7 +180,7 @@ 0 0 - 604 + 741 436 @@ -213,117 +213,175 @@ Optional ESPs - - - - - List of esps, esms, and esls that can not be loaded by the game. - - - List of esps, esms, and esls contained in this plugin that currently can not be loaded by the game. They will not even appear in the esp-list in the main MO-window. -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. - - - - - - - Optional ESPs + + + + + Qt::Horizontal - - - - - - - - - 96 - 0 - - - - Make the selected mod in the lower list unavailable. - - - The selected esp (in the lower list) will be pushed into a subdirectory of the mod and will thus become "invisible" to the game. It can then no longer be activated. - - - - - - - :/MO/gui/resources/go-up.png:/MO/gui/resources/go-up.png + + + + 0 - - - 22 - 22 - + + 0 - - - - - - - 96 - 0 - + + 0 - - Move a file to the data directory. + + 0 - - This moves a esp to the esp directory so it can be enabled in the main window. Please note that the ESP merely becomes "available", it will not necessarily be loaded! That is configured in the main window of omo. + + + + Optional ESPs + + + + + + + List of esps, esms, and esls that can not be loaded by the game. + + + List of esps, esms, and esls contained in this plugin that currently can not be loaded by the game. They will not even appear in the esp-list in the main MO-window. +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. + + + + + + + + + 0 - - + + 0 - - - :/MO/gui/resources/go-down.png:/MO/gui/resources/go-down.png + + 0 - - - 22 - 22 - + + 0 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - ESPs in the data directory and thus visible to the game. - - - 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. - - - - - - - Available ESPs - + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Move a file to the data directory. + + + This moves a esp to the esp directory so it can be enabled in the main window. Please note that the ESP merely becomes "available", it will not necessarily be loaded! That is configured in the main window of omo. + + + + + + + :/MO/gui/next:/MO/gui/next + + + + 22 + 22 + + + + + + + + Make the selected mod in the lower list unavailable. + + + The selected esp (in the lower list) will be pushed into a subdirectory of the mod and will thus become "invisible" to the game. It can then no longer be activated. + + + + + + + :/MO/gui/previous:/MO/gui/previous + + + + 22 + 22 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Available ESPs + + + + + + + ESPs in the data directory and thus visible to the game. + + + 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. + + + + + + + + diff --git a/src/modinfodialogesps.cpp b/src/modinfodialogesps.cpp new file mode 100644 index 00000000..32dcdb35 --- /dev/null +++ b/src/modinfodialogesps.cpp @@ -0,0 +1,287 @@ +#include "modinfodialogesps.h" +#include "ui_modinfodialog.h" +#include + +using MOBase::reportError; + + +class ESP +{ +public: + ESP(QString rootPath, QString relativePath) + : m_rootPath(std::move(rootPath)), m_active(false) + { + if (relativePath.contains('/')) { + m_inactivePath = relativePath; + } else { + m_activePath = relativePath; + m_active = true; + } + } + + const QString& rootPath() const + { + return m_rootPath; + } + + const QString& relativePath() const + { + if (m_active) { + return m_activePath; + } else { + return m_inactivePath; + } + } + + const QString& activePath() const + { + return m_activePath; + } + + const QString& inactivePath() const + { + return m_inactivePath; + } + + QFileInfo fileInfo() const + { + return m_rootPath + QDir::separator() + relativePath(); + } + + bool isActive() const + { + return m_active; + } + + bool activate(const QString& newName) + { + QDir root(m_rootPath); + + if (root.rename(m_inactivePath, newName)) { + m_active = true; + m_activePath = newName; + + if (QFileInfo(m_inactivePath).fileName() != newName) { + // file was renamed + m_inactivePath = QFileInfo(m_inactivePath).path() + QDir::separator() + newName; + } + + return true; + } + + return false; + } + + bool deactivate(const QString& newName) + { + QDir root(m_rootPath); + + if (root.rename(m_activePath, newName)) { + m_active = false; + m_inactivePath = newName; + return true; + } + + return false; + } + +private: + QString m_rootPath; + QString m_activePath; + QString m_inactivePath; + bool m_active; +}; + + +class ESPItem : public QListWidgetItem +{ +public: + ESPItem(ESP esp) + : m_esp(std::move(esp)) + { + updateText(); + } + + const ESP& esp() const + { + return m_esp; + } + + void setESP(ESP esp) + { + m_esp = esp; + updateText(); + } + +private: + ESP m_esp; + + void updateText() + { + setText(m_esp.fileInfo().fileName()); + } +}; + + +ESPsTab::ESPsTab(Ui::ModInfoDialog* ui) + : ui(ui) +{ + QObject::connect( + ui->activateESP1, &QToolButton::clicked, [&]{ onActivate(); }); + + QObject::connect( + ui->deactivateESP1, &QToolButton::clicked, [&]{ onDeactivate(); }); +} + +void ESPsTab::clear() +{ + ui->inactiveESPList1->clear(); + ui->activeESPList1->clear(); +} + +bool ESPsTab::feedFile(const QString& rootPath, const QString& fullPath) +{ + static constexpr const char* extensions[] = { + ".esp", ".esm", ".esl" + }; + + for (const auto* e : extensions) { + if (fullPath.endsWith(e, Qt::CaseInsensitive)) { + QString relativePath = fullPath.mid(rootPath.length() + 1); + + auto* item = new ESPItem({rootPath, relativePath}); + + if (item->esp().isActive()) { + ui->activeESPList1->addItem(item); + } else { + ui->inactiveESPList1->addItem(item); + } + + return true; + } + } + + return false; +} + +void ESPsTab::onActivate() +{ + auto* item = selectedInactive(); + if (!item) { + qWarning("ESPsTab::onActivate(): no selection"); + return; + } + + ESP esp = item->esp(); + + if (esp.isActive()) { + qWarning("ESPsTab::onActive(): item is already active"); + return; + } + + QDir root(esp.rootPath()); + const QFileInfo file(esp.fileInfo()); + + QString newName = file.fileName(); + + while (root.exists(newName)) { + bool okClicked = false; + + newName = QInputDialog::getText( + ui->tabESPs, + QObject::tr("File Exists"), + QObject::tr("A file with that name exists, please enter a new one"), + QLineEdit::Normal, file.fileName(), &okClicked); + + if (!okClicked) { + return; + } + + if (newName.isEmpty()) { + newName = file.fileName(); + } + } + + if (esp.activate(newName)) { + ui->inactiveESPList1->takeItem(ui->inactiveESPList1->row(item)); + ui->activeESPList1->addItem(item); + item->setESP(esp); + } else { + reportError(QObject::tr("Failed to move file")); + } +} + +void ESPsTab::onDeactivate() +{ + auto* item = selectedActive(); + if (!item) { + qWarning("ESPsTab::onDeactivate(): no selection"); + return; + } + + ESP esp = item->esp(); + + if (!esp.isActive()) { + qWarning("ESPsTab::onDeactivate(): item is already inactive"); + return; + } + + 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(); + + if (newName.isEmpty()) { + if (!root.exists("optional")) { + if (!root.mkdir("optional")) { + reportError(QObject::tr("Failed to create directory \"optional\"")); + return; + } + } + + newName = QString("optional") + QDir::separator() + esp.fileInfo().fileName(); + } + + if (esp.deactivate(newName)) { + ui->activeESPList1->takeItem(ui->activeESPList1->row(item)); + ui->inactiveESPList1->addItem(item); + item->setESP(esp); + } else { + reportError(QObject::tr("Failed to move file")); + } +} + +ESPItem* ESPsTab::selectedInactive() +{ + auto* item = ui->inactiveESPList1->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() +{ + auto* item = ui->activeESPList1->currentItem(); + if (!item) { + return nullptr; + } + + auto* esp = dynamic_cast(item); + if (!esp) { + qCritical("ESPsTab: active item is not an ESPItem"); + return nullptr; + } + + return esp; +} diff --git a/src/modinfodialogesps.h b/src/modinfodialogesps.h new file mode 100644 index 00000000..a46677cb --- /dev/null +++ b/src/modinfodialogesps.h @@ -0,0 +1,26 @@ +#ifndef MODINFODIALOGESPS_H +#define MODINFODIALOGESPS_H + +#include "modinfodialog.h" + +class ESPItem; + +class ESPsTab : public ModInfoDialogTab +{ +public: + ESPsTab(Ui::ModInfoDialog* ui); + + void clear() override; + bool feedFile(const QString& rootPath, const QString& fullPath) override; + +private: + Ui::ModInfoDialog* ui; + + void onActivate(); + void onDeactivate(); + + ESPItem* selectedInactive(); + ESPItem* selectedActive(); +}; + +#endif // MODINFODIALOGESPS_H diff --git a/src/modinfodialogimages.cpp b/src/modinfodialogimages.cpp index fb2ad1fe..1659bed3 100644 --- a/src/modinfodialogimages.cpp +++ b/src/modinfodialogimages.cpp @@ -52,8 +52,6 @@ void ScalableImage::paintEvent(QPaintEvent* e) static_cast(std::round(m_original.height() * ratio))); if (m_scaled.isNull() || m_scaled.size() != scaledSize) { - qDebug() << "scaled to " << scaledSize; - m_scaled = m_original.scaled( scaledSize.width(), scaledSize.height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); diff --git a/src/resources/go-next_16.png b/src/resources/go-next_16.png index 6ef8de76..58742d39 100644 Binary files a/src/resources/go-next_16.png and b/src/resources/go-next_16.png differ diff --git a/src/resources/go-previous_16.png b/src/resources/go-previous_16.png index 659cd90d..b4b22d04 100644 Binary files a/src/resources/go-previous_16.png and b/src/resources/go-previous_16.png differ -- cgit v1.3.1 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/CMakeLists.txt | 3 + src/modinfodialog.cpp | 480 ++----------------------------------- src/modinfodialog.h | 45 ++-- src/modinfodialog.ui | 58 ++--- src/modinfodialogconflicts.cpp | 530 +++++++++++++++++++++++++++++++++++++++++ src/modinfodialogconflicts.h | 86 +++++++ 6 files changed, 689 insertions(+), 513 deletions(-) create mode 100644 src/modinfodialogconflicts.cpp create mode 100644 src/modinfodialogconflicts.h (limited to 'src/modinfodialog.cpp') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index aba922f9..eead6541 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -54,6 +54,7 @@ SET(organizer_SRCS modlist.cpp modidlineedit.cpp modinfodialog.cpp + modinfodialogconflicts.cpp modinfodialogesps.cpp modinfodialogimages.cpp modinfodialogtextfiles.cpp @@ -157,6 +158,7 @@ SET(organizer_HDRS modlist.h modidlineedit.h modinfodialog.h + modinfodialogconflicts.h modinfodialogesps.h modinfodialogimages.h modinfodialogtextfiles.h @@ -358,6 +360,7 @@ set(modinfo modinfo modinfobackup modinfodialog + modinfodialogconflicts modinfodialogesps modinfodialogimages modinfodialogtextfiles diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index a52f2031..c24176b3 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -41,6 +41,7 @@ along with Mod Organizer. If not, see . #include "modinfodialogtextfiles.h" #include "modinfodialogimages.h" #include "modinfodialogesps.h" +#include "modinfodialogconflicts.h" #include #include @@ -72,6 +73,7 @@ std::vector> ModInfoDialogTab::createTabs( 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; } @@ -81,6 +83,15 @@ bool ModInfoDialogTab::canClose() return true; } +void ModInfoDialogTab::saveState(Settings&) +{ + // no-op +} + +void ModInfoDialogTab::restoreState(const Settings& s) +{ + // no-op +} class ModFileListWidget : public QListWidgetItem { @@ -103,20 +114,6 @@ static bool operator<(const ModFileListWidget &LHS, const ModFileListWidget &RHS const int max_scan_for_context_menu = 50; -class ElideLeftDelegate : public QStyledItemDelegate -{ -public: - using QStyledItemDelegate::QStyledItemDelegate; - -protected: - void initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const - { - QStyledItemDelegate::initStyleOption(option, index); - option->textElideMode = Qt::ElideLeft; - } -}; - - bool canPreviewFile( PluginContainer* pluginContainer, bool isArchive, const QString& filename) { @@ -165,110 +162,6 @@ bool canUnhideFile(bool isArchive, const QString& filename) } -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); - } -}; - - 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), @@ -356,6 +249,7 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo 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_FILETREE, false); @@ -365,13 +259,13 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo ui->tabWidget->setTabEnabled(TAB_INIFILES, true); ui->tabWidget->setTabEnabled(TAB_IMAGES, true); ui->tabWidget->setTabEnabled(TAB_ESPS, true); + ui->tabWidget->setTabEnabled(TAB_CONFLICTS, true); initFiletree(modInfo); addCategories(CategoryFactory::instance(), modInfo->getCategories(), ui->categoriesTree->invisibleRootItem(), 0); refreshPrimaryCategoriesBox(); } - ui->tabWidget->setTabEnabled(TAB_CONFLICTS, m_Origin != nullptr); ui->endorseBtn->setVisible(Settings::instance().endorsementIntegration()); @@ -389,36 +283,6 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo if (ui->tabWidget->currentIndex() == TAB_NEXUS) { activateNexusTab(); } - - m_overwriteExpander.set(ui->overwriteExpander, ui->overwriteTree, true); - m_overwrittenExpander.set(ui->overwrittenExpander, ui->overwrittenTree, true); - m_nonconflictExpander.set(ui->noConflictExpander, ui->noConflictTree); - - - m_advancedConflictFilter.set(ui->conflictsAdvancedFilter); - m_advancedConflictFilter.changed = [&]{ refreshConflictLists(false, true); }; - - // left-elide the overwrites column so that the nearest are visible - ui->conflictsAdvancedList->setItemDelegateForColumn( - 0, new ElideLeftDelegate(ui->conflictsAdvancedList)); - - // left-elide the file column to see filenames - ui->conflictsAdvancedList->setItemDelegateForColumn( - 1, new ElideLeftDelegate(ui->conflictsAdvancedList)); - - // don't elide the overwritten by column so that the nearest are visible - - connect(ui->conflictsAdvancedShowNoConflict, &QCheckBox::clicked, [&] { - refreshConflictLists(false, true); - }); - - connect(ui->conflictsAdvancedShowAll, &QRadioButton::clicked, [&] { - refreshConflictLists(false, true); - }); - - connect(ui->conflictsAdvancedShowNearest, &QRadioButton::clicked, [&] { - refreshConflictLists(false, true); - }); } @@ -488,41 +352,19 @@ int ModInfoDialog::tabIndex(const QString &tabId) void ModInfoDialog::saveState(Settings& s) const { s.directInterface().setValue("mod_info_tabs", saveTabState()); - s.directInterface().setValue("mod_info_conflicts", saveConflictsState()); - s.directInterface().setValue( - "mod_info_conflicts_overwrite", - ui->overwriteTree->header()->saveState()); - - s.directInterface().setValue( - "mod_info_conflicts_noconflict", - ui->noConflictTree->header()->saveState()); - - s.directInterface().setValue( - "mod_info_conflicts_overwritten", - ui->overwrittenTree->header()->saveState()); - - s.directInterface().setValue( - "mod_info_advanced_conflicts", - ui->conflictsAdvancedList->header()->saveState()); + for (const auto& tab : m_tabs) { + tab->saveState(s); + } } void ModInfoDialog::restoreState(const Settings& s) { restoreTabState(s.directInterface().value("mod_info_tabs").toByteArray()); - restoreConflictsState(s.directInterface().value("mod_info_conflicts").toByteArray()); - - ui->overwriteTree->header()->restoreState( - s.directInterface().value("mod_info_conflicts_overwrite").toByteArray()); - - ui->noConflictTree->header()->restoreState( - s.directInterface().value("mod_info_conflicts_noconflict").toByteArray()); - ui->overwrittenTree->header()->restoreState( - s.directInterface().value("mod_info_conflicts_overwritten").toByteArray()); - - ui->conflictsAdvancedList->header()->restoreState( - s.directInterface().value("mod_info_advanced_conflicts").toByteArray()); + for (const auto& tab : m_tabs) { + tab->restoreState(s); + } } void ModInfoDialog::restoreTabState(const QByteArray &state) @@ -556,37 +398,6 @@ void ModInfoDialog::restoreTabState(const QByteArray &state) ui->tabWidget->blockSignals(false); } -void ModInfoDialog::restoreConflictsState(const QByteArray &state) -{ - QDataStream stream(state); - - bool overwriteExpanded = false; - bool overwrittenExpanded = false; - bool noConflictExpanded = false; - - stream >> overwriteExpanded >> overwrittenExpanded >> noConflictExpanded; - - if (stream.status() == QDataStream::Ok) { - m_overwriteExpander.toggle(overwriteExpanded); - m_overwrittenExpander.toggle(overwrittenExpanded); - m_nonconflictExpander.toggle(noConflictExpanded); - } - - int index = 0; - bool noConflictChecked = false; - bool showAllChecked = false; - bool showNearestChecked = false; - - stream >> index >> noConflictChecked >> showAllChecked >> showNearestChecked; - - if (stream.status() == QDataStream::Ok) { - ui->tabConflictsTabs->setCurrentIndex(index); - ui->conflictsAdvancedShowNoConflict->setChecked(noConflictChecked); - ui->conflictsAdvancedShowAll->setChecked(showAllChecked); - ui->conflictsAdvancedShowNearest->setChecked(showNearestChecked); - } -} - QByteArray ModInfoDialog::saveTabState() const { QByteArray result; @@ -599,251 +410,12 @@ QByteArray ModInfoDialog::saveTabState() const return result; } -QByteArray ModInfoDialog::saveConflictsState() const -{ - QByteArray result; - QDataStream stream(&result, QIODevice::WriteOnly); - - stream - << m_overwriteExpander.opened() - << m_overwrittenExpander.opened() - << m_nonconflictExpander.opened() - << ui->tabConflictsTabs->currentIndex() - << ui->conflictsAdvancedShowNoConflict->isChecked() - << ui->conflictsAdvancedShowAll->isChecked() - << ui->conflictsAdvancedShowNearest->isChecked(); - - return result; -} - void ModInfoDialog::refreshLists() { refreshConflictLists(true, true); refreshFiles(); } -void ModInfoDialog::refreshConflictLists( - bool refreshGeneral, bool refreshAdvanced) -{ - int numNonConflicting = 0; - int numOverwrite = 0; - int numOverwritten = 0; - - if (refreshGeneral) { - ui->overwriteTree->clear(); - ui->overwrittenTree->clear(); - ui->noConflictTree->clear(); - } - - if (refreshAdvanced) { - ui->conflictsAdvancedList->clear(); - } - - if (m_Origin != nullptr) { - std::vector files = m_Origin->getFiles(); - - for (const auto& file : m_Origin->getFiles()) { - const QString relativeName = QDir::fromNativeSeparators(ToQString(file->getRelativePath())); - const QString fileName = relativeName.mid(0).prepend(m_RootPath); - - bool archive = false; - const int fileOrigin = file->getOrigin(archive); - const auto& alternatives = file->getAlternatives(); - - if (refreshGeneral) { - if (fileOrigin == m_Origin->getID()) { - if (!alternatives.empty()) { - ui->overwriteTree->addTopLevelItem(createOverwriteItem( - file->getIndex(), archive, fileName, relativeName, alternatives)); - - ++numOverwrite; - } else { - // otherwise, put the file in the noconflict tree - ui->noConflictTree->addTopLevelItem(createNoConflictItem( - file->getIndex(), archive, fileName, relativeName)); - - ++numNonConflicting; - } - } else { - ui->overwrittenTree->addTopLevelItem(createOverwrittenItem( - file->getIndex(), fileOrigin, archive, fileName, relativeName)); - - ++numOverwritten; - } - } - - if (refreshAdvanced) { - auto* advancedItem = createAdvancedConflictItem( - file->getIndex(), fileOrigin, archive, - fileName, relativeName, alternatives); - - if (advancedItem) { - ui->conflictsAdvancedList->addTopLevelItem(advancedItem); - } - } - } - } - - if (refreshGeneral) { - ui->overwriteCount->display(numOverwrite); - ui->overwrittenCount->display(numOverwritten); - ui->noConflictCount->display(numNonConflicting); - } -} - -QTreeWidgetItem* ModInfoDialog::createOverwriteItem( - FileEntry::Index index, bool archive, - const QString& fileName, const QString& relativeName, - const FileEntry::AlternativesVector& alternatives) -{ - QString altString; - - for (const auto& alt : alternatives) { - if (!altString.isEmpty()) { - altString += ", "; - } - - altString += ToQString(m_Directory->getOriginByID(alt.first).getName()); - } - - QStringList fields(relativeName); - fields.append(altString); - - const auto origin = ToQString(m_Directory->getOriginByID(alternatives.back().first).getName()); - - return new ConflictItem(fields, index, fileName, true, origin, archive); -} - -QTreeWidgetItem* ModInfoDialog::createNoConflictItem( - FileEntry::Index index, bool archive, - const QString& fileName, const QString& relativeName) -{ - return new ConflictItem({relativeName}, index, fileName, false, "", archive); -} - -QTreeWidgetItem* ModInfoDialog::createOverwrittenItem( - FileEntry::Index index, int fileOrigin, bool archive, - const QString& fileName, const QString& relativeName) -{ - const FilesOrigin &realOrigin = m_Directory->getOriginByID(fileOrigin); - - QStringList fields(relativeName); - fields.append(ToQString(realOrigin.getName())); - - return new ConflictItem( - fields, index, fileName, true, ToQString(realOrigin.getName()), archive); -} - -QTreeWidgetItem* ModInfoDialog::createAdvancedConflictItem( - FileEntry::Index index,int fileOrigin, bool archive, - const QString& fileName, const QString& relativeName, - const MOShared::FileEntry::AlternativesVector& alternatives) -{ - QString before, after; - - if (!alternatives.empty()) { - int beforePrio = 0; - int afterPrio = std::numeric_limits::max(); - - for (const auto& alt : alternatives) - { - const auto altOrigin = m_Directory->getOriginByID(alt.first); - - if (ui->conflictsAdvancedShowAll->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->conflictsAdvancedShowAll->isChecked()) { - FilesOrigin &realOrigin = m_Directory->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->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) { - return nullptr; - } - } - - bool matched = m_advancedConflictFilter.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); -} - void ModInfoDialog::refreshFiles() { // clearing @@ -1475,7 +1047,6 @@ void ModInfoDialog::refreshPrimaryCategoriesBox() } } - void ModInfoDialog::on_primaryCategoryBox_currentIndexChanged(int index) { if (index != -1) { @@ -1483,19 +1054,6 @@ void ModInfoDialog::on_primaryCategoryBox_currentIndexChanged(int index) } } - -void ModInfoDialog::on_overwriteTree_itemDoubleClicked(QTreeWidgetItem *item, int) -{ - if (auto* ci=dynamic_cast(item)) { - const auto origin = ci->altOrigin(); - - if (!origin.isEmpty()) { - close(); - emit modOpen(origin, TAB_CONFLICTS); - } - } -} - FileRenamer::RenameResults ModInfoDialog::hideFile(FileRenamer& renamer, const QString &oldName) { const QString newName = oldName + ModInfo::s_HiddenExt; diff --git a/src/modinfodialog.h b/src/modinfodialog.h index 6ecf16bf..53b5d94a 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -70,9 +70,31 @@ public: 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); }; +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); +bool canHideFile(bool isArchive, const QString& filename); +bool canUnhideFile(bool isArchive, const QString& filename); + + /** * this is a larger dialog used to visualise information about the mod. * @todo this would probably a good place for a plugin-system @@ -262,36 +284,13 @@ private: std::map m_RealTabPos; - ExpanderWidget m_overwriteExpander, m_overwrittenExpander, m_nonconflictExpander; - FilterWidget m_advancedConflictFilter; void refreshConflictLists(bool refreshGeneral, bool refreshAdvanced); void refreshFiles(); - QTreeWidgetItem* createOverwriteItem( - FileEntry::Index index, bool archive, - const QString& fileName, const QString& relativeName, - const MOShared::FileEntry::AlternativesVector& alternatives); - - QTreeWidgetItem* createNoConflictItem( - FileEntry::Index index, bool archive, - const QString& fileName, const QString& relativeName); - - QTreeWidgetItem* createOverwrittenItem( - FileEntry::Index index, int fileOrigin, bool archive, - const QString& fileName, const QString& relativeName); - - QTreeWidgetItem* createAdvancedConflictItem( - FileEntry::Index index, int fileOrigin, bool archive, - const QString& fileName, const QString& relativeName, - const MOShared::FileEntry::AlternativesVector& alternatives); - void restoreTabState(const QByteArray &state); - void restoreConflictsState(const QByteArray &state); - QByteArray saveTabState() const; - QByteArray saveConflictsState() const; void changeFiletreeVisibility(bool visible); diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui index f1212634..a26d15ab 100644 --- a/src/modinfodialog.ui +++ b/src/modinfodialog.ui @@ -241,7 +241,7 @@ - + List of esps, esms, and esls that can not be loaded by the game. @@ -285,7 +285,7 @@ Most mods do not have optional esps, so chances are good you are looking at an e - + Move a file to the data directory. @@ -308,7 +308,7 @@ Most mods do not have optional esps, so chances are good you are looking at an e - + Make the selected mod in the lower list unavailable. @@ -368,7 +368,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. @@ -386,23 +386,23 @@ Most mods do not have optional esps, so chances are good you are looking at an e - + Conflicts - + - 0 + 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 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); +} diff --git a/src/modinfodialogconflicts.h b/src/modinfodialogconflicts.h new file mode 100644 index 00000000..894f9dd4 --- /dev/null +++ b/src/modinfodialogconflicts.h @@ -0,0 +1,86 @@ +#ifndef MODINFODIALOGCONFLICTS_H +#define MODINFODIALOGCONFLICTS_H + +#include "modinfodialog.h" +#include "expanderwidget.h" + +class GeneralConflictsTab +{ +public: + GeneralConflictsTab(Ui::ModInfoDialog* ui, OrganizerCore& oc); + + void saveState(Settings& s); + void restoreState(const Settings& s); + + void rebuild(ModInfo::Ptr mod, MOShared::FilesOrigin* origin); + void update(); + +private: + struct Expanders + { + ExpanderWidget overwrite, overwritten, nonconflict; + }; + + Ui::ModInfoDialog* ui; + OrganizerCore& m_core; + ModInfo::Ptr m_mod; + MOShared::FilesOrigin* m_origin; + Expanders m_expanders; + + QTreeWidgetItem* createOverwriteItem( + MOShared::FileEntry::Index index, bool archive, + const QString& fileName, const QString& relativeName, + const MOShared::FileEntry::AlternativesVector& alternatives); + + QTreeWidgetItem* createNoConflictItem( + MOShared::FileEntry::Index index, bool archive, + const QString& fileName, const QString& relativeName); + + QTreeWidgetItem* createOverwrittenItem( + MOShared::FileEntry::Index index, int fileOrigin, bool archive, + const QString& fileName, const QString& relativeName); + + void onOverwriteActivated(QTreeWidgetItem* item, int); +}; + + +class AdvancedConflictsTab +{ +public: + AdvancedConflictsTab(Ui::ModInfoDialog* ui, OrganizerCore& oc); + + void saveState(Settings& s); + void restoreState(const Settings& s); + + void rebuild(ModInfo::Ptr mod, MOShared::FilesOrigin* origin); + void update(); + +private: + Ui::ModInfoDialog* ui; + OrganizerCore& m_core; + ModInfo::Ptr m_mod; + MOShared::FilesOrigin* m_origin; + FilterWidget m_filter; + + QTreeWidgetItem* createItem( + MOShared::FileEntry::Index index, int fileOrigin, bool archive, + const QString& fileName, const QString& relativeName, + const MOShared::FileEntry::AlternativesVector& alternatives); +}; + + +class ConflictsTab : public ModInfoDialogTab +{ +public: + ConflictsTab(Ui::ModInfoDialog* ui, OrganizerCore& oc); + + void saveState(Settings& s) override; + void restoreState(const Settings& s) override; + +private: + Ui::ModInfoDialog* ui; + GeneralConflictsTab m_general; + AdvancedConflictsTab m_advanced; +}; + +#endif // MODINFODIALOGCONFLICTS_H -- 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/modinfodialog.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 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/modinfodialog.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/modinfodialog.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/modinfodialog.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 cbdc4cc3284f13477bfbf292d15c4a5742627091 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sun, 23 Jun 2019 03:03:41 -0400 Subject: split notes tab added new HTMLEditor that triggers an editingFinished() on focus our, used by notes tab --- src/modinfodialog.cpp | 14 +------------- src/modinfodialog.ui | 17 +++++++++++------ src/modinfodialognexus.cpp | 2 -- src/modinfodialogtab.cpp | 39 +++++++++++++++++++++++++++++++++++++++ src/modinfodialogtab.h | 16 ++++++++++++++++ src/texteditor.cpp | 10 ++++++++++ src/texteditor.h | 17 +++++++++++++++++ 7 files changed, 94 insertions(+), 21 deletions(-) (limited to 'src/modinfodialog.cpp') diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 30110d14..b78f4515 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -19,7 +19,6 @@ along with Mod Organizer. If not, see . #include "modinfodialog.h" #include "ui_modinfodialog.h" -#include "descriptionpage.h" #include "mainwindow.h" #include "modidlineedit.h" @@ -173,9 +172,6 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo m_RootPath = modInfo->absolutePath(); - ui->commentsEdit->setText(modInfo->comments()); - ui->notesEdit->setText(modInfo->notes()); - //TODO: No easy way to delegate links //ui->descriptionView->page()->acceptNavigationRequest(QWebEnginePage::DelegateAllLinks); @@ -238,14 +234,6 @@ 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()); - delete ui; } @@ -264,7 +252,7 @@ std::vector> ModInfoDialog::createTabs() { return createTabsImpl< TextFilesTab, IniFilesTab, ImagesTab, ESPsTab, - ConflictsTab, CategoriesTab, NexusTab>( + ConflictsTab, CategoriesTab, NexusTab, NotesTab>( *m_OrganizerCore, *m_PluginContainer, this, ui); } diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui index 29a7400f..360ecc79 100644 --- a/src/modinfodialog.ui +++ b/src/modinfodialog.ui @@ -6,7 +6,7 @@ 0 0 - 790 + 735 534 @@ -20,7 +20,7 @@ QTabWidget::Rounded - 0 + 7 true @@ -180,7 +180,7 @@ 0 0 - 741 + 686 436 @@ -859,7 +859,7 @@ text-align: left;
- + :/MO/gui/resources/internet-web-browser.png:/MO/gui/resources/internet-web-browser.png @@ -963,7 +963,7 @@ p, li { white-space: pre-wrap; }
- + <!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"> @@ -1071,7 +1071,7 @@ p, li { white-space: pre-wrap; } - + Enter notes about the mod here. These can be viewed in the mod list by hovering over the notes column or the flags column. @@ -1210,6 +1210,11 @@ p, li { white-space: pre-wrap; } QPlainTextEdit
texteditor.h
+ + HTMLEditor + QTextEdit +
texteditor.h
+
diff --git a/src/modinfodialognexus.cpp b/src/modinfodialognexus.cpp index 753d43de..55b55439 100644 --- a/src/modinfodialognexus.cpp +++ b/src/modinfodialognexus.cpp @@ -248,8 +248,6 @@ void NexusTab::refreshData(int modID) } else { onModChanged(); } - - //MessageDialog::showMessage(tr("Info requested, please wait"), this); } bool NexusTab::tryRefreshData(int modID) diff --git a/src/modinfodialogtab.cpp b/src/modinfodialogtab.cpp index ae0de5e8..b59f4dcc 100644 --- a/src/modinfodialogtab.cpp +++ b/src/modinfodialogtab.cpp @@ -1,4 +1,6 @@ #include "modinfodialogtab.h" +#include "ui_modinfodialog.h" +#include "texteditor.h" ModInfoDialogTab::ModInfoDialogTab( OrganizerCore& oc, PluginContainer& plugin, @@ -73,3 +75,40 @@ void ModInfoDialogTab::emitModOpen(QString name) { emit modOpen(name); } + + +NotesTab::NotesTab( + OrganizerCore& oc, PluginContainer& plugin, + QWidget* parent, Ui::ModInfoDialog* ui) + : ModInfoDialogTab(oc, plugin, parent, ui) +{ + connect(ui->commentsEdit, &QLineEdit::editingFinished, [&]{ onComments(); }); + connect(ui->notesEdit, &HTMLEditor::editingFinished, [&]{ onNotes(); }); +} + +void NotesTab::clear() +{ + ui->commentsEdit->clear(); + ui->notesEdit->clear(); +} + +void NotesTab::update() +{ + ui->commentsEdit->setText(mod()->comments()); + ui->notesEdit->setText(mod()->notes()); +} + +void NotesTab::onComments() +{ + mod()->setComments(ui->commentsEdit->text()); +} + +void NotesTab::onNotes() +{ + // Avoid saving html stub if notes field is empty. + if (ui->notesEdit->toPlainText().isEmpty()) { + mod()->setNotes({}); + } else { + mod()->setNotes(ui->notesEdit->toHtml()); + } +} diff --git a/src/modinfodialogtab.h b/src/modinfodialogtab.h index dd851b31..60371954 100644 --- a/src/modinfodialogtab.h +++ b/src/modinfodialogtab.h @@ -60,4 +60,20 @@ private: MOShared::FilesOrigin* m_origin; }; + +class NotesTab : public ModInfoDialogTab +{ +public: + NotesTab( + OrganizerCore& oc, PluginContainer& plugin, + QWidget* parent, Ui::ModInfoDialog* ui); + + void clear() override; + void update() override; + +private: + void onComments(); + void onNotes(); +}; + #endif // MODINFODIALOGTAB_H diff --git a/src/texteditor.cpp b/src/texteditor.cpp index 99490b22..6c1685da 100644 --- a/src/texteditor.cpp +++ b/src/texteditor.cpp @@ -458,3 +458,13 @@ void TextEditorToolbar::onWordWrap(bool b) { m_wordWrap->setChecked(b); } + + +void HTMLEditor::focusOutEvent(QFocusEvent* e) +{ + if (document() && document()->isModified()) { + emit editingFinished(); + } + + QTextEdit::focusInEvent(e); +} diff --git a/src/texteditor.h b/src/texteditor.h index eef5ca52..f3031731 100644 --- a/src/texteditor.h +++ b/src/texteditor.h @@ -137,4 +137,21 @@ private: void paintLineNumbers(QPaintEvent* e, const QColor& textColor); }; + +class HTMLEditor : public QTextEdit +{ + Q_OBJECT; + +public: + using QTextEdit::QTextEdit; + +signals: + void editingFinished(); + +protected: + void focusOutEvent(QFocusEvent* e); + +private: +}; + #endif // MO_TEXTEDITOR_H -- 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/modinfodialog.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/modinfodialog.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/modinfodialog.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/modinfodialog.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 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/modinfodialog.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 f14f2bad3ba7440d6f36657f32d00c89cae54623 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Mon, 24 Jun 2019 10:35:31 -0400 Subject: don't update invisible tabs update tabs when origin changes change setting name because 2.2.0 can't handle the text list --- src/modinfodialog.cpp | 57 ++++++++++++++++++++++++++++------------- src/modinfodialog.h | 7 ++--- src/modinfodialogcategories.cpp | 5 ++++ src/modinfodialogcategories.h | 1 + src/modinfodialognexus.cpp | 5 ++++ src/modinfodialognexus.h | 1 + src/modinfodialogtab.cpp | 10 ++++++++ src/modinfodialogtab.h | 3 +++ 8 files changed, 68 insertions(+), 21 deletions(-) (limited to 'src/modinfodialog.cpp') diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index c03739ca..4c169cc4 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -100,6 +100,12 @@ ModInfoDialog::TabInfo::TabInfo(std::unique_ptr tab) { } +bool ModInfoDialog::TabInfo::isVisible() const +{ + return (realPos != -1); +} + + ModInfoDialog::ModInfoDialog( MainWindow* mw, OrganizerCore* core, PluginContainer* plugin) : TutorableDialog("ModInfoDialog", mw), @@ -126,7 +132,9 @@ ModInfoDialog::ModInfoDialog( connect( tabInfo.tab.get(), &ModInfoDialogTab::originModified, - [&](int originID){ emit originModified(originID); }); + [this, i](int originID) { + onOriginModified(static_cast(i), originID); + }); connect( tabInfo.tab.get(), &ModInfoDialogTab::modOpen, @@ -207,9 +215,8 @@ void ModInfoDialog::update(bool firstTime) { setWindowTitle(m_mod->name()); setTabsVisibility(firstTime); + updateTabs(); - feedFiles(); - setTabsColors(); if (m_initialTab >= 0) { switchToTab(m_initialTab); @@ -261,18 +268,29 @@ void ModInfoDialog::setTabsVisibility(bool firstTime) reAddTabs(visibility, sel); } -void ModInfoDialog::updateTabs() +void ModInfoDialog::updateTabs(bool becauseOriginChanged) { auto* origin = getOrigin(); for (auto& tabInfo : m_tabs) { + if (!tabInfo.isVisible()) { + continue; + } + + if (becauseOriginChanged && !tabInfo.tab->usesOriginFiles()) { + continue; + } + tabInfo.tab->setMod(m_mod, origin); tabInfo.tab->clear(); tabInfo.tab->update(); } + + feedFiles(becauseOriginChanged); + setTabsColors(); } -void ModInfoDialog::feedFiles() +void ModInfoDialog::feedFiles(bool becauseOriginChanged) { const auto rootPath = m_mod->absolutePath(); @@ -282,6 +300,14 @@ void ModInfoDialog::feedFiles() QString fileName = dirIterator.next(); for (auto& tabInfo : m_tabs) { + if (!tabInfo.isVisible()) { + continue; + } + + if (becauseOriginChanged && !tabInfo.tab->usesOriginFiles()) { + continue; + } + if (tabInfo.tab->feedFile(rootPath, fileName)) { break; } @@ -332,7 +358,7 @@ void ModInfoDialog::saveState(Settings& s) const { const auto tabState = saveTabState(); if (!tabState.isEmpty()) { - s.directInterface().setValue("mod_info_tabs", tabState); + s.directInterface().setValue("mod_info_tab_order", tabState); } for (const auto& tabInfo : m_tabs) { @@ -366,14 +392,13 @@ QString ModInfoDialog::saveTabState() const std::vector ModInfoDialog::getOrderedTabNames() const { - const auto value = Settings::instance() - .directInterface().value("mod_info_tabs"); + const auto& settings = Settings::instance().directInterface(); std::vector v; - if (value.type() == QVariant::ByteArray) { + if (settings.contains("mod_info_tabs")) { // old byte array - QDataStream stream(value.toByteArray()); + QDataStream stream(settings.value("mod_info_tabs").toByteArray()); int count = 0; stream >> count; @@ -385,7 +410,7 @@ std::vector ModInfoDialog::getOrderedTabNames() const } } else { // string list - QString string = value.toString(); + QString string = settings.value("mod_info_tab_order").toString(); QTextStream stream(&string); while (!stream.atEnd()) { @@ -458,14 +483,10 @@ void ModInfoDialog::reAddTabs(const std::vector& visibility, ETabs sel) } } -int ModInfoDialog::tabIndex(const QString& tabId) +void ModInfoDialog::onOriginModified(std::size_t tabIndex, int originID) { - for (int i = 0; i < ui->tabWidget->count(); ++i) { - if (ui->tabWidget->widget(i)->objectName() == tabId) { - return i; - } - } - return -1; + emit originModified(originID); + updateTabs(true); } void ModInfoDialog::onDeleteShortcut() diff --git a/src/modinfodialog.h b/src/modinfodialog.h index 54e056b8..31ea5536 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -116,6 +116,7 @@ private: QIcon icon; TabInfo(std::unique_ptr tab); + bool isVisible() const; }; std::unique_ptr ui; @@ -131,15 +132,15 @@ private: QString saveTabState() const; void update(bool firstTime=false); void onDeleteShortcut(); - int tabIndex(const QString &tabId); MOShared::FilesOrigin* getOrigin(); void setTabsVisibility(bool firstTime); - void updateTabs(); - void feedFiles(); + void updateTabs(bool becauseOriginChanged=false); + void feedFiles(bool becauseOriginChanged); void setTabsColors(); void switchToTab(ETabs id); void reAddTabs(const std::vector& visibility, ETabs sel); std::vector getOrderedTabNames() const; + void onOriginModified(std::size_t tabIndex, int originID); template std::unique_ptr createTab(int index) diff --git a/src/modinfodialogcategories.cpp b/src/modinfodialogcategories.cpp index 4bd10028..0d739d1f 100644 --- a/src/modinfodialogcategories.cpp +++ b/src/modinfodialogcategories.cpp @@ -42,6 +42,11 @@ bool CategoriesTab::canHandleSeparators() const return true; } +bool CategoriesTab::usesOriginFiles() const +{ + return false; +} + 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 738b4e4d..392023e7 100644 --- a/src/modinfodialogcategories.h +++ b/src/modinfodialogcategories.h @@ -12,6 +12,7 @@ public: void clear() override; void update() override; bool canHandleSeparators() const override; + bool usesOriginFiles() const override; private: void add( diff --git a/src/modinfodialognexus.cpp b/src/modinfodialognexus.cpp index 172968ab..d296e000 100644 --- a/src/modinfodialognexus.cpp +++ b/src/modinfodialognexus.cpp @@ -102,6 +102,11 @@ void NexusTab::setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin) mod.data(), &ModInfo::modDetailsUpdated, [&]{ onModChanged(); }); } +bool NexusTab::usesOriginFiles() const +{ + return false; +} + void NexusTab::updateVersionColor() { if (mod()->getVersion() != mod()->getNewestVersion()) { diff --git a/src/modinfodialognexus.h b/src/modinfodialognexus.h index ce1ef426..a09f4316 100644 --- a/src/modinfodialognexus.h +++ b/src/modinfodialognexus.h @@ -42,6 +42,7 @@ public: void clear() override; void update() override; void setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin) override; + bool usesOriginFiles() const override; private: QMetaObject::Connection m_modConnection; diff --git a/src/modinfodialogtab.cpp b/src/modinfodialogtab.cpp index 2f5fbdb8..009fb804 100644 --- a/src/modinfodialogtab.cpp +++ b/src/modinfodialogtab.cpp @@ -53,6 +53,11 @@ bool ModInfoDialogTab::canHandleUnmanaged() const return false; } +bool ModInfoDialogTab::usesOriginFiles() const +{ + return true; +} + void ModInfoDialogTab::setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin) { m_mod = mod; @@ -158,3 +163,8 @@ void NotesTab::onNotes() mod()->setNotes(ui->notesEdit->toHtml()); } } + +bool NotesTab::usesOriginFiles() const +{ + return false; +} diff --git a/src/modinfodialogtab.h b/src/modinfodialogtab.h index fae5bc41..c85d2ded 100644 --- a/src/modinfodialogtab.h +++ b/src/modinfodialogtab.h @@ -27,10 +27,12 @@ public: virtual bool canClose(); virtual void saveState(Settings& s); virtual void restoreState(const Settings& s); + virtual bool deleteRequested(); virtual bool canHandleSeparators() const; virtual bool canHandleUnmanaged() const; + virtual bool usesOriginFiles() const; virtual void setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin); @@ -81,6 +83,7 @@ public: void clear() override; void update() override; bool canHandleSeparators() const override; + bool usesOriginFiles() const override; private: void onComments(); -- 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/modinfodialog.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 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/modinfodialog.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 00d4921e47e5eef08b10dac127795ecc8ccf84ae Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Tue, 25 Jun 2019 15:36:46 -0400 Subject: only load nexus website on activation fixed refresh not working, will now always clear the browser so the refresh is obvious --- src/modinfodialog.cpp | 34 +++++++++++++++++++++++++++++++++- src/modinfodialog.h | 3 ++- src/modinfodialognexus.cpp | 31 ++++++++++++++++--------------- src/modinfodialognexus.h | 1 + src/modinfodialogtab.cpp | 20 +++++++++++++++++++- src/modinfodialogtab.h | 5 +++++ 6 files changed, 76 insertions(+), 18 deletions(-) (limited to 'src/modinfodialog.cpp') diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index c6cdb961..f11fa6f3 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -157,6 +157,8 @@ ModInfoDialog::ModInfoDialog( update(); }); } + + connect(ui->tabWidget, &QTabWidget::currentChanged, [&]{ onTabChanged(); }); } ModInfoDialog::~ModInfoDialog() = default; @@ -196,6 +198,10 @@ int ModInfoDialog::exec() void ModInfoDialog::setMod(ModInfo::Ptr mod) { m_mod = mod; + + for (auto& tabInfo : m_tabs) { + tabInfo.tab->resetFirstActivation(); + } } void ModInfoDialog::setMod(const QString& name) @@ -225,8 +231,25 @@ void ModInfoDialog::setTab(ETabs id) switchToTab(id); } +ModInfoDialog::TabInfo* ModInfoDialog::currentTab() +{ + const auto index = ui->tabWidget->currentIndex(); + if (index < 0) { + return nullptr; + } + + const auto i = static_cast(index); + if (i >= m_tabs.size()) { + return nullptr; + } + + return &m_tabs[i]; +} + void ModInfoDialog::update(bool firstTime) { + const int oldTab = ui->tabWidget->currentIndex(); + setWindowTitle(m_mod->name()); setTabsVisibility(firstTime); @@ -236,6 +259,12 @@ void ModInfoDialog::update(bool firstTime) switchToTab(m_initialTab); m_initialTab = ETabs(-1); } + + if (ui->tabWidget->currentIndex() == oldTab) { + if (auto* tabInfo=currentTab()) { + tabInfo->tab->activated(); + } + } } void ModInfoDialog::setTabsVisibility(bool firstTime) @@ -533,8 +562,11 @@ void ModInfoDialog::on_closeButton_clicked() close(); } -void ModInfoDialog::on_tabWidget_currentChanged(int index) +void ModInfoDialog::onTabChanged() { + if (auto* tabInfo=currentTab()) { + tabInfo->tab->activated(); + } } void ModInfoDialog::on_nextButton_clicked() diff --git a/src/modinfodialog.h b/src/modinfodialog.h index 8ddaf86c..36363c34 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -118,7 +118,6 @@ signals: private slots: void on_closeButton_clicked(); - void on_tabWidget_currentChanged(int index); void on_nextButton_clicked(); void on_prevButton_clicked(); @@ -144,6 +143,7 @@ private: ETabs m_initialTab; std::vector createTabs(); + TabInfo* currentTab(); void restoreTabState(const QString& state); QString saveTabState() const; void update(bool firstTime=false); @@ -157,6 +157,7 @@ private: void reAddTabs(const std::vector& visibility, ETabs sel); std::vector getOrderedTabNames() const; void onOriginModified(std::size_t tabIndex, int originID); + void onTabChanged(); template std::unique_ptr createTab(int index) diff --git a/src/modinfodialognexus.cpp b/src/modinfodialognexus.cpp index 797f7923..adf79060 100644 --- a/src/modinfodialognexus.cpp +++ b/src/modinfodialognexus.cpp @@ -26,7 +26,7 @@ NexusTab::NexusTab( 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->refresh, &QToolButton::clicked, [&]{ onRefreshBrowser(); }); connect( ui->sourceGame, @@ -96,10 +96,14 @@ void NexusTab::update() (mod()->endorsedState() == ModInfo::ENDORSED_FALSE) || (mod()->endorsedState() == ModInfo::ENDORSED_NEVER)); - updateWebpage(); setHasData(mod()->getNexusID() >= 0); } +void NexusTab::firstActivation() +{ + updateWebpage(); +} + void NexusTab::setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin) { cleanup(); @@ -265,9 +269,11 @@ void NexusTab::onRefreshBrowser() const auto modID = mod()->getNexusID(); if (isValidModID(modID)) { - refreshData(modID); - } else + mod()->setLastNexusQuery(QDateTime::fromSecsSinceEpoch(0)); + updateWebpage(); + } else { qInfo("Mod has no valid Nexus ID, info can't be updated."); + } } void NexusTab::onEndorse() @@ -286,17 +292,12 @@ void NexusTab::refreshData(int modID) bool NexusTab::tryRefreshData(int modID) { - if (!isValidModID(modID)) { - return false; - } - - if (m_requestStarted) { - return false; - } - - if (!mod()->updateNXMInfo()) { - return false; + if (isValidModID(modID) && !m_requestStarted) { + if (mod()->updateNXMInfo()) { + ui->browser->setHtml(""); + return true; + } } - return true; + return false; } diff --git a/src/modinfodialognexus.h b/src/modinfodialognexus.h index 86d87c30..92330704 100644 --- a/src/modinfodialognexus.h +++ b/src/modinfodialognexus.h @@ -41,6 +41,7 @@ public: void clear() override; void update() override; + void firstActivation() override; void setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin) override; bool usesOriginFiles() const override; diff --git a/src/modinfodialogtab.cpp b/src/modinfodialogtab.cpp index 009fb804..d99e8727 100644 --- a/src/modinfodialogtab.cpp +++ b/src/modinfodialogtab.cpp @@ -7,10 +7,23 @@ 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_origin(nullptr), m_tabID(id), m_hasData(false), m_firstActivation(true) { } +void ModInfoDialogTab::activated() +{ + if (m_firstActivation) { + m_firstActivation = false; + firstActivation(); + } +} + +void ModInfoDialogTab::resetFirstActivation() +{ + m_firstActivation = true; +} + void ModInfoDialogTab::update() { // no-op @@ -22,6 +35,11 @@ bool ModInfoDialogTab::feedFile(const QString&, const QString&) return false; } +void ModInfoDialogTab::firstActivation() +{ + // no-op +} + bool ModInfoDialogTab::canClose() { return true; diff --git a/src/modinfodialogtab.h b/src/modinfodialogtab.h index c85d2ded..41d913f8 100644 --- a/src/modinfodialogtab.h +++ b/src/modinfodialogtab.h @@ -21,9 +21,13 @@ public: ModInfoDialogTab& operator=(ModInfoDialogTab&&) = default; virtual ~ModInfoDialogTab() = default; + void activated(); + void resetFirstActivation(); + virtual void clear() = 0; virtual void update(); virtual bool feedFile(const QString& rootPath, const QString& filename); + virtual void firstActivation(); virtual bool canClose(); virtual void saveState(Settings& s); virtual void restoreState(const Settings& s); @@ -70,6 +74,7 @@ private: MOShared::FilesOrigin* m_origin; int m_tabID; bool m_hasData; + bool m_firstActivation; }; -- cgit v1.3.1 From fa20f81b46c3f8c89fcbbdff6e5da0225c707358 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Tue, 25 Jun 2019 17:44:41 -0400 Subject: replaced reportError() calls by qCritical() because they can come from a thread, which hangs the ui categories and notes will update the tab color when changing fixed rare crash when reordering tabs while loading a mod --- src/modinfodialog.cpp | 18 ++++++++++++++++-- src/modinfodialog.h | 1 + src/modinfodialog.ui | 7 ++----- src/modinfodialogcategories.cpp | 3 ++- src/modinfodialogesps.cpp | 3 ++- src/modinfodialogtab.cpp | 36 ++++++++++++++++++++++++------------ src/modinfodialogtab.h | 2 ++ src/modinfodialogtextfiles.cpp | 2 +- src/modinforegular.cpp | 8 ++++++-- 9 files changed, 56 insertions(+), 24 deletions(-) (limited to 'src/modinfodialog.cpp') diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index f11fa6f3..7696023f 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -124,7 +124,8 @@ 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(ETabs(-1)) + m_core(core), m_plugin(plugin), m_initialTab(ETabs(-1)), + m_arrangingTabs(false) { ui->setupUi(this); @@ -156,6 +157,10 @@ ModInfoDialog::ModInfoDialog( setMod(name); update(); }); + + connect( + tabInfo.tab.get(), &ModInfoDialogTab::hasDataChanged, + [&]{ setTabsColors(); }); } connect(ui->tabWidget, &QTabWidget::currentChanged, [&]{ onTabChanged(); }); @@ -269,6 +274,8 @@ void ModInfoDialog::update(bool firstTime) void ModInfoDialog::setTabsVisibility(bool firstTime) { + QScopedValueRollback arrangingTabs(m_arrangingTabs, true); + std::vector visibility(m_tabs.size()); bool changed = false; @@ -374,7 +381,7 @@ 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); + m_mainWindow->palette().color(QPalette::Disabled, QPalette::WindowText); ui->tabWidget->tabBar()->setTabTextColor(tabInfo.realPos, c); } @@ -564,6 +571,13 @@ void ModInfoDialog::on_closeButton_clicked() void ModInfoDialog::onTabChanged() { + if (m_arrangingTabs) { + // this can be fired while re-arranging tabs, which happens before mods + // are given to tabs, and might trigger first activation, which breaks all + // sorts of things + return; + } + if (auto* tabInfo=currentTab()) { tabInfo->tab->activated(); } diff --git a/src/modinfodialog.h b/src/modinfodialog.h index 36363c34..6dc66003 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -141,6 +141,7 @@ private: PluginContainer* m_plugin; std::vector m_tabs; ETabs m_initialTab; + bool m_arrangingTabs; std::vector createTabs(); TabInfo* currentTab(); diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui index fd367e74..30ea9217 100644 --- a/src/modinfodialog.ui +++ b/src/modinfodialog.ui @@ -810,9 +810,6 @@ text-align: left; - - true - false @@ -1038,7 +1035,7 @@ p, li { white-space: pre-wrap; } - + Enter comments about the mod here. These are displayed in the notes column of the mod list. @@ -1051,7 +1048,7 @@ p, li { white-space: pre-wrap; } - + Enter notes about the mod here. These can be viewed in the mod list by hovering over the notes column or the flags column. diff --git a/src/modinfodialogcategories.cpp b/src/modinfodialogcategories.cpp index 0d739d1f..8ffded59 100644 --- a/src/modinfodialogcategories.cpp +++ b/src/modinfodialogcategories.cpp @@ -34,7 +34,6 @@ void CategoriesTab::update() ui->categories->invisibleRootItem(), 0); updatePrimary(); - setHasData(ui->primaryCategories->count() > 0); } bool CategoriesTab::canHandleSeparators() const @@ -92,6 +91,8 @@ void CategoriesTab::updatePrimary() break; } } + + setHasData(ui->primaryCategories->count() > 0); } void CategoriesTab::addChecked(QTreeWidgetItem* tree) diff --git a/src/modinfodialogesps.cpp b/src/modinfodialogesps.cpp index ecb341e8..7961096d 100644 --- a/src/modinfodialogesps.cpp +++ b/src/modinfodialogesps.cpp @@ -260,7 +260,6 @@ bool ESPsTab::feedFile(const QString& rootPath, const QString& fullPath) m_inactiveModel->add(std::move(esp)); } - setHasData(true); return true; } } @@ -272,6 +271,8 @@ void ESPsTab::update() { m_inactiveModel->finished(); m_activeModel->finished(); + + setHasData(m_inactiveModel->rowCount() > 0 || m_activeModel->rowCount() > 0); } void ESPsTab::onActivate() diff --git a/src/modinfodialogtab.cpp b/src/modinfodialogtab.cpp index d99e8727..f5eeeed1 100644 --- a/src/modinfodialogtab.cpp +++ b/src/modinfodialogtab.cpp @@ -131,7 +131,10 @@ void ModInfoDialogTab::emitModOpen(QString name) void ModInfoDialogTab::setHasData(bool b) { - m_hasData = b; + if (m_hasData != b) { + m_hasData = b; + emit hasDataChanged(); + } } @@ -140,14 +143,14 @@ NotesTab::NotesTab( 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(); }); + connect(ui->comments, &QLineEdit::editingFinished, [&]{ onComments(); }); + connect(ui->notes, &HTMLEditor::editingFinished, [&]{ onNotes(); }); } void NotesTab::clear() { - ui->commentsEdit->clear(); - ui->notesEdit->clear(); + ui->comments->clear(); + ui->notes->clear(); setHasData(false); } @@ -156,10 +159,9 @@ void NotesTab::update() const auto comments = mod()->comments(); const auto notes = mod()->notes(); - ui->commentsEdit->setText(comments); - ui->notesEdit->setText(notes); - - setHasData(!comments.isEmpty() || !notes.isEmpty()); + ui->comments->setText(comments); + ui->notes->setText(notes); + checkHasData(); } bool NotesTab::canHandleSeparators() const @@ -169,20 +171,30 @@ bool NotesTab::canHandleSeparators() const void NotesTab::onComments() { - mod()->setComments(ui->commentsEdit->text()); + mod()->setComments(ui->comments->text()); + checkHasData(); } void NotesTab::onNotes() { // Avoid saving html stub if notes field is empty. - if (ui->notesEdit->toPlainText().isEmpty()) { + if (ui->notes->toPlainText().isEmpty()) { mod()->setNotes({}); } else { - mod()->setNotes(ui->notesEdit->toHtml()); + mod()->setNotes(ui->notes->toHtml()); } + + checkHasData(); } bool NotesTab::usesOriginFiles() const { return false; } + +void NotesTab::checkHasData() +{ + setHasData( + !ui->comments->text().isEmpty() || + !ui->notes->toPlainText().isEmpty()); +} diff --git a/src/modinfodialogtab.h b/src/modinfodialogtab.h index 41d913f8..058035ab 100644 --- a/src/modinfodialogtab.h +++ b/src/modinfodialogtab.h @@ -49,6 +49,7 @@ public: signals: void originModified(int originID); void modOpen(QString name); + void hasDataChanged(); protected: Ui::ModInfoDialog* ui; @@ -93,6 +94,7 @@ public: private: void onComments(); void onNotes(); + void checkHasData(); }; #endif // MODINFODIALOGTAB_H diff --git a/src/modinfodialogtextfiles.cpp b/src/modinfodialogtextfiles.cpp index 2b5ab489..e3d00049 100644 --- a/src/modinfodialogtextfiles.cpp +++ b/src/modinfodialogtextfiles.cpp @@ -158,7 +158,6 @@ bool GenericFilesTab::feedFile(const QString& rootPath, const QString& fullPath) for (const auto* e : extensions) { if (wantsFile(rootPath, fullPath)) { m_model->add(rootPath, fullPath); - setHasData(true); return true; } } @@ -169,6 +168,7 @@ bool GenericFilesTab::feedFile(const QString& rootPath, const QString& fullPath) void GenericFilesTab::update() { m_model->finished(); + setHasData(m_model->rowCount() > 0); } void GenericFilesTab::onSelection( diff --git a/src/modinforegular.cpp b/src/modinforegular.cpp index babbd665..4333e351 100644 --- a/src/modinforegular.cpp +++ b/src/modinforegular.cpp @@ -194,10 +194,14 @@ void ModInfoRegular::saveMeta() if (metaFile.status() == QSettings::NoError) { m_MetaInfoChanged = false; } else { - reportError(tr("failed to write %1/meta.ini: error %2").arg(absolutePath()).arg(metaFile.status())); + qCritical() + << QString("failed to write %1/meta.ini: error %2") + .arg(absolutePath()).arg(metaFile.status()); } } else { - reportError(tr("failed to write %1/meta.ini: error %2").arg(absolutePath()).arg(metaFile.status())); + qCritical() + << QString("failed to write %1/meta.ini: error %2") + .arg(absolutePath()).arg(metaFile.status()); } } } -- 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/modinfodialog.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 e1990df0a2f25b4ac9227655fd5d597cf8ab6cf4 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Thu, 27 Jun 2019 11:36:40 -0400 Subject: fixed tab re-ordering not being used if the old setting was present --- src/modinfodialog.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/modinfodialog.cpp') diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 2772994c..ca20b318 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -427,6 +427,11 @@ void ModInfoDialog::saveState(Settings& s) const s.directInterface().setValue("mod_info_tab_order", tabState); } + // remove old settings + if (s.directInterface().contains("mod_info_tabs")) { + s.directInterface().remove("mod_info_tabs"); + } + for (const auto& tabInfo : m_tabs) { tabInfo.tab->saveState(s); } -- cgit v1.3.1 From 6547197e5f685439371446b499427848ed687871 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Thu, 27 Jun 2019 12:01:38 -0400 Subject: remember state of various widgets remove obsolete settings --- src/modinfodialog.cpp | 12 ++++++++---- src/modinfodialogesps.cpp | 11 +++++++++++ src/modinfodialogesps.h | 2 ++ src/modinfodialogimages.cpp | 4 ++++ src/modinfodialogtab.h | 23 +++++++++++++++++++++++ src/modinfodialogtextfiles.cpp | 19 +++++++++++++++---- src/modinfodialogtextfiles.h | 3 +++ 7 files changed, 66 insertions(+), 8 deletions(-) (limited to 'src/modinfodialog.cpp') diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index ca20b318..bcb979d2 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -427,10 +427,14 @@ void ModInfoDialog::saveState(Settings& s) const s.directInterface().setValue("mod_info_tab_order", tabState); } - // remove old settings - if (s.directInterface().contains("mod_info_tabs")) { - s.directInterface().remove("mod_info_tabs"); - } + // remove 2.2.0 settings + s.directInterface().remove("mod_info_tabs"); + s.directInterface().remove("mod_info_conflict_expanders"); + s.directInterface().remove("mod_info_conflicts"); + s.directInterface().remove("mod_info_advanced_conflicts"); + s.directInterface().remove("mod_info_conflicts_overwrite"); + s.directInterface().remove("mod_info_conflicts_noconflict"); + s.directInterface().remove("mod_info_conflicts_overwritten"); for (const auto& tabInfo : m_tabs) { tabInfo.tab->saveState(s); diff --git a/src/modinfodialogesps.cpp b/src/modinfodialogesps.cpp index 7961096d..d00eccf3 100644 --- a/src/modinfodialogesps.cpp +++ b/src/modinfodialogesps.cpp @@ -1,6 +1,7 @@ #include "modinfodialogesps.h" #include "ui_modinfodialog.h" #include "modinfodialog.h" +#include "settings.h" #include using MOBase::reportError; @@ -275,6 +276,16 @@ void ESPsTab::update() setHasData(m_inactiveModel->rowCount() > 0 || m_activeModel->rowCount() > 0); } +void ESPsTab::saveState(Settings& s) +{ + saveWidgetState(s.directInterface(), ui->ESPsSplitter); +} + +void ESPsTab::restoreState(const Settings& s) +{ + restoreWidgetState(s.directInterface(), ui->ESPsSplitter); +} + void ESPsTab::onActivate() { const auto index = ui->inactiveESPList->currentIndex(); diff --git a/src/modinfodialogesps.h b/src/modinfodialogesps.h index c972074a..217863c6 100644 --- a/src/modinfodialogesps.h +++ b/src/modinfodialogesps.h @@ -18,6 +18,8 @@ public: void clear() override; bool feedFile(const QString& rootPath, const QString& fullPath) override; void update(); + void saveState(Settings& s) override; + void restoreState(const Settings& s) override; private: ESPListModel* m_inactiveModel; diff --git a/src/modinfodialogimages.cpp b/src/modinfodialogimages.cpp index 02fc7c20..307fa8e8 100644 --- a/src/modinfodialogimages.cpp +++ b/src/modinfodialogimages.cpp @@ -133,12 +133,16 @@ void ImagesTab::saveState(Settings& s) { s.directInterface().setValue( "mod_info_dialog_images_show_dds", m_ddsEnabled); + + saveWidgetState(s.directInterface(), ui->tabImagesSplitter); } void ImagesTab::restoreState(const Settings& s) { ui->imagesShowDDS->setChecked(s.directInterface() .value("mod_info_dialog_images_show_dds", false).toBool()); + + restoreWidgetState(s.directInterface(), ui->tabImagesSplitter); } void ImagesTab::checkFiltering() diff --git a/src/modinfodialogtab.h b/src/modinfodialogtab.h index 058035ab..ce29c868 100644 --- a/src/modinfodialogtab.h +++ b/src/modinfodialogtab.h @@ -67,6 +67,23 @@ protected: void emitModOpen(QString name); void setHasData(bool b); + // this needs to be a template because saveState() and restoreState() are + // not in QWidget, but they're in various widgets + // + template + void saveWidgetState(QSettings& s, Widget* w) + { + s.setValue(settingName(w), w->saveState()); + } + + template + void restoreWidgetState(const QSettings& s, Widget* w) + { + if (s.contains(settingName(w))) { + w->restoreState(s.value(settingName(w)).toByteArray()); + } + } + private: OrganizerCore& m_core; PluginContainer& m_plugin; @@ -76,6 +93,12 @@ private: int m_tabID; bool m_hasData; bool m_firstActivation; + + + QString settingName(QWidget* w) + { + return "geometry/modinfodialog_" + w->objectName(); + } }; diff --git a/src/modinfodialogtextfiles.cpp b/src/modinfodialogtextfiles.cpp index 5b035c53..52891bf5 100644 --- a/src/modinfodialogtextfiles.cpp +++ b/src/modinfodialogtextfiles.cpp @@ -1,6 +1,7 @@ #include "modinfodialogtextfiles.h" #include "ui_modinfodialog.h" #include "modinfodialog.h" +#include "settings.h" #include class FileListModel : public QAbstractItemModel @@ -102,14 +103,14 @@ GenericFilesTab::GenericFilesTab( QWidget* parent, Ui::ModInfoDialog* ui, int id, QListView* list, QSplitter* sp, TextEditor* e, QLineEdit* filter) : ModInfoDialogTab(oc, plugin, parent, ui, id), - m_list(list), m_editor(e), m_model(new FileListModel) + m_list(list), m_editor(e), m_splitter(sp), m_model(new FileListModel) { m_list->setModel(m_model); m_editor->setupToolbar(); - sp->setSizes({200, 1}); - sp->setStretchFactor(0, 0); - sp->setStretchFactor(1, 1); + m_splitter->setSizes({200, 1}); + m_splitter->setStretchFactor(0, 0); + m_splitter->setStretchFactor(1, 1); m_filter.setEdit(filter); m_filter.setList(m_list); @@ -171,6 +172,16 @@ void GenericFilesTab::update() setHasData(m_model->rowCount() > 0); } +void GenericFilesTab::saveState(Settings& s) +{ + saveWidgetState(s.directInterface(), m_splitter); +} + +void GenericFilesTab::restoreState(const Settings& s) +{ + restoreWidgetState(s.directInterface(), m_splitter); +} + void GenericFilesTab::onSelection( const QModelIndex& current, const QModelIndex& previous) { diff --git a/src/modinfodialogtextfiles.h b/src/modinfodialogtextfiles.h index 037c6bb3..ffe49904 100644 --- a/src/modinfodialogtextfiles.h +++ b/src/modinfodialogtextfiles.h @@ -19,10 +19,13 @@ public: bool canClose() override; bool feedFile(const QString& rootPath, const QString& fullPath) override; void update() override; + void saveState(Settings& s) override; + void restoreState(const Settings& s) override; protected: QListView* m_list; TextEditor* m_editor; + QSplitter* m_splitter; FileListModel* m_model; FilterWidget m_filter; -- cgit v1.3.1 From 9ec0feba4395d492d814c03cf8be67a196bab36e Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Thu, 27 Jun 2019 12:57:21 -0400 Subject: fixed crash when opening separators --- src/modinfodialog.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/modinfodialog.cpp') diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index bcb979d2..676f3480 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -345,7 +345,9 @@ void ModInfoDialog::updateTabs(bool becauseOriginChanged) feedFiles(becauseOriginChanged); for (auto& tabInfo : m_tabs) { - tabInfo.tab->update(); + if (tabInfo.isVisible()) { + tabInfo.tab->update(); + } } setTabsColors(); -- cgit v1.3.1 From 093eb5650bdd010d9c779e64d3ca63682ba6ca19 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Thu, 27 Jun 2019 15:17:05 -0400 Subject: fixed problems with re-ordering tabs, realPos was never updated properly and it wasn't actually used everywhere --- src/modinfodialog.cpp | 40 ++++++++++++++++++++++++++++++++++++---- src/modinfodialog.h | 2 ++ 2 files changed, 38 insertions(+), 4 deletions(-) (limited to 'src/modinfodialog.cpp') diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 676f3480..a5e8d8cd 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -170,6 +170,7 @@ ModInfoDialog::ModInfoDialog( } connect(ui->tabWidget, &QTabWidget::currentChanged, [&]{ onTabChanged(); }); + connect(ui->tabWidget->tabBar(), &QTabBar::tabMoved, [&]{ onTabMoved(); }); } ModInfoDialog::~ModInfoDialog() = default; @@ -249,12 +250,14 @@ ModInfoDialog::TabInfo* ModInfoDialog::currentTab() return nullptr; } - const auto i = static_cast(index); - if (i >= m_tabs.size()) { - return nullptr; + for (auto& tabInfo : m_tabs) { + if (tabInfo.realPos == index) { + return &tabInfo; + } } - return &m_tabs[i]; + qCritical() << "tab index " << index << " not found"; + return nullptr; } void ModInfoDialog::update(bool firstTime) @@ -272,6 +275,7 @@ void ModInfoDialog::update(bool firstTime) } if (ui->tabWidget->currentIndex() == oldTab) { + // manually fire activated() because the tab index hasn't been changed if (auto* tabInfo=currentTab()) { tabInfo->tab->activated(); } @@ -600,6 +604,34 @@ void ModInfoDialog::onTabChanged() } } +void ModInfoDialog::onTabMoved() +{ + // reset + for (auto& tabInfo : m_tabs) { + tabInfo.realPos = -1; + } + + // for each tab in the widget + for (int i=0; itabWidget->count(); ++i) { + const auto* w = ui->tabWidget->widget(i); + + bool found = false; + + // find the corresponding tab info + for (auto& tabInfo : m_tabs) { + if (tabInfo.widget == w) { + tabInfo.realPos = i; + found = true; + break; + } + } + + if (!found) { + qCritical() << "unknown tab at index " << i; + } + } +} + void ModInfoDialog::on_nextButton_clicked() { auto mod = m_mainWindow->nextModInList(); diff --git a/src/modinfodialog.h b/src/modinfodialog.h index 273af8c7..030b3f93 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -158,8 +158,10 @@ private: void switchToTab(ETabs id); void reAddTabs(const std::vector& visibility, ETabs sel); std::vector getOrderedTabNames() const; + void onOriginModified(std::size_t tabIndex, int originID); void onTabChanged(); + void onTabMoved(); template std::unique_ptr createTab(int index) -- cgit v1.3.1 From ee71f40ce9e4edf985ee13437afc6be94c45e925 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Mon, 1 Jul 2019 03:59:24 -0400 Subject: convert fs::path to utf-16 instead of ansi --- src/modinfodialog.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'src/modinfodialog.cpp') diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index a5e8d8cd..b3055b5a 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -360,16 +360,18 @@ void ModInfoDialog::updateTabs(bool becauseOriginChanged) void ModInfoDialog::feedFiles(bool becauseOriginChanged) { namespace fs = std::filesystem; + const auto rootPath = m_mod->absolutePath(); if (rootPath.length() > 0) { - - for (const auto& entry : fs::recursive_directory_iterator(rootPath.toStdString())) { + fs::path path(rootPath.toStdWString()); + for (const auto& entry : fs::recursive_directory_iterator(path)) { if (!entry.is_regular_file()) { continue; } - const auto fileName = QString::fromWCharArray(entry.path().c_str()); + const auto fileName = QString::fromWCharArray( + entry.path().native().c_str()); for (auto& tabInfo : m_tabs) { if (!tabInfo.isVisible()) { -- cgit v1.3.1 From c6e80f1a5eed4a57663e7fc55c2727258eaad552 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Tue, 2 Jul 2019 11:05:31 -0400 Subject: focus tabs when they need a confirmation to close dialog did not handle closeEvent() at all, it only checked the close button bunch of comments --- src/modinfodialog.cpp | 34 +++++++++++++++-- src/modinfodialog.h | 6 ++- src/modinfodialogtab.cpp | 5 +++ src/modinfodialogtab.h | 87 ++++++++++++++++++++++++++++++++++++++++-- src/modinfodialogtextfiles.cpp | 2 + 5 files changed, 126 insertions(+), 8 deletions(-) (limited to 'src/modinfodialog.cpp') diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index b3055b5a..4af479c4 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -154,7 +154,7 @@ ModInfoDialog::ModInfoDialog( connect( tabInfo.tab.get(), &ModInfoDialogTab::originModified, [this, i](int originID) { - onOriginModified(static_cast(i), originID); + onOriginModified(originID); }); connect( @@ -167,6 +167,15 @@ ModInfoDialog::ModInfoDialog( connect( tabInfo.tab.get(), &ModInfoDialogTab::hasDataChanged, [&]{ setTabsColors(); }); + + connect( + tabInfo.tab.get(), &ModInfoDialogTab::wantsFocus, + [&, i=static_cast(i)] + { + if (i < m_tabs.size()) { + switchToTab(ETabs(m_tabs[i].tab->tabID())); + } + }); } connect(ui->tabWidget, &QTabWidget::currentChanged, [&]{ onTabChanged(); }); @@ -546,6 +555,7 @@ void ModInfoDialog::reAddTabs(const std::vector& visibility, ETabs sel) } + // removing all tabs ui->tabWidget->clear(); // reset real positions @@ -566,7 +576,7 @@ void ModInfoDialog::reAddTabs(const std::vector& visibility, ETabs sel) } } -void ModInfoDialog::onOriginModified(std::size_t tabIndex, int originID) +void ModInfoDialog::onOriginModified(int originID) { emit originModified(originID); updateTabs(true); @@ -581,15 +591,31 @@ void ModInfoDialog::onDeleteShortcut() } } +void ModInfoDialog::closeEvent(QCloseEvent* e) +{ + if (tryClose()) { + e->accept(); + } else { + e->ignore(); + } +} + void ModInfoDialog::on_closeButton_clicked() +{ + if (tryClose()) { + close(); + } +} + +bool ModInfoDialog::tryClose() { for (auto& tabInfo : m_tabs) { if (!tabInfo.tab->canClose()) { - return; + return false; } } - close(); + return true; } void ModInfoDialog::onTabChanged() diff --git a/src/modinfodialog.h b/src/modinfodialog.h index 030b3f93..899a3eab 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -117,6 +117,9 @@ public: signals: void originModified(int originID); +protected: + void closeEvent(QCloseEvent* e); + private slots: void on_closeButton_clicked(); void on_nextButton_clicked(); @@ -158,8 +161,9 @@ private: void switchToTab(ETabs id); void reAddTabs(const std::vector& visibility, ETabs sel); std::vector getOrderedTabNames() const; + bool tryClose(); - void onOriginModified(std::size_t tabIndex, int originID); + void onOriginModified(int originID); void onTabChanged(); void onTabMoved(); diff --git a/src/modinfodialogtab.cpp b/src/modinfodialogtab.cpp index f5eeeed1..0468b405 100644 --- a/src/modinfodialogtab.cpp +++ b/src/modinfodialogtab.cpp @@ -137,6 +137,11 @@ void ModInfoDialogTab::setHasData(bool b) } } +void ModInfoDialogTab::setFocus() +{ + emit wantsFocus(); +} + NotesTab::NotesTab( OrganizerCore& oc, PluginContainer& plugin, diff --git a/src/modinfodialogtab.h b/src/modinfodialogtab.h index ce29c868..3f98314a 100644 --- a/src/modinfodialogtab.h +++ b/src/modinfodialogtab.h @@ -10,6 +10,36 @@ namespace Ui { class ModInfoDialog; } class Settings; class OrganizerCore; +// base class for all tabs in the mod info dialog +// +// when the dialog is opened or when next/previous is clicked, the sequence is: +// setMod(), clear(), feedFile() an update() +// +// when the dialog is closed, canClose() is called on all tabs +// +// when a tab is selected for the first time for the current mod, +// firstActivation() is called; this is used by NexusTab to refresh stuff +// +// when the dialog is first shown, restoreState() is called on all tabs and +// saveState() is called when the dialog is closed +// +// there isn't a good framework for keyboard shortcuts because only the delete +// key is used for now, which calls deletedRequested() on all tabs until one +// returns true +// +// each tab override canHandleSeparators() and canHandleUnmanaged() to return +// true if they can handle separators or unmanaged mods; if these return false +// (which they do by default), the tabs will be removed from the widget entirely +// +// when tabs modify the origin and call emitOriginModified() (such as the +// conflicts tabs), all tabs that return true in usesOriginFiles() will go +// through the full update sequence as above +// +// 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 +// class ModInfoDialogTab : public QObject { Q_OBJECT; @@ -21,13 +51,63 @@ public: ModInfoDialogTab& operator=(ModInfoDialogTab&&) = default; virtual ~ModInfoDialogTab() = default; + // called by ModInfoDialog every time this tab is selected; this will call + // firstActivation() the first time it's called, until resetFirstActivation() + // is called + // void activated(); + + // called by ModInfoDialog when the selected mod has changed, to make sure + // activated() will call firstActivation() next time + // void resetFirstActivation(); + + // called when the selected mod changed, `mod` can never be empty, but + // `origin` can (if the mod is not active, for example) + // + // derived classes can override this to connect to events on the mod for + // examples (see NexusTab), but must call the base class implementation + // + virtual void setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin); + + // this tab should clear its user interface; clear() will always be called + // before feedFile() and update() + // virtual void clear() = 0; - virtual void update(); + + // the dialog will go through each file in the mod and call feedFile() + // with it on all tabs; if a tab handles the file, it should return true to + // prevent other tabs from displaying it + // + // this prevents individual tabs from having to go through the filesystem + // independently, which would kill performance, but it cannot be the only way + // to update tabs because some of them don't actually use files (like + // NotesTab) or they use the internal structures such as `FilesOrigin` (like + // ConflictsTab) + // + // once all the files have been fed to tabs, update() will be called; tabs + // that need to do additional work after being fed files, or tabs that are + // unable to use feedFile() at all can use update() to do work + // virtual bool feedFile(const QString& rootPath, const QString& filename); + + // called after all the files on the filesystem have been sent through + // feedFile() + // + // this can be used to do a final processing of the files handled by + // feedFile() (ImagesTab will set up the scrollbars, for example) or something + // completely different (CategoriesTab will fill up the lists) + // + virtual void update(); + + // called when a tab is visible for the first time for the current mod, can + // be used to do expensive work that's not worth doing until the tab is + // actually selected by the user + // virtual void firstActivation(); + + // virtual bool canClose(); virtual void saveState(Settings& s); virtual void restoreState(const Settings& s); @@ -38,8 +118,6 @@ public: virtual bool canHandleUnmanaged() const; virtual bool usesOriginFiles() const; - virtual void setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin); - ModInfo::Ptr mod() const; MOShared::FilesOrigin* origin() const; @@ -50,6 +128,7 @@ signals: void originModified(int originID); void modOpen(QString name); void hasDataChanged(); + void wantsFocus(); protected: Ui::ModInfoDialog* ui; @@ -67,6 +146,8 @@ protected: void emitModOpen(QString name); void setHasData(bool b); + void setFocus(); + // this needs to be a template because saveState() and restoreState() are // not in QWidget, but they're in various widgets // diff --git a/src/modinfodialogtextfiles.cpp b/src/modinfodialogtextfiles.cpp index 52891bf5..bc44ee3e 100644 --- a/src/modinfodialogtextfiles.cpp +++ b/src/modinfodialogtextfiles.cpp @@ -133,6 +133,8 @@ bool GenericFilesTab::canClose() return true; } + setFocus(); + const int res = QMessageBox::question( parentWidget(), QObject::tr("Save changes?"), -- 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/modinfodialog.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 From 4aa59cdc7dd779c7e864a1c4e96c6b52c61879ff Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Tue, 2 Jul 2019 12:35:33 -0400 Subject: added modinfodialogfwd.h, mostly for the enum that's used in various places renamed ETabs to ModInfoTabIDs and changed all ints to use the enum instead added ModInfoPtr to avoid having to include modinfo.h just to get ModInfo::Ptr --- src/CMakeLists.txt | 2 ++ src/iuserinterface.h | 5 ++-- src/mainwindow.cpp | 37 +++++++++++++++------------- src/mainwindow.h | 7 +++--- src/modinfodialog.cpp | 60 ++++++++++++++++++++-------------------------- src/modinfodialog.h | 50 ++++++-------------------------------- src/modinfodialogfwd.h | 50 ++++++++++++++++++++++++++++++++++++++ src/modinfodialognexus.cpp | 2 +- src/modinfodialognexus.h | 2 +- src/modinfodialogtab.cpp | 7 +++--- src/modinfodialogtab.h | 20 ++++++++-------- src/organizercore.cpp | 8 +++---- 12 files changed, 132 insertions(+), 118 deletions(-) create mode 100644 src/modinfodialogfwd.h (limited to 'src/modinfodialog.cpp') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1d27f444..929ee296 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -166,6 +166,7 @@ SET(organizer_HDRS modinfodialogconflicts.h modinfodialogesps.h modinfodialogfiletree.h + modinfodialogfwd.h modinfodialogimages.h modinfodialognexus.h modinfodialogtab.h @@ -379,6 +380,7 @@ set(modinfo\\dialog modinfodialogconflicts modinfodialogesps modinfodialogfiletree + modinfodialogfwd modinfodialogimages modinfodialognexus modinfodialogtab diff --git a/src/iuserinterface.h b/src/iuserinterface.h index 034fa029..bba8de2b 100644 --- a/src/iuserinterface.h +++ b/src/iuserinterface.h @@ -2,7 +2,7 @@ #define IUSERINTERFACE_H -#include "modinfo.h" +#include "modinfodialogfwd.h" #include "ilockedwaitingforprocess.h" #include #include @@ -29,7 +29,8 @@ public: virtual bool closeWindow() = 0; virtual void setWindowEnabled(bool enabled) = 0; - virtual void displayModInformation(ModInfo::Ptr modInfo, unsigned int index, int tab) = 0; + virtual void displayModInformation( + ModInfoPtr modInfo, unsigned int modIndex, ModInfoTabIDs tabID) = 0; virtual void updateBSAList(const QStringList &defaultArchives, const QStringList &activeArchives) = 0; diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 7ab555fa..a596c542 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3192,7 +3192,8 @@ void MainWindow::overwriteClosed(int) } -void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index, int tab) +void MainWindow::displayModInformation( + ModInfo::Ptr modInfo, unsigned int modIndex, ModInfoTabIDs tabID) { if (!m_OrganizerCore.modList()->modInfoAboutToChange(modInfo)) { qDebug("A different mod information dialog is open. If this is incorrect, please restart MO"); @@ -3227,8 +3228,8 @@ void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index, 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.setTab(ModInfoDialog::ETabs(tab)); + if (tabID != ModInfoTabIDs::None) { + dialog.setTab(tabID); } dialog.restoreState(m_OrganizerCore.settings()); @@ -3247,7 +3248,7 @@ void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index, m_OrganizerCore.modList()->modInfoChanged(modInfo); } - if (m_OrganizerCore.currentProfile()->modEnabled(index) + if (m_OrganizerCore.currentProfile()->modEnabled(modIndex) && !modInfo->hasFlag(ModInfo::FLAG_FOREIGN)) { FilesOrigin& origin = m_OrganizerCore.directoryStructure()->getOriginByName(ToWString(modInfo->name())); origin.enable(false); @@ -3258,7 +3259,7 @@ void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index, m_OrganizerCore.directoryRefresher()->addModToStructure(m_OrganizerCore.directoryStructure() , modInfo->name() - , m_OrganizerCore.currentProfile()->getModPriority(index) + , m_OrganizerCore.currentProfile()->getModPriority(modIndex) , modInfo->absolutePath() , modInfo->stealFiles() , modInfo->archives()); @@ -3347,7 +3348,7 @@ ModInfo::Ptr MainWindow::previousModInList() return {}; } -void MainWindow::displayModInformation(const QString &modName, int tab) +void MainWindow::displayModInformation(const QString &modName, ModInfoTabIDs tabID) { unsigned int index = ModInfo::getIndex(modName); if (index == UINT_MAX) { @@ -3356,14 +3357,14 @@ void MainWindow::displayModInformation(const QString &modName, int tab) } ModInfo::Ptr modInfo = ModInfo::getByIndex(index); - displayModInformation(modInfo, index, tab); + displayModInformation(modInfo, index, tabID); } -void MainWindow::displayModInformation(int row, int tab) +void MainWindow::displayModInformation(int row, ModInfoTabIDs tabID) { ModInfo::Ptr modInfo = ModInfo::getByIndex(row); - displayModInformation(modInfo, row, tab); + displayModInformation(modInfo, row, tabID); } @@ -4048,16 +4049,18 @@ void MainWindow::on_modList_doubleClicked(const QModelIndex &index) try { m_ContextRow = m_ModListSortProxy->mapToSource(index).row(); sourceIdx.column(); - int tab = -1; + + auto tab = ModInfoTabIDs::None; + switch (sourceIdx.column()) { - case ModList::COL_NOTES: tab = ModInfoDialog::TAB_NOTES; break; - case ModList::COL_VERSION: tab = ModInfoDialog::TAB_NEXUS; break; - case ModList::COL_MODID: tab = ModInfoDialog::TAB_NEXUS; break; - case ModList::COL_GAME: tab = ModInfoDialog::TAB_NEXUS; break; - case ModList::COL_CATEGORY: tab = ModInfoDialog::TAB_CATEGORIES; break; - case ModList::COL_FLAGS: tab = ModInfoDialog::TAB_CONFLICTS; break; - default: tab = -1; + case ModList::COL_NOTES: tab = ModInfoTabIDs::Notes; break; + case ModList::COL_VERSION: tab = ModInfoTabIDs::Nexus; break; + case ModList::COL_MODID: tab = ModInfoTabIDs::Nexus; break; + case ModList::COL_GAME: tab = ModInfoTabIDs::Nexus; break; + case ModList::COL_CATEGORY: tab = ModInfoTabIDs::Categories; break; + case ModList::COL_FLAGS: tab = ModInfoTabIDs::Conflicts; break; } + displayModInformation(sourceIdx.row(), tab); // workaround to cancel the editor that might have opened because of // selection-click diff --git a/src/mainwindow.h b/src/mainwindow.h index f204211e..00f15a2b 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -151,7 +151,8 @@ public: virtual void disconnectPlugins(); - void displayModInformation(ModInfo::Ptr modInfo, unsigned int index, int tab); + void displayModInformation( + ModInfo::Ptr modInfo, unsigned int modIndex, ModInfoTabIDs tabID) override; bool confirmExit(); @@ -235,7 +236,7 @@ private: QList findFileInfos(const QString &path, const std::function &filter) const; bool modifyExecutablesDialog(); - void displayModInformation(int row, int tab = -1); + void displayModInformation(int row, ModInfoTabIDs tab=ModInfoTabIDs::None); void testExtractBSA(int modIndex); void writeDataToFile(QFile &file, const QString &directory, const MOShared::DirectoryEntry &directoryEntry); @@ -551,7 +552,7 @@ private slots: void editCategories(); void deselectFilters(); - void displayModInformation(const QString &modName, int tab); + void displayModInformation(const QString &modName, ModInfoTabIDs tabID); void modRenamed(const QString &oldName, const QString &newName); void modRemoved(const QString &fileName); diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 4ef010e4..c8ffa35b 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -131,7 +131,7 @@ ModInfoDialog::ModInfoDialog( ModInfo::Ptr mod) : TutorableDialog("ModInfoDialog", mw), ui(new Ui::ModInfoDialog), m_mainWindow(mw), - m_core(core), m_plugin(plugin), m_initialTab(ETabs(-1)), + m_core(core), m_plugin(plugin), m_initialTab(ModInfoTabIDs::None), m_arrangingTabs(false) { ui->setupUi(this); @@ -155,16 +155,11 @@ ModInfoDialog::ModInfoDialog( connect( tabInfo.tab.get(), &ModInfoDialogTab::originModified, - [this, i](int originID) { - onOriginModified(originID); - }); + [this](int originID){ onOriginModified(originID); }); connect( tabInfo.tab.get(), &ModInfoDialogTab::modOpen, - [&](const QString& name){ - setMod(name); - update(); - }); + [&](const QString& name){ setMod(name); update(); }); connect( tabInfo.tab.get(), &ModInfoDialogTab::hasDataChanged, @@ -172,12 +167,7 @@ ModInfoDialog::ModInfoDialog( connect( tabInfo.tab.get(), &ModInfoDialogTab::wantsFocus, - [&, i=static_cast(i)] - { - if (i < m_tabs.size()) { - switchToTab(ETabs(m_tabs[i].tab->tabID())); - } - }); + [&, id=tabInfo.tab->tabID()]{ switchToTab(id); }); } connect(ui->tabWidget, &QTabWidget::currentChanged, [&]{ onTabChanged(); }); @@ -187,32 +177,32 @@ ModInfoDialog::ModInfoDialog( ModInfoDialog::~ModInfoDialog() = default; template -std::unique_ptr createTab(ModInfoDialog& d, int index) +std::unique_ptr createTab(ModInfoDialog& d, ModInfoTabIDs id) { return std::make_unique(ModInfoDialogTabContext( - *d.m_core, *d.m_plugin, &d, d.ui.get(), index, d.m_mod, d.getOrigin())); + *d.m_core, *d.m_plugin, &d, d.ui.get(), id, d.m_mod, d.getOrigin())); } std::vector ModInfoDialog::createTabs() { std::vector v; - 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)); + v.push_back(createTab(*this, ModInfoTabIDs::TextFiles)); + v.push_back(createTab(*this, ModInfoTabIDs::IniFiles)); + v.push_back(createTab(*this, ModInfoTabIDs::Images)); + v.push_back(createTab(*this, ModInfoTabIDs::Esps)); + v.push_back(createTab(*this, ModInfoTabIDs::Conflicts)); + v.push_back(createTab(*this, ModInfoTabIDs::Categories)); + v.push_back(createTab(*this, ModInfoTabIDs::Nexus)); + v.push_back(createTab(*this, ModInfoTabIDs::Notes)); + v.push_back(createTab(*this, ModInfoTabIDs::Filetree)); return v; } int ModInfoDialog::exec() { - const auto selectFirst = (m_initialTab == -1); + const auto selectFirst = (m_initialTab == ModInfoTabIDs::None); update(true); @@ -252,7 +242,7 @@ void ModInfoDialog::setMod(const QString& name) setMod(mod); } -void ModInfoDialog::setTab(ETabs id) +void ModInfoDialog::setTab(ModInfoTabIDs id) { if (!isVisible()) { m_initialTab = id; @@ -288,9 +278,9 @@ void ModInfoDialog::update(bool firstTime) updateTabs(); - if (m_initialTab >= 0) { + if (m_initialTab != ModInfoTabIDs::None) { switchToTab(m_initialTab); - m_initialTab = ETabs(-1); + m_initialTab = ModInfoTabIDs::None; } if (ui->tabWidget->currentIndex() == oldTab) { @@ -335,11 +325,11 @@ void ModInfoDialog::setTabsVisibility(bool firstTime) // remember selection const int selIndex = ui->tabWidget->currentIndex(); - ETabs sel = ETabs(-1); + auto sel = ModInfoTabIDs::None; for (const auto& tabInfo : m_tabs) { if (tabInfo.realPos == selIndex) { - sel = ETabs(tabInfo.tab->tabID()); + sel = tabInfo.tab->tabID(); break; } } @@ -420,7 +410,7 @@ void ModInfoDialog::setTabsColors() } } -void ModInfoDialog::switchToTab(ETabs id) +void ModInfoDialog::switchToTab(ModInfoTabIDs id) { for (const auto& tabInfo : m_tabs) { if (tabInfo.tab->tabID() == id) { @@ -429,7 +419,8 @@ void ModInfoDialog::switchToTab(ETabs id) } } - qDebug() << "can't switch to tab " << id << ", not available"; + qDebug() + << "can't switch to tab ID " << static_cast(id) << ", not available"; } MOShared::FilesOrigin* ModInfoDialog::getOrigin() @@ -525,7 +516,8 @@ std::vector ModInfoDialog::getOrderedTabNames() const return v; } -void ModInfoDialog::reAddTabs(const std::vector& visibility, ETabs sel) +void ModInfoDialog::reAddTabs( + const std::vector& visibility, ModInfoTabIDs sel) { Q_ASSERT(visibility.size() == m_tabs.size()); diff --git a/src/modinfodialog.h b/src/modinfodialog.h index 9eb00a3b..effb5d98 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -24,6 +24,7 @@ along with Mod Organizer. If not, see . #include "modinfo.h" #include "tutorabledialog.h" #include "filerenamer.h" +#include "modinfodialogfwd.h" namespace Ui { class ModInfoDialog; } namespace MOShared { class FilesOrigin; } @@ -34,32 +35,6 @@ class Settings; class ModInfoDialogTab; 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); - -FileRenamer::RenameResults hideFile(FileRenamer& renamer, const QString &oldName); -FileRenamer::RenameResults unhideFile(FileRenamer& renamer, const QString &oldName); - -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 @@ -70,21 +45,9 @@ class ModInfoDialog : public MOBase::TutorableDialog template friend std::unique_ptr createTab( - ModInfoDialog& d, int index); + ModInfoDialog& d, ModInfoTabIDs index); public: - enum ETabs { - TAB_TEXTFILES, - TAB_INIFILES, - TAB_IMAGES, - TAB_ESPS, - TAB_CONFLICTS, - TAB_CATEGORIES, - TAB_NEXUS, - TAB_NOTES, - TAB_FILETREE - }; - ModInfoDialog( MainWindow* mw, OrganizerCore* core, PluginContainer* plugin, ModInfo::Ptr mod); @@ -93,7 +56,8 @@ public: void setMod(ModInfo::Ptr mod); void setMod(const QString& name); - void setTab(ETabs id); + + void setTab(ModInfoTabIDs id); int exec() override; @@ -130,7 +94,7 @@ private: OrganizerCore* m_core; PluginContainer* m_plugin; std::vector m_tabs; - ETabs m_initialTab; + ModInfoTabIDs m_initialTab; bool m_arrangingTabs; std::vector createTabs(); @@ -144,8 +108,8 @@ private: void updateTabs(bool becauseOriginChanged=false); void feedFiles(bool becauseOriginChanged); void setTabsColors(); - void switchToTab(ETabs id); - void reAddTabs(const std::vector& visibility, ETabs sel); + void switchToTab(ModInfoTabIDs id); + void reAddTabs(const std::vector& visibility, ModInfoTabIDs sel); std::vector getOrderedTabNames() const; bool tryClose(); diff --git a/src/modinfodialogfwd.h b/src/modinfodialogfwd.h new file mode 100644 index 00000000..9ede766f --- /dev/null +++ b/src/modinfodialogfwd.h @@ -0,0 +1,50 @@ +#ifndef MODINFODIALOGFWD_H +#define MODINFODIALOGFWD_H + +#include "filerenamer.h" + +class ModInfo; +using ModInfoPtr = QSharedPointer; + +enum class ModInfoTabIDs +{ + None = -1, + TextFiles = 0, + IniFiles, + Images, + Esps, + Conflicts, + Categories, + Nexus, + Notes, + Filetree +}; + +class PluginContainer; + +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); + +FileRenamer::RenameResults hideFile(FileRenamer& renamer, const QString &oldName); +FileRenamer::RenameResults unhideFile(FileRenamer& renamer, const QString &oldName); + +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; + } +}; + +#endif // MODINFODIALOGFWD_H diff --git a/src/modinfodialognexus.cpp b/src/modinfodialognexus.cpp index 81546f58..04683c89 100644 --- a/src/modinfodialognexus.cpp +++ b/src/modinfodialognexus.cpp @@ -108,7 +108,7 @@ void NexusTab::firstActivation() updateWebpage(); } -void NexusTab::setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin) +void NexusTab::setMod(ModInfoPtr mod, MOShared::FilesOrigin* origin) { cleanup(); diff --git a/src/modinfodialognexus.h b/src/modinfodialognexus.h index 6478375b..7f894dbf 100644 --- a/src/modinfodialognexus.h +++ b/src/modinfodialognexus.h @@ -40,7 +40,7 @@ public: void clear() override; void update() override; void firstActivation() override; - void setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin) override; + void setMod(ModInfoPtr mod, MOShared::FilesOrigin* origin) override; bool usesOriginFiles() const override; private: diff --git a/src/modinfodialogtab.cpp b/src/modinfodialogtab.cpp index 554df6df..9748d059 100644 --- a/src/modinfodialogtab.cpp +++ b/src/modinfodialogtab.cpp @@ -2,6 +2,7 @@ #include "ui_modinfodialog.h" #include "texteditor.h" #include "directoryentry.h" +#include "modinfo.h" ModInfoDialogTab::ModInfoDialogTab(ModInfoDialogTabContext cx) : ui(cx.ui), m_core(cx.core), m_plugin(cx.plugin), m_parent(cx.parent), @@ -74,7 +75,7 @@ bool ModInfoDialogTab::usesOriginFiles() const return true; } -void ModInfoDialogTab::setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin) +void ModInfoDialogTab::setMod(ModInfoPtr mod, MOShared::FilesOrigin* origin) { m_mod = mod; m_origin = origin; @@ -86,7 +87,7 @@ ModInfo& ModInfoDialogTab::mod() const return *m_mod; } -ModInfo::Ptr ModInfoDialogTab::modPtr() const +ModInfoPtr ModInfoDialogTab::modPtr() const { Q_ASSERT(m_mod); return m_mod; @@ -97,7 +98,7 @@ MOShared::FilesOrigin* ModInfoDialogTab::origin() const return m_origin; } -int ModInfoDialogTab::tabID() const +ModInfoTabIDs ModInfoDialogTab::tabID() const { return m_tabID; } diff --git a/src/modinfodialogtab.h b/src/modinfodialogtab.h index eb574de0..283d9e73 100644 --- a/src/modinfodialogtab.h +++ b/src/modinfodialogtab.h @@ -1,7 +1,7 @@ #ifndef MODINFODIALOGTAB_H #define MODINFODIALOGTAB_H -#include "modinfo.h" +#include "modinfodialogfwd.h" #include namespace MOShared { class FilesOrigin; } @@ -18,8 +18,8 @@ struct ModInfoDialogTabContext PluginContainer& plugin; QWidget* parent; Ui::ModInfoDialog* ui; - int id; - ModInfo::Ptr mod; + ModInfoTabIDs id; + ModInfoPtr mod; MOShared::FilesOrigin* origin; ModInfoDialogTabContext( @@ -27,8 +27,8 @@ struct ModInfoDialogTabContext PluginContainer& plugin, QWidget* parent, Ui::ModInfoDialog* ui, - int id, - ModInfo::Ptr mod, + ModInfoTabIDs id, + ModInfoPtr mod, MOShared::FilesOrigin* origin) : core(core), plugin(plugin), parent(parent), ui(ui), id(id), mod(mod), origin(origin) @@ -96,7 +96,7 @@ public: // derived classes can override this to connect to events on the mod for // examples (see NexusTab), but must call the base class implementation // - virtual void setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin); + virtual void setMod(ModInfoPtr mod, MOShared::FilesOrigin* origin); // this tab should clear its user interface; clear() will always be called // before feedFile() and update() @@ -195,7 +195,7 @@ public: // returns the currently selected mod, can never be empty // - ModInfo::Ptr modPtr() const; + ModInfoPtr modPtr() const; // returns the origin of the selected mod; this can be null for mods that // don't have an origin, like deactivated mods @@ -203,7 +203,7 @@ public: MOShared::FilesOrigin* origin() const; - int tabID() const; + ModInfoTabIDs tabID() const; bool hasData() const; signals: @@ -249,9 +249,9 @@ private: OrganizerCore& m_core; PluginContainer& m_plugin; QWidget* m_parent; - ModInfo::Ptr m_mod; + ModInfoPtr m_mod; MOShared::FilesOrigin* m_origin; - int m_tabID; + ModInfoTabIDs m_tabID; bool m_hasData; bool m_firstActivation; diff --git a/src/organizercore.cpp b/src/organizercore.cpp index e073924b..99ddda1d 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -966,8 +966,8 @@ MOBase::IModInterface *OrganizerCore::installMod(const QString &fileName, "want to configure them now?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes)) { - m_UserInterface->displayModInformation(modInfo, modIndex, - ModInfoDialog::TAB_INIFILES); + m_UserInterface->displayModInformation( + modInfo, modIndex, ModInfoTabIDs::IniFiles); } m_ModInstalled(modName); m_DownloadManager.markInstalled(fileName); @@ -1033,8 +1033,8 @@ void OrganizerCore::installDownload(int index) "want to configure them now?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes)) { - m_UserInterface->displayModInformation(modInfo, modIndex, - ModInfoDialog::TAB_INIFILES); + m_UserInterface->displayModInformation( + modInfo, modIndex, ModInfoTabIDs::IniFiles); } m_ModInstalled(modName); -- cgit v1.3.1 From d9a7ef636bc111f71de697caed89171335a5f3e3 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Tue, 2 Jul 2019 13:21:48 -0400 Subject: reordering tabs, then switching to a mod with a different tab set would discard the changes comments, moving some stuff around --- src/mainwindow.cpp | 2 +- src/modinfodialog.cpp | 163 ++++++++++++++++++++++++++----------------------- src/modinfodialog.h | 134 +++++++++++++++++++++++++++++++++++----- src/modinfodialog.ui | 6 +- src/modinfodialogtab.h | 61 +++++++++++++++++- 5 files changed, 268 insertions(+), 98 deletions(-) (limited to 'src/modinfodialog.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index a596c542..7681b482 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3229,7 +3229,7 @@ void MainWindow::displayModInformation( //Open the tab first if we want to use the standard indexes of the tabs. if (tabID != ModInfoTabIDs::None) { - dialog.setTab(tabID); + dialog.selectTab(tabID); } dialog.restoreState(m_OrganizerCore.settings()); diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index c8ffa35b..adbd22df 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -170,8 +170,11 @@ ModInfoDialog::ModInfoDialog( [&, id=tabInfo.tab->tabID()]{ switchToTab(id); }); } - connect(ui->tabWidget, &QTabWidget::currentChanged, [&]{ onTabChanged(); }); + connect(ui->tabWidget, &QTabWidget::currentChanged, [&]{ onTabSelectionChanged(); }); connect(ui->tabWidget->tabBar(), &QTabBar::tabMoved, [&]{ onTabMoved(); }); + connect(ui->close, &QPushButton::clicked, [&]{ onCloseButton(); }); + connect(ui->previousMod, &QPushButton::clicked, [&]{ onPreviousMod(); }); + connect(ui->nextMod, &QPushButton::clicked, [&]{ onNextMod(); }); } ModInfoDialog::~ModInfoDialog() = default; @@ -242,7 +245,7 @@ void ModInfoDialog::setMod(const QString& name) setMod(mod); } -void ModInfoDialog::setTab(ModInfoTabIDs id) +void ModInfoDialog::selectTab(ModInfoTabIDs id) { if (!isVisible()) { m_initialTab = id; @@ -323,6 +326,10 @@ void ModInfoDialog::setTabsVisibility(bool firstTime) return; } + // something changed; save the current order (if necessary) because some tabs + // will be removed and others added + saveTabOrder(Settings::instance()); + // remember selection const int selIndex = ui->tabWidget->currentIndex(); auto sel = ModInfoTabIDs::None; @@ -337,6 +344,68 @@ void ModInfoDialog::setTabsVisibility(bool firstTime) reAddTabs(visibility, sel); } +void ModInfoDialog::reAddTabs( + const std::vector& visibility, ModInfoTabIDs sel) +{ + 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; + } + } + } + } + + // 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); + }); + } + + + // removing all tabs + 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)); + } + } +} + void ModInfoDialog::updateTabs(bool becauseOriginChanged) { auto* origin = getOrigin(); @@ -440,10 +509,7 @@ MOShared::FilesOrigin* ModInfoDialog::getOrigin() void ModInfoDialog::saveState(Settings& s) const { - const auto tabState = saveTabState(); - if (!tabState.isEmpty()) { - s.directInterface().setValue("mod_info_tab_order", tabState); - } + saveTabOrder(s); // remove 2.2.0 settings s.directInterface().remove("mod_info_tabs"); @@ -466,21 +532,24 @@ void ModInfoDialog::restoreState(const Settings& s) } } -QString ModInfoDialog::saveTabState() const +void ModInfoDialog::saveTabOrder(Settings& s) const { if (static_cast(m_tabs.size()) != ui->tabWidget->count()) { // only save tab state when all tabs are visible - return {}; + return; } - QString result; - QTextStream stream(&result); + QString names; for (int i=0; itabWidget->count(); ++i) { - stream << ui->tabWidget->widget(i)->objectName() << " "; + if (!names.isEmpty()) { + names += " "; + } + + names += ui->tabWidget->widget(i)->objectName(); } - return result.trimmed(); + s.directInterface().setValue("mod_info_tab_order", names); } std::vector ModInfoDialog::getOrderedTabNames() const @@ -516,68 +585,6 @@ std::vector ModInfoDialog::getOrderedTabNames() const return v; } -void ModInfoDialog::reAddTabs( - const std::vector& visibility, ModInfoTabIDs sel) -{ - 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; - } - } - } - } - - // 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); - }); - } - - - // removing all tabs - 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)); - } - } -} - void ModInfoDialog::onOriginModified(int originID) { emit originModified(originID); @@ -600,7 +607,7 @@ void ModInfoDialog::closeEvent(QCloseEvent* e) } } -void ModInfoDialog::on_closeButton_clicked() +void ModInfoDialog::onCloseButton() { if (tryClose()) { close(); @@ -618,7 +625,7 @@ bool ModInfoDialog::tryClose() return true; } -void ModInfoDialog::onTabChanged() +void ModInfoDialog::onTabSelectionChanged() { if (m_arrangingTabs) { // this can be fired while re-arranging tabs, which happens before mods @@ -660,7 +667,7 @@ void ModInfoDialog::onTabMoved() } } -void ModInfoDialog::on_nextButton_clicked() +void ModInfoDialog::onNextMod() { auto mod = m_mainWindow->nextModInList(); if (!mod || mod == m_mod) { @@ -671,7 +678,7 @@ void ModInfoDialog::on_nextButton_clicked() update(); } -void ModInfoDialog::on_prevButton_clicked() +void ModInfoDialog::onPreviousMod() { auto mod = m_mainWindow->previousModInList(); if (!mod || mod == m_mod) { diff --git a/src/modinfodialog.h b/src/modinfodialog.h index effb5d98..035eb6d6 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -43,6 +43,9 @@ class ModInfoDialog : public MOBase::TutorableDialog { Q_OBJECT; + // creates a tab, it's a friend because it uses a bunch of member variables + // to create ModInfoDialogTabContext + // template friend std::unique_ptr createTab( ModInfoDialog& d, ModInfoTabIDs index); @@ -54,37 +57,67 @@ public: ~ModInfoDialog(); - void setMod(ModInfo::Ptr mod); - void setMod(const QString& name); - - void setTab(ModInfoTabIDs id); + // switches to the tab with the given id + // + void selectTab(ModInfoTabIDs id); + // updates all tabs, selects the initial tab and opens the dialog + // int exec() override; + // saves the dialog state and calls saveState() on all tabs + // void saveState(Settings& s) const; + + // restores the dialog state and calls restoreState() on all tabs + // void restoreState(const Settings& s); signals: + // emitted when a tab changes the origin + // void originModified(int originID); protected: + // forwards to tryClose() + // void closeEvent(QCloseEvent* e); -private slots: - void on_closeButton_clicked(); - void on_nextButton_clicked(); - void on_prevButton_clicked(); - private: + // represents a single tab + // struct TabInfo { + // tab implementation std::unique_ptr tab; + + // actual position in the tab bar, updated every time a tab is moved int realPos; + + // widget used by the QTabWidget for this tab + // + // because QTabWidget doesn't support simply hiding tabs, they have to be + // completely removed from the widget when they don't support the current + // mod + // + // therefore, `widget, `caption` and `icon` are remembered so tabs can be + // removed and re-added when navigating between mods + // + // `widget` is also used figure out which tab is where when they're + // re-ordered QWidget* widget; + + // caption for this tab, see `widget` QString caption; + + // icon for this tab, see `widget` QIcon icon; + TabInfo(std::unique_ptr tab); + + // returns whether this tab is part of the tab widget + // bool isVisible() const; }; @@ -94,28 +127,99 @@ private: OrganizerCore* m_core; PluginContainer* m_plugin; std::vector m_tabs; + + // initial tab requested by the main window when the dialog is opened; whether + // the request can be honoured depends on what tabs are present ModInfoTabIDs m_initialTab; + + // set to true when tabs are being removed and re-added while navigating + // between mods; since the current index changes while this is happening, + // onTabSelectionChanged() will be called repeatedly + // + // however, it will check this flag and ignore the event so first activations + // are not fired incorrectly bool m_arrangingTabs; + + // creates all the tabs + // std::vector createTabs(); + + + // sets the currently selected mod; resets first activation, but doesn't + // update anything + // + void setMod(ModInfo::Ptr mod); + + // sets the currently selected mod, if found; forwards to setMod() above + // + void setMod(const QString& name); + + + // returns the currently selected tab, taking re-ordering in to account; + // shouldn't be null, but could be + // TabInfo* currentTab(); - void restoreTabState(const QString& state); - QString saveTabState() const; + + // returns a list of tab object names in their visual order, used by + // saveState() + // + void saveTabOrder(Settings& s) const; + + + // fully updates the dialog; sets the title, the tab visibility and updates + // all the tabs; used when the current mod changes + // + // see setTabsVisibility() for firstTime + // void update(bool firstTime=false); + + // builds the list of visible tabs; if the list is different from what's + // currently displayed, or firstTime is true, forwards to reAddTabs() + void setTabsVisibility(bool firstTime); + + // clears the tab widgets and re-adds the tabs having the visible flag in + // the given vector, following the + void reAddTabs(const std::vector& visibility, ModInfoTabIDs sel); + void onDeleteShortcut(); MOShared::FilesOrigin* getOrigin(); - void setTabsVisibility(bool firstTime); void updateTabs(bool becauseOriginChanged=false); void feedFiles(bool becauseOriginChanged); void setTabsColors(); void switchToTab(ModInfoTabIDs id); - void reAddTabs(const std::vector& visibility, ModInfoTabIDs sel); std::vector getOrderedTabNames() const; bool tryClose(); - void onOriginModified(int originID); - void onTabChanged(); + + // called when the user clicks the close button; closing the dialog by other + // means ends up in closeEvent(); forwards to tryClose() + // + void onCloseButton(); + + // called when the user clicks the previous button; asks the main window for + // the previous mod in the list and loads it + // + void onPreviousMod(); + + // called when the user clicks the next button; asks the main window for the + // next mod in the list and loads it + // + void onNextMod(); + + // called when the selects a tab; handles first activation + // + void onTabSelectionChanged(); + + // called when the user re-orders tabs; sets the correct TabInfo::realPos for + // all tabs + // void onTabMoved(); + + // called when a tab has modified the origin; emits originModified() and + // updates all the tabs that use origin files + // + void onOriginModified(int originID); }; #endif // MODINFODIALOG_H diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui index e54b94b3..5b559992 100644 --- a/src/modinfodialog.ui +++ b/src/modinfodialog.ui @@ -1260,7 +1260,7 @@ p, li { white-space: pre-wrap; } - + Previous @@ -1270,7 +1270,7 @@ p, li { white-space: pre-wrap; } - + Next @@ -1293,7 +1293,7 @@ p, li { white-space: pre-wrap; } - + Close diff --git a/src/modinfodialogtab.h b/src/modinfodialogtab.h index 283d9e73..c43fa076 100644 --- a/src/modinfodialogtab.h +++ b/src/modinfodialogtab.h @@ -203,13 +203,29 @@ public: MOShared::FilesOrigin* origin() const; + // return this tab's ID + // ModInfoTabIDs tabID() const; + + // returns whether this tab has data; derived classes should call setHasData() + // bool hasData() const; signals: + // emitted when a tab modified the files in a mod + // void originModified(int originID); + + // emitted when a tab wants to open a mod by name + // void modOpen(QString name); + + // emitted when a tab used to have data and is now empty, or vice versa + // void hasDataChanged(); + + // emitted when a tab wants focus + // void wantsFocus(); protected: @@ -219,15 +235,27 @@ protected: OrganizerCore& core(); PluginContainer& plugin(); - QWidget* parentWidget(); + // emits originModified + // void emitOriginModified(); + + // emits modOpen + // void emitModOpen(QString name); + + // emits hasDataChanged + // void setHasData(bool b); + // emits wantsFocus + // void setFocus(); + + // saves the sate of the given widget in geometry/modinfodialog_[objectname] + // // this needs to be a template because saveState() and restoreState() are // not in QWidget, but they're in various widgets // @@ -237,6 +265,12 @@ protected: s.setValue(settingName(w), w->saveState()); } + // restores the sate of the given widget from + // geometry/modinfodialog_[objectname] + // + // this needs to be a template because saveState() and restoreState() are + // not in QWidget, but they're in various widgets + // template void restoreWidgetState(const QSettings& s, Widget* w) { @@ -246,16 +280,33 @@ protected: } private: + // core OrganizerCore& m_core; + + // plugin PluginContainer& m_plugin; + + // parent widget, used to display modal dialogs QWidget* m_parent; + + // current mod, never null ModInfoPtr m_mod; + + // current mod origin, may be null MOShared::FilesOrigin* m_origin; + + // tab ID ModInfoTabIDs m_tabID; + + // whether the tab has data bool m_hasData; + + // true if the tab has never been selected for the current mod bool m_firstActivation; + // used by saveWidgetState() and restoreWidgetState() + // QString settingName(QWidget* w) { return "geometry/modinfodialog_" + w->objectName(); @@ -263,6 +314,8 @@ private: }; +// the Notes tab +// class NotesTab : public ModInfoDialogTab { public: @@ -270,7 +323,13 @@ public: void clear() override; void update() override; + + // returns true, separators can have notes + // bool canHandleSeparators() const override; + + // returns false, notes don't use files + // bool usesOriginFiles() const override; private: -- cgit v1.3.1 From 971f890826412994a56fc1b37b10d3bf21e1275d Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Tue, 2 Jul 2019 14:36:59 -0400 Subject: some refactoring updateTabs() creates a list of tabs to update instead of checking for each file comments --- src/modinfodialog.cpp | 307 +++++++++++++++++++++++++++--------------- src/modinfodialog.h | 55 ++++++-- src/modinfodialogfiletree.cpp | 1 - 3 files changed, 241 insertions(+), 122 deletions(-) (limited to 'src/modinfodialog.cpp') diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index adbd22df..16690019 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -33,6 +33,7 @@ along with Mod Organizer. If not, see . using namespace MOBase; using namespace MOShared; +namespace fs = std::filesystem; const int max_scan_for_context_menu = 50; @@ -140,15 +141,51 @@ ModInfoDialog::ModInfoDialog( connect(sc, &QShortcut::activated, [&]{ onDeleteShortcut(); }); setMod(mod); - m_tabs = createTabs(); + createTabs(); - for (int i=0; itabWidget->count(); ++i) { - if (static_cast(i) >= m_tabs.size()) { - qCritical() << "mod info dialog has more tabs than expected"; - break; - } + connect(ui->tabWidget, &QTabWidget::currentChanged, [&]{ onTabSelectionChanged(); }); + connect(ui->tabWidget->tabBar(), &QTabBar::tabMoved, [&]{ onTabMoved(); }); + connect(ui->close, &QPushButton::clicked, [&]{ onCloseButton(); }); + connect(ui->previousMod, &QPushButton::clicked, [&]{ onPreviousMod(); }); + connect(ui->nextMod, &QPushButton::clicked, [&]{ onNextMod(); }); +} + +ModInfoDialog::~ModInfoDialog() = default; + +template +std::unique_ptr createTab(ModInfoDialog& d, ModInfoTabIDs id) +{ + return std::make_unique(ModInfoDialogTabContext( + *d.m_core, *d.m_plugin, &d, d.ui.get(), id, d.m_mod, d.getOrigin())); +} + +void ModInfoDialog::createTabs() +{ + m_tabs.clear(); + + m_tabs.push_back(createTab(*this, ModInfoTabIDs::TextFiles)); + m_tabs.push_back(createTab(*this, ModInfoTabIDs::IniFiles)); + m_tabs.push_back(createTab(*this, ModInfoTabIDs::Images)); + m_tabs.push_back(createTab(*this, ModInfoTabIDs::Esps)); + m_tabs.push_back(createTab(*this, ModInfoTabIDs::Conflicts)); + m_tabs.push_back(createTab(*this, ModInfoTabIDs::Categories)); + m_tabs.push_back(createTab(*this, ModInfoTabIDs::Nexus)); + m_tabs.push_back(createTab(*this, ModInfoTabIDs::Notes)); + m_tabs.push_back(createTab(*this, ModInfoTabIDs::Filetree)); + + // check for tabs in the ui not having a corresponding tab in the list + int count = ui->tabWidget->count(); + if (count < 0 || count > static_cast(m_tabs.size())) { + qCritical() << "mod info dialog has more tabs than expected"; + count = static_cast(m_tabs.size()); + } + // for each tab in the widget; connects the widgets with the tab objects + // + for (int i=0; i(i)]; + + // remembering tab information so tabs can be removed and re-added tabInfo.widget = ui->tabWidget->widget(i); tabInfo.caption = ui->tabWidget->tabText(i); tabInfo.icon = ui->tabWidget->tabIcon(i); @@ -169,50 +206,18 @@ ModInfoDialog::ModInfoDialog( tabInfo.tab.get(), &ModInfoDialogTab::wantsFocus, [&, id=tabInfo.tab->tabID()]{ switchToTab(id); }); } - - connect(ui->tabWidget, &QTabWidget::currentChanged, [&]{ onTabSelectionChanged(); }); - connect(ui->tabWidget->tabBar(), &QTabBar::tabMoved, [&]{ onTabMoved(); }); - connect(ui->close, &QPushButton::clicked, [&]{ onCloseButton(); }); - connect(ui->previousMod, &QPushButton::clicked, [&]{ onPreviousMod(); }); - connect(ui->nextMod, &QPushButton::clicked, [&]{ onNextMod(); }); -} - -ModInfoDialog::~ModInfoDialog() = default; - -template -std::unique_ptr createTab(ModInfoDialog& d, ModInfoTabIDs id) -{ - return std::make_unique(ModInfoDialogTabContext( - *d.m_core, *d.m_plugin, &d, d.ui.get(), id, d.m_mod, d.getOrigin())); -} - -std::vector ModInfoDialog::createTabs() -{ - std::vector v; - - v.push_back(createTab(*this, ModInfoTabIDs::TextFiles)); - v.push_back(createTab(*this, ModInfoTabIDs::IniFiles)); - v.push_back(createTab(*this, ModInfoTabIDs::Images)); - v.push_back(createTab(*this, ModInfoTabIDs::Esps)); - v.push_back(createTab(*this, ModInfoTabIDs::Conflicts)); - v.push_back(createTab(*this, ModInfoTabIDs::Categories)); - v.push_back(createTab(*this, ModInfoTabIDs::Nexus)); - v.push_back(createTab(*this, ModInfoTabIDs::Notes)); - v.push_back(createTab(*this, ModInfoTabIDs::Filetree)); - - return v; } int ModInfoDialog::exec() { + // whether to select the first tab; if the main window requested a specific + // tab, it is selected when encountered in update() const auto selectFirst = (m_initialTab == ModInfoTabIDs::None); update(true); - if (selectFirst) { - if (ui->tabWidget->count() > 0) { - ui->tabWidget->setCurrentIndex(0); - } + if (selectFirst && ui->tabWidget->count() > 0) { + ui->tabWidget->setCurrentIndex(0); } return TutorableDialog::exec(); @@ -223,6 +228,8 @@ void ModInfoDialog::setMod(ModInfo::Ptr mod) Q_ASSERT(mod); m_mod = mod; + // resetting the first activation flag so selecting tabs will trigger it + // again for (auto& tabInfo : m_tabs) { tabInfo.tab->resetFirstActivation(); } @@ -248,6 +255,7 @@ void ModInfoDialog::setMod(const QString& name) void ModInfoDialog::selectTab(ModInfoTabIDs id) { if (!isVisible()) { + // can't select a tab if the dialog hasn't been properly updated yet m_initialTab = id; return; } @@ -262,42 +270,55 @@ ModInfoDialog::TabInfo* ModInfoDialog::currentTab() return nullptr; } + // looking for the actual tab at that position for (auto& tabInfo : m_tabs) { if (tabInfo.realPos == index) { return &tabInfo; } } - qCritical() << "tab index " << index << " not found"; return nullptr; } void ModInfoDialog::update(bool firstTime) { + // remembering the current selection, will be restored if the tab still + // exists const int oldTab = ui->tabWidget->currentIndex(); setWindowTitle(m_mod->name()); + + // rebuilding the tab widget if needed depending on what tabs are valid for + // the current mod setTabsVisibility(firstTime); + // updating the data in all tabs updateTabs(); + // switching to the initial tab, if any if (m_initialTab != ModInfoTabIDs::None) { switchToTab(m_initialTab); m_initialTab = ModInfoTabIDs::None; } if (ui->tabWidget->currentIndex() == oldTab) { - // manually fire activated() because the tab index hasn't been changed if (auto* tabInfo=currentTab()) { + // activated() has to be fired manually because the tab index hasn't been + // changed tabInfo->tab->activated(); + } else { + qCritical() << "tab index " << oldTab << " not found"; } } } void ModInfoDialog::setTabsVisibility(bool firstTime) { + // this flag is picked up by onTabSelectionChanged() to avoid triggering + // activation events while moving tabs around QScopedValueRollback arrangingTabs(m_arrangingTabs, true); + // one bool per tab to indicate whether the tab should be visible std::vector visibility(m_tabs.size()); bool changed = false; @@ -307,14 +328,16 @@ void ModInfoDialog::setTabsVisibility(bool firstTime) bool visible = true; + // a tab is visible if it can handle the current mod if (m_mod->hasFlag(ModInfo::FLAG_FOREIGN)) { visible = tabInfo.tab->canHandleUnmanaged(); } else if (m_mod->hasFlag(ModInfo::FLAG_SEPARATOR)) { visible = tabInfo.tab->canHandleSeparators(); } + // if the visibility of this tab is changing, set changed to true because + // the tabs have to be rebuilt const auto currentlyVisible = (ui->tabWidget->indexOf(tabInfo.widget) != -1); - if (visible != currentlyVisible) { changed = true; } @@ -322,25 +345,23 @@ void ModInfoDialog::setTabsVisibility(bool firstTime) visibility[i] = visible; } + // the tabs have to be rebuilt the first time the dialog is shown, or when + // the visibility of any tab has changed if (!firstTime && !changed) { return; } - // something changed; save the current order (if necessary) because some tabs - // will be removed and others added + // save the current order (if necessary) because some tabs will be removed and + // others added saveTabOrder(Settings::instance()); - // remember selection - const int selIndex = ui->tabWidget->currentIndex(); + // remember selection, if any auto sel = ModInfoTabIDs::None; - - for (const auto& tabInfo : m_tabs) { - if (tabInfo.realPos == selIndex) { - sel = tabInfo.tab->tabID(); - break; - } + if (const auto* tabInfo=currentTab()) { + sel = tabInfo->tab->tabID(); } + // removes all tabs and re-adds the visible ones reAddTabs(visibility, sel); } @@ -352,21 +373,31 @@ void ModInfoDialog::reAddTabs( // ordered tab names from settings const auto orderedNames = getOrderedTabNames(); + // whether the tabs can be sorted; if the object name of a tab widget is not + // found in orderedNames, the list cannot be sorted safely 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; - } + if (!visibility[i]) { + // this tab is not visible, skip it + continue; + } + + // this tab is visible + visibleTabs.push_back(&m_tabs[i]); + + if (canSort) { + // make sure the widget object name is found in the list + const auto objectName = m_tabs[i].widget->objectName(); + auto itor = std::find(orderedNames.begin(), orderedNames.end(), objectName); + + if (itor == orderedNames.end()) { + // this shouldn't happen, it means there's a tab in the UI that's no + // in the list + qCritical() << "can't sort tabs, '" << objectName << "' not found"; + canSort = false; } } } @@ -374,10 +405,16 @@ void ModInfoDialog::reAddTabs( // 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()); + // looking the names in the ordered list + auto aItor = std::find( + orderedNames.begin(), orderedNames.end(), + a->widget->objectName()); - // this was checked above + auto bItor = std::find( + orderedNames.begin(), orderedNames.end(), + b->widget->objectName()); + + // this shouldn't happen, it was checked above Q_ASSERT(aItor != orderedNames.end() && bItor != orderedNames.end()); return (aItor < bItor); @@ -397,9 +434,13 @@ void ModInfoDialog::reAddTabs( for (std::size_t i=0; i(i); + + // adding tab ui->tabWidget->addTab(tabInfo.widget, tabInfo.icon, tabInfo.caption); + // selecting if (tabInfo.tab->tabID() == sel) { ui->tabWidget->setCurrentIndex(static_cast(i)); } @@ -410,59 +451,72 @@ void ModInfoDialog::updateTabs(bool becauseOriginChanged) { auto* origin = getOrigin(); + // list of tabs that should be updated + std::vector interestedTabs; + for (auto& tabInfo : m_tabs) { + // don't touch invisible tabs if (!tabInfo.isVisible()) { continue; } + // updateTabs() is also called from onOriginModified() to update all the + // tabs that depend on the origin; if updateTabs() is called because the + // origin changed, but the tab doesn't use origin files, it can be safely + // skipped + // + // this happens for tabs like notes and categories, which don't need to + // be updated when files change if (becauseOriginChanged && !tabInfo.tab->usesOriginFiles()) { continue; } - tabInfo.tab->setMod(m_mod, origin); - tabInfo.tab->clear(); + // this tab should be updated + interestedTabs.push_back(&tabInfo); } - feedFiles(becauseOriginChanged); + for (auto* tabInfo : interestedTabs) { + // set the current mod + tabInfo->tab->setMod(m_mod, origin); - for (auto& tabInfo : m_tabs) { - if (tabInfo.isVisible()) { - tabInfo.tab->update(); - } + // clear + tabInfo->tab->clear(); } + // feed all the files from the filesystem + feedFiles(interestedTabs); + + // call update() on all tabs + for (auto* tabInfo : interestedTabs) { + tabInfo->tab->update(); + } + + // update the text colours setTabsColors(); } -void ModInfoDialog::feedFiles(bool becauseOriginChanged) +void ModInfoDialog::feedFiles(std::vector& interestedTabs) { - namespace fs = std::filesystem; - const auto rootPath = m_mod->absolutePath(); + if (rootPath.isEmpty()) { + return; + } - if (rootPath.length() > 0) { - fs::path path(rootPath.toStdWString()); - for (const auto& entry : fs::recursive_directory_iterator(path)) { - if (!entry.is_regular_file()) { - continue; - } - - const auto fileName = QString::fromWCharArray( - entry.path().native().c_str()); + const fs::path fsPath(rootPath.toStdWString()); - for (auto& tabInfo : m_tabs) { - if (!tabInfo.isVisible()) { - continue; - } + for (const auto& entry : fs::recursive_directory_iterator(fsPath)) { + if (!entry.is_regular_file()) { + // skip directories + continue; + } - if (becauseOriginChanged && !tabInfo.tab->usesOriginFiles()) { - continue; - } + const auto filePath = QString::fromStdWString(entry.path().native()); - if (tabInfo.tab->feedFile(rootPath, fileName)) { - break; - } + // for each tab + for (auto* tabInfo : interestedTabs) { + if (tabInfo->tab->feedFile(rootPath, filePath)) { + break; } } } @@ -470,41 +524,53 @@ void ModInfoDialog::feedFiles(bool becauseOriginChanged) void ModInfoDialog::setTabsColors() { + const auto p = m_mainWindow->palette(); + for (const auto& tabInfo : m_tabs) { - const auto c = tabInfo.tab->hasData() ? + if (!tabInfo.isVisible()) { + // don't bother with invisible tabs + continue; + } + + const QColor color = tabInfo.tab->hasData() ? QColor::Invalid : - m_mainWindow->palette().color(QPalette::Disabled, QPalette::WindowText); + p.color(QPalette::Disabled, QPalette::WindowText); - ui->tabWidget->tabBar()->setTabTextColor(tabInfo.realPos, c); + ui->tabWidget->tabBar()->setTabTextColor(tabInfo.realPos, color); } } void ModInfoDialog::switchToTab(ModInfoTabIDs id) { + // look a tab with the given id for (const auto& tabInfo : m_tabs) { if (tabInfo.tab->tabID() == id) { + // use realPos to select the proper tab in the widget ui->tabWidget->setCurrentIndex(tabInfo.realPos); return; } } + // this could happen if the tab is not visible right now qDebug() - << "can't switch to tab ID " << static_cast(id) << ", not available"; + << "can't switch to tab ID " << static_cast(id) + << ", not available"; } 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; - } + + if (!ds->originExists(m_mod->name().toStdWString())) { + return nullptr; } - return nullptr; + auto* origin = &ds->getOriginByName(m_mod->name().toStdWString()); + if (origin->isDisabled()) { + return nullptr; + } + + return origin; } void ModInfoDialog::saveState(Settings& s) const @@ -520,6 +586,7 @@ void ModInfoDialog::saveState(Settings& s) const s.directInterface().remove("mod_info_conflicts_noconflict"); s.directInterface().remove("mod_info_conflicts_overwritten"); + // save state for each tab for (const auto& tabInfo : m_tabs) { tabInfo.tab->saveState(s); } @@ -527,6 +594,10 @@ void ModInfoDialog::saveState(Settings& s) const void ModInfoDialog::restoreState(const Settings& s) { + // tab order is not restored here, it will be picked up if tabs have to be + // removed and re-added + + // restore state for each tab for (const auto& tabInfo : m_tabs) { tabInfo.tab->restoreState(s); } @@ -536,6 +607,13 @@ void ModInfoDialog::saveTabOrder(Settings& s) const { if (static_cast(m_tabs.size()) != ui->tabWidget->count()) { // only save tab state when all tabs are visible + // + // if not all tabs are visible, it becomes very difficult to figure out in + // what order the user wants these tabs to be, so just avoid saving it + // completely + // + // this means that reordering tabs when not all tabs are visible is not + // saved, but it's better than breaking everything return; } @@ -559,7 +637,7 @@ std::vector ModInfoDialog::getOrderedTabNames() const std::vector v; if (settings.contains("mod_info_tabs")) { - // old byte array + // old byte array from 2.2.0 QDataStream stream(settings.value("mod_info_tabs").toByteArray()); int count = 0; @@ -587,12 +665,16 @@ std::vector ModInfoDialog::getOrderedTabNames() const void ModInfoDialog::onOriginModified(int originID) { + // tell the main window the origin changed emit originModified(originID); + + // update tabs that depend on the origin updateTabs(true); } void ModInfoDialog::onDeleteShortcut() { + // forward the request to the current tab if (auto* tabInfo=currentTab()) { tabInfo->tab->deleteRequested(); } @@ -616,6 +698,10 @@ void ModInfoDialog::onCloseButton() bool ModInfoDialog::tryClose() { + // cancel the close if any tab returns false; for example. this can happen if + // a tab has unsaved content, pops a confirmation dialog, and the user clicks + // cancel + for (auto& tabInfo : m_tabs) { if (!tabInfo.tab->canClose()) { return false; @@ -634,6 +720,7 @@ void ModInfoDialog::onTabSelectionChanged() return; } + // this will call firstActivation() on the tab if needed if (auto* tabInfo=currentTab()) { tabInfo->tab->activated(); } diff --git a/src/modinfodialog.h b/src/modinfodialog.h index 035eb6d6..34555b0c 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -141,9 +141,9 @@ private: bool m_arrangingTabs; - // creates all the tabs + // creates all the tabs and connects events // - std::vector createTabs(); + void createTabs(); // sets the currently selected mod; resets first activation, but doesn't @@ -155,17 +155,16 @@ private: // void setMod(const QString& name); + // returns the origin of the current mod, may be null + // + MOShared::FilesOrigin* getOrigin(); + // returns the currently selected tab, taking re-ordering in to account; // shouldn't be null, but could be // TabInfo* currentTab(); - // returns a list of tab object names in their visual order, used by - // saveState() - // - void saveTabOrder(Settings& s) const; - // fully updates the dialog; sets the title, the tab visibility and updates // all the tabs; used when the current mod changes @@ -179,16 +178,50 @@ private: void setTabsVisibility(bool firstTime); // clears the tab widgets and re-adds the tabs having the visible flag in - // the given vector, following the + // the given vector, following the tab order from the settings + // void reAddTabs(const std::vector& visibility, ModInfoTabIDs sel); - void onDeleteShortcut(); - MOShared::FilesOrigin* getOrigin(); + // called by update(); clears tabs, feeds files and calls update() on all + // tabs, then setTabsColors() + // void updateTabs(bool becauseOriginChanged=false); - void feedFiles(bool becauseOriginChanged); + + // goes through all files on the filesystem for the current mod and calls + // feedFile() on every tab until one accepts it + // + void feedFiles(std::vector& interestedTabs); + + // goes through all tabs and sets the tab text colour depending on whether + // they have data or not + // void setTabsColors(); + + + // called when the delete key is pressed anywhere in the dialog; forwards to + // ModInfoDialogTab::deleteRequest() for the currently selected tab + // + void onDeleteShortcut(); + + + // finds the tab with the given id and selects it + // void switchToTab(ModInfoTabIDs id); + + + // saves the current tab order; used by saveState(), but also by + // setTabsVisibility() to make sure any changes to order are saved before + // re-adding tabs + // + void saveTabOrder(Settings& s) const; + + // returns a list of tab names in the order they should appear on the widget + // std::vector getOrderedTabNames() const; + + // asks all the tabs if they accept closing the dialog, returns false if one + // objected + // bool tryClose(); diff --git a/src/modinfodialogfiletree.cpp b/src/modinfodialogfiletree.cpp index 35480e2c..0b519932 100644 --- a/src/modinfodialogfiletree.cpp +++ b/src/modinfodialogfiletree.cpp @@ -314,7 +314,6 @@ void FileTreeTab::changeVisibility(bool visible) qDebug().nospace() << (visible ? "unhiding" : "hiding") << " filetree files done"; if (changed) { - qDebug().nospace() << "triggering refresh"; if (origin()) { emitOriginModified(); } -- cgit v1.3.1