1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-01-25 00:53:52 +00:00

Merge branch 'master' into pathgrid-edit

This commit is contained in:
cc9cii 2014-11-23 06:43:16 +11:00
commit 6e334064d3
90 changed files with 5084 additions and 1621 deletions

View file

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

View file

@ -51,7 +51,7 @@ bool parseOptions (int argc, char** argv, Arguments &info)
("help,h", "print help message.") ("help,h", "print help message.")
("version,v", "print version information and quit.") ("version,v", "print version information and quit.")
("long,l", "Include extra information in archive listing.") ("long,l", "Include extra information in archive listing.")
("full-path,f", "Create diretory hierarchy on file extraction " ("full-path,f", "Create directory hierarchy on file extraction "
"(always true for extractall).") "(always true for extractall).")
; ;

View file

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

View file

@ -1,5 +1,7 @@
#include "datafilespage.hpp" #include "datafilespage.hpp"
#include <QDebug>
#include <QPushButton> #include <QPushButton>
#include <QMessageBox> #include <QMessageBox>
#include <QCheckBox> #include <QCheckBox>
@ -9,18 +11,16 @@
#include <components/files/configurationmanager.hpp> #include <components/files/configurationmanager.hpp>
#include <components/contentselector/model/esmfile.hpp> #include <components/contentselector/model/esmfile.hpp>
#include <components/contentselector/model/naturalsort.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/textinputdialog.hpp"
#include "utils/profilescombobox.hpp" #include "utils/profilescombobox.hpp"
#include "settings/gamesettings.hpp" Launcher::DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, Config::GameSettings &gameSettings, Config::LauncherSettings &launcherSettings, QWidget *parent)
#include "settings/launchersettings.hpp"
#include "components/contentselector/view/contentselector.hpp"
Launcher::DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gameSettings, LauncherSettings &launcherSettings, QWidget *parent)
: mCfgMgr(cfg) : mCfgMgr(cfg)
, mGameSettings(gameSettings) , mGameSettings(gameSettings)
, mLauncherSettings(launcherSettings) , mLauncherSettings(launcherSettings)
@ -30,20 +30,71 @@ Launcher::DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSet
setObjectName ("DataFilesPage"); setObjectName ("DataFilesPage");
mSelector = new ContentSelectorView::ContentSelector (ui.contentSelectorWidget); 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(); 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(); 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); paths.insert (0, mDataLocal);
PathIterator pathIterator (paths); PathIterator pathIterator (paths);
QString profileName = ui.profilesComboBox->currentText(); QStringList profiles = mLauncherSettings.subKeys(QString("Profiles/"));
QString currentProfile = mLauncherSettings.getSettings().value("Profiles/currentprofile");
QStringList files = mLauncherSettings.values(QString("Profiles/") + profileName, Qt::MatchExactly); qDebug() << "current profile is: " << currentProfile;
foreach (const QString &item, profiles)
addProfile (item, false);
// 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; QStringList filepaths;
foreach (const QString &file, files) foreach (const QString &file, files)
@ -55,6 +106,8 @@ void Launcher::DataFilesPage::loadSettings()
} }
mSelector->setProfileContent (filepaths); mSelector->setProfileContent (filepaths);
return true;
} }
void Launcher::DataFilesPage::saveSettings(const QString &profile) 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()); mLauncherSettings.setValue(QString("Profiles/currentprofile"), ui.profilesComboBox->currentText());
foreach(const ContentSelectorModel::EsmFile *item, items) { 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()); 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) void Launcher::DataFilesPage::removeProfile(const QString &profile)
{ {
mLauncherSettings.remove(QString("Profiles/") + profile); mLauncherSettings.remove(QString("Profiles/") + profile);
@ -140,6 +166,9 @@ void Launcher::DataFilesPage::setProfile (const QString &previous, const QString
if (previous == current) if (previous == current)
return; return;
if (previous.isEmpty())
return;
if (!previous.isEmpty() && savePrevious) if (!previous.isEmpty() && savePrevious)
saveSettings (previous); saveSettings (previous);
@ -180,53 +209,15 @@ void Launcher::DataFilesPage::slotProfileChanged(int index)
setProfile (index, true); 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() void Launcher::DataFilesPage::on_newProfileAction_triggered()
{ {
TextInputDialog newDialog (tr("New Profile"), tr("Profile name:"), this); if (!mProfileDialog->exec() == QDialog::Accepted)
if (newDialog.exec() != QDialog::Accepted)
return; return;
QString profile = newDialog.getText(); QString profile = mProfileDialog->lineEdit()->text();
if (profile.isEmpty()) if (profile.isEmpty())
return; return;
saveSettings(); saveSettings();
@ -277,6 +268,19 @@ void Launcher::DataFilesPage::on_deleteProfileAction_triggered()
checkForDefaultProfile(); 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() void Launcher::DataFilesPage::checkForDefaultProfile()
{ {
//don't allow deleting "Default" profile //don't allow deleting "Default" profile

View file

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

View file

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

View file

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

View file

@ -5,29 +5,24 @@
#include <QLabel> #include <QLabel>
#include <QDate> #include <QDate>
#include <QTime> #include <QTime>
#include <QMessageBox>
#include <QPushButton> #include <QPushButton>
#include <QFontDatabase> #include <QFontDatabase>
#include <QInputDialog> #include <QInputDialog>
#include <QFileDialog> #include <QFileDialog>
#include <QCloseEvent> #include <QCloseEvent>
#include <QTextCodec> #include <QTextCodec>
#include <QProcess>
#include <QFile> #include <QFile>
#include <QDir> #include <QDir>
#include <QDebug> #include <QDebug>
#ifndef WIN32
#include "unshieldthread.hpp"
#endif
#include "textslotmsgbox.hpp"
#include "utils/checkablemessagebox.hpp"
#include "playpage.hpp" #include "playpage.hpp"
#include "graphicspage.hpp" #include "graphicspage.hpp"
#include "datafilespage.hpp" #include "datafilespage.hpp"
#include "settingspage.hpp"
using namespace Process;
Launcher::MainDialog::MainDialog(QWidget *parent) Launcher::MainDialog::MainDialog(QWidget *parent)
: mGameSettings(mCfgMgr), QMainWindow (parent) : mGameSettings(mCfgMgr), QMainWindow (parent)
@ -53,6 +48,15 @@ Launcher::MainDialog::MainDialog(QWidget *parent)
setupUi(this); 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->setViewMode(QListView::IconMode);
iconWidget->setWrapping(false); iconWidget->setWrapping(false);
iconWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // Just to be sure iconWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // Just to be sure
@ -79,13 +83,13 @@ Launcher::MainDialog::MainDialog(QWidget *parent)
if (!revision.isEmpty() && !tag.isEmpty()) if (!revision.isEmpty() && !tag.isEmpty())
{ {
if (revision == tag) { if (revision == tag) {
versionLabel->setText(tr("OpenMW %0 release").arg(OPENMW_VERSION)); versionLabel->setText(tr("OpenMW %1 release").arg(OPENMW_VERSION));
} else { } 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 // 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), QLatin1String("MMM d yyyy")).toString(Qt::SystemLocaleLongDate),
QLocale(QLocale::C).toTime(QString(__TIME__).simplified(), QLocale(QLocale::C).toTime(QString(__TIME__).simplified(),
QLatin1String("hh:mm:ss")).toString(Qt::SystemLocaleShortDate))); QLatin1String("hh:mm:ss")).toString(Qt::SystemLocaleShortDate)));
@ -94,32 +98,41 @@ Launcher::MainDialog::MainDialog(QWidget *parent)
createIcons(); createIcons();
} }
Launcher::MainDialog::~MainDialog()
{
delete mGameInvoker;
delete mWizardInvoker;
}
void Launcher::MainDialog::createIcons() void Launcher::MainDialog::createIcons()
{ {
if (!QIcon::hasThemeIcon("document-new")) if (!QIcon::hasThemeIcon("document-new"))
QIcon::setThemeName("tango"); 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); QListWidgetItem *playButton = new QListWidgetItem(iconWidget);
playButton->setIcon(QIcon(":/images/openmw.png")); playButton->setIcon(QIcon(":/images/openmw.png"));
playButton->setText(tr("Play")); playButton->setText(tr("Play"));
playButton->setTextAlignment(Qt::AlignCenter); playButton->setTextAlignment(Qt::AlignCenter);
playButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); 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); QListWidgetItem *dataFilesButton = new QListWidgetItem(iconWidget);
dataFilesButton->setIcon(QIcon(":/images/openmw-plugin.png")); dataFilesButton->setIcon(QIcon(":/images/openmw-plugin.png"));
dataFilesButton->setText(tr("Data Files")); dataFilesButton->setText(tr("Data Files"));
dataFilesButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom); dataFilesButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom);
dataFilesButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); 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, connect(iconWidget,
SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)),
this, SLOT(changePage(QListWidgetItem*,QListWidgetItem*))); this, SLOT(changePage(QListWidgetItem*,QListWidgetItem*)));
@ -129,8 +142,9 @@ void Launcher::MainDialog::createIcons()
void Launcher::MainDialog::createPages() void Launcher::MainDialog::createPages()
{ {
mPlayPage = new PlayPage(this); mPlayPage = new PlayPage(this);
mGraphicsPage = new GraphicsPage(mCfgMgr, mGraphicsSettings, this);
mDataFilesPage = new DataFilesPage(mCfgMgr, mGameSettings, mLauncherSettings, 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 // Set the combobox of the play page to imitate the combobox on the datafilespage
mPlayPage->setProfilesModel(mDataFilesPage->profilesModel()); mPlayPage->setProfilesModel(mDataFilesPage->profilesModel());
@ -138,8 +152,9 @@ void Launcher::MainDialog::createPages()
// Add the pages to the stacked widget // Add the pages to the stacked widget
pagesWidget->addWidget(mPlayPage); pagesWidget->addWidget(mPlayPage);
pagesWidget->addWidget(mGraphicsPage);
pagesWidget->addWidget(mDataFilesPage); pagesWidget->addWidget(mDataFilesPage);
pagesWidget->addWidget(mGraphicsPage);
pagesWidget->addWidget(mSettingsPage);
// Select the first page // Select the first page
iconWidget->setCurrentItem(iconWidget->item(0), QItemSelectionModel::Select); iconWidget->setCurrentItem(iconWidget->item(0), QItemSelectionModel::Select);
@ -153,153 +168,63 @@ void Launcher::MainDialog::createPages()
bool Launcher::MainDialog::showFirstRunDialog() bool Launcher::MainDialog::showFirstRunDialog()
{ {
QStringList iniPaths; if (!setupLauncherSettings())
return false;
foreach (const QString &path, mGameSettings.getDataDirs()) { if (mLauncherSettings.value(QString("General/firstrun"), QString("true")) == QLatin1String("true"))
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")));
}
}
// Ask the user where the Morrowind.ini is
if (iniPaths.empty()) {
QMessageBox msgBox; QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error detecting Morrowind configuration")); msgBox.setWindowTitle(tr("First run"));
msgBox.setIcon(QMessageBox::Warning); msgBox.setIcon(QMessageBox::Question);
msgBox.setStandardButtons(QMessageBox::Cancel); msgBox.setStandardButtons(QMessageBox::NoButton);
msgBox.setText(QObject::tr("<br><b>Could not find Morrowind.ini</b><br><br> \ msgBox.setText(tr("<html><head/><body><p><b>Welcome to OpenMW!</b></p> \
OpenMW needs to import settings from this file.<br><br> \ <p>It is recommended to run the Installation Wizard.</p> \
Press \"Browse...\" to specify the location manually.<br>")); <p>The Wizard will let you select an existing Morrowind installation, \
or install Morrowind for OpenMW to use.</p></body></html>"));
QAbstractButton *dirSelectButton = QAbstractButton *wizardButton =
msgBox.addButton(QObject::tr("B&rowse..."), QMessageBox::ActionRole); msgBox.addButton(tr("Run &Installation Wizard"), QMessageBox::AcceptRole); // ActionRole doesn't work?!
QAbstractButton *skipButton =
msgBox.addButton(tr("Skip"), QMessageBox::RejectRole);
Q_UNUSED(skipButton); // Surpress compiler unused warning
msgBox.exec(); msgBox.exec();
QString iniFile; if (msgBox.clickedButton() == wizardButton)
if (msgBox.clickedButton() == dirSelectButton) { {
iniFile = QFileDialog::getOpenFileName( if (!mWizardInvoker->startProcess(QLatin1String("openmw-wizard"), false)) {
NULL, return false;
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();
if (msgBox.clickedButton() == importerButton) {
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 { } else {
// Cancel was clicked return true;
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);
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();
return false;
}
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);
if (!startProgram(QString("mwiniimport"), arguments, false))
return false;
// Re-read the game settings
if (!setupGameSettings())
return false;
// Add a new profile
if (msgBox.isChecked()) {
mLauncherSettings.setValue(QString("Profiles/currentprofile"), QString("Imported"));
mLauncherSettings.remove(QString("Profiles/Imported/content"));
QStringList contents = mGameSettings.values(QString("content"));
foreach (const QString &content, contents) {
mLauncherSettings.setMultiValue(QString("Profiles/Imported/content"), content);
}
}
} }
return setup();
}
bool Launcher::MainDialog::setup()
{
if (!setupGameSettings())
return false;
if (!setupGraphicsSettings())
return false;
// Now create the pages as they need the settings
createPages();
// Call this so we can exit on Ogre/SDL errors before mainwindow is shown
if (!mGraphicsPage->loadSettings())
return false;
loadSettings();
return true; return true;
} }
bool Launcher::MainDialog::setup() bool Launcher::MainDialog::reloadSettings()
{ {
if (!setupLauncherSettings()) if (!setupLauncherSettings())
return false; return false;
@ -310,21 +235,15 @@ bool Launcher::MainDialog::setup()
if (!setupGraphicsSettings()) if (!setupGraphicsSettings())
return false; return false;
// Check if we need to show the importer if (!mSettingsPage->loadSettings())
if (mLauncherSettings.value(QString("General/firstrun"), QString("true")) == QLatin1String("true")) return false;
{
if (!showFirstRunDialog())
return false;
}
// Now create the pages as they need the settings if (!mDataFilesPage->loadSettings())
createPages(); return false;
// Call this so we can exit on Ogre/SDL errors before mainwindow is shown
if (!mGraphicsPage->loadSettings()) if (!mGraphicsPage->loadSettings())
return false; return false;
loadSettings();
return true; return true;
} }
@ -334,24 +253,24 @@ void Launcher::MainDialog::changePage(QListWidgetItem *current, QListWidgetItem
current = previous; current = previous;
int currentIndex = iconWidget->row(current); int currentIndex = iconWidget->row(current);
int previousIndex = iconWidget->row(previous); // int previousIndex = iconWidget->row(previous);
pagesWidget->setCurrentIndex(currentIndex); pagesWidget->setCurrentIndex(currentIndex);
DataFilesPage *previousPage = dynamic_cast<DataFilesPage *>(pagesWidget->widget(previousIndex)); // DataFilesPage *previousPage = dynamic_cast<DataFilesPage *>(pagesWidget->widget(previousIndex));
DataFilesPage *currentPage = dynamic_cast<DataFilesPage *>(pagesWidget->widget(currentIndex)); // DataFilesPage *currentPage = dynamic_cast<DataFilesPage *>(pagesWidget->widget(currentIndex));
//special call to update/save data files page list view when it's displayed/hidden. // //special call to update/save data files page list view when it's displayed/hidden.
if (previousPage) // if (previousPage)
{ // {
if (previousPage->objectName() == "DataFilesPage") // if (previousPage->objectName() == "DataFilesPage")
previousPage->saveSettings(); // previousPage->saveSettings();
} // }
else if (currentPage) // else if (currentPage)
{ // {
if (currentPage->objectName() == "DataFilesPage") // if (currentPage->objectName() == "DataFilesPage")
currentPage->loadSettings(); // currentPage->loadSettings();
} // }
} }
bool Launcher::MainDialog::setupLauncherSettings() bool Launcher::MainDialog::setupLauncherSettings()
@ -373,10 +292,10 @@ bool Launcher::MainDialog::setupLauncherSettings()
msgBox.setWindowTitle(tr("Error opening OpenMW configuration file")); msgBox.setWindowTitle(tr("Error opening OpenMW configuration file"));
msgBox.setIcon(QMessageBox::Critical); msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok); 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 \ Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName())); and try again.<br>").arg(file.fileName()));
msgBox.exec(); msgBox.exec();
return false; return false;
} }
QTextStream stream(&file); QTextStream stream(&file);
@ -390,78 +309,6 @@ bool Launcher::MainDialog::setupLauncherSettings()
return true; 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() bool Launcher::MainDialog::setupGameSettings()
{ {
QString userPath = QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str()); 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.setWindowTitle(tr("Error opening OpenMW configuration file"));
msgBox.setIcon(QMessageBox::Critical); msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok); 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 \ Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName())); and try again.<br>").arg(file.fileName()));
msgBox.exec(); msgBox.exec();
@ -508,7 +355,7 @@ bool Launcher::MainDialog::setupGameSettings()
msgBox.setWindowTitle(tr("Error opening OpenMW configuration file")); msgBox.setWindowTitle(tr("Error opening OpenMW configuration file"));
msgBox.setIcon(QMessageBox::Critical); msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok); 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 \ Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName())); and try again.<br>").arg(file.fileName()));
msgBox.exec(); msgBox.exec();
@ -540,72 +387,22 @@ bool Launcher::MainDialog::setupGameSettings()
msgBox.setWindowTitle(tr("Error detecting Morrowind installation")); msgBox.setWindowTitle(tr("Error detecting Morrowind installation"));
msgBox.setIcon(QMessageBox::Warning); msgBox.setIcon(QMessageBox::Warning);
msgBox.setStandardButtons(QMessageBox::Cancel); msgBox.setStandardButtons(QMessageBox::Cancel);
msgBox.setText(QObject::tr("<br><b>Could not find the Data Files location</b><br><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.<br><br> \ The directory containing the data files was not found."));
Press \"Browse...\" to specify the location manually.<br>"));
QAbstractButton *dirSelectButton = QAbstractButton *wizardButton =
msgBox.addButton(QObject::tr("Browse to &Install..."), QMessageBox::ActionRole); msgBox.addButton(tr("Run &Installation Wizard..."), QMessageBox::ActionRole);
#ifndef WIN32 msgBox.exec();
QAbstractButton *cdSelectButton =
msgBox.addButton(QObject::tr("Browse to &CD..."), QMessageBox::ActionRole);
#endif
if (msgBox.clickedButton() == wizardButton)
msgBox.exec(); {
if (!mWizardInvoker->startProcess(QLatin1String("openmw-wizard"), false)) {
QString selectedFile; return false;
if (msgBox.clickedButton() == dirSelectButton) { } else {
selectedFile = QFileDialog::getOpenFileName( return true;
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();
} }
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; return true;
@ -626,7 +423,7 @@ bool Launcher::MainDialog::setupGraphicsSettings()
msgBox.setWindowTitle(tr("Error reading OpenMW configuration file")); msgBox.setWindowTitle(tr("Error reading OpenMW configuration file"));
msgBox.setIcon(QMessageBox::Critical); msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok); 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> \ The problem may be due to an incomplete installation of OpenMW.<br> \
Reinstalling OpenMW may resolve the problem.")); Reinstalling OpenMW may resolve the problem."));
msgBox.exec(); msgBox.exec();
@ -648,7 +445,7 @@ bool Launcher::MainDialog::setupGraphicsSettings()
msgBox.setWindowTitle(tr("Error opening OpenMW configuration file")); msgBox.setWindowTitle(tr("Error opening OpenMW configuration file"));
msgBox.setIcon(QMessageBox::Critical); msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok); 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 \ Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName())); and try again.<br>").arg(file.fileName()));
msgBox.exec(); msgBox.exec();
@ -699,8 +496,9 @@ bool Launcher::MainDialog::writeSettings()
{ {
// Now write all config files // Now write all config files
saveSettings(); saveSettings();
mGraphicsPage->saveSettings();
mDataFilesPage->saveSettings(); mDataFilesPage->saveSettings();
mGraphicsPage->saveSettings();
mSettingsPage->saveSettings();
QString userPath = QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str()); QString userPath = QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str());
QDir dir(userPath); QDir dir(userPath);
@ -714,8 +512,8 @@ bool Launcher::MainDialog::writeSettings()
msgBox.setText(tr("<br><b>Could not create %0</b><br><br> \ msgBox.setText(tr("<br><b>Could not create %0</b><br><br> \
Please make sure you have the right permissions \ Please make sure you have the right permissions \
and try again.<br>").arg(userPath)); and try again.<br>").arg(userPath));
msgBox.exec(); msgBox.exec();
return false; 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> \ msgBox.setText(tr("<br><b>Could not open or create %0 for writing</b><br><br> \
Please make sure you have the right permissions \ Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName())); and try again.<br>").arg(file.fileName()));
msgBox.exec(); msgBox.exec();
return false; return false;
} }
QTextStream stream(&file); 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> \ msgBox.setText(tr("<br><b>Could not open or create %0 for writing</b><br><br> \
Please make sure you have the right permissions \ Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName())); and try again.<br>").arg(file.fileName()));
msgBox.exec(); msgBox.exec();
return false; return false;
} }
stream.setDevice(&file); 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> \ msgBox.setText(tr("<br><b>Could not open or create %0 for writing</b><br><br> \
Please make sure you have the right permissions \ Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName())); and try again.<br>").arg(file.fileName()));
msgBox.exec(); msgBox.exec();
return false; return false;
} }
stream.setDevice(&file); stream.setDevice(&file);
@ -794,122 +592,41 @@ void Launcher::MainDialog::closeEvent(QCloseEvent *event)
event->accept(); event->accept();
} }
void Launcher::MainDialog::wizardStarted()
{
hide();
}
void Launcher::MainDialog::wizardFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
if (exitCode != 0 || exitStatus == QProcess::CrashExit)
return qApp->quit();
// HACK: Ensure the pages are created, else segfault
setup();
if (reloadSettings())
show();
}
void Launcher::MainDialog::play() void Launcher::MainDialog::play()
{ {
if (!writeSettings()) { if (!writeSettings())
qApp->quit(); return qApp->quit();
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; 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 // Launch the game detached
startProgram(QString("openmw"), true);
qApp->quit(); if (mGameInvoker->startProcess(QLatin1String("openmw"), true))
} return qApp->quit();
bool Launcher::MainDialog::startProgram(const QString &name, const QStringList &arguments, bool detached)
{
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);
QProcess process;
QFileInfo info(file);
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();
return false;
}
if (!info.isExecutable()) {
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error starting executable"));
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;
}
}
return true;
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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));
}

