Merge remote-tracking branch 'scrawl/wizard'
@ -0,0 +1,269 @@
|
||||
#include "settingspage.hpp"
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
|
||||
#include <components/files/configurationmanager.hpp>
|
||||
|
||||
#include <components/config/gamesettings.hpp>
|
||||
#include <components/config/launchersettings.hpp>
|
||||
|
||||
#include "utils/textinputdialog.hpp"
|
||||
|
||||
using namespace Process;
|
||||
|
||||
Launcher::SettingsPage::SettingsPage(Files::ConfigurationManager &cfg,
|
||||
Config::GameSettings &gameSettings,
|
||||
Config::LauncherSettings &launcherSettings, MainDialog *parent)
|
||||
: mCfgMgr(cfg)
|
||||
, mGameSettings(gameSettings)
|
||||
, mLauncherSettings(launcherSettings)
|
||||
, QWidget(parent)
|
||||
, mMain(parent)
|
||||
{
|
||||
setupUi(this);
|
||||
|
||||
QStringList languages;
|
||||
languages << QLatin1String("English")
|
||||
<< QLatin1String("French")
|
||||
<< QLatin1String("German")
|
||||
<< QLatin1String("Italian")
|
||||
<< QLatin1String("Polish")
|
||||
<< QLatin1String("Russian")
|
||||
<< QLatin1String("Spanish");
|
||||
|
||||
languageComboBox->addItems(languages);
|
||||
|
||||
mWizardInvoker = new ProcessInvoker();
|
||||
mImporterInvoker = new ProcessInvoker();
|
||||
|
||||
connect(mWizardInvoker->getProcess(), SIGNAL(started()),
|
||||
this, SLOT(wizardStarted()));
|
||||
|
||||
connect(mWizardInvoker->getProcess(), SIGNAL(finished(int,QProcess::ExitStatus)),
|
||||
this, SLOT(wizardFinished(int,QProcess::ExitStatus)));
|
||||
|
||||
connect(mImporterInvoker->getProcess(), SIGNAL(started()),
|
||||
this, SLOT(importerStarted()));
|
||||
|
||||
connect(mImporterInvoker->getProcess(), SIGNAL(finished(int,QProcess::ExitStatus)),
|
||||
this, SLOT(importerFinished(int,QProcess::ExitStatus)));
|
||||
|
||||
mProfileDialog = new TextInputDialog(tr("New Profile"), tr("Profile name:"), this);
|
||||
|
||||
connect(mProfileDialog->lineEdit(), SIGNAL(textChanged(QString)),
|
||||
this, SLOT(updateOkButton(QString)));
|
||||
|
||||
// Detect Morrowind configuration files
|
||||
QStringList iniPaths;
|
||||
|
||||
foreach (const QString &path, mGameSettings.getDataDirs()) {
|
||||
QDir dir(path);
|
||||
dir.setPath(dir.canonicalPath()); // Resolve symlinks
|
||||
|
||||
if (dir.exists(QString("Morrowind.ini")))
|
||||
iniPaths.append(dir.absoluteFilePath(QString("Morrowind.ini")));
|
||||
else
|
||||
{
|
||||
if (!dir.cdUp())
|
||||
continue; // Cannot move from Data Files
|
||||
|
||||
if (dir.exists(QString("Morrowind.ini")))
|
||||
iniPaths.append(dir.absoluteFilePath(QString("Morrowind.ini")));
|
||||
}
|
||||
}
|
||||
|
||||
if (!iniPaths.isEmpty()) {
|
||||
settingsComboBox->addItems(iniPaths);
|
||||
importerButton->setEnabled(true);
|
||||
} else {
|
||||
importerButton->setEnabled(false);
|
||||
}
|
||||
|
||||
loadSettings();
|
||||
}
|
||||
|
||||
Launcher::SettingsPage::~SettingsPage()
|
||||
{
|
||||
delete mWizardInvoker;
|
||||
delete mImporterInvoker;
|
||||
}
|
||||
|
||||
void Launcher::SettingsPage::on_wizardButton_clicked()
|
||||
{
|
||||
saveSettings();
|
||||
|
||||
if (!mWizardInvoker->startProcess(QLatin1String("openmw-wizard"), false))
|
||||
return;
|
||||
}
|
||||
|
||||
void Launcher::SettingsPage::on_importerButton_clicked()
|
||||
{
|
||||
saveSettings();
|
||||
|
||||
// Create the file if it doesn't already exist, else the importer will fail
|
||||
QString path(QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str()));
|
||||
path.append(QLatin1String("openmw.cfg"));
|
||||
QFile file(path);
|
||||
|
||||
if (!file.exists()) {
|
||||
if (!file.open(QIODevice::ReadWrite)) {
|
||||
// File cannot be created
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("Error writing OpenMW configuration file"));
|
||||
msgBox.setIcon(QMessageBox::Critical);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setText(tr("<html><head/><body><p><b>Could not open or create %1 for writing </b></p> \
|
||||
<p>Please make sure you have the right permissions \
|
||||
and try again.</p></body></html>").arg(file.fileName()));
|
||||
msgBox.exec();
|
||||
return;
|
||||
}
|
||||
|
||||
file.close();
|
||||
}
|
||||
|
||||
// Construct the arguments to run the importer
|
||||
QStringList arguments;
|
||||
|
||||
if (addonsCheckBox->isChecked())
|
||||
arguments.append(QString("--game-files"));
|
||||
|
||||
arguments.append(QString("--encoding"));
|
||||
arguments.append(mGameSettings.value(QString("encoding"), QString("win1252")));
|
||||
arguments.append(QString("--ini"));
|
||||
arguments.append(settingsComboBox->currentText());
|
||||
arguments.append(QString("--cfg"));
|
||||
arguments.append(path);
|
||||
|
||||
qDebug() << "arguments " << arguments;
|
||||
|
||||
if (!mImporterInvoker->startProcess(QLatin1String("mwiniimport"), arguments, false))
|
||||
return;
|
||||
}
|
||||
|
||||
void Launcher::SettingsPage::on_browseButton_clicked()
|
||||
{
|
||||
QString iniFile = QFileDialog::getOpenFileName(
|
||||
this,
|
||||
QObject::tr("Select configuration file"),
|
||||
QDir::currentPath(),
|
||||
QString(tr("Morrowind configuration file (*.ini)")));
|
||||
|
||||
|
||||
if (iniFile.isEmpty())
|
||||
return;
|
||||
|
||||
QFileInfo info(iniFile);
|
||||
|
||||
if (!info.exists() || !info.isReadable())
|
||||
return;
|
||||
|
||||
const QString path(QDir::toNativeSeparators(info.absoluteFilePath()));
|
||||
|
||||
if (settingsComboBox->findText(path) == -1) {
|
||||
settingsComboBox->addItem(path);
|
||||
settingsComboBox->setCurrentIndex(settingsComboBox->findText(path));
|
||||
importerButton->setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
void Launcher::SettingsPage::wizardStarted()
|
||||
{
|
||||
mMain->hide(); // Hide the launcher
|
||||
|
||||
wizardButton->setEnabled(false);
|
||||
}
|
||||
|
||||
void Launcher::SettingsPage::wizardFinished(int exitCode, QProcess::ExitStatus exitStatus)
|
||||
{
|
||||
if (exitCode != 0 || exitStatus == QProcess::CrashExit)
|
||||
return qApp->quit();
|
||||
|
||||
mMain->reloadSettings();
|
||||
wizardButton->setEnabled(true);
|
||||
|
||||
mMain->show(); // Show the launcher again
|
||||
}
|
||||
|
||||
void Launcher::SettingsPage::importerStarted()
|
||||
{
|
||||
importerButton->setEnabled(false);
|
||||
}
|
||||
|
||||
void Launcher::SettingsPage::importerFinished(int exitCode, QProcess::ExitStatus exitStatus)
|
||||
{
|
||||
if (exitCode != 0 || exitStatus == QProcess::CrashExit)
|
||||
return;
|
||||
|
||||
// Re-read the settings in their current state
|
||||
mMain->reloadSettings();
|
||||
|
||||
// Import selected data files from openmw.cfg
|
||||
if (addonsCheckBox->isChecked())
|
||||
{
|
||||
if (mProfileDialog->exec() == QDialog::Accepted)
|
||||
{
|
||||
const QString profile(mProfileDialog->lineEdit()->text());
|
||||
const QStringList files(mGameSettings.values(QLatin1String("content")));
|
||||
|
||||
qDebug() << "Profile " << profile << files;
|
||||
|
||||
// Doesn't quite work right now
|
||||
mLauncherSettings.setValue(QLatin1String("Profiles/currentprofile"), profile);
|
||||
|
||||
foreach (const QString &file, files) {
|
||||
mLauncherSettings.setMultiValue(QLatin1String("Profiles/") + profile + QLatin1String("/content"), file);
|
||||
}
|
||||
|
||||
mGameSettings.remove(QLatin1String("content"));
|
||||
}
|
||||
}
|
||||
|
||||
mMain->reloadSettings();
|
||||
importerButton->setEnabled(true);
|
||||
}
|
||||
|
||||
void Launcher::SettingsPage::updateOkButton(const QString &text)
|
||||
{
|
||||
// We do this here because we need to access the profiles
|
||||
if (text.isEmpty()) {
|
||||
mProfileDialog->setOkButtonEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const QStringList profiles(mLauncherSettings.subKeys(QString("Profiles/")));
|
||||
|
||||
(profiles.contains(text))
|
||||
? mProfileDialog->setOkButtonEnabled(false)
|
||||
: mProfileDialog->setOkButtonEnabled(true);
|
||||
}
|
||||
|
||||
void Launcher::SettingsPage::saveSettings()
|
||||
{
|
||||
QString language(languageComboBox->currentText());
|
||||
|
||||
mLauncherSettings.setValue(QLatin1String("Settings/language"), language);
|
||||
|
||||
if (language == QLatin1String("Polish")) {
|
||||
mGameSettings.setValue(QLatin1String("encoding"), QLatin1String("win1250"));
|
||||
} else if (language == QLatin1String("Russian")) {
|
||||
mGameSettings.setValue(QLatin1String("encoding"), QLatin1String("win1251"));
|
||||
} else {
|
||||
mGameSettings.setValue(QLatin1String("encoding"), QLatin1String("win1252"));
|
||||
}
|
||||
}
|
||||
|
||||
bool Launcher::SettingsPage::loadSettings()
|
||||
{
|
||||
QString language(mLauncherSettings.value(QLatin1String("Settings/language")));
|
||||
|
||||
int index = languageComboBox->findText(language);
|
||||
|
||||
if (index != -1)
|
||||
languageComboBox->setCurrentIndex(index);
|
||||
|
||||
return true;
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
#ifndef SETTINGSPAGE_HPP
|
||||
#define SETTINGSPAGE_HPP
|
||||
|
||||
#include <QWidget>
|
||||
#include <QProcess>
|
||||
|
||||
#include <components/process/processinvoker.hpp>
|
||||
|
||||
#include "ui_settingspage.h"
|
||||
|
||||
#include "maindialog.hpp"
|
||||
|
||||
namespace Files { struct ConfigurationManager; }
|
||||
namespace Config { class GameSettings;
|
||||
class LauncherSettings; }
|
||||
|
||||
namespace Launcher
|
||||
{
|
||||
class TextInputDialog;
|
||||
|
||||
class SettingsPage : public QWidget, private Ui::SettingsPage
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SettingsPage(Files::ConfigurationManager &cfg, Config::GameSettings &gameSettings,
|
||||
Config::LauncherSettings &launcherSettings, MainDialog *parent = 0);
|
||||
~SettingsPage();
|
||||
|
||||
void saveSettings();
|
||||
bool loadSettings();
|
||||
|
||||
private slots:
|
||||
|
||||
void on_wizardButton_clicked();
|
||||
void on_importerButton_clicked();
|
||||
void on_browseButton_clicked();
|
||||
|
||||
void wizardStarted();
|
||||
void wizardFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
||||
|
||||
void importerStarted();
|
||||
void importerFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
||||
|
||||
void updateOkButton(const QString &text);
|
||||
|
||||
private:
|
||||
|
||||
Process::ProcessInvoker *mWizardInvoker;
|
||||
Process::ProcessInvoker *mImporterInvoker;
|
||||
|
||||
Files::ConfigurationManager &mCfgMgr;
|
||||
|
||||
Config::GameSettings &mGameSettings;
|
||||
Config::LauncherSettings &mLauncherSettings;
|
||||
|
||||
MainDialog *mMain;
|
||||
TextInputDialog *mProfileDialog;
|
||||
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif // SETTINGSPAGE_HPP
|
@ -1,521 +0,0 @@
|
||||
#include "unshieldthread.hpp"
|
||||
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
namespace bfs = boost::filesystem;
|
||||
|
||||
namespace
|
||||
{
|
||||
static bool make_sure_directory_exists(bfs::path directory)
|
||||
{
|
||||
|
||||
if(!bfs::exists(directory))
|
||||
{
|
||||
bfs::create_directories(directory);
|
||||
}
|
||||
|
||||
return bfs::exists(directory);
|
||||
}
|
||||
|
||||
void fill_path(bfs::path& path, const std::string& name)
|
||||
{
|
||||
size_t start = 0;
|
||||
|
||||
size_t i;
|
||||
for(i = 0; i < name.length(); i++)
|
||||
{
|
||||
switch(name[i])
|
||||
{
|
||||
case '\\':
|
||||
path /= name.substr(start, i-start);
|
||||
start = i+1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
path /= name.substr(start, i-start);
|
||||
}
|
||||
|
||||
std::string get_setting(const std::string& category, const std::string& setting, const std::string& inx)
|
||||
{
|
||||
size_t start = inx.find(category);
|
||||
start = inx.find(setting, start) + setting.length() + 3;
|
||||
|
||||
size_t end = inx.find("!", start);
|
||||
|
||||
return inx.substr(start, end-start);
|
||||
}
|
||||
|
||||
std::string read_to_string(const bfs::path& path)
|
||||
{
|
||||
bfs::ifstream strstream(path, std::ios::in | std::ios::binary);
|
||||
std::string str;
|
||||
|
||||
strstream.seekg(0, std::ios::end);
|
||||
str.resize(strstream.tellg());
|
||||
strstream.seekg(0, std::ios::beg);
|
||||
strstream.read(&str[0], str.size());
|
||||
strstream.close();
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
void add_setting(const std::string& category, const std::string& setting, const std::string& val, std::string& ini)
|
||||
{
|
||||
size_t loc;
|
||||
loc = ini.find("[" + category + "]");
|
||||
|
||||
// If category is not found, create it
|
||||
if(loc == std::string::npos)
|
||||
{
|
||||
loc = ini.size() + 2;
|
||||
ini += ("\r\n[" + category + "]\r\n");
|
||||
}
|
||||
|
||||
loc += category.length() +2 +2;
|
||||
ini.insert(loc, setting + "=" + val + "\r\n");
|
||||
}
|
||||
|
||||
#define FIX(setting) add_setting(category, setting, get_setting(category, setting, inx), ini)
|
||||
|
||||
void bloodmoon_fix_ini(std::string& ini, const bfs::path inxPath)
|
||||
{
|
||||
std::string inx = read_to_string(inxPath);
|
||||
|
||||
// Remove this one setting (the only one actually changed by bloodmoon, as opposed to just adding new ones)
|
||||
size_t start = ini.find("[Weather Blight]");
|
||||
start = ini.find("Ambient Loop Sound ID", start);
|
||||
size_t end = ini.find("\r\n", start) +2;
|
||||
ini.erase(start, end-start);
|
||||
|
||||
std::string category;
|
||||
|
||||
category = "General";
|
||||
{
|
||||
FIX("Werewolf FOV");
|
||||
}
|
||||
category = "Moons";
|
||||
{
|
||||
FIX("Script Color");
|
||||
}
|
||||
category = "Weather";
|
||||
{
|
||||
FIX("Snow Ripples");
|
||||
FIX("Snow Ripple Radius");
|
||||
FIX("Snow Ripples Per Flake");
|
||||
FIX("Snow Ripple Scale");
|
||||
FIX("Snow Ripple Speed");
|
||||
FIX("Snow Gravity Scale");
|
||||
FIX("Snow High Kill");
|
||||
FIX("Snow Low Kill");
|
||||
}
|
||||
category = "Weather Blight";
|
||||
{
|
||||
FIX("Ambient Loop Sound ID");
|
||||
}
|
||||
category = "Weather Snow";
|
||||
{
|
||||
FIX("Sky Sunrise Color");
|
||||
FIX("Sky Day Color");
|
||||
FIX("Sky Sunset Color");
|
||||
FIX("Sky Night Color");
|
||||
FIX("Fog Sunrise Color");
|
||||
FIX("Fog Day Color");
|
||||
FIX("Fog Sunset Color");
|
||||
FIX("Fog Night Color");
|
||||
FIX("Ambient Sunrise Color");
|
||||
FIX("Ambient Day Color");
|
||||
FIX("Ambient Sunset Color");
|
||||
FIX("Ambient Night Color");
|
||||
FIX("Sun Sunrise Color");
|
||||
FIX("Sun Day Color");
|
||||
FIX("Sun Sunset Color");
|
||||
FIX("Sun Night Color");
|
||||
FIX("Sun Disc Sunset Color");
|
||||
FIX("Transition Delta");
|
||||
FIX("Land Fog Day Depth");
|
||||
FIX("Land Fog Night Depth");
|
||||
FIX("Clouds Maximum Percent");
|
||||
FIX("Wind Speed");
|
||||
FIX("Cloud Speed");
|
||||
FIX("Glare View");
|
||||
FIX("Cloud Texture");
|
||||
FIX("Ambient Loop Sound ID");
|
||||
FIX("Snow Threshold");
|
||||
FIX("Snow Diameter");
|
||||
FIX("Snow Height Min");
|
||||
FIX("Snow Height Max");
|
||||
FIX("Snow Entrance Speed");
|
||||
FIX("Max Snowflakes");
|
||||
}
|
||||
category = "Weather Blizzard";
|
||||
{
|
||||
FIX("Sky Sunrise Color");
|
||||
FIX("Sky Day Color");
|
||||
FIX("Sky Sunset Color");
|
||||
FIX("Sky Night Color");
|
||||
FIX("Fog Sunrise Color");
|
||||
FIX("Fog Day Color");
|
||||
FIX("Fog Sunset Color");
|
||||
FIX("Fog Night Color");
|
||||
FIX("Ambient Sunrise Color");
|
||||
FIX("Ambient Day Color");
|
||||
FIX("Ambient Sunset Color");
|
||||
FIX("Ambient Night Color");
|
||||
FIX("Sun Sunrise Color");
|
||||
FIX("Sun Day Color");
|
||||
FIX("Sun Sunset Color");
|
||||
FIX("Sun Night Color");
|
||||
FIX("Sun Disc Sunset Color");
|
||||
FIX("Transition Delta");
|
||||
FIX("Land Fog Day Depth");
|
||||
FIX("Land Fog Night Depth");
|
||||
FIX("Clouds Maximum Percent");
|
||||
FIX("Wind Speed");
|
||||
FIX("Cloud Speed");
|
||||
FIX("Glare View");
|
||||
FIX("Cloud Texture");
|
||||
FIX("Ambient Loop Sound ID");
|
||||
FIX("Storm Threshold");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void fix_ini(const bfs::path& output_dir, bfs::path cdPath, bool tribunal, bool bloodmoon)
|
||||
{
|
||||
bfs::path ini_path = output_dir;
|
||||
ini_path /= "Morrowind.ini";
|
||||
|
||||
std::string ini = read_to_string(ini_path.string());
|
||||
|
||||
if(tribunal)
|
||||
{
|
||||
add_setting("Game Files", "GameFile1", "Tribunal.esm", ini);
|
||||
add_setting("Archives", "Archive 0", "Tribunal.bsa", ini);
|
||||
}
|
||||
if(bloodmoon)
|
||||
{
|
||||
bloodmoon_fix_ini(ini, cdPath / "setup.inx");
|
||||
add_setting("Game Files", "GameFile2", "Bloodmoon.esm", ini);
|
||||
add_setting("Archives", "Archive 1", "Bloodmoon.bsa", ini);
|
||||
}
|
||||
|
||||
bfs::ofstream inistream((ini_path));
|
||||
inistream << ini;
|
||||
inistream.close();
|
||||
}
|
||||
|
||||
void installToPath(const bfs::path& from, const bfs::path& to, bool copy = false)
|
||||
{
|
||||
make_sure_directory_exists(to);
|
||||
|
||||
for ( bfs::directory_iterator end, dir(from); dir != end; ++dir )
|
||||
{
|
||||
if(bfs::is_directory(dir->path()))
|
||||
installToPath(dir->path(), to / dir->path().filename(), copy);
|
||||
else
|
||||
{
|
||||
if(copy)
|
||||
{
|
||||
bfs::path dest = to / dir->path().filename();
|
||||
if(bfs::exists(dest))
|
||||
bfs::remove_all(dest);
|
||||
bfs::copy_file(dir->path(), dest);
|
||||
}
|
||||
else
|
||||
bfs::rename(dir->path(), to / dir->path().filename());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bfs::path findFile(const bfs::path& in, std::string filename, bool recursive = true)
|
||||
{
|
||||
if(recursive)
|
||||
{
|
||||
for ( bfs::recursive_directory_iterator end, dir(in); dir != end; ++dir )
|
||||
{
|
||||
if(Misc::StringUtils::ciEqual(dir->path().filename().string(), filename))
|
||||
return dir->path();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( bfs::directory_iterator end, dir(in); dir != end; ++dir )
|
||||
{
|
||||
if(Misc::StringUtils::ciEqual(dir->path().filename().string(), filename))
|
||||
return dir->path();
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
bool contains(const bfs::path& in, std::string filename)
|
||||
{
|
||||
for(bfs::directory_iterator end, dir(in); dir != end; ++dir)
|
||||
{
|
||||
if(Misc::StringUtils::ciEqual(dir->path().filename().string(), filename))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
time_t getTime(const char* time)
|
||||
{
|
||||
struct tm tms;
|
||||
memset(&tms, 0, sizeof(struct tm));
|
||||
strptime(time, "%d %B %Y", &tms);
|
||||
return mktime(&tms);
|
||||
}
|
||||
|
||||
// Some cds have cab files which have the Data Files subfolders outside the Data Files folder
|
||||
void install_dfiles_outside(const bfs::path& from, const bfs::path& dFiles)
|
||||
{
|
||||
bfs::path fonts = findFile(from, "fonts", false);
|
||||
if(fonts.string() != "")
|
||||
installToPath(fonts, dFiles / "Fonts");
|
||||
|
||||
bfs::path music = findFile(from, "music", false);
|
||||
if(music.string() != "")
|
||||
installToPath(music, dFiles / "Music");
|
||||
|
||||
bfs::path sound = findFile(from, "sound", false);
|
||||
if(sound.string() != "")
|
||||
installToPath(sound, dFiles / "Sound");
|
||||
|
||||
bfs::path splash = findFile(from, "splash", false);
|
||||
if(splash.string() != "")
|
||||
installToPath(splash, dFiles / "Splash");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool Launcher::UnshieldThread::SetMorrowindPath(const std::string& path)
|
||||
{
|
||||
mMorrowindPath = path;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Launcher::UnshieldThread::SetTribunalPath(const std::string& path)
|
||||
{
|
||||
mTribunalPath = path;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Launcher::UnshieldThread::SetBloodmoonPath(const std::string& path)
|
||||
{
|
||||
mBloodmoonPath = path;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Launcher::UnshieldThread::SetOutputPath(const std::string& path)
|
||||
{
|
||||
mOutputPath = path;
|
||||
}
|
||||
|
||||
bool Launcher::UnshieldThread::extract_file(Unshield* unshield, bfs::path output_dir, const char* prefix, int index)
|
||||
{
|
||||
bool success;
|
||||
bfs::path dirname;
|
||||
bfs::path filename;
|
||||
int directory = unshield_file_directory(unshield, index);
|
||||
|
||||
dirname = output_dir;
|
||||
|
||||
if (prefix && prefix[0])
|
||||
dirname /= prefix;
|
||||
|
||||
if (directory >= 0)
|
||||
{
|
||||
const char* tmp = unshield_directory_name(unshield, directory);
|
||||
if (tmp && tmp[0])
|
||||
fill_path(dirname, tmp);
|
||||
}
|
||||
|
||||
make_sure_directory_exists(dirname);
|
||||
|
||||
filename = dirname;
|
||||
filename /= unshield_file_name(unshield, index);
|
||||
|
||||
emit signalGUI(QString("Extracting: ") + QString(filename.c_str()));
|
||||
|
||||
success = unshield_file_save(unshield, index, filename.c_str());
|
||||
|
||||
if (!success)
|
||||
bfs::remove(filename);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void Launcher::UnshieldThread::extract_cab(const bfs::path& cab, const bfs::path& output_dir, bool extract_ini)
|
||||
{
|
||||
Unshield * unshield;
|
||||
unshield = unshield_open(cab.c_str());
|
||||
|
||||
int i;
|
||||
for (i = 0; i < unshield_file_group_count(unshield); i++)
|
||||
{
|
||||
UnshieldFileGroup* file_group = unshield_file_group_get(unshield, i);
|
||||
|
||||
for (size_t j = file_group->first_file; j <= file_group->last_file; j++)
|
||||
{
|
||||
if (unshield_file_is_valid(unshield, j))
|
||||
extract_file(unshield, output_dir, file_group->name, j);
|
||||
}
|
||||
}
|
||||
unshield_close(unshield);
|
||||
}
|
||||
|
||||
|
||||
bool Launcher::UnshieldThread::extract()
|
||||
{
|
||||
bfs::path outputDataFilesDir = mOutputPath;
|
||||
outputDataFilesDir /= "Data Files";
|
||||
bfs::path extractPath = mOutputPath;
|
||||
extractPath /= "extract-temp";
|
||||
|
||||
if(!mMorrowindDone && mMorrowindPath.string().length() > 0)
|
||||
{
|
||||
mMorrowindDone = true;
|
||||
|
||||
bfs::path mwExtractPath = extractPath / "morrowind";
|
||||
extract_cab(mMorrowindPath, mwExtractPath, true);
|
||||
|
||||
bfs::path dFilesDir = findFile(mwExtractPath, "morrowind.esm").parent_path();
|
||||
|
||||
installToPath(dFilesDir, outputDataFilesDir);
|
||||
|
||||
install_dfiles_outside(mwExtractPath, outputDataFilesDir);
|
||||
|
||||
// Videos are often kept uncompressed on the cd
|
||||
bfs::path videosPath = findFile(mMorrowindPath.parent_path(), "video", false);
|
||||
if(videosPath.string() != "")
|
||||
{
|
||||
emit signalGUI(QString("Installing Videos..."));
|
||||
installToPath(videosPath, outputDataFilesDir / "Video", true);
|
||||
}
|
||||
|
||||
bfs::path cdDFiles = findFile(mMorrowindPath.parent_path(), "data files", false);
|
||||
if(cdDFiles.string() != "")
|
||||
{
|
||||
emit signalGUI(QString("Installing Uncompressed Data files from CD..."));
|
||||
installToPath(cdDFiles, outputDataFilesDir, true);
|
||||
}
|
||||
|
||||
|
||||
bfs::rename(findFile(mwExtractPath, "morrowind.ini"), outputDataFilesDir / "Morrowind.ini");
|
||||
|
||||
mTribunalDone = contains(outputDataFilesDir, "tribunal.esm");
|
||||
mBloodmoonDone = contains(outputDataFilesDir, "bloodmoon.esm");
|
||||
|
||||
}
|
||||
|
||||
else if(!mTribunalDone && mTribunalPath.string().length() > 0)
|
||||
{
|
||||
mTribunalDone = true;
|
||||
|
||||
bfs::path tbExtractPath = extractPath / "tribunal";
|
||||
extract_cab(mTribunalPath, tbExtractPath, true);
|
||||
|
||||
bfs::path dFilesDir = findFile(tbExtractPath, "tribunal.esm").parent_path();
|
||||
|
||||
installToPath(dFilesDir, outputDataFilesDir);
|
||||
|
||||
install_dfiles_outside(tbExtractPath, outputDataFilesDir);
|
||||
|
||||
// Mt GOTY CD has Sounds in a seperate folder from the rest of the data files
|
||||
bfs::path soundsPath = findFile(tbExtractPath, "sounds", false);
|
||||
if(soundsPath.string() != "")
|
||||
installToPath(soundsPath, outputDataFilesDir / "Sounds");
|
||||
|
||||
bfs::path cdDFiles = findFile(mTribunalPath.parent_path(), "data files", false);
|
||||
if(cdDFiles.string() != "")
|
||||
{
|
||||
emit signalGUI(QString("Installing Uncompressed Data files from CD..."));
|
||||
installToPath(cdDFiles, outputDataFilesDir, true);
|
||||
}
|
||||
|
||||
mBloodmoonDone = contains(outputDataFilesDir, "bloodmoon.esm");
|
||||
|
||||
fix_ini(outputDataFilesDir, bfs::path(mTribunalPath).parent_path(), mTribunalDone, mBloodmoonDone);
|
||||
}
|
||||
|
||||
else if(!mBloodmoonDone && mBloodmoonPath.string().length() > 0)
|
||||
{
|
||||
mBloodmoonDone = true;
|
||||
|
||||
bfs::path bmExtractPath = extractPath / "bloodmoon";
|
||||
extract_cab(mBloodmoonPath, bmExtractPath, true);
|
||||
|
||||
bfs::path dFilesDir = findFile(bmExtractPath, "bloodmoon.esm").parent_path();
|
||||
|
||||
installToPath(dFilesDir, outputDataFilesDir);
|
||||
|
||||
install_dfiles_outside(bmExtractPath, outputDataFilesDir);
|
||||
|
||||
// My GOTY CD contains a folder within cab files called Tribunal patch,
|
||||
// which contains Tribunal.esm
|
||||
bfs::path tbPatchPath = findFile(bmExtractPath, "tribunal.esm");
|
||||
if(tbPatchPath.string() != "")
|
||||
bfs::rename(tbPatchPath, outputDataFilesDir / "Tribunal.esm");
|
||||
|
||||
bfs::path cdDFiles = findFile(mBloodmoonPath.parent_path(), "data files", false);
|
||||
if(cdDFiles.string() != "")
|
||||
{
|
||||
emit signalGUI(QString("Installing Uncompressed Data files from CD..."));
|
||||
installToPath(cdDFiles, outputDataFilesDir, true);
|
||||
}
|
||||
|
||||
fix_ini(outputDataFilesDir, bfs::path(mBloodmoonPath).parent_path(), false, mBloodmoonDone);
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Launcher::UnshieldThread::Done()
|
||||
{
|
||||
// Get rid of unnecessary files
|
||||
bfs::remove_all(mOutputPath / "extract-temp");
|
||||
|
||||
// Set modified time to release dates, to preserve load order
|
||||
if(mMorrowindDone)
|
||||
bfs::last_write_time(findFile(mOutputPath, "morrowind.esm"), getTime("1 May 2002"));
|
||||
|
||||
if(mTribunalDone)
|
||||
bfs::last_write_time(findFile(mOutputPath, "tribunal.esm"), getTime("6 November 2002"));
|
||||
|
||||
if(mBloodmoonDone)
|
||||
bfs::last_write_time(findFile(mOutputPath, "bloodmoon.esm"), getTime("3 June 2003"));
|
||||
}
|
||||
|
||||
std::string Launcher::UnshieldThread::GetMWEsmPath()
|
||||
{
|
||||
return findFile(mOutputPath / "Data Files", "morrowind.esm").string();
|
||||
}
|
||||
|
||||
bool Launcher::UnshieldThread::TribunalDone()
|
||||
{
|
||||
return mTribunalDone;
|
||||
}
|
||||
|
||||
bool Launcher::UnshieldThread::BloodmoonDone()
|
||||
{
|
||||
return mBloodmoonDone;
|
||||
}
|
||||
|
||||
void Launcher::UnshieldThread::run()
|
||||
{
|
||||
extract();
|
||||
emit close();
|
||||
}
|
||||
|
||||
Launcher::UnshieldThread::UnshieldThread()
|
||||
{
|
||||
unshield_set_log_level(0);
|
||||
mMorrowindDone = false;
|
||||
mTribunalDone = false;
|
||||
mBloodmoonDone = false;
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
#ifndef UNSHIELD_THREAD_H
|
||||
#define UNSHIELD_THREAD_H
|
||||
|
||||
#include <QThread>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <libunshield.h>
|
||||
|
||||
namespace Launcher
|
||||
{
|
||||
class UnshieldThread : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
bool SetMorrowindPath(const std::string& path);
|
||||
bool SetTribunalPath(const std::string& path);
|
||||
bool SetBloodmoonPath(const std::string& path);
|
||||
|
||||
void SetOutputPath(const std::string& path);
|
||||
|
||||
bool extract();
|
||||
|
||||
bool TribunalDone();
|
||||
bool BloodmoonDone();
|
||||
|
||||
void Done();
|
||||
|
||||
std::string GetMWEsmPath();
|
||||
|
||||
UnshieldThread();
|
||||
|
||||
private:
|
||||
|
||||
void extract_cab(const boost::filesystem::path& cab, const boost::filesystem::path& output_dir, bool extract_ini = false);
|
||||
bool extract_file(Unshield* unshield, boost::filesystem::path output_dir, const char* prefix, int index);
|
||||
|
||||
boost::filesystem::path mMorrowindPath;
|
||||
boost::filesystem::path mTribunalPath;
|
||||
boost::filesystem::path mBloodmoonPath;
|
||||
|
||||
bool mMorrowindDone;
|
||||
bool mTribunalDone;
|
||||
bool mBloodmoonDone;
|
||||
|
||||
boost::filesystem::path mOutputPath;
|
||||
|
||||
|
||||
protected:
|
||||
virtual void run();
|
||||
|
||||
signals:
|
||||
void signalGUI(QString);
|
||||
void close();
|
||||
};
|
||||
}
|
||||
#endif
|
@ -1,258 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "checkablemessagebox.hpp"
|
||||
|
||||
#include <QVariant>
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QAction>
|
||||
#include <QApplication>
|
||||
#include <QButtonGroup>
|
||||
#include <QCheckBox>
|
||||
#include <QDialog>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QHeaderView>
|
||||
#include <QLabel>
|
||||
#include <QSpacerItem>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
|
||||
/*!
|
||||
\class Utils::CheckableMessageBox
|
||||
|
||||
\brief A messagebox suitable for questions with a
|
||||
"Do not ask me again" checkbox.
|
||||
|
||||
Emulates the QMessageBox API with
|
||||
static conveniences. The message label can open external URLs.
|
||||
*/
|
||||
Launcher::CheckableMessageBoxPrivate::CheckableMessageBoxPrivate(QDialog *q)
|
||||
: clickedButton(0)
|
||||
{
|
||||
QSizePolicy sizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
|
||||
|
||||
pixmapLabel = new QLabel(q);
|
||||
sizePolicy.setHorizontalStretch(0);
|
||||
sizePolicy.setVerticalStretch(0);
|
||||
sizePolicy.setHeightForWidth(pixmapLabel->sizePolicy().hasHeightForWidth());
|
||||
pixmapLabel->setSizePolicy(sizePolicy);
|
||||
pixmapLabel->setVisible(false);
|
||||
|
||||
QSpacerItem *pixmapSpacer =
|
||||
new QSpacerItem(0, 5, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding);
|
||||
|
||||
messageLabel = new QLabel(q);
|
||||
messageLabel->setMinimumSize(QSize(300, 0));
|
||||
messageLabel->setWordWrap(true);
|
||||
messageLabel->setOpenExternalLinks(true);
|
||||
messageLabel->setTextInteractionFlags(Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse);
|
||||
|
||||
QSpacerItem *checkBoxRightSpacer =
|
||||
new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||
QSpacerItem *buttonSpacer =
|
||||
new QSpacerItem(0, 1, QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||
|
||||
checkBox = new QCheckBox(q);
|
||||
checkBox->setText(Launcher::CheckableMessageBox::tr("Do not ask again"));
|
||||
|
||||
buttonBox = new QDialogButtonBox(q);
|
||||
buttonBox->setOrientation(Qt::Horizontal);
|
||||
buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
|
||||
|
||||
QVBoxLayout *verticalLayout = new QVBoxLayout();
|
||||
verticalLayout->addWidget(pixmapLabel);
|
||||
verticalLayout->addItem(pixmapSpacer);
|
||||
|
||||
QHBoxLayout *horizontalLayout_2 = new QHBoxLayout();
|
||||
horizontalLayout_2->addLayout(verticalLayout);
|
||||
horizontalLayout_2->addWidget(messageLabel);
|
||||
|
||||
QHBoxLayout *horizontalLayout = new QHBoxLayout();
|
||||
horizontalLayout->addWidget(checkBox);
|
||||
horizontalLayout->addItem(checkBoxRightSpacer);
|
||||
|
||||
QVBoxLayout *verticalLayout_2 = new QVBoxLayout(q);
|
||||
verticalLayout_2->addLayout(horizontalLayout_2);
|
||||
verticalLayout_2->addLayout(horizontalLayout);
|
||||
verticalLayout_2->addItem(buttonSpacer);
|
||||
verticalLayout_2->addWidget(buttonBox);
|
||||
}
|
||||
|
||||
Launcher::CheckableMessageBox::CheckableMessageBox(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
d(new Launcher::CheckableMessageBoxPrivate(this))
|
||||
{
|
||||
setModal(true);
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
connect(d->buttonBox, SIGNAL(accepted()), SLOT(accept()));
|
||||
connect(d->buttonBox, SIGNAL(rejected()), SLOT(reject()));
|
||||
connect(d->buttonBox, SIGNAL(clicked(QAbstractButton*)),
|
||||
SLOT(slotClicked(QAbstractButton*)));
|
||||
}
|
||||
|
||||
Launcher::CheckableMessageBox::~CheckableMessageBox()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
void Launcher::CheckableMessageBox::slotClicked(QAbstractButton *b)
|
||||
{
|
||||
d->clickedButton = b;
|
||||
}
|
||||
|
||||
QAbstractButton *Launcher::CheckableMessageBox::clickedButton() const
|
||||
{
|
||||
return d->clickedButton;
|
||||
}
|
||||
|
||||
QDialogButtonBox::StandardButton Launcher::CheckableMessageBox::clickedStandardButton() const
|
||||
{
|
||||
if (d->clickedButton)
|
||||
return d->buttonBox->standardButton(d->clickedButton);
|
||||
return QDialogButtonBox::NoButton;
|
||||
}
|
||||
|
||||
QString Launcher::CheckableMessageBox::text() const
|
||||
{
|
||||
return d->messageLabel->text();
|
||||
}
|
||||
|
||||
void Launcher::CheckableMessageBox::setText(const QString &t)
|
||||
{
|
||||
d->messageLabel->setText(t);
|
||||
}
|
||||
|
||||
QPixmap Launcher::CheckableMessageBox::iconPixmap() const
|
||||
{
|
||||
if (const QPixmap *p = d->pixmapLabel->pixmap())
|
||||
return QPixmap(*p);
|
||||
return QPixmap();
|
||||
}
|
||||
|
||||
void Launcher::CheckableMessageBox::setIconPixmap(const QPixmap &p)
|
||||
{
|
||||
d->pixmapLabel->setPixmap(p);
|
||||
d->pixmapLabel->setVisible(!p.isNull());
|
||||
}
|
||||
|
||||
bool Launcher::CheckableMessageBox::isChecked() const
|
||||
{
|
||||
return d->checkBox->isChecked();
|
||||
}
|
||||
|
||||
void Launcher::CheckableMessageBox::setChecked(bool s)
|
||||
{
|
||||
d->checkBox->setChecked(s);
|
||||
}
|
||||
|
||||
QString Launcher::CheckableMessageBox::checkBoxText() const
|
||||
{
|
||||
return d->checkBox->text();
|
||||
}
|
||||
|
||||
void Launcher::CheckableMessageBox::setCheckBoxText(const QString &t)
|
||||
{
|
||||
d->checkBox->setText(t);
|
||||
}
|
||||
|
||||
bool Launcher::CheckableMessageBox::isCheckBoxVisible() const
|
||||
{
|
||||
return d->checkBox->isVisible();
|
||||
}
|
||||
|
||||
void Launcher::CheckableMessageBox::setCheckBoxVisible(bool v)
|
||||
{
|
||||
d->checkBox->setVisible(v);
|
||||
}
|
||||
|
||||
QDialogButtonBox::StandardButtons Launcher::CheckableMessageBox::standardButtons() const
|
||||
{
|
||||
return d->buttonBox->standardButtons();
|
||||
}
|
||||
|
||||
void Launcher::CheckableMessageBox::setStandardButtons(QDialogButtonBox::StandardButtons s)
|
||||
{
|
||||
d->buttonBox->setStandardButtons(s);
|
||||
}
|
||||
|
||||
QPushButton *Launcher::CheckableMessageBox::button(QDialogButtonBox::StandardButton b) const
|
||||
{
|
||||
return d->buttonBox->button(b);
|
||||
}
|
||||
|
||||
QPushButton *Launcher::CheckableMessageBox::addButton(const QString &text, QDialogButtonBox::ButtonRole role)
|
||||
{
|
||||
return d->buttonBox->addButton(text, role);
|
||||
}
|
||||
|
||||
QDialogButtonBox::StandardButton Launcher::CheckableMessageBox::defaultButton() const
|
||||
{
|
||||
foreach (QAbstractButton *b, d->buttonBox->buttons())
|
||||
if (QPushButton *pb = qobject_cast<QPushButton *>(b))
|
||||
if (pb->isDefault())
|
||||
return d->buttonBox->standardButton(pb);
|
||||
return QDialogButtonBox::NoButton;
|
||||
}
|
||||
|
||||
void Launcher::CheckableMessageBox::setDefaultButton(QDialogButtonBox::StandardButton s)
|
||||
{
|
||||
if (QPushButton *b = d->buttonBox->button(s)) {
|
||||
b->setDefault(true);
|
||||
b->setFocus();
|
||||
}
|
||||
}
|
||||
|
||||
QDialogButtonBox::StandardButton
|
||||
Launcher::CheckableMessageBox::question(QWidget *parent,
|
||||
const QString &title,
|
||||
const QString &question,
|
||||
const QString &checkBoxText,
|
||||
bool *checkBoxSetting,
|
||||
QDialogButtonBox::StandardButtons buttons,
|
||||
QDialogButtonBox::StandardButton defaultButton)
|
||||
{
|
||||
CheckableMessageBox mb(parent);
|
||||
mb.setWindowTitle(title);
|
||||
mb.setIconPixmap(QMessageBox::standardIcon(QMessageBox::Question));
|
||||
mb.setText(question);
|
||||
mb.setCheckBoxText(checkBoxText);
|
||||
mb.setChecked(*checkBoxSetting);
|
||||
mb.setStandardButtons(buttons);
|
||||
mb.setDefaultButton(defaultButton);
|
||||
mb.exec();
|
||||
*checkBoxSetting = mb.isChecked();
|
||||
return mb.clickedStandardButton();
|
||||
}
|
||||
|
||||
QMessageBox::StandardButton Launcher::CheckableMessageBox::dialogButtonBoxToMessageBoxButton(QDialogButtonBox::StandardButton db)
|
||||
{
|
||||
return static_cast<QMessageBox::StandardButton>(int(db));
|
||||
}
|
@ -1,116 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef CHECKABLEMESSAGEBOX_HPP
|
||||
#define CHECKABLEMESSAGEBOX_HPP
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
#include <QMessageBox>
|
||||
#include <QDialog>
|
||||
|
||||
class QCheckBox;
|
||||
|
||||
namespace Launcher
|
||||
{
|
||||
class CheckableMessageBoxPrivate
|
||||
{
|
||||
public:
|
||||
|
||||
QLabel *pixmapLabel;
|
||||
QLabel *messageLabel;
|
||||
QCheckBox *checkBox;
|
||||
QDialogButtonBox *buttonBox;
|
||||
QAbstractButton *clickedButton;
|
||||
|
||||
public:
|
||||
CheckableMessageBoxPrivate(QDialog *q);
|
||||
};
|
||||
|
||||
class CheckableMessageBox : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString text READ text WRITE setText)
|
||||
Q_PROPERTY(QPixmap iconPixmap READ iconPixmap WRITE setIconPixmap)
|
||||
Q_PROPERTY(bool isChecked READ isChecked WRITE setChecked)
|
||||
Q_PROPERTY(QString checkBoxText READ checkBoxText WRITE setCheckBoxText)
|
||||
Q_PROPERTY(QDialogButtonBox::StandardButtons buttons READ standardButtons WRITE setStandardButtons)
|
||||
Q_PROPERTY(QDialogButtonBox::StandardButton defaultButton READ defaultButton WRITE setDefaultButton)
|
||||
|
||||
public:
|
||||
explicit CheckableMessageBox(QWidget *parent);
|
||||
virtual ~CheckableMessageBox();
|
||||
|
||||
static QDialogButtonBox::StandardButton
|
||||
question(QWidget *parent,
|
||||
const QString &title,
|
||||
const QString &question,
|
||||
const QString &checkBoxText,
|
||||
bool *checkBoxSetting,
|
||||
QDialogButtonBox::StandardButtons buttons = QDialogButtonBox::Yes|QDialogButtonBox::No,
|
||||
QDialogButtonBox::StandardButton defaultButton = QDialogButtonBox::No);
|
||||
|
||||
QString text() const;
|
||||
void setText(const QString &);
|
||||
|
||||
bool isChecked() const;
|
||||
void setChecked(bool s);
|
||||
|
||||
QString checkBoxText() const;
|
||||
void setCheckBoxText(const QString &);
|
||||
|
||||
bool isCheckBoxVisible() const;
|
||||
void setCheckBoxVisible(bool);
|
||||
|
||||
QDialogButtonBox::StandardButtons standardButtons() const;
|
||||
void setStandardButtons(QDialogButtonBox::StandardButtons s);
|
||||
QPushButton *button(QDialogButtonBox::StandardButton b) const;
|
||||
QPushButton *addButton(const QString &text, QDialogButtonBox::ButtonRole role);
|
||||
|
||||
QDialogButtonBox::StandardButton defaultButton() const;
|
||||
void setDefaultButton(QDialogButtonBox::StandardButton s);
|
||||
|
||||
// See static QMessageBox::standardPixmap()
|
||||
QPixmap iconPixmap() const;
|
||||
void setIconPixmap (const QPixmap &p);
|
||||
|
||||
// Query the result
|
||||
QAbstractButton *clickedButton() const;
|
||||
QDialogButtonBox::StandardButton clickedStandardButton() const;
|
||||
|
||||
// Conversion convenience
|
||||
static QMessageBox::StandardButton dialogButtonBoxToMessageBoxButton(QDialogButtonBox::StandardButton);
|
||||
|
||||
private slots:
|
||||
void slotClicked(QAbstractButton *b);
|
||||
|
||||
private:
|
||||
CheckableMessageBoxPrivate *d;
|
||||
};
|
||||
}
|
||||
#endif // CHECKABLEMESSAGEBOX_HPP
|
@ -0,0 +1,116 @@
|
||||
set(WIZARD
|
||||
componentselectionpage.cpp
|
||||
conclusionpage.cpp
|
||||
existinginstallationpage.cpp
|
||||
importpage.cpp
|
||||
inisettings.cpp
|
||||
installationpage.cpp
|
||||
installationtargetpage.cpp
|
||||
intropage.cpp
|
||||
languageselectionpage.cpp
|
||||
main.cpp
|
||||
mainwizard.cpp
|
||||
methodselectionpage.cpp
|
||||
|
||||
unshield/unshieldworker.cpp
|
||||
|
||||
utils/componentlistwidget.cpp
|
||||
)
|
||||
|
||||
set(WIZARD_HEADER
|
||||
componentselectionpage.hpp
|
||||
conclusionpage.hpp
|
||||
existinginstallationpage.hpp
|
||||
importpage.hpp
|
||||
inisettings.hpp
|
||||
installationpage.hpp
|
||||
installationtargetpage.hpp
|
||||
intropage.hpp
|
||||
languageselectionpage.hpp
|
||||
mainwizard.hpp
|
||||
methodselectionpage.hpp
|
||||
|
||||
unshield/unshieldworker.hpp
|
||||
|
||||
utils/componentlistwidget.hpp
|
||||
)
|
||||
|
||||
# Headers that must be pre-processed
|
||||
set(WIZARD_HEADER_MOC
|
||||
componentselectionpage.hpp
|
||||
conclusionpage.hpp
|
||||
existinginstallationpage.hpp
|
||||
importpage.hpp
|
||||
installationpage.hpp
|
||||
installationtargetpage.hpp
|
||||
intropage.hpp
|
||||
languageselectionpage.hpp
|
||||
mainwizard.hpp
|
||||
methodselectionpage.hpp
|
||||
|
||||
unshield/unshieldworker.hpp
|
||||
|
||||
utils/componentlistwidget.hpp
|
||||
)
|
||||
|
||||
set(WIZARD_UI
|
||||
${CMAKE_SOURCE_DIR}/files/ui/wizard/componentselectionpage.ui
|
||||
${CMAKE_SOURCE_DIR}/files/ui/wizard/conclusionpage.ui
|
||||
${CMAKE_SOURCE_DIR}/files/ui/wizard/existinginstallationpage.ui
|
||||
${CMAKE_SOURCE_DIR}/files/ui/wizard/importpage.ui
|
||||
${CMAKE_SOURCE_DIR}/files/ui/wizard/installationpage.ui
|
||||
${CMAKE_SOURCE_DIR}/files/ui/wizard/installationtargetpage.ui
|
||||
${CMAKE_SOURCE_DIR}/files/ui/wizard/intropage.ui
|
||||
${CMAKE_SOURCE_DIR}/files/ui/wizard/languageselectionpage.ui
|
||||
${CMAKE_SOURCE_DIR}/files/ui/wizard/methodselectionpage.ui
|
||||
)
|
||||
|
||||
source_group(wizard FILES ${WIZARD} ${WIZARD_HEADER})
|
||||
|
||||
find_package(Qt4 REQUIRED)
|
||||
set(QT_USE_QTGUI 1)
|
||||
|
||||
# Set some platform specific settings
|
||||
if(WIN32)
|
||||
set(GUI_TYPE WIN32)
|
||||
set(QT_USE_QTMAIN TRUE)
|
||||
endif(WIN32)
|
||||
|
||||
QT4_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/wizard/wizard.qrc)
|
||||
QT4_WRAP_CPP(MOC_SRCS ${WIZARD_HEADER_MOC})
|
||||
QT4_WRAP_UI(UI_HDRS ${WIZARD_UI})
|
||||
|
||||
|
||||
include(${QT_USE_FILE})
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR} ${LIBUNSHIELD_INCLUDE})
|
||||
|
||||
add_executable(openmw-wizard
|
||||
${GUI_TYPE}
|
||||
${WIZARD}
|
||||
${WIZARD_HEADER}
|
||||
${RCC_SRCS}
|
||||
${MOC_SRCS}
|
||||
${UI_HDRS}
|
||||
)
|
||||
|
||||
target_link_libraries(openmw-wizard
|
||||
${Boost_LIBRARIES}
|
||||
${QT_LIBRARIES}
|
||||
${LIBUNSHIELD_LIBRARY}
|
||||
components
|
||||
)
|
||||
|
||||
if(DPKG_PROGRAM)
|
||||
INSTALL(TARGETS openmw-wizard RUNTIME DESTINATION games COMPONENT openmw-wizard)
|
||||
endif()
|
||||
|
||||
if (BUILD_WITH_CODE_COVERAGE)
|
||||
add_definitions (--coverage)
|
||||
target_link_libraries(openmw-wizard gcov)
|
||||
endif()
|
||||
|
||||
# Workaround for binutil => 2.23 problem when linking, should be fixed eventually upstream
|
||||
if (UNIX AND NOT APPLE)
|
||||
target_link_libraries(openmw-wizard dl Xt)
|
||||
endif()
|
||||
|
@ -0,0 +1,164 @@
|
||||
#include "componentselectionpage.hpp"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QPushButton>
|
||||
#include <QAbstractButton>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "mainwizard.hpp"
|
||||
|
||||
Wizard::ComponentSelectionPage::ComponentSelectionPage(QWidget *parent) :
|
||||
QWizardPage(parent)
|
||||
{
|
||||
mWizard = qobject_cast<MainWizard*>(parent);
|
||||
|
||||
setupUi(this);
|
||||
|
||||
setCommitPage(true);
|
||||
setButtonText(QWizard::CommitButton, tr("&Install"));
|
||||
|
||||
registerField(QLatin1String("installation.components"), componentsList);
|
||||
|
||||
connect(componentsList, SIGNAL(itemChanged(QListWidgetItem *)),
|
||||
this, SLOT(updateButton(QListWidgetItem *)));
|
||||
|
||||
}
|
||||
|
||||
void Wizard::ComponentSelectionPage::updateButton(QListWidgetItem *item)
|
||||
{
|
||||
if (field(QLatin1String("installation.new")).toBool() == true)
|
||||
return; // Morrowind is always checked here
|
||||
|
||||
bool unchecked = true;
|
||||
|
||||
for (int i =0; i < componentsList->count(); ++i) {
|
||||
QListWidgetItem *item = componentsList->item(i);
|
||||
|
||||
if (!item)
|
||||
continue;
|
||||
|
||||
if (item->checkState() == Qt::Checked) {
|
||||
unchecked = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (unchecked) {
|
||||
setCommitPage(false);
|
||||
setButtonText(QWizard::NextButton, tr("&Skip"));
|
||||
} else {
|
||||
setCommitPage(true);
|
||||
}
|
||||
}
|
||||
|
||||
void Wizard::ComponentSelectionPage::initializePage()
|
||||
{
|
||||
componentsList->clear();
|
||||
|
||||
QString path(field(QLatin1String("installation.path")).toString());
|
||||
|
||||
QListWidgetItem *morrowindItem = new QListWidgetItem(QLatin1String("Morrowind"));
|
||||
QListWidgetItem *tribunalItem = new QListWidgetItem(QLatin1String("Tribunal"));
|
||||
QListWidgetItem *bloodmoonItem = new QListWidgetItem(QLatin1String("Bloodmoon"));
|
||||
|
||||
if (field(QLatin1String("installation.new")).toBool() == true)
|
||||
{
|
||||
morrowindItem->setFlags(morrowindItem->flags() & !Qt::ItemIsEnabled & Qt::ItemIsUserCheckable);
|
||||
morrowindItem->setData(Qt::CheckStateRole, Qt::Checked);
|
||||
componentsList->addItem(morrowindItem);
|
||||
|
||||
tribunalItem->setFlags(tribunalItem->flags() | Qt::ItemIsUserCheckable);
|
||||
tribunalItem->setData(Qt::CheckStateRole, Qt::Checked);
|
||||
componentsList->addItem(tribunalItem);
|
||||
|
||||
bloodmoonItem->setFlags(bloodmoonItem->flags() | Qt::ItemIsUserCheckable);
|
||||
bloodmoonItem->setData(Qt::CheckStateRole, Qt::Checked);
|
||||
componentsList->addItem(bloodmoonItem);
|
||||
} else {
|
||||
|
||||
if (mWizard->mInstallations[path].hasMorrowind) {
|
||||
morrowindItem->setText(tr("Morrowind\t\t(installed)"));
|
||||
morrowindItem->setFlags(morrowindItem->flags() & !Qt::ItemIsEnabled & Qt::ItemIsUserCheckable);
|
||||
morrowindItem->setData(Qt::CheckStateRole, Qt::Unchecked);
|
||||
} else {
|
||||
morrowindItem->setText(tr("Morrowind"));
|
||||
morrowindItem->setData(Qt::CheckStateRole, Qt::Checked);
|
||||
}
|
||||
|
||||
componentsList->addItem(morrowindItem);
|
||||
|
||||
if (mWizard->mInstallations[path].hasTribunal) {
|
||||
tribunalItem->setText(tr("Tribunal\t\t(installed)"));
|
||||
tribunalItem->setFlags(tribunalItem->flags() & !Qt::ItemIsEnabled & Qt::ItemIsUserCheckable);
|
||||
tribunalItem->setData(Qt::CheckStateRole, Qt::Unchecked);
|
||||
} else {
|
||||
tribunalItem->setText(tr("Tribunal"));
|
||||
tribunalItem->setData(Qt::CheckStateRole, Qt::Checked);
|
||||
}
|
||||
|
||||
componentsList->addItem(tribunalItem);
|
||||
|
||||
if (mWizard->mInstallations[path].hasBloodmoon) {
|
||||
bloodmoonItem->setText(tr("Bloodmoon\t\t(installed)"));
|
||||
bloodmoonItem->setFlags(bloodmoonItem->flags() & !Qt::ItemIsEnabled & Qt::ItemIsUserCheckable);
|
||||
bloodmoonItem->setData(Qt::CheckStateRole, Qt::Unchecked);
|
||||
} else {
|
||||
bloodmoonItem->setText(tr("Bloodmoon"));
|
||||
bloodmoonItem->setData(Qt::CheckStateRole, Qt::Checked);
|
||||
}
|
||||
|
||||
componentsList->addItem(bloodmoonItem);
|
||||
}
|
||||
}
|
||||
|
||||
bool Wizard::ComponentSelectionPage::validatePage()
|
||||
{
|
||||
QStringList components(field(QLatin1String("installation.components")).toStringList());
|
||||
QString path(field(QLatin1String("installation.path")).toString());
|
||||
|
||||
// qDebug() << components << path << mWizard->mInstallations[path];
|
||||
|
||||
if (field(QLatin1String("installation.new")).toBool() == false) {
|
||||
if (components.contains(QLatin1String("Tribunal")) && !components.contains(QLatin1String("Bloodmoon")))
|
||||
{
|
||||
if (mWizard->mInstallations[path].hasBloodmoon)
|
||||
{
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("About to install Tribunal after Bloodmoon"));
|
||||
msgBox.setIcon(QMessageBox::Information);
|
||||
msgBox.setStandardButtons(QMessageBox::Cancel);
|
||||
msgBox.setText(tr("<html><head/><body><p><b>You are about to install Tribunal</b></p> \
|
||||
<p>Bloodmoon is already installed on your computer.</p> \
|
||||
<p>However, it is recommended that you install Tribunal before Bloodmoon.</p> \
|
||||
<p>Would you like to re-install Bloodmoon?</p></body></html>"));
|
||||
|
||||
QAbstractButton *reinstallButton = msgBox.addButton(tr("Re-install &Bloodmoon"), QMessageBox::ActionRole);
|
||||
msgBox.exec();
|
||||
|
||||
|
||||
if (msgBox.clickedButton() == reinstallButton) {
|
||||
// Force reinstallation
|
||||
mWizard->mInstallations[path].hasBloodmoon = false;
|
||||
QList<QListWidgetItem*> items = componentsList->findItems(QLatin1String("Bloodmoon"), Qt::MatchStartsWith);
|
||||
|
||||
foreach (QListWidgetItem *item, items) {
|
||||
item->setText(QLatin1String("Bloodmoon"));
|
||||
item->setCheckState(Qt::Checked);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int Wizard::ComponentSelectionPage::nextId() const
|
||||
{
|
||||
if (isCommitPage()) {
|
||||
return MainWizard::Page_Installation;
|
||||
} else {
|
||||
return MainWizard::Page_Import;
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
#ifndef COMPONENTSELECTIONPAGE_HPP
|
||||
#define COMPONENTSELECTIONPAGE_HPP
|
||||
|
||||
#include <QWizardPage>
|
||||
|
||||
#include "ui_componentselectionpage.h"
|
||||
|
||||
namespace Wizard
|
||||
{
|
||||
class MainWizard;
|
||||
|
||||
class ComponentSelectionPage : public QWizardPage, private Ui::ComponentSelectionPage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ComponentSelectionPage(QWidget *parent);
|
||||
|
||||
int nextId() const;
|
||||
virtual bool validatePage();
|
||||
|
||||
private slots:
|
||||
void updateButton(QListWidgetItem *item);
|
||||
|
||||
private:
|
||||
MainWizard *mWizard;
|
||||
|
||||
protected:
|
||||
void initializePage();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // COMPONENTSELECTIONPAGE_HPP
|
@ -0,0 +1,54 @@
|
||||
#include "conclusionpage.hpp"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include "mainwizard.hpp"
|
||||
|
||||
Wizard::ConclusionPage::ConclusionPage(QWidget *parent) :
|
||||
QWizardPage(parent)
|
||||
{
|
||||
mWizard = qobject_cast<MainWizard*>(parent);
|
||||
|
||||
setupUi(this);
|
||||
setPixmap(QWizard::WatermarkPixmap, QPixmap(QLatin1String(":/images/intropage-background.png")));
|
||||
}
|
||||
|
||||
void Wizard::ConclusionPage::initializePage()
|
||||
{
|
||||
// Write the path to openmw.cfg
|
||||
if (field(QLatin1String("installation.new")).toBool() == true) {
|
||||
QString path(field(QLatin1String("installation.path")).toString());
|
||||
mWizard->addInstallation(path);
|
||||
}
|
||||
|
||||
if (!mWizard->mError)
|
||||
{
|
||||
if ((field(QLatin1String("installation.new")).toBool() == true)
|
||||
|| (field(QLatin1String("installation.import-settings")).toBool() == true))
|
||||
{
|
||||
qDebug() << "IMPORT SETTINGS";
|
||||
mWizard->runSettingsImporter();
|
||||
}
|
||||
}
|
||||
|
||||
if (!mWizard->mError)
|
||||
{
|
||||
if (field(QLatin1String("installation.new")).toBool() == true)
|
||||
{
|
||||
textLabel->setText(tr("<html><head/><body><p>The OpenMW Wizard successfully installed Morrowind on your computer.</p> \
|
||||
<p>Click Finish to close the Wizard.</p></body></html>"));
|
||||
} else {
|
||||
textLabel->setText(tr("<html><head/><body><p>The OpenMW Wizard successfully modified your existing Morrowind installation.</p> \
|
||||
<p>Click Finish to close the Wizard.</p></body></html>"));
|
||||
}
|
||||
} else {
|
||||
textLabel->setText(tr("<html><head/><body><p>The OpenMW Wizard failed to install Morrowind on your computer.</p> \
|
||||
<p>Please report any bugs you might have encountered to our \
|
||||
<a href=\"https://bugs.openmw.org\">bug tracker</a>.<br/>Make sure to include the installation log.</p><br/></body></html>"));
|
||||
}
|
||||
}
|
||||
|
||||
int Wizard::ConclusionPage::nextId() const
|
||||
{
|
||||
return -1;
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
#ifndef CONCLUSIONPAGE_HPP
|
||||
#define CONCLUSIONPAGE_HPP
|
||||
|
||||
#include <QWizardPage>
|
||||
|
||||
#include "ui_conclusionpage.h"
|
||||
|
||||
namespace Wizard
|
||||
{
|
||||
class MainWizard;
|
||||
|
||||
class ConclusionPage : public QWizardPage, private Ui::ConclusionPage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ConclusionPage(QWidget *parent);
|
||||
|
||||
int nextId() const;
|
||||
|
||||
private:
|
||||
MainWizard *mWizard;
|
||||
|
||||
protected:
|
||||
void initializePage();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // CONCLUSIONPAGE_HPP
|
@ -0,0 +1,158 @@
|
||||
#include "existinginstallationpage.hpp"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QMessageBox>
|
||||
#include <QFileDialog>
|
||||
#include <QFileInfo>
|
||||
#include <QFile>
|
||||
|
||||
#include "mainwizard.hpp"
|
||||
|
||||
Wizard::ExistingInstallationPage::ExistingInstallationPage(QWidget *parent) :
|
||||
QWizardPage(parent)
|
||||
{
|
||||
mWizard = qobject_cast<MainWizard*>(parent);
|
||||
|
||||
setupUi(this);
|
||||
|
||||
// Add a placeholder item to the list of installations
|
||||
QListWidgetItem *emptyItem = new QListWidgetItem(tr("No existing installations detected"));
|
||||
emptyItem->setFlags(Qt::NoItemFlags);
|
||||
|
||||
installationsList->insertItem(0, emptyItem);
|
||||
|
||||
}
|
||||
|
||||
void Wizard::ExistingInstallationPage::initializePage()
|
||||
{
|
||||
// Add the available installation paths
|
||||
QStringList paths(mWizard->mInstallations.keys());
|
||||
|
||||
// Hide the default item if there are installations to choose from
|
||||
if (paths.isEmpty()) {
|
||||
installationsList->item(0)->setHidden(false);
|
||||
} else {
|
||||
installationsList->item(0)->setHidden(true);
|
||||
}
|
||||
|
||||
foreach (const QString &path, paths) {
|
||||
QListWidgetItem *item = new QListWidgetItem(path);
|
||||
|
||||
if (installationsList->findItems(path, Qt::MatchExactly).isEmpty())
|
||||
installationsList->addItem(item);
|
||||
}
|
||||
|
||||
connect(installationsList, SIGNAL(currentTextChanged(QString)),
|
||||
this, SLOT(textChanged(QString)));
|
||||
|
||||
connect(installationsList,SIGNAL(itemSelectionChanged()),
|
||||
this, SIGNAL(completeChanged()));
|
||||
}
|
||||
|
||||
bool Wizard::ExistingInstallationPage::validatePage()
|
||||
{
|
||||
// See if Morrowind.ini is detected, if not, ask the user
|
||||
// It can be missing entirely
|
||||
// Or failed to be detected due to the target being a symlink
|
||||
|
||||
QString path(field(QLatin1String("installation.path")).toString());
|
||||
QFile file(mWizard->mInstallations[path].iniPath);
|
||||
|
||||
if (!file.exists()) {
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("Error detecting Morrowind configuration"));
|
||||
msgBox.setIcon(QMessageBox::Warning);
|
||||
msgBox.setStandardButtons(QMessageBox::Cancel);
|
||||
msgBox.setText(QObject::tr("<br><b>Could not find Morrowind.ini</b><br><br> \
|
||||
The Wizard needs to update settings in this file.<br><br> \
|
||||
Press \"Browse...\" to specify the location manually.<br>"));
|
||||
|
||||
QAbstractButton *browseButton =
|
||||
msgBox.addButton(QObject::tr("B&rowse..."), QMessageBox::ActionRole);
|
||||
|
||||
msgBox.exec();
|
||||
|
||||
QString iniFile;
|
||||
if (msgBox.clickedButton() == browseButton) {
|
||||
iniFile = QFileDialog::getOpenFileName(
|
||||
this,
|
||||
QObject::tr("Select configuration file"),
|
||||
QDir::currentPath(),
|
||||
QString(tr("Morrowind configuration file (*.ini)")));
|
||||
}
|
||||
|
||||
if (iniFile.isEmpty()) {
|
||||
return false; // Cancel was clicked;
|
||||
}
|
||||
|
||||
// A proper Morrowind.ini was selected, set it
|
||||
QFileInfo info(iniFile);
|
||||
mWizard->mInstallations[path].iniPath = info.absoluteFilePath();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Wizard::ExistingInstallationPage::on_browseButton_clicked()
|
||||
{
|
||||
QString selectedFile = QFileDialog::getOpenFileName(
|
||||
this,
|
||||
tr("Select master file"),
|
||||
QDir::currentPath(),
|
||||
QString(tr("Morrowind master file (*.esm)")),
|
||||
NULL,
|
||||
QFileDialog::DontResolveSymlinks);
|
||||
|
||||
if (selectedFile.isEmpty())
|
||||
return;
|
||||
|
||||
QFileInfo info(selectedFile);
|
||||
|
||||
if (!info.exists())
|
||||
return;
|
||||
|
||||
if (!mWizard->findFiles(QLatin1String("Morrowind"), info.absolutePath()))
|
||||
return; // No valid Morrowind installation found
|
||||
|
||||
QString path(QDir::toNativeSeparators(info.absolutePath()));
|
||||
QList<QListWidgetItem*> items = installationsList->findItems(path, Qt::MatchExactly);
|
||||
|
||||
if (items.isEmpty()) {
|
||||
// Path is not yet in the list, add it
|
||||
mWizard->addInstallation(path);
|
||||
|
||||
// Hide the default item
|
||||
installationsList->item(0)->setHidden(true);
|
||||
|
||||
QListWidgetItem *item = new QListWidgetItem(path);
|
||||
installationsList->addItem(item);
|
||||
installationsList->setCurrentItem(item); // Select it too
|
||||
} else {
|
||||
installationsList->setCurrentItem(items.first());
|
||||
}
|
||||
|
||||
// Update the button
|
||||
emit completeChanged();
|
||||
}
|
||||
|
||||
void Wizard::ExistingInstallationPage::textChanged(const QString &text)
|
||||
{
|
||||
// Set the installation path manually, as registerField doesn't work
|
||||
// Because it doesn't accept two widgets operating on a single field
|
||||
if (!text.isEmpty())
|
||||
mWizard->setField(QLatin1String("installation.path"), text);
|
||||
}
|
||||
|
||||
bool Wizard::ExistingInstallationPage::isComplete() const
|
||||
{
|
||||
if (installationsList->selectionModel()->hasSelection()) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int Wizard::ExistingInstallationPage::nextId() const
|
||||
{
|
||||
return MainWizard::Page_LanguageSelection;
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
#ifndef EXISTINGINSTALLATIONPAGE_HPP
|
||||
#define EXISTINGINSTALLATIONPAGE_HPP
|
||||
|
||||
#include <QWizardPage>
|
||||
|
||||
#include "ui_existinginstallationpage.h"
|
||||
|
||||
namespace Wizard
|
||||
{
|
||||
class MainWizard;
|
||||
|
||||
class ExistingInstallationPage : public QWizardPage, private Ui::ExistingInstallationPage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ExistingInstallationPage(QWidget *parent);
|
||||
|
||||
int nextId() const;
|
||||
virtual bool isComplete() const;
|
||||
virtual bool validatePage();
|
||||
|
||||
private slots:
|
||||
void on_browseButton_clicked();
|
||||
void textChanged(const QString &text);
|
||||
|
||||
|
||||
private:
|
||||
MainWizard *mWizard;
|
||||
|
||||
protected:
|
||||
void initializePage();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // EXISTINGINSTALLATIONPAGE_HPP
|
@ -0,0 +1,19 @@
|
||||
#include "importpage.hpp"
|
||||
|
||||
#include "mainwizard.hpp"
|
||||
|
||||
Wizard::ImportPage::ImportPage(QWidget *parent) :
|
||||
QWizardPage(parent)
|
||||
{
|
||||
mWizard = qobject_cast<MainWizard*>(parent);
|
||||
|
||||
setupUi(this);
|
||||
|
||||
registerField(QLatin1String("installation.import-settings"), importCheckBox);
|
||||
registerField(QLatin1String("installation.import-addons"), addonsCheckBox);
|
||||
}
|
||||
|
||||
int Wizard::ImportPage::nextId() const
|
||||
{
|
||||
return MainWizard::Page_Conclusion;
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
#ifndef IMPORTPAGE_HPP
|
||||
#define IMPORTPAGE_HPP
|
||||
|
||||
#include <QWizardPage>
|
||||
|
||||
#include "ui_importpage.h"
|
||||
|
||||
namespace Wizard
|
||||
{
|
||||
class MainWizard;
|
||||
|
||||
class ImportPage : public QWizardPage, private Ui::ImportPage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ImportPage(QWidget *parent);
|
||||
|
||||
int nextId() const;
|
||||
|
||||
private:
|
||||
MainWizard *mWizard;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // IMPORTPAGE_HPP
|
@ -0,0 +1,219 @@
|
||||
#include "inisettings.hpp"
|
||||
|
||||
#include <QDir>
|
||||
|
||||
#include <QTextStream>
|
||||
#include <QFile>
|
||||
#include <QStringList>
|
||||
#include <QString>
|
||||
#include <QRegExp>
|
||||
#include <QDebug>
|
||||
|
||||
Wizard::IniSettings::IniSettings()
|
||||
{
|
||||
}
|
||||
|
||||
Wizard::IniSettings::~IniSettings()
|
||||
{
|
||||
}
|
||||
|
||||
QStringList Wizard::IniSettings::findKeys(const QString &text)
|
||||
{
|
||||
QStringList result;
|
||||
|
||||
foreach (const QString &key, mSettings.keys()) {
|
||||
|
||||
if (key.startsWith(text))
|
||||
result << key;
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Wizard::IniSettings::readFile(QTextStream &stream)
|
||||
{
|
||||
// Look for a square bracket, "'\\["
|
||||
// that has one or more "not nothing" in it, "([^]]+)"
|
||||
// and is closed with a square bracket, "\\]"
|
||||
QRegExp sectionRe(QLatin1String("^\\[([^]]+)\\]"));
|
||||
|
||||
// Find any character(s) that is/are not equal sign(s), "[^=]+"
|
||||
// followed by an optional whitespace, an equal sign, and another optional whitespace, "\\s*=\\s*"
|
||||
// and one or more periods, "(.+)"
|
||||
QRegExp keyRe(QLatin1String("^([^=]+)\\s*=\\s*(.+)$"));
|
||||
|
||||
QString currentSection;
|
||||
|
||||
while (!stream.atEnd())
|
||||
{
|
||||
const QString line(stream.readLine());
|
||||
|
||||
if (line.isEmpty() || line.startsWith(QLatin1Char(';')))
|
||||
continue;
|
||||
|
||||
if (sectionRe.exactMatch(line))
|
||||
{
|
||||
currentSection = sectionRe.cap(1);
|
||||
}
|
||||
else if (keyRe.indexIn(line) != -1)
|
||||
{
|
||||
QString key = keyRe.cap(1).trimmed();
|
||||
QString value = keyRe.cap(2).trimmed();
|
||||
|
||||
// Append the section, but only if there is one
|
||||
if (!currentSection.isEmpty())
|
||||
key = currentSection + QLatin1Char('/') + key;
|
||||
|
||||
mSettings[key] = QVariant(value);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Wizard::IniSettings::writeFile(const QString &path, QTextStream &stream)
|
||||
{
|
||||
// Look for a square bracket, "'\\["
|
||||
// that has one or more "not nothing" in it, "([^]]+)"
|
||||
// and is closed with a square bracket, "\\]"
|
||||
QRegExp sectionRe(QLatin1String("^\\[([^]]+)\\]"));
|
||||
|
||||
// Find any character(s) that is/are not equal sign(s), "[^=]+"
|
||||
// followed by an optional whitespace, an equal sign, and another optional whitespace, "\\s*=\\s*"
|
||||
// and one or more periods, "(.+)"
|
||||
QRegExp keyRe(QLatin1String("^([^=]+)\\s*=\\s*(.+)$"));
|
||||
|
||||
const QStringList keys(mSettings.keys());
|
||||
|
||||
QString currentSection;
|
||||
QString buffer;
|
||||
|
||||
while (!stream.atEnd()) {
|
||||
|
||||
const QString line(stream.readLine());
|
||||
|
||||
if (line.isEmpty() || line.startsWith(QLatin1Char(';'))) {
|
||||
buffer.append(line + QLatin1String("\n"));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sectionRe.exactMatch(line)) {
|
||||
buffer.append(line + QLatin1String("\n"));
|
||||
currentSection = sectionRe.cap(1);
|
||||
} else if (keyRe.indexIn(line) != -1) {
|
||||
QString key(keyRe.cap(1).trimmed());
|
||||
QString lookupKey(key);
|
||||
|
||||
// Append the section, but only if there is one
|
||||
if (!currentSection.isEmpty())
|
||||
lookupKey = currentSection + QLatin1Char('/') + key;
|
||||
|
||||
buffer.append(key + QLatin1Char('=') + mSettings[lookupKey].toString() + QLatin1String("\n"));
|
||||
mSettings.remove(lookupKey);
|
||||
}
|
||||
}
|
||||
|
||||
// Add the new settings to the buffer
|
||||
QHashIterator<QString, QVariant> i(mSettings);
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
|
||||
QStringList fullKey(i.key().split(QLatin1Char('/')));
|
||||
QString section(fullKey.at(0));
|
||||
section.prepend(QLatin1Char('['));
|
||||
section.append(QLatin1Char(']'));
|
||||
QString key(fullKey.at(1));
|
||||
|
||||
int index = buffer.lastIndexOf(section);
|
||||
if (index != -1) {
|
||||
// Look for the next section
|
||||
index = buffer.indexOf(QLatin1Char('['), index + 1);
|
||||
|
||||
if (index == -1 ) {
|
||||
// We are at the last section, append it to the bottom of the file
|
||||
buffer.append(QString("\n%1=%2").arg(key, i.value().toString()));
|
||||
mSettings.remove(i.key());
|
||||
continue;
|
||||
} else {
|
||||
// Not at last section, add the key at the index
|
||||
buffer.insert(index - 1, QString("\n%1=%2").arg(key, i.value().toString()));
|
||||
mSettings.remove(i.key());
|
||||
}
|
||||
|
||||
} else {
|
||||
// Add the section to the end of the file, because it's not found
|
||||
buffer.append(QString("\n%1\n").arg(section));
|
||||
i.previous();
|
||||
}
|
||||
}
|
||||
|
||||
// Now we reopen the file, this time we write
|
||||
QFile file(path);
|
||||
|
||||
if (file.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text)) {
|
||||
QTextStream in(&file);
|
||||
in.setCodec(stream.codec());
|
||||
|
||||
// Write the updated buffer to an empty file
|
||||
in << buffer;
|
||||
file.flush();
|
||||
file.close();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Wizard::IniSettings::parseInx(const QString &path)
|
||||
{
|
||||
QFile file(path);
|
||||
|
||||
if (file.open(QIODevice::ReadOnly))
|
||||
{
|
||||
|
||||
const QByteArray data(file.readAll());
|
||||
const QByteArray pattern("\x21\x00\x1A\x01\x04\x00\x04\x97\xFF\x06", 10);
|
||||
|
||||
int i = 0;
|
||||
while ((i = data.indexOf(pattern, i)) != -1) {
|
||||
|
||||
int next = data.indexOf(pattern, i + 1);
|
||||
if (next == -1)
|
||||
break;
|
||||
|
||||
QByteArray array(data.mid(i, (next - i)));
|
||||
|
||||
// Skip some invalid entries
|
||||
if (array.contains("\x04\x96\xFF")) {
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remove the pattern from the beginning
|
||||
array.remove(0, 12);
|
||||
|
||||
int index = array.indexOf("\x06");
|
||||
const QString section(array.left(index));
|
||||
|
||||
// Figure how many characters to read for the key
|
||||
int lenght = array.indexOf("\x06", section.length() + 3) - (section.length() + 3);
|
||||
const QString key(array.mid(section.length() + 3, lenght));
|
||||
|
||||
QString value(array.mid(section.length() + key.length() + 6));
|
||||
|
||||
// Add the value
|
||||
setValue(section + QLatin1Char('/') + key, QVariant(value));
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
file.close();
|
||||
} else {
|
||||
qDebug() << "Failed to open INX file: " << path;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
#ifndef INISETTINGS_HPP
|
||||
#define INISETTINGS_HPP
|
||||
|
||||
#include <QHash>
|
||||
#include <QVariant>
|
||||
|
||||
class QTextStream;
|
||||
|
||||
namespace Wizard
|
||||
{
|
||||
|
||||
typedef QHash<QString, QVariant> SettingsMap;
|
||||
|
||||
class IniSettings
|
||||
{
|
||||
public:
|
||||
explicit IniSettings();
|
||||
~IniSettings();
|
||||
|
||||
inline QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const
|
||||
{
|
||||
return mSettings.value(key, defaultValue);
|
||||
}
|
||||
|
||||
inline QList<QVariant> values() const
|
||||
{
|
||||
return mSettings.values();
|
||||
}
|
||||
|
||||
inline void setValue(const QString &key, const QVariant &value)
|
||||
{
|
||||
mSettings.insert(key, value);
|
||||
}
|
||||
|
||||
inline void remove(const QString &key)
|
||||
{
|
||||
mSettings.remove(key);
|
||||
}
|
||||
|
||||
QStringList findKeys(const QString &text);
|
||||
|
||||
bool readFile(QTextStream &stream);
|
||||
bool writeFile(const QString &path, QTextStream &stream);
|
||||
|
||||
bool parseInx(const QString &path);
|
||||
|
||||
private:
|
||||
|
||||
int getLastNewline(const QString &buffer, int from);
|
||||
|
||||
SettingsMap mSettings;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // INISETTINGS_HPP
|
@ -0,0 +1,244 @@
|
||||
#include "installationpage.hpp"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QTextCodec>
|
||||
#include <QFileInfo>
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "mainwizard.hpp"
|
||||
#include "inisettings.hpp"
|
||||
|
||||
Wizard::InstallationPage::InstallationPage(QWidget *parent) :
|
||||
QWizardPage(parent)
|
||||
{
|
||||
mWizard = qobject_cast<MainWizard*>(parent);
|
||||
|
||||
setupUi(this);
|
||||
|
||||
mFinished = false;
|
||||
|
||||
mThread = new QThread();
|
||||
mUnshield = new UnshieldWorker();
|
||||
mUnshield->moveToThread(mThread);
|
||||
|
||||
connect(mThread, SIGNAL(started()),
|
||||
mUnshield, SLOT(extract()));
|
||||
|
||||
connect(mUnshield, SIGNAL(finished()),
|
||||
mThread, SLOT(quit()));
|
||||
|
||||
connect(mUnshield, SIGNAL(finished()),
|
||||
mUnshield, SLOT(deleteLater()));
|
||||
|
||||
connect(mUnshield, SIGNAL(finished()),
|
||||
mThread, SLOT(deleteLater()));;
|
||||
|
||||
connect(mUnshield, SIGNAL(finished()),
|
||||
this, SLOT(installationFinished()), Qt::QueuedConnection);
|
||||
|
||||
connect(mUnshield, SIGNAL(error(QString, QString)),
|
||||
this, SLOT(installationError(QString, QString)), Qt::QueuedConnection);
|
||||
|
||||
connect(mUnshield, SIGNAL(textChanged(QString)),
|
||||
installProgressLabel, SLOT(setText(QString)), Qt::QueuedConnection);
|
||||
|
||||
connect(mUnshield, SIGNAL(textChanged(QString)),
|
||||
logTextEdit, SLOT(appendPlainText(QString)), Qt::QueuedConnection);
|
||||
|
||||
connect(mUnshield, SIGNAL(textChanged(QString)),
|
||||
mWizard, SLOT(addLogText(QString)), Qt::QueuedConnection);
|
||||
|
||||
connect(mUnshield, SIGNAL(progressChanged(int)),
|
||||
installProgressBar, SLOT(setValue(int)), Qt::QueuedConnection);
|
||||
|
||||
connect(mUnshield, SIGNAL(requestFileDialog(Wizard::Component)),
|
||||
this, SLOT(showFileDialog(Wizard::Component)), Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
Wizard::InstallationPage::~InstallationPage()
|
||||
{
|
||||
if (mThread->isRunning()) {
|
||||
mUnshield->stopWorker();
|
||||
mThread->wait();
|
||||
}
|
||||
|
||||
delete mUnshield;
|
||||
delete mThread;
|
||||
}
|
||||
|
||||
void Wizard::InstallationPage::initializePage()
|
||||
{
|
||||
QString path(field(QLatin1String("installation.path")).toString());
|
||||
QStringList components(field(QLatin1String("installation.components")).toStringList());
|
||||
|
||||
logTextEdit->appendPlainText(QString("Installing to %1").arg(path));
|
||||
logTextEdit->appendPlainText(QString("Installing %1.").arg(components.join(", ")));
|
||||
|
||||
installProgressBar->setMinimum(0);
|
||||
|
||||
// Set the progressbar maximum to a multiple of 100
|
||||
// That way installing all three components would yield 300%
|
||||
// When one component is done the bar will be filled by 33%
|
||||
|
||||
if (field(QLatin1String("installation.new")).toBool() == true) {
|
||||
installProgressBar->setMaximum((components.count() * 100));
|
||||
} else {
|
||||
if (components.contains(QLatin1String("Tribunal"))
|
||||
&& !mWizard->mInstallations[path].hasTribunal)
|
||||
installProgressBar->setMaximum(100);
|
||||
|
||||
if (components.contains(QLatin1String("Bloodmoon"))
|
||||
&& !mWizard->mInstallations[path].hasBloodmoon)
|
||||
installProgressBar->setMaximum(installProgressBar->maximum() + 100);
|
||||
}
|
||||
|
||||
startInstallation();
|
||||
}
|
||||
|
||||
void Wizard::InstallationPage::startInstallation()
|
||||
{
|
||||
QStringList components(field(QLatin1String("installation.components")).toStringList());
|
||||
QString path(field(QLatin1String("installation.path")).toString());
|
||||
|
||||
if (field(QLatin1String("installation.new")).toBool() == true)
|
||||
{
|
||||
// Always install Morrowind
|
||||
mUnshield->setInstallComponent(Wizard::Component_Morrowind, true);
|
||||
|
||||
if (components.contains(QLatin1String("Tribunal")))
|
||||
mUnshield->setInstallComponent(Wizard::Component_Tribunal, true);
|
||||
|
||||
if (components.contains(QLatin1String("Bloodmoon")))
|
||||
mUnshield->setInstallComponent(Wizard::Component_Bloodmoon, true);
|
||||
} else {
|
||||
// Morrowind should already be installed
|
||||
mUnshield->setInstallComponent(Wizard::Component_Morrowind, false);
|
||||
|
||||
if (components.contains(QLatin1String("Tribunal"))
|
||||
&& !mWizard->mInstallations[path].hasTribunal)
|
||||
mUnshield->setInstallComponent(Wizard::Component_Tribunal, true);
|
||||
|
||||
if (components.contains(QLatin1String("Bloodmoon"))
|
||||
&& !mWizard->mInstallations[path].hasBloodmoon)
|
||||
mUnshield->setInstallComponent(Wizard::Component_Bloodmoon, true);
|
||||
|
||||
// Set the location of the Morrowind.ini to update
|
||||
mUnshield->setIniPath(mWizard->mInstallations[path].iniPath);
|
||||
mUnshield->setupSettings();
|
||||
}
|
||||
|
||||
// Set the installation target path
|
||||
mUnshield->setPath(path);
|
||||
|
||||
// Set the right codec to use for Morrowind.ini
|
||||
QString language(field(QLatin1String("installation.language")).toString());
|
||||
|
||||
if (language == QLatin1String("Polish")) {
|
||||
mUnshield->setIniCodec(QTextCodec::codecForName("windows-1250"));
|
||||
} else if (language == QLatin1String("Russian")) {
|
||||
mUnshield->setIniCodec(QTextCodec::codecForName("windows-1251"));
|
||||
} else {
|
||||
mUnshield->setIniCodec(QTextCodec::codecForName("windows-1252"));
|
||||
}
|
||||
|
||||
mThread->start();
|
||||
}
|
||||
|
||||
void Wizard::InstallationPage::showFileDialog(Wizard::Component component)
|
||||
{
|
||||
QString name;
|
||||
switch (component) {
|
||||
|
||||
case Wizard::Component_Morrowind:
|
||||
name = QLatin1String("Morrowind");
|
||||
break;
|
||||
case Wizard::Component_Tribunal:
|
||||
name = QLatin1String("Tribunal");
|
||||
break;
|
||||
case Wizard::Component_Bloodmoon:
|
||||
name = QLatin1String("Bloodmoon");
|
||||
break;
|
||||
}
|
||||
|
||||
QString path = QFileDialog::getExistingDirectory(this,
|
||||
tr("Select %1 installation media").arg(name),
|
||||
QDir::rootPath());
|
||||
|
||||
if (path.isEmpty()) {
|
||||
logTextEdit->appendHtml(tr("<p><br/><span style=\"color:red;\"> \
|
||||
<b>Error: The installation was aborted by the user</b></p>"));
|
||||
|
||||
mWizard->addLogText(QLatin1String("Error: The installation was aborted by the user"));
|
||||
mWizard->mError = true;
|
||||
|
||||
emit completeChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
mUnshield->setDiskPath(path);
|
||||
}
|
||||
|
||||
void Wizard::InstallationPage::installationFinished()
|
||||
{
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("Installation finished"));
|
||||
msgBox.setIcon(QMessageBox::Information);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setText(tr("Installation completed sucessfully!"));
|
||||
|
||||
msgBox.exec();
|
||||
|
||||
mFinished = true;
|
||||
emit completeChanged();
|
||||
}
|
||||
|
||||
void Wizard::InstallationPage::installationError(const QString &text, const QString &details)
|
||||
{
|
||||
installProgressLabel->setText(tr("Installation failed!"));
|
||||
|
||||
logTextEdit->appendHtml(tr("<p><br/><span style=\"color:red;\"> \
|
||||
<b>Error: %1</b></p>").arg(text));
|
||||
logTextEdit->appendHtml(tr("<p><span style=\"color:red;\"> \
|
||||
<b>%1</b></p>").arg(details));
|
||||
|
||||
mWizard->addLogText(QLatin1String("Error: ") + text);
|
||||
mWizard->addLogText(details);
|
||||
|
||||
mWizard->mError = true;
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("An error occurred"));
|
||||
msgBox.setIcon(QMessageBox::Critical);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setText(tr("<html><head/><body><p><b>The Wizard has encountered an error</b></p> \
|
||||
<p>The error reported was:</p><p>%1</p> \
|
||||
<p>Press "Show Details..." for more information.</p></body></html>").arg(text));
|
||||
|
||||
msgBox.setDetailedText(details);
|
||||
msgBox.exec();
|
||||
|
||||
|
||||
emit completeChanged();
|
||||
}
|
||||
|
||||
bool Wizard::InstallationPage::isComplete() const
|
||||
{
|
||||
if (!mWizard->mError) {
|
||||
return mFinished;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
int Wizard::InstallationPage::nextId() const
|
||||
{
|
||||
if (field(QLatin1String("installation.new")).toBool() == true) {
|
||||
return MainWizard::Page_Conclusion;
|
||||
} else {
|
||||
if (!mWizard->mError) {
|
||||
return MainWizard::Page_Import;
|
||||
} else {
|
||||
return MainWizard::Page_Conclusion;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
#ifndef INSTALLATIONPAGE_HPP
|
||||
#define INSTALLATIONPAGE_HPP
|
||||
|
||||
#include <QWizardPage>
|
||||
|
||||
#include "unshield/unshieldworker.hpp"
|
||||
#include "ui_installationpage.h"
|
||||
#include "inisettings.hpp"
|
||||
|
||||
class QThread;
|
||||
|
||||
namespace Wizard
|
||||
{
|
||||
class MainWizard;
|
||||
class IniSettings;
|
||||
class UnshieldWorker;
|
||||
|
||||
class InstallationPage : public QWizardPage, private Ui::InstallationPage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
InstallationPage(QWidget *parent);
|
||||
~InstallationPage();
|
||||
|
||||
int nextId() const;
|
||||
virtual bool isComplete() const;
|
||||
|
||||
private:
|
||||
MainWizard *mWizard;
|
||||
bool mFinished;
|
||||
|
||||
QThread* mThread;
|
||||
UnshieldWorker *mUnshield;
|
||||
|
||||
void startInstallation();
|
||||
|
||||
private slots:
|
||||
void showFileDialog(Wizard::Component component);
|
||||
|
||||
void installationFinished();
|
||||
void installationError(const QString &text, const QString &details);
|
||||
|
||||
protected:
|
||||
void initializePage();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // INSTALLATIONPAGE_HPP
|
@ -0,0 +1,101 @@
|
||||
#include "installationtargetpage.hpp"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "mainwizard.hpp"
|
||||
|
||||
Wizard::InstallationTargetPage::InstallationTargetPage(QWidget *parent, const Files::ConfigurationManager &cfg) :
|
||||
QWizardPage(parent),
|
||||
mCfgMgr(cfg)
|
||||
{
|
||||
mWizard = qobject_cast<MainWizard*>(parent);
|
||||
|
||||
setupUi(this);
|
||||
|
||||
registerField(QLatin1String("installation.path*"), targetLineEdit);
|
||||
}
|
||||
|
||||
void Wizard::InstallationTargetPage::initializePage()
|
||||
{
|
||||
QString path(QFile::decodeName(mCfgMgr.getUserDataPath().string().c_str()));
|
||||
path.append(QDir::separator() + QLatin1String("data"));
|
||||
|
||||
QDir dir(path);
|
||||
targetLineEdit->setText(QDir::toNativeSeparators(dir.absolutePath()));
|
||||
}
|
||||
|
||||
bool Wizard::InstallationTargetPage::validatePage()
|
||||
{
|
||||
QString path(field(QLatin1String("installation.path")).toString());
|
||||
|
||||
qDebug() << "Validating path: " << path;
|
||||
|
||||
if (!QFile::exists(path)) {
|
||||
QDir dir;
|
||||
|
||||
if (!dir.mkpath(path)) {
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("Error creating destination"));
|
||||
msgBox.setIcon(QMessageBox::Warning);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setText(tr("<html><head/><body><p><b>Could not create the destination directory</b></p> \
|
||||
<p>Please make sure you have the right permissions \
|
||||
and try again, or specify a different location.</p></body></html>"));
|
||||
msgBox.exec();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
QFileInfo info(path);
|
||||
|
||||
if (!info.isWritable()) {
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("Insufficient permissions"));
|
||||
msgBox.setIcon(QMessageBox::Warning);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setText(tr("<html><head/><body><p><b>Could not write to the destination directory</b></p> \
|
||||
<p>Please make sure you have the right permissions \
|
||||
and try again, or specify a different location.</p></body></html>"));
|
||||
msgBox.exec();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mWizard->findFiles(QLatin1String("Morrowind"), path)) {
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("Destination not empty"));
|
||||
msgBox.setIcon(QMessageBox::Warning);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setText(tr("<html><head/><body><p><b>The destination directory is not empty</b></p> \
|
||||
<p>An existing Morrowind installation is present in the specified location.</p> \
|
||||
<p>Please specify a different location, or go back and select the location as an existing installation.</p></body></html>"));
|
||||
msgBox.exec();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Wizard::InstallationTargetPage::on_browseButton_clicked()
|
||||
{
|
||||
QString selectedPath = QFileDialog::getExistingDirectory(
|
||||
this,
|
||||
tr("Select where to install Morrowind"),
|
||||
QDir::homePath(),
|
||||
QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
|
||||
|
||||
qDebug() << selectedPath;
|
||||
QFileInfo info(selectedPath);
|
||||
if (!info.exists())
|
||||
return;
|
||||
|
||||
if (info.isWritable())
|
||||
targetLineEdit->setText(info.absoluteFilePath());
|
||||
|
||||
}
|
||||
|
||||
int Wizard::InstallationTargetPage::nextId() const
|
||||
{
|
||||
return MainWizard::Page_LanguageSelection;
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
#ifndef INSTALLATIONTARGETPAGE_HPP
|
||||
#define INSTALLATIONTARGETPAGE_HPP
|
||||
|
||||
#include <QWizardPage>
|
||||
|
||||
#include "ui_installationtargetpage.h"
|
||||
|
||||
namespace Files
|
||||
{
|
||||
struct ConfigurationManager;
|
||||
}
|
||||
|
||||
namespace Wizard
|
||||
{
|
||||
class MainWizard;
|
||||
|
||||
class InstallationTargetPage : public QWizardPage, private Ui::InstallationTargetPage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
InstallationTargetPage(QWidget *parent, const Files::ConfigurationManager &cfg);
|
||||
|
||||
int nextId() const;
|
||||
virtual bool validatePage();
|
||||
|
||||
private slots:
|
||||
void on_browseButton_clicked();
|
||||
|
||||
private:
|
||||
MainWizard *mWizard;
|
||||
const Files::ConfigurationManager &mCfgMgr;
|
||||
|
||||
protected:
|
||||
void initializePage();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // INSTALLATIONTARGETPAGE_HPP
|
@ -0,0 +1,17 @@
|
||||
#include "intropage.hpp"
|
||||
|
||||
#include "mainwizard.hpp"
|
||||
|
||||
Wizard::IntroPage::IntroPage(QWidget *parent) :
|
||||
QWizardPage(parent)
|
||||
{
|
||||
mWizard = qobject_cast<MainWizard*>(parent);
|
||||
|
||||
setupUi(this);
|
||||
setPixmap(QWizard::WatermarkPixmap, QPixmap(QLatin1String(":/images/intropage-background.png")));
|
||||
}
|
||||
|
||||
int Wizard::IntroPage::nextId() const
|
||||
{
|
||||
return MainWizard::Page_MethodSelection;
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
#ifndef INTROPAGE_HPP
|
||||
#define INTROPAGE_HPP
|
||||
|
||||
#include <QWizardPage>
|
||||
|
||||
#include "ui_intropage.h"
|
||||
|
||||
namespace Wizard
|
||||
{
|
||||
class MainWizard;
|
||||
|
||||
class IntroPage : public QWizardPage, private Ui::IntroPage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
IntroPage(QWidget *parent);
|
||||
|
||||
int nextId() const;
|
||||
|
||||
private:
|
||||
MainWizard *mWizard;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // INTROPAGE_HPP
|
@ -0,0 +1,51 @@
|
||||
#include "languageselectionpage.hpp"
|
||||
|
||||
#include "mainwizard.hpp"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
Wizard::LanguageSelectionPage::LanguageSelectionPage(QWidget *parent) :
|
||||
QWizardPage(parent)
|
||||
{
|
||||
mWizard = qobject_cast<MainWizard*>(parent);
|
||||
|
||||
setupUi(this);
|
||||
|
||||
registerField(QLatin1String("installation.language"), languageComboBox);
|
||||
}
|
||||
|
||||
void Wizard::LanguageSelectionPage::initializePage()
|
||||
{
|
||||
QStringList languages;
|
||||
languages << QLatin1String("English")
|
||||
<< QLatin1String("French")
|
||||
<< QLatin1String("German")
|
||||
<< QLatin1String("Italian")
|
||||
<< QLatin1String("Polish")
|
||||
<< QLatin1String("Russian")
|
||||
<< QLatin1String("Spanish");
|
||||
|
||||
languageComboBox->addItems(languages);
|
||||
}
|
||||
|
||||
int Wizard::LanguageSelectionPage::nextId() const
|
||||
{
|
||||
if (field(QLatin1String("installation.new")).toBool() == true) {
|
||||
return MainWizard::Page_ComponentSelection;
|
||||
} else {
|
||||
QString path(field(QLatin1String("installation.path")).toString());
|
||||
|
||||
if (path.isEmpty())
|
||||
return MainWizard::Page_ComponentSelection;
|
||||
|
||||
// Check if we have to install something
|
||||
if (mWizard->mInstallations[path].hasMorrowind == true &&
|
||||
mWizard->mInstallations[path].hasTribunal == true &&
|
||||
mWizard->mInstallations[path].hasBloodmoon == true)
|
||||
{
|
||||
return MainWizard::Page_Import;
|
||||
} else {
|
||||
return MainWizard::Page_ComponentSelection;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
#ifndef LANGUAGESELECTIONPAGE_HPP
|
||||
#define LANGUAGESELECTIONPAGE_HPP
|
||||
|
||||
#include <QWizardPage>
|
||||
|
||||
#include "ui_languageselectionpage.h"
|
||||
|
||||
namespace Wizard
|
||||
{
|
||||
class MainWizard;
|
||||
|
||||
class LanguageSelectionPage : public QWizardPage, private Ui::LanguageSelectionPage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
LanguageSelectionPage(QWidget *parent);
|
||||
|
||||
int nextId() const;
|
||||
|
||||
private:
|
||||
MainWizard *mWizard;
|
||||
|
||||
protected:
|
||||
void initializePage();
|
||||
};
|
||||
}
|
||||
|
||||
#endif // LANGUAGESELECTIONPAGE_HPP
|
@ -0,0 +1,48 @@
|
||||
#include <QApplication>
|
||||
#include <QTextCodec>
|
||||
#include <QDir>
|
||||
#include <QDebug>
|
||||
|
||||
#include "mainwizard.hpp"
|
||||
|
||||
#ifdef MAC_OS_X_VERSION_MIN_REQUIRED
|
||||
#undef MAC_OS_X_VERSION_MIN_REQUIRED
|
||||
// We need to do this because of Qt: https://bugreports.qt-project.org/browse/QTBUG-22154
|
||||
#define MAC_OS_X_VERSION_MIN_REQUIRED __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__
|
||||
#endif // MAC_OS_X_VERSION_MIN_REQUIRED
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
||||
QApplication app(argc, argv);
|
||||
|
||||
// Now we make sure the current dir is set to application path
|
||||
QDir dir(QCoreApplication::applicationDirPath());
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
if (dir.dirName() == "MacOS") {
|
||||
dir.cdUp();
|
||||
dir.cdUp();
|
||||
dir.cdUp();
|
||||
}
|
||||
|
||||
// force Qt to load only LOCAL plugins, don't touch system Qt installation
|
||||
QDir pluginsPath(QCoreApplication::applicationDirPath());
|
||||
pluginsPath.cdUp();
|
||||
pluginsPath.cd("Plugins");
|
||||
|
||||
QStringList libraryPaths;
|
||||
libraryPaths << pluginsPath.path() << QCoreApplication::applicationDirPath();
|
||||
app.setLibraryPaths(libraryPaths);
|
||||
#endif
|
||||
|
||||
QDir::setCurrent(dir.absolutePath());
|
||||
|
||||
// Support non-latin characters
|
||||
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
|
||||
|
||||
Wizard::MainWizard wizard;
|
||||
|
||||
wizard.show();
|
||||
return app.exec();
|
||||
}
|
@ -0,0 +1,457 @@
|
||||
#include "mainwizard.hpp"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include <QTime>
|
||||
#include <QDateTime>
|
||||
#include <QCloseEvent>
|
||||
#include <QMessageBox>
|
||||
#include <QTextCodec>
|
||||
#include <QDir>
|
||||
|
||||
#include "intropage.hpp"
|
||||
#include "methodselectionpage.hpp"
|
||||
#include "languageselectionpage.hpp"
|
||||
#include "existinginstallationpage.hpp"
|
||||
#include "installationtargetpage.hpp"
|
||||
#include "componentselectionpage.hpp"
|
||||
#include "installationpage.hpp"
|
||||
#include "importpage.hpp"
|
||||
#include "conclusionpage.hpp"
|
||||
|
||||
using namespace Process;
|
||||
|
||||
Wizard::MainWizard::MainWizard(QWidget *parent) :
|
||||
mGameSettings(mCfgMgr),
|
||||
QWizard(parent),
|
||||
mError(false),
|
||||
mInstallations()
|
||||
{
|
||||
#ifndef Q_OS_MAC
|
||||
setWizardStyle(QWizard::ModernStyle);
|
||||
#else
|
||||
setWizardStyle(QWizard::ClassicStyle);
|
||||
#endif
|
||||
|
||||
setWindowTitle(tr("OpenMW Wizard"));
|
||||
setWindowIcon(QIcon(QLatin1String(":/images/openmw-wizard.png")));
|
||||
setMinimumWidth(550);
|
||||
|
||||
// Set the property for comboboxes to the text instead of index
|
||||
setDefaultProperty("QComboBox", "currentText", "currentIndexChanged");
|
||||
|
||||
setDefaultProperty("ComponentListWidget", "mCheckedItems", "checkedItemsChanged");
|
||||
|
||||
mImporterInvoker = new ProcessInvoker();
|
||||
|
||||
connect(mImporterInvoker->getProcess(), SIGNAL(started()),
|
||||
this, SLOT(importerStarted()));
|
||||
|
||||
connect(mImporterInvoker->getProcess(), SIGNAL(finished(int,QProcess::ExitStatus)),
|
||||
this, SLOT(importerFinished(int,QProcess::ExitStatus)));
|
||||
|
||||
mLogError = tr("<html><head/><body><p><b>Could not open %1 for writing</b></p> \
|
||||
<p>Please make sure you have the right permissions \
|
||||
and try again.</p></body></html>");
|
||||
|
||||
setupLog();
|
||||
setupGameSettings();
|
||||
setupLauncherSettings();
|
||||
setupInstallations();
|
||||
setupPages();
|
||||
}
|
||||
|
||||
Wizard::MainWizard::~MainWizard()
|
||||
{
|
||||
delete mImporterInvoker;
|
||||
}
|
||||
|
||||
void Wizard::MainWizard::setupLog()
|
||||
{
|
||||
QString logPath(QString::fromUtf8(mCfgMgr.getLogPath().string().c_str()));
|
||||
logPath.append(QLatin1String("wizard.log"));
|
||||
|
||||
QFile file(logPath);
|
||||
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("Error opening Wizard log file"));
|
||||
msgBox.setIcon(QMessageBox::Critical);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setText(mLogError.arg(file.fileName()));
|
||||
msgBox.exec();
|
||||
return qApp->quit();
|
||||
}
|
||||
|
||||
addLogText(QString("Started OpenMW Wizard on %1").arg(QDateTime::currentDateTime().toString()));
|
||||
|
||||
qDebug() << logPath;
|
||||
}
|
||||
|
||||
void Wizard::MainWizard::addLogText(const QString &text)
|
||||
{
|
||||
QString logPath(QString::fromUtf8(mCfgMgr.getLogPath().string().c_str()));
|
||||
logPath.append(QLatin1String("wizard.log"));
|
||||
|
||||
QFile file(logPath);
|
||||
|
||||
if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) {
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("Error opening Wizard log file"));
|
||||
msgBox.setIcon(QMessageBox::Critical);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setText(mLogError.arg(file.fileName()));
|
||||
msgBox.exec();
|
||||
return qApp->quit();
|
||||
}
|
||||
|
||||
if (!file.isSequential())
|
||||
file.seek(file.size());
|
||||
|
||||
QTextStream out(&file);
|
||||
|
||||
if (!text.isEmpty())
|
||||
out << text << endl;
|
||||
|
||||
// file.close();
|
||||
}
|
||||
|
||||
void Wizard::MainWizard::setupGameSettings()
|
||||
{
|
||||
QString userPath(QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str()));
|
||||
QString globalPath(QString::fromUtf8(mCfgMgr.getGlobalPath().string().c_str()));
|
||||
QString message(tr("<html><head/><body><p><b>Could not open %1 for reading</b></p> \
|
||||
<p>Please make sure you have the right permissions \
|
||||
and try again.</p></body></html>"));
|
||||
|
||||
// Load the user config file first, separately
|
||||
// So we can write it properly, uncontaminated
|
||||
QString path(userPath + QLatin1String("openmw.cfg"));
|
||||
QFile file(path);
|
||||
|
||||
qDebug() << "Loading config file:" << qPrintable(path);
|
||||
|
||||
if (file.exists()) {
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("Error opening OpenMW configuration file"));
|
||||
msgBox.setIcon(QMessageBox::Critical);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setText(message.arg(file.fileName()));
|
||||
msgBox.exec();
|
||||
return qApp->quit();
|
||||
}
|
||||
QTextStream stream(&file);
|
||||
stream.setCodec(QTextCodec::codecForName("UTF-8"));
|
||||
|
||||
mGameSettings.readUserFile(stream);
|
||||
}
|
||||
|
||||
// Now the rest
|
||||
QStringList paths;
|
||||
paths.append(userPath + QLatin1String("openmw.cfg"));
|
||||
paths.append(QLatin1String("openmw.cfg"));
|
||||
paths.append(globalPath + QLatin1String("openmw.cfg"));
|
||||
|
||||
foreach (const QString &path, paths) {
|
||||
qDebug() << "Loading config file:" << qPrintable(path);
|
||||
|
||||
QFile file(path);
|
||||
if (file.exists()) {
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("Error opening OpenMW configuration file"));
|
||||
msgBox.setIcon(QMessageBox::Critical);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setText(message.arg(file.fileName()));
|
||||
|
||||
return qApp->quit();
|
||||
}
|
||||
QTextStream stream(&file);
|
||||
stream.setCodec(QTextCodec::codecForName("UTF-8"));
|
||||
|
||||
mGameSettings.readFile(stream);
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
|
||||
void Wizard::MainWizard::setupLauncherSettings()
|
||||
{
|
||||
QString path(QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str()));
|
||||
path.append(QLatin1String("launcher.cfg"));
|
||||
|
||||
QString message(tr("<html><head/><body><p><b>Could not open %1 for reading</b></p> \
|
||||
<p>Please make sure you have the right permissions \
|
||||
and try again.</p></body></html>"));
|
||||
|
||||
|
||||
QFile file(path);
|
||||
|
||||
qDebug() << "Loading config file:" << qPrintable(path);
|
||||
|
||||
if (file.exists()) {
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("Error opening OpenMW configuration file"));
|
||||
msgBox.setIcon(QMessageBox::Critical);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setText(message.arg(file.fileName()));
|
||||
msgBox.exec();
|
||||
return qApp->quit();
|
||||
}
|
||||
QTextStream stream(&file);
|
||||
stream.setCodec(QTextCodec::codecForName("UTF-8"));
|
||||
|
||||
mLauncherSettings.readFile(stream);
|
||||
}
|
||||
|
||||
file.close();
|
||||
|
||||
}
|
||||
|
||||
void Wizard::MainWizard::setupInstallations()
|
||||
{
|
||||
// Check if the paths actually contain a Morrowind installation
|
||||
foreach (const QString path, mGameSettings.getDataDirs()) {
|
||||
|
||||
if (findFiles(QLatin1String("Morrowind"), path))
|
||||
addInstallation(path);
|
||||
}
|
||||
}
|
||||
|
||||
void Wizard::MainWizard::runSettingsImporter()
|
||||
{
|
||||
QString path(field(QLatin1String("installation.path")).toString());
|
||||
|
||||
// Create the file if it doesn't already exist, else the importer will fail
|
||||
QString userPath(QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str()));
|
||||
QFile file(userPath + QLatin1String("openmw.cfg"));
|
||||
|
||||
if (!file.exists()) {
|
||||
if (!file.open(QIODevice::ReadWrite)) {
|
||||
// File cannot be created
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("Error writing OpenMW configuration file"));
|
||||
msgBox.setIcon(QMessageBox::Critical);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setText(tr("<html><head/><body><p><b>Could not open or create %1 for writing</b></p> \
|
||||
<p>Please make sure you have the right permissions \
|
||||
and try again.</p></body></html>").arg(file.fileName()));
|
||||
msgBox.exec();
|
||||
return qApp->quit();
|
||||
}
|
||||
|
||||
file.close();
|
||||
}
|
||||
|
||||
// Construct the arguments to run the importer
|
||||
QStringList arguments;
|
||||
|
||||
// Import plugin selection?
|
||||
if (field(QLatin1String("installation.new")).toBool() == true
|
||||
|| field(QLatin1String("installation.import-addons")).toBool() == true)
|
||||
arguments.append(QLatin1String("--game-files"));
|
||||
|
||||
arguments.append(QLatin1String("--encoding"));
|
||||
|
||||
// Set encoding
|
||||
QString language(field(QLatin1String("installation.language")).toString());
|
||||
|
||||
if (language == QLatin1String("Polish")) {
|
||||
arguments.append(QLatin1String("win1250"));
|
||||
} else if (language == QLatin1String("Russian")) {
|
||||
arguments.append(QLatin1String("win1251"));
|
||||
} else {
|
||||
arguments.append(QLatin1String("win1252"));
|
||||
}
|
||||
|
||||
// Now the paths
|
||||
arguments.append(QLatin1String("--ini"));
|
||||
|
||||
if (field(QLatin1String("installation.new")).toBool() == true) {
|
||||
arguments.append(path + QDir::separator() + QLatin1String("Morrowind.ini"));
|
||||
} else {
|
||||
arguments.append(mInstallations[path].iniPath);
|
||||
}
|
||||
|
||||
arguments.append(QLatin1String("--cfg"));
|
||||
arguments.append(userPath + QLatin1String("openmw.cfg"));
|
||||
|
||||
if (!mImporterInvoker->startProcess(QLatin1String("mwiniimport"), arguments, false))
|
||||
return qApp->quit();
|
||||
}
|
||||
|
||||
void Wizard::MainWizard::addInstallation(const QString &path)
|
||||
{
|
||||
qDebug() << "add installation in: " << path;
|
||||
Installation install;// = new Installation();
|
||||
|
||||
install.hasMorrowind = findFiles(QLatin1String("Morrowind"), path);
|
||||
install.hasTribunal = findFiles(QLatin1String("Tribunal"), path);
|
||||
install.hasBloodmoon = findFiles(QLatin1String("Bloodmoon"), path);
|
||||
|
||||
// Try to autodetect the Morrowind.ini location
|
||||
QDir dir(path);
|
||||
QFile file(dir.filePath("Morrowind.ini"));
|
||||
|
||||
// Try the parent directory
|
||||
// In normal Morrowind installations that's where Morrowind.ini is
|
||||
if (!file.exists()) {
|
||||
dir.cdUp();
|
||||
file.setFileName(dir.filePath(QLatin1String("Morrowind.ini")));
|
||||
}
|
||||
|
||||
if (file.exists())
|
||||
install.iniPath = file.fileName();
|
||||
|
||||
mInstallations.insert(QDir::toNativeSeparators(path), install);
|
||||
|
||||
// Add it to the openmw.cfg too
|
||||
if (!mGameSettings.getDataDirs().contains(path)) {
|
||||
mGameSettings.setMultiValue(QLatin1String("data"), path);
|
||||
mGameSettings.addDataDir(path);
|
||||
}
|
||||
}
|
||||
|
||||
void Wizard::MainWizard::setupPages()
|
||||
{
|
||||
setPage(Page_Intro, new IntroPage(this));
|
||||
setPage(Page_MethodSelection, new MethodSelectionPage(this));
|
||||
setPage(Page_LanguageSelection, new LanguageSelectionPage(this));
|
||||
setPage(Page_ExistingInstallation, new ExistingInstallationPage(this));
|
||||
setPage(Page_InstallationTarget, new InstallationTargetPage(this, mCfgMgr));
|
||||
setPage(Page_ComponentSelection, new ComponentSelectionPage(this));
|
||||
setPage(Page_Installation, new InstallationPage(this));
|
||||
setPage(Page_Import, new ImportPage(this));
|
||||
setPage(Page_Conclusion, new ConclusionPage(this));
|
||||
setStartId(Page_Intro);
|
||||
|
||||
}
|
||||
|
||||
void Wizard::MainWizard::importerStarted()
|
||||
{
|
||||
}
|
||||
|
||||
void Wizard::MainWizard::importerFinished(int exitCode, QProcess::ExitStatus exitStatus)
|
||||
{
|
||||
if (exitCode != 0 || exitStatus == QProcess::CrashExit)
|
||||
return;
|
||||
|
||||
// Re-read the settings
|
||||
setupGameSettings();
|
||||
}
|
||||
|
||||
void Wizard::MainWizard::accept()
|
||||
{
|
||||
writeSettings();
|
||||
QWizard::accept();
|
||||
}
|
||||
|
||||
void Wizard::MainWizard::reject()
|
||||
{
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("Quit Wizard"));
|
||||
msgBox.setIcon(QMessageBox::Question);
|
||||
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
|
||||
msgBox.setText(tr("Are you sure you want to exit the Wizard?"));
|
||||
|
||||
if (msgBox.exec() == QMessageBox::Yes) {
|
||||
QWizard::reject();
|
||||
}
|
||||
}
|
||||
|
||||
void Wizard::MainWizard::writeSettings()
|
||||
{
|
||||
// Write the encoding and language settings
|
||||
QString language(field(QLatin1String("installation.language")).toString());
|
||||
mLauncherSettings.setValue(QLatin1String("Settings/language"), language);
|
||||
|
||||
if (language == QLatin1String("Polish")) {
|
||||
mGameSettings.setValue(QLatin1String("encoding"), QLatin1String("win1250"));
|
||||
} else if (language == QLatin1String("Russian")) {
|
||||
mGameSettings.setValue(QLatin1String("encoding"), QLatin1String("win1251"));
|
||||
} else {
|
||||
mGameSettings.setValue(QLatin1String("encoding"), QLatin1String("win1252"));
|
||||
}
|
||||
|
||||
// Write the installation path so that openmw can find them
|
||||
QString path(field(QLatin1String("installation.path")).toString());
|
||||
|
||||
// Make sure the installation path is the last data= entry
|
||||
mGameSettings.removeDataDir(path);
|
||||
mGameSettings.addDataDir(path);
|
||||
|
||||
QString userPath(QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str()));
|
||||
QDir dir(userPath);
|
||||
|
||||
if (!dir.exists()) {
|
||||
if (!dir.mkpath(userPath)) {
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("Error creating OpenMW configuration directory"));
|
||||
msgBox.setIcon(QMessageBox::Critical);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setText(tr("<html><head/><body><p><b>Could not create %1</b></p> \
|
||||
<p>Please make sure you have the right permissions \
|
||||
and try again.</p></body></html>").arg(userPath));
|
||||
msgBox.exec();
|
||||
return qApp->quit();
|
||||
}
|
||||
}
|
||||
|
||||
// Game settings
|
||||
QFile file(userPath + QLatin1String("openmw.cfg"));
|
||||
|
||||
if (!file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate)) {
|
||||
// File cannot be opened or created
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("Error writing OpenMW configuration file"));
|
||||
msgBox.setIcon(QMessageBox::Critical);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setText(tr("<html><head/><body><p><b>Could not open %1 for writing</b></p> \
|
||||
<p>Please make sure you have the right permissions \
|
||||
and try again.</p></body></html>").arg(file.fileName()));
|
||||
msgBox.exec();
|
||||
return qApp->quit();
|
||||
}
|
||||
|
||||
QTextStream stream(&file);
|
||||
stream.setCodec(QTextCodec::codecForName("UTF-8"));
|
||||
|
||||
mGameSettings.writeFile(stream);
|
||||
file.close();
|
||||
|
||||
// Launcher settings
|
||||
file.setFileName(userPath + QLatin1String("launcher.cfg"));
|
||||
|
||||
if (!file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate)) {
|
||||
// File cannot be opened or created
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("Error writing OpenMW configuration file"));
|
||||
msgBox.setIcon(QMessageBox::Critical);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setText(tr("<html><head/><body><p><b>Could not open %1 for writing</b></p> \
|
||||
<p>Please make sure you have the right permissions \
|
||||
and try again.</p></body></html>").arg(file.fileName()));
|
||||
msgBox.exec();
|
||||
return qApp->quit();
|
||||
}
|
||||
|
||||
stream.setDevice(&file);
|
||||
stream.setCodec(QTextCodec::codecForName("UTF-8"));
|
||||
|
||||
mLauncherSettings.writeFile(stream);
|
||||
file.close();
|
||||
}
|
||||
|
||||
bool Wizard::MainWizard::findFiles(const QString &name, const QString &path)
|
||||
{
|
||||
QDir dir(path);
|
||||
|
||||
if (!dir.exists())
|
||||
return false;
|
||||
|
||||
// TODO: add MIME handling to make sure the files are real
|
||||
return (dir.entryList().contains(name + QLatin1String(".esm"), Qt::CaseInsensitive)
|
||||
&& dir.entryList().contains(name + QLatin1String(".bsa"), Qt::CaseInsensitive));
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
#ifndef MAINWIZARD_HPP
|
||||
#define MAINWIZARD_HPP
|
||||
|
||||
#include <QProcess>
|
||||
#include <QWizard>
|
||||
#include <QMap>
|
||||
|
||||
#include <components/process/processinvoker.hpp>
|
||||
|
||||
#ifndef Q_MOC_RUN
|
||||
#include <components/files/configurationmanager.hpp>
|
||||
#endif
|
||||
#include <components/config/gamesettings.hpp>
|
||||
#include <components/config/launchersettings.hpp>
|
||||
|
||||
namespace Wizard
|
||||
{
|
||||
class MainWizard : public QWizard
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
struct Installation {
|
||||
bool hasMorrowind;
|
||||
bool hasTribunal;
|
||||
bool hasBloodmoon;
|
||||
|
||||
QString iniPath;
|
||||
};
|
||||
|
||||
enum {
|
||||
Page_Intro,
|
||||
Page_MethodSelection,
|
||||
Page_LanguageSelection,
|
||||
Page_ExistingInstallation,
|
||||
Page_InstallationTarget,
|
||||
Page_ComponentSelection,
|
||||
Page_Installation,
|
||||
Page_Import,
|
||||
Page_Conclusion
|
||||
};
|
||||
|
||||
MainWizard(QWidget *parent = 0);
|
||||
~MainWizard();
|
||||
|
||||
bool findFiles(const QString &name, const QString &path);
|
||||
void addInstallation(const QString &path);
|
||||
void runSettingsImporter();
|
||||
|
||||
QMap<QString, Installation> mInstallations;
|
||||
|
||||
Files::ConfigurationManager mCfgMgr;
|
||||
|
||||
Process::ProcessInvoker *mImporterInvoker;
|
||||
|
||||
bool mError;
|
||||
|
||||
public slots:
|
||||
void addLogText(const QString &text);
|
||||
|
||||
private:
|
||||
|
||||
void setupLog();
|
||||
void setupGameSettings();
|
||||
void setupLauncherSettings();
|
||||
void setupInstallations();
|
||||
void setupPages();
|
||||
|
||||
void writeSettings();
|
||||
|
||||
Config::GameSettings mGameSettings;
|
||||
Config::LauncherSettings mLauncherSettings;
|
||||
|
||||
QString mLogError;
|
||||
|
||||
private slots:
|
||||
|
||||
void importerStarted();
|
||||
void importerFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
||||
|
||||
void accept();
|
||||
void reject();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // MAINWIZARD_HPP
|
@ -0,0 +1,22 @@
|
||||
#include "methodselectionpage.hpp"
|
||||
#include <QDebug>
|
||||
#include "mainwizard.hpp"
|
||||
|
||||
Wizard::MethodSelectionPage::MethodSelectionPage(QWidget *parent) :
|
||||
QWizardPage(parent)
|
||||
{
|
||||
mWizard = qobject_cast<MainWizard*>(parent);
|
||||
|
||||
setupUi(this);
|
||||
|
||||
registerField(QLatin1String("installation.new"), newLocationRadioButton);
|
||||
}
|
||||
|
||||
int Wizard::MethodSelectionPage::nextId() const
|
||||
{
|
||||
if (field(QLatin1String("installation.new")).toBool() == true) {
|
||||
return MainWizard::Page_InstallationTarget;
|
||||
} else {
|
||||
return MainWizard::Page_ExistingInstallation;
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
#ifndef METHODSELECTIONPAGE_HPP
|
||||
#define METHODSELECTIONPAGE_HPP
|
||||
|
||||
#include <QWizardPage>
|
||||
|
||||
#include "ui_methodselectionpage.h"
|
||||
|
||||
namespace Wizard
|
||||
{
|
||||
class MainWizard;
|
||||
|
||||
class MethodSelectionPage : public QWizardPage, private Ui::MethodSelectionPage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
MethodSelectionPage(QWidget *parent);
|
||||
|
||||
int nextId() const;
|
||||
|
||||
private:
|
||||
MainWizard *mWizard;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // METHODSELECTIONPAGE_HPP
|
@ -0,0 +1,923 @@
|
||||
#include "unshieldworker.hpp"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include <QReadLocker>
|
||||
#include <QWriteLocker>
|
||||
#include <QFileDialog>
|
||||
#include <QFileInfo>
|
||||
#include <QFileInfoListIterator>
|
||||
#include <QStringList>
|
||||
#include <QTextStream>
|
||||
#include <QTextCodec>
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
#include <QDirIterator>
|
||||
|
||||
Wizard::UnshieldWorker::UnshieldWorker(QObject *parent) :
|
||||
QObject(parent),
|
||||
mIniSettings()
|
||||
{
|
||||
unshield_set_log_level(0);
|
||||
|
||||
mPath = QString();
|
||||
mIniPath = QString();
|
||||
mDiskPath = QString();
|
||||
|
||||
// Default to Latin encoding
|
||||
mIniCodec = QTextCodec::codecForName("windows-1252");
|
||||
|
||||
mInstallMorrowind = false;
|
||||
mInstallTribunal = false;
|
||||
mInstallBloodmoon = false;
|
||||
|
||||
mMorrowindDone = false;
|
||||
mTribunalDone = false;
|
||||
mBloodmoonDone = false;
|
||||
|
||||
mStopped = false;
|
||||
|
||||
qRegisterMetaType<Wizard::Component>("Wizard::Component");
|
||||
}
|
||||
|
||||
Wizard::UnshieldWorker::~UnshieldWorker()
|
||||
{
|
||||
}
|
||||
|
||||
void Wizard::UnshieldWorker::stopWorker()
|
||||
{
|
||||
mMutex.lock();
|
||||
mStopped = true;
|
||||
mMutex.unlock();
|
||||
}
|
||||
|
||||
void Wizard::UnshieldWorker::setInstallComponent(Wizard::Component component, bool install)
|
||||
{
|
||||
QWriteLocker writeLock(&mLock);
|
||||
switch (component) {
|
||||
|
||||
case Wizard::Component_Morrowind:
|
||||
mInstallMorrowind = install;
|
||||
break;
|
||||
case Wizard::Component_Tribunal:
|
||||
mInstallTribunal = install;
|
||||
break;
|
||||
case Wizard::Component_Bloodmoon:
|
||||
mInstallBloodmoon = install;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool Wizard::UnshieldWorker::getInstallComponent(Component component)
|
||||
{
|
||||
QReadLocker readLock(&mLock);
|
||||
switch (component) {
|
||||
|
||||
case Wizard::Component_Morrowind:
|
||||
return mInstallMorrowind;
|
||||
case Wizard::Component_Tribunal:
|
||||
return mInstallTribunal;
|
||||
case Wizard::Component_Bloodmoon:
|
||||
return mInstallBloodmoon;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Wizard::UnshieldWorker::setComponentDone(Component component, bool done)
|
||||
{
|
||||
QWriteLocker writeLock(&mLock);
|
||||
switch (component) {
|
||||
|
||||
case Wizard::Component_Morrowind:
|
||||
mMorrowindDone = done;
|
||||
break;
|
||||
case Wizard::Component_Tribunal:
|
||||
mTribunalDone = done;
|
||||
break;
|
||||
case Wizard::Component_Bloodmoon:
|
||||
mBloodmoonDone = done;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool Wizard::UnshieldWorker::getComponentDone(Component component)
|
||||
{
|
||||
QReadLocker readLock(&mLock);
|
||||
switch (component)
|
||||
{
|
||||
|
||||
case Wizard::Component_Morrowind:
|
||||
return mMorrowindDone;
|
||||
case Wizard::Component_Tribunal:
|
||||
return mTribunalDone;
|
||||
case Wizard::Component_Bloodmoon:
|
||||
return mBloodmoonDone;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Wizard::UnshieldWorker::setPath(const QString &path)
|
||||
{
|
||||
QWriteLocker writeLock(&mLock);
|
||||
mPath = path;
|
||||
}
|
||||
|
||||
void Wizard::UnshieldWorker::setIniPath(const QString &path)
|
||||
{
|
||||
QWriteLocker writeLock(&mLock);
|
||||
mIniPath = path;
|
||||
}
|
||||
|
||||
void Wizard::UnshieldWorker::setDiskPath(const QString &path)
|
||||
{
|
||||
QWriteLocker writeLock(&mLock);
|
||||
mDiskPath = path;
|
||||
mWait.wakeAll();
|
||||
}
|
||||
|
||||
QString Wizard::UnshieldWorker::getPath()
|
||||
{
|
||||
QReadLocker readLock(&mLock);
|
||||
return mPath;
|
||||
}
|
||||
|
||||
QString Wizard::UnshieldWorker::getIniPath()
|
||||
{
|
||||
QReadLocker readLock(&mLock);
|
||||
return mIniPath;
|
||||
}
|
||||
|
||||
QString Wizard::UnshieldWorker::getDiskPath()
|
||||
{
|
||||
QReadLocker readLock(&mLock);
|
||||
return mDiskPath;
|
||||
}
|
||||
|
||||
|
||||
void Wizard::UnshieldWorker::setIniCodec(QTextCodec *codec)
|
||||
{
|
||||
QWriteLocker writeLock(&mLock);
|
||||
mIniCodec = codec;
|
||||
}
|
||||
|
||||
bool Wizard::UnshieldWorker::setupSettings()
|
||||
{
|
||||
// Create Morrowind.ini settings map
|
||||
if (getIniPath().isEmpty())
|
||||
return false;
|
||||
|
||||
QFile file(getIniPath());
|
||||
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
emit error(tr("Failed to open Morrowind configuration file!"),
|
||||
tr("Opening %1 failed: %2.").arg(getIniPath(), file.errorString()));
|
||||
return false;
|
||||
}
|
||||
|
||||
QTextStream stream(&file);
|
||||
stream.setCodec(mIniCodec);
|
||||
|
||||
mIniSettings.readFile(stream);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Wizard::UnshieldWorker::writeSettings()
|
||||
{
|
||||
if (getIniPath().isEmpty())
|
||||
return false;
|
||||
|
||||
QFile file(getIniPath());
|
||||
|
||||
if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) {
|
||||
emit error(tr("Failed to open Morrowind configuration file!"),
|
||||
tr("Opening %1 failed: %2.").arg(getIniPath(), file.errorString()));
|
||||
return false;
|
||||
}
|
||||
|
||||
QTextStream stream(&file);
|
||||
stream.setCodec(mIniCodec);
|
||||
|
||||
if (!mIniSettings.writeFile(getIniPath(), stream)) {
|
||||
emit error(tr("Failed to write Morrowind configuration file!"),
|
||||
tr("Writing to %1 failed: %2.").arg(getIniPath(), file.errorString()));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Wizard::UnshieldWorker::removeDirectory(const QString &dirName)
|
||||
{
|
||||
bool result = false;
|
||||
QDir dir(dirName);
|
||||
|
||||
if (dir.exists(dirName))
|
||||
{
|
||||
QFileInfoList list(dir.entryInfoList(QDir::NoDotAndDotDot |
|
||||
QDir::System | QDir::Hidden |
|
||||
QDir::AllDirs | QDir::Files, QDir::DirsFirst));
|
||||
foreach(QFileInfo info, list) {
|
||||
if (info.isDir()) {
|
||||
result = removeDirectory(info.absoluteFilePath());
|
||||
} else {
|
||||
result = QFile::remove(info.absoluteFilePath());
|
||||
}
|
||||
|
||||
if (!result)
|
||||
return result;
|
||||
}
|
||||
|
||||
result = dir.rmdir(dirName);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Wizard::UnshieldWorker::copyFile(const QString &source, const QString &destination, bool keepSource)
|
||||
{
|
||||
QDir dir;
|
||||
QFile file;
|
||||
|
||||
QFileInfo info(destination);
|
||||
|
||||
if (info.exists()) {
|
||||
if (!dir.remove(info.absoluteFilePath()))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file.copy(source, destination)) {
|
||||
if (!keepSource) {
|
||||
if (!file.remove(source))
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Wizard::UnshieldWorker::copyDirectory(const QString &source, const QString &destination, bool keepSource)
|
||||
{
|
||||
QDir sourceDir(source);
|
||||
QDir destDir(destination);
|
||||
bool result = true;
|
||||
|
||||
if (!destDir.exists()) {
|
||||
if (!sourceDir.mkpath(destination))
|
||||
return false;
|
||||
}
|
||||
|
||||
destDir.refresh();
|
||||
|
||||
if (!destDir.exists())
|
||||
return false;
|
||||
|
||||
QFileInfoList list(sourceDir.entryInfoList(QDir::NoDotAndDotDot |
|
||||
QDir::System | QDir::Hidden |
|
||||
QDir::AllDirs | QDir::Files, QDir::DirsFirst));
|
||||
|
||||
foreach (const QFileInfo &info, list) {
|
||||
QString relativePath(info.absoluteFilePath());
|
||||
relativePath.remove(source);
|
||||
|
||||
QString destinationPath(destDir.absolutePath() + relativePath);
|
||||
|
||||
if (info.isSymLink())
|
||||
continue;
|
||||
|
||||
if (info.isDir()) {
|
||||
result = copyDirectory(info.absoluteFilePath(), destinationPath);
|
||||
} else {
|
||||
result = copyFile(info.absoluteFilePath(), destinationPath);
|
||||
}
|
||||
}
|
||||
|
||||
if (!keepSource)
|
||||
return result && removeDirectory(sourceDir.absolutePath());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Wizard::UnshieldWorker::installFile(const QString &fileName, const QString &path, Qt::MatchFlags flags, bool keepSource)
|
||||
{
|
||||
return installFiles(fileName, path, flags, true, keepSource);
|
||||
}
|
||||
|
||||
bool Wizard::UnshieldWorker::installFiles(const QString &fileName, const QString &path, Qt::MatchFlags flags, bool keepSource, bool single)
|
||||
{
|
||||
QDir dir(path);
|
||||
|
||||
if (!dir.exists())
|
||||
return false;
|
||||
|
||||
QStringList files(findFiles(fileName, path, flags));
|
||||
|
||||
foreach (const QString &file, files) {
|
||||
QFileInfo info(file);
|
||||
emit textChanged(tr("Installing: %1").arg(info.fileName()));
|
||||
|
||||
if (single) {
|
||||
return copyFile(info.absoluteFilePath(), getPath() + QDir::separator() + info.fileName(), keepSource);
|
||||
} else {
|
||||
if (!copyFile(info.absoluteFilePath(), getPath() + QDir::separator() + info.fileName(), keepSource))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Wizard::UnshieldWorker::installDirectories(const QString &dirName, const QString &path, bool recursive, bool keepSource)
|
||||
{
|
||||
QDir dir(path);
|
||||
|
||||
if (!dir.exists())
|
||||
return false;
|
||||
|
||||
QStringList directories(findDirectories(dirName, path, recursive));
|
||||
|
||||
foreach (const QString &dir, directories) {
|
||||
QFileInfo info(dir);
|
||||
emit textChanged(tr("Installing: %1 directory").arg(info.fileName()));
|
||||
if (!copyDirectory(info.absoluteFilePath(), getPath() + QDir::separator() + info.fileName(), keepSource))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Wizard::UnshieldWorker::extract()
|
||||
{
|
||||
if (getInstallComponent(Wizard::Component_Morrowind))
|
||||
{
|
||||
if (!getComponentDone(Wizard::Component_Morrowind))
|
||||
if (!setupComponent(Wizard::Component_Morrowind))
|
||||
return;
|
||||
}
|
||||
|
||||
if (getInstallComponent(Wizard::Component_Tribunal))
|
||||
{
|
||||
if (!getComponentDone(Wizard::Component_Tribunal))
|
||||
if (!setupComponent(Wizard::Component_Tribunal))
|
||||
return;
|
||||
}
|
||||
|
||||
if (getInstallComponent(Wizard::Component_Bloodmoon))
|
||||
{
|
||||
if (!getComponentDone(Wizard::Component_Bloodmoon))
|
||||
if (!setupComponent(Wizard::Component_Bloodmoon))
|
||||
return;
|
||||
}
|
||||
|
||||
// Update Morrowind configuration
|
||||
if (getInstallComponent(Wizard::Component_Tribunal))
|
||||
{
|
||||
mIniSettings.setValue(QLatin1String("Archives/Archive 0"), QVariant(QString("Tribunal.bsa")));
|
||||
mIniSettings.setValue(QLatin1String("Game Files/GameFile1"), QVariant(QString("Tribunal.esm")));
|
||||
}
|
||||
|
||||
if (getInstallComponent(Wizard::Component_Bloodmoon))
|
||||
{
|
||||
mIniSettings.setValue(QLatin1String("Archives/Archive 0"), QVariant(QString("Bloodmoon.bsa")));
|
||||
mIniSettings.setValue(QLatin1String("Game Files/GameFile1"), QVariant(QString("Bloodmoon.esm")));
|
||||
}
|
||||
|
||||
if (getInstallComponent(Wizard::Component_Tribunal) &&
|
||||
getInstallComponent(Wizard::Component_Bloodmoon))
|
||||
{
|
||||
mIniSettings.setValue(QLatin1String("Archives/Archive 0"), QVariant(QString("Tribunal.bsa")));
|
||||
mIniSettings.setValue(QLatin1String("Archives/Archive 1"), QVariant(QString("Bloodmoon.bsa")));
|
||||
mIniSettings.setValue(QLatin1String("Game Files/GameFile1"), QVariant(QString("Tribunal.esm")));
|
||||
mIniSettings.setValue(QLatin1String("Game Files/GameFile2"), QVariant(QString("Bloodmoon.esm")));
|
||||
}
|
||||
|
||||
// Write the settings to the Morrowind config file
|
||||
if (!writeSettings())
|
||||
return;
|
||||
|
||||
// Remove the temporary directory
|
||||
removeDirectory(getPath() + QDir::separator() + QLatin1String("extract-temp"));
|
||||
|
||||
// Fill the progress bar
|
||||
int total = 0;
|
||||
|
||||
if (getInstallComponent(Wizard::Component_Morrowind))
|
||||
total = 100;
|
||||
|
||||
if (getInstallComponent(Wizard::Component_Tribunal))
|
||||
total = total + 100;
|
||||
|
||||
if (getInstallComponent(Wizard::Component_Bloodmoon))
|
||||
total = total + 100;
|
||||
|
||||
emit textChanged(tr("Installation finished!"));
|
||||
emit progressChanged(total);
|
||||
emit finished();
|
||||
}
|
||||
|
||||
bool Wizard::UnshieldWorker::setupComponent(Component component)
|
||||
{
|
||||
QString name;
|
||||
switch (component) {
|
||||
|
||||
case Wizard::Component_Morrowind:
|
||||
name = QLatin1String("Morrowind");
|
||||
break;
|
||||
case Wizard::Component_Tribunal:
|
||||
name = QLatin1String("Tribunal");
|
||||
break;
|
||||
case Wizard::Component_Bloodmoon:
|
||||
name = QLatin1String("Bloodmoon");
|
||||
break;
|
||||
}
|
||||
|
||||
if (name.isEmpty()) {
|
||||
emit error(tr("Component parameter is invalid!"), tr("An invalid component parameter was supplied."));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
QString cabFile;
|
||||
QDir disk;
|
||||
|
||||
// Keep showing the file dialog until we find the necessary install files
|
||||
while (!found) {
|
||||
if (getDiskPath().isEmpty()) {
|
||||
QReadLocker readLock(&mLock);
|
||||
emit requestFileDialog(component);
|
||||
mWait.wait(&mLock);
|
||||
disk.setPath(getDiskPath());
|
||||
} else {
|
||||
disk.setPath(getDiskPath());
|
||||
}
|
||||
|
||||
QStringList list(findFiles(QLatin1String("data1.hdr"), disk.absolutePath()));
|
||||
|
||||
foreach (const QString &file, list) {
|
||||
|
||||
qDebug() << "current archive: " << file;
|
||||
|
||||
if (component == Wizard::Component_Morrowind)
|
||||
{
|
||||
bool morrowindFound = findInCab(QLatin1String("Morrowind.bsa"), file);
|
||||
bool tribunalFound = findInCab(QLatin1String("Tribunal.bsa"), file);
|
||||
bool bloodmoonFound = findInCab(QLatin1String("Bloodmoon.bsa"), file);
|
||||
|
||||
if (morrowindFound) {
|
||||
// Check if we have correct archive, other archives have Morrowind.bsa too
|
||||
if ((tribunalFound && bloodmoonFound)
|
||||
|| (!tribunalFound && !bloodmoonFound)) {
|
||||
cabFile = file;
|
||||
found = true; // We have a GoTY disk or a Morrowind-only disk
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
if (findInCab(name + QLatin1String(".bsa"), file)) {
|
||||
cabFile = file;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
QReadLocker readLock(&mLock);
|
||||
emit requestFileDialog(component);
|
||||
mWait.wait(&mLock);
|
||||
}
|
||||
}
|
||||
|
||||
if (installComponent(component, cabFile)) {
|
||||
setComponentDone(component, true);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Wizard::UnshieldWorker::installComponent(Component component, const QString &path)
|
||||
{
|
||||
QString name;
|
||||
switch (component) {
|
||||
|
||||
case Wizard::Component_Morrowind:
|
||||
name = QLatin1String("Morrowind");
|
||||
break;
|
||||
case Wizard::Component_Tribunal:
|
||||
name = QLatin1String("Tribunal");
|
||||
break;
|
||||
case Wizard::Component_Bloodmoon:
|
||||
name = QLatin1String("Bloodmoon");
|
||||
break;
|
||||
}
|
||||
|
||||
if (name.isEmpty()) {
|
||||
emit error(tr("Component parameter is invalid!"), tr("An invalid component parameter was supplied."));
|
||||
return false;
|
||||
}
|
||||
|
||||
emit textChanged(tr("Installing %1").arg(name));
|
||||
|
||||
QFileInfo info(path);
|
||||
|
||||
if (!info.exists()) {
|
||||
emit error(tr("Installation media path not set!"), tr("The source path for %1 was not set.").arg(name));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create temporary extract directory
|
||||
// TODO: Use QTemporaryDir in Qt 5.0
|
||||
QString tempPath(getPath() + QDir::separator() + QLatin1String("extract-temp"));
|
||||
QDir temp;
|
||||
|
||||
// Make sure the temporary folder is empty
|
||||
removeDirectory(tempPath);
|
||||
|
||||
if (!temp.mkpath(tempPath)) {
|
||||
emit error(tr("Cannot create temporary directory!"), tr("Failed to create %1.").arg(tempPath));
|
||||
return false;
|
||||
}
|
||||
|
||||
temp.setPath(tempPath);
|
||||
|
||||
if (!temp.mkdir(name)) {
|
||||
emit error(tr("Cannot create temporary directory!"), tr("Failed to create %1.").arg(temp.absoluteFilePath(name)));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!temp.cd(name)) {
|
||||
emit error(tr("Cannot move into temporary directory!"), tr("Failed to move into %1.").arg(temp.absoluteFilePath(name)));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Extract the installation files
|
||||
if (!extractCab(info.absoluteFilePath(), temp.absolutePath()))
|
||||
return false;
|
||||
|
||||
// Move the files from the temporary path to the destination folder
|
||||
emit textChanged(tr("Moving installation files"));
|
||||
|
||||
// Install extracted directories
|
||||
QStringList directories;
|
||||
directories << QLatin1String("BookArt")
|
||||
<< QLatin1String("Fonts")
|
||||
<< QLatin1String("Icons")
|
||||
<< QLatin1String("Meshes")
|
||||
<< QLatin1String("Music")
|
||||
<< QLatin1String("Sound")
|
||||
<< QLatin1String("Splash")
|
||||
<< QLatin1String("Textures")
|
||||
<< QLatin1String("Video");
|
||||
|
||||
foreach (const QString &dir, directories) {
|
||||
if (!installDirectories(dir, temp.absolutePath())) {
|
||||
emit error(tr("Could not install directory!"),
|
||||
tr("Installing %1 to %2 failed.").arg(dir, temp.absolutePath()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Install directories from disk
|
||||
foreach (const QString &dir, directories) {
|
||||
if (!installDirectories(dir, info.absolutePath(), false, true)) {
|
||||
emit error(tr("Could not install directory!"),
|
||||
tr("Installing %1 to %2 failed.").arg(dir, info.absolutePath()));
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Install translation files
|
||||
QStringList extensions;
|
||||
extensions << QLatin1String(".cel")
|
||||
<< QLatin1String(".top")
|
||||
<< QLatin1String(".mrk");
|
||||
|
||||
foreach (const QString &extension, extensions) {
|
||||
if (!installFiles(extension, info.absolutePath(), Qt::MatchEndsWith)) {
|
||||
emit error(tr("Could not install translation file!"),
|
||||
tr("Failed to install *%1 files.").arg(extension));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (component == Wizard::Component_Morrowind)
|
||||
{
|
||||
QStringList files;
|
||||
files << QLatin1String("Morrowind.esm")
|
||||
<< QLatin1String("Morrowind.bsa");
|
||||
|
||||
foreach (const QString &file, files) {
|
||||
if (!installFile(file, temp.absolutePath())) {
|
||||
emit error(tr("Could not install Morrowind data file!"),
|
||||
tr("Failed to install %1.").arg(file));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy Morrowind configuration file
|
||||
if (!installFile(QLatin1String("Morrowind.ini"), temp.absolutePath())) {
|
||||
emit error(tr("Could not install Morrowind configuration file!"),
|
||||
tr("Failed to install %1.").arg(QLatin1String("Morrowind.ini")));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Setup Morrowind configuration
|
||||
setIniPath(getPath() + QDir::separator() + QLatin1String("Morrowind.ini"));
|
||||
|
||||
if (!setupSettings())
|
||||
return false;
|
||||
}
|
||||
|
||||
if (component == Wizard::Component_Tribunal)
|
||||
{
|
||||
QFileInfo sounds(temp.absoluteFilePath(QLatin1String("Sounds")));
|
||||
QString dest(getPath() + QDir::separator() + QLatin1String("Sound"));
|
||||
|
||||
if (sounds.exists()) {
|
||||
emit textChanged(tr("Installing: Sound directory"));
|
||||
if (!copyDirectory(sounds.absoluteFilePath(), dest)) {
|
||||
emit error(tr("Could not install directory!"),
|
||||
tr("Installing %1 to %2 failed.").arg(sounds.absoluteFilePath(), dest));
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
QStringList files;
|
||||
files << QLatin1String("Tribunal.esm")
|
||||
<< QLatin1String("Tribunal.bsa");
|
||||
|
||||
foreach (const QString &file, files) {
|
||||
if (!installFile(file, temp.absolutePath())) {
|
||||
emit error(tr("Could not find Tribunal data file!"),
|
||||
tr("Failed to find %1.").arg(file));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (component == Wizard::Component_Bloodmoon)
|
||||
{
|
||||
QFileInfo original(getPath() + QDir::separator() + QLatin1String("Tribunal.esm"));
|
||||
|
||||
if (original.exists()) {
|
||||
if (!installFile(QLatin1String("Tribunal.esm"), temp.absolutePath())) {
|
||||
emit error(tr("Could not find Tribunal patch file!"),
|
||||
tr("Failed to find %1.").arg(QLatin1String("Tribunal.esm")));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
QStringList files;
|
||||
files << QLatin1String("Bloodmoon.esm")
|
||||
<< QLatin1String("Bloodmoon.bsa");
|
||||
|
||||
foreach (const QString &file, files) {
|
||||
if (!installFile(file, temp.absolutePath())) {
|
||||
emit error(tr("Could not find Bloodmoon data file!"),
|
||||
tr("Failed to find %1.").arg(file));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Load Morrowind configuration settings from the setup script
|
||||
QStringList list(findFiles(QLatin1String("setup.inx"), getDiskPath()));
|
||||
|
||||
emit textChanged(tr("Updating Morrowind configuration file"));
|
||||
|
||||
foreach (const QString &inx, list) {
|
||||
mIniSettings.parseInx(inx);
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, install Data Files directories from temp and disk
|
||||
QStringList datafiles(findDirectories(QLatin1String("Data Files"), temp.absolutePath()));
|
||||
datafiles.append(findDirectories(QLatin1String("Data Files"), info.absolutePath()));
|
||||
|
||||
foreach (const QString &dir, datafiles) {
|
||||
QFileInfo info(dir);
|
||||
emit textChanged(tr("Installing: %1 directory").arg(info.fileName()));
|
||||
|
||||
if (!copyDirectory(info.absoluteFilePath(), getPath())) {
|
||||
emit error(tr("Could not install directory!"),
|
||||
tr("Installing %1 to %2 failed.").arg(info.absoluteFilePath(), getPath()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
emit textChanged(tr("%1 installation finished!").arg(name));
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
bool Wizard::UnshieldWorker::extractFile(Unshield *unshield, const QString &destination, const QString &prefix, int index, int counter)
|
||||
{
|
||||
bool success = false;
|
||||
QString path(destination);
|
||||
path.append(QDir::separator());
|
||||
|
||||
int directory = unshield_file_directory(unshield, index);
|
||||
|
||||
if (!prefix.isEmpty())
|
||||
path.append(prefix + QDir::separator());
|
||||
|
||||
if (directory >= 0)
|
||||
path.append(QString::fromUtf8(unshield_directory_name(unshield, directory)) + QDir::separator());
|
||||
|
||||
// Ensure the path has the right separators
|
||||
path.replace(QLatin1Char('\\'), QDir::separator());
|
||||
path = QDir::toNativeSeparators(path);
|
||||
|
||||
// Ensure the target path exists
|
||||
QDir dir;
|
||||
dir.mkpath(path);
|
||||
|
||||
QString fileName(path);
|
||||
fileName.append(QString::fromUtf8(unshield_file_name(unshield, index)));
|
||||
|
||||
// Calculate the percentage done
|
||||
int progress = (((float) counter / (float) unshield_file_count(unshield)) * 100);
|
||||
|
||||
if (getComponentDone(Wizard::Component_Morrowind))
|
||||
progress = progress + 100;
|
||||
|
||||
if (getComponentDone(Wizard::Component_Tribunal))
|
||||
progress = progress + 100;
|
||||
|
||||
emit textChanged(tr("Extracting: %1").arg(QString::fromUtf8(unshield_file_name(unshield, index))));
|
||||
emit progressChanged(progress);
|
||||
|
||||
QByteArray array(fileName.toUtf8());
|
||||
success = unshield_file_save(unshield, index, array.constData());
|
||||
|
||||
if (!success) {
|
||||
qDebug() << "error";
|
||||
dir.remove(fileName);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool Wizard::UnshieldWorker::extractCab(const QString &cabFile, const QString &destination)
|
||||
{
|
||||
bool success = false;
|
||||
|
||||
QByteArray array(cabFile.toUtf8());
|
||||
|
||||
Unshield *unshield;
|
||||
unshield = unshield_open(array.constData());
|
||||
|
||||
if (!unshield) {
|
||||
emit error(tr("Failed to open InstallShield Cabinet File."), tr("Opening %1 failed.").arg(cabFile));
|
||||
unshield_close(unshield);
|
||||
return false;
|
||||
}
|
||||
|
||||
int counter = 0;
|
||||
|
||||
for (int i=0; i<unshield_file_group_count(unshield); ++i)
|
||||
{
|
||||
UnshieldFileGroup *group = unshield_file_group_get(unshield, i);
|
||||
|
||||
for (size_t j=group->first_file; j<=group->last_file; ++j)
|
||||
{
|
||||
if (mStopped) {
|
||||
qDebug() << "We're asked to stop!";
|
||||
|
||||
unshield_close(unshield);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (unshield_file_is_valid(unshield, j)) {
|
||||
success = extractFile(unshield, destination, group->name, j, counter);
|
||||
|
||||
if (!success) {
|
||||
QString name(QString::fromUtf8(unshield_file_name(unshield, j)));
|
||||
|
||||
emit error(tr("Failed to extract %1.").arg(name),
|
||||
tr("Complete path: %1").arg(destination + QDir::separator() + name));
|
||||
|
||||
unshield_close(unshield);
|
||||
return false;
|
||||
}
|
||||
|
||||
++counter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unshield_close(unshield);
|
||||
return success;
|
||||
}
|
||||
|
||||
QString Wizard::UnshieldWorker::findFile(const QString &fileName, const QString &path)
|
||||
{
|
||||
return findFiles(fileName, path).first();
|
||||
}
|
||||
|
||||
QStringList Wizard::UnshieldWorker::findFiles(const QString &fileName, const QString &path, int depth, bool recursive,
|
||||
bool directories, Qt::MatchFlags flags)
|
||||
{
|
||||
static const int MAXIMUM_DEPTH = 10;
|
||||
|
||||
if (depth >= MAXIMUM_DEPTH) {
|
||||
qWarning("Maximum directory depth limit reached.");
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
QStringList result;
|
||||
QDir dir(path);
|
||||
|
||||
// Prevent parsing over the complete filesystem
|
||||
if (dir == QDir::rootPath())
|
||||
return QStringList();
|
||||
|
||||
if (!dir.exists())
|
||||
return QStringList();
|
||||
|
||||
QFileInfoList list(dir.entryInfoList(QDir::NoDotAndDotDot |
|
||||
QDir::AllDirs | QDir::Files, QDir::DirsFirst));
|
||||
foreach(QFileInfo info, list) {
|
||||
if (info.isSymLink())
|
||||
continue;
|
||||
|
||||
if (info.isDir()) {
|
||||
if (directories)
|
||||
{
|
||||
if (info.fileName() == fileName) {
|
||||
result.append(info.absoluteFilePath());
|
||||
} else {
|
||||
if (recursive)
|
||||
result.append(findFiles(fileName, info.absoluteFilePath(), depth + 1, recursive, true));
|
||||
}
|
||||
} else {
|
||||
if (recursive)
|
||||
result.append(findFiles(fileName, info.absoluteFilePath(), depth + 1));
|
||||
}
|
||||
} else {
|
||||
if (directories)
|
||||
break;
|
||||
|
||||
switch (flags) {
|
||||
case Qt::MatchExactly:
|
||||
if (info.fileName() == fileName)
|
||||
result.append(info.absoluteFilePath());
|
||||
break;
|
||||
case Qt::MatchEndsWith:
|
||||
if (info.fileName().endsWith(fileName))
|
||||
result.append(info.absoluteFilePath());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QStringList Wizard::UnshieldWorker::findDirectories(const QString &dirName, const QString &path, bool recursive)
|
||||
{
|
||||
return findFiles(dirName, path, 0, true, true);
|
||||
}
|
||||
|
||||
bool Wizard::UnshieldWorker::findInCab(const QString &fileName, const QString &cabFile)
|
||||
{
|
||||
QByteArray array(cabFile.toUtf8());
|
||||
|
||||
Unshield *unshield;
|
||||
unshield = unshield_open(array.constData());
|
||||
|
||||
if (!unshield) {
|
||||
emit error(tr("Failed to open InstallShield Cabinet File."), tr("Opening %1 failed.").arg(cabFile));
|
||||
unshield_close(unshield);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i=0; i<unshield_file_group_count(unshield); ++i)
|
||||
{
|
||||
UnshieldFileGroup *group = unshield_file_group_get(unshield, i);
|
||||
|
||||
for (size_t j=group->first_file; j<=group->last_file; ++j)
|
||||
{
|
||||
|
||||
if (unshield_file_is_valid(unshield, j)) {
|
||||
QString current(QString::fromUtf8(unshield_file_name(unshield, j)));
|
||||
if (current.toLower() == fileName.toLower()) {
|
||||
unshield_close(unshield);
|
||||
return true; // File is found!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unshield_close(unshield);
|
||||
return false;
|
||||
}
|
@ -0,0 +1,127 @@
|
||||
#ifndef UNSHIELDWORKER_HPP
|
||||
#define UNSHIELDWORKER_HPP
|
||||
|
||||
#include <QObject>
|
||||
#include <QThread>
|
||||
#include <QMutex>
|
||||
#include <QWaitCondition>
|
||||
#include <QReadWriteLock>
|
||||
#include <QStringList>
|
||||
|
||||
#include <libunshield.h>
|
||||
|
||||
#include "../inisettings.hpp"
|
||||
|
||||
|
||||
namespace Wizard
|
||||
{
|
||||
enum Component {
|
||||
Component_Morrowind,
|
||||
Component_Tribunal,
|
||||
Component_Bloodmoon
|
||||
};
|
||||
|
||||
class UnshieldWorker : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_ENUMS(Wizard::Component)
|
||||
|
||||
public:
|
||||
UnshieldWorker(QObject *parent = 0);
|
||||
~UnshieldWorker();
|
||||
|
||||
void stopWorker();
|
||||
|
||||
void setInstallComponent(Wizard::Component component, bool install);
|
||||
|
||||
void setDiskPath(const QString &path);
|
||||
|
||||
void setPath(const QString &path);
|
||||
void setIniPath(const QString &path);
|
||||
|
||||
QString getPath();
|
||||
QString getIniPath();
|
||||
|
||||
void setIniCodec(QTextCodec *codec);
|
||||
|
||||
bool setupSettings();
|
||||
|
||||
private:
|
||||
|
||||
bool writeSettings();
|
||||
|
||||
bool getInstallComponent(Component component);
|
||||
|
||||
QString getDiskPath();
|
||||
|
||||
void setComponentDone(Component component, bool done = true);
|
||||
bool getComponentDone(Component component);
|
||||
|
||||
bool removeDirectory(const QString &dirName);
|
||||
|
||||
bool copyFile(const QString &source, const QString &destination, bool keepSource = true);
|
||||
bool copyDirectory(const QString &source, const QString &destination, bool keepSource = true);
|
||||
|
||||
bool extractCab(const QString &cabFile, const QString &destination);
|
||||
bool extractFile(Unshield *unshield, const QString &destination, const QString &prefix, int index, int counter);
|
||||
|
||||
bool findInCab(const QString &fileName, const QString &cabFile);
|
||||
|
||||
QString findFile(const QString &fileName, const QString &path);
|
||||
|
||||
QStringList findFiles(const QString &fileName, const QString &path, int depth = 0, bool recursive = true,
|
||||
bool directories = false, Qt::MatchFlags flags = Qt::MatchExactly);
|
||||
|
||||
QStringList findDirectories(const QString &dirName, const QString &path, bool recursive = true);
|
||||
|
||||
bool installFile(const QString &fileName, const QString &path, Qt::MatchFlags flags = Qt::MatchExactly,
|
||||
bool keepSource = false);
|
||||
|
||||
bool installFiles(const QString &fileName, const QString &path, Qt::MatchFlags flags = Qt::MatchExactly,
|
||||
bool keepSource = false, bool single = false);
|
||||
|
||||
bool installDirectories(const QString &dirName, const QString &path,
|
||||
bool recursive = true, bool keepSource = false);
|
||||
|
||||
bool installComponent(Component component, const QString &path);
|
||||
bool setupComponent(Component component);
|
||||
|
||||
bool mInstallMorrowind;
|
||||
bool mInstallTribunal;
|
||||
bool mInstallBloodmoon;
|
||||
|
||||
bool mMorrowindDone;
|
||||
bool mTribunalDone;
|
||||
bool mBloodmoonDone;
|
||||
|
||||
bool mStopped;
|
||||
|
||||
QString mPath;
|
||||
QString mIniPath;
|
||||
QString mDiskPath;
|
||||
|
||||
IniSettings mIniSettings;
|
||||
|
||||
QTextCodec *mIniCodec;
|
||||
|
||||
QWaitCondition mWait;
|
||||
QMutex mMutex;
|
||||
|
||||
QReadWriteLock mLock;
|
||||
|
||||
public slots:
|
||||
void extract();
|
||||
|
||||
signals:
|
||||
void finished();
|
||||
void requestFileDialog(Wizard::Component component);
|
||||
|
||||
void textChanged(const QString &text);
|
||||
|
||||
void error(const QString &text, const QString &details);
|
||||
void progressChanged(int progress);
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif // UNSHIELDWORKER_HPP
|
@ -0,0 +1,48 @@
|
||||
#include "componentlistwidget.hpp"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QStringList>
|
||||
|
||||
ComponentListWidget::ComponentListWidget(QWidget *parent) :
|
||||
QListWidget(parent)
|
||||
{
|
||||
mCheckedItems = QStringList();
|
||||
|
||||
connect(this, SIGNAL(itemChanged(QListWidgetItem *)),
|
||||
this, SLOT(updateCheckedItems(QListWidgetItem *)));
|
||||
|
||||
connect(model(), SIGNAL(rowsInserted(QModelIndex, int, int)),
|
||||
this, SLOT(updateCheckedItems(QModelIndex, int, int)));
|
||||
}
|
||||
|
||||
QStringList ComponentListWidget::checkedItems()
|
||||
{
|
||||
mCheckedItems.removeDuplicates();
|
||||
return mCheckedItems;
|
||||
}
|
||||
|
||||
void ComponentListWidget::updateCheckedItems(const QModelIndex &index, int start, int end)
|
||||
{
|
||||
updateCheckedItems(item(start));
|
||||
}
|
||||
|
||||
void ComponentListWidget::updateCheckedItems(QListWidgetItem *item)
|
||||
{
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
QString text = item->text();
|
||||
|
||||
if (item->checkState() == Qt::Checked) {
|
||||
if (!mCheckedItems.contains(text))
|
||||
mCheckedItems.append(text);
|
||||
} else {
|
||||
if (mCheckedItems.contains(text))
|
||||
mCheckedItems.removeAll(text);
|
||||
}
|
||||
|
||||
mCheckedItems.removeDuplicates();
|
||||
|
||||
emit checkedItemsChanged(mCheckedItems);
|
||||
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
#ifndef COMPONENTLISTWIDGET_HPP
|
||||
#define COMPONENTLISTWIDGET_HPP
|
||||
|
||||
#include <QListWidget>
|
||||
|
||||
class ComponentListWidget : public QListWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QStringList mCheckedItems READ checkedItems)
|
||||
|
||||
public:
|
||||
ComponentListWidget(QWidget *parent = 0);
|
||||
|
||||
QStringList mCheckedItems;
|
||||
QStringList checkedItems();
|
||||
|
||||
signals:
|
||||
void checkedItemsChanged(const QStringList &items);
|
||||
|
||||
private slots:
|
||||
void updateCheckedItems(QListWidgetItem *item);
|
||||
void updateCheckedItems(const QModelIndex &index, int start, int end);
|
||||
};
|
||||
|
||||
#endif // COMPONENTLISTWIDGET_HPP
|
@ -0,0 +1,185 @@
|
||||
#include "processinvoker.hpp"
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <QStringList>
|
||||
#include <QString>
|
||||
#include <QFileInfo>
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
#include <QDebug>
|
||||
#include <QCoreApplication>
|
||||
|
||||
Process::ProcessInvoker::ProcessInvoker()
|
||||
{
|
||||
mProcess = new QProcess(this);
|
||||
|
||||
connect(mProcess, SIGNAL(error(QProcess::ProcessError)),
|
||||
this, SLOT(processError(QProcess::ProcessError)));
|
||||
|
||||
connect(mProcess, SIGNAL(finished(int,QProcess::ExitStatus)),
|
||||
this, SLOT(processFinished(int,QProcess::ExitStatus)));
|
||||
|
||||
|
||||
mName = QString();
|
||||
mArguments = QStringList();
|
||||
}
|
||||
|
||||
Process::ProcessInvoker::~ProcessInvoker()
|
||||
{
|
||||
}
|
||||
|
||||
//void Process::ProcessInvoker::setProcessName(const QString &name)
|
||||
//{
|
||||
// mName = name;
|
||||
//}
|
||||
|
||||
//void Process::ProcessInvoker::setProcessArguments(const QStringList &arguments)
|
||||
//{
|
||||
// mArguments = arguments;
|
||||
//}
|
||||
|
||||
QProcess* Process::ProcessInvoker::getProcess()
|
||||
{
|
||||
return mProcess;
|
||||
}
|
||||
|
||||
//QString Process::ProcessInvoker::getProcessName()
|
||||
//{
|
||||
// return mName;
|
||||
//}
|
||||
|
||||
//QStringList Process::ProcessInvoker::getProcessArguments()
|
||||
//{
|
||||
// return mArguments;
|
||||
//}
|
||||
|
||||
bool Process::ProcessInvoker::startProcess(const QString &name, const QStringList &arguments, bool detached)
|
||||
{
|
||||
// mProcess = new QProcess(this);
|
||||
mName = name;
|
||||
mArguments = arguments;
|
||||
|
||||
QString path(name);
|
||||
#ifdef Q_OS_WIN
|
||||
path.append(QLatin1String(".exe"));
|
||||
#elif defined(Q_OS_MAC)
|
||||
QDir dir(QCoreApplication::applicationDirPath());
|
||||
path = dir.absoluteFilePath(name);
|
||||
#else
|
||||
path.prepend(QLatin1String("./"));
|
||||
#endif
|
||||
|
||||
QFileInfo info(path);
|
||||
|
||||
if (!info.exists()) {
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("Error starting executable"));
|
||||
msgBox.setIcon(QMessageBox::Warning);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setText(tr("<html><head/><body><p><b>Could not find %1</b></p> \
|
||||
<p>The application is not found.</p> \
|
||||
<p>Please make sure OpenMW is installed correctly and try again.</p></body></html>").arg(info.fileName()));
|
||||
msgBox.exec();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!info.isExecutable()) {
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("Error starting executable"));
|
||||
msgBox.setIcon(QMessageBox::Warning);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setText(tr("<html><head/><body><p><b>Could not start %1</b></p> \
|
||||
<p>The application is not executable.</p> \
|
||||
<p>Please make sure you have the right permissions and try again.</p></body></html>").arg(info.fileName()));
|
||||
msgBox.exec();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Start the executable
|
||||
if (detached) {
|
||||
if (!mProcess->startDetached(path, arguments)) {
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("Error starting executable"));
|
||||
msgBox.setIcon(QMessageBox::Critical);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setText(tr("<html><head/><body><p><b>Could not start %1</b></p> \
|
||||
<p>An error occurred while starting %1.</p> \
|
||||
<p>Press \"Show Details...\" for more information.</p></body></html>").arg(info.fileName()));
|
||||
msgBox.setDetailedText(mProcess->errorString());
|
||||
msgBox.exec();
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
mProcess->start(path, arguments);
|
||||
|
||||
/*
|
||||
if (!mProcess->waitForFinished()) {
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("Error starting executable"));
|
||||
msgBox.setIcon(QMessageBox::Critical);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setText(tr("<html><head/><body><p><b>Could not start %1</b></p> \
|
||||
<p>An error occurred while starting %1.</p> \
|
||||
<p>Press \"Show Details...\" for more information.</p></body></html>").arg(info.fileName()));
|
||||
msgBox.setDetailedText(mProcess->errorString());
|
||||
msgBox.exec();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mProcess->exitCode() != 0 || mProcess->exitStatus() == QProcess::CrashExit) {
|
||||
QString error(mProcess->readAllStandardError());
|
||||
error.append(tr("\nArguments:\n"));
|
||||
error.append(arguments.join(" "));
|
||||
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("Error running executable"));
|
||||
msgBox.setIcon(QMessageBox::Critical);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setText(tr("<html><head/><body><p><b>Executable %1 returned an error</b></p> \
|
||||
<p>An error occurred while running %1.</p> \
|
||||
<p>Press \"Show Details...\" for more information.</p></body></html>").arg(info.fileName()));
|
||||
msgBox.setDetailedText(error);
|
||||
msgBox.exec();
|
||||
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
void Process::ProcessInvoker::processError(QProcess::ProcessError error)
|
||||
{
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("Error running executable"));
|
||||
msgBox.setIcon(QMessageBox::Critical);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setText(tr("<html><head/><body><p><b>Executable %1 returned an error</b></p> \
|
||||
<p>An error occurred while running %1.</p> \
|
||||
<p>Press \"Show Details...\" for more information.</p></body></html>").arg(mName));
|
||||
msgBox.setDetailedText(mProcess->errorString());
|
||||
msgBox.exec();
|
||||
|
||||
}
|
||||
|
||||
void Process::ProcessInvoker::processFinished(int exitCode, QProcess::ExitStatus exitStatus)
|
||||
{
|
||||
if (exitCode != 0 || exitStatus == QProcess::CrashExit) {
|
||||
QString error(mProcess->readAllStandardError());
|
||||
error.append(tr("\nArguments:\n"));
|
||||
error.append(mArguments.join(" "));
|
||||
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("Error running executable"));
|
||||
msgBox.setIcon(QMessageBox::Critical);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setText(tr("<html><head/><body><p><b>Executable %1 returned an error</b></p> \
|
||||
<p>An error occurred while running %1.</p> \
|
||||
<p>Press \"Show Details...\" for more information.</p></body></html>").arg(mName));
|
||||
msgBox.setDetailedText(error);
|
||||
msgBox.exec();
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
#ifndef PROCESSINVOKER_HPP
|
||||
#define PROCESSINVOKER_HPP
|
||||
|
||||
#include <QStringList>
|
||||
#include <QString>
|
||||
#include <QProcess>
|
||||
|
||||
namespace Process
|
||||
{
|
||||
class ProcessInvoker : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
ProcessInvoker();
|
||||
~ProcessInvoker();
|
||||
|
||||
// void setProcessName(const QString &name);
|
||||
// void setProcessArguments(const QStringList &arguments);
|
||||
|
||||
QProcess* getProcess();
|
||||
// QString getProcessName();
|
||||
// QStringList getProcessArguments();
|
||||
|
||||
// inline bool startProcess(bool detached = false) { return startProcess(mName, mArguments, detached); }
|
||||
inline bool startProcess(const QString &name, bool detached = false) { return startProcess(name, QStringList(), detached); }
|
||||
bool startProcess(const QString &name, const QStringList &arguments, bool detached = false);
|
||||
|
||||
private:
|
||||
QProcess *mProcess;
|
||||
|
||||
QString mName;
|
||||
QStringList mArguments;
|
||||
|
||||
private slots:
|
||||
void processError(QProcess::ProcessError error);
|
||||
void processFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif // PROCESSINVOKER_HPP
|
Before Width: | Height: | Size: 477 B After Width: | Height: | Size: 477 B |
Before Width: | Height: | Size: 498 B After Width: | Height: | Size: 498 B |
Before Width: | Height: | Size: 793 B After Width: | Height: | Size: 793 B |
Before Width: | Height: | Size: 663 B After Width: | Height: | Size: 663 B |
Before Width: | Height: | Size: 683 B After Width: | Height: | Size: 683 B |
Before Width: | Height: | Size: 636 B After Width: | Height: | Size: 636 B |
Before Width: | Height: | Size: 652 B After Width: | Height: | Size: 652 B |
After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
@ -0,0 +1,156 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>SettingsPage</class>
|
||||
<widget class="QWidget" name="SettingsPage">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>514</width>
|
||||
<height>397</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="generalGroup">
|
||||
<property name="title">
|
||||
<string>General</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="languageLabel">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>The language of the original Morrowind installation files (used for the character encoding)</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Morrowind content language:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="languageComboBox">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>250</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="wizardGroup">
|
||||
<property name="title">
|
||||
<string>Morrowind Installation Wizard</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_3" columnstretch="1,1">
|
||||
<item row="1" column="1">
|
||||
<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 row="1" column="0">
|
||||
<widget class="QPushButton" name="wizardButton">
|
||||
<property name="text">
|
||||
<string>Run &Installation Wizard</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="importerGroup">
|
||||
<property name="title">
|
||||
<string>Morrowind Settings Importer</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_2" rowstretch="0,0" columnstretch="1,0">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="importerLabel">
|
||||
<property name="text">
|
||||
<string>File to import settings from:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QComboBox" name="settingsComboBox"/>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QPushButton" name="browseButton">
|
||||
<property name="text">
|
||||
<string>Browse...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="addonsCheckBox">
|
||||
<property name="text">
|
||||
<string>Import add-on and plugin selection (creates a new Content List)</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_4" columnstretch="1,1">
|
||||
<item row="2" column="1">
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<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 row="2" column="0">
|
||||
<widget class="QPushButton" name="importerButton">
|
||||
<property name="text">
|
||||
<string>Run &Settings Importer</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ComponentSelectionPage</class>
|
||||
<widget class="QWizardPage" name="ComponentSelectionPage">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>448</width>
|
||||
<height>387</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>WizardPage</string>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Select Components</string>
|
||||
</property>
|
||||
<property name="subTitle">
|
||||
<string>Which components should be installed?</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>Select which official Morrowind expansions should be installed. For best results, it is recommended to have both expansions installed.</p><p><span style=" font-weight:bold;">Note:</span> It is possible to install expansions later by re-running this Wizard.<br/></p></body></html></string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="componentsLabel">
|
||||
<property name="text">
|
||||
<string>Selected components:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ComponentListWidget" name="componentsList"/>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>81</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>ComponentListWidget</class>
|
||||
<extends>QListWidget</extends>
|
||||
<header>apps/wizard/utils/componentlistwidget.hpp</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ConclusionPage</class>
|
||||
<widget class="QWizardPage" name="ConclusionPage">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>398</width>
|
||||
<height>298</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>WizardPage</string>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Completing the OpenMW Wizard</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="textLabel">
|
||||
<property name="text">
|
||||
<string>Placeholder</string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
@ -0,0 +1,79 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ExistingInstallationPage</class>
|
||||
<widget class="QWizardPage" name="ExistingInstallationPage">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>394</width>
|
||||
<height>294</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>WizardPage</string>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Select Existing Installation</string>
|
||||
</property>
|
||||
<property name="subTitle">
|
||||
<string>Select an existing installation for OpenMW to use or modify.</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="installationsLabel">
|
||||
<property name="text">
|
||||
<string>Detected installations:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QListWidget" name="installationsList"/>
|
||||
</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="browseButton">
|
||||
<property name="text">
|
||||
<string>Browse...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="folder">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ImportPage</class>
|
||||
<widget class="QWizardPage" name="ImportPage">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>508</width>
|
||||
<height>322</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>WizardPage</string>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Import Settings</string>
|
||||
</property>
|
||||
<property name="subTitle">
|
||||
<string>Import settings from the Morrowind installation.</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="infoLabel">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>OpenMW needs to import settings from the Morrowind configuration file in order to function properly.</p><p><span style=" font-weight:bold;">Note:</span> It is possible to import settings later by re-running this Wizard.</p><p/></body></html></string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="importCheckBox">
|
||||
<property name="text">
|
||||
<string>Import settings from Morrowind.ini</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="addonsCheckBox">
|
||||
<property name="text">
|
||||
<string>Import add-on and plugin selection</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>InstallationPage</class>
|
||||
<widget class="QWizardPage" name="InstallationPage">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>514</width>
|
||||
<height>419</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>WizardPage</string>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Installing</string>
|
||||
</property>
|
||||
<property name="subTitle">
|
||||
<string>Please wait while Morrowind is installed on your computer.</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="installProgressLabel">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QProgressBar" name="installProgressBar">
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="logTextEdit">
|
||||
<property name="undoRedoEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
@ -0,0 +1,89 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>InstallationTargetPage</class>
|
||||
<widget class="QWizardPage" name="InstallationTargetPage">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>WizardPage</string>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Select Installation Destination</string>
|
||||
</property>
|
||||
<property name="subTitle">
|
||||
<string>Where should Morrowind be installed?</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="folderIconLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><img src=":/icons/tango/48x48/folder.png"/></p></body></html></string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="infoLabel">
|
||||
<property name="text">
|
||||
<string>Morrowind will be installed to the following location. </string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="targetLineEdit"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="browseButton">
|
||||
<property name="text">
|
||||
<string>Browse...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="../../wizard/wizard.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>IntroPage</class>
|
||||
<widget class="QWizardPage" name="IntroPage">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>472</width>
|
||||
<height>368</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>WizardPage</string>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Welcome to the OpenMW Wizard</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="textLabel">
|
||||
<property name="text">
|
||||
<string>This Wizard will help you install Morrowind and its add-ons for OpenMW to use.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
@ -0,0 +1,113 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>LanguageSelectionPage</class>
|
||||
<widget class="QWizardPage" name="LanguageSelectionPage">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>396</width>
|
||||
<height>296</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>WizardPage</string>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Select Morrowind Language</string>
|
||||
</property>
|
||||
<property name="subTitle">
|
||||
<string>What is the language of the Morrowind installation?</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="flagIconLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><img src=":/icons/tango/48x48/preferences-desktop-locale.png"/></p></body></html></string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="infoLabel">
|
||||
<property name="text">
|
||||
<string>Select the language of the Morrowind installation.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<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="QComboBox" name="languageComboBox">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>250</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>230</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
@ -0,0 +1,154 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MethodSelectionPage</class>
|
||||
<widget class="QWizardPage" name="MethodSelectionPage">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>396</width>
|
||||
<height>296</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>WizardPage</string>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Select Installation Method</string>
|
||||
</property>
|
||||
<property name="subTitle">
|
||||
<string>Select how OpenMW should get the required Morrowind installation files.</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="newLocationRadioButton">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font-weight:bold;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Install Morrowind to a new location</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="newLocationLayout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Maximum</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="installerIconLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><img src=":/icons/tango/48x48/system-installer.png"/></p></body></html></string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>newLocationRadioButton</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="newLocationLabel">
|
||||
<property name="text">
|
||||
<string>Install Morrowind from a retail disk to a new location for OpenMW to use.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="existingLocationRadioButton">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font-weight:bold</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Select an existing Morrowind installation</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="existingLocationLayout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Maximum</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="folderIconLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><img src=":/icons/tango/48x48/folder.png"/></p></body></html></string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="existingLocationLabel">
|
||||
<property name="text">
|
||||
<string>Select an existing Morrowind installation for OpenMW to use.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 2.6 KiB |
@ -0,0 +1,8 @@
|
||||
[Icon Theme]
|
||||
Name=Tango
|
||||
Comment=Tango Theme
|
||||
Inherits=default
|
||||
Directories=48x48
|
||||
|
||||
[48x48]
|
||||
Size=48
|
After Width: | Height: | Size: 147 KiB |
After Width: | Height: | Size: 71 KiB |
@ -0,0 +1,12 @@
|
||||
<RCC>
|
||||
<qresource prefix="icons/tango">
|
||||
<file alias="48x48/preferences-desktop-locale.png">icons/tango/48x48/preferences-desktop-locale.png</file>
|
||||
<file alias="index.theme">icons/tango/index.theme</file>
|
||||
<file alias="48x48/folder.png">icons/tango/48x48/folder.png</file>
|
||||
<file alias="48x48/system-installer.png">icons/tango/48x48/system-installer.png</file>
|
||||
</qresource>
|
||||
<qresource prefix="images">
|
||||
<file alias="intropage-background.png">images/intropage-background.png</file>
|
||||
<file alias="openmw-wizard.png">images/openmw-wizard.png</file>
|
||||
</qresource>
|
||||
</RCC>
|