main.cpp 19.9 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
3
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
con's avatar
con committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
con's avatar
con committed
7
**
hjk's avatar
hjk committed
8 9 10 11 12 13 14
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
15
**
16
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17 18 19 20 21 22
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23
**
hjk's avatar
hjk committed
24 25
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
con's avatar
con committed
26 27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
con's avatar
con committed
29 30

#include "qtsingleapplication.h"
31
#include "../tools/qtcreatorcrashhandler/crashhandlersetup.h"
con's avatar
con committed
32

33
#include <app/app_version.h>
con's avatar
con committed
34
#include <extensionsystem/iplugin.h>
35
#include <extensionsystem/pluginerroroverview.h>
Yuchen Deng's avatar
Yuchen Deng committed
36 37
#include <extensionsystem/pluginmanager.h>
#include <extensionsystem/pluginspec.h>
con's avatar
con committed
38

39 40 41 42 43 44 45 46 47 48 49
#include <QDebug>
#include <QDir>
#include <QFileInfo>
#include <QLibraryInfo>
#include <QSettings>
#include <QTextStream>
#include <QThreadPool>
#include <QTimer>
#include <QTranslator>
#include <QUrl>
#include <QVariant>
con's avatar
con committed
50

51
#include <QNetworkProxyFactory>
52

53 54 55
#include <QApplication>
#include <QDesktopServices>
#include <QMessageBox>
con's avatar
con committed
56

Marco Bubke's avatar
Marco Bubke committed
57 58 59 60
#ifdef ENABLE_QT_BREAKPAD
#include <qtsystemexceptionhandler.h>
#endif

61 62
using namespace ExtensionSystem;

63
enum { OptionIndent = 4, DescriptionIndent = 34 };
con's avatar
con committed
64

65 66 67
static const char appNameC[] = "Qt Creator";
static const char corePluginNameC[] = "Core";
static const char fixedOptionsC[] =
con's avatar
con committed
68 69
" [OPTION]... [FILE]...\n"
"Options:\n"
70 71
"    -help                         Display this help\n"
"    -version                      Display program version\n"
72 73
"    -client                       Attempt to connect to already running first instance\n"
"    -settingspath <path>          Override the default path where user settings are stored\n"
74
"    -pid <pid>                    Attempt to connect to instance given by pid\n"
75 76
"    -block                        Block until editor is closed\n"
"    -pluginpath <path>            Add a custom search path for plugins\n";
con's avatar
con committed
77

78 79 80 81 82 83 84
static const char HELP_OPTION1[] = "-h";
static const char HELP_OPTION2[] = "-help";
static const char HELP_OPTION3[] = "/h";
static const char HELP_OPTION4[] = "--help";
static const char VERSION_OPTION[] = "-version";
static const char CLIENT_OPTION[] = "-client";
static const char SETTINGS_OPTION[] = "-settingspath";
85
static const char PID_OPTION[] = "-pid";
86
static const char BLOCK_OPTION[] = "-block";
87
static const char PLUGINPATH_OPTION[] = "-pluginpath";
con's avatar
con committed
88

89
typedef QList<PluginSpec *> PluginSpecSet;
con's avatar
con committed
90 91

// Helpers for displaying messages. Note that there is no console on Windows.
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
92
#ifdef Q_OS_WIN
con's avatar
con committed
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
// Format as <pre> HTML
static inline void toHtml(QString &t)
{
    t.replace(QLatin1Char('&'), QLatin1String("&amp;"));
    t.replace(QLatin1Char('<'), QLatin1String("&lt;"));
    t.replace(QLatin1Char('>'), QLatin1String("&gt;"));
    t.insert(0, QLatin1String("<html><pre>"));
    t.append(QLatin1String("</pre></html>"));
}

static void displayHelpText(QString t) // No console on Windows.
{
    toHtml(t);
    QMessageBox::information(0, QLatin1String(appNameC), t);
}

static void displayError(const QString &t) // No console on Windows.
{
    QMessageBox::critical(0, QLatin1String(appNameC), t);
}

#else