View file

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

View file

@ -16,15 +16,15 @@ Launcher::TextInputDialog::TextInputDialog(const QString& title, const QString &
mButtonBox->addButton(QDialogButtonBox::Cancel); mButtonBox->addButton(QDialogButtonBox::Cancel);
mButtonBox->button(QDialogButtonBox::Ok)->setEnabled (false); mButtonBox->button(QDialogButtonBox::Ok)->setEnabled (false);
QLabel *label = new QLabel(this);
label->setText(text);
// Line edit // Line edit
QValidator *validator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9_]*$"), this); // Alpha-numeric + underscore 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->setValidator(validator);
mLineEdit->setCompleter(0); mLineEdit->setCompleter(0);
QLabel *label = new QLabel(this);
label->setText(text);
QVBoxLayout *dialogLayout = new QVBoxLayout(this); QVBoxLayout *dialogLayout = new QVBoxLayout(this);
dialogLayout->addWidget(label); dialogLayout->addWidget(label);
dialogLayout->addWidget(mLineEdit); 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(accepted()), this, SLOT(accept()));
connect(mButtonBox, SIGNAL(rejected()), this, SLOT(reject())); connect(mButtonBox, SIGNAL(rejected()), this, SLOT(reject()));
connect(mLineEdit, SIGNAL(textChanged(QString)), this, SLOT(slotUpdateOkButton(QString))); }
Launcher::TextInputDialog::~TextInputDialog()
{
} }
int Launcher::TextInputDialog::exec() int Launcher::TextInputDialog::exec()
@ -52,36 +54,18 @@ int Launcher::TextInputDialog::exec()
return QDialog::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) QPalette *palette = new QPalette();
{ palette->setColor(QPalette::Text, Qt::red);
bool enabled = !(text.isEmpty());
mButtonBox->button(QDialogButtonBox::Ok)->setEnabled(enabled);
if (enabled) if (enabled) {
mLineEdit->setPalette(QApplication::palette()); mLineEdit->setPalette(QApplication::palette());
else } else {
{
// Existing profile name, make the text red // Existing profile name, make the text red
QPalette *palette = new QPalette();
palette->setColor(QPalette::Text,Qt::red);
mLineEdit->setPalette(*palette); 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));
}

