From dfca8be71b15bc13997110cc8777dd5a84a1fde7 Mon Sep 17 00:00:00 2001 From: Tannin Date: Sat, 21 Sep 2013 19:25:33 +0200 Subject: - esp reader now handles invalid files more gracefully - files moved will now also be treated as "deleted" in the old location so a newly created file with that same name is not created in overwrite - introduced a mechanism by which MO can recognize if it crashed before when attempting to load a plugin. That plugin can be blacklisted so it doesn't get loaded again - plugins can now programaticaly change their settings - plugins can now store data persistently without exposing that data as settings - requesting an unset-setting from a plugin is no longer treated as a bug - clarified warning message for when files are in overwrite directory - the proxyPython plugin will now discover if python initialization crashed MO on a previous session and give the user a chance to fix it or disable the plugin - bugfix: GetModuleFileName modified the buffer past the zero termination. While this doesn't violate the API documentation it is different from the regular windows implementation - bugfix: proxy plugins couldn't access the parent widget - bugfix: when moving a file from overwrite to a mod the in-memory file structure wasn't updated - bugfix: name input dialog for profiles allowed names that weren't valid directory names - bugfix: profile dialog wasn't able to delete profiles if the name started or ended in whitespaces - bugfix: The name-cells for plugin settings could be changed (without effect) - removed a few obsolete files from the repository --- src/mainwindow.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/mainwindow.h') diff --git a/src/mainwindow.h b/src/mainwindow.h index 152c0a27..54ae0e96 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -105,6 +105,9 @@ public: virtual bool removeMod(MOBase::IModInterface *mod); virtual void modDataChanged(MOBase::IModInterface *mod); virtual QVariant pluginSetting(const QString &pluginName, const QString &key) const; + virtual void setPluginSetting(const QString &pluginName, const QString &key, const QVariant &value); + virtual QVariant persistent(const QString &pluginName, const QString &key, const QVariant &def = QVariant()) const; + virtual void setPersistent(const QString &pluginName, const QString &key, const QVariant &value, bool sync = true); virtual QString pluginDataPath() const; virtual void installMod(const QString &fileName); virtual QString resolvePath(const QString &fileName) const; @@ -315,6 +318,8 @@ private: std::vector m_DiagnosisPlugins; std::vector m_UnloadedPlugins; + QFile m_PluginsCheck; + private slots: void showMessage(const QString &message); @@ -435,6 +440,7 @@ private slots: void updateStyle(const QString &style); void modlistChanged(const QModelIndex &index, int role); + void fileMoved(const QString &filePath, const QString &oldOriginName, const QString &newOriginName); void savePluginList(); -- cgit v1.3.1 From c57b172204432f18856ef05d8faa9ebf3001b0c8 Mon Sep 17 00:00:00 2001 From: Tannin Date: Sun, 22 Sep 2013 13:10:21 +0200 Subject: - removed some obsolete code - MO will no longer start an application while the directory structure is being refreshed because MO may need to access profile files afterwards - bugfix: the overwrite info-dialog was not destroyed and could thus keep a lock on files thus preventing those files from being moved/deleted --- src/mainwindow.cpp | 21 ++++++++++++++++++--- src/mainwindow.h | 1 + 2 files changed, 19 insertions(+), 3 deletions(-) (limited to 'src/mainwindow.h') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index ffec91a1..fdd16049 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1272,6 +1272,11 @@ HANDLE MainWindow::spawnBinaryDirect(const QFileInfo &binary, const QString &arg } } + while (m_RefreshProgress->isVisible()) { + ::Sleep(1000); + QCoreApplication::processEvents(); + } + return startBinary(binary, arguments, profileName, m_Settings.logLevel(), currentDirectory, true); } @@ -2612,7 +2617,7 @@ void MainWindow::fileMoved(const QString &filePath, const QString &oldOriginName reportError(tr("Failed to move \"%1\" from mod \"%2\" to \"%3\": %4").arg(filePath).arg(oldOriginName).arg(newOriginName).arg(e.what())); } } else { - reportError(tr("Failed to relocate \"%1\"").arg(filePath)); + // this is probably not an error, the specified path is likely a directory } } @@ -2871,6 +2876,15 @@ void MainWindow::unendorse_clicked() } +void MainWindow::overwriteClosed(int) +{ + QDialog *dialog = this->findChild("__overwriteDialog"); + if (dialog != NULL) { + dialog->deleteLater(); + } +} + + void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index, int tab) { std::vector flags = modInfo->getFlags(); @@ -2883,6 +2897,7 @@ void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index, dialog->show(); dialog->raise(); dialog->activateWindow(); + connect(dialog, SIGNAL(finished(int)), this, SLOT(overwriteClosed(int))); } else { ModInfoDialog dialog(modInfo, m_DirectoryStructure, this); connect(&dialog, SIGNAL(nexusLinkActivated(QString)), this, SLOT(nexusLinkActivated(QString))); @@ -3100,8 +3115,8 @@ void MainWindow::createModFromOverwrite() } ModInfo::Ptr overwriteInfo = ModInfo::getByIndex(m_ContextRow); - - shellMove(QStringList(overwriteInfo->absolutePath() + "\\*"), QStringList(newMod->absolutePath()), this); + shellMove(QStringList(QDir::toNativeSeparators(overwriteInfo->absolutePath()) + "\\*"), + QStringList(QDir::toNativeSeparators(newMod->absolutePath())), this); refreshModList(); } diff --git a/src/mainwindow.h b/src/mainwindow.h index 54ae0e96..80dca172 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -464,6 +464,7 @@ private slots: void toolBar_customContextMenuRequested(const QPoint &point); void removeFromToolbar(); + void overwriteClosed(int); private slots: // ui slots // actions -- cgit v1.3.1 From cffd9eb4e21f0ddcddca68d8eb0f1c80c8ac6ae1 Mon Sep 17 00:00:00 2001 From: Tannin Date: Sat, 5 Oct 2013 14:20:48 +0200 Subject: - hook.dll now doesn't inject to certain applications (currently steam, chrome and firefox) - versioning system improved. Will now report "downgrades" for mods and support a different versioning system (requires manual switch) - updates can now be ignored until a new version is uploaded - new splash screen - bugfix: a few memory leaks (shouldn't account for much) - bugfix: result of GetModuleHandle wasn't zero-terminated in some cases --- src/categories.cpp | 7 ++++ src/categories.h | 4 ++ src/downloadmanager.cpp | 1 + src/installationmanager.cpp | 6 +-- src/main.cpp | 97 ++++++++++++++++++++++++-------------------- src/mainwindow.cpp | 87 +++++++++++++++++++++++++++------------ src/mainwindow.h | 4 ++ src/modinfo.cpp | 30 ++++++++++++-- src/modinfo.h | 49 ++++++++++++++++++++++ src/modinfodialog.cpp | 11 +---- src/modlist.cpp | 34 ++++++++++------ src/modlistsortproxy.cpp | 2 +- src/modlistview.cpp | 4 +- src/nexusinterface.cpp | 10 +++++ src/nexusinterface.h | 1 + src/nxmaccessmanager.cpp | 8 ++++ src/nxmaccessmanager.h | 2 + src/organizer.pro | 7 +++- src/pluginlist.cpp | 4 ++ src/pluginlist.h | 2 + src/profile.cpp | 3 +- src/selfupdater.cpp | 2 - src/shared/gameinfo.cpp | 7 ++++ src/shared/gameinfo.h | 4 +- src/shared/leaktrace.cpp | 1 + src/shared/shared.pro | 10 ++++- src/spawn.cpp | 4 +- src/spawn.h | 4 +- src/splash.png | Bin 103422 -> 260807 bytes 29 files changed, 296 insertions(+), 109 deletions(-) (limited to 'src/mainwindow.h') diff --git a/src/categories.cpp b/src/categories.cpp index 62ba3ca5..c084c238 100644 --- a/src/categories.cpp +++ b/src/categories.cpp @@ -43,6 +43,7 @@ QString CategoryFactory::categoriesFilePath() CategoryFactory::CategoryFactory() { + atexit(&cleanup); reset(); QFile categoryFile(categoriesFilePath()); @@ -124,6 +125,12 @@ void CategoryFactory::setParents() } } +void CategoryFactory::cleanup() +{ + delete s_Instance; + s_Instance = NULL; +} + void CategoryFactory::saveCategories() { diff --git a/src/categories.h b/src/categories.h index 75698149..31fccd6d 100644 --- a/src/categories.h +++ b/src/categories.h @@ -180,6 +180,8 @@ private: void setParents(); + static void cleanup(); + private: static CategoryFactory *s_Instance; @@ -188,6 +190,8 @@ private: std::map m_IDMap; std::map m_NexusMap; +private: + }; diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index bbb34ccc..b965d598 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -162,6 +162,7 @@ DownloadManager::~DownloadManager() for (QVector::iterator iter = m_ActiveDownloads.begin(); iter != m_ActiveDownloads.end(); ++iter) { delete *iter; } + m_ActiveDownloads.clear(); } diff --git a/src/installationmanager.cpp b/src/installationmanager.cpp index 378eb38a..87efecf1 100644 --- a/src/installationmanager.cpp +++ b/src/installationmanager.cpp @@ -46,6 +46,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include using namespace MOBase; @@ -678,8 +679,7 @@ bool InstallationManager::install(const QString &fileName, GuessedValue this->m_CurrentArchive->close(); }); - DirectoryTree *filesTree = archiveOpen ? createFilesTree() : NULL; - + QScopedPointer filesTree(archiveOpen ? createFilesTree() : NULL); IPluginInstaller::EInstallResult installResult = IPluginInstaller::RESULT_NOTATTEMPTED; std::sort(m_Installers.begin(), m_Installers.end(), [] (IPluginInstaller *LHS, IPluginInstaller *RHS) { @@ -704,7 +704,7 @@ bool InstallationManager::install(const QString &fileName, GuessedValue (filesTree != NULL) && (installer->isArchiveSupported(*filesTree))) { installResult = installerSimple->install(modName, *filesTree, version, modID); if (installResult == IPluginInstaller::RESULT_SUCCESS) { - mapToArchive(filesTree); + mapToArchive(filesTree.data()); // the simple installer only prepares the installation, the rest works the same for all installers if (!doInstall(modName, modID, version, newestVersion, categoryID)) { installResult = IPluginInstaller::RESULT_FAILED; diff --git a/src/main.cpp b/src/main.cpp index 5a694121..c4431ac4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,6 +18,11 @@ along with Mod Organizer. If not, see . */ +#ifdef LEAK_CHECK_WITH_VLD +#include +#include +#endif // LEAK_CHECK_WITH_VLD + #include #include #include @@ -430,61 +435,65 @@ int main(int argc, char *argv[]) application.setStyleFile(settings.value("Settings/style", "").toString()); - // set up main window and its data structures - MainWindow mainWindow(argv[0], settings); - QObject::connect(&mainWindow, SIGNAL(styleChanged(QString)), &application, SLOT(setStyleFile(QString))); - QObject::connect(&instance, SIGNAL(messageSent(QString)), &mainWindow, SLOT(externalMessage(QString))); + int res = 1; + { // scope to control lifetime of mainwindow + // set up main window and its data structures + MainWindow mainWindow(argv[0], settings); + QObject::connect(&mainWindow, SIGNAL(styleChanged(QString)), &application, SLOT(setStyleFile(QString))); + QObject::connect(&instance, SIGNAL(messageSent(QString)), &mainWindow, SLOT(externalMessage(QString))); - mainWindow.setExecutablesList(executablesList); - mainWindow.readSettings(); + mainWindow.setExecutablesList(executablesList); + mainWindow.readSettings(); - QString selectedProfileName = QString::fromUtf8(settings.value("selected_profile", "").toByteArray()); + QString selectedProfileName = QString::fromUtf8(settings.value("selected_profile", "").toByteArray()); - { // see if there is a profile on the command line - int profileIndex = arguments.indexOf("-p", 1); - if ((profileIndex != -1) && (profileIndex < arguments.size() - 1)) { - qDebug("profile overwritten on command line"); - selectedProfileName = arguments.at(profileIndex + 1); + { // see if there is a profile on the command line + int profileIndex = arguments.indexOf("-p", 1); + if ((profileIndex != -1) && (profileIndex < arguments.size() - 1)) { + qDebug("profile overwritten on command line"); + selectedProfileName = arguments.at(profileIndex + 1); + } + arguments.removeAt(profileIndex); + arguments.removeAt(profileIndex); + } + qDebug("configured profile: %s", qPrintable(selectedProfileName)); + + // if we have a command line parameter, it is either a nxm link or + // a binary to start + if ((arguments.size() > 1) && (!isNxmLink(arguments.at(1)))) { + QString exeName = arguments.at(1); + qDebug("starting %s from command line", qPrintable(exeName)); + arguments.removeFirst(); // remove application name (ModOrganizer.exe) + arguments.removeFirst(); // remove binary name + // pass the remaining parameters to the binary + mainWindow.spawnProgram(exeName, arguments.join(" "), selectedProfileName, QDir()); + return 0; } - arguments.removeAt(profileIndex); - arguments.removeAt(profileIndex); - } - qDebug("configured profile: %s", qPrintable(selectedProfileName)); - - // if we have a command line parameter, it is either a nxm link or - // a binary to start - if ((arguments.size() > 1) && (!isNxmLink(arguments.at(1)))) { - QString exeName = arguments.at(1); - qDebug("starting %s from command line", qPrintable(exeName)); - arguments.removeFirst(); // remove application name (ModOrganizer.exe) - arguments.removeFirst(); // remove binary name - // pass the remaining parameters to the binary - mainWindow.spawnProgram(exeName, arguments.join(" "), selectedProfileName, QDir()); - return 0; - } - mainWindow.createFirstProfile(); + mainWindow.createFirstProfile(); - if (selectedProfileName.length() != 0) { - if (!mainWindow.setCurrentProfile(selectedProfileName)) { + if (selectedProfileName.length() != 0) { + if (!mainWindow.setCurrentProfile(selectedProfileName)) { + mainWindow.setCurrentProfile(1); + qWarning("failed to set profile: %s", + selectedProfileName.toUtf8().constData()); + } + } else { mainWindow.setCurrentProfile(1); - qWarning("failed to set profile: %s", - selectedProfileName.toUtf8().constData()); } - } else { - mainWindow.setCurrentProfile(1); - } - qDebug("displaying main window"); - mainWindow.show(); + qDebug("displaying main window"); + mainWindow.show(); - if ((arguments.size() > 1) && - (isNxmLink(arguments.at(1)))) { - qDebug("starting download from command line: %s", qPrintable(arguments.at(1))); - mainWindow.externalMessage(arguments.at(1)); + if ((arguments.size() > 1) && + (isNxmLink(arguments.at(1)))) { + qDebug("starting download from command line: %s", qPrintable(arguments.at(1))); + mainWindow.externalMessage(arguments.at(1)); + } + splash.finish(&mainWindow); + res = application.exec(); } - splash.finish(&mainWindow); - return application.exec(); + return res; } catch (const std::exception &e) { reportError(e.what()); return 1; diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 24772db1..1ea35d74 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -139,8 +139,8 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget : QMainWindow(parent), ui(new Ui::MainWindow), m_Tutorial(this, "MainWindow"), m_ExeName(exeName), m_OldProfileIndex(-1), m_DirectoryStructure(new DirectoryEntry(L"data", NULL, 0)), - m_ModList(NexusInterface::instance()), m_ModListGroupingProxy(NULL), m_ModListSortProxy(NULL), - m_OldExecutableIndex(-1), m_GamePath(ToQString(GameInfo::instance().getGameDirectory())), + m_ModList(this), m_ModListGroupingProxy(NULL), m_ModListSortProxy(NULL), + m_PluginList(this), m_OldExecutableIndex(-1), m_GamePath(ToQString(GameInfo::instance().getGameDirectory())), m_DownloadManager(NexusInterface::instance(), this), m_InstallationManager(this), m_Translator(NULL), m_TranslatorQt(NULL), m_Updater(NexusInterface::instance(), this), m_CategoryFactory(CategoryFactory::instance()), @@ -149,7 +149,7 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget m_GameInfo(new GameInfoImpl()) { ui->setupUi(this); - this->setWindowTitle(ToQString(GameInfo::instance().getGameName()).append(" Mod Organizer v").append(m_Updater.getVersion().canonicalString())); + this->setWindowTitle(ToQString(GameInfo::instance().getGameName()) + " Mod Organizer v" + m_Updater.getVersion().displayString()); m_RefreshProgress = new QProgressBar(statusBar()); m_RefreshProgress->setTextVisible(true); @@ -255,7 +255,7 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget connect(&m_Updater, SIGNAL(updateAvailable()), this, SLOT(updateAvailable())); connect(&m_Updater, SIGNAL(motdAvailable(QString)), this, SLOT(motdReceived(QString))); - connect(ExitProxy::instance(), SIGNAL(exit()), this, SLOT(close())); +// connect(ExitProxy::instance(), SIGNAL(exit()), this, SLOT(close())); connect(NexusInterface::instance()->getAccessManager(), SIGNAL(loginSuccessful(bool)), this, SLOT(loginSuccessful(bool))); connect(NexusInterface::instance()->getAccessManager(), SIGNAL(loginFailed(QString)), this, SLOT(loginFailed(QString))); @@ -412,7 +412,7 @@ void MainWindow::actionToToolButton(QAction *&sourceAction) button->setToolButtonStyle(ui->toolBar->toolButtonStyle()); button->setToolTip(sourceAction->toolTip()); button->setShortcut(sourceAction->shortcut()); - QMenu *buttonMenu = new QMenu(sourceAction->text()); + QMenu *buttonMenu = new QMenu(sourceAction->text(), button); button->setMenu(buttonMenu); QAction *newAction = ui->toolBar->insertWidget(sourceAction, button); newAction->setObjectName(sourceAction->objectName()); @@ -1499,6 +1499,8 @@ void MainWindow::updateTo(QTreeWidgetItem *subTree, const std::wstring &director updateTo(directoryChild, temp.str(), **current, conflictsOnly); if (directoryChild->childCount() != 0) { subTree->addChild(directoryChild); + } else { + delete directoryChild; } } } @@ -3069,7 +3071,6 @@ void MainWindow::openExplorer_clicked() ::ShellExecuteW(NULL, L"explore", ToWString(modInfo->absolutePath()).c_str(), NULL, NULL, SW_SHOWNORMAL); } - void MainWindow::information_clicked() { try { @@ -3079,7 +3080,6 @@ void MainWindow::information_clicked() } } - void MainWindow::syncOverwrite() { ModInfo::Ptr modInfo = ModInfo::getByIndex(m_ContextRow); @@ -3091,7 +3091,6 @@ void MainWindow::syncOverwrite() } } - void MainWindow::createModFromOverwrite() { GuessedValue name; @@ -3125,14 +3124,12 @@ void MainWindow::createModFromOverwrite() refreshModList(); } - void MainWindow::cancelModListEditor() { ui->modList->setEnabled(false); ui->modList->setEnabled(true); } - void MainWindow::on_modList_doubleClicked(const QModelIndex &index) { if (!index.isValid()) { @@ -3155,7 +3152,6 @@ void MainWindow::on_modList_doubleClicked(const QModelIndex &index) } } - bool MainWindow::addCategories(QMenu *menu, int targetID) { ModInfo::Ptr modInfo = ModInfo::getByIndex(m_ContextRow); @@ -3210,7 +3206,6 @@ void MainWindow::saveCategoriesFromMenu(QMenu *menu, int modRow) } } - void MainWindow::saveCategories() { QMenu *menu = qobject_cast(sender()); @@ -3254,8 +3249,6 @@ void MainWindow::saveCategories() refreshFilters(); } - - void MainWindow::savePrimaryCategory() { QMenu *menu = qobject_cast(sender()); @@ -3280,7 +3273,6 @@ void MainWindow::savePrimaryCategory() } } - void MainWindow::checkModsForUpdates() { statusBar()->show(); @@ -3298,6 +3290,45 @@ void MainWindow::checkModsForUpdates() } } +void MainWindow::changeVersioningScheme() { + if (QMessageBox::question(this, tr("Continue?"), + tr("This will try to change the versioning scheme so that the newest version is interpreted as an update to " + "the installed version."), + QMessageBox::Yes | QMessageBox::Cancel) == QMessageBox::Yes) { + + ModInfo::Ptr info = ModInfo::getByIndex(m_ContextRow); + + bool success = false; + + static VersionInfo::VersionScheme schemes[] = { VersionInfo::SCHEME_REGULAR, VersionInfo::SCHEME_DECIMALMARK, VersionInfo::SCHEME_NUMBERSANDLETTERS }; + + for (int i = 0; i < sizeof(schemes) / sizeof(VersionInfo::VersionScheme) && !success; ++i) { + VersionInfo verOld(info->getVersion().canonicalString(), schemes[i]); + VersionInfo verNew(info->getNewestVersion().canonicalString(), schemes[i]); + if (verOld < verNew) { + info->setVersion(verOld); + info->setNewestVersion(verNew); + success = true; + } + } + if (!success) { + QMessageBox::information(this, tr("Sorry"), + tr("I don't know a versioning scheme where %1 is newer than %2.").arg(info->getNewestVersion().canonicalString()).arg(info->getVersion().canonicalString()), + QMessageBox::Ok); + } + } +} + +void MainWindow::ignoreUpdate() { + ModInfo::Ptr info = ModInfo::getByIndex(m_ContextRow); + info->ignoreUpdate(true); +} + +void MainWindow::unignoreUpdate() +{ + ModInfo::Ptr info = ModInfo::getByIndex(m_ContextRow); + info->ignoreUpdate(false); +} void MainWindow::addPrimaryCategoryCandidates(QMenu *primaryCategoryMenu, ModInfo::Ptr info) { @@ -3319,7 +3350,6 @@ void MainWindow::addPrimaryCategoryCandidates(QMenu *primaryCategoryMenu, ModInf } } - void MainWindow::addPrimaryCategoryCandidates() { QMenu *menu = qobject_cast(sender()); @@ -3333,7 +3363,6 @@ void MainWindow::addPrimaryCategoryCandidates() addPrimaryCategoryCandidates(menu, modInfo); } - void MainWindow::enableVisibleMods() { if (QMessageBox::question(NULL, tr("Confirm"), tr("Really enable all visible mods?"), @@ -3342,7 +3371,6 @@ void MainWindow::enableVisibleMods() } } - void MainWindow::disableVisibleMods() { if (QMessageBox::question(NULL, tr("Confirm"), tr("Really disable all visible mods?"), @@ -3351,7 +3379,6 @@ void MainWindow::disableVisibleMods() } } - void MainWindow::exportModListCSV() { SelectionDialog selection(tr("Choose what to export")); @@ -3406,7 +3433,6 @@ void MainWindow::exportModListCSV() } } - void addMenuAsPushButton(QMenu *menu, QMenu *subMenu) { QPushButton *pushBtn = new QPushButton(subMenu->title()); @@ -3416,13 +3442,13 @@ void addMenuAsPushButton(QMenu *menu, QMenu *subMenu) menu->addAction(action); } - void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) { try { QTreeView *modList = findChild("modList"); - m_ContextRow = mapToModel(&m_ModList, modList->indexAt(pos)).row(); + QModelIndex index = mapToModel(&m_ModList, modList->indexAt(pos)); + m_ContextRow = index.row(); QMenu menu; @@ -3461,6 +3487,17 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) connect(primaryCategoryMenu, SIGNAL(aboutToHide()), this, SLOT(savePrimaryCategory())); addMenuAsPushButton(&menu, primaryCategoryMenu); + menu.addSeparator(); + if (info->downgradeAvailable()) { + menu.addAction(tr("Change versioning scheme"), this, SLOT(changeVersioningScheme())); + } + if (info->updateIgnored()) { + menu.addAction(tr("Un-ignore update"), this, SLOT(unignoreUpdate())); + } else { + menu.addAction(tr("Ignore update"), this, SLOT(ignoreUpdate())); + } + menu.addSeparator(); + menu.addAction(tr("Rename Mod..."), this, SLOT(renameMod_clicked())); menu.addAction(tr("Remove Mod..."), this, SLOT(removeMod_clicked())); menu.addAction(tr("Reinstall Mod"), this, SLOT(reinstallMod_clicked())); @@ -4207,9 +4244,9 @@ void MainWindow::on_actionEndorseMO_triggered() void MainWindow::updateDownloadListDelegate() { if (ui->compactBox->isChecked()) { - ui->downloadView->setItemDelegate(new DownloadListWidgetCompactDelegate(&m_DownloadManager, ui->downloadView)); + ui->downloadView->setItemDelegate(new DownloadListWidgetCompactDelegate(&m_DownloadManager, ui->downloadView, ui->downloadView)); } else { - ui->downloadView->setItemDelegate(new DownloadListWidgetDelegate(&m_DownloadManager, ui->downloadView)); + ui->downloadView->setItemDelegate(new DownloadListWidgetDelegate(&m_DownloadManager, ui->downloadView, ui->downloadView)); } DownloadListSortProxy *sortProxy = new DownloadListSortProxy(&m_DownloadManager, ui->downloadView); @@ -4267,7 +4304,7 @@ void MainWindow::nxmUpdatesAvailable(const std::vector &modIDs, QVariant us } else { std::vector info = ModInfo::getByModID(result["id"].toInt()); for (auto iter = info.begin(); iter != info.end(); ++iter) { - (*iter)->setNewestVersion(VersionInfo(result["version"].toString())); + (*iter)->setNewestVersion(result["version"].toString()); (*iter)->setNexusDescription(result["description"].toString()); if (NexusInterface::instance()->getAccessManager()->loggedIn() && result.contains("voted_by_user")) { diff --git a/src/mainwindow.h b/src/mainwindow.h index 80dca172..dfec5f49 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -466,6 +466,10 @@ private slots: void removeFromToolbar(); void overwriteClosed(int); + void changeVersioningScheme(); + void ignoreUpdate(); + void unignoreUpdate(); + private slots: // ui slots // actions void on_actionAdd_Profile_triggered(); diff --git a/src/modinfo.cpp b/src/modinfo.cpp index 7632a86e..1479f7b4 100644 --- a/src/modinfo.cpp +++ b/src/modinfo.cpp @@ -303,10 +303,11 @@ ModInfoRegular::ModInfoRegular(const QDir &path, DirectoryEntry **directoryStruc QString metaFileName = path.absoluteFilePath("meta.ini"); QSettings metaFile(metaFileName, QSettings::IniFormat); - m_Notes = metaFile.value("notes", "").toString(); - m_NexusID = metaFile.value("modid", -1).toInt(); + m_Notes = metaFile.value("notes", "").toString(); + m_NexusID = metaFile.value("modid", -1).toInt(); m_Version.parse(metaFile.value("version", "").toString()); - m_NewestVersion = metaFile.value("newestVersion", "").toString(); + m_NewestVersion = metaFile.value("newestVersion", "").toString(); + m_IgnoredVersion = metaFile.value("ignoredVersion", "").toString(); m_InstallationFile = metaFile.value("installationFile", "").toString(); m_NexusDescription = metaFile.value("nexusDescription", "").toString(); m_LastNexusQuery = QDateTime::fromString(metaFile.value("lastNexusQuery", "").toString(), Qt::ISODate); @@ -377,6 +378,7 @@ void ModInfoRegular::saveMeta() temp.erase(m_PrimaryCategory); metaFile.setValue("category", QString("%1").arg(m_PrimaryCategory) + "," + SetJoin(temp, ",")); metaFile.setValue("newestVersion", m_NewestVersion.canonicalString()); + metaFile.setValue("ignoredVersion", m_IgnoredVersion.canonicalString()); metaFile.setValue("version", m_Version.canonicalString()); metaFile.setValue("modid", m_NexusID); metaFile.setValue("notes", m_Notes); @@ -396,10 +398,22 @@ void ModInfoRegular::saveMeta() bool ModInfoRegular::updateAvailable() const { + if (m_IgnoredVersion.isValid() && (m_IgnoredVersion == m_NewestVersion)) { + return false; + } return m_NewestVersion.isValid() && (m_Version < m_NewestVersion); } +bool ModInfoRegular::downgradeAvailable() const +{ + if (m_IgnoredVersion.isValid() && (m_IgnoredVersion == m_NewestVersion)) { + return false; + } + return m_NewestVersion.isValid() && (m_NewestVersion < m_Version); +} + + void ModInfoRegular::nxmDescriptionAvailable(int, QVariant, QVariant resultData) { QVariantMap result = resultData.toMap(); @@ -576,6 +590,16 @@ QString ModInfoRegular::absolutePath() const return m_Path; } +void ModInfoRegular::ignoreUpdate(bool ignore) +{ + if (ignore) { + m_IgnoredVersion = m_NewestVersion; + } else { + m_IgnoredVersion.clear(); + } + m_MetaInfoChanged = true; +} + std::vector ModInfoRegular::getFlags() const { diff --git a/src/modinfo.h b/src/modinfo.h index 7e217de7..3b83d207 100644 --- a/src/modinfo.h +++ b/src/modinfo.h @@ -192,6 +192,22 @@ public: **/ virtual bool updateAvailable() const = 0; + /** + * @return true if the update currently available is ignored + */ + virtual bool updateIgnored() const = 0; + + /** + * @brief test if the "newest" version of the mod is older than the installed version + * + * test if there is a newer version of the mod. This does NOT cause + * information to be retrieved from the nexus, it will only test version information already + * available locally. Use checkAllForUpdate() to update this version information + * + * @return true if the newest version is older than the installed one + **/ + virtual bool downgradeAvailable() const = 0; + /** * @brief request an update of nexus description for this mod. * @@ -309,6 +325,11 @@ public: **/ virtual MOBase::VersionInfo getNewestVersion() const = 0; + /** + * @brief ignore the newest version for updates + */ + virtual void ignoreUpdate(bool ignore) = 0; + /** * @brief getter for the nexus mod id * @@ -494,6 +515,22 @@ public: **/ bool updateAvailable() const; + /** + * @return true if the current update is being ignored + */ + virtual bool updateIgnored() const { return m_IgnoredVersion == m_NewestVersion; } + + /** + * @brief test if there is a newer version of the mod + * + * test if there is a newer version of the mod. This does NOT cause + * information to be retrieved from the nexus, it will only test version information already + * available locally. Use checkAllForUpdate() to update this version information + * + * @return true if there is a newer version + **/ + bool downgradeAvailable() const; + /** * @brief request an update of nexus description for this mod. * @@ -620,6 +657,11 @@ public: **/ MOBase::VersionInfo getNewestVersion() const { return m_NewestVersion; } + /** + * @brief ignore the newest version for updates + */ + void ignoreUpdate(bool ignore); + /** * @brief getter for the installation file * @@ -746,6 +788,7 @@ private: bool m_MetaInfoChanged; MOBase::VersionInfo m_NewestVersion; + MOBase::VersionInfo m_IgnoredVersion; EEndorsedState m_EndorsedState; @@ -767,10 +810,13 @@ class ModInfoBackup : public ModInfoRegular public: virtual bool updateAvailable() const { return false; } + virtual bool updateIgnored() const { return false; } + virtual bool downgradeAvailable() const { return false; } virtual bool updateNXMInfo() { return false; } virtual void setNexusID(int) {} virtual void endorse(bool) {} virtual int getFixedPriority() const { return -1; } + virtual void ignoreUpdate(bool) {} virtual bool canBeUpdated() const { return false; } virtual bool canBeEnabled() const { return false; } virtual std::vector getIniTweaks() const { return std::vector(); } @@ -798,12 +844,15 @@ class ModInfoOverwrite : public ModInfo public: virtual bool updateAvailable() const { return false; } + virtual bool updateIgnored() const { return false; } + virtual bool downgradeAvailable() const { return false; } virtual bool updateNXMInfo() { return false; } virtual void setCategory(int, bool) {} virtual bool setName(const QString&) { return false; } virtual void setNotes(const QString&) {} virtual void setNexusID(int) {} virtual void setNewestVersion(const MOBase::VersionInfo&) {} + virtual void ignoreUpdate(bool) {} virtual void setNexusDescription(const QString&) {} virtual void setIsEndorsed(bool) {} virtual void setNeverEndorse() {} diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 75e7e499..b4ae57ea 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -76,7 +76,6 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo m_UTF8Codec = QTextCodec::codecForName("utf-8"); QListWidget *textFileList = findChild("textFileList"); - QListWidget *iniFileList = findChild("iniFileList"); QListWidget *iniTweaksList = findChild("iniTweaksList"); QListWidget *activeESPList = findChild("activeESPList"); QListWidget *inactiveESPList = findChild("inactiveESPList"); @@ -707,7 +706,7 @@ QString ModInfoDialog::getFileCategory(int categoryID) void ModInfoDialog::updateVersionColor() { // QPalette versionColor; - if (m_ModInfo->getVersion() < m_ModInfo->getNewestVersion()) { + 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())); @@ -758,13 +757,7 @@ void ModInfoDialog::modDetailsUpdated(bool success) ui->descriptionView->setHtml(tr("(description incomplete, please visit nexus)")); } - QString version = m_ModInfo->getNewestVersion().canonicalString(); - - if (!version.isEmpty()) { - m_ModInfo->setNewestVersion(version); - - updateVersionColor(); - } + updateVersionColor(); } } diff --git a/src/modlist.cpp b/src/modlist.cpp index b5a7182e..7367b2ae 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -143,13 +143,16 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const } else if (column == COL_NAME) { return modInfo->name(); } else if (column == COL_VERSION) { - QString version = modInfo->getVersion().canonicalString(); - if (version.isEmpty() && modInfo->canBeUpdated()) { - version = "?"; - } else if (version[0] == 'd') { - version.remove(0, 1); + VersionInfo verInfo = modInfo->getVersion(); + if (role == Qt::EditRole) { + return verInfo.canonicalString(); + } else { + QString version = verInfo.displayString(); + if (version.isEmpty() && modInfo->canBeUpdated()) { + version = "?"; + } + return version; } - return version; } else if (column == COL_PRIORITY) { int priority = modInfo->getFixedPriority(); if (priority != INT_MIN) { @@ -241,17 +244,18 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const result.setItalic(true); } } else if (column == COL_VERSION) { - if (modInfo->updateAvailable()) { + if (modInfo->updateAvailable() || modInfo->downgradeAvailable()) { result.setWeight(QFont::Bold); } } return result; } else if (role == Qt::DecorationRole) { if (column == COL_VERSION) { - if (modInfo->updateAvailable() && - modInfo->getNewestVersion().isValid()) { + if (modInfo->updateAvailable()) { return QIcon(":/MO/gui/update_available"); - } else if (modInfo->getVersion().isVersionDate()) { + } else if (modInfo->downgradeAvailable()) { + return QIcon(":/MO/gui/warning"); + } else if (modInfo->getVersion().scheme() == VersionInfo::SCHEME_DATE) { return QIcon(":/MO/gui/version_date"); } } @@ -264,7 +268,7 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const } else if (column == COL_VERSION) { if (!modInfo->getNewestVersion().isValid()) { return QVariant(); - } else if (modInfo->updateAvailable()) { + } else if (modInfo->updateAvailable() || modInfo->downgradeAvailable()) { return QBrush(Qt::red); } else { return QBrush(Qt::darkGreen); @@ -291,7 +295,13 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const return QString(); } } else if (column == COL_VERSION) { - return tr("installed version: %1, newest version: %2").arg(modInfo->getVersion().canonicalString()).arg(modInfo->getNewestVersion().canonicalString()); + QString text = tr("installed version: %1, newest version: %2").arg(modInfo->getVersion().displayString()).arg(modInfo->getNewestVersion().displayString()); + if (modInfo->downgradeAvailable()) { + text += "
" + tr("The newest version on Nexus seems to be older than the one you have installed. This could either mean the version you have has been withdrawn " + "(i.e. due to a bug) or the author uses a non-standard versioning scheme and that newest version is actually newer. " + "Either way you may want to \"upgrade\"."); + } + return text; } else if (column == COL_CATEGORY) { const std::set &categories = modInfo->getCategories(); std::wostringstream categoryString; diff --git a/src/modlistsortproxy.cpp b/src/modlistsortproxy.cpp index 53c03c6a..c7c3ab28 100644 --- a/src/modlistsortproxy.cpp +++ b/src/modlistsortproxy.cpp @@ -211,7 +211,7 @@ bool ModListSortProxy::filterMatches(ModInfo::Ptr info, bool enabled) const if (enabled) return false; } break; case CategoryFactory::CATEGORY_SPECIAL_UPDATEAVAILABLE: { - if (!info->updateAvailable()) return false; + if (!info->updateAvailable() && !info->downgradeAvailable()) return false; } break; case CategoryFactory::CATEGORY_SPECIAL_NOCATEGORY: { if (info->getCategories().size() > 0) return false; diff --git a/src/modlistview.cpp b/src/modlistview.cpp index 44228d5b..1a4f6266 100644 --- a/src/modlistview.cpp +++ b/src/modlistview.cpp @@ -4,7 +4,7 @@ #include -class ModListViewStyle: public QProxyStyle{ +class ModListViewStyle: public QProxyStyle { public: ModListViewStyle(QStyle *style, int indentation); @@ -37,7 +37,7 @@ void ModListViewStyle::drawPrimitive(PrimitiveElement element, const QStyleOptio ModListView::ModListView(QWidget *parent) : QTreeView(parent) { - setStyle(new ModListViewStyle(style(), indentation())); +// setStyle(new ModListViewStyle(style(), indentation())); } void ModListView::dragEnterEvent(QDragEnterEvent *event) diff --git a/src/nexusinterface.cpp b/src/nexusinterface.cpp index cd78ca4c..bee0bb44 100644 --- a/src/nexusinterface.cpp +++ b/src/nexusinterface.cpp @@ -68,6 +68,7 @@ void NexusBridge::nxmDescriptionAvailable(int modID, QVariant userData, QVariant std::set::iterator iter = m_RequestIDs.find(requestID); if (iter != m_RequestIDs.end()) { m_RequestIDs.erase(iter); + emit descriptionAvailable(modID, userData, resultData); } } @@ -142,6 +143,7 @@ QAtomicInt NexusInterface::NXMRequestInfo::s_NextID(0); NexusInterface::NexusInterface() : m_NMMVersion() { + atexit(&cleanup); m_AccessManager = new NXMAccessManager(this); m_DiskCache = new QNetworkDiskCache(this); @@ -149,6 +151,13 @@ NexusInterface::NexusInterface() } +void NexusInterface::cleanup() +{ + delete NexusInterface::s_Instance; + NexusInterface::s_Instance = NULL; +} + + NXMAccessManager *NexusInterface::getAccessManager() { return m_AccessManager; @@ -405,6 +414,7 @@ void NexusInterface::downloadRequestedNXM(const QString &url) emit requestNXMDownload(url); } +#include void NexusInterface::requestFinished(std::list::iterator iter) { diff --git a/src/nexusinterface.h b/src/nexusinterface.h index 0c304a71..0b53f9d0 100644 --- a/src/nexusinterface.h +++ b/src/nexusinterface.h @@ -294,6 +294,7 @@ private: NexusInterface(); void nextRequest(); void requestFinished(std::list::iterator iter); + static void cleanup(); private: diff --git a/src/nxmaccessmanager.cpp b/src/nxmaccessmanager.cpp index d83ffa61..a05b8a6c 100644 --- a/src/nxmaccessmanager.cpp +++ b/src/nxmaccessmanager.cpp @@ -44,6 +44,14 @@ NXMAccessManager::NXMAccessManager(QObject *parent) { } +NXMAccessManager::~NXMAccessManager() +{ + if (m_LoginReply != NULL) { + m_LoginReply->deleteLater(); + m_LoginReply = NULL; + } +} + QNetworkReply *NXMAccessManager::createRequest( QNetworkAccessManager::Operation operation, const QNetworkRequest &request, diff --git a/src/nxmaccessmanager.h b/src/nxmaccessmanager.h index b0c43978..1d8d36ab 100644 --- a/src/nxmaccessmanager.h +++ b/src/nxmaccessmanager.h @@ -37,6 +37,8 @@ public: explicit NXMAccessManager(QObject *parent); + ~NXMAccessManager(); + bool loggedIn() const; void login(const QString &username, const QString &password); diff --git a/src/organizer.pro b/src/organizer.pro index e89b6e99..a9353361 100644 --- a/src/organizer.pro +++ b/src/organizer.pro @@ -183,7 +183,6 @@ INCLUDEPATH += ../shared ../archive ../uibase ../bsatk ../esptk "$(BOOSTPATH)" LIBS += -L"$(BOOSTPATH)/stage/lib" - CONFIG(debug, debug|release) { OUTDIR = $$OUT_PWD/debug DSTDIR = $$PWD/../../outputd @@ -294,6 +293,12 @@ OTHER_FILES += \ INCLUDEPATH += "$(ZLIBPATH)" "$(ZLIBPATH)/build" "$(BOOSTPATH)" LIBS += -L"$(ZLIBPATH)/build" -lzlibstatic + +# leak detection with vld +#INCLUDEPATH += "E:/Visual Leak Detector/include" +#LIBS += -L"E:/Visual Leak Detector/lib/Win32" +#DEFINES += LEAK_CHECK_WITH_VLD + #SOURCES += modeltest.cpp #HEADERS += modeltest.h #DEFINES += TEST_MODELS diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index 39cca6af..e730d3f4 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -90,6 +90,10 @@ PluginList::PluginList(QObject *parent) } +PluginList::~PluginList() +{ +} + QString PluginList::getColumnName(int column) { diff --git a/src/pluginlist.h b/src/pluginlist.h index 3442a9f7..60c9be95 100644 --- a/src/pluginlist.h +++ b/src/pluginlist.h @@ -53,6 +53,8 @@ public: **/ PluginList(QObject *parent = NULL); + ~PluginList(); + /** * @brief does a complete refresh of the list * diff --git a/src/profile.cpp b/src/profile.cpp index 07f7010e..3da12d7d 100644 --- a/src/profile.cpp +++ b/src/profile.cpp @@ -185,7 +185,7 @@ void Profile::writeModlistNow(bool onlyOnTimer) const QString fileName = getModlistFileName(); if (QFile::exists(fileName)) { - shellDelete(QStringList(fileName)); + shellDelete(QStringList(fileName), false, QApplication::activeModalWidget()); } if (!file.copy(fileName)) { @@ -238,6 +238,7 @@ void Profile::createTweakedIniFile() if (error) { reportError(tr("failed to create tweaked ini: %1").arg(getCurrentErrorStringA().c_str())); } + qDebug("%s saved", qPrintable(QDir::toNativeSeparators(tweakedIni))); } diff --git a/src/selfupdater.cpp b/src/selfupdater.cpp index 421fbeab..bdace814 100644 --- a/src/selfupdater.cpp +++ b/src/selfupdater.cpp @@ -126,7 +126,6 @@ void SelfUpdater::startUpdate() void SelfUpdater::download(const QString &downloadLink, const QString &fileName) { - qDebug("download: %s", downloadLink.toUtf8().constData()); QNetworkAccessManager *accessManager = m_Interface->getAccessManager(); QUrl dlUrl(downloadLink); QNetworkRequest request(dlUrl); @@ -324,7 +323,6 @@ void SelfUpdater::nxmDescriptionAvailable(int, QVariant, QVariant resultData, in if (m_NewestVersion.isEmpty()) { QTimer::singleShot(5000, this, SLOT(testForUpdate())); } - VersionInfo currentVersion(m_MOVersion); VersionInfo newestVersion(m_NewestVersion); diff --git a/src/shared/gameinfo.cpp b/src/shared/gameinfo.cpp index 6b53450d..f74e52b4 100644 --- a/src/shared/gameinfo.cpp +++ b/src/shared/gameinfo.cpp @@ -40,6 +40,13 @@ GameInfo* GameInfo::s_Instance = NULL; GameInfo::GameInfo(const std::wstring &omoDirectory, const std::wstring &gameDirectory) : m_GameDirectory(gameDirectory), m_OrganizerDirectory(omoDirectory) { + atexit(&cleanup); +} + + +void GameInfo::cleanup() { + delete GameInfo::s_Instance; + GameInfo::s_Instance = NULL; } diff --git a/src/shared/gameinfo.h b/src/shared/gameinfo.h index 3e022ef4..14f52f05 100644 --- a/src/shared/gameinfo.h +++ b/src/shared/gameinfo.h @@ -176,9 +176,11 @@ private: static bool identifyGame(const std::wstring &omoDirectory, const std::wstring &searchPath); std::wstring getSpecialPath(LPCWSTR name) const; + static void cleanup(); + private: - static GameInfo* s_Instance; + static GameInfo *s_Instance; std::wstring m_MyGamesDirectory; diff --git a/src/shared/leaktrace.cpp b/src/shared/leaktrace.cpp index 83ed4fd0..c3721557 100644 --- a/src/shared/leaktrace.cpp +++ b/src/shared/leaktrace.cpp @@ -114,6 +114,7 @@ static struct __TraceData { } std::map m_Traces; + } __trace; diff --git a/src/shared/shared.pro b/src/shared/shared.pro index ab0bd8a0..992fd7f2 100644 --- a/src/shared/shared.pro +++ b/src/shared/shared.pro @@ -13,9 +13,17 @@ CONFIG += staticlib INCLUDEPATH += ../bsatk "$(BOOSTPATH)" + +# only for custom leak detection +#DEFINES += TRACE_LEAKS +#LIBS += -lDbgHelp + + CONFIG(debug, debug|release) { - LIBS += -L$$OUT_PWD/../bsatk/debug + LIBS += -L$$OUT_PWD/../bsatk/debug LIBS += -lDbgHelp + QMAKE_CXXFLAGS_DEBUG -= -Zi + QMAKE_CXXFLAGS += -Z7 } else { LIBS += -L$$OUT_PWD/../bsatk/release } diff --git a/src/spawn.cpp b/src/spawn.cpp index ab43b687..5639a78c 100644 --- a/src/spawn.cpp +++ b/src/spawn.cpp @@ -156,7 +156,7 @@ HANDLE startBinary(const QFileInfo &binary, const QString &arguments, const QStr return processHandle; } - +/* ExitProxy *ExitProxy::s_Instance = NULL; ExitProxy *ExitProxy::instance() @@ -170,4 +170,4 @@ ExitProxy *ExitProxy::instance() void ExitProxy::emitExit() { emit exit(); -} +}*/ diff --git a/src/spawn.h b/src/spawn.h index e0d1f958..48320fea 100644 --- a/src/spawn.h +++ b/src/spawn.h @@ -31,7 +31,7 @@ along with Mod Organizer. If not, see . * @brief a dirty little trick so we can issue a clean restart from startBinary * @note unused */ -class ExitProxy : public QObject { +/*class ExitProxy : public QObject { Q_OBJECT public: static ExitProxy *instance(); @@ -42,7 +42,7 @@ private: ExitProxy() {} private: static ExitProxy *s_Instance; -}; +};*/ /** diff --git a/src/splash.png b/src/splash.png index 45ace8f8..0137bf72 100644 Binary files a/src/splash.png and b/src/splash.png differ -- cgit v1.3.1 From 54c7131a5e2fa282369e25344ac190d51676c383 Mon Sep 17 00:00:00 2001 From: Tannin Date: Thu, 10 Oct 2013 19:32:01 +0200 Subject: - new toggle to display hidden downloads - hidden downloads can be un-hidden - the installation manager now more thoroughly cleans up the temporary directory after installation - added SkyrimLauncher.exe to the list of auto-detected executables - bugfix: shutting down MO while downloads where active in some occasions didn't work - bugfix: when canceling the only active download the taskbar icon didn't return to normal --- src/downloadlistwidget.cpp | 20 ++++++++--- src/downloadlistwidget.h | 2 ++ src/downloadlistwidgetcompact.cpp | 20 ++++++++--- src/downloadlistwidgetcompact.h | 2 ++ src/downloadmanager.cpp | 58 ++++++++++++++++++++++++++----- src/downloadmanager.h | 24 ++++++++++++- src/installationmanager.cpp | 72 +++++++++++++-------------------------- src/installationmanager.h | 5 +-- src/main.cpp | 5 ++- src/mainwindow.cpp | 6 ++++ src/mainwindow.h | 1 + src/mainwindow.ui | 9 ++++- src/moapplication.cpp | 6 +++- src/moapplication.h | 60 ++++++++++++++++---------------- src/shared/skyriminfo.cpp | 1 + src/version.rc | 4 +-- 16 files changed, 189 insertions(+), 106 deletions(-) (limited to 'src/mainwindow.h') diff --git a/src/downloadlistwidget.cpp b/src/downloadlistwidget.cpp index b657ca69..b4d40799 100644 --- a/src/downloadlistwidget.cpp +++ b/src/downloadlistwidget.cpp @@ -208,6 +208,11 @@ void DownloadListWidgetDelegate::issueRemoveFromView() emit removeDownload(m_ContextRow, false); } +void DownloadListWidgetDelegate::issueRestoreToView() +{ + emit restoreDownload(m_ContextRow); +} + void DownloadListWidgetDelegate::issueCancel() { emit cancelDownload(m_ContextRow); @@ -277,13 +282,18 @@ bool DownloadListWidgetDelegate::editorEvent(QEvent *event, QAbstractItemModel * QMenu menu(m_View); m_ContextRow = qobject_cast(model)->mapToSource(index).row(); DownloadManager::DownloadState state = m_Manager->getState(m_ContextRow); + bool hidden = m_Manager->isHidden(m_ContextRow); if (state >= DownloadManager::STATE_READY) { menu.addAction(tr("Install"), this, SLOT(issueInstall())); if (m_Manager->isInfoIncomplete(m_ContextRow)) { menu.addAction(tr("Query Info"), this, SLOT(issueQueryInfo())); } menu.addAction(tr("Delete"), this, SLOT(issueDelete())); - menu.addAction(tr("Remove from View"), this, SLOT(issueRemoveFromView())); + if (hidden) { + menu.addAction(tr("Un-Hide"), this, SLOT(issueRestoreToView())); + } else { + menu.addAction(tr("Remove from View"), this, SLOT(issueRemoveFromView())); + } } else if (state == DownloadManager::STATE_DOWNLOADING){ menu.addAction(tr("Cancel"), this, SLOT(issueCancel())); menu.addAction(tr("Pause"), this, SLOT(issuePause())); @@ -295,9 +305,11 @@ bool DownloadListWidgetDelegate::editorEvent(QEvent *event, QAbstractItemModel * menu.addSeparator(); menu.addAction(tr("Delete Installed..."), this, SLOT(issueDeleteCompleted())); menu.addAction(tr("Delete All..."), this, SLOT(issueDeleteAll())); - menu.addSeparator(); - menu.addAction(tr("Remove Installed..."), this, SLOT(issueRemoveFromViewCompleted())); - menu.addAction(tr("Remove All..."), this, SLOT(issueRemoveFromViewAll())); + if (!hidden) { + menu.addSeparator(); + menu.addAction(tr("Remove Installed..."), this, SLOT(issueRemoveFromViewCompleted())); + menu.addAction(tr("Remove All..."), this, SLOT(issueRemoveFromViewAll())); + } menu.exec(mouseEvent->globalPos()); event->accept(); diff --git a/src/downloadlistwidget.h b/src/downloadlistwidget.h index c80200fb..f5bfdbaa 100644 --- a/src/downloadlistwidget.h +++ b/src/downloadlistwidget.h @@ -65,6 +65,7 @@ signals: void installDownload(int index); void queryInfo(int index); void removeDownload(int index, bool deleteFile); + void restoreDownload(int index); void cancelDownload(int index); void pauseDownload(int index); void resumeDownload(int index); @@ -83,6 +84,7 @@ private slots: void issueInstall(); void issueDelete(); void issueRemoveFromView(); + void issueRestoreToView(); void issueCancel(); void issuePause(); void issueResume(); diff --git a/src/downloadlistwidgetcompact.cpp b/src/downloadlistwidgetcompact.cpp index 8dd6e275..d2a71dd5 100644 --- a/src/downloadlistwidgetcompact.cpp +++ b/src/downloadlistwidgetcompact.cpp @@ -195,6 +195,11 @@ void DownloadListWidgetCompactDelegate::issueRemoveFromView() emit removeDownload(m_ContextIndex.row(), false); } +void DownloadListWidgetCompactDelegate::issueRestoreToView() +{ + emit restoreDownload(m_ContextIndex.row()); +} + void DownloadListWidgetCompactDelegate::issueCancel() { emit cancelDownload(m_ContextIndex.row()); @@ -265,13 +270,18 @@ bool DownloadListWidgetCompactDelegate::editorEvent(QEvent *event, QAbstractItem QMenu menu; m_ContextIndex = qobject_cast(model)->mapToSource(index); DownloadManager::DownloadState state = m_Manager->getState(m_ContextIndex.row()); + bool hidden = m_Manager->isHidden(m_ContextIndex.row()); if (state >= DownloadManager::STATE_READY) { menu.addAction(tr("Install"), this, SLOT(issueInstall())); if (m_Manager->isInfoIncomplete(m_ContextIndex.row())) { menu.addAction(tr("Query Info"), this, SLOT(issueQueryInfo())); } menu.addAction(tr("Delete"), this, SLOT(issueDelete())); - menu.addAction(tr("Remove from View"), this, SLOT(issueRemoveFromView())); + if (hidden) { + menu.addAction(tr("Un-Hide"), this, SLOT(issueRestoreToView())); + } else { + menu.addAction(tr("Remove from View"), this, SLOT(issueRemoveFromView())); + } } else if (state == DownloadManager::STATE_DOWNLOADING){ menu.addAction(tr("Cancel"), this, SLOT(issueCancel())); menu.addAction(tr("Pause"), this, SLOT(issuePause())); @@ -283,9 +293,11 @@ bool DownloadListWidgetCompactDelegate::editorEvent(QEvent *event, QAbstractItem menu.addSeparator(); menu.addAction(tr("Delete Installed..."), this, SLOT(issueDeleteCompleted())); menu.addAction(tr("Delete All..."), this, SLOT(issueDeleteAll())); - menu.addSeparator(); - menu.addAction(tr("Remove Installed..."), this, SLOT(issueRemoveFromViewCompleted())); - menu.addAction(tr("Remove All..."), this, SLOT(issueRemoveFromViewAll())); + if (!hidden) { + menu.addSeparator(); + menu.addAction(tr("Remove Installed..."), this, SLOT(issueRemoveFromViewCompleted())); + menu.addAction(tr("Remove All..."), this, SLOT(issueRemoveFromViewAll())); + } menu.exec(mouseEvent->globalPos()); event->accept(); diff --git a/src/downloadlistwidgetcompact.h b/src/downloadlistwidgetcompact.h index 78f51840..05d00b9d 100644 --- a/src/downloadlistwidgetcompact.h +++ b/src/downloadlistwidgetcompact.h @@ -65,6 +65,7 @@ signals: void installDownload(int index); void queryInfo(int index); void removeDownload(int index, bool deleteFile); + void restoreDownload(int index); void cancelDownload(int index); void pauseDownload(int index); void resumeDownload(int index); @@ -83,6 +84,7 @@ private slots: void issueInstall(); void issueDelete(); void issueRemoveFromView(); + void issueRestoreToView(); void issueCancel(); void issuePause(); void issueResume(); diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index b965d598..eca5600a 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -69,14 +69,16 @@ DownloadManager::DownloadInfo *DownloadManager::DownloadInfo::createNew(const Ne return info; } -DownloadManager::DownloadInfo *DownloadManager::DownloadInfo::createFromMeta(const QString &filePath) +DownloadManager::DownloadInfo *DownloadManager::DownloadInfo::createFromMeta(const QString &filePath, bool showHidden) { DownloadInfo *info = new DownloadInfo; QString metaFileName = filePath + ".meta"; QSettings metaFile(metaFileName, QSettings::IniFormat); - if (metaFile.value("removed", false).toBool()) { + if (!showHidden && metaFile.value("removed", false).toBool()) { return NULL; + } else { + info->m_Hidden = metaFile.value("removed", false).toBool(); } QString fileName = QFileInfo(filePath).fileName(); @@ -151,7 +153,7 @@ QString DownloadManager::DownloadInfo::currentURL() DownloadManager::DownloadManager(NexusInterface *nexusInterface, QObject *parent) - : IDownloadManager(parent), m_NexusInterface(nexusInterface), m_DirWatcher() + : IDownloadManager(parent), m_NexusInterface(nexusInterface), m_DirWatcher(), m_ShowHidden(false) { connect(&m_DirWatcher, SIGNAL(directoryChanged(QString)), this, SLOT(directoryChanged(QString))); } @@ -188,13 +190,15 @@ void DownloadManager::pauseAll() ::Sleep(100); bool done = false; + QTime startTime = QTime::currentTime(); // further loops: busy waiting for all downloads to complete. This could be neater... - while (!done) { + while (!done && (startTime.secsTo(QTime::currentTime()) < 5)) { QCoreApplication::processEvents(); done = true; foreach (DownloadInfo *info, m_ActiveDownloads) { if ((info->m_State < STATE_CANCELED) || - (info->m_State != STATE_FETCHINGFILEINFO) || (info->m_State != STATE_FETCHINGMODINFO)) { + (info->m_State == STATE_FETCHINGFILEINFO) || + (info->m_State == STATE_FETCHINGMODINFO)) { done = false; break; } @@ -230,6 +234,12 @@ void DownloadManager::setSupportedExtensions(const QStringList &extensions) refreshList(); } +void DownloadManager::setShowHidden(bool showHidden) +{ + m_ShowHidden = showHidden; + refreshList(); +} + void DownloadManager::refreshList() { // remove finished downloads @@ -267,7 +277,7 @@ void DownloadManager::refreshList() QString fileName = QDir::fromNativeSeparators(m_OutputDirectory) + "/" + file; - DownloadInfo *info = DownloadInfo::createFromMeta(fileName); + DownloadInfo *info = DownloadInfo::createFromMeta(fileName, m_ShowHidden); if (info != NULL) { m_ActiveDownloads.push_front(info); } @@ -437,6 +447,22 @@ void DownloadManager::refreshAlphabeticalTranslation() } +void DownloadManager::restoreDownload(int index) +{ + if ((index < 0) || (index >= m_ActiveDownloads.size())) { + throw MyException(tr("invalid index")); + } + + DownloadInfo *download = m_ActiveDownloads.at(index); + download->m_Hidden = false; + + QString filePath = m_OutputDirectory + "/" + download->m_FileName; + + QSettings metaSettings(filePath.append(".meta"), QSettings::IniFormat); + metaSettings.setValue("removed", false); +} + + void DownloadManager::removeDownload(int index, bool deleteFile) { try { @@ -492,9 +518,13 @@ void DownloadManager::pauseDownload(int index) return; } - if (m_ActiveDownloads.at(index)->m_State == STATE_DOWNLOADING) { - setState(m_ActiveDownloads.at(index), STATE_PAUSING); - qDebug("pausing %d - %s", index, m_ActiveDownloads[index]->m_FileName.toUtf8().constData()); + DownloadInfo *info = m_ActiveDownloads.at(index); + + if (info->m_State == STATE_DOWNLOADING) { + setState(info, STATE_PAUSING); + qDebug("pausing %d - %s", index, info->m_FileName.toUtf8().constData()); + } else if ((info->m_State == STATE_FETCHINGMODINFO) || (info->m_State == STATE_FETCHINGFILEINFO)) { + setState(info, STATE_READY); } } @@ -671,6 +701,14 @@ int DownloadManager::getModID(int index) const return m_ActiveDownloads.at(index)->m_ModID; } +bool DownloadManager::isHidden(int index) const +{ + if ((index < 0) || (index >= m_ActiveDownloads.size())) { + throw MyException(tr("invalid index")); + } + return m_ActiveDownloads.at(index)->m_Hidden; +} + NexusInfo DownloadManager::getNexusInfo(int index) const { @@ -843,6 +881,7 @@ void DownloadManager::createMetaFile(DownloadInfo *info) metaFile.setValue("uninstalled", info->m_State == DownloadManager::STATE_UNINSTALLED); metaFile.setValue("paused", (info->m_State == DownloadManager::STATE_PAUSED) || (info->m_State == DownloadManager::STATE_ERROR)); + metaFile.setValue("removed", info->m_Hidden); // slightly hackish... for (int i = 0; i < m_ActiveDownloads.size(); ++i) { @@ -1110,6 +1149,7 @@ void DownloadManager::downloadFinished() QByteArray data = info->m_Reply->readAll(); info->m_Output.write(data); info->m_Output.close(); + TaskProgressManager::instance().forgetMe(info->m_TaskProgressId); bool error = false; diff --git a/src/downloadmanager.h b/src/downloadmanager.h index 0d49aa35..a42ac073 100644 --- a/src/downloadmanager.h +++ b/src/downloadmanager.h @@ -102,8 +102,10 @@ private: NexusInfo m_NexusInfo; + bool m_Hidden; + static DownloadInfo *createNew(const NexusInfo &nexusInfo, int modID, int fileID, const QStringList &URLs); - static DownloadInfo *createFromMeta(const QString &filePath); + static DownloadInfo *createFromMeta(const QString &filePath, bool showHidden); /** * @brief rename the file @@ -168,6 +170,11 @@ public: */ void setSupportedExtensions(const QStringList &extensions); + /** + * @brief sets whether hidden files are to be shown after all + */ + void setShowHidden(bool showHidden); + /** * @brief download from an already open network connection * @@ -263,6 +270,13 @@ public: **/ int getModID(int index) const; + /** + * @brief determine if the specified file is supposed to be hidden + * @param index index of the file to look up + * @return true if the specified file is supposed to be hidden + */ + bool isHidden(int index) const; + /** * @brief retrieve all nexus info of the download specified by index * @@ -352,6 +366,12 @@ public slots: **/ void removeDownload(int index, bool deleteFile); + /** + * @brief restores the specified download to view (which was previously hidden + * @param index index of the download to restore + */ + void restoreDownload(int index); + /** * @brief cancel the specified download. This will lead to the corresponding file to be deleted * @@ -436,6 +456,8 @@ private: std::map m_DownloadFails; + bool m_ShowHidden; + }; #endif // DOWNLOADMANAGER_H diff --git a/src/installationmanager.cpp b/src/installationmanager.cpp index 87efecf1..6b430eea 100644 --- a/src/installationmanager.cpp +++ b/src/installationmanager.cpp @@ -184,11 +184,7 @@ bool InstallationManager::unpackSingleFile(const QString &fileName) QString InstallationManager::extractFile(const QString &fileName) { if (unpackSingleFile(fileName)) { - QString tempFileName = QDir::tempPath().append("/").append(QFileInfo(fileName).fileName()); - - m_FilesToDelete.insert(tempFileName); - - return tempFileName; + return QDir::tempPath().append("/").append(QFileInfo(fileName).fileName()); } else { return QString(); } @@ -571,49 +567,36 @@ bool InstallationManager::doInstall(GuessedValue &modName, int modID, } -void InstallationManager::openFile(const QString &fileName) +bool InstallationManager::wasCancelled() { - unpackSingleFile(fileName); + return m_CurrentArchive->getLastError() == Archive::ERROR_EXTRACT_CANCELLED; +} - QString tempFileName = QDir::tempPath().append("/").append(QFileInfo(fileName).fileName()); - SHELLEXECUTEINFOW execInfo; - memset(&execInfo, 0, sizeof(SHELLEXECUTEINFOW)); - execInfo.cbSize = sizeof(SHELLEXECUTEINFOW); - execInfo.fMask = SEE_MASK_NOCLOSEPROCESS; - execInfo.lpVerb = L"open"; - std::wstring fileNameW = ToWString(tempFileName); - execInfo.lpFile = fileNameW.c_str(); - execInfo.nShow = SW_SHOWNORMAL; - if (!::ShellExecuteExW(&execInfo)) { - qCritical("failed to spawn %s: %d", tempFileName.toUtf8().constData(), ::GetLastError()); - } +void InstallationManager::postInstallCleanup() const +{ + m_CurrentArchive->close(); - m_FilesToDelete.insert(tempFileName); -} + // directories we may want to remove. sorted from longest to shortest to ensure we remove subdirectories first. + auto longestFirst = [](const QString &LHS, const QString &RHS) -> bool { + if (LHS.size() != RHS.size()) return LHS.size() > RHS.size(); + else return LHS < RHS; + }; + std::set> directoriesToRemove(longestFirst); -// copy and pasted from mo_dll -bool EndsWith(LPCWSTR string, LPCWSTR subString) -{ - size_t slen = wcslen(string); - size_t len = wcslen(subString); - if (slen < len) { - return false; + // clean up temp files + // TODO: this doesn't yet remove directories. Also, the files may be left there if this point isn't reached + foreach (const QString &tempFile, m_TempFilesToDelete) { + QFileInfo fileInfo(QDir::tempPath() + "/" + tempFile); + QFile::remove(fileInfo.absoluteFilePath()); + directoriesToRemove.insert(fileInfo.absolutePath()); } - for (size_t i = 0; i < len; ++i) { - if (towlower(string[slen - len + i]) != towlower(subString[i])) { - return false; - } + // try to delete each directory we had temporary files in. the call fails for non-empty directories which is ok + foreach (const QString &dir, directoriesToRemove) { + QDir().rmdir(dir); } - return true; -} - - -bool InstallationManager::wasCancelled() -{ - return m_CurrentArchive->getLastError() == Archive::ERROR_EXTRACT_CANCELLED; } @@ -675,9 +658,7 @@ bool InstallationManager::install(const QString &fileName, GuessedValue bool archiveOpen = m_CurrentArchive->open(ToWString(QDir::toNativeSeparators(fileName)).c_str(), new MethodCallback(this, &InstallationManager::queryPassword)); - ON_BLOCK_EXIT([this] { - this->m_CurrentArchive->close(); - }); + ON_BLOCK_EXIT(std::bind(&InstallationManager::postInstallCleanup, this)); QScopedPointer filesTree(archiveOpen ? createFilesTree() : NULL); IPluginInstaller::EInstallResult installResult = IPluginInstaller::RESULT_NOTATTEMPTED; @@ -729,13 +710,6 @@ bool InstallationManager::install(const QString &fileName, GuessedValue qPrintable(installer->name()), e.what()); } - // clean up temp files - // TODO: this doesn't yet remove directories. Also, the files may be left there if this point isn't reached - foreach (const QString &tempFile, m_TempFilesToDelete) { - QFile::remove(QDir::tempPath() + "/" + tempFile); - } - - // act upon the installation result. at this point the files have already been // extracted to the correct location switch (installResult) { diff --git a/src/installationmanager.h b/src/installationmanager.h index 0e43a15d..1c6f9f19 100644 --- a/src/installationmanager.h +++ b/src/installationmanager.h @@ -168,9 +168,7 @@ private: bool ensureValidModName(MOBase::GuessedValue &name) const; -private slots: - - void openFile(const QString &fileName); + void postInstallCleanup() const; private: @@ -202,7 +200,6 @@ private: QProgressDialog m_InstallationProgress; - std::set m_FilesToDelete; std::set m_TempFilesToDelete; }; diff --git a/src/main.cpp b/src/main.cpp index c4431ac4..7c2a9b0e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -433,7 +433,10 @@ int main(int argc, char *argv[]) qDebug("initializing tutorials"); TutorialManager::init(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getTutorialDir())).append("/")); - application.setStyleFile(settings.value("Settings/style", "").toString()); + if (!application.setStyleFile(settings.value("Settings/style", "").toString())) { + // disable invalid stylesheet + settings.setValue("Settings/style", ""); + } int res = 1; { // scope to control lifetime of mainwindow diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 57201db9..33dc75ed 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -4265,6 +4265,7 @@ void MainWindow::updateDownloadListDelegate() connect(ui->downloadView->itemDelegate(), SIGNAL(installDownload(int)), this, SLOT(installDownload(int))); connect(ui->downloadView->itemDelegate(), SIGNAL(queryInfo(int)), &m_DownloadManager, SLOT(queryInfo(int))); connect(ui->downloadView->itemDelegate(), SIGNAL(removeDownload(int, bool)), &m_DownloadManager, SLOT(removeDownload(int, bool))); + connect(ui->downloadView->itemDelegate(), SIGNAL(restoreDownload(int)), &m_DownloadManager, SLOT(restoreDownload(int))); connect(ui->downloadView->itemDelegate(), SIGNAL(cancelDownload(int)), &m_DownloadManager, SLOT(cancelDownload(int))); connect(ui->downloadView->itemDelegate(), SIGNAL(pauseDownload(int)), &m_DownloadManager, SLOT(pauseDownload(int))); connect(ui->downloadView->itemDelegate(), SIGNAL(resumeDownload(int)), &m_DownloadManager, SLOT(resumeDownload(int))); @@ -4735,3 +4736,8 @@ void MainWindow::on_linkButton_pressed() ui->linkButton->menu()->actions().at(1)->setIcon(linkDesktopFile.exists() ? removeIcon : addIcon); ui->linkButton->menu()->actions().at(2)->setIcon(linkMenuFile.exists() ? removeIcon : addIcon); } + +void MainWindow::on_showHiddenBox_toggled(bool checked) +{ + m_DownloadManager.setShowHidden(checked); +} diff --git a/src/mainwindow.h b/src/mainwindow.h index dfec5f49..ad4ed82e 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -502,6 +502,7 @@ private slots: // ui slots void on_groupCombo_currentIndexChanged(int index); void on_categoriesList_itemSelectionChanged(); void on_linkButton_pressed(); + void on_showHiddenBox_toggled(bool checked); }; #endif // MAINWINDOW_H diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 923df17c..b29136ff 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -968,7 +968,7 @@ p, li { white-space: pre-wrap; } - + @@ -976,6 +976,13 @@ p, li { white-space: pre-wrap; } + + + + Show Hidden + + + diff --git a/src/moapplication.cpp b/src/moapplication.cpp index efb0b615..ac660c6c 100644 --- a/src/moapplication.cpp +++ b/src/moapplication.cpp @@ -32,7 +32,7 @@ MOApplication::MOApplication(int argc, char **argv) } -void MOApplication::setStyleFile(const QString &styleName) +bool MOApplication::setStyleFile(const QString &styleName) { // remove all files from watch QStringList currentWatch = m_StyleWatcher.files(); @@ -42,11 +42,15 @@ void MOApplication::setStyleFile(const QString &styleName) // set new stylesheet or clear it if (styleName.length() != 0) { QString styleSheetName = applicationDirPath() + "/" + MOBase::ToQString(AppConfig::stylesheetsPath()) + "/" + styleName; + if (!QFile::exists(styleSheetName)) { + return false; + } m_StyleWatcher.addPath(styleSheetName); updateStyle(styleSheetName); } else { setStyleSheet(""); } + return true; } diff --git a/src/moapplication.h b/src/moapplication.h index 3d74031d..dd7f5eab 100644 --- a/src/moapplication.h +++ b/src/moapplication.h @@ -17,33 +17,33 @@ You should have received a copy of the GNU General Public License along with Mod Organizer. If not, see . */ -#ifndef MOAPPLICATION_H -#define MOAPPLICATION_H - -#include -#include - - -class MOApplication : public QApplication { -Q_OBJECT -public: - - MOApplication(int argc, char **argv); - - virtual bool notify (QObject *receiver, QEvent *event); - -public slots: - - void setStyleFile(const QString &style); - -private slots: - - void updateStyle(const QString &fileName); - -private: - - QFileSystemWatcher m_StyleWatcher; -}; - - -#endif // MOAPPLICATION_H +#ifndef MOAPPLICATION_H +#define MOAPPLICATION_H + +#include +#include + + +class MOApplication : public QApplication { +Q_OBJECT +public: + + MOApplication(int argc, char **argv); + + virtual bool notify (QObject *receiver, QEvent *event); + +public slots: + + bool setStyleFile(const QString &style); + +private slots: + + void updateStyle(const QString &fileName); + +private: + + QFileSystemWatcher m_StyleWatcher; +}; + + +#endif // MOAPPLICATION_H diff --git a/src/shared/skyriminfo.cpp b/src/shared/skyriminfo.cpp index 8c6b79ef..71d791f5 100644 --- a/src/shared/skyriminfo.cpp +++ b/src/shared/skyriminfo.cpp @@ -295,6 +295,7 @@ std::vector SkyrimInfo::getExecutables() result.push_back(ExecutableInfo(L"SKSE", L"skse_loader.exe", L"", L"", DEFAULT_CLOSE)); result.push_back(ExecutableInfo(L"SBW", L"SBW.exe", L"", L"", DEFAULT_CLOSE)); result.push_back(ExecutableInfo(L"Skyrim", L"TESV.exe", L"", L"", DEFAULT_CLOSE)); + result.push_back(ExecutableInfo(L"Skyrim Launcher", L"SkyrimLauncher.exe", L"", L"", DEFAULT_CLOSE)); result.push_back(ExecutableInfo(L"BOSS", L"BOSS/BOSS.exe", L"", L"", DEFAULT_STAY)); result.push_back(ExecutableInfo(L"Creation Kit", L"CreationKit.exe", L"", L"", DEFAULT_STAY, L"202480")); diff --git a/src/version.rc b/src/version.rc index 16669011..e71bc1b5 100644 --- a/src/version.rc +++ b/src/version.rc @@ -1,7 +1,7 @@ #include "Winver.h" -#define VER_FILEVERSION 1,0,4,0 -#define VER_FILEVERSION_STR "1,0,4,0\0" +#define VER_FILEVERSION 1,0,5,0 +#define VER_FILEVERSION_STR "1,0,5,0\0" VS_VERSION_INFO VERSIONINFO FILEVERSION VER_FILEVERSION -- cgit v1.3.1 From 382226bb46541f07e62ce689cfeaf395aa673a44 Mon Sep 17 00:00:00 2001 From: Tannin Date: Fri, 11 Oct 2013 23:03:49 +0200 Subject: - added new plugin to test if fnis needs to be run - some functionality to the plugin interface to enable them to search for files&directories in the virtual FS (rudimentary atm) - functionality for plugins to react to application being started from MO - broken ESPs are no longer reported as popup windows but only in the log file - bugfix: plugins couldn't store persistent data if they had no user-editable settings --- src/main.cpp | 2 +- src/mainwindow.cpp | 87 +++++++++++++++++++++++++++++++++++++++++-- src/mainwindow.h | 10 ++++- src/organizer.pro | 4 +- src/pluginlist.cpp | 2 +- src/settings.cpp | 1 + src/shared/directoryentry.cpp | 6 +++ src/shared/directoryentry.h | 1 + 8 files changed, 103 insertions(+), 10 deletions(-) (limited to 'src/mainwindow.h') diff --git a/src/main.cpp b/src/main.cpp index c4431ac4..49c7b3c0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -466,7 +466,7 @@ int main(int argc, char *argv[]) arguments.removeFirst(); // remove application name (ModOrganizer.exe) arguments.removeFirst(); // remove binary name // pass the remaining parameters to the binary - mainWindow.spawnProgram(exeName, arguments.join(" "), selectedProfileName, QDir()); + mainWindow.startApplication(exeName, arguments, QString(), selectedProfileName); return 0; } diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 1ea35d74..1ee4b66e 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1276,10 +1276,15 @@ HANDLE MainWindow::spawnBinaryDirect(const QFileInfo &binary, const QString &arg QCoreApplication::processEvents(); } - return startBinary(binary, arguments, profileName, m_Settings.logLevel(), currentDirectory, true); + if (m_AboutToRun(binary.absoluteFilePath())) { + return startBinary(binary, arguments, profileName, m_Settings.logLevel(), currentDirectory, true); + } else { + qDebug("start of \"%s\" canceled by plugin", qPrintable(binary.absoluteFilePath())); + return INVALID_HANDLE_VALUE; + } } - +/* void MainWindow::spawnProgram(const QString &fileName, const QString &argumentsArg, const QString &profileName, const QDir ¤tDirectory) { @@ -1299,7 +1304,7 @@ void MainWindow::spawnProgram(const QString &fileName, const QString &argumentsA } spawnBinaryDirect(binary, arguments, profileName, currentDirectory, steamAppID); } - +*/ void MainWindow::spawnBinary(const QFileInfo &binary, const QString &arguments, const QDir ¤tDirectory, bool closeAfterStart, const QString &steamAppID) { @@ -1895,7 +1900,6 @@ void MainWindow::activateProxy(bool activate) busyDialog.hide(); } - void MainWindow::readSettings() { QSettings settings(ToQString(GameInfo::instance().getIniFilename()), QSettings::IniFormat); @@ -2053,11 +2057,86 @@ QString MainWindow::resolvePath(const QString &fileName) const } } +QStringList MainWindow::listDirectories(const QString &directoryName) const +{ + QStringList result; + DirectoryEntry *dir = m_DirectoryStructure->findSubDirectoryRecursive(ToWString(directoryName)); + if (dir != NULL) { + std::vector::iterator current, end; + dir->getSubDirectories(current, end); + for (; current != end; ++current) { + result.append(ToQString((*current)->getName())); + } + } + return result; +} + +QStringList MainWindow::findFiles(const QString &path, const std::function &filter) const +{ + QStringList result; + DirectoryEntry *dir = m_DirectoryStructure->findSubDirectoryRecursive(ToWString(path)); + if (dir != NULL) { + std::vector files = dir->getFiles(); + foreach (FileEntry::Ptr file, files) { + if (filter(ToQString(file->getName()))) { + result.append(ToQString(file->getFullPath())); + } + } + } else { + qWarning("directory %s not found", qPrintable(path)); + } + return result; +} + IDownloadManager *MainWindow::downloadManager() { return &m_DownloadManager; } +HANDLE MainWindow::startApplication(const QString &executable, const QStringList &args, const QString &cwd, const QString &profile) +{ + QFileInfo binary; + QString arguments = args.join(" "); + QString currentDirectory = cwd; + QString profileName = (profile.length() > 0) ? profile : m_CurrentProfile->getName(); + QString steamAppID; + if (executable.contains('\\') || executable.contains('/')) { + // file path + binary = QFileInfo(executable); + if (binary.isRelative()) { + // relative path, should be relative to game directory + binary = QFileInfo(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory())) + "/" + executable); + } + if (cwd.length() == 0) { + currentDirectory = binary.absolutePath(); + } + } else { + // only a file name, search executables list + try { + const Executable &exe = m_ExecutablesList.find(executable); + steamAppID = exe.m_SteamAppID; + if (arguments == "") { + arguments = exe.m_Arguments; + } + binary = exe.m_BinaryInfo; + if (cwd.length() == 0) { + currentDirectory = exe.m_WorkingDirectory; + } + } catch (const std::runtime_error&) { + qWarning("\"%s\" not set up as executable", executable.toUtf8().constData()); + return INVALID_HANDLE_VALUE; + } + } + + return spawnBinaryDirect(binary, arguments, profileName, currentDirectory, steamAppID); +} + +bool MainWindow::onAboutToRun(const boost::function &func) +{ + auto conn = m_AboutToRun.connect(func); + return conn.connected(); +} + std::vector MainWindow::activeProblems() const { std::vector problems; diff --git a/src/mainwindow.h b/src/mainwindow.h index dfec5f49..6992861b 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -89,8 +89,8 @@ public: void createFirstProfile(); - void spawnProgram(const QString &fileName, const QString &argumentsArg, - const QString &profileName, const QDir ¤tDirectory); +/* void spawnProgram(const QString &fileName, const QString &argumentsArg, + const QString &profileName, const QDir ¤tDirectory);*/ void loadPlugins(); @@ -111,7 +111,11 @@ public: virtual QString pluginDataPath() const; virtual void installMod(const QString &fileName); virtual QString resolvePath(const QString &fileName) const; + virtual QStringList listDirectories(const QString &directoryName) const; + virtual QStringList findFiles(const QString &path, const std::function &filter) const; virtual MOBase::IDownloadManager *downloadManager(); + virtual HANDLE startApplication(const QString &executable, const QStringList &args = QStringList(), const QString &cwd = "", const QString &profile = ""); + virtual bool onAboutToRun(const boost::function &func); virtual std::vector activeProblems() const; virtual QString shortDescription(unsigned int key) const; @@ -320,6 +324,8 @@ private: QFile m_PluginsCheck; + SignalAboutToRunApplication m_AboutToRun; + private slots: void showMessage(const QString &message); diff --git a/src/organizer.pro b/src/organizer.pro index a9353361..c817f2da 100644 --- a/src/organizer.pro +++ b/src/organizer.pro @@ -199,8 +199,8 @@ CONFIG(debug, debug|release) { QMAKE_LFLAGS += /DEBUG } -QMAKE_CXXFLAGS_WARN_ON -= -W3 -QMAKE_CXXFLAGS_WARN_ON += -W4 +#QMAKE_CXXFLAGS_WARN_ON -= -W3 +#QMAKE_CXXFLAGS_WARN_ON += -W4 QMAKE_CXXFLAGS += -wd4127 -wd4512 -wd4189 CONFIG += embed_manifest_exe diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index e730d3f4..55f8a39e 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -953,7 +953,7 @@ PluginList::ESPInfo::ESPInfo(const QString &name, bool enabled, FILETIME time, c m_Masters.insert(QString(iter->c_str())); } } catch (const std::exception &e) { - reportError(tr("failed to parse esp file %1: %2").arg(fullPath).arg(e.what())); + qCritical("failed to parse esp file %s: %s", qPrintable(fullPath), e.what()); m_IsMaster = false; } } diff --git a/src/settings.cpp b/src/settings.cpp index 42a8d693..4f301b0c 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -105,6 +105,7 @@ bool Settings::pluginBlacklisted(const QString &fileName) const void Settings::registerPlugin(IPlugin *plugin) { m_Plugins.push_back(plugin); + m_PluginSettings.insert(plugin->name(), QMap()); foreach (const PluginSetting &setting, plugin->settings()) { QVariant temp = m_Settings.value("Plugins/" + plugin->name() + "/" + setting.key, setting.defaultValue); if (!temp.convert(setting.defaultValue.type())) { diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp index 8380181f..b3a12498 100644 --- a/src/shared/directoryentry.cpp +++ b/src/shared/directoryentry.cpp @@ -746,6 +746,12 @@ DirectoryEntry *DirectoryEntry::findSubDirectory(const std::wstring &name) const } +DirectoryEntry *DirectoryEntry::findSubDirectoryRecursive(const std::wstring &path) +{ + return getSubDirectoryRecursive(path, false, -1); +} + + const FileEntry::Ptr DirectoryEntry::findFile(const std::wstring &name) { auto iter = m_Files.find(name); diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h index 498ebfe1..1ad9816c 100644 --- a/src/shared/directoryentry.h +++ b/src/shared/directoryentry.h @@ -227,6 +227,7 @@ public: } DirectoryEntry *findSubDirectory(const std::wstring &name) const; + DirectoryEntry *findSubDirectoryRecursive(const std::wstring &path); /** retrieve a file in this directory by name. * @param name name of the file -- cgit v1.3.1 From f3cb50057ba5cfe5496fda6ed860b14331fdb5c0 Mon Sep 17 00:00:00 2001 From: Tannin Date: Sat, 12 Oct 2013 13:21:37 +0200 Subject: - bugfixes to last merge - fnis-check plugin can now be disabled --- src/mainwindow.cpp | 6 ++++-- src/mainwindow.h | 25 ++++++++++++++++++++++++- 2 files changed, 28 insertions(+), 3 deletions(-) (limited to 'src/mainwindow.h') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 7ef2dab6..b7a0fc2f 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -111,6 +111,8 @@ using namespace MOBase; using namespace MOShared; + + static bool isOnline() { QList interfaces = QNetworkInterface::allInterfaces(); @@ -146,7 +148,7 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget m_Updater(NexusInterface::instance(), this), m_CategoryFactory(CategoryFactory::instance()), m_CurrentProfile(NULL), m_AskForNexusPW(false), m_LoginAttempted(false), m_ArchivesInit(false), m_ContextItem(NULL), m_ContextAction(NULL), m_CurrentSaveView(NULL), - m_GameInfo(new GameInfoImpl()) + m_GameInfo(new GameInfoImpl()), m_AboutToRun() { ui->setupUi(this); this->setWindowTitle(ToQString(GameInfo::instance().getGameName()) + " Mod Organizer v" + m_Updater.getVersion().displayString()); @@ -2131,7 +2133,7 @@ HANDLE MainWindow::startApplication(const QString &executable, const QStringList return spawnBinaryDirect(binary, arguments, profileName, currentDirectory, steamAppID); } -bool MainWindow::onAboutToRun(const boost::function &func) +bool MainWindow::onAboutToRun(const std::function &func) { auto conn = m_AboutToRun.connect(func); return conn.connected(); diff --git a/src/mainwindow.h b/src/mainwindow.h index fab850cf..7d5d4a63 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -50,6 +50,7 @@ along with Mod Organizer. If not, see . #include "savegameinfowidgetgamebryo.h" #include #include +#include namespace Ui { class MainWindow; @@ -64,6 +65,28 @@ class MainWindow : public QMainWindow, public MOBase::IOrganizer, public MOBase: Q_OBJECT Q_INTERFACES(MOBase::IPluginDiagnose) +private: + + struct SignalCombinerAnd + { + typedef bool result_type; + template + bool operator()(InputIterator first, InputIterator last) const + { + while (first != last) { + if (!(*first)) { + return false; + } + ++first; + } + return true; + } + }; + +public: + + typedef boost::signals2::signal SignalAboutToRunApplication; + public: explicit MainWindow(const QString &exeName, QSettings &initSettings, QWidget *parent = 0); ~MainWindow(); @@ -115,7 +138,7 @@ public: virtual QStringList findFiles(const QString &path, const std::function &filter) const; virtual MOBase::IDownloadManager *downloadManager(); virtual HANDLE startApplication(const QString &executable, const QStringList &args = QStringList(), const QString &cwd = "", const QString &profile = ""); - virtual bool onAboutToRun(const boost::function &func); + virtual bool onAboutToRun(const std::function &func); virtual std::vector activeProblems() const; virtual QString shortDescription(unsigned int key) const; -- cgit v1.3.1 From 1bf3cc585e28516f4ff6c9edf720fa9d290f47c2 Mon Sep 17 00:00:00 2001 From: Tannin Date: Wed, 16 Oct 2013 19:32:59 +0200 Subject: - nmm importer now also transfers category, endorsement state and lastest version information - nmm importer should now always determine the correct nexus id - nmm importer will now correctly restore overwritten files for imported mods --- src/mainwindow.cpp | 2 +- src/mainwindow.h | 25 +++++++++++++++++++++++-- src/modinfo.cpp | 5 +++++ src/modinfo.h | 15 +++++++++++++++ 4 files changed, 44 insertions(+), 3 deletions(-) (limited to 'src/mainwindow.h') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 7ef2dab6..1b6fd047 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2131,7 +2131,7 @@ HANDLE MainWindow::startApplication(const QString &executable, const QStringList return spawnBinaryDirect(binary, arguments, profileName, currentDirectory, steamAppID); } -bool MainWindow::onAboutToRun(const boost::function &func) +bool MainWindow::onAboutToRun(const std::function &func) { auto conn = m_AboutToRun.connect(func); return conn.connected(); diff --git a/src/mainwindow.h b/src/mainwindow.h index fab850cf..8a34d16a 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -50,6 +50,7 @@ along with Mod Organizer. If not, see . #include "savegameinfowidgetgamebryo.h" #include #include +#include namespace Ui { class MainWindow; @@ -59,11 +60,32 @@ class QToolButton; class ModListSortProxy; class ModListGroupCategoriesProxy; + class MainWindow : public QMainWindow, public MOBase::IOrganizer, public MOBase::IPluginDiagnose { Q_OBJECT Q_INTERFACES(MOBase::IPluginDiagnose) +public: + + struct SignalCombinerAnd + { + typedef bool result_type; + template + bool operator()(InputIterator first, InputIterator last) const + { + while (first != last) { + if (!(*first)) { + return false; + } + ++first; + } + return true; + } + }; + + typedef boost::signals2::signal SignalAboutToRunApplication; + public: explicit MainWindow(const QString &exeName, QSettings &initSettings, QWidget *parent = 0); ~MainWindow(); @@ -115,7 +137,7 @@ public: virtual QStringList findFiles(const QString &path, const std::function &filter) const; virtual MOBase::IDownloadManager *downloadManager(); virtual HANDLE startApplication(const QString &executable, const QStringList &args = QStringList(), const QString &cwd = "", const QString &profile = ""); - virtual bool onAboutToRun(const boost::function &func); + virtual bool onAboutToRun(const std::function &func); virtual std::vector activeProblems() const; virtual QString shortDescription(unsigned int key) const; @@ -157,7 +179,6 @@ signals: void styleChanged(const QString &styleFile); - void modListDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); protected: diff --git a/src/modinfo.cpp b/src/modinfo.cpp index 1479f7b4..9bc013f1 100644 --- a/src/modinfo.cpp +++ b/src/modinfo.cpp @@ -554,6 +554,11 @@ void ModInfoRegular::setNexusDescription(const QString &description) m_MetaInfoChanged = true; } +void ModInfoRegular::addNexusCategory(int categoryID) +{ + m_Categories.insert(CategoryFactory::instance().resolveNexusID(categoryID)); +} + void ModInfoRegular::setIsEndorsed(bool endorsed) { diff --git a/src/modinfo.h b/src/modinfo.h index 3b83d207..e408e812 100644 --- a/src/modinfo.h +++ b/src/modinfo.h @@ -267,6 +267,13 @@ public: */ virtual void setNexusDescription(const QString &description) = 0; + /** + * @brief sets the category id from a nexus category id. Conversion to MO id happens internally + * @param categoryID the nexus category id + * @note if a mapping is not possible, the category is set to the default value + */ + virtual void addNexusCategory(int categoryID) = 0; + /** * update the endorsement state for the mod. This only changes the * buffered state, it does not sync with Nexus @@ -605,6 +612,13 @@ public: */ virtual void setNexusDescription(const QString &description); + /** + * @brief sets the category id from a nexus category id. Conversion to MO id happens internally + * @param categoryID the nexus category id + * @note if a mapping is not possible, the category is set to the default value + */ + virtual void addNexusCategory(int categoryID); + /** * @brief sets the new primary category of the mod * @param categoryID the category to set @@ -854,6 +868,7 @@ public: virtual void setNewestVersion(const MOBase::VersionInfo&) {} virtual void ignoreUpdate(bool) {} virtual void setNexusDescription(const QString&) {} + virtual void addNexusCategory(int) {} virtual void setIsEndorsed(bool) {} virtual void setNeverEndorse() {} virtual bool remove() { return false; } -- cgit v1.3.1 From b47c89661e603336abd98d5ecd0a82f6743cab18 Mon Sep 17 00:00:00 2001 From: Tannin Date: Wed, 16 Oct 2013 23:04:03 +0200 Subject: - plugin (esp/esm) list is now exposed to plugins (read-only functionality right now) - integrated fomod installer now supports file dependencies - bugfix: integrated fomod installer crashed if the installer had no optiosnns MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/mainwindow.cpp | 5 +++++ src/mainwindow.h | 23 +---------------------- src/pluginlist.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ src/pluginlist.h | 9 ++++++++- 4 files changed, 54 insertions(+), 23 deletions(-) (limited to 'src/mainwindow.h') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index b7a0fc2f..de6f20cf 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2095,6 +2095,11 @@ IDownloadManager *MainWindow::downloadManager() return &m_DownloadManager; } +IPluginList *MainWindow::pluginList() +{ + return &m_PluginList; +} + HANDLE MainWindow::startApplication(const QString &executable, const QStringList &args, const QString &cwd, const QString &profile) { QFileInfo binary; diff --git a/src/mainwindow.h b/src/mainwindow.h index dc8d20c8..a96e5fcf 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -84,28 +84,6 @@ private: } }; -public: - - typedef boost::signals2::signal SignalAboutToRunApplication; - -public: - - struct SignalCombinerAnd - { - typedef bool result_type; - template - bool operator()(InputIterator first, InputIterator last) const - { - while (first != last) { - if (!(*first)) { - return false; - } - ++first; - } - return true; - } - }; - typedef boost::signals2::signal SignalAboutToRunApplication; public: @@ -158,6 +136,7 @@ public: virtual QStringList listDirectories(const QString &directoryName) const; virtual QStringList findFiles(const QString &path, const std::function &filter) const; virtual MOBase::IDownloadManager *downloadManager(); + virtual MOBase::IPluginList *pluginList(); virtual HANDLE startApplication(const QString &executable, const QStringList &args = QStringList(), const QString &cwd = "", const QString &profile = ""); virtual bool onAboutToRun(const std::function &func); diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index 55f8a39e..74aa6ff3 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -571,6 +571,46 @@ void PluginList::refreshLoadOrder() } } +IPluginList::PluginState PluginList::state(const QString &name) const +{ + auto iter = m_ESPsByName.find(name.toLower()); + if (iter == m_ESPsByName.end()) { + return IPluginList::STATE_MISSING; + } else { + return m_ESPs[iter->second].m_Enabled ? IPluginList::STATE_ACTIVE : IPluginList::STATE_INACTIVE; + } +} + +int PluginList::priority(const QString &name) const +{ + auto iter = m_ESPsByName.find(name.toLower()); + if (iter == m_ESPsByName.end()) { + return -1; + } else { + return m_ESPs[iter->second].m_Priority; + } +} + +int PluginList::loadOrder(const QString &name) const +{ + auto iter = m_ESPsByName.find(name.toLower()); + if (iter == m_ESPsByName.end()) { + return -1; + } else { + return m_ESPs[iter->second].m_LoadOrder; + } +} + +bool PluginList::isMaster(const QString &name) const +{ + auto iter = m_ESPsByName.find(name.toLower()); + if (iter == m_ESPsByName.end()) { + return false; + } else { + return m_ESPs[iter->second].m_IsMaster; + } +} + void PluginList::updateIndices() { diff --git a/src/pluginlist.h b/src/pluginlist.h index 60c9be95..2ad54d6d 100644 --- a/src/pluginlist.h +++ b/src/pluginlist.h @@ -25,12 +25,13 @@ along with Mod Organizer. If not, see . #include #include #include +#include /** * @brief model representing the plugins (.esp/.esm) in the current virtual data folder **/ -class PluginList : public QAbstractTableModel +class PluginList : public QAbstractTableModel, public MOBase::IPluginList { Q_OBJECT @@ -134,6 +135,12 @@ public: void refreshLoadOrder(); +public: + virtual PluginState state(const QString &name) const; + virtual int priority(const QString &name) const; + virtual int loadOrder(const QString &name) const; + virtual bool isMaster(const QString &name) const; + public: // implementation of the QAbstractTableModel interface virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; -- cgit v1.3.1 From 09bd3dbead9afd6a57684908e77aba6960ad464d Mon Sep 17 00:00:00 2001 From: Tannin Date: Sat, 26 Oct 2013 15:02:31 +0200 Subject: - text files encoded in system encoding should now be correctly recognized in the mod info dialog - archive-list can be user-sorted again (within one mod) --- src/mainwindow.cpp | 11 ++++++----- src/mainwindow.h | 2 +- src/mainwindow.ui | 26 ++++++++++++++++---------- src/modinfodialog.cpp | 30 +++++++++++++++++++----------- 4 files changed, 42 insertions(+), 27 deletions(-) (limited to 'src/mainwindow.h') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index cf8e355f..967a3842 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -205,7 +205,6 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget resizeLists(initSettings.contains("mod_list_state"), initSettings.contains("plugin_list_state")); - QMenu *linkMenu = new QMenu(this); linkMenu->addAction(QIcon(":/MO/gui/link"), tr("Toolbar"), this, SLOT(linkToolbar())); linkMenu->addAction(QIcon(":/MO/gui/link"), tr("Desktop"), this, SLOT(linkDesktop())); @@ -247,6 +246,8 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget connect(ui->espFilterEdit, SIGNAL(textChanged(QString)), this, SLOT(espFilterChanged(QString))); connect(&m_PluginList, SIGNAL(saveTimer()), this, SLOT(savePluginList())); + connect(ui->bsaList, SIGNAL(itemsMoved()), this, SLOT(on_bsaList_itemMoved())); + connect(ui->bsaWarning, SIGNAL(linkActivated(QString)), this, SLOT(linkClicked(QString))); connect(&m_DirectoryRefresher, SIGNAL(refreshed()), this, SLOT(directory_refreshed())); @@ -681,7 +682,7 @@ bool MainWindow::saveCurrentLists() reportError(tr("failed to save load order: %1").arg(e.what())); } - // ensure the archive list exists + // save only if the file doesn't exist at all, changes made in the ui are saved immediately if (!QFile::exists(m_CurrentProfile->getArchivesFileName())) { saveArchiveList(); } @@ -883,8 +884,7 @@ void MainWindow::hideSaveGameInfo() } } - -bool MainWindow::eventFilter(QObject* object, QEvent *event) +bool MainWindow::eventFilter(QObject *object, QEvent *event) { if ((object == ui->savegameList) && ((event->type() == QEvent::Leave) || (event->type() == QEvent::WindowDeactivate))) { @@ -1802,6 +1802,7 @@ void MainWindow::refreshBSAList() subItem = items.at(0); } else { subItem = new QTreeWidgetItem(QStringList(ToQString(origin.getName()))); + subItem->setFlags(subItem->flags() & ~Qt::ItemIsDragEnabled); ui->bsaList->addTopLevelItem(subItem); } subItem->addChild(iter->second); @@ -4649,7 +4650,7 @@ void MainWindow::on_bsaList_customContextMenuRequested(const QPoint &pos) menu.exec(ui->bsaList->mapToGlobal(pos)); } -void MainWindow::on_bsaList_itemChanged(QTreeWidgetItem*, int) +void MainWindow::on_bsaList_itemMoved() { saveArchiveList(); m_CheckBSATimer.start(500); diff --git a/src/mainwindow.h b/src/mainwindow.h index a96e5fcf..93a6af80 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -510,7 +510,7 @@ private slots: // ui slots void on_actionEndorseMO_triggered(); void on_bsaList_customContextMenuRequested(const QPoint &pos); - void on_bsaList_itemChanged(QTreeWidgetItem *item, int column); + void on_bsaList_itemMoved(); void on_btnRefreshData_clicked(); void on_categoriesList_customContextMenuRequested(const QPoint &pos); void on_compactBox_toggled(bool checked); diff --git a/src/mainwindow.ui b/src/mainwindow.ui index b29136ff..73ca868e 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -577,7 +577,7 @@ p, li { white-space: pre-wrap; } QTabWidget::Rounded - 0 + 1 @@ -725,7 +725,7 @@ p, li { white-space: pre-wrap; } 6 - + Qt::CustomContextMenu @@ -742,7 +742,7 @@ BSAs checked here are loaded in such a way that your installation order is obeye QAbstractItemView::NoEditTriggers - false + true false @@ -751,10 +751,10 @@ BSAs checked here are loaded in such a way that your installation order is obeye false - QAbstractItemView::NoDragDrop + QAbstractItemView::DragDrop - Qt::IgnoreAction + Qt::MoveAction QAbstractItemView::SingleSelection @@ -768,6 +768,12 @@ BSAs checked here are loaded in such a way that your installation order is obeye true + + 1 + + + false + 200 @@ -776,11 +782,6 @@ BSAs checked here are loaded in such a way that your installation order is obeye File - - - Mod - - @@ -1224,6 +1225,11 @@ Right now this has very limited functionality QTreeView
modlistview.h
+ + MOBase::SortableTreeWidget + QTreeWidget +
sortabletreewidget.h
+
diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index b4ae57ea..fae21cc4 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -254,17 +254,19 @@ void ModInfoDialog::refreshLists() } else if ((fileName.endsWith(".png", Qt::CaseInsensitive)) || (fileName.endsWith(".jpg", Qt::CaseInsensitive))) { QImage image = QImage(fileName); - if (static_cast(image.width()) / static_cast(image.height()) > 1.34) { - image = image.scaledToWidth(128); - } else { - image = image.scaledToHeight(96); - } + 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); + 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); + } } } @@ -400,7 +402,13 @@ void ModInfoDialog::openTextFile(const QString &fileName) textFile.open(QIODevice::ReadOnly); QByteArray buffer = textFile.readAll(); QTextCodec *codec = QTextCodec::codecForUtfText(buffer, m_UTF8Codec); - ui->textFileView->setText(codec->toUnicode(buffer)); + QString text = codec->toUnicode(buffer); + if (codec->fromUnicode(text) != buffer) { + qDebug("conversion failed assuming local encoding"); + codec = QTextCodec::codecForLocale(); + text = codec->toUnicode(buffer); + } + ui->textFileView->setText(text); ui->textFileView->setProperty("currentFile", fileName); ui->textFileView->setProperty("encoding", codec->name()); ui->saveTXTButton->setEnabled(false); -- cgit v1.3.1 From d1594798e6e78bb329e744a72e92d3f292f38a20 Mon Sep 17 00:00:00 2001 From: Tannin Date: Fri, 1 Nov 2013 14:59:25 +0100 Subject: - added a new diagnosis to detect potential problems in regards to esp vs. asset ordering (not fully functional yet) - new plugin interfaces to the mod list - plugins can now query the origin (mod) of a esp/esm - bugfix: potential access to profile before one was activated - bugfix: regression in previous (not-yet-released) commit prevented changes to bsa list from being saved --- src/mainwindow.cpp | 18 +++++++++++++--- src/mainwindow.h | 4 +++- src/modlist.cpp | 40 +++++++++++++++++++++++++++++++++++ src/modlist.h | 15 ++++++++++++- src/pluginlist.cpp | 9 ++++++++ src/pluginlist.h | 1 + src/problemsdialog.ui | 58 +++++++++++++++++++++++++-------------------------- 7 files changed, 110 insertions(+), 35 deletions(-) (limited to 'src/mainwindow.h') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 967a3842..774c99f5 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -246,7 +246,7 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget connect(ui->espFilterEdit, SIGNAL(textChanged(QString)), this, SLOT(espFilterChanged(QString))); connect(&m_PluginList, SIGNAL(saveTimer()), this, SLOT(savePluginList())); - connect(ui->bsaList, SIGNAL(itemsMoved()), this, SLOT(on_bsaList_itemMoved())); + connect(ui->bsaList, SIGNAL(itemsMoved()), this, SLOT(bsaList_itemMoved())); connect(ui->bsaWarning, SIGNAL(linkActivated(QString)), this, SLOT(linkClicked(QString))); @@ -1673,7 +1673,7 @@ void MainWindow::refreshSaveList() void MainWindow::refreshLists() { - if (m_DirectoryStructure->isPopulated()) { + if ((m_CurrentProfile != NULL) && m_DirectoryStructure->isPopulated()) { refreshESPList(); refreshBSAList(); } // no point in refreshing lists if no files have been added to the directory tree @@ -2104,6 +2104,11 @@ IPluginList *MainWindow::pluginList() return &m_PluginList; } +IModList *MainWindow::modList() +{ + return &m_ModList; +} + HANDLE MainWindow::startApplication(const QString &executable, const QStringList &args, const QString &cwd, const QString &profile) { QFileInfo binary; @@ -4650,13 +4655,19 @@ void MainWindow::on_bsaList_customContextMenuRequested(const QPoint &pos) menu.exec(ui->bsaList->mapToGlobal(pos)); } -void MainWindow::on_bsaList_itemMoved() +void MainWindow::bsaList_itemMoved() { saveArchiveList(); m_CheckBSATimer.start(500); } +void MainWindow::on_bsaList_itemChanged(QTreeWidgetItem*, int) +{ + saveArchiveList(); + m_CheckBSATimer.start(500); +} + void MainWindow::on_actionProblems_triggered() { // QString problemDescription; @@ -4836,3 +4847,4 @@ void MainWindow::on_showHiddenBox_toggled(bool checked) { m_DownloadManager.setShowHidden(checked); } + diff --git a/src/mainwindow.h b/src/mainwindow.h index 93a6af80..cf97d4f9 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -137,6 +137,7 @@ public: virtual QStringList findFiles(const QString &path, const std::function &filter) const; virtual MOBase::IDownloadManager *downloadManager(); virtual MOBase::IPluginList *pluginList(); + virtual MOBase::IModList *modList(); virtual HANDLE startApplication(const QString &executable, const QStringList &args = QStringList(), const QString &cwd = "", const QString &profile = ""); virtual bool onAboutToRun(const std::function &func); @@ -510,7 +511,7 @@ private slots: // ui slots void on_actionEndorseMO_triggered(); void on_bsaList_customContextMenuRequested(const QPoint &pos); - void on_bsaList_itemMoved(); + void bsaList_itemMoved(); void on_btnRefreshData_clicked(); void on_categoriesList_customContextMenuRequested(const QPoint &pos); void on_compactBox_toggled(bool checked); @@ -531,6 +532,7 @@ private slots: // ui slots void on_categoriesList_itemSelectionChanged(); void on_linkButton_pressed(); void on_showHiddenBox_toggled(bool checked); + void on_bsaList_itemChanged(QTreeWidgetItem *item, int column); }; #endif // MAINWINDOW_H diff --git a/src/modlist.cpp b/src/modlist.cpp index 0fdcf6fa..541e1e6e 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -551,6 +551,46 @@ void ModList::changeModPriority(int sourceIndex, int newPriority) emit modorder_changed(); } +IModList::ModState ModList::state(const QString &name) const +{ + unsigned int modIndex = ModInfo::getIndex(name); + if (modIndex == UINT_MAX) { + return IModList::STATE_MISSING; + } else { + ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex); + if (modInfo->canBeEnabled()) { + return m_Profile->modEnabled(modIndex) ? IModList::STATE_ACTIVE : IModList::STATE_INACTIVE; + } else { + return IModList::STATE_NOTOPTIONAL; + } + } +} + +int ModList::priority(const QString &name) const +{ + unsigned int modIndex = ModInfo::getIndex(name); + if (modIndex == UINT_MAX) { + return -1; + } else { + return m_Profile->getModPriority(modIndex); + } +} + +bool ModList::setPriority(const QString &name, int newPriority) +{ + if ((newPriority < 0) || (newPriority >= static_cast(m_Profile->numRegularMods()))) { + return false; + } + + unsigned int modIndex = ModInfo::getIndex(name); + if (modIndex == UINT_MAX) { + return false; + } else { + m_Profile->setModPriority(modIndex, newPriority); + notifyChange(modIndex); + return true; + } +} bool ModList::dropURLs(const QMimeData *mimeData, int row, const QModelIndex &parent) { diff --git a/src/modlist.h b/src/modlist.h index 7e76d677..cdaa24ca 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -26,6 +26,8 @@ along with Mod Organizer. If not, see . #include "modinfo.h" #include "profile.h" +#include + #include #include #include @@ -41,7 +43,7 @@ along with Mod Organizer. If not, see . * This is used in a view in the main window of MO. It combines general information about * the mods from ModInfo with status information from the Profile **/ -class ModList : public QAbstractItemModel +class ModList : public QAbstractItemModel, public MOBase::IModList { Q_OBJECT @@ -92,6 +94,17 @@ public: void changeModPriority(int sourceIndex, int newPriority); +public: + + /// \copydoc MOBase::IModList::state + virtual ModState state(const QString &name) const; + + /// \copydoc MOBase::IModList::priority + virtual int priority(const QString &name) const; + + /// \copydoc MOBase::IModList::setPriority + virtual bool setPriority(const QString &name, int newPriority); + public: // implementation of virtual functions of QAbstractItemModel virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index 74aa6ff3..456d29ee 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -611,6 +611,15 @@ bool PluginList::isMaster(const QString &name) const } } +QString PluginList::origin(const QString &name) const +{ + auto iter = m_ESPsByName.find(name.toLower()); + if (iter == m_ESPsByName.end()) { + return false; + } else { + return m_ESPs[iter->second].m_OriginName; + } +} void PluginList::updateIndices() { diff --git a/src/pluginlist.h b/src/pluginlist.h index 2ad54d6d..4da7be4b 100644 --- a/src/pluginlist.h +++ b/src/pluginlist.h @@ -140,6 +140,7 @@ public: virtual int priority(const QString &name) const; virtual int loadOrder(const QString &name) const; virtual bool isMaster(const QString &name) const; + virtual QString origin(const QString &name) const; public: // implementation of the QAbstractTableModel interface diff --git a/src/problemsdialog.ui b/src/problemsdialog.ui index b194bf31..d3a0d959 100644 --- a/src/problemsdialog.ui +++ b/src/problemsdialog.ui @@ -49,54 +49,52 @@ <!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"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:7.8pt; font-weight:400; font-style:normal;"> +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"><br /></p></body></html>
- - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Close + + + +
- buttonBox - accepted() + closeButton + clicked() ProblemsDialog accept() - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - ProblemsDialog - reject() - - - 316 - 260 + 526 + 354 286 - 274 + 187 -- cgit v1.3.1 From c6101e34e1e142a3442c3a4543ea22dabd31fa33 Mon Sep 17 00:00:00 2001 From: Tannin Date: Mon, 18 Nov 2013 20:17:14 +0100 Subject: - archive.dll now supports querying the crc value of files - esptk now determines if a esp is a dummy (without records) - hook.dll no longer creates a log file if noone wrote to it - nexus id and installtime columns are now hidden by default - modlist can now be refreshed without saving first (so plugins can replace the modlist.txt as a whole) - plugins can now query more details about virtualised files - added style options "plastique" and "cleanlooks" - "overwrite" is no longer listed with a creation time - a warning will now be displayed if the user has too many plugins active - a warning will now be displayed if mods with scripts have an installation order that doesn't match the corresponding esp load order - nmm importer now has select all/deselect all buttons - nmm importer no longer tries to unpack missing files from archives (won't work anyway) - initial support for importing from nmm 0.5 alpha - removed some broken warning suppresions - python runner now works with bundled python - extended qbs build system (still fails to build the main gui application) - implemented a nsis-based installer --- src/installationmanager.cpp | 2 -- src/main.cpp | 2 +- src/mainwindow.cpp | 50 ++++++++++++++++++++++++++++++++++++++++++--- src/mainwindow.h | 5 ++++- src/moapplication.cpp | 29 ++++++++++++++++++-------- src/moapplication.h | 2 ++ src/modinfodialog.ui | 33 ++++++++++-------------------- src/modlist.cpp | 7 ++++++- src/modlist.h | 4 ++++ src/organizer.qbs | 44 ++++++++++++++++++++++++++++----------- src/pluginlist.cpp | 21 +++++++++++++++++++ src/pluginlist.h | 7 +++++++ src/settings.cpp | 3 +++ src/shared/shared.qbs | 14 ++++++++----- 14 files changed, 167 insertions(+), 56 deletions(-) (limited to 'src/mainwindow.h') diff --git a/src/installationmanager.cpp b/src/installationmanager.cpp index 6b430eea..a8571ec9 100644 --- a/src/installationmanager.cpp +++ b/src/installationmanager.cpp @@ -506,7 +506,6 @@ bool InstallationManager::ensureValidModName(GuessedValue &name) const return true; } - bool InstallationManager::doInstall(GuessedValue &modName, int modID, const QString &version, const QString &newestVersion, int categoryID) { @@ -528,7 +527,6 @@ bool InstallationManager::doInstall(GuessedValue &modName, int modID, m_InstallationProgress.setValue(0); m_InstallationProgress.setWindowModality(Qt::WindowModal); m_InstallationProgress.show(); - if (!m_CurrentArchive->extract(ToWString(QDir::toNativeSeparators(targetDirectory)).c_str(), new MethodCallback(this, &InstallationManager::updateProgress), new MethodCallback(this, &InstallationManager::updateProgressFile), diff --git a/src/main.cpp b/src/main.cpp index 5053d21d..8b74ba83 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -60,7 +60,6 @@ along with Mod Organizer. If not, see . #include "selectiondialog.h" #include "moapplication.h" #include "tutorialmanager.h" -#include #include #include #include @@ -156,6 +155,7 @@ bool bootstrap() // verify the hook-dll exists QString dllName = qApp->applicationDirPath() + "/" + ToQString(AppConfig::hookDLLName()); + HMODULE dllMod = ::LoadLibraryW(ToWString(dllName).c_str()); if (dllMod == NULL) { throw windows_error("hook.dll is missing or invalid"); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index f726757c..a8026b68 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -188,8 +188,13 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget ui->modList->header()->installEventFilter(&m_ModList); if (initSettings.contains("mod_list_state")) { ui->modList->header()->restoreState(initSettings.value("mod_list_state").toByteArray()); + } else { + // hide these columns by default + ui->modList->header()->setSectionHidden(ModList::COL_MODID, true); + ui->modList->header()->setSectionHidden(ModList::COL_INSTALLTIME, true); } - ui->modList->header()->setSectionHidden(0, false); // prevent the name-column from being hidden + + ui->modList->header()->setSectionHidden(ModList::COL_NAME, false); // prevent the name-column from being hidden ui->modList->installEventFilter(&m_ModList); // set up plugin list @@ -1364,10 +1369,12 @@ void MainWindow::startExeAction() } -void MainWindow::refreshModList() +void MainWindow::refreshModList(bool saveChanges) { // don't lose changes! - m_CurrentProfile->writeModlistNow(true); + if (saveChanges) { + m_CurrentProfile->writeModlistNow(true); + } ModInfo::updateFromDisc(m_Settings.getModDirectory(), &m_DirectoryStructure); m_CurrentProfile->refreshModStatus(); @@ -2096,6 +2103,32 @@ QStringList MainWindow::findFiles(const QString &path, const std::function MainWindow::findFileInfos(const QString &path, const std::function &filter) const +{ + QList result; + DirectoryEntry *dir = m_DirectoryStructure->findSubDirectoryRecursive(ToWString(path)); + if (dir != NULL) { + std::vector files = dir->getFiles(); + foreach (FileEntry::Ptr file, files) { + FileInfo info; + info.filePath = ToQString(file->getFullPath()); + bool fromArchive = false; + info.origins.append(ToQString(m_DirectoryStructure->getOriginByID(file->getOrigin(fromArchive)).getName())); + info.archive = fromArchive ? ToQString(file->getArchive()) : ""; + foreach (int idx, file->getAlternatives()) { + info.origins.append(ToQString(m_DirectoryStructure->getOriginByID(idx).getName())); + } + + if (filter(info)) { + result.append(info); + } + } + } else { + qWarning("directory %s not found", qPrintable(path)); + } + return result; +} + IDownloadManager *MainWindow::downloadManager() { return &m_DownloadManager; @@ -2161,6 +2194,9 @@ std::vector MainWindow::activeProblems() const if (m_UnloadedPlugins.size() != 0) { problems.push_back(PROBLEM_PLUGINSNOTLOADED); } + if (m_PluginList.enabledCount() > 256) { + problems.push_back(PROBLEM_TOOMANYPLUGINS); + } return problems; } @@ -2170,6 +2206,9 @@ QString MainWindow::shortDescription(unsigned int key) const case PROBLEM_PLUGINSNOTLOADED: { return tr("Some plugins could not be loaded"); } break; + case PROBLEM_TOOMANYPLUGINS: { + return tr("Too many esps and esms enabled"); + } break; default: { return tr("Description missing"); } break; @@ -2187,6 +2226,10 @@ QString MainWindow::fullDescription(unsigned int key) const result += "
    "; return result; } break; + case PROBLEM_TOOMANYPLUGINS: { + return tr("The game doesn't allow more than 256 active plugins (including the official ones) to be loaded. You have to disable some unused plugins or " + "merge some plugins into one. You can find a guide here: http://wiki.step-project.com/Guide:Merging_Plugins"); + } break; default: { return tr("Description missing"); } break; @@ -3195,6 +3238,7 @@ void MainWindow::syncOverwrite() modInfo->testValid(); refreshDirectoryStructure(); } + } void MainWindow::createModFromOverwrite() diff --git a/src/mainwindow.h b/src/mainwindow.h index cf97d4f9..dfd2e571 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -98,7 +98,6 @@ public: void refreshBSAList(); void refreshDataTree(); void refreshSaveList(); - void refreshModList(); void setExecutablesList(const ExecutablesList &executablesList); @@ -135,11 +134,14 @@ public: virtual QString resolvePath(const QString &fileName) const; virtual QStringList listDirectories(const QString &directoryName) const; virtual QStringList findFiles(const QString &path, const std::function &filter) const; + virtual QList findFileInfos(const QString &path, const std::function &filter) const; + virtual MOBase::IDownloadManager *downloadManager(); virtual MOBase::IPluginList *pluginList(); virtual MOBase::IModList *modList(); virtual HANDLE startApplication(const QString &executable, const QStringList &args = QStringList(), const QString &cwd = "", const QString &profile = ""); virtual bool onAboutToRun(const std::function &func); + virtual void refreshModList(bool saveChanges = true); virtual std::vector activeProblems() const; virtual QString shortDescription(unsigned int key) const; @@ -274,6 +276,7 @@ private: private: static const unsigned int PROBLEM_PLUGINSNOTLOADED = 1; + static const unsigned int PROBLEM_TOOMANYPLUGINS = 2; private: diff --git a/src/moapplication.cpp b/src/moapplication.cpp index ba23808b..7e3104db 100644 --- a/src/moapplication.cpp +++ b/src/moapplication.cpp @@ -23,12 +23,15 @@ along with Mod Organizer. If not, see . #include #include #include +#include +#include MOApplication::MOApplication(int argc, char **argv) : QApplication(argc, argv) { connect(&m_StyleWatcher, SIGNAL(fileChanged(QString)), SLOT(updateStyle(QString))); + m_DefaultStyle = style()->objectName(); } @@ -42,11 +45,12 @@ bool MOApplication::setStyleFile(const QString &styleName) // set new stylesheet or clear it if (styleName.length() != 0) { QString styleSheetName = applicationDirPath() + "/" + MOBase::ToQString(AppConfig::stylesheetsPath()) + "/" + styleName; - if (!QFile::exists(styleSheetName)) { - return false; + if (QFile::exists(styleSheetName)) { + m_StyleWatcher.addPath(styleSheetName); + updateStyle(styleSheetName); + } else { + updateStyle(styleName); } - m_StyleWatcher.addPath(styleSheetName); - updateStyle(styleSheetName); } else { setStyleSheet(""); } @@ -74,11 +78,18 @@ bool MOApplication::notify(QObject *receiver, QEvent *event) void MOApplication::updateStyle(const QString &fileName) { - QFile file(fileName); - if (file.open(QFile::ReadOnly)) { - setStyleSheet(file.readAll()); + if (fileName == "Plastique") { + setStyle(new QPlastiqueStyle); + setStyleSheet(""); + } else if (fileName == "Cleanlooks") { + setStyle(new QCleanlooksStyle); + setStyleSheet(""); } else { - qDebug("no stylesheet"); + setStyle(m_DefaultStyle); + if (QFile::exists(fileName)) { + setStyleSheet(QString("file:///%1").arg(fileName)); + } else { + qWarning("invalid stylesheet: %s", qPrintable(fileName)); + } } - file.close(); } diff --git a/src/moapplication.h b/src/moapplication.h index dd7f5eab..645b9144 100644 --- a/src/moapplication.h +++ b/src/moapplication.h @@ -43,6 +43,8 @@ private slots: private: QFileSystemWatcher m_StyleWatcher; + QString m_DefaultStyle; + }; diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui index 2fdbe5f3..d0039a95 100644 --- a/src/modinfodialog.ui +++ b/src/modinfodialog.ui @@ -209,7 +209,7 @@ 0 0 - 668 + 676 126 @@ -223,16 +223,7 @@ 0 - - 0 - - - 0 - - - 0 - - + 0 @@ -293,7 +284,7 @@ Most mods do not have optional esps, so chances are good you are looking at an e - + :/MO/gui/resources/go-up.png:/MO/gui/resources/go-up.png @@ -322,7 +313,7 @@ Most mods do not have optional esps, so chances are good you are looking at an e - + :/MO/gui/resources/go-down.png:/MO/gui/resources/go-down.png @@ -525,9 +516,9 @@ Most mods do not have optional esps, so chances are good you are looking at an e - + - + Primary Category @@ -542,7 +533,7 @@ Most mods do not have optional esps, so chances are good you are looking at an e - + :/MO/gui/resources/internet-web-browser.png:/MO/gui/resources/internet-web-browser.png @@ -634,7 +625,7 @@ p, li { white-space: pre-wrap; } - + :/MO/gui/refresh:/MO/gui/refresh @@ -660,7 +651,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"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:7.8pt; font-weight:400; font-style:normal;"> +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"><br /></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"><br /></p></body></html> @@ -696,7 +687,7 @@ p, li { white-space: pre-wrap; } Endorse - + :/MO/gui/icon_favorite:/MO/gui/icon_favorite @@ -791,8 +782,6 @@ p, li { white-space: pre-wrap; } - - - + diff --git a/src/modlist.cpp b/src/modlist.cpp index eac02372..b35d6ad2 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -186,7 +186,12 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const return QVariant(); } } else if (column == COL_INSTALLTIME) { - return modInfo->creationTime(); + // display installation time for mods that can be updated + if (modInfo->canBeUpdated()) { + return modInfo->creationTime(); + } else { + return QVariant(); + } } else { return tr("invalid"); } diff --git a/src/modlist.h b/src/modlist.h index 8cab7f3e..cc5955d0 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -275,6 +275,10 @@ private: SignalModStateChanged m_ModStateChanged; + + // QAbstractItemModel interface + + // IModList interface }; #endif // MODLIST_H diff --git a/src/organizer.qbs b/src/organizer.qbs index fdfb914c..2b7f45fc 100644 --- a/src/organizer.qbs +++ b/src/organizer.qbs @@ -3,21 +3,41 @@ import qbs.base 1.0 Application { name: 'Organizer' - Depends { name: 'Qt.core' } - Depends { name: 'Qt.gui' } - Depends { name: 'Qt.network' } - Depends { name: 'Qt.declarative' } - Depends { name: 'UIBase' } + Depends { name: "Qt"; submodules: ["core", "gui", "network", "declarative"] } Depends { name: 'Shared' } + Depends { name: 'UIBase' } Depends { name: 'cpp' } - cpp.defines: [] - cpp.includePaths: [ '../shared', '../archive', '../uibase', qbs.getenv("BOOSTPATH") ] + cpp.defines: [ 'UNICODE', '_UNICODE' ] + cpp.libraryPaths: [ qbs.getenv('BOOSTPATH') + '/stage/lib' ] + cpp.includePaths: [ '../shared', '../archive', '../bsatk', '../esptk', '../uibase', qbs.getenv("BOOSTPATH") ] // '../bsatk', '../esptk', - files: [ - '*.cpp', - '*.h', - '*.ui' - ] + cpp.staticLibraries: [ 'shell32', 'user32', 'Version', 'shlwapi' ] + //LIBS += -lmo_shared -luibase -lshell32 -lole32 -luser32 -ladvapi32 -lgdi32 -lPsapi -lVersion -lbsatk -lshlwapi + + Group { + name: 'Headers' + files: [ '*.h' ] + } + + Group { + name: 'Sources' + files: [ '*.cpp' ] + } + + Group { + name: 'UI Files' + files: [ '*.ui' ] + } + + Group { + name: 'ESP Toolkit' + files: [ '../esptk/*.h', '../esptk/*.cpp' ] + } } + +// /nologo /c + + +// /Zi -GR -W3 diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index b0228849..8eeed76e 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -499,6 +499,17 @@ bool PluginList::saveLoadOrder(DirectoryEntry &directoryStructure) return true; } +int PluginList::enabledCount() const +{ + int enabled = 0; + foreach (auto info, m_ESPs) { + if (info.m_Enabled) { + ++enabled; + } + } + return enabled; +} + bool PluginList::isESPLocked(int index) const { return m_LockedOrder.find(m_ESPs.at(index).m_Name.toLower()) != m_LockedOrder.end(); @@ -713,6 +724,8 @@ QVariant PluginList::data(const QModelIndex &modelIndex, int role) const return QIcon(":/MO/gui/warning"); } else if (m_LockedOrder.find(m_ESPs[index].m_Name.toLower()) != m_LockedOrder.end()) { return QIcon(":/MO/gui/locked"); + } else if (m_ESPs[index].m_IsDummy && m_ESPs[index].m_Enabled) { + return QIcon(":/MO/gui/edit_clear"); } else { return QVariant(); } @@ -723,6 +736,8 @@ QVariant PluginList::data(const QModelIndex &modelIndex, int role) const if (m_ESPs[index].m_IsMaster) { result.setItalic(true); result.setWeight(QFont::Bold); + } else if (m_ESPs[index].m_IsDummy) { + result.setItalic(true); } return result; } else if (role == Qt::TextAlignmentRole) { @@ -744,6 +759,10 @@ QVariant PluginList::data(const QModelIndex &modelIndex, int role) const m_ESPs[index].m_MasterUnset.begin(), m_ESPs[index].m_MasterUnset.end(), std::inserter(enabledMasters, enabledMasters.end())); text += "
    " + tr("Enabled Masters") + ": " + SetJoin(enabledMasters, ", "); + if (m_ESPs[index].m_IsDummy) { + text += "
    This file is a dummy! It exists only so the bsa with the same name gets loaded. With MO you don't need this: " + "If you enable the archive with the same name in the \"Archive\" tab you can disable this plugin."; + } return text; } } else { @@ -1009,6 +1028,7 @@ PluginList::ESPInfo::ESPInfo(const QString &name, bool enabled, FILETIME time, c try { ESP::File file(ToWString(fullPath)); m_IsMaster = file.isMaster(); + m_IsDummy = file.isDummy(); std::set masters = file.masters(); for (auto iter = masters.begin(); iter != masters.end(); ++iter) { m_Masters.insert(QString(iter->c_str())); @@ -1016,5 +1036,6 @@ PluginList::ESPInfo::ESPInfo(const QString &name, bool enabled, FILETIME time, c } catch (const std::exception &e) { qCritical("failed to parse esp file %s: %s", qPrintable(fullPath), e.what()); m_IsMaster = false; + m_IsDummy = false; } } diff --git a/src/pluginlist.h b/src/pluginlist.h index 2af5a0df..2f1a3f5f 100644 --- a/src/pluginlist.h +++ b/src/pluginlist.h @@ -126,6 +126,12 @@ public: **/ bool saveLoadOrder(MOShared::DirectoryEntry &directoryStructure); + /** + * @return number of enabled plugins in the list + */ + int enabledCount() const; + + QString getName(int index) const { return m_ESPs.at(index).m_Name; } int getPriority(int index) const { return m_ESPs.at(index).m_Priority; } bool isESPLocked(int index) const; @@ -198,6 +204,7 @@ private: FILETIME m_Time; QString m_OriginName; bool m_IsMaster; + bool m_IsDummy; std::set m_Masters; mutable std::set m_MasterUnset; }; diff --git a/src/settings.cpp b/src/settings.cpp index 36a4f1f8..144049aa 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -422,6 +422,9 @@ void Settings::addLanguages(QComboBox *languageBox) void Settings::addStyles(QComboBox *styleBox) { styleBox->addItem("None", ""); + styleBox->addItem("Plastique", "Plastique"); + styleBox->addItem("Cleanlooks", "Cleanlooks"); + QDirIterator langIter(QCoreApplication::applicationDirPath() + "/" + ToQString(AppConfig::stylesheetsPath()), QStringList("*.qss"), QDir::Files); while (langIter.hasNext()) { langIter.next(); diff --git a/src/shared/shared.qbs b/src/shared/shared.qbs index 44b7539f..1fa471a0 100644 --- a/src/shared/shared.qbs +++ b/src/shared/shared.qbs @@ -1,16 +1,20 @@ import qbs.base 1.0 -Application { - name: 'Shared' +StaticLibrary { + name: { + print(qbs.getenv("BOOSTPATH") + "/stage/lib") + return 'Shared' + } Depends { name: 'cpp' } Depends { name: 'BSAToolkit' } cpp.defines: [] - cpp.libraryPaths: [ qbs.getenv("BOOSTPATH") ] - cpp.includePaths: [ 'bsatk', qbs.getenv("BOOSTPATH") ] + cpp.libraryPaths: [ qbs.getenv("BOOSTPATH") + "/stage/lib" ] + cpp.includePaths: [ '../bsatk', qbs.getenv("BOOSTPATH") ] files: [ '*.h', - '*.cpp' + '*.cpp', + '*.inc' ] } -- cgit v1.3.1 From f1515193ca82c229a712f067f61f1c26e9b83400 Mon Sep 17 00:00:00 2001 From: Tannin Date: Wed, 27 Nov 2013 20:25:24 +0100 Subject: - plugins can now be localized too - fixes to the installer script - extended build scripts to fetch translations from pootle server automatically --- src/mainwindow.cpp | 63 ++++---- src/mainwindow.h | 9 +- src/organizer.pro | 7 +- src/organizer_cs.ts | 407 ++++++++++++++++++++++++------------------------- src/organizer_de.ts | 407 ++++++++++++++++++++++++------------------------- src/organizer_es.ts | 407 ++++++++++++++++++++++++------------------------- src/organizer_fr.ts | 407 ++++++++++++++++++++++++------------------------- src/organizer_ru.ts | 407 ++++++++++++++++++++++++------------------------- src/organizer_tr.ts | 407 ++++++++++++++++++++++++------------------------- src/organizer_zh_CN.ts | 407 ++++++++++++++++++++++++------------------------- src/organizer_zh_TW.ts | 407 ++++++++++++++++++++++++------------------------- src/settings.h | 5 + 12 files changed, 1655 insertions(+), 1685 deletions(-) (limited to 'src/mainwindow.h') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 31ee1ae7..5c487e7a 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -144,8 +144,7 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget m_DirectoryStructure(new DirectoryEntry(L"data", NULL, 0)), m_ModList(this), m_ModListGroupingProxy(NULL), m_ModListSortProxy(NULL), m_PluginList(this), m_OldExecutableIndex(-1), m_GamePath(ToQString(GameInfo::instance().getGameDirectory())), - m_DownloadManager(NexusInterface::instance(), this), - m_InstallationManager(this), m_Translator(NULL), m_TranslatorQt(NULL), + m_DownloadManager(NexusInterface::instance(), this), m_InstallationManager(this), m_Updater(NexusInterface::instance(), this), m_CategoryFactory(CategoryFactory::instance()), m_CurrentProfile(NULL), m_AskForNexusPW(false), m_LoginAttempted(false), m_ArchivesInit(false), m_ContextItem(NULL), m_ContextAction(NULL), m_CurrentSaveView(NULL), @@ -975,7 +974,7 @@ void MainWindow::registerPluginTool(IPluginTool *tool) } -bool MainWindow::registerPlugin(QObject *plugin) +bool MainWindow::registerPlugin(QObject *plugin, const QString &fileName) { { // generic treatment for all plugins IPlugin *pluginObj = qobject_cast(plugin); @@ -983,6 +982,7 @@ bool MainWindow::registerPlugin(QObject *plugin) qDebug("not an IPlugin"); return false; } + plugin->setProperty("filename", fileName); m_Settings.registerPlugin(pluginObj); } @@ -1017,7 +1017,7 @@ bool MainWindow::registerPlugin(QObject *plugin) try { QObject *proxiedPlugin = proxy->instantiate(pluginName); if (proxiedPlugin != NULL) { - if (registerPlugin(proxiedPlugin)) { + if (registerPlugin(proxiedPlugin, pluginName)) { qDebug("loaded plugin \"%s\"", QDir::toNativeSeparators(pluginName).toUtf8().constData()); } else { qWarning("plugin \"%s\" failed to load", pluginName.toUtf8().constData()); @@ -1052,7 +1052,7 @@ void MainWindow::loadPlugins() m_Settings.clearPlugins(); foreach (QObject *plugin, QPluginLoader::staticInstances()) { - registerPlugin(plugin); + registerPlugin(plugin, ""); } QFile loadCheck(QCoreApplication::applicationDirPath() + "/plugin_loadcheck.tmp"); @@ -1095,7 +1095,7 @@ void MainWindow::loadPlugins() qCritical("failed to load plugin %s: %s", pluginName.toUtf8().constData(), pluginLoader.errorString().toUtf8().constData()); } else { - if (registerPlugin(pluginLoader.instance())) { + if (registerPlugin(pluginLoader.instance(), pluginName)) { qDebug("loaded plugin \"%s\"", QDir::toNativeSeparators(pluginName).toUtf8().constData()); } else { m_UnloadedPlugins.push_back(pluginName); @@ -3998,35 +3998,40 @@ void MainWindow::downloadRequested(QNetworkReply *reply, int modID, const QStrin } -void MainWindow::languageChange(const QString &newLanguage) +QTranslator *MainWindow::installTranslator(const QString &name) { - if (m_Translator != NULL) { - QCoreApplication::removeTranslator(m_Translator); - delete m_Translator; - m_Translator = NULL; + QTranslator *translator = new QTranslator(this); + QString fileName = name + "_" + m_CurrentLanguage; + if (!translator->load(fileName, qApp->applicationDirPath() + "/translations")) { + qWarning("localization file %s not found", qPrintable(fileName)); } - if (m_TranslatorQt != NULL) { - QCoreApplication::removeTranslator(m_TranslatorQt); - delete m_TranslatorQt; - m_TranslatorQt = NULL; + qApp->installTranslator(translator); + return translator; +} + + +void MainWindow::languageChange(const QString &newLanguage) +{ + foreach (QTranslator *trans, m_Translators) { + qApp->removeTranslator(trans); } + m_Translators.clear(); - if (newLanguage != "en_US") { - // add our own translations - m_Translator = new QTranslator(this); - QString locFile = ToQString(AppConfig::translationPrefix()) + "_" + newLanguage; - if (!m_Translator->load(locFile, QCoreApplication::applicationDirPath() + "/translations")) { - qDebug("localization %s not found", locFile.toUtf8().constData()); - } - QCoreApplication::installTranslator(m_Translator); + m_CurrentLanguage = newLanguage; - // also add the translations for qt default strings - m_TranslatorQt = new QTranslator(this); - locFile = QString("qt_") + newLanguage; - if (!m_TranslatorQt->load(locFile, QCoreApplication::applicationDirPath() + "/translations")) { - qDebug("localization %s not found", locFile.toUtf8().constData()); + if (newLanguage != "en_US") { + installTranslator("qt"); + installTranslator(ToQString(AppConfig::translationPrefix())); + foreach(IPlugin *plugin, m_Settings.plugins()) { + QObject *pluginObj = dynamic_cast(plugin); + if (pluginObj != NULL) { + QVariant fileNameVariant = pluginObj->property("filename"); + if (fileNameVariant.isValid()) { + QString fileName = QFileInfo(fileNameVariant.toString()).baseName(); + m_Translators.push_back(installTranslator(fileName)); + } + } } - QCoreApplication::installTranslator(m_TranslatorQt); } ui->retranslateUi(this); ui->profileBox->setItemText(0, QObject::tr("")); diff --git a/src/mainwindow.h b/src/mainwindow.h index dfd2e571..5a8278f9 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -197,7 +197,7 @@ private: void actionToToolButton(QAction *&sourceAction); bool verifyPlugin(MOBase::IPlugin *plugin); void registerPluginTool(MOBase::IPluginTool *tool); - bool registerPlugin(QObject *pluginObj); + bool registerPlugin(QObject *pluginObj, const QString &fileName); void updateToolBar(); void activateSelectedProfile(); @@ -272,6 +272,7 @@ private: static void setupNetworkProxy(bool activate); void activateProxy(bool activate); + QTranslator *installTranslator(const QString &name); private: @@ -318,9 +319,6 @@ private: DownloadManager m_DownloadManager; InstallationManager m_InstallationManager; - QTranslator *m_Translator; - QTranslator *m_TranslatorQt; - SelfUpdater m_Updater; CategoryFactory &m_CategoryFactory; @@ -352,6 +350,9 @@ private: SignalAboutToRunApplication m_AboutToRun; + QString m_CurrentLanguage; + std::vector m_Translators; + private slots: void showMessage(const QString &message); diff --git a/src/organizer.pro b/src/organizer.pro index f4be6f80..30454ea8 100644 --- a/src/organizer.pro +++ b/src/organizer.pro @@ -207,13 +207,12 @@ CONFIG += embed_manifest_exe # QMAKE_LFLAGS += /MANIFESTUAC:\"level=\'highestAvailable\' uiAccess=\'false\'\" TRANSLATIONS = organizer_de.ts \ - organizer_es.ts \ - organizer_fr.ts \ - organizer_zh_TW.ts \ + organizer_es.ts \ + organizer_fr.ts \ + organizer_zh_TW.ts \ organizer_zh_CN.ts \ organizer_cs.ts \ organizer_tr.ts \ - organizer_en.ts \ organizer_ru.ts !isEmpty(TRANSLATIONS) { diff --git a/src/organizer_cs.ts b/src/organizer_cs.ts index 10c5f555..59f8b52b 100644 --- a/src/organizer_cs.ts +++ b/src/organizer_cs.ts @@ -267,11 +267,6 @@ p, li { white-space: pre-wrap; } Placeholder #Placeholder - - - KB - - @@ -1563,8 +1558,8 @@ BSAs checked here are loaded in such a way that your installation order is obeye - - + + Refresh Znovunačíst @@ -1744,7 +1739,7 @@ p, li { white-space: pre-wrap; } - + Update @@ -1755,7 +1750,7 @@ p, li { white-space: pre-wrap; } - + No Problems Žádné problémy @@ -1788,7 +1783,7 @@ V současnosti má omezenou funkcionalitu - + Endorse Mod Organizer @@ -1808,17 +1803,17 @@ V současnosti má omezenou funkcionalitu - + Problems Problémy - + There are potential problems with your setup Existují potenciální problémy s programem - + Everything seems to be in order Všechno se jeví v pořádku @@ -1835,22 +1830,22 @@ V současnosti má omezenou funkcionalitu <li>dotNet není nainstalován nebo je neaktuální. Tohle vyžaduje NCC. Najděte ho zde: <a href="%1">%1</a></li> - + Help on UI Pomoc s programem - + Documentation Wiki Dokumentace wiki - + Report Issue Nahlásit chybu - + Tutorials @@ -1859,431 +1854,431 @@ V současnosti má omezenou funkcionalitu pořadí načtení se nezdařilo uložit - + failed to save load order: %1 zlyhalo uložení pořadí načtení: %1 - + failed to save archives order, do you have write access to "%1"? zlyhal zápis pořadí archivů, máte administrátorsky povoleno zapisovat na "%1"? - + Name Jméno - + Please enter a name for the new profile Prosím zadej jméno pro nový profil - + failed to create profile: %1 Zlyhalo vytvoření profilu: %1 - + Show tutorial? - + You are starting Mod Organizer for the first time. Do you want to show a tutorial of its basic features? If you choose no you can always start the tutorial from the "Help"-menu. - + Downloads in progress Probíhá stahování - + There are still downloads in progress, do you really want to quit? Pořád probíhá stahování, určitě chcete skončit (zruší stahování)? - + failed to read savegame: %1 nezdařilo se načíst pozici: %1 - + Plugin "%1" failed: %2 - + Plugin error - + It appears the plugin "%1" failed to load last startup and caused MO to crash. Do you want to disable it? (Please note: If this is the first time you see this message for this plugin you may want to give it another try. The plugin may be able to recover from the problem) - + Failed to start "%1" Zlyhal start "%1" - + Waiting Čekání - + Please press OK once you're logged into steam. Stiskni OK až budeš přihlášen do Steamu. - + "%1" not found "%1" nenalezeno - + Start Steam? Spustit Steam? - + Steam is required to be running already to correctly start the game. Should MO try to start steam now? Steam by měl běžet aby se podařilo spustit hru. Má se MO pokusit spustit steam teď? - + Also in: <br> Také v: <br> - + No conflict Žádné konflikty - + <Edit...> <Edit...> - + Failed to refresh list of esps: %s - + This bsa is enabled in the ini file so it may be required! Tenhle BSA soubor je aktivován v ini souboru, tak zřejmě je vyžadován! - + This archive will still be loaded since there is a plugin of the same name but its files will not follow installation order! Tento archív se stejně načte, protože existuje plugin se stejným jménem ale jeho soubory nebudou zodpovídat pořadí nainstalování! - + Activating Network Proxy - - + + Installation successful Instalace úspěšná - - + + Configure Mod Konfigurace Modu - - + + This mod contains ini tweaks. Do you want to configure them now? Tenhle mod obsahuje ini úpravy. Chcete je nastavovat teď? - - + + mod "%1" not found mod "%1" nenalezen - - + + Installation cancelled Instalace zrušena - - + + The mod was not installed completely. Tento mod se nenainstaloval úplne. - + Some plugins could not be loaded - + Too many esps and esms enabled - - + + Description missing - + Choose Mod Vyber Mod - + Mod Archive Archív Modu - + Start Tutorial? - + You're about to start a tutorial. For technical reasons it's not possible to end the tutorial early. Continue? - - + + Download started Stahování začalo - + failed to update mod list: %1 nezdařilo se aktualizovat seznam modů: %1 - + failed to spawn notepad.exe: %1 zlyhalo otevření notepad.exe: %1 - + failed to open %1 - + failed to change origin name: %1 Nezdařilo se změnit původní jméno: %1 - + Multiple esps activated, please check that they don't conflict. - - + + Create Mod... - + This will move all files from overwrite into a new, regular mod. Please enter a name: - + A mod with this name already exists - + Continue? - + The versioning scheme decides which version is considered newer than another. This function will guess the versioning scheme under the assumption that the installed version is outdated. - + Sorry - + I don't know a versioning scheme where %1 is newer than %2. - + <All> <Všechny> - + <Checked> <Aktivované> - + Plugin "%1" failed - + failed to init plugin %1: %2 - + The following plugins could not be loaded. The reason may be missing dependencies (i.e. python) or an outdated version: - + The game doesn't allow more than 256 active plugins (including the official ones) to be loaded. You have to disable some unused plugins or merge some plugins into one. You can find a guide here: <a href="http://wiki.step-project.com/Guide:Merging_Plugins">http://wiki.step-project.com/Guide:Merging_Plugins</a> - + Failed to move "%1" from mod "%2" to "%3": %4 - + <Unchecked> <Neaktivované> - + <Update> <Aktualizace> - + <No category> <Bez kategorie> - + <Conflicted> - + <Not Endorsed> - + failed to rename mod: %1 Nezdařilo se přejmenovat mod: %1 - + Overwrite? - + This will replace the existing mod "%1". Continue? - + failed to remove mod "%1" - - - + + + failed to rename "%1" to "%2" Nezdařilo se přejmenovat "%1" na "%2" - - - + + + Confirm Potvrdit - + Remove the following mods?<br><ul>%1</ul> - + failed to remove mod: %1 Nezdařilo se odstranit mod: %1 - - + + Failed Zlyhání - + Installation file no longer exists Instalační soubor již neexistuje - + Mods installed with old versions of MO can't be reinstalled in this way. Mody nainstalovány staršími verzemi MO nemůžou být přeinstalovány tímto spůsobem. - - + + You need to be logged in with Nexus to endorse - - + + Extract BSA Extrakce BSA - + This mod contains at least one BSA. Do you want to unpack it? (This removes the BSA after completion. If you don't know about BSAs, just select no) Tento mod obsahuje alespoň jeden BSA soubor. Chcete rozpakovat i jeho obsah? (BSA se po úspěšném rozpakování odstrání. Pokud nevíte, co je BSA, zvolte možnost Ne) - - - + + + failed to read %1: %2 - - + + This archive contains invalid hashes. Some files may be broken. Tento archiv má neplatné identifikační součty. Nekteré soubory mohou být poškozeny. - + Nexus ID for this Mod is unknown Nexus ID pro tento Mod není známo @@ -2296,391 +2291,391 @@ This function will guess the versioning scheme under the assumption that the ins Zvol Prioritu - + Really enable all visible mods? Opravdu aktivovat všechny zobrazené mody? - + Really disable all visible mods? Opravdu deaktivovat všechny zobrazené mody? - + Choose what to export - + Everything - + All installed mods are included in the list - + Active Mods - + Only active (checked) mods from your current profile are included - + Visible - + All mods visible in the mod list are included - + export failed: %1 - + Install Mod... Instaluj Mod... - + Enable all visible Aktivuj všechny v seznamu - + Disable all visible Deaktivuj všechny v seznamu - + Check all for update Skontroluj všechny pro aktualizaci - + Export to csv... - + Sync to Mods... Synchronizuj s Mody... - + Restore Backup - + Remove Backup... - + Set Category Označ Kategorii - + Primary Category - + Change versioning scheme - + Un-ignore update - + Ignore update - + Rename Mod... Přejmenuj Mod... - + Remove Mod... Odstranit Mod... - + Reinstall Mod Přeinstaluj Mod - + Un-Endorse - - + + Endorse - + Won't endorse - + Endorsement state unknown - + Ignore missing data - + Visit on Nexus Navštiv na Nexusu - + Open in explorer Otevři v prohlížeči - + Information... Informace... - - + + Exception: Výnimky: - - + + Unknown exception Neznámá výnimka - + <Multiple> - + Fix Mods... Oprav Mody... - - + + failed to remove %1 - - + + failed to create %1 - + Can't change download directory while downloads are in progress! Není možné změnit cíl pro stahování když probíhá stahování! - + Download failed Stahování zlyhalo - + failed to write to file %1 Nezdařil se zápis do souboru %1 - + %1 written %1 zapsáno - + Select binary Vyber binární soubor - + Binary Soubor - + Enter Name Zadej jméno - + Please enter a name for the executable Prosím zadej jméno pro spouštění - + Not an executable Není spustitelný - + This is not a recognized executable. Tenhle soubor není rozpoznán jako spustitelný. - - + + Replace file? Nahradit soubor? - + There already is a hidden version of this file. Replace it? Už existuje skrytá verze tohto souboru. Nahradit? - - + + File operation failed Operace se souborem zlyhala - - + + Failed to remove "%1". Maybe you lack the required file permissions? Nepodařilo se odstranit "%1". Možná nejsou k dispozici požadována práva? - + There already is a visible version of this file. Replace it? Už existuje viditelná verze tohto souboru. Nahradit? - + Update available Aktualizace k dispozici - + Open/Execute Otevřít/Spustit - + Add as Executable Přidat Spouštení - + Un-Hide Odekrýt - + Hide Skrýt - + Write To File... Zápis do souboru... - + Do you want to endorse Mod Organizer on %1 now? - + Remove - + Unlock load order - + Lock load order - + Request to Nexus failed: %1 - - + + login successful - + login failed: %1. Trying to download anyway přihlášení zlyhalo: %1. Pokouším se beztak stahovat - + login failed: %1 přihlášení zlyhalo: %1 - + login failed: %1. You need to log-in with Nexus to update MO. přihlášení zlyhalo: %1. Na aktualizaci MO je potřebné přihlášení k Nexusu. - + Error Chyba - + failed to extract %1 (errorcode %2) zlyhala extrakce %1 (errorcode %2) - + Extract... Extrakce... - + Edit Categories... Editovat Kategorie... - + Enable all - + Disable all @@ -5349,18 +5344,18 @@ p, li { white-space: pre-wrap; } - + Please use "Help" from the toolbar to get usage instructions to all elements Prosím použijte "Pomoc" z panelu nástrojú pro instrukce ke všem elementům - - + + <Manage...> <Manage...> - + failed to parse profile %1: %2 Nezdařilo se rozebrat profil %1: %2 diff --git a/src/organizer_de.ts b/src/organizer_de.ts index 68af9c3a..a80d3156 100644 --- a/src/organizer_de.ts +++ b/src/organizer_de.ts @@ -267,11 +267,6 @@ p, li { white-space: pre-wrap; } Placeholder Platzhalter - - - KB - - @@ -1567,8 +1562,8 @@ BSAs checked here are loaded in such a way that your installation order is obeye - - + + Refresh Neu laden @@ -1748,7 +1743,7 @@ p, li { white-space: pre-wrap; } - + Update Aktualisierung @@ -1759,7 +1754,7 @@ p, li { white-space: pre-wrap; } - + No Problems Keine Probleme @@ -1790,7 +1785,7 @@ Diese Funktion ist noch in arbeit und ist sehr eingeschränkt. - + Endorse Mod Organizer @@ -1810,17 +1805,17 @@ Diese Funktion ist noch in arbeit und ist sehr eingeschränkt. - + Problems Probleme - + There are potential problems with your setup Es bestehen möglicherweise Probleme mit Ihrer Konfiguration - + Everything seems to be in order Alles in bester Ordnung @@ -1837,22 +1832,22 @@ Diese Funktion ist noch in arbeit und ist sehr eingeschränkt. <li>dotNet ist nicht installiert oder veraltet. Dies wird benötigt um NCC nutzen zu können. Laden Sie die korrekte Version von <a href="%1">%1</a> herunter</li> - + Help on UI Hilfe zur Oberfläche - + Documentation Wiki Wiki Dokumentation - + Report Issue Fehler melden - + Tutorials @@ -1861,431 +1856,431 @@ Diese Funktion ist noch in arbeit und ist sehr eingeschränkt. Ladereihenfolge konnte nicht gespeichert werden - + failed to save load order: %1 Reihenfolge konnt nicht gespeichert werden: %1 - + failed to save archives order, do you have write access to "%1"? - + Name Name - + Please enter a name for the new profile Bitte geben Sie einen Namen für das neue Profil an - + failed to create profile: %1 Erstellen des Profils fehlgeschlagen: %1 - + Show tutorial? - + You are starting Mod Organizer for the first time. Do you want to show a tutorial of its basic features? If you choose no you can always start the tutorial from the "Help"-menu. - + Downloads in progress Download in Bearbeitung - + There are still downloads in progress, do you really want to quit? Es gibt noch unfertige Downloads, wollen Sie wirklich das Programm beenden? - + failed to read savegame: %1 Spielstand konnte nicht gelesen werden: %1 - + Plugin "%1" failed: %2 - + Plugin error - + It appears the plugin "%1" failed to load last startup and caused MO to crash. Do you want to disable it? (Please note: If this is the first time you see this message for this plugin you may want to give it another try. The plugin may be able to recover from the problem) - + Failed to start "%1" - + Waiting Warte - + Please press OK once you're logged into steam. Bitte drücken sie OK sobald sie bei Steam angemeldet sind. - + "%1" not found "%1" nicht gefunden - + Start Steam? - + Steam is required to be running already to correctly start the game. Should MO try to start steam now? Steam muss laufen um das Spiel korrekt zu starten. Soll MO versuchen Steam zu starten? - + Also in: <br> Auch in: <br> - + No conflict Keine Konflikte - + <Edit...> <Bearbeiten...> - + Failed to refresh list of esps: %s - + This bsa is enabled in the ini file so it may be required! Dieses Archiv ist in der ini Konfiguration gelistet, daher ist es möglicherweise erforderlich! - + This archive will still be loaded since there is a plugin of the same name but its files will not follow installation order! Dieses Archiv wird vom Spiel geladen werden da ein Plugin mit dem selben Namen aktiv ist aber die enthaltenen Dateien werden sich nicht an Ihre Installations-Reihenfolge halten! - + Activating Network Proxy - - + + Installation successful Installation erfolgreich - - + + Configure Mod Mod konfigurieren - - + + This mod contains ini tweaks. Do you want to configure them now? Diese Mod enthält Anpassungen für die Ini datei. Wollen Sie diese nun konfigurieren? - - + + mod "%1" not found mod "%1" nicht gefunden - - + + Installation cancelled Installation abgebrochen - - + + The mod was not installed completely. Die mod wurde nicht erfolgreich installiert. - + Some plugins could not be loaded - + Too many esps and esms enabled - - + + Description missing - + Choose Mod Mod wählen - + Mod Archive Mod Archiv - + Start Tutorial? - + You're about to start a tutorial. For technical reasons it's not possible to end the tutorial early. Continue? - - + + Download started Download gestartet - + failed to update mod list: %1 Aktualisieren der Modliste fehlgeschlagen: %1 - + failed to spawn notepad.exe: %1 notepad.exe konnte nicht aufgerufen werden: %1 - + failed to open %1 %1 konnte nicht geöffnet werden - + failed to change origin name: %1 konnte den Namen der Dateiquelle nicht ändern: %1 - + Multiple esps activated, please check that they don't conflict. - - + + Create Mod... - + This will move all files from overwrite into a new, regular mod. Please enter a name: - + A mod with this name already exists - + Continue? - + The versioning scheme decides which version is considered newer than another. This function will guess the versioning scheme under the assumption that the installed version is outdated. - + Sorry - + I don't know a versioning scheme where %1 is newer than %2. - + <All> <Alle> - + <Checked> <Markierte> - + Plugin "%1" failed - + failed to init plugin %1: %2 - + The following plugins could not be loaded. The reason may be missing dependencies (i.e. python) or an outdated version: - + The game doesn't allow more than 256 active plugins (including the official ones) to be loaded. You have to disable some unused plugins or merge some plugins into one. You can find a guide here: <a href="http://wiki.step-project.com/Guide:Merging_Plugins">http://wiki.step-project.com/Guide:Merging_Plugins</a> - + Failed to move "%1" from mod "%2" to "%3": %4 - + <Unchecked> <Nicht markierte> - + <Update> <Update> - + <No category> <Ohne Kategorie> - + <Conflicted> - + <Not Endorsed> - + failed to rename mod: %1 konnte die Mod nicht umbenennen: %1 - + Overwrite? - + This will replace the existing mod "%1". Continue? - + failed to remove mod "%1" - - - + + + failed to rename "%1" to "%2" - - - + + + Confirm Bestätigen - + Remove the following mods?<br><ul>%1</ul> - + failed to remove mod: %1 konnte die mod nicht entfernen: %1 - - + + Failed Fehlgeschlagen - + Installation file no longer exists Installationsdatei existiert nicht mehr - + Mods installed with old versions of MO can't be reinstalled in this way. Mods die mit alten Versionen von MO installiert wurden können nicht auf diese Weise neu-installiert werden. - - + + You need to be logged in with Nexus to endorse - - + + Extract BSA BSA extrahieren - + This mod contains at least one BSA. Do you want to unpack it? (This removes the BSA after completion. If you don't know about BSAs, just select no) Diese mod enthält mindestens eine BSA datei. Soll sie entpackt werden? (Das BSA wird danach gelöscht. Wenn Sie nicht wissen was BSAs sind wählen Sie am besten \"nein\") - - - + + + failed to read %1: %2 - - + + This archive contains invalid hashes. Some files may be broken. Dieses Archiv enthält ungültige Prüfsummen. Einige Dateien sind evtl. defekt. - + Nexus ID for this Mod is unknown Nexus ID für diese Mod unbekannt @@ -2298,391 +2293,391 @@ This function will guess the versioning scheme under the assumption that the ins Priorität wählen - + Really enable all visible mods? Alle angezeigten Mods aktivieren? - + Really disable all visible mods? Alle angezeigten Mods deaktivieren? - + Choose what to export - + Everything - + All installed mods are included in the list - + Active Mods - + Only active (checked) mods from your current profile are included - + Visible - + All mods visible in the mod list are included - + export failed: %1 - + Install Mod... Mod installieren... - + Enable all visible Alle sichtbaren aktvieren - + Disable all visible Alle sichtbaren deaktvieren - + Check all for update Alle auf Aktualisierungen überprüfen - + Export to csv... - + Sync to Mods... Mods synchronisieren... - + Restore Backup - + Remove Backup... - + Set Category Kategorie festlegen - + Primary Category - + Change versioning scheme - + Un-ignore update - + Ignore update - + Rename Mod... Mod umbenennen... - + Remove Mod... Mod entfernen... - + Reinstall Mod Mod neu installieren - + Un-Endorse - - + + Endorse - + Won't endorse - + Endorsement state unknown - + Ignore missing data - + Visit on Nexus Auf Nexus besuchen - + Open in explorer In Explorer öffnen - + Information... Informationen... - - + + Exception: Ausnahme: - - + + Unknown exception Unbekannte Ausnahme - + <Multiple> - + Fix Mods... Mods reparieren... - - + + failed to remove %1 %1 konnte nicht entfernt werden - - + + failed to create %1 %1 konnte nicht erstellt werden - + Can't change download directory while downloads are in progress! Das download verzeichnis kann nicht geändert werden solange Downloads laufen! - + Download failed Download fehlgeschlagen - + failed to write to file %1 Speichern in Datei "%1" fehlgeschlagen - + %1 written "%1" gespeichert - + Select binary Binary wählen - + Binary Ausführbare Datei - + Enter Name Namen eingeben - + Please enter a name for the executable Bitte geben Sie einen Namen für die Anwendungsdatei ein - + Not an executable Datei ist nicht ausführbar - + This is not a recognized executable. - - + + Replace file? Datei ersetzen? - + There already is a hidden version of this file. Replace it? Es existiert bereits eine versteckte Variante von dieser Datei. Soll diese ersetzt werden? - - + + File operation failed Dateioperation fehlgeschlagen - - + + Failed to remove "%1". Maybe you lack the required file permissions? Konnte "%1" nicht löschen. Fehlen Ihnen evtl. die nötigen Berechtigungen? - + There already is a visible version of this file. Replace it? Es existiert bereits eine sichtbare Variante dieser Datei. Soll diese ersetzt werden? - + Update available Aktualisierung verfügbar - + Open/Execute Öffnen/Ausführen - + Add as Executable Als Anwendung hinzufügen - + Un-Hide Sichtbar machen - + Hide Verstecken - + Write To File... In Datei speichern... - + Do you want to endorse Mod Organizer on %1 now? - + Remove - + Unlock load order - + Lock load order - + Request to Nexus failed: %1 - - + + login successful - + login failed: %1. Trying to download anyway login fehlgeschlagen: %1. Der Download scheitert vermutlich - + login failed: %1 login fehlgeschlagen: %1 - + login failed: %1. You need to log-in with Nexus to update MO. - + Error Fehler - + failed to extract %1 (errorcode %2) konnte "%1" nicht extrahieren (fehlercode %2) - + Extract... Extrahieren... - + Edit Categories... - + Enable all - + Disable all @@ -5454,7 +5449,7 @@ p, li { white-space: pre-wrap; } "%1" nicht gefunden - + Please use "Help" from the toolbar to get usage instructions to all elements Bitte verwenden Sie die "Hilfe" Funktion um Hinweise zu Nutzung aller Elemete zu bekommen @@ -5473,13 +5468,13 @@ p, li { white-space: pre-wrap; } Konnte "%1" nicht finden - - + + <Manage...> <Verwalten...> - + failed to parse profile %1: %2 Konnte Profil %1 nicht verarbeiten: %2 diff --git a/src/organizer_es.ts b/src/organizer_es.ts index ed278075..ba096644 100644 --- a/src/organizer_es.ts +++ b/src/organizer_es.ts @@ -260,11 +260,6 @@ p, li { white-space: pre-wrap; } Placeholder Marcador de posicion - - - KB - - @@ -1426,8 +1421,8 @@ BSAs checked here are loaded in such a way that your installation order is obeye - - + + Refresh Recargar @@ -1601,7 +1596,7 @@ p, li { white-space: pre-wrap; } - + Update @@ -1612,7 +1607,7 @@ p, li { white-space: pre-wrap; } - + No Problems @@ -1642,7 +1637,7 @@ Right now this has very limited functionality - + Endorse Mod Organizer @@ -1662,465 +1657,465 @@ Right now this has very limited functionality - + Problems - + There are potential problems with your setup - + Everything seems to be in order - + Help on UI - + Documentation Wiki - + Report Issue - + Tutorials - + failed to save load order: %1 Fallo guardando el orden de carga: %1 - + failed to save archives order, do you have write access to "%1"? - + Name Nombre - + Please enter a name for the new profile Por favor introduzca un nombre para el nuevo perfil - + failed to create profile: %1 Fallo al crear el perfil: %1 - + Show tutorial? - + You are starting Mod Organizer for the first time. Do you want to show a tutorial of its basic features? If you choose no you can always start the tutorial from the "Help"-menu. - + Downloads in progress Descarga en progreso - + There are still downloads in progress, do you really want to quit? Aun hay descargas en progreso, estas seguro que quieres salir? - + failed to read savegame: %1 Fallo al leer la partida guardada: %1 - + Plugin "%1" failed: %2 - + Plugin error - + It appears the plugin "%1" failed to load last startup and caused MO to crash. Do you want to disable it? (Please note: If this is the first time you see this message for this plugin you may want to give it another try. The plugin may be able to recover from the problem) - + Failed to start "%1" - + Waiting - + Please press OK once you're logged into steam. - + "%1" not found "%1" no encontrado - + Start Steam? - + Steam is required to be running already to correctly start the game. Should MO try to start steam now? - + Also in: <br> - + No conflict Sin conflictos - + <Edit...> <Editar...> - + Failed to refresh list of esps: %s - + This bsa is enabled in the ini file so it may be required! - + This archive will still be loaded since there is a plugin of the same name but its files will not follow installation order! - + Activating Network Proxy - - + + Installation successful Instalacion completada - - + + Configure Mod Configurar Mod - - + + This mod contains ini tweaks. Do you want to configure them now? Este mod contiene modificaciones del Ini. Quieres configurarlas ahora? - - + + mod "%1" not found - - + + Installation cancelled - - + + The mod was not installed completely. - + Some plugins could not be loaded - + Too many esps and esms enabled - - + + Description missing - + Choose Mod Seleccione Mod - + Mod Archive Fichero Mod - + Start Tutorial? - + You're about to start a tutorial. For technical reasons it's not possible to end the tutorial early. Continue? - - + + Download started Descarga comenzada - + failed to update mod list: %1 Fallo al actualizar la lista de Mods: %1 - + failed to spawn notepad.exe: %1 Fallo al cargar el Bloc de notas: %1 - + failed to open %1 Fallo al abrir %1 - + failed to change origin name: %1 fallo al cambiar el nombre original del fichero %1 - + Multiple esps activated, please check that they don't conflict. - - + + Create Mod... - + This will move all files from overwrite into a new, regular mod. Please enter a name: - + A mod with this name already exists - + Continue? - + The versioning scheme decides which version is considered newer than another. This function will guess the versioning scheme under the assumption that the installed version is outdated. - + Sorry - + I don't know a versioning scheme where %1 is newer than %2. - + <All> <Todos> - + <Checked> <Marcado> - + Plugin "%1" failed - + failed to init plugin %1: %2 - + The following plugins could not be loaded. The reason may be missing dependencies (i.e. python) or an outdated version: - + The game doesn't allow more than 256 active plugins (including the official ones) to be loaded. You have to disable some unused plugins or merge some plugins into one. You can find a guide here: <a href="http://wiki.step-project.com/Guide:Merging_Plugins">http://wiki.step-project.com/Guide:Merging_Plugins</a> - + Failed to move "%1" from mod "%2" to "%3": %4 - + <Unchecked> <Desmarcado> - + <Update> <Actualizacion> - + <No category> - + <Conflicted> - + <Not Endorsed> - + failed to rename mod: %1 fallo al cambiar el nombre al mod: %1 - + Overwrite? - + This will replace the existing mod "%1". Continue? - + failed to remove mod "%1" - - - + + + failed to rename "%1" to "%2" - - - + + + Confirm Confirma - + Remove the following mods?<br><ul>%1</ul> - + failed to remove mod: %1 - - + + Failed - + Installation file no longer exists - + Mods installed with old versions of MO can't be reinstalled in this way. - - + + You need to be logged in with Nexus to endorse - - + + Extract BSA - + This mod contains at least one BSA. Do you want to unpack it? (This removes the BSA after completion. If you don't know about BSAs, just select no) - - - + + + failed to read %1: %2 - - + + This archive contains invalid hashes. Some files may be broken. - + Nexus ID for this Mod is unknown @@ -2133,391 +2128,391 @@ This function will guess the versioning scheme under the assumption that the ins Selecciona Prioridad - + Really enable all visible mods? - + Really disable all visible mods? - + Choose what to export - + Everything - + All installed mods are included in the list - + Active Mods - + Only active (checked) mods from your current profile are included - + Visible - + All mods visible in the mod list are included - + export failed: %1 - + Install Mod... Instalar Mod... - + Enable all visible Activar todos los visibles - + Disable all visible Desactivar todos los visibles - + Check all for update Buscar Actualizaciones - + Export to csv... - + Sync to Mods... - + Restore Backup - + Remove Backup... - + Set Category Definir Categoria - + Primary Category - + Change versioning scheme - + Un-ignore update - + Ignore update - + Rename Mod... Cambiar nombre... - + Remove Mod... Quitar Mod... - + Reinstall Mod - + Un-Endorse - - + + Endorse - + Won't endorse - + Endorsement state unknown - + Ignore missing data - + Visit on Nexus - + Open in explorer - + Information... Informacion... - - + + Exception: - - + + Unknown exception - + <Multiple> - + Fix Mods... Activar Mods faltantes... - - + + failed to remove %1 Fallo eliminando %1 - - + + failed to create %1 - + Can't change download directory while downloads are in progress! - + Download failed - + failed to write to file %1 Fallo de escritura en el fichero %1 - + %1 written %1 escrito - + Select binary - + Binary Fichero - + Enter Name - + Please enter a name for the executable - + Not an executable - + This is not a recognized executable. - - + + Replace file? - + There already is a hidden version of this file. Replace it? - - + + File operation failed - - + + Failed to remove "%1". Maybe you lack the required file permissions? - + There already is a visible version of this file. Replace it? - + Update available Disponible actualización - + Open/Execute - + Add as Executable - + Un-Hide - + Hide - + Write To File... Escribir al fichero... - + Do you want to endorse Mod Organizer on %1 now? - + Remove - + Unlock load order - + Lock load order - + Request to Nexus failed: %1 - - + + login successful - + login failed: %1. Trying to download anyway - + login failed: %1 - + login failed: %1. You need to log-in with Nexus to update MO. - + Error Error - + failed to extract %1 (errorcode %2) - + Extract... - + Edit Categories... - + Enable all - + Disable all @@ -4820,7 +4815,7 @@ p, li { white-space: pre-wrap; } "%1" no encontrado - + Please use "Help" from the toolbar to get usage instructions to all elements Por favor utilice "Ayuda" en la barra superior para obtener informacion sobre todos los elementos @@ -4839,13 +4834,13 @@ p, li { white-space: pre-wrap; } fallo al encontrar %1 - - + + <Manage...> <Definir...> - + failed to parse profile %1: %2 no se pudo analizar el perfil % 1: %2 diff --git a/src/organizer_fr.ts b/src/organizer_fr.ts index 1bf335ce..58c871d7 100644 --- a/src/organizer_fr.ts +++ b/src/organizer_fr.ts @@ -268,11 +268,6 @@ p, li { white-space: pre-wrap; } Placeholder Signet - - - KB - - @@ -1466,8 +1461,8 @@ BSAs checked here are loaded in such a way that your installation order is obeye - - + + Refresh Actualiser @@ -1647,7 +1642,7 @@ p, li { white-space: pre-wrap; } - + Update @@ -1658,7 +1653,7 @@ p, li { white-space: pre-wrap; } - + No Problems @@ -1688,7 +1683,7 @@ Right now this has very limited functionality - + Endorse Mod Organizer @@ -1708,465 +1703,465 @@ Right now this has very limited functionality - + Problems - + There are potential problems with your setup - + Everything seems to be in order - + Help on UI - + Documentation Wiki - + Report Issue - + Tutorials - + failed to save load order: %1 impossible d'enregistrer l'ordre de chargement: %1 - + failed to save archives order, do you have write access to "%1"? - + Name Nom - + Please enter a name for the new profile Veuillez inscrire un nom pour le nouveau profil - + failed to create profile: %1 impossible de créer le profil: %1 - + Show tutorial? - + You are starting Mod Organizer for the first time. Do you want to show a tutorial of its basic features? If you choose no you can always start the tutorial from the "Help"-menu. - + Downloads in progress Téléchargements en cours - + There are still downloads in progress, do you really want to quit? Il encore des téléchargements en cours, voulez-vous vraiment quitter? - + failed to read savegame: %1 impossible de lire la sauvegarde: %1 - + Plugin "%1" failed: %2 - + Plugin error - + It appears the plugin "%1" failed to load last startup and caused MO to crash. Do you want to disable it? (Please note: If this is the first time you see this message for this plugin you may want to give it another try. The plugin may be able to recover from the problem) - + Failed to start "%1" - + Waiting Attente - + Please press OK once you're logged into steam. Veuillez cliquer OK une fois connecté à steam. - + "%1" not found "%1" introuvable - + Start Steam? - + Steam is required to be running already to correctly start the game. Should MO try to start steam now? - + Also in: <br> - + No conflict Aucun conflit - + <Edit...> <Modifier...> - + Failed to refresh list of esps: %s - + This bsa is enabled in the ini file so it may be required! - + This archive will still be loaded since there is a plugin of the same name but its files will not follow installation order! - + Activating Network Proxy - - + + Installation successful Installation réussie - - + + Configure Mod Configurer mod - - + + This mod contains ini tweaks. Do you want to configure them now? Ce mod contient des ajustement pour les fichiers ini. Désirez-vous les configurer maintenant? - - + + mod "%1" not found - - + + Installation cancelled - - + + The mod was not installed completely. - + Some plugins could not be loaded - + Too many esps and esms enabled - - + + Description missing - + Choose Mod Choisir mod - + Mod Archive Archive de mod - + Start Tutorial? - + You're about to start a tutorial. For technical reasons it's not possible to end the tutorial early. Continue? - - + + Download started Téléchargement commencé - + failed to update mod list: %1 impossible de mettre à jour la liste de mods: %1 - + failed to spawn notepad.exe: %1 impossible de lancer notepad.exe: %1 - + failed to open %1 impossible d'ouvrir %1 - + failed to change origin name: %1 impossible de changer le nom d'origine: %1 - + Multiple esps activated, please check that they don't conflict. - - + + Create Mod... - + This will move all files from overwrite into a new, regular mod. Please enter a name: - + A mod with this name already exists - + Continue? - + The versioning scheme decides which version is considered newer than another. This function will guess the versioning scheme under the assumption that the installed version is outdated. - + Sorry - + I don't know a versioning scheme where %1 is newer than %2. - + <All> <Tous> - + <Checked> <Cochés> - + Plugin "%1" failed - + failed to init plugin %1: %2 - + The following plugins could not be loaded. The reason may be missing dependencies (i.e. python) or an outdated version: - + The game doesn't allow more than 256 active plugins (including the official ones) to be loaded. You have to disable some unused plugins or merge some plugins into one. You can find a guide here: <a href="http://wiki.step-project.com/Guide:Merging_Plugins">http://wiki.step-project.com/Guide:Merging_Plugins</a> - + Failed to move "%1" from mod "%2" to "%3": %4 - + <Unchecked> <Décochés> - + <Update> <Rafraichir> - + <No category> - + <Conflicted> - + <Not Endorsed> - + failed to rename mod: %1 impossible de renommer le mod: %1 - + Overwrite? - + This will replace the existing mod "%1". Continue? - + failed to remove mod "%1" - - - + + + failed to rename "%1" to "%2" - - - + + + Confirm Confirmer - + Remove the following mods?<br><ul>%1</ul> - + failed to remove mod: %1 - - + + Failed - + Installation file no longer exists - + Mods installed with old versions of MO can't be reinstalled in this way. - - + + You need to be logged in with Nexus to endorse - - + + Extract BSA - + This mod contains at least one BSA. Do you want to unpack it? (This removes the BSA after completion. If you don't know about BSAs, just select no) - - - + + + failed to read %1: %2 Échec de lecture %1: %2 - - + + This archive contains invalid hashes. Some files may be broken. - + Nexus ID for this Mod is unknown @@ -2179,391 +2174,391 @@ This function will guess the versioning scheme under the assumption that the ins Choisir priorité - + Really enable all visible mods? - + Really disable all visible mods? - + Choose what to export - + Everything - + All installed mods are included in the list - + Active Mods - + Only active (checked) mods from your current profile are included - + Visible - + All mods visible in the mod list are included - + export failed: %1 - + Install Mod... Installer mod... - + Enable all visible Activer tous les mods visibles - + Disable all visible Désactiver tous les mods visibles - + Check all for update Vérifier toutes les mises à jour - + Export to csv... - + Sync to Mods... - + Restore Backup - + Remove Backup... - + Set Category Assigner catégorie - + Primary Category - + Change versioning scheme - + Un-ignore update - + Ignore update - + Rename Mod... Renommer mod... - + Remove Mod... Supprimer mod... - + Reinstall Mod - + Un-Endorse - - + + Endorse - + Won't endorse - + Endorsement state unknown - + Ignore missing data - + Visit on Nexus - + Open in explorer - + Information... Information... - - + + Exception: - - + + Unknown exception - + <Multiple> - + Fix Mods... Réparer mods... - - + + failed to remove %1 Impossible de supprimer %1 - - + + failed to create %1 impossible de créer %1 - + Can't change download directory while downloads are in progress! - + Download failed - + failed to write to file %1 impossible d'écrire dans le fichier %1 - + %1 written %1 écrit - + Select binary - + Binary Programme - + Enter Name - + Please enter a name for the executable - + Not an executable - + This is not a recognized executable. - - + + Replace file? - + There already is a hidden version of this file. Replace it? - - + + File operation failed - - + + Failed to remove "%1". Maybe you lack the required file permissions? - + There already is a visible version of this file. Replace it? - + Update available Mise à jour disponible - + Open/Execute - + Add as Executable - + Un-Hide - + Hide - + Write To File... Écriture du fichier... - + Do you want to endorse Mod Organizer on %1 now? - + Remove Supprimer - + Unlock load order - + Lock load order - + Request to Nexus failed: %1 - - + + login successful - + login failed: %1. Trying to download anyway - + login failed: %1 - + login failed: %1. You need to log-in with Nexus to update MO. - + Error Erreur - + failed to extract %1 (errorcode %2) - + Extract... - + Edit Categories... - + Enable all - + Disable all @@ -4929,18 +4924,18 @@ p, li { white-space: pre-wrap; } "%1" introuvable - + Please use "Help" from the toolbar to get usage instructions to all elements Veuillez utiliser l'aide dans la barre d'outil pour obtenir des instructions à propos de tous les éléments - - + + <Manage...> <Gérer...> - + failed to parse profile %1: %2 impossible d'analyser le profil %1: %2 diff --git a/src/organizer_ru.ts b/src/organizer_ru.ts index d377c1e0..f7518508 100644 --- a/src/organizer_ru.ts +++ b/src/organizer_ru.ts @@ -263,11 +263,6 @@ p, li { white-space: pre-wrap; } Placeholder Заполнитель - - - KB - - @@ -1359,8 +1354,8 @@ BSA, отмеченные здесь, загружаются так, чтобы - - + + Refresh Обновить @@ -1540,7 +1535,7 @@ p, li { white-space: pre-wrap; } - + Update Обновление @@ -1551,7 +1546,7 @@ p, li { white-space: pre-wrap; } - + No Problems Проблем не обнаружено @@ -1584,7 +1579,7 @@ Right now this has very limited functionality - + Endorse Mod Organizer Одобрить Mod Organizer @@ -1604,219 +1599,219 @@ Right now this has very limited functionality Меню Пуск - + Problems Проблемы - + There are potential problems with your setup Есть возможные проблемы с вашей установкой - + Everything seems to be in order Кажется всё в порядке - + Help on UI Справка по интерфейсу - + Documentation Wiki Wiki-документация - + Report Issue Сообщить о проблеме - + Tutorials Уроки - + failed to save archives order, do you have write access to "%1"? не удалось сохранить порядок архивов, у вас имеется доступ на запись к "%1"? - + failed to save load order: %1 не удалось сохранить порядок загрузки: %1 - + Name Имя - + Please enter a name for the new profile Введите имя нового профиля - + failed to create profile: %1 не удалось создать профиль: %1 - + Show tutorial? Показать урок? - + You are starting Mod Organizer for the first time. Do you want to show a tutorial of its basic features? If you choose no you can always start the tutorial from the "Help"-menu. Вы запустили Mod Organizer впервые. Вы хотите просмотреть уроки по основным возможностям? В случае отказа вы всегда можете открыть уроки из меню "Помощь". - + Downloads in progress Загрузки в процессе - + There are still downloads in progress, do you really want to quit? Загрузки всё ещё в процессе, вы правда хотите выйти? - + failed to read savegame: %1 не удалось прочесть сохранение: %1 - + Plugin "%1" failed: %2 Плагин "%1" не удалось: %2 - + Plugin "%1" failed Плагин "%1" не удалось - + failed to init plugin %1: %2 не удалось инициализировать плагин %1: %2 - + Plugin error - + It appears the plugin "%1" failed to load last startup and caused MO to crash. Do you want to disable it? (Please note: If this is the first time you see this message for this plugin you may want to give it another try. The plugin may be able to recover from the problem) - + Failed to start "%1" Не удалось запустить "%1" - + Waiting Ожидание - + Please press OK once you're logged into steam. Нажмите OK как только вы войдете в Steam. - + "%1" not found "%1" не найден - + Start Steam? Запустить Steam? - + Steam is required to be running already to correctly start the game. Should MO try to start steam now? Требуется запущенный Steam, для корректного запуска игры. Должен ли MO попытаться запустить Steam сейчас? - + Also in: <br> Также в: <br> - + No conflict Конфликтов нет - + <Edit...> <Правка...> - + Failed to refresh list of esps: %s - + This bsa is enabled in the ini file so it may be required! Этот bsa подключен через ini, так что он может быть необходим! - + This archive will still be loaded since there is a plugin of the same name but its files will not follow installation order! Этот архив все равно будет загружен, так как есть плагин с одноименным названием, но его файлы не будут следовать порядку установки! - + Activating Network Proxy Подключение сетевого прокси - - + + Installation successful Установка завершена - - + + Configure Mod Настройка мода - - + + This mod contains ini tweaks. Do you want to configure them now? Этот мод включает настройки ini. Вы хотите настроить их сейчас? - - + + mod "%1" not found мод "%1" не найден - - + + Installation cancelled Установка отменена - - + + The mod was not installed completely. Мод не был установлен полностью. - + Some plugins could not be loaded Некоторые плагины не могут быть загружены @@ -1825,639 +1820,639 @@ Right now this has very limited functionality Следующие плагины не могут быть загружены. Причина может быть в отсутствующих зависимостях (таких как python) или в устаревшей версии:<ul> - + Too many esps and esms enabled - - + + Description missing - + The following plugins could not be loaded. The reason may be missing dependencies (i.e. python) or an outdated version: - + The game doesn't allow more than 256 active plugins (including the official ones) to be loaded. You have to disable some unused plugins or merge some plugins into one. You can find a guide here: <a href="http://wiki.step-project.com/Guide:Merging_Plugins">http://wiki.step-project.com/Guide:Merging_Plugins</a> - + Choose Mod Выберете мод - + Mod Archive Архив мода - + Start Tutorial? Начать обучение? - + You're about to start a tutorial. For technical reasons it's not possible to end the tutorial early. Continue? Вы собираетесь открыть обучение. По техническим причинам будет невозможно досрочно закончить обучение. Продолжить? - - + + Download started Загрузка начата - + failed to update mod list: %1 не удалось обновить список модов: %1 - + failed to spawn notepad.exe: %1 не удалось вызвать notepad.exe: %1 - + failed to open %1 не удалось открыть %1 - + failed to change origin name: %1 не удалось изменить оригинальное имя: %1 - + Failed to move "%1" from mod "%2" to "%3": %4 - + <Checked> <Подключен> - + <Unchecked> <Отключен> - + <Update> <Обновлен> - + <No category> <Без категории> - + <Conflicted> <Конфликтует> - + <Not Endorsed> - + failed to rename mod: %1 не удалось переименовать мод: %1 - + Overwrite? Перезаписать? - + This will replace the existing mod "%1". Continue? Это заменит существующий мод "%1". Продолжить? - + failed to remove mod "%1" не удалось удалить мод "%1" - - - + + + failed to rename "%1" to "%2" не удалось переименовать "%1" в "%2" - + Multiple esps activated, please check that they don't conflict. Подключено несколько esp, выберете из них не конфликтующие. - - - + + + Confirm Подтверждение - + Remove the following mods?<br><ul>%1</ul> Удалить следующие моды?<br><ul>%1</ul> - + failed to remove mod: %1 не удалось удалить мод: %1 - - + + Failed Неудача - + Installation file no longer exists Установочный файл больше не существует - + Mods installed with old versions of MO can't be reinstalled in this way. Моды, установленные с использованием старых версий MO не могут быть перестановленны таким образом. - - + + You need to be logged in with Nexus to endorse Вы должны быть авторизированы на Nexus, чтобы одобрять. - - + + Extract BSA Извлечь BSA - + This mod contains at least one BSA. Do you want to unpack it? (This removes the BSA after completion. If you don't know about BSAs, just select no) Этот мод включает как минимум один BSA. Вы хотите распаковать их? (Это удалит BSA после завершения. Если вы не знаете ничего о BSAs, просто откажитесь) - - - + + + failed to read %1: %2 не удалось прочесть %1: %2 - - + + This archive contains invalid hashes. Some files may be broken. Архив содержит неверные хеши. Некоторые файлы могут быть испорчены. - + Nexus ID for this Mod is unknown Nexus ID для этого мода неизвестен - - + + Create Mod... Создать мод... - + This will move all files from overwrite into a new, regular mod. Please enter a name: Это переместит все файлы от перезаписи в новый, отдельный мод. Пожалуйста, введите имя: - + A mod with this name already exists Мод с таким именем уже существует - + Continue? - + The versioning scheme decides which version is considered newer than another. This function will guess the versioning scheme under the assumption that the installed version is outdated. - + Sorry - + I don't know a versioning scheme where %1 is newer than %2. - + Really enable all visible mods? Действительно подключить все видимые моды? - + Really disable all visible mods? Действительно отключить все видимые моды? - + Choose what to export Выберете, что экспортировать - + Everything Всё - + All installed mods are included in the list Все установленные моды, включенные в список - + Active Mods Активные моды - + Only active (checked) mods from your current profile are included Включены все активные (подключенные) моды вашего текущего профиля - + Visible Видимые - + All mods visible in the mod list are included Включены все моды, видимые в списке модов - + export failed: %1 экспорт не удался: %1 - + Install Mod... Установить мод... - + Enable all visible Включить все видимые - + Disable all visible Отключить все видимые - + Check all for update Проверить все на обновления - + Export to csv... Экспорт в csv... - + Sync to Mods... Синхронизировать с модами... - + Restore Backup Восстановить из резервной копии - + Remove Backup... Удалить резервную копию... - + Set Category Задать категорию - + Primary Category Основная категория - + Change versioning scheme - + Un-ignore update - + Ignore update - + Rename Mod... Переименовать мод... - + Remove Mod... Удалить мод... - + Reinstall Mod Переустановить мод - + Un-Endorse Отменить одобрение - - + + Endorse Одобрить - + Won't endorse Не одобрять - + Endorsement state unknown Статус одобрения неизвестен - + Ignore missing data Игнорировать отсутствующие данные - + Visit on Nexus Перейти на Nexus - + Open in explorer Открыть в проводнике - + Information... Информация... - - + + Exception: Исключение: - - + + Unknown exception Неизвестное исключение - + <All> <Все> - + <Multiple> <Несколько> - + Fix Mods... Исправить моды... - - + + failed to remove %1 не удалось удалить %1 - - + + failed to create %1 не удалось создать %1 - + Can't change download directory while downloads are in progress! Нельзя изменить каталог для загрузок, когда загрузки ещё не завершены! - + Download failed Загрузка не удалась - + failed to write to file %1 ошибка записи в файл %1 - + %1 written %1 записан - + Select binary Выбрать исполняемый файл - + Binary Исполняемый файл - + Enter Name Введите имя - + Please enter a name for the executable Введите название для программы - + Not an executable Не является исполняемым - + This is not a recognized executable. Это неверный исполняемый файл. - - + + Replace file? Заменить файл? - + There already is a hidden version of this file. Replace it? Уже существует скрытая версия этого файла. Заменить? - - + + File operation failed Операция с файлом не удалась - - + + Failed to remove "%1". Maybe you lack the required file permissions? Не удалось удалить "%1". Может быть, вам не хватает необходимых прав доступа к файлу? - + There already is a visible version of this file. Replace it? Видимая версия этого файла уже существует. Заменить? - + Update available Доступно обновление - + Open/Execute Открыть/Выполнить - + Add as Executable Добавить как исполняемый - + Un-Hide Показать - + Hide Скрыть - + Write To File... Записать в файл... - + Do you want to endorse Mod Organizer on %1 now? Вы хотите одобрить Mod Organizer на %1 сейчас? - + Request to Nexus failed: %1 Запрос на Nexus не удался: %1 - - + + login successful успешный вход - + login failed: %1. Trying to download anyway вход не удался: %1. Пытаюсь загрузить всё равно - + login failed: %1 войти не удалось: %1 - + login failed: %1. You need to log-in with Nexus to update MO. войти не удалось: %1. Вам нужно войти на Nexus, чтобы обновить MO. - + Error Ошибка - + failed to extract %1 (errorcode %2) не удалось извлечь %1 (код ошибки %2) - + Extract... Распаковка... - + Edit Categories... Изменить категории... - + Remove - + Enable all Включить все - + Disable all Отключить все - + Unlock load order Разблокировать порядок загрузки - + Lock load order Заблокировать порядок загрузки @@ -4033,18 +4028,18 @@ p, li { white-space: pre-wrap; } - + Please use "Help" from the toolbar to get usage instructions to all elements Используйте пункт "Справка" на панели инструментов, чтобы получить инструкции по использованию всех элементов. - - + + <Manage...> <Управлять...> - + failed to parse profile %1: %2 не удалось обработать профиль %1: %2 diff --git a/src/organizer_tr.ts b/src/organizer_tr.ts index c23ee738..92e0b7b9 100644 --- a/src/organizer_tr.ts +++ b/src/organizer_tr.ts @@ -269,11 +269,6 @@ p, li { white-space: pre-wrap; } Placeholder Yer tutucu - - - KB - - @@ -1508,8 +1503,8 @@ BSAs checked here are loaded in such a way that your installation order is obeye - - + + Refresh @@ -1683,7 +1678,7 @@ p, li { white-space: pre-wrap; } - + Update @@ -1694,7 +1689,7 @@ p, li { white-space: pre-wrap; } - + No Problems @@ -1724,7 +1719,7 @@ Right now this has very limited functionality - + Endorse Mod Organizer @@ -1744,854 +1739,854 @@ Right now this has very limited functionality - + Problems - + There are potential problems with your setup - + Everything seems to be in order - + Help on UI - + Documentation Wiki - + Report Issue - + Tutorials - + failed to save load order: %1 - + failed to save archives order, do you have write access to "%1"? - + Name İsim - + Please enter a name for the new profile - + failed to create profile: %1 - + Show tutorial? - + You are starting Mod Organizer for the first time. Do you want to show a tutorial of its basic features? If you choose no you can always start the tutorial from the "Help"-menu. - + Downloads in progress - + There are still downloads in progress, do you really want to quit? - + failed to read savegame: %1 - + Plugin "%1" failed: %2 - + Failed to start "%1" - + Waiting - + Please press OK once you're logged into steam. - + "%1" not found - + Start Steam? - + Steam is required to be running already to correctly start the game. Should MO try to start steam now? - + Also in: <br> - + No conflict - + <Edit...> - + This bsa is enabled in the ini file so it may be required! - + This archive will still be loaded since there is a plugin of the same name but its files will not follow installation order! - + Activating Network Proxy - - + + Installation successful - - + + Configure Mod - - + + This mod contains ini tweaks. Do you want to configure them now? - - + + mod "%1" not found - - + + Installation cancelled - - + + The mod was not installed completely. - + Some plugins could not be loaded - + Choose Mod - + Mod Archive - + Start Tutorial? - + You're about to start a tutorial. For technical reasons it's not possible to end the tutorial early. Continue? - - + + Download started - + failed to update mod list: %1 - + failed to spawn notepad.exe: %1 - + failed to open %1 - + failed to change origin name: %1 - + Multiple esps activated, please check that they don't conflict. - - + + Create Mod... - + This will move all files from overwrite into a new, regular mod. Please enter a name: - + A mod with this name already exists - + <All> - + <Checked> - + Plugin "%1" failed - + failed to init plugin %1: %2 - + Plugin error - + It appears the plugin "%1" failed to load last startup and caused MO to crash. Do you want to disable it? (Please note: If this is the first time you see this message for this plugin you may want to give it another try. The plugin may be able to recover from the problem) - + Failed to refresh list of esps: %s - + Too many esps and esms enabled - - + + Description missing - + The following plugins could not be loaded. The reason may be missing dependencies (i.e. python) or an outdated version: - + The game doesn't allow more than 256 active plugins (including the official ones) to be loaded. You have to disable some unused plugins or merge some plugins into one. You can find a guide here: <a href="http://wiki.step-project.com/Guide:Merging_Plugins">http://wiki.step-project.com/Guide:Merging_Plugins</a> - + Failed to move "%1" from mod "%2" to "%3": %4 - + <Unchecked> - + <Update> - + <No category> - + <Conflicted> - + <Not Endorsed> - + failed to rename mod: %1 - + Overwrite? - + This will replace the existing mod "%1". Continue? - + failed to remove mod "%1" - - - + + + failed to rename "%1" to "%2" "%1"yi "%2" olarak yeniden adlandırma başarılı olamadı. - - - + + + Confirm Onayla - + Remove the following mods?<br><ul>%1</ul> - + failed to remove mod: %1 - - + + Failed - + Installation file no longer exists - + Mods installed with old versions of MO can't be reinstalled in this way. - - + + You need to be logged in with Nexus to endorse - - + + Extract BSA - + This mod contains at least one BSA. Do you want to unpack it? (This removes the BSA after completion. If you don't know about BSAs, just select no) - - - + + + failed to read %1: %2 %1: %2 okunamadı - - + + This archive contains invalid hashes. Some files may be broken. - + Nexus ID for this Mod is unknown - + Continue? - + The versioning scheme decides which version is considered newer than another. This function will guess the versioning scheme under the assumption that the installed version is outdated. - + Sorry - + I don't know a versioning scheme where %1 is newer than %2. - + Really enable all visible mods? - + Really disable all visible mods? - + Choose what to export - + Everything - + All installed mods are included in the list - + Active Mods - + Only active (checked) mods from your current profile are included - + Visible - + All mods visible in the mod list are included - + export failed: %1 - + Install Mod... - + Enable all visible - + Disable all visible - + Check all for update - + Export to csv... - + Sync to Mods... - + Restore Backup - + Remove Backup... - + Set Category - + Primary Category - + Change versioning scheme - + Un-ignore update - + Ignore update - + Rename Mod... - + Remove Mod... - + Reinstall Mod - + Un-Endorse - - + + Endorse - + Won't endorse - + Endorsement state unknown - + Ignore missing data - + Visit on Nexus - + Open in explorer - + Information... - - + + Exception: - - + + Unknown exception - + <Multiple> - + Fix Mods... - - + + failed to remove %1 - - + + failed to create %1 - + Can't change download directory while downloads are in progress! - + Download failed - + failed to write to file %1 - + %1 written - + Select binary - + Binary İkili değer - + Enter Name - + Please enter a name for the executable - + Not an executable - + This is not a recognized executable. - - + + Replace file? - + There already is a hidden version of this file. Replace it? - - + + File operation failed - - + + Failed to remove "%1". Maybe you lack the required file permissions? - + There already is a visible version of this file. Replace it? - + Update available - + Open/Execute - + Add as Executable - + Un-Hide - + Hide - + Write To File... - + Do you want to endorse Mod Organizer on %1 now? - + Remove - + Unlock load order - + Lock load order - + Request to Nexus failed: %1 - - + + login successful - + login failed: %1. Trying to download anyway - + login failed: %1 - + login failed: %1. You need to log-in with Nexus to update MO. - + Error - + failed to extract %1 (errorcode %2) - + Extract... - + Edit Categories... - + Enable all - + Disable all @@ -4084,18 +4079,18 @@ p, li { white-space: pre-wrap; } - + Please use "Help" from the toolbar to get usage instructions to all elements - - + + <Manage...> - + failed to parse profile %1: %2 diff --git a/src/organizer_zh_CN.ts b/src/organizer_zh_CN.ts index 5ef313be..74918970 100644 --- a/src/organizer_zh_CN.ts +++ b/src/organizer_zh_CN.ts @@ -268,11 +268,6 @@ p, li { white-space: pre-wrap; } Placeholder 占位符 - - - KB - - @@ -1554,8 +1549,8 @@ BSAs checked here are loaded in such a way that your installation order is obeye - - + + Refresh 刷新 @@ -1735,7 +1730,7 @@ p, li { white-space: pre-wrap; } - + Update 更新 @@ -1746,7 +1741,7 @@ p, li { white-space: pre-wrap; } - + No Problems 没有问题 @@ -1779,7 +1774,7 @@ Right now this has very limited functionality - + Endorse Mod Organizer @@ -1799,17 +1794,17 @@ Right now this has very limited functionality - + Problems 问题 - + There are potential problems with your setup 您的安装中存在潜在的问题 - + Everything seems to be in order 一切井然有序 @@ -1826,22 +1821,22 @@ Right now this has very limited functionality <li>.Net 未安装或版本过旧。想要运行 NCC 您必须先安装 .Net,您可以在以下地址中获取: <a href="%1">%1</a></li> - + Help on UI 界面帮助 - + Documentation Wiki 说明文档 (维基) - + Report Issue 报告问题 - + Tutorials @@ -1850,431 +1845,431 @@ Right now this has very limited functionality 无法保存加载顺序 - + failed to save load order: %1 无法保存加载顺序: %1 - + failed to save archives order, do you have write access to "%1"? 无法保存档案顺序,您确定您有权限更改 "%1"? - + Name 名称 - + Please enter a name for the new profile - + failed to create profile: %1 无法创建配置文件: %1 - + Show tutorial? - + You are starting Mod Organizer for the first time. Do you want to show a tutorial of its basic features? If you choose no you can always start the tutorial from the "Help"-menu. - + Downloads in progress 正在下载 - + There are still downloads in progress, do you really want to quit? 仍有正在进行中的下载,您确定要退出吗? - + failed to read savegame: %1 无法读取存档: %1 - + Plugin "%1" failed: %2 - + Plugin error - + It appears the plugin "%1" failed to load last startup and caused MO to crash. Do you want to disable it? (Please note: If this is the first time you see this message for this plugin you may want to give it another try. The plugin may be able to recover from the problem) - + Failed to start "%1" 无法启动 "%1" - + Waiting 稍等 - + Please press OK once you're logged into steam. 当您登录 Steam 时请点击确定。 - + "%1" not found "%1" 未找到 - + Start Steam? 启动 Steam? - + Steam is required to be running already to correctly start the game. Should MO try to start steam now? 想要正确地启动遊戲,Steam 必须处于运行状态。MO 要立即启动 Steam 吗? - + Also in: <br> 也在: <br> - + No conflict 没有冲突 - + <Edit...> <编辑...> - + Failed to refresh list of esps: %s - + This bsa is enabled in the ini file so it may be required! 该 BSA 已在 Ini 文件中启用,因此它可能是必需的。 - + This archive will still be loaded since there is a plugin of the same name but its files will not follow installation order! 此档案还是会被加载,因为存在同名插件。不过它所包含的的文件不会遵循安装顺序! - + Activating Network Proxy - - + + Installation successful 安装成功 - - + + Configure Mod 配置 Mod - - + + This mod contains ini tweaks. Do you want to configure them now? 此 Mod 中包含 Ini 设定文件,您想现在就对它们进行配置吗? - - + + mod "%1" not found Mod "%1" 未找到 - - + + Installation cancelled 安装已取消 - - + + The mod was not installed completely. Mod 没有完全安装。 - + Some plugins could not be loaded - + Too many esps and esms enabled - - + + Description missing - + Choose Mod 选择 Mod - + Mod Archive Mod 压缩包 - + Start Tutorial? - + You're about to start a tutorial. For technical reasons it's not possible to end the tutorial early. Continue? - - + + Download started 开始下载 - + failed to update mod list: %1 无法更新 Mod 列表: %1 - + failed to spawn notepad.exe: %1 无法生成 notepad.exe: %1 - + failed to open %1 无法打开 %1 - + failed to change origin name: %1 无法更改原始文件名: %1 - + Multiple esps activated, please check that they don't conflict. - - + + Create Mod... - + This will move all files from overwrite into a new, regular mod. Please enter a name: - + A mod with this name already exists - + Continue? - + The versioning scheme decides which version is considered newer than another. This function will guess the versioning scheme under the assumption that the installed version is outdated. - + Sorry - + I don't know a versioning scheme where %1 is newer than %2. - + <All> <全部> - + <Checked> <已勾选> - + Plugin "%1" failed - + failed to init plugin %1: %2 - + The following plugins could not be loaded. The reason may be missing dependencies (i.e. python) or an outdated version: - + The game doesn't allow more than 256 active plugins (including the official ones) to be loaded. You have to disable some unused plugins or merge some plugins into one. You can find a guide here: <a href="http://wiki.step-project.com/Guide:Merging_Plugins">http://wiki.step-project.com/Guide:Merging_Plugins</a> - + Failed to move "%1" from mod "%2" to "%3": %4 - + <Unchecked> <未勾选> - + <Update> <有更新> - + <No category> <无类别> - + <Conflicted> - + <Not Endorsed> - + failed to rename mod: %1 无法重命名 Mod: %1 - + Overwrite? - + This will replace the existing mod "%1". Continue? - + failed to remove mod "%1" - - - + + + failed to rename "%1" to "%2" - - - + + + Confirm 确认 - + Remove the following mods?<br><ul>%1</ul> - + failed to remove mod: %1 无法移动 Mod: %1 - - + + Failed 失败 - + Installation file no longer exists 安装文件不复存在 - + Mods installed with old versions of MO can't be reinstalled in this way. 旧版 MO 安装的 Mod 无法使用此方法重新安装。 - - + + You need to be logged in with Nexus to endorse - - + + Extract BSA 解压 BSA - + This mod contains at least one BSA. Do you want to unpack it? (This removes the BSA after completion. If you don't know about BSAs, just select no) 此 Mod 中至少包含一个 BSA。您确定要解压吗? (解压完成后,BSA 文件将会被删除。如果您不了解 BSA 的话,请选择“否”) - - - + + + failed to read %1: %2 无法读取 %1: %2 - - + + This archive contains invalid hashes. Some files may be broken. 压缩包 Hash 值错误。部分文件可能已经损坏。 - + Nexus ID for this Mod is unknown 此 Mod 的N网 ID 未知 @@ -2287,391 +2282,391 @@ This function will guess the versioning scheme under the assumption that the ins 选择优先级 - + Really enable all visible mods? 确定要启用全部可见的 Mod 吗? - + Really disable all visible mods? 确定要禁用全部可见的 Mod 吗? - + Choose what to export - + Everything - + All installed mods are included in the list - + Active Mods - + Only active (checked) mods from your current profile are included - + Visible - + All mods visible in the mod list are included - + export failed: %1 - + Install Mod... 安装 Mod... - + Enable all visible 启用所有可见项目 - + Disable all visible 禁用所有可见项目 - + Check all for update 检查更新 - + Export to csv... - + Sync to Mods... 同步到 Mod... - + Restore Backup - + Remove Backup... - + Set Category 设置类别 - + Primary Category - + Change versioning scheme - + Un-ignore update - + Ignore update - + Rename Mod... 重命名... - + Remove Mod... 移除 Mod... - + Reinstall Mod 重新安装 Mod - + Un-Endorse - - + + Endorse - + Won't endorse - + Endorsement state unknown - + Ignore missing data - + Visit on Nexus 在N网上浏览 - + Open in explorer 在资源管理器中打开 - + Information... 信息... - - + + Exception: 例外: - - + + Unknown exception 未知的例外 - + <Multiple> - + Fix Mods... 修复 Mod... - - + + failed to remove %1 无法删除 %1 - - + + failed to create %1 无法创建 %1 - + Can't change download directory while downloads are in progress! 下载文件时不能修改下载目录! - + Download failed 下载失败 - + failed to write to file %1 无法写入文件 %1 - + %1 written 已写入 %1 - + Select binary 选择可执行文件 - + Binary - + Enter Name 输入名称 - + Please enter a name for the executable 请为程序输入一个名称 - + Not an executable 不是可执行程序 - + This is not a recognized executable. 无法识别的可执行文件 - - + + Replace file? 替换文件? - + There already is a hidden version of this file. Replace it? 已存在同名文件,但该文件被隐藏了。确定要覆盖吗? - - + + File operation failed 文件操作错误 - - + + Failed to remove "%1". Maybe you lack the required file permissions? - + There already is a visible version of this file. Replace it? - + Update available 更新可用 - + Open/Execute 打开/执行 - + Add as Executable 添加为可执行文件 - + Un-Hide 取消隐藏 - + Hide 隐藏 - + Write To File... 写入文件... - + Do you want to endorse Mod Organizer on %1 now? - + Remove 移除 - + Unlock load order - + Lock load order - + Request to Nexus failed: %1 - - + + login successful 登录成功 - + login failed: %1. Trying to download anyway 登录失败: %1,请尝试使用别的方法下载 - + login failed: %1 无法登录: %1 - + login failed: %1. You need to log-in with Nexus to update MO. 登录失败: %1。您需要登录到N网才能更新 MO - + Error 错误 - + failed to extract %1 (errorcode %2) 无法解压 %1 (错误代码 %2) - + Extract... 解压... - + Edit Categories... 编辑类别... - + Enable all 全部启用 - + Disable all 全部禁用 @@ -5303,18 +5298,18 @@ p, li { white-space: pre-wrap; } - + Please use "Help" from the toolbar to get usage instructions to all elements 请使用工具栏上的“帮助”来获得所有元素的使用说明 - - + + <Manage...> <管理...> - + failed to parse profile %1: %2 无法解析配置文件 %1: %2 diff --git a/src/organizer_zh_TW.ts b/src/organizer_zh_TW.ts index 70b41e50..76e0d658 100644 --- a/src/organizer_zh_TW.ts +++ b/src/organizer_zh_TW.ts @@ -268,11 +268,6 @@ p, li { white-space: pre-wrap; } Placeholder 占位符 - - - KB - - @@ -1554,8 +1549,8 @@ BSAs checked here are loaded in such a way that your installation order is obeye - - + + Refresh 重新整理 @@ -1735,7 +1730,7 @@ p, li { white-space: pre-wrap; } - + Update 更新 @@ -1746,7 +1741,7 @@ p, li { white-space: pre-wrap; } - + No Problems 沒有問題 @@ -1779,7 +1774,7 @@ Right now this has very limited functionality - + Endorse Mod Organizer @@ -1799,17 +1794,17 @@ Right now this has very limited functionality - + Problems 問題 - + There are potential problems with your setup 您的安裝中存在潛在的問題 - + Everything seems to be in order 一切井然有序 @@ -1826,22 +1821,22 @@ Right now this has very limited functionality <li>.Net 未安裝或版本過舊。想要運行 NCC 您必須先安裝 .Net,您可以在以下地址中獲取: <a href="%1">%1</a></li> - + Help on UI 介面幫助 - + Documentation Wiki 說明文檔 (維基) - + Report Issue 報告問題 - + Tutorials @@ -1850,431 +1845,431 @@ Right now this has very limited functionality 無法儲存加載順序 - + failed to save load order: %1 無法儲存加載順序: %1 - + failed to save archives order, do you have write access to "%1"? 無法儲存檔案順序,您確定您有權限更改 "%1"? - + Name 名稱 - + Please enter a name for the new profile - + failed to create profile: %1 無法建立配置檔案: %1 - + Show tutorial? - + You are starting Mod Organizer for the first time. Do you want to show a tutorial of its basic features? If you choose no you can always start the tutorial from the "Help"-menu. - + Downloads in progress 正在下載 - + There are still downloads in progress, do you really want to quit? 仍有正在進行中的下載,您確定要退出嗎? - + failed to read savegame: %1 無法讀取存檔: %1 - + Plugin "%1" failed: %2 - + Plugin error - + It appears the plugin "%1" failed to load last startup and caused MO to crash. Do you want to disable it? (Please note: If this is the first time you see this message for this plugin you may want to give it another try. The plugin may be able to recover from the problem) - + Failed to start "%1" 無法啟動 "%1" - + Waiting 稍等 - + Please press OK once you're logged into steam. 當您登入 Steam 時請點擊確定。 - + "%1" not found "%1" 未找到 - + Start Steam? 啟動 Steam? - + Steam is required to be running already to correctly start the game. Should MO try to start steam now? 想要正確地啟動遊戲,Steam 必須處於運行狀態,MO 要立即啟動 Steam 嗎? - + Also in: <br> 也在: <br> - + No conflict 沒有衝突 - + <Edit...> <編輯...> - + Failed to refresh list of esps: %s - + This bsa is enabled in the ini file so it may be required! 該 BSA 已在 Ini 檔案中啟用,因此它可能是必需的。 - + This archive will still be loaded since there is a plugin of the same name but its files will not follow installation order! 此檔案還是會被加載,因為存在同名插件。不過它所包含的的檔案不會遵循安裝順序! - + Activating Network Proxy - - + + Installation successful 安裝成功 - - + + Configure Mod 配置 Mod - - + + This mod contains ini tweaks. Do you want to configure them now? 此 Mod 中包含 Ini 設定檔案,您想現在就對它們進行配置嗎? - - + + mod "%1" not found Mod "%1" 未找到 - - + + Installation cancelled 安裝已取消 - - + + The mod was not installed completely. Mod 沒有完全安裝。 - + Some plugins could not be loaded - + Too many esps and esms enabled - - + + Description missing - + Choose Mod 選擇 Mod - + Mod Archive Mod 壓縮包 - + Start Tutorial? - + You're about to start a tutorial. For technical reasons it's not possible to end the tutorial early. Continue? - - + + Download started 開始下載 - + failed to update mod list: %1 無法更新 Mod 列表: %1 - + failed to spawn notepad.exe: %1 無法生成 notepad.exe: %1 - + failed to open %1 無法開啟 %1 - + failed to change origin name: %1 無法更改原始檔案名: %1 - + Multiple esps activated, please check that they don't conflict. - - + + Create Mod... - + This will move all files from overwrite into a new, regular mod. Please enter a name: - + A mod with this name already exists - + Continue? - + The versioning scheme decides which version is considered newer than another. This function will guess the versioning scheme under the assumption that the installed version is outdated. - + Sorry - + I don't know a versioning scheme where %1 is newer than %2. - + <All> <全部> - + <Checked> <已勾選> - + Plugin "%1" failed - + failed to init plugin %1: %2 - + The following plugins could not be loaded. The reason may be missing dependencies (i.e. python) or an outdated version: - + The game doesn't allow more than 256 active plugins (including the official ones) to be loaded. You have to disable some unused plugins or merge some plugins into one. You can find a guide here: <a href="http://wiki.step-project.com/Guide:Merging_Plugins">http://wiki.step-project.com/Guide:Merging_Plugins</a> - + Failed to move "%1" from mod "%2" to "%3": %4 - + <Unchecked> <未勾選> - + <Update> <有更新> - + <No category> <無類別> - + <Conflicted> - + <Not Endorsed> - + failed to rename mod: %1 無法重新命名 Mod: %1 - + Overwrite? - + This will replace the existing mod "%1". Continue? - + failed to remove mod "%1" - - - + + + failed to rename "%1" to "%2" - - - + + + Confirm 確認 - + Remove the following mods?<br><ul>%1</ul> - + failed to remove mod: %1 無法移動 Mod: %1 - - + + Failed 失敗 - + Installation file no longer exists 安裝檔案不複存在 - + Mods installed with old versions of MO can't be reinstalled in this way. 舊版 MO 安裝的 Mod 無法使用此方法重新安裝。 - - + + You need to be logged in with Nexus to endorse - - + + Extract BSA 解壓 BSA - + This mod contains at least one BSA. Do you want to unpack it? (This removes the BSA after completion. If you don't know about BSAs, just select no) 此 Mod 中至少包含一個 BSA。您確定要解壓嗎? (解壓完成後,BSA 檔案將會被刪除。如果您不瞭解 BSA 的話,請選擇“否”) - - - + + + failed to read %1: %2 無法讀取 %1: %2 - - + + This archive contains invalid hashes. Some files may be broken. 壓縮包 Hash 值錯誤。部分檔案可能已經損壞。 - + Nexus ID for this Mod is unknown 此 Mod 的N網 ID 未知 @@ -2287,391 +2282,391 @@ This function will guess the versioning scheme under the assumption that the ins 選擇優先級 - + Really enable all visible mods? 確定要啟用全部可見的 Mod 嗎? - + Really disable all visible mods? 確定要禁用全部可見的 Mod 嗎? - + Choose what to export - + Everything - + All installed mods are included in the list - + Active Mods - + Only active (checked) mods from your current profile are included - + Visible - + All mods visible in the mod list are included - + export failed: %1 - + Install Mod... 安裝 Mod... - + Enable all visible 啟用所有可見項目 - + Disable all visible 禁用所有可見項目 - + Check all for update 檢查更新 - + Export to csv... - + Sync to Mods... 同步到 Mod... - + Restore Backup - + Remove Backup... - + Set Category 設定類別 - + Primary Category - + Change versioning scheme - + Un-ignore update - + Ignore update - + Rename Mod... 重新命名... - + Remove Mod... 移除 Mod... - + Reinstall Mod 重新安裝 Mod - + Un-Endorse - - + + Endorse - + Won't endorse - + Endorsement state unknown - + Ignore missing data - + Visit on Nexus 在N網上流覽 - + Open in explorer 在檔案總管中開啟 - + Information... 訊息... - - + + Exception: 例外: - - + + Unknown exception 未知的例外 - + <Multiple> - + Fix Mods... 修復 Mod... - - + + failed to remove %1 無法刪除 %1 - - + + failed to create %1 無法建立 %1 - + Can't change download directory while downloads are in progress! 下載檔案時不能修改下載目錄! - + Download failed 下載失敗 - + failed to write to file %1 無法寫入檔案 %1 - + %1 written 已寫入 %1 - + Select binary 選擇可執行檔案 - + Binary - + Enter Name 輸入名稱 - + Please enter a name for the executable 請為程式輸入一個名稱 - + Not an executable 不是可執行程式 - + This is not a recognized executable. 無法識別的可執行檔案 - - + + Replace file? 取代檔案? - + There already is a hidden version of this file. Replace it? 已存在同名檔案,但該檔案被隱藏了。確定要覆蓋嗎? - - + + File operation failed 檔案操作錯誤 - - + + Failed to remove "%1". Maybe you lack the required file permissions? - + There already is a visible version of this file. Replace it? - + Update available 更新可用 - + Open/Execute 開啟/執行 - + Add as Executable 添加為可執行檔案 - + Un-Hide 取消隱藏 - + Hide 隱藏 - + Write To File... 寫入檔案... - + Do you want to endorse Mod Organizer on %1 now? - + Remove 移除 - + Unlock load order - + Lock load order - + Request to Nexus failed: %1 - - + + login successful 登入成功 - + login failed: %1. Trying to download anyway 登入失敗: %1,請嘗試使用別的方法下載 - + login failed: %1 無法登入: %1 - + login failed: %1. You need to log-in with Nexus to update MO. 登入失敗: %1。您需要登入到N網才能更新 MO - + Error 錯誤 - + failed to extract %1 (errorcode %2) 無法解壓 %1 (錯誤代碼 %2) - + Extract... 解壓... - + Edit Categories... 編輯類別... - + Enable all 全部啟用 - + Disable all 全部禁用 @@ -5303,18 +5298,18 @@ p, li { white-space: pre-wrap; } - + Please use "Help" from the toolbar to get usage instructions to all elements 請使用工具列上的“幫助”來獲得所有元素的使用說明 - - + + <Manage...> <管理...> - + failed to parse profile %1: %2 無法解析配置檔案 %1: %2 diff --git a/src/settings.h b/src/settings.h index 51ee6b92..abffc94b 100644 --- a/src/settings.h +++ b/src/settings.h @@ -251,6 +251,11 @@ public: */ bool pluginBlacklisted(const QString &fileName) const; + /** + * @return all loaded MO plugins + */ + std::vector plugins() const { return m_Plugins; } + private: QString obfuscate(const QString &password) const; -- cgit v1.3.1 From daf4ae44faee3e641a743cc9f4e0c56f016faad4 Mon Sep 17 00:00:00 2001 From: Tannin Date: Sun, 1 Dec 2013 23:50:24 +0100 Subject: - added second context menu for changing categories that applies only changes to the selected mods instead of replacing the existing set of categories (thanks to Ross!) - bugfix: category filtering didn't work correctly when grouping was also active --- src/mainwindow.cpp | 109 ++++++++++++++++++++++++++++++++++++++++------- src/mainwindow.h | 22 ++++++++-- src/modlistsortproxy.cpp | 24 +++++++++-- src/modlistsortproxy.h | 9 +--- 4 files changed, 134 insertions(+), 30 deletions(-) (limited to 'src/mainwindow.h') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 31ee1ae7..d5330017 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3309,7 +3309,7 @@ void MainWindow::on_modList_doubleClicked(const QModelIndex &index) } } -bool MainWindow::addCategories(QMenu *menu, int targetID) +bool MainWindow::populateMenuCategories(QMenu *menu, int targetID) { ModInfo::Ptr modInfo = ModInfo::getByIndex(m_ContextRow); const std::set &categories = modInfo->getCategories(); @@ -3338,7 +3338,7 @@ bool MainWindow::addCategories(QMenu *menu, int targetID) targetMenu->addAction(checkableAction.take()); if (m_CategoryFactory.hasChildren(i)) { - if (addCategories(targetMenu, m_CategoryFactory.getCategoryID(i)) || enabled) { + if (populateMenuCategories(targetMenu, m_CategoryFactory.getCategoryID(i)) || enabled) { targetMenu->setIcon(QIcon(":/MO/gui/resources/check.png")); } } @@ -3347,12 +3347,12 @@ bool MainWindow::addCategories(QMenu *menu, int targetID) return childEnabled; } -void MainWindow::saveCategoriesFromMenu(QMenu *menu, int modRow) +void MainWindow::replaceCategoriesFromMenu(QMenu *menu, int modRow) { ModInfo::Ptr modInfo = ModInfo::getByIndex(modRow); foreach (QAction* action, menu->actions()) { if (action->menu() != NULL) { - saveCategoriesFromMenu(action->menu(), modRow); + replaceCategoriesFromMenu(action->menu(), modRow); } else { QWidgetAction *widgetAction = qobject_cast(action); if (widgetAction != NULL) { @@ -3363,8 +3363,35 @@ void MainWindow::saveCategoriesFromMenu(QMenu *menu, int modRow) } } -void MainWindow::saveCategories() +void MainWindow::addRemoveCategoriesFromMenu(QMenu *menu, int modRow) { + if (m_ContextRow != -1 && m_ContextRow != modRow) { + ModInfo::Ptr editedModInfo = ModInfo::getByIndex(m_ContextRow); + foreach (QAction* action, menu->actions()) { + if (action->menu() != NULL) { + addRemoveCategoriesFromMenu(action->menu(), modRow); + } else { + QWidgetAction *widgetAction = qobject_cast(action); + if (widgetAction != NULL) { + QCheckBox *checkbox = qobject_cast(widgetAction->defaultWidget()); + int categoryId = widgetAction->data().toInt(); + bool checkedBefore = editedModInfo->categorySet(categoryId); + bool checkedAfter = checkbox->isChecked(); + + if (checkedBefore != checkedAfter) { // only update if the category was changed on the edited mod + ModInfo::Ptr currentModInfo = ModInfo::getByIndex(modRow); + currentModInfo->setCategory(categoryId, checkedAfter); + } + } + } + } + } else { + //This block shouldn't be reached, but if it is then fall back to replace (context row is invalid or replacing edited mod) + replaceCategoriesFromMenu(menu, modRow); + } +} + +void MainWindow::addRemoveCategories_MenuHandler() { QMenu *menu = qobject_cast(sender()); if (menu == NULL) { qCritical("not a menu?"); @@ -3383,11 +3410,58 @@ void MainWindow::saveCategories() selectedMods.append(temp.data().toString()); if (temp.row() < min) min = temp.row(); if (temp.row() > max) max = temp.row(); - saveCategoriesFromMenu(menu, mapToModel(&m_ModList, selected.at(i)).row()); + // save the currently selected mod for last... then we can use it as a pattern for what is changing... + int modRow = m_ModListSortProxy->mapToSource(selected.at(i)).row(); + if (modRow != m_ContextRow) { + addRemoveCategoriesFromMenu(menu,modRow); + } } - //m_ModList.notifyChange(min, max); + //come back to the currently selected mod, after the others have been set + replaceCategoriesFromMenu(menu, m_ContextRow); + + m_ModList.notifyChange(-1); + + // find mods by their name because indices are invalidated + QAbstractItemModel *model = ui->modList->model(); + Q_FOREACH(const QString &mod, selectedMods) { + QModelIndexList matches = model->match(model->index(0, 0), Qt::DisplayRole, mod, 1, + Qt::MatchFixedString | Qt::MatchCaseSensitive | Qt::MatchRecursive); + if (matches.size() > 0) { + ui->modList->selectionModel()->select(matches.at(0), QItemSelectionModel::Select | QItemSelectionModel::Rows); + } + } + } else { + //For single mod selections, just do a replace + replaceCategoriesFromMenu(menu, m_ContextRow); + m_ModList.notifyChange(m_ContextRow); + } + + refreshFilters(); +} + +void MainWindow::replaceCategories_MenuHandler() { + QMenu *menu = qobject_cast(sender()); + if (menu == NULL) { + qCritical("not a menu?"); + return; + } + + QModelIndexList selected = ui->modList->selectionModel()->selectedRows(); + + if (selected.size() > 0) { + int min = INT_MAX; + int max = INT_MIN; + + QStringList selectedMods; + for (int i = 0; i < selected.size(); ++i) { + QModelIndex temp = mapToModel(&m_ModList, selected.at(i)); + selectedMods.append(temp.data().toString()); + if (temp.row() < min) min = temp.row(); + if (temp.row() > max) max = temp.row(); + replaceCategoriesFromMenu(menu, mapToModel(&m_ModList, selected.at(i)).row()); + } + m_ModList.notifyChange(-1); -// refreshModList(); // find mods by their name because indices are invalidated QAbstractItemModel *model = ui->modList->model(); @@ -3399,7 +3473,8 @@ void MainWindow::saveCategories() } } } else { - saveCategoriesFromMenu(menu, m_ContextRow); + //For single mod selections, just do a replace + replaceCategoriesFromMenu(menu, m_ContextRow); m_ModList.notifyChange(m_ContextRow); } @@ -3567,7 +3642,7 @@ void MainWindow::exportModListCSV() bool enabled = m_CurrentProfile->modEnabled(i); if ((selection.getChoiceData().toInt() == 1) && !enabled) { continue; - } else if ((selection.getChoiceData().toInt() == 2) && !m_ModListSortProxy->filterMatches(info, enabled)) { + } else if ((selection.getChoiceData().toInt() == 2) && !m_ModListSortProxy->filterMatchesMod(info, enabled)) { continue; } std::vector flags = info->getFlags(); @@ -3633,11 +3708,15 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) menu.addAction(tr("Restore Backup"), this, SLOT(restoreBackup_clicked())); menu.addAction(tr("Remove Backup..."), this, SLOT(removeMod_clicked())); } else { - // Set categories is a separate menu connected to a push button. This way it doesn't simply close every time you hover the mouse outside - QMenu *addCategoryMenu = new QMenu(tr("Set Category")); - addCategories(addCategoryMenu, 0); - connect(addCategoryMenu, SIGNAL(aboutToHide()), this, SLOT(saveCategories())); - addMenuAsPushButton(&menu, addCategoryMenu); + QMenu *addRemoveCategoriesMenu = new QMenu(tr("Add/Remove Categories")); + populateMenuCategories(addRemoveCategoriesMenu, 0); + connect(addRemoveCategoriesMenu, SIGNAL(aboutToHide()), this, SLOT(addRemoveCategories_MenuHandler())); + addMenuAsPushButton(&menu, addRemoveCategoriesMenu); + + QMenu *replaceCategoriesMenu = new QMenu(tr("Replace Categories")); + populateMenuCategories(replaceCategoriesMenu, 0); + connect(replaceCategoriesMenu, SIGNAL(aboutToHide()), this, SLOT(replaceCategories_MenuHandler())); + addMenuAsPushButton(&menu, replaceCategoriesMenu); QMenu *primaryCategoryMenu = new QMenu(tr("Primary Category")); connect(primaryCategoryMenu, SIGNAL(aboutToShow()), this, SLOT(addPrimaryCategoryCandidates())); diff --git a/src/mainwindow.h b/src/mainwindow.h index dfd2e571..27a56812 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -230,9 +230,23 @@ private: void refreshFilters(); - void saveCategoriesFromMenu(QMenu *menu, int modRow); + /** + * Sets category selections from menu; for multiple mods, this will only apply + * the changes made in the menu (which is the delta between the current menu selection and the reference mod) + * @param menu the menu after editing by the user + * @param modRow index of the mod to edit + */ + void addRemoveCategoriesFromMenu(QMenu *menu, int modRow); + + /** + * Sets category selections from menu; for multiple mods, this will completely + * replace the current set of categories on each selected with those selected in the menu + * @param menu the menu after editing by the user + * @param modRow index of the mod to edit + */ + void replaceCategoriesFromMenu(QMenu *menu, int modRow); - bool addCategories(QMenu *menu, int targetID); + bool populateMenuCategories(QMenu *menu, int targetID); void updateDownloadListDelegate(); @@ -431,7 +445,9 @@ private slots: void originModified(int originID); - void saveCategories(); + void addRemoveCategories_MenuHandler(); + void replaceCategories_MenuHandler(); + void savePrimaryCategory(); void addPrimaryCategoryCandidates(); diff --git a/src/modlistsortproxy.cpp b/src/modlistsortproxy.cpp index 888ecdb8..4d767230 100644 --- a/src/modlistsortproxy.cpp +++ b/src/modlistsortproxy.cpp @@ -220,7 +220,7 @@ bool ModListSortProxy::hasConflictFlag(const std::vector &flags) } -bool ModListSortProxy::filterMatches(ModInfo::Ptr info, bool enabled) const +bool ModListSortProxy::filterMatchesMod(ModInfo::Ptr info, bool enabled) const { if (!m_CurrentFilter.isEmpty() && !info->name().contains(m_CurrentFilter, Qt::CaseInsensitive)) { @@ -258,7 +258,7 @@ bool ModListSortProxy::filterMatches(ModInfo::Ptr info, bool enabled) const } -bool ModListSortProxy::filterAcceptsRow(int row, const QModelIndex&) const +bool ModListSortProxy::filterAcceptsRow(int row, const QModelIndex &parent) const { if (m_Profile == NULL) { return false; @@ -268,9 +268,25 @@ bool ModListSortProxy::filterAcceptsRow(int row, const QModelIndex&) const qWarning("invalid row idx %d", row); return false; } - bool modEnabled = m_Profile->modEnabled(row); - return filterMatches(ModInfo::getByIndex(row), modEnabled); + QModelIndex idx = sourceModel()->index(row, 0, parent); + if (!idx.isValid()) { + qDebug("invalid index"); + return false; + } + if (idx.isValid() && sourceModel()->hasChildren(idx)) { + for (int i = 0; i < sourceModel()->rowCount(idx); ++i) { + if (filterAcceptsRow(i, idx)) { + return true; + } + } + + return false; + } else { + bool modEnabled = idx.sibling(row, 0).data(Qt::CheckStateRole).toInt() == Qt::Checked; + unsigned int index = idx.data(Qt::UserRole + 1).toInt(); + return filterMatchesMod(ModInfo::getByIndex(index), modEnabled); + } } diff --git a/src/modlistsortproxy.h b/src/modlistsortproxy.h index dd968b9e..3e18ea4e 100644 --- a/src/modlistsortproxy.h +++ b/src/modlistsortproxy.h @@ -52,14 +52,7 @@ public: **/ void disableAllVisible(); - bool filterMatches(ModInfo::Ptr info, bool enabled) const; - -/* - virtual int rowCount( const QModelIndex & parent = QModelIndex() ) const { - int rc = QSortFilterProxyModel::rowCount(parent); - qDebug() << parent << " - " << rc; - return rc; - }*/ + bool filterMatchesMod(ModInfo::Ptr info, bool enabled) const; virtual bool hasChildren ( const QModelIndex & parent = QModelIndex() ) const { return rowCount(parent) > 0; -- cgit v1.3.1 From b32457a7d2af4aeb095308c7f358a7a398ad849e Mon Sep 17 00:00:00 2001 From: Tannin Date: Sat, 7 Dec 2013 16:07:28 +0100 Subject: - minor text fixes - versions without a subminor version are now displayed without it (1.1 instead of 1.1.0) - version compares now prefer the decimal comparison over the traditional --- src/mainwindow.cpp | 6 +++--- src/mainwindow.h | 2 +- src/mainwindow.ui | 57 ++++++------------------------------------------------ src/version.rc | 4 ++-- 4 files changed, 12 insertions(+), 57 deletions(-) (limited to 'src/mainwindow.h') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 5c487e7a..59666a20 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3998,7 +3998,7 @@ void MainWindow::downloadRequested(QNetworkReply *reply, int modID, const QStrin } -QTranslator *MainWindow::installTranslator(const QString &name) +void MainWindow::installTranslator(const QString &name) { QTranslator *translator = new QTranslator(this); QString fileName = name + "_" + m_CurrentLanguage; @@ -4006,7 +4006,7 @@ QTranslator *MainWindow::installTranslator(const QString &name) qWarning("localization file %s not found", qPrintable(fileName)); } qApp->installTranslator(translator); - return translator; + m_Translators.push_back(translator); } @@ -4028,7 +4028,7 @@ void MainWindow::languageChange(const QString &newLanguage) QVariant fileNameVariant = pluginObj->property("filename"); if (fileNameVariant.isValid()) { QString fileName = QFileInfo(fileNameVariant.toString()).baseName(); - m_Translators.push_back(installTranslator(fileName)); + installTranslator(fileName); } } } diff --git a/src/mainwindow.h b/src/mainwindow.h index 5a8278f9..197ac73c 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -272,7 +272,7 @@ private: static void setupNetworkProxy(bool activate); void activateProxy(bool activate); - QTranslator *installTranslator(const QString &name); + void installTranslator(const QString &name); private: diff --git a/src/mainwindow.ui b/src/mainwindow.ui index bc7dc212..73ca868e 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -31,16 +31,7 @@ 4 - - 6 - - - 6 - - - 6 - - + 6 @@ -586,7 +577,7 @@ p, li { white-space: pre-wrap; } QTabWidget::Rounded - 0 + 1 @@ -730,16 +721,7 @@ p, li { white-space: pre-wrap; } Archives - - 6 - - - 6 - - - 6 - - + 6 @@ -819,16 +801,7 @@ BSAs checked here are loaded in such a way that your installation order is obeye Data - - 6 - - - 6 - - - 6 - - + 6 @@ -898,16 +871,7 @@ BSAs checked here are loaded in such a way that your installation order is obeye Saves - - 6 - - - 6 - - - 6 - - + 6 @@ -936,16 +900,7 @@ p, li { white-space: pre-wrap; } Downloads - - 2 - - - 2 - - - 2 - - + 2 diff --git a/src/version.rc b/src/version.rc index ab144909..b91dce85 100644 --- a/src/version.rc +++ b/src/version.rc @@ -1,7 +1,7 @@ #include "Winver.h" -#define VER_FILEVERSION 1,0,8,0 -#define VER_FILEVERSION_STR "1,0,8,0\0" +#define VER_FILEVERSION 1,0,10,0 +#define VER_FILEVERSION_STR "1,0,10,0\0" VS_VERSION_INFO VERSIONINFO FILEVERSION VER_FILEVERSION -- cgit v1.3.1 From db09b806b9e765b5cb49a9a80f74a4440fff3027 Mon Sep 17 00:00:00 2001 From: Tannin Date: Sun, 5 Jan 2014 18:50:56 +0100 Subject: - Mod Organizer can now display most image types (including dds) and txt files from the data tree, presenting a comparison of variants in case of overwritten files --- src/mainwindow.cpp | 46 ++++++++++++++++++ src/mainwindow.h | 4 ++ src/modinfodialog.cpp | 15 ++---- src/organizer.pro | 13 ++++-- src/previewdialog.cpp | 57 +++++++++++++++++++++++ src/previewdialog.h | 36 ++++++++++++++ src/previewdialog.ui | 90 +++++++++++++++++++++++++++++++++++ src/previewgenerator.cpp | 111 ++++++++++++++++++++++++++++++++++++++++++++ src/previewgenerator.h | 51 ++++++++++++++++++++ src/shared/directoryentry.h | 1 + 10 files changed, 408 insertions(+), 16 deletions(-) create mode 100644 src/previewdialog.cpp create mode 100644 src/previewdialog.h create mode 100644 src/previewdialog.ui create mode 100644 src/previewgenerator.cpp create mode 100644 src/previewgenerator.h (limited to 'src/mainwindow.h') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 1f1d5f95..80465fb9 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -55,6 +55,7 @@ along with Mod Organizer. If not, see . #include "gameinfoimpl.h" #include "savetextasdialog.h" #include "problemsdialog.h" +#include "previewdialog.h" #include #include #include @@ -4372,6 +4373,44 @@ void MainWindow::unhideFile() } } +void MainWindow::previewDataFile() +{ + QString fileName = QDir::fromNativeSeparators(m_ContextItem->data(0, Qt::UserRole).toString()); + + // what we have is an absolute path to the file in its actual location (for the primary origin) + // what we want is the path relative to the virtual data directory + + // crude: we search for the next slash after the base mod directory to skip everything up to the data-relative directory + int offset = m_Settings.getModDirectory().size() + 1; + offset = fileName.indexOf("/", offset); + fileName = fileName.mid(offset + 1); + + const FileEntry::Ptr file = m_DirectoryStructure->searchFile(ToWString(fileName), NULL); + + if (file.get() == NULL) { + reportError(tr("file not found: %1").arg(fileName)); + return; + } + + // set up preview dialog + PreviewDialog preview(fileName); + auto addFunc = [&] (int originId) { + FilesOrigin &origin = m_DirectoryStructure->getOriginByID(originId); + QString filePath = QDir::fromNativeSeparators(ToQString(origin.getPath())) + "/" + fileName; + if (QFile::exists(filePath)) { + // it's very possible the file doesn't exist, because it's inside an archive. we don't support that + preview.addVariant(ToQString(origin.getName()), m_PreviewGenerator.genPreview(filePath)); + } + }; + + addFunc(file->getOrigin()); + foreach (int i, file->getAlternatives()) { + addFunc(i); + } + if (preview.numVariants() > 0) { + preview.exec(); + } +} void MainWindow::openDataFile() { @@ -4439,6 +4478,12 @@ void MainWindow::on_dataTree_customContextMenuRequested(const QPoint &pos) if ((m_ContextItem != NULL) && (m_ContextItem->childCount() == 0)) { menu.addAction(tr("Open/Execute"), this, SLOT(openDataFile())); menu.addAction(tr("Add as Executable"), this, SLOT(addAsExecutable())); + + QString fileName = m_ContextItem->text(0); + if (m_PreviewGenerator.previewSupported(QFileInfo(fileName).completeSuffix())) { + menu.addAction(tr("Preview"), this, SLOT(previewDataFile())); + } + // offer to hide/unhide file, but not for files from archives if (!m_ContextItem->data(0, Qt::UserRole + 1).toBool()) { if (m_ContextItem->text(0).endsWith(ModInfo::s_HiddenExt)) { @@ -4447,6 +4492,7 @@ void MainWindow::on_dataTree_customContextMenuRequested(const QPoint &pos) menu.addAction(tr("Hide"), this, SLOT(hideFile())); } } + menu.addSeparator(); } menu.addAction(tr("Write To File..."), this, SLOT(writeDataToFile())); diff --git a/src/mainwindow.h b/src/mainwindow.h index 31fbeef3..21c121fb 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -48,6 +48,7 @@ along with Mod Organizer. If not, see . #include "pluginlistsortproxy.h" #include "tutorialcontrol.h" #include "savegameinfowidgetgamebryo.h" +#include "previewgenerator.h" #include #include #include @@ -367,6 +368,8 @@ private: QString m_CurrentLanguage; std::vector m_Translators; + PreviewGenerator m_PreviewGenerator; + private slots: void showMessage(const QString &message); @@ -398,6 +401,7 @@ private slots: void writeDataToFile(); void openDataFile(); void addAsExecutable(); + void previewDataFile(); void hideFile(); void unhideFile(); diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 1da050ed..674e7165 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -398,19 +398,10 @@ void ModInfoDialog::on_textFileList_currentItemChanged(QListWidgetItem *current, void ModInfoDialog::openTextFile(const QString &fileName) { - QFile textFile(fileName); - textFile.open(QIODevice::ReadOnly); - QByteArray buffer = textFile.readAll(); - QTextCodec *codec = QTextCodec::codecForUtfText(buffer, m_UTF8Codec); - QString text = codec->toUnicode(buffer); - if (codec->fromUnicode(text) != buffer) { - qDebug("conversion failed assuming local encoding"); - codec = QTextCodec::codecForLocale(); - text = codec->toUnicode(buffer); - } - ui->textFileView->setText(text); + QString encoding; + ui->textFileView->setText(MOBase::readFileText(fileName, &encoding)); ui->textFileView->setProperty("currentFile", fileName); - ui->textFileView->setProperty("encoding", codec->name()); + ui->textFileView->setProperty("encoding", encoding); ui->saveTXTButton->setEnabled(false); } diff --git a/src/organizer.pro b/src/organizer.pro index 5988056e..07feb326 100644 --- a/src/organizer.pro +++ b/src/organizer.pro @@ -7,7 +7,7 @@ contains(QT_VERSION, "^5.*") { QT += core gui widgets network declarative script xml sql xmlpatterns } else { - QT += core gui network xml declarative script sql xmlpatterns + QT += core gui network xml declarative script sql xmlpatterns opengl } TARGET = ModOrganizer @@ -78,7 +78,9 @@ SOURCES += \ ../esptk/record.cpp \ ../esptk/espfile.cpp \ ../esptk/subrecord.cpp \ - noeditdelegate.cpp + noeditdelegate.cpp \ + previewgenerator.cpp \ + previewdialog.cpp HEADERS += \ transfersavesdialog.h \ @@ -145,7 +147,9 @@ HEADERS += \ ../esptk/espfile.h \ ../esptk/subrecord.h \ ../esptk/espexceptions.h \ - noeditdelegate.h + noeditdelegate.h \ + previewgenerator.h \ + previewdialog.h FORMS += \ transfersavesdialog.ui \ @@ -174,7 +178,8 @@ FORMS += \ activatemodsdialog.ui \ profileinputdialog.ui \ savetextasdialog.ui \ - problemsdialog.ui + problemsdialog.ui \ + previewdialog.ui INCLUDEPATH += ../shared ../archive ../uibase ../bsatk ../esptk "$(BOOSTPATH)" diff --git a/src/previewdialog.cpp b/src/previewdialog.cpp new file mode 100644 index 00000000..de33cdd0 --- /dev/null +++ b/src/previewdialog.cpp @@ -0,0 +1,57 @@ +#include "previewdialog.h" +#include "ui_previewdialog.h" +#include + +PreviewDialog::PreviewDialog(const QString &fileName, QWidget *parent) : + QDialog(parent), + ui(new Ui::PreviewDialog) +{ + ui->setupUi(this); + ui->nameLabel->setText(QFileInfo(fileName).fileName()); + ui->nextButton->setEnabled(false); + ui->previousButton->setEnabled(false); +} + +PreviewDialog::~PreviewDialog() +{ + delete ui; +} + +void PreviewDialog::addVariant(const QString &modName, QWidget *widget) +{ + widget->setProperty("modName", modName); + ui->variantsStack->addWidget(widget); + if (ui->variantsStack->count() > 1) { + ui->nextButton->setEnabled(true); + ui->previousButton->setEnabled(true); + } +} + +int PreviewDialog::numVariants() const +{ + return ui->variantsStack->count(); +} + +void PreviewDialog::on_variantsStack_currentChanged(int index) +{ + ui->modLabel->setText(ui->variantsStack->widget(index)->property("modName").toString()); +} + +void PreviewDialog::on_closeButton_clicked() +{ + this->accept(); +} + +void PreviewDialog::on_previousButton_clicked() +{ + int i = ui->variantsStack->currentIndex() - 1; + if (i < 0) { + i = ui->variantsStack->count() - 1; + } + ui->variantsStack->setCurrentIndex(i); +} + +void PreviewDialog::on_nextButton_clicked() +{ + ui->variantsStack->setCurrentIndex((ui->variantsStack->currentIndex() + 1) % ui->variantsStack->count()); +} diff --git a/src/previewdialog.h b/src/previewdialog.h new file mode 100644 index 00000000..0011bc50 --- /dev/null +++ b/src/previewdialog.h @@ -0,0 +1,36 @@ +#ifndef PREVIEWDIALOG_H +#define PREVIEWDIALOG_H + +#include + +namespace Ui { +class PreviewDialog; +} + +class PreviewDialog : public QDialog +{ + Q_OBJECT + +public: + explicit PreviewDialog(const QString &fileName, QWidget *parent = 0); + ~PreviewDialog(); + + void addVariant(const QString &modName, QWidget *widget); + int numVariants() const; + +private slots: + + void on_variantsStack_currentChanged(int arg1); + + void on_closeButton_clicked(); + + void on_previousButton_clicked(); + + void on_nextButton_clicked(); + +private: + + Ui::PreviewDialog *ui; +}; + +#endif // PREVIEWDIALOG_H diff --git a/src/previewdialog.ui b/src/previewdialog.ui new file mode 100644 index 00000000..e2dc824a --- /dev/null +++ b/src/previewdialog.ui @@ -0,0 +1,90 @@ + + + PreviewDialog + + + + 0 + 0 + 736 + 583 + + + + Preview + + + + + + + + + + + + + + + + + + + + + + + + + + :/MO/gui/previous:/MO/gui/previous + + + + + + + + + + + :/MO/gui/next:/MO/gui/next + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Close + + + + + + + + + + + + diff --git a/src/previewgenerator.cpp b/src/previewgenerator.cpp new file mode 100644 index 00000000..5b0a44d8 --- /dev/null +++ b/src/previewgenerator.cpp @@ -0,0 +1,111 @@ +/* +Copyright (C) 2014 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 "previewgenerator.h" +#include +#include +#include +#include +#include +#include +#include + +PreviewGenerator::PreviewGenerator() +{ + // set up image reader to be used for all image types qt (the current installation) supports + auto imageReader = std::bind(&PreviewGenerator::genImagePreview, this, std::placeholders::_1); + + foreach (const QByteArray &fileType, QImageReader::supportedImageFormats()) { + m_PreviewGenerators[QString(fileType).toLower()] = imageReader; + } + + m_PreviewGenerators["txt"] + = std::bind(&PreviewGenerator::genTxtPreview, this, std::placeholders::_1); + + m_PreviewGenerators["dds"] + = std::bind(&PreviewGenerator::genDDSPreview, this, std::placeholders::_1); + + QDesktopWidget desk; + m_MaxSize = desk.screenGeometry().size() * 0.8; +} + +bool PreviewGenerator::previewSupported(const QString &fileExtension) const +{ + return m_PreviewGenerators.find(fileExtension.toLower()) != m_PreviewGenerators.end(); +} + +QWidget *PreviewGenerator::genPreview(const QString &fileName) const +{ + auto iter = m_PreviewGenerators.find(QFileInfo(fileName).completeSuffix().toLower()); + if (iter != m_PreviewGenerators.end()) { + return iter->second(fileName); + } else { + return nullptr; + } +} + +QWidget *PreviewGenerator::genImagePreview(const QString &fileName) const +{ + QLabel *label = new QLabel(); + label->setPixmap(QPixmap(fileName)); + return label; +} + +QWidget *PreviewGenerator::genTxtPreview(const QString &fileName) const +{ + QTextEdit *edit = new QTextEdit(); + edit->setText(MOBase::readFileText(fileName)); + edit->setReadOnly(true); + return edit; +} + +QWidget *PreviewGenerator::genDDSPreview(const QString &fileName) const +{ + QGLWidget glWidget; + glWidget.makeCurrent(); + + GLuint texture = glWidget.bindTexture(fileName); + if (!texture) + return nullptr; + + // Determine the size of the DDS image + GLint width, height; + glBindTexture(GL_TEXTURE_2D, texture); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height); + + if (width == 0 || height == 0) + return nullptr; + + QGLPixelBuffer pbuffer(QSize(width, height), glWidget.format(), &glWidget); + if (!pbuffer.makeCurrent()) + return nullptr; + + pbuffer.drawTexture(QRectF(-1, -1, 2, 2), texture); + + QLabel *label = new QLabel(); + QImage image = pbuffer.toImage(); + if ((image.size().width() > m_MaxSize.width()) || + (image.size().height() > m_MaxSize.height())) { + image = image.scaled(m_MaxSize, Qt::KeepAspectRatio); + } + + label->setPixmap(QPixmap::fromImage(image)); + return label; +} diff --git a/src/previewgenerator.h b/src/previewgenerator.h new file mode 100644 index 00000000..4648e27e --- /dev/null +++ b/src/previewgenerator.h @@ -0,0 +1,51 @@ +/* +Copyright (C) 2014 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 . +*/ + +#ifndef PREVIEWGENERATOR_H +#define PREVIEWGENERATOR_H + +#include +#include +#include +#include + +class PreviewGenerator +{ +public: + PreviewGenerator(); + + bool previewSupported(const QString &fileExtension) const; + + QWidget *genPreview(const QString &fileName) const; + +private: + + QWidget *genImagePreview(const QString &fileName) const; + QWidget *genTxtPreview(const QString &fileName) const; + QWidget *genDDSPreview(const QString &fileName) const; + +private: + + std::map > m_PreviewGenerators; + + QSize m_MaxSize; + +}; + +#endif // PREVIEWGENERATOR_H diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h index 22f00a74..ad5d179b 100644 --- a/src/shared/directoryentry.h +++ b/src/shared/directoryentry.h @@ -71,6 +71,7 @@ public: const std::vector &getAlternatives() const { return m_Alternatives; } const std::wstring &getName() const { return m_Name; } + int getOrigin() const { return m_Origin; } int getOrigin(bool &archive) const { archive = (m_Archive.length() != 0); return m_Origin; } const std::wstring &getArchive() const { return m_Archive; } bool isFromArchive() const { return m_Archive.length() != 0; } -- cgit v1.3.1 From a083a2d3b6506ab95d3b18ab0ffa7751750e0249 Mon Sep 17 00:00:00 2001 From: Tannin Date: Mon, 13 Jan 2014 19:32:54 +0100 Subject: - implemented hook for NtQueryDirectoryFile - saves list is now automatically updated on FS changes - optimization: data tree widget is no longer filled completely at once but one directory at a time - bugfix: pending downloads were not removed from list after a failed nxm request - bugfix: there was still a nmm.nexusmods.com link - bugfix: the text "alpha" in version strings wasn't interpreted correctly --- src/downloadmanager.cpp | 6 ++-- src/logbuffer.cpp | 2 +- src/mainwindow.cpp | 67 +++++++++++++++++++++++++++++++++++++++------ src/mainwindow.h | 5 ++++ src/modinfodialog.cpp | 2 +- src/organizer.pro | 10 ++++++- src/shared/directoryentry.h | 2 ++ 7 files changed, 81 insertions(+), 13 deletions(-) (limited to 'src/mainwindow.h') diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index f0f4ba2d..fdc825f1 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -429,7 +429,7 @@ void DownloadManager::addNXMDownload(const QString &url) emit update(-1); emit downloadAdded(); - m_RequestIDs.insert(m_NexusInterface->requestFileInfo(nxmInfo.modId(), nxmInfo.fileId(), this, QVariant())); + m_RequestIDs.insert(m_NexusInterface->requestFileInfo(nxmInfo.modId(), nxmInfo.fileId(), this, nxmInfo.fileId())); } @@ -1201,7 +1201,7 @@ void DownloadManager::nxmDownloadURLsAvailable(int modID, int fileID, QVariant u } -void DownloadManager::nxmRequestFailed(int modID, QVariant, int requestID, const QString &errorString) +void DownloadManager::nxmRequestFailed(int modID, QVariant userData, int requestID, const QString &errorString) { std::set::iterator idIter = m_RequestIDs.find(requestID); if (idIter == m_RequestIDs.end()) { @@ -1225,6 +1225,8 @@ void DownloadManager::nxmRequestFailed(int modID, QVariant, int requestID, const break; } } + + removePending(modID, userData.toInt()); emit showMessage(tr("Failed to request file info from nexus: %1").arg(errorString)); } diff --git a/src/logbuffer.cpp b/src/logbuffer.cpp index f930cf10..6f3f640f 100644 --- a/src/logbuffer.cpp +++ b/src/logbuffer.cpp @@ -131,7 +131,7 @@ void LogBuffer::log(QtMsgType type, const char *message) if (!s_Instance.isNull()) { s_Instance->logMessage(type, message); } - fprintf(stdout, "[%c] %s: %s\n", msgTypeID(type), qPrintable(QTime::currentTime().toString()), message); + fprintf(stdout, "%s [%c] %s\n", qPrintable(QTime::currentTime().toString()), msgTypeID(type), message); fflush(stdout); } diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 80465fb9..139db3f0 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -256,13 +256,16 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget connect(&m_PluginList, SIGNAL(saveTimer()), this, SLOT(savePluginList())); connect(ui->bsaList, SIGNAL(itemsMoved()), this, SLOT(bsaList_itemMoved())); - connect(ui->bsaWarning, SIGNAL(linkActivated(QString)), this, SLOT(linkClicked(QString))); + connect(ui->dataTree, SIGNAL(itemExpanded(QTreeWidgetItem*)), this, SLOT(expandDataTreeItem(QTreeWidgetItem*))); + connect(&m_DirectoryRefresher, SIGNAL(refreshed()), this, SLOT(directory_refreshed())); connect(&m_DirectoryRefresher, SIGNAL(progress(int)), this, SLOT(refresher_progress(int))); connect(&m_DirectoryRefresher, SIGNAL(error(QString)), this, SLOT(showError(QString))); + connect(&m_SavesWatcher, SIGNAL(directoryChanged(QString)), this, SLOT(refreshSavesIfOpen())); + connect(&m_Settings, SIGNAL(languageChanged(QString)), this, SLOT(languageChange(QString))); connect(&m_Settings, SIGNAL(styleChanged(QString)), this, SIGNAL(styleChanged(QString))); @@ -1526,21 +1529,52 @@ void MainWindow::updateTo(QTreeWidgetItem *subTree, const std::wstring &director std::vector::const_iterator current, end; directoryEntry.getSubDirectories(current, end); for (; current != end; ++current) { - QStringList columns(ToQString((*current)->getName())); + QString pathName = ToQString((*current)->getName()); + QStringList columns(pathName); columns.append(""); - QTreeWidgetItem *directoryChild = new QTreeWidgetItem(columns); - updateTo(directoryChild, temp.str(), **current, conflictsOnly); - if (directoryChild->childCount() != 0) { + if (!(*current)->isEmpty()) { + QTreeWidgetItem *directoryChild = new QTreeWidgetItem(columns); + QTreeWidgetItem *onDemandLoad = new QTreeWidgetItem(QStringList()); + onDemandLoad->setData(0, Qt::UserRole + 0, "__loaded_on_demand__"); + onDemandLoad->setData(0, Qt::UserRole + 1, ToQString(temp.str())); + onDemandLoad->setData(0, Qt::UserRole + 2, conflictsOnly); + directoryChild->addChild(onDemandLoad); subTree->addChild(directoryChild); - } else { - delete directoryChild; +/* updateTo(directoryChild, temp.str(), **current, conflictsOnly); + if (directoryChild->childCount() != 0) { + subTree->addChild(directoryChild); + } else { + delete directoryChild; + }*/ } } } + subTree->sortChildren(0, Qt::AscendingOrder); } +void MainWindow::expandDataTreeItem(QTreeWidgetItem *item) +{ + if ((item->childCount() == 1) && (item->child(0)->data(0, Qt::UserRole).toString() == "__loaded_on_demand__")) { + // read the data we need from the sub-item, then dispose of it + QTreeWidgetItem *onDemandDataItem = item->child(0); + std::wstring path = ToWString(onDemandDataItem->data(0, Qt::UserRole + 1).toString()); + bool conflictsOnly = onDemandDataItem->data(0, Qt::UserRole + 2).toBool(); + item->removeChild(onDemandDataItem); + + std::wstring virtualPath = (path + L"\\").substr(6) + ToWString(item->text(0)); + DirectoryEntry *dir = m_DirectoryStructure->findSubDirectoryRecursive(virtualPath); + if (dir != NULL) { + updateTo(item, path, *dir, conflictsOnly); + } else { + qWarning("failed to update view of %ls", path.c_str()); + } + + } +} + + bool MainWindow::refreshProfiles(bool selectProfile) { QComboBox* profileBox = findChild("profileBox"); @@ -1665,6 +1699,14 @@ void MainWindow::refreshDataTree() } +void MainWindow::refreshSavesIfOpen() +{ + if (ui->tabWidget->currentIndex() == 3) { + refreshSaveList(); + } +} + + void MainWindow::refreshSaveList() { ui->savegameList->clear(); @@ -1680,6 +1722,8 @@ void MainWindow::refreshSaveList() savesDir.setPath(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getDocumentsDir() + L"\\" + path))); } + m_SavesWatcher.addPath(savesDir.absolutePath()); + QStringList filters; filters << ToQString(GameInfo::instance().getSaveGameExtension()); savesDir.setNameFilters(filters); @@ -4399,7 +4443,12 @@ void MainWindow::previewDataFile() QString filePath = QDir::fromNativeSeparators(ToQString(origin.getPath())) + "/" + fileName; if (QFile::exists(filePath)) { // it's very possible the file doesn't exist, because it's inside an archive. we don't support that - preview.addVariant(ToQString(origin.getName()), m_PreviewGenerator.genPreview(filePath)); + QWidget *wid = m_PreviewGenerator.genPreview(filePath); + if (wid == NULL) { + reportError(tr("failed to generate preview for %1").arg(filePath)); + } else { + preview.addVariant(ToQString(origin.getName()), wid); + } } }; @@ -4409,6 +4458,8 @@ void MainWindow::previewDataFile() } if (preview.numVariants() > 0) { preview.exec(); + } else { + QMessageBox::information(this, tr("Sorry"), tr("Sorry, can't preview anything. This function currently does not support extracting from bsas.")); } } diff --git a/src/mainwindow.h b/src/mainwindow.h index 21c121fb..59bee7bb 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -370,6 +370,8 @@ private: PreviewGenerator m_PreviewGenerator; + QFileSystemWatcher m_SavesWatcher; + private slots: void showMessage(const QString &message); @@ -523,6 +525,9 @@ private slots: void ignoreUpdate(); void unignoreUpdate(); + void refreshSavesIfOpen(); + void expandDataTreeItem(QTreeWidgetItem *item); + private slots: // ui slots // actions void on_actionAdd_Profile_triggered(); diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 674e7165..7f221bdc 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -766,7 +766,7 @@ void ModInfoDialog::activateNexusTab() QLineEdit *modIDEdit = findChild("modIDEdit"); int modID = modIDEdit->text().toInt(); if (modID != 0) { - QString nexusLink = QString("%1/downloads/file.php?id=%2").arg(ToQString(GameInfo::instance().getNexusPage())).arg(modID); + QString nexusLink = QString("%1/downloads/file.php?id=%2").arg(ToQString(GameInfo::instance().getNexusPage(false))).arg(modID); QLabel *visitNexusLabel = findChild("visitNexusLabel"); visitNexusLabel->setText(tr("Visit on Nexus").arg(nexusLink)); visitNexusLabel->setToolTip(nexusLink); diff --git a/src/organizer.pro b/src/organizer.pro index 07feb326..25384e69 100644 --- a/src/organizer.pro +++ b/src/organizer.pro @@ -80,7 +80,15 @@ SOURCES += \ ../esptk/subrecord.cpp \ noeditdelegate.cpp \ previewgenerator.cpp \ - previewdialog.cpp + previewdialog.cpp \ + gl/gltexloaders.cpp \ + gl/dds/dds_api.cpp \ + gl/dds/Image.cpp \ + gl/dds/DirectDrawSurface.cpp \ + gl/dds/Stream.cpp \ + gl/dds/BlockDXT.cpp \ + gl/dds/ColorBlock.cpp + HEADERS += \ transfersavesdialog.h \ diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h index ad5d179b..15af5e9e 100644 --- a/src/shared/directoryentry.h +++ b/src/shared/directoryentry.h @@ -205,6 +205,8 @@ public: void clear(); bool isPopulated() const { return m_Populated; } + bool isEmpty() const { return (m_Files.size() == 0) && (m_SubDirectories.size() == 0); } + const DirectoryEntry *getParent() const { return m_Parent; } // add files to this directory (and subdirectories) from the specified origin. That origin may exist or not -- cgit v1.3.1 From 40d20ab294ad7afd4b5747b306e45d0f8712a386 Mon Sep 17 00:00:00 2001 From: Tannin Date: Thu, 16 Jan 2014 20:43:42 +0100 Subject: - added an about dialog - updated json library --- src/aboutdialog.cpp | 82 +++ src/aboutdialog.h | 69 ++ src/aboutdialog.ui | 295 +++++++++ src/downloadmanager.cpp | 1 - src/json.cpp | 1110 +++++++++++++++----------------- src/json.h | 298 +++------ src/logbuffer.cpp | 1 + src/mainwindow.cpp | 9 + src/mainwindow.h | 1 + src/modinfodialog.cpp | 3 - src/modlist.cpp | 2 +- src/nexusinterface.cpp | 5 +- src/organizer.pro | 16 +- src/resources/emblem-favorite - 64.png | Bin 0 -> 3710 bytes 14 files changed, 1087 insertions(+), 805 deletions(-) create mode 100644 src/aboutdialog.cpp create mode 100644 src/aboutdialog.h create mode 100644 src/aboutdialog.ui create mode 100644 src/resources/emblem-favorite - 64.png (limited to 'src/mainwindow.h') diff --git a/src/aboutdialog.cpp b/src/aboutdialog.cpp new file mode 100644 index 00000000..94e4e54c --- /dev/null +++ b/src/aboutdialog.cpp @@ -0,0 +1,82 @@ +/* +Copyright (C) 2014 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 "aboutdialog.h" +#include "ui_aboutdialog.h" +#include + + +AboutDialog::AboutDialog(const QString &version, QWidget *parent) + : QDialog(parent) + , ui(new Ui::AboutDialog) +{ + ui->setupUi(this); + + m_LicenseFiles[LICENSE_LGPL3] = "lgpl-3.0.txt"; + m_LicenseFiles[LICENSE_GPL3] = "gpl-3.0.txt"; + m_LicenseFiles[LICENSE_BSD3] = "bsd3.txt"; + m_LicenseFiles[LICENSE_BOOST] = "boost.txt"; + m_LicenseFiles[LICENSE_CCBY3] = "by-sa3.txt"; + m_LicenseFiles[LICENSE_ZLIB] = "zlib.txt"; + + addLicense("Qt 4.8.5", LICENSE_LGPL3); + addLicense("Qt Json", LICENSE_GPL3); + addLicense("Boost Library", LICENSE_BOOST); + addLicense("Tango Icon Theme", LICENSE_NONE); + addLicense("RRZE Icon Set", LICENSE_CCBY3); + addLicense("7-zip", LICENSE_LGPL3); + addLicense("ZLib", LICENSE_ZLIB); + addLicense("NIF File Format Library", LICENSE_BSD3); + + ui->nameLabel->setText(QString("%1 %2").arg(ui->nameLabel->text()).arg(version)); +#ifdef HGID + ui->revisionLabel->setText(ui->revisionLabel->text() + " " + HGID); +#else + ui->revisionLabel->setText(ui->revisionLabel->text() + " unknown"); +#endif +} + + +AboutDialog::~AboutDialog() +{ + delete ui; +} + + +void AboutDialog::addLicense(const QString &name, Licenses license) +{ + QListWidgetItem *item = new QListWidgetItem(name); + item->setData(Qt::UserRole, license); + ui->creditsList->addItem(item); +} + + +void AboutDialog::on_creditsList_currentItemChanged(QListWidgetItem *current, QListWidgetItem*) +{ + auto iter = m_LicenseFiles.find(current->data(Qt::UserRole).toInt()); + if (iter != m_LicenseFiles.end()) { + QString filePath = qApp->applicationDirPath() + "/license/" + iter->second; +qDebug("%s", qPrintable(filePath)); + QString text = MOBase::readFileText(filePath); + ui->licenseText->setText(text); + } else { + ui->licenseText->setText(tr("No license")); + } +} diff --git a/src/aboutdialog.h b/src/aboutdialog.h new file mode 100644 index 00000000..9c0901a8 --- /dev/null +++ b/src/aboutdialog.h @@ -0,0 +1,69 @@ +#ifndef ABOUTDIALOG_H +/* +Copyright (C) 2014 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 . +*/ + + +#define ABOUTDIALOG_H + +#include +#include +#include +#include +#include + +namespace Ui { + class AboutDialog; +} + +class AboutDialog : public QDialog +{ + Q_OBJECT + +public: + explicit AboutDialog(const QString &version, QWidget *parent = 0); + ~AboutDialog(); + +private: + + enum Licenses { + LICENSE_NONE, + LICENSE_LGPL3, + LICENSE_GPL3, + LICENSE_BSD3, + LICENSE_BOOST, + LICENSE_CCBY3, + LICENSE_ZLIB + }; + +private: + + void addLicense(const QString &name, Licenses license); + +private slots: + void on_creditsList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous); + +private: + + Ui::AboutDialog *ui; + + std::map m_LicenseFiles; + +}; + +#endif // ABOUTDIALOG_H diff --git a/src/aboutdialog.ui b/src/aboutdialog.ui new file mode 100644 index 00000000..985700db --- /dev/null +++ b/src/aboutdialog.ui @@ -0,0 +1,295 @@ + + + AboutDialog + + + + 0 + 0 + 508 + 335 + + + + About + + + + + + + + + + + + + :/MO/gui/mo_icon.ico + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + 0 + + + + About + + + + + + Mod Organizer + + + + + + + Revision: + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Copyright 2011-2014 Sebastian Herbord + + + + + + + <html><head/><body><p>This program 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.</p><p>See the GNU General Public License for more details.</p></body></html> + + + true + + + + + + + + Used Software + + + + + + + + + + + + + Credits + + + + + + Translators + + + + + + QAbstractItemView::NoSelection + + + + pndrev (German) + + + + + DaWul (Spanish) + + + + + Fiama (Spanish) + + + + + Alyndiar (French) + + + + + Jlkawaii (French) + + + + + Rigoletto (French) + + + + + Scythe1912 (Chinese) + + + + + yc0620shen (Chinese) + + + + + miraclefreak (Czech) + + + + + tokcdk (Russian) + + + + + + + + + + + Others + + + + + + QAbstractItemView::NoSelection + + + + blacksol + + + + + DoubleYou + + + + + deathneko11 + + + + + Bridger + + + + + GSDFan + + + + + Uhuru + + + + + Wolverine2710 + + + + + z929669 + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Close + + + + + + + + + + + + + closeButton + clicked() + AboutDialog + accept() + + + 460 + 313 + + + 253 + 167 + + + + + diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index fdc825f1..dd320f70 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -37,7 +37,6 @@ along with Mod Organizer. If not, see . #include -using QtJson::Json; using namespace MOBase; diff --git a/src/json.cpp b/src/json.cpp index 7bfaed39..ca2d728f 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -1,588 +1,522 @@ -/* Copyright 2011 Eeli Reilin. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY ''AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL EELI REILIN OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, - * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation - * are those of the authors and should not be interpreted as representing - * official policies, either expressed or implied, of Eeli Reilin. - */ - -/** - * \file json.cpp - */ - -#include "json.h" -#include - -namespace QtJson -{ - - -static QString sanitizeString(QString str) -{ - str.replace(QLatin1String("\\"), QLatin1String("\\\\")); - str.replace(QLatin1String("\""), QLatin1String("\\\"")); - str.replace(QLatin1String("\b"), QLatin1String("\\b")); - str.replace(QLatin1String("\f"), QLatin1String("\\f")); - str.replace(QLatin1String("\n"), QLatin1String("\\n")); - str.replace(QLatin1String("\r"), QLatin1String("\\r")); - str.replace(QLatin1String("\t"), QLatin1String("\\t")); - return QString(QLatin1String("\"%1\"")).arg(str); -} - -static QByteArray join(const QList &list, const QByteArray &sep) -{ - QByteArray res; - Q_FOREACH(const QByteArray &i, list) - { - if(!res.isEmpty()) - { - res += sep; - } - res += i; - } - return res; -} - -/** - * parse - */ -QVariant Json::parse(const QString &json) -{ - bool success = true; - return Json::parse(json, success); -} - -/** - * parse - */ -QVariant Json::parse(const QString &json, bool &success) -{ - success = true; - - //Return an empty QVariant if the JSON data is either null or empty - if(!json.isNull() || !json.isEmpty()) - { - QString data = json; - //We'll start from index 0 - int index = 0; - - //Parse the first value - QVariant value = Json::parseValue(data, index, success); - - //Return the parsed value - return value; - } - else - { - //Return the empty QVariant - return QVariant(); - } -} - -QByteArray Json::serialize(const QVariant &data) -{ - bool success = true; - return Json::serialize(data, success); -} - -QByteArray Json::serialize(const QVariant &data, bool &success) -{ - QByteArray str; - success = true; - - if(!data.isValid()) // invalid or null? - { - str = "null"; - } - else if((data.type() == QVariant::List) || (data.type() == QVariant::StringList)) // variant is a list? - { - QList values; - const QVariantList list = data.toList(); - Q_FOREACH(const QVariant& v, list) - { - QByteArray serializedValue = serialize(v); - if(serializedValue.isNull()) - { - success = false; - break; - } - values << serializedValue; - } - - str = "[ " + join( values, ", " ) + " ]"; - } - else if(data.type() == QVariant::Map) // variant is a map? - { - const QVariantMap vmap = data.toMap(); - QMapIterator it( vmap ); - str = "{ "; - QList pairs; - while(it.hasNext()) - { - it.next(); - QByteArray serializedValue = serialize(it.value()); - if(serializedValue.isNull()) - { - success = false; - break; - } - pairs << sanitizeString(it.key()).toUtf8() + " : " + serializedValue; - } - str += join(pairs, ", "); - str += " }"; - } - else if((data.type() == QVariant::String) || (data.type() == QVariant::ByteArray)) // a string or a byte array? - { - str = sanitizeString(data.toString()).toUtf8(); - } - else if(data.type() == QVariant::Double) // double? - { - str = QByteArray::number(data.toDouble()); - if(!str.contains(".") && ! str.contains("e")) - { - str += ".0"; - } - } - else if (data.type() == QVariant::Bool) // boolean value? - { - str = data.toBool() ? "true" : "false"; - } - else if (data.type() == QVariant::ULongLong) // large unsigned number? - { - str = QByteArray::number(data.value()); - } - else if ( data.canConvert() ) // any signed number? - { - str = QByteArray::number(data.value()); - } - else if (data.canConvert()) - { - str = sanitizeString(QString::number(data.value())).toUtf8(); - } - else if (data.canConvert()) // can value be converted to string? - { - // this will catch QDate, QDateTime, QUrl, ... - str = sanitizeString(data.toString()).toUtf8(); - } - else - { - success = false; - } - if (success) - { - return str; - } - else - { - return QByteArray(); - } -} - -/** - * parseValue - */ -QVariant Json::parseValue(const QString &json, int &index, bool &success) -{ - //Determine what kind of data we should parse by - //checking out the upcoming token - switch(Json::lookAhead(json, index)) - { - case JsonTokenString: - return Json::parseString(json, index, success); - case JsonTokenNumber: - return Json::parseNumber(json, index); - case JsonTokenCurlyOpen: - return Json::parseObject(json, index, success); - case JsonTokenSquaredOpen: - return Json::parseArray(json, index, success); - case JsonTokenTrue: - Json::nextToken(json, index); - return QVariant(true); - case JsonTokenFalse: - Json::nextToken(json, index); - return QVariant(false); - case JsonTokenNull: - Json::nextToken(json, index); - return QVariant(); - case JsonTokenNone: - break; - } - - //If there were no tokens, flag the failure and return an empty QVariant - success = false; - return QVariant(); -} - -/** - * parseObject - */ -QVariant Json::parseObject(const QString &json, int &index, bool &success) -{ - QVariantMap map; - int token; - - //Get rid of the whitespace and increment index - Json::nextToken(json, index); - - //Loop through all of the key/value pairs of the object - bool done = false; - while(!done) - { - //Get the upcoming token - token = Json::lookAhead(json, index); - - if(token == JsonTokenNone) - { - success = false; - return QVariantMap(); - } - else if(token == JsonTokenComma) - { - Json::nextToken(json, index); - } - else if(token == JsonTokenCurlyClose) - { - Json::nextToken(json, index); - return map; - } - else - { - //Parse the key/value pair's name - QString name = Json::parseString(json, index, success).toString(); - - if(!success) - { - return QVariantMap(); - } - - //Get the next token - token = Json::nextToken(json, index); - - //If the next token is not a colon, flag the failure - //return an empty QVariant - if(token != JsonTokenColon) - { - success = false; - return QVariant(QVariantMap()); - } - - //Parse the key/value pair's value - QVariant value = Json::parseValue(json, index, success); - - if(!success) - { - return QVariantMap(); - } - - //Assign the value to the key in the map - map[name] = value; - } - } - - //Return the map successfully - return QVariant(map); -} - -/** - * parseArray - */ -QVariant Json::parseArray(const QString &json, int &index, bool &success) -{ - QVariantList list; - - Json::nextToken(json, index); - - bool done = false; - while(!done) - { - int token = Json::lookAhead(json, index); - - if(token == JsonTokenNone) - { - success = false; - return QVariantList(); - } - else if(token == JsonTokenComma) - { - Json::nextToken(json, index); - } - else if(token == JsonTokenSquaredClose) - { - Json::nextToken(json, index); - break; - } - else - { - QVariant value = Json::parseValue(json, index, success); - - if(!success) - { - return QVariantList(); - } - - list.push_back(value); - } - } - - return QVariant(list); -} - -/** - * parseString - */ -QVariant Json::parseString(const QString &json, int &index, bool &success) -{ - QString s; - QChar c; - - Json::eatWhitespace(json, index); - - c = json[index++]; - - bool complete = false; - while(!complete) - { - if(index == json.size()) - { - break; - } - - c = json[index++]; - - if(c == '\"') - { - complete = true; - break; - } - else if(c == '\\') - { - if(index == json.size()) - { - break; - } - - c = json[index++]; - - if(c == '\"') - { - s.append('\"'); - } - else if(c == '\\') - { - s.append('\\'); - } - else if(c == '/') - { - s.append('/'); - } - else if(c == 'b') - { - s.append('\b'); - } - else if(c == 'f') - { - s.append('\f'); - } - else if(c == 'n') - { - s.append('\n'); - } - else if(c == 'r') - { - s.append('\r'); - } - else if(c == 't') - { - s.append('\t'); - } - else if(c == 'u') - { - int remainingLength = json.size() - index; - - if(remainingLength >= 4) - { - QString unicodeStr = json.mid(index, 4); - - int symbol = unicodeStr.toInt(0, 16); - - s.append(QChar(symbol)); - - index += 4; - } - else - { - break; - } - } - } - else - { - s.append(c); - } - } - - if(!complete) - { - success = false; - return QVariant(); - } - - return QVariant(s); -} - -/** - * parseNumber - */ -QVariant Json::parseNumber(const QString &json, int &index) -{ - Json::eatWhitespace(json, index); - - int lastIndex = Json::lastIndexOfNumber(json, index); - int charLength = (lastIndex - index) + 1; - QString numberStr; - - numberStr = json.mid(index, charLength); - - index = lastIndex + 1; - - if (numberStr.contains('.')) { - return QVariant(numberStr.toDouble(NULL)); - } else if (numberStr.startsWith('-')) { - return QVariant(numberStr.toLongLong(NULL)); - } else { - return QVariant(numberStr.toULongLong(NULL)); - } -} - -/** - * lastIndexOfNumber - */ -int Json::lastIndexOfNumber(const QString &json, int index) -{ - int lastIndex; - - for(lastIndex = index; lastIndex < json.size(); lastIndex++) - { - if(QString("0123456789+-.eE").indexOf(json[lastIndex]) == -1) - { - break; - } - } - - return lastIndex -1; -} - -/** - * eatWhitespace - */ -void Json::eatWhitespace(const QString &json, int &index) -{ - for(; index < json.size(); index++) - { - if(QString(" \t\n\r").indexOf(json[index]) == -1) - { - break; - } - } -} - -/** - * lookAhead - */ -int Json::lookAhead(const QString &json, int index) -{ - int saveIndex = index; - return Json::nextToken(json, saveIndex); -} - -/** - * nextToken - */ -int Json::nextToken(const QString &json, int &index) -{ - Json::eatWhitespace(json, index); - - if(index == json.size()) - { - return JsonTokenNone; - } - - QChar c = json[index]; - index++; - switch(c.toLatin1()) - { - case '{': return JsonTokenCurlyOpen; - case '}': return JsonTokenCurlyClose; - case '[': return JsonTokenSquaredOpen; - case ']': return JsonTokenSquaredClose; - case ',': return JsonTokenComma; - case '"': return JsonTokenString; - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - case '-': return JsonTokenNumber; - case ':': return JsonTokenColon; - } - - index--; - - int remainingLength = json.size() - index; - - //True - if(remainingLength >= 4) - { - if (json[index] == 't' && json[index + 1] == 'r' && - json[index + 2] == 'u' && json[index + 3] == 'e') - { - index += 4; - return JsonTokenTrue; - } - } - - //False - if (remainingLength >= 5) - { - if (json[index] == 'f' && json[index + 1] == 'a' && - json[index + 2] == 'l' && json[index + 3] == 's' && - json[index + 4] == 'e') - { - index += 5; - return JsonTokenFalse; - } - } - - //Null - if (remainingLength >= 4) - { - if (json[index] == 'n' && json[index + 1] == 'u' && - json[index + 2] == 'l' && json[index + 3] == 'l') - { - index += 4; - return JsonTokenNull; - } - } - - return JsonTokenNone; -} - - -} //end namespace +/** + * QtJson - A simple class for parsing JSON data into a QVariant hierarchies and vice-versa. + * Copyright (C) 2011 Eeli Reilin + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +/** + * \file json.cpp + */ + +#include "json.h" + +namespace QtJson { + static QString sanitizeString(QString str); + static QByteArray join(const QList &list, const QByteArray &sep); + static QVariant parseValue(const QString &json, int &index, bool &success); + static QVariant parseObject(const QString &json, int &index, bool &success); + static QVariant parseArray(const QString &json, int &index, bool &success); + static QVariant parseString(const QString &json, int &index, bool &success); + static QVariant parseNumber(const QString &json, int &index); + static int lastIndexOfNumber(const QString &json, int index); + static void eatWhitespace(const QString &json, int &index); + static int lookAhead(const QString &json, int index); + static int nextToken(const QString &json, int &index); + + template + QByteArray serializeMap(const T &map, bool &success) { + QByteArray str = "{ "; + QList pairs; + for (typename T::const_iterator it = map.begin(), itend = map.end(); it != itend; ++it) { + QByteArray serializedValue = serialize(it.value()); + if (serializedValue.isNull()) { + success = false; + break; + } + pairs << sanitizeString(it.key()).toUtf8() + " : " + serializedValue; + } + + str += join(pairs, ", "); + str += " }"; + return str; + } + + + /** + * parse + */ + QVariant parse(const QString &json) { + bool success = true; + return parse(json, success); + } + + /** + * parse + */ + QVariant parse(const QString &json, bool &success) { + success = true; + + // Return an empty QVariant if the JSON data is either null or empty + if (!json.isNull() || !json.isEmpty()) { + QString data = json; + // We'll start from index 0 + int index = 0; + + // Parse the first value + QVariant value = parseValue(data, index, success); + + // Return the parsed value + return value; + } else { + // Return the empty QVariant + return QVariant(); + } + } + + QByteArray serialize(const QVariant &data) { + bool success = true; + return serialize(data, success); + } + + QByteArray serialize(const QVariant &data, bool &success) { + QByteArray str; + success = true; + + if (!data.isValid()) { // invalid or null? + str = "null"; + } else if ((data.type() == QVariant::List) || + (data.type() == QVariant::StringList)) { // variant is a list? + QList values; + const QVariantList list = data.toList(); + Q_FOREACH(const QVariant& v, list) { + QByteArray serializedValue = serialize(v); + if (serializedValue.isNull()) { + success = false; + break; + } + values << serializedValue; + } + + str = "[ " + join( values, ", " ) + " ]"; + } else if (data.type() == QVariant::Hash) { // variant is a hash? + str = serializeMap<>(data.toHash(), success); + } else if (data.type() == QVariant::Map) { // variant is a map? + str = serializeMap<>(data.toMap(), success); + } else if ((data.type() == QVariant::String) || + (data.type() == QVariant::ByteArray)) {// a string or a byte array? + str = sanitizeString(data.toString()).toUtf8(); + } else if (data.type() == QVariant::Double) { // double? + double value = data.toDouble(); + if ((value - value) == 0.0) { + str = QByteArray::number(value, 'g'); + if (!str.contains(".") && ! str.contains("e")) { + str += ".0"; + } + } else { + success = false; + } + } else if (data.type() == QVariant::Bool) { // boolean value? + str = data.toBool() ? "true" : "false"; + } else if (data.type() == QVariant::ULongLong) { // large unsigned number? + str = QByteArray::number(data.value()); + } else if (data.canConvert()) { // any signed number? + str = QByteArray::number(data.value()); + } else if (data.canConvert()) { //TODO: this code is never executed + str = QString::number(data.value()).toUtf8(); + } else if (data.canConvert()) { // can value be converted to string? + // this will catch QDate, QDateTime, QUrl, ... + str = sanitizeString(data.toString()).toUtf8(); + } else { + success = false; + } + + if (success) { + return str; + } else { + return QByteArray(); + } + } + + QString serializeStr(const QVariant &data) { + return QString::fromUtf8(serialize(data)); + } + + QString serializeStr(const QVariant &data, bool &success) { + return QString::fromUtf8(serialize(data, success)); + } + + + /** + * \enum JsonToken + */ + enum JsonToken { + JsonTokenNone = 0, + JsonTokenCurlyOpen = 1, + JsonTokenCurlyClose = 2, + JsonTokenSquaredOpen = 3, + JsonTokenSquaredClose = 4, + JsonTokenColon = 5, + JsonTokenComma = 6, + JsonTokenString = 7, + JsonTokenNumber = 8, + JsonTokenTrue = 9, + JsonTokenFalse = 10, + JsonTokenNull = 11 + }; + + static QString sanitizeString(QString str) { + str.replace(QLatin1String("\\"), QLatin1String("\\\\")); + str.replace(QLatin1String("\""), QLatin1String("\\\"")); + str.replace(QLatin1String("\b"), QLatin1String("\\b")); + str.replace(QLatin1String("\f"), QLatin1String("\\f")); + str.replace(QLatin1String("\n"), QLatin1String("\\n")); + str.replace(QLatin1String("\r"), QLatin1String("\\r")); + str.replace(QLatin1String("\t"), QLatin1String("\\t")); + return QString(QLatin1String("\"%1\"")).arg(str); + } + + static QByteArray join(const QList &list, const QByteArray &sep) { + QByteArray res; + Q_FOREACH(const QByteArray &i, list) { + if (!res.isEmpty()) { + res += sep; + } + res += i; + } + return res; + } + + /** + * parseValue + */ + static QVariant parseValue(const QString &json, int &index, bool &success) { + // Determine what kind of data we should parse by + // checking out the upcoming token + switch(lookAhead(json, index)) { + case JsonTokenString: + return parseString(json, index, success); + case JsonTokenNumber: + return parseNumber(json, index); + case JsonTokenCurlyOpen: + return parseObject(json, index, success); + case JsonTokenSquaredOpen: + return parseArray(json, index, success); + case JsonTokenTrue: + nextToken(json, index); + return QVariant(true); + case JsonTokenFalse: + nextToken(json, index); + return QVariant(false); + case JsonTokenNull: + nextToken(json, index); + return QVariant(); + case JsonTokenNone: + break; + } + + // If there were no tokens, flag the failure and return an empty QVariant + success = false; + return QVariant(); + } + + /** + * parseObject + */ + static QVariant parseObject(const QString &json, int &index, bool &success) { + QVariantMap map; + int token; + + // Get rid of the whitespace and increment index + nextToken(json, index); + + // Loop through all of the key/value pairs of the object + bool done = false; + while (!done) { + // Get the upcoming token + token = lookAhead(json, index); + + if (token == JsonTokenNone) { + success = false; + return QVariantMap(); + } else if (token == JsonTokenComma) { + nextToken(json, index); + } else if (token == JsonTokenCurlyClose) { + nextToken(json, index); + return map; + } else { + // Parse the key/value pair's name + QString name = parseString(json, index, success).toString(); + + if (!success) { + return QVariantMap(); + } + + // Get the next token + token = nextToken(json, index); + + // If the next token is not a colon, flag the failure + // return an empty QVariant + if (token != JsonTokenColon) { + success = false; + return QVariant(QVariantMap()); + } + + // Parse the key/value pair's value + QVariant value = parseValue(json, index, success); + + if (!success) { + return QVariantMap(); + } + + // Assign the value to the key in the map + map[name] = value; + } + } + + // Return the map successfully + return QVariant(map); + } + + /** + * parseArray + */ + static QVariant parseArray(const QString &json, int &index, bool &success) { + QVariantList list; + + nextToken(json, index); + + bool done = false; + while(!done) { + int token = lookAhead(json, index); + + if (token == JsonTokenNone) { + success = false; + return QVariantList(); + } else if (token == JsonTokenComma) { + nextToken(json, index); + } else if (token == JsonTokenSquaredClose) { + nextToken(json, index); + break; + } else { + QVariant value = parseValue(json, index, success); + if (!success) { + return QVariantList(); + } + list.push_back(value); + } + } + + return QVariant(list); + } + + /** + * parseString + */ + static QVariant parseString(const QString &json, int &index, bool &success) { + QString s; + QChar c; + + eatWhitespace(json, index); + + c = json[index++]; + + bool complete = false; + while(!complete) { + if (index == json.size()) { + break; + } + + c = json[index++]; + + if (c == '\"') { + complete = true; + break; + } else if (c == '\\') { + if (index == json.size()) { + break; + } + + c = json[index++]; + + if (c == '\"') { + s.append('\"'); + } else if (c == '\\') { + s.append('\\'); + } else if (c == '/') { + s.append('/'); + } else if (c == 'b') { + s.append('\b'); + } else if (c == 'f') { + s.append('\f'); + } else if (c == 'n') { + s.append('\n'); + } else if (c == 'r') { + s.append('\r'); + } else if (c == 't') { + s.append('\t'); + } else if (c == 'u') { + int remainingLength = json.size() - index; + if (remainingLength >= 4) { + QString unicodeStr = json.mid(index, 4); + + int symbol = unicodeStr.toInt(0, 16); + + s.append(QChar(symbol)); + + index += 4; + } else { + break; + } + } + } else { + s.append(c); + } + } + + if (!complete) { + success = false; + return QVariant(); + } + + return QVariant(s); + } + + /** + * parseNumber + */ + static QVariant parseNumber(const QString &json, int &index) { + eatWhitespace(json, index); + + int lastIndex = lastIndexOfNumber(json, index); + int charLength = (lastIndex - index) + 1; + QString numberStr; + + numberStr = json.mid(index, charLength); + + index = lastIndex + 1; + bool ok; + + if (numberStr.contains('.')) { + return QVariant(numberStr.toDouble(NULL)); + } else if (numberStr.startsWith('-')) { + int i = numberStr.toInt(&ok); + if (!ok) { + qlonglong ll = numberStr.toLongLong(&ok); + return ok ? ll : QVariant(numberStr); + } + return i; + } else { + uint u = numberStr.toUInt(&ok); + if (!ok) { + qulonglong ull = numberStr.toULongLong(&ok); + return ok ? ull : QVariant(numberStr); + } + return u; + } + } + + /** + * lastIndexOfNumber + */ + static int lastIndexOfNumber(const QString &json, int index) { + int lastIndex; + + for(lastIndex = index; lastIndex < json.size(); lastIndex++) { + if (QString("0123456789+-.eE").indexOf(json[lastIndex]) == -1) { + break; + } + } + + return lastIndex -1; + } + + /** + * eatWhitespace + */ + static void eatWhitespace(const QString &json, int &index) { + for(; index < json.size(); index++) { + if (QString(" \t\n\r").indexOf(json[index]) == -1) { + break; + } + } + } + + /** + * lookAhead + */ + static int lookAhead(const QString &json, int index) { + int saveIndex = index; + return nextToken(json, saveIndex); + } + + /** + * nextToken + */ + static int nextToken(const QString &json, int &index) { + eatWhitespace(json, index); + + if (index == json.size()) { + return JsonTokenNone; + } + + QChar c = json[index]; + index++; + switch(c.toLatin1()) { + case '{': return JsonTokenCurlyOpen; + case '}': return JsonTokenCurlyClose; + case '[': return JsonTokenSquaredOpen; + case ']': return JsonTokenSquaredClose; + case ',': return JsonTokenComma; + case '"': return JsonTokenString; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + case '-': return JsonTokenNumber; + case ':': return JsonTokenColon; + } + index--; // ^ WTF? + + int remainingLength = json.size() - index; + + // True + if (remainingLength >= 4) { + if (json[index] == 't' && json[index + 1] == 'r' && + json[index + 2] == 'u' && json[index + 3] == 'e') { + index += 4; + return JsonTokenTrue; + } + } + + // False + if (remainingLength >= 5) { + if (json[index] == 'f' && json[index + 1] == 'a' && + json[index + 2] == 'l' && json[index + 3] == 's' && + json[index + 4] == 'e') { + index += 5; + return JsonTokenFalse; + } + } + + // Null + if (remainingLength >= 4) { + if (json[index] == 'n' && json[index + 1] == 'u' && + json[index + 2] == 'l' && json[index + 3] == 'l') { + index += 4; + return JsonTokenNull; + } + } + + return JsonTokenNone; + } +} //end namespace diff --git a/src/json.h b/src/json.h index d2cfa9a8..a61f6c85 100644 --- a/src/json.h +++ b/src/json.h @@ -1,204 +1,94 @@ -/* Copyright 2011 Eeli Reilin. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY ''AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL EELI REILIN OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, - * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation - * are those of the authors and should not be interpreted as representing - * official policies, either expressed or implied, of Eeli Reilin. - */ - -/** - * \file json.h - */ - -#ifndef JSON_H -#define JSON_H - -#include -#include - -namespace QtJson -{ - -/** - * \enum JsonToken - */ -enum JsonToken -{ - JsonTokenNone = 0, - JsonTokenCurlyOpen = 1, - JsonTokenCurlyClose = 2, - JsonTokenSquaredOpen = 3, - JsonTokenSquaredClose = 4, - JsonTokenColon = 5, - JsonTokenComma = 6, - JsonTokenString = 7, - JsonTokenNumber = 8, - JsonTokenTrue = 9, - JsonTokenFalse = 10, - JsonTokenNull = 11 -}; - -/** - * \class Json - * \brief A JSON data parser - * - * Json parses a JSON data into a QVariant hierarchy. - */ -class Json -{ - public: - /** - * Parse a JSON string - * - * \param json The JSON data - */ - static QVariant parse(const QString &json); - - /** - * Parse a JSON string - * - * \param json The JSON data - * \param success The success of the parsing - */ - static QVariant parse(const QString &json, bool &success); - - /** - * This method generates a textual JSON representation - * - * \param data The JSON data generated by the parser. - * \param success The success of the serialization - */ - static QByteArray serialize(const QVariant &data); - - /** - * This method generates a textual JSON representation - * - * \param data The JSON data generated by the parser. - * \param success The success of the serialization - * - * \return QByteArray Textual JSON representation - */ - static QByteArray serialize(const QVariant &data, bool &success); - - private: - /** - * Parses a value starting from index - * - * \param json The JSON data - * \param index The start index - * \param success The success of the parse process - * - * \return QVariant The parsed value - */ - static QVariant parseValue(const QString &json, int &index, - bool &success); - - /** - * Parses an object starting from index - * - * \param json The JSON data - * \param index The start index - * \param success The success of the object parse - * - * \return QVariant The parsed object map - */ - static QVariant parseObject(const QString &json, int &index, - bool &success); - - /** - * Parses an array starting from index - * - * \param json The JSON data - * \param index The starting index - * \param success The success of the array parse - * - * \return QVariant The parsed variant array - */ - static QVariant parseArray(const QString &json, int &index, - bool &success); - - /** - * Parses a string starting from index - * - * \param json The JSON data - * \param index The starting index - * \param success The success of the string parse - * - * \return QVariant The parsed string - */ - static QVariant parseString(const QString &json, int &index, - bool &success); - - /** - * Parses a number starting from index - * - * \param json The JSON data - * \param index The starting index - * - * \return QVariant The parsed number - */ - static QVariant parseNumber(const QString &json, int &index); - - /** - * Get the last index of a number starting from index - * - * \param json The JSON data - * \param index The starting index - * - * \return The last index of the number - */ - static int lastIndexOfNumber(const QString &json, int index); - - /** - * Skip unwanted whitespace symbols starting from index - * - * \param json The JSON data - * \param index The start index - */ - static void eatWhitespace(const QString &json, int &index); - - /** - * Check what token lies ahead - * - * \param json The JSON data - * \param index The starting index - * - * \return int The upcoming token - */ - static int lookAhead(const QString &json, int index); - - /** - * Get the next JSON token - * - * \param json The JSON data - * \param index The starting index - * - * \return int The next JSON token - */ - static int nextToken(const QString &json, int &index); -}; - - -} //end namespace - -#endif //JSON_H +/** + * QtJson - A simple class for parsing JSON data into a QVariant hierarchies and vice-versa. + * Copyright (C) 2011 Eeli Reilin + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +/** + * \file json.h + */ + +#ifndef JSON_H +#define JSON_H + +#include +#include + + +/** + * \namespace QtJson + * \brief A JSON data parser + * + * Json parses a JSON data into a QVariant hierarchy. + */ +namespace QtJson { + typedef QVariantMap JsonObject; + typedef QVariantList JsonArray; + + /** + * Parse a JSON string + * + * \param json The JSON data + */ + QVariant parse(const QString &json); + + /** + * Parse a JSON string + * + * \param json The JSON data + * \param success The success of the parsing + */ + QVariant parse(const QString &json, bool &success); + + /** + * This method generates a textual JSON representation + * + * \param data The JSON data generated by the parser. + * + * \return QByteArray Textual JSON representation in UTF-8 + */ + QByteArray serialize(const QVariant &data); + + /** + * This method generates a textual JSON representation + * + * \param data The JSON data generated by the parser. + * \param success The success of the serialization + * + * \return QByteArray Textual JSON representation in UTF-8 + */ + QByteArray serialize(const QVariant &data, bool &success); + + /** + * This method generates a textual JSON representation + * + * \param data The JSON data generated by the parser. + * + * \return QString Textual JSON representation + */ + QString serializeStr(const QVariant &data); + + /** + * This method generates a textual JSON representation + * + * \param data The JSON data generated by the parser. + * \param success The success of the serialization + * + * \return QString Textual JSON representation + */ + QString serializeStr(const QVariant &data, bool &success); +} + +#endif //JSON_H diff --git a/src/logbuffer.cpp b/src/logbuffer.cpp index 6f3f640f..4f72e551 100644 --- a/src/logbuffer.cpp +++ b/src/logbuffer.cpp @@ -120,6 +120,7 @@ char LogBuffer::msgTypeID(QtMsgType type) case QtWarningMsg: return 'W'; case QtCriticalMsg: return 'C'; case QtFatalMsg: return 'F'; + default: return '?'; } } diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 76bff3a0..14e27e76 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -56,6 +56,7 @@ along with Mod Organizer. If not, see . #include "savetextasdialog.h" #include "problemsdialog.h" #include "previewdialog.h" +#include "aboutdialog.h" #include #include #include @@ -547,6 +548,12 @@ bool MainWindow::checkForProblems() return false; } +void MainWindow::about() +{ + AboutDialog dialog(m_Updater.getVersion().displayString(), this); + dialog.exec(); +} + void MainWindow::createHelpWidget() { @@ -604,6 +611,8 @@ void MainWindow::createHelpWidget() } buttonMenu->addMenu(tutorialMenu); + buttonMenu->addAction(tr("About"), this, SLOT(about())); + buttonMenu->addAction(tr("About Qt"), qApp, SLOT(aboutQt())); } diff --git a/src/mainwindow.h b/src/mainwindow.h index 59bee7bb..e7b3f7e1 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -527,6 +527,7 @@ private slots: void refreshSavesIfOpen(); void expandDataTreeItem(QTreeWidgetItem *item); + void about(); private slots: // ui slots // actions diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 7f221bdc..3657d48a 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -22,7 +22,6 @@ along with Mod Organizer. If not, see . #include "report.h" #include "utility.h" -#include "json.h" #include "messagedialog.h" #include "bbcode.h" #include "questionboxmemory.h" @@ -41,8 +40,6 @@ along with Mod Organizer. If not, see . #include -using QtJson::Json; - using namespace MOBase; using namespace MOShared; diff --git a/src/modlist.cpp b/src/modlist.cpp index 7f09654d..e2cb7cf0 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -305,7 +305,7 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const return QString(); } } else if (column == COL_VERSION) { - QString text = tr("installed version: %1, newest version: %2").arg(modInfo->getVersion().displayString()).arg(modInfo->getNewestVersion().displayString()); + QString text = tr("installed version: \"%1\", newest version: \"%2\"").arg(modInfo->getVersion().displayString()).arg(modInfo->getNewestVersion().displayString()); if (modInfo->downgradeAvailable()) { text += "
    " + tr("The newest version on Nexus seems to be older than the one you have installed. This could either mean the version you have has been withdrawn " "(i.e. due to a bug) or the author uses a non-standard versioning scheme and that newest version is actually newer. " diff --git a/src/nexusinterface.cpp b/src/nexusinterface.cpp index 309915aa..6b87822a 100644 --- a/src/nexusinterface.cpp +++ b/src/nexusinterface.cpp @@ -20,14 +20,13 @@ along with Mod Organizer. If not, see . #include "nexusinterface.h" #include "nxmaccessmanager.h" #include "utility.h" -#include "json.h" +#include #include "selectiondialog.h" #include #include #include #include -using QtJson::Json; using namespace MOBase; using namespace MOShared; @@ -458,7 +457,7 @@ void NexusInterface::requestFinished(std::list::iterator iter) emit nxmRequestFailed(iter->m_ModID, iter->m_UserData, iter->m_ID, nexusError); } else { bool ok; - QVariant result = Json::parse(data, ok); + QVariant result = QtJson::parse(data, ok); if (result.isValid() && ok) { switch (iter->m_Type) { case NXMRequestInfo::TYPE_DESCRIPTION: { diff --git a/src/organizer.pro b/src/organizer.pro index dd745edb..fd72dea3 100644 --- a/src/organizer.pro +++ b/src/organizer.pro @@ -47,7 +47,6 @@ SOURCES += \ logbuffer.cpp \ lockeddialog.cpp \ loadmechanism.cpp \ - json.cpp \ installationmanager.cpp \ helper.cpp \ filedialogmemory.cpp \ @@ -80,7 +79,9 @@ SOURCES += \ ../esptk/subrecord.cpp \ noeditdelegate.cpp \ previewgenerator.cpp \ - previewdialog.cpp + previewdialog.cpp \ + aboutdialog.cpp \ + json.cpp HEADERS += \ @@ -116,7 +117,6 @@ HEADERS += \ logbuffer.h \ lockeddialog.h \ loadmechanism.h \ - json.h \ installationmanager.h \ helper.h \ filedialogmemory.h \ @@ -150,7 +150,9 @@ HEADERS += \ ../esptk/espexceptions.h \ noeditdelegate.h \ previewgenerator.h \ - previewdialog.h + previewdialog.h \ + aboutdialog.h \ + json.h FORMS += \ transfersavesdialog.ui \ @@ -180,7 +182,8 @@ FORMS += \ profileinputdialog.ui \ savetextasdialog.ui \ problemsdialog.ui \ - previewdialog.ui + previewdialog.ui \ + aboutdialog.ui INCLUDEPATH += ../shared ../archive ../uibase ../bsatk ../esptk "$(BOOSTPATH)" @@ -246,6 +249,9 @@ DEFINES += UNICODE _UNICODE _CRT_SECURE_NO_WARNINGS NOMINMAX DEFINES += BOOST_DISABLE_ASSERTS NDEBUG #DEFINES += QMLJSDEBUGGER +HGID = $$system(hg id -i) +DEFINES += HGID=\\\"$${HGID}\\\" + SRCDIR = $$PWD SRCDIR ~= s,/,$$QMAKE_DIR_SEP,g OUTDIR ~= s,/,$$QMAKE_DIR_SEP,g diff --git a/src/resources/emblem-favorite - 64.png b/src/resources/emblem-favorite - 64.png new file mode 100644 index 00000000..97b507ed Binary files /dev/null and b/src/resources/emblem-favorite - 64.png differ -- cgit v1.3.1 From e69210b3a78c4a6c63086d84e6bdb2c3b47d8944 Mon Sep 17 00:00:00 2001 From: Tannin Date: Sat, 18 Jan 2014 19:51:58 +0100 Subject: - when a download server returns a text file, it's assumed to be an error and the text displayed as an error - save games can now be deleted from inside MO - bugfix: removing the pending download entry failed if the download-url request failed - bugfix: download manager didn't stop automatically resuming failed downloads under certain circumstances - bugfix: uninstalled downloads were treated as not-finished when refreshing the download list - bugfix: updating the filesystem watcher on the saves directory didn't work correctly --- src/aboutdialog.cpp | 1 - src/downloadmanager.cpp | 33 ++++++++++++++++----------------- src/downloadmanager.h | 2 +- src/mainwindow.cpp | 23 ++++++++++++++++++++++- src/mainwindow.h | 3 ++- src/modinfo.cpp | 4 ++-- src/modinfo.h | 2 +- src/nexusinterface.cpp | 34 +++++++++++++++++----------------- src/nexusinterface.h | 8 ++++---- src/pluginlist.cpp | 1 - src/selfupdater.cpp | 2 +- src/selfupdater.h | 2 +- 12 files changed, 67 insertions(+), 48 deletions(-) (limited to 'src/mainwindow.h') diff --git a/src/aboutdialog.cpp b/src/aboutdialog.cpp index 94e4e54c..c7f95640 100644 --- a/src/aboutdialog.cpp +++ b/src/aboutdialog.cpp @@ -73,7 +73,6 @@ void AboutDialog::on_creditsList_currentItemChanged(QListWidgetItem *current, QL auto iter = m_LicenseFiles.find(current->data(Qt::UserRole).toInt()); if (iter != m_LicenseFiles.end()) { QString filePath = qApp->applicationDirPath() + "/license/" + iter->second; -qDebug("%s", qPrintable(filePath)); QString text = MOBase::readFileText(filePath); ui->licenseText->setText(text); } else { diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index dd320f70..31d8bcec 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -26,15 +26,16 @@ along with Mod Organizer. If not, see . #include "utility.h" #include "json.h" #include "selectiondialog.h" +#include #include #include #include #include #include -#include -#include #include #include +#include +#include using namespace MOBase; @@ -246,7 +247,7 @@ void DownloadManager::refreshList() // remove finished downloads for (QVector::iterator Iter = m_ActiveDownloads.begin(); Iter != m_ActiveDownloads.end();) { - if (((*Iter)->m_State == STATE_READY) || ((*Iter)->m_State == STATE_INSTALLED)) { + if (((*Iter)->m_State == STATE_READY) || ((*Iter)->m_State == STATE_INSTALLED) || ((*Iter)->m_State == STATE_UNINSTALLED)) { delete *Iter; Iter = m_ActiveDownloads.erase(Iter); } else { @@ -414,7 +415,7 @@ void DownloadManager::addNXMDownload(const QString &url) NXMUrl nxmInfo(url); QString managedGame = ToQString(MOShared::GameInfo::instance().getGameShortName()); - qDebug("add nxm download", qPrintable(url)); + qDebug("add nxm download: %s", qPrintable(url)); if (nxmInfo.game().compare(managedGame, Qt::CaseInsensitive) != 0) { qDebug("download requested for wrong game (game: %s, url: %s)", qPrintable(managedGame), qPrintable(nxmInfo.game())); QMessageBox::information(NULL, tr("Wrong Game"), tr("The download link is for a mod for \"%1\" but this instance of MO " @@ -1089,11 +1090,7 @@ void DownloadManager::nxmFileInfoAvailable(int modID, int fileID, QVariant userD info.m_FileName = result["uri"].toString(); info.m_FileTime = matchDate(result["date"].toString()); - if (userData.isValid()) { - m_RequestIDs.insert(m_NexusInterface->requestDownloadURL(modID, fileID, this, qVariantFromValue(info), userData.toString())); - } else { - m_RequestIDs.insert(m_NexusInterface->requestDownloadURL(modID, fileID, this, qVariantFromValue(info))); - } + m_RequestIDs.insert(m_NexusInterface->requestDownloadURL(modID, fileID, this, QVariant::fromValue(info))); } @@ -1176,8 +1173,6 @@ void DownloadManager::nxmDownloadURLsAvailable(int modID, int fileID, QVariant u m_RequestIDs.erase(idIter); } - qDebug("download urls received (modid %d, fileid %d)", modID, fileID); - NexusInfo info = userData.value(); QVariantList resultList = resultData.toList(); if (resultList.length() == 0) { @@ -1200,7 +1195,7 @@ void DownloadManager::nxmDownloadURLsAvailable(int modID, int fileID, QVariant u } -void DownloadManager::nxmRequestFailed(int modID, QVariant userData, int requestID, const QString &errorString) +void DownloadManager::nxmRequestFailed(int modID, int fileID, QVariant userData, int requestID, const QString &errorString) { std::set::iterator idIter = m_RequestIDs.find(requestID); if (idIter == m_RequestIDs.end()) { @@ -1225,7 +1220,7 @@ void DownloadManager::nxmRequestFailed(int modID, QVariant userData, int request } } - removePending(modID, userData.toInt()); + removePending(modID, fileID); emit showMessage(tr("Failed to request file info from nexus: %1").arg(errorString)); } @@ -1243,14 +1238,18 @@ void DownloadManager::downloadFinished() TaskProgressManager::instance().forgetMe(info->m_TaskProgressId); bool error = false; - if ((info->m_State != STATE_CANCELING) && (info->m_State != STATE_PAUSING)) { + bool textData = reply->header(QNetworkRequest::ContentTypeHeader).toString().startsWith("text", Qt::CaseInsensitive); if ((info->m_Output.size() == 0) || ((reply->error() != QNetworkReply::NoError) && (reply->error() != QNetworkReply::OperationCanceledError)) || - reply->header(QNetworkRequest::ContentTypeHeader).toString().startsWith("text", Qt::CaseInsensitive)) { + textData) { if (info->m_Tries == 0) { - emit showMessage(tr("Download failed: %1 (%2)").arg(reply->errorString()).arg(reply->error())); + if (textData && (reply->error() == QNetworkReply::NoError)) { + emit showMessage(tr("Download failed. Server reported: %1").arg(readFileText(info->m_Output.fileName()))); + } else { + emit showMessage(tr("Download failed: %1 (%2)").arg(reply->errorString()).arg(reply->error())); + } } error = true; setState(info, STATE_PAUSING); @@ -1310,7 +1309,7 @@ void DownloadManager::downloadFinished() if ((info->m_Tries > 0) && error) { --info->m_Tries; - resumeDownload(index); + resumeDownloadInt(index); } } else { qWarning("no download index %d", index); diff --git a/src/downloadmanager.h b/src/downloadmanager.h index 62396666..80b99ad2 100644 --- a/src/downloadmanager.h +++ b/src/downloadmanager.h @@ -412,7 +412,7 @@ public slots: void nxmDownloadURLsAvailable(int modID, int fileID, QVariant userData, QVariant resultData, int requestID); - void nxmRequestFailed(int modID, QVariant userData, int requestID, const QString &errorString); + void nxmRequestFailed(int modID, int fileID, QVariant userData, int requestID, const QString &errorString); private slots: diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 14e27e76..582bc283 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -66,6 +66,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include #include #include #include @@ -1737,6 +1738,9 @@ void MainWindow::refreshSaveList() savesDir.setPath(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getDocumentsDir() + L"\\" + path))); } + if (m_SavesWatcher.directories().length() > 0) { + m_SavesWatcher.removePaths(m_SavesWatcher.directories()); + } m_SavesWatcher.addPath(savesDir.absolutePath()); QStringList filters; @@ -3868,6 +3872,22 @@ void MainWindow::on_categoriesList_itemSelectionChanged() } +void MainWindow::deleteSavegame_clicked() +{ + QListWidgetItem *selectedItem = ui->savegameList->item(m_SelectedSaveGame); + if (selectedItem == NULL) { + return; + } + + QString fileName = selectedItem->data(Qt::UserRole).toString(); + + if (QMessageBox::question(this, tr("Confirm"), tr("Really delete \"%1\"?").arg(selectedItem->text()), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { + shellDelete(QStringList() << fileName); + } +} + + void MainWindow::fixMods_clicked() { QListWidgetItem *selectedItem = ui->savegameList->item(m_SelectedSaveGame); @@ -3973,6 +3993,7 @@ void MainWindow::on_savegameList_customContextMenuRequested(const QPoint &pos) QMenu menu; menu.addAction(tr("Fix Mods..."), this, SLOT(fixMods_clicked())); + menu.addAction(tr("Delete"), this, SLOT(deleteSavegame_clicked())); menu.exec(ui->savegameList->mapToGlobal(pos)); } @@ -4721,7 +4742,7 @@ void MainWindow::nxmDownloadURLs(int, int, QVariant, QVariant resultData, int) } -void MainWindow::nxmRequestFailed(int modID, QVariant, int, const QString &errorString) +void MainWindow::nxmRequestFailed(int modID, int, QVariant, int, const QString &errorString) { if (modID == -1) { // must be the update-check that failed diff --git a/src/mainwindow.h b/src/mainwindow.h index e7b3f7e1..c01ce767 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -398,6 +398,7 @@ private slots: void openExplorer_clicked(); void information_clicked(); // savegame context menu + void deleteSavegame_clicked(); void fixMods_clicked(); // data-tree context menu void writeDataToFile(); @@ -464,7 +465,7 @@ private slots: void nxmUpdatesAvailable(const std::vector &modIDs, QVariant userData, QVariant resultData, int requestID); // void nxmEndorsementToggled(int, QVariant, QVariant resultData, int); void nxmDownloadURLs(int modID, int fileID, QVariant userData, QVariant resultData, int requestID); - void nxmRequestFailed(int modID, QVariant userData, int requestID, const QString &errorString); + void nxmRequestFailed(int modID, int fileID, QVariant userData, int requestID, const QString &errorString); void editCategories(); diff --git a/src/modinfo.cpp b/src/modinfo.cpp index 6569f897..85612b32 100644 --- a/src/modinfo.cpp +++ b/src/modinfo.cpp @@ -345,7 +345,7 @@ ModInfoRegular::ModInfoRegular(const QDir &path, DirectoryEntry **directoryStruc connect(&m_NexusBridge, SIGNAL(descriptionAvailable(int,QVariant,QVariant)), this, SLOT(nxmDescriptionAvailable(int,QVariant,QVariant))); connect(&m_NexusBridge, SIGNAL(endorsementToggled(int,QVariant,QVariant)), this, SLOT(nxmEndorsementToggled(int,QVariant,QVariant))); - connect(&m_NexusBridge, SIGNAL(requestFailed(int,QVariant,QString)), this, SLOT(nxmRequestFailed(int,QVariant,QString))); + connect(&m_NexusBridge, SIGNAL(requestFailed(int,int,QVariant,QString)), this, SLOT(nxmRequestFailed(int,int,QVariant,QString))); } @@ -439,7 +439,7 @@ void ModInfoRegular::nxmEndorsementToggled(int, QVariant, QVariant resultData) } -void ModInfoRegular::nxmRequestFailed(int, QVariant userData, const QString &errorMessage) +void ModInfoRegular::nxmRequestFailed(int, int, QVariant userData, const QString &errorMessage) { QString fullMessage = errorMessage; if (userData.canConvert() && (userData.toInt() == 1)) { diff --git a/src/modinfo.h b/src/modinfo.h index 677d8a82..83654c7e 100644 --- a/src/modinfo.h +++ b/src/modinfo.h @@ -762,7 +762,7 @@ private slots: void nxmDescriptionAvailable(int modID, QVariant userData, QVariant resultData); void nxmEndorsementToggled(int, QVariant userData, QVariant resultData); - void nxmRequestFailed(int modID, QVariant userData, const QString &errorMessage); + void nxmRequestFailed(int modID, int fileID, QVariant userData, const QString &errorMessage); private: diff --git a/src/nexusinterface.cpp b/src/nexusinterface.cpp index 6b87822a..64dd6272 100644 --- a/src/nexusinterface.cpp +++ b/src/nexusinterface.cpp @@ -128,12 +128,12 @@ void NexusBridge::nxmEndorsementToggled(int modID, QVariant userData, QVariant r } } -void NexusBridge::nxmRequestFailed(int modID, QVariant userData, int requestID, const QString &errorMessage) +void NexusBridge::nxmRequestFailed(int modID, int fileID, QVariant userData, int requestID, const QString &errorMessage) { std::set::iterator iter = m_RequestIDs.find(requestID); if (iter != m_RequestIDs.end()) { m_RequestIDs.erase(iter); - emit requestFailed(modID, userData, errorMessage); + emit requestFailed(modID, fileID, userData, errorMessage); } } @@ -257,8 +257,8 @@ int NexusInterface::requestDescription(int modID, QObject *receiver, QVariant us connect(this, SIGNAL(nxmDescriptionAvailable(int,QVariant,QVariant,int)), receiver, SLOT(nxmDescriptionAvailable(int,QVariant,QVariant,int)), Qt::UniqueConnection); - connect(this, SIGNAL(nxmRequestFailed(int,QVariant,int,QString)), - receiver, SLOT(nxmRequestFailed(int,QVariant,int,QString)), Qt::UniqueConnection); + connect(this, SIGNAL(nxmRequestFailed(int,int,QVariant,int,QString)), + receiver, SLOT(nxmRequestFailed(int,int,QVariant,int,QString)), Qt::UniqueConnection); nextRequest(); return requestInfo.m_ID; @@ -273,8 +273,8 @@ int NexusInterface::requestUpdates(const std::vector &modIDs, QObject *rece connect(this, SIGNAL(nxmUpdatesAvailable(std::vector,QVariant,QVariant,int)), receiver, SLOT(nxmUpdatesAvailable(std::vector,QVariant,QVariant,int)), Qt::UniqueConnection); - connect(this, SIGNAL(nxmRequestFailed(int,QVariant,int,QString)), - receiver, SLOT(nxmRequestFailed(int,QVariant,int,QString)), Qt::UniqueConnection); + connect(this, SIGNAL(nxmRequestFailed(int,int,QVariant,int,QString)), + receiver, SLOT(nxmRequestFailed(int,int,QVariant,int,QString)), Qt::UniqueConnection); nextRequest(); return requestInfo.m_ID; @@ -307,8 +307,8 @@ int NexusInterface::requestFiles(int modID, QObject *receiver, QVariant userData connect(this, SIGNAL(nxmFilesAvailable(int,QVariant,QVariant,int)), receiver, SLOT(nxmFilesAvailable(int,QVariant,QVariant,int)), Qt::UniqueConnection); - connect(this, SIGNAL(nxmRequestFailed(int,QVariant,int,QString)), - receiver, SLOT(nxmRequestFailed(int,QVariant,int,QString)), Qt::UniqueConnection); + connect(this, SIGNAL(nxmRequestFailed(int,int,QVariant,int,QString)), + receiver, SLOT(nxmRequestFailed(int,int,QVariant,int,QString)), Qt::UniqueConnection); // QTimer::singleShot(1000, this, SLOT(fakeFiles())); // static int fID = 42; @@ -327,8 +327,8 @@ int NexusInterface::requestFileInfo(int modID, int fileID, QObject *receiver, QV connect(this, SIGNAL(nxmFileInfoAvailable(int,int,QVariant,QVariant,int)), receiver, SLOT(nxmFileInfoAvailable(int,int,QVariant,QVariant,int)), Qt::UniqueConnection); - connect(this, SIGNAL(nxmRequestFailed(int,QVariant,int,QString)), - receiver, SLOT(nxmRequestFailed(int,QVariant,int,QString)), Qt::UniqueConnection); + connect(this, SIGNAL(nxmRequestFailed(int,int,QVariant,int,QString)), + receiver, SLOT(nxmRequestFailed(int,int,QVariant,int,QString)), Qt::UniqueConnection); nextRequest(); return requestInfo.m_ID; @@ -343,8 +343,8 @@ int NexusInterface::requestDownloadURL(int modID, int fileID, QObject *receiver, connect(this, SIGNAL(nxmDownloadURLsAvailable(int,int,QVariant,QVariant,int)), receiver, SLOT(nxmDownloadURLsAvailable(int,int,QVariant,QVariant,int)), Qt::UniqueConnection); - connect(this, SIGNAL(nxmRequestFailed(int,QVariant,int,QString)), - receiver, SLOT(nxmRequestFailed(int,QVariant,int,QString)), Qt::UniqueConnection); + connect(this, SIGNAL(nxmRequestFailed(int,int,QVariant,int,QString)), + receiver, SLOT(nxmRequestFailed(int,int,QVariant,int,QString)), Qt::UniqueConnection); nextRequest(); return requestInfo.m_ID; @@ -360,8 +360,8 @@ int NexusInterface::requestToggleEndorsement(int modID, bool endorse, QObject *r connect(this, SIGNAL(nxmEndorsementToggled(int,QVariant,QVariant,int)), receiver, SLOT(nxmEndorsementToggled(int,QVariant,QVariant,int)), Qt::UniqueConnection); - connect(this, SIGNAL(nxmRequestFailed(int,QVariant,int,QString)), - receiver, SLOT(nxmRequestFailed(int,QVariant,int,QString)), Qt::UniqueConnection); + connect(this, SIGNAL(nxmRequestFailed(int,int,QVariant,int,QString)), + receiver, SLOT(nxmRequestFailed(int,int,QVariant,int,QString)), Qt::UniqueConnection); nextRequest(); return requestInfo.m_ID; @@ -437,7 +437,7 @@ void NexusInterface::requestFinished(std::list::iterator iter) if (reply->error() != QNetworkReply::NoError) { qWarning("request failed: %s", reply->errorString().toUtf8().constData()); - emit nxmRequestFailed(iter->m_ModID, iter->m_UserData, iter->m_ID, reply->errorString()); + emit nxmRequestFailed(iter->m_ModID, iter->m_FileID, iter->m_UserData, iter->m_ID, reply->errorString()); } else { int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); if (statusCode == 301) { @@ -454,7 +454,7 @@ void NexusInterface::requestFinished(std::list::iterator iter) if (nexusError.length() == 0) { nexusError = tr("empty response"); } - emit nxmRequestFailed(iter->m_ModID, iter->m_UserData, iter->m_ID, nexusError); + emit nxmRequestFailed(iter->m_ModID, iter->m_FileID, iter->m_UserData, iter->m_ID, nexusError); } else { bool ok; QVariant result = QtJson::parse(data, ok); @@ -480,7 +480,7 @@ void NexusInterface::requestFinished(std::list::iterator iter) } break; } } else { - emit nxmRequestFailed(iter->m_ModID, iter->m_UserData, iter->m_ID, tr("invalid response")); + emit nxmRequestFailed(iter->m_ModID, iter->m_FileID, iter->m_UserData, iter->m_ID, tr("invalid response")); } } } diff --git a/src/nexusinterface.h b/src/nexusinterface.h index e7a01b0f..7b709e1c 100644 --- a/src/nexusinterface.h +++ b/src/nexusinterface.h @@ -105,7 +105,7 @@ public slots: void nxmFileInfoAvailable(int modID, int fileID, QVariant userData, QVariant resultData, int requestID); void nxmDownloadURLsAvailable(int modID, int fileID, QVariant userData, QVariant resultData, int requestID); void nxmEndorsementToggled(int modID, QVariant userData, QVariant resultData, int requestID); - void nxmRequestFailed(int modID, QVariant userData, int requestID, const QString &errorMessage); + void nxmRequestFailed(int modID, int fileID, QVariant userData, int requestID, const QString &errorMessage); private: @@ -240,7 +240,7 @@ signals: void nxmFileInfoAvailable(int modID, int fileID, QVariant userData, QVariant resultData, int requestID); void nxmDownloadURLsAvailable(int modID, int fileID, QVariant userData, QVariant resultData, int requestID); void nxmEndorsementToggled(int modID, QVariant userData, QVariant resultData, int requestID); - void nxmRequestFailed(int modID, QVariant userData, int requestID, const QString &errorString); + void nxmRequestFailed(int modID, int fileID, QVariant userData, int requestID, const QString &errorString); private slots: @@ -276,13 +276,13 @@ private: NXMRequestInfo(int modID, Type type, QVariant userData, const QString &url) : m_ModID(modID), m_FileID(0), m_Reply(NULL), m_Type(type), m_UserData(userData), - m_Timeout(NULL), m_Reroute(false), m_ID(s_NextID.fetchAndAddAcquire(1)), m_URL(url) {} + m_Timeout(NULL), m_Reroute(false), m_ID(s_NextID.fetchAndAddAcquire(1)), m_URL(url) {} NXMRequestInfo(std::vector modIDList, Type type, QVariant userData, const QString &url) : m_ModID(-1), m_ModIDList(modIDList), m_FileID(0), m_Reply(NULL), m_Type(type), m_UserData(userData), m_Timeout(NULL), m_Reroute(false), m_ID(s_NextID.fetchAndAddAcquire(1)), m_URL(url) {} NXMRequestInfo(int modID, int fileID, Type type, QVariant userData, const QString &url) : m_ModID(modID), m_FileID(fileID), m_Reply(NULL), m_Type(type), m_UserData(userData), - m_Timeout(NULL), m_Reroute(false), m_ID(s_NextID.fetchAndAddAcquire(1)), m_URL(url) {} + m_Timeout(NULL), m_Reroute(false), m_ID(s_NextID.fetchAndAddAcquire(1)), m_URL(url) {} private: static QAtomicInt s_NextID; diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index a0045ab3..85137390 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -312,7 +312,6 @@ bool PluginList::readLoadOrder(const QString &fileName) void PluginList::readEnabledFrom(const QString &fileName) { -qDebug("read enabled from %s", qPrintable(fileName)); for (std::vector::iterator iter = m_ESPs.begin(); iter != m_ESPs.end(); ++iter) { if (!iter->m_ForceEnabled) { iter->m_Enabled = false; diff --git a/src/selfupdater.cpp b/src/selfupdater.cpp index 2a9ca893..3a0db83f 100644 --- a/src/selfupdater.cpp +++ b/src/selfupdater.cpp @@ -428,7 +428,7 @@ void SelfUpdater::nxmFilesAvailable(int, QVariant userData, QVariant resultData, } -void SelfUpdater::nxmRequestFailed(int, QVariant, int requestID, const QString &errorMessage) +void SelfUpdater::nxmRequestFailed(int, int, QVariant, int requestID, const QString &errorMessage) { if (requestID == m_UpdateRequestID) { m_UpdateRequestID = -1; diff --git a/src/selfupdater.h b/src/selfupdater.h index 2c4c1ecd..14f7e90a 100644 --- a/src/selfupdater.h +++ b/src/selfupdater.h @@ -87,7 +87,7 @@ public slots: void nxmDescriptionAvailable(int modID, QVariant userData, QVariant resultData, int requestID); void nxmFilesAvailable(int modID, QVariant userData, QVariant resultData, int requestID); - void nxmRequestFailed(int modID, QVariant userData, int requestID, const QString &errorMessage); + void nxmRequestFailed(int modID, int fileID, QVariant userData, int requestID, const QString &errorMessage); void nxmDownloadURLsAvailable(int modID, int fileID, QVariant userData, QVariant resultData, int requestID); signals: -- cgit v1.3.1 From e597823337c858f2985c615ee5147f47567991ee Mon Sep 17 00:00:00 2001 From: Tannin Date: Thu, 23 Jan 2014 00:05:46 +0100 Subject: - boss integration - plugin list can now also display multiple flags for a file (like the mod list) - changed some compiler&linker settings to produce smaller binaries --- src/ModOrganizer.pro | 3 +- src/aboutdialog.cpp | 2 + src/icondelegate.cpp | 30 +-- src/icondelegate.h | 5 +- src/lockeddialog.cpp | 104 ++++++----- src/lockeddialog.h | 96 +++++----- src/logbuffer.cpp | 3 +- src/main.cpp | 5 +- src/mainwindow.cpp | 26 ++- src/mainwindow.h | 1 + src/mainwindow.ui | 82 +++++++-- src/modflagicondelegate.cpp | 50 +++++ src/modflagicondelegate.h | 18 ++ src/organizer.pro | 32 ++-- src/pdll.h | 402 +++++++++++++++++++++++++++++++++++++++++ src/pluginflagicondelegate.cpp | 24 +++ src/pluginflagicondelegate.h | 17 ++ src/pluginlist.cpp | 270 +++++++++++++++++++++++---- src/pluginlist.h | 95 +++++++++- src/pluginlistsortproxy.cpp | 5 - src/profile.cpp | 34 +--- src/resources.qrc | 2 +- src/safewritefile.cpp | 48 +++++ src/safewritefile.h | 51 ++++++ 24 files changed, 1179 insertions(+), 226 deletions(-) create mode 100644 src/modflagicondelegate.cpp create mode 100644 src/modflagicondelegate.h create mode 100644 src/pdll.h create mode 100644 src/pluginflagicondelegate.cpp create mode 100644 src/pluginflagicondelegate.h create mode 100644 src/safewritefile.cpp create mode 100644 src/safewritefile.h (limited to 'src/mainwindow.h') diff --git a/src/ModOrganizer.pro b/src/ModOrganizer.pro index dbf6ab6e..05b05855 100644 --- a/src/ModOrganizer.pro +++ b/src/ModOrganizer.pro @@ -13,11 +13,12 @@ SUBDIRS = bsatk \ nxmhandler \ BossDummy \ pythonRunner \ + boss_modified \ esptk plugins.depends = pythonRunner hookdll.depends = shared -organizer.depends = shared uibase plugins +organizer.depends = shared uibase plugins boss_modified CONFIG(debug, debug|release) { DESTDIR = outputd diff --git a/src/aboutdialog.cpp b/src/aboutdialog.cpp index c7f95640..a8f9b4db 100644 --- a/src/aboutdialog.cpp +++ b/src/aboutdialog.cpp @@ -44,6 +44,8 @@ AboutDialog::AboutDialog(const QString &version, QWidget *parent) addLicense("7-zip", LICENSE_LGPL3); addLicense("ZLib", LICENSE_ZLIB); addLicense("NIF File Format Library", LICENSE_BSD3); + addLicense("BOSS (modified)", LICENSE_GPL3); + addLicense("Alphanum Algorithm", LICENSE_ZLIB); ui->nameLabel->setText(QString("%1 %2").arg(ui->nameLabel->text()).arg(version)); #ifdef HGID diff --git a/src/icondelegate.cpp b/src/icondelegate.cpp index 2540e1d5..048b09f9 100644 --- a/src/icondelegate.cpp +++ b/src/icondelegate.cpp @@ -29,38 +29,17 @@ IconDelegate::IconDelegate(QObject *parent) } -QIcon IconDelegate::getFlagIcon(ModInfo::EFlag flag) const -{ - switch (flag) { - case ModInfo::FLAG_BACKUP: return QIcon(":/MO/gui/emblem_backup"); - case ModInfo::FLAG_INVALID: return QIcon(":/MO/gui/emblem_problem"); - case ModInfo::FLAG_NOTENDORSED: return QIcon(":/MO/gui/emblem_notendorsed"); - case ModInfo::FLAG_NOTES: return QIcon(":/MO/gui/emblem_notes"); - case ModInfo::FLAG_CONFLICT_OVERWRITE: return QIcon(":/MO/gui/emblem_conflict_overwrite"); - case ModInfo::FLAG_CONFLICT_OVERWRITTEN: return QIcon(":/MO/gui/emblem_conflict_overwritten"); - case ModInfo::FLAG_CONFLICT_MIXED: return QIcon(":/MO/gui/emblem_conflict_mixed"); - case ModInfo::FLAG_CONFLICT_REDUNDANT: return QIcon(":MO/gui/emblem_conflict_redundant"); - default: return QIcon(); - } -} - - void IconDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { QStyledItemDelegate::paint(painter, option, index); - QVariant modid = index.data(Qt::UserRole + 1); - if (!modid.isValid()) { - return; - } - ModInfo::Ptr info = ModInfo::getByIndex(modid.toInt()); - std::vector flags = info->getFlags(); + + QList icons = getIcons(index); int x = 4; painter->save(); painter->translate(option.rect.topLeft()); - for (auto iter = flags.begin(); iter != flags.end(); ++iter) { - QIcon temp = getFlagIcon(*iter); - painter->drawPixmap(x, 2, 16, 16, temp.pixmap(QSize(16, 16))); + for (auto iter = icons.begin(); iter != icons.end(); ++iter) { + painter->drawPixmap(x, 2, 16, 16, iter->pixmap(QSize(16, 16))); x += 20; } @@ -70,6 +49,7 @@ void IconDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, QSize IconDelegate::sizeHint(const QStyleOptionViewItem&, const QModelIndex &modelIndex) const { + int count = getNumIcons(modelIndex); unsigned int index = modelIndex.data(Qt::UserRole + 1).toInt(); if (index < ModInfo::getNumMods()) { ModInfo::Ptr info = ModInfo::getByIndex(index); diff --git a/src/icondelegate.h b/src/icondelegate.h index dd9f9dfc..cf292443 100644 --- a/src/icondelegate.h +++ b/src/icondelegate.h @@ -34,13 +34,16 @@ public: virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; + signals: public slots: private: - QIcon getFlagIcon(ModInfo::EFlag flag) const; + virtual QList getIcons(const QModelIndex &index) const = 0; + virtual size_t getNumIcons(const QModelIndex &index) const = 0; + private: diff --git a/src/lockeddialog.cpp b/src/lockeddialog.cpp index 4c19c615..065d9270 100644 --- a/src/lockeddialog.cpp +++ b/src/lockeddialog.cpp @@ -17,51 +17,59 @@ You should have received a copy of the GNU General Public License along with Mod Organizer. If not, see . */ -#include "lockeddialog.h" -#include "ui_lockeddialog.h" -#include - - -LockedDialog::LockedDialog(QWidget *parent) : - QDialog(parent), - ui(new Ui::LockedDialog), m_UnlockClicked(false) -{ - ui->setupUi(this); - - this->setWindowFlags(this->windowFlags() | Qt::ToolTip | Qt::FramelessWindowHint); - - if (parent != NULL) { - QPoint position = parent->mapToGlobal(QPoint(parent->width() / 2, parent->height() / 2)); - position.rx() -= this->width() / 2; - position.ry() -= this->height() / 2; - move(position); - } -} - -LockedDialog::~LockedDialog() -{ - delete ui; -} - - -void LockedDialog::setProcessName(const QString &name) -{ - ui->processLabel->setText(name); -} - - -void LockedDialog::resizeEvent(QResizeEvent *event) -{ - QWidget *par = parentWidget(); - if (par != NULL) { - QPoint position = par->mapToGlobal(QPoint(par->width() / 2, par->height() / 2)); - position.rx() -= event->size().width() / 2; - position.ry() -= event->size().height() / 2; - move(position); - } -} - -void LockedDialog::on_unlockButton_clicked() -{ - m_UnlockClicked = true; -} +#include "lockeddialog.h" +#include "ui_lockeddialog.h" +#include + + +LockedDialog::LockedDialog(QWidget *parent, const QString &text, bool unlockButton) + : QDialog(parent) + , ui(new Ui::LockedDialog) + , m_UnlockClicked(false) +{ + ui->setupUi(this); + + this->setWindowFlags(this->windowFlags() | Qt::ToolTip | Qt::FramelessWindowHint); + + if (parent != NULL) { + QPoint position = parent->mapToGlobal(QPoint(parent->width() / 2, parent->height() / 2)); + position.rx() -= this->width() / 2; + position.ry() -= this->height() / 2; + move(position); + } + + if (text.length() > 0) { + ui->label->setText(text); + } + if (!unlockButton) { + ui->unlockButton->hide(); + } +} + +LockedDialog::~LockedDialog() +{ + delete ui; +} + + +void LockedDialog::setProcessName(const QString &name) +{ + ui->processLabel->setText(name); +} + + +void LockedDialog::resizeEvent(QResizeEvent *event) +{ + QWidget *par = parentWidget(); + if (par != NULL) { + QPoint position = par->mapToGlobal(QPoint(par->width() / 2, par->height() / 2)); + position.rx() -= event->size().width() / 2; + position.ry() -= event->size().height() / 2; + move(position); + } +} + +void LockedDialog::on_unlockButton_clicked() +{ + m_UnlockClicked = true; +} diff --git a/src/lockeddialog.h b/src/lockeddialog.h index 5be398bc..0f3b29b0 100644 --- a/src/lockeddialog.h +++ b/src/lockeddialog.h @@ -17,51 +17,51 @@ You should have received a copy of the GNU General Public License along with Mod Organizer. If not, see . */ -#ifndef LOCKEDDIALOG_H -#define LOCKEDDIALOG_H - -#include - -namespace Ui { - class LockedDialog; -} - -/** - * a small borderless dialog displayed while the Mod Organizer UI is locked - * The dialog contains only a label and a button to force the UI to be unlocked - * - * The UI gets locked while running external applications since they may modify the - * data on which Mod Organizer works. After the UI is unlocked (manually or after the - * external application closed) MO will refresh all of its data sources - **/ -class LockedDialog : public QDialog -{ - Q_OBJECT - -public: - explicit LockedDialog(QWidget *parent = 0); - ~LockedDialog(); - - /** - * @brief see if the user clicked the unlock-button - * - * @return true if the user clicked the unlock button - **/ - bool unlockClicked() const { return m_UnlockClicked; } - - void setProcessName(const QString &name); - -protected: - - virtual void resizeEvent(QResizeEvent *event); - -private slots: - - void on_unlockButton_clicked(); - -private: - Ui::LockedDialog *ui; - bool m_UnlockClicked; -}; - -#endif // LOCKEDDIALOG_H +#ifndef LOCKEDDIALOG_H +#define LOCKEDDIALOG_H + +#include + +namespace Ui { + class LockedDialog; +} + +/** + * a small borderless dialog displayed while the Mod Organizer UI is locked + * The dialog contains only a label and a button to force the UI to be unlocked + * + * The UI gets locked while running external applications since they may modify the + * data on which Mod Organizer works. After the UI is unlocked (manually or after the + * external application closed) MO will refresh all of its data sources + **/ +class LockedDialog : public QDialog +{ + Q_OBJECT + +public: + explicit LockedDialog(QWidget *parent = 0, const QString &text = "", bool unlockButton = true); + ~LockedDialog(); + + /** + * @brief see if the user clicked the unlock-button + * + * @return true if the user clicked the unlock button + **/ + bool unlockClicked() const { return m_UnlockClicked; } + + void setProcessName(const QString &name); + +protected: + + virtual void resizeEvent(QResizeEvent *event); + +private slots: + + void on_unlockButton_clicked(); + +private: + Ui::LockedDialog *ui; + bool m_UnlockClicked; +}; + +#endif // LOCKEDDIALOG_H diff --git a/src/logbuffer.cpp b/src/logbuffer.cpp index 4f72e551..689d2b55 100644 --- a/src/logbuffer.cpp +++ b/src/logbuffer.cpp @@ -21,6 +21,7 @@ along with Mod Organizer. If not, see . #include "report.h" #include #include +#include #include QScopedPointer LogBuffer::s_Instance; @@ -124,8 +125,6 @@ char LogBuffer::msgTypeID(QtMsgType type) } } -#include - void LogBuffer::log(QtMsgType type, const char *message) { QMutexLocker guard(&s_Mutex); diff --git a/src/main.cpp b/src/main.cpp index 8b74ba83..05d68ec7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -119,7 +119,8 @@ bool bootstrap() QDir::fromNativeSeparators(ToQString(gameInfo.getDownloadDir())), QDir::fromNativeSeparators(ToQString(gameInfo.getOverwriteDir())), QDir::fromNativeSeparators(ToQString(gameInfo.getLogDir())), - QDir::fromNativeSeparators(ToQString(gameInfo.getTutorialDir())) + QDir::fromNativeSeparators(ToQString(gameInfo.getTutorialDir())), + QDir::fromNativeSeparators(ToQString(gameInfo.getOrganizerDirectory()) + "/boss") }; static const int NUM_DIRECTORIES = sizeof(dirNames) / sizeof(QString); @@ -284,7 +285,7 @@ int main(int argc, char *argv[]) SetUnhandledExceptionFilter(MyUnhandledExceptionFilter); - LogBuffer::init(20, QtDebugMsg, application.applicationDirPath() + "/logs/mo_interface.log"); + LogBuffer::init(200, QtDebugMsg, application.applicationDirPath() + "/logs/mo_interface.log"); qDebug("Working directory: %s", qPrintable(QDir::toNativeSeparators(QDir::currentPath()))); qDebug("MO at: %s", qPrintable(QDir::toNativeSeparators(application.applicationDirPath()))); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 582bc283..0f98989c 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -48,7 +48,8 @@ along with Mod Organizer. If not, see . #include "filedialogmemory.h" #include "questionboxmemory.h" #include "tutorialmanager.h" -#include "icondelegate.h" +#include "modflagicondelegate.h" +#include "pluginflagicondelegate.h" #include "credentialsdialog.h" #include "selectiondialog.h" #include "csvbuilder.h" @@ -104,6 +105,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include #ifdef TEST_MODELS @@ -186,7 +188,7 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget ui->modList->setModel(m_ModListSortProxy); ui->modList->sortByColumn(ModList::COL_PRIORITY, Qt::AscendingOrder); - ui->modList->setItemDelegateForColumn(ModList::COL_FLAGS, new IconDelegate(ui->modList)); + ui->modList->setItemDelegateForColumn(ModList::COL_FLAGS, new ModFlagIconDelegate(ui->modList)); //ui->modList->setAcceptDrops(true); ui->modList->header()->installEventFilter(&m_ModList); if (initSettings.contains("mod_list_state")) { @@ -206,6 +208,7 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget ui->espList->setModel(m_PluginListSortProxy); ui->espList->sortByColumn(PluginList::COL_PRIORITY, Qt::AscendingOrder); + ui->espList->setItemDelegateForColumn(PluginList::COL_FLAGS, new PluginFlagIconDelegate(ui->espList)); if (initSettings.contains("plugin_list_state")) { ui->espList->header()->restoreState(initSettings.value("plugin_list_state").toByteArray()); } @@ -1314,6 +1317,8 @@ HANDLE MainWindow::spawnBinaryDirect(const QFileInfo &binary, const QString &arg QCoreApplication::processEvents(); } + m_CurrentProfile->writeModlistNow(true); + // TODO: should also pass arguments if (m_AboutToRun(binary.absoluteFilePath())) { return startBinary(binary, arguments, profileName, m_Settings.logLevel(), currentDirectory, true); @@ -1778,7 +1783,7 @@ void MainWindow::refreshESPList() m_CurrentProfile->getLoadOrderFileName(), m_CurrentProfile->getLockedOrderFileName()); } catch (const std::exception &e) { - reportError(tr("Failed to refresh list of esps: %s").arg(e.what())); + reportError(tr("Failed to refresh list of esps: %1").arg(e.what())); } } @@ -5123,3 +5128,18 @@ void MainWindow::on_showHiddenBox_toggled(bool checked) m_DownloadManager.setShowHidden(checked); } +void MainWindow::on_bossButton_clicked() +{ + try { + LockedDialog dialog(this, tr("BOSS working"), false); + dialog.show(); + qApp->processEvents(); + m_PluginList.bossSort(); + + savePluginList(); + dialog.hide(); + } catch (const std::exception &e) { + reportError(tr("failed to run boss: %1").arg(e.what())); + ui->bossButton->setEnabled(false); + } +} diff --git a/src/mainwindow.h b/src/mainwindow.h index c01ce767..b247b924 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -564,6 +564,7 @@ private slots: // ui slots void on_linkButton_pressed(); void on_showHiddenBox_toggled(bool checked); void on_bsaList_itemChanged(QTreeWidgetItem *item, int column); + void on_bossButton_clicked(); }; #endif // MAINWINDOW_H diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 5c89a7e0..f3bb425c 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -31,7 +31,16 @@ 4 - + + 6 + + + 6 + + + 6 + + 6 @@ -705,14 +714,25 @@ p, li { white-space: pre-wrap; }
    - - - - - - Namefilter - - + + + + + + + + Namefilter + + + + + + + Sort + + + + @@ -721,7 +741,16 @@ p, li { white-space: pre-wrap; } Archives - + + 6 + + + 6 + + + 6 + + 6 @@ -801,7 +830,16 @@ BSAs checked here are loaded in such a way that your installation order is obeye Data - + + 6 + + + 6 + + + 6 + + 6 @@ -871,7 +909,16 @@ BSAs checked here are loaded in such a way that your installation order is obeye Saves - + + 6 + + + 6 + + + 6 + + 6 @@ -900,7 +947,16 @@ p, li { white-space: pre-wrap; } Downloads - + + 2 + + + 2 + + + 2 + + 2 diff --git a/src/modflagicondelegate.cpp b/src/modflagicondelegate.cpp new file mode 100644 index 00000000..db69eb52 --- /dev/null +++ b/src/modflagicondelegate.cpp @@ -0,0 +1,50 @@ +#include "modflagicondelegate.h" +#include + + +ModFlagIconDelegate::ModFlagIconDelegate(QObject *parent) + : IconDelegate(parent) +{ +} + +QList ModFlagIconDelegate::getIcons(const QModelIndex &index) const +{ + QList result; + QVariant modid = index.data(Qt::UserRole + 1); + if (modid.isValid()) { + ModInfo::Ptr info = ModInfo::getByIndex(modid.toInt()); + std::vector flags = info->getFlags(); + + for (auto iter = flags.begin(); iter != flags.end(); ++iter) { + result.append(getFlagIcon(*iter)); + } + } + return result; +} + +QIcon ModFlagIconDelegate::getFlagIcon(ModInfo::EFlag flag) const +{ + switch (flag) { + case ModInfo::FLAG_BACKUP: return QIcon(":/MO/gui/emblem_backup"); + case ModInfo::FLAG_INVALID: return QIcon(":/MO/gui/emblem_problem"); + case ModInfo::FLAG_NOTENDORSED: return QIcon(":/MO/gui/emblem_notendorsed"); + case ModInfo::FLAG_NOTES: return QIcon(":/MO/gui/emblem_notes"); + case ModInfo::FLAG_CONFLICT_OVERWRITE: return QIcon(":/MO/gui/emblem_conflict_overwrite"); + case ModInfo::FLAG_CONFLICT_OVERWRITTEN: return QIcon(":/MO/gui/emblem_conflict_overwritten"); + case ModInfo::FLAG_CONFLICT_MIXED: return QIcon(":/MO/gui/emblem_conflict_mixed"); + case ModInfo::FLAG_CONFLICT_REDUNDANT: return QIcon(":MO/gui/emblem_conflict_redundant"); + default: return QIcon(); + } +} + +size_t ModFlagIconDelegate::getNumIcons(const QModelIndex &index) const +{ + unsigned int modIdx = index.data(Qt::UserRole + 1).toInt(); + if (modIdx < ModInfo::getNumMods()) { + ModInfo::Ptr info = ModInfo::getByIndex(modIdx); + return info->getFlags().size(); + } else { + return 0; + } +} + diff --git a/src/modflagicondelegate.h b/src/modflagicondelegate.h new file mode 100644 index 00000000..800e2741 --- /dev/null +++ b/src/modflagicondelegate.h @@ -0,0 +1,18 @@ +#ifndef MODFLAGICONDELEGATE_H +#define MODFLAGICONDELEGATE_H + +#include "icondelegate.h" + +class ModFlagIconDelegate : public IconDelegate +{ +public: + explicit ModFlagIconDelegate(QObject *parent = 0); +private: + virtual QList getIcons(const QModelIndex &index) const; + virtual size_t getNumIcons(const QModelIndex &index) const; + + QIcon getFlagIcon(ModInfo::EFlag flag) const; + +}; + +#endif // MODFLAGICONDELEGATE_H diff --git a/src/organizer.pro b/src/organizer.pro index fd72dea3..9a9d0da3 100644 --- a/src/organizer.pro +++ b/src/organizer.pro @@ -81,7 +81,10 @@ SOURCES += \ previewgenerator.cpp \ previewdialog.cpp \ aboutdialog.cpp \ - json.cpp + json.cpp \ + safewritefile.cpp \ + modflagicondelegate.cpp \ + pluginflagicondelegate.cpp HEADERS += \ @@ -152,7 +155,11 @@ HEADERS += \ previewgenerator.h \ previewdialog.h \ aboutdialog.h \ - json.h + json.h \ + safewritefile.h\ + pdll.h \ + modflagicondelegate.h \ + pluginflagicondelegate.h FORMS += \ transfersavesdialog.ui \ @@ -185,24 +192,28 @@ FORMS += \ previewdialog.ui \ aboutdialog.ui -INCLUDEPATH += ../shared ../archive ../uibase ../bsatk ../esptk "$(BOOSTPATH)" +INCLUDEPATH += ../shared ../archive ../uibase ../bsatk ../esptk ../boss_modified/boss-api "$(BOOSTPATH)" LIBS += -L"$(BOOSTPATH)/stage/lib" CONFIG(debug, debug|release) { OUTDIR = $$OUT_PWD/debug DSTDIR = $$PWD/../../outputd - LIBS += -L$$OUT_PWD/../shared/debug -L$$OUT_PWD/../bsatk/debug - LIBS += -L$$OUT_PWD/../uibase/debug - LIBS += -lDbgHelp + LIBS += -L$$OUT_PWD/../shared/debug + LIBS += -L$$OUT_PWD/../bsatk/debug + LIBS += -L$$OUT_PWD/../uibase/debug + LIBS += -L$$OUT_PWD/../boss_modified/debug + LIBS += -lDbgHelp } else { OUTDIR = $$OUT_PWD/release DSTDIR = $$PWD/../../output - LIBS += -L$$OUT_PWD/../shared/release -L$$OUT_PWD/../bsatk/release + LIBS += -L$$OUT_PWD/../shared/release + LIBS += -L$$OUT_PWD/../bsatk/release LIBS += -L$$OUT_PWD/../uibase/release - QMAKE_CXXFLAGS += /Zi + LIBS += -L$$OUT_PWD/../boss_modified/release + QMAKE_CXXFLAGS += /Zi /GL # QMAKE_CXXFLAGS -= -O2 - QMAKE_LFLAGS += /DEBUG + QMAKE_LFLAGS += /DEBUG /LTCG /OPT:REF /OPT:ICF } #QMAKE_CXXFLAGS_WARN_ON -= -W3 @@ -299,9 +310,6 @@ OTHER_FILES += \ tutorials/tutorial_window_installer.js \ tutorials/tutorials_installdialog.qml -INCLUDEPATH += "$(ZLIBPATH)" "$(ZLIBPATH)/build" "$(BOOSTPATH)" -LIBS += -L"$(ZLIBPATH)/build" -lzlibstatic - # leak detection with vld #INCLUDEPATH += "E:/Visual Leak Detector/include" diff --git a/src/pdll.h b/src/pdll.h new file mode 100644 index 00000000..6e1d06f3 --- /dev/null +++ b/src/pdll.h @@ -0,0 +1,402 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Class: PDll // +// Authors: MicHael Galkovsky // +// Date: April 14, 1998 // +// Company: Pervasive Software // +// Purpose: Base class to wrap dynamic use of dll // +////////////////////////////////////////////////////////////////////////////////////////////// + +#if !defined (_PDLL_H_) +#define _PDLL_H_ + +#include +#include + +#include +#include +#include + + +#define FUNC_LOADED 3456 + +//function declarations according to the number of parameters +//define the type +//declare a variable of that type +//declare a member function by the same name as the dll function +//check for dll handle +//if this is the first call to the function then try to load it +//if not then if the function was loaded successfully make a call to it +//otherwise return a NULL cast to the return parameter. + +#define DECLARE_FUNCTION0(CallType, retVal, FuncName) \ + typedef retVal (CallType* TYPE_##FuncName)(); \ + TYPE_##FuncName m_##FuncName; \ + short m_is##FuncName; \ + retVal FuncName() \ + { \ + if (m_dllHandle) \ + { \ + if (FUNC_LOADED != m_is##FuncName) \ + {\ + m_##FuncName = NULL; \ + m_##FuncName = (TYPE_##FuncName)GetProcAddress(m_dllHandle, #FuncName); \ + m_is##FuncName = FUNC_LOADED;\ + }\ + if (NULL != m_##FuncName) \ + return m_##FuncName(); \ + else \ + return (retVal)NULL; \ + } \ + else \ + return (retVal)NULL; \ + } + +#define DECLARE_FUNCTION1(CallType,retVal, FuncName, Param1) \ + typedef retVal (CallType* TYPE_##FuncName)(Param1); \ + TYPE_##FuncName m_##FuncName; \ + short m_is##FuncName;\ + retVal FuncName(Param1 p1) \ + { \ + if (m_dllHandle) \ + { \ + if (FUNC_LOADED != m_is##FuncName) \ + {\ + m_##FuncName = NULL; \ + m_##FuncName = (TYPE_##FuncName)GetProcAddress(m_dllHandle, #FuncName); \ + m_is##FuncName = FUNC_LOADED;\ + }\ + if (NULL != m_##FuncName) \ + return m_##FuncName(p1); \ + else \ + return (retVal)NULL; \ + } \ + else \ + return (retVal)NULL; \ + } + +#define DECLARE_FUNCTION2(CallType,retVal, FuncName, Param1, Param2) \ + typedef retVal (CallType* TYPE_##FuncName)(Param1, Param2); \ + TYPE_##FuncName m_##FuncName; \ + short m_is##FuncName;\ + retVal FuncName (Param1 p1, Param2 p2) \ + {\ + if (m_dllHandle)\ + {\ + if (FUNC_LOADED != m_is##FuncName) \ + {\ + m_##FuncName = NULL; \ + m_##FuncName = (TYPE_##FuncName)GetProcAddress(m_dllHandle, #FuncName); \ + m_is##FuncName = FUNC_LOADED;\ + }\ + if (NULL != m_##FuncName) \ + return m_##FuncName(p1, p2); \ + else \ + return (retVal)NULL; \ + } \ + else\ + return (retVal)NULL; \ + } + +#define DECLARE_FUNCTION3(CallType,retVal, FuncName, Param1, Param2, Param3) \ + typedef retVal (CallType* TYPE_##FuncName)(Param1, Param2, Param3); \ + TYPE_##FuncName m_##FuncName; \ + short m_is##FuncName;\ + retVal FuncName (Param1 p1, Param2 p2, Param3 p3) \ + {\ + if (m_dllHandle)\ + {\ + if (FUNC_LOADED != m_is##FuncName) \ + {\ + m_##FuncName = NULL; \ + m_##FuncName = (TYPE_##FuncName)GetProcAddress(m_dllHandle, #FuncName); \ + m_is##FuncName = FUNC_LOADED; \ + }\ + if (NULL != m_##FuncName) \ + return m_##FuncName(p1, p2, p3);\ + else \ + return (retVal)NULL; \ + } \ + else\ + return (retVal)NULL; \ + } + +#define DECLARE_FUNCTION4(CallType,retVal, FuncName, Param1, Param2, Param3, Param4) \ + typedef retVal (CallType* TYPE_##FuncName)(Param1, Param2, Param3, Param4); \ + TYPE_##FuncName m_##FuncName; \ + short m_is##FuncName;\ + retVal FuncName (Param1 p1, Param2 p2, Param3 p3, Param4 p4) \ + {\ + if (m_dllHandle)\ + {\ + if (FUNC_LOADED != m_is##FuncName) \ + {\ + m_##FuncName = NULL; \ + m_##FuncName = (TYPE_##FuncName)GetProcAddress(m_dllHandle, #FuncName); \ + m_is##FuncName = FUNC_LOADED;\ + }\ + if (NULL != m_##FuncName) \ + return m_##FuncName(p1, p2, p3, p4);\ + else \ + return (retVal)NULL; \ + } \ + else\ + return (retVal)NULL; \ + } + +#define DECLARE_FUNCTION5(CallType,retVal, FuncName, Param1, Param2, Param3, Param4, Param5) \ + typedef retVal (CallType* TYPE_##FuncName)(Param1, Param2, Param3, Param4, Param5); \ + TYPE_##FuncName m_##FuncName; \ + short m_is##FuncName; \ + retVal FuncName (Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5) \ + {\ + if (m_dllHandle)\ + {\ + if (FUNC_LOADED != m_is##FuncName) \ + {\ + m_##FuncName = NULL; \ + m_##FuncName = (TYPE_##FuncName)GetProcAddress(m_dllHandle, #FuncName); \ + m_is##FuncName = FUNC_LOADED;\ + }\ + if (NULL != m_##FuncName) \ + return m_##FuncName(p1, p2, p3, p4, p5);\ + else \ + return (retVal)NULL; \ + } \ + else\ + return (retVal)NULL; \ + } + +#define DECLARE_FUNCTION6(CallType,retVal, FuncName, Param1, Param2, Param3, Param4, Param5, Param6) \ + typedef retVal (CallType* TYPE_##FuncName)(Param1, Param2, Param3, Param4, Param5, Param6); \ + TYPE_##FuncName m_##FuncName; \ + short m_is##FuncName;\ + retVal FuncName (Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6) \ + {\ + if (m_dllHandle)\ + {\ + if (FUNC_LOADED != m_is##FuncName) \ + {\ + m_##FuncName = NULL; \ + m_##FuncName = (TYPE_##FuncName)GetProcAddress(m_dllHandle, #FuncName); \ + m_is##FuncName = FUNC_LOADED;\ + }\ + if (NULL != m_##FuncName) \ + return m_##FuncName(p1, p2, p3, p4, p5, p6);\ + else \ + return (retVal)NULL; \ + } \ + else\ + return (retVal)NULL; \ + } + +#define DECLARE_FUNCTION7(CallType,retVal, FuncName, Param1, Param2, Param3, Param4, Param5, Param6, Param7) \ + typedef retVal (CallType* TYPE_##FuncName)(Param1, Param2, Param3, Param4, Param5, Param6, Param7); \ + TYPE_##FuncName m_##FuncName; \ + short m_is##FuncName;\ + retVal FuncName (Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7) \ + {\ + if (m_dllHandle)\ + {\ + if (FUNC_LOADED != m_is##FuncName) \ + {\ + m_##FuncName = NULL; \ + m_##FuncName = (TYPE_##FuncName)GetProcAddress(m_dllHandle, #FuncName); \ + m_is##FuncName = FUNC_LOADED;\ + }\ + if (NULL != m_##FuncName) \ + return m_##FuncName(p1, p2, p3, p4, p5, p6, p7);\ + else \ + return (retVal)NULL; \ + } \ + else\ + return (retVal)NULL; \ + } + +#define DECLARE_FUNCTION8(CallType,retVal, FuncName, Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8) \ + typedef retVal (CallType* TYPE_##FuncName)(Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8); \ + TYPE_##FuncName m_##FuncName; \ + short m_is##FuncName;\ + retVal FuncName (Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8) \ + {\ + if (m_dllHandle)\ + {\ + if (FUNC_LOADED != m_is##FuncName) \ + {\ + m_##FuncName = NULL; \ + m_##FuncName = (TYPE_##FuncName)GetProcAddress(m_dllHandle, #FuncName); \ + m_is##FuncName = FUNC_LOADED;\ + }\ + if (NULL != m_##FuncName) \ + return m_##FuncName(p1, p2, p3, p4, p5, p6, p7, p8);\ + else \ + return (retVal)NULL; \ + }\ + else\ + return (retVal)NULL; \ + } + +#define DECLARE_FUNCTION9(CallType,retVal, FuncName, Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, Param9) \ + typedef retVal (CallType* TYPE_##FuncName)(Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, Param9); \ + TYPE_##FuncName m_##FuncName; \ + short m_is##FuncName; \ + retVal FuncName (Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8, Param9 p9) \ + {\ + if (m_dllHandle)\ + {\ + if (FUNC_NAME != m_is##FuncName) \ + {\ + m_##FuncName = NULL; \ + m_##FuncName = (TYPE_##FuncName)GetProcAddress(m_dllHandle, #FuncName); \ + m_is##FuncName = FUNC_LOADED;\ + }\ + if (NULL != m_##FuncName) \ + return m_##FuncName(p1, p2, p3, p4, p5, p6, p7, p8, p9);\ + else \ + return (retVal)NULL; \ + }\ + else\ + return (retVal)NULL; \ + } + +#define DECLARE_FUNCTION10(CallType,retVal, FuncName, Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, Param9, Param10) \ + typedef retVal (CallType* TYPE_##FuncName)FuncName(Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, Param9, Param10); \ + TYPE_##FuncName m_##FuncName; \ + short m_is##FuncName;\ + retVal FuncName (Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8, Param9 p9, Param10 p10) \ + {\ + if (m_dllHandle)\ + {\ + if (FUNC_LOADED != m_is##FuncName) \ + {\ + m_##FuncName = NULL; \ + m_##FuncName = (TYPE_##FuncName)GetProcAddress(m_dllHandle, #FuncName); \ + m_is##FuncName = FUNC_LOADED;\ + }\ + if (NULL != m_##FuncName) \ + return m_##FuncName(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10);\ + else \ + return (retVal)NULL; \ + }\ + else \ + return (retVal)NULL;\ + } + +//declare constructors and LoadFunctions +#define DECLARE_CLASS(ClassName) \ + public: \ + ClassName (LPCTSTR name){LoadDll(name);} \ + ClassName () {PDLL();} + +class PDLL +{ +protected: + HINSTANCE m_dllHandle; +private: + LPTSTR m_dllName; + int m_refCount; + +public: + + PDLL() + { + m_dllHandle = NULL; + m_dllName = NULL; + m_refCount = 0; + } + + //A NULL here means the name has already been set + void LoadDll(LPCTSTR name, bool showMsg = true) + { + if (name) + SetDllName(name); + + //try to load + m_dllHandle = LoadLibrary(m_dllName); + + if (m_dllHandle == NULL && showMsg) + { + std::ostringstream message; + message << "failed to load dll: " << ::GetLastError(); + throw std::runtime_error(message.str().c_str()); + } + } + + bool SetDllName(LPCTSTR newName) + { + bool retVal = false; + + //we allow name resets only if the current DLL handle is invalid + //once they've hooked into a DLL, the name cannot be changed + if (!m_dllHandle) + { + if (m_dllName) + { + delete []m_dllName; + m_dllName = NULL; + } + + //They may be setting this null (e.g., uninitialize) + if (newName) + { + m_dllName = new TCHAR[_tcslen(newName) + 1]; + _tcscpy(m_dllName, newName); + } + retVal = true; + } + return retVal; + } + + virtual bool Initialize(short showMsg = 1) + { + + bool retVal = false; + + //Add one to our internal reference counter + m_refCount++; + + if (m_refCount == 1 && m_dllName) //if this is first time, load the DLL + { + //we are assuming the name is already set + LoadDll(NULL, showMsg); + retVal = (m_dllHandle != NULL); + } + return retVal; + } + + virtual void Uninitialize(void) + { + //If we're already completely unintialized, early exit + if (!m_refCount) + return; + //if this is the last time this instance has been unitialized, + //then do a full uninitialization + m_refCount--; + + if (m_refCount < 1) + { + if (m_dllHandle) + { + FreeLibrary(m_dllHandle); + m_dllHandle = NULL; + } + + SetDllName(NULL); //clear out the name & free memory + } + } + + virtual ~PDLL() + { + //force this to be a true uninitialize + m_refCount = 1; + Uninitialize(); + + //free name + if (m_dllName) + { + delete [] m_dllName; + m_dllName = NULL; + } + } + +}; +#endif diff --git a/src/pluginflagicondelegate.cpp b/src/pluginflagicondelegate.cpp new file mode 100644 index 00000000..6c0bb29e --- /dev/null +++ b/src/pluginflagicondelegate.cpp @@ -0,0 +1,24 @@ +#include "pluginflagicondelegate.h" +#include "pluginlist.h" +#include + + +PluginFlagIconDelegate::PluginFlagIconDelegate(QObject *parent) + : IconDelegate(parent) +{ +} + +QList PluginFlagIconDelegate::getIcons(const QModelIndex &index) const +{ + QList result; + foreach (const QVariant &var, index.data(Qt::UserRole + 1).toList()) { + result.append(var.value()); + } + return result; +} + +size_t PluginFlagIconDelegate::getNumIcons(const QModelIndex &index) const +{ + return index.data(Qt::UserRole + 1).toList().count(); +} + diff --git a/src/pluginflagicondelegate.h b/src/pluginflagicondelegate.h new file mode 100644 index 00000000..554e968b --- /dev/null +++ b/src/pluginflagicondelegate.h @@ -0,0 +1,17 @@ +#ifndef PLUGINFLAGICONDELEGATE_H +#define PLUGINFLAGICONDELEGATE_H + +#include "icondelegate.h" + +class PluginFlagIconDelegate : public IconDelegate +{ +public: + PluginFlagIconDelegate(QObject *parent = NULL); + + // IconDelegate interface +private: + virtual QList getIcons(const QModelIndex &index) const; + virtual size_t getNumIcons(const QModelIndex &index) const; +}; + +#endif // PLUGINFLAGICONDELEGATE_H diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index 85137390..c2e14182 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -20,8 +20,10 @@ along with Mod Organizer. If not, see . #include "pluginlist.h" #include "report.h" #include "inject.h" -#include #include "settings.h" +#include "safewritefile.h" +#include "scopeguard.h" +#include #include #include #include @@ -39,7 +41,6 @@ along with Mod Organizer. If not, see . #include #include -#include #include #include #include @@ -74,8 +75,10 @@ bool ByDate(const PluginList::ESPInfo& LHS, const PluginList::ESPInfo& RHS) { } PluginList::PluginList(QObject *parent) - : QAbstractTableModel(parent), - m_FontMetrics(QFont()), m_SaveTimer(this) + : QAbstractTableModel(parent) + , m_FontMetrics(QFont()) + , m_SaveTimer(this) + , m_BOSS(NULL) { m_SaveTimer.setSingleShot(true); connect(&m_SaveTimer, SIGNAL(timeout()), this, SIGNAL(saveTimer())); @@ -92,6 +95,12 @@ PluginList::PluginList(QObject *parent) PluginList::~PluginList() { + if (m_BOSS != NULL) { + m_BOSS->DestroyBossDb(m_BOSSDB); + m_BOSS->CleanUpAPI(); + delete m_BOSS; + m_BOSS = NULL; + } } @@ -101,6 +110,7 @@ QString PluginList::getColumnName(int column) case COL_NAME: return tr("Name"); case COL_PRIORITY: return tr("Priority"); case COL_MODINDEX: return tr("Mod Index"); + case COL_FLAGS: return tr("Flags"); default: return tr("unknown"); } } @@ -205,7 +215,7 @@ void PluginList::refresh(const QString &profileName, const DirectoryEntry &baseD emit layoutChanged(); refreshLoadOrder(); - emit dataChanged(this->index(0, 0), this->index(m_ESPs.size(), 1)); + emit dataChanged(this->index(0, 0), this->index(m_ESPs.size(), columnCount())); m_Refreshed(); } @@ -375,18 +385,16 @@ void PluginList::readLockedOrderFrom(const QString &fileName) } + void PluginList::writePlugins(const QString &fileName, bool writeUnchecked) const { - QFile file(fileName); - if (!file.open(QIODevice::WriteOnly)) { - throw MyException(tr("failed to open output file: %1").arg(fileName)); - } + SafeWriteFile file(fileName); QTextCodec *textCodec = writeUnchecked ? m_Utf8Codec : m_LocalCodec; - file.resize(0); + file->resize(0); - file.write(textCodec->fromUnicode("# This file was automatically generated by Mod Organizer.\r\n")); + file->write(textCodec->fromUnicode("# This file was automatically generated by Mod Organizer.\r\n")); bool invalidFileNames = false; int writtenCount = 0; @@ -398,36 +406,34 @@ void PluginList::writePlugins(const QString &fileName, bool writeUnchecked) cons invalidFileNames = true; qCritical("invalid plugin name %s", m_ESPs[priority].m_Name.toUtf8().constData()); } else { - file.write(textCodec->fromUnicode(m_ESPs[priority].m_Name)); + file->write(textCodec->fromUnicode(m_ESPs[priority].m_Name)); } - file.write("\r\n"); + file->write("\r\n"); ++writtenCount; } } - file.close(); if (invalidFileNames) { reportError(tr("Some of your plugins have invalid names! These plugins can not be loaded by the game. " "Please see mo_interface.log for a list of affected plugins and rename them.")); } + file.commit(); + qDebug("%s saved", QDir::toNativeSeparators(fileName).toUtf8().constData()); } void PluginList::writeLockedOrder(const QString &fileName) const { - QFile file(fileName); - if (!file.open(QIODevice::WriteOnly)) { - throw MyException(tr("failed to open output file: %1").arg(fileName)); - } + SafeWriteFile file(fileName); - file.resize(0); - file.write(QString("# This file was automatically generated by Mod Organizer.\r\n").toUtf8()); + file->resize(0); + file->write(QString("# This file was automatically generated by Mod Organizer.\r\n").toUtf8()); for (auto iter = m_LockedOrder.begin(); iter != m_LockedOrder.end(); ++iter) { - file.write(QString("%1|%2\r\n").arg(iter->first).arg(iter->second).toUtf8()); + file->write(QString("%1|%2\r\n").arg(iter->first).arg(iter->second).toUtf8()); } - file.close(); + file.commit(); } @@ -590,6 +596,175 @@ void PluginList::refreshLoadOrder() emit layoutChanged(); } + +class boss_exception : public std::runtime_error { +public: + boss_exception(const std::string &message) : std::runtime_error(message) {} +}; + +#define THROW_BOSS_ERROR(obj) \ + uint8_t *message; \ + obj->GetLastErrorDetails(&message); \ + throw boss_exception(std::string(reinterpret_cast(message))); + +#define U8(text) reinterpret_cast(text) + + +void outputBossLog(const QString &filename) +{ + QFile file(filename); + if (file.open(QIODevice::ReadOnly)) { + while (!file.atEnd()) { + QByteArray line = file.readLine().trimmed(); + if (line.length() < 1) + continue; + + int endFirstWord = line.indexOf(':'); + if (endFirstWord < 0) { + endFirstWord = 0; + } + QString firstWord = line.mid(0, endFirstWord); + if (firstWord == "DEBUG") { + qDebug("(boss) %s", line.mid(endFirstWord + 2).constData()); + } else { + qWarning("(boss) %s", line.mid(endFirstWord + 2).constData()); + } + } + } + file.resize(0); +} + + +void PluginList::initBoss() +{ + m_BOSS = new BossDLL(TEXT("dlls\\boss.dll")); + + if (!m_BOSS->IsCompatibleVersion(2,1,1)) { + throw MyException(tr("BOSS dll incompatible")); + } + uint8_t *versionString; + if (m_BOSS->GetVersionString(&versionString) != BossDLL::RESULT_OK) { + THROW_BOSS_ERROR(m_BOSS) + } + qDebug("using boss version %s", versionString); + + m_TempFile.open(); m_TempFile.close(); // yeah, stupid, but open is required to generate the name + m_BOSS->SetLoggerOutput(m_TempFile.fileName().toLocal8Bit().constData(), 4); + + if (m_BOSS->CreateBossDb(&m_BOSSDB, BossDLL::SKYRIM, NULL) != BossDLL::RESULT_OK) { + uint8_t *message; + m_BOSS->GetLastErrorDetails(&message); + std::string messageCopy(reinterpret_cast(message)); + delete m_BOSS; + m_BOSS = NULL; + throw boss_exception(messageCopy); + } + qApp->processEvents(); + + QString masterlistName = QDir::toNativeSeparators(qApp->applicationDirPath() + "/boss/masterlist.txt"); + + uint32_t res = m_BOSS->UpdateMasterlist(m_BOSSDB, U8(masterlistName.toUtf8().constData())); + qApp->processEvents(); + if (res == BossDLL::RESULT_OK) { + qDebug("boss masterlist updated"); + } else if (res == BossDLL::RESULT_NO_UPDATE_NECESSARY) { + qDebug("boss masterlist already up-to-date"); + } else { + THROW_BOSS_ERROR(m_BOSS) + } + if (m_BOSS->Load(m_BOSSDB, + U8(masterlistName.toUtf8().constData()), + U8(QDir::toNativeSeparators(qApp->applicationDirPath() + "/boss/userlist.txt").toUtf8().constData())) != BossDLL::RESULT_OK) { + THROW_BOSS_ERROR(m_BOSS) + } +} + +void PluginList::convertPluginListForBoss(boost::ptr_vector &inputPlugins, std::vector &activePlugins) +{ + foreach (int idx, m_ESPsByPriority) { + QString fileName = m_ESPs[idx].m_Name; + QByteArray name = fileName.toUtf8(); + + uint8_t *nameU8 = new uint8_t[name.length() + 1]; + memcpy(nameU8, name.constData(), name.length() + 1); + if (m_ESPs[idx].m_Enabled) { + activePlugins.push_back(nameU8); + } + inputPlugins.push_back(nameU8); + } + if (m_BOSS->SetActivePluginsDumb(m_BOSSDB, &activePlugins[0], activePlugins.size()) != BossDLL::RESULT_OK) { + THROW_BOSS_ERROR(m_BOSS) + } +} + +void PluginList::applyBOSSSorting(uint8_t **pluginList, size_t size, int &priority, bool recognized, const char *extension) +{ + for (size_t i= 0; i < size; ++i) { + QString name = QString::fromUtf8(reinterpret_cast(pluginList[i])).toLower(); + if (name.endsWith(extension)) { + auto iter = m_ESPsByName.find(name); + if (iter == m_ESPsByName.end()) { + throw MyException("boss returned invalid data"); + } + BossMessage *message; + size_t numMessages = 0; + m_BOSS->GetPluginMessages(m_BOSSDB, pluginList[i], &message, &numMessages); + m_ESPs[iter->second].m_BOSSMessages.clear(); + for (size_t im = 0; im < numMessages; ++im) { + m_ESPs[iter->second].m_BOSSMessages.append(QString::fromUtf8(reinterpret_cast(message[im].message))); + } + m_ESPs[iter->second].m_Priority = priority++; + m_ESPs[iter->second].m_BOSSUnrecognized = !recognized; + } + } +} + +void PluginList::bossSort() +{ + if (m_BOSS == NULL) { + // first run, check boss compatibility and update + initBoss(); + } + + // create a boss-compatible representation of our current mod list. + boost::ptr_vector inputPlugins; + std::vector activePlugins; + convertPluginListForBoss(inputPlugins, activePlugins); + + // sort mods in-memory + uint8_t **sortedPlugins; + uint8_t **unrecognizedPlugins; + size_t sizeSorted, sizeUnrecognized; + + if (m_BOSS->SortCustomMods(m_BOSSDB, + inputPlugins.c_array(), inputPlugins.size(), + &sortedPlugins, &sizeSorted, + &unrecognizedPlugins, &sizeUnrecognized) != BossDLL::RESULT_OK) { + THROW_BOSS_ERROR(m_BOSS) + } + + // output the log from boss to our own log to make it visible + outputBossLog(m_TempFile.fileName()); + + qDebug("%d sorted, %d unrecognized", sizeSorted, sizeUnrecognized); + + emit layoutAboutToBeChanged(); + + int priority = 0; + applyBOSSSorting(sortedPlugins, sizeSorted, priority, true, "esm"); + applyBOSSSorting(unrecognizedPlugins, sizeUnrecognized, priority, false, "esm"); + applyBOSSSorting(sortedPlugins, sizeSorted, priority, true, "esp"); + applyBOSSSorting(unrecognizedPlugins, sizeUnrecognized, priority, false, "esp"); + + // inform view of the changed data + updateIndices(); + emit layoutChanged(); + syncLoadOrder(); + + emit dataChanged(this->index(0, 0), this->index(m_ESPs.size(), columnCount())); + m_Refreshed(); +} + IPluginList::PluginState PluginList::state(const QString &name) const { auto iter = m_ESPsByName.find(name.toLower()); @@ -670,7 +845,7 @@ int PluginList::rowCount(const QModelIndex &parent) const int PluginList::columnCount(const QModelIndex &) const { - return 3; + return COL_LASTCOLUMN + 1; } @@ -724,18 +899,6 @@ QVariant PluginList::data(const QModelIndex &modelIndex, int role) const return QVariant(); } break; } - } else if ((role == Qt::DecorationRole) && (modelIndex.column() == 0)) { - if (m_ESPs[index].m_MasterUnset.size() > 0) { - return QIcon(":/MO/gui/warning"); - } else if (m_LockedOrder.find(m_ESPs[index].m_Name.toLower()) != m_LockedOrder.end()) { - return QIcon(":/MO/gui/locked"); - } else if (m_ESPs[index].m_HasIni) { - return QIcon(":/MO/gui/attachment"); - } else if (m_ESPs[index].m_IsDummy && m_ESPs[index].m_Enabled && !m_ESPs[index].m_HasIni) { - return QIcon(":/MO/gui/edit_clear"); - } else { - return QVariant(); - } } else if ((role == Qt::CheckStateRole) && (modelIndex.column() == 0)) { return m_ESPs[index].m_Enabled ? Qt::Checked : Qt::Unchecked; } else if (role == Qt::FontRole) { @@ -754,8 +917,15 @@ QVariant PluginList::data(const QModelIndex &modelIndex, int role) const return QVariant(Qt::AlignHCenter | Qt::AlignVCenter); } } else if (role == Qt::ToolTipRole) { + QString toolTip; + if (!m_ESPs[index].m_BOSSMessages.isEmpty()) { + toolTip += m_ESPs[index].m_BOSSMessages.join("
    ") + "

    "; + } + if (m_ESPs[index].m_BOSSUnrecognized) { + toolTip += "Not recognized by BOSS
    "; + } if (m_ESPs[index].m_ForceEnabled) { - return tr("This plugin can't be disabled (enforced by the game)"); + toolTip += tr("This plugin can't be disabled (enforced by the game)"); } else { QString text = tr("Origin: %1").arg(m_ESPs[index].m_OriginName); if (m_ESPs[index].m_MasterUnset.size() > 0) { @@ -773,8 +943,30 @@ QVariant PluginList::data(const QModelIndex &modelIndex, int role) const text += "
    This file is a dummy! It exists only so the bsa with the same name gets loaded. With MO you don't need this: " "If you enable the archive with the same name in the \"Archive\" tab you can disable this plugin."; } - return text; + toolTip += text; + } + return toolTip; + } else if (role == Qt::UserRole + 1) { + QVariantList result; + if (m_ESPs[index].m_MasterUnset.size() > 0) { + result.append(QIcon(":/MO/gui/warning")); + } + if (m_LockedOrder.find(m_ESPs[index].m_Name.toLower()) != m_LockedOrder.end()) { + result.append(QIcon(":/MO/gui/locked")); } + if (!m_ESPs[index].m_BOSSMessages.isEmpty()) { + result.append(QIcon(":/MO/gui/information")); + } + if (m_ESPs[index].m_BOSSUnrecognized) { + result.append(QIcon(":/MO/gui/help")); + } + if (m_ESPs[index].m_HasIni) { + result.append(QIcon(":/MO/gui/attachment")); + } + if (m_ESPs[index].m_IsDummy && m_ESPs[index].m_Enabled && !m_ESPs[index].m_HasIni) { + result.append(QIcon(":/MO/gui/edit_clear")); + } + return result; } else { return QVariant(); } @@ -784,7 +976,6 @@ QVariant PluginList::data(const QModelIndex &modelIndex, int role) const bool PluginList::setData(const QModelIndex &modIndex, const QVariant &value, int role) { if (role == Qt::CheckStateRole) { -qDebug("uncheck plugin"); m_ESPs[modIndex.row()].m_Enabled = value.toInt() == Qt::Checked; emit dataChanged(modIndex, modIndex); @@ -1036,7 +1227,8 @@ PluginList::ESPInfo::ESPInfo(const QString &name, bool enabled, FILETIME time, const QString &originName, const QString &fullPath, bool hasIni) : m_Name(name), m_Enabled(enabled), m_ForceEnabled(enabled), m_Removed(false), - m_Priority(0), m_LoadOrder(-1), m_Time(time), m_OriginName(originName), m_HasIni(hasIni) + m_Priority(0), m_LoadOrder(-1), m_Time(time), m_OriginName(originName), m_HasIni(hasIni), + m_BOSSUnrecognized(false) { try { ESP::File file(ToWString(fullPath)); diff --git a/src/pluginlist.h b/src/pluginlist.h index 64c914df..bb7428d0 100644 --- a/src/pluginlist.h +++ b/src/pluginlist.h @@ -25,8 +25,12 @@ along with Mod Organizer. If not, see . #include #include #include +#include #include +#include #include +#include +#include /** @@ -40,6 +44,7 @@ public: enum EColumn { COL_NAME, + COL_FLAGS, COL_PRIORITY, COL_MODINDEX, @@ -143,6 +148,8 @@ public: void refreshLoadOrder(); + void bossSort(); + public: virtual PluginState state(const QString &name) const; virtual int priority(const QString &name) const; @@ -162,6 +169,7 @@ public: // implementation of the QAbstractTableModel interface virtual Qt::DropActions supportedDropActions() const { return Qt::MoveAction; } virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); + void applyBOSSSorting(uint8_t **pluginList, size_t size, int &priority, bool recognized, const char *extension); public slots: /** @@ -194,7 +202,6 @@ private: ESPInfo(const QString &name, bool enabled, FILETIME time, const QString &originName, const QString &fullPath, bool hasIni); QString m_Name; - QString m_FullPath; bool m_Enabled; bool m_ForceEnabled; bool m_Removed; @@ -207,12 +214,91 @@ private: bool m_HasIni; std::set m_Masters; mutable std::set m_MasterUnset; + QStringList m_BOSSMessages; + bool m_BOSSUnrecognized; }; friend bool ByName(const ESPInfo& LHS, const ESPInfo& RHS); friend bool ByDate(const ESPInfo& LHS, const ESPInfo& RHS); friend bool ByPriority(const ESPInfo& LHS, const ESPInfo& RHS); + + class BossDLL : public PDLL { + DECLARE_CLASS(BossDLL) + + DECLARE_FUNCTION3(__cdecl, uint32_t, CreateBossDb, boss_db*, const uint32_t, const uint8_t*) + DECLARE_FUNCTION1(__cdecl, void, DestroyBossDb, boss_db) + DECLARE_FUNCTION0(__cdecl, void, CleanUpAPI) + + DECLARE_FUNCTION7(__cdecl, uint32_t, SortCustomMods, boss_db, uint8_t**, size_t, uint8_t***, size_t*, uint8_t***, size_t*) + DECLARE_FUNCTION3(__cdecl, uint32_t, SetActivePluginsDumb, boss_db, uint8_t**, const size_t) + + DECLARE_FUNCTION3(__cdecl, uint32_t, GetActivePluginsDumb , boss_db, uint8_t***, size_t*) + + DECLARE_FUNCTION2(__cdecl, uint32_t, UpdateMasterlist, boss_db, const uint8_t*) + DECLARE_FUNCTION3(__cdecl, uint32_t, Load, boss_db, const uint8_t*, const uint8_t*) + + DECLARE_FUNCTION2(__cdecl, void, SetLoggerOutput, const char*, uint8_t) + DECLARE_FUNCTION1(__cdecl, uint32_t, GetLastErrorDetails, uint8_t**) + + DECLARE_FUNCTION1(__cdecl, uint32_t, GetVersionString, uint8_t**) + DECLARE_FUNCTION3(__cdecl, bool, IsCompatibleVersion, const uint32_t, const uint32_t, const uint32_t) + + DECLARE_FUNCTION4(__cdecl, uint32_t, GetPluginMessages, boss_db, const uint8_t*, BossMessage**, size_t*) + + enum ResultCode { + RESULT_OK = 0, + RESULT_NO_MASTER_FILE = 1, + RESULT_FILE_READ_FAIL = 2, + RESULT_FILE_WRITE_FAIL = 3, + RESULT_FILE_NOT_UTF8 = 4, + RESULT_FILE_NOT_FOUND = 5, + RESULT_FILE_PARSE_FAIL = 6, + RESULT_CONDITION_EVAL_FAIL = 7, + RESULT_REGEX_EVAL_FAIL = 8, + RESULT_NO_GAME_DETECTED = 9, + RESULT_ENCODING_CONVERSION_FAIL = 10, + RESULT_FIND_ONLINE_MASTERLIST_REVISION_FAIL = 11, + RESULT_FIND_ONLINE_MASTERLIST_DATE_FAIL = 12, + RESULT_READ_UPDATE_FILE_LIST_FAIL = 13, + RESULT_FILE_CRC_MISMATCH = 14, + RESULT_FS_FILE_MOD_TIME_READ_FAIL = 15, + RESULT_FS_FILE_MOD_TIME_WRITE_FAIL = 16, + RESULT_FS_FILE_RENAME_FAIL = 17, + RESULT_FS_FILE_DELETE_FAIL = 18, + RESULT_FS_CREATE_DIRECTORY_FAIL = 19, + RESULT_FS_ITER_DIRECTORY_FAIL = 20, + RESULT_CURL_INIT_FAIL = 21, + RESULT_CURL_SET_ERRBUFF_FAIL = 22, + RESULT_CURL_SET_OPTION_FAIL = 23, + RESULT_CURL_SET_PROXY_FAIL = 24, + RESULT_CURL_SET_PROXY_TYPE_FAIL = 25, + RESULT_CURL_SET_PROXY_AUTH_FAIL = 26, + RESULT_CURL_SET_PROXY_AUTH_TYPE_FAIL = 27, + RESULT_CURL_PERFORM_FAIL = 28, + RESULT_CURL_USER_CANCEL = 29, + RESULT_GUI_WINDOW_INIT_FAIL = 30, + RESULT_NO_UPDATE_NECESSARY = 31, + RESULT_LO_MISMATCH = 32, + RESULT_NO_MEM = 33, + RESULT_INVALID_ARGS = 34, + RESULT_NETWORK_FAIL = 35, + RESULT_NO_INTERNET_CONNECTION = 36, + RESULT_NO_TAG_MAP = 37, + RESULT_PLUGINS_FULL = 38, + RESULT_PLUGIN_BEFORE_MASTER = 39, + RESULT_INVALID_SYNTAX = 40 + }; + + enum GameIDs { + AUTODETECT = 0, + OBLIVION = 1, + SKYRIM = 3, + FALLOUT3 = 4, + FALLOUTNV = 5 + }; + }; + private: void syncLoadOrder(); @@ -230,6 +316,9 @@ private: void testMasters(); + void initBoss(); + void convertPluginListForBoss(boost::ptr_vector &inputPlugins, std::vector &activePlugins); + private: std::vector m_ESPs; @@ -253,6 +342,10 @@ private: SignalRefreshed m_Refreshed; + BossDLL *m_BOSS; + boss_db m_BOSSDB; + QTemporaryFile m_TempFile; + }; #endif // PLUGINLIST_H diff --git a/src/pluginlistsortproxy.cpp b/src/pluginlistsortproxy.cpp index 5bff24f5..8412fa27 100644 --- a/src/pluginlistsortproxy.cpp +++ b/src/pluginlistsortproxy.cpp @@ -125,11 +125,6 @@ bool PluginListSortProxy::lessThan(const QModelIndex &left, return plugins->getPriority(left.row()) < plugins->getPriority(right.row()); } break; default: { - static bool first = true; - if (first) { - qCritical("invalid sort column %d", left.column()); - first = false; - } return plugins->getPriority(left.row()) < plugins->getPriority(right.row()); } break; } diff --git a/src/profile.cpp b/src/profile.cpp index e075a4e6..c1f1025e 100644 --- a/src/profile.cpp +++ b/src/profile.cpp @@ -23,6 +23,7 @@ along with Mod Organizer. If not, see . #include "windows_error.h" #include "dummybsa.h" #include "modinfo.h" +#include "safewritefile.h" #include #include #include @@ -149,15 +150,10 @@ void Profile::writeModlistNow(bool onlyOnTimer) const #pragma message("right now, this is doing unnecessary saves. Need a flag that says that mod priority, enabled-state or name of a mod has changed") try { - QTemporaryFile file; - - if (!file.open()) { - reportError(tr("failed to open temporary file")); - return; - } + QString fileName = getModlistFileName(); + SafeWriteFile file(fileName); - file.resize(0); - file.write(QString("# This file was automatically generated by Mod Organizer.\r\n").toUtf8()); + file->write(QString("# This file was automatically generated by Mod Organizer.\r\n").toUtf8()); if (m_ModStatus.empty()) { return; } @@ -169,29 +165,17 @@ void Profile::writeModlistNow(bool onlyOnTimer) const ModInfo::Ptr modInfo = ModInfo::getByIndex(index); if (modInfo->getFixedPriority() == INT_MIN) { if (m_ModStatus[index].m_Enabled) { - file.write("+"); + file->write("+"); } else { - file.write("-"); + file->write("-"); } - file.write(modInfo->name().toUtf8()); - file.write("\r\n"); + file->write(modInfo->name().toUtf8()); + file->write("\r\n"); } } } - file.close(); - - - QString fileName = getModlistFileName(); - - if (QFile::exists(fileName)) { - shellDeleteQuiet(fileName); - } - - if (!file.copy(fileName)) { - reportError(tr("failed to open \"%1\" for writing").arg(fileName)); - return; - } + file.commit(); qDebug("%s saved", QDir::toNativeSeparators(fileName).toUtf8().constData()); } catch (const std::exception &e) { diff --git a/src/resources.qrc b/src/resources.qrc index 1582a3f2..73921f64 100644 --- a/src/resources.qrc +++ b/src/resources.qrc @@ -18,7 +18,7 @@ resources/contact-new.png resources/preferences-system.png resources/application-x-executable.png - resources/dialog-information.png + resources/dialog-information.png resources/emblem-readonly.png resources/go-next_16.png resources/go-previous_16.png diff --git a/src/safewritefile.cpp b/src/safewritefile.cpp new file mode 100644 index 00000000..6df8c2b8 --- /dev/null +++ b/src/safewritefile.cpp @@ -0,0 +1,48 @@ +/* +Copyright (C) 2014 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 "safewritefile.h" +#include + + +using namespace MOBase; + + +SafeWriteFile::SafeWriteFile(const QString &fileName) +: m_FileName(fileName) +{ + if (!m_TempFile.open()) { + throw MyException(QObject::tr("failed to open temporary file")); + } +} + + +QFile *SafeWriteFile::operator->() { + Q_ASSERT(m_TempFile.isOpen()); + return &m_TempFile; +} + + +void SafeWriteFile::commit() { + shellDeleteQuiet(m_FileName); + m_TempFile.rename(m_FileName); + m_TempFile.setAutoRemove(false); + m_TempFile.close(); +} diff --git a/src/safewritefile.h b/src/safewritefile.h new file mode 100644 index 00000000..56bd7744 --- /dev/null +++ b/src/safewritefile.h @@ -0,0 +1,51 @@ +/* +Copyright (C) 2014 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 . +*/ + + +#ifndef SAFEWRITEFILE_H +#define SAFEWRITEFILE_H + + +#include +#include +#include + +/** + * @brief a wrapper for QFile that ensures the file is only actually (over-)written if writing was successful + */ +class SafeWriteFile { +public: + SafeWriteFile(const QString &fileName); + + QFile *operator->(); + + void commit(); + +private: + QString m_FileName; + QTemporaryFile m_TempFile; +}; + + +#endif // SAFEWRITEFILE_H + + + + + -- cgit v1.3.1 From af50eedbe275062eda933ccc200be66a9d3ee94d Mon Sep 17 00:00:00 2001 From: Tannin Date: Wed, 29 Jan 2014 00:23:16 +0100 Subject: - bugfix: dds preview or transparent textures looked odd - bugfix: lazy loaded data tree deleted the placeholder --- src/mainwindow.cpp | 11 +++++++++-- src/mainwindow.h | 3 +++ src/version.rc | 4 ++-- 3 files changed, 14 insertions(+), 4 deletions(-) (limited to 'src/mainwindow.h') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index ce4471a6..ce2c02b6 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1578,6 +1578,13 @@ void MainWindow::updateTo(QTreeWidgetItem *subTree, const std::wstring &director subTree->sortChildren(0, Qt::AscendingOrder); } +void MainWindow::delayedRemove() +{ + foreach (QTreeWidgetItem *item, m_RemoveWidget) { + item->removeChild(item->child(0)); + } + m_RemoveWidget.clear(); +} void MainWindow::expandDataTreeItem(QTreeWidgetItem *item) { @@ -1586,7 +1593,6 @@ void MainWindow::expandDataTreeItem(QTreeWidgetItem *item) QTreeWidgetItem *onDemandDataItem = item->child(0); std::wstring path = ToWString(onDemandDataItem->data(0, Qt::UserRole + 1).toString()); bool conflictsOnly = onDemandDataItem->data(0, Qt::UserRole + 2).toBool(); - item->removeChild(onDemandDataItem); std::wstring virtualPath = (path + L"\\").substr(6) + ToWString(item->text(0)); DirectoryEntry *dir = m_DirectoryStructure->findSubDirectoryRecursive(virtualPath); @@ -1595,7 +1601,8 @@ void MainWindow::expandDataTreeItem(QTreeWidgetItem *item) } else { qWarning("failed to update view of %ls", path.c_str()); } - + m_RemoveWidget.push_back(item); + QTimer::singleShot(5, this, SLOT(delayedRemove())); } } diff --git a/src/mainwindow.h b/src/mainwindow.h index b247b924..527db8b3 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -372,6 +372,8 @@ private: QFileSystemWatcher m_SavesWatcher; + std::vector m_RemoveWidget; + private slots: void showMessage(const QString &message); @@ -529,6 +531,7 @@ private slots: void refreshSavesIfOpen(); void expandDataTreeItem(QTreeWidgetItem *item); void about(); + void delayedRemove(); private slots: // ui slots // actions diff --git a/src/version.rc b/src/version.rc index 1beee814..14b2781f 100644 --- a/src/version.rc +++ b/src/version.rc @@ -1,7 +1,7 @@ #include "Winver.h" -#define VER_FILEVERSION 1,1,0,0 -#define VER_FILEVERSION_STR "1,1,0,0\0" +#define VER_FILEVERSION 1,1,1,0 +#define VER_FILEVERSION_STR "1,1,1,0\0" VS_VERSION_INFO VERSIONINFO FILEVERSION VER_FILEVERSION -- cgit v1.3.1 From 9c8e43853dfeb6ac80faffe0960222ff0f087fd3 Mon Sep 17 00:00:00 2001 From: Tannin Date: Wed, 12 Feb 2014 21:00:49 +0100 Subject: - a few hooks will now somewhat handle file names starting with \\?\ - mod meta information is now (also) saved by a timer to reduce the likelyhood of a data loss in case of a crash - mod meta files are now written to a temporary file and then renamed to real name to reduce chance of breaking the file - updated minimum compatible nmm version to 0.47.0 - bugfix: defaults for newestVersion, version and installationFile when creating an empty mod were integers instead of strings - bugfix: "Plugins" and "Archives" weren't translatable --- src/mainwindow.cpp | 19 +- src/mainwindow.h | 3 + src/mainwindow.ui | 4 +- src/modinfo.cpp | 62 ++++-- src/modinfo.h | 2 +- src/organizer_cs.ts | 498 ++++++++++++++++++++++++------------------------ src/organizer_de.ts | 500 +++++++++++++++++++++++++------------------------ src/organizer_es.ts | 500 +++++++++++++++++++++++++------------------------ src/organizer_fr.ts | 500 +++++++++++++++++++++++++------------------------ src/organizer_ru.ts | 498 ++++++++++++++++++++++++------------------------ src/organizer_tr.ts | 500 +++++++++++++++++++++++++------------------------ src/organizer_zh_CN.ts | 498 ++++++++++++++++++++++++------------------------ src/organizer_zh_TW.ts | 498 ++++++++++++++++++++++++------------------------ src/settings.cpp | 2 +- 14 files changed, 2111 insertions(+), 1973 deletions(-) (limited to 'src/mainwindow.h') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index d83d4d06..12db5e2b 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -295,6 +295,10 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget m_CheckBSATimer.setSingleShot(true); connect(&m_CheckBSATimer, SIGNAL(timeout()), this, SLOT(checkBSAList())); + m_SaveMetaTimer.setSingleShot(false); + connect(&m_SaveMetaTimer, SIGNAL(timeout()), this, SLOT(saveModMetas())); + m_SaveMetaTimer.start(5000); + m_DirectoryRefresher.moveToThread(&m_RefresherThread); m_RefresherThread.start(); @@ -1208,10 +1212,10 @@ IModInterface *MainWindow::createMod(GuessedValue &name) QSettings settingsFile(targetDirectory.mid(0).append("/meta.ini"), QSettings::IniFormat); settingsFile.setValue("modid", 0); - settingsFile.setValue("version", 0); - settingsFile.setValue("newestVersion", 0); + settingsFile.setValue("version", ""); + settingsFile.setValue("newestVersion", ""); settingsFile.setValue("category", 0); - settingsFile.setValue("installationFile", 0); + settingsFile.setValue("installationFile", ""); return ModInfo::createFrom(QDir(targetDirectory), &m_DirectoryStructure).data(); // } } @@ -1956,6 +1960,15 @@ void MainWindow::checkBSAList() } +void MainWindow::saveModMetas() +{ + for (unsigned int i = 0; i < ModInfo::getNumMods(); ++i) { + ModInfo::Ptr modInfo = ModInfo::getByIndex(i); + modInfo->saveMeta(); + } + } + + void MainWindow::fixCategories() { for (unsigned int i = 0; i < ModInfo::getNumMods(); ++i) { diff --git a/src/mainwindow.h b/src/mainwindow.h index 527db8b3..250f2a48 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -352,6 +352,7 @@ private: bool m_DirectoryUpdate; bool m_ArchivesInit; QTimer m_CheckBSATimer; + QTimer m_SaveMetaTimer; QTime m_StartTime; SaveGameInfoWidget *m_CurrentSaveView; @@ -495,6 +496,8 @@ private slots: void startExeAction(); void checkBSAList(); + void saveModMetas(); + void updateStyle(const QString &style); void modlistChanged(const QModelIndex &index, int role); diff --git a/src/mainwindow.ui b/src/mainwindow.ui index f3bb425c..110c14c6 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -602,7 +602,7 @@ p, li { white-space: pre-wrap; }
    - Plugins + Plugins @@ -738,7 +738,7 @@ p, li { white-space: pre-wrap; } - Archives + Archives diff --git a/src/modinfo.cpp b/src/modinfo.cpp index 85612b32..ff44b392 100644 --- a/src/modinfo.cpp +++ b/src/modinfo.cpp @@ -373,26 +373,48 @@ void ModInfoRegular::saveMeta() { // only write meta data if the mod directory exists if (m_MetaInfoChanged && QFile::exists(absolutePath())) { - QSettings metaFile(absolutePath().append("/meta.ini"), QSettings::IniFormat); - if (metaFile.status() == QSettings::NoError) { - std::set temp = m_Categories; - temp.erase(m_PrimaryCategory); - metaFile.setValue("category", QString("%1").arg(m_PrimaryCategory) + "," + SetJoin(temp, ",")); - metaFile.setValue("newestVersion", m_NewestVersion.canonicalString()); - metaFile.setValue("ignoredVersion", m_IgnoredVersion.canonicalString()); - metaFile.setValue("version", m_Version.canonicalString()); - metaFile.setValue("modid", m_NexusID); - metaFile.setValue("notes", m_Notes); - metaFile.setValue("nexusDescription", m_NexusDescription); - metaFile.setValue("lastNexusQuery", m_LastNexusQuery.toString(Qt::ISODate)); - if (m_EndorsedState != ENDORSED_UNKNOWN) { - metaFile.setValue("endorsed", m_EndorsedState); + bool success = false; + { + QSettings metaFile(absolutePath().append("/meta.ini.new"), QSettings::IniFormat); + if (metaFile.status() == QSettings::NoError) { + std::set temp = m_Categories; + temp.erase(m_PrimaryCategory); + metaFile.setValue("category", QString("%1").arg(m_PrimaryCategory) + "," + SetJoin(temp, ",")); + metaFile.setValue("newestVersion", m_NewestVersion.canonicalString()); + metaFile.setValue("ignoredVersion", m_IgnoredVersion.canonicalString()); + metaFile.setValue("version", m_Version.canonicalString()); + if (m_NexusID != -1) { + metaFile.setValue("modid", m_NexusID); + } + metaFile.setValue("notes", m_Notes); + metaFile.setValue("nexusDescription", m_NexusDescription); + metaFile.setValue("lastNexusQuery", m_LastNexusQuery.toString(Qt::ISODate)); + if (m_EndorsedState != ENDORSED_UNKNOWN) { + metaFile.setValue("endorsed", m_EndorsedState); + } + metaFile.sync(); // sync needs to be called to ensure the file is created + + if (metaFile.status() == QSettings::NoError) { + success = true; + m_MetaInfoChanged = false; + } else { + reportError(tr("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())); + } + } + if (success) { + if (!QFile::remove(absolutePath().append("/meta.ini"))) { + qCritical("failed to remove %s", qPrintable(absolutePath().append("/meta.ini"))); + return; + } + if (QFile::rename(absolutePath().append("/meta.ini.new"), absolutePath().append("/meta.ini"))) { + qDebug("%s written", qPrintable(absolutePath().append("/meta.ini"))); + } else { + qCritical("writing %s failed", qPrintable(absolutePath().append("/meta.ini"))); } - metaFile.sync(); // sync needs to be called to ensure the file is created - } else { - reportError(tr("failed to write %1/meta.ini: %2").arg(absolutePath()).arg(metaFile.status())); } - m_MetaInfoChanged = false; } } @@ -556,6 +578,10 @@ void ModInfoRegular::setVersion(const VersionInfo &version) m_MetaInfoChanged = true; } +void ModInfoRegular::setNewestVersion(const VersionInfo &version) { + m_NewestVersion = version; +} + void ModInfoRegular::setNexusDescription(const QString &description) { m_NexusDescription = description; diff --git a/src/modinfo.h b/src/modinfo.h index 83654c7e..b7d72584 100644 --- a/src/modinfo.h +++ b/src/modinfo.h @@ -592,7 +592,7 @@ public: * @todo this function should be made obsolete. All queries for mod information should go through * this class so no public function for this change is required **/ - void setNewestVersion(const MOBase::VersionInfo &version) { m_NewestVersion = version; } + void setNewestVersion(const MOBase::VersionInfo &version); /** * @brief changes/updates the nexus description text diff --git a/src/organizer_cs.ts b/src/organizer_cs.ts index 08f8d70e..654ca00c 100644 --- a/src/organizer_cs.ts +++ b/src/organizer_cs.ts @@ -1638,11 +1638,21 @@ BSAs checked here are loaded in such a way that your installation order is obeye Data Data + + + Plugins + + Sort + + + Archives + + refresh data-directory overview @@ -1655,8 +1665,8 @@ BSAs checked here are loaded in such a way that your installation order is obeye - - + + Refresh Znovunačíst @@ -1836,7 +1846,7 @@ p, li { white-space: pre-wrap; } - + Update @@ -1847,7 +1857,7 @@ p, li { white-space: pre-wrap; } - + No Problems Žádné problémy @@ -1880,37 +1890,37 @@ V současnosti má omezenou funkcionalitu - + Endorse Mod Organizer - + Toolbar - + Desktop - + Start Menu - + Problems Problémy - + There are potential problems with your setup Existují potenciální problémy s programem - + Everything seems to be in order Všechno se jeví v pořádku @@ -1927,22 +1937,22 @@ V současnosti má omezenou funkcionalitu <li>dotNet není nainstalován nebo je neaktuální. Tohle vyžaduje NCC. Najděte ho zde: <a href="%1">%1</a></li> - + Help on UI Pomoc s programem - + Documentation Wiki Dokumentace wiki - + Report Issue Nahlásit chybu - + Tutorials @@ -1951,93 +1961,92 @@ V současnosti má omezenou funkcionalitu pořadí načtení se nezdařilo uložit - + failed to save load order: %1 zlyhalo uložení pořadí načtení: %1 - failed to save archives order, do you have write access to "%1"? - zlyhal zápis pořadí archivů, máte administrátorsky povoleno zapisovat na "%1"? + zlyhal zápis pořadí archivů, máte administrátorsky povoleno zapisovat na "%1"? - + About - + About Qt - + Name Jméno - + Please enter a name for the new profile Prosím zadej jméno pro nový profil - + failed to create profile: %1 Zlyhalo vytvoření profilu: %1 - + Show tutorial? - + You are starting Mod Organizer for the first time. Do you want to show a tutorial of its basic features? If you choose no you can always start the tutorial from the "Help"-menu. - + Downloads in progress Probíhá stahování - + There are still downloads in progress, do you really want to quit? Pořád probíhá stahování, určitě chcete skončit (zruší stahování)? - + failed to read savegame: %1 nezdařilo se načíst pozici: %1 - + Plugin "%1" failed: %2 - + Plugin error - + It appears the plugin "%1" failed to load last startup and caused MO to crash. Do you want to disable it? (Please note: If this is the first time you see this message for this plugin you may want to give it another try. The plugin may be able to recover from the problem) - + Failed to start "%1" Zlyhal start "%1" - + Waiting Čekání - + Please press OK once you're logged into steam. Stiskni OK až budeš přihlášen do Steamu. @@ -2046,331 +2055,336 @@ V současnosti má omezenou funkcionalitu "%1" nenalezeno - + Start Steam? Spustit Steam? - + Steam is required to be running already to correctly start the game. Should MO try to start steam now? Steam by měl běžet aby se podařilo spustit hru. Má se MO pokusit spustit steam teď? - + Also in: <br> Také v: <br> - + No conflict Žádné konflikty - + <Edit...> <Edit...> - + This bsa is enabled in the ini file so it may be required! Tenhle BSA soubor je aktivován v ini souboru, tak zřejmě je vyžadován! - + This archive will still be loaded since there is a plugin of the same name but its files will not follow installation order! Tento archív se stejně načte, protože existuje plugin se stejným jménem ale jeho soubory nebudou zodpovídat pořadí nainstalování! - + Activating Network Proxy - - + + Installation successful Instalace úspěšná - - + + Configure Mod Konfigurace Modu - - + + This mod contains ini tweaks. Do you want to configure them now? Tenhle mod obsahuje ini úpravy. Chcete je nastavovat teď? - - + + mod "%1" not found mod "%1" nenalezen - - + + Installation cancelled Instalace zrušena - - + + The mod was not installed completely. Tento mod se nenainstaloval úplne. - + Some plugins could not be loaded - + Too many esps and esms enabled - - + + Description missing - + The game doesn't allow more than 255 active plugins (including the official ones) to be loaded. You have to disable some unused plugins or merge some plugins into one. You can find a guide here: <a href="http://wiki.step-project.com/Guide:Merging_Plugins">http://wiki.step-project.com/Guide:Merging_Plugins</a> - + Choose Mod Vyber Mod - + Mod Archive Archív Modu - + Start Tutorial? - + You're about to start a tutorial. For technical reasons it's not possible to end the tutorial early. Continue? - - + + Download started Stahování začalo - + failed to update mod list: %1 nezdařilo se aktualizovat seznam modů: %1 - + failed to spawn notepad.exe: %1 zlyhalo otevření notepad.exe: %1 - + failed to open %1 - + failed to change origin name: %1 Nezdařilo se změnit původní jméno: %1 - + + failed to move "%1" from mod "%2" to "%3": %4 + + + + Multiple esps activated, please check that they don't conflict. - - + + Create Mod... - + A mod with this name already exists - + Continue? - + The versioning scheme decides which version is considered newer than another. This function will guess the versioning scheme under the assumption that the installed version is outdated. - - + + Sorry - + I don't know a versioning scheme where %1 is newer than %2. - + <All> <Všechny> - + <Checked> <Aktivované> - + Plugin "%1" failed - + failed to init plugin %1: %2 - + The following plugins could not be loaded. The reason may be missing dependencies (i.e. python) or an outdated version: - + <Unchecked> <Neaktivované> - + <Update> <Aktualizace> - + <No category> <Bez kategorie> - + <Conflicted> - + <Not Endorsed> - + failed to rename mod: %1 Nezdařilo se přejmenovat mod: %1 - + Overwrite? - + This will replace the existing mod "%1". Continue? - + failed to remove mod "%1" - - - + + + failed to rename "%1" to "%2" Nezdařilo se přejmenovat "%1" na "%2" - - - - + + + + Confirm Potvrdit - + Remove the following mods?<br><ul>%1</ul> - + failed to remove mod: %1 Nezdařilo se odstranit mod: %1 - - + + Failed Zlyhání - + Installation file no longer exists Instalační soubor již neexistuje - + Mods installed with old versions of MO can't be reinstalled in this way. Mody nainstalovány staršími verzemi MO nemůžou být přeinstalovány tímto spůsobem. - - + + You need to be logged in with Nexus to endorse - - + + Extract BSA Extrakce BSA - + This mod contains at least one BSA. Do you want to unpack it? (This removes the BSA after completion. If you don't know about BSAs, just select no) Tento mod obsahuje alespoň jeden BSA soubor. Chcete rozpakovat i jeho obsah? (BSA se po úspěšném rozpakování odstrání. Pokud nevíte, co je BSA, zvolte možnost Ne) - - - + + + failed to read %1: %2 - - + + This archive contains invalid hashes. Some files may be broken. Tento archiv má neplatné identifikační součty. Nekteré soubory mohou být poškozeny. - + Nexus ID for this Mod is unknown Nexus ID pro tento Mod není známo @@ -2383,92 +2397,92 @@ This function will guess the versioning scheme under the assumption that the ins Zvol Prioritu - + Really enable all visible mods? Opravdu aktivovat všechny zobrazené mody? - + Really disable all visible mods? Opravdu deaktivovat všechny zobrazené mody? - + Choose what to export - + Everything - + All installed mods are included in the list - + Active Mods - + Only active (checked) mods from your current profile are included - + Visible - + All mods visible in the mod list are included - + export failed: %1 - + Install Mod... Instaluj Mod... - + Enable all visible Aktivuj všechny v seznamu - + Disable all visible Deaktivuj všechny v seznamu - + Check all for update Skontroluj všechny pro aktualizaci - + Export to csv... - + Sync to Mods... Synchronizuj s Mody... - + Restore Backup - + Remove Backup... @@ -2477,362 +2491,362 @@ This function will guess the versioning scheme under the assumption that the ins Označ Kategorii - + Primary Category - + Change versioning scheme - + Un-ignore update - + Ignore update - + Rename Mod... Přejmenuj Mod... - + Remove Mod... Odstranit Mod... - + Reinstall Mod Přeinstaluj Mod - + Un-Endorse - - + + Endorse - + Won't endorse - + Endorsement state unknown - + Ignore missing data - + Visit on Nexus Navštiv na Nexusu - + Open in explorer Otevři v prohlížeči - + Information... Informace... - - + + Exception: Výnimky: - - + + Unknown exception Neznámá výnimka - + <Multiple> - + Really delete "%1"? - + Fix Mods... Oprav Mody... - + Delete - - + + failed to remove %1 - - + + failed to create %1 - + Can't change download directory while downloads are in progress! Není možné změnit cíl pro stahování když probíhá stahování! - + Download failed Stahování zlyhalo - + failed to write to file %1 Nezdařil se zápis do souboru %1 - + %1 written %1 zapsáno - + Select binary Vyber binární soubor - + Binary Soubor - + Enter Name Zadej jméno - + Please enter a name for the executable Prosím zadej jméno pro spouštění - + Not an executable Není spustitelný - + This is not a recognized executable. Tenhle soubor není rozpoznán jako spustitelný. - - + + Replace file? Nahradit soubor? - + There already is a hidden version of this file. Replace it? Už existuje skrytá verze tohto souboru. Nahradit? - - + + File operation failed Operace se souborem zlyhala - - + + Failed to remove "%1". Maybe you lack the required file permissions? Nepodařilo se odstranit "%1". Možná nejsou k dispozici požadována práva? - + There already is a visible version of this file. Replace it? Už existuje viditelná verze tohto souboru. Nahradit? - + file not found: %1 soubor nenalezen: %1 - + failed to generate preview for %1 - + Sorry, can't preview anything. This function currently does not support extracting from bsas. - + Update available Aktualizace k dispozici - + Open/Execute Otevřít/Spustit - + Add as Executable Přidat Spouštení - + Preview - + Un-Hide Odekrýt - + Hide Skrýt - + Write To File... Zápis do souboru... - + Do you want to endorse Mod Organizer on %1 now? - + Remove - + Unlock load order - + Lock load order - + BOSS working - + failed to run boss: %1 - + Request to Nexus failed: %1 - + Executable "%1" not found - + Failed to refresh list of esps: %1 - + This will move all files from overwrite into a new, regular mod. Please enter a name: - + Add/Remove Categories - + Replace Categories - - + + login successful - + login failed: %1. Trying to download anyway přihlášení zlyhalo: %1. Pokouším se beztak stahovat - + login failed: %1 přihlášení zlyhalo: %1 - + login failed: %1. You need to log-in with Nexus to update MO. přihlášení zlyhalo: %1. Na aktualizaci MO je potřebné přihlášení k Nexusu. - + Error Chyba - + failed to extract %1 (errorcode %2) zlyhala extrakce %1 (errorcode %2) - + Extract... Extrakce... - + Edit Categories... Editovat Kategorie... - + Enable all - + Disable all @@ -4811,64 +4825,64 @@ V současnosti má omezenou funkcionalitu esp nenalezeno: %1 - + Mod Index - + Flags - - + + unknown - + Name of your mods - + Load priority of your mod. The higher, the more "important" it is and thus overwrites data from plugins with lower priority. - + The modindex determins the formids of objects originating from this mods. - + failed to update esp info for file %1 (source id: %2), error: %3 - + esp not found: %1 - - + + Confirm Potvrdit - + Really enable all plugins? - + Really disable all plugins? - + The file containing locked plugin indices is broken @@ -4877,7 +4891,7 @@ V současnosti má omezenou funkcionalitu zlyhalo otevření výstupního souboru: %1 - + Some of your plugins have invalid names! These plugins can not be loaded by the game. Please see mo_interface.log for a list of affected plugins and rename them. Některé vaše pluginy mají neplatné názvy! Tyhle pluginy nemůžou být načteny hrou. Prosím nahlédněte do souboru mo_interface.log pro kompletní seznam pluginů a přejmenujte je. @@ -4887,17 +4901,17 @@ V současnosti má omezenou funkcionalitu - + Missing Masters - + Enabled Masters - + failed to restore load order for %1 @@ -4910,17 +4924,17 @@ V současnosti má omezenou funkcionalitu max - + This plugin can't be disabled (enforced by the game) Tenhle plugin nemůže být deaktivován (vyžaduje to hra) - + Origin: %1 Původní mod: %1 - + Name Jméno @@ -4929,7 +4943,7 @@ V současnosti má omezenou funkcionalitu Jména vašich modů - + Priority Priorita @@ -5512,23 +5526,23 @@ p, li { white-space: pre-wrap; } - + Please use "Help" from the toolbar to get usage instructions to all elements Prosím použijte "Pomoc" z panelu nástrojú pro instrukce ke všem elementům - - + + <Manage...> <Manage...> - + failed to parse profile %1: %2 Nezdařilo se rozebrat profil %1: %2 - + failed to find "%1" Nepodařilo sa najít "%1" @@ -5538,12 +5552,12 @@ p, li { white-space: pre-wrap; } Chyba kódování, prosím nahlaste tuto chybu a poskytněte záznamový soubor mo_interface.log! - + failed to access %1 zlyhal přístup k %1 - + failed to set file time %1 nepodařilo se nastavit čas souboru %1 diff --git a/src/organizer_de.ts b/src/organizer_de.ts index 306ce0ef..16d4521f 100644 --- a/src/organizer_de.ts +++ b/src/organizer_de.ts @@ -1642,11 +1642,21 @@ BSAs checked here are loaded in such a way that your installation order is obeye Data Data + + + Plugins + + Sort + + + Archives + + refresh data-directory overview @@ -1659,8 +1669,8 @@ BSAs checked here are loaded in such a way that your installation order is obeye - - + + Refresh Neu laden @@ -1840,7 +1850,7 @@ p, li { white-space: pre-wrap; } - + Update Aktualisierung @@ -1851,7 +1861,7 @@ p, li { white-space: pre-wrap; } - + No Problems Keine Probleme @@ -1882,37 +1892,37 @@ Diese Funktion ist noch in arbeit und ist sehr eingeschränkt. - + Endorse Mod Organizer - + Toolbar - + Desktop - + Start Menu - + Problems Probleme - + There are potential problems with your setup Es bestehen möglicherweise Probleme mit Ihrer Konfiguration - + Everything seems to be in order Alles in bester Ordnung @@ -1929,22 +1939,22 @@ Diese Funktion ist noch in arbeit und ist sehr eingeschränkt. <li>dotNet ist nicht installiert oder veraltet. Dies wird benötigt um NCC nutzen zu können. Laden Sie die korrekte Version von <a href="%1">%1</a> herunter</li> - + Help on UI Hilfe zur Oberfläche - + Documentation Wiki Wiki Dokumentation - + Report Issue Fehler melden - + Tutorials @@ -1953,93 +1963,88 @@ Diese Funktion ist noch in arbeit und ist sehr eingeschränkt. Ladereihenfolge konnte nicht gespeichert werden - + failed to save load order: %1 Reihenfolge konnt nicht gespeichert werden: %1 - - failed to save archives order, do you have write access to "%1"? - - - - + About - + About Qt - + Name Name - + Please enter a name for the new profile Bitte geben Sie einen Namen für das neue Profil an - + failed to create profile: %1 Erstellen des Profils fehlgeschlagen: %1 - + Show tutorial? - + You are starting Mod Organizer for the first time. Do you want to show a tutorial of its basic features? If you choose no you can always start the tutorial from the "Help"-menu. - + Downloads in progress Download in Bearbeitung - + There are still downloads in progress, do you really want to quit? Es gibt noch unfertige Downloads, wollen Sie wirklich das Programm beenden? - + failed to read savegame: %1 Spielstand konnte nicht gelesen werden: %1 - + Plugin "%1" failed: %2 - + Plugin error - + It appears the plugin "%1" failed to load last startup and caused MO to crash. Do you want to disable it? (Please note: If this is the first time you see this message for this plugin you may want to give it another try. The plugin may be able to recover from the problem) - + Failed to start "%1" - + Waiting Warte - + Please press OK once you're logged into steam. Bitte drücken sie OK sobald sie bei Steam angemeldet sind. @@ -2048,331 +2053,336 @@ Diese Funktion ist noch in arbeit und ist sehr eingeschränkt. "%1" nicht gefunden - + Start Steam? - + Steam is required to be running already to correctly start the game. Should MO try to start steam now? Steam muss laufen um das Spiel korrekt zu starten. Soll MO versuchen Steam zu starten? - + Also in: <br> Auch in: <br> - + No conflict Keine Konflikte - + <Edit...> <Bearbeiten...> - + This bsa is enabled in the ini file so it may be required! Dieses Archiv ist in der ini Konfiguration gelistet, daher ist es möglicherweise erforderlich! - + This archive will still be loaded since there is a plugin of the same name but its files will not follow installation order! Dieses Archiv wird vom Spiel geladen werden da ein Plugin mit dem selben Namen aktiv ist aber die enthaltenen Dateien werden sich nicht an Ihre Installations-Reihenfolge halten! - + Activating Network Proxy - - + + Installation successful Installation erfolgreich - - + + Configure Mod Mod konfigurieren - - + + This mod contains ini tweaks. Do you want to configure them now? Diese Mod enthält Anpassungen für die Ini datei. Wollen Sie diese nun konfigurieren? - - + + mod "%1" not found mod "%1" nicht gefunden - - + + Installation cancelled Installation abgebrochen - - + + The mod was not installed completely. Die mod wurde nicht erfolgreich installiert. - + Some plugins could not be loaded - + Too many esps and esms enabled - - + + Description missing - + The game doesn't allow more than 255 active plugins (including the official ones) to be loaded. You have to disable some unused plugins or merge some plugins into one. You can find a guide here: <a href="http://wiki.step-project.com/Guide:Merging_Plugins">http://wiki.step-project.com/Guide:Merging_Plugins</a> - + Choose Mod Mod wählen - + Mod Archive Mod Archiv - + Start Tutorial? - + You're about to start a tutorial. For technical reasons it's not possible to end the tutorial early. Continue? - - + + Download started Download gestartet - + failed to update mod list: %1 Aktualisieren der Modliste fehlgeschlagen: %1 - + failed to spawn notepad.exe: %1 notepad.exe konnte nicht aufgerufen werden: %1 - + failed to open %1 %1 konnte nicht geöffnet werden - + failed to change origin name: %1 konnte den Namen der Dateiquelle nicht ändern: %1 - + + failed to move "%1" from mod "%2" to "%3": %4 + + + + Multiple esps activated, please check that they don't conflict. - - + + Create Mod... - + A mod with this name already exists - + Continue? - + The versioning scheme decides which version is considered newer than another. This function will guess the versioning scheme under the assumption that the installed version is outdated. - - + + Sorry - + I don't know a versioning scheme where %1 is newer than %2. - + <All> <Alle> - + <Checked> <Markierte> - + Plugin "%1" failed - + failed to init plugin %1: %2 - + The following plugins could not be loaded. The reason may be missing dependencies (i.e. python) or an outdated version: - + <Unchecked> <Nicht markierte> - + <Update> <Update> - + <No category> <Ohne Kategorie> - + <Conflicted> - + <Not Endorsed> - + failed to rename mod: %1 konnte die Mod nicht umbenennen: %1 - + Overwrite? - + This will replace the existing mod "%1". Continue? - + failed to remove mod "%1" - - - + + + failed to rename "%1" to "%2" - - - - + + + + Confirm Bestätigen - + Remove the following mods?<br><ul>%1</ul> - + failed to remove mod: %1 konnte die mod nicht entfernen: %1 - - + + Failed Fehlgeschlagen - + Installation file no longer exists Installationsdatei existiert nicht mehr - + Mods installed with old versions of MO can't be reinstalled in this way. Mods die mit alten Versionen von MO installiert wurden können nicht auf diese Weise neu-installiert werden. - - + + You need to be logged in with Nexus to endorse - - + + Extract BSA BSA extrahieren - + This mod contains at least one BSA. Do you want to unpack it? (This removes the BSA after completion. If you don't know about BSAs, just select no) Diese mod enthält mindestens eine BSA datei. Soll sie entpackt werden? (Das BSA wird danach gelöscht. Wenn Sie nicht wissen was BSAs sind wählen Sie am besten \"nein\") - - - + + + failed to read %1: %2 - - + + This archive contains invalid hashes. Some files may be broken. Dieses Archiv enthält ungültige Prüfsummen. Einige Dateien sind evtl. defekt. - + Nexus ID for this Mod is unknown Nexus ID für diese Mod unbekannt @@ -2385,92 +2395,92 @@ This function will guess the versioning scheme under the assumption that the ins Priorität wählen - + Really enable all visible mods? Alle angezeigten Mods aktivieren? - + Really disable all visible mods? Alle angezeigten Mods deaktivieren? - + Choose what to export - + Everything - + All installed mods are included in the list - + Active Mods - + Only active (checked) mods from your current profile are included - + Visible - + All mods visible in the mod list are included - + export failed: %1 - + Install Mod... Mod installieren... - + Enable all visible Alle sichtbaren aktvieren - + Disable all visible Alle sichtbaren deaktvieren - + Check all for update Alle auf Aktualisierungen überprüfen - + Export to csv... - + Sync to Mods... Mods synchronisieren... - + Restore Backup - + Remove Backup... @@ -2479,362 +2489,362 @@ This function will guess the versioning scheme under the assumption that the ins Kategorie festlegen - + Primary Category - + Change versioning scheme - + Un-ignore update - + Ignore update - + Rename Mod... Mod umbenennen... - + Remove Mod... Mod entfernen... - + Reinstall Mod Mod neu installieren - + Un-Endorse - - + + Endorse - + Won't endorse - + Endorsement state unknown - + Ignore missing data - + Visit on Nexus Auf Nexus besuchen - + Open in explorer In Explorer öffnen - + Information... Informationen... - - + + Exception: Ausnahme: - - + + Unknown exception Unbekannte Ausnahme - + <Multiple> - + Really delete "%1"? - + Fix Mods... Mods reparieren... - + Delete - - + + failed to remove %1 %1 konnte nicht entfernt werden - - + + failed to create %1 %1 konnte nicht erstellt werden - + Can't change download directory while downloads are in progress! Das download verzeichnis kann nicht geändert werden solange Downloads laufen! - + Download failed Download fehlgeschlagen - + failed to write to file %1 Speichern in Datei "%1" fehlgeschlagen - + %1 written "%1" gespeichert - + Select binary Binary wählen - + Binary Ausführbare Datei - + Enter Name Namen eingeben - + Please enter a name for the executable Bitte geben Sie einen Namen für die Anwendungsdatei ein - + Not an executable Datei ist nicht ausführbar - + This is not a recognized executable. - - + + Replace file? Datei ersetzen? - + There already is a hidden version of this file. Replace it? Es existiert bereits eine versteckte Variante von dieser Datei. Soll diese ersetzt werden? - - + + File operation failed Dateioperation fehlgeschlagen - - + + Failed to remove "%1". Maybe you lack the required file permissions? Konnte "%1" nicht löschen. Fehlen Ihnen evtl. die nötigen Berechtigungen? - + There already is a visible version of this file. Replace it? Es existiert bereits eine sichtbare Variante dieser Datei. Soll diese ersetzt werden? - + file not found: %1 Datei nicht gefunden: %1 - + failed to generate preview for %1 - + Sorry, can't preview anything. This function currently does not support extracting from bsas. - + Update available Aktualisierung verfügbar - + Open/Execute Öffnen/Ausführen - + Add as Executable Als Anwendung hinzufügen - + Preview - + Un-Hide Sichtbar machen - + Hide Verstecken - + Write To File... In Datei speichern... - + Do you want to endorse Mod Organizer on %1 now? - + Remove - + Unlock load order - + Lock load order - + BOSS working - + failed to run boss: %1 - + Request to Nexus failed: %1 - + Executable "%1" not found - + Failed to refresh list of esps: %1 - + This will move all files from overwrite into a new, regular mod. Please enter a name: - + Add/Remove Categories - + Replace Categories - - + + login successful - + login failed: %1. Trying to download anyway login fehlgeschlagen: %1. Der Download scheitert vermutlich - + login failed: %1 login fehlgeschlagen: %1 - + login failed: %1. You need to log-in with Nexus to update MO. - + Error Fehler - + failed to extract %1 (errorcode %2) konnte "%1" nicht extrahieren (fehlercode %2) - + Extract... Extrahieren... - + Edit Categories... - + Enable all - + Disable all @@ -4908,64 +4918,64 @@ Diese Funktion ist noch in arbeit und ist sehr eingeschränkt. PluginList - + Mod Index - + Flags - - + + unknown - + Name of your mods - + Load priority of your mod. The higher, the more "important" it is and thus overwrites data from plugins with lower priority. - + The modindex determins the formids of objects originating from this mods. - + failed to update esp info for file %1 (source id: %2), error: %3 - + esp not found: %1 ESP nicht gefunden: %1 - - + + Confirm Bestätigen - + Really enable all plugins? - + Really disable all plugins? - + The file containing locked plugin indices is broken @@ -4974,7 +4984,7 @@ Diese Funktion ist noch in arbeit und ist sehr eingeschränkt. konnte die Ausgabedatei nicht öffnen: %1 - + Some of your plugins have invalid names! These plugins can not be loaded by the game. Please see mo_interface.log for a list of affected plugins and rename them. Einige Ihrer Plugins haben ungültige Namen! Diese Plugins können nicht vom Spiel geladen werden. Die Datei mo_interface.log enthält eine Liste der betroffenen Plugins. Bitte benennen Sie diese um. @@ -4984,17 +4994,17 @@ Diese Funktion ist noch in arbeit und ist sehr eingeschränkt. - + Missing Masters - + Enabled Masters - + failed to restore load order for %1 @@ -5007,17 +5017,17 @@ Diese Funktion ist noch in arbeit und ist sehr eingeschränkt. max - + This plugin can't be disabled (enforced by the game) Dieses Plugin kann nicht deaktiviert werden (vom Spiel benötigt) - + Origin: %1 Ursprung: %1 - + Name Name @@ -5026,7 +5036,7 @@ Diese Funktion ist noch in arbeit und ist sehr eingeschränkt. Namen Ihrer Mods - + Priority Priorität @@ -5617,7 +5627,7 @@ p, li { white-space: pre-wrap; } "%1" nicht gefunden - + Please use "Help" from the toolbar to get usage instructions to all elements Bitte verwenden Sie die "Hilfe" Funktion um Hinweise zu Nutzung aller Elemete zu bekommen @@ -5630,19 +5640,19 @@ p, li { white-space: pre-wrap; } Ungültige Priorität %1 - + failed to find "%1" Konnte "%1" nicht finden - - + + <Manage...> <Verwalten...> - + failed to parse profile %1: %2 Konnte Profil %1 nicht verarbeiten: %2 @@ -5651,12 +5661,12 @@ p, li { white-space: pre-wrap; } Kodierungsfehler! Bitte melden Sie dies als Bug und fügen Sie die Datei mo_interface.log bei! - + failed to access %1 Auf %1 konnte nicht zugegriffen werden - + failed to set file time %1 Konnte Dateizeit nicht setzen %1 diff --git a/src/organizer_es.ts b/src/organizer_es.ts index 0a19f56f..1dcb62c3 100644 --- a/src/organizer_es.ts +++ b/src/organizer_es.ts @@ -1501,11 +1501,21 @@ BSAs checked here are loaded in such a way that your installation order is obeye Data Datos + + + Plugins + + Sort + + + Archives + + refresh data-directory overview @@ -1518,8 +1528,8 @@ BSAs checked here are loaded in such a way that your installation order is obeye - - + + Refresh Recargar @@ -1693,7 +1703,7 @@ p, li { white-space: pre-wrap; } - + Update @@ -1704,7 +1714,7 @@ p, li { white-space: pre-wrap; } - + No Problems @@ -1734,148 +1744,143 @@ Right now this has very limited functionality - + Endorse Mod Organizer - + Toolbar - + Desktop - + Start Menu - + Problems - + There are potential problems with your setup - + Everything seems to be in order - + Help on UI - + Documentation Wiki - + Report Issue - + Tutorials - + failed to save load order: %1 Fallo guardando el orden de carga: %1 - - failed to save archives order, do you have write access to "%1"? - - - - + About - + About Qt - + Name Nombre - + Please enter a name for the new profile Por favor introduzca un nombre para el nuevo perfil - + failed to create profile: %1 Fallo al crear el perfil: %1 - + Show tutorial? - + You are starting Mod Organizer for the first time. Do you want to show a tutorial of its basic features? If you choose no you can always start the tutorial from the "Help"-menu. - + Downloads in progress Descarga en progreso - + There are still downloads in progress, do you really want to quit? Aun hay descargas en progreso, estas seguro que quieres salir? - + failed to read savegame: %1 Fallo al leer la partida guardada: %1 - + Plugin "%1" failed: %2 - + Plugin error - + It appears the plugin "%1" failed to load last startup and caused MO to crash. Do you want to disable it? (Please note: If this is the first time you see this message for this plugin you may want to give it another try. The plugin may be able to recover from the problem) - + Failed to start "%1" - + Waiting - + Please press OK once you're logged into steam. @@ -1884,330 +1889,335 @@ Right now this has very limited functionality "%1" no encontrado - + Start Steam? - + Steam is required to be running already to correctly start the game. Should MO try to start steam now? - + Also in: <br> - + No conflict Sin conflictos - + <Edit...> <Editar...> - + This bsa is enabled in the ini file so it may be required! - + This archive will still be loaded since there is a plugin of the same name but its files will not follow installation order! - + Activating Network Proxy - - + + Installation successful Instalacion completada - - + + Configure Mod Configurar Mod - - + + This mod contains ini tweaks. Do you want to configure them now? Este mod contiene modificaciones del Ini. Quieres configurarlas ahora? - - + + mod "%1" not found - - + + Installation cancelled - - + + The mod was not installed completely. - + Some plugins could not be loaded - + Too many esps and esms enabled - - + + Description missing - + The game doesn't allow more than 255 active plugins (including the official ones) to be loaded. You have to disable some unused plugins or merge some plugins into one. You can find a guide here: <a href="http://wiki.step-project.com/Guide:Merging_Plugins">http://wiki.step-project.com/Guide:Merging_Plugins</a> - + Choose Mod Seleccione Mod - + Mod Archive Fichero Mod - + Start Tutorial? - + You're about to start a tutorial. For technical reasons it's not possible to end the tutorial early. Continue? - - + + Download started Descarga comenzada - + failed to update mod list: %1 Fallo al actualizar la lista de Mods: %1 - + failed to spawn notepad.exe: %1 Fallo al cargar el Bloc de notas: %1 - + failed to open %1 Fallo al abrir %1 - + failed to change origin name: %1 fallo al cambiar el nombre original del fichero %1 - + + failed to move "%1" from mod "%2" to "%3": %4 + + + + Multiple esps activated, please check that they don't conflict. - - + + Create Mod... - + A mod with this name already exists - + Continue? - + The versioning scheme decides which version is considered newer than another. This function will guess the versioning scheme under the assumption that the installed version is outdated. - - + + Sorry - + I don't know a versioning scheme where %1 is newer than %2. - + <All> <Todos> - + <Checked> <Marcado> - + Plugin "%1" failed - + failed to init plugin %1: %2 - + The following plugins could not be loaded. The reason may be missing dependencies (i.e. python) or an outdated version: - + <Unchecked> <Desmarcado> - + <Update> <Actualizacion> - + <No category> - + <Conflicted> - + <Not Endorsed> - + failed to rename mod: %1 fallo al cambiar el nombre al mod: %1 - + Overwrite? - + This will replace the existing mod "%1". Continue? - + failed to remove mod "%1" - - - + + + failed to rename "%1" to "%2" - - - - + + + + Confirm Confirma - + Remove the following mods?<br><ul>%1</ul> - + failed to remove mod: %1 - - + + Failed - + Installation file no longer exists - + Mods installed with old versions of MO can't be reinstalled in this way. - - + + You need to be logged in with Nexus to endorse - - + + Extract BSA - + This mod contains at least one BSA. Do you want to unpack it? (This removes the BSA after completion. If you don't know about BSAs, just select no) - - - + + + failed to read %1: %2 - - + + This archive contains invalid hashes. Some files may be broken. - + Nexus ID for this Mod is unknown @@ -2220,92 +2230,92 @@ This function will guess the versioning scheme under the assumption that the ins Selecciona Prioridad - + Really enable all visible mods? - + Really disable all visible mods? - + Choose what to export - + Everything - + All installed mods are included in the list - + Active Mods - + Only active (checked) mods from your current profile are included - + Visible - + All mods visible in the mod list are included - + export failed: %1 - + Install Mod... Instalar Mod... - + Enable all visible Activar todos los visibles - + Disable all visible Desactivar todos los visibles - + Check all for update Buscar Actualizaciones - + Export to csv... - + Sync to Mods... - + Restore Backup - + Remove Backup... @@ -2314,362 +2324,362 @@ This function will guess the versioning scheme under the assumption that the ins Definir Categoria - + Primary Category - + Change versioning scheme - + Un-ignore update - + Ignore update - + Rename Mod... Cambiar nombre... - + Remove Mod... Quitar Mod... - + Reinstall Mod - + Un-Endorse - - + + Endorse - + Won't endorse - + Endorsement state unknown - + Ignore missing data - + Visit on Nexus - + Open in explorer - + Information... Informacion... - - + + Exception: - - + + Unknown exception - + <Multiple> - + Really delete "%1"? - + Fix Mods... Activar Mods faltantes... - + Delete - - + + failed to remove %1 Fallo eliminando %1 - - + + failed to create %1 - + Can't change download directory while downloads are in progress! - + Download failed - + failed to write to file %1 Fallo de escritura en el fichero %1 - + %1 written %1 escrito - + Select binary - + Binary Fichero - + Enter Name - + Please enter a name for the executable - + Not an executable - + This is not a recognized executable. - - + + Replace file? - + There already is a hidden version of this file. Replace it? - - + + File operation failed - - + + Failed to remove "%1". Maybe you lack the required file permissions? - + There already is a visible version of this file. Replace it? - + file not found: %1 fichero no encontrado: %1 - + failed to generate preview for %1 - + Sorry, can't preview anything. This function currently does not support extracting from bsas. - + Update available Disponible actualización - + Open/Execute - + Add as Executable - + Preview - + Un-Hide - + Hide - + Write To File... Escribir al fichero... - + Do you want to endorse Mod Organizer on %1 now? - + Remove - + Unlock load order - + Lock load order - + BOSS working - + failed to run boss: %1 - + Request to Nexus failed: %1 - + Executable "%1" not found - + Failed to refresh list of esps: %1 - + This will move all files from overwrite into a new, regular mod. Please enter a name: - + Add/Remove Categories - + Replace Categories - - + + login successful - + login failed: %1. Trying to download anyway - + login failed: %1 - + login failed: %1. You need to log-in with Nexus to update MO. - + Error Error - + failed to extract %1 (errorcode %2) - + Extract... - + Edit Categories... - + Enable all - + Disable all @@ -4272,69 +4282,69 @@ Activar Mods faltantes en el menu contextual del raton intentara activar los mod PluginList - + Mod Index - + Flags - - + + unknown - + Name of your mods - + Load priority of your mod. The higher, the more "important" it is and thus overwrites data from plugins with lower priority. - + The modindex determins the formids of objects originating from this mods. - + failed to update esp info for file %1 (source id: %2), error: %3 - + esp not found: %1 ESP no encontrado: %1 - - + + Confirm Confirma - + Really enable all plugins? - + Really disable all plugins? - + The file containing locked plugin indices is broken - + Some of your plugins have invalid names! These plugins can not be loaded by the game. Please see mo_interface.log for a list of affected plugins and rename them. @@ -4344,17 +4354,17 @@ Activar Mods faltantes en el menu contextual del raton intentara activar los mod - + Missing Masters - + Enabled Masters - + failed to restore load order for %1 @@ -4367,17 +4377,17 @@ Activar Mods faltantes en el menu contextual del raton intentara activar los mod max - + This plugin can't be disabled (enforced by the game) - + Origin: %1 - + Name Nombre @@ -4386,7 +4396,7 @@ Activar Mods faltantes en el menu contextual del raton intentara activar los mod Nombres de tus Mods - + Priority Prioridad @@ -4979,7 +4989,7 @@ p, li { white-space: pre-wrap; } "%1" no encontrado - + Please use "Help" from the toolbar to get usage instructions to all elements Por favor utilice "Ayuda" en la barra superior para obtener informacion sobre todos los elementos @@ -4992,19 +5002,19 @@ p, li { white-space: pre-wrap; } prioridad invalida %1 - + failed to find "%1" fallo al encontrar %1 - - + + <Manage...> <Definir...> - + failed to parse profile %1: %2 no se pudo analizar el perfil % 1: %2 @@ -5017,12 +5027,12 @@ p, li { white-space: pre-wrap; } Esto tomara un momento - + failed to access %1 Fallo al acceder %1 - + failed to set file time %1 Fallo al definir la hora al fihcero %1 diff --git a/src/organizer_fr.ts b/src/organizer_fr.ts index c283cb71..462e1699 100644 --- a/src/organizer_fr.ts +++ b/src/organizer_fr.ts @@ -1541,11 +1541,21 @@ BSAs checked here are loaded in such a way that your installation order is obeye Data DATA + + + Plugins + + Sort + + + Archives + + refresh data-directory overview @@ -1558,8 +1568,8 @@ BSAs checked here are loaded in such a way that your installation order is obeye - - + + Refresh Actualiser @@ -1739,7 +1749,7 @@ p, li { white-space: pre-wrap; } - + Update @@ -1750,7 +1760,7 @@ p, li { white-space: pre-wrap; } - + No Problems @@ -1780,148 +1790,143 @@ Right now this has very limited functionality - + Endorse Mod Organizer - + Toolbar - + Desktop - + Start Menu - + Problems - + There are potential problems with your setup - + Everything seems to be in order - + Help on UI - + Documentation Wiki - + Report Issue - + Tutorials - + failed to save load order: %1 impossible d'enregistrer l'ordre de chargement: %1 - - failed to save archives order, do you have write access to "%1"? - - - - + About - + About Qt - + Name Nom - + Please enter a name for the new profile Veuillez inscrire un nom pour le nouveau profil - + failed to create profile: %1 impossible de créer le profil: %1 - + Show tutorial? - + You are starting Mod Organizer for the first time. Do you want to show a tutorial of its basic features? If you choose no you can always start the tutorial from the "Help"-menu. - + Downloads in progress Téléchargements en cours - + There are still downloads in progress, do you really want to quit? Il encore des téléchargements en cours, voulez-vous vraiment quitter? - + failed to read savegame: %1 impossible de lire la sauvegarde: %1 - + Plugin "%1" failed: %2 - + Plugin error - + It appears the plugin "%1" failed to load last startup and caused MO to crash. Do you want to disable it? (Please note: If this is the first time you see this message for this plugin you may want to give it another try. The plugin may be able to recover from the problem) - + Failed to start "%1" - + Waiting Attente - + Please press OK once you're logged into steam. Veuillez cliquer OK une fois connecté à steam. @@ -1930,330 +1935,335 @@ Right now this has very limited functionality "%1" introuvable - + Start Steam? - + Steam is required to be running already to correctly start the game. Should MO try to start steam now? - + Also in: <br> - + No conflict Aucun conflit - + <Edit...> <Modifier...> - + This bsa is enabled in the ini file so it may be required! - + This archive will still be loaded since there is a plugin of the same name but its files will not follow installation order! - + Activating Network Proxy - - + + Installation successful Installation réussie - - + + Configure Mod Configurer mod - - + + This mod contains ini tweaks. Do you want to configure them now? Ce mod contient des ajustement pour les fichiers ini. Désirez-vous les configurer maintenant? - - + + mod "%1" not found - - + + Installation cancelled - - + + The mod was not installed completely. - + Some plugins could not be loaded - + Too many esps and esms enabled - - + + Description missing - + The game doesn't allow more than 255 active plugins (including the official ones) to be loaded. You have to disable some unused plugins or merge some plugins into one. You can find a guide here: <a href="http://wiki.step-project.com/Guide:Merging_Plugins">http://wiki.step-project.com/Guide:Merging_Plugins</a> - + Choose Mod Choisir mod - + Mod Archive Archive de mod - + Start Tutorial? - + You're about to start a tutorial. For technical reasons it's not possible to end the tutorial early. Continue? - - + + Download started Téléchargement commencé - + failed to update mod list: %1 impossible de mettre à jour la liste de mods: %1 - + failed to spawn notepad.exe: %1 impossible de lancer notepad.exe: %1 - + failed to open %1 impossible d'ouvrir %1 - + failed to change origin name: %1 impossible de changer le nom d'origine: %1 - + + failed to move "%1" from mod "%2" to "%3": %4 + + + + Multiple esps activated, please check that they don't conflict. - - + + Create Mod... - + A mod with this name already exists - + Continue? - + The versioning scheme decides which version is considered newer than another. This function will guess the versioning scheme under the assumption that the installed version is outdated. - - + + Sorry - + I don't know a versioning scheme where %1 is newer than %2. - + <All> <Tous> - + <Checked> <Cochés> - + Plugin "%1" failed - + failed to init plugin %1: %2 - + The following plugins could not be loaded. The reason may be missing dependencies (i.e. python) or an outdated version: - + <Unchecked> <Décochés> - + <Update> <Rafraichir> - + <No category> - + <Conflicted> - + <Not Endorsed> - + failed to rename mod: %1 impossible de renommer le mod: %1 - + Overwrite? - + This will replace the existing mod "%1". Continue? - + failed to remove mod "%1" - - - + + + failed to rename "%1" to "%2" - - - - + + + + Confirm Confirmer - + Remove the following mods?<br><ul>%1</ul> - + failed to remove mod: %1 - - + + Failed - + Installation file no longer exists - + Mods installed with old versions of MO can't be reinstalled in this way. - - + + You need to be logged in with Nexus to endorse - - + + Extract BSA - + This mod contains at least one BSA. Do you want to unpack it? (This removes the BSA after completion. If you don't know about BSAs, just select no) - - - + + + failed to read %1: %2 Échec de lecture %1: %2 - - + + This archive contains invalid hashes. Some files may be broken. - + Nexus ID for this Mod is unknown @@ -2266,92 +2276,92 @@ This function will guess the versioning scheme under the assumption that the ins Choisir priorité - + Really enable all visible mods? - + Really disable all visible mods? - + Choose what to export - + Everything - + All installed mods are included in the list - + Active Mods - + Only active (checked) mods from your current profile are included - + Visible - + All mods visible in the mod list are included - + export failed: %1 - + Install Mod... Installer mod... - + Enable all visible Activer tous les mods visibles - + Disable all visible Désactiver tous les mods visibles - + Check all for update Vérifier toutes les mises à jour - + Export to csv... - + Sync to Mods... - + Restore Backup - + Remove Backup... @@ -2360,362 +2370,362 @@ This function will guess the versioning scheme under the assumption that the ins Assigner catégorie - + Primary Category - + Change versioning scheme - + Un-ignore update - + Ignore update - + Rename Mod... Renommer mod... - + Remove Mod... Supprimer mod... - + Reinstall Mod - + Un-Endorse - - + + Endorse - + Won't endorse - + Endorsement state unknown - + Ignore missing data - + Visit on Nexus - + Open in explorer - + Information... Information... - - + + Exception: - - + + Unknown exception - + <Multiple> - + Really delete "%1"? - + Fix Mods... Réparer mods... - + Delete - - + + failed to remove %1 Impossible de supprimer %1 - - + + failed to create %1 impossible de créer %1 - + Can't change download directory while downloads are in progress! - + Download failed - + failed to write to file %1 impossible d'écrire dans le fichier %1 - + %1 written %1 écrit - + Select binary - + Binary Programme - + Enter Name - + Please enter a name for the executable - + Not an executable - + This is not a recognized executable. - - + + Replace file? - + There already is a hidden version of this file. Replace it? - - + + File operation failed - - + + Failed to remove "%1". Maybe you lack the required file permissions? - + There already is a visible version of this file. Replace it? - + file not found: %1 fichier introuvable: %1 - + failed to generate preview for %1 - + Sorry, can't preview anything. This function currently does not support extracting from bsas. - + Update available Mise à jour disponible - + Open/Execute - + Add as Executable - + Preview - + Un-Hide - + Hide - + Write To File... Écriture du fichier... - + Do you want to endorse Mod Organizer on %1 now? - + Remove Supprimer - + Unlock load order - + Lock load order - + BOSS working - + failed to run boss: %1 - + Request to Nexus failed: %1 - + Executable "%1" not found - + Failed to refresh list of esps: %1 - + This will move all files from overwrite into a new, regular mod. Please enter a name: - + Add/Remove Categories - + Replace Categories - - + + login successful - + login failed: %1. Trying to download anyway - + login failed: %1 - + login failed: %1. You need to log-in with Nexus to update MO. - + Error Erreur - + failed to extract %1 (errorcode %2) - + Extract... - + Edit Categories... - + Enable all - + Disable all @@ -4391,69 +4401,69 @@ p, li { white-space: pre-wrap; } PluginList - + Mod Index - + Flags - - + + unknown - + Name of your mods - + Load priority of your mod. The higher, the more "important" it is and thus overwrites data from plugins with lower priority. - + The modindex determins the formids of objects originating from this mods. - + failed to update esp info for file %1 (source id: %2), error: %3 - + esp not found: %1 ESP introuvable: %1 - - + + Confirm Confirmer - + Really enable all plugins? - + Really disable all plugins? - + The file containing locked plugin indices is broken - + Some of your plugins have invalid names! These plugins can not be loaded by the game. Please see mo_interface.log for a list of affected plugins and rename them. @@ -4463,17 +4473,17 @@ p, li { white-space: pre-wrap; } - + Missing Masters - + Enabled Masters - + failed to restore load order for %1 @@ -4486,17 +4496,17 @@ p, li { white-space: pre-wrap; } max - + This plugin can't be disabled (enforced by the game) - + Origin: %1 - + Name Nom @@ -4505,7 +4515,7 @@ p, li { white-space: pre-wrap; } Les noms de vos mods - + Priority Priorité @@ -5088,34 +5098,34 @@ p, li { white-space: pre-wrap; } "%1" introuvable - + Please use "Help" from the toolbar to get usage instructions to all elements Veuillez utiliser l'aide dans la barre d'outil pour obtenir des instructions à propos de tous les éléments - - + + <Manage...> <Gérer...> - + failed to parse profile %1: %2 impossible d'analyser le profil %1: %2 - + failed to find "%1" impossible de trouver "%1" - + failed to access %1 impossible d'accéder à %1 - + failed to set file time %1 impossible de changer la date du fichier %1 diff --git a/src/organizer_ru.ts b/src/organizer_ru.ts index 2a75ce82..0fc6ecea 100644 --- a/src/organizer_ru.ts +++ b/src/organizer_ru.ts @@ -1375,6 +1375,11 @@ p, li { white-space: pre-wrap; } Shortcut Ярлык + + + Plugins + Плагины + List of available esp/esm files @@ -1398,6 +1403,11 @@ p, li { white-space: pre-wrap; } Sort + + + Archives + + List of available BS Archives. Archives not checked here are not managed by MO and ignore installation order. @@ -1451,8 +1461,8 @@ BSA, отмеченные здесь, загружаются так, чтобы - - + + Refresh Обновить @@ -1632,7 +1642,7 @@ p, li { white-space: pre-wrap; } - + Update Обновление @@ -1643,7 +1653,7 @@ p, li { white-space: pre-wrap; } - + No Problems Проблем не обнаружено @@ -1676,158 +1686,157 @@ Right now this has very limited functionality - + Endorse Mod Organizer Одобрить Mod Organizer - + Toolbar Панель инструментов - + Desktop Рабочий стол - + Start Menu Меню Пуск - + Problems Проблемы - + There are potential problems with your setup Есть возможные проблемы с вашей установкой - + Everything seems to be in order Кажется всё в порядке - + Help on UI Справка по интерфейсу - + Documentation Wiki Wiki-документация - + Report Issue Сообщить о проблеме - + Tutorials Уроки - + About - + About Qt - failed to save archives order, do you have write access to "%1"? - не удалось сохранить порядок архивов, у вас имеется доступ на запись к "%1"? + не удалось сохранить порядок архивов, у вас имеется доступ на запись к "%1"? - + failed to save load order: %1 не удалось сохранить порядок загрузки: %1 - + Name Имя - + Please enter a name for the new profile Введите имя нового профиля - + failed to create profile: %1 не удалось создать профиль: %1 - + Show tutorial? Показать урок? - + You are starting Mod Organizer for the first time. Do you want to show a tutorial of its basic features? If you choose no you can always start the tutorial from the "Help"-menu. Вы запустили Mod Organizer впервые. Вы хотите просмотреть уроки по основным возможностям? В случае отказа вы всегда можете открыть уроки из меню "Помощь". - + Downloads in progress Загрузки в процессе - + There are still downloads in progress, do you really want to quit? Загрузки всё ещё в процессе, вы правда хотите выйти? - + failed to read savegame: %1 не удалось прочесть сохранение: %1 - + Plugin "%1" failed: %2 Плагин "%1" не удалось: %2 - + Plugin "%1" failed Плагин "%1" не удалось - + failed to init plugin %1: %2 не удалось инициализировать плагин %1: %2 - + Plugin error - + It appears the plugin "%1" failed to load last startup and caused MO to crash. Do you want to disable it? (Please note: If this is the first time you see this message for this plugin you may want to give it another try. The plugin may be able to recover from the problem) - + Failed to start "%1" Не удалось запустить "%1" - + Waiting Ожидание - + Please press OK once you're logged into steam. Нажмите OK как только вы войдете в Steam. @@ -1836,83 +1845,83 @@ Right now this has very limited functionality "%1" не найден - + Start Steam? Запустить Steam? - + Steam is required to be running already to correctly start the game. Should MO try to start steam now? Требуется запущенный Steam, для корректного запуска игры. Должен ли MO попытаться запустить Steam сейчас? - + Also in: <br> Также в: <br> - + No conflict Конфликтов нет - + <Edit...> <Правка...> - + This bsa is enabled in the ini file so it may be required! Этот bsa подключен через ini, так что он может быть необходим! - + This archive will still be loaded since there is a plugin of the same name but its files will not follow installation order! Этот архив все равно будет загружен, так как есть плагин с одноименным названием, но его файлы не будут следовать порядку установки! - + Activating Network Proxy Подключение сетевого прокси - - + + Installation successful Установка завершена - - + + Configure Mod Настройка мода - - + + This mod contains ini tweaks. Do you want to configure them now? Этот мод включает настройки ini. Вы хотите настроить их сейчас? - - + + mod "%1" not found мод "%1" не найден - - + + Installation cancelled Установка отменена - - + + The mod was not installed completely. Мод не был установлен полностью. - + Some plugins could not be loaded Некоторые плагины не могут быть загружены @@ -1921,203 +1930,203 @@ Right now this has very limited functionality Следующие плагины не могут быть загружены. Причина может быть в отсутствующих зависимостях (таких как python) или в устаревшей версии:<ul> - + Too many esps and esms enabled - - + + Description missing - + The following plugins could not be loaded. The reason may be missing dependencies (i.e. python) or an outdated version: - + Choose Mod Выберете мод - + Mod Archive Архив мода - + Start Tutorial? Начать обучение? - + You're about to start a tutorial. For technical reasons it's not possible to end the tutorial early. Continue? Вы собираетесь открыть обучение. По техническим причинам будет невозможно досрочно закончить обучение. Продолжить? - - + + Download started Загрузка начата - + failed to update mod list: %1 не удалось обновить список модов: %1 - + failed to spawn notepad.exe: %1 не удалось вызвать notepad.exe: %1 - + failed to open %1 не удалось открыть %1 - + failed to change origin name: %1 не удалось изменить оригинальное имя: %1 - + <Checked> <Подключен> - + <Unchecked> <Отключен> - + <Update> <Обновлен> - + <No category> <Без категории> - + <Conflicted> <Конфликтует> - + <Not Endorsed> - + failed to rename mod: %1 не удалось переименовать мод: %1 - + Overwrite? Перезаписать? - + This will replace the existing mod "%1". Continue? Это заменит существующий мод "%1". Продолжить? - + failed to remove mod "%1" не удалось удалить мод "%1" - - - + + + failed to rename "%1" to "%2" не удалось переименовать "%1" в "%2" - + Multiple esps activated, please check that they don't conflict. Подключено несколько esp, выберете из них не конфликтующие. - - - - + + + + Confirm Подтверждение - + Remove the following mods?<br><ul>%1</ul> Удалить следующие моды?<br><ul>%1</ul> - + failed to remove mod: %1 не удалось удалить мод: %1 - - + + Failed Неудача - + Installation file no longer exists Установочный файл больше не существует - + Mods installed with old versions of MO can't be reinstalled in this way. Моды, установленные с использованием старых версий MO не могут быть перестановленны таким образом. - - + + You need to be logged in with Nexus to endorse Вы должны быть авторизированы на Nexus, чтобы одобрять. - - + + Extract BSA Извлечь BSA - + This mod contains at least one BSA. Do you want to unpack it? (This removes the BSA after completion. If you don't know about BSAs, just select no) Этот мод включает как минимум один BSA. Вы хотите распаковать их? (Это удалит BSA после завершения. Если вы не знаете ничего о BSAs, просто откажитесь) - - - + + + failed to read %1: %2 не удалось прочесть %1: %2 - - + + This archive contains invalid hashes. Some files may be broken. Архив содержит неверные хеши. Некоторые файлы могут быть испорчены. - + Nexus ID for this Mod is unknown Nexus ID для этого мода неизвестен - - + + Create Mod... Создать мод... @@ -2128,119 +2137,119 @@ Please enter a name: Пожалуйста, введите имя: - + A mod with this name already exists Мод с таким именем уже существует - + Continue? - + The versioning scheme decides which version is considered newer than another. This function will guess the versioning scheme under the assumption that the installed version is outdated. - - + + Sorry - + I don't know a versioning scheme where %1 is newer than %2. - + Really enable all visible mods? Действительно подключить все видимые моды? - + Really disable all visible mods? Действительно отключить все видимые моды? - + Choose what to export Выберете, что экспортировать - + Everything Всё - + All installed mods are included in the list Все установленные моды, включенные в список - + Active Mods Активные моды - + Only active (checked) mods from your current profile are included Включены все активные (подключенные) моды вашего текущего профиля - + Visible Видимые - + All mods visible in the mod list are included Включены все моды, видимые в списке модов - + export failed: %1 экспорт не удался: %1 - + Install Mod... Установить мод... - + Enable all visible Включить все видимые - + Disable all visible Отключить все видимые - + Check all for update Проверить все на обновления - + Export to csv... Экспорт в csv... - + Sync to Mods... Синхронизировать с модами... - + Restore Backup Восстановить из резервной копии - + Remove Backup... Удалить резервную копию... @@ -2249,372 +2258,377 @@ This function will guess the versioning scheme under the assumption that the ins Задать категорию - + Executable "%1" not found - + Failed to refresh list of esps: %1 - + The game doesn't allow more than 255 active plugins (including the official ones) to be loaded. You have to disable some unused plugins or merge some plugins into one. You can find a guide here: <a href="http://wiki.step-project.com/Guide:Merging_Plugins">http://wiki.step-project.com/Guide:Merging_Plugins</a> - + + failed to move "%1" from mod "%2" to "%3": %4 + + + + This will move all files from overwrite into a new, regular mod. Please enter a name: - + Add/Remove Categories - + Replace Categories - + Primary Category Основная категория - + Change versioning scheme - + Un-ignore update - + Ignore update - + Rename Mod... Переименовать мод... - + Remove Mod... Удалить мод... - + Reinstall Mod Переустановить мод - + Un-Endorse Отменить одобрение - - + + Endorse Одобрить - + Won't endorse Не одобрять - + Endorsement state unknown Статус одобрения неизвестен - + Ignore missing data Игнорировать отсутствующие данные - + Visit on Nexus Перейти на Nexus - + Open in explorer Открыть в проводнике - + Information... Информация... - - + + Exception: Исключение: - - + + Unknown exception Неизвестное исключение - + <All> <Все> - + <Multiple> <Несколько> - + Really delete "%1"? - + Fix Mods... Исправить моды... - + Delete Удалить - - + + failed to remove %1 не удалось удалить %1 - - + + failed to create %1 не удалось создать %1 - + Can't change download directory while downloads are in progress! Нельзя изменить каталог для загрузок, когда загрузки ещё не завершены! - + Download failed Загрузка не удалась - + failed to write to file %1 ошибка записи в файл %1 - + %1 written %1 записан - + Select binary Выбрать исполняемый файл - + Binary Исполняемый файл - + Enter Name Введите имя - + Please enter a name for the executable Введите название для программы - + Not an executable Не является исполняемым - + This is not a recognized executable. Это неверный исполняемый файл. - - + + Replace file? Заменить файл? - + There already is a hidden version of this file. Replace it? Уже существует скрытая версия этого файла. Заменить? - - + + File operation failed Операция с файлом не удалась - - + + Failed to remove "%1". Maybe you lack the required file permissions? Не удалось удалить "%1". Может быть, вам не хватает необходимых прав доступа к файлу? - + There already is a visible version of this file. Replace it? Видимая версия этого файла уже существует. Заменить? - + file not found: %1 - + failed to generate preview for %1 - + Sorry, can't preview anything. This function currently does not support extracting from bsas. - + Update available Доступно обновление - + Open/Execute Открыть/Выполнить - + Add as Executable Добавить как исполняемый - + Preview - + Un-Hide Показать - + Hide Скрыть - + Write To File... Записать в файл... - + Do you want to endorse Mod Organizer on %1 now? Вы хотите одобрить Mod Organizer на %1 сейчас? - + Request to Nexus failed: %1 Запрос на Nexus не удался: %1 - - + + login successful успешный вход - + login failed: %1. Trying to download anyway вход не удался: %1. Пытаюсь загрузить всё равно - + login failed: %1 войти не удалось: %1 - + login failed: %1. You need to log-in with Nexus to update MO. войти не удалось: %1. Вам нужно войти на Nexus, чтобы обновить MO. - + Error Ошибка - + failed to extract %1 (errorcode %2) не удалось извлечь %1 (код ошибки %2) - + Extract... Распаковка... - + Edit Categories... Изменить категории... - + Remove - + Enable all Включить все - + Disable all Отключить все - + Unlock load order Разблокировать порядок загрузки - + Lock load order Заблокировать порядок загрузки - + BOSS working - + failed to run boss: %1 @@ -3539,74 +3553,74 @@ p, li { white-space: pre-wrap; } PluginList - + Name Имя - + Priority Приоритет - + Mod Index Индекс мода - + Flags Флаги - - + + unknown неизвестно - + Name of your mods Имена ваших модов - + Load priority of your mod. The higher, the more "important" it is and thus overwrites data from plugins with lower priority. Приоритет загрузки ваших модов. Моды с большим приоритетом перезапишут данные модов с меньшим приоритетом. - + The modindex determins the formids of objects originating from this mods. Индекс мода определяющий id форм объектов, происходящих из этих модов. - + failed to update esp info for file %1 (source id: %2), error: %3 - + esp not found: %1 esp не найден: %1 - - + + Confirm - + Really enable all plugins? - + Really disable all plugins? - + The file containing locked plugin indices is broken Файл, содержащий индексы заблокированного плагина, не работает. @@ -3615,7 +3629,7 @@ p, li { white-space: pre-wrap; } не удалось открыть выходной файл: %1 - + Some of your plugins have invalid names! These plugins can not be loaded by the game. Please see mo_interface.log for a list of affected plugins and rename them. Некоторые из ваших плагинов имеют неверные имена. Эти плагины не могут быть загружены игрой. Смотрите mo_interface.log для получения списка таких плагинов и переименуйте их. @@ -3625,27 +3639,27 @@ p, li { white-space: pre-wrap; } - + This plugin can't be disabled (enforced by the game) Этот плагин не может быть отключен (грузится игрой принудительно) - + Origin: %1 Источник: %1 - + Missing Masters - + Enabled Masters - + failed to restore load order for %1 не удалось восстановить порядок загрузки для %1 @@ -4226,23 +4240,23 @@ p, li { white-space: pre-wrap; } - + Please use "Help" from the toolbar to get usage instructions to all elements Используйте пункт "Справка" на панели инструментов, чтобы получить инструкции по использованию всех элементов. - - + + <Manage...> <Управлять...> - + failed to parse profile %1: %2 не удалось обработать профиль %1: %2 - + failed to find "%1" не удалось найти "%1" @@ -4252,12 +4266,12 @@ p, li { white-space: pre-wrap; } ошибка кодирования, пожалуйста сообщите об этом баге, включив файл mo_interface.log! - + failed to access %1 не удалось получить доступ к %1 - + failed to set file time %1 не удалось изменить дату модификации для %1 diff --git a/src/organizer_tr.ts b/src/organizer_tr.ts index 58d30d00..d091ad76 100644 --- a/src/organizer_tr.ts +++ b/src/organizer_tr.ts @@ -1583,11 +1583,21 @@ BSAs checked here are loaded in such a way that your installation order is obeye Data + + + Plugins + + Sort + + + Archives + + refresh data-directory overview @@ -1600,8 +1610,8 @@ BSAs checked here are loaded in such a way that your installation order is obeye - - + + Refresh @@ -1775,7 +1785,7 @@ p, li { white-space: pre-wrap; } - + Update @@ -1786,7 +1796,7 @@ p, li { white-space: pre-wrap; } - + No Problems @@ -1816,926 +1826,926 @@ Right now this has very limited functionality - + Endorse Mod Organizer - + Toolbar - + Desktop - + Start Menu - + Problems - + There are potential problems with your setup - + Everything seems to be in order - + Help on UI - + Documentation Wiki - + Report Issue - + Tutorials - + failed to save load order: %1 - - failed to save archives order, do you have write access to "%1"? - - - - + About - + About Qt - + Name İsim - + Please enter a name for the new profile - + failed to create profile: %1 - + Show tutorial? - + You are starting Mod Organizer for the first time. Do you want to show a tutorial of its basic features? If you choose no you can always start the tutorial from the "Help"-menu. - + Downloads in progress - + There are still downloads in progress, do you really want to quit? - + failed to read savegame: %1 - + Plugin "%1" failed: %2 - + Failed to start "%1" - + Waiting - + Please press OK once you're logged into steam. - + Start Steam? - + Steam is required to be running already to correctly start the game. Should MO try to start steam now? - + Also in: <br> - + No conflict - + <Edit...> - + Failed to refresh list of esps: %1 - + This bsa is enabled in the ini file so it may be required! - + This archive will still be loaded since there is a plugin of the same name but its files will not follow installation order! - + Activating Network Proxy - - + + Installation successful - - + + Configure Mod - - + + This mod contains ini tweaks. Do you want to configure them now? - - + + mod "%1" not found - - + + Installation cancelled - - + + The mod was not installed completely. - + Some plugins could not be loaded - + The game doesn't allow more than 255 active plugins (including the official ones) to be loaded. You have to disable some unused plugins or merge some plugins into one. You can find a guide here: <a href="http://wiki.step-project.com/Guide:Merging_Plugins">http://wiki.step-project.com/Guide:Merging_Plugins</a> - + Choose Mod - + Mod Archive - + Start Tutorial? - + You're about to start a tutorial. For technical reasons it's not possible to end the tutorial early. Continue? - - + + Download started - + failed to update mod list: %1 - + failed to spawn notepad.exe: %1 - + failed to open %1 - + failed to change origin name: %1 - + Multiple esps activated, please check that they don't conflict. - - + + Create Mod... - + A mod with this name already exists - + <All> - + <Checked> - + Plugin "%1" failed - + failed to init plugin %1: %2 - + Plugin error - + It appears the plugin "%1" failed to load last startup and caused MO to crash. Do you want to disable it? (Please note: If this is the first time you see this message for this plugin you may want to give it another try. The plugin may be able to recover from the problem) - + Too many esps and esms enabled - - + + Description missing - + The following plugins could not be loaded. The reason may be missing dependencies (i.e. python) or an outdated version: - + + failed to move "%1" from mod "%2" to "%3": %4 + + + + <Unchecked> - + <Update> - + <No category> - + <Conflicted> - + <Not Endorsed> - + failed to rename mod: %1 - + Overwrite? - + This will replace the existing mod "%1". Continue? - + failed to remove mod "%1" - - - + + + failed to rename "%1" to "%2" "%1"yi "%2" olarak yeniden adlandırma başarılı olamadı. - - - - + + + + Confirm Onayla - + Remove the following mods?<br><ul>%1</ul> - + failed to remove mod: %1 - - + + Failed - + Installation file no longer exists - + Mods installed with old versions of MO can't be reinstalled in this way. - - + + You need to be logged in with Nexus to endorse - - + + Extract BSA - + This mod contains at least one BSA. Do you want to unpack it? (This removes the BSA after completion. If you don't know about BSAs, just select no) - - - + + + failed to read %1: %2 %1: %2 okunamadı - - + + This archive contains invalid hashes. Some files may be broken. - + Nexus ID for this Mod is unknown - + This will move all files from overwrite into a new, regular mod. Please enter a name: - + Continue? - + The versioning scheme decides which version is considered newer than another. This function will guess the versioning scheme under the assumption that the installed version is outdated. - - + + Sorry - + I don't know a versioning scheme where %1 is newer than %2. - + Really enable all visible mods? - + Really disable all visible mods? - + Choose what to export - + Everything - + All installed mods are included in the list - + Active Mods - + Only active (checked) mods from your current profile are included - + Visible - + All mods visible in the mod list are included - + export failed: %1 - + Install Mod... - + Enable all visible - + Disable all visible - + Check all for update - + Export to csv... - + Sync to Mods... - + Restore Backup - + Remove Backup... - + Primary Category - + Change versioning scheme - + Un-ignore update - + Ignore update - + Rename Mod... - + Remove Mod... - + Reinstall Mod - + Un-Endorse - - + + Endorse - + Won't endorse - + Endorsement state unknown - + Ignore missing data - + Visit on Nexus - + Open in explorer - + Information... - - + + Exception: - - + + Unknown exception - + <Multiple> - + Really delete "%1"? - + Fix Mods... - + Delete - - + + failed to remove %1 - - + + failed to create %1 - + Can't change download directory while downloads are in progress! - + Download failed - + failed to write to file %1 - + %1 written - + Select binary - + Binary İkili değer - + Enter Name - + Please enter a name for the executable - + Not an executable - + This is not a recognized executable. - - + + Replace file? - + There already is a hidden version of this file. Replace it? - - + + File operation failed - - + + Failed to remove "%1". Maybe you lack the required file permissions? - + There already is a visible version of this file. Replace it? - + file not found: %1 - + failed to generate preview for %1 - + Sorry, can't preview anything. This function currently does not support extracting from bsas. - + Update available - + Open/Execute - + Add as Executable - + Preview - + Un-Hide - + Hide - + Write To File... - + Do you want to endorse Mod Organizer on %1 now? - + Remove - + Unlock load order - + Lock load order - + BOSS working - + failed to run boss: %1 - + Request to Nexus failed: %1 - + Executable "%1" not found - + Add/Remove Categories - + Replace Categories - - + + login successful - + login failed: %1. Trying to download anyway - + login failed: %1 - + login failed: %1. You need to log-in with Nexus to update MO. - + Error - + failed to extract %1 (errorcode %2) - + Extract... - + Edit Categories... - + Enable all - + Disable all @@ -3625,69 +3635,69 @@ p, li { white-space: pre-wrap; } PluginList - + Mod Index - + Flags - - + + unknown - + Name of your mods - + Load priority of your mod. The higher, the more "important" it is and thus overwrites data from plugins with lower priority. - + The modindex determins the formids of objects originating from this mods. - + failed to update esp info for file %1 (source id: %2), error: %3 - + esp not found: %1 - - + + Confirm Onayla - + Really enable all plugins? - + Really disable all plugins? - + The file containing locked plugin indices is broken - + Some of your plugins have invalid names! These plugins can not be loaded by the game. Please see mo_interface.log for a list of affected plugins and rename them. @@ -3697,37 +3707,37 @@ p, li { white-space: pre-wrap; } - + Missing Masters - + Enabled Masters - + failed to restore load order for %1 - + This plugin can't be disabled (enforced by the game) - + Origin: %1 - + Name İsim - + Priority @@ -4235,34 +4245,34 @@ p, li { white-space: pre-wrap; } - + Please use "Help" from the toolbar to get usage instructions to all elements - - + + <Manage...> - + failed to parse profile %1: %2 - + failed to find "%1" - + failed to access %1 - + failed to set file time %1 diff --git a/src/organizer_zh_CN.ts b/src/organizer_zh_CN.ts index 4e24267f..a5fb1bcc 100644 --- a/src/organizer_zh_CN.ts +++ b/src/organizer_zh_CN.ts @@ -1629,11 +1629,21 @@ BSAs checked here are loaded in such a way that your installation order is obeye Data Data + + + Plugins + + Sort + + + Archives + + refresh data-directory overview @@ -1646,8 +1656,8 @@ BSAs checked here are loaded in such a way that your installation order is obeye - - + + Refresh 刷新 @@ -1827,7 +1837,7 @@ p, li { white-space: pre-wrap; } - + Update 更新 @@ -1838,7 +1848,7 @@ p, li { white-space: pre-wrap; } - + No Problems 没有问题 @@ -1871,37 +1881,37 @@ Right now this has very limited functionality - + Endorse Mod Organizer - + Toolbar - + Desktop - + Start Menu - + Problems 问题 - + There are potential problems with your setup 您的安装中存在潜在的问题 - + Everything seems to be in order 一切井然有序 @@ -1918,22 +1928,22 @@ Right now this has very limited functionality <li>.Net 未安装或版本过旧。想要运行 NCC 您必须先安装 .Net,您可以在以下地址中获取: <a href="%1">%1</a></li> - + Help on UI 界面帮助 - + Documentation Wiki 说明文档 (维基) - + Report Issue 报告问题 - + Tutorials @@ -1942,93 +1952,92 @@ Right now this has very limited functionality 无法保存加载顺序 - + failed to save load order: %1 无法保存加载顺序: %1 - failed to save archives order, do you have write access to "%1"? - 无法保存档案顺序,您确定您有权限更改 "%1"? + 无法保存档案顺序,您确定您有权限更改 "%1"? - + About - + About Qt - + Name 名称 - + Please enter a name for the new profile - + failed to create profile: %1 无法创建配置文件: %1 - + Show tutorial? - + You are starting Mod Organizer for the first time. Do you want to show a tutorial of its basic features? If you choose no you can always start the tutorial from the "Help"-menu. - + Downloads in progress 正在下载 - + There are still downloads in progress, do you really want to quit? 仍有正在进行中的下载,您确定要退出吗? - + failed to read savegame: %1 无法读取存档: %1 - + Plugin "%1" failed: %2 - + Plugin error - + It appears the plugin "%1" failed to load last startup and caused MO to crash. Do you want to disable it? (Please note: If this is the first time you see this message for this plugin you may want to give it another try. The plugin may be able to recover from the problem) - + Failed to start "%1" 无法启动 "%1" - + Waiting 稍等 - + Please press OK once you're logged into steam. 当您登录 Steam 时请点击确定。 @@ -2037,331 +2046,336 @@ Right now this has very limited functionality "%1" 未找到 - + Start Steam? 启动 Steam? - + Steam is required to be running already to correctly start the game. Should MO try to start steam now? 想要正确地启动遊戲,Steam 必须处于运行状态。MO 要立即启动 Steam 吗? - + Also in: <br> 也在: <br> - + No conflict 没有冲突 - + <Edit...> <编辑...> - + This bsa is enabled in the ini file so it may be required! 该 BSA 已在 Ini 文件中启用,因此它可能是必需的。 - + This archive will still be loaded since there is a plugin of the same name but its files will not follow installation order! 此档案还是会被加载,因为存在同名插件。不过它所包含的的文件不会遵循安装顺序! - + Activating Network Proxy - - + + Installation successful 安装成功 - - + + Configure Mod 配置 Mod - - + + This mod contains ini tweaks. Do you want to configure them now? 此 Mod 中包含 Ini 设定文件,您想现在就对它们进行配置吗? - - + + mod "%1" not found Mod "%1" 未找到 - - + + Installation cancelled 安装已取消 - - + + The mod was not installed completely. Mod 没有完全安装。 - + Some plugins could not be loaded - + Too many esps and esms enabled - - + + Description missing - + The game doesn't allow more than 255 active plugins (including the official ones) to be loaded. You have to disable some unused plugins or merge some plugins into one. You can find a guide here: <a href="http://wiki.step-project.com/Guide:Merging_Plugins">http://wiki.step-project.com/Guide:Merging_Plugins</a> - + Choose Mod 选择 Mod - + Mod Archive Mod 压缩包 - + Start Tutorial? - + You're about to start a tutorial. For technical reasons it's not possible to end the tutorial early. Continue? - - + + Download started 开始下载 - + failed to update mod list: %1 无法更新 Mod 列表: %1 - + failed to spawn notepad.exe: %1 无法生成 notepad.exe: %1 - + failed to open %1 无法打开 %1 - + failed to change origin name: %1 无法更改原始文件名: %1 - + + failed to move "%1" from mod "%2" to "%3": %4 + + + + Multiple esps activated, please check that they don't conflict. - - + + Create Mod... - + A mod with this name already exists - + Continue? - + The versioning scheme decides which version is considered newer than another. This function will guess the versioning scheme under the assumption that the installed version is outdated. - - + + Sorry - + I don't know a versioning scheme where %1 is newer than %2. - + <All> <全部> - + <Checked> <已勾选> - + Plugin "%1" failed - + failed to init plugin %1: %2 - + The following plugins could not be loaded. The reason may be missing dependencies (i.e. python) or an outdated version: - + <Unchecked> <未勾选> - + <Update> <有更新> - + <No category> <无类别> - + <Conflicted> - + <Not Endorsed> - + failed to rename mod: %1 无法重命名 Mod: %1 - + Overwrite? - + This will replace the existing mod "%1". Continue? - + failed to remove mod "%1" - - - + + + failed to rename "%1" to "%2" - - - - + + + + Confirm 确认 - + Remove the following mods?<br><ul>%1</ul> - + failed to remove mod: %1 无法移动 Mod: %1 - - + + Failed 失败 - + Installation file no longer exists 安装文件不复存在 - + Mods installed with old versions of MO can't be reinstalled in this way. 旧版 MO 安装的 Mod 无法使用此方法重新安装。 - - + + You need to be logged in with Nexus to endorse - - + + Extract BSA 解压 BSA - + This mod contains at least one BSA. Do you want to unpack it? (This removes the BSA after completion. If you don't know about BSAs, just select no) 此 Mod 中至少包含一个 BSA。您确定要解压吗? (解压完成后,BSA 文件将会被删除。如果您不了解 BSA 的话,请选择“否”) - - - + + + failed to read %1: %2 无法读取 %1: %2 - - + + This archive contains invalid hashes. Some files may be broken. 压缩包 Hash 值错误。部分文件可能已经损坏。 - + Nexus ID for this Mod is unknown 此 Mod 的N网 ID 未知 @@ -2374,92 +2388,92 @@ This function will guess the versioning scheme under the assumption that the ins 选择优先级 - + Really enable all visible mods? 确定要启用全部可见的 Mod 吗? - + Really disable all visible mods? 确定要禁用全部可见的 Mod 吗? - + Choose what to export - + Everything - + All installed mods are included in the list - + Active Mods - + Only active (checked) mods from your current profile are included - + Visible - + All mods visible in the mod list are included - + export failed: %1 - + Install Mod... 安装 Mod... - + Enable all visible 启用所有可见项目 - + Disable all visible 禁用所有可见项目 - + Check all for update 检查更新 - + Export to csv... - + Sync to Mods... 同步到 Mod... - + Restore Backup - + Remove Backup... @@ -2468,362 +2482,362 @@ This function will guess the versioning scheme under the assumption that the ins 设置类别 - + Primary Category - + Change versioning scheme - + Un-ignore update - + Ignore update - + Rename Mod... 重命名... - + Remove Mod... 移除 Mod... - + Reinstall Mod 重新安装 Mod - + Un-Endorse - - + + Endorse - + Won't endorse - + Endorsement state unknown - + Ignore missing data - + Visit on Nexus 在N网上浏览 - + Open in explorer 在资源管理器中打开 - + Information... 信息... - - + + Exception: 例外: - - + + Unknown exception 未知的例外 - + <Multiple> - + Really delete "%1"? - + Fix Mods... 修复 Mod... - + Delete - - + + failed to remove %1 无法删除 %1 - - + + failed to create %1 无法创建 %1 - + Can't change download directory while downloads are in progress! 下载文件时不能修改下载目录! - + Download failed 下载失败 - + failed to write to file %1 无法写入文件 %1 - + %1 written 已写入 %1 - + Select binary 选择可执行文件 - + Binary - + Enter Name 输入名称 - + Please enter a name for the executable 请为程序输入一个名称 - + Not an executable 不是可执行程序 - + This is not a recognized executable. 无法识别的可执行文件 - - + + Replace file? 替换文件? - + There already is a hidden version of this file. Replace it? 已存在同名文件,但该文件被隐藏了。确定要覆盖吗? - - + + File operation failed 文件操作错误 - - + + Failed to remove "%1". Maybe you lack the required file permissions? - + There already is a visible version of this file. Replace it? - + file not found: %1 文件未找到: %1 - + failed to generate preview for %1 - + Sorry, can't preview anything. This function currently does not support extracting from bsas. - + Update available 更新可用 - + Open/Execute 打开/执行 - + Add as Executable 添加为可执行文件 - + Preview - + Un-Hide 取消隐藏 - + Hide 隐藏 - + Write To File... 写入文件... - + Do you want to endorse Mod Organizer on %1 now? - + Remove 移除 - + Unlock load order - + Lock load order - + BOSS working - + failed to run boss: %1 - + Request to Nexus failed: %1 - + Executable "%1" not found - + Failed to refresh list of esps: %1 - + This will move all files from overwrite into a new, regular mod. Please enter a name: - + Add/Remove Categories - + Replace Categories - - + + login successful 登录成功 - + login failed: %1. Trying to download anyway 登录失败: %1,请尝试使用别的方法下载 - + login failed: %1 无法登录: %1 - + login failed: %1. You need to log-in with Nexus to update MO. 登录失败: %1。您需要登录到N网才能更新 MO - + Error 错误 - + failed to extract %1 (errorcode %2) 无法解压 %1 (错误代码 %2) - + Extract... 解压... - + Edit Categories... 编辑类别... - + Enable all 全部启用 - + Disable all 全部禁用 @@ -4765,64 +4779,64 @@ Right now this has very limited functionality ESP 文件没有找到: %1 - + Mod Index - + Flags - - + + unknown - + Name of your mods - + Load priority of your mod. The higher, the more "important" it is and thus overwrites data from plugins with lower priority. - + The modindex determins the formids of objects originating from this mods. - + failed to update esp info for file %1 (source id: %2), error: %3 - + esp not found: %1 - - + + Confirm 确认 - + Really enable all plugins? - + Really disable all plugins? - + The file containing locked plugin indices is broken @@ -4831,7 +4845,7 @@ Right now this has very limited functionality 无法打开输出文件: %1 - + Some of your plugins have invalid names! These plugins can not be loaded by the game. Please see mo_interface.log for a list of affected plugins and rename them. 您的一些插件名称无效!这些插件无法被游戏载入。请查看 mo_interface.log 来确认那些受影响的插件并重命名它们。 @@ -4841,17 +4855,17 @@ Right now this has very limited functionality - + Missing Masters - + Enabled Masters - + failed to restore load order for %1 @@ -4864,17 +4878,17 @@ Right now this has very limited functionality 最高值 - + This plugin can't be disabled (enforced by the game) 这个插件不能被禁用 (由游戏执行) - + Origin: %1 隶属于: %1 - + Name 名称 @@ -4883,7 +4897,7 @@ Right now this has very limited functionality Mod 名称 - + Priority 优先级 @@ -5466,23 +5480,23 @@ p, li { white-space: pre-wrap; } - + Please use "Help" from the toolbar to get usage instructions to all elements 请使用工具栏上的“帮助”来获得所有元素的使用说明 - - + + <Manage...> <管理...> - + failed to parse profile %1: %2 无法解析配置文件 %1: %2 - + failed to find "%1" 未能找到 "%1" @@ -5492,12 +5506,12 @@ p, li { white-space: pre-wrap; } 编码错误,请向作者汇报此 Bug 并且附上 mo_interface.log 文件! - + failed to access %1 无法访问 %1 - + failed to set file time %1 无法设置文件时间 %1 diff --git a/src/organizer_zh_TW.ts b/src/organizer_zh_TW.ts index 1fc6b17b..0ae46a41 100644 --- a/src/organizer_zh_TW.ts +++ b/src/organizer_zh_TW.ts @@ -1629,11 +1629,21 @@ BSAs checked here are loaded in such a way that your installation order is obeye Data Data + + + Plugins + + Sort + + + Archives + + refresh data-directory overview @@ -1646,8 +1656,8 @@ BSAs checked here are loaded in such a way that your installation order is obeye - - + + Refresh 重新整理 @@ -1827,7 +1837,7 @@ p, li { white-space: pre-wrap; } - + Update 更新 @@ -1838,7 +1848,7 @@ p, li { white-space: pre-wrap; } - + No Problems 沒有問題 @@ -1871,37 +1881,37 @@ Right now this has very limited functionality - + Endorse Mod Organizer - + Toolbar - + Desktop - + Start Menu - + Problems 問題 - + There are potential problems with your setup 您的安裝中存在潛在的問題 - + Everything seems to be in order 一切井然有序 @@ -1918,22 +1928,22 @@ Right now this has very limited functionality <li>.Net 未安裝或版本過舊。想要運行 NCC 您必須先安裝 .Net,您可以在以下地址中獲取: <a href="%1">%1</a></li> - + Help on UI 介面幫助 - + Documentation Wiki 說明文檔 (維基) - + Report Issue 報告問題 - + Tutorials @@ -1942,93 +1952,92 @@ Right now this has very limited functionality 無法儲存加載順序 - + failed to save load order: %1 無法儲存加載順序: %1 - failed to save archives order, do you have write access to "%1"? - 無法儲存檔案順序,您確定您有權限更改 "%1"? + 無法儲存檔案順序,您確定您有權限更改 "%1"? - + About - + About Qt - + Name 名稱 - + Please enter a name for the new profile - + failed to create profile: %1 無法建立配置檔案: %1 - + Show tutorial? - + You are starting Mod Organizer for the first time. Do you want to show a tutorial of its basic features? If you choose no you can always start the tutorial from the "Help"-menu. - + Downloads in progress 正在下載 - + There are still downloads in progress, do you really want to quit? 仍有正在進行中的下載,您確定要退出嗎? - + failed to read savegame: %1 無法讀取存檔: %1 - + Plugin "%1" failed: %2 - + Plugin error - + It appears the plugin "%1" failed to load last startup and caused MO to crash. Do you want to disable it? (Please note: If this is the first time you see this message for this plugin you may want to give it another try. The plugin may be able to recover from the problem) - + Failed to start "%1" 無法啟動 "%1" - + Waiting 稍等 - + Please press OK once you're logged into steam. 當您登入 Steam 時請點擊確定。 @@ -2037,331 +2046,336 @@ Right now this has very limited functionality "%1" 未找到 - + Start Steam? 啟動 Steam? - + Steam is required to be running already to correctly start the game. Should MO try to start steam now? 想要正確地啟動遊戲,Steam 必須處於運行狀態,MO 要立即啟動 Steam 嗎? - + Also in: <br> 也在: <br> - + No conflict 沒有衝突 - + <Edit...> <編輯...> - + This bsa is enabled in the ini file so it may be required! 該 BSA 已在 Ini 檔案中啟用,因此它可能是必需的。 - + This archive will still be loaded since there is a plugin of the same name but its files will not follow installation order! 此檔案還是會被加載,因為存在同名插件。不過它所包含的的檔案不會遵循安裝順序! - + Activating Network Proxy - - + + Installation successful 安裝成功 - - + + Configure Mod 配置 Mod - - + + This mod contains ini tweaks. Do you want to configure them now? 此 Mod 中包含 Ini 設定檔案,您想現在就對它們進行配置嗎? - - + + mod "%1" not found Mod "%1" 未找到 - - + + Installation cancelled 安裝已取消 - - + + The mod was not installed completely. Mod 沒有完全安裝。 - + Some plugins could not be loaded - + Too many esps and esms enabled - - + + Description missing - + The game doesn't allow more than 255 active plugins (including the official ones) to be loaded. You have to disable some unused plugins or merge some plugins into one. You can find a guide here: <a href="http://wiki.step-project.com/Guide:Merging_Plugins">http://wiki.step-project.com/Guide:Merging_Plugins</a> - + Choose Mod 選擇 Mod - + Mod Archive Mod 壓縮包 - + Start Tutorial? - + You're about to start a tutorial. For technical reasons it's not possible to end the tutorial early. Continue? - - + + Download started 開始下載 - + failed to update mod list: %1 無法更新 Mod 列表: %1 - + failed to spawn notepad.exe: %1 無法生成 notepad.exe: %1 - + failed to open %1 無法開啟 %1 - + failed to change origin name: %1 無法更改原始檔案名: %1 - + + failed to move "%1" from mod "%2" to "%3": %4 + + + + Multiple esps activated, please check that they don't conflict. - - + + Create Mod... - + A mod with this name already exists - + Continue? - + The versioning scheme decides which version is considered newer than another. This function will guess the versioning scheme under the assumption that the installed version is outdated. - - + + Sorry - + I don't know a versioning scheme where %1 is newer than %2. - + <All> <全部> - + <Checked> <已勾選> - + Plugin "%1" failed - + failed to init plugin %1: %2 - + The following plugins could not be loaded. The reason may be missing dependencies (i.e. python) or an outdated version: - + <Unchecked> <未勾選> - + <Update> <有更新> - + <No category> <無類別> - + <Conflicted> - + <Not Endorsed> - + failed to rename mod: %1 無法重新命名 Mod: %1 - + Overwrite? - + This will replace the existing mod "%1". Continue? - + failed to remove mod "%1" - - - + + + failed to rename "%1" to "%2" - - - - + + + + Confirm 確認 - + Remove the following mods?<br><ul>%1</ul> - + failed to remove mod: %1 無法移動 Mod: %1 - - + + Failed 失敗 - + Installation file no longer exists 安裝檔案不複存在 - + Mods installed with old versions of MO can't be reinstalled in this way. 舊版 MO 安裝的 Mod 無法使用此方法重新安裝。 - - + + You need to be logged in with Nexus to endorse - - + + Extract BSA 解壓 BSA - + This mod contains at least one BSA. Do you want to unpack it? (This removes the BSA after completion. If you don't know about BSAs, just select no) 此 Mod 中至少包含一個 BSA。您確定要解壓嗎? (解壓完成後,BSA 檔案將會被刪除。如果您不瞭解 BSA 的話,請選擇“否”) - - - + + + failed to read %1: %2 無法讀取 %1: %2 - - + + This archive contains invalid hashes. Some files may be broken. 壓縮包 Hash 值錯誤。部分檔案可能已經損壞。 - + Nexus ID for this Mod is unknown 此 Mod 的N網 ID 未知 @@ -2374,92 +2388,92 @@ This function will guess the versioning scheme under the assumption that the ins 選擇優先級 - + Really enable all visible mods? 確定要啟用全部可見的 Mod 嗎? - + Really disable all visible mods? 確定要禁用全部可見的 Mod 嗎? - + Choose what to export - + Everything - + All installed mods are included in the list - + Active Mods - + Only active (checked) mods from your current profile are included - + Visible - + All mods visible in the mod list are included - + export failed: %1 - + Install Mod... 安裝 Mod... - + Enable all visible 啟用所有可見項目 - + Disable all visible 禁用所有可見項目 - + Check all for update 檢查更新 - + Export to csv... - + Sync to Mods... 同步到 Mod... - + Restore Backup - + Remove Backup... @@ -2468,362 +2482,362 @@ This function will guess the versioning scheme under the assumption that the ins 設定類別 - + Primary Category - + Change versioning scheme - + Un-ignore update - + Ignore update - + Rename Mod... 重新命名... - + Remove Mod... 移除 Mod... - + Reinstall Mod 重新安裝 Mod - + Un-Endorse - - + + Endorse - + Won't endorse - + Endorsement state unknown - + Ignore missing data - + Visit on Nexus 在N網上流覽 - + Open in explorer 在檔案總管中開啟 - + Information... 訊息... - - + + Exception: 例外: - - + + Unknown exception 未知的例外 - + <Multiple> - + Really delete "%1"? - + Fix Mods... 修復 Mod... - + Delete - - + + failed to remove %1 無法刪除 %1 - - + + failed to create %1 無法建立 %1 - + Can't change download directory while downloads are in progress! 下載檔案時不能修改下載目錄! - + Download failed 下載失敗 - + failed to write to file %1 無法寫入檔案 %1 - + %1 written 已寫入 %1 - + Select binary 選擇可執行檔案 - + Binary - + Enter Name 輸入名稱 - + Please enter a name for the executable 請為程式輸入一個名稱 - + Not an executable 不是可執行程式 - + This is not a recognized executable. 無法識別的可執行檔案 - - + + Replace file? 取代檔案? - + There already is a hidden version of this file. Replace it? 已存在同名檔案,但該檔案被隱藏了。確定要覆蓋嗎? - - + + File operation failed 檔案操作錯誤 - - + + Failed to remove "%1". Maybe you lack the required file permissions? - + There already is a visible version of this file. Replace it? - + file not found: %1 檔案未找到: %1 - + failed to generate preview for %1 - + Sorry, can't preview anything. This function currently does not support extracting from bsas. - + Update available 更新可用 - + Open/Execute 開啟/執行 - + Add as Executable 添加為可執行檔案 - + Preview - + Un-Hide 取消隱藏 - + Hide 隱藏 - + Write To File... 寫入檔案... - + Do you want to endorse Mod Organizer on %1 now? - + Remove 移除 - + Unlock load order - + Lock load order - + BOSS working - + failed to run boss: %1 - + Request to Nexus failed: %1 - + Executable "%1" not found - + Failed to refresh list of esps: %1 - + This will move all files from overwrite into a new, regular mod. Please enter a name: - + Add/Remove Categories - + Replace Categories - - + + login successful 登入成功 - + login failed: %1. Trying to download anyway 登入失敗: %1,請嘗試使用別的方法下載 - + login failed: %1 無法登入: %1 - + login failed: %1. You need to log-in with Nexus to update MO. 登入失敗: %1。您需要登入到N網才能更新 MO - + Error 錯誤 - + failed to extract %1 (errorcode %2) 無法解壓 %1 (錯誤代碼 %2) - + Extract... 解壓... - + Edit Categories... 編輯類別... - + Enable all 全部啟用 - + Disable all 全部禁用 @@ -4765,64 +4779,64 @@ Right now this has very limited functionality ESP 檔案沒有找到: %1 - + Mod Index - + Flags - - + + unknown - + Name of your mods - + Load priority of your mod. The higher, the more "important" it is and thus overwrites data from plugins with lower priority. - + The modindex determins the formids of objects originating from this mods. - + failed to update esp info for file %1 (source id: %2), error: %3 - + esp not found: %1 - - + + Confirm 確認 - + Really enable all plugins? - + Really disable all plugins? - + The file containing locked plugin indices is broken @@ -4831,7 +4845,7 @@ Right now this has very limited functionality 無法開啟輸出檔案: %1 - + Some of your plugins have invalid names! These plugins can not be loaded by the game. Please see mo_interface.log for a list of affected plugins and rename them. 您的一些插件名稱無效!這些插件無法被遊戲載入。請查看 mo_interface.log 來確認那些受影響的插件並重新命名它們。 @@ -4841,17 +4855,17 @@ Right now this has very limited functionality - + Missing Masters - + Enabled Masters - + failed to restore load order for %1 @@ -4864,17 +4878,17 @@ Right now this has very limited functionality 最高值 - + This plugin can't be disabled (enforced by the game) 這個插件不能被禁用 (由遊戲執行) - + Origin: %1 隸屬於: %1 - + Name 名稱 @@ -4883,7 +4897,7 @@ Right now this has very limited functionality Mod 名稱 - + Priority 優先級 @@ -5466,23 +5480,23 @@ p, li { white-space: pre-wrap; } - + Please use "Help" from the toolbar to get usage instructions to all elements 請使用工具列上的“幫助”來獲得所有元素的使用說明 - - + + <Manage...> <管理...> - + failed to parse profile %1: %2 無法解析配置檔案 %1: %2 - + failed to find "%1" 未能找到 "%1" @@ -5492,12 +5506,12 @@ p, li { white-space: pre-wrap; } 編碼錯誤,請向作者彙報此 Bug 並且附上 mo_interface.log 檔案! - + failed to access %1 無法訪問 %1 - + failed to set file time %1 無法設定檔案時間 %1 diff --git a/src/settings.cpp b/src/settings.cpp index 895094f7..dd3891d4 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -223,7 +223,7 @@ QString Settings::getModDirectory() const QString Settings::getNMMVersion() const { - static const QString MIN_NMM_VERSION = "0.46.0"; + static const QString MIN_NMM_VERSION = "0.47.0"; QString result = m_Settings.value("Settings/nmm_version", MIN_NMM_VERSION).toString(); if (VersionInfo(result) < VersionInfo(MIN_NMM_VERSION)) { result = MIN_NMM_VERSION; -- cgit v1.3.1 From 8ab1b479438dd929bc953e554629d1bddca61c9e Mon Sep 17 00:00:00 2001 From: Tannin Date: Tue, 18 Mar 2014 18:29:32 +0100 Subject: - bugfix: if resuming a download failed with the server sending a textual error message, MO tried to display the whole file inside the error message - bugfix: resuming a download didn't trigger a nexus-login when necessary - bugfix: integrated fomod installer only used the first block of data inside a description --- src/downloadmanager.cpp | 2 +- src/mainwindow.cpp | 18 +++++++++++++++++- src/mainwindow.h | 1 + 3 files changed, 19 insertions(+), 2 deletions(-) (limited to 'src/mainwindow.h') diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index 31d8bcec..e747b299 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -1246,7 +1246,7 @@ void DownloadManager::downloadFinished() textData) { if (info->m_Tries == 0) { if (textData && (reply->error() == QNetworkReply::NoError)) { - emit showMessage(tr("Download failed. Server reported: %1").arg(readFileText(info->m_Output.fileName()))); + emit showMessage(tr("Download failed. Server reported: %1").arg(QString(data))); } else { emit showMessage(tr("Download failed: %1 (%2)").arg(reply->errorString()).arg(reply->error())); } diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index a0af63bd..d4826b30 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3099,6 +3099,22 @@ void MainWindow::reinstallMod_clicked() } +void MainWindow::resumeDownload(int downloadIndex) +{ + if (NexusInterface::instance()->getAccessManager()->loggedIn()) { + m_DownloadManager.resumeDownload(downloadIndex); + } else { + QString username, password; + if (m_Settings.getNexusLogin(username, password)) { + m_PostLoginTasks.push_back(boost::bind(&MainWindow::resumeDownload, _1, downloadIndex)); + NexusInterface::instance()->getAccessManager()->login(username, password); + } 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()->getAccessManager()->loggedIn()) { @@ -4685,7 +4701,7 @@ void MainWindow::updateDownloadListDelegate() connect(ui->downloadView->itemDelegate(), SIGNAL(restoreDownload(int)), &m_DownloadManager, SLOT(restoreDownload(int))); connect(ui->downloadView->itemDelegate(), SIGNAL(cancelDownload(int)), &m_DownloadManager, SLOT(cancelDownload(int))); connect(ui->downloadView->itemDelegate(), SIGNAL(pauseDownload(int)), &m_DownloadManager, SLOT(pauseDownload(int))); - connect(ui->downloadView->itemDelegate(), SIGNAL(resumeDownload(int)), &m_DownloadManager, SLOT(resumeDownload(int))); + connect(ui->downloadView->itemDelegate(), SIGNAL(resumeDownload(int)), this, SLOT(resumeDownload(int))); } diff --git a/src/mainwindow.h b/src/mainwindow.h index 250f2a48..46165f0f 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -483,6 +483,7 @@ private slots: void hookUpWindowTutorials(); + void resumeDownload(int downloadIndex); void endorseMod(ModInfo::Ptr mod); void cancelModListEditor(); -- cgit v1.3.1 From b1f1682790072fbfb45bd9eaa3c6890edfb81a22 Mon Sep 17 00:00:00 2001 From: Tannin Date: Wed, 26 Mar 2014 17:23:26 +0100 Subject: - bugfix: the add/remove categories function could fail when a filter/grouping on categories was active --- src/mainwindow.cpp | 68 +++++++++++++++--------------------------------------- src/mainwindow.h | 4 +++- 2 files changed, 22 insertions(+), 50 deletions(-) (limited to 'src/mainwindow.h') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 93c1f2f1..bf5ea7f0 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3494,13 +3494,13 @@ void MainWindow::replaceCategoriesFromMenu(QMenu *menu, int modRow) } } -void MainWindow::addRemoveCategoriesFromMenu(QMenu *menu, int modRow) +void MainWindow::addRemoveCategoriesFromMenu(QMenu *menu, int modRow, int referenceRow) { - if (m_ContextRow != -1 && m_ContextRow != modRow) { - ModInfo::Ptr editedModInfo = ModInfo::getByIndex(m_ContextRow); + if (referenceRow != -1 && referenceRow != modRow) { + ModInfo::Ptr editedModInfo = ModInfo::getByIndex(referenceRow); foreach (QAction* action, menu->actions()) { if (action->menu() != NULL) { - addRemoveCategoriesFromMenu(action->menu(), modRow); + addRemoveCategoriesFromMenu(action->menu(), modRow, referenceRow); } else { QWidgetAction *widgetAction = qobject_cast(action); if (widgetAction != NULL) { @@ -3517,7 +3517,6 @@ void MainWindow::addRemoveCategoriesFromMenu(QMenu *menu, int modRow) } } } else { - //This block shouldn't be reached, but if it is then fall back to replace (context row is invalid or replacing edited mod) replaceCategoriesFromMenu(menu, modRow); } } @@ -3529,37 +3528,26 @@ void MainWindow::addRemoveCategories_MenuHandler() { return; } - QModelIndexList selected = ui->modList->selectionModel()->selectedRows(); + QModelIndexList selectedTemp = ui->modList->selectionModel()->selectedRows(); + QList selected; + foreach (const QModelIndex &idx, selectedTemp) { + selected.append(QPersistentModelIndex(idx)); + } if (selected.size() > 0) { - int min = INT_MAX; - int max = INT_MIN; - - QStringList selectedMods; - for (int i = 0; i < selected.size(); ++i) { - QModelIndex temp = mapToModel(&m_ModList, selected.at(i)); - selectedMods.append(temp.data().toString()); - if (temp.row() < min) min = temp.row(); - if (temp.row() > max) max = temp.row(); - // save the currently selected mod for last... then we can use it as a pattern for what is changing... - int modRow = m_ModListSortProxy->mapToSource(selected.at(i)).row(); - if (modRow != m_ContextRow) { - addRemoveCategoriesFromMenu(menu,modRow); + foreach (const QPersistentModelIndex &idx, selected) { + qDebug("change categories on: %s (ref: %s)", qPrintable(idx.data().toString()), qPrintable(m_ContextIdx.data().toString())); + QModelIndex modIdx = mapToModel(&m_ModList, idx); + if (modIdx.row() != m_ContextIdx.row()) { + addRemoveCategoriesFromMenu(menu, modIdx.row(), m_ContextIdx.row()); } } - //come back to the currently selected mod, after the others have been set - replaceCategoriesFromMenu(menu, m_ContextRow); + replaceCategoriesFromMenu(menu, m_ContextIdx.row()); m_ModList.notifyChange(-1); - // find mods by their name because indices are invalidated - QAbstractItemModel *model = ui->modList->model(); - Q_FOREACH(const QString &mod, selectedMods) { - QModelIndexList matches = model->match(model->index(0, 0), Qt::DisplayRole, mod, 1, - Qt::MatchFixedString | Qt::MatchCaseSensitive | Qt::MatchRecursive); - if (matches.size() > 0) { - ui->modList->selectionModel()->select(matches.at(0), QItemSelectionModel::Select | QItemSelectionModel::Rows); - } + foreach (const QPersistentModelIndex &idx, selected) { + ui->modList->selectionModel()->select(idx, QItemSelectionModel::Select | QItemSelectionModel::Rows); } } else { //For single mod selections, just do a replace @@ -3580,15 +3568,10 @@ void MainWindow::replaceCategories_MenuHandler() { QModelIndexList selected = ui->modList->selectionModel()->selectedRows(); if (selected.size() > 0) { - int min = INT_MAX; - int max = INT_MIN; - QStringList selectedMods; for (int i = 0; i < selected.size(); ++i) { QModelIndex temp = mapToModel(&m_ModList, selected.at(i)); selectedMods.append(temp.data().toString()); - if (temp.row() < min) min = temp.row(); - if (temp.row() > max) max = temp.row(); replaceCategoriesFromMenu(menu, mapToModel(&m_ModList, selected.at(i)).row()); } @@ -3810,8 +3793,8 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) try { QTreeView *modList = findChild("modList"); - QModelIndex index = mapToModel(&m_ModList, modList->indexAt(pos)); - m_ContextRow = index.row(); + m_ContextIdx = mapToModel(&m_ModList, modList->indexAt(pos)); + m_ContextRow = m_ContextIdx.row(); QMenu menu; @@ -4771,19 +4754,6 @@ void MainWindow::nxmUpdatesAvailable(const std::vector &modIDs, QVariant us } } -/* -void MainWindow::nxmEndorsementToggled(int, QVariant, QVariant resultData, int) -{ - if (resultData.toBool()) { - ui->actionEndorseMO->setVisible(false); - QMessageBox::question(this, tr("Thank you!"), tr("Thank you for your endorsement!")); - } - - if (!disconnect(sender(), SIGNAL(nxmEndorsementToggled(int, QVariant, QVariant, int)), - this, SLOT(nxmEndorsementToggled(int, QVariant, QVariant, int)))) { - qCritical("failed to disconnect endorsement slot"); - } -}*/ void MainWindow::nxmDownloadURLs(int, int, QVariant, QVariant resultData, int) { diff --git a/src/mainwindow.h b/src/mainwindow.h index 46165f0f..23923677 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -236,8 +236,9 @@ private: * the changes made in the menu (which is the delta between the current menu selection and the reference mod) * @param menu the menu after editing by the user * @param modRow index of the mod to edit + * @param referenceRow row of the reference mod */ - void addRemoveCategoriesFromMenu(QMenu *menu, int modRow); + void addRemoveCategoriesFromMenu(QMenu *menu, int modRow, int referenceRow); /** * Sets category selections from menu; for multiple mods, this will completely @@ -324,6 +325,7 @@ private: QString m_GamePath; int m_ContextRow; + QPersistentModelIndex m_ContextIdx; QTreeWidgetItem *m_ContextItem; QAction *m_ContextAction; -- cgit v1.3.1 From c017f4a0d50b67a44e276bd5ae8929ed3990c62c Mon Sep 17 00:00:00 2001 From: Tannin Date: Sat, 5 Apr 2014 15:14:37 +0200 Subject: - added buttons to backup and restore the modlist and pluginlist - replaced boss integration with loot --- src/ModOrganizer.pro | 3 +- src/main.cpp | 23 +--- src/mainwindow.cpp | 204 ++++++++++++++++++++++++++++++++- src/mainwindow.h | 16 ++- src/mainwindow.ui | 112 +++++++++++++++++-- src/modlist.cpp | 2 +- src/pluginlist.cpp | 224 +------------------------------------ src/pluginlist.h | 83 +------------- src/profile.h | 6 +- src/resources.qrc | 3 + src/resources/arrange-boxes.png | Bin 0 -> 1634 bytes src/resources/document-save_32.png | Bin 0 -> 1971 bytes src/resources/edit-undo.png | Bin 0 -> 1601 bytes src/selectiondialog.cpp | 123 ++++++++++---------- src/selectiondialog.h | 96 ++++++++-------- src/shared/gameinfo.cpp | 8 ++ src/shared/gameinfo.h | 1 + src/spawn.cpp | 29 ++++- src/spawn.h | 8 +- 19 files changed, 486 insertions(+), 455 deletions(-) create mode 100644 src/resources/arrange-boxes.png create mode 100644 src/resources/document-save_32.png create mode 100644 src/resources/edit-undo.png (limited to 'src/mainwindow.h') diff --git a/src/ModOrganizer.pro b/src/ModOrganizer.pro index 05b05855..62de3f66 100644 --- a/src/ModOrganizer.pro +++ b/src/ModOrganizer.pro @@ -14,7 +14,8 @@ SUBDIRS = bsatk \ BossDummy \ pythonRunner \ boss_modified \ - esptk + esptk \ + loot_cli plugins.depends = pythonRunner hookdll.depends = shared diff --git a/src/main.cpp b/src/main.cpp index ac903615..d1c5e263 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -80,24 +80,6 @@ using namespace MOBase; using namespace MOShared; -void removeOldLogfiles() -{ - QFileInfoList files = QDir(ToQString(GameInfo::instance().getLogDir())).entryInfoList(QStringList("ModOrganizer*.log"), - QDir::Files, QDir::Name); - - if (files.count() > 5) { - QStringList deleteFiles; - for (int i = 0; i < files.count() - 5; ++i) { - deleteFiles.append(files.at(i).absoluteFilePath()); - } - - if (!shellDelete(deleteFiles)) { - qWarning("failed to remove log files: %s", qPrintable(windowsErrorString(::GetLastError()))); - } - } -} - - // set up required folders (for a first install or after an update or to fix a broken installation) bool bootstrap() { @@ -111,7 +93,7 @@ bool bootstrap() } // cycle logfile - removeOldLogfiles(); + removeOldFiles(ToQString(GameInfo::instance().getLogDir()), "ModOrganizer*.log", 5, QDir::Name); // create organizer directories QString dirNames[] = { @@ -120,8 +102,7 @@ bool bootstrap() QDir::fromNativeSeparators(ToQString(gameInfo.getDownloadDir())), QDir::fromNativeSeparators(ToQString(gameInfo.getOverwriteDir())), QDir::fromNativeSeparators(ToQString(gameInfo.getLogDir())), - QDir::fromNativeSeparators(ToQString(gameInfo.getTutorialDir())), - QDir::fromNativeSeparators(ToQString(gameInfo.getOrganizerDirectory()) + "/boss") + QDir::fromNativeSeparators(ToQString(gameInfo.getTutorialDir())) }; static const int NUM_DIRECTORIES = sizeof(dirNames) / sizeof(QString); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index bf5ea7f0..87360f3c 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -109,8 +109,10 @@ along with Mod Organizer. If not, see . #include #endif #include +#include #include #include +#include #ifdef TEST_MODELS @@ -2649,7 +2651,6 @@ void MainWindow::directory_refreshed() delete oldStructure; refreshDataTree(); - refreshLists(); } else { // TODO: don't know why this happens, this slot seems to get called twice with only one emit return; @@ -5155,20 +5156,213 @@ void MainWindow::on_showHiddenBox_toggled(bool checked) m_DownloadManager.setShowHidden(checked); } + +void MainWindow::createStdoutPipe(HANDLE *stdOutRead, HANDLE *stdOutWrite) +{ + SECURITY_ATTRIBUTES secAttributes; + secAttributes.nLength = sizeof(SECURITY_ATTRIBUTES); + secAttributes.bInheritHandle = TRUE; + secAttributes.lpSecurityDescriptor = NULL; + + if (!::CreatePipe(stdOutRead, stdOutWrite, &secAttributes, 0)) { + qCritical("failed to create stdout reroute"); + } + + if (!::SetHandleInformation(*stdOutRead, HANDLE_FLAG_INHERIT, 0)) { + qCritical("failed to correctly set up the stdout reroute"); + *stdOutWrite = *stdOutRead = INVALID_HANDLE_VALUE; + } +} + +std::string MainWindow::readFromPipe(HANDLE stdOutRead) +{ + static const int chunkSize = 128; + std::string result; + + char buffer[chunkSize + 1]; + buffer[chunkSize] = '\0'; + + DWORD read = 1; + while (read > 0) { + if (!::ReadFile(stdOutRead, buffer, chunkSize, &read, NULL)) { + break; + } + if (read > 0) { + result.append(buffer, read); + if (read < chunkSize) { + break; + } + } + } + return result; +} + void MainWindow::on_bossButton_clicked() { try { this->setEnabled(false); ON_BLOCK_EXIT([&] () { this->setEnabled(true); }); - LockedDialog dialog(this, tr("BOSS working"), false); + QProgressDialog dialog(this); + dialog.setLabelText(tr("LOOT working")); + dialog.setMaximum(0); dialog.show(); - qApp->processEvents(); - m_PluginList.bossSort(); - savePluginList(); + QStringList parameters; + parameters << "--game" << ToQString(GameInfo::instance().getGameName()) + << "--gamePath" << ToQString(GameInfo::instance().getGameDirectory()); + + HANDLE stdOutWrite = INVALID_HANDLE_VALUE; + HANDLE stdOutRead = INVALID_HANDLE_VALUE; + createStdoutPipe(&stdOutRead, &stdOutWrite); + + HANDLE loot = startBinary(QFileInfo(qApp->applicationDirPath() + "/lootcli.exe"), + parameters.join(" "), + m_CurrentProfile->getName(), + m_Settings.logLevel(), + qApp->applicationDirPath(), + true, + stdOutWrite); + + // we don't use the write end + ::CloseHandle(stdOutWrite); + + if (loot != INVALID_HANDLE_VALUE) { + while (::WaitForSingleObject(loot, 100) == WAIT_TIMEOUT) { + // keep processing events so the app doesn't appear dead + QCoreApplication::processEvents(); + if (dialog.wasCanceled()) { + ::TerminateProcess(loot, 1); + } + std::string lootOut = readFromPipe(stdOutRead); + std::vector lines; + boost::split(lines, lootOut, boost::is_any_of("\r\n")); + foreach (const std::string &line, lines) { + if (line.length() > 0) { + size_t progidx = line.find("[progress]"); + size_t reportidx = line.find("[report]"); + if (progidx != std::string::npos) { + dialog.setLabelText(line.substr(progidx + 11).c_str()); + } else if (reportidx != std::string::npos) { + qDebug("report at %s", line.substr(reportidx + 9).c_str()); + } else { + qDebug("%s", line.c_str()); + } + } + } + } + std::string remainder = readFromPipe(stdOutRead).c_str(); + if (remainder.length() > 0) { + qDebug("%s", remainder.c_str()); + } + + refreshESPList(); + + if (GameInfo::instance().getLoadOrderMechanism() == GameInfo::TYPE_FILETIME) { + QFile::remove(m_CurrentProfile->getLoadOrderFileName()); + } + } + dialog.hide(); } catch (const std::exception &e) { reportError(tr("failed to run boss: %1").arg(e.what())); ui->bossButton->setEnabled(false); } } + + +const char *MainWindow::PATTERN_BACKUP_GLOB = ".????_??_??_??_??_??"; +const char *MainWindow::PATTERN_BACKUP_REGEX = "\\.(\\d\\d\\d\\d_\\d\\d_\\d\\d_\\d\\d_\\d\\d_\\d\\d)"; +const char *MainWindow::PATTERN_BACKUP_DATE = "yyyy_MM_dd_hh_mm_ss"; + + +bool MainWindow::createBackup(const QString &filePath, const QDateTime &time) +{ + QString outPath = filePath + "." + time.toString(PATTERN_BACKUP_DATE); + if (shellCopy(QStringList(filePath), QStringList(outPath), this)) { + QFileInfo fileInfo(filePath); + removeOldFiles(fileInfo.absolutePath(), fileInfo.fileName() + PATTERN_BACKUP_GLOB, 3, QDir::Name); + return true; + } else { + return false; + } +} + +void MainWindow::on_saveButton_clicked() +{ + savePluginList(); + QDateTime now = QDateTime::currentDateTime(); + if (createBackup(m_CurrentProfile->getPluginsFileName(), now) + && createBackup(m_CurrentProfile->getLoadOrderFileName(), now) + && createBackup(m_CurrentProfile->getLockedOrderFileName(), now)) { + MessageDialog::showMessage(tr("Backup of load order created"), this); + } +} + +QString MainWindow::queryRestore(const QString &filePath) +{ + QFileInfo pluginFileInfo(filePath); + QString pattern = pluginFileInfo.fileName() + ".*"; + QFileInfoList files = pluginFileInfo.absoluteDir().entryInfoList(QStringList(pattern), QDir::Files, QDir::Name); + + SelectionDialog dialog(tr("Choose backup to restore"), this); + QRegExp exp(pluginFileInfo.fileName() + PATTERN_BACKUP_REGEX); + QRegExp exp2(pluginFileInfo.fileName() + "\\.(.*)"); + foreach(const QFileInfo &info, files) { + if (exp.exactMatch(info.fileName())) { + QDateTime time = QDateTime::fromString(exp.cap(1), PATTERN_BACKUP_DATE); + dialog.addChoice(time.toString(), "", exp.cap(1)); + } else if (exp2.exactMatch(info.fileName())) { + dialog.addChoice(exp2.cap(1), "", exp2.cap(1)); + } + } + + if (dialog.numChoices() == 0) { + QMessageBox::information(this, tr("No Backups"), tr("There are no backups to restore")); + return QString(); + } + + if (dialog.exec() == QDialog::Accepted) { + return dialog.getChoiceData().toString(); + } else { + return QString(); + } +} + +void MainWindow::on_restoreButton_clicked() +{ + QString pluginName = m_CurrentProfile->getPluginsFileName(); + QString choice = queryRestore(pluginName); + if (!choice.isEmpty()) { + QString loadOrderName = m_CurrentProfile->getLoadOrderFileName(); + QString lockedName = m_CurrentProfile->getLockedOrderFileName(); + if (!shellCopy(pluginName + "." + choice, pluginName, true, this) || + !shellCopy(loadOrderName + "." + choice, loadOrderName, true, this) || + !shellCopy(lockedName + "." + choice, lockedName, true, this)) { + QMessageBox::critical(this, tr("Restore failed"), + tr("Failed to restore the backup. Errorcode: %1").arg(windowsErrorString(::GetLastError()))); + } + refreshESPList(); + } +} + +void MainWindow::on_saveModsButton_clicked() +{ + m_CurrentProfile->writeModlistNow(true); + QDateTime now = QDateTime::currentDateTime(); + if (createBackup(m_CurrentProfile->getModlistFileName(), now)) { + MessageDialog::showMessage(tr("Backup of modlist created"), this); + } +} +void MainWindow::on_restoreModsButton_clicked() +{ + QString modlistName = m_CurrentProfile->getModlistFileName(); + QString choice = queryRestore(modlistName); + if (!choice.isEmpty()) { + if (!shellCopy(modlistName + "." + choice, modlistName, true, this)) { + QMessageBox::critical(this, tr("Restore failed"), + tr("Failed to restore the backup. Errorcode: %1").arg(windowsErrorString(::GetLastError()))); + } + refreshModList(false); + } +} + diff --git a/src/mainwindow.h b/src/mainwindow.h index 23923677..c5b4a8d3 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -154,6 +154,8 @@ public: void saveArchiveList(); + void createStdoutPipe(HANDLE *stdOutRead, HANDLE *stdOutWrite); + std::string readFromPipe(HANDLE stdOutRead); public slots: void displayColumnSelection(const QPoint &pos); @@ -207,8 +209,6 @@ private: bool nexusLogin(); - void saveCurrentESPList(); - bool testForSteam(); void startSteam(); @@ -290,11 +290,18 @@ private: void activateProxy(bool activate); void installTranslator(const QString &name); + bool createBackup(const QString &filePath, const QDateTime &time); + QString queryRestore(const QString &filePath); + private: static const unsigned int PROBLEM_PLUGINSNOTLOADED = 1; static const unsigned int PROBLEM_TOOMANYPLUGINS = 2; + static const char *PATTERN_BACKUP_GLOB; + static const char *PATTERN_BACKUP_REGEX; + static const char *PATTERN_BACKUP_DATE; + private: Ui::MainWindow *ui; @@ -574,6 +581,11 @@ private slots: // ui slots void on_showHiddenBox_toggled(bool checked); void on_bsaList_itemChanged(QTreeWidgetItem *item, int column); void on_bossButton_clicked(); + + void on_saveButton_clicked(); + void on_restoreButton_clicked(); + void on_restoreModsButton_clicked(); + void on_saveModsButton_clicked(); }; #endif // MAINWINDOW_H diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 26cbbf83..dc91121c 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -115,7 +115,7 @@ 2 - + @@ -170,6 +170,47 @@ p, li { white-space: pre-wrap; } + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Restore Backup... + + + + + + + :/MO/gui/restore:/MO/gui/restore + + + + + + + Create Backup + + + + + + + :/MO/gui/backup:/MO/gui/backup + + + @@ -617,6 +658,68 @@ p, li { white-space: pre-wrap; } 0 + + + + + + Sort + + + + :/MO/gui/sort:/MO/gui/sort + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Restore Backup... + + + + + + + :/MO/gui/restore:/MO/gui/restore + + + + 16 + 16 + + + + + + + + Create Backup + + + + + + + :/MO/gui/backup:/MO/gui/backup + + + + + @@ -725,13 +828,6 @@ p, li { white-space: pre-wrap; } - - - - Sort - - -
    diff --git a/src/modlist.cpp b/src/modlist.cpp index e2cb7cf0..836406e4 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -669,7 +669,7 @@ bool ModList::dropURLs(const QMimeData *mimeData, int row, const QModelIndex &pa } if (source.count() != 0) { - shellMove(source, target, NULL); + shellMove(source, target); } return true; diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index af6f42da..1f4ed9b1 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -78,7 +78,6 @@ PluginList::PluginList(QObject *parent) : QAbstractItemModel(parent) , m_FontMetrics(QFont()) , m_SaveTimer(this) - , m_BOSS(NULL) { m_SaveTimer.setSingleShot(true); connect(&m_SaveTimer, SIGNAL(timeout()), this, SIGNAL(saveTimer())); @@ -95,11 +94,6 @@ PluginList::PluginList(QObject *parent) PluginList::~PluginList() { - if (m_BOSS != NULL) { - m_BOSS->CleanUpAPI(); - delete m_BOSS; - m_BOSS = NULL; - } } @@ -595,225 +589,9 @@ void PluginList::refreshLoadOrder() } -class boss_exception : public std::runtime_error { -public: - boss_exception(const std::string &message) : std::runtime_error(message) {} -}; - -#define THROW_BOSS_ERROR(obj) \ - uint8_t *message; \ - obj->GetLastErrorDetails(&message); \ - throw boss_exception(std::string(reinterpret_cast(message))); - -#define U8(text) reinterpret_cast(text) - - -void outputBossLog(const QString &filename) -{ - QFile file(filename); - if (file.open(QIODevice::ReadOnly)) { - while (!file.atEnd()) { - QByteArray line = file.readLine().trimmed(); - if (line.length() < 1) - continue; - - int endFirstWord = line.indexOf(':'); - if (endFirstWord < 0) { - endFirstWord = 0; - } - QString firstWord = line.mid(0, endFirstWord); - if (firstWord == "DEBUG") { - qDebug("(boss) %s", line.mid(endFirstWord + 2).constData()); - } else { - qWarning("(boss) %s", line.mid(endFirstWord + 2).constData()); - } - } - } - file.resize(0); -} - - -boss_db PluginList::initBoss() -{ - boss_db result; - bool firstRun = (m_BOSS == nullptr); - if (firstRun) { - m_BOSS = new BossDLL(TEXT("dlls\\boss.dll")); - - if (!m_BOSS->IsCompatibleVersion(2,1,1)) { - throw MyException(tr("BOSS dll incompatible")); - } - uint8_t *versionString; - if (m_BOSS->GetVersionString(&versionString) != BossDLL::RESULT_OK) { - THROW_BOSS_ERROR(m_BOSS) - } - qDebug("using boss version %s", versionString); - - m_TempFile.open(); m_TempFile.close(); // yeah, stupid, but open is required to generate the name - m_BOSS->SetLoggerOutput(m_TempFile.fileName().toLocal8Bit().constData(), 4); - } - - if (m_BOSS->CreateBossDb(&result, BossDLL::SKYRIM, NULL) != BossDLL::RESULT_OK) { - uint8_t *message; - m_BOSS->GetLastErrorDetails(&message); - std::string messageCopy(reinterpret_cast(message)); - delete m_BOSS; - m_BOSS = NULL; - throw boss_exception(messageCopy); - } - qApp->processEvents(); - - QString masterlistName = QDir::toNativeSeparators(qApp->applicationDirPath() + "/boss/masterlist.txt"); - - if (firstRun) { - uint32_t res = m_BOSS->UpdateMasterlist(result, U8(masterlistName.toUtf8().constData())); - qApp->processEvents(); - if (res == BossDLL::RESULT_OK) { - qDebug("boss masterlist updated"); - } else if (res == BossDLL::RESULT_NO_UPDATE_NECESSARY) { - qDebug("boss masterlist already up-to-date"); - } else { - THROW_BOSS_ERROR(m_BOSS) - } - } - if (m_BOSS->Load(result, - U8(masterlistName.toUtf8().constData()), - U8(QDir::toNativeSeparators(qApp->applicationDirPath() + "/boss/userlist.txt").toUtf8().constData())) != BossDLL::RESULT_OK) { - THROW_BOSS_ERROR(m_BOSS) - } - return result; -} - -void PluginList::convertPluginListForBoss(boss_db db, boost::ptr_vector &inputPlugins, std::vector &activePlugins) -{ - foreach (int idx, m_ESPsByPriority) { - QString fileName = m_ESPs[idx].m_Name; - QByteArray name = fileName.toUtf8(); - - uint8_t *nameU8 = new uint8_t[name.length() + 1]; - memcpy(nameU8, name.constData(), name.length() + 1); - if (m_ESPs[idx].m_Enabled) { - activePlugins.push_back(nameU8); - } - inputPlugins.push_back(nameU8); - } - if (m_BOSS->SetActivePluginsDumb(db, &activePlugins[0], activePlugins.size()) != BossDLL::RESULT_OK) { - THROW_BOSS_ERROR(m_BOSS) - } -} - -void PluginList::applyBOSSSorting(boss_db db, std::map &lockedLoadOrder, uint8_t **pluginList, size_t size, - int &priority, int &loadOrder, bool recognized, const char *extension) -{ - for (size_t i = 0; i < size; ++i) { - QString name = QString::fromUtf8(reinterpret_cast(pluginList[i])).toLower(); - if (name.endsWith(extension)) { - auto iter = m_ESPsByName.find(name); - if (iter == m_ESPsByName.end()) { - // boss seems to report plugins from userlist as sorted that aren't even installed - continue; - } - - BossMessage *message; - size_t numMessages = 0; - m_BOSS->GetPluginMessages(db, pluginList[i], &message, &numMessages); - BossInfo newInfo; - for (size_t im = 0; im < numMessages; ++im) { - newInfo.m_BOSSMessages.append(QString::fromUtf8(reinterpret_cast(message[im].message))); - } - newInfo.m_BOSSUnrecognized = !recognized; - m_BossInfo[name] = newInfo; - - // locked order plugins are not inserted by boss sorting ... - if (m_LockedOrder.find(name) != m_LockedOrder.end()) { - continue; - } - - // ... but by their enforced priority - while (lockedLoadOrder.find(loadOrder) != lockedLoadOrder.end()) { - auto lloIter = lockedLoadOrder.find(loadOrder); - auto nameIter = m_ESPsByName.find(lloIter->second); - if (nameIter != m_ESPsByName.end()) { - m_ESPs[nameIter->second].m_Priority = priority++; - if (m_ESPs[nameIter->second].m_Enabled) { - m_ESPs[nameIter->second].m_LoadOrder = loadOrder++; - } else { - m_ESPs[nameIter->second].m_LoadOrder = -1; - } - } - lockedLoadOrder.erase(lloIter); - } - - m_ESPs[iter->second].m_Priority = priority++; - if (m_ESPs[iter->second].m_Enabled) { - m_ESPs[iter->second].m_LoadOrder = loadOrder++; - } else { - m_ESPs[iter->second].m_LoadOrder = -1; - } - } - } -} - -void PluginList::bossSort() +void PluginList::lootSort() { - boss_db db = initBoss(); - ON_BLOCK_EXIT([&] { m_BOSS->DestroyBossDb(db); }); - - // create a boss-compatible representation of our current mod list. - boost::ptr_vector inputPlugins; - std::vector activePlugins; - convertPluginListForBoss(db, inputPlugins, activePlugins); - - // sort mods in-memory - uint8_t **sortedPlugins; - uint8_t **unrecognizedPlugins; - size_t sizeSorted, sizeUnrecognized; - if (m_BOSS->SortCustomMods(db, - inputPlugins.c_array(), inputPlugins.size(), - &sortedPlugins, &sizeSorted, - &unrecognizedPlugins, &sizeUnrecognized) != BossDLL::RESULT_OK) { - THROW_BOSS_ERROR(m_BOSS) - } - - // output the log from boss to our own log to make it visible - outputBossLog(m_TempFile.fileName()); - - qDebug("%d sorted, %d unrecognized", sizeSorted, sizeUnrecognized); - ChangeBracket layoutChange(this); - - std::map lockedLoadOrder; - std::for_each(m_LockedOrder.begin(), m_LockedOrder.end(), - [&lockedLoadOrder] (const std::pair &ele) { lockedLoadOrder[ele.second] = ele.first; }); - - int priority = 0; - int loadOrder = 0; - applyBOSSSorting(db, lockedLoadOrder, sortedPlugins, sizeSorted, priority, loadOrder, true, "esm"); - applyBOSSSorting(db, lockedLoadOrder, unrecognizedPlugins, sizeUnrecognized, priority, loadOrder, false, "esm"); - applyBOSSSorting(db, lockedLoadOrder, sortedPlugins, sizeSorted, priority, loadOrder, true, "esp"); - applyBOSSSorting(db, lockedLoadOrder, unrecognizedPlugins, sizeUnrecognized, priority, loadOrder, false, "esp"); - - // applyBOSSSorting removed entries from lockedLoadOrder when they were inserted so everything that's left is plugins - // locked to the end of the list. Now this inserts the rest in the ascending priority order. If the list is too short - // to place the plugins in their intended position then this guarantees plugins are kept in the correct relative order - // but it doesn't minimize the number of misplaced plugins. - for (auto iter = lockedLoadOrder.begin(); iter != lockedLoadOrder.end(); ++iter) { - auto nameIter = m_ESPsByName.find(iter->second); - if (nameIter != m_ESPsByName.end()) { - m_ESPs[nameIter->second].m_Priority = priority++; - if (m_ESPs[nameIter->second].m_Enabled) { - m_ESPs[nameIter->second].m_LoadOrder = loadOrder++; - } else { - m_ESPs[nameIter->second].m_LoadOrder = -1; - } - } - } - - // inform view of the changed data - updateIndices(); - layoutChange.finish(); - emit dataChanged(this->index(0, 0), this->index(m_ESPs.size(), columnCount())); - m_Refreshed(); } IPluginList::PluginState PluginList::state(const QString &name) const diff --git a/src/pluginlist.h b/src/pluginlist.h index fff2a595..5e8557e2 100644 --- a/src/pluginlist.h +++ b/src/pluginlist.h @@ -181,7 +181,7 @@ public: void refreshLoadOrder(); - void bossSort(); + void lootSort(); public: virtual PluginState state(const QString &name) const; @@ -204,7 +204,6 @@ public: // implementation of the QAbstractTableModel interface virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; virtual QModelIndex parent(const QModelIndex &child) const; - void applyBOSSSorting(boss_db db, std::map &lockedLoadOrder, uint8_t **pluginList, size_t size, int &priority, int &loadOrder, bool recognized, const char *extension); public slots: /** @@ -260,82 +259,6 @@ private: friend bool ByDate(const ESPInfo& LHS, const ESPInfo& RHS); friend bool ByPriority(const ESPInfo& LHS, const ESPInfo& RHS); - class BossDLL : public PDLL { - DECLARE_CLASS(BossDLL) - - DECLARE_FUNCTION3(__cdecl, uint32_t, CreateBossDb, boss_db*, const uint32_t, const uint8_t*) - DECLARE_FUNCTION1(__cdecl, void, DestroyBossDb, boss_db) - DECLARE_FUNCTION0(__cdecl, void, CleanUpAPI) - - DECLARE_FUNCTION7(__cdecl, uint32_t, SortCustomMods, boss_db, uint8_t**, size_t, uint8_t***, size_t*, uint8_t***, size_t*) - DECLARE_FUNCTION3(__cdecl, uint32_t, SetActivePluginsDumb, boss_db, uint8_t**, const size_t) - - DECLARE_FUNCTION3(__cdecl, uint32_t, GetActivePluginsDumb , boss_db, uint8_t***, size_t*) - - DECLARE_FUNCTION2(__cdecl, uint32_t, UpdateMasterlist, boss_db, const uint8_t*) - DECLARE_FUNCTION3(__cdecl, uint32_t, Load, boss_db, const uint8_t*, const uint8_t*) - - DECLARE_FUNCTION2(__cdecl, void, SetLoggerOutput, const char*, uint8_t) - DECLARE_FUNCTION1(__cdecl, uint32_t, GetLastErrorDetails, uint8_t**) - - DECLARE_FUNCTION1(__cdecl, uint32_t, GetVersionString, uint8_t**) - DECLARE_FUNCTION3(__cdecl, bool, IsCompatibleVersion, const uint32_t, const uint32_t, const uint32_t) - - DECLARE_FUNCTION4(__cdecl, uint32_t, GetPluginMessages, boss_db, const uint8_t*, BossMessage**, size_t*) - - enum ResultCode { - RESULT_OK = 0, - RESULT_NO_MASTER_FILE = 1, - RESULT_FILE_READ_FAIL = 2, - RESULT_FILE_WRITE_FAIL = 3, - RESULT_FILE_NOT_UTF8 = 4, - RESULT_FILE_NOT_FOUND = 5, - RESULT_FILE_PARSE_FAIL = 6, - RESULT_CONDITION_EVAL_FAIL = 7, - RESULT_REGEX_EVAL_FAIL = 8, - RESULT_NO_GAME_DETECTED = 9, - RESULT_ENCODING_CONVERSION_FAIL = 10, - RESULT_FIND_ONLINE_MASTERLIST_REVISION_FAIL = 11, - RESULT_FIND_ONLINE_MASTERLIST_DATE_FAIL = 12, - RESULT_READ_UPDATE_FILE_LIST_FAIL = 13, - RESULT_FILE_CRC_MISMATCH = 14, - RESULT_FS_FILE_MOD_TIME_READ_FAIL = 15, - RESULT_FS_FILE_MOD_TIME_WRITE_FAIL = 16, - RESULT_FS_FILE_RENAME_FAIL = 17, - RESULT_FS_FILE_DELETE_FAIL = 18, - RESULT_FS_CREATE_DIRECTORY_FAIL = 19, - RESULT_FS_ITER_DIRECTORY_FAIL = 20, - RESULT_CURL_INIT_FAIL = 21, - RESULT_CURL_SET_ERRBUFF_FAIL = 22, - RESULT_CURL_SET_OPTION_FAIL = 23, - RESULT_CURL_SET_PROXY_FAIL = 24, - RESULT_CURL_SET_PROXY_TYPE_FAIL = 25, - RESULT_CURL_SET_PROXY_AUTH_FAIL = 26, - RESULT_CURL_SET_PROXY_AUTH_TYPE_FAIL = 27, - RESULT_CURL_PERFORM_FAIL = 28, - RESULT_CURL_USER_CANCEL = 29, - RESULT_GUI_WINDOW_INIT_FAIL = 30, - RESULT_NO_UPDATE_NECESSARY = 31, - RESULT_LO_MISMATCH = 32, - RESULT_NO_MEM = 33, - RESULT_INVALID_ARGS = 34, - RESULT_NETWORK_FAIL = 35, - RESULT_NO_INTERNET_CONNECTION = 36, - RESULT_NO_TAG_MAP = 37, - RESULT_PLUGINS_FULL = 38, - RESULT_PLUGIN_BEFORE_MASTER = 39, - RESULT_INVALID_SYNTAX = 40 - }; - - enum GameIDs { - AUTODETECT = 0, - OBLIVION = 1, - SKYRIM = 3, - FALLOUT3 = 4, - FALLOUTNV = 5 - }; - }; - private: void syncLoadOrder(); @@ -353,9 +276,6 @@ private: void testMasters(); - boss_db initBoss(); - void convertPluginListForBoss(boss_db db, boost::ptr_vector &inputPlugins, std::vector &activePlugins); - private: std::vector m_ESPs; @@ -381,7 +301,6 @@ private: SignalRefreshed m_Refreshed; - BossDLL *m_BOSS; QTemporaryFile m_TempFile; }; diff --git a/src/profile.h b/src/profile.h index 4960671a..5aa77357 100644 --- a/src/profile.h +++ b/src/profile.h @@ -159,6 +159,11 @@ public: */ QString getLockedOrderFileName() const; + /** + * @return the path of the modlist file in this profile + */ + QString getModlistFileName() const; + /** * @return path of the archives file in this profile */ @@ -301,7 +306,6 @@ private: void updateIndices(); - QString getModlistFileName() const; void copyFilesTo(QString &target) const; std::vector splitDZString(const wchar_t *buffer) const; diff --git a/src/resources.qrc b/src/resources.qrc index 73921f64..bf53ea95 100644 --- a/src/resources.qrc +++ b/src/resources.qrc @@ -52,5 +52,8 @@ 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 diff --git a/src/resources/arrange-boxes.png b/src/resources/arrange-boxes.png new file mode 100644 index 00000000..b1ab67cf Binary files /dev/null and b/src/resources/arrange-boxes.png differ diff --git a/src/resources/document-save_32.png b/src/resources/document-save_32.png new file mode 100644 index 00000000..db5c52b7 Binary files /dev/null and b/src/resources/document-save_32.png differ diff --git a/src/resources/edit-undo.png b/src/resources/edit-undo.png new file mode 100644 index 00000000..61b2ce9a Binary files /dev/null and b/src/resources/edit-undo.png differ diff --git a/src/selectiondialog.cpp b/src/selectiondialog.cpp index dbc10791..902b4d9c 100644 --- a/src/selectiondialog.cpp +++ b/src/selectiondialog.cpp @@ -17,62 +17,67 @@ You should have received a copy of the GNU General Public License along with Mod Organizer. If not, see . */ -#include "selectiondialog.h" -#include "ui_selectiondialog.h" - -#include - -SelectionDialog::SelectionDialog(const QString &description, QWidget *parent) - : QDialog(parent), ui(new Ui::SelectionDialog), m_Choice(NULL), m_ValidateByData(false) -{ - ui->setupUi(this); - - ui->descriptionLabel->setText(description); -} - -SelectionDialog::~SelectionDialog() -{ - delete ui; -} - - -void SelectionDialog::addChoice(const QString &buttonText, const QString &description, const QVariant &data) -{ - QCommandLinkButton *button = new QCommandLinkButton(buttonText, description, ui->buttonBox); - button->setProperty("data", data); - ui->buttonBox->addButton(button, QDialogButtonBox::AcceptRole); - if (data.isValid()) m_ValidateByData = true; -} - - -QVariant SelectionDialog::getChoiceData() -{ - return m_Choice->property("data"); -} - - -QString SelectionDialog::getChoiceString() -{ - if ((m_Choice == NULL) || - (m_ValidateByData && !m_Choice->property("data").isValid())) { - return QString(); - } else { - return m_Choice->text(); - } -} - - -void SelectionDialog::on_buttonBox_clicked(QAbstractButton *button) -{ - m_Choice = button; - if (!m_ValidateByData || m_Choice->property("data").isValid()) { - this->accept(); - } else { - this->reject(); - } -} - -void SelectionDialog::on_cancelButton_clicked() -{ - this->reject(); -} +#include "selectiondialog.h" +#include "ui_selectiondialog.h" + +#include + +SelectionDialog::SelectionDialog(const QString &description, QWidget *parent) + : QDialog(parent), ui(new Ui::SelectionDialog), m_Choice(NULL), m_ValidateByData(false) +{ + ui->setupUi(this); + + ui->descriptionLabel->setText(description); +} + +SelectionDialog::~SelectionDialog() +{ + delete ui; +} + + +void SelectionDialog::addChoice(const QString &buttonText, const QString &description, const QVariant &data) +{ + QCommandLinkButton *button = new QCommandLinkButton(buttonText, description, ui->buttonBox); + button->setProperty("data", data); + ui->buttonBox->addButton(button, QDialogButtonBox::AcceptRole); + if (data.isValid()) m_ValidateByData = true; +} + +int SelectionDialog::numChoices() const +{ + return ui->buttonBox->findChildren(QString()).count(); +} + + +QVariant SelectionDialog::getChoiceData() +{ + return m_Choice->property("data"); +} + + +QString SelectionDialog::getChoiceString() +{ + if ((m_Choice == NULL) || + (m_ValidateByData && !m_Choice->property("data").isValid())) { + return QString(); + } else { + return m_Choice->text(); + } +} + + +void SelectionDialog::on_buttonBox_clicked(QAbstractButton *button) +{ + m_Choice = button; + if (!m_ValidateByData || m_Choice->property("data").isValid()) { + this->accept(); + } else { + this->reject(); + } +} + +void SelectionDialog::on_cancelButton_clicked() +{ + this->reject(); +} diff --git a/src/selectiondialog.h b/src/selectiondialog.h index a2844e97..fc04291e 100644 --- a/src/selectiondialog.h +++ b/src/selectiondialog.h @@ -17,50 +17,52 @@ You should have received a copy of the GNU General Public License along with Mod Organizer. If not, see . */ -#ifndef SELECTIONDIALOG_H -#define SELECTIONDIALOG_H - -#include -#include - -namespace Ui { -class SelectionDialog; -} - -class SelectionDialog : public QDialog -{ - Q_OBJECT - -public: - - explicit SelectionDialog(const QString &description, QWidget *parent = 0); - - ~SelectionDialog(); - - /** - * @brief add a choice to the dialog - * @param buttonText the text to be displayed on the button - * @param description the description that shows up under in small letters inside the button - * @param data data to be stored with the button. Please note that as soon as one choice has data associated with it (non-invalid QVariant) - * all buttons that contain no data will be treated as "cancel" buttons - */ - void addChoice(const QString &buttonText, const QString &description, const QVariant &data); - - QVariant getChoiceData(); - QString getChoiceString(); - -private slots: - - void on_buttonBox_clicked(QAbstractButton *button); - - void on_cancelButton_clicked(); - -private: - - Ui::SelectionDialog *ui; - QAbstractButton *m_Choice; - bool m_ValidateByData; - -}; - -#endif // SELECTIONDIALOG_H +#ifndef SELECTIONDIALOG_H +#define SELECTIONDIALOG_H + +#include +#include + +namespace Ui { +class SelectionDialog; +} + +class SelectionDialog : public QDialog +{ + Q_OBJECT + +public: + + explicit SelectionDialog(const QString &description, QWidget *parent = 0); + + ~SelectionDialog(); + + /** + * @brief add a choice to the dialog + * @param buttonText the text to be displayed on the button + * @param description the description that shows up under in small letters inside the button + * @param data data to be stored with the button. Please note that as soon as one choice has data associated with it (non-invalid QVariant) + * all buttons that contain no data will be treated as "cancel" buttons + */ + void addChoice(const QString &buttonText, const QString &description, const QVariant &data); + + int numChoices() const; + + QVariant getChoiceData(); + QString getChoiceString(); + +private slots: + + void on_buttonBox_clicked(QAbstractButton *button); + + void on_cancelButton_clicked(); + +private: + + Ui::SelectionDialog *ui; + QAbstractButton *m_Choice; + bool m_ValidateByData; + +}; + +#endif // SELECTIONDIALOG_H diff --git a/src/shared/gameinfo.cpp b/src/shared/gameinfo.cpp index 00bb42fd..b580a226 100644 --- a/src/shared/gameinfo.cpp +++ b/src/shared/gameinfo.cpp @@ -172,6 +172,14 @@ std::wstring GameInfo::getLogDir() const } +std::wstring GameInfo::getLootDir() const +{ + std::wostringstream temp; + temp << m_OrganizerDirectory << "\\loot"; + return temp.str(); +} + + std::wstring GameInfo::getTutorialDir() const { std::wostringstream temp; diff --git a/src/shared/gameinfo.h b/src/shared/gameinfo.h index 10775e6c..89c9402d 100644 --- a/src/shared/gameinfo.h +++ b/src/shared/gameinfo.h @@ -113,6 +113,7 @@ public: virtual std::wstring getCacheDir() const; virtual std::wstring getOverwriteDir() const; virtual std::wstring getLogDir() const; + virtual std::wstring getLootDir() const; virtual std::wstring getTutorialDir() const; virtual bool requiresBSAInvalidation() const { return true; } diff --git a/src/spawn.cpp b/src/spawn.cpp index b1c5e963..6adafba0 100644 --- a/src/spawn.cpp +++ b/src/spawn.cpp @@ -36,10 +36,23 @@ using namespace MOShared; static const int BUFSIZE = 4096; -bool spawn(LPCWSTR binary, LPCWSTR arguments, LPCWSTR currentDirectory, bool suspended, HANDLE& processHandle, HANDLE& threadHandle) +bool spawn(LPCWSTR binary, LPCWSTR arguments, LPCWSTR currentDirectory, bool suspended, + HANDLE stdOut, HANDLE stdErr, + HANDLE& processHandle, HANDLE& threadHandle) { + BOOL inheritHandles = FALSE; STARTUPINFO si; ::ZeroMemory(&si, sizeof(si)); + if (stdOut != INVALID_HANDLE_VALUE) { + si.hStdOutput = stdOut; + inheritHandles = TRUE; + si.dwFlags |= STARTF_USESTDHANDLES; + } + if (stdErr != INVALID_HANDLE_VALUE) { + si.hStdError = stdErr; + inheritHandles = TRUE; + si.dwFlags |= STARTF_USESTDHANDLES; + } si.cb = sizeof(si); int length = wcslen(binary) + wcslen(arguments) + 4; wchar_t *commandLine = NULL; @@ -74,7 +87,7 @@ bool spawn(LPCWSTR binary, LPCWSTR arguments, LPCWSTR currentDirectory, bool sus BOOL success = ::CreateProcess(NULL, commandLine, NULL, NULL, // no special process or thread attributes - FALSE, // don't inherit handle + inheritHandles, // inherit handles if we plan to use stdout or stderr reroute suspended ? CREATE_SUSPENDED : 0, // create suspended so I have time to inject the DLL NULL, // same environment as parent currentDirectory, // current directory @@ -95,14 +108,22 @@ bool spawn(LPCWSTR binary, LPCWSTR arguments, LPCWSTR currentDirectory, bool sus } -HANDLE startBinary(const QFileInfo &binary, const QString &arguments, const QString& profileName, int logLevel, const QDir ¤tDirectory, bool hooked) +HANDLE startBinary(const QFileInfo &binary, + const QString &arguments, + const QString& profileName, + int logLevel, + const QDir ¤tDirectory, + bool hooked, + HANDLE stdOut, + HANDLE stdErr) { HANDLE processHandle, threadHandle; std::wstring binaryName = ToWString(QDir::toNativeSeparators(binary.absoluteFilePath())); std::wstring currentDirectoryName = ToWString(QDir::toNativeSeparators(currentDirectory.absolutePath())); try { - if (!spawn(binaryName.c_str(), ToWString(arguments).c_str(), currentDirectoryName.c_str(), hooked, processHandle, threadHandle)) { + if (!spawn(binaryName.c_str(), ToWString(arguments).c_str(), currentDirectoryName.c_str(), hooked, + stdOut, stdErr, processHandle, threadHandle)) { reportError(QObject::tr("failed to spawn \"%1\"").arg(binary.fileName())); return INVALID_HANDLE_VALUE; } diff --git a/src/spawn.h b/src/spawn.h index 48320fea..3f037119 100644 --- a/src/spawn.h +++ b/src/spawn.h @@ -52,11 +52,17 @@ private: * @param arguments arguments to pass to the binary * @param profileName name of the active profile * @param currentDirectory the directory to use as the working directory to run in + * @param logLevel log level to be used by the hook library. Ignored if hooked is false * @param hooked if set, the binary is started with mo injected + * @param stdout if not equal to INVALID_HANDLE_VALUE, this is used as stdout for the process + * @param stderr if not equal to INVALID_HANDLE_VALUE, this is used as stderr for the process * @return the process handle * @todo is the profile name even used any more? * @todo is the hooked parameter used? **/ -HANDLE startBinary(const QFileInfo &binary, const QString &arguments, const QString &profileName, int logLevel, const QDir ¤tDirectory, bool hooked); +HANDLE startBinary(const QFileInfo &binary, const QString &arguments, const QString &profileName, int logLevel, + const QDir ¤tDirectory, bool hooked, + HANDLE stdOut = INVALID_HANDLE_VALUE, HANDLE stdErr = INVALID_HANDLE_VALUE); #endif // SPAWN_H + -- cgit v1.3.1