static void displayHelpText(const QString &t)
{
118
    qWarning("%s", qPrintable(t));
con's avatar
con committed
119 120 121 122
}

static void displayError(const QString &t)
{
123
    qCritical("%s", qPrintable(t));
con's avatar
con committed
124 125 126 127
}

#endif

128
static void printVersion(const PluginSpec *coreplugin)
con's avatar
con committed
129 130 131 132
{
    QString version;
    QTextStream str(&version);
    str << '\n' << appNameC << ' ' << coreplugin->version()<< " based on Qt " << qVersion() << "\n\n";
133
    PluginManager::formatPluginVersions(str);
con's avatar
con committed
134 135 136 137
    str << '\n' << coreplugin->copyright() << '\n';
    displayHelpText(version);
}

138
static void printHelp(const QString &a0)
con's avatar
con committed
139 140 141
{
    QString help;
    QTextStream str(&help);
Yuchen Deng's avatar
Yuchen Deng committed
142
    str << "Usage: " << a0 << fixedOptionsC;
143 144
    PluginManager::formatOptions(str, OptionIndent, DescriptionIndent);
    PluginManager::formatPluginOptions(str, OptionIndent, DescriptionIndent);
con's avatar
con committed
145 146 147 148 149 150 151 152
    displayHelpText(help);
}

static inline QString msgCoreLoadFailure(const QString &why)
{
    return QCoreApplication::translate("Application", "Failed to load core: %1").arg(why);
}

153
static inline int askMsgSendFailed()
con's avatar
con committed
154
{
155
    return QMessageBox::question(0, QApplication::translate("Application","Could not send message"),
156
                                 QCoreApplication::translate("Application", "Unable to send command line arguments to the already running instance. "
157 158 159
                                                             "It appears to be not responding. Do you want to start a new instance of Creator?"),
                                 QMessageBox::Yes | QMessageBox::No | QMessageBox::Retry,
                                 QMessageBox::Retry);
con's avatar
con committed
160 161
}

162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
// taken from utils/fileutils.cpp. We can not use utils here since that depends app_version.h.
static bool copyRecursively(const QString &srcFilePath,
                            const QString &tgtFilePath)
{
    QFileInfo srcFileInfo(srcFilePath);
    if (srcFileInfo.isDir()) {
        QDir targetDir(tgtFilePath);
        targetDir.cdUp();
        if (!targetDir.mkdir(QFileInfo(tgtFilePath).fileName()))
            return false;
        QDir sourceDir(srcFilePath);
        QStringList fileNames = sourceDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System);
        foreach (const QString &fileName, fileNames) {
            const QString newSrcFilePath
                    = srcFilePath + QLatin1Char('/') + fileName;
            const QString newTgtFilePath
                    = tgtFilePath + QLatin1Char('/') + fileName;
            if (!copyRecursively(newSrcFilePath, newTgtFilePath))
                return false;
        }
    } else {
        if (!QFile::copy(srcFilePath, tgtFilePath))
            return false;
    }
    return true;
}

Eike Ziller's avatar
Eike Ziller committed
189
static inline QStringList getPluginPaths()
con's avatar
con committed
190 191 192 193 194
{
    QStringList rc;
    // Figure out root:  Up one from 'bin'
    QDir rootDir = QApplication::applicationDirPath();
    rootDir.cdUp();
195
    const QString rootDirPath = rootDir.canonicalPath();
196
#if !defined(Q_OS_MAC)
197
    // 1) "plugins" (Win/Linux)
con's avatar
con committed
198
    QString pluginPath = rootDirPath;
199
    pluginPath += QLatin1Char('/');
200
    pluginPath += QLatin1String(IDE_LIBRARY_BASENAME);
201
    pluginPath += QLatin1String("/qtcreator/plugins");
con's avatar
con committed
202
    rc.push_back(pluginPath);
203
#else
204
    // 2) "PlugIns" (OS X)
205
    QString pluginPath = rootDirPath;
206
    pluginPath += QLatin1String("/PlugIns");
con's avatar
con committed
207
    rc.push_back(pluginPath);
208
#endif
209 210
    // 3) <localappdata>/plugins/<ideversion>
    //    where <localappdata> is e.g.
211 212 213
    //    "%LOCALAPPDATA%\QtProject\qtcreator" on Windows Vista and later
    //    "$XDG_DATA_HOME/data/QtProject/qtcreator" or "~/.local/share/data/QtProject/qtcreator" on Linux
    //    "~/Library/Application Support/QtProject/Qt Creator" on Mac
214
    pluginPath = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
215 216 217
    pluginPath += QLatin1Char('/')
            + QLatin1String(Core::Constants::IDE_SETTINGSVARIANT_STR)
            + QLatin1Char('/');
218 219 220 221 222 223
#if !defined(Q_OS_MAC)
    pluginPath += QLatin1String("qtcreator");
#else
    pluginPath += QLatin1String("Qt Creator");
#endif
    pluginPath += QLatin1String("/plugins/");
224 225
    pluginPath += QLatin1String(Core::Constants::IDE_VERSION_LONG);
    rc.push_back(pluginPath);
con's avatar
con committed
226 227 228
    return rc;
}

