Example #1
0
//
// By default will open last athlete, but will also provide
// a dialog to select an athlete if not found, and then upgrade
// the athlete one selected before opening a mainwindow
//
// It will also respawn mainwindows when restarting for changes
// to application settings (athlete folder, language)
//
// Also creates singleton instances prior to application launching
//
int
main(int argc, char *argv[])
{
    int ret=2; // return code from qapplication, default to error

    //
    // PROCESS COMMAND LINE SWITCHES
    //

    // snaffle arguments into a stringlist we can play with into sargs
    // and only keep non-switch args in the args string list
    QStringList sargs, args;
    for (int i=0; i<argc; i++) sargs << argv[i];
#ifdef GC_WANT_R
    bool noR=false;
#endif
#ifdef GC_DEBUG
    bool debug = true;
#else
    bool debug = false;
#endif
    bool server = false;
    nogui = false;
    bool help = false;

    // honour command line switches
    foreach (QString arg, sargs) {

        // help or version requested
        if (arg == "--help" || arg == "--version") {

            help = true;
            fprintf(stderr, "GoldenCheetah %s (%d)\nusage: GoldenCheetah [[directory] athlete]\n\n", VERSION_STRING, VERSION_LATEST);
            fprintf(stderr, "--help or --version to print this message and exit\n");
#ifdef GC_WANT_HTTP
            fprintf(stderr, "--server            to run as an API server\n");
#endif
#ifdef GC_DEBUG
            fprintf(stderr, "--debug             to turn on redirection of messages to goldencheetah.log [debug build]\n");
#else
            fprintf(stderr, "--debug             to direct diagnostic messages to the terminal instead of goldencheetah.log\n");
#endif
#ifdef GC_HAS_CLOUD_DB
            fprintf(stderr, "--clouddbcurator    to add CloudDB curator specific functions to the menus\n");
#endif
#ifdef GC_WANT_R
            fprintf(stderr, "--no-r              to disable R startup\n");
#endif
            fprintf (stderr, "\nSpecify the folder and/or athlete to open on startup\n");
            fprintf(stderr, "If no parameters are passed it will reopen the last athlete.\n\n");

        } else if (arg == "--server") {
#ifdef GC_WANT_HTTP
                nogui = server = true;
#else
                fprintf(stderr, "HTTP support not compiled in, exiting.\n");
                exit(1);
#endif

#ifdef GC_WANT_R
        } else if (arg == "--no-r") {

            noR = true;
#endif
        } else if (arg == "--debug") {

#ifdef GC_DEBUG
            // debug, so don't redirect stderr!
            debug = false;
#else
            debug = true;
#endif
        } else if (arg == "--clouddbcurator") {
#ifdef GC_HAS_CLOUD_DB
            CloudDBCommon::addCuratorFeatures = true;
#else
            fprintf(stderr, "CloudDB support not compiled in, exiting.\n");
            exit(1);
#endif
        } else {

            // not switches !
            args << arg;
        }
    }

    #if 0 // quick hack to get list of metrics and descriptions
    RideMetricFactory::instance().initialize();

    const RideMetricFactory &factory = RideMetricFactory::instance();
    QHashIterator<QString,RideMetric*> it(factory.metricHash());
    it.toFront();
    while(it.hasNext()) {
        it.next();
        fprintf(stderr, "%s|%s\n",
                it.value()->name().toUtf8().data(),
                it.value()->description().toUtf8().data());
    }
    exit(0);
    #endif

    // help or version printed so just exit now
    if (help) {
        exit(0);
    }

    //
    // INITIALISE ONE TIME OBJECTS
    //
#ifdef GC_WANT_HTTP
    listener = NULL;
#endif

#ifdef Q_OS_X11
    XInitThreads();
#endif

#ifdef Q_OS_MACX
    if ( QSysInfo::MacintoshVersion > QSysInfo::MV_10_8 )
    {
        // fix Mac OS X 10.9 (mavericks) font issue
        // https://bugreports.qt-project.org/browse/QTBUG-32789
        QFont::insertSubstitution("LucidaGrande", "Lucida Grande");
    }
#endif

#ifdef GC_WANT_R
    rtool = NULL;
#endif

    // create the application -- only ever ONE regardless of restarts
    application = new QApplication(argc, argv);
    //XXXIdleEventFilter idleFilter;
    //XXXapplication->installEventFilter(&idleFilter);

#ifdef Q_OS_MAC
    // get an autorelease pool setup
    static CocoaInitializer cocoaInitializer;
#endif

    // set default colors
    GCColor::setupColors();
    appsettings->migrateQSettingsSystem(); // colors must be setup before migration can take place, but reading has to be from the migrated ones
    GCColor::readConfig();

    // set defaultfont
    QFont font;
    font.fromString(appsettings->value(NULL, GC_FONT_DEFAULT, QFont().toString()).toString());
    font.setPointSize(appsettings->value(NULL, GC_FONT_DEFAULT_SIZE, 10).toInt());
    application->setFont(font); // set default font


    //
    // OPEN FIRST MAINWINDOW
    //
    do {

        // lets not restart endlessly
        restarting = false;

        // we reload R if we are restarting to get that, but
        // only if it failed
#ifdef GC_WANT_R
        // create the singleton in the main thread
        // will be shared by all athletes and all charts (!!)
        if (noR) {
            rtool = NULL;
        } else if (rtool == NULL && appsettings->value(NULL, GC_EMBED_R, true).toBool()) {
            rtool = new RTool();
            if (rtool->failed == true) rtool=NULL;
        }
#endif
        //this is the path within the current directory where GC will look for
        //files to allow USB stick support
        QString localLibraryPath="Library/GoldenCheetah";

        //this is the path that used to be used for all platforms
        //now different platforms will use their own path
        //this path is checked first to make things easier for long-time users
        QString oldLibraryPath=QDir::home().canonicalPath()+"/Library/GoldenCheetah";

        //these are the new platform-dependent library paths
#if defined(Q_OS_MACX)
        QString libraryPath="Library/GoldenCheetah";
#elif defined(Q_OS_WIN)
#if QT_VERSION > 0x050000 // windows and qt5
        QStringList paths=QStandardPaths::standardLocations(QStandardPaths::DataLocation);
        QString libraryPath = paths.at(0); 
#else // windows not qt5
        QString libraryPath=QDesktopServices::storageLocation(QDesktopServices::DataLocation) + "/GoldenCheetah";
#endif // qt5
#else // not windows or osx (must be Linux or OpenBSD)
        // Q_OS_LINUX et al
        QString libraryPath=".goldencheetah";
#endif //

        // or did we override in settings?
        QString sh;
        if ((sh=appsettings->value(NULL, GC_HOMEDIR, "").toString()) != QString("")) localLibraryPath = sh;

        // lets try the local library we've worked out...
        QDir home = QDir();
        if(QDir(localLibraryPath).exists() || home.exists(localLibraryPath)) {

            home.cd(localLibraryPath);

        } else {

            // YIKES !! The directory we should be using doesn't exist!
            home = QDir::home();
            if (home.exists(oldLibraryPath)) { // there is an old style path, lets fo there
                home.cd(oldLibraryPath);
            } else {

                if (!home.exists(libraryPath)) {
                    if (!home.mkpath(libraryPath)) {

                        // tell user why we aborted !
                        QMessageBox::critical(NULL, "Exiting", QString("Cannot create library directory (%1)").arg(libraryPath));
                        exit(0);
                    }
                }
                home.cd(libraryPath);
            }
        }

        // set global root directory
        gcroot = home.canonicalPath();
        appsettings->initializeQSettingsGlobal(gcroot);


        // now redirect stderr
#ifndef WIN32
        if (!debug) nostderr(home.canonicalPath());
#else
        Q_UNUSED(debug)
#endif

        // install QT Translator to enable QT Dialogs translation
        // we may have restarted JUST to get this!
        QTranslator qtTranslator;
        qtTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath));
        application->installTranslator(&qtTranslator);

        // Language setting (default to system locale)
        QVariant lang = appsettings->value(NULL, GC_LANG, QLocale::system().name());

        // Load specific translation, try from GCROOT otherwise from binary
        QTranslator gcTranslator;
        QString translation_file = "/gc_" + lang.toString() + ".qm";
        if (gcTranslator.load(gcroot + translation_file))
            qDebug() << "Loaded translation from"+gcroot+translation_file;
        else
            gcTranslator.load(":translations" + translation_file);
        application->installTranslator(&gcTranslator);

        // Initialize metrics once the translator is installed
        RideMetricFactory::instance().initialize();

        // Initialize global registry once the translator is installed
        GcWindowRegistry::initialize();

        // initialise the trainDB
        trainDB = new TrainDB(home);

        // lets do what the command line says ...
        QVariant lastOpened;
        if(args.count() == 2) { // $ ./GoldenCheetah Mark -or- ./GoldenCheetah --server ~/athletedir

            // athlete
            if (!server) lastOpened = args.at(1);
            else home.cd(args.at(1));

        } else if (args.count() == 3) { // $ ./GoldenCheetah ~/Athletes Mark

            // first parameter is a folder that exists?
            if (QFileInfo(args.at(1)).isDir()) {
                home.cd(args.at(1));
            }

            // folder and athlete
            lastOpened = args.at(2);

        } else {

            // no parameters passed lets open the last athlete we worked with
            lastOpened = appsettings->value(NULL, GC_SETTINGS_LAST);

            // does lastopened Directory exists at all
            QDir lastOpenedDir(gcroot+"/"+lastOpened.toString());
            if (lastOpenedDir.exists()) {
                // but hang on, did they crash? if so we need to open with a menu
                appsettings->initializeQSettingsAthlete(gcroot, lastOpened.toString());
                if(appsettings->cvalue(lastOpened.toString(), GC_SAFEEXIT, true).toBool() != true)
                    lastOpened = QVariant();
            } else {
                lastOpened = QVariant();
            }
            
        }

