diff options
Diffstat (limited to 'src/urlbar/urlsuggester.cpp')
-rw-r--r-- | src/urlbar/urlsuggester.cpp | 397 |
1 files changed, 397 insertions, 0 deletions
diff --git a/src/urlbar/urlsuggester.cpp b/src/urlbar/urlsuggester.cpp new file mode 100644 index 00000000..52ba7640 --- /dev/null +++ b/src/urlbar/urlsuggester.cpp @@ -0,0 +1,397 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2009-2012 by Andrea Diamantini <adjam7 at gmail dot com> +* +* +* 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 <http://www.gnu.org/licenses/>. +* +* ============================================================ */ + + +// Self Includes +#include "urlsuggester.h" +#include "urlsuggester.moc" + +// Local Includes +#include "historymanager.h" +#include "bookmarkmanager.h" + +#include "searchengine.h" + +// KDE Includes +#include <KBookmark> +#include <KService> +#include <KProtocolInfo> + +// Qt Includes +#include <QByteArray> + + +// NOTE +// default kurifilter plugin list (at least in my box): +// 1. "kshorturifilter" +// 2. "kurisearchfilter" +// 3. "localdomainurifilter" +// 4 ."kuriikwsfilter" +// 5. "fixhosturifilter" + + +// ------------------------------------------------------------------------ + + +// NOTE +// Induce an order in the history items +bool isHistoryItemRelevant(const HistoryItem &a, const HistoryItem &b) +{ + return a.relevance() > b.relevance(); +} + + +// ------------------------------------------------------------------------ + + +QRegExp UrlSuggester::_browseRegexp; +QRegExp UrlSuggester::_searchEnginesRegexp; + + +UrlSuggester::UrlSuggester(const QString &typedUrl) + : QObject() + , _typedString(typedUrl.trimmed()) +{ + if (_browseRegexp.isEmpty()) + { + QString protocol = QString("^(%1)").arg(KProtocolInfo::protocols().join("|")); + + QString localhost = QL1S("^localhost"); + + QString local = QL1S("^/"); + + QString ipv4 = QL1S("^0*([1-9]?\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.0*([1-9]?\\d|1\\d\\d|2[0-4]\\d|25[0-5])"\ + "\\.0*([1-9]?\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.0*([1-9]?\\d|1\\d\\d|2[0-4]\\d|25[0-5])"); + + QString ipv6 = QL1S("^([0-9a-fA-F]{4}|0)(\\:([0-9a-fA-F]{4}|0)){7}"); + + QString address = QL1S("[\\d\\w-.]+\\.(a[cdefgilmnoqrstuwz]|b[abdefghijmnorstvwyz]|"\ + "c[acdfghiklmnoruvxyz]|d[ejkmnoz]|e[ceghrstu]|f[ijkmnor]|g[abdefghilmnpqrstuwy]|"\ + "h[kmnrtu]|i[delmnoqrst]|j[emop]|k[eghimnprwyz]|l[abcikrstuvy]|"\ + "m[acdghklmnopqrstuvwxyz]|n[acefgilopruz]|om|p[aefghklmnrstwy]|qa|r[eouw]|"\ + "s[abcdeghijklmnortuvyz]|t[cdfghjkmnoprtvwz]|u[augkmsyz]|v[aceginu]|w[fs]|"\ + "y[etu]|z[amw]|aero|arpa|biz|com|coop|edu|info|int|gov|local|mil|museum|name|net|org|"\ + "pro)"); + + QString joiner = QL1S(")|("); + _browseRegexp = QRegExp(QL1C('(') + + protocol + joiner + + localhost + joiner + + local + joiner + + address + joiner + + ipv6 + joiner + + ipv4 + QL1C(')') + ); + } + + if (_searchEnginesRegexp.isEmpty()) + { + QString reg; + QString engineUrl; + Q_FOREACH(KService::Ptr s, SearchEngine::favorites()) + { + engineUrl = QRegExp::escape(s->property("Query").toString()).replace(QL1S("\\\\\\{@\\}"), QL1S("[\\d\\w-.]+")); + if (reg.isEmpty()) + reg = QL1C('(') + engineUrl + QL1C(')'); + else + reg = reg + QL1S("|(") + engineUrl + QL1C(')'); + } + _searchEnginesRegexp = QRegExp(reg); + } +} + + +UrlSuggestionList UrlSuggester::orderedSearchItems() +{ + if (_typedString.startsWith(QL1S("about:"))) + { + QStringList aboutUrlList; + aboutUrlList + << QL1S("about:home") + << QL1S("about:favorites") + << QL1S("about:closedTabs") + << QL1S("about:bookmarks") + << QL1S("about:history") + << QL1S("about:downloads") + << QL1S("about:tabs") + << QL1S("about:info"); + + QStringList aboutUrlResults = aboutUrlList.filter(_typedString, Qt::CaseInsensitive); + + UrlSuggestionList list; + + if (aboutUrlResults.isEmpty()) + { + UrlSuggestionItem info(UrlSuggestionItem::Browse, QL1S("about:info"), QL1S("info")); + list << info; + + return list; + } + + Q_FOREACH(const QString & urlResult, aboutUrlResults) + { + QString name = urlResult; + name.remove(0, 6); + UrlSuggestionItem item(UrlSuggestionItem::Browse, urlResult, name); + list << item; + } + + return list; + } + + //compute lists + computeHistory(); + computeQurlFromUserInput(); + computeWebSearches(); + computeBookmarks(); + + return orderLists(); +} + + +UrlSuggestionList UrlSuggester::orderLists() +{ + // NOTE + // The const int here decides the number of proper suggestions, taken from history & bookmarks + // You have to add here the "browse & search" options, always available. + const int availableEntries = 8; + + bool webSearchFirst = false; + // Browse & Search results + UrlSuggestionList browseSearch; + QString lowerTypedString = _typedString.toLower(); + if (_browseRegexp.indexIn(lowerTypedString) != -1) + { + webSearchFirst = true; + browseSearch << _webSearches; + } + else + { + browseSearch << _webSearches; + browseSearch << _qurlFromUserInput; + } + + + // find relevant items (the one you are more probably searching...) + UrlSuggestionList relevant; + + // history + Q_FOREACH(const UrlSuggestionItem & item, _history) + { + QString hst = KUrl(item.url).host(); + if (item.url.startsWith(_typedString) + || hst.startsWith(_typedString) + || hst.remove("www.").startsWith(_typedString)) + { + relevant << item; + _history.removeOne(item); + break; + } + } + + // bookmarks + Q_FOREACH(const UrlSuggestionItem & item, _bookmarks) + { + QString hst = KUrl(item.url).host(); + if (item.url.startsWith(_typedString) + || hst.startsWith(_typedString) + || hst.remove("www.").startsWith(_typedString)) + { + relevant << item; + _bookmarks.removeOne(item); + break; + } + } + + // decide history & bookmarks number + int historyCount = _history.count(); + int bookmarksCount = _bookmarks.count(); + int relevantCount = relevant.count(); + + const int historyEntries = (availableEntries - relevantCount) / 2; + const int bookmarksEntries = availableEntries - relevantCount - historyEntries; + + if (historyCount >= historyEntries && bookmarksCount >= bookmarksEntries) + { + _history = _history.mid(0, historyEntries); + _bookmarks = _bookmarks.mid(0, bookmarksEntries); + } + else if (historyCount < historyEntries && bookmarksCount >= bookmarksEntries) + { + if (historyCount + bookmarksCount > availableEntries) + { + _bookmarks = _bookmarks.mid(0, availableEntries - historyCount); + } + } + else if (historyCount >= historyEntries && bookmarksCount < bookmarksEntries) + { + if (historyCount + bookmarksCount > availableEntries) + { + _history = _history.mid(0, availableEntries - bookmarksCount); + } + } + + // and finally, results + UrlSuggestionList list; + + if (webSearchFirst) + list << _qurlFromUserInput; + list += relevant + browseSearch + _history + _bookmarks; + return list; +} + + +////////////////////////////////////////////////////////////////////////// +// PRIVATE ENGINES + + +// QUrl from User Input (easily the best solution... ) +void UrlSuggester::computeQurlFromUserInput() +{ + QString url = _typedString; + QUrl urlFromUserInput = QUrl::fromUserInput(url); + if (urlFromUserInput.isValid()) + { + // ensure http(s) hosts are lower cases + if (urlFromUserInput.scheme().startsWith(QL1S("http"))) + { + QString hst = urlFromUserInput.host(); + urlFromUserInput.setHost(hst.toLower()); + } + + QString urlString = urlFromUserInput.toString(); + QString gTitle = i18nc("Browse a website", "Browse"); + UrlSuggestionItem gItem(UrlSuggestionItem::Browse, urlString, gTitle); + _qurlFromUserInput << gItem; + } +} + + +// webSearches +void UrlSuggester::computeWebSearches() +{ + QString query = _typedString; + KService::Ptr engine = SearchEngine::fromString(_typedString); + if (engine) + { + query = query.remove(0, _typedString.indexOf(SearchEngine::delimiter()) + 1); + + UrlSuggestionItem item = UrlSuggestionItem(UrlSuggestionItem::Search, SearchEngine::buildQuery(engine, query), query); + UrlSuggestionList list; + list << item; + _webSearches = list; + } +} + + +// history +void UrlSuggester::computeHistory() +{ + QList<HistoryItem> found = HistoryManager::self()->find(_typedString); + qSort(found.begin(), found.end(), isHistoryItemRelevant); + + Q_FOREACH(const HistoryItem & i, found) + { + if (_searchEnginesRegexp.isEmpty() || _searchEnginesRegexp.indexIn(i.url) == -1) //filter all urls that are search engine results + { + UrlSuggestionItem gItem(UrlSuggestionItem::History, i.url, i.title); + _history << gItem; + } + } +} + + +// bookmarks +void UrlSuggester::computeBookmarks() +{ + QList<KBookmark> found = BookmarkManager::self()->find(_typedString); + Q_FOREACH(const KBookmark & b, found) + { + UrlSuggestionItem gItem(UrlSuggestionItem::Bookmark, b.url().url(), b.fullText()); + _bookmarks << gItem; + } +} + + +// opensearch suggestion +void UrlSuggester::computeSuggestions() +{ + // NOTE + // This attempt basically cuts out open search suggestions. + UrlSuggestionList list; + emit suggestionsReady(list, _typedString); + return; + +// // if a string startsWith /, it is probably a local path +// // so, no need for suggestions... +// if (_typedString.startsWith('/') || !rApp->opensearchManager()->isSuggestionAvailable()) +// { +// UrlSuggestionList list; +// emit suggestionsReady(list, _typedString); +// return; +// } +// +// QString query = _typedString; +// KService::Ptr engine = SearchEngine::fromString(_typedString); +// if (engine) +// { +// query = query.remove(0, _typedString.indexOf(SearchEngine::delimiter()) + 1); +// setSearchEngine(engine); +// } +// +// connect(rApp->opensearchManager(), +// SIGNAL(suggestionsReceived(QString,ResponseList)), +// this, +// SLOT(suggestionsReceived(QString,ResponseList))); +// +// _typedQuery = query; +// rApp->opensearchManager()->requestSuggestion(query); +} + + +// void UrlSuggester::suggestionsReceived(const QString &text, const ResponseList &suggestions) +// { +// if (text != _typedString) +// return; +// +// UrlSuggestionList sugList; +// QString urlString; +// Q_FOREACH(const Response & i, suggestions) +// { +// if (text == i.title) +// continue; +// +// urlString = i.url; +// if (urlString.isEmpty()) +// { +// urlString = SearchEngine::buildQuery(UrlSuggester::searchEngine(), i.title); +// } +// +// UrlSuggestionItem gItem(UrlSuggestionItem::Suggestion, urlString, i.title, i.description, i.image, i.image_width, i.image_height); +// sugList << gItem; +// } +// emit suggestionsReady(sugList, _typedString); +// this->deleteLater(); +// } |