View file

@ -13,26 +13,20 @@ namespace Launcher
{ {
Q_OBJECT Q_OBJECT
class DialogLineEdit : public LineEdit
{
public:
explicit DialogLineEdit (QWidget *parent = 0);
};
DialogLineEdit *mLineEdit;
QDialogButtonBox *mButtonBox;
public: public:
explicit TextInputDialog(const QString& title, const QString &text, QWidget *parent = 0); 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(); int exec();
private slots: private:
void slotUpdateOkButton(QString text);
QDialogButtonBox *mButtonBox;
LineEdit *mLineEdit;
}; };
} }

View file

@ -382,5 +382,5 @@ void CS::Editor::documentAdded (CSMDoc::Document *document)
void CS::Editor::lastDocumentDeleted() void CS::Editor::lastDocumentDeleted()
{ {
exit (0); QApplication::quit();
} }

View file

@ -60,7 +60,7 @@ int CSMWorld::Data::count (RecordBase::State state, const CollectionBase& collec
CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourcesManager) CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourcesManager)
: mEncoder (encoding), mPathgrids (mCells), mRefs (mCells), : mEncoder (encoding), mPathgrids (mCells), mRefs (mCells),
mResourcesManager (resourcesManager), mReader (0), mDialogue (0) mResourcesManager (resourcesManager), mReader (0), mDialogue (0), mReaderIndex(0)
{ {
mGlobals.addColumn (new StringIdColumn<ESM::Global>); mGlobals.addColumn (new StringIdColumn<ESM::Global>);
mGlobals.addColumn (new RecordStateColumn<ESM::Global>); mGlobals.addColumn (new RecordStateColumn<ESM::Global>);
@ -659,6 +659,7 @@ int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base
mReader = new ESM::ESMReader; mReader = new ESM::ESMReader;
mReader->setEncoder (&mEncoder); mReader->setEncoder (&mEncoder);
mReader->setIndex(mReaderIndex++);
mReader->open (path.string()); mReader->open (path.string());
mBase = base; mBase = base;

View file

@ -98,6 +98,7 @@ namespace CSMWorld
bool mBase; bool mBase;
bool mProject; bool mProject;
std::map<std::string, std::map<ESM::RefNum, std::string> > mRefLoadCache; std::map<std::string, std::map<ESM::RefNum, std::string> > mRefLoadCache;
int mReaderIndex;
std::vector<boost::shared_ptr<ESM::ESMReader> > mReaders; std::vector<boost::shared_ptr<ESM::ESMReader> > mReaders;

View file

@ -464,8 +464,7 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id, const std::strin
{ {
CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance(); CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance();
const std::vector<CSMWorld::UniversalId::Type> referenceables(CSMWorld::UniversalId::listReferenceableTypes()); bool isReferenceable = id.getClass() == CSMWorld::UniversalId::Class_RefRecord;
bool isReferenceable = std::find(referenceables.begin(), referenceables.end(), id.getType()) != referenceables.end();
// User setting to reuse sub views (on a per top level view basis) // User setting to reuse sub views (on a per top level view basis)
bool reuse = bool reuse =
@ -474,10 +473,14 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id, const std::strin
{ {
foreach(SubView *sb, mSubViews) foreach(SubView *sb, mSubViews)
{ {
if((isReferenceable && (CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Referenceable, id.getId()) == CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Referenceable, sb->getUniversalId().getId()))) bool isSubViewReferenceable =
|| (!isReferenceable && (id == sb->getUniversalId()))) sb->getUniversalId().getType() == CSMWorld::UniversalId::Type_Referenceable;
if((isReferenceable && isSubViewReferenceable && id.getId() == sb->getUniversalId().getId())
||
(!isReferenceable && id == sb->getUniversalId()))
{ {
sb->setFocus(Qt::OtherFocusReason); // FIXME: focus not quite working sb->setFocus();
return; return;
} }
} }
@ -497,7 +500,7 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id, const std::strin
} }
SubView *view = NULL; SubView *view = NULL;
if(std::find(referenceables.begin(), referenceables.end(), id.getType()) != referenceables.end()) if(isReferenceable)
{ {
view = mSubViewFactory.makeSubView (CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Referenceable, id.getId()), *mDocument); view = mSubViewFactory.makeSubView (CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Referenceable, id.getId()), *mDocument);
} }