#ifdef GC_WANT_HTTP

        // The API server offers webservices (default port 12021, see httpserver.ini)
        // This is to enable integration with R and similar
        if (appsettings->value(NULL, GC_START_HTTP, true).toBool() || server) {

            // notifications etc
            if (nogui) {
                qDebug()<<"Starting GoldenCheetah API web-services... (hit ^C to close)";
                qDebug()<<"Athlete directory:"<<home.absolutePath();
            } else {
                // switch off warnings if in gui mode
#ifndef GC_WANT_ALLDEBUG
#if QT_VERSION > 0x50000
                qInstallMessageHandler(myMessageOutput);
#else
                qInstallMsgHandler(myMessageOutput);
#endif
#endif
            }

            QString httpini = home.absolutePath() + "/httpserver.ini";
            if (!QFile(httpini).exists()) {

                // read default ini file
                QFile file(":webservice/httpserver.ini");
                QString content;
                if (file.open(QIODevice::ReadOnly)) {
                    content = file.readAll();
                    file.close();
                }

                // write default ini file
                QFile out(httpini);
                if (out.open(QIODevice::WriteOnly)) {
                
                    out.resize(0);
                    QTextStream stream(&out);
                    stream << content;
                    out.close();
                }
            }

            // use the default handler (just get an error page)
            QSettings* settings=new QSettings(httpini,QSettings::IniFormat,application);

            if (listener) {
                // when changing the Athlete Directory, there is already a listener running
                // close first to avoid errors
                listener->close();
            }
            listener=new HttpListener(settings,new APIWebService(home, application),application);

            // if not going on to launch a gui...
            if (nogui) {
                // catch ^C exit
                signal(SIGINT, sigabort);

                ret = application->exec();

                // stop web server if running
                qDebug()<<"Stopping GoldenCheetah API web-services...";
                listener->close();

                // and done
                terminate(0);
            }
        }
