--- a/QTfrontend/main.cpp Sun Oct 28 15:18:26 2012 +0100
+++ b/QTfrontend/main.cpp Fri Dec 06 22:20:53 2019 +0100
@@ -1,6 +1,6 @@
/*
* Hedgewars, a free turn based strategy game
- * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ * Copyright (c) 2004-2015 Andrey Korotaev <unC0Rr@gmail.com>
*
* 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
@@ -13,34 +13,52 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "HWApplication.h"
#include <QTranslator>
#include <QLocale>
-#include <QMessageBox>
-#include <QPlastiqueStyle>
#include <QRegExp>
#include <QMap>
#include <QSettings>
#include <QStringListModel>
#include <QDate>
+#include <QDesktopWidget>
+#include <QLabel>
+#include <QLibraryInfo>
+#include <QStyle>
+#include <QStyleFactory>
#include "hwform.h"
#include "hwconsts.h"
#include "newnetclient.h"
#include "DataManager.h"
+#include "FileEngine.h"
+#include "MessageDialog.h"
+
+#include "SDLInteraction.h"
#ifdef _WIN32
#include <Shlobj.h>
-#endif
-#ifdef __APPLE__
+#elif defined __APPLE__
#include "CocoaInitializer.h"
+
#endif
+#ifdef Q_OS_WIN
+#include <QSplashScreen>
+#endif
+
+#include <QMessageBox>
+
+// Program resources
+#ifdef __APPLE__
+static CocoaInitializer * cocoaInit = NULL;
+#endif
+static FileEngineHandler * engine = NULL;
//Determines the day of easter in year
//from http://aa.usno.navy.mil/faq/docs/easter.php,adapted to C/C++
@@ -78,6 +96,10 @@
season = SEASON_HWBDAY;
years_since_foundation = date.year() - 2004;
}
+ else if (date.month() == 4 && date.day() == 1)
+ {
+ season = SEASON_APRIL1;
+ }
//Easter?
else if (calculateEaster(date.year()) == date)
season = SEASON_EASTER;
@@ -85,29 +107,114 @@
season = SEASON_NONE;
}
+
bool checkForDir(const QString & dir)
{
- QDir tmpdir;
- if (!tmpdir.exists(dir))
- if (!tmpdir.mkdir(dir))
+ QDir tmpdir(dir);
+ if (!tmpdir.exists())
+ if (!tmpdir.mkpath(dir))
{
- QMessageBox directoryMsg(QApplication::activeWindow());
- directoryMsg.setIcon(QMessageBox::Warning);
- directoryMsg.setWindowTitle(QMessageBox::tr("Main - Error"));
- directoryMsg.setText(QMessageBox::tr("Cannot create directory %1").arg(dir));
- directoryMsg.setWindowModality(Qt::WindowModal);
- directoryMsg.exec();
+ MessageDialog::ShowErrorMessage(HWApplication::tr("Cannot create directory %1").arg(dir));
return false;
}
return true;
}
-int main(int argc, char *argv[])
+// Guaranteed to be the last thing ran in the application's life time.
+// Closes resources that need to exist as long as possible.
+void closeResources(void)
+{
+#ifdef __APPLE__
+ if (cocoaInit != NULL)
+ {
+ delete cocoaInit;
+ cocoaInit = NULL;
+ }
+#endif
+ if (engine != NULL)
+ {
+ delete engine;
+ engine = NULL;
+ }
+}
+
+// Simple Message handler that suppresses Qt debug and info messages (qDebug, qInfo).
+// Used when printing command line help (--help) or related error to keep console clean.
+void restrictedMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
+{
+ Q_UNUSED(context)
+ QByteArray localMsg = msg.toLocal8Bit();
+ switch (type) {
+ case QtWarningMsg:
+ case QtCriticalMsg:
+ case QtFatalMsg:
+ fprintf(stderr, "%s\n", localMsg.constData());
+ break;
+ default:
+ break;
+ }
+}
+
+QString getUsage()
{
+ return QString(
+"%1: hedgewars [%2...] [%3]\n"
+"\n"
+"%4:\n"
+" --help %5\n"
+" --config-dir=PATH %6\n"
+" --data-dir=PATH %7\n"
+"\n"
+"%8"
+"\n"
+).arg(
+//: “Usage” as in “how the command-line syntax works”. Shown when running “hedgewars --help” in command-line
+HWApplication::tr("Usage", "command-line")
+).arg(
+//: Name of a command-line argument, shown when running “hedgewars --help” in command-line. “OPTION” as in “command-line option”
+HWApplication::tr("OPTION", "command-line")
+).arg(
+//: Name of a command-line argument, shown when running “hedgewars --help” in command-line
+HWApplication::tr("CONNECTSTRING", "command-line")
+).arg(
+//: “Options” as in “command-line options”
+HWApplication::tr("Options", "command-line")
+).arg(HWApplication::tr("Display this help", "command-line"))
+.arg(HWApplication::tr("Custom path for configuration data and user data", "command-line"))
+.arg(HWApplication::tr("Custom path to the game data folder", "command-line"))
+.arg(HWApplication::tr("Hedgewars can use a %1 (e.g. \"%2\") to connect on start.", "command-line").arg(HWApplication::tr("CONNECTSTRING", "command-line")).arg(QString("hwplay://") + NETGAME_DEFAULT_SERVER));
+}
+
+int main(int argc, char *argv[]) {
+
+ // Since we're calling this first, closeResources() will be the last thing called after main() returns.
+ atexit(closeResources);
+
+#ifdef __APPLE__
+ cocoaInit = new CocoaInitializer(); // Creates the autoreleasepool preventing cocoa object leaks on OS X.
+#endif
+
HWApplication app(argc, argv);
-
app.setAttribute(Qt::AA_DontShowIconsInMenus,false);
+ // file engine, to be initialized later
+ engine = NULL;
+
+ /*
+ This is for messages frelated to translatable command-line arguments.
+ If it is non-zero, will print out a message after loading locale
+ and exit.
+ */
+ enum cmdMsgStateEnum {
+ cmdMsgNone,
+ cmdMsgHelp,
+ cmdMsgMalformedArg,
+ cmdMsgUnknownArg,
+ };
+ enum cmdMsgStateEnum cmdMsgState = cmdMsgNone;
+ QString cmdMsgStateStr;
+
+ // parse arguments
QStringList arguments = app.arguments();
QMap<QString, QString> parsedArgs;
{
@@ -116,6 +223,7 @@
{
QString arg = *i;
+
QRegExp opt("--(\\S+)=(.+)");
if(opt.exactMatch(arg))
{
@@ -124,30 +232,85 @@
}
else
{
+ if(arg.startsWith("--")) {
+ if(arg == "--help")
+ {
+ cmdMsgState = cmdMsgHelp;
+ qInstallMessageHandler(restrictedMessageHandler);
+ }
+ else
+ {
+ // argument is something wrong
+ cmdMsgState = cmdMsgMalformedArg;
+ cmdMsgStateStr = arg;
+ qInstallMessageHandler(restrictedMessageHandler);
+ break;
+ }
+ }
+
+ // if not starting with --, then always skip
+ // (because we can't determine if executable path/call or not - on windows)
++i;
}
}
}
- if(parsedArgs.contains("data-dir"))
+ if(cmdMsgState == cmdMsgNone)
{
- QFileInfo f(parsedArgs["data-dir"]);
- if(!f.exists())
+ if(parsedArgs.contains("data-dir"))
+ {
+ QFileInfo f(parsedArgs["data-dir"]);
+ parsedArgs.remove("data-dir");
+ if(!f.exists())
+ {
+ qWarning() << "WARNING: Cannot open data-dir=" << f.absoluteFilePath();
+ }
+ *cDataDir = f.absoluteFilePath();
+ custom_data = true;
+ }
+
+ if(parsedArgs.contains("config-dir"))
+ {
+ QFileInfo f(parsedArgs["config-dir"]);
+ parsedArgs.remove("config-dir");
+ cfgdir->setPath(f.absoluteFilePath());
+ custom_config = true;
+ }
+ else
{
- qWarning() << "WARNING: Cannot open DATA_PATH=" << f.absoluteFilePath();
+ cfgdir->setPath(QDir::homePath());
+ custom_config = false;
+ }
+
+ if (!parsedArgs.isEmpty())
+ {
+ cmdMsgState = cmdMsgUnknownArg;
+ qInstallMessageHandler(restrictedMessageHandler);
}
- *cDataDir = f.absoluteFilePath();
- custom_data = true;
+
+ // end of parameter parsing
+
+ // Select Qt style
+ QStyle* coreStyle;
+ coreStyle = QStyleFactory::create("Windows");
+ if(coreStyle != 0) {
+ QApplication::setStyle(coreStyle);
+ qDebug("Qt style set: Windows");
+ } else {
+ // Windows style should not be missing in Qt5 Base. If it does, something went terribly wrong!
+ qWarning("No Qt style could be set! Using the default one.");
+ }
}
- if(parsedArgs.contains("config-dir"))
+#ifdef Q_OS_WIN
+ // Splash screen for Windows
+ QPixmap pixmap(":/res/splash.png");
+ QSplashScreen splash(pixmap);
+ if(cmdMsgState == cmdMsgNone)
{
- QFileInfo f(parsedArgs["config-dir"]);
- *cConfigDir = f.absoluteFilePath();
- custom_config = true;
+ splash.show();
}
-
- app.setStyle(new QPlastiqueStyle);
+#endif
QDateTime now = QDateTime::currentDateTime();
srand(now.toTime_t());
@@ -157,23 +320,18 @@
qRegisterMetaType<HWTeam>("HWTeam");
- bindir->cd("bin"); // workaround over NSIS installer
+ bindir->cd(QCoreApplication::applicationDirPath());
- if(cConfigDir->length() == 0)
- cfgdir->setPath(cfgdir->homePath());
- else
- cfgdir->setPath(*cConfigDir);
-
- if(cConfigDir->length() == 0)
+ if(custom_config == false)
{
#ifdef __APPLE__
checkForDir(cfgdir->absolutePath() + "/Library/Application Support/Hedgewars");
cfgdir->cd("Library/Application Support/Hedgewars");
#elif defined _WIN32
- char path[1024];
- if(!SHGetFolderPathA(0, CSIDL_PERSONAL, NULL, 0, path))
+ wchar_t path[MAX_PATH];
+ if(SHGetFolderPathW(0, CSIDL_PERSONAL, NULL, 0, path) == S_OK)
{
- cfgdir->cd(path);
+ cfgdir->cd(QString::fromWCharArray(path));
checkForDir(cfgdir->absolutePath() + "/Hedgewars");
cfgdir->cd("Hedgewars");
}
@@ -195,49 +353,121 @@
// config/save paths
checkForDir(cfgdir->absolutePath() + "/Demos");
+ checkForDir(cfgdir->absolutePath() + "/DrawnMaps");
checkForDir(cfgdir->absolutePath() + "/Saves");
checkForDir(cfgdir->absolutePath() + "/Screenshots");
checkForDir(cfgdir->absolutePath() + "/Teams");
checkForDir(cfgdir->absolutePath() + "/Logs");
checkForDir(cfgdir->absolutePath() + "/Videos");
checkForDir(cfgdir->absolutePath() + "/VideoTemp");
+ checkForDir(cfgdir->absolutePath() + "/VideoThumbnails");
}
datadir->cd(bindir->absolutePath());
datadir->cd(*cDataDir);
- if(!datadir->cd("hedgewars/Data"))
+ if (!datadir->cd("Data"))
{
- QMessageBox missingMsg(QApplication::activeWindow());
- missingMsg.setIcon(QMessageBox::Critical);
- missingMsg.setWindowTitle(QMessageBox::tr("Main - Error"));
- missingMsg.setText(QMessageBox::tr("Failed to open data directory:\n%1\n\n"
- "Please check your installation!").
- arg(datadir->absolutePath()+"/hedgewars/Data"));
- missingMsg.setWindowModality(Qt::WindowModal);
- missingMsg.exec();
+ MessageDialog::ShowFatalMessage(HWApplication::tr("Failed to open data directory:\n%1\n\nPlease check your installation!").arg(datadir->absolutePath()+"/Data"));
return 1;
}
- DataManager & dataMgr = DataManager::instance();
+ bool isProbablyNewPlayer = false;
- QTranslator Translator;
+ // setup PhysFS
+ engine = new FileEngineHandler(argv[0]);
+ engine->mount(datadir->absolutePath());
+ engine->mount(cfgdir->absolutePath() + "/Data");
+ engine->mount(cfgdir->absolutePath());
+ engine->setWriteDir(cfgdir->absolutePath());
+ engine->mountPacks();
+
+ QTranslator TranslatorHedgewars;
+ QTranslator TranslatorQt;
+ QSettings settings(DataManager::instance().settingsFileName(), QSettings::IniFormat);
+ settings.setIniCodec("UTF-8");
{
- QSettings settings(cfgdir->absolutePath() + "/hedgewars.ini", QSettings::IniFormat);
QString cc = settings.value("misc/locale", QString()).toString();
- if(cc.isEmpty())
+ if (cc.isEmpty())
+ {
cc = QLocale::system().name();
+ qDebug("Detected system locale: %s", qPrintable(cc));
- // load locale file into translator
- Translator.load(
- dataMgr.findFileForRead(
- QString("Locale/hedgewars_" + cc)
- )
- );
- app.installTranslator(&Translator);
+ // Fallback to current input locale if "C" locale is returned
+ if(cc == "C")
+ cc = HWApplication::inputMethod()->locale().name();
+ }
+ else
+ {
+ qDebug("Configured frontend locale: %s", qPrintable(cc));
+ }
+ QLocale::setDefault(cc);
+ QString defaultLocaleName = QLocale().name();
+ qDebug("Frontend uses locale: %s", qPrintable(defaultLocaleName));
+
+ if (defaultLocaleName != "C")
+ {
+ // Load locale files into translators
+ if (!TranslatorHedgewars.load(QLocale(), "hedgewars", "_", QString("physfs://Locale")))
+ qWarning("Failed to install Hedgewars translation (%s)", qPrintable(defaultLocaleName));
+ if (!TranslatorQt.load(QLocale(), "qt", "_", QString(QLibraryInfo::location(QLibraryInfo::TranslationsPath))))
+ qWarning("Failed to install Qt translation (%s)", qPrintable(defaultLocaleName));
+ app.installTranslator(&TranslatorHedgewars);
+ app.installTranslator(&TranslatorQt);
+ }
+ app.setLayoutDirection(QLocale().textDirection());
+
+ // Handle command line messages
+ switch(cmdMsgState)
+ {
+ case cmdMsgHelp:
+ {
+ printf("%s", getUsage().toUtf8().constData());
+ return 0;
+ }
+ case cmdMsgMalformedArg:
+ {
+ fprintf(stderr, "%s\n\n%s",
+ HWApplication::tr("Malformed option argument: %1", "command-line").arg(cmdMsgStateStr).toUtf8().constData(),
+ getUsage().toUtf8().constData());
+ return 1;
+ }
+ case cmdMsgUnknownArg:
+ {
+ foreach (const QString & key, parsedArgs.keys())
+ {
+ fprintf(stderr, "%s\n",
+ HWApplication::tr("Unknown option argument: %1", "command-line").arg(QString("--") + key).toUtf8().constData());
+ }
+ fprintf(stderr, "\n%s", getUsage().toUtf8().constData());
+ return 1;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ // Heuristic to figure out if the user is (probably) a first-time player.
+ // If nickname is not set, then probably yes.
+ // The hidden setting firstLaunch is, if present, used to force HW to
+ // treat iself as if it were launched the first time.
+ QString nick = settings.value("net/nick", QString()).toString();
+ if (settings.contains("frontend/firstLaunch"))
+ {
+ isProbablyNewPlayer = settings.value("frontend/firstLaunch").toBool();
+ }
+ else
+ {
+ isProbablyNewPlayer = nick.isNull();
+ }
+
+ // Set firstLaunch to false to make sure we remember we have been launched before.
+ settings.setValue("frontend/firstLaunch", false);
}
#ifdef _WIN32
- // Win32 registry setup (used for xfire detection etc. - don't set it if we're running in "portable" mode with a custom config dir)
+ // Win32 registry setup (used for external software detection etc.
+ // don't set it if running in "portable" mode with a custom config dir)
if(!custom_config)
{
QSettings registry_hklm("HKEY_LOCAL_MACHINE", QSettings::NativeFormat);
@@ -245,24 +475,30 @@
registry_hklm.setValue("Software/Hedgewars/Path", bindir->absolutePath().replace("/", "\\"));
}
#endif
-#ifdef __APPLE__
- // this creates the autoreleasepool that prevents leaking
- CocoaInitializer initializer;
-#endif
+
+ SDLInteraction::instance();
QString style = "";
QString fname;
- checkSeason();
- //For each season, there is an extra stylesheet
- //Todo: change background for easter and birthday
- //(simply replace res/BackgroundBirthday.png and res/BackgroundEaster.png
- //with an appropriate background
+ bool holidaySilliness = settings.value("misc/holidaySilliness", true).toBool();
+ if(holidaySilliness)
+ checkSeason();
+ else
+ season = SEASON_NONE;
+
+ // For each season, there is an extra stylesheet.
+ // TODO: change background for easter
+ // (simply replace res/BackgroundEaster.png
+ // with an appropriate background).
switch (season)
{
case SEASON_CHRISTMAS :
fname = "christmas.css";
break;
+ case SEASON_APRIL1 :
+ fname = "april1.css";
+ break;
case SEASON_EASTER :
fname = "easter.css";
break;
@@ -271,10 +507,11 @@
break;
default :
fname = "qt.css";
+ break;
}
// load external stylesheet if there is any
- QFile extFile(dataMgr.findFileForRead("css/" + fname));
+ QFile extFile("physfs://css/" + fname);
QFile resFile(":/res/css/" + fname);
@@ -283,7 +520,34 @@
if (file.open(QIODevice::ReadOnly | QIODevice::Text))
style.append(file.readAll());
+ qWarning("Starting Hedgewars %s-r%d (%s)", qPrintable(*cVersionString), cRevisionString->toInt(), qPrintable(*cHashString));
+
app.form = new HWForm(NULL, style);
+#ifdef Q_OS_WIN
+ if(cmdMsgState == cmdMsgNone)
+ splash.finish(app.form);
+#endif
app.form->show();
+
+ // Show welcome message for (suspected) first-time player and
+ // point towards the Training menu.
+ if(isProbablyNewPlayer) {
+ QMessageBox questionTutorialMsg(app.form);
+ questionTutorialMsg.setIcon(QMessageBox::Question);
+ questionTutorialMsg.setWindowTitle(QMessageBox::tr("Welcome to Hedgewars"));
+ questionTutorialMsg.setText(QMessageBox::tr("Welcome to Hedgewars!\n\nYou seem to be new around here. Would you like to play some training missions first to learn the basics of Hedgewars?"));
+ questionTutorialMsg.setTextFormat(Qt::PlainText);
+ questionTutorialMsg.setWindowModality(Qt::WindowModal);
+ questionTutorialMsg.addButton(QMessageBox::Yes);
+ questionTutorialMsg.addButton(QMessageBox::No);
+
+ int answer = questionTutorialMsg.exec();
+ if (answer == QMessageBox::Yes) {
+ app.form->GoToTraining();
+ }
+ }
+
+ if (app.urlString)
+ app.fakeEvent();
return app.exec();
}