Generic highlighter: Interface for automatic download of available definitions. Refactorings and changes in related parts of the code.

This commit is contained in:
Leandro Melo
2010-06-11 12:13:52 +02:00
parent 90cb7df5be
commit ce80ee4f5e
16 changed files with 948 additions and 145 deletions

View File

@@ -0,0 +1,94 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 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.
**
**************************************************************************/
#include "definitiondownloader.h"
#include <QtCore/QLatin1Char>
#include <QtCore/QEventLoop>
#include <QtCore/QFile>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkRequest>
#include <QtNetwork/QNetworkReply>
using namespace TextEditor;
using namespace Internal;
DefinitionDownloader::DefinitionDownloader(const QUrl &url, const QString &localPath) :
m_url(url), m_localPath(localPath)
{}
void DefinitionDownloader::start()
{
QNetworkReply *reply;
QNetworkAccessManager manager;
int currentAttempt = 0;
const int maxAttempts = 5;
while (currentAttempt < maxAttempts) {
reply = getData(&manager);
if (reply->error() != QNetworkReply::NoError)
break;
++currentAttempt;
QVariant variant = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
if (variant.isValid() && currentAttempt < maxAttempts) {
m_url = variant.toUrl();
delete reply;
} else if (!variant.isValid()) {
saveData(reply);
break;
}
}
delete reply;
}
QNetworkReply *DefinitionDownloader::getData(QNetworkAccessManager *manager) const
{
QNetworkRequest request(m_url);
QNetworkReply *reply = manager->get(request);
QEventLoop eventLoop;
connect(reply, SIGNAL(finished()), &eventLoop, SLOT(quit()));
eventLoop.exec();
return reply;
}
void DefinitionDownloader::saveData(QNetworkReply *reply) const
{
const QString &urlPath = m_url.path();
const QString &fileName =
urlPath.right(urlPath.length() - urlPath.lastIndexOf(QLatin1Char('/')) - 1);
QFile file(m_localPath + fileName);
if (!file.open(QIODevice::Text | QIODevice::WriteOnly))
return;
file.write(reply->readAll());
file.close();
}

View File

@@ -0,0 +1,75 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 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.
**
**************************************************************************/
#ifndef DEFINITIONDOWNLOADER_H
#define DEFINITIONDOWNLOADER_H
#include <QtCore/QObject>
#include <QtCore/QString>
#include <QtCore/QUrl>
QT_BEGIN_NAMESPACE
class QNetworkReply;
class QNetworkAccessManager;
QT_END_NAMESPACE
namespace TextEditor {
namespace Internal {
class DefinitionDownloader : public QObject
{
Q_OBJECT
public:
DefinitionDownloader(const QUrl &url, const QString &localPath);
void start();
private:
QNetworkReply *getData(QNetworkAccessManager *manager) const;
void saveData(QNetworkReply *reply) const;
QUrl m_url;
QString m_localPath;
};
// Currently QtConcurrent::map does not support passing member functions for sequence of pointers
// (only works for operator.*) which is the case for the downloaders held by the manager. Then the
// reason for the following functor. If something is implemented (for example a type traits) to
// handle operator->* in QtConcurrent::map this functor will not be necessary since it would be
// possible to directly pass DefinitionDownloader::start.
struct DownloaderStarter
{
void operator()(DefinitionDownloader *downloader)
{ downloader->start(); }
};
} // namespace Internal
} // namespace TextEditor
#endif // DEFINITIONDOWNLOADER_H

View File

