File indexing completed on 2024-03-24 15:32:36

0001 /* This file is part of the KDE project
0002  *
0003  * Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
0004  *                     1999 Lars Knoll <knoll@kde.org>
0005  *                     1999 Antti Koivisto <koivisto@kde.org>
0006  *                     2000-2004 Dirk Mueller <mueller@kde.org>
0007  *                     2003 Leo Savernik <l.savernik@aon.at>
0008  *                     2003-2008 Apple Computer, Inc.
0009  *                     2008 Allan Sandfeld Jensen <kde@carewolf.com>
0010  *                     2006-2008 Germain Garand <germain@ebooksfrance.org>
0011  *
0012  * This library is free software; you can redistribute it and/or
0013  * modify it under the terms of the GNU Library General Public
0014  * License as published by the Free Software Foundation; either
0015  * version 2 of the License, or (at your option) any later version.
0016  *
0017  * This library is distributed in the hope that it will be useful,
0018  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0019  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0020  * Library General Public License for more details.
0021  *
0022  * You should have received a copy of the GNU Library General Public License
0023  * along with this library; see the file COPYING.LIB.  If not, write to
0024  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0025  * Boston, MA 02110-1301, USA.
0026  */
0027 
0028 #include "khtmlview.h"
0029 
0030 #include "khtml_part.h"
0031 #include "khtml_events.h"
0032 #include <config-khtml.h>
0033 #if HAVE_X11
0034 #include <qx11info_x11.h>
0035 #endif
0036 
0037 #include "html/html_documentimpl.h"
0038 #include "html/html_inlineimpl.h"
0039 #include "html/html_formimpl.h"
0040 #include "html/htmltokenizer.h"
0041 #include "editing/editor.h"
0042 #include "rendering/render_arena.h"
0043 #include "rendering/render_canvas.h"
0044 #include "rendering/render_frames.h"
0045 #include "rendering/render_replaced.h"
0046 #include "rendering/render_form.h"
0047 #include "rendering/render_layer.h"
0048 #include "rendering/render_line.h"
0049 #include "rendering/render_table.h"
0050 // removeme
0051 #define protected public
0052 #include "rendering/render_text.h"
0053 #undef protected
0054 #include "xml/dom2_eventsimpl.h"
0055 #include "css/cssstyleselector.h"
0056 #include "misc/loader.h"
0057 #include "khtml_settings.h"
0058 #include "khtml_printsettings.h"
0059 
0060 #include "khtmlpart_p.h"
0061 
0062 #include <kcursor.h>
0063 #include "khtml_debug.h"
0064 #include <kiconloader.h>
0065 #include <knotification.h>
0066 #include <kconfig.h>
0067 #include <../khtml_version.h>
0068 
0069 #include <kstringhandler.h>
0070 #include <kconfiggroup.h>
0071 #include <ksharedconfig.h>
0072 #include <KWindowSystem>
0073 
0074 #include <QBitmap>
0075 #include <QDialog>
0076 #include <QDialogButtonBox>
0077 #include <QLabel>
0078 #include <QKeyEvent>
0079 #include <QObject>
0080 #include <QPainter>
0081 #include <QHash>
0082 #include <QToolTip>
0083 #include <QString>
0084 #include <QTextDocument>
0085 #include <QTimer>
0086 #include <QAbstractEventDispatcher>
0087 #include <QVector>
0088 #include <QAbstractScrollArea>
0089 #include <QPrinter>
0090 #include <QPrintDialog>
0091 #include <qstandardpaths.h>
0092 
0093 //#define DEBUG_FLICKER
0094 
0095 #include <limits.h>
0096 #if HAVE_X11
0097 #include <X11/Xlib.h>
0098 #include <fixx11h.h>
0099 #elif defined(Q_OS_WIN)
0100 #include <windows.h>
0101 #endif
0102 
0103 #if 0
0104 namespace khtml
0105 {
0106 void dumpLineBoxes(RenderFlow *flow);
0107 }
0108 #endif
0109 
0110 using namespace DOM;
0111 using namespace khtml;
0112 
0113 #ifndef NDEBUG
0114 static const int sFirstLayoutDelay = 520;
0115 static const int sParsingLayoutsInterval = 380;
0116 static const int sLayoutAttemptDelay = 300;
0117 #else
0118 static const int sFirstLayoutDelay = 280;
0119 static const int sParsingLayoutsInterval = 320;
0120 static const int sLayoutAttemptDelay = 200;
0121 #endif
0122 static const int sLayoutAttemptIncrement = 20;
0123 static const int sParsingLayoutsIncrement = 60;
0124 
0125 static const int sSmoothScrollTime = 128;
0126 static const int sSmoothScrollTick = 16;
0127 static const int sSmoothScrollMinStaticPixels = 320 * 200;
0128 
0129 static const int sMaxMissedDeadlines = 12;
0130 static const int sWayTooMany = -1;
0131 
0132 class KHTMLViewPrivate
0133 {
0134     friend class KHTMLView;
0135 public:
0136 
0137     enum PseudoFocusNodes {
0138         PFNone,
0139         PFTop,
0140         PFBottom
0141     };
0142 
0143     enum StaticBackgroundState {
0144         SBNone = 0,
0145         SBPartial,
0146         SBFull
0147     };
0148 
0149     enum CompletedState {
0150         CSNone = 0,
0151         CSFull,
0152         CSActionPending
0153     };
0154 
0155     KHTMLViewPrivate(KHTMLView *v)
0156         : underMouse(nullptr), underMouseNonShared(nullptr), oldUnderMouse(nullptr)
0157     {
0158         postponed_autorepeat = nullptr;
0159         scrollingFromWheelTimerId = 0;
0160         smoothScrollMode = KHTMLView::SSMWhenEfficient;
0161 
0162         reset();
0163         vpolicy = Qt::ScrollBarAsNeeded;
0164         hpolicy = Qt::ScrollBarAsNeeded;
0165         formCompletions = nullptr;
0166         prevScrollbarVisible = true;
0167 
0168         possibleTripleClick = false;
0169         emitCompletedAfterRepaint = CSNone;
0170         cursorIconWidget = nullptr;
0171         cursorIconType   = KHTMLView::LINK_NORMAL;
0172         m_mouseScrollTimer = nullptr;
0173         m_mouseScrollIndicator = nullptr;
0174         contentsX = 0;
0175         contentsY = 0;
0176         view = v;
0177     }
0178     ~KHTMLViewPrivate()
0179     {
0180         delete formCompletions;
0181         delete postponed_autorepeat;
0182         if (underMouse) {
0183             underMouse->deref();
0184         }
0185         if (underMouseNonShared) {
0186             underMouseNonShared->deref();
0187         }
0188         if (oldUnderMouse) {
0189             oldUnderMouse->deref();
0190         }
0191 
0192         delete cursorIconWidget;
0193         delete m_mouseScrollTimer;
0194         delete m_mouseScrollIndicator;
0195     }
0196     void reset()
0197     {
0198         if (underMouse) {
0199             underMouse->deref();
0200         }
0201         underMouse = nullptr;
0202         if (underMouseNonShared) {
0203             underMouseNonShared->deref();
0204         }
0205         underMouseNonShared = nullptr;
0206         if (oldUnderMouse) {
0207             oldUnderMouse->deref();
0208         }
0209         oldUnderMouse = nullptr;
0210         linkPressed = false;
0211         staticWidget = SBNone;
0212         fixedObjectsCount = 0;
0213         staticObjectsCount = 0;
0214         tabMovePending = false;
0215         lastTabbingDirection = true;
0216         pseudoFocusNode = PFNone;
0217         zoomLevel = 100;
0218 #ifndef KHTML_NO_SCROLLBARS
0219         //We don't turn off the toolbars here
0220         //since if the user turns them
0221         //off, then chances are they want them turned
0222         //off always - even after a reset.
0223 #else
0224         vpolicy = ScrollBarAlwaysOff;
0225         hpolicy = ScrollBarAlwaysOff;
0226 #endif
0227         scrollBarMoved = false;
0228         contentsMoving = false;
0229         ignoreWheelEvents = false;
0230         scrollingFromWheel = QPoint(-1, -1);
0231         borderX = 30;
0232         borderY = 30;
0233         steps = 0;
0234         dx = dy = 0;
0235         paged = false;
0236         clickX = -1;
0237         clickY = -1;
0238         clickCount = 0;
0239         isDoubleClick = false;
0240         scrollingSelf = false;
0241         delete postponed_autorepeat;
0242         postponed_autorepeat = nullptr;
0243         layoutTimerId = 0;
0244         repaintTimerId = 0;
0245         scrollTimerId = 0;
0246         scrollSuspended = false;
0247         scrollSuspendPreActivate = false;
0248         smoothScrolling = false;
0249         smoothScrollModeIsDefault = true;
0250         shouldSmoothScroll = false;
0251         smoothScrollMissedDeadlines = 0;
0252         hasFrameset = false;
0253         complete = false;
0254         firstLayoutPending = true;
0255 #ifdef SPEED_DEBUG
0256         firstRepaintPending = true;
0257 #endif
0258         needsFullRepaint = true;
0259         dirtyLayout = false;
0260         layoutSchedulingEnabled = true;
0261         painting = false;
0262         layoutCounter = 0;
0263         layoutAttemptCounter = 0;
0264         scheduledLayoutCounter = 0;
0265         updateRegion = QRegion();
0266         m_dialogsAllowed = true;
0267         accessKeysActivated = false;
0268         accessKeysPreActivate = false;
0269 
0270         // the view might have been built before the part it will be assigned to,
0271         // so exceptionally, we need to directly ref/deref KHTMLGlobal to
0272         // account for this transitory case.
0273         KHTMLGlobal::ref();
0274         accessKeysEnabled = KHTMLGlobal::defaultHTMLSettings()->accessKeysEnabled();
0275         KHTMLGlobal::deref();
0276 
0277         emitCompletedAfterRepaint = CSNone;
0278         m_mouseEventsTarget = nullptr;
0279         m_clipHolder = nullptr;
0280     }
0281     void newScrollTimer(QWidget *view, int tid)
0282     {
0283         //qCDebug(KHTML_LOG) << "newScrollTimer timer " << tid;
0284         view->killTimer(scrollTimerId);
0285         scrollTimerId = tid;
0286         scrollSuspended = false;
0287     }
0288     enum ScrollDirection { ScrollLeft, ScrollRight, ScrollUp, ScrollDown };
0289 
0290     void adjustScroller(QWidget *view, ScrollDirection direction, ScrollDirection oppositedir)
0291     {
0292         static const struct {
0293             int msec, pixels;
0294         } timings [] = {
0295             {320, 1}, {224, 1}, {160, 1}, {112, 1}, {80, 1}, {56, 1}, {40, 1},
0296             {28, 1}, {20, 1}, {20, 2}, {20, 3}, {20, 4}, {20, 6}, {20, 8}, {0, 0}
0297         };
0298         if (!scrollTimerId ||
0299                 (static_cast<int>(scrollDirection) != direction &&
0300                  (static_cast<int>(scrollDirection) != oppositedir || scrollSuspended))) {
0301             scrollTiming = 6;
0302             scrollBy = timings[scrollTiming].pixels;
0303             scrollDirection = direction;
0304             newScrollTimer(view, view->startTimer(timings[scrollTiming].msec));
0305         } else if (scrollDirection == direction &&
0306                    timings[scrollTiming + 1].msec && !scrollSuspended) {
0307             scrollBy = timings[++scrollTiming].pixels;
0308             newScrollTimer(view, view->startTimer(timings[scrollTiming].msec));
0309         } else if (scrollDirection == oppositedir) {
0310             if (scrollTiming) {
0311                 scrollBy = timings[--scrollTiming].pixels;
0312                 newScrollTimer(view, view->startTimer(timings[scrollTiming].msec));
0313             }
0314         }
0315         scrollSuspended = false;
0316     }
0317 
0318     bool haveZoom() const
0319     {
0320         return zoomLevel != 100;
0321     }
0322 
0323     void startScrolling()
0324     {
0325         smoothScrolling = true;
0326         smoothScrollTimer.start(sSmoothScrollTick);
0327         shouldSmoothScroll = false;
0328     }
0329 
0330     void stopScrolling()
0331     {
0332         smoothScrollTimer.stop();
0333         dx = dy = 0;
0334         steps = 0;
0335         updateContentsXY();
0336         smoothScrolling = false;
0337         shouldSmoothScroll = false;
0338     }
0339 
0340     void updateContentsXY()
0341     {
0342         contentsX = QApplication::isRightToLeft() ?
0343                     view->horizontalScrollBar()->maximum() - view->horizontalScrollBar()->value() : view->horizontalScrollBar()->value();
0344         contentsY = view->verticalScrollBar()->value();
0345     }
0346     void scrollAccessKeys(int dx, int dy)
0347     {
0348         QList<QLabel *> wl = view->widget()->findChildren<QLabel *>("KHTMLAccessKey");
0349         foreach (QLabel *w, wl) {
0350             w->move(w->pos() + QPoint(dx, dy));
0351         }
0352     }
0353     void scrollExternalWidgets(int dx, int dy)
0354     {
0355         if (visibleWidgets.isEmpty()) {
0356             return;
0357         }
0358 
0359         QHashIterator<void *, QWidget *> it(visibleWidgets);
0360         while (it.hasNext()) {
0361             it.next();
0362             it.value()->move(it.value()->pos() + QPoint(dx, dy));
0363         }
0364     }
0365 
0366     NodeImpl *underMouse;
0367     NodeImpl *underMouseNonShared;
0368     NodeImpl *oldUnderMouse;
0369 
0370     // Do not adjust bitfield enums sizes.
0371     // They are oversized because they are signed on some platforms.
0372     bool tabMovePending: 1;
0373     bool lastTabbingDirection: 1;
0374     PseudoFocusNodes pseudoFocusNode: 3;
0375     bool scrollBarMoved: 1;
0376     bool contentsMoving: 1;
0377 
0378     Qt::ScrollBarPolicy vpolicy;
0379     Qt::ScrollBarPolicy hpolicy;
0380     bool prevScrollbarVisible: 1;
0381     bool linkPressed: 1;
0382     bool ignoreWheelEvents: 1;
0383     StaticBackgroundState staticWidget: 3;
0384     int staticObjectsCount;
0385     int fixedObjectsCount;
0386 
0387     int zoomLevel;
0388     int borderX, borderY;
0389     int dx, dy;
0390     int steps;
0391     KConfig *formCompletions;
0392 
0393     int clickX, clickY, clickCount;
0394     bool isDoubleClick;
0395 
0396     bool paged;
0397 
0398     bool scrollingSelf;
0399     int contentsX, contentsY;
0400     int layoutTimerId;
0401     QKeyEvent *postponed_autorepeat;
0402 
0403     int repaintTimerId;
0404     int scrollTimerId;
0405     int scrollTiming;
0406     int scrollBy;
0407     ScrollDirection scrollDirection     : 3;
0408     bool scrollSuspended            : 1;
0409     bool scrollSuspendPreActivate       : 1;
0410     KHTMLView::SmoothScrollingMode smoothScrollMode : 3;
0411     bool smoothScrolling                          : 1;
0412     bool smoothScrollModeIsDefault                : 1;
0413     bool shouldSmoothScroll                       : 1;
0414     bool hasFrameset                              : 1;
0415     bool complete               : 1;
0416     bool firstLayoutPending         : 1;
0417 #ifdef SPEED_DEBUG
0418     bool firstRepaintPending                    : 1;
0419 #endif
0420     bool layoutSchedulingEnabled        : 1;
0421     bool needsFullRepaint           : 1;
0422     bool painting               : 1;
0423     bool possibleTripleClick            : 1;
0424     bool dirtyLayout                           : 1;
0425     bool m_dialogsAllowed           : 1;
0426     short smoothScrollMissedDeadlines;
0427     int layoutCounter;
0428     int layoutAttemptCounter;
0429     int scheduledLayoutCounter;
0430     QRegion updateRegion;
0431     QTimer smoothScrollTimer;
0432     QTime smoothScrollStopwatch;
0433     QHash<void *, QWidget *> visibleWidgets;
0434     bool accessKeysEnabled;
0435     bool accessKeysActivated;
0436     bool accessKeysPreActivate;
0437     CompletedState emitCompletedAfterRepaint;
0438 
0439     QLabel               *cursorIconWidget;
0440     KHTMLView::LinkCursor cursorIconType;
0441 
0442     // scrolling activated by MMB
0443     short m_mouseScroll_byX;
0444     short m_mouseScroll_byY;
0445     QPoint scrollingFromWheel;
0446     int scrollingFromWheelTimerId;
0447     QTimer *m_mouseScrollTimer;
0448     QWidget *m_mouseScrollIndicator;
0449     QPointer<QWidget> m_mouseEventsTarget;
0450     QStack<QRegion> *m_clipHolder;
0451     KHTMLView *view;
0452 };
0453 
0454 #ifndef QT_NO_TOOLTIP
0455 
0456 /** calculates the client-side image map rectangle for the given image element
0457  * @param img image element
0458  * @param scrollOfs scroll offset of viewport in content coordinates
0459  * @param p position to be probed in viewport coordinates
0460  * @param r returns the bounding rectangle in content coordinates
0461  * @param s returns the title string
0462  * @return true if an appropriate area was found -- only in this case r and
0463  *  s are valid, false otherwise
0464  */
0465 static bool findImageMapRect(HTMLImageElementImpl *img, const QPoint &scrollOfs,
0466                              const QPoint &p, QRect &r, QString &s)
0467 {
0468     HTMLMapElementImpl *map;
0469     if (img && img->document()->isHTMLDocument() &&
0470             (map = static_cast<HTMLDocumentImpl *>(img->document())->getMap(img->imageMap()))) {
0471         RenderObject::NodeInfo info(true, false);
0472         RenderObject *rend = img->renderer();
0473         int ax, ay;
0474         if (!rend || !rend->absolutePosition(ax, ay)) {
0475             return false;
0476         }
0477         // we're a client side image map
0478         bool inside = map->mapMouseEvent(p.x() - ax + scrollOfs.x(),
0479                                          p.y() - ay + scrollOfs.y(), rend->contentWidth(),
0480                                          rend->contentHeight(), info);
0481         if (inside && info.URLElement()) {
0482             HTMLAreaElementImpl *area = static_cast<HTMLAreaElementImpl *>(info.URLElement());
0483             Q_ASSERT(area->id() == ID_AREA);
0484             s = area->getAttribute(ATTR_TITLE).string();
0485             QRegion reg = area->cachedRegion();
0486             if (!s.isEmpty() && !reg.isEmpty()) {
0487                 r = reg.boundingRect();
0488                 r.translate(ax, ay);
0489                 return true;
0490             }
0491         }
0492     }
0493     return false;
0494 }
0495 
0496 bool KHTMLView::event(QEvent *e)
0497 {
0498     switch (e->type()) {
0499     case QEvent::ToolTip: {
0500         QHelpEvent *he = static_cast<QHelpEvent *>(e);
0501         QPoint     p   = he->pos();
0502 
0503         DOM::NodeImpl *node = d->underMouseNonShared;
0504         QRect region;
0505         while (node) {
0506             if (node->isElementNode()) {
0507                 DOM::ElementImpl *e = static_cast<DOM::ElementImpl *>(node);
0508                 QRect r;
0509                 QString s;
0510                 bool found = false;
0511                 // for images, check if it is part of a client-side image map,
0512                 // and query the <area>s' title attributes, too
0513                 if (e->id() == ID_IMG && !e->getAttribute(ATTR_USEMAP).isEmpty()) {
0514                     found = findImageMapRect(static_cast<HTMLImageElementImpl *>(e),
0515                                              viewportToContents(QPoint(0, 0)), p, r, s);
0516                 }
0517                 if (!found) {
0518                     s = e->getAttribute(ATTR_TITLE).string().trimmed();
0519                     r = node->getRect();
0520                 }
0521                 region |= QRect(contentsToViewport(r.topLeft()), r.size());
0522                 if (!s.isEmpty()) {
0523                     QToolTip::showText(he->globalPos(),
0524                                        Qt::convertFromPlainText(s, Qt::WhiteSpaceNormal),
0525                                        widget(), region);
0526                     break;
0527                 }
0528             }
0529             node = node->parentNode();
0530         }
0531         // Qt makes tooltip events happen nearly immediately when a preceding one was processed in the past few seconds.
0532         // We don't want that feature to apply to web tootlips however, as it clashes with dhtml menus.
0533         // So we'll just pretend we did not process that event.
0534         return false;
0535     }
0536 
0537     case QEvent::DragEnter:
0538     case QEvent::DragMove:
0539     case QEvent::DragLeave:
0540     case QEvent::Drop:
0541         // In Qt4, one needs to both call accept() on the DND event and return
0542         // true on ::event for the candidate widget for the drop to be possible.
0543         // Apps hosting us, such as konq, can do the former but not the later.
0544         // We will do the second bit, as it's a no-op unless someone else explicitly
0545         // accepts the event. We need to skip the scrollarea to do that,
0546         // since it will just skip the events, both killing the drop, and
0547         // not permitting us to forward it up the part hiearchy in our dragEnterEvent,
0548         // etc. handlers
0549         return QWidget::event(e);
0550     case QEvent::StyleChange:
0551     case QEvent::LayoutRequest: {
0552         updateScrollBars();
0553         return QAbstractScrollArea::event(e);
0554     }
0555     case QEvent::PaletteChange:
0556         slotPaletteChanged();
0557         return QScrollArea::event(e);
0558     default:
0559         return QScrollArea::event(e);
0560     }
0561 }
0562 #endif
0563 
0564 KHTMLView::KHTMLView(KHTMLPart *part, QWidget *parent)
0565     : QScrollArea(parent), d(new KHTMLViewPrivate(this))
0566 {
0567     m_medium = "screen";
0568 
0569     m_part = part;
0570 
0571     QScrollArea::setVerticalScrollBarPolicy(d->vpolicy);
0572     QScrollArea::setHorizontalScrollBarPolicy(d->hpolicy);
0573 
0574     initWidget();
0575     widget()->setMouseTracking(true);
0576 }
0577 
0578 KHTMLView::~KHTMLView()
0579 {
0580     closeChildDialogs();
0581     if (m_part) {
0582         DOM::DocumentImpl *doc = m_part->xmlDocImpl();
0583         if (doc) {
0584             doc->detach();
0585         }
0586     }
0587     delete d;
0588 }
0589 
0590 void KHTMLView::setPart(KHTMLPart *part)
0591 {
0592     assert(part && !m_part);
0593     m_part = part;
0594 }
0595 
0596 void KHTMLView::initWidget()
0597 {
0598     // Do not access the part here. It might not be fully constructed.
0599 
0600     setFrameStyle(QFrame::NoFrame);
0601     setFocusPolicy(Qt::StrongFocus);
0602     viewport()->setFocusProxy(this);
0603 
0604     _marginWidth = -1; // undefined
0605     _marginHeight = -1;
0606     _width = 0;
0607     _height = 0;
0608 
0609     installEventFilter(this);
0610 
0611     setAcceptDrops(true);
0612     if (!widget()) {
0613         setWidget(new QWidget(this));
0614     }
0615     widget()->setAttribute(Qt::WA_NoSystemBackground);
0616 
0617     // Do *not* remove this attribute frivolously.
0618     // You might not notice a change of behaviour in Debug builds
0619     // but removing opaque events will make QWidget::scroll fail horribly
0620     // in Release builds.
0621     widget()->setAttribute(Qt::WA_OpaquePaintEvent);
0622 
0623     verticalScrollBar()->setCursor(Qt::ArrowCursor);
0624     horizontalScrollBar()->setCursor(Qt::ArrowCursor);
0625 
0626     connect(&d->smoothScrollTimer, SIGNAL(timeout()), this, SLOT(scrollTick()));
0627 }
0628 
0629 void KHTMLView::resizeContentsToViewport()
0630 {
0631     QSize s = viewport()->size();
0632     resizeContents(s.width(), s.height());
0633 }
0634 
0635 // called by KHTMLPart::clear()
0636 void KHTMLView::clear()
0637 {
0638     if (d->accessKeysEnabled && d->accessKeysActivated) {
0639         accessKeysTimeout();
0640     }
0641     viewport()->unsetCursor();
0642     if (d->cursorIconWidget) {
0643         d->cursorIconWidget->hide();
0644     }
0645     if (d->smoothScrolling) {
0646         d->stopScrolling();
0647     }
0648     d->reset();
0649     QAbstractEventDispatcher::instance()->unregisterTimers(this);
0650     emit cleared();
0651 
0652     QScrollArea::setHorizontalScrollBarPolicy(d->hpolicy);
0653     QScrollArea::setVerticalScrollBarPolicy(d->vpolicy);
0654     verticalScrollBar()->setEnabled(false);
0655     horizontalScrollBar()->setEnabled(false);
0656 
0657 }
0658 
0659 void KHTMLView::hideEvent(QHideEvent *e)
0660 {
0661     QScrollArea::hideEvent(e);
0662 }
0663 
0664 void KHTMLView::showEvent(QShowEvent *e)
0665 {
0666     QScrollArea::showEvent(e);
0667 }
0668 
0669 void KHTMLView::setMouseEventsTarget(QWidget *w)
0670 {
0671     d->m_mouseEventsTarget = w;
0672 }
0673 
0674 QWidget *KHTMLView::mouseEventsTarget() const
0675 {
0676     return d->m_mouseEventsTarget;
0677 }
0678 
0679 void KHTMLView::setClipHolder(QStack<QRegion> *ch)
0680 {
0681     d->m_clipHolder = ch;
0682 }
0683 
0684 QStack<QRegion> *KHTMLView::clipHolder() const
0685 {
0686     return d->m_clipHolder;
0687 }
0688 
0689 int KHTMLView::contentsWidth() const
0690 {
0691     return widget() ? widget()->width() : 0;
0692 }
0693 
0694 int KHTMLView::contentsHeight() const
0695 {
0696     return widget() ? widget()->height() : 0;
0697 }
0698 
0699 void KHTMLView::resizeContents(int w, int h)
0700 {
0701     if (!widget()) {
0702         return;
0703     }
0704     widget()->resize(w, h);
0705     if (!widget()->isVisible()) {
0706         updateScrollBars();
0707     }
0708 }
0709 
0710 int KHTMLView::contentsX() const
0711 {
0712     return d->contentsX;
0713 }
0714 
0715 int KHTMLView::contentsY() const
0716 {
0717     return d->contentsY;
0718 }
0719 
0720 int KHTMLView::visibleWidth() const
0721 {
0722     if (m_kwp->isRedirected()) {
0723         // our RenderWidget knows better
0724         if (RenderWidget *rw = m_kwp->renderWidget()) {
0725             int ret = rw->width() - rw->paddingLeft() - rw->paddingRight() - rw->borderLeft() - rw->borderRight();
0726             if (verticalScrollBar()->isVisible()) {
0727                 ret -= verticalScrollBar()->sizeHint().width();
0728                 ret = qMax(0, ret);
0729             }
0730             return ret;
0731         }
0732     }
0733     return viewport()->width();
0734 }
0735 
0736 int KHTMLView::visibleHeight() const
0737 {
0738     if (m_kwp->isRedirected()) {
0739         // our RenderWidget knows better
0740         if (RenderWidget *rw = m_kwp->renderWidget()) {
0741             int ret = rw->height() - rw->paddingBottom() - rw->paddingTop() - rw->borderTop() - rw->borderBottom();
0742             if (horizontalScrollBar()->isVisible()) {
0743                 ret -= horizontalScrollBar()->sizeHint().height();
0744                 ret = qMax(0, ret);
0745             }
0746             return ret;
0747         }
0748     }
0749     return viewport()->height();
0750 }
0751 
0752 void KHTMLView::setContentsPos(int x, int y)
0753 {
0754     horizontalScrollBar()->setValue(QApplication::isRightToLeft() ?
0755                                     horizontalScrollBar()->maximum() - x : x);
0756     verticalScrollBar()->setValue(y);
0757 }
0758 
0759 void KHTMLView::scrollBy(int x, int y)
0760 {
0761     if (d->scrollTimerId) {
0762         d->newScrollTimer(this, 0);
0763     }
0764     horizontalScrollBar()->setValue(horizontalScrollBar()->value() + x);
0765     verticalScrollBar()->setValue(verticalScrollBar()->value() + y);
0766 }
0767 
0768 QPoint KHTMLView::contentsToViewport(const QPoint &p) const
0769 {
0770     return QPoint(p.x() - contentsX(), p.y() - contentsY());
0771 }
0772 
0773 void KHTMLView::contentsToViewport(int x, int y, int &cx, int &cy) const
0774 {
0775     QPoint p(x, y);
0776     p = contentsToViewport(p);
0777     cx = p.x();
0778     cy = p.y();
0779 }
0780 
0781 QPoint KHTMLView::viewportToContents(const QPoint &p) const
0782 {
0783     return QPoint(p.x() + contentsX(), p.y() + contentsY());
0784 }
0785 
0786 void KHTMLView::viewportToContents(int x, int y, int &cx, int &cy) const
0787 {
0788     QPoint p(x, y);
0789     p = viewportToContents(p);
0790     cx = p.x();
0791     cy = p.y();
0792 }
0793 
0794 void KHTMLView::updateContents(int x, int y, int w, int h)
0795 {
0796     applyTransforms(x, y, w, h);
0797     if (m_kwp->isRedirected()) {
0798         QPoint off = m_kwp->absolutePos();
0799         KHTMLView *pview = m_part->parentPart()->view();
0800         pview->updateContents(x + off.x(), y + off.y(), w, h);
0801     } else {
0802         widget()->update(x, y, w, h);
0803     }
0804 }
0805 
0806 void KHTMLView::updateContents(const QRect &r)
0807 {
0808     updateContents(r.x(), r.y(), r.width(), r.height());
0809 }
0810 
0811 void KHTMLView::repaintContents(int x, int y, int w, int h)
0812 {
0813     applyTransforms(x, y, w, h);
0814     if (m_kwp->isRedirected()) {
0815         QPoint off = m_kwp->absolutePos();
0816         KHTMLView *pview = m_part->parentPart()->view();
0817         pview->repaintContents(x + off.x(), y + off.y(), w, h);
0818     } else {
0819         widget()->repaint(x, y, w, h);
0820     }
0821 }
0822 
0823 void KHTMLView::repaintContents(const QRect &r)
0824 {
0825     repaintContents(r.x(), r.y(), r.width(), r.height());
0826 }
0827 
0828 void KHTMLView::applyTransforms(int &x, int &y, int &w, int &h) const
0829 {
0830     if (d->haveZoom()) {
0831         const int z = d->zoomLevel;
0832         x = x * z / 100;
0833         y = y * z / 100;
0834         w = w * z / 100;
0835         h = h * z / 100;
0836     }
0837     x -= contentsX();
0838     y -= contentsY();
0839 }
0840 
0841 void KHTMLView::revertTransforms(int &x, int &y, int &w, int &h) const
0842 {
0843     x += contentsX();
0844     y += contentsY();
0845     if (d->haveZoom()) {
0846         const int z = d->zoomLevel;
0847         x = x * 100 / z;
0848         y = y * 100 / z;
0849         w = w * 100 / z;
0850         h = h * 100 / z;
0851     }
0852 }
0853 
0854 void KHTMLView::revertTransforms(int &x, int &y) const
0855 {
0856     int dummy = 0;
0857     revertTransforms(x, y, dummy, dummy);
0858 }
0859 
0860 void KHTMLView::resizeEvent(QResizeEvent * /*e*/)
0861 {
0862     updateScrollBars();
0863 
0864     // If we didn't load anything, make white area as big as the view
0865     if (!m_part->xmlDocImpl()) {
0866         resizeContentsToViewport();
0867     }
0868 
0869     // Viewport-dependent media queries may cause us to need completely different style information.
0870     if (m_part->xmlDocImpl() && m_part->xmlDocImpl()->styleSelector()->affectedByViewportChange()) {
0871         m_part->xmlDocImpl()->updateStyleSelector();
0872     }
0873 
0874     if (d->layoutSchedulingEnabled) {
0875         layout();
0876     }
0877 
0878     QApplication::sendPostedEvents(viewport(), QEvent::Paint);
0879 
0880     if (m_part && m_part->xmlDocImpl()) {
0881         if (m_part->parentPart()) {
0882             // sub-frame : queue the resize event until our toplevel is done layouting
0883             khtml::ChildFrame *cf = m_part->parentPart()->frame(m_part);
0884             if (cf && !cf->m_partContainerElement.isNull()) {
0885                 cf->m_partContainerElement.data()->postResizeEvent();
0886             }
0887         } else {
0888             // toplevel : dispatch sub-frames'resize events before our own
0889             HTMLPartContainerElementImpl::sendPostedResizeEvents();
0890             m_part->xmlDocImpl()->dispatchWindowEvent(EventImpl::RESIZE_EVENT, false, false);
0891         }
0892     }
0893 }
0894 
0895 void KHTMLView::paintEvent(QPaintEvent *e)
0896 {
0897     QRect r = e->rect();
0898     QRect v(contentsX(), contentsY(), visibleWidth(), visibleHeight());
0899     QPoint off(contentsX(), contentsY());
0900     r.translate(off);
0901     r = r.intersected(v);
0902     if (!r.isValid() || r.isEmpty()) {
0903         return;
0904     }
0905 
0906     QPainter p(widget());
0907     p.translate(-off);
0908 
0909     if (d->haveZoom()) {
0910         p.scale(d->zoomLevel / 100., d->zoomLevel / 100.);
0911 
0912         r.setX(r.x() * 100 / d->zoomLevel);
0913         r.setY(r.y() * 100 / d->zoomLevel);
0914         r.setWidth(r.width() * 100 / d->zoomLevel);
0915         r.setHeight(r.height() * 100 / d->zoomLevel);
0916         r.adjust(-1, -1, 1, 1);
0917     }
0918     p.setClipRect(r);
0919 
0920     int ex = r.x();
0921     int ey = r.y();
0922     int ew = r.width();
0923     int eh = r.height();
0924 
0925     if (!m_part || !m_part->xmlDocImpl() || !m_part->xmlDocImpl()->renderer()) {
0926         p.fillRect(ex, ey, ew, eh, palette().brush(QPalette::Active, QPalette::Base));
0927         return;
0928     } else if (d->complete && static_cast<RenderCanvas *>(m_part->xmlDocImpl()->renderer())->needsLayout()) {
0929         // an external update request happens while we have a layout scheduled
0930         unscheduleRelayout();
0931         layout();
0932     } else if (m_part->xmlDocImpl()->tokenizer()) {
0933         m_part->xmlDocImpl()->tokenizer()->setNormalYieldDelay();
0934     }
0935 
0936     if (d->painting) {
0937         // qCDebug(KHTML_LOG) << "WARNING: paintEvent reentered! ";
0938         return;
0939     }
0940     d->painting = true;
0941 
0942     m_part->xmlDocImpl()->renderer()->layer()->paint(&p, r);
0943 
0944     if (d->hasFrameset) {
0945         NodeImpl *body = static_cast<HTMLDocumentImpl *>(m_part->xmlDocImpl())->body();
0946         if (body && body->renderer() && body->id() == ID_FRAMESET) {
0947             static_cast<RenderFrameSet *>(body->renderer())->paintFrameSetRules(&p, r);
0948         } else {
0949             d->hasFrameset = false;
0950         }
0951     }
0952 
0953     khtml::DrawContentsEvent event(&p, ex, ey, ew, eh);
0954     QApplication::sendEvent(m_part, &event);
0955 
0956     if (d->contentsMoving && !d->smoothScrolling && widget()->underMouse()) {
0957         QMouseEvent *tempEvent = new QMouseEvent(QEvent::MouseMove, widget()->mapFromGlobal(QCursor::pos()),
0958                 Qt::NoButton, Qt::NoButton, Qt::NoModifier);
0959         QApplication::postEvent(widget(), tempEvent);
0960     }
0961 #ifdef SPEED_DEBUG
0962     if (d->firstRepaintPending && !m_part->parentPart()) {
0963         qCDebug(KHTML_LOG) << "FIRST PAINT:" << m_part->d->m_parsetime.elapsed();
0964     }
0965     d->firstRepaintPending = false;
0966 #endif
0967     d->painting = false;
0968 }
0969 
0970 void KHTMLView::setMarginWidth(int w)
0971 {
0972     // make it update the rendering area when set
0973     _marginWidth = w;
0974 }
0975 
0976 void KHTMLView::setMarginHeight(int h)
0977 {
0978     // make it update the rendering area when set
0979     _marginHeight = h;
0980 }
0981 
0982 void KHTMLView::layout()
0983 {
0984     if (m_part && m_part->xmlDocImpl()) {
0985         DOM::DocumentImpl *document = m_part->xmlDocImpl();
0986 
0987         khtml::RenderCanvas *canvas = static_cast<khtml::RenderCanvas *>(document->renderer());
0988         if (!canvas) {
0989             return;
0990         }
0991 
0992         d->layoutSchedulingEnabled = false;
0993         d->dirtyLayout = true;
0994 
0995         // the reference object for the overflow property on canvas
0996         RenderObject *ref = nullptr;
0997         RenderObject *root = document->documentElement() ? document->documentElement()->renderer() : nullptr;
0998 
0999         if (document->isHTMLDocument()) {
1000             NodeImpl *body = static_cast<HTMLDocumentImpl *>(document)->body();
1001             if (body && body->renderer() && body->id() == ID_FRAMESET) {
1002                 QScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1003                 QScrollArea::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1004                 body->renderer()->setNeedsLayout(true);
1005                 d->hasFrameset = true;
1006             } else if (root) { // only apply body's overflow to canvas if root has a visible overflow
1007                 ref = (!body || root->style()->hidesOverflow()) ? root : body->renderer();
1008             }
1009         } else {
1010             ref = root;
1011         }
1012         if (ref) {
1013             if (ref->style()->overflowX() == OHIDDEN) {
1014                 if (d->hpolicy == Qt::ScrollBarAsNeeded) {
1015                     QScrollArea::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1016                 }
1017             } else if (ref->style()->overflowX() == OSCROLL) {
1018                 if (d->hpolicy == Qt::ScrollBarAsNeeded) {
1019                     QScrollArea::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
1020                 }
1021             } else if (horizontalScrollBarPolicy() != d->hpolicy) {
1022                 QScrollArea::setHorizontalScrollBarPolicy(d->hpolicy);
1023             }
1024             if (ref->style()->overflowY() == OHIDDEN) {
1025                 if (d->vpolicy == Qt::ScrollBarAsNeeded) {
1026                     QScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1027                 }
1028             } else if (ref->style()->overflowY() == OSCROLL) {
1029                 if (d->vpolicy == Qt::ScrollBarAsNeeded) {
1030                     QScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
1031                 }
1032             } else if (verticalScrollBarPolicy() != d->vpolicy) {
1033                 QScrollArea::setVerticalScrollBarPolicy(d->vpolicy);
1034             }
1035         }
1036         d->needsFullRepaint = d->firstLayoutPending;
1037         if (_height !=  visibleHeight() || _width != visibleWidth()) {
1038             ;
1039             d->needsFullRepaint = true;
1040             _height = visibleHeight();
1041             _width = visibleWidth();
1042         }
1043 
1044         canvas->layout();
1045 
1046         emit finishedLayout();
1047         if (d->firstLayoutPending) {
1048             // make sure firstLayoutPending is set to false now in case this layout
1049             // wasn't scheduled
1050             d->firstLayoutPending = false;
1051             verticalScrollBar()->setEnabled(true);
1052             horizontalScrollBar()->setEnabled(true);
1053         }
1054         d->layoutCounter++;
1055 
1056         if (d->accessKeysEnabled && d->accessKeysActivated) {
1057             emit hideAccessKeys();
1058             displayAccessKeys();
1059         }
1060     } else {
1061         _width = visibleWidth();
1062     }
1063 
1064     if (d->layoutTimerId) {
1065         killTimer(d->layoutTimerId);
1066     }
1067     d->layoutTimerId = 0;
1068     d->layoutSchedulingEnabled = true;
1069 }
1070 
1071 void KHTMLView::closeChildDialogs()
1072 {
1073     QList<QDialog *> dlgs = findChildren<QDialog *>();
1074     foreach (QDialog *dlg, dlgs) {
1075         if (dlg->testAttribute(Qt::WA_ShowModal)) {
1076             // qCDebug(KHTML_LOG) << "closeChildDialogs: closing dialog " << dlg;
1077             // close() ends up calling QButton::animateClick, which isn't immediate
1078             // we need something the exits the event loop immediately (#49068)
1079             dlg->reject();
1080         }
1081     }
1082     d->m_dialogsAllowed = false;
1083 }
1084 
1085 bool KHTMLView::dialogsAllowed()
1086 {
1087     bool allowed = d->m_dialogsAllowed;
1088     KHTMLPart *p = m_part->parentPart();
1089     if (p && p->view()) {
1090         allowed &= p->view()->dialogsAllowed();
1091     }
1092     return allowed;
1093 }
1094 
1095 void KHTMLView::closeEvent(QCloseEvent *ev)
1096 {
1097     closeChildDialogs();
1098     QScrollArea::closeEvent(ev);
1099 }
1100 
1101 void KHTMLView::setZoomLevel(int percent)
1102 {
1103     percent = percent < 20 ? 20 : (percent > 800 ? 800 : percent);
1104     int oldpercent = d->zoomLevel;
1105     d->zoomLevel = percent;
1106     if (percent != oldpercent) {
1107         if (d->layoutSchedulingEnabled) {
1108             layout();
1109         }
1110         widget()->update();
1111     }
1112 }
1113 
1114 int KHTMLView::zoomLevel() const
1115 {
1116     return d->zoomLevel;
1117 }
1118 
1119 void KHTMLView::setSmoothScrollingMode(SmoothScrollingMode m)
1120 {
1121     d->smoothScrollMode = m;
1122     d->smoothScrollModeIsDefault = false;
1123     if (d->smoothScrolling && !m) {
1124         d->stopScrolling();
1125     }
1126 }
1127 
1128 void KHTMLView::setSmoothScrollingModeDefault(SmoothScrollingMode m)
1129 {
1130     // check for manual override
1131     if (!d->smoothScrollModeIsDefault) {
1132         return;
1133     }
1134     d->smoothScrollMode = m;
1135     if (d->smoothScrolling && !m) {
1136         d->stopScrolling();
1137     }
1138 }
1139 
1140 KHTMLView::SmoothScrollingMode KHTMLView::smoothScrollingMode() const
1141 {
1142     return d->smoothScrollMode;
1143 }
1144 
1145 //
1146 // Event Handling
1147 //
1148 /////////////////
1149 
1150 void KHTMLView::mousePressEvent(QMouseEvent *_mouse)
1151 {
1152     if (!m_part->xmlDocImpl()) {
1153         return;
1154     }
1155     if (d->possibleTripleClick && (_mouse->button() & Qt::MouseButtonMask) == Qt::LeftButton) {
1156         mouseDoubleClickEvent(_mouse);   // it handles triple clicks too
1157         return;
1158     }
1159 
1160     int xm = _mouse->x();
1161     int ym = _mouse->y();
1162     revertTransforms(xm, ym);
1163 
1164     // qCDebug(KHTML_LOG) << "mousePressEvent: viewport=("<<_mouse->x()-contentsX()<<"/"<<_mouse->y()-contentsY()<<"), contents=(" << xm << "/" << ym << ")\n";
1165 
1166     d->isDoubleClick = false;
1167 
1168     DOM::NodeImpl::MouseEvent mev(_mouse->buttons(), DOM::NodeImpl::MousePress);
1169     m_part->xmlDocImpl()->prepareMouseEvent(false, xm, ym, &mev);
1170 
1171     //qCDebug(KHTML_LOG) << "innerNode="<<mev.innerNode.nodeName().string();
1172 
1173     if ((_mouse->button() == Qt::MidButton) &&
1174             !m_part->d->m_bOpenMiddleClick && !d->m_mouseScrollTimer &&
1175             mev.url.isNull() && (mev.innerNode.elementId() != ID_INPUT)) {
1176         QPoint point = mapFromGlobal(_mouse->globalPos());
1177 
1178         d->m_mouseScroll_byX = 0;
1179         d->m_mouseScroll_byY = 0;
1180 
1181         d->m_mouseScrollTimer = new QTimer(this);
1182         connect(d->m_mouseScrollTimer, SIGNAL(timeout()), this, SLOT(slotMouseScrollTimer()));
1183 
1184         if (!d->m_mouseScrollIndicator) {
1185             QPixmap pixmap(48, 48), icon;
1186             pixmap.fill(QColor(qRgba(127, 127, 127, 127)));
1187 
1188             QPainter p(&pixmap);
1189             QStyleOption option;
1190 
1191             option.rect.setRect(16, 0, 16, 16);
1192             QApplication::style()->drawPrimitive(QStyle::PE_IndicatorArrowUp, &option, &p);
1193             option.rect.setRect(0, 16, 16, 16);
1194             QApplication::style()->drawPrimitive(QStyle::PE_IndicatorArrowLeft, &option, &p);
1195             option.rect.setRect(16, 32, 16, 16);
1196             QApplication::style()->drawPrimitive(QStyle::PE_IndicatorArrowDown, &option, &p);
1197             option.rect.setRect(32, 16, 16, 16);
1198             QApplication::style()->drawPrimitive(QStyle::PE_IndicatorArrowRight, &option, &p);
1199             p.drawEllipse(23, 23, 2, 2);
1200 
1201             d->m_mouseScrollIndicator = new QWidget(this);
1202             d->m_mouseScrollIndicator->setFixedSize(48, 48);
1203             QPalette palette;
1204             palette.setBrush(d->m_mouseScrollIndicator->backgroundRole(), QBrush(pixmap));
1205             d->m_mouseScrollIndicator->setPalette(palette);
1206         }
1207         d->m_mouseScrollIndicator->move(point.x() - 24, point.y() - 24);
1208 
1209         bool hasHorBar = visibleWidth() < contentsWidth();
1210         bool hasVerBar = visibleHeight() < contentsHeight();
1211 
1212         KConfigGroup cg(KSharedConfig::openConfig(), "HTML Settings");
1213         if (cg.readEntry("ShowMouseScrollIndicator", true)) {
1214             d->m_mouseScrollIndicator->show();
1215             d->m_mouseScrollIndicator->unsetCursor();
1216 
1217             QBitmap mask = d->m_mouseScrollIndicator->palette().brush(d->m_mouseScrollIndicator->backgroundRole()).texture().createHeuristicMask(true);
1218 
1219             if (hasHorBar && !hasVerBar) {
1220                 QBitmap bm(16, 16);
1221                 bm.clear();
1222                 QPainter painter(&mask);
1223                 painter.drawPixmap(QRectF(16, 0, bm.width(), bm.height()), bm, bm.rect());
1224                 painter.drawPixmap(QRectF(16, 32, bm.width(), bm.height()), bm, bm.rect());
1225                 d->m_mouseScrollIndicator->setCursor(Qt::SizeHorCursor);
1226             } else if (!hasHorBar && hasVerBar) {
1227                 QBitmap bm(16, 16);
1228                 bm.clear();
1229                 QPainter painter(&mask);
1230                 painter.drawPixmap(QRectF(0, 16, bm.width(), bm.height()), bm, bm.rect());
1231                 painter.drawPixmap(QRectF(32, 16, bm.width(), bm.height()), bm, bm.rect());
1232                 d->m_mouseScrollIndicator->setCursor(Qt::SizeVerCursor);
1233             } else {
1234                 d->m_mouseScrollIndicator->setCursor(Qt::SizeAllCursor);
1235             }
1236 
1237             d->m_mouseScrollIndicator->setMask(mask);
1238         } else {
1239             if (hasHorBar && !hasVerBar) {
1240                 viewport()->setCursor(Qt::SizeHorCursor);
1241             } else if (!hasHorBar && hasVerBar) {
1242                 viewport()->setCursor(Qt::SizeVerCursor);
1243             } else {
1244                 viewport()->setCursor(Qt::SizeAllCursor);
1245             }
1246         }
1247 
1248         return;
1249     } else if (d->m_mouseScrollTimer) {
1250         delete d->m_mouseScrollTimer;
1251         d->m_mouseScrollTimer = nullptr;
1252 
1253         if (d->m_mouseScrollIndicator) {
1254             d->m_mouseScrollIndicator->hide();
1255         }
1256     }
1257 
1258     if (d->clickCount > 0 &&
1259             QPoint(d->clickX - xm, d->clickY - ym).manhattanLength() <= QApplication::startDragDistance()) {
1260         d->clickCount++;
1261     } else {
1262         d->clickCount = 1;
1263         d->clickX = xm;
1264         d->clickY = ym;
1265     }
1266 
1267     bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEDOWN_EVENT, mev.innerNode.handle(), mev.innerNonSharedNode.handle(), true,
1268                                            d->clickCount, _mouse, true, DOM::NodeImpl::MousePress);
1269 
1270     if (!swallowEvent) {
1271         emit m_part->nodeActivated(mev.innerNode);
1272 
1273         khtml::MousePressEvent event(_mouse, xm, ym, mev.url, mev.target, mev.innerNode);
1274         QApplication::sendEvent(m_part, &event);
1275         // we might be deleted after this
1276     }
1277 }
1278 
1279 void KHTMLView::mouseDoubleClickEvent(QMouseEvent *_mouse)
1280 {
1281     if (!m_part->xmlDocImpl()) {
1282         return;
1283     }
1284 
1285     int xm = _mouse->x();
1286     int ym = _mouse->y();
1287     revertTransforms(xm, ym);
1288 
1289     // qCDebug(KHTML_LOG) << "mouseDblClickEvent: x=" << xm << ", y=" << ym;
1290 
1291     d->isDoubleClick = true;
1292 
1293     DOM::NodeImpl::MouseEvent mev(_mouse->buttons(), DOM::NodeImpl::MouseDblClick);
1294     m_part->xmlDocImpl()->prepareMouseEvent(false, xm, ym, &mev);
1295 
1296     // We do the same thing as mousePressEvent() here, since the DOM does not treat
1297     // single and double-click events as separate (only the detail, i.e. number of clicks differs)
1298     if (d->clickCount > 0 &&
1299             QPoint(d->clickX - xm, d->clickY - ym).manhattanLength() <= QApplication::startDragDistance()) {
1300         d->clickCount++;
1301     } else { // shouldn't happen, if Qt has the same criterias for double clicks.
1302         d->clickCount = 1;
1303         d->clickX = xm;
1304         d->clickY = ym;
1305     }
1306     bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEDOWN_EVENT, mev.innerNode.handle(), mev.innerNonSharedNode.handle(), true,
1307                                            d->clickCount, _mouse, true, DOM::NodeImpl::MouseDblClick);
1308 
1309     if (!swallowEvent) {
1310         khtml::MouseDoubleClickEvent event(_mouse, xm, ym, mev.url, mev.target, mev.innerNode, d->clickCount);
1311         QApplication::sendEvent(m_part, &event);
1312     }
1313 
1314     d->possibleTripleClick = true;
1315     QTimer::singleShot(QApplication::doubleClickInterval(), this, SLOT(tripleClickTimeout()));
1316 }
1317 
1318 void KHTMLView::tripleClickTimeout()
1319 {
1320     d->possibleTripleClick = false;
1321     d->clickCount = 0;
1322 }
1323 
1324 static bool targetOpensNewWindow(KHTMLPart *part, QString target)
1325 {
1326     if (!target.isEmpty() && (target.toLower() != "_top") &&
1327             (target.toLower() != "_self") && (target.toLower() != "_parent")) {
1328         if (target.toLower() == "_blank") {
1329             return true;
1330         } else {
1331             while (part->parentPart()) {
1332                 part = part->parentPart();
1333             }
1334             if (!part->frameExists(target)) {
1335                 return true;
1336             }
1337         }
1338     }
1339     return false;
1340 }
1341 
1342 void KHTMLView::mouseMoveEvent(QMouseEvent *_mouse)
1343 {
1344     if (d->m_mouseScrollTimer) {
1345         QPoint point = mapFromGlobal(_mouse->globalPos());
1346 
1347         int deltaX = point.x() - d->m_mouseScrollIndicator->x() - 24;
1348         int deltaY = point.y() - d->m_mouseScrollIndicator->y() - 24;
1349 
1350         (deltaX > 0) ? d->m_mouseScroll_byX = 1 : d->m_mouseScroll_byX = -1;
1351         (deltaY > 0) ? d->m_mouseScroll_byY = 1 : d->m_mouseScroll_byY = -1;
1352 
1353         double adX = qAbs(deltaX) / 30.0;
1354         double adY = qAbs(deltaY) / 30.0;
1355 
1356         d->m_mouseScroll_byX = qMax(qMin(d->m_mouseScroll_byX * int(adX * adX), SHRT_MAX), SHRT_MIN);
1357         d->m_mouseScroll_byY = qMax(qMin(d->m_mouseScroll_byY * int(adY * adY), SHRT_MAX), SHRT_MIN);
1358 
1359         if (d->m_mouseScroll_byX == 0 && d->m_mouseScroll_byY == 0) {
1360             d->m_mouseScrollTimer->stop();
1361         } else if (!d->m_mouseScrollTimer->isActive()) {
1362             d->m_mouseScrollTimer->start(20);
1363         }
1364     }
1365 
1366     if (!m_part->xmlDocImpl()) {
1367         return;
1368     }
1369 
1370     int xm = _mouse->x();
1371     int ym = _mouse->y();
1372     revertTransforms(xm, ym);
1373 
1374     DOM::NodeImpl::MouseEvent mev(_mouse->buttons(), DOM::NodeImpl::MouseMove);
1375     // Do not modify :hover/:active state while mouse is pressed.
1376     m_part->xmlDocImpl()->prepareMouseEvent(_mouse->buttons() /*readonly ?*/, xm, ym, &mev);
1377 
1378     // qCDebug(KHTML_LOG) << "mouse move: " << _mouse->pos()
1379     //        << " button " << _mouse->button()
1380     //        << " state " << _mouse->state();
1381 
1382     DOM::NodeImpl *target = mev.innerNode.handle();
1383     DOM::NodeImpl *fn = m_part->xmlDocImpl()->focusNode();
1384 
1385     // a widget may be the real target of this event (e.g. if a scrollbar's slider is being moved)
1386     if (d->m_mouseEventsTarget && fn && fn->renderer() && fn->renderer()->isWidget()) {
1387         target = fn;
1388     }
1389 
1390     bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEMOVE_EVENT, target, mev.innerNonSharedNode.handle(), false,
1391                                            0, _mouse, true, DOM::NodeImpl::MouseMove);
1392 
1393     if (d->clickCount > 0 &&
1394             QPoint(d->clickX - xm, d->clickY - ym).manhattanLength() > QApplication::startDragDistance()) {
1395         d->clickCount = 0;  // moving the mouse outside the threshold invalidates the click
1396     }
1397 
1398     khtml::RenderObject *r = target ? target->renderer() : nullptr;
1399     bool setCursor = true;
1400     bool forceDefault = false;
1401     if (r && r->isWidget()) {
1402         RenderWidget *rw = static_cast<RenderWidget *>(r);
1403         KHTMLWidget *kw = qobject_cast<KHTMLView *>(rw->widget()) ? dynamic_cast<KHTMLWidget *>(rw->widget()) : nullptr;
1404         if (kw && kw->m_kwp->isRedirected()) {
1405             setCursor = false;
1406         } else if (QLineEdit *le = qobject_cast<QLineEdit *>(rw->widget())) {
1407             QList<QWidget *> wl = le->findChildren<QWidget *>("KLineEditButton");
1408             // force arrow cursor above lineedit clear button
1409             foreach (QWidget *w, wl) {
1410                 if (w->underMouse()) {
1411                     forceDefault = true;
1412                     break;
1413                 }
1414             }
1415         } else if (QTextEdit *te = qobject_cast<QTextEdit *>(rw->widget())) {
1416             if (te->verticalScrollBar()->underMouse() || te->horizontalScrollBar()->underMouse()) {
1417                 forceDefault = true;
1418             }
1419         }
1420     }
1421     khtml::RenderStyle *style = (r && r->style()) ? r->style() : nullptr;
1422     QCursor c;
1423     LinkCursor linkCursor = LINK_NORMAL;
1424     switch (!forceDefault ? (style ? style->cursor() : CURSOR_AUTO) : CURSOR_DEFAULT) {
1425     case CURSOR_AUTO:
1426         if (r && r->isText() && ((m_part->d->m_bMousePressed && m_part->d->editor_context.m_beganSelectingText) ||
1427                                  !r->isPointInsideSelection(xm, ym, m_part->caret()))) {
1428             c = QCursor(Qt::IBeamCursor);
1429         }
1430         if (mev.url.length() && m_part->settings()->changeCursor()) {
1431             c = m_part->urlCursor();
1432             if (mev.url.string().startsWith("mailto:") && mev.url.string().indexOf('@') > 0) {
1433                 linkCursor = LINK_MAILTO;
1434             } else if (targetOpensNewWindow(m_part, mev.target.string())) {
1435                 linkCursor = LINK_NEWWINDOW;
1436             }
1437         }
1438 
1439         if (r && r->isFrameSet() && !static_cast<RenderFrameSet *>(r)->noResize()) {
1440             c = QCursor(static_cast<RenderFrameSet *>(r)->cursorShape());
1441         }
1442 
1443         break;
1444     case CURSOR_CROSS:
1445         c = QCursor(Qt::CrossCursor);
1446         break;
1447     case CURSOR_POINTER:
1448         c = m_part->urlCursor();
1449         if (mev.url.string().startsWith("mailto:") && mev.url.string().indexOf('@') > 0) {
1450             linkCursor = LINK_MAILTO;
1451         } else if (targetOpensNewWindow(m_part, mev.target.string())) {
1452             linkCursor = LINK_NEWWINDOW;
1453         }
1454         break;
1455     case CURSOR_PROGRESS:
1456         c = QCursor(Qt::BusyCursor); // working_cursor
1457         break;
1458     case CURSOR_MOVE:
1459     case CURSOR_ALL_SCROLL:
1460         c = QCursor(Qt::SizeAllCursor);
1461         break;
1462     case CURSOR_E_RESIZE:
1463     case CURSOR_W_RESIZE:
1464     case CURSOR_EW_RESIZE:
1465         c = QCursor(Qt::SizeHorCursor);
1466         break;
1467     case CURSOR_N_RESIZE:
1468     case CURSOR_S_RESIZE:
1469     case CURSOR_NS_RESIZE:
1470         c = QCursor(Qt::SizeVerCursor);
1471         break;
1472     case CURSOR_NE_RESIZE:
1473     case CURSOR_SW_RESIZE:
1474     case CURSOR_NESW_RESIZE:
1475         c = QCursor(Qt::SizeBDiagCursor);
1476         break;
1477     case CURSOR_NW_RESIZE:
1478     case CURSOR_SE_RESIZE:
1479     case CURSOR_NWSE_RESIZE:
1480         c = QCursor(Qt::SizeFDiagCursor);
1481         break;
1482     case CURSOR_TEXT:
1483         c = QCursor(Qt::IBeamCursor);
1484         break;
1485     case CURSOR_WAIT:
1486         c = QCursor(Qt::WaitCursor);
1487         break;
1488     case CURSOR_HELP:
1489         c = QCursor(Qt::WhatsThisCursor);
1490         break;
1491     case CURSOR_DEFAULT:
1492         break;
1493     case CURSOR_NONE:
1494     case CURSOR_NOT_ALLOWED:
1495         c = QCursor(Qt::ForbiddenCursor);
1496         break;
1497     case CURSOR_ROW_RESIZE:
1498         c = QCursor(Qt::SplitVCursor);
1499         break;
1500     case CURSOR_COL_RESIZE:
1501         c = QCursor(Qt::SplitHCursor);
1502         break;
1503     case CURSOR_VERTICAL_TEXT:
1504     case CURSOR_CONTEXT_MENU:
1505     case CURSOR_NO_DROP:
1506     case CURSOR_CELL:
1507     case CURSOR_COPY:
1508     case CURSOR_ALIAS:
1509         c = QCursor(Qt::ArrowCursor);
1510         break;
1511     }
1512 
1513     if (!setCursor && style && style->cursor() != CURSOR_AUTO) {
1514         setCursor = true;
1515     }
1516 
1517     QWidget *vp = viewport();
1518     for (KHTMLPart *p = m_part; p; p = p->parentPart())
1519         if (!p->parentPart()) {
1520             vp = p->view()->viewport();
1521         }
1522     if (setCursor && (vp->cursor().shape() != c.shape() || c.shape() == Qt::BitmapCursor)) {
1523         if (c.shape() == Qt::ArrowCursor) {
1524             for (KHTMLPart *p = m_part; p; p = p->parentPart()) {
1525                 p->view()->viewport()->unsetCursor();
1526             }
1527         } else {
1528             vp->setCursor(c);
1529         }
1530     }
1531 
1532     if (linkCursor != LINK_NORMAL && isVisible() && hasFocus()) {
1533 #if HAVE_X11
1534     // ensure we don't trigger this code paths if we run in a Wayland session
1535     if (KWindowSystem::isPlatformX11()) {
1536         if (!d->cursorIconWidget) {
1537 #if HAVE_X11
1538             d->cursorIconWidget = new QLabel(nullptr, Qt::X11BypassWindowManagerHint);
1539             XSetWindowAttributes attr;
1540             attr.save_under = True;
1541             XChangeWindowAttributes(QX11Info::display(), d->cursorIconWidget->winId(), CWSaveUnder, &attr);
1542 #else
1543             d->cursorIconWidget = new QLabel(NULL, NULL);
1544             //TODO
1545 #endif
1546         }
1547 
1548         // Update the pixmap if need be.
1549         if (linkCursor != d->cursorIconType) {
1550             d->cursorIconType = linkCursor;
1551             QString cursorIcon;
1552             switch (linkCursor) {
1553             case LINK_MAILTO:     cursorIcon = "mail-message-new"; break;
1554             case LINK_NEWWINDOW:  cursorIcon = "window-new";       break;
1555             default:              cursorIcon = "dialog-error";     break;
1556             }
1557 
1558             QPixmap icon_pixmap = KHTMLGlobal::iconLoader()->loadIcon(cursorIcon, KIconLoader::Small, 0, KIconLoader::DefaultState, QStringList(), nullptr, true);
1559 
1560             d->cursorIconWidget->resize(icon_pixmap.width(), icon_pixmap.height());
1561             d->cursorIconWidget->setMask(icon_pixmap.createMaskFromColor(Qt::transparent));
1562             d->cursorIconWidget->setPixmap(icon_pixmap);
1563             d->cursorIconWidget->update();
1564         }
1565 
1566         QPoint c_pos = QCursor::pos();
1567         d->cursorIconWidget->move(c_pos.x() + 15, c_pos.y() + 15);
1568 #if HAVE_X11
1569         XRaiseWindow(QX11Info::display(), d->cursorIconWidget->winId());
1570         QApplication::flush();
1571 #elif defined(Q_OS_WIN)
1572         SetWindowPos(d->cursorIconWidget->winId(), HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
1573 #else
1574         //TODO?
1575 #endif
1576         d->cursorIconWidget->show();
1577       }
1578 #endif
1579     } else if (d->cursorIconWidget) {
1580         d->cursorIconWidget->hide();
1581     }
1582 
1583     if (r && r->isWidget()) {
1584         _mouse->ignore();
1585     }
1586 
1587     if (!swallowEvent) {
1588         khtml::MouseMoveEvent event(_mouse, xm, ym, mev.url, mev.target, mev.innerNode);
1589         QApplication::sendEvent(m_part, &event);
1590     }
1591 }
1592 
1593 void KHTMLView::mouseReleaseEvent(QMouseEvent *_mouse)
1594 {
1595     bool swallowEvent = false;
1596 
1597     int xm = _mouse->x();
1598     int ym = _mouse->y();
1599     revertTransforms(xm, ym);
1600 
1601     DOM::NodeImpl::MouseEvent mev(_mouse->buttons(), DOM::NodeImpl::MouseRelease);
1602 
1603     if (m_part->xmlDocImpl()) {
1604         m_part->xmlDocImpl()->prepareMouseEvent(false, xm, ym, &mev);
1605 
1606         DOM::NodeImpl *target = mev.innerNode.handle();
1607         DOM::NodeImpl *fn = m_part->xmlDocImpl()->focusNode();
1608 
1609         // a widget may be the real target of this event (e.g. if a scrollbar's slider is being moved)
1610         if (d->m_mouseEventsTarget && fn && fn->renderer() && fn->renderer()->isWidget()) {
1611             target = fn;
1612         }
1613 
1614         swallowEvent = dispatchMouseEvent(EventImpl::MOUSEUP_EVENT, target, mev.innerNonSharedNode.handle(), true,
1615                                           d->clickCount, _mouse, false, DOM::NodeImpl::MouseRelease);
1616 
1617         // clear our sticky event target on any mouseRelease event
1618         if (d->m_mouseEventsTarget) {
1619             d->m_mouseEventsTarget = nullptr;
1620         }
1621 
1622         if (d->clickCount > 0 &&
1623                 QPoint(d->clickX - xm, d->clickY - ym).manhattanLength() <= QApplication::startDragDistance()) {
1624             QMouseEvent me(d->isDoubleClick ? QEvent::MouseButtonDblClick : QEvent::MouseButtonRelease,
1625                            _mouse->pos(), _mouse->button(), _mouse->buttons(), _mouse->modifiers());
1626             dispatchMouseEvent(EventImpl::CLICK_EVENT, mev.innerNode.handle(), mev.innerNonSharedNode.handle(), true,
1627                                d->clickCount, &me, true, DOM::NodeImpl::MouseRelease);
1628         }
1629 
1630         khtml::RenderObject *r = target ? target->renderer() : nullptr;
1631         if (r && r->isWidget()) {
1632             _mouse->ignore();
1633         }
1634     }
1635 
1636     if (!swallowEvent) {
1637         khtml::MouseReleaseEvent event(_mouse, xm, ym, mev.url, mev.target, mev.innerNode);
1638         QApplication::sendEvent(m_part, &event);
1639     }
1640 }
1641 
1642 // returns true if event should be swallowed
1643 bool KHTMLView::dispatchKeyEvent(QKeyEvent *_ke)
1644 {
1645     if (!m_part->xmlDocImpl()) {
1646         return false;
1647     }
1648     // Pressing and releasing a key should generate keydown, keypress and keyup events
1649     // Holding it down should generated keydown, keypress (repeatedly) and keyup events
1650     // The problem here is that Qt generates two autorepeat events (keyrelease+keypress)
1651     // for autorepeating, while DOM wants only one autorepeat event (keypress), so one
1652     // of the Qt events shouldn't be passed to DOM, but it should be still filtered
1653     // out if DOM would filter the autorepeat event. Additional problem is that Qt keyrelease
1654     // events don't have text() set (Qt bug?), so DOM often would ignore the keypress event
1655     // if it was created using Qt keyrelease, but Qt autorepeat keyrelease comes
1656     // before Qt autorepeat keypress (i.e. problem whether to filter it out or not).
1657     // The solution is to filter out and postpone the Qt autorepeat keyrelease until
1658     // the following Qt keypress event comes. If DOM accepts the DOM keypress event,
1659     // the postponed event will be simply discarded. If not, it will be passed to keyPressEvent()
1660     // again, and here it will be ignored.
1661     //
1662     //  Qt:      Press      | Release(autorepeat) Press(autorepeat) etc. |   Release
1663     //  DOM:   Down + Press |      (nothing)           Press             |     Up
1664 
1665     // It's also possible to get only Releases. E.g. the release of alt-tab,
1666     // or when the keypresses get captured by an accel.
1667 
1668     if (_ke == d->postponed_autorepeat) { // replayed event
1669         return false;
1670     }
1671 
1672     if (_ke->type() == QEvent::KeyPress) {
1673         if (!_ke->isAutoRepeat()) {
1674             bool ret = dispatchKeyEventHelper(_ke, false);   // keydown
1675             // don't send keypress even if keydown was blocked, like IE (and unlike Mozilla)
1676             if (!ret && dispatchKeyEventHelper(_ke, true)) { // keypress
1677                 ret = true;
1678             }
1679             return ret;
1680         } else { // autorepeat
1681             bool ret = dispatchKeyEventHelper(_ke, true);   // keypress
1682             if (!ret && d->postponed_autorepeat) {
1683                 keyPressEvent(d->postponed_autorepeat);
1684             }
1685             delete d->postponed_autorepeat;
1686             d->postponed_autorepeat = nullptr;
1687             return ret;
1688         }
1689     } else { // QEvent::KeyRelease
1690         // Discard postponed "autorepeat key-release" events that didn't see
1691         // a keypress after them (e.g. due to QAccel)
1692         delete d->postponed_autorepeat;
1693         d->postponed_autorepeat = nullptr;
1694 
1695         if (!_ke->isAutoRepeat()) {
1696             return dispatchKeyEventHelper(_ke, false);   // keyup
1697         } else {
1698             d->postponed_autorepeat = new QKeyEvent(_ke->type(), _ke->key(), _ke->modifiers(),
1699                                                     _ke->text(), _ke->isAutoRepeat(), _ke->count());
1700             if (_ke->isAccepted()) {
1701                 d->postponed_autorepeat->accept();
1702             } else {
1703                 d->postponed_autorepeat->ignore();
1704             }
1705             return true;
1706         }
1707     }
1708 }
1709 
1710 // returns true if event should be swallowed
1711 bool KHTMLView::dispatchKeyEventHelper(QKeyEvent *_ke, bool keypress)
1712 {
1713     DOM::NodeImpl *keyNode = m_part->xmlDocImpl()->focusNode();
1714     if (keyNode) {
1715         return keyNode->dispatchKeyEvent(_ke, keypress);
1716     } else { // no focused node, send to document
1717         return m_part->xmlDocImpl()->dispatchKeyEvent(_ke, keypress);
1718     }
1719 }
1720 
1721 void KHTMLView::keyPressEvent(QKeyEvent *_ke)
1722 {
1723     // If CTRL was hit, be prepared for access keys
1724     if (d->accessKeysEnabled && _ke->key() == Qt::Key_Control && !(_ke->modifiers() & ~Qt::ControlModifier) && !d->accessKeysActivated) {
1725         d->accessKeysPreActivate = true;
1726         _ke->accept();
1727         return;
1728     }
1729 
1730     if (_ke->key() == Qt::Key_Shift && !(_ke->modifiers() & ~Qt::ShiftModifier)) {
1731         d->scrollSuspendPreActivate = true;
1732     }
1733 
1734     // accesskey handling needs to be done before dispatching, otherwise e.g. lineedits
1735     // may eat the event
1736 
1737     if (d->accessKeysEnabled && d->accessKeysActivated) {
1738         int state = (_ke->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier));
1739         if (state == 0 || state == Qt::ShiftModifier) {
1740             if (_ke->key() != Qt::Key_Shift) {
1741                 accessKeysTimeout();
1742             }
1743             handleAccessKey(_ke);
1744             _ke->accept();
1745             return;
1746         }
1747         accessKeysTimeout();
1748         _ke->accept();
1749         return;
1750     }
1751 
1752     if (dispatchKeyEvent(_ke)) {
1753         // If either keydown or keypress was accepted by a widget, or canceled by JS, stop here.
1754         _ke->accept();
1755         return;
1756     }
1757 
1758     int offs = (viewport()->height() < 30) ? viewport()->height() : 30; // ### ??
1759     if (_ke->modifiers() & Qt::ShiftModifier)
1760         switch (_ke->key()) {
1761         case Qt::Key_Space:
1762             verticalScrollBar()->setValue(verticalScrollBar()->value() - viewport()->height() + offs);
1763             if (d->scrollSuspended) {
1764                 d->newScrollTimer(this, 0);
1765             }
1766             break;
1767 
1768         case Qt::Key_Down:
1769         case Qt::Key_J:
1770             d->adjustScroller(this, KHTMLViewPrivate::ScrollDown, KHTMLViewPrivate::ScrollUp);
1771             break;
1772 
1773         case Qt::Key_Up:
1774         case Qt::Key_K:
1775             d->adjustScroller(this, KHTMLViewPrivate::ScrollUp, KHTMLViewPrivate::ScrollDown);
1776             break;
1777 
1778         case Qt::Key_Left:
1779         case Qt::Key_H:
1780             d->adjustScroller(this, KHTMLViewPrivate::ScrollLeft, KHTMLViewPrivate::ScrollRight);
1781             break;
1782 
1783         case Qt::Key_Right:
1784         case Qt::Key_L:
1785             d->adjustScroller(this, KHTMLViewPrivate::ScrollRight, KHTMLViewPrivate::ScrollLeft);
1786             break;
1787         }
1788     else
1789         switch (_ke->key()) {
1790         case Qt::Key_Down:
1791         case Qt::Key_J:
1792             if (!d->scrollTimerId || d->scrollSuspended) {
1793                 verticalScrollBar()->setValue(verticalScrollBar()->value() + 10);
1794             }
1795             if (d->scrollTimerId) {
1796                 d->newScrollTimer(this, 0);
1797             }
1798             break;
1799 
1800         case Qt::Key_Space:
1801         case Qt::Key_PageDown:
1802             d->shouldSmoothScroll = true;
1803             verticalScrollBar()->setValue(verticalScrollBar()->value() + viewport()->height() - offs);
1804             if (d->scrollSuspended) {
1805                 d->newScrollTimer(this, 0);
1806             }
1807             break;
1808 
1809         case Qt::Key_Up:
1810         case Qt::Key_K:
1811             if (!d->scrollTimerId || d->scrollSuspended) {
1812                 verticalScrollBar()->setValue(verticalScrollBar()->value() - 10);
1813             }
1814             if (d->scrollTimerId) {
1815                 d->newScrollTimer(this, 0);
1816             }
1817             break;
1818 
1819         case Qt::Key_PageUp:
1820             d->shouldSmoothScroll = true;
1821             verticalScrollBar()->setValue(verticalScrollBar()->value() - viewport()->height() + offs);
1822             if (d->scrollSuspended) {
1823                 d->newScrollTimer(this, 0);
1824             }
1825             break;
1826         case Qt::Key_Right:
1827         case Qt::Key_L:
1828             if (!d->scrollTimerId || d->scrollSuspended) {
1829                 horizontalScrollBar()->setValue(horizontalScrollBar()->value() + 10);
1830             }
1831             if (d->scrollTimerId) {
1832                 d->newScrollTimer(this, 0);
1833             }
1834             break;
1835 
1836         case Qt::Key_Left:
1837         case Qt::Key_H:
1838             if (!d->scrollTimerId || d->scrollSuspended) {
1839                 horizontalScrollBar()->setValue(horizontalScrollBar()->value() - 10);
1840             }
1841             if (d->scrollTimerId) {
1842                 d->newScrollTimer(this, 0);
1843             }
1844             break;
1845         case Qt::Key_Enter:
1846         case Qt::Key_Return:
1847             // ### FIXME:
1848             // or even better to HTMLAnchorElementImpl::event()
1849             if (m_part->xmlDocImpl()) {
1850                 NodeImpl *n = m_part->xmlDocImpl()->focusNode();
1851                 if (n) {
1852                     n->setActive();
1853                 }
1854             }
1855             break;
1856         case Qt::Key_Home:
1857             verticalScrollBar()->setValue(0);
1858             horizontalScrollBar()->setValue(0);
1859             if (d->scrollSuspended) {
1860                 d->newScrollTimer(this, 0);
1861             }
1862             break;
1863         case Qt::Key_End:
1864             verticalScrollBar()->setValue(contentsHeight() - visibleHeight());
1865             if (d->scrollSuspended) {
1866                 d->newScrollTimer(this, 0);
1867             }
1868             break;
1869         case Qt::Key_Shift:
1870             // what are you doing here?
1871             _ke->ignore();
1872             return;
1873         default:
1874             if (d->scrollTimerId) {
1875                 d->newScrollTimer(this, 0);
1876             }
1877             _ke->ignore();
1878             return;
1879         }
1880 
1881     _ke->accept();
1882 }
1883 
1884 void KHTMLView::keyReleaseEvent(QKeyEvent *_ke)
1885 {
1886     if (d->scrollSuspendPreActivate && _ke->key() != Qt::Key_Shift) {
1887         d->scrollSuspendPreActivate = false;
1888     }
1889     if (_ke->key() == Qt::Key_Shift && d->scrollSuspendPreActivate && !(_ke->modifiers() & Qt::ShiftModifier))
1890         if (d->scrollTimerId) {
1891             d->scrollSuspended = !d->scrollSuspended;
1892             if (d->scrollSuspended) {
1893                 d->stopScrolling();
1894             }
1895         }
1896 
1897     if (d->accessKeysEnabled) {
1898         if (d->accessKeysPreActivate && _ke->key() != Qt::Key_Control) {
1899             d->accessKeysPreActivate = false;
1900         }
1901         if (d->accessKeysPreActivate && !(_ke->modifiers() & Qt::ControlModifier)) {
1902             displayAccessKeys();
1903             m_part->setStatusBarText(i18n("Access Keys activated"), KHTMLPart::BarOverrideText);
1904             d->accessKeysActivated = true;
1905             d->accessKeysPreActivate = false;
1906             _ke->accept();
1907             return;
1908         } else if (d->accessKeysActivated) {
1909             accessKeysTimeout();
1910             _ke->accept();
1911             return;
1912         }
1913     }
1914 
1915     // Send keyup event
1916     if (dispatchKeyEvent(_ke)) {
1917         _ke->accept();
1918         return;
1919     }
1920 
1921     QScrollArea::keyReleaseEvent(_ke);
1922 }
1923 
1924 bool KHTMLView::focusNextPrevChild(bool next)
1925 {
1926     // Now try to find the next child
1927     if (m_part->xmlDocImpl() && focusNextPrevNode(next)) {
1928         //if (m_part->xmlDocImpl()->focusNode())
1929         // qCDebug(KHTML_LOG) << "focusNode.name: "
1930         //      << m_part->xmlDocImpl()->focusNode()->nodeName().string();
1931         return true; // focus node found
1932     }
1933 
1934     // If we get here, pass tabbing control up to the next/previous child in our parent
1935     d->pseudoFocusNode = KHTMLViewPrivate::PFNone;
1936     if (m_part->parentPart() && m_part->parentPart()->view()) {
1937         return m_part->parentPart()->view()->focusNextPrevChild(next);
1938     }
1939 
1940     return QWidget::focusNextPrevChild(next);
1941 }
1942 
1943 void KHTMLView::doAutoScroll()
1944 {
1945     QPoint pos = QCursor::pos();
1946     QPoint off;
1947     KHTMLView *v = m_kwp->isRedirected() ? m_kwp->rootViewPos(off) : this;
1948     pos = v->viewport()->mapFromGlobal(pos);
1949     pos -= off;
1950     int xm, ym;
1951     viewportToContents(pos.x(), pos.y(), xm, ym); // ###
1952 
1953     pos = QPoint(pos.x() - viewport()->x(), pos.y() - viewport()->y());
1954     if ((pos.y() < 0) || (pos.y() > visibleHeight()) ||
1955             (pos.x() < 0) || (pos.x() > visibleWidth())) {
1956         ensureVisible(xm, ym, 0, 5);
1957 
1958 #ifndef KHTML_NO_SELECTION
1959         // extend the selection while scrolling
1960         DOM::Node innerNode;
1961         if (m_part->isExtendingSelection()) {
1962             RenderObject::NodeInfo renderInfo(true/*readonly*/, false/*active*/);
1963             m_part->xmlDocImpl()->renderer()->layer()
1964             ->nodeAtPoint(renderInfo, xm, ym);
1965             innerNode = renderInfo.innerNode();
1966         }/*end if*/
1967 
1968         if (innerNode.handle() && innerNode.handle()->renderer()
1969                 && innerNode.handle()->renderer()->shouldSelect()) {
1970             m_part->extendSelectionTo(xm, ym, innerNode);
1971         }/*end if*/
1972 #endif // KHTML_NO_SELECTION
1973     }
1974 }
1975 
1976 // KHTML defines its own stacking order for any object and thus takes
1977 // control of widget painting whenever it can. This is called "redirection".
1978 //
1979 // Redirected widgets are placed off screen. When they are declared as a child of our view (ChildPolished event),
1980 // an event filter is installed, so as to catch any paint event and translate them as update() of the view's main widget.
1981 //
1982 // Painting also happens spontaneously within widgets. In this case, the widget would update() parts of itself.
1983 // While this ordinarily results in a paintEvent being schedduled, it is not the case with off screen widgets.
1984 // Thus update() is monitored by using the mechanism that deffers any update call happening during a paint event,
1985 // transforming it into a posted UpdateLater event. Hence the need to set Qt::WA_WState_InPaintEvent on redirected widgets.
1986 //
1987 // Once the UpdateLater event has been received, Qt::WA_WState_InPaintEvent is removed and the process continues
1988 // with the update of the corresponding rect on the view. That in turn will make our painting subsystem render()
1989 // the widget at the correct stacking position.
1990 //
1991 // For non-redirected (e.g. external) widgets, z-order is honoured through masking. cf.RenderLayer::updateWidgetMasks
1992 
1993 static void handleWidget(QWidget *w, KHTMLView *view, bool recurse = true)
1994 {
1995     if (w->isWindow()) {
1996         return;
1997     }
1998 
1999     if (!qobject_cast<QFrame *>(w)) {
2000         w->setAttribute(Qt::WA_NoSystemBackground);
2001     }
2002 
2003     w->setAttribute(Qt::WA_WState_InPaintEvent);
2004 
2005     if (!(w->objectName() == "KLineEditButton")) {
2006         w->setAttribute(Qt::WA_OpaquePaintEvent);
2007     }
2008 
2009     w->installEventFilter(view);
2010 
2011     if (!recurse) {
2012         return;
2013     }
2014     if (qobject_cast<KHTMLView *>(w)) {
2015         handleWidget(static_cast<KHTMLView *>(w)->widget(), view, false);
2016         handleWidget(static_cast<KHTMLView *>(w)->horizontalScrollBar(), view, false);
2017         handleWidget(static_cast<KHTMLView *>(w)->verticalScrollBar(), view, false);
2018         return;
2019     }
2020 
2021     QObjectList children = w->children();
2022     foreach (QObject *object, children) {
2023         QWidget *widget = qobject_cast<QWidget *>(object);
2024         if (widget) {
2025             handleWidget(widget, view);
2026         }
2027     }
2028 }
2029 
2030 class KHTMLBackingStoreHackWidget : public QWidget
2031 {
2032 public:
2033     void publicEvent(QEvent *e)
2034     {
2035         QWidget::event(e);
2036     }
2037 };
2038 
2039 bool  KHTMLView::viewportEvent(QEvent *e)
2040 {
2041     switch (e->type()) {
2042     // those must not be dispatched to the specialized handlers
2043     // as widgetEvent() already took care of that
2044     case QEvent::MouseButtonPress:
2045     case QEvent::MouseButtonRelease:
2046     case QEvent::MouseButtonDblClick:
2047     case QEvent::MouseMove:
2048 #ifndef QT_NO_WHEELEVENT
2049     case QEvent::Wheel:
2050 #endif
2051     case QEvent::ContextMenu:
2052     case QEvent::DragEnter:
2053     case QEvent::DragMove:
2054     case QEvent::DragLeave:
2055     case QEvent::Drop:
2056         return false;
2057     default:
2058         break;
2059     }
2060     return QScrollArea::viewportEvent(e);
2061 }
2062 
2063 static void setInPaintEventFlag(QWidget *w, bool b = true, bool recurse = true)
2064 {
2065     w->setAttribute(Qt::WA_WState_InPaintEvent, b);
2066 
2067     if (!recurse) {
2068         return;
2069     }
2070     if (qobject_cast<KHTMLView *>(w)) {
2071         setInPaintEventFlag(static_cast<KHTMLView *>(w)->widget(), b, false);
2072         setInPaintEventFlag(static_cast<KHTMLView *>(w)->horizontalScrollBar(), b, false);
2073         setInPaintEventFlag(static_cast<KHTMLView *>(w)->verticalScrollBar(), b, false);
2074         return;
2075     }
2076 
2077     foreach (QObject *cw, w->children()) {
2078         if (cw->isWidgetType() && ! static_cast<QWidget *>(cw)->isWindow()
2079                 && !(static_cast<QWidget *>(cw)->windowModality() & Qt::ApplicationModal)) {
2080             setInPaintEventFlag(static_cast<QWidget *>(cw), b);
2081         }
2082     }
2083 }
2084 
2085 bool KHTMLView::eventFilter(QObject *o, QEvent *e)
2086 {
2087     if (e->type() == QEvent::ShortcutOverride) {
2088         QKeyEvent *ke = (QKeyEvent *) e;
2089         if (m_part->isEditable() || m_part->isCaretMode()
2090                 || (m_part->xmlDocImpl() && m_part->xmlDocImpl()->focusNode()
2091                     && m_part->xmlDocImpl()->focusNode()->isContentEditable())) {
2092             if ((ke->modifiers() & Qt::ControlModifier) || (ke->modifiers() & Qt::ShiftModifier)) {
2093                 switch (ke->key()) {
2094                 case Qt::Key_Left:
2095                 case Qt::Key_Right:
2096                 case Qt::Key_Up:
2097                 case Qt::Key_Down:
2098                 case Qt::Key_Home:
2099                 case Qt::Key_End:
2100                     ke->accept();
2101                     return true;
2102                 default:
2103                     break;
2104                 }
2105             }
2106         }
2107     }
2108 
2109     if (e->type() == QEvent::Leave) {
2110         if (d->cursorIconWidget) {
2111             d->cursorIconWidget->hide();
2112         }
2113         m_part->resetHoverText();
2114     }
2115 
2116     QWidget *view = widget();
2117     if (o == view) {
2118         if (widgetEvent(e)) {
2119             return true;
2120         } else if (e->type() == QEvent::Resize) {
2121             updateScrollBars();
2122             return false;
2123         }
2124     } else if (o->isWidgetType()) {
2125         QWidget *v = static_cast<QWidget *>(o);
2126         QWidget *c = v;
2127         while (v && v != view) {
2128             c = v;
2129             v = v->parentWidget();
2130         }
2131         KHTMLWidget *k = dynamic_cast<KHTMLWidget *>(c);
2132         if (v && k && k->m_kwp->isRedirected()) {
2133             bool block = false;
2134             QWidget *w = static_cast<QWidget *>(o);
2135             switch (e->type()) {
2136             case QEvent::UpdateRequest: {
2137                 // implicitly call qt_syncBackingStore(w)
2138                 static_cast<KHTMLBackingStoreHackWidget *>(w)->publicEvent(e);
2139                 block = true;
2140                 break;
2141             }
2142             case QEvent::UpdateLater:
2143             // no break;
2144             case QEvent::Paint:
2145                 if (!allowWidgetPaintEvents) {
2146                     // eat the event. Like this we can control exactly when the widget
2147                     // gets repainted.
2148                     block = true;
2149                     int x = 0, y = 0;
2150                     QWidget *v = w;
2151                     while (v && v->parentWidget() != view) {
2152                         x += v->x();
2153                         y += v->y();
2154                         v = v->parentWidget();
2155                     }
2156 
2157                     QPoint ap = k->m_kwp->absolutePos();
2158                     x += ap.x();
2159                     y += ap.y();
2160                 }
2161                 break;
2162             case QEvent::MouseMove:
2163             case QEvent::MouseButtonPress:
2164             case QEvent::MouseButtonRelease:
2165             case QEvent::MouseButtonDblClick: {
2166 
2167                 if (0 && w->parentWidget() == view && !qobject_cast<QScrollBar *>(w) && !::qobject_cast<QScrollBar *>(w)) {
2168                     QMouseEvent *me = static_cast<QMouseEvent *>(e);
2169                     QPoint pt = w->mapTo(view, me->pos());
2170                     QMouseEvent me2(me->type(), pt, me->button(), me->buttons(), me->modifiers());
2171 
2172                     if (e->type() == QEvent::MouseMove) {
2173                         mouseMoveEvent(&me2);
2174                     } else if (e->type() == QEvent::MouseButtonPress) {
2175                         mousePressEvent(&me2);
2176                     } else if (e->type() == QEvent::MouseButtonRelease) {
2177                         mouseReleaseEvent(&me2);
2178                     } else {
2179                         mouseDoubleClickEvent(&me2);
2180                     }
2181                     block = true;
2182                 }
2183                 break;
2184             }
2185             case QEvent::KeyPress:
2186             case QEvent::KeyRelease:
2187                 if (w->parentWidget() == view && !qobject_cast<QScrollBar *>(w)) {
2188                     QKeyEvent *ke = static_cast<QKeyEvent *>(e);
2189                     if (e->type() == QEvent::KeyPress) {
2190                         keyPressEvent(ke);
2191                         ke->accept();
2192                     } else {
2193                         keyReleaseEvent(ke);
2194                         ke->accept();
2195                     }
2196                     block = true;
2197                 }
2198 
2199                 if (qobject_cast<KUrlRequester *>(w->parentWidget()) &&
2200                         e->type() == QEvent::KeyPress) {
2201                     // Since keypress events on the upload widget will
2202                     // be forwarded to the lineedit anyway,
2203                     // block the original copy at this level to prevent
2204                     // double-emissions of events it doesn't accept
2205                     e->ignore();
2206                     block = true;
2207                 }
2208 
2209                 break;
2210             case QEvent::FocusIn:
2211             case QEvent::FocusOut: {
2212                 QPoint dummy;
2213                 KHTMLView *root = m_kwp->rootViewPos(dummy);
2214                 if (!root) {
2215                     root = this;
2216                 }
2217                 block = static_cast<QFocusEvent *>(e)->reason() != Qt::MouseFocusReason ||  root->underMouse();
2218                 break;
2219             }
2220             default:
2221                 break;
2222             }
2223             if (block) {
2224                 //qDebug("eating event");
2225                 return true;
2226             }
2227         }
2228     }
2229 
2230 //    qCDebug(KHTML_LOG) <<"passing event on to sv event filter object=" << o->className() << " event=" << e->type();
2231     return QScrollArea::eventFilter(o, e);
2232 }
2233 
2234 bool KHTMLView::widgetEvent(QEvent *e)
2235 {
2236     switch (e->type()) {
2237     case QEvent::MouseButtonPress:
2238     case QEvent::MouseButtonRelease:
2239     case QEvent::MouseButtonDblClick:
2240     case QEvent::MouseMove:
2241     case QEvent::Paint:
2242 #ifndef QT_NO_WHEELEVENT
2243     case QEvent::Wheel:
2244 #endif
2245     case QEvent::ContextMenu:
2246     case QEvent::DragEnter:
2247     case QEvent::DragMove:
2248     case QEvent::DragLeave:
2249     case QEvent::Drop:
2250         return QFrame::event(e);
2251     case QEvent::ChildPolished: {
2252         // we need to install an event filter on all children of the widget() to
2253         // be able to get correct stacking of children within the document.
2254         QObject *c = static_cast<QChildEvent *>(e)->child();
2255         if (c->isWidgetType()) {
2256             QWidget *w = static_cast<QWidget *>(c);
2257             // don't install the event filter on toplevels
2258             if (!(w->windowFlags() & Qt::Window) && !(w->windowModality() & Qt::ApplicationModal)) {
2259                 KHTMLWidget *k = dynamic_cast<KHTMLWidget *>(w);
2260                 if (k && k->m_kwp->isRedirected()) {
2261                     w->unsetCursor();
2262                     handleWidget(w, this);
2263                 }
2264             }
2265         }
2266         break;
2267     }
2268     case QEvent::Move: {
2269         if (static_cast<QMoveEvent *>(e)->pos() != QPoint(0, 0)) {
2270             widget()->move(0, 0);
2271             updateScrollBars();
2272             return true;
2273         }
2274         break;
2275     }
2276     default:
2277         break;
2278     }
2279     return false;
2280 }
2281 
2282 bool KHTMLView::hasLayoutPending()
2283 {
2284     return d->layoutTimerId && !d->firstLayoutPending;
2285 }
2286 
2287 DOM::NodeImpl *KHTMLView::nodeUnderMouse() const
2288 {
2289     return d->underMouse;
2290 }
2291 
2292 DOM::NodeImpl *KHTMLView::nonSharedNodeUnderMouse() const
2293 {
2294     return d->underMouseNonShared;
2295 }
2296 
2297 bool KHTMLView::scrollTo(const QRect &bounds)
2298 {
2299     d->scrollingSelf = true; // so scroll events get ignored
2300 
2301     int x, y, xe, ye;
2302     x = bounds.left();
2303     y = bounds.top();
2304     xe = bounds.right();
2305     ye = bounds.bottom();
2306 
2307     //qCDebug(KHTML_LOG)<<"scrolling coords: x="<<x<<" y="<<y<<" width="<<xe-x<<" height="<<ye-y;
2308 
2309     int deltax;
2310     int deltay;
2311 
2312     int curHeight = visibleHeight();
2313     int curWidth = visibleWidth();
2314 
2315     if (ye - y > curHeight - d->borderY) {
2316         ye  = y + curHeight - d->borderY;
2317     }
2318 
2319     if (xe - x > curWidth - d->borderX) {
2320         xe = x + curWidth - d->borderX;
2321     }
2322 
2323     // is xpos of target left of the view's border?
2324     if (x < contentsX() + d->borderX) {
2325         deltax = x - contentsX() - d->borderX;
2326     }
2327     // is xpos of target right of the view's right border?
2328     else if (xe + d->borderX > contentsX() + curWidth) {
2329         deltax = xe + d->borderX - (contentsX() + curWidth);
2330     } else {
2331         deltax = 0;
2332     }
2333 
2334     // is ypos of target above upper border?
2335     if (y < contentsY() + d->borderY) {
2336         deltay = y - contentsY() - d->borderY;
2337     }
2338     // is ypos of target below lower border?
2339     else if (ye + d->borderY > contentsY() + curHeight) {
2340         deltay = ye + d->borderY - (contentsY() + curHeight);
2341     } else {
2342         deltay = 0;
2343     }
2344 
2345     int maxx = curWidth - d->borderX;
2346     int maxy = curHeight - d->borderY;
2347 
2348     int scrollX, scrollY;
2349 
2350     scrollX = deltax > 0 ? (deltax > maxx ? maxx : deltax) : deltax == 0 ? 0 : (deltax > -maxx ? deltax : -maxx);
2351     scrollY = deltay > 0 ? (deltay > maxy ? maxy : deltay) : deltay == 0 ? 0 : (deltay > -maxy ? deltay : -maxy);
2352 
2353     if (contentsX() + scrollX < 0) {
2354         scrollX = -contentsX();
2355     } else if (contentsWidth() - visibleWidth() - contentsX() < scrollX) {
2356         scrollX = contentsWidth() - visibleWidth() - contentsX();
2357     }
2358 
2359     if (contentsY() + scrollY < 0) {
2360         scrollY = -contentsY();
2361     } else if (contentsHeight() - visibleHeight() - contentsY() < scrollY) {
2362         scrollY = contentsHeight() - visibleHeight() - contentsY();
2363     }
2364 
2365     horizontalScrollBar()->setValue(horizontalScrollBar()->value() + scrollX);
2366     verticalScrollBar()->setValue(verticalScrollBar()->value() + scrollY);
2367 
2368     d->scrollingSelf = false;
2369 
2370     if ((abs(deltax) <= maxx) && (abs(deltay) <= maxy)) {
2371         return true;
2372     } else {
2373         return false;
2374     }
2375 
2376 }
2377 
2378 bool KHTMLView::focusNextPrevNode(bool next)
2379 {
2380     // Sets the focus node of the document to be the node after (or if
2381     // next is false, before) the current focus node.  Only nodes that
2382     // are selectable (i.e. for which isFocusable() returns true) are
2383     // taken into account, and the order used is that specified in the
2384     // HTML spec (see DocumentImpl::nextFocusNode() and
2385     // DocumentImpl::previousFocusNode() for details).
2386 
2387     DocumentImpl *doc = m_part->xmlDocImpl();
2388     NodeImpl *oldFocusNode = doc->focusNode();
2389 
2390     // See whether we're in the middle of a detach, or hiding of the
2391     // widget. In this case, we will just clear focus, being careful not to emit events
2392     // or update rendering. Doing this also prevents the code below from going bonkers with
2393     // oldFocusNode not actually being focusable, etc.
2394     if (oldFocusNode) {
2395         if ((oldFocusNode->renderer() && !oldFocusNode->renderer()->parent())
2396                 || !oldFocusNode->isTabFocusable()) {
2397             doc->quietResetFocus();
2398             return true;
2399         }
2400     }
2401 
2402 #if 1
2403     // If the user has scrolled the document, then instead of picking
2404     // the next focusable node in the document, use the first one that
2405     // is within the visible area (if possible).
2406     if (d->scrollBarMoved) {
2407         NodeImpl *toFocus;
2408         if (next) {
2409             toFocus = doc->nextFocusNode(oldFocusNode);
2410         } else {
2411             toFocus = doc->previousFocusNode(oldFocusNode);
2412         }
2413 
2414         if (!toFocus && oldFocusNode) {
2415             if (next) {
2416                 toFocus = doc->nextFocusNode(nullptr);
2417             } else {
2418                 toFocus = doc->previousFocusNode(nullptr);
2419             }
2420         }
2421 
2422         while (toFocus && toFocus != oldFocusNode) {
2423 
2424             QRect focusNodeRect = toFocus->getRect();
2425             if ((focusNodeRect.left() > contentsX()) && (focusNodeRect.right() < contentsX() + visibleWidth()) &&
2426                     (focusNodeRect.top() > contentsY()) && (focusNodeRect.bottom() < contentsY() + visibleHeight())) {
2427                 {
2428                     QRect r = toFocus->getRect();
2429                     ensureVisible(r.right(), r.bottom());
2430                     ensureVisible(r.left(), r.top());
2431                     d->scrollBarMoved = false;
2432                     d->tabMovePending = false;
2433                     d->lastTabbingDirection = next;
2434                     d->pseudoFocusNode = KHTMLViewPrivate::PFNone;
2435                     m_part->xmlDocImpl()->setFocusNode(toFocus);
2436                     Node guard(toFocus);
2437                     if (!toFocus->hasOneRef()) {
2438                         emit m_part->nodeActivated(Node(toFocus));
2439                     }
2440                     return true;
2441                 }
2442             }
2443             if (next) {
2444                 toFocus = doc->nextFocusNode(toFocus);
2445             } else {
2446                 toFocus = doc->previousFocusNode(toFocus);
2447             }
2448 
2449             if (!toFocus && oldFocusNode) {
2450                 if (next) {
2451                     toFocus = doc->nextFocusNode(nullptr);
2452                 } else {
2453                     toFocus = doc->previousFocusNode(nullptr);
2454                 }
2455             }
2456         }
2457 
2458         d->scrollBarMoved = false;
2459     }
2460 #endif
2461 
2462     if (!oldFocusNode && d->pseudoFocusNode == KHTMLViewPrivate::PFNone) {
2463         ensureVisible(contentsX(), next ? 0 : contentsHeight());
2464         d->scrollBarMoved = false;
2465         d->pseudoFocusNode = next ? KHTMLViewPrivate::PFTop : KHTMLViewPrivate::PFBottom;
2466         return true;
2467     }
2468 
2469     NodeImpl *newFocusNode = nullptr;
2470 
2471     if (d->tabMovePending && next != d->lastTabbingDirection) {
2472         //qCDebug(KHTML_LOG) << " tab move pending and tabbing direction changed!\n";
2473         newFocusNode = oldFocusNode;
2474     } else if (next) {
2475         if (oldFocusNode || d->pseudoFocusNode == KHTMLViewPrivate::PFTop) {
2476             newFocusNode = doc->nextFocusNode(oldFocusNode);
2477         }
2478     } else {
2479         if (oldFocusNode || d->pseudoFocusNode == KHTMLViewPrivate::PFBottom) {
2480             newFocusNode = doc->previousFocusNode(oldFocusNode);
2481         }
2482     }
2483 
2484     bool targetVisible = false;
2485     if (!newFocusNode) {
2486         if (next) {
2487             targetVisible = scrollTo(QRect(contentsX() + visibleWidth() / 2, contentsHeight() - d->borderY, 0, 0));
2488         } else {
2489             targetVisible = scrollTo(QRect(contentsX() + visibleWidth() / 2, d->borderY, 0, 0));
2490         }
2491     } else {
2492         // if it's an editable element, activate the caret
2493         if (!m_part->isCaretMode() && newFocusNode->isContentEditable()) {
2494             // qCDebug(KHTML_LOG) << "show caret! fn: " << newFocusNode->nodeName().string();
2495             m_part->clearCaretRectIfNeeded();
2496             m_part->d->editor_context.m_selection.moveTo(Position(newFocusNode, 0L));
2497             m_part->setCaretVisible(true);
2498         } else {
2499             m_part->setCaretVisible(false);
2500             // qCDebug(KHTML_LOG) << "hide caret! fn: " << newFocusNode->nodeName().string();
2501         }
2502         m_part->notifySelectionChanged();
2503 
2504         targetVisible = scrollTo(newFocusNode->getRect());
2505     }
2506 
2507     if (targetVisible) {
2508         //qCDebug(KHTML_LOG) << " target reached.\n";
2509         d->tabMovePending = false;
2510 
2511         m_part->xmlDocImpl()->setFocusNode(newFocusNode);
2512         if (newFocusNode) {
2513             Node guard(newFocusNode);
2514             if (!newFocusNode->hasOneRef()) {
2515                 emit m_part->nodeActivated(Node(newFocusNode));
2516             }
2517             return true;
2518         } else {
2519             d->pseudoFocusNode = next ? KHTMLViewPrivate::PFBottom : KHTMLViewPrivate::PFTop;
2520             return false;
2521         }
2522     } else {
2523         if (!d->tabMovePending) {
2524             d->lastTabbingDirection = next;
2525         }
2526         d->tabMovePending = true;
2527         return true;
2528     }
2529 }
2530 
2531 void KHTMLView::displayAccessKeys()
2532 {
2533     QVector< QChar > taken;
2534     displayAccessKeys(nullptr, this, taken, false);
2535     displayAccessKeys(nullptr, this, taken, true);
2536 }
2537 
2538 void KHTMLView::displayAccessKeys(KHTMLView *caller, KHTMLView *origview, QVector< QChar > &taken, bool use_fallbacks)
2539 {
2540     QMap< ElementImpl *, QChar > fallbacks;
2541     if (use_fallbacks) {
2542         fallbacks = buildFallbackAccessKeys();
2543     }
2544     for (NodeImpl *n = m_part->xmlDocImpl(); n != nullptr; n = n->traverseNextNode()) {
2545         if (n->isElementNode()) {
2546             ElementImpl *en = static_cast< ElementImpl * >(n);
2547             DOMString s = en->getAttribute(ATTR_ACCESSKEY);
2548             QString accesskey;
2549             if (s.length() == 1) {
2550                 QChar a = s.string()[ 0 ].toUpper();
2551                 if (qFind(taken.begin(), taken.end(), a) == taken.end()) { // !contains
2552                     accesskey = a;
2553                 }
2554             }
2555             if (accesskey.isNull() && fallbacks.contains(en)) {
2556                 QChar a = fallbacks[ en ].toUpper();
2557                 if (qFind(taken.begin(), taken.end(), a) == taken.end()) { // !contains
2558                     accesskey = QString("<qt><i>") + a + "</i></qt>";
2559                 }
2560             }
2561             if (!accesskey.isNull()) {
2562                 QRect rec = en->getRect();
2563                 QLabel *lab = new QLabel(accesskey, widget());
2564                 lab->setAttribute(Qt::WA_DeleteOnClose);
2565                 lab->setObjectName("KHTMLAccessKey");
2566                 connect(origview, SIGNAL(hideAccessKeys()), lab, SLOT(close()));
2567                 connect(this, SIGNAL(repaintAccessKeys()), lab, SLOT(repaint()));
2568                 lab->setPalette(QToolTip::palette());
2569                 lab->setLineWidth(2);
2570                 lab->setFrameStyle(QFrame::Box | QFrame::Plain);
2571                 lab->setContentsMargins(3, 3, 3, 3);
2572                 lab->adjustSize();
2573                 lab->setParent(widget());
2574                 lab->setAutoFillBackground(true);
2575                 lab->move(
2576                     qMin(rec.left() + rec.width() / 2 - contentsX(), contentsWidth() - lab->width()),
2577                     qMin(rec.top() + rec.height() / 2 - contentsY(), contentsHeight() - lab->height()));
2578                 lab->show();
2579                 taken.append(accesskey[ 0 ]);
2580             }
2581         }
2582     }
2583     if (use_fallbacks) {
2584         return;
2585     }
2586 
2587     QList<KParts::ReadOnlyPart *> frames = m_part->frames();
2588     foreach (KParts::ReadOnlyPart *cur, frames) {
2589         if (!qobject_cast<KHTMLPart *>(cur)) {
2590             continue;
2591         }
2592         KHTMLPart *part = static_cast< KHTMLPart * >(cur);
2593         if (part->view() && part->view() != caller) {
2594             part->view()->displayAccessKeys(this, origview, taken, use_fallbacks);
2595         }
2596     }
2597 
2598     // pass up to the parent
2599     if (m_part->parentPart() && m_part->parentPart()->view()
2600             && m_part->parentPart()->view() != caller) {
2601         m_part->parentPart()->view()->displayAccessKeys(this, origview, taken, use_fallbacks);
2602     }
2603 }
2604 
2605 bool KHTMLView::isScrollingFromMouseWheel() const
2606 {
2607     return d->scrollingFromWheel != QPoint(-1, -1);
2608 }
2609 
2610 void KHTMLView::accessKeysTimeout()
2611 {
2612     d->accessKeysActivated = false;
2613     d->accessKeysPreActivate = false;
2614     m_part->setStatusBarText(QString(), KHTMLPart::BarOverrideText);
2615     emit hideAccessKeys();
2616 }
2617 
2618 // Handling of the HTML accesskey attribute.
2619 bool KHTMLView::handleAccessKey(const QKeyEvent *ev)
2620 {
2621 // Qt interprets the keyevent also with the modifiers, and ev->text() matches that,
2622 // but this code must act as if the modifiers weren't pressed
2623     QChar c;
2624     if (ev->key() >= Qt::Key_A && ev->key() <= Qt::Key_Z) {
2625         c = 'A' + ev->key() - Qt::Key_A;
2626     } else if (ev->key() >= Qt::Key_0 && ev->key() <= Qt::Key_9) {
2627         c = '0' + ev->key() - Qt::Key_0;
2628     } else {
2629         // TODO fake XKeyEvent and XLookupString ?
2630         // This below seems to work e.g. for eacute though.
2631         if (ev->text().length() == 1) {
2632             c = ev->text()[ 0 ];
2633         }
2634     }
2635     if (c.isNull()) {
2636         return false;
2637     }
2638     return focusNodeWithAccessKey(c);
2639 }
2640 
2641 bool KHTMLView::focusNodeWithAccessKey(QChar c, KHTMLView *caller)
2642 {
2643     DocumentImpl *doc = m_part->xmlDocImpl();
2644     if (!doc) {
2645         return false;
2646     }
2647     ElementImpl *node = doc->findAccessKeyElement(c);
2648     if (!node) {
2649         QList<KParts::ReadOnlyPart *> frames = m_part->frames();
2650         foreach (KParts::ReadOnlyPart *cur, frames) {
2651             if (!qobject_cast<KHTMLPart *>(cur)) {
2652                 continue;
2653             }
2654             KHTMLPart *part = static_cast< KHTMLPart * >(cur);
2655             if (part->view() && part->view() != caller
2656                     && part->view()->focusNodeWithAccessKey(c, this)) {
2657                 return true;
2658             }
2659         }
2660         // pass up to the parent
2661         if (m_part->parentPart() && m_part->parentPart()->view()
2662                 && m_part->parentPart()->view() != caller
2663                 && m_part->parentPart()->view()->focusNodeWithAccessKey(c, this)) {
2664             return true;
2665         }
2666         if (caller == nullptr) {  // the active frame (where the accesskey was pressed)
2667             const QMap< ElementImpl *, QChar > fallbacks = buildFallbackAccessKeys();
2668             for (QMap< ElementImpl *, QChar >::ConstIterator it = fallbacks.begin();
2669                     it != fallbacks.end();
2670                     ++it)
2671                 if (*it == c) {
2672                     node = it.key();
2673                     break;
2674                 }
2675         }
2676         if (node == nullptr) {
2677             return false;
2678         }
2679     }
2680 
2681     // Scroll the view as necessary to ensure that the new focus node is visible
2682 
2683     QRect r = node->getRect();
2684     ensureVisible(r.right(), r.bottom());
2685     ensureVisible(r.left(), r.top());
2686 
2687     Node guard(node);
2688     if (node->isFocusable()) {
2689         if (node->id() == ID_LABEL) {
2690             // if Accesskey is a label, give focus to the label's referrer.
2691             node = static_cast<ElementImpl *>(static_cast< HTMLLabelElementImpl * >(node)->getFormElement());
2692             if (!node) {
2693                 return true;
2694             }
2695             guard = node;
2696         }
2697         // Set focus node on the document
2698         m_part->xmlDocImpl()->setFocusNode(node);
2699 
2700         if (node != nullptr && node->hasOneRef()) { // deleted, only held by guard
2701             return true;
2702         }
2703         emit m_part->nodeActivated(Node(node));
2704         if (node != nullptr && node->hasOneRef()) {
2705             return true;
2706         }
2707     }
2708 
2709     switch (node->id()) {
2710     case ID_A:
2711         static_cast< HTMLAnchorElementImpl * >(node)->click();
2712         break;
2713     case ID_INPUT:
2714         static_cast< HTMLInputElementImpl * >(node)->click();
2715         break;
2716     case ID_BUTTON:
2717         static_cast< HTMLButtonElementImpl * >(node)->click();
2718         break;
2719     case ID_AREA:
2720         static_cast< HTMLAreaElementImpl * >(node)->click();
2721         break;
2722     case ID_TEXTAREA:
2723         break; // just focusing it is enough
2724     case ID_LEGEND:
2725         // TODO
2726         break;
2727     }
2728     return true;
2729 }
2730 
2731 static QString getElementText(NodeImpl *start, bool after)
2732 {
2733     QString ret;             // nextSibling(), to go after e.g. </select>
2734     for (NodeImpl *n = after ? start->nextSibling() : start->traversePreviousNode();
2735             n != nullptr;
2736             n = after ? n->traverseNextNode() : n->traversePreviousNode()) {
2737         if (n->isTextNode()) {
2738             if (after) {
2739                 ret += static_cast< TextImpl * >(n)->toString().string();
2740             } else {
2741                 ret.prepend(static_cast< TextImpl * >(n)->toString().string());
2742             }
2743         } else {
2744             switch (n->id()) {
2745             case ID_A:
2746             case ID_FONT:
2747             case ID_TT:
2748             case ID_U:
2749             case ID_B:
2750             case ID_I:
2751             case ID_S:
2752             case ID_STRIKE:
2753             case ID_BIG:
2754             case ID_SMALL:
2755             case ID_EM:
2756             case ID_STRONG:
2757             case ID_DFN:
2758             case ID_CODE:
2759             case ID_SAMP:
2760             case ID_KBD:
2761             case ID_VAR:
2762             case ID_CITE:
2763             case ID_ABBR:
2764             case ID_ACRONYM:
2765             case ID_SUB:
2766             case ID_SUP:
2767             case ID_SPAN:
2768             case ID_NOBR:
2769             case ID_WBR:
2770                 break;
2771             case ID_TD:
2772                 if (ret.trimmed().isEmpty()) {
2773                     break;
2774                 }
2775             // fall through
2776             default:
2777                 return ret.simplified();
2778             }
2779         }
2780     }
2781     return ret.simplified();
2782 }
2783 
2784 static QMap< NodeImpl *, QString > buildLabels(NodeImpl *start)
2785 {
2786     QMap< NodeImpl *, QString > ret;
2787     for (NodeImpl *n = start;
2788             n != nullptr;
2789             n = n->traverseNextNode()) {
2790         if (n->id() == ID_LABEL) {
2791             HTMLLabelElementImpl *label = static_cast< HTMLLabelElementImpl * >(n);
2792             NodeImpl *labelfor = label->getFormElement();
2793             if (labelfor) {
2794                 ret[ labelfor ] = label->innerText().string().simplified();
2795             }
2796         }
2797     }
2798     return ret;
2799 }
2800 
2801 namespace khtml
2802 {
2803 struct AccessKeyData {
2804     ElementImpl *element;
2805     QString text;
2806     QString url;
2807     int priority; // 10(highest) - 0(lowest)
2808 };
2809 }
2810 
2811 QMap< ElementImpl *, QChar > KHTMLView::buildFallbackAccessKeys() const
2812 {
2813     // build a list of all possible candidate elements that could use an accesskey
2814     QLinkedList< AccessKeyData > data; // Note: this has to be a list type that keep iterators valid
2815     // when other entries are removed
2816     QMap< NodeImpl *, QString > labels = buildLabels(m_part->xmlDocImpl());
2817     QMap< QString, QChar > hrefs;
2818 
2819     for (NodeImpl *n = m_part->xmlDocImpl();
2820             n != nullptr;
2821             n = n->traverseNextNode()) {
2822         if (n->isElementNode()) {
2823             ElementImpl *element = static_cast< ElementImpl * >(n);
2824             if (element->renderer() == nullptr) {
2825                 continue;    // not visible
2826             }
2827             QString text;
2828             QString url;
2829             int priority = 0;
2830             bool ignore = false;
2831             bool text_after = false;
2832             bool text_before = false;
2833             switch (element->id()) {
2834             case ID_A:
2835                 url = element->getAttribute(ATTR_HREF).trimSpaces().string();
2836                 if (url.isEmpty()) { // doesn't have href, it's only an anchor
2837                     continue;
2838                 }
2839                 text = static_cast< HTMLElementImpl * >(element)->innerText().string().simplified();
2840                 priority = 2;
2841                 break;
2842             case ID_INPUT: {
2843                 HTMLInputElementImpl *in = static_cast< HTMLInputElementImpl * >(element);
2844                 switch (in->inputType()) {
2845                 case HTMLInputElementImpl::SUBMIT:
2846                     text = in->value().string();
2847                     if (text.isEmpty()) {
2848                         text = i18n("Submit");
2849                     }
2850                     priority = 7;
2851                     break;
2852                 case HTMLInputElementImpl::IMAGE:
2853                     text = in->altText().string();
2854                     priority = 7;
2855                     break;
2856                 case HTMLInputElementImpl::BUTTON:
2857                     text = in->value().string();
2858                     priority = 5;
2859                     break;
2860                 case HTMLInputElementImpl::RESET:
2861                     text = in->value().string();
2862                     if (text.isEmpty()) {
2863                         text = i18n("Reset");
2864                     }
2865                     priority = 5;
2866                     break;
2867                 case HTMLInputElementImpl::HIDDEN:
2868                     ignore = true;
2869                     break;
2870                 case HTMLInputElementImpl::CHECKBOX:
2871                 case HTMLInputElementImpl::RADIO:
2872                     text_after = true;
2873                     priority = 5;
2874                     break;
2875                 case HTMLInputElementImpl::TEXT:
2876                 case HTMLInputElementImpl::PASSWORD:
2877                 case HTMLInputElementImpl::FILE:
2878                     text_before = true;
2879                     priority = 5;
2880                     break;
2881                 default:
2882                     priority = 5;
2883                     break;
2884                 }
2885                 break;
2886             }
2887             case ID_BUTTON:
2888                 text = static_cast< HTMLElementImpl * >(element)->innerText().string().simplified();
2889                 switch (static_cast< HTMLButtonElementImpl * >(element)->buttonType()) {
2890                 case HTMLButtonElementImpl::SUBMIT:
2891                     if (text.isEmpty()) {
2892                         text = i18n("Submit");
2893                     }
2894                     priority = 7;
2895                     break;
2896                 case HTMLButtonElementImpl::RESET:
2897                     if (text.isEmpty()) {
2898                         text = i18n("Reset");
2899                     }
2900                     priority = 5;
2901                     break;
2902                 default:
2903                     priority = 5;
2904                     break;
2905                 }
2906                 break;
2907             case ID_SELECT: // these don't have accesskey attribute, but quick access may be handy
2908                 text_before = true;
2909                 text_after = true;
2910                 priority = 5;
2911                 break;
2912             case ID_FRAME:
2913                 ignore = true;
2914                 break;
2915             default:
2916                 ignore = !element->isFocusable();
2917                 priority = 2;
2918                 break;
2919             }
2920             if (ignore) {
2921                 continue;
2922             }
2923 
2924             // build map of manually assigned accesskeys and their targets
2925             DOMString akey = element->getAttribute(ATTR_ACCESSKEY);
2926             if (akey.length() == 1) {
2927                 hrefs[url] = akey.string()[ 0 ].toUpper();
2928                 continue; // has accesskey set, ignore
2929             }
2930             if (text.isNull() && labels.contains(element)) {
2931                 text = labels[ element ];
2932             }
2933             if (text.isNull() && text_before) {
2934                 text = getElementText(element, false);
2935             }
2936             if (text.isNull() && text_after) {
2937                 text = getElementText(element, true);
2938             }
2939             text = text.trimmed();
2940             // increase priority of items which have explicitly specified accesskeys in the config
2941             const QList< QPair< QString, QChar > > priorities
2942                 = m_part->settings()->fallbackAccessKeysAssignments();
2943             for (QList< QPair< QString, QChar > >::ConstIterator it = priorities.begin();
2944                     it != priorities.end();
2945                     ++it) {
2946                 if (text == (*it).first) {
2947                     priority = 10;
2948                 }
2949             }
2950             AccessKeyData tmp = { element, text, url, priority };
2951             data.append(tmp);
2952         }
2953     }
2954 
2955     QList< QChar > keys;
2956     for (char c = 'A'; c <= 'Z'; ++c) {
2957         keys << c;
2958     }
2959     for (char c = '0'; c <= '9'; ++c) {
2960         keys << c;
2961     }
2962     for (NodeImpl *n = m_part->xmlDocImpl();
2963             n != nullptr;
2964             n = n->traverseNextNode()) {
2965         if (n->isElementNode()) {
2966             ElementImpl *en = static_cast< ElementImpl * >(n);
2967             DOMString s = en->getAttribute(ATTR_ACCESSKEY);
2968             if (s.length() == 1) {
2969                 QChar c = s.string()[ 0 ].toUpper();
2970                 keys.removeAll(c);   // remove manually assigned accesskeys
2971             }
2972         }
2973     }
2974 
2975     QMap< ElementImpl *, QChar > ret;
2976     for (int priority = 10; priority >= 0; --priority) {
2977         for (QLinkedList< AccessKeyData >::Iterator it = data.begin();
2978                 it != data.end();
2979             ) {
2980             if ((*it).priority != priority) {
2981                 ++it;
2982                 continue;
2983             }
2984             if (keys.isEmpty()) {
2985                 break;
2986             }
2987             QString text = (*it).text;
2988             QChar key;
2989             const QString url = (*it).url;
2990             // an identical link already has an accesskey assigned
2991             if (hrefs.contains(url)) {
2992                 it = data.erase(it);
2993                 continue;
2994             }
2995             if (!text.isEmpty()) {
2996                 const QList< QPair< QString, QChar > > priorities
2997                     = m_part->settings()->fallbackAccessKeysAssignments();
2998                 for (QList< QPair< QString, QChar > >::ConstIterator it = priorities.begin();
2999                         it != priorities.end();
3000                         ++it)
3001                     if (text == (*it).first && keys.contains((*it).second)) {
3002                         key = (*it).second;
3003                         break;
3004                     }
3005             }
3006             // try first to select the first character as the accesskey,
3007             // then first character of the following words,
3008             // and then simply the first free character
3009             if (key.isNull() && !text.isEmpty()) {
3010                 const QStringList words = text.split(' ');
3011                 for (QStringList::ConstIterator it = words.begin();
3012                         it != words.end();
3013                         ++it) {
3014                     if (keys.contains((*it)[ 0 ].toUpper())) {
3015                         key = (*it)[ 0 ].toUpper();
3016                         break;
3017                     }
3018                 }
3019             }
3020             if (key.isNull() && !text.isEmpty()) {
3021                 for (int i = 0; i < text.length(); ++i) {
3022                     if (keys.contains(text[ i ].toUpper())) {
3023                         key = text[ i ].toUpper();
3024                         break;
3025                     }
3026                 }
3027             }
3028             if (key.isNull()) {
3029                 key = keys.front();
3030             }
3031             ret[(*it).element ] = key;
3032             keys.removeAll(key);
3033             it = data.erase(it);
3034             // assign the same accesskey also to other elements pointing to the same url
3035             if (!url.isEmpty() && !url.startsWith("javascript:", Qt::CaseInsensitive)) {
3036                 for (QLinkedList< AccessKeyData >::Iterator it2 = data.begin();
3037                         it2 != data.end();
3038                     ) {
3039                     if ((*it2).url == url) {
3040                         ret[(*it2).element ] = key;
3041                         if (it == it2) {
3042                             ++it;
3043                         }
3044                         it2 = data.erase(it2);
3045                     } else {
3046                         ++it2;
3047                     }
3048                 }
3049             }
3050         }
3051     }
3052     return ret;
3053 }
3054 
3055 void KHTMLView::setMediaType(const QString &medium)
3056 {
3057     m_medium = medium;
3058 }
3059 
3060 QString KHTMLView::mediaType() const
3061 {
3062     return m_medium;
3063 }
3064 
3065 bool KHTMLView::pagedMode() const
3066 {
3067     return d->paged;
3068 }
3069 
3070 void KHTMLView::setWidgetVisible(RenderWidget *w, bool vis)
3071 {
3072     if (vis) {
3073         d->visibleWidgets.insert(w, w->widget());
3074     } else {
3075         d->visibleWidgets.remove(w);
3076     }
3077 }
3078 
3079 bool KHTMLView::needsFullRepaint() const
3080 {
3081     return d->needsFullRepaint;
3082 }
3083 
3084 namespace
3085 {
3086 class QPointerDeleter
3087 {
3088 public:
3089     explicit QPointerDeleter(QObject *o) : obj(o) {}
3090     ~QPointerDeleter()
3091     {
3092         delete obj;
3093     }
3094 private:
3095     const QPointer<QObject> obj;
3096 };
3097 }
3098 
3099 void KHTMLView::print(bool quick)
3100 {
3101     QPrinter printer;
3102     print(&printer, quick);
3103 }
3104 
3105 void KHTMLView::print(QPrinter *_printer, bool quick)
3106 {
3107     if (!m_part->xmlDocImpl()) {
3108         return;
3109     }
3110     khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer());
3111     if (!root) {
3112         return;
3113     }
3114 
3115     QPrinter &printer = *_printer;
3116     QPointer<QPrintDialog> dialog(new QPrintDialog(&printer, this));
3117     QPointer<KHTMLPrintSettings> printSettings(new KHTMLPrintSettings(dialog)); //XXX: doesn't save settings between prints like this
3118     dialog->setOptionTabs(QList<QWidget *>() << printSettings.data());
3119 
3120     const QPointerDeleter dialogDeleter(dialog);
3121 
3122     QString docname = m_part->xmlDocImpl()->URL().toDisplayString();
3123     if (!docname.isEmpty()) {
3124         docname = KStringHandler::csqueeze(docname, 80);
3125     }
3126 
3127     if (quick || (dialog->exec() && dialog)) { /*'this' and thus dialog might have been deleted while exec()!*/
3128         viewport()->setCursor(Qt::WaitCursor);   // only viewport(), no QApplication::, otherwise we get the busy cursor in kdeprint's dialogs
3129         // set up KPrinter
3130         printer.setFullPage(false);
3131         printer.setCreator(QString("KDE %1.%2.%3 HTML Library").arg(KHTML_VERSION_MAJOR).arg(KHTML_VERSION_MINOR).arg(KHTML_VERSION_PATCH));
3132         printer.setDocName(docname);
3133 
3134         QPainter *p = new QPainter;
3135         p->begin(&printer);
3136         khtml::setPrintPainter(p);
3137 
3138         m_part->xmlDocImpl()->setPaintDevice(&printer);
3139         QString oldMediaType = mediaType();
3140         setMediaType("print");
3141         // We ignore margin settings for html and body when printing
3142         // and use the default margins from the print-system
3143         // (In Qt 3.0.x the default margins are hardcoded in Qt)
3144         m_part->xmlDocImpl()->setPrintStyleSheet(printSettings->printFriendly() ?
3145                 "* { background-image: none !important;"
3146                 "    background-color: white !important;"
3147                 "    color: black !important; }"
3148                 "body { margin: 0px !important; }"
3149                 "html { margin: 0px !important; }" :
3150                 "body { margin: 0px !important; }"
3151                 "html { margin: 0px !important; }"
3152                                                 );
3153 
3154         // qCDebug(KHTML_LOG) << "printing: physical page width = " << printer.width()
3155         //              << " height = " << printer.height();
3156         root->setStaticMode(true);
3157         root->setPagedMode(true);
3158         root->setWidth(printer.width());
3159 //         root->setHeight(printer.height());
3160         root->setPageTop(0);
3161         root->setPageBottom(0);
3162         d->paged = true;
3163 
3164         m_part->xmlDocImpl()->styleSelector()->computeFontSizes(printer.logicalDpiY(), 100);
3165         m_part->xmlDocImpl()->updateStyleSelector();
3166         root->setPrintImages(printSettings->printImages());
3167         root->makePageBreakAvoidBlocks();
3168 
3169         root->setNeedsLayoutAndMinMaxRecalc();
3170         root->layout();
3171 
3172         // check sizes ask for action.. (scale or clip)
3173 
3174         bool printHeader = printSettings->printHeader();
3175 
3176         int headerHeight = 0;
3177         QFont headerFont("Sans Serif", 8);
3178 
3179         QString headerLeft = QDate::currentDate().toString(Qt::DefaultLocaleShortDate);
3180         QString headerMid = docname;
3181         QString headerRight;
3182 
3183         if (printHeader) {
3184             p->setFont(headerFont);
3185             headerHeight = (p->fontMetrics().lineSpacing() * 3) / 2;
3186         }
3187 
3188         // ok. now print the pages.
3189         // qCDebug(KHTML_LOG) << "printing: html page width = " << root->docWidth()
3190         //              << " height = " << root->docHeight();
3191         // qCDebug(KHTML_LOG) << "printing: margins left = " << printer.pageRect().left() - printer.paperRect().left()
3192         //              << " top = " << printer.pageRect().top() - printer.paperRect().top();
3193         // qCDebug(KHTML_LOG) << "printing: paper width = " << printer.width()
3194         //              << " height = " << printer.height();
3195         // if the width is too large to fit on the paper we just scale
3196         // the whole thing.
3197         int pageWidth = printer.width();
3198         int pageHeight = printer.height();
3199         p->setClipRect(0, 0, pageWidth, pageHeight);
3200 
3201         pageHeight -= headerHeight;
3202 
3203 #ifndef QT_NO_TRANSFORMATIONS
3204         bool scalePage = false;
3205         double scale = 0.0;
3206         if (root->docWidth() > printer.width()) {
3207             scalePage = true;
3208             scale = ((double) printer.width()) / ((double) root->docWidth());
3209             pageHeight = (int)(pageHeight / scale);
3210             pageWidth = (int)(pageWidth / scale);
3211             headerHeight = (int)(headerHeight / scale);
3212         }
3213 #endif
3214         // qCDebug(KHTML_LOG) << "printing: scaled html width = " << pageWidth
3215         //              << " height = " << pageHeight;
3216 
3217         root->setHeight(pageHeight);
3218         root->setPageBottom(pageHeight);
3219         root->setNeedsLayout(true);
3220         root->layoutIfNeeded();
3221 //         m_part->slotDebugRenderTree();
3222 
3223         // Squeeze header to make it it on the page.
3224         if (printHeader) {
3225             int available_width = printer.width() - 10 -
3226                                   2 * qMax(p->boundingRect(0, 0, printer.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerLeft).width(),
3227                                            p->boundingRect(0, 0, printer.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerRight).width());
3228             if (available_width < 150) {
3229                 available_width = 150;
3230             }
3231             int mid_width;
3232             int squeeze = 120;
3233             do {
3234                 headerMid = KStringHandler::csqueeze(docname, squeeze);
3235                 mid_width = p->boundingRect(0, 0, printer.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerMid).width();
3236                 squeeze -= 10;
3237             } while (mid_width > available_width);
3238         }
3239 
3240         int top = 0;
3241         int bottom = 0;
3242         int page = 1;
3243         while (top < root->docHeight()) {
3244             if (top > 0) {
3245                 printer.newPage();
3246             }
3247 #ifndef QT_NO_TRANSFORMATIONS
3248             if (scalePage) {
3249                 p->scale(scale, scale);
3250             }
3251 #endif
3252             p->save();
3253             p->setClipRect(0, 0, pageWidth, headerHeight);
3254             if (printHeader) {
3255                 int dy = p->fontMetrics().lineSpacing();
3256                 p->setPen(Qt::black);
3257                 p->setFont(headerFont);
3258 
3259                 headerRight = QString("#%1").arg(page);
3260 
3261                 p->drawText(0, 0, printer.width(), dy, Qt::AlignLeft, headerLeft);
3262                 p->drawText(0, 0, printer.width(), dy, Qt::AlignHCenter, headerMid);
3263                 p->drawText(0, 0, printer.width(), dy, Qt::AlignRight, headerRight);
3264             }
3265 
3266             p->restore();
3267             p->translate(0, headerHeight - top);
3268 
3269             bottom = top + pageHeight;
3270 
3271             root->setPageTop(top);
3272             root->setPageBottom(bottom);
3273             root->setPageNumber(page);
3274 
3275             root->layer()->paint(p, QRect(0, top, pageWidth, pageHeight));
3276             // qCDebug(KHTML_LOG) << "printed: page " << page <<" bottom At = " << bottom;
3277 
3278             top = bottom;
3279             p->resetTransform();
3280             page++;
3281         }
3282 
3283         p->end();
3284         delete p;
3285 
3286         // and now reset the layout to the usual one...
3287         root->setPagedMode(false);
3288         root->setStaticMode(false);
3289         d->paged = false;
3290         khtml::setPrintPainter(nullptr);
3291         setMediaType(oldMediaType);
3292         m_part->xmlDocImpl()->setPaintDevice(this);
3293         m_part->xmlDocImpl()->styleSelector()->computeFontSizes(m_part->xmlDocImpl()->logicalDpiY(), m_part->fontScaleFactor());
3294         m_part->xmlDocImpl()->updateStyleSelector();
3295         viewport()->unsetCursor();
3296     }
3297 }
3298 
3299 void KHTMLView::slotPaletteChanged()
3300 {
3301     if (!m_part->xmlDocImpl()) {
3302         return;
3303     }
3304     DOM::DocumentImpl *document = m_part->xmlDocImpl();
3305     if (!document->isHTMLDocument()) {
3306         return;
3307     }
3308     khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(document->renderer());
3309     if (!root) {
3310         return;
3311     }
3312     root->style()->resetPalette();
3313     NodeImpl *body = static_cast<HTMLDocumentImpl *>(document)->body();
3314     if (!body) {
3315         return;
3316     }
3317     body->setChanged(true);
3318     body->recalcStyle(NodeImpl::Force);
3319 }
3320 
3321 void KHTMLView::paint(QPainter *p, const QRect &rc, int yOff, bool *more)
3322 {
3323     if (!m_part->xmlDocImpl()) {
3324         return;
3325     }
3326     khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer());
3327     if (!root) {
3328         return;
3329     }
3330 #ifdef SPEED_DEBUG
3331     d->firstRepaintPending = false;
3332 #endif
3333 
3334     QPaintDevice *opd = m_part->xmlDocImpl()->paintDevice();
3335     m_part->xmlDocImpl()->setPaintDevice(p->device());
3336     root->setPagedMode(true);
3337     root->setStaticMode(true);
3338     root->setWidth(rc.width());
3339 
3340     // save()
3341     QRegion creg = p->clipRegion();
3342     QTransform t = p->worldTransform();
3343     QRect w = p->window();
3344     QRect v = p->viewport();
3345     bool vte = p->viewTransformEnabled();
3346     bool wme = p->worldMatrixEnabled();
3347 
3348     p->setClipRect(rc);
3349     p->translate(rc.left(), rc.top());
3350     double scale = ((double) rc.width() / (double) root->docWidth());
3351     int height = (int)((double) rc.height() / scale);
3352 #ifndef QT_NO_TRANSFORMATIONS
3353     p->scale(scale, scale);
3354 #endif
3355     root->setPageTop(yOff);
3356     root->setPageBottom(yOff + height);
3357 
3358     root->layer()->paint(p, QRect(0, yOff, root->docWidth(), height));
3359     if (more) {
3360         *more = yOff + height < root->docHeight();
3361     }
3362 
3363     // restore()
3364     p->setWorldTransform(t);
3365     p->setWindow(w);
3366     p->setViewport(v);
3367     p->setViewTransformEnabled(vte);
3368     p->setWorldMatrixEnabled(wme);
3369     if (!creg.isEmpty()) {
3370         p->setClipRegion(creg);
3371     } else {
3372         p->setClipRegion(QRegion(), Qt::NoClip);
3373     }
3374 
3375     root->setPagedMode(false);
3376     root->setStaticMode(false);
3377     m_part->xmlDocImpl()->setPaintDevice(opd);
3378 }
3379 
3380 void KHTMLView::render(QPainter *p, const QRect &r, const QPoint &off)
3381 {
3382 #ifdef SPEED_DEBUG
3383     d->firstRepaintPending = false;
3384 #endif
3385     QRect clip(off.x() + r.x(), off.y() + r.y(), r.width(), r.height());
3386     if (!m_part || !m_part->xmlDocImpl() || !m_part->xmlDocImpl()->renderer()) {
3387         p->fillRect(clip, palette().brush(QPalette::Active, QPalette::Base));
3388         return;
3389     }
3390     QPaintDevice *opd = m_part->xmlDocImpl()->paintDevice();
3391     m_part->xmlDocImpl()->setPaintDevice(p->device());
3392 
3393     // save()
3394     QRegion creg = p->clipRegion();
3395     QTransform t = p->worldTransform();
3396     QRect w = p->window();
3397     QRect v = p->viewport();
3398     bool vte = p->viewTransformEnabled();
3399     bool wme = p->worldMatrixEnabled();
3400 
3401     p->setClipRect(clip);
3402     QRect rect = r.translated(contentsX(), contentsY());
3403     p->translate(off.x() - contentsX(), off.y() - contentsY());
3404 
3405     m_part->xmlDocImpl()->renderer()->layer()->paint(p, rect);
3406 
3407     // restore()
3408     p->setWorldTransform(t);
3409     p->setWindow(w);
3410     p->setViewport(v);
3411     p->setViewTransformEnabled(vte);
3412     p->setWorldMatrixEnabled(wme);
3413     if (!creg.isEmpty()) {
3414         p->setClipRegion(creg);
3415     } else {
3416         p->setClipRegion(QRegion(), Qt::NoClip);
3417     }
3418 
3419     m_part->xmlDocImpl()->setPaintDevice(opd);
3420 }
3421 
3422 void KHTMLView::setHasStaticBackground(bool partial)
3423 {
3424     // full static iframe is irreversible for now
3425     if (d->staticWidget == KHTMLViewPrivate::SBFull && m_kwp->isRedirected()) {
3426         return;
3427     }
3428 
3429     d->staticWidget = partial ?
3430                       KHTMLViewPrivate::SBPartial : KHTMLViewPrivate::SBFull;
3431 }
3432 
3433 void KHTMLView::setHasNormalBackground()
3434 {
3435     // full static iframe is irreversible for now
3436     if (d->staticWidget == KHTMLViewPrivate::SBFull && m_kwp->isRedirected()) {
3437         return;
3438     }
3439 
3440     d->staticWidget = KHTMLViewPrivate::SBNone;
3441 }
3442 
3443 void KHTMLView::addStaticObject(bool fixed)
3444 {
3445     if (fixed) {
3446         d->fixedObjectsCount++;
3447     } else {
3448         d->staticObjectsCount++;
3449     }
3450 
3451     setHasStaticBackground(true /*partial*/);
3452 }
3453 
3454 void KHTMLView::removeStaticObject(bool fixed)
3455 {
3456     if (fixed) {
3457         d->fixedObjectsCount--;
3458     } else {
3459         d->staticObjectsCount--;
3460     }
3461 
3462     assert(d->fixedObjectsCount >= 0 && d->staticObjectsCount >= 0);
3463 
3464     if (!d->staticObjectsCount && !d->fixedObjectsCount) {
3465         setHasNormalBackground();
3466     } else {
3467         setHasStaticBackground(true /*partial*/);
3468     }
3469 }
3470 
3471 void KHTMLView::setVerticalScrollBarPolicy(Qt::ScrollBarPolicy policy)
3472 {
3473 #ifndef KHTML_NO_SCROLLBARS
3474     d->vpolicy = policy;
3475     QScrollArea::setVerticalScrollBarPolicy(policy);
3476 #else
3477     Q_UNUSED(policy);
3478 #endif
3479 }
3480 
3481 void KHTMLView::setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy policy)
3482 {
3483 #ifndef KHTML_NO_SCROLLBARS
3484     d->hpolicy = policy;
3485     QScrollArea::setHorizontalScrollBarPolicy(policy);
3486 #else
3487     Q_UNUSED(policy);
3488 #endif
3489 }
3490 
3491 void KHTMLView::restoreScrollBar()
3492 {
3493     int ow = visibleWidth();
3494     QScrollArea::setVerticalScrollBarPolicy(d->vpolicy);
3495     if (visibleWidth() != ow) {
3496         layout();
3497     }
3498     d->prevScrollbarVisible = verticalScrollBar()->isVisible();
3499 }
3500 
3501 QStringList KHTMLView::formCompletionItems(const QString &name) const
3502 {
3503     if (!m_part->settings()->isFormCompletionEnabled()) {
3504         return QStringList();
3505     }
3506     if (!d->formCompletions) {
3507         d->formCompletions = new KConfig(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + "khtml/formcompletions");
3508     }
3509     return d->formCompletions->group("").readEntry(name, QStringList());
3510 }
3511 
3512 void KHTMLView::clearCompletionHistory(const QString &name)
3513 {
3514     if (!d->formCompletions) {
3515         d->formCompletions = new KConfig(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + "khtml/formcompletions");
3516     }
3517     d->formCompletions->group("").writeEntry(name, "");
3518     d->formCompletions->sync();
3519 }
3520 
3521 void KHTMLView::addFormCompletionItem(const QString &name, const QString &value)
3522 {
3523     if (!m_part->settings()->isFormCompletionEnabled()) {
3524         return;
3525     }
3526     // don't store values that are all numbers or just numbers with
3527     // dashes or spaces as those are likely credit card numbers or
3528     // something similar
3529     bool cc_number(true);
3530     for (int i = 0; i < value.length(); ++i) {
3531         QChar c(value[i]);
3532         if (!c.isNumber() && c != '-' && !c.isSpace()) {
3533             cc_number = false;
3534             break;
3535         }
3536     }
3537     if (cc_number) {
3538         return;
3539     }
3540     QStringList items = formCompletionItems(name);
3541     if (!items.contains(value)) {
3542         items.prepend(value);
3543     }
3544     while ((int)items.count() > m_part->settings()->maxFormCompletionItems()) {
3545         items.erase(items.isEmpty() ? items.end() : --items.end());
3546     }
3547     d->formCompletions->group("").writeEntry(name, items);
3548 }
3549 
3550 void KHTMLView::addNonPasswordStorableSite(const QString &host)
3551 {
3552     if (!d->formCompletions) {
3553         d->formCompletions = new KConfig(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + "khtml/formcompletions");
3554     }
3555 
3556     KConfigGroup cg(d->formCompletions, "NonPasswordStorableSites");
3557     QStringList sites = cg.readEntry("Sites", QStringList());
3558     sites.append(host);
3559     cg.writeEntry("Sites", sites);
3560     cg.sync();
3561 }
3562 
3563 void KHTMLView::delNonPasswordStorableSite(const QString &host)
3564 {
3565     if (!d->formCompletions) {
3566         d->formCompletions = new KConfig(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + "khtml/formcompletions");
3567     }
3568 
3569     KConfigGroup cg(d->formCompletions, "NonPasswordStorableSites");
3570     QStringList sites = cg.readEntry("Sites", QStringList());
3571     sites.removeOne(host);
3572     cg.writeEntry("Sites", sites);
3573     cg.sync();
3574 }
3575 
3576 bool KHTMLView::nonPasswordStorableSite(const QString &host) const
3577 {
3578     if (!d->formCompletions) {
3579         d->formCompletions = new KConfig(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + "khtml/formcompletions");
3580     }
3581     QStringList sites =  d->formCompletions->group("NonPasswordStorableSites").readEntry("Sites", QStringList());
3582     return (sites.indexOf(host) != -1);
3583 }
3584 
3585 // returns true if event should be swallowed
3586 bool KHTMLView::dispatchMouseEvent(int eventId, DOM::NodeImpl *targetNode,
3587                                    DOM::NodeImpl *targetNodeNonShared, bool cancelable,
3588                                    int detail, QMouseEvent *_mouse, bool setUnder,
3589                                    int mouseEventType, int orient)
3590 {
3591     // if the target node is a text node, dispatch on the parent node - rdar://4196646 (and #76948)
3592     if (targetNode && targetNode->isTextNode()) {
3593         targetNode = targetNode->parentNode();
3594     }
3595 
3596     if (d->underMouse) {
3597         d->underMouse->deref();
3598     }
3599     d->underMouse = targetNode;
3600     if (d->underMouse) {
3601         d->underMouse->ref();
3602     }
3603 
3604     if (d->underMouseNonShared) {
3605         d->underMouseNonShared->deref();
3606     }
3607     d->underMouseNonShared = targetNodeNonShared;
3608     if (d->underMouseNonShared) {
3609         d->underMouseNonShared->ref();
3610     }
3611 
3612     bool isWheelEvent = (mouseEventType == DOM::NodeImpl::MouseWheel);
3613 
3614     int exceptioncode = 0;
3615     int pageX = _mouse->x();
3616     int pageY = _mouse->y();
3617     revertTransforms(pageX, pageY);
3618     int clientX = pageX - contentsX();
3619     int clientY = pageY - contentsY();
3620     int screenX = _mouse->globalX();
3621     int screenY = _mouse->globalY();
3622     int button = -1;
3623     switch (_mouse->button()) {
3624     case Qt::LeftButton:
3625         button = 0;
3626         break;
3627     case Qt::MidButton:
3628         button = 1;
3629         break;
3630     case Qt::RightButton:
3631         button = 2;
3632         break;
3633     default:
3634         break;
3635     }
3636     if (d->accessKeysEnabled && d->accessKeysPreActivate && button != -1) {
3637         d->accessKeysPreActivate = false;
3638     }
3639 
3640     bool ctrlKey = (_mouse->modifiers() & Qt::ControlModifier);
3641     bool altKey = (_mouse->modifiers() & Qt::AltModifier);
3642     bool shiftKey = (_mouse->modifiers() & Qt::ShiftModifier);
3643     bool metaKey = (_mouse->modifiers() & Qt::MetaModifier);
3644 
3645     // mouseout/mouseover
3646     if (setUnder && d->oldUnderMouse != targetNode) {
3647         if (d->oldUnderMouse && d->oldUnderMouse->document() != m_part->xmlDocImpl()) {
3648             d->oldUnderMouse->deref();
3649             d->oldUnderMouse = nullptr;
3650         }
3651         // send mouseout event to the old node
3652         if (d->oldUnderMouse) {
3653             // send mouseout event to the old node
3654             MouseEventImpl *me = new MouseEventImpl(EventImpl::MOUSEOUT_EVENT,
3655                                                     true, true, m_part->xmlDocImpl()->defaultView(),
3656                                                     0, screenX, screenY, clientX, clientY, pageX, pageY,
3657                                                     ctrlKey, altKey, shiftKey, metaKey,
3658                                                     button, targetNode);
3659             me->ref();
3660             d->oldUnderMouse->dispatchEvent(me, exceptioncode, true);
3661             me->deref();
3662         }
3663         // send mouseover event to the new node
3664         if (targetNode) {
3665             MouseEventImpl *me = new MouseEventImpl(EventImpl::MOUSEOVER_EVENT,
3666                                                     true, true, m_part->xmlDocImpl()->defaultView(),
3667                                                     0, screenX, screenY, clientX, clientY, pageX, pageY,
3668                                                     ctrlKey, altKey, shiftKey, metaKey,
3669                                                     button, d->oldUnderMouse);
3670 
3671             me->ref();
3672             targetNode->dispatchEvent(me, exceptioncode, true);
3673             me->deref();
3674         }
3675         if (d->oldUnderMouse) {
3676             d->oldUnderMouse->deref();
3677         }
3678         d->oldUnderMouse = targetNode;
3679         if (d->oldUnderMouse) {
3680             d->oldUnderMouse->ref();
3681         }
3682     }
3683 
3684     bool swallowEvent = false;
3685 
3686     if (targetNode) {
3687         // if the target node is a disabled widget, we don't want any full-blown mouse events
3688         if (targetNode->isGenericFormElement()
3689                 && static_cast<HTMLGenericFormElementImpl *>(targetNode)->disabled()) {
3690             return true;
3691         }
3692 
3693         // send the actual event
3694         bool dblclick = (eventId == EventImpl::CLICK_EVENT &&
3695                          _mouse->type() == QEvent::MouseButtonDblClick);
3696         MouseEventImpl *me = new MouseEventImpl(static_cast<EventImpl::EventId>(eventId),
3697                                                 true, cancelable, m_part->xmlDocImpl()->defaultView(),
3698                                                 detail, screenX, screenY, clientX, clientY, pageX, pageY,
3699                                                 ctrlKey, altKey, shiftKey, metaKey,
3700                                                 button, nullptr, isWheelEvent ? nullptr : _mouse, dblclick,
3701                                                 isWheelEvent ? static_cast<MouseEventImpl::Orientation>(orient) : MouseEventImpl::ONone);
3702         me->ref();
3703         if (!d->m_mouseEventsTarget && RenderLayer::gScrollBar && eventId == EventImpl::MOUSEDOWN_EVENT)
3704             // button is pressed inside a layer scrollbar, so make it the target for future mousemove events until released
3705         {
3706             d->m_mouseEventsTarget = RenderLayer::gScrollBar;
3707         }
3708         if (d->m_mouseEventsTarget && qobject_cast<QScrollBar *>(d->m_mouseEventsTarget) &&
3709                 dynamic_cast<KHTMLWidget *>(static_cast<QWidget *>(d->m_mouseEventsTarget))) {
3710             // we have a sticky mouse event target and it is a layer's scrollbar. Forward events manually.
3711             // ### should use the dom
3712             KHTMLWidget *w = dynamic_cast<KHTMLWidget *>(static_cast<QWidget *>(d->m_mouseEventsTarget));
3713             QPoint p = w->m_kwp->absolutePos();
3714             QMouseEvent fw(_mouse->type(), QPoint(pageX, pageY) - p, _mouse->button(), _mouse->buttons(), _mouse->modifiers());
3715             static_cast<RenderWidget::EventPropagator *>(static_cast<QWidget *>(d->m_mouseEventsTarget))->sendEvent(&fw);
3716             if (_mouse->type() == QMouseEvent::MouseButtonPress && _mouse->button() == Qt::RightButton) {
3717                 QContextMenuEvent cme(QContextMenuEvent::Mouse, p);
3718                 static_cast<RenderWidget::EventPropagator *>(static_cast<QWidget *>(d->m_mouseEventsTarget))->sendEvent(&cme);
3719                 d->m_mouseEventsTarget = nullptr;
3720             }
3721             swallowEvent = true;
3722         } else {
3723             targetNode->dispatchEvent(me, exceptioncode, true);
3724             bool defaultHandled = me->defaultHandled();
3725             if (defaultHandled || me->defaultPrevented()) {
3726                 swallowEvent = true;
3727             }
3728         }
3729         if (eventId == EventImpl::MOUSEDOWN_EVENT && !me->defaultPrevented()) {
3730             // Focus should be shifted on mouse down, not on a click.  -dwh
3731             // Blur current focus node when a link/button is clicked; this
3732             // is expected by some sites that rely on onChange handlers running
3733             // from form fields before the button click is processed.
3734             DOM::NodeImpl *nodeImpl = targetNode;
3735             for (; nodeImpl && !nodeImpl->isFocusable(); nodeImpl = nodeImpl->parentNode()) {
3736             }
3737             if (nodeImpl && nodeImpl->isMouseFocusable()) {
3738                 m_part->xmlDocImpl()->setFocusNode(nodeImpl);
3739             } else if (!nodeImpl || !nodeImpl->focused()) {
3740                 m_part->xmlDocImpl()->setFocusNode(nullptr);
3741             }
3742         }
3743         me->deref();
3744     }
3745 
3746     return swallowEvent;
3747 }
3748 
3749 void KHTMLView::setIgnoreWheelEvents(bool e)
3750 {
3751     d->ignoreWheelEvents = e;
3752 }
3753 
3754 #ifndef QT_NO_WHEELEVENT
3755 
3756 void KHTMLView::wheelEvent(QWheelEvent *e)
3757 {
3758     // check if we should reset the state of the indicator describing if
3759     // we are currently scrolling the view as a result of wheel events
3760     if (d->scrollingFromWheel != QPoint(-1, -1) && d->scrollingFromWheel != QCursor::pos()) {
3761         d->scrollingFromWheel = d->scrollingFromWheelTimerId ? QCursor::pos() : QPoint(-1, -1);
3762     }
3763 
3764     if (d->accessKeysEnabled && d->accessKeysPreActivate) {
3765         d->accessKeysPreActivate = false;
3766     }
3767 
3768     if ((e->modifiers() & Qt::ControlModifier) == Qt::ControlModifier) {
3769         emit zoomView(- e->delta());
3770         e->accept();
3771     } else if (d->firstLayoutPending) {
3772         e->accept();
3773     } else if (!m_kwp->isRedirected() &&
3774                ((e->orientation() == Qt::Vertical &&
3775                  ((d->ignoreWheelEvents && !verticalScrollBar()->isVisible())
3776                   || (e->delta() > 0 && contentsY() <= 0)
3777                   || (e->delta() < 0 && contentsY() >= contentsHeight() - visibleHeight())))
3778                 ||
3779                 (e->orientation() == Qt::Horizontal &&
3780                  ((d->ignoreWheelEvents && !horizontalScrollBar()->isVisible())
3781                   || (e->delta() > 0 && contentsX() <= 0)
3782                   || (e->delta() < 0 && contentsX() >= contentsWidth() - visibleWidth()))))
3783                && m_part->parentPart()) {
3784         if (m_part->parentPart()->view()) {
3785             m_part->parentPart()->view()->wheelEvent(e);
3786         }
3787         e->ignore();
3788     } else {
3789         int xm = e->x();
3790         int ym = e->y();
3791         revertTransforms(xm, ym);
3792 
3793         DOM::NodeImpl::MouseEvent mev(e->buttons(), DOM::NodeImpl::MouseWheel);
3794         m_part->xmlDocImpl()->prepareMouseEvent(false, xm, ym, &mev);
3795 
3796         MouseEventImpl::Orientation o = MouseEventImpl::OVertical;
3797         if (e->orientation() == Qt::Horizontal) {
3798             o = MouseEventImpl::OHorizontal;
3799         }
3800 
3801         QMouseEvent _mouse(QEvent::MouseMove, e->pos(), Qt::NoButton, e->buttons(), e->modifiers());
3802         bool swallow = dispatchMouseEvent(EventImpl::KHTML_MOUSEWHEEL_EVENT, mev.innerNode.handle(), mev.innerNonSharedNode.handle(),
3803                                           true, -e->delta() / 40, &_mouse, true, DOM::NodeImpl::MouseWheel, o);
3804 
3805         if (swallow) {
3806             return;
3807         }
3808 
3809         d->scrollBarMoved = true;
3810         d->scrollingFromWheel = QCursor::pos();
3811         if (d->smoothScrollMode != SSMDisabled) {
3812             d->shouldSmoothScroll = true;
3813         }
3814         if (d->scrollingFromWheelTimerId) {
3815             killTimer(d->scrollingFromWheelTimerId);
3816         }
3817         d->scrollingFromWheelTimerId = startTimer(400);
3818 
3819         if (m_part->parentPart()) {
3820             // don't propagate if we are a sub-frame and our scrollbars are already at end of range
3821             bool h = (static_cast<QWheelEvent *>(e)->orientation() == Qt::Horizontal);
3822             bool d = (static_cast<QWheelEvent *>(e)->delta() < 0);
3823             QScrollBar *hsb = horizontalScrollBar();
3824             QScrollBar *vsb = verticalScrollBar();
3825             if ((h && ((d && hsb->value() == hsb->maximum()) || (!d && hsb->value() == hsb->minimum()))) ||
3826                     (!h && ((d && vsb->value() == vsb->maximum()) || (!d && vsb->value() == vsb->minimum())))) {
3827                 e->accept();
3828                 return;
3829             }
3830         }
3831         QScrollArea::wheelEvent(e);
3832     }
3833 
3834 }
3835 #endif
3836 
3837 void KHTMLView::dragEnterEvent(QDragEnterEvent *ev)
3838 {
3839     // Still overridden for BC reasons only...
3840     QScrollArea::dragEnterEvent(ev);
3841 }
3842 
3843 void KHTMLView::dropEvent(QDropEvent *ev)
3844 {
3845     // Still overridden for BC reasons only...
3846     QScrollArea::dropEvent(ev);
3847 }
3848 
3849 void KHTMLView::focusInEvent(QFocusEvent *e)
3850 {
3851     DOM::NodeImpl *fn = m_part->xmlDocImpl() ? m_part->xmlDocImpl()->focusNode() : nullptr;
3852     if (fn && fn->renderer() && fn->renderer()->isWidget() &&
3853             (e->reason() != Qt::MouseFocusReason) &&
3854             static_cast<khtml::RenderWidget *>(fn->renderer())->widget()) {
3855         static_cast<khtml::RenderWidget *>(fn->renderer())->widget()->setFocus();
3856     }
3857     m_part->setSelectionVisible();
3858     QScrollArea::focusInEvent(e);
3859 }
3860 
3861 void KHTMLView::focusOutEvent(QFocusEvent *e)
3862 {
3863     if (m_part) {
3864         m_part->stopAutoScroll();
3865         m_part->setSelectionVisible(false);
3866     }
3867 
3868     if (d->cursorIconWidget) {
3869         d->cursorIconWidget->hide();
3870     }
3871 
3872     QScrollArea::focusOutEvent(e);
3873 }
3874 
3875 void KHTMLView::scrollContentsBy(int dx, int dy)
3876 {
3877     if (!dx && !dy) {
3878         return;
3879     }
3880 
3881     if (!d->firstLayoutPending && !d->complete && m_part->xmlDocImpl() &&
3882             d->layoutSchedulingEnabled) {
3883         // contents scroll while we are not complete: we need to check our layout *now*
3884         khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer());
3885         if (root && root->needsLayout()) {
3886             unscheduleRelayout();
3887             layout();
3888         }
3889     }
3890 
3891     if (d->shouldSmoothScroll && d->smoothScrollMode != SSMDisabled && m_part->xmlDocImpl() &&
3892             m_part->xmlDocImpl()->renderer() && (d->smoothScrollMode != SSMWhenEfficient || d->smoothScrollMissedDeadlines != sWayTooMany)) {
3893 
3894         bool doSmoothScroll = (!d->staticWidget || d->smoothScrollMode == SSMEnabled);
3895 
3896         int numStaticPixels = 0;
3897         QRegion r = static_cast<RenderCanvas *>(m_part->xmlDocImpl()->renderer())->staticRegion();
3898 
3899         // only do smooth scrolling if static region is relatively small
3900         if (!doSmoothScroll && d->staticWidget == KHTMLViewPrivate::SBPartial && r.rects().size() <= 10) {
3901             foreach (const QRect &rr, r.rects()) {
3902                 numStaticPixels += rr.width() * rr.height();
3903             }
3904             if ((numStaticPixels < sSmoothScrollMinStaticPixels) || (numStaticPixels * 8 < visibleWidth()*visibleHeight())) {
3905                 doSmoothScroll = true;
3906             }
3907         }
3908         if (doSmoothScroll) {
3909             setupSmoothScrolling(dx, dy);
3910             return;
3911         }
3912     }
3913 
3914     if (underMouse() && QToolTip::isVisible()) {
3915         QToolTip::hideText();
3916     }
3917 
3918     if (!d->scrollingSelf) {
3919         d->scrollBarMoved = true;
3920         d->contentsMoving = true;
3921         // ensure quick reset of contentsMoving flag
3922         scheduleRepaint(0, 0, 0, 0);
3923     }
3924 
3925     if (m_part->xmlDocImpl() && m_part->xmlDocImpl()->documentElement()) {
3926         m_part->xmlDocImpl()->documentElement()->dispatchHTMLEvent(EventImpl::SCROLL_EVENT, true, false);
3927     }
3928 
3929     if (QApplication::isRightToLeft()) {
3930         dx = -dx;
3931     }
3932 
3933     if (!d->smoothScrolling) {
3934         d->updateContentsXY();
3935     } else {
3936         d->contentsX -= dx;
3937         d->contentsY -= dy;
3938     }
3939     if (widget()->pos() != QPoint(0, 0)) {
3940         // qCDebug(KHTML_LOG) << "Static widget wasn't positioned at (0,0). This should NOT happen. Please report this event to developers.";
3941         widget()->move(0, 0);
3942     }
3943 
3944     QWidget *w = widget();
3945     QPoint off;
3946     if (m_kwp->isRedirected()) {
3947         // This is a redirected sub frame. Translate to root view context
3948         KHTMLView *v = m_kwp->rootViewPos(off);
3949         if (v) {
3950             w = v->widget();
3951         }
3952         off = viewport()->mapTo(this, off);
3953     }
3954 
3955     if (d->staticWidget) {
3956 
3957         // now remove from view the external widgets that must have completely
3958         // disappeared after dx/dy scroll delta is effective
3959         if (!d->visibleWidgets.isEmpty()) {
3960             checkExternalWidgetsPosition();
3961         }
3962 
3963         if (d->staticWidget == KHTMLViewPrivate::SBPartial
3964                 && m_part->xmlDocImpl() && m_part->xmlDocImpl()->renderer()) {
3965             // static objects might be selectively repainted, like stones in flowing water
3966             QRegion r = static_cast<RenderCanvas *>(m_part->xmlDocImpl()->renderer())->staticRegion();
3967             r.translate(-contentsX(), -contentsY());
3968             QVector<QRect> ar = r.rects();
3969 
3970             for (int i = 0; i < ar.size(); ++i) {
3971                 widget()->update(ar[i]);
3972             }
3973             r = QRegion(QRect(0, 0, visibleWidth(), visibleHeight())) - r;
3974             ar = r.rects();
3975             for (int i = 0; i < ar.size(); ++i) {
3976                 w->scroll(dx, dy, ar[i].translated(off));
3977             }
3978             d->scrollExternalWidgets(dx, dy);
3979         } else {
3980             // we can't avoid a full update
3981             widget()->update();
3982         }
3983         if (d->accessKeysActivated) {
3984             d->scrollAccessKeys(dx, dy);
3985         }
3986 
3987         return;
3988     }
3989 
3990     if (m_kwp->isRedirected()) {
3991         const QRect rect(off.x(), off.y(), visibleWidth() * d->zoomLevel / 100, visibleHeight() * d->zoomLevel / 100);
3992         w->scroll(dx, dy, rect);
3993         if (d->zoomLevel != 100) {
3994             w->update(rect); // without this update we are getting bad rendering when an iframe is zoomed in
3995         }
3996     }  else {
3997         widget()->scroll(dx, dy, widget()->rect() & viewport()->rect());
3998     }
3999 
4000     d->scrollExternalWidgets(dx, dy);
4001     if (d->accessKeysActivated) {
4002         d->scrollAccessKeys(dx, dy);
4003     }
4004 }
4005 
4006 void KHTMLView::setupSmoothScrolling(int dx, int dy)
4007 {
4008     // old or minimum speed
4009     int ddx = qMax(d->steps ? abs(d->dx) / d->steps : 0, 3);
4010     int ddy = qMax(d->steps ? abs(d->dy) / d->steps : 0, 3);
4011 
4012     // full scroll is remaining scroll plus new scroll
4013     d->dx = d->dx + dx;
4014     d->dy = d->dy + dy;
4015 
4016     if (d->dx == 0 && d->dy == 0) {
4017         d->stopScrolling();
4018         return;
4019     }
4020 
4021     d->steps = (sSmoothScrollTime - 1) / sSmoothScrollTick + 1;
4022 
4023     if (qMax(abs(d->dx), abs(d->dy)) / d->steps < qMax(ddx, ddy)) {
4024         // Don't move slower than average 4px/step in minimum one direction
4025         // This means fewer than normal steps
4026         d->steps = qMax((abs(d->dx) + ddx - 1) / ddx, (abs(d->dy) + ddy - 1) / ddy);
4027         if (d->steps < 1) {
4028             d->steps = 1;
4029         }
4030     }
4031 
4032     d->smoothScrollStopwatch.start();
4033     if (!d->smoothScrolling) {
4034         d->startScrolling();
4035         scrollTick();
4036     }
4037 }
4038 
4039 void KHTMLView::scrollTick()
4040 {
4041     if (d->dx == 0 && d->dy == 0) {
4042         d->stopScrolling();
4043         return;
4044     }
4045 
4046     if (d->steps < 1) {
4047         d->steps = 1;
4048     }
4049     int takesteps = d->smoothScrollStopwatch.restart() / sSmoothScrollTick;
4050     int scroll_x = 0;
4051     int scroll_y = 0;
4052     if (takesteps < 1) {
4053         takesteps = 1;
4054     }
4055     if (takesteps > d->steps) {
4056         takesteps = d->steps;
4057     }
4058     for (int i = 0; i < takesteps; i++) {
4059         int ddx = (d->dx / (d->steps + 1)) * 2;
4060         int ddy = (d->dy / (d->steps + 1)) * 2;
4061 
4062         // limit step to requested scrolling distance
4063         if (abs(ddx) > abs(d->dx)) {
4064             ddx = d->dx;
4065         }
4066         if (abs(ddy) > abs(d->dy)) {
4067             ddy = d->dy;
4068         }
4069 
4070         // update remaining scroll
4071         d->dx -= ddx;
4072         d->dy -= ddy;
4073         scroll_x += ddx;
4074         scroll_y += ddy;
4075         d->steps--;
4076     }
4077 
4078     d->shouldSmoothScroll = false;
4079     scrollContentsBy(scroll_x, scroll_y);
4080 
4081     if (takesteps < 2) {
4082         d->smoothScrollMissedDeadlines = 0;
4083     } else {
4084         if (d->smoothScrollMissedDeadlines != sWayTooMany &&
4085                 (!m_part->xmlDocImpl() || !m_part->xmlDocImpl()->parsing())) {
4086             d->smoothScrollMissedDeadlines++;
4087             if (d->smoothScrollMissedDeadlines >= sMaxMissedDeadlines) {
4088                 // we missed many deadlines in a row!
4089                 // time to signal we had enough..
4090                 d->smoothScrollMissedDeadlines = sWayTooMany;
4091             }
4092         }
4093     }
4094 }
4095 
4096 void KHTMLView::addChild(QWidget *child, int x, int y)
4097 {
4098     if (!child) {
4099         return;
4100     }
4101 
4102     if (child->parent() != widget()) {
4103         child->setParent(widget());
4104     }
4105 
4106     // ### handle pseudo-zooming of non-redirected widgets (e.g. just resize'em)
4107 
4108     child->move(x - contentsX(), y - contentsY());
4109 }
4110 
4111 void KHTMLView::timerEvent(QTimerEvent *e)
4112 {
4113 //    qCDebug(KHTML_LOG) << "timer event " << e->timerId();
4114     if (e->timerId() == d->scrollTimerId) {
4115         if (d->scrollSuspended) {
4116             return;
4117         }
4118         switch (d->scrollDirection) {
4119         case KHTMLViewPrivate::ScrollDown:
4120             if (contentsY() + visibleHeight() >= contentsHeight()) {
4121                 d->newScrollTimer(this, 0);
4122             } else {
4123                 verticalScrollBar()->setValue(verticalScrollBar()->value() + d->scrollBy);
4124             }
4125             break;
4126         case KHTMLViewPrivate::ScrollUp:
4127             if (contentsY() <= 0) {
4128                 d->newScrollTimer(this, 0);
4129             } else {
4130                 verticalScrollBar()->setValue(verticalScrollBar()->value() - d->scrollBy);
4131             }
4132             break;
4133         case KHTMLViewPrivate::ScrollRight:
4134             if (contentsX() + visibleWidth() >= contentsWidth()) {
4135                 d->newScrollTimer(this, 0);
4136             } else {
4137                 horizontalScrollBar()->setValue(horizontalScrollBar()->value() + d->scrollBy);
4138             }
4139             break;
4140         case KHTMLViewPrivate::ScrollLeft:
4141             if (contentsX() <= 0) {
4142                 d->newScrollTimer(this, 0);
4143             } else {
4144                 horizontalScrollBar()->setValue(horizontalScrollBar()->value() - d->scrollBy);
4145             }
4146             break;
4147         }
4148         return;
4149     } else if (e->timerId() == d->scrollingFromWheelTimerId) {
4150         killTimer(d->scrollingFromWheelTimerId);
4151         d->scrollingFromWheelTimerId = 0;
4152     } else if (e->timerId() == d->layoutTimerId) {
4153         if (d->firstLayoutPending && d->layoutAttemptCounter < 4
4154                 && (!m_part->xmlDocImpl() || !m_part->xmlDocImpl()->readyForLayout())) {
4155             d->layoutAttemptCounter++;
4156             killTimer(d->layoutTimerId);
4157             d->layoutTimerId = 0;
4158             scheduleRelayout();
4159             return;
4160         }
4161         layout();
4162         d->scheduledLayoutCounter++;
4163         if (d->firstLayoutPending) {
4164             d->firstLayoutPending = false;
4165             verticalScrollBar()->setEnabled(true);
4166             horizontalScrollBar()->setEnabled(true);
4167         }
4168     }
4169 
4170     d->contentsMoving = false;
4171     if (m_part->xmlDocImpl()) {
4172         DOM::DocumentImpl *document = m_part->xmlDocImpl();
4173         khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(document->renderer());
4174 
4175         if (root && root->needsLayout()) {
4176             if (d->repaintTimerId) {
4177                 killTimer(d->repaintTimerId);
4178             }
4179             d->repaintTimerId = 0;
4180             scheduleRelayout();
4181             return;
4182         }
4183     }
4184 
4185     if (d->repaintTimerId) {
4186         killTimer(d->repaintTimerId);
4187     }
4188     d->repaintTimerId = 0;
4189 
4190     QRect updateRegion;
4191     const QVector<QRect> rects = d->updateRegion.rects();
4192 
4193     d->updateRegion = QRegion();
4194 
4195     if (rects.size()) {
4196         updateRegion = rects[0];
4197     }
4198 
4199     for (int i = 1; i < rects.size(); ++i) {
4200         QRect newRegion = updateRegion.united(rects[i]);
4201         if (2 * newRegion.height() > 3 * updateRegion.height()) {
4202             repaintContents(updateRegion);
4203             updateRegion = rects[i];
4204         } else {
4205             updateRegion = newRegion;
4206         }
4207     }
4208 
4209     if (!updateRegion.isNull()) {
4210         repaintContents(updateRegion);
4211     }
4212 
4213     // As widgets can only be accurately positioned during painting, every layout might
4214     // dissociate a widget from its RenderWidget. E.g: if a RenderWidget was visible before layout, but the layout
4215     // pushed it out of the viewport, it will not be repainted, and consequently it's associated widget won't be repositioned.
4216     // Thus we need to check each supposedly 'visible' widget at the end of layout, and remove it in case it's no more in sight.
4217 
4218     if (d->dirtyLayout && !d->visibleWidgets.isEmpty()) {
4219         checkExternalWidgetsPosition();
4220     }
4221 
4222     d->dirtyLayout = false;
4223 
4224     emit repaintAccessKeys();
4225     if (d->emitCompletedAfterRepaint) {
4226         bool full = d->emitCompletedAfterRepaint == KHTMLViewPrivate::CSFull;
4227         d->emitCompletedAfterRepaint = KHTMLViewPrivate::CSNone;
4228         if (full) {
4229             emit m_part->completed();
4230         } else {
4231             emit m_part->completed(true);
4232         }
4233     }
4234 }
4235 
4236 void KHTMLView::checkExternalWidgetsPosition()
4237 {
4238     QWidget *w;
4239     QRect visibleRect(contentsX(), contentsY(), visibleWidth(), visibleHeight());
4240     QList<RenderWidget *> toRemove;
4241     QHashIterator<void *, QWidget *> it(d->visibleWidgets);
4242     while (it.hasNext()) {
4243         int xp = 0, yp = 0;
4244         it.next();
4245         RenderWidget *rw = static_cast<RenderWidget *>(it.key());
4246         if (!rw->absolutePosition(xp, yp) ||
4247                 !visibleRect.intersects(QRect(xp, yp, it.value()->width(), it.value()->height()))) {
4248             toRemove.append(rw);
4249         }
4250     }
4251     foreach (RenderWidget *r, toRemove)
4252         if ((w = d->visibleWidgets.take(r))) {
4253             w->move(0, -500000);
4254         }
4255 }
4256 
4257 void KHTMLView::scheduleRelayout(khtml::RenderObject * /*clippedObj*/)
4258 {
4259     if (!d->layoutSchedulingEnabled || d->layoutTimerId) {
4260         return;
4261     }
4262 
4263     int time = 0;
4264     if (d->firstLayoutPending) {
4265         // Any repaint happening while we have no content blanks the viewport ("white flash").
4266         // Hence the need to delay the first layout as much as we can.
4267         // Only if the document gets stuck for too long in incomplete state will we allow the blanking.
4268         time = d->layoutAttemptCounter ?
4269                sLayoutAttemptDelay + sLayoutAttemptIncrement * d->layoutAttemptCounter : sFirstLayoutDelay;
4270     } else if (m_part->xmlDocImpl() && m_part->xmlDocImpl()->parsing()) {
4271         // Delay between successive layouts in parsing mode.
4272         // Increment reflects the decaying importance of visual feedback over time.
4273         time = qMin(2000, sParsingLayoutsInterval + d->scheduledLayoutCounter * sParsingLayoutsIncrement);
4274     }
4275     d->layoutTimerId = startTimer(time);
4276 }
4277 
4278 void KHTMLView::unscheduleRelayout()
4279 {
4280     if (!d->layoutTimerId) {
4281         return;
4282     }
4283 
4284     killTimer(d->layoutTimerId);
4285     d->layoutTimerId = 0;
4286 }
4287 
4288 void KHTMLView::unscheduleRepaint()
4289 {
4290     if (!d->repaintTimerId) {
4291         return;
4292     }
4293 
4294     killTimer(d->repaintTimerId);
4295     d->repaintTimerId = 0;
4296 }
4297 
4298 void KHTMLView::scheduleRepaint(int x, int y, int w, int h, bool asap)
4299 {
4300     bool parsing = !m_part->xmlDocImpl() || m_part->xmlDocImpl()->parsing();
4301 
4302 //     qCDebug(KHTML_LOG) << "parsing " << parsing;
4303 //     qCDebug(KHTML_LOG) << "complete " << d->complete;
4304 
4305     int time = parsing && !d->firstLayoutPending ? 150 : (!asap ? (!d->complete ? 80 : 20) : 0);
4306 
4307 #ifdef DEBUG_FLICKER
4308     QPainter p;
4309     p.begin(viewport());
4310 
4311     int vx, vy;
4312     contentsToViewport(x, y, vx, vy);
4313     p.fillRect(vx, vy, w, h, Qt::red);
4314     p.end();
4315 #endif
4316 
4317     d->updateRegion = d->updateRegion.united(QRect(x, y, w, h));
4318 
4319     if (asap && !parsing) {
4320         unscheduleRepaint();
4321     }
4322 
4323     if (!d->repaintTimerId) {
4324         d->repaintTimerId = startTimer(time);
4325     }
4326 
4327 //     qCDebug(KHTML_LOG) << "starting timer " << time;
4328 }
4329 
4330 void KHTMLView::complete(bool pendingAction)
4331 {
4332 //     qCDebug(KHTML_LOG) << "KHTMLView::complete()";
4333 
4334     d->complete = true;
4335 
4336     // is there a relayout pending?
4337     if (d->layoutTimerId) {
4338 //         qCDebug(KHTML_LOG) << "requesting relayout now";
4339         // do it now
4340         killTimer(d->layoutTimerId);
4341         d->layoutTimerId = startTimer(0);
4342         d->emitCompletedAfterRepaint = pendingAction ?
4343                                        KHTMLViewPrivate::CSActionPending : KHTMLViewPrivate::CSFull;
4344     }
4345 
4346     // is there a repaint pending?
4347     if (d->repaintTimerId) {
4348 //         qCDebug(KHTML_LOG) << "requesting repaint now";
4349         // do it now
4350         killTimer(d->repaintTimerId);
4351         d->repaintTimerId = startTimer(0);
4352         d->emitCompletedAfterRepaint = pendingAction ?
4353                                        KHTMLViewPrivate::CSActionPending : KHTMLViewPrivate::CSFull;
4354     }
4355 
4356     if (!d->emitCompletedAfterRepaint) {
4357         if (!pendingAction) {
4358             emit m_part->completed();
4359         } else {
4360             emit m_part->completed(true);
4361         }
4362     }
4363 
4364 }
4365 
4366 void KHTMLView::updateScrollBars()
4367 {
4368     const QWidget *view = widget();
4369     if (!view) {
4370         return;
4371     }
4372 
4373     QSize p = viewport()->size();
4374     QSize m = maximumViewportSize();
4375 
4376     if (m.expandedTo(view->size()) == m) {
4377         p = m;    // no scroll bars needed
4378     }
4379 
4380     QSize v = view->size();
4381     horizontalScrollBar()->setRange(0, v.width() - p.width());
4382     horizontalScrollBar()->setPageStep(p.width());
4383     verticalScrollBar()->setRange(0, v.height() - p.height());
4384     verticalScrollBar()->setPageStep(p.height());
4385     if (!d->smoothScrolling) {
4386         d->updateContentsXY();
4387     }
4388 }
4389 
4390 void KHTMLView::slotMouseScrollTimer()
4391 {
4392     horizontalScrollBar()->setValue(horizontalScrollBar()->value() + d->m_mouseScroll_byX);
4393     verticalScrollBar()->setValue(verticalScrollBar()->value() + d->m_mouseScroll_byY);
4394 }
4395 
4396 static DOM::Position positionOfLineBoundary(const DOM::Position &pos, bool toEnd)
4397 {
4398     Selection sel = pos;
4399     sel.expandUsingGranularity(Selection::LINE);
4400     return toEnd ? sel.end() : sel.start();
4401 }
4402 
4403 inline static DOM::Position positionOfLineBegin(const DOM::Position &pos)
4404 {
4405     return positionOfLineBoundary(pos, false);
4406 }
4407 
4408 inline static DOM::Position positionOfLineEnd(const DOM::Position &pos)
4409 {
4410     return positionOfLineBoundary(pos, true);
4411 }
4412 
4413 bool KHTMLView::caretKeyPressEvent(QKeyEvent *_ke)
4414 {
4415     EditorContext *ec = &m_part->d->editor_context;
4416     Selection &caret = ec->m_selection;
4417     Position old_pos = caret.caretPos();
4418     Position pos = old_pos;
4419     bool recalcXPos = true;
4420     bool handled = true;
4421 
4422     bool ctrl = _ke->modifiers() & Qt::ControlModifier;
4423     bool shift = _ke->modifiers() & Qt::ShiftModifier;
4424 
4425     switch (_ke->key()) {
4426 
4427     // -- Navigational keys
4428     case Qt::Key_Down:
4429         pos = old_pos.nextLinePosition(caret.xPosForVerticalArrowNavigation(Selection::EXTENT));
4430         recalcXPos = false;
4431         break;
4432 
4433     case Qt::Key_Up:
4434         pos = old_pos.previousLinePosition(caret.xPosForVerticalArrowNavigation(Selection::EXTENT));
4435         recalcXPos = false;
4436         break;
4437 
4438     case Qt::Key_Left:
4439         pos = ctrl ? old_pos.previousWordPosition() : old_pos.previousCharacterPosition();
4440         break;
4441 
4442     case Qt::Key_Right:
4443         pos = ctrl ? old_pos.nextWordPosition() : old_pos.nextCharacterPosition();
4444         break;
4445 
4446     case Qt::Key_PageDown:
4447 //       moveCaretNextPage(); ###
4448         break;
4449 
4450     case Qt::Key_PageUp:
4451 //       moveCaretPrevPage(); ###
4452         break;
4453 
4454     case Qt::Key_Home:
4455         if (ctrl)
4456             /*moveCaretToDocumentBoundary(false)*/; // ###
4457         else {
4458             pos = positionOfLineBegin(old_pos);
4459         }
4460         break;
4461 
4462     case Qt::Key_End:
4463         if (ctrl)
4464             /*moveCaretToDocumentBoundary(true)*/; // ###
4465         else {
4466             pos = positionOfLineEnd(old_pos);
4467         }
4468         break;
4469 
4470     default:
4471         handled = false;
4472 
4473     }/*end switch*/
4474 
4475     if (pos != old_pos) {
4476         m_part->clearCaretRectIfNeeded();
4477 
4478         caret.moveTo(shift ? caret.nonCaretPos() : pos, pos);
4479         int old_x = caret.xPosForVerticalArrowNavigation(Selection::CARETPOS);
4480 
4481         m_part->selectionLayoutChanged();
4482 
4483         // restore old x-position to prevent recalculation
4484         if (!recalcXPos) {
4485             m_part->d->editor_context.m_xPosForVerticalArrowNavigation = old_x;
4486         }
4487 
4488         m_part->emitCaretPositionChanged(pos);
4489         // ### check when to emit it
4490         m_part->notifySelectionChanged();
4491 
4492     }
4493 
4494     if (handled) {
4495         _ke->accept();
4496     }
4497     return handled;
4498 }
4499 
4500 #undef DEBUG_CARETMODE
4501 
4502 #include "moc_khtmlview.cpp"