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 "clickablelabel.h"
27 #include "buttonwithmenu.h"
28 #include "searchenginesmanager.h"
29 #include "searchenginesdialog.h"
30 #include "networkmanager.h"
31 #include "iconprovider.h"
32 #include "scripts.h"
33
34 #include <QMimeData>
35 #include <QAbstractItemView>
36 #include <QCompleter>
37 #include <QStringListModel>
38 #include <QMenu>
39 #include <QTimer>
40 #include <QClipboard>
41 #include <QContextMenuEvent>
42
WebSearchBar_Button(QWidget * parent)43 WebSearchBar_Button::WebSearchBar_Button(QWidget* parent)
44 : ClickableLabel(parent)
45 {
46 setObjectName("websearchbar-searchbutton");
47 setCursor(QCursor(Qt::PointingHandCursor));
48 setFocusPolicy(Qt::ClickFocus);
49 }
50
contextMenuEvent(QContextMenuEvent * event)51 void WebSearchBar_Button::contextMenuEvent(QContextMenuEvent* event)
52 {
53 event->accept();
54 }
55
WebSearchBar(BrowserWindow * window)56 WebSearchBar::WebSearchBar(BrowserWindow* window)
57 : LineEdit(window)
58 , m_window(window)
59 , m_reloadingEngines(false)
60 {
61 setObjectName("websearchbar");
62 setDragEnabled(true);
63
64 m_buttonSearch = new WebSearchBar_Button(this);
65
66 m_boxSearchType = new ButtonWithMenu(this);
67 m_boxSearchType->setObjectName("websearchbar-searchprovider-combobox");
68 m_boxSearchType->setFocusProxy(this);
69 // RTL Support
70 // If we don't add 'm_boxSearchType' by following code, then we should use suitable padding-left value
71 // but then, when typing RTL text the layout dynamically changed and within RTL layout direction
72 // padding-left is equivalent to padding-right and vice versa, and because style sheet is
73 // not changed dynamically this create padding problems.
74 addWidget(m_boxSearchType, LineEdit::LeftSide);
75
76 addWidget(m_buttonSearch, LineEdit::RightSide);
77
78 connect(m_buttonSearch, &ClickableLabel::clicked, this, &WebSearchBar::search);
79 connect(m_buttonSearch, &ClickableLabel::middleClicked, this, &WebSearchBar::searchInNewTab);
80 connect(m_boxSearchType, &ButtonWithMenu::activeItemChanged, this, &WebSearchBar::searchChanged);
81
82 setWidgetSpacing(0);
83
84 m_searchManager = mApp->searchEnginesManager();
85 connect(m_boxSearchType->menu(), &QMenu::aboutToShow, this, &WebSearchBar::aboutToShowMenu);
86
87 m_completer = new QCompleter(this);
88 m_completer->setCompletionMode(QCompleter::UnfilteredPopupCompletion);
89 m_completerModel = new QStringListModel(this);
90 m_completer->setModel(m_completerModel);
91 m_completer->popup()->setMinimumHeight(90);
92 setCompleter(m_completer);
93 connect(m_completer->popup(), &QAbstractItemView::activated, this, &WebSearchBar::search);
94
95 m_openSearchEngine = new OpenSearchEngine(this);
96 m_openSearchEngine->setNetworkAccessManager(mApp->networkManager());
97 connect(m_openSearchEngine, &OpenSearchEngine::suggestions, this, &WebSearchBar::addSuggestions);
98 connect(this, &QLineEdit::textEdited, m_openSearchEngine, &OpenSearchEngine::requestSuggestions);
99
100 editAction(PasteAndGo)->setText(tr("Paste And &Search"));
101 editAction(PasteAndGo)->setIcon(QIcon::fromTheme(QSL("edit-paste")));
102 connect(editAction(PasteAndGo), &QAction::triggered, this, &WebSearchBar::pasteAndGo);
103
104 QTimer::singleShot(0, this, &WebSearchBar::setupEngines);
105 }
106
aboutToShowMenu()107 void WebSearchBar::aboutToShowMenu()
108 {
109 QMenu* menu = m_boxSearchType->menu();
110
111 menu->addSeparator();
112
113 m_window->weView()->page()->runJavaScript(Scripts::getOpenSearchLinks(), WebPage::SafeJsWorld, [this, menu](const QVariant &res) {
114 const QVariantList &list = res.toList();
115 for (const QVariant &val : list) {
116 const QVariantMap &link = val.toMap();
117 QUrl url = m_window->weView()->url().resolved(link.value(QSL("url")).toUrl());
118 QString title = link.value(QSL("title")).toString();
119
120 if (url.isEmpty())
121 continue;
122
123 if (title.isEmpty())
124 title = m_window->weView()->title();
125
126 menu->addAction(m_window->weView()->icon(), tr("Add %1 ...").arg(title), this, &WebSearchBar::addEngineFromAction)->setData(url);
127 }
128
129 menu->addSeparator();
130 menu->addAction(IconProvider::settingsIcon(), tr("Manage Search Engines"), this, &WebSearchBar::openSearchEnginesDialog);
131 });
132 }
133
addSuggestions(const QStringList & list)134 void WebSearchBar::addSuggestions(const QStringList &list)
135 {
136 if (qzSettings->showWSBSearchSuggestions) {
137 QStringList list_ = list.mid(0, 6);
138 m_completerModel->setStringList(list_);
139 m_completer->complete();
140 }
141 }
142
openSearchEnginesDialog()143 void WebSearchBar::openSearchEnginesDialog()
144 {
145 if (!m_searchDialog)
146 m_searchDialog = new SearchEnginesDialog(this);
147
148 m_searchDialog->open();
149 m_searchDialog->raise();
150 m_searchDialog->activateWindow();
151 }
152
enableSearchSuggestions(bool enable)153 void WebSearchBar::enableSearchSuggestions(bool enable)
154 {
155 Settings settings;
156 settings.beginGroup("SearchEngines");
157 settings.setValue("showSuggestions", enable);
158 settings.endGroup();
159
160 qzSettings->showWSBSearchSuggestions = enable;
161 m_completerModel->setStringList(QStringList());
162 }
163
setupEngines()164 void WebSearchBar::setupEngines()
165 {
166 disconnect(m_searchManager, &SearchEnginesManager::enginesChanged, this, &WebSearchBar::setupEngines);
167 m_reloadingEngines = true;
168
169 QString activeEngine = m_searchManager->startingEngineName();
170
171 if (m_boxSearchType->allItems().count() != 0) {
172 activeEngine = m_activeEngine.name;
173 }
174
175 m_boxSearchType->clearItems();
176
177 const auto engines = m_searchManager->allEngines();
178 for (const SearchEngine &en : engines) {
179 ButtonWithMenu::Item item;
180 item.icon = en.icon;
181 item.text = en.name;
182 QVariant v;
183 v.setValue<SearchEngine>(en);
184 item.userData = v;
185
186 m_boxSearchType->addItem(item);
187
188 if (item.text == activeEngine) {
189 m_boxSearchType->setCurrentItem(item, false);
190 }
191 }
192
193 searchChanged(m_boxSearchType->currentItem());
194
195 connect(m_searchManager, &SearchEnginesManager::enginesChanged, this, &WebSearchBar::setupEngines);
196 m_reloadingEngines = false;
197 }
198
searchChanged(const ButtonWithMenu::Item & item)199 void WebSearchBar::searchChanged(const ButtonWithMenu::Item &item)
200 {
201 setPlaceholderText(item.text);
202 m_completerModel->setStringList(QStringList());
203
204 m_activeEngine = item.userData.value<SearchEngine>();
205
206 m_openSearchEngine->setSuggestionsUrl(m_activeEngine.suggestionsUrl);
207 m_openSearchEngine->setSuggestionsParameters(m_activeEngine.suggestionsParameters);
208
209 m_searchManager->setActiveEngine(m_activeEngine);
210
211 if (qzSettings->searchOnEngineChange && !m_reloadingEngines && !text().isEmpty()) {
212 search();
213 }
214 }
215
instantSearchChanged(bool enable)216 void WebSearchBar::instantSearchChanged(bool enable)
217 {
218 Settings settings;
219 settings.beginGroup("SearchEngines");
220 settings.setValue("SearchOnEngineChange", enable);
221 settings.endGroup();
222 qzSettings->searchOnEngineChange = enable;
223 }
224
search()225 void WebSearchBar::search()
226 {
227 m_window->weView()->setFocus();
228 m_window->weView()->load(m_searchManager->searchResult(m_activeEngine, text()));
229 }
230
searchInNewTab()231 void WebSearchBar::searchInNewTab()
232 {
233 int index = m_window->tabWidget()->addView(QUrl());
234 m_window->weView(index)->setFocus();
235 m_window->weView(index)->load(m_searchManager->searchResult(m_activeEngine, text()));
236 }
237
addEngineFromAction()238 void WebSearchBar::addEngineFromAction()
239 {
240 if (QAction* action = qobject_cast<QAction*>(sender())) {
241 m_searchManager->addEngine(action->data().toUrl());
242 }
243 }
244
pasteAndGo()245 void WebSearchBar::pasteAndGo()
246 {
247 clear();
248 paste();
249 search();
250 }
251
contextMenuEvent(QContextMenuEvent * event)252 void WebSearchBar::contextMenuEvent(QContextMenuEvent* event)
253 {
254 Q_UNUSED(event)
255
256 QMenu* menu = createContextMenu();
257 menu->setAttribute(Qt::WA_DeleteOnClose);
258
259 menu->addSeparator();
260 QAction* act = menu->addAction(tr("Show suggestions"));
261 act->setCheckable(true);
262 act->setChecked(qzSettings->showWSBSearchSuggestions);
263 connect(act, &QAction::triggered, this, &WebSearchBar::enableSearchSuggestions);
264
265 QAction* instantSearch = menu->addAction(tr("Search when engine changed"));
266 instantSearch->setCheckable(true);
267 instantSearch->setChecked(qzSettings->searchOnEngineChange);
268 connect(instantSearch, &QAction::triggered, this, &WebSearchBar::instantSearchChanged);
269
270 // Prevent choosing first option with double rightclick
271 QPoint pos = event->globalPos();
272 pos.setY(pos.y() + 1);
273 menu->popup(pos);
274 }
275
focusOutEvent(QFocusEvent * e)276 void WebSearchBar::focusOutEvent(QFocusEvent* e)
277 {
278 if (text().isEmpty()) {
279 QString search = m_boxSearchType->currentItem().text;
280 setPlaceholderText(search);
281 }
282
283 LineEdit::focusOutEvent(e);
284 }
285
dropEvent(QDropEvent * event)286 void WebSearchBar::dropEvent(QDropEvent* event)
287 {
288 if (event->mimeData()->hasText()) {
289 QString dropText = event->mimeData()->text();
290 setText(dropText);
291 search();
292
293 QFocusEvent event(QFocusEvent::FocusOut);
294 LineEdit::focusOutEvent(&event);
295 return;
296 }
297
298 LineEdit::dropEvent(event);
299 }
300
keyPressEvent(QKeyEvent * event)301 void WebSearchBar::keyPressEvent(QKeyEvent* event)
302 {
303 switch (event->key()) {
304 case Qt::Key_V:
305 if (event->modifiers() == (Qt::ControlModifier | Qt::ShiftModifier)) {
306 pasteAndGo();
307 event->accept();
308 return;
309 }
310 break;
311
312 case Qt::Key_Return:
313 case Qt::Key_Enter:
314 if (event->modifiers() == Qt::AltModifier) {
315 searchInNewTab();
316 }
317 else {
318 search();
319 }
320 break;
321
322 case Qt::Key_Up:
323 if (event->modifiers() == Qt::ControlModifier) {
324 m_boxSearchType->selectPreviousItem();
325 }
326 break;
327
328 case Qt::Key_Down:
329 if (event->modifiers() == Qt::ControlModifier) {
330 m_boxSearchType->selectNextItem();
331 }
332 break;
333
334 default:
335 break;
336 }
337
338 LineEdit::keyPressEvent(event);
339 }
340