Major rework, lots of internal maintenance, version editor removed

Fri, 05 Jun 2015 18:33:51 +0300

author
Teemu Piippo <crimsondusk64@gmail.com>
date
Fri, 05 Jun 2015 18:33:51 +0300
changeset 37
c82a86ea87be
parent 36
b8fa9171be6e
child 38
db677d321cf4

Major rework, lots of internal maintenance, version editor removed

.hgignore file | annotate | diff | comparison | revisions
CMakeLists.txt file | annotate | diff | comparison | revisions
src/cfg.cpp file | annotate | diff | comparison | revisions
src/cfg.h file | annotate | diff | comparison | revisions
src/config.cpp file | annotate | diff | comparison | revisions
src/config.h file | annotate | diff | comparison | revisions
src/demo.cpp file | annotate | diff | comparison | revisions
src/demo.h file | annotate | diff | comparison | revisions
src/main.cpp file | annotate | diff | comparison | revisions
src/main.h file | annotate | diff | comparison | revisions
src/misc.cpp file | annotate | diff | comparison | revisions
src/misc.h file | annotate | diff | comparison | revisions
src/prompts.cpp file | annotate | diff | comparison | revisions
src/prompts.h file | annotate | diff | comparison | revisions
src/types.cpp file | annotate | diff | comparison | revisions
src/types.h file | annotate | diff | comparison | revisions
src/versionEditor.cpp file | annotate | diff | comparison | revisions
src/versionEditor.h file | annotate | diff | comparison | revisions
ui/configbox.ui file | annotate | diff | comparison | revisions
--- a/.hgignore	Mon Jun 01 17:06:13 2015 +0300
+++ b/.hgignore	Fri Jun 05 18:33:51 2015 +0300
@@ -2,3 +2,4 @@
 debug
 release
 build
+CMakeLists.txt.user
--- a/CMakeLists.txt	Mon Jun 01 17:06:13 2015 +0300
+++ b/CMakeLists.txt	Fri Jun 05 18:33:51 2015 +0300
@@ -20,8 +20,6 @@
 	src/main.cpp
 	src/misc.cpp
 	src/prompts.cpp
-	src/types.cpp
-	src/versionEditor.cpp
 )
 
 set (ZCINEMA_HEADERS
@@ -31,7 +29,6 @@
 	src/main.h
 	src/misc.h
 	src/prompts.h
-	src/versionEditor.h
 	src/types.h
 )
 
