aboutsummaryrefslogtreecommitdiff
path: root/src/qt
diff options
context:
space:
mode:
Diffstat (limited to 'src/qt')
-rw-r--r--src/qt/CMakeLists.txt34
-rw-r--r--src/qt/assets.qrc15
-rw-r--r--src/qt/assets/background.bmpbin0 -> 2073738 bytes
-rw-r--r--src/qt/assets/discord.pngbin0 -> 2412 bytes
-rw-r--r--src/qt/assets/game.icobin0 -> 93062 bytes
-rw-r--r--src/qt/assets/gear-solid.pngbin0 -> 2550 bytes
-rw-r--r--src/qt/assets/globe.pngbin0 -> 2849 bytes
-rw-r--r--src/qt/assets/logo.pngbin0 -> 21482 bytes
-rw-r--r--src/qt/assets/tf2build.ttfbin0 -> 57168 bytes
-rw-r--r--src/qt/assets/version.rc.in21
-rw-r--r--src/qt/main.cpp12
-rw-r--r--src/qt/mainwindow.cpp249
-rw-r--r--src/qt/mainwindow.hpp50
-rw-r--r--src/qt/mainwindow.ui349
-rw-r--r--src/qt/settings.cpp44
-rw-r--r--src/qt/settings.hpp35
-rw-r--r--src/qt/settings.ui215
-rw-r--r--src/qt/workers.cpp299
-rw-r--r--src/qt/workers.hpp94
19 files changed, 1417 insertions, 0 deletions
diff --git a/src/qt/CMakeLists.txt b/src/qt/CMakeLists.txt
new file mode 100644
index 0000000..c0566d9
--- /dev/null
+++ b/src/qt/CMakeLists.txt
@@ -0,0 +1,34 @@
+SET(FRONTEND_NAME "OFQT")
+enable_language(CXX)
+
+find_package(QT NAMES Qt6 Qt5 COMPONENTS Widgets Gui REQUIRED)
+find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets Gui REQUIRED)
+
+set(CMAKE_AUTOUIC ON)
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTORCC ON)
+
+list(APPEND
+ QT_SOURCES
+ ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/mainwindow.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/mainwindow.hpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/settings.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/settings.hpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/workers.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/workers.hpp
+
+ ${CMAKE_CURRENT_SOURCE_DIR}/mainwindow.ui
+ ${CMAKE_CURRENT_SOURCE_DIR}/assets.qrc
+)
+
+if(WIN32)
+ string(REPLACE "." "," CMAKE_PROJECT_COMMAVERSION ${CMAKE_PROJECT_VERSION})
+ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/assets/version.rc.in ${CMAKE_CURRENT_BINARY_DIR}/version.rc @ONLY)
+ list(APPEND QT_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
+endif()
+
+add_executable(${FRONTEND_NAME} WIN32 ${QT_SOURCES})
+target_link_libraries(${FRONTEND_NAME} PRIVATE libofqt)
+target_link_libraries(${FRONTEND_NAME} PRIVATE Qt${QT_VERSION_MAJOR}::Widgets)
+set_property(TARGET ${FRONTEND_NAME} PROPERTY CXX_STANDARD 11)
diff --git a/src/qt/assets.qrc b/src/qt/assets.qrc
new file mode 100644
index 0000000..10bca84
--- /dev/null
+++ b/src/qt/assets.qrc
@@ -0,0 +1,15 @@
+<RCC>
+ <qresource prefix="background">
+ <file>assets/background.bmp</file>
+ </qresource>
+ <qresource prefix="icon">
+ <file>assets/globe.png</file>
+ <file>assets/logo.png</file>
+ <file>assets/gear-solid.png</file>
+ <file>assets/discord.png</file>
+ <file>assets/game.ico</file>
+ </qresource>
+ <qresource prefix="font">
+ <file>assets/tf2build.ttf</file>
+ </qresource>
+</RCC>
diff --git a/src/qt/assets/background.bmp b/src/qt/assets/background.bmp
new file mode 100644
index 0000000..37156be
--- /dev/null
+++ b/src/qt/assets/background.bmp
Binary files differ
diff --git a/src/qt/assets/discord.png b/src/qt/assets/discord.png
new file mode 100644
index 0000000..cc52539
--- /dev/null
+++ b/src/qt/assets/discord.png
Binary files differ
diff --git a/src/qt/assets/game.ico b/src/qt/assets/game.ico
new file mode 100644
index 0000000..7ebf98a
--- /dev/null
+++ b/src/qt/assets/game.ico
Binary files differ
diff --git a/src/qt/assets/gear-solid.png b/src/qt/assets/gear-solid.png
new file mode 100644
index 0000000..8540b00
--- /dev/null
+++ b/src/qt/assets/gear-solid.png
Binary files differ
diff --git a/src/qt/assets/globe.png b/src/qt/assets/globe.png
new file mode 100644
index 0000000..98374e3
--- /dev/null
+++ b/src/qt/assets/globe.png
Binary files differ
diff --git a/src/qt/assets/logo.png b/src/qt/assets/logo.png
new file mode 100644
index 0000000..ad04eca
--- /dev/null
+++ b/src/qt/assets/logo.png
Binary files differ
diff --git a/src/qt/assets/tf2build.ttf b/src/qt/assets/tf2build.ttf
new file mode 100644
index 0000000..d59eec9
--- /dev/null
+++ b/src/qt/assets/tf2build.ttf
Binary files differ
diff --git a/src/qt/assets/version.rc.in b/src/qt/assets/version.rc.in
new file mode 100644
index 0000000..06d3430
--- /dev/null
+++ b/src/qt/assets/version.rc.in
@@ -0,0 +1,21 @@
+IDI_ICON1 ICON DISCARDABLE "@CMAKE_CURRENT_SOURCE_DIR@/assets/game.ico"
+1 VERSIONINFO
+FILEVERSION @CMAKE_PROJECT_COMMAVERSION@
+PRODUCTVERSION @CMAKE_PROJECT_COMMAVERSION@
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904E4"
+ BEGIN
+ VALUE "FileVersion", "1.0"
+ VALUE "InternalName", "@CMAKE_PROJECT_NAME@"
+ VALUE "OriginalFilename", "@FRONTEND_NAME@.exe"
+ VALUE "ProductName", "@CMAKE_PROJECT_NAME@"
+ VALUE "ProductVersion", "@CMAKE_PROJECT_VERSION@"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1252
+ END
+END \ No newline at end of file
diff --git a/src/qt/main.cpp b/src/qt/main.cpp
new file mode 100644
index 0000000..9cf530b
--- /dev/null
+++ b/src/qt/main.cpp
@@ -0,0 +1,12 @@
+#include "mainwindow.hpp"
+
+#include <QApplication>
+
+int main(int argc, char *argv[])
+{
+ QApplication a(argc, argv);
+ MainWindow w;
+ w.show();
+
+ return a.exec();
+}
diff --git a/src/qt/mainwindow.cpp b/src/qt/mainwindow.cpp
new file mode 100644
index 0000000..35151ca
--- /dev/null
+++ b/src/qt/mainwindow.cpp
@@ -0,0 +1,249 @@
+#include <QApplication>
+#include <QMessageBox>
+#include <QFontDatabase>
+#include <QDesktopServices>
+#include <QUrl>
+
+#include <limits.h>
+#include <iostream>
+
+#include "steam.h"
+
+#include "mainwindow.hpp"
+#include "./ui_mainwindow.h"
+#include "workers.hpp"
+
+
+
+#define FONT "tf2build"
+
+MainWindow::MainWindow(QWidget *parent)
+ : QMainWindow(parent)
+ , ui(new Ui::MainWindow)
+{
+ ui->setupUi(this);
+
+ centralWidget()->layout()->setContentsMargins(0, 0, 0, 0);
+
+ qRegisterMetaType<Worker::Tasks_t>("Task_t");
+ qRegisterMetaType<Worker::Results_t>("Results_t");
+
+ QFontDatabase::addApplicationFont (":/font/assets/" FONT ".ttf");
+ QFont playFont(FONT, 20, QFont::Bold);
+ QFont progressFont(FONT, 10, QFont::Normal);
+
+ ui->mainButton->setFont(playFont);
+ ui->progressBar->setFont(progressFont);
+ ui->statusLabel->setFont(progressFont);
+ ui->infoLabel->setFont(progressFont);
+
+ QPixmap bkgnd(":/background/assets/background.bmp");
+ bkgnd = bkgnd.scaled(this->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
+
+ QPalette palette;
+ palette.setBrush(QPalette::Window, bkgnd);
+ this->setPalette(palette);
+
+ worker = new Worker();
+ worker->moveToThread(&thread);
+
+ connect(&thread, &QThread::finished, worker, &QObject::deleteLater);
+ connect(this, &MainWindow::workerOperate, worker, &Worker::doWork);
+ connect(worker, &Worker::resultReady, this, &MainWindow::workerResult);
+
+ thread.start();
+
+ //operateSVN(svnWorker::SVN_INSTALL);
+
+ connect(ui->settingsButton, SIGNAL(clicked()), this, SLOT(settingsWindow()));
+
+ settings = new Settings(worker, this);
+ settings->setModal(true);
+ //connect(settings, SIGNAL(visibleChanged()), this, SLOT(enable()));
+
+ ui->mainButton->setContextMenuPolicy(Qt::CustomContextMenu);
+
+ connect(ui->mainButton, SIGNAL(clicked()), this, SLOT(updateButton()));
+ connect(ui->mainButton, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showButtonContext(const QPoint&)));
+ connect(ui->buttonDiscord, SIGNAL(clicked()), this, SLOT(openDiscordInvite()));
+ connect(ui->buttonWebsite, SIGNAL(clicked()), this, SLOT(openWebsite()));
+
+ ui->mainButton->setText("...");
+ ui->statusLabel->setText("...");
+ ui->infoLabel->setText("...");
+
+ in_progress = false;
+ installed = false;
+ uptodate = false;
+ workerOperate(Worker::TASK_INIT);
+}
+
+void MainWindow::workerResult(const enum Worker::Results_t& result)
+{
+ switch (result)
+ {
+
+ case Worker::RESULT_UNINSTALL_COMPLETE:
+ case Worker::RESULT_UNINSTALL_FAILURE:
+ // TODO find better use for these
+ case Worker::RESULT_NONE:
+ resetProgress();
+ break;
+
+ case Worker::RESULT_EXIT:
+ QCoreApplication::quit();
+ break;
+
+ case Worker::RESULT_UPDATE_TEXT:
+ ui->progressBar->setValue(worker->progress);
+ ui->infoLabel->setText(worker->infoText);
+ break;
+
+ case Worker::RESULT_IS_INSTALLED:
+ installed = true;
+ workerOperate(Worker::TASK_IS_UPTODATE);
+ break;
+
+ case Worker::RESULT_IS_NOT_INSTALLED:
+ ui->mainButton->setText("Install");
+ installed = false;
+ break;
+
+ case Worker::RESULT_IS_UPTODATE:
+ uptodate = true;
+ ui->mainButton->setText("Play");
+ ui->statusLabel->setText("Up to Date");
+ break;
+
+ case Worker::RESULT_IS_OUTDATED:
+ uptodate = false;
+ ui->mainButton->setText("Update");
+ ui->statusLabel->setText(QString("Revision %1 is available").arg(worker->getRemoteRevision()));
+ break;
+
+ case Worker::RESULT_INIT_COMPLETE:
+ ui->statusLabel->setText("");
+ ui->infoLabel->setText("");
+ workerOperate(Worker::TASK_IS_INSTALLED);
+ break;
+
+ case Worker::RESULT_INIT_FAILURE:
+ QMessageBox::information(this, windowTitle(), "Could not find install location.\nIs Steam installed?");
+ QCoreApplication::quit();
+ break;
+
+ case Worker::RESULT_INSTALL_COMPLETE:
+ resetProgress();
+ ui->statusLabel->setText("Installed");
+ workerOperate(Worker::TASK_IS_INSTALLED);
+ break;
+
+ case Worker::RESULT_INSTALL_FAILURE:
+ ui->progressBar->setFormat("Install failed");
+ break;
+
+ case Worker::RESULT_UPDATE_COMPLETE:
+ resetProgress();
+ ui->statusLabel->setText("Updated");
+ workerOperate(Worker::TASK_IS_UPTODATE);
+ break;
+
+ case Worker::RESULT_UPDATE_RUN:
+ ui->statusLabel->setText("Launching");
+ workerOperate(Worker::TASK_RUN);
+ break;
+
+ case Worker::RESULT_UPDATE_FAILURE:
+ ui->statusLabel->setText("Update failed");
+ break;
+
+ case Worker::RESULT_NO_STEAM:
+ resetProgress();
+ QMessageBox::information(this, windowTitle(), "Steam is not running" );
+ break;
+
+ }
+
+ in_progress = false;
+}
+
+void MainWindow::settingsWindow()
+{
+ settings->refresh();
+ settings->show();
+ //this->setEnabled(false);
+}
+
+void MainWindow::setupButton()
+{
+ workerOperate(Worker::TASK_IS_INSTALLED);
+}
+
+void MainWindow::updateButton()
+{
+ if (in_progress)
+ return;
+
+ if (installed)
+ {
+ if (!uptodate)
+ {
+ workerOperate(Worker::TASK_UPDATE);
+ ui->statusLabel->setText("Updating (may take a while)");
+ }
+ else
+ {
+ workerOperate(Worker::TASK_RUN);
+ }
+ }
+ else
+ {
+ workerOperate(Worker::TASK_INSTALL);
+ ui->statusLabel->setText("Installing (may take a while)");
+ }
+
+ in_progress = true;
+}
+
+void MainWindow::showButtonContext(const QPoint& pos)
+{
+ QPoint absPos = ui->mainButton->mapToGlobal(pos);
+
+ QMenu ctxMenu;
+ ctxMenu.addAction("Run without Update");
+
+ QAction* selectedItem = ctxMenu.exec(absPos);
+
+ if (selectedItem)
+ {
+ workerOperate(Worker::TASK_RUN);
+ }
+}
+
+
+void MainWindow::openDiscordInvite()
+{
+ QDesktopServices::openUrl(QUrl("https://discord.gg/mKjW2ACCrm", QUrl::TolerantMode));
+}
+
+void MainWindow::openWebsite()
+{
+ QDesktopServices::openUrl(QUrl("https://openfortress.fun/", QUrl::TolerantMode));
+}
+
+void MainWindow::resetProgress()
+{
+ ui->progressBar->setFormat("");
+ ui->progressBar->setValue(-1);
+}
+
+MainWindow::~MainWindow()
+{
+ delete ui;
+ delete settings;
+
+ worker->stop_work();
+ thread.quit();
+ thread.wait();
+}
+
diff --git a/src/qt/mainwindow.hpp b/src/qt/mainwindow.hpp
new file mode 100644
index 0000000..0d21efc
--- /dev/null
+++ b/src/qt/mainwindow.hpp
@@ -0,0 +1,50 @@
+#ifndef MAINWINDOW_HPP
+#define MAINWINDOW_HPP
+
+#include <QThread>
+#include <QMainWindow>
+#include <QMenu>
+
+#include "settings.hpp"
+#include "workers.hpp"
+
+QT_BEGIN_NAMESPACE
+namespace Ui { class MainWindow; }
+QT_END_NAMESPACE
+
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ MainWindow(QWidget* parent = nullptr);
+ ~MainWindow();
+
+private:
+ Ui::MainWindow *ui;
+ Settings* settings;
+ QThread thread;
+ Worker* worker;
+
+ bool in_progress;
+ bool installed;
+ bool uptodate;
+
+ void setupButton();
+ void resetProgress();
+
+public slots:
+ void workerResult(const Worker::Results_t&);
+
+private slots:
+ void settingsWindow();
+ void updateButton();
+ void showButtonContext(const QPoint&);
+ void openDiscordInvite();
+ void openWebsite();
+
+signals:
+ void workerOperate(const Worker::Tasks_t&);
+
+};
+#endif // MAINWINDOW_HPP
diff --git a/src/qt/mainwindow.ui b/src/qt/mainwindow.ui
new file mode 100644
index 0000000..574129b
--- /dev/null
+++ b/src/qt/mainwindow.ui
@@ -0,0 +1,349 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>736</width>
+ <height>413</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>736</width>
+ <height>413</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>736</width>
+ <height>413</height>
+ </size>
+ </property>
+ <property name="windowTitle">
+ <string>OFQT</string>
+ </property>
+ <property name="windowIcon">
+ <iconset resource="assets.qrc">
+ <normaloff>:/icon/assets/game.ico</normaloff>:/icon/assets/game.ico</iconset>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <widget class="QWidget" name="centralwidget">
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <layout class="QVBoxLayout" name="layoutLeft">
+ <item>
+ <widget class="QLabel" name="logoOF">
+ <property name="styleSheet">
+ <string notr="true">QLabel {
+ margin: 10px;
+}</string>
+ </property>
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img width=&quot;300&quot; src=&quot;:/icon/assets/logo.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </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>
+ </item>
+ <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>
+ <layout class="QVBoxLayout" name="layoutRight">
+ <item>
+ <layout class="QHBoxLayout" name="layoutSocial">
+ <property name="leftMargin">
+ <number>6</number>
+ </property>
+ <property name="topMargin">
+ <number>6</number>
+ </property>
+ <property name="rightMargin">
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonWebsite">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>45</width>
+ <height>45</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">QPushButton {
+ image: url(:/icon/assets/globe.png);
+ background-color: rgba(0, 0, 0, 0);
+ border: 0;
+}</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="buttonDiscord">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>45</width>
+ <height>45</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">QPushButton {
+ image: url(:/icon/assets/discord.png);
+ background-color: rgba(0, 0, 0, 0);
+ border: 0;
+}</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_2">
+ <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>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="sizeConstraint">
+ <enum>QLayout::SetMinimumSize</enum>
+ </property>
+ <item>
+ <layout class="QVBoxLayout" name="statusLayout">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_4">
+ <item>
+ <widget class="QLabel" name="statusLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">QLabel {
+ color: white;
+ margin-left: 5px;
+ margin-right: 5px;
+}</string>
+ </property>
+ <property name="text">
+ <string>STATUS</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="infoLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">QLabel {
+ color: white;
+ margin-left: 5px;
+ margin-right: 5px;
+}</string>
+ </property>
+ <property name="text">
+ <string>INFO</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QProgressBar" name="progressBar">
+ <property name="styleSheet">
+ <string notr="true">QProgressBar
+{
+ text-align: center;
+ border: none;
+ background-color: rgba(0, 0, 0, 140);
+ color: white;
+}
+
+QProgressBar::chunk
+{
+ background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0,
+ stop:0 rgba(42, 28, 77, 255),
+ stop:1 rgba(73, 43, 133, 255));
+ border: none;
+}</string>
+ </property>
+ <property name="minimum">
+ <number>0</number>
+ </property>
+ <property name="value">
+ <number>-1</number>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <property name="invertedAppearance">
+ <bool>false</bool>
+ </property>
+ <property name="textDirection">
+ <enum>QProgressBar::TopToBottom</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QPushButton" name="settingsButton">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>60</width>
+ <height>60</height>
+ </size>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">QPushButton {
+ padding: 10px;
+ image: url(:/icon/assets/gear-solid.png);
+ background-color: rgba(0, 0, 0, 140);
+ border: none;
+ color: white;
+}
+
+QPushButton:pressed {
+ /*background-color: rgba(0, 0, 0, 200);*/
+}
+</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="mainButton">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>175</width>
+ <height>60</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">QPushButton {
+ /*background-color: rgba(0, 0, 0, 140);*/
+ background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 rgba(38, 33, 76, 255), stop:0.0534188 rgba(38, 33, 76, 255), stop:0.0566239 rgba(0, 0, 0, 140));
+ border: none;
+ color: white;
+}
+
+QPushButton:pressed {
+ /*background-color: rgba(0, 0, 0, 200);*/
+ background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 rgba(38, 33, 76, 255), stop:0.0534188 rgba(38, 33, 76, 255), stop:0.0566239 rgba(0, 0, 0, 200));
+}
+</string>
+ </property>
+ <property name="text">
+ <string>MAIN_BUTTON</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ <tabstops>
+ <tabstop>mainButton</tabstop>
+ <tabstop>settingsButton</tabstop>
+ <tabstop>buttonDiscord</tabstop>
+ </tabstops>
+ <resources>
+ <include location="assets.qrc"/>
+ </resources>
+ <connections/>
+</ui>
diff --git a/src/qt/settings.cpp b/src/qt/settings.cpp
new file mode 100644
index 0000000..507e8c8
--- /dev/null
+++ b/src/qt/settings.cpp
@@ -0,0 +1,44 @@
+#include <QMessageBox>
+#include <iostream>
+
+#include "workers.hpp"
+#include "settings.hpp"
+#include "./ui_settings.h"
+
+Settings::Settings(Worker* pworker, QWidget *parent)
+ : QDialog(parent)
+ , ui(new Ui::Settings)
+{
+ this->worker = pworker;
+ ui->setupUi(this);
+
+ connect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(applySettings()));
+ connect(ui->verifyButton, SIGNAL(clicked()), this, SLOT(verify()));
+ connect(this, &Settings::workerOperate, worker, &Worker::doWork);
+}
+
+void Settings::refresh()
+{
+ ui->serverEdit->setText(this->worker->getRemote());
+
+ ui->revisionLabel->setText(QString("%1").arg(this->worker->getRevision()));
+ ui->installLabel->setText(this->worker->getOfDir());
+}
+
+void Settings::applySettings()
+{
+ worker->setRemote(ui->serverEdit->text());
+ this->hide();
+}
+
+void Settings::verify()
+{
+ workerOperate(Worker::TASK_INSTALL);
+ QMessageBox::information(this, windowTitle(), "Verification Started" );
+}
+
+Settings::~Settings()
+{
+ delete ui;
+}
+
diff --git a/src/qt/settings.hpp b/src/qt/settings.hpp
new file mode 100644
index 0000000..3718f06
--- /dev/null
+++ b/src/qt/settings.hpp
@@ -0,0 +1,35 @@
+#ifndef SETTINGS_HPP
+#define SETTINGS_HPP
+
+#include <QWidget>
+#include <QDialog>
+
+#include "workers.hpp"
+
+QT_BEGIN_NAMESPACE
+namespace Ui { class Settings; }
+QT_END_NAMESPACE
+
+class Settings : public QDialog
+{
+ Q_OBJECT
+
+public:
+ Settings(Worker* pworker, QWidget *parent = nullptr);
+ ~Settings();
+
+ void refresh();
+
+private:
+ Ui::Settings *ui;
+ Worker* worker;
+
+public slots:
+ void applySettings();
+ void verify();
+
+signals:
+ void workerOperate(const Worker::Tasks_t &);
+
+};
+#endif // SETTINGS_HPP
diff --git a/src/qt/settings.ui b/src/qt/settings.ui
new file mode 100644
index 0000000..4fde446
--- /dev/null
+++ b/src/qt/settings.ui
@@ -0,0 +1,215 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Settings</class>
+ <widget class="QWidget" name="Settings">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>375</width>
+ <height>259</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Settings</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QTabWidget" name="tabWidget">
+ <property name="currentIndex">
+ <number>0</number>
+ </property>
+ <widget class="QWidget" name="serverTab">
+ <attribute name="title">
+ <string>Server</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QLabel" name="serverLabel">
+ <property name="text">
+ <string>Server</string>
+ </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>
+ <item>
+ <widget class="QLineEdit" name="serverEdit"/>
+ </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>
+ <widget class="QWidget" name="infoTab">
+ <attribute name="title">
+ <string>Info</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <layout class="QHBoxLayout" name="revisionLayout">
+ <item>
+ <widget class="QLabel" name="revisionText">
+ <property name="text">
+ <string>Current Revision</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_3">
+ <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="QLabel" name="revisionLabel">
+ <property name="text">
+ <string>CURRENT_REVISION</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="installLayout">
+ <item>
+ <widget class="QLabel" name="installText">
+ <property name="text">
+ <string>Install Path</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_4">
+ <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="QLabel" name="installLabel">
+ <property name="text">
+ <string>INSTALL_PATH</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLabel" name="verifyLabel">
+ <property name="text">
+ <string>Verify Files</string>
+ </property>
+ </widget>
+ </item>
+ <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="verifyButton">
+ <property name="text">
+ <string>Verify</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ <property name="centerButtons">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>Settings</receiver>
+ <slot>hide()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>187</x>
+ <y>235</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>187</x>
+ <y>129</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/qt/workers.cpp b/src/qt/workers.cpp
new file mode 100644
index 0000000..3fce706
--- /dev/null
+++ b/src/qt/workers.cpp
@@ -0,0 +1,299 @@
+#include <iostream>
+#include <limits.h>
+#include <pthread.h>
+
+#include "net.h"
+#include "steam.h"
+#include "toast.h"
+
+#include "./ui_mainwindow.h"
+#include "workers.hpp"
+
+#define THREAD_COUNT 4
+
+struct thread_object_info {
+ int working;
+
+ QString* infoText;
+ char* of_dir;
+ char* remote;
+ struct revision_t* rev;
+ size_t index;
+};
+
+static void* thread_download(void* pinfo)
+{
+ struct thread_object_info* info = (struct thread_object_info*)pinfo;
+ if (info)
+ {
+ QString* infoText = info->infoText;
+ char* of_dir = info->of_dir;
+ char* remote = info->remote;
+ struct revision_t* rev = info->rev;
+ size_t i = info->index;
+
+ struct file_info* file = &rev->files[i];
+ if (file->type == TYPE_WRITE)
+ {
+ *info->infoText = QString("Verifying %1").arg(file->object);
+ if (verifyFileHash(of_dir, file))
+ {
+ *infoText = QString("Downloading %1").arg(file->object);
+ downloadObject(of_dir, remote, file);
+ }
+ }
+ }
+
+ info->working = 0;
+ pthread_exit(0);
+
+ return NULL;
+}
+
+Worker::Worker()
+{
+ net_init();
+ of_dir = NULL;
+ remote = NULL;
+}
+
+Worker::~Worker()
+{
+ net_deinit();
+
+ if (of_dir)
+ free(of_dir);
+
+ if (remote)
+ free(remote);
+}
+
+QString Worker::getOfDir()
+{
+ return QString(of_dir);
+}
+
+QString Worker::getRemote()
+{
+ return QString(remote);
+}
+
+void Worker::setRemote(QString remotestr)
+{
+ if (remote)
+ free(remote);
+
+ remote = strdup(remotestr.toStdString().c_str());
+ setLocalRemote(of_dir, remote);
+}
+
+int Worker::getRevision()
+{
+ return getLocalRevision(of_dir);
+}
+
+int Worker::getRemoteRevision()
+{
+ return getLatestRemoteRevision(remote);
+}
+
+bool Worker::isOutdated()
+{
+ return getRemoteRevision() > getRevision();
+}
+
+void Worker::stop_work()
+{
+ do_work = false;
+}
+
+int Worker::update_setup(int local_rev, int remote_rev)
+{
+ if (!of_dir) return 1;
+
+ int retval = 0;
+
+
+ struct revision_t* rev = fastFowardRevisions(remote, local_rev, remote_rev);
+
+ if (rev)
+ {
+ pthread_t download_threads[THREAD_COUNT] = {0};
+ struct thread_object_info thread_info[THREAD_COUNT] = {0, NULL, NULL, NULL, NULL, 0};
+ size_t tindex = 0;
+ QString infoStrings[THREAD_COUNT];
+
+ for (size_t i = 0; i < rev->file_count && do_work; ++i)
+ {
+ while (thread_info[tindex].working)
+ {
+ tindex = (tindex+1) % THREAD_COUNT;
+ }
+
+ pthread_t* thread = &download_threads[tindex];
+ struct thread_object_info* info = &thread_info[tindex];
+ QString* threadString = &infoStrings[tindex];
+
+ if (!threadString->isEmpty())
+ {
+ infoText = *threadString;
+ emit resultReady(RESULT_UPDATE_TEXT);
+ }
+
+ info->working = 1;
+ info->infoText = threadString;
+ info->of_dir = of_dir;
+ info->remote = remote;
+ info->rev = rev;
+ info->index = i;
+ progress = (int)(((i * 100) + 1) / rev->file_count);
+
+ emit resultReady(RESULT_UPDATE_TEXT);
+ pthread_create(thread, NULL, thread_download, info);
+ }
+
+ for (size_t i = 0; i < THREAD_COUNT; ++i)
+ {
+ pthread_t* thread = &download_threads[i];
+ if (*thread)
+ pthread_join(*thread, NULL);
+ }
+
+ progress = 0;
+ infoText = QString("Processing");
+ emit resultReady(RESULT_UPDATE_TEXT);
+
+ for (size_t i = 0; i < rev->file_count && do_work; ++i)
+ {
+ struct file_info* file = &rev->files[i];
+ if (file->type != TYPE_MKDIR)
+ continue;
+
+ progress = (int)(((i * 100) + 1) / rev->file_count);
+ emit resultReady(RESULT_UPDATE_TEXT);
+
+ size_t len = strlen(of_dir) + strlen(OS_PATH_SEP) + strlen(file->path) + 1;
+ char* buf = (char*)malloc(len);
+ snprintf(buf, len, "%s%s%s", of_dir, OS_PATH_SEP, file->path);
+ makeDir(buf);
+ free(buf);
+ }
+
+ for (size_t i = 0; i < rev->file_count && do_work; ++i)
+ {
+ struct file_info* file = &rev->files[i];
+
+ progress = (int)(((i * 100) + 1) / rev->file_count);
+ emit resultReady(RESULT_UPDATE_TEXT);
+
+ switch (file->type)
+ {
+ case TYPE_WRITE:
+ case TYPE_MKDIR:
+ {
+ retval += applyObject(of_dir, file);
+ }
+ break;
+
+ case TYPE_DELETE:
+ {
+ size_t len = strlen(of_dir) + strlen(OS_PATH_SEP) + strlen(file->path) + 1;
+ char* buf = (char*)malloc(len);
+ snprintf(buf, len, "%s%s%s", of_dir, OS_PATH_SEP, file->path);
+ if (isFile(buf))
+ retval += remove(buf);
+ free(buf);
+ }
+ break;
+ }
+ }
+
+ if (do_work)
+ {
+ removeObjects(of_dir);
+ setLocalRemote(of_dir, remote);
+ setLocalRevision(of_dir, remote_rev);
+ }
+
+ progress = 0;
+ infoText = QString("");
+ emit resultReady(RESULT_UPDATE_TEXT);
+
+ freeRevision(rev);
+ }
+
+ return retval;
+}
+
+
+void Worker::doWork(const enum Worker::Tasks_t &parameter) {
+ Results_t result = RESULT_NONE;
+
+
+ switch (parameter)
+ {
+ case TASK_INVALID:
+ break;
+
+ case TASK_INIT:
+ result = RESULT_INIT_FAILURE;
+ of_dir = getOpenFortressDir();
+
+ if (of_dir)
+ {
+ of_dir_len = strlen(of_dir);
+ remote = getLocalRemote(of_dir);
+
+ if (remote)
+ {
+ remote_len = strlen(remote);
+ result = RESULT_INIT_COMPLETE;
+ }
+ else
+ {
+ free(of_dir);
+ of_dir = NULL;
+ remote = NULL;
+ }
+ }
+ else
+ {
+ of_dir = NULL;
+ remote = NULL;
+ }
+
+ break;
+
+ case TASK_IS_INSTALLED:
+ result = getRevision() > -1 ? RESULT_IS_INSTALLED : RESULT_IS_NOT_INSTALLED;
+ break;
+
+ case TASK_IS_UPTODATE:
+ result = isOutdated() ? RESULT_IS_OUTDATED : RESULT_IS_UPTODATE;
+ break;
+
+ case TASK_UNINSTALL:
+ result = RESULT_UNINSTALL_FAILURE;
+ //if (direxists) result = svn_delete(mod) ? RESULT_UNINSTALL_FAILURE : RESULT_UNINSTALL_COMPLETE;
+ break;
+
+ case TASK_INSTALL:
+ result = update_setup(0, getRemoteRevision()) > 0 ? RESULT_INSTALL_FAILURE : RESULT_INSTALL_COMPLETE;
+ break;
+
+ case TASK_UPDATE:
+ result = update_setup(getRevision(), getRemoteRevision()) > 0 ? RESULT_UPDATE_FAILURE : RESULT_UPDATE_COMPLETE;
+ break;
+
+ case TASK_UPDATE_RUN:
+ result = update_setup(getRevision(), getRemoteRevision()) > 0 ? RESULT_UPDATE_FAILURE : RESULT_UPDATE_COMPLETE;
+ break;
+
+ case TASK_RUN:
+ result = getSteamPID() > -1 ? RESULT_EXIT : RESULT_NO_STEAM;
+ if (result == RESULT_EXIT) runOpenFortress();
+ break;
+ }
+
+ emit resultReady(result);
+} \ No newline at end of file
diff --git a/src/qt/workers.hpp b/src/qt/workers.hpp
new file mode 100644
index 0000000..a31d9eb
--- /dev/null
+++ b/src/qt/workers.hpp
@@ -0,0 +1,94 @@
+#ifndef WORKERS_HPP
+#define WORKERS_HPP
+
+#include <QObject>
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+namespace Ui { class MainWindow; }
+QT_END_NAMESPACE
+
+class Worker : public QObject
+{
+ Q_OBJECT
+
+private:
+ char* of_dir;
+ size_t of_dir_len;
+
+ char* remote;
+ size_t remote_len;
+
+ bool do_work = true;
+
+public:
+ int progress = -1;
+ QString infoText;
+
+ Worker();
+ ~Worker();
+
+ QString getOfDir();
+ QString getRemote();
+ void setRemote(QString);
+
+ int getRevision();
+ int getRemoteRevision();
+ bool isOutdated();
+
+ void stop_work();
+
+ int update_setup(int, int);
+
+ enum Tasks_t
+ {
+ TASK_INVALID,
+
+ TASK_IS_INSTALLED,
+ TASK_IS_UPTODATE,
+
+ TASK_INIT,
+ TASK_INSTALL,
+ TASK_UNINSTALL,
+ TASK_UPDATE,
+ TASK_UPDATE_RUN,
+ TASK_RUN,
+ };
+ Q_ENUM(Tasks_t)
+
+ enum Results_t
+ {
+ RESULT_NONE,
+ RESULT_EXIT,
+
+ RESULT_UPDATE_TEXT,
+
+ RESULT_IS_INSTALLED,
+ RESULT_IS_NOT_INSTALLED,
+ RESULT_IS_UPTODATE,
+ RESULT_IS_OUTDATED,
+
+ RESULT_INIT_COMPLETE,
+ RESULT_INIT_FAILURE,
+ RESULT_INSTALL_COMPLETE,
+ RESULT_INSTALL_FAILURE,
+ RESULT_UNINSTALL_COMPLETE,
+ RESULT_UNINSTALL_FAILURE,
+ RESULT_UPDATE_COMPLETE,
+ RESULT_UPDATE_FAILURE,
+ RESULT_UPDATE_RUN,
+
+ RESULT_NO_STEAM
+ };
+ Q_ENUM(Results_t)
+
+public slots:
+ void doWork(const Tasks_t &);
+
+signals:
+ void resultReady(const Results_t &);
+
+};
+
+
+#endif \ No newline at end of file