/* Copyright (C) 2012 Sebastian Herbord. All rights reserved. This file is part of Mod Organizer. Mod Organizer is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Mod Organizer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Mod Organizer. If not, see . */ #include "modinfodialog.h" #include "ui_modinfodialog.h" #include "plugincontainer.h" #include "organizercore.h" #include "mainwindow.h" #include "modinfodialogtextfiles.h" #include "modinfodialogimages.h" #include "modinfodialogesps.h" #include "modinfodialogconflicts.h" #include "modinfodialogcategories.h" #include "modinfodialognexus.h" #include "modinfodialogfiletree.h" using namespace MOBase; using namespace MOShared; const int max_scan_for_context_menu = 50; bool canPreviewFile( PluginContainer& pluginContainer, bool isArchive, const QString& filename) { if (isArchive) { return false; } const auto ext = QFileInfo(filename).suffix(); return pluginContainer.previewGenerator().previewSupported(ext); } bool canOpenFile(bool isArchive, const QString&) { // can open anything as long as it's not in an archive return !isArchive; } bool canHideFile(bool isArchive, const QString& filename) { if (isArchive) { // can't hide files from archives return false; } if (filename.endsWith(ModInfo::s_HiddenExt)) { // already hidden return false; } return true; } bool canUnhideFile(bool isArchive, const QString& filename) { if (isArchive) { // can't unhide files from archives return false; } if (!filename.endsWith(ModInfo::s_HiddenExt)) { // already visible return false; } 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::TabInfo::TabInfo(std::unique_ptr tab) : tab(std::move(tab)), realPos(-1), widget(nullptr) { } 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) { ui->setupUi(this); auto* sc = new QShortcut(QKeySequence::Delete, this); connect(sc, &QShortcut::activated, [&]{ onDeleteShortcut(); }); m_tabs = 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; } 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( tabInfo.tab.get(), &ModInfoDialogTab::originModified, [&](int originID){ emit originModified(originID); }); connect( tabInfo.tab.get(), &ModInfoDialogTab::modOpen, [&](const QString& name){ setMod(name); update(); }); } } ModInfoDialog::~ModInfoDialog() = default; 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)); return v; } int ModInfoDialog::exec() { 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()); for (const auto& tabInfo : m_tabs) { tabInfo.tab->saveState(s); } } 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 { 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); 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; } } // 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); } ui->tabWidget->blockSignals(false);*/ } 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& tabInfo : m_tabs) { if (tabInfo.tab->deleteRequested()) { break; } } } void ModInfoDialog::on_closeButton_clicked() { for (auto& tabInfo : m_tabs) { if (!tabInfo.tab->canClose()) { return; } } close(); } void ModInfoDialog::on_tabWidget_currentChanged(int index) { } void ModInfoDialog::on_nextButton_clicked() { auto mod = m_mainWindow->nextModInList(); if (mod == m_mod) { return; } setMod(mod); update(); } void ModInfoDialog::on_prevButton_clicked() { auto mod = m_mainWindow->previousModInList(); if (mod == m_mod) { return; } setMod(mod); update(); }