--- a/src/cfg.cpp	Mon Jun 01 17:06:13 2015 +0300
+++ b/src/cfg.cpp	Fri Jun 05 18:33:51 2015 +0300
@@ -1,6 +1,6 @@
 /*
  *  ZCinema: Zandronum demo launcher
- *  Copyright (C) 2013 Santeri Piippo
+ *  Copyright (C) 2013-2015 Teemu Piippo
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -16,138 +16,55 @@
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include <cassert>
+#include <assert.h>
 #include <QDir>
 #include <QTextStream>
 #include <QSettings>
 #include "main.h"
 #include "config.h"
 
-#define MAX_CONFIG 512
+typedef QMap<QString, QVariant> DefaultsMap;
+
+static QSettings* getSettingsObject()
+{
+	return new QSettings;
+}
 
-// =============================================================================
-// -----------------------------------------------------------------------------
-namespace cfg {
-	static struct {
-		void* ptr;
-		Type type;
-		const char* name;
-		QVariant def;
-	} g_configData[MAX_CONFIG];
-	
-	static int g_cfgDataCursor = 0;
-	
-#define CASE(T) case T##Type: return QVariant (*(reinterpret_cast<T*> (ptr)));
-	static QVariant getConfigValue (void* ptr, Type type) {
-		switch (type) {
-		CASE (Int)
-		CASE (String)
-		CASE (Float)
-		CASE (Bool)
-		CASE (List)
-		CASE (Map)
-		
-		case KeySeqType:
-			return QVariant (reinterpret_cast<KeySeq*> (ptr)->toString());
-		}
-		
-		assert (false);
-		return QVariant();
-	}
-#undef CASE
-	
-#define CASE(T, METHOD) case T##Type: (*(reinterpret_cast<T*> (ptr))) = val.METHOD(); return;
-	static void setConfigValue (void* ptr, Type type, const QVariant& val) {
-		switch (type) {
-		CASE (Int, toInt)
-		CASE (String, toString)
-		CASE (Float, toFloat)
-		CASE (Bool, toBool)
-		CASE (List, toList)
-		CASE (Map, toMap)
-		
-		case KeySeqType:
-			reinterpret_cast<KeySeq*> (ptr)->fromString (val.toString());
-			break;
-		}
-	}
-	
-	// =============================================================================
-	// Get the QSettings object.
-	// -----------------------------------------------------------------------------
-	static QSettings* getSettingsObject() {
-		return new QSettings;
-	}
-	
-	// =============================================================================
-	// Load the configuration from file
-	// -----------------------------------------------------------------------------
-	bool load() {
-		QSettings* settings = getSettingsObject();
-		print ("config::load: Loading configuration file from %1\n", settings->fileName());
-		
-		for (alias i : g_configData) {
-			if (i.name == null)
-				break;
-			
-			setConfigValue (i.ptr, i.type, settings->value (i.name, i.def));
-		}
-		
-		settings->deleteLater();
-		return true;
-	}
-	
-	// =============================================================================
-	// Save the configuration to disk
-	// -----------------------------------------------------------------------------
-	bool save() {
-		QSettings* settings = getSettingsObject();
-		settings->clear();
-		print ("Saving configuration to %1...\n", settings->fileName());
-		
-		for (alias i : g_configData) {
-			if (i.name == null)
-				break;
-			
-			QVariant val = getConfigValue (i.ptr, i.type);
-			
-			if (val == i.def)
-				continue;
-			
-			settings->setValue (i.name, val);
-		}
-		
-		settings->sync();
-		settings->deleteLater();
-		return true;
-	}
-	
-	// =============================================================================
-	// Reset configuration defaults.
-	// -----------------------------------------------------------------------------
-	void reset() {
-		for (alias i : g_configData) {
-			if (i.name == null)
-				break;
-			
-			setConfigValue (i.ptr, i.type, i.def);
-		}
+static DefaultsMap& getDefaults()
+{
+	static DefaultsMap defaults;
+
+	if (defaults.isEmpty())
+	{
+		// Initialize defaults here.
 	}
 
-	// =============================================================================
-	// We cannot just add config objects to a list or vector because that would rely
-	// on the vector's c-tor being called before the configs' c-tors. With global
-	// variables we cannot assume that!! Therefore we need to use a C-style array here.
-	// -----------------------------------------------------------------------------
-	ConfigAdder::ConfigAdder (void* ptr, Type type, const char* name, QVariant def) {
-		if (g_cfgDataCursor == 0)
-			memset (g_configData, 0, sizeof g_configData);
-		
-		assert (g_cfgDataCursor < MAX_CONFIG);
-		alias i = g_configData[g_cfgDataCursor++];
-		i.ptr = ptr;
-		i.type = type;
-		i.name = name;
-		i.def = def;
-	}
+	return defaults;
+}
+
+void Config::reset()
+{
+	DefaultsMap& defaults = getDefaults();
+
+	for (DefaultsMap::iterator it = defaults.begin(); it != defaults.end(); ++it)
+		set (it.key(), it.value());
+}
+
+QVariant Config::get (const QString& name)
+{
+	QSettings* settings = getSettingsObject();
+	DefaultsMap& defaults = getDefaults();
+	DefaultsMap::iterator it = defaults.find (name);
+	QVariant def = it != defaults.end() ? *it : QVariant();
+	QVariant value = settings->value (name, def);
+	settings->deleteLater();
+	return value;
+}
+
+bool Config::set (const QString& name, const QVariant& value)
+{
+	QSettings* settings = getSettingsObject();
+	settings->setValue (name, value);
+	settings->deleteLater();
+	return settings->status() == QSettings::NoError;
 }
\ No newline at end of file
--- a/src/cfg.h	Mon Jun 01 17:06:13 2015 +0300
+++ b/src/cfg.h	Fri Jun 05 18:33:51 2015 +0300
@@ -1,6 +1,6 @@
 /*
  *  ZCinema: Zandronum demo launcher
- *  Copyright (C) 2013 Santeri Piippo
+ *  Copyright (C) 2013-2015 Teemu Piippo
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -16,49 +16,13 @@
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef ZCINEMA_CFG_H
-#define ZCINEMA_CFG_H
-
-// =============================================================================
-#include <QString>
+#pragma once
 #include <QVariant>
-#include <QKeySequence>
-
-#define CONFIG(T, NAME, DEFAULT) namespace cfg { T NAME = DEFAULT; } \
-	cfg::ConfigAdder zz_ConfigAdder_##NAME (&cfg::NAME, cfg::T##Type, #NAME, DEFAULT);
-
-#define EXTERN_CONFIG(T, NAME) namespace cfg { extern T NAME; }
 
 // =========================================================
-namespace cfg {
-	enum Type {
-		IntType,
-		StringType,
-		FloatType,
-		BoolType,
-		KeySeqType,
-		ListType,
-		MapType,
-	};
-	
-	// Type-definitions for the above enum list
-	typedef int Int;
-	typedef QString String;
-	typedef float Float;
-	typedef bool Bool;
-	typedef QKeySequence KeySeq;
-	typedef QList<QVariant> List;
-	typedef QMap<QString, QVariant> Map;
-	
-	// ------------------------------------------
-	bool load();
-	bool save();
+namespace Config
+{
 	void reset();
-	
-	class ConfigAdder {
-	public:
-		ConfigAdder (void* ptr, Type type, const char* name, QVariant def);
-	};
-};
-
-#endif // ZCINEMA_CFG_H
\ No newline at end of file
+	QVariant get (const QString& name);
+	bool set(const QString& name, const QVariant& value);
+};
\ No newline at end of file
--- a/src/config.cpp	Mon Jun 01 17:06:13 2015 +0300
+++ b/src/config.cpp	Fri Jun 05 18:33:51 2015 +0300
@@ -1,6 +1,6 @@
 /*
  *  ZCinema: Zandronum demo launcher
- *  Copyright (C) 2013 Santeri Piippo
+ *  Copyright (C) 2013-2015 Teemu Piippo
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -21,30 +21,33 @@
 #include <QFormLayout>
 #include <QProgressBar>
 #include <QMessageBox>
+#include <QLineEdit>
+#include <QListWidget>
+#include <QPushButton>
+#include <QCheckBox>
+#include <QDialogButtonBox>
+#include <QVBoxLayout>
 #include "config.h"
 #include "ui_configbox.h"
 #include "misc.h"
 #include "demo.h"
-#include "versionEditor.h"
 
-CONFIG (Bool, noprompt,      false)
-CONFIG (List, devBuildNames, cfg::List())
-CONFIG (List, releaseNames,  cfg::List())
-CONFIG (List, wadpaths,      cfg::List())
-CONFIG (Map,  binaryPaths,   cfg::Map())
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-class FindPathButton : public QPushButton {
+class FindPathButton : public QPushButton
+{
 public:
-	explicit FindPathButton (QWidget* parent = null) : QPushButton (parent) {
+	explicit FindPathButton (QWidget* parent = NULL) :
+		QPushButton (parent)
+	{
 		setText ("...");
 	}
 
-	QLineEdit* editWidget() const {
+	QLineEdit* editWidget() const
+	{
 		return m_editWidget;
 	}
-	void setEditWidget (QLineEdit* edit) {
+
+	void setEditWidget (QLineEdit* edit)
+	{
 		m_editWidget = edit;
 	}
 
@@ -52,13 +55,11 @@
 	QLineEdit* m_editWidget;
 };
 
-// =============================================================================
-// -----------------------------------------------------------------------------
-ConfigBox::ConfigBox (QWidget* parent, Qt::WindowFlags f) :
+ConfigWindow::ConfigWindow (QWidget* parent, Qt::WindowFlags f) :
 	QDialog (parent, f),
 	ui (new Ui_ConfigBox),
-	m_releaseLayout (null),
-	m_testLayout (null)
+	m_releaseLayout (NULL),
+	m_testLayout (NULL)
 {
 	ui->setupUi (this);
 	
@@ -70,119 +71,159 @@
 	connect (ui->wad_findPath, SIGNAL (clicked()), this, SLOT (findPath()));
 	connect (ui->wad_del, SIGNAL (clicked()), this, SLOT (delPath()));
 	connect (ui->buttonBox, SIGNAL (clicked (QAbstractButton*)), this,
-	         SLOT (buttonPressed (QAbstractButton*)));
-	connect (ui->m_editVersions, SIGNAL (clicked()), this, SLOT (editBinaries()));
-	connect (ui->m_editVersions_2, SIGNAL (clicked()), this, SLOT (editBinaries()));
+		SLOT (buttonPressed (QAbstractButton*)));
 	setWindowTitle (versionSignature());
 }
 
-// =============================================================================
+//
 // -----------------------------------------------------------------------------
-ConfigBox::~ConfigBox() {
+//
+
+ConfigWindow::~ConfigWindow()
+{
 	delete ui;
 }
 
-// =============================================================================
+//
 // -----------------------------------------------------------------------------
-void ConfigBox::initVersions() {
-	if (m_releaseLayout == null) {
-		m_releaseLayout = new QFormLayout;
-		m_testLayout = new QFormLayout;
-		ui->zandronumVersions->setLayout (m_releaseLayout);
-		ui->betaVersions->setLayout (m_testLayout);
-	} else {
-		// re-init, clear the layouts
+//
+
+void ConfigWindow::initVersions()
+{
+	if (m_releaseLayout == NULL)
+	{
+		m_releaseLayout = new QVBoxLayout;
+		m_testLayout = new QVBoxLayout;
+		ui->releaseVersions->setLayout (m_releaseLayout);
+		ui->testingVersions->setLayout (m_testLayout);
+	}
+	else
+	{
+		// Versions are being re-initialized, clear everything
 		for (QWidget* w : m_binaryLayoutWidgets)
-			delete w;
+			w->deleteLater();
 		
 		m_binaryLayoutWidgets.clear();
-		m_zanBinaries.clear();
+		m_pathInputFields.clear();
+	}
+
+	QList<QVariant> versions = getVersions();
+
+	for (int i = 0; i < versions.size(); ++i)
+	{
+		if (not versions[i].canConvert<ZandronumVersion>())
+			continue;
+
+		ZandronumVersion version = versions[i].value<ZandronumVersion>();
+		addVersion (version);
 	}
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+void ConfigWindow::addVersion (const ZandronumVersion& version)
+{
+	QLabel* label = new QLabel (version.name + ":");
+	QLineEdit* pathInput = new QLineEdit;
+	FindPathButton* pathFindButton = new FindPathButton;
+	pathFindButton->setEditWidget (pathInput);
+	connect (pathFindButton, SIGNAL (clicked()), this, SLOT (findZanBinary()));
+
+	QHBoxLayout* pathInputLayout = new QHBoxLayout;
+	pathInputLayout->addWidget (label);
+	pathInputLayout->addWidget (pathInput);
+	pathInputLayout->addWidget (pathFindButton);
+	pathInput->setText (version.binaryPath);
 	
-	for (const QVariant& ver : cfg::devBuildNames)
-		addVersion (ver.toString(), false);
+	m_pathInputFields[version.name] = pathInput;
 	
-	for (const QVariant& rel : cfg::releaseNames)
-		addVersion (rel.toString(), true);
+	if (version.isRelease)
+		m_releaseLayout->addLayout (pathInputLayout);
+	else
+		m_testLayout->addLayout (pathInputLayout);
+	
+	m_binaryLayoutWidgets << pathInput << pathFindButton << label;
 }
 
 // =============================================================================
 // -----------------------------------------------------------------------------
-void ConfigBox::addVersion (const str& name, bool isRelease) {
-	QLabel* lb = new QLabel (name + ":");
-	QLineEdit* ledit = new QLineEdit;
-	FindPathButton* btn = new FindPathButton;
-	btn->setEditWidget (ledit);
+void ConfigWindow::initFromSettings()
+{
+	ui->wad_pathsList->clear();
 	
-	QWidget* wdg = new QWidget;
-	QHBoxLayout* leditLayout = new QHBoxLayout (wdg);
-	leditLayout->addWidget (ledit);
-	leditLayout->addWidget (btn);
+	for (const QVariant& it : Config::get ("wadpaths").toList())
+		addPath (it.toString());
+
+	QList<QVariant> versions = Config::get ("versions").toList();
+
+	for (int i = 0; i < versions.size(); ++i)
+	{
+		ZandronumVersion version = versions[i].value<ZandronumVersion>();
+		m_pathInputFields[version.name]->setText (version.binaryPath);
+	}
 	
-	m_zanBinaries << ledit;
-	connect (btn, SIGNAL (clicked()), this, SLOT (findZanBinary()));
-	
-	if (isRelease)
-		m_releaseLayout->addRow (lb, wdg);
-	else
-		m_testLayout->addRow (lb, wdg);
-	
-	m_binaryLayoutWidgets << ledit << btn << wdg << lb;
+	ui->noDemoPrompt->setChecked (Config::get ("noprompt").toBool());
 }
 
-// =============================================================================
+//
 // -----------------------------------------------------------------------------
-void ConfigBox::initFromSettings() {
-	ui->wad_pathsList->clear();
-	
-	for (const QVariant& it : cfg::wadpaths)
-		addPath (it.toString());
-	
-	int i = 0;
-	
-	for (const QVariant& ver : getVersions())
-		m_zanBinaries[i++]->setText (cfg::binaryPaths[ver.toString()].toString());
-	
-	ui->noDemoPrompt->setChecked (cfg::noprompt);
-}
+//
 
-// =============================================================================
-// -----------------------------------------------------------------------------
-void ConfigBox::saveSettings() {
+void ConfigWindow::saveSettings()
+{
 	QList<QVariant> wadPathList;
 	
 	for (int i = 0; i < ui->wad_pathsList->count(); ++i)
 		wadPathList << ui->wad_pathsList->item (i)->text();
 	
-	cfg::wadpaths = wadPathList;
-	cfg::noprompt = ui->noDemoPrompt->isChecked();
-	
-	int i = 0;
-	for (const QVariant& ver : getVersions())
-		cfg::binaryPaths[ver.toString()] = m_zanBinaries[i++]->text();
-	
-	cfg::save();
+	Config::set ("wadpaths", wadPathList);
+	Config::set ("noprompt", ui->noDemoPrompt->isChecked());
+
+	QList<QVariant> versions = getVersions();
+
+	for (int i = 0; i < versions.size(); ++i)
+	{
+		if (not versions[i].canConvert<ZandronumVersion>())
+			continue;
+
+		ZandronumVersion version = versions[i].value<ZandronumVersion>();
+		version.binaryPath = m_pathInputFields[version.name]->text();
+		versions[i].setValue (version);
+	}
+
+	Config::set ("versions", versions);
 }
 
-// =============================================================================
+//
 // -----------------------------------------------------------------------------
-void ConfigBox::addPath() {
+//
+
+void ConfigWindow::addPath()
+{
 	addPath (ui->wad_pathEntry->text());
 	ui->wad_pathEntry->clear();
 }
 
-// =============================================================================
+//
 // -----------------------------------------------------------------------------
-void ConfigBox::addPath (str path) {
+//
+
+void ConfigWindow::addPath (QString path)
+{
 	ui->wad_pathsList->addItem (path);
 	QListWidgetItem* item = ui->wad_pathsList->item (ui->wad_pathsList->count() - 1);
 	item->setFlags (item->flags() | Qt::ItemIsEditable);
 }
 
-// =============================================================================
+//
 // -----------------------------------------------------------------------------
-void ConfigBox::findPath() {
-	str path = QFileDialog::getExistingDirectory (this);
+//
+
+void ConfigWindow::findPath()
+{
+	QString path = QFileDialog::getExistingDirectory (this);
 
 	if (path.isEmpty())
 		return;
@@ -190,60 +231,58 @@
 	ui->wad_pathEntry->setText (path);
 }
 
-// =============================================================================
+//
 // -----------------------------------------------------------------------------
-void ConfigBox::delPath() {
+//
+
+void ConfigWindow::delPath()
+{
 	delete ui->wad_pathsList->currentItem();
 }
 
-// =============================================================================
+//
 // -----------------------------------------------------------------------------
-void ConfigBox::findZanBinary() {
-	FindPathButton* btn = dynamic_cast<FindPathButton*> (sender());
-	
-	if (!btn)
-		return;
-	
-	str path = getBinaryPath (this);
+//
+
+void ConfigWindow::findZanBinary()
+{
+	FindPathButton* button = static_cast<FindPathButton*> (sender());
+	QString path = getBinaryPath (this);
+
 	if (path.isEmpty())
 		return;
-	
-	btn->editWidget()->setText (path);
+
+	button->editWidget()->setText (path);
 }
 
-// =============================================================================
+//
 // -----------------------------------------------------------------------------
-str ConfigBox::getBinaryPath (QWidget* parent) {
-	str path;
-	const str filter =
+//
+
+QString ConfigWindow::getBinaryPath (QWidget* parent)
+{	
 #ifdef _WIN32
-		"Zandronum Binaries (zandronum.exe)(zandronum.exe);;All files (*.*)(*.*)";
+# define ZAN_EXE_NAME "zandronum.exe"
 #else
-		"Zandronum Binaries (zandronum)(zandronum);;All files (*.*)(*.*)";
+# define ZAN_EXE_NAME "zandronum"
 #endif
-	
-	return QFileDialog::getOpenFileName (parent, QString(), QString(), filter);
+
+	return QFileDialog::getOpenFileName (parent, "", "",
+		"Zandronum Binaries (" ZAN_EXE_NAME ")(" ZAN_EXE_NAME ");;All files (*.*)(*.*)");
 }
 
-// =============================================================================
+//
 // -----------------------------------------------------------------------------
-void ConfigBox::buttonPressed (QAbstractButton* btn) {
-	if (btn == ui->buttonBox->button (QDialogButtonBox::Ok)) {
+//
+
+void ConfigWindow::buttonPressed (QAbstractButton* btn) {
+	if (btn == ui->buttonBox->button (QDialogButtonBox::Ok))
+	{
 		saveSettings();
 		accept();
-	} elif (btn == ui->buttonBox->button (QDialogButtonBox::Cancel))
+	}
+	else if (btn == ui->buttonBox->button (QDialogButtonBox::Cancel))
 		reject();
-	elif (btn == ui->buttonBox->button (QDialogButtonBox::Apply))
+	else if (btn == ui->buttonBox->button (QDialogButtonBox::Apply))
 		saveSettings();
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void ConfigBox::editBinaries() {
-	VersionEditor* dlg = new VersionEditor (this);
-	
-	if (dlg->exec()) {
-		dlg->saveChanges();
-		initVersions();
-	}
-}
+}
\ No newline at end of file
--- a/src/config.h	Mon Jun 01 17:06:13 2015 +0300
+++ b/src/config.h	Fri Jun 05 18:33:51 2015 +0300
@@ -1,6 +1,6 @@
 /*
  *  ZCinema: Zandronum demo launcher
- *  Copyright (C) 2013 Santeri Piippo
+ *  Copyright (C) 2013-2015 Teemu Piippo
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -16,51 +16,40 @@
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef ZCINEMA_CONFIG_H
-#define ZCINEMA_CONFIG_H
-
+#pragma once
 #include <QDialog>
 #include "main.h"
 #include "types.h"
 
-class QFormLayout;
-class QNetworkReply;
-class QHBoxLayout;
-class QLabel;
-class QAbstractButton;
-class QLineEdit;
-class Ui_ConfigBox;
-class QNetworkAccessManager;
-
-class ConfigBox : public QDialog {
+class ConfigWindow : public QDialog
+{
 	Q_OBJECT
 	
 public:
-	explicit ConfigBox (QWidget* parent = null, Qt::WindowFlags f = 0);
-	virtual ~ConfigBox();
-	void addPath (str path);
+	typedef QMap<QString, class QLineEdit*> ZanBinaryMap;
+
+	ConfigWindow (QWidget* parent = NULL, Qt::WindowFlags f = 0);
+	virtual ~ConfigWindow();
+	void addPath (QString path);
 	void initFromSettings();
 	void saveSettings();
 	void initVersions();
 	
-	static str getBinaryPath (QWidget* parent);
+	static QString getBinaryPath (QWidget* parent);
 	
 public slots:
 	void addPath();
 	void findPath();
 	void delPath();
 	void findZanBinary();
-	void editBinaries();
-	void buttonPressed (QAbstractButton* btn);
+	void buttonPressed (class QAbstractButton* btn);
 	
 private:
-	Ui_ConfigBox*        ui;
-	list<QLineEdit*>   m_zanBinaries;
-	list<QWidget*>     m_binaryLayoutWidgets;
-	QFormLayout*       m_releaseLayout,
-	           *       m_testLayout;
+	class Ui_ConfigBox* ui;
+	ZanBinaryMap m_pathInputFields;
+	QList<QWidget*> m_binaryLayoutWidgets;
+	class QVBoxLayout* m_releaseLayout;
+	class QVBoxLayout* m_testLayout;
 	
-	void addVersion (const str& name, bool isRelease);
-};
-
-#endif // ZCINEMA_CONFIG_H
\ No newline at end of file
+	void addVersion (const ZandronumVersion& version);
+};
\ No newline at end of file
--- a/src/demo.cpp	Mon Jun 01 17:06:13 2015 +0300
+++ b/src/demo.cpp	Fri Jun 05 18:33:51 2015 +0300
@@ -1,6 +1,6 @@
 /*
  *  ZCinema: Zandronum demo launcher
- *  Copyright (C) 2013 Santeri Piippo
+ *  Copyright (C) 2013-2015 Teemu Piippo
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -25,23 +25,23 @@
 #include "ui_demoprompt.h"
 #include "prompts.h"
 
-EXTERN_CONFIG (Map,  binaryPaths)
-EXTERN_CONFIG (List, wadpaths)
-EXTERN_CONFIG (Bool, noprompt)
+//
+// -------------------------------------------------------------------------------------------------
+//
 
-static const uint32 g_demoSignature = makeByteID ('Z', 'C', 'L', 'D');
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-str uncolorize (const str& in) {
-	str out;
+QString uncolorize (const QString& in)
+{
+	// TODO: Handle long-form colors like \c[Red]
+	QString out;
 	int skip = 0;
 	
-	for (const QChar& c : in) {
+	for (QChar c : in)
+	{
 		if (skip-- > 0)
 			continue;
 		
-		if (c == QChar ('\034')) {
+		if (c == QChar ('\034'))
+		{
 			skip = 1;
 			continue;
 		}
@@ -52,41 +52,59 @@
 	return out;
 }
 
-// =============================================================================
-// -----------------------------------------------------------------------------
-static str tr (const char* msg) {
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+static QString tr (const char* msg)
+{
 	return QObject::tr (msg);
 }
 
-// =============================================================================
-// -----------------------------------------------------------------------------
-static void error (str msg) {
-	QMessageBox::critical (null, "Error", msg);
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+static void error (QString msg)
+{
+	QMessageBox::critical (NULL, "Error", msg);
 }
 
-// =============================================================================
-// -----------------------------------------------------------------------------
-static bool isKnownVersion (str ver) {
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+static bool isKnownVersion (QString ver)
+{
 	for (const QVariant& it : getVersions())
+	{
 		if (it.toString() == ver || it.toString() + 'M' == ver)
 			return true;
+	}
 	
 	return false;
 }
 
-// =============================================================================
-// -----------------------------------------------------------------------------
-static str findWAD (str name) {
-	if (cfg::wadpaths.size() == 0) {
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+static QString findWAD (QString name)
+{
+	QStringList wadpaths = Config::get ("wadpaths").toStringList();
+
+	if (wadpaths.empty())
+	{
 		error (tr ("No WAD paths configured!"));
-		
+
 		// Cannot just return an empty string here since that'd trigger
 		// another error prompt - skip ahead and exit.
 		exit (9);
 	}
-	
-	for (const QVariant& it : cfg::wadpaths) {
-		str fullpath = fmt ("%1/%2", it.toString(), name);
+
+	for (int i = 0; i < wadpaths.size(); ++i)
+	{
+		QString fullpath = QString ("%1/%2").arg (wadpaths[i]).arg (name);
 		QFile f (fullpath);
 		
 		if (f.exists())
@@ -96,54 +114,78 @@
 	return "";
 }
 
-// =============================================================================
-// -----------------------------------------------------------------------------
-str readString (QDataStream& stream) {
-	str out;
-	uint8 c;
-	
-	for (stream >> c; c != '\0'; stream >> c)
-		out += c;
-	
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+QString readString (QDataStream& stream)
+{
+	QString out;
+	uint8 ch;
+
+	for (stream >> ch; ch != 0; stream >> ch)
+		out += QChar (ch);
+
 	return out;
 }
 
-// =============================================================================
-// -----------------------------------------------------------------------------
-int launchDemo (str path) {
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+struct UserInfo
+{
+	QString netname;
+	QString skin;
+	QString className;
+	uint32 color;
+	uint32 aimdist;
+	uint32 railcolor;
+	uint8 gender;
+	uint8 handicap;
+	uint8 unlagged;
+	uint8 respawnOnFire;
+	uint8 ticsPerUpdate;
+	uint8 connectionType;
+};
+
+int launchDemo (QString path)
+{
 	QFile f (path);
 	
-	if (!f.open (QIODevice::ReadOnly)) {
-		error (fmt (tr ("Couldn't open '%1' for reading: %2"), path, strerror (errno)));
+	if (not f.open (QIODevice::ReadOnly))
+	{
+		error (tr ("Couldn't open '%1' for reading: %2").arg (path).arg (strerror (errno)));
 		return 1;
 	}
 	
 	QDataStream stream (&f);
 	stream.setByteOrder (QDataStream::LittleEndian);
-	
+
 	uint8 offset;
 	uint32 length;
 	uint16 zanversionID, numWads;
 	uint32 longSink;
-	str zanversion;
-	list<str> wads;
-	BuildType buildID;
+	QString zanversion;
+	QStringList wads;
+	UserInfo userinfo;
+
+	// Assume a release build if the build ID not supplied. The demo only got the "ZCLD" signature
+	// in the 1.1 release build, 1.1.1 had no testing binaries and the build ID is included in 1.2
+	// onward.
+	BuildType buildID = ReleaseBuild;
+
 	bool ready = false;
-	
-	struct {
-		str netname, skin, className;
-		uint8 gender, handicap, unlagged, respawnOnFire, ticsPerUpdate, connectionType;
-		uint32 color, aimdist, railcolor;
-	} userinfo;
-	
+
 	// Check signature
 	{
-		uint32 sig;
-		
-		stream >> sig;
-		if (sig != g_demoSignature) {
-			error (fmt (tr ("'%1' is not a Zandronum demo file!"), path));
-			return 3;
+		uint32 demosignature;
+		stream >> demosignature;
+
+		if (demosignature != makeByteID ('Z', 'C', 'L', 'D'))
+		{
+			error (tr ("'%1' is not a valid Zandronum demo file!").arg (path));
+			return 1;
 		}
 	}
 	
@@ -154,48 +196,52 @@
 	       >> length;
 	
 	// Read the demo header and get data
-	for (;;) {
+	for (;;)
+	{
 		uint8 header;
 		stream >> header;
 		
-		if (header == DemoBodyStart + offset) {
+		if (header == DemoBodyStart + offset)
+		{
 			ready = true;
 			break;
-		} elif (header == DemoVersion + offset) {
+		}
+		else if (header == DemoVersion + offset)
+		{
 			stream >> zanversionID;
 			zanversion = readString (stream);
-			
-			if (!zanversion.startsWith ("1.1-") && !zanversion.startsWith ("1.1.1-")) {
+
+			if (not zanversion.startsWith ("1.1-") and not zanversion.startsWith ("1.1.1-"))
+			{
 				uint8 a;
 				stream >> a;
 				buildID = (BuildType) a;
-			} else {
-				// Assume a release build if not supplied. The demo only got the
-				// "ZCLD" signature in the 1.1 release build, 1.1.1 had no testing
-				// binaries and the build ID is included in 1.2 onward.
-				buildID = ReleaseBuild;
 			}
-			
+
 			stream >> longSink; // rng seed - we don't need it
-		} elif (header == DemoUserInfo + offset) {
+		}
+		else if (header == DemoUserInfo + offset)
+		{
 			userinfo.netname = readString (stream);
-			stream >> userinfo.gender
-			       >> userinfo.color
-			       >> userinfo.aimdist;
+			stream >> userinfo.gender;
+			stream >> userinfo.color;
+			stream >> userinfo.aimdist;
 			userinfo.skin = readString (stream);
-			stream >> userinfo.railcolor
-			       >> userinfo.handicap
-			       >> userinfo.unlagged
-			       >> userinfo.respawnOnFire
-			       >> userinfo.ticsPerUpdate
-			       >> userinfo.connectionType;
+			stream >> userinfo.railcolor;
+			stream >> userinfo.handicap;
+			stream >> userinfo.unlagged;
+			stream >> userinfo.respawnOnFire;
+			stream >> userinfo.ticsPerUpdate;
+			stream >> userinfo.connectionType;
 			userinfo.className = readString (stream);
-		} elif (header == DemoWads + offset) {
-			str sink;
+		}
+		else if (header == DemoWads + offset)
+		{
+			QString sink;
 			stream >> numWads;
 			
 			for (uint8 i = 0; i < numWads; ++i) {
-				str wad = readString (stream);
+				QString wad = readString (stream);
 				wads << wad;
 			}
 			
@@ -203,70 +249,74 @@
 			// in them though. Down the sink they go...
 			for (int i = 0; i < 2; ++i)
 				sink = readString (stream);
-		} else {
-			error (fmt (tr ("Unknown header %1!\n"), (int) header));
-			return 3;
+		}
+		else
+		{
+			error (tr ("Unknown header %1!\n").arg (int (header)));
+			return 1;
 		}
 	}
 
-	if (!ready) {
-		error (fmt (tr ("Incomplete demo header in '%s'!"), path));
-		return 3;
-	}
-	
-	if (!isKnownVersion (zanversion)) {
-		UnknownVersionPrompt* prompt = new UnknownVersionPrompt (path, zanversion,
-			(buildID == ReleaseBuild));
-		
-		if (!prompt->exec())
-			return 6;
-		
-		if (!isKnownVersion (zanversion)) {
-			error (tr ("Failure in configuration! This shouldn't happen."));
-			return 6;
-		}
+	if (not ready)
+	{
+		error (tr ("Incomplete demo header in '%s'!").arg (path));
+		return 1;
 	}
 	
-	str binarypath = cfg::binaryPaths [zanversion].toString();
-	
-	if (binarypath.isEmpty()) {
-		error (fmt (tr ("No binary path specified for Zandronum version %1!"), zanversion));
-		return 7;
+	if (not isKnownVersion (zanversion))
+	{
+		QDialog* prompt = new UnknownVersionPrompt (path, zanversion, (buildID == ReleaseBuild));
+
+		if (not prompt->exec())
+			return 1;
+	}
+
+	QString binarypath = Config::get ("binarypaths").toMap()[zanversion].toString();
+
+	if (binarypath.isEmpty())
+	{
+		error (tr ("No binary path specified for Zandronum version %1!").arg (zanversion));
+		return 1;
 	}
 	
-	str iwadpath;
-	list<str> pwadpaths;
+	QString iwadpath;
+	QStringList pwadpaths;
 	
 	// Find the WADs
-	for (const str& wad : wads) {
-		str path = findWAD (wad);
-		
-		// IWAD names can appear in uppercase too. Linux is case-sensitive, so..
-		if (&wad == &wads[0] && path.isEmpty())
+	for (const QString& wad : wads)
+	{
+		QString path = findWAD (wad);
+
+		// WAD names are case-sensitive under non-Windows and they can appear in uppercase
+		// so we need to test that too.
+		if (path.isEmpty())
 			path = findWAD (wad.toUpper());
 		
-		if (path.isEmpty()) {
-			error (fmt (tr ("Couldn't find %1!"), wad));
-			return 8;
+		if (path.isEmpty())
+		{
+			error (tr ("Couldn't find %1!").arg (wad));
+			return 1;
 		}
-		
-		if (&wad == &wads[0])
+
+		if (&wad == &wads.first())
 			iwadpath = path;
 		else
 			pwadpaths << path;
 	}
 	
-	if (!cfg::noprompt) {
-		str pwadtext;
+	if (not Config::get ("noprompt").toBool())
+	{
+		QString pwadtext;
 		
-		for (const str& pwad : wads) {
-			if (&pwad == &wads[0])
+		for (const QString& wad : wads)
+		{
+			if (&wad == &wads.first())
 				continue; // skip the IWAD
 			
-			if (!pwadtext.isEmpty())
+			if (not pwadtext.isEmpty())
 				pwadtext += "<br />";
 			
-			pwadtext += pwad;
+			pwadtext += wad;
 		}
 		
 		QDialog* dlg = new QDialog;
@@ -279,19 +329,17 @@
 		ui.pwadsLabel->setText (pwadtext);
 		dlg->setWindowTitle (versionSignature());
 		
-		if (!dlg->exec())
-			return 1;
+		if (not dlg->exec())
+			return 0;
 	}
 
 	QStringList cmdlineList;
 	cmdlineList << "-playdemo" << path << "-iwad" << iwadpath;
-	
-	if (pwadpaths.size() > 0) {
-		cmdlineList << "-file";
-		cmdlineList << pwadpaths;
-	}
-	
-	print ("Executing: %1 %2\n", binarypath, cmdlineList.join (" "));
+
+	if (pwadpaths.size() > 0)
+		cmdlineList << "-file" << pwadpaths;
+
+	// print ("Executing: %1 %2\n", binarypath, cmdlineList.join (" "));
 	QProcess* proc = new QProcess;
 	proc->start (binarypath, cmdlineList);
 	proc->waitForFinished (-1);
--- a/src/demo.h	Mon Jun 01 17:06:13 2015 +0300
+++ b/src/demo.h	Fri Jun 05 18:33:51 2015 +0300
@@ -1,6 +1,6 @@
 /*
  *  ZCinema: Zandronum demo launcher
- *  Copyright (C) 2013 Santeri Piippo
+ *  Copyright (C) 2013-2015 Teemu Piippo
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -16,12 +16,11 @@
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef ZCINEMA_DEMO_H
-#define ZCINEMA_DEMO_H
-
+#pragma once
 #include "types.h"
 
-enum {
+enum
+{
 	DemoLength,
 	DemoVersion,
 	DemoCVars,
@@ -35,19 +34,19 @@
 	DemoWads
 };
 
-enum BuildType {
+enum BuildType
+{
 	OtherBuild    = 0,
 	ReleaseBuild  = 1,
 	InternalBuild = 2,
 	PrivateBuild  = 3
 };
 
-struct VersionInfo {
-	str shortVersion;
-	str versionString;
+struct VersionInfo
+{
+	QString shortVersion;
+	QString versionString;
 	bool release;
 };
 
-int launchDemo (str path);
-
-#endif // ZCINEMA_DEMO_H
\ No newline at end of file
+int launchDemo (QString path);
\ No newline at end of file
--- a/src/main.cpp	Mon Jun 01 17:06:13 2015 +0300
+++ b/src/main.cpp	Fri Jun 05 18:33:51 2015 +0300
@@ -1,6 +1,6 @@
 /*
  *  ZCinema: Zandronum demo launcher
- *  Copyright (C) 2013 Santeri Piippo
+ *  Copyright (C) 2013-2015 Teemu Piippo
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -22,61 +22,57 @@
 #include "demo.h"
 #include "prompts.h"
 
-// =============================================================================
-// -----------------------------------------------------------------------------
-int main (int argc, char* argv[]) {
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+int main (int argc, char* argv[])
+{
 	QApplication app (argc, argv);
 	app.setApplicationName (UNIXNAME);
 	app.setOrganizationName (UNIXNAME);
 	app.setApplicationVersion (versionString());
-	
-	cfg::load();
-	
-	for (int i = 1; i < argc; ++i) {
-		str arg = argv[i];
+
+	for (int i = 1; i < argc; ++i)
+	{
+		QString arg = argv[i];
 		
-		if (arg == "--config") {
-			ConfigBox dlg;
+		if (arg == "--config")
+		{
+			ConfigWindow dlg;
 			return dlg.exec();
 		}
 	}
 	
-	if (argc != 2) {
-		FindFilePrompt* dlg = new FindFilePrompt (null);
-		if (!dlg->exec())
-			return 255;
-		
+	if (argc > 1)
+	{
+		return launchDemo (argv[1]);
+	}
+	else
+	{
+		FindFilePrompt* dlg = new FindFilePrompt (NULL);
+
+		if (not dlg->exec())
+			return 0;
+
 		return launchDemo (dlg->path());
 	}
-	
-	return launchDemo (argv[1]);
 }
 
-// =============================================================================
-// -----------------------------------------------------------------------------
-str versionString() {
-	str text = fmt ("v%1.%2", VERSION_MAJOR, VERSION_MINOR);
-#if VERSION_PATCH != 0
-	text += fmt (".%1", VERSION_PATCH);
-#endif
-	
-#if BUILD_ID == BUILD_INTERNAL
-	text += "-intern";
-#elif BUILD_ID == BUILD_ALPHA
-	text += "-alpha";
-#elif BUILD_ID == BUILD_BETA
-	text += "-beta";
-#elif BUILD_ID == BUILD_RC
-	text += fmt ("-rc%1", RC_ID);
-#elif BUILD_ID == BUILD_RELEASE
-	text += "-rel";
-#else
-# error Invalid build code!
-#endif // BUILD_ID
-	
-	return text;
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+QString versionString()
+{
+	return VERSION_STRING;
 }
 
-QString versionSignature() {
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+QString versionSignature()
+{
 	return QString (APPNAME) + " " + versionString();
 }
\ No newline at end of file
--- a/src/main.h	Mon Jun 01 17:06:13 2015 +0300
+++ b/src/main.h	Fri Jun 05 18:33:51 2015 +0300
@@ -1,6 +1,6 @@
 /*
  *  ZCinema: Zandronum demo launcher
- *  Copyright (C) 2013 Santeri Piippo
+ *  Copyright (C) 2013-2015 Teemu Piippo
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -16,33 +16,28 @@
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef ZCINEMA_MAIN_H
-#define ZCINEMA_MAIN_H
-
+#pragma once
 #define APPNAME "ZCinema"
 #define UNIXNAME "zcinema"
-#define VERSION_MAJOR 0
-#define VERSION_MINOR 1
+#define VERSION_MAJOR 1
+#define VERSION_MINOR 0
 #define VERSION_PATCH 0
-#define BUILD_ID BUILD_BETA
-#define RC_ID 0
+// #define IS_RELEASE
+
+#define MACRO_TO_STRING(A) MACRO_TO_STRING_(A)
+#define MACRO_TO_STRING_(A) #A
 
-#define BUILD_INTERNAL 0
-#define BUILD_ALPHA    1
-#define BUILD_BETA     2
-#define BUILD_RC       3
-#define BUILD_RELEASE  4
-
-#define elif(A) else if (A)
-#define alias   auto&
+#if VERSION_PATCH == 0
+# define VERSION_STRING MACRO_TO_STRING (VERSION_MAJOR) \
+                    "." MACRO_TO_STRING (VERSION_MINOR)
+#else
+# define VERSION_STRING MACRO_TO_STRING (VERSION_MAJOR) \
+                    "." MACRO_TO_STRING (VERSION_MINOR) \
+                    "." MACRO_TO_STRING (VERSION_PATCH)
+#endif
 
 #include <QSettings>
 #include "cfg.h"
 
-struct VersionInfo;
-static const std::nullptr_t null = nullptr;
-
 QString versionString();
-QString versionSignature();
-
-#endif // ZCINEMA_MAIN_H
\ No newline at end of file
+QString versionSignature();
\ No newline at end of file
--- a/src/misc.cpp	Mon Jun 01 17:06:13 2015 +0300
+++ b/src/misc.cpp	Fri Jun 05 18:33:51 2015 +0300
@@ -1,6 +1,6 @@
 /*
  *  ZCinema: Zandronum demo launcher
- *  Copyright (C) 2013 Santeri Piippo
+ *  Copyright (C) 2013-2015 Teemu Piippo
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -19,53 +19,53 @@
 #include "misc.h"
 #include <QMessageBox>
 
-EXTERN_CONFIG (List, devBuildNames)
-EXTERN_CONFIG (List, releaseNames)
-EXTERN_CONFIG (Map, binaryPaths)
-
-uint32 makeByteID (uint8 a, uint8 b, uint8 c, uint8 d) {
+uint32 makeByteID (uint8 a, uint8 b, uint8 c, uint8 d)
+{
 	return a | (b << 8) | (c << 16) | (d << 24);
 }
 
-// =============================================================================
+//
 // -----------------------------------------------------------------------------
-QList<QVariant> getVersions() {
-	return cfg::devBuildNames + cfg::releaseNames;
-}
+//
 
-// =============================================================================
-// -----------------------------------------------------------------------------
-str binaryConfigName (str ver) {
-	return fmt ("binaries/%1", ver);
+QList<QVariant> getVersions()
+{
+	return Config::get ("versions").toList();
 }
 
-// =============================================================================
+//
 // -----------------------------------------------------------------------------
-void addVersion (str name, bool isRelease, str binaryPath) {
-	cfg::binaryPaths[name] = binaryPath;
-	
-	if (isRelease)
-		cfg::releaseNames << QVariant (name);
-	else
-		cfg::devBuildNames << QVariant (name);
-	
-	cfg::save();
+//
+
+void addVersion (const ZandronumVersion& version)
+{
+	QList<QVariant> versions = getVersions();
+	QVariant var;
+	var.setValue (version);
+	versions.append (var);
+	Config::set ("versions", versions);
 }
 
-// =============================================================================
+//
 // -----------------------------------------------------------------------------
-str basename (str path) {
-	long lastpos = path.lastIndexOf ("/");
-	
+//
+
+QString basename (const QString& path)
+{
+	int lastpos = path.lastIndexOf ("/");
+
 	if (lastpos != -1)
 		return path.mid (lastpos + 1);
-	
+
 	return path;
 }
 
-// =============================================================================
+//
 // -----------------------------------------------------------------------------
-bool confirm (str text) {
-	return QMessageBox::question (null, QObject::tr ("Confirm"), text,
+//
+
+bool confirm (const QString& text)
+{
+	return QMessageBox::question (NULL, QObject::tr ("Confirm"), text,
 		QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::No;
 }
\ No newline at end of file
--- a/src/misc.h	Mon Jun 01 17:06:13 2015 +0300
+++ b/src/misc.h	Fri Jun 05 18:33:51 2015 +0300
@@ -1,6 +1,6 @@
 /*
  *  ZCinema: Zandronum demo launcher
- *  Copyright (C) 2013 Santeri Piippo
+ *  Copyright (C) 2013-2015 Teemu Piippo
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -16,38 +16,51 @@
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef ZCINEMA_MISC_H
-#define ZCINEMA_MISC_H
-
+#pragma once
 #include "types.h"
 
 uint32 makeByteID (uint8 a, uint8 b, uint8 c, uint8 d);
-str binaryConfigName (str ver);
-str basename (str path);
-void addVersion (str name, bool isRelease, str binaryPath);
-bool confirm (str text);
-
+QString basename (const QString& path);
+void addVersion (const ZandronumVersion& version);
+bool confirm (const QString& text);
 QList<QVariant> getVersions();
 
+//
 // -----------------------------------------------------------------------------
-// Templated clamp
-template<class T> static inline T clamp (T a, T min, T max) {
+//
+
+template<typename T>
+T clamp (T a, T min, T max)
+{
 	return (a > max) ? max : (a < min) ? min : a;
 }
 
-// Templated minimum
-template<class T> static inline T min (T a, T b) {
+//
+// -----------------------------------------------------------------------------
+//
+
+template<typename T>
+T min (T a, T b)
+{
 	return (a < b) ? a : b;
 }
 
-// Templated maximum
-template<class T> static inline T max (T a, T b) {
+//
+// -----------------------------------------------------------------------------
+//
+
+template<typename T>
+T max (T a, T b)
+{
 	return (a > b) ? a : b;
 }
 
-// Templated absolute value
-template<class T> static inline T abs (T a) {
-	return (a >= 0) ? a : -a;
-}
+//
+// -----------------------------------------------------------------------------
+//
 
-#endif // ZCINEMA_MISC_H
\ No newline at end of file
+template<typename T>
+T abs (T a)
+{
+	return (a < 0) ? -a : a;
+}
\ No newline at end of file
--- a/src/prompts.cpp	Mon Jun 01 17:06:13 2015 +0300
+++ b/src/prompts.cpp	Fri Jun 05 18:33:51 2015 +0300
@@ -1,6 +1,6 @@
 /*
  *  ZCinema: Zandronum demo launcher
- *  Copyright (C) 2013 Santeri Piippo
+ *  Copyright (C) 2013-2015 Teemu Piippo
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -26,8 +26,8 @@
 // =============================================================================
 // -----------------------------------------------------------------------------
 UnknownVersionPrompt::UnknownVersionPrompt (
-	str fileName,
-	str binaryName,
+	QString fileName,
+	QString binaryName,
 	bool isRelease,
 	QWidget* parent,
 	Qt::WindowFlags f
@@ -40,7 +40,7 @@
 	ui->setupUi (this);
 	
 	// Replace the placeholders
-	str text = ui->m_description->text();
+	QString text = ui->m_description->text();
 	text.replace ("<DEMO>", basename (fileName));
 	text.replace ("<VERSION>", binaryName);
 	ui->m_description->setText (text);
@@ -58,15 +58,16 @@
 
 // =============================================================================
 // -----------------------------------------------------------------------------
-void UnknownVersionPrompt::addBinary() {
-	addVersion (m_binaryString, m_isRelease, ui->m_binaryPath->text());
+void UnknownVersionPrompt::addBinary()
+{
+	addVersion (ZandronumVersion (m_binaryString, m_isRelease, ui->m_binaryPath->text()));
 	accept();
 }
 
 // =============================================================================
 // -----------------------------------------------------------------------------
 void UnknownVersionPrompt::findBinary() {
-	str path = ConfigBox::getBinaryPath (this);
+	QString path = ConfigWindow::getBinaryPath (this);
 	
 	if (path.isEmpty())
 		return;
@@ -95,7 +96,7 @@
 // =============================================================================
 // -----------------------------------------------------------------------------
 void FindFilePrompt::findDemo() {
-	str path = QFileDialog::getOpenFileName (this, tr ("Open Demo File"),
+	QString path = QFileDialog::getOpenFileName (this, tr ("Open Demo File"),
 		QDir::homePath(), tr ("Demo files (*.cld);;All files (*.*)"));
 	
 	if (!path.isEmpty())
@@ -104,6 +105,6 @@
 
 // =============================================================================
 // -----------------------------------------------------------------------------
-str FindFilePrompt::path() const {
+QString FindFilePrompt::path() const {
 	return m_ui->m_path->text();
 }
--- a/src/prompts.h	Mon Jun 01 17:06:13 2015 +0300
+++ b/src/prompts.h	Fri Jun 05 18:33:51 2015 +0300
@@ -1,6 +1,6 @@
 /*
  *  ZCinema: Zandronum demo launcher
- *  Copyright (C) 2013 Santeri Piippo
+ *  Copyright (C) 2013-2015 Teemu Piippo
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -33,8 +33,8 @@
 	Q_OBJECT
 	
 public:
-	explicit UnknownVersionPrompt (str fileName, str binaryName, bool isRelease,
-		QWidget* parent = null, Qt::WindowFlags f = 0);
+	explicit UnknownVersionPrompt (QString fileName, QString binaryName, bool isRelease,
+		QWidget* parent = NULL, Qt::WindowFlags f = 0);
 	virtual ~UnknownVersionPrompt();
 	
 public slots:
@@ -56,7 +56,7 @@
 	explicit FindFilePrompt (QWidget* parent = 0, Qt::WindowFlags f = 0);
 	virtual ~FindFilePrompt();
 	
-	str path() const;
+	QString path() const;
 	
 public slots:
 	void findDemo();
--- a/src/types.cpp	Mon Jun 01 17:06:13 2015 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-/*
- *  ZCinema: Zandronum demo launcher
- *  Copyright (C) 2013 Santeri Piippo
- *
- *  This program is free software: you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation, either version 3 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <QObject>
-#include <QStringList>
-#include <QTextStream>
-#include <assert.h>
-#include "types.h"
-
-str doFormat (initlist<StringFormatArg> args) {
-	assert (args.size() >= 1);
-	str text = args.begin()->value();
-	
-	for (initlist<StringFormatArg>::iterator it = args.begin() + 1; it != args.end(); ++it)
-		text = text.arg (it->value());
-	
-	return text;
-}
-
-void doPrint (FILE* fp, initlist<StringFormatArg> args) {
-	fprintf (fp, "%s", doFormat (args).toStdString().c_str());
-}
-
-// =============================================================================
-StringFormatArg::StringFormatArg (const str& v)    { m_val = v; }
-StringFormatArg::StringFormatArg (const char& v)   { m_val = v; }
-StringFormatArg::StringFormatArg (const uchar& v)  { m_val = v; }
-StringFormatArg::StringFormatArg (const QChar& v)  { m_val = v; }
-StringFormatArg::StringFormatArg (const float& v)  { m_val = str::number (v); }
-StringFormatArg::StringFormatArg (const double& v) { m_val = str::number (v); }
-StringFormatArg::StringFormatArg (const char* v)   { m_val = v; }
-StringFormatArg::StringFormatArg (const void* v)   { m_val.sprintf ("%p", v); }
\ No newline at end of file
--- a/src/types.h	Mon Jun 01 17:06:13 2015 +0300
+++ b/src/types.h	Fri Jun 05 18:33:51 2015 +0300
@@ -1,6 +1,6 @@
 /*
  *  ZCinema: Zandronum demo launcher
- *  Copyright (C) 2013 Santeri Piippo
+ *  Copyright (C) 2013-2015 Teemu Piippo
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -16,19 +16,13 @@
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef ZCINEMA_TYPES_H
-#define ZCINEMA_TYPES_H
-
+#pragma once
 #include "main.h"
 #include <QString>
 #include <QList>
 #include <QVariant>
 
-typedef QString str;
 template<class T> using list = QList<T>;
-typedef unsigned int uint;
-typedef unsigned short ushort;
-typedef unsigned long ulong;
 template<class T> using initlist = std::initializer_list<T>;
 using std::size_t;
 
@@ -45,85 +39,21 @@
 typedef void FILE;
 #endif
 
-// =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
-// StringFormatArg
-//
-// Converts a given value into a string that can be retrieved with ::value ().
-// Used as the argument type to the formatting functions, hence its name.
-// =============================================================================
-class StringFormatArg {
-public:
-	StringFormatArg (const str& v);
-	StringFormatArg (const char& v);
-	StringFormatArg (const uchar& v);
-	StringFormatArg (const QChar& v);
-	
-#define NUMERIC_FORMAT_ARG(T,C) \
-	StringFormatArg (const T& v) { \
-		char valstr[32]; \
-		sprintf (valstr, "%" #C, v); \
-		m_val = valstr; \
-	}
-	
-	NUMERIC_FORMAT_ARG (int, d)
-	NUMERIC_FORMAT_ARG (short, d)
-	NUMERIC_FORMAT_ARG (long, ld)
-	NUMERIC_FORMAT_ARG (uint, u)
-	NUMERIC_FORMAT_ARG (ushort, u)
-	NUMERIC_FORMAT_ARG (ulong, lu)
-	
-	StringFormatArg (const float& v);
-	StringFormatArg (const double& v);
-	StringFormatArg (const char* v);
-	StringFormatArg (const void* v);
-	
-	template<class T> StringFormatArg (const list<T>& v) {
-		m_val = "{ ";
-		uint i = 0;
-		const bool isString = typeid (T) == typeid (str);
-		
-		for (const T& it : v) {
-			if (i++)
-				m_val += ", ";
-			
-			StringFormatArg arg (it);
-			
-			if (isString)
-				m_val += "\"" + arg.value() + "\"";
-			else
-				m_val += arg.value();
-		}
-		
-		if (i)
-			m_val += " ";
-		
-		m_val += "}";
-	}
-	
-	str value() const {
-		return m_val;
-	}
-private:
-	str m_val;
+struct ZandronumVersion
+{
+	ZandronumVersion (QString name, bool isRelease, QString binaryPath) :
+		name (name),
+		binaryPath (binaryPath),
+		isRelease (isRelease) {}
+
+	ZandronumVersion() :
+		isRelease (false) {}
+
+	QString name;
+	QString binaryPath;
+	bool isRelease;
 };
 
-// Formatter function
-str doFormat (initlist<StringFormatArg> args);
-
-// printf replacement
-void doPrint (FILE* fp, initlist<StringFormatArg> args);
+Q_DECLARE_METATYPE (ZandronumVersion)
+typedef QList<ZandronumVersion> VersionList;
 
-// Macros to access these functions
-#ifndef IN_IDE_PARSER
-# define fmt(...) doFormat({ __VA_ARGS__ })
-# define print(...) doPrint (stdout, { __VA_ARGS__ })
-# define fprint(FP, ...) doPrint (FP, { __VA_ARGS__ })
-#else
-str fmt (const char* fmtstr, ...);
-void print (const char* fmtstr, ...);
-void fprint (FILE* fp, const char* fmtstr, ...);
-#endif
-
-#endif // ZCINEMA_TYPES_H
\ No newline at end of file
--- a/src/versionEditor.cpp	Mon Jun 01 17:06:13 2015 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,238 +0,0 @@
-/*
- *  ZCinema: Zandronum demo launcher
- *  Copyright (C) 2013 Santeri Piippo
- *
- *  This program is free software: you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation, either version 3 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "versionEditor.h"
-#include "ui_versionEditor.h"
-#include "ui_addversion.h"
-#include "config.h"
-#include "misc.h"
-
-EXTERN_CONFIG (Map,  binaryPaths)
-EXTERN_CONFIG (List, devBuildNames)
-EXTERN_CONFIG (List, releaseNames)
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-VersionEditor::VersionEditor (QWidget* parent, Qt::WindowFlags f) :
-	QDialog (parent, f),
-	m_ui (new Ui_VersionEditor)
-{
-	m_ui->setupUi (this);
-	
-	connect (m_ui->m_versions, SIGNAL (currentCellChanged (int, int, int, int)),
-	         this, SLOT (selectionChanged (int)));
-	
-	connect (m_ui->m_add,    SIGNAL (clicked (bool)), this, SLOT (add()));
-	connect (m_ui->m_edit,   SIGNAL (clicked (bool)), this, SLOT (edit()));
-	connect (m_ui->m_remove, SIGNAL (clicked (bool)), this, SLOT (remove()));
-	connect (m_ui->m_clear,  SIGNAL (clicked (bool)), this, SLOT (clear()));
-	
-	initVersions();
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-VersionEditor::~VersionEditor() {
-	delete m_ui;
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void VersionEditor::initVersions() {
-	int i = 0;
-	
-	for (const QVariant& ver : (cfg::releaseNames + cfg::devBuildNames)) {
-		bool isRelease = (i < cfg::releaseNames.size());
-		addVersion (ver.toString(), cfg::binaryPaths[ver.toString()].toString(), isRelease);
-		
-		++i;
-	}
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void VersionEditor::add() {
-	AddVersionPrompt* dlg = new AddVersionPrompt (this);
-	if (!dlg->exec())
-		return;
-	
-	addVersion (dlg->name(), dlg->path(), dlg->release());
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void VersionEditor::edit() {
-	QTableWidget* const vers = m_ui->m_versions;
-	int row = vers->currentRow();
-	
-	if (row < 0)
-		return;
-	
-	AddVersionPrompt* dlg = new AddVersionPrompt (this);
-	dlg->setName (vers->item (row, NameColumn)->text());
-	dlg->setPath (vers->item (row, PathColumn)->text());
-	dlg->setRelease (getReleaseCheckbox(row)->isChecked());
-	
-	if (!dlg->exec())
-		return;
-	
-	addVersion (dlg->name(), dlg->path(), dlg->release());
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void VersionEditor::remove() {
-	QTableWidget* const vers = m_ui->m_versions;
-	
-	str name = vers->item (vers->currentRow(), NameColumn)->text();
-	vers->removeRow (vers->currentRow());
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void VersionEditor::clear() {
-	if (!confirm (tr ("Really remove all versions?")))
-		return;
-	
-	for (int i = m_ui->m_versions->rowCount() - 1; i >= 0; --i)
-		m_ui->m_versions->removeRow (i);
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void VersionEditor::addVersion (str name, str path, bool isRelease) {
-	QTableWidget* const vers = m_ui->m_versions;
-	
-	int row = vers->rowCount();
-	vers->insertRow (row);
-	vers->setItem (row, NameColumn, new QTableWidgetItem);
-	vers->setItem (row, PathColumn, new QTableWidgetItem);
-	vers->item (row, NameColumn)->setText (name);
-	vers->item (row, PathColumn)->setText (path);
-	
-	QCheckBox* cb = new QCheckBox;
-	cb->setChecked (isRelease);
-	vers->setCellWidget (row, ReleaseColumn, cb);
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void VersionEditor::saveChanges() {
-	QTableWidget* const vers = m_ui->m_versions;
-	
-	cfg::devBuildNames.clear();
-	cfg::releaseNames.clear();
-	cfg::binaryPaths.clear();
-	
-	for (int i = 0; i < vers->rowCount(); ++i) {
-		const QCheckBox* cb = getReleaseCheckbox (i);
-		bool isRelease = cb->isChecked();
-		const str name = vers->item (i, NameColumn)->text(),
-		          path = vers->item (i, PathColumn)->text();
-		
-		if (isRelease)
-			cfg::releaseNames << QVariant (name);
-		else
-			cfg::devBuildNames << QVariant (name);
-		
-		cfg::binaryPaths[name] = path;
-	}
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void VersionEditor::selectionChanged (int row) {
-	bool ok = (row != -1);
-	m_ui->m_edit->setEnabled (ok);
-	m_ui->m_remove->setEnabled (ok);
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-QCheckBox* VersionEditor::getReleaseCheckbox (int i) {
-	return static_cast<QCheckBox*> (m_ui->m_versions->cellWidget (i, ReleaseColumn));
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-AddVersionPrompt::AddVersionPrompt (QWidget* parent, Qt::WindowFlags f) :
-	QDialog (parent, f),
-	m_ui (new Ui_AddVersion)
-{
-	m_ui->setupUi (this);
-	connect (m_ui->m_binaryName, SIGNAL (textChanged (QString)), this, SLOT (fieldsChanged()));
-	connect (m_ui->m_binaryPath, SIGNAL (textChanged (QString)), this, SLOT (fieldsChanged()));
-	connect (m_ui->m_findBinary, SIGNAL (clicked (bool)), this, SLOT (findPath()));
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-AddVersionPrompt::~AddVersionPrompt() {
-	delete m_ui;
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void AddVersionPrompt::findPath() {
-	str path = ConfigBox::getBinaryPath (this);
-	if (!path.isEmpty())
-		m_ui->m_binaryPath->setText (path);
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-str AddVersionPrompt::name() {
-	return m_ui->m_binaryName->text();
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-str AddVersionPrompt::path() {
-	return m_ui->m_binaryPath->text();
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-bool AddVersionPrompt::release() {
-	return m_ui->m_release->isChecked();
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void AddVersionPrompt::setName (const str& a) {
-	m_ui->m_binaryName->setText (a);
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void AddVersionPrompt::setPath (const str& a) {
-	m_ui->m_binaryPath->setText (a);
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void AddVersionPrompt::setRelease (bool a) {
-	m_ui->m_release->setChecked (a);
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void AddVersionPrompt::fieldsChanged() {
-	bool ok = (!m_ui->m_binaryName->text().isEmpty()) && (!m_ui->m_binaryPath->text().isEmpty());
-	m_ui->buttonBox->button (QDialogButtonBox::Ok)->setEnabled (ok);
-}
--- a/src/versionEditor.h	Mon Jun 01 17:06:13 2015 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,81 +0,0 @@
-/*
- *  ZCinema: Zandronum demo launcher
- *  Copyright (C) 2013 Santeri Piippo
- *
- *  This program is free software: you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation, either version 3 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef ZCINEMA_VERSIONEDITOR_H
-#define ZCINEMA_VERSIONEDITOR_H
-
-#include <QDialog>
-#include "types.h"
-
-class QCheckBox;
-class Ui_VersionEditor;
-class Ui_AddVersion;
-
-class VersionEditor : public QDialog {
-	Q_OBJECT
-	
-public:
-	enum {
-		NameColumn,
-		ReleaseColumn,
-		PathColumn
-	};
-	
-	explicit VersionEditor (QWidget* parent = 0, Qt::WindowFlags f = 0);
-	virtual ~VersionEditor();
-	
-public slots:
-	void add();
-	void edit();
-	void remove();
-	void clear();
-	void saveChanges();
-	void selectionChanged (int row);
-	
-private:
-	Ui_VersionEditor* m_ui;
-	
-	void initVersions();
-	void addVersion (str name, str path, bool isRelease);
-	QCheckBox* getReleaseCheckbox (int i);
-};
-
-class AddVersionPrompt : public QDialog {
-	Q_OBJECT
-	
-public:
-	explicit AddVersionPrompt (QWidget* parent = 0, Qt::WindowFlags f = 0);
-	virtual ~AddVersionPrompt();
-	
-public:
-	str name();
-	str path();
-	bool release();
-	void setName (const str& a);
-	void setPath (const str& a);
-	void setRelease (bool a);
-	
-public slots:
-	void findPath();
-	void fieldsChanged();
-	
-private:
-	Ui_AddVersion* m_ui;
-};
-
-#endif // ZCINEMA_VERSIONEDITOR_H
\ No newline at end of file
--- a/ui/configbox.ui	Mon Jun 01 17:06:13 2015 +0300
+++ b/ui/configbox.ui	Fri Jun 05 18:33:51 2015 +0300
@@ -20,7 +20,7 @@
    <string>Dialog</string>
   </property>
   <property name="windowIcon">
-   <iconset resource="../../zcinema.qrc">
+   <iconset resource="../zcinema.qrc">
     <normaloff>:/icons/zcinema.ico</normaloff>:/icons/zcinema.ico</iconset>
   </property>
   <layout class="QVBoxLayout" name="verticalLayout">
@@ -117,9 +117,9 @@
        </item>
       </layout>
      </widget>
-     <widget class="QWidget" name="tab_2">
+     <widget class="QWidget" name="binaryPaths">
       <attribute name="title">
-       <string>Release Binaries</string>
+       <string>Binary Paths</string>
       </attribute>
       <layout class="QVBoxLayout" name="verticalLayout_4">
        <item>
@@ -135,93 +135,34 @@
            <rect>
             <x>0</x>
             <y>0</y>
-            <width>516</width>
-            <height>225</height>
+            <width>504</width>
+            <height>207</height>
            </rect>
           </property>
-          <layout class="QVBoxLayout" name="verticalLayout_5" stretch="1,0">
+          <layout class="QVBoxLayout" name="verticalLayout_5" stretch="0,0">
            <item>
-            <widget class="QWidget" name="zandronumVersions" native="true"/>
+            <widget class="QGroupBox" name="groupBox">
+             <property name="title">
+              <string>Release versions</string>
+             </property>
+             <layout class="QVBoxLayout" name="verticalLayout_7">
+              <item>
+               <widget class="QWidget" name="releaseVersions" native="true"/>
+              </item>
+             </layout>
+            </widget>
            </item>
            <item>
-            <layout class="QHBoxLayout" name="horizontalLayout_2">
-             <item>
-              <widget class="QPushButton" name="m_editVersions">
-               <property name="text">
-                <string>Edit Versions</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>
-            </layout>
-           </item>
-          </layout>
-         </widget>
-        </widget>
-       </item>
-      </layout>
-     </widget>
-     <widget class="QWidget" name="tab_4">
-      <attribute name="title">
-       <string>Testing Binaries</string>
-      </attribute>
-      <layout class="QVBoxLayout" name="verticalLayout_7">
-       <item>
-        <widget class="QScrollArea" name="scrollArea_2">
-         <property name="frameShape">
-          <enum>QFrame::NoFrame</enum>
-         </property>
-         <property name="widgetResizable">
-          <bool>true</bool>
-         </property>
-         <widget class="QWidget" name="scrollAreaWidgetContents_2">
-          <property name="geometry">
-           <rect>
-            <x>0</x>
-            <y>0</y>
-            <width>516</width>
-            <height>225</height>
-           </rect>
-          </property>
-          <layout class="QVBoxLayout" name="verticalLayout_8" stretch="1,0">
-           <item>
-            <widget class="QWidget" name="betaVersions" native="true"/>
-           </item>
-           <item>
-            <layout class="QHBoxLayout" name="horizontalLayout_3">
-             <item>
-              <widget class="QPushButton" name="m_editVersions_2">
-               <property name="text">
-                <string>Edit Versions</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>
-            </layout>
+            <widget class="QGroupBox" name="groupBox_2">
+             <property name="title">
+              <string>Testing versions</string>
+             </property>
+             <layout class="QVBoxLayout" name="verticalLayout_8">
+              <item>
+               <widget class="QWidget" name="testingVersions" native="true"/>
+              </item>
+             </layout>
+            </widget>
            </item>
           </layout>
          </widget>
@@ -274,7 +215,7 @@
   </layout>
  </widget>
  <resources>
-  <include location="../../zcinema.qrc"/>
+  <include location="../zcinema.qrc"/>
  </resources>
  <connections>
   <connection>

mercurial