229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
static QSettings *createUserSettings()
{
    return new QSettings(QSettings::IniFormat, QSettings::UserScope,
                         QLatin1String(Core::Constants::IDE_SETTINGSVARIANT_STR),
                         QLatin1String("QtCreator"));
}

static inline QSettings *userSettings()
{
    QSettings *settings = createUserSettings();
    const QString fromVariant = QLatin1String(Core::Constants::IDE_COPY_SETTINGS_FROM_VARIANT_STR);
    if (fromVariant.isEmpty())
        return settings;

    // Copy old settings to new ones:
    QFileInfo pathFi = QFileInfo(settings->fileName());
    if (pathFi.exists()) // already copied.
        return settings;

    QDir destDir = pathFi.absolutePath();
    if (!destDir.exists())
        destDir.mkpath(pathFi.absolutePath());

    QDir srcDir = destDir;
    srcDir.cdUp();
    if (!srcDir.cd(fromVariant))
        return settings;

    if (srcDir == destDir) // Nothing to copy and no settings yet
        return settings;

    QStringList entries = srcDir.entryList();
    foreach (const QString &file, entries) {
        const QString lowerFile = file.toLower();
        if (lowerFile.startsWith(QLatin1String("profiles.xml"))
                || lowerFile.startsWith(QLatin1String("toolchains.xml"))
                || lowerFile.startsWith(QLatin1String("qtversion.xml"))
                || lowerFile.startsWith(QLatin1String("devices.xml"))
                || lowerFile.startsWith(QLatin1String("qtcreator.")))
            QFile::copy(srcDir.absoluteFilePath(file), destDir.absoluteFilePath(file));
        if (file == QLatin1String("qtcreator"))
            copyRecursively(srcDir.absoluteFilePath(file), destDir.absoluteFilePath(file));
    }

    // Make sure to use the copied settings:
    delete settings;
    return createUserSettings();
}

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
278 279 280 281 282 283
#ifdef Q_OS_MAC
#  define SHARE_PATH "/../Resources"
#else
#  define SHARE_PATH "/../share/qtcreator"
#endif

con's avatar
con committed
284 285
int main(int argc, char **argv)
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
286
#ifdef Q_OS_MAC
con's avatar
con committed
287 288 289
    // increase the number of file that can be opened in Qt Creator.
    struct rlimit rl;
    getrlimit(RLIMIT_NOFILE, &rl);
290 291

    rl.rlim_cur = qMin((rlim_t)OPEN_MAX, rl.rlim_max);
con's avatar
con committed
292 293 294
    setrlimit(RLIMIT_NOFILE, &rl);
#endif

Nikolai Kosjar's avatar
Nikolai Kosjar committed
295
#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
296
    // QML is unusable with the xlib backend
297
    QApplication::setGraphicsSystem(QLatin1String("raster"));
298 299
#endif

con's avatar
con committed
300
    SharedTools::QtSingleApplication app((QLatin1String(appNameC)), argc, argv);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
301

