KHtml

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

KDE's Doxygen guidelines are available online.