diff --git a/autotests/kurifiltertest.cpp b/autotests/kurifiltertest.cpp --- a/autotests/kurifiltertest.cpp +++ b/autotests/kurifiltertest.cpp @@ -180,6 +180,10 @@ qputenv("KDE_FORK_SLAVES", "yes"); // simpler, for the final cleanup QLoggingCategory::setFilterRules(QStringLiteral("org.kde.kurifilter-*=true")); + QString searchProvidersDir = QFINDTESTDATA("../src/urifilters/ikws/searchproviders/google.desktop").section('/', 0, -2); + QVERIFY(!searchProvidersDir.isEmpty()); + qputenv("KIO_SEARCHPROVIDERS_DIR", QFile::encodeName(searchProvidersDir)); + // Allow testing of the search engine using both delimiters... const char *envDelimiter = ::getenv("KURIFILTERTEST_DELIMITER"); if (envDelimiter) { diff --git a/src/urifilters/ikws/CMakeLists.txt b/src/urifilters/ikws/CMakeLists.txt --- a/src/urifilters/ikws/CMakeLists.txt +++ b/src/urifilters/ikws/CMakeLists.txt @@ -2,7 +2,12 @@ #### -set(kuriikwsfilter_SRCS kuriikwsfiltereng.cpp kuriikwsfilter.cpp searchprovider.cpp) +set(kuriikwsfilter_SRCS + kuriikwsfiltereng.cpp + kuriikwsfilter.cpp + searchprovider.cpp + searchproviderregistry.cpp +) ki18n_wrap_ui(kuriikwsfilter_SRCS ikwsopts_ui.ui searchproviderdlg_ui.ui) @@ -22,7 +27,9 @@ kuriikwsfiltereng.cpp ikwsopts.cpp searchproviderdlg.cpp - searchprovider.cpp ) + searchprovider.cpp + searchproviderregistry.cpp +) ki18n_wrap_ui(kurisearchfilter_SRCS ikwsopts_ui.ui searchproviderdlg_ui.ui) diff --git a/src/urifilters/ikws/ikwsopts.h b/src/urifilters/ikws/ikwsopts.h --- a/src/urifilters/ikws/ikwsopts.h +++ b/src/urifilters/ikws/ikwsopts.h @@ -25,9 +25,9 @@ #include #include -#include #include "ui_ikwsopts_ui.h" +#include "searchproviderregistry.h" class SearchProvider; class ProvidersModel; @@ -61,6 +61,7 @@ // on save if a global service file exists for it. QStringList m_deletedProviders; ProvidersModel* m_providersModel; + SearchProviderRegistry m_registry; Ui::FilterOptionsUI m_dlg; }; diff --git a/src/urifilters/ikws/ikwsopts.cpp b/src/urifilters/ikws/ikwsopts.cpp --- a/src/urifilters/ikws/ikwsopts.cpp +++ b/src/urifilters/ikws/ikwsopts.cpp @@ -25,7 +25,6 @@ #include "searchprovider.h" #include "searchproviderdlg.h" -#include #include #include #include @@ -291,16 +290,13 @@ const QString defaultSearchEngine = group.readEntry("DefaultWebShortcut"); const QStringList favoriteEngines = group.readEntry("PreferredWebShortcuts", DEFAULT_PREFERRED_SEARCH_PROVIDERS); - QList providers; - const KService::List services = KServiceTypeTrader::self()->query(QStringLiteral("SearchProvider")); - int defaultProviderIndex = services.size(); //default is "None", it is last in the list + const QList providers = m_registry.findAll(); + int defaultProviderIndex = providers.size(); //default is "None", it is last in the list - Q_FOREACH(const KService::Ptr &service, services) + for (SearchProvider *provider : providers) { - SearchProvider* provider = new SearchProvider(service); if (defaultSearchEngine == provider->desktopEntryName()) defaultProviderIndex = providers.size(); - providers.append(provider); } m_providersModel->setProviders(providers, favoriteEngines); diff --git a/src/urifilters/ikws/kuriikwsfilter.cpp b/src/urifilters/ikws/kuriikwsfilter.cpp --- a/src/urifilters/ikws/kuriikwsfilter.cpp +++ b/src/urifilters/ikws/kuriikwsfilter.cpp @@ -68,7 +68,7 @@ const QString searchTerm = filter->keywordDelimiter() + data.typedString(); if (allproviders) - providers = SearchProvider::findAll(); + providers = filter->registry()->findAll(); else { // Start with the search engines marked as preferred... @@ -92,7 +92,7 @@ QStringListIterator it (favEngines); while (it.hasNext()) { - SearchProvider *favProvider = SearchProvider::findByDesktopName(it.next()); + SearchProvider *favProvider = filter->registry()->findByDesktopName(it.next()); if (favProvider) providers << favProvider; } @@ -163,7 +163,6 @@ QList searchProviders; populateProvidersList(searchProviders, data); setSearchProviders(data, searchProviders); - delete provider; return true; } } diff --git a/src/urifilters/ikws/kuriikwsfiltereng.h b/src/urifilters/ikws/kuriikwsfiltereng.h --- a/src/urifilters/ikws/kuriikwsfiltereng.h +++ b/src/urifilters/ikws/kuriikwsfiltereng.h @@ -28,6 +28,7 @@ #include #include #include +#include "searchproviderregistry.h" #define DEFAULT_PREFERRED_SEARCH_PROVIDERS \ QStringList() << QStringLiteral("google") << QStringLiteral("youtube") << QStringLiteral("yahoo") << QStringLiteral("wikipedia") << QStringLiteral("wikit") @@ -51,21 +52,24 @@ QUrl formatResult (const QString& url, const QString& cset1, const QString& cset2, const QString& query, bool isMalformed) const; + SearchProviderRegistry *registry(); + static KURISearchFilterEngine *self(); void loadConfig(); protected: QUrl formatResult (const QString& url, const QString& cset1, const QString& cset2, const QString& query, bool isMalformed, SubstMap& map) const; private: - KURISearchFilterEngine(const KURISearchFilterEngine&); - KURISearchFilterEngine& operator= (const KURISearchFilterEngine&); + KURISearchFilterEngine(const KURISearchFilterEngine&) = delete; + KURISearchFilterEngine& operator= (const KURISearchFilterEngine&) = delete; QStringList modifySubstitutionMap (SubstMap& map, const QString& query) const; QString substituteQuery (const QString& url, SubstMap &map, const QString& userquery, QTextCodec *codec) const; + SearchProviderRegistry m_registry; QString m_defaultWebShortcut; QStringList m_preferredWebShortcuts; bool m_bWebShortcutsEnabled; diff --git a/src/urifilters/ikws/kuriikwsfiltereng.cpp b/src/urifilters/ikws/kuriikwsfiltereng.cpp --- a/src/urifilters/ikws/kuriikwsfiltereng.cpp +++ b/src/urifilters/ikws/kuriikwsfiltereng.cpp @@ -72,14 +72,13 @@ if (!key.isEmpty() && !KProtocolInfo::isKnownProtocol(key)) { - provider = SearchProvider::findByKey(key); + provider = m_registry.findByKey(key); if (provider) { if (!m_bUseOnlyPreferredWebShortcuts || m_preferredWebShortcuts.contains(provider->desktopEntryName())) { searchTerm = typedString.mid(pos+1); qCDebug(category) << "found provider" << provider->desktopEntryName() << "searchTerm=" << searchTerm; } else { - delete provider; provider = nullptr; } } @@ -100,8 +99,9 @@ // Make sure we ignore supported protocols, e.g. "smb:", "http:" const int pos = typedString.indexOf(':'); - if (pos == -1 || !KProtocolInfo::isKnownProtocol(typedString.left(pos))) - provider = SearchProvider::findByDesktopName(defaultSearchProvider); + if (pos == -1 || !KProtocolInfo::isKnownProtocol(typedString.left(pos))) { + provider = m_registry.findByDesktopName(defaultSearchProvider); + } } return provider; @@ -442,3 +442,8 @@ qCDebug(category) << "Default Shortcut: " << m_defaultWebShortcut; qCDebug(category) << "Keyword Delimiter: " << m_cKeywordDelimiter; } + +SearchProviderRegistry * KURISearchFilterEngine::registry() +{ + return &m_registry; +} diff --git a/src/urifilters/ikws/kurisearchfilter.cpp b/src/urifilters/ikws/kurisearchfilter.cpp --- a/src/urifilters/ikws/kurisearchfilter.cpp +++ b/src/urifilters/ikws/kurisearchfilter.cpp @@ -25,6 +25,7 @@ #include "ikwsopts.h" #include +#include #include #include @@ -70,7 +71,7 @@ QString searchTerm; KURISearchFilterEngine *filter = KURISearchFilterEngine::self(); - QScopedPointer provider(filter->webShortcutQuery(data.typedString(), searchTerm)); + SearchProvider* provider(filter->webShortcutQuery(data.typedString(), searchTerm)); if (!provider) { return false; } diff --git a/src/urifilters/ikws/searchprovider.h b/src/urifilters/ikws/searchprovider.h --- a/src/urifilters/ikws/searchprovider.h +++ b/src/urifilters/ikws/searchprovider.h @@ -20,14 +20,14 @@ #ifndef SEARCHPROVIDER_H #define SEARCHPROVIDER_H -#include #include class SearchProvider : public KUriFilterSearchProvider { public: SearchProvider() : m_dirty(false) {} - explicit SearchProvider(const KService::Ptr service); + explicit SearchProvider(const QString &servicePath); + ~SearchProvider(); const QString& charset() const { return m_charset; } const QString& query() const { return m_query; } @@ -41,9 +41,6 @@ QString iconName() const Q_DECL_OVERRIDE; - static SearchProvider *findByDesktopName(const QString &); - static SearchProvider *findByKey(const QString &); - static QList findAll(); private: QString m_query; QString m_charset; diff --git a/src/urifilters/ikws/searchprovider.cpp b/src/urifilters/ikws/searchprovider.cpp --- a/src/urifilters/ikws/searchprovider.cpp +++ b/src/urifilters/ikws/searchprovider.cpp @@ -22,16 +22,26 @@ #include #include #include // KIO::iconNameForUrl +#include +#include +#include +#include -SearchProvider::SearchProvider(const KService::Ptr service) +SearchProvider::SearchProvider(const QString &servicePath) : m_dirty(false) { - setDesktopEntryName(service->desktopEntryName()); - setName(service->name()); - setKeys(service->property(QStringLiteral("Keys")).toStringList()); + setDesktopEntryName(QFileInfo(servicePath).baseName()); + KDesktopFile parser(servicePath); + setName(parser.name()); + KConfigGroup group(parser.desktopGroup()); + setKeys(group.readEntry(QStringLiteral("Keys"), QStringList())); + + m_query = group.readEntry(QStringLiteral("Query")); + m_charset = group.readEntry(QStringLiteral("Charset")); +} - m_query = service->property(QStringLiteral("Query")).toString(); - m_charset = service->property(QStringLiteral("Charset")).toString(); +SearchProvider::~SearchProvider() +{ } void SearchProvider::setName(const QString &name) @@ -116,26 +126,3 @@ { m_dirty = dirty; } - -SearchProvider *SearchProvider::findByDesktopName(const QString &name) -{ - KService::Ptr service = - KService::serviceByDesktopPath(QStringLiteral("searchproviders/%1.desktop").arg(name)); - return service ? new SearchProvider(service) : nullptr; -} - -SearchProvider *SearchProvider::findByKey(const QString &key) -{ - KService::List providers = - KServiceTypeTrader::self()->query(QStringLiteral("SearchProvider"), QStringLiteral("'%1' in Keys").arg(key)); - return providers.count() ? new SearchProvider(providers[0]) : nullptr; -} - -QList SearchProvider::findAll() -{ - QList ret; - Q_FOREACH (const KService::Ptr &provider, KServiceTypeTrader::self()->query(QLatin1String("SearchProvider"))) { - ret.append(new SearchProvider(provider)); - } - return ret; -} diff --git a/src/urifilters/ikws/searchproviderregistry.h b/src/urifilters/ikws/searchproviderregistry.h new file mode 100644 --- /dev/null +++ b/src/urifilters/ikws/searchproviderregistry.h @@ -0,0 +1,61 @@ +/* + * This file is part of the KDE project + * Copyright 2017 David Faure + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef SEARCHPROVIDERREGISTRY_H +#define SEARCHPROVIDERREGISTRY_H + +#include +#include + +class SearchProvider; + +/** + * Memory cache for search provider desktop files + */ +class SearchProviderRegistry +{ +public: + /** + * Default constructor + */ + SearchProviderRegistry(); + + /** + * Destructor + */ + ~SearchProviderRegistry(); + + QList findAll(); + + SearchProvider *findByKey(const QString &key) const; + + SearchProvider *findByDesktopName(const QString &desktopName) const; + +private: + void reload(); + QStringList directories() const; + + QList m_searchProviders; + QMap m_searchProvidersByKey; + QMap m_searchProvidersByDesktopName; +}; + +#endif // SEARCHPROVIDERREGISTRY_H diff --git a/src/urifilters/ikws/searchproviderregistry.cpp b/src/urifilters/ikws/searchproviderregistry.cpp new file mode 100644 --- /dev/null +++ b/src/urifilters/ikws/searchproviderregistry.cpp @@ -0,0 +1,84 @@ +/* + * This file is part of the KDE project + * Copyright 2017 David Faure + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "searchproviderregistry.h" +#include "searchprovider.h" + +#include +#include +#include + +SearchProviderRegistry::SearchProviderRegistry() +{ + reload(); +} + +SearchProviderRegistry::~SearchProviderRegistry() +{ + qDeleteAll(m_searchProviders); +} + +QStringList SearchProviderRegistry::directories() const +{ + const QString testDir = QFile::decodeName(qgetenv("KIO_SEARCHPROVIDERS_DIR")); // for unittests + if (!testDir.isEmpty()) + return { testDir }; + return QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("kservices5/searchproviders/"), QStandardPaths::LocateDirectory); +} + +void SearchProviderRegistry::reload() +{ + m_searchProvidersByKey.clear(); + m_searchProvidersByDesktopName.clear(); + qDeleteAll(m_searchProviders); + m_searchProviders.clear(); + + const QStringList servicesDirs = directories(); + for (const QString &dirPath : servicesDirs) { + QDir dir(dirPath); + for (const QString &file : dir.entryList({QStringLiteral("*.desktop")}, QDir::Files)) { + if (!m_searchProvidersByDesktopName.contains(file)) { + const QString filePath = dir.path() + QLatin1Char('/') + file; + auto *provider = new SearchProvider(filePath); + m_searchProvidersByDesktopName.insert(file, provider); + m_searchProviders.append(provider); + for (const QString &key : provider->keys()) { + m_searchProvidersByKey.insert(key, provider); + } + } + } + } +} + +QList SearchProviderRegistry::findAll() +{ + return m_searchProviders; +} + +SearchProvider* SearchProviderRegistry::findByKey(const QString& key) const +{ + return m_searchProvidersByKey.value(key); +} + +SearchProvider* SearchProviderRegistry::findByDesktopName(const QString &name) const +{ + return m_searchProvidersByDesktopName.value(name + ".desktop"); +} diff --git a/src/widgets/kurifilter.cpp b/src/widgets/kurifilter.cpp --- a/src/widgets/kurifilter.cpp +++ b/src/widgets/kurifilter.cpp @@ -186,7 +186,6 @@ ~KUriFilterDataPrivate() { - qDeleteAll(searchProviderMap.begin(), searchProviderMap.end()); } void setData(const QUrl &u, const QString &typedUrl)