1 /**************************************************************************
2 * Otter Browser: Web browser controlled by the user, not vice-versa.
3 * Copyright (C) 2013 - 2018 Michal Dutkiewicz aka Emdek <michal@emdek.pl>
4 * Copyright (C) 2014 - 2017 Jan Bajer aka bajasoft <jbajer@gmail.com>
5 * Copyright (C) 2014 Piotr Wójcik <chocimier@tlen.pl>
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 **************************************************************************/
21 
22 #include "AddressWidget.h"
23 #include "../../../core/ActionsManager.h"
24 #include "../../../core/AddressCompletionModel.h"
25 #include "../../../core/Application.h"
26 #include "../../../core/BookmarksManager.h"
27 #include "../../../core/InputInterpreter.h"
28 #include "../../../core/HistoryManager.h"
29 #include "../../../core/SearchEnginesManager.h"
30 #include "../../../core/ThemesManager.h"
31 #include "../../../core/Utils.h"
32 #include "../../../ui/Action.h"
33 #include "../../../ui/BookmarkPropertiesDialog.h"
34 #include "../../../ui/ContentsWidget.h"
35 #include "../../../ui/MainWindow.h"
36 #include "../../../ui/ToolBarWidget.h"
37 #include "../../../ui/Window.h"
38 
39 #include <QtCore/QMetaEnum>
40 #include <QtGui/QAbstractTextDocumentLayout>
41 #include <QtGui/QClipboard>
42 #include <QtGui/QContextMenuEvent>
43 #include <QtGui/QPainter>
44 #include <QtGui/QTextBlock>
45 #include <QtGui/QTextDocument>
46 #include <QtWidgets/QApplication>
47 #include <QtWidgets/QMenu>
48 #include <QtWidgets/QToolTip>
49 
50 namespace Otter
51 {
52 
53 int AddressWidget::m_entryIdentifierEnumerator(-1);
54 
AddressDelegate(const QString & highlight,ViewMode mode,QObject * parent)55 AddressDelegate::AddressDelegate(const QString &highlight, ViewMode mode, QObject *parent) : QStyledItemDelegate(parent),
56 	m_highlight(highlight),
57 	m_displayMode((SettingsManager::getOption(SettingsManager::AddressField_CompletionDisplayModeOption).toString() == QLatin1String("columns")) ? ColumnsMode : CompactMode),
58 	m_viewMode(mode)
59 {
60 	connect(SettingsManager::getInstance(), &SettingsManager::optionChanged, this, &AddressDelegate::handleOptionChanged);
61 }
62 
paint(QPainter * painter,const QStyleOptionViewItem & option,const QModelIndex & index) const63 void AddressDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
64 {
65 	QRect titleRectangle(option.rect);
66 	const bool isRightToLeft(option.direction == Qt::RightToLeft);
67 
68 	if (static_cast<AddressCompletionModel::CompletionEntry::EntryType>(index.data(AddressCompletionModel::TypeRole).toInt()) == AddressCompletionModel::CompletionEntry::HeaderType)
69 	{
70 		QStyleOptionViewItem headerOption(option);
71 		headerOption.rect = titleRectangle.marginsRemoved(QMargins(0, 2, (isRightToLeft ? 3 : 0), 2));
72 		headerOption.text = index.data(AddressCompletionModel::TitleRole).toString();
73 
74 		if (index.row() > 0)
75 		{
76 			QPen pen(option.palette.color(QPalette::Disabled, QPalette::Text).lighter());
77 			pen.setWidth(1);
78 			pen.setStyle(Qt::SolidLine);
79 
80 			painter->save();
81 			painter->setPen(pen);
82 			painter->drawLine((option.rect.left() + 5), (option.rect.top() + 3), (option.rect.right() - 5), (option.rect.top() + 3));
83 			painter->restore();
84 		}
85 
86 		QStyledItemDelegate::paint(painter, headerOption, index);
87 
88 		return;
89 	}
90 
91 	QAbstractTextDocumentLayout::PaintContext paintContext;
92 	QTextDocument document;
93 	document.setDefaultFont(option.font);
94 
95 	QString url(index.data(Qt::DisplayRole).toString());
96 	QString description((m_viewMode == HistoryMode) ? Utils::formatDateTime(index.data(AddressCompletionModel::TimeVisitedRole).toDateTime()) : index.data(AddressCompletionModel::TitleRole).toString());
97 	const int topPosition(titleRectangle.top() - qRound((titleRectangle.height() - painter->clipBoundingRect().united(document.documentLayout()->blockBoundingRect(document.firstBlock())).height()) / 2));
98 	const bool isSearchSuggestion(static_cast<AddressCompletionModel::CompletionEntry::EntryType>(index.data(AddressCompletionModel::TypeRole).toInt()) == AddressCompletionModel::CompletionEntry::SearchSuggestionType);
99 
100 	if (option.state.testFlag(QStyle::State_Selected))
101 	{
102 		painter->fillRect(option.rect, option.palette.color(QPalette::Highlight));
103 
104 		paintContext.palette.setColor(QPalette::Text, option.palette.color(QPalette::HighlightedText));
105 	}
106 	else if (!isSearchSuggestion)
107 	{
108 		paintContext.palette.setColor(QPalette::Text, option.palette.color(QPalette::Link));
109 	}
110 
111 	QRect decorationRectangle(option.rect);
112 
113 	if (isRightToLeft)
114 	{
115 		decorationRectangle.setLeft(option.rect.width() - option.rect.height() - 5);
116 
117 		titleRectangle.setRight(option.rect.width() - option.rect.height() - 10);
118 	}
119 	else
120 	{
121 		decorationRectangle.setRight(option.rect.height());
122 
123 		titleRectangle.setLeft(option.rect.height());
124 	}
125 
126 	decorationRectangle = decorationRectangle.marginsRemoved(QMargins(2, 2, 2, 2));
127 
128 	QIcon icon(index.data(Qt::DecorationRole).value<QIcon>());
129 
130 	if (icon.isNull())
131 	{
132 		icon = ThemesManager::createIcon(QLatin1String("tab"));
133 	}
134 
135 	icon.paint(painter, decorationRectangle, option.decorationAlignment);
136 
137 	if (m_displayMode == ColumnsMode)
138 	{
139 		const int maxUrlWidth(option.rect.width() / 2);
140 
141 		url = option.fontMetrics.elidedText(url, Qt::ElideRight, (maxUrlWidth - 40));
142 
143 		painter->save();
144 
145 		if (isRightToLeft)
146 		{
147 			painter->translate((titleRectangle.right() - calculateLength(option, url)), topPosition);
148 		}
149 		else
150 		{
151 			painter->translate(titleRectangle.left(), topPosition);
152 		}
153 
154 		document.setHtml(isSearchSuggestion ? url : highlightText(url));
155 		document.documentLayout()->draw(painter, paintContext);
156 
157 		painter->restore();
158 
159 		if (!description.isEmpty())
160 		{
161 			painter->save();
162 
163 			description = option.fontMetrics.elidedText(description, (isRightToLeft ? Qt::ElideLeft : Qt::ElideRight), (maxUrlWidth - 10));
164 
165 			if (isRightToLeft)
166 			{
167 				titleRectangle.setRight(maxUrlWidth);
168 
169 				painter->translate((titleRectangle.right() - calculateLength(option, description)), topPosition);
170 			}
171 			else
172 			{
173 				titleRectangle.setLeft(maxUrlWidth);
174 
175 				painter->translate(titleRectangle.left(), topPosition);
176 			}
177 
178 			document.setHtml(highlightText(description));
179 
180 			if (option.state.testFlag(QStyle::State_Selected))
181 			{
182 				document.documentLayout()->draw(painter, paintContext);
183 			}
184 			else
185 			{
186 				document.drawContents(painter);
187 			}
188 
189 			painter->restore();
190 		}
191 
192 		return;
193 	}
194 
195 	painter->save();
196 
197 	url = option.fontMetrics.elidedText(url, Qt::ElideRight, (option.rect.width() - 40));
198 
199 	if (isRightToLeft)
200 	{
201 		painter->translate((titleRectangle.right() - calculateLength(option, url)), topPosition);
202 	}
203 	else
204 	{
205 		painter->translate(titleRectangle.left(), topPosition);
206 	}
207 
208 	document.setHtml(isSearchSuggestion ? url : highlightText(url));
209 	document.documentLayout()->draw(painter, paintContext);
210 
211 	painter->restore();
212 
213 	if (!description.isEmpty())
214 	{
215 		const int urlLength(calculateLength(option, url + QLatin1Char(' ')));
216 
217 		if ((urlLength + 40) < titleRectangle.width())
218 		{
219 			painter->save();
220 
221 			description = option.fontMetrics.elidedText(description, (isRightToLeft ? Qt::ElideLeft : Qt::ElideRight), (option.rect.width() - urlLength - 50));
222 
223 			if (isRightToLeft)
224 			{
225 				description.append(QLatin1String(" -"));
226 
227 				titleRectangle.setRight(option.rect.width() - calculateLength(option, description) - (urlLength + 33));
228 
229 				painter->translate(titleRectangle.right(), topPosition);
230 			}
231 			else
232 			{
233 				description.insert(0, QLatin1String("- "));
234 
235 				titleRectangle.setLeft(urlLength + 33);
236 
237 				painter->translate(titleRectangle.left(), topPosition);
238 			}
239 
240 			document.setHtml(highlightText(description));
241 
242 			if (option.state.testFlag(QStyle::State_Selected))
243 			{
244 				document.documentLayout()->draw(painter, paintContext);
245 			}
246 			else
247 			{
248 				document.drawContents(painter);
249 			}
250 
251 			painter->restore();
252 		}
253 	}
254 }
255 
handleOptionChanged(int identifier,const QVariant & value)256 void AddressDelegate::handleOptionChanged(int identifier, const QVariant &value)
257 {
258 	if (identifier == SettingsManager::AddressField_CompletionDisplayModeOption)
259 	{
260 		m_displayMode = ((value.toString() == QLatin1String("columns")) ? ColumnsMode : CompactMode);
261 	}
262 }
263 
highlightText(const QString & text,const QString & html) const264 QString AddressDelegate::highlightText(const QString &text, const QString &html) const
265 {
266 	const int index(text.indexOf(m_highlight, 0, Qt::CaseInsensitive));
267 
268 	if (m_highlight.isEmpty() || index < 0)
269 	{
270 		return (html + text);
271 	}
272 
273 	return highlightText(text.mid(index + m_highlight.length()), html + text.left(index) + QStringLiteral("<b>%1</b>").arg(text.mid(index, m_highlight.length())));
274 }
275 
calculateLength(const QStyleOptionViewItem & option,const QString & text,int length) const276 int AddressDelegate::calculateLength(const QStyleOptionViewItem &option, const QString &text, int length) const
277 {
278 	const int index(text.indexOf(m_highlight, 0, Qt::CaseInsensitive));
279 
280 	if (m_highlight.isEmpty() || index < 0)
281 	{
282 		return (length + option.fontMetrics.width(text));
283 	}
284 
285 	length += option.fontMetrics.width(text.left(index));
286 
287 	QStyleOptionViewItem highlightedOption(option);
288 	highlightedOption.font.setBold(true);
289 
290 	length += highlightedOption.fontMetrics.width(text.mid(index, m_highlight.length()));
291 
292 	return calculateLength(option, text.mid(index + m_highlight.length()), length);
293 }
294 
sizeHint(const QStyleOptionViewItem & option,const QModelIndex & index) const295 QSize AddressDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
296 {
297 	QSize size(index.data(Qt::SizeHintRole).toSize());
298 
299 	if (index.row() != 0 && static_cast<AddressCompletionModel::CompletionEntry::EntryType>(index.data(AddressCompletionModel::TypeRole).toInt()) == AddressCompletionModel::CompletionEntry::HeaderType)
300 	{
301 		size.setHeight(qRound(option.fontMetrics.lineSpacing() * 1.75));
302 	}
303 	else
304 	{
305 		size.setHeight(qRound(option.fontMetrics.lineSpacing() * 1.25));
306 	}
307 
308 	return size;
309 }
310 
AddressWidget(Window * window,QWidget * parent)311 AddressWidget::AddressWidget(Window *window, QWidget *parent) : LineEditWidget(parent),
312 	m_window(nullptr),
313 	m_completionModel(new AddressCompletionModel(this)),
314 	m_clickedEntry(UnknownEntry),
315 	m_hoveredEntry(UnknownEntry),
316 	m_completionModes(NoCompletionMode),
317 	m_hints(SessionsManager::DefaultOpen),
318 	m_hasFeeds(false),
319 	m_isNavigatingCompletion(false),
320 	m_isUsingSimpleMode(false),
321 	m_wasEdited(false)
322 {
323 	const ToolBarWidget *toolBar(qobject_cast<ToolBarWidget*>(parent));
324 
325 	if (!toolBar)
326 	{
327 		m_isUsingSimpleMode = true;
328 	}
329 
330 	setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
331 	setMinimumWidth(100);
332 	setWindow(window);
333 	updateCompletion(true);
334 	handleOptionChanged(SettingsManager::AddressField_CompletionModeOption, SettingsManager::getOption(SettingsManager::AddressField_CompletionModeOption));
335 	handleOptionChanged(SettingsManager::AddressField_DropActionOption, SettingsManager::getOption(SettingsManager::AddressField_DropActionOption));
336 	handleOptionChanged(SettingsManager::AddressField_LayoutOption, SettingsManager::getOption(SettingsManager::AddressField_LayoutOption));
337 	handleOptionChanged(SettingsManager::AddressField_SelectAllOnFocusOption, SettingsManager::getOption(SettingsManager::AddressField_SelectAllOnFocusOption));
338 
339 	if (toolBar)
340 	{
341 		setPlaceholderText(tr("Enter address or search…"));
342 
343 		connect(SettingsManager::getInstance(), &SettingsManager::optionChanged, this, &AddressWidget::handleOptionChanged);
344 
345 		if (toolBar->getDefinition().isGlobal())
346 		{
347 			connect(toolBar, &ToolBarWidget::windowChanged, this, &AddressWidget::setWindow);
348 		}
349 	}
350 
351 	connect(this, &AddressWidget::textEdited, this, [&]()
352 	{
353 		m_wasEdited = true;
354 	});
355 	connect(this, &AddressWidget::textDropped, this, [&](const QString &text)
356 	{
357 		handleUserInput(text);
358 	});
359 	connect(m_completionModel, &AddressCompletionModel::completionReady, this, &AddressWidget::setCompletion);
360 	connect(BookmarksManager::getModel(), &BookmarksModel::modelModified, this, &AddressWidget::updateGeometries);
361 }
362 
changeEvent(QEvent * event)363 void AddressWidget::changeEvent(QEvent *event)
364 {
365 	LineEditWidget::changeEvent(event);
366 
367 	switch (event->type())
368 	{
369 		case QEvent::LanguageChange:
370 			if (!m_isUsingSimpleMode)
371 			{
372 				setPlaceholderText(tr("Enter address or search…"));
373 			}
374 
375 			break;
376 		case QEvent::LayoutDirectionChange:
377 			updateGeometries();
378 
379 			break;
380 		default:
381 			break;
382 	}
383 }
384 
paintEvent(QPaintEvent * event)385 void AddressWidget::paintEvent(QPaintEvent *event)
386 {
387 	LineEditWidget::paintEvent(event);
388 
389 	QPainter painter(this);
390 
391 	if (m_entries.contains(HistoryDropdownEntry))
392 	{
393 		QStyleOption arrowOption;
394 		arrowOption.initFrom(this);
395 		arrowOption.rect = m_entries[HistoryDropdownEntry].rectangle;
396 
397 		if (HistoryManager::getTypedHistoryModel()->rowCount() == 0)
398 		{
399 			arrowOption.palette.setCurrentColorGroup(QPalette::Disabled);
400 		}
401 
402 		style()->drawPrimitive(QStyle::PE_IndicatorArrowDown, &arrowOption, &painter, this);
403 	}
404 
405 	if (m_isUsingSimpleMode)
406 	{
407 		return;
408 	}
409 
410 	QHash<EntryIdentifier, EntryDefinition>::const_iterator iterator;
411 
412 	for (iterator = m_entries.begin(); iterator != m_entries.end(); ++iterator)
413 	{
414 		if (!iterator.value().icon.isNull())
415 		{
416 			iterator.value().icon.paint(&painter, iterator.value().rectangle, Qt::AlignCenter, ((iterator.key() == m_hoveredEntry) ? QIcon::Active : QIcon::Normal));
417 		}
418 	}
419 }
420 
resizeEvent(QResizeEvent * event)421 void AddressWidget::resizeEvent(QResizeEvent *event)
422 {
423 	LineEditWidget::resizeEvent(event);
424 
425 	updateGeometries();
426 }
427 
focusInEvent(QFocusEvent * event)428 void AddressWidget::focusInEvent(QFocusEvent *event)
429 {
430 	if (event->reason() == Qt::MouseFocusReason)
431 	{
432 		const EntryIdentifier entry(getEntry(mapFromGlobal(QCursor::pos())));
433 
434 		if (entry != UnknownEntry && entry != AddressEntry && entry != HistoryDropdownEntry)
435 		{
436 			Application::triggerAction(ActionsManager::ActivateContentAction, {}, this);
437 
438 			return;
439 		}
440 	}
441 
442 	LineEditWidget::focusInEvent(event);
443 
444 	activate(event->reason());
445 }
446 
keyPressEvent(QKeyEvent * event)447 void AddressWidget::keyPressEvent(QKeyEvent *event)
448 {
449 	switch (event->key())
450 	{
451 		case Qt::Key_Down:
452 			if (!isPopupVisible() && HistoryManager::getTypedHistoryModel()->rowCount() > 0)
453 			{
454 				showCompletion(true);
455 			}
456 
457 			break;
458 		case Qt::Key_Enter:
459 		case Qt::Key_Return:
460 			if (!m_isUsingSimpleMode)
461 			{
462 				handleUserInput(text().trimmed(), SessionsManager::calculateOpenHints(SessionsManager::CurrentTabOpen, Qt::LeftButton, event->modifiers()));
463 			}
464 
465 			break;
466 		case Qt::Key_Escape:
467 			if (isPopupVisible())
468 			{
469 				hidePopup();
470 			}
471 			else if (m_window)
472 			{
473 				const QUrl url(m_window->getUrl());
474 				const QString text(this->text().trimmed());
475 
476 				if (text.isEmpty() || text != url.toString())
477 				{
478 					setText(Utils::isUrlEmpty(url) ? QString() : url.toString());
479 
480 					if (!text.isEmpty() && SettingsManager::getOption(SettingsManager::AddressField_SelectAllOnFocusOption).toBool())
481 					{
482 						selectAll();
483 					}
484 				}
485 				else
486 				{
487 					m_window->setFocus();
488 				}
489 			}
490 
491 			break;
492 		default:
493 			break;
494 	}
495 
496 	LineEditWidget::keyPressEvent(event);
497 }
498 
contextMenuEvent(QContextMenuEvent * event)499 void AddressWidget::contextMenuEvent(QContextMenuEvent *event)
500 {
501 	const EntryIdentifier entry(getEntry(event->pos()));
502 	QMenu menu(this);
503 
504 	if (entry == UnknownEntry || entry == AddressEntry)
505 	{
506 		ActionExecutor::Object executor(this, this);
507 
508 		menu.addAction(new Action(ActionsManager::UndoAction, {}, executor, &menu));
509 		menu.addAction(new Action(ActionsManager::RedoAction, {}, executor, &menu));
510 		menu.addSeparator();
511 		menu.addAction(new Action(ActionsManager::CutAction, {}, executor, &menu));
512 		menu.addAction(new Action(ActionsManager::CopyAction, {}, executor, &menu));
513 		menu.addAction(new Action(ActionsManager::PasteAction, {}, executor, &menu));
514 
515 		if (!m_isUsingSimpleMode)
516 		{
517 			menu.addAction(new Action(ActionsManager::PasteAndGoAction, {}, ActionExecutor::Object(m_window, m_window), this));
518 		}
519 
520 		menu.addAction(new Action(ActionsManager::DeleteAction, {}, executor, &menu));
521 		menu.addSeparator();
522 		menu.addAction(new Action(ActionsManager::CopyToNoteAction, {}, executor, &menu));
523 		menu.addSeparator();
524 		menu.addAction(new Action(ActionsManager::ClearAllAction, {}, executor, &menu));
525 		menu.addAction(new Action(ActionsManager::SelectAllAction, {}, executor, &menu));
526 	}
527 	else
528 	{
529 		const QUrl url(getUrl());
530 
531 		if (entry == WebsiteInformationEntry && !Utils::isUrlEmpty(url) && url.scheme() != QLatin1String("about"))
532 		{
533 			ActionExecutor::Object executor(m_window, m_window);
534 
535 			menu.addAction(new Action(ActionsManager::WebsiteInformationAction, {}, executor, &menu));
536 			menu.addAction(new Action(ActionsManager::WebsitePreferencesAction, {}, executor, &menu));
537 			menu.addSeparator();
538 		}
539 
540 		menu.addAction(tr("Remove this Icon"), this, &AddressWidget::removeEntry)->setData(entry);
541 	}
542 
543 	const ToolBarWidget *toolBar(qobject_cast<ToolBarWidget*>(parentWidget()));
544 
545 	if (toolBar)
546 	{
547 		menu.addSeparator();
548 		menu.addMenu(ToolBarWidget::createCustomizationMenu(toolBar->getIdentifier(), {}, &menu));
549 	}
550 
551 	menu.exec(event->globalPos());
552 }
553 
mousePressEvent(QMouseEvent * event)554 void AddressWidget::mousePressEvent(QMouseEvent *event)
555 {
556 	m_clickedEntry = ((event->button() == Qt::LeftButton) ? getEntry(event->pos()) : UnknownEntry);
557 
558 	if (m_clickedEntry == WebsiteInformationEntry || m_clickedEntry == FaviconEntry)
559 	{
560 		m_dragStartPosition = event->pos();
561 	}
562 	else
563 	{
564 		m_dragStartPosition = {};
565 	}
566 
567 	LineEditWidget::mousePressEvent(event);
568 }
569 
mouseMoveEvent(QMouseEvent * event)570 void AddressWidget::mouseMoveEvent(QMouseEvent *event)
571 {
572 	const QUrl url(getUrl());
573 	const EntryIdentifier entry(getEntry(event->pos()));
574 
575 	if (entry != m_hoveredEntry)
576 	{
577 		if (entry == UnknownEntry || entry == AddressEntry)
578 		{
579 			setCursor(Qt::IBeamCursor);
580 		}
581 		else
582 		{
583 			setCursor(Qt::ArrowCursor);
584 		}
585 
586 		m_hoveredEntry = entry;
587 
588 		update();
589 	}
590 
591 	if (event->buttons().testFlag(Qt::LeftButton) && !m_dragStartPosition.isNull() && (event->pos() - m_dragStartPosition).manhattanLength() >= QApplication::startDragDistance() && url.isValid())
592 	{
593 		Utils::startLinkDrag(url, (m_window ? m_window->getTitle() : QString()), ((m_window ? m_window->getIcon() : ThemesManager::createIcon(QLatin1String("tab"))).pixmap(16, 16)), this);
594 	}
595 	else
596 	{
597 		LineEditWidget::mouseMoveEvent(event);
598 	}
599 }
600 
mouseReleaseEvent(QMouseEvent * event)601 void AddressWidget::mouseReleaseEvent(QMouseEvent *event)
602 {
603 	if (event->button() == Qt::LeftButton && m_clickedEntry == getEntry(event->pos()))
604 	{
605 		switch (m_clickedEntry)
606 		{
607 			case WebsiteInformationEntry:
608 				if (m_window)
609 				{
610 					m_window->triggerAction(ActionsManager::WebsiteInformationAction);
611 				}
612 
613 				event->accept();
614 
615 				return;
616 			case ListFeedsEntry:
617 				{
618 					const QVector<WebWidget::LinkUrl> feeds((m_window && m_window->getLoadingState() == WebWidget::FinishedLoadingState && m_window->getWebWidget()) ? m_window->getWebWidget()->getFeeds() : QVector<WebWidget::LinkUrl>());
619 
620 					if (feeds.count() == 1 && m_window)
621 					{
622 						m_window->setUrl(QUrl(QLatin1String("view-feed:") + feeds.first().url.toDisplayString()));
623 					}
624 					else if (feeds.count() > 1)
625 					{
626 						QMenu menu;
627 
628 						for (int i = 0; i < feeds.count(); ++i)
629 						{
630 							menu.addAction(feeds.at(i).title.isEmpty() ? tr("(Untitled)") : feeds.at(i).title)->setData(QUrl(QLatin1String("view-feed:") + feeds.at(i).url.toDisplayString()));
631 						}
632 
633 						connect(&menu, &QMenu::triggered, this, [&](QAction *action)
634 						{
635 							if (action && m_window)
636 							{
637 								m_window->setUrl(action->data().toUrl());
638 							}
639 						});
640 
641 						menu.exec(mapToGlobal(m_entries.value(ListFeedsEntry).rectangle.bottomLeft()));
642 					}
643 
644 					event->accept();
645 				}
646 
647 				return;
648 			case BookmarkEntry:
649 				{
650 					const QUrl url(getUrl());
651 
652 					if (!Utils::isUrlEmpty(url) && url.scheme() != QLatin1String("about"))
653 					{
654 						if (BookmarksManager::hasBookmark(url))
655 						{
656 							const QVector<BookmarksModel::Bookmark*> bookmarks(BookmarksManager::getModel()->findUrls(url));
657 
658 							for (int i = 0; i < bookmarks.count(); ++i)
659 							{
660 								BookmarksManager::getModel()->trashBookmark(bookmarks.at(i));
661 							}
662 						}
663 						else
664 						{
665 							QMenu menu;
666 							QAction *addBookmarkAction(menu.addAction(tr("Add to Bookmarks"), [&]()
667 							{
668 								if (m_window)
669 								{
670 									BookmarkPropertiesDialog dialog(getUrl().adjusted(QUrl::RemovePassword), m_window->getTitle(), (m_window->getContentsWidget() ? m_window->getContentsWidget()->getDescription() : QString()), nullptr, -1, true, this);
671 									dialog.exec();
672 								}
673 							}, ActionsManager::getActionShortcut(ActionsManager::BookmarkPageAction)));
674 							addBookmarkAction->setShortcutContext(Qt::WidgetShortcut);
675 
676 							menu.addAction(tr("Add to Start Page"), [&]()
677 							{
678 								if (m_window)
679 								{
680 									BookmarksManager::addBookmark(BookmarksModel::UrlBookmark, {{BookmarksModel::UrlRole, getUrl().adjusted(QUrl::RemovePassword)}, {BookmarksModel::TitleRole, m_window->getTitle()}}, BookmarksManager::getModel()->getBookmarkByPath(SettingsManager::getOption(SettingsManager::StartPage_BookmarksFolderOption).toString()));
681 								}
682 							});
683 							menu.exec(mapToGlobal(m_entries.value(BookmarkEntry).rectangle.bottomLeft()));
684 						}
685 
686 						updateGeometries();
687 					}
688 
689 					event->accept();
690 				}
691 
692 				return;
693 			case LoadPluginsEntry:
694 				m_window->triggerAction(ActionsManager::LoadPluginsAction);
695 
696 				updateGeometries();
697 
698 				event->accept();
699 
700 				return;
701 			case FillPasswordEntry:
702 				m_window->triggerAction(ActionsManager::FillPasswordAction);
703 
704 				event->accept();
705 
706 				return;
707 			case HistoryDropdownEntry:
708 				if (!isPopupVisible() && HistoryManager::getTypedHistoryModel()->rowCount() > 0)
709 				{
710 					showCompletion(true);
711 				}
712 
713 				break;
714 			default:
715 				break;
716 		}
717 	}
718 
719 	if (event->button() == Qt::MiddleButton && text().isEmpty() && !QApplication::clipboard()->text().isEmpty() && SettingsManager::getOption(SettingsManager::AddressField_PasteAndGoOnMiddleClickOption).toBool())
720 	{
721 		handleUserInput(QApplication::clipboard()->text().trimmed(), SessionsManager::CurrentTabOpen);
722 
723 		event->accept();
724 	}
725 
726 	m_clickedEntry = UnknownEntry;
727 
728 	LineEditWidget::mouseReleaseEvent(event);
729 }
730 
dragEnterEvent(QDragEnterEvent * event)731 void AddressWidget::dragEnterEvent(QDragEnterEvent *event)
732 {
733 	if (event->mimeData()->hasUrls())
734 	{
735 		event->accept();
736 	}
737 
738 	LineEditWidget::dragEnterEvent(event);
739 }
740 
openUrl(const QString & url)741 void AddressWidget::openUrl(const QString &url)
742 {
743 	setUrl(url);
744 	handleUserInput(url, SessionsManager::CurrentTabOpen);
745 }
746 
removeEntry()747 void AddressWidget::removeEntry()
748 {
749 	const QAction *action(qobject_cast<QAction*>(sender()));
750 
751 	if (action)
752 	{
753 		QStringList layout(SettingsManager::getOption(SettingsManager::AddressField_LayoutOption).toStringList());
754 		QString name(metaObject()->enumerator(m_entryIdentifierEnumerator).valueToKey(action->data().toInt()));
755 
756 		if (!name.isEmpty())
757 		{
758 			name.chop(5);
759 			name[0] = name.at(0).toLower();
760 
761 			layout.removeAll(name);
762 
763 			SettingsManager::setOption(SettingsManager::AddressField_LayoutOption, layout);
764 		}
765 	}
766 }
767 
showCompletion(bool isTypedHistory)768 void AddressWidget::showCompletion(bool isTypedHistory)
769 {
770 	PopupViewWidget *popupWidget(getPopup());
771 	popupWidget->setModel(m_completionModel);
772 	popupWidget->setItemDelegate(new AddressDelegate((isTypedHistory ? QString() : text()), (isTypedHistory ? AddressDelegate::HistoryMode : AddressDelegate::CompletionMode), popupWidget));
773 
774 	updateCompletion(isTypedHistory);
775 
776 	if (!isPopupVisible())
777 	{
778 		connect(popupWidget, &PopupViewWidget::clicked, this, [&](const QModelIndex &index)
779 		{
780 			hidePopup();
781 
782 			if (index.isValid())
783 			{
784 				if (static_cast<AddressCompletionModel::CompletionEntry::EntryType>(index.data(AddressCompletionModel::TypeRole).toInt()) == AddressCompletionModel::CompletionEntry::SearchSuggestionType)
785 				{
786 					emit requestedSearch(index.data(AddressCompletionModel::TextRole).toString(), SearchEnginesManager::getSearchEngine(index.data(AddressCompletionModel::KeywordRole).toString(), true).identifier, SessionsManager::CurrentTabOpen);
787 				}
788 				else
789 				{
790 					const QString url(index.data(AddressCompletionModel::UrlRole).toUrl().toString());
791 
792 					setUrl(url);
793 					handleUserInput(url, SessionsManager::CurrentTabOpen);
794 				}
795 			}
796 		});
797 		connect(popupWidget->selectionModel(), &QItemSelectionModel::currentChanged, this, [&](const QModelIndex &index)
798 		{
799 			if (m_isNavigatingCompletion)
800 			{
801 				m_isNavigatingCompletion = false;
802 
803 				setText(index.data(AddressCompletionModel::TextRole).toString());
804 			}
805 		});
806 
807 		showPopup();
808 	}
809 
810 	popupWidget->setCurrentIndex(m_completionModel->index(0, 0));
811 	popupWidget->setFocus();
812 }
813 
handleOptionChanged(int identifier,const QVariant & value)814 void AddressWidget::handleOptionChanged(int identifier, const QVariant &value)
815 {
816 	switch (identifier)
817 	{
818 		case SettingsManager::AddressField_CompletionModeOption:
819 			{
820 				const QString completionMode(value.toString());
821 
822 				if (completionMode == QLatin1String("inlineAndPopup"))
823 				{
824 					m_completionModes = (InlineCompletionMode | PopupCompletionMode);
825 				}
826 				else if (completionMode == QLatin1String("inline"))
827 				{
828 					m_completionModes = InlineCompletionMode;
829 				}
830 				else if (completionMode == QLatin1String("popup"))
831 				{
832 					m_completionModes = PopupCompletionMode;
833 				}
834 
835 				disconnect(this, &AddressWidget::textEdited, m_completionModel, &AddressCompletionModel::setFilter);
836 
837 				if (m_completionModes != NoCompletionMode)
838 				{
839 					connect(this, &AddressWidget::textEdited, m_completionModel, &AddressCompletionModel::setFilter);
840 				}
841 			}
842 
843 			break;
844 		case SettingsManager::AddressField_DropActionOption:
845 			{
846 				const QString dropAction(value.toString());
847 
848 				if (dropAction == QLatin1String("pasteAndGo"))
849 				{
850 					setDropMode(LineEditWidget::ReplaceAndNotifyDropMode);
851 				}
852 				else if (dropAction == QLatin1String("replace"))
853 				{
854 					setDropMode(LineEditWidget::ReplaceDropMode);
855 				}
856 				else
857 				{
858 					setDropMode(LineEditWidget::PasteDropMode);
859 				}
860 			}
861 
862 			break;
863 		case SettingsManager::AddressField_SelectAllOnFocusOption:
864 			setSelectAllOnFocus(value.toBool());
865 
866 			break;
867 		case SettingsManager::AddressField_LayoutOption:
868 			if (m_isUsingSimpleMode)
869 			{
870 				m_layout = {AddressEntry, HistoryDropdownEntry};
871 			}
872 			else
873 			{
874 				if (m_entryIdentifierEnumerator < 0)
875 				{
876 					m_entryIdentifierEnumerator = metaObject()->indexOfEnumerator("EntryIdentifier");
877 				}
878 
879 				const QStringList rawLayout(value.toStringList());
880 				QVector<EntryIdentifier> layout;
881 				layout.reserve(rawLayout.count());
882 
883 				for (int i = 0; i < rawLayout.count(); ++i)
884 				{
885 					QString name(rawLayout.at(i) + QLatin1String("Entry"));
886 					name[0] = name.at(0).toUpper();
887 
888 					const EntryIdentifier entryIdentifier(static_cast<EntryIdentifier>(metaObject()->enumerator(m_entryIdentifierEnumerator).keyToValue(name.toLatin1())));
889 
890 					if (entryIdentifier > UnknownEntry && !layout.contains(entryIdentifier))
891 					{
892 						layout.append(entryIdentifier);
893 					}
894 				}
895 
896 				if (!layout.contains(AddressEntry))
897 				{
898 					layout.prepend(AddressEntry);
899 				}
900 
901 				m_layout = layout;
902 			}
903 
904 			updateGeometries();
905 
906 			break;
907 		default:
908 			break;
909 	}
910 }
911 
handleActionsStateChanged(const QVector<int> & identifiers)912 void AddressWidget::handleActionsStateChanged(const QVector<int> &identifiers)
913 {
914 	if (identifiers.contains(ActionsManager::LoadPluginsAction) && m_layout.contains(LoadPluginsEntry))
915 	{
916 		updateGeometries();
917 	}
918 }
919 
handleWatchedDataChanged(WebWidget::ChangeWatcher watcher)920 void AddressWidget::handleWatchedDataChanged(WebWidget::ChangeWatcher watcher)
921 {
922 	if (watcher == WebWidget::FeedsWatcher)
923 	{
924 		m_hasFeeds = (m_window && m_window->getWebWidget() && !m_window->getWebWidget()->getFeeds().isEmpty());
925 
926 		updateGeometries();
927 	}
928 }
929 
handleLoadingStateChanged()930 void AddressWidget::handleLoadingStateChanged()
931 {
932 	m_hasFeeds = (m_window && m_window->getWebWidget() && !m_window->getWebWidget()->getFeeds().isEmpty());
933 
934 	updateGeometries();
935 }
936 
handleUserInput(const QString & text,SessionsManager::OpenHints hints)937 void AddressWidget::handleUserInput(const QString &text, SessionsManager::OpenHints hints)
938 {
939 	if (m_isUsingSimpleMode)
940 	{
941 		return;
942 	}
943 
944 	if (hints == SessionsManager::DefaultOpen)
945 	{
946 		hints = SessionsManager::calculateOpenHints(SessionsManager::CurrentTabOpen);
947 	}
948 
949 	if (!text.isEmpty())
950 	{
951 		const InputInterpreter::InterpreterResult result(InputInterpreter::interpret(text));
952 
953 		if (result.isValid())
954 		{
955 			MainWindow *mainWindow(m_window ? MainWindow::findMainWindow(m_window) : MainWindow::findMainWindow(this));
956 			ActionExecutor::Object executor(mainWindow, mainWindow);
957 
958 			switch (result.type)
959 			{
960 				case InputInterpreter::InterpreterResult::BookmarkType:
961 					if (executor.isValid())
962 					{
963 						executor.triggerAction(ActionsManager::OpenBookmarkAction, {{QLatin1String("bookmark"), result.bookmark->getIdentifier()}, {QLatin1String("hints"), QVariant(hints)}});
964 					}
965 
966 					break;
967 				case InputInterpreter::InterpreterResult::UrlType:
968 					if (executor.isValid())
969 					{
970 						executor.triggerAction(ActionsManager::OpenUrlAction, {{QLatin1String("url"), result.url}, {QLatin1String("hints"), QVariant(hints)}});
971 					}
972 
973 					break;
974 				case InputInterpreter::InterpreterResult::SearchType:
975 					emit requestedSearch(result.searchQuery, result.searchEngine, hints);
976 
977 					break;
978 				default:
979 					break;
980 			}
981 		}
982 	}
983 }
984 
updateGeometries()985 void AddressWidget::updateGeometries()
986 {
987 	QHash<EntryIdentifier, EntryDefinition> entries;
988 	QVector<EntryDefinition> leadingEntries;
989 	QVector<EntryDefinition> trailingEntries;
990 	const int offset(qMax(((height() - 16) / 2), 2));
991 	QMargins margins(offset, 0, offset, 0);
992 	const QUrl url(getUrl());
993 	int availableWidth(width() - margins.left() - margins.right());
994 	const bool hasValidWindow(m_window && !m_window->isAboutToClose() && m_window->getLoadingState() == WebWidget::FinishedLoadingState);
995 	bool isLeading(true);
996 	const bool isRightToLeft(layoutDirection() == Qt::RightToLeft);
997 
998 	if (m_layout.contains(WebsiteInformationEntry))
999 	{
1000 		availableWidth -= 20;
1001 	}
1002 
1003 	if (m_layout.contains(HistoryDropdownEntry))
1004 	{
1005 		availableWidth -= 16;
1006 	}
1007 
1008 	if (isRightToLeft)
1009 	{
1010 		isLeading = false;
1011 	}
1012 
1013 	for (int i = 0; i < m_layout.count(); ++i)
1014 	{
1015 		EntryDefinition definition;
1016 		definition.identifier = m_layout.at(i);
1017 
1018 		switch (m_layout.at(i))
1019 		{
1020 			case AddressEntry:
1021 				isLeading = !isLeading;
1022 
1023 				break;
1024 			case WebsiteInformationEntry:
1025 				{
1026 					QString icon(QLatin1String("unknown"));
1027 					const WebWidget::ContentStates state(m_window ? m_window->getContentState() : WebWidget::UnknownContentState);
1028 
1029 					if (state.testFlag(WebWidget::FraudContentState))
1030 					{
1031 						icon = QLatin1String("badge-fraud");
1032 					}
1033 					else if (state.testFlag(WebWidget::MixedContentState))
1034 					{
1035 						icon = QLatin1String("badge-mixed");
1036 					}
1037 					else if (state.testFlag(WebWidget::SecureContentState))
1038 					{
1039 						icon = QLatin1String("badge-secure");
1040 					}
1041 					else if (state.testFlag(WebWidget::RemoteContentState))
1042 					{
1043 						icon = QLatin1String("badge-remote");
1044 					}
1045 					else if (state.testFlag(WebWidget::LocalContentState))
1046 					{
1047 						icon = QLatin1String("badge-local");
1048 					}
1049 					else if (state.testFlag(WebWidget::ApplicationContentState))
1050 					{
1051 						icon = QLatin1String("otter-browser");
1052 					}
1053 
1054 					if (!Utils::isUrlEmpty(url) && url.scheme() != QLatin1String("about"))
1055 					{
1056 						definition.title = QT_TR_NOOP("Show website information");
1057 					}
1058 
1059 					definition.icon = ThemesManager::createIcon(icon, false);
1060 				}
1061 
1062 				break;
1063 			case FaviconEntry:
1064 				definition.icon = (m_window ? m_window->getIcon() : ThemesManager::createIcon((SessionsManager::isPrivate() ? QLatin1String("tab-private") : QLatin1String("tab")), false));
1065 
1066 				break;
1067 			case ListFeedsEntry:
1068 				if (m_hasFeeds)
1069 				{
1070 					definition.title = QT_TR_NOOP("Show feed list");
1071 					definition.icon = ThemesManager::createIcon(QLatin1String("application-rss+xml"), false);
1072 				}
1073 
1074 				break;
1075 			case BookmarkEntry:
1076 				if (!Utils::isUrlEmpty(url) && url.scheme() != QLatin1String("about"))
1077 				{
1078 					if (BookmarksManager::hasBookmark(url))
1079 					{
1080 						definition.title = QT_TR_NOOP("Remove bookmark");
1081 						definition.icon = ThemesManager::createIcon(QLatin1String("bookmark-page-remove"), false);
1082 					}
1083 					else
1084 					{
1085 						definition.title = QT_TR_NOOP("Add bookmark");
1086 						definition.icon = ThemesManager::createIcon(QLatin1String("bookmark-page-new"), false);
1087 					}
1088 				}
1089 
1090 				break;
1091 			case LoadPluginsEntry:
1092 				if (hasValidWindow && m_window->getActionState(ActionsManager::LoadPluginsAction).isEnabled)
1093 				{
1094 					definition.title = QT_TR_NOOP("Load all plugins on the page");
1095 					definition.icon = ThemesManager::createIcon(QLatin1String("preferences-plugin"), false);
1096 				}
1097 
1098 				break;
1099 			case FillPasswordEntry:
1100 				if (hasValidWindow && !Utils::isUrlEmpty(url) && url.scheme() != QLatin1String("about") && PasswordsManager::hasPasswords(url, PasswordsManager::FormPassword))
1101 				{
1102 					definition.title = QT_TR_NOOP("Log in");
1103 					definition.icon = ThemesManager::createIcon(QLatin1String("fill-password"), false);
1104 				}
1105 
1106 				break;
1107 			default:
1108 				break;
1109 		}
1110 
1111 		if (m_layout.at(i) != HistoryDropdownEntry && definition.icon.isNull())
1112 		{
1113 			continue;
1114 		}
1115 
1116 		switch (m_layout.at(i))
1117 		{
1118 			case AddressEntry:
1119 			case HistoryDropdownEntry:
1120 			case WebsiteInformationEntry:
1121 				break;
1122 			default:
1123 				availableWidth -= 20;
1124 
1125 				if (availableWidth < 100)
1126 				{
1127 					continue;
1128 				}
1129 
1130 				break;
1131 		}
1132 
1133 		if (isLeading)
1134 		{
1135 			if (isRightToLeft)
1136 			{
1137 				leadingEntries.prepend(definition);
1138 			}
1139 			else
1140 			{
1141 				leadingEntries.append(definition);
1142 			}
1143 		}
1144 		else
1145 		{
1146 			if (isRightToLeft)
1147 			{
1148 				trailingEntries.append(definition);
1149 			}
1150 			else
1151 			{
1152 				trailingEntries.prepend(definition);
1153 			}
1154 		}
1155 	}
1156 
1157 	for (int i = 0; i < leadingEntries.count(); ++i)
1158 	{
1159 		switch (leadingEntries.at(i).identifier)
1160 		{
1161 			case WebsiteInformationEntry:
1162 			case FaviconEntry:
1163 			case ListFeedsEntry:
1164 			case BookmarkEntry:
1165 			case LoadPluginsEntry:
1166 			case FillPasswordEntry:
1167 				leadingEntries[i].rectangle = QRect(margins.left(), ((height() - 16) / 2), 16, 16);
1168 
1169 				margins.setLeft(margins.left() + 20);
1170 
1171 				break;
1172 			case HistoryDropdownEntry:
1173 				leadingEntries[i].rectangle = QRect(margins.left(), 0, 14, height());
1174 
1175 				margins.setLeft(margins.left() + 16);
1176 
1177 				break;
1178 			default:
1179 				break;
1180 		}
1181 
1182 		entries[leadingEntries.at(i).identifier] = leadingEntries.at(i);
1183 	}
1184 
1185 	for (int i = 0; i < trailingEntries.count(); ++i)
1186 	{
1187 		switch (trailingEntries.at(i).identifier)
1188 		{
1189 			case WebsiteInformationEntry:
1190 			case FaviconEntry:
1191 			case ListFeedsEntry:
1192 			case BookmarkEntry:
1193 			case LoadPluginsEntry:
1194 			case FillPasswordEntry:
1195 				trailingEntries[i].rectangle = QRect((width() - margins.right() - 20), ((height() - 16) / 2), 16, 16);
1196 
1197 				margins.setRight(margins.right() + 20);
1198 
1199 				break;
1200 			case HistoryDropdownEntry:
1201 				trailingEntries[i].rectangle = QRect((width() - margins.right() - 14), 0, 14, height());
1202 
1203 				margins.setRight(margins.right() + 16);
1204 
1205 				break;
1206 			default:
1207 				break;
1208 		}
1209 
1210 		entries[trailingEntries.at(i).identifier] = trailingEntries.at(i);
1211 	}
1212 
1213 	m_entries = entries;
1214 
1215 	if (margins.left() > offset)
1216 	{
1217 		margins.setLeft(margins.left() - 2);
1218 	}
1219 
1220 	if (margins.right() > offset)
1221 	{
1222 		margins.setRight(margins.right() + 2);
1223 	}
1224 
1225 	setTextMargins(margins);
1226 }
1227 
updateCompletion(bool isTypedHistory)1228 void AddressWidget::updateCompletion(bool isTypedHistory)
1229 {
1230 	AddressCompletionModel::CompletionTypes types(AddressCompletionModel::UnknownCompletionType);
1231 
1232 	if (isTypedHistory)
1233 	{
1234 		types = AddressCompletionModel::TypedHistoryCompletionType;
1235 	}
1236 	else
1237 	{
1238 		if (SettingsManager::getOption(SettingsManager::AddressField_SuggestBookmarksOption).toBool())
1239 		{
1240 			types |= AddressCompletionModel::BookmarksCompletionType;
1241 		}
1242 
1243 		if (SettingsManager::getOption(SettingsManager::AddressField_SuggestHistoryOption).toBool())
1244 		{
1245 			types |= AddressCompletionModel::HistoryCompletionType;
1246 		}
1247 
1248 		if (!m_isUsingSimpleMode && SettingsManager::getOption(SettingsManager::AddressField_SuggestSearchOption).toBool())
1249 		{
1250 			types |= AddressCompletionModel::SearchSuggestionsCompletionType;
1251 		}
1252 
1253 		if (SettingsManager::getOption(SettingsManager::AddressField_SuggestSpecialPagesOption).toBool())
1254 		{
1255 			types |= AddressCompletionModel::SpecialPagesCompletionType;
1256 		}
1257 
1258 		if (SettingsManager::getOption(SettingsManager::AddressField_SuggestLocalPathsOption).toBool())
1259 		{
1260 			types |= AddressCompletionModel::LocalPathSuggestionsCompletionType;
1261 		}
1262 	}
1263 
1264 	m_completionModel->setTypes(types);
1265 }
1266 
setCompletion(const QString & filter)1267 void AddressWidget::setCompletion(const QString &filter)
1268 {
1269 	if (filter.isEmpty() || m_completionModel->rowCount() == 0)
1270 	{
1271 		hidePopup();
1272 
1273 		LineEditWidget::setCompletion({});
1274 
1275 		return;
1276 	}
1277 
1278 	if (m_completionModes.testFlag(PopupCompletionMode))
1279 	{
1280 		showCompletion(false);
1281 	}
1282 
1283 	if (m_completionModes.testFlag(InlineCompletionMode))
1284 	{
1285 		for (int i = 0; i < m_completionModel->rowCount(); ++i)
1286 		{
1287 			const QString matchedText(m_completionModel->index(i).data(AddressCompletionModel::MatchRole).toString());
1288 
1289 			if (!matchedText.isEmpty())
1290 			{
1291 				LineEditWidget::setCompletion(matchedText);
1292 
1293 				break;
1294 			}
1295 		}
1296 	}
1297 }
1298 
setWindow(Window * window)1299 void AddressWidget::setWindow(Window *window)
1300 {
1301 	const MainWindow *mainWindow(MainWindow::findMainWindow(this));
1302 
1303 	if (m_window && !m_window->isAboutToClose() && (!sender() || sender() != m_window))
1304 	{
1305 		disconnect(this, &AddressWidget::requestedSearch, m_window.data(), &Window::requestedSearch);
1306 		disconnect(m_window.data(), &Window::urlChanged, this, &AddressWidget::setUrl);
1307 		disconnect(m_window.data(), &Window::iconChanged, this, &AddressWidget::setIcon);
1308 		disconnect(m_window.data(), &Window::arbitraryActionsStateChanged, this, &AddressWidget::handleActionsStateChanged);
1309 		disconnect(m_window.data(), &Window::contentStateChanged, this, &AddressWidget::updateGeometries);
1310 		disconnect(m_window.data(), &Window::loadingStateChanged, this, &AddressWidget::handleLoadingStateChanged);
1311 
1312 		if (m_window->getWebWidget())
1313 		{
1314 			m_window->getWebWidget()->stopWatchingChanges(this, WebWidget::FeedsWatcher);
1315 
1316 			disconnect(m_window->getWebWidget(), &WebWidget::watchedDataChanged, this, &AddressWidget::handleWatchedDataChanged);
1317 		}
1318 	}
1319 
1320 	m_window = window;
1321 
1322 	if (window)
1323 	{
1324 		if (mainWindow)
1325 		{
1326 			disconnect(this, &AddressWidget::requestedSearch, mainWindow, &MainWindow::search);
1327 		}
1328 
1329 		if (isVisible() && window->isActive() && Utils::isUrlEmpty(window->getUrl()))
1330 		{
1331 			const AddressWidget *addressWidget(qobject_cast<AddressWidget*>(QApplication::focusWidget()));
1332 
1333 			if (!addressWidget)
1334 			{
1335 				setFocus();
1336 			}
1337 		}
1338 
1339 		connect(this, &AddressWidget::requestedSearch, window, &Window::requestedSearch);
1340 		connect(window, &Window::urlChanged, this, &AddressWidget::setUrl);
1341 		connect(window, &Window::iconChanged, this, &AddressWidget::setIcon);
1342 		connect(window, &Window::arbitraryActionsStateChanged, this, &AddressWidget::handleActionsStateChanged);
1343 		connect(window, &Window::contentStateChanged, this, &AddressWidget::updateGeometries);
1344 		connect(window, &Window::loadingStateChanged, this, &AddressWidget::handleLoadingStateChanged);
1345 		connect(window, &Window::destroyed, this, [&](QObject *object)
1346 		{
1347 			if (qobject_cast<Window*>(object) == m_window)
1348 			{
1349 				setWindow(nullptr);
1350 			}
1351 		});
1352 
1353 		if (window->getWebWidget())
1354 		{
1355 			window->getWebWidget()->startWatchingChanges(this, WebWidget::FeedsWatcher);
1356 
1357 			connect(window->getWebWidget(), &WebWidget::watchedDataChanged, this, &AddressWidget::handleWatchedDataChanged);
1358 		}
1359 
1360 		const ToolBarWidget *toolBar(qobject_cast<ToolBarWidget*>(parentWidget()));
1361 
1362 		if (!toolBar || toolBar->getDefinition().isGlobal())
1363 		{
1364 			connect(window, &Window::aboutToClose, this, [&]()
1365 			{
1366 				setWindow(nullptr);
1367 			});
1368 		}
1369 	}
1370 	else if (mainWindow && !mainWindow->isAboutToClose() && !m_isUsingSimpleMode)
1371 	{
1372 		connect(this, &AddressWidget::requestedSearch, mainWindow, &MainWindow::search);
1373 	}
1374 
1375 	setIcon(window ? window->getIcon() : QIcon());
1376 	setUrl(window ? window->getUrl() : QUrl());
1377 	update();
1378 }
1379 
setUrl(const QUrl & url,bool force)1380 void AddressWidget::setUrl(const QUrl &url, bool force)
1381 {
1382 	const QString text(Utils::isUrlEmpty(url) ? QString() : url.toString());
1383 
1384 	if (!m_isUsingSimpleMode)
1385 	{
1386 		updateGeometries();
1387 	}
1388 
1389 	if (m_isUsingSimpleMode || ((force || !m_wasEdited || !hasFocus()) && url.scheme() != QLatin1String("javascript")))
1390 	{
1391 		setToolTip(text);
1392 		setText(text);
1393 		setCursorPosition(0);
1394 
1395 		m_wasEdited = false;
1396 	}
1397 }
1398 
setIcon(const QIcon & icon)1399 void AddressWidget::setIcon(const QIcon &icon)
1400 {
1401 	if (m_layout.contains(FaviconEntry))
1402 	{
1403 		m_entries[FaviconEntry].icon = (icon.isNull() ? ThemesManager::createIcon((SessionsManager::isPrivate() ? QLatin1String("tab-private") : QLatin1String("tab")), false) : icon);
1404 
1405 		update();
1406 	}
1407 }
1408 
getUrl() const1409 QUrl AddressWidget::getUrl() const
1410 {
1411 	return (m_window ? m_window->getUrl() : QUrl(QLatin1String("about:blank")));
1412 }
1413 
getEntry(const QPoint & position) const1414 AddressWidget::EntryIdentifier AddressWidget::getEntry(const QPoint &position) const
1415 {
1416 	QHash<EntryIdentifier, EntryDefinition>::const_iterator iterator;
1417 
1418 	for (iterator = m_entries.begin(); iterator != m_entries.end(); ++iterator)
1419 	{
1420 		if (iterator.value().rectangle.contains(position))
1421 		{
1422 			return iterator.key();
1423 		}
1424 	}
1425 
1426 	return UnknownEntry;
1427 }
1428 
event(QEvent * event)1429 bool AddressWidget::event(QEvent *event)
1430 {
1431 	if (event->type() == QEvent::ToolTip)
1432 	{
1433 		const QHelpEvent *helpEvent(static_cast<QHelpEvent*>(event));
1434 		const EntryIdentifier entry(getEntry(helpEvent->pos()));
1435 
1436 		if (entry != UnknownEntry && entry != AddressEntry)
1437 		{
1438 			QString toolTip;
1439 			QKeySequence shortcut;
1440 			const QString title(m_entries[entry].title);
1441 
1442 			if (!title.isEmpty())
1443 			{
1444 				toolTip = tr(title.toUtf8().constData());
1445 			}
1446 
1447 			switch (entry)
1448 			{
1449 				case HistoryDropdownEntry:
1450 					shortcut = ActionsManager::getActionShortcut(ActionsManager::ActivateAddressFieldAction, {{QLatin1String("showTypedHistoryDropdown"), true}});
1451 
1452 					break;
1453 				case WebsiteInformationEntry:
1454 					shortcut = ActionsManager::getActionShortcut(ActionsManager::WebsiteInformationAction);
1455 
1456 					break;
1457 				default:
1458 					break;
1459 			}
1460 
1461 			if (!shortcut.isEmpty())
1462 			{
1463 				if (!toolTip.isEmpty())
1464 				{
1465 					toolTip.append(QLatin1Char(' '));
1466 				}
1467 
1468 				toolTip.append(QLatin1Char('(') + shortcut.toString(QKeySequence::NativeText) + QLatin1Char(')'));
1469 			}
1470 
1471 			if (!toolTip.isEmpty())
1472 			{
1473 				QToolTip::showText(helpEvent->globalPos(), toolTip);
1474 
1475 				return true;
1476 			}
1477 		}
1478 	}
1479 
1480 	return LineEditWidget::event(event);
1481 }
1482 
1483 }
1484