diff options
| -rw-r--r-- | src/mainwindow.cpp | 46 | ||||
| -rw-r--r-- | src/mainwindow.h | 4 | ||||
| -rw-r--r-- | src/modinfodialog.cpp | 15 | ||||
| -rw-r--r-- | src/organizer.pro | 13 | ||||
| -rw-r--r-- | src/previewdialog.cpp | 57 | ||||
| -rw-r--r-- | src/previewdialog.h | 36 | ||||
| -rw-r--r-- | src/previewdialog.ui | 90 | ||||
| -rw-r--r-- | src/previewgenerator.cpp | 111 | ||||
| -rw-r--r-- | src/previewgenerator.h | 51 | ||||
| -rw-r--r-- | src/shared/directoryentry.h | 1 |
10 files changed, 408 insertions, 16 deletions
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 <http://www.gnu.org/licenses/>. #include "gameinfoimpl.h" #include "savetextasdialog.h" #include "problemsdialog.h" +#include "previewdialog.h" #include <gameinfo.h> #include <appconfig.h> #include <utility.h> @@ -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 <http://www.gnu.org/licenses/>. #include "pluginlistsortproxy.h" #include "tutorialcontrol.h" #include "savegameinfowidgetgamebryo.h" +#include "previewgenerator.h" #include <guessedvalue.h> #include <directoryentry.h> #include <boost/signals2.hpp> @@ -367,6 +368,8 @@ private: QString m_CurrentLanguage; std::vector<QTranslator*> 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 <QFileInfo>
+
+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 <QDialog>
+
+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 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>PreviewDialog</class>
+ <widget class="QDialog" name="PreviewDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>736</width>
+ <height>583</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Preview</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2" stretch="1,1,0,0">
+ <item>
+ <widget class="QLabel" name="nameLabel">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="modLabel">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="previousButton">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset resource="resources.qrc">
+ <normaloff>:/MO/gui/previous</normaloff>:/MO/gui/previous</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="nextButton">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset resource="resources.qrc">
+ <normaloff>:/MO/gui/next</normaloff>:/MO/gui/next</iconset>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QStackedWidget" name="variantsStack"/>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="closeButton">
+ <property name="text">
+ <string>Close</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources>
+ <include location="resources.qrc"/>
+ </resources>
+ <connections/>
+</ui>
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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "previewgenerator.h"
+#include <QFileInfo>
+#include <QLabel>
+#include <QImageReader>
+#include <QTextEdit>
+#include <QGLPixelBuffer>
+#include <QDesktopWidget>
+#include <utility.h>
+
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef PREVIEWGENERATOR_H
+#define PREVIEWGENERATOR_H
+
+#include <QString>
+#include <QWidget>
+#include <map>
+#include <functional>
+
+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<QString, std::function<QWidget*(const QString&)> > 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<int> &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; }
|
