fix logic
[personal-kdelibs.git] / khtml / khtmlview.cpp
blob21e439284baaf72a5ec4ece309596cc89e6d83c6
1 /* This file is part of the KDE project
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>
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.
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.
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.
29 #include "khtmlview.h"
31 #include "khtmlview.moc"
33 #include "khtml_part.h"
34 #include "khtml_events.h"
35 #ifdef Q_WS_X11
36 #include <qx11info_x11.h>
37 #endif
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/htmlhashes.h"
60 #include "misc/helper.h"
61 #include "misc/loader.h"
62 #include "khtml_settings.h"
63 #include "khtml_printsettings.h"
65 #include "khtmlpart_p.h"
67 #include <kcursor.h>
68 #include <kdebug.h>
69 #include <kglobalsettings.h>
70 #include <kdialog.h>
71 #include <kiconloader.h>
72 #include <klocale.h>
73 #include <knotification.h>
74 #include <kdeprintdialog.h>
75 #include <kconfig.h>
76 #include <kstandarddirs.h>
77 #include <kstandardshortcut.h>
78 #include <kstringhandler.h>
79 #include <kconfiggroup.h>
81 #include <QtGui/QBitmap>
82 #include <QtGui/QLabel>
83 #include <QtCore/QObject>
84 #include <QtGui/QPainter>
85 #include <QtCore/QHash>
86 #include <QtGui/QToolTip>
87 #include <QtCore/QString>
88 #include <QtGui/QTextDocument>
89 #include <QtCore/QTimer>
90 #include <QtCore/QAbstractEventDispatcher>
91 #include <QtCore/QVector>
92 #include <QtGui/QAbstractScrollArea>
93 #include <QtGui/QPrinter>
94 #include <QtGui/QPrintDialog>
96 //#define DEBUG_FLICKER
98 //#define DEBUG_PIXEL
99 #define FIX_QT_BROKEN_QWIDGET_SCROLL
101 #include <limits.h>
102 #ifdef Q_WS_X11
103 #include <X11/Xlib.h>
104 #include <fixx11h.h>
105 #elif defined(Q_WS_WIN)
106 #include <windows.h>
107 #endif
109 #if 0
110 namespace khtml {
111 void dumpLineBoxes(RenderFlow *flow);
113 #endif
115 using namespace DOM;
116 using namespace khtml;
118 #ifndef NDEBUG
119 static const int sFirstLayoutDelay = 760;
120 static const int sParsingLayoutsInterval = 420;
121 static const int sLayoutAttemptDelay = 400;
122 #else
123 static const int sFirstLayoutDelay = 540;
124 static const int sParsingLayoutsInterval = 360;
125 static const int sLayoutAttemptDelay = 340;
126 #endif
127 static const int sLayoutAttemptIncrement = 20;
128 static const int sParsingLayoutsIncrement = 60;
130 static const int sSmoothScrollTime = 140;
131 static const int sSmoothScrollTick = 14;
132 static const int sSmoothScrollMinStaticPixels = 320*200;
134 class KHTMLViewPrivate {
135 friend class KHTMLView;
136 public:
138 enum PseudoFocusNodes {
139 PFNone,
140 PFTop,
141 PFBottom
144 enum StaticBackgroundState {
145 SBNone = 0,
146 SBPartial,
147 SBFull
150 enum CompletedState {
151 CSNone = 0,
152 CSFull,
153 CSActionPending
156 KHTMLViewPrivate(KHTMLView* v)
157 : underMouse( 0 ), underMouseNonShared( 0 ), oldUnderMouse( 0 )
159 postponed_autorepeat = NULL;
160 scrollingFromWheelTimerId = 0;
161 smoothScrollMode = KHTMLView::SSMWhenEfficient;
162 reset();
163 vpolicy = Qt::ScrollBarAsNeeded;
164 hpolicy = Qt::ScrollBarAsNeeded;
165 formCompletions=0;
166 prevScrollbarVisible = true;
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;
178 ~KHTMLViewPrivate()
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();
189 delete cursorIconWidget;
190 delete m_mouseScrollTimer;
191 delete m_mouseScrollIndicator;
193 void reset()
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 #ifdef DEBUG_PIXEL
222 timer.start();
223 pixelbooth = 0;
224 repaintbooth = 0;
225 #endif
226 scrollBarMoved = false;
227 contentsMoving = false;
228 ignoreWheelEvents = false;
229 scrollingFromWheel = QPoint(-1,-1);
230 borderX = 30;
231 borderY = 30;
232 dx = dy = ddx = ddy = rdx = rdy = dddx = dddy = 0;
233 paged = false;
234 clickX = -1;
235 clickY = -1;
236 clickCount = 0;
237 isDoubleClick = false;
238 scrollingSelf = false;
239 delete postponed_autorepeat;
240 postponed_autorepeat = NULL;
241 layoutTimerId = 0;
242 repaintTimerId = 0;
243 scrollTimerId = 0;
244 scrollSuspended = false;
245 scrollSuspendPreActivate = false;
246 smoothScrolling = false;
247 smoothScrollModeIsDefault = true;
248 shouldSmoothScroll = false;
249 hasFrameset = false;
250 #ifdef FIX_QT_BROKEN_QWIDGET_SCROLL
251 oldVScrollUpdatesEnabled = true;
252 oldHScrollUpdatesEnabled = true;
253 oldHScrollOpaquePE = false;
254 oldVScrollOpaquePE = false;
255 brokenQWidgetScroll = false;
256 shouldBeBlitting = false;
257 #endif
258 complete = false;
259 firstLayoutPending = true;
260 firstRepaintPending = true;
261 needsFullRepaint = true;
262 dirtyLayout = false;
263 layoutSchedulingEnabled = true;
264 painting = false;
265 layoutCounter = 0;
266 layoutAttemptCounter = 0;
267 scheduledLayoutCounter = 0;
268 updateRegion = QRegion();
269 m_dialogsAllowed = true;
270 #ifndef KHTML_NO_TYPE_AHEAD_FIND
271 typeAheadActivated = false;
272 #endif // KHTML_NO_TYPE_AHEAD_FIND
273 accessKeysActivated = false;
274 accessKeysPreActivate = false;
276 // the view might have been built before the part it will be assigned to,
277 // so exceptionally, we need to directly ref/deref KHTMLGlobal to
278 // account for this transitory case.
279 KHTMLGlobal::ref();
280 accessKeysEnabled = KHTMLGlobal::defaultHTMLSettings()->accessKeysEnabled();
281 KHTMLGlobal::deref();
283 emitCompletedAfterRepaint = CSNone;
284 m_mouseEventsTarget = 0;
285 m_clipHolder = 0;
287 void newScrollTimer(QWidget *view, int tid)
289 //kDebug(6000) << "newScrollTimer timer " << tid;
290 view->killTimer(scrollTimerId);
291 scrollTimerId = tid;
292 scrollSuspended = false;
294 enum ScrollDirection { ScrollLeft, ScrollRight, ScrollUp, ScrollDown };
296 void adjustScroller(QWidget *view, ScrollDirection direction, ScrollDirection oppositedir)
298 static const struct { int msec, pixels; } timings [] = {
299 {320,1}, {224,1}, {160,1}, {112,1}, {80,1}, {56,1}, {40,1},
300 {28,1}, {20,1}, {20,2}, {20,3}, {20,4}, {20,6}, {20,8}, {0,0}
302 if (!scrollTimerId ||
303 (static_cast<int>(scrollDirection) != direction &&
304 (static_cast<int>(scrollDirection) != oppositedir || scrollSuspended))) {
305 scrollTiming = 6;
306 scrollBy = timings[scrollTiming].pixels;
307 scrollDirection = direction;
308 newScrollTimer(view, view->startTimer(timings[scrollTiming].msec));
309 } else if (scrollDirection == direction &&
310 timings[scrollTiming+1].msec && !scrollSuspended) {
311 scrollBy = timings[++scrollTiming].pixels;
312 newScrollTimer(view, view->startTimer(timings[scrollTiming].msec));
313 } else if (scrollDirection == oppositedir) {
314 if (scrollTiming) {
315 scrollBy = timings[--scrollTiming].pixels;
316 newScrollTimer(view, view->startTimer(timings[scrollTiming].msec));
319 scrollSuspended = false;
322 bool haveZoom() const { return zoomLevel != 100; }
324 void startScrolling()
326 smoothScrolling = true;
327 smoothScrollTimer.start(sSmoothScrollTick);
328 shouldSmoothScroll = false;
329 #ifdef FIX_QT_BROKEN_QWIDGET_SCROLL
330 if (view->horizontalScrollBar()->isVisible() && view->verticalScrollBar()->isVisible()) {
331 if (!dx) {
332 oldHScrollOpaquePE = view->horizontalScrollBar()->parentWidget()->testAttribute( Qt::WA_OpaquePaintEvent );
333 view->horizontalScrollBar()->parentWidget()->setAttribute( Qt::WA_OpaquePaintEvent );
334 oldHScrollUpdatesEnabled = view->horizontalScrollBar()->parentWidget()->updatesEnabled();
335 view->horizontalScrollBar()->parentWidget()->setUpdatesEnabled( false );
337 if (!dy) {
338 oldVScrollOpaquePE = view->verticalScrollBar()->parentWidget()->testAttribute( Qt::WA_OpaquePaintEvent );
339 view->verticalScrollBar()->parentWidget()->setAttribute( Qt::WA_OpaquePaintEvent );
340 oldVScrollUpdatesEnabled = view->verticalScrollBar()->parentWidget()->updatesEnabled();
341 view->verticalScrollBar()->parentWidget()->setUpdatesEnabled( false );
344 #endif
347 void stopScrolling()
349 smoothScrollTimer.stop();
350 dx = dy = 0;
351 ddx = ddy = 0;
352 rdx = rdy = 0;
353 dddx = dddy = 0;
354 updateContentsXY();
355 smoothScrolling = false;
356 shouldSmoothScroll = false;
357 #ifdef FIX_QT_BROKEN_QWIDGET_SCROLL
358 if (!oldHScrollOpaquePE && view->horizontalScrollBar()->parentWidget()->testAttribute( Qt::WA_OpaquePaintEvent ))
359 view->horizontalScrollBar()->parentWidget()->setAttribute( Qt::WA_OpaquePaintEvent, false );
360 if (!oldVScrollOpaquePE && view->verticalScrollBar()->parentWidget()->testAttribute( Qt::WA_OpaquePaintEvent ))
361 view->verticalScrollBar()->parentWidget()->setAttribute( Qt::WA_OpaquePaintEvent, false );
362 if (!view->horizontalScrollBar()->parentWidget()->updatesEnabled() && oldHScrollUpdatesEnabled)
363 view->horizontalScrollBar()->parentWidget()->setUpdatesEnabled( true );
364 if (!view->verticalScrollBar()->parentWidget()->updatesEnabled() && oldVScrollUpdatesEnabled)
365 view->verticalScrollBar()->parentWidget()->setUpdatesEnabled( true );
366 #endif
369 void updateContentsXY()
371 contentsX = QApplication::isRightToLeft() ?
372 view->horizontalScrollBar()->maximum()-view->horizontalScrollBar()->value() : view->horizontalScrollBar()->value();
373 contentsY = view->verticalScrollBar()->value();
376 void scrollExternalWidgets(int dx, int dy)
378 if (visibleWidgets.isEmpty())
379 return;
381 QHashIterator<void*, QWidget*> it(visibleWidgets);
382 while (it.hasNext()) {
383 it.next();
384 it.value()->move( it.value()->pos() + QPoint(dx, dy) );
388 #ifdef DEBUG_PIXEL
389 QTime timer;
390 unsigned int pixelbooth;
391 unsigned int repaintbooth;
392 #endif
394 NodeImpl *underMouse;
395 NodeImpl *underMouseNonShared;
396 NodeImpl *oldUnderMouse;
398 // Do not adjust bitfield enums sizes.
399 // They are oversized because they are signed on some platforms.
400 bool tabMovePending:1;
401 bool lastTabbingDirection:1;
402 PseudoFocusNodes pseudoFocusNode:3;
403 bool scrollBarMoved:1;
404 bool contentsMoving:1;
406 Qt::ScrollBarPolicy vpolicy;
407 Qt::ScrollBarPolicy hpolicy;
408 bool prevScrollbarVisible:1;
409 bool linkPressed:1;
410 bool ignoreWheelEvents:1;
411 StaticBackgroundState staticWidget: 3;
412 int staticObjectsCount;
413 int fixedObjectsCount;
415 int zoomLevel;
416 int borderX, borderY;
417 int dx, dy, ddx, ddy, rdx, rdy, dddx, dddy;
418 KConfig *formCompletions;
420 int clickX, clickY, clickCount;
421 bool isDoubleClick;
423 bool paged;
425 bool scrollingSelf;
426 int contentsX, contentsY;
427 int layoutTimerId;
428 QKeyEvent* postponed_autorepeat;
430 int repaintTimerId;
431 int scrollTimerId;
432 int scrollTiming;
433 int scrollBy;
434 ScrollDirection scrollDirection :3;
435 bool scrollSuspended :1;
436 bool scrollSuspendPreActivate :1;
437 KHTMLView::SmoothScrollingMode smoothScrollMode :3;
438 bool smoothScrolling :1;
439 bool smoothScrollModeIsDefault :1;
440 bool shouldSmoothScroll :1;
441 bool hasFrameset :1;
442 #ifdef FIX_QT_BROKEN_QWIDGET_SCROLL
443 bool oldHScrollUpdatesEnabled :1;
444 bool oldVScrollUpdatesEnabled :1;
445 bool oldHScrollOpaquePE :1;
446 bool oldVScrollOpaquePE :1;
447 bool brokenQWidgetScroll :1;
448 bool shouldBeBlitting :1;
449 #endif
450 bool complete :1;
451 bool firstLayoutPending :1;
452 bool firstRepaintPending :1;
453 bool layoutSchedulingEnabled :1;
454 bool needsFullRepaint :1;
455 bool painting :1;
456 bool possibleTripleClick :1;
457 bool dirtyLayout :1;
458 bool m_dialogsAllowed :1;
459 int layoutCounter;
460 int layoutAttemptCounter;
461 int scheduledLayoutCounter;
462 QRegion updateRegion;
463 QTimer smoothScrollTimer;
464 QHash<void*, QWidget*> visibleWidgets;
465 #ifndef KHTML_NO_TYPE_AHEAD_FIND
466 QString findString;
467 QTimer timer;
468 bool findLinksOnly;
469 bool typeAheadActivated;
470 #endif // KHTML_NO_TYPE_AHEAD_FIND
471 bool accessKeysEnabled;
472 bool accessKeysActivated;
473 bool accessKeysPreActivate;
474 CompletedState emitCompletedAfterRepaint;
476 QLabel* cursorIconWidget;
477 KHTMLView::LinkCursor cursorIconType;
479 // scrolling activated by MMB
480 short m_mouseScroll_byX;
481 short m_mouseScroll_byY;
482 QPoint scrollingFromWheel;
483 int scrollingFromWheelTimerId;
484 QTimer *m_mouseScrollTimer;
485 QWidget *m_mouseScrollIndicator;
486 QPointer<QWidget> m_mouseEventsTarget;
487 QStack<QRegion>* m_clipHolder;
488 KHTMLView* view;
491 #ifndef QT_NO_TOOLTIP
493 /** calculates the client-side image map rectangle for the given image element
494 * @param img image element
495 * @param scrollOfs scroll offset of viewport in content coordinates
496 * @param p position to be probed in viewport coordinates
497 * @param r returns the bounding rectangle in content coordinates
498 * @param s returns the title string
499 * @return true if an appropriate area was found -- only in this case r and
500 * s are valid, false otherwise
502 static bool findImageMapRect(HTMLImageElementImpl *img, const QPoint &scrollOfs,
503 const QPoint &p, QRect &r, QString &s)
505 HTMLMapElementImpl* map;
506 if (img && img->document()->isHTMLDocument() &&
507 (map = static_cast<HTMLDocumentImpl*>(img->document())->getMap(img->imageMap()))) {
508 RenderObject::NodeInfo info(true, false);
509 RenderObject *rend = img->renderer();
510 int ax, ay;
511 if (!rend || !rend->absolutePosition(ax, ay))
512 return false;
513 // we're a client side image map
514 bool inside = map->mapMouseEvent(p.x() - ax + scrollOfs.x(),
515 p.y() - ay + scrollOfs.y(), rend->contentWidth(),
516 rend->contentHeight(), info);
517 if (inside && info.URLElement()) {
518 HTMLAreaElementImpl *area = static_cast<HTMLAreaElementImpl *>(info.URLElement());
519 Q_ASSERT(area->id() == ID_AREA);
520 s = area->getAttribute(ATTR_TITLE).string();
521 QRegion reg = area->cachedRegion();
522 if (!s.isEmpty() && !reg.isEmpty()) {
523 r = reg.boundingRect();
524 r.translate(ax, ay);
525 return true;
529 return false;
532 bool KHTMLView::event( QEvent* e )
534 switch ( e->type() ) {
535 case QEvent::ToolTip: {
536 QHelpEvent *he = static_cast<QHelpEvent*>(e);
537 QPoint p = he->pos();
539 DOM::NodeImpl *node = d->underMouseNonShared;
540 QRect region;
541 while ( node ) {
542 if ( node->isElementNode() ) {
543 DOM::ElementImpl *e = static_cast<DOM::ElementImpl*>( node );
544 QRect r;
545 QString s;
546 bool found = false;
547 // for images, check if it is part of a client-side image map,
548 // and query the <area>s' title attributes, too
549 if (e->id() == ID_IMG && !e->getAttribute( ATTR_USEMAP ).isEmpty()) {
550 found = findImageMapRect(static_cast<HTMLImageElementImpl *>(e),
551 viewportToContents(QPoint(0, 0)), p, r, s);
553 if (!found) {
554 s = e->getAttribute( ATTR_TITLE ).string();
555 r = node->getRect();
557 region |= QRect( contentsToViewport( r.topLeft() ), r.size() );
558 if ( !s.isEmpty() ) {
559 QToolTip::showText( viewport()->mapToGlobal(region.bottomLeft()),
560 Qt::convertFromPlainText( s, Qt::WhiteSpaceNormal ) );
561 break;
564 node = node->parentNode();
566 return true;
569 case QEvent::DragEnter:
570 case QEvent::DragMove:
571 case QEvent::DragLeave:
572 case QEvent::Drop:
573 // In Qt4, one needs to both call accept() on the DND event and return
574 // true on ::event for the candidate widget for the drop to be possible.
575 // Apps hosting us, such as konq, can do the former but not the later.
576 // We will do the second bit, as it's a no-op unless someone else explicitly
577 // accepts the event. We need to skip the scrollarea to do that,
578 // since it will just skip the events, both killing the drop, and
579 // not permitting us to forward it up the part hiearchy in our dragEnterEvent,
580 // etc. handlers
581 return QWidget::event(e);
582 case QEvent::StyleChange:
583 case QEvent::LayoutRequest: {
584 updateScrollBars();
585 return QAbstractScrollArea::event(e);
587 case QEvent::PaletteChange:
588 slotPaletteChanged();
589 return QScrollArea::event(e);
590 default:
591 return QScrollArea::event(e);
594 #endif
596 KHTMLView::KHTMLView( KHTMLPart *part, QWidget *parent )
597 : QScrollArea( parent ), d( new KHTMLViewPrivate( this ) )
599 m_medium = "screen";
601 m_part = part;
603 QScrollArea::setVerticalScrollBarPolicy(d->vpolicy);
604 QScrollArea::setHorizontalScrollBarPolicy(d->hpolicy);
606 #ifndef KHTML_NO_TYPE_AHEAD_FIND
607 connect(&d->timer, SIGNAL(timeout()), this, SLOT(findTimeout()));
608 #endif // KHTML_NO_TYPE_AHEAD_FIND
610 init();
611 widget()->setMouseTracking(true);
614 KHTMLView::~KHTMLView()
616 closeChildDialogs();
617 if (m_part)
619 DOM::DocumentImpl *doc = m_part->xmlDocImpl();
620 if (doc)
621 doc->detach();
623 delete d;
626 void KHTMLView::setPart(KHTMLPart *part)
628 assert(part && !m_part);
629 m_part = part;
632 void KHTMLView::init()
634 // Do not access the part here. It might not be fully constructed.
636 setFrameStyle(QFrame::NoFrame);
637 setFocusPolicy(Qt::StrongFocus);
638 viewport()->setFocusProxy(this);
640 _marginWidth = -1; // undefined
641 _marginHeight = -1;
642 _width = 0;
643 _height = 0;
645 installEventFilter(this);
647 setAcceptDrops(true);
648 if (!widget())
649 setWidget( new QWidget(this) );
650 widget()->setAttribute( Qt::WA_NoSystemBackground );
652 verticalScrollBar()->setCursor( Qt::ArrowCursor );
653 horizontalScrollBar()->setCursor( Qt::ArrowCursor );
655 connect(&d->smoothScrollTimer, SIGNAL(timeout()), this, SLOT(scrollTick()));
658 void KHTMLView::resizeContentsToViewport()
660 QSize s = viewport()->size();
661 resizeContents(s.width(), s.height());
665 // called by KHTMLPart::clear()
666 void KHTMLView::clear()
668 #ifndef KHTML_NO_TYPE_AHEAD_FIND
669 if( d->typeAheadActivated )
670 findTimeout();
671 #endif
672 if (d->accessKeysEnabled && d->accessKeysActivated)
673 accessKeysTimeout();
674 viewport()->unsetCursor();
675 if ( d->cursorIconWidget )
676 d->cursorIconWidget->hide();
677 if (d->smoothScrolling)
678 d->stopScrolling();
679 d->reset();
680 QAbstractEventDispatcher::instance()->unregisterTimers(this);
681 emit cleared();
683 QScrollArea::setHorizontalScrollBarPolicy(d->hpolicy);
684 QScrollArea::setVerticalScrollBarPolicy(d->vpolicy);
685 verticalScrollBar()->setEnabled( false );
686 horizontalScrollBar()->setEnabled( false );
690 void KHTMLView::hideEvent(QHideEvent* e)
692 QScrollArea::hideEvent(e);
693 if ( m_part && m_part->xmlDocImpl() )
694 m_part->xmlDocImpl()->docLoader()->pauseAnimations();
697 void KHTMLView::showEvent(QShowEvent* e)
699 QScrollArea::showEvent(e);
700 if ( m_part && m_part->xmlDocImpl() )
701 m_part->xmlDocImpl()->docLoader()->resumeAnimations();
704 void KHTMLView::setMouseEventsTarget( QWidget* w )
706 d->m_mouseEventsTarget = w;
709 QWidget* KHTMLView::mouseEventsTarget() const
711 return d->m_mouseEventsTarget;
714 void KHTMLView::setClipHolder( QStack<QRegion>* ch )
716 d->m_clipHolder = ch;
719 QStack<QRegion>* KHTMLView::clipHolder() const
721 return d->m_clipHolder;
724 int KHTMLView::contentsWidth() const
726 return widget() ? widget()->width() : 0;
729 int KHTMLView::contentsHeight() const
731 return widget() ? widget()->height() : 0;
734 void KHTMLView::resizeContents(int w, int h)
736 if (!widget())
737 return;
738 widget()->resize(w, h);
739 if (!widget()->isVisible())
740 updateScrollBars();
741 #ifdef FIX_QT_BROKEN_QWIDGET_SCROLL
742 if (!horizontalScrollBar()->isVisible() || !verticalScrollBar()->isVisible())
743 d->brokenQWidgetScroll = false;
744 #endif
747 int KHTMLView::contentsX() const
749 return d->contentsX;
752 int KHTMLView::contentsY() const
754 return d->contentsY;
757 int KHTMLView::visibleWidth() const
759 if (m_kwp->isRedirected()) {
760 // our RenderWidget knows better
761 if (RenderWidget* rw = m_kwp->renderWidget()) {
762 int ret = rw->width()-rw->paddingLeft()-rw->paddingRight()-rw->borderLeft()-rw->borderRight();
763 if (verticalScrollBar()->isVisible()) {
764 ret -= style()->pixelMetric(QStyle::PM_ScrollBarExtent);
765 int lhs = style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing);
766 if (lhs > 0)
767 ret -= lhs;
768 ret = qMax(0, ret);
770 return ret;
773 return viewport()->width();
776 int KHTMLView::visibleHeight() const
778 if (m_kwp->isRedirected()) {
779 // our RenderWidget knows better
780 if (RenderWidget* rw = m_kwp->renderWidget()) {
781 int ret = rw->height()-rw->paddingBottom()-rw->paddingTop()-rw->borderTop()-rw->borderBottom();
782 if (horizontalScrollBar()->isVisible()) {
783 ret -= style()->pixelMetric(QStyle::PM_ScrollBarExtent);
784 int lvs = style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing);
785 if (lvs > 0)
786 ret -= lvs;
787 ret = qMax(0, ret);
789 return ret;
792 return viewport()->height();
795 void KHTMLView::setContentsPos( int x, int y)
797 horizontalScrollBar()->setValue( QApplication::isRightToLeft() ?
798 horizontalScrollBar()->maximum()-x : x );
799 verticalScrollBar()->setValue( y );
802 void KHTMLView::scrollBy(int x, int y)
804 horizontalScrollBar()->setValue( horizontalScrollBar()->value()+x );
805 verticalScrollBar()->setValue( verticalScrollBar()->value()+y );
808 QPoint KHTMLView::contentsToViewport(const QPoint& p) const
810 return QPoint(p.x()-contentsX(), p.y()-contentsY());
813 void KHTMLView::contentsToViewport(int x, int y, int& cx, int& cy) const
815 QPoint p(x,y);
816 p = contentsToViewport(p);
817 cx = p.x();
818 cy = p.y();
821 QPoint KHTMLView::viewportToContents(const QPoint& p) const
823 return QPoint(p.x()+contentsX(), p.y()+contentsY());
826 void KHTMLView::viewportToContents(int x, int y, int& cx, int& cy) const
828 QPoint p(x,y);
829 p = viewportToContents(p);
830 cx = p.x();
831 cy = p.y();
834 void KHTMLView::updateContents(int x, int y, int w, int h)
836 applyTransforms(x, y, w, h);
837 if (m_kwp->isRedirected()) {
838 QPoint off = m_kwp->absolutePos();
839 KHTMLView* pview = m_part->parentPart()->view();
840 pview->updateContents(x+off.x(), y+off.y(), w, h);
841 } else
842 widget()->update(x, y, w, h);
845 void KHTMLView::updateContents( const QRect& r )
847 updateContents( r.x(), r.y(), r.width(), r.height() );
850 void KHTMLView::repaintContents(int x, int y, int w, int h)
852 applyTransforms(x, y, w, h);
853 if (m_kwp->isRedirected()) {
854 QPoint off = m_kwp->absolutePos();
855 KHTMLView* pview = m_part->parentPart()->view();
856 pview->repaintContents(x+off.x(), y+off.y(), w, h);
857 } else
858 widget()->repaint(x, y, w, h);
861 void KHTMLView::repaintContents( const QRect& r )
863 repaintContents( r.x(), r.y(), r.width(), r.height() );
866 void KHTMLView::applyTransforms( int& x, int& y, int& w, int& h) const
868 if (d->haveZoom()) {
869 const int z = d->zoomLevel;
870 x = x*z/100;
871 y = y*z/100;
872 w = w*z/100;
873 h = h*z/100;
875 x -= contentsX();
876 y -= contentsY();
879 void KHTMLView::revertTransforms( int& x, int& y, int& w, int& h) const
881 x += contentsX();
882 y += contentsY();
883 if (d->haveZoom()) {
884 const int z = d->zoomLevel;
885 x = x*100/z;
886 y = y*100/z;
887 w = w*100/z;
888 h = h*100/z;
892 void KHTMLView::revertTransforms( int& x, int& y ) const
894 int dummy = 0;
895 revertTransforms(x, y, dummy, dummy);
898 void KHTMLView::resizeEvent (QResizeEvent* /*e*/)
900 updateScrollBars();
902 // If we didn't load anything, make white area as big as the view
903 if (!m_part->xmlDocImpl())
904 resizeContentsToViewport();
906 // Viewport-dependent media queries may cause us to need completely different style information.
907 if (m_part->xmlDocImpl() && m_part->xmlDocImpl()->styleSelector()->affectedByViewportChange()) {
908 m_part->xmlDocImpl()->updateStyleSelector();
911 if (d->layoutSchedulingEnabled)
912 layout();
914 QApplication::sendPostedEvents(viewport(), QEvent::Paint);
916 if ( m_part && m_part->xmlDocImpl() )
917 m_part->xmlDocImpl()->dispatchWindowEvent( EventImpl::RESIZE_EVENT, false, false );
920 void KHTMLView::paintEvent( QPaintEvent *e )
922 QPainter p(widget());
924 QRect r = e->rect();
925 QRect v(contentsX(), contentsY(), visibleWidth(), visibleHeight());
926 QPoint off(contentsX(),contentsY());
927 p.translate(-off);
928 r.translate(off);
930 r = r.intersect(v);
932 if (!r.isValid() || r.isEmpty()) return;
934 #ifdef FIX_QT_BROKEN_QWIDGET_SCROLL
935 if (d->shouldBeBlitting && r.width() == v.width() && r.height() == v.height()) {
936 d->brokenQWidgetScroll = true;
938 d->shouldBeBlitting = false;
939 #endif
941 if (d->haveZoom()) {
942 p.scale( d->zoomLevel/100., d->zoomLevel/100.);
944 r.setX(r.x()*100/d->zoomLevel);
945 r.setY(r.y()*100/d->zoomLevel);
946 r.setWidth(r.width()*100/d->zoomLevel);
947 r.setHeight(r.height()*100/d->zoomLevel);
948 r.adjust(-1,-1,1,1);
950 p.setClipRect(r);
952 int ex = r.x();
953 int ey = r.y();
954 int ew = r.width();
955 int eh = r.height();
957 if(!m_part || !m_part->xmlDocImpl() || !m_part->xmlDocImpl()->renderer()) {
958 p.fillRect(ex, ey, ew, eh, palette().brush(QPalette::Active, QPalette::Base));
959 return;
960 } else if ( d->complete && static_cast<RenderCanvas*>(m_part->xmlDocImpl()->renderer())->needsLayout() ) {
961 // an external update request happens while we have a layout scheduled
962 unscheduleRelayout();
963 layout();
964 } else if (m_part->xmlDocImpl()->tokenizer()) {
965 m_part->xmlDocImpl()->tokenizer()->setNormalYeldDelay();
968 if (d->painting) {
969 kDebug( 6000 ) << "WARNING: paintEvent reentered! ";
970 kDebug( 6000 ) << kBacktrace();
971 return;
973 d->painting = true;
975 m_part->xmlDocImpl()->renderer()->layer()->paint(&p, r);
977 if (d->hasFrameset) {
978 NodeImpl *body = static_cast<HTMLDocumentImpl*>(m_part->xmlDocImpl())->body();
979 if(body && body->renderer() && body->id() == ID_FRAMESET)
980 static_cast<RenderFrameSet*>(body->renderer())->paintFrameSetRules(&p, r);
981 else
982 d->hasFrameset = false;
985 khtml::DrawContentsEvent event( &p, ex, ey, ew, eh );
986 QApplication::sendEvent( m_part, &event );
988 if (d->contentsMoving && !d->smoothScrolling && widget()->underMouse()) {
989 QMouseEvent *tempEvent = new QMouseEvent( QEvent::MouseMove, widget()->mapFromGlobal( QCursor::pos() ),
990 Qt::NoButton, Qt::NoButton, Qt::NoModifier );
991 QApplication::postEvent(widget(), tempEvent);
994 d->painting = false;
995 d->firstRepaintPending = false;
998 void KHTMLView::setMarginWidth(int w)
1000 // make it update the rendering area when set
1001 _marginWidth = w;
1004 void KHTMLView::setMarginHeight(int h)
1006 // make it update the rendering area when set
1007 _marginHeight = h;
1010 void KHTMLView::layout()
1012 if( m_part && m_part->xmlDocImpl() ) {
1013 DOM::DocumentImpl *document = m_part->xmlDocImpl();
1015 khtml::RenderCanvas* canvas = static_cast<khtml::RenderCanvas *>(document->renderer());
1016 if ( !canvas ) return;
1018 d->layoutSchedulingEnabled=false;
1019 d->dirtyLayout = true;
1021 // the reference object for the overflow property on canvas
1022 RenderObject * ref = 0;
1023 RenderObject* root = document->documentElement() ? document->documentElement()->renderer() : 0;
1025 if (document->isHTMLDocument()) {
1026 NodeImpl *body = static_cast<HTMLDocumentImpl*>(document)->body();
1027 if(body && body->renderer() && body->id() == ID_FRAMESET) {
1028 QScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1029 QScrollArea::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1030 body->renderer()->setNeedsLayout(true);
1031 d->hasFrameset = true;
1033 else if (root) // only apply body's overflow to canvas if root has a visible overflow
1034 ref = (!body || root->style()->hidesOverflow()) ? root : body->renderer();
1035 } else {
1036 ref = root;
1038 if (ref) {
1039 if( ref->style()->overflowX() == OHIDDEN ) {
1040 if (d->hpolicy == Qt::ScrollBarAsNeeded) QScrollArea::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1041 } else if (ref->style()->overflowX() == OSCROLL ) {
1042 if (d->hpolicy == Qt::ScrollBarAsNeeded) QScrollArea::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
1043 } else if (horizontalScrollBarPolicy() != d->hpolicy) {
1044 QScrollArea::setHorizontalScrollBarPolicy(d->hpolicy);
1046 if ( ref->style()->overflowY() == OHIDDEN ) {
1047 if (d->vpolicy == Qt::ScrollBarAsNeeded) QScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1048 } else if (ref->style()->overflowY() == OSCROLL ) {
1049 if (d->vpolicy == Qt::ScrollBarAsNeeded) QScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
1050 } else if (verticalScrollBarPolicy() != d->vpolicy) {
1051 QScrollArea::setVerticalScrollBarPolicy(d->vpolicy);
1054 d->needsFullRepaint = d->firstLayoutPending;
1055 if (_height != visibleHeight() || _width != visibleWidth()) {;
1056 d->needsFullRepaint = true;
1057 _height = visibleHeight();
1058 _width = visibleWidth();
1061 canvas->layout();
1063 emit finishedLayout();
1064 if (d->firstLayoutPending) {
1065 // make sure firstLayoutPending is set to false now in case this layout
1066 // wasn't scheduled
1067 d->firstLayoutPending = false;
1068 verticalScrollBar()->setEnabled( true );
1069 horizontalScrollBar()->setEnabled( true );
1071 d->layoutCounter++;
1073 if (d->accessKeysEnabled && d->accessKeysActivated) {
1074 emit hideAccessKeys();
1075 displayAccessKeys();
1078 else
1079 _width = visibleWidth();
1081 if (d->layoutTimerId)
1082 killTimer(d->layoutTimerId);
1083 d->layoutTimerId = 0;
1084 d->layoutSchedulingEnabled=true;
1087 void KHTMLView::closeChildDialogs()
1089 QList<QDialog *> dlgs = findChildren<QDialog *>();
1090 foreach (QDialog *dlg, dlgs)
1092 KDialog* dlgbase = dynamic_cast<KDialog*>( dlg );
1093 if ( dlgbase ) {
1094 if ( dlgbase->testAttribute( Qt::WA_ShowModal ) ) {
1095 kDebug(6000) << "closeChildDialogs: closing dialog " << dlgbase;
1096 // close() ends up calling QButton::animateClick, which isn't immediate
1097 // we need something the exits the event loop immediately (#49068)
1098 dlgbase->reject();
1101 else
1103 kWarning() << "closeChildDialogs: not a KDialog! Don't use QDialogs in KDE! " << static_cast<QWidget*>(dlg);
1104 static_cast<QWidget*>(dlg)->hide();
1107 d->m_dialogsAllowed = false;
1110 bool KHTMLView::dialogsAllowed() {
1111 bool allowed = d->m_dialogsAllowed;
1112 KHTMLPart* p = m_part->parentPart();
1113 if (p && p->view())
1114 allowed &= p->view()->dialogsAllowed();
1115 return allowed;
1118 void KHTMLView::closeEvent( QCloseEvent* ev )
1120 closeChildDialogs();
1121 QScrollArea::closeEvent( ev );
1124 void KHTMLView::setZoomLevel(int percent)
1126 percent = percent < 20 ? 20 : (percent > 800 ? 800 : percent);
1127 int oldpercent = d->zoomLevel;
1128 d->zoomLevel = percent;
1129 if (percent != oldpercent) {
1130 if (d->layoutSchedulingEnabled)
1131 layout();
1132 widget()->update();
1136 int KHTMLView::zoomLevel() const
1138 return d->zoomLevel;
1141 void KHTMLView::setSmoothScrollingMode( SmoothScrollingMode m )
1143 d->smoothScrollMode = m;
1144 d->smoothScrollModeIsDefault = false;
1145 if (d->smoothScrolling && !m)
1146 d->stopScrolling();
1149 void KHTMLView::setSmoothScrollingModeDefault( SmoothScrollingMode m )
1151 // check for manual override
1152 if (!d->smoothScrollModeIsDefault)
1153 return;
1154 d->smoothScrollMode = m;
1155 if (d->smoothScrolling && !m)
1156 d->stopScrolling();
1159 KHTMLView::SmoothScrollingMode KHTMLView::smoothScrollingMode( ) const
1161 return d->smoothScrollMode;
1165 // Event Handling
1167 /////////////////
1169 void KHTMLView::mousePressEvent( QMouseEvent *_mouse )
1171 if (!m_part->xmlDocImpl()) return;
1172 if (d->possibleTripleClick && ( _mouse->button() & Qt::MouseButtonMask ) == Qt::LeftButton)
1174 mouseDoubleClickEvent( _mouse ); // it handles triple clicks too
1175 return;
1178 int xm = _mouse->x();
1179 int ym = _mouse->y();
1180 revertTransforms(xm, ym);
1182 // kDebug( 6000 ) << "mousePressEvent: viewport=("<<_mouse->x()-contentsX()<<"/"<<_mouse->y()-contentsY()<<"), contents=(" << xm << "/" << ym << ")\n";
1184 d->isDoubleClick = false;
1186 DOM::NodeImpl::MouseEvent mev( _mouse->buttons(), DOM::NodeImpl::MousePress );
1187 m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev );
1189 //kDebug(6000) << "innerNode="<<mev.innerNode.nodeName().string();
1191 if ( (_mouse->button() == Qt::MidButton) &&
1192 !m_part->d->m_bOpenMiddleClick && !d->m_mouseScrollTimer &&
1193 mev.url.isNull() && (mev.innerNode.elementId() != ID_INPUT) ) {
1194 QPoint point = mapFromGlobal( _mouse->globalPos() );
1196 d->m_mouseScroll_byX = 0;
1197 d->m_mouseScroll_byY = 0;
1199 d->m_mouseScrollTimer = new QTimer( this );
1200 connect( d->m_mouseScrollTimer, SIGNAL(timeout()), this, SLOT(slotMouseScrollTimer()) );
1202 if ( !d->m_mouseScrollIndicator ) {
1203 QPixmap pixmap( 48, 48 ), icon;
1204 pixmap.fill( QColor( qRgba( 127, 127, 127, 127 ) ) );
1206 QPainter p( &pixmap );
1207 QStyleOption option;
1209 option.rect.setRect( 16, 0, 16, 16 );
1210 QApplication::style()->drawPrimitive( QStyle::PE_IndicatorArrowUp, &option, &p );
1211 option.rect.setRect( 0, 16, 16, 16 );
1212 QApplication::style()->drawPrimitive( QStyle::PE_IndicatorArrowLeft, &option, &p );
1213 option.rect.setRect( 16, 32, 16, 16 );
1214 QApplication::style()->drawPrimitive( QStyle::PE_IndicatorArrowDown, &option, &p );
1215 option.rect.setRect( 32, 16, 16, 16 );
1216 QApplication::style()->drawPrimitive( QStyle::PE_IndicatorArrowRight, &option, &p );
1217 p.drawEllipse( 23, 23, 2, 2 );
1219 d->m_mouseScrollIndicator = new QWidget( this );
1220 d->m_mouseScrollIndicator->setFixedSize( 48, 48 );
1221 QPalette palette;
1222 palette.setBrush( d->m_mouseScrollIndicator->backgroundRole(), QBrush( pixmap ) );
1223 d->m_mouseScrollIndicator->setPalette( palette );
1225 d->m_mouseScrollIndicator->move( point.x()-24, point.y()-24 );
1227 bool hasHorBar = visibleWidth() < contentsWidth();
1228 bool hasVerBar = visibleHeight() < contentsHeight();
1230 KConfigGroup cg( KGlobal::config(), "HTML Settings" );
1231 if ( cg.readEntry( "ShowMouseScrollIndicator", true ) ) {
1232 d->m_mouseScrollIndicator->show();
1233 d->m_mouseScrollIndicator->unsetCursor();
1235 QBitmap mask = d->m_mouseScrollIndicator->palette().brush(d->m_mouseScrollIndicator->backgroundRole()).texture().createHeuristicMask( true );
1237 if ( hasHorBar && !hasVerBar ) {
1238 QBitmap bm( 16, 16 );
1239 bm.clear();
1240 QPainter painter( &mask );
1241 painter.drawPixmap( QRectF( 16, 0, bm.width(), bm.height() ), bm, bm.rect() );
1242 painter.drawPixmap( QRectF( 16, 32, bm.width(), bm.height() ), bm, bm.rect() );
1243 d->m_mouseScrollIndicator->setCursor( Qt::SizeHorCursor );
1245 else if ( !hasHorBar && hasVerBar ) {
1246 QBitmap bm( 16, 16 );
1247 bm.clear();
1248 QPainter painter( &mask );
1249 painter.drawPixmap( QRectF( 0, 16, bm.width(), bm.height() ), bm, bm.rect() );
1250 painter.drawPixmap( QRectF( 32, 16, bm.width(), bm.height() ), bm, bm.rect() );
1251 d->m_mouseScrollIndicator->setCursor( Qt::SizeVerCursor );
1253 else
1254 d->m_mouseScrollIndicator->setCursor( Qt::SizeAllCursor );
1256 d->m_mouseScrollIndicator->setMask( mask );
1258 else {
1259 if ( hasHorBar && !hasVerBar )
1260 viewport()->setCursor( Qt::SizeHorCursor );
1261 else if ( !hasHorBar && hasVerBar )
1262 viewport()->setCursor( Qt::SizeVerCursor );
1263 else
1264 viewport()->setCursor( Qt::SizeAllCursor );
1267 return;
1269 else if ( d->m_mouseScrollTimer ) {
1270 delete d->m_mouseScrollTimer;
1271 d->m_mouseScrollTimer = 0;
1273 if ( d->m_mouseScrollIndicator )
1274 d->m_mouseScrollIndicator->hide();
1277 if (d->clickCount > 0 &&
1278 QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() <= QApplication::startDragDistance())
1279 d->clickCount++;
1280 else {
1281 d->clickCount = 1;
1282 d->clickX = xm;
1283 d->clickY = ym;
1286 bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEDOWN_EVENT,mev.innerNode.handle(),mev.innerNonSharedNode.handle(),true,
1287 d->clickCount,_mouse,true,DOM::NodeImpl::MousePress);
1289 if (!swallowEvent) {
1290 emit m_part->nodeActivated(mev.innerNode);
1292 khtml::MousePressEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode );
1293 QApplication::sendEvent( m_part, &event );
1294 // we might be deleted after this
1298 void KHTMLView::mouseDoubleClickEvent( QMouseEvent *_mouse )
1300 if(!m_part->xmlDocImpl()) return;
1302 int xm = _mouse->x();
1303 int ym = _mouse->y();
1304 revertTransforms(xm, ym);
1306 // kDebug( 6000 ) << "mouseDblClickEvent: x=" << xm << ", y=" << ym;
1308 d->isDoubleClick = true;
1310 DOM::NodeImpl::MouseEvent mev( _mouse->buttons(), DOM::NodeImpl::MouseDblClick );
1311 m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev );
1313 // We do the same thing as mousePressEvent() here, since the DOM does not treat
1314 // single and double-click events as separate (only the detail, i.e. number of clicks differs)
1315 if (d->clickCount > 0 &&
1316 QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() <= QApplication::startDragDistance())
1317 d->clickCount++;
1318 else { // shouldn't happen, if Qt has the same criterias for double clicks.
1319 d->clickCount = 1;
1320 d->clickX = xm;
1321 d->clickY = ym;
1323 bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEDOWN_EVENT,mev.innerNode.handle(),mev.innerNonSharedNode.handle(),true,
1324 d->clickCount,_mouse,true,DOM::NodeImpl::MouseDblClick);
1326 if (!swallowEvent) {
1327 khtml::MouseDoubleClickEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode, d->clickCount );
1328 QApplication::sendEvent( m_part, &event );
1331 d->possibleTripleClick=true;
1332 QTimer::singleShot(QApplication::doubleClickInterval(),this,SLOT(tripleClickTimeout()));
1335 void KHTMLView::tripleClickTimeout()
1337 d->possibleTripleClick = false;
1338 d->clickCount = 0;
1341 static bool targetOpensNewWindow(KHTMLPart *part, QString target)
1343 if (!target.isEmpty() && (target.toLower() != "_top") &&
1344 (target.toLower() != "_self") && (target.toLower() != "_parent")) {
1345 if (target.toLower() == "_blank")
1346 return true;
1347 else {
1348 while (part->parentPart())
1349 part = part->parentPart();
1350 if (!part->frameExists(target))
1351 return true;
1354 return false;
1357 void KHTMLView::mouseMoveEvent( QMouseEvent * _mouse )
1359 if ( d->m_mouseScrollTimer ) {
1360 QPoint point = mapFromGlobal( _mouse->globalPos() );
1362 int deltaX = point.x() - d->m_mouseScrollIndicator->x() - 24;
1363 int deltaY = point.y() - d->m_mouseScrollIndicator->y() - 24;
1365 (deltaX > 0) ? d->m_mouseScroll_byX = 1 : d->m_mouseScroll_byX = -1;
1366 (deltaY > 0) ? d->m_mouseScroll_byY = 1 : d->m_mouseScroll_byY = -1;
1368 double adX = qAbs(deltaX)/30.0;
1369 double adY = qAbs(deltaY)/30.0;
1371 d->m_mouseScroll_byX = qMax(qMin(d->m_mouseScroll_byX * int(adX*adX), SHRT_MAX), SHRT_MIN);
1372 d->m_mouseScroll_byY = qMax(qMin(d->m_mouseScroll_byY * int(adY*adY), SHRT_MAX), SHRT_MIN);
1374 if (d->m_mouseScroll_byX == 0 && d->m_mouseScroll_byY == 0) {
1375 d->m_mouseScrollTimer->stop();
1377 else if (!d->m_mouseScrollTimer->isActive()) {
1378 d->m_mouseScrollTimer->start( 20 );
1382 if(!m_part->xmlDocImpl()) return;
1384 int xm = _mouse->x();
1385 int ym = _mouse->y();
1386 revertTransforms(xm, ym);
1388 DOM::NodeImpl::MouseEvent mev( _mouse->buttons(), DOM::NodeImpl::MouseMove );
1389 // Do not modify :hover/:active state while mouse is pressed.
1390 m_part->xmlDocImpl()->prepareMouseEvent( _mouse->buttons() /*readonly ?*/, xm, ym, &mev );
1392 // kDebug(6000) << "mouse move: " << _mouse->pos()
1393 // << " button " << _mouse->button()
1394 // << " state " << _mouse->state() << endl;
1396 DOM::NodeImpl* target = mev.innerNode.handle();
1397 DOM::NodeImpl* fn = m_part->xmlDocImpl()->focusNode();
1399 // a widget may be the real target of this event (e.g. if a scrollbar's slider is being moved)
1400 if (d->m_mouseEventsTarget && fn && fn->renderer() && fn->renderer()->isWidget())
1401 target = fn;
1403 bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEMOVE_EVENT,target,mev.innerNonSharedNode.handle(),false,
1404 0,_mouse,true,DOM::NodeImpl::MouseMove);
1406 if (d->clickCount > 0 &&
1407 QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() > QApplication::startDragDistance()) {
1408 d->clickCount = 0; // moving the mouse outside the threshold invalidates the click
1411 khtml::RenderObject* r = target ? target->renderer() : 0;
1412 bool setCursor = true;
1413 if (r && r->isWidget()) {
1414 RenderWidget* rw = static_cast<RenderWidget*>(r);
1415 KHTMLWidget* kw = qobject_cast<KHTMLView*>(rw->widget())? dynamic_cast<KHTMLWidget*>(rw->widget()) : 0;
1416 if (kw && kw->m_kwp->isRedirected())
1417 setCursor = false;
1419 khtml::RenderStyle* style = (r && r->style()) ? r->style() : 0;
1420 QCursor c;
1421 LinkCursor linkCursor = LINK_NORMAL;
1422 switch ( style ? style->cursor() : CURSOR_AUTO) {
1423 case CURSOR_AUTO:
1424 if ( r && r->isText() && !r->isPointInsideSelection(xm, ym, m_part->caret()) )
1425 c = QCursor(Qt::IBeamCursor);
1426 if ( mev.url.length() && m_part->settings()->changeCursor() ) {
1427 c = m_part->urlCursor();
1428 if (mev.url.string().startsWith("mailto:") && mev.url.string().indexOf('@')>0)
1429 linkCursor = LINK_MAILTO;
1430 else
1431 if ( targetOpensNewWindow( m_part, mev.target.string() ) )
1432 linkCursor = LINK_NEWWINDOW;
1435 if (r && r->isFrameSet() && !static_cast<RenderFrameSet*>(r)->noResize())
1436 c = QCursor(static_cast<RenderFrameSet*>(r)->cursorShape());
1438 break;
1439 case CURSOR_CROSS:
1440 c = QCursor(Qt::CrossCursor);
1441 break;
1442 case CURSOR_POINTER:
1443 c = m_part->urlCursor();
1444 if (mev.url.string().startsWith("mailto:") && mev.url.string().indexOf('@')>0)
1445 linkCursor = LINK_MAILTO;
1446 else
1447 if ( targetOpensNewWindow( m_part, mev.target.string() ) )
1448 linkCursor = LINK_NEWWINDOW;
1449 break;
1450 case CURSOR_PROGRESS:
1451 c = QCursor(Qt::BusyCursor); // working_cursor
1452 break;
1453 case CURSOR_MOVE:
1454 case CURSOR_ALL_SCROLL:
1455 c = QCursor(Qt::SizeAllCursor);
1456 break;
1457 case CURSOR_E_RESIZE:
1458 case CURSOR_W_RESIZE:
1459 case CURSOR_EW_RESIZE:
1460 c = QCursor(Qt::SizeHorCursor);
1461 break;
1462 case CURSOR_N_RESIZE:
1463 case CURSOR_S_RESIZE:
1464 case CURSOR_NS_RESIZE:
1465 c = QCursor(Qt::SizeVerCursor);
1466 break;
1467 case CURSOR_NE_RESIZE:
1468 case CURSOR_SW_RESIZE:
1469 case CURSOR_NESW_RESIZE:
1470 c = QCursor(Qt::SizeBDiagCursor);
1471 break;
1472 case CURSOR_NW_RESIZE:
1473 case CURSOR_SE_RESIZE:
1474 case CURSOR_NWSE_RESIZE:
1475 c = QCursor(Qt::SizeFDiagCursor);
1476 break;
1477 case CURSOR_TEXT:
1478 c = QCursor(Qt::IBeamCursor);
1479 break;
1480 case CURSOR_WAIT:
1481 c = QCursor(Qt::WaitCursor);
1482 break;
1483 case CURSOR_HELP:
1484 c = QCursor(Qt::WhatsThisCursor);
1485 break;
1486 case CURSOR_DEFAULT:
1487 break;
1488 case CURSOR_NONE:
1489 case CURSOR_NOT_ALLOWED:
1490 c = QCursor(Qt::ForbiddenCursor);
1491 break;
1492 case CURSOR_ROW_RESIZE:
1493 c = QCursor(Qt::SplitVCursor);
1494 break;
1495 case CURSOR_COL_RESIZE:
1496 c = QCursor(Qt::SplitHCursor);
1497 break;
1498 case CURSOR_VERTICAL_TEXT:
1499 case CURSOR_CONTEXT_MENU:
1500 case CURSOR_NO_DROP:
1501 case CURSOR_CELL:
1502 case CURSOR_COPY:
1503 case CURSOR_ALIAS:
1504 c = QCursor(Qt::ArrowCursor);
1505 break;
1508 if (!setCursor && style && style->cursor() != CURSOR_AUTO)
1509 setCursor = true;
1511 QWidget* vp = viewport();
1512 for (KHTMLPart* p = m_part; p; p = p->parentPart())
1513 if (!p->parentPart())
1514 vp = p->view()->viewport();
1515 if ( setCursor && vp->cursor().handle() != c.handle() ) {
1516 if( c.shape() == Qt::ArrowCursor) {
1517 for (KHTMLPart* p = m_part; p; p = p->parentPart())
1518 p->view()->viewport()->unsetCursor();
1520 else {
1521 vp->setCursor( c );
1525 if ( linkCursor!=LINK_NORMAL && isVisible() && hasFocus() ) {
1526 #ifdef Q_WS_X11
1528 if( !d->cursorIconWidget ) {
1529 #ifdef Q_WS_X11
1530 d->cursorIconWidget = new QLabel( 0, Qt::X11BypassWindowManagerHint );
1531 XSetWindowAttributes attr;
1532 attr.save_under = True;
1533 XChangeWindowAttributes( QX11Info::display(), d->cursorIconWidget->winId(), CWSaveUnder, &attr );
1534 #else
1535 d->cursorIconWidget = new QLabel( NULL, NULL );
1536 //TODO
1537 #endif
1540 // Update the pixmap if need be.
1541 if (linkCursor != d->cursorIconType) {
1542 d->cursorIconType = linkCursor;
1543 QString cursorIcon;
1544 switch (linkCursor)
1546 case LINK_MAILTO: cursorIcon = "mail-message-new"; break;
1547 case LINK_NEWWINDOW: cursorIcon = "window-new"; break;
1548 default: cursorIcon = "dialog-error"; break;
1551 QPixmap icon_pixmap = KHTMLGlobal::iconLoader()->loadIcon( cursorIcon, KIconLoader::Small, 0, KIconLoader::DefaultState, QStringList(), 0, true );
1553 d->cursorIconWidget->resize( icon_pixmap.width(), icon_pixmap.height());
1554 d->cursorIconWidget->setMask( icon_pixmap.createMaskFromColor(Qt::transparent));
1555 d->cursorIconWidget->setPixmap( icon_pixmap);
1556 d->cursorIconWidget->update();
1559 QPoint c_pos = QCursor::pos();
1560 d->cursorIconWidget->move( c_pos.x() + 15, c_pos.y() + 15 );
1561 #ifdef Q_WS_X11
1562 XRaiseWindow( QX11Info::display(), d->cursorIconWidget->winId());
1563 QApplication::flush();
1564 #elif defined(Q_WS_WIN)
1565 SetWindowPos( d->cursorIconWidget->winId(), HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE );
1566 #else
1567 //TODO?
1568 #endif
1569 d->cursorIconWidget->show();
1570 #endif
1572 else if ( d->cursorIconWidget )
1573 d->cursorIconWidget->hide();
1575 if (r && r->isWidget()) {
1576 _mouse->ignore();
1579 if (!swallowEvent) {
1580 khtml::MouseMoveEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode );
1581 QApplication::sendEvent( m_part, &event );
1585 void KHTMLView::mouseReleaseEvent( QMouseEvent * _mouse )
1587 bool swallowEvent = false;
1589 int xm = _mouse->x();
1590 int ym = _mouse->y();
1591 revertTransforms(xm, ym);
1593 DOM::NodeImpl::MouseEvent mev( _mouse->buttons(), DOM::NodeImpl::MouseRelease );
1595 if ( m_part->xmlDocImpl() )
1597 m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev );
1599 DOM::NodeImpl* target = mev.innerNode.handle();
1600 DOM::NodeImpl* fn = m_part->xmlDocImpl()->focusNode();
1602 // a widget may be the real target of this event (e.g. if a scrollbar's slider is being moved)
1603 if (d->m_mouseEventsTarget && fn && fn->renderer() && fn->renderer()->isWidget())
1604 target = fn;
1606 swallowEvent = dispatchMouseEvent(EventImpl::MOUSEUP_EVENT,target,mev.innerNonSharedNode.handle(),true,
1607 d->clickCount,_mouse,false,DOM::NodeImpl::MouseRelease);
1609 // clear our sticky event target on any mouseRelease event
1610 if (d->m_mouseEventsTarget)
1611 d->m_mouseEventsTarget = 0;
1613 if (d->clickCount > 0 &&
1614 QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() <= QApplication::startDragDistance()) {
1615 QMouseEvent me(d->isDoubleClick ? QEvent::MouseButtonDblClick : QEvent::MouseButtonRelease,
1616 _mouse->pos(), _mouse->button(), _mouse->buttons(), _mouse->modifiers());
1617 dispatchMouseEvent(EventImpl::CLICK_EVENT, mev.innerNode.handle(),mev.innerNonSharedNode.handle(),true,
1618 d->clickCount, &me, true, DOM::NodeImpl::MouseRelease);
1621 khtml::RenderObject* r = target ? target->renderer() : 0;
1622 if (r && r->isWidget())
1623 _mouse->ignore();
1626 if (!swallowEvent) {
1627 khtml::MouseReleaseEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode );
1628 QApplication::sendEvent( m_part, &event );
1632 // returns true if event should be swallowed
1633 bool KHTMLView::dispatchKeyEvent( QKeyEvent *_ke )
1635 if (!m_part->xmlDocImpl())
1636 return false;
1637 // Pressing and releasing a key should generate keydown, keypress and keyup events
1638 // Holding it down should generated keydown, keypress (repeatedly) and keyup events
1639 // The problem here is that Qt generates two autorepeat events (keyrelease+keypress)
1640 // for autorepeating, while DOM wants only one autorepeat event (keypress), so one
1641 // of the Qt events shouldn't be passed to DOM, but it should be still filtered
1642 // out if DOM would filter the autorepeat event. Additional problem is that Qt keyrelease
1643 // events don't have text() set (Qt bug?), so DOM often would ignore the keypress event
1644 // if it was created using Qt keyrelease, but Qt autorepeat keyrelease comes
1645 // before Qt autorepeat keypress (i.e. problem whether to filter it out or not).
1646 // The solution is to filter out and postpone the Qt autorepeat keyrelease until
1647 // the following Qt keypress event comes. If DOM accepts the DOM keypress event,
1648 // the postponed event will be simply discarded. If not, it will be passed to keyPressEvent()
1649 // again, and here it will be ignored.
1651 // Qt: Press | Release(autorepeat) Press(autorepeat) etc. | Release
1652 // DOM: Down + Press | (nothing) Press | Up
1654 // It's also possible to get only Releases. E.g. the release of alt-tab,
1655 // or when the keypresses get captured by an accel.
1657 if( _ke == d->postponed_autorepeat ) // replayed event
1659 return false;
1662 if( _ke->type() == QEvent::KeyPress )
1664 if( !_ke->isAutoRepeat())
1666 bool ret = dispatchKeyEventHelper( _ke, false ); // keydown
1667 // don't send keypress even if keydown was blocked, like IE (and unlike Mozilla)
1668 if( !ret && dispatchKeyEventHelper( _ke, true )) // keypress
1669 ret = true;
1670 return ret;
1672 else // autorepeat
1674 bool ret = dispatchKeyEventHelper( _ke, true ); // keypress
1675 if( !ret && d->postponed_autorepeat )
1676 keyPressEvent( d->postponed_autorepeat );
1677 delete d->postponed_autorepeat;
1678 d->postponed_autorepeat = NULL;
1679 return ret;
1682 else // QEvent::KeyRelease
1684 // Discard postponed "autorepeat key-release" events that didn't see
1685 // a keypress after them (e.g. due to QAccel)
1686 if ( d->postponed_autorepeat ) {
1687 delete d->postponed_autorepeat;
1688 d->postponed_autorepeat = 0;
1691 if( !_ke->isAutoRepeat()) {
1692 return dispatchKeyEventHelper( _ke, false ); // keyup
1694 else
1696 d->postponed_autorepeat = new QKeyEvent( _ke->type(), _ke->key(), _ke->modifiers(),
1697 _ke->text(), _ke->isAutoRepeat(), _ke->count());
1698 if( _ke->isAccepted())
1699 d->postponed_autorepeat->accept();
1700 else
1701 d->postponed_autorepeat->ignore();
1702 return true;
1707 // returns true if event should be swallowed
1708 bool KHTMLView::dispatchKeyEventHelper( QKeyEvent *_ke, bool keypress )
1710 DOM::NodeImpl* keyNode = m_part->xmlDocImpl()->focusNode();
1711 if (keyNode) {
1712 return keyNode->dispatchKeyEvent(_ke, keypress);
1713 } else { // no focused node, send to document
1714 return m_part->xmlDocImpl()->dispatchKeyEvent(_ke, keypress);
1718 void KHTMLView::keyPressEvent( QKeyEvent *_ke )
1720 #ifndef KHTML_NO_TYPE_AHEAD_FIND
1721 if(d->typeAheadActivated)
1723 // type-ahead find aka find-as-you-type
1724 if(_ke->key() == Qt::Key_Backspace)
1726 d->findString = d->findString.left(d->findString.length() - 1);
1728 if(!d->findString.isEmpty())
1730 findAhead(false);
1732 else
1734 findTimeout();
1737 d->timer.setSingleShot(true);
1738 d->timer.start(3000);
1739 _ke->accept();
1740 return;
1742 else if(_ke->key() == Qt::Key_Escape)
1744 findTimeout();
1746 _ke->accept();
1747 return;
1749 else if(_ke->key() == Qt::Key_Space || !_ke->text().trimmed().isEmpty())
1751 d->findString += _ke->text();
1753 findAhead(true);
1755 d->timer.setSingleShot(true);
1756 d->timer.start(3000);
1757 _ke->accept();
1758 return;
1761 #endif // KHTML_NO_TYPE_AHEAD_FIND
1763 // If CTRL was hit, be prepared for access keys
1764 if (d->accessKeysEnabled && _ke->key() == Qt::Key_Control && !(_ke->modifiers() & ~Qt::ControlModifier) && !d->accessKeysActivated)
1766 d->accessKeysPreActivate=true;
1767 _ke->accept();
1768 return;
1771 if (_ke->key() == Qt::Key_Shift && !(_ke->modifiers() & ~Qt::ShiftModifier))
1772 d->scrollSuspendPreActivate=true;
1774 // accesskey handling needs to be done before dispatching, otherwise e.g. lineedits
1775 // may eat the event
1777 if (d->accessKeysEnabled && d->accessKeysActivated)
1779 int state = ( _ke->modifiers() & ( Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier ));
1780 if ( state==0 || state==Qt::ShiftModifier) {
1781 if (_ke->key() != Qt::Key_Shift) accessKeysTimeout();
1782 handleAccessKey( _ke );
1783 _ke->accept();
1784 return;
1786 accessKeysTimeout();
1789 if ( dispatchKeyEvent( _ke )) {
1790 // If either keydown or keypress was accepted by a widget, or canceled by JS, stop here.
1791 _ke->accept();
1792 return;
1795 int offs = (viewport()->height() < 30) ? viewport()->height() : 30; // ### ??
1796 if (_ke->modifiers() & Qt::ShiftModifier)
1797 switch(_ke->key())
1799 case Qt::Key_Space:
1800 verticalScrollBar()->setValue( verticalScrollBar()->value() -viewport()->height() + offs );
1801 if(d->scrollSuspended)
1802 d->newScrollTimer(this, 0);
1803 break;
1805 case Qt::Key_Down:
1806 case Qt::Key_J:
1807 d->adjustScroller(this, KHTMLViewPrivate::ScrollDown, KHTMLViewPrivate::ScrollUp);
1808 break;
1810 case Qt::Key_Up:
1811 case Qt::Key_K:
1812 d->adjustScroller(this, KHTMLViewPrivate::ScrollUp, KHTMLViewPrivate::ScrollDown);
1813 break;
1815 case Qt::Key_Left:
1816 case Qt::Key_H:
1817 d->adjustScroller(this, KHTMLViewPrivate::ScrollLeft, KHTMLViewPrivate::ScrollRight);
1818 break;
1820 case Qt::Key_Right:
1821 case Qt::Key_L:
1822 d->adjustScroller(this, KHTMLViewPrivate::ScrollRight, KHTMLViewPrivate::ScrollLeft);
1823 break;
1825 else
1826 switch ( _ke->key() )
1828 case Qt::Key_Down:
1829 case Qt::Key_J:
1830 if (!d->scrollTimerId || d->scrollSuspended)
1831 verticalScrollBar()->setValue( verticalScrollBar()->value()+10 );
1832 if (d->scrollTimerId)
1833 d->newScrollTimer(this, 0);
1834 break;
1836 case Qt::Key_Space:
1837 case Qt::Key_PageDown:
1838 d->shouldSmoothScroll = true;
1839 verticalScrollBar()->setValue( verticalScrollBar()->value() +viewport()->height() - offs );
1840 if(d->scrollSuspended)
1841 d->newScrollTimer(this, 0);
1842 break;
1844 case Qt::Key_Up:
1845 case Qt::Key_K:
1846 if (!d->scrollTimerId || d->scrollSuspended)
1847 verticalScrollBar()->setValue( verticalScrollBar()->value()-10 );
1848 if (d->scrollTimerId)
1849 d->newScrollTimer(this, 0);
1850 break;
1852 case Qt::Key_PageUp:
1853 d->shouldSmoothScroll = true;
1854 verticalScrollBar()->setValue( verticalScrollBar()->value() -viewport()->height() + offs );
1855 if(d->scrollSuspended)
1856 d->newScrollTimer(this, 0);
1857 break;
1858 case Qt::Key_Right:
1859 case Qt::Key_L:
1860 if (!d->scrollTimerId || d->scrollSuspended)
1861 horizontalScrollBar()->setValue( horizontalScrollBar()->value()+10 );
1862 if (d->scrollTimerId)
1863 d->newScrollTimer(this, 0);
1864 break;
1866 case Qt::Key_Left:
1867 case Qt::Key_H:
1868 if (!d->scrollTimerId || d->scrollSuspended)
1869 horizontalScrollBar()->setValue( horizontalScrollBar()->value()-10 );
1870 if (d->scrollTimerId)
1871 d->newScrollTimer(this, 0);
1872 break;
1873 case Qt::Key_Enter:
1874 case Qt::Key_Return:
1875 // ### FIXME:
1876 // or even better to HTMLAnchorElementImpl::event()
1877 if (m_part->xmlDocImpl()) {
1878 NodeImpl *n = m_part->xmlDocImpl()->focusNode();
1879 if (n)
1880 n->setActive();
1882 break;
1883 case Qt::Key_Home:
1884 verticalScrollBar()->setValue( 0 );
1885 horizontalScrollBar()->setValue( 0 );
1886 if(d->scrollSuspended)
1887 d->newScrollTimer(this, 0);
1888 break;
1889 case Qt::Key_End:
1890 verticalScrollBar()->setValue( contentsHeight() - visibleHeight() );
1891 if(d->scrollSuspended)
1892 d->newScrollTimer(this, 0);
1893 break;
1894 case Qt::Key_Shift:
1895 // what are you doing here?
1896 _ke->ignore();
1897 return;
1898 default:
1899 if (d->scrollTimerId)
1900 d->newScrollTimer(this, 0);
1901 _ke->ignore();
1902 return;
1905 _ke->accept();
1908 void KHTMLView::findTimeout()
1910 #ifndef KHTML_NO_TYPE_AHEAD_FIND
1911 d->typeAheadActivated = false;
1912 d->findString = "";
1913 m_part->setStatusBarText(i18n("Find stopped."), KHTMLPart::BarDefaultText);
1914 m_part->enableFindAheadActions( true );
1915 #endif // KHTML_NO_TYPE_AHEAD_FIND
1918 #ifndef KHTML_NO_TYPE_AHEAD_FIND
1919 void KHTMLView::startFindAhead( bool linksOnly )
1921 if( linksOnly )
1923 d->findLinksOnly = true;
1924 m_part->setStatusBarText(i18n("Starting -- find links as you type"),
1925 KHTMLPart::BarDefaultText);
1927 else
1929 d->findLinksOnly = false;
1930 m_part->setStatusBarText(i18n("Starting -- find text as you type"),
1931 KHTMLPart::BarDefaultText);
1934 m_part->findTextBegin();
1935 d->typeAheadActivated = true;
1936 // disable, so that the shortcut ( / or ' by default ) doesn't interfere
1937 m_part->enableFindAheadActions( false );
1938 d->timer.setSingleShot(true);
1939 d->timer.start(3000);
1942 void KHTMLView::findAhead(bool increase)
1944 QString status;
1945 QString text = d->findString.toLower();
1947 if(d->findLinksOnly)
1949 m_part->findText(d->findString, KHTMLPart::FindNoPopups |
1950 KHTMLPart::FindLinksOnly, this);
1951 if(m_part->findTextNext())
1953 status = i18n("Link found: \"%1\".", Qt::escape(text));
1955 else
1957 if(increase) KNotification::beep();
1958 status = i18n("Link not found: \"%1\".", Qt::escape(text));
1961 else
1963 m_part->findText(d->findString, KHTMLPart::FindNoPopups, this);
1964 if(m_part->findTextNext())
1966 status = i18n("Text found: \"%1\".", Qt::escape(text));
1968 else
1970 if(increase) KNotification::beep();
1971 status = i18n("Text not found: \"%1\".", Qt::escape(text));
1975 // Note: we need to escape -twice-: the above just escape for i18n, now we need to do it for Qt, too.
1976 m_part->setStatusBarText(Qt::escape(status), KHTMLPart::BarDefaultText);
1979 void KHTMLView::updateFindAheadTimeout()
1981 if( d->typeAheadActivated ) {
1982 d->timer.setSingleShot( true );
1983 d->timer.start( 3000 );
1987 #endif // KHTML_NO_TYPE_AHEAD_FIND
1989 void KHTMLView::keyReleaseEvent(QKeyEvent *_ke)
1991 #ifndef KHTML_NO_TYPE_AHEAD_FIND
1992 if(d->typeAheadActivated) {
1993 _ke->accept();
1994 return;
1996 #endif
1998 if( d->scrollSuspendPreActivate && _ke->key() != Qt::Key_Shift )
1999 d->scrollSuspendPreActivate = false;
2000 if( _ke->key() == Qt::Key_Shift && d->scrollSuspendPreActivate && !(_ke->modifiers() & Qt::ShiftModifier))
2001 if (d->scrollTimerId) {
2002 d->scrollSuspended = !d->scrollSuspended;
2003 if (d->scrollSuspended)
2004 d->stopScrolling();
2007 if (d->accessKeysEnabled)
2009 if (d->accessKeysPreActivate && _ke->key() != Qt::Key_Control)
2010 d->accessKeysPreActivate=false;
2011 if (d->accessKeysPreActivate && !(_ke->modifiers() & Qt::ControlModifier))
2013 displayAccessKeys();
2014 m_part->setStatusBarText(i18n("Access Keys activated"),KHTMLPart::BarOverrideText);
2015 d->accessKeysActivated = true;
2016 d->accessKeysPreActivate = false;
2017 _ke->accept();
2018 return;
2020 else if (d->accessKeysActivated)
2022 accessKeysTimeout();
2023 _ke->accept();
2024 return;
2028 // Send keyup event
2029 if ( dispatchKeyEvent( _ke ) )
2031 _ke->accept();
2032 return;
2035 QScrollArea::keyReleaseEvent(_ke);
2038 bool KHTMLView::focusNextPrevChild( bool next )
2040 // Now try to find the next child
2041 if (m_part->xmlDocImpl() && focusNextPrevNode(next))
2043 if (m_part->xmlDocImpl()->focusNode())
2044 kDebug() << "focusNode.name: "
2045 << m_part->xmlDocImpl()->focusNode()->nodeName().string() << endl;
2046 return true; // focus node found
2049 // If we get here, pass tabbing control up to the next/previous child in our parent
2050 d->pseudoFocusNode = KHTMLViewPrivate::PFNone;
2051 if (m_part->parentPart() && m_part->parentPart()->view())
2052 return m_part->parentPart()->view()->focusNextPrevChild(next);
2054 return QWidget::focusNextPrevChild(next);
2057 void KHTMLView::doAutoScroll()
2059 QPoint pos = QCursor::pos();
2060 QPoint off;
2061 KHTMLView* v = m_kwp->isRedirected() ? m_kwp->rootViewPos(off) : this;
2062 pos = v->viewport()->mapFromGlobal( pos );
2063 pos -= off;
2064 int xm, ym;
2065 viewportToContents(pos.x(), pos.y(), xm, ym); // ###
2067 pos = QPoint(pos.x() - viewport()->x(), pos.y() - viewport()->y());
2068 if ( (pos.y() < 0) || (pos.y() > visibleHeight()) ||
2069 (pos.x() < 0) || (pos.x() > visibleWidth()) )
2071 ensureVisible( xm, ym, 0, 5 );
2073 #ifndef KHTML_NO_SELECTION
2074 // extend the selection while scrolling
2075 DOM::Node innerNode;
2076 if (m_part->isExtendingSelection()) {
2077 RenderObject::NodeInfo renderInfo(true/*readonly*/, false/*active*/);
2078 m_part->xmlDocImpl()->renderer()->layer()
2079 ->nodeAtPoint(renderInfo, xm, ym);
2080 innerNode = renderInfo.innerNode();
2081 }/*end if*/
2083 if (innerNode.handle() && innerNode.handle()->renderer()
2084 && innerNode.handle()->renderer()->shouldSelect()) {
2085 m_part->extendSelectionTo(xm, ym, innerNode);
2086 }/*end if*/
2087 #endif // KHTML_NO_SELECTION
2091 // KHTML defines its own stacking order for any object and thus takes
2092 // control of widget painting whenever it can. This is called "redirection".
2094 // Redirected widgets are placed off screen. When they are declared as a child of our view (ChildPolished event),
2095 // an event filter is installed, so as to catch any paint event and translate them as update() of the view's main widget.
2097 // Painting also happens spontaneously within widgets. In this case, the widget would update() parts of itself.
2098 // While this ordinarily results in a paintEvent being schedduled, it is not the case with off screen widgets.
2099 // Thus update() is monitored by using the mechanism that deffers any update call happening during a paint event,
2100 // transforming it into a posted UpdateLater event. Hence the need to set Qt::WA_WState_InPaintEvent on redirected widgets.
2102 // Once the UpdateLater event has been received, Qt::WA_WState_InPaintEvent is removed and the process continues
2103 // with the update of the corresponding rect on the view. That in turn will make our painting subsystem render()
2104 // the widget at the correct stacking position.
2106 // For non-redirected (e.g. external) widgets, z-order is honoured through masking. cf.RenderLayer::updateWidgetMasks
2108 static void handleWidget(QWidget* w, KHTMLView* view, bool recurse=true)
2110 if (w->isWindow())
2111 return;
2113 if (!qobject_cast<QFrame*>(w))
2114 w->setAttribute( Qt::WA_NoSystemBackground );
2116 w->setAttribute(Qt::WA_WState_InPaintEvent);
2117 w->setAttribute(Qt::WA_OpaquePaintEvent);
2118 w->installEventFilter(view);
2120 if (!recurse)
2121 return;
2122 if (qobject_cast<KHTMLView*>(w)) {
2123 handleWidget(static_cast<KHTMLView*>(w)->widget(), view, false);
2124 handleWidget(static_cast<KHTMLView*>(w)->horizontalScrollBar(), view, false);
2125 handleWidget(static_cast<KHTMLView*>(w)->verticalScrollBar(), view, false);
2126 return;
2129 QObjectList children = w->children();
2130 foreach (QObject* object, children) {
2131 QWidget *widget = qobject_cast<QWidget*>(object);
2132 if (widget)
2133 handleWidget(widget, view);
2137 class KHTMLBackingStoreHackWidget : public QWidget
2139 public:
2140 void publicEvent(QEvent *e)
2142 QWidget::event(e);
2146 bool KHTMLView::viewportEvent ( QEvent * e )
2148 switch (e->type()) {
2149 // those must not be dispatched to the specialized handlers
2150 // as widgetEvent() already took care of that
2151 case QEvent::MouseButtonPress:
2152 case QEvent::MouseButtonRelease:
2153 case QEvent::MouseButtonDblClick:
2154 case QEvent::MouseMove:
2155 #ifndef QT_NO_WHEELEVENT
2156 case QEvent::Wheel:
2157 #endif
2158 case QEvent::ContextMenu:
2159 case QEvent::DragEnter:
2160 case QEvent::DragMove:
2161 case QEvent::DragLeave:
2162 case QEvent::Drop:
2163 return false;
2164 case QEvent::Paint: {
2165 QRect r = static_cast<QPaintEvent*>(e)->rect();
2166 r.setX(r.x() +contentsX());
2167 r.setY(r.y() +contentsY());
2168 QPaintEvent pe(r);
2169 paintEvent(&pe);
2170 return true;
2172 default:
2173 break;
2175 return QScrollArea::viewportEvent(e);
2178 static void setInPaintEventFlag(QWidget* w, bool b = true, bool recurse=true)
2180 w->setAttribute(Qt::WA_WState_InPaintEvent, b);
2182 if (!recurse)
2183 return;
2184 if (qobject_cast<KHTMLView*>(w)) {
2185 setInPaintEventFlag(static_cast<KHTMLView*>(w)->widget(), b, false);
2186 setInPaintEventFlag(static_cast<KHTMLView*>(w)->horizontalScrollBar(), b, false);
2187 setInPaintEventFlag(static_cast<KHTMLView*>(w)->verticalScrollBar(), b, false);
2188 return;
2191 foreach(QObject* cw, w->children()) {
2192 if (cw->isWidgetType() && ! static_cast<QWidget*>(cw)->isWindow()
2193 && !(static_cast<QWidget*>(cw)->windowModality() & Qt::ApplicationModal)) {
2194 setInPaintEventFlag(static_cast<QWidget*>(cw), b);
2199 bool KHTMLView::eventFilter(QObject *o, QEvent *e)
2201 if ( e->type() == QEvent::ShortcutOverride ) {
2202 QKeyEvent* ke = (QKeyEvent*) e;
2203 if (m_part->isEditable() || m_part->isCaretMode()
2204 || (m_part->xmlDocImpl() && m_part->xmlDocImpl()->focusNode()
2205 && m_part->xmlDocImpl()->focusNode()->isContentEditable())) {
2206 if ( (ke->modifiers() & Qt::ControlModifier) || (ke->modifiers() & Qt::ShiftModifier) ) {
2207 switch ( ke->key() ) {
2208 case Qt::Key_Left:
2209 case Qt::Key_Right:
2210 case Qt::Key_Up:
2211 case Qt::Key_Down:
2212 case Qt::Key_Home:
2213 case Qt::Key_End:
2214 ke->accept();
2215 return true;
2216 default:
2217 break;
2223 if ( e->type() == QEvent::Leave ) {
2224 if ( d->cursorIconWidget )
2225 d->cursorIconWidget->hide();
2226 m_part->resetHoverText();
2229 QWidget *view = widget();
2230 if (o == view) {
2231 if (widgetEvent(e))
2232 return true;
2233 else if (e->type() == QEvent::Resize) {
2234 updateScrollBars();
2235 return false;
2237 } else if (o->isWidgetType()) {
2238 QWidget *v = static_cast<QWidget *>(o);
2239 QWidget *c = v;
2240 while (v && v != view) {
2241 c = v;
2242 v = v->parentWidget();
2244 KHTMLWidget* k = dynamic_cast<KHTMLWidget*>(c);
2245 if (v && k && k->m_kwp->isRedirected()) {
2246 bool block = false;
2247 bool isUpdate = false;
2248 QWidget *w = static_cast<QWidget *>(o);
2249 switch(e->type()) {
2250 case QEvent::UpdateRequest: {
2251 // implicitly call qt_syncBackingStore(w)
2252 static_cast<KHTMLBackingStoreHackWidget *>(w)->publicEvent(e);
2253 block = true;
2254 break;
2256 case QEvent::UpdateLater:
2257 isUpdate = true;
2258 // no break;
2259 case QEvent::Paint:
2260 if (!allowWidgetPaintEvents) {
2261 // eat the event. Like this we can control exactly when the widget
2262 // gets repainted.
2263 block = true;
2264 int x = 0, y = 0;
2265 QWidget *v = w;
2266 while (v && v->parentWidget() != view) {
2267 x += v->x();
2268 y += v->y();
2269 v = v->parentWidget();
2272 QPoint ap = k->m_kwp->absolutePos();
2273 x += ap.x();
2274 y += ap.y();
2276 QRect pr = isUpdate ? static_cast<QUpdateLaterEvent*>(e)->region().boundingRect() : static_cast<QPaintEvent*>(e)->rect();
2277 bool asap = !d->contentsMoving && qobject_cast<QAbstractScrollArea*>(c);
2279 if (isUpdate) {
2280 setInPaintEventFlag(w, false);
2281 if (asap)
2282 w->repaint(static_cast<QUpdateLaterEvent*>(e)->region());
2283 else
2284 w->update(static_cast<QUpdateLaterEvent*>(e)->region());
2285 setInPaintEventFlag(w);
2288 // QScrollView needs fast repaints
2289 if ( asap && !isUpdate && !d->painting && m_part->xmlDocImpl() && m_part->xmlDocImpl()->renderer() &&
2290 !static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer())->needsLayout() ) {
2291 repaintContents(x + pr.x(), y + pr.y(),
2292 pr.width(), pr.height()+1); // ### investigate that +1 (shows up when
2293 // updating e.g a textarea's blinking cursor)
2294 } else if (!d->painting) {
2295 scheduleRepaint(x + pr.x(), y + pr.y(),
2296 pr.width(), pr.height()+1, asap);
2299 break;
2300 case QEvent::MouseMove:
2301 case QEvent::MouseButtonPress:
2302 case QEvent::MouseButtonRelease:
2303 case QEvent::MouseButtonDblClick: {
2305 if (0 && w->parentWidget() == view && !qobject_cast<QScrollBar*>(w) && !::qobject_cast<QScrollBar *>(w)) {
2306 QMouseEvent *me = static_cast<QMouseEvent *>(e);
2307 QPoint pt = w->mapTo( view, me->pos());
2308 QMouseEvent me2(me->type(), pt, me->button(), me->buttons(), me->modifiers());
2310 if (e->type() == QEvent::MouseMove)
2311 mouseMoveEvent(&me2);
2312 else if(e->type() == QEvent::MouseButtonPress)
2313 mousePressEvent(&me2);
2314 else if(e->type() == QEvent::MouseButtonRelease)
2315 mouseReleaseEvent(&me2);
2316 else
2317 mouseDoubleClickEvent(&me2);
2318 block = true;
2320 break;
2322 case QEvent::KeyPress:
2323 case QEvent::KeyRelease:
2324 if (w->parentWidget() == view && !qobject_cast<QScrollBar*>(w)) {
2325 QKeyEvent *ke = static_cast<QKeyEvent *>(e);
2326 if (e->type() == QEvent::KeyPress)
2327 keyPressEvent(ke);
2328 else
2329 keyReleaseEvent(ke);
2330 block = true;
2332 break;
2333 case QEvent::FocusIn:
2334 case QEvent::FocusOut:
2335 block = true;
2336 break;
2337 default:
2338 break;
2340 if (block) {
2341 //qDebug("eating event");
2342 return true;
2347 // kDebug(6000) <<"passing event on to sv event filter object=" << o->className() << " event=" << e->type();
2348 return QScrollArea::eventFilter(o, e);
2351 bool KHTMLView::widgetEvent(QEvent* e)
2353 switch (e->type()) {
2354 case QEvent::MouseButtonPress:
2355 case QEvent::MouseButtonRelease:
2356 case QEvent::MouseButtonDblClick:
2357 case QEvent::MouseMove:
2358 case QEvent::Paint:
2359 #ifndef QT_NO_WHEELEVENT
2360 case QEvent::Wheel:
2361 #endif
2362 case QEvent::ContextMenu:
2363 case QEvent::DragEnter:
2364 case QEvent::DragMove:
2365 case QEvent::DragLeave:
2366 case QEvent::Drop:
2367 return QFrame::event(e);
2368 case QEvent::ChildPolished: {
2369 // we need to install an event filter on all children of the widget() to
2370 // be able to get correct stacking of children within the document.
2371 QObject *c = static_cast<QChildEvent *>(e)->child();
2372 if (c->isWidgetType()) {
2373 QWidget *w = static_cast<QWidget *>(c);
2374 // don't install the event filter on toplevels
2375 if (!(w->windowFlags() & Qt::Window) && !(w->windowModality() & Qt::ApplicationModal)) {
2376 KHTMLWidget* k = dynamic_cast<KHTMLWidget*>(w);
2377 if (k && k->m_kwp->isRedirected()) {
2378 w->unsetCursor();
2379 handleWidget(w, this);
2384 case QEvent::Move: {
2385 if (static_cast<QMoveEvent*>(e)->pos() != QPoint(0,0)) {
2386 widget()->move(0,0);
2387 updateScrollBars();
2388 return true;
2391 default:
2392 break;
2394 return false;
2397 DOM::NodeImpl *KHTMLView::nodeUnderMouse() const
2399 return d->underMouse;
2402 DOM::NodeImpl *KHTMLView::nonSharedNodeUnderMouse() const
2404 return d->underMouseNonShared;
2407 bool KHTMLView::scrollTo(const QRect &bounds)
2409 d->scrollingSelf = true; // so scroll events get ignored
2411 int x, y, xe, ye;
2412 x = bounds.left();
2413 y = bounds.top();
2414 xe = bounds.right();
2415 ye = bounds.bottom();
2417 //kDebug(6000)<<"scrolling coords: x="<<x<<" y="<<y<<" width="<<xe-x<<" height="<<ye-y;
2419 int deltax;
2420 int deltay;
2422 int curHeight = visibleHeight();
2423 int curWidth = visibleWidth();
2425 if (ye-y>curHeight-d->borderY)
2426 ye = y + curHeight - d->borderY;
2428 if (xe-x>curWidth-d->borderX)
2429 xe = x + curWidth - d->borderX;
2431 // is xpos of target left of the view's border?
2432 if (x < contentsX() + d->borderX )
2433 deltax = x - contentsX() - d->borderX;
2434 // is xpos of target right of the view's right border?
2435 else if (xe + d->borderX > contentsX() + curWidth)
2436 deltax = xe + d->borderX - ( contentsX() + curWidth );
2437 else
2438 deltax = 0;
2440 // is ypos of target above upper border?
2441 if (y < contentsY() + d->borderY)
2442 deltay = y - contentsY() - d->borderY;
2443 // is ypos of target below lower border?
2444 else if (ye + d->borderY > contentsY() + curHeight)
2445 deltay = ye + d->borderY - ( contentsY() + curHeight );
2446 else
2447 deltay = 0;
2449 int maxx = curWidth-d->borderX;
2450 int maxy = curHeight-d->borderY;
2452 int scrollX, scrollY;
2454 scrollX = deltax > 0 ? (deltax > maxx ? maxx : deltax) : deltax == 0 ? 0 : (deltax>-maxx ? deltax : -maxx);
2455 scrollY = deltay > 0 ? (deltay > maxy ? maxy : deltay) : deltay == 0 ? 0 : (deltay>-maxy ? deltay : -maxy);
2457 if (contentsX() + scrollX < 0)
2458 scrollX = -contentsX();
2459 else if (contentsWidth() - visibleWidth() - contentsX() < scrollX)
2460 scrollX = contentsWidth() - visibleWidth() - contentsX();
2462 if (contentsY() + scrollY < 0)
2463 scrollY = -contentsY();
2464 else if (contentsHeight() - visibleHeight() - contentsY() < scrollY)
2465 scrollY = contentsHeight() - visibleHeight() - contentsY();
2467 horizontalScrollBar()->setValue( horizontalScrollBar()->value()+scrollX );
2468 verticalScrollBar()->setValue( verticalScrollBar()->value()+scrollY );
2470 d->scrollingSelf = false;
2472 if ( (abs(deltax)<=maxx) && (abs(deltay)<=maxy) )
2473 return true;
2474 else return false;
2478 bool KHTMLView::focusNextPrevNode(bool next)
2480 // Sets the focus node of the document to be the node after (or if
2481 // next is false, before) the current focus node. Only nodes that
2482 // are selectable (i.e. for which isFocusable() returns true) are
2483 // taken into account, and the order used is that specified in the
2484 // HTML spec (see DocumentImpl::nextFocusNode() and
2485 // DocumentImpl::previousFocusNode() for details).
2487 DocumentImpl *doc = m_part->xmlDocImpl();
2488 NodeImpl *oldFocusNode = doc->focusNode();
2490 // See whether we're in the middle of a detach, or hiding of the
2491 // widget. In this case, we will just clear focus, being careful not to emit events
2492 // or update rendering. Doing this also prevents the code below from going bonkers with
2493 // oldFocusNode not actually being focusable, etc.
2494 if (oldFocusNode) {
2495 if (oldFocusNode->renderer() && !oldFocusNode->renderer()->parent()
2496 || !oldFocusNode->isTabFocusable()) {
2497 doc->quietResetFocus();
2498 return true;
2502 #if 1
2503 // If the user has scrolled the document, then instead of picking
2504 // the next focusable node in the document, use the first one that
2505 // is within the visible area (if possible).
2506 if (d->scrollBarMoved)
2508 NodeImpl *toFocus;
2509 if (next)
2510 toFocus = doc->nextFocusNode(oldFocusNode);
2511 else
2512 toFocus = doc->previousFocusNode(oldFocusNode);
2514 if (!toFocus && oldFocusNode) {
2515 if (next)
2516 toFocus = doc->nextFocusNode(NULL);
2517 else
2518 toFocus = doc->previousFocusNode(NULL);
2521 while (toFocus && toFocus != oldFocusNode)
2524 QRect focusNodeRect = toFocus->getRect();
2525 if ((focusNodeRect.left() > contentsX()) && (focusNodeRect.right() < contentsX() + visibleWidth()) &&
2526 (focusNodeRect.top() > contentsY()) && (focusNodeRect.bottom() < contentsY() + visibleHeight())) {
2528 QRect r = toFocus->getRect();
2529 ensureVisible( r.right(), r.bottom());
2530 ensureVisible( r.left(), r.top());
2531 d->scrollBarMoved = false;
2532 d->tabMovePending = false;
2533 d->lastTabbingDirection = next;
2534 d->pseudoFocusNode = KHTMLViewPrivate::PFNone;
2535 m_part->xmlDocImpl()->setFocusNode(toFocus);
2536 Node guard(toFocus);
2537 if (!toFocus->hasOneRef() )
2539 emit m_part->nodeActivated(Node(toFocus));
2541 return true;
2544 if (next)
2545 toFocus = doc->nextFocusNode(toFocus);
2546 else
2547 toFocus = doc->previousFocusNode(toFocus);
2549 if (!toFocus && oldFocusNode)
2550 if (next)
2551 toFocus = doc->nextFocusNode(NULL);
2552 else
2553 toFocus = doc->previousFocusNode(NULL);
2556 d->scrollBarMoved = false;
2558 #endif
2560 if (!oldFocusNode && d->pseudoFocusNode == KHTMLViewPrivate::PFNone)
2562 ensureVisible(contentsX(), next?0:contentsHeight());
2563 d->scrollBarMoved = false;
2564 d->pseudoFocusNode = next?KHTMLViewPrivate::PFTop:KHTMLViewPrivate::PFBottom;
2565 return true;
2568 NodeImpl *newFocusNode = NULL;
2570 if (d->tabMovePending && next != d->lastTabbingDirection)
2572 //kDebug ( 6000 ) << " tab move pending and tabbing direction changed!\n";
2573 newFocusNode = oldFocusNode;
2575 else if (next)
2577 if (oldFocusNode || d->pseudoFocusNode == KHTMLViewPrivate::PFTop )
2578 newFocusNode = doc->nextFocusNode(oldFocusNode);
2580 else
2582 if (oldFocusNode || d->pseudoFocusNode == KHTMLViewPrivate::PFBottom )
2583 newFocusNode = doc->previousFocusNode(oldFocusNode);
2586 bool targetVisible = false;
2587 if (!newFocusNode)
2589 if ( next )
2591 targetVisible = scrollTo(QRect(contentsX()+visibleWidth()/2,contentsHeight()-d->borderY,0,0));
2593 else
2595 targetVisible = scrollTo(QRect(contentsX()+visibleWidth()/2,d->borderY,0,0));
2598 else
2600 // if it's an editable element, activate the caret
2601 if (!m_part->isCaretMode() && newFocusNode->isContentEditable()) {
2602 kDebug(6200) << "show caret! fn: " << newFocusNode->nodeName().string() << endl;
2603 m_part->clearCaretRectIfNeeded();
2604 m_part->d->editor_context.m_selection.moveTo(Position(newFocusNode, 0L));
2605 m_part->setCaretVisible(true);
2606 } else {
2607 m_part->setCaretVisible(false);
2608 kDebug(6200) << "hide caret! fn: " << newFocusNode->nodeName().string() << endl;
2610 m_part->notifySelectionChanged();
2612 targetVisible = scrollTo(newFocusNode->getRect());
2615 if (targetVisible)
2617 //kDebug ( 6000 ) << " target reached.\n";
2618 d->tabMovePending = false;
2620 m_part->xmlDocImpl()->setFocusNode(newFocusNode);
2621 if (newFocusNode)
2623 Node guard(newFocusNode);
2624 if (!newFocusNode->hasOneRef() )
2626 emit m_part->nodeActivated(Node(newFocusNode));
2628 return true;
2630 else
2632 d->pseudoFocusNode = next?KHTMLViewPrivate::PFBottom:KHTMLViewPrivate::PFTop;
2633 return false;
2636 else
2638 if (!d->tabMovePending)
2639 d->lastTabbingDirection = next;
2640 d->tabMovePending = true;
2641 return true;
2645 void KHTMLView::displayAccessKeys()
2647 QVector< QChar > taken;
2648 displayAccessKeys( NULL, this, taken, false );
2649 displayAccessKeys( NULL, this, taken, true );
2652 void KHTMLView::displayAccessKeys( KHTMLView* caller, KHTMLView* origview, QVector< QChar >& taken, bool use_fallbacks )
2654 QMap< ElementImpl*, QChar > fallbacks;
2655 if( use_fallbacks )
2656 fallbacks = buildFallbackAccessKeys();
2657 for( NodeImpl* n = m_part->xmlDocImpl(); n != NULL; n = n->traverseNextNode()) {
2658 if( n->isElementNode()) {
2659 ElementImpl* en = static_cast< ElementImpl* >( n );
2660 DOMString s = en->getAttribute( ATTR_ACCESSKEY );
2661 QString accesskey;
2662 if( s.length() == 1 ) {
2663 QChar a = s.string()[ 0 ].toUpper();
2664 if( qFind( taken.begin(), taken.end(), a ) == taken.end()) // !contains
2665 accesskey = a;
2667 if( accesskey.isNull() && fallbacks.contains( en )) {
2668 QChar a = fallbacks[ en ].toUpper();
2669 if( qFind( taken.begin(), taken.end(), a ) == taken.end()) // !contains
2670 accesskey = QString( "<qt><i>" ) + a + "</i></qt>";
2672 if( !accesskey.isNull()) {
2673 QRect rec=en->getRect();
2674 QLabel *lab=new QLabel(accesskey,viewport());
2675 lab->setAttribute(Qt::WA_DeleteOnClose);
2676 connect( origview, SIGNAL(hideAccessKeys()), lab, SLOT(close()) );
2677 connect( this, SIGNAL(repaintAccessKeys()), lab, SLOT(repaint()));
2678 lab->setPalette(QToolTip::palette());
2679 lab->setLineWidth(2);
2680 lab->setFrameStyle(QFrame::Box | QFrame::Plain);
2681 lab->setMargin(3);
2682 lab->adjustSize();
2683 lab->setParent( widget() );
2684 lab->setAutoFillBackground(true);
2685 lab->move(
2686 qMin(rec.left()+rec.width()/2 - contentsX(), contentsWidth() - lab->width()),
2687 qMin(rec.top()+rec.height()/2 - contentsY(), contentsHeight() - lab->height()));
2688 lab->show();
2689 taken.append( accesskey[ 0 ] );
2693 if( use_fallbacks )
2694 return;
2696 QList<KParts::ReadOnlyPart*> frames = m_part->frames();
2697 foreach( KParts::ReadOnlyPart* cur, frames ) {
2698 if( !qobject_cast<KHTMLPart*>(cur) )
2699 continue;
2700 KHTMLPart* part = static_cast< KHTMLPart* >( cur );
2701 if( part->view() && part->view() != caller )
2702 part->view()->displayAccessKeys( this, origview, taken, use_fallbacks );
2705 // pass up to the parent
2706 if (m_part->parentPart() && m_part->parentPart()->view()
2707 && m_part->parentPart()->view() != caller)
2708 m_part->parentPart()->view()->displayAccessKeys( this, origview, taken, use_fallbacks );
2711 bool KHTMLView::isScrollingFromMouseWheel() const
2713 return d->scrollingFromWheel != QPoint(-1,-1);
2716 void KHTMLView::accessKeysTimeout()
2718 d->accessKeysActivated=false;
2719 d->accessKeysPreActivate = false;
2720 m_part->setStatusBarText(QString(), KHTMLPart::BarOverrideText);
2721 emit hideAccessKeys();
2724 // Handling of the HTML accesskey attribute.
2725 bool KHTMLView::handleAccessKey( const QKeyEvent* ev )
2727 // Qt interprets the keyevent also with the modifiers, and ev->text() matches that,
2728 // but this code must act as if the modifiers weren't pressed
2729 QChar c;
2730 if( ev->key() >= Qt::Key_A && ev->key() <= Qt::Key_Z )
2731 c = 'A' + ev->key() - Qt::Key_A;
2732 else if( ev->key() >= Qt::Key_0 && ev->key() <= Qt::Key_9 )
2733 c = '0' + ev->key() - Qt::Key_0;
2734 else {
2735 // TODO fake XKeyEvent and XLookupString ?
2736 // This below seems to work e.g. for eacute though.
2737 if( ev->text().length() == 1 )
2738 c = ev->text()[ 0 ];
2740 if( c.isNull())
2741 return false;
2742 return focusNodeWithAccessKey( c );
2745 bool KHTMLView::focusNodeWithAccessKey( QChar c, KHTMLView* caller )
2747 DocumentImpl *doc = m_part->xmlDocImpl();
2748 if( !doc )
2749 return false;
2750 ElementImpl* node = doc->findAccessKeyElement( c );
2751 if( !node ) {
2752 QList<KParts::ReadOnlyPart*> frames = m_part->frames();
2753 foreach( KParts::ReadOnlyPart* cur, frames ) {
2754 if( !qobject_cast<KHTMLPart*>(cur) )
2755 continue;
2756 KHTMLPart* part = static_cast< KHTMLPart* >( cur );
2757 if( part->view() && part->view() != caller
2758 && part->view()->focusNodeWithAccessKey( c, this ))
2759 return true;
2761 // pass up to the parent
2762 if (m_part->parentPart() && m_part->parentPart()->view()
2763 && m_part->parentPart()->view() != caller
2764 && m_part->parentPart()->view()->focusNodeWithAccessKey( c, this ))
2765 return true;
2766 if( caller == NULL ) { // the active frame (where the accesskey was pressed)
2767 const QMap< ElementImpl*, QChar > fallbacks = buildFallbackAccessKeys();
2768 for( QMap< ElementImpl*, QChar >::ConstIterator it = fallbacks.begin();
2769 it != fallbacks.end();
2770 ++it )
2771 if( *it == c ) {
2772 node = it.key();
2773 break;
2776 if( node == NULL )
2777 return false;
2780 // Scroll the view as necessary to ensure that the new focus node is visible
2782 QRect r = node->getRect();
2783 ensureVisible( r.right(), r.bottom());
2784 ensureVisible( r.left(), r.top());
2786 Node guard( node );
2787 if( node->isFocusable()) {
2788 if (node->id()==ID_LABEL) {
2789 // if Accesskey is a label, give focus to the label's referrer.
2790 node=static_cast<ElementImpl *>(static_cast< HTMLLabelElementImpl* >( node )->getFormElement());
2791 if (!node) return true;
2792 guard = node;
2794 // Set focus node on the document
2795 #ifdef __GNUC__
2796 #warning "port QFocusEvent::setReason( QFocusEvent::Shortcut ); to qt4"
2797 #endif
2798 //QFocusEvent::setReason( QFocusEvent::Shortcut );
2799 m_part->xmlDocImpl()->setFocusNode(node);
2800 #ifdef __GNUC__
2801 #warning "port QFocusEvent::resetReason(); to qt4"
2802 #endif
2803 //QFocusEvent::resetReason();
2804 if( node != NULL && node->hasOneRef()) // deleted, only held by guard
2805 return true;
2806 emit m_part->nodeActivated(Node(node));
2807 if( node != NULL && node->hasOneRef())
2808 return true;
2811 switch( node->id()) {
2812 case ID_A:
2813 static_cast< HTMLAnchorElementImpl* >( node )->click();
2814 break;
2815 case ID_INPUT:
2816 static_cast< HTMLInputElementImpl* >( node )->click();
2817 break;
2818 case ID_BUTTON:
2819 static_cast< HTMLButtonElementImpl* >( node )->click();
2820 break;
2821 case ID_AREA:
2822 static_cast< HTMLAreaElementImpl* >( node )->click();
2823 break;
2824 case ID_TEXTAREA:
2825 break; // just focusing it is enough
2826 case ID_LEGEND:
2827 // TODO
2828 break;
2830 return true;
2833 static QString getElementText( NodeImpl* start, bool after )
2835 QString ret; // nextSibling(), to go after e.g. </select>
2836 for( NodeImpl* n = after ? start->nextSibling() : start->traversePreviousNode();
2837 n != NULL;
2838 n = after ? n->traverseNextNode() : n->traversePreviousNode()) {
2839 if( n->isTextNode()) {
2840 if( after )
2841 ret += static_cast< TextImpl* >( n )->toString().string();
2842 else
2843 ret.prepend( static_cast< TextImpl* >( n )->toString().string());
2844 } else {
2845 switch( n->id()) {
2846 case ID_A:
2847 case ID_FONT:
2848 case ID_TT:
2849 case ID_U:
2850 case ID_B:
2851 case ID_I:
2852 case ID_S:
2853 case ID_STRIKE:
2854 case ID_BIG:
2855 case ID_SMALL:
2856 case ID_EM:
2857 case ID_STRONG:
2858 case ID_DFN:
2859 case ID_CODE:
2860 case ID_SAMP:
2861 case ID_KBD:
2862 case ID_VAR:
2863 case ID_CITE:
2864 case ID_ABBR:
2865 case ID_ACRONYM:
2866 case ID_SUB:
2867 case ID_SUP:
2868 case ID_SPAN:
2869 case ID_NOBR:
2870 case ID_WBR:
2871 break;
2872 case ID_TD:
2873 if( ret.trimmed().isEmpty())
2874 break;
2875 // fall through
2876 default:
2877 return ret.simplified();
2881 return ret.simplified();
2884 static QMap< NodeImpl*, QString > buildLabels( NodeImpl* start )
2886 QMap< NodeImpl*, QString > ret;
2887 for( NodeImpl* n = start;
2888 n != NULL;
2889 n = n->traverseNextNode()) {
2890 if( n->id() == ID_LABEL ) {
2891 HTMLLabelElementImpl* label = static_cast< HTMLLabelElementImpl* >( n );
2892 NodeImpl* labelfor = label->getFormElement();
2893 if( labelfor )
2894 ret[ labelfor ] = label->innerText().string().simplified();
2897 return ret;
2900 namespace khtml {
2901 struct AccessKeyData {
2902 ElementImpl* element;
2903 QString text;
2904 QString url;
2905 int priority; // 10(highest) - 0(lowest)
2909 QMap< ElementImpl*, QChar > KHTMLView::buildFallbackAccessKeys() const
2911 // build a list of all possible candidate elements that could use an accesskey
2912 QLinkedList< AccessKeyData > data; // Note: this has to be a list type that keep iterators valid
2913 // when other entries are removed
2914 QMap< NodeImpl*, QString > labels = buildLabels( m_part->xmlDocImpl());
2915 for( NodeImpl* n = m_part->xmlDocImpl();
2916 n != NULL;
2917 n = n->traverseNextNode()) {
2918 if( n->isElementNode()) {
2919 ElementImpl* element = static_cast< ElementImpl* >( n );
2920 if( element->getAttribute( ATTR_ACCESSKEY ).length() == 1 )
2921 continue; // has accesskey set, ignore
2922 if( element->renderer() == NULL )
2923 continue; // not visible
2924 QString text;
2925 QString url;
2926 int priority = 0;
2927 bool ignore = false;
2928 bool text_after = false;
2929 bool text_before = false;
2930 switch( element->id()) {
2931 case ID_A:
2932 url = khtml::parseURL(element->getAttribute(ATTR_HREF)).string();
2933 if( url.isEmpty()) // doesn't have href, it's only an anchor
2934 continue;
2935 text = static_cast< HTMLElementImpl* >( element )->innerText().string().simplified();
2936 priority = 2;
2937 break;
2938 case ID_INPUT: {
2939 HTMLInputElementImpl* in = static_cast< HTMLInputElementImpl* >( element );
2940 switch( in->inputType()) {
2941 case HTMLInputElementImpl::SUBMIT:
2942 text = in->value().string();
2943 if( text.isEmpty())
2944 text = i18n( "Submit" );
2945 priority = 7;
2946 break;
2947 case HTMLInputElementImpl::IMAGE:
2948 text = in->altText().string();
2949 priority = 7;
2950 break;
2951 case HTMLInputElementImpl::BUTTON:
2952 text = in->value().string();
2953 priority = 5;
2954 break;
2955 case HTMLInputElementImpl::RESET:
2956 text = in->value().string();
2957 if( text.isEmpty())
2958 text = i18n( "Reset" );
2959 priority = 5;
2960 break;
2961 case HTMLInputElementImpl::HIDDEN:
2962 ignore = true;
2963 break;
2964 case HTMLInputElementImpl::CHECKBOX:
2965 case HTMLInputElementImpl::RADIO:
2966 text_after = true;
2967 priority = 5;
2968 break;
2969 case HTMLInputElementImpl::TEXT:
2970 case HTMLInputElementImpl::PASSWORD:
2971 case HTMLInputElementImpl::FILE:
2972 text_before = true;
2973 priority = 5;
2974 break;
2975 default:
2976 priority = 5;
2977 break;
2979 break;
2981 case ID_BUTTON:
2982 text = static_cast< HTMLElementImpl* >( element )->innerText().string().simplified();
2983 switch( static_cast< HTMLButtonElementImpl* >( element )->buttonType()) {
2984 case HTMLButtonElementImpl::SUBMIT:
2985 if( text.isEmpty())
2986 text = i18n( "Submit" );
2987 priority = 7;
2988 break;
2989 case HTMLButtonElementImpl::RESET:
2990 if( text.isEmpty())
2991 text = i18n( "Reset" );
2992 priority = 5;
2993 break;
2994 default:
2995 priority = 5;
2996 break;
2998 break;
2999 case ID_SELECT: // these don't have accesskey attribute, but quick access may be handy
3000 text_before = true;
3001 text_after = true;
3002 priority = 5;
3003 break;
3004 case ID_FRAME:
3005 ignore = true;
3006 break;
3007 default:
3008 ignore = !element->isFocusable();
3009 priority = 2;
3010 break;
3012 if( ignore )
3013 continue;
3014 if( text.isNull() && labels.contains( element ))
3015 text = labels[ element ];
3016 if( text.isNull() && text_before )
3017 text = getElementText( element, false );
3018 if( text.isNull() && text_after )
3019 text = getElementText( element, true );
3020 text = text.trimmed();
3021 // increase priority of items which have explicitly specified accesskeys in the config
3022 const QList< QPair< QString, QChar > > priorities
3023 = m_part->settings()->fallbackAccessKeysAssignments();
3024 for( QList< QPair< QString, QChar > >::ConstIterator it = priorities.begin();
3025 it != priorities.end();
3026 ++it ) {
3027 if( text == (*it).first )
3028 priority = 10;
3030 AccessKeyData tmp = { element, text, url, priority };
3031 data.append( tmp );
3035 QList< QChar > keys;
3036 for( char c = 'A'; c <= 'Z'; ++c )
3037 keys << c;
3038 for( char c = '0'; c <= '9'; ++c )
3039 keys << c;
3040 for( NodeImpl* n = m_part->xmlDocImpl();
3041 n != NULL;
3042 n = n->traverseNextNode()) {
3043 if( n->isElementNode()) {
3044 ElementImpl* en = static_cast< ElementImpl* >( n );
3045 DOMString s = en->getAttribute( ATTR_ACCESSKEY );
3046 if( s.length() == 1 ) {
3047 QChar c = s.string()[ 0 ].toUpper();
3048 keys.removeAll( c ); // remove manually assigned accesskeys
3053 QMap< ElementImpl*, QChar > ret;
3054 for( int priority = 10; priority >= 0; --priority ) {
3055 for( QLinkedList< AccessKeyData >::Iterator it = data.begin();
3056 it != data.end();
3058 if( (*it).priority != priority ) {
3059 ++it;
3060 continue;
3062 if( keys.isEmpty())
3063 break;
3064 QString text = (*it).text;
3065 QChar key;
3066 if( key.isNull() && !text.isEmpty()) {
3067 const QList< QPair< QString, QChar > > priorities
3068 = m_part->settings()->fallbackAccessKeysAssignments();
3069 for( QList< QPair< QString, QChar > >::ConstIterator it = priorities.begin();
3070 it != priorities.end();
3071 ++it )
3072 if( text == (*it).first && keys.contains( (*it).second )) {
3073 key = (*it).second;
3074 break;
3077 // try first to select the first character as the accesskey,
3078 // then first character of the following words,
3079 // and then simply the first free character
3080 if( key.isNull() && !text.isEmpty()) {
3081 const QStringList words = text.split( ' ' );
3082 for( QStringList::ConstIterator it = words.begin();
3083 it != words.end();
3084 ++it ) {
3085 if( keys.contains( (*it)[ 0 ].toUpper())) {
3086 key = (*it)[ 0 ].toUpper();
3087 break;
3091 if( key.isNull() && !text.isEmpty()) {
3092 for( int i = 0; i < text.length(); ++i ) {
3093 if( keys.contains( text[ i ].toUpper())) {
3094 key = text[ i ].toUpper();
3095 break;
3099 if( key.isNull())
3100 key = keys.front();
3101 ret[ (*it).element ] = key;
3102 keys.removeAll( key );
3103 QString url = (*it).url;
3104 it = data.erase( it );
3105 // assign the same accesskey also to other elements pointing to the same url
3106 if( !url.isEmpty() && !url.startsWith( "javascript:", Qt::CaseInsensitive )) {
3107 for( QLinkedList< AccessKeyData >::Iterator it2 = data.begin();
3108 it2 != data.end();
3110 if( (*it2).url == url ) {
3111 ret[ (*it2).element ] = key;
3112 if( it == it2 )
3113 ++it;
3114 it2 = data.erase( it2 );
3115 } else
3116 ++it2;
3121 return ret;
3124 void KHTMLView::setMediaType( const QString &medium )
3126 m_medium = medium;
3129 QString KHTMLView::mediaType() const
3131 return m_medium;
3134 bool KHTMLView::pagedMode() const
3136 return d->paged;
3139 void KHTMLView::setWidgetVisible(RenderWidget* w, bool vis)
3141 if (vis) {
3142 d->visibleWidgets.insert(w, w->widget());
3144 else
3145 d->visibleWidgets.remove(w);
3148 bool KHTMLView::needsFullRepaint() const
3150 return d->needsFullRepaint;
3153 namespace {
3154 class QPointerDeleter
3156 public:
3157 explicit QPointerDeleter(QObject* o) : obj(o) {}
3158 ~QPointerDeleter() { delete obj; }
3159 private:
3160 const QPointer<QObject> obj;
3164 void KHTMLView::print(bool quick)
3166 if(!m_part->xmlDocImpl()) return;
3167 khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer());
3168 if(!root) return;
3170 QPointer<KHTMLPrintSettings> printSettings(new KHTMLPrintSettings); //XXX: doesn't save settings between prints like this
3171 const QPointerDeleter settingsDeleter(printSettings); //the printdialog takes ownership of the settings widget, thus this workaround to avoid double deletion
3172 QPrinter printer;
3173 QPointer<QPrintDialog> dialog = KdePrint::createPrintDialog(&printer, QList<QWidget*>() << printSettings, this);
3174 const QPointerDeleter dialogDeleter(dialog);
3176 QString docname = m_part->xmlDocImpl()->URL().prettyUrl();
3177 if ( !docname.isEmpty() )
3178 docname = KStringHandler::csqueeze(docname, 80);
3180 if(quick || (dialog->exec() && dialog)) { /*'this' and thus dialog might have been deleted while exec()!*/
3181 viewport()->setCursor( Qt::WaitCursor ); // only viewport(), no QApplication::, otherwise we get the busy cursor in kdeprint's dialogs
3182 // set up KPrinter
3183 printer.setFullPage(false);
3184 printer.setCreator(QString("KDE %1.%2.%3 HTML Library").arg(KDE_VERSION_MAJOR).arg(KDE_VERSION_MINOR).arg(KDE_VERSION_RELEASE));
3185 printer.setDocName(docname);
3187 QPainter *p = new QPainter;
3188 p->begin( &printer );
3189 khtml::setPrintPainter( p );
3191 m_part->xmlDocImpl()->setPaintDevice( &printer );
3192 QString oldMediaType = mediaType();
3193 setMediaType( "print" );
3194 // We ignore margin settings for html and body when printing
3195 // and use the default margins from the print-system
3196 // (In Qt 3.0.x the default margins are hardcoded in Qt)
3197 m_part->xmlDocImpl()->setPrintStyleSheet( printSettings->printFriendly() ?
3198 "* { background-image: none !important;"
3199 " background-color: white !important;"
3200 " color: black !important; }"
3201 "body { margin: 0px !important; }"
3202 "html { margin: 0px !important; }" :
3203 "body { margin: 0px !important; }"
3204 "html { margin: 0px !important; }"
3207 kDebug(6000) << "printing: physical page width = " << printer.width()
3208 << " height = " << printer.height() << endl;
3209 root->setStaticMode(true);
3210 root->setPagedMode(true);
3211 root->setWidth(printer.width());
3212 // root->setHeight(printer.height());
3213 root->setPageTop(0);
3214 root->setPageBottom(0);
3215 d->paged = true;
3217 m_part->xmlDocImpl()->styleSelector()->computeFontSizes(printer.logicalDpiY(), 100);
3218 m_part->xmlDocImpl()->updateStyleSelector();
3219 root->setPrintImages(printSettings->printImages());
3220 root->makePageBreakAvoidBlocks();
3222 root->setNeedsLayoutAndMinMaxRecalc();
3223 root->layout();
3225 // check sizes ask for action.. (scale or clip)
3227 bool printHeader = printSettings->printHeader();
3229 int headerHeight = 0;
3230 QFont headerFont("Sans Serif", 8);
3232 QString headerLeft = KGlobal::locale()->formatDate(QDate::currentDate(),KLocale::ShortDate);
3233 QString headerMid = docname;
3234 QString headerRight;
3236 if (printHeader)
3238 p->setFont(headerFont);
3239 headerHeight = (p->fontMetrics().lineSpacing() * 3) / 2;
3242 // ok. now print the pages.
3243 kDebug(6000) << "printing: html page width = " << root->docWidth()
3244 << " height = " << root->docHeight() << endl;
3245 kDebug(6000) << "printing: margins left = " << printer.pageRect().left() - printer.paperRect().left()
3246 << " top = " << printer.pageRect().top() - printer.paperRect().top() << endl;
3247 kDebug(6000) << "printing: paper width = " << printer.width()
3248 << " height = " << printer.height() << endl;
3249 // if the width is too large to fit on the paper we just scale
3250 // the whole thing.
3251 int pageWidth = printer.width();
3252 int pageHeight = printer.height();
3253 p->setClipRect(0,0, pageWidth, pageHeight);
3255 pageHeight -= headerHeight;
3257 bool scalePage = false;
3258 double scale = 0.0;
3259 #ifndef QT_NO_TRANSFORMATIONS
3260 if(root->docWidth() > printer.width()) {
3261 scalePage = true;
3262 scale = ((double) printer.width())/((double) root->docWidth());
3263 pageHeight = (int) (pageHeight/scale);
3264 pageWidth = (int) (pageWidth/scale);
3265 headerHeight = (int) (headerHeight/scale);
3267 #endif
3268 kDebug(6000) << "printing: scaled html width = " << pageWidth
3269 << " height = " << pageHeight << endl;
3271 root->setHeight(pageHeight);
3272 root->setPageBottom(pageHeight);
3273 root->setNeedsLayout(true);
3274 root->layoutIfNeeded();
3275 // m_part->slotDebugRenderTree();
3277 // Squeeze header to make it it on the page.
3278 if (printHeader)
3280 int available_width = printer.width() - 10 -
3281 2 * qMax(p->boundingRect(0, 0, printer.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerLeft).width(),
3282 p->boundingRect(0, 0, printer.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerRight).width());
3283 if (available_width < 150)
3284 available_width = 150;
3285 int mid_width;
3286 int squeeze = 120;
3287 do {
3288 headerMid = KStringHandler::csqueeze(docname, squeeze);
3289 mid_width = p->boundingRect(0, 0, printer.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerMid).width();
3290 squeeze -= 10;
3291 } while (mid_width > available_width);
3294 int top = 0;
3295 int bottom = 0;
3296 int page = 1;
3297 while(top < root->docHeight()) {
3298 if(top > 0) printer.newPage();
3299 p->save();
3300 p->setClipRect(0, 0, pageWidth, headerHeight);
3301 if (printHeader)
3303 int dy = p->fontMetrics().lineSpacing();
3304 p->setPen(Qt::black);
3305 p->setFont(headerFont);
3307 headerRight = QString("#%1").arg(page);
3309 p->drawText(0, 0, printer.width(), dy, Qt::AlignLeft, headerLeft);
3310 p->drawText(0, 0, printer.width(), dy, Qt::AlignHCenter, headerMid);
3311 p->drawText(0, 0, printer.width(), dy, Qt::AlignRight, headerRight);
3314 #ifndef QT_NO_TRANSFORMATIONS
3315 if (scalePage)
3316 p->scale(scale, scale);
3317 #endif
3318 p->restore();
3319 p->translate(0, headerHeight-top);
3321 bottom = top+pageHeight;
3323 root->setPageTop(top);
3324 root->setPageBottom(bottom);
3325 root->setPageNumber(page);
3327 root->layer()->paint(p, QRect(0, top, pageWidth, pageHeight));
3328 kDebug(6000) << "printed: page " << page <<" bottom At = " << bottom;
3330 top = bottom;
3331 p->resetTransform();
3332 page++;
3335 p->end();
3336 delete p;
3338 // and now reset the layout to the usual one...
3339 root->setPagedMode(false);
3340 root->setStaticMode(false);
3341 d->paged = false;
3342 khtml::setPrintPainter( 0 );
3343 setMediaType( oldMediaType );
3344 m_part->xmlDocImpl()->setPaintDevice( this );
3345 m_part->xmlDocImpl()->styleSelector()->computeFontSizes(m_part->xmlDocImpl()->logicalDpiY(), m_part->fontScaleFactor());
3346 m_part->xmlDocImpl()->updateStyleSelector();
3347 viewport()->unsetCursor();
3351 void KHTMLView::slotPaletteChanged()
3353 if(!m_part->xmlDocImpl()) return;
3354 DOM::DocumentImpl *document = m_part->xmlDocImpl();
3355 if (!document->isHTMLDocument()) return;
3356 khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(document->renderer());
3357 if(!root) return;
3358 root->style()->resetPalette();
3359 NodeImpl *body = static_cast<HTMLDocumentImpl*>(document)->body();
3360 if(!body) return;
3361 body->setChanged(true);
3362 body->recalcStyle( NodeImpl::Force );
3365 void KHTMLView::paint(QPainter *p, const QRect &rc, int yOff, bool *more)
3367 if(!m_part->xmlDocImpl()) return;
3368 khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer());
3369 if(!root) return;
3370 d->firstRepaintPending = false;
3372 QPaintDevice* opd = m_part->xmlDocImpl()->paintDevice();
3373 m_part->xmlDocImpl()->setPaintDevice(p->device());
3374 root->setPagedMode(true);
3375 root->setStaticMode(true);
3376 root->setWidth(rc.width());
3378 // save()
3379 QRegion creg = p->clipRegion();
3380 QTransform t = p->worldTransform();
3381 QRect w = p->window();
3382 QRect v = p->viewport();
3383 bool vte = p->viewTransformEnabled();
3384 bool wme = p->worldMatrixEnabled();
3386 p->setClipRect(rc);
3387 p->translate(rc.left(), rc.top());
3388 double scale = ((double) rc.width()/(double) root->docWidth());
3389 int height = (int) ((double) rc.height() / scale);
3390 #ifndef QT_NO_TRANSFORMATIONS
3391 p->scale(scale, scale);
3392 #endif
3393 root->setPageTop(yOff);
3394 root->setPageBottom(yOff+height);
3396 root->layer()->paint(p, QRect(0, yOff, root->docWidth(), height));
3397 if (more)
3398 *more = yOff + height < root->docHeight();
3400 // restore()
3401 p->setWorldTransform(t);
3402 p->setWindow(w);
3403 p->setViewport(v);
3404 p->setViewTransformEnabled( vte );
3405 p->setWorldMatrixEnabled( wme );
3406 if (!creg.isEmpty())
3407 p->setClipRegion( creg );
3408 else
3409 p->setClipRegion(QRegion(), Qt::NoClip);
3411 root->setPagedMode(false);
3412 root->setStaticMode(false);
3413 m_part->xmlDocImpl()->setPaintDevice( opd );
3416 void KHTMLView::render(QPainter* p, const QRect& r, const QPoint& off)
3418 d->firstRepaintPending = false;
3419 QRect clip(off.x()+r.x(), off.y()+r.y(),r.width(),r.height());
3420 if(!m_part || !m_part->xmlDocImpl() || !m_part->xmlDocImpl()->renderer()) {
3421 p->fillRect(clip, palette().brush(QPalette::Active, QPalette::Base));
3422 return;
3424 QPaintDevice* opd = m_part->xmlDocImpl()->paintDevice();
3425 m_part->xmlDocImpl()->setPaintDevice(p->device());
3427 // save()
3428 QRegion creg = p->clipRegion();
3429 QTransform t = p->worldTransform();
3430 QRect w = p->window();
3431 QRect v = p->viewport();
3432 bool vte = p->viewTransformEnabled();
3433 bool wme = p->worldMatrixEnabled();
3435 p->setClipRect(clip);
3436 QRect rect = r.translated(contentsX(),contentsY());
3437 p->translate(off.x()-contentsX(), off.y()-contentsY());
3439 m_part->xmlDocImpl()->renderer()->layer()->paint(p, rect);
3441 // restore()
3442 p->setWorldTransform(t);
3443 p->setWindow(w);
3444 p->setViewport(v);
3445 p->setViewTransformEnabled( vte );
3446 p->setWorldMatrixEnabled( wme );
3447 if (!creg.isEmpty())
3448 p->setClipRegion( creg );
3449 else
3450 p->setClipRegion(QRegion(), Qt::NoClip);
3452 m_part->xmlDocImpl()->setPaintDevice( opd );
3455 void KHTMLView::setHasStaticBackground(bool partial)
3457 // full static iframe is irreversible for now
3458 if (d->staticWidget == KHTMLViewPrivate::SBFull && m_kwp->isRedirected())
3459 return;
3461 d->staticWidget = partial ?
3462 KHTMLViewPrivate::SBPartial : KHTMLViewPrivate::SBFull;
3465 void KHTMLView::setHasNormalBackground()
3467 // full static iframe is irreversible for now
3468 if (d->staticWidget == KHTMLViewPrivate::SBFull && m_kwp->isRedirected())
3469 return;
3471 d->staticWidget = KHTMLViewPrivate::SBNone;
3474 void KHTMLView::addStaticObject(bool fixed)
3476 if (fixed)
3477 d->fixedObjectsCount++;
3478 else
3479 d->staticObjectsCount++;
3481 setHasStaticBackground( true /*partial*/ );
3484 void KHTMLView::removeStaticObject(bool fixed)
3486 if (fixed)
3487 d->fixedObjectsCount--;
3488 else
3489 d->staticObjectsCount--;
3491 assert( d->fixedObjectsCount >= 0 && d->staticObjectsCount >= 0 );
3493 if (!d->staticObjectsCount && !d->fixedObjectsCount)
3494 setHasNormalBackground();
3495 else
3496 setHasStaticBackground( true /*partial*/ );
3499 void KHTMLView::setVerticalScrollBarPolicy( Qt::ScrollBarPolicy policy )
3501 #ifndef KHTML_NO_SCROLLBARS
3502 d->vpolicy = policy;
3503 QScrollArea::setVerticalScrollBarPolicy(policy);
3504 #else
3505 Q_UNUSED( policy );
3506 #endif
3509 void KHTMLView::setHorizontalScrollBarPolicy( Qt::ScrollBarPolicy policy )
3511 #ifndef KHTML_NO_SCROLLBARS
3512 d->hpolicy = policy;
3513 QScrollArea::setHorizontalScrollBarPolicy(policy);
3514 #else
3515 Q_UNUSED( policy );
3516 #endif
3519 void KHTMLView::restoreScrollBar()
3521 int ow = visibleWidth();
3522 QScrollArea::setVerticalScrollBarPolicy(d->vpolicy);
3523 if (visibleWidth() != ow)
3524 layout();
3525 d->prevScrollbarVisible = verticalScrollBar()->isVisible();
3528 QStringList KHTMLView::formCompletionItems(const QString &name) const
3530 if (!m_part->settings()->isFormCompletionEnabled())
3531 return QStringList();
3532 if (!d->formCompletions)
3533 d->formCompletions = new KConfig(KStandardDirs::locateLocal("data", "khtml/formcompletions"));
3534 return d->formCompletions->group("").readEntry(name, QStringList());
3537 void KHTMLView::clearCompletionHistory(const QString& name)
3539 if (!d->formCompletions)
3541 d->formCompletions = new KConfig(KStandardDirs::locateLocal("data", "khtml/formcompletions"));
3543 d->formCompletions->group("").writeEntry(name, "");
3544 d->formCompletions->sync();
3547 void KHTMLView::addFormCompletionItem(const QString &name, const QString &value)
3549 if (!m_part->settings()->isFormCompletionEnabled())
3550 return;
3551 // don't store values that are all numbers or just numbers with
3552 // dashes or spaces as those are likely credit card numbers or
3553 // something similar
3554 bool cc_number(true);
3555 for ( int i = 0; i < value.length(); ++i)
3557 QChar c(value[i]);
3558 if (!c.isNumber() && c != '-' && !c.isSpace())
3560 cc_number = false;
3561 break;
3564 if (cc_number)
3565 return;
3566 QStringList items = formCompletionItems(name);
3567 if (!items.contains(value))
3568 items.prepend(value);
3569 while ((int)items.count() > m_part->settings()->maxFormCompletionItems())
3570 items.erase(items.isEmpty() ? items.end() : --items.end());
3571 d->formCompletions->group("").writeEntry(name, items);
3574 void KHTMLView::addNonPasswordStorableSite(const QString& host)
3576 if (!d->formCompletions) {
3577 d->formCompletions = new KConfig(KStandardDirs::locateLocal("data", "khtml/formcompletions"));
3580 KConfigGroup cg( d->formCompletions, "NonPasswordStorableSites");
3581 QStringList sites = cg.readEntry("Sites", QStringList());
3582 sites.append(host);
3583 cg.writeEntry("Sites", sites);
3584 cg.sync();
3587 bool KHTMLView::nonPasswordStorableSite(const QString& host) const
3589 if (!d->formCompletions) {
3590 d->formCompletions = new KConfig(KStandardDirs::locateLocal("data", "khtml/formcompletions"));
3592 QStringList sites = d->formCompletions->group( "NonPasswordStorableSites" ).readEntry("Sites", QStringList());
3593 return (sites.indexOf(host) != -1);
3596 // returns true if event should be swallowed
3597 bool KHTMLView::dispatchMouseEvent(int eventId, DOM::NodeImpl *targetNode,
3598 DOM::NodeImpl *targetNodeNonShared, bool cancelable,
3599 int detail,QMouseEvent *_mouse, bool setUnder,
3600 int mouseEventType, int orient)
3602 // if the target node is a text node, dispatch on the parent node - rdar://4196646 (and #76948)
3603 if (targetNode && targetNode->isTextNode())
3604 targetNode = targetNode->parentNode();
3606 if (d->underMouse)
3607 d->underMouse->deref();
3608 d->underMouse = targetNode;
3609 if (d->underMouse)
3610 d->underMouse->ref();
3612 if (d->underMouseNonShared)
3613 d->underMouseNonShared->deref();
3614 d->underMouseNonShared = targetNodeNonShared;
3615 if (d->underMouseNonShared)
3616 d->underMouseNonShared->ref();
3618 bool isWheelEvent = (mouseEventType == DOM::NodeImpl::MouseWheel);
3620 int exceptioncode = 0;
3621 int pageX = _mouse->x();
3622 int pageY = _mouse->y();
3623 revertTransforms(pageX, pageY);
3624 int clientX = pageX - contentsX();
3625 int clientY = pageY - contentsY();
3626 int screenX = _mouse->globalX();
3627 int screenY = _mouse->globalY();
3628 int button = -1;
3629 switch (_mouse->button()) {
3630 case Qt::LeftButton:
3631 button = 0;
3632 break;
3633 case Qt::MidButton:
3634 button = 1;
3635 break;
3636 case Qt::RightButton:
3637 button = 2;
3638 break;
3639 default:
3640 break;
3642 if (d->accessKeysEnabled && d->accessKeysPreActivate && button!=-1)
3643 d->accessKeysPreActivate=false;
3645 bool ctrlKey = (_mouse->modifiers() & Qt::ControlModifier);
3646 bool altKey = (_mouse->modifiers() & Qt::AltModifier);
3647 bool shiftKey = (_mouse->modifiers() & Qt::ShiftModifier);
3648 bool metaKey = (_mouse->modifiers() & Qt::MetaModifier);
3650 // mouseout/mouseover
3651 if (setUnder && d->oldUnderMouse != targetNode) {
3652 if (d->oldUnderMouse && d->oldUnderMouse->document() != m_part->xmlDocImpl()) {
3653 d->oldUnderMouse->deref();
3654 d->oldUnderMouse = 0;
3656 // send mouseout event to the old node
3657 if (d->oldUnderMouse) {
3658 // send mouseout event to the old node
3659 MouseEventImpl *me = new MouseEventImpl(EventImpl::MOUSEOUT_EVENT,
3660 true,true,m_part->xmlDocImpl()->defaultView(),
3661 0,screenX,screenY,clientX,clientY,pageX, pageY,
3662 ctrlKey,altKey,shiftKey,metaKey,
3663 button,targetNode);
3664 me->ref();
3665 d->oldUnderMouse->dispatchEvent(me,exceptioncode,true);
3666 me->deref();
3668 // send mouseover event to the new node
3669 if (targetNode) {
3670 MouseEventImpl *me = new MouseEventImpl(EventImpl::MOUSEOVER_EVENT,
3671 true,true,m_part->xmlDocImpl()->defaultView(),
3672 0,screenX,screenY,clientX,clientY,pageX, pageY,
3673 ctrlKey,altKey,shiftKey,metaKey,
3674 button,d->oldUnderMouse);
3676 me->ref();
3677 targetNode->dispatchEvent(me,exceptioncode,true);
3678 me->deref();
3680 if (d->oldUnderMouse)
3681 d->oldUnderMouse->deref();
3682 d->oldUnderMouse = targetNode;
3683 if (d->oldUnderMouse)
3684 d->oldUnderMouse->ref();
3687 bool swallowEvent = false;
3689 if (targetNode) {
3690 // if the target node is a disabled widget, we don't want any full-blown mouse events
3691 if (targetNode->isGenericFormElement()
3692 && static_cast<HTMLGenericFormElementImpl*>(targetNode)->disabled())
3693 return true;
3695 // send the actual event
3696 bool dblclick = ( eventId == EventImpl::CLICK_EVENT &&
3697 _mouse->type() == QEvent::MouseButtonDblClick );
3698 MouseEventImpl *me = new MouseEventImpl(static_cast<EventImpl::EventId>(eventId),
3699 true,cancelable,m_part->xmlDocImpl()->defaultView(),
3700 detail,screenX,screenY,clientX,clientY,pageX, pageY,
3701 ctrlKey,altKey,shiftKey,metaKey,
3702 button,0, isWheelEvent ? 0 : _mouse, dblclick,
3703 isWheelEvent ? static_cast<MouseEventImpl::Orientation>(orient) : MouseEventImpl::ONone );
3704 me->ref();
3705 if ( !d->m_mouseEventsTarget && RenderLayer::gScrollBar && eventId == EventImpl::MOUSEDOWN_EVENT )
3706 // button is pressed inside a layer scrollbar, so make it the target for future mousemove events until released
3707 d->m_mouseEventsTarget = RenderLayer::gScrollBar;
3708 if ( d->m_mouseEventsTarget && qobject_cast<QScrollBar*>(d->m_mouseEventsTarget) &&
3709 dynamic_cast<KHTMLWidget*>(static_cast<QWidget*>(d->m_mouseEventsTarget)) ) {
3710 // we have a sticky mouse event target and it is a layer's scrollbar. Forward events manually.
3711 // ### should use the dom
3712 KHTMLWidget*w = dynamic_cast<KHTMLWidget*>(static_cast<QWidget*>(d->m_mouseEventsTarget));
3713 QPoint p = w->m_kwp->absolutePos();
3714 QMouseEvent fw(_mouse->type(), QPoint(pageX, pageY)-p, _mouse->button(), _mouse->buttons(), _mouse->modifiers());
3715 static_cast<RenderWidget::EventPropagator *>(static_cast<QWidget*>(d->m_mouseEventsTarget))->sendEvent(&fw);
3716 if (_mouse->type() == QMouseEvent::MouseButtonPress && _mouse->button() == Qt::RightButton) {
3717 QContextMenuEvent cme(QContextMenuEvent::Mouse, p);
3718 static_cast<RenderWidget::EventPropagator *>(static_cast<QWidget*>(d->m_mouseEventsTarget))->sendEvent(&cme);
3719 d->m_mouseEventsTarget = 0;
3721 swallowEvent = true;
3722 } else {
3723 targetNode->dispatchEvent(me,exceptioncode,true);
3724 bool defaultHandled = me->defaultHandled();
3725 if (defaultHandled || me->defaultPrevented())
3726 swallowEvent = true;
3728 me->deref();
3730 if (eventId == EventImpl::MOUSEDOWN_EVENT) {
3731 // Focus should be shifted on mouse down, not on a click. -dwh
3732 // Blur current focus node when a link/button is clicked; this
3733 // is expected by some sites that rely on onChange handlers running
3734 // from form fields before the button click is processed.
3735 DOM::NodeImpl* nodeImpl = targetNode;
3736 for ( ; nodeImpl && !nodeImpl->isFocusable(); nodeImpl = nodeImpl->parentNode())
3738 if (nodeImpl && nodeImpl->isMouseFocusable())
3739 m_part->xmlDocImpl()->setFocusNode(nodeImpl);
3740 else if (!nodeImpl || !nodeImpl->focused())
3741 m_part->xmlDocImpl()->setFocusNode(0);
3745 return swallowEvent;
3748 void KHTMLView::setIgnoreWheelEvents( bool e )
3750 d->ignoreWheelEvents = e;
3753 #ifndef QT_NO_WHEELEVENT
3755 void KHTMLView::wheelEvent(QWheelEvent* e)
3757 // check if we should reset the state of the indicator describing if
3758 // we are currently scrolling the view as a result of wheel events
3759 if (d->scrollingFromWheel != QPoint(-1,-1) && d->scrollingFromWheel != QCursor::pos())
3760 d->scrollingFromWheel = d->scrollingFromWheelTimerId ? QCursor::pos() : QPoint(-1,-1);
3762 if (d->accessKeysEnabled && d->accessKeysPreActivate) d->accessKeysPreActivate=false;
3764 if ( ( e->modifiers() & Qt::ControlModifier) == Qt::ControlModifier )
3766 emit zoomView( - e->delta() );
3767 e->accept();
3769 else if (d->firstLayoutPending)
3771 e->accept();
3773 else if( !m_kwp->isRedirected() &&
3774 ( (e->orientation() == Qt::Vertical &&
3775 ((d->ignoreWheelEvents && !verticalScrollBar()->isVisible())
3776 || e->delta() > 0 && contentsY() <= 0
3777 || e->delta() < 0 && contentsY() >= contentsHeight() - visibleHeight()))
3779 (e->orientation() == Qt::Horizontal &&
3780 ((d->ignoreWheelEvents && !horizontalScrollBar()->isVisible())
3781 || e->delta() > 0 && contentsX() <=0
3782 || e->delta() < 0 && contentsX() >= contentsWidth() - visibleWidth())))
3783 && m_part->parentPart())
3785 if ( m_part->parentPart()->view() )
3786 m_part->parentPart()->view()->wheelEvent( e );
3787 e->ignore();
3789 else
3791 int xm = e->x();
3792 int ym = e->y();
3793 revertTransforms(xm, ym);
3795 DOM::NodeImpl::MouseEvent mev( e->buttons(), DOM::NodeImpl::MouseWheel );
3796 m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev );
3798 MouseEventImpl::Orientation o = MouseEventImpl::OVertical;
3799 if (e->orientation() == Qt::Horizontal)
3800 o = MouseEventImpl::OHorizontal;
3802 QMouseEvent _mouse(QEvent::MouseMove, QPoint(xm,ym), Qt::NoButton, e->buttons(), e->modifiers());
3803 bool swallow = dispatchMouseEvent(EventImpl::KHTML_MOUSEWHEEL_EVENT,mev.innerNode.handle(),mev.innerNonSharedNode.handle(),
3804 true,-e->delta()/40,&_mouse,true,DOM::NodeImpl::MouseWheel,o);
3806 if (swallow)
3807 return;
3809 d->scrollBarMoved = true;
3810 d->scrollingFromWheel = QCursor::pos();
3811 if (d->smoothScrollMode != SSMDisabled)
3812 d->shouldSmoothScroll = true;
3813 if (d->scrollingFromWheelTimerId)
3814 killTimer(d->scrollingFromWheelTimerId);
3815 d->scrollingFromWheelTimerId = startTimer(400);
3817 if (m_part->parentPart()) {
3818 // don't propagate if we are a sub-frame and our scrollbars are already at end of range
3819 bool h = (static_cast<QWheelEvent*>(e)->orientation() == Qt::Horizontal);
3820 bool d = (static_cast<QWheelEvent*>(e)->delta() < 0);
3821 QScrollBar* hsb = horizontalScrollBar();
3822 QScrollBar* vsb = verticalScrollBar();
3823 if ( h && (d && hsb->value() == hsb->maximum() || !d && hsb->value() == hsb->minimum()) ||
3824 !h && (d && vsb->value() == vsb->maximum() || !d && vsb->value() == vsb->minimum()) ) {
3825 e->accept();
3826 return;
3829 QScrollArea::wheelEvent( e );
3833 #endif
3835 void KHTMLView::dragEnterEvent( QDragEnterEvent* ev )
3837 // Still overridden for BC reasons only...
3838 QScrollArea::dragEnterEvent( ev );
3841 void KHTMLView::dropEvent( QDropEvent *ev )
3843 // Still overridden for BC reasons only...
3844 QScrollArea::dropEvent( ev );
3847 void KHTMLView::focusInEvent( QFocusEvent *e )
3849 DOM::NodeImpl* fn = m_part->xmlDocImpl() ? m_part->xmlDocImpl()->focusNode() : 0;
3850 #ifndef KHTML_NO_TYPE_AHEAD_FIND
3851 if (!fn || m_part->isCaretMode())
3852 m_part->enableFindAheadActions( true );
3853 #endif
3854 if (fn && fn->renderer() && fn->renderer()->isWidget() &&
3855 (e->reason() != Qt::MouseFocusReason) &&
3856 static_cast<khtml::RenderWidget*>(fn->renderer())->widget())
3857 static_cast<khtml::RenderWidget*>(fn->renderer())->widget()->setFocus();
3858 m_part->setSelectionVisible();
3859 QScrollArea::focusInEvent( e );
3862 void KHTMLView::focusOutEvent( QFocusEvent *e )
3864 if (m_part) {
3865 m_part->stopAutoScroll();
3866 m_part->setSelectionVisible(false);
3869 #ifndef KHTML_NO_TYPE_AHEAD_FIND
3870 if(d->typeAheadActivated)
3872 findTimeout();
3874 if (m_part)
3875 m_part->enableFindAheadActions( false );
3876 #endif // KHTML_NO_TYPE_AHEAD_FIND
3878 if ( d->cursorIconWidget )
3879 d->cursorIconWidget->hide();
3881 QScrollArea::focusOutEvent( e );
3884 void KHTMLView::scrollContentsBy( int dx, int dy )
3886 if (!dx && !dy) return;
3888 if ( !d->firstLayoutPending && !d->complete && m_part->xmlDocImpl() &&
3889 d->layoutSchedulingEnabled) {
3890 // contents scroll while we are not complete: we need to check our layout *now*
3891 khtml::RenderCanvas* root = static_cast<khtml::RenderCanvas *>( m_part->xmlDocImpl()->renderer() );
3892 if (root && root->needsLayout()) {
3893 unscheduleRelayout();
3894 layout();
3896 if (d->smoothScrollMode == KHTMLView::SSMWhenEfficient && m_part->xmlDocImpl()->parsing())
3897 d->shouldSmoothScroll = false;
3900 if ( d->shouldSmoothScroll && d->smoothScrollMode != SSMDisabled && m_part->xmlDocImpl() &&
3901 m_part->xmlDocImpl()->renderer()) {
3903 bool doSmoothScroll = (!d->staticWidget || d->smoothScrollMode == SSMEnabled);
3905 int numStaticPixels = 0;
3906 QRegion r = static_cast<RenderCanvas*>(m_part->xmlDocImpl()->renderer())->staticRegion();
3908 // only do smooth scrolling if static region is relatively small
3909 if (!doSmoothScroll && d->staticWidget == KHTMLViewPrivate::SBPartial && r.rects().size() <= 10) {
3910 foreach(QRect rr, r.rects())
3911 numStaticPixels += rr.width()*rr.height();
3912 if ((numStaticPixels < sSmoothScrollMinStaticPixels) || (numStaticPixels*8 < visibleWidth()*visibleHeight()))
3913 doSmoothScroll = true;
3915 if (doSmoothScroll) {
3916 setupSmoothScrolling(dx, dy);
3917 return;
3921 if (!d->scrollingSelf) {
3922 d->scrollBarMoved = true;
3923 d->contentsMoving = true;
3924 // ensure quick reset of contentsMoving flag
3925 scheduleRepaint(0, 0, 0, 0);
3928 if (m_part->xmlDocImpl() && m_part->xmlDocImpl()->documentElement()) {
3929 // ### FIXME: there is something wrong with this event.
3930 // With a capturing listener on document and window, window's should fire first, then document's.
3931 // Also, this doesn't work: <body onload="document.onscroll=function() {alert('ok')}"><div style=height:2000>
3932 m_part->xmlDocImpl()->documentElement()->dispatchWindowEvent(EventImpl::SCROLL_EVENT, false, false);
3935 if (QApplication::isRightToLeft())
3936 dx = -dx;
3938 if (!d->smoothScrolling) {
3939 d->updateContentsXY();
3940 } else {
3941 d->contentsX -= dx;
3942 d->contentsY -= dy;
3944 if (widget()->pos() != QPoint(0,0)) {
3945 kDebug(6000) << "Static widget wasn't positioned at (0,0). This should NOT happen. Please report this event to developers.";
3946 kDebug(6000) << kBacktrace();
3947 widget()->move(0,0);
3950 QWidget *w = widget();
3951 QPoint off;
3952 if (m_kwp->isRedirected()) {
3953 // This is a redirected sub frame. Translate to root view context
3954 KHTMLView* v = m_kwp->rootViewPos( off );
3955 if (v)
3956 w = v->widget();
3957 off = viewport()->mapTo(this, off);
3960 #ifdef FIX_QT_BROKEN_QWIDGET_SCROLL
3961 bool hideScrollBars = false;
3962 if (horizontalScrollBar()->isVisible() && verticalScrollBar()->isVisible()) {
3963 if (!d->brokenQWidgetScroll) {
3964 d->shouldBeBlitting = true;
3965 } else {
3966 hideScrollBars = true;
3969 #endif
3971 if ( d->staticWidget ) {
3973 // now remove from view the external widgets that must have completely
3974 // disappeared after dx/dy scroll delta is effective
3975 if (!d->visibleWidgets.isEmpty())
3976 checkExternalWidgetsPosition();
3978 if ( d->staticWidget == KHTMLViewPrivate::SBPartial
3979 && m_part->xmlDocImpl() && m_part->xmlDocImpl()->renderer() ) {
3980 #ifdef FIX_QT_BROKEN_QWIDGET_SCROLL
3981 if (hideScrollBars) {
3982 horizontalScrollBar()->parentWidget()->lower();
3983 verticalScrollBar()->parentWidget()->lower();
3985 #endif
3986 // static objects might be selectively repainted, like stones in flowing water
3987 QRegion r = static_cast<RenderCanvas*>(m_part->xmlDocImpl()->renderer())->staticRegion();
3988 r.translate( -contentsX(), -contentsY());
3989 QVector<QRect> ar = r.rects();
3990 #ifdef FIX_QT_BROKEN_QWIDGET_SCROLL
3991 if (ar.size() == 1 && ar[0].width() >= visibleWidth() && ar[0].height() >= visibleHeight())
3992 d->shouldBeBlitting = false;
3993 #endif
3994 for (int i = 0; i < ar.size() ; ++i) {
3995 widget()->update( ar[i] );
3997 r = QRegion(QRect(0, 0, visibleWidth(), visibleHeight())) - r;
3998 ar = r.rects();
3999 for (int i = 0; i < ar.size() ; ++i) {
4000 w->scroll( dx, dy, ar[i].translated(off) );
4002 #ifdef FIX_QT_BROKEN_QWIDGET_SCROLL
4003 if (hideScrollBars) {
4004 horizontalScrollBar()->parentWidget()->raise();
4005 verticalScrollBar()->parentWidget()->raise();
4007 #endif
4008 d->scrollExternalWidgets(dx, dy);
4009 } else {
4010 #ifdef FIX_QT_BROKEN_QWIDGET_SCROLL
4011 d->shouldBeBlitting = false;
4012 #endif
4013 // we can't avoid a full update
4014 widget()->update();
4016 return;
4019 #ifdef FIX_QT_BROKEN_QWIDGET_SCROLL
4020 if (hideScrollBars) {
4021 horizontalScrollBar()->parentWidget()->lower();
4022 verticalScrollBar()->parentWidget()->lower();
4024 #endif
4026 if (m_kwp->isRedirected()) {
4027 const QRect rect(off.x(), off.y(), visibleWidth() * d->zoomLevel / 100, visibleHeight() * d->zoomLevel / 100);
4028 w->scroll(dx, dy, rect);
4029 if (d->zoomLevel != 100) {
4030 w->update(rect); // without this update we are getting bad rendering when an iframe is zoomed in
4032 } else {
4033 widget()->scroll(dx, dy, widget()->rect() & viewport()->rect());
4036 #ifdef FIX_QT_BROKEN_QWIDGET_SCROLL
4037 if (hideScrollBars) {
4038 horizontalScrollBar()->parentWidget()->raise();
4039 verticalScrollBar()->parentWidget()->raise();
4041 #endif
4043 d->scrollExternalWidgets(dx, dy);
4046 void KHTMLView::setupSmoothScrolling(int dx, int dy)
4048 // full scroll is remaining scroll plus new scroll
4049 d->dx = d->dx + dx;
4050 d->dy = d->dy + dy;
4052 if (d->dx == 0 && d->dy == 0) return;
4054 int steps = sSmoothScrollTime/sSmoothScrollTick;
4056 // average step size (stored in 1/16 px/step)
4057 d->ddx = (d->dx*16)/(steps+1);
4058 d->ddy = (d->dy*16)/(steps+1);
4060 if (abs(d->ddx) < 64 && abs(d->ddy) < 64) {
4061 // Don't move slower than average 4px/step in minimum one direction
4062 if (d->ddx > 0) d->ddx = qMax(d->ddx, 64);
4063 if (d->ddy > 0) d->ddy = qMax(d->ddy, 64);
4064 if (d->ddx < 0) d->ddx = qMin(d->ddx, -64);
4065 if (d->ddy < 0) d->ddy = qMin(d->ddy, -64);
4066 // This means fewer than normal steps
4067 steps = qMax(d->ddx ? (d->dx*16)/d->ddx : 0, d->ddy ? (d->dy*16)/d->ddy : 0);
4068 if (steps < 1) steps = 1;
4069 d->ddx = (d->dx*16)/(steps+1);
4070 d->ddy = (d->dy*16)/(steps+1);
4073 // step size starts at double average speed and ends at 0
4074 d->ddx *= 2;
4075 d->ddy *= 2;
4077 // deacceleration speed
4078 d->dddx = (d->ddx+1)/steps;
4079 d->dddy = (d->ddy+1)/steps;
4081 if (!d->smoothScrolling) {
4082 d->startScrolling();
4083 scrollTick();
4087 void KHTMLView::scrollTick() {
4088 if (d->dx == 0 && d->dy == 0) {
4089 d->stopScrolling();
4090 return;
4093 // step size + remaining partial step
4094 int tddx = d->ddx + d->rdx;
4095 int tddy = d->ddy + d->rdy;
4097 // don't go under 1px/step
4098 if (tddx > 0 && tddx < 16) tddx = 16;
4099 if (tddy > 0 && tddy < 16) tddy = 16;
4100 if (tddx < 0 && tddx > -16) tddx = -16;
4101 if (tddy < 0 && tddy > -16) tddy = -16;
4103 // full pixel steps to scroll in this step
4104 int ddx = tddx / 16;
4105 int ddy = tddy / 16;
4106 // remaining partial step (this is especially needed for 1.x sized steps)
4107 d->rdx = tddx % 16;
4108 d->rdy = tddy % 16;
4110 // limit step to requested scrolling distance
4111 if (abs(ddx) > abs(d->dx)) ddx = d->dx;
4112 if (abs(ddy) > abs(d->dy)) ddy = d->dy;
4114 // Don't stop if deaccelerated too fast
4115 if (!ddx) ddx = d->dx;
4116 if (!ddy) ddy = d->dy;
4118 // update remaining scroll
4119 d->dx -= ddx;
4120 d->dy -= ddy;
4122 d->shouldSmoothScroll = false;
4123 scrollContentsBy(ddx, ddy);
4125 // update scrolling speed
4126 int dddx = d->dddx;
4127 int dddy = d->dddy;
4128 // don't change direction
4129 if (abs(dddx) > abs(d->ddx)) dddx = d->ddx;
4130 if (abs(dddy) > abs(d->ddy)) dddy = d->ddy;
4132 d->ddx -= dddx;
4133 d->ddy -= dddy;
4137 void KHTMLView::addChild(QWidget * child, int x, int y)
4139 if (!child)
4140 return;
4142 if (child->parent() != widget())
4143 child->setParent( widget() );
4145 // ### handle pseudo-zooming of non-redirected widgets (e.g. just resize'em)
4147 child->move(x-contentsX(), y-contentsY());
4150 void KHTMLView::timerEvent ( QTimerEvent *e )
4152 // kDebug() << "timer event " << e->timerId();
4153 if ( e->timerId() == d->scrollTimerId ) {
4154 if( d->scrollSuspended )
4155 return;
4156 switch (d->scrollDirection) {
4157 case KHTMLViewPrivate::ScrollDown:
4158 if (contentsY() + visibleHeight () >= contentsHeight())
4159 d->newScrollTimer(this, 0);
4160 else
4161 verticalScrollBar()->setValue( verticalScrollBar()->value() +d->scrollBy );
4162 break;
4163 case KHTMLViewPrivate::ScrollUp:
4164 if (contentsY() <= 0)
4165 d->newScrollTimer(this, 0);
4166 else
4167 verticalScrollBar()->setValue( verticalScrollBar()->value() -d->scrollBy );
4168 break;
4169 case KHTMLViewPrivate::ScrollRight:
4170 if (contentsX() + visibleWidth () >= contentsWidth())
4171 d->newScrollTimer(this, 0);
4172 else
4173 horizontalScrollBar()->setValue( horizontalScrollBar()->value() +d->scrollBy );
4174 break;
4175 case KHTMLViewPrivate::ScrollLeft:
4176 if (contentsX() <= 0)
4177 d->newScrollTimer(this, 0);
4178 else
4179 horizontalScrollBar()->setValue( horizontalScrollBar()->value() -d->scrollBy );
4180 break;
4182 return;
4184 else if ( e->timerId() == d->scrollingFromWheelTimerId ) {
4185 killTimer( d->scrollingFromWheelTimerId );
4186 d->scrollingFromWheelTimerId = 0;
4187 } else if ( e->timerId() == d->layoutTimerId ) {
4188 if (d->firstLayoutPending && d->layoutAttemptCounter < 4
4189 && (!m_part->xmlDocImpl() || !m_part->xmlDocImpl()->readyForLayout())) {
4190 d->layoutAttemptCounter++;
4191 killTimer(d->layoutTimerId);
4192 d->layoutTimerId = 0;
4193 scheduleRelayout();
4194 return;
4196 layout();
4197 d->scheduledLayoutCounter++;
4198 if (d->firstLayoutPending) {
4199 d->firstLayoutPending = false;
4200 verticalScrollBar()->setEnabled( true );
4201 horizontalScrollBar()->setEnabled( true );
4205 d->contentsMoving = false;
4206 if( m_part->xmlDocImpl() ) {
4207 DOM::DocumentImpl *document = m_part->xmlDocImpl();
4208 khtml::RenderCanvas* root = static_cast<khtml::RenderCanvas *>(document->renderer());
4210 if ( root && root->needsLayout() ) {
4211 if (d->repaintTimerId)
4212 killTimer(d->repaintTimerId);
4213 d->repaintTimerId = 0;
4214 scheduleRelayout();
4215 return;
4219 if (d->repaintTimerId)
4220 killTimer(d->repaintTimerId);
4221 d->repaintTimerId = 0;
4223 QRect updateRegion;
4224 const QVector<QRect> rects = d->updateRegion.rects();
4226 d->updateRegion = QRegion();
4228 if ( rects.size() )
4229 updateRegion = rects[0];
4231 for ( int i = 1; i < rects.size(); ++i ) {
4232 QRect newRegion = updateRegion.unite(rects[i]);
4233 if (2*newRegion.height() > 3*updateRegion.height() )
4235 repaintContents( updateRegion );
4236 updateRegion = rects[i];
4238 else
4239 updateRegion = newRegion;
4242 if ( !updateRegion.isNull() )
4243 repaintContents( updateRegion );
4245 // As widgets can only be accurately positioned during painting, every layout might
4246 // dissociate a widget from its RenderWidget. E.g: if a RenderWidget was visible before layout, but the layout
4247 // pushed it out of the viewport, it will not be repainted, and consequently it's associated widget won't be repositioned.
4248 // 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.
4250 if (d->dirtyLayout && !d->visibleWidgets.isEmpty())
4251 checkExternalWidgetsPosition();
4253 d->dirtyLayout = false;
4255 emit repaintAccessKeys();
4256 if (d->emitCompletedAfterRepaint) {
4257 bool full = d->emitCompletedAfterRepaint == KHTMLViewPrivate::CSFull;
4258 d->emitCompletedAfterRepaint = KHTMLViewPrivate::CSNone;
4259 if ( full )
4260 emit m_part->completed();
4261 else
4262 emit m_part->completed(true);
4266 void KHTMLView::checkExternalWidgetsPosition()
4268 QWidget* w;
4269 QRect visibleRect(contentsX(), contentsY(), visibleWidth(), visibleHeight());
4270 QList<RenderWidget*> toRemove;
4271 QHashIterator<void*, QWidget*> it(d->visibleWidgets);
4272 while (it.hasNext()) {
4273 int xp = 0, yp = 0;
4274 it.next();
4275 RenderWidget* rw = static_cast<RenderWidget*>( it.key() );
4276 if (!rw->absolutePosition(xp, yp) ||
4277 !visibleRect.intersects(QRect(xp, yp, it.value()->width(), it.value()->height())))
4278 toRemove.append(rw);
4280 foreach (RenderWidget* r, toRemove)
4281 if ( (w = d->visibleWidgets.take(r) ) )
4282 w->move( 0, -500000);
4285 void KHTMLView::scheduleRelayout(khtml::RenderObject * /*clippedObj*/)
4287 if (!d->layoutSchedulingEnabled || d->layoutTimerId)
4288 return;
4290 int time = 0;
4291 if (d->firstLayoutPending) {
4292 // Any repaint happening while we have no content blanks the viewport ("white flash").
4293 // Hence the need to delay the first layout as much as we can.
4294 // Only if the document gets stuck for too long in incomplete state will we allow the blanking.
4295 time = d->layoutAttemptCounter ?
4296 sLayoutAttemptDelay + sLayoutAttemptIncrement*d->layoutAttemptCounter : sFirstLayoutDelay;
4297 } else if (m_part->xmlDocImpl() && m_part->xmlDocImpl()->parsing()) {
4298 // Delay between successive layouts in parsing mode.
4299 // Increment reflects the decaying importance of visual feedback over time.
4300 time = qMin(2000, sParsingLayoutsInterval + d->scheduledLayoutCounter*sParsingLayoutsIncrement);
4302 d->layoutTimerId = startTimer( time );
4305 void KHTMLView::unscheduleRelayout()
4307 if (!d->layoutTimerId)
4308 return;
4310 killTimer(d->layoutTimerId);
4311 d->layoutTimerId = 0;
4314 void KHTMLView::unscheduleRepaint()
4316 if (!d->repaintTimerId)
4317 return;
4319 killTimer(d->repaintTimerId);
4320 d->repaintTimerId = 0;
4323 void KHTMLView::scheduleRepaint(int x, int y, int w, int h, bool asap)
4325 bool parsing = !m_part->xmlDocImpl() || m_part->xmlDocImpl()->parsing();
4327 // kDebug() << "parsing " << parsing;
4328 // kDebug() << "complete " << d->complete;
4330 int time = parsing && !d->firstLayoutPending ? 150 : (!asap ? ( !d->complete ? 80 : 20 ) : 0);
4332 #ifdef DEBUG_FLICKER
4333 QPainter p;
4334 p.begin( viewport() );
4336 int vx, vy;
4337 contentsToViewport( x, y, vx, vy );
4338 p.fillRect( vx, vy, w, h, Qt::red );
4339 p.end();
4340 #endif
4342 d->updateRegion = d->updateRegion.unite(QRect(x,y,w,h));
4344 if (asap && !parsing)
4345 unscheduleRepaint();
4347 if ( !d->repaintTimerId )
4348 d->repaintTimerId = startTimer( time );
4350 // kDebug() << "starting timer " << time;
4353 void KHTMLView::complete( bool pendingAction )
4355 // kDebug() << "KHTMLView::complete()";
4357 d->complete = true;
4359 // is there a relayout pending?
4360 if (d->layoutTimerId)
4362 // kDebug() << "requesting relayout now";
4363 // do it now
4364 killTimer(d->layoutTimerId);
4365 d->layoutTimerId = startTimer( 0 );
4366 d->emitCompletedAfterRepaint = pendingAction ?
4367 KHTMLViewPrivate::CSActionPending : KHTMLViewPrivate::CSFull;
4370 // is there a repaint pending?
4371 if (d->repaintTimerId)
4373 // kDebug() << "requesting repaint now";
4374 // do it now
4375 killTimer(d->repaintTimerId);
4376 d->repaintTimerId = startTimer( 0 );
4377 d->emitCompletedAfterRepaint = pendingAction ?
4378 KHTMLViewPrivate::CSActionPending : KHTMLViewPrivate::CSFull;
4381 if (!d->emitCompletedAfterRepaint)
4383 if (!pendingAction)
4384 emit m_part->completed();
4385 else
4386 emit m_part->completed(true);
4391 void KHTMLView::updateScrollBars()
4393 const QWidget *view = widget();
4394 if (!view)
4395 return;
4397 QSize p = viewport()->size();
4398 QSize m = maximumViewportSize();
4400 if (m.expandedTo(view->size()) == m)
4401 p = m; // no scroll bars needed
4403 QSize v = view->size();
4404 horizontalScrollBar()->setRange(0, v.width() - p.width());
4405 horizontalScrollBar()->setPageStep(p.width());
4406 verticalScrollBar()->setRange(0, v.height() - p.height());
4407 verticalScrollBar()->setPageStep(p.height());
4408 if (!d->smoothScrolling) {
4409 d->updateContentsXY();
4413 void KHTMLView::slotMouseScrollTimer()
4415 horizontalScrollBar()->setValue( horizontalScrollBar()->value() +d->m_mouseScroll_byX );
4416 verticalScrollBar()->setValue( verticalScrollBar()->value() +d->m_mouseScroll_byY);
4420 static DOM::Position positionOfLineBoundary(const DOM::Position &pos, bool toEnd)
4422 Selection sel = pos;
4423 sel.expandUsingGranularity(Selection::LINE);
4424 return toEnd ? sel.end() : sel.start();
4427 inline static DOM::Position positionOfLineBegin(const DOM::Position &pos)
4429 return positionOfLineBoundary(pos, false);
4432 inline static DOM::Position positionOfLineEnd(const DOM::Position &pos)
4434 return positionOfLineBoundary(pos, true);
4437 bool KHTMLView::caretKeyPressEvent(QKeyEvent *_ke)
4439 EditorContext *ec = &m_part->d->editor_context;
4440 Selection &caret = ec->m_selection;
4441 Position old_pos = caret.caretPos();
4442 Position pos = old_pos;
4443 bool recalcXPos = true;
4444 bool handled = true;
4446 bool ctrl = _ke->modifiers() & Qt::ControlModifier;
4447 bool shift = _ke->modifiers() & Qt::ShiftModifier;
4449 switch(_ke->key()) {
4451 // -- Navigational keys
4452 case Qt::Key_Down:
4453 pos = old_pos.nextLinePosition(caret.xPosForVerticalArrowNavigation(Selection::EXTENT));
4454 recalcXPos = false;
4455 break;
4457 case Qt::Key_Up:
4458 pos = old_pos.previousLinePosition(caret.xPosForVerticalArrowNavigation(Selection::EXTENT));
4459 recalcXPos = false;
4460 break;
4462 case Qt::Key_Left:
4463 pos = ctrl ? old_pos.previousWordPosition() : old_pos.previousCharacterPosition();
4464 break;
4466 case Qt::Key_Right:
4467 pos = ctrl ? old_pos.nextWordPosition() : old_pos.nextCharacterPosition();
4468 break;
4470 case Qt::Key_PageDown:
4471 // moveCaretNextPage(); ###
4472 break;
4474 case Qt::Key_PageUp:
4475 // moveCaretPrevPage(); ###
4476 break;
4478 case Qt::Key_Home:
4479 if (ctrl)
4480 /*moveCaretToDocumentBoundary(false)*/; // ###
4481 else
4482 pos = positionOfLineBegin(old_pos);
4483 break;
4485 case Qt::Key_End:
4486 if (ctrl)
4487 /*moveCaretToDocumentBoundary(true)*/; // ###
4488 else
4489 pos = positionOfLineEnd(old_pos);
4490 break;
4492 default:
4493 handled = false;
4495 }/*end switch*/
4497 if (pos != old_pos) {
4498 m_part->clearCaretRectIfNeeded();
4500 caret.moveTo(shift ? caret.nonCaretPos() : pos, pos);
4501 int old_x = caret.xPosForVerticalArrowNavigation(Selection::CARETPOS);
4503 m_part->selectionLayoutChanged();
4505 // restore old x-position to prevent recalculation
4506 if (!recalcXPos)
4507 m_part->d->editor_context.m_xPosForVerticalArrowNavigation = old_x;
4509 m_part->emitCaretPositionChanged(pos);
4510 // ### check when to emit it
4511 m_part->notifySelectionChanged();
4515 if (handled) _ke->accept();
4516 return handled;
4519 #undef DEBUG_CARETMODE