From ce80ee4f5e14eae425fd99c570885ac3e39814c6 Mon Sep 17 00:00:00 2001 From: Leandro Melo Date: Fri, 11 Jun 2010 12:13:52 +0200 Subject: Generic highlighter: Interface for automatic download of available definitions. Refactorings and changes in related parts of the code. --- .../generichighlighter/definitiondownloader.cpp | 94 +++++++++ .../generichighlighter/definitiondownloader.h | 75 +++++++ .../highlightdefinitionhandler.cpp | 5 + .../highlightdefinitionmetadata.cpp | 72 +++++++ .../highlightdefinitionmetadata.h | 78 +++++++ .../generichighlighter/highlightersettingspage.cpp | 47 ++++- .../generichighlighter/highlightersettingspage.h | 12 ++ .../generichighlighter/highlightersettingspage.ui | 88 ++++++-- .../generichighlighter/managedefinitionsdialog.cpp | 121 +++++++++++ .../generichighlighter/managedefinitionsdialog.h | 64 ++++++ .../generichighlighter/managedefinitionsdialog.ui | 117 +++++++++++ .../texteditor/generichighlighter/manager.cpp | 229 +++++++++++++-------- .../texteditor/generichighlighter/manager.h | 54 +++-- src/plugins/texteditor/plaintexteditor.cpp | 8 +- src/plugins/texteditor/texteditor.pro | 15 +- src/plugins/texteditor/texteditorconstants.h | 2 +- 16 files changed, 942 insertions(+), 139 deletions(-) create mode 100644 src/plugins/texteditor/generichighlighter/definitiondownloader.cpp create mode 100644 src/plugins/texteditor/generichighlighter/definitiondownloader.h create mode 100644 src/plugins/texteditor/generichighlighter/highlightdefinitionmetadata.cpp create mode 100644 src/plugins/texteditor/generichighlighter/highlightdefinitionmetadata.h create mode 100644 src/plugins/texteditor/generichighlighter/managedefinitionsdialog.cpp create mode 100644 src/plugins/texteditor/generichighlighter/managedefinitionsdialog.h create mode 100644 src/plugins/texteditor/generichighlighter/managedefinitionsdialog.ui (limited to 'src') diff --git a/src/plugins/texteditor/generichighlighter/definitiondownloader.cpp b/src/plugins/texteditor/generichighlighter/definitiondownloader.cpp new file mode 100644 index 0000000000..373c16b927 --- /dev/null +++ b/src/plugins/texteditor/generichighlighter/definitiondownloader.cpp @@ -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 +#include +#include +#include +#include +#include + +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(); +} diff --git a/src/plugins/texteditor/generichighlighter/definitiondownloader.h b/src/plugins/texteditor/generichighlighter/definitiondownloader.h new file mode 100644 index 0000000000..c2866183c7 --- /dev/null +++ b/src/plugins/texteditor/generichighlighter/definitiondownloader.h @@ -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 +#include +#include + +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 diff --git a/src/plugins/texteditor/generichighlighter/highlightdefinitionhandler.cpp b/src/plugins/texteditor/generichighlighter/highlightdefinitionhandler.cpp index c6a023aaf8..a157d94461 100644 --- a/src/plugins/texteditor/generichighlighter/highlightdefinitionhandler.cpp +++ b/src/plugins/texteditor/generichighlighter/highlightdefinitionhandler.cpp @@ -437,6 +437,9 @@ void HighlightDefinitionHandler::processIncludeRules(const QSharedPointer &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 +#include +#include +#include + +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 diff --git a/src/plugins/texteditor/generichighlighter/highlightersettingspage.cpp b/src/plugins/texteditor/generichighlighter/highlightersettingspage.cpp index 2209fd0958..adb6af254c 100644 --- a/src/plugins/texteditor/generichighlighter/highlightersettingspage.cpp +++ b/src/plugins/texteditor/generichighlighter/highlightersettingspage.cpp @@ -30,11 +30,14 @@ #include "highlightersettingspage.h" #include "highlightersettings.h" #include "manager.h" +#include "managedefinitionsdialog.h" #include "ui_highlightersettingspage.h" #include #include +#include + 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)), + this, + SLOT(manageDefinitions(QList)), + Qt::UniqueConnection); + connect(Manager::instance(), SIGNAL(errorDownloadingDefinitionsMetaData()), + this, SLOT(showError()), Qt::UniqueConnection); +} + +void HighlighterSettingsPage::ignoreDownloadReply() +{ + disconnect(Manager::instance(), + SIGNAL(definitionsMetaDataReady(QList)), + this, + SLOT(manageDefinitions(QList))); + disconnect(Manager::instance(), SIGNAL(errorDownloadingDefinitionsMetaData()), + this, SLOT(showError())); +} + +void HighlighterSettingsPage::manageDefinitions(const QList &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()) diff --git a/src/plugins/texteditor/generichighlighter/highlightersettingspage.h b/src/plugins/texteditor/generichighlighter/highlightersettingspage.h index 0edef5fa07..262f3d78cc 100644 --- a/src/plugins/texteditor/generichighlighter/highlightersettingspage.h +++ b/src/plugins/texteditor/generichighlighter/highlightersettingspage.h @@ -32,8 +32,16 @@ #include "texteditoroptionspage.h" +QT_BEGIN_NAMESPACE +template 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 &metaData); + void showError(); + void ignoreDownloadReply(); private: void settingsFromUI(); diff --git a/src/plugins/texteditor/generichighlighter/highlightersettingspage.ui b/src/plugins/texteditor/generichighlighter/highlightersettingspage.ui index ee365fb118..812cdaf509 100644 --- a/src/plugins/texteditor/generichighlighter/highlightersettingspage.ui +++ b/src/plugins/texteditor/generichighlighter/highlightersettingspage.ui @@ -21,47 +21,87 @@ - + + + + + + + Location: + + + + + + + + + + + + Reset to default + + + R + + + + :/core/images/reset.png:/core/images/reset.png + + + + + + + + + + 0 + 0 + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Definitions can be downloaded from the </span><a href="http://kate-editor.org/downloads/syntax_highlighting"><span style=" font-size:8pt; text-decoration: underline; color:#0000ff;">Kate Text Editor</span></a><span style=" font-size:8pt;"> website and stored at the location below.</span></p></body></html> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Highlight definitions are available from the </span><a href="http://kate-editor.org/downloads/syntax_highlighting?kateversion=3.2"><span style=" font-size:8pt; text-decoration: underline; color:#0000ff;">Kate Text Editor</span></a><span style=" font-size:8pt;"> website. Qt Creator can download a list of available definitions and then install the selected ones.</span></p></body></html> Qt::RichText - - Qt::TextBrowserInteraction + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + true + + + true - + - + - Location: + Manage Definitions - - - - - - Reset to default - - - R + + + Qt::Horizontal - - - :/core/images/reset.png:/core/images/reset.png + + + 328 + 20 + - + @@ -77,7 +117,7 @@ p, li { white-space: pre-wrap; } - Alert when a highlight definition is not found. + Alert when a highlight definition is not found @@ -125,6 +165,12 @@ p, li { white-space: pre-wrap; } + + resetButton + manageDefinitionsButton + alertWhenNoDefinition + ignoreEdit + diff --git a/src/plugins/texteditor/generichighlighter/managedefinitionsdialog.cpp b/src/plugins/texteditor/generichighlighter/managedefinitionsdialog.cpp new file mode 100644 index 0000000000..bc69ca272a --- /dev/null +++ b/src/plugins/texteditor/generichighlighter/managedefinitionsdialog.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace TextEditor; +using namespace Internal; + +ManageDefinitionsDialog::ManageDefinitionsDialog( + const QList &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 &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 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; + } +} diff --git a/src/plugins/texteditor/generichighlighter/managedefinitionsdialog.h b/src/plugins/texteditor/generichighlighter/managedefinitionsdialog.h new file mode 100644 index 0000000000..80961ba3ab --- /dev/null +++ b/src/plugins/texteditor/generichighlighter/managedefinitionsdialog.h @@ -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 + +namespace TextEditor { +namespace Internal { + +class ManageDefinitionsDialog : public QDialog +{ + Q_OBJECT +public: + explicit ManageDefinitionsDialog(const QList &metaDataList, + QWidget *parent = 0); + +protected: + void changeEvent(QEvent *e); + +private slots: + void downloadDefinitions(); + +private: + void populateDefinitionsWidget(); + + QList m_definitionsMetaData; + Ui::ManageDefinitionsDialog ui; +}; + +} // namespace Internal +} // namespace TextEditor + +#endif // MANAGEDEFINITIONSDIALOG_H diff --git a/src/plugins/texteditor/generichighlighter/managedefinitionsdialog.ui b/src/plugins/texteditor/generichighlighter/managedefinitionsdialog.ui new file mode 100644 index 0000000000..2f1a4501f6 --- /dev/null +++ b/src/plugins/texteditor/generichighlighter/managedefinitionsdialog.ui @@ -0,0 +1,117 @@ + + + ManageDefinitionsDialog + + + + 0 + 0 + 447 + 315 + + + + Dialog + + + + + + Available Definitions + + + + + + true + + + QAbstractItemView::SelectRows + + + 3 + + + false + + + 20 + + + + + + + + + + + + Download Selected Definitions + + + + + + + Qt::Horizontal + + + + 188 + 20 + + + + + + + + + + + + + + + Qt::Horizontal + + + + 298 + 20 + + + + + + + + Close + + + + + + + + + + + closeButton + clicked() + ManageDefinitionsDialog + close() + + + 381 + 294 + + + 306 + 298 + + + + + diff --git a/src/plugins/texteditor/generichighlighter/manager.cpp b/src/plugins/texteditor/generichighlighter/manager.cpp index 2a7d1b6be3..b5a9858872 100644 --- a/src/plugins/texteditor/generichighlighter/manager.cpp +++ b/src/plugins/texteditor/generichighlighter/manager.cpp @@ -31,14 +31,16 @@ #include "highlightdefinition.h" #include "highlightdefinitionhandler.h" #include "highlighterexception.h" -#include "texteditorplugin.h" -#include "texteditorsettings.h" -#include "plaintexteditorfactory.h" +#include "definitiondownloader.h" #include "highlightersettings.h" +#include "plaintexteditorfactory.h" #include "texteditorconstants.h" +#include "texteditorplugin.h" +#include "texteditorsettings.h" #include #include +#include #include #include @@ -50,22 +52,27 @@ #include #include #include -#include #include #include #include +#include #include #include #include #include #include #include +#include +#include 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::const_iterator it = m_idByMimeType.find(mimeType); - QMultiHash::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 &Manager::definition(const QString &id) +QSharedPointer 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(); QSharedPointer definition(new HighlightDefinition); HighlightDefinitionHandler handler(definition); + QXmlInputSource source(&definitionFile); QXmlSimpleReader reader; reader.setContentHandler(&handler); - reader.parse(source); + 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 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 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 &future) @@ -153,10 +152,13 @@ void Manager::gatherDefinitionsMimeTypes(QFutureInterface &futur const QFileInfoList &filesInfo = definitionsDir.entryInfoList(); foreach (const QFileInfo &fileInfo, filesInfo) { - QString comment; - QStringList mimeTypes; - QStringList patterns; - parseDefinitionMetadata(fileInfo, &comment, &mimeTypes, &patterns); + const QSharedPointer &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 &futur static const QStringList textPlain(QLatin1String("text/plain")); - QList expressions; - foreach (const QString &type, mimeTypes) { + QList 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 &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 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(); + + QSharedPointer 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 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 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(sender())) { + if (reply->error() == QNetworkReply::NoError) + emit definitionsMetaDataReady(parseAvailableDefinitionsList(reply)); + else + emit errorDownloadingDefinitionsMetaData(); + reply->deleteLater(); + } +} + +void Manager::downloadDefinitions(const QList &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 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(); } diff --git a/src/plugins/texteditor/generichighlighter/manager.h b/src/plugins/texteditor/generichighlighter/manager.h index 4963c33c5a..6c31a4451c 100644 --- a/src/plugins/texteditor/generichighlighter/manager.h +++ b/src/plugins/texteditor/generichighlighter/manager.h @@ -30,19 +30,23 @@ #ifndef MANAGER_H #define MANAGER_H +#include "highlightdefinitionmetadata.h" + #include #include #include -#include #include +#include +#include #include #include +#include QT_BEGIN_NAMESPACE class QFileInfo; class QStringList; -class QDir; +class QIODevice; template 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 &definition(const QString &id); + + QSharedPointer definition(const QString &id); + QSharedPointer definitionMetaData(const QString &id) const; + + void downloadAvailableDefinitionsMetaData(); + void downloadDefinitions(const QList &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 &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 m_priorityById; - }; - PriorityCompare m_priorityComp; - - QFutureWatcher m_watcher; + QSharedPointer parseMetadata(const QFileInfo &fileInfo); + QList parseAvailableDefinitionsList(QIODevice *device) const; + void clear(); QHash m_idByName; - QMultiHash m_idByMimeType; + QHash m_idByMimeType; QHash > m_definitions; + QHash > m_definitionsMetaData; QSet m_isBuilding; + + QList m_downloaders; + bool m_downloadingDefinitions; + QNetworkAccessManager m_networkManager; + + QFutureWatcher m_downloadWatcher; + QFutureWatcher m_mimeTypeWatcher; + +signals: + void definitionsMetaDataReady(const QList&); + void errorDownloadingDefinitionsMetaData(); }; } // namespace Internal diff --git a/src/plugins/texteditor/plaintexteditor.cpp b/src/plugins/texteditor/plaintexteditor.cpp index 222517bec1..17a77fb80a 100644 --- a/src/plugins/texteditor/plaintexteditor.cpp +++ b/src/plugins/texteditor/plaintexteditor.cpp @@ -173,10 +173,9 @@ void PlainTextEditor::configure(const Core::MimeType &mimeType) definitionId = findDefinitionId(mimeType, true); if (!definitionId.isEmpty()) { - try { - const QSharedPointer &definition = - Manager::instance()->definition(definitionId); - + const QSharedPointer &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(); diff --git a/src/plugins/texteditor/texteditor.pro b/src/plugins/texteditor/texteditor.pro index 747c5dadc7..0c404e9ea7 100644 --- a/src/plugins/texteditor/texteditor.pro +++ b/src/plugins/texteditor/texteditor.pro @@ -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 diff --git a/src/plugins/texteditor/texteditorconstants.h b/src/plugins/texteditor/texteditorconstants.h index 12338d16f1..1dc5d365d4 100644 --- a/src/plugins/texteditor/texteditorconstants.h +++ b/src/plugins/texteditor/texteditorconstants.h @@ -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"; -- cgit v1.2.3