#endif

        // lets attempt to open as asked/remembered
        bool anyOpened = false;
        if (lastOpened != QVariant()) {
            QStringList list = lastOpened.toStringList();
            QStringListIterator i(list);
            while (i.hasNext()) {
                QString cyclist = i.next();
                QString homeDir = home.canonicalPath();
                if (home.cd(cyclist)) {
                    appsettings->initializeQSettingsAthlete(homeDir, cyclist);
                    GcUpgrade v3;
                    if (v3.upgradeConfirmedByUser(home)) {
                        MainWindow *mainWindow = new MainWindow(home);
                        mainWindow->show();
                        mainWindow->ridesAutoImport();
                        gc_opened++;
                        home.cdUp();
                        anyOpened = true;
                    } else {
                        delete trainDB;
                        terminate(0);
                    }
                }
            }
        }

        // ack, didn't manage to open an athlete
        // and the upgradeWarning was
        // lets ask the user which / create a new one
        if (!anyOpened) {
            ChooseCyclistDialog d(home, true);
            d.setModal(true);

            // choose cancel?
            if ((ret=d.exec()) != QDialog::Accepted) {
                delete trainDB;
                terminate(0);
            }

            // chosen, so lets get the choice..
            QString homeDir = home.canonicalPath();
            home.cd(d.choice());
            if (!home.exists()) {
                delete trainDB;
                terminate(0);
            }

            appsettings->initializeQSettingsAthlete(homeDir, d.choice());
            // .. and open a mainwindow
            GcUpgrade v3;
            if (v3.upgradeConfirmedByUser(home)) {
                MainWindow *mainWindow = new MainWindow(home);
                mainWindow->show();
                mainWindow->ridesAutoImport();
                gc_opened++;
            } else {
                delete trainDB;
                terminate(0);
            }
        }

        ret=application->exec();

        // close trainDB
        delete trainDB;

        // reset QSettings (global & Athlete)
        appsettings->clearGlobalAndAthletes();

