Merge remote-tracking branch 'scrawl/wizard'

loadfix
Marc Zinnschlag 10 years ago
commit cd5d02402f

@ -75,6 +75,7 @@ option(BUILD_ESMTOOL "build ESM inspector" ON)
option(BUILD_LAUNCHER "build Launcher" ON)
option(BUILD_MWINIIMPORTER "build MWiniImporter" ON)
option(BUILD_OPENCS "build OpenMW Construction Set" ON)
option(BUILD_WIZARD "build Installation Wizard" ON)
option(BUILD_WITH_CODE_COVERAGE "Enable code coverage with gconv" OFF)
option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest and GMock frameworks" OFF)
option(BUILD_NIFTEST "build nif file tester" OFF)
@ -433,6 +434,9 @@ IF(NOT WIN32 AND NOT APPLE)
IF(BUILD_NIFTEST)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/niftest" DESTINATION "${BINDIR}" )
ENDIF(BUILD_NIFTEST)
IF(BUILD_WIZARD)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-wizard" DESTINATION "${BINDIR}" )
ENDIF(BUILD_WIZARD)
if(BUILD_MYGUI_PLUGIN)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Plugin_MyGUI_OpenMW_Resources.so" DESTINATION "${LIBDIR}" )
ENDIF(BUILD_MYGUI_PLUGIN)
@ -485,6 +489,9 @@ if(WIN32)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/opencs.exe" DESTINATION ".")
INSTALL(FILES "${OpenMW_BINARY_DIR}/opencs.ini" DESTINATION ".")
ENDIF(BUILD_OPENCS)
IF(BUILD_WIZARD)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-wizard.exe" DESTINATION ".")
ENDIF(BUILD_WIZARD)
if(BUILD_MYGUI_PLUGIN)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/Plugin_MyGUI_OpenMW_Resources.dll" DESTINATION ".")
ENDIF(BUILD_MYGUI_PLUGIN)
@ -505,6 +512,9 @@ if(WIN32)
IF(BUILD_OPENCS)
SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};opencs;OpenMW Construction Set")
ENDIF(BUILD_OPENCS)
IF(BUILD_WIZARD)
SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};openmw-wizard;OpenMW Wizard")
ENDIF(BUILD_WIZARD)
SET(CPACK_NSIS_CREATE_ICONS_EXTRA "CreateShortCut '\$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Readme.lnk' '\$INSTDIR\\\\readme.txt'")
SET(CPACK_NSIS_DELETE_ICONS_EXTRA "
!insertmacro MUI_STARTMENU_GETFOLDER Application $MUI_TEMP
@ -578,13 +588,6 @@ if (BUILD_ESMTOOL)
endif()
if (BUILD_LAUNCHER)
if(NOT WIN32)
find_package(LIBUNSHIELD REQUIRED)
if(NOT LIBUNSHIELD_FOUND)
message(SEND_ERROR "Failed to find libunshield")
endif(NOT LIBUNSHIELD_FOUND)
endif(NOT WIN32)
add_subdirectory( apps/launcher )
endif()
@ -596,6 +599,15 @@ if (BUILD_OPENCS)
add_subdirectory (apps/opencs)
endif()
if (BUILD_WIZARD)
find_package(LIBUNSHIELD REQUIRED)
if(NOT LIBUNSHIELD_FOUND)
message(FATAL_ERROR "Failed to find Unshield library")
endif(NOT LIBUNSHIELD_FOUND)
add_subdirectory(apps/wizard)
endif()
# UnitTests
if (BUILD_UNITTESTS)
add_subdirectory( apps/openmw_test_suite )
@ -698,6 +710,9 @@ if (WIN32)
if (BUILD_ESMTOOL)
set_target_properties(esmtool PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
endif (BUILD_ESMTOOL)
if (BUILD_WIZARD)
set_target_properties(openmw-wizard PROPERTIES COMPILE_FLAGS ${WARNINGS})
endif (BUILD_WIZARD)
if (BUILD_OPENCS)
# QT triggers an informational warning that the object layout may differ when compiled with /vd2
set(OPENCS_WARNINGS "${WARNINGS} ${MT_BUILD} /wd4435")

@ -5,21 +5,16 @@ set(LAUNCHER
maindialog.cpp
playpage.cpp
textslotmsgbox.cpp
settingspage.cpp
settings/gamesettings.cpp
settings/graphicssettings.cpp
settings/launchersettings.cpp
utils/checkablemessagebox.cpp
utils/profilescombobox.cpp
utils/textinputdialog.cpp
utils/lineedit.cpp
${CMAKE_SOURCE_DIR}/files/windows/launcher.rc
)
if(NOT WIN32)
LIST(APPEND LAUNCHER unshieldthread.cpp)
endif(NOT WIN32)
set(LAUNCHER_HEADER
datafilespage.hpp
@ -27,21 +22,14 @@ set(LAUNCHER_HEADER
maindialog.hpp
playpage.hpp
textslotmsgbox.hpp
settingspage.hpp
settings/gamesettings.hpp
settings/graphicssettings.hpp
settings/launchersettings.hpp
settings/settingsbase.hpp
utils/checkablemessagebox.hpp
utils/profilescombobox.hpp
utils/textinputdialog.hpp
utils/lineedit.hpp
)
if(NOT WIN32)
LIST(APPEND LAUNCHER_HEADER unshieldthread.hpp)
endif(NOT WIN32)
# Headers that must be pre-processed
set(LAUNCHER_HEADER_MOC
@ -50,25 +38,21 @@ set(LAUNCHER_HEADER_MOC
maindialog.hpp
playpage.hpp
textslotmsgbox.hpp
settingspage.hpp
utils/textinputdialog.hpp
utils/checkablemessagebox.hpp
utils/profilescombobox.hpp
utils/lineedit.hpp
)
if(NOT WIN32)
LIST(APPEND LAUNCHER_HEADER_MOC unshieldthread.hpp)
endif(NOT WIN32)
set(LAUNCHER_UI
${CMAKE_SOURCE_DIR}/files/ui/datafilespage.ui
${CMAKE_SOURCE_DIR}/files/ui/graphicspage.ui
${CMAKE_SOURCE_DIR}/files/ui/mainwindow.ui
${CMAKE_SOURCE_DIR}/files/ui/playpage.ui
${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui
${CMAKE_SOURCE_DIR}/files/ui/settingspage.ui
)
source_group(launcher FILES ${LAUNCHER} ${LAUNCHER_HEADER})
@ -111,12 +95,6 @@ target_link_libraries(omwlauncher
${QT_LIBRARIES}
components
)
if(NOT WIN32)
target_link_libraries(omwlauncher
${LIBUNSHIELD_LIBRARY}
)
endif(NOT WIN32)
if (BUILD_WITH_CODE_COVERAGE)

@ -1,5 +1,7 @@
#include "datafilespage.hpp"
#include <QDebug>
#include <QPushButton>
#include <QMessageBox>
#include <QCheckBox>
@ -9,18 +11,16 @@
#include <components/files/configurationmanager.hpp>
#include <components/contentselector/model/esmfile.hpp>
#include <components/contentselector/model/naturalsort.hpp>
#include <components/contentselector/view/contentselector.hpp>
#include <components/config/gamesettings.hpp>
#include <components/config/launchersettings.hpp>
#include "utils/textinputdialog.hpp"
#include "utils/profilescombobox.hpp"
#include "settings/gamesettings.hpp"
#include "settings/launchersettings.hpp"
#include "components/contentselector/view/contentselector.hpp"
Launcher::DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gameSettings, LauncherSettings &launcherSettings, QWidget *parent)
Launcher::DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, Config::GameSettings &gameSettings, Config::LauncherSettings &launcherSettings, QWidget *parent)
: mCfgMgr(cfg)
, mGameSettings(gameSettings)
, mLauncherSettings(launcherSettings)
@ -30,20 +30,71 @@ Launcher::DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSet
setObjectName ("DataFilesPage");
mSelector = new ContentSelectorView::ContentSelector (ui.contentSelectorWidget);
mProfileDialog = new TextInputDialog(tr("New Profile"), tr("Profile name:"), this);
connect(mProfileDialog->lineEdit(), SIGNAL(textChanged(QString)),
this, SLOT(updateOkButton(QString)));
buildView();
setupDataFiles();
loadSettings();
}
void Launcher::DataFilesPage::loadSettings()
void Launcher::DataFilesPage::buildView()
{
ui.verticalLayout->insertWidget (0, mSelector->uiWidget());
//tool buttons
ui.newProfileButton->setToolTip ("Create a new profile");
ui.deleteProfileButton->setToolTip ("Delete an existing profile");
//combo box
ui.profilesComboBox->addItem ("Default");
ui.profilesComboBox->setPlaceholderText (QString("Select a profile..."));
ui.profilesComboBox->setCurrentIndex(ui.profilesComboBox->findText(QLatin1String("Default")));
// Add the actions to the toolbuttons
ui.newProfileButton->setDefaultAction (ui.newProfileAction);
ui.deleteProfileButton->setDefaultAction (ui.deleteProfileAction);
//establish connections
connect (ui.profilesComboBox, SIGNAL (currentIndexChanged(int)),
this, SLOT (slotProfileChanged(int)));
connect (ui.profilesComboBox, SIGNAL (profileRenamed(QString, QString)),
this, SLOT (slotProfileRenamed(QString, QString)));
connect (ui.profilesComboBox, SIGNAL (signalProfileChanged(QString, QString)),
this, SLOT (slotProfileChangedByUser(QString, QString)));
}
bool Launcher::DataFilesPage::loadSettings()
{
QStringList paths = mGameSettings.getDataDirs();
foreach (const QString &path, paths)
mSelector->addFiles(path);
mDataLocal = mGameSettings.getDataLocal();
if (!mDataLocal.isEmpty())
mSelector->addFiles(mDataLocal);
paths.insert (0, mDataLocal);
PathIterator pathIterator (paths);
QString profileName = ui.profilesComboBox->currentText();
QStringList profiles = mLauncherSettings.subKeys(QString("Profiles/"));
QString currentProfile = mLauncherSettings.getSettings().value("Profiles/currentprofile");
qDebug() << "current profile is: " << currentProfile;
foreach (const QString &item, profiles)
addProfile (item, false);
QStringList files = mLauncherSettings.values(QString("Profiles/") + profileName, Qt::MatchExactly);
// Hack: also add the current profile
if (!currentProfile.isEmpty())
addProfile(currentProfile, true);
QStringList files = mLauncherSettings.values(QString("Profiles/") + currentProfile + QString("/content"), Qt::MatchExactly);
QStringList filepaths;
foreach (const QString &file, files)
@ -55,6 +106,8 @@ void Launcher::DataFilesPage::loadSettings()
}
mSelector->setProfileContent (filepaths);
return true;
}
void Launcher::DataFilesPage::saveSettings(const QString &profile)
@ -75,39 +128,12 @@ void Launcher::DataFilesPage::saveSettings(const QString &profile)
mLauncherSettings.setValue(QString("Profiles/currentprofile"), ui.profilesComboBox->currentText());
foreach(const ContentSelectorModel::EsmFile *item, items) {
mLauncherSettings.setMultiValue(QString("Profiles/") + profileName, item->fileName());
mLauncherSettings.setMultiValue(QString("Profiles/") + profileName + QString("/content"), item->fileName());
mGameSettings.setMultiValue(QString("content"), item->fileName());
}
}
void Launcher::DataFilesPage::buildView()
{
ui.verticalLayout->insertWidget (0, mSelector->uiWidget());
//tool buttons
ui.newProfileButton->setToolTip ("Create a new profile");
ui.deleteProfileButton->setToolTip ("Delete an existing profile");
//combo box
ui.profilesComboBox->addItem ("Default");
ui.profilesComboBox->setPlaceholderText (QString("Select a profile..."));
// Add the actions to the toolbuttons
ui.newProfileButton->setDefaultAction (ui.newProfileAction);
ui.deleteProfileButton->setDefaultAction (ui.deleteProfileAction);
//establish connections
connect (ui.profilesComboBox, SIGNAL (currentIndexChanged(int)),
this, SLOT (slotProfileChanged(int)));
connect (ui.profilesComboBox, SIGNAL (profileRenamed(QString, QString)),
this, SLOT (slotProfileRenamed(QString, QString)));
connect (ui.profilesComboBox, SIGNAL (signalProfileChanged(QString, QString)),
this, SLOT (slotProfileChangedByUser(QString, QString)));
}
void Launcher::DataFilesPage::removeProfile(const QString &profile)
{
mLauncherSettings.remove(QString("Profiles/") + profile);
@ -140,6 +166,9 @@ void Launcher::DataFilesPage::setProfile (const QString &previous, const QString
if (previous == current)
return;
if (previous.isEmpty())
return;
if (!previous.isEmpty() && savePrevious)
saveSettings (previous);
@ -180,53 +209,15 @@ void Launcher::DataFilesPage::slotProfileChanged(int index)
setProfile (index, true);
}
void Launcher::DataFilesPage::setupDataFiles()
{
QStringList paths = mGameSettings.getDataDirs();
foreach (const QString &path, paths)
mSelector->addFiles(path);
mDataLocal = mGameSettings.getDataLocal();
if (!mDataLocal.isEmpty())
mSelector->addFiles(mDataLocal);
QStringList profiles;
QString currentProfile = mLauncherSettings.getSettings().value("Profiles/currentprofile");
foreach (QString key, mLauncherSettings.getSettings().keys())
{
if (key.contains("Profiles/"))
{
QString profile = key.mid (9);
if (profile != "currentprofile")
{
if (!profiles.contains(profile))
profiles << profile;
}
}
}
foreach (const QString &item, profiles)
addProfile (item, false);
setProfile (ui.profilesComboBox->findText(currentProfile), false);
loadSettings();
}
void Launcher::DataFilesPage::on_newProfileAction_triggered()
{
TextInputDialog newDialog (tr("New Profile"), tr("Profile name:"), this);
if (newDialog.exec() != QDialog::Accepted)
if (!mProfileDialog->exec() == QDialog::Accepted)
return;
QString profile = newDialog.getText();
QString profile = mProfileDialog->lineEdit()->text();
if (profile.isEmpty())
return;
return;
saveSettings();
@ -277,6 +268,19 @@ void Launcher::DataFilesPage::on_deleteProfileAction_triggered()
checkForDefaultProfile();
}
void Launcher::DataFilesPage::updateOkButton(const QString &text)
{
// We do this here because we need the profiles combobox text
if (text.isEmpty()) {
mProfileDialog->setOkButtonEnabled(false);
return;
}
(ui.profilesComboBox->findText(text) == -1)
? mProfileDialog->setOkButtonEnabled(true)
: mProfileDialog->setOkButtonEnabled(false);
}
void Launcher::DataFilesPage::checkForDefaultProfile()
{
//don't allow deleting "Default" profile

@ -14,12 +14,12 @@ class QMenu;
namespace Files { struct ConfigurationManager; }
namespace ContentSelectorView { class ContentSelector; }
namespace Config { class GameSettings;
class LauncherSettings; }
namespace Launcher
{
class TextInputDialog;
class GameSettings;
class LauncherSettings;
class ProfilesComboBox;
class DataFilesPage : public QWidget
@ -30,8 +30,8 @@ namespace Launcher
Ui::DataFilesPage ui;
public:
explicit DataFilesPage (Files::ConfigurationManager &cfg, GameSettings &gameSettings,
LauncherSettings &launcherSettings, QWidget *parent = 0);
explicit DataFilesPage (Files::ConfigurationManager &cfg, Config::GameSettings &gameSettings,
Config::LauncherSettings &launcherSettings, QWidget *parent = 0);
QAbstractItemModel* profilesModel() const;
@ -39,7 +39,7 @@ namespace Launcher
//void writeConfig(QString profile = QString());
void saveSettings(const QString &profile = "");
void loadSettings();
bool loadSettings();
signals:
void signalProfileChanged (int index);
@ -53,24 +53,25 @@ namespace Launcher
void slotProfileRenamed(const QString &previous, const QString &current);
void slotProfileDeleted(const QString &item);
void updateOkButton(const QString &text);
void on_newProfileAction_triggered();
void on_deleteProfileAction_triggered();
private:
QMenu *mContextMenu;
TextInputDialog *mProfileDialog;
Files::ConfigurationManager &mCfgMgr;
GameSettings &mGameSettings;
LauncherSettings &mLauncherSettings;
Config::GameSettings &mGameSettings;
Config::LauncherSettings &mLauncherSettings;
QString mDataLocal;
void setPluginsCheckstates(Qt::CheckState state);
void buildView();
void setupDataFiles();
void setupConfig();
void readConfig();
void setProfile (int index, bool savePrevious);

@ -137,6 +137,7 @@ bool Launcher::GraphicsPage::setupSDL()
return false;
}
screenComboBox->clear();
for (int i = 0; i < displays; i++)
{
screenComboBox->addItem(QString(tr("Screen ")) + QString::number(i + 1));
@ -149,7 +150,7 @@ bool Launcher::GraphicsPage::loadSettings()
{
if (!setupSDL())
return false;
if (!setupOgre())
if (!mOgre && !setupOgre())
return false;
if (mGraphicsSettings.value(QString("Video/vsync")) == QLatin1String("true"))

@ -52,11 +52,14 @@ int main(int argc, char *argv[])
Launcher::MainDialog mainWin;
if (mainWin.setup()) {
mainWin.show();
} else {
if (!mainWin.showFirstRunDialog())
return 0;
}
// if (!mainWin.setup()) {
// return 0;
// }
mainWin.show();
int returnValue = app.exec();
SDL_Quit();

@ -5,29 +5,24 @@
#include <QLabel>
#include <QDate>
#include <QTime>
#include <QMessageBox>
#include <QPushButton>
#include <QFontDatabase>
#include <QInputDialog>
#include <QFileDialog>
#include <QCloseEvent>
#include <QTextCodec>
#include <QProcess>
#include <QFile>
#include <QDir>
#include <QDebug>
#ifndef WIN32
#include "unshieldthread.hpp"
#endif
#include "textslotmsgbox.hpp"
#include "utils/checkablemessagebox.hpp"
#include "playpage.hpp"
#include "graphicspage.hpp"
#include "datafilespage.hpp"
#include "settingspage.hpp"
using namespace Process;
Launcher::MainDialog::MainDialog(QWidget *parent)
: mGameSettings(mCfgMgr), QMainWindow (parent)
@ -53,6 +48,15 @@ Launcher::MainDialog::MainDialog(QWidget *parent)
setupUi(this);
mGameInvoker = new ProcessInvoker();
mWizardInvoker = new ProcessInvoker();
connect(mWizardInvoker->getProcess(), SIGNAL(started()),
this, SLOT(wizardStarted()));
connect(mWizardInvoker->getProcess(), SIGNAL(finished(int,QProcess::ExitStatus)),
this, SLOT(wizardFinished(int,QProcess::ExitStatus)));
iconWidget->setViewMode(QListView::IconMode);
iconWidget->setWrapping(false);
iconWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // Just to be sure
@ -79,13 +83,13 @@ Launcher::MainDialog::MainDialog(QWidget *parent)
if (!revision.isEmpty() && !tag.isEmpty())
{
if (revision == tag) {
versionLabel->setText(tr("OpenMW %0 release").arg(OPENMW_VERSION));
versionLabel->setText(tr("OpenMW %1 release").arg(OPENMW_VERSION));
} else {
versionLabel->setText(tr("OpenMW development (%0)").arg(revision.left(10)));
versionLabel->setText(tr("OpenMW development (%1)").arg(revision.left(10)));
}
// Add the compile date and time
versionLabel->setToolTip(tr("Compiled on %0 %1").arg(QLocale(QLocale::C).toDate(QString(__DATE__).simplified(),
versionLabel->setToolTip(tr("Compiled on %1 %2").arg(QLocale(QLocale::C).toDate(QString(__DATE__).simplified(),
QLatin1String("MMM d yyyy")).toString(Qt::SystemLocaleLongDate),
QLocale(QLocale::C).toTime(QString(__TIME__).simplified(),
QLatin1String("hh:mm:ss")).toString(Qt::SystemLocaleShortDate)));
@ -94,32 +98,41 @@ Launcher::MainDialog::MainDialog(QWidget *parent)
createIcons();
}
Launcher::MainDialog::~MainDialog()
{
delete mGameInvoker;
delete mWizardInvoker;
}
void Launcher::MainDialog::createIcons()
{
if (!QIcon::hasThemeIcon("document-new"))
QIcon::setThemeName("tango");
// We create a fallback icon because the default fallback doesn't work
QIcon graphicsIcon = QIcon(":/icons/tango/video-display.png");
QListWidgetItem *playButton = new QListWidgetItem(iconWidget);
playButton->setIcon(QIcon(":/images/openmw.png"));
playButton->setText(tr("Play"));
playButton->setTextAlignment(Qt::AlignCenter);
playButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
QListWidgetItem *graphicsButton = new QListWidgetItem(iconWidget);
graphicsButton->setIcon(QIcon::fromTheme("video-display", graphicsIcon));
graphicsButton->setText(tr("Graphics"));
graphicsButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom | Qt::AlignAbsolute);
graphicsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
QListWidgetItem *dataFilesButton = new QListWidgetItem(iconWidget);
dataFilesButton->setIcon(QIcon(":/images/openmw-plugin.png"));
dataFilesButton->setText(tr("Data Files"));
dataFilesButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom);
dataFilesButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
QListWidgetItem *graphicsButton = new QListWidgetItem(iconWidget);
graphicsButton->setIcon(QIcon::fromTheme("video-display"));
graphicsButton->setText(tr("Graphics"));
graphicsButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom | Qt::AlignAbsolute);
graphicsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
QListWidgetItem *settingsButton = new QListWidgetItem(iconWidget);
settingsButton->setIcon(QIcon::fromTheme("preferences-system"));
settingsButton->setText(tr("Settings"));
settingsButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom);
settingsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
connect(iconWidget,
SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)),
this, SLOT(changePage(QListWidgetItem*,QListWidgetItem*)));
@ -129,8 +142,9 @@ void Launcher::MainDialog::createIcons()
void Launcher::MainDialog::createPages()
{
mPlayPage = new PlayPage(this);
mGraphicsPage = new GraphicsPage(mCfgMgr, mGraphicsSettings, this);
mDataFilesPage = new DataFilesPage(mCfgMgr, mGameSettings, mLauncherSettings, this);
mGraphicsPage = new GraphicsPage(mCfgMgr, mGraphicsSettings, this);
mSettingsPage = new SettingsPage(mCfgMgr, mGameSettings, mLauncherSettings, this);
// Set the combobox of the play page to imitate the combobox on the datafilespage
mPlayPage->setProfilesModel(mDataFilesPage->profilesModel());
@ -138,8 +152,9 @@ void Launcher::MainDialog::createPages()
// Add the pages to the stacked widget
pagesWidget->addWidget(mPlayPage);
pagesWidget->addWidget(mGraphicsPage);
pagesWidget->addWidget(mDataFilesPage);
pagesWidget->addWidget(mGraphicsPage);
pagesWidget->addWidget(mSettingsPage);
// Select the first page
iconWidget->setCurrentItem(iconWidget->item(0), QItemSelectionModel::Select);
@ -153,153 +168,63 @@ void Launcher::MainDialog::createPages()
bool Launcher::MainDialog::showFirstRunDialog()
{
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 (!setupLauncherSettings())
return false;
// Ask the user where the Morrowind.ini is
if (iniPaths.empty()) {
if (mLauncherSettings.value(QString("General/firstrun"), QString("true")) == QLatin1String("true"))
{
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> \
OpenMW needs to import settings from this file.<br><br> \
Press \"Browse...\" to specify the location manually.<br>"));
QAbstractButton *dirSelectButton =
msgBox.addButton(QObject::tr("B&rowse..."), QMessageBox::ActionRole);
msgBox.exec();
QString iniFile;
if (msgBox.clickedButton() == dirSelectButton) {
iniFile = QFileDialog::getOpenFileName(
NULL,
QObject::tr("Select configuration file"),
QDir::currentPath(),
QString(tr("Morrowind configuration file (*.ini)")));
}
if (iniFile.isEmpty())
return false; // Cancel was clicked;
QFileInfo info(iniFile);
iniPaths.clear();
iniPaths.append(info.absoluteFilePath());
}
CheckableMessageBox msgBox(this);
msgBox.setWindowTitle(tr("Morrowind installation detected"));
QIcon icon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxQuestion);
int size = QApplication::style()->pixelMetric(QStyle::PM_MessageBoxIconSize);
msgBox.setIconPixmap(icon.pixmap(size, size));
QAbstractButton *importerButton =
msgBox.addButton(tr("Import"), QDialogButtonBox::AcceptRole); // ActionRole doesn't work?!
QAbstractButton *skipButton =
msgBox.addButton(tr("Skip"), QDialogButtonBox::RejectRole);
Q_UNUSED(skipButton); // Surpress compiler unused warning
msgBox.setStandardButtons(QDialogButtonBox::NoButton);
msgBox.setText(tr("<br><b>An existing Morrowind configuration was detected</b><br> \
<br>Would you like to import settings from Morrowind.ini?<br> \
<br><b>Warning: In most cases OpenMW needs these settings to run properly</b><br>"));
msgBox.setCheckBoxText(tr("Include selected masters and plugins (creates a new profile)"));
msgBox.exec();
msgBox.setWindowTitle(tr("First run"));
msgBox.setIcon(QMessageBox::Question);
msgBox.setStandardButtons(QMessageBox::NoButton);
msgBox.setText(tr("<html><head/><body><p><b>Welcome to OpenMW!</b></p> \
<p>It is recommended to run the Installation Wizard.</p> \
<p>The Wizard will let you select an existing Morrowind installation, \
or install Morrowind for OpenMW to use.</p></body></html>"));
QAbstractButton *wizardButton =
msgBox.addButton(tr("Run &Installation Wizard"), QMessageBox::AcceptRole); // ActionRole doesn't work?!
QAbstractButton *skipButton =
msgBox.addButton(tr("Skip"), QMessageBox::RejectRole);
if (msgBox.clickedButton() == importerButton) {
Q_UNUSED(skipButton); // Surpress compiler unused warning
if (iniPaths.count() > 1) {
// Multiple Morrowind.ini files found
bool ok;
QString path = QInputDialog::getItem(this, tr("Multiple configurations found"),
tr("<br><b>There are multiple Morrowind.ini files found.</b><br><br> \
Please select the one you wish to import from:"), iniPaths, 0, false, &ok);
if (ok && !path.isEmpty()) {
iniPaths.clear();
iniPaths.append(path);
} else {
// Cancel was clicked
return false;
}
}
// Create the file if it doesn't already exist, else the importer will fail
QString path = QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str()) + QString("openmw.cfg");
QFile file(path);
msgBox.exec();
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("<br><b>Could not open or create %0 for writing</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
msgBox.exec();
if (msgBox.clickedButton() == wizardButton)
{
if (!mWizardInvoker->startProcess(QLatin1String("openmw-wizard"), false)) {
return false;
} else {
return true;
}
file.close();
}
}
// Construct the arguments to run the importer
QStringList arguments;
if (msgBox.isChecked())
arguments.append(QString("--game-files"));
arguments.append(QString("--encoding"));
arguments.append(mGameSettings.value(QString("encoding"), QString("win1252")));
arguments.append(QString("--ini"));
arguments.append(iniPaths.first());
arguments.append(QString("--cfg"));
arguments.append(path);
return setup();
}
if (!startProgram(QString("mwiniimport"), arguments, false))
return false;
bool Launcher::MainDialog::setup()
{
if (!setupGameSettings())
return false;
// Re-read the game settings
if (!setupGameSettings())
return false;
if (!setupGraphicsSettings())
return false;
// Add a new profile
if (msgBox.isChecked()) {
mLauncherSettings.setValue(QString("Profiles/currentprofile"), QString("Imported"));
mLauncherSettings.remove(QString("Profiles/Imported/content"));
// Now create the pages as they need the settings
createPages();
QStringList contents = mGameSettings.values(QString("content"));
foreach (const QString &content, contents) {
mLauncherSettings.setMultiValue(QString("Profiles/Imported/content"), content);
}
}
// Call this so we can exit on Ogre/SDL errors before mainwindow is shown
if (!mGraphicsPage->loadSettings())
return false;
}
loadSettings();
return true;
}
bool Launcher::MainDialog::setup()
bool Launcher::MainDialog::reloadSettings()
{
if (!setupLauncherSettings())
return false;
@ -310,21 +235,15 @@ bool Launcher::MainDialog::setup()
if (!setupGraphicsSettings())
return false;
// Check if we need to show the importer
if (mLauncherSettings.value(QString("General/firstrun"), QString("true")) == QLatin1String("true"))
{
if (!showFirstRunDialog())
return false;
}
if (!mSettingsPage->loadSettings())
return false;
// Now create the pages as they need the settings
createPages();
if (!mDataFilesPage->loadSettings())
return false;
// Call this so we can exit on Ogre/SDL errors before mainwindow is shown
if (!mGraphicsPage->loadSettings())
return false;
loadSettings();
return true;
}
@ -334,24 +253,24 @@ void Launcher::MainDialog::changePage(QListWidgetItem *current, QListWidgetItem
current = previous;
int currentIndex = iconWidget->row(current);
int previousIndex = iconWidget->row(previous);
// int previousIndex = iconWidget->row(previous);
pagesWidget->setCurrentIndex(currentIndex);
DataFilesPage *previousPage = dynamic_cast<DataFilesPage *>(pagesWidget->widget(previousIndex));
DataFilesPage *currentPage = dynamic_cast<DataFilesPage *>(pagesWidget->widget(currentIndex));
//special call to update/save data files page list view when it's displayed/hidden.
if (previousPage)
{
if (previousPage->objectName() == "DataFilesPage")
previousPage->saveSettings();
}
else if (currentPage)
{
if (currentPage->objectName() == "DataFilesPage")
currentPage->loadSettings();
}
// DataFilesPage *previousPage = dynamic_cast<DataFilesPage *>(pagesWidget->widget(previousIndex));
// DataFilesPage *currentPage = dynamic_cast<DataFilesPage *>(pagesWidget->widget(currentIndex));
// //special call to update/save data files page list view when it's displayed/hidden.
// if (previousPage)
// {
// if (previousPage->objectName() == "DataFilesPage")
// previousPage->saveSettings();
// }
// else if (currentPage)
// {
// if (currentPage->objectName() == "DataFilesPage")
// currentPage->loadSettings();
// }
}
bool Launcher::MainDialog::setupLauncherSettings()
@ -373,10 +292,10 @@ bool Launcher::MainDialog::setupLauncherSettings()
msgBox.setWindowTitle(tr("Error opening OpenMW configuration file"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(QObject::tr("<br><b>Could not open %0 for reading</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
msgBox.exec();
msgBox.setText(tr("<br><b>Could not open %0 for reading</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
msgBox.exec();
return false;
}
QTextStream stream(&file);
@ -390,78 +309,6 @@ bool Launcher::MainDialog::setupLauncherSettings()
return true;
}
#ifndef WIN32
bool Launcher::expansions(Launcher::UnshieldThread& cd)
{
if(cd.BloodmoonDone())
{
cd.Done();
return false;
}
QMessageBox expansionsBox;
expansionsBox.setText(QObject::tr("<br>Would you like to install expansions now ? (make sure you have the disc)<br> \
If you want to install both Bloodmoon and Tribunal, you have to install Tribunal first.<br>"));
QAbstractButton* tribunalButton = NULL;
if(!cd.TribunalDone())
tribunalButton = expansionsBox.addButton(QObject::tr("&Tribunal"), QMessageBox::ActionRole);
QAbstractButton* bloodmoonButton = expansionsBox.addButton(QObject::tr("&Bloodmoon"), QMessageBox::ActionRole);
QAbstractButton* noneButton = expansionsBox.addButton(QObject::tr("&None"), QMessageBox::ActionRole);
expansionsBox.exec();
if(expansionsBox.clickedButton() == noneButton)
{
cd.Done();
return false;
}
else if(expansionsBox.clickedButton() == tribunalButton)
{
TextSlotMsgBox cdbox;
cdbox.setStandardButtons(QMessageBox::Cancel);
QObject::connect(&cd,SIGNAL(signalGUI(const QString&)), &cdbox, SLOT(setTextSlot(const QString&)));
QObject::connect(&cd,SIGNAL(close()), &cdbox, SLOT(reject()));
cd.SetTribunalPath(
QFileDialog::getOpenFileName(
NULL,
QObject::tr("Select data1.hdr from Tribunal Installation CD (Tribunal/data1.hdr on GOTY CDs)"),
QDir::currentPath(),
QString(QObject::tr("Installshield hdr file (*.hdr)"))).toUtf8().constData());
cd.start();
cdbox.exec();
}
else if(expansionsBox.clickedButton() == bloodmoonButton)
{
TextSlotMsgBox cdbox;
cdbox.setStandardButtons(QMessageBox::Cancel);
QObject::connect(&cd,SIGNAL(signalGUI(const QString&)), &cdbox, SLOT(setTextSlot(const QString&)));
QObject::connect(&cd,SIGNAL(close()), &cdbox, SLOT(reject()));
cd.SetBloodmoonPath(
QFileDialog::getOpenFileName(
NULL,
QObject::tr("Select data1.hdr from Bloodmoon Installation CD (Bloodmoon/data1.hdr on GOTY CDs)"),
QDir::currentPath(),
QString(QObject::tr("Installshield hdr file (*.hdr)"))).toUtf8().constData());
cd.start();
cdbox.exec();
}
return true;
}
#endif // WIN32
bool Launcher::MainDialog::setupGameSettings()
{
QString userPath = QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str());
@ -480,7 +327,7 @@ bool Launcher::MainDialog::setupGameSettings()
msgBox.setWindowTitle(tr("Error opening OpenMW configuration file"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(QObject::tr("<br><b>Could not open %0 for reading</b><br><br> \
msgBox.setText(tr("<br><b>Could not open %0 for reading</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
msgBox.exec();
@ -508,7 +355,7 @@ bool Launcher::MainDialog::setupGameSettings()
msgBox.setWindowTitle(tr("Error opening OpenMW configuration file"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(QObject::tr("<br><b>Could not open %0 for reading</b><br><br> \
msgBox.setText(tr("<br><b>Could not open %0 for reading</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
msgBox.exec();
@ -540,72 +387,22 @@ bool Launcher::MainDialog::setupGameSettings()
msgBox.setWindowTitle(tr("Error detecting Morrowind installation"));
msgBox.setIcon(QMessageBox::Warning);
msgBox.setStandardButtons(QMessageBox::Cancel);
msgBox.setText(QObject::tr("<br><b>Could not find the Data Files location</b><br><br> \
The directory containing the data files was not found.<br><br> \
Press \"Browse...\" to specify the location manually.<br>"));
msgBox.setText(tr("<br><b>Could not find the Data Files location</b><br><br> \
The directory containing the data files was not found."));
QAbstractButton *dirSelectButton =
msgBox.addButton(QObject::tr("Browse to &Install..."), QMessageBox::ActionRole);
QAbstractButton *wizardButton =
msgBox.addButton(tr("Run &Installation Wizard..."), QMessageBox::ActionRole);
#ifndef WIN32
QAbstractButton *cdSelectButton =
msgBox.addButton(QObject::tr("Browse to &CD..."), QMessageBox::ActionRole);
#endif
msgBox.exec();
msgBox.exec();
QString selectedFile;
if (msgBox.clickedButton() == dirSelectButton) {
selectedFile = QFileDialog::getOpenFileName(
NULL,
QObject::tr("Select master file"),
QDir::currentPath(),
QString(tr("Morrowind master file (*.esm)")));
}
#ifndef WIN32
else if(msgBox.clickedButton() == cdSelectButton) {
UnshieldThread cd;
{
TextSlotMsgBox cdbox;
cdbox.setStandardButtons(QMessageBox::Cancel);
QObject::connect(&cd,SIGNAL(signalGUI(const QString&)), &cdbox, SLOT(setTextSlot(const QString&)));
QObject::connect(&cd,SIGNAL(close()), &cdbox, SLOT(reject()));
cd.SetMorrowindPath(
QFileDialog::getOpenFileName(
NULL,
QObject::tr("Select data1.hdr from Morrowind Installation CD"),
QDir::currentPath(),
QString(tr("Installshield hdr file (*.hdr)"))).toUtf8().constData());
cd.SetOutputPath(
QFileDialog::getExistingDirectory(
NULL,
QObject::tr("Select where to extract files to"),
QDir::currentPath(),
QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks).toUtf8().constData());
cd.start();
cdbox.exec();
if (msgBox.clickedButton() == wizardButton)
{
if (!mWizardInvoker->startProcess(QLatin1String("openmw-wizard"), false)) {
return false;
} else {
return true;
}
while(expansions(cd));
selectedFile = QString::fromUtf8(cd.GetMWEsmPath().c_str());
}
#endif // WIN32
if (selectedFile.isEmpty())
return false; // Cancel was clicked;
QFileInfo info(selectedFile);
// Add the new dir to the settings file and to the data dir container
mGameSettings.setMultiValue(QString("data"), info.absolutePath());
mGameSettings.addDataDir(info.absolutePath());
}
return true;
@ -626,7 +423,7 @@ bool Launcher::MainDialog::setupGraphicsSettings()
msgBox.setWindowTitle(tr("Error reading OpenMW configuration file"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(QObject::tr("<br><b>Could not find settings-default.cfg</b><br><br> \
msgBox.setText(tr("<br><b>Could not find settings-default.cfg</b><br><br> \
The problem may be due to an incomplete installation of OpenMW.<br> \
Reinstalling OpenMW may resolve the problem."));
msgBox.exec();
@ -648,7 +445,7 @@ bool Launcher::MainDialog::setupGraphicsSettings()
msgBox.setWindowTitle(tr("Error opening OpenMW configuration file"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(QObject::tr("<br><b>Could not open %0 for reading</b><br><br> \
msgBox.setText(tr("<br><b>Could not open %0 for reading</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
msgBox.exec();
@ -699,8 +496,9 @@ bool Launcher::MainDialog::writeSettings()
{
// Now write all config files
saveSettings();
mGraphicsPage->saveSettings();
mDataFilesPage->saveSettings();
mGraphicsPage->saveSettings();
mSettingsPage->saveSettings();
QString userPath = QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str());
QDir dir(userPath);
@ -714,8 +512,8 @@ bool Launcher::MainDialog::writeSettings()
msgBox.setText(tr("<br><b>Could not create %0</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(userPath));
msgBox.exec();
return false;
msgBox.exec();
return false;
}
}
@ -731,8 +529,8 @@ bool Launcher::MainDialog::writeSettings()
msgBox.setText(tr("<br><b>Could not open or create %0 for writing</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
msgBox.exec();
return false;
msgBox.exec();
return false;
}
QTextStream stream(&file);
@ -753,8 +551,8 @@ bool Launcher::MainDialog::writeSettings()
msgBox.setText(tr("<br><b>Could not open or create %0 for writing</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
msgBox.exec();
return false;
msgBox.exec();
return false;
}
stream.setDevice(&file);
@ -775,8 +573,8 @@ bool Launcher::MainDialog::writeSettings()
msgBox.setText(tr("<br><b>Could not open or create %0 for writing</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
msgBox.exec();
return false;
msgBox.exec();
return false;
}
stream.setDevice(&file);
@ -794,122 +592,41 @@ void Launcher::MainDialog::closeEvent(QCloseEvent *event)
event->accept();
}
void Launcher::MainDialog::play()
void Launcher::MainDialog::wizardStarted()
{
if (!writeSettings()) {
qApp->quit();
return;
}
if(!mGameSettings.hasMaster()) {
QMessageBox msgBox;
msgBox.setWindowTitle(tr("No game file selected"));
msgBox.setIcon(QMessageBox::Warning);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>You do not have a game file selected.</b><br><br> \
OpenMW will not start without a game file selected.<br>"));
msgBox.exec();
return;
}
// Launch the game detached
startProgram(QString("openmw"), true);
qApp->quit();
hide();
}
bool Launcher::MainDialog::startProgram(const QString &name, const QStringList &arguments, bool detached)
void Launcher::MainDialog::wizardFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
QString path = name;
#ifdef Q_OS_WIN
path.append(QString(".exe"));
#elif defined(Q_OS_MAC)
QDir dir(QCoreApplication::applicationDirPath());
path = dir.absoluteFilePath(name);
#else
path.prepend(QString("./"));
#endif
QFile file(path);
if (exitCode != 0 || exitStatus == QProcess::CrashExit)
return qApp->quit();
QProcess process;
QFileInfo info(file);
// HACK: Ensure the pages are created, else segfault
setup();
if (!file.exists()) {
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error starting executable"));
msgBox.setIcon(QMessageBox::Warning);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not find %1</b><br><br> \
The application is not found.<br> \
Please make sure OpenMW is installed correctly and try again.<br>").arg(info.fileName()));
msgBox.exec();
if (reloadSettings())
show();
}
return false;
}
void Launcher::MainDialog::play()
{
if (!writeSettings())
return qApp->quit();
if (!info.isExecutable()) {
if (!mGameSettings.hasMaster()) {
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error starting executable"));
msgBox.setWindowTitle(tr("No game file selected"));
msgBox.setIcon(QMessageBox::Warning);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not start %1</b><br><br> \
The application is not executable.<br> \
Please make sure you have the right permissions and try again.<br>").arg(info.fileName()));
msgBox.exec();
return false;
}
// Start the executable
if (detached) {
if (!process.startDetached(path, arguments)) {
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error starting executable"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not start %1</b><br><br> \
An error occurred while starting %1.<br><br> \
Press \"Show Details...\" for more information.<br>").arg(info.fileName()));
msgBox.setDetailedText(process.errorString());
msgBox.exec();
return false;
}
} else {
process.start(path, arguments);
if (!process.waitForFinished()) {
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error starting executable"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not start %1</b><br><br> \
An error occurred while starting %1.<br><br> \
Press \"Show Details...\" for more information.<br>").arg(info.fileName()));
msgBox.setDetailedText(process.errorString());
msgBox.exec();
return false;
}
if (process.exitCode() != 0 || process.exitStatus() == QProcess::CrashExit) {
QString error(process.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("<br><b>Executable %1 returned an error</b><br><br> \
An error occurred while running %1.<br><br> \
Press \"Show Details...\" for more information.<br>").arg(info.fileName()));
msgBox.setDetailedText(error);
msgBox.exec();
return false;
}
msgBox.setText(tr("<br><b>You do not have a game file selected.</b><br><br> \
OpenMW will not start without a game file selected.<br>"));
msgBox.exec();
return;
}
return true;
// Launch the game detached
if (mGameInvoker->startProcess(QLatin1String("openmw"), true))
return qApp->quit();
}

@ -2,16 +2,26 @@
#define MAINDIALOG_H
#include <QMainWindow>
#include <QProcess>
#ifndef Q_MOC_RUN
#include <components/files/configurationmanager.hpp>
#endif
#include "settings/gamesettings.hpp"
#include <components/process/processinvoker.hpp>
#include <components/config/gamesettings.hpp>
#include <components/config/launchersettings.hpp>
#include "settings/graphicssettings.hpp"
#include "settings/launchersettings.hpp"
#include "ui_mainwindow.h"
class QListWidgetItem;
class QStackedWidget;
class QStringList;
class QStringListModel;
class QString;
namespace Launcher
{
@ -19,6 +29,7 @@ namespace Launcher
class GraphicsPage;
class DataFilesPage;
class UnshieldThread;
class SettingsPage;
#ifndef WIN32
bool expansions(Launcher::UnshieldThread& cd);
@ -30,13 +41,22 @@ namespace Launcher
public:
explicit MainDialog(QWidget *parent = 0);
~MainDialog();
bool setup();
bool showFirstRunDialog();
bool reloadSettings();
bool writeSettings();
public slots:
void changePage(QListWidgetItem *current, QListWidgetItem *previous);
void play();
private slots:
void wizardStarted();
void wizardFinished(int exitCode, QProcess::ExitStatus exitStatus);
private:
void createIcons();
void createPages();
@ -47,7 +67,6 @@ namespace Launcher
void loadSettings();
void saveSettings();
bool writeSettings();
inline bool startProgram(const QString &name, bool detached = false) { return startProgram(name, QStringList(), detached); }
bool startProgram(const QString &name, const QStringList &arguments, bool detached = false);
@ -57,12 +76,16 @@ namespace Launcher
PlayPage *mPlayPage;
GraphicsPage *mGraphicsPage;
DataFilesPage *mDataFilesPage;
SettingsPage *mSettingsPage;
Process::ProcessInvoker *mGameInvoker;
Process::ProcessInvoker *mWizardInvoker;
Files::ConfigurationManager mCfgMgr;
GameSettings mGameSettings;
Config::GameSettings mGameSettings;
GraphicsSettings mGraphicsSettings;
LauncherSettings mLauncherSettings;
Config::LauncherSettings mLauncherSettings;
};
}

@ -1,11 +1,11 @@
#ifndef GRAPHICSSETTINGS_HPP
#define GRAPHICSSETTINGS_HPP
#include "settingsbase.hpp"
#include <components/config/settingsbase.hpp>
namespace Launcher
{
class GraphicsSettings : public SettingsBase<QMap<QString, QString> >
class GraphicsSettings : public Config::SettingsBase<QMap<QString, QString> >
{
public:
GraphicsSettings();

@ -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

@ -16,15 +16,15 @@ Launcher::TextInputDialog::TextInputDialog(const QString& title, const QString &
mButtonBox->addButton(QDialogButtonBox::Cancel);
mButtonBox->button(QDialogButtonBox::Ok)->setEnabled (false);
QLabel *label = new QLabel(this);
label->setText(text);
// Line edit
QValidator *validator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9_]*$"), this); // Alpha-numeric + underscore
mLineEdit = new DialogLineEdit(this);
mLineEdit = new LineEdit(this);
mLineEdit->setValidator(validator);
mLineEdit->setCompleter(0);
QLabel *label = new QLabel(this);
label->setText(text);
QVBoxLayout *dialogLayout = new QVBoxLayout(this);
dialogLayout->addWidget(label);
dialogLayout->addWidget(mLineEdit);
@ -41,8 +41,10 @@ Launcher::TextInputDialog::TextInputDialog(const QString& title, const QString &
connect(mButtonBox, SIGNAL(accepted()), this, SLOT(accept()));
connect(mButtonBox, SIGNAL(rejected()), this, SLOT(reject()));
connect(mLineEdit, SIGNAL(textChanged(QString)), this, SLOT(slotUpdateOkButton(QString)));
}
Launcher::TextInputDialog::~TextInputDialog()
{
}
int Launcher::TextInputDialog::exec()
@ -52,36 +54,18 @@ int Launcher::TextInputDialog::exec()
return QDialog::exec();
}
QString Launcher::TextInputDialog::getText() const
void Launcher::TextInputDialog::setOkButtonEnabled(bool enabled)
{
return mLineEdit->text();
}
QPushButton *okButton = mButtonBox->button(QDialogButtonBox::Ok);
okButton->setEnabled(enabled);
void Launcher::TextInputDialog::slotUpdateOkButton(QString text)
{
bool enabled = !(text.isEmpty());
mButtonBox->button(QDialogButtonBox::Ok)->setEnabled(enabled);
QPalette *palette = new QPalette();
palette->setColor(QPalette::Text, Qt::red);
if (enabled)
if (enabled) {
mLineEdit->setPalette(QApplication::palette());
else
{
} else {
// Existing profile name, make the text red
QPalette *palette = new QPalette();
palette->setColor(QPalette::Text,Qt::red);
mLineEdit->setPalette(*palette);
}
}
Launcher::TextInputDialog::DialogLineEdit::DialogLineEdit (QWidget *parent) :
LineEdit (parent)
{
int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
setObjectName(QString("LineEdit"));
setStyleSheet(QString("LineEdit { padding-right: %1px; } ").arg(mClearButton->sizeHint().width() + frameWidth + 1));
QSize msz = minimumSizeHint();
setMinimumSize(qMax(msz.width(), mClearButton->sizeHint().height() + frameWidth * 2 + 2),
qMax(msz.height(), mClearButton->sizeHint().height() + frameWidth * 2 + 2));
}

@ -13,26 +13,20 @@ namespace Launcher
{
Q_OBJECT
class DialogLineEdit : public LineEdit
{
public:
explicit DialogLineEdit (QWidget *parent = 0);
};
DialogLineEdit *mLineEdit;
QDialogButtonBox *mButtonBox;
public:
explicit TextInputDialog(const QString& title, const QString &text, QWidget *parent = 0);
~TextInputDialog () {}
~TextInputDialog ();
QString getText() const;
inline LineEdit *lineEdit() { return mLineEdit; }
void setOkButtonEnabled(bool enabled);
int exec();
private slots:
void slotUpdateOkButton(QString text);
private:
QDialogButtonBox *mButtonBox;
LineEdit *mLineEdit;
};
}

@ -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 &quot;Show Details...&quot; 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

@ -113,11 +113,20 @@ set (ESM_UI ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui
find_package(Qt4 COMPONENTS QtCore QtGui)
if(QT_QTGUI_LIBRARY AND QT_QTCORE_LIBRARY)
add_component_qt_dir (contentselector
add_component_qt_dir (contentselector
model/modelitem model/esmfile
model/naturalsort model/contentmodel
view/combobox view/contentselector
)
)
add_component_qt_dir (config
gamesettings
launchersettings
settingsbase
)
add_component_qt_dir (process
processinvoker
)
include(${QT_USE_FILE})
QT4_WRAP_UI(ESM_UI_HDR ${ESM_UI})

@ -27,20 +27,17 @@ namespace boost
#endif /* (BOOST_VERSION <= 104600) */
Launcher::GameSettings::GameSettings(Files::ConfigurationManager &cfg)
Config::GameSettings::GameSettings(Files::ConfigurationManager &cfg)
: mCfgMgr(cfg)
{
}
Launcher::GameSettings::~GameSettings()
Config::GameSettings::~GameSettings()
{
}
void Launcher::GameSettings::validatePaths()
void Config::GameSettings::validatePaths()
{
if (mSettings.isEmpty() || !mDataDirs.isEmpty())
return; // Don't re-validate paths if they are already parsed
QStringList paths = mSettings.values(QString("data"));
Files::PathContainer dataDirs;
@ -84,24 +81,24 @@ void Launcher::GameSettings::validatePaths()
}
}
QStringList Launcher::GameSettings::values(const QString &key, const QStringList &defaultValues)
QStringList Config::GameSettings::values(const QString &key, const QStringList &defaultValues)
{
if (!mSettings.values(key).isEmpty())
return mSettings.values(key);
return defaultValues;
}
bool Launcher::GameSettings::readFile(QTextStream &stream)
bool Config::GameSettings::readFile(QTextStream &stream)
{
return readFile(stream, mSettings);
}
bool Launcher::GameSettings::readUserFile(QTextStream &stream)
bool Config::GameSettings::readUserFile(QTextStream &stream)
{
return readFile(stream, mUserSettings);
}
bool Launcher::GameSettings::readFile(QTextStream &stream, QMap<QString, QString> &settings)
bool Config::GameSettings::readFile(QTextStream &stream, QMap<QString, QString> &settings)
{
QMap<QString, QString> cache;
QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$");
@ -143,8 +140,7 @@ bool Launcher::GameSettings::readFile(QTextStream &stream, QMap<QString, QString
return true;
}
bool Launcher::GameSettings::writeFile(QTextStream &stream)
bool Config::GameSettings::writeFile(QTextStream &stream)
{
// Iterate in reverse order to preserve insertion order
QMapIterator<QString, QString> i(mUserSettings);
@ -183,7 +179,7 @@ bool Launcher::GameSettings::writeFile(QTextStream &stream)
return true;
}
bool Launcher::GameSettings::hasMaster()
bool Config::GameSettings::hasMaster()
{
bool result = false;
QStringList content = mSettings.values(QString("content"));

@ -14,7 +14,7 @@ namespace Files
struct ConfigurationManager;
}
namespace Launcher
namespace Config
{
class GameSettings
{
@ -52,6 +52,8 @@ namespace Launcher
}
inline QStringList getDataDirs() { return mDataDirs; }
inline void removeDataDir(const QString &dir) { if(!dir.isEmpty()) mDataDirs.removeAll(dir); }
inline void addDataDir(const QString &dir) { if(!dir.isEmpty()) mDataDirs.append(dir); }
inline QString getDataLocal() {return mDataLocal; }

@ -7,15 +7,15 @@
#include <QDebug>
Launcher::LauncherSettings::LauncherSettings()
Config::LauncherSettings::LauncherSettings()
{
}
Launcher::LauncherSettings::~LauncherSettings()
Config::LauncherSettings::~LauncherSettings()
{
}
QStringList Launcher::LauncherSettings::values(const QString &key, Qt::MatchFlags flags)
QStringList Config::LauncherSettings::values(const QString &key, Qt::MatchFlags flags)
{
QMap<QString, QString> settings = SettingsBase::getSettings();
@ -36,19 +36,25 @@ QStringList Launcher::LauncherSettings::values(const QString &key, Qt::MatchFlag
return result;
}
QStringList Launcher::LauncherSettings::subKeys(const QString &key)
QStringList Config::LauncherSettings::subKeys(const QString &key)
{
QMap<QString, QString> settings = SettingsBase::getSettings();
QStringList keys = settings.uniqueKeys();
qDebug() << keys;
QRegExp keyRe("(.+)/");
QStringList result;
foreach (const QString &currentKey, keys) {
if (keyRe.indexIn(currentKey) != -1) {
if (keyRe.indexIn(currentKey) != -1)
{
QString prefixedKey = keyRe.cap(1);
if(prefixedKey.startsWith(key)) {
if(prefixedKey.startsWith(key))
{
QString subKey = prefixedKey.remove(key);
if (!subKey.isEmpty())
result.append(subKey);
@ -60,7 +66,7 @@ QStringList Launcher::LauncherSettings::subKeys(const QString &key)
return result;
}
bool Launcher::LauncherSettings::writeFile(QTextStream &stream)
bool Config::LauncherSettings::writeFile(QTextStream &stream)
{
QString sectionPrefix;
QRegExp sectionRe("([^/]+)/(.+)$");

@ -3,7 +3,7 @@
#include "settingsbase.hpp"
namespace Launcher
namespace Config
{
class LauncherSettings : public SettingsBase<QMap<QString, QString> >
{

@ -7,7 +7,7 @@
#include <QRegExp>
#include <QMap>
namespace Launcher
namespace Config
{
template <class Map>
class SettingsBase

@ -94,6 +94,8 @@ namespace OgreInit
Ogre::Root* OgreInit::init(const std::string &logPath)
{
if (mRoot)
throw std::runtime_error("OgreInit was already initialised");
#ifndef ANDROID
// Set up logging first

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

@ -2,7 +2,10 @@
Name=Tango
Comment=Tango Theme
Inherits=default
Directories=16x16
Directories=16x16,48x48
[16x16]
Size=16
Size=16
[48x48]
Size=48

@ -9,13 +9,14 @@
</qresource>
<qresource prefix="icons/tango">
<file alias="index.theme">icons/tango/index.theme</file>
<file alias="video-display.png">icons/tango/video-display.png</file>
<file alias="16x16/document-new.png">icons/tango/document-new.png</file>
<file alias="16x16/edit-copy.png">icons/tango/edit-copy.png</file>
<file alias="16x16/edit-delete.png">icons/tango/edit-delete.png</file>
<file alias="16x16/go-bottom.png">icons/tango/go-bottom.png</file>
<file alias="16x16/go-down.png">icons/tango/go-down.png</file>
<file alias="16x16/go-top.png">icons/tango/go-top.png</file>
<file alias="16x16/go-up.png">icons/tango/go-up.png</file>
<file alias="48x48/video-display.png">icons/tango/48x48/video-display.png</file>
<file alias="48x48/preferences-system.png">icons/tango/48x48/preferences-system.png</file>
<file alias="16x16/document-new.png">icons/tango/16x16/document-new.png</file>
<file alias="16x16/edit-copy.png">icons/tango/16x16/edit-copy.png</file>
<file alias="16x16/edit-delete.png">icons/tango/16x16/edit-delete.png</file>
<file alias="16x16/go-bottom.png">icons/tango/16x16/go-bottom.png</file>
<file alias="16x16/go-down.png">icons/tango/16x16/go-down.png</file>
<file alias="16x16/go-top.png">icons/tango/16x16/go-top.png</file>
<file alias="16x16/go-up.png">icons/tango/16x16/go-up.png</file>
</qresource>
</RCC>

@ -6,13 +6,13 @@
<rect>
<x>0</x>
<y>0</y>
<width>575</width>
<height>535</height>
<width>635</width>
<height>575</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>575</width>
<width>635</width>
<height>535</height>
</size>
</property>

@ -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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The language of the original Morrowind installation files (used for the character encoding)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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 &amp;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 &amp;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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Select which official Morrowind expansions should be installed. For best results, it is recommended to have both expansions installed.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:bold;&quot;&gt;Note:&lt;/span&gt; It is possible to install expansions later by re-running this Wizard.&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;OpenMW needs to import settings from the Morrowind configuration file in order to function properly.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:bold;&quot;&gt;Note:&lt;/span&gt; It is possible to import settings later by re-running this Wizard.&lt;/p&gt;&lt;p/&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/tango/48x48/folder.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/tango/48x48/preferences-desktop-locale.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/tango/48x48/system-installer.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/tango/48x48/folder.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

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>
Loading…
Cancel
Save