302 303 304
    const int threadCount = QThreadPool::globalInstance()->maxThreadCount();
    QThreadPool::globalInstance()->setMaxThreadCount(qMax(4, 2 * threadCount));

305 306
    setupCrashHandler(); // Display a backtrace once a serious signal is delivered.

Marco Bubke's avatar
Marco Bubke committed
307 308 309 310
#ifdef ENABLE_QT_BREAKPAD
    QtSystemExceptionHandler systemExceptionHandler;
#endif

311 312 313 314
#if QT_VERSION >= 0x050100
    app.setAttribute(Qt::AA_UseHighDpiPixmaps);
#endif

315 316 317 318
    // Manually determine -settingspath command line option
    // We can't use the regular way of the plugin manager, because that needs to parse pluginspecs
    // but the settings path can influence which plugins are enabled
    QString settingsPath;
319
    QStringList customPluginPaths;
Yuchen Deng's avatar
Yuchen Deng committed
320
    QStringList arguments = app.arguments(); // adapted arguments list is passed to plugin manager later
321 322 323 324 325 326 327 328 329
    QMutableStringListIterator it(arguments);
    while (it.hasNext()) {
        const QString &arg = it.next();
        if (arg == QLatin1String(SETTINGS_OPTION)) {
            it.remove();
            if (it.hasNext()) {
                settingsPath = QDir::fromNativeSeparators(it.next());
                it.remove();
            }
330 331 332 333 334 335
        } else if (arg == QLatin1String(PLUGINPATH_OPTION)) {
            it.remove();
            if (it.hasNext()) {
                customPluginPaths << QDir::fromNativeSeparators(it.next());
                it.remove();
            }
336 337 338 339 340
        }
    }
    if (!settingsPath.isEmpty())
        QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, settingsPath);

341
    // Must be done before any QSettings class is created
con's avatar
con committed
342
    QSettings::setPath(QSettings::IniFormat, QSettings::SystemScope,
Yuchen Deng's avatar
Yuchen Deng committed
343
                       QCoreApplication::applicationDirPath() + QLatin1String(SHARE_PATH));
344
    QSettings::setDefaultFormat(QSettings::IniFormat);
345
    // plugin manager takes control of this settings object
346 347
    QSettings *settings = userSettings();

348
    QSettings *globalSettings = new QSettings(QSettings::IniFormat, QSettings::SystemScope,
349 350
                                              QLatin1String(Core::Constants::IDE_SETTINGSVARIANT_STR),
                                              QLatin1String("QtCreator"));
351 352 353 354
    PluginManager pluginManager;
    PluginManager::setFileExtension(QLatin1String("pluginspec"));
    PluginManager::setGlobalSettings(globalSettings);
    PluginManager::setSettings(settings);
355

356 357 358
    QTranslator translator;
    QTranslator qtTranslator;
    QStringList uiLanguages;
359 360
// uiLanguages crashes on Windows with 4.8.0 release builds
#if (QT_VERSION >= 0x040801) || (QT_VERSION >= 0x040800 && !defined(Q_OS_WIN))
361 362 363 364
    uiLanguages = QLocale::system().uiLanguages();
#else
    uiLanguages << QLocale::system().name();
#endif
365
    QString overrideLanguage = settings->value(QLatin1String("General/OverrideLanguage")).toString();
366 367
    if (!overrideLanguage.isEmpty())
        uiLanguages.prepend(overrideLanguage);
368
    const QString &creatorTrPath = QCoreApplication::applicationDirPath()
Yuchen Deng's avatar
Yuchen Deng committed
369
            + QLatin1String(SHARE_PATH "/translations");
