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