Falkon Develop
Cross-platform Qt-based web browser
websearchbar.cpp
Go to the documentation of this file.
1/* ============================================================
2* Falkon - Qt web browser
3* Copyright (C) 2010-2018 David Rosca <nowrep@gmail.com>
4*
5* This program is free software: you can redistribute it and/or modify
6* it under the terms of the GNU General Public License as published by
7* the Free Software Foundation, either version 3 of the License, or
8* (at your option) any later version.
9*
10* This program is distributed in the hope that it will be useful,
11* but WITHOUT ANY WARRANTY; without even the implied warranty of
12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13* GNU General Public License for more details.
14*
15* You should have received a copy of the GNU General Public License
16* along with this program. If not, see <http://www.gnu.org/licenses/>.
17* ============================================================ */
18#include "websearchbar.h"
19#include "browserwindow.h"
20#include "mainapplication.h"
21#include "tabbedwebview.h"
22#include "webpage.h"
23#include "settings.h"
24#include "qzsettings.h"
25#include "tabwidget.h"
26#include "searchenginesdialog.h"
27#include "networkmanager.h"
28#include "iconprovider.h"
29#include "scripts.h"
30
31#include <QMimeData>
32#include <QAbstractItemView>
33#include <QCompleter>
34#include <QStringListModel>
35#include <QMenu>
36#include <QTimer>
37#include <QClipboard>
38#include <QContextMenuEvent>
39
41 : ClickableLabel(parent)
42{
43 setObjectName("websearchbar-searchbutton");
44 setCursor(QCursor(Qt::PointingHandCursor));
45 setFocusPolicy(Qt::ClickFocus);
46}
47
48void WebSearchBar_Button::contextMenuEvent(QContextMenuEvent* event)
49{
50 event->accept();
51}
52
54 : LineEdit(window)
55 , m_window(window)
56 , m_reloadingEngines(false)
57{
58 setObjectName("websearchbar");
59 setDragEnabled(true);
60
61 m_buttonSearch = new WebSearchBar_Button(this);
62
63 m_boxSearchType = new ButtonWithMenu(this);
64 m_boxSearchType->setObjectName("websearchbar-searchprovider-combobox");
65 m_boxSearchType->setFocusProxy(this);
66 // RTL Support
67 // If we don't add 'm_boxSearchType' by following code, then we should use suitable padding-left value
68 // but then, when typing RTL text the layout dynamically changed and within RTL layout direction
69 // padding-left is equivalent to padding-right and vice versa, and because style sheet is
70 // not changed dynamically this create padding problems.
71 addWidget(m_boxSearchType, LineEdit::LeftSide);
72
73 addWidget(m_buttonSearch, LineEdit::RightSide);
74
75 connect(m_buttonSearch, &ClickableLabel::clicked, this, &WebSearchBar::search);
76 connect(m_buttonSearch, &ClickableLabel::middleClicked, this, &WebSearchBar::searchInNewTab);
77 connect(m_boxSearchType, &ButtonWithMenu::activeItemChanged, this, &WebSearchBar::searchChanged);
78
80
81 m_searchManager = mApp->searchEnginesManager();
82 connect(m_boxSearchType->menu(), &QMenu::aboutToShow, this, &WebSearchBar::aboutToShowMenu);
83
84 m_completer = new QCompleter(this);
85 m_completer->setCompletionMode(QCompleter::UnfilteredPopupCompletion);
86 m_completerModel = new QStringListModel(this);
87 m_completer->setModel(m_completerModel);
88 m_completer->popup()->setMinimumHeight(90);
89 setCompleter(m_completer);
90 connect(m_completer->popup(), &QAbstractItemView::activated, this, &WebSearchBar::search);
91
92 m_openSearchEngine = new OpenSearchEngine(this);
93 m_openSearchEngine->setNetworkAccessManager(mApp->networkManager());
94 connect(m_openSearchEngine, &OpenSearchEngine::suggestions, this, &WebSearchBar::addSuggestions);
95 connect(this, &QLineEdit::textEdited, m_openSearchEngine, &OpenSearchEngine::requestSuggestions);
96
97 editAction(PasteAndGo)->setText(tr("Paste And &Search"));
98 editAction(PasteAndGo)->setIcon(QIcon::fromTheme(QSL("edit-paste")));
99 connect(editAction(PasteAndGo), &QAction::triggered, this, &WebSearchBar::pasteAndGo);
100
101 QTimer::singleShot(0, this, &WebSearchBar::setupEngines);
102}
103
104void WebSearchBar::aboutToShowMenu()
105{
106 QMenu* menu = m_boxSearchType->menu();
107
108 menu->addSeparator();
109
110 m_window->weView()->page()->runJavaScript(Scripts::getOpenSearchLinks(), WebPage::SafeJsWorld, [this, menu](const QVariant &res) {
111 const QVariantList &list = res.toList();
112 for (const QVariant &val : list) {
113 const QVariantMap &link = val.toMap();
114 QUrl url = m_window->weView()->url().resolved(link.value(QSL("url")).toUrl());
115 QString title = link.value(QSL("title")).toString();
116
117 if (url.isEmpty())
118 continue;
119
120 if (title.isEmpty())
121 title = m_window->weView()->title();
122
123 menu->addAction(m_window->weView()->icon(), tr("Add %1 ...").arg(title), this, &WebSearchBar::addEngineFromAction)->setData(url);
124 }
125
126 menu->addSeparator();
127 menu->addAction(IconProvider::settingsIcon(), tr("Manage Search Engines"), this, &WebSearchBar::openSearchEnginesDialog);
128 });
129}
130
131void WebSearchBar::addSuggestions(const QStringList &list)
132{
133 if (qzSettings->showWSBSearchSuggestions) {
134 QStringList list_ = list.mid(0, 6);
135 m_completerModel->setStringList(list_);
136 m_completer->complete();
137 }
138}
139
140void WebSearchBar::openSearchEnginesDialog()
141{
142 if (!m_searchDialog)
143 m_searchDialog = new SearchEnginesDialog(this);
144
145 m_searchDialog->open();
146 m_searchDialog->raise();
147 m_searchDialog->activateWindow();
148}
149
150void WebSearchBar::enableSearchSuggestions(bool enable)
151{
152 Settings settings;
153 settings.beginGroup(QSL("SearchEngines"));
154 settings.setValue(QSL("showSuggestions"), enable);
155 settings.endGroup();
156
157 qzSettings->showWSBSearchSuggestions = enable;
158 m_completerModel->setStringList(QStringList());
159 updateOpenSearchEngine();
160}
161
162void WebSearchBar::setupEngines()
163{
164 disconnect(m_searchManager, &SearchEnginesManager::enginesChanged, this, &WebSearchBar::setupEngines);
165 m_reloadingEngines = true;
166
167 QString activeEngine = m_searchManager->startingEngineName();
168
169 if (m_boxSearchType->allItems().count() != 0) {
170 activeEngine = m_activeEngine.name;
171 }
172
173 m_boxSearchType->clearItems();
174
175 const auto engines = m_searchManager->allEngines();
176 for (const SearchEngine &en : engines) {
178 item.icon = en.icon;
179 item.text = en.name;
180 QVariant v;
181 v.setValue(en);
182 item.userData = v;
183
184 m_boxSearchType->addItem(item);
185
186 if (item.text == activeEngine) {
187 m_boxSearchType->setCurrentItem(item, false);
188 }
189 }
190
191 searchChanged(m_boxSearchType->currentItem());
192
193 connect(m_searchManager, &SearchEnginesManager::enginesChanged, this, &WebSearchBar::setupEngines);
194 m_reloadingEngines = false;
195}
196
197void WebSearchBar::searchChanged(const ButtonWithMenu::Item &item)
198{
199 setPlaceholderText(item.text);
200 m_completerModel->setStringList(QStringList());
201
202 m_activeEngine = item.userData.value<SearchEngine>();
203
204 updateOpenSearchEngine();
205
206 m_searchManager->setActiveEngine(m_activeEngine);
207
208 if (qzSettings->searchOnEngineChange && !m_reloadingEngines && !text().isEmpty()) {
209 search();
210 }
211}
212
213void WebSearchBar::instantSearchChanged(bool enable)
214{
215 Settings settings;
216 settings.beginGroup(QSL("SearchEngines"));
217 settings.setValue(QSL("SearchOnEngineChange"), enable);
218 settings.endGroup();
219 qzSettings->searchOnEngineChange = enable;
220}
221
222void WebSearchBar::search()
223{
224 m_window->weView()->setFocus();
225 m_window->weView()->load(m_searchManager->searchResult(m_activeEngine, text()));
226}
227
228void WebSearchBar::searchInNewTab()
229{
230 int index = m_window->tabWidget()->addView(QUrl());
231 m_window->weView(index)->setFocus();
232 m_window->weView(index)->load(m_searchManager->searchResult(m_activeEngine, text()));
233}
234
235void WebSearchBar::addEngineFromAction()
236{
237 if (auto* action = qobject_cast<QAction*>(sender())) {
238 m_searchManager->addEngine(action->data().toUrl());
239 }
240}
241
242void WebSearchBar::pasteAndGo()
243{
244 clear();
245 paste();
246 search();
247}
248
249void WebSearchBar::contextMenuEvent(QContextMenuEvent* event)
250{
251 Q_UNUSED(event)
252
253 QMenu* menu = createContextMenu();
254 menu->setAttribute(Qt::WA_DeleteOnClose);
255
256 menu->addSeparator();
257 QAction* act = menu->addAction(tr("Show suggestions"));
258 act->setCheckable(true);
259 act->setChecked(qzSettings->showWSBSearchSuggestions);
260 connect(act, &QAction::triggered, this, &WebSearchBar::enableSearchSuggestions);
261
262 QAction* instantSearch = menu->addAction(tr("Search when engine changed"));
263 instantSearch->setCheckable(true);
264 instantSearch->setChecked(qzSettings->searchOnEngineChange);
265 connect(instantSearch, &QAction::triggered, this, &WebSearchBar::instantSearchChanged);
266
267 // Prevent choosing first option with double rightclick
268 QPoint pos = event->globalPos();
269 pos.setY(pos.y() + 1);
270 menu->popup(pos);
271}
272
273void WebSearchBar::focusOutEvent(QFocusEvent* e)
274{
275 if (text().isEmpty()) {
276 QString search = m_boxSearchType->currentItem().text;
277 setPlaceholderText(search);
278 }
279
280 LineEdit::focusOutEvent(e);
281}
282
283void WebSearchBar::dropEvent(QDropEvent* event)
284{
285 if (event->mimeData()->hasText()) {
286 QString dropText = event->mimeData()->text();
287 setText(dropText);
288 search();
289
290 QFocusEvent event(QFocusEvent::FocusOut);
291 LineEdit::focusOutEvent(&event);
292 return;
293 }
294
295 LineEdit::dropEvent(event);
296}
297
298void WebSearchBar::keyPressEvent(QKeyEvent* event)
299{
300 switch (event->key()) {
301 case Qt::Key_V:
302 if (event->modifiers() == (Qt::ControlModifier | Qt::ShiftModifier)) {
303 pasteAndGo();
304 event->accept();
305 return;
306 }
307 break;
308
309 case Qt::Key_Return:
310 case Qt::Key_Enter:
311 if (event->modifiers() == Qt::AltModifier) {
312 searchInNewTab();
313 }
314 else {
315 search();
316 }
317 break;
318
319 case Qt::Key_Up:
320 if (event->modifiers() == Qt::ControlModifier) {
321 m_boxSearchType->selectPreviousItem();
322 }
323 break;
324
325 case Qt::Key_Down:
326 if (event->modifiers() == Qt::ControlModifier) {
327 m_boxSearchType->selectNextItem();
328 }
329 break;
330
331 default:
332 break;
333 }
334
335 LineEdit::keyPressEvent(event);
336}
337
338void WebSearchBar::updateOpenSearchEngine()
339{
340 if (qzSettings->showWSBSearchSuggestions) {
341 m_openSearchEngine->setSuggestionsUrl(m_activeEngine.suggestionsUrl);
342 m_openSearchEngine->setSuggestionsParameters(m_activeEngine.suggestionsParameters);
343 }
344 else {
345 m_openSearchEngine->setSuggestionsUrl(QL1S(""));
346 }
347}
TabWidget * tabWidget() const
TabbedWebView * weView() const
QMenu * menu() const
void selectPreviousItem()
void addItem(const Item &item)
QVector< Item > allItems()
void activeItemChanged(const ButtonWithMenu::Item &item)
void setCurrentItem(const Item &item, bool emitSignal=true)
void clicked(QPoint)
void middleClicked(QPoint)
static QIcon settingsIcon()
QAction * editAction(EditAction action) const
Definition: lineedit.cpp:343
void addWidget(QWidget *widget, WidgetPosition position)
Definition: lineedit.cpp:259
@ LeftSide
Definition: lineedit.h:79
@ RightSide
Definition: lineedit.h:80
QMenu * createContextMenu()
Definition: lineedit.cpp:191
void setWidgetSpacing(int spacing)
Definition: lineedit.cpp:283
bool event(QEvent *event) override
Definition: lineedit.cpp:170
@ PasteAndGo
Definition: lineedit.h:89
A class representing a single search engine described in OpenSearch format.
void requestSuggestions(const QString &searchTerm)
void setNetworkAccessManager(QNetworkAccessManager *networkAccessManager)
void setSuggestionsParameters(const Parameters &suggestionsParameters)
void suggestions(const QStringList &suggestions)
void setSuggestionsUrl(const QString &string)
static QString getOpenSearchLinks()
Definition: scripts.cpp:281
QVector< Engine > allEngines()
void addEngine(const QUrl &url)
void setActiveEngine(const Engine &engine)
LoadRequest searchResult(const Engine &engine, const QString &string)
void beginGroup(const QString &prefix)
Definition: settings.cpp:79
void endGroup()
Definition: settings.cpp:84
void setValue(const QString &key, const QVariant &defaultValue=QVariant())
Definition: settings.cpp:69
int addView(const LoadRequest &req, const Qz::NewTabPositionFlags &openFlags, bool selectLine=false, bool pinned=false)
Definition: tabwidget.cpp:314
@ SafeJsWorld
Definition: webpage.h:45
WebSearchBar_Button(QWidget *parent=nullptr)
WebSearchBar(BrowserWindow *window)
WebPage * page() const
Definition: webview.cpp:132
QIcon icon(bool allowNull=false) const
Definition: webview.cpp:90
void load(const QUrl &url)
Definition: webview.cpp:177
QString title(bool allowEmpty=false) const
Definition: webview.cpp:107
#define mApp
#define QL1S(x)
Definition: qzcommon.h:44
#define QSL(x)
Definition: qzcommon.h:40
#define qzSettings
Definition: qzsettings.h:69