370
    foreach (QString locale, uiLanguages) {
Liang Qi's avatar
Liang Qi committed
371 372 373
#if (QT_VERSION >= 0x050000)
        locale = QLocale(locale).name();
#else
374
        locale.replace(QLatin1Char('-'), QLatin1Char('_')); // work around QTBUG-25973
Liang Qi's avatar
Liang Qi committed
375
#endif
376 377 378 379 380 381 382 383 384 385
        if (translator.load(QLatin1String("qtcreator_") + locale, creatorTrPath)) {
            const QString &qtTrPath = QLibraryInfo::location(QLibraryInfo::TranslationsPath);
            const QString &qtTrFile = QLatin1String("qt_") + locale;
            // Binary installer puts Qt tr files into creatorTrPath
            if (qtTranslator.load(qtTrFile, qtTrPath) || qtTranslator.load(qtTrFile, creatorTrPath)) {
                app.installTranslator(&translator);
                app.installTranslator(&qtTranslator);
                app.setProperty("qtc_locale", locale);
                break;
            }
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
386
            translator.load(QString()); // unload()
387 388 389 390 391 392
        } else if (locale == QLatin1String("C") /* overrideLanguage == "English" */) {
            // use built-in
            break;
        } else if (locale.startsWith(QLatin1String("en")) /* "English" is built-in */) {
            // use built-in
            break;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
393 394 395
        }
    }

396
    // Make sure we honor the system's proxy settings
397
#if defined(Q_OS_UNIX)
398 399
    QUrl proxyUrl(QString::fromLatin1(qgetenv("http_proxy")));
    if (proxyUrl.isValid()) {
400
        QNetworkProxy proxy(QNetworkProxy::HttpProxy, proxyUrl.host(),
401 402
                            proxyUrl.port(), proxyUrl.userName(), proxyUrl.password());
        QNetworkProxy::setApplicationProxy(proxy);
Eike Ziller's avatar
Eike Ziller committed
403
# if defined(Q_OS_MAC) // unix and mac
404
    } else {
405
        QNetworkProxyFactory::setUseSystemConfiguration(true);
Eike Ziller's avatar
Eike Ziller committed
406
# endif
407
    }
Eike Ziller's avatar
Eike Ziller committed
408 409
#else // windows
    QNetworkProxyFactory::setUseSystemConfiguration(true);
410
#endif
con's avatar
con committed
411
    // Load
412
    const QStringList pluginPaths = getPluginPaths() + customPluginPaths;
413
    PluginManager::setPluginPaths(pluginPaths);
hjk's avatar
hjk committed
414
    QMap<QString, QString> foundAppOptions;
con's avatar
con committed
415
    if (arguments.size() > 1) {
hjk's avatar
hjk committed
416
        QMap<QString, bool> appOptions;
con's avatar
con committed
417 418 419 420 421 422
        appOptions.insert(QLatin1String(HELP_OPTION1), false);
        appOptions.insert(QLatin1String(HELP_OPTION2), false);
        appOptions.insert(QLatin1String(HELP_OPTION3), false);
        appOptions.insert(QLatin1String(HELP_OPTION4), false);
        appOptions.insert(QLatin1String(VERSION_OPTION), false);
        appOptions.insert(QLatin1String(CLIENT_OPTION), false);
423
        appOptions.insert(QLatin1String(PID_OPTION), true);
424
        appOptions.insert(QLatin1String(BLOCK_OPTION), false);
con's avatar
con committed
425
        QString errorMessage;
426
        if (!PluginManager::parseOptions(arguments, appOptions, &foundAppOptions, &errorMessage)) {
con's avatar
con committed
427
            displayError(errorMessage);
428
            printHelp(QFileInfo(app.applicationFilePath()).baseName());
con's avatar
con committed
429 430 431 432
            return -1;
        }
    }

433 434 435
    const PluginSpecSet plugins = PluginManager::plugins();
    PluginSpec *coreplugin = 0;
    foreach (PluginSpec *spec, plugins) {
con's avatar
con committed
436 437 438 439 440 441
        if (spec->name() == QLatin1String(corePluginNameC)) {
            coreplugin = spec;
            break;
        }
    }
    if (!coreplugin) {
442
        QString nativePaths = QDir::toNativeSeparators(pluginPaths.join(QLatin1String(",")));
443
        const QString reason = QCoreApplication::translate("Application", "Could not find 'Core.pluginspec' in %1").arg(nativePaths);
con's avatar
con committed
444 445 446 447 448 449 450 451
        displayError(msgCoreLoadFailure(reason));
        return 1;
    }
    if (coreplugin->hasError()) {
        displayError(msgCoreLoadFailure(coreplugin->errorString()));
        return 1;
    }
    if (foundAppOptions.contains(QLatin1String(VERSION_OPTION))) {
452
        printVersion(coreplugin);
con's avatar
con committed
453 454 455 456 457 458
        return 0;
    }
    if (foundAppOptions.contains(QLatin1String(HELP_OPTION1))
            || foundAppOptions.contains(QLatin1String(HELP_OPTION2))
            || foundAppOptions.contains(QLatin1String(HELP_OPTION3))
            || foundAppOptions.contains(QLatin1String(HELP_OPTION4))) {
459
        printHelp(QFileInfo(app.applicationFilePath()).baseName());
con's avatar
con committed
460 461 462
        return 0;
    }

