1 /* ============================================================
2 * AutoScroll - Autoscroll for Falkon
3 * Copyright (C) 2014-2017 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 "autoscroller.h"
19 #include "framescroller.h"
20 #include "webview.h"
21 #include "webpage.h"
22 #include "webhittestresult.h"
23
24 #include <QApplication>
25 #include <QMouseEvent>
26 #include <QSettings>
27 #include <QLabel>
28 #include <QIcon>
29
ScrollIndicator(QWidget * parent)30 ScrollIndicator::ScrollIndicator(QWidget *parent)
31 : QLabel(parent)
32 {
33 resize(33, 33);
34 setContentsMargins(0, 0, 0, 0);
35 }
36
orientations() const37 Qt::Orientations ScrollIndicator::orientations() const
38 {
39 return m_orientations;
40 }
41
setOrientations(Qt::Orientations orientations)42 void ScrollIndicator::setOrientations(Qt::Orientations orientations)
43 {
44 m_orientations = orientations;
45
46 if (m_orientations == Qt::Vertical) {
47 setPixmap(QIcon(QSL(":/autoscroll/data/scroll_vertical.png")).pixmap(33));
48 } else if (m_orientations == Qt::Horizontal) {
49 setPixmap(QIcon(QSL(":/autoscroll/data/scroll_horizontal.png")).pixmap(33));
50 } else {
51 setPixmap(QIcon(QSL(":/autoscroll/data/scroll_all.png")).pixmap(33));
52 }
53
54 update();
55 }
56
paintEvent(QPaintEvent * event)57 void ScrollIndicator::paintEvent(QPaintEvent *event)
58 {
59 QPainter p(this);
60 p.setRenderHint(QPainter::Antialiasing);
61
62 QRectF r(rect());
63 r.adjust(1, 1, -1, -1);
64
65 QColor c1(Qt::gray);
66 c1.setAlpha(190);
67
68 QColor c2(Qt::white);
69 c2.setAlpha(190);
70
71 QRadialGradient g(r.center(), r.height() / 2.0);
72 g.setColorAt(1, c1);
73 g.setColorAt(0.7, c2);
74
75 p.setPen(Qt::NoPen);
76 p.setBrush(g);
77 p.drawEllipse(r);
78
79 QLabel::paintEvent(event);
80 }
81
AutoScroller(const QString & settingsFile,QObject * parent)82 AutoScroller::AutoScroller(const QString &settingsFile, QObject* parent)
83 : QObject(parent)
84 , m_view(0)
85 , m_settingsFile(settingsFile)
86 {
87 m_indicator = new ScrollIndicator;
88 m_indicator->installEventFilter(this);
89
90 QSettings settings(m_settingsFile, QSettings::IniFormat);
91 settings.beginGroup("AutoScroll");
92
93 m_frameScroller = new FrameScroller(this);
94 m_frameScroller->setScrollDivider(settings.value("ScrollDivider", 8.0).toDouble());
95
96 settings.endGroup();
97 }
98
~AutoScroller()99 AutoScroller::~AutoScroller()
100 {
101 delete m_indicator;
102 }
103
mouseMove(QObject * obj,QMouseEvent * event)104 bool AutoScroller::mouseMove(QObject* obj, QMouseEvent* event)
105 {
106 Q_UNUSED(obj)
107
108 if (m_indicator->isVisible()) {
109 QRect rect = indicatorGlobalRect();
110 int xlength = 0;
111 int ylength = 0;
112
113 if (rect.left() > event->globalPos().x()) {
114 xlength = event->globalPos().x() - rect.left();
115 }
116 else if (rect.right() < event->globalPos().x()) {
117 xlength = event->globalPos().x() - rect.right();
118 }
119 if (rect.top() > event->globalPos().y()) {
120 ylength = event->globalPos().y() - rect.top();
121 }
122 else if (rect.bottom() < event->globalPos().y()) {
123 ylength = event->globalPos().y() - rect.bottom();
124 }
125
126 m_frameScroller->startScrolling(xlength, ylength);
127 }
128
129 return false;
130 }
131
mousePress(QObject * obj,QMouseEvent * event)132 bool AutoScroller::mousePress(QObject* obj, QMouseEvent* event)
133 {
134 bool middleButton = event->buttons() == Qt::MiddleButton;
135 WebView* view = qobject_cast<WebView*>(obj);
136 Q_ASSERT(view);
137
138 // Start?
139 if (m_view != view && middleButton) {
140 return showIndicator(view, event->pos());
141 }
142 else if (!m_indicator->isVisible() && middleButton) {
143 return showIndicator(view, event->pos());
144 }
145
146 // Stop
147 if (m_indicator->isVisible()) {
148 stopScrolling();
149 return true;
150 }
151
152 return false;
153 }
154
mouseRelease(QObject * obj,QMouseEvent * event)155 bool AutoScroller::mouseRelease(QObject* obj, QMouseEvent* event)
156 {
157 Q_UNUSED(obj)
158
159 if (m_indicator->isVisible()) {
160 if (!indicatorGlobalRect().contains(event->globalPos())) {
161 stopScrolling();
162 }
163 return true;
164 }
165
166 return false;
167 }
168
wheel(QObject * obj,QWheelEvent * event)169 bool AutoScroller::wheel(QObject *obj, QWheelEvent *event)
170 {
171 Q_UNUSED(obj)
172 Q_UNUSED(event);
173
174 if (m_indicator->isVisible()) {
175 stopScrolling();
176 return true;
177 }
178
179 return false;
180 }
181
scrollDivider() const182 double AutoScroller::scrollDivider() const
183 {
184 return m_frameScroller->scrollDivider();
185 }
186
setScrollDivider(double divider)187 void AutoScroller::setScrollDivider(double divider)
188 {
189 QSettings settings(m_settingsFile, QSettings::IniFormat);
190 settings.beginGroup("AutoScroll");
191 settings.setValue("ScrollDivider", divider);
192 settings.endGroup();
193
194 m_frameScroller->setScrollDivider(divider);
195 }
196
eventFilter(QObject * obj,QEvent * event)197 bool AutoScroller::eventFilter(QObject* obj, QEvent* event)
198 {
199 if (obj == m_indicator) {
200 switch (event->type()) {
201 case QEvent::Enter:
202 m_frameScroller->stopScrolling();
203 break;
204
205 case QEvent::Wheel:
206 case QEvent::Hide:
207 case QEvent::MouseButtonPress:
208 stopScrolling();
209 break;
210
211 default:
212 break;
213 }
214 }
215
216 return false;
217 }
218
showIndicator(WebView * view,const QPoint & pos)219 bool AutoScroller::showIndicator(WebView* view, const QPoint &pos)
220 {
221 const WebHitTestResult res = view->page()->hitTestContent(pos);
222
223 if (res.isContentEditable() || !res.linkUrl().isEmpty() || res.tagName().endsWith(QL1S("frame"))) {
224 return false;
225 }
226
227 QString source = QL1S("var out = {"
228 " vertical: window.innerWidth > document.documentElement.clientWidth,"
229 " horizontal: window.innerHeight > document.documentElement.clientHeight"
230 "};"
231 "out;");
232
233 const QVariantMap &map = view->page()->execJavaScript(source, WebPage::SafeJsWorld).toMap();
234
235 bool vertical = map.value(QSL("vertical")).toBool();
236 bool horizontal = map.value(QSL("horizontal")).toBool();
237
238 if (!vertical && !horizontal) {
239 return false;
240 }
241
242 Qt::Orientations orientations;
243 if (vertical) {
244 orientations |= Qt::Vertical;
245 }
246 if (horizontal) {
247 orientations |= Qt::Horizontal;
248 }
249 m_indicator->setOrientations(orientations);
250
251 m_view = view;
252
253 QPoint p;
254 p.setX(pos.x() - m_indicator->width() / 2);
255 p.setY(pos.y() - m_indicator->height() / 2);
256
257 m_indicator->setParent(m_view->overlayWidget());
258 m_indicator->move(m_view->mapTo(m_view->overlayWidget(), p));
259 m_indicator->show();
260
261 m_frameScroller->setPage(view->page());
262
263 m_view->inputWidget()->grabMouse();
264 QApplication::setOverrideCursor(Qt::ArrowCursor);
265
266 return true;
267 }
268
stopScrolling()269 void AutoScroller::stopScrolling()
270 {
271 m_view->inputWidget()->releaseMouse();
272 QApplication::restoreOverrideCursor();
273
274 m_indicator->hide();
275 m_indicator->setParent(0);
276 m_frameScroller->stopScrolling();
277 }
278
indicatorGlobalRect() const279 QRect AutoScroller::indicatorGlobalRect() const
280 {
281 QPoint pos = m_indicator->parentWidget()->mapToGlobal(m_indicator->geometry().topLeft());
282 return QRect(pos.x(), pos.y(), m_indicator->width(), m_indicator->height());
283 }
284