diff --git a/ground/src/app/main.cpp b/ground/src/app/main.cpp index 4bc89d977..d07007d8c 100644 --- a/ground/src/app/main.cpp +++ b/ground/src/app/main.cpp @@ -1,321 +1,321 @@ -/** - ****************************************************************************** - * - * @file main.cpp - * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. - * Parts by Nokia Corporation (qt-info@nokia.com) Copyright (C) 2009. - * @brief - * @see The GNU Public License (GPL) Version 3 - * @defgroup - * @{ - * - *****************************************************************************/ -/* - * 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; either version 3 of the License, or - * (at your option) any later version. - * - * 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 "qtsingleapplication.h" - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -enum { OptionIndent = 4, DescriptionIndent = 24 }; - -static const char *appNameC = "OpenPilot GCS"; -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"; - -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"; - -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 QString msgSendArgumentFailed() -{ - return QCoreApplication::translate("Application", "Unable to send command line arguments to the already running instance. It appears to be not responding."); -} - -// Prepare a remote argument: If it is a relative file, add the current directory -// since the the central instance might be running in a different directory. - -static inline QString prepareRemoteArgument(const QString &a) -{ - QFileInfo fi(a); - if (!fi.exists()) - return a; - if (fi.isRelative()) - return fi.absoluteFilePath(); - return a; -} - -// Send the arguments to an already running instance of OpenPilot GCS -static bool sendArguments(SharedTools::QtSingleApplication &app, const QStringList &arguments) -{ - if (!arguments.empty()) { - // Send off arguments - const QStringList::const_iterator acend = arguments.constEnd(); - for (QStringList::const_iterator it = arguments.constBegin(); it != acend; ++it) { - if (!app.sendMessage(prepareRemoteArgument(*it))) { - displayError(msgSendArgumentFailed()); - return false; - } - } - } - // Special empty argument means: Show and raise (the slot just needs to be triggered) - if (!app.sendMessage(QString())) { - displayError(msgSendArgumentFailed()); - return false; - } - return true; -} - -static inline QStringList getPluginPaths() -{ - QStringList rc; - // Figure out root: Up one from 'bin' - QDir rootDir = QApplication::applicationDirPath(); - rootDir.cdUp(); - const QString rootDirPath = rootDir.canonicalPath(); - // 1) "plugins" (Win/Linux) - QString pluginPath = rootDirPath; - pluginPath += QLatin1Char('/'); - pluginPath += QLatin1String(GCS_LIBRARY_BASENAME); - pluginPath += QLatin1Char('/'); - pluginPath += QLatin1String("openpilotgcs"); - pluginPath += QLatin1Char('/'); - pluginPath += QLatin1String("plugins"); - rc.push_back(pluginPath); - // 2) "PlugIns" (OS X) - pluginPath = rootDirPath; - pluginPath += QLatin1Char('/'); - pluginPath += QLatin1String("PlugIns"); - rc.push_back(pluginPath); - return rc; -} - -#ifdef Q_OS_MAC -# define SHARE_PATH "/../Resources" -#else -# define SHARE_PATH "/../share/openpilotgcs" -#endif - -int main(int argc, char **argv) -{ -#ifdef Q_OS_MAC - // increase the number of file that can be opened in OpenPilot GCS - struct rlimit rl; - getrlimit(RLIMIT_NOFILE, &rl); - rl.rlim_cur = rl.rlim_max; - setrlimit(RLIMIT_NOFILE, &rl); -#endif - - SharedTools::QtSingleApplication app((QLatin1String(appNameC)), argc, argv); - - QTranslator translator; - QTranslator qtTranslator; - QString locale = QLocale::system().name(); - const QString &creatorTrPath = QCoreApplication::applicationDirPath() - + QLatin1String(SHARE_PATH "/translations"); - if (translator.load(QLatin1String("openpilotgcs_") + 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() - } - } - - // Load - ExtensionSystem::PluginManager pluginManager; - pluginManager.setFileExtension(QLatin1String("pluginspec")); - - const QStringList pluginPaths = getPluginPaths(); - pluginManager.setPluginPaths(pluginPaths); - - const QStringList arguments = app.arguments(); - 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))) - return sendArguments(app, pluginManager.arguments()) ? 0 : -1; - - pluginManager.loadPlugins(); - if (coreplugin->hasError()) { - displayError(msgCoreLoadFailure(coreplugin->errorString())); - return 1; - } - { - QStringList errors; - foreach (ExtensionSystem::PluginSpec *p, pluginManager.plugins()) - if (p->hasError()) - errors.append(p->errorString()); - if (!errors.isEmpty()) - QMessageBox::warning(0, - QCoreApplication::translate("Application", "Qt Creator - Plugin loader messages"), - errors.join(QString::fromLatin1("\n\n"))); - } - - 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)), coreplugin->plugin(), SLOT(remoteArgument(QString))); - } - QObject::connect(&app, SIGNAL(fileOpenRequest(QString)), coreplugin->plugin(), SLOT(remoteArgument(QString))); - - // Do this after the event loop has started - QTimer::singleShot(100, &pluginManager, SLOT(startTests())); - return app.exec(); -} - +/** + ****************************************************************************** + * + * @file main.cpp + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. + * Parts by Nokia Corporation (qt-info@nokia.com) Copyright (C) 2009. + * @brief + * @see The GNU Public License (GPL) Version 3 + * @defgroup + * @{ + * + *****************************************************************************/ +/* + * 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; either version 3 of the License, or + * (at your option) any later version. + * + * 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 "qtsingleapplication.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +enum { OptionIndent = 4, DescriptionIndent = 24 }; + +static const char *appNameC = "OpenPilot GCS"; +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"; + +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"; + +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 QString msgSendArgumentFailed() +{ + return QCoreApplication::translate("Application", "Unable to send command line arguments to the already running instance. It appears to be not responding."); +} + +// Prepare a remote argument: If it is a relative file, add the current directory +// since the the central instance might be running in a different directory. + +static inline QString prepareRemoteArgument(const QString &a) +{ + QFileInfo fi(a); + if (!fi.exists()) + return a; + if (fi.isRelative()) + return fi.absoluteFilePath(); + return a; +} + +// Send the arguments to an already running instance of OpenPilot GCS +static bool sendArguments(SharedTools::QtSingleApplication &app, const QStringList &arguments) +{ + if (!arguments.empty()) { + // Send off arguments + const QStringList::const_iterator acend = arguments.constEnd(); + for (QStringList::const_iterator it = arguments.constBegin(); it != acend; ++it) { + if (!app.sendMessage(prepareRemoteArgument(*it))) { + displayError(msgSendArgumentFailed()); + return false; + } + } + } + // Special empty argument means: Show and raise (the slot just needs to be triggered) + if (!app.sendMessage(QString())) { + displayError(msgSendArgumentFailed()); + return false; + } + return true; +} + +static inline QStringList getPluginPaths() +{ + QStringList rc; + // Figure out root: Up one from 'bin' + QDir rootDir = QApplication::applicationDirPath(); + rootDir.cdUp(); + const QString rootDirPath = rootDir.canonicalPath(); + // 1) "plugins" (Win/Linux) + QString pluginPath = rootDirPath; + pluginPath += QLatin1Char('/'); + pluginPath += QLatin1String(GCS_LIBRARY_BASENAME); + pluginPath += QLatin1Char('/'); + pluginPath += QLatin1String("openpilotgcs"); + pluginPath += QLatin1Char('/'); + pluginPath += QLatin1String("plugins"); + rc.push_back(pluginPath); + // 2) "PlugIns" (OS X) + pluginPath = rootDirPath; + pluginPath += QLatin1Char('/'); + pluginPath += QLatin1String("PlugIns"); + rc.push_back(pluginPath); + return rc; +} + +#ifdef Q_OS_MAC +# define SHARE_PATH "/../Resources" +#else +# define SHARE_PATH "/../share/openpilotgcs" +#endif + +int main(int argc, char **argv) +{ +#ifdef Q_OS_MAC + // increase the number of file that can be opened in OpenPilot GCS + struct rlimit rl; + getrlimit(RLIMIT_NOFILE, &rl); + rl.rlim_cur = rl.rlim_max; + setrlimit(RLIMIT_NOFILE, &rl); +#endif + + SharedTools::QtSingleApplication app((QLatin1String(appNameC)), argc, argv); + + QTranslator translator; + QTranslator qtTranslator; + QString locale = QLocale::system().name(); + const QString &creatorTrPath = QCoreApplication::applicationDirPath() + + QLatin1String(SHARE_PATH "/translations"); + if (translator.load(QLatin1String("openpilotgcs_") + 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() + } + } + + // Load + ExtensionSystem::PluginManager pluginManager; + pluginManager.setFileExtension(QLatin1String("pluginspec")); + + const QStringList pluginPaths = getPluginPaths(); + pluginManager.setPluginPaths(pluginPaths); + + const QStringList arguments = app.arguments(); + 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))) + return sendArguments(app, pluginManager.arguments()) ? 0 : -1; + + pluginManager.loadPlugins(); + if (coreplugin->hasError()) { + displayError(msgCoreLoadFailure(coreplugin->errorString())); + return 1; + } + { + QStringList errors; + foreach (ExtensionSystem::PluginSpec *p, pluginManager.plugins()) + if (p->hasError()) + errors.append(p->errorString()); + if (!errors.isEmpty()) + QMessageBox::warning(0, + QCoreApplication::translate("Application", "OpenPilot GCS - Plugin loader messages"), + errors.join(QString::fromLatin1("\n\n"))); + } + + 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)), coreplugin->plugin(), SLOT(remoteArgument(QString))); + } + QObject::connect(&app, SIGNAL(fileOpenRequest(QString)), coreplugin->plugin(), SLOT(remoteArgument(QString))); + + // Do this after the event loop has started + QTimer::singleShot(100, &pluginManager, SLOT(startTests())); + return app.exec(); +} + diff --git a/ground/src/libs/extensionsystem/pluginmanager.cpp b/ground/src/libs/extensionsystem/pluginmanager.cpp index 00f44a525..edd65d3ce 100644 --- a/ground/src/libs/extensionsystem/pluginmanager.cpp +++ b/ground/src/libs/extensionsystem/pluginmanager.cpp @@ -1,58 +1,30 @@ -/** - ****************************************************************************** - * - * @file pluginmanager.cpp - * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. - * Parts by Nokia Corporation (qt-info@nokia.com) Copyright (C) 2009. - * @brief - * @see The GNU Public License (GPL) Version 3 - * @defgroup - * @{ - * - *****************************************************************************/ -/* - * 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; either version 3 of the License, or - * (at your option) any later version. - * - * 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 - */ -/************************************************************************** -** -** This file is part of Qt Creator -** -** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** Commercial Usage -** -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Nokia. -** -** GNU Lesser General Public License Usage -** -** 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. -** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. -** -**************************************************************************/ +/** + ****************************************************************************** + * + * @file pluginmanager.cpp + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. + * Parts by Nokia Corporation (qt-info@nokia.com) Copyright (C) 2009. + * @brief + * @see The GNU Public License (GPL) Version 3 + * @defgroup + * @{ + * + *****************************************************************************/ +/* + * 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; either version 3 of the License, or + * (at your option) any later version. + * + * 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 "pluginmanager.h" #include "pluginmanager_p.h" diff --git a/ground/src/plugins/coreplugin/actionmanager/actioncontainer.cpp b/ground/src/plugins/coreplugin/actionmanager/actioncontainer.cpp index 0c7bc1076..0e32d1f1f 100644 --- a/ground/src/plugins/coreplugin/actionmanager/actioncontainer.cpp +++ b/ground/src/plugins/coreplugin/actionmanager/actioncontainer.cpp @@ -1,469 +1,469 @@ -/** - ****************************************************************************** - * - * @file actioncontainer.cpp - * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. - * Parts by Nokia Corporation (qt-info@nokia.com) Copyright (C) 2009. - * @brief - * @see The GNU Public License (GPL) Version 3 - * @defgroup coreplugin - * @{ - * - *****************************************************************************/ -/* - * 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; either version 3 of the License, or - * (at your option) any later version. - * - * 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 "actioncontainer_p.h" -#include "actionmanager_p.h" - -#include "command_p.h" - -#include "coreconstants.h" -#include "uniqueidmanager.h" - -#include -#include -#include - -Q_DECLARE_METATYPE(Core::Internal::MenuActionContainer*) - -using namespace Core; -using namespace Core::Internal; - -/*! - \class ActionContainer - \mainclass - - \brief The ActionContainer class represents a menu or menu bar in Qt Creator. - - You don't create instances of this class directly, but instead use the - \l{ActionManager::createMenu()} - and \l{ActionManager::createMenuBar()} methods. - Retrieve existing action containers for an ID with - \l{ActionManager::actionContainer()}. - - Within a menu or menu bar you can group menus and items together by defining groups - (the order of the groups is defined by the order of the \l{ActionContainer::appendGroup()} calls), and - adding menus/actions to these groups. If no custom groups are defined, an action container - has three default groups \c{Core::Constants::G_DEFAULT_ONE}, \c{Core::Constants::G_DEFAULT_TWO} - and \c{Core::Constants::G_DEFAULT_THREE}. - - You can define if the menu represented by this action container should automatically disable - or hide whenever it only contains disabled items and submenus by setting the corresponding - \l{ActionContainer::setEmptyAction()}{EmptyAction}. -*/ - -/*! - \enum ActionContainer::EmptyAction - Defines what happens when the represented menu is empty or contains only disabled/invisible items. - \omitvalue EA_Mask - \value EA_None - The menu will still be visible and active. - \value EA_Disable - The menu will be visible but disabled. - \value EA_Hide - The menu will not be visible until the state of the subitems change. -*/ - -/*! - \fn ActionContainer::setEmptyAction(EmptyAction disableOrHide) - Defines if the menu represented by this action container should automatically \a disableOrHide - whenever it only contains disabled items and submenus. - \sa ActionContainer::EmptyAction -*/ - -/*! - \fn int ActionContainer::id() const - \internal -*/ - -/*! - \fn QMenu *ActionContainer::menu() const - Returns the QMenu instance that is represented by this action container, or - 0 if this action container represents a menu bar. -*/ - -/*! - \fn QMenuBar *ActionContainer::menuBar() const - Returns the QMenuBar instance that is represented by this action container, or - 0 if this action container represents a menu. -*/ - -/*! - \fn QAction *ActionContainer::insertLocation(const QString &group) const - Returns an action representing the \a group, - that could be used with \c{QWidget::insertAction}. -*/ - -/*! - \fn void ActionContainer::appendGroup(const QString &identifier) - Adds a group with the given \a identifier to the action container. Using groups - you can segment your action container into logical parts and add actions and - menus directly to these parts. - \sa addAction() - \sa addMenu() -*/ - -/*! - \fn void ActionContainer::addAction(Core::Command *action, const QString &group) - Add the \a action as a menu item to this action container. The action is added as the - last item of the specified \a group. - \sa appendGroup() - \sa addMenu() -*/ - -/*! - \fn void ActionContainer::addMenu(Core::ActionContainer *menu, const QString &group) - Add the \a menu as a submenu to this action container. The menu is added as the - last item of the specified \a group. - \sa appendGroup() - \sa addAction() -*/ - -/*! - \fn bool ActionContainer::update() - \internal -*/ - -/*! - \fn ActionContainer::~ActionContainer() - \internal -*/ - -// ---------- ActionContainerPrivate ------------ - -/*! - \class Core::Internal::ActionContainerPrivate - \internal -*/ - -ActionContainerPrivate::ActionContainerPrivate(int id) - : m_data(0), m_id(id) -{ - -} - -void ActionContainerPrivate::setEmptyAction(EmptyAction ea) -{ - m_data = ((m_data & ~EA_Mask) | ea); -} - -bool ActionContainerPrivate::hasEmptyAction(EmptyAction ea) const -{ - return (m_data & EA_Mask) == ea; -} - -void ActionContainerPrivate::appendGroup(const QString &group) -{ - int gid = UniqueIDManager::instance()->uniqueIdentifier(group); - m_groups << gid; -} - -QAction *ActionContainerPrivate::insertLocation(const QString &group) const -{ - int grpid = UniqueIDManager::instance()->uniqueIdentifier(group); - int prevKey = 0; - int pos = ((grpid << 16) | 0xFFFF); - return beforeAction(pos, &prevKey); -} - -void ActionContainerPrivate::addAction(Command *action, const QString &group) -{ - if (!canAddAction(action)) - return; - - ActionManagerPrivate *am = ActionManagerPrivate::instance(); - UniqueIDManager *idmanager = UniqueIDManager::instance(); - int grpid = idmanager->uniqueIdentifier(Constants::G_DEFAULT_TWO); - if (!group.isEmpty()) - grpid = idmanager->uniqueIdentifier(group); - if (!m_groups.contains(grpid) && !am->defaultGroups().contains(grpid)) - qWarning() << "*** addAction(): Unknown group: " << group; - int pos = ((grpid << 16) | 0xFFFF); - addAction(action, pos, true); -} - -void ActionContainerPrivate::addMenu(ActionContainer *menu, const QString &group) -{ - ActionContainerPrivate *container = static_cast(menu); - if (!container->canBeAddedToMenu()) - return; - - ActionManagerPrivate *am = ActionManagerPrivate::instance(); - UniqueIDManager *idmanager = UniqueIDManager::instance(); - int grpid = idmanager->uniqueIdentifier(Constants::G_DEFAULT_TWO); - if (!group.isEmpty()) - grpid = idmanager->uniqueIdentifier(group); - if (!m_groups.contains(grpid) && !am->defaultGroups().contains(grpid)) - qWarning() << "*** addMenu(): Unknown group: " << group; - int pos = ((grpid << 16) | 0xFFFF); - addMenu(menu, pos, true); -} - -int ActionContainerPrivate::id() const -{ - return m_id; -} - -QMenu *ActionContainerPrivate::menu() const -{ - return 0; -} - -QMenuBar *ActionContainerPrivate::menuBar() const -{ - return 0; -} - -bool ActionContainerPrivate::canAddAction(Command *action) const -{ - return (action->action() != 0); -} - -void ActionContainerPrivate::addAction(Command *action, int pos, bool setpos) -{ - Action *a = static_cast(action); - - int prevKey = 0; - QAction *ba = beforeAction(pos, &prevKey); - - if (setpos) { - pos = calcPosition(pos, prevKey); - CommandLocation loc; - loc.m_container = m_id; - loc.m_position = pos; - QList locs = a->locations(); - locs.append(loc); - a->setLocations(locs); - } - - m_commands.append(action); - m_posmap.insert(pos, action->id()); - insertAction(ba, a->action()); -} - -void ActionContainerPrivate::addMenu(ActionContainer *menu, int pos, bool setpos) -{ - MenuActionContainer *mc = static_cast(menu); - - int prevKey = 0; - QAction *ba = beforeAction(pos, &prevKey); - - if (setpos) { - pos = calcPosition(pos, prevKey); - CommandLocation loc; - loc.m_container = m_id; - loc.m_position = pos; - mc->setLocation(loc); - } - - m_subContainers.append(menu); - m_posmap.insert(pos, menu->id()); - insertMenu(ba, mc->menu()); -} - -QAction *ActionContainerPrivate::beforeAction(int pos, int *prevKey) const -{ - ActionManagerPrivate *am = ActionManagerPrivate::instance(); - - int baId = -1; - - (*prevKey) = -1; - - QMap::const_iterator i = m_posmap.constBegin(); - while (i != m_posmap.constEnd()) { - if (i.key() > pos) { - baId = i.value(); - break; - } - (*prevKey) = i.key(); - ++i; - } - - if (baId == -1) - return 0; - - if (Command *cmd = am->command(baId)) - return cmd->action(); - if (ActionContainer *container = am->actionContainer(baId)) - if (QMenu *menu = container->menu()) - return menu->menuAction(); - - return 0; -} - -int ActionContainerPrivate::calcPosition(int pos, int prevKey) const -{ - int grp = (pos & 0xFFFF0000); - if (prevKey == -1) - return grp; - - int prevgrp = (prevKey & 0xFFFF0000); - - if (grp != prevgrp) - return grp; - - return grp + (prevKey & 0xFFFF) + 10; -} - -// ---------- MenuActionContainer ------------ - -/*! - \class Core::Internal::MenuActionContainer - \internal -*/ - -MenuActionContainer::MenuActionContainer(int id) - : ActionContainerPrivate(id), m_menu(0) -{ - setEmptyAction(EA_Disable); -} - -void MenuActionContainer::setMenu(QMenu *menu) -{ - m_menu = menu; - - QVariant v; - qVariantSetValue(v, this); - - m_menu->menuAction()->setData(v); -} - -QMenu *MenuActionContainer::menu() const -{ - return m_menu; -} - -void MenuActionContainer::insertAction(QAction *before, QAction *action) -{ - m_menu->insertAction(before, action); -} - -void MenuActionContainer::insertMenu(QAction *before, QMenu *menu) -{ - m_menu->insertMenu(before, menu); -} - -void MenuActionContainer::setLocation(const CommandLocation &location) -{ - m_location = location; -} - -CommandLocation MenuActionContainer::location() const -{ - return m_location; -} - -bool MenuActionContainer::update() -{ - if (hasEmptyAction(EA_None)) - return true; - - bool hasitems = false; - - foreach (ActionContainer *container, subContainers()) { - if (container == this) { - qWarning() << Q_FUNC_INFO << "container" << (this->menu() ? this->menu()->title() : "") << "contains itself as subcontainer"; - continue; - } - if (container->update()) { - hasitems = true; - break; - } - } - if (!hasitems) { - foreach (Command *command, commands()) { - if (command->isActive()) { - hasitems = true; - break; - } - } - } - - if (hasEmptyAction(EA_Hide)) - m_menu->setVisible(hasitems); - else if (hasEmptyAction(EA_Disable)) - m_menu->setEnabled(hasitems); - - return hasitems; -} - -bool MenuActionContainer::canBeAddedToMenu() const -{ - return true; -} - - -// ---------- MenuBarActionContainer ------------ - -/*! - \class Core::Internal::MenuBarActionContainer - \internal -*/ - -MenuBarActionContainer::MenuBarActionContainer(int id) - : ActionContainerPrivate(id), m_menuBar(0) -{ - setEmptyAction(EA_None); -} - -void MenuBarActionContainer::setMenuBar(QMenuBar *menuBar) -{ - m_menuBar = menuBar; -} - -QMenuBar *MenuBarActionContainer::menuBar() const -{ - return m_menuBar; -} - -void MenuBarActionContainer::insertAction(QAction *before, QAction *action) -{ - m_menuBar->insertAction(before, action); -} - -void MenuBarActionContainer::insertMenu(QAction *before, QMenu *menu) -{ - m_menuBar->insertMenu(before, menu); -} - -bool MenuBarActionContainer::update() -{ - if (hasEmptyAction(EA_None)) - return true; - - bool hasitems = false; - QList actions = m_menuBar->actions(); - for (int i=0; iisVisible()) { - hasitems = true; - break; - } - } - - if (hasEmptyAction(EA_Hide)) - m_menuBar->setVisible(hasitems); - else if (hasEmptyAction(EA_Disable)) - m_menuBar->setEnabled(hasitems); - - return hasitems; -} - -bool MenuBarActionContainer::canBeAddedToMenu() const -{ - return false; -} - +/** + ****************************************************************************** + * + * @file actioncontainer.cpp + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. + * Parts by Nokia Corporation (qt-info@nokia.com) Copyright (C) 2009. + * @brief + * @see The GNU Public License (GPL) Version 3 + * @defgroup coreplugin + * @{ + * + *****************************************************************************/ +/* + * 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; either version 3 of the License, or + * (at your option) any later version. + * + * 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 "actioncontainer_p.h" +#include "actionmanager_p.h" + +#include "command_p.h" + +#include "coreconstants.h" +#include "uniqueidmanager.h" + +#include +#include +#include + +Q_DECLARE_METATYPE(Core::Internal::MenuActionContainer*) + +using namespace Core; +using namespace Core::Internal; + +/*! + \class ActionContainer + \mainclass + + \brief The ActionContainer class represents a menu or menu bar in the OpenPilot GCS. + + You don't create instances of this class directly, but instead use the + \l{ActionManager::createMenu()} + and \l{ActionManager::createMenuBar()} methods. + Retrieve existing action containers for an ID with + \l{ActionManager::actionContainer()}. + + Within a menu or menu bar you can group menus and items together by defining groups + (the order of the groups is defined by the order of the \l{ActionContainer::appendGroup()} calls), and + adding menus/actions to these groups. If no custom groups are defined, an action container + has three default groups \c{Core::Constants::G_DEFAULT_ONE}, \c{Core::Constants::G_DEFAULT_TWO} + and \c{Core::Constants::G_DEFAULT_THREE}. + + You can define if the menu represented by this action container should automatically disable + or hide whenever it only contains disabled items and submenus by setting the corresponding + \l{ActionContainer::setEmptyAction()}{EmptyAction}. +*/ + +/*! + \enum ActionContainer::EmptyAction + Defines what happens when the represented menu is empty or contains only disabled/invisible items. + \omitvalue EA_Mask + \value EA_None + The menu will still be visible and active. + \value EA_Disable + The menu will be visible but disabled. + \value EA_Hide + The menu will not be visible until the state of the subitems change. +*/ + +/*! + \fn ActionContainer::setEmptyAction(EmptyAction disableOrHide) + Defines if the menu represented by this action container should automatically \a disableOrHide + whenever it only contains disabled items and submenus. + \sa ActionContainer::EmptyAction +*/ + +/*! + \fn int ActionContainer::id() const + \internal +*/ + +/*! + \fn QMenu *ActionContainer::menu() const + Returns the QMenu instance that is represented by this action container, or + 0 if this action container represents a menu bar. +*/ + +/*! + \fn QMenuBar *ActionContainer::menuBar() const + Returns the QMenuBar instance that is represented by this action container, or + 0 if this action container represents a menu. +*/ + +/*! + \fn QAction *ActionContainer::insertLocation(const QString &group) const + Returns an action representing the \a group, + that could be used with \c{QWidget::insertAction}. +*/ + +/*! + \fn void ActionContainer::appendGroup(const QString &identifier) + Adds a group with the given \a identifier to the action container. Using groups + you can segment your action container into logical parts and add actions and + menus directly to these parts. + \sa addAction() + \sa addMenu() +*/ + +/*! + \fn void ActionContainer::addAction(Core::Command *action, const QString &group) + Add the \a action as a menu item to this action container. The action is added as the + last item of the specified \a group. + \sa appendGroup() + \sa addMenu() +*/ + +/*! + \fn void ActionContainer::addMenu(Core::ActionContainer *menu, const QString &group) + Add the \a menu as a submenu to this action container. The menu is added as the + last item of the specified \a group. + \sa appendGroup() + \sa addAction() +*/ + +/*! + \fn bool ActionContainer::update() + \internal +*/ + +/*! + \fn ActionContainer::~ActionContainer() + \internal +*/ + +// ---------- ActionContainerPrivate ------------ + +/*! + \class Core::Internal::ActionContainerPrivate + \internal +*/ + +ActionContainerPrivate::ActionContainerPrivate(int id) + : m_data(0), m_id(id) +{ + +} + +void ActionContainerPrivate::setEmptyAction(EmptyAction ea) +{ + m_data = ((m_data & ~EA_Mask) | ea); +} + +bool ActionContainerPrivate::hasEmptyAction(EmptyAction ea) const +{ + return (m_data & EA_Mask) == ea; +} + +void ActionContainerPrivate::appendGroup(const QString &group) +{ + int gid = UniqueIDManager::instance()->uniqueIdentifier(group); + m_groups << gid; +} + +QAction *ActionContainerPrivate::insertLocation(const QString &group) const +{ + int grpid = UniqueIDManager::instance()->uniqueIdentifier(group); + int prevKey = 0; + int pos = ((grpid << 16) | 0xFFFF); + return beforeAction(pos, &prevKey); +} + +void ActionContainerPrivate::addAction(Command *action, const QString &group) +{ + if (!canAddAction(action)) + return; + + ActionManagerPrivate *am = ActionManagerPrivate::instance(); + UniqueIDManager *idmanager = UniqueIDManager::instance(); + int grpid = idmanager->uniqueIdentifier(Constants::G_DEFAULT_TWO); + if (!group.isEmpty()) + grpid = idmanager->uniqueIdentifier(group); + if (!m_groups.contains(grpid) && !am->defaultGroups().contains(grpid)) + qWarning() << "*** addAction(): Unknown group: " << group; + int pos = ((grpid << 16) | 0xFFFF); + addAction(action, pos, true); +} + +void ActionContainerPrivate::addMenu(ActionContainer *menu, const QString &group) +{ + ActionContainerPrivate *container = static_cast(menu); + if (!container->canBeAddedToMenu()) + return; + + ActionManagerPrivate *am = ActionManagerPrivate::instance(); + UniqueIDManager *idmanager = UniqueIDManager::instance(); + int grpid = idmanager->uniqueIdentifier(Constants::G_DEFAULT_TWO); + if (!group.isEmpty()) + grpid = idmanager->uniqueIdentifier(group); + if (!m_groups.contains(grpid) && !am->defaultGroups().contains(grpid)) + qWarning() << "*** addMenu(): Unknown group: " << group; + int pos = ((grpid << 16) | 0xFFFF); + addMenu(menu, pos, true); +} + +int ActionContainerPrivate::id() const +{ + return m_id; +} + +QMenu *ActionContainerPrivate::menu() const +{ + return 0; +} + +QMenuBar *ActionContainerPrivate::menuBar() const +{ + return 0; +} + +bool ActionContainerPrivate::canAddAction(Command *action) const +{ + return (action->action() != 0); +} + +void ActionContainerPrivate::addAction(Command *action, int pos, bool setpos) +{ + Action *a = static_cast(action); + + int prevKey = 0; + QAction *ba = beforeAction(pos, &prevKey); + + if (setpos) { + pos = calcPosition(pos, prevKey); + CommandLocation loc; + loc.m_container = m_id; + loc.m_position = pos; + QList locs = a->locations(); + locs.append(loc); + a->setLocations(locs); + } + + m_commands.append(action); + m_posmap.insert(pos, action->id()); + insertAction(ba, a->action()); +} + +void ActionContainerPrivate::addMenu(ActionContainer *menu, int pos, bool setpos) +{ + MenuActionContainer *mc = static_cast(menu); + + int prevKey = 0; + QAction *ba = beforeAction(pos, &prevKey); + + if (setpos) { + pos = calcPosition(pos, prevKey); + CommandLocation loc; + loc.m_container = m_id; + loc.m_position = pos; + mc->setLocation(loc); + } + + m_subContainers.append(menu); + m_posmap.insert(pos, menu->id()); + insertMenu(ba, mc->menu()); +} + +QAction *ActionContainerPrivate::beforeAction(int pos, int *prevKey) const +{ + ActionManagerPrivate *am = ActionManagerPrivate::instance(); + + int baId = -1; + + (*prevKey) = -1; + + QMap::const_iterator i = m_posmap.constBegin(); + while (i != m_posmap.constEnd()) { + if (i.key() > pos) { + baId = i.value(); + break; + } + (*prevKey) = i.key(); + ++i; + } + + if (baId == -1) + return 0; + + if (Command *cmd = am->command(baId)) + return cmd->action(); + if (ActionContainer *container = am->actionContainer(baId)) + if (QMenu *menu = container->menu()) + return menu->menuAction(); + + return 0; +} + +int ActionContainerPrivate::calcPosition(int pos, int prevKey) const +{ + int grp = (pos & 0xFFFF0000); + if (prevKey == -1) + return grp; + + int prevgrp = (prevKey & 0xFFFF0000); + + if (grp != prevgrp) + return grp; + + return grp + (prevKey & 0xFFFF) + 10; +} + +// ---------- MenuActionContainer ------------ + +/*! + \class Core::Internal::MenuActionContainer + \internal +*/ + +MenuActionContainer::MenuActionContainer(int id) + : ActionContainerPrivate(id), m_menu(0) +{ + setEmptyAction(EA_Disable); +} + +void MenuActionContainer::setMenu(QMenu *menu) +{ + m_menu = menu; + + QVariant v; + qVariantSetValue(v, this); + + m_menu->menuAction()->setData(v); +} + +QMenu *MenuActionContainer::menu() const +{ + return m_menu; +} + +void MenuActionContainer::insertAction(QAction *before, QAction *action) +{ + m_menu->insertAction(before, action); +} + +void MenuActionContainer::insertMenu(QAction *before, QMenu *menu) +{ + m_menu->insertMenu(before, menu); +} + +void MenuActionContainer::setLocation(const CommandLocation &location) +{ + m_location = location; +} + +CommandLocation MenuActionContainer::location() const +{ + return m_location; +} + +bool MenuActionContainer::update() +{ + if (hasEmptyAction(EA_None)) + return true; + + bool hasitems = false; + + foreach (ActionContainer *container, subContainers()) { + if (container == this) { + qWarning() << Q_FUNC_INFO << "container" << (this->menu() ? this->menu()->title() : "") << "contains itself as subcontainer"; + continue; + } + if (container->update()) { + hasitems = true; + break; + } + } + if (!hasitems) { + foreach (Command *command, commands()) { + if (command->isActive()) { + hasitems = true; + break; + } + } + } + + if (hasEmptyAction(EA_Hide)) + m_menu->setVisible(hasitems); + else if (hasEmptyAction(EA_Disable)) + m_menu->setEnabled(hasitems); + + return hasitems; +} + +bool MenuActionContainer::canBeAddedToMenu() const +{ + return true; +} + + +// ---------- MenuBarActionContainer ------------ + +/*! + \class Core::Internal::MenuBarActionContainer + \internal +*/ + +MenuBarActionContainer::MenuBarActionContainer(int id) + : ActionContainerPrivate(id), m_menuBar(0) +{ + setEmptyAction(EA_None); +} + +void MenuBarActionContainer::setMenuBar(QMenuBar *menuBar) +{ + m_menuBar = menuBar; +} + +QMenuBar *MenuBarActionContainer::menuBar() const +{ + return m_menuBar; +} + +void MenuBarActionContainer::insertAction(QAction *before, QAction *action) +{ + m_menuBar->insertAction(before, action); +} + +void MenuBarActionContainer::insertMenu(QAction *before, QMenu *menu) +{ + m_menuBar->insertMenu(before, menu); +} + +bool MenuBarActionContainer::update() +{ + if (hasEmptyAction(EA_None)) + return true; + + bool hasitems = false; + QList actions = m_menuBar->actions(); + for (int i=0; iisVisible()) { + hasitems = true; + break; + } + } + + if (hasEmptyAction(EA_Hide)) + m_menuBar->setVisible(hasitems); + else if (hasEmptyAction(EA_Disable)) + m_menuBar->setEnabled(hasitems); + + return hasitems; +} + +bool MenuBarActionContainer::canBeAddedToMenu() const +{ + return false; +} + diff --git a/ground/src/plugins/coreplugin/icore.cpp b/ground/src/plugins/coreplugin/icore.cpp index f96383684..95c00f702 100644 --- a/ground/src/plugins/coreplugin/icore.cpp +++ b/ground/src/plugins/coreplugin/icore.cpp @@ -1,330 +1,330 @@ -/** - ****************************************************************************** - * - * @file icore.cpp - * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. - * Parts by Nokia Corporation (qt-info@nokia.com) Copyright (C) 2009. - * @brief - * @see The GNU Public License (GPL) Version 3 - * @defgroup coreplugin - * @{ - * - *****************************************************************************/ -/* - * 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; either version 3 of the License, or - * (at your option) any later version. - * - * 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 "icore.h" - -/*! - \namespace Core - \brief The Core namespace contains all classes that make up the Core plugin - which constitute the basic functionality of Qt Creator. -*/ - -/*! - \namespace Core::Internal - \internal -*/ - -/*! - \class Core::ICore - \brief The ICore class allows access to the different part that make up - the basic functionality of Qt Creator. - - You should never create a subclass of this interface. The one and only - instance is created by the Core plugin. You can access this instance - from your plugin through \c{Core::instance()}. - - \mainclass -*/ - -/*! - \fn QStringList ICore::showNewItemDialog(const QString &title, - const QList &wizards, - const QString &defaultLocation = QString()) - \brief Opens a dialog where the user can choose from a set of \a wizards that - create new files/classes/projects. - - The \a title argument is shown as the dialogs title. The path where the - files will be created (if the user doesn't change it) is set - in \a defaultLocation. It defaults to the path of the file manager's - current file. - - \sa Core::FileManager -*/ - -/*! - \fn bool ICore::showOptionsDialog(const QString &group = QString(), - const QString &page = QString()) - \brief Opens the application options/preferences dialog with preselected - \a page in a specified \a group. - - The arguments refer to the string IDs of the corresponding IOptionsPage. -*/ - -/*! - \fn bool ICore::showWarningWithOptions(const QString &title, const QString &text, - const QString &details = QString(), - const QString &settingsCategory = QString(), - const QString &settingsId = QString(), - QWidget *parent = 0); - - \brief Show a warning message with a button that opens a settings page. - - Should be used to display configuration errors and point users to the setting. - Returns true if the settings dialog was accepted. -*/ - - -/*! - \fn ActionManager *ICore::actionManager() const - \brief Returns the application's action manager. - - The action manager is responsible for registration of menus and - menu items and keyboard shortcuts. -*/ - -/*! - \fn FileManager *ICore::fileManager() const - \brief Returns the application's file manager. - - The file manager keeps track of files for changes outside the application. -*/ - -/*! - \fn UniqueIDManager *ICore::uniqueIDManager() const - \brief Returns the application's id manager. - - The unique ID manager transforms strings in unique integers and the other way round. -*/ - -/*! - \fn MessageManager *ICore::messageManager() const - \brief Returns the application's message manager. - - The message manager is the interface to the "General" output pane for - general application debug messages. -*/ - -/*! - \fn ExtensionSystem::PluginManager *ICore::pluginManager() const - \brief Returns the application's plugin manager. - - The plugin manager handles the plugin life cycles and manages - the common object pool. -*/ - -/*! - \fn EditorManager *ICore::editorManager() const - \brief Returns the application's editor manager. - - The editor manager handles all editor related tasks like opening - documents, the stack of currently open documents and the currently - active document. -*/ - - - -/*! - \fn VariableManager *ICore::variableManager() const - \brief Returns the application's variable manager. - - The variable manager is used to register application wide string variables - like \c MY_PROJECT_DIR such that strings like \c{somecommand ${MY_PROJECT_DIR}/sub} - can be resolved/expanded from anywhere in the application. -*/ - -/*! - \fn ModeManager *ICore::modeManager() const - \brief Returns the application's mode manager. - - The mode manager handles everything related to the instances of IMode - that were added to the plugin manager's object pool as well as their - buttons and the tool bar with the round buttons in the lower left - corner of Qt Creator. -*/ - -/*! - \fn MimeDatabase *ICore::mimeDatabase() const - \brief Returns the application's mime database. - - Use the mime database to manage mime types. -*/ - -/*! - \fn QSettings *ICore::settings() const - \brief Returns the application's main settings object. - - You can use it to retrieve or set application wide settings - (in contrast to session or project specific settings). - - \see settingsDatabase() -*/ - -/*! - \fn SettingsDatabase *ICore::settingsDatabase() const - \brief Returns the application's settings database. - - The settings database is meant as an alternative to the regular settings - object. It is more suitable for storing large amounts of data. The settings - are application wide. - - \see SettingsDatabase -*/ - -/*! - \fn QString ICore::resourcePath() const - \brief Returns the absolute path that is used for resources like - project templates and the debugger macros. - - This abstraction is needed to avoid platform-specific code all over - the place, since e.g. on Mac the resources are part of the application bundle. -*/ - -/*! - \fn QMainWindow *ICore::mainWindow() const - \brief Returns the main application window. - - For use as dialog parent etc. -*/ - -/*! - \fn IContext *ICore::currentContextObject() const - \brief Returns the context object of the current main context. - - \sa ICore::addAdditionalContext() - \sa ICore::addContextObject() -*/ - -/*! - \fn void ICore::addAdditionalContext(int context) - \brief Register additional context to be currently active. - - Appends the additional \a context to the list of currently active - contexts. You need to call ICore::updateContext to make that change - take effect. - - \sa ICore::removeAdditionalContext() - \sa ICore::hasContext() - \sa ICore::updateContext() -*/ - -/*! - \fn void ICore::removeAdditionalContext(int context) - \brief Removes the given \a context from the list of currently active contexts. - - You need to call ICore::updateContext to make that change - take effect. - - \sa ICore::addAdditionalContext() - \sa ICore::hasContext() - \sa ICore::updateContext() -*/ - -/*! - \fn bool ICore::hasContext(int context) const - \brief Returns if the given \a context is currently one of the active contexts. - - \sa ICore::addAdditionalContext() - \sa ICore::addContextObject() -*/ - -/*! - \fn void ICore::addContextObject(IContext *context) - \brief Registers an additional \a context object. - - After registration this context object gets automatically the - current context object whenever its widget gets focus. - - \sa ICore::removeContextObject() - \sa ICore::addAdditionalContext() - \sa ICore::currentContextObject() -*/ - -/*! - \fn void ICore::removeContextObject(IContext *context) - \brief Unregisters a \a context object from the list of know contexts. - - \sa ICore::addContextObject() - \sa ICore::addAdditionalContext() - \sa ICore::currentContextObject() -} -*/ - -/*! - \fn void ICore::updateContext() - \brief Update the list of active contexts after adding or removing additional ones. - - \sa ICore::addAdditionalContext() - \sa ICore::removeAdditionalContext() -*/ - -/*! - \fn void ICore::openFiles(const QStringList &fileNames) - \brief Open all files from a list of \a fileNames like it would be - done if they were given to Qt Creator on the command line, or - they were opened via \gui{File|Open}. -*/ - -/*! - \fn ICore::ICore() - \internal -*/ - -/*! - \fn ICore::~ICore() - \internal -*/ - -/*! - \fn void ICore::coreOpened() - \brief Emitted after all plugins have been loaded and the main window shown. -*/ - -/*! - \fn void ICore::saveSettingsRequested() - \brief Emitted to signal that the user has requested that the global settings - should be saved to disk. - - At the moment that happens when the application is closed, and on \gui{Save All}. -*/ - -/*! - \fn void ICore::optionsDialogRequested() - \brief Signal that allows plugins to perform actions just before the \gui{Tools|Options} - dialog is shown. -*/ - -/*! - \fn void ICore::coreAboutToClose() - \brief Plugins can do some pre-end-of-life actions when they get this signal. - - The application is guaranteed to shut down after this signal is emitted. - It's there as an addition to the usual plugin lifecycle methods, namely - IPlugin::shutdown(), just for convenience. -*/ - -/*! - \fn void ICore::contextAboutToChange(Core::IContext *context) - \brief Sent just before a new \a context becomes the current context - (meaning that its widget got focus). -*/ - -/*! - \fn void ICore::contextChanged(Core::IContext *context) - \brief Sent just after a new \a context became the current context - (meaning that its widget got focus). -*/ +/** + ****************************************************************************** + * + * @file icore.cpp + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. + * Parts by Nokia Corporation (qt-info@nokia.com) Copyright (C) 2009. + * @brief + * @see The GNU Public License (GPL) Version 3 + * @defgroup coreplugin + * @{ + * + *****************************************************************************/ +/* + * 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; either version 3 of the License, or + * (at your option) any later version. + * + * 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 "icore.h" + +/*! + \namespace Core + \brief The Core namespace contains all classes that make up the Core plugin + which constitute the basic functionality of the OpenPilot GCS. +*/ + +/*! + \namespace Core::Internal + \internal +*/ + +/*! + \class Core::ICore + \brief The ICore class allows access to the different part that make up + the basic functionality of the OpenPilot GCS. + + You should never create a subclass of this interface. The one and only + instance is created by the Core plugin. You can access this instance + from your plugin through \c{Core::instance()}. + + \mainclass +*/ + +/*! + \fn QStringList ICore::showNewItemDialog(const QString &title, + const QList &wizards, + const QString &defaultLocation = QString()) + \brief Opens a dialog where the user can choose from a set of \a wizards that + create new files/classes/projects. + + The \a title argument is shown as the dialogs title. The path where the + files will be created (if the user doesn't change it) is set + in \a defaultLocation. It defaults to the path of the file manager's + current file. + + \sa Core::FileManager +*/ + +/*! + \fn bool ICore::showOptionsDialog(const QString &group = QString(), + const QString &page = QString()) + \brief Opens the application options/preferences dialog with preselected + \a page in a specified \a group. + + The arguments refer to the string IDs of the corresponding IOptionsPage. +*/ + +/*! + \fn bool ICore::showWarningWithOptions(const QString &title, const QString &text, + const QString &details = QString(), + const QString &settingsCategory = QString(), + const QString &settingsId = QString(), + QWidget *parent = 0); + + \brief Show a warning message with a button that opens a settings page. + + Should be used to display configuration errors and point users to the setting. + Returns true if the settings dialog was accepted. +*/ + + +/*! + \fn ActionManager *ICore::actionManager() const + \brief Returns the application's action manager. + + The action manager is responsible for registration of menus and + menu items and keyboard shortcuts. +*/ + +/*! + \fn FileManager *ICore::fileManager() const + \brief Returns the application's file manager. + + The file manager keeps track of files for changes outside the application. +*/ + +/*! + \fn UniqueIDManager *ICore::uniqueIDManager() const + \brief Returns the application's id manager. + + The unique ID manager transforms strings in unique integers and the other way round. +*/ + +/*! + \fn MessageManager *ICore::messageManager() const + \brief Returns the application's message manager. + + The message manager is the interface to the "General" output pane for + general application debug messages. +*/ + +/*! + \fn ExtensionSystem::PluginManager *ICore::pluginManager() const + \brief Returns the application's plugin manager. + + The plugin manager handles the plugin life cycles and manages + the common object pool. +*/ + +/*! + \fn EditorManager *ICore::editorManager() const + \brief Returns the application's editor manager. + + The editor manager handles all editor related tasks like opening + documents, the stack of currently open documents and the currently + active document. +*/ + + + +/*! + \fn VariableManager *ICore::variableManager() const + \brief Returns the application's variable manager. + + The variable manager is used to register application wide string variables + like \c MY_PROJECT_DIR such that strings like \c{somecommand ${MY_PROJECT_DIR}/sub} + can be resolved/expanded from anywhere in the application. +*/ + +/*! + \fn ModeManager *ICore::modeManager() const + \brief Returns the application's mode manager. + + The mode manager handles everything related to the instances of IMode + that were added to the plugin manager's object pool as well as their + buttons and the tool bar with the round buttons in the lower left + corner of the OpenPilot GCS. +*/ + +/*! + \fn MimeDatabase *ICore::mimeDatabase() const + \brief Returns the application's mime database. + + Use the mime database to manage mime types. +*/ + +/*! + \fn QSettings *ICore::settings() const + \brief Returns the application's main settings object. + + You can use it to retrieve or set application wide settings + (in contrast to session or project specific settings). + + \see settingsDatabase() +*/ + +/*! + \fn SettingsDatabase *ICore::settingsDatabase() const + \brief Returns the application's settings database. + + The settings database is meant as an alternative to the regular settings + object. It is more suitable for storing large amounts of data. The settings + are application wide. + + \see SettingsDatabase +*/ + +/*! + \fn QString ICore::resourcePath() const + \brief Returns the absolute path that is used for resources like + project templates and the debugger macros. + + This abstraction is needed to avoid platform-specific code all over + the place, since e.g. on Mac the resources are part of the application bundle. +*/ + +/*! + \fn QMainWindow *ICore::mainWindow() const + \brief Returns the main application window. + + For use as dialog parent etc. +*/ + +/*! + \fn IContext *ICore::currentContextObject() const + \brief Returns the context object of the current main context. + + \sa ICore::addAdditionalContext() + \sa ICore::addContextObject() +*/ + +/*! + \fn void ICore::addAdditionalContext(int context) + \brief Register additional context to be currently active. + + Appends the additional \a context to the list of currently active + contexts. You need to call ICore::updateContext to make that change + take effect. + + \sa ICore::removeAdditionalContext() + \sa ICore::hasContext() + \sa ICore::updateContext() +*/ + +/*! + \fn void ICore::removeAdditionalContext(int context) + \brief Removes the given \a context from the list of currently active contexts. + + You need to call ICore::updateContext to make that change + take effect. + + \sa ICore::addAdditionalContext() + \sa ICore::hasContext() + \sa ICore::updateContext() +*/ + +/*! + \fn bool ICore::hasContext(int context) const + \brief Returns if the given \a context is currently one of the active contexts. + + \sa ICore::addAdditionalContext() + \sa ICore::addContextObject() +*/ + +/*! + \fn void ICore::addContextObject(IContext *context) + \brief Registers an additional \a context object. + + After registration this context object gets automatically the + current context object whenever its widget gets focus. + + \sa ICore::removeContextObject() + \sa ICore::addAdditionalContext() + \sa ICore::currentContextObject() +*/ + +/*! + \fn void ICore::removeContextObject(IContext *context) + \brief Unregisters a \a context object from the list of know contexts. + + \sa ICore::addContextObject() + \sa ICore::addAdditionalContext() + \sa ICore::currentContextObject() +} +*/ + +/*! + \fn void ICore::updateContext() + \brief Update the list of active contexts after adding or removing additional ones. + + \sa ICore::addAdditionalContext() + \sa ICore::removeAdditionalContext() +*/ + +/*! + \fn void ICore::openFiles(const QStringList &fileNames) + \brief Open all files from a list of \a fileNames like it would be + done if they were given to the OpenPilot GCS on the command line, or + they were opened via \gui{File|Open}. +*/ + +/*! + \fn ICore::ICore() + \internal +*/ + +/*! + \fn ICore::~ICore() + \internal +*/ + +/*! + \fn void ICore::coreOpened() + \brief Emitted after all plugins have been loaded and the main window shown. +*/ + +/*! + \fn void ICore::saveSettingsRequested() + \brief Emitted to signal that the user has requested that the global settings + should be saved to disk. + + At the moment that happens when the application is closed, and on \gui{Save All}. +*/ + +/*! + \fn void ICore::optionsDialogRequested() + \brief Signal that allows plugins to perform actions just before the \gui{Tools|Options} + dialog is shown. +*/ + +/*! + \fn void ICore::coreAboutToClose() + \brief Plugins can do some pre-end-of-life actions when they get this signal. + + The application is guaranteed to shut down after this signal is emitted. + It's there as an addition to the usual plugin lifecycle methods, namely + IPlugin::shutdown(), just for convenience. +*/ + +/*! + \fn void ICore::contextAboutToChange(Core::IContext *context) + \brief Sent just before a new \a context becomes the current context + (meaning that its widget got focus). +*/ + +/*! + \fn void ICore::contextChanged(Core::IContext *context) + \brief Sent just after a new \a context became the current context + (meaning that its widget got focus). +*/ diff --git a/ground/src/plugins/coreplugin/mimedatabase.cpp b/ground/src/plugins/coreplugin/mimedatabase.cpp index b99321997..989574591 100644 --- a/ground/src/plugins/coreplugin/mimedatabase.cpp +++ b/ground/src/plugins/coreplugin/mimedatabase.cpp @@ -1,1180 +1,1180 @@ -/** - ****************************************************************************** - * - * @file mimedatabase.cpp - * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. - * Parts by Nokia Corporation (qt-info@nokia.com) Copyright (C) 2009. - * @brief - * @see The GNU Public License (GPL) Version 3 - * @defgroup coreplugin - * @{ - * - *****************************************************************************/ -/* - * 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; either version 3 of the License, or - * (at your option) any later version. - * - * 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 "mimedatabase.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -enum { debugMimeDB = 0 }; - -// XML tags in mime files -static const char *mimeInfoTagC = "mime-info"; -static const char *mimeTypeTagC = "mime-type"; -static const char *mimeTypeAttributeC = "type"; -static const char *subClassTagC = "sub-class-of"; -static const char *commentTagC = "comment"; -static const char *globTagC = "glob"; -static const char *aliasTagC = "alias"; -static const char *patternAttributeC = "pattern"; -static const char *localeAttributeC = "xml:lang"; - -static const char *magicTagC = "magic"; -static const char *priorityAttributeC = "priority"; -static const char *matchTagC = "match"; -static const char *matchValueAttributeC = "value"; -static const char *matchTypeAttributeC = "type"; -static const char *matchStringTypeValueC = "string"; -static const char *matchOffsetAttributeC = "offset"; - -// Types -static const char *textTypeC = "text/plain"; -static const char *binaryTypeC = "application/octet-stream"; - -// UTF16 byte order marks -static const char bigEndianByteOrderMarkC[] = "\xFE\xFF"; -static const char littleEndianByteOrderMarkC[] = "\xFF\xFE"; - -// Fallback priorities, must be low. -enum { BinaryMatchPriority = 1, TextMatchPriority = 2}; - -/* Parse sth like ( being optional): - *\code -?xml version="1.0" encoding="UTF-8"?> - - - - Qt QMake Profile - - - - *\endcode -*/ - -namespace Core { -namespace Internal { - -// FileMatchContext: Passed on to the mimetypes from the database -// when looking for a file match. It exists to enable reading the file -// contents "on demand" (as opposed to each mime type trying to open -// and read while checking). - -class FileMatchContext { - Q_DISABLE_COPY(FileMatchContext); -public: - // Max data to be read from a file - enum { MaxData = 2048 }; - - explicit FileMatchContext(const QFileInfo &fi); - - inline QString fileName() const { return m_fileName; } - // Return (cached) first MaxData bytes of file - QByteArray data(); - -private: - enum State { - // File cannot be read/does not exist - NoDataAvailable, - // Not read yet - DataNotRead, - // Available - DataRead }; - const QFileInfo m_fileInfo; - const QString m_fileName; - State m_state; - QByteArray m_data; -}; - -FileMatchContext::FileMatchContext(const QFileInfo &fi) : - m_fileInfo(fi), - m_fileName(fi.fileName()), - m_state(fi.isFile() && fi.isReadable() && fi.size() > 0 ? DataNotRead : NoDataAvailable) -{ -} - -QByteArray FileMatchContext::data() -{ - // Do we need to read? - if (m_state == DataNotRead) { - const QString fullName = m_fileInfo.absoluteFilePath(); - QFile file(fullName); - if (file.open(QIODevice::ReadOnly)) { - m_data = file.read(MaxData); - m_state = DataRead; - } else { - qWarning("%s failed to open %s: %s\n", Q_FUNC_INFO, fullName.toUtf8().constData(), file.errorString().toUtf8().constData()); - m_state = NoDataAvailable; - } - } - return m_data; -} - -// The binary fallback matcher for "application/octet-stream". -class BinaryMatcher : public IMagicMatcher { - Q_DISABLE_COPY(BinaryMatcher); -public: - BinaryMatcher() {} - virtual bool matches(const QByteArray & /*data*/) const { return true; } - virtual int priority() const { return BinaryMatchPriority; } -}; - -// A heuristic text file matcher: If the data do not contain any character -// below tab (9), detect as text. -class HeuristicTextMagicMatcher : public IMagicMatcher { - Q_DISABLE_COPY(HeuristicTextMagicMatcher); -public: - HeuristicTextMagicMatcher() {} - virtual bool matches(const QByteArray &data) const; - virtual int priority() const { return TextMatchPriority; } - - static bool isTextFile(const QByteArray &data); -}; - -bool HeuristicTextMagicMatcher::isTextFile(const QByteArray &data) -{ - const int size = data.size(); - for (int i = 0; i < size; i++) { - const char c = data.at(i); - if (c >= 0x01 && c < 0x09) // Sure-fire binary - return false; - if (c == 0) // Check for UTF16 - return data.startsWith(bigEndianByteOrderMarkC) || data.startsWith(littleEndianByteOrderMarkC); - } - return true; -} - -bool HeuristicTextMagicMatcher::matches(const QByteArray &data) const -{ - const bool rc = isTextFile(data); - if (debugMimeDB) - qDebug() << Q_FUNC_INFO << " on " << data.size() << " returns " << rc; - return rc; -} - -} // namespace Internal - -// MagicRule -MagicRule::MagicRule(const QByteArray &pattern, int startPos, int endPos) : - m_pattern(pattern), - m_startPos(startPos), - m_endPos(endPos) -{ -} - -bool MagicRule::matches(const QByteArray &data) const -{ - // Quick check - const int dataSize = data.size(); - if ((m_startPos + m_pattern.size()) >= dataSize) - return false; - // Most common: some string at position 0: - if (m_startPos == 0 && m_startPos == m_endPos) - return data.startsWith(m_pattern); - // Range - const int index = data.indexOf(m_pattern, m_startPos); - return index != -1 && index < m_endPos; -} - -MagicRule *MagicRule::createStringRule(const QString &c, int startPos, int endPos) -{ - return new MagicRule(c.toUtf8(), startPos, endPos); -} - -// List matcher -MagicRuleMatcher::MagicRuleMatcher() : - m_priority(65535) -{ -} - -void MagicRuleMatcher::add(const MagicRuleSharedPointer &rule) -{ - m_list.push_back(rule); -} - -bool MagicRuleMatcher::matches(const QByteArray &data) const -{ - const MagicRuleList::const_iterator cend = m_list.constEnd(); - for (MagicRuleList::const_iterator it = m_list.constBegin(); it != cend; ++it) - if ( (*it)->matches(data)) - return true; - return false; -} - -int MagicRuleMatcher::priority() const -{ - return m_priority; -} - -void MagicRuleMatcher::setPriority(int p) -{ - m_priority = p; -} - -// ---------- MimeTypeData -class MimeTypeData : public QSharedData { -public: - typedef QHash LocaleHash; - void clear(); - void debug(QTextStream &str, int indent = 0) const; - - QString type; - QString comment; - - LocaleHash localeComments; - QStringList aliases; - QList globPatterns; - QStringList subClassesOf; - QString preferredSuffix; - QStringList suffixes; - - typedef QSharedPointer IMagicMatcherSharedPointer; - typedef QList IMagicMatcherList; - IMagicMatcherList magicMatchers; -}; - -void MimeTypeData::clear() -{ - type.clear(); - comment.clear(); - aliases.clear(); - globPatterns.clear(); - subClassesOf.clear(); - preferredSuffix.clear(); - suffixes.clear(); - magicMatchers.clear(); -} - -void MimeTypeData::debug(QTextStream &str, int indent) const -{ - const QString indentS = QString(indent, QLatin1Char(' ')); - const QString comma = QString(1, QLatin1Char(',')); - str << indentS << "Type: " << type; - if (!aliases.empty()) - str << " Aliases: " << aliases.join(comma); - str << ", magic: " << magicMatchers.size() << '\n'; - str << indentS << "Comment: " << comment << '\n'; - if (!subClassesOf.empty()) - str << indentS << "SubClassesOf: " << subClassesOf.join(comma) << '\n'; - if (!globPatterns.empty()) { - str << indentS << "Glob: "; - foreach (const QRegExp &r, globPatterns) - str << r.pattern() << ' '; - str << '\n'; - if (!suffixes.empty()) { - str << indentS << "Suffixes: " << suffixes.join(comma) - << " preferred: " << preferredSuffix << '\n'; - } - } - str << '\n'; -} - -// ---------------- MimeType -MimeType::MimeType() : - m_d(new MimeTypeData) -{ -} - -MimeType::MimeType(const MimeType &rhs) : - m_d(rhs.m_d) -{ -} - -MimeType &MimeType::operator=(const MimeType &rhs) -{ - if (this != &rhs) - m_d = rhs.m_d; - return *this; -} - -MimeType::MimeType(const MimeTypeData &d) : - m_d(new MimeTypeData(d)) -{ -} - -MimeType::~MimeType() -{ -} - -void MimeType::clear() -{ - m_d->clear(); -} - -bool MimeType::isNull() const -{ - return m_d->type.isEmpty(); -} - -MimeType::operator bool() const -{ - return !isNull(); -} - -bool MimeType::isTopLevel() const -{ - return m_d->subClassesOf.empty(); -} - -QString MimeType::type() const -{ - return m_d->type; -} - -void MimeType::setType(const QString &type) -{ - m_d->type = type; -} - -QString MimeType::comment() const -{ - return m_d->comment; -} - -void MimeType::setComment(const QString &comment) -{ - m_d->comment = comment; -} - -// Return "en", "de", etc. derived from "en_US", de_DE". -static inline QString systemLanguage() -{ - QString name = QLocale::system().name(); - const int underScorePos = name.indexOf(QLatin1Char('_')); - if (underScorePos != -1) - name.truncate(underScorePos); - return name; -} - -QString MimeType::localeComment(const QString &localeArg) const -{ - const QString locale = localeArg.isEmpty() ? systemLanguage() : localeArg; - const MimeTypeData::LocaleHash::const_iterator it = m_d->localeComments.constFind(locale); - if (it == m_d->localeComments.constEnd()) - return m_d->comment; - return it.value(); -} - -void MimeType::setLocaleComment(const QString &locale, const QString &comment) -{ - m_d->localeComments[locale] = comment; -} - -QStringList MimeType::aliases() const -{ - return m_d->aliases; -} - -void MimeType::setAliases(const QStringList &a) -{ - m_d->aliases = a; -} - -QList MimeType::globPatterns() const -{ - return m_d->globPatterns; -} - -void MimeType::setGlobPatterns(const QList &g) -{ - m_d->globPatterns = g; -} - -QStringList MimeType::subClassesOf() const -{ - return m_d->subClassesOf; -} - -void MimeType::setSubClassesOf(const QStringList &s) -{ - m_d->subClassesOf = s; -} - -QString MimeType::preferredSuffix() const -{ - return m_d->preferredSuffix; -} - -bool MimeType::setPreferredSuffix(const QString &s) -{ - if (!m_d->suffixes.contains(s)) { - qWarning("%s: Attempt to set preferred suffix to '%s', which is not in the list of suffixes: %s.", - m_d->type.toUtf8().constData(), - s.toUtf8().constData(), - m_d->suffixes.join(QLatin1String(",")).toUtf8().constData()); - return false; - } - m_d->preferredSuffix = s; - return true; -} - -static QString formatFilterString(const QString &description, const QList &globs) -{ - QString rc; - if (globs.empty()) // Binary files - return rc; - { - QTextStream str(&rc); - str << description; - if (!globs.empty()) { - str << " ("; - const int size = globs.size(); - for (int i = 0; i < size; i++) { - if (i) - str << ' '; - str << globs.at(i).pattern(); - } - str << ')'; - } - } - return rc; -} - -QString MimeType::filterString() const -{ - // @todo: Use localeComment() once creator is shipped with translations - return formatFilterString(comment(), m_d->globPatterns); -} - -bool MimeType::matchesType(const QString &type) const -{ - return m_d->type == type || m_d->aliases.contains(type); -} - -unsigned MimeType::matchesFile(const QFileInfo &file) const -{ - Internal::FileMatchContext context(file); - return matchesFile(context); -} - -unsigned MimeType::matchesFile(Internal::FileMatchContext &c) const -{ - // check globs - foreach (QRegExp pattern, m_d->globPatterns) { - if (pattern.exactMatch(c.fileName())) - return GlobMatchPriority; - } - - // Nope, try magic matchers on context data - if (m_d->magicMatchers.isEmpty()) - return 0; - - const QByteArray data = c.data(); - if (!data.isEmpty()) { - foreach (MimeTypeData::IMagicMatcherSharedPointer matcher, m_d->magicMatchers) { - if (matcher->matches(data)) - return matcher->priority(); - } - } - return 0; -} - -QStringList MimeType::suffixes() const -{ - return m_d->suffixes; -} - -void MimeType::setSuffixes(const QStringList &s) -{ - m_d->suffixes = s; -} - -void MimeType::addMagicMatcher(const QSharedPointer &matcher) -{ - m_d->magicMatchers.push_back(matcher); -} - -QDebug operator<<(QDebug d, const MimeType &mt) -{ - QString s; - { - QTextStream str(&s); - mt.m_d->debug(str); - } - d << s; - return d; -} - -namespace Internal { - -// MimeDatabase helpers: Generic parser for a sequence of . -// Calls abstract handler function process for MimeType it finds. -class BaseMimeTypeParser { - Q_DISABLE_COPY(BaseMimeTypeParser); -public: - BaseMimeTypeParser(); - virtual ~BaseMimeTypeParser() {} - - bool parse(QIODevice *dev, const QString &fileName, QString *errorMessage); - -private: - // Overwrite to process the sequence of parsed data - virtual bool process(const MimeType &t, QString *errorMessage) = 0; - - void addGlobPattern(const QString &pattern, MimeTypeData *d) const; - - enum ParseStage { ParseBeginning, - ParseMimeInfo, - ParseMimeType, - ParseComment, - ParseGlobPattern, - ParseSubClass, - ParseAlias, - ParseMagic, - ParseMagicMatchRule, - ParseOtherMimeTypeSubTag, - ParseError }; - - static ParseStage nextStage(ParseStage currentStage, const QStringRef &startElement); - - const QRegExp m_suffixPattern; -}; - -BaseMimeTypeParser:: BaseMimeTypeParser() : - // RE to match a suffix glob pattern: "*.ext" (and not sth like "Makefile" or - // "*.log[1-9]" - m_suffixPattern(QLatin1String("^\\*\\.[\\w+]+$")) -{ - QTC_ASSERT(m_suffixPattern.isValid(), /**/); -} - -void BaseMimeTypeParser::addGlobPattern(const QString &pattern, MimeTypeData *d) const -{ - if (pattern.isEmpty()) - return; - // Collect patterns as a QRegExp list and filter out the plain - // suffix ones for our suffix list. Use first one as preferred - const QRegExp wildCard(pattern, Qt::CaseSensitive, QRegExp::Wildcard); - if (!wildCard.isValid()) { - qWarning("%s: Invalid wildcard '%s'.", - Q_FUNC_INFO, pattern.toUtf8().constData()); - return; - } - - d->globPatterns.push_back(wildCard); - if (m_suffixPattern.exactMatch(pattern)) { - const QString suffix = pattern.right(pattern.size() - 2); - d->suffixes.push_back(suffix); - if (d->preferredSuffix.isEmpty()) - d->preferredSuffix = suffix; - } -} - -BaseMimeTypeParser::ParseStage BaseMimeTypeParser::nextStage(ParseStage currentStage, const QStringRef &startElement) -{ - switch (currentStage) { - case ParseBeginning: - if (startElement == QLatin1String(mimeInfoTagC)) - return ParseMimeInfo; - if (startElement == QLatin1String(mimeTypeTagC)) - return ParseMimeType; - return ParseError; - case ParseMimeInfo: - return startElement == QLatin1String(mimeTypeTagC) ? ParseMimeType : ParseError; - case ParseMimeType: - case ParseComment: - case ParseGlobPattern: - case ParseSubClass: - case ParseAlias: - case ParseOtherMimeTypeSubTag: - case ParseMagicMatchRule: - if (startElement == QLatin1String(mimeTypeTagC)) // Sequence of - return ParseMimeType; - if (startElement == QLatin1String(commentTagC )) - return ParseComment; - if (startElement == QLatin1String(globTagC)) - return ParseGlobPattern; - if (startElement == QLatin1String(subClassTagC)) - return ParseSubClass; - if (startElement == QLatin1String(aliasTagC)) - return ParseAlias; - if (startElement == QLatin1String(magicTagC)) - return ParseMagic; - return ParseOtherMimeTypeSubTag; - case ParseMagic: - if (startElement == QLatin1String(matchTagC)) - return ParseMagicMatchRule; - break; - case ParseError: - break; - } - return ParseError; -} - -// Parse int number from an (attribute) string) -static bool parseNumber(const QString &n, int *target, QString *errorMessage) -{ - bool ok; - *target = n.toInt(&ok); - if (!ok) { - *errorMessage = QString::fromLatin1("Not a number '%1'.").arg(n); - return false; - } - return true; -} - -// Evaluate a magic match rule like -// -// -static bool addMagicMatchRule(const QXmlStreamAttributes &atts, - MagicRuleMatcher *ruleMatcher, - QString *errorMessage) -{ - const QString type = atts.value(QLatin1String(matchTypeAttributeC)).toString(); - if (type != QLatin1String(matchStringTypeValueC)) { - qWarning("%s: match type %s is not supported.", Q_FUNC_INFO, type.toUtf8().constData()); - return true; - } - const QString value = atts.value(QLatin1String(matchValueAttributeC)).toString(); - if (value.isEmpty()) { - *errorMessage = QString::fromLatin1("Empty match value detected."); - return false; - } - // Parse for offset as "1" or "1:10" - int startPos, endPos; - const QString offsetS = atts.value(QLatin1String(matchOffsetAttributeC)).toString(); - const int colonIndex = offsetS.indexOf(QLatin1Char(':')); - const QString startPosS = colonIndex == -1 ? offsetS : offsetS.mid(0, colonIndex); - const QString endPosS = colonIndex == -1 ? offsetS : offsetS.mid(colonIndex + 1); - if (!parseNumber(startPosS, &startPos, errorMessage) || !parseNumber(endPosS, &endPos, errorMessage)) - return false; - if (debugMimeDB) - qDebug() << Q_FUNC_INFO << value << startPos << endPos; - ruleMatcher->add(QSharedPointer(MagicRule::createStringRule(value, startPos, endPos))); - return true; -} - -bool BaseMimeTypeParser::parse(QIODevice *dev, const QString &fileName, QString *errorMessage) -{ - MimeTypeData data; - MagicRuleMatcher *ruleMatcher = 0; - - QXmlStreamReader reader(dev); - ParseStage ps = ParseBeginning; - while (!reader.atEnd()) { - switch (reader.readNext()) { - case QXmlStreamReader::StartElement: - ps = nextStage(ps, reader.name()); - switch (ps) { - case ParseMimeType: { // start parsing a type - const QString type = reader.attributes().value(QLatin1String(mimeTypeAttributeC)).toString(); - if (type.isEmpty()) { - reader.raiseError(QString::fromLatin1("Missing 'type'-attribute")); - } else { - data.type = type; - } - } - break; - case ParseGlobPattern: - addGlobPattern(reader.attributes().value(QLatin1String(patternAttributeC)).toString(), &data); - break; - case ParseSubClass: { - const QString inheritsFrom = reader.attributes().value(QLatin1String(mimeTypeAttributeC)).toString(); - if (!inheritsFrom.isEmpty()) - data.subClassesOf.push_back(inheritsFrom); - } - break; - case ParseComment: { - // comments have locale attributes. We want the default, English one - QString locale = reader.attributes().value(QLatin1String(localeAttributeC)).toString(); - const QString comment = QCoreApplication::translate("MimeType", reader.readElementText().toAscii()); - if (locale.isEmpty()) { - data.comment = comment; - } else { - data.localeComments.insert(locale, comment); - } - } - break; - case ParseAlias: { - const QString alias = reader.attributes().value(QLatin1String(mimeTypeAttributeC)).toString(); - if (!alias.isEmpty()) - data.aliases.push_back(alias); - } - break; - case ParseMagic: { - int priority = 0; - const QString priorityS = reader.attributes().value(QLatin1String(priorityAttributeC)).toString(); - if (!priorityS.isEmpty()) { - if (!parseNumber(priorityS, &priority, errorMessage)) - return false; - - } - ruleMatcher = new MagicRuleMatcher; - ruleMatcher->setPriority(priority); - } - break; - case ParseMagicMatchRule: - if (!addMagicMatchRule(reader.attributes(), ruleMatcher, errorMessage)) - return false; - break; - case ParseError: - reader.raiseError(QString::fromLatin1("Unexpected element <%1>").arg(reader.name().toString())); - break; - default: - break; - } // switch nextStage - break; - // continue switch QXmlStreamReader::Token... - case QXmlStreamReader::EndElement: // Finished element - if (reader.name() == QLatin1String(mimeTypeTagC)) { - if (!process(MimeType(data), errorMessage)) - return false; - data.clear(); - } else { - // Finished a match sequence - if (reader.name() == QLatin1String(QLatin1String(magicTagC))) { - data.magicMatchers.push_back(QSharedPointer(ruleMatcher)); - ruleMatcher = 0; - } - } - break; - - default: - break; - } // switch reader.readNext() - } - - if (reader.hasError()) { - *errorMessage = QString::fromLatin1("An error has been encountered at line %1 of %2: %3:").arg(reader.lineNumber()).arg(fileName, reader.errorString()); - return false; - } - return true; -} - -} // namespace Internal - -// MimeMapEntry: Entry of a type map, consisting of type and level. - -enum { Dangling = 32767 }; - -struct MimeMapEntry -{ - explicit MimeMapEntry(const MimeType &t = MimeType(), int aLevel = Dangling); - MimeType type; - int level; // hierachy level -}; - -MimeMapEntry::MimeMapEntry(const MimeType &t, int aLevel) : - type(t), - level(aLevel) -{ -} - -/* MimeDatabasePrivate: Requirements for storage: - * - Must be robust in case of incomplete hierachies, dangling entries - * - Plugins will not load and register their mime types in order - * of inheritance. - * - Multiple inheritance (several subClassesOf) can occur - * - Provide quick lookup by name - * - Provide quick lookup by file type. - * This basically rules out some pointer-based tree, so the structure choosen - * is: - * - An alias map QString> for mapping aliases to types - * - A Map for the types (MimeMapEntry being a pair of - * MimeType and (hierarchy) level. - * - A map QString> representing parent->child relations (enabling - * recursing over children) - * Using strings avoids dangling pointers. - * The hierarchy level is used for mapping by file types. When findByFile() - * is first called after addMimeType() it recurses over the hierarchy and sets - * the hierarchy level of the entries accordingly (0 toplevel, 1 first - * order...). It then does several passes over the type map, checking the - * globs for maxLevel, maxLevel-1....until it finds a match (idea being to - * to check the most specific types first). Starting a recursion from the - * leaves is not suitable since it will hit parent nodes several times. */ - -class MimeDatabasePrivate -{ - Q_DISABLE_COPY(MimeDatabasePrivate) -public: - MimeDatabasePrivate(); - - bool addMimeTypes(const QString &fileName, QString *errorMessage); - bool addMimeTypes(QIODevice *device, QString *errorMessage); - bool addMimeType(MimeType mt); - - // Returns a mime type or Null one if none found - MimeType findByType(const QString &type) const; - // Returns a mime type or Null one if none found - MimeType findByFile(const QFileInfo &f) const; - - bool setPreferredSuffix(const QString &typeOrAlias, const QString &suffix); - - // Return all known suffixes - QStringList suffixes() const; - QStringList filterStrings() const; - - void debug(QTextStream &str) const; - -private: - typedef QHash TypeMimeTypeMap; - typedef QHash AliasMap; - typedef QMultiHash ParentChildrenMap; - - bool addMimeTypes(QIODevice *device, const QString &fileName, QString *errorMessage); - inline const QString &resolveAlias(const QString &name) const; - MimeType findByFile(const QFileInfo &f, unsigned *priority) const; - void determineLevels(); - void raiseLevelRecursion(MimeMapEntry &e, int level); - - TypeMimeTypeMap m_typeMimeTypeMap; - AliasMap m_aliasMap; - ParentChildrenMap m_parentChildrenMap; - int m_maxLevel; -}; - -MimeDatabasePrivate::MimeDatabasePrivate() : - m_maxLevel(-1) -{ -} - -namespace Internal { - // Parser that builds MimeDB hierarchy by adding to MimeDatabasePrivate - class MimeTypeParser : public BaseMimeTypeParser { - public: - explicit MimeTypeParser(MimeDatabasePrivate &db) : m_db(db) {} - private: - virtual bool process(const MimeType &t, QString *) { m_db.addMimeType(t); return true; } - - MimeDatabasePrivate &m_db; - }; -} // namespace Internal - -bool MimeDatabasePrivate::addMimeTypes(QIODevice *device, const QString &fileName, QString *errorMessage) -{ - Internal::MimeTypeParser parser(*this); - return parser.parse(device, fileName, errorMessage); -} - -bool MimeDatabasePrivate::addMimeTypes(const QString &fileName, QString *errorMessage) -{ - QFile file(fileName); - if (!file.open(QIODevice::ReadOnly|QIODevice::Text)) { - *errorMessage = QString::fromLatin1("Cannot open %1: %2").arg(fileName, file.errorString()); - return false; - } - return addMimeTypes(&file, fileName, errorMessage); -} - -bool MimeDatabasePrivate::addMimeTypes(QIODevice *device, QString *errorMessage) -{ - return addMimeTypes(device, QLatin1String(""), errorMessage); -} - -bool MimeDatabasePrivate::addMimeType(MimeType mt) -{ - if (!mt) - return false; - - const QString type = mt.type(); - // Hack: Add a magic text matcher to "text/plain" and the fallback matcher to - // binary types "application/octet-stream" - if (type == QLatin1String(textTypeC)) { - mt.addMagicMatcher(QSharedPointer(new Internal::HeuristicTextMagicMatcher)); - } else { - if (type == QLatin1String(binaryTypeC)) - mt.addMagicMatcher(QSharedPointer(new Internal::BinaryMatcher)); - } - // insert the type. - m_typeMimeTypeMap.insert(type, MimeMapEntry(mt)); - // Register the children, resolved via alias map. Note that it is still - // possible that aliases end up in the map if the parent classes are not inserted - // at this point (thus their aliases not known). - const QStringList subClassesOf = mt.subClassesOf(); - if (!subClassesOf.empty()) { - const QStringList::const_iterator socend = subClassesOf.constEnd(); - for (QStringList::const_iterator soit = subClassesOf.constBegin(); soit != socend; ++soit) - m_parentChildrenMap.insert(resolveAlias(*soit), type); - } - // register aliasses - const QStringList aliases = mt.aliases(); - if (!aliases.empty()) { - const QStringList::const_iterator cend = aliases.constEnd(); - for (QStringList::const_iterator it = aliases.constBegin(); it != cend; ++it) - m_aliasMap.insert(*it, type); - } - m_maxLevel = -1; // Mark as dirty - return true; -} - -const QString &MimeDatabasePrivate::resolveAlias(const QString &name) const -{ - const AliasMap::const_iterator aliasIt = m_aliasMap.constFind(name); - return aliasIt == m_aliasMap.constEnd() ? name : aliasIt.value(); -} - -void MimeDatabasePrivate::raiseLevelRecursion(MimeMapEntry &e, int level) -{ - if (e.level == Dangling || e.level < level) - e.level = level; - if (m_maxLevel < level) - m_maxLevel = level; - // At all events recurse over children since nodes might have been - // added. - const QStringList childTypes = m_parentChildrenMap.values(e.type.type()); - if (childTypes.empty()) - return; - // look them up in the type->mime type map - const int nextLevel = level + 1; - const TypeMimeTypeMap::iterator tm_end = m_typeMimeTypeMap.end(); - const QStringList::const_iterator cend = childTypes.constEnd(); - for (QStringList::const_iterator it = childTypes.constBegin(); it != cend; ++it) { - const TypeMimeTypeMap::iterator tm_it = m_typeMimeTypeMap.find(resolveAlias(*it)); - if (tm_it == tm_end) { - qWarning("%s: Inconsistent mime hierarchy detected, child %s of %s cannot be found.", - Q_FUNC_INFO, it->toUtf8().constData(), e.type.type().toUtf8().constData()); - } else { - raiseLevelRecursion(*tm_it, nextLevel); - } - } -} - -void MimeDatabasePrivate::determineLevels() -{ - // Loop over toplevels and recurse down their hierarchies. - // Determine top levels by subtracting the children from the parent - // set. Note that a toplevel at this point might have 'subclassesOf' - // set to some class that is not in the DB, so, checking for an empty - // 'subclassesOf' set is not sufficient to find the toplevels. - // First, take the parent->child entries whose parent exists and build - // sets of parents/children - QSet parentSet, childrenSet; - const ParentChildrenMap::const_iterator pcend = m_parentChildrenMap.constEnd(); - for (ParentChildrenMap::const_iterator it = m_parentChildrenMap.constBegin(); it != pcend; ++it) - if (m_typeMimeTypeMap.contains(it.key())) { - parentSet.insert(it.key()); - childrenSet.insert(it.value()); - } - const QSet topLevels = parentSet.subtract(childrenSet); - if (debugMimeDB) - qDebug() << Q_FUNC_INFO << "top levels" << topLevels; - const TypeMimeTypeMap::iterator tm_end = m_typeMimeTypeMap.end(); - const QSet::const_iterator tl_cend = topLevels.constEnd(); - for (QSet::const_iterator tl_it = topLevels.constBegin(); tl_it != tl_cend; ++tl_it) { - const TypeMimeTypeMap::iterator tm_it = m_typeMimeTypeMap.find(resolveAlias(*tl_it)); - if (tm_it == tm_end) { - qWarning("%s: Inconsistent mime hierarchy detected, top level element %s cannot be found.", - Q_FUNC_INFO, tl_it->toUtf8().constData()); - } else { - raiseLevelRecursion(tm_it.value(), 0); - } - } -} - -bool MimeDatabasePrivate::setPreferredSuffix(const QString &typeOrAlias, const QString &suffix) -{ - TypeMimeTypeMap::iterator tit = m_typeMimeTypeMap.find(resolveAlias(typeOrAlias)); - if (tit != m_typeMimeTypeMap.end()) - return tit.value().type.setPreferredSuffix(suffix); - return false; -} - -// Returns a mime type or Null one if none found -MimeType MimeDatabasePrivate::findByType(const QString &typeOrAlias) const -{ - const TypeMimeTypeMap::const_iterator tit = m_typeMimeTypeMap.constFind(resolveAlias(typeOrAlias)); - if (tit != m_typeMimeTypeMap.constEnd()) - return tit.value().type; - return MimeType(); -} - -// Debugging wrapper around findByFile() -MimeType MimeDatabasePrivate::findByFile(const QFileInfo &f) const -{ - unsigned priority = 0; - if (debugMimeDB) - qDebug() << '>' << Q_FUNC_INFO << f.fileName(); - const MimeType rc = findByFile(f, &priority); - if (debugMimeDB) { - if (rc) { - qDebug() << " MimeMapEntryList; - - // Is the hierarchy set up in case we find several matches? - if (m_maxLevel < 0) { - MimeDatabasePrivate *db = const_cast(this); - db->determineLevels(); - } - // Starting from max level (most specific): Try to find a match of - // best (max) priority. Return if a glob match triggers. - *priorityPtr = 0; - unsigned maxPriority = 0; - MimeType rc; - Internal::FileMatchContext context(f); - const TypeMimeTypeMap::const_iterator cend = m_typeMimeTypeMap.constEnd(); - for (int level = m_maxLevel; level >= 0; level--) - for (TypeMimeTypeMap::const_iterator it = m_typeMimeTypeMap.constBegin(); it != cend; ++it) - if (it.value().level == level) { - const unsigned priority = it.value().type.matchesFile(context); - if (debugMimeDB > 1) - qDebug() << "pass" << level << it.value().type.type() << " matches " << priority; - if (priority) - if (priority > maxPriority) { - rc = it.value().type; - maxPriority = priority; - // Glob (exact) match?! We are done - if (maxPriority == MimeType::GlobMatchPriority) { - *priorityPtr = priority; - return rc; - } - } - } - return rc; -} - -// Return all known suffixes -QStringList MimeDatabasePrivate::suffixes() const -{ - QStringList rc; - const TypeMimeTypeMap::const_iterator cend = m_typeMimeTypeMap.constEnd(); - for (TypeMimeTypeMap::const_iterator it = m_typeMimeTypeMap.constBegin(); it != cend; ++it) - rc += it.value().type.suffixes(); - return rc; -} - -QStringList MimeDatabasePrivate::filterStrings() const -{ - QStringList rc; - const TypeMimeTypeMap::const_iterator cend = m_typeMimeTypeMap.constEnd(); - for (TypeMimeTypeMap::const_iterator it = m_typeMimeTypeMap.constBegin(); it != cend; ++it) - rc += it.value().type.filterString(); - return rc; -} - -void MimeDatabasePrivate::debug(QTextStream &str) const -{ - str << ">MimeDatabase\n"; - const TypeMimeTypeMap::const_iterator cend = m_typeMimeTypeMap.constEnd(); - for (TypeMimeTypeMap::const_iterator it = m_typeMimeTypeMap.constBegin(); it != cend; ++it) { - str << "Entry level " << it.value().level << '\n'; - it.value().type.m_d->debug(str); - } - str << "findByType(typeOrAlias); -} - -MimeType MimeDatabase::findByFile(const QFileInfo &f) const -{ - return m_d->findByFile(f); -} - -bool MimeDatabase::addMimeType(const MimeType &mt) -{ - return m_d->addMimeType(mt); -} - -bool MimeDatabase::addMimeTypes(const QString &fileName, QString *errorMessage) -{ - return m_d->addMimeTypes(fileName, errorMessage); -} - -bool MimeDatabase::addMimeTypes(QIODevice *device, QString *errorMessage) -{ - return m_d->addMimeTypes(device, errorMessage); -} - -QStringList MimeDatabase::suffixes() const -{ - return m_d->suffixes(); -} - -QStringList MimeDatabase::filterStrings() const -{ - return m_d->filterStrings(); -} - -QString MimeDatabase::preferredSuffixByType(const QString &type) const -{ - if (const MimeType mt = findByType(type)) - return mt.preferredSuffix(); - return QString(); -} - -QString MimeDatabase::preferredSuffixByFile(const QFileInfo &f) const -{ - if (const MimeType mt = findByFile(f)) - return mt.preferredSuffix(); - return QString(); -} - -bool MimeDatabase::setPreferredSuffix(const QString &typeOrAlias, const QString &suffix) -{ - return m_d->setPreferredSuffix(typeOrAlias, suffix); -} - -QDebug operator<<(QDebug d, const MimeDatabase &mt) -{ - QString s; - { - QTextStream str(&s); - mt.m_d->debug(str); - } - d << s; - return d; -} - -} // namespace Core +/** + ****************************************************************************** + * + * @file mimedatabase.cpp + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. + * Parts by Nokia Corporation (qt-info@nokia.com) Copyright (C) 2009. + * @brief + * @see The GNU Public License (GPL) Version 3 + * @defgroup coreplugin + * @{ + * + *****************************************************************************/ +/* + * 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; either version 3 of the License, or + * (at your option) any later version. + * + * 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 "mimedatabase.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +enum { debugMimeDB = 0 }; + +// XML tags in mime files +static const char *mimeInfoTagC = "mime-info"; +static const char *mimeTypeTagC = "mime-type"; +static const char *mimeTypeAttributeC = "type"; +static const char *subClassTagC = "sub-class-of"; +static const char *commentTagC = "comment"; +static const char *globTagC = "glob"; +static const char *aliasTagC = "alias"; +static const char *patternAttributeC = "pattern"; +static const char *localeAttributeC = "xml:lang"; + +static const char *magicTagC = "magic"; +static const char *priorityAttributeC = "priority"; +static const char *matchTagC = "match"; +static const char *matchValueAttributeC = "value"; +static const char *matchTypeAttributeC = "type"; +static const char *matchStringTypeValueC = "string"; +static const char *matchOffsetAttributeC = "offset"; + +// Types +static const char *textTypeC = "text/plain"; +static const char *binaryTypeC = "application/octet-stream"; + +// UTF16 byte order marks +static const char bigEndianByteOrderMarkC[] = "\xFE\xFF"; +static const char littleEndianByteOrderMarkC[] = "\xFF\xFE"; + +// Fallback priorities, must be low. +enum { BinaryMatchPriority = 1, TextMatchPriority = 2}; + +/* Parse sth like ( being optional): + *\code +?xml version="1.0" encoding="UTF-8"?> + + + + Qt QMake Profile + + + + *\endcode +*/ + +namespace Core { +namespace Internal { + +// FileMatchContext: Passed on to the mimetypes from the database +// when looking for a file match. It exists to enable reading the file +// contents "on demand" (as opposed to each mime type trying to open +// and read while checking). + +class FileMatchContext { + Q_DISABLE_COPY(FileMatchContext); +public: + // Max data to be read from a file + enum { MaxData = 2048 }; + + explicit FileMatchContext(const QFileInfo &fi); + + inline QString fileName() const { return m_fileName; } + // Return (cached) first MaxData bytes of file + QByteArray data(); + +private: + enum State { + // File cannot be read/does not exist + NoDataAvailable, + // Not read yet + DataNotRead, + // Available + DataRead }; + const QFileInfo m_fileInfo; + const QString m_fileName; + State m_state; + QByteArray m_data; +}; + +FileMatchContext::FileMatchContext(const QFileInfo &fi) : + m_fileInfo(fi), + m_fileName(fi.fileName()), + m_state(fi.isFile() && fi.isReadable() && fi.size() > 0 ? DataNotRead : NoDataAvailable) +{ +} + +QByteArray FileMatchContext::data() +{ + // Do we need to read? + if (m_state == DataNotRead) { + const QString fullName = m_fileInfo.absoluteFilePath(); + QFile file(fullName); + if (file.open(QIODevice::ReadOnly)) { + m_data = file.read(MaxData); + m_state = DataRead; + } else { + qWarning("%s failed to open %s: %s\n", Q_FUNC_INFO, fullName.toUtf8().constData(), file.errorString().toUtf8().constData()); + m_state = NoDataAvailable; + } + } + return m_data; +} + +// The binary fallback matcher for "application/octet-stream". +class BinaryMatcher : public IMagicMatcher { + Q_DISABLE_COPY(BinaryMatcher); +public: + BinaryMatcher() {} + virtual bool matches(const QByteArray & /*data*/) const { return true; } + virtual int priority() const { return BinaryMatchPriority; } +}; + +// A heuristic text file matcher: If the data do not contain any character +// below tab (9), detect as text. +class HeuristicTextMagicMatcher : public IMagicMatcher { + Q_DISABLE_COPY(HeuristicTextMagicMatcher); +public: + HeuristicTextMagicMatcher() {} + virtual bool matches(const QByteArray &data) const; + virtual int priority() const { return TextMatchPriority; } + + static bool isTextFile(const QByteArray &data); +}; + +bool HeuristicTextMagicMatcher::isTextFile(const QByteArray &data) +{ + const int size = data.size(); + for (int i = 0; i < size; i++) { + const char c = data.at(i); + if (c >= 0x01 && c < 0x09) // Sure-fire binary + return false; + if (c == 0) // Check for UTF16 + return data.startsWith(bigEndianByteOrderMarkC) || data.startsWith(littleEndianByteOrderMarkC); + } + return true; +} + +bool HeuristicTextMagicMatcher::matches(const QByteArray &data) const +{ + const bool rc = isTextFile(data); + if (debugMimeDB) + qDebug() << Q_FUNC_INFO << " on " << data.size() << " returns " << rc; + return rc; +} + +} // namespace Internal + +// MagicRule +MagicRule::MagicRule(const QByteArray &pattern, int startPos, int endPos) : + m_pattern(pattern), + m_startPos(startPos), + m_endPos(endPos) +{ +} + +bool MagicRule::matches(const QByteArray &data) const +{ + // Quick check + const int dataSize = data.size(); + if ((m_startPos + m_pattern.size()) >= dataSize) + return false; + // Most common: some string at position 0: + if (m_startPos == 0 && m_startPos == m_endPos) + return data.startsWith(m_pattern); + // Range + const int index = data.indexOf(m_pattern, m_startPos); + return index != -1 && index < m_endPos; +} + +MagicRule *MagicRule::createStringRule(const QString &c, int startPos, int endPos) +{ + return new MagicRule(c.toUtf8(), startPos, endPos); +} + +// List matcher +MagicRuleMatcher::MagicRuleMatcher() : + m_priority(65535) +{ +} + +void MagicRuleMatcher::add(const MagicRuleSharedPointer &rule) +{ + m_list.push_back(rule); +} + +bool MagicRuleMatcher::matches(const QByteArray &data) const +{ + const MagicRuleList::const_iterator cend = m_list.constEnd(); + for (MagicRuleList::const_iterator it = m_list.constBegin(); it != cend; ++it) + if ( (*it)->matches(data)) + return true; + return false; +} + +int MagicRuleMatcher::priority() const +{ + return m_priority; +} + +void MagicRuleMatcher::setPriority(int p) +{ + m_priority = p; +} + +// ---------- MimeTypeData +class MimeTypeData : public QSharedData { +public: + typedef QHash LocaleHash; + void clear(); + void debug(QTextStream &str, int indent = 0) const; + + QString type; + QString comment; + + LocaleHash localeComments; + QStringList aliases; + QList globPatterns; + QStringList subClassesOf; + QString preferredSuffix; + QStringList suffixes; + + typedef QSharedPointer IMagicMatcherSharedPointer; + typedef QList IMagicMatcherList; + IMagicMatcherList magicMatchers; +}; + +void MimeTypeData::clear() +{ + type.clear(); + comment.clear(); + aliases.clear(); + globPatterns.clear(); + subClassesOf.clear(); + preferredSuffix.clear(); + suffixes.clear(); + magicMatchers.clear(); +} + +void MimeTypeData::debug(QTextStream &str, int indent) const +{ + const QString indentS = QString(indent, QLatin1Char(' ')); + const QString comma = QString(1, QLatin1Char(',')); + str << indentS << "Type: " << type; + if (!aliases.empty()) + str << " Aliases: " << aliases.join(comma); + str << ", magic: " << magicMatchers.size() << '\n'; + str << indentS << "Comment: " << comment << '\n'; + if (!subClassesOf.empty()) + str << indentS << "SubClassesOf: " << subClassesOf.join(comma) << '\n'; + if (!globPatterns.empty()) { + str << indentS << "Glob: "; + foreach (const QRegExp &r, globPatterns) + str << r.pattern() << ' '; + str << '\n'; + if (!suffixes.empty()) { + str << indentS << "Suffixes: " << suffixes.join(comma) + << " preferred: " << preferredSuffix << '\n'; + } + } + str << '\n'; +} + +// ---------------- MimeType +MimeType::MimeType() : + m_d(new MimeTypeData) +{ +} + +MimeType::MimeType(const MimeType &rhs) : + m_d(rhs.m_d) +{ +} + +MimeType &MimeType::operator=(const MimeType &rhs) +{ + if (this != &rhs) + m_d = rhs.m_d; + return *this; +} + +MimeType::MimeType(const MimeTypeData &d) : + m_d(new MimeTypeData(d)) +{ +} + +MimeType::~MimeType() +{ +} + +void MimeType::clear() +{ + m_d->clear(); +} + +bool MimeType::isNull() const +{ + return m_d->type.isEmpty(); +} + +MimeType::operator bool() const +{ + return !isNull(); +} + +bool MimeType::isTopLevel() const +{ + return m_d->subClassesOf.empty(); +} + +QString MimeType::type() const +{ + return m_d->type; +} + +void MimeType::setType(const QString &type) +{ + m_d->type = type; +} + +QString MimeType::comment() const +{ + return m_d->comment; +} + +void MimeType::setComment(const QString &comment) +{ + m_d->comment = comment; +} + +// Return "en", "de", etc. derived from "en_US", de_DE". +static inline QString systemLanguage() +{ + QString name = QLocale::system().name(); + const int underScorePos = name.indexOf(QLatin1Char('_')); + if (underScorePos != -1) + name.truncate(underScorePos); + return name; +} + +QString MimeType::localeComment(const QString &localeArg) const +{ + const QString locale = localeArg.isEmpty() ? systemLanguage() : localeArg; + const MimeTypeData::LocaleHash::const_iterator it = m_d->localeComments.constFind(locale); + if (it == m_d->localeComments.constEnd()) + return m_d->comment; + return it.value(); +} + +void MimeType::setLocaleComment(const QString &locale, const QString &comment) +{ + m_d->localeComments[locale] = comment; +} + +QStringList MimeType::aliases() const +{ + return m_d->aliases; +} + +void MimeType::setAliases(const QStringList &a) +{ + m_d->aliases = a; +} + +QList MimeType::globPatterns() const +{ + return m_d->globPatterns; +} + +void MimeType::setGlobPatterns(const QList &g) +{ + m_d->globPatterns = g; +} + +QStringList MimeType::subClassesOf() const +{ + return m_d->subClassesOf; +} + +void MimeType::setSubClassesOf(const QStringList &s) +{ + m_d->subClassesOf = s; +} + +QString MimeType::preferredSuffix() const +{ + return m_d->preferredSuffix; +} + +bool MimeType::setPreferredSuffix(const QString &s) +{ + if (!m_d->suffixes.contains(s)) { + qWarning("%s: Attempt to set preferred suffix to '%s', which is not in the list of suffixes: %s.", + m_d->type.toUtf8().constData(), + s.toUtf8().constData(), + m_d->suffixes.join(QLatin1String(",")).toUtf8().constData()); + return false; + } + m_d->preferredSuffix = s; + return true; +} + +static QString formatFilterString(const QString &description, const QList &globs) +{ + QString rc; + if (globs.empty()) // Binary files + return rc; + { + QTextStream str(&rc); + str << description; + if (!globs.empty()) { + str << " ("; + const int size = globs.size(); + for (int i = 0; i < size; i++) { + if (i) + str << ' '; + str << globs.at(i).pattern(); + } + str << ')'; + } + } + return rc; +} + +QString MimeType::filterString() const +{ + // @todo: Use localeComment() once the GCS is shipped with translations + return formatFilterString(comment(), m_d->globPatterns); +} + +bool MimeType::matchesType(const QString &type) const +{ + return m_d->type == type || m_d->aliases.contains(type); +} + +unsigned MimeType::matchesFile(const QFileInfo &file) const +{ + Internal::FileMatchContext context(file); + return matchesFile(context); +} + +unsigned MimeType::matchesFile(Internal::FileMatchContext &c) const +{ + // check globs + foreach (QRegExp pattern, m_d->globPatterns) { + if (pattern.exactMatch(c.fileName())) + return GlobMatchPriority; + } + + // Nope, try magic matchers on context data + if (m_d->magicMatchers.isEmpty()) + return 0; + + const QByteArray data = c.data(); + if (!data.isEmpty()) { + foreach (MimeTypeData::IMagicMatcherSharedPointer matcher, m_d->magicMatchers) { + if (matcher->matches(data)) + return matcher->priority(); + } + } + return 0; +} + +QStringList MimeType::suffixes() const +{ + return m_d->suffixes; +} + +void MimeType::setSuffixes(const QStringList &s) +{ + m_d->suffixes = s; +} + +void MimeType::addMagicMatcher(const QSharedPointer &matcher) +{ + m_d->magicMatchers.push_back(matcher); +} + +QDebug operator<<(QDebug d, const MimeType &mt) +{ + QString s; + { + QTextStream str(&s); + mt.m_d->debug(str); + } + d << s; + return d; +} + +namespace Internal { + +// MimeDatabase helpers: Generic parser for a sequence of . +// Calls abstract handler function process for MimeType it finds. +class BaseMimeTypeParser { + Q_DISABLE_COPY(BaseMimeTypeParser); +public: + BaseMimeTypeParser(); + virtual ~BaseMimeTypeParser() {} + + bool parse(QIODevice *dev, const QString &fileName, QString *errorMessage); + +private: + // Overwrite to process the sequence of parsed data + virtual bool process(const MimeType &t, QString *errorMessage) = 0; + + void addGlobPattern(const QString &pattern, MimeTypeData *d) const; + + enum ParseStage { ParseBeginning, + ParseMimeInfo, + ParseMimeType, + ParseComment, + ParseGlobPattern, + ParseSubClass, + ParseAlias, + ParseMagic, + ParseMagicMatchRule, + ParseOtherMimeTypeSubTag, + ParseError }; + + static ParseStage nextStage(ParseStage currentStage, const QStringRef &startElement); + + const QRegExp m_suffixPattern; +}; + +BaseMimeTypeParser:: BaseMimeTypeParser() : + // RE to match a suffix glob pattern: "*.ext" (and not sth like "Makefile" or + // "*.log[1-9]" + m_suffixPattern(QLatin1String("^\\*\\.[\\w+]+$")) +{ + QTC_ASSERT(m_suffixPattern.isValid(), /**/); +} + +void BaseMimeTypeParser::addGlobPattern(const QString &pattern, MimeTypeData *d) const +{ + if (pattern.isEmpty()) + return; + // Collect patterns as a QRegExp list and filter out the plain + // suffix ones for our suffix list. Use first one as preferred + const QRegExp wildCard(pattern, Qt::CaseSensitive, QRegExp::Wildcard); + if (!wildCard.isValid()) { + qWarning("%s: Invalid wildcard '%s'.", + Q_FUNC_INFO, pattern.toUtf8().constData()); + return; + } + + d->globPatterns.push_back(wildCard); + if (m_suffixPattern.exactMatch(pattern)) { + const QString suffix = pattern.right(pattern.size() - 2); + d->suffixes.push_back(suffix); + if (d->preferredSuffix.isEmpty()) + d->preferredSuffix = suffix; + } +} + +BaseMimeTypeParser::ParseStage BaseMimeTypeParser::nextStage(ParseStage currentStage, const QStringRef &startElement) +{ + switch (currentStage) { + case ParseBeginning: + if (startElement == QLatin1String(mimeInfoTagC)) + return ParseMimeInfo; + if (startElement == QLatin1String(mimeTypeTagC)) + return ParseMimeType; + return ParseError; + case ParseMimeInfo: + return startElement == QLatin1String(mimeTypeTagC) ? ParseMimeType : ParseError; + case ParseMimeType: + case ParseComment: + case ParseGlobPattern: + case ParseSubClass: + case ParseAlias: + case ParseOtherMimeTypeSubTag: + case ParseMagicMatchRule: + if (startElement == QLatin1String(mimeTypeTagC)) // Sequence of + return ParseMimeType; + if (startElement == QLatin1String(commentTagC )) + return ParseComment; + if (startElement == QLatin1String(globTagC)) + return ParseGlobPattern; + if (startElement == QLatin1String(subClassTagC)) + return ParseSubClass; + if (startElement == QLatin1String(aliasTagC)) + return ParseAlias; + if (startElement == QLatin1String(magicTagC)) + return ParseMagic; + return ParseOtherMimeTypeSubTag; + case ParseMagic: + if (startElement == QLatin1String(matchTagC)) + return ParseMagicMatchRule; + break; + case ParseError: + break; + } + return ParseError; +} + +// Parse int number from an (attribute) string) +static bool parseNumber(const QString &n, int *target, QString *errorMessage) +{ + bool ok; + *target = n.toInt(&ok); + if (!ok) { + *errorMessage = QString::fromLatin1("Not a number '%1'.").arg(n); + return false; + } + return true; +} + +// Evaluate a magic match rule like +// +// +static bool addMagicMatchRule(const QXmlStreamAttributes &atts, + MagicRuleMatcher *ruleMatcher, + QString *errorMessage) +{ + const QString type = atts.value(QLatin1String(matchTypeAttributeC)).toString(); + if (type != QLatin1String(matchStringTypeValueC)) { + qWarning("%s: match type %s is not supported.", Q_FUNC_INFO, type.toUtf8().constData()); + return true; + } + const QString value = atts.value(QLatin1String(matchValueAttributeC)).toString(); + if (value.isEmpty()) { + *errorMessage = QString::fromLatin1("Empty match value detected."); + return false; + } + // Parse for offset as "1" or "1:10" + int startPos, endPos; + const QString offsetS = atts.value(QLatin1String(matchOffsetAttributeC)).toString(); + const int colonIndex = offsetS.indexOf(QLatin1Char(':')); + const QString startPosS = colonIndex == -1 ? offsetS : offsetS.mid(0, colonIndex); + const QString endPosS = colonIndex == -1 ? offsetS : offsetS.mid(colonIndex + 1); + if (!parseNumber(startPosS, &startPos, errorMessage) || !parseNumber(endPosS, &endPos, errorMessage)) + return false; + if (debugMimeDB) + qDebug() << Q_FUNC_INFO << value << startPos << endPos; + ruleMatcher->add(QSharedPointer(MagicRule::createStringRule(value, startPos, endPos))); + return true; +} + +bool BaseMimeTypeParser::parse(QIODevice *dev, const QString &fileName, QString *errorMessage) +{ + MimeTypeData data; + MagicRuleMatcher *ruleMatcher = 0; + + QXmlStreamReader reader(dev); + ParseStage ps = ParseBeginning; + while (!reader.atEnd()) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement: + ps = nextStage(ps, reader.name()); + switch (ps) { + case ParseMimeType: { // start parsing a type + const QString type = reader.attributes().value(QLatin1String(mimeTypeAttributeC)).toString(); + if (type.isEmpty()) { + reader.raiseError(QString::fromLatin1("Missing 'type'-attribute")); + } else { + data.type = type; + } + } + break; + case ParseGlobPattern: + addGlobPattern(reader.attributes().value(QLatin1String(patternAttributeC)).toString(), &data); + break; + case ParseSubClass: { + const QString inheritsFrom = reader.attributes().value(QLatin1String(mimeTypeAttributeC)).toString(); + if (!inheritsFrom.isEmpty()) + data.subClassesOf.push_back(inheritsFrom); + } + break; + case ParseComment: { + // comments have locale attributes. We want the default, English one + QString locale = reader.attributes().value(QLatin1String(localeAttributeC)).toString(); + const QString comment = QCoreApplication::translate("MimeType", reader.readElementText().toAscii()); + if (locale.isEmpty()) { + data.comment = comment; + } else { + data.localeComments.insert(locale, comment); + } + } + break; + case ParseAlias: { + const QString alias = reader.attributes().value(QLatin1String(mimeTypeAttributeC)).toString(); + if (!alias.isEmpty()) + data.aliases.push_back(alias); + } + break; + case ParseMagic: { + int priority = 0; + const QString priorityS = reader.attributes().value(QLatin1String(priorityAttributeC)).toString(); + if (!priorityS.isEmpty()) { + if (!parseNumber(priorityS, &priority, errorMessage)) + return false; + + } + ruleMatcher = new MagicRuleMatcher; + ruleMatcher->setPriority(priority); + } + break; + case ParseMagicMatchRule: + if (!addMagicMatchRule(reader.attributes(), ruleMatcher, errorMessage)) + return false; + break; + case ParseError: + reader.raiseError(QString::fromLatin1("Unexpected element <%1>").arg(reader.name().toString())); + break; + default: + break; + } // switch nextStage + break; + // continue switch QXmlStreamReader::Token... + case QXmlStreamReader::EndElement: // Finished element + if (reader.name() == QLatin1String(mimeTypeTagC)) { + if (!process(MimeType(data), errorMessage)) + return false; + data.clear(); + } else { + // Finished a match sequence + if (reader.name() == QLatin1String(QLatin1String(magicTagC))) { + data.magicMatchers.push_back(QSharedPointer(ruleMatcher)); + ruleMatcher = 0; + } + } + break; + + default: + break; + } // switch reader.readNext() + } + + if (reader.hasError()) { + *errorMessage = QString::fromLatin1("An error has been encountered at line %1 of %2: %3:").arg(reader.lineNumber()).arg(fileName, reader.errorString()); + return false; + } + return true; +} + +} // namespace Internal + +// MimeMapEntry: Entry of a type map, consisting of type and level. + +enum { Dangling = 32767 }; + +struct MimeMapEntry +{ + explicit MimeMapEntry(const MimeType &t = MimeType(), int aLevel = Dangling); + MimeType type; + int level; // hierachy level +}; + +MimeMapEntry::MimeMapEntry(const MimeType &t, int aLevel) : + type(t), + level(aLevel) +{ +} + +/* MimeDatabasePrivate: Requirements for storage: + * - Must be robust in case of incomplete hierachies, dangling entries + * - Plugins will not load and register their mime types in order + * of inheritance. + * - Multiple inheritance (several subClassesOf) can occur + * - Provide quick lookup by name + * - Provide quick lookup by file type. + * This basically rules out some pointer-based tree, so the structure choosen + * is: + * - An alias map QString> for mapping aliases to types + * - A Map for the types (MimeMapEntry being a pair of + * MimeType and (hierarchy) level. + * - A map QString> representing parent->child relations (enabling + * recursing over children) + * Using strings avoids dangling pointers. + * The hierarchy level is used for mapping by file types. When findByFile() + * is first called after addMimeType() it recurses over the hierarchy and sets + * the hierarchy level of the entries accordingly (0 toplevel, 1 first + * order...). It then does several passes over the type map, checking the + * globs for maxLevel, maxLevel-1....until it finds a match (idea being to + * to check the most specific types first). Starting a recursion from the + * leaves is not suitable since it will hit parent nodes several times. */ + +class MimeDatabasePrivate +{ + Q_DISABLE_COPY(MimeDatabasePrivate) +public: + MimeDatabasePrivate(); + + bool addMimeTypes(const QString &fileName, QString *errorMessage); + bool addMimeTypes(QIODevice *device, QString *errorMessage); + bool addMimeType(MimeType mt); + + // Returns a mime type or Null one if none found + MimeType findByType(const QString &type) const; + // Returns a mime type or Null one if none found + MimeType findByFile(const QFileInfo &f) const; + + bool setPreferredSuffix(const QString &typeOrAlias, const QString &suffix); + + // Return all known suffixes + QStringList suffixes() const; + QStringList filterStrings() const; + + void debug(QTextStream &str) const; + +private: + typedef QHash TypeMimeTypeMap; + typedef QHash AliasMap; + typedef QMultiHash ParentChildrenMap; + + bool addMimeTypes(QIODevice *device, const QString &fileName, QString *errorMessage); + inline const QString &resolveAlias(const QString &name) const; + MimeType findByFile(const QFileInfo &f, unsigned *priority) const; + void determineLevels(); + void raiseLevelRecursion(MimeMapEntry &e, int level); + + TypeMimeTypeMap m_typeMimeTypeMap; + AliasMap m_aliasMap; + ParentChildrenMap m_parentChildrenMap; + int m_maxLevel; +}; + +MimeDatabasePrivate::MimeDatabasePrivate() : + m_maxLevel(-1) +{ +} + +namespace Internal { + // Parser that builds MimeDB hierarchy by adding to MimeDatabasePrivate + class MimeTypeParser : public BaseMimeTypeParser { + public: + explicit MimeTypeParser(MimeDatabasePrivate &db) : m_db(db) {} + private: + virtual bool process(const MimeType &t, QString *) { m_db.addMimeType(t); return true; } + + MimeDatabasePrivate &m_db; + }; +} // namespace Internal + +bool MimeDatabasePrivate::addMimeTypes(QIODevice *device, const QString &fileName, QString *errorMessage) +{ + Internal::MimeTypeParser parser(*this); + return parser.parse(device, fileName, errorMessage); +} + +bool MimeDatabasePrivate::addMimeTypes(const QString &fileName, QString *errorMessage) +{ + QFile file(fileName); + if (!file.open(QIODevice::ReadOnly|QIODevice::Text)) { + *errorMessage = QString::fromLatin1("Cannot open %1: %2").arg(fileName, file.errorString()); + return false; + } + return addMimeTypes(&file, fileName, errorMessage); +} + +bool MimeDatabasePrivate::addMimeTypes(QIODevice *device, QString *errorMessage) +{ + return addMimeTypes(device, QLatin1String(""), errorMessage); +} + +bool MimeDatabasePrivate::addMimeType(MimeType mt) +{ + if (!mt) + return false; + + const QString type = mt.type(); + // Hack: Add a magic text matcher to "text/plain" and the fallback matcher to + // binary types "application/octet-stream" + if (type == QLatin1String(textTypeC)) { + mt.addMagicMatcher(QSharedPointer(new Internal::HeuristicTextMagicMatcher)); + } else { + if (type == QLatin1String(binaryTypeC)) + mt.addMagicMatcher(QSharedPointer(new Internal::BinaryMatcher)); + } + // insert the type. + m_typeMimeTypeMap.insert(type, MimeMapEntry(mt)); + // Register the children, resolved via alias map. Note that it is still + // possible that aliases end up in the map if the parent classes are not inserted + // at this point (thus their aliases not known). + const QStringList subClassesOf = mt.subClassesOf(); + if (!subClassesOf.empty()) { + const QStringList::const_iterator socend = subClassesOf.constEnd(); + for (QStringList::const_iterator soit = subClassesOf.constBegin(); soit != socend; ++soit) + m_parentChildrenMap.insert(resolveAlias(*soit), type); + } + // register aliasses + const QStringList aliases = mt.aliases(); + if (!aliases.empty()) { + const QStringList::const_iterator cend = aliases.constEnd(); + for (QStringList::const_iterator it = aliases.constBegin(); it != cend; ++it) + m_aliasMap.insert(*it, type); + } + m_maxLevel = -1; // Mark as dirty + return true; +} + +const QString &MimeDatabasePrivate::resolveAlias(const QString &name) const +{ + const AliasMap::const_iterator aliasIt = m_aliasMap.constFind(name); + return aliasIt == m_aliasMap.constEnd() ? name : aliasIt.value(); +} + +void MimeDatabasePrivate::raiseLevelRecursion(MimeMapEntry &e, int level) +{ + if (e.level == Dangling || e.level < level) + e.level = level; + if (m_maxLevel < level) + m_maxLevel = level; + // At all events recurse over children since nodes might have been + // added. + const QStringList childTypes = m_parentChildrenMap.values(e.type.type()); + if (childTypes.empty()) + return; + // look them up in the type->mime type map + const int nextLevel = level + 1; + const TypeMimeTypeMap::iterator tm_end = m_typeMimeTypeMap.end(); + const QStringList::const_iterator cend = childTypes.constEnd(); + for (QStringList::const_iterator it = childTypes.constBegin(); it != cend; ++it) { + const TypeMimeTypeMap::iterator tm_it = m_typeMimeTypeMap.find(resolveAlias(*it)); + if (tm_it == tm_end) { + qWarning("%s: Inconsistent mime hierarchy detected, child %s of %s cannot be found.", + Q_FUNC_INFO, it->toUtf8().constData(), e.type.type().toUtf8().constData()); + } else { + raiseLevelRecursion(*tm_it, nextLevel); + } + } +} + +void MimeDatabasePrivate::determineLevels() +{ + // Loop over toplevels and recurse down their hierarchies. + // Determine top levels by subtracting the children from the parent + // set. Note that a toplevel at this point might have 'subclassesOf' + // set to some class that is not in the DB, so, checking for an empty + // 'subclassesOf' set is not sufficient to find the toplevels. + // First, take the parent->child entries whose parent exists and build + // sets of parents/children + QSet parentSet, childrenSet; + const ParentChildrenMap::const_iterator pcend = m_parentChildrenMap.constEnd(); + for (ParentChildrenMap::const_iterator it = m_parentChildrenMap.constBegin(); it != pcend; ++it) + if (m_typeMimeTypeMap.contains(it.key())) { + parentSet.insert(it.key()); + childrenSet.insert(it.value()); + } + const QSet topLevels = parentSet.subtract(childrenSet); + if (debugMimeDB) + qDebug() << Q_FUNC_INFO << "top levels" << topLevels; + const TypeMimeTypeMap::iterator tm_end = m_typeMimeTypeMap.end(); + const QSet::const_iterator tl_cend = topLevels.constEnd(); + for (QSet::const_iterator tl_it = topLevels.constBegin(); tl_it != tl_cend; ++tl_it) { + const TypeMimeTypeMap::iterator tm_it = m_typeMimeTypeMap.find(resolveAlias(*tl_it)); + if (tm_it == tm_end) { + qWarning("%s: Inconsistent mime hierarchy detected, top level element %s cannot be found.", + Q_FUNC_INFO, tl_it->toUtf8().constData()); + } else { + raiseLevelRecursion(tm_it.value(), 0); + } + } +} + +bool MimeDatabasePrivate::setPreferredSuffix(const QString &typeOrAlias, const QString &suffix) +{ + TypeMimeTypeMap::iterator tit = m_typeMimeTypeMap.find(resolveAlias(typeOrAlias)); + if (tit != m_typeMimeTypeMap.end()) + return tit.value().type.setPreferredSuffix(suffix); + return false; +} + +// Returns a mime type or Null one if none found +MimeType MimeDatabasePrivate::findByType(const QString &typeOrAlias) const +{ + const TypeMimeTypeMap::const_iterator tit = m_typeMimeTypeMap.constFind(resolveAlias(typeOrAlias)); + if (tit != m_typeMimeTypeMap.constEnd()) + return tit.value().type; + return MimeType(); +} + +// Debugging wrapper around findByFile() +MimeType MimeDatabasePrivate::findByFile(const QFileInfo &f) const +{ + unsigned priority = 0; + if (debugMimeDB) + qDebug() << '>' << Q_FUNC_INFO << f.fileName(); + const MimeType rc = findByFile(f, &priority); + if (debugMimeDB) { + if (rc) { + qDebug() << " MimeMapEntryList; + + // Is the hierarchy set up in case we find several matches? + if (m_maxLevel < 0) { + MimeDatabasePrivate *db = const_cast(this); + db->determineLevels(); + } + // Starting from max level (most specific): Try to find a match of + // best (max) priority. Return if a glob match triggers. + *priorityPtr = 0; + unsigned maxPriority = 0; + MimeType rc; + Internal::FileMatchContext context(f); + const TypeMimeTypeMap::const_iterator cend = m_typeMimeTypeMap.constEnd(); + for (int level = m_maxLevel; level >= 0; level--) + for (TypeMimeTypeMap::const_iterator it = m_typeMimeTypeMap.constBegin(); it != cend; ++it) + if (it.value().level == level) { + const unsigned priority = it.value().type.matchesFile(context); + if (debugMimeDB > 1) + qDebug() << "pass" << level << it.value().type.type() << " matches " << priority; + if (priority) + if (priority > maxPriority) { + rc = it.value().type; + maxPriority = priority; + // Glob (exact) match?! We are done + if (maxPriority == MimeType::GlobMatchPriority) { + *priorityPtr = priority; + return rc; + } + } + } + return rc; +} + +// Return all known suffixes +QStringList MimeDatabasePrivate::suffixes() const +{ + QStringList rc; + const TypeMimeTypeMap::const_iterator cend = m_typeMimeTypeMap.constEnd(); + for (TypeMimeTypeMap::const_iterator it = m_typeMimeTypeMap.constBegin(); it != cend; ++it) + rc += it.value().type.suffixes(); + return rc; +} + +QStringList MimeDatabasePrivate::filterStrings() const +{ + QStringList rc; + const TypeMimeTypeMap::const_iterator cend = m_typeMimeTypeMap.constEnd(); + for (TypeMimeTypeMap::const_iterator it = m_typeMimeTypeMap.constBegin(); it != cend; ++it) + rc += it.value().type.filterString(); + return rc; +} + +void MimeDatabasePrivate::debug(QTextStream &str) const +{ + str << ">MimeDatabase\n"; + const TypeMimeTypeMap::const_iterator cend = m_typeMimeTypeMap.constEnd(); + for (TypeMimeTypeMap::const_iterator it = m_typeMimeTypeMap.constBegin(); it != cend; ++it) { + str << "Entry level " << it.value().level << '\n'; + it.value().type.m_d->debug(str); + } + str << "findByType(typeOrAlias); +} + +MimeType MimeDatabase::findByFile(const QFileInfo &f) const +{ + return m_d->findByFile(f); +} + +bool MimeDatabase::addMimeType(const MimeType &mt) +{ + return m_d->addMimeType(mt); +} + +bool MimeDatabase::addMimeTypes(const QString &fileName, QString *errorMessage) +{ + return m_d->addMimeTypes(fileName, errorMessage); +} + +bool MimeDatabase::addMimeTypes(QIODevice *device, QString *errorMessage) +{ + return m_d->addMimeTypes(device, errorMessage); +} + +QStringList MimeDatabase::suffixes() const +{ + return m_d->suffixes(); +} + +QStringList MimeDatabase::filterStrings() const +{ + return m_d->filterStrings(); +} + +QString MimeDatabase::preferredSuffixByType(const QString &type) const +{ + if (const MimeType mt = findByType(type)) + return mt.preferredSuffix(); + return QString(); +} + +QString MimeDatabase::preferredSuffixByFile(const QFileInfo &f) const +{ + if (const MimeType mt = findByFile(f)) + return mt.preferredSuffix(); + return QString(); +} + +bool MimeDatabase::setPreferredSuffix(const QString &typeOrAlias, const QString &suffix) +{ + return m_d->setPreferredSuffix(typeOrAlias, suffix); +} + +QDebug operator<<(QDebug d, const MimeDatabase &mt) +{ + QString s; + { + QTextStream str(&s); + mt.m_d->debug(str); + } + d << s; + return d; +} + +} // namespace Core diff --git a/ground/src/plugins/coreplugin/mimedatabase.h b/ground/src/plugins/coreplugin/mimedatabase.h index 57507840d..39095256f 100644 --- a/ground/src/plugins/coreplugin/mimedatabase.h +++ b/ground/src/plugins/coreplugin/mimedatabase.h @@ -1,226 +1,226 @@ -/** - ****************************************************************************** - * - * @file mimedatabase.h - * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. - * Parts by Nokia Corporation (qt-info@nokia.com) Copyright (C) 2009. - * @brief - * @see The GNU Public License (GPL) Version 3 - * @defgroup coreplugin - * @{ - * - *****************************************************************************/ -/* - * 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; either version 3 of the License, or - * (at your option) any later version. - * - * 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 - */ - -#ifndef MIMEDATABASE_H -#define MIMEDATABASE_H - -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE -class QIODevice; -class QRegExp; -class QDebug; -class QFileInfo; -QT_END_NAMESPACE - -namespace Core { - -class MimeTypeData; -class MimeDatabasePrivate; - -namespace Internal { - class BaseMimeTypeParser; - class FileMatchContext; -} - -/* Magic (file contents) matcher interface. */ -class CORE_EXPORT IMagicMatcher -{ - Q_DISABLE_COPY(IMagicMatcher) -protected: - IMagicMatcher() {} -public: - // Check for a match on contents of a file - virtual bool matches(const QByteArray &data) const = 0; - // Return a priority value from 1..100 - virtual int priority() const = 0; - virtual ~IMagicMatcher() {} -}; - -/* Utility class: A standard Magic match rule based on contents. Provides - * static factory methods for creation (currently only for "string". This can - * be extended to handle "little16"/"big16", etc.). */ -class CORE_EXPORT MagicRule -{ - Q_DISABLE_COPY(MagicRule) -public: - explicit MagicRule(const QByteArray &pattern, int startPos, int endPos); - bool matches(const QByteArray &data) const; - - // Convenience factory methods - static MagicRule *createStringRule(const QString &c, int startPos, int endPos); - -private: - const QByteArray m_pattern; - const int m_startPos; - const int m_endPos; -}; - -/* Utility class: A Magic matcher that checks a number of rules based on - * operator "or". It is used for rules parsed from XML files. */ -class CORE_EXPORT MagicRuleMatcher : public IMagicMatcher -{ - Q_DISABLE_COPY(MagicRuleMatcher) -public: - typedef QSharedPointer MagicRuleSharedPointer; - - MagicRuleMatcher(); - void add(const MagicRuleSharedPointer &rule); - virtual bool matches(const QByteArray &data) const; - - virtual int priority() const; - void setPriority(int p); - -private: - typedef QList MagicRuleList; - MagicRuleList m_list; - int m_priority; -}; - -/* Mime type data used in Qt Creator. Contains most information from - * standard mime type XML database files. - * Omissions: - * - Only magic of type "string" is supported. In addition, C++ classes - * derived from IMagicMatcher can be added to check on contents - * - acronyms, language-specific comments - * Extensions: - * - List of suffixes and preferred suffix (derived from glob patterns). - */ -class CORE_EXPORT MimeType -{ -public: - /* Return value of a glob match, which is higher than magic */ - enum { GlobMatchPriority = 101 }; - - MimeType(); - MimeType(const MimeType&); - MimeType &operator=(const MimeType&); - ~MimeType(); - - void clear(); - bool isNull() const; - operator bool() const; - - bool isTopLevel() const; - - QString type() const; - void setType(const QString &type); - - QStringList aliases() const; - void setAliases(const QStringList &); - - QString comment() const; - void setComment(const QString &comment); - - QString localeComment(const QString &locale = QString() /* en, de...*/) const; - void setLocaleComment(const QString &locale, const QString &comment); - - QList globPatterns() const; - void setGlobPatterns(const QList &); - - QStringList subClassesOf() const; - void setSubClassesOf(const QStringList &); - - // Extension over standard mime data - QStringList suffixes() const; - void setSuffixes(const QStringList &); - - // Extension over standard mime data - QString preferredSuffix() const; - bool setPreferredSuffix(const QString&); - - // Check for type or one of the aliases - bool matchesType(const QString &type) const; - // Check glob patterns and magic. Returns the match priority (0 no match, - // 1..100 indicating a magic match or GlobMatchPriority indicating an - // exact glob match). - unsigned matchesFile(const QFileInfo &file) const; - - // Return a filter string usable for a file dialog - QString filterString() const; - - // Add magic matcher - void addMagicMatcher(const QSharedPointer &matcher); - - friend QDebug operator<<(QDebug d, const MimeType &mt); - -private: - explicit MimeType(const MimeTypeData &d); - unsigned matchesFile(Internal::FileMatchContext &c) const; - - friend class Internal::BaseMimeTypeParser; - friend class MimeDatabasePrivate; - QSharedDataPointer m_d; -}; - -/* A Mime data base to which the plugins can add the mime types they handle. - * When adding a "text/plain" to it, the mimetype will receive a magic matcher - * that checks for text files that do not match the globs by heuristics. - * - * A good testcase is to run it over '/usr/share/mime/<*>/<*>.xml' on Linux. */ - -class CORE_EXPORT MimeDatabase -{ - Q_DISABLE_COPY(MimeDatabase) -public: - MimeDatabase(); - - ~MimeDatabase(); - - bool addMimeTypes(const QString &fileName, QString *errorMessage); - bool addMimeTypes(QIODevice *device, QString *errorMessage); - bool addMimeType(const MimeType &mt); - - // Returns a mime type or Null one if none found - MimeType findByType(const QString &type) const; - // Returns a mime type or Null one if none found - MimeType findByFile(const QFileInfo &f) const; - - // Convenience - QString preferredSuffixByType(const QString &type) const; - QString preferredSuffixByFile(const QFileInfo &f) const; - - // Return all known suffixes - QStringList suffixes() const; - bool setPreferredSuffix(const QString &typeOrAlias, const QString &suffix); - - QStringList filterStrings() const; - - friend QDebug operator<<(QDebug d, const MimeDatabase &mt); - -private: - MimeDatabasePrivate *m_d; -}; - -} // namespace Core - -#endif // MIMEDATABASE_H +/** + ****************************************************************************** + * + * @file mimedatabase.h + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. + * Parts by Nokia Corporation (qt-info@nokia.com) Copyright (C) 2009. + * @brief + * @see The GNU Public License (GPL) Version 3 + * @defgroup coreplugin + * @{ + * + *****************************************************************************/ +/* + * 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; either version 3 of the License, or + * (at your option) any later version. + * + * 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 + */ + +#ifndef MIMEDATABASE_H +#define MIMEDATABASE_H + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE +class QIODevice; +class QRegExp; +class QDebug; +class QFileInfo; +QT_END_NAMESPACE + +namespace Core { + +class MimeTypeData; +class MimeDatabasePrivate; + +namespace Internal { + class BaseMimeTypeParser; + class FileMatchContext; +} + +/* Magic (file contents) matcher interface. */ +class CORE_EXPORT IMagicMatcher +{ + Q_DISABLE_COPY(IMagicMatcher) +protected: + IMagicMatcher() {} +public: + // Check for a match on contents of a file + virtual bool matches(const QByteArray &data) const = 0; + // Return a priority value from 1..100 + virtual int priority() const = 0; + virtual ~IMagicMatcher() {} +}; + +/* Utility class: A standard Magic match rule based on contents. Provides + * static factory methods for creation (currently only for "string". This can + * be extended to handle "little16"/"big16", etc.). */ +class CORE_EXPORT MagicRule +{ + Q_DISABLE_COPY(MagicRule) +public: + explicit MagicRule(const QByteArray &pattern, int startPos, int endPos); + bool matches(const QByteArray &data) const; + + // Convenience factory methods + static MagicRule *createStringRule(const QString &c, int startPos, int endPos); + +private: + const QByteArray m_pattern; + const int m_startPos; + const int m_endPos; +}; + +/* Utility class: A Magic matcher that checks a number of rules based on + * operator "or". It is used for rules parsed from XML files. */ +class CORE_EXPORT MagicRuleMatcher : public IMagicMatcher +{ + Q_DISABLE_COPY(MagicRuleMatcher) +public: + typedef QSharedPointer MagicRuleSharedPointer; + + MagicRuleMatcher(); + void add(const MagicRuleSharedPointer &rule); + virtual bool matches(const QByteArray &data) const; + + virtual int priority() const; + void setPriority(int p); + +private: + typedef QList MagicRuleList; + MagicRuleList m_list; + int m_priority; +}; + +/* Mime type data used in the OpenPilot GCS. Contains most information from + * standard mime type XML database files. + * Omissions: + * - Only magic of type "string" is supported. In addition, C++ classes + * derived from IMagicMatcher can be added to check on contents + * - acronyms, language-specific comments + * Extensions: + * - List of suffixes and preferred suffix (derived from glob patterns). + */ +class CORE_EXPORT MimeType +{ +public: + /* Return value of a glob match, which is higher than magic */ + enum { GlobMatchPriority = 101 }; + + MimeType(); + MimeType(const MimeType&); + MimeType &operator=(const MimeType&); + ~MimeType(); + + void clear(); + bool isNull() const; + operator bool() const; + + bool isTopLevel() const; + + QString type() const; + void setType(const QString &type); + + QStringList aliases() const; + void setAliases(const QStringList &); + + QString comment() const; + void setComment(const QString &comment); + + QString localeComment(const QString &locale = QString() /* en, de...*/) const; + void setLocaleComment(const QString &locale, const QString &comment); + + QList globPatterns() const; + void setGlobPatterns(const QList &); + + QStringList subClassesOf() const; + void setSubClassesOf(const QStringList &); + + // Extension over standard mime data + QStringList suffixes() const; + void setSuffixes(const QStringList &); + + // Extension over standard mime data + QString preferredSuffix() const; + bool setPreferredSuffix(const QString&); + + // Check for type or one of the aliases + bool matchesType(const QString &type) const; + // Check glob patterns and magic. Returns the match priority (0 no match, + // 1..100 indicating a magic match or GlobMatchPriority indicating an + // exact glob match). + unsigned matchesFile(const QFileInfo &file) const; + + // Return a filter string usable for a file dialog + QString filterString() const; + + // Add magic matcher + void addMagicMatcher(const QSharedPointer &matcher); + + friend QDebug operator<<(QDebug d, const MimeType &mt); + +private: + explicit MimeType(const MimeTypeData &d); + unsigned matchesFile(Internal::FileMatchContext &c) const; + + friend class Internal::BaseMimeTypeParser; + friend class MimeDatabasePrivate; + QSharedDataPointer m_d; +}; + +/* A Mime data base to which the plugins can add the mime types they handle. + * When adding a "text/plain" to it, the mimetype will receive a magic matcher + * that checks for text files that do not match the globs by heuristics. + * + * A good testcase is to run it over '/usr/share/mime/<*>/<*>.xml' on Linux. */ + +class CORE_EXPORT MimeDatabase +{ + Q_DISABLE_COPY(MimeDatabase) +public: + MimeDatabase(); + + ~MimeDatabase(); + + bool addMimeTypes(const QString &fileName, QString *errorMessage); + bool addMimeTypes(QIODevice *device, QString *errorMessage); + bool addMimeType(const MimeType &mt); + + // Returns a mime type or Null one if none found + MimeType findByType(const QString &type) const; + // Returns a mime type or Null one if none found + MimeType findByFile(const QFileInfo &f) const; + + // Convenience + QString preferredSuffixByType(const QString &type) const; + QString preferredSuffixByFile(const QFileInfo &f) const; + + // Return all known suffixes + QStringList suffixes() const; + bool setPreferredSuffix(const QString &typeOrAlias, const QString &suffix); + + QStringList filterStrings() const; + + friend QDebug operator<<(QDebug d, const MimeDatabase &mt); + +private: + MimeDatabasePrivate *m_d; +}; + +} // namespace Core + +#endif // MIMEDATABASE_H diff --git a/ground/src/plugins/modelview/modelviewgadgetconfiguration.cpp b/ground/src/plugins/modelview/modelviewgadgetconfiguration.cpp index a53b89592..d26956220 100644 --- a/ground/src/plugins/modelview/modelviewgadgetconfiguration.cpp +++ b/ground/src/plugins/modelview/modelviewgadgetconfiguration.cpp @@ -30,8 +30,8 @@ ModelViewGadgetConfiguration::ModelViewGadgetConfiguration(QString classId, const QByteArray &state, QObject *parent) : IUAVGadgetConfiguration(classId, parent), - m_acFilename("../artwork/3D Model/quad.dae"), - m_bgFilename("../artwork/3D Model/default_background.png"), + m_acFilename("../share/openpilotgcs/models/Easystar/EasyStar.3ds"), + m_bgFilename(""), m_enableVbo(false) { if (state.count() > 0) { diff --git a/ground/src/plugins/welcome/rssfetcher.cpp b/ground/src/plugins/welcome/rssfetcher.cpp index df26e6fdb..fad426964 100644 --- a/ground/src/plugins/welcome/rssfetcher.cpp +++ b/ground/src/plugins/welcome/rssfetcher.cpp @@ -1,186 +1,186 @@ -/** - ****************************************************************************** - * - * @file rssfetcher.cpp - * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. - * Parts by Nokia Corporation (qt-info@nokia.com) Copyright (C) 2009. - * @brief - * @see The GNU Public License (GPL) Version 3 - * @defgroup welcomeplugin - * @{ - * - *****************************************************************************/ -/* - * 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; either version 3 of the License, or - * (at your option) any later version. - * - * 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 -#include -#include -#include -#include -#include -#include - -#include - -#include "rssfetcher.h" - -#ifdef Q_OS_UNIX -#include -#endif - -using namespace Welcome::Internal; - -static const QString getOsString() -{ - QString osString; -#if defined(Q_OS_WIN) - switch (QSysInfo::WindowsVersion) { - case (QSysInfo::WV_4_0): - osString += QLatin1String("WinNT4.0"); - break; - case (QSysInfo::WV_5_0): - osString += QLatin1String("Windows NT 5.0"); - break; - case (QSysInfo::WV_5_1): - osString += QLatin1String("Windows NT 5.1"); - break; - case (QSysInfo::WV_5_2): - osString += QLatin1String("Windows NT 5.2"); - break; - case (QSysInfo::WV_6_0): - osString += QLatin1String("Windows NT 6.0"); - break; - case (QSysInfo::WV_6_1): - osString += QLatin1String("Windows NT 6.1"); - break; - default: - osString += QLatin1String("Windows NT (Unknown)"); - break; - } -#elif defined (Q_OS_MAC) - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) - osString += QLatin1String("PPC "); - else - osString += QLatin1String("Intel "); - osString += QLatin1String("Mac OS X "); - switch (QSysInfo::MacintoshVersion) { - case (QSysInfo::MV_10_3): - osString += QLatin1String("10_3"); - break; - case (QSysInfo::MV_10_4): - osString += QLatin1String("10_4"); - break; - case (QSysInfo::MV_10_5): - osString += QLatin1String("10_5"); - break; - case (QSysInfo::MV_10_6): - osString += QLatin1String("10_6"); - break; - default: - osString += QLatin1String("(Unknown)"); - break; - } -#elif defined (Q_OS_UNIX) - struct utsname uts; - if (uname(&uts) == 0) - osString += QString("%1 %2").arg(QLatin1String(uts.sysname)) - .arg(QLatin1String(uts.release)); - else - osString += QLatin1String("Unix (Unknown)"); -#else - ossttring = QLatin1String("Unknown OS"); -#endif - return osString; -} - -RSSFetcher::RSSFetcher(int maxItems, QObject *parent) - : QObject(parent), m_items(0), m_maxItems(maxItems) -{ - connect(&m_http, SIGNAL(readyRead(const QHttpResponseHeader &)), - this, SLOT(readData(const QHttpResponseHeader &))); - - connect(&m_http, SIGNAL(requestFinished(int, bool)), - this, SLOT(finished(int, bool))); -} - -void RSSFetcher::fetch(const QUrl &url) -{ - QList proxies = QNetworkProxyFactory::systemProxyForQuery(QNetworkProxyQuery(url)); - if (proxies.count() > 0) - m_http.setProxy(proxies.first()); - m_http.setHost(url.host()); - QString agentStr = QString("Qt-Creator/%1 (QHttp %2; %3; %4; %5 bit)") - .arg(Core::Constants::GCS_VERSION_LONG).arg(qVersion()) - .arg(getOsString()).arg(QLocale::system().name()) - .arg(QSysInfo::WordSize); - QHttpRequestHeader header("GET", url.path()); - //qDebug() << agentStr; - header.setValue("User-Agent", agentStr); - header.setValue("Host", url.host()); - m_connectionId = m_http.request(header); -} - -void RSSFetcher::readData(const QHttpResponseHeader &resp) -{ - if (resp.statusCode() != 200) - m_http.abort(); - else { - m_xml.addData(m_http.readAll()); - parseXml(); - } -} - -void RSSFetcher::finished(int id, bool error) -{ - Q_UNUSED(id) - m_items = 0; - emit finished(error); -} - -void RSSFetcher::parseXml() -{ - while (!m_xml.atEnd()) { - m_xml.readNext(); - if (m_xml.isStartElement()) { - if (m_xml.name() == "item") { - m_titleString.clear(); - m_descriptionString.clear(); - m_linkString.clear(); - } - m_currentTag = m_xml.name().toString(); - } else if (m_xml.isEndElement()) { - if (m_xml.name() == "item") { - m_items++; - if (m_items > m_maxItems) - return; - emit newsItemReady(m_titleString, m_descriptionString, m_linkString); - } - - } else if (m_xml.isCharacters() && !m_xml.isWhitespace()) { - if (m_currentTag == "title") - m_titleString += m_xml.text().toString(); - else if (m_currentTag == "description") - m_descriptionString += m_xml.text().toString(); - else if (m_currentTag == "link") - m_linkString += m_xml.text().toString(); - } - } - if (m_xml.error() && m_xml.error() != QXmlStreamReader::PrematureEndOfDocumentError) { - qWarning() << "XML ERROR:" << m_xml.lineNumber() << ": " << m_xml.errorString(); - m_http.abort(); - } -} +/** + ****************************************************************************** + * + * @file rssfetcher.cpp + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. + * Parts by Nokia Corporation (qt-info@nokia.com) Copyright (C) 2009. + * @brief + * @see The GNU Public License (GPL) Version 3 + * @defgroup welcomeplugin + * @{ + * + *****************************************************************************/ +/* + * 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; either version 3 of the License, or + * (at your option) any later version. + * + * 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 +#include +#include +#include +#include +#include +#include + +#include + +#include "rssfetcher.h" + +#ifdef Q_OS_UNIX +#include +#endif + +using namespace Welcome::Internal; + +static const QString getOsString() +{ + QString osString; +#if defined(Q_OS_WIN) + switch (QSysInfo::WindowsVersion) { + case (QSysInfo::WV_4_0): + osString += QLatin1String("WinNT4.0"); + break; + case (QSysInfo::WV_5_0): + osString += QLatin1String("Windows NT 5.0"); + break; + case (QSysInfo::WV_5_1): + osString += QLatin1String("Windows NT 5.1"); + break; + case (QSysInfo::WV_5_2): + osString += QLatin1String("Windows NT 5.2"); + break; + case (QSysInfo::WV_6_0): + osString += QLatin1String("Windows NT 6.0"); + break; + case (QSysInfo::WV_6_1): + osString += QLatin1String("Windows NT 6.1"); + break; + default: + osString += QLatin1String("Windows NT (Unknown)"); + break; + } +#elif defined (Q_OS_MAC) + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) + osString += QLatin1String("PPC "); + else + osString += QLatin1String("Intel "); + osString += QLatin1String("Mac OS X "); + switch (QSysInfo::MacintoshVersion) { + case (QSysInfo::MV_10_3): + osString += QLatin1String("10_3"); + break; + case (QSysInfo::MV_10_4): + osString += QLatin1String("10_4"); + break; + case (QSysInfo::MV_10_5): + osString += QLatin1String("10_5"); + break; + case (QSysInfo::MV_10_6): + osString += QLatin1String("10_6"); + break; + default: + osString += QLatin1String("(Unknown)"); + break; + } +#elif defined (Q_OS_UNIX) + struct utsname uts; + if (uname(&uts) == 0) + osString += QString("%1 %2").arg(QLatin1String(uts.sysname)) + .arg(QLatin1String(uts.release)); + else + osString += QLatin1String("Unix (Unknown)"); +#else + ossttring = QLatin1String("Unknown OS"); +#endif + return osString; +} + +RSSFetcher::RSSFetcher(int maxItems, QObject *parent) + : QObject(parent), m_items(0), m_maxItems(maxItems) +{ + connect(&m_http, SIGNAL(readyRead(const QHttpResponseHeader &)), + this, SLOT(readData(const QHttpResponseHeader &))); + + connect(&m_http, SIGNAL(requestFinished(int, bool)), + this, SLOT(finished(int, bool))); +} + +void RSSFetcher::fetch(const QUrl &url) +{ + QList proxies = QNetworkProxyFactory::systemProxyForQuery(QNetworkProxyQuery(url)); + if (proxies.count() > 0) + m_http.setProxy(proxies.first()); + m_http.setHost(url.host()); + QString agentStr = QString("OP-GCS/%1 (QHttp %2; %3; %4; %5 bit)") + .arg(Core::Constants::GCS_VERSION_LONG).arg(qVersion()) + .arg(getOsString()).arg(QLocale::system().name()) + .arg(QSysInfo::WordSize); + QHttpRequestHeader header("GET", url.path()); + //qDebug() << agentStr; + header.setValue("User-Agent", agentStr); + header.setValue("Host", url.host()); + m_connectionId = m_http.request(header); +} + +void RSSFetcher::readData(const QHttpResponseHeader &resp) +{ + if (resp.statusCode() != 200) + m_http.abort(); + else { + m_xml.addData(m_http.readAll()); + parseXml(); + } +} + +void RSSFetcher::finished(int id, bool error) +{ + Q_UNUSED(id) + m_items = 0; + emit finished(error); +} + +void RSSFetcher::parseXml() +{ + while (!m_xml.atEnd()) { + m_xml.readNext(); + if (m_xml.isStartElement()) { + if (m_xml.name() == "item") { + m_titleString.clear(); + m_descriptionString.clear(); + m_linkString.clear(); + } + m_currentTag = m_xml.name().toString(); + } else if (m_xml.isEndElement()) { + if (m_xml.name() == "item") { + m_items++; + if (m_items > m_maxItems) + return; + emit newsItemReady(m_titleString, m_descriptionString, m_linkString); + } + + } else if (m_xml.isCharacters() && !m_xml.isWhitespace()) { + if (m_currentTag == "title") + m_titleString += m_xml.text().toString(); + else if (m_currentTag == "description") + m_descriptionString += m_xml.text().toString(); + else if (m_currentTag == "link") + m_linkString += m_xml.text().toString(); + } + } + if (m_xml.error() && m_xml.error() != QXmlStreamReader::PrematureEndOfDocumentError) { + qWarning() << "XML ERROR:" << m_xml.lineNumber() << ": " << m_xml.errorString(); + m_http.abort(); + } +} diff --git a/ground/src/plugins/welcome/welcomemode.h b/ground/src/plugins/welcome/welcomemode.h index 8b70c1694..5a4d8d4f2 100644 --- a/ground/src/plugins/welcome/welcomemode.h +++ b/ground/src/plugins/welcome/welcomemode.h @@ -1,76 +1,76 @@ -/** - ****************************************************************************** - * - * @file welcomemode.h - * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. - * Parts by Nokia Corporation (qt-info@nokia.com) Copyright (C) 2009. - * @brief - * @see The GNU Public License (GPL) Version 3 - * @defgroup welcomeplugin - * @{ - * - *****************************************************************************/ -/* - * 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; either version 3 of the License, or - * (at your option) any later version. - * - * 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 - */ - -#ifndef WELCOMEMODE_H -#define WELCOMEMODE_H - -#include "welcome_global.h" - -#include - - -QT_BEGIN_NAMESPACE -class QWidget; -class QUrl; -QT_END_NAMESPACE - -namespace Welcome { - -struct WelcomeModePrivate; - -class WELCOME_EXPORT WelcomeMode : public Core::IMode -{ - Q_OBJECT - -public: - WelcomeMode(); - ~WelcomeMode(); - - // IMode - QString name() const; - QIcon icon() const; - int priority() const; - QWidget *widget(); - const char *uniqueModeName() const; - QList context() const; - void activated(); - QString contextHelpId() const { return QLatin1String("Qt Creator"); } - void initPlugins(); - -private slots: - void slotFeedback(); - void welcomePluginAdded(QObject*); - void showClickedPage(); - -private: - WelcomeModePrivate *m_d; -}; - -} // namespace Welcome - -#endif // WELCOMEMODE_H +/** + ****************************************************************************** + * + * @file welcomemode.h + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. + * Parts by Nokia Corporation (qt-info@nokia.com) Copyright (C) 2009. + * @brief + * @see The GNU Public License (GPL) Version 3 + * @defgroup welcomeplugin + * @{ + * + *****************************************************************************/ +/* + * 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; either version 3 of the License, or + * (at your option) any later version. + * + * 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 + */ + +#ifndef WELCOMEMODE_H +#define WELCOMEMODE_H + +#include "welcome_global.h" + +#include + + +QT_BEGIN_NAMESPACE +class QWidget; +class QUrl; +QT_END_NAMESPACE + +namespace Welcome { + +struct WelcomeModePrivate; + +class WELCOME_EXPORT WelcomeMode : public Core::IMode +{ + Q_OBJECT + +public: + WelcomeMode(); + ~WelcomeMode(); + + // IMode + QString name() const; + QIcon icon() const; + int priority() const; + QWidget *widget(); + const char *uniqueModeName() const; + QList context() const; + void activated(); + QString contextHelpId() const { return QLatin1String("OpenPilot GCS"); } + void initPlugins(); + +private slots: + void slotFeedback(); + void welcomePluginAdded(QObject*); + void showClickedPage(); + +private: + WelcomeModePrivate *m_d; +}; + +} // namespace Welcome + +#endif // WELCOMEMODE_H