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/shared/directoryentry.h | 1 + 1 file changed, 1 insertion(+) (limited to 'src/shared/directoryentry.h') 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 14ac234daf1680b0685f6bea3e74aa09dfcf38ef Mon Sep 17 00:00:00 2001 From: Tannin Date: Sat, 14 Dec 2013 13:23:13 +0100 Subject: - plugin list now highlights plugins with attached ini files - bugfix: opening nexus through the globe icon used the nmm.nexusmods.com url --- src/mainwindow.cpp | 2 +- src/pluginlist.cpp | 23 +++++++++++++++++------ src/pluginlist.h | 4 ++-- src/resources.qrc | 1 + src/resources/mail-attachment.png | Bin 0 -> 649 bytes src/shared/directoryentry.cpp | 4 ++-- src/shared/directoryentry.h | 9 ++++----- src/shared/util.cpp | 3 --- 8 files changed, 27 insertions(+), 19 deletions(-) create mode 100644 src/resources/mail-attachment.png (limited to 'src/shared/directoryentry.h') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 17f2de09..3a543e0b 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -4031,7 +4031,7 @@ void MainWindow::on_actionNexus_triggered() ::ShellExecuteW(NULL, L"open", nxmPath.c_str(), (std::wstring(L"reg ") + GameInfo::instance().getGameShortName() + L" \"" + executable + L"\"").c_str(), NULL, SW_SHOWNORMAL); - ::ShellExecuteW(NULL, L"open", GameInfo::instance().getNexusPage().c_str(), NULL, NULL, SW_SHOWNORMAL); + ::ShellExecuteW(NULL, L"open", GameInfo::instance().getNexusPage(false).c_str(), NULL, NULL, SW_SHOWNORMAL); ui->tabWidget->setCurrentIndex(4); } diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index 8eeed76e..20d4d357 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -146,7 +146,11 @@ void PluginList::refresh(const QString &profileName, const DirectoryEntry &baseD bool archive = false; try { FilesOrigin &origin = baseDirectory.getOriginByID(current->getOrigin(archive)); - m_ESPs.push_back(ESPInfo(filename, forceEnabled, current->getFileTime(), ToQString(origin.getName()), ToQString(current->getFullPath()))); + + QString iniPath = QFileInfo(filename).baseName() + ".ini"; + bool hasIni = baseDirectory.findFile(ToWString(iniPath)).get() != NULL; + + m_ESPs.push_back(ESPInfo(filename, forceEnabled, current->getFileTime(), ToQString(origin.getName()), ToQString(current->getFullPath()), hasIni)); } catch (const std::exception &e) { reportError(tr("failed to update esp info for file %1 (source id: %2), error: %3").arg(filename).arg(current->getOrigin(archive)).arg(e.what())); } @@ -724,7 +728,9 @@ 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) { + } 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(); @@ -759,7 +765,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) { + if (m_ESPs[index].m_HasIni) { + text += "
There is an ini file connected to this esp. Its settings will be added to your game settings, overwriting " + "in case of conflicts."; + } else 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."; } @@ -1021,9 +1030,11 @@ bool PluginList::eventFilter(QObject *obj, QEvent *event) } -PluginList::ESPInfo::ESPInfo(const QString &name, bool enabled, FILETIME time, const QString &originName, const QString &fullPath) - : m_Name(name), m_Enabled(enabled), m_ForceEnabled(enabled), m_Removed(false), m_Priority(0), - m_LoadOrder(-1), m_Time(time), m_OriginName(originName) +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) { try { ESP::File file(ToWString(fullPath)); diff --git a/src/pluginlist.h b/src/pluginlist.h index 2f1a3f5f..64c914df 100644 --- a/src/pluginlist.h +++ b/src/pluginlist.h @@ -131,7 +131,6 @@ public: */ 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; @@ -193,7 +192,7 @@ private: struct ESPInfo { - ESPInfo(const QString &name, bool enabled, FILETIME time, const QString &originName, const QString &fullPath); + 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; @@ -205,6 +204,7 @@ private: QString m_OriginName; bool m_IsMaster; bool m_IsDummy; + bool m_HasIni; std::set m_Masters; mutable std::set m_MasterUnset; }; diff --git a/src/resources.qrc b/src/resources.qrc index 3f27e2cc..1582a3f2 100644 --- a/src/resources.qrc +++ b/src/resources.qrc @@ -51,5 +51,6 @@ resources/accessories-text-editor.png resources/x-office-calendar.png resources/dialog-warning_16.png + resources/mail-attachment.png diff --git a/src/resources/mail-attachment.png b/src/resources/mail-attachment.png new file mode 100644 index 00000000..529bb7f5 Binary files /dev/null and b/src/resources/mail-attachment.png differ diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp index 685e14a9..a232ea19 100644 --- a/src/shared/directoryentry.cpp +++ b/src/shared/directoryentry.cpp @@ -759,7 +759,7 @@ DirectoryEntry *DirectoryEntry::findSubDirectoryRecursive(const std::wstring &pa } -const FileEntry::Ptr DirectoryEntry::findFile(const std::wstring &name) +const FileEntry::Ptr DirectoryEntry::findFile(const std::wstring &name) const { auto iter = m_Files.find(name); if (iter != m_Files.end()) { @@ -840,7 +840,7 @@ FileEntry::Ptr FileRegister::createFile(const std::wstring &name, DirectoryEntry } -FileEntry::Ptr FileRegister::getFile(FileEntry::Index index) +FileEntry::Ptr FileRegister::getFile(FileEntry::Index index) const { auto iter = m_Files.find(index); if (iter != m_Files.end()) { diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h index 1ad9816c..22f00a74 100644 --- a/src/shared/directoryentry.h +++ b/src/shared/directoryentry.h @@ -168,7 +168,7 @@ public: bool indexValid(FileEntry::Index index) const; FileEntry::Ptr createFile(const std::wstring &name, DirectoryEntry *parent); - FileEntry::Ptr getFile(FileEntry::Index index); + FileEntry::Ptr getFile(FileEntry::Index index) const; void removeFile(FileEntry::Index index); void removeOrigin(FileEntry::Index index, int originID); @@ -233,11 +233,10 @@ public: * @param name name of the file * @return fileentry object for the file or NULL if no file matches */ - const FileEntry::Ptr findFile(const std::wstring &name); + const FileEntry::Ptr findFile(const std::wstring &name) const; - /** search through this directory and all subdirectories for a file by the specified name. - if directory is not NULL, the referenced variable will be set to true if the path refers to a directory. - the returned pointer is NULL in that case */ + /** search through this directory and all subdirectories for a file by the specified name (relative path). + if directory is not NULL, the referenced variable will be set to the path containing the file */ const FileEntry::Ptr searchFile(const std::wstring &path, const DirectoryEntry **directory) const; void insertFile(const std::wstring &filePath, FilesOrigin &origin, FILETIME fileTime); diff --git a/src/shared/util.cpp b/src/shared/util.cpp index 4378e03c..22657e6c 100644 --- a/src/shared/util.cpp +++ b/src/shared/util.cpp @@ -69,7 +69,6 @@ std::string ToString(const std::wstring &source, bool utf8) ::WideCharToMultiByte(CP_UTF8, 0, source.c_str(), -1, buffer, MAX_PATH, NULL, NULL); } else { ::WideCharToMultiByte(GetACP(), 0, source.c_str(), -1, buffer, MAX_PATH, NULL, NULL); -// wcstombs(buffer, source.c_str(), MAX_PATH); } return std::string(buffer); } @@ -80,9 +79,7 @@ std::wstring ToWString(const std::string &source, bool utf8) if (utf8) { ::MultiByteToWideChar(CP_UTF8, 0, source.c_str(), -1, buffer, MAX_PATH); } else { - // codepage encoding ::MultiByteToWideChar(GetACP(), 0, source.c_str(), -1, buffer, MAX_PATH); -// mbstowcs(buffer, source.c_str(), MAX_PATH); } return std::wstring(buffer); } -- 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/shared/directoryentry.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/shared/directoryentry.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