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-2004 Apple Computer, Inc.
9 * 2006 Germain Garand <germain@ebooksfrance.org>
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Library General Public
13 * License as published by the Free Software Foundation; either
14 * version 2 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Library General Public License for more details.
21 * You should have received a copy of the GNU Library General Public License
22 * along with this library; see the file COPYING.LIB. If not, write to
23 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24 * Boston, MA 02110-1301, USA.
28 #include "khtmlview.moc"
30 #include "khtmlview.h"
32 #include "khtml_part.h"
33 #include "khtml_events.h"
35 #include <qx11info_x11.h>
38 #include "html/html_documentimpl.h"
39 #include "html/html_inlineimpl.h"
40 #include "html/html_formimpl.h"
41 #include "rendering/render_arena.h"
42 #include "rendering/render_canvas.h"
43 #include "rendering/render_frames.h"
44 #include "rendering/render_replaced.h"
45 #include "rendering/render_form.h"
46 #include "rendering/render_layer.h"
47 #include "rendering/render_line.h"
48 #include "rendering/render_table.h"
50 #define protected public
51 #include "rendering/render_text.h"
53 #include "xml/dom2_eventsimpl.h"
54 #include "css/cssstyleselector.h"
55 #include "css/csshelper.h"
56 #include "misc/htmlhashes.h"
57 #include "misc/helper.h"
58 #include "misc/loader.h"
59 #include "khtml_settings.h"
60 #include "khtml_printsettings.h"
62 #include "khtmlpart_p.h"
64 #ifndef KHTML_NO_CARET
65 #include "khtml_caret_p.h"
66 #include "xml/dom2_rangeimpl.h"
69 #include <kapplication.h>
72 #include <kglobalsettings.h>
74 #include <kiconloader.h>
76 #include <knotification.h>
78 #include <ksimpleconfig.h>
79 #include <kstandarddirs.h>
80 #include <kstandardshortcut.h>
81 #include <kstringhandler.h>
82 #include <kconfiggroup.h>
87 #include <q3paintdevicemetrics.h>
89 #include <q3ptrdict.h>
92 #include <QTextDocument>
94 #include <QAbstractEventDispatcher>
97 #include <QAbstractScrollArea>
99 //#define DEBUG_FLICKER
101 //#define DEBUG_PIXEL
104 #include <X11/Xlib.h>
106 #elif defined(Q_WS_WIN)
112 void dumpLineBoxes(RenderFlow
*flow
);
117 using namespace khtml
;
122 class KHTMLViewPrivate
{
123 friend class KHTMLView
;
126 enum PseudoFocusNodes
{
132 enum CompletedState
{
139 : underMouse( 0 ), underMouseNonShared( 0 ), oldUnderMouse( 0 ), visibleWidgets( 107 )
141 #ifndef KHTML_NO_CARET
142 m_caretViewContext
= 0;
144 #endif // KHTML_NO_CARET
145 postponed_autorepeat
= NULL
;
147 vpolicy
= Qt::ScrollBarAsNeeded
;
148 hpolicy
= Qt::ScrollBarAsNeeded
;
150 prevScrollbarVisible
= true;
152 possibleTripleClick
= false;
153 emitCompletedAfterRepaint
= CSNone
;
154 cursor_icon_widget
= NULL
;
155 m_mouseScrollTimer
= 0;
156 m_mouseScrollIndicator
= 0;
160 delete formCompletions
;
161 delete postponed_autorepeat
;
164 if (underMouseNonShared
)
165 underMouseNonShared
->deref();
167 oldUnderMouse
->deref();
169 #ifndef KHTML_NO_CARET
170 delete m_caretViewContext
;
171 delete m_editorContext
;
172 #endif // KHTML_NO_CARET
173 delete cursor_icon_widget
;
174 delete m_mouseScrollTimer
;
175 delete m_mouseScrollIndicator
;
182 if (underMouseNonShared
)
183 underMouseNonShared
->deref();
184 underMouseNonShared
= 0;
186 oldUnderMouse
->deref();
189 useSlowRepaints
= false;
190 tabMovePending
= false;
191 lastTabbingDirection
= true;
192 pseudoFocusNode
= PFNone
;
193 #ifndef KHTML_NO_SCROLLBARS
194 //We don't turn off the toolbars here
195 //since if the user turns them
196 //off, then chances are they want them turned
197 //off always - even after a reset.
199 vpolicy
= ScrollBarAlwaysOff
;
200 hpolicy
= ScrollBarAlwaysOff
;
207 scrollBarMoved
= false;
208 contentsMoving
= false;
209 ignoreWheelEvents
= false;
218 isDoubleClick
= false;
219 scrollingSelf
= false;
220 delete postponed_autorepeat
;
221 postponed_autorepeat
= NULL
;
225 scrollSuspended
= false;
226 scrollSuspendPreActivate
= false;
228 firstRelayout
= true;
229 needsFullRepaint
= true;
231 layoutSchedulingEnabled
= true;
233 updateRegion
= QRegion();
234 m_dialogsAllowed
= true;
235 #ifndef KHTML_NO_CARET
236 if (m_caretViewContext
) {
237 m_caretViewContext
->caretMoved
= false;
238 m_caretViewContext
->keyReleasePending
= false;
240 #endif // KHTML_NO_CARET
241 #ifndef KHTML_NO_TYPE_AHEAD_FIND
242 typeAheadActivated
= false;
243 #endif // KHTML_NO_TYPE_AHEAD_FIND
244 accessKeysActivated
= false;
245 accessKeysPreActivate
= false;
247 // We ref/deref to ensure defaultHTMLSettings is available
249 accessKeysEnabled
= KHTMLFactory::defaultHTMLSettings()->accessKeysEnabled();
250 KHTMLFactory::deref();
252 emitCompletedAfterRepaint
= CSNone
;
253 m_mouseEventsTarget
= 0;
255 void newScrollTimer(QWidget
*view
, int tid
)
257 //kDebug(6000) << "newScrollTimer timer " << tid << endl;
258 view
->killTimer(scrollTimerId
);
260 scrollSuspended
= false;
262 enum ScrollDirection
{ ScrollLeft
, ScrollRight
, ScrollUp
, ScrollDown
};
264 void adjustScroller(QWidget
*view
, ScrollDirection direction
, ScrollDirection oppositedir
)
266 static const struct { int msec
, pixels
; } timings
[] = {
267 {320,1}, {224,1}, {160,1}, {112,1}, {80,1}, {56,1}, {40,1},
268 {28,1}, {20,1}, {20,2}, {20,3}, {20,4}, {20,6}, {20,8}, {0,0}
270 if (!scrollTimerId
||
271 (static_cast<int>(scrollDirection
) != direction
&&
272 (static_cast<int>(scrollDirection
) != oppositedir
|| scrollSuspended
))) {
274 scrollBy
= timings
[scrollTiming
].pixels
;
275 scrollDirection
= direction
;
276 newScrollTimer(view
, view
->startTimer(timings
[scrollTiming
].msec
));
277 } else if (scrollDirection
== direction
&&
278 timings
[scrollTiming
+1].msec
&& !scrollSuspended
) {
279 scrollBy
= timings
[++scrollTiming
].pixels
;
280 newScrollTimer(view
, view
->startTimer(timings
[scrollTiming
].msec
));
281 } else if (scrollDirection
== oppositedir
) {
283 scrollBy
= timings
[--scrollTiming
].pixels
;
284 newScrollTimer(view
, view
->startTimer(timings
[scrollTiming
].msec
));
287 scrollSuspended
= false;
290 #ifndef KHTML_NO_CARET
291 /** this function returns an instance of the caret view context. If none
292 * exists, it will be instantiated.
294 CaretViewContext
*caretViewContext() {
295 if (!m_caretViewContext
) m_caretViewContext
= new CaretViewContext();
296 return m_caretViewContext
;
298 /** this function returns an instance of the editor context. If none
299 * exists, it will be instantiated.
301 EditorContext
*editorContext() {
302 if (!m_editorContext
) m_editorContext
= new EditorContext();
303 return m_editorContext
;
305 #endif // KHTML_NO_CARET
309 unsigned int pixelbooth
;
310 unsigned int repaintbooth
;
313 NodeImpl
*underMouse
;
314 NodeImpl
*underMouseNonShared
;
315 NodeImpl
*oldUnderMouse
;
317 bool tabMovePending
:1;
318 bool lastTabbingDirection
:1;
319 PseudoFocusNodes pseudoFocusNode
:2;
320 bool scrollBarMoved
:1;
321 bool contentsMoving
:1;
323 Qt::ScrollBarPolicy vpolicy
;
324 Qt::ScrollBarPolicy hpolicy
;
325 bool prevScrollbarVisible
:1;
327 bool useSlowRepaints
:1;
328 bool ignoreWheelEvents
:1;
330 int borderX
, borderY
;
331 KSimpleConfig
*formCompletions
;
335 int clickX
, clickY
, clickCount
;
338 int contentsX
, contentsY
;
341 QKeyEvent
* postponed_autorepeat
;
347 ScrollDirection scrollDirection
:2;
348 bool scrollSuspended
:1;
349 bool scrollSuspendPreActivate
:1;
351 bool firstRelayout
:1;
352 bool layoutSchedulingEnabled
:1;
353 bool needsFullRepaint
:1;
355 bool possibleTripleClick
:1;
357 bool m_dialogsAllowed
:1;
358 QRegion updateRegion
;
359 Q3PtrDict
<QWidget
> visibleWidgets
;
360 #ifndef KHTML_NO_CARET
361 CaretViewContext
*m_caretViewContext
;
362 EditorContext
*m_editorContext
;
363 #endif // KHTML_NO_CARET
364 #ifndef KHTML_NO_TYPE_AHEAD_FIND
368 bool typeAheadActivated
;
369 #endif // KHTML_NO_TYPE_AHEAD_FIND
370 bool accessKeysEnabled
;
371 bool accessKeysActivated
;
372 bool accessKeysPreActivate
;
373 CompletedState emitCompletedAfterRepaint
;
375 QWidget
* cursor_icon_widget
;
377 // scrolling activated by MMB
378 short m_mouseScroll_byX
;
379 short m_mouseScroll_byY
;
380 QTimer
*m_mouseScrollTimer
;
381 QWidget
*m_mouseScrollIndicator
;
382 QPointer
<QWidget
> m_mouseEventsTarget
;
385 #ifndef QT_NO_TOOLTIP
387 /** calculates the client-side image map rectangle for the given image element
388 * @param img image element
389 * @param scrollOfs scroll offset of viewport in content coordinates
390 * @param p position to be probed in viewport coordinates
391 * @param r returns the bounding rectangle in content coordinates
392 * @param s returns the title string
393 * @return true if an appropriate area was found -- only in this case r and
394 * s are valid, false otherwise
396 static bool findImageMapRect(HTMLImageElementImpl
*img
, const QPoint
&scrollOfs
,
397 const QPoint
&p
, QRect
&r
, QString
&s
)
399 HTMLMapElementImpl
* map
;
400 if (img
&& img
->getDocument()->isHTMLDocument() &&
401 (map
= static_cast<HTMLDocumentImpl
*>(img
->getDocument())->getMap(img
->imageMap()))) {
402 RenderObject::NodeInfo
info(true, false);
403 RenderObject
*rend
= img
->renderer();
405 if (!rend
|| !rend
->absolutePosition(ax
, ay
))
407 // we're a client side image map
408 bool inside
= map
->mapMouseEvent(p
.x() - ax
+ scrollOfs
.x(),
409 p
.y() - ay
+ scrollOfs
.y(), rend
->contentWidth(),
410 rend
->contentHeight(), info
);
411 if (inside
&& info
.URLElement()) {
412 HTMLAreaElementImpl
*area
= static_cast<HTMLAreaElementImpl
*>(info
.URLElement());
413 Q_ASSERT(area
->id() == ID_AREA
);
414 s
= area
->getAttribute(ATTR_TITLE
).string();
415 QRegion reg
= area
->cachedRegion();
416 if (!s
.isEmpty() && !reg
.isEmpty()) {
417 r
= reg
.boundingRect();
426 bool KHTMLView::event( QEvent
* e
)
428 if ( e
->type() == QEvent::ToolTip
) {
429 QHelpEvent
*he
= static_cast<QHelpEvent
*>(e
);
430 QPoint p
= he
->pos();
432 DOM::NodeImpl
*node
= d
->underMouseNonShared
;
435 if ( node
->isElementNode() ) {
436 DOM::ElementImpl
*e
= static_cast<DOM::ElementImpl
*>( node
);
440 // for images, check if it is part of a client-side image map,
441 // and query the <area>s' title attributes, too
442 if (e
->id() == ID_IMG
&& !e
->getAttribute( ATTR_USEMAP
).isEmpty()) {
443 found
= findImageMapRect(static_cast<HTMLImageElementImpl
*>(e
),
444 viewportToContents(QPoint(0, 0)), p
, r
, s
);
447 s
= e
->getAttribute( ATTR_TITLE
).string();
450 region
|= QRect( contentsToViewport( r
.topLeft() ), r
.size() );
451 if ( !s
.isEmpty() ) {
452 QToolTip::showText( viewport()->mapToGlobal(region
.bottomLeft()),
453 Qt::convertFromPlainText( s
, Qt::WhiteSpaceNormal
) );
457 node
= node
->parentNode();
461 return QScrollArea::event(e
);
465 KHTMLView::KHTMLView( KHTMLPart
*part
, QWidget
*parent
)
466 : QScrollArea( parent
), d( new KHTMLViewPrivate
)
471 QScrollArea::setVerticalScrollBarPolicy(d
->vpolicy
);
472 QScrollArea::setHorizontalScrollBarPolicy(d
->hpolicy
);
473 connect(KGlobalSettings::self(), SIGNAL(kdisplayPaletteChanged()), this, SLOT(slotPaletteChanged()));
475 #ifndef KHTML_NO_TYPE_AHEAD_FIND
476 connect(&d
->timer
, SIGNAL(timeout()), this, SLOT(findTimeout()));
477 #endif // KHTML_NO_TYPE_AHEAD_FIND
480 widget()->setMouseTracking(true);
483 KHTMLView::~KHTMLView()
488 DOM::DocumentImpl
*doc
= m_part
->xmlDocImpl();
495 void KHTMLView::init()
497 setFocusPolicy(Qt::StrongFocus
);
498 viewport()->setFocusProxy(this);
500 _marginWidth
= -1; // undefined
505 installEventFilter(this);
507 setAcceptDrops(true);
509 setWidget( new QWidget(this) );
510 QSize s
= viewport()->size();
511 resizeContents(s
.width(), s
.height());
513 // ### we'll enable redirection of khtmlview here
514 // when event and painting issues have been thoroughly worked out
515 // m_kwp->setIsRedirected(true);
518 void KHTMLView::clear()
520 #ifndef KHTML_NO_CARET
521 if (!m_part
->isCaretMode() && !m_part
->isEditable()) caretOff();
523 #ifndef KHTML_NO_TYPE_AHEAD_FIND
524 if( d
->typeAheadActivated
)
527 if (d
->accessKeysEnabled
&& d
->accessKeysActivated
)
529 viewport()->unsetCursor();
530 if ( d
->cursor_icon_widget
)
531 d
->cursor_icon_widget
->hide();
533 QAbstractEventDispatcher::instance()->unregisterTimers(this);
536 QScrollArea::setHorizontalScrollBarPolicy(d
->hpolicy
);
537 QScrollArea::setVerticalScrollBarPolicy(d
->vpolicy
);
538 verticalScrollBar()->setEnabled( false );
539 horizontalScrollBar()->setEnabled( false );
542 void KHTMLView::hideEvent(QHideEvent
* e
)
544 QScrollArea::hideEvent(e
);
545 if ( m_part
&& m_part
->xmlDocImpl() )
546 m_part
->xmlDocImpl()->docLoader()->pauseAnimations();
549 void KHTMLView::showEvent(QShowEvent
* e
)
551 QScrollArea::showEvent(e
);
552 if ( m_part
&& m_part
->xmlDocImpl() )
553 m_part
->xmlDocImpl()->docLoader()->resumeAnimations();
556 void KHTMLView::setMouseEventsTarget( QWidget
* w
)
558 d
->m_mouseEventsTarget
= w
;
561 QWidget
* KHTMLView::mouseEventsTarget() const
563 return d
->m_mouseEventsTarget
;
566 int KHTMLView::contentsWidth() const
568 return widget() ? widget()->width() : 0;
571 int KHTMLView::contentsHeight() const
573 return widget() ? widget()->height() : 0;
576 void KHTMLView::resizeContents(int w
, int h
)
580 widget()->resize(w
, h
);
583 int KHTMLView::contentsX() const
588 int KHTMLView::contentsY() const
593 int KHTMLView::visibleWidth() const
595 return viewport()->width();
598 int KHTMLView::visibleHeight() const
600 return viewport()->height();
603 void KHTMLView::setContentsPos( int x
, int y
)
605 horizontalScrollBar()->setValue( x
);
606 verticalScrollBar()->setValue( y
);
609 void KHTMLView::scrollBy(int x
, int y
)
611 horizontalScrollBar()->setValue( horizontalScrollBar()->value()+x
);
612 verticalScrollBar()->setValue( verticalScrollBar()->value()+y
);
615 QPoint
KHTMLView::contentsToViewport(const QPoint
& p
) const
617 return QPoint(p
.x()-contentsX(), p
.y()-contentsY());
620 void KHTMLView::contentsToViewport(int x
, int y
, int& cx
, int& cy
) const
623 p
= contentsToViewport(p
);
628 QPoint
KHTMLView::viewportToContents(const QPoint
& p
) const
630 return QPoint(p
.x()+contentsX(), p
.y()+contentsY());
633 void KHTMLView::viewportToContents(int x
, int y
, int& cx
, int& cy
) const
636 p
= viewportToContents(p
);
641 void KHTMLView::updateContents(int x
, int y
, int w
, int h
)
643 widget()->update(x
, y
, w
, h
);
646 void KHTMLView::updateContents( const QRect
& r
)
648 updateContents( r
.x(), r
.y(), r
.width(), r
.height() );
651 void KHTMLView::repaintContents(int x
, int y
, int w
, int h
)
653 widget()->repaint(x
, y
, w
, h
);
656 void KHTMLView::repaintContents( const QRect
& r
)
658 repaintContents( r
.x(), r
.y(), r
.width(), r
.height() );
661 void KHTMLView::resizeEvent (QResizeEvent
* e
)
663 int dw
= e
->oldSize().width() - e
->size().width();
664 int dh
= e
->oldSize().height() - e
->size().height();
666 // if we are shrinking the view, don't allow the content to overflow
667 // before the layout occurs - we don't know if we need scrollbars yet
668 dw
= dw
>0 ? qMax(0, contentsWidth()-dw
) : contentsWidth();
669 dh
= dh
>0 ? qMax(0, contentsHeight()-dh
) : contentsHeight();
671 resizeContents(dw
, dh
);
673 if (d
->layoutSchedulingEnabled
)
675 #ifndef KHTML_NO_CARET
678 recalcAndStoreCaretPos();
683 KApplication::sendPostedEvents(viewport(), QEvent::Paint
);
685 if ( m_part
&& m_part
->xmlDocImpl() )
686 m_part
->xmlDocImpl()->dispatchWindowEvent( EventImpl::RESIZE_EVENT
, false, false );
689 void KHTMLView::paintEvent( QPaintEvent
*e
)
691 QPainter
p(widget());
694 QRect
v(contentsX(), contentsY(), visibleWidth(), visibleHeight());
697 if (!r
.isValid() || r
.isEmpty()) return;
705 if(!m_part
|| !m_part
->xmlDocImpl() || !m_part
->xmlDocImpl()->renderer()) {
706 p
.fillRect(ex
, ey
, ew
, eh
, palette().brush(QPalette::Active
, QPalette::Base
));
708 } else if ( d
->complete
&& static_cast<RenderCanvas
*>(m_part
->xmlDocImpl()->renderer())->needsLayout() ) {
709 // an external update request happens while we have a layout scheduled
710 unscheduleRelayout();
715 kDebug( 6000 ) << "WARNING: paintEvent reentered! " << endl
;
716 kDebug( 6000 ) << kBacktrace() << endl
;
721 m_part
->xmlDocImpl()->renderer()->layer()->paint(&p
, r
);
723 #ifndef KHTML_NO_CARET
724 if (d
->m_caretViewContext
&& d
->m_caretViewContext
->visible
) {
725 QRect
pos(d
->m_caretViewContext
->x
, d
->m_caretViewContext
->y
,
726 d
->m_caretViewContext
->width
, d
->m_caretViewContext
->height
);
727 if (pos
.intersects(QRect(ex
, ey
, ew
, eh
))) {
728 p
.setCompositionMode(QPainter::CompositionMode_Xor
);
730 if (pos
.width() == 1)
731 p
.drawLine(pos
.topLeft(), pos
.bottomRight());
733 p
.fillRect(pos
, Qt::white
);
737 #endif // KHTML_NO_CARET
739 khtml::DrawContentsEvent
event( &p
, ex
, ey
, ew
, eh
);
740 QApplication::sendEvent( m_part
, &event
);
742 if (d
->scrollingSelf
|| d
->contentsMoving
|| r
.contains(widget()->mapFromGlobal(QCursor::pos()))) {
743 QMouseEvent
*tempEvent
= new QMouseEvent( QEvent::MouseMove
, widget()->mapFromGlobal( QCursor::pos() ),
744 Qt::NoButton
, Qt::NoButton
, Qt::NoModifier
);
745 mouseMoveEvent( tempEvent
);
752 void KHTMLView::setMarginWidth(int w
)
754 // make it update the rendering area when set
758 void KHTMLView::setMarginHeight(int h
)
760 // make it update the rendering area when set
764 void KHTMLView::layout()
766 if( m_part
&& m_part
->xmlDocImpl() ) {
767 DOM::DocumentImpl
*document
= m_part
->xmlDocImpl();
769 khtml::RenderCanvas
* canvas
= static_cast<khtml::RenderCanvas
*>(document
->renderer());
770 if ( !canvas
) return;
772 d
->layoutSchedulingEnabled
=false;
774 // the reference object for the overflow property on canvas
775 RenderObject
* ref
= 0;
776 RenderObject
* root
= document
->documentElement() ? document
->documentElement()->renderer() : 0;
778 if (document
->isHTMLDocument()) {
779 NodeImpl
*body
= static_cast<HTMLDocumentImpl
*>(document
)->body();
780 if(body
&& body
->renderer() && body
->id() == ID_FRAMESET
) {
781 QScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
782 QScrollArea::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
783 body
->renderer()->setNeedsLayout(true);
785 else if (root
) // only apply body's overflow to canvas if root has a visible overflow
786 ref
= (!body
|| root
->style()->hidesOverflow()) ? root
: body
->renderer();
791 if( ref
->style()->overflowX() == OHIDDEN
) {
792 if (d
->hpolicy
== Qt::ScrollBarAsNeeded
) QScrollArea::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
793 } else if (ref
->style()->overflowX() == OSCROLL
) {
794 if (d
->hpolicy
== Qt::ScrollBarAsNeeded
) QScrollArea::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn
);
796 QScrollArea::setHorizontalScrollBarPolicy(d
->hpolicy
);
798 if ( ref
->style()->overflowY() == OHIDDEN
) {
799 if (d
->vpolicy
== Qt::ScrollBarAsNeeded
) QScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
800 } else if (ref
->style()->overflowY() == OSCROLL
) {
801 if (d
->vpolicy
== Qt::ScrollBarAsNeeded
) QScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn
);
803 QScrollArea::setVerticalScrollBarPolicy(d
->vpolicy
);
806 d
->needsFullRepaint
= d
->firstRelayout
;
807 if (_height
!= visibleHeight() || _width
!= visibleWidth()) {;
808 d
->needsFullRepaint
= true;
809 _height
= visibleHeight();
810 _width
= visibleWidth();
815 emit
finishedLayout();
816 if (d
->firstRelayout
) {
817 // make sure firstRelayout is set to false now in case this layout
819 d
->firstRelayout
= false;
820 verticalScrollBar()->setEnabled( true );
821 horizontalScrollBar()->setEnabled( true );
823 #ifndef KHTML_NO_CARET
825 if ((m_part
->isCaretMode() || m_part
->isEditable())
826 && !d
->complete
&& d
->m_caretViewContext
827 && !d
->m_caretViewContext
->caretMoved
) {
830 recalcAndStoreCaretPos();
834 if (d
->accessKeysEnabled
&& d
->accessKeysActivated
) {
835 emit
hideAccessKeys();
840 _width
= visibleWidth();
842 if (d
->layoutTimerId
)
843 killTimer(d
->layoutTimerId
);
844 d
->layoutTimerId
= 0;
845 d
->layoutSchedulingEnabled
=true;
848 void KHTMLView::closeChildDialogs()
850 QList
<QDialog
*> dlgs
= findChildren
<QDialog
*>();
851 foreach (QDialog
*dlg
, dlgs
)
853 KDialog
* dlgbase
= dynamic_cast<KDialog
*>( dlg
);
855 if ( dlgbase
->testAttribute( Qt::WA_ShowModal
) ) {
856 kDebug(6000) << "closeChildDialogs: closing dialog " << dlgbase
<< endl
;
857 // close() ends up calling QButton::animateClick, which isn't immediate
858 // we need something the exits the event loop immediately (#49068)
864 kWarning() << "closeChildDialogs: not a KDialog! Don't use QDialogs in KDE! " << static_cast<QWidget
*>(dlg
) << endl
;
865 static_cast<QWidget
*>(dlg
)->hide();
868 d
->m_dialogsAllowed
= false;
871 bool KHTMLView::dialogsAllowed() {
872 bool allowed
= d
->m_dialogsAllowed
;
873 KHTMLPart
* p
= m_part
->parentPart();
875 allowed
&= p
->view()->dialogsAllowed();
879 void KHTMLView::closeEvent( QCloseEvent
* ev
)
882 QScrollArea::closeEvent( ev
);
890 void KHTMLView::mousePressEvent( QMouseEvent
*_mouse
)
892 if (!m_part
->xmlDocImpl()) return;
893 if (d
->possibleTripleClick
&& ( _mouse
->button() & Qt::MouseButtonMask
) == Qt::LeftButton
)
895 mouseDoubleClickEvent( _mouse
); // it handles triple clicks too
899 int xm
= _mouse
->x();
900 int ym
= _mouse
->y();
902 // kDebug( 6000 ) << "mousePressEvent: viewport=("<<_mouse->x()-contentsX()<<"/"<<_mouse->y()-contentsY()<<"), contents=(" << xm << "/" << ym << ")\n";
904 d
->isDoubleClick
= false;
906 DOM::NodeImpl::MouseEvent
mev( _mouse
->buttons(), DOM::NodeImpl::MousePress
);
907 m_part
->xmlDocImpl()->prepareMouseEvent( false, xm
, ym
, &mev
);
909 //kDebug(6000) << "innerNode="<<mev.innerNode.nodeName().string()<<endl;
911 if ( (_mouse
->button() == Qt::MidButton
) &&
912 !m_part
->d
->m_bOpenMiddleClick
&& !d
->m_mouseScrollTimer
&&
913 mev
.url
.isNull() && (mev
.innerNode
.elementId() != ID_INPUT
) ) {
914 QPoint point
= mapFromGlobal( _mouse
->globalPos() );
916 d
->m_mouseScroll_byX
= 0;
917 d
->m_mouseScroll_byY
= 0;
919 d
->m_mouseScrollTimer
= new QTimer( this );
920 connect( d
->m_mouseScrollTimer
, SIGNAL(timeout()), this, SLOT(slotMouseScrollTimer()) );
922 if ( !d
->m_mouseScrollIndicator
) {
923 QPixmap
pixmap( 48, 48 ), icon
;
924 pixmap
.fill( QColor( qRgba( 127, 127, 127, 127 ) ) );
926 QPainter
p( &pixmap
);
927 icon
= KHTMLFactory::iconLoader()->loadIcon( "1uparrow", K3Icon::Small
);
928 p
.drawPixmap( 16, 0, icon
);
929 icon
= KHTMLFactory::iconLoader()->loadIcon( "1leftarrow", K3Icon::Small
);
930 p
.drawPixmap( 0, 16, icon
);
931 icon
= KHTMLFactory::iconLoader()->loadIcon( "1downarrow", K3Icon::Small
);
932 p
.drawPixmap( 16, 32,icon
);
933 icon
= KHTMLFactory::iconLoader()->loadIcon( "1rightarrow", K3Icon::Small
);
934 p
.drawPixmap( 32, 16, icon
);
935 p
.drawEllipse( 23, 23, 2, 2 );
937 d
->m_mouseScrollIndicator
= new QWidget( this );
938 d
->m_mouseScrollIndicator
->setFixedSize( 48, 48 );
940 palette
.setBrush( d
->m_mouseScrollIndicator
->backgroundRole(), QBrush( pixmap
) );
941 d
->m_mouseScrollIndicator
->setPalette( palette
);
943 d
->m_mouseScrollIndicator
->move( point
.x()-24, point
.y()-24 );
945 bool hasHorBar
= visibleWidth() < contentsWidth();
946 bool hasVerBar
= visibleHeight() < contentsHeight();
948 KConfigGroup
cg( KGlobal::config(), "HTML Settings" );
949 if ( cg
.readEntry( "ShowMouseScrollIndicator", true ) ) {
950 d
->m_mouseScrollIndicator
->show();
951 d
->m_mouseScrollIndicator
->unsetCursor();
953 QBitmap mask
= d
->m_mouseScrollIndicator
->palette().brush(d
->m_mouseScrollIndicator
->backgroundRole()).texture().createHeuristicMask( true );
955 if ( hasHorBar
&& !hasVerBar
) {
956 QBitmap
bm( 16, 16 );
958 QPainter
painter( &mask
);
959 painter
.drawPixmap( QRectF( 16, 0, bm
.width(), bm
.height() ), bm
, bm
.rect() );
960 painter
.drawPixmap( QRectF( 16, 32, bm
.width(), bm
.height() ), bm
, bm
.rect() );
961 d
->m_mouseScrollIndicator
->setCursor( Qt::SizeHorCursor
);
963 else if ( !hasHorBar
&& hasVerBar
) {
964 QBitmap
bm( 16, 16 );
966 QPainter
painter( &mask
);
967 painter
.drawPixmap( QRectF( 0, 16, bm
.width(), bm
.height() ), bm
, bm
.rect() );
968 painter
.drawPixmap( QRectF( 32, 16, bm
.width(), bm
.height() ), bm
, bm
.rect() );
969 d
->m_mouseScrollIndicator
->setCursor( Qt::SizeVerCursor
);
972 d
->m_mouseScrollIndicator
->setCursor( Qt::SizeAllCursor
);
974 d
->m_mouseScrollIndicator
->setMask( mask
);
977 if ( hasHorBar
&& !hasVerBar
)
978 viewport()->setCursor( Qt::SizeHorCursor
);
979 else if ( !hasHorBar
&& hasVerBar
)
980 viewport()->setCursor( Qt::SizeVerCursor
);
982 viewport()->setCursor( Qt::SizeAllCursor
);
987 else if ( d
->m_mouseScrollTimer
) {
988 delete d
->m_mouseScrollTimer
;
989 d
->m_mouseScrollTimer
= 0;
991 if ( d
->m_mouseScrollIndicator
)
992 d
->m_mouseScrollIndicator
->hide();
999 bool swallowEvent
= dispatchMouseEvent(EventImpl::MOUSEDOWN_EVENT
,mev
.innerNode
.handle(),mev
.innerNonSharedNode
.handle(),true,
1000 d
->clickCount
,_mouse
,true,DOM::NodeImpl::MousePress
);
1002 khtml::RenderObject
* r
= mev
.innerNode
.handle() ? mev
.innerNode
.handle()->renderer() : 0;
1003 if (r
&& r
->isWidget())
1006 if (!swallowEvent
) {
1007 emit m_part
->nodeActivated(mev
.innerNode
);
1009 khtml::MousePressEvent
event( _mouse
, xm
, ym
, mev
.url
, mev
.target
, mev
.innerNode
);
1010 QApplication::sendEvent( m_part
, &event
);
1011 // we might be deleted after this
1015 void KHTMLView::mouseDoubleClickEvent( QMouseEvent
*_mouse
)
1017 if(!m_part
->xmlDocImpl()) return;
1019 int xm
= _mouse
->x();
1020 int ym
= _mouse
->y();
1022 // kDebug( 6000 ) << "mouseDblClickEvent: x=" << xm << ", y=" << ym << endl;
1024 d
->isDoubleClick
= true;
1026 DOM::NodeImpl::MouseEvent
mev( _mouse
->buttons(), DOM::NodeImpl::MouseDblClick
);
1027 m_part
->xmlDocImpl()->prepareMouseEvent( false, xm
, ym
, &mev
);
1029 // We do the same thing as mousePressEvent() here, since the DOM does not treat
1030 // single and double-click events as separate (only the detail, i.e. number of clicks differs)
1031 if (d
->clickCount
> 0 &&
1032 QPoint(d
->clickX
-xm
,d
->clickY
-ym
).manhattanLength() <= QApplication::startDragDistance())
1034 else { // shouldn't happen, if Qt has the same criterias for double clicks.
1039 bool swallowEvent
= dispatchMouseEvent(EventImpl::MOUSEDOWN_EVENT
,mev
.innerNode
.handle(),mev
.innerNonSharedNode
.handle(),true,
1040 d
->clickCount
,_mouse
,true,DOM::NodeImpl::MouseDblClick
);
1042 khtml::RenderObject
* r
= mev
.innerNode
.handle() ? mev
.innerNode
.handle()->renderer() : 0;
1043 if (r
&& r
->isWidget())
1046 if (!swallowEvent
) {
1047 khtml::MouseDoubleClickEvent
event( _mouse
, xm
, ym
, mev
.url
, mev
.target
, mev
.innerNode
, d
->clickCount
);
1048 QApplication::sendEvent( m_part
, &event
);
1051 d
->possibleTripleClick
=true;
1052 QTimer::singleShot(QApplication::doubleClickInterval(),this,SLOT(tripleClickTimeout()));
1055 void KHTMLView::tripleClickTimeout()
1057 d
->possibleTripleClick
= false;
1061 void KHTMLView::mouseMoveEvent( QMouseEvent
* _mouse
)
1063 if ( d
->m_mouseScrollTimer
) {
1064 QPoint point
= mapFromGlobal( _mouse
->globalPos() );
1066 int deltaX
= point
.x() - d
->m_mouseScrollIndicator
->x() - 24;
1067 int deltaY
= point
.y() - d
->m_mouseScrollIndicator
->y() - 24;
1069 (deltaX
> 0) ? d
->m_mouseScroll_byX
= 1 : d
->m_mouseScroll_byX
= -1;
1070 (deltaY
> 0) ? d
->m_mouseScroll_byY
= 1 : d
->m_mouseScroll_byY
= -1;
1072 double adX
= QABS(deltaX
)/30.0;
1073 double adY
= QABS(deltaY
)/30.0;
1075 d
->m_mouseScroll_byX
= qMax(qMin(d
->m_mouseScroll_byX
* int(adX
*adX
), SHRT_MAX
), SHRT_MIN
);
1076 d
->m_mouseScroll_byY
= qMax(qMin(d
->m_mouseScroll_byY
* int(adY
*adY
), SHRT_MAX
), SHRT_MIN
);
1078 if (d
->m_mouseScroll_byX
== 0 && d
->m_mouseScroll_byY
== 0) {
1079 d
->m_mouseScrollTimer
->stop();
1081 else if (!d
->m_mouseScrollTimer
->isActive()) {
1082 d
->m_mouseScrollTimer
->start( 20 );
1086 if(!m_part
->xmlDocImpl()) return;
1088 int xm
= _mouse
->x();
1089 int ym
= _mouse
->y();
1091 DOM::NodeImpl::MouseEvent
mev( _mouse
->buttons(), DOM::NodeImpl::MouseMove
);
1092 // Do not modify :hover/:active state while mouse is pressed.
1093 m_part
->xmlDocImpl()->prepareMouseEvent( _mouse
->buttons() /*readonly ?*/, xm
, ym
, &mev
);
1095 // kDebug(6000) << "mouse move: " << _mouse->pos()
1096 // << " button " << _mouse->button()
1097 // << " state " << _mouse->state() << endl;
1099 DOM::NodeImpl
* target
= mev
.innerNode
.handle();
1100 DOM::NodeImpl
* fn
= m_part
->xmlDocImpl()->focusNode();
1102 // a widget may be the real target of this event (e.g. if a scrollbar's slider is being moved)
1103 if (d
->m_mouseEventsTarget
&& fn
&& fn
->renderer() && fn
->renderer()->isWidget())
1106 bool swallowEvent
= dispatchMouseEvent(EventImpl::MOUSEMOVE_EVENT
,target
,mev
.innerNonSharedNode
.handle(),false,
1107 0,_mouse
,true,DOM::NodeImpl::MouseMove
);
1109 if (d
->clickCount
> 0 &&
1110 QPoint(d
->clickX
-xm
,d
->clickY
-ym
).manhattanLength() > QApplication::startDragDistance()) {
1111 d
->clickCount
= 0; // moving the mouse outside the threshold invalidates the click
1114 // execute the scheduled script. This is to make sure the mouseover events come after the mouseout events
1115 m_part
->executeScheduledScript();
1117 khtml::RenderObject
* r
= target
? target
->renderer() : 0;
1118 khtml::RenderStyle
* style
= (r
&& r
->style()) ? r
->style() : 0;
1120 bool mailtoCursor
= false;
1121 switch ( style
? style
->cursor() : CURSOR_AUTO
) {
1123 if ( r
&& r
->isText() )
1124 c
= KCursor::ibeamCursor();
1125 if ( mev
.url
.length() && m_part
->settings()->changeCursor() ) {
1126 c
= m_part
->urlCursor();
1127 if (mev
.url
.string().startsWith("mailto:") && mev
.url
.string().indexOf('@')>0)
1128 mailtoCursor
= true;
1131 if (r
&& r
->isFrameSet() && !static_cast<RenderFrameSet
*>(r
)->noResize())
1132 c
= QCursor(static_cast<RenderFrameSet
*>(r
)->cursorShape());
1136 c
= KCursor::crossCursor();
1138 case CURSOR_POINTER
:
1139 c
= m_part
->urlCursor();
1140 if (mev
.url
.string().startsWith("mailto:") && mev
.url
.string().indexOf('@')>0)
1141 mailtoCursor
= true;
1143 case CURSOR_PROGRESS
:
1144 c
= KCursor::workingCursor();
1147 c
= KCursor::sizeAllCursor();
1149 case CURSOR_E_RESIZE
:
1150 case CURSOR_W_RESIZE
:
1151 c
= KCursor::sizeHorCursor();
1153 case CURSOR_N_RESIZE
:
1154 case CURSOR_S_RESIZE
:
1155 c
= KCursor::sizeVerCursor();
1157 case CURSOR_NE_RESIZE
:
1158 case CURSOR_SW_RESIZE
:
1159 c
= KCursor::sizeBDiagCursor();
1161 case CURSOR_NW_RESIZE
:
1162 case CURSOR_SE_RESIZE
:
1163 c
= KCursor::sizeFDiagCursor();
1166 c
= KCursor::ibeamCursor();
1169 c
= KCursor::waitCursor();
1172 c
= KCursor::whatsThisCursor();
1174 case CURSOR_DEFAULT
:
1178 if ( viewport()->cursor().handle() != c
.handle() ) {
1179 if( c
.handle() == KCursor::arrowCursor().handle()) {
1180 for (KHTMLPart
* p
= m_part
; p
; p
= p
->parentPart())
1181 p
->view()->viewport()->unsetCursor();
1184 viewport()->setCursor( c
);
1188 if ( mailtoCursor
&& isVisible() && hasFocus() ) {
1190 if( !d
->cursor_icon_widget
) {
1191 QPixmap icon_pixmap
= KHTMLFactory::iconLoader()->loadIcon( "mail_generic", K3Icon::Small
, 0, K3Icon::DefaultState
, 0, true );
1193 d
->cursor_icon_widget
= new QWidget( 0, Qt::WX11BypassWM
);
1194 XSetWindowAttributes attr
;
1195 attr
.save_under
= True
;
1196 XChangeWindowAttributes( QX11Info::display(), d
->cursor_icon_widget
->winId(), CWSaveUnder
, &attr
);
1198 d
->cursor_icon_widget
= new QWidget( NULL
, NULL
);
1201 d
->cursor_icon_widget
->resize( icon_pixmap
.width(), icon_pixmap
.height());
1202 if( !icon_pixmap
.mask().isNull() )
1203 d
->cursor_icon_widget
->setMask( icon_pixmap
.mask());
1205 d
->cursor_icon_widget
->clearMask();
1207 palette
.setBrush( d
->cursor_icon_widget
->backgroundRole(), QBrush( icon_pixmap
) );
1208 d
->cursor_icon_widget
->setPalette( palette
);
1209 d
->cursor_icon_widget
->erase();
1211 QPoint c_pos
= QCursor::pos();
1212 d
->cursor_icon_widget
->move( c_pos
.x() + 15, c_pos
.y() + 15 );
1214 XRaiseWindow( QX11Info::display(), d
->cursor_icon_widget
->winId());
1215 QApplication::flush();
1216 #elif defined(Q_WS_WIN)
1217 SetWindowPos( d
->cursor_icon_widget
->winId(), HWND_TOP
, 0, 0, 0, 0, SWP_NOACTIVATE
| SWP_NOMOVE
| SWP_NOSIZE
);
1221 d
->cursor_icon_widget
->show();
1224 else if ( d
->cursor_icon_widget
)
1225 d
->cursor_icon_widget
->hide();
1227 if (r
&& r
->isWidget()) {
1231 if (!swallowEvent
) {
1232 khtml::MouseMoveEvent
event( _mouse
, xm
, ym
, mev
.url
, mev
.target
, mev
.innerNode
);
1233 QApplication::sendEvent( m_part
, &event
);
1237 void KHTMLView::mouseReleaseEvent( QMouseEvent
* _mouse
)
1239 bool swallowEvent
= false;
1241 int xm
= _mouse
->x();
1242 int ym
= _mouse
->y();
1244 DOM::NodeImpl::MouseEvent
mev( _mouse
->buttons(), DOM::NodeImpl::MouseRelease
);
1246 if ( m_part
->xmlDocImpl() )
1248 m_part
->xmlDocImpl()->prepareMouseEvent( false, xm
, ym
, &mev
);
1250 DOM::NodeImpl
* target
= mev
.innerNode
.handle();
1251 DOM::NodeImpl
* fn
= m_part
->xmlDocImpl()->focusNode();
1253 // a widget may be the real target of this event (e.g. if a scrollbar's slider is being moved)
1254 if (d
->m_mouseEventsTarget
&& fn
&& fn
->renderer() && fn
->renderer()->isWidget())
1257 swallowEvent
= dispatchMouseEvent(EventImpl::MOUSEUP_EVENT
,target
,mev
.innerNonSharedNode
.handle(),true,
1258 d
->clickCount
,_mouse
,false,DOM::NodeImpl::MouseRelease
);
1260 // clear our sticky event target on any mouseRelease event
1261 if (d
->m_mouseEventsTarget
)
1262 d
->m_mouseEventsTarget
= 0;
1264 if (d
->clickCount
> 0 &&
1265 QPoint(d
->clickX
-xm
,d
->clickY
-ym
).manhattanLength() <= QApplication::startDragDistance()) {
1266 QMouseEvent
me(d
->isDoubleClick
? QEvent::MouseButtonDblClick
: QEvent::MouseButtonRelease
,
1267 _mouse
->pos(), _mouse
->button(), _mouse
->buttons(), _mouse
->modifiers());
1268 dispatchMouseEvent(EventImpl::CLICK_EVENT
, mev
.innerNode
.handle(),mev
.innerNonSharedNode
.handle(),true,
1269 d
->clickCount
, &me
, true, DOM::NodeImpl::MouseRelease
);
1272 khtml::RenderObject
* r
= target
? target
->renderer() : 0;
1273 if (r
&& r
->isWidget())
1277 if (!swallowEvent
) {
1278 khtml::MouseReleaseEvent
event( _mouse
, xm
, ym
, mev
.url
, mev
.target
, mev
.innerNode
);
1279 QApplication::sendEvent( m_part
, &event
);
1283 // returns true if event should be swallowed
1284 bool KHTMLView::dispatchKeyEvent( QKeyEvent
*_ke
)
1286 if (!m_part
->xmlDocImpl())
1288 // Pressing and releasing a key should generate keydown, keypress and keyup events
1289 // Holding it down should generated keydown, keypress (repeatedly) and keyup events
1290 // The problem here is that Qt generates two autorepeat events (keyrelease+keypress)
1291 // for autorepeating, while DOM wants only one autorepeat event (keypress), so one
1292 // of the Qt events shouldn't be passed to DOM, but it should be still filtered
1293 // out if DOM would filter the autorepeat event. Additional problem is that Qt keyrelease
1294 // events don't have text() set (Qt bug?), so DOM often would ignore the keypress event
1295 // if it was created using Qt keyrelease, but Qt autorepeat keyrelease comes
1296 // before Qt autorepeat keypress (i.e. problem whether to filter it out or not).
1297 // The solution is to filter out and postpone the Qt autorepeat keyrelease until
1298 // the following Qt keypress event comes. If DOM accepts the DOM keypress event,
1299 // the postponed event will be simply discarded. If not, it will be passed to keyPressEvent()
1300 // again, and here it will be ignored.
1302 // Qt: Press | Release(autorepeat) Press(autorepeat) etc. | Release
1303 // DOM: Down + Press | (nothing) Press | Up
1305 // It's also possible to get only Releases. E.g. the release of alt-tab,
1306 // or when the keypresses get captured by an accel.
1308 if( _ke
== d
->postponed_autorepeat
) // replayed event
1313 if( _ke
->type() == QEvent::KeyPress
)
1315 if( !_ke
->isAutoRepeat())
1317 bool ret
= dispatchKeyEventHelper( _ke
, false ); // keydown
1318 // don't send keypress even if keydown was blocked, like IE (and unlike Mozilla)
1319 if( !ret
&& dispatchKeyEventHelper( _ke
, true )) // keypress
1325 bool ret
= dispatchKeyEventHelper( _ke
, true ); // keypress
1326 if( !ret
&& d
->postponed_autorepeat
)
1327 keyPressEvent( d
->postponed_autorepeat
);
1328 delete d
->postponed_autorepeat
;
1329 d
->postponed_autorepeat
= NULL
;
1333 else // QEvent::KeyRelease
1335 // Discard postponed "autorepeat key-release" events that didn't see
1336 // a keypress after them (e.g. due to QAccel)
1337 if ( d
->postponed_autorepeat
) {
1338 delete d
->postponed_autorepeat
;
1339 d
->postponed_autorepeat
= 0;
1342 if( !_ke
->isAutoRepeat()) {
1343 return dispatchKeyEventHelper( _ke
, false ); // keyup
1347 d
->postponed_autorepeat
= new QKeyEvent( _ke
->type(), _ke
->key(), _ke
->modifiers(),
1348 _ke
->text(), _ke
->isAutoRepeat(), _ke
->count());
1349 if( _ke
->isAccepted())
1350 d
->postponed_autorepeat
->accept();
1352 d
->postponed_autorepeat
->ignore();
1358 // returns true if event should be swallowed
1359 bool KHTMLView::dispatchKeyEventHelper( QKeyEvent
*_ke
, bool keypress
)
1361 DOM::NodeImpl
* keyNode
= m_part
->xmlDocImpl()->focusNode();
1363 return keyNode
->dispatchKeyEvent(_ke
, keypress
);
1364 } else { // no focused node, send to document
1365 return m_part
->xmlDocImpl()->dispatchKeyEvent(_ke
, keypress
);
1369 void KHTMLView::keyPressEvent( QKeyEvent
*_ke
)
1371 #ifndef KHTML_NO_TYPE_AHEAD_FIND
1372 if(d
->typeAheadActivated
)
1374 // type-ahead find aka find-as-you-type
1375 if(_ke
->key() == Qt::Key_Backspace
)
1377 d
->findString
= d
->findString
.left(d
->findString
.length() - 1);
1379 if(!d
->findString
.isEmpty())
1388 d
->timer
.setSingleShot(true);
1389 d
->timer
.start(3000);
1393 else if(_ke
->key() == Qt::Key_Escape
)
1400 else if(_ke
->key() == Qt::Key_Space
|| !_ke
->text().trimmed().isEmpty())
1402 d
->findString
+= _ke
->text();
1406 d
->timer
.setSingleShot(true);
1407 d
->timer
.start(3000);
1412 #endif // KHTML_NO_TYPE_AHEAD_FIND
1414 #ifndef KHTML_NO_CARET
1415 if (m_part
->isEditable() || m_part
->isCaretMode()
1416 || (m_part
->xmlDocImpl() && m_part
->xmlDocImpl()->focusNode()
1417 && m_part
->xmlDocImpl()->focusNode()->contentEditable())) {
1418 d
->caretViewContext()->keyReleasePending
= true;
1419 caretKeyPressEvent(_ke
);
1422 #endif // KHTML_NO_CARET
1424 // If CTRL was hit, be prepared for access keys
1425 if (d
->accessKeysEnabled
&& _ke
->key() == Qt::Key_Control
&& _ke
->modifiers()==0 && !d
->accessKeysActivated
)
1427 d
->accessKeysPreActivate
=true;
1432 if (_ke
->key() == Qt::Key_Shift
&& _ke
->modifiers()==0)
1433 d
->scrollSuspendPreActivate
=true;
1435 // accesskey handling needs to be done before dispatching, otherwise e.g. lineedits
1436 // may eat the event
1438 if (d
->accessKeysEnabled
&& d
->accessKeysActivated
)
1440 int state
= ( _ke
->modifiers() & ( Qt::ShiftModifier
| Qt::ControlModifier
| Qt::AltModifier
| Qt::MetaModifier
));
1441 if ( state
==0 || state
==Qt::ShiftModifier
) {
1442 if (_ke
->key() != Qt::Key_Shift
) accessKeysTimeout();
1443 handleAccessKey( _ke
);
1447 accessKeysTimeout();
1450 if ( dispatchKeyEvent( _ke
)) {
1451 // If either keydown or keypress was accepted by a widget, or canceled by JS, stop here.
1456 int offs
= (viewport()->height() < 30) ? viewport()->height() : 30; // ### ??
1457 if (_ke
->modifiers() & Qt::ShiftModifier
)
1461 verticalScrollBar()->setValue( verticalScrollBar()->value() -viewport()->height() + offs
);
1462 if(d
->scrollSuspended
)
1463 d
->newScrollTimer(this, 0);
1468 d
->adjustScroller(this, KHTMLViewPrivate::ScrollDown
, KHTMLViewPrivate::ScrollUp
);
1473 d
->adjustScroller(this, KHTMLViewPrivate::ScrollUp
, KHTMLViewPrivate::ScrollDown
);
1478 d
->adjustScroller(this, KHTMLViewPrivate::ScrollLeft
, KHTMLViewPrivate::ScrollRight
);
1483 d
->adjustScroller(this, KHTMLViewPrivate::ScrollRight
, KHTMLViewPrivate::ScrollLeft
);
1487 switch ( _ke
->key() )
1491 if (!d
->scrollTimerId
|| d
->scrollSuspended
)
1492 verticalScrollBar()->setValue( verticalScrollBar()->value()+10 );
1493 if (d
->scrollTimerId
)
1494 d
->newScrollTimer(this, 0);
1498 case Qt::Key_PageDown
:
1499 verticalScrollBar()->setValue( verticalScrollBar()->value() +viewport()->height() - offs
);
1500 if(d
->scrollSuspended
)
1501 d
->newScrollTimer(this, 0);
1506 if (!d
->scrollTimerId
|| d
->scrollSuspended
)
1507 verticalScrollBar()->setValue( verticalScrollBar()->value()-10 );
1508 if (d
->scrollTimerId
)
1509 d
->newScrollTimer(this, 0);
1512 case Qt::Key_PageUp
:
1513 verticalScrollBar()->setValue( verticalScrollBar()->value() -viewport()->height() + offs
);
1514 if(d
->scrollSuspended
)
1515 d
->newScrollTimer(this, 0);
1519 if (!d
->scrollTimerId
|| d
->scrollSuspended
)
1520 horizontalScrollBar()->setValue( horizontalScrollBar()->value()+10 );
1521 if (d
->scrollTimerId
)
1522 d
->newScrollTimer(this, 0);
1527 if (!d
->scrollTimerId
|| d
->scrollSuspended
)
1528 horizontalScrollBar()->setValue( horizontalScrollBar()->value()-10 );
1529 if (d
->scrollTimerId
)
1530 d
->newScrollTimer(this, 0);
1533 case Qt::Key_Return
:
1535 // or even better to HTMLAnchorElementImpl::event()
1536 if (m_part
->xmlDocImpl()) {
1537 NodeImpl
*n
= m_part
->xmlDocImpl()->focusNode();
1543 verticalScrollBar()->setValue( 0 );
1544 horizontalScrollBar()->setValue( 0 );
1545 if(d
->scrollSuspended
)
1546 d
->newScrollTimer(this, 0);
1549 verticalScrollBar()->setValue( contentsHeight() - visibleHeight() );
1550 if(d
->scrollSuspended
)
1551 d
->newScrollTimer(this, 0);
1554 // what are you doing here?
1558 if (d
->scrollTimerId
)
1559 d
->newScrollTimer(this, 0);
1567 void KHTMLView::findTimeout()
1569 #ifndef KHTML_NO_TYPE_AHEAD_FIND
1570 d
->typeAheadActivated
= false;
1572 m_part
->setStatusBarText(i18n("Find stopped."), KHTMLPart::BarDefaultText
);
1573 m_part
->enableFindAheadActions( true );
1574 #endif // KHTML_NO_TYPE_AHEAD_FIND
1577 #ifndef KHTML_NO_TYPE_AHEAD_FIND
1578 void KHTMLView::startFindAhead( bool linksOnly
)
1582 d
->findLinksOnly
= true;
1583 m_part
->setStatusBarText(i18n("Starting -- find links as you type"),
1584 KHTMLPart::BarDefaultText
);
1588 d
->findLinksOnly
= false;
1589 m_part
->setStatusBarText(i18n("Starting -- find text as you type"),
1590 KHTMLPart::BarDefaultText
);
1593 m_part
->findTextBegin();
1594 d
->typeAheadActivated
= true;
1595 // disable, so that the shortcut ( / or ' by default ) doesn't interfere
1596 m_part
->enableFindAheadActions( false );
1597 d
->timer
.setSingleShot(true);
1598 d
->timer
.start(3000);
1601 void KHTMLView::findAhead(bool increase
)
1604 QString text
= d
->findString
.toLower();
1606 if(d
->findLinksOnly
)
1608 m_part
->findText(d
->findString
, KHTMLPart::FindNoPopups
|
1609 KHTMLPart::FindLinksOnly
, this);
1610 if(m_part
->findTextNext())
1612 status
= i18n("Link found: \"%1\".", text
);
1616 if(increase
) KNotification::beep();
1617 status
= i18n("Link not found: \"%1\".", text
);
1622 m_part
->findText(d
->findString
, KHTMLPart::FindNoPopups
, this);
1623 if(m_part
->findTextNext())
1625 status
= i18n("Text found: \"%1\".", text
);
1629 if(increase
) KNotification::beep();
1630 status
= i18n("Text not found: \"%1\".", text
);
1634 m_part
->setStatusBarText(status
, KHTMLPart::BarDefaultText
);
1637 void KHTMLView::updateFindAheadTimeout()
1639 if( d
->typeAheadActivated
) {
1640 d
->timer
.setSingleShot( true );
1641 d
->timer
.start( 3000 );
1645 #endif // KHTML_NO_TYPE_AHEAD_FIND
1647 void KHTMLView::keyReleaseEvent(QKeyEvent
*_ke
)
1649 #ifndef KHTML_NO_TYPE_AHEAD_FIND
1650 if(d
->typeAheadActivated
) {
1655 if (d
->m_caretViewContext
&& d
->m_caretViewContext
->keyReleasePending
) {
1656 //caretKeyReleaseEvent(_ke);
1657 d
->m_caretViewContext
->keyReleasePending
= false;
1661 if( d
->scrollSuspendPreActivate
&& _ke
->key() != Qt::Key_Shift
)
1662 d
->scrollSuspendPreActivate
= false;
1663 if( _ke
->key() == Qt::Key_Shift
&& d
->scrollSuspendPreActivate
&& _ke
->modifiers() == Qt::ShiftModifier
1664 && !(QApplication::keyboardModifiers() & Qt::ShiftModifier
))
1665 if (d
->scrollTimerId
)
1666 d
->scrollSuspended
= !d
->scrollSuspended
;
1668 if (d
->accessKeysEnabled
)
1670 if (d
->accessKeysPreActivate
&& _ke
->key() != Qt::Key_Control
)
1671 d
->accessKeysPreActivate
=false;
1672 if (d
->accessKeysPreActivate
&& _ke
->modifiers() == Qt::ControlModifier
&&
1673 !(QApplication::keyboardModifiers() & Qt::ControlModifier
))
1675 displayAccessKeys();
1676 m_part
->setStatusBarText(i18n("Access Keys activated"),KHTMLPart::BarOverrideText
);
1677 d
->accessKeysActivated
= true;
1678 d
->accessKeysPreActivate
= false;
1682 else if (d
->accessKeysActivated
)
1684 accessKeysTimeout();
1691 if ( dispatchKeyEvent( _ke
) )
1697 QScrollArea::keyReleaseEvent(_ke
);
1700 bool KHTMLView::focusNextPrevChild( bool next
)
1702 // Now try to find the next child
1703 if (m_part
->xmlDocImpl() && focusNextPrevNode(next
))
1705 if (m_part
->xmlDocImpl()->focusNode())
1706 kDebug() << "focusNode.name: "
1707 << m_part
->xmlDocImpl()->focusNode()->nodeName().string() << endl
;
1708 return true; // focus node found
1711 // If we get here, pass tabbing control up to the next/previous child in our parent
1712 d
->pseudoFocusNode
= KHTMLViewPrivate::PFNone
;
1713 if (m_part
->parentPart() && m_part
->parentPart()->view())
1714 return m_part
->parentPart()->view()->focusNextPrevChild(next
);
1716 return QWidget::focusNextPrevChild(next
);
1719 void KHTMLView::doAutoScroll()
1721 QPoint pos
= QCursor::pos();
1722 pos
= viewport()->mapFromGlobal( pos
);
1725 viewportToContents(pos
.x(), pos
.y(), xm
, ym
);
1727 pos
= QPoint(pos
.x() - viewport()->x(), pos
.y() - viewport()->y());
1728 if ( (pos
.y() < 0) || (pos
.y() > visibleHeight()) ||
1729 (pos
.x() < 0) || (pos
.x() > visibleWidth()) )
1731 ensureVisible( xm
, ym
, 0, 5 );
1733 #ifndef KHTML_NO_SELECTION
1734 // extend the selection while scrolling
1735 DOM::Node innerNode
;
1736 if (m_part
->isExtendingSelection()) {
1737 RenderObject::NodeInfo
renderInfo(true/*readonly*/, false/*active*/);
1738 m_part
->xmlDocImpl()->renderer()->layer()
1739 ->nodeAtPoint(renderInfo
, xm
, ym
);
1740 innerNode
= renderInfo
.innerNode();
1743 if (innerNode
.handle() && innerNode
.handle()->renderer()) {
1745 innerNode
.handle()->renderer()->absolutePosition(absX
, absY
);
1747 m_part
->extendSelectionTo(xm
, ym
, absX
, absY
, innerNode
);
1749 #endif // KHTML_NO_SELECTION
1753 static void handleWidget(QWidget
* w
, KHTMLView
* view
)
1755 if (w
->isTopLevel())
1758 if (!qobject_cast
<QFrame
*>(w
))
1759 w
->setAttribute( Qt::WA_NoSystemBackground
);
1760 w
->setAttribute(Qt::WA_WState_InPaintEvent
); // ### horrible - FIXME (needs Qt change to Widget::update)
1761 w
->setAttribute(Qt::WA_OpaquePaintEvent
);
1762 w
->installEventFilter(view
);
1764 QObjectList children
= w
->children();
1765 foreach (QObject
* object
, children
) {
1766 QWidget
*widget
= qobject_cast
<QWidget
*>(object
);
1768 handleWidget(widget
, view
);
1772 class KHTMLBackingStoreHackWidget
: public QWidget
1775 void publicEvent(QEvent
*e
)
1781 bool KHTMLView::viewportEvent ( QEvent
* e
)
1783 switch (e
->type()) {
1784 // those must not be dispatched to the specialized handlers
1785 // as widgetEvent() already took care of that
1786 case QEvent::MouseButtonPress
:
1787 case QEvent::MouseButtonRelease
:
1788 case QEvent::MouseButtonDblClick
:
1789 case QEvent::MouseMove
:
1790 #ifndef QT_NO_WHEELEVENT
1793 case QEvent::ContextMenu
:
1794 case QEvent::DragEnter
:
1795 case QEvent::DragMove
:
1796 case QEvent::DragLeave
:
1799 case QEvent::Paint
: {
1800 QRect r
= static_cast<QPaintEvent
*>(e
)->rect();
1801 r
.setX(r
.x() +contentsX());
1802 r
.setY(r
.y() +contentsY());
1810 return QScrollArea::viewportEvent(e
);
1813 bool KHTMLView::eventFilter(QObject
*o
, QEvent
*e
)
1815 if ( e
->type() == QEvent::ShortcutOverride
) {
1816 QKeyEvent
* ke
= (QKeyEvent
*) e
;
1817 if (m_part
->isEditable() || m_part
->isCaretMode()
1818 || (m_part
->xmlDocImpl() && m_part
->xmlDocImpl()->focusNode()
1819 && m_part
->xmlDocImpl()->focusNode()->contentEditable())) {
1820 if ( (ke
->modifiers() & Qt::ControlModifier
) || (ke
->modifiers() & Qt::ShiftModifier
) ) {
1821 switch ( ke
->key() ) {
1837 if ( e
->type() == QEvent::Leave
) {
1838 if ( d
->cursor_icon_widget
)
1839 d
->cursor_icon_widget
->hide();
1840 m_part
->resetHoverText();
1843 QWidget
*view
= widget();
1847 } else if (o
->isWidgetType()) {
1848 QWidget
*v
= static_cast<QWidget
*>(o
);
1850 while (v
&& v
!= view
) {
1852 v
= v
->parentWidget();
1854 KHTMLWidget
* k
= dynamic_cast<KHTMLWidget
*>(c
);
1855 if (v
&& k
&& k
->m_kwp
->isRedirected()) {
1857 bool isUpdate
= false;
1858 QWidget
*w
= static_cast<QWidget
*>(o
);
1860 case QEvent::UpdateRequest
: {
1861 // implicitly call qt_syncBackingStore(w)
1862 static_cast<KHTMLBackingStoreHackWidget
*>(w
)->publicEvent(e
);
1866 case QEvent::UpdateLater
:
1870 if (!allowWidgetPaintEvents
) {
1871 // eat the event. Like this we can control exactly when the widget
1876 while (v
&& v
->parentWidget() != view
) {
1879 v
= v
->parentWidget();
1882 QPoint ap
= k
->m_kwp
->absolutePos();
1886 QRect pr
= isUpdate
? static_cast<QUpdateLaterEvent
*>(e
)->region().boundingRect() : static_cast<QPaintEvent
*>(e
)->rect();
1887 bool asap
= !isUpdate
&& !d
->contentsMoving
&& (qobject_cast
<Q3ScrollView
*>(c
) || qobject_cast
<QAbstractScrollArea
*>(c
));
1890 // ### horrible - FIXME
1891 w
->setAttribute(Qt::WA_WState_InPaintEvent
, false);
1892 w
->update(static_cast<QUpdateLaterEvent
*>(e
)->region());
1893 w
->setAttribute(Qt::WA_WState_InPaintEvent
);
1894 // implicitly call qt_syncBackingStore(w)
1895 QEvent
fakeEvent(QEvent::UpdateRequest
);
1896 static_cast<KHTMLBackingStoreHackWidget
*>(w
)->publicEvent(&fakeEvent
);
1899 // QScrollView needs fast repaints
1900 if ( asap
&& !d
->painting
&& m_part
->xmlDocImpl() && m_part
->xmlDocImpl()->renderer() &&
1901 !static_cast<khtml::RenderCanvas
*>(m_part
->xmlDocImpl()->renderer())->needsLayout() ) {
1903 repaintContents(x
+ pr
.x(), y
+ pr
.y(),
1904 pr
.width(), pr
.height()+1); // ### investigate that +1 (shows up when
1905 // updating e.g a textarea's blinking cursor)
1906 d
->painting
= false;
1907 } else if (!d
->painting
) {
1908 scheduleRepaint(x
+ pr
.x(), y
+ pr
.y(),
1909 pr
.width(), pr
.height()+1, asap
);
1913 case QEvent::MouseMove
:
1914 case QEvent::MouseButtonPress
:
1915 case QEvent::MouseButtonRelease
:
1916 case QEvent::MouseButtonDblClick
: {
1918 if (0 && w
->parentWidget() == view
&& !qobject_cast
<QScrollBar
*>(w
) && !::qobject_cast
<QScrollBar
*>(w
)) {
1919 QMouseEvent
*me
= static_cast<QMouseEvent
*>(e
);
1920 QPoint pt
= w
->mapTo( view
, me
->pos());
1921 QMouseEvent
me2(me
->type(), pt
, me
->button(), me
->buttons(), me
->modifiers());
1923 if (e
->type() == QEvent::MouseMove
)
1924 mouseMoveEvent(&me2
);
1925 else if(e
->type() == QEvent::MouseButtonPress
)
1926 mousePressEvent(&me2
);
1927 else if(e
->type() == QEvent::MouseButtonRelease
)
1928 mouseReleaseEvent(&me2
);
1930 mouseDoubleClickEvent(&me2
);
1935 case QEvent::KeyPress
:
1936 case QEvent::KeyRelease
:
1937 if (w
->parentWidget() == view
&& !qobject_cast
<QScrollBar
*>(w
)) {
1938 QKeyEvent
*ke
= static_cast<QKeyEvent
*>(e
);
1939 if (e
->type() == QEvent::KeyPress
)
1942 keyReleaseEvent(ke
);
1946 case QEvent::FocusIn
:
1947 case QEvent::FocusOut
:
1954 //qDebug("eating event");
1960 // kDebug(6000) <<"passing event on to sv event filter object=" << o->className() << " event=" << e->type() << endl;
1961 return QScrollArea::eventFilter(o
, e
);
1965 bool KHTMLView::widgetEvent(QEvent
* e
)
1967 switch (e
->type()) {
1968 case QEvent::MouseButtonPress
:
1969 case QEvent::MouseButtonRelease
:
1970 case QEvent::MouseButtonDblClick
:
1971 case QEvent::MouseMove
:
1973 #ifndef QT_NO_WHEELEVENT
1976 case QEvent::ContextMenu
:
1977 case QEvent::DragEnter
:
1978 case QEvent::DragMove
:
1979 case QEvent::DragLeave
:
1981 return QFrame::event(e
);
1982 case QEvent::ChildInserted
: {
1983 // we need to install an event filter on all children of the widget() to
1984 // be able to get correct stacking of children within the document.
1985 QObject
*c
= static_cast<QChildEvent
*>(e
)->child();
1986 if (c
->isWidgetType()) {
1987 QWidget
*w
= static_cast<QWidget
*>(c
);
1988 // don't install the event filter on toplevels
1989 if (w
->parentWidget() == widget()) {
1990 KHTMLWidget
* k
= dynamic_cast<KHTMLWidget
*>(w
);
1991 if (k
&& k
->m_kwp
->isRedirected()) {
1993 handleWidget(w
, this);
2004 DOM::NodeImpl
*KHTMLView::nodeUnderMouse() const
2006 return d
->underMouse
;
2009 DOM::NodeImpl
*KHTMLView::nonSharedNodeUnderMouse() const
2011 return d
->underMouseNonShared
;
2014 bool KHTMLView::scrollTo(const QRect
&bounds
)
2016 d
->scrollingSelf
= true; // so scroll events get ignored
2021 xe
= bounds
.right();
2022 ye
= bounds
.bottom();
2024 //kDebug(6000)<<"scrolling coords: x="<<x<<" y="<<y<<" width="<<xe-x<<" height="<<ye-y<<endl;
2029 int curHeight
= visibleHeight();
2030 int curWidth
= visibleWidth();
2032 if (ye
-y
>curHeight
-d
->borderY
)
2033 ye
= y
+ curHeight
- d
->borderY
;
2035 if (xe
-x
>curWidth
-d
->borderX
)
2036 xe
= x
+ curWidth
- d
->borderX
;
2038 // is xpos of target left of the view's border?
2039 if (x
< contentsX() + d
->borderX
)
2040 deltax
= x
- contentsX() - d
->borderX
;
2041 // is xpos of target right of the view's right border?
2042 else if (xe
+ d
->borderX
> contentsX() + curWidth
)
2043 deltax
= xe
+ d
->borderX
- ( contentsX() + curWidth
);
2047 // is ypos of target above upper border?
2048 if (y
< contentsY() + d
->borderY
)
2049 deltay
= y
- contentsY() - d
->borderY
;
2050 // is ypos of target below lower border?
2051 else if (ye
+ d
->borderY
> contentsY() + curHeight
)
2052 deltay
= ye
+ d
->borderY
- ( contentsY() + curHeight
);
2056 int maxx
= curWidth
-d
->borderX
;
2057 int maxy
= curHeight
-d
->borderY
;
2059 int scrollX
, scrollY
;
2061 scrollX
= deltax
> 0 ? (deltax
> maxx
? maxx
: deltax
) : deltax
== 0 ? 0 : (deltax
>-maxx
? deltax
: -maxx
);
2062 scrollY
= deltay
> 0 ? (deltay
> maxy
? maxy
: deltay
) : deltay
== 0 ? 0 : (deltay
>-maxy
? deltay
: -maxy
);
2064 if (contentsX() + scrollX
< 0)
2065 scrollX
= -contentsX();
2066 else if (contentsWidth() - visibleWidth() - contentsX() < scrollX
)
2067 scrollX
= contentsWidth() - visibleWidth() - contentsX();
2069 if (contentsY() + scrollY
< 0)
2070 scrollY
= -contentsY();
2071 else if (contentsHeight() - visibleHeight() - contentsY() < scrollY
)
2072 scrollY
= contentsHeight() - visibleHeight() - contentsY();
2074 horizontalScrollBar()->setValue( horizontalScrollBar()->value()+scrollX
);
2075 verticalScrollBar()->setValue( verticalScrollBar()->value()+scrollY
);
2077 d
->scrollingSelf
= false;
2079 if ( (abs(deltax
)<=maxx
) && (abs(deltay
)<=maxy
) )
2085 bool KHTMLView::focusNextPrevNode(bool next
)
2087 // Sets the focus node of the document to be the node after (or if
2088 // next is false, before) the current focus node. Only nodes that
2089 // are selectable (i.e. for which isFocusable() returns true) are
2090 // taken into account, and the order used is that specified in the
2091 // HTML spec (see DocumentImpl::nextFocusNode() and
2092 // DocumentImpl::previousFocusNode() for details).
2094 DocumentImpl
*doc
= m_part
->xmlDocImpl();
2095 NodeImpl
*oldFocusNode
= doc
->focusNode();
2097 // See whether we're in the middle of detach. If so, we want to
2098 // clear focus... The document code will be careful to not
2099 // emit events in that case..
2100 if (oldFocusNode
&& oldFocusNode
->renderer() &&
2101 !oldFocusNode
->renderer()->parent()) {
2102 doc
->setFocusNode(0);
2107 // If the user has scrolled the document, then instead of picking
2108 // the next focusable node in the document, use the first one that
2109 // is within the visible area (if possible).
2110 if (d
->scrollBarMoved
)
2114 toFocus
= doc
->nextFocusNode(oldFocusNode
);
2116 toFocus
= doc
->previousFocusNode(oldFocusNode
);
2118 if (!toFocus
&& oldFocusNode
)
2120 toFocus
= doc
->nextFocusNode(NULL
);
2122 toFocus
= doc
->previousFocusNode(NULL
);
2124 while (toFocus
&& toFocus
!= oldFocusNode
)
2127 QRect focusNodeRect
= toFocus
->getRect();
2128 if ((focusNodeRect
.left() > contentsX()) && (focusNodeRect
.right() < contentsX() + visibleWidth()) &&
2129 (focusNodeRect
.top() > contentsY()) && (focusNodeRect
.bottom() < contentsY() + visibleHeight())) {
2131 QRect r
= toFocus
->getRect();
2132 ensureVisible( r
.right(), r
.bottom());
2133 ensureVisible( r
.left(), r
.top());
2134 d
->scrollBarMoved
= false;
2135 d
->tabMovePending
= false;
2136 d
->lastTabbingDirection
= next
;
2137 d
->pseudoFocusNode
= KHTMLViewPrivate::PFNone
;
2138 m_part
->xmlDocImpl()->setFocusNode(toFocus
);
2139 Node
guard(toFocus
);
2140 if (!toFocus
->hasOneRef() )
2142 emit m_part
->nodeActivated(Node(toFocus
));
2148 toFocus
= doc
->nextFocusNode(toFocus
);
2150 toFocus
= doc
->previousFocusNode(toFocus
);
2152 if (!toFocus
&& oldFocusNode
)
2154 toFocus
= doc
->nextFocusNode(NULL
);
2156 toFocus
= doc
->previousFocusNode(NULL
);
2159 d
->scrollBarMoved
= false;
2163 if (!oldFocusNode
&& d
->pseudoFocusNode
== KHTMLViewPrivate::PFNone
)
2165 ensureVisible(contentsX(), next
?0:contentsHeight());
2166 d
->scrollBarMoved
= false;
2167 d
->pseudoFocusNode
= next
?KHTMLViewPrivate::PFTop
:KHTMLViewPrivate::PFBottom
;
2171 NodeImpl
*newFocusNode
= NULL
;
2173 if (d
->tabMovePending
&& next
!= d
->lastTabbingDirection
)
2175 //kDebug ( 6000 ) << " tab move pending and tabbing direction changed!\n";
2176 newFocusNode
= oldFocusNode
;
2180 if (oldFocusNode
|| d
->pseudoFocusNode
== KHTMLViewPrivate::PFTop
)
2181 newFocusNode
= doc
->nextFocusNode(oldFocusNode
);
2185 if (oldFocusNode
|| d
->pseudoFocusNode
== KHTMLViewPrivate::PFBottom
)
2186 newFocusNode
= doc
->previousFocusNode(oldFocusNode
);
2189 bool targetVisible
= false;
2194 targetVisible
= scrollTo(QRect(contentsX()+visibleWidth()/2,contentsHeight()-d
->borderY
,0,0));
2198 targetVisible
= scrollTo(QRect(contentsX()+visibleWidth()/2,d
->borderY
,0,0));
2203 #ifndef KHTML_NO_CARET
2204 // if it's an editable element, activate the caret
2205 if (!m_part
->isCaretMode() && !m_part
->isEditable()
2206 && newFocusNode
->contentEditable()) {
2207 d
->caretViewContext();
2208 moveCaretTo(newFocusNode
, 0L, true);
2212 #endif // KHTML_NO_CARET
2214 targetVisible
= scrollTo(newFocusNode
->getRect());
2219 //kDebug ( 6000 ) << " target reached.\n";
2220 d
->tabMovePending
= false;
2222 m_part
->xmlDocImpl()->setFocusNode(newFocusNode
);
2225 Node
guard(newFocusNode
);
2226 if (!newFocusNode
->hasOneRef() )
2228 emit m_part
->nodeActivated(Node(newFocusNode
));
2234 d
->pseudoFocusNode
= next
?KHTMLViewPrivate::PFBottom
:KHTMLViewPrivate::PFTop
;
2240 if (!d
->tabMovePending
)
2241 d
->lastTabbingDirection
= next
;
2242 d
->tabMovePending
= true;
2247 void KHTMLView::displayAccessKeys()
2249 QVector
< QChar
> taken
;
2250 displayAccessKeys( NULL
, this, taken
, false );
2251 displayAccessKeys( NULL
, this, taken
, true );
2254 void KHTMLView::displayAccessKeys( KHTMLView
* caller
, KHTMLView
* origview
, QVector
< QChar
>& taken
, bool use_fallbacks
)
2256 QMap
< ElementImpl
*, QChar
> fallbacks
;
2258 fallbacks
= buildFallbackAccessKeys();
2259 for( NodeImpl
* n
= m_part
->xmlDocImpl(); n
!= NULL
; n
= n
->traverseNextNode()) {
2260 if( n
->isElementNode()) {
2261 ElementImpl
* en
= static_cast< ElementImpl
* >( n
);
2262 DOMString s
= en
->getAttribute( ATTR_ACCESSKEY
);
2264 if( s
.length() == 1 ) {
2265 QChar a
= s
.string()[ 0 ].toUpper();
2266 if( qFind( taken
.begin(), taken
.end(), a
) == taken
.end()) // !contains
2269 if( accesskey
.isNull() && fallbacks
.contains( en
)) {
2270 QChar a
= fallbacks
[ en
].toUpper();
2271 if( qFind( taken
.begin(), taken
.end(), a
) == taken
.end()) // !contains
2272 accesskey
= QString( "<qt><i>" ) + a
+ "</i></qt>";
2274 if( !accesskey
.isNull()) {
2275 QRect rec
=en
->getRect();
2276 QLabel
*lab
=new QLabel(accesskey
,viewport(),Qt::WDestructiveClose
);
2277 connect( origview
, SIGNAL(hideAccessKeys()), lab
, SLOT(close()) );
2278 connect( this, SIGNAL(repaintAccessKeys()), lab
, SLOT(repaint()));
2279 lab
->setPalette(QToolTip::palette());
2280 lab
->setLineWidth(2);
2281 lab
->setFrameStyle(QFrame::Box
| QFrame::Plain
);
2284 lab
->setParent( widget() );
2286 qMin(rec
.left()+rec
.width()/2, contentsWidth() - lab
->width()),
2287 qMin(rec
.top()+rec
.height()/2, contentsHeight() - lab
->height()));
2289 taken
.append( accesskey
[ 0 ] );
2296 QList
<KParts::ReadOnlyPart
*> frames
= m_part
->frames();
2297 foreach( KParts::ReadOnlyPart
* cur
, frames
) {
2298 if( !qobject_cast
<KHTMLPart
*>(cur
) )
2300 KHTMLPart
* part
= static_cast< KHTMLPart
* >( cur
);
2301 if( part
->view() && part
->view() != caller
)
2302 part
->view()->displayAccessKeys( this, origview
, taken
, use_fallbacks
);
2305 // pass up to the parent
2306 if (m_part
->parentPart() && m_part
->parentPart()->view()
2307 && m_part
->parentPart()->view() != caller
)
2308 m_part
->parentPart()->view()->displayAccessKeys( this, origview
, taken
, use_fallbacks
);
2313 void KHTMLView::accessKeysTimeout()
2315 d
->accessKeysActivated
=false;
2316 d
->accessKeysPreActivate
= false;
2317 m_part
->setStatusBarText(QString(), KHTMLPart::BarOverrideText
);
2318 emit
hideAccessKeys();
2321 // Handling of the HTML accesskey attribute.
2322 bool KHTMLView::handleAccessKey( const QKeyEvent
* ev
)
2324 // Qt interprets the keyevent also with the modifiers, and ev->text() matches that,
2325 // but this code must act as if the modifiers weren't pressed
2327 if( ev
->key() >= Qt::Key_A
&& ev
->key() <= Qt::Key_Z
)
2328 c
= 'A' + ev
->key() - Qt::Key_A
;
2329 else if( ev
->key() >= Qt::Key_0
&& ev
->key() <= Qt::Key_9
)
2330 c
= '0' + ev
->key() - Qt::Key_0
;
2332 // TODO fake XKeyEvent and XLookupString ?
2333 // This below seems to work e.g. for eacute though.
2334 if( ev
->text().length() == 1 )
2335 c
= ev
->text()[ 0 ];
2339 return focusNodeWithAccessKey( c
);
2342 bool KHTMLView::focusNodeWithAccessKey( QChar c
, KHTMLView
* caller
)
2344 DocumentImpl
*doc
= m_part
->xmlDocImpl();
2347 ElementImpl
* node
= doc
->findAccessKeyElement( c
);
2349 QList
<KParts::ReadOnlyPart
*> frames
= m_part
->frames();
2350 foreach( KParts::ReadOnlyPart
* cur
, frames
) {
2351 if( !qobject_cast
<KHTMLPart
*>(cur
) )
2353 KHTMLPart
* part
= static_cast< KHTMLPart
* >( cur
);
2354 if( part
->view() && part
->view() != caller
2355 && part
->view()->focusNodeWithAccessKey( c
, this ))
2358 // pass up to the parent
2359 if (m_part
->parentPart() && m_part
->parentPart()->view()
2360 && m_part
->parentPart()->view() != caller
2361 && m_part
->parentPart()->view()->focusNodeWithAccessKey( c
, this ))
2363 if( caller
== NULL
) { // the active frame (where the accesskey was pressed)
2364 QMap
< ElementImpl
*, QChar
> fallbacks
= buildFallbackAccessKeys();
2365 for( QMap
< ElementImpl
*, QChar
>::ConstIterator it
= fallbacks
.begin();
2366 it
!= fallbacks
.end();
2377 // Scroll the view as necessary to ensure that the new focus node is visible
2378 #ifndef KHTML_NO_CARET
2379 // if it's an editable element, activate the caret
2380 if (!m_part
->isCaretMode() && !m_part
->isEditable()
2381 && node
->contentEditable()) {
2382 d
->caretViewContext();
2383 moveCaretTo(node
, 0L, true);
2387 #endif // KHTML_NO_CARET
2389 QRect r
= node
->getRect();
2390 ensureVisible( r
.right(), r
.bottom());
2391 ensureVisible( r
.left(), r
.top());
2394 if( node
->isFocusable()) {
2395 if (node
->id()==ID_LABEL
) {
2396 // if Accesskey is a label, give focus to the label's referrer.
2397 node
=static_cast<ElementImpl
*>(static_cast< HTMLLabelElementImpl
* >( node
)->getFormElement());
2398 if (!node
) return true;
2401 // Set focus node on the document
2403 #warning "port QFocusEvent::setReason( QFocusEvent::Shortcut ); to qt4"
2405 //QFocusEvent::setReason( QFocusEvent::Shortcut );
2406 m_part
->xmlDocImpl()->setFocusNode(node
);
2408 #warning "port QFocusEvent::resetReason(); to qt4"
2410 //QFocusEvent::resetReason();
2411 if( node
!= NULL
&& node
->hasOneRef()) // deleted, only held by guard
2413 emit m_part
->nodeActivated(Node(node
));
2414 if( node
!= NULL
&& node
->hasOneRef())
2418 switch( node
->id()) {
2420 static_cast< HTMLAnchorElementImpl
* >( node
)->click();
2423 static_cast< HTMLInputElementImpl
* >( node
)->click();
2426 static_cast< HTMLButtonElementImpl
* >( node
)->click();
2429 static_cast< HTMLAreaElementImpl
* >( node
)->click();
2432 break; // just focusing it is enough
2440 static QString
getElementText( NodeImpl
* start
, bool after
)
2442 QString ret
; // nextSibling(), to go after e.g. </select>
2443 for( NodeImpl
* n
= after
? start
->nextSibling() : start
->traversePreviousNode();
2445 n
= after
? n
->traverseNextNode() : n
->traversePreviousNode()) {
2446 if( n
->isTextNode()) {
2448 ret
+= static_cast< TextImpl
* >( n
)->toString().string();
2450 ret
.prepend( static_cast< TextImpl
* >( n
)->toString().string());
2480 if( ret
.trimmed().isEmpty())
2484 return ret
.simplified();
2488 return ret
.simplified();
2491 static QMap
< NodeImpl
*, QString
> buildLabels( NodeImpl
* start
)
2493 QMap
< NodeImpl
*, QString
> ret
;
2494 for( NodeImpl
* n
= start
;
2496 n
= n
->traverseNextNode()) {
2497 if( n
->id() == ID_LABEL
) {
2498 HTMLLabelElementImpl
* label
= static_cast< HTMLLabelElementImpl
* >( n
);
2499 NodeImpl
* labelfor
= label
->getFormElement();
2501 ret
[ labelfor
] = label
->innerText().string().simplified();
2508 struct AccessKeyData
{
2509 ElementImpl
* element
;
2512 int priority
; // 10(highest) - 0(lowest)
2516 QMap
< ElementImpl
*, QChar
> KHTMLView::buildFallbackAccessKeys() const
2518 // build a list of all possible candidate elements that could use an accesskey
2519 QList
< AccessKeyData
> data
;
2520 QMap
< NodeImpl
*, QString
> labels
= buildLabels( m_part
->xmlDocImpl());
2521 for( NodeImpl
* n
= m_part
->xmlDocImpl();
2523 n
= n
->traverseNextNode()) {
2524 if( n
->isElementNode()) {
2525 ElementImpl
* element
= static_cast< ElementImpl
* >( n
);
2526 if( element
->getAttribute( ATTR_ACCESSKEY
).length() == 1 )
2527 continue; // has accesskey set, ignore
2528 if( element
->renderer() == NULL
)
2529 continue; // not visible
2533 bool ignore
= false;
2534 bool text_after
= false;
2535 bool text_before
= false;
2536 switch( element
->id()) {
2538 url
= khtml::parseURL(element
->getAttribute(ATTR_HREF
)).string();
2539 if( url
.isEmpty()) // doesn't have href, it's only an anchor
2541 text
= static_cast< HTMLElementImpl
* >( element
)->innerText().string().simplified();
2545 HTMLInputElementImpl
* in
= static_cast< HTMLInputElementImpl
* >( element
);
2546 switch( in
->inputType()) {
2547 case HTMLInputElementImpl::SUBMIT
:
2548 text
= in
->value().string();
2550 text
= i18n( "Submit" );
2553 case HTMLInputElementImpl::IMAGE
:
2554 text
= in
->altText().string();
2557 case HTMLInputElementImpl::BUTTON
:
2558 text
= in
->value().string();
2561 case HTMLInputElementImpl::RESET
:
2562 text
= in
->value().string();
2564 text
= i18n( "Reset" );
2567 case HTMLInputElementImpl::HIDDEN
:
2570 case HTMLInputElementImpl::CHECKBOX
:
2571 case HTMLInputElementImpl::RADIO
:
2575 case HTMLInputElementImpl::TEXT
:
2576 case HTMLInputElementImpl::PASSWORD
:
2577 case HTMLInputElementImpl::FILE:
2588 text
= static_cast< HTMLElementImpl
* >( element
)->innerText().string().simplified();
2589 switch( static_cast< HTMLButtonElementImpl
* >( element
)->buttonType()) {
2590 case HTMLButtonElementImpl::SUBMIT
:
2592 text
= i18n( "Submit" );
2595 case HTMLButtonElementImpl::RESET
:
2597 text
= i18n( "Reset" );
2605 case ID_SELECT
: // these don't have accesskey attribute, but quick access may be handy
2614 ignore
= !element
->isFocusable();
2620 if( text
.isNull() && labels
.contains( element
))
2621 text
= labels
[ element
];
2622 if( text
.isNull() && text_before
)
2623 text
= getElementText( element
, false );
2624 if( text
.isNull() && text_after
)
2625 text
= getElementText( element
, true );
2626 text
= text
.trimmed();
2627 // increase priority of items which have explicitly specified accesskeys in the config
2628 QList
< QPair
< QString
, QChar
> > priorities
2629 = m_part
->settings()->fallbackAccessKeysAssignments();
2630 for( QList
< QPair
< QString
, QChar
> >::ConstIterator it
= priorities
.begin();
2631 it
!= priorities
.end();
2633 if( text
== (*it
).first
)
2636 AccessKeyData tmp
= { element
, text
, url
, priority
};
2641 QList
< QChar
> keys
;
2642 for( char c
= 'A'; c
<= 'Z'; ++c
)
2644 for( char c
= '0'; c
<= '9'; ++c
)
2646 for( NodeImpl
* n
= m_part
->xmlDocImpl();
2648 n
= n
->traverseNextNode()) {
2649 if( n
->isElementNode()) {
2650 ElementImpl
* en
= static_cast< ElementImpl
* >( n
);
2651 DOMString s
= en
->getAttribute( ATTR_ACCESSKEY
);
2652 if( s
.length() == 1 ) {
2653 QChar c
= s
.string()[ 0 ].toUpper();
2654 keys
.removeAll( c
); // remove manually assigned accesskeys
2659 QMap
< ElementImpl
*, QChar
> ret
;
2660 for( int priority
= 10;
2663 for( QList
< AccessKeyData
>::Iterator it
= data
.begin();
2666 if( (*it
).priority
!= priority
) {
2672 QString text
= (*it
).text
;
2674 if( key
.isNull() && !text
.isEmpty()) {
2675 QList
< QPair
< QString
, QChar
> > priorities
2676 = m_part
->settings()->fallbackAccessKeysAssignments();
2677 for( QList
< QPair
< QString
, QChar
> >::ConstIterator it
= priorities
.begin();
2678 it
!= priorities
.end();
2680 if( text
== (*it
).first
&& keys
.contains( (*it
).second
)) {
2685 // try first to select the first character as the accesskey,
2686 // then first character of the following words,
2687 // and then simply the first free character
2688 if( key
.isNull() && !text
.isEmpty()) {
2689 QStringList words
= text
.split( ' ' );
2690 for( QStringList::ConstIterator it
= words
.begin();
2693 if( keys
.contains( (*it
)[ 0 ].toUpper())) {
2694 key
= (*it
)[ 0 ].toUpper();
2699 if( key
.isNull() && !text
.isEmpty()) {
2700 for( int i
= 0; i
< text
.length(); ++i
) {
2701 if( keys
.contains( text
[ i
].toUpper())) {
2702 key
= text
[ i
].toUpper();
2709 ret
[ (*it
).element
] = key
;
2710 keys
.removeAll( key
);
2711 QString url
= (*it
).url
;
2712 it
= data
.erase( it
);
2713 // assign the same accesskey also to other elements pointing to the same url
2714 if( !url
.isEmpty() && !url
.startsWith( "javascript:", Qt::CaseInsensitive
)) {
2715 for( QList
< AccessKeyData
>::Iterator it2
= data
.begin();
2718 if( (*it2
).url
== url
) {
2719 ret
[ (*it2
).element
] = key
;
2722 it2
= data
.erase( it2
);
2732 void KHTMLView::setMediaType( const QString
&medium
)
2737 QString
KHTMLView::mediaType() const
2742 bool KHTMLView::pagedMode() const
2747 void KHTMLView::setWidgetVisible(RenderWidget
* w
, bool vis
)
2750 d
->visibleWidgets
.replace(w
, w
->widget());
2753 d
->visibleWidgets
.remove(w
);
2756 bool KHTMLView::needsFullRepaint() const
2758 return d
->needsFullRepaint
;
2761 void KHTMLView::print(bool quick
)
2763 if(!m_part
->xmlDocImpl()) return;
2764 khtml::RenderCanvas
*root
= static_cast<khtml::RenderCanvas
*>(m_part
->xmlDocImpl()->renderer());
2767 KPrinter
*printer
= new KPrinter(true, QPrinter::ScreenResolution
);
2768 printer
->addDialogPage(new KHTMLPrintSettings());
2769 QString docname
= m_part
->xmlDocImpl()->URL().prettyUrl();
2770 if ( !docname
.isEmpty() )
2771 docname
= KStringHandler::csqueeze(docname
, 80);
2772 if(quick
|| printer
->setup(this, i18n("Print %1", docname
))) {
2773 viewport()->setCursor( Qt::WaitCursor
); // only viewport(), no QApplication::, otherwise we get the busy cursor in kdeprint's dialogs
2775 printer
->setFullPage(false);
2776 printer
->setCreator(QString("KDE %1.%2.%3 HTML Library").arg(KDE_VERSION_MAJOR
).arg(KDE_VERSION_MINOR
).arg(KDE_VERSION_RELEASE
));
2777 printer
->setDocName(docname
);
2779 QPainter
*p
= new QPainter
;
2780 p
->begin( printer
);
2781 khtml::setPrintPainter( p
);
2783 m_part
->xmlDocImpl()->setPaintDevice( printer
);
2784 QString oldMediaType
= mediaType();
2785 setMediaType( "print" );
2786 // We ignore margin settings for html and body when printing
2787 // and use the default margins from the print-system
2788 // (In Qt 3.0.x the default margins are hardcoded in Qt)
2789 m_part
->xmlDocImpl()->setPrintStyleSheet( printer
->option("app-khtml-printfriendly") == "true" ?
2790 "* { background-image: none !important;"
2791 " background-color: white !important;"
2792 " color: black !important; }"
2793 "body { margin: 0px !important; }"
2794 "html { margin: 0px !important; }" :
2795 "body { margin: 0px !important; }"
2796 "html { margin: 0px !important; }"
2799 Q3PaintDeviceMetrics
metrics( printer
);
2801 kDebug(6000) << "printing: physical page width = " << metrics
.width()
2802 << " height = " << metrics
.height() << endl
;
2803 root
->setStaticMode(true);
2804 root
->setPagedMode(true);
2805 root
->setWidth(metrics
.width());
2806 // root->setHeight(metrics.height());
2807 root
->setPageTop(0);
2808 root
->setPageBottom(0);
2811 m_part
->xmlDocImpl()->styleSelector()->computeFontSizes(&metrics
, 100);
2812 m_part
->xmlDocImpl()->updateStyleSelector();
2813 root
->setPrintImages( printer
->option("app-khtml-printimages") == "true");
2814 root
->makePageBreakAvoidBlocks();
2816 root
->setNeedsLayoutAndMinMaxRecalc();
2818 khtml::RenderWidget::flushWidgetResizes(); // make sure widgets have their final size
2820 // check sizes ask for action.. (scale or clip)
2822 bool printHeader
= (printer
->option("app-khtml-printheader") == "true");
2824 int headerHeight
= 0;
2825 QFont
headerFont("Sans Serif", 8);
2827 QString headerLeft
= KGlobal::locale()->formatDate(QDate::currentDate(),true);
2828 QString headerMid
= docname
;
2829 QString headerRight
;
2833 p
->setFont(headerFont
);
2834 headerHeight
= (p
->fontMetrics().lineSpacing() * 3) / 2;
2837 // ok. now print the pages.
2838 kDebug(6000) << "printing: html page width = " << root
->docWidth()
2839 << " height = " << root
->docHeight() << endl
;
2840 kDebug(6000) << "printing: margins left = " << printer
->margins().width()
2841 << " top = " << printer
->margins().height() << endl
;
2842 kDebug(6000) << "printing: paper width = " << metrics
.width()
2843 << " height = " << metrics
.height() << endl
;
2844 // if the width is too large to fit on the paper we just scale
2846 int pageWidth
= metrics
.width();
2847 int pageHeight
= metrics
.height();
2848 p
->setClipRect(0,0, pageWidth
, pageHeight
);
2850 pageHeight
-= headerHeight
;
2852 bool scalePage
= false;
2854 #ifndef QT_NO_TRANSFORMATIONS
2855 if(root
->docWidth() > metrics
.width()) {
2857 scale
= ((double) metrics
.width())/((double) root
->docWidth());
2858 pageHeight
= (int) (pageHeight
/scale
);
2859 pageWidth
= (int) (pageWidth
/scale
);
2860 headerHeight
= (int) (headerHeight
/scale
);
2863 kDebug(6000) << "printing: scaled html width = " << pageWidth
2864 << " height = " << pageHeight
<< endl
;
2866 root
->setHeight(pageHeight
);
2867 root
->setPageBottom(pageHeight
);
2868 root
->setNeedsLayout(true);
2869 root
->layoutIfNeeded();
2870 // m_part->slotDebugRenderTree();
2872 // Squeeze header to make it it on the page.
2875 int available_width
= metrics
.width() - 10 -
2876 2 * qMax(p
->boundingRect(0, 0, metrics
.width(), p
->fontMetrics().lineSpacing(), Qt::AlignLeft
, headerLeft
).width(),
2877 p
->boundingRect(0, 0, metrics
.width(), p
->fontMetrics().lineSpacing(), Qt::AlignLeft
, headerRight
).width());
2878 if (available_width
< 150)
2879 available_width
= 150;
2883 headerMid
= KStringHandler::csqueeze(docname
, squeeze
);
2884 mid_width
= p
->boundingRect(0, 0, metrics
.width(), p
->fontMetrics().lineSpacing(), Qt::AlignLeft
, headerMid
).width();
2886 } while (mid_width
> available_width
);
2892 while(top
< root
->docHeight()) {
2893 if(top
> 0) printer
->newPage();
2895 #warning "This could not be tested when merge was done, suspect"
2897 p
->setClipRect(0, 0, pageWidth
, headerHeight
);
2900 int dy
= p
->fontMetrics().lineSpacing();
2901 p
->setPen(Qt::black
);
2902 p
->setFont(headerFont
);
2904 headerRight
= QString("#%1").arg(page
);
2906 p
->drawText(0, 0, metrics
.width(), dy
, Qt::AlignLeft
, headerLeft
);
2907 p
->drawText(0, 0, metrics
.width(), dy
, Qt::AlignHCenter
, headerMid
);
2908 p
->drawText(0, 0, metrics
.width(), dy
, Qt::AlignRight
, headerRight
);
2912 #ifndef QT_NO_TRANSFORMATIONS
2914 p
->scale(scale
, scale
);
2918 #warning "This could not be tested when merge was done, suspect"
2920 p
->setClipRect(0, (int)(headerHeight
/scale
), (int)(pageWidth
/scale
), (int)(pageHeight
/scale
));
2921 p
->translate(0, headerHeight
-top
);
2923 bottom
= top
+pageHeight
;
2925 root
->setPageTop(top
);
2926 root
->setPageBottom(bottom
);
2927 root
->setPageNumber(page
);
2929 root
->layer()->paint(p
, QRect(0, top
, pageWidth
, pageHeight
));
2930 // m_part->xmlDocImpl()->renderer()->layer()->paint(p, QRect(0, top, pageWidth, pageHeight));
2933 kDebug(6000) << "printed: page " << page
<<" bottom At = " << bottom
<< endl
;
2943 // and now reset the layout to the usual one...
2944 root
->setPagedMode(false);
2945 root
->setStaticMode(false);
2947 khtml::setPrintPainter( 0 );
2948 setMediaType( oldMediaType
);
2949 m_part
->xmlDocImpl()->setPaintDevice( this );
2950 m_part
->xmlDocImpl()->styleSelector()->computeFontSizes(m_part
->xmlDocImpl()->paintDeviceMetrics(), m_part
->zoomFactor());
2951 m_part
->xmlDocImpl()->updateStyleSelector();
2952 viewport()->unsetCursor();
2957 void KHTMLView::slotPaletteChanged()
2959 if(!m_part
->xmlDocImpl()) return;
2960 DOM::DocumentImpl
*document
= m_part
->xmlDocImpl();
2961 if (!document
->isHTMLDocument()) return;
2962 khtml::RenderCanvas
*root
= static_cast<khtml::RenderCanvas
*>(document
->renderer());
2964 root
->style()->resetPalette();
2965 NodeImpl
*body
= static_cast<HTMLDocumentImpl
*>(document
)->body();
2967 body
->setChanged(true);
2968 body
->recalcStyle( NodeImpl::Force
);
2971 void KHTMLView::paint(QPainter
*p
, const QRect
&rc
, int yOff
, bool *more
)
2973 if(!m_part
->xmlDocImpl()) return;
2974 khtml::RenderCanvas
*root
= static_cast<khtml::RenderCanvas
*>(m_part
->xmlDocImpl()->renderer());
2977 m_part
->xmlDocImpl()->setPaintDevice(p
->device());
2978 root
->setPagedMode(true);
2979 root
->setStaticMode(true);
2980 root
->setWidth(rc
.width());
2984 p
->translate(rc
.left(), rc
.top());
2985 double scale
= ((double) rc
.width()/(double) root
->docWidth());
2986 int height
= (int) ((double) rc
.height() / scale
);
2987 #ifndef QT_NO_TRANSFORMATIONS
2988 p
->scale(scale
, scale
);
2990 root
->setPageTop(yOff
);
2991 root
->setPageBottom(yOff
+height
);
2993 root
->layer()->paint(p
, QRect(0, yOff
, root
->docWidth(), height
));
2995 *more
= yOff
+ height
< root
->docHeight();
2998 root
->setPagedMode(false);
2999 root
->setStaticMode(false);
3000 m_part
->xmlDocImpl()->setPaintDevice( this );
3004 void KHTMLView::useSlowRepaints()
3006 d
->useSlowRepaints
= true;
3007 // setStaticBackground(true); ### ?? FIXME
3011 void KHTMLView::setVerticalScrollBarPolicy( Qt::ScrollBarPolicy policy
)
3013 #ifndef KHTML_NO_SCROLLBARS
3014 d
->vpolicy
= policy
;
3015 QScrollArea::setVerticalScrollBarPolicy(policy
);
3021 void KHTMLView::setHorizontalScrollBarPolicy( Qt::ScrollBarPolicy policy
)
3023 #ifndef KHTML_NO_SCROLLBARS
3024 d
->hpolicy
= policy
;
3025 QScrollArea::setHorizontalScrollBarPolicy(policy
);
3031 void KHTMLView::restoreScrollBar()
3033 int ow
= visibleWidth();
3034 QScrollArea::setVerticalScrollBarPolicy(d
->vpolicy
);
3035 if (visibleWidth() != ow
)
3037 d
->prevScrollbarVisible
= verticalScrollBar()->isVisible();
3040 QStringList
KHTMLView::formCompletionItems(const QString
&name
) const
3042 if (!m_part
->settings()->isFormCompletionEnabled())
3043 return QStringList();
3044 if (!d
->formCompletions
)
3045 d
->formCompletions
= new KSimpleConfig(KStandardDirs::locateLocal("data", "khtml/formcompletions"));
3046 return d
->formCompletions
->readEntry(name
, QStringList());
3049 void KHTMLView::clearCompletionHistory(const QString
& name
)
3051 if (!d
->formCompletions
)
3053 d
->formCompletions
= new KSimpleConfig(KStandardDirs::locateLocal("data", "khtml/formcompletions"));
3055 d
->formCompletions
->writeEntry(name
, "");
3056 d
->formCompletions
->sync();
3059 void KHTMLView::addFormCompletionItem(const QString
&name
, const QString
&value
)
3061 if (!m_part
->settings()->isFormCompletionEnabled())
3063 // don't store values that are all numbers or just numbers with
3064 // dashes or spaces as those are likely credit card numbers or
3065 // something similar
3066 bool cc_number(true);
3067 for ( int i
= 0; i
< value
.length(); ++i
)
3070 if (!c
.isNumber() && c
!= '-' && !c
.isSpace())
3078 QStringList items
= formCompletionItems(name
);
3079 if (!items
.contains(value
))
3080 items
.prepend(value
);
3081 while ((int)items
.count() > m_part
->settings()->maxFormCompletionItems())
3082 items
.erase(items
.isEmpty() ? items
.end() : --items
.end());
3083 d
->formCompletions
->writeEntry(name
, items
);
3086 void KHTMLView::addNonPasswordStorableSite(const QString
& host
)
3088 if (!d
->formCompletions
) {
3089 d
->formCompletions
= new KSimpleConfig(KStandardDirs::locateLocal("data", "khtml/formcompletions"));
3092 d
->formCompletions
->setGroup("NonPasswordStorableSites");
3093 QStringList sites
= d
->formCompletions
->readEntry("Sites", QStringList());
3095 d
->formCompletions
->writeEntry("Sites", sites
);
3096 d
->formCompletions
->sync();
3097 d
->formCompletions
->setGroup(QString());//reset
3100 bool KHTMLView::nonPasswordStorableSite(const QString
& host
) const
3102 if (!d
->formCompletions
) {
3103 d
->formCompletions
= new KSimpleConfig(KStandardDirs::locateLocal("data", "khtml/formcompletions"));
3105 d
->formCompletions
->setGroup("NonPasswordStorableSites");
3106 QStringList sites
= d
->formCompletions
->readEntry("Sites", QStringList());
3107 d
->formCompletions
->setGroup(QString());//reset
3109 return (sites
.indexOf(host
) != -1);
3112 // returns true if event should be swallowed
3113 bool KHTMLView::dispatchMouseEvent(int eventId
, DOM::NodeImpl
*targetNode
,
3114 DOM::NodeImpl
*targetNodeNonShared
, bool cancelable
,
3115 int detail
,QMouseEvent
*_mouse
, bool setUnder
,
3116 int mouseEventType
, int orient
)
3118 // if the target node is a text node, dispatch on the parent node - rdar://4196646 (and #76948)
3119 if (targetNode
&& targetNode
->isTextNode())
3120 targetNode
= targetNode
->parentNode();
3123 d
->underMouse
->deref();
3124 d
->underMouse
= targetNode
;
3126 d
->underMouse
->ref();
3128 if (d
->underMouseNonShared
)
3129 d
->underMouseNonShared
->deref();
3130 d
->underMouseNonShared
= targetNodeNonShared
;
3131 if (d
->underMouseNonShared
)
3132 d
->underMouseNonShared
->ref();
3134 bool isWheelEvent
= (mouseEventType
== DOM::NodeImpl::MouseWheel
);
3136 int exceptioncode
= 0;
3137 int pageX
= _mouse
->x();
3138 int pageY
= _mouse
->y();
3139 int clientX
= pageX
- contentsX();
3140 int clientY
= pageY
- contentsY();
3141 int screenX
= _mouse
->globalX();
3142 int screenY
= _mouse
->globalY();
3144 switch (_mouse
->button()) {
3145 case Qt::LeftButton
:
3151 case Qt::RightButton
:
3157 if (d
->accessKeysEnabled
&& d
->accessKeysPreActivate
&& button
!=-1)
3158 d
->accessKeysPreActivate
=false;
3160 bool ctrlKey
= (_mouse
->modifiers() & Qt::ControlModifier
);
3161 bool altKey
= (_mouse
->modifiers() & Qt::AltModifier
);
3162 bool shiftKey
= (_mouse
->modifiers() & Qt::ShiftModifier
);
3163 bool metaKey
= (_mouse
->modifiers() & Qt::MetaModifier
);
3165 // mouseout/mouseover
3166 if (setUnder
&& d
->oldUnderMouse
!= targetNode
) {
3167 if (d
->oldUnderMouse
&& d
->oldUnderMouse
->getDocument() != targetNode
->getDocument()) {
3168 d
->oldUnderMouse
->deref();
3169 d
->oldUnderMouse
= 0;
3171 // send mouseout event to the old node
3172 if (d
->oldUnderMouse
) {
3173 // send mouseout event to the old node
3174 MouseEventImpl
*me
= new MouseEventImpl(EventImpl::MOUSEOUT_EVENT
,
3175 true,true,m_part
->xmlDocImpl()->defaultView(),
3176 0,screenX
,screenY
,clientX
,clientY
,pageX
, pageY
,
3177 ctrlKey
,altKey
,shiftKey
,metaKey
,
3180 d
->oldUnderMouse
->dispatchEvent(me
,exceptioncode
,true);
3183 // send mouseover event to the new node
3185 MouseEventImpl
*me
= new MouseEventImpl(EventImpl::MOUSEOVER_EVENT
,
3186 true,true,m_part
->xmlDocImpl()->defaultView(),
3187 0,screenX
,screenY
,clientX
,clientY
,pageX
, pageY
,
3188 ctrlKey
,altKey
,shiftKey
,metaKey
,
3189 button
,d
->oldUnderMouse
);
3192 targetNode
->dispatchEvent(me
,exceptioncode
,true);
3195 if (d
->oldUnderMouse
)
3196 d
->oldUnderMouse
->deref();
3197 d
->oldUnderMouse
= targetNode
;
3198 d
->oldUnderMouse
->ref();
3201 bool swallowEvent
= false;
3204 // send the actual event
3205 bool dblclick
= ( eventId
== EventImpl::CLICK_EVENT
&&
3206 _mouse
->type() == QEvent::MouseButtonDblClick
);
3207 MouseEventImpl
*me
= new MouseEventImpl(static_cast<EventImpl::EventId
>(eventId
),
3208 true,cancelable
,m_part
->xmlDocImpl()->defaultView(),
3209 detail
,screenX
,screenY
,clientX
,clientY
,pageX
, pageY
,
3210 ctrlKey
,altKey
,shiftKey
,metaKey
,
3211 button
,0, isWheelEvent
? 0 : _mouse
, dblclick
,
3212 isWheelEvent
? static_cast<MouseEventImpl::Orientation
>(orient
) : MouseEventImpl::ONone
);
3214 if ( !d
->m_mouseEventsTarget
&& RenderLayer::gScrollBar
&& eventId
== EventImpl::MOUSEDOWN_EVENT
)
3215 // button is pressed inside a layer scrollbar, so make it the target for future mousemove events until released
3216 d
->m_mouseEventsTarget
= RenderLayer::gScrollBar
;
3217 if ( d
->m_mouseEventsTarget
&& qobject_cast
<QScrollBar
*>(d
->m_mouseEventsTarget
) &&
3218 dynamic_cast<KHTMLWidget
*>(static_cast<QWidget
*>(d
->m_mouseEventsTarget
)) ) {
3219 // we have a sticky mouse event target and it is a layer's scrollbar. Forward events manually.
3220 // ### should use the dom
3221 KHTMLWidget
*w
= dynamic_cast<KHTMLWidget
*>(static_cast<QWidget
*>(d
->m_mouseEventsTarget
));
3222 QPoint p
= w
->m_kwp
->absolutePos();
3223 QMouseEvent
fw(_mouse
->type(), _mouse
->pos()-p
, _mouse
->button(), _mouse
->buttons(), _mouse
->modifiers());
3224 static_cast<RenderWidget::EventPropagator
*>(static_cast<QWidget
*>(d
->m_mouseEventsTarget
))->sendEvent(&fw
);
3225 if (_mouse
->type() == QMouseEvent::MouseButtonPress
&& _mouse
->button() == Qt::RightButton
) {
3226 QContextMenuEvent
cme(QContextMenuEvent::Mouse
, p
);
3227 static_cast<RenderWidget::EventPropagator
*>(static_cast<QWidget
*>(d
->m_mouseEventsTarget
))->sendEvent(&cme
);
3229 swallowEvent
= true;
3231 targetNode
->dispatchEvent(me
,exceptioncode
,true);
3232 bool defaultHandled
= me
->defaultHandled();
3233 if (defaultHandled
|| me
->defaultPrevented())
3234 swallowEvent
= true;
3238 if (eventId
== EventImpl::MOUSEDOWN_EVENT
) {
3239 // Focus should be shifted on mouse down, not on a click. -dwh
3240 // Blur current focus node when a link/button is clicked; this
3241 // is expected by some sites that rely on onChange handlers running
3242 // from form fields before the button click is processed.
3243 DOM::NodeImpl
* nodeImpl
= targetNode
;
3244 for ( ; nodeImpl
&& !nodeImpl
->isFocusable(); nodeImpl
= nodeImpl
->parentNode());
3245 if (nodeImpl
&& nodeImpl
->isMouseFocusable())
3246 m_part
->xmlDocImpl()->setFocusNode(nodeImpl
);
3247 else if (!nodeImpl
|| !nodeImpl
->focused())
3248 m_part
->xmlDocImpl()->setFocusNode(0);
3252 return swallowEvent
;
3255 void KHTMLView::setIgnoreWheelEvents( bool e
)
3257 d
->ignoreWheelEvents
= e
;
3260 #ifndef QT_NO_WHEELEVENT
3262 void KHTMLView::wheelEvent(QWheelEvent
* e
)
3264 if (d
->accessKeysEnabled
&& d
->accessKeysPreActivate
) d
->accessKeysPreActivate
=false;
3266 if ( ( e
->modifiers() & Qt::ControlModifier
) == Qt::ControlModifier
)
3268 emit
zoomView( - e
->delta() );
3271 else if (d
->firstRelayout
)
3275 else if( ( (e
->orientation() == Qt::Vertical
&&
3276 ((d
->ignoreWheelEvents
&& !verticalScrollBar()->isVisible())
3277 || e
->delta() > 0 && contentsY() <= 0
3278 || e
->delta() < 0 && contentsY() >= contentsHeight() - visibleHeight()))
3280 (e
->orientation() == Qt::Horizontal
&&
3281 ((d
->ignoreWheelEvents
&& !horizontalScrollBar()->isVisible())
3282 || e
->delta() > 0 && contentsX() <=0
3283 || e
->delta() < 0 && contentsX() >= contentsWidth() - visibleWidth())))
3284 && m_part
->parentPart())
3286 if ( m_part
->parentPart()->view() )
3287 m_part
->parentPart()->view()->wheelEvent( e
);
3292 DOM::NodeImpl::MouseEvent
mev( e
->buttons(), DOM::NodeImpl::MouseWheel
);
3293 m_part
->xmlDocImpl()->prepareMouseEvent( false, e
->x(), e
->y(), &mev
);
3295 MouseEventImpl::Orientation o
= MouseEventImpl::OVertical
;
3296 if (e
->orientation() == Qt::Horizontal
)
3297 o
= MouseEventImpl::OHorizontal
;
3299 QMouseEvent
_mouse(QEvent::MouseMove
, e
->pos(), Qt::NoButton
, e
->buttons(), e
->modifiers());
3300 bool swallow
= dispatchMouseEvent(EventImpl::KHTML_MOUSEWHEEL_EVENT
,mev
.innerNode
.handle(),mev
.innerNonSharedNode
.handle(),
3301 true,-e
->delta()/40,&_mouse
,true,DOM::NodeImpl::MouseWheel
,o
);
3305 d
->scrollBarMoved
= true;
3306 QScrollArea::wheelEvent( e
);
3312 void KHTMLView::dragEnterEvent( QDragEnterEvent
* ev
)
3314 // Handle drops onto frames (#16820)
3315 // Drops on the main html part is handled by Konqueror (and shouldn't do anything
3316 // in e.g. kmail, so not handled here).
3317 if ( m_part
->parentPart() )
3319 QApplication::sendEvent(m_part
->parentPart()->widget(), ev
);
3322 QScrollArea::dragEnterEvent( ev
);
3325 void KHTMLView::dropEvent( QDropEvent
*ev
)
3327 // Handle drops onto frames (#16820)
3328 // Drops on the main html part is handled by Konqueror (and shouldn't do anything
3329 // in e.g. kmail, so not handled here).
3330 if ( m_part
->parentPart() )
3332 QApplication::sendEvent(m_part
->parentPart()->widget(), ev
);
3335 QScrollArea::dropEvent( ev
);
3338 void KHTMLView::focusInEvent( QFocusEvent
*e
)
3340 #ifndef KHTML_NO_TYPE_AHEAD_FIND
3341 m_part
->enableFindAheadActions( true );
3343 DOM::NodeImpl
* fn
= m_part
->xmlDocImpl() ? m_part
->xmlDocImpl()->focusNode() : 0;
3344 if (fn
&& fn
->renderer() && fn
->renderer()->isWidget() &&
3345 (e
->reason() != Qt::MouseFocusReason
) &&
3346 static_cast<khtml::RenderWidget
*>(fn
->renderer())->widget())
3347 static_cast<khtml::RenderWidget
*>(fn
->renderer())->widget()->setFocus();
3348 #ifndef KHTML_NO_CARET
3349 // Restart blink frequency timer if it has been killed, but only on
3351 if (d
->m_caretViewContext
&&
3352 d
->m_caretViewContext
->freqTimerId
== -1 &&
3354 if (m_part
->isCaretMode()
3355 || m_part
->isEditable()
3356 || (fn
&& fn
->renderer()
3357 && fn
->renderer()->style()->userInput()
3359 d
->m_caretViewContext
->freqTimerId
= startTimer(500);
3360 d
->m_caretViewContext
->visible
= true;
3364 #endif // KHTML_NO_CARET
3365 QScrollArea::focusInEvent( e
);
3368 void KHTMLView::focusOutEvent( QFocusEvent
*e
)
3370 m_part
->stopAutoScroll();
3372 #ifndef KHTML_NO_TYPE_AHEAD_FIND
3373 if(d
->typeAheadActivated
)
3377 m_part
->enableFindAheadActions( false );
3378 #endif // KHTML_NO_TYPE_AHEAD_FIND
3380 #ifndef KHTML_NO_CARET
3381 if (d
->m_caretViewContext
) {
3382 switch (d
->m_caretViewContext
->displayNonFocused
) {
3383 case KHTMLPart::CaretInvisible
:
3386 case KHTMLPart::CaretVisible
: {
3387 if (d
->m_caretViewContext
->freqTimerId
!= -1)
3388 killTimer(d
->m_caretViewContext
->freqTimerId
);
3389 d
->m_caretViewContext
->freqTimerId
= -1;
3390 NodeImpl
*caretNode
= m_part
->xmlDocImpl()->focusNode();
3391 if (!d
->m_caretViewContext
->visible
&& (m_part
->isCaretMode()
3392 || m_part
->isEditable()
3393 || (caretNode
&& caretNode
->renderer()
3394 && caretNode
->renderer()->style()->userInput()
3396 d
->m_caretViewContext
->visible
= true;
3401 case KHTMLPart::CaretBlink
:
3402 // simply leave as is
3406 #endif // KHTML_NO_CARET
3408 if ( d
->cursor_icon_widget
)
3409 d
->cursor_icon_widget
->hide();
3411 QScrollArea::focusOutEvent( e
);
3414 void KHTMLView::scrollContentsBy( int dx
, int dy
)
3416 if ( !d
->firstRelayout
&& !d
->complete
&& m_part
->xmlDocImpl() &&
3417 d
->layoutSchedulingEnabled
) {
3418 // contents scroll while we are not complete: we need to check our layout *now*
3419 khtml::RenderCanvas
* root
= static_cast<khtml::RenderCanvas
*>( m_part
->xmlDocImpl()->renderer() );
3420 if (root
&& root
->needsLayout()) {
3421 unscheduleRelayout();
3425 if (!d
->scrollingSelf
) {
3426 d
->scrollBarMoved
= true;
3427 d
->contentsMoving
= true;
3428 // ensure quick reset of contentsMoving flag
3429 scheduleRepaint(0, 0, 0, 0);
3432 if (m_part
->xmlDocImpl() && m_part
->xmlDocImpl()->documentElement())
3433 m_part
->xmlDocImpl()->documentElement()->dispatchHTMLEvent(EventImpl::SCROLL_EVENT
, true, false);
3435 d
->contentsX
= QApplication::isRightToLeft() ?
3436 horizontalScrollBar()->maximum()-horizontalScrollBar()->value() : horizontalScrollBar()->value();
3437 d
->contentsY
= verticalScrollBar()->value();
3439 if (d
->useSlowRepaints
) {
3440 widget()->blockSignals( true );
3441 widget()->move( widget()->pos().x() + dx
, widget()->pos().y() +dy
);
3442 widget()->blockSignals( false );
3443 widget()->repaint();
3446 QScrollArea::scrollContentsBy(dx
, dy
);
3449 void KHTMLView::timerEvent ( QTimerEvent
*e
)
3451 // kDebug() << "timer event " << e->timerId() << endl;
3452 if ( e
->timerId() == d
->scrollTimerId
) {
3453 if( d
->scrollSuspended
)
3455 switch (d
->scrollDirection
) {
3456 case KHTMLViewPrivate::ScrollDown
:
3457 if (contentsY() + visibleHeight () >= contentsHeight())
3458 d
->newScrollTimer(this, 0);
3460 verticalScrollBar()->setValue( verticalScrollBar()->value() +d
->scrollBy
);
3462 case KHTMLViewPrivate::ScrollUp
:
3463 if (contentsY() <= 0)
3464 d
->newScrollTimer(this, 0);
3466 verticalScrollBar()->setValue( verticalScrollBar()->value() -d
->scrollBy
);
3468 case KHTMLViewPrivate::ScrollRight
:
3469 if (contentsX() + visibleWidth () >= contentsWidth())
3470 d
->newScrollTimer(this, 0);
3472 horizontalScrollBar()->setValue( horizontalScrollBar()->value() +d
->scrollBy
);
3474 case KHTMLViewPrivate::ScrollLeft
:
3475 if (contentsX() <= 0)
3476 d
->newScrollTimer(this, 0);
3478 horizontalScrollBar()->setValue( horizontalScrollBar()->value() -d
->scrollBy
);
3483 else if ( e
->timerId() == d
->layoutTimerId
) {
3484 d
->dirtyLayout
= true;
3486 if (d
->firstRelayout
) {
3487 d
->firstRelayout
= false;
3488 verticalScrollBar()->setEnabled( true );
3489 horizontalScrollBar()->setEnabled( true );
3492 #ifndef KHTML_NO_CARET
3493 else if (d
->m_caretViewContext
3494 && e
->timerId() == d
->m_caretViewContext
->freqTimerId
) {
3495 d
->m_caretViewContext
->visible
= !d
->m_caretViewContext
->visible
;
3496 if (d
->m_caretViewContext
->displayed
) {
3497 updateContents(d
->m_caretViewContext
->x
, d
->m_caretViewContext
->y
,
3498 d
->m_caretViewContext
->width
,
3499 d
->m_caretViewContext
->height
);
3501 // if (d->m_caretViewContext->visible) cout << "|" << flush;
3502 // else cout << "" << flush;
3507 d
->contentsMoving
= false;
3508 if( m_part
->xmlDocImpl() ) {
3509 DOM::DocumentImpl
*document
= m_part
->xmlDocImpl();
3510 khtml::RenderCanvas
* root
= static_cast<khtml::RenderCanvas
*>(document
->renderer());
3512 if ( root
&& root
->needsLayout() ) {
3513 if (d
->repaintTimerId
)
3514 killTimer(d
->repaintTimerId
);
3515 d
->repaintTimerId
= 0;
3521 // setStaticBackground(d->useSlowRepaints); ?? ### FIXME
3523 // kDebug() << "scheduled repaint "<< d->repaintTimerId << endl;
3524 if (d
->repaintTimerId
)
3525 killTimer(d
->repaintTimerId
);
3526 d
->repaintTimerId
= 0;
3529 QVector
<QRect
> rects
= d
->updateRegion
.rects();
3531 d
->updateRegion
= QRegion();
3534 updateRegion
= rects
[0];
3536 for ( int i
= 1; i
< rects
.size(); ++i
) {
3537 QRect newRegion
= updateRegion
.unite(rects
[i
]);
3538 if (2*newRegion
.height() > 3*updateRegion
.height() )
3540 repaintContents( updateRegion
);
3541 updateRegion
= rects
[i
];
3544 updateRegion
= newRegion
;
3547 if ( !updateRegion
.isNull() )
3548 repaintContents( updateRegion
);
3550 // As widgets can only be accurately positioned during painting, every layout might
3551 // dissociate a widget from its RenderWidget. E.g: if a RenderWidget was visible before layout, but the layout
3552 // pushed it out of the viewport, it will not be repainted, and consequently it's associated widget won't be repositioned.
3553 // 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.
3555 if (d
->dirtyLayout
&& !d
->visibleWidgets
.isEmpty()) {
3557 d
->dirtyLayout
= false;
3559 QRect
visibleRect(contentsX(), contentsY(), visibleWidth(), visibleHeight());
3560 QList
<RenderWidget
*> toRemove
;
3561 for (Q3PtrDictIterator
<QWidget
> it(d
->visibleWidgets
); it
.current(); ++it
) {
3564 RenderWidget
* rw
= static_cast<RenderWidget
*>( it
.currentKey() );
3565 if (!rw
->absolutePosition(xp
, yp
) ||
3566 !visibleRect
.intersects(QRect(xp
, yp
, w
->width(), w
->height())))
3567 toRemove
.append(rw
);
3570 foreach (RenderWidget
* r
, toRemove
)
3571 if ( (w
= d
->visibleWidgets
.take(r
) ) )
3572 w
->move( 0, -500000);
3575 emit
repaintAccessKeys();
3576 if (d
->emitCompletedAfterRepaint
) {
3577 bool full
= d
->emitCompletedAfterRepaint
== KHTMLViewPrivate::CSFull
;
3578 d
->emitCompletedAfterRepaint
= KHTMLViewPrivate::CSNone
;
3580 emit m_part
->completed();
3582 emit m_part
->completed(true);
3586 void KHTMLView::scheduleRelayout(khtml::RenderObject
* /*clippedObj*/)
3588 if (!d
->layoutSchedulingEnabled
|| d
->layoutTimerId
)
3591 d
->layoutTimerId
= startTimer( m_part
->xmlDocImpl() && m_part
->xmlDocImpl()->parsing()
3595 void KHTMLView::unscheduleRelayout()
3597 if (!d
->layoutTimerId
)
3600 killTimer(d
->layoutTimerId
);
3601 d
->layoutTimerId
= 0;
3604 void KHTMLView::unscheduleRepaint()
3606 if (!d
->repaintTimerId
)
3609 killTimer(d
->repaintTimerId
);
3610 d
->repaintTimerId
= 0;
3613 void KHTMLView::scheduleRepaint(int x
, int y
, int w
, int h
, bool asap
)
3615 bool parsing
= !m_part
->xmlDocImpl() || m_part
->xmlDocImpl()->parsing();
3617 // kDebug() << "parsing " << parsing << endl;
3618 // kDebug() << "complete " << d->complete << endl;
3620 int time
= parsing
? 300 : (!asap
? ( !d
->complete
? 100 : 20 ) : 0);
3622 #ifdef DEBUG_FLICKER
3624 p
.begin( viewport() );
3627 contentsToViewport( x
, y
, vx
, vy
);
3628 p
.fillRect( vx
, vy
, w
, h
, Qt::red
);
3632 d
->updateRegion
= d
->updateRegion
.unite(QRect(x
,y
,w
,h
));
3634 if (asap
&& !parsing
)
3635 unscheduleRepaint();
3637 if ( !d
->repaintTimerId
)
3638 d
->repaintTimerId
= startTimer( time
);
3640 // kDebug() << "starting timer " << time << endl;
3643 void KHTMLView::complete( bool pendingAction
)
3645 // kDebug() << "KHTMLView::complete()" << endl;
3649 // is there a relayout pending?
3650 if (d
->layoutTimerId
)
3652 // kDebug() << "requesting relayout now" << endl;
3654 killTimer(d
->layoutTimerId
);
3655 d
->layoutTimerId
= startTimer( 0 );
3656 d
->emitCompletedAfterRepaint
= pendingAction
?
3657 KHTMLViewPrivate::CSActionPending
: KHTMLViewPrivate::CSFull
;
3660 // is there a repaint pending?
3661 if (d
->repaintTimerId
)
3663 // kDebug() << "requesting repaint now" << endl;
3665 killTimer(d
->repaintTimerId
);
3666 d
->repaintTimerId
= startTimer( 20 );
3667 d
->emitCompletedAfterRepaint
= pendingAction
?
3668 KHTMLViewPrivate::CSActionPending
: KHTMLViewPrivate::CSFull
;
3671 if (!d
->emitCompletedAfterRepaint
)
3674 emit m_part
->completed();
3676 emit m_part
->completed(true);
3681 void KHTMLView::slotMouseScrollTimer()
3683 horizontalScrollBar()->setValue( horizontalScrollBar()->value() +d
->m_mouseScroll_byX
);
3684 verticalScrollBar()->setValue( verticalScrollBar()->value() +d
->m_mouseScroll_byY
);
3687 #ifndef KHTML_NO_CARET
3689 // ### the dependencies on static functions are a nightmare. just be
3690 // hacky and include the implementation here. Clean me up, please.
3692 #include "khtml_caret.cpp"
3694 void KHTMLView::initCaret(bool keepSelection
)
3696 #if DEBUG_CARETMODE > 0
3697 kDebug(6200) << "begin initCaret" << endl
;
3699 // save caretMoved state as moveCaretTo changes it
3700 if (m_part
->xmlDocImpl()) {
3702 ElementImpl
*listitem
= m_part
->xmlDocImpl()->getElementById("__test_element__");
3703 if (listitem
) dumpLineBoxes(static_cast<RenderFlow
*>(listitem
->renderer()));
3705 d
->caretViewContext();
3706 bool cmoved
= d
->m_caretViewContext
->caretMoved
;
3707 if (m_part
->d
->caretNode().isNull()) {
3708 // set to document, position will be sanitized anyway
3709 m_part
->d
->caretNode() = m_part
->document();
3710 m_part
->d
->caretOffset() = 0L;
3711 // This sanity check is necessary for the not so unlikely case that
3712 // setEditable or setCaretMode is called before any render objects have
3714 if (!m_part
->d
->caretNode().handle()->renderer()) return;
3716 // kDebug(6200) << "d->m_selectionStart " << m_part->d->m_selectionStart.handle()
3717 // << " d->m_selectionEnd " << m_part->d->m_selectionEnd.handle() << endl;
3718 // ### does not repaint the selection on keepSelection!=false
3719 moveCaretTo(m_part
->d
->caretNode().handle(), m_part
->d
->caretOffset(), !keepSelection
);
3720 // kDebug(6200) << "d->m_selectionStart " << m_part->d->m_selectionStart.handle()
3721 // << " d->m_selectionEnd " << m_part->d->m_selectionEnd.handle() << endl;
3722 d
->m_caretViewContext
->caretMoved
= cmoved
;
3724 #if DEBUG_CARETMODE > 0
3725 kDebug(6200) << "end initCaret" << endl
;
3729 bool KHTMLView::caretOverrides() const
3731 bool cm
= m_part
->isCaretMode();
3732 bool dm
= m_part
->isEditable();
3733 return cm
&& !dm
? false
3734 : (dm
|| m_part
->d
->caretNode().handle()->contentEditable())
3735 && d
->editorContext()->override
;
3738 void KHTMLView::ensureNodeHasFocus(NodeImpl
*node
)
3740 if (m_part
->isCaretMode() || m_part
->isEditable()) return;
3741 if (node
->focused()) return;
3743 // Find first ancestor whose "user-input" is "enabled"
3744 NodeImpl
*firstAncestor
= 0;
3746 if (node
->renderer()
3747 && node
->renderer()->style()->userInput() != UI_ENABLED
)
3749 firstAncestor
= node
;
3750 node
= node
->parentNode();
3753 if (!node
) firstAncestor
= 0;
3755 DocumentImpl
*doc
= m_part
->xmlDocImpl();
3756 // ensure that embedded widgets don't lose their focus
3757 if (!firstAncestor
&& doc
->focusNode() && doc
->focusNode()->renderer()
3758 && doc
->focusNode()->renderer()->isWidget())
3761 // Set focus node on the document
3762 #if DEBUG_CARETMODE > 1
3763 kDebug(6200) << k_funcinfo
<< "firstAncestor " << firstAncestor
<< ": "
3764 << (firstAncestor
? firstAncestor
->nodeName().string() : QString()) << endl
;
3766 doc
->setFocusNode(firstAncestor
);
3767 emit m_part
->nodeActivated(Node(firstAncestor
));
3770 void KHTMLView::recalcAndStoreCaretPos(CaretBox
*hintBox
)
3772 if (!m_part
|| m_part
->d
->caretNode().isNull()) return;
3773 d
->caretViewContext();
3774 NodeImpl
*caretNode
= m_part
->d
->caretNode().handle();
3775 #if DEBUG_CARETMODE > 0
3776 kDebug(6200) << "recalcAndStoreCaretPos: caretNode=" << caretNode
<< (caretNode
? " "+caretNode
->nodeName().string() : QString()) << " r@" << caretNode
->renderer() << (caretNode
->renderer() && caretNode
->renderer()->isText() ? " \"" + QConstString(static_cast<RenderText
*>(caretNode
->renderer())->str
->s
, qMin(static_cast<RenderText
*>(caretNode
->renderer())->str
->l
, 15u)).string() + "\"" : QString()) << endl
;
3778 caretNode
->getCaret(m_part
->d
->caretOffset(), caretOverrides(),
3779 d
->m_caretViewContext
->x
, d
->m_caretViewContext
->y
,
3780 d
->m_caretViewContext
->width
,
3781 d
->m_caretViewContext
->height
);
3783 if (hintBox
&& d
->m_caretViewContext
->x
== -1) {
3784 #if DEBUG_CARETMODE > 1
3785 kDebug(6200) << "using hint inline box coordinates" << endl
;
3787 RenderObject
*r
= caretNode
->renderer();
3788 const QFontMetrics
&fm
= r
->style()->fontMetrics();
3790 r
->containingBlock()->absolutePosition(absx
, absy
,
3791 false); // ### what about fixed?
3792 d
->m_caretViewContext
->x
= absx
+ hintBox
->xPos();
3793 d
->m_caretViewContext
->y
= absy
+ hintBox
->yPos();
3794 // + hintBox->baseline() - fm.ascent();
3795 d
->m_caretViewContext
->width
= 1;
3796 // ### firstline not regarded. But I think it can be safely neglected
3797 // as hint boxes are only used for empty lines.
3798 d
->m_caretViewContext
->height
= fm
.height();
3801 #if DEBUG_CARETMODE > 4
3802 // kDebug(6200) << "freqTimerId: "<<d->m_caretViewContext->freqTimerId<<endl;
3804 #if DEBUG_CARETMODE > 0
3805 kDebug(6200) << "caret: ofs="<<m_part
->d
->caretOffset()<<" "
3806 <<" x="<<d
->m_caretViewContext
->x
<<" y="<<d
->m_caretViewContext
->y
3807 <<" h="<<d
->m_caretViewContext
->height
<<endl
;
3811 void KHTMLView::caretOn()
3813 if (d
->m_caretViewContext
) {
3814 if (d
->m_caretViewContext
->freqTimerId
!= -1)
3815 killTimer(d
->m_caretViewContext
->freqTimerId
);
3817 if (hasFocus() || d
->m_caretViewContext
->displayNonFocused
3818 == KHTMLPart::CaretBlink
) {
3819 d
->m_caretViewContext
->freqTimerId
= startTimer(500);
3821 d
->m_caretViewContext
->freqTimerId
= -1;
3824 d
->m_caretViewContext
->visible
= true;
3825 if ((d
->m_caretViewContext
->displayed
= (hasFocus()
3826 || d
->m_caretViewContext
->displayNonFocused
3827 != KHTMLPart::CaretInvisible
))) {
3828 updateContents(d
->m_caretViewContext
->x
, d
->m_caretViewContext
->y
,
3829 d
->m_caretViewContext
->width
,
3830 d
->m_caretViewContext
->height
);
3832 // kDebug(6200) << "caret on" << endl;
3836 void KHTMLView::caretOff()
3838 if (d
->m_caretViewContext
) {
3839 if (d
->m_caretViewContext
->freqTimerId
!= -1)
3840 killTimer(d
->m_caretViewContext
->freqTimerId
);
3841 d
->m_caretViewContext
->freqTimerId
= -1;
3842 d
->m_caretViewContext
->displayed
= false;
3843 if (d
->m_caretViewContext
->visible
) {
3844 d
->m_caretViewContext
->visible
= false;
3845 updateContents(d
->m_caretViewContext
->x
, d
->m_caretViewContext
->y
,
3846 d
->m_caretViewContext
->width
,
3847 d
->m_caretViewContext
->height
);
3849 // kDebug(6200) << "caret off" << endl;
3853 void KHTMLView::showCaret(bool forceRepaint
)
3855 if (d
->m_caretViewContext
) {
3856 d
->m_caretViewContext
->displayed
= true;
3857 if (d
->m_caretViewContext
->visible
) {
3858 if (!forceRepaint
) {
3859 updateContents(d
->m_caretViewContext
->x
, d
->m_caretViewContext
->y
,
3860 d
->m_caretViewContext
->width
,
3861 d
->m_caretViewContext
->height
);
3863 repaintContents(d
->m_caretViewContext
->x
, d
->m_caretViewContext
->y
,
3864 d
->m_caretViewContext
->width
,
3865 d
->m_caretViewContext
->height
);
3868 // kDebug(6200) << "caret shown" << endl;
3872 bool KHTMLView::foldSelectionToCaret(NodeImpl
*startNode
, long startOffset
,
3873 NodeImpl
*endNode
, long endOffset
)
3875 m_part
->d
->m_selectionStart
= m_part
->d
->m_selectionEnd
= m_part
->d
->caretNode();
3876 m_part
->d
->m_startOffset
= m_part
->d
->m_endOffset
= m_part
->d
->caretOffset();
3877 m_part
->d
->m_extendAtEnd
= true;
3879 bool folded
= startNode
!= endNode
|| startOffset
!= endOffset
;
3881 // Only clear the selection if there has been one.
3883 m_part
->xmlDocImpl()->clearSelection();
3889 void KHTMLView::hideCaret()
3891 if (d
->m_caretViewContext
) {
3892 if (d
->m_caretViewContext
->visible
) {
3893 // kDebug(6200) << "redraw caret hidden" << endl;
3894 d
->m_caretViewContext
->visible
= false;
3895 // force repaint, otherwise the event won't be handled
3896 // before the focus leaves the window
3897 repaintContents(d
->m_caretViewContext
->x
, d
->m_caretViewContext
->y
,
3898 d
->m_caretViewContext
->width
,
3899 d
->m_caretViewContext
->height
);
3900 d
->m_caretViewContext
->visible
= true;
3902 d
->m_caretViewContext
->displayed
= false;
3903 // kDebug(6200) << "caret hidden" << endl;
3907 int KHTMLView::caretDisplayPolicyNonFocused() const
3909 if (d
->m_caretViewContext
)
3910 return d
->m_caretViewContext
->displayNonFocused
;
3912 return KHTMLPart::CaretInvisible
;
3915 void KHTMLView::setCaretDisplayPolicyNonFocused(int policy
)
3917 d
->caretViewContext();
3918 // int old = d->m_caretViewContext->displayNonFocused;
3919 d
->m_caretViewContext
->displayNonFocused
= (KHTMLPart::CaretDisplayPolicy
)policy
;
3921 // make change immediately take effect if not focused
3923 switch (d
->m_caretViewContext
->displayNonFocused
) {
3924 case KHTMLPart::CaretInvisible
:
3927 case KHTMLPart::CaretBlink
:
3928 if (d
->m_caretViewContext
->freqTimerId
!= -1) break;
3929 d
->m_caretViewContext
->freqTimerId
= startTimer(500);
3931 case KHTMLPart::CaretVisible
:
3932 d
->m_caretViewContext
->displayed
= true;
3939 bool KHTMLView::placeCaret(CaretBox
*hintBox
)
3941 CaretViewContext
*cv
= d
->caretViewContext();
3943 NodeImpl
*caretNode
= m_part
->d
->caretNode().handle();
3944 // ### why is it sometimes null?
3945 if (!caretNode
|| !caretNode
->renderer()) return false;
3946 ensureNodeHasFocus(caretNode
);
3947 if (m_part
->isCaretMode() || m_part
->isEditable()
3948 || caretNode
->renderer()->style()->userInput() == UI_ENABLED
) {
3949 recalcAndStoreCaretPos(hintBox
);
3959 void KHTMLView::ensureCaretVisible()
3961 CaretViewContext
*cv
= d
->m_caretViewContext
;
3963 ensureVisible(cv
->x
, cv
->y
, cv
->width
, cv
->height
);
3964 d
->scrollBarMoved
= false;
3967 bool KHTMLView::extendSelection(NodeImpl
*oldStartSel
, long oldStartOfs
,
3968 NodeImpl
*oldEndSel
, long oldEndOfs
)
3970 bool changed
= false;
3971 if (m_part
->d
->m_selectionStart
== m_part
->d
->m_selectionEnd
3972 && m_part
->d
->m_startOffset
== m_part
->d
->m_endOffset
) {
3973 changed
= foldSelectionToCaret(oldStartSel
, oldStartOfs
, oldEndSel
, oldEndOfs
);
3974 m_part
->d
->m_extendAtEnd
= true;
3976 changed
= m_part
->d
->m_selectionStart
.handle() != oldStartSel
3977 || m_part
->d
->m_startOffset
!= oldStartOfs
3978 || m_part
->d
->m_selectionEnd
.handle() != oldEndSel
3979 || m_part
->d
->m_endOffset
!= oldEndOfs
;
3980 if (!changed
) break;
3982 // determine start position -- caret position is always at end.
3983 NodeImpl
*startNode
;
3985 if (m_part
->d
->m_extendAtEnd
) {
3986 startNode
= m_part
->d
->m_selectionStart
.handle();
3987 startOffset
= m_part
->d
->m_startOffset
;
3989 startNode
= m_part
->d
->m_selectionEnd
.handle();
3990 startOffset
= m_part
->d
->m_endOffset
;
3991 m_part
->d
->m_selectionEnd
= m_part
->d
->m_selectionStart
;
3992 m_part
->d
->m_endOffset
= m_part
->d
->m_startOffset
;
3993 m_part
->d
->m_extendAtEnd
= true;
3996 bool swapNeeded
= false;
3997 if (!m_part
->d
->m_selectionEnd
.isNull() && startNode
) {
3998 swapNeeded
= RangeImpl::compareBoundaryPoints(startNode
, startOffset
,
3999 m_part
->d
->m_selectionEnd
.handle(),
4000 m_part
->d
->m_endOffset
) >= 0;
4003 m_part
->d
->m_selectionStart
= startNode
;
4004 m_part
->d
->m_startOffset
= startOffset
;
4007 m_part
->xmlDocImpl()->setSelection(m_part
->d
->m_selectionEnd
.handle(),
4008 m_part
->d
->m_endOffset
, m_part
->d
->m_selectionStart
.handle(),
4009 m_part
->d
->m_startOffset
);
4011 m_part
->xmlDocImpl()->setSelection(m_part
->d
->m_selectionStart
.handle(),
4012 m_part
->d
->m_startOffset
, m_part
->d
->m_selectionEnd
.handle(),
4013 m_part
->d
->m_endOffset
);
4015 } while(false);/*end if*/
4019 void KHTMLView::updateSelection(NodeImpl
*oldStartSel
, long oldStartOfs
,
4020 NodeImpl
*oldEndSel
, long oldEndOfs
)
4022 if (m_part
->d
->m_selectionStart
== m_part
->d
->m_selectionEnd
4023 && m_part
->d
->m_startOffset
== m_part
->d
->m_endOffset
) {
4024 if (foldSelectionToCaret(oldStartSel
, oldStartOfs
, oldEndSel
, oldEndOfs
)) {
4025 m_part
->emitSelectionChanged();
4027 m_part
->d
->m_extendAtEnd
= true;
4029 // check if the extending end has passed the immobile end
4030 if (!m_part
->d
->m_selectionEnd
.isNull() && !m_part
->d
->m_selectionEnd
.isNull()) {
4031 bool swapNeeded
= RangeImpl::compareBoundaryPoints(
4032 m_part
->d
->m_selectionStart
.handle(), m_part
->d
->m_startOffset
,
4033 m_part
->d
->m_selectionEnd
.handle(), m_part
->d
->m_endOffset
) >= 0;
4035 DOM::Node tmpNode
= m_part
->d
->m_selectionStart
;
4036 long tmpOffset
= m_part
->d
->m_startOffset
;
4037 m_part
->d
->m_selectionStart
= m_part
->d
->m_selectionEnd
;
4038 m_part
->d
->m_startOffset
= m_part
->d
->m_endOffset
;
4039 m_part
->d
->m_selectionEnd
= tmpNode
;
4040 m_part
->d
->m_endOffset
= tmpOffset
;
4041 m_part
->d
->m_startBeforeEnd
= true;
4042 m_part
->d
->m_extendAtEnd
= !m_part
->d
->m_extendAtEnd
;
4046 m_part
->xmlDocImpl()->setSelection(m_part
->d
->m_selectionStart
.handle(),
4047 m_part
->d
->m_startOffset
, m_part
->d
->m_selectionEnd
.handle(),
4048 m_part
->d
->m_endOffset
);
4049 m_part
->emitSelectionChanged();
4053 void KHTMLView::caretKeyPressEvent(QKeyEvent
*_ke
)
4055 NodeImpl
*oldStartSel
= m_part
->d
->m_selectionStart
.handle();
4056 long oldStartOfs
= m_part
->d
->m_startOffset
;
4057 NodeImpl
*oldEndSel
= m_part
->d
->m_selectionEnd
.handle();
4058 long oldEndOfs
= m_part
->d
->m_endOffset
;
4060 NodeImpl
*oldCaretNode
= m_part
->d
->caretNode().handle();
4061 long oldOffset
= m_part
->d
->caretOffset();
4063 bool ctrl
= _ke
->modifiers() & Qt::ControlModifier
;
4065 // FIXME: this is that widely indented because I will write ifs around it.
4066 switch(_ke
->key()) {
4071 moveCaretNextLine(1);
4075 moveCaretPrevLine(1);
4079 moveCaretBy(false, ctrl
? CaretByWord
: CaretByCharacter
, 1);
4083 moveCaretBy(true, ctrl
? CaretByWord
: CaretByCharacter
, 1);
4086 case Qt::Key_PageDown
:
4087 moveCaretNextPage();
4090 case Qt::Key_PageUp
:
4091 moveCaretPrevPage();
4096 moveCaretToDocumentBoundary(false);
4098 moveCaretToLineBegin();
4103 moveCaretToDocumentBoundary(true);
4105 moveCaretToLineEnd();
4110 if ((m_part
->d
->caretNode().handle() != oldCaretNode
4111 || m_part
->d
->caretOffset() != oldOffset
)
4112 // node should never be null, but faulty conditions may cause it to be
4113 && !m_part
->d
->caretNode().isNull()) {
4115 d
->m_caretViewContext
->caretMoved
= true;
4117 if (_ke
->modifiers() & Qt::ShiftModifier
) { // extend selection
4118 updateSelection(oldStartSel
, oldStartOfs
, oldEndSel
, oldEndOfs
);
4119 } else { // clear any selection
4120 if (foldSelectionToCaret(oldStartSel
, oldStartOfs
, oldEndSel
, oldEndOfs
))
4121 m_part
->emitSelectionChanged();
4124 m_part
->emitCaretPositionChanged(m_part
->d
->caretNode(), m_part
->d
->caretOffset());
4130 bool KHTMLView::moveCaretTo(NodeImpl
*node
, long offset
, bool clearSel
)
4132 if (!node
) return false;
4133 ElementImpl
*baseElem
= determineBaseElement(node
);
4134 RenderFlow
*base
= static_cast<RenderFlow
*>(baseElem
? baseElem
->renderer() : 0);
4135 if (!node
) return false;
4137 // need to find out the node's inline box. If there is none, this function
4138 // will snap to the next node that has one. This is necessary to make the
4139 // caret visible in any case.
4140 CaretBoxLineDeleter cblDeleter
;
4143 CaretBoxIterator cbit
;
4144 CaretBoxLine
*cbl
= findCaretBoxLine(node
, offset
, &cblDeleter
, base
, r_ofs
, cbit
);
4146 kWarning() << "KHTMLView::moveCaretTo - findCaretBoxLine() returns NULL" << endl
;
4150 #if DEBUG_CARETMODE > 3
4151 if (cbl
) kDebug(6200) << cbl
->information() << endl
;
4153 CaretBox
*box
= *cbit
;
4154 if (cbit
!= cbl
->end() && box
->object() != node
->renderer()) {
4155 if (box
->object()->element()) {
4156 mapRenderPosToDOMPos(box
->object(), r_ofs
, box
->isOutside(),
4157 box
->isOutsideEnd(), node
, offset
);
4158 //if (!outside) offset = node->minOffset();
4159 #if DEBUG_CARETMODE > 1
4160 kDebug(6200) << "set new node " << node
->nodeName().string() << "@" << node
<< endl
;
4162 } else { // box has no associated element -> do not use
4163 // this case should actually never happen.
4165 kError(6200) << "Box contains no node! Crash imminent" << endl
;
4169 NodeImpl
*oldStartSel
= m_part
->d
->m_selectionStart
.handle();
4170 long oldStartOfs
= m_part
->d
->m_startOffset
;
4171 NodeImpl
*oldEndSel
= m_part
->d
->m_selectionEnd
.handle();
4172 long oldEndOfs
= m_part
->d
->m_endOffset
;
4174 // test for position change
4175 bool posChanged
= m_part
->d
->caretNode().handle() != node
4176 || m_part
->d
->caretOffset() != offset
;
4177 bool selChanged
= false;
4179 m_part
->d
->caretNode() = node
;
4180 m_part
->d
->caretOffset() = offset
;
4181 if (clearSel
|| !oldStartSel
|| !oldEndSel
) {
4182 selChanged
= foldSelectionToCaret(oldStartSel
, oldStartOfs
, oldEndSel
, oldEndOfs
);
4184 //kDebug(6200) << "moveToCaret: extendSelection: m_extendAtEnd " << m_part->d->m_extendAtEnd << endl;
4185 //kDebug(6200) << "selection: start(" << m_part->d->m_selectionStart.handle() << "," << m_part->d->m_startOffset << "), end(" << m_part->d->m_selectionEnd.handle() << "," << m_part->d->m_endOffset << "), caret(" << m_part->d->caretNode().handle() << "," << m_part->d->caretOffset() << ")" << endl;
4186 selChanged
= extendSelection(oldStartSel
, oldStartOfs
, oldEndSel
, oldEndOfs
);
4187 //kDebug(6200) << "after extendSelection: m_extendAtEnd " << m_part->d->m_extendAtEnd << endl;
4188 //kDebug(6200) << "selection: start(" << m_part->d->m_selectionStart.handle() << "," << m_part->d->m_startOffset << "), end(" << m_part->d->m_selectionEnd.handle() << "," << m_part->d->m_endOffset << "), caret(" << m_part->d->caretNode().handle() << "," << m_part->d->caretOffset() << ")" << endl;
4191 d
->caretViewContext()->caretMoved
= true;
4193 bool visible_caret
= placeCaret(box
);
4195 // FIXME: if the old position was !visible_caret, and the new position is
4196 // also, then two caretPositionChanged signals with a null Node are
4197 // emitted in series.
4199 m_part
->emitCaretPositionChanged(visible_caret
? node
: 0, offset
);
4205 void KHTMLView::moveCaretByLine(bool next
, int count
)
4207 Node
&caretNodeRef
= m_part
->d
->caretNode();
4208 if (caretNodeRef
.isNull()) return;
4210 NodeImpl
*caretNode
= caretNodeRef
.handle();
4211 // kDebug(6200) << ": caretNode=" << caretNode << endl;
4212 long offset
= m_part
->d
->caretOffset();
4214 CaretViewContext
*cv
= d
->caretViewContext();
4216 ElementImpl
*baseElem
= determineBaseElement(caretNode
);
4217 LinearDocument
ld(m_part
, caretNode
, offset
, LeafsOnly
, baseElem
);
4219 ErgonomicEditableLineIterator
it(ld
.current(), cv
->origX
);
4221 // move count lines vertically
4222 while (count
> 0 && it
!= ld
.end() && it
!= ld
.preBegin()) {
4224 if (next
) ++it
; else --it
;
4227 // Nothing? Then leave everything as is.
4228 if (it
== ld
.end() || it
== ld
.preBegin()) return;
4231 CaretBox
*caretBox
= nearestCaretBox(it
, d
->m_caretViewContext
, x
, absx
, absy
);
4233 placeCaretOnLine(caretBox
, x
, absx
, absy
);
4236 void KHTMLView::placeCaretOnLine(CaretBox
*caretBox
, int x
, int absx
, int absy
)
4238 // paranoia sanity check
4239 if (!caretBox
) return;
4241 RenderObject
*caretRender
= caretBox
->object();
4243 #if DEBUG_CARETMODE > 0
4244 kDebug(6200) << "got valid caretBox " << caretBox
<< endl
;
4245 kDebug(6200) << "xPos: " << caretBox
->xPos() << " yPos: " << caretBox
->yPos()
4246 << " width: " << caretBox
->width() << " height: " << caretBox
->height() << endl
;
4247 InlineTextBox
*tb
= static_cast<InlineTextBox
*>(caretBox
->inlineBox());
4248 if (caretBox
->isInlineTextBox()) { kDebug(6200) << "contains \"" << QString(static_cast<RenderText
*>(tb
->object())->str
->s
+ tb
->m_start
, tb
->m_len
) << "\"" << endl
;}
4250 // inquire height of caret
4251 int caretHeight
= caretBox
->height();
4252 bool isText
= caretBox
->isInlineTextBox();
4253 int yOfs
= 0; // y-offset for text nodes
4255 // text boxes need extrawurst
4256 RenderText
*t
= static_cast<RenderText
*>(caretRender
);
4257 const QFontMetrics
&fm
= t
->metrics(caretBox
->inlineBox()->m_firstLine
);
4258 caretHeight
= fm
.height();
4259 yOfs
= caretBox
->inlineBox()->baseline() - fm
.ascent();
4264 // set new caret node
4265 NodeImpl
*caretNode
;
4266 long &offset
= m_part
->d
->caretOffset();
4267 mapRenderPosToDOMPos(caretRender
, offset
, caretBox
->isOutside(),
4268 caretBox
->isOutsideEnd(), caretNode
, offset
);
4270 // set all variables not needing special treatment
4271 d
->m_caretViewContext
->y
= caretBox
->yPos() + yOfs
;
4272 d
->m_caretViewContext
->height
= caretHeight
;
4273 d
->m_caretViewContext
->width
= 1; // FIXME: regard override
4275 int xPos
= caretBox
->xPos();
4276 int caretBoxWidth
= caretBox
->width();
4277 d
->m_caretViewContext
->x
= xPos
;
4279 if (!caretBox
->isOutside()) {
4280 // before or at beginning of inline box -> place at beginning
4283 r_ofs
= caretBox
->minOffset();
4284 // somewhere within this block
4285 } else if (x
> xPos
&& x
<= xPos
+ caretBoxWidth
) {
4286 if (isText
) { // find out where exactly
4287 r_ofs
= static_cast<InlineTextBox
*>(caretBox
->inlineBox())
4288 ->offsetForPoint(x
, d
->m_caretViewContext
->x
);
4289 #if DEBUG_CARETMODE > 2
4290 kDebug(6200) << "deviation from origX " << d
->m_caretViewContext
->x
- x
<< endl
;
4293 } else { // snap to nearest end
4294 if (xPos
+ caretBoxWidth
- x
< x
- xPos
) {
4295 d
->m_caretViewContext
->x
= xPos
+ caretBoxWidth
;
4296 r_ofs
= caretNode
? caretNode
->maxOffset() : 1;
4298 d
->m_caretViewContext
->x
= xPos
;
4299 r_ofs
= caretNode
? caretNode
->minOffset() : 0;
4303 } else { // after the inline box -> place at end
4304 d
->m_caretViewContext
->x
= xPos
+ caretBoxWidth
;
4305 r_ofs
= caretBox
->maxOffset();
4309 #if DEBUG_CARETMODE > 0
4310 kDebug(6200) << "new offset: " << offset
<< endl
;
4313 m_part
->d
->caretNode() = caretNode
;
4314 m_part
->d
->caretOffset() = offset
;
4316 d
->m_caretViewContext
->x
+= absx
;
4317 d
->m_caretViewContext
->y
+= absy
;
4319 #if DEBUG_CARETMODE > 1
4320 kDebug(6200) << "new caret position: x " << d
->m_caretViewContext
->x
<< " y " << d
->m_caretViewContext
->y
<< " w " << d
->m_caretViewContext
->width
<< " h " << d
->m_caretViewContext
->height
<< " absx " << absx
<< " absy " << absy
<< endl
;
4323 ensureVisible(d
->m_caretViewContext
->x
, d
->m_caretViewContext
->y
,
4324 d
->m_caretViewContext
->width
, d
->m_caretViewContext
->height
);
4325 d
->scrollBarMoved
= false;
4327 ensureNodeHasFocus(caretNode
);
4331 void KHTMLView::moveCaretToLineBoundary(bool end
)
4333 Node
&caretNodeRef
= m_part
->d
->caretNode();
4334 if (caretNodeRef
.isNull()) return;
4336 NodeImpl
*caretNode
= caretNodeRef
.handle();
4337 // kDebug(6200) << ": caretNode=" << caretNode << endl;
4338 long offset
= m_part
->d
->caretOffset();
4340 ElementImpl
*baseElem
= determineBaseElement(caretNode
);
4341 LinearDocument
ld(m_part
, caretNode
, offset
, LeafsOnly
, baseElem
);
4343 EditableLineIterator it
= ld
.current();
4344 if (it
== ld
.end()) return; // should not happen, but who knows
4346 EditableCaretBoxIterator
fbit(it
, end
);
4347 Q_ASSERT(fbit
!= (*it
)->end() && fbit
!= (*it
)->preBegin());
4348 CaretBox
*b
= *fbit
;
4350 RenderObject
*cb
= b
->containingBlock();
4353 if (cb
) cb
->absolutePosition(absx
,absy
);
4354 else absx
= absy
= 0;
4356 int x
= b
->xPos() + (end
&& !b
->isOutside() ? b
->width() : 0);
4357 d
->m_caretViewContext
->origX
= absx
+ x
;
4358 placeCaretOnLine(b
, x
, absx
, absy
);
4361 void KHTMLView::moveCaretToDocumentBoundary(bool end
)
4363 Node
&caretNodeRef
= m_part
->d
->caretNode();
4364 if (caretNodeRef
.isNull()) return;
4366 NodeImpl
*caretNode
= caretNodeRef
.handle();
4367 // kDebug(6200) << ": caretNode=" << caretNode << endl;
4368 long offset
= m_part
->d
->caretOffset();
4370 ElementImpl
*baseElem
= determineBaseElement(caretNode
);
4371 LinearDocument
ld(m_part
, caretNode
, offset
, IndicatedFlows
, baseElem
);
4373 EditableLineIterator
it(end
? ld
.preEnd() : ld
.begin(), end
);
4374 if (it
== ld
.end() || it
== ld
.preBegin()) return; // should not happen, but who knows
4376 EditableCaretBoxIterator fbit
= it
;
4377 Q_ASSERT(fbit
!= (*it
)->end() && fbit
!= (*it
)->preBegin());
4378 CaretBox
*b
= *fbit
;
4380 RenderObject
*cb
= (*it
)->containingBlock();
4383 if (cb
) cb
->absolutePosition(absx
, absy
);
4384 else absx
= absy
= 0;
4386 int x
= b
->xPos()/* + (end ? b->width() : 0) reactivate for rtl*/;
4387 d
->m_caretViewContext
->origX
= absx
+ x
;
4388 placeCaretOnLine(b
, x
, absx
, absy
);
4391 void KHTMLView::moveCaretBy(bool next
, CaretMovement cmv
, int count
)
4393 if (!m_part
) return;
4394 Node
&caretNodeRef
= m_part
->d
->caretNode();
4395 if (caretNodeRef
.isNull()) return;
4397 NodeImpl
*caretNode
= caretNodeRef
.handle();
4398 // kDebug(6200) << ": caretNode=" << caretNode << endl;
4399 long &offset
= m_part
->d
->caretOffset();
4401 ElementImpl
*baseElem
= determineBaseElement(caretNode
);
4402 CaretAdvancePolicy advpol
= cmv
!= CaretByWord
? IndicatedFlows
: LeafsOnly
;
4403 LinearDocument
ld(m_part
, caretNode
, offset
, advpol
, baseElem
);
4405 EditableCharacterIterator
it(&ld
);
4406 while (!it
.isEnd() && count
> 0) {
4408 if (cmv
== CaretByCharacter
) {
4411 } else if (cmv
== CaretByWord
) {
4412 if (next
) moveItToNextWord(it
);
4413 else moveItToPrevWord(it
);
4415 //kDebug(6200) << "movecaret" << endl;
4417 CaretBox
*hintBox
= 0; // make gcc uninit warning disappear
4419 NodeImpl
*node
= caretNodeRef
.handle();
4420 hintBox
= it
.caretBox();
4421 //kDebug(6200) << "hintBox = " << hintBox << endl;
4422 //kDebug(6200) << " outside " << hintBox->isOutside() << " outsideEnd " << hintBox->isOutsideEnd() << " r " << it.renderer() << " ofs " << it.offset() << " cb " << hintBox->containingBlock() << endl;
4423 mapRenderPosToDOMPos(it
.renderer(), it
.offset(), hintBox
->isOutside(),
4424 hintBox
->isOutsideEnd(), node
, offset
);
4425 //kDebug(6200) << "mapRTD" << endl;
4426 caretNodeRef
= node
;
4427 #if DEBUG_CARETMODE > 2
4428 kDebug(6200) << "set by valid node " << node
<< " " << (node
?node
->nodeName().string():QString()) << " offset: " << offset
<< endl
;
4431 offset
= next
? caretNode
->maxOffset() : caretNode
->minOffset();
4432 #if DEBUG_CARETMODE > 0
4433 kDebug(6200) << "set by INvalid node. offset: " << offset
<< endl
;
4436 placeCaretOnChar(hintBox
);
4439 void KHTMLView::placeCaretOnChar(CaretBox
*hintBox
)
4442 recalcAndStoreCaretPos(hintBox
);
4443 ensureVisible(d
->m_caretViewContext
->x
, d
->m_caretViewContext
->y
,
4444 d
->m_caretViewContext
->width
, d
->m_caretViewContext
->height
);
4445 d
->m_caretViewContext
->origX
= d
->m_caretViewContext
->x
;
4446 d
->scrollBarMoved
= false;
4447 #if DEBUG_CARETMODE > 3
4448 //if (caretNode->isTextNode()) kDebug(6200) << "text[0] = " << (int)*((TextImpl *)caretNode)->data().unicode() << " text :\"" << ((TextImpl *)caretNode)->data().string() << "\"" << endl;
4450 ensureNodeHasFocus(m_part
->d
->caretNode().handle());
4454 void KHTMLView::moveCaretByPage(bool next
)
4456 Node
&caretNodeRef
= m_part
->d
->caretNode();
4457 if (caretNodeRef
.isNull()) return;
4459 NodeImpl
*caretNode
= caretNodeRef
.handle();
4460 // kDebug(6200) << ": caretNode=" << caretNode << endl;
4461 long offset
= m_part
->d
->caretOffset();
4463 int offs
= (viewport()->height() < 30) ? viewport()->height() : 30;
4464 // Minimum distance the caret must be moved
4465 int mindist
= viewport()->height() - offs
;
4467 CaretViewContext
*cv
= d
->caretViewContext();
4468 // int y = cv->y; // we always measure the top border
4470 ElementImpl
*baseElem
= determineBaseElement(caretNode
);
4471 LinearDocument
ld(m_part
, caretNode
, offset
, LeafsOnly
, baseElem
);
4473 ErgonomicEditableLineIterator
it(ld
.current(), cv
->origX
);
4475 moveIteratorByPage(ld
, it
, mindist
, next
);
4478 CaretBox
*caretBox
= nearestCaretBox(it
, d
->m_caretViewContext
, x
, absx
, absy
);
4480 placeCaretOnLine(caretBox
, x
, absx
, absy
);
4483 void KHTMLView::moveCaretPrevWord()
4485 moveCaretBy(false, CaretByWord
, 1);
4488 void KHTMLView::moveCaretNextWord()
4490 moveCaretBy(true, CaretByWord
, 1);
4493 void KHTMLView::moveCaretPrevLine(int n
)
4495 moveCaretByLine(false, n
);
4498 void KHTMLView::moveCaretNextLine(int n
)
4500 moveCaretByLine(true, n
);
4503 void KHTMLView::moveCaretPrevPage()
4505 moveCaretByPage(false);
4508 void KHTMLView::moveCaretNextPage()
4510 moveCaretByPage(true);
4513 void KHTMLView::moveCaretToLineBegin()
4515 moveCaretToLineBoundary(false);
4518 void KHTMLView::moveCaretToLineEnd()
4520 moveCaretToLineBoundary(true);
4523 #endif // KHTML_NO_CARET
4525 #undef DEBUG_CARETMODE