File indexing completed on 2024-05-12 04:58:11

0001 /*
0002  * Copyright 2009 Jakub Wieczorek <faw217@gmail.com>
0003  *
0004  * This program is free software; you can redistribute it and/or modify
0005  * it under the terms of the GNU General Public License as published by
0006  * the Free Software Foundation; either version 2 of the License, or
0007  * (at your option) any later version.
0008  *
0009  * This program is distributed in the hope that it will be useful,
0010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0012  * GNU General Public License for more details.
0013  *
0014  * You should have received a copy of the GNU General Public License
0015  * along with this program; if not, write to the Free Software
0016  * Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017  * Boston, MA  02110-1301  USA
0018  */
0019 /* ============================================================
0020 * Falkon - Qt web browser
0021 * Copyright (C) 2010-2016  David Rosca <nowrep@gmail.com>
0022 *
0023 * This program is free software: you can redistribute it and/or modify
0024 * it under the terms of the GNU General Public License as published by
0025 * the Free Software Foundation, either version 3 of the License, or
0026 * (at your option) any later version.
0027 *
0028 * This program is distributed in the hope that it will be useful,
0029 * but WITHOUT ANY WARRANTY; without even the implied warranty of
0030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0031 * GNU General Public License for more details.
0032 *
0033 * You should have received a copy of the GNU General Public License
0034 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
0035 * ============================================================ */
0036 
0037 #include "opensearchengine.h"
0038 #include "opensearchenginedelegate.h"
0039 
0040 #include <qbuffer.h>
0041 #include <qcoreapplication.h>
0042 #include <qlocale.h>
0043 #include <qnetworkrequest.h>
0044 #include <qnetworkreply.h>
0045 #include <qregularexpression.h>
0046 #include <qstringlist.h>
0047 
0048 #include <QUrlQuery>
0049 #include <QJsonDocument>
0050 
0051 
0052 /*!
0053     \class OpenSearchEngine
0054     \brief A class representing a single search engine described in OpenSearch format
0055 
0056     OpenSearchEngine is a class that represents a single search engine based on
0057     the OpenSearch format.
0058     For more information about the format, see http://www.opensearch.org/.
0059 
0060     Instances of the class hold all the data associated with the corresponding search
0061     engines, such as name(), description() and also URL templates that are used
0062     to construct URLs, which can be used later to perform search queries. Search engine
0063     can also have an image, even an external one, in this case it will be downloaded
0064     automatically from the network.
0065 
0066     OpenSearchEngine instances can be constructed from scratch but also read from
0067     external sources and written back to them. OpenSearchReader and OpenSearchWriter
0068     are the classes provided for reading and writing OpenSearch descriptions.
0069 
0070     Default constructed engines need to be filled with the necessary information before
0071     they can be used to perform search requests. First of all, a search engine should have
0072     the metadata including the name and the description.
0073     However, the most important are URL templates, which are the construction of URLs
0074     but can also contain template parameters, that are replaced with corresponding values
0075     at the time of constructing URLs.
0076 
0077     There are two types of URL templates: search URL template and suggestions URL template.
0078     Search URL template is needed for constructing search URLs, which point directly to
0079     search results. Suggestions URL template is necessary to construct suggestion queries
0080     URLs, which are then used for requesting contextual suggestions, a popular service
0081     offered along with search results that provides search terms related to what has been
0082     supplied by the user.
0083 
0084     Both types of URLs are constructed by the class, by searchUrl() and suggestionsUrl()
0085     functions respectively. However, search requests are supposed to be performed outside
0086     the class, while suggestion queries can be executed using the requestSuggestions()
0087     method. The class will take care of performing the network request and parsing the
0088     JSON response.
0089 
0090     Both the image request and suggestion queries need network access. The class can
0091     perform network requests on its own, though the client application needs to provide
0092     a network access manager, which then will to be used for network operations.
0093     Without that, both images delivered from remote locations and contextual suggestions
0094     will be disabled.
0095 
0096     \sa OpenSearchReader, OpenSearchWriter
0097 */
0098 
0099 /*!
0100     Constructs an engine with a given \a parent.
0101 */
0102 OpenSearchEngine::OpenSearchEngine(QObject* parent)
0103     : QObject(parent)
0104     , m_searchMethod(QLatin1String("get"))
0105     , m_suggestionsMethod(QLatin1String("get"))
0106     , m_networkAccessManager(nullptr)
0107     , m_suggestionsReply(nullptr)
0108     , m_delegate(nullptr)
0109 {
0110     m_requestMethods.insert(QLatin1String("get"), QNetworkAccessManager::GetOperation);
0111     m_requestMethods.insert(QLatin1String("post"), QNetworkAccessManager::PostOperation);
0112 }
0113 
0114 /*!
0115     A destructor.
0116 */
0117 OpenSearchEngine::~OpenSearchEngine()
0118 = default;
0119 
0120 QString OpenSearchEngine::parseTemplate(const QString &searchTerm, const QString &searchTemplate)
0121 {
0122     QString language = QLocale().name();
0123     // Simple conversion to RFC 3066.
0124     language.replace(QLatin1Char('_'), QLatin1Char('-'));
0125 
0126     QString result = searchTemplate;
0127     result.replace(QLatin1String("{count}"), QLatin1String("20"));
0128     result.replace(QLatin1String("{startIndex}"), QLatin1String("0"));
0129     result.replace(QLatin1String("{startPage}"), QLatin1String("0"));
0130     result.replace(QLatin1String("{language}"), language);
0131     result.replace(QLatin1String("{inputEncoding}"), QLatin1String("UTF-8"));
0132     result.replace(QLatin1String("{outputEncoding}"), QLatin1String("UTF-8"));
0133     result.replace(QRegularExpression(QSL("\\{([^\\}]*:|)source\\??\\}")), QCoreApplication::applicationName());
0134     result.replace(QLatin1String("{searchTerms}"), QLatin1String(QUrl::toPercentEncoding(searchTerm)));
0135 
0136     return result;
0137 }
0138 
0139 /*!
0140     \property OpenSearchEngine::name
0141     \brief the name of the engine
0142 
0143     \sa description()
0144 */
0145 QString OpenSearchEngine::name() const
0146 {
0147     return m_name;
0148 }
0149 
0150 void OpenSearchEngine::setName(const QString &name)
0151 {
0152     m_name = name;
0153 }
0154 
0155 /*!
0156     \property OpenSearchEngine::description
0157     \brief the description of the engine
0158 
0159     \sa name()
0160 */
0161 QString OpenSearchEngine::description() const
0162 {
0163     return m_description;
0164 }
0165 
0166 void OpenSearchEngine::setDescription(const QString &description)
0167 {
0168     m_description = description;
0169 }
0170 
0171 /*!
0172     \property OpenSearchEngine::searchUrlTemplate
0173     \brief the template of the search URL
0174 
0175     \sa searchUrl(), searchParameters(), suggestionsUrlTemplate()
0176 */
0177 QString OpenSearchEngine::searchUrlTemplate() const
0178 {
0179     return m_searchUrlTemplate;
0180 }
0181 
0182 void OpenSearchEngine::setSearchUrlTemplate(const QString &searchUrlTemplate)
0183 {
0184     if (!searchUrlTemplate.startsWith(QL1S("http://")) && !searchUrlTemplate.startsWith(QL1S("https://"))) {
0185         return;
0186     }
0187 
0188     m_searchUrlTemplate = searchUrlTemplate;
0189 }
0190 
0191 /*!
0192     Constructs and returns a search URL with a given \a searchTerm.
0193 
0194     The URL template is processed according to the specification:
0195     http://www.opensearch.org/Specifications/OpenSearch/1.1#OpenSearch_URL_template_syntax
0196 
0197     A list of template parameters currently supported and what they are replaced with:
0198     \table
0199     \header \o parameter
0200             \o value
0201     \row    \o "{count}"
0202             \o "20"
0203     \row    \o "{startIndex}"
0204             \o "0"
0205     \row    \o "{startPage}"
0206             \o "0"
0207     \row    \o "{language}"
0208             \o "the default language code (RFC 3066)"
0209     \row    \o "{inputEncoding}"
0210             \o "UTF-8"
0211     \row    \o "{outputEncoding}"
0212             \o "UTF-8"
0213     \row    \o "{*:source}"
0214             \o "application name, QCoreApplication::applicationName()"
0215     \row    \o "{searchTerms}"
0216             \o "the string supplied by the user"
0217     \endtable
0218 
0219     \sa searchUrlTemplate(), searchParameters(), suggestionsUrl()
0220 */
0221 QUrl OpenSearchEngine::searchUrl(const QString &searchTerm) const
0222 {
0223     if (m_searchUrlTemplate.isEmpty()) {
0224         return {};
0225     }
0226 
0227     QUrl retVal = QUrl::fromEncoded(parseTemplate(searchTerm, m_searchUrlTemplate).toUtf8());
0228 
0229     QUrlQuery query(retVal);
0230     if (m_searchMethod != QLatin1String("post")) {
0231         Parameters::const_iterator end = m_searchParameters.constEnd();
0232         Parameters::const_iterator i = m_searchParameters.constBegin();
0233         for (; i != end; ++i) {
0234             query.addQueryItem(i->first, parseTemplate(searchTerm, i->second));
0235         }
0236         retVal.setQuery(query);
0237     }
0238 
0239     return retVal;
0240 }
0241 
0242 QByteArray OpenSearchEngine::getPostData(const QString &searchTerm) const
0243 {
0244     if (m_searchMethod != QLatin1String("post")) {
0245         return {};
0246     }
0247 
0248     QUrl retVal = QUrl(QSL("http://foo.bar"));
0249 
0250     QUrlQuery query(retVal);
0251     Parameters::const_iterator end = m_searchParameters.constEnd();
0252     Parameters::const_iterator i = m_searchParameters.constBegin();
0253     for (; i != end; ++i) {
0254         query.addQueryItem(i->first, parseTemplate(searchTerm, i->second));
0255     }
0256     retVal.setQuery(query);
0257 
0258     QByteArray data = retVal.toEncoded(QUrl::RemoveScheme);
0259     return data.contains('?') ? data.mid(data.lastIndexOf('?') + 1) : QByteArray();
0260 }
0261 
0262 /*!
0263     \property providesSuggestions
0264     \brief indicates whether the engine supports contextual suggestions
0265 */
0266 bool OpenSearchEngine::providesSuggestions() const
0267 {
0268     return (!m_suggestionsUrlTemplate.isEmpty() || !m_preparedSuggestionsUrl.isEmpty());
0269 }
0270 
0271 /*!
0272     \property OpenSearchEngine::suggestionsUrlTemplate
0273     \brief the template of the suggestions URL
0274 
0275     \sa suggestionsUrl(), suggestionsParameters(), searchUrlTemplate()
0276 */
0277 QString OpenSearchEngine::suggestionsUrlTemplate() const
0278 {
0279     return m_suggestionsUrlTemplate;
0280 }
0281 
0282 void OpenSearchEngine::setSuggestionsUrlTemplate(const QString &suggestionsUrlTemplate)
0283 {
0284     if (!suggestionsUrlTemplate.startsWith(QL1S("http://")) && !suggestionsUrlTemplate.startsWith(QL1S("https://"))) {
0285         return;
0286     }
0287 
0288     m_suggestionsUrlTemplate = suggestionsUrlTemplate;
0289 }
0290 
0291 /*!
0292     Constructs a suggestions URL with a given \a searchTerm.
0293 
0294     The URL template is processed according to the specification:
0295     http://www.opensearch.org/Specifications/OpenSearch/1.1#OpenSearch_URL_template_syntax
0296 
0297     See searchUrl() for more information about processing template parameters.
0298 
0299     \sa suggestionsUrlTemplate(), suggestionsParameters(), searchUrl()
0300 */
0301 QUrl OpenSearchEngine::suggestionsUrl(const QString &searchTerm) const
0302 {
0303     if (!m_preparedSuggestionsUrl.isEmpty()) {
0304         QString s = m_preparedSuggestionsUrl;
0305         s.replace(QLatin1String("%s"), searchTerm);
0306         return QUrl(s);
0307     }
0308 
0309     if (m_suggestionsUrlTemplate.isEmpty()) {
0310         return {};
0311     }
0312 
0313     QUrl retVal = QUrl::fromEncoded(parseTemplate(searchTerm, m_suggestionsUrlTemplate).toUtf8());
0314 
0315     QUrlQuery query(retVal);
0316     if (m_suggestionsMethod != QLatin1String("post")) {
0317         Parameters::const_iterator end = m_suggestionsParameters.constEnd();
0318         Parameters::const_iterator i = m_suggestionsParameters.constBegin();
0319         for (; i != end; ++i) {
0320             query.addQueryItem(i->first, parseTemplate(searchTerm, i->second));
0321         }
0322         retVal.setQuery(query);
0323     }
0324 
0325     return retVal;
0326 }
0327 
0328 /*!
0329     \property searchParameters
0330     \brief additional parameters that will be included in the search URL
0331 
0332     For more information see:
0333     http://www.opensearch.org/Specifications/OpenSearch/Extensions/Parameter/1.0
0334 */
0335 OpenSearchEngine::Parameters OpenSearchEngine::searchParameters() const
0336 {
0337     return m_searchParameters;
0338 }
0339 
0340 void OpenSearchEngine::setSearchParameters(const Parameters &searchParameters)
0341 {
0342     m_searchParameters = searchParameters;
0343 }
0344 
0345 /*!
0346     \property suggestionsParameters
0347     \brief additional parameters that will be included in the suggestions URL
0348 
0349     For more information see:
0350     http://www.opensearch.org/Specifications/OpenSearch/Extensions/Parameter/1.0
0351 */
0352 OpenSearchEngine::Parameters OpenSearchEngine::suggestionsParameters() const
0353 {
0354     return m_suggestionsParameters;
0355 }
0356 
0357 void OpenSearchEngine::setSuggestionsParameters(const Parameters &suggestionsParameters)
0358 {
0359     m_suggestionsParameters = suggestionsParameters;
0360 }
0361 
0362 /*!
0363     \property searchMethod
0364     \brief HTTP request method that will be used to perform search requests
0365 */
0366 QString OpenSearchEngine::searchMethod() const
0367 {
0368     return m_searchMethod;
0369 }
0370 
0371 void OpenSearchEngine::setSearchMethod(const QString &method)
0372 {
0373     QString requestMethod = method.toLower();
0374     if (!m_requestMethods.contains(requestMethod)) {
0375         return;
0376     }
0377 
0378     m_searchMethod = requestMethod;
0379 }
0380 
0381 /*!
0382     \property suggestionsMethod
0383     \brief HTTP request method that will be used to perform suggestions requests
0384 */
0385 QString OpenSearchEngine::suggestionsMethod() const
0386 {
0387     return m_suggestionsMethod;
0388 }
0389 
0390 void OpenSearchEngine::setSuggestionsMethod(const QString &method)
0391 {
0392     QString requestMethod = method.toLower();
0393     if (!m_requestMethods.contains(requestMethod)) {
0394         return;
0395     }
0396 
0397     m_suggestionsMethod = requestMethod;
0398 }
0399 
0400 /*!
0401     \property imageUrl
0402     \brief the image URL of the engine
0403 
0404     When setting a new image URL, it won't be loaded immediately. The first request will be
0405     deferred until image() is called for the first time.
0406 
0407     \note To be able to request external images, you need to provide a network access manager,
0408           which will be used for network operations.
0409 
0410     \sa image(), networkAccessManager()
0411 */
0412 QString OpenSearchEngine::imageUrl() const
0413 {
0414     return m_imageUrl;
0415 }
0416 
0417 void OpenSearchEngine::setImageUrl(const QString &imageUrl)
0418 {
0419     m_imageUrl = imageUrl;
0420 }
0421 
0422 void OpenSearchEngine::loadImage() const
0423 {
0424     if (!m_networkAccessManager || m_imageUrl.isEmpty()) {
0425         return;
0426     }
0427 
0428     QNetworkReply* reply = m_networkAccessManager->get(QNetworkRequest(QUrl::fromEncoded(m_imageUrl.toUtf8())));
0429     connect(reply, &QNetworkReply::finished, this, &OpenSearchEngine::imageObtained);
0430 }
0431 
0432 void OpenSearchEngine::imageObtained()
0433 {
0434     auto* reply = qobject_cast<QNetworkReply*>(sender());
0435 
0436     if (!reply) {
0437         return;
0438     }
0439 
0440     QByteArray response = reply->readAll();
0441 
0442     reply->close();
0443     reply->deleteLater();
0444 
0445     if (response.isEmpty()) {
0446         return;
0447     }
0448 
0449     m_image.loadFromData(response);
0450     Q_EMIT imageChanged();
0451 }
0452 
0453 /*!
0454     \property image
0455     \brief the image of the engine
0456 
0457     When no image URL has been set and an image will be set explicitly, a new data URL
0458     will be constructed, holding the image data encoded with Base64.
0459 
0460     \sa imageUrl()
0461 */
0462 QImage OpenSearchEngine::image() const
0463 {
0464     if (m_image.isNull()) {
0465         loadImage();
0466     }
0467     return m_image;
0468 }
0469 
0470 void OpenSearchEngine::setImage(const QImage &image)
0471 {
0472     if (m_imageUrl.isEmpty()) {
0473         QBuffer imageBuffer;
0474         imageBuffer.open(QBuffer::ReadWrite);
0475         if (image.save(&imageBuffer, "PNG")) {
0476             m_imageUrl = QString(QLatin1String("data:image/png;base64,%1"))
0477                          .arg(QLatin1String(imageBuffer.buffer().toBase64()));
0478         }
0479     }
0480 
0481     m_image = image;
0482     Q_EMIT imageChanged();
0483 }
0484 
0485 /*!
0486     \property valid
0487     \brief indicates whether the engine is valid i.e. the description was properly formed and included all necessary information
0488 */
0489 bool OpenSearchEngine::isValid() const
0490 {
0491     return (!m_name.isEmpty() && !m_searchUrlTemplate.isEmpty());
0492 }
0493 
0494 bool OpenSearchEngine::operator==(const OpenSearchEngine &other) const
0495 {
0496     return (m_name == other.m_name
0497             && m_description == other.m_description
0498             && m_imageUrl == other.m_imageUrl
0499             && m_searchUrlTemplate == other.m_searchUrlTemplate
0500             && m_suggestionsUrlTemplate == other.m_suggestionsUrlTemplate
0501             && m_searchParameters == other.m_searchParameters
0502             && m_suggestionsParameters == other.m_suggestionsParameters);
0503 }
0504 
0505 bool OpenSearchEngine::operator<(const OpenSearchEngine &other) const
0506 {
0507     return (m_name < other.m_name);
0508 }
0509 
0510 /*!
0511     Requests contextual suggestions on the search engine, for a given \a searchTerm.
0512 
0513     If succeeded, suggestions() signal will be emitted once the suggestions are received.
0514 
0515     \note To be able to request suggestions, you need to provide a network access manager,
0516           which will be used for network operations.
0517 
0518     \sa requestSearchResults()
0519 */
0520 
0521 void OpenSearchEngine::setSuggestionsParameters(const QByteArray &parameters)
0522 {
0523     m_preparedSuggestionsParameters = parameters;
0524 }
0525 
0526 void OpenSearchEngine::setSuggestionsUrl(const QString &string)
0527 {
0528     m_preparedSuggestionsUrl = string;
0529 }
0530 
0531 QString OpenSearchEngine::getSuggestionsUrl()
0532 {
0533     return suggestionsUrl(QSL("searchstring")).toString().replace(QLatin1String("searchstring"), QLatin1String("%s"));
0534 }
0535 
0536 QByteArray OpenSearchEngine::getSuggestionsParameters()
0537 {
0538     QStringList parameters;
0539     Parameters::const_iterator end = m_suggestionsParameters.constEnd();
0540     Parameters::const_iterator i = m_suggestionsParameters.constBegin();
0541     for (; i != end; ++i) {
0542         parameters.append(i->first + QLatin1Char('=') + i->second);
0543     }
0544 
0545     QByteArray data = parameters.join(QLatin1String("&")).toUtf8();
0546 
0547     return data;
0548 }
0549 
0550 void OpenSearchEngine::requestSuggestions(const QString &searchTerm)
0551 {
0552     if (searchTerm.isEmpty() || !providesSuggestions()) {
0553         return;
0554     }
0555 
0556     Q_ASSERT(m_networkAccessManager);
0557 
0558     if (!m_networkAccessManager) {
0559         return;
0560     }
0561 
0562     if (m_suggestionsReply) {
0563         m_suggestionsReply->disconnect(this);
0564         m_suggestionsReply->abort();
0565         m_suggestionsReply->deleteLater();
0566         m_suggestionsReply = nullptr;
0567     }
0568 
0569     Q_ASSERT(m_requestMethods.contains(m_suggestionsMethod));
0570     if (m_suggestionsMethod == QLatin1String("get")) {
0571         m_suggestionsReply = m_networkAccessManager->get(QNetworkRequest(suggestionsUrl(searchTerm)));
0572     }
0573     else {
0574         QStringList parameters;
0575         Parameters::const_iterator end = m_suggestionsParameters.constEnd();
0576         Parameters::const_iterator i = m_suggestionsParameters.constBegin();
0577         for (; i != end; ++i) {
0578             parameters.append(i->first + QLatin1Char('=') + i->second);
0579         }
0580 
0581         QByteArray data = parameters.join(QLatin1String("&")).toUtf8();
0582         m_suggestionsReply = m_networkAccessManager->post(QNetworkRequest(suggestionsUrl(searchTerm)), data);
0583     }
0584 
0585     connect(m_suggestionsReply, &QNetworkReply::finished, this, &OpenSearchEngine::suggestionsObtained);
0586 }
0587 
0588 /*!
0589     Requests search results on the search engine, for a given \a searchTerm.
0590 
0591     The default implementation does nothing, to supply your own you need to create your own
0592     OpenSearchEngineDelegate subclass and supply it to the engine. Then the function will call
0593     the performSearchRequest() method of the delegate, which can then handle the request
0594     in a custom way.
0595 
0596     \sa requestSuggestions(), delegate()
0597 */
0598 void OpenSearchEngine::requestSearchResults(const QString &searchTerm)
0599 {
0600     if (!m_delegate || searchTerm.isEmpty()) {
0601         return;
0602     }
0603 
0604     Q_ASSERT(m_requestMethods.contains(m_searchMethod));
0605 
0606     QNetworkRequest request(QUrl(searchUrl(searchTerm)));
0607     QByteArray data;
0608     QNetworkAccessManager::Operation operation = m_requestMethods.value(m_searchMethod);
0609 
0610     if (operation == QNetworkAccessManager::PostOperation) {
0611         QStringList parameters;
0612         Parameters::const_iterator end = m_searchParameters.constEnd();
0613         Parameters::const_iterator i = m_searchParameters.constBegin();
0614         for (; i != end; ++i) {
0615             parameters.append(i->first + QLatin1Char('=') + i->second);
0616         }
0617 
0618         data = parameters.join(QLatin1String("&")).toUtf8();
0619     }
0620 
0621     m_delegate->performSearchRequest(request, operation, data);
0622 }
0623 
0624 void OpenSearchEngine::suggestionsObtained()
0625 {
0626     const QByteArray response = m_suggestionsReply->readAll();
0627 
0628     m_suggestionsReply->close();
0629     m_suggestionsReply->deleteLater();
0630     m_suggestionsReply = nullptr;
0631 
0632     QJsonParseError err;
0633     QJsonDocument json = QJsonDocument::fromJson(response, &err);
0634     const QVariant res = json.toVariant();
0635 
0636     if (err.error != QJsonParseError::NoError || res.typeId() != QMetaType::QVariantList)
0637         return;
0638 
0639     const QVariantList list = res.toList();
0640 
0641     if (list.size() < 2)
0642         return;
0643 
0644     QStringList out;
0645 
0646     const auto items = list.at(1).toList();
0647     for (const QVariant &v : items) {
0648         out.append(v.toString());
0649     }
0650 
0651     Q_EMIT suggestions(out);
0652 }
0653 
0654 /*!
0655     \property networkAccessManager
0656     \brief the network access manager that is used to perform network requests
0657 
0658     It is required for network operations: loading external images and requesting
0659     contextual suggestions.
0660 */
0661 QNetworkAccessManager* OpenSearchEngine::networkAccessManager() const
0662 {
0663     return m_networkAccessManager;
0664 }
0665 
0666 void OpenSearchEngine::setNetworkAccessManager(QNetworkAccessManager* networkAccessManager)
0667 {
0668     m_networkAccessManager = networkAccessManager;
0669 }
0670 
0671 /*!
0672     \property delegate
0673     \brief the delegate that is used to perform specific tasks.
0674 
0675     It can be currently supplied to provide a custom behaviour ofthe requetSearchResults() method.
0676     The default implementation does nothing.
0677 */
0678 OpenSearchEngineDelegate* OpenSearchEngine::delegate() const
0679 {
0680     return m_delegate;
0681 }
0682 
0683 void OpenSearchEngine::setDelegate(OpenSearchEngineDelegate* delegate)
0684 {
0685     m_delegate = delegate;
0686 }
0687 
0688 /*!
0689     \fn void OpenSearchEngine::imageChanged()
0690 
0691     This signal is emitted whenever the image of the engine changes.
0692 
0693     \sa image(), imageUrl()
0694 */
0695 
0696 /*!
0697     \fn void OpenSearchEngine::suggestions(const QStringList &suggestions)
0698 
0699     This signal is emitted whenever new contextual suggestions have been provided
0700     by the search engine. To request suggestions, use requestSuggestions().
0701     The suggestion set is specified by \a suggestions.
0702 
0703     \sa requestSuggestions()
0704 */