/************************************************************************** ** ** This file is part of Qt Creator ** ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** ** GNU Lesser General Public License Usage ** ** 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. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** If you have questions regarding the use of this file, please contact ** Nokia at qt-info@nokia.com. ** **************************************************************************/ #include "qtsingleapplication.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef ENABLE_QT_BREAKPAD #include #endif enum { OptionIndent = 4, DescriptionIndent = 34 }; static const char appNameC[] = "Qt Creator"; static const char corePluginNameC[] = "Core"; static const char fixedOptionsC[] = " [OPTION]... [FILE]...\n" "Options:\n" " -help Display this help\n" " -version Display program version\n" " -client Attempt to connect to already running instance\n" " -settingspath Override the default path where user settings are stored\n"; 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"; typedef QList PluginSpecSet; // Helpers for displaying messages. Note that there is no console on Windows. #ifdef Q_OS_WIN // Format as
 HTML
static inline void toHtml(QString &t)
{
    t.replace(QLatin1Char('&'), QLatin1String("&"));
    t.replace(QLatin1Char('<'), QLatin1String("<"));
    t.replace(QLatin1Char('>'), QLatin1String(">"));
    t.insert(0, QLatin1String("
"));
    t.append(QLatin1String("
")); } 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) { qWarning("%s", qPrintable(t)); } static void displayError(const QString &t) { qCritical("%s", qPrintable(t)); } #endif static void printVersion(const ExtensionSystem::PluginSpec *coreplugin, const ExtensionSystem::PluginManager &pm) { QString version; QTextStream str(&version); str << '\n' << appNameC << ' ' << coreplugin->version()<< " based on Qt " << qVersion() << "\n\n"; pm.formatPluginVersions(str); str << '\n' << coreplugin->copyright() << '\n'; displayHelpText(version); } static void printHelp(const QString &a0, const ExtensionSystem::PluginManager &pm) { QString help; QTextStream str(&help); str << "Usage: " << a0 << fixedOptionsC; ExtensionSystem::PluginManager::formatOptions(str, OptionIndent, DescriptionIndent); pm.formatPluginOptions(str, OptionIndent, DescriptionIndent); displayHelpText(help); } static inline QString msgCoreLoadFailure(const QString &why) { return QCoreApplication::translate("Application", "Failed to load core: %1").arg(why); } static inline int askMsgSendFailed() { return QMessageBox::question(0, QApplication::translate("Application","Could not send message"), QCoreApplication::translate("Application", "Unable to send command line arguments to the already running instance. " "It appears to be not responding. Do you want to start a new instance of Creator?"), QMessageBox::Yes | QMessageBox::No | QMessageBox::Retry, QMessageBox::Retry); } static inline QStringList getPluginPaths() { QStringList rc; // Figure out root: Up one from 'bin' QDir rootDir = QApplication::applicationDirPath(); rootDir.cdUp(); const QString rootDirPath = rootDir.canonicalPath(); #if !defined(Q_OS_MAC) // 1) "plugins" (Win/Linux) QString pluginPath = rootDirPath; pluginPath += QLatin1Char('/'); pluginPath += QLatin1String(IDE_LIBRARY_BASENAME); pluginPath += QLatin1String("/qtcreator/plugins"); rc.push_back(pluginPath); #else // 2) "PlugIns" (OS X) QString pluginPath = rootDirPath; pluginPath += QLatin1String("/PlugIns"); rc.push_back(pluginPath); #endif // 3) /plugins/ // where is e.g. // :\Users\\AppData\Local\Nokia\qtcreator on Windows Vista and later // $XDG_DATA_HOME or ~/.local/share/Nokia/qtcreator on Linux // ~/Library/Application Support/Nokia/Qt Creator on Mac pluginPath = QDesktopServices::storageLocation(QDesktopServices::DataLocation); pluginPath += QLatin1String("/Nokia/"); #if !defined(Q_OS_MAC) pluginPath += QLatin1String("qtcreator"); #else pluginPath += QLatin1String("Qt Creator"); #endif pluginPath += QLatin1String("/plugins/"); pluginPath += QLatin1String(Core::Constants::IDE_VERSION_LONG); rc.push_back(pluginPath); return rc; } #ifdef Q_OS_MAC # define SHARE_PATH "/../Resources" #else # define SHARE_PATH "/../share/qtcreator" #endif int main(int argc, char **argv) { #ifdef Q_OS_MAC // increase the number of file that can be opened in Qt Creator. struct rlimit rl; getrlimit(RLIMIT_NOFILE, &rl); rl.rlim_cur = qMin((rlim_t)OPEN_MAX, rl.rlim_max); setrlimit(RLIMIT_NOFILE, &rl); #endif #ifdef Q_WS_X11 // QML is unusable with the xlib backend QApplication::setGraphicsSystem("raster"); #endif SharedTools::QtSingleApplication app((QLatin1String(appNameC)), argc, argv); const int threadCount = QThreadPool::globalInstance()->maxThreadCount(); QThreadPool::globalInstance()->setMaxThreadCount(qMax(4, 2 * threadCount)); #ifdef ENABLE_QT_BREAKPAD QtSystemExceptionHandler systemExceptionHandler; #endif QTranslator translator; QTranslator qtTranslator; QString locale = QLocale::system().name(); // 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; QStringList arguments = app.arguments(); /* adapted arguments list is passed to plugin manager later */ 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(); } } } if (!settingsPath.isEmpty()) QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, settingsPath); // Must be done before any QSettings class is created QSettings::setPath(QSettings::IniFormat, QSettings::SystemScope, QCoreApplication::applicationDirPath()+QLatin1String(SHARE_PATH)); // plugin manager takes control of this settings object QSettings *settings = new QSettings(QSettings::IniFormat, QSettings::UserScope, QLatin1String("Nokia"), QLatin1String("QtCreator")); ExtensionSystem::PluginManager pluginManager; pluginManager.setFileExtension(QLatin1String("pluginspec")); pluginManager.setSettings(settings); locale = settings->value("General/OverrideLanguage", locale).toString(); const QString &creatorTrPath = QCoreApplication::applicationDirPath() + QLatin1String(SHARE_PATH "/translations"); 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); } else { translator.load(QString()); // unload() } } // Make sure we honor the system's proxy settings #if defined(Q_OS_UNIX) QUrl proxyUrl(QString::fromLatin1(qgetenv("http_proxy"))); if (proxyUrl.isValid()) { QNetworkProxy proxy(QNetworkProxy::HttpProxy, proxyUrl.host(), proxyUrl.port(), proxyUrl.userName(), proxyUrl.password()); QNetworkProxy::setApplicationProxy(proxy); } #if defined(Q_OS_MAC) else { QNetworkProxyFactory::setUseSystemConfiguration(true); } #endif #endif // Load const QStringList pluginPaths = getPluginPaths(); pluginManager.setPluginPaths(pluginPaths); QMap foundAppOptions; if (arguments.size() > 1) { QMap appOptions; 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); QString errorMessage; if (!pluginManager.parseOptions(arguments, appOptions, &foundAppOptions, &errorMessage)) { displayError(errorMessage); printHelp(QFileInfo(app.applicationFilePath()).baseName(), pluginManager); return -1; } } const PluginSpecSet plugins = pluginManager.plugins(); ExtensionSystem::PluginSpec *coreplugin = 0; foreach (ExtensionSystem::PluginSpec *spec, plugins) { if (spec->name() == QLatin1String(corePluginNameC)) { coreplugin = spec; break; } } if (!coreplugin) { QString nativePaths = QDir::toNativeSeparators(pluginPaths.join(QLatin1String(","))); const QString reason = QCoreApplication::translate("Application", "Could not find 'Core.pluginspec' in %1").arg(nativePaths); displayError(msgCoreLoadFailure(reason)); return 1; } if (coreplugin->hasError()) { displayError(msgCoreLoadFailure(coreplugin->errorString())); return 1; } if (foundAppOptions.contains(QLatin1String(VERSION_OPTION))) { printVersion(coreplugin, pluginManager); return 0; } if (foundAppOptions.contains(QLatin1String(HELP_OPTION1)) || foundAppOptions.contains(QLatin1String(HELP_OPTION2)) || foundAppOptions.contains(QLatin1String(HELP_OPTION3)) || foundAppOptions.contains(QLatin1String(HELP_OPTION4))) { printHelp(QFileInfo(app.applicationFilePath()).baseName(), pluginManager); return 0; } const bool isFirstInstance = !app.isRunning(); if (!isFirstInstance && foundAppOptions.contains(QLatin1String(CLIENT_OPTION))) { if (app.sendMessage(pluginManager.serializedArguments())) return 0; // Message could not be send, maybe it was in the process of quitting if (app.isRunning()) { // Nah app is still running, ask the user int button = askMsgSendFailed(); while(button == QMessageBox::Retry) { if (app.sendMessage(pluginManager.serializedArguments())) return 0; if (!app.isRunning()) // App quit while we were trying so start a new creator button = QMessageBox::Yes; else button = askMsgSendFailed(); } if (button == QMessageBox::No) return -1; } } pluginManager.loadPlugins(); if (coreplugin->hasError()) { displayError(msgCoreLoadFailure(coreplugin->errorString())); return 1; } { if (pluginManager.hasError()) { ExtensionSystem::PluginErrorOverview errorOverview(&pluginManager); errorOverview.exec(); } } if (isFirstInstance) { // Set up lock and remote arguments for the first instance only. // Silently fallback to unconnected instances for any subsequent // instances. app.initialize(); QObject::connect(&app, SIGNAL(messageReceived(QString)), &pluginManager, SLOT(remoteArguments(QString))); } QObject::connect(&app, SIGNAL(fileOpenRequest(QString)), coreplugin->plugin(), SLOT(fileOpenRequest(QString))); // shutdown plugin manager on the exit QObject::connect(&app, SIGNAL(aboutToQuit()), &pluginManager, SLOT(shutdown())); #ifdef WITH_TESTS // Do this after the event loop has started if(pluginManager.runningTests()) QTimer::singleShot(100, &pluginManager, SLOT(startTests())); #endif return app.exec(); }