diff --git a/launch.pro b/launch.pro index 502ce84..59f0d32 100644 --- a/launch.pro +++ b/launch.pro @@ -13,9 +13,11 @@ TEMPLATE = app SOURCES += main.cpp\ - mainwindow.cpp + mainwindow.cpp \ + settings.cpp -HEADERS += mainwindow.h +HEADERS += mainwindow.h \ + settings.h FORMS += mainwindow.ui diff --git a/mainwindow.cpp b/mainwindow.cpp index 17ccca3..8328117 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -1,16 +1,79 @@ +#include #include +#include + +#ifdef Q_OS_WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#include +#endif + #include "mainwindow.h" #include "ui_mainwindow.h" ioLaunch::ioLaunch(QWidget *parent) : QMainWindow(parent), - ui(new Ui::ioLaunch), - ioWidth(0), ioHeight(0), ioWedited(false), ioHedited(false) + ui(new Ui::ioLaunch) { ui->setupUi(this); - resOption = ""; - screenOption = ""; + // Calculate ioquake3 home path. +#ifdef Q_OS_WIN32 + wchar_t path[MAX_PATH]; + + if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, path))) + { + homePath = QString::fromWCharArray(path) + "/Quake3"; + } +#elif defined(Q_OS_MAC) || defined(Q_OS_UNIX) + const QByteArray homeEnvRaw = qgetenv("HOME"); + const QString homeEnv(homeEnvRaw.constData()); + + #if defined Q_OS_MAC + homePath = homeEnv + "/Library/Application Support/Quake3"; + #elif defined Q_OS_UNIX + homePath = homeEnv + "/.q3a"; + #endif +#endif + + QDir homeDir(homePath); + + // Try to parse q3config.cfg to get default settings if this is the first time the program has run. + if (!homePath.isEmpty() && homeDir.exists() && !settings.getHaveRun()) + { + parseQuake3Config(); + } + + settings.setHaveRun(true); + + // Populate the GUI with values read from settings. + if (settings.getResolutionMode() >= 0) + { + // Predefined. + ui->cbResolution->setCurrentIndex(2 + settings.getResolutionMode()); + } + else if (settings.getResolutionMode() == -1) + { + // Custom. + ui->cbResolution->setCurrentIndex(1); + } + else if (settings.getResolutionMode() == -2) + { + // Desktop. + ui->cbResolution->setCurrentIndex(0); + } + + ui->sbWidth->setValue(settings.getResolutionWidth()); + ui->sbHeight->setValue(settings.getResolutionHeight()); + + if (settings.getResolutionFullscreen()) + { + ui->rbFull->setChecked(true); + } + else + { + ui->rbWin->setChecked(true); + } } ioLaunch::~ioLaunch() @@ -20,150 +83,112 @@ ioLaunch::~ioLaunch() void ioLaunch::on_btnLaunch_clicked() { + QString ioq3; + #ifdef Q_OS_WIN32 - if(ioq3 == NULL) + // Prompt the user to set the ioq3 path if the settings value either doesn't exist or is invalid. + bool promptForPath = true; + + if (settings.containsQuakePath()) { + const QString path = settings.getQuakePath(); + const QDir dir(path); + + if (!path.isEmpty() && dir.exists()) + promptForPath = false; + } + + if(promptForPath) + { + QMessageBox msg; msg.setText("Please select your Quake3 directory"); msg.exec(); - QString path = QFileDialog::getExistingDirectory (this, tr("Directory"), directory.path()); - ioq3 = QString("\"") + path + QDir::separator() + "ioquake3.x86.exe\" +set r_mode -1"; + + const QString path = QFileDialog::getExistingDirectory (this, tr("Directory")); + + if (path.isEmpty()) + return; + + settings.setQuakePath(path); } + + ioq3 = QString("\"") + settings.getQuakePath() + "/ioquake3.x86.exe\""; #elif defined Q_OS_MAC - ioq3 = "open -a ioquake3 --args +set r_mode -1"; + ioq3 = "open -a ioquake3 --args"; #elif defined Q_OS_UNIX - ioq3 = "ioquake3 +set r_mode -1"; + ioq3 = "ioquake3"; #else #error "Unsupported platform" #endif + int r_mode = settings.getResolutionMode(); + int r_width = settings.getResolutionWidth(); + int r_height = settings.getResolutionHeight(); - if(ioWedited == true && ioHedited == true) + // Handle modes outside what ioquake3 recognize. + if (r_mode == 12) { - - resOption = " +set r_customwidth " + QString::number(ioWidth) + " +set r_customheight " + QString::number(ioHeight); - + r_mode = -1; + r_width = 1280; + r_height = 720; } - if(resOption == NULL) + else if (r_mode == 13) { - resOption = ""; + r_mode = -1; + r_width = 1920; + r_height = 1080; } - if(screenOption == NULL) + else if (r_mode == 14) { - screenOption = ""; + r_mode = -1; + r_width = 1280; + r_height = 800; } - if(!QProcess::startDetached(ioq3+resOption+screenOption)) + ioq3 += QString(" +set r_mode \"%1\"").arg(r_mode); + + if (r_mode == -1) { + ioq3 += QString(" +set r_customwidth %1").arg(r_width) + QString(" +set r_customheight %1").arg(r_height); + } + + ioq3 += QString(" +set r_fullscreen %1").arg(settings.getResolutionFullscreen() ? "1" : "0"); + + if(!QProcess::startDetached(ioq3)) + { + QMessageBox ioq3Failed; ioq3Failed.setText("ioquake3 failed to start!\nIs it installed?\n"); ioq3Failed.exec(); } } -void ioLaunch::on_cbResolution_highlighted(int /*index*/) -{ - ioWedited = false; - ioHedited = false; -} - void ioLaunch::on_cbResolution_currentIndexChanged(int index) { - ioWedited = false; - ioHedited = false; - switch(index) + if (index == 0) { - case 0: - { - resOption = ""; - break; - } - case 1: - { - ioWidth = QApplication::desktop()->screenGeometry().width(); - ioHeight = QApplication::desktop()->screenGeometry().height(); + // Desktop. + settings.setResolutionMode(-2); + } + else if (index == 1) + { + // Custom. + settings.setResolutionMode(-1); + } + else if (index >= 2) + { + // Predefined. + settings.setResolutionMode(index - 2); + } - resOption = " +set r_customwidth " + QString::number(ioWidth) + " +set r_customheight " + QString::number(ioHeight); - break; - } - case 2: - { - resOption = " +set r_customwidth 320 +set r_customheight 240"; - break; - } - case 3: - { - resOption = " +set r_customwidth 400 +set r_customheight 300"; - break; - } - case 4: - { - resOption = " +set r_customwidth 512 +set r_customheight 384"; - break; - } - case 5: - { - resOption = " +set r_customwidth 640 +set r_customheight 480"; - break; - } - case 6: - { - resOption = " +set r_customwidth 800 +set r_customheight 600"; - break; - } - case 7: - { - resOption = " +set r_customwidth 960 +set r_customheight 720"; - break; - } - case 8: - { - resOption = " +set r_customwidth 1024 +set r_customheight 768"; - break; - } - case 9: - { - resOption = " +set r_customwidth 1152 +set r_customheight 864"; - break; - } - case 10: - { - resOption = " +set r_customwidth 1280 +set r_customheight 1024"; - break; - } - case 11: - { - resOption = " +set r_customwidth 1600 +set r_customheight 1200"; - break; - } - case 12: - { - resOption = " +set r_customwidth 2048 +set r_customheight 1536"; - break; - } - case 13: - { - resOption = " +set r_customwidth 856 +set r_customheight 480"; - break; - } - case 14: - { - resOption = " +set r_customwidth 1280 +set r_customheight 720"; - break; - } - case 15: - { - resOption = " +set r_customwidth 1920 +set r_customheight 1080"; - break; - } - case 16: - { - resOption = " +set r_customwidth 1280 +set r_customheight 800"; - break; - } - default: - { - resOption = ""; - break; - } + if (index == 1) + { + ui->sbWidth->setEnabled(true); + ui->sbHeight->setEnabled(true); + } + else + { + ui->sbWidth->setEnabled(false); + ui->sbHeight->setEnabled(false); } } @@ -171,7 +196,7 @@ void ioLaunch::on_rbFull_toggled(bool checked) { if(checked) { - screenOption = " +set r_fullscreen 1"; + settings.setResolutionFullscreen(true); } } @@ -179,41 +204,99 @@ void ioLaunch::on_rbWin_toggled(bool checked) { if(checked) { - screenOption = " +set r_fullscreen 0"; + settings.setResolutionFullscreen(false); } } -void ioLaunch::on_rbDefault_toggled(bool checked) -{ - if(checked) - { - screenOption = ""; - } -} - - void ioLaunch::on_sbWidth_valueChanged(int arg1) { - ioWidth = 0; - if(arg1 >= 0) - { - ioWidth = arg1; - ioWedited = true; - } - else{ - ioWedited = false; - } + settings.setResolutionWidth(arg1); } void ioLaunch::on_sbHeight_valueChanged(int arg1) { - ioHeight = 0; - if(arg1 >= 0) + settings.setResolutionHeight(arg1); +} + +// Since q3config.cfg is generated it's nice and clean and shouldn't need a full parser. +static QString ParseToken(const QString &s, int &offset) +{ + // Skip whitespace. + while (offset < s.length() && s[offset] == ' ') { - ioHeight = arg1; - ioHedited = true; + offset++; } - else{ - ioHedited = false; + + if (offset >= s.length()) + return QString(); + + // Check for quoted token. + bool quoted = s[offset] == '\"'; + + if (quoted) + offset++; + + // Parse token. + int start = offset; + + while (offset < s.length() && ((quoted && s[offset] != '\"') || (!quoted && s[offset] != ' '))) + { + offset++; + } + + // Get token substring. + int end = offset; + + if (quoted && s[offset] == '\"') + { + offset++; + } + + if (end - start <= 0) + return QString(); + + return s.mid(start, end - start); +} + +void ioLaunch::parseQuake3Config() +{ + QFile file(homePath + "/baseq3/q3config.cfg"); + + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) + return; + + QTextStream stream(&file); + + while (!stream.atEnd()) + { + const QString line(stream.readLine()); + + // Skip comments. + if (line.startsWith("//")) + continue; + + int offset = 0; + + if (ParseToken(line, offset) == "seta") + { + const QString cvar(ParseToken(line, offset)); + + if (cvar == "r_mode") + { + settings.setResolutionMode(ParseToken(line, offset).toInt()); + } + else if (cvar == "r_customwidth") + { + settings.setResolutionWidth(ParseToken(line, offset).toInt()); + } + else if (cvar == "r_customheight") + { + settings.setResolutionHeight(ParseToken(line, offset).toInt()); + } + else if (cvar == "r_fullscreen") + { + settings.setResolutionFullscreen(ParseToken(line, offset).toInt() != 0); + } + } } } diff --git a/mainwindow.h b/mainwindow.h index 5d1bc8a..ad2dfde 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -8,6 +8,7 @@ #include #include #include +#include "settings.h" namespace Ui { class ioLaunch; @@ -24,34 +25,22 @@ public: private slots: void on_btnLaunch_clicked(); - - void on_cbResolution_highlighted(int index); - void on_cbResolution_currentIndexChanged(int index); void on_rbFull_toggled(bool checked); void on_rbWin_toggled(bool checked); - void on_rbDefault_toggled(bool checked); - void on_sbWidth_valueChanged(int arg1); void on_sbHeight_valueChanged(int arg1); private: - Ui::ioLaunch *ui; - QString ioq3; - QString resOption; - QString screenOption; - QMessageBox msg; - QMessageBox ioq3Failed; - QDir directory; - int ioWidth; - int ioHeight; - bool ioWedited; - bool ioHedited; + void parseQuake3Config(); + Ui::ioLaunch *ui; + Settings settings; + QString homePath; }; #endif // MAINWINDOW_H diff --git a/mainwindow.ui b/mainwindow.ui index a6ca48c..4ae9d84 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -137,22 +137,6 @@ QTabWidget::tab-bar{ Settings - - - - 110 - 40 - 91 - 21 - - - - Default - - - true - - @@ -164,12 +148,12 @@ QTabWidget::tab-bar{ - Default + Native - Native + Custom @@ -264,7 +248,7 @@ QTabWidget::tab-bar{ - 310 + 200 40 121 21 @@ -329,7 +313,7 @@ QTabWidget::tab-bar{ - 200 + 90 40 111 21 @@ -339,7 +323,7 @@ QTabWidget::tab-bar{ FullScreen - false + true diff --git a/settings.cpp b/settings.cpp new file mode 100644 index 0000000..e7a5f62 --- /dev/null +++ b/settings.cpp @@ -0,0 +1,94 @@ +/* +The MIT License (MIT) + +Copyright (c) 2013 The ioquake Group + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#include +#include "settings.h" + +Settings::Settings() : + settings("ioquake", "launch") +{ +} + +bool Settings::getHaveRun() const +{ + return settings.value("haveRun").toBool(); +} + +void Settings::setHaveRun(bool value) +{ + settings.setValue("haveRun", value); +} + +QString Settings::getQuakePath() const +{ + return settings.value("quakePath").toString(); +} + +bool Settings::containsQuakePath() const +{ + return settings.contains("quakePath"); +} + +void Settings::setQuakePath(const QString &path) +{ + settings.setValue("quakePath", path); +} + +int Settings::getResolutionMode() const +{ + return settings.value("resolution/mode", 3).toInt(); +} + +void Settings::setResolutionMode(int mode) +{ + settings.setValue("resolution/mode", mode); +} + +int Settings::getResolutionWidth() const +{ + return settings.value("resolution/width", 1600).toInt(); +} + +void Settings::setResolutionWidth(int width) +{ + settings.setValue("resolution/width", width); +} + +int Settings::getResolutionHeight() const +{ + return settings.value("resolution/height", 1024).toInt(); +} + +void Settings::setResolutionHeight(int height) +{ + settings.setValue("resolution/height", height); +} + +bool Settings::getResolutionFullscreen() const +{ + return settings.value("resolution/fullscreen", 1).toBool(); +} + +void Settings::setResolutionFullscreen(bool value) +{ + settings.setValue("resolution/fullscreen", value); +} diff --git a/settings.h b/settings.h new file mode 100644 index 0000000..15f35ce --- /dev/null +++ b/settings.h @@ -0,0 +1,58 @@ +/* +The MIT License (MIT) + +Copyright (c) 2013 The ioquake Group + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#ifndef SETTINGS_H +#define SETTINGS_H + +#include + +class QString; + +class Settings +{ +public: + Settings(); + + bool getHaveRun() const; + void setHaveRun(bool value); + + QString getQuakePath() const; + bool containsQuakePath() const; + void setQuakePath(const QString &path); + + int getResolutionMode() const; + void setResolutionMode(int mode); + + int getResolutionWidth() const; + void setResolutionWidth(int width); + + int getResolutionHeight() const; + void setResolutionHeight(int height); + + bool getResolutionFullscreen() const; + void setResolutionFullscreen(bool value); + +private: + QSettings settings; +}; + +#endif // SETTINGS_H