1 /* This file is part of the KDE project
2 *
3 * Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
4 * 1999 Lars Knoll <knoll@kde.org>
5 * 1999 Antti Koivisto <koivisto@kde.org>
6 * 2000-2004 Dirk Mueller <mueller@kde.org>
7 * 2003 Leo Savernik <l.savernik@aon.at>
8 * 2003-2008 Apple Computer, Inc.
9 * 2008 Allan Sandfeld Jensen <kde@carewolf.com>
10 * 2006-2008 Germain Garand <germain@ebooksfrance.org>
11 *
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Library General Public
14 * License as published by the Free Software Foundation; either
15 * version 2 of the License, or (at your option) any later version.
16 *
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Library General Public License for more details.
21 *
22 * You should have received a copy of the GNU Library General Public License
23 * along with this library; see the file COPYING.LIB. If not, write to
24 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25 * Boston, MA 02110-1301, USA.
26 */
27
28 #include "khtmlview.h"
29
30 #include "khtml_part.h"
31 #include "khtml_events.h"
32 #include <config-khtml.h>
33 #if HAVE_X11
34 #include <qx11info_x11.h>
35 #endif
36
37 #include "html/html_documentimpl.h"
38 #include "html/html_inlineimpl.h"
39 #include "html/html_formimpl.h"
40 #include "html/htmltokenizer.h"
41 #include "editing/editor.h"
42 #include "rendering/render_arena.h"
43 #include "rendering/render_canvas.h"
44 #include "rendering/render_frames.h"
45 #include "rendering/render_replaced.h"
46 #include "rendering/render_form.h"
47 #include "rendering/render_layer.h"
48 #include "rendering/render_line.h"
49 #include "rendering/render_table.h"
50 // removeme
51 #define protected public
52 #include "rendering/render_text.h"
53 #undef protected
54 #include "xml/dom2_eventsimpl.h"
55 #include "css/cssstyleselector.h"
56 #include "misc/loader.h"
57 #include "khtml_settings.h"
58 #include "khtml_printsettings.h"
59
60 #include "khtmlpart_p.h"
61
62 #include <kcursor.h>
63 #include "khtml_debug.h"
64 #include <kiconloader.h>
65 #include <knotification.h>
66 #include <kconfig.h>
67 #include <../khtml_version.h>
68
69 #include <kstringhandler.h>
70 #include <kconfiggroup.h>
71 #include <ksharedconfig.h>
72
73 #include <QBitmap>
74 #include <QDialog>
75 #include <QDialogButtonBox>
76 #include <QLabel>
77 #include <QKeyEvent>
78 #include <QObject>
79 #include <QPainter>
80 #include <QHash>
81 #include <QToolTip>
82 #include <QString>
83 #include <QTextDocument>
84 #include <QTimer>
85 #include <QAbstractEventDispatcher>
86 #include <QVector>
87 #include <QAbstractScrollArea>
88 #include <QPrinter>
89 #include <QPrintDialog>
90 #include <qstandardpaths.h>
91
92 //#define DEBUG_FLICKER
93
94 #include <limits.h>
95 #if HAVE_X11
96 #include <X11/Xlib.h>
97 #include <fixx11h.h>
98 #elif defined(Q_OS_WIN)
99 #include <windows.h>
100 #endif
101
102 #if 0
103 namespace khtml
104 {
105 void dumpLineBoxes(RenderFlow *flow);
106 }
107 #endif
108
109 using namespace DOM;
110 using namespace khtml;
111
112 #ifndef NDEBUG
113 static const int sFirstLayoutDelay = 520;
114 static const int sParsingLayoutsInterval = 380;
115 static const int sLayoutAttemptDelay = 300;
116 #else
117 static const int sFirstLayoutDelay = 280;
118 static const int sParsingLayoutsInterval = 320;
119 static const int sLayoutAttemptDelay = 200;
120 #endif
121 static const int sLayoutAttemptIncrement = 20;
122 static const int sParsingLayoutsIncrement = 60;
123
124 static const int sSmoothScrollTime = 128;
125 static const int sSmoothScrollTick = 16;
126 static const int sSmoothScrollMinStaticPixels = 320 * 200;
127
128 static const int sMaxMissedDeadlines = 12;
129 static const int sWayTooMany = -1;
130
131 class KHTMLViewPrivate
132 {
133 friend class KHTMLView;
134 public:
135
136 enum PseudoFocusNodes {
137 PFNone,
138 PFTop,
139 PFBottom
140 };
141
142 enum StaticBackgroundState {
143 SBNone = 0,
144 SBPartial,
145 SBFull
146 };
147
148 enum CompletedState {
149 CSNone = 0,
150 CSFull,
151 CSActionPending
152 };
153
KHTMLViewPrivate(KHTMLView * v)154 KHTMLViewPrivate(KHTMLView *v)
155 : underMouse(nullptr), underMouseNonShared(nullptr), oldUnderMouse(nullptr)
156 {
157 postponed_autorepeat = nullptr;
158 scrollingFromWheelTimerId = 0;
159 smoothScrollMode = KHTMLView::SSMWhenEfficient;
160
161 reset();
162 vpolicy = Qt::ScrollBarAsNeeded;
163 hpolicy = Qt::ScrollBarAsNeeded;
164 formCompletions = nullptr;
165 prevScrollbarVisible = true;
166
167 possibleTripleClick = false;
168 emitCompletedAfterRepaint = CSNone;
169 cursorIconWidget = nullptr;
170 cursorIconType = KHTMLView::LINK_NORMAL;
171 m_mouseScrollTimer = nullptr;
172 m_mouseScrollIndicator = nullptr;
173 contentsX = 0;
174 contentsY = 0;
175 view = v;
176 }
~KHTMLViewPrivate()177 ~KHTMLViewPrivate()
178 {
179 delete formCompletions;
180 delete postponed_autorepeat;
181 if (underMouse) {
182 underMouse->deref();
183 }
184 if (underMouseNonShared) {
185 underMouseNonShared->deref();
186 }
187 if (oldUnderMouse) {
188 oldUnderMouse->deref();
189 }
190
191 delete cursorIconWidget;
192 delete m_mouseScrollTimer;
193 delete m_mouseScrollIndicator;
194 }
reset()195 void reset()
196 {
197 if (underMouse) {
198 underMouse->deref();
199 }
200 underMouse = nullptr;
201 if (underMouseNonShared) {
202 underMouseNonShared->deref();
203 }
204 underMouseNonShared = nullptr;
205 if (oldUnderMouse) {
206 oldUnderMouse->deref();
207 }
208 oldUnderMouse = nullptr;
209 linkPressed = false;
210 staticWidget = SBNone;
211 fixedObjectsCount = 0;
212 staticObjectsCount = 0;
213 tabMovePending = false;
214 lastTabbingDirection = true;
215 pseudoFocusNode = PFNone;
216 zoomLevel = 100;
217 #ifndef KHTML_NO_SCROLLBARS
218 //We don't turn off the toolbars here
219 //since if the user turns them
220 //off, then chances are they want them turned
221 //off always - even after a reset.
222 #else
223 vpolicy = ScrollBarAlwaysOff;
224 hpolicy = ScrollBarAlwaysOff;
225 #endif
226 scrollBarMoved = false;
227 contentsMoving = false;
228 ignoreWheelEvents = false;
229 scrollingFromWheel = QPoint(-1, -1);
230 borderX = 30;
231 borderY = 30;
232 steps = 0;
233 dx = dy = 0;
234 paged = false;
235 clickX = -1;
236 clickY = -1;
237 clickCount = 0;
238 isDoubleClick = false;
239 scrollingSelf = false;
240 delete postponed_autorepeat;
241 postponed_autorepeat = nullptr;
242 layoutTimerId = 0;
243 repaintTimerId = 0;
244 scrollTimerId = 0;
245 scrollSuspended = false;
246 scrollSuspendPreActivate = false;
247 smoothScrolling = false;
248 smoothScrollModeIsDefault = true;
249 shouldSmoothScroll = false;
250 smoothScrollMissedDeadlines = 0;
251 hasFrameset = false;
252 complete = false;
253 firstLayoutPending = true;
254 #ifdef SPEED_DEBUG
255 firstRepaintPending = true;
256 #endif
257 needsFullRepaint = true;
258 dirtyLayout = false;
259 layoutSchedulingEnabled = true;
260 painting = false;
261 layoutCounter = 0;
262 layoutAttemptCounter = 0;
263 scheduledLayoutCounter = 0;
264 updateRegion = QRegion();
265 m_dialogsAllowed = true;
266 accessKeysActivated = false;
267 accessKeysPreActivate = false;
268
269 // the view might have been built before the part it will be assigned to,
270 // so exceptionally, we need to directly ref/deref KHTMLGlobal to
271 // account for this transitory case.
272 KHTMLGlobal::ref();
273 accessKeysEnabled = KHTMLGlobal::defaultHTMLSettings()->accessKeysEnabled();
274 KHTMLGlobal::deref();
275
276 emitCompletedAfterRepaint = CSNone;
277 m_mouseEventsTarget = nullptr;
278 m_clipHolder = nullptr;
279 }
newScrollTimer(QWidget * view,int tid)280 void newScrollTimer(QWidget *view, int tid)
281 {
282 //qCDebug(KHTML_LOG) << "newScrollTimer timer " << tid;
283 view->killTimer(scrollTimerId);
284 scrollTimerId = tid;
285 scrollSuspended = false;
286 }
287 enum ScrollDirection { ScrollLeft, ScrollRight, ScrollUp, ScrollDown };
288
adjustScroller(QWidget * view,ScrollDirection direction,ScrollDirection oppositedir)289 void adjustScroller(QWidget *view, ScrollDirection direction, ScrollDirection oppositedir)
290 {
291 static const struct {
292 int msec, pixels;
293 } timings [] = {
294 {320, 1}, {224, 1}, {160, 1}, {112, 1}, {80, 1}, {56, 1}, {40, 1},
295 {28, 1}, {20, 1}, {20, 2}, {20, 3}, {20, 4}, {20, 6}, {20, 8}, {0, 0}
296 };
297 if (!scrollTimerId ||
298 (static_cast<int>(scrollDirection) != direction &&
299 (static_cast<int>(scrollDirection) != oppositedir || scrollSuspended))) {
300 scrollTiming = 6;
301 scrollBy = timings[scrollTiming].pixels;
302 scrollDirection = direction;
303 newScrollTimer(view, view->startTimer(timings[scrollTiming].msec));
304 } else if (scrollDirection == direction &&
305 timings[scrollTiming + 1].msec && !scrollSuspended) {
306 scrollBy = timings[++scrollTiming].pixels;
307 newScrollTimer(view, view->startTimer(timings[scrollTiming].msec));
308 } else if (scrollDirection == oppositedir) {
309 if (scrollTiming) {
310 scrollBy = timings[--scrollTiming].pixels;
311 newScrollTimer(view, view->startTimer(timings[scrollTiming].msec));
312 }
313 }
314 scrollSuspended = false;
315 }
316
haveZoom() const317 bool haveZoom() const
318 {
319 return zoomLevel != 100;
320 }
321
startScrolling()322 void startScrolling()
323 {
324 smoothScrolling = true;
325 smoothScrollTimer.start(sSmoothScrollTick);
326 shouldSmoothScroll = false;
327 }
328
stopScrolling()329 void stopScrolling()
330 {
331 smoothScrollTimer.stop();
332 dx = dy = 0;
333 steps = 0;
334 updateContentsXY();
335 smoothScrolling = false;
336 shouldSmoothScroll = false;
337 }
338
updateContentsXY()339 void updateContentsXY()
340 {
341 contentsX = QApplication::isRightToLeft() ?
342 view->horizontalScrollBar()->maximum() - view->horizontalScrollBar()->value() : view->horizontalScrollBar()->value();
343 contentsY = view->verticalScrollBar()->value();
344 }
scrollAccessKeys(int dx,int dy)345 void scrollAccessKeys(int dx, int dy)
346 {
347 QList<QLabel *> wl = view->widget()->findChildren<QLabel *>("KHTMLAccessKey");
348 foreach (QLabel *w, wl) {
349 w->move(w->pos() + QPoint(dx, dy));
350 }
351 }
scrollExternalWidgets(int dx,int dy)352 void scrollExternalWidgets(int dx, int dy)
353 {
354 if (visibleWidgets.isEmpty()) {
355 return;
356 }
357
358 QHashIterator<void *, QWidget *> it(visibleWidgets);
359 while (it.hasNext()) {
360 it.next();
361 it.value()->move(it.value()->pos() + QPoint(dx, dy));
362 }
363 }
364
365 NodeImpl *underMouse;
366 NodeImpl *underMouseNonShared;
367 NodeImpl *oldUnderMouse;
368
369 // Do not adjust bitfield enums sizes.
370 // They are oversized because they are signed on some platforms.
371 bool tabMovePending: 1;
372 bool lastTabbingDirection: 1;
373 PseudoFocusNodes pseudoFocusNode: 3;
374 bool scrollBarMoved: 1;
375 bool contentsMoving: 1;
376
377 Qt::ScrollBarPolicy vpolicy;
378 Qt::ScrollBarPolicy hpolicy;
379 bool prevScrollbarVisible: 1;
380 bool linkPressed: 1;
381 bool ignoreWheelEvents: 1;
382 StaticBackgroundState staticWidget: 3;
383 int staticObjectsCount;
384 int fixedObjectsCount;
385
386 int zoomLevel;
387 int borderX, borderY;
388 int dx, dy;
389 int steps;
390 KConfig *formCompletions;
391
392 int clickX, clickY, clickCount;
393 bool isDoubleClick;
394
395 bool paged;
396
397 bool scrollingSelf;
398 int contentsX, contentsY;
399 int layoutTimerId;
400 QKeyEvent *postponed_autorepeat;
401
402 int repaintTimerId;
403 int scrollTimerId;
404 int scrollTiming;
405 int scrollBy;
406 ScrollDirection scrollDirection : 3;
407 bool scrollSuspended : 1;
408 bool scrollSuspendPreActivate : 1;
409 KHTMLView::SmoothScrollingMode smoothScrollMode : 3;
410 bool smoothScrolling : 1;
411 bool smoothScrollModeIsDefault : 1;
412 bool shouldSmoothScroll : 1;
413 bool hasFrameset : 1;
414 bool complete : 1;
415 bool firstLayoutPending : 1;
416 #ifdef SPEED_DEBUG
417 bool firstRepaintPending : 1;
418 #endif
419 bool layoutSchedulingEnabled : 1;
420 bool needsFullRepaint : 1;
421 bool painting : 1;
422 bool possibleTripleClick : 1;
423 bool dirtyLayout : 1;
424 bool m_dialogsAllowed : 1;
425 short smoothScrollMissedDeadlines;
426 int layoutCounter;
427 int layoutAttemptCounter;
428 int scheduledLayoutCounter;
429 QRegion updateRegion;
430 QTimer smoothScrollTimer;
431 QTime smoothScrollStopwatch;
432 QHash<void *, QWidget *> visibleWidgets;
433 bool accessKeysEnabled;
434 bool accessKeysActivated;
435 bool accessKeysPreActivate;
436 CompletedState emitCompletedAfterRepaint;
437
438 QLabel *cursorIconWidget;
439 KHTMLView::LinkCursor cursorIconType;
440
441 // scrolling activated by MMB
442 short m_mouseScroll_byX;
443 short m_mouseScroll_byY;
444 QPoint scrollingFromWheel;
445 int scrollingFromWheelTimerId;
446 QTimer *m_mouseScrollTimer;
447 QWidget *m_mouseScrollIndicator;
448 QPointer<QWidget> m_mouseEventsTarget;
449 QStack<QRegion> *m_clipHolder;
450 KHTMLView *view;
451 };
452
453 #ifndef QT_NO_TOOLTIP
454
455 /** calculates the client-side image map rectangle for the given image element
456 * @param img image element
457 * @param scrollOfs scroll offset of viewport in content coordinates
458 * @param p position to be probed in viewport coordinates
459 * @param r returns the bounding rectangle in content coordinates
460 * @param s returns the title string
461 * @return true if an appropriate area was found -- only in this case r and
462 * s are valid, false otherwise
463 */
findImageMapRect(HTMLImageElementImpl * img,const QPoint & scrollOfs,const QPoint & p,QRect & r,QString & s)464 static bool findImageMapRect(HTMLImageElementImpl *img, const QPoint &scrollOfs,
465 const QPoint &p, QRect &r, QString &s)
466 {
467 HTMLMapElementImpl *map;
468 if (img && img->document()->isHTMLDocument() &&
469 (map = static_cast<HTMLDocumentImpl *>(img->document())->getMap(img->imageMap()))) {
470 RenderObject::NodeInfo info(true, false);
471 RenderObject *rend = img->renderer();
472 int ax, ay;
473 if (!rend || !rend->absolutePosition(ax, ay)) {
474 return false;
475 }
476 // we're a client side image map
477 bool inside = map->mapMouseEvent(p.x() - ax + scrollOfs.x(),
478 p.y() - ay + scrollOfs.y(), rend->contentWidth(),
479 rend->contentHeight(), info);
480 if (inside && info.URLElement()) {
481 HTMLAreaElementImpl *area = static_cast<HTMLAreaElementImpl *>(info.URLElement());
482 Q_ASSERT(area->id() == ID_AREA);
483 s = area->getAttribute(ATTR_TITLE).string();
484 QRegion reg = area->cachedRegion();
485 if (!s.isEmpty() && !reg.isEmpty()) {
486 r = reg.boundingRect();
487 r.translate(ax, ay);
488 return true;
489 }
490 }
491 }
492 return false;
493 }
494
event(QEvent * e)495 bool KHTMLView::event(QEvent *e)
496 {
497 switch (e->type()) {
498 case QEvent::ToolTip: {
499 QHelpEvent *he = static_cast<QHelpEvent *>(e);
500 QPoint p = he->pos();
501
502 DOM::NodeImpl *node = d->underMouseNonShared;
503 QRect region;
504 while (node) {
505 if (node->isElementNode()) {
506 DOM::ElementImpl *e = static_cast<DOM::ElementImpl *>(node);
507 QRect r;
508 QString s;
509 bool found = false;
510 // for images, check if it is part of a client-side image map,
511 // and query the <area>s' title attributes, too
512 if (e->id() == ID_IMG && !e->getAttribute(ATTR_USEMAP).isEmpty()) {
513 found = findImageMapRect(static_cast<HTMLImageElementImpl *>(e),
514 viewportToContents(QPoint(0, 0)), p, r, s);
515 }
516 if (!found) {
517 s = e->getAttribute(ATTR_TITLE).string().trimmed();
518 r = node->getRect();
519 }
520 region |= QRect(contentsToViewport(r.topLeft()), r.size());
521 if (!s.isEmpty()) {
522 QToolTip::showText(he->globalPos(),
523 Qt::convertFromPlainText(s, Qt::WhiteSpaceNormal),
524 widget(), region);
525 break;
526 }
527 }
528 node = node->parentNode();
529 }
530 // Qt makes tooltip events happen nearly immediately when a preceding one was processed in the past few seconds.
531 // We don't want that feature to apply to web tootlips however, as it clashes with dhtml menus.
532 // So we'll just pretend we did not process that event.
533 return false;
534 }
535
536 case QEvent::DragEnter:
537 case QEvent::DragMove:
538 case QEvent::DragLeave:
539 case QEvent::Drop:
540 // In Qt4, one needs to both call accept() on the DND event and return
541 // true on ::event for the candidate widget for the drop to be possible.
542 // Apps hosting us, such as konq, can do the former but not the later.
543 // We will do the second bit, as it's a no-op unless someone else explicitly
544 // accepts the event. We need to skip the scrollarea to do that,
545 // since it will just skip the events, both killing the drop, and
546 // not permitting us to forward it up the part hiearchy in our dragEnterEvent,
547 // etc. handlers
548 return QWidget::event(e);
549 case QEvent::StyleChange:
550 case QEvent::LayoutRequest: {
551 updateScrollBars();
552 return QAbstractScrollArea::event(e);
553 }
554 case QEvent::PaletteChange:
555 slotPaletteChanged();
556 return QScrollArea::event(e);
557 default:
558 return QScrollArea::event(e);
559 }
560 }
561 #endif
562
KHTMLView(KHTMLPart * part,QWidget * parent)563 KHTMLView::KHTMLView(KHTMLPart *part, QWidget *parent)
564 : QScrollArea(parent), d(new KHTMLViewPrivate(this))
565 {
566 m_medium = "screen";
567
568 m_part = part;
569
570 QScrollArea::setVerticalScrollBarPolicy(d->vpolicy);
571 QScrollArea::setHorizontalScrollBarPolicy(d->hpolicy);
572
573 initWidget();
574 widget()->setMouseTracking(true);
575 }
576
~KHTMLView()577 KHTMLView::~KHTMLView()
578 {
579 closeChildDialogs();
580 if (m_part) {
581 DOM::DocumentImpl *doc = m_part->xmlDocImpl();
582 if (doc) {
583 doc->detach();
584 }
585 }
586 delete d;
587 }
588
setPart(KHTMLPart * part)589 void KHTMLView::setPart(KHTMLPart *part)
590 {
591 assert(part && !m_part);
592 m_part = part;
593 }
594
initWidget()595 void KHTMLView::initWidget()
596 {
597 // Do not access the part here. It might not be fully constructed.
598
599 setFrameStyle(QFrame::NoFrame);
600 setFocusPolicy(Qt::StrongFocus);
601 viewport()->setFocusProxy(this);
602
603 _marginWidth = -1; // undefined
604 _marginHeight = -1;
605 _width = 0;
606 _height = 0;
607
608 installEventFilter(this);
609
610 setAcceptDrops(true);
611 if (!widget()) {
612 setWidget(new QWidget(this));
613 }
614 widget()->setAttribute(Qt::WA_NoSystemBackground);
615
616 // Do *not* remove this attribute frivolously.
617 // You might not notice a change of behaviour in Debug builds
618 // but removing opaque events will make QWidget::scroll fail horribly
619 // in Release builds.
620 widget()->setAttribute(Qt::WA_OpaquePaintEvent);
621
622 verticalScrollBar()->setCursor(Qt::ArrowCursor);
623 horizontalScrollBar()->setCursor(Qt::ArrowCursor);
624
625 connect(&d->smoothScrollTimer, SIGNAL(timeout()), this, SLOT(scrollTick()));
626 }
627
resizeContentsToViewport()628 void KHTMLView::resizeContentsToViewport()
629 {
630 QSize s = viewport()->size();
631 resizeContents(s.width(), s.height());
632 }
633
634 // called by KHTMLPart::clear()
clear()635 void KHTMLView::clear()
636 {
637 if (d->accessKeysEnabled && d->accessKeysActivated) {
638 accessKeysTimeout();
639 }
640 viewport()->unsetCursor();
641 if (d->cursorIconWidget) {
642 d->cursorIconWidget->hide();
643 }
644 if (d->smoothScrolling) {
645 d->stopScrolling();
646 }
647 d->reset();
648 QAbstractEventDispatcher::instance()->unregisterTimers(this);
649 emit cleared();
650
651 QScrollArea::setHorizontalScrollBarPolicy(d->hpolicy);
652 QScrollArea::setVerticalScrollBarPolicy(d->vpolicy);
653 verticalScrollBar()->setEnabled(false);
654 horizontalScrollBar()->setEnabled(false);
655
656 }
657
hideEvent(QHideEvent * e)658 void KHTMLView::hideEvent(QHideEvent *e)
659 {
660 QScrollArea::hideEvent(e);
661 }
662
showEvent(QShowEvent * e)663 void KHTMLView::showEvent(QShowEvent *e)
664 {
665 QScrollArea::showEvent(e);
666 }
667
setMouseEventsTarget(QWidget * w)668 void KHTMLView::setMouseEventsTarget(QWidget *w)
669 {
670 d->m_mouseEventsTarget = w;
671 }
672
mouseEventsTarget() const673 QWidget *KHTMLView::mouseEventsTarget() const
674 {
675 return d->m_mouseEventsTarget;
676 }
677
setClipHolder(QStack<QRegion> * ch)678 void KHTMLView::setClipHolder(QStack<QRegion> *ch)
679 {
680 d->m_clipHolder = ch;
681 }
682
clipHolder() const683 QStack<QRegion> *KHTMLView::clipHolder() const
684 {
685 return d->m_clipHolder;
686 }
687
contentsWidth() const688 int KHTMLView::contentsWidth() const
689 {
690 return widget() ? widget()->width() : 0;
691 }
692
contentsHeight() const693 int KHTMLView::contentsHeight() const
694 {
695 return widget() ? widget()->height() : 0;
696 }
697
resizeContents(int w,int h)698 void KHTMLView::resizeContents(int w, int h)
699 {
700 if (!widget()) {
701 return;
702 }
703 widget()->resize(w, h);
704 if (!widget()->isVisible()) {
705 updateScrollBars();
706 }
707 }
708
contentsX() const709 int KHTMLView::contentsX() const
710 {
711 return d->contentsX;
712 }
713
contentsY() const714 int KHTMLView::contentsY() const
715 {
716 return d->contentsY;
717 }
718
visibleWidth() const719 int KHTMLView::visibleWidth() const
720 {
721 if (m_kwp->isRedirected()) {
722 // our RenderWidget knows better
723 if (RenderWidget *rw = m_kwp->renderWidget()) {
724 int ret = rw->width() - rw->paddingLeft() - rw->paddingRight() - rw->borderLeft() - rw->borderRight();
725 if (verticalScrollBar()->isVisible()) {
726 ret -= verticalScrollBar()->sizeHint().width();
727 ret = qMax(0, ret);
728 }
729 return ret;
730 }
731 }
732 return viewport()->width();
733 }
734
visibleHeight() const735 int KHTMLView::visibleHeight() const
736 {
737 if (m_kwp->isRedirected()) {
738 // our RenderWidget knows better
739 if (RenderWidget *rw = m_kwp->renderWidget()) {
740 int ret = rw->height() - rw->paddingBottom() - rw->paddingTop() - rw->borderTop() - rw->borderBottom();
741 if (horizontalScrollBar()->isVisible()) {
742 ret -= horizontalScrollBar()->sizeHint().height();
743 ret = qMax(0, ret);
744 }
745 return ret;
746 }
747 }
748 return viewport()->height();
749 }
750
setContentsPos(int x,int y)751 void KHTMLView::setContentsPos(int x, int y)
752 {
753 horizontalScrollBar()->setValue(QApplication::isRightToLeft() ?
754 horizontalScrollBar()->maximum() - x : x);
755 verticalScrollBar()->setValue(y);
756 }
757
scrollBy(int x,int y)758 void KHTMLView::scrollBy(int x, int y)
759 {
760 if (d->scrollTimerId) {
761 d->newScrollTimer(this, 0);
762 }
763 horizontalScrollBar()->setValue(horizontalScrollBar()->value() + x);
764 verticalScrollBar()->setValue(verticalScrollBar()->value() + y);
765 }
766
contentsToViewport(const QPoint & p) const767 QPoint KHTMLView::contentsToViewport(const QPoint &p) const
768 {
769 return QPoint(p.x() - contentsX(), p.y() - contentsY());
770 }
771
contentsToViewport(int x,int y,int & cx,int & cy) const772 void KHTMLView::contentsToViewport(int x, int y, int &cx, int &cy) const
773 {
774 QPoint p(x, y);
775 p = contentsToViewport(p);
776 cx = p.x();
777 cy = p.y();
778 }
779
viewportToContents(const QPoint & p) const780 QPoint KHTMLView::viewportToContents(const QPoint &p) const
781 {
782 return QPoint(p.x() + contentsX(), p.y() + contentsY());
783 }
784
viewportToContents(int x,int y,int & cx,int & cy) const785 void KHTMLView::viewportToContents(int x, int y, int &cx, int &cy) const
786 {
787 QPoint p(x, y);
788 p = viewportToContents(p);
789 cx = p.x();
790 cy = p.y();
791 }
792
updateContents(int x,int y,int w,int h)793 void KHTMLView::updateContents(int x, int y, int w, int h)
794 {
795 applyTransforms(x, y, w, h);
796 if (m_kwp->isRedirected()) {
797 QPoint off = m_kwp->absolutePos();
798 KHTMLView *pview = m_part->parentPart()->view();
799 pview->updateContents(x + off.x(), y + off.y(), w, h);
800 } else {
801 widget()->update(x, y, w, h);
802 }
803 }
804
updateContents(const QRect & r)805 void KHTMLView::updateContents(const QRect &r)
806 {
807 updateContents(r.x(), r.y(), r.width(), r.height());
808 }
809
repaintContents(int x,int y,int w,int h)810 void KHTMLView::repaintContents(int x, int y, int w, int h)
811 {
812 applyTransforms(x, y, w, h);
813 if (m_kwp->isRedirected()) {
814 QPoint off = m_kwp->absolutePos();
815 KHTMLView *pview = m_part->parentPart()->view();
816 pview->repaintContents(x + off.x(), y + off.y(), w, h);
817 } else {
818 widget()->repaint(x, y, w, h);
819 }
820 }
821
repaintContents(const QRect & r)822 void KHTMLView::repaintContents(const QRect &r)
823 {
824 repaintContents(r.x(), r.y(), r.width(), r.height());
825 }
826
applyTransforms(int & x,int & y,int & w,int & h) const827 void KHTMLView::applyTransforms(int &x, int &y, int &w, int &h) const
828 {
829 if (d->haveZoom()) {
830 const int z = d->zoomLevel;
831 x = x * z / 100;
832 y = y * z / 100;
833 w = w * z / 100;
834 h = h * z / 100;
835 }
836 x -= contentsX();
837 y -= contentsY();
838 }
839
revertTransforms(int & x,int & y,int & w,int & h) const840 void KHTMLView::revertTransforms(int &x, int &y, int &w, int &h) const
841 {
842 x += contentsX();
843 y += contentsY();
844 if (d->haveZoom()) {
845 const int z = d->zoomLevel;
846 x = x * 100 / z;
847 y = y * 100 / z;
848 w = w * 100 / z;
849 h = h * 100 / z;
850 }
851 }
852
revertTransforms(int & x,int & y) const853 void KHTMLView::revertTransforms(int &x, int &y) const
854 {
855 int dummy = 0;
856 revertTransforms(x, y, dummy, dummy);
857 }
858
resizeEvent(QResizeEvent *)859 void KHTMLView::resizeEvent(QResizeEvent * /*e*/)
860 {
861 updateScrollBars();
862
863 // If we didn't load anything, make white area as big as the view
864 if (!m_part->xmlDocImpl()) {
865 resizeContentsToViewport();
866 }
867
868 // Viewport-dependent media queries may cause us to need completely different style information.
869 if (m_part->xmlDocImpl() && m_part->xmlDocImpl()->styleSelector()->affectedByViewportChange()) {
870 m_part->xmlDocImpl()->updateStyleSelector();
871 }
872
873 if (d->layoutSchedulingEnabled) {
874 layout();
875 }
876
877 QApplication::sendPostedEvents(viewport(), QEvent::Paint);
878
879 if (m_part && m_part->xmlDocImpl()) {
880 if (m_part->parentPart()) {
881 // sub-frame : queue the resize event until our toplevel is done layouting
882 khtml::ChildFrame *cf = m_part->parentPart()->frame(m_part);
883 if (cf && !cf->m_partContainerElement.isNull()) {
884 cf->m_partContainerElement.data()->postResizeEvent();
885 }
886 } else {
887 // toplevel : dispatch sub-frames'resize events before our own
888 HTMLPartContainerElementImpl::sendPostedResizeEvents();
889 m_part->xmlDocImpl()->dispatchWindowEvent(EventImpl::RESIZE_EVENT, false, false);
890 }
891 }
892 }
893
paintEvent(QPaintEvent * e)894 void KHTMLView::paintEvent(QPaintEvent *e)
895 {
896 QRect r = e->rect();
897 QRect v(contentsX(), contentsY(), visibleWidth(), visibleHeight());
898 QPoint off(contentsX(), contentsY());
899 r.translate(off);
900 r = r.intersected(v);
901 if (!r.isValid() || r.isEmpty()) {
902 return;
903 }
904
905 QPainter p(widget());
906 p.translate(-off);
907
908 if (d->haveZoom()) {
909 p.scale(d->zoomLevel / 100., d->zoomLevel / 100.);
910
911 r.setX(r.x() * 100 / d->zoomLevel);
912 r.setY(r.y() * 100 / d->zoomLevel);
913 r.setWidth(r.width() * 100 / d->zoomLevel);
914 r.setHeight(r.height() * 100 / d->zoomLevel);
915 r.adjust(-1, -1, 1, 1);
916 }
917 p.setClipRect(r);
918
919 int ex = r.x();
920 int ey = r.y();
921 int ew = r.width();
922 int eh = r.height();
923
924 if (!m_part || !m_part->xmlDocImpl() || !m_part->xmlDocImpl()->renderer()) {
925 p.fillRect(ex, ey, ew, eh, palette().brush(QPalette::Active, QPalette::Base));
926 return;
927 } else if (d->complete && static_cast<RenderCanvas *>(m_part->xmlDocImpl()->renderer())->needsLayout()) {
928 // an external update request happens while we have a layout scheduled
929 unscheduleRelayout();
930 layout();
931 } else if (m_part->xmlDocImpl()->tokenizer()) {
932 m_part->xmlDocImpl()->tokenizer()->setNormalYieldDelay();
933 }
934
935 if (d->painting) {
936 // qCDebug(KHTML_LOG) << "WARNING: paintEvent reentered! ";
937 return;
938 }
939 d->painting = true;
940
941 m_part->xmlDocImpl()->renderer()->layer()->paint(&p, r);
942
943 if (d->hasFrameset) {
944 NodeImpl *body = static_cast<HTMLDocumentImpl *>(m_part->xmlDocImpl())->body();
945 if (body && body->renderer() && body->id() == ID_FRAMESET) {
946 static_cast<RenderFrameSet *>(body->renderer())->paintFrameSetRules(&p, r);
947 } else {
948 d->hasFrameset = false;
949 }
950 }
951
952 khtml::DrawContentsEvent event(&p, ex, ey, ew, eh);
953 QApplication::sendEvent(m_part, &event);
954
955 if (d->contentsMoving && !d->smoothScrolling && widget()->underMouse()) {
956 QMouseEvent *tempEvent = new QMouseEvent(QEvent::MouseMove, widget()->mapFromGlobal(QCursor::pos()),
957 Qt::NoButton, Qt::NoButton, Qt::NoModifier);
958 QApplication::postEvent(widget(), tempEvent);
959 }
960 #ifdef SPEED_DEBUG
961 if (d->firstRepaintPending && !m_part->parentPart()) {
962 qCDebug(KHTML_LOG) << "FIRST PAINT:" << m_part->d->m_parsetime.elapsed();
963 }
964 d->firstRepaintPending = false;
965 #endif
966 d->painting = false;
967 }
968
setMarginWidth(int w)969 void KHTMLView::setMarginWidth(int w)
970 {
971 // make it update the rendering area when set
972 _marginWidth = w;
973 }
974
setMarginHeight(int h)975 void KHTMLView::setMarginHeight(int h)
976 {
977 // make it update the rendering area when set
978 _marginHeight = h;
979 }
980
layout()981 void KHTMLView::layout()
982 {
983 if (m_part && m_part->xmlDocImpl()) {
984 DOM::DocumentImpl *document = m_part->xmlDocImpl();
985
986 khtml::RenderCanvas *canvas = static_cast<khtml::RenderCanvas *>(document->renderer());
987 if (!canvas) {
988 return;
989 }
990
991 d->layoutSchedulingEnabled = false;
992 d->dirtyLayout = true;
993
994 // the reference object for the overflow property on canvas
995 RenderObject *ref = nullptr;
996 RenderObject *root = document->documentElement() ? document->documentElement()->renderer() : nullptr;
997
998 if (document->isHTMLDocument()) {
999 NodeImpl *body = static_cast<HTMLDocumentImpl *>(document)->body();
1000 if (body && body->renderer() && body->id() == ID_FRAMESET) {
1001 QScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1002 QScrollArea::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1003 body->renderer()->setNeedsLayout(true);
1004 d->hasFrameset = true;
1005 } else if (root) { // only apply body's overflow to canvas if root has a visible overflow
1006 ref = (!body || root->style()->hidesOverflow()) ? root : body->renderer();
1007 }
1008 } else {
1009 ref = root;
1010 }
1011 if (ref) {
1012 if (ref->style()->overflowX() == OHIDDEN) {
1013 if (d->hpolicy == Qt::ScrollBarAsNeeded) {
1014 QScrollArea::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1015 }
1016 } else if (ref->style()->overflowX() == OSCROLL) {
1017 if (d->hpolicy == Qt::ScrollBarAsNeeded) {
1018 QScrollArea::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
1019 }
1020 } else if (horizontalScrollBarPolicy() != d->hpolicy) {
1021 QScrollArea::setHorizontalScrollBarPolicy(d->hpolicy);
1022 }
1023 if (ref->style()->overflowY() == OHIDDEN) {
1024 if (d->vpolicy == Qt::ScrollBarAsNeeded) {
1025 QScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1026 }
1027 } else if (ref->style()->overflowY() == OSCROLL) {
1028 if (d->vpolicy == Qt::ScrollBarAsNeeded) {
1029 QScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
1030 }
1031 } else if (verticalScrollBarPolicy() != d->vpolicy) {
1032 QScrollArea::setVerticalScrollBarPolicy(d->vpolicy);
1033 }
1034 }
1035 d->needsFullRepaint = d->firstLayoutPending;
1036 if (_height != visibleHeight() || _width != visibleWidth()) {
1037 ;
1038 d->needsFullRepaint = true;
1039 _height = visibleHeight();
1040 _width = visibleWidth();
1041 }
1042
1043 canvas->layout();
1044
1045 emit finishedLayout();
1046 if (d->firstLayoutPending) {
1047 // make sure firstLayoutPending is set to false now in case this layout
1048 // wasn't scheduled
1049 d->firstLayoutPending = false;
1050 verticalScrollBar()->setEnabled(true);
1051 horizontalScrollBar()->setEnabled(true);
1052 }
1053 d->layoutCounter++;
1054
1055 if (d->accessKeysEnabled && d->accessKeysActivated) {
1056 emit hideAccessKeys();
1057 displayAccessKeys();
1058 }
1059 } else {
1060 _width = visibleWidth();
1061 }
1062
1063 if (d->layoutTimerId) {
1064 killTimer(d->layoutTimerId);
1065 }
1066 d->layoutTimerId = 0;
1067 d->layoutSchedulingEnabled = true;
1068 }
1069
closeChildDialogs()1070 void KHTMLView::closeChildDialogs()
1071 {
1072 QList<QDialog *> dlgs = findChildren<QDialog *>();
1073 foreach (QDialog *dlg, dlgs) {
1074 if (dlg->testAttribute(Qt::WA_ShowModal)) {
1075 // qCDebug(KHTML_LOG) << "closeChildDialogs: closing dialog " << dlg;
1076 // close() ends up calling QButton::animateClick, which isn't immediate
1077 // we need something the exits the event loop immediately (#49068)
1078 dlg->reject();
1079 }
1080 }
1081 d->m_dialogsAllowed = false;
1082 }
1083
dialogsAllowed()1084 bool KHTMLView::dialogsAllowed()
1085 {
1086 bool allowed = d->m_dialogsAllowed;
1087 KHTMLPart *p = m_part->parentPart();
1088 if (p && p->view()) {
1089 allowed &= p->view()->dialogsAllowed();
1090 }
1091 return allowed;
1092 }
1093
closeEvent(QCloseEvent * ev)1094 void KHTMLView::closeEvent(QCloseEvent *ev)
1095 {
1096 closeChildDialogs();
1097 QScrollArea::closeEvent(ev);
1098 }
1099
setZoomLevel(int percent)1100 void KHTMLView::setZoomLevel(int percent)
1101 {
1102 percent = percent < 20 ? 20 : (percent > 800 ? 800 : percent);
1103 int oldpercent = d->zoomLevel;
1104 d->zoomLevel = percent;
1105 if (percent != oldpercent) {
1106 if (d->layoutSchedulingEnabled) {
1107 layout();
1108 }
1109 widget()->update();
1110 }
1111 }
1112
zoomLevel() const1113 int KHTMLView::zoomLevel() const
1114 {
1115 return d->zoomLevel;
1116 }
1117
setSmoothScrollingMode(SmoothScrollingMode m)1118 void KHTMLView::setSmoothScrollingMode(SmoothScrollingMode m)
1119 {
1120 d->smoothScrollMode = m;
1121 d->smoothScrollModeIsDefault = false;
1122 if (d->smoothScrolling && !m) {
1123 d->stopScrolling();
1124 }
1125 }
1126
setSmoothScrollingModeDefault(SmoothScrollingMode m)1127 void KHTMLView::setSmoothScrollingModeDefault(SmoothScrollingMode m)
1128 {
1129 // check for manual override
1130 if (!d->smoothScrollModeIsDefault) {
1131 return;
1132 }
1133 d->smoothScrollMode = m;
1134 if (d->smoothScrolling && !m) {
1135 d->stopScrolling();
1136 }
1137 }
1138
smoothScrollingMode() const1139 KHTMLView::SmoothScrollingMode KHTMLView::smoothScrollingMode() const
1140 {
1141 return d->smoothScrollMode;
1142 }
1143
1144 //
1145 // Event Handling
1146 //
1147 /////////////////
1148
mousePressEvent(QMouseEvent * _mouse)1149 void KHTMLView::mousePressEvent(QMouseEvent *_mouse)
1150 {
1151 if (!m_part->xmlDocImpl()) {
1152 return;
1153 }
1154 if (d->possibleTripleClick && (_mouse->button() & Qt::MouseButtonMask) == Qt::LeftButton) {
1155 mouseDoubleClickEvent(_mouse); // it handles triple clicks too
1156 return;
1157 }
1158
1159 int xm = _mouse->x();
1160 int ym = _mouse->y();
1161 revertTransforms(xm, ym);
1162
1163 // qCDebug(KHTML_LOG) << "mousePressEvent: viewport=("<<_mouse->x()-contentsX()<<"/"<<_mouse->y()-contentsY()<<"), contents=(" << xm << "/" << ym << ")\n";
1164
1165 d->isDoubleClick = false;
1166
1167 DOM::NodeImpl::MouseEvent mev(_mouse->buttons(), DOM::NodeImpl::MousePress);
1168 m_part->xmlDocImpl()->prepareMouseEvent(false, xm, ym, &mev);
1169
1170 //qCDebug(KHTML_LOG) << "innerNode="<<mev.innerNode.nodeName().string();
1171
1172 if ((_mouse->button() == Qt::MidButton) &&
1173 !m_part->d->m_bOpenMiddleClick && !d->m_mouseScrollTimer &&
1174 mev.url.isNull() && (mev.innerNode.elementId() != ID_INPUT)) {
1175 QPoint point = mapFromGlobal(_mouse->globalPos());
1176
1177 d->m_mouseScroll_byX = 0;
1178 d->m_mouseScroll_byY = 0;
1179
1180 d->m_mouseScrollTimer = new QTimer(this);
1181 connect(d->m_mouseScrollTimer, SIGNAL(timeout()), this, SLOT(slotMouseScrollTimer()));
1182
1183 if (!d->m_mouseScrollIndicator) {
1184 QPixmap pixmap(48, 48), icon;
1185 pixmap.fill(QColor(qRgba(127, 127, 127, 127)));
1186
1187 QPainter p(&pixmap);
1188 QStyleOption option;
1189
1190 option.rect.setRect(16, 0, 16, 16);
1191 QApplication::style()->drawPrimitive(QStyle::PE_IndicatorArrowUp, &option, &p);
1192 option.rect.setRect(0, 16, 16, 16);
1193 QApplication::style()->drawPrimitive(QStyle::PE_IndicatorArrowLeft, &option, &p);
1194 option.rect.setRect(16, 32, 16, 16);
1195 QApplication::style()->drawPrimitive(QStyle::PE_IndicatorArrowDown, &option, &p);
1196 option.rect.setRect(32, 16, 16, 16);
1197 QApplication::style()->drawPrimitive(QStyle::PE_IndicatorArrowRight, &option, &p);
1198 p.drawEllipse(23, 23, 2, 2);
1199
1200 d->m_mouseScrollIndicator = new QWidget(this);
1201 d->m_mouseScrollIndicator->setFixedSize(48, 48);
1202 QPalette palette;
1203 palette.setBrush(d->m_mouseScrollIndicator->backgroundRole(), QBrush(pixmap));
1204 d->m_mouseScrollIndicator->setPalette(palette);
1205 }
1206 d->m_mouseScrollIndicator->move(point.x() - 24, point.y() - 24);
1207
1208 bool hasHorBar = visibleWidth() < contentsWidth();
1209 bool hasVerBar = visibleHeight() < contentsHeight();
1210
1211 KConfigGroup cg(KSharedConfig::openConfig(), "HTML Settings");
1212 if (cg.readEntry("ShowMouseScrollIndicator", true)) {
1213 d->m_mouseScrollIndicator->show();
1214 d->m_mouseScrollIndicator->unsetCursor();
1215
1216 QBitmap mask = d->m_mouseScrollIndicator->palette().brush(d->m_mouseScrollIndicator->backgroundRole()).texture().createHeuristicMask(true);
1217
1218 if (hasHorBar && !hasVerBar) {
1219 QBitmap bm(16, 16);
1220 bm.clear();
1221 QPainter painter(&mask);
1222 painter.drawPixmap(QRectF(16, 0, bm.width(), bm.height()), bm, bm.rect());
1223 painter.drawPixmap(QRectF(16, 32, bm.width(), bm.height()), bm, bm.rect());
1224 d->m_mouseScrollIndicator->setCursor(Qt::SizeHorCursor);
1225 } else if (!hasHorBar && hasVerBar) {
1226 QBitmap bm(16, 16);
1227 bm.clear();
1228 QPainter painter(&mask);
1229 painter.drawPixmap(QRectF(0, 16, bm.width(), bm.height()), bm, bm.rect());
1230 painter.drawPixmap(QRectF(32, 16, bm.width(), bm.height()), bm, bm.rect());
1231 d->m_mouseScrollIndicator->setCursor(Qt::SizeVerCursor);
1232 } else {
1233 d->m_mouseScrollIndicator->setCursor(Qt::SizeAllCursor);
1234 }
1235
1236 d->m_mouseScrollIndicator->setMask(mask);
1237 } else {
1238 if (hasHorBar && !hasVerBar) {
1239 viewport()->setCursor(Qt::SizeHorCursor);
1240 } else if (!hasHorBar && hasVerBar) {
1241 viewport()->setCursor(Qt::SizeVerCursor);
1242 } else {
1243 viewport()->setCursor(Qt::SizeAllCursor);
1244 }
1245 }
1246
1247 return;
1248 } else if (d->m_mouseScrollTimer) {
1249 delete d->m_mouseScrollTimer;
1250 d->m_mouseScrollTimer = nullptr;
1251
1252 if (d->m_mouseScrollIndicator) {
1253 d->m_mouseScrollIndicator->hide();
1254 }
1255 }
1256
1257 if (d->clickCount > 0 &&
1258 QPoint(d->clickX - xm, d->clickY - ym).manhattanLength() <= QApplication::startDragDistance()) {
1259 d->clickCount++;
1260 } else {
1261 d->clickCount = 1;
1262 d->clickX = xm;
1263 d->clickY = ym;
1264 }
1265
1266 bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEDOWN_EVENT, mev.innerNode.handle(), mev.innerNonSharedNode.handle(), true,
1267 d->clickCount, _mouse, true, DOM::NodeImpl::MousePress);
1268
1269 if (!swallowEvent) {
1270 emit m_part->nodeActivated(mev.innerNode);
1271
1272 khtml::MousePressEvent event(_mouse, xm, ym, mev.url, mev.target, mev.innerNode);
1273 QApplication::sendEvent(m_part, &event);
1274 // we might be deleted after this
1275 }
1276 }
1277
mouseDoubleClickEvent(QMouseEvent * _mouse)1278 void KHTMLView::mouseDoubleClickEvent(QMouseEvent *_mouse)
1279 {
1280 if (!m_part->xmlDocImpl()) {
1281 return;
1282 }
1283
1284 int xm = _mouse->x();
1285 int ym = _mouse->y();
1286 revertTransforms(xm, ym);
1287
1288 // qCDebug(KHTML_LOG) << "mouseDblClickEvent: x=" << xm << ", y=" << ym;
1289
1290 d->isDoubleClick = true;
1291
1292 DOM::NodeImpl::MouseEvent mev(_mouse->buttons(), DOM::NodeImpl::MouseDblClick);
1293 m_part->xmlDocImpl()->prepareMouseEvent(false, xm, ym, &mev);
1294
1295 // We do the same thing as mousePressEvent() here, since the DOM does not treat
1296 // single and double-click events as separate (only the detail, i.e. number of clicks differs)
1297 if (d->clickCount > 0 &&
1298 QPoint(d->clickX - xm, d->clickY - ym).manhattanLength() <= QApplication::startDragDistance()) {
1299 d->clickCount++;
1300 } else { // shouldn't happen, if Qt has the same criterias for double clicks.
1301 d->clickCount = 1;
1302 d->clickX = xm;
1303 d->clickY = ym;
1304 }
1305 bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEDOWN_EVENT, mev.innerNode.handle(), mev.innerNonSharedNode.handle(), true,
1306 d->clickCount, _mouse, true, DOM::NodeImpl::MouseDblClick);
1307
1308 if (!swallowEvent) {
1309 khtml::MouseDoubleClickEvent event(_mouse, xm, ym, mev.url, mev.target, mev.innerNode, d->clickCount);
1310 QApplication::sendEvent(m_part, &event);
1311 }
1312
1313 d->possibleTripleClick = true;
1314 QTimer::singleShot(QApplication::doubleClickInterval(), this, SLOT(tripleClickTimeout()));
1315 }
1316
tripleClickTimeout()1317 void KHTMLView::tripleClickTimeout()
1318 {
1319 d->possibleTripleClick = false;
1320 d->clickCount = 0;
1321 }
1322
targetOpensNewWindow(KHTMLPart * part,QString target)1323 static bool targetOpensNewWindow(KHTMLPart *part, QString target)
1324 {
1325 if (!target.isEmpty() && (target.toLower() != "_top") &&
1326 (target.toLower() != "_self") && (target.toLower() != "_parent")) {
1327 if (target.toLower() == "_blank") {
1328 return true;
1329 } else {
1330 while (part->parentPart()) {
1331 part = part->parentPart();
1332 }
1333 if (!part->frameExists(target)) {
1334 return true;
1335 }
1336 }
1337 }
1338 return false;
1339 }
1340
mouseMoveEvent(QMouseEvent * _mouse)1341 void KHTMLView::mouseMoveEvent(QMouseEvent *_mouse)
1342 {
1343 if (d->m_mouseScrollTimer) {
1344 QPoint point = mapFromGlobal(_mouse->globalPos());
1345
1346 int deltaX = point.x() - d->m_mouseScrollIndicator->x() - 24;
1347 int deltaY = point.y() - d->m_mouseScrollIndicator->y() - 24;
1348
1349 (deltaX > 0) ? d->m_mouseScroll_byX = 1 : d->m_mouseScroll_byX = -1;
1350 (deltaY > 0) ? d->m_mouseScroll_byY = 1 : d->m_mouseScroll_byY = -1;
1351
1352 double adX = qAbs(deltaX) / 30.0;
1353 double adY = qAbs(deltaY) / 30.0;
1354
1355 d->m_mouseScroll_byX = qMax(qMin(d->m_mouseScroll_byX * int(adX * adX), SHRT_MAX), SHRT_MIN);
1356 d->m_mouseScroll_byY = qMax(qMin(d->m_mouseScroll_byY * int(adY * adY), SHRT_MAX), SHRT_MIN);
1357
1358 if (d->m_mouseScroll_byX == 0 && d->m_mouseScroll_byY == 0) {
1359 d->m_mouseScrollTimer->stop();
1360 } else if (!d->m_mouseScrollTimer->isActive()) {
1361 d->m_mouseScrollTimer->start(20);
1362 }
1363 }
1364
1365 if (!m_part->xmlDocImpl()) {
1366 return;
1367 }
1368
1369 int xm = _mouse->x();
1370 int ym = _mouse->y();
1371 revertTransforms(xm, ym);
1372
1373 DOM::NodeImpl::MouseEvent mev(_mouse->buttons(), DOM::NodeImpl::MouseMove);
1374 // Do not modify :hover/:active state while mouse is pressed.
1375 m_part->xmlDocImpl()->prepareMouseEvent(_mouse->buttons() /*readonly ?*/, xm, ym, &mev);
1376
1377 // qCDebug(KHTML_LOG) << "mouse move: " << _mouse->pos()
1378 // << " button " << _mouse->button()
1379 // << " state " << _mouse->state();
1380
1381 DOM::NodeImpl *target = mev.innerNode.handle();
1382 DOM::NodeImpl *fn = m_part->xmlDocImpl()->focusNode();
1383
1384 // a widget may be the real target of this event (e.g. if a scrollbar's slider is being moved)
1385 if (d->m_mouseEventsTarget && fn && fn->renderer() && fn->renderer()->isWidget()) {
1386 target = fn;
1387 }
1388
1389 bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEMOVE_EVENT, target, mev.innerNonSharedNode.handle(), false,
1390 0, _mouse, true, DOM::NodeImpl::MouseMove);
1391
1392 if (d->clickCount > 0 &&
1393 QPoint(d->clickX - xm, d->clickY - ym).manhattanLength() > QApplication::startDragDistance()) {
1394 d->clickCount = 0; // moving the mouse outside the threshold invalidates the click
1395 }
1396
1397 khtml::RenderObject *r = target ? target->renderer() : nullptr;
1398 bool setCursor = true;
1399 bool forceDefault = false;
1400 if (r && r->isWidget()) {
1401 RenderWidget *rw = static_cast<RenderWidget *>(r);
1402 KHTMLWidget *kw = qobject_cast<KHTMLView *>(rw->widget()) ? dynamic_cast<KHTMLWidget *>(rw->widget()) : nullptr;
1403 if (kw && kw->m_kwp->isRedirected()) {
1404 setCursor = false;
1405 } else if (QLineEdit *le = qobject_cast<QLineEdit *>(rw->widget())) {
1406 QList<QWidget *> wl = le->findChildren<QWidget *>("KLineEditButton");
1407 // force arrow cursor above lineedit clear button
1408 foreach (QWidget *w, wl) {
1409 if (w->underMouse()) {
1410 forceDefault = true;
1411 break;
1412 }
1413 }
1414 } else if (QTextEdit *te = qobject_cast<QTextEdit *>(rw->widget())) {
1415 if (te->verticalScrollBar()->underMouse() || te->horizontalScrollBar()->underMouse()) {
1416 forceDefault = true;
1417 }
1418 }
1419 }
1420 khtml::RenderStyle *style = (r && r->style()) ? r->style() : nullptr;
1421 QCursor c;
1422 LinkCursor linkCursor = LINK_NORMAL;
1423 switch (!forceDefault ? (style ? style->cursor() : CURSOR_AUTO) : CURSOR_DEFAULT) {
1424 case CURSOR_AUTO:
1425 if (r && r->isText() && ((m_part->d->m_bMousePressed && m_part->d->editor_context.m_beganSelectingText) ||
1426 !r->isPointInsideSelection(xm, ym, m_part->caret()))) {
1427 c = QCursor(Qt::IBeamCursor);
1428 }
1429 if (mev.url.length() && m_part->settings()->changeCursor()) {
1430 c = m_part->urlCursor();
1431 if (mev.url.string().startsWith("mailto:") && mev.url.string().indexOf('@') > 0) {
1432 linkCursor = LINK_MAILTO;
1433 } else if (targetOpensNewWindow(m_part, mev.target.string())) {
1434 linkCursor = LINK_NEWWINDOW;
1435 }
1436 }
1437
1438 if (r && r->isFrameSet() && !static_cast<RenderFrameSet *>(r)->noResize()) {
1439 c = QCursor(static_cast<RenderFrameSet *>(r)->cursorShape());
1440 }
1441
1442 break;
1443 case CURSOR_CROSS:
1444 c = QCursor(Qt::CrossCursor);
1445 break;
1446 case CURSOR_POINTER:
1447 c = m_part->urlCursor();
1448 if (mev.url.string().startsWith("mailto:") && mev.url.string().indexOf('@') > 0) {
1449 linkCursor = LINK_MAILTO;
1450 } else if (targetOpensNewWindow(m_part, mev.target.string())) {
1451 linkCursor = LINK_NEWWINDOW;
1452 }
1453 break;
1454 case CURSOR_PROGRESS:
1455 c = QCursor(Qt::BusyCursor); // working_cursor
1456 break;
1457 case CURSOR_MOVE:
1458 case CURSOR_ALL_SCROLL:
1459 c = QCursor(Qt::SizeAllCursor);
1460 break;
1461 case CURSOR_E_RESIZE:
1462 case CURSOR_W_RESIZE:
1463 case CURSOR_EW_RESIZE:
1464 c = QCursor(Qt::SizeHorCursor);
1465 break;
1466 case CURSOR_N_RESIZE:
1467 case CURSOR_S_RESIZE:
1468 case CURSOR_NS_RESIZE:
1469 c = QCursor(Qt::SizeVerCursor);
1470 break;
1471 case CURSOR_NE_RESIZE:
1472 case CURSOR_SW_RESIZE:
1473 case CURSOR_NESW_RESIZE:
1474 c = QCursor(Qt::SizeBDiagCursor);
1475 break;
1476 case CURSOR_NW_RESIZE:
1477 case CURSOR_SE_RESIZE:
1478 case CURSOR_NWSE_RESIZE:
1479 c = QCursor(Qt::SizeFDiagCursor);
1480 break;
1481 case CURSOR_TEXT:
1482 c = QCursor(Qt::IBeamCursor);
1483 break;
1484 case CURSOR_WAIT:
1485 c = QCursor(Qt::WaitCursor);
1486 break;
1487 case CURSOR_HELP:
1488 c = QCursor(Qt::WhatsThisCursor);
1489 break;
1490 case CURSOR_DEFAULT:
1491 break;
1492 case CURSOR_NONE:
1493 case CURSOR_NOT_ALLOWED:
1494 c = QCursor(Qt::ForbiddenCursor);
1495 break;
1496 case CURSOR_ROW_RESIZE:
1497 c = QCursor(Qt::SplitVCursor);
1498 break;
1499 case CURSOR_COL_RESIZE:
1500 c = QCursor(Qt::SplitHCursor);
1501 break;
1502 case CURSOR_VERTICAL_TEXT:
1503 case CURSOR_CONTEXT_MENU:
1504 case CURSOR_NO_DROP:
1505 case CURSOR_CELL:
1506 case CURSOR_COPY:
1507 case CURSOR_ALIAS:
1508 c = QCursor(Qt::ArrowCursor);
1509 break;
1510 }
1511
1512 if (!setCursor && style && style->cursor() != CURSOR_AUTO) {
1513 setCursor = true;
1514 }
1515
1516 QWidget *vp = viewport();
1517 for (KHTMLPart *p = m_part; p; p = p->parentPart())
1518 if (!p->parentPart()) {
1519 vp = p->view()->viewport();
1520 }
1521 if (setCursor && (vp->cursor().shape() != c.shape() || c.shape() == Qt::BitmapCursor)) {
1522 if (c.shape() == Qt::ArrowCursor) {
1523 for (KHTMLPart *p = m_part; p; p = p->parentPart()) {
1524 p->view()->viewport()->unsetCursor();
1525 }
1526 } else {
1527 vp->setCursor(c);
1528 }
1529 }
1530
1531 if (linkCursor != LINK_NORMAL && isVisible() && hasFocus()) {
1532 #if HAVE_X11
1533
1534 if (!d->cursorIconWidget) {
1535 #if HAVE_X11
1536 d->cursorIconWidget = new QLabel(nullptr, Qt::X11BypassWindowManagerHint);
1537 XSetWindowAttributes attr;
1538 attr.save_under = True;
1539 XChangeWindowAttributes(QX11Info::display(), d->cursorIconWidget->winId(), CWSaveUnder, &attr);
1540 #else
1541 d->cursorIconWidget = new QLabel(NULL, NULL);
1542 //TODO
1543 #endif
1544 }
1545
1546 // Update the pixmap if need be.
1547 if (linkCursor != d->cursorIconType) {
1548 d->cursorIconType = linkCursor;
1549 QString cursorIcon;
1550 switch (linkCursor) {
1551 case LINK_MAILTO: cursorIcon = "mail-message-new"; break;
1552 case LINK_NEWWINDOW: cursorIcon = "window-new"; break;
1553 default: cursorIcon = "dialog-error"; break;
1554 }
1555
1556 QPixmap icon_pixmap = KHTMLGlobal::iconLoader()->loadIcon(cursorIcon, KIconLoader::Small, 0, KIconLoader::DefaultState, QStringList(), nullptr, true);
1557
1558 d->cursorIconWidget->resize(icon_pixmap.width(), icon_pixmap.height());
1559 d->cursorIconWidget->setMask(icon_pixmap.createMaskFromColor(Qt::transparent));
1560 d->cursorIconWidget->setPixmap(icon_pixmap);
1561 d->cursorIconWidget->update();
1562 }
1563
1564 QPoint c_pos = QCursor::pos();
1565 d->cursorIconWidget->move(c_pos.x() + 15, c_pos.y() + 15);
1566 #if HAVE_X11
1567 XRaiseWindow(QX11Info::display(), d->cursorIconWidget->winId());
1568 QApplication::flush();
1569 #elif defined(Q_OS_WIN)
1570 SetWindowPos(d->cursorIconWidget->winId(), HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
1571 #else
1572 //TODO?
1573 #endif
1574 d->cursorIconWidget->show();
1575 #endif
1576 } else if (d->cursorIconWidget) {
1577 d->cursorIconWidget->hide();
1578 }
1579
1580 if (r && r->isWidget()) {
1581 _mouse->ignore();
1582 }
1583
1584 if (!swallowEvent) {
1585 khtml::MouseMoveEvent event(_mouse, xm, ym, mev.url, mev.target, mev.innerNode);
1586 QApplication::sendEvent(m_part, &event);
1587 }
1588 }
1589
mouseReleaseEvent(QMouseEvent * _mouse)1590 void KHTMLView::mouseReleaseEvent(QMouseEvent *_mouse)
1591 {
1592 bool swallowEvent = false;
1593
1594 int xm = _mouse->x();
1595 int ym = _mouse->y();
1596 revertTransforms(xm, ym);
1597
1598 DOM::NodeImpl::MouseEvent mev(_mouse->buttons(), DOM::NodeImpl::MouseRelease);
1599
1600 if (m_part->xmlDocImpl()) {
1601 m_part->xmlDocImpl()->prepareMouseEvent(false, xm, ym, &mev);
1602
1603 DOM::NodeImpl *target = mev.innerNode.handle();
1604 DOM::NodeImpl *fn = m_part->xmlDocImpl()->focusNode();
1605
1606 // a widget may be the real target of this event (e.g. if a scrollbar's slider is being moved)
1607 if (d->m_mouseEventsTarget && fn && fn->renderer() && fn->renderer()->isWidget()) {
1608 target = fn;
1609 }
1610
1611 swallowEvent = dispatchMouseEvent(EventImpl::MOUSEUP_EVENT, target, mev.innerNonSharedNode.handle(), true,
1612 d->clickCount, _mouse, false, DOM::NodeImpl::MouseRelease);
1613
1614 // clear our sticky event target on any mouseRelease event
1615 if (d->m_mouseEventsTarget) {
1616 d->m_mouseEventsTarget = nullptr;
1617 }
1618
1619 if (d->clickCount > 0 &&
1620 QPoint(d->clickX - xm, d->clickY - ym).manhattanLength() <= QApplication::startDragDistance()) {
1621 QMouseEvent me(d->isDoubleClick ? QEvent::MouseButtonDblClick : QEvent::MouseButtonRelease,
1622 _mouse->pos(), _mouse->button(), _mouse->buttons(), _mouse->modifiers());
1623 dispatchMouseEvent(EventImpl::CLICK_EVENT, mev.innerNode.handle(), mev.innerNonSharedNode.handle(), true,
1624 d->clickCount, &me, true, DOM::NodeImpl::MouseRelease);
1625 }
1626
1627 khtml::RenderObject *r = target ? target->renderer() : nullptr;
1628 if (r && r->isWidget()) {
1629 _mouse->ignore();
1630 }
1631 }
1632
1633 if (!swallowEvent) {
1634 khtml::MouseReleaseEvent event(_mouse, xm, ym, mev.url, mev.target, mev.innerNode);
1635 QApplication::sendEvent(m_part, &event);
1636 }
1637 }
1638
1639 // returns true if event should be swallowed
dispatchKeyEvent(QKeyEvent * _ke)1640 bool KHTMLView::dispatchKeyEvent(QKeyEvent *_ke)
1641 {
1642 if (!m_part->xmlDocImpl()) {
1643 return false;
1644 }
1645 // Pressing and releasing a key should generate keydown, keypress and keyup events
1646 // Holding it down should generated keydown, keypress (repeatedly) and keyup events
1647 // The problem here is that Qt generates two autorepeat events (keyrelease+keypress)
1648 // for autorepeating, while DOM wants only one autorepeat event (keypress), so one
1649 // of the Qt events shouldn't be passed to DOM, but it should be still filtered
1650 // out if DOM would filter the autorepeat event. Additional problem is that Qt keyrelease
1651 // events don't have text() set (Qt bug?), so DOM often would ignore the keypress event
1652 // if it was created using Qt keyrelease, but Qt autorepeat keyrelease comes
1653 // before Qt autorepeat keypress (i.e. problem whether to filter it out or not).
1654 // The solution is to filter out and postpone the Qt autorepeat keyrelease until
1655 // the following Qt keypress event comes. If DOM accepts the DOM keypress event,
1656 // the postponed event will be simply discarded. If not, it will be passed to keyPressEvent()
1657 // again, and here it will be ignored.
1658 //
1659 // Qt: Press | Release(autorepeat) Press(autorepeat) etc. | Release
1660 // DOM: Down + Press | (nothing) Press | Up
1661
1662 // It's also possible to get only Releases. E.g. the release of alt-tab,
1663 // or when the keypresses get captured by an accel.
1664
1665 if (_ke == d->postponed_autorepeat) { // replayed event
1666 return false;
1667 }
1668
1669 if (_ke->type() == QEvent::KeyPress) {
1670 if (!_ke->isAutoRepeat()) {
1671 bool ret = dispatchKeyEventHelper(_ke, false); // keydown
1672 // don't send keypress even if keydown was blocked, like IE (and unlike Mozilla)
1673 if (!ret && dispatchKeyEventHelper(_ke, true)) { // keypress
1674 ret = true;
1675 }
1676 return ret;
1677 } else { // autorepeat
1678 bool ret = dispatchKeyEventHelper(_ke, true); // keypress
1679 if (!ret && d->postponed_autorepeat) {
1680 keyPressEvent(d->postponed_autorepeat);
1681 }
1682 delete d->postponed_autorepeat;
1683 d->postponed_autorepeat = nullptr;
1684 return ret;
1685 }
1686 } else { // QEvent::KeyRelease
1687 // Discard postponed "autorepeat key-release" events that didn't see
1688 // a keypress after them (e.g. due to QAccel)
1689 delete d->postponed_autorepeat;
1690 d->postponed_autorepeat = nullptr;
1691
1692 if (!_ke->isAutoRepeat()) {
1693 return dispatchKeyEventHelper(_ke, false); // keyup
1694 } else {
1695 d->postponed_autorepeat = new QKeyEvent(_ke->type(), _ke->key(), _ke->modifiers(),
1696 _ke->text(), _ke->isAutoRepeat(), _ke->count());
1697 if (_ke->isAccepted()) {
1698 d->postponed_autorepeat->accept();
1699 } else {
1700 d->postponed_autorepeat->ignore();
1701 }
1702 return true;
1703 }
1704 }
1705 }
1706
1707 // returns true if event should be swallowed
dispatchKeyEventHelper(QKeyEvent * _ke,bool keypress)1708 bool KHTMLView::dispatchKeyEventHelper(QKeyEvent *_ke, bool keypress)
1709 {
1710 DOM::NodeImpl *keyNode = m_part->xmlDocImpl()->focusNode();
1711 if (keyNode) {
1712 return keyNode->dispatchKeyEvent(_ke, keypress);
1713 } else { // no focused node, send to document
1714 return m_part->xmlDocImpl()->dispatchKeyEvent(_ke, keypress);
1715 }
1716 }
1717
keyPressEvent(QKeyEvent * _ke)1718 void KHTMLView::keyPressEvent(QKeyEvent *_ke)
1719 {
1720 // If CTRL was hit, be prepared for access keys
1721 if (d->accessKeysEnabled && _ke->key() == Qt::Key_Control && !(_ke->modifiers() & ~Qt::ControlModifier) && !d->accessKeysActivated) {
1722 d->accessKeysPreActivate = true;
1723 _ke->accept();
1724 return;
1725 }
1726
1727 if (_ke->key() == Qt::Key_Shift && !(_ke->modifiers() & ~Qt::ShiftModifier)) {
1728 d->scrollSuspendPreActivate = true;
1729 }
1730
1731 // accesskey handling needs to be done before dispatching, otherwise e.g. lineedits
1732 // may eat the event
1733
1734 if (d->accessKeysEnabled && d->accessKeysActivated) {
1735 int state = (_ke->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier));
1736 if (state == 0 || state == Qt::ShiftModifier) {
1737 if (_ke->key() != Qt::Key_Shift) {
1738 accessKeysTimeout();
1739 }
1740 handleAccessKey(_ke);
1741 _ke->accept();
1742 return;
1743 }
1744 accessKeysTimeout();
1745 _ke->accept();
1746 return;
1747 }
1748
1749 if (dispatchKeyEvent(_ke)) {
1750 // If either keydown or keypress was accepted by a widget, or canceled by JS, stop here.
1751 _ke->accept();
1752 return;
1753 }
1754
1755 int offs = (viewport()->height() < 30) ? viewport()->height() : 30; // ### ??
1756 if (_ke->modifiers() & Qt::ShiftModifier)
1757 switch (_ke->key()) {
1758 case Qt::Key_Space:
1759 verticalScrollBar()->setValue(verticalScrollBar()->value() - viewport()->height() + offs);
1760 if (d->scrollSuspended) {
1761 d->newScrollTimer(this, 0);
1762 }
1763 break;
1764
1765 case Qt::Key_Down:
1766 case Qt::Key_J:
1767 d->adjustScroller(this, KHTMLViewPrivate::ScrollDown, KHTMLViewPrivate::ScrollUp);
1768 break;
1769
1770 case Qt::Key_Up:
1771 case Qt::Key_K:
1772 d->adjustScroller(this, KHTMLViewPrivate::ScrollUp, KHTMLViewPrivate::ScrollDown);
1773 break;
1774
1775 case Qt::Key_Left:
1776 case Qt::Key_H:
1777 d->adjustScroller(this, KHTMLViewPrivate::ScrollLeft, KHTMLViewPrivate::ScrollRight);
1778 break;
1779
1780 case Qt::Key_Right:
1781 case Qt::Key_L:
1782 d->adjustScroller(this, KHTMLViewPrivate::ScrollRight, KHTMLViewPrivate::ScrollLeft);
1783 break;
1784 }
1785 else
1786 switch (_ke->key()) {
1787 case Qt::Key_Down:
1788 case Qt::Key_J:
1789 if (!d->scrollTimerId || d->scrollSuspended) {
1790 verticalScrollBar()->setValue(verticalScrollBar()->value() + 10);
1791 }
1792 if (d->scrollTimerId) {
1793 d->newScrollTimer(this, 0);
1794 }
1795 break;
1796
1797 case Qt::Key_Space:
1798 case Qt::Key_PageDown:
1799 d->shouldSmoothScroll = true;
1800 verticalScrollBar()->setValue(verticalScrollBar()->value() + viewport()->height() - offs);
1801 if (d->scrollSuspended) {
1802 d->newScrollTimer(this, 0);
1803 }
1804 break;
1805
1806 case Qt::Key_Up:
1807 case Qt::Key_K:
1808 if (!d->scrollTimerId || d->scrollSuspended) {
1809 verticalScrollBar()->setValue(verticalScrollBar()->value() - 10);
1810 }
1811 if (d->scrollTimerId) {
1812 d->newScrollTimer(this, 0);
1813 }
1814 break;
1815
1816 case Qt::Key_PageUp:
1817 d->shouldSmoothScroll = true;
1818 verticalScrollBar()->setValue(verticalScrollBar()->value() - viewport()->height() + offs);
1819 if (d->scrollSuspended) {
1820 d->newScrollTimer(this, 0);
1821 }
1822 break;
1823 case Qt::Key_Right:
1824 case Qt::Key_L:
1825 if (!d->scrollTimerId || d->scrollSuspended) {
1826 horizontalScrollBar()->setValue(horizontalScrollBar()->value() + 10);
1827 }
1828 if (d->scrollTimerId) {
1829 d->newScrollTimer(this, 0);
1830 }
1831 break;
1832
1833 case Qt::Key_Left:
1834 case Qt::Key_H:
1835 if (!d->scrollTimerId || d->scrollSuspended) {
1836 horizontalScrollBar()->setValue(horizontalScrollBar()->value() - 10);
1837 }
1838 if (d->scrollTimerId) {
1839 d->newScrollTimer(this, 0);
1840 }
1841 break;
1842 case Qt::Key_Enter:
1843 case Qt::Key_Return:
1844 // ### FIXME:
1845 // or even better to HTMLAnchorElementImpl::event()
1846 if (m_part->xmlDocImpl()) {
1847 NodeImpl *n = m_part->xmlDocImpl()->focusNode();
1848 if (n) {
1849 n->setActive();
1850 }
1851 }
1852 break;
1853 case Qt::Key_Home:
1854 verticalScrollBar()->setValue(0);
1855 horizontalScrollBar()->setValue(0);
1856 if (d->scrollSuspended) {
1857 d->newScrollTimer(this, 0);
1858 }
1859 break;
1860 case Qt::Key_End:
1861 verticalScrollBar()->setValue(contentsHeight() - visibleHeight());
1862 if (d->scrollSuspended) {
1863 d->newScrollTimer(this, 0);
1864 }
1865 break;
1866 case Qt::Key_Shift:
1867 // what are you doing here?
1868 _ke->ignore();
1869 return;
1870 default:
1871 if (d->scrollTimerId) {
1872 d->newScrollTimer(this, 0);
1873 }
1874 _ke->ignore();
1875 return;
1876 }
1877
1878 _ke->accept();
1879 }
1880
keyReleaseEvent(QKeyEvent * _ke)1881 void KHTMLView::keyReleaseEvent(QKeyEvent *_ke)
1882 {
1883 if (d->scrollSuspendPreActivate && _ke->key() != Qt::Key_Shift) {
1884 d->scrollSuspendPreActivate = false;
1885 }
1886 if (_ke->key() == Qt::Key_Shift && d->scrollSuspendPreActivate && !(_ke->modifiers() & Qt::ShiftModifier))
1887 if (d->scrollTimerId) {
1888 d->scrollSuspended = !d->scrollSuspended;
1889 if (d->scrollSuspended) {
1890 d->stopScrolling();
1891 }
1892 }
1893
1894 if (d->accessKeysEnabled) {
1895 if (d->accessKeysPreActivate && _ke->key() != Qt::Key_Control) {
1896 d->accessKeysPreActivate = false;
1897 }
1898 if (d->accessKeysPreActivate && !(_ke->modifiers() & Qt::ControlModifier)) {
1899 displayAccessKeys();
1900 m_part->setStatusBarText(i18n("Access Keys activated"), KHTMLPart::BarOverrideText);
1901 d->accessKeysActivated = true;
1902 d->accessKeysPreActivate = false;
1903 _ke->accept();
1904 return;
1905 } else if (d->accessKeysActivated) {
1906 accessKeysTimeout();
1907 _ke->accept();
1908 return;
1909 }
1910 }
1911
1912 // Send keyup event
1913 if (dispatchKeyEvent(_ke)) {
1914 _ke->accept();
1915 return;
1916 }
1917
1918 QScrollArea::keyReleaseEvent(_ke);
1919 }
1920
focusNextPrevChild(bool next)1921 bool KHTMLView::focusNextPrevChild(bool next)
1922 {
1923 // Now try to find the next child
1924 if (m_part->xmlDocImpl() && focusNextPrevNode(next)) {
1925 //if (m_part->xmlDocImpl()->focusNode())
1926 // qCDebug(KHTML_LOG) << "focusNode.name: "
1927 // << m_part->xmlDocImpl()->focusNode()->nodeName().string();
1928 return true; // focus node found
1929 }
1930
1931 // If we get here, pass tabbing control up to the next/previous child in our parent
1932 d->pseudoFocusNode = KHTMLViewPrivate::PFNone;
1933 if (m_part->parentPart() && m_part->parentPart()->view()) {
1934 return m_part->parentPart()->view()->focusNextPrevChild(next);
1935 }
1936
1937 return QWidget::focusNextPrevChild(next);
1938 }
1939
doAutoScroll()1940 void KHTMLView::doAutoScroll()
1941 {
1942 QPoint pos = QCursor::pos();
1943 QPoint off;
1944 KHTMLView *v = m_kwp->isRedirected() ? m_kwp->rootViewPos(off) : this;
1945 pos = v->viewport()->mapFromGlobal(pos);
1946 pos -= off;
1947 int xm, ym;
1948 viewportToContents(pos.x(), pos.y(), xm, ym); // ###
1949
1950 pos = QPoint(pos.x() - viewport()->x(), pos.y() - viewport()->y());
1951 if ((pos.y() < 0) || (pos.y() > visibleHeight()) ||
1952 (pos.x() < 0) || (pos.x() > visibleWidth())) {
1953 ensureVisible(xm, ym, 0, 5);
1954
1955 #ifndef KHTML_NO_SELECTION
1956 // extend the selection while scrolling
1957 DOM::Node innerNode;
1958 if (m_part->isExtendingSelection()) {
1959 RenderObject::NodeInfo renderInfo(true/*readonly*/, false/*active*/);
1960 m_part->xmlDocImpl()->renderer()->layer()
1961 ->nodeAtPoint(renderInfo, xm, ym);
1962 innerNode = renderInfo.innerNode();
1963 }/*end if*/
1964
1965 if (innerNode.handle() && innerNode.handle()->renderer()
1966 && innerNode.handle()->renderer()->shouldSelect()) {
1967 m_part->extendSelectionTo(xm, ym, innerNode);
1968 }/*end if*/
1969 #endif // KHTML_NO_SELECTION
1970 }
1971 }
1972
1973 // KHTML defines its own stacking order for any object and thus takes
1974 // control of widget painting whenever it can. This is called "redirection".
1975 //
1976 // Redirected widgets are placed off screen. When they are declared as a child of our view (ChildPolished event),
1977 // an event filter is installed, so as to catch any paint event and translate them as update() of the view's main widget.
1978 //
1979 // Painting also happens spontaneously within widgets. In this case, the widget would update() parts of itself.
1980 // While this ordinarily results in a paintEvent being schedduled, it is not the case with off screen widgets.
1981 // Thus update() is monitored by using the mechanism that deffers any update call happening during a paint event,
1982 // transforming it into a posted UpdateLater event. Hence the need to set Qt::WA_WState_InPaintEvent on redirected widgets.
1983 //
1984 // Once the UpdateLater event has been received, Qt::WA_WState_InPaintEvent is removed and the process continues
1985 // with the update of the corresponding rect on the view. That in turn will make our painting subsystem render()
1986 // the widget at the correct stacking position.
1987 //
1988 // For non-redirected (e.g. external) widgets, z-order is honoured through masking. cf.RenderLayer::updateWidgetMasks
1989
handleWidget(QWidget * w,KHTMLView * view,bool recurse=true)1990 static void handleWidget(QWidget *w, KHTMLView *view, bool recurse = true)
1991 {
1992 if (w->isWindow()) {
1993 return;
1994 }
1995
1996 if (!qobject_cast<QFrame *>(w)) {
1997 w->setAttribute(Qt::WA_NoSystemBackground);
1998 }
1999
2000 w->setAttribute(Qt::WA_WState_InPaintEvent);
2001
2002 if (!(w->objectName() == "KLineEditButton")) {
2003 w->setAttribute(Qt::WA_OpaquePaintEvent);
2004 }
2005
2006 w->installEventFilter(view);
2007
2008 if (!recurse) {
2009 return;
2010 }
2011 if (qobject_cast<KHTMLView *>(w)) {
2012 handleWidget(static_cast<KHTMLView *>(w)->widget(), view, false);
2013 handleWidget(static_cast<KHTMLView *>(w)->horizontalScrollBar(), view, false);
2014 handleWidget(static_cast<KHTMLView *>(w)->verticalScrollBar(), view, false);
2015 return;
2016 }
2017
2018 QObjectList children = w->children();
2019 foreach (QObject *object, children) {
2020 QWidget *widget = qobject_cast<QWidget *>(object);
2021 if (widget) {
2022 handleWidget(widget, view);
2023 }
2024 }
2025 }
2026
2027 class KHTMLBackingStoreHackWidget : public QWidget
2028 {
2029 public:
publicEvent(QEvent * e)2030 void publicEvent(QEvent *e)
2031 {
2032 QWidget::event(e);
2033 }
2034 };
2035
viewportEvent(QEvent * e)2036 bool KHTMLView::viewportEvent(QEvent *e)
2037 {
2038 switch (e->type()) {
2039 // those must not be dispatched to the specialized handlers
2040 // as widgetEvent() already took care of that
2041 case QEvent::MouseButtonPress:
2042 case QEvent::MouseButtonRelease:
2043 case QEvent::MouseButtonDblClick:
2044 case QEvent::MouseMove:
2045 #ifndef QT_NO_WHEELEVENT
2046 case QEvent::Wheel:
2047 #endif
2048 case QEvent::ContextMenu:
2049 case QEvent::DragEnter:
2050 case QEvent::DragMove:
2051 case QEvent::DragLeave:
2052 case QEvent::Drop:
2053 return false;
2054 default:
2055 break;
2056 }
2057 return QScrollArea::viewportEvent(e);
2058 }
2059
setInPaintEventFlag(QWidget * w,bool b=true,bool recurse=true)2060 static void setInPaintEventFlag(QWidget *w, bool b = true, bool recurse = true)
2061 {
2062 w->setAttribute(Qt::WA_WState_InPaintEvent, b);
2063
2064 if (!recurse) {
2065 return;
2066 }
2067 if (qobject_cast<KHTMLView *>(w)) {
2068 setInPaintEventFlag(static_cast<KHTMLView *>(w)->widget(), b, false);
2069 setInPaintEventFlag(static_cast<KHTMLView *>(w)->horizontalScrollBar(), b, false);
2070 setInPaintEventFlag(static_cast<KHTMLView *>(w)->verticalScrollBar(), b, false);
2071 return;
2072 }
2073
2074 foreach (QObject *cw, w->children()) {
2075 if (cw->isWidgetType() && ! static_cast<QWidget *>(cw)->isWindow()
2076 && !(static_cast<QWidget *>(cw)->windowModality() & Qt::ApplicationModal)) {
2077 setInPaintEventFlag(static_cast<QWidget *>(cw), b);
2078 }
2079 }
2080 }
2081
eventFilter(QObject * o,QEvent * e)2082 bool KHTMLView::eventFilter(QObject *o, QEvent *e)
2083 {
2084 if (e->type() == QEvent::ShortcutOverride) {
2085 QKeyEvent *ke = (QKeyEvent *) e;
2086 if (m_part->isEditable() || m_part->isCaretMode()
2087 || (m_part->xmlDocImpl() && m_part->xmlDocImpl()->focusNode()
2088 && m_part->xmlDocImpl()->focusNode()->isContentEditable())) {
2089 if ((ke->modifiers() & Qt::ControlModifier) || (ke->modifiers() & Qt::ShiftModifier)) {
2090 switch (ke->key()) {
2091 case Qt::Key_Left:
2092 case Qt::Key_Right:
2093 case Qt::Key_Up:
2094 case Qt::Key_Down:
2095 case Qt::Key_Home:
2096 case Qt::Key_End:
2097 ke->accept();
2098 return true;
2099 default:
2100 break;
2101 }
2102 }
2103 }
2104 }
2105
2106 if (e->type() == QEvent::Leave) {
2107 if (d->cursorIconWidget) {
2108 d->cursorIconWidget->hide();
2109 }
2110 m_part->resetHoverText();
2111 }
2112
2113 QWidget *view = widget();
2114 if (o == view) {
2115 if (widgetEvent(e)) {
2116 return true;
2117 } else if (e->type() == QEvent::Resize) {
2118 updateScrollBars();
2119 return false;
2120 }
2121 } else if (o->isWidgetType()) {
2122 QWidget *v = static_cast<QWidget *>(o);
2123 QWidget *c = v;
2124 while (v && v != view) {
2125 c = v;
2126 v = v->parentWidget();
2127 }
2128 KHTMLWidget *k = dynamic_cast<KHTMLWidget *>(c);
2129 if (v && k && k->m_kwp->isRedirected()) {
2130 bool block = false;
2131 QWidget *w = static_cast<QWidget *>(o);
2132 switch (e->type()) {
2133 case QEvent::UpdateRequest: {
2134 // implicitly call qt_syncBackingStore(w)
2135 static_cast<KHTMLBackingStoreHackWidget *>(w)->publicEvent(e);
2136 block = true;
2137 break;
2138 }
2139 case QEvent::UpdateLater:
2140 // no break;
2141 case QEvent::Paint:
2142 if (!allowWidgetPaintEvents) {
2143 // eat the event. Like this we can control exactly when the widget
2144 // gets repainted.
2145 block = true;
2146 int x = 0, y = 0;
2147 QWidget *v = w;
2148 while (v && v->parentWidget() != view) {
2149 x += v->x();
2150 y += v->y();
2151 v = v->parentWidget();
2152 }
2153
2154 QPoint ap = k->m_kwp->absolutePos();
2155 x += ap.x();
2156 y += ap.y();
2157 }
2158 break;
2159 case QEvent::MouseMove:
2160 case QEvent::MouseButtonPress:
2161 case QEvent::MouseButtonRelease:
2162 case QEvent::MouseButtonDblClick: {
2163
2164 if (0 && w->parentWidget() == view && !qobject_cast<QScrollBar *>(w) && !::qobject_cast<QScrollBar *>(w)) {
2165 QMouseEvent *me = static_cast<QMouseEvent *>(e);
2166 QPoint pt = w->mapTo(view, me->pos());
2167 QMouseEvent me2(me->type(), pt, me->button(), me->buttons(), me->modifiers());
2168
2169 if (e->type() == QEvent::MouseMove) {
2170 mouseMoveEvent(&me2);
2171 } else if (e->type() == QEvent::MouseButtonPress) {
2172 mousePressEvent(&me2);
2173 } else if (e->type() == QEvent::MouseButtonRelease) {
2174 mouseReleaseEvent(&me2);
2175 } else {
2176 mouseDoubleClickEvent(&me2);
2177 }
2178 block = true;
2179 }
2180 break;
2181 }
2182 case QEvent::KeyPress:
2183 case QEvent::KeyRelease:
2184 if (w->parentWidget() == view && !qobject_cast<QScrollBar *>(w)) {
2185 QKeyEvent *ke = static_cast<QKeyEvent *>(e);
2186 if (e->type() == QEvent::KeyPress) {
2187 keyPressEvent(ke);
2188 ke->accept();
2189 } else {
2190 keyReleaseEvent(ke);
2191 ke->accept();
2192 }
2193 block = true;
2194 }
2195
2196 if (qobject_cast<KUrlRequester *>(w->parentWidget()) &&
2197 e->type() == QEvent::KeyPress) {
2198 // Since keypress events on the upload widget will
2199 // be forwarded to the lineedit anyway,
2200 // block the original copy at this level to prevent
2201 // double-emissions of events it doesn't accept
2202 e->ignore();
2203 block = true;
2204 }
2205
2206 break;
2207 case QEvent::FocusIn:
2208 case QEvent::FocusOut: {
2209 QPoint dummy;
2210 KHTMLView *root = m_kwp->rootViewPos(dummy);
2211 if (!root) {
2212 root = this;
2213 }
2214 block = static_cast<QFocusEvent *>(e)->reason() != Qt::MouseFocusReason || root->underMouse();
2215 break;
2216 }
2217 default:
2218 break;
2219 }
2220 if (block) {
2221 //qDebug("eating event");
2222 return true;
2223 }
2224 }
2225 }
2226
2227 // qCDebug(KHTML_LOG) <<"passing event on to sv event filter object=" << o->className() << " event=" << e->type();
2228 return QScrollArea::eventFilter(o, e);
2229 }
2230
widgetEvent(QEvent * e)2231 bool KHTMLView::widgetEvent(QEvent *e)
2232 {
2233 switch (e->type()) {
2234 case QEvent::MouseButtonPress:
2235 case QEvent::MouseButtonRelease:
2236 case QEvent::MouseButtonDblClick:
2237 case QEvent::MouseMove:
2238 case QEvent::Paint:
2239 #ifndef QT_NO_WHEELEVENT
2240 case QEvent::Wheel:
2241 #endif
2242 case QEvent::ContextMenu:
2243 case QEvent::DragEnter:
2244 case QEvent::DragMove:
2245 case QEvent::DragLeave:
2246 case QEvent::Drop:
2247 return QFrame::event(e);
2248 case QEvent::ChildPolished: {
2249 // we need to install an event filter on all children of the widget() to
2250 // be able to get correct stacking of children within the document.
2251 QObject *c = static_cast<QChildEvent *>(e)->child();
2252 if (c->isWidgetType()) {
2253 QWidget *w = static_cast<QWidget *>(c);
2254 // don't install the event filter on toplevels
2255 if (!(w->windowFlags() & Qt::Window) && !(w->windowModality() & Qt::ApplicationModal)) {
2256 KHTMLWidget *k = dynamic_cast<KHTMLWidget *>(w);
2257 if (k && k->m_kwp->isRedirected()) {
2258 w->unsetCursor();
2259 handleWidget(w, this);
2260 }
2261 }
2262 }
2263 break;
2264 }
2265 case QEvent::Move: {
2266 if (static_cast<QMoveEvent *>(e)->pos() != QPoint(0, 0)) {
2267 widget()->move(0, 0);
2268 updateScrollBars();
2269 return true;
2270 }
2271 break;
2272 }
2273 default:
2274 break;
2275 }
2276 return false;
2277 }
2278
hasLayoutPending()2279 bool KHTMLView::hasLayoutPending()
2280 {
2281 return d->layoutTimerId && !d->firstLayoutPending;
2282 }
2283
nodeUnderMouse() const2284 DOM::NodeImpl *KHTMLView::nodeUnderMouse() const
2285 {
2286 return d->underMouse;
2287 }
2288
nonSharedNodeUnderMouse() const2289 DOM::NodeImpl *KHTMLView::nonSharedNodeUnderMouse() const
2290 {
2291 return d->underMouseNonShared;
2292 }
2293
scrollTo(const QRect & bounds)2294 bool KHTMLView::scrollTo(const QRect &bounds)
2295 {
2296 d->scrollingSelf = true; // so scroll events get ignored
2297
2298 int x, y, xe, ye;
2299 x = bounds.left();
2300 y = bounds.top();
2301 xe = bounds.right();
2302 ye = bounds.bottom();
2303
2304 //qCDebug(KHTML_LOG)<<"scrolling coords: x="<<x<<" y="<<y<<" width="<<xe-x<<" height="<<ye-y;
2305
2306 int deltax;
2307 int deltay;
2308
2309 int curHeight = visibleHeight();
2310 int curWidth = visibleWidth();
2311
2312 if (ye - y > curHeight - d->borderY) {
2313 ye = y + curHeight - d->borderY;
2314 }
2315
2316 if (xe - x > curWidth - d->borderX) {
2317 xe = x + curWidth - d->borderX;
2318 }
2319
2320 // is xpos of target left of the view's border?
2321 if (x < contentsX() + d->borderX) {
2322 deltax = x - contentsX() - d->borderX;
2323 }
2324 // is xpos of target right of the view's right border?
2325 else if (xe + d->borderX > contentsX() + curWidth) {
2326 deltax = xe + d->borderX - (contentsX() + curWidth);
2327 } else {
2328 deltax = 0;
2329 }
2330
2331 // is ypos of target above upper border?
2332 if (y < contentsY() + d->borderY) {
2333 deltay = y - contentsY() - d->borderY;
2334 }
2335 // is ypos of target below lower border?
2336 else if (ye + d->borderY > contentsY() + curHeight) {
2337 deltay = ye + d->borderY - (contentsY() + curHeight);
2338 } else {
2339 deltay = 0;
2340 }
2341
2342 int maxx = curWidth - d->borderX;
2343 int maxy = curHeight - d->borderY;
2344
2345 int scrollX, scrollY;
2346
2347 scrollX = deltax > 0 ? (deltax > maxx ? maxx : deltax) : deltax == 0 ? 0 : (deltax > -maxx ? deltax : -maxx);
2348 scrollY = deltay > 0 ? (deltay > maxy ? maxy : deltay) : deltay == 0 ? 0 : (deltay > -maxy ? deltay : -maxy);
2349
2350 if (contentsX() + scrollX < 0) {
2351 scrollX = -contentsX();
2352 } else if (contentsWidth() - visibleWidth() - contentsX() < scrollX) {
2353 scrollX = contentsWidth() - visibleWidth() - contentsX();
2354 }
2355
2356 if (contentsY() + scrollY < 0) {
2357 scrollY = -contentsY();
2358 } else if (contentsHeight() - visibleHeight() - contentsY() < scrollY) {
2359 scrollY = contentsHeight() - visibleHeight() - contentsY();
2360 }
2361
2362 horizontalScrollBar()->setValue(horizontalScrollBar()->value() + scrollX);
2363 verticalScrollBar()->setValue(verticalScrollBar()->value() + scrollY);
2364
2365 d->scrollingSelf = false;
2366
2367 if ((abs(deltax) <= maxx) && (abs(deltay) <= maxy)) {
2368 return true;
2369 } else {
2370 return false;
2371 }
2372
2373 }
2374
focusNextPrevNode(bool next)2375 bool KHTMLView::focusNextPrevNode(bool next)
2376 {
2377 // Sets the focus node of the document to be the node after (or if
2378 // next is false, before) the current focus node. Only nodes that
2379 // are selectable (i.e. for which isFocusable() returns true) are
2380 // taken into account, and the order used is that specified in the
2381 // HTML spec (see DocumentImpl::nextFocusNode() and
2382 // DocumentImpl::previousFocusNode() for details).
2383
2384 DocumentImpl *doc = m_part->xmlDocImpl();
2385 NodeImpl *oldFocusNode = doc->focusNode();
2386
2387 // See whether we're in the middle of a detach, or hiding of the
2388 // widget. In this case, we will just clear focus, being careful not to emit events
2389 // or update rendering. Doing this also prevents the code below from going bonkers with
2390 // oldFocusNode not actually being focusable, etc.
2391 if (oldFocusNode) {
2392 if ((oldFocusNode->renderer() && !oldFocusNode->renderer()->parent())
2393 || !oldFocusNode->isTabFocusable()) {
2394 doc->quietResetFocus();
2395 return true;
2396 }
2397 }
2398
2399 #if 1
2400 // If the user has scrolled the document, then instead of picking
2401 // the next focusable node in the document, use the first one that
2402 // is within the visible area (if possible).
2403 if (d->scrollBarMoved) {
2404 NodeImpl *toFocus;
2405 if (next) {
2406 toFocus = doc->nextFocusNode(oldFocusNode);
2407 } else {
2408 toFocus = doc->previousFocusNode(oldFocusNode);
2409 }
2410
2411 if (!toFocus && oldFocusNode) {
2412 if (next) {
2413 toFocus = doc->nextFocusNode(nullptr);
2414 } else {
2415 toFocus = doc->previousFocusNode(nullptr);
2416 }
2417 }
2418
2419 while (toFocus && toFocus != oldFocusNode) {
2420
2421 QRect focusNodeRect = toFocus->getRect();
2422 if ((focusNodeRect.left() > contentsX()) && (focusNodeRect.right() < contentsX() + visibleWidth()) &&
2423 (focusNodeRect.top() > contentsY()) && (focusNodeRect.bottom() < contentsY() + visibleHeight())) {
2424 {
2425 QRect r = toFocus->getRect();
2426 ensureVisible(r.right(), r.bottom());
2427 ensureVisible(r.left(), r.top());
2428 d->scrollBarMoved = false;
2429 d->tabMovePending = false;
2430 d->lastTabbingDirection = next;
2431 d->pseudoFocusNode = KHTMLViewPrivate::PFNone;
2432 m_part->xmlDocImpl()->setFocusNode(toFocus);
2433 Node guard(toFocus);
2434 if (!toFocus->hasOneRef()) {
2435 emit m_part->nodeActivated(Node(toFocus));
2436 }
2437 return true;
2438 }
2439 }
2440 if (next) {
2441 toFocus = doc->nextFocusNode(toFocus);
2442 } else {
2443 toFocus = doc->previousFocusNode(toFocus);
2444 }
2445
2446 if (!toFocus && oldFocusNode) {
2447 if (next) {
2448 toFocus = doc->nextFocusNode(nullptr);
2449 } else {
2450 toFocus = doc->previousFocusNode(nullptr);
2451 }
2452 }
2453 }
2454
2455 d->scrollBarMoved = false;
2456 }
2457 #endif
2458
2459 if (!oldFocusNode && d->pseudoFocusNode == KHTMLViewPrivate::PFNone) {
2460 ensureVisible(contentsX(), next ? 0 : contentsHeight());
2461 d->scrollBarMoved = false;
2462 d->pseudoFocusNode = next ? KHTMLViewPrivate::PFTop : KHTMLViewPrivate::PFBottom;
2463 return true;
2464 }
2465
2466 NodeImpl *newFocusNode = nullptr;
2467
2468 if (d->tabMovePending && next != d->lastTabbingDirection) {
2469 //qCDebug(KHTML_LOG) << " tab move pending and tabbing direction changed!\n";
2470 newFocusNode = oldFocusNode;
2471 } else if (next) {
2472 if (oldFocusNode || d->pseudoFocusNode == KHTMLViewPrivate::PFTop) {
2473 newFocusNode = doc->nextFocusNode(oldFocusNode);
2474 }
2475 } else {
2476 if (oldFocusNode || d->pseudoFocusNode == KHTMLViewPrivate::PFBottom) {
2477 newFocusNode = doc->previousFocusNode(oldFocusNode);
2478 }
2479 }
2480
2481 bool targetVisible = false;
2482 if (!newFocusNode) {
2483 if (next) {
2484 targetVisible = scrollTo(QRect(contentsX() + visibleWidth() / 2, contentsHeight() - d->borderY, 0, 0));
2485 } else {
2486 targetVisible = scrollTo(QRect(contentsX() + visibleWidth() / 2, d->borderY, 0, 0));
2487 }
2488 } else {
2489 // if it's an editable element, activate the caret
2490 if (!m_part->isCaretMode() && newFocusNode->isContentEditable()) {
2491 // qCDebug(KHTML_LOG) << "show caret! fn: " << newFocusNode->nodeName().string();
2492 m_part->clearCaretRectIfNeeded();
2493 m_part->d->editor_context.m_selection.moveTo(Position(newFocusNode, 0L));
2494 m_part->setCaretVisible(true);
2495 } else {
2496 m_part->setCaretVisible(false);
2497 // qCDebug(KHTML_LOG) << "hide caret! fn: " << newFocusNode->nodeName().string();
2498 }
2499 m_part->notifySelectionChanged();
2500
2501 targetVisible = scrollTo(newFocusNode->getRect());
2502 }
2503
2504 if (targetVisible) {
2505 //qCDebug(KHTML_LOG) << " target reached.\n";
2506 d->tabMovePending = false;
2507
2508 m_part->xmlDocImpl()->setFocusNode(newFocusNode);
2509 if (newFocusNode) {
2510 Node guard(newFocusNode);
2511 if (!newFocusNode->hasOneRef()) {
2512 emit m_part->nodeActivated(Node(newFocusNode));
2513 }
2514 return true;
2515 } else {
2516 d->pseudoFocusNode = next ? KHTMLViewPrivate::PFBottom : KHTMLViewPrivate::PFTop;
2517 return false;
2518 }
2519 } else {
2520 if (!d->tabMovePending) {
2521 d->lastTabbingDirection = next;
2522 }
2523 d->tabMovePending = true;
2524 return true;
2525 }
2526 }
2527
displayAccessKeys()2528 void KHTMLView::displayAccessKeys()
2529 {
2530 QVector< QChar > taken;
2531 displayAccessKeys(nullptr, this, taken, false);
2532 displayAccessKeys(nullptr, this, taken, true);
2533 }
2534
displayAccessKeys(KHTMLView * caller,KHTMLView * origview,QVector<QChar> & taken,bool use_fallbacks)2535 void KHTMLView::displayAccessKeys(KHTMLView *caller, KHTMLView *origview, QVector< QChar > &taken, bool use_fallbacks)
2536 {
2537 QMap< ElementImpl *, QChar > fallbacks;
2538 if (use_fallbacks) {
2539 fallbacks = buildFallbackAccessKeys();
2540 }
2541 for (NodeImpl *n = m_part->xmlDocImpl(); n != nullptr; n = n->traverseNextNode()) {
2542 if (n->isElementNode()) {
2543 ElementImpl *en = static_cast< ElementImpl * >(n);
2544 DOMString s = en->getAttribute(ATTR_ACCESSKEY);
2545 QString accesskey;
2546 if (s.length() == 1) {
2547 QChar a = s.string()[ 0 ].toUpper();
2548 if (qFind(taken.begin(), taken.end(), a) == taken.end()) { // !contains
2549 accesskey = a;
2550 }
2551 }
2552 if (accesskey.isNull() && fallbacks.contains(en)) {
2553 QChar a = fallbacks[ en ].toUpper();
2554 if (qFind(taken.begin(), taken.end(), a) == taken.end()) { // !contains
2555 accesskey = QString("<qt><i>") + a + "</i></qt>";
2556 }
2557 }
2558 if (!accesskey.isNull()) {
2559 QRect rec = en->getRect();
2560 QLabel *lab = new QLabel(accesskey, widget());
2561 lab->setAttribute(Qt::WA_DeleteOnClose);
2562 lab->setObjectName("KHTMLAccessKey");
2563 connect(origview, SIGNAL(hideAccessKeys()), lab, SLOT(close()));
2564 connect(this, SIGNAL(repaintAccessKeys()), lab, SLOT(repaint()));
2565 lab->setPalette(QToolTip::palette());
2566 lab->setLineWidth(2);
2567 lab->setFrameStyle(QFrame::Box | QFrame::Plain);
2568 lab->setContentsMargins(3, 3, 3, 3);
2569 lab->adjustSize();
2570 lab->setParent(widget());
2571 lab->setAutoFillBackground(true);
2572 lab->move(
2573 qMin(rec.left() + rec.width() / 2 - contentsX(), contentsWidth() - lab->width()),
2574 qMin(rec.top() + rec.height() / 2 - contentsY(), contentsHeight() - lab->height()));
2575 lab->show();
2576 taken.append(accesskey[ 0 ]);
2577 }
2578 }
2579 }
2580 if (use_fallbacks) {
2581 return;
2582 }
2583
2584 QList<KParts::ReadOnlyPart *> frames = m_part->frames();
2585 foreach (KParts::ReadOnlyPart *cur, frames) {
2586 if (!qobject_cast<KHTMLPart *>(cur)) {
2587 continue;
2588 }
2589 KHTMLPart *part = static_cast< KHTMLPart * >(cur);
2590 if (part->view() && part->view() != caller) {
2591 part->view()->displayAccessKeys(this, origview, taken, use_fallbacks);
2592 }
2593 }
2594
2595 // pass up to the parent
2596 if (m_part->parentPart() && m_part->parentPart()->view()
2597 && m_part->parentPart()->view() != caller) {
2598 m_part->parentPart()->view()->displayAccessKeys(this, origview, taken, use_fallbacks);
2599 }
2600 }
2601
isScrollingFromMouseWheel() const2602 bool KHTMLView::isScrollingFromMouseWheel() const
2603 {
2604 return d->scrollingFromWheel != QPoint(-1, -1);
2605 }
2606
accessKeysTimeout()2607 void KHTMLView::accessKeysTimeout()
2608 {
2609 d->accessKeysActivated = false;
2610 d->accessKeysPreActivate = false;
2611 m_part->setStatusBarText(QString(), KHTMLPart::BarOverrideText);
2612 emit hideAccessKeys();
2613 }
2614
2615 // Handling of the HTML accesskey attribute.
handleAccessKey(const QKeyEvent * ev)2616 bool KHTMLView::handleAccessKey(const QKeyEvent *ev)
2617 {
2618 // Qt interprets the keyevent also with the modifiers, and ev->text() matches that,
2619 // but this code must act as if the modifiers weren't pressed
2620 QChar c;
2621 if (ev->key() >= Qt::Key_A && ev->key() <= Qt::Key_Z) {
2622 c = 'A' + ev->key() - Qt::Key_A;
2623 } else if (ev->key() >= Qt::Key_0 && ev->key() <= Qt::Key_9) {
2624 c = '0' + ev->key() - Qt::Key_0;
2625 } else {
2626 // TODO fake XKeyEvent and XLookupString ?
2627 // This below seems to work e.g. for eacute though.
2628 if (ev->text().length() == 1) {
2629 c = ev->text()[ 0 ];
2630 }
2631 }
2632 if (c.isNull()) {
2633 return false;
2634 }
2635 return focusNodeWithAccessKey(c);
2636 }
2637
focusNodeWithAccessKey(QChar c,KHTMLView * caller)2638 bool KHTMLView::focusNodeWithAccessKey(QChar c, KHTMLView *caller)
2639 {
2640 DocumentImpl *doc = m_part->xmlDocImpl();
2641 if (!doc) {
2642 return false;
2643 }
2644 ElementImpl *node = doc->findAccessKeyElement(c);
2645 if (!node) {
2646 QList<KParts::ReadOnlyPart *> frames = m_part->frames();
2647 foreach (KParts::ReadOnlyPart *cur, frames) {
2648 if (!qobject_cast<KHTMLPart *>(cur)) {
2649 continue;
2650 }
2651 KHTMLPart *part = static_cast< KHTMLPart * >(cur);
2652 if (part->view() && part->view() != caller
2653 && part->view()->focusNodeWithAccessKey(c, this)) {
2654 return true;
2655 }
2656 }
2657 // pass up to the parent
2658 if (m_part->parentPart() && m_part->parentPart()->view()
2659 && m_part->parentPart()->view() != caller
2660 && m_part->parentPart()->view()->focusNodeWithAccessKey(c, this)) {
2661 return true;
2662 }
2663 if (caller == nullptr) { // the active frame (where the accesskey was pressed)
2664 const QMap< ElementImpl *, QChar > fallbacks = buildFallbackAccessKeys();
2665 for (QMap< ElementImpl *, QChar >::ConstIterator it = fallbacks.begin();
2666 it != fallbacks.end();
2667 ++it)
2668 if (*it == c) {
2669 node = it.key();
2670 break;
2671 }
2672 }
2673 if (node == nullptr) {
2674 return false;
2675 }
2676 }
2677
2678 // Scroll the view as necessary to ensure that the new focus node is visible
2679
2680 QRect r = node->getRect();
2681 ensureVisible(r.right(), r.bottom());
2682 ensureVisible(r.left(), r.top());
2683
2684 Node guard(node);
2685 if (node->isFocusable()) {
2686 if (node->id() == ID_LABEL) {
2687 // if Accesskey is a label, give focus to the label's referrer.
2688 node = static_cast<ElementImpl *>(static_cast< HTMLLabelElementImpl * >(node)->getFormElement());
2689 if (!node) {
2690 return true;
2691 }
2692 guard = node;
2693 }
2694 // Set focus node on the document
2695 m_part->xmlDocImpl()->setFocusNode(node);
2696
2697 if (node != nullptr && node->hasOneRef()) { // deleted, only held by guard
2698 return true;
2699 }
2700 emit m_part->nodeActivated(Node(node));
2701 if (node != nullptr && node->hasOneRef()) {
2702 return true;
2703 }
2704 }
2705
2706 switch (node->id()) {
2707 case ID_A:
2708 static_cast< HTMLAnchorElementImpl * >(node)->click();
2709 break;
2710 case ID_INPUT:
2711 static_cast< HTMLInputElementImpl * >(node)->click();
2712 break;
2713 case ID_BUTTON:
2714 static_cast< HTMLButtonElementImpl * >(node)->click();
2715 break;
2716 case ID_AREA:
2717 static_cast< HTMLAreaElementImpl * >(node)->click();
2718 break;
2719 case ID_TEXTAREA:
2720 break; // just focusing it is enough
2721 case ID_LEGEND:
2722 // TODO
2723 break;
2724 }
2725 return true;
2726 }
2727
getElementText(NodeImpl * start,bool after)2728 static QString getElementText(NodeImpl *start, bool after)
2729 {
2730 QString ret; // nextSibling(), to go after e.g. </select>
2731 for (NodeImpl *n = after ? start->nextSibling() : start->traversePreviousNode();
2732 n != nullptr;
2733 n = after ? n->traverseNextNode() : n->traversePreviousNode()) {
2734 if (n->isTextNode()) {
2735 if (after) {
2736 ret += static_cast< TextImpl * >(n)->toString().string();
2737 } else {
2738 ret.prepend(static_cast< TextImpl * >(n)->toString().string());
2739 }
2740 } else {
2741 switch (n->id()) {
2742 case ID_A:
2743 case ID_FONT:
2744 case ID_TT:
2745 case ID_U:
2746 case ID_B:
2747 case ID_I:
2748 case ID_S:
2749 case ID_STRIKE:
2750 case ID_BIG:
2751 case ID_SMALL:
2752 case ID_EM:
2753 case ID_STRONG:
2754 case ID_DFN:
2755 case ID_CODE:
2756 case ID_SAMP:
2757 case ID_KBD:
2758 case ID_VAR:
2759 case ID_CITE:
2760 case ID_ABBR:
2761 case ID_ACRONYM:
2762 case ID_SUB:
2763 case ID_SUP:
2764 case ID_SPAN:
2765 case ID_NOBR:
2766 case ID_WBR:
2767 break;
2768 case ID_TD:
2769 if (ret.trimmed().isEmpty()) {
2770 break;
2771 }
2772 // fall through
2773 default:
2774 return ret.simplified();
2775 }
2776 }
2777 }
2778 return ret.simplified();
2779 }
2780
buildLabels(NodeImpl * start)2781 static QMap< NodeImpl *, QString > buildLabels(NodeImpl *start)
2782 {
2783 QMap< NodeImpl *, QString > ret;
2784 for (NodeImpl *n = start;
2785 n != nullptr;
2786 n = n->traverseNextNode()) {
2787 if (n->id() == ID_LABEL) {
2788 HTMLLabelElementImpl *label = static_cast< HTMLLabelElementImpl * >(n);
2789 NodeImpl *labelfor = label->getFormElement();
2790 if (labelfor) {
2791 ret[ labelfor ] = label->innerText().string().simplified();
2792 }
2793 }
2794 }
2795 return ret;
2796 }
2797
2798 namespace khtml
2799 {
2800 struct AccessKeyData {
2801 ElementImpl *element;
2802 QString text;
2803 QString url;
2804 int priority; // 10(highest) - 0(lowest)
2805 };
2806 }
2807
buildFallbackAccessKeys() const2808 QMap< ElementImpl *, QChar > KHTMLView::buildFallbackAccessKeys() const
2809 {
2810 // build a list of all possible candidate elements that could use an accesskey
2811 QLinkedList< AccessKeyData > data; // Note: this has to be a list type that keep iterators valid
2812 // when other entries are removed
2813 QMap< NodeImpl *, QString > labels = buildLabels(m_part->xmlDocImpl());
2814 QMap< QString, QChar > hrefs;
2815
2816 for (NodeImpl *n = m_part->xmlDocImpl();
2817 n != nullptr;
2818 n = n->traverseNextNode()) {
2819 if (n->isElementNode()) {
2820 ElementImpl *element = static_cast< ElementImpl * >(n);
2821 if (element->renderer() == nullptr) {
2822 continue; // not visible
2823 }
2824 QString text;
2825 QString url;
2826 int priority = 0;
2827 bool ignore = false;
2828 bool text_after = false;
2829 bool text_before = false;
2830 switch (element->id()) {
2831 case ID_A:
2832 url = element->getAttribute(ATTR_HREF).trimSpaces().string();
2833 if (url.isEmpty()) { // doesn't have href, it's only an anchor
2834 continue;
2835 }
2836 text = static_cast< HTMLElementImpl * >(element)->innerText().string().simplified();
2837 priority = 2;
2838 break;
2839 case ID_INPUT: {
2840 HTMLInputElementImpl *in = static_cast< HTMLInputElementImpl * >(element);
2841 switch (in->inputType()) {
2842 case HTMLInputElementImpl::SUBMIT:
2843 text = in->value().string();
2844 if (text.isEmpty()) {
2845 text = i18n("Submit");
2846 }
2847 priority = 7;
2848 break;
2849 case HTMLInputElementImpl::IMAGE:
2850 text = in->altText().string();
2851 priority = 7;
2852 break;
2853 case HTMLInputElementImpl::BUTTON:
2854 text = in->value().string();
2855 priority = 5;
2856 break;
2857 case HTMLInputElementImpl::RESET:
2858 text = in->value().string();
2859 if (text.isEmpty()) {
2860 text = i18n("Reset");
2861 }
2862 priority = 5;
2863 break;
2864 case HTMLInputElementImpl::HIDDEN:
2865 ignore = true;
2866 break;
2867 case HTMLInputElementImpl::CHECKBOX:
2868 case HTMLInputElementImpl::RADIO:
2869 text_after = true;
2870 priority = 5;
2871 break;
2872 case HTMLInputElementImpl::TEXT:
2873 case HTMLInputElementImpl::PASSWORD:
2874 case HTMLInputElementImpl::FILE:
2875 text_before = true;
2876 priority = 5;
2877 break;
2878 default:
2879 priority = 5;
2880 break;
2881 }
2882 break;
2883 }
2884 case ID_BUTTON:
2885 text = static_cast< HTMLElementImpl * >(element)->innerText().string().simplified();
2886 switch (static_cast< HTMLButtonElementImpl * >(element)->buttonType()) {
2887 case HTMLButtonElementImpl::SUBMIT:
2888 if (text.isEmpty()) {
2889 text = i18n("Submit");
2890 }
2891 priority = 7;
2892 break;
2893 case HTMLButtonElementImpl::RESET:
2894 if (text.isEmpty()) {
2895 text = i18n("Reset");
2896 }
2897 priority = 5;
2898 break;
2899 default:
2900 priority = 5;
2901 break;
2902 }
2903 break;
2904 case ID_SELECT: // these don't have accesskey attribute, but quick access may be handy
2905 text_before = true;
2906 text_after = true;
2907 priority = 5;
2908 break;
2909 case ID_FRAME:
2910 ignore = true;
2911 break;
2912 default:
2913 ignore = !element->isFocusable();
2914 priority = 2;
2915 break;
2916 }
2917 if (ignore) {
2918 continue;
2919 }
2920
2921 // build map of manually assigned accesskeys and their targets
2922 DOMString akey = element->getAttribute(ATTR_ACCESSKEY);
2923 if (akey.length() == 1) {
2924 hrefs[url] = akey.string()[ 0 ].toUpper();
2925 continue; // has accesskey set, ignore
2926 }
2927 if (text.isNull() && labels.contains(element)) {
2928 text = labels[ element ];
2929 }
2930 if (text.isNull() && text_before) {
2931 text = getElementText(element, false);
2932 }
2933 if (text.isNull() && text_after) {
2934 text = getElementText(element, true);
2935 }
2936 text = text.trimmed();
2937 // increase priority of items which have explicitly specified accesskeys in the config
2938 const QList< QPair< QString, QChar > > priorities
2939 = m_part->settings()->fallbackAccessKeysAssignments();
2940 for (QList< QPair< QString, QChar > >::ConstIterator it = priorities.begin();
2941 it != priorities.end();
2942 ++it) {
2943 if (text == (*it).first) {
2944 priority = 10;
2945 }
2946 }
2947 AccessKeyData tmp = { element, text, url, priority };
2948 data.append(tmp);
2949 }
2950 }
2951
2952 QList< QChar > keys;
2953 for (char c = 'A'; c <= 'Z'; ++c) {
2954 keys << c;
2955 }
2956 for (char c = '0'; c <= '9'; ++c) {
2957 keys << c;
2958 }
2959 for (NodeImpl *n = m_part->xmlDocImpl();
2960 n != nullptr;
2961 n = n->traverseNextNode()) {
2962 if (n->isElementNode()) {
2963 ElementImpl *en = static_cast< ElementImpl * >(n);
2964 DOMString s = en->getAttribute(ATTR_ACCESSKEY);
2965 if (s.length() == 1) {
2966 QChar c = s.string()[ 0 ].toUpper();
2967 keys.removeAll(c); // remove manually assigned accesskeys
2968 }
2969 }
2970 }
2971
2972 QMap< ElementImpl *, QChar > ret;
2973 for (int priority = 10; priority >= 0; --priority) {
2974 for (QLinkedList< AccessKeyData >::Iterator it = data.begin();
2975 it != data.end();
2976 ) {
2977 if ((*it).priority != priority) {
2978 ++it;
2979 continue;
2980 }
2981 if (keys.isEmpty()) {
2982 break;
2983 }
2984 QString text = (*it).text;
2985 QChar key;
2986 const QString url = (*it).url;
2987 // an identical link already has an accesskey assigned
2988 if (hrefs.contains(url)) {
2989 it = data.erase(it);
2990 continue;
2991 }
2992 if (!text.isEmpty()) {
2993 const QList< QPair< QString, QChar > > priorities
2994 = m_part->settings()->fallbackAccessKeysAssignments();
2995 for (QList< QPair< QString, QChar > >::ConstIterator it = priorities.begin();
2996 it != priorities.end();
2997 ++it)
2998 if (text == (*it).first && keys.contains((*it).second)) {
2999 key = (*it).second;
3000 break;
3001 }
3002 }
3003 // try first to select the first character as the accesskey,
3004 // then first character of the following words,
3005 // and then simply the first free character
3006 if (key.isNull() && !text.isEmpty()) {
3007 const QStringList words = text.split(' ');
3008 for (QStringList::ConstIterator it = words.begin();
3009 it != words.end();
3010 ++it) {
3011 if (keys.contains((*it)[ 0 ].toUpper())) {
3012 key = (*it)[ 0 ].toUpper();
3013 break;
3014 }
3015 }
3016 }
3017 if (key.isNull() && !text.isEmpty()) {
3018 for (int i = 0; i < text.length(); ++i) {
3019 if (keys.contains(text[ i ].toUpper())) {
3020 key = text[ i ].toUpper();
3021 break;
3022 }
3023 }
3024 }
3025 if (key.isNull()) {
3026 key = keys.front();
3027 }
3028 ret[(*it).element ] = key;
3029 keys.removeAll(key);
3030 it = data.erase(it);
3031 // assign the same accesskey also to other elements pointing to the same url
3032 if (!url.isEmpty() && !url.startsWith("javascript:", Qt::CaseInsensitive)) {
3033 for (QLinkedList< AccessKeyData >::Iterator it2 = data.begin();
3034 it2 != data.end();
3035 ) {
3036 if ((*it2).url == url) {
3037 ret[(*it2).element ] = key;
3038 if (it == it2) {
3039 ++it;
3040 }
3041 it2 = data.erase(it2);
3042 } else {
3043 ++it2;
3044 }
3045 }
3046 }
3047 }
3048 }
3049 return ret;
3050 }
3051
setMediaType(const QString & medium)3052 void KHTMLView::setMediaType(const QString &medium)
3053 {
3054 m_medium = medium;
3055 }
3056
mediaType() const3057 QString KHTMLView::mediaType() const
3058 {
3059 return m_medium;
3060 }
3061
pagedMode() const3062 bool KHTMLView::pagedMode() const
3063 {
3064 return d->paged;
3065 }
3066
setWidgetVisible(RenderWidget * w,bool vis)3067 void KHTMLView::setWidgetVisible(RenderWidget *w, bool vis)
3068 {
3069 if (vis) {
3070 d->visibleWidgets.insert(w, w->widget());
3071 } else {
3072 d->visibleWidgets.remove(w);
3073 }
3074 }
3075
needsFullRepaint() const3076 bool KHTMLView::needsFullRepaint() const
3077 {
3078 return d->needsFullRepaint;
3079 }
3080
3081 namespace
3082 {
3083 class QPointerDeleter
3084 {
3085 public:
QPointerDeleter(QObject * o)3086 explicit QPointerDeleter(QObject *o) : obj(o) {}
~QPointerDeleter()3087 ~QPointerDeleter()
3088 {
3089 delete obj;
3090 }
3091 private:
3092 const QPointer<QObject> obj;
3093 };
3094 }
3095
print(bool quick)3096 void KHTMLView::print(bool quick)
3097 {
3098 QPrinter printer;
3099 print(&printer, quick);
3100 }
3101
print(QPrinter * _printer,bool quick)3102 void KHTMLView::print(QPrinter *_printer, bool quick)
3103 {
3104 if (!m_part->xmlDocImpl()) {
3105 return;
3106 }
3107 khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer());
3108 if (!root) {
3109 return;
3110 }
3111
3112 QPrinter &printer = *_printer;
3113 QPointer<QPrintDialog> dialog(new QPrintDialog(&printer, this));
3114 QPointer<KHTMLPrintSettings> printSettings(new KHTMLPrintSettings(dialog)); //XXX: doesn't save settings between prints like this
3115 dialog->setOptionTabs(QList<QWidget *>() << printSettings.data());
3116
3117 const QPointerDeleter dialogDeleter(dialog);
3118
3119 QString docname = m_part->xmlDocImpl()->URL().toDisplayString();
3120 if (!docname.isEmpty()) {
3121 docname = KStringHandler::csqueeze(docname, 80);
3122 }
3123
3124 if (quick || (dialog->exec() && dialog)) { /*'this' and thus dialog might have been deleted while exec()!*/
3125 viewport()->setCursor(Qt::WaitCursor); // only viewport(), no QApplication::, otherwise we get the busy cursor in kdeprint's dialogs
3126 // set up KPrinter
3127 printer.setFullPage(false);
3128 printer.setCreator(QString("KDE %1.%2.%3 HTML Library").arg(KHTML_VERSION_MAJOR).arg(KHTML_VERSION_MINOR).arg(KHTML_VERSION_PATCH));
3129 printer.setDocName(docname);
3130
3131 QPainter *p = new QPainter;
3132 p->begin(&printer);
3133 khtml::setPrintPainter(p);
3134
3135 m_part->xmlDocImpl()->setPaintDevice(&printer);
3136 QString oldMediaType = mediaType();
3137 setMediaType("print");
3138 // We ignore margin settings for html and body when printing
3139 // and use the default margins from the print-system
3140 // (In Qt 3.0.x the default margins are hardcoded in Qt)
3141 m_part->xmlDocImpl()->setPrintStyleSheet(printSettings->printFriendly() ?
3142 "* { background-image: none !important;"
3143 " background-color: white !important;"
3144 " color: black !important; }"
3145 "body { margin: 0px !important; }"
3146 "html { margin: 0px !important; }" :
3147 "body { margin: 0px !important; }"
3148 "html { margin: 0px !important; }"
3149 );
3150
3151 // qCDebug(KHTML_LOG) << "printing: physical page width = " << printer.width()
3152 // << " height = " << printer.height();
3153 root->setStaticMode(true);
3154 root->setPagedMode(true);
3155 root->setWidth(printer.width());
3156 // root->setHeight(printer.height());
3157 root->setPageTop(0);
3158 root->setPageBottom(0);
3159 d->paged = true;
3160
3161 m_part->xmlDocImpl()->styleSelector()->computeFontSizes(printer.logicalDpiY(), 100);
3162 m_part->xmlDocImpl()->updateStyleSelector();
3163 root->setPrintImages(printSettings->printImages());
3164 root->makePageBreakAvoidBlocks();
3165
3166 root->setNeedsLayoutAndMinMaxRecalc();
3167 root->layout();
3168
3169 // check sizes ask for action.. (scale or clip)
3170
3171 bool printHeader = printSettings->printHeader();
3172
3173 int headerHeight = 0;
3174 QFont headerFont("Sans Serif", 8);
3175
3176 QString headerLeft = QDate::currentDate().toString(Qt::DefaultLocaleShortDate);
3177 QString headerMid = docname;
3178 QString headerRight;
3179
3180 if (printHeader) {
3181 p->setFont(headerFont);
3182 headerHeight = (p->fontMetrics().lineSpacing() * 3) / 2;
3183 }
3184
3185 // ok. now print the pages.
3186 // qCDebug(KHTML_LOG) << "printing: html page width = " << root->docWidth()
3187 // << " height = " << root->docHeight();
3188 // qCDebug(KHTML_LOG) << "printing: margins left = " << printer.pageRect().left() - printer.paperRect().left()
3189 // << " top = " << printer.pageRect().top() - printer.paperRect().top();
3190 // qCDebug(KHTML_LOG) << "printing: paper width = " << printer.width()
3191 // << " height = " << printer.height();
3192 // if the width is too large to fit on the paper we just scale
3193 // the whole thing.
3194 int pageWidth = printer.width();
3195 int pageHeight = printer.height();
3196 p->setClipRect(0, 0, pageWidth, pageHeight);
3197
3198 pageHeight -= headerHeight;
3199
3200 #ifndef QT_NO_TRANSFORMATIONS
3201 bool scalePage = false;
3202 double scale = 0.0;
3203 if (root->docWidth() > printer.width()) {
3204 scalePage = true;
3205 scale = ((double) printer.width()) / ((double) root->docWidth());
3206 pageHeight = (int)(pageHeight / scale);
3207 pageWidth = (int)(pageWidth / scale);
3208 headerHeight = (int)(headerHeight / scale);
3209 }
3210 #endif
3211 // qCDebug(KHTML_LOG) << "printing: scaled html width = " << pageWidth
3212 // << " height = " << pageHeight;
3213
3214 root->setHeight(pageHeight);
3215 root->setPageBottom(pageHeight);
3216 root->setNeedsLayout(true);
3217 root->layoutIfNeeded();
3218 // m_part->slotDebugRenderTree();
3219
3220 // Squeeze header to make it it on the page.
3221 if (printHeader) {
3222 int available_width = printer.width() - 10 -
3223 2 * qMax(p->boundingRect(0, 0, printer.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerLeft).width(),
3224 p->boundingRect(0, 0, printer.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerRight).width());
3225 if (available_width < 150) {
3226 available_width = 150;
3227 }
3228 int mid_width;
3229 int squeeze = 120;
3230 do {
3231 headerMid = KStringHandler::csqueeze(docname, squeeze);
3232 mid_width = p->boundingRect(0, 0, printer.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerMid).width();
3233 squeeze -= 10;
3234 } while (mid_width > available_width);
3235 }
3236
3237 int top = 0;
3238 int bottom = 0;
3239 int page = 1;
3240 while (top < root->docHeight()) {
3241 if (top > 0) {
3242 printer.newPage();
3243 }
3244 #ifndef QT_NO_TRANSFORMATIONS
3245 if (scalePage) {
3246 p->scale(scale, scale);
3247 }
3248 #endif
3249 p->save();
3250 p->setClipRect(0, 0, pageWidth, headerHeight);
3251 if (printHeader) {
3252 int dy = p->fontMetrics().lineSpacing();
3253 p->setPen(Qt::black);
3254 p->setFont(headerFont);
3255
3256 headerRight = QString("#%1").arg(page);
3257
3258 p->drawText(0, 0, printer.width(), dy, Qt::AlignLeft, headerLeft);
3259 p->drawText(0, 0, printer.width(), dy, Qt::AlignHCenter, headerMid);
3260 p->drawText(0, 0, printer.width(), dy, Qt::AlignRight, headerRight);
3261 }
3262
3263 p->restore();
3264 p->translate(0, headerHeight - top);
3265
3266 bottom = top + pageHeight;
3267
3268 root->setPageTop(top);
3269 root->setPageBottom(bottom);
3270 root->setPageNumber(page);
3271
3272 root->layer()->paint(p, QRect(0, top, pageWidth, pageHeight));
3273 // qCDebug(KHTML_LOG) << "printed: page " << page <<" bottom At = " << bottom;
3274
3275 top = bottom;
3276 p->resetTransform();
3277 page++;
3278 }
3279
3280 p->end();
3281 delete p;
3282
3283 // and now reset the layout to the usual one...
3284 root->setPagedMode(false);
3285 root->setStaticMode(false);
3286 d->paged = false;
3287 khtml::setPrintPainter(nullptr);
3288 setMediaType(oldMediaType);
3289 m_part->xmlDocImpl()->setPaintDevice(this);
3290 m_part->xmlDocImpl()->styleSelector()->computeFontSizes(m_part->xmlDocImpl()->logicalDpiY(), m_part->fontScaleFactor());
3291 m_part->xmlDocImpl()->updateStyleSelector();
3292 viewport()->unsetCursor();
3293 }
3294 }
3295
slotPaletteChanged()3296 void KHTMLView::slotPaletteChanged()
3297 {
3298 if (!m_part->xmlDocImpl()) {
3299 return;
3300 }
3301 DOM::DocumentImpl *document = m_part->xmlDocImpl();
3302 if (!document->isHTMLDocument()) {
3303 return;
3304 }
3305 khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(document->renderer());
3306 if (!root) {
3307 return;
3308 }
3309 root->style()->resetPalette();
3310 NodeImpl *body = static_cast<HTMLDocumentImpl *>(document)->body();
3311 if (!body) {
3312 return;
3313 }
3314 body->setChanged(true);
3315 body->recalcStyle(NodeImpl::Force);
3316 }
3317
paint(QPainter * p,const QRect & rc,int yOff,bool * more)3318 void KHTMLView::paint(QPainter *p, const QRect &rc, int yOff, bool *more)
3319 {
3320 if (!m_part->xmlDocImpl()) {
3321 return;
3322 }
3323 khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer());
3324 if (!root) {
3325 return;
3326 }
3327 #ifdef SPEED_DEBUG
3328 d->firstRepaintPending = false;
3329 #endif
3330
3331 QPaintDevice *opd = m_part->xmlDocImpl()->paintDevice();
3332 m_part->xmlDocImpl()->setPaintDevice(p->device());
3333 root->setPagedMode(true);
3334 root->setStaticMode(true);
3335 root->setWidth(rc.width());
3336
3337 // save()
3338 QRegion creg = p->clipRegion();
3339 QTransform t = p->worldTransform();
3340 QRect w = p->window();
3341 QRect v = p->viewport();
3342 bool vte = p->viewTransformEnabled();
3343 bool wme = p->worldMatrixEnabled();
3344
3345 p->setClipRect(rc);
3346 p->translate(rc.left(), rc.top());
3347 double scale = ((double) rc.width() / (double) root->docWidth());
3348 int height = (int)((double) rc.height() / scale);
3349 #ifndef QT_NO_TRANSFORMATIONS
3350 p->scale(scale, scale);
3351 #endif
3352 root->setPageTop(yOff);
3353 root->setPageBottom(yOff + height);
3354
3355 root->layer()->paint(p, QRect(0, yOff, root->docWidth(), height));
3356 if (more) {
3357 *more = yOff + height < root->docHeight();
3358 }
3359
3360 // restore()
3361 p->setWorldTransform(t);
3362 p->setWindow(w);
3363 p->setViewport(v);
3364 p->setViewTransformEnabled(vte);
3365 p->setWorldMatrixEnabled(wme);
3366 if (!creg.isEmpty()) {
3367 p->setClipRegion(creg);
3368 } else {
3369 p->setClipRegion(QRegion(), Qt::NoClip);
3370 }
3371
3372 root->setPagedMode(false);
3373 root->setStaticMode(false);
3374 m_part->xmlDocImpl()->setPaintDevice(opd);
3375 }
3376
render(QPainter * p,const QRect & r,const QPoint & off)3377 void KHTMLView::render(QPainter *p, const QRect &r, const QPoint &off)
3378 {
3379 #ifdef SPEED_DEBUG
3380 d->firstRepaintPending = false;
3381 #endif
3382 QRect clip(off.x() + r.x(), off.y() + r.y(), r.width(), r.height());
3383 if (!m_part || !m_part->xmlDocImpl() || !m_part->xmlDocImpl()->renderer()) {
3384 p->fillRect(clip, palette().brush(QPalette::Active, QPalette::Base));
3385 return;
3386 }
3387 QPaintDevice *opd = m_part->xmlDocImpl()->paintDevice();
3388 m_part->xmlDocImpl()->setPaintDevice(p->device());
3389
3390 // save()
3391 QRegion creg = p->clipRegion();
3392 QTransform t = p->worldTransform();
3393 QRect w = p->window();
3394 QRect v = p->viewport();
3395 bool vte = p->viewTransformEnabled();
3396 bool wme = p->worldMatrixEnabled();
3397
3398 p->setClipRect(clip);
3399 QRect rect = r.translated(contentsX(), contentsY());
3400 p->translate(off.x() - contentsX(), off.y() - contentsY());
3401
3402 m_part->xmlDocImpl()->renderer()->layer()->paint(p, rect);
3403
3404 // restore()
3405 p->setWorldTransform(t);
3406 p->setWindow(w);
3407 p->setViewport(v);
3408 p->setViewTransformEnabled(vte);
3409 p->setWorldMatrixEnabled(wme);
3410 if (!creg.isEmpty()) {
3411 p->setClipRegion(creg);
3412 } else {
3413 p->setClipRegion(QRegion(), Qt::NoClip);
3414 }
3415
3416 m_part->xmlDocImpl()->setPaintDevice(opd);
3417 }
3418
setHasStaticBackground(bool partial)3419 void KHTMLView::setHasStaticBackground(bool partial)
3420 {
3421 // full static iframe is irreversible for now
3422 if (d->staticWidget == KHTMLViewPrivate::SBFull && m_kwp->isRedirected()) {
3423 return;
3424 }
3425
3426 d->staticWidget = partial ?
3427 KHTMLViewPrivate::SBPartial : KHTMLViewPrivate::SBFull;
3428 }
3429
setHasNormalBackground()3430 void KHTMLView::setHasNormalBackground()
3431 {
3432 // full static iframe is irreversible for now
3433 if (d->staticWidget == KHTMLViewPrivate::SBFull && m_kwp->isRedirected()) {
3434 return;
3435 }
3436
3437 d->staticWidget = KHTMLViewPrivate::SBNone;
3438 }
3439
addStaticObject(bool fixed)3440 void KHTMLView::addStaticObject(bool fixed)
3441 {
3442 if (fixed) {
3443 d->fixedObjectsCount++;
3444 } else {
3445 d->staticObjectsCount++;
3446 }
3447
3448 setHasStaticBackground(true /*partial*/);
3449 }
3450
removeStaticObject(bool fixed)3451 void KHTMLView::removeStaticObject(bool fixed)
3452 {
3453 if (fixed) {
3454 d->fixedObjectsCount--;
3455 } else {
3456 d->staticObjectsCount--;
3457 }
3458
3459 assert(d->fixedObjectsCount >= 0 && d->staticObjectsCount >= 0);
3460
3461 if (!d->staticObjectsCount && !d->fixedObjectsCount) {
3462 setHasNormalBackground();
3463 } else {
3464 setHasStaticBackground(true /*partial*/);
3465 }
3466 }
3467
setVerticalScrollBarPolicy(Qt::ScrollBarPolicy policy)3468 void KHTMLView::setVerticalScrollBarPolicy(Qt::ScrollBarPolicy policy)
3469 {
3470 #ifndef KHTML_NO_SCROLLBARS
3471 d->vpolicy = policy;
3472 QScrollArea::setVerticalScrollBarPolicy(policy);
3473 #else
3474 Q_UNUSED(policy);
3475 #endif
3476 }
3477
setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy policy)3478 void KHTMLView::setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy policy)
3479 {
3480 #ifndef KHTML_NO_SCROLLBARS
3481 d->hpolicy = policy;
3482 QScrollArea::setHorizontalScrollBarPolicy(policy);
3483 #else
3484 Q_UNUSED(policy);
3485 #endif
3486 }
3487
restoreScrollBar()3488 void KHTMLView::restoreScrollBar()
3489 {
3490 int ow = visibleWidth();
3491 QScrollArea::setVerticalScrollBarPolicy(d->vpolicy);
3492 if (visibleWidth() != ow) {
3493 layout();
3494 }
3495 d->prevScrollbarVisible = verticalScrollBar()->isVisible();
3496 }
3497
formCompletionItems(const QString & name) const3498 QStringList KHTMLView::formCompletionItems(const QString &name) const
3499 {
3500 if (!m_part->settings()->isFormCompletionEnabled()) {
3501 return QStringList();
3502 }
3503 if (!d->formCompletions) {
3504 d->formCompletions = new KConfig(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + "khtml/formcompletions");
3505 }
3506 return d->formCompletions->group("").readEntry(name, QStringList());
3507 }
3508
clearCompletionHistory(const QString & name)3509 void KHTMLView::clearCompletionHistory(const QString &name)
3510 {
3511 if (!d->formCompletions) {
3512 d->formCompletions = new KConfig(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + "khtml/formcompletions");
3513 }
3514 d->formCompletions->group("").writeEntry(name, "");
3515 d->formCompletions->sync();
3516 }
3517
addFormCompletionItem(const QString & name,const QString & value)3518 void KHTMLView::addFormCompletionItem(const QString &name, const QString &value)
3519 {
3520 if (!m_part->settings()->isFormCompletionEnabled()) {
3521 return;
3522 }
3523 // don't store values that are all numbers or just numbers with
3524 // dashes or spaces as those are likely credit card numbers or
3525 // something similar
3526 bool cc_number(true);
3527 for (int i = 0; i < value.length(); ++i) {
3528 QChar c(value[i]);
3529 if (!c.isNumber() && c != '-' && !c.isSpace()) {
3530 cc_number = false;
3531 break;
3532 }
3533 }
3534 if (cc_number) {
3535 return;
3536 }
3537 QStringList items = formCompletionItems(name);
3538 if (!items.contains(value)) {
3539 items.prepend(value);
3540 }
3541 while ((int)items.count() > m_part->settings()->maxFormCompletionItems()) {
3542 items.erase(items.isEmpty() ? items.end() : --items.end());
3543 }
3544 d->formCompletions->group("").writeEntry(name, items);
3545 }
3546
addNonPasswordStorableSite(const QString & host)3547 void KHTMLView::addNonPasswordStorableSite(const QString &host)
3548 {
3549 if (!d->formCompletions) {
3550 d->formCompletions = new KConfig(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + "khtml/formcompletions");
3551 }
3552
3553 KConfigGroup cg(d->formCompletions, "NonPasswordStorableSites");
3554 QStringList sites = cg.readEntry("Sites", QStringList());
3555 sites.append(host);
3556 cg.writeEntry("Sites", sites);
3557 cg.sync();
3558 }
3559
delNonPasswordStorableSite(const QString & host)3560 void KHTMLView::delNonPasswordStorableSite(const QString &host)
3561 {
3562 if (!d->formCompletions) {
3563 d->formCompletions = new KConfig(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + "khtml/formcompletions");
3564 }
3565
3566 KConfigGroup cg(d->formCompletions, "NonPasswordStorableSites");
3567 QStringList sites = cg.readEntry("Sites", QStringList());
3568 sites.removeOne(host);
3569 cg.writeEntry("Sites", sites);
3570 cg.sync();
3571 }
3572
nonPasswordStorableSite(const QString & host) const3573 bool KHTMLView::nonPasswordStorableSite(const QString &host) const
3574 {
3575 if (!d->formCompletions) {
3576 d->formCompletions = new KConfig(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + "khtml/formcompletions");
3577 }
3578 QStringList sites = d->formCompletions->group("NonPasswordStorableSites").readEntry("Sites", QStringList());
3579 return (sites.indexOf(host) != -1);
3580 }
3581
3582 // returns true if event should be swallowed
dispatchMouseEvent(int eventId,DOM::NodeImpl * targetNode,DOM::NodeImpl * targetNodeNonShared,bool cancelable,int detail,QMouseEvent * _mouse,bool setUnder,int mouseEventType,int orient)3583 bool KHTMLView::dispatchMouseEvent(int eventId, DOM::NodeImpl *targetNode,
3584 DOM::NodeImpl *targetNodeNonShared, bool cancelable,
3585 int detail, QMouseEvent *_mouse, bool setUnder,
3586 int mouseEventType, int orient)
3587 {
3588 // if the target node is a text node, dispatch on the parent node - rdar://4196646 (and #76948)
3589 if (targetNode && targetNode->isTextNode()) {
3590 targetNode = targetNode->parentNode();
3591 }
3592
3593 if (d->underMouse) {
3594 d->underMouse->deref();
3595 }
3596 d->underMouse = targetNode;
3597 if (d->underMouse) {
3598 d->underMouse->ref();
3599 }
3600
3601 if (d->underMouseNonShared) {
3602 d->underMouseNonShared->deref();
3603 }
3604 d->underMouseNonShared = targetNodeNonShared;
3605 if (d->underMouseNonShared) {
3606 d->underMouseNonShared->ref();
3607 }
3608
3609 bool isWheelEvent = (mouseEventType == DOM::NodeImpl::MouseWheel);
3610
3611 int exceptioncode = 0;
3612 int pageX = _mouse->x();
3613 int pageY = _mouse->y();
3614 revertTransforms(pageX, pageY);
3615 int clientX = pageX - contentsX();
3616 int clientY = pageY - contentsY();
3617 int screenX = _mouse->globalX();
3618 int screenY = _mouse->globalY();
3619 int button = -1;
3620 switch (_mouse->button()) {
3621 case Qt::LeftButton:
3622 button = 0;
3623 break;
3624 case Qt::MidButton:
3625 button = 1;
3626 break;
3627 case Qt::RightButton:
3628 button = 2;
3629 break;
3630 default:
3631 break;
3632 }
3633 if (d->accessKeysEnabled && d->accessKeysPreActivate && button != -1) {
3634 d->accessKeysPreActivate = false;
3635 }
3636
3637 bool ctrlKey = (_mouse->modifiers() & Qt::ControlModifier);
3638 bool altKey = (_mouse->modifiers() & Qt::AltModifier);
3639 bool shiftKey = (_mouse->modifiers() & Qt::ShiftModifier);
3640 bool metaKey = (_mouse->modifiers() & Qt::MetaModifier);
3641
3642 // mouseout/mouseover
3643 if (setUnder && d->oldUnderMouse != targetNode) {
3644 if (d->oldUnderMouse && d->oldUnderMouse->document() != m_part->xmlDocImpl()) {
3645 d->oldUnderMouse->deref();
3646 d->oldUnderMouse = nullptr;
3647 }
3648 // send mouseout event to the old node
3649 if (d->oldUnderMouse) {
3650 // send mouseout event to the old node
3651 MouseEventImpl *me = new MouseEventImpl(EventImpl::MOUSEOUT_EVENT,
3652 true, true, m_part->xmlDocImpl()->defaultView(),
3653 0, screenX, screenY, clientX, clientY, pageX, pageY,
3654 ctrlKey, altKey, shiftKey, metaKey,
3655 button, targetNode);
3656 me->ref();
3657 d->oldUnderMouse->dispatchEvent(me, exceptioncode, true);
3658 me->deref();
3659 }
3660 // send mouseover event to the new node
3661 if (targetNode) {
3662 MouseEventImpl *me = new MouseEventImpl(EventImpl::MOUSEOVER_EVENT,
3663 true, true, m_part->xmlDocImpl()->defaultView(),
3664 0, screenX, screenY, clientX, clientY, pageX, pageY,
3665 ctrlKey, altKey, shiftKey, metaKey,
3666 button, d->oldUnderMouse);
3667
3668 me->ref();
3669 targetNode->dispatchEvent(me, exceptioncode, true);
3670 me->deref();
3671 }
3672 if (d->oldUnderMouse) {
3673 d->oldUnderMouse->deref();
3674 }
3675 d->oldUnderMouse = targetNode;
3676 if (d->oldUnderMouse) {
3677 d->oldUnderMouse->ref();
3678 }
3679 }
3680
3681 bool swallowEvent = false;
3682
3683 if (targetNode) {
3684 // if the target node is a disabled widget, we don't want any full-blown mouse events
3685 if (targetNode->isGenericFormElement()
3686 && static_cast<HTMLGenericFormElementImpl *>(targetNode)->disabled()) {
3687 return true;
3688 }
3689
3690 // send the actual event
3691 bool dblclick = (eventId == EventImpl::CLICK_EVENT &&
3692 _mouse->type() == QEvent::MouseButtonDblClick);
3693 MouseEventImpl *me = new MouseEventImpl(static_cast<EventImpl::EventId>(eventId),
3694 true, cancelable, m_part->xmlDocImpl()->defaultView(),
3695 detail, screenX, screenY, clientX, clientY, pageX, pageY,
3696 ctrlKey, altKey, shiftKey, metaKey,
3697 button, nullptr, isWheelEvent ? nullptr : _mouse, dblclick,
3698 isWheelEvent ? static_cast<MouseEventImpl::Orientation>(orient) : MouseEventImpl::ONone);
3699 me->ref();
3700 if (!d->m_mouseEventsTarget && RenderLayer::gScrollBar && eventId == EventImpl::MOUSEDOWN_EVENT)
3701 // button is pressed inside a layer scrollbar, so make it the target for future mousemove events until released
3702 {
3703 d->m_mouseEventsTarget = RenderLayer::gScrollBar;
3704 }
3705 if (d->m_mouseEventsTarget && qobject_cast<QScrollBar *>(d->m_mouseEventsTarget) &&
3706 dynamic_cast<KHTMLWidget *>(static_cast<QWidget *>(d->m_mouseEventsTarget))) {
3707 // we have a sticky mouse event target and it is a layer's scrollbar. Forward events manually.
3708 // ### should use the dom
3709 KHTMLWidget *w = dynamic_cast<KHTMLWidget *>(static_cast<QWidget *>(d->m_mouseEventsTarget));
3710 QPoint p = w->m_kwp->absolutePos();
3711 QMouseEvent fw(_mouse->type(), QPoint(pageX, pageY) - p, _mouse->button(), _mouse->buttons(), _mouse->modifiers());
3712 static_cast<RenderWidget::EventPropagator *>(static_cast<QWidget *>(d->m_mouseEventsTarget))->sendEvent(&fw);
3713 if (_mouse->type() == QMouseEvent::MouseButtonPress && _mouse->button() == Qt::RightButton) {
3714 QContextMenuEvent cme(QContextMenuEvent::Mouse, p);
3715 static_cast<RenderWidget::EventPropagator *>(static_cast<QWidget *>(d->m_mouseEventsTarget))->sendEvent(&cme);
3716 d->m_mouseEventsTarget = nullptr;
3717 }
3718 swallowEvent = true;
3719 } else {
3720 targetNode->dispatchEvent(me, exceptioncode, true);
3721 bool defaultHandled = me->defaultHandled();
3722 if (defaultHandled || me->defaultPrevented()) {
3723 swallowEvent = true;
3724 }
3725 }
3726 if (eventId == EventImpl::MOUSEDOWN_EVENT && !me->defaultPrevented()) {
3727 // Focus should be shifted on mouse down, not on a click. -dwh
3728 // Blur current focus node when a link/button is clicked; this
3729 // is expected by some sites that rely on onChange handlers running
3730 // from form fields before the button click is processed.
3731 DOM::NodeImpl *nodeImpl = targetNode;
3732 for (; nodeImpl && !nodeImpl->isFocusable(); nodeImpl = nodeImpl->parentNode()) {
3733 }
3734 if (nodeImpl && nodeImpl->isMouseFocusable()) {
3735 m_part->xmlDocImpl()->setFocusNode(nodeImpl);
3736 } else if (!nodeImpl || !nodeImpl->focused()) {
3737 m_part->xmlDocImpl()->setFocusNode(nullptr);
3738 }
3739 }
3740 me->deref();
3741 }
3742
3743 return swallowEvent;
3744 }
3745
setIgnoreWheelEvents(bool e)3746 void KHTMLView::setIgnoreWheelEvents(bool e)
3747 {
3748 d->ignoreWheelEvents = e;
3749 }
3750
3751 #ifndef QT_NO_WHEELEVENT
3752
wheelEvent(QWheelEvent * e)3753 void KHTMLView::wheelEvent(QWheelEvent *e)
3754 {
3755 // check if we should reset the state of the indicator describing if
3756 // we are currently scrolling the view as a result of wheel events
3757 if (d->scrollingFromWheel != QPoint(-1, -1) && d->scrollingFromWheel != QCursor::pos()) {
3758 d->scrollingFromWheel = d->scrollingFromWheelTimerId ? QCursor::pos() : QPoint(-1, -1);
3759 }
3760
3761 if (d->accessKeysEnabled && d->accessKeysPreActivate) {
3762 d->accessKeysPreActivate = false;
3763 }
3764
3765 if ((e->modifiers() & Qt::ControlModifier) == Qt::ControlModifier) {
3766 emit zoomView(- e->delta());
3767 e->accept();
3768 } else if (d->firstLayoutPending) {
3769 e->accept();
3770 } else if (!m_kwp->isRedirected() &&
3771 ((e->orientation() == Qt::Vertical &&
3772 ((d->ignoreWheelEvents && !verticalScrollBar()->isVisible())
3773 || (e->delta() > 0 && contentsY() <= 0)
3774 || (e->delta() < 0 && contentsY() >= contentsHeight() - visibleHeight())))
3775 ||
3776 (e->orientation() == Qt::Horizontal &&
3777 ((d->ignoreWheelEvents && !horizontalScrollBar()->isVisible())
3778 || (e->delta() > 0 && contentsX() <= 0)
3779 || (e->delta() < 0 && contentsX() >= contentsWidth() - visibleWidth()))))
3780 && m_part->parentPart()) {
3781 if (m_part->parentPart()->view()) {
3782 m_part->parentPart()->view()->wheelEvent(e);
3783 }
3784 e->ignore();
3785 } else {
3786 int xm = e->x();
3787 int ym = e->y();
3788 revertTransforms(xm, ym);
3789
3790 DOM::NodeImpl::MouseEvent mev(e->buttons(), DOM::NodeImpl::MouseWheel);
3791 m_part->xmlDocImpl()->prepareMouseEvent(false, xm, ym, &mev);
3792
3793 MouseEventImpl::Orientation o = MouseEventImpl::OVertical;
3794 if (e->orientation() == Qt::Horizontal) {
3795 o = MouseEventImpl::OHorizontal;
3796 }
3797
3798 QMouseEvent _mouse(QEvent::MouseMove, e->pos(), Qt::NoButton, e->buttons(), e->modifiers());
3799 bool swallow = dispatchMouseEvent(EventImpl::KHTML_MOUSEWHEEL_EVENT, mev.innerNode.handle(), mev.innerNonSharedNode.handle(),
3800 true, -e->delta() / 40, &_mouse, true, DOM::NodeImpl::MouseWheel, o);
3801
3802 if (swallow) {
3803 return;
3804 }
3805
3806 d->scrollBarMoved = true;
3807 d->scrollingFromWheel = QCursor::pos();
3808 if (d->smoothScrollMode != SSMDisabled) {
3809 d->shouldSmoothScroll = true;
3810 }
3811 if (d->scrollingFromWheelTimerId) {
3812 killTimer(d->scrollingFromWheelTimerId);
3813 }
3814 d->scrollingFromWheelTimerId = startTimer(400);
3815
3816 if (m_part->parentPart()) {
3817 // don't propagate if we are a sub-frame and our scrollbars are already at end of range
3818 bool h = (static_cast<QWheelEvent *>(e)->orientation() == Qt::Horizontal);
3819 bool d = (static_cast<QWheelEvent *>(e)->delta() < 0);
3820 QScrollBar *hsb = horizontalScrollBar();
3821 QScrollBar *vsb = verticalScrollBar();
3822 if ((h && ((d && hsb->value() == hsb->maximum()) || (!d && hsb->value() == hsb->minimum()))) ||
3823 (!h && ((d && vsb->value() == vsb->maximum()) || (!d && vsb->value() == vsb->minimum())))) {
3824 e->accept();
3825 return;
3826 }
3827 }
3828 QScrollArea::wheelEvent(e);
3829 }
3830
3831 }
3832 #endif
3833
dragEnterEvent(QDragEnterEvent * ev)3834 void KHTMLView::dragEnterEvent(QDragEnterEvent *ev)
3835 {
3836 // Still overridden for BC reasons only...
3837 QScrollArea::dragEnterEvent(ev);
3838 }
3839
dropEvent(QDropEvent * ev)3840 void KHTMLView::dropEvent(QDropEvent *ev)
3841 {
3842 // Still overridden for BC reasons only...
3843 QScrollArea::dropEvent(ev);
3844 }
3845
focusInEvent(QFocusEvent * e)3846 void KHTMLView::focusInEvent(QFocusEvent *e)
3847 {
3848 DOM::NodeImpl *fn = m_part->xmlDocImpl() ? m_part->xmlDocImpl()->focusNode() : nullptr;
3849 if (fn && fn->renderer() && fn->renderer()->isWidget() &&
3850 (e->reason() != Qt::MouseFocusReason) &&
3851 static_cast<khtml::RenderWidget *>(fn->renderer())->widget()) {
3852 static_cast<khtml::RenderWidget *>(fn->renderer())->widget()->setFocus();
3853 }
3854 m_part->setSelectionVisible();
3855 QScrollArea::focusInEvent(e);
3856 }
3857
focusOutEvent(QFocusEvent * e)3858 void KHTMLView::focusOutEvent(QFocusEvent *e)
3859 {
3860 if (m_part) {
3861 m_part->stopAutoScroll();
3862 m_part->setSelectionVisible(false);
3863 }
3864
3865 if (d->cursorIconWidget) {
3866 d->cursorIconWidget->hide();
3867 }
3868
3869 QScrollArea::focusOutEvent(e);
3870 }
3871
scrollContentsBy(int dx,int dy)3872 void KHTMLView::scrollContentsBy(int dx, int dy)
3873 {
3874 if (!dx && !dy) {
3875 return;
3876 }
3877
3878 if (!d->firstLayoutPending && !d->complete && m_part->xmlDocImpl() &&
3879 d->layoutSchedulingEnabled) {
3880 // contents scroll while we are not complete: we need to check our layout *now*
3881 khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer());
3882 if (root && root->needsLayout()) {
3883 unscheduleRelayout();
3884 layout();
3885 }
3886 }
3887
3888 if (d->shouldSmoothScroll && d->smoothScrollMode != SSMDisabled && m_part->xmlDocImpl() &&
3889 m_part->xmlDocImpl()->renderer() && (d->smoothScrollMode != SSMWhenEfficient || d->smoothScrollMissedDeadlines != sWayTooMany)) {
3890
3891 bool doSmoothScroll = (!d->staticWidget || d->smoothScrollMode == SSMEnabled);
3892
3893 int numStaticPixels = 0;
3894 QRegion r = static_cast<RenderCanvas *>(m_part->xmlDocImpl()->renderer())->staticRegion();
3895
3896 // only do smooth scrolling if static region is relatively small
3897 if (!doSmoothScroll && d->staticWidget == KHTMLViewPrivate::SBPartial && r.rects().size() <= 10) {
3898 foreach (const QRect &rr, r.rects()) {
3899 numStaticPixels += rr.width() * rr.height();
3900 }
3901 if ((numStaticPixels < sSmoothScrollMinStaticPixels) || (numStaticPixels * 8 < visibleWidth()*visibleHeight())) {
3902 doSmoothScroll = true;
3903 }
3904 }
3905 if (doSmoothScroll) {
3906 setupSmoothScrolling(dx, dy);
3907 return;
3908 }
3909 }
3910
3911 if (underMouse() && QToolTip::isVisible()) {
3912 QToolTip::hideText();
3913 }
3914
3915 if (!d->scrollingSelf) {
3916 d->scrollBarMoved = true;
3917 d->contentsMoving = true;
3918 // ensure quick reset of contentsMoving flag
3919 scheduleRepaint(0, 0, 0, 0);
3920 }
3921
3922 if (m_part->xmlDocImpl() && m_part->xmlDocImpl()->documentElement()) {
3923 m_part->xmlDocImpl()->documentElement()->dispatchHTMLEvent(EventImpl::SCROLL_EVENT, true, false);
3924 }
3925
3926 if (QApplication::isRightToLeft()) {
3927 dx = -dx;
3928 }
3929
3930 if (!d->smoothScrolling) {
3931 d->updateContentsXY();
3932 } else {
3933 d->contentsX -= dx;
3934 d->contentsY -= dy;
3935 }
3936 if (widget()->pos() != QPoint(0, 0)) {
3937 // qCDebug(KHTML_LOG) << "Static widget wasn't positioned at (0,0). This should NOT happen. Please report this event to developers.";
3938 widget()->move(0, 0);
3939 }
3940
3941 QWidget *w = widget();
3942 QPoint off;
3943 if (m_kwp->isRedirected()) {
3944 // This is a redirected sub frame. Translate to root view context
3945 KHTMLView *v = m_kwp->rootViewPos(off);
3946 if (v) {
3947 w = v->widget();
3948 }
3949 off = viewport()->mapTo(this, off);
3950 }
3951
3952 if (d->staticWidget) {
3953
3954 // now remove from view the external widgets that must have completely
3955 // disappeared after dx/dy scroll delta is effective
3956 if (!d->visibleWidgets.isEmpty()) {
3957 checkExternalWidgetsPosition();
3958 }
3959
3960 if (d->staticWidget == KHTMLViewPrivate::SBPartial
3961 && m_part->xmlDocImpl() && m_part->xmlDocImpl()->renderer()) {
3962 // static objects might be selectively repainted, like stones in flowing water
3963 QRegion r = static_cast<RenderCanvas *>(m_part->xmlDocImpl()->renderer())->staticRegion();
3964 r.translate(-contentsX(), -contentsY());
3965 QVector<QRect> ar = r.rects();
3966
3967 for (int i = 0; i < ar.size(); ++i) {
3968 widget()->update(ar[i]);
3969 }
3970 r = QRegion(QRect(0, 0, visibleWidth(), visibleHeight())) - r;
3971 ar = r.rects();
3972 for (int i = 0; i < ar.size(); ++i) {
3973 w->scroll(dx, dy, ar[i].translated(off));
3974 }
3975 d->scrollExternalWidgets(dx, dy);
3976 } else {
3977 // we can't avoid a full update
3978 widget()->update();
3979 }
3980 if (d->accessKeysActivated) {
3981 d->scrollAccessKeys(dx, dy);
3982 }
3983
3984 return;
3985 }
3986
3987 if (m_kwp->isRedirected()) {
3988 const QRect rect(off.x(), off.y(), visibleWidth() * d->zoomLevel / 100, visibleHeight() * d->zoomLevel / 100);
3989 w->scroll(dx, dy, rect);
3990 if (d->zoomLevel != 100) {
3991 w->update(rect); // without this update we are getting bad rendering when an iframe is zoomed in
3992 }
3993 } else {
3994 widget()->scroll(dx, dy, widget()->rect() & viewport()->rect());
3995 }
3996
3997 d->scrollExternalWidgets(dx, dy);
3998 if (d->accessKeysActivated) {
3999 d->scrollAccessKeys(dx, dy);
4000 }
4001 }
4002
setupSmoothScrolling(int dx,int dy)4003 void KHTMLView::setupSmoothScrolling(int dx, int dy)
4004 {
4005 // old or minimum speed
4006 int ddx = qMax(d->steps ? abs(d->dx) / d->steps : 0, 3);
4007 int ddy = qMax(d->steps ? abs(d->dy) / d->steps : 0, 3);
4008
4009 // full scroll is remaining scroll plus new scroll
4010 d->dx = d->dx + dx;
4011 d->dy = d->dy + dy;
4012
4013 if (d->dx == 0 && d->dy == 0) {
4014 d->stopScrolling();
4015 return;
4016 }
4017
4018 d->steps = (sSmoothScrollTime - 1) / sSmoothScrollTick + 1;
4019
4020 if (qMax(abs(d->dx), abs(d->dy)) / d->steps < qMax(ddx, ddy)) {
4021 // Don't move slower than average 4px/step in minimum one direction
4022 // This means fewer than normal steps
4023 d->steps = qMax((abs(d->dx) + ddx - 1) / ddx, (abs(d->dy) + ddy - 1) / ddy);
4024 if (d->steps < 1) {
4025 d->steps = 1;
4026 }
4027 }
4028
4029 d->smoothScrollStopwatch.start();
4030 if (!d->smoothScrolling) {
4031 d->startScrolling();
4032 scrollTick();
4033 }
4034 }
4035
scrollTick()4036 void KHTMLView::scrollTick()
4037 {
4038 if (d->dx == 0 && d->dy == 0) {
4039 d->stopScrolling();
4040 return;
4041 }
4042
4043 if (d->steps < 1) {
4044 d->steps = 1;
4045 }
4046 int takesteps = d->smoothScrollStopwatch.restart() / sSmoothScrollTick;
4047 int scroll_x = 0;
4048 int scroll_y = 0;
4049 if (takesteps < 1) {
4050 takesteps = 1;
4051 }
4052 if (takesteps > d->steps) {
4053 takesteps = d->steps;
4054 }
4055 for (int i = 0; i < takesteps; i++) {
4056 int ddx = (d->dx / (d->steps + 1)) * 2;
4057 int ddy = (d->dy / (d->steps + 1)) * 2;
4058
4059 // limit step to requested scrolling distance
4060 if (abs(ddx) > abs(d->dx)) {
4061 ddx = d->dx;
4062 }
4063 if (abs(ddy) > abs(d->dy)) {
4064 ddy = d->dy;
4065 }
4066
4067 // update remaining scroll
4068 d->dx -= ddx;
4069 d->dy -= ddy;
4070 scroll_x += ddx;
4071 scroll_y += ddy;
4072 d->steps--;
4073 }
4074
4075 d->shouldSmoothScroll = false;
4076 scrollContentsBy(scroll_x, scroll_y);
4077
4078 if (takesteps < 2) {
4079 d->smoothScrollMissedDeadlines = 0;
4080 } else {
4081 if (d->smoothScrollMissedDeadlines != sWayTooMany &&
4082 (!m_part->xmlDocImpl() || !m_part->xmlDocImpl()->parsing())) {
4083 d->smoothScrollMissedDeadlines++;
4084 if (d->smoothScrollMissedDeadlines >= sMaxMissedDeadlines) {
4085 // we missed many deadlines in a row!
4086 // time to signal we had enough..
4087 d->smoothScrollMissedDeadlines = sWayTooMany;
4088 }
4089 }
4090 }
4091 }
4092
addChild(QWidget * child,int x,int y)4093 void KHTMLView::addChild(QWidget *child, int x, int y)
4094 {
4095 if (!child) {
4096 return;
4097 }
4098
4099 if (child->parent() != widget()) {
4100 child->setParent(widget());
4101 }
4102
4103 // ### handle pseudo-zooming of non-redirected widgets (e.g. just resize'em)
4104
4105 child->move(x - contentsX(), y - contentsY());
4106 }
4107
timerEvent(QTimerEvent * e)4108 void KHTMLView::timerEvent(QTimerEvent *e)
4109 {
4110 // qCDebug(KHTML_LOG) << "timer event " << e->timerId();
4111 if (e->timerId() == d->scrollTimerId) {
4112 if (d->scrollSuspended) {
4113 return;
4114 }
4115 switch (d->scrollDirection) {
4116 case KHTMLViewPrivate::ScrollDown:
4117 if (contentsY() + visibleHeight() >= contentsHeight()) {
4118 d->newScrollTimer(this, 0);
4119 } else {
4120 verticalScrollBar()->setValue(verticalScrollBar()->value() + d->scrollBy);
4121 }
4122 break;
4123 case KHTMLViewPrivate::ScrollUp:
4124 if (contentsY() <= 0) {
4125 d->newScrollTimer(this, 0);
4126 } else {
4127 verticalScrollBar()->setValue(verticalScrollBar()->value() - d->scrollBy);
4128 }
4129 break;
4130 case KHTMLViewPrivate::ScrollRight:
4131 if (contentsX() + visibleWidth() >= contentsWidth()) {
4132 d->newScrollTimer(this, 0);
4133 } else {
4134 horizontalScrollBar()->setValue(horizontalScrollBar()->value() + d->scrollBy);
4135 }
4136 break;
4137 case KHTMLViewPrivate::ScrollLeft:
4138 if (contentsX() <= 0) {
4139 d->newScrollTimer(this, 0);
4140 } else {
4141 horizontalScrollBar()->setValue(horizontalScrollBar()->value() - d->scrollBy);
4142 }
4143 break;
4144 }
4145 return;
4146 } else if (e->timerId() == d->scrollingFromWheelTimerId) {
4147 killTimer(d->scrollingFromWheelTimerId);
4148 d->scrollingFromWheelTimerId = 0;
4149 } else if (e->timerId() == d->layoutTimerId) {
4150 if (d->firstLayoutPending && d->layoutAttemptCounter < 4
4151 && (!m_part->xmlDocImpl() || !m_part->xmlDocImpl()->readyForLayout())) {
4152 d->layoutAttemptCounter++;
4153 killTimer(d->layoutTimerId);
4154 d->layoutTimerId = 0;
4155 scheduleRelayout();
4156 return;
4157 }
4158 layout();
4159 d->scheduledLayoutCounter++;
4160 if (d->firstLayoutPending) {
4161 d->firstLayoutPending = false;
4162 verticalScrollBar()->setEnabled(true);
4163 horizontalScrollBar()->setEnabled(true);
4164 }
4165 }
4166
4167 d->contentsMoving = false;
4168 if (m_part->xmlDocImpl()) {
4169 DOM::DocumentImpl *document = m_part->xmlDocImpl();
4170 khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(document->renderer());
4171
4172 if (root && root->needsLayout()) {
4173 if (d->repaintTimerId) {
4174 killTimer(d->repaintTimerId);
4175 }
4176 d->repaintTimerId = 0;
4177 scheduleRelayout();
4178 return;
4179 }
4180 }
4181
4182 if (d->repaintTimerId) {
4183 killTimer(d->repaintTimerId);
4184 }
4185 d->repaintTimerId = 0;
4186
4187 QRect updateRegion;
4188 const QVector<QRect> rects = d->updateRegion.rects();
4189
4190 d->updateRegion = QRegion();
4191
4192 if (rects.size()) {
4193 updateRegion = rects[0];
4194 }
4195
4196 for (int i = 1; i < rects.size(); ++i) {
4197 QRect newRegion = updateRegion.united(rects[i]);
4198 if (2 * newRegion.height() > 3 * updateRegion.height()) {
4199 repaintContents(updateRegion);
4200 updateRegion = rects[i];
4201 } else {
4202 updateRegion = newRegion;
4203 }
4204 }
4205
4206 if (!updateRegion.isNull()) {
4207 repaintContents(updateRegion);
4208 }
4209
4210 // As widgets can only be accurately positioned during painting, every layout might
4211 // dissociate a widget from its RenderWidget. E.g: if a RenderWidget was visible before layout, but the layout
4212 // pushed it out of the viewport, it will not be repainted, and consequently it's associated widget won't be repositioned.
4213 // 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.
4214
4215 if (d->dirtyLayout && !d->visibleWidgets.isEmpty()) {
4216 checkExternalWidgetsPosition();
4217 }
4218
4219 d->dirtyLayout = false;
4220
4221 emit repaintAccessKeys();
4222 if (d->emitCompletedAfterRepaint) {
4223 bool full = d->emitCompletedAfterRepaint == KHTMLViewPrivate::CSFull;
4224 d->emitCompletedAfterRepaint = KHTMLViewPrivate::CSNone;
4225 if (full) {
4226 emit m_part->completed();
4227 } else {
4228 emit m_part->completed(true);
4229 }
4230 }
4231 }
4232
checkExternalWidgetsPosition()4233 void KHTMLView::checkExternalWidgetsPosition()
4234 {
4235 QWidget *w;
4236 QRect visibleRect(contentsX(), contentsY(), visibleWidth(), visibleHeight());
4237 QList<RenderWidget *> toRemove;
4238 QHashIterator<void *, QWidget *> it(d->visibleWidgets);
4239 while (it.hasNext()) {
4240 int xp = 0, yp = 0;
4241 it.next();
4242 RenderWidget *rw = static_cast<RenderWidget *>(it.key());
4243 if (!rw->absolutePosition(xp, yp) ||
4244 !visibleRect.intersects(QRect(xp, yp, it.value()->width(), it.value()->height()))) {
4245 toRemove.append(rw);
4246 }
4247 }
4248 foreach (RenderWidget *r, toRemove)
4249 if ((w = d->visibleWidgets.take(r))) {
4250 w->move(0, -500000);
4251 }
4252 }
4253
scheduleRelayout(khtml::RenderObject *)4254 void KHTMLView::scheduleRelayout(khtml::RenderObject * /*clippedObj*/)
4255 {
4256 if (!d->layoutSchedulingEnabled || d->layoutTimerId) {
4257 return;
4258 }
4259
4260 int time = 0;
4261 if (d->firstLayoutPending) {
4262 // Any repaint happening while we have no content blanks the viewport ("white flash").
4263 // Hence the need to delay the first layout as much as we can.
4264 // Only if the document gets stuck for too long in incomplete state will we allow the blanking.
4265 time = d->layoutAttemptCounter ?
4266 sLayoutAttemptDelay + sLayoutAttemptIncrement * d->layoutAttemptCounter : sFirstLayoutDelay;
4267 } else if (m_part->xmlDocImpl() && m_part->xmlDocImpl()->parsing()) {
4268 // Delay between successive layouts in parsing mode.
4269 // Increment reflects the decaying importance of visual feedback over time.
4270 time = qMin(2000, sParsingLayoutsInterval + d->scheduledLayoutCounter * sParsingLayoutsIncrement);
4271 }
4272 d->layoutTimerId = startTimer(time);
4273 }
4274
unscheduleRelayout()4275 void KHTMLView::unscheduleRelayout()
4276 {
4277 if (!d->layoutTimerId) {
4278 return;
4279 }
4280
4281 killTimer(d->layoutTimerId);
4282 d->layoutTimerId = 0;
4283 }
4284
unscheduleRepaint()4285 void KHTMLView::unscheduleRepaint()
4286 {
4287 if (!d->repaintTimerId) {
4288 return;
4289 }
4290
4291 killTimer(d->repaintTimerId);
4292 d->repaintTimerId = 0;
4293 }
4294
scheduleRepaint(int x,int y,int w,int h,bool asap)4295 void KHTMLView::scheduleRepaint(int x, int y, int w, int h, bool asap)
4296 {
4297 bool parsing = !m_part->xmlDocImpl() || m_part->xmlDocImpl()->parsing();
4298
4299 // qCDebug(KHTML_LOG) << "parsing " << parsing;
4300 // qCDebug(KHTML_LOG) << "complete " << d->complete;
4301
4302 int time = parsing && !d->firstLayoutPending ? 150 : (!asap ? (!d->complete ? 80 : 20) : 0);
4303
4304 #ifdef DEBUG_FLICKER
4305 QPainter p;
4306 p.begin(viewport());
4307
4308 int vx, vy;
4309 contentsToViewport(x, y, vx, vy);
4310 p.fillRect(vx, vy, w, h, Qt::red);
4311 p.end();
4312 #endif
4313
4314 d->updateRegion = d->updateRegion.united(QRect(x, y, w, h));
4315
4316 if (asap && !parsing) {
4317 unscheduleRepaint();
4318 }
4319
4320 if (!d->repaintTimerId) {
4321 d->repaintTimerId = startTimer(time);
4322 }
4323
4324 // qCDebug(KHTML_LOG) << "starting timer " << time;
4325 }
4326
complete(bool pendingAction)4327 void KHTMLView::complete(bool pendingAction)
4328 {
4329 // qCDebug(KHTML_LOG) << "KHTMLView::complete()";
4330
4331 d->complete = true;
4332
4333 // is there a relayout pending?
4334 if (d->layoutTimerId) {
4335 // qCDebug(KHTML_LOG) << "requesting relayout now";
4336 // do it now
4337 killTimer(d->layoutTimerId);
4338 d->layoutTimerId = startTimer(0);
4339 d->emitCompletedAfterRepaint = pendingAction ?
4340 KHTMLViewPrivate::CSActionPending : KHTMLViewPrivate::CSFull;
4341 }
4342
4343 // is there a repaint pending?
4344 if (d->repaintTimerId) {
4345 // qCDebug(KHTML_LOG) << "requesting repaint now";
4346 // do it now
4347 killTimer(d->repaintTimerId);
4348 d->repaintTimerId = startTimer(0);
4349 d->emitCompletedAfterRepaint = pendingAction ?
4350 KHTMLViewPrivate::CSActionPending : KHTMLViewPrivate::CSFull;
4351 }
4352
4353 if (!d->emitCompletedAfterRepaint) {
4354 if (!pendingAction) {
4355 emit m_part->completed();
4356 } else {
4357 emit m_part->completed(true);
4358 }
4359 }
4360
4361 }
4362
updateScrollBars()4363 void KHTMLView::updateScrollBars()
4364 {
4365 const QWidget *view = widget();
4366 if (!view) {
4367 return;
4368 }
4369
4370 QSize p = viewport()->size();
4371 QSize m = maximumViewportSize();
4372
4373 if (m.expandedTo(view->size()) == m) {
4374 p = m; // no scroll bars needed
4375 }
4376
4377 QSize v = view->size();
4378 horizontalScrollBar()->setRange(0, v.width() - p.width());
4379 horizontalScrollBar()->setPageStep(p.width());
4380 verticalScrollBar()->setRange(0, v.height() - p.height());
4381 verticalScrollBar()->setPageStep(p.height());
4382 if (!d->smoothScrolling) {
4383 d->updateContentsXY();
4384 }
4385 }
4386
slotMouseScrollTimer()4387 void KHTMLView::slotMouseScrollTimer()
4388 {
4389 horizontalScrollBar()->setValue(horizontalScrollBar()->value() + d->m_mouseScroll_byX);
4390 verticalScrollBar()->setValue(verticalScrollBar()->value() + d->m_mouseScroll_byY);
4391 }
4392
positionOfLineBoundary(const DOM::Position & pos,bool toEnd)4393 static DOM::Position positionOfLineBoundary(const DOM::Position &pos, bool toEnd)
4394 {
4395 Selection sel = pos;
4396 sel.expandUsingGranularity(Selection::LINE);
4397 return toEnd ? sel.end() : sel.start();
4398 }
4399
positionOfLineBegin(const DOM::Position & pos)4400 inline static DOM::Position positionOfLineBegin(const DOM::Position &pos)
4401 {
4402 return positionOfLineBoundary(pos, false);
4403 }
4404
positionOfLineEnd(const DOM::Position & pos)4405 inline static DOM::Position positionOfLineEnd(const DOM::Position &pos)
4406 {
4407 return positionOfLineBoundary(pos, true);
4408 }
4409
caretKeyPressEvent(QKeyEvent * _ke)4410 bool KHTMLView::caretKeyPressEvent(QKeyEvent *_ke)
4411 {
4412 EditorContext *ec = &m_part->d->editor_context;
4413 Selection &caret = ec->m_selection;
4414 Position old_pos = caret.caretPos();
4415 Position pos = old_pos;
4416 bool recalcXPos = true;
4417 bool handled = true;
4418
4419 bool ctrl = _ke->modifiers() & Qt::ControlModifier;
4420 bool shift = _ke->modifiers() & Qt::ShiftModifier;
4421
4422 switch (_ke->key()) {
4423
4424 // -- Navigational keys
4425 case Qt::Key_Down:
4426 pos = old_pos.nextLinePosition(caret.xPosForVerticalArrowNavigation(Selection::EXTENT));
4427 recalcXPos = false;
4428 break;
4429
4430 case Qt::Key_Up:
4431 pos = old_pos.previousLinePosition(caret.xPosForVerticalArrowNavigation(Selection::EXTENT));
4432 recalcXPos = false;
4433 break;
4434
4435 case Qt::Key_Left:
4436 pos = ctrl ? old_pos.previousWordPosition() : old_pos.previousCharacterPosition();
4437 break;
4438
4439 case Qt::Key_Right:
4440 pos = ctrl ? old_pos.nextWordPosition() : old_pos.nextCharacterPosition();
4441 break;
4442
4443 case Qt::Key_PageDown:
4444 // moveCaretNextPage(); ###
4445 break;
4446
4447 case Qt::Key_PageUp:
4448 // moveCaretPrevPage(); ###
4449 break;
4450
4451 case Qt::Key_Home:
4452 if (ctrl)
4453 /*moveCaretToDocumentBoundary(false)*/; // ###
4454 else {
4455 pos = positionOfLineBegin(old_pos);
4456 }
4457 break;
4458
4459 case Qt::Key_End:
4460 if (ctrl)
4461 /*moveCaretToDocumentBoundary(true)*/; // ###
4462 else {
4463 pos = positionOfLineEnd(old_pos);
4464 }
4465 break;
4466
4467 default:
4468 handled = false;
4469
4470 }/*end switch*/
4471
4472 if (pos != old_pos) {
4473 m_part->clearCaretRectIfNeeded();
4474
4475 caret.moveTo(shift ? caret.nonCaretPos() : pos, pos);
4476 int old_x = caret.xPosForVerticalArrowNavigation(Selection::CARETPOS);
4477
4478 m_part->selectionLayoutChanged();
4479
4480 // restore old x-position to prevent recalculation
4481 if (!recalcXPos) {
4482 m_part->d->editor_context.m_xPosForVerticalArrowNavigation = old_x;
4483 }
4484
4485 m_part->emitCaretPositionChanged(pos);
4486 // ### check when to emit it
4487 m_part->notifySelectionChanged();
4488
4489 }
4490
4491 if (handled) {
4492 _ke->accept();
4493 }
4494 return handled;
4495 }
4496
4497 #undef DEBUG_CARETMODE
4498