QTfrontend/main.cpp
author Medo <smaxein@googlemail.com>
Sat, 02 Feb 2013 14:22:57 +0100
changeset 8469 018da0724a93
parent 8406 8a834943609d
child 8508 f849b7b3af1d
permissions -rw-r--r--
Merge

/*
 * Hedgewars, a free turn based strategy game
 * Copyright (c) 2004-2012 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
 * the Free Software Foundation; version 2 of the License
 *
 * 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 "hwform.h"
#include "hwconsts.h"
#include "newnetclient.h"

#include "DataManager.h"
#include "FileEngine.h"

#ifdef _WIN32
#include <Shlobj.h>
#elif defined __APPLE__
#include "CocoaInitializer.h"
#endif
#ifndef _WIN32
#include <signal.h>
#endif

// 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++
QDate calculateEaster(long year)
{
    int c, n, k, i, j, l, m, d;

    c = year/100;
    n = year - 19*(year/19);
    k = (c - 17)/25;
    i = c - c/4 - (c - k)/3 + 19*n + 15;
    i = i - 30*(i/30);
    i = i - (i/28)*(1 - (i/28)*(29/(i + 1))*((21 - n)/11));
    j = year + year/4 + i + 2 - c + c/4;
    j = j - 7*(j/7);
    l = i - j;
    m = 3 + (l + 40)/44;
    d = l + 28 - 31*(m / 4);

    return QDate(year, m, d);
}

//Checks season and assigns it to the variable season in "hwconsts.h"
void checkSeason()
{
    QDate date = QDate::currentDate();

    //Christmas?
    if (date.month() == 12 && date.day() >= 24
            && date.day() <= 26)
        season = SEASON_CHRISTMAS;
    //Hedgewars birthday?
    else if (date.month() == 10 && date.day() == 31)
    {
        season = SEASON_HWBDAY;
        years_since_foundation = date.year() - 2004;
    }
    //Easter?
    else if (calculateEaster(date.year()) == date)
        season = SEASON_EASTER;
    else
        season = SEASON_NONE;
}
#ifndef _WIN32
void terminateFrontend(int signal)
{
    Q_UNUSED(signal);

    QCoreApplication::exit(0);
}
#endif

bool checkForDir(const QString & 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();
            return false;
        }
    return true;
}

bool checkForFile(const QString & file)
{
    QFile tmpfile(file);
    if (!tmpfile.exists())
        return tmpfile.open(QFile::WriteOnly);
    else
        return true;
}

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

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

#ifndef _WIN32
    signal(SIGINT, &terminateFrontend);
#endif

    HWApplication app(argc, argv);

    QLabel *splash = NULL;
#if defined Q_WS_WIN
    QPixmap pixmap(":res/splash.png");
    splash = new QLabel(0, Qt::FramelessWindowHint|Qt::WindowStaysOnTopHint);
    splash->setAttribute(Qt::WA_TranslucentBackground);
    const QRect deskSize = QApplication::desktop()->screenGeometry(-1);
    QPoint splashCenter = QPoint( (deskSize.width() - pixmap.width())/2,
                                  (deskSize.height() - pixmap.height())/2 );
    splash->move(splashCenter);
    splash->setPixmap(pixmap);
    splash->show();
#endif

    engine = new FileEngineHandler(argv[0]);

    app.setAttribute(Qt::AA_DontShowIconsInMenus,false);

    QStringList arguments = app.arguments();
    QMap<QString, QString> parsedArgs;
    {
        QList<QString>::iterator i = arguments.begin();
        while(i != arguments.end())
        {
            QString arg = *i;

            QRegExp opt("--(\\S+)=(.+)");
            if(opt.exactMatch(arg))
            {
                parsedArgs[opt.cap(1)] = opt.cap(2);
                i = arguments.erase(i);
            }
            else
            {
                ++i;
            }
        }
    }

    if(parsedArgs.contains("data-dir"))
    {
        QFileInfo f(parsedArgs["data-dir"]);
        if(!f.exists())
        {
            qWarning() << "WARNING: Cannot open DATA_PATH=" << f.absoluteFilePath();
        }
        *cDataDir = f.absoluteFilePath();
        custom_data = true;
    }

    if(parsedArgs.contains("config-dir"))
    {
        QFileInfo f(parsedArgs["config-dir"]);
        cfgdir->setPath(f.absoluteFilePath());
        custom_config = true;
    }
    else
    {
        cfgdir->setPath(QDir::homePath());
        custom_config = false;
    }

    app.setStyle(new QPlastiqueStyle());

    QDateTime now = QDateTime::currentDateTime();
    srand(now.toTime_t());
    rand();

    Q_INIT_RESOURCE(hedgewars);

    qRegisterMetaType<HWTeam>("HWTeam");

    bindir->cd(QCoreApplication::applicationDirPath());

    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))
        {
            cfgdir->cd(path);
            checkForDir(cfgdir->absolutePath() + "/Hedgewars");
            cfgdir->cd("Hedgewars");
        }
        else // couldn't retrieve documents folder? almost impossible, but in case fall back to classic path
        {
            checkForDir(cfgdir->absolutePath() + "/.hedgewars");
            cfgdir->cd(".hedgewars");
        }
#else
        checkForDir(cfgdir->absolutePath() + "/.hedgewars");
        cfgdir->cd(".hedgewars");
#endif
    }

    if (checkForDir(cfgdir->absolutePath()))
    {
        // alternative loading/lookup paths
        checkForDir(cfgdir->absolutePath() + "/Data");

        // config/save paths
        checkForDir(cfgdir->absolutePath() + "/Demos");
        checkForDir(cfgdir->absolutePath() + "/Saves");
        checkForDir(cfgdir->absolutePath() + "/Screenshots");
        checkForDir(cfgdir->absolutePath() + "/Teams");
        checkForDir(cfgdir->absolutePath() + "/Logs");
        checkForDir(cfgdir->absolutePath() + "/Videos");
        checkForDir(cfgdir->absolutePath() + "/VideoTemp");
    }

    datadir->cd(bindir->absolutePath());
    datadir->cd(*cDataDir);
    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()+"/Data"));
        missingMsg.setWindowModality(Qt::WindowModal);
        missingMsg.exec();
        return 1;
    }

    // setup PhysFS
    engine->mount(datadir->absolutePath());
    engine->mount(cfgdir->absolutePath() + "/Data");
    engine->mount(cfgdir->absolutePath());
    engine->setWriteDir(cfgdir->absolutePath());
    engine->mountPacks();

    checkForFile("physfs://hedgewars.ini");

    QTranslator Translator;
    {
        QSettings settings("physfs://hedgewars.ini", QSettings::IniFormat);
        QString cc = settings.value("misc/locale", QString()).toString();
        if(cc.isEmpty())
            cc = QLocale::system().name();

        // load locale file into translator
        if(!Translator.load(QString("physfs://Locale/hedgewars_%1").arg(cc)))
            qWarning("Failed to install translation");
        app.installTranslator(&Translator);
    }

#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)
    if(!custom_config)
    {
        QSettings registry_hklm("HKEY_LOCAL_MACHINE", QSettings::NativeFormat);
        registry_hklm.setValue("Software/Hedgewars/Frontend", bindir->absolutePath().replace("/", "\\") + "\\hedgewars.exe");
        registry_hklm.setValue("Software/Hedgewars/Path", bindir->absolutePath().replace("/", "\\"));
    }
#endif

    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
    switch (season)
    {
        case SEASON_CHRISTMAS :
            fname = "christmas.css";
            break;
        case SEASON_EASTER :
            fname = "easter.css";
            break;
        case SEASON_HWBDAY :
            fname = "birthday.css";
            break;
        default :
            fname = "qt.css";
            break;
    }

    // load external stylesheet if there is any
    QFile extFile("physfs://css/" + fname);

    QFile resFile(":/res/css/" + fname);

    QFile & file = (extFile.exists() ? extFile : resFile);

    if (file.open(QIODevice::ReadOnly | QIODevice::Text))
        style.append(file.readAll());

    app.form = new HWForm(NULL, style);
    app.form->show();
    if(splash)
        splash->close();
    return app.exec();
}