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"
36 #include <qx11info_x11.h>
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"
53 #define protected public
54 #include "rendering/render_text.h"
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"
69 #include <kglobalsettings.h>
71 #include <kiconloader.h>
73 #include <knotification.h>
74 #include <kdeprintdialog.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
99 #define FIX_QT_BROKEN_QWIDGET_SCROLL
103 #include <X11/Xlib.h>
105 #elif defined(Q_WS_WIN)
111 void dumpLineBoxes(RenderFlow
*flow
);
116 using namespace khtml
;
119 static const int sFirstLayoutDelay
= 760;
120 static const int sParsingLayoutsInterval
= 420;
121 static const int sLayoutAttemptDelay
= 400;
123 static const int sFirstLayoutDelay
= 540;
124 static const int sParsingLayoutsInterval
= 360;
125 static const int sLayoutAttemptDelay
= 340;
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
;
138 enum PseudoFocusNodes
{
144 enum StaticBackgroundState
{
150 enum CompletedState
{
156 KHTMLViewPrivate(KHTMLView
* v
)
157 : underMouse( 0 ), underMouseNonShared( 0 ), oldUnderMouse( 0 )
159 postponed_autorepeat
= NULL
;
160 scrollingFromWheelTimerId
= 0;
161 smoothScrollMode
= KHTMLView::SSMWhenEfficient
;
163 vpolicy
= Qt::ScrollBarAsNeeded
;
164 hpolicy
= Qt::ScrollBarAsNeeded
;
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;
180 delete formCompletions
;
181 delete postponed_autorepeat
;
184 if (underMouseNonShared
)
185 underMouseNonShared
->deref();
187 oldUnderMouse
->deref();
189 delete cursorIconWidget
;
190 delete m_mouseScrollTimer
;
191 delete m_mouseScrollIndicator
;
198 if (underMouseNonShared
)
199 underMouseNonShared
->deref();
200 underMouseNonShared
= 0;
202 oldUnderMouse
->deref();
205 staticWidget
= SBNone
;
206 fixedObjectsCount
= 0;
207 staticObjectsCount
= 0;
208 tabMovePending
= false;
209 lastTabbingDirection
= true;
210 pseudoFocusNode
= PFNone
;
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.
218 vpolicy
= ScrollBarAlwaysOff
;
219 hpolicy
= ScrollBarAlwaysOff
;
226 scrollBarMoved
= false;
227 contentsMoving
= false;
228 ignoreWheelEvents
= false;
229 scrollingFromWheel
= QPoint(-1,-1);
232 dx
= dy
= ddx
= ddy
= rdx
= rdy
= dddx
= dddy
= 0;
237 isDoubleClick
= false;
238 scrollingSelf
= false;
239 delete postponed_autorepeat
;
240 postponed_autorepeat
= NULL
;
244 scrollSuspended
= false;
245 scrollSuspendPreActivate
= false;
246 smoothScrolling
= false;
247 smoothScrollModeIsDefault
= true;
248 shouldSmoothScroll
= 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;
259 firstLayoutPending
= true;
260 firstRepaintPending
= true;
261 needsFullRepaint
= true;
263 layoutSchedulingEnabled
= true;
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.
280 accessKeysEnabled
= KHTMLGlobal::defaultHTMLSettings()->accessKeysEnabled();
281 KHTMLGlobal::deref();
283 emitCompletedAfterRepaint
= CSNone
;
284 m_mouseEventsTarget
= 0;
287 void newScrollTimer(QWidget
*view
, int tid
)
289 //kDebug(6000) << "newScrollTimer timer " << tid;
290 view
->killTimer(scrollTimerId
);
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
))) {
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
) {
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()) {
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 );
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 );
349 smoothScrollTimer
.stop();
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 );
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())
381 QHashIterator
<void*, QWidget
*> it(visibleWidgets
);
382 while (it
.hasNext()) {
384 it
.value()->move( it
.value()->pos() + QPoint(dx
, dy
) );
390 unsigned int pixelbooth
;
391 unsigned int repaintbooth
;
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;
410 bool ignoreWheelEvents
:1;
411 StaticBackgroundState staticWidget
: 3;
412 int staticObjectsCount
;
413 int fixedObjectsCount
;
416 int borderX
, borderY
;
417 int dx
, dy
, ddx
, ddy
, rdx
, rdy
, dddx
, dddy
;
418 KConfig
*formCompletions
;
420 int clickX
, clickY
, clickCount
;
426 int contentsX
, contentsY
;
428 QKeyEvent
* postponed_autorepeat
;
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;
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;
451 bool firstLayoutPending
:1;
452 bool firstRepaintPending
:1;
453 bool layoutSchedulingEnabled
:1;
454 bool needsFullRepaint
:1;
456 bool possibleTripleClick
:1;
458 bool m_dialogsAllowed
:1;
460 int layoutAttemptCounter
;
461 int scheduledLayoutCounter
;
462 QRegion updateRegion
;
463 QTimer smoothScrollTimer
;
464 QHash
<void*, QWidget
*> visibleWidgets
;
465 #ifndef KHTML_NO_TYPE_AHEAD_FIND
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
;
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();
511 if (!rend
|| !rend
->absolutePosition(ax
, ay
))
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();
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
;
542 if ( node
->isElementNode() ) {
543 DOM::ElementImpl
*e
= static_cast<DOM::ElementImpl
*>( node
);
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
);
554 s
= e
->getAttribute( ATTR_TITLE
).string();
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
) );
564 node
= node
->parentNode();
569 case QEvent::DragEnter
:
570 case QEvent::DragMove
:
571 case QEvent::DragLeave
:
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,
581 return QWidget::event(e
);
582 case QEvent::StyleChange
:
583 case QEvent::LayoutRequest
: {
585 return QAbstractScrollArea::event(e
);
587 case QEvent::PaletteChange
:
588 slotPaletteChanged();
589 return QScrollArea::event(e
);
591 return QScrollArea::event(e
);
596 KHTMLView::KHTMLView( KHTMLPart
*part
, QWidget
*parent
)
597 : QScrollArea( parent
), d( new KHTMLViewPrivate( this ) )
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
611 widget()->setMouseTracking(true);
614 KHTMLView::~KHTMLView()
619 DOM::DocumentImpl
*doc
= m_part
->xmlDocImpl();
626 void KHTMLView::setPart(KHTMLPart
*part
)
628 assert(part
&& !m_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
645 installEventFilter(this);
647 setAcceptDrops(true);
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
)
672 if (d
->accessKeysEnabled
&& d
->accessKeysActivated
)
674 viewport()->unsetCursor();
675 if ( d
->cursorIconWidget
)
676 d
->cursorIconWidget
->hide();
677 if (d
->smoothScrolling
)
680 QAbstractEventDispatcher::instance()->unregisterTimers(this);
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
)
738 widget()->resize(w
, h
);
739 if (!widget()->isVisible())
741 #ifdef FIX_QT_BROKEN_QWIDGET_SCROLL
742 if (!horizontalScrollBar()->isVisible() || !verticalScrollBar()->isVisible())
743 d
->brokenQWidgetScroll
= false;
747 int KHTMLView::contentsX() const
752 int KHTMLView::contentsY() const
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
);
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
);
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
816 p
= contentsToViewport(p
);
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
829 p
= viewportToContents(p
);
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
);
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
);
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
869 const int z
= d
->zoomLevel
;
879 void KHTMLView::revertTransforms( int& x
, int& y
, int& w
, int& h
) const
884 const int z
= d
->zoomLevel
;
892 void KHTMLView::revertTransforms( int& x
, int& y
) const
895 revertTransforms(x
, y
, dummy
, dummy
);
898 void KHTMLView::resizeEvent (QResizeEvent
* /*e*/)
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
)
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());
925 QRect
v(contentsX(), contentsY(), visibleWidth(), visibleHeight());
926 QPoint
off(contentsX(),contentsY());
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;
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
);
957 if(!m_part
|| !m_part
->xmlDocImpl() || !m_part
->xmlDocImpl()->renderer()) {
958 p
.fillRect(ex
, ey
, ew
, eh
, palette().brush(QPalette::Active
, QPalette::Base
));
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();
964 } else if (m_part
->xmlDocImpl()->tokenizer()) {
965 m_part
->xmlDocImpl()->tokenizer()->setNormalYeldDelay();
969 kDebug( 6000 ) << "WARNING: paintEvent reentered! ";
970 kDebug( 6000 ) << kBacktrace();
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
);
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
);
995 d
->firstRepaintPending
= false;
998 void KHTMLView::setMarginWidth(int w
)
1000 // make it update the rendering area when set
1004 void KHTMLView::setMarginHeight(int h
)
1006 // make it update the rendering area when set
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();
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();
1063 emit
finishedLayout();
1064 if (d
->firstLayoutPending
) {
1065 // make sure firstLayoutPending is set to false now in case this layout
1067 d
->firstLayoutPending
= false;
1068 verticalScrollBar()->setEnabled( true );
1069 horizontalScrollBar()->setEnabled( true );
1073 if (d
->accessKeysEnabled
&& d
->accessKeysActivated
) {
1074 emit
hideAccessKeys();
1075 displayAccessKeys();
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
);
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)
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();
1114 allowed
&= p
->view()->dialogsAllowed();
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
)
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
)
1149 void KHTMLView::setSmoothScrollingModeDefault( SmoothScrollingMode m
)
1151 // check for manual override
1152 if (!d
->smoothScrollModeIsDefault
)
1154 d
->smoothScrollMode
= m
;
1155 if (d
->smoothScrolling
&& !m
)
1159 KHTMLView::SmoothScrollingMode
KHTMLView::smoothScrollingMode( ) const
1161 return d
->smoothScrollMode
;
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
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 );
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 );
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 );
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
);
1254 d
->m_mouseScrollIndicator
->setCursor( Qt::SizeAllCursor
);
1256 d
->m_mouseScrollIndicator
->setMask( mask
);
1259 if ( hasHorBar
&& !hasVerBar
)
1260 viewport()->setCursor( Qt::SizeHorCursor
);
1261 else if ( !hasHorBar
&& hasVerBar
)
1262 viewport()->setCursor( Qt::SizeVerCursor
);
1264 viewport()->setCursor( Qt::SizeAllCursor
);
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())
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())
1318 else { // shouldn't happen, if Qt has the same criterias for double clicks.
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;
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")
1348 while (part
->parentPart())
1349 part
= part
->parentPart();
1350 if (!part
->frameExists(target
))
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())
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())
1419 khtml::RenderStyle
* style
= (r
&& r
->style()) ? r
->style() : 0;
1421 LinkCursor linkCursor
= LINK_NORMAL
;
1422 switch ( style
? style
->cursor() : 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
;
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());
1440 c
= QCursor(Qt::CrossCursor
);
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
;
1447 if ( targetOpensNewWindow( m_part
, mev
.target
.string() ) )
1448 linkCursor
= LINK_NEWWINDOW
;
1450 case CURSOR_PROGRESS
:
1451 c
= QCursor(Qt::BusyCursor
); // working_cursor
1454 case CURSOR_ALL_SCROLL
:
1455 c
= QCursor(Qt::SizeAllCursor
);
1457 case CURSOR_E_RESIZE
:
1458 case CURSOR_W_RESIZE
:
1459 case CURSOR_EW_RESIZE
:
1460 c
= QCursor(Qt::SizeHorCursor
);
1462 case CURSOR_N_RESIZE
:
1463 case CURSOR_S_RESIZE
:
1464 case CURSOR_NS_RESIZE
:
1465 c
= QCursor(Qt::SizeVerCursor
);
1467 case CURSOR_NE_RESIZE
:
1468 case CURSOR_SW_RESIZE
:
1469 case CURSOR_NESW_RESIZE
:
1470 c
= QCursor(Qt::SizeBDiagCursor
);
1472 case CURSOR_NW_RESIZE
:
1473 case CURSOR_SE_RESIZE
:
1474 case CURSOR_NWSE_RESIZE
:
1475 c
= QCursor(Qt::SizeFDiagCursor
);
1478 c
= QCursor(Qt::IBeamCursor
);
1481 c
= QCursor(Qt::WaitCursor
);
1484 c
= QCursor(Qt::WhatsThisCursor
);
1486 case CURSOR_DEFAULT
:
1489 case CURSOR_NOT_ALLOWED
:
1490 c
= QCursor(Qt::ForbiddenCursor
);
1492 case CURSOR_ROW_RESIZE
:
1493 c
= QCursor(Qt::SplitVCursor
);
1495 case CURSOR_COL_RESIZE
:
1496 c
= QCursor(Qt::SplitHCursor
);
1498 case CURSOR_VERTICAL_TEXT
:
1499 case CURSOR_CONTEXT_MENU
:
1500 case CURSOR_NO_DROP
:
1504 c
= QCursor(Qt::ArrowCursor
);
1508 if (!setCursor
&& style
&& style
->cursor() != CURSOR_AUTO
)
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();
1525 if ( linkCursor
!=LINK_NORMAL
&& isVisible() && hasFocus() ) {
1528 if( !d
->cursorIconWidget
) {
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
);
1535 d
->cursorIconWidget
= new QLabel( NULL
, NULL
);
1540 // Update the pixmap if need be.
1541 if (linkCursor
!= d
->cursorIconType
) {
1542 d
->cursorIconType
= 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 );
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
);
1569 d
->cursorIconWidget
->show();
1572 else if ( d
->cursorIconWidget
)
1573 d
->cursorIconWidget
->hide();
1575 if (r
&& r
->isWidget()) {
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())
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())
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())
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
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
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
;
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
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();
1701 d
->postponed_autorepeat
->ignore();
1707 // returns true if event should be swallowed
1708 bool KHTMLView::dispatchKeyEventHelper( QKeyEvent
*_ke
, bool keypress
)
1710 DOM::NodeImpl
* keyNode
= m_part
->xmlDocImpl()->focusNode();
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())
1737 d
->timer
.setSingleShot(true);
1738 d
->timer
.start(3000);
1742 else if(_ke
->key() == Qt::Key_Escape
)
1749 else if(_ke
->key() == Qt::Key_Space
|| !_ke
->text().trimmed().isEmpty())
1751 d
->findString
+= _ke
->text();
1755 d
->timer
.setSingleShot(true);
1756 d
->timer
.start(3000);
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;
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
);
1786 accessKeysTimeout();
1789 if ( dispatchKeyEvent( _ke
)) {
1790 // If either keydown or keypress was accepted by a widget, or canceled by JS, stop here.
1795 int offs
= (viewport()->height() < 30) ? viewport()->height() : 30; // ### ??
1796 if (_ke
->modifiers() & Qt::ShiftModifier
)
1800 verticalScrollBar()->setValue( verticalScrollBar()->value() -viewport()->height() + offs
);
1801 if(d
->scrollSuspended
)
1802 d
->newScrollTimer(this, 0);
1807 d
->adjustScroller(this, KHTMLViewPrivate::ScrollDown
, KHTMLViewPrivate::ScrollUp
);
1812 d
->adjustScroller(this, KHTMLViewPrivate::ScrollUp
, KHTMLViewPrivate::ScrollDown
);
1817 d
->adjustScroller(this, KHTMLViewPrivate::ScrollLeft
, KHTMLViewPrivate::ScrollRight
);
1822 d
->adjustScroller(this, KHTMLViewPrivate::ScrollRight
, KHTMLViewPrivate::ScrollLeft
);
1826 switch ( _ke
->key() )
1830 if (!d
->scrollTimerId
|| d
->scrollSuspended
)
1831 verticalScrollBar()->setValue( verticalScrollBar()->value()+10 );
1832 if (d
->scrollTimerId
)
1833 d
->newScrollTimer(this, 0);
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);
1846 if (!d
->scrollTimerId
|| d
->scrollSuspended
)
1847 verticalScrollBar()->setValue( verticalScrollBar()->value()-10 );
1848 if (d
->scrollTimerId
)
1849 d
->newScrollTimer(this, 0);
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);
1860 if (!d
->scrollTimerId
|| d
->scrollSuspended
)
1861 horizontalScrollBar()->setValue( horizontalScrollBar()->value()+10 );
1862 if (d
->scrollTimerId
)
1863 d
->newScrollTimer(this, 0);
1868 if (!d
->scrollTimerId
|| d
->scrollSuspended
)
1869 horizontalScrollBar()->setValue( horizontalScrollBar()->value()-10 );
1870 if (d
->scrollTimerId
)
1871 d
->newScrollTimer(this, 0);
1874 case Qt::Key_Return
:
1876 // or even better to HTMLAnchorElementImpl::event()
1877 if (m_part
->xmlDocImpl()) {
1878 NodeImpl
*n
= m_part
->xmlDocImpl()->focusNode();
1884 verticalScrollBar()->setValue( 0 );
1885 horizontalScrollBar()->setValue( 0 );
1886 if(d
->scrollSuspended
)
1887 d
->newScrollTimer(this, 0);
1890 verticalScrollBar()->setValue( contentsHeight() - visibleHeight() );
1891 if(d
->scrollSuspended
)
1892 d
->newScrollTimer(this, 0);
1895 // what are you doing here?
1899 if (d
->scrollTimerId
)
1900 d
->newScrollTimer(this, 0);
1908 void KHTMLView::findTimeout()
1910 #ifndef KHTML_NO_TYPE_AHEAD_FIND
1911 d
->typeAheadActivated
= false;
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
)
1923 d
->findLinksOnly
= true;
1924 m_part
->setStatusBarText(i18n("Starting -- find links as you type"),
1925 KHTMLPart::BarDefaultText
);
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
)
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
));
1957 if(increase
) KNotification::beep();
1958 status
= i18n("Link not found: \"%1\".", Qt::escape(text
));
1963 m_part
->findText(d
->findString
, KHTMLPart::FindNoPopups
, this);
1964 if(m_part
->findTextNext())
1966 status
= i18n("Text found: \"%1\".", Qt::escape(text
));
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
) {
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
)
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;
2020 else if (d
->accessKeysActivated
)
2022 accessKeysTimeout();
2029 if ( dispatchKeyEvent( _ke
) )
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();
2061 KHTMLView
* v
= m_kwp
->isRedirected() ? m_kwp
->rootViewPos(off
) : this;
2062 pos
= v
->viewport()->mapFromGlobal( pos
);
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();
2083 if (innerNode
.handle() && innerNode
.handle()->renderer()
2084 && innerNode
.handle()->renderer()->shouldSelect()) {
2085 m_part
->extendSelectionTo(xm
, ym
, innerNode
);
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)
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
);
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);
2129 QObjectList children
= w
->children();
2130 foreach (QObject
* object
, children
) {
2131 QWidget
*widget
= qobject_cast
<QWidget
*>(object
);
2133 handleWidget(widget
, view
);
2137 class KHTMLBackingStoreHackWidget
: public QWidget
2140 void publicEvent(QEvent
*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
2158 case QEvent::ContextMenu
:
2159 case QEvent::DragEnter
:
2160 case QEvent::DragMove
:
2161 case QEvent::DragLeave
:
2164 case QEvent::Paint
: {
2165 QRect r
= static_cast<QPaintEvent
*>(e
)->rect();
2166 r
.setX(r
.x() +contentsX());
2167 r
.setY(r
.y() +contentsY());
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
);
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);
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() ) {
2223 if ( e
->type() == QEvent::Leave
) {
2224 if ( d
->cursorIconWidget
)
2225 d
->cursorIconWidget
->hide();
2226 m_part
->resetHoverText();
2229 QWidget
*view
= widget();
2233 else if (e
->type() == QEvent::Resize
) {
2237 } else if (o
->isWidgetType()) {
2238 QWidget
*v
= static_cast<QWidget
*>(o
);
2240 while (v
&& v
!= view
) {
2242 v
= v
->parentWidget();
2244 KHTMLWidget
* k
= dynamic_cast<KHTMLWidget
*>(c
);
2245 if (v
&& k
&& k
->m_kwp
->isRedirected()) {
2247 bool isUpdate
= false;
2248 QWidget
*w
= static_cast<QWidget
*>(o
);
2250 case QEvent::UpdateRequest
: {
2251 // implicitly call qt_syncBackingStore(w)
2252 static_cast<KHTMLBackingStoreHackWidget
*>(w
)->publicEvent(e
);
2256 case QEvent::UpdateLater
:
2260 if (!allowWidgetPaintEvents
) {
2261 // eat the event. Like this we can control exactly when the widget
2266 while (v
&& v
->parentWidget() != view
) {
2269 v
= v
->parentWidget();
2272 QPoint ap
= k
->m_kwp
->absolutePos();
2276 QRect pr
= isUpdate
? static_cast<QUpdateLaterEvent
*>(e
)->region().boundingRect() : static_cast<QPaintEvent
*>(e
)->rect();
2277 bool asap
= !d
->contentsMoving
&& qobject_cast
<QAbstractScrollArea
*>(c
);
2280 setInPaintEventFlag(w
, false);
2282 w
->repaint(static_cast<QUpdateLaterEvent
*>(e
)->region());
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
);
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
);
2317 mouseDoubleClickEvent(&me2
);
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
)
2329 keyReleaseEvent(ke
);
2333 case QEvent::FocusIn
:
2334 case QEvent::FocusOut
:
2341 //qDebug("eating event");
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
:
2359 #ifndef QT_NO_WHEELEVENT
2362 case QEvent::ContextMenu
:
2363 case QEvent::DragEnter
:
2364 case QEvent::DragMove
:
2365 case QEvent::DragLeave
:
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()) {
2379 handleWidget(w
, this);
2384 case QEvent::Move
: {
2385 if (static_cast<QMoveEvent
*>(e
)->pos() != QPoint(0,0)) {
2386 widget()->move(0,0);
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
2414 xe
= bounds
.right();
2415 ye
= bounds
.bottom();
2417 //kDebug(6000)<<"scrolling coords: x="<<x<<" y="<<y<<" width="<<xe-x<<" height="<<ye-y;
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
);
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
);
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
) )
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.
2495 if (oldFocusNode
->renderer() && !oldFocusNode
->renderer()->parent()
2496 || !oldFocusNode
->isTabFocusable()) {
2497 doc
->quietResetFocus();
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
)
2510 toFocus
= doc
->nextFocusNode(oldFocusNode
);
2512 toFocus
= doc
->previousFocusNode(oldFocusNode
);
2514 if (!toFocus
&& oldFocusNode
) {
2516 toFocus
= doc
->nextFocusNode(NULL
);
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
));
2545 toFocus
= doc
->nextFocusNode(toFocus
);
2547 toFocus
= doc
->previousFocusNode(toFocus
);
2549 if (!toFocus
&& oldFocusNode
)
2551 toFocus
= doc
->nextFocusNode(NULL
);
2553 toFocus
= doc
->previousFocusNode(NULL
);
2556 d
->scrollBarMoved
= false;
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
;
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
;
2577 if (oldFocusNode
|| d
->pseudoFocusNode
== KHTMLViewPrivate::PFTop
)
2578 newFocusNode
= doc
->nextFocusNode(oldFocusNode
);
2582 if (oldFocusNode
|| d
->pseudoFocusNode
== KHTMLViewPrivate::PFBottom
)
2583 newFocusNode
= doc
->previousFocusNode(oldFocusNode
);
2586 bool targetVisible
= false;
2591 targetVisible
= scrollTo(QRect(contentsX()+visibleWidth()/2,contentsHeight()-d
->borderY
,0,0));
2595 targetVisible
= scrollTo(QRect(contentsX()+visibleWidth()/2,d
->borderY
,0,0));
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);
2607 m_part
->setCaretVisible(false);
2608 kDebug(6200) << "hide caret! fn: " << newFocusNode
->nodeName().string() << endl
;
2610 m_part
->notifySelectionChanged();
2612 targetVisible
= scrollTo(newFocusNode
->getRect());
2617 //kDebug ( 6000 ) << " target reached.\n";
2618 d
->tabMovePending
= false;
2620 m_part
->xmlDocImpl()->setFocusNode(newFocusNode
);
2623 Node
guard(newFocusNode
);
2624 if (!newFocusNode
->hasOneRef() )
2626 emit m_part
->nodeActivated(Node(newFocusNode
));
2632 d
->pseudoFocusNode
= next
?KHTMLViewPrivate::PFBottom
:KHTMLViewPrivate::PFTop
;
2638 if (!d
->tabMovePending
)
2639 d
->lastTabbingDirection
= next
;
2640 d
->tabMovePending
= 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
;
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
);
2662 if( s
.length() == 1 ) {
2663 QChar a
= s
.string()[ 0 ].toUpper();
2664 if( qFind( taken
.begin(), taken
.end(), a
) == taken
.end()) // !contains
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
);
2683 lab
->setParent( widget() );
2684 lab
->setAutoFillBackground(true);
2686 qMin(rec
.left()+rec
.width()/2 - contentsX(), contentsWidth() - lab
->width()),
2687 qMin(rec
.top()+rec
.height()/2 - contentsY(), contentsHeight() - lab
->height()));
2689 taken
.append( accesskey
[ 0 ] );
2696 QList
<KParts::ReadOnlyPart
*> frames
= m_part
->frames();
2697 foreach( KParts::ReadOnlyPart
* cur
, frames
) {
2698 if( !qobject_cast
<KHTMLPart
*>(cur
) )
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
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
;
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 ];
2742 return focusNodeWithAccessKey( c
);
2745 bool KHTMLView::focusNodeWithAccessKey( QChar c
, KHTMLView
* caller
)
2747 DocumentImpl
*doc
= m_part
->xmlDocImpl();
2750 ElementImpl
* node
= doc
->findAccessKeyElement( c
);
2752 QList
<KParts::ReadOnlyPart
*> frames
= m_part
->frames();
2753 foreach( KParts::ReadOnlyPart
* cur
, frames
) {
2754 if( !qobject_cast
<KHTMLPart
*>(cur
) )
2756 KHTMLPart
* part
= static_cast< KHTMLPart
* >( cur
);
2757 if( part
->view() && part
->view() != caller
2758 && part
->view()->focusNodeWithAccessKey( c
, this ))
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 ))
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();
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());
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;
2794 // Set focus node on the document
2796 #warning "port QFocusEvent::setReason( QFocusEvent::Shortcut ); to qt4"
2798 //QFocusEvent::setReason( QFocusEvent::Shortcut );
2799 m_part
->xmlDocImpl()->setFocusNode(node
);
2801 #warning "port QFocusEvent::resetReason(); to qt4"
2803 //QFocusEvent::resetReason();
2804 if( node
!= NULL
&& node
->hasOneRef()) // deleted, only held by guard
2806 emit m_part
->nodeActivated(Node(node
));
2807 if( node
!= NULL
&& node
->hasOneRef())
2811 switch( node
->id()) {
2813 static_cast< HTMLAnchorElementImpl
* >( node
)->click();
2816 static_cast< HTMLInputElementImpl
* >( node
)->click();
2819 static_cast< HTMLButtonElementImpl
* >( node
)->click();
2822 static_cast< HTMLAreaElementImpl
* >( node
)->click();
2825 break; // just focusing it is enough
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();
2838 n
= after
? n
->traverseNextNode() : n
->traversePreviousNode()) {
2839 if( n
->isTextNode()) {
2841 ret
+= static_cast< TextImpl
* >( n
)->toString().string();
2843 ret
.prepend( static_cast< TextImpl
* >( n
)->toString().string());
2873 if( ret
.trimmed().isEmpty())
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
;
2889 n
= n
->traverseNextNode()) {
2890 if( n
->id() == ID_LABEL
) {
2891 HTMLLabelElementImpl
* label
= static_cast< HTMLLabelElementImpl
* >( n
);
2892 NodeImpl
* labelfor
= label
->getFormElement();
2894 ret
[ labelfor
] = label
->innerText().string().simplified();
2901 struct AccessKeyData
{
2902 ElementImpl
* element
;
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();
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
2927 bool ignore
= false;
2928 bool text_after
= false;
2929 bool text_before
= false;
2930 switch( element
->id()) {
2932 url
= khtml::parseURL(element
->getAttribute(ATTR_HREF
)).string();
2933 if( url
.isEmpty()) // doesn't have href, it's only an anchor
2935 text
= static_cast< HTMLElementImpl
* >( element
)->innerText().string().simplified();
2939 HTMLInputElementImpl
* in
= static_cast< HTMLInputElementImpl
* >( element
);
2940 switch( in
->inputType()) {
2941 case HTMLInputElementImpl::SUBMIT
:
2942 text
= in
->value().string();
2944 text
= i18n( "Submit" );
2947 case HTMLInputElementImpl::IMAGE
:
2948 text
= in
->altText().string();
2951 case HTMLInputElementImpl::BUTTON
:
2952 text
= in
->value().string();
2955 case HTMLInputElementImpl::RESET
:
2956 text
= in
->value().string();
2958 text
= i18n( "Reset" );
2961 case HTMLInputElementImpl::HIDDEN
:
2964 case HTMLInputElementImpl::CHECKBOX
:
2965 case HTMLInputElementImpl::RADIO
:
2969 case HTMLInputElementImpl::TEXT
:
2970 case HTMLInputElementImpl::PASSWORD
:
2971 case HTMLInputElementImpl::FILE:
2982 text
= static_cast< HTMLElementImpl
* >( element
)->innerText().string().simplified();
2983 switch( static_cast< HTMLButtonElementImpl
* >( element
)->buttonType()) {
2984 case HTMLButtonElementImpl::SUBMIT
:
2986 text
= i18n( "Submit" );
2989 case HTMLButtonElementImpl::RESET
:
2991 text
= i18n( "Reset" );
2999 case ID_SELECT
: // these don't have accesskey attribute, but quick access may be handy
3008 ignore
= !element
->isFocusable();
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();
3027 if( text
== (*it
).first
)
3030 AccessKeyData tmp
= { element
, text
, url
, priority
};
3035 QList
< QChar
> keys
;
3036 for( char c
= 'A'; c
<= 'Z'; ++c
)
3038 for( char c
= '0'; c
<= '9'; ++c
)
3040 for( NodeImpl
* n
= m_part
->xmlDocImpl();
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();
3058 if( (*it
).priority
!= priority
) {
3064 QString text
= (*it
).text
;
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();
3072 if( text
== (*it
).first
&& keys
.contains( (*it
).second
)) {
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();
3085 if( keys
.contains( (*it
)[ 0 ].toUpper())) {
3086 key
= (*it
)[ 0 ].toUpper();
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();
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();
3110 if( (*it2
).url
== url
) {
3111 ret
[ (*it2
).element
] = key
;
3114 it2
= data
.erase( it2
);
3124 void KHTMLView::setMediaType( const QString
&medium
)
3129 QString
KHTMLView::mediaType() const
3134 bool KHTMLView::pagedMode() const
3139 void KHTMLView::setWidgetVisible(RenderWidget
* w
, bool vis
)
3142 d
->visibleWidgets
.insert(w
, w
->widget());
3145 d
->visibleWidgets
.remove(w
);
3148 bool KHTMLView::needsFullRepaint() const
3150 return d
->needsFullRepaint
;
3154 class QPointerDeleter
3157 explicit QPointerDeleter(QObject
* o
) : obj(o
) {}
3158 ~QPointerDeleter() { delete obj
; }
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());
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
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
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);
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();
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
;
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
3251 int pageWidth
= printer
.width();
3252 int pageHeight
= printer
.height();
3253 p
->setClipRect(0,0, pageWidth
, pageHeight
);
3255 pageHeight
-= headerHeight
;
3257 bool scalePage
= false;
3259 #ifndef QT_NO_TRANSFORMATIONS
3260 if(root
->docWidth() > printer
.width()) {
3262 scale
= ((double) printer
.width())/((double) root
->docWidth());
3263 pageHeight
= (int) (pageHeight
/scale
);
3264 pageWidth
= (int) (pageWidth
/scale
);
3265 headerHeight
= (int) (headerHeight
/scale
);
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.
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;
3288 headerMid
= KStringHandler::csqueeze(docname
, squeeze
);
3289 mid_width
= p
->boundingRect(0, 0, printer
.width(), p
->fontMetrics().lineSpacing(), Qt::AlignLeft
, headerMid
).width();
3291 } while (mid_width
> available_width
);
3297 while(top
< root
->docHeight()) {
3298 if(top
> 0) printer
.newPage();
3300 p
->setClipRect(0, 0, pageWidth
, headerHeight
);
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
3316 p
->scale(scale
, scale
);
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
;
3331 p
->resetTransform();
3338 // and now reset the layout to the usual one...
3339 root
->setPagedMode(false);
3340 root
->setStaticMode(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());
3358 root
->style()->resetPalette();
3359 NodeImpl
*body
= static_cast<HTMLDocumentImpl
*>(document
)->body();
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());
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());
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();
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
);
3393 root
->setPageTop(yOff
);
3394 root
->setPageBottom(yOff
+height
);
3396 root
->layer()->paint(p
, QRect(0, yOff
, root
->docWidth(), height
));
3398 *more
= yOff
+ height
< root
->docHeight();
3401 p
->setWorldTransform(t
);
3404 p
->setViewTransformEnabled( vte
);
3405 p
->setWorldMatrixEnabled( wme
);
3406 if (!creg
.isEmpty())
3407 p
->setClipRegion( creg
);
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
));
3424 QPaintDevice
* opd
= m_part
->xmlDocImpl()->paintDevice();
3425 m_part
->xmlDocImpl()->setPaintDevice(p
->device());
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
);
3442 p
->setWorldTransform(t
);
3445 p
->setViewTransformEnabled( vte
);
3446 p
->setWorldMatrixEnabled( wme
);
3447 if (!creg
.isEmpty())
3448 p
->setClipRegion( creg
);
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())
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())
3471 d
->staticWidget
= KHTMLViewPrivate::SBNone
;
3474 void KHTMLView::addStaticObject(bool fixed
)
3477 d
->fixedObjectsCount
++;
3479 d
->staticObjectsCount
++;
3481 setHasStaticBackground( true /*partial*/ );
3484 void KHTMLView::removeStaticObject(bool fixed
)
3487 d
->fixedObjectsCount
--;
3489 d
->staticObjectsCount
--;
3491 assert( d
->fixedObjectsCount
>= 0 && d
->staticObjectsCount
>= 0 );
3493 if (!d
->staticObjectsCount
&& !d
->fixedObjectsCount
)
3494 setHasNormalBackground();
3496 setHasStaticBackground( true /*partial*/ );
3499 void KHTMLView::setVerticalScrollBarPolicy( Qt::ScrollBarPolicy policy
)
3501 #ifndef KHTML_NO_SCROLLBARS
3502 d
->vpolicy
= policy
;
3503 QScrollArea::setVerticalScrollBarPolicy(policy
);
3509 void KHTMLView::setHorizontalScrollBarPolicy( Qt::ScrollBarPolicy policy
)
3511 #ifndef KHTML_NO_SCROLLBARS
3512 d
->hpolicy
= policy
;
3513 QScrollArea::setHorizontalScrollBarPolicy(policy
);
3519 void KHTMLView::restoreScrollBar()
3521 int ow
= visibleWidth();
3522 QScrollArea::setVerticalScrollBarPolicy(d
->vpolicy
);
3523 if (visibleWidth() != ow
)
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())
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
)
3558 if (!c
.isNumber() && c
!= '-' && !c
.isSpace())
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());
3583 cg
.writeEntry("Sites", sites
);
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();
3607 d
->underMouse
->deref();
3608 d
->underMouse
= targetNode
;
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();
3629 switch (_mouse
->button()) {
3630 case Qt::LeftButton
:
3636 case Qt::RightButton
:
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
,
3665 d
->oldUnderMouse
->dispatchEvent(me
,exceptioncode
,true);
3668 // send mouseover event to the new node
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
);
3677 targetNode
->dispatchEvent(me
,exceptioncode
,true);
3680 if (d
->oldUnderMouse
)
3681 d
->oldUnderMouse
->deref();
3682 d
->oldUnderMouse
= targetNode
;
3683 if (d
->oldUnderMouse
)
3684 d
->oldUnderMouse
->ref();
3687 bool swallowEvent
= false;
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())
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
);
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;
3723 targetNode
->dispatchEvent(me
,exceptioncode
,true);
3724 bool defaultHandled
= me
->defaultHandled();
3725 if (defaultHandled
|| me
->defaultPrevented())
3726 swallowEvent
= true;
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() );
3769 else if (d
->firstLayoutPending
)
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
);
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
);
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()) ) {
3829 QScrollArea::wheelEvent( e
);
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 );
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
)
3865 m_part
->stopAutoScroll();
3866 m_part
->setSelectionVisible(false);
3869 #ifndef KHTML_NO_TYPE_AHEAD_FIND
3870 if(d
->typeAheadActivated
)
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();
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
);
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())
3938 if (!d
->smoothScrolling
) {
3939 d
->updateContentsXY();
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();
3952 if (m_kwp
->isRedirected()) {
3953 // This is a redirected sub frame. Translate to root view context
3954 KHTMLView
* v
= m_kwp
->rootViewPos( off
);
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;
3966 hideScrollBars
= true;
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();
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;
3994 for (int i
= 0; i
< ar
.size() ; ++i
) {
3995 widget()->update( ar
[i
] );
3997 r
= QRegion(QRect(0, 0, visibleWidth(), visibleHeight())) - r
;
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();
4008 d
->scrollExternalWidgets(dx
, dy
);
4010 #ifdef FIX_QT_BROKEN_QWIDGET_SCROLL
4011 d
->shouldBeBlitting
= false;
4013 // we can't avoid a full update
4019 #ifdef FIX_QT_BROKEN_QWIDGET_SCROLL
4020 if (hideScrollBars
) {
4021 horizontalScrollBar()->parentWidget()->lower();
4022 verticalScrollBar()->parentWidget()->lower();
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
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();
4043 d
->scrollExternalWidgets(dx
, dy
);
4046 void KHTMLView::setupSmoothScrolling(int dx
, int dy
)
4048 // full scroll is remaining scroll plus new scroll
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
4077 // deacceleration speed
4078 d
->dddx
= (d
->ddx
+1)/steps
;
4079 d
->dddy
= (d
->ddy
+1)/steps
;
4081 if (!d
->smoothScrolling
) {
4082 d
->startScrolling();
4087 void KHTMLView::scrollTick() {
4088 if (d
->dx
== 0 && d
->dy
== 0) {
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)
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
4122 d
->shouldSmoothScroll
= false;
4123 scrollContentsBy(ddx
, ddy
);
4125 // update scrolling speed
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
;
4137 void KHTMLView::addChild(QWidget
* child
, int x
, int y
)
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
)
4156 switch (d
->scrollDirection
) {
4157 case KHTMLViewPrivate::ScrollDown
:
4158 if (contentsY() + visibleHeight () >= contentsHeight())
4159 d
->newScrollTimer(this, 0);
4161 verticalScrollBar()->setValue( verticalScrollBar()->value() +d
->scrollBy
);
4163 case KHTMLViewPrivate::ScrollUp
:
4164 if (contentsY() <= 0)
4165 d
->newScrollTimer(this, 0);
4167 verticalScrollBar()->setValue( verticalScrollBar()->value() -d
->scrollBy
);
4169 case KHTMLViewPrivate::ScrollRight
:
4170 if (contentsX() + visibleWidth () >= contentsWidth())
4171 d
->newScrollTimer(this, 0);
4173 horizontalScrollBar()->setValue( horizontalScrollBar()->value() +d
->scrollBy
);
4175 case KHTMLViewPrivate::ScrollLeft
:
4176 if (contentsX() <= 0)
4177 d
->newScrollTimer(this, 0);
4179 horizontalScrollBar()->setValue( horizontalScrollBar()->value() -d
->scrollBy
);
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;
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;
4219 if (d
->repaintTimerId
)
4220 killTimer(d
->repaintTimerId
);
4221 d
->repaintTimerId
= 0;
4224 const QVector
<QRect
> rects
= d
->updateRegion
.rects();
4226 d
->updateRegion
= QRegion();
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
];
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
;
4260 emit m_part
->completed();
4262 emit m_part
->completed(true);
4266 void KHTMLView::checkExternalWidgetsPosition()
4269 QRect
visibleRect(contentsX(), contentsY(), visibleWidth(), visibleHeight());
4270 QList
<RenderWidget
*> toRemove
;
4271 QHashIterator
<void*, QWidget
*> it(d
->visibleWidgets
);
4272 while (it
.hasNext()) {
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
)
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
)
4310 killTimer(d
->layoutTimerId
);
4311 d
->layoutTimerId
= 0;
4314 void KHTMLView::unscheduleRepaint()
4316 if (!d
->repaintTimerId
)
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
4334 p
.begin( viewport() );
4337 contentsToViewport( x
, y
, vx
, vy
);
4338 p
.fillRect( vx
, vy
, w
, h
, Qt::red
);
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()";
4359 // is there a relayout pending?
4360 if (d
->layoutTimerId
)
4362 // kDebug() << "requesting relayout 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";
4375 killTimer(d
->repaintTimerId
);
4376 d
->repaintTimerId
= startTimer( 0 );
4377 d
->emitCompletedAfterRepaint
= pendingAction
?
4378 KHTMLViewPrivate::CSActionPending
: KHTMLViewPrivate::CSFull
;
4381 if (!d
->emitCompletedAfterRepaint
)
4384 emit m_part
->completed();
4386 emit m_part
->completed(true);
4391 void KHTMLView::updateScrollBars()
4393 const QWidget
*view
= widget();
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
4453 pos
= old_pos
.nextLinePosition(caret
.xPosForVerticalArrowNavigation(Selection::EXTENT
));
4458 pos
= old_pos
.previousLinePosition(caret
.xPosForVerticalArrowNavigation(Selection::EXTENT
));
4463 pos
= ctrl
? old_pos
.previousWordPosition() : old_pos
.previousCharacterPosition();
4467 pos
= ctrl
? old_pos
.nextWordPosition() : old_pos
.nextCharacterPosition();
4470 case Qt::Key_PageDown
:
4471 // moveCaretNextPage(); ###
4474 case Qt::Key_PageUp
:
4475 // moveCaretPrevPage(); ###
4480 /*moveCaretToDocumentBoundary(false)*/; // ###
4482 pos
= positionOfLineBegin(old_pos
);
4487 /*moveCaretToDocumentBoundary(true)*/; // ###
4489 pos
= positionOfLineEnd(old_pos
);
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
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();
4519 #undef DEBUG_CARETMODE