463 464 465 466 467 468 469 470 471
    qint64 pid = -1;
    if (foundAppOptions.contains(QLatin1String(PID_OPTION))) {
        QString pidString = foundAppOptions.value(QLatin1String(PID_OPTION));
        bool pidOk;
        qint64 tmpPid = pidString.toInt(&pidOk);
        if (pidOk)
            pid = tmpPid;
    }

472 473 474 475
    bool isBlock = foundAppOptions.contains(QLatin1String(BLOCK_OPTION));
    if (app.isRunning() && (pid != -1 || isBlock
                            || foundAppOptions.contains(QLatin1String(CLIENT_OPTION)))) {
        app.setBlock(isBlock);
476
        if (app.sendMessage(PluginManager::serializedArguments(), 5000 /*timeout*/, pid))
477 478 479
            return 0;

        // Message could not be send, maybe it was in the process of quitting
480
        if (app.isRunning(pid)) {
481 482
            // Nah app is still running, ask the user
            int button = askMsgSendFailed();
483
            while (button == QMessageBox::Retry) {
484
                if (app.sendMessage(PluginManager::serializedArguments(), 5000 /*timeout*/, pid))
485
                    return 0;
486
                if (!app.isRunning(pid)) // App quit while we were trying so start a new creator
487 488 489 490 491 492
                    button = QMessageBox::Yes;
                else
                    button = askMsgSendFailed();
            }
            if (button == QMessageBox::No)
                return -1;
493 494
        }
    }
con's avatar
con committed
495

496
    PluginManager::loadPlugins();
con's avatar
con committed
497 498 499 500
    if (coreplugin->hasError()) {
        displayError(msgCoreLoadFailure(coreplugin->errorString()));
        return 1;
    }
501 502
    if (PluginManager::hasError()) {
        PluginErrorOverview errorOverview;
Yuchen Deng's avatar
Yuchen Deng committed
503
        errorOverview.exec();
504 505
    }

Orgad Shaneh's avatar
Orgad Shaneh committed
506
    // Set up remote arguments.
507 508
    QObject::connect(&app, SIGNAL(messageReceived(QString,QObject*)),
                     &pluginManager, SLOT(remoteArguments(QString,QObject*)));
Yuchen Deng's avatar
Yuchen Deng committed
509 510 511

    QObject::connect(&app, SIGNAL(fileOpenRequest(QString)), coreplugin->plugin(),
                     SLOT(fileOpenRequest(QString)));
con's avatar
con committed
512

513 514 515 516
    // quit when last window (relevant window, see WA_QuitOnClose) is closed
    // this should actually be the default, but doesn't work in Qt 5
    // QTBUG-31569
    QObject::connect(&app, SIGNAL(lastWindowClosed()), &app, SLOT(quit()));
517 518 519
    // shutdown plugin manager on the exit
    QObject::connect(&app, SIGNAL(aboutToQuit()), &pluginManager, SLOT(shutdown()));

520
#ifdef WITH_TESTS
con's avatar
con committed
521
    // Do this after the event loop has started
522
    if (PluginManager::testRunRequested())
523 524
        QTimer::singleShot(100, &pluginManager, SLOT(startTests()));
#endif
525

526 527 528
    const int r = app.exec();
    cleanupCrashHandler();
    return r;
con's avatar
con committed
529
}