1
0
mirror of https://bitbucket.org/librepilot/librepilot.git synced 2025-01-18 03:52:11 +01:00

Doxygen updates, minor janitor work.

git-svn-id: svn://svn.openpilot.org/OpenPilot/trunk@872 ebee16cc-31ac-478f-84a7-5cbb03baadba
This commit is contained in:
dankers 2010-06-23 14:32:10 +00:00 committed by dankers
parent 277ad88f50
commit 9b422de663
9 changed files with 2817 additions and 2845 deletions

View File

@ -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 <extensionsystem/pluginmanager.h>
#include <extensionsystem/pluginspec.h>
#include <extensionsystem/iplugin.h>
#include <QtCore/QDir>
#include <QtCore/QTextStream>
#include <QtCore/QFileInfo>
#include <QtCore/QDebug>
#include <QtCore/QTimer>
#include <QtCore/QLibraryInfo>
#include <QtCore/QTranslator>
#include <QtCore/QVariant>
#include <QtGui/QMessageBox>
#include <QtGui/QApplication>
#include <QtGui/QMainWindow>
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<ExtensionSystem::PluginSpec *> PluginSpecSet;
// Helpers for displaying messages. Note that there is no console on Windows.
#ifdef Q_OS_WIN
// Format as <pre> HTML
static inline void toHtml(QString &t)
{
t.replace(QLatin1Char('&'), QLatin1String("&amp;"));
t.replace(QLatin1Char('<'), QLatin1String("&lt;"));
t.replace(QLatin1Char('>'), QLatin1String("&gt;"));
t.insert(0, QLatin1String("<html><pre>"));
t.append(QLatin1String("</pre></html>"));
}
static void displayHelpText(QString t) // No console on Windows.
{
toHtml(t);
QMessageBox::information(0, QLatin1String(appNameC), t);
}
static void displayError(const QString &t) // No console on Windows.
{
QMessageBox::critical(0, QLatin1String(appNameC), t);
}
#else
static void displayHelpText(const QString &t)
{
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<QString, QString> foundAppOptions;
if (arguments.size() > 1) {
QMap<QString, bool> 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 <extensionsystem/pluginmanager.h>
#include <extensionsystem/pluginspec.h>
#include <extensionsystem/iplugin.h>
#include <QtCore/QDir>
#include <QtCore/QTextStream>
#include <QtCore/QFileInfo>
#include <QtCore/QDebug>
#include <QtCore/QTimer>
#include <QtCore/QLibraryInfo>
#include <QtCore/QTranslator>
#include <QtCore/QVariant>
#include <QtGui/QMessageBox>
#include <QtGui/QApplication>
#include <QtGui/QMainWindow>
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<ExtensionSystem::PluginSpec *> PluginSpecSet;
// Helpers for displaying messages. Note that there is no console on Windows.
#ifdef Q_OS_WIN
// Format as <pre> HTML
static inline void toHtml(QString &t)
{
t.replace(QLatin1Char('&'), QLatin1String("&amp;"));
t.replace(QLatin1Char('<'), QLatin1String("&lt;"));
t.replace(QLatin1Char('>'), QLatin1String("&gt;"));
t.insert(0, QLatin1String("<html><pre>"));
t.append(QLatin1String("</pre></html>"));
}
static void displayHelpText(QString t) // No console on Windows.
{
toHtml(t);
QMessageBox::information(0, QLatin1String(appNameC), t);
}
static void displayError(const QString &t) // No console on Windows.
{
QMessageBox::critical(0, QLatin1String(appNameC), t);
}
#else
static void displayHelpText(const QString &t)
{
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<QString, QString> foundAppOptions;
if (arguments.size() > 1) {
QMap<QString, bool> 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();
}

View File

@ -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"

View File

@ -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 <QtCore/QDebug>
#include <QtGui/QAction>
#include <QtGui/QMenuBar>
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<ActionContainerPrivate *>(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 *>(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<CommandLocation> 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<MenuActionContainer *>(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<int, int>::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<MenuActionContainer*>(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<QAction *> actions = m_menuBar->actions();
for (int i=0; i<actions.size(); ++i) {
if (actions.at(i)->isVisible()) {
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 <QtCore/QDebug>
#include <QtGui/QAction>
#include <QtGui/QMenuBar>
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<ActionContainerPrivate *>(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 *>(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<CommandLocation> 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<MenuActionContainer *>(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<int, int>::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<MenuActionContainer*>(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<QAction *> actions = m_menuBar->actions();
for (int i=0; i<actions.size(); ++i) {
if (actions.at(i)->isVisible()) {
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;
}

View File

@ -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<IWizard *> &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<IWizard *> &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).
*/

View File

@ -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 <utils/qtcassert.h>
#include <QtCore/QByteArray>
#include <QtCore/QCoreApplication>
#include <QtCore/QDebug>
#include <QtCore/QFile>
#include <QtCore/QFileInfo>
#include <QtCore/QLocale>
#include <QtCore/QMap>
#include <QtCore/QMultiHash>
#include <QtCore/QRegExp>
#include <QtCore/QSharedData>
#include <QtCore/QSharedPointer>
#include <QtCore/QStringList>
#include <QtCore/QTextStream>
#include <QtXml/QXmlStreamReader>
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 (<mime-info> being optional):
*\code
?xml version="1.0" encoding="UTF-8"?>
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
<!-- Mime types must match the desktop file associations -->
<mime-type type="application/vnd.nokia.qt.qmakeprofile">
<comment xml:lang="en">Qt QMake Profile</comment>
<glob pattern="*.pro"/>
</mime-type>
</mime-info>
*\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<QString,QString> LocaleHash;
void clear();
void debug(QTextStream &str, int indent = 0) const;
QString type;
QString comment;
LocaleHash localeComments;
QStringList aliases;
QList<QRegExp> globPatterns;
QStringList subClassesOf;
QString preferredSuffix;
QStringList suffixes;
typedef QSharedPointer<IMagicMatcher> IMagicMatcherSharedPointer;
typedef QList<IMagicMatcherSharedPointer> 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<QRegExp> MimeType::globPatterns() const
{
return m_d->globPatterns;
}
void MimeType::setGlobPatterns(const QList<QRegExp> &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<QRegExp> &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<IMagicMatcher> &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 <mime-type>.
// 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 <mime-type>
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
// <match value="must be converted with BinHex" type="string" offset="11"/>
// <match value="0x9501" type="big16" offset="0:64"/>
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>(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<IMagicMatcher>(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->QString> for mapping aliases to types
* - A Map <QString-MimeMapEntry> for the types (MimeMapEntry being a pair of
* MimeType and (hierarchy) level.
* - A map <QString->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<QString, MimeMapEntry> TypeMimeTypeMap;
typedef QHash<QString, QString> AliasMap;
typedef QMultiHash<QString, QString> 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("<stream>"), 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<IMagicMatcher>(new Internal::HeuristicTextMagicMatcher));
} else {
if (type == QLatin1String(binaryTypeC))
mt.addMagicMatcher(QSharedPointer<IMagicMatcher>(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<QString> 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<QString> topLevels = parentSet.subtract(childrenSet);
if (debugMimeDB)
qDebug() << Q_FUNC_INFO << "top levels" << topLevels;
const TypeMimeTypeMap::iterator tm_end = m_typeMimeTypeMap.end();
const QSet<QString>::const_iterator tl_cend = topLevels.constEnd();
for (QSet<QString>::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() << "<MimeDatabase::findByFile: match prio=" << priority << rc.type();
} else {
qDebug() << "<MimeDatabase::findByFile: no match";
}
}
return rc;
}
// Returns a mime type or Null one if none found
MimeType MimeDatabasePrivate::findByFile(const QFileInfo &f, unsigned *priorityPtr) const
{
typedef QList<MimeMapEntry> MimeMapEntryList;
// Is the hierarchy set up in case we find several matches?
if (m_maxLevel < 0) {
MimeDatabasePrivate *db = const_cast<MimeDatabasePrivate *>(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 << "<MimeDatabase\n";
}
// --------------- MimeDatabase
MimeDatabase::MimeDatabase() :
m_d(new MimeDatabasePrivate)
{
}
MimeDatabase::~MimeDatabase()
{
delete m_d;
}
MimeType MimeDatabase::findByType(const QString &typeOrAlias) const
{
return m_d->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 <utils/qtcassert.h>
#include <QtCore/QByteArray>
#include <QtCore/QCoreApplication>
#include <QtCore/QDebug>
#include <QtCore/QFile>
#include <QtCore/QFileInfo>
#include <QtCore/QLocale>
#include <QtCore/QMap>
#include <QtCore/QMultiHash>
#include <QtCore/QRegExp>
#include <QtCore/QSharedData>
#include <QtCore/QSharedPointer>
#include <QtCore/QStringList>
#include <QtCore/QTextStream>
#include <QtXml/QXmlStreamReader>
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 (<mime-info> being optional):
*\code
?xml version="1.0" encoding="UTF-8"?>
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
<!-- Mime types must match the desktop file associations -->
<mime-type type="application/vnd.nokia.qt.qmakeprofile">
<comment xml:lang="en">Qt QMake Profile</comment>
<glob pattern="*.pro"/>
</mime-type>
</mime-info>
*\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<QString,QString> LocaleHash;
void clear();
void debug(QTextStream &str, int indent = 0) const;
QString type;
QString comment;
LocaleHash localeComments;
QStringList aliases;
QList<QRegExp> globPatterns;
QStringList subClassesOf;
QString preferredSuffix;
QStringList suffixes;
typedef QSharedPointer<IMagicMatcher> IMagicMatcherSharedPointer;
typedef QList<IMagicMatcherSharedPointer> 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<QRegExp> MimeType::globPatterns() const
{
return m_d->globPatterns;
}
void MimeType::setGlobPatterns(const QList<QRegExp> &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<QRegExp> &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<IMagicMatcher> &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 <mime-type>.
// 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 <mime-type>
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
// <match value="must be converted with BinHex" type="string" offset="11"/>
// <match value="0x9501" type="big16" offset="0:64"/>
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>(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<IMagicMatcher>(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->QString> for mapping aliases to types
* - A Map <QString-MimeMapEntry> for the types (MimeMapEntry being a pair of
* MimeType and (hierarchy) level.
* - A map <QString->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<QString, MimeMapEntry> TypeMimeTypeMap;
typedef QHash<QString, QString> AliasMap;
typedef QMultiHash<QString, QString> 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("<stream>"), 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<IMagicMatcher>(new Internal::HeuristicTextMagicMatcher));
} else {
if (type == QLatin1String(binaryTypeC))
mt.addMagicMatcher(QSharedPointer<IMagicMatcher>(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<QString> 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<QString> topLevels = parentSet.subtract(childrenSet);
if (debugMimeDB)
qDebug() << Q_FUNC_INFO << "top levels" << topLevels;
const TypeMimeTypeMap::iterator tm_end = m_typeMimeTypeMap.end();
const QSet<QString>::const_iterator tl_cend = topLevels.constEnd();
for (QSet<QString>::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() << "<MimeDatabase::findByFile: match prio=" << priority << rc.type();
} else {
qDebug() << "<MimeDatabase::findByFile: no match";
}
}
return rc;
}
// Returns a mime type or Null one if none found
MimeType MimeDatabasePrivate::findByFile(const QFileInfo &f, unsigned *priorityPtr) const
{
typedef QList<MimeMapEntry> MimeMapEntryList;
// Is the hierarchy set up in case we find several matches?
if (m_maxLevel < 0) {
MimeDatabasePrivate *db = const_cast<MimeDatabasePrivate *>(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 << "<MimeDatabase\n";
}
// --------------- MimeDatabase
MimeDatabase::MimeDatabase() :
m_d(new MimeDatabasePrivate)
{
}
MimeDatabase::~MimeDatabase()
{
delete m_d;
}
MimeType MimeDatabase::findByType(const QString &typeOrAlias) const
{
return m_d->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

View File

@ -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 <coreplugin/core_global.h>
#include <QtCore/QStringList>
#include <QtCore/QSharedDataPointer>
#include <QtCore/QSharedPointer>
#include <QtCore/QByteArray>
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<MagicRule> 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<MagicRuleSharedPointer> 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<QRegExp> globPatterns() const;
void setGlobPatterns(const QList<QRegExp> &);
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<IMagicMatcher> &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<MimeTypeData> 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 <coreplugin/core_global.h>
#include <QtCore/QStringList>
#include <QtCore/QSharedDataPointer>
#include <QtCore/QSharedPointer>
#include <QtCore/QByteArray>
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<MagicRule> 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<MagicRuleSharedPointer> 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<QRegExp> globPatterns() const;
void setGlobPatterns(const QList<QRegExp> &);
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<IMagicMatcher> &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<MimeTypeData> 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

View File

@ -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) {

View File

@ -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 <QtCore/QDebug>
#include <QtCore/QSysInfo>
#include <QtCore/QLocale>
#include <QtGui/QDesktopServices>
#include <QtGui/QLineEdit>
#include <QtNetwork/QHttp>
#include <QtNetwork/QNetworkProxyFactory>
#include <coreplugin/coreconstants.h>
#include "rssfetcher.h"
#ifdef Q_OS_UNIX
#include <sys/utsname.h>
#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<QNetworkProxy> 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 <QtCore/QDebug>
#include <QtCore/QSysInfo>
#include <QtCore/QLocale>
#include <QtGui/QDesktopServices>
#include <QtGui/QLineEdit>
#include <QtNetwork/QHttp>
#include <QtNetwork/QNetworkProxyFactory>
#include <coreplugin/coreconstants.h>
#include "rssfetcher.h"
#ifdef Q_OS_UNIX
#include <sys/utsname.h>
#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<QNetworkProxy> 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();
}
}

View File

@ -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 <coreplugin/imode.h>
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<int> 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 <coreplugin/imode.h>
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<int> 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