diff --git a/src/lib/navigation/completer/locationcompleterdelegate.cpp b/src/lib/navigation/completer/locationcompleterdelegate.cpp index 7859d534..9d2499f9 100644 --- a/src/lib/navigation/completer/locationcompleterdelegate.cpp +++ b/src/lib/navigation/completer/locationcompleterdelegate.cpp @@ -1,364 +1,364 @@ /* ============================================================ * Falkon - Qt web browser * Copyright (C) 2010-2018 David Rosca * * 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 3 of the License, or * (at your option) any later version. * * 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 "locationcompleterdelegate.h" #include "locationcompletermodel.h" #include "locationbar.h" #include "iconprovider.h" #include "qzsettings.h" #include "mainapplication.h" #include "bookmarkitem.h" #include #include #include #include #include LocationCompleterDelegate::LocationCompleterDelegate(QObject *parent) : QStyledItemDelegate(parent) , m_rowHeight(0) , m_padding(0) { } void LocationCompleterDelegate::paint(QPainter* painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { QStyleOptionViewItem opt = option; initStyleOption(&opt, index); const QWidget* w = opt.widget; const QStyle* style = w ? w->style() : QApplication::style(); const int height = opt.rect.height(); const int center = height / 2 + opt.rect.top(); // Prepare link font QFont linkFont = opt.font; linkFont.setPointSize(linkFont.pointSize() - 1); const QFontMetrics linkMetrics(linkFont); int leftPosition = m_padding * 2; int rightPosition = opt.rect.right() - m_padding; opt.state |= QStyle::State_Active; const QIcon::Mode iconMode = opt.state & QStyle::State_Selected ? QIcon::Selected : QIcon::Normal; const QPalette::ColorRole colorRole = opt.state & QStyle::State_Selected ? QPalette::HighlightedText : QPalette::Text; const QPalette::ColorRole colorLinkRole = opt.state & QStyle::State_Selected ? QPalette::HighlightedText : QPalette::Link; #ifdef Q_OS_WIN opt.palette.setColor(QPalette::All, QPalette::HighlightedText, opt.palette.color(QPalette::Active, QPalette::Text)); opt.palette.setColor(QPalette::All, QPalette::Highlight, opt.palette.base().color().darker(108)); #endif QPalette textPalette = opt.palette; textPalette.setCurrentColorGroup(opt.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled); // Draw background style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, w); const bool isVisitSearchItem = index.data(LocationCompleterModel::VisitSearchItemRole).toBool(); const bool isSearchSuggestion = index.data(LocationCompleterModel::SearchSuggestionRole).toBool(); LocationBar::LoadAction loadAction; bool isWebSearch = isSearchSuggestion; BookmarkItem *bookmark = static_cast(index.data(LocationCompleterModel::BookmarkItemRole).value()); if (isVisitSearchItem) { loadAction = LocationBar::loadAction(index.data(LocationCompleterModel::SearchStringRole).toString()); isWebSearch = loadAction.type == LocationBar::LoadAction::Search; if (!m_forceVisitItem) { bookmark = loadAction.bookmark; } } // Draw icon const int iconSize = 16; const int iconYPos = center - (iconSize / 2); QRect iconRect(leftPosition, iconYPos, iconSize, iconSize); QPixmap pixmap = index.data(Qt::DecorationRole).value().pixmap(iconSize); if (isSearchSuggestion || (isVisitSearchItem && isWebSearch)) { pixmap = QIcon::fromTheme(QSL("edit-find"), QIcon(QSL(":icons/menu/search-icon.svg"))).pixmap(iconSize, iconMode); } if (isVisitSearchItem && bookmark) { pixmap = bookmark->icon().pixmap(iconSize); } else if (loadAction.type == LocationBar::LoadAction::Search) { if (loadAction.searchEngine.name != LocationBar::searchEngine().name) { pixmap = loadAction.searchEngine.icon.pixmap(iconSize); } } painter->drawPixmap(iconRect, pixmap); leftPosition = iconRect.right() + m_padding * 2; // Draw star to bookmark items int starPixmapWidth = 0; if (bookmark) { const QIcon icon = IconProvider::instance()->bookmarkIcon(); const QSize starSize(16, 16); starPixmapWidth = starSize.width(); QPoint pos(rightPosition - starPixmapWidth, center - starSize.height() / 2); QRect starRect(pos, starSize); painter->drawPixmap(starRect, icon.pixmap(starSize, iconMode)); } QString searchText = index.data(LocationCompleterModel::SearchStringRole).toString(); // Draw title leftPosition += 2; QRect titleRect(leftPosition, center - opt.fontMetrics.height() / 2, opt.rect.width() * 0.6, opt.fontMetrics.height()); QString title = index.data(LocationCompleterModel::TitleRole).toString(); painter->setFont(opt.font); if (isVisitSearchItem) { if (bookmark) { title = bookmark->title(); } else { title = index.data(LocationCompleterModel::SearchStringRole).toString(); searchText.clear(); } } leftPosition += viewItemDrawText(painter, &opt, titleRect, title, textPalette.color(colorRole), searchText); leftPosition += m_padding * 2; // Trim link to maximum number of characters that can be visible, otherwise there may be perf issue with huge URLs const int maxChars = (opt.rect.width() - leftPosition) / opt.fontMetrics.width(QL1C('i')); QString link; const QByteArray linkArray = index.data(Qt::DisplayRole).toByteArray(); if (!linkArray.startsWith("data") && !linkArray.startsWith("javascript")) { link = QString::fromUtf8(QByteArray::fromPercentEncoding(linkArray)).left(maxChars); } else { link = QString::fromLatin1(linkArray.left(maxChars)); } if (isVisitSearchItem || isSearchSuggestion) { if (!opt.state.testFlag(QStyle::State_Selected) && !opt.state.testFlag(QStyle::State_MouseOver)) { link.clear(); } else if (isVisitSearchItem && (!isWebSearch || m_forceVisitItem)) { link = tr("Visit"); } else { QString searchEngineName = loadAction.searchEngine.name; if (searchEngineName.isEmpty()) { searchEngineName = LocationBar::searchEngine().name; } link = tr("Search with %1").arg(searchEngineName); } } if (bookmark) { link = bookmark->url().toString(); } // Draw separator if (!link.isEmpty()) { QChar separator = QL1C('-'); QRect separatorRect(leftPosition, center - linkMetrics.height() / 2, linkMetrics.width(separator), linkMetrics.height()); style->drawItemText(painter, separatorRect, Qt::AlignCenter, textPalette, true, separator, colorRole); leftPosition += separatorRect.width() + m_padding * 2; } // Draw link const int leftLinkEdge = leftPosition; const int rightLinkEdge = rightPosition - m_padding - starPixmapWidth; QRect linkRect(leftLinkEdge, center - linkMetrics.height() / 2, rightLinkEdge - leftLinkEdge, linkMetrics.height()); painter->setFont(linkFont); // Draw url (or switch to tab) int tabPos = index.data(LocationCompleterModel::TabPositionTabRole).toInt(); if (qzSettings->showSwitchTab && !m_forceVisitItem && tabPos != -1) { const QIcon tabIcon = QIcon(QSL(":icons/menu/tab.svg")); QRect iconRect(linkRect); iconRect.setX(iconRect.x()); iconRect.setWidth(16); painter->drawPixmap(iconRect, tabIcon.pixmap(iconRect.size(), iconMode)); QRect textRect(linkRect); textRect.setX(textRect.x() + m_padding + 16 + m_padding); viewItemDrawText(painter, &opt, textRect, tr("Switch to tab"), textPalette.color(colorLinkRole)); } else if (isVisitSearchItem || isSearchSuggestion) { viewItemDrawText(painter, &opt, linkRect, link, textPalette.color(colorLinkRole)); } else { viewItemDrawText(painter, &opt, linkRect, link, textPalette.color(colorLinkRole), searchText); } // Draw line at the very bottom of item if the item is not highlighted if (!(opt.state & QStyle::State_Selected)) { QRect lineRect(opt.rect.left(), opt.rect.bottom(), opt.rect.width(), 1); painter->fillRect(lineRect, opt.palette.color(QPalette::AlternateBase)); } } QSize LocationCompleterDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { Q_UNUSED(index) if (!m_rowHeight) { QStyleOptionViewItem opt(option); initStyleOption(&opt, index); const QWidget* w = opt.widget; const QStyle* style = w ? w->style() : QApplication::style(); const int padding = style->pixelMetric(QStyle::PM_FocusFrameHMargin, 0) + 1; m_padding = padding > 3 ? padding : 3; m_rowHeight = 4 * m_padding + qMax(16, opt.fontMetrics.height()); } return QSize(200, m_rowHeight); } void LocationCompleterDelegate::setForceVisitItem(bool enable) { m_forceVisitItem = enable; } static bool sizeBiggerThan(const QString &s1, const QString &s2) { return s1.size() > s2.size(); } static QSizeF viewItemTextLayout(QTextLayout &textLayout, int lineWidth) { qreal height = 0; qreal widthUsed = 0; textLayout.beginLayout(); QTextLine line = textLayout.createLine(); if (line.isValid()) { line.setLineWidth(lineWidth); line.setPosition(QPointF(0, height)); height += line.height(); widthUsed = qMax(widthUsed, line.naturalTextWidth()); textLayout.endLayout(); } return QSizeF(widthUsed, height); } // most of codes taken from QCommonStylePrivate::viewItemDrawText() // added highlighting and simplified for single-line textlayouts int LocationCompleterDelegate::viewItemDrawText(QPainter *p, const QStyleOptionViewItem *option, const QRect &rect, const QString &text, const QColor &color, const QString &searchText) const { if (text.isEmpty()) { return 0; } const QFontMetrics fontMetrics(p->font()); QString elidedText = fontMetrics.elidedText(text, option->textElideMode, rect.width()); QTextOption textOption; textOption.setWrapMode(QTextOption::NoWrap); textOption.setAlignment(QStyle::visualAlignment(textOption.textDirection(), option->displayAlignment)); QTextLayout textLayout; textLayout.setFont(p->font()); textLayout.setText(elidedText); textLayout.setTextOption(textOption); if (!searchText.isEmpty()) { QList delimiters; QStringList searchStrings = searchText.split(QLatin1Char(' '), QString::SkipEmptyParts); // Look for longer parts first std::sort(searchStrings.begin(), searchStrings.end(), sizeBiggerThan); - foreach (const QString &string, searchStrings) { + for (const QString &string : qAsConst(searchStrings)) { int delimiter = text.indexOf(string, 0, Qt::CaseInsensitive); while (delimiter != -1) { int start = delimiter; int end = delimiter + string.length(); bool alreadyContains = false; for (int i = 0; i < delimiters.count(); ++i) { int dStart = delimiters.at(i); int dEnd = delimiters.at(++i); if (dStart <= start && dEnd >= end) { alreadyContains = true; break; } } if (!alreadyContains) { delimiters.append(start); delimiters.append(end); } delimiter = text.indexOf(string, end, Qt::CaseInsensitive); } } // We need to sort delimiters to properly paint all parts that user typed std::sort(delimiters.begin(), delimiters.end()); // If we don't find any match, just paint it without any highlight if (!delimiters.isEmpty() && !(delimiters.count() % 2)) { QList highlightParts; while (!delimiters.isEmpty()) { QTextLayout::FormatRange highlightedPart; int start = delimiters.takeFirst(); int end = delimiters.takeFirst(); highlightedPart.start = start; highlightedPart.length = end - start; highlightedPart.format.setFontWeight(QFont::Bold); highlightedPart.format.setUnderlineStyle(QTextCharFormat::SingleUnderline); highlightParts << highlightedPart; } textLayout.setAdditionalFormats(highlightParts); } } // do layout viewItemTextLayout(textLayout, rect.width()); if (textLayout.lineCount() <= 0) { return 0; } QTextLine textLine = textLayout.lineAt(0); // if elidedText after highlighting is longer // than available width then re-elide it and redo layout int diff = textLine.naturalTextWidth() - rect.width(); if (diff > 0) { elidedText = fontMetrics.elidedText(elidedText, option->textElideMode, rect.width() - diff); textLayout.setText(elidedText); // redo layout viewItemTextLayout(textLayout, rect.width()); if (textLayout.lineCount() <= 0) { return 0; } textLine = textLayout.lineAt(0); } // draw line p->setPen(color); qreal width = qMax(rect.width(), textLayout.lineAt(0).width()); const QRect &layoutRect = QStyle::alignedRect(option->direction, option->displayAlignment, QSize(int(width), int(textLine.height())), rect); const QPointF &position = layoutRect.topLeft(); textLine.draw(p, position); return qMin(rect.width(), textLayout.lineAt(0).naturalTextWidth()); } diff --git a/src/lib/navigation/completer/locationcompletermodel.cpp b/src/lib/navigation/completer/locationcompletermodel.cpp index 7b4656d4..a69493bc 100644 --- a/src/lib/navigation/completer/locationcompletermodel.cpp +++ b/src/lib/navigation/completer/locationcompletermodel.cpp @@ -1,174 +1,174 @@ /* ============================================================ * Falkon - Qt web browser * Copyright (C) 2010-2017 David Rosca * * 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 3 of the License, or * (at your option) any later version. * * 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 "locationcompletermodel.h" #include "mainapplication.h" #include "iconprovider.h" #include "bookmarkitem.h" #include "bookmarks.h" #include "qzsettings.h" #include "browserwindow.h" #include "tabwidget.h" #include "sqldatabase.h" LocationCompleterModel::LocationCompleterModel(QObject* parent) : QStandardItemModel(parent) { } void LocationCompleterModel::setCompletions(const QList &items) { clear(); addCompletions(items); } void LocationCompleterModel::addCompletions(const QList &items) { for (QStandardItem *item : items) { item->setIcon(QPixmap::fromImage(item->data(ImageRole).value())); setTabPosition(item); if (item->icon().isNull()) { item->setIcon(IconProvider::emptyWebIcon()); } appendRow(QList{item}); } } QList LocationCompleterModel::suggestionItems() const { QList items; for (int i = 0; i < rowCount(); ++i) { QStandardItem *it = item(i); if (it->data(SearchSuggestionRole).toBool()) { items.append(it); } } return items; } QSqlQuery LocationCompleterModel::createDomainQuery(const QString &text) { if (text.isEmpty() || text == QLatin1String("www.")) { return QSqlQuery(SqlDatabase::instance()->database()); } bool withoutWww = text.startsWith(QLatin1Char('w')) && !text.startsWith(QLatin1String("www.")); QString query = "SELECT url FROM history WHERE "; if (withoutWww) { query.append(QLatin1String("url NOT LIKE ? AND url NOT LIKE ? AND ")); } else { query.append(QLatin1String("url LIKE ? OR url LIKE ? OR ")); } query.append(QLatin1String("(url LIKE ? OR url LIKE ?) ORDER BY date DESC LIMIT 1")); QSqlQuery sqlQuery(SqlDatabase::instance()->database()); sqlQuery.prepare(query); if (withoutWww) { sqlQuery.addBindValue(QString("http://www.%")); sqlQuery.addBindValue(QString("https://www.%")); sqlQuery.addBindValue(QString("http://%1%").arg(text)); sqlQuery.addBindValue(QString("https://%1%").arg(text)); } else { sqlQuery.addBindValue(QString("http://%1%").arg(text)); sqlQuery.addBindValue(QString("https://%1%").arg(text)); sqlQuery.addBindValue(QString("http://www.%1%").arg(text)); sqlQuery.addBindValue(QString("https://www.%1%").arg(text)); } return sqlQuery; } QSqlQuery LocationCompleterModel::createHistoryQuery(const QString &searchString, int limit, bool exactMatch) { QStringList searchList; QString query = QLatin1String("SELECT id, url, title, count FROM history WHERE "); if (exactMatch) { query.append(QLatin1String("title LIKE ? OR url LIKE ? ")); } else { searchList = searchString.split(QLatin1Char(' '), QString::SkipEmptyParts); const int slSize = searchList.size(); for (int i = 0; i < slSize; ++i) { query.append(QLatin1String("(title LIKE ? OR url LIKE ?) ")); if (i < slSize - 1) { query.append(QLatin1String("AND ")); } } } query.append(QLatin1String("ORDER BY date DESC LIMIT ?")); QSqlQuery sqlQuery(SqlDatabase::instance()->database()); sqlQuery.prepare(query); if (exactMatch) { sqlQuery.addBindValue(QString("%%1%").arg(searchString)); sqlQuery.addBindValue(QString("%%1%").arg(searchString)); } else { - foreach (const QString &str, searchList) { + for (const QString &str : qAsConst(searchList)) { sqlQuery.addBindValue(QString("%%1%").arg(str)); sqlQuery.addBindValue(QString("%%1%").arg(str)); } } sqlQuery.addBindValue(limit); return sqlQuery; } void LocationCompleterModel::setTabPosition(QStandardItem* item) const { Q_ASSERT(item); item->setData(-1, TabPositionTabRole); if (!qzSettings->showSwitchTab || item->data(VisitSearchItemRole).toBool()) { return; } const QUrl url = item->data(UrlRole).toUrl(); const QList windows = mApp->windows(); - foreach (BrowserWindow* window, windows) { + for (BrowserWindow* window : windows) { QList tabs = window->tabWidget()->allTabs(); for (int i = 0; i < tabs.count(); ++i) { WebTab* tab = tabs.at(i); if (tab->url() == url) { item->setData(QVariant::fromValue(static_cast(window)), TabPositionWindowRole); item->setData(i, TabPositionTabRole); return; } } } } void LocationCompleterModel::refreshTabPositions() const { for (int row = 0; row < rowCount(); ++row) { QStandardItem* itm = item(row); if (itm) { setTabPosition(itm); } } } diff --git a/src/lib/navigation/completer/locationcompleterrefreshjob.cpp b/src/lib/navigation/completer/locationcompleterrefreshjob.cpp index 34c40877..f41b3037 100644 --- a/src/lib/navigation/completer/locationcompleterrefreshjob.cpp +++ b/src/lib/navigation/completer/locationcompleterrefreshjob.cpp @@ -1,241 +1,241 @@ /* ============================================================ * Falkon - Qt web browser * Copyright (C) 2014-2018 David Rosca * * 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 3 of the License, or * (at your option) any later version. * * 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 "locationcompleterrefreshjob.h" #include "locationcompletermodel.h" #include "mainapplication.h" #include "bookmarkitem.h" #include "iconprovider.h" #include "sqldatabase.h" #include "qzsettings.h" #include "bookmarks.h" #include "qztools.h" #include #include #include LocationCompleterRefreshJob::LocationCompleterRefreshJob(const QString &searchString) : QObject() , m_timestamp(QDateTime::currentMSecsSinceEpoch()) , m_searchString(searchString) , m_jobCancelled(false) { m_watcher = new QFutureWatcher(this); connect(m_watcher, &QFutureWatcherBase::finished, this, &LocationCompleterRefreshJob::slotFinished); QFuture future = QtConcurrent::run(this, &LocationCompleterRefreshJob::runJob); m_watcher->setFuture(future); } qint64 LocationCompleterRefreshJob::timestamp() const { return m_timestamp; } QString LocationCompleterRefreshJob::searchString() const { return m_searchString; } bool LocationCompleterRefreshJob::isCanceled() const { return m_jobCancelled; } QList LocationCompleterRefreshJob::completions() const { return m_items; } QString LocationCompleterRefreshJob::domainCompletion() const { return m_domainCompletion; } void LocationCompleterRefreshJob::jobCancelled() { m_jobCancelled = true; } void LocationCompleterRefreshJob::slotFinished() { emit finished(); } static bool countBiggerThan(const QStandardItem* i1, const QStandardItem* i2) { int i1Count = i1->data(LocationCompleterModel::CountRole).toInt(); int i2Count = i2->data(LocationCompleterModel::CountRole).toInt(); return i1Count > i2Count; } void LocationCompleterRefreshJob::runJob() { if (m_jobCancelled || mApp->isClosing() || !mApp) { return; } if (m_searchString.isEmpty()) { completeMostVisited(); } else { completeFromHistory(); } // Load all icons into QImage - foreach (QStandardItem* item, m_items) { + for (QStandardItem* item : qAsConst(m_items)) { if (m_jobCancelled) { return; } const QUrl url = item->data(LocationCompleterModel::UrlRole).toUrl(); item->setData(IconProvider::imageForUrl(url), LocationCompleterModel::ImageRole); } if (m_jobCancelled) { return; } // Get domain completion if (!m_searchString.isEmpty() && qzSettings->useInlineCompletion) { QSqlQuery domainQuery = LocationCompleterModel::createDomainQuery(m_searchString); if (!domainQuery.lastQuery().isEmpty()) { domainQuery.exec(); if (domainQuery.next()) { m_domainCompletion = createDomainCompletion(domainQuery.value(0).toUrl().host()); } } } if (m_jobCancelled) { return; } // Add search/visit item if (!m_searchString.isEmpty()) { QStandardItem* item = new QStandardItem(); item->setText(m_searchString); item->setData(m_searchString, LocationCompleterModel::UrlRole); item->setData(m_searchString, LocationCompleterModel::SearchStringRole); item->setData(true, LocationCompleterModel::VisitSearchItemRole); if (!m_domainCompletion.isEmpty()) { const QUrl url = QUrl(QSL("http://%1").arg(m_domainCompletion)); item->setData(IconProvider::imageForDomain(url), LocationCompleterModel::ImageRole); } m_items.prepend(item); } } void LocationCompleterRefreshJob::completeFromHistory() { QList urlList; Type showType = (Type) qzSettings->showLocationSuggestions; // Search in bookmarks if (showType == HistoryAndBookmarks || showType == Bookmarks) { const int bookmarksLimit = 10; - QList bookmarks = mApp->bookmarks()->searchBookmarks(m_searchString, bookmarksLimit); + const QList bookmarks = mApp->bookmarks()->searchBookmarks(m_searchString, bookmarksLimit); - foreach (BookmarkItem* bookmark, bookmarks) { + for (BookmarkItem* bookmark : bookmarks) { Q_ASSERT(bookmark->isUrl()); // Keyword bookmark replaces visit/search item if (bookmark->keyword() == m_searchString) { continue; } QStandardItem* item = new QStandardItem(); item->setText(bookmark->url().toEncoded()); item->setData(-1, LocationCompleterModel::IdRole); item->setData(bookmark->title(), LocationCompleterModel::TitleRole); item->setData(bookmark->url(), LocationCompleterModel::UrlRole); item->setData(bookmark->visitCount(), LocationCompleterModel::CountRole); item->setData(true, LocationCompleterModel::BookmarkRole); item->setData(QVariant::fromValue(static_cast(bookmark)), LocationCompleterModel::BookmarkItemRole); item->setData(m_searchString, LocationCompleterModel::SearchStringRole); urlList.append(bookmark->url()); m_items.append(item); } } // Sort by count std::sort(m_items.begin(), m_items.end(), countBiggerThan); // Search in history if (showType == HistoryAndBookmarks || showType == History) { const int historyLimit = 20; QSqlQuery query = LocationCompleterModel::createHistoryQuery(m_searchString, historyLimit); query.exec(); while (query.next()) { const QUrl url = query.value(1).toUrl(); if (urlList.contains(url)) { continue; } QStandardItem* item = new QStandardItem(); item->setText(url.toEncoded()); item->setData(query.value(0), LocationCompleterModel::IdRole); item->setData(query.value(2), LocationCompleterModel::TitleRole); item->setData(url, LocationCompleterModel::UrlRole); item->setData(query.value(3), LocationCompleterModel::CountRole); item->setData(true, LocationCompleterModel::HistoryRole); item->setData(m_searchString, LocationCompleterModel::SearchStringRole); m_items.append(item); } } } void LocationCompleterRefreshJob::completeMostVisited() { QSqlQuery query(SqlDatabase::instance()->database()); query.exec(QSL("SELECT id, url, title FROM history ORDER BY count DESC LIMIT 15")); while (query.next()) { QStandardItem* item = new QStandardItem(); const QUrl url = query.value(1).toUrl(); item->setText(url.toEncoded()); item->setData(query.value(0), LocationCompleterModel::IdRole); item->setData(query.value(2), LocationCompleterModel::TitleRole); item->setData(url, LocationCompleterModel::UrlRole); item->setData(true, LocationCompleterModel::HistoryRole); m_items.append(item); } } QString LocationCompleterRefreshJob::createDomainCompletion(const QString &completion) const { // Make sure search string and completion matches if (m_searchString.startsWith(QL1S("www.")) && !completion.startsWith(QL1S("www."))) { return QL1S("www.") + completion; } if (!m_searchString.startsWith(QL1S("www.")) && completion.startsWith(QL1S("www."))) { return completion.mid(4); } return completion; } diff --git a/src/lib/navigation/websearchbar.cpp b/src/lib/navigation/websearchbar.cpp index 09add2d5..bad7cd00 100644 --- a/src/lib/navigation/websearchbar.cpp +++ b/src/lib/navigation/websearchbar.cpp @@ -1,338 +1,339 @@ /* ============================================================ * Falkon - Qt web browser * Copyright (C) 2010-2018 David Rosca * * 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 3 of the License, or * (at your option) any later version. * * 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 "websearchbar.h" #include "browserwindow.h" #include "mainapplication.h" #include "tabbedwebview.h" #include "webpage.h" #include "settings.h" #include "qzsettings.h" #include "tabwidget.h" #include "clickablelabel.h" #include "buttonwithmenu.h" #include "searchenginesmanager.h" #include "searchenginesdialog.h" #include "networkmanager.h" #include "iconprovider.h" #include "scripts.h" #include #include #include #include #include #include #include #include WebSearchBar_Button::WebSearchBar_Button(QWidget* parent) : ClickableLabel(parent) { setObjectName("websearchbar-searchbutton"); setCursor(QCursor(Qt::PointingHandCursor)); setFocusPolicy(Qt::ClickFocus); } void WebSearchBar_Button::contextMenuEvent(QContextMenuEvent* event) { event->accept(); } WebSearchBar::WebSearchBar(BrowserWindow* window) : LineEdit(window) , m_window(window) , m_reloadingEngines(false) { setObjectName("websearchbar"); setDragEnabled(true); m_buttonSearch = new WebSearchBar_Button(this); m_boxSearchType = new ButtonWithMenu(this); m_boxSearchType->setObjectName("websearchbar-searchprovider-combobox"); m_boxSearchType->setFocusProxy(this); // RTL Support // If we don't add 'm_boxSearchType' by following code, then we should use suitable padding-left value // but then, when typing RTL text the layout dynamically changed and within RTL layout direction // padding-left is equivalent to padding-right and vice versa, and because style sheet is // not changed dynamically this create padding problems. addWidget(m_boxSearchType, LineEdit::LeftSide); addWidget(m_buttonSearch, LineEdit::RightSide); connect(m_buttonSearch, &ClickableLabel::clicked, this, &WebSearchBar::search); connect(m_buttonSearch, &ClickableLabel::middleClicked, this, &WebSearchBar::searchInNewTab); connect(m_boxSearchType, &ButtonWithMenu::activeItemChanged, this, &WebSearchBar::searchChanged); setWidgetSpacing(0); m_searchManager = mApp->searchEnginesManager(); connect(m_boxSearchType->menu(), &QMenu::aboutToShow, this, &WebSearchBar::aboutToShowMenu); m_completer = new QCompleter(this); m_completer->setCompletionMode(QCompleter::UnfilteredPopupCompletion); m_completerModel = new QStringListModel(this); m_completer->setModel(m_completerModel); m_completer->popup()->setMinimumHeight(90); setCompleter(m_completer); connect(m_completer->popup(), &QAbstractItemView::activated, this, &WebSearchBar::search); m_openSearchEngine = new OpenSearchEngine(this); m_openSearchEngine->setNetworkAccessManager(mApp->networkManager()); connect(m_openSearchEngine, &OpenSearchEngine::suggestions, this, &WebSearchBar::addSuggestions); connect(this, &QLineEdit::textEdited, m_openSearchEngine, &OpenSearchEngine::requestSuggestions); editAction(PasteAndGo)->setText(tr("Paste And &Search")); editAction(PasteAndGo)->setIcon(QIcon::fromTheme(QSL("edit-paste"))); connect(editAction(PasteAndGo), &QAction::triggered, this, &WebSearchBar::pasteAndGo); QTimer::singleShot(0, this, &WebSearchBar::setupEngines); } void WebSearchBar::aboutToShowMenu() { QMenu* menu = m_boxSearchType->menu(); menu->addSeparator(); m_window->weView()->page()->runJavaScript(Scripts::getOpenSearchLinks(), WebPage::SafeJsWorld, [this, menu](const QVariant &res) { const QVariantList &list = res.toList(); - Q_FOREACH (const QVariant &val, list) { + for (const QVariant &val : list) { const QVariantMap &link = val.toMap(); QUrl url = m_window->weView()->url().resolved(link.value(QSL("url")).toUrl()); QString title = link.value(QSL("title")).toString(); if (url.isEmpty()) continue; if (title.isEmpty()) title = m_window->weView()->title(); menu->addAction(m_window->weView()->icon(), tr("Add %1 ...").arg(title), this, &WebSearchBar::addEngineFromAction)->setData(url); } menu->addSeparator(); menu->addAction(IconProvider::settingsIcon(), tr("Manage Search Engines"), this, &WebSearchBar::openSearchEnginesDialog); }); } void WebSearchBar::addSuggestions(const QStringList &list) { if (qzSettings->showWSBSearchSuggestions) { QStringList list_ = list.mid(0, 6); m_completerModel->setStringList(list_); m_completer->complete(); } } void WebSearchBar::openSearchEnginesDialog() { if (!m_searchDialog) m_searchDialog = new SearchEnginesDialog(this); m_searchDialog->open(); m_searchDialog->raise(); m_searchDialog->activateWindow(); } void WebSearchBar::enableSearchSuggestions(bool enable) { Settings settings; settings.beginGroup("SearchEngines"); settings.setValue("showSuggestions", enable); settings.endGroup(); qzSettings->showWSBSearchSuggestions = enable; m_completerModel->setStringList(QStringList()); } void WebSearchBar::setupEngines() { disconnect(m_searchManager, &SearchEnginesManager::enginesChanged, this, &WebSearchBar::setupEngines); m_reloadingEngines = true; QString activeEngine = m_searchManager->startingEngineName(); if (m_boxSearchType->allItems().count() != 0) { activeEngine = m_activeEngine.name; } m_boxSearchType->clearItems(); - foreach (const SearchEngine &en, m_searchManager->allEngines()) { + const auto engines = m_searchManager->allEngines(); + for (const SearchEngine &en : engines) { ButtonWithMenu::Item item; item.icon = en.icon; item.text = en.name; QVariant v; v.setValue(en); item.userData = v; m_boxSearchType->addItem(item); if (item.text == activeEngine) { m_boxSearchType->setCurrentItem(item, false); } } searchChanged(m_boxSearchType->currentItem()); connect(m_searchManager, &SearchEnginesManager::enginesChanged, this, &WebSearchBar::setupEngines); m_reloadingEngines = false; } void WebSearchBar::searchChanged(const ButtonWithMenu::Item &item) { setPlaceholderText(item.text); m_completerModel->setStringList(QStringList()); m_activeEngine = item.userData.value(); m_openSearchEngine->setSuggestionsUrl(m_activeEngine.suggestionsUrl); m_openSearchEngine->setSuggestionsParameters(m_activeEngine.suggestionsParameters); m_searchManager->setActiveEngine(m_activeEngine); if (qzSettings->searchOnEngineChange && !m_reloadingEngines && !text().isEmpty()) { search(); } } void WebSearchBar::instantSearchChanged(bool enable) { Settings settings; settings.beginGroup("SearchEngines"); settings.setValue("SearchOnEngineChange", enable); settings.endGroup(); qzSettings->searchOnEngineChange = enable; } void WebSearchBar::search() { m_window->weView()->setFocus(); m_window->weView()->load(m_searchManager->searchResult(m_activeEngine, text())); } void WebSearchBar::searchInNewTab() { int index = m_window->tabWidget()->addView(QUrl()); m_window->weView(index)->setFocus(); m_window->weView(index)->load(m_searchManager->searchResult(m_activeEngine, text())); } void WebSearchBar::addEngineFromAction() { if (QAction* action = qobject_cast(sender())) { m_searchManager->addEngine(action->data().toUrl()); } } void WebSearchBar::pasteAndGo() { clear(); paste(); search(); } void WebSearchBar::contextMenuEvent(QContextMenuEvent* event) { Q_UNUSED(event) QMenu* menu = createContextMenu(); menu->setAttribute(Qt::WA_DeleteOnClose); menu->addSeparator(); QAction* act = menu->addAction(tr("Show suggestions")); act->setCheckable(true); act->setChecked(qzSettings->showWSBSearchSuggestions); connect(act, &QAction::triggered, this, &WebSearchBar::enableSearchSuggestions); QAction* instantSearch = menu->addAction(tr("Search when engine changed")); instantSearch->setCheckable(true); instantSearch->setChecked(qzSettings->searchOnEngineChange); connect(instantSearch, &QAction::triggered, this, &WebSearchBar::instantSearchChanged); // Prevent choosing first option with double rightclick QPoint pos = event->globalPos(); pos.setY(pos.y() + 1); menu->popup(pos); } void WebSearchBar::focusOutEvent(QFocusEvent* e) { if (text().isEmpty()) { QString search = m_boxSearchType->currentItem().text; setPlaceholderText(search); } LineEdit::focusOutEvent(e); } void WebSearchBar::dropEvent(QDropEvent* event) { if (event->mimeData()->hasText()) { QString dropText = event->mimeData()->text(); setText(dropText); search(); QFocusEvent event(QFocusEvent::FocusOut); LineEdit::focusOutEvent(&event); return; } LineEdit::dropEvent(event); } void WebSearchBar::keyPressEvent(QKeyEvent* event) { switch (event->key()) { case Qt::Key_V: if (event->modifiers() == (Qt::ControlModifier | Qt::ShiftModifier)) { pasteAndGo(); event->accept(); return; } break; case Qt::Key_Return: case Qt::Key_Enter: if (event->modifiers() == Qt::AltModifier) { searchInNewTab(); } else { search(); } break; case Qt::Key_Up: if (event->modifiers() == Qt::ControlModifier) { m_boxSearchType->selectPreviousItem(); } break; case Qt::Key_Down: if (event->modifiers() == Qt::ControlModifier) { m_boxSearchType->selectNextItem(); } break; default: break; } LineEdit::keyPressEvent(event); }