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) 2016 Piotr Wójcik <chocimier@tlen.pl>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 **************************************************************************/
20
21 #include "ActionWidget.h"
22 #include "../../../core/Application.h"
23 #include "../../../core/GesturesManager.h"
24 #include "../../../core/HistoryManager.h"
25 #include "../../../ui/Action.h"
26 #include "../../../ui/ContentsWidget.h"
27 #include "../../../ui/MainWindow.h"
28 #include "../../../ui/ToolBarWidget.h"
29 #include "../../../ui/Window.h"
30
31 #include <QtGui/QMouseEvent>
32 #include <QtWidgets/QToolTip>
33
34 namespace Otter
35 {
36
ActionWidget(int identifier,Window * window,const ToolBarsManager::ToolBarDefinition::Entry & definition,QWidget * parent)37 ActionWidget::ActionWidget(int identifier, Window *window, const ToolBarsManager::ToolBarDefinition::Entry &definition, QWidget *parent) : ToolButtonWidget(definition, parent),
38 m_action(new Action(identifier, definition.parameters, definition.options, ActionExecutor::Object(window, window), this))
39 {
40 setDefaultAction(m_action);
41 setWindow(window);
42
43 switch (identifier)
44 {
45 case ActionsManager::NewTabAction:
46 case ActionsManager::NewTabPrivateAction:
47 case ActionsManager::NewWindowAction:
48 case ActionsManager::NewWindowPrivateAction:
49 setAcceptDrops(true);
50
51 break;
52 default:
53 break;
54 }
55
56 const ToolBarWidget *toolBar(qobject_cast<ToolBarWidget*>(parent));
57
58 if (toolBar && toolBar->getDefinition().isGlobal())
59 {
60 connect(toolBar, &ToolBarWidget::windowChanged, this, &ActionWidget::setWindow);
61 }
62 }
63
mouseReleaseEvent(QMouseEvent * event)64 void ActionWidget::mouseReleaseEvent(QMouseEvent *event)
65 {
66 if (event->button() != Qt::LeftButton)
67 {
68 ToolButtonWidget::mouseReleaseEvent(event);
69
70 return;
71 }
72
73 int identifier(m_action->getIdentifier());
74 QVariantMap parameters(m_action->getParameters());
75
76 switch (identifier)
77 {
78 case ActionsManager::NewTabAction:
79 case ActionsManager::NewTabPrivateAction:
80 case ActionsManager::NewWindowAction:
81 case ActionsManager::NewWindowPrivateAction:
82 {
83 SessionsManager::OpenHints hints(SessionsManager::calculateOpenHints(((identifier == ActionsManager::NewWindowAction || identifier == ActionsManager::NewWindowPrivateAction) ? SessionsManager::NewWindowOpen : SessionsManager::NewTabOpen), event->button(), event->modifiers()));
84
85 if (identifier == ActionsManager::NewTabPrivateAction || identifier == ActionsManager::NewWindowPrivateAction)
86 {
87 hints |= SessionsManager::PrivateOpen;
88 }
89
90 parameters[QLatin1String("hints")] = QVariant(hints);
91
92 identifier = ActionsManager::OpenUrlAction;
93 }
94
95 break;
96 default:
97 break;
98 }
99
100 if (isCheckable())
101 {
102 parameters[QLatin1String("isChecked")] = !isChecked();
103 }
104
105 Application::triggerAction(identifier, parameters, this);
106
107 setDefaultAction(nullptr);
108
109 ToolButtonWidget::mouseReleaseEvent(event);
110
111 setDefaultAction(m_action);
112 }
113
dragEnterEvent(QDragEnterEvent * event)114 void ActionWidget::dragEnterEvent(QDragEnterEvent *event)
115 {
116 if (event->mimeData()->hasUrls())
117 {
118 event->accept();
119 }
120 else
121 {
122 event->ignore();
123 }
124 }
125
dropEvent(QDropEvent * event)126 void ActionWidget::dropEvent(QDropEvent *event)
127 {
128 if (event->mimeData()->hasUrls())
129 {
130 QVariantMap parameters(getParameters());
131 const QVector<QUrl> urls(Utils::extractUrls(event->mimeData()));
132 SessionsManager::OpenHints hints(SessionsManager::calculateOpenHints(((m_action->getIdentifier() == ActionsManager::NewWindowAction || m_action->getIdentifier() == ActionsManager::NewWindowPrivateAction) ? SessionsManager::NewWindowOpen : SessionsManager::NewTabOpen), Qt::LeftButton, event->keyboardModifiers()));
133
134 if (m_action->getIdentifier() == ActionsManager::NewTabPrivateAction || m_action->getIdentifier() == ActionsManager::NewWindowPrivateAction)
135 {
136 hints |= SessionsManager::PrivateOpen;
137 }
138
139 parameters[QLatin1String("hints")] = QVariant(hints);
140
141 for (int i = 0; i < urls.count(); ++i)
142 {
143 QVariantMap actionParameters(parameters);
144 actionParameters[QLatin1String("url")] = urls.at(i);
145
146 Application::triggerAction(ActionsManager::OpenUrlAction, actionParameters, this);
147 }
148
149 event->accept();
150 }
151 else
152 {
153 event->ignore();
154 }
155 }
156
setWindow(Window * window)157 void ActionWidget::setWindow(Window *window)
158 {
159 if (m_action->getDefinition().scope == ActionsManager::ActionDefinition::WindowScope)
160 {
161 m_action->setExecutor(ActionExecutor::Object(window, window));
162 }
163 else
164 {
165 MainWindow *mainWindow(MainWindow::findMainWindow(this));
166
167 m_action->setExecutor(ActionExecutor::Object(mainWindow, mainWindow));
168 }
169 }
170
getIdentifier() const171 int ActionWidget::getIdentifier() const
172 {
173 return m_action->getIdentifier();
174 }
175
event(QEvent * event)176 bool ActionWidget::event(QEvent *event)
177 {
178 if (event->type() == QEvent::ToolTip)
179 {
180 QToolTip::showText(static_cast<QHelpEvent*>(event)->globalPos(), text() + (m_action->shortcut().isEmpty() ? QString() : QLatin1String(" (") + m_action->shortcut().toString(QKeySequence::NativeText) + QLatin1Char(')')));
181
182 return true;
183 }
184
185 return ToolButtonWidget::event(event);
186 }
187
NavigationActionWidget(Window * window,const ToolBarsManager::ToolBarDefinition::Entry & definition,QWidget * parent)188 NavigationActionWidget::NavigationActionWidget(Window *window, const ToolBarsManager::ToolBarDefinition::Entry &definition, QWidget *parent) : ActionWidget(((definition.action == QLatin1String("GoBackAction")) ? ActionsManager::GoBackAction : ActionsManager::GoForwardAction), window, definition, parent),
189 m_window(window)
190 {
191 setMenu(new QMenu(this));
192 setPopupMode(QToolButton::DelayedPopup);
193 setContextMenuPolicy(Qt::DefaultContextMenu);
194
195 menu()->installEventFilter(this);
196
197 const ToolBarWidget *toolBar(qobject_cast<ToolBarWidget*>(parent));
198
199 if (toolBar && toolBar->getDefinition().isGlobal())
200 {
201 connect(toolBar, &ToolBarWidget::windowChanged, this, &NavigationActionWidget::setWindow);
202 }
203
204 connect(menu(), &QMenu::aboutToShow, this, &NavigationActionWidget::updateMenu);
205 }
206
addMenuEntry(int index,const WindowHistoryEntry & entry)207 void NavigationActionWidget::addMenuEntry(int index, const WindowHistoryEntry &entry)
208 {
209 Action *action(new Action(ActionsManager::GoToHistoryIndexAction, {{QLatin1String("index"), index}}, ActionExecutor::Object(m_window, m_window), this));
210 action->setStatusTip(entry.url);
211
212 menu()->addAction(action);
213 }
214
updateMenu()215 void NavigationActionWidget::updateMenu()
216 {
217 if (!menu() || !m_window)
218 {
219 return;
220 }
221
222 menu()->clear();
223
224 const WindowHistoryInformation history(m_window->getContentsWidget()->getHistory());
225
226 if (getIdentifier() == ActionsManager::GoBackAction)
227 {
228 for (int i = (history.index - 1); i >= 0; --i)
229 {
230 addMenuEntry(i, history.entries.at(i));
231 }
232 }
233 else
234 {
235 for (int i = (history.index + 1); i < history.entries.count(); ++i)
236 {
237 addMenuEntry(i, history.entries.at(i));
238 }
239 }
240 }
241
setWindow(Window * window)242 void NavigationActionWidget::setWindow(Window *window)
243 {
244 m_window = window;
245 }
246
event(QEvent * event)247 bool NavigationActionWidget::event(QEvent *event)
248 {
249 switch (event->type())
250 {
251 case QEvent::ContextMenu:
252 {
253 QContextMenuEvent *contextMenuEvent(static_cast<QContextMenuEvent*>(event));
254
255 if (contextMenuEvent->reason() == QContextMenuEvent::Mouse)
256 {
257 contextMenuEvent->accept();
258
259 return true;
260 }
261
262 event->accept();
263
264 ActionExecutor::Object executor(m_window, m_window);
265 QMenu menu(this);
266 menu.addAction(new Action(ActionsManager::ClearTabHistoryAction, {}, executor, &menu));
267 menu.addAction(new Action(ActionsManager::ClearTabHistoryAction, {{QLatin1String("clearGlobalHistory"), true}}, {{QLatin1String("text"), QT_TRANSLATE_NOOP("actions", "Purge Tab History")}}, executor, &menu));
268
269 const ToolBarWidget *toolBar(qobject_cast<ToolBarWidget*>(parentWidget()));
270
271 if (toolBar)
272 {
273 menu.addSeparator();
274 menu.addActions(ToolBarWidget::createCustomizationMenu(toolBar->getIdentifier(), {}, &menu)->actions());
275 }
276
277 menu.exec(contextMenuEvent->globalPos());
278
279 return true;
280 }
281
282 return false;
283 case QEvent::MouseButtonDblClick:
284 case QEvent::MouseButtonPress:
285 case QEvent::Wheel:
286 GesturesManager::startGesture(this, event, {GesturesManager::ToolBarContext, GesturesManager::GenericContext});
287
288 break;
289 case QEvent::ToolTip:
290 {
291 const QKeySequence shortcut(ActionsManager::getActionShortcut(getIdentifier()));
292 QString toolTip(text() + (shortcut.isEmpty() ? QString() : QLatin1String(" (") + shortcut.toString(QKeySequence::NativeText) + QLatin1Char(')')));
293
294 if (m_window)
295 {
296 const WindowHistoryInformation history(m_window->getContentsWidget()->getHistory());
297
298 if (!history.entries.isEmpty())
299 {
300 int index(-1);
301
302 if (getIdentifier() == ActionsManager::GoBackAction && history.index > 0)
303 {
304 index = (history.index - 1);
305 }
306 else if (getIdentifier() == ActionsManager::GoForwardAction && history.index < (history.entries.count() - 1))
307 {
308 index = (history.index + 1);
309 }
310
311 if (index >= 0)
312 {
313 toolTip = history.entries.at(index).getTitle().replace(QLatin1Char('&'), QLatin1String("&&")) + QLatin1String(" (") + text() + (shortcut.isEmpty() ? QString() : QLatin1String(" - ") + shortcut.toString(QKeySequence::NativeText)) + QLatin1Char(')');
314 }
315 }
316 }
317
318 QToolTip::showText(static_cast<QHelpEvent*>(event)->globalPos(), toolTip);
319 }
320
321 return true;
322 default:
323 break;
324 }
325
326 return ActionWidget::event(event);
327 }
328
eventFilter(QObject * object,QEvent * event)329 bool NavigationActionWidget::eventFilter(QObject *object, QEvent *event)
330 {
331 if (event->type() == QEvent::ContextMenu)
332 {
333 const Action *action(qobject_cast<Action*>(menu()->activeAction()));
334
335 if (action && action->getIdentifier() == ActionsManager::GoToHistoryIndexAction)
336 {
337 ActionExecutor::Object executor(m_window, m_window);
338 const int index(action->getParameters().value(QLatin1String("index")).toInt());
339 QMenu contextMenu(menu());
340 Action *removeEntryAction(new Action(ActionsManager::RemoveHistoryIndexAction, {{QLatin1String("index"), index}}, {{QLatin1String("text"), tr("Remove Entry")}}, executor, &contextMenu));
341 removeEntryAction->setShortcut(QKeySequence(Qt::Key_Delete));
342
343 Action *purgeEntryAction(new Action(ActionsManager::RemoveHistoryIndexAction, {{QLatin1String("index"), index}}, {{QLatin1String("text"), tr("Purge Entry")}}, executor, &contextMenu));
344 purgeEntryAction->setShortcut(QKeySequence(Qt::ShiftModifier | Qt::Key_Delete));
345
346 contextMenu.addAction(removeEntryAction);
347 contextMenu.addAction(purgeEntryAction);
348
349 const QAction *selectedAction(contextMenu.exec(static_cast<QContextMenuEvent*>(event)->globalPos()));
350
351 if (selectedAction == removeEntryAction || selectedAction == purgeEntryAction)
352 {
353 menu()->close();
354 }
355 }
356 }
357 else if (event->type() == QEvent::KeyPress)
358 {
359 const QKeyEvent *keyEvent(static_cast<QKeyEvent*>(event));
360
361 if (keyEvent->key() == Qt::Key_Delete && m_window)
362 {
363 const Action *action(qobject_cast<Action*>(menu()->activeAction()));
364
365 if (action && action->getIdentifier() == ActionsManager::GoToHistoryIndexAction)
366 {
367 menu()->close();
368
369 m_window->triggerAction(ActionsManager::RemoveHistoryIndexAction, {{QLatin1String("index"), action->getParameters().value(QLatin1String("index"), -1).toInt()}, {QLatin1String("clearGlobalHistory"), keyEvent->modifiers().testFlag(Qt::ShiftModifier)}});
370 }
371 }
372 }
373
374 return QObject::eventFilter(object, event);
375 }
376
377 }
378