116
apps/wizard/CMakeLists.txt Normal file
View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

219
apps/wizard/inisettings.cpp Normal file
View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

17
apps/wizard/intropage.cpp Normal file
View file

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

26
apps/wizard/intropage.hpp Normal file
View file

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

View file

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

View file

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

48
apps/wizard/main.cpp Normal file
View file

@ -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();
}

457
apps/wizard/mainwizard.cpp Normal file
View file

@ -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));
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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);
}

View file

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

View file

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

View file

@ -240,7 +240,7 @@ namespace Compiler
{ {
extensions.registerFunction ("xbox", 'l', "", opcodeXBox); extensions.registerFunction ("xbox", 'l', "", opcodeXBox);
extensions.registerFunction ("onactivate", 'l', "", opcodeOnActivate); extensions.registerFunction ("onactivate", 'l', "", opcodeOnActivate);
extensions.registerInstruction ("activate", "", opcodeActivate, opcodeActivateExplicit); extensions.registerInstruction ("activate", "x", opcodeActivate, opcodeActivateExplicit);
extensions.registerInstruction ("lock", "/l", opcodeLock, opcodeLockExplicit); extensions.registerInstruction ("lock", "/l", opcodeLock, opcodeLockExplicit);
extensions.registerInstruction ("unlock", "", opcodeUnlock, opcodeUnlockExplicit); extensions.registerInstruction ("unlock", "", opcodeUnlock, opcodeUnlockExplicit);
extensions.registerInstruction ("cast", "SS", opcodeCast, opcodeCastExplicit); extensions.registerInstruction ("cast", "SS", opcodeCast, opcodeCastExplicit);

View file

@ -512,7 +512,8 @@ namespace Compiler
bool Scanner::isWhitespace (char c) bool Scanner::isWhitespace (char c)
{ {
return c==' ' || c=='\t'; return c==' ' || c=='\t'
|| c=='['; ///< \todo disable this when doing more strict compiling
} }
// constructor // constructor

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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();
}
}

View file

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

View file

Before

Width:  |  Height:  |  Size: 477 B

After

Width:  |  Height:  |  Size: 477 B

View file

Before

Width:  |  Height:  |  Size: 498 B

After

Width:  |  Height:  |  Size: 498 B

View file

Before

Width:  |  Height:  |  Size: 793 B

After

Width:  |  Height:  |  Size: 793 B

View file

Before

Width:  |  Height:  |  Size: 663 B

After

Width:  |  Height:  |  Size: 663 B

View file

Before

Width:  |  Height:  |  Size: 683 B

After

Width:  |  Height:  |  Size: 683 B

View file

Before

Width:  |  Height:  |  Size: 636 B

After

Width:  |  Height:  |  Size: 636 B

View file

Before

Width:  |  Height:  |  Size: 652 B

After

Width:  |  Height:  |  Size: 652 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View file

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View file

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

View file

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

View file

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

156
files/ui/settingspage.ui Normal file
View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

12
files/wizard/wizard.qrc Normal file
View file

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