File indexing completed on 2024-12-22 04:41:07
0001 /* ============================================================ 0002 * Falkon - Qt web browser 0003 * Copyright (C) 2014-2018 David Rosca <nowrep@gmail.com> 0004 * 0005 * This program is free software: you can redistribute it and/or modify 0006 * it under the terms of the GNU General Public License as published by 0007 * the Free Software Foundation, either version 3 of the License, or 0008 * (at your option) any later version. 0009 * 0010 * This program is distributed in the hope that it will be useful, 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0013 * GNU General Public License for more details. 0014 * 0015 * You should have received a copy of the GNU General Public License 0016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 0017 * ============================================================ */ 0018 #include "locationcompleterrefreshjob.h" 0019 #include "locationcompletermodel.h" 0020 #include "mainapplication.h" 0021 #include "bookmarkitem.h" 0022 #include "iconprovider.h" 0023 #include "sqldatabase.h" 0024 #include "qzsettings.h" 0025 #include "bookmarks.h" 0026 #include "qztools.h" 0027 0028 #include <algorithm> 0029 0030 #include <QDateTime> 0031 0032 #include <QtConcurrent/QtConcurrentRun> 0033 0034 LocationCompleterRefreshJob::LocationCompleterRefreshJob(const QString &searchString) 0035 : QObject() 0036 , m_timestamp(QDateTime::currentMSecsSinceEpoch()) 0037 , m_searchString(searchString) 0038 , m_jobCancelled(false) 0039 { 0040 m_watcher = new QFutureWatcher<void>(this); 0041 connect(m_watcher, &QFutureWatcherBase::finished, this, &LocationCompleterRefreshJob::slotFinished); 0042 0043 QFuture<void> future = QtConcurrent::run(&LocationCompleterRefreshJob::runJob, this); 0044 m_watcher->setFuture(future); 0045 } 0046 0047 qint64 LocationCompleterRefreshJob::timestamp() const 0048 { 0049 return m_timestamp; 0050 } 0051 0052 QString LocationCompleterRefreshJob::searchString() const 0053 { 0054 return m_searchString; 0055 } 0056 0057 bool LocationCompleterRefreshJob::isCanceled() const 0058 { 0059 return m_jobCancelled; 0060 } 0061 0062 QList<QStandardItem*> LocationCompleterRefreshJob::completions() const 0063 { 0064 return m_items; 0065 } 0066 0067 QString LocationCompleterRefreshJob::domainCompletion() const 0068 { 0069 return m_domainCompletion; 0070 } 0071 0072 void LocationCompleterRefreshJob::jobCancelled() 0073 { 0074 m_jobCancelled = true; 0075 } 0076 0077 void LocationCompleterRefreshJob::slotFinished() 0078 { 0079 Q_EMIT finished(); 0080 } 0081 0082 static bool countBiggerThan(const QStandardItem* i1, const QStandardItem* i2) 0083 { 0084 int i1Count = i1->data(LocationCompleterModel::CountRole).toInt(); 0085 int i2Count = i2->data(LocationCompleterModel::CountRole).toInt(); 0086 return i1Count > i2Count; 0087 } 0088 0089 void LocationCompleterRefreshJob::runJob() 0090 { 0091 if (m_jobCancelled || mApp->isClosing() || !mApp) { 0092 return; 0093 } 0094 0095 if (m_searchString.isEmpty()) { 0096 completeMostVisited(); 0097 } 0098 else { 0099 completeFromHistory(); 0100 } 0101 0102 // Load all icons into QImage 0103 for (QStandardItem* item : std::as_const(m_items)) { 0104 if (m_jobCancelled) { 0105 return; 0106 } 0107 0108 const QUrl url = item->data(LocationCompleterModel::UrlRole).toUrl(); 0109 item->setData(IconProvider::imageForUrl(url), LocationCompleterModel::ImageRole); 0110 } 0111 0112 if (m_jobCancelled) { 0113 return; 0114 } 0115 0116 // Get domain completion 0117 if (!m_searchString.isEmpty() && qzSettings->useInlineCompletion) { 0118 QSqlQuery domainQuery = LocationCompleterModel::createDomainQuery(m_searchString); 0119 if (!domainQuery.lastQuery().isEmpty()) { 0120 domainQuery.exec(); 0121 if (domainQuery.next()) { 0122 m_domainCompletion = createDomainCompletion(domainQuery.value(0).toUrl().host()); 0123 } 0124 } 0125 } 0126 0127 if (m_jobCancelled) { 0128 return; 0129 } 0130 0131 // Add search/visit item 0132 if (!m_searchString.isEmpty()) { 0133 auto* item = new QStandardItem(); 0134 item->setText(m_searchString); 0135 item->setData(m_searchString, LocationCompleterModel::UrlRole); 0136 item->setData(m_searchString, LocationCompleterModel::SearchStringRole); 0137 item->setData(true, LocationCompleterModel::VisitSearchItemRole); 0138 if (!m_domainCompletion.isEmpty()) { 0139 const QUrl url = QUrl(QSL("http://%1").arg(m_domainCompletion)); 0140 item->setData(IconProvider::imageForDomain(url), LocationCompleterModel::ImageRole); 0141 } 0142 m_items.prepend(item); 0143 } 0144 } 0145 0146 void LocationCompleterRefreshJob::completeFromHistory() 0147 { 0148 QList<QUrl> urlList; 0149 Type showType = (Type) qzSettings->showLocationSuggestions; 0150 0151 // Search in bookmarks 0152 if (showType == HistoryAndBookmarks || showType == Bookmarks) { 0153 const int bookmarksLimit = 10; 0154 const QList<BookmarkItem*> bookmarks = mApp->bookmarks()->searchBookmarks(m_searchString, bookmarksLimit); 0155 0156 for (BookmarkItem* bookmark : bookmarks) { 0157 Q_ASSERT(bookmark->isUrl()); 0158 0159 // Keyword bookmark replaces visit/search item 0160 if (bookmark->keyword() == m_searchString) { 0161 continue; 0162 } 0163 0164 auto* item = new QStandardItem(); 0165 item->setText(QString::fromUtf8(bookmark->url().toEncoded())); 0166 item->setData(-1, LocationCompleterModel::IdRole); 0167 item->setData(bookmark->title(), LocationCompleterModel::TitleRole); 0168 item->setData(bookmark->url(), LocationCompleterModel::UrlRole); 0169 item->setData(bookmark->visitCount(), LocationCompleterModel::CountRole); 0170 item->setData(true, LocationCompleterModel::BookmarkRole); 0171 item->setData(QVariant::fromValue<void*>(static_cast<void*>(bookmark)), LocationCompleterModel::BookmarkItemRole); 0172 item->setData(m_searchString, LocationCompleterModel::SearchStringRole); 0173 0174 urlList.append(bookmark->url()); 0175 m_items.append(item); 0176 } 0177 } 0178 0179 // Sort by count 0180 std::sort(m_items.begin(), m_items.end(), countBiggerThan); 0181 0182 // Search in history 0183 if (showType == HistoryAndBookmarks || showType == History) { 0184 const int historyLimit = 20; 0185 QSqlQuery query = LocationCompleterModel::createHistoryQuery(m_searchString, historyLimit); 0186 query.exec(); 0187 0188 while (query.next()) { 0189 const QUrl url = query.value(1).toUrl(); 0190 0191 if (urlList.contains(url)) { 0192 continue; 0193 } 0194 0195 auto* item = new QStandardItem(); 0196 item->setText(QString::fromUtf8(url.toEncoded())); 0197 item->setData(query.value(0), LocationCompleterModel::IdRole); 0198 item->setData(query.value(2), LocationCompleterModel::TitleRole); 0199 item->setData(url, LocationCompleterModel::UrlRole); 0200 item->setData(query.value(3), LocationCompleterModel::CountRole); 0201 item->setData(true, LocationCompleterModel::HistoryRole); 0202 item->setData(m_searchString, LocationCompleterModel::SearchStringRole); 0203 0204 m_items.append(item); 0205 } 0206 } 0207 } 0208 0209 void LocationCompleterRefreshJob::completeMostVisited() 0210 { 0211 QSqlQuery query(SqlDatabase::instance()->database()); 0212 query.exec(QSL("SELECT id, url, title FROM history ORDER BY count DESC LIMIT 15")); 0213 0214 while (query.next()) { 0215 auto* item = new QStandardItem(); 0216 const QUrl url = query.value(1).toUrl(); 0217 0218 item->setText(QString::fromUtf8(url.toEncoded())); 0219 item->setData(query.value(0), LocationCompleterModel::IdRole); 0220 item->setData(query.value(2), LocationCompleterModel::TitleRole); 0221 item->setData(url, LocationCompleterModel::UrlRole); 0222 item->setData(true, LocationCompleterModel::HistoryRole); 0223 0224 m_items.append(item); 0225 } 0226 } 0227 0228 QString LocationCompleterRefreshJob::createDomainCompletion(const QString &completion) const 0229 { 0230 // Make sure search string and completion matches 0231 0232 if (m_searchString.startsWith(QL1S("www.")) && !completion.startsWith(QL1S("www."))) { 0233 return QL1S("www.") + completion; 0234 } 0235 0236 if (!m_searchString.startsWith(QL1S("www.")) && completion.startsWith(QL1S("www."))) { 0237 return completion.mid(4); 0238 } 0239 0240 return completion; 0241 }