@@ -437,6 +437,9 @@ void HighlightDefinitionHandler::processIncludeRules(const QSharedPointer<Contex
const QSharedPointer<HighlightDefinition> &externalDefinition =
Manager::instance()->definition(id);
if (externalDefinition.isNull())
continue;
sourceContext = externalDefinition->initialContext();
} else if (!sourceName.startsWith(kHash)) {
sourceContext = m_definition->context(sourceName);
@@ -444,6 +447,8 @@ void HighlightDefinitionHandler::processIncludeRules(const QSharedPointer<Contex
// Recursion is done only for context direct rules. Child rules are not processed
// because they cannot be include rules.
processIncludeRules(sourceContext);
} else {
continue;
}
if (instruction.replaceItemData()) {

View File

@@ -0,0 +1,72 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 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.
**
**************************************************************************/
#include "highlightdefinitionmetadata.h"
using namespace TextEditor;
using namespace Internal;
const QLatin1String HighlightDefinitionMetaData::kName("name");
const QLatin1String HighlightDefinitionMetaData::kExtensions("extensions");
const QLatin1String HighlightDefinitionMetaData::kMimeType("mimetype");
const QLatin1String HighlightDefinitionMetaData::kVersion("version");
const QLatin1String HighlightDefinitionMetaData::kUrl("url");
HighlightDefinitionMetaData::HighlightDefinitionMetaData()
{}
void HighlightDefinitionMetaData::setName(const QString &name)
{ m_name = name; }
const QString &HighlightDefinitionMetaData::name() const
{ return m_name; }
void HighlightDefinitionMetaData::setVersion(const QString &version)
{ m_version = version; }
const QString &HighlightDefinitionMetaData::version() const
{ return m_version; }
void HighlightDefinitionMetaData::setPatterns(const QStringList &patterns)
{ m_patterns = patterns; }
const QStringList &HighlightDefinitionMetaData::patterns() const
{ return m_patterns; }
void HighlightDefinitionMetaData::setMimeTypes(const QStringList &mimeTypes)
{ m_mimeTypes = mimeTypes; }
const QStringList &HighlightDefinitionMetaData::mimeTypes() const
{ return m_mimeTypes; }
void HighlightDefinitionMetaData::setUrl(const QUrl &url)
{ m_url = url; }
const QUrl &HighlightDefinitionMetaData::url() const
{ return m_url; }

View File

@@ -0,0 +1,78 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 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.
**
**************************************************************************/
#ifndef HIGHLIGHTDEFINITIONMETADATA_H
#define HIGHLIGHTDEFINITIONMETADATA_H
#include <QtCore/QString>
#include <QtCore/QLatin1String>
#include <QtCore/QStringList>
#include <QtCore/QUrl>
namespace TextEditor {
namespace Internal {
class HighlightDefinitionMetaData
{
public:
HighlightDefinitionMetaData();
void setName(const QString &name);
const QString &name() const;
void setVersion(const QString &version);
const QString &version() const;
void setPatterns(const QStringList &patterns);
const QStringList &patterns() const;
void setMimeTypes(const QStringList &mimeTypes);
const QStringList &mimeTypes() const;
void setUrl(const QUrl &url);
const QUrl &url() const;
static const QLatin1String kName;
static const QLatin1String kExtensions;
static const QLatin1String kMimeType;
static const QLatin1String kVersion;
static const QLatin1String kUrl;
private:
QString m_name;
QString m_version;
QStringList m_patterns;
QStringList m_mimeTypes;
QUrl m_url;
};
} // namespace Internal
} // namespace TextEditor
#endif // HIGHLIGHTDEFINITIONMETADATA_H

View File

@@ -30,11 +30,14 @@
#include "highlightersettingspage.h"
#include "highlightersettings.h"
#include "manager.h"
#include "managedefinitionsdialog.h"
#include "ui_highlightersettingspage.h"
#include <coreplugin/icore.h>
#include <coreplugin/coreconstants.h>
#include <QtGui/QMessageBox>
using namespace TextEditor;
using namespace Internal;
@@ -102,8 +105,9 @@ QWidget *HighlighterSettingsPage::createPage(QWidget *parent)
}
connect(m_d->m_page.resetButton, SIGNAL(clicked()), this, SLOT(resetDefinitionsLocation()));
connect(m_d->m_page.downloadNoteLabel, SIGNAL(linkActivated(QString)),
Manager::instance(), SLOT(openDefinitionsUrl(QString)));
connect(m_d->m_page.manageDefinitionsButton, SIGNAL(clicked()),
this, SLOT(requestAvailableDefinitionsMetaData()));
connect(w, SIGNAL(destroyed()), this, SLOT(ignoreDownloadReply()));
return w;
}
@@ -152,6 +156,45 @@ void HighlighterSettingsPage::resetDefinitionsLocation()
m_d->m_page.definitionFilesPath->setPath(findDefinitionsLocation());
}
void HighlighterSettingsPage::requestAvailableDefinitionsMetaData()
{
m_d->m_page.manageDefinitionsButton->setEnabled(false);
Manager::instance()->downloadAvailableDefinitionsMetaData();
connect(Manager::instance(),
SIGNAL(definitionsMetaDataReady(QList<Internal::HighlightDefinitionMetaData>)),
this,
SLOT(manageDefinitions(QList<Internal::HighlightDefinitionMetaData>)),
Qt::UniqueConnection);
connect(Manager::instance(), SIGNAL(errorDownloadingDefinitionsMetaData()),
this, SLOT(showError()), Qt::UniqueConnection);
}
void HighlighterSettingsPage::ignoreDownloadReply()
{
disconnect(Manager::instance(),
SIGNAL(definitionsMetaDataReady(QList<Internal::HighlightDefinitionMetaData>)),
this,
SLOT(manageDefinitions(QList<Internal::HighlightDefinitionMetaData>)));
disconnect(Manager::instance(), SIGNAL(errorDownloadingDefinitionsMetaData()),
this, SLOT(showError()));
}
void HighlighterSettingsPage::manageDefinitions(const QList<HighlightDefinitionMetaData> &metaData)
{
ManageDefinitionsDialog dialog(metaData, m_d->m_page.manageDefinitionsButton->window());
dialog.exec();
m_d->m_page.manageDefinitionsButton->setEnabled(true);
}
void HighlighterSettingsPage::showError()
{
QMessageBox::critical(m_d->m_page.manageDefinitionsButton->window(),
tr("Error connecting to server."),
tr("Not possible to retrieve data."));
m_d->m_page.manageDefinitionsButton->setEnabled(true);
}
bool HighlighterSettingsPage::settingsChanged() const
{
if (m_d->m_settings.definitionFilesPath() != m_d->m_page.definitionFilesPath->path())

View File

@@ -32,8 +32,16 @@
#include "texteditoroptionspage.h"
QT_BEGIN_NAMESPACE
template <class> class QList;
QT_END_NAMESPACE
namespace TextEditor {
namespace Internal {
class HighlightDefinitionMetaData;
}
class HighlighterSettings;
class HighlighterSettingsPage : public TextEditorOptionsPage
@@ -58,6 +66,10 @@ signals:
private slots:
void resetDefinitionsLocation();
void requestAvailableDefinitionsMetaData();
void manageDefinitions(const QList<Internal::HighlightDefinitionMetaData> &metaData);
void showError();
void ignoreDownloadReply();
private:
void settingsFromUI();

View File

@@ -21,22 +21,7 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="downloadNoteLabel">
<property name="text">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Definitions can be downloaded from the &lt;/span&gt;&lt;a href=&quot;http://kate-editor.org/downloads/syntax_highlighting&quot;&gt;&lt;span style=&quot; font-size:8pt; text-decoration: underline; color:#0000ff;&quot;&gt;Kate Text Editor&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; website and stored at the location below.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="textInteractionFlags">
<set>Qt::TextBrowserInteraction</set>
</property>
</widget>
</item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
@@ -49,6 +34,8 @@ p, li { white-space: pre-wrap; }
<item>
<widget class="Utils::PathChooser" name="definitionFilesPath" native="true"/>
</item>
</layout>
</item>
<item>
<widget class="QToolButton" name="resetButton">
<property name="toolTip">
@@ -65,6 +52,59 @@ p, li { white-space: pre-wrap; }
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="definitionsInfolabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Highlight definitions are available from the &lt;/span&gt;&lt;a href=&quot;http://kate-editor.org/downloads/syntax_highlighting?kateversion=3.2&quot;&gt;&lt;span style=&quot; font-size:8pt; text-decoration: underline; color:#0000ff;&quot;&gt;Kate Text Editor&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; website. Qt Creator can download a list of available definitions and then install the selected ones.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QPushButton" name="manageDefinitionsButton">
<property name="text">
<string>Manage Definitions</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>328</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
@@ -77,7 +117,7 @@ p, li { white-space: pre-wrap; }
<item>
<widget class="QCheckBox" name="alertWhenNoDefinition">
<property name="text">
<string>Alert when a highlight definition is not found.</string>
<string>Alert when a highlight definition is not found</string>
</property>
</widget>
</item>
@@ -125,6 +165,12 @@ p, li { white-space: pre-wrap; }
</slots>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>resetButton</tabstop>
<tabstop>manageDefinitionsButton</tabstop>
<tabstop>alertWhenNoDefinition</tabstop>
<tabstop>ignoreEdit</tabstop>
</tabstops>
<resources>
<include location="../../coreplugin/core.qrc"/>
</resources>

View File

@@ -0,0 +1,121 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 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.
**
**************************************************************************/
#include "managedefinitionsdialog.h"
#include "manager.h"
#include <QtCore>
#include <QtCore/QUrl>
#include <QtCore/QIODevice>
#include <QtCore/QXmlStreamReader>
#include <QtCore/QXmlStreamAttributes>
#include <QtCore/QFuture>
#include <QtCore/QFutureWatcher>
#include <QtCore/QtConcurrentMap>
#include <QtGui/QMessageBox>
#include <QDebug>
using namespace TextEditor;
using namespace Internal;
ManageDefinitionsDialog::ManageDefinitionsDialog(
const QList<HighlightDefinitionMetaData> &metaDataList, QWidget *parent) :
m_definitionsMetaData(metaDataList), QDialog(parent)
{
ui.setupUi(this);
ui.definitionsTable->setHorizontalHeaderLabels(
QStringList() << tr("Definition Name") << tr("Installed") << tr("Available"));
ui.definitionsTable->horizontalHeader()->setResizeMode(0, QHeaderView::Stretch);
setWindowTitle(tr("Manage Definitions"));
populateDefinitionsWidget();
connect(ui.downloadButton, SIGNAL(clicked()), this, SLOT(downloadDefinitions()));
}
void ManageDefinitionsDialog::populateDefinitionsWidget()
{
const int size = m_definitionsMetaData.size();
ui.definitionsTable->setRowCount(size);
for (int i = 0; i < size; ++i) {
const HighlightDefinitionMetaData &downloadData = m_definitionsMetaData.at(i);
QString installedVersion;
const QString &id = Manager::instance()->definitionIdByName(downloadData.name());
const QSharedPointer<HighlightDefinitionMetaData> &metaData =
Manager::instance()->definitionMetaData(id);
if (!metaData.isNull())
installedVersion = metaData->version();
for (int j = 0; j < 3; ++j) {
QTableWidgetItem *item = new QTableWidgetItem;
if (j == 0)
item->setText(downloadData.name());
else if (j == 1) {
item->setText(installedVersion);
item->setTextAlignment(Qt::AlignCenter);
} else if (j == 2) {
item->setText(downloadData.version());
item->setTextAlignment(Qt::AlignCenter);
}
ui.definitionsTable->setItem(i, j, item);
}
}
}
void ManageDefinitionsDialog::downloadDefinitions()
{
if (Manager::instance()->isDownloadingDefinitions()) {
QMessageBox::information(
this,
tr("Download Information"),
tr("There is already one download in progress. Please wait until it is finished."));
return;
}
QList<QUrl> urls;
foreach (const QModelIndex &index, ui.definitionsTable->selectionModel()->selectedRows())
urls.append(m_definitionsMetaData.at(index.row()).url());
Manager::instance()->downloadDefinitions(urls);
close();
}
void ManageDefinitionsDialog::changeEvent(QEvent *e)
{
QDialog::changeEvent(e);
switch (e->type()) {
case QEvent::LanguageChange:
ui.retranslateUi(this);
break;
default:
break;
}
}

View File

@@ -0,0 +1,64 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 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.
**
**************************************************************************/
#ifndef MANAGEDEFINITIONSDIALOG_H
#define MANAGEDEFINITIONSDIALOG_H
#include "ui_managedefinitionsdialog.h"
#include "highlightdefinitionmetadata.h"
#include <QtCore/QList>
namespace TextEditor {
namespace Internal {
class ManageDefinitionsDialog : public QDialog
{
Q_OBJECT
public:
explicit ManageDefinitionsDialog(const QList<HighlightDefinitionMetaData> &metaDataList,
QWidget *parent = 0);
protected:
void changeEvent(QEvent *e);
private slots:
void downloadDefinitions();
private:
void populateDefinitionsWidget();
QList<HighlightDefinitionMetaData> m_definitionsMetaData;
Ui::ManageDefinitionsDialog ui;
};
} // namespace Internal
} // namespace TextEditor
#endif // MANAGEDEFINITIONSDIALOG_H

View File

@@ -0,0 +1,117 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ManageDefinitionsDialog</class>
<widget class="QDialog" name="ManageDefinitionsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>447</width>
<height>315</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QGroupBox" name="definitionsGroupBox">
<property name="title">
<string>Available Definitions</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTableWidget" name="definitionsTable">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="columnCount">
<number>3</number>
</property>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderDefaultSectionSize">
<number>20</number>
</attribute>
<column/>
<column/>
<column/>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="downloadButton">
<property name="text">
<string>Download Selected Definitions</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>188</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>298</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="closeButton">
<property name="text">
<string>Close</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>closeButton</sender>
<signal>clicked()</signal>
<receiver>ManageDefinitionsDialog</receiver>
<slot>close()</slot>
<hints>
<hint type="sourcelabel">
<x>381</x>
<y>294</y>
</hint>
<hint type="destinationlabel">
<x>306</x>
<y>298</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@@ -31,14 +31,16 @@
#include "highlightdefinition.h"
#include "highlightdefinitionhandler.h"
#include "highlighterexception.h"
#include "definitiondownloader.h"
#include "highlightersettings.h"
#include "plaintexteditorfactory.h"
#include "texteditorconstants.h"
#include "texteditorplugin.h"
#include "texteditorsettings.h"
#include "plaintexteditorfactory.h"
#include "highlightersettings.h"
#include "texteditorconstants.h"
#include <coreplugin/icore.h>
#include <utils/qtcassert.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <qtconcurrent/QtConcurrentTools>
#include <QtCore/QtAlgorithms>
@@ -50,22 +52,27 @@
#include <QtCore/QFile>
#include <QtCore/QFileInfo>
#include <QtCore/QDir>
#include <QtCore/QList>
#include <QtCore/QRegExp>
#include <QtCore/QFuture>
#include <QtCore/QtConcurrentRun>
#include <QtCore/QtConcurrentMap>
#include <QtCore/QUrl>
#include <QtGui/QDesktopServices>
#include <QtXml/QXmlSimpleReader>
#include <QtXml/QXmlInputSource>
#include <QtXml/QXmlStreamReader>
#include <QtXml/QXmlStreamAttributes>
#include <QtNetwork/QNetworkRequest>
#include <QtNetwork/QNetworkReply>
using namespace TextEditor;
using namespace Internal;
Manager::Manager()
{}
Manager::Manager() : m_downloadingDefinitions(false)
{
connect(&m_mimeTypeWatcher, SIGNAL(resultReadyAt(int)), this, SLOT(registerMimeType(int)));
connect(&m_downloadWatcher, SIGNAL(finished()), this, SLOT(downloadDefinitionsFinished()));
}
Manager::~Manager()
{}
@@ -80,19 +87,7 @@ QString Manager::definitionIdByName(const QString &name) const
{ return m_idByName.value(name); }
QString Manager::definitionIdByMimeType(const QString &mimeType) const
{
if (m_idByMimeType.count(mimeType) > 1) {
QStringList candidateIds;
QMultiHash<QString, QString>::const_iterator it = m_idByMimeType.find(mimeType);
QMultiHash<QString, QString>::const_iterator endIt = m_idByMimeType.end();
for (; it != endIt && it.key() == mimeType; ++it)
candidateIds.append(it.value());
qSort(candidateIds.begin(), candidateIds.end(), m_priorityComp);
return candidateIds.last();
}
return m_idByMimeType.value(mimeType);
}
{ return m_idByMimeType.value(mimeType); }
QString Manager::definitionIdByAnyMimeType(const QStringList &mimeTypes) const
{
@@ -105,31 +100,37 @@ QString Manager::definitionIdByAnyMimeType(const QStringList &mimeTypes) const
return definitionId;
}
const QSharedPointer<HighlightDefinition> &Manager::definition(const QString &id)
QSharedPointer<HighlightDefinition> Manager::definition(const QString &id)
{
if (!m_definitions.contains(id)) {
m_isBuilding.insert(id);
if (!id.isEmpty() && !m_definitions.contains(id)) {
QFile definitionFile(id);
if (!definitionFile.open(QIODevice::ReadOnly | QIODevice::Text))
throw HighlighterException();
QXmlInputSource source(&definitionFile);
return QSharedPointer<HighlightDefinition>();
QSharedPointer<HighlightDefinition> definition(new HighlightDefinition);
HighlightDefinitionHandler handler(definition);
QXmlInputSource source(&definitionFile);
QXmlSimpleReader reader;
reader.setContentHandler(&handler);
m_isBuilding.insert(id);
try {
reader.parse(source);
} catch (HighlighterException &) {
definition.clear();
}
m_isBuilding.remove(id);
definitionFile.close();
m_definitions.insert(id, definition);
definitionFile.close();
m_isBuilding.remove(id);
}
return *m_definitions.constFind(id);
return m_definitions.value(id);
}
QSharedPointer<HighlightDefinitionMetaData> Manager::definitionMetaData(const QString &id) const
{ return m_definitionsMetaData.value(id); }
bool Manager::isBuildingDefinition(const QString &id) const
{ return m_isBuilding.contains(id); }
@@ -138,9 +139,7 @@ void Manager::registerMimeTypes()
clear();
QFuture<Core::MimeType> future =
QtConcurrent::run(&Manager::gatherDefinitionsMimeTypes, this);
m_watcher.setFuture(future);
connect(&m_watcher, SIGNAL(resultReadyAt(int)), this, SLOT(registerMimeType(int)));
m_mimeTypeWatcher.setFuture(future);
}
void Manager::gatherDefinitionsMimeTypes(QFutureInterface<Core::MimeType> &future)
@@ -153,10 +152,13 @@ void Manager::gatherDefinitionsMimeTypes(QFutureInterface<Core::MimeType> &futur
const QFileInfoList &filesInfo = definitionsDir.entryInfoList();
foreach (const QFileInfo &fileInfo, filesInfo) {
QString comment;
QStringList mimeTypes;
QStringList patterns;
parseDefinitionMetadata(fileInfo, &comment, &mimeTypes, &patterns);
const QSharedPointer<HighlightDefinitionMetaData> &metaData = parseMetadata(fileInfo);
if (metaData.isNull())
continue;
const QString &id = fileInfo.absoluteFilePath();
m_idByName.insert(metaData->name(), id);
m_definitionsMetaData.insert(id, metaData);
// A definition can specify multiple MIME types and file extensions/patterns. However, each
// thing is done with a single string. Then, there is no direct way to tell which patterns
@@ -165,19 +167,20 @@ void Manager::gatherDefinitionsMimeTypes(QFutureInterface<Core::MimeType> &futur
static const QStringList textPlain(QLatin1String("text/plain"));
QList<QRegExp> expressions;
foreach (const QString &type, mimeTypes) {
QList<QRegExp> patterns;
foreach (const QString &type, metaData->mimeTypes()) {
m_idByMimeType.insert(type, id);
Core::MimeType mimeType = Core::ICore::instance()->mimeDatabase()->findByType(type);
if (mimeType.isNull()) {
if (expressions.isEmpty()) {
foreach (const QString &pattern, patterns)
expressions.append(QRegExp(pattern, Qt::CaseSensitive, QRegExp::Wildcard));
if (patterns.isEmpty()) {
foreach (const QString &pattern, metaData->patterns())
patterns.append(QRegExp(pattern, Qt::CaseSensitive, QRegExp::Wildcard));
}
mimeType.setType(type);
mimeType.setSubClassesOf(textPlain);
mimeType.setComment(comment);
mimeType.setGlobPatterns(expressions);
mimeType.setComment(metaData->name());
mimeType.setGlobPatterns(patterns);
future.reportResult(mimeType);
}
@@ -187,76 +190,129 @@ void Manager::gatherDefinitionsMimeTypes(QFutureInterface<Core::MimeType> &futur
void Manager::registerMimeType(int index) const
{
const Core::MimeType &mimeType = m_watcher.resultAt(index);
Core::ICore::instance()->mimeDatabase()->addMimeType(mimeType);
const Core::MimeType &mimeType = m_mimeTypeWatcher.resultAt(index);
TextEditorPlugin::instance()->editorFactory()->addMimeType(mimeType.type());
Core::ICore::instance()->mimeDatabase()->addMimeType(mimeType);
}
void Manager::parseDefinitionMetadata(const QFileInfo &fileInfo,
QString *comment,
QStringList *mimeTypes,
QStringList *patterns)
QSharedPointer<HighlightDefinitionMetaData> Manager::parseMetadata(const QFileInfo &fileInfo)
{
static const QLatin1Char kSemiColon(';');
static const QLatin1Char kSlash('/');
static const QLatin1String kLanguage("language");
static const QLatin1String kName("name");
static const QLatin1String kExtensions("extensions");
static const QLatin1String kMimeType("mimetype");
static const QLatin1String kPriority("priority");
static const QLatin1String kArtificial("artificial");
const QString &id = fileInfo.absoluteFilePath();
QFile definitionFile(id);
QFile definitionFile(fileInfo.absoluteFilePath());
if (!definitionFile.open(QIODevice::ReadOnly | QIODevice::Text))
return;
return QSharedPointer<HighlightDefinitionMetaData>();
QSharedPointer<HighlightDefinitionMetaData> metaData(new HighlightDefinitionMetaData);
QXmlStreamReader reader(&definitionFile);
while (!reader.atEnd() && !reader.hasError()) {
if (reader.readNext() == QXmlStreamReader::StartElement &&
reader.name() == kLanguage) {
const QXmlStreamAttributes &attr = reader.attributes();
const QXmlStreamAttributes &atts = reader.attributes();
*comment = attr.value(kName).toString();
m_idByName.insert(*comment, id);
metaData->setName(atts.value(HighlightDefinitionMetaData::kName).toString());
metaData->setVersion(atts.value(HighlightDefinitionMetaData::kVersion).toString());
metaData->setPatterns(atts.value(HighlightDefinitionMetaData::kExtensions).
toString().split(kSemiColon, QString::SkipEmptyParts));
*patterns = attr.value(kExtensions).toString().split(kSemiColon,
QString::SkipEmptyParts);
*mimeTypes = attr.value(kMimeType).toString().split(kSemiColon,
QString::SkipEmptyParts);
if (mimeTypes->isEmpty()) {
QStringList mimeTypes = atts.value(HighlightDefinitionMetaData::kMimeType).
toString().split(kSemiColon, QString::SkipEmptyParts);
if (mimeTypes.isEmpty()) {
// 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(kSlash).append(*comment);
m_idByMimeType.insert(artificialType, id);
mimeTypes->append(artificialType);
} else {
foreach (const QString &type, *mimeTypes)
m_idByMimeType.insert(type, id);
artificialType.append(kSlash).append(metaData->name());
mimeTypes.append(artificialType);
}
// The priority below should not be confused with the priority used when matching files
// to MIME types. Kate uses this when there are definitions which share equal
// extensions/patterns. Here it is for choosing a highlight definition if there are
// multiple ones associated with the same MIME type (should not happen in general).
m_priorityComp.m_priorityById.insert(id, attr.value(kPriority).toString().toInt());
metaData->setMimeTypes(mimeTypes);
break;
}
}
reader.clear();
definitionFile.close();
return metaData;
}
void Manager::clear()
QList<HighlightDefinitionMetaData> Manager::parseAvailableDefinitionsList(QIODevice *device) const
{
m_priorityComp.m_priorityById.clear();
m_idByName.clear();
m_idByMimeType.clear();
m_definitions.clear();
static const QLatin1String kDefinition("Definition");
QList<HighlightDefinitionMetaData> metaDataList;
QXmlStreamReader reader(device);
while (!reader.atEnd() && !reader.hasError()) {
if (reader.readNext() == QXmlStreamReader::StartElement &&
reader.name() == kDefinition) {
const QXmlStreamAttributes &atts = reader.attributes();
HighlightDefinitionMetaData metaData;
metaData.setName(atts.value(HighlightDefinitionMetaData::kName).toString());
metaData.setUrl(QUrl(atts.value(HighlightDefinitionMetaData::kUrl).toString()));
metaData.setVersion(atts.value(HighlightDefinitionMetaData::kVersion).toString());
metaDataList.append(metaData);
}
}
reader.clear();
return metaDataList;
}
void Manager::downloadAvailableDefinitionsMetaData()
{
QUrl url(QLatin1String("http://www.kate-editor.org/syntax/update-3.2.xml"));
QNetworkRequest request(url);
// Currently this takes a couple of seconds on Windows 7: QTBUG-10106.
QNetworkReply *reply = m_networkManager.get(request);
connect(reply, SIGNAL(finished()), this, SLOT(downloadAvailableDefinitionsListFinished()));
}
void Manager::downloadAvailableDefinitionsListFinished()
{
if (QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender())) {
if (reply->error() == QNetworkReply::NoError)
emit definitionsMetaDataReady(parseAvailableDefinitionsList(reply));
else
emit errorDownloadingDefinitionsMetaData();
reply->deleteLater();
}
}
void Manager::downloadDefinitions(const QList<QUrl> &urls)
{
const QString &savePath =
TextEditorSettings::instance()->highlighterSettings().definitionFilesPath() +
QLatin1Char('/');
m_downloaders.clear();
foreach (const QUrl &url, urls)
m_downloaders.append(new DefinitionDownloader(url, savePath));
m_downloadingDefinitions = true;
QFuture<void> future = QtConcurrent::map(m_downloaders, DownloaderStarter());
m_downloadWatcher.setFuture(future);
Core::ICore::instance()->progressManager()->addTask(future,
tr("Downloading definitions"),
Constants::TASK_DOWNLOAD);
}
void Manager::downloadDefinitionsFinished()
{
foreach (DefinitionDownloader *downloader, m_downloaders)
delete downloader;
registerMimeTypes();
m_downloadingDefinitions = false;
}
bool Manager::isDownloadingDefinitions() const
{
return m_downloadingDefinitions;
}
void Manager::showGenericHighlighterOptions() const
@@ -265,7 +321,10 @@ void Manager::showGenericHighlighterOptions() const
Constants::TEXT_EDITOR_HIGHLIGHTER_SETTINGS);
}
void Manager::openDefinitionsUrl(const QString &location) const
void Manager::clear()
{
QDesktopServices::openUrl(QUrl(location));
m_idByName.clear();
m_idByMimeType.clear();
m_definitions.clear();
m_definitionsMetaData.clear();
}

View File

@@ -30,19 +30,23 @@
#ifndef MANAGER_H
#define MANAGER_H
#include "highlightdefinitionmetadata.h"
#include <coreplugin/mimedatabase.h>
#include <QtCore/QString>
#include <QtCore/QHash>
#include <QtCore/QMultiHash>
#include <QtCore/QSet>
#include <QtCore/QUrl>
#include <QtCore/QList>
#include <QtCore/QSharedPointer>
#include <QtCore/QFutureWatcher>
#include <QtNetwork/QNetworkAccessManager>
QT_BEGIN_NAMESPACE
class QFileInfo;
class QStringList;
class QDir;
class QIODevice;
template <class> class QFutureInterface;
QT_END_NAMESPACE
@@ -50,6 +54,7 @@ namespace TextEditor {
namespace Internal {
class HighlightDefinition;
class DefinitionDownloader;
class Manager : public QObject
{
@@ -61,43 +66,50 @@ public:
QString definitionIdByName(const QString &name) const;
QString definitionIdByMimeType(const QString &mimeType) const;
QString definitionIdByAnyMimeType(const QStringList &mimeTypes) const;
bool isBuildingDefinition(const QString &id) const;
const QSharedPointer<HighlightDefinition> &definition(const QString &id);
QSharedPointer<HighlightDefinition> definition(const QString &id);
QSharedPointer<HighlightDefinitionMetaData> definitionMetaData(const QString &id) const;
void downloadAvailableDefinitionsMetaData();
void downloadDefinitions(const QList<QUrl> &urls);
bool isDownloadingDefinitions() const;
public slots:
void registerMimeTypes();
void showGenericHighlighterOptions() const;
void openDefinitionsUrl(const QString &location) const;
private slots:
void registerMimeType(int index) const;
void downloadAvailableDefinitionsListFinished();
void downloadDefinitionsFinished();
private:
Manager();
Q_DISABLE_COPY(Manager)
void clear();
void gatherDefinitionsMimeTypes(QFutureInterface<Core::MimeType> &future);
void parseDefinitionMetadata(const QFileInfo &fileInfo,
QString *comment,
QStringList *mimeTypes,
QStringList *patterns);
struct PriorityCompare
{
bool operator()(const QString &a, const QString &b) const
{ return m_priorityById.value(a) < m_priorityById.value(b); }
QHash<QString, int> m_priorityById;
};
PriorityCompare m_priorityComp;
QFutureWatcher<Core::MimeType> m_watcher;
QSharedPointer<HighlightDefinitionMetaData> parseMetadata(const QFileInfo &fileInfo);
QList<HighlightDefinitionMetaData> parseAvailableDefinitionsList(QIODevice *device) const;
void clear();
QHash<QString, QString> m_idByName;
QMultiHash<QString, QString> m_idByMimeType;
QHash<QString, QString> m_idByMimeType;
QHash<QString, QSharedPointer<HighlightDefinition> > m_definitions;
QHash<QString, QSharedPointer<HighlightDefinitionMetaData> > m_definitionsMetaData;
QSet<QString> m_isBuilding;
QList<DefinitionDownloader *> m_downloaders;
bool m_downloadingDefinitions;
QNetworkAccessManager m_networkManager;
QFutureWatcher<void> m_downloadWatcher;
QFutureWatcher<Core::MimeType> m_mimeTypeWatcher;
signals:
void definitionsMetaDataReady(const QList<Internal::HighlightDefinitionMetaData>&);
void errorDownloadingDefinitionsMetaData();
};
} // namespace Internal

View File

@@ -173,10 +173,9 @@ void PlainTextEditor::configure(const Core::MimeType &mimeType)
definitionId = findDefinitionId(mimeType, true);
if (!definitionId.isEmpty()) {
try {
const QSharedPointer<HighlightDefinition> &definition =
Manager::instance()->definition(definitionId);
if (!definition.isNull()) {
Highlighter *highlighter = new Highlighter(definition->initialContext());
baseTextDocument()->setSyntaxHighlighter(highlighter);
@@ -188,7 +187,6 @@ void PlainTextEditor::configure(const Core::MimeType &mimeType)
setFontSettings(TextEditorSettings::instance()->fontSettings());
m_isMissingSyntaxDefinition = false;
} catch (const HighlighterException &) {
}
} else if (file()) {
const QString &fileName = file()->fileName();

View File

@@ -1,7 +1,7 @@
TEMPLATE = lib
TARGET = TextEditor
DEFINES += TEXTEDITOR_LIBRARY
QT += xml
QT += xml network
include(../../qtcreatorplugin.pri)
include(texteditor_dependencies.pri)
INCLUDEPATH += generichighlighter
@@ -55,7 +55,10 @@ SOURCES += texteditorplugin.cpp \
generichighlighter/manager.cpp \
generichighlighter/highlightdefinitionhandler.cpp \
generichighlighter/highlightersettingspage.cpp \
generichighlighter/highlightersettings.cpp
generichighlighter/highlightersettings.cpp \
generichighlighter/managedefinitionsdialog.cpp \
generichighlighter/highlightdefinitionmetadata.cpp \
generichighlighter/definitiondownloader.cpp
HEADERS += texteditorplugin.h \
textfilewizard.h \
@@ -113,13 +116,17 @@ HEADERS += texteditorplugin.h \
generichighlighter/manager.h \
generichighlighter/highlightdefinitionhandler.h \
generichighlighter/highlightersettingspage.h \
generichighlighter/highlightersettings.h
generichighlighter/highlightersettings.h \
generichighlighter/managedefinitionsdialog.h \
generichighlighter/highlightdefinitionmetadata.h \
generichighlighter/definitiondownloader.h
FORMS += behaviorsettingspage.ui \
displaysettingspage.ui \
fontsettingspage.ui \
colorschemeedit.ui \
generichighlighter/highlightersettingspage.ui
generichighlighter/highlightersettingspage.ui \
generichighlighter/managedefinitionsdialog.ui
RESOURCES += texteditor.qrc
OTHER_FILES += TextEditor.pluginspec TextEditor.mimetypes.xml

View File

@@ -88,7 +88,7 @@ const char * const GOTO_PREVIOUS_WORD_WITH_SELECTION = "TextEditor.GotoPreviousW
const char * const GOTO_NEXT_WORD_WITH_SELECTION = "TextEditor.GotoNextWordWithSelection";
const char * const C_TEXTEDITOR_MIMETYPE_TEXT = "text/plain";
const char * const INFO_SYNTAX_DEFINITION = "TextEditor.InfoSyntaxDefinition";
const char * const TASK_DOWNLOAD = "TextEditor.Task.Download";
// Text color and style categories
const char * const C_TEXT = "Text";