• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.8.4 API Reference
  • KDE Home
  • Contact Us
 

KHTML

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

KDE's Doxygen guidelines are available online.

KHTML

Skip menu "KHTML"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdelibs-4.8.4 API Reference

Skip menu "kdelibs-4.8.4 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal