summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeandro Melo <leandro.melo@nokia.com>2011-02-22 15:11:50 +0100
committerLeandro Melo <leandro.melo@nokia.com>2011-03-02 10:13:40 +0100
commit3db53206c2e4908cfc86b65918cad2127f2174cd (patch)
treee179c08a32375cfb6e91e99fece1d5babe068389
parentc8f19854f9dae2148ba1a20c93ec5233f1f88aea (diff)
downloadqt-creator-3db53206c2e4908cfc86b65918cad2127f2174cd.tar.gz
MIME types: Introduce user extensions feature
Creates a new page in the options dialog which allows the user to modify MIME type's globl patterns and rule-based magic matchers. As a side-effect of this feature our MIME database (and related components) got some improvements. Reviewed-by: Friedemann Kleint
-rw-r--r--src/plugins/coreplugin/coreconstants.h4
-rw-r--r--src/plugins/coreplugin/coreplugin.cpp2
-rw-r--r--src/plugins/coreplugin/coreplugin.pro13
-rw-r--r--src/plugins/coreplugin/dialogs/shortcutsettings.cpp2
-rw-r--r--src/plugins/coreplugin/mainwindow.cpp21
-rw-r--r--src/plugins/coreplugin/mainwindow.h4
-rw-r--r--src/plugins/coreplugin/mimedatabase.cpp516
-rw-r--r--src/plugins/coreplugin/mimedatabase.h90
-rw-r--r--src/plugins/coreplugin/mimetypemagicdialog.cpp109
-rw-r--r--src/plugins/coreplugin/mimetypemagicdialog.h83
-rw-r--r--src/plugins/coreplugin/mimetypemagicdialog.ui318
-rw-r--r--src/plugins/coreplugin/mimetypesettings.cpp605
-rw-r--r--src/plugins/coreplugin/mimetypesettings.h72
-rw-r--r--src/plugins/coreplugin/mimetypesettingspage.ui169
-rw-r--r--src/plugins/texteditor/generichighlighter/manager.cpp51
15 files changed, 1956 insertions, 103 deletions
diff --git a/src/plugins/coreplugin/coreconstants.h b/src/plugins/coreplugin/coreconstants.h
index cdfc0c06b0..7cf5e13847 100644
--- a/src/plugins/coreplugin/coreconstants.h
+++ b/src/plugins/coreplugin/coreconstants.h
@@ -253,7 +253,9 @@ const char * const SETTINGS_CATEGORY_CORE = "A.Core";
const char * const SETTINGS_CATEGORY_CORE_ICON = ":/core/images/category_core.png";
const char * const SETTINGS_TR_CATEGORY_CORE = QT_TRANSLATE_NOOP("Core", "Environment");
const char * const SETTINGS_ID_ENVIRONMENT = "A.General";
-const char * const SETTINGS_ID_TOOLS = "G.ExternalTools";
+const char * const SETTINGS_ID_SHORTCUTS = "B.Keyboard";
+const char * const SETTINGS_ID_TOOLS = "C.ExternalTools";
+const char * const SETTINGS_ID_MIMETYPES = "D.MimeTypes";
const char * const SETTINGS_DEFAULTTEXTENCODING = "General/DefaultFileEncoding";
diff --git a/src/plugins/coreplugin/coreplugin.cpp b/src/plugins/coreplugin/coreplugin.cpp
index 36ef2b94ce..5092ba8dc4 100644
--- a/src/plugins/coreplugin/coreplugin.cpp
+++ b/src/plugins/coreplugin/coreplugin.cpp
@@ -38,6 +38,7 @@
#include "modemanager.h"
#include "fileiconprovider.h"
#include "designmode.h"
+#include "mimedatabase.h"
#include <extensionsystem/pluginmanager.h>
@@ -99,6 +100,7 @@ bool CorePlugin::initialize(const QStringList &arguments, QString *errorMessage)
void CorePlugin::extensionsInitialized()
{
+ m_mainWindow->mimeDatabase()->syncUserModifiedMimeTypes();
m_mainWindow->extensionsInitialized();
}
diff --git a/src/plugins/coreplugin/coreplugin.pro b/src/plugins/coreplugin/coreplugin.pro
index aee407e7a0..c25dcb3c43 100644
--- a/src/plugins/coreplugin/coreplugin.pro
+++ b/src/plugins/coreplugin/coreplugin.pro
@@ -91,7 +91,9 @@ SOURCES += mainwindow.cpp \
externaltool.cpp \
dialogs/externaltoolconfig.cpp \
toolsettings.cpp \
- variablechooser.cpp
+ variablechooser.cpp \
+ mimetypemagicdialog.cpp \
+ mimetypesettings.cpp
HEADERS += mainwindow.h \
editmode.h \
@@ -180,7 +182,9 @@ HEADERS += mainwindow.h \
externaltool.h \
dialogs/externaltoolconfig.h \
toolsettings.h \
- variablechooser.h
+ variablechooser.h \
+ mimetypemagicdialog.h \
+ mimetypesettings.h
FORMS += dialogs/newdialog.ui \
actionmanager/commandmappings.ui \
@@ -189,7 +193,10 @@ FORMS += dialogs/newdialog.ui \
editormanager/openeditorsview.ui \
generalsettings.ui \
dialogs/externaltoolconfig.ui \
- variablechooser.ui
+ variablechooser.ui \
+ mimetypesettingspage.ui \
+ mimetypemagicdialog.ui
+
RESOURCES += core.qrc \
fancyactionbar.qrc
diff --git a/src/plugins/coreplugin/dialogs/shortcutsettings.cpp b/src/plugins/coreplugin/dialogs/shortcutsettings.cpp
index ba9f438212..72c6281c24 100644
--- a/src/plugins/coreplugin/dialogs/shortcutsettings.cpp
+++ b/src/plugins/coreplugin/dialogs/shortcutsettings.cpp
@@ -75,7 +75,7 @@ ShortcutSettings::~ShortcutSettings()
QString ShortcutSettings::id() const
{
- return QLatin1String("D.Keyboard");
+ return QLatin1String(Core::Constants::SETTINGS_ID_SHORTCUTS);
}
QString ShortcutSettings::displayName() const
diff --git a/src/plugins/coreplugin/mainwindow.cpp b/src/plugins/coreplugin/mainwindow.cpp
index ac9ce6d311..05f0844efd 100644
--- a/src/plugins/coreplugin/mainwindow.cpp
+++ b/src/plugins/coreplugin/mainwindow.cpp
@@ -40,6 +40,7 @@
#include "editormanager.h"
#include "externaltool.h"
#include "toolsettings.h"
+#include "mimetypesettings.h"
#include "fancytabwidget.h"
#include "filemanager.h"
#include "generalsettings.h"
@@ -152,6 +153,7 @@ MainWindow::MainWindow() :
m_generalSettings(new GeneralSettings),
m_shortcutSettings(new ShortcutSettings),
m_toolSettings(new ToolSettings),
+ m_mimeTypeSettings(new MimeTypeSettings),
m_systemEditor(new SystemEditor),
m_focusToEditor(0),
m_newAction(0),
@@ -256,6 +258,7 @@ MainWindow::~MainWindow()
pm->removeObject(m_shortcutSettings);
pm->removeObject(m_generalSettings);
pm->removeObject(m_toolSettings);
+ pm->removeObject(m_mimeTypeSettings);
pm->removeObject(m_systemEditor);
delete m_externalToolManager;
m_externalToolManager = 0;
@@ -267,6 +270,8 @@ MainWindow::~MainWindow()
m_generalSettings = 0;
delete m_toolSettings;
m_toolSettings = 0;
+ delete m_mimeTypeSettings;
+ m_mimeTypeSettings = 0;
delete m_systemEditor;
m_systemEditor = 0;
delete m_settings;
@@ -325,9 +330,9 @@ bool MainWindow::init(QString *errorMessage)
pm->addObject(m_generalSettings);
pm->addObject(m_shortcutSettings);
pm->addObject(m_toolSettings);
+ pm->addObject(m_mimeTypeSettings);
pm->addObject(m_systemEditor);
-
// Add widget to the bottom, we create the view here instead of inside the
// OutputPaneManager, since the StatusBarManager needs to be initialized before
m_outputView = new Core::StatusBarWidget;
@@ -352,8 +357,6 @@ void MainWindow::extensionsInitialized()
readSettings();
updateContext();
- registerUserMimeTypes();
-
emit m_coreImpl->coreAboutToOpen();
show();
emit m_coreImpl->coreOpened();
@@ -1397,15 +1400,3 @@ bool MainWindow::showWarningWithOptions(const QString &title,
}
return false;
}
-
-void MainWindow::registerUserMimeTypes() const
-{
- // This is to temporarily allow user specific MIME types (without recompilation).
- // Be careful with the file contents. Otherwise unpredictable behavior might arise.
- const QString &fileName = m_coreImpl->userResourcePath() + QLatin1String("/mimetypes.xml");
- if (QFile::exists(fileName)) {
- QString error;
- if (!m_coreImpl->mimeDatabase()->addMimeTypes(fileName, &error))
- qWarning() << error;
- }
-}
diff --git a/src/plugins/coreplugin/mainwindow.h b/src/plugins/coreplugin/mainwindow.h
index 1d72a35912..f64731fc65 100644
--- a/src/plugins/coreplugin/mainwindow.h
+++ b/src/plugins/coreplugin/mainwindow.h
@@ -78,6 +78,7 @@ class GeneralSettings;
class ProgressManagerPrivate;
class ShortcutSettings;
class ToolSettings;
+class MimeTypeSettings;
class StatusBarManager;
class VersionDialog;
class SystemEditor;
@@ -179,8 +180,6 @@ private:
void readSettings();
void writeSettings();
- void registerUserMimeTypes() const;
-
CoreImpl *m_coreImpl;
UniqueIDManager *m_uniqueIDManager;
Context m_additionalContexts;
@@ -214,6 +213,7 @@ private:
GeneralSettings *m_generalSettings;
ShortcutSettings *m_shortcutSettings;
ToolSettings *m_toolSettings;
+ MimeTypeSettings *m_mimeTypeSettings;
SystemEditor *m_systemEditor;
// actions
diff --git a/src/plugins/coreplugin/mimedatabase.cpp b/src/plugins/coreplugin/mimedatabase.cpp
index 82f02f13bb..ad5853deb2 100644
--- a/src/plugins/coreplugin/mimedatabase.cpp
+++ b/src/plugins/coreplugin/mimedatabase.cpp
@@ -33,6 +33,7 @@
#include "mimedatabase.h"
#include "coreconstants.h"
+#include "icore.h"
#include <utils/qtcassert.h>
@@ -40,9 +41,11 @@
#include <QtCore/QCoreApplication>
#include <QtCore/QDebug>
#include <QtCore/QFile>
+#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include <QtCore/QLocale>
#include <QtCore/QMap>
+#include <QtCore/QHash>
#include <QtCore/QMultiHash>
#include <QtCore/QRegExp>
#include <QtCore/QSharedData>
@@ -50,10 +53,11 @@
#include <QtCore/QStringList>
#include <QtCore/QTextStream>
#include <QtCore/QMutexLocker>
-
-#include <QtXml/QXmlStreamReader>
+#include <QtCore/QXmlStreamReader>
+#include <QtCore/QXmlStreamWriter>
#include <algorithm>
+#include <functional>
enum { debugMimeDB = 0 };
@@ -211,6 +215,8 @@ bool HeuristicTextMagicMatcher::matches(const QByteArray &data) const
} // namespace Internal
// MagicRule
+const QChar MagicRule::kColon(QLatin1Char(':'));
+
MagicRule::MagicRule(int startPos, int endPos) : m_startPos(startPos), m_endPos(endPos)
{
}
@@ -229,6 +235,20 @@ int MagicRule::endPos() const
return m_endPos;
}
+QString MagicRule::toOffset(const QPair<int, int> &startEnd)
+{
+ return QString(QLatin1String("%1:%2")).arg(startEnd.first).arg(startEnd.second);
+}
+
+QPair<int, int> MagicRule::fromOffset(const QString &offset)
+{
+ const QStringList &startEnd = offset.split(kColon);
+ Q_ASSERT(startEnd.size() == 2);
+ return qMakePair(startEnd.at(0).toInt(), startEnd.at(1).toInt());
+}
+
+const QString MagicStringRule::kMatchType("string");
+
MagicStringRule::MagicStringRule(const QString &s, int startPos, int endPos) :
MagicRule(startPos, endPos), m_pattern(s.toUtf8())
{
@@ -238,6 +258,16 @@ MagicStringRule::~MagicStringRule()
{
}
+QString MagicStringRule::matchType() const
+{
+ return kMatchType;
+}
+
+QString MagicStringRule::matchValue() const
+{
+ return m_pattern;
+}
+
bool MagicStringRule::matches(const QByteArray &data) const
{
// Quick check
@@ -254,26 +284,49 @@ bool MagicStringRule::matches(const QByteArray &data) const
return rc;
}
+const QString MagicByteRule::kMatchType(QLatin1String("byte"));
+
MagicByteRule::MagicByteRule(const QString &s, int startPos, int endPos) :
- MagicRule(startPos, endPos)
+ MagicRule(startPos, endPos), m_bytesSize(0)
+{
+ if (validateByteSequence(s, &m_bytes))
+ m_bytesSize = m_bytes.size();
+ else
+ m_bytes.clear();
+}
+
+MagicByteRule::~MagicByteRule()
+{
+}
+
+bool MagicByteRule::validateByteSequence(const QString &sequence, QList<int> *bytes)
{
// Expect an hex format value like this: \0x7f\0x45\0x4c\0x46
- const QStringList &bytes = s.split(QLatin1Char('\\'), QString::SkipEmptyParts);
- foreach (const QString &byte, bytes) {
+ const QStringList &byteSequence = sequence.split(QLatin1Char('\\'), QString::SkipEmptyParts);
+ foreach (const QString &byte, byteSequence) {
bool ok;
const int hex = byte.toInt(&ok, 16);
if (ok) {
- m_bytes.push_back(hex);
+ if (bytes)
+ bytes->push_back(hex);
} else {
- m_bytes.clear();
- break;
+ return false;
}
}
- m_bytesSize = m_bytes.size();
+ return true;
}
-MagicByteRule::~MagicByteRule()
+QString MagicByteRule::matchType() const
{
+ return kMatchType;
+}
+
+QString MagicByteRule::matchValue() const
+{
+ QString value;
+ foreach (int byte, m_bytes)
+ value.append(QString(QLatin1String("\\0x%1")).arg(byte, 0, 16));
+ return value;
}
bool MagicByteRule::matches(const QByteArray &data) const
@@ -307,7 +360,17 @@ MagicRuleMatcher::MagicRuleMatcher() :
void MagicRuleMatcher::add(const MagicRuleSharedPointer &rule)
{
- m_list.push_back(rule);
+ m_list.append(rule);
+}
+
+void MagicRuleMatcher::add(const MagicRuleList &ruleList)
+{
+ m_list.append(ruleList);
+}
+
+MagicRuleMatcher::MagicRuleList MagicRuleMatcher::magicRules() const
+{
+ return m_list;
}
bool MagicRuleMatcher::matches(const QByteArray &data) const
@@ -329,6 +392,20 @@ void MagicRuleMatcher::setPriority(int p)
m_priority = p;
}
+IMagicMatcher::IMagicMatcherList MagicRuleMatcher::createMatchers(
+ const QHash<int, MagicRuleList> &rulesByPriority)
+{
+ IMagicMatcher::IMagicMatcherList matchers;
+ QHash<int, MagicRuleList>::const_iterator ruleIt = rulesByPriority.begin();
+ for (; ruleIt != rulesByPriority.end(); ++ruleIt) {
+ MagicRuleMatcher *magicRuleMatcher = new MagicRuleMatcher();
+ magicRuleMatcher->setPriority(ruleIt.key());
+ magicRuleMatcher->add(ruleIt.value());
+ matchers.append(IMagicMatcher::IMagicMatcherSharedPointer(magicRuleMatcher));
+ }
+ return matchers;
+}
+
// GlobPattern
MimeGlobPattern::MimeGlobPattern(const QRegExp &regExp, unsigned weight) :
m_regExp(regExp), m_weight(weight)
@@ -353,9 +430,16 @@ unsigned MimeGlobPattern::weight() const
class MimeTypeData : public QSharedData {
public:
typedef QHash<QString,QString> LocaleHash;
+
+ MimeTypeData();
+
void clear();
+ void assignSuffix(const QString &pattern);
+ void assignSuffixes(const QStringList &patterns);
void debug(QTextStream &str, int indent = 0) const;
+ const QRegExp suffixPattern;
+
QString type;
QString comment;
@@ -365,12 +449,17 @@ public:
QStringList subClassesOf;
QString preferredSuffix;
QStringList suffixes;
-
- typedef QSharedPointer<IMagicMatcher> IMagicMatcherSharedPointer;
- typedef QList<IMagicMatcherSharedPointer> IMagicMatcherList;
- IMagicMatcherList magicMatchers;
+ IMagicMatcher::IMagicMatcherList magicMatchers;
};
+MimeTypeData::MimeTypeData()
+ // RE to match a suffix glob pattern: "*.ext" (and not sth like "Makefile" or
+ // "*.log[1-9]"
+ : suffixPattern(QLatin1String("^\\*\\.[\\w+]+$"))
+{
+ QTC_ASSERT(suffixPattern.isValid(), /**/);
+}
+
void MimeTypeData::clear()
{
type.clear();
@@ -383,6 +472,22 @@ void MimeTypeData::clear()
magicMatchers.clear();
}
+void MimeTypeData::assignSuffix(const QString &pattern)
+{
+ if (suffixPattern.exactMatch(pattern)) {
+ const QString suffix = pattern.right(pattern.size() - 2);
+ suffixes.push_back(suffix);
+ if (preferredSuffix.isEmpty())
+ preferredSuffix = suffix;
+ }
+}
+
+void MimeTypeData::assignSuffixes(const QStringList &patterns)
+{
+ foreach (const QString &pattern, patterns)
+ assignSuffix(pattern);
+}
+
void MimeTypeData::debug(QTextStream &str, int indent) const
{
const QString indentS = QString(indent, QLatin1Char(' '));
@@ -516,6 +621,13 @@ QList<MimeGlobPattern> MimeType::globPatterns() const
void MimeType::setGlobPatterns(const QList<MimeGlobPattern> &g)
{
m_d->globPatterns = g;
+
+ QString oldPrefferedSuffix = m_d->preferredSuffix;
+ m_d->suffixes.clear();
+ m_d->preferredSuffix.clear();
+ m_d->assignSuffixes(MimeDatabase::fromGlobPatterns(g));
+ if (m_d->preferredSuffix != oldPrefferedSuffix && m_d->suffixes.contains(oldPrefferedSuffix))
+ m_d->preferredSuffix = oldPrefferedSuffix;
}
QStringList MimeType::subClassesOf() const
@@ -608,7 +720,7 @@ unsigned MimeType::matchesFileByContent(Internal::FileMatchContext &c) const
const QByteArray data = c.data();
if (!data.isEmpty()) {
- foreach (const MimeTypeData::IMagicMatcherSharedPointer &matcher, m_d->magicMatchers) {
+ foreach (const IMagicMatcher::IMagicMatcherSharedPointer &matcher, m_d->magicMatchers) {
if (matcher->matches(data)) {
const unsigned magicPriority = matcher->priority();
if (magicPriority > priority)
@@ -624,14 +736,50 @@ QStringList MimeType::suffixes() const
return m_d->suffixes;
}
-void MimeType::setSuffixes(const QStringList &s)
+void MimeType::addMagicMatcher(const IMagicMatcherSharedPointer &matcher)
{
- m_d->suffixes = s;
+ m_d->magicMatchers.push_back(matcher);
}
-void MimeType::addMagicMatcher(const QSharedPointer<IMagicMatcher> &matcher)
+const MimeType::IMagicMatcherList &MimeType::magicMatchers() const
{
- m_d->magicMatchers.push_back(matcher);
+ return m_d->magicMatchers;
+}
+
+void MimeType::setMagicMatchers(const IMagicMatcherList &matchers)
+{
+ m_d->magicMatchers = matchers;
+}
+
+namespace {
+struct RemovePred : std::unary_function<MimeType::IMagicMatcherSharedPointer, bool>
+{
+ RemovePred(bool keepRuleBased) : m_keepRuleBase(keepRuleBased) {}
+ bool m_keepRuleBase;
+
+ bool operator()(const MimeType::IMagicMatcherSharedPointer &matcher) {
+ if ((m_keepRuleBase && !dynamic_cast<MagicRuleMatcher *>(matcher.data()))
+ || (!m_keepRuleBase && dynamic_cast<MagicRuleMatcher *>(matcher.data())))
+ return true;
+ return false;
+ }
+};
+} // Anonymous
+
+MimeType::IMagicMatcherList MimeType::magicRuleMatchers() const
+{
+ IMagicMatcherList ruleMatchers = m_d->magicMatchers;
+ ruleMatchers.erase(std::remove_if(ruleMatchers.begin(), ruleMatchers.end(), RemovePred(true)),
+ ruleMatchers.end());
+ return ruleMatchers;
+}
+
+void MimeType::setMagicRuleMatchers(const IMagicMatcherList &matchers)
+{
+ m_d->magicMatchers.erase(std::remove_if(m_d->magicMatchers.begin(), m_d->magicMatchers.end(),
+ RemovePred(false)),
+ m_d->magicMatchers.end());
+ m_d->magicMatchers.append(matchers);
}
QDebug operator<<(QDebug d, const MimeType &mt)
@@ -652,7 +800,7 @@ namespace Internal {
class BaseMimeTypeParser {
Q_DISABLE_COPY(BaseMimeTypeParser)
public:
- BaseMimeTypeParser();
+ BaseMimeTypeParser() {}
virtual ~BaseMimeTypeParser() {}
bool parse(QIODevice *dev, const QString &fileName, QString *errorMessage);
@@ -676,18 +824,8 @@ private:
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, const QString &weight, MimeTypeData *d) const
{
if (pattern.isEmpty())
@@ -706,12 +844,7 @@ void BaseMimeTypeParser::addGlobPattern(const QString &pattern, const QString &w
else
d->globPatterns.push_back(MimeGlobPattern(wildCard, weight.toInt()));
- 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;
- }
+ d->assignSuffix(pattern);
}
BaseMimeTypeParser::ParseStage BaseMimeTypeParser::nextStage(ParseStage currentStage, const QStringRef &startElement)
@@ -934,10 +1067,10 @@ MimeMapEntry::MimeMapEntry(const MimeType &t, int aLevel) :
* - Provide quick lookup by file type.
* This basically rules out some pointer-based tree, so the structure chosen
* is:
- * - An alias map <QString->QString> for mapping aliases to types
- * - A Map <QString-MimeMapEntry> for the types (MimeMapEntry being a pair of
+ * - 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
+ * - 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()
@@ -958,16 +1091,31 @@ public:
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);
+ QStringList filterStrings() const;
- // Return all known suffixes
QStringList suffixes() const;
- QStringList filterStrings() const;
+ bool setPreferredSuffix(const QString &typeOrAlias, const QString &suffix);
+
+ QList<MimeGlobPattern> globPatterns() const;
+ void setGlobPatterns(const QString &typeOrAlias, const QList<MimeGlobPattern> &globPatterns);
+
+ QList<QSharedPointer<IMagicMatcher> > magicMatchers() const;
+ void setMagicMatchers(const QString &typeOrAlias,
+ const QList<QSharedPointer<IMagicMatcher> > &matchers);
+
+ QList<MimeType> mimeTypes() const;
+
+ void syncUserModifiedMimeTypes();
+ static QList<MimeType> readUserModifiedMimeTypes();
+ static void writeUserModifiedMimeTypes(const QList<MimeType> &mimeTypes);
+ void clearUserModifiedMimeTypes();
+
+ static QList<MimeGlobPattern> toGlobPatterns(const QStringList &patterns,
+ int weight = MimeGlobPattern::MaxWeight);
+ static QStringList fromGlobPatterns(const QList<MimeGlobPattern> &globPatterns);
void debug(QTextStream &str) const;
@@ -976,6 +1124,11 @@ private:
typedef QHash<QString, QString> AliasMap;
typedef QMultiHash<QString, QString> ParentChildrenMap;
+ static const QChar kSemiColon;
+ static const QString kModifiedMimeTypesFile;
+ static QString kModifiedMimeTypesPath;
+
+
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;
@@ -988,9 +1141,15 @@ private:
int m_maxLevel;
};
+const QChar MimeDatabasePrivate::kSemiColon(QLatin1Char(';'));
+const QString MimeDatabasePrivate::kModifiedMimeTypesFile(QLatin1String("modifiedmimetypes.xml"));
+QString MimeDatabasePrivate::kModifiedMimeTypesPath;
+
MimeDatabasePrivate::MimeDatabasePrivate() :
m_maxLevel(-1)
{
+ // Assign here to avoid non-local static data initialization issues.
+ kModifiedMimeTypesPath = ICore::instance()->userResourcePath() + QLatin1String("/mimetypes/");
}
namespace Internal {
@@ -1230,6 +1389,197 @@ QStringList MimeDatabasePrivate::filterStrings() const
return rc;
}
+QList<MimeGlobPattern> MimeDatabasePrivate::globPatterns() const
+{
+ QList<MimeGlobPattern> globPatterns;
+ const TypeMimeTypeMap::const_iterator cend = m_typeMimeTypeMap.constEnd();
+ for (TypeMimeTypeMap::const_iterator it = m_typeMimeTypeMap.constBegin(); it != cend; ++it)
+ globPatterns.append(it.value().type.globPatterns());
+ return globPatterns;
+}
+
+void MimeDatabasePrivate::setGlobPatterns(const QString &typeOrAlias,
+ const QList<MimeGlobPattern> &globPatterns)
+{
+ TypeMimeTypeMap::iterator tit = m_typeMimeTypeMap.find(resolveAlias(typeOrAlias));
+ if (tit != m_typeMimeTypeMap.end())
+ tit.value().type.setGlobPatterns(globPatterns);
+}
+
+QList<QSharedPointer<IMagicMatcher> > MimeDatabasePrivate::magicMatchers() const
+{
+ QList<QSharedPointer<IMagicMatcher> > magicMatchers;
+ const TypeMimeTypeMap::const_iterator cend = m_typeMimeTypeMap.constEnd();
+ for (TypeMimeTypeMap::const_iterator it = m_typeMimeTypeMap.constBegin(); it != cend; ++it)
+ magicMatchers.append(it.value().type.magicMatchers());
+ return magicMatchers;
+}
+
+void MimeDatabasePrivate::setMagicMatchers(const QString &typeOrAlias,
+ const QList<QSharedPointer<IMagicMatcher> > &matchers)
+{
+ TypeMimeTypeMap::iterator tit = m_typeMimeTypeMap.find(resolveAlias(typeOrAlias));
+ if (tit != m_typeMimeTypeMap.end())
+ tit.value().type.setMagicMatchers(matchers);
+}
+
+QList<MimeType> MimeDatabasePrivate::mimeTypes() const
+{
+ QList<MimeType> mimeTypes;
+ const TypeMimeTypeMap::const_iterator cend = m_typeMimeTypeMap.constEnd();
+ for (TypeMimeTypeMap::const_iterator it = m_typeMimeTypeMap.constBegin(); it != cend; ++it)
+ mimeTypes.append(it.value().type);
+ return mimeTypes;
+}
+
+void MimeDatabasePrivate::syncUserModifiedMimeTypes()
+{
+ QHash<QString, MimeType> userModified;
+ const QList<MimeType> &userMimeTypes = readUserModifiedMimeTypes();
+ foreach (const MimeType &userMimeType, userMimeTypes)
+ userModified.insert(userMimeType.type(), userMimeType);
+
+ TypeMimeTypeMap::iterator end = m_typeMimeTypeMap.end();
+ QHash<QString, MimeType>::const_iterator userMimeEnd = userModified.end();
+ for (TypeMimeTypeMap::iterator it = m_typeMimeTypeMap.begin(); it != end; ++it) {
+ QHash<QString, MimeType>::const_iterator userMimeIt =
+ userModified.find(it.value().type.type());
+ if (userMimeIt != userMimeEnd) {
+ it.value().type.setGlobPatterns(userMimeIt.value().globPatterns());
+ it.value().type.setMagicRuleMatchers(userMimeIt.value().magicRuleMatchers());
+ }
+ }
+}
+
+QList<MimeType> MimeDatabasePrivate::readUserModifiedMimeTypes()
+{
+ typedef MagicRuleMatcher::MagicRuleList MagicRuleList;
+ typedef MagicRuleMatcher::MagicRuleSharedPointer MagicRuleSharedPointer;
+
+ QList<MimeType> mimeTypes;
+ QFile file(kModifiedMimeTypesPath + kModifiedMimeTypesFile);
+ if (file.open(QFile::ReadOnly)) {
+ MimeType mimeType;
+ QHash<int, MagicRuleList> rules;
+ QXmlStreamReader reader(&file);
+ QXmlStreamAttributes atts;
+ while (!reader.atEnd()) {
+ switch (reader.readNext()) {
+ case QXmlStreamReader::StartElement:
+ atts = reader.attributes();
+ if (reader.name() == mimeTypeTagC) {
+ mimeType.setType(atts.value(mimeTypeAttributeC).toString());
+ const QString &patterns = atts.value(patternAttributeC).toString();
+ mimeType.setGlobPatterns(toGlobPatterns(patterns.split(kSemiColon)));
+ } else if (reader.name() == matchTagC) {
+ const QString &value = atts.value(matchValueAttributeC).toString();
+ const QString &type = atts.value(matchTypeAttributeC).toString();
+ const QString &offset = atts.value(matchOffsetAttributeC).toString();
+ QPair<int, int> range = MagicRule::fromOffset(offset);
+ const int priority = atts.value(priorityAttributeC).toString().toInt();
+
+ MagicRule *magicRule;
+ if (type == MagicStringRule::kMatchType)
+ magicRule = new MagicStringRule(value, range.first, range.second);
+ else
+ magicRule = new MagicByteRule(value, range.first, range.second);
+ rules[priority].append(MagicRuleSharedPointer(magicRule));
+ }
+ break;
+ case QXmlStreamReader::EndElement:
+ if (reader.name() == mimeTypeTagC) {
+ mimeType.setMagicRuleMatchers(MagicRuleMatcher::createMatchers(rules));
+ mimeTypes.append(mimeType);
+ mimeType.clear();
+ rules.clear();
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if (reader.hasError())
+ qWarning() << kModifiedMimeTypesFile << reader.errorString() << reader.lineNumber()
+ << reader.columnNumber();
+ file.close();
+ }
+ return mimeTypes;
+}
+
+void MimeDatabasePrivate::writeUserModifiedMimeTypes(const QList<MimeType> &mimeTypes)
+{
+ // Keep mime types modified which are already on file.
+ QList<MimeType> allModifiedMimeTypes = mimeTypes;
+ allModifiedMimeTypes.append(readUserModifiedMimeTypes());
+
+ if (QFile::exists(kModifiedMimeTypesPath) || QDir().mkpath(kModifiedMimeTypesPath)) {
+ QFile file(kModifiedMimeTypesPath + kModifiedMimeTypesFile);
+ if (file.open(QFile::WriteOnly | QFile::Truncate)) {
+ // Notice this file only represents user modifications. It is writen in a
+ // convienient way for synchronization, which is similar to but not exactly the
+ // same format we use for the embedded mime type files.
+ QXmlStreamWriter writer(&file);
+ writer.setAutoFormatting(true);
+ writer.writeStartDocument();
+ writer.writeStartElement(QLatin1String(mimeInfoTagC));
+ foreach (const MimeType &mimeType, allModifiedMimeTypes) {
+ writer.writeStartElement(mimeTypeTagC);
+ writer.writeAttribute(mimeTypeAttributeC, mimeType.type());
+ writer.writeAttribute(patternAttributeC,
+ fromGlobPatterns(mimeType.globPatterns()).join(kSemiColon));
+ const QList<QSharedPointer<IMagicMatcher> > &matchers = mimeType.magicMatchers();
+ foreach (const QSharedPointer<IMagicMatcher> &matcher, matchers) {
+ // Only care about rule-based matchers.
+ if (MagicRuleMatcher *ruleMatcher =
+ dynamic_cast<MagicRuleMatcher *>(matcher.data())) {
+ const MagicRuleMatcher::MagicRuleList &rules = ruleMatcher->magicRules();
+ foreach (const MagicRuleMatcher::MagicRuleSharedPointer &rule, rules) {
+ writer.writeStartElement(matchTagC);
+ writer.writeAttribute(matchValueAttributeC, rule->matchValue());
+ writer.writeAttribute(matchTypeAttributeC, rule->matchType());
+ writer.writeAttribute(matchOffsetAttributeC,
+ MagicRule::toOffset(
+ qMakePair(rule->startPos(), rule->endPos())));
+ writer.writeAttribute(priorityAttributeC,
+ QString::number(ruleMatcher->priority()));
+ writer.writeEndElement();
+ }
+ }
+ }
+ writer.writeEndElement();
+ }
+ writer.writeEndElement();
+ writer.writeEndDocument();
+ file.close();
+ }
+ }
+}
+
+void MimeDatabasePrivate::clearUserModifiedMimeTypes()
+{
+ // This removes the user's file. However, the operation will actually take place the next time
+ // Creator starts, since we currently don't support removing stuff from the mime database.
+ QFile::remove(kModifiedMimeTypesPath + kModifiedMimeTypesFile);
+}
+
+QList<MimeGlobPattern> MimeDatabasePrivate::toGlobPatterns(const QStringList &patterns, int weight)
+{
+ QList<MimeGlobPattern> globPatterns;
+ foreach (const QString &pattern, patterns) {
+ QRegExp regExp(pattern, Qt::CaseSensitive, QRegExp::Wildcard);
+ globPatterns.append(Core::MimeGlobPattern(regExp, weight));
+ }
+ return globPatterns;
+}
+
+QStringList MimeDatabasePrivate::fromGlobPatterns(const QList<MimeGlobPattern> &globPatterns)
+{
+ QStringList patterns;
+ foreach (const MimeGlobPattern &globPattern, globPatterns)
+ patterns.append(globPattern.regExp().pattern());
+ return patterns;
+}
+
void MimeDatabasePrivate::debug(QTextStream &str) const
{
str << ">MimeDatabase\n";
@@ -1308,7 +1658,7 @@ QStringList MimeDatabase::suffixes() const
QStringList MimeDatabase::filterStrings() const
{
m_mutex.lock();
- const QStringList rc = m_d->filterStrings();
+ const QStringList rc = m_d->filterStrings();
m_mutex.unlock();
return rc;
}
@@ -1336,6 +1686,70 @@ QString MimeDatabase::allFiltersString(QString *allFilesFilter) const
return filters.join(QLatin1String(";;"));
}
+QList<MimeGlobPattern> MimeDatabase::globPatterns() const
+{
+ m_mutex.lock();
+ const QList<MimeGlobPattern> rc = m_d->globPatterns();
+ m_mutex.unlock();
+ return rc;
+}
+
+void MimeDatabase::setGlobPatterns(const QString &typeOrAlias,
+ const QList<MimeGlobPattern> &globPatterns)
+{
+ m_mutex.lock();
+ m_d->setGlobPatterns(typeOrAlias, globPatterns);
+ m_mutex.unlock();
+}
+
+MimeDatabase::IMagicMatcherList MimeDatabase::magicMatchers() const
+{
+ m_mutex.lock();
+ const IMagicMatcherList rc = m_d->magicMatchers();
+ m_mutex.unlock();
+ return rc;
+}
+
+void MimeDatabase::setMagicMatchers(const QString &typeOrAlias,
+ const IMagicMatcherList &matchers)
+{
+ m_mutex.lock();
+ m_d->setMagicMatchers(typeOrAlias, matchers);
+ m_mutex.unlock();
+}
+
+QList<MimeType> MimeDatabase::mimeTypes() const
+{
+ m_mutex.lock();
+ const QList<MimeType> &mimeTypes = m_d->mimeTypes();
+ m_mutex.unlock();
+ return mimeTypes;
+}
+
+void MimeDatabase::syncUserModifiedMimeTypes()
+{
+ m_mutex.lock();
+ m_d->syncUserModifiedMimeTypes();
+ m_mutex.unlock();
+}
+
+void MimeDatabase::clearUserModifiedMimeTypes()
+{
+ m_mutex.lock();
+ m_d->clearUserModifiedMimeTypes();
+ m_mutex.unlock();
+}
+
+QList<MimeType> MimeDatabase::readUserModifiedMimeTypes()
+{
+ return MimeDatabasePrivate::readUserModifiedMimeTypes();
+}
+
+void MimeDatabase::writeUserModifiedMimeTypes(const QList<MimeType> &mimeTypes)
+{
+ MimeDatabasePrivate::writeUserModifiedMimeTypes(mimeTypes);
+}
+
QString MimeDatabase::preferredSuffixByType(const QString &type) const
{
if (const MimeType mt = findByType(type))
@@ -1358,6 +1772,16 @@ bool MimeDatabase::setPreferredSuffix(const QString &typeOrAlias, const QString
return rc;
}
+QList<MimeGlobPattern> MimeDatabase::toGlobPatterns(const QStringList &patterns, int weight)
+{
+ return MimeDatabasePrivate::toGlobPatterns(patterns, weight);
+}
+
+QStringList MimeDatabase::fromGlobPatterns(const QList<MimeGlobPattern> &globPatterns)
+{
+ return MimeDatabasePrivate::fromGlobPatterns(globPatterns);
+}
+
QDebug operator<<(QDebug d, const MimeDatabase &mt)
{
QString s;
diff --git a/src/plugins/coreplugin/mimedatabase.h b/src/plugins/coreplugin/mimedatabase.h
index 954cf8e50b..722a015870 100644
--- a/src/plugins/coreplugin/mimedatabase.h
+++ b/src/plugins/coreplugin/mimedatabase.h
@@ -41,6 +41,7 @@
#include <QtCore/QByteArray>
#include <QtCore/QMutex>
#include <QtCore/QFileInfo>
+#include <QtCore/QPair>
QT_BEGIN_NAMESPACE
class QIODevice;
@@ -66,6 +67,9 @@ class CORE_EXPORT IMagicMatcher
protected:
IMagicMatcher() {}
public:
+ typedef QSharedPointer<IMagicMatcher> IMagicMatcherSharedPointer;
+ typedef QList<IMagicMatcherSharedPointer> IMagicMatcherList;
+
// Check for a match on contents of a file
virtual bool matches(const QByteArray &data) const = 0;
// Return a priority value from 1..100
@@ -83,13 +87,19 @@ public:
MagicRule(int startPos, int endPos);
virtual ~MagicRule();
+ virtual QString matchType() const = 0;
+ virtual QString matchValue() const = 0;
virtual bool matches(const QByteArray &data) const = 0;
-protected:
int startPos() const;
int endPos() const;
+ static QString toOffset(const QPair<int, int> &startEnd);
+ static QPair<int, int> fromOffset(const QString &offset);
+
private:
+ static const QChar kColon;
+
const int m_startPos;
const int m_endPos;
};
@@ -100,8 +110,12 @@ public:
MagicStringRule(const QString &s, int startPos, int endPos);
virtual ~MagicStringRule();
+ virtual QString matchType() const;
+ virtual QString matchValue() const;
virtual bool matches(const QByteArray &data) const;
+ static const QString kMatchType;
+
private:
const QByteArray m_pattern;
};
@@ -112,11 +126,17 @@ public:
MagicByteRule(const QString &s, int startPos, int endPos);
virtual ~MagicByteRule();
+ virtual QString matchType() const;
+ virtual QString matchValue() const;
virtual bool matches(const QByteArray &data) const;
+ static bool validateByteSequence(const QString &sequence, QList<int> *bytes = 0);
+
+ static const QString kMatchType;
+
private:
- QList<int> m_bytes;
int m_bytesSize;
+ QList<int> m_bytes;
};
/* Utility class: A Magic matcher that checks a number of rules based on
@@ -125,17 +145,24 @@ class CORE_EXPORT MagicRuleMatcher : public IMagicMatcher
{
Q_DISABLE_COPY(MagicRuleMatcher)
public:
- typedef QSharedPointer<MagicRule> MagicRuleSharedPointer;
+ typedef QSharedPointer<MagicRule> MagicRuleSharedPointer;
+ typedef QList<MagicRuleSharedPointer> MagicRuleList;
MagicRuleMatcher();
+
void add(const MagicRuleSharedPointer &rule);
+ void add(const MagicRuleList &ruleList);
+ MagicRuleList magicRules() const;
+
virtual bool matches(const QByteArray &data) const;
virtual int priority() const;
void setPriority(int p);
+ // Create a list of MagicRuleMatchers from a hash of rules indexed by priorities.
+ static IMagicMatcher::IMagicMatcherList createMatchers(const QHash<int, MagicRuleList> &);
+
private:
- typedef QList<MagicRuleSharedPointer> MagicRuleList;
MagicRuleList m_list;
int m_priority;
};
@@ -169,6 +196,9 @@ private:
class CORE_EXPORT MimeType
{
public:
+ typedef IMagicMatcher::IMagicMatcherList IMagicMatcherList;
+ typedef IMagicMatcher::IMagicMatcherSharedPointer IMagicMatcherSharedPointer;
+
MimeType();
MimeType(const MimeType&);
MimeType &operator=(const MimeType&);
@@ -200,9 +230,6 @@ public:
// Extension over standard mime data
QStringList suffixes() const;
- void setSuffixes(const QStringList &);
-
- // Extension over standard mime data
QString preferredSuffix() const;
bool setPreferredSuffix(const QString&);
@@ -216,12 +243,19 @@ public:
// Return a filter string usable for a file dialog
QString filterString() const;
- // Add magic matcher
- void addMagicMatcher(const QSharedPointer<IMagicMatcher> &matcher);
+ void addMagicMatcher(const IMagicMatcherSharedPointer &matcher);
+
+ const IMagicMatcherList &magicMatchers() const;
+ void setMagicMatchers(const IMagicMatcherList &matchers);
+
+ // Convenience for rule-base matchers.
+ IMagicMatcherList magicRuleMatchers() const;
+ void setMagicRuleMatchers(const IMagicMatcherList &matchers);
friend QDebug operator<<(QDebug d, const MimeType &mt);
- static QString formatFilterString(const QString &description, const QList<MimeGlobPattern> &globs);
+ static QString formatFilterString(const QString &description,
+ const QList<MimeGlobPattern> &globs);
private:
explicit MimeType(const MimeTypeData &d);
@@ -243,8 +277,10 @@ class CORE_EXPORT MimeDatabase
{
Q_DISABLE_COPY(MimeDatabase)
public:
- MimeDatabase();
+ typedef IMagicMatcher::IMagicMatcherList IMagicMatcherList;
+ typedef IMagicMatcher::IMagicMatcherSharedPointer IMagicMatcherSharedPointer;
+ MimeDatabase();
~MimeDatabase();
bool addMimeTypes(const QString &fileName, QString *errorMessage);
@@ -256,27 +292,45 @@ public:
// Returns a mime type or Null one if none found
MimeType findByFile(const QFileInfo &f) const;
+
// Convenience that mutex-locks the DB and calls a function
// of the signature 'void f(const MimeType &, const QFileInfo &, const QString &)'
// for each filename of a sequence. This avoids locking the DB for each
// single file.
template <class Iterator, typename Function>
- inline void findByFile(Iterator i1, const Iterator &i2, Function f) const;
-
- // Convenience
- QString preferredSuffixByType(const QString &type) const;
- QString preferredSuffixByFile(const QFileInfo &f) const;
+ inline void findByFile(Iterator i1, const Iterator &i2, Function f) const;
// Return all known suffixes
QStringList suffixes() const;
bool setPreferredSuffix(const QString &typeOrAlias, const QString &suffix);
+ QString preferredSuffixByType(const QString &type) const;
+ QString preferredSuffixByFile(const QFileInfo &f) const;
QStringList filterStrings() const;
+ // Return a string with all the possible file filters, for use with file dialogs
+ QString allFiltersString(QString *allFilesFilter = 0) const;
+
+ QList<MimeGlobPattern> globPatterns() const;
+ void setGlobPatterns(const QString &typeOrAlias, const QList<MimeGlobPattern> &globPatterns);
+
+ IMagicMatcherList magicMatchers() const;
+ void setMagicMatchers(const QString &typeOrAlias, const IMagicMatcherList &matchers);
+
+ QList<MimeType> mimeTypes() const;
+
+ // The mime types from the functions bellow are considered only in regard to
+ // their glob patterns and rule-based magic matchers.
+ void syncUserModifiedMimeTypes();
+ static QList<MimeType> readUserModifiedMimeTypes();
+ static void writeUserModifiedMimeTypes(const QList<MimeType> &mimeTypes);
+ void clearUserModifiedMimeTypes();
+
+ static QList<MimeGlobPattern> toGlobPatterns(const QStringList &patterns,
+ int weight = MimeGlobPattern::MaxWeight);
+ static QStringList fromGlobPatterns(const QList<MimeGlobPattern> &globPatterns);
friend QDebug operator<<(QDebug d, const MimeDatabase &mt);
- // returns a string with all the possible file filters, for use with file dialogs
- QString allFiltersString(QString *allFilesFilter = 0) const;
private:
MimeType findByFileUnlocked(const QFileInfo &f) const;
diff --git a/src/plugins/coreplugin/mimetypemagicdialog.cpp b/src/plugins/coreplugin/mimetypemagicdialog.cpp
new file mode 100644
index 0000000000..ea444ba74b
--- /dev/null
+++ b/src/plugins/coreplugin/mimetypemagicdialog.cpp
@@ -0,0 +1,109 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** No Commercial Usage
+**
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "mimetypemagicdialog.h"
+#include "mimedatabase.h"
+
+#include <QtCore/QLatin1String>
+#include <QtGui/QMessageBox>
+
+using namespace Core;
+using namespace Internal;
+
+MimeTypeMagicDialog::MimeTypeMagicDialog(QWidget *parent) :
+ QDialog(parent)
+{
+ ui.setupUi(this);
+ setWindowTitle(tr("Magic Header"));
+ connect(ui.useRecommendedGroupBox, SIGNAL(clicked(bool)),
+ this, SLOT(applyRecommended(bool)));
+ connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(validateAccept()));
+}
+
+void MimeTypeMagicDialog::changeEvent(QEvent *e)
+{
+ QDialog::changeEvent(e);
+ switch (e->type()) {
+ case QEvent::LanguageChange:
+ ui.retranslateUi(this);
+ break;
+ default:
+ break;
+ }
+}
+
+void MimeTypeMagicDialog::applyRecommended(bool checked)
+{
+ if (checked) {
+ ui.startRangeSpinBox->setValue(0);
+ ui.endRangeSpinBox->setValue(0);
+ ui.prioritySpinBox->setValue(50);
+ }
+}
+
+void MimeTypeMagicDialog::validateAccept()
+{
+ if (ui.valueLineEdit->text().isEmpty()
+ || (ui.byteRadioButton->isChecked()
+ && !Core::MagicByteRule::validateByteSequence(ui.valueLineEdit->text()))) {
+ QMessageBox::critical(0, tr("Error"), tr("Not a valid byte pattern."));
+ return;
+ }
+ accept();
+}
+
+void MimeTypeMagicDialog::setMagicData(const MagicData &data)
+{
+ ui.valueLineEdit->setText(data.m_value);
+ if (data.m_type == Core::MagicStringRule::kMatchType)
+ ui.stringRadioButton->setChecked(true);
+ else
+ ui.byteRadioButton->setChecked(true);
+ ui.startRangeSpinBox->setValue(data.m_start);
+ ui.endRangeSpinBox->setValue(data.m_end);
+ ui.prioritySpinBox->setValue(data.m_priority);
+}
+
+MagicData MimeTypeMagicDialog::magicData() const
+{
+ MagicData data;
+ data.m_value = ui.valueLineEdit->text();
+ if (ui.stringRadioButton->isChecked())
+ data.m_type = Core::MagicStringRule::kMatchType;
+ else
+ data.m_type = Core::MagicByteRule::kMatchType;
+ data.m_start = ui.startRangeSpinBox->value();
+ data.m_end = ui.endRangeSpinBox->value();
+ data.m_priority = ui.prioritySpinBox->value();
+ return data;
+}
diff --git a/src/plugins/coreplugin/mimetypemagicdialog.h b/src/plugins/coreplugin/mimetypemagicdialog.h
new file mode 100644
index 0000000000..a9cd3ca97f
--- /dev/null
+++ b/src/plugins/coreplugin/mimetypemagicdialog.h
@@ -0,0 +1,83 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** No Commercial Usage
+**
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef MIMETYPEMAGICDIALOG_H
+#define MIMETYPEMAGICDIALOG_H
+
+#include "ui_mimetypemagicdialog.h"
+
+namespace Core {
+namespace Internal {
+
+struct MagicData
+{
+ MagicData() {}
+ MagicData(const QString &value, const QString &type, int start, int end, int p)
+ : m_value(value)
+ , m_type(type)
+ , m_start(start)
+ , m_end(end)
+ , m_priority(p) {}
+
+ QString m_value;
+ QString m_type;
+ int m_start;
+ int m_end;
+ int m_priority;
+};
+
+class MimeTypeMagicDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit MimeTypeMagicDialog(QWidget *parent = 0);
+
+ void setMagicData(const MagicData &data);
+ MagicData magicData() const;
+
+protected:
+ void changeEvent(QEvent *e);
+
+private slots:
+ void applyRecommended(bool checked);
+ void validateAccept();
+
+private:
+ Ui::MimeTypeMagicDialog ui;
+};
+
+} // Internal
+} // Core
+
+#endif // MIMETYPEMAGICDIALOG_H
diff --git a/src/plugins/coreplugin/mimetypemagicdialog.ui b/src/plugins/coreplugin/mimetypemagicdialog.ui
new file mode 100644
index 0000000000..3da4ddbeaf
--- /dev/null
+++ b/src/plugins/coreplugin/mimetypemagicdialog.ui
@@ -0,0 +1,318 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MimeTypeMagicDialog</class>
+ <widget class="QDialog" name="MimeTypeMagicDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>480</width>
+ <height>286</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Dialog</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QLabel" name="valueLabel">
+ <property name="text">
+ <string>Value:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="valueLineEdit"/>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="typeGroupBox">
+ <property name="title">
+ <string>Type</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QRadioButton" name="stringRadioButton">
+ <property name="text">
+ <string>String</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="byteRadioButton">
+ <property name="text">
+ <string>Byte</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="useRecommendedGroupBox">
+ <property name="title">
+ <string>Use Recommended</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="0" column="0">
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="startRangeLabel">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Start range:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="endRangeLabel">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>End range:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="priorityLabel">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Priority:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QSpinBox" name="prioritySpinBox">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="value">
+ <number>50</number>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QSpinBox" name="startRangeSpinBox">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="maximum">
+ <number>9999</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QSpinBox" name="endRangeSpinBox">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="maximum">
+ <number>9999</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="0" column="1">
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>233</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="noteLabel">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&lt;i&gt;Note: Wide range values might impact on Qt Creator's performance when opening files.&lt;/i&gt;</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::RichText</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>11</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>MimeTypeMagicDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>295</x>
+ <y>297</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>useRecommendedGroupBox</sender>
+ <signal>clicked(bool)</signal>
+ <receiver>prioritySpinBox</receiver>
+ <slot>setDisabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>48</x>
+ <y>171</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>105</x>
+ <y>249</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>useRecommendedGroupBox</sender>
+ <signal>clicked(bool)</signal>
+ <receiver>startRangeSpinBox</receiver>
+ <slot>setDisabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>195</x>
+ <y>169</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>107</x>
+ <y>149</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>useRecommendedGroupBox</sender>
+ <signal>clicked(bool)</signal>
+ <receiver>endRangeSpinBox</receiver>
+ <slot>setDisabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>195</x>
+ <y>169</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>107</x>
+ <y>175</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>useRecommendedGroupBox</sender>
+ <signal>clicked(bool)</signal>
+ <receiver>priorityLabel</receiver>
+ <slot>setDisabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>225</x>
+ <y>178</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>99</x>
+ <y>201</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>useRecommendedGroupBox</sender>
+ <signal>clicked(bool)</signal>
+ <receiver>startRangeLabel</receiver>
+ <slot>setDisabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>225</x>
+ <y>178</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>99</x>
+ <y>149</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>useRecommendedGroupBox</sender>
+ <signal>clicked(bool)</signal>
+ <receiver>endRangeLabel</receiver>
+ <slot>setDisabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>225</x>
+ <y>178</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>99</x>
+ <y>175</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>useRecommendedGroupBox</sender>
+ <signal>clicked(bool)</signal>
+ <receiver>noteLabel</receiver>
+ <slot>setDisabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>225</x>
+ <y>178</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>182</x>
+ <y>225</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/plugins/coreplugin/mimetypesettings.cpp b/src/plugins/coreplugin/mimetypesettings.cpp
new file mode 100644
index 0000000000..ebede48aa1
--- /dev/null
+++ b/src/plugins/coreplugin/mimetypesettings.cpp
@@ -0,0 +1,605 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** No Commercial Usage
+**
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "mimetypesettings.h"
+#include "ui_mimetypesettingspage.h"
+#include "mimetypemagicdialog.h"
+#include "mimedatabase.h"
+#include "coreconstants.h"
+#include "editormanager.h"
+#include "icore.h"
+#include "ieditorfactory.h"
+#include "iexternaleditor.h"
+
+#include <extensionsystem/pluginmanager.h>
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QLatin1String>
+#include <QtCore/QStringList>
+#include <QtCore/QVector>
+#include <QtCore/QSet>
+#include <QtCore/QScopedPointer>
+#include <QtCore/QAbstractTableModel>
+#include <QtCore/QHash>
+#include <QtGui/QIcon>
+#include <QtGui/QTableWidgetItem>
+#include <QtGui/QMessageBox>
+#include <QtAlgorithms>
+
+#include <algorithm>
+
+namespace Core {
+namespace Internal {
+
+struct MimeTypeComp
+{
+ bool operator()(const MimeType &a, const MimeType &b)
+ { return a.type().compare(b.type(), Qt::CaseInsensitive) < 0; }
+};
+
+// MimeTypeSettingsModel
+class MimeTypeSettingsModel : public QAbstractTableModel
+{
+ Q_OBJECT
+
+public:
+ MimeTypeSettingsModel(QObject *parent = 0)
+ : QAbstractTableModel(parent), m_core(ICore::instance()) {}
+ virtual ~MimeTypeSettingsModel() {}
+
+ virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ virtual int columnCount(const QModelIndex &parent = QModelIndex()) const;
+ virtual QVariant headerData(int section, Qt::Orientation orientation,
+ int role = Qt::DisplayRole) const;
+ virtual QVariant data(const QModelIndex &modelIndex, int role = Qt::DisplayRole) const;
+
+ void load();
+ void validatePatterns(QStringList *candidates, const MimeType &mimeType) const;
+ void updateKnownPatterns(const QStringList &oldPatterns, const QStringList &newPatterns);
+
+ ICore *m_core;
+ QList<MimeType> m_mimeTypes;
+ QSet<QString> m_knownPatterns;
+ QHash<QString, QString> m_handlersByMimeType;
+};
+
+int MimeTypeSettingsModel::rowCount(const QModelIndex &) const
+{
+ return m_mimeTypes.size();
+}
+
+int MimeTypeSettingsModel::columnCount(const QModelIndex &) const
+{
+ return 2;
+}
+
+QVariant MimeTypeSettingsModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (role != Qt::DisplayRole || orientation != Qt::Horizontal)
+ return QVariant();
+
+ if (section == 0)
+ return tr("MIME Type");
+ else
+ return tr("Handler");
+}
+
+QVariant MimeTypeSettingsModel::data(const QModelIndex &modelIndex, int role) const
+{
+ if (!modelIndex.isValid())
+ return QVariant();
+
+ const int column = modelIndex.column();
+ if (role == Qt::DisplayRole) {
+ const QString &type = m_mimeTypes.at(modelIndex.row()).type();
+ if (column == 0)
+ return type;
+ else
+ return m_handlersByMimeType.value(type);
+ } else if (role == Qt::TextAlignmentRole) {
+ if (column == 1)
+ return Qt::AlignCenter;
+ }
+ return QVariant();
+}
+
+void MimeTypeSettingsModel::load()
+{
+ m_mimeTypes = m_core->mimeDatabase()->mimeTypes();
+ qSort(m_mimeTypes.begin(), m_mimeTypes.end(), MimeTypeComp());
+ m_knownPatterns = QSet<QString>::fromList(
+ MimeDatabase::fromGlobPatterns(m_core->mimeDatabase()->globPatterns()));
+
+ foreach (const MimeType &mimeType, m_mimeTypes) {
+ QString value;
+ const QList<IEditorFactory *> factories =
+ m_core->editorManager()->editorFactories(mimeType);
+ if (!factories.isEmpty()) {
+ value = factories.front()->displayName();
+ } else {
+ const QList<IExternalEditor *> externalEditors =
+ m_core->editorManager()->externalEditors(mimeType);
+ if (!externalEditors.isEmpty())
+ value = externalEditors.front()->displayName();
+ else
+ value = tr("Undefined");
+ }
+ m_handlersByMimeType.insert(mimeType.type(), value);
+ }
+}
+
+void MimeTypeSettingsModel::validatePatterns(QStringList *candidates,
+ const MimeType &mimeType) const
+{
+ QSet<QString> oldPatterns =
+ QSet<QString>::fromList(MimeDatabase::fromGlobPatterns(mimeType.globPatterns()));
+
+ QStringList duplicates;
+ QStringList::iterator it = candidates->begin();
+ while (it != candidates->end()) {
+ const QString &current = *it;
+ if (!oldPatterns.contains(current) && m_knownPatterns.contains(current)) {
+ duplicates.append(current);
+ it = candidates->erase(it);
+ } else {
+ ++it;
+ }
+ }
+
+ if (!duplicates.isEmpty()) {
+ QMessageBox msgBox;
+ msgBox.setWindowTitle(tr("Invalid MIME Type"));
+ msgBox.setText(tr("Conflicting pattern(s) will be discarded."));
+ msgBox.setInformativeText(tr("%n pattern(s) already in use.", 0, duplicates.size()));
+ msgBox.setDetailedText(duplicates.join(QLatin1String("\n")));
+ msgBox.exec();
+ }
+}
+
+void MimeTypeSettingsModel::updateKnownPatterns(const QStringList &oldPatterns,
+ const QStringList &newPatterns)
+{
+ QStringList all = oldPatterns;
+ all.append(newPatterns);
+ all.removeDuplicates();
+ foreach (const QString &pattern, all) {
+ QSet<QString>::iterator it = m_knownPatterns.find(pattern);
+ if (it == m_knownPatterns.end()) {
+ // A pattern was added.
+ m_knownPatterns.insert(pattern);
+ } else {
+ // A pattern was removed.
+ m_knownPatterns.erase(it);
+ }
+ }
+}
+
+// MimeTypeSettingsPagePrivate
+class MimeTypeSettingsPrivate : public QObject
+{
+ Q_OBJECT
+
+public:
+ MimeTypeSettingsPrivate();
+ virtual ~MimeTypeSettingsPrivate();
+
+ void configureUi(QWidget *w);
+ static void configureTable(QTableView *tableView);
+
+ bool checkSelectedMimeType() const;
+ bool checkSelectedMagicHeader() const;
+
+ void markMimeForPatternSync(int index);
+ void markMimeForMagicSync(int index);
+ void syncMimePattern();
+ void syncMimeMagic();
+ void clearSyncData();
+ void markAsModified(int index);
+
+ void addMagicHeaderRow(const MagicData &data);
+ MagicData getMagicHeaderRowData(const int row) const;
+ void editMagicHeaderRowData(const int row, const MagicData &data);
+
+ void updateMimeDatabase();
+
+public slots:
+ void syncData(const QModelIndex &current, const QModelIndex &previous);
+ void handlePatternEdited();
+ void addMagicHeader();
+ void removeMagicHeader();
+ void editMagicHeader();
+ void resetMimeTypes();
+
+public:
+ static const QChar kSemiColon;
+
+ QString m_keywords;
+ MimeDatabase *m_mimeDatabase;
+ QScopedPointer<MimeTypeSettingsModel> m_model;
+ int m_mimeForPatternSync;
+ int m_mimeForMagicSync;
+ bool m_reset;
+ QList<int> m_modifiedMimeTypes;
+ Ui::MimeTypeSettingsPage m_ui;
+};
+
+const QChar MimeTypeSettingsPrivate::kSemiColon(QLatin1Char(';'));
+
+MimeTypeSettingsPrivate::MimeTypeSettingsPrivate()
+ : m_mimeDatabase(ICore::instance()->mimeDatabase())
+ , m_model(new MimeTypeSettingsModel)
+ , m_mimeForPatternSync(-1)
+ , m_mimeForMagicSync(-1)
+ , m_reset(false)
+{}
+
+MimeTypeSettingsPrivate::~MimeTypeSettingsPrivate()
+{}
+
+void MimeTypeSettingsPrivate::configureUi(QWidget *w)
+{
+ m_ui.setupUi(w);
+
+ m_model->load();
+ m_ui.mimeTypesTableView->setModel(m_model.data());
+
+ configureTable(m_ui.mimeTypesTableView);
+ configureTable(m_ui.magicHeadersTableWidget);
+
+ connect(m_ui.mimeTypesTableView->selectionModel(),
+ SIGNAL(currentChanged(QModelIndex,QModelIndex)),
+ this,
+ SLOT(syncData(QModelIndex,QModelIndex)));
+ connect(m_ui.patternsLineEdit, SIGNAL(textEdited(QString)),
+ this, SLOT(handlePatternEdited()));
+ connect(m_ui.addMagicButton, SIGNAL(clicked()), this, SLOT(addMagicHeader()));
+ connect(m_ui.removeMagicButton, SIGNAL(clicked()), this, SLOT(removeMagicHeader()));
+ connect(m_ui.editMagicButton, SIGNAL(clicked()), this, SLOT(editMagicHeader()));
+ connect(m_ui.resetButton, SIGNAL(clicked()), this, SLOT(resetMimeTypes()));
+}
+
+void MimeTypeSettingsPrivate::configureTable(QTableView *tableView)
+{
+ tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
+ tableView->setSelectionMode(QAbstractItemView::SingleSelection);
+ tableView->verticalHeader()->setVisible(false);
+ tableView->verticalHeader()->setDefaultSectionSize(20);
+ tableView->horizontalHeader()->setResizeMode(QHeaderView::Stretch);
+ tableView->horizontalHeader()->setResizeMode(0, QHeaderView::Interactive);
+ tableView->horizontalHeader()->resizeSection(
+ 0, 4 * tableView->horizontalHeader()->defaultSectionSize());
+ tableView->horizontalHeader()->setHighlightSections(false);
+ tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
+}
+
+bool MimeTypeSettingsPrivate::checkSelectedMimeType() const
+{
+ const QModelIndex &modelIndex = m_ui.mimeTypesTableView->selectionModel()->currentIndex();
+ if (!modelIndex.isValid()) {
+ QMessageBox::critical(0, tr("Error"), tr("No MIME type selected."));
+ return false;
+ }
+ return true;
+}
+
+bool MimeTypeSettingsPrivate::checkSelectedMagicHeader() const
+{
+ const QModelIndex &modelIndex = m_ui.magicHeadersTableWidget->selectionModel()->currentIndex();
+ if (!modelIndex.isValid()) {
+ QMessageBox::critical(0, tr("Error"), tr("No magic header selected."));
+ return false;
+ }
+ return true;
+}
+
+void MimeTypeSettingsPrivate::markMimeForPatternSync(int index)
+{
+ if (m_mimeForPatternSync != index) {
+ m_mimeForPatternSync = index;
+ markAsModified(index);
+ }
+}
+
+void MimeTypeSettingsPrivate::markMimeForMagicSync(int index)
+{
+ if (m_mimeForMagicSync != index) {
+ m_mimeForMagicSync = index;
+ markAsModified(index);
+ }
+}
+
+void MimeTypeSettingsPrivate::markAsModified(int index)
+{
+ // Duplicates are handled later.
+ m_modifiedMimeTypes.append(index);
+}
+
+void MimeTypeSettingsPrivate::syncMimePattern()
+{
+ MimeType &mimeType = m_model->m_mimeTypes[m_mimeForPatternSync];
+ QStringList patterns = m_ui.patternsLineEdit->text().split(kSemiColon);
+ patterns.removeDuplicates();
+ m_model->validatePatterns(&patterns, mimeType);
+ m_model->updateKnownPatterns(MimeDatabase::fromGlobPatterns(mimeType.globPatterns()), patterns);
+ mimeType.setGlobPatterns(MimeDatabase::toGlobPatterns(patterns));
+}
+
+void MimeTypeSettingsPrivate::syncMimeMagic()
+{
+ typedef MagicRuleMatcher::MagicRuleList MagicRuleList;
+ typedef MagicRuleMatcher::MagicRuleSharedPointer MagicRuleSharedPointer;
+
+ // Gather the magic rules.
+ QHash<int, MagicRuleList> rulesByPriority;
+ for (int row = 0; row < m_ui.magicHeadersTableWidget->rowCount(); ++row) {
+ const MagicData &data = getMagicHeaderRowData(row);
+ // @TODO: Validate magic rule?
+ MagicRule *magicRule;
+ if (data.m_type == MagicStringRule::kMatchType)
+ magicRule = new MagicStringRule(data.m_value, data.m_start, data.m_end);
+ else
+ magicRule = new MagicByteRule(data.m_value, data.m_start, data.m_end);
+ rulesByPriority[data.m_priority].append(MagicRuleSharedPointer(magicRule));
+ }
+
+ const QList<QSharedPointer<IMagicMatcher> > &matchers =
+ MagicRuleMatcher::createMatchers(rulesByPriority);
+ m_model->m_mimeTypes[m_mimeForMagicSync].setMagicRuleMatchers(matchers);
+}
+
+void MimeTypeSettingsPrivate::clearSyncData()
+{
+ m_mimeForPatternSync = -1;
+ m_mimeForMagicSync = -1;
+}
+
+void MimeTypeSettingsPrivate::syncData(const QModelIndex &current,
+ const QModelIndex &previous)
+{
+ if (previous.isValid()) {
+ if (m_mimeForPatternSync == previous.row())
+ syncMimePattern();
+ if (m_mimeForMagicSync == previous.row())
+ syncMimeMagic();
+ clearSyncData();
+
+ m_ui.patternsLineEdit->clear();
+ m_ui.magicHeadersTableWidget->clearContents();
+ m_ui.magicHeadersTableWidget->setRowCount(0);
+ }
+
+ if (current.isValid()) {
+ const MimeType &currentMimeType = m_model->m_mimeTypes.at(current.row());
+
+ QStringList formatedPatterns;
+ foreach (const MimeGlobPattern &pattern, currentMimeType.globPatterns())
+ formatedPatterns.append(pattern.regExp().pattern());
+ m_ui.patternsLineEdit->setText(formatedPatterns.join(kSemiColon));
+
+ // Consider only rule-based matchers.
+ const QList<QSharedPointer<IMagicMatcher> > &matchers = currentMimeType.magicRuleMatchers();
+ foreach (const QSharedPointer<IMagicMatcher> &matcher, matchers) {
+ MagicRuleMatcher *ruleMatcher = static_cast<MagicRuleMatcher *>(matcher.data());
+ const int priority = ruleMatcher->priority();
+ const MagicRuleMatcher::MagicRuleList &rules = ruleMatcher->magicRules();
+ foreach (const MagicRuleMatcher::MagicRuleSharedPointer &rule, rules)
+ addMagicHeaderRow(MagicData(rule->matchValue(),
+ rule->matchType(),
+ rule->startPos(),
+ rule->endPos(),
+ priority));
+ }
+ }
+}
+
+void MimeTypeSettingsPrivate::handlePatternEdited()
+{
+ if (m_mimeForPatternSync == -1) {
+ const QModelIndex &modelIndex = m_ui.mimeTypesTableView->selectionModel()->currentIndex();
+ if (modelIndex.isValid())
+ markMimeForPatternSync(modelIndex.row());
+ }
+}
+
+void MimeTypeSettingsPrivate::addMagicHeaderRow(const MagicData &data)
+{
+ const int row = m_ui.magicHeadersTableWidget->rowCount();
+ m_ui.magicHeadersTableWidget->insertRow(row);
+ editMagicHeaderRowData(row, data);
+}
+
+MagicData MimeTypeSettingsPrivate::getMagicHeaderRowData(const int row) const
+{
+ MagicData data;
+ data.m_value = m_ui.magicHeadersTableWidget->item(row, 0)->text();
+ data.m_type = m_ui.magicHeadersTableWidget->item(row, 1)->text();
+ QPair<int, int> startEnd =
+ MagicRule::fromOffset(m_ui.magicHeadersTableWidget->item(row, 2)->text());
+ data.m_start = startEnd.first;
+ data.m_end = startEnd.second;
+ data.m_priority = m_ui.magicHeadersTableWidget->item(row, 3)->text().toInt();
+
+ return data;
+}
+
+void MimeTypeSettingsPrivate::editMagicHeaderRowData(const int row, const MagicData &data)
+{
+ for (int col = 0; col < m_ui.magicHeadersTableWidget->columnCount(); ++col) {
+ QTableWidgetItem *item = new QTableWidgetItem;
+ if (col == 0) {
+ item->setText(data.m_value);
+ } else {
+ item->setTextAlignment(Qt::AlignCenter);
+ if (col == 1)
+ item->setText(data.m_type);
+ else if (col == 2)
+ item->setText(MagicRule::toOffset(qMakePair(data.m_start, data.m_end)));
+ else
+ item->setText(QString::number(data.m_priority));
+ }
+ m_ui.magicHeadersTableWidget->setItem(row, col, item);
+ }
+}
+
+void MimeTypeSettingsPrivate::addMagicHeader()
+{
+ if (!checkSelectedMimeType())
+ return;
+
+ MimeTypeMagicDialog dlg;
+ if (dlg.exec()) {
+ addMagicHeaderRow(dlg.magicData());
+ markMimeForMagicSync(m_ui.mimeTypesTableView->selectionModel()->currentIndex().row());
+ }
+}
+
+void MimeTypeSettingsPrivate::removeMagicHeader()
+{
+ if (!checkSelectedMagicHeader())
+ return;
+
+ m_ui.magicHeadersTableWidget->removeRow(m_ui.magicHeadersTableWidget->currentRow());
+ markMimeForMagicSync(m_ui.mimeTypesTableView->selectionModel()->currentIndex().row());
+}
+
+void MimeTypeSettingsPrivate::editMagicHeader()
+{
+ if (!checkSelectedMagicHeader())
+ return;
+
+ MimeTypeMagicDialog dlg;
+ dlg.setMagicData(getMagicHeaderRowData(m_ui.magicHeadersTableWidget->currentRow()));
+ if (dlg.exec()) {
+ editMagicHeaderRowData(m_ui.magicHeadersTableWidget->currentRow(), dlg.magicData());
+ markMimeForMagicSync(m_ui.mimeTypesTableView->selectionModel()->currentIndex().row());
+ }
+}
+
+void MimeTypeSettingsPrivate::updateMimeDatabase()
+{
+ MimeDatabase *db = ICore::instance()->mimeDatabase();
+ // For this case it is a better approach to simply use a list and to remove duplicates
+ // afterwards than to keep a more complex data structure like a hash table.
+ qSort(m_modifiedMimeTypes.begin(), m_modifiedMimeTypes.end());
+ m_modifiedMimeTypes.erase(std::unique(m_modifiedMimeTypes.begin(), m_modifiedMimeTypes.end()),
+ m_modifiedMimeTypes.end());
+ if (!m_modifiedMimeTypes.isEmpty()) {
+ QList<MimeType> allModified;
+ foreach (int index, m_modifiedMimeTypes) {
+ const MimeType &mimeType = m_model->m_mimeTypes.at(index);
+ db->setGlobPatterns(mimeType.type(), mimeType.globPatterns());
+ db->setMagicMatchers(mimeType.type(), mimeType.magicMatchers());
+ allModified.append(mimeType);
+ }
+ db->writeUserModifiedMimeTypes(allModified);
+ }
+}
+
+void MimeTypeSettingsPrivate::resetMimeTypes()
+{
+ QMessageBox::information(0,
+ tr("MIME Types"),
+ tr("Changes will take effect in the next time you start Qt Creator."));
+ m_reset = true;
+}
+
+// MimeTypeSettingsPage
+MimeTypeSettings::MimeTypeSettings(QObject *parent)
+ : IOptionsPage(parent)
+ , m_d(new MimeTypeSettingsPrivate)
+{}
+
+MimeTypeSettings::~MimeTypeSettings()
+{}
+
+QString MimeTypeSettings::id() const
+{
+ return QLatin1String(Core::Constants::SETTINGS_ID_MIMETYPES);
+}
+
+QString MimeTypeSettings::displayName() const
+{
+ return tr("MIME Types");
+}
+
+QString MimeTypeSettings::category() const
+{
+ return QLatin1String(Core::Constants::SETTINGS_CATEGORY_CORE);
+}
+
+QString MimeTypeSettings::displayCategory() const
+{
+ return QCoreApplication::translate("Core", Core::Constants::SETTINGS_TR_CATEGORY_CORE);
+}
+
+QIcon MimeTypeSettings::categoryIcon() const
+{
+ return QIcon(QLatin1String(Core::Constants::SETTINGS_CATEGORY_CORE_ICON));
+}
+
+bool MimeTypeSettings::matches(const QString &s) const
+{
+ return m_d->m_keywords.contains(s, Qt::CaseInsensitive);
+}
+
+QWidget *MimeTypeSettings::createPage(QWidget *parent)
+{
+ QWidget *w = new QWidget(parent);
+ m_d->configureUi(w);
+ return w;
+}
+
+void MimeTypeSettings::apply()
+{
+ if (m_d->m_reset) {
+ ICore::instance()->mimeDatabase()->clearUserModifiedMimeTypes();
+ } else if (!m_d->m_modifiedMimeTypes.isEmpty()) {
+ const QModelIndex &modelIndex =
+ m_d->m_ui.mimeTypesTableView->selectionModel()->currentIndex();
+ if (modelIndex.isValid()) {
+ if (m_d->m_mimeForPatternSync == modelIndex.row())
+ m_d->syncMimePattern();
+ if (m_d->m_mimeForMagicSync == modelIndex.row())
+ m_d->syncMimeMagic();
+ }
+ m_d->updateMimeDatabase();
+ }
+}
+
+void MimeTypeSettings::finish()
+{}
+
+} // Internal
+} // Core
+
+#include "mimetypesettings.moc"
diff --git a/src/plugins/coreplugin/mimetypesettings.h b/src/plugins/coreplugin/mimetypesettings.h
new file mode 100644
index 0000000000..6faa93ccd3
--- /dev/null
+++ b/src/plugins/coreplugin/mimetypesettings.h
@@ -0,0 +1,72 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** No Commercial Usage
+**
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef MIMETYPESETTINGSPAGE_H
+#define MIMETYPESETTINGSPAGE_H
+
+#include "ioptionspage.h"
+
+#include <QtCore/QScopedPointer>
+
+namespace Core {
+namespace Internal {
+
+class MimeTypeSettingsPrivate;
+
+class MimeTypeSettings : public IOptionsPage
+{
+ Q_OBJECT
+
+public:
+ MimeTypeSettings(QObject *parent = 0);
+ virtual ~MimeTypeSettings();
+
+ virtual QString id() const;
+ virtual QString displayName() const;
+ virtual QString category() const;
+ virtual QString displayCategory() const;
+ virtual QIcon categoryIcon() const;
+ virtual bool matches(const QString &s) const;
+
+ virtual QWidget *createPage(QWidget *parent);
+ virtual void apply();
+ virtual void finish();
+
+private:
+ QScopedPointer<MimeTypeSettingsPrivate> m_d;
+};
+
+} // Internal
+} // Core
+
+#endif // MIMETYPESETTINGSPAGE_H
diff --git a/src/plugins/coreplugin/mimetypesettingspage.ui b/src/plugins/coreplugin/mimetypesettingspage.ui
new file mode 100644
index 0000000000..5a0c497df1
--- /dev/null
+++ b/src/plugins/coreplugin/mimetypesettingspage.ui
@@ -0,0 +1,169 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MimeTypeSettingsPage</class>
+ <widget class="QWidget" name="MimeTypeSettingsPage">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>666</width>
+ <height>407</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <widget class="QTableView" name="mimeTypesTableView"/>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="detailsGroupBox">
+ <property name="title">
+ <string>Details</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLabel" name="patternsLabel">
+ <property name="text">
+ <string>Patterns:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="patternsLineEdit"/>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QTableWidget" name="magicHeadersTableWidget">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>16777215</width>
+ <height>100</height>
+ </size>
+ </property>
+ <column>
+ <property name="text">
+ <string>Magic Header</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Type</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Range</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Priority</string>
+ </property>
+ </column>
+ </widget>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QPushButton" name="addMagicButton">
+ <property name="text">
+ <string>Add</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="editMagicButton">
+ <property name="text">
+ <string>Edit</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="removeMagicButton">
+ <property name="text">
+ <string>Remove</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>72</width>
+ <height>18</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="0" column="1">
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QPushButton" name="resetButton">
+ <property name="toolTip">
+ <string>Reset all to default</string>
+ </property>
+ <property name="text">
+ <string>Reset All</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>18</width>
+ <height>17</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="0">
+ <spacer name="verticalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>504</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/plugins/texteditor/generichighlighter/manager.cpp b/src/plugins/texteditor/generichighlighter/manager.cpp
index 16388f629a..ca68aaf5a4 100644
--- a/src/plugins/texteditor/generichighlighter/manager.cpp
+++ b/src/plugins/texteditor/generichighlighter/manager.cpp
@@ -184,6 +184,11 @@ void Manager::gatherDefinitionsMimeTypes(QFutureInterface<Core::MimeType> &futur
Core::MimeDatabase *mimeDatabase = Core::ICore::instance()->mimeDatabase();
QSet<QString> knownSuffixes = QSet<QString>::fromList(mimeDatabase->suffixes());
+ QHash<QString, Core::MimeType> userModified;
+ const QList<Core::MimeType> &userMimeTypes = mimeDatabase->readUserModifiedMimeTypes();
+ foreach (const Core::MimeType &userMimeType, userMimeTypes)
+ userModified.insert(userMimeType.type(), userMimeType);
+
foreach (const QString &path, definitionsPaths) {
if (path.isEmpty())
continue;
@@ -227,25 +232,35 @@ void Manager::gatherDefinitionsMimeTypes(QFutureInterface<Core::MimeType> &futur
m_idByMimeType.insert(type, id);
Core::MimeType mimeType = mimeDatabase->findByType(type);
if (mimeType.isNull()) {
- if (globPatterns.isEmpty()) {
- foreach (const QString &pattern, metaData->patterns()) {
- static const QLatin1String mark("*.");
- if (pattern.startsWith(mark)) {
- const QString &suffix = pattern.right(pattern.length() - 2);
- if (!knownSuffixes.contains(suffix))
- knownSuffixes.insert(suffix);
- else
- continue;
- }
- QRegExp regExp(pattern, Qt::CaseSensitive, QRegExp::Wildcard);
- globPatterns.append(Core::MimeGlobPattern(regExp, 50));
- }
- }
-
mimeType.setType(type);
mimeType.setSubClassesOf(textPlain);
mimeType.setComment(metaData->name());
- mimeType.setGlobPatterns(globPatterns);
+
+ // If there's a user modification for this mime type, we want to use the
+ // modified patterns and rule-based matchers. If not, just consider what
+ // is specified in the definition file.
+ QHash<QString, Core::MimeType>::const_iterator it =
+ userModified.find(mimeType.type());
+ if (it == userModified.end()) {
+ if (globPatterns.isEmpty()) {
+ foreach (const QString &pattern, metaData->patterns()) {
+ static const QLatin1String mark("*.");
+ if (pattern.startsWith(mark)) {
+ const QString &suffix = pattern.right(pattern.length() - 2);
+ if (!knownSuffixes.contains(suffix))
+ knownSuffixes.insert(suffix);
+ else
+ continue;
+ }
+ QRegExp regExp(pattern, Qt::CaseSensitive, QRegExp::Wildcard);
+ globPatterns.append(Core::MimeGlobPattern(regExp, 50));
+ }
+ }
+ mimeType.setGlobPatterns(globPatterns);
+ } else {
+ mimeType.setGlobPatterns(it.value().globPatterns());
+ mimeType.setMagicRuleMatchers(it.value().magicRuleMatchers());
+ }
mimeDatabase->addMimeType(mimeType);
future.reportResult(mimeType);
@@ -275,6 +290,8 @@ void Manager::registerMimeTypesFinished()
QSharedPointer<HighlightDefinitionMetaData> Manager::parseMetadata(const QFileInfo &fileInfo)
{
static const QLatin1Char kSemiColon(';');
+ static const QLatin1Char kSpace(' ');
+ static const QLatin1Char kDash('-');
static const QLatin1String kLanguage("language");
static const QLatin1String kArtificial("text/x-artificial-");
@@ -304,7 +321,7 @@ QSharedPointer<HighlightDefinitionMetaData> Manager::parseMetadata(const QFileIn
// There are definitions which do not specify a MIME type, but specify file
// patterns. Creating an artificial MIME type is a workaround.
QString artificialType(kArtificial);
- artificialType.append(metaData->name());
+ artificialType.append(metaData->name().trimmed().replace(kSpace, kDash));
mimeTypes.append(artificialType);
}
metaData->setMimeTypes(mimeTypes);