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') 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