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:
parent
277ad88f50
commit
9b422de663
@ -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("&"));
|
||||
t.replace(QLatin1Char('<'), QLatin1String("<"));
|
||||
t.replace(QLatin1Char('>'), QLatin1String(">"));
|
||||
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("&"));
|
||||
t.replace(QLatin1Char('<'), QLatin1String("<"));
|
||||
t.replace(QLatin1Char('>'), QLatin1String(">"));
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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).
|
||||
*/
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user