File indexing completed on 2024-05-19 04:59:14

0001 /* ============================================================
0002 * AutoScroll - Autoscroll for Falkon
0003 * Copyright (C) 2014-2017 David Rosca <nowrep@gmail.com>
0004 *
0005 * This program is free software: you can redistribute it and/or modify
0006 * it under the terms of the GNU General Public License as published by
0007 * the Free Software Foundation, either version 3 of the License, or
0008 * (at your option) any later version.
0009 *
0010 * This program is distributed in the hope that it will be useful,
0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0013 * GNU General Public License for more details.
0014 *
0015 * You should have received a copy of the GNU General Public License
0016 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
0017 * ============================================================ */
0018 #include "autoscroller.h"
0019 #include "framescroller.h"
0020 #include "webview.h"
0021 #include "webpage.h"
0022 #include "webhittestresult.h"
0023 
0024 #include <QApplication>
0025 #include <QMouseEvent>
0026 #include <QSettings>
0027 #include <QIcon>
0028 #include <QPainter>
0029 
0030 ScrollIndicator::ScrollIndicator(QWidget *parent)
0031     : QLabel(parent)
0032 {
0033     resize(33, 33);
0034     setContentsMargins(0, 0, 0, 0);
0035 }
0036 
0037 Qt::Orientations ScrollIndicator::orientations() const
0038 {
0039     return m_orientations;
0040 }
0041 
0042 void ScrollIndicator::setOrientations(Qt::Orientations orientations)
0043 {
0044     m_orientations = orientations;
0045 
0046     if (m_orientations == Qt::Vertical) {
0047         setPixmap(QIcon(QSL(":/autoscroll/data/scroll_vertical.png")).pixmap(33));
0048     } else if (m_orientations == Qt::Horizontal) {
0049         setPixmap(QIcon(QSL(":/autoscroll/data/scroll_horizontal.png")).pixmap(33));
0050     } else {
0051         setPixmap(QIcon(QSL(":/autoscroll/data/scroll_all.png")).pixmap(33));
0052     }
0053 
0054     update();
0055 }
0056 
0057 void ScrollIndicator::paintEvent(QPaintEvent *event)
0058 {
0059     QPainter p(this);
0060     p.setRenderHint(QPainter::Antialiasing);
0061 
0062     QRectF r(rect());
0063     r.adjust(1, 1, -1, -1);
0064 
0065     QColor c1(Qt::gray);
0066     c1.setAlpha(190);
0067 
0068     QColor c2(Qt::white);
0069     c2.setAlpha(190);
0070 
0071     QRadialGradient g(r.center(), r.height() / 2.0);
0072     g.setColorAt(1, c1);
0073     g.setColorAt(0.7, c2);
0074 
0075     p.setPen(Qt::NoPen);
0076     p.setBrush(g);
0077     p.drawEllipse(r);
0078 
0079     QLabel::paintEvent(event);
0080 }
0081 
0082 AutoScroller::AutoScroller(const QString &settingsFile, QObject* parent)
0083     : QObject(parent)
0084     , m_view(nullptr)
0085     , m_settingsFile(settingsFile)
0086 {
0087     m_indicator = new ScrollIndicator;
0088     m_indicator->installEventFilter(this);
0089 
0090     QSettings settings(m_settingsFile, QSettings::IniFormat);
0091     settings.beginGroup("AutoScroll");
0092 
0093     m_frameScroller = new FrameScroller(this);
0094     m_frameScroller->setScrollDivider(settings.value("ScrollDivider", 8.0).toDouble());
0095 
0096     settings.endGroup();
0097 }
0098 
0099 AutoScroller::~AutoScroller()
0100 {
0101     delete m_indicator;
0102 }
0103 
0104 bool AutoScroller::mouseMove(QObject* obj, QMouseEvent* event)
0105 {
0106     Q_UNUSED(obj)
0107 
0108     if (m_indicator->isVisible()) {
0109         QRect rect = indicatorGlobalRect();
0110         int xlength = 0;
0111         int ylength = 0;
0112 
0113         if (rect.left() > event->globalPosition().toPoint().x()) {
0114             xlength = event->globalPosition().toPoint().x() - rect.left();
0115         }
0116         else if (rect.right() < event->globalPosition().toPoint().x()) {
0117             xlength = event->globalPosition().toPoint().x() - rect.right();
0118         }
0119         if (rect.top() > event->globalPosition().toPoint().y()) {
0120             ylength = event->globalPosition().toPoint().y() - rect.top();
0121         }
0122         else if (rect.bottom() < event->globalPosition().toPoint().y()) {
0123             ylength = event->globalPosition().toPoint().y() - rect.bottom();
0124         }
0125 
0126         m_frameScroller->startScrolling(xlength, ylength);
0127     }
0128 
0129     return false;
0130 }
0131 
0132 bool AutoScroller::mousePress(QObject* obj, QMouseEvent* event)
0133 {
0134     bool middleButton = event->buttons() == Qt::MiddleButton;
0135     auto* view = qobject_cast<WebView*>(obj);
0136     Q_ASSERT(view);
0137 
0138     // Start?
0139     if (m_view != view && middleButton) {
0140         return showIndicator(view, event->position().toPoint());
0141     }
0142     else if (!m_indicator->isVisible() && middleButton) {
0143         return showIndicator(view, event->position().toPoint());
0144     }
0145 
0146     // Stop
0147     if (m_indicator->isVisible()) {
0148         stopScrolling();
0149         return true;
0150     }
0151 
0152     return false;
0153 }
0154 
0155 bool AutoScroller::mouseRelease(QObject* obj, QMouseEvent* event)
0156 {
0157     Q_UNUSED(obj)
0158 
0159     if (m_indicator->isVisible()) {
0160         if (!indicatorGlobalRect().contains(event->globalPosition().toPoint())) {
0161             stopScrolling();
0162         }
0163         return true;
0164     }
0165 
0166     return false;
0167 }
0168 
0169 bool AutoScroller::wheel(QObject *obj, QWheelEvent *event)
0170 {
0171     Q_UNUSED(obj)
0172     Q_UNUSED(event);
0173 
0174     if (m_indicator->isVisible()) {
0175         stopScrolling();
0176         return true;
0177     }
0178 
0179     return false;
0180 }
0181 
0182 double AutoScroller::scrollDivider() const
0183 {
0184     return m_frameScroller->scrollDivider();
0185 }
0186 
0187 void AutoScroller::setScrollDivider(double divider)
0188 {
0189     QSettings settings(m_settingsFile, QSettings::IniFormat);
0190     settings.beginGroup("AutoScroll");
0191     settings.setValue("ScrollDivider", divider);
0192     settings.endGroup();
0193 
0194     m_frameScroller->setScrollDivider(divider);
0195 }
0196 
0197 bool AutoScroller::eventFilter(QObject* obj, QEvent* event)
0198 {
0199     if (obj == m_indicator) {
0200         switch (event->type()) {
0201         case QEvent::Enter:
0202             m_frameScroller->stopScrolling();
0203             break;
0204 
0205         case QEvent::Wheel:
0206         case QEvent::Hide:
0207         case QEvent::MouseButtonPress:
0208             stopScrolling();
0209             break;
0210 
0211         default:
0212             break;
0213         }
0214     }
0215 
0216     return false;
0217 }
0218 
0219 bool AutoScroller::showIndicator(WebView* view, const QPoint &pos)
0220 {
0221     const WebHitTestResult res = view->page()->hitTestContent(pos);
0222 
0223     if (res.isContentEditable() || !res.linkUrl().isEmpty() || res.tagName().endsWith(QL1S("frame"))) {
0224         return false;
0225     }
0226 
0227     QString source = QL1S("var out = {"
0228                           " vertical: window.innerWidth > document.documentElement.clientWidth,"
0229                           " horizontal: window.innerHeight > document.documentElement.clientHeight"
0230                           "};"
0231                           "out;");
0232 
0233     const QVariantMap &map = view->page()->execJavaScript(source, WebPage::SafeJsWorld).toMap();
0234 
0235     bool vertical = map.value(QSL("vertical")).toBool();
0236     bool horizontal = map.value(QSL("horizontal")).toBool();
0237 
0238     if (!vertical && !horizontal) {
0239         return false;
0240     }
0241 
0242     Qt::Orientations orientations;
0243     if (vertical) {
0244         orientations |= Qt::Vertical;
0245     }
0246     if (horizontal) {
0247         orientations |= Qt::Horizontal;
0248     }
0249     m_indicator->setOrientations(orientations);
0250 
0251     m_view = view;
0252 
0253     QPoint p;
0254     p.setX(pos.x() - m_indicator->width() / 2);
0255     p.setY(pos.y() - m_indicator->height() / 2);
0256 
0257     m_indicator->setParent(m_view->overlayWidget());
0258     m_indicator->move(m_view->mapTo(m_view->overlayWidget(), p));
0259     m_indicator->show();
0260 
0261     m_frameScroller->setPage(view->page());
0262 
0263     m_view->inputWidget()->grabMouse();
0264     QApplication::setOverrideCursor(Qt::ArrowCursor);
0265 
0266     return true;
0267 }
0268 
0269 void AutoScroller::stopScrolling()
0270 {
0271     m_view->inputWidget()->releaseMouse();
0272     QApplication::restoreOverrideCursor();
0273 
0274     m_indicator->hide();
0275     m_indicator->setParent(nullptr);
0276     m_frameScroller->stopScrolling();
0277 }
0278 
0279 QRect AutoScroller::indicatorGlobalRect() const
0280 {
0281     QPoint pos = m_indicator->parentWidget()->mapToGlobal(m_indicator->geometry().topLeft());
0282     return QRect(pos.x(), pos.y(), m_indicator->width(), m_indicator->height());
0283 }