#ifndef NOWEBKIT
        // clear web caches (stop warning of WebKit leaks)
        QWebSettings::clearMemoryCaches();
#endif

    } while (restarting);

    return ret;
}
Example #2
0
int
main(int argc, char *argv[])
{
    int ret=2; // return code from qapplication, default to error

    //
    // PROCESS COMMAND LINE SWITCHES
    //

    // snaffle arguments into a stringlist we can play with into sargs
    // and only keep non-switch args in the args string list
    QStringList sargs, args;
    for (int i=0; i<argc; i++) sargs << argv[i];

#ifdef GC_DEBUG
    bool debug = true;
#else
    bool debug = false;
#endif

    bool help = false;

    // honour command line switches
    foreach (QString arg, sargs) {

        // help or version requested
        if (arg == "--help" || arg == "--version") {

            help = true;
            fprintf(stderr, "GoldenCheetah %s (%d)\nusage: GoldenCheetah [[directory] athlete]\n\n", VERSION_STRING, VERSION_LATEST);
            fprintf(stderr, "--help or --version to print this message and exit\n");
#ifdef GC_DEBUG
            fprintf(stderr, "--debug             to turn on redirection of messages to goldencheetah.log [debug build]\n");
#else
            fprintf(stderr, "--debug             to direct diagnostic messages to the terminal instead of goldencheetah.log\n");
#endif
            fprintf (stderr, "\nSpecify the folder and/or athlete to open on startup\n");
            fprintf(stderr, "If no parameters are passed it will reopen the last athlete.\n\n");

        } else if (arg == "--debug") {

#ifdef GC_DEBUG
            // debug, so don't redirect stderr!
            debug = false;
#else
            debug = true;
#endif

        } else {

            // not switches !
            args << arg;
        }
    }

    // help or version printed so just exit now
    if (help) {
        exit(0);
    }

    //
    // INITIALISE ONE TIME OBJECTS
    //

#ifdef Q_OS_X11
    XInitThreads();
#endif

#ifdef Q_OS_MACX
    if ( QSysInfo::MacintoshVersion > QSysInfo::MV_10_8 )
    {
        // fix Mac OS X 10.9 (mavericks) font issue
        // https://bugreports.qt-project.org/browse/QTBUG-32789
        QFont::insertSubstitution("LucidaGrande", "Lucida Grande");
    }
#endif

    // create the application -- only ever ONE regardless of restarts
    application = new QApplication(argc, argv);

#ifdef Q_OS_MAC
    // get an autorelease pool setup
    static CocoaInitializer cocoaInitializer;
#endif

    // set defaultfont
    QFont font;
    font.fromString(appsettings->value(NULL, GC_FONT_DEFAULT, QFont().toString()).toString());
    font.setPointSize(appsettings->value(NULL, GC_FONT_DEFAULT_SIZE, 12).toInt());
    application->setFont(font); // set default font


    //
    // OPEN FIRST MAINWINDOW
    //
    do {

        // lets not restart endlessly
        restarting = false;

        //this is the path within the current directory where GC will look for
        //files to allow USB stick support
        QString localLibraryPath="Library/GoldenCheetah";

        //this is the path that used to be used for all platforms
        //now different platforms will use their own path
        //this path is checked first to make things easier for long-time users
        QString oldLibraryPath=QDir::home().path()+"/Library/GoldenCheetah";

        //these are the new platform-dependent library paths
#if defined(Q_OS_MACX)
        QString libraryPath="Library/GoldenCheetah";
#elif defined(Q_OS_WIN)
#if QT_VERSION > 0x050000 // windows and qt5
        QStringList paths=QStandardPaths::standardLocations(QStandardPaths::DataLocation);
	    QString libraryPath = paths.at(0) + "/GoldenCheetah";
#else // windows not qt5
        QString libraryPath=QDesktopServices::storageLocation(QDesktopServices::DataLocation) + "/GoldenCheetah";
#endif // qt5
#else // not windows or osx (must be Linux or OpenBSD)
        // Q_OS_LINUX et al
        QString libraryPath=".goldencheetah";
#endif //

        // or did we override in settings?
        QString sh;
        if ((sh=appsettings->value(NULL, GC_HOMEDIR).toString()) != "") localLibraryPath = sh;

        // lets try the local library we've worked out...
        QDir home = QDir();
        if(home.exists(localLibraryPath)) {

            home.cd(localLibraryPath);

        } else {

            // YIKES !! The directory we should be using doesn't exist!
            home = QDir::home();
            if (home.exists(oldLibraryPath)) { // there is an old style path, lets fo there
                home.cd(oldLibraryPath);
            } else {

                if (!home.exists(libraryPath)) {
                    if (!home.mkpath(libraryPath)) {

                    qDebug()<<"Failed to create library path\n";
                    exit(0);

                    }
                }
                home.cd(libraryPath);
            }
        }

        // set global root directory
        gcroot = home.absolutePath();

        // now redirect stderr
#ifndef WIN32
        if (!debug) nostderr(home.absolutePath());
#endif

        // install QT Translator to enable QT Dialogs translation
        // we may have restarted JUST to get this!
        QTranslator qtTranslator;
        qtTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath));
        application->installTranslator(&qtTranslator);

        // Language setting (default to system locale)
        QVariant lang = appsettings->value(NULL, GC_LANG, QLocale::system().name());

        // Load specific translation
        QTranslator gcTranslator;
        gcTranslator.load(":translations/gc_" + lang.toString() + ".qm");
        application->installTranslator(&gcTranslator);

        // Initialize metrics once the translator is installed
        RideMetricFactory::instance().initialize();

        // Initialize global registry once the translator is installed
        GcWindowRegistry::initialize();

        // initialise the trainDB
        trainDB = new TrainDB(home);

        // lets do what the command line says ...
        QVariant lastOpened;
        if(args.count() == 2) { // $ ./GoldenCheetah Mark

            // athlete
            lastOpened = args.at(1);

        } else if (args.count() == 3) { // $ ./GoldenCheetah ~/Athletes Mark

            // first parameter is a folder that exists?
            if (QFileInfo(args.at(1)).isDir()) {
                home.cd(args.at(1));
            }

            // folder and athlete
            lastOpened = args.at(2);

        } else {

            // no parameters passed lets open the last athlete we worked with
            lastOpened = appsettings->value(NULL, GC_SETTINGS_LAST);

            // but hang on, did they crash? if so we need to open with a menu
            if(appsettings->cvalue(lastOpened.toString(), GC_SAFEEXIT, true).toBool() != true)
                lastOpened = QVariant();
            
        }

        // lets attempt to open as asked/remembered
        bool anyOpened = false;
        if (lastOpened != QVariant()) {
            QStringList list = lastOpened.toStringList();
            QStringListIterator i(list);
            while (i.hasNext()) {
                QString cyclist = i.next();
                if (home.cd(cyclist)) {
                    MainWindow *mainWindow = new MainWindow(home);
                    mainWindow->show();
                    home.cdUp();
                    anyOpened = true;
                }
            }
        }

        // ack, didn't manage to open an athlete
        // lets ask the user which / create a new one
        if (!anyOpened) {
            ChooseCyclistDialog d(home, true);
            d.setModal(true);

            // choose cancel?
            if ((ret=d.exec()) != QDialog::Accepted) {
                delete trainDB;
                return ret;
            }

            // chosen, so lets get the choice..
            home.cd(d.choice());
            if (!home.exists()) {
                delete trainDB;
                exit(0);
            }

            // .. and open a mainwindow
            MainWindow *mainWindow = new MainWindow(home);
            mainWindow->show();
        }

        ret=application->exec();

        // close trainDB
        delete trainDB;

    } while (restarting);

    return ret;
}