mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
Implemented suggestions popup. Cleanup
This commit is contained in:
parent
748ef8bb87
commit
6de00c7969
6 changed files with 946 additions and 39 deletions
|
@ -21,6 +21,8 @@ import "../controls-uit" as HifiControls
|
|||
import "../windows"
|
||||
import "../controls"
|
||||
|
||||
import HifiWeb 1.0
|
||||
|
||||
Rectangle {
|
||||
id: root;
|
||||
|
||||
|
@ -31,17 +33,61 @@ Rectangle {
|
|||
property bool keyboardEnabled: true // FIXME - Keyboard HMD only: Default to false
|
||||
property bool keyboardRaised: false
|
||||
property bool punctuationMode: false
|
||||
property var suggestionsList: []
|
||||
|
||||
OpenSearchEngine {
|
||||
id: searchEngine
|
||||
name: "Google";
|
||||
//icon: ":icons/sites/google.png"
|
||||
searchUrlTemplate: "https://www.google.com/search?client=qupzilla&q=%s";
|
||||
suggestionsUrlTemplate: "https://suggestqueries.google.com/complete/search?output=firefox&q=%s";
|
||||
suggestionsUrl: "https://suggestqueries.google.com/complete/search?output=firefox&q=%s";
|
||||
|
||||
onSuggestions: {
|
||||
if (suggestions.length > 0) {
|
||||
console.log("suggestions:", suggestions)
|
||||
suggestionsList = []
|
||||
suggestionsList.push(addressBar.editText) //do not overwrite edit text
|
||||
for(var i = 0; i < suggestions.length; i++) {
|
||||
suggestionsList.push(suggestions[i])
|
||||
}
|
||||
addressBar.model = suggestionsList
|
||||
if (!addressBar.popup.visible) {
|
||||
addressBar.popup.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: suggestionRequestTimer
|
||||
interval: 200
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
if (addressBar.editText !== "") {
|
||||
searchEngine.requestSuggestions(addressBar.editText)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
color: hifi.colors.baseGray;
|
||||
|
||||
// only show the title if loaded through a "loader"
|
||||
function goTo(url) {
|
||||
if (url.indexOf("http") !== 0) {
|
||||
url = "http://" + url;
|
||||
}
|
||||
webEngineView.url = url
|
||||
event.accepted = true;
|
||||
suggestionRequestTimer.stop()
|
||||
addressBar.popup.close()
|
||||
}
|
||||
|
||||
Column {
|
||||
spacing: 2
|
||||
width: parent.width;
|
||||
|
||||
RowLayout {
|
||||
id: addressBarRow
|
||||
width: parent.width;
|
||||
height: 48
|
||||
|
||||
|
@ -70,50 +116,65 @@ Rectangle {
|
|||
|
||||
//selectByMouse: true
|
||||
focus: true
|
||||
//placeholderText: "Enter URL"
|
||||
|
||||
editable: true
|
||||
//flat: true
|
||||
indicator: Item {}
|
||||
background: Item {}
|
||||
onActivated: {
|
||||
goTo(textAt(index))
|
||||
}
|
||||
|
||||
contentItem: QQControls.TextField {
|
||||
id: addressBarInput
|
||||
leftPadding: 26
|
||||
rightPadding: hifi.dimensions.controlLineHeight
|
||||
text: addressBar.editText
|
||||
placeholderText: qsTr("Enter URL")
|
||||
font: addressBar.font
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
|
||||
Image {
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
x: 5
|
||||
z: 2
|
||||
id: faviconImage
|
||||
width: 16; height: 16
|
||||
sourceSize: Qt.size(width, height)
|
||||
source: webEngineView.icon
|
||||
onSourceChanged: console.log("web icon", source)
|
||||
}
|
||||
|
||||
HifiControls.WebGlyphButton {
|
||||
glyph: webEngineView.loading ? hifi.glyphs.closeSmall : hifi.glyphs.reloadSmall;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
width: hifi.dimensions.controlLineHeight
|
||||
z: 2
|
||||
x: addressBarInput.width - implicitWidth
|
||||
onClicked: {
|
||||
if (webEngineView.loading) {
|
||||
webEngineView.stop()
|
||||
} else {
|
||||
reloadTimer.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: ScriptDiscoveryService.scriptsModelFilter.filterRegExp = new RegExp("^.*$", "i")
|
||||
|
||||
Keys.onPressed: {
|
||||
if (event.key === Qt.Key_Return) {
|
||||
if (editText.indexOf("http") != 0) {
|
||||
editText = "http://" + editText;
|
||||
}
|
||||
webEngineView.url = editText
|
||||
event.accepted = true;
|
||||
goTo(editText)
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
anchors.verticalCenter: addressBar.verticalCenter;
|
||||
x: 5
|
||||
z: 2
|
||||
id: faviconImage
|
||||
width: 16; height: 16
|
||||
sourceSize: Qt.size(width, height)
|
||||
source: webEngineView.icon
|
||||
onEditTextChanged: {
|
||||
console.log("edit text", addressBar.editText)
|
||||
suggestionRequestTimer.restart()
|
||||
}
|
||||
|
||||
HifiControls.WebGlyphButton {
|
||||
glyph: webEngineView.loading ? hifi.glyphs.closeSmall : hifi.glyphs.reloadSmall;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
width: hifi.dimensions.controlLineHeight
|
||||
z: 2
|
||||
x: addressBar.width - 28
|
||||
onClicked: {
|
||||
if (webEngineView.loading) {
|
||||
webEngineView.stop()
|
||||
} else {
|
||||
reloadTimer.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
leftPadding: 26
|
||||
rightPadding: 26
|
||||
|
||||
Layout.fillWidth: true
|
||||
editText: webEngineView.url
|
||||
onAccepted: webEngineView.url = editText
|
||||
|
@ -121,7 +182,6 @@ Rectangle {
|
|||
|
||||
HifiControls.WebGlyphButton {
|
||||
checkable: true
|
||||
//only QtWebEngine 1.3
|
||||
checked: webEngineView.audioMuted
|
||||
glyph: checked ? hifi.glyphs.unmuted : hifi.glyphs.muted
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
|
@ -162,7 +222,6 @@ Rectangle {
|
|||
property real webViewHeight: root.height - loadProgressBar.height - 48 - 4
|
||||
height: keyboardEnabled && keyboardRaised ? webViewHeight - keyboard.height : webViewHeight
|
||||
|
||||
|
||||
focus: true
|
||||
objectName: "tabletWebEngineView"
|
||||
|
||||
|
@ -172,6 +231,13 @@ Rectangle {
|
|||
|
||||
property string userScriptUrl: ""
|
||||
|
||||
onLoadingChanged: {
|
||||
if (!loading) {
|
||||
suggestionRequestTimer.stop()
|
||||
addressBar.popup.close()
|
||||
}
|
||||
}
|
||||
|
||||
// creates a global EventBridge object.
|
||||
WebEngineScript {
|
||||
id: createGlobalEventBridge
|
||||
|
@ -202,10 +268,9 @@ Rectangle {
|
|||
settings.javascriptEnabled: true
|
||||
settings.errorPageEnabled: true
|
||||
settings.pluginsEnabled: true
|
||||
settings.fullScreenSupportEnabled: false
|
||||
//from WebEngine 1.3
|
||||
settings.autoLoadIconsForPage: false
|
||||
settings.touchIconsEnabled: false
|
||||
settings.fullScreenSupportEnabled: true
|
||||
settings.autoLoadIconsForPage: true
|
||||
settings.touchIconsEnabled: true
|
||||
|
||||
onCertificateError: {
|
||||
error.defer();
|
||||
|
|
|
@ -201,6 +201,8 @@
|
|||
#include "commerce/Wallet.h"
|
||||
#include "commerce/QmlCommerce.h"
|
||||
|
||||
#include "opensearch/opensearchengine.h"
|
||||
|
||||
// On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU
|
||||
// FIXME seems to be broken.
|
||||
#if defined(Q_OS_WIN)
|
||||
|
@ -2224,6 +2226,7 @@ void Application::initializeUi() {
|
|||
QmlCommerce::registerType();
|
||||
qmlRegisterType<ResourceImageItem>("Hifi", 1, 0, "ResourceImageItem");
|
||||
qmlRegisterType<Preference>("Hifi", 1, 0, "Preference");
|
||||
qmlRegisterType<OpenSearchEngine>("HifiWeb", 1, 0, "OpenSearchEngine");
|
||||
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
offscreenUi->create();
|
||||
|
|
650
interface/src/opensearch/opensearchengine.cpp
Normal file
650
interface/src/opensearch/opensearchengine.cpp
Normal file
|
@ -0,0 +1,650 @@
|
|||
#include "opensearchengine.h"
|
||||
#include "qregexp.h"
|
||||
#include "opensearchenginedelegate.h"
|
||||
|
||||
#include <qbuffer.h>
|
||||
#include <qcoreapplication.h>
|
||||
#include <qlocale.h>
|
||||
#include <qnetworkrequest.h>
|
||||
#include <qnetworkreply.h>
|
||||
#include <qregexp.h>
|
||||
#include <qstringlist.h>
|
||||
|
||||
#include <QUrlQuery>
|
||||
#include <QJsonDocument>
|
||||
|
||||
#include <NetworkAccessManager.h>
|
||||
|
||||
|
||||
/*!
|
||||
\class OpenSearchEngine
|
||||
\brief A class representing a single search engine described in OpenSearch format
|
||||
|
||||
OpenSearchEngine is a class that represents a single search engine based on
|
||||
the OpenSearch format.
|
||||
For more information about the format, see http://www.opensearch.org/.
|
||||
|
||||
Instances of the class hold all the data associated with the corresponding search
|
||||
engines, such as name(), description() and also URL templates that are used
|
||||
to construct URLs, which can be used later to perform search queries. Search engine
|
||||
can also have an image, even an external one, in this case it will be downloaded
|
||||
automatically from the network.
|
||||
|
||||
OpenSearchEngine instances can be constructed from scratch but also read from
|
||||
external sources and written back to them. OpenSearchReader and OpenSearchWriter
|
||||
are the classes provided for reading and writing OpenSearch descriptions.
|
||||
|
||||
Default constructed engines need to be filled with the necessary information before
|
||||
they can be used to peform search requests. First of all, a search engine should have
|
||||
the metadata including the name and the description.
|
||||
However, the most important are URL templates, which are the construction of URLs
|
||||
but can also contain template parameters, that are replaced with corresponding values
|
||||
at the time of constructing URLs.
|
||||
|
||||
There are two types of URL templates: search URL template and suggestions URL template.
|
||||
Search URL template is needed for constructing search URLs, which point directly to
|
||||
search results. Suggestions URL template is necessary to construct suggestion queries
|
||||
URLs, which are then used for requesting contextual suggestions, a popular service
|
||||
offered along with search results that provides search terms related to what has been
|
||||
supplied by the user.
|
||||
|
||||
Both types of URLs are constructed by the class, by searchUrl() and suggestionsUrl()
|
||||
functions respectively. However, search requests are supposed to be performed outside
|
||||
the class, while suggestion queries can be executed using the requestSuggestions()
|
||||
method. The class will take care of peforming the network request and parsing the
|
||||
JSON response.
|
||||
|
||||
Both the image request and suggestion queries need network access. The class can
|
||||
perform network requests on its own, though the client application needs to provide
|
||||
a network access manager, which then will to be used for network operations.
|
||||
Without that, both images delivered from remote locations and contextual suggestions
|
||||
will be disabled.
|
||||
|
||||
\sa OpenSearchReader, OpenSearchWriter
|
||||
*/
|
||||
|
||||
/*!
|
||||
Constructs an engine with a given \a parent.
|
||||
*/
|
||||
OpenSearchEngine::OpenSearchEngine(QObject* parent)
|
||||
: QObject(parent)
|
||||
, m_searchMethod(QLatin1String("get"))
|
||||
, m_suggestionsMethod(QLatin1String("get"))
|
||||
, m_suggestionsReply(0)
|
||||
, m_delegate(0)
|
||||
{
|
||||
m_requestMethods.insert(QLatin1String("get"), QNetworkAccessManager::GetOperation);
|
||||
m_requestMethods.insert(QLatin1String("post"), QNetworkAccessManager::PostOperation);
|
||||
}
|
||||
|
||||
/*!
|
||||
A destructor.
|
||||
*/
|
||||
OpenSearchEngine::~OpenSearchEngine()
|
||||
{
|
||||
}
|
||||
|
||||
QString OpenSearchEngine::parseTemplate(const QString &searchTerm, const QString &searchTemplate)
|
||||
{
|
||||
QString language = QLocale().name();
|
||||
// Simple conversion to RFC 3066.
|
||||
language = language.replace(QLatin1Char('_'), QLatin1Char('-'));
|
||||
|
||||
QString result = searchTemplate;
|
||||
result.replace(QLatin1String("{count}"), QLatin1String("20"));
|
||||
result.replace(QLatin1String("{startIndex}"), QLatin1String("0"));
|
||||
result.replace(QLatin1String("{startPage}"), QLatin1String("0"));
|
||||
result.replace(QLatin1String("{language}"), language);
|
||||
result.replace(QLatin1String("{inputEncoding}"), QLatin1String("UTF-8"));
|
||||
result.replace(QLatin1String("{outputEncoding}"), QLatin1String("UTF-8"));
|
||||
result.replace(QRegExp(QLatin1String("\\{([^\\}]*:|)source\\??\\}")), QCoreApplication::applicationName());
|
||||
result.replace(QLatin1String("{searchTerms}"), QLatin1String(QUrl::toPercentEncoding(searchTerm)));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
\property OpenSearchEngine::name
|
||||
\brief the name of the engine
|
||||
|
||||
\sa description()
|
||||
*/
|
||||
QString OpenSearchEngine::name() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
void OpenSearchEngine::setName(const QString &name)
|
||||
{
|
||||
m_name = name;
|
||||
}
|
||||
|
||||
/*!
|
||||
\property OpenSearchEngine::description
|
||||
\brief the description of the engine
|
||||
|
||||
\sa name()
|
||||
*/
|
||||
QString OpenSearchEngine::description() const
|
||||
{
|
||||
return m_description;
|
||||
}
|
||||
|
||||
void OpenSearchEngine::setDescription(const QString &description)
|
||||
{
|
||||
m_description = description;
|
||||
}
|
||||
|
||||
/*!
|
||||
\property OpenSearchEngine::searchUrlTemplate
|
||||
\brief the template of the search URL
|
||||
|
||||
\sa searchUrl(), searchParameters(), suggestionsUrlTemplate()
|
||||
*/
|
||||
QString OpenSearchEngine::searchUrlTemplate() const
|
||||
{
|
||||
return m_searchUrlTemplate;
|
||||
}
|
||||
|
||||
void OpenSearchEngine::setSearchUrlTemplate(const QString &searchUrlTemplate)
|
||||
{
|
||||
if (!searchUrlTemplate.startsWith(QLatin1String("http://")) && !searchUrlTemplate.startsWith(QLatin1String("https://"))) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_searchUrlTemplate = searchUrlTemplate;
|
||||
}
|
||||
|
||||
/*!
|
||||
Constructs and returns a search URL with a given \a searchTerm.
|
||||
|
||||
The URL template is processed according to the specification:
|
||||
http://www.opensearch.org/Specifications/OpenSearch/1.1#OpenSearch_URL_template_syntax
|
||||
|
||||
A list of template parameters currently supported and what they are replaced with:
|
||||
\table
|
||||
\header \o parameter
|
||||
\o value
|
||||
\row \o "{count}"
|
||||
\o "20"
|
||||
\row \o "{startIndex}"
|
||||
\o "0"
|
||||
\row \o "{startPage}"
|
||||
\o "0"
|
||||
\row \o "{language}"
|
||||
\o "the default language code (RFC 3066)"
|
||||
\row \o "{inputEncoding}"
|
||||
\o "UTF-8"
|
||||
\row \o "{outputEncoding}"
|
||||
\o "UTF-8"
|
||||
\row \o "{*:source}"
|
||||
\o "application name, QCoreApplication::applicationName()"
|
||||
\row \o "{searchTerms}"
|
||||
\o "the string supplied by the user"
|
||||
\endtable
|
||||
|
||||
\sa searchUrlTemplate(), searchParameters(), suggestionsUrl()
|
||||
*/
|
||||
QUrl OpenSearchEngine::searchUrl(const QString &searchTerm) const
|
||||
{
|
||||
if (m_searchUrlTemplate.isEmpty()) {
|
||||
return QUrl();
|
||||
}
|
||||
|
||||
QUrl retVal = QUrl::fromEncoded(parseTemplate(searchTerm, m_searchUrlTemplate).toUtf8());
|
||||
|
||||
QUrlQuery query(retVal);
|
||||
if (m_searchMethod != QLatin1String("post")) {
|
||||
Parameters::const_iterator end = m_searchParameters.constEnd();
|
||||
Parameters::const_iterator i = m_searchParameters.constBegin();
|
||||
for (; i != end; ++i) {
|
||||
query.addQueryItem(i->first, parseTemplate(searchTerm, i->second));
|
||||
}
|
||||
retVal.setQuery(query);
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
QByteArray OpenSearchEngine::getPostData(const QString &searchTerm) const
|
||||
{
|
||||
if (m_searchMethod != QLatin1String("post")) {
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
QUrl retVal = QUrl("http://foo.bar");
|
||||
|
||||
QUrlQuery query(retVal);
|
||||
Parameters::const_iterator end = m_searchParameters.constEnd();
|
||||
Parameters::const_iterator i = m_searchParameters.constBegin();
|
||||
for (; i != end; ++i) {
|
||||
query.addQueryItem(i->first, parseTemplate(searchTerm, i->second));
|
||||
}
|
||||
retVal.setQuery(query);
|
||||
|
||||
QByteArray data = retVal.toEncoded(QUrl::RemoveScheme);
|
||||
return data.contains('?') ? data.mid(data.lastIndexOf('?') + 1) : QByteArray();
|
||||
}
|
||||
|
||||
/*!
|
||||
\property providesSuggestions
|
||||
\brief indicates whether the engine supports contextual suggestions
|
||||
*/
|
||||
bool OpenSearchEngine::providesSuggestions() const
|
||||
{
|
||||
return (!m_suggestionsUrlTemplate.isEmpty() || !m_preparedSuggestionsUrl.isEmpty());
|
||||
}
|
||||
|
||||
/*!
|
||||
\property OpenSearchEngine::suggestionsUrlTemplate
|
||||
\brief the template of the suggestions URL
|
||||
|
||||
\sa suggestionsUrl(), suggestionsParameters(), searchUrlTemplate()
|
||||
*/
|
||||
QString OpenSearchEngine::suggestionsUrlTemplate() const
|
||||
{
|
||||
return m_suggestionsUrlTemplate;
|
||||
}
|
||||
|
||||
void OpenSearchEngine::setSuggestionsUrlTemplate(const QString &suggestionsUrlTemplate)
|
||||
{
|
||||
if (!suggestionsUrlTemplate.startsWith(QLatin1String("http://")) && !suggestionsUrlTemplate.startsWith(QLatin1String("https://"))) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_suggestionsUrlTemplate = suggestionsUrlTemplate;
|
||||
}
|
||||
|
||||
/*!
|
||||
Constructs a suggestions URL with a given \a searchTerm.
|
||||
|
||||
The URL template is processed according to the specification:
|
||||
http://www.opensearch.org/Specifications/OpenSearch/1.1#OpenSearch_URL_template_syntax
|
||||
|
||||
See searchUrl() for more information about processing template parameters.
|
||||
|
||||
\sa suggestionsUrlTemplate(), suggestionsParameters(), searchUrl()
|
||||
*/
|
||||
QUrl OpenSearchEngine::suggestionsUrl(const QString &searchTerm) const
|
||||
{
|
||||
if (!m_preparedSuggestionsUrl.isEmpty()) {
|
||||
QString s = m_preparedSuggestionsUrl;
|
||||
s.replace(QLatin1String("%s"), searchTerm);
|
||||
return QUrl(s);
|
||||
}
|
||||
|
||||
if (m_suggestionsUrlTemplate.isEmpty()) {
|
||||
return QUrl();
|
||||
}
|
||||
|
||||
QUrl retVal = QUrl::fromEncoded(parseTemplate(searchTerm, m_suggestionsUrlTemplate).toUtf8());
|
||||
|
||||
QUrlQuery query(retVal);
|
||||
if (m_suggestionsMethod != QLatin1String("post")) {
|
||||
Parameters::const_iterator end = m_suggestionsParameters.constEnd();
|
||||
Parameters::const_iterator i = m_suggestionsParameters.constBegin();
|
||||
for (; i != end; ++i) {
|
||||
query.addQueryItem(i->first, parseTemplate(searchTerm, i->second));
|
||||
}
|
||||
retVal.setQuery(query);
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/*!
|
||||
\property searchParameters
|
||||
\brief additional parameters that will be included in the search URL
|
||||
|
||||
For more information see:
|
||||
http://www.opensearch.org/Specifications/OpenSearch/Extensions/Parameter/1.0
|
||||
*/
|
||||
OpenSearchEngine::Parameters OpenSearchEngine::searchParameters() const
|
||||
{
|
||||
return m_searchParameters;
|
||||
}
|
||||
|
||||
void OpenSearchEngine::setSearchParameters(const Parameters &searchParameters)
|
||||
{
|
||||
m_searchParameters = searchParameters;
|
||||
}
|
||||
|
||||
/*!
|
||||
\property suggestionsParameters
|
||||
\brief additional parameters that will be included in the suggestions URL
|
||||
|
||||
For more information see:
|
||||
http://www.opensearch.org/Specifications/OpenSearch/Extensions/Parameter/1.0
|
||||
*/
|
||||
OpenSearchEngine::Parameters OpenSearchEngine::suggestionsParameters() const
|
||||
{
|
||||
return m_suggestionsParameters;
|
||||
}
|
||||
|
||||
void OpenSearchEngine::setSuggestionsParameters(const Parameters &suggestionsParameters)
|
||||
{
|
||||
m_suggestionsParameters = suggestionsParameters;
|
||||
}
|
||||
|
||||
/*!
|
||||
\property searchMethod
|
||||
\brief HTTP request method that will be used to perform search requests
|
||||
*/
|
||||
QString OpenSearchEngine::searchMethod() const
|
||||
{
|
||||
return m_searchMethod;
|
||||
}
|
||||
|
||||
void OpenSearchEngine::setSearchMethod(const QString &method)
|
||||
{
|
||||
QString requestMethod = method.toLower();
|
||||
if (!m_requestMethods.contains(requestMethod)) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_searchMethod = requestMethod;
|
||||
}
|
||||
|
||||
/*!
|
||||
\property suggestionsMethod
|
||||
\brief HTTP request method that will be used to perform suggestions requests
|
||||
*/
|
||||
QString OpenSearchEngine::suggestionsMethod() const
|
||||
{
|
||||
return m_suggestionsMethod;
|
||||
}
|
||||
|
||||
void OpenSearchEngine::setSuggestionsMethod(const QString &method)
|
||||
{
|
||||
QString requestMethod = method.toLower();
|
||||
if (!m_requestMethods.contains(requestMethod)) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_suggestionsMethod = requestMethod;
|
||||
}
|
||||
|
||||
/*!
|
||||
\property imageUrl
|
||||
\brief the image URL of the engine
|
||||
|
||||
When setting a new image URL, it won't be loaded immediately. The first request will be
|
||||
deferred until image() is called for the first time.
|
||||
|
||||
\note To be able to request external images, you need to provide a network access manager,
|
||||
which will be used for network operations.
|
||||
|
||||
\sa image(), networkAccessManager()
|
||||
*/
|
||||
QString OpenSearchEngine::imageUrl() const
|
||||
{
|
||||
return m_imageUrl;
|
||||
}
|
||||
|
||||
void OpenSearchEngine::setImageUrl(const QString &imageUrl)
|
||||
{
|
||||
m_imageUrl = imageUrl;
|
||||
}
|
||||
|
||||
void OpenSearchEngine::loadImage() const
|
||||
{
|
||||
if (m_imageUrl.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
|
||||
QNetworkReply* reply = networkAccessManager.get(QNetworkRequest(QUrl::fromEncoded(m_imageUrl.toUtf8())));
|
||||
connect(reply, SIGNAL(finished()), this, SLOT(imageObtained()));
|
||||
}
|
||||
|
||||
void OpenSearchEngine::imageObtained()
|
||||
{
|
||||
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
|
||||
|
||||
if (!reply) {
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray response = reply->readAll();
|
||||
|
||||
reply->close();
|
||||
reply->deleteLater();
|
||||
|
||||
if (response.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_image.loadFromData(response);
|
||||
emit imageChanged();
|
||||
}
|
||||
|
||||
/*!
|
||||
\property image
|
||||
\brief the image of the engine
|
||||
|
||||
When no image URL has been set and an image will be set explicitly, a new data URL
|
||||
will be constructed, holding the image data encoded with Base64.
|
||||
|
||||
\sa imageUrl()
|
||||
*/
|
||||
QImage OpenSearchEngine::image() const
|
||||
{
|
||||
if (m_image.isNull()) {
|
||||
loadImage();
|
||||
}
|
||||
return m_image;
|
||||
}
|
||||
|
||||
void OpenSearchEngine::setImage(const QImage &image)
|
||||
{
|
||||
if (m_imageUrl.isEmpty()) {
|
||||
QBuffer imageBuffer;
|
||||
imageBuffer.open(QBuffer::ReadWrite);
|
||||
if (image.save(&imageBuffer, "PNG")) {
|
||||
m_imageUrl = QString(QLatin1String("data:image/png;base64,%1"))
|
||||
.arg(QLatin1String(imageBuffer.buffer().toBase64()));
|
||||
}
|
||||
}
|
||||
|
||||
m_image = image;
|
||||
emit imageChanged();
|
||||
}
|
||||
|
||||
/*!
|
||||
\property valid
|
||||
\brief indicates whether the engine is valid i.e. the description was properly formed and included all necessary information
|
||||
*/
|
||||
bool OpenSearchEngine::isValid() const
|
||||
{
|
||||
return (!m_name.isEmpty() && !m_searchUrlTemplate.isEmpty());
|
||||
}
|
||||
|
||||
bool OpenSearchEngine::operator==(const OpenSearchEngine &other) const
|
||||
{
|
||||
return (m_name == other.m_name
|
||||
&& m_description == other.m_description
|
||||
&& m_imageUrl == other.m_imageUrl
|
||||
&& m_searchUrlTemplate == other.m_searchUrlTemplate
|
||||
&& m_suggestionsUrlTemplate == other.m_suggestionsUrlTemplate
|
||||
&& m_searchParameters == other.m_searchParameters
|
||||
&& m_suggestionsParameters == other.m_suggestionsParameters);
|
||||
}
|
||||
|
||||
bool OpenSearchEngine::operator<(const OpenSearchEngine &other) const
|
||||
{
|
||||
return (m_name < other.m_name);
|
||||
}
|
||||
|
||||
/*!
|
||||
Requests contextual suggestions on the search engine, for a given \a searchTerm.
|
||||
|
||||
If succeeded, suggestions() signal will be emitted once the suggestions are received.
|
||||
|
||||
\note To be able to request suggestions, you need to provide a network access manager,
|
||||
which will be used for network operations.
|
||||
|
||||
\sa requestSearchResults()
|
||||
*/
|
||||
|
||||
void OpenSearchEngine::setSuggestionsParameters(const QByteArray ¶meters)
|
||||
{
|
||||
m_preparedSuggestionsParameters = parameters;
|
||||
}
|
||||
|
||||
void OpenSearchEngine::setSuggestionsUrl(const QString &string)
|
||||
{
|
||||
m_preparedSuggestionsUrl = string;
|
||||
}
|
||||
|
||||
QString OpenSearchEngine::getSuggestionsUrl()
|
||||
{
|
||||
return suggestionsUrl("searchstring").toString().replace(QLatin1String("searchstring"), QLatin1String("%s"));
|
||||
}
|
||||
|
||||
QByteArray OpenSearchEngine::getSuggestionsParameters()
|
||||
{
|
||||
QStringList parameters;
|
||||
Parameters::const_iterator end = m_suggestionsParameters.constEnd();
|
||||
Parameters::const_iterator i = m_suggestionsParameters.constBegin();
|
||||
for (; i != end; ++i) {
|
||||
parameters.append(i->first + QLatin1String("=") + i->second);
|
||||
}
|
||||
|
||||
QByteArray data = parameters.join(QLatin1String("&")).toUtf8();
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void OpenSearchEngine::requestSuggestions(const QString &searchTerm)
|
||||
{
|
||||
if (searchTerm.isEmpty() || !providesSuggestions()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
|
||||
if (m_suggestionsReply) {
|
||||
m_suggestionsReply->disconnect(this);
|
||||
m_suggestionsReply->abort();
|
||||
m_suggestionsReply->deleteLater();
|
||||
m_suggestionsReply = 0;
|
||||
}
|
||||
|
||||
Q_ASSERT(m_requestMethods.contains(m_suggestionsMethod));
|
||||
if (m_suggestionsMethod == QLatin1String("get")) {
|
||||
m_suggestionsReply = networkAccessManager.get(QNetworkRequest(suggestionsUrl(searchTerm)));
|
||||
}
|
||||
else {
|
||||
QStringList parameters;
|
||||
Parameters::const_iterator end = m_suggestionsParameters.constEnd();
|
||||
Parameters::const_iterator i = m_suggestionsParameters.constBegin();
|
||||
for (; i != end; ++i) {
|
||||
parameters.append(i->first + QLatin1String("=") + i->second);
|
||||
}
|
||||
|
||||
QByteArray data = parameters.join(QLatin1String("&")).toUtf8();
|
||||
m_suggestionsReply = networkAccessManager.post(QNetworkRequest(suggestionsUrl(searchTerm)), data);
|
||||
}
|
||||
|
||||
connect(m_suggestionsReply, SIGNAL(finished()), this, SLOT(suggestionsObtained()));
|
||||
}
|
||||
|
||||
/*!
|
||||
Requests search results on the search engine, for a given \a searchTerm.
|
||||
|
||||
The default implementation does nothing, to supply your own you need to create your own
|
||||
OpenSearchEngineDelegate subclass and supply it to the engine. Then the function will call
|
||||
the performSearchRequest() method of the delegate, which can then handle the request
|
||||
in a custom way.
|
||||
|
||||
\sa requestSuggestions(), delegate()
|
||||
*/
|
||||
void OpenSearchEngine::requestSearchResults(const QString &searchTerm)
|
||||
{
|
||||
if (!m_delegate || searchTerm.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Q_ASSERT(m_requestMethods.contains(m_searchMethod));
|
||||
|
||||
QNetworkRequest request(QUrl(searchUrl(searchTerm)));
|
||||
QByteArray data;
|
||||
QNetworkAccessManager::Operation operation = m_requestMethods.value(m_searchMethod);
|
||||
|
||||
if (operation == QNetworkAccessManager::PostOperation) {
|
||||
QStringList parameters;
|
||||
Parameters::const_iterator end = m_searchParameters.constEnd();
|
||||
Parameters::const_iterator i = m_searchParameters.constBegin();
|
||||
for (; i != end; ++i) {
|
||||
parameters.append(i->first + QLatin1String("=") + i->second);
|
||||
}
|
||||
|
||||
data = parameters.join(QLatin1String("&")).toUtf8();
|
||||
}
|
||||
|
||||
m_delegate->performSearchRequest(request, operation, data);
|
||||
}
|
||||
|
||||
void OpenSearchEngine::suggestionsObtained()
|
||||
{
|
||||
const QByteArray response = m_suggestionsReply->readAll();
|
||||
|
||||
m_suggestionsReply->close();
|
||||
m_suggestionsReply->deleteLater();
|
||||
m_suggestionsReply = 0;
|
||||
|
||||
QJsonParseError err;
|
||||
QJsonDocument json = QJsonDocument::fromJson(response, &err);
|
||||
const QVariant res = json.toVariant();
|
||||
|
||||
if (err.error != QJsonParseError::NoError || res.type() != QVariant::List)
|
||||
return;
|
||||
|
||||
const QVariantList list = res.toList();
|
||||
|
||||
if (list.size() < 2)
|
||||
return;
|
||||
|
||||
QStringList out;
|
||||
|
||||
foreach (const QVariant &v, list.at(1).toList())
|
||||
out.append(v.toString());
|
||||
|
||||
emit suggestions(out);
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
\property delegate
|
||||
\brief the delegate that is used to perform specific tasks.
|
||||
|
||||
It can be currently supplied to provide a custom behaviour ofthe requetSearchResults() method.
|
||||
The default implementation does nothing.
|
||||
*/
|
||||
OpenSearchEngineDelegate* OpenSearchEngine::delegate() const
|
||||
{
|
||||
return m_delegate;
|
||||
}
|
||||
|
||||
void OpenSearchEngine::setDelegate(OpenSearchEngineDelegate* delegate)
|
||||
{
|
||||
m_delegate = delegate;
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn void OpenSearchEngine::imageChanged()
|
||||
|
||||
This signal is emitted whenever the image of the engine changes.
|
||||
|
||||
\sa image(), imageUrl()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn void OpenSearchEngine::suggestions(const QStringList &suggestions)
|
||||
|
||||
This signal is emitted whenever new contextual suggestions have been provided
|
||||
by the search engine. To request suggestions, use requestSuggestions().
|
||||
The suggestion set is specified by \a suggestions.
|
||||
|
||||
\sa requestSuggestions()
|
||||
*/
|
128
interface/src/opensearch/opensearchengine.h
Normal file
128
interface/src/opensearch/opensearchengine.h
Normal file
|
@ -0,0 +1,128 @@
|
|||
#ifndef OPENSEARCHENGINE_H
|
||||
#define OPENSEARCHENGINE_H
|
||||
|
||||
#include <qpair.h>
|
||||
#include <qimage.h>
|
||||
#include <qmap.h>
|
||||
#include <qnetworkaccessmanager.h>
|
||||
#include <qstring.h>
|
||||
#include <qurl.h>
|
||||
|
||||
class QNetworkReply;
|
||||
|
||||
class OpenSearchEngineDelegate;
|
||||
class OpenSearchEngine : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
signals:
|
||||
void imageChanged();
|
||||
void suggestions(const QStringList &suggestions);
|
||||
|
||||
public:
|
||||
typedef QPair<QString, QString> Parameter;
|
||||
typedef QList<Parameter> Parameters;
|
||||
|
||||
Q_PROPERTY(QString name READ name WRITE setName)
|
||||
Q_PROPERTY(QString description READ description WRITE setDescription)
|
||||
Q_PROPERTY(QString searchUrlTemplate READ searchUrlTemplate WRITE setSearchUrlTemplate)
|
||||
Q_PROPERTY(Parameters searchParameters READ searchParameters WRITE setSearchParameters)
|
||||
Q_PROPERTY(QString searchMethod READ searchMethod WRITE setSearchMethod)
|
||||
Q_PROPERTY(QString suggestionsUrlTemplate READ suggestionsUrlTemplate WRITE setSuggestionsUrlTemplate)
|
||||
Q_PROPERTY(QString suggestionsUrl READ getSuggestionsUrl WRITE setSuggestionsUrl)
|
||||
Q_PROPERTY(Parameters suggestionsParameters READ suggestionsParameters WRITE setSuggestionsParameters)
|
||||
Q_PROPERTY(QString suggestionsMethod READ suggestionsMethod WRITE setSuggestionsMethod)
|
||||
Q_PROPERTY(bool providesSuggestions READ providesSuggestions)
|
||||
Q_PROPERTY(QString imageUrl READ imageUrl WRITE setImageUrl)
|
||||
Q_PROPERTY(bool valid READ isValid)
|
||||
|
||||
OpenSearchEngine(QObject* parent = 0);
|
||||
~OpenSearchEngine();
|
||||
|
||||
QString name() const;
|
||||
void setName(const QString &name);
|
||||
|
||||
QString description() const;
|
||||
void setDescription(const QString &description);
|
||||
|
||||
QString searchUrlTemplate() const;
|
||||
void setSearchUrlTemplate(const QString &searchUrl);
|
||||
QUrl searchUrl(const QString &searchTerm) const;
|
||||
|
||||
QByteArray getPostData(const QString &searchTerm) const;
|
||||
|
||||
bool providesSuggestions() const;
|
||||
|
||||
QString suggestionsUrlTemplate() const;
|
||||
void setSuggestionsUrlTemplate(const QString &suggestionsUrl);
|
||||
QUrl suggestionsUrl(const QString &searchTerm) const;
|
||||
|
||||
Parameters searchParameters() const;
|
||||
void setSearchParameters(const Parameters &searchParameters);
|
||||
|
||||
Parameters suggestionsParameters() const;
|
||||
void setSuggestionsParameters(const Parameters &suggestionsParameters);
|
||||
|
||||
QString searchMethod() const;
|
||||
void setSearchMethod(const QString &method);
|
||||
|
||||
QString suggestionsMethod() const;
|
||||
void setSuggestionsMethod(const QString &method);
|
||||
|
||||
QString imageUrl() const;
|
||||
void setImageUrl(const QString &url);
|
||||
|
||||
QImage image() const;
|
||||
void setImage(const QImage &image);
|
||||
|
||||
bool isValid() const;
|
||||
|
||||
void setSuggestionsUrl(const QString &string);
|
||||
void setSuggestionsParameters(const QByteArray ¶meters);
|
||||
QString getSuggestionsUrl();
|
||||
QByteArray getSuggestionsParameters();
|
||||
|
||||
OpenSearchEngineDelegate* delegate() const;
|
||||
void setDelegate(OpenSearchEngineDelegate* delegate);
|
||||
|
||||
bool operator==(const OpenSearchEngine &other) const;
|
||||
bool operator<(const OpenSearchEngine &other) const;
|
||||
|
||||
public slots:
|
||||
void requestSuggestions(const QString &searchTerm);
|
||||
void requestSearchResults(const QString &searchTerm);
|
||||
|
||||
protected:
|
||||
static QString parseTemplate(const QString &searchTerm, const QString &searchTemplate);
|
||||
void loadImage() const;
|
||||
|
||||
private slots:
|
||||
void imageObtained();
|
||||
void suggestionsObtained();
|
||||
|
||||
private:
|
||||
QString m_name;
|
||||
QString m_description;
|
||||
|
||||
QString m_imageUrl;
|
||||
QImage m_image;
|
||||
|
||||
QString m_searchUrlTemplate;
|
||||
QString m_suggestionsUrlTemplate;
|
||||
Parameters m_searchParameters;
|
||||
Parameters m_suggestionsParameters;
|
||||
QString m_searchMethod;
|
||||
QString m_suggestionsMethod;
|
||||
|
||||
QByteArray m_preparedSuggestionsParameters;
|
||||
QString m_preparedSuggestionsUrl;
|
||||
|
||||
QMap<QString, QNetworkAccessManager::Operation> m_requestMethods;
|
||||
|
||||
QNetworkReply* m_suggestionsReply;
|
||||
|
||||
OpenSearchEngineDelegate* m_delegate;
|
||||
};
|
||||
|
||||
#endif // OPENSEARCHENGINE_H
|
||||
|
43
interface/src/opensearch/opensearchenginedelegate.cpp
Normal file
43
interface/src/opensearch/opensearchenginedelegate.cpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
#include "opensearchenginedelegate.h"
|
||||
|
||||
/*!
|
||||
\class OpenSearchEngineDelegate
|
||||
\brief An abstract class providing custom processing of specific activities.
|
||||
|
||||
OpenSearchEngineDelegate is an abstract class that can be subclassed and set on
|
||||
an OpenSearchEngine. It allows to customize some parts of the default implementation
|
||||
or even extend it with missing bits.
|
||||
|
||||
Currently subclasses can only provide a custom way of handling search requests by
|
||||
reimplementing the performSearchRequest() method.
|
||||
|
||||
\sa OpenSearchEngine
|
||||
*/
|
||||
|
||||
/*!
|
||||
Constructs the delegate.
|
||||
*/
|
||||
OpenSearchEngineDelegate::OpenSearchEngineDelegate()
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Destructs the delegate.
|
||||
*/
|
||||
OpenSearchEngineDelegate::~OpenSearchEngineDelegate()
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn void performSearchRequest(const QNetworkRequest &request,
|
||||
QNetworkAccessManager::Operation operation, const QByteArray &data) = 0
|
||||
|
||||
This method will be used after OpenSearchEngine::requestResults() is called.
|
||||
|
||||
For example, a console application that uses the OpenSearchEngine class to generate
|
||||
a search URL for a search term supplied by the user would reimplement the function
|
||||
and forward the request to e.g. a web browser.
|
||||
|
||||
Likewise, a web browser that uses the OpenSearchEngine class to support multiple search
|
||||
engines e.g. in a toolbar would perform the request and navigate to the search results site.
|
||||
*/
|
18
interface/src/opensearch/opensearchenginedelegate.h
Normal file
18
interface/src/opensearch/opensearchenginedelegate.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
#ifndef OPENSEARCHENGINEDELEGATE_H
|
||||
#define OPENSEARCHENGINEDELEGATE_H
|
||||
|
||||
#include <qnetworkaccessmanager.h>
|
||||
#include <qnetworkrequest.h>
|
||||
|
||||
class OpenSearchEngineDelegate
|
||||
{
|
||||
public:
|
||||
OpenSearchEngineDelegate();
|
||||
virtual ~OpenSearchEngineDelegate();
|
||||
|
||||
virtual void performSearchRequest(const QNetworkRequest &request,
|
||||
QNetworkAccessManager::Operation operation,
|
||||
const QByteArray &data) = 0;
|
||||
};
|
||||
|
||||
#endif // OPENSEARCHENGINEDELEGATE_H
|
Loading…
Reference in a new issue