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>
86 #include <q3paintdevicemetrics.h>
88 #include <q3ptrdict.h>
91 #include <QTextDocument>
93 #include <QAbstractEventDispatcher>
96 #include <QAbstractScrollArea>
98 //#define DEBUG_FLICKER
100 //#define DEBUG_PIXEL
103 #include <X11/Xlib.h>
105 #elif defined(Q_WS_WIN)
111 void dumpLineBoxes(RenderFlow
*flow
);
116 using namespace khtml
;
121 class KHTMLViewPrivate
{
122 friend class KHTMLView
;
125 enum PseudoFocusNodes
{
131 enum CompletedState
{
138 : underMouse( 0 ), underMouseNonShared( 0 ), visibleWidgets( 107 )
140 #ifndef KHTML_NO_CARET
141 m_caretViewContext
= 0;
143 #endif // KHTML_NO_CARET
144 postponed_autorepeat
= NULL
;
146 vpolicy
= Qt::ScrollBarAsNeeded
;
147 hpolicy
= Qt::ScrollBarAsNeeded
;
149 prevScrollbarVisible
= true;
151 possibleTripleClick
= false;
152 emitCompletedAfterRepaint
= CSNone
;
153 cursor_icon_widget
= NULL
;
154 m_mouseScrollTimer
= 0;
155 m_mouseScrollIndicator
= 0;
159 delete formCompletions
;
160 delete postponed_autorepeat
;
163 if (underMouseNonShared
)
164 underMouseNonShared
->deref();
166 #ifndef KHTML_NO_CARET
167 delete m_caretViewContext
;
168 delete m_editorContext
;
169 #endif // KHTML_NO_CARET
170 delete cursor_icon_widget
;
171 delete m_mouseScrollTimer
;
172 delete m_mouseScrollIndicator
;
179 if (underMouseNonShared
)
180 underMouseNonShared
->deref();
181 underMouseNonShared
= 0;
183 useSlowRepaints
= false;
184 tabMovePending
= false;
185 lastTabbingDirection
= true;
186 pseudoFocusNode
= PFNone
;
187 #ifndef KHTML_NO_SCROLLBARS
188 //We don't turn off the toolbars here
189 //since if the user turns them
190 //off, then chances are they want them turned
191 //off always - even after a reset.
193 vpolicy
= ScrollBarAlwaysOff
;
194 hpolicy
= ScrollBarAlwaysOff
;
201 scrollBarMoved
= false;
202 contentsMoving
= false;
203 ignoreWheelEvents
= false;
214 isDoubleClick
= false;
215 scrollingSelf
= false;
216 delete postponed_autorepeat
;
217 postponed_autorepeat
= NULL
;
221 scrollSuspended
= false;
222 scrollSuspendPreActivate
= false;
224 firstRelayout
= true;
225 needsFullRepaint
= true;
227 layoutSchedulingEnabled
= true;
229 updateRegion
= QRegion();
230 m_dialogsAllowed
= true;
231 #ifndef KHTML_NO_CARET
232 if (m_caretViewContext
) {
233 m_caretViewContext
->caretMoved
= false;
234 m_caretViewContext
->keyReleasePending
= false;
236 #endif // KHTML_NO_CARET
237 #ifndef KHTML_NO_TYPE_AHEAD_FIND
238 typeAheadActivated
= false;
239 #endif // KHTML_NO_TYPE_AHEAD_FIND
240 accessKeysActivated
= false;
241 accessKeysPreActivate
= false;
243 // We ref/deref to ensure defaultHTMLSettings is available
245 accessKeysEnabled
= KHTMLFactory::defaultHTMLSettings()->accessKeysEnabled();
246 KHTMLFactory::deref();
248 emitCompletedAfterRepaint
= CSNone
;
249 m_mouseEventsTarget
= 0;
251 void newScrollTimer(QWidget
*view
, int tid
)
253 //kDebug(6000) << "newScrollTimer timer " << tid << endl;
254 view
->killTimer(scrollTimerId
);
256 scrollSuspended
= false;
258 enum ScrollDirection
{ ScrollLeft
, ScrollRight
, ScrollUp
, ScrollDown
};
260 void adjustScroller(QWidget
*view
, ScrollDirection direction
, ScrollDirection oppositedir
)
262 static const struct { int msec
, pixels
; } timings
[] = {
263 {320,1}, {224,1}, {160,1}, {112,1}, {80,1}, {56,1}, {40,1},
264 {28,1}, {20,1}, {20,2}, {20,3}, {20,4}, {20,6}, {20,8}, {0,0}
266 if (!scrollTimerId
||
267 (static_cast<int>(scrollDirection
) != direction
&&
268 (static_cast<int>(scrollDirection
) != oppositedir
|| scrollSuspended
))) {
270 scrollBy
= timings
[scrollTiming
].pixels
;
271 scrollDirection
= direction
;
272 newScrollTimer(view
, view
->startTimer(timings
[scrollTiming
].msec
));
273 } else if (scrollDirection
== direction
&&
274 timings
[scrollTiming
+1].msec
&& !scrollSuspended
) {
275 scrollBy
= timings
[++scrollTiming
].pixels
;
276 newScrollTimer(view
, view
->startTimer(timings
[scrollTiming
].msec
));
277 } else if (scrollDirection
== oppositedir
) {
279 scrollBy
= timings
[--scrollTiming
].pixels
;
280 newScrollTimer(view
, view
->startTimer(timings
[scrollTiming
].msec
));
283 scrollSuspended
= false;
286 #ifndef KHTML_NO_CARET
287 /** this function returns an instance of the caret view context. If none
288 * exists, it will be instantiated.
290 CaretViewContext
*caretViewContext() {
291 if (!m_caretViewContext
) m_caretViewContext
= new CaretViewContext();
292 return m_caretViewContext
;
294 /** this function returns an instance of the editor context. If none
295 * exists, it will be instantiated.
297 EditorContext
*editorContext() {
298 if (!m_editorContext
) m_editorContext
= new EditorContext();
299 return m_editorContext
;
301 #endif // KHTML_NO_CARET
305 unsigned int pixelbooth
;
306 unsigned int repaintbooth
;
309 NodeImpl
*underMouse
;
310 NodeImpl
*underMouseNonShared
;
312 bool tabMovePending
:1;
313 bool lastTabbingDirection
:1;
314 PseudoFocusNodes pseudoFocusNode
:2;
315 bool scrollBarMoved
:1;
316 bool contentsMoving
:1;
318 Qt::ScrollBarPolicy vpolicy
;
319 Qt::ScrollBarPolicy hpolicy
;
320 bool prevScrollbarVisible
:1;
322 bool useSlowRepaints
:1;
323 bool ignoreWheelEvents
:1;
325 int borderX
, borderY
;
326 KSimpleConfig
*formCompletions
;
330 int clickX
, clickY
, clickCount
;
333 int prevMouseX
, prevMouseY
;
334 int contentsX
, contentsY
;
337 QKeyEvent
* postponed_autorepeat
;
343 ScrollDirection scrollDirection
:2;
344 bool scrollSuspended
:1;
345 bool scrollSuspendPreActivate
:1;
347 bool firstRelayout
:1;
348 bool layoutSchedulingEnabled
:1;
349 bool needsFullRepaint
:1;
351 bool possibleTripleClick
:1;
353 bool m_dialogsAllowed
:1;
354 QRegion updateRegion
;
355 Q3PtrDict
<QWidget
> visibleWidgets
;
356 #ifndef KHTML_NO_CARET
357 CaretViewContext
*m_caretViewContext
;
358 EditorContext
*m_editorContext
;
359 #endif // KHTML_NO_CARET
360 #ifndef KHTML_NO_TYPE_AHEAD_FIND
364 bool typeAheadActivated
;
365 #endif // KHTML_NO_TYPE_AHEAD_FIND
366 bool accessKeysEnabled
;
367 bool accessKeysActivated
;
368 bool accessKeysPreActivate
;
369 CompletedState emitCompletedAfterRepaint
;
371 QWidget
* cursor_icon_widget
;
373 // scrolling activated by MMB
374 short m_mouseScroll_byX
;
375 short m_mouseScroll_byY
;
376 QTimer
*m_mouseScrollTimer
;
377 QWidget
*m_mouseScrollIndicator
;
378 QPointer
<QWidget
> m_mouseEventsTarget
;
381 #ifndef QT_NO_TOOLTIP
383 /** calculates the client-side image map rectangle for the given image element
384 * @param img image element
385 * @param scrollOfs scroll offset of viewport in content coordinates
386 * @param p position to be probed in viewport coordinates
387 * @param r returns the bounding rectangle in content coordinates
388 * @param s returns the title string
389 * @return true if an appropriate area was found -- only in this case r and
390 * s are valid, false otherwise
392 static bool findImageMapRect(HTMLImageElementImpl
*img
, const QPoint
&scrollOfs
,
393 const QPoint
&p
, QRect
&r
, QString
&s
)
395 HTMLMapElementImpl
* map
;
396 if (img
&& img
->getDocument()->isHTMLDocument() &&
397 (map
= static_cast<HTMLDocumentImpl
*>(img
->getDocument())->getMap(img
->imageMap()))) {
398 RenderObject::NodeInfo
info(true, false);
399 RenderObject
*rend
= img
->renderer();
401 if (!rend
|| !rend
->absolutePosition(ax
, ay
))
403 // we're a client side image map
404 bool inside
= map
->mapMouseEvent(p
.x() - ax
+ scrollOfs
.x(),
405 p
.y() - ay
+ scrollOfs
.y(), rend
->contentWidth(),
406 rend
->contentHeight(), info
);
407 if (inside
&& info
.URLElement()) {
408 HTMLAreaElementImpl
*area
= static_cast<HTMLAreaElementImpl
*>(info
.URLElement());
409 Q_ASSERT(area
->id() == ID_AREA
);
410 s
= area
->getAttribute(ATTR_TITLE
).string();
411 QRegion reg
= area
->cachedRegion();
412 if (!s
.isEmpty() && !reg
.isEmpty()) {
413 r
= reg
.boundingRect();
422 bool KHTMLView::event( QEvent
* e
)
424 if ( e
->type() == QEvent::ToolTip
) {
425 QHelpEvent
*he
= static_cast<QHelpEvent
*>(e
);
426 QPoint p
= he
->pos();
428 DOM::NodeImpl
*node
= d
->underMouseNonShared
;
431 if ( node
->isElementNode() ) {
432 DOM::ElementImpl
*e
= static_cast<DOM::ElementImpl
*>( node
);
436 // for images, check if it is part of a client-side image map,
437 // and query the <area>s' title attributes, too
438 if (e
->id() == ID_IMG
&& !e
->getAttribute( ATTR_USEMAP
).isEmpty()) {
439 found
= findImageMapRect(static_cast<HTMLImageElementImpl
*>(e
),
440 viewportToContents(QPoint(0, 0)), p
, r
, s
);
443 s
= e
->getAttribute( ATTR_TITLE
).string();
446 region
|= QRect( contentsToViewport( r
.topLeft() ), r
.size() );
447 if ( !s
.isEmpty() ) {
448 QToolTip::showText( viewport()->mapToGlobal(region
.bottomLeft()),
449 Qt::convertFromPlainText( s
, Qt::WhiteSpaceNormal
) );
453 node
= node
->parentNode();
457 return QScrollArea::event(e
);
461 KHTMLView::KHTMLView( KHTMLPart
*part
, QWidget
*parent
)
462 : QScrollArea( parent
), d( new KHTMLViewPrivate
)
467 QScrollArea::setVerticalScrollBarPolicy(d
->vpolicy
);
468 QScrollArea::setHorizontalScrollBarPolicy(d
->hpolicy
);
469 connect(KGlobalSettings::self(), SIGNAL(kdisplayPaletteChanged()), this, SLOT(slotPaletteChanged()));
471 #ifndef KHTML_NO_TYPE_AHEAD_FIND
472 connect(&d
->timer
, SIGNAL(timeout()), this, SLOT(findTimeout()));
473 #endif // KHTML_NO_TYPE_AHEAD_FIND
476 widget()->setMouseTracking(true);
479 KHTMLView::~KHTMLView()
484 DOM::DocumentImpl
*doc
= m_part
->xmlDocImpl();
491 void KHTMLView::init()
493 setFocusPolicy(Qt::StrongFocus
);
494 viewport()->setFocusProxy(this);
496 _marginWidth
= -1; // undefined
501 installEventFilter(this);
503 setAcceptDrops(true);
505 setWidget( new QWidget(this) );
506 QSize s
= viewport()->size();
507 resizeContents(s
.width(), s
.height());
509 // ### we'll enable redirection of khtmlview here
510 // when event and painting issues have been thoroughly worked out
511 // m_kwp->setIsRedirected(true);
514 void KHTMLView::clear()
516 #ifndef KHTML_NO_CARET
517 if (!m_part
->isCaretMode() && !m_part
->isEditable()) caretOff();
519 #ifndef KHTML_NO_TYPE_AHEAD_FIND
520 if( d
->typeAheadActivated
)
523 if (d
->accessKeysEnabled
&& d
->accessKeysActivated
)
525 viewport()->unsetCursor();
526 if ( d
->cursor_icon_widget
)
527 d
->cursor_icon_widget
->hide();
529 QAbstractEventDispatcher::instance()->unregisterTimers(this);
532 QScrollArea::setHorizontalScrollBarPolicy(d
->hpolicy
);
533 QScrollArea::setVerticalScrollBarPolicy(d
->vpolicy
);
534 verticalScrollBar()->setEnabled( false );
535 horizontalScrollBar()->setEnabled( false );
538 void KHTMLView::hideEvent(QHideEvent
* e
)
540 QScrollArea::hideEvent(e
);
541 if ( m_part
&& m_part
->xmlDocImpl() )
542 m_part
->xmlDocImpl()->docLoader()->pauseAnimations();
545 void KHTMLView::showEvent(QShowEvent
* e
)
547 QScrollArea::showEvent(e
);
548 if ( m_part
&& m_part
->xmlDocImpl() )
549 m_part
->xmlDocImpl()->docLoader()->resumeAnimations();
552 void KHTMLView::setMouseEventsTarget( QWidget
* w
)
554 d
->m_mouseEventsTarget
= w
;
557 QWidget
* KHTMLView::mouseEventsTarget() const
559 return d
->m_mouseEventsTarget
;
562 int KHTMLView::contentsWidth() const
564 return widget() ? widget()->width() : 0;
567 int KHTMLView::contentsHeight() const
569 return widget() ? widget()->height() : 0;
572 void KHTMLView::resizeContents(int w
, int h
)
576 widget()->resize(w
, h
);
579 int KHTMLView::contentsX() const
584 int KHTMLView::contentsY() const
589 int KHTMLView::visibleWidth() const
591 return viewport()->width();
594 int KHTMLView::visibleHeight() const
596 return viewport()->height();
599 void KHTMLView::setContentsPos( int x
, int y
)
601 horizontalScrollBar()->setValue( x
);
602 verticalScrollBar()->setValue( y
);
605 void KHTMLView::scrollBy(int x
, int y
)
607 horizontalScrollBar()->setValue( horizontalScrollBar()->value()+x
);
608 verticalScrollBar()->setValue( verticalScrollBar()->value()+y
);
611 QPoint
KHTMLView::contentsToViewport(const QPoint
& p
) const
613 return QPoint(p
.x()-contentsX(), p
.y()-contentsY());
616 void KHTMLView::contentsToViewport(int x
, int y
, int& cx
, int& cy
) const
619 p
= contentsToViewport(p
);
624 QPoint
KHTMLView::viewportToContents(const QPoint
& p
) const
626 return QPoint(p
.x()+contentsX(), p
.y()+contentsY());
629 void KHTMLView::viewportToContents(int x
, int y
, int& cx
, int& cy
) const
632 p
= viewportToContents(p
);
637 void KHTMLView::updateContents(int x
, int y
, int w
, int h
)
639 widget()->update(x
, y
, w
, h
);
642 void KHTMLView::updateContents( const QRect
& r
)
644 updateContents( r
.x(), r
.y(), r
.width(), r
.height() );
647 void KHTMLView::repaintContents(int x
, int y
, int w
, int h
)
649 widget()->repaint(x
, y
, w
, h
);
652 void KHTMLView::repaintContents( const QRect
& r
)
654 repaintContents( r
.x(), r
.y(), r
.width(), r
.height() );
657 void KHTMLView::resizeEvent (QResizeEvent
* e
)
659 int dw
= e
->oldSize().width() - e
->size().width();
660 int dh
= e
->oldSize().height() - e
->size().height();
662 // if we are shrinking the view, don't allow the content to overflow
663 // before the layout occurs - we don't know if we need scrollbars yet
664 dw
= dw
>0 ? qMax(0, contentsWidth()-dw
) : contentsWidth();
665 dh
= dh
>0 ? qMax(0, contentsHeight()-dh
) : contentsHeight();
667 resizeContents(dw
, dh
);
669 if (d
->layoutSchedulingEnabled
)
671 #ifndef KHTML_NO_CARET
674 recalcAndStoreCaretPos();
679 KApplication::sendPostedEvents(viewport(), QEvent::Paint
);
681 if ( m_part
&& m_part
->xmlDocImpl() )
682 m_part
->xmlDocImpl()->dispatchWindowEvent( EventImpl::RESIZE_EVENT
, false, false );
685 void KHTMLView::paintEvent( QPaintEvent
*e
)
687 QPainter
p(widget());
690 QRect
v(contentsX(), contentsY(), visibleWidth(), visibleHeight());
693 if (!r
.isValid() || r
.isEmpty()) return;
701 if(!m_part
|| !m_part
->xmlDocImpl() || !m_part
->xmlDocImpl()->renderer()) {
702 p
.fillRect(ex
, ey
, ew
, eh
, palette().brush(QPalette::Active
, QPalette::Base
));
704 } else if ( d
->complete
&& static_cast<RenderCanvas
*>(m_part
->xmlDocImpl()->renderer())->needsLayout() ) {
705 // an external update request happens while we have a layout scheduled
706 unscheduleRelayout();
711 kDebug( 6000 ) << "WARNING: paintEvent reentered! " << endl
;
712 kDebug( 6000 ) << kBacktrace() << endl
;
717 m_part
->xmlDocImpl()->renderer()->layer()->paint(&p
, r
);
719 #ifndef KHTML_NO_CARET
720 if (d
->m_caretViewContext
&& d
->m_caretViewContext
->visible
) {
721 QRect
pos(d
->m_caretViewContext
->x
, d
->m_caretViewContext
->y
,
722 d
->m_caretViewContext
->width
, d
->m_caretViewContext
->height
);
723 if (pos
.intersects(QRect(ex
, ey
, ew
, eh
))) {
724 p
.setCompositionMode(QPainter::CompositionMode_Xor
);
726 if (pos
.width() == 1)
727 p
.drawLine(pos
.topLeft(), pos
.bottomRight());
729 p
.fillRect(pos
, Qt::white
);
733 #endif // KHTML_NO_CARET
735 khtml::DrawContentsEvent
event( &p
, ex
, ey
, ew
, eh
);
736 QApplication::sendEvent( m_part
, &event
);
738 if (d
->scrollingSelf
|| d
->contentsMoving
|| r
.contains(widget()->mapFromGlobal(QCursor::pos()))) {
739 QMouseEvent
*tempEvent
= new QMouseEvent( QEvent::MouseMove
, widget()->mapFromGlobal( QCursor::pos() ),
740 Qt::NoButton
, Qt::NoButton
, Qt::NoModifier
);
741 mouseMoveEvent( tempEvent
);
748 void KHTMLView::setMarginWidth(int w
)
750 // make it update the rendering area when set
754 void KHTMLView::setMarginHeight(int h
)
756 // make it update the rendering area when set
760 void KHTMLView::layout()
762 if( m_part
&& m_part
->xmlDocImpl() ) {
763 DOM::DocumentImpl
*document
= m_part
->xmlDocImpl();
765 khtml::RenderCanvas
* canvas
= static_cast<khtml::RenderCanvas
*>(document
->renderer());
766 if ( !canvas
) return;
768 d
->layoutSchedulingEnabled
=false;
770 // the reference object for the overflow property on canvas
771 RenderObject
* ref
= 0;
772 RenderObject
* root
= document
->documentElement() ? document
->documentElement()->renderer() : 0;
774 if (document
->isHTMLDocument()) {
775 NodeImpl
*body
= static_cast<HTMLDocumentImpl
*>(document
)->body();
776 if(body
&& body
->renderer() && body
->id() == ID_FRAMESET
) {
777 QScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
778 QScrollArea::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
779 body
->renderer()->setNeedsLayout(true);
781 else if (root
) // only apply body's overflow to canvas if root has a visible overflow
782 ref
= (!body
|| root
->style()->hidesOverflow()) ? root
: body
->renderer();
788 if( ref
->style()->overflowX() == OHIDDEN
)
789 if (d
->hpolicy
== Qt::ScrollBarAsNeeded
) QScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
791 if (QScrollArea::horizontalScrollBarPolicy() == Qt::ScrollBarAlwaysOff
) QScrollArea::setHorizontalScrollBarPolicy(d
->hpolicy
);
792 if ( ref
->style()->overflowY() == OHIDDEN
)
793 if (d
->vpolicy
== Qt::ScrollBarAsNeeded
) QScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
795 if (QScrollArea::verticalScrollBarPolicy() == Qt::ScrollBarAlwaysOff
) QScrollArea::setVerticalScrollBarPolicy(d
->vpolicy
);
797 d
->needsFullRepaint
= d
->firstRelayout
;
798 if (_height
!= visibleHeight() || _width
!= visibleWidth()) {;
799 d
->needsFullRepaint
= true;
800 _height
= visibleHeight();
801 _width
= visibleWidth();
806 emit
finishedLayout();
807 if (d
->firstRelayout
) {
808 // make sure firstRelayout is set to false now in case this layout
810 d
->firstRelayout
= false;
811 verticalScrollBar()->setEnabled( true );
812 horizontalScrollBar()->setEnabled( true );
814 #ifndef KHTML_NO_CARET
816 if ((m_part
->isCaretMode() || m_part
->isEditable())
817 && !d
->complete
&& d
->m_caretViewContext
818 && !d
->m_caretViewContext
->caretMoved
) {
821 recalcAndStoreCaretPos();
825 if (d
->accessKeysEnabled
&& d
->accessKeysActivated
) {
826 emit
hideAccessKeys();
831 _width
= visibleWidth();
833 if (d
->layoutTimerId
)
834 killTimer(d
->layoutTimerId
);
835 d
->layoutTimerId
= 0;
836 d
->layoutSchedulingEnabled
=true;
839 void KHTMLView::closeChildDialogs()
841 QList
<QDialog
*> dlgs
= findChildren
<QDialog
*>();
842 foreach (QDialog
*dlg
, dlgs
)
844 KDialog
* dlgbase
= dynamic_cast<KDialog
*>( dlg
);
846 if ( dlgbase
->testAttribute( Qt::WA_ShowModal
) ) {
847 kDebug(6000) << "closeChildDialogs: closing dialog " << dlgbase
<< endl
;
848 // close() ends up calling QButton::animateClick, which isn't immediate
849 // we need something the exits the event loop immediately (#49068)
855 kWarning() << "closeChildDialogs: not a KDialog! Don't use QDialogs in KDE! " << static_cast<QWidget
*>(dlg
) << endl
;
856 static_cast<QWidget
*>(dlg
)->hide();
859 d
->m_dialogsAllowed
= false;
862 bool KHTMLView::dialogsAllowed() {
863 bool allowed
= d
->m_dialogsAllowed
;
864 KHTMLPart
* p
= m_part
->parentPart();
866 allowed
&= p
->view()->dialogsAllowed();
870 void KHTMLView::closeEvent( QCloseEvent
* ev
)
873 QScrollArea::closeEvent( ev
);
881 void KHTMLView::mousePressEvent( QMouseEvent
*_mouse
)
883 if (!m_part
->xmlDocImpl()) return;
884 if (d
->possibleTripleClick
&& ( _mouse
->button() & Qt::MouseButtonMask
) == Qt::LeftButton
)
886 mouseDoubleClickEvent( _mouse
); // it handles triple clicks too
890 int xm
= _mouse
->x();
891 int ym
= _mouse
->y();
893 // kDebug( 6000 ) << "mousePressEvent: viewport=("<<_mouse->x()-contentsX()<<"/"<<_mouse->y()-contentsY()<<"), contents=(" << xm << "/" << ym << ")\n";
895 d
->isDoubleClick
= false;
897 DOM::NodeImpl::MouseEvent
mev( _mouse
->buttons(), DOM::NodeImpl::MousePress
);
898 m_part
->xmlDocImpl()->prepareMouseEvent( false, xm
, ym
, &mev
);
900 //kDebug(6000) << "innerNode="<<mev.innerNode.nodeName().string()<<endl;
902 if ( (_mouse
->button() == Qt::MidButton
) &&
903 !m_part
->d
->m_bOpenMiddleClick
&& !d
->m_mouseScrollTimer
&&
904 mev
.url
.isNull() && (mev
.innerNode
.elementId() != ID_INPUT
) ) {
905 QPoint point
= mapFromGlobal( _mouse
->globalPos() );
907 d
->m_mouseScroll_byX
= 0;
908 d
->m_mouseScroll_byY
= 0;
910 d
->m_mouseScrollTimer
= new QTimer( this );
911 connect( d
->m_mouseScrollTimer
, SIGNAL(timeout()), this, SLOT(slotMouseScrollTimer()) );
913 if ( !d
->m_mouseScrollIndicator
) {
914 QPixmap
pixmap( 48, 48 ), icon
;
915 pixmap
.fill( QColor( qRgba( 127, 127, 127, 127 ) ) );
917 QPainter
p( &pixmap
);
918 icon
= KHTMLFactory::iconLoader()->loadIcon( "1uparrow", K3Icon::Small
);
919 p
.drawPixmap( 16, 0, icon
);
920 icon
= KHTMLFactory::iconLoader()->loadIcon( "1leftarrow", K3Icon::Small
);
921 p
.drawPixmap( 0, 16, icon
);
922 icon
= KHTMLFactory::iconLoader()->loadIcon( "1downarrow", K3Icon::Small
);
923 p
.drawPixmap( 16, 32,icon
);
924 icon
= KHTMLFactory::iconLoader()->loadIcon( "1rightarrow", K3Icon::Small
);
925 p
.drawPixmap( 32, 16, icon
);
926 p
.drawEllipse( 23, 23, 2, 2 );
928 d
->m_mouseScrollIndicator
= new QWidget( this );
929 d
->m_mouseScrollIndicator
->setFixedSize( 48, 48 );
931 palette
.setBrush( d
->m_mouseScrollIndicator
->backgroundRole(), QBrush( pixmap
) );
932 d
->m_mouseScrollIndicator
->setPalette( palette
);
934 d
->m_mouseScrollIndicator
->move( point
.x()-24, point
.y()-24 );
936 bool hasHorBar
= visibleWidth() < contentsWidth();
937 bool hasVerBar
= visibleHeight() < contentsHeight();
939 KConfigGroup
cg( KGlobal::config(), "HTML Settings" );
940 if ( cg
.readEntry( "ShowMouseScrollIndicator", true ) ) {
941 d
->m_mouseScrollIndicator
->show();
942 d
->m_mouseScrollIndicator
->unsetCursor();
944 QBitmap mask
= d
->m_mouseScrollIndicator
->palette().brush(d
->m_mouseScrollIndicator
->backgroundRole()).texture().createHeuristicMask( true );
946 if ( hasHorBar
&& !hasVerBar
) {
947 QBitmap
bm( 16, 16 );
949 QPainter
painter( &mask
);
950 painter
.drawPixmap( QRectF( 16, 0, bm
.width(), bm
.height() ), bm
, bm
.rect() );
951 painter
.drawPixmap( QRectF( 16, 32, bm
.width(), bm
.height() ), bm
, bm
.rect() );
952 d
->m_mouseScrollIndicator
->setCursor( Qt::SizeHorCursor
);
954 else if ( !hasHorBar
&& hasVerBar
) {
955 QBitmap
bm( 16, 16 );
957 QPainter
painter( &mask
);
958 painter
.drawPixmap( QRectF( 0, 16, bm
.width(), bm
.height() ), bm
, bm
.rect() );
959 painter
.drawPixmap( QRectF( 32, 16, bm
.width(), bm
.height() ), bm
, bm
.rect() );
960 d
->m_mouseScrollIndicator
->setCursor( Qt::SizeVerCursor
);
963 d
->m_mouseScrollIndicator
->setCursor( Qt::SizeAllCursor
);
965 d
->m_mouseScrollIndicator
->setMask( mask
);
968 if ( hasHorBar
&& !hasVerBar
)
969 viewport()->setCursor( Qt::SizeHorCursor
);
970 else if ( !hasHorBar
&& hasVerBar
)
971 viewport()->setCursor( Qt::SizeVerCursor
);
973 viewport()->setCursor( Qt::SizeAllCursor
);
978 else if ( d
->m_mouseScrollTimer
) {
979 delete d
->m_mouseScrollTimer
;
980 d
->m_mouseScrollTimer
= 0;
982 if ( d
->m_mouseScrollIndicator
)
983 d
->m_mouseScrollIndicator
->hide();
990 bool swallowEvent
= dispatchMouseEvent(EventImpl::MOUSEDOWN_EVENT
,mev
.innerNode
.handle(),mev
.innerNonSharedNode
.handle(),true,
991 d
->clickCount
,_mouse
,true,DOM::NodeImpl::MousePress
);
993 khtml::RenderObject
* r
= mev
.innerNode
.handle() ? mev
.innerNode
.handle()->renderer() : 0;
994 if (r
&& r
->isWidget())
998 emit m_part
->nodeActivated(mev
.innerNode
);
1000 khtml::MousePressEvent
event( _mouse
, xm
, ym
, mev
.url
, mev
.target
, mev
.innerNode
);
1001 QApplication::sendEvent( m_part
, &event
);
1002 // we might be deleted after this
1006 void KHTMLView::mouseDoubleClickEvent( QMouseEvent
*_mouse
)
1008 if(!m_part
->xmlDocImpl()) return;
1010 int xm
= _mouse
->x();
1011 int ym
= _mouse
->y();
1013 // kDebug( 6000 ) << "mouseDblClickEvent: x=" << xm << ", y=" << ym << endl;
1015 d
->isDoubleClick
= true;
1017 DOM::NodeImpl::MouseEvent
mev( _mouse
->buttons(), DOM::NodeImpl::MouseDblClick
);
1018 m_part
->xmlDocImpl()->prepareMouseEvent( false, xm
, ym
, &mev
);
1020 // We do the same thing as mousePressEvent() here, since the DOM does not treat
1021 // single and double-click events as separate (only the detail, i.e. number of clicks differs)
1022 if (d
->clickCount
> 0 &&
1023 QPoint(d
->clickX
-xm
,d
->clickY
-ym
).manhattanLength() <= QApplication::startDragDistance())
1025 else { // shouldn't happen, if Qt has the same criterias for double clicks.
1030 bool swallowEvent
= dispatchMouseEvent(EventImpl::MOUSEDOWN_EVENT
,mev
.innerNode
.handle(),mev
.innerNonSharedNode
.handle(),true,
1031 d
->clickCount
,_mouse
,true,DOM::NodeImpl::MouseDblClick
);
1033 khtml::RenderObject
* r
= mev
.innerNode
.handle() ? mev
.innerNode
.handle()->renderer() : 0;
1034 if (r
&& r
->isWidget())
1037 if (!swallowEvent
) {
1038 khtml::MouseDoubleClickEvent
event( _mouse
, xm
, ym
, mev
.url
, mev
.target
, mev
.innerNode
, d
->clickCount
);
1039 QApplication::sendEvent( m_part
, &event
);
1042 d
->possibleTripleClick
=true;
1043 QTimer::singleShot(QApplication::doubleClickInterval(),this,SLOT(tripleClickTimeout()));
1046 void KHTMLView::tripleClickTimeout()
1048 d
->possibleTripleClick
= false;
1052 void KHTMLView::mouseMoveEvent( QMouseEvent
* _mouse
)
1054 if ( d
->m_mouseScrollTimer
) {
1055 QPoint point
= mapFromGlobal( _mouse
->globalPos() );
1057 int deltaX
= point
.x() - d
->m_mouseScrollIndicator
->x() - 24;
1058 int deltaY
= point
.y() - d
->m_mouseScrollIndicator
->y() - 24;
1060 (deltaX
> 0) ? d
->m_mouseScroll_byX
= 1 : d
->m_mouseScroll_byX
= -1;
1061 (deltaY
> 0) ? d
->m_mouseScroll_byY
= 1 : d
->m_mouseScroll_byY
= -1;
1063 double adX
= QABS(deltaX
)/30.0;
1064 double adY
= QABS(deltaY
)/30.0;
1066 d
->m_mouseScroll_byX
= qMax(qMin(d
->m_mouseScroll_byX
* int(adX
*adX
), SHRT_MAX
), SHRT_MIN
);
1067 d
->m_mouseScroll_byY
= qMax(qMin(d
->m_mouseScroll_byY
* int(adY
*adY
), SHRT_MAX
), SHRT_MIN
);
1069 if (d
->m_mouseScroll_byX
== 0 && d
->m_mouseScroll_byY
== 0) {
1070 d
->m_mouseScrollTimer
->stop();
1072 else if (!d
->m_mouseScrollTimer
->isActive()) {
1073 d
->m_mouseScrollTimer
->start( 20 );
1077 if(!m_part
->xmlDocImpl()) return;
1079 int xm
= _mouse
->x();
1080 int ym
= _mouse
->y();
1082 DOM::NodeImpl::MouseEvent
mev( _mouse
->buttons(), DOM::NodeImpl::MouseMove
);
1083 // Do not modify :hover/:active state while mouse is pressed.
1084 m_part
->xmlDocImpl()->prepareMouseEvent( _mouse
->buttons() /*readonly ?*/, xm
, ym
, &mev
);
1086 // kDebug(6000) << "mouse move: " << _mouse->pos()
1087 // << " button " << _mouse->button()
1088 // << " state " << _mouse->state() << endl;
1090 DOM::NodeImpl
* target
= mev
.innerNode
.handle();
1091 DOM::NodeImpl
* fn
= m_part
->xmlDocImpl()->focusNode();
1093 // a widget may be the real target of this event (e.g. if a scrollbar's slider is being moved)
1094 if (d
->m_mouseEventsTarget
&& fn
&& fn
->renderer() && fn
->renderer()->isWidget())
1097 bool swallowEvent
= dispatchMouseEvent(EventImpl::MOUSEMOVE_EVENT
,target
,mev
.innerNonSharedNode
.handle(),false,
1098 0,_mouse
,true,DOM::NodeImpl::MouseMove
);
1100 if (d
->clickCount
> 0 &&
1101 QPoint(d
->clickX
-xm
,d
->clickY
-ym
).manhattanLength() > QApplication::startDragDistance()) {
1102 d
->clickCount
= 0; // moving the mouse outside the threshold invalidates the click
1105 // execute the scheduled script. This is to make sure the mouseover events come after the mouseout events
1106 m_part
->executeScheduledScript();
1108 khtml::RenderObject
* r
= target
? target
->renderer() : 0;
1109 khtml::RenderStyle
* style
= (r
&& r
->style()) ? r
->style() : 0;
1111 bool mailtoCursor
= false;
1112 switch ( style
? style
->cursor() : CURSOR_AUTO
) {
1114 if ( r
&& r
->isText() )
1115 c
= KCursor::ibeamCursor();
1116 if ( mev
.url
.length() && m_part
->settings()->changeCursor() ) {
1117 c
= m_part
->urlCursor();
1118 if (mev
.url
.string().startsWith("mailto:") && mev
.url
.string().indexOf('@')>0)
1119 mailtoCursor
= true;
1122 if (r
&& r
->isFrameSet() && !static_cast<RenderFrameSet
*>(r
)->noResize())
1123 c
= QCursor(static_cast<RenderFrameSet
*>(r
)->cursorShape());
1127 c
= KCursor::crossCursor();
1129 case CURSOR_POINTER
:
1130 c
= m_part
->urlCursor();
1131 if (mev
.url
.string().startsWith("mailto:") && mev
.url
.string().indexOf('@')>0)
1132 mailtoCursor
= true;
1134 case CURSOR_PROGRESS
:
1135 c
= KCursor::workingCursor();
1138 c
= KCursor::sizeAllCursor();
1140 case CURSOR_E_RESIZE
:
1141 case CURSOR_W_RESIZE
:
1142 c
= KCursor::sizeHorCursor();
1144 case CURSOR_N_RESIZE
:
1145 case CURSOR_S_RESIZE
:
1146 c
= KCursor::sizeVerCursor();
1148 case CURSOR_NE_RESIZE
:
1149 case CURSOR_SW_RESIZE
:
1150 c
= KCursor::sizeBDiagCursor();
1152 case CURSOR_NW_RESIZE
:
1153 case CURSOR_SE_RESIZE
:
1154 c
= KCursor::sizeFDiagCursor();
1157 c
= KCursor::ibeamCursor();
1160 c
= KCursor::waitCursor();
1163 c
= KCursor::whatsThisCursor();
1165 case CURSOR_DEFAULT
:
1169 if ( viewport()->cursor().handle() != c
.handle() ) {
1170 if( c
.handle() == KCursor::arrowCursor().handle()) {
1171 for (KHTMLPart
* p
= m_part
; p
; p
= p
->parentPart())
1172 p
->view()->viewport()->unsetCursor();
1175 viewport()->setCursor( c
);
1179 if ( mailtoCursor
&& isVisible() && hasFocus() ) {
1181 if( !d
->cursor_icon_widget
) {
1182 QPixmap icon_pixmap
= KHTMLFactory::iconLoader()->loadIcon( "mail_generic", K3Icon::Small
, 0, K3Icon::DefaultState
, 0, true );
1184 d
->cursor_icon_widget
= new QWidget( 0, Qt::WX11BypassWM
);
1185 XSetWindowAttributes attr
;
1186 attr
.save_under
= True
;
1187 XChangeWindowAttributes( QX11Info::display(), d
->cursor_icon_widget
->winId(), CWSaveUnder
, &attr
);
1189 d
->cursor_icon_widget
= new QWidget( NULL
, NULL
);
1192 d
->cursor_icon_widget
->resize( icon_pixmap
.width(), icon_pixmap
.height());
1193 if( !icon_pixmap
.mask().isNull() )
1194 d
->cursor_icon_widget
->setMask( icon_pixmap
.mask());
1196 d
->cursor_icon_widget
->clearMask();
1198 palette
.setBrush( d
->cursor_icon_widget
->backgroundRole(), QBrush( icon_pixmap
) );
1199 d
->cursor_icon_widget
->setPalette( palette
);
1200 d
->cursor_icon_widget
->erase();
1202 QPoint c_pos
= QCursor::pos();
1203 d
->cursor_icon_widget
->move( c_pos
.x() + 15, c_pos
.y() + 15 );
1205 XRaiseWindow( QX11Info::display(), d
->cursor_icon_widget
->winId());
1206 QApplication::flush();
1207 #elif defined(Q_WS_WIN)
1208 SetWindowPos( d
->cursor_icon_widget
->winId(), HWND_TOP
, 0, 0, 0, 0, SWP_NOACTIVATE
| SWP_NOMOVE
| SWP_NOSIZE
);
1212 d
->cursor_icon_widget
->show();
1215 else if ( d
->cursor_icon_widget
)
1216 d
->cursor_icon_widget
->hide();
1218 if (r
&& r
->isWidget()) {
1225 if (!swallowEvent
) {
1226 khtml::MouseMoveEvent
event( _mouse
, xm
, ym
, mev
.url
, mev
.target
, mev
.innerNode
);
1227 QApplication::sendEvent( m_part
, &event
);
1231 void KHTMLView::mouseReleaseEvent( QMouseEvent
* _mouse
)
1233 bool swallowEvent
= false;
1235 int xm
= _mouse
->x();
1236 int ym
= _mouse
->y();
1238 DOM::NodeImpl::MouseEvent
mev( _mouse
->buttons(), DOM::NodeImpl::MouseRelease
);
1240 if ( m_part
->xmlDocImpl() )
1242 m_part
->xmlDocImpl()->prepareMouseEvent( false, xm
, ym
, &mev
);
1244 DOM::NodeImpl
* target
= mev
.innerNode
.handle();
1245 DOM::NodeImpl
* fn
= m_part
->xmlDocImpl()->focusNode();
1247 // a widget may be the real target of this event (e.g. if a scrollbar's slider is being moved)
1248 if (d
->m_mouseEventsTarget
&& fn
&& fn
->renderer() && fn
->renderer()->isWidget())
1251 swallowEvent
= dispatchMouseEvent(EventImpl::MOUSEUP_EVENT
,target
,mev
.innerNonSharedNode
.handle(),true,
1252 d
->clickCount
,_mouse
,false,DOM::NodeImpl::MouseRelease
);
1254 // clear our sticky event target on any mouseRelease event
1255 if (d
->m_mouseEventsTarget
)
1256 d
->m_mouseEventsTarget
= 0;
1258 if (d
->clickCount
> 0 &&
1259 QPoint(d
->clickX
-xm
,d
->clickY
-ym
).manhattanLength() <= QApplication::startDragDistance()) {
1260 QMouseEvent
me(d
->isDoubleClick
? QEvent::MouseButtonDblClick
: QEvent::MouseButtonRelease
,
1261 _mouse
->pos(), _mouse
->button(), _mouse
->buttons(), _mouse
->modifiers());
1262 dispatchMouseEvent(EventImpl::CLICK_EVENT
, mev
.innerNode
.handle(),mev
.innerNonSharedNode
.handle(),true,
1263 d
->clickCount
, &me
, true, DOM::NodeImpl::MouseRelease
);
1266 khtml::RenderObject
* r
= target
? target
->renderer() : 0;
1267 if (r
&& r
->isWidget())
1271 if (!swallowEvent
) {
1272 khtml::MouseReleaseEvent
event( _mouse
, xm
, ym
, mev
.url
, mev
.target
, mev
.innerNode
);
1273 QApplication::sendEvent( m_part
, &event
);
1277 // returns true if event should be swallowed
1278 bool KHTMLView::dispatchKeyEvent( QKeyEvent
*_ke
)
1280 if (!m_part
->xmlDocImpl())
1282 // Pressing and releasing a key should generate keydown, keypress and keyup events
1283 // Holding it down should generated keydown, keypress (repeatedly) and keyup events
1284 // The problem here is that Qt generates two autorepeat events (keyrelease+keypress)
1285 // for autorepeating, while DOM wants only one autorepeat event (keypress), so one
1286 // of the Qt events shouldn't be passed to DOM, but it should be still filtered
1287 // out if DOM would filter the autorepeat event. Additional problem is that Qt keyrelease
1288 // events don't have text() set (Qt bug?), so DOM often would ignore the keypress event
1289 // if it was created using Qt keyrelease, but Qt autorepeat keyrelease comes
1290 // before Qt autorepeat keypress (i.e. problem whether to filter it out or not).
1291 // The solution is to filter out and postpone the Qt autorepeat keyrelease until
1292 // the following Qt keypress event comes. If DOM accepts the DOM keypress event,
1293 // the postponed event will be simply discarded. If not, it will be passed to keyPressEvent()
1294 // again, and here it will be ignored.
1296 // Qt: Press | Release(autorepeat) Press(autorepeat) etc. | Release
1297 // DOM: Down + Press | (nothing) Press | Up
1299 // It's also possible to get only Releases. E.g. the release of alt-tab,
1300 // or when the keypresses get captured by an accel.
1302 if( _ke
== d
->postponed_autorepeat
) // replayed event
1307 if( _ke
->type() == QEvent::KeyPress
)
1309 if( !_ke
->isAutoRepeat())
1311 bool ret
= dispatchKeyEventHelper( _ke
, false ); // keydown
1312 // don't send keypress even if keydown was blocked, like IE (and unlike Mozilla)
1313 if( !ret
&& dispatchKeyEventHelper( _ke
, true )) // keypress
1319 bool ret
= dispatchKeyEventHelper( _ke
, true ); // keypress
1320 if( !ret
&& d
->postponed_autorepeat
)
1321 keyPressEvent( d
->postponed_autorepeat
);
1322 delete d
->postponed_autorepeat
;
1323 d
->postponed_autorepeat
= NULL
;
1327 else // QEvent::KeyRelease
1329 // Discard postponed "autorepeat key-release" events that didn't see
1330 // a keypress after them (e.g. due to QAccel)
1331 if ( d
->postponed_autorepeat
) {
1332 delete d
->postponed_autorepeat
;
1333 d
->postponed_autorepeat
= 0;
1336 if( !_ke
->isAutoRepeat()) {
1337 return dispatchKeyEventHelper( _ke
, false ); // keyup
1341 d
->postponed_autorepeat
= new QKeyEvent( _ke
->type(), _ke
->key(), _ke
->modifiers(),
1342 _ke
->text(), _ke
->isAutoRepeat(), _ke
->count());
1343 if( _ke
->isAccepted())
1344 d
->postponed_autorepeat
->accept();
1346 d
->postponed_autorepeat
->ignore();
1352 // returns true if event should be swallowed
1353 bool KHTMLView::dispatchKeyEventHelper( QKeyEvent
*_ke
, bool keypress
)
1355 DOM::NodeImpl
* keyNode
= m_part
->xmlDocImpl()->focusNode();
1357 return keyNode
->dispatchKeyEvent(_ke
, keypress
);
1358 } else { // no focused node, send to document
1359 return m_part
->xmlDocImpl()->dispatchKeyEvent(_ke
, keypress
);
1363 void KHTMLView::keyPressEvent( QKeyEvent
*_ke
)
1365 #ifndef KHTML_NO_TYPE_AHEAD_FIND
1366 if(d
->typeAheadActivated
)
1368 // type-ahead find aka find-as-you-type
1369 if(_ke
->key() == Qt::Key_Backspace
)
1371 d
->findString
= d
->findString
.left(d
->findString
.length() - 1);
1373 if(!d
->findString
.isEmpty())
1382 d
->timer
.setSingleShot(true);
1383 d
->timer
.start(3000);
1387 else if(_ke
->key() == Qt::Key_Escape
)
1394 else if(_ke
->key() == Qt::Key_Space
|| !_ke
->text().trimmed().isEmpty())
1396 d
->findString
+= _ke
->text();
1400 d
->timer
.setSingleShot(true);
1401 d
->timer
.start(3000);
1406 #endif // KHTML_NO_TYPE_AHEAD_FIND
1408 #ifndef KHTML_NO_CARET
1409 if (m_part
->isEditable() || m_part
->isCaretMode()
1410 || (m_part
->xmlDocImpl() && m_part
->xmlDocImpl()->focusNode()
1411 && m_part
->xmlDocImpl()->focusNode()->contentEditable())) {
1412 d
->caretViewContext()->keyReleasePending
= true;
1413 caretKeyPressEvent(_ke
);
1416 #endif // KHTML_NO_CARET
1418 // If CTRL was hit, be prepared for access keys
1419 if (d
->accessKeysEnabled
&& _ke
->key() == Qt::Key_Control
&& _ke
->modifiers()==0 && !d
->accessKeysActivated
)
1421 d
->accessKeysPreActivate
=true;
1426 if (_ke
->key() == Qt::Key_Shift
&& _ke
->modifiers()==0)
1427 d
->scrollSuspendPreActivate
=true;
1429 // accesskey handling needs to be done before dispatching, otherwise e.g. lineedits
1430 // may eat the event
1432 if (d
->accessKeysEnabled
&& d
->accessKeysActivated
)
1434 int state
= ( _ke
->modifiers() & ( Qt::ShiftModifier
| Qt::ControlModifier
| Qt::AltModifier
| Qt::MetaModifier
));
1435 if ( state
==0 || state
==Qt::ShiftModifier
) {
1436 if (_ke
->key() != Qt::Key_Shift
) accessKeysTimeout();
1437 handleAccessKey( _ke
);
1441 accessKeysTimeout();
1444 if ( dispatchKeyEvent( _ke
)) {
1445 // If either keydown or keypress was accepted by a widget, or canceled by JS, stop here.
1450 int offs
= (viewport()->height() < 30) ? viewport()->height() : 30; // ### ??
1451 if (_ke
->modifiers() & Qt::ShiftModifier
)
1455 verticalScrollBar()->setValue( verticalScrollBar()->value() -viewport()->height() + offs
);
1456 if(d
->scrollSuspended
)
1457 d
->newScrollTimer(this, 0);
1462 d
->adjustScroller(this, KHTMLViewPrivate::ScrollDown
, KHTMLViewPrivate::ScrollUp
);
1467 d
->adjustScroller(this, KHTMLViewPrivate::ScrollUp
, KHTMLViewPrivate::ScrollDown
);
1472 d
->adjustScroller(this, KHTMLViewPrivate::ScrollLeft
, KHTMLViewPrivate::ScrollRight
);
1477 d
->adjustScroller(this, KHTMLViewPrivate::ScrollRight
, KHTMLViewPrivate::ScrollLeft
);
1481 switch ( _ke
->key() )
1485 if (!d
->scrollTimerId
|| d
->scrollSuspended
)
1486 verticalScrollBar()->setValue( verticalScrollBar()->value()+10 );
1487 if (d
->scrollTimerId
)
1488 d
->newScrollTimer(this, 0);
1492 case Qt::Key_PageDown
:
1493 verticalScrollBar()->setValue( verticalScrollBar()->value() +viewport()->height() - offs
);
1494 if(d
->scrollSuspended
)
1495 d
->newScrollTimer(this, 0);
1500 if (!d
->scrollTimerId
|| d
->scrollSuspended
)
1501 verticalScrollBar()->setValue( verticalScrollBar()->value()-10 );
1502 if (d
->scrollTimerId
)
1503 d
->newScrollTimer(this, 0);
1506 case Qt::Key_PageUp
:
1507 verticalScrollBar()->setValue( verticalScrollBar()->value() -viewport()->height() + offs
);
1508 if(d
->scrollSuspended
)
1509 d
->newScrollTimer(this, 0);
1513 if (!d
->scrollTimerId
|| d
->scrollSuspended
)
1514 horizontalScrollBar()->setValue( horizontalScrollBar()->value()+10 );
1515 if (d
->scrollTimerId
)
1516 d
->newScrollTimer(this, 0);
1521 if (!d
->scrollTimerId
|| d
->scrollSuspended
)
1522 horizontalScrollBar()->setValue( horizontalScrollBar()->value()-10 );
1523 if (d
->scrollTimerId
)
1524 d
->newScrollTimer(this, 0);
1527 case Qt::Key_Return
:
1529 // or even better to HTMLAnchorElementImpl::event()
1530 if (m_part
->xmlDocImpl()) {
1531 NodeImpl
*n
= m_part
->xmlDocImpl()->focusNode();
1537 verticalScrollBar()->setValue( 0 );
1538 horizontalScrollBar()->setValue( 0 );
1539 if(d
->scrollSuspended
)
1540 d
->newScrollTimer(this, 0);
1543 verticalScrollBar()->setValue( contentsHeight() - visibleHeight() );
1544 if(d
->scrollSuspended
)
1545 d
->newScrollTimer(this, 0);
1548 // what are you doing here?
1552 if (d
->scrollTimerId
)
1553 d
->newScrollTimer(this, 0);
1561 void KHTMLView::findTimeout()
1563 #ifndef KHTML_NO_TYPE_AHEAD_FIND
1564 d
->typeAheadActivated
= false;
1566 m_part
->setStatusBarText(i18n("Find stopped."), KHTMLPart::BarDefaultText
);
1567 m_part
->enableFindAheadActions( true );
1568 #endif // KHTML_NO_TYPE_AHEAD_FIND
1571 #ifndef KHTML_NO_TYPE_AHEAD_FIND
1572 void KHTMLView::startFindAhead( bool linksOnly
)
1576 d
->findLinksOnly
= true;
1577 m_part
->setStatusBarText(i18n("Starting -- find links as you type"),
1578 KHTMLPart::BarDefaultText
);
1582 d
->findLinksOnly
= false;
1583 m_part
->setStatusBarText(i18n("Starting -- find text as you type"),
1584 KHTMLPart::BarDefaultText
);
1587 m_part
->findTextBegin();
1588 d
->typeAheadActivated
= true;
1589 // disable, so that the shortcut ( / or ' by default ) doesn't interfere
1590 m_part
->enableFindAheadActions( false );
1591 d
->timer
.setSingleShot(true);
1592 d
->timer
.start(3000);
1595 void KHTMLView::findAhead(bool increase
)
1598 QString text
= d
->findString
.toLower();
1600 if(d
->findLinksOnly
)
1602 m_part
->findText(d
->findString
, KHTMLPart::FindNoPopups
|
1603 KHTMLPart::FindLinksOnly
, this);
1604 if(m_part
->findTextNext())
1606 status
= i18n("Link found: \"%1\".", text
);
1610 if(increase
) KNotification::beep();
1611 status
= i18n("Link not found: \"%1\".", text
);
1616 m_part
->findText(d
->findString
, KHTMLPart::FindNoPopups
, this);
1617 if(m_part
->findTextNext())
1619 status
= i18n("Text found: \"%1\".", text
);
1623 if(increase
) KNotification::beep();
1624 status
= i18n("Text not found: \"%1\".", text
);
1628 m_part
->setStatusBarText(status
, KHTMLPart::BarDefaultText
);
1631 void KHTMLView::updateFindAheadTimeout()
1633 if( d
->typeAheadActivated
) {
1634 d
->timer
.setSingleShot( true );
1635 d
->timer
.start( 3000 );
1639 #endif // KHTML_NO_TYPE_AHEAD_FIND
1641 void KHTMLView::keyReleaseEvent(QKeyEvent
*_ke
)
1643 #ifndef KHTML_NO_TYPE_AHEAD_FIND
1644 if(d
->typeAheadActivated
) {
1649 if (d
->m_caretViewContext
&& d
->m_caretViewContext
->keyReleasePending
) {
1650 //caretKeyReleaseEvent(_ke);
1651 d
->m_caretViewContext
->keyReleasePending
= false;
1655 if( d
->scrollSuspendPreActivate
&& _ke
->key() != Qt::Key_Shift
)
1656 d
->scrollSuspendPreActivate
= false;
1657 if( _ke
->key() == Qt::Key_Shift
&& d
->scrollSuspendPreActivate
&& _ke
->modifiers() == Qt::ShiftModifier
1658 && !(QApplication::keyboardModifiers() & Qt::ShiftModifier
))
1659 if (d
->scrollTimerId
)
1660 d
->scrollSuspended
= !d
->scrollSuspended
;
1662 if (d
->accessKeysEnabled
)
1664 if (d
->accessKeysPreActivate
&& _ke
->key() != Qt::Key_Control
)
1665 d
->accessKeysPreActivate
=false;
1666 if (d
->accessKeysPreActivate
&& _ke
->modifiers() == Qt::ControlModifier
&&
1667 !(QApplication::keyboardModifiers() & Qt::ControlModifier
))
1669 displayAccessKeys();
1670 m_part
->setStatusBarText(i18n("Access Keys activated"),KHTMLPart::BarOverrideText
);
1671 d
->accessKeysActivated
= true;
1672 d
->accessKeysPreActivate
= false;
1676 else if (d
->accessKeysActivated
)
1678 accessKeysTimeout();
1685 if ( dispatchKeyEvent( _ke
) )
1691 QScrollArea::keyReleaseEvent(_ke
);
1694 bool KHTMLView::focusNextPrevChild( bool next
)
1696 // Now try to find the next child
1697 if (m_part
->xmlDocImpl() && focusNextPrevNode(next
))
1699 if (m_part
->xmlDocImpl()->focusNode())
1700 kDebug() << "focusNode.name: "
1701 << m_part
->xmlDocImpl()->focusNode()->nodeName().string() << endl
;
1702 return true; // focus node found
1705 // If we get here, pass tabbing control up to the next/previous child in our parent
1706 d
->pseudoFocusNode
= KHTMLViewPrivate::PFNone
;
1707 if (m_part
->parentPart() && m_part
->parentPart()->view())
1708 return m_part
->parentPart()->view()->focusNextPrevChild(next
);
1710 return QWidget::focusNextPrevChild(next
);
1713 void KHTMLView::doAutoScroll()
1715 QPoint pos
= QCursor::pos();
1716 pos
= viewport()->mapFromGlobal( pos
);
1719 viewportToContents(pos
.x(), pos
.y(), xm
, ym
);
1721 pos
= QPoint(pos
.x() - viewport()->x(), pos
.y() - viewport()->y());
1722 if ( (pos
.y() < 0) || (pos
.y() > visibleHeight()) ||
1723 (pos
.x() < 0) || (pos
.x() > visibleWidth()) )
1725 ensureVisible( xm
, ym
, 0, 5 );
1727 #ifndef KHTML_NO_SELECTION
1728 // extend the selection while scrolling
1729 DOM::Node innerNode
;
1730 if (m_part
->isExtendingSelection()) {
1731 RenderObject::NodeInfo
renderInfo(true/*readonly*/, false/*active*/);
1732 m_part
->xmlDocImpl()->renderer()->layer()
1733 ->nodeAtPoint(renderInfo
, xm
, ym
);
1734 innerNode
= renderInfo
.innerNode();
1737 if (innerNode
.handle() && innerNode
.handle()->renderer()) {
1739 innerNode
.handle()->renderer()->absolutePosition(absX
, absY
);
1741 m_part
->extendSelectionTo(xm
, ym
, absX
, absY
, innerNode
);
1743 #endif // KHTML_NO_SELECTION
1747 static void handleWidget(QWidget
* w
, KHTMLView
* view
)
1749 if (w
->isTopLevel())
1752 if (!qobject_cast
<QFrame
*>(w
))
1753 w
->setAttribute( Qt::WA_NoSystemBackground
);
1754 w
->setAttribute(Qt::WA_WState_InPaintEvent
); // ### horrible - FIXME (needs Qt change to Widget::update)
1755 w
->setAttribute(Qt::WA_OpaquePaintEvent
);
1756 w
->installEventFilter(view
);
1758 QObjectList children
= w
->children();
1759 foreach (QObject
* object
, children
) {
1760 QWidget
*widget
= qobject_cast
<QWidget
*>(object
);
1762 handleWidget(widget
, view
);
1766 class KHTMLBackingStoreHackWidget
: public QWidget
1769 void publicEvent(QEvent
*e
)
1775 bool KHTMLView::viewportEvent ( QEvent
* e
)
1777 switch (e
->type()) {
1778 // those must not be dispatched to the specialized handlers
1779 // as widgetEvent() already took care of that
1780 case QEvent::MouseButtonPress
:
1781 case QEvent::MouseButtonRelease
:
1782 case QEvent::MouseButtonDblClick
:
1783 case QEvent::MouseMove
:
1784 #ifndef QT_NO_WHEELEVENT
1787 case QEvent::ContextMenu
:
1788 case QEvent::DragEnter
:
1789 case QEvent::DragMove
:
1790 case QEvent::DragLeave
:
1793 case QEvent::Paint
: {
1794 QRect r
= static_cast<QPaintEvent
*>(e
)->rect();
1795 r
.setX(r
.x() +contentsX());
1796 r
.setY(r
.y() +contentsY());
1804 return QScrollArea::viewportEvent(e
);
1807 bool KHTMLView::eventFilter(QObject
*o
, QEvent
*e
)
1809 if ( e
->type() == QEvent::ShortcutOverride
) {
1810 QKeyEvent
* ke
= (QKeyEvent
*) e
;
1811 if (m_part
->isEditable() || m_part
->isCaretMode()
1812 || (m_part
->xmlDocImpl() && m_part
->xmlDocImpl()->focusNode()
1813 && m_part
->xmlDocImpl()->focusNode()->contentEditable())) {
1814 if ( (ke
->modifiers() & Qt::ControlModifier
) || (ke
->modifiers() & Qt::ShiftModifier
) ) {
1815 switch ( ke
->key() ) {
1831 if ( e
->type() == QEvent::Leave
) {
1832 if ( d
->cursor_icon_widget
)
1833 d
->cursor_icon_widget
->hide();
1834 m_part
->resetHoverText();
1837 QWidget
*view
= widget();
1841 } else if (o
->isWidgetType()) {
1842 QWidget
*v
= static_cast<QWidget
*>(o
);
1844 while (v
&& v
!= view
) {
1846 v
= v
->parentWidget();
1848 KHTMLWidget
* k
= dynamic_cast<KHTMLWidget
*>(c
);
1849 if (v
&& k
&& k
->m_kwp
->isRedirected()) {
1851 bool isUpdate
= false;
1852 QWidget
*w
= static_cast<QWidget
*>(o
);
1854 case QEvent::UpdateRequest
: {
1855 // implicitly call qt_syncBackingStore(w)
1856 static_cast<KHTMLBackingStoreHackWidget
*>(w
)->publicEvent(e
);
1860 case QEvent::UpdateLater
:
1864 if (!allowWidgetPaintEvents
) {
1865 // eat the event. Like this we can control exactly when the widget
1870 while (v
&& v
->parentWidget() != view
) {
1873 v
= v
->parentWidget();
1876 QPoint ap
= k
->m_kwp
->absolutePos();
1880 QRect pr
= isUpdate
? static_cast<QUpdateLaterEvent
*>(e
)->region().boundingRect() : static_cast<QPaintEvent
*>(e
)->rect();
1881 bool asap
= !isUpdate
&& !d
->contentsMoving
&& (qobject_cast
<Q3ScrollView
*>(c
) || qobject_cast
<QAbstractScrollArea
*>(c
));
1884 // ### horrible - FIXME
1885 w
->setAttribute(Qt::WA_WState_InPaintEvent
, false);
1886 w
->update(static_cast<QUpdateLaterEvent
*>(e
)->region());
1887 w
->setAttribute(Qt::WA_WState_InPaintEvent
);
1888 // implicitly call qt_syncBackingStore(w)
1889 QEvent
fakeEvent(QEvent::UpdateRequest
);
1890 static_cast<KHTMLBackingStoreHackWidget
*>(w
)->publicEvent(&fakeEvent
);
1893 // QScrollView needs fast repaints
1894 if ( asap
&& !d
->painting
&& m_part
->xmlDocImpl() && m_part
->xmlDocImpl()->renderer() &&
1895 !static_cast<khtml::RenderCanvas
*>(m_part
->xmlDocImpl()->renderer())->needsLayout() ) {
1897 repaintContents(x
+ pr
.x(), y
+ pr
.y(),
1898 pr
.width(), pr
.height()+1); // ### investigate that +1 (shows up when
1899 // updating e.g a textarea's blinking cursor)
1900 d
->painting
= false;
1901 } else if (!d
->painting
) {
1902 scheduleRepaint(x
+ pr
.x(), y
+ pr
.y(),
1903 pr
.width(), pr
.height()+1, asap
);
1907 case QEvent::MouseMove
:
1908 case QEvent::MouseButtonPress
:
1909 case QEvent::MouseButtonRelease
:
1910 case QEvent::MouseButtonDblClick
: {
1912 if (0 && w
->parentWidget() == view
&& !qobject_cast
<QScrollBar
*>(w
) && !::qobject_cast
<QScrollBar
*>(w
)) {
1913 QMouseEvent
*me
= static_cast<QMouseEvent
*>(e
);
1914 QPoint pt
= w
->mapTo( view
, me
->pos());
1915 QMouseEvent
me2(me
->type(), pt
, me
->button(), me
->buttons(), me
->modifiers());
1917 if (e
->type() == QEvent::MouseMove
)
1918 mouseMoveEvent(&me2
);
1919 else if(e
->type() == QEvent::MouseButtonPress
)
1920 mousePressEvent(&me2
);
1921 else if(e
->type() == QEvent::MouseButtonRelease
)
1922 mouseReleaseEvent(&me2
);
1924 mouseDoubleClickEvent(&me2
);
1929 case QEvent::KeyPress
:
1930 case QEvent::KeyRelease
:
1931 if (w
->parentWidget() == view
&& !qobject_cast
<QScrollBar
*>(w
)) {
1932 QKeyEvent
*ke
= static_cast<QKeyEvent
*>(e
);
1933 if (e
->type() == QEvent::KeyPress
)
1936 keyReleaseEvent(ke
);
1940 case QEvent::FocusIn
:
1941 case QEvent::FocusOut
:
1948 //qDebug("eating event");
1954 // kDebug(6000) <<"passing event on to sv event filter object=" << o->className() << " event=" << e->type() << endl;
1955 return QScrollArea::eventFilter(o
, e
);
1959 bool KHTMLView::widgetEvent(QEvent
* e
)
1961 switch (e
->type()) {
1962 case QEvent::MouseButtonPress
:
1963 case QEvent::MouseButtonRelease
:
1964 case QEvent::MouseButtonDblClick
:
1965 case QEvent::MouseMove
:
1967 #ifndef QT_NO_WHEELEVENT
1970 case QEvent::ContextMenu
:
1971 case QEvent::DragEnter
:
1972 case QEvent::DragMove
:
1973 case QEvent::DragLeave
:
1975 return QFrame::event(e
);
1976 case QEvent::ChildInserted
: {
1977 // we need to install an event filter on all children of the widget() to
1978 // be able to get correct stacking of children within the document.
1979 QObject
*c
= static_cast<QChildEvent
*>(e
)->child();
1980 if (c
->isWidgetType()) {
1981 QWidget
*w
= static_cast<QWidget
*>(c
);
1982 // don't install the event filter on toplevels
1983 if (w
->parentWidget() == widget()) {
1984 KHTMLWidget
* k
= dynamic_cast<KHTMLWidget
*>(w
);
1985 if (k
&& k
->m_kwp
->isRedirected()) {
1987 handleWidget(w
, this);
1998 DOM::NodeImpl
*KHTMLView::nodeUnderMouse() const
2000 return d
->underMouse
;
2003 DOM::NodeImpl
*KHTMLView::nonSharedNodeUnderMouse() const
2005 return d
->underMouseNonShared
;
2008 bool KHTMLView::scrollTo(const QRect
&bounds
)
2010 d
->scrollingSelf
= true; // so scroll events get ignored
2015 xe
= bounds
.right();
2016 ye
= bounds
.bottom();
2018 //kDebug(6000)<<"scrolling coords: x="<<x<<" y="<<y<<" width="<<xe-x<<" height="<<ye-y<<endl;
2023 int curHeight
= visibleHeight();
2024 int curWidth
= visibleWidth();
2026 if (ye
-y
>curHeight
-d
->borderY
)
2027 ye
= y
+ curHeight
- d
->borderY
;
2029 if (xe
-x
>curWidth
-d
->borderX
)
2030 xe
= x
+ curWidth
- d
->borderX
;
2032 // is xpos of target left of the view's border?
2033 if (x
< contentsX() + d
->borderX
)
2034 deltax
= x
- contentsX() - d
->borderX
;
2035 // is xpos of target right of the view's right border?
2036 else if (xe
+ d
->borderX
> contentsX() + curWidth
)
2037 deltax
= xe
+ d
->borderX
- ( contentsX() + curWidth
);
2041 // is ypos of target above upper border?
2042 if (y
< contentsY() + d
->borderY
)
2043 deltay
= y
- contentsY() - d
->borderY
;
2044 // is ypos of target below lower border?
2045 else if (ye
+ d
->borderY
> contentsY() + curHeight
)
2046 deltay
= ye
+ d
->borderY
- ( contentsY() + curHeight
);
2050 int maxx
= curWidth
-d
->borderX
;
2051 int maxy
= curHeight
-d
->borderY
;
2053 int scrollX
, scrollY
;
2055 scrollX
= deltax
> 0 ? (deltax
> maxx
? maxx
: deltax
) : deltax
== 0 ? 0 : (deltax
>-maxx
? deltax
: -maxx
);
2056 scrollY
= deltay
> 0 ? (deltay
> maxy
? maxy
: deltay
) : deltay
== 0 ? 0 : (deltay
>-maxy
? deltay
: -maxy
);
2058 if (contentsX() + scrollX
< 0)
2059 scrollX
= -contentsX();
2060 else if (contentsWidth() - visibleWidth() - contentsX() < scrollX
)
2061 scrollX
= contentsWidth() - visibleWidth() - contentsX();
2063 if (contentsY() + scrollY
< 0)
2064 scrollY
= -contentsY();
2065 else if (contentsHeight() - visibleHeight() - contentsY() < scrollY
)
2066 scrollY
= contentsHeight() - visibleHeight() - contentsY();
2068 horizontalScrollBar()->setValue( horizontalScrollBar()->value()+scrollX
);
2069 verticalScrollBar()->setValue( verticalScrollBar()->value()+scrollY
);
2071 d
->scrollingSelf
= false;
2073 if ( (abs(deltax
)<=maxx
) && (abs(deltay
)<=maxy
) )
2079 bool KHTMLView::focusNextPrevNode(bool next
)
2081 // Sets the focus node of the document to be the node after (or if
2082 // next is false, before) the current focus node. Only nodes that
2083 // are selectable (i.e. for which isFocusable() returns true) are
2084 // taken into account, and the order used is that specified in the
2085 // HTML spec (see DocumentImpl::nextFocusNode() and
2086 // DocumentImpl::previousFocusNode() for details).
2088 DocumentImpl
*doc
= m_part
->xmlDocImpl();
2089 NodeImpl
*oldFocusNode
= doc
->focusNode();
2091 // See whether we're in the middle of detach. If so, we want to
2092 // clear focus... The document code will be careful to not
2093 // emit events in that case..
2094 if (oldFocusNode
&& oldFocusNode
->renderer() &&
2095 !oldFocusNode
->renderer()->parent()) {
2096 doc
->setFocusNode(0);
2101 // If the user has scrolled the document, then instead of picking
2102 // the next focusable node in the document, use the first one that
2103 // is within the visible area (if possible).
2104 if (d
->scrollBarMoved
)
2108 toFocus
= doc
->nextFocusNode(oldFocusNode
);
2110 toFocus
= doc
->previousFocusNode(oldFocusNode
);
2112 if (!toFocus
&& oldFocusNode
)
2114 toFocus
= doc
->nextFocusNode(NULL
);
2116 toFocus
= doc
->previousFocusNode(NULL
);
2118 while (toFocus
&& toFocus
!= oldFocusNode
)
2121 QRect focusNodeRect
= toFocus
->getRect();
2122 if ((focusNodeRect
.left() > contentsX()) && (focusNodeRect
.right() < contentsX() + visibleWidth()) &&
2123 (focusNodeRect
.top() > contentsY()) && (focusNodeRect
.bottom() < contentsY() + visibleHeight())) {
2125 QRect r
= toFocus
->getRect();
2126 ensureVisible( r
.right(), r
.bottom());
2127 ensureVisible( r
.left(), r
.top());
2128 d
->scrollBarMoved
= false;
2129 d
->tabMovePending
= false;
2130 d
->lastTabbingDirection
= next
;
2131 d
->pseudoFocusNode
= KHTMLViewPrivate::PFNone
;
2132 m_part
->xmlDocImpl()->setFocusNode(toFocus
);
2133 Node
guard(toFocus
);
2134 if (!toFocus
->hasOneRef() )
2136 emit m_part
->nodeActivated(Node(toFocus
));
2142 toFocus
= doc
->nextFocusNode(toFocus
);
2144 toFocus
= doc
->previousFocusNode(toFocus
);
2146 if (!toFocus
&& oldFocusNode
)
2148 toFocus
= doc
->nextFocusNode(NULL
);
2150 toFocus
= doc
->previousFocusNode(NULL
);
2153 d
->scrollBarMoved
= false;
2157 if (!oldFocusNode
&& d
->pseudoFocusNode
== KHTMLViewPrivate::PFNone
)
2159 ensureVisible(contentsX(), next
?0:contentsHeight());
2160 d
->scrollBarMoved
= false;
2161 d
->pseudoFocusNode
= next
?KHTMLViewPrivate::PFTop
:KHTMLViewPrivate::PFBottom
;
2165 NodeImpl
*newFocusNode
= NULL
;
2167 if (d
->tabMovePending
&& next
!= d
->lastTabbingDirection
)
2169 //kDebug ( 6000 ) << " tab move pending and tabbing direction changed!\n";
2170 newFocusNode
= oldFocusNode
;
2174 if (oldFocusNode
|| d
->pseudoFocusNode
== KHTMLViewPrivate::PFTop
)
2175 newFocusNode
= doc
->nextFocusNode(oldFocusNode
);
2179 if (oldFocusNode
|| d
->pseudoFocusNode
== KHTMLViewPrivate::PFBottom
)
2180 newFocusNode
= doc
->previousFocusNode(oldFocusNode
);
2183 bool targetVisible
= false;
2188 targetVisible
= scrollTo(QRect(contentsX()+visibleWidth()/2,contentsHeight()-d
->borderY
,0,0));
2192 targetVisible
= scrollTo(QRect(contentsX()+visibleWidth()/2,d
->borderY
,0,0));
2197 #ifndef KHTML_NO_CARET
2198 // if it's an editable element, activate the caret
2199 if (!m_part
->isCaretMode() && !m_part
->isEditable()
2200 && newFocusNode
->contentEditable()) {
2201 d
->caretViewContext();
2202 moveCaretTo(newFocusNode
, 0L, true);
2206 #endif // KHTML_NO_CARET
2208 targetVisible
= scrollTo(newFocusNode
->getRect());
2213 //kDebug ( 6000 ) << " target reached.\n";
2214 d
->tabMovePending
= false;
2216 m_part
->xmlDocImpl()->setFocusNode(newFocusNode
);
2219 Node
guard(newFocusNode
);
2220 if (!newFocusNode
->hasOneRef() )
2222 emit m_part
->nodeActivated(Node(newFocusNode
));
2228 d
->pseudoFocusNode
= next
?KHTMLViewPrivate::PFBottom
:KHTMLViewPrivate::PFTop
;
2234 if (!d
->tabMovePending
)
2235 d
->lastTabbingDirection
= next
;
2236 d
->tabMovePending
= true;
2241 void KHTMLView::displayAccessKeys()
2243 QVector
< QChar
> taken
;
2244 displayAccessKeys( NULL
, this, taken
, false );
2245 displayAccessKeys( NULL
, this, taken
, true );
2248 void KHTMLView::displayAccessKeys( KHTMLView
* caller
, KHTMLView
* origview
, QVector
< QChar
>& taken
, bool use_fallbacks
)
2250 QMap
< ElementImpl
*, QChar
> fallbacks
;
2252 fallbacks
= buildFallbackAccessKeys();
2253 for( NodeImpl
* n
= m_part
->xmlDocImpl(); n
!= NULL
; n
= n
->traverseNextNode()) {
2254 if( n
->isElementNode()) {
2255 ElementImpl
* en
= static_cast< ElementImpl
* >( n
);
2256 DOMString s
= en
->getAttribute( ATTR_ACCESSKEY
);
2258 if( s
.length() == 1 ) {
2259 QChar a
= s
.string()[ 0 ].toUpper();
2260 if( qFind( taken
.begin(), taken
.end(), a
) == taken
.end()) // !contains
2263 if( accesskey
.isNull() && fallbacks
.contains( en
)) {
2264 QChar a
= fallbacks
[ en
].toUpper();
2265 if( qFind( taken
.begin(), taken
.end(), a
) == taken
.end()) // !contains
2266 accesskey
= QString( "<qt><i>" ) + a
+ "</i></qt>";
2268 if( !accesskey
.isNull()) {
2269 QRect rec
=en
->getRect();
2270 QLabel
*lab
=new QLabel(accesskey
,viewport(),Qt::WDestructiveClose
);
2271 connect( origview
, SIGNAL(hideAccessKeys()), lab
, SLOT(close()) );
2272 connect( this, SIGNAL(repaintAccessKeys()), lab
, SLOT(repaint()));
2273 lab
->setPalette(QToolTip::palette());
2274 lab
->setLineWidth(2);
2275 lab
->setFrameStyle(QFrame::Box
| QFrame::Plain
);
2278 lab
->setParent( widget() );
2280 qMin(rec
.left()+rec
.width()/2, contentsWidth() - lab
->width()),
2281 qMin(rec
.top()+rec
.height()/2, contentsHeight() - lab
->height()));
2283 taken
.append( accesskey
[ 0 ] );
2290 QList
<KParts::ReadOnlyPart
*> frames
= m_part
->frames();
2291 foreach( KParts::ReadOnlyPart
* cur
, frames
) {
2292 if( !qobject_cast
<KHTMLPart
*>(cur
) )
2294 KHTMLPart
* part
= static_cast< KHTMLPart
* >( cur
);
2295 if( part
->view() && part
->view() != caller
)
2296 part
->view()->displayAccessKeys( this, origview
, taken
, use_fallbacks
);
2299 // pass up to the parent
2300 if (m_part
->parentPart() && m_part
->parentPart()->view()
2301 && m_part
->parentPart()->view() != caller
)
2302 m_part
->parentPart()->view()->displayAccessKeys( this, origview
, taken
, use_fallbacks
);
2307 void KHTMLView::accessKeysTimeout()
2309 d
->accessKeysActivated
=false;
2310 d
->accessKeysPreActivate
= false;
2311 m_part
->setStatusBarText(QString(), KHTMLPart::BarOverrideText
);
2312 emit
hideAccessKeys();
2315 // Handling of the HTML accesskey attribute.
2316 bool KHTMLView::handleAccessKey( const QKeyEvent
* ev
)
2318 // Qt interprets the keyevent also with the modifiers, and ev->text() matches that,
2319 // but this code must act as if the modifiers weren't pressed
2321 if( ev
->key() >= Qt::Key_A
&& ev
->key() <= Qt::Key_Z
)
2322 c
= 'A' + ev
->key() - Qt::Key_A
;
2323 else if( ev
->key() >= Qt::Key_0
&& ev
->key() <= Qt::Key_9
)
2324 c
= '0' + ev
->key() - Qt::Key_0
;
2326 // TODO fake XKeyEvent and XLookupString ?
2327 // This below seems to work e.g. for eacute though.
2328 if( ev
->text().length() == 1 )
2329 c
= ev
->text()[ 0 ];
2333 return focusNodeWithAccessKey( c
);
2336 bool KHTMLView::focusNodeWithAccessKey( QChar c
, KHTMLView
* caller
)
2338 DocumentImpl
*doc
= m_part
->xmlDocImpl();
2341 ElementImpl
* node
= doc
->findAccessKeyElement( c
);
2343 QList
<KParts::ReadOnlyPart
*> frames
= m_part
->frames();
2344 foreach( KParts::ReadOnlyPart
* cur
, frames
) {
2345 if( !qobject_cast
<KHTMLPart
*>(cur
) )
2347 KHTMLPart
* part
= static_cast< KHTMLPart
* >( cur
);
2348 if( part
->view() && part
->view() != caller
2349 && part
->view()->focusNodeWithAccessKey( c
, this ))
2352 // pass up to the parent
2353 if (m_part
->parentPart() && m_part
->parentPart()->view()
2354 && m_part
->parentPart()->view() != caller
2355 && m_part
->parentPart()->view()->focusNodeWithAccessKey( c
, this ))
2357 if( caller
== NULL
) { // the active frame (where the accesskey was pressed)
2358 QMap
< ElementImpl
*, QChar
> fallbacks
= buildFallbackAccessKeys();
2359 for( QMap
< ElementImpl
*, QChar
>::ConstIterator it
= fallbacks
.begin();
2360 it
!= fallbacks
.end();
2371 // Scroll the view as necessary to ensure that the new focus node is visible
2372 #ifndef KHTML_NO_CARET
2373 // if it's an editable element, activate the caret
2374 if (!m_part
->isCaretMode() && !m_part
->isEditable()
2375 && node
->contentEditable()) {
2376 d
->caretViewContext();
2377 moveCaretTo(node
, 0L, true);
2381 #endif // KHTML_NO_CARET
2383 QRect r
= node
->getRect();
2384 ensureVisible( r
.right(), r
.bottom());
2385 ensureVisible( r
.left(), r
.top());
2388 if( node
->isFocusable()) {
2389 if (node
->id()==ID_LABEL
) {
2390 // if Accesskey is a label, give focus to the label's referrer.
2391 node
=static_cast<ElementImpl
*>(static_cast< HTMLLabelElementImpl
* >( node
)->getFormElement());
2392 if (!node
) return true;
2395 // Set focus node on the document
2397 #warning "port QFocusEvent::setReason( QFocusEvent::Shortcut ); to qt4"
2399 //QFocusEvent::setReason( QFocusEvent::Shortcut );
2400 m_part
->xmlDocImpl()->setFocusNode(node
);
2402 #warning "port QFocusEvent::resetReason(); to qt4"
2404 //QFocusEvent::resetReason();
2405 if( node
!= NULL
&& node
->hasOneRef()) // deleted, only held by guard
2407 emit m_part
->nodeActivated(Node(node
));
2408 if( node
!= NULL
&& node
->hasOneRef())
2412 switch( node
->id()) {
2414 static_cast< HTMLAnchorElementImpl
* >( node
)->click();
2417 static_cast< HTMLInputElementImpl
* >( node
)->click();
2420 static_cast< HTMLButtonElementImpl
* >( node
)->click();
2423 static_cast< HTMLAreaElementImpl
* >( node
)->click();
2426 break; // just focusing it is enough
2434 static QString
getElementText( NodeImpl
* start
, bool after
)
2436 QString ret
; // nextSibling(), to go after e.g. </select>
2437 for( NodeImpl
* n
= after
? start
->nextSibling() : start
->traversePreviousNode();
2439 n
= after
? n
->traverseNextNode() : n
->traversePreviousNode()) {
2440 if( n
->isTextNode()) {
2442 ret
+= static_cast< TextImpl
* >( n
)->toString().string();
2444 ret
.prepend( static_cast< TextImpl
* >( n
)->toString().string());
2474 if( ret
.trimmed().isEmpty())
2478 return ret
.simplified();
2482 return ret
.simplified();
2485 static QMap
< NodeImpl
*, QString
> buildLabels( NodeImpl
* start
)
2487 QMap
< NodeImpl
*, QString
> ret
;
2488 for( NodeImpl
* n
= start
;
2490 n
= n
->traverseNextNode()) {
2491 if( n
->id() == ID_LABEL
) {
2492 HTMLLabelElementImpl
* label
= static_cast< HTMLLabelElementImpl
* >( n
);
2493 NodeImpl
* labelfor
= label
->getFormElement();
2495 ret
[ labelfor
] = label
->innerText().string().simplified();
2502 struct AccessKeyData
{
2503 ElementImpl
* element
;
2506 int priority
; // 10(highest) - 0(lowest)
2510 QMap
< ElementImpl
*, QChar
> KHTMLView::buildFallbackAccessKeys() const
2512 // build a list of all possible candidate elements that could use an accesskey
2513 QList
< AccessKeyData
> data
;
2514 QMap
< NodeImpl
*, QString
> labels
= buildLabels( m_part
->xmlDocImpl());
2515 for( NodeImpl
* n
= m_part
->xmlDocImpl();
2517 n
= n
->traverseNextNode()) {
2518 if( n
->isElementNode()) {
2519 ElementImpl
* element
= static_cast< ElementImpl
* >( n
);
2520 if( element
->getAttribute( ATTR_ACCESSKEY
).length() == 1 )
2521 continue; // has accesskey set, ignore
2522 if( element
->renderer() == NULL
)
2523 continue; // not visible
2527 bool ignore
= false;
2528 bool text_after
= false;
2529 bool text_before
= false;
2530 switch( element
->id()) {
2532 url
= khtml::parseURL(element
->getAttribute(ATTR_HREF
)).string();
2533 if( url
.isEmpty()) // doesn't have href, it's only an anchor
2535 text
= static_cast< HTMLElementImpl
* >( element
)->innerText().string().simplified();
2539 HTMLInputElementImpl
* in
= static_cast< HTMLInputElementImpl
* >( element
);
2540 switch( in
->inputType()) {
2541 case HTMLInputElementImpl::SUBMIT
:
2542 text
= in
->value().string();
2544 text
= i18n( "Submit" );
2547 case HTMLInputElementImpl::IMAGE
:
2548 text
= in
->altText().string();
2551 case HTMLInputElementImpl::BUTTON
:
2552 text
= in
->value().string();
2555 case HTMLInputElementImpl::RESET
:
2556 text
= in
->value().string();
2558 text
= i18n( "Reset" );
2561 case HTMLInputElementImpl::HIDDEN
:
2564 case HTMLInputElementImpl::CHECKBOX
:
2565 case HTMLInputElementImpl::RADIO
:
2569 case HTMLInputElementImpl::TEXT
:
2570 case HTMLInputElementImpl::PASSWORD
:
2571 case HTMLInputElementImpl::FILE:
2582 text
= static_cast< HTMLElementImpl
* >( element
)->innerText().string().simplified();
2583 switch( static_cast< HTMLButtonElementImpl
* >( element
)->buttonType()) {
2584 case HTMLButtonElementImpl::SUBMIT
:
2586 text
= i18n( "Submit" );
2589 case HTMLButtonElementImpl::RESET
:
2591 text
= i18n( "Reset" );
2599 case ID_SELECT
: // these don't have accesskey attribute, but quick access may be handy
2608 ignore
= !element
->isFocusable();
2614 if( text
.isNull() && labels
.contains( element
))
2615 text
= labels
[ element
];
2616 if( text
.isNull() && text_before
)
2617 text
= getElementText( element
, false );
2618 if( text
.isNull() && text_after
)
2619 text
= getElementText( element
, true );
2620 text
= text
.trimmed();
2621 // increase priority of items which have explicitly specified accesskeys in the config
2622 QList
< QPair
< QString
, QChar
> > priorities
2623 = m_part
->settings()->fallbackAccessKeysAssignments();
2624 for( QList
< QPair
< QString
, QChar
> >::ConstIterator it
= priorities
.begin();
2625 it
!= priorities
.end();
2627 if( text
== (*it
).first
)
2630 AccessKeyData tmp
= { element
, text
, url
, priority
};
2635 QList
< QChar
> keys
;
2636 for( char c
= 'A'; c
<= 'Z'; ++c
)
2638 for( char c
= '0'; c
<= '9'; ++c
)
2640 for( NodeImpl
* n
= m_part
->xmlDocImpl();
2642 n
= n
->traverseNextNode()) {
2643 if( n
->isElementNode()) {
2644 ElementImpl
* en
= static_cast< ElementImpl
* >( n
);
2645 DOMString s
= en
->getAttribute( ATTR_ACCESSKEY
);
2646 if( s
.length() == 1 ) {
2647 QChar c
= s
.string()[ 0 ].toUpper();
2648 keys
.removeAll( c
); // remove manually assigned accesskeys
2653 QMap
< ElementImpl
*, QChar
> ret
;
2654 for( int priority
= 10;
2657 for( QList
< AccessKeyData
>::Iterator it
= data
.begin();
2660 if( (*it
).priority
!= priority
) {
2666 QString text
= (*it
).text
;
2668 if( key
.isNull() && !text
.isEmpty()) {
2669 QList
< QPair
< QString
, QChar
> > priorities
2670 = m_part
->settings()->fallbackAccessKeysAssignments();
2671 for( QList
< QPair
< QString
, QChar
> >::ConstIterator it
= priorities
.begin();
2672 it
!= priorities
.end();
2674 if( text
== (*it
).first
&& keys
.contains( (*it
).second
)) {
2679 // try first to select the first character as the accesskey,
2680 // then first character of the following words,
2681 // and then simply the first free character
2682 if( key
.isNull() && !text
.isEmpty()) {
2683 QStringList words
= text
.split( ' ' );
2684 for( QStringList::ConstIterator it
= words
.begin();
2687 if( keys
.contains( (*it
)[ 0 ].toUpper())) {
2688 key
= (*it
)[ 0 ].toUpper();
2693 if( key
.isNull() && !text
.isEmpty()) {
2694 for( int i
= 0; i
< text
.length(); ++i
) {
2695 if( keys
.contains( text
[ i
].toUpper())) {
2696 key
= text
[ i
].toUpper();
2703 ret
[ (*it
).element
] = key
;
2704 keys
.removeAll( key
);
2705 QString url
= (*it
).url
;
2706 it
= data
.erase( it
);
2707 // assign the same accesskey also to other elements pointing to the same url
2708 if( !url
.isEmpty() && !url
.startsWith( "javascript:", Qt::CaseInsensitive
)) {
2709 for( QList
< AccessKeyData
>::Iterator it2
= data
.begin();
2712 if( (*it2
).url
== url
) {
2713 ret
[ (*it2
).element
] = key
;
2716 it2
= data
.erase( it2
);
2726 void KHTMLView::setMediaType( const QString
&medium
)
2731 QString
KHTMLView::mediaType() const
2736 bool KHTMLView::pagedMode() const
2741 void KHTMLView::setWidgetVisible(RenderWidget
* w
, bool vis
)
2744 d
->visibleWidgets
.replace(w
, w
->widget());
2747 d
->visibleWidgets
.remove(w
);
2750 bool KHTMLView::needsFullRepaint() const
2752 return d
->needsFullRepaint
;
2755 void KHTMLView::print(bool quick
)
2757 if(!m_part
->xmlDocImpl()) return;
2758 khtml::RenderCanvas
*root
= static_cast<khtml::RenderCanvas
*>(m_part
->xmlDocImpl()->renderer());
2761 KPrinter
*printer
= new KPrinter(true, QPrinter::ScreenResolution
);
2762 printer
->addDialogPage(new KHTMLPrintSettings());
2763 QString docname
= m_part
->xmlDocImpl()->URL().prettyUrl();
2764 if ( !docname
.isEmpty() )
2765 docname
= KStringHandler::csqueeze(docname
, 80);
2766 if(quick
|| printer
->setup(this, i18n("Print %1", docname
))) {
2767 viewport()->setCursor( Qt::WaitCursor
); // only viewport(), no QApplication::, otherwise we get the busy cursor in kdeprint's dialogs
2769 printer
->setFullPage(false);
2770 printer
->setCreator(QString("KDE %1.%2.%3 HTML Library").arg(KDE_VERSION_MAJOR
).arg(KDE_VERSION_MINOR
).arg(KDE_VERSION_RELEASE
));
2771 printer
->setDocName(docname
);
2773 QPainter
*p
= new QPainter
;
2774 p
->begin( printer
);
2775 khtml::setPrintPainter( p
);
2777 m_part
->xmlDocImpl()->setPaintDevice( printer
);
2778 QString oldMediaType
= mediaType();
2779 setMediaType( "print" );
2780 // We ignore margin settings for html and body when printing
2781 // and use the default margins from the print-system
2782 // (In Qt 3.0.x the default margins are hardcoded in Qt)
2783 m_part
->xmlDocImpl()->setPrintStyleSheet( printer
->option("app-khtml-printfriendly") == "true" ?
2784 "* { background-image: none !important;"
2785 " background-color: white !important;"
2786 " color: black !important; }"
2787 "body { margin: 0px !important; }"
2788 "html { margin: 0px !important; }" :
2789 "body { margin: 0px !important; }"
2790 "html { margin: 0px !important; }"
2793 Q3PaintDeviceMetrics
metrics( printer
);
2795 kDebug(6000) << "printing: physical page width = " << metrics
.width()
2796 << " height = " << metrics
.height() << endl
;
2797 root
->setStaticMode(true);
2798 root
->setPagedMode(true);
2799 root
->setWidth(metrics
.width());
2800 // root->setHeight(metrics.height());
2801 root
->setPageTop(0);
2802 root
->setPageBottom(0);
2805 m_part
->xmlDocImpl()->styleSelector()->computeFontSizes(&metrics
, 100);
2806 m_part
->xmlDocImpl()->updateStyleSelector();
2807 root
->setPrintImages( printer
->option("app-khtml-printimages") == "true");
2808 root
->makePageBreakAvoidBlocks();
2810 root
->setNeedsLayoutAndMinMaxRecalc();
2812 khtml::RenderWidget::flushWidgetResizes(); // make sure widgets have their final size
2814 // check sizes ask for action.. (scale or clip)
2816 bool printHeader
= (printer
->option("app-khtml-printheader") == "true");
2818 int headerHeight
= 0;
2819 QFont
headerFont("Sans Serif", 8);
2821 QString headerLeft
= KGlobal::locale()->formatDate(QDate::currentDate(),true);
2822 QString headerMid
= docname
;
2823 QString headerRight
;
2827 p
->setFont(headerFont
);
2828 headerHeight
= (p
->fontMetrics().lineSpacing() * 3) / 2;
2831 // ok. now print the pages.
2832 kDebug(6000) << "printing: html page width = " << root
->docWidth()
2833 << " height = " << root
->docHeight() << endl
;
2834 kDebug(6000) << "printing: margins left = " << printer
->margins().width()
2835 << " top = " << printer
->margins().height() << endl
;
2836 kDebug(6000) << "printing: paper width = " << metrics
.width()
2837 << " height = " << metrics
.height() << endl
;
2838 // if the width is too large to fit on the paper we just scale
2840 int pageWidth
= metrics
.width();
2841 int pageHeight
= metrics
.height();
2842 p
->setClipRect(0,0, pageWidth
, pageHeight
);
2844 pageHeight
-= headerHeight
;
2846 bool scalePage
= false;
2848 #ifndef QT_NO_TRANSFORMATIONS
2849 if(root
->docWidth() > metrics
.width()) {
2851 scale
= ((double) metrics
.width())/((double) root
->docWidth());
2852 pageHeight
= (int) (pageHeight
/scale
);
2853 pageWidth
= (int) (pageWidth
/scale
);
2854 headerHeight
= (int) (headerHeight
/scale
);
2857 kDebug(6000) << "printing: scaled html width = " << pageWidth
2858 << " height = " << pageHeight
<< endl
;
2860 root
->setHeight(pageHeight
);
2861 root
->setPageBottom(pageHeight
);
2862 root
->setNeedsLayout(true);
2863 root
->layoutIfNeeded();
2864 // m_part->slotDebugRenderTree();
2866 // Squeeze header to make it it on the page.
2869 int available_width
= metrics
.width() - 10 -
2870 2 * qMax(p
->boundingRect(0, 0, metrics
.width(), p
->fontMetrics().lineSpacing(), Qt::AlignLeft
, headerLeft
).width(),
2871 p
->boundingRect(0, 0, metrics
.width(), p
->fontMetrics().lineSpacing(), Qt::AlignLeft
, headerRight
).width());
2872 if (available_width
< 150)
2873 available_width
= 150;
2877 headerMid
= KStringHandler::csqueeze(docname
, squeeze
);
2878 mid_width
= p
->boundingRect(0, 0, metrics
.width(), p
->fontMetrics().lineSpacing(), Qt::AlignLeft
, headerMid
).width();
2880 } while (mid_width
> available_width
);
2886 while(top
< root
->docHeight()) {
2887 if(top
> 0) printer
->newPage();
2889 #warning "This could not be tested when merge was done, suspect"
2891 p
->setClipRect(0, 0, pageWidth
, headerHeight
);
2894 int dy
= p
->fontMetrics().lineSpacing();
2895 p
->setPen(Qt::black
);
2896 p
->setFont(headerFont
);
2898 headerRight
= QString("#%1").arg(page
);
2900 p
->drawText(0, 0, metrics
.width(), dy
, Qt::AlignLeft
, headerLeft
);
2901 p
->drawText(0, 0, metrics
.width(), dy
, Qt::AlignHCenter
, headerMid
);
2902 p
->drawText(0, 0, metrics
.width(), dy
, Qt::AlignRight
, headerRight
);
2906 #ifndef QT_NO_TRANSFORMATIONS
2908 p
->scale(scale
, scale
);
2912 #warning "This could not be tested when merge was done, suspect"
2914 p
->setClipRect(0, (int)(headerHeight
/scale
), (int)(pageWidth
/scale
), (int)(pageHeight
/scale
));
2915 p
->translate(0, headerHeight
-top
);
2917 bottom
= top
+pageHeight
;
2919 root
->setPageTop(top
);
2920 root
->setPageBottom(bottom
);
2921 root
->setPageNumber(page
);
2923 root
->layer()->paint(p
, QRect(0, top
, pageWidth
, pageHeight
));
2924 // m_part->xmlDocImpl()->renderer()->layer()->paint(p, QRect(0, top, pageWidth, pageHeight));
2927 kDebug(6000) << "printed: page " << page
<<" bottom At = " << bottom
<< endl
;
2937 // and now reset the layout to the usual one...
2938 root
->setPagedMode(false);
2939 root
->setStaticMode(false);
2941 khtml::setPrintPainter( 0 );
2942 setMediaType( oldMediaType
);
2943 m_part
->xmlDocImpl()->setPaintDevice( this );
2944 m_part
->xmlDocImpl()->styleSelector()->computeFontSizes(m_part
->xmlDocImpl()->paintDeviceMetrics(), m_part
->zoomFactor());
2945 m_part
->xmlDocImpl()->updateStyleSelector();
2946 viewport()->unsetCursor();
2951 void KHTMLView::slotPaletteChanged()
2953 if(!m_part
->xmlDocImpl()) return;
2954 DOM::DocumentImpl
*document
= m_part
->xmlDocImpl();
2955 if (!document
->isHTMLDocument()) return;
2956 khtml::RenderCanvas
*root
= static_cast<khtml::RenderCanvas
*>(document
->renderer());
2958 root
->style()->resetPalette();
2959 NodeImpl
*body
= static_cast<HTMLDocumentImpl
*>(document
)->body();
2961 body
->setChanged(true);
2962 body
->recalcStyle( NodeImpl::Force
);
2965 void KHTMLView::paint(QPainter
*p
, const QRect
&rc
, int yOff
, bool *more
)
2967 if(!m_part
->xmlDocImpl()) return;
2968 khtml::RenderCanvas
*root
= static_cast<khtml::RenderCanvas
*>(m_part
->xmlDocImpl()->renderer());
2971 m_part
->xmlDocImpl()->setPaintDevice(p
->device());
2972 root
->setPagedMode(true);
2973 root
->setStaticMode(true);
2974 root
->setWidth(rc
.width());
2978 p
->translate(rc
.left(), rc
.top());
2979 double scale
= ((double) rc
.width()/(double) root
->docWidth());
2980 int height
= (int) ((double) rc
.height() / scale
);
2981 #ifndef QT_NO_TRANSFORMATIONS
2982 p
->scale(scale
, scale
);
2984 root
->setPageTop(yOff
);
2985 root
->setPageBottom(yOff
+height
);
2987 root
->layer()->paint(p
, QRect(0, yOff
, root
->docWidth(), height
));
2989 *more
= yOff
+ height
< root
->docHeight();
2992 root
->setPagedMode(false);
2993 root
->setStaticMode(false);
2994 m_part
->xmlDocImpl()->setPaintDevice( this );
2998 void KHTMLView::useSlowRepaints()
3000 d
->useSlowRepaints
= true;
3001 // setStaticBackground(true); ### ?? FIXME
3005 void KHTMLView::setVerticalScrollBarPolicy( Qt::ScrollBarPolicy policy
)
3007 #ifndef KHTML_NO_SCROLLBARS
3008 d
->vpolicy
= policy
;
3009 QScrollArea::setVerticalScrollBarPolicy(policy
);
3015 void KHTMLView::setHorizontalScrollBarPolicy( Qt::ScrollBarPolicy policy
)
3017 #ifndef KHTML_NO_SCROLLBARS
3018 d
->hpolicy
= policy
;
3019 QScrollArea::setHorizontalScrollBarPolicy(policy
);
3025 void KHTMLView::restoreScrollBar()
3027 int ow
= visibleWidth();
3028 QScrollArea::setVerticalScrollBarPolicy(d
->vpolicy
);
3029 if (visibleWidth() != ow
)
3031 d
->prevScrollbarVisible
= verticalScrollBar()->isVisible();
3034 QStringList
KHTMLView::formCompletionItems(const QString
&name
) const
3036 if (!m_part
->settings()->isFormCompletionEnabled())
3037 return QStringList();
3038 if (!d
->formCompletions
)
3039 d
->formCompletions
= new KSimpleConfig(KStandardDirs::locateLocal("data", "khtml/formcompletions"));
3040 return d
->formCompletions
->readEntry(name
, QStringList());
3043 void KHTMLView::clearCompletionHistory(const QString
& name
)
3045 if (!d
->formCompletions
)
3047 d
->formCompletions
= new KSimpleConfig(KStandardDirs::locateLocal("data", "khtml/formcompletions"));
3049 d
->formCompletions
->writeEntry(name
, "");
3050 d
->formCompletions
->sync();
3053 void KHTMLView::addFormCompletionItem(const QString
&name
, const QString
&value
)
3055 if (!m_part
->settings()->isFormCompletionEnabled())
3057 // don't store values that are all numbers or just numbers with
3058 // dashes or spaces as those are likely credit card numbers or
3059 // something similar
3060 bool cc_number(true);
3061 for ( int i
= 0; i
< value
.length(); ++i
)
3064 if (!c
.isNumber() && c
!= '-' && !c
.isSpace())
3072 QStringList items
= formCompletionItems(name
);
3073 if (!items
.contains(value
))
3074 items
.prepend(value
);
3075 while ((int)items
.count() > m_part
->settings()->maxFormCompletionItems())
3076 items
.erase(items
.isEmpty() ? items
.end() : --items
.end());
3077 d
->formCompletions
->writeEntry(name
, items
);
3080 void KHTMLView::addNonPasswordStorableSite(const QString
& host
)
3082 if (!d
->formCompletions
) {
3083 d
->formCompletions
= new KSimpleConfig(KStandardDirs::locateLocal("data", "khtml/formcompletions"));
3086 d
->formCompletions
->setGroup("NonPasswordStorableSites");
3087 QStringList sites
= d
->formCompletions
->readEntry("Sites", QStringList());
3089 d
->formCompletions
->writeEntry("Sites", sites
);
3090 d
->formCompletions
->sync();
3091 d
->formCompletions
->setGroup(QString());//reset
3094 bool KHTMLView::nonPasswordStorableSite(const QString
& host
) const
3096 if (!d
->formCompletions
) {
3097 d
->formCompletions
= new KSimpleConfig(KStandardDirs::locateLocal("data", "khtml/formcompletions"));
3099 d
->formCompletions
->setGroup("NonPasswordStorableSites");
3100 QStringList sites
= d
->formCompletions
->readEntry("Sites", QStringList());
3101 d
->formCompletions
->setGroup(QString());//reset
3103 return (sites
.indexOf(host
) != -1);
3106 // returns true if event should be swallowed
3107 bool KHTMLView::dispatchMouseEvent(int eventId
, DOM::NodeImpl
*targetNode
,
3108 DOM::NodeImpl
*targetNodeNonShared
, bool cancelable
,
3109 int detail
,QMouseEvent
*_mouse
, bool setUnder
,
3110 int mouseEventType
, int orient
)
3112 // if the target node is a text node, dispatch on the parent node - rdar://4196646 (and #76948)
3113 if (targetNode
&& targetNode
->isTextNode())
3114 targetNode
= targetNode
->parentNode();
3117 d
->underMouse
->deref();
3118 d
->underMouse
= targetNode
;
3120 d
->underMouse
->ref();
3122 if (d
->underMouseNonShared
)
3123 d
->underMouseNonShared
->deref();
3124 d
->underMouseNonShared
= targetNodeNonShared
;
3125 if (d
->underMouseNonShared
)
3126 d
->underMouseNonShared
->ref();
3128 bool isWheelEvent
= (mouseEventType
== DOM::NodeImpl::MouseWheel
);
3130 int exceptioncode
= 0;
3131 int pageX
= _mouse
->x();
3132 int pageY
= _mouse
->y();
3133 int clientX
= pageX
- contentsX();
3134 int clientY
= pageY
- contentsY();
3135 int screenX
= _mouse
->globalX();
3136 int screenY
= _mouse
->globalY();
3138 switch (_mouse
->button()) {
3139 case Qt::LeftButton
:
3145 case Qt::RightButton
:
3151 if (d
->accessKeysEnabled
&& d
->accessKeysPreActivate
&& button
!=-1)
3152 d
->accessKeysPreActivate
=false;
3154 bool ctrlKey
= (_mouse
->modifiers() & Qt::ControlModifier
);
3155 bool altKey
= (_mouse
->modifiers() & Qt::AltModifier
);
3156 bool shiftKey
= (_mouse
->modifiers() & Qt::ShiftModifier
);
3157 bool metaKey
= (_mouse
->modifiers() & Qt::MetaModifier
);
3159 // mouseout/mouseover
3160 if (setUnder
&& (d
->prevMouseX
!= pageX
|| d
->prevMouseY
!= pageY
)) {
3162 // ### this code sucks. we should save the oldUnder instead of calculating
3163 // it again. calculating is expensive! (Dirk)
3165 NodeImpl
*oldUnder
= 0;
3166 if (d
->prevMouseX
>= 0 && d
->prevMouseY
>= 0) {
3167 NodeImpl::MouseEvent
mev( _mouse
->buttons(), static_cast<NodeImpl::MouseEventType
>(mouseEventType
));
3168 m_part
->xmlDocImpl()->prepareMouseEvent( true, d
->prevMouseX
, d
->prevMouseY
, &mev
);
3169 oldUnder
= mev
.innerNode
.handle();
3171 if (oldUnder
&& oldUnder
->isTextNode())
3172 oldUnder
= oldUnder
->parentNode();
3174 // qDebug("oldunder=%p (%s), target=%p (%s) x/y=%d/%d", oldUnder, oldUnder ? oldUnder->renderer()->renderName() : 0, targetNode, targetNode ? targetNode->renderer()->renderName() : 0, _mouse->x(), _mouse->y());
3175 if (oldUnder
!= targetNode
) {
3176 // send mouseout event to the old node
3179 MouseEventImpl
*me
= new MouseEventImpl(EventImpl::MOUSEOUT_EVENT
,
3180 true,true,m_part
->xmlDocImpl()->defaultView(),
3181 0,screenX
,screenY
,clientX
,clientY
,pageX
, pageY
,
3182 ctrlKey
,altKey
,shiftKey
,metaKey
,
3185 oldUnder
->dispatchEvent(me
,exceptioncode
,true);
3189 // send mouseover event to the new node
3191 MouseEventImpl
*me
= new MouseEventImpl(EventImpl::MOUSEOVER_EVENT
,
3192 true,true,m_part
->xmlDocImpl()->defaultView(),
3193 0,screenX
,screenY
,clientX
,clientY
,pageX
, pageY
,
3194 ctrlKey
,altKey
,shiftKey
,metaKey
,
3198 targetNode
->dispatchEvent(me
,exceptioncode
,true);
3207 bool swallowEvent
= false;
3210 // send the actual event
3211 bool dblclick
= ( eventId
== EventImpl::CLICK_EVENT
&&
3212 _mouse
->type() == QEvent::MouseButtonDblClick
);
3213 MouseEventImpl
*me
= new MouseEventImpl(static_cast<EventImpl::EventId
>(eventId
),
3214 true,cancelable
,m_part
->xmlDocImpl()->defaultView(),
3215 detail
,screenX
,screenY
,clientX
,clientY
,pageX
, pageY
,
3216 ctrlKey
,altKey
,shiftKey
,metaKey
,
3217 button
,0, isWheelEvent
? 0 : _mouse
, dblclick
,
3218 isWheelEvent
? static_cast<MouseEventImpl::Orientation
>(orient
) : MouseEventImpl::ONone
);
3220 if ( !d
->m_mouseEventsTarget
&& RenderLayer::gScrollBar
&& eventId
== EventImpl::MOUSEDOWN_EVENT
)
3221 // button is pressed inside a layer scrollbar, so make it the target for future mousemove events until released
3222 d
->m_mouseEventsTarget
= RenderLayer::gScrollBar
;
3223 if ( d
->m_mouseEventsTarget
&& qobject_cast
<QScrollBar
*>(d
->m_mouseEventsTarget
) &&
3224 dynamic_cast<KHTMLWidget
*>(static_cast<QWidget
*>(d
->m_mouseEventsTarget
)) ) {
3225 // we have a sticky mouse event target and it is a layer's scrollbar. Forward events manually.
3226 // ### should use the dom
3227 KHTMLWidget
*w
= dynamic_cast<KHTMLWidget
*>(static_cast<QWidget
*>(d
->m_mouseEventsTarget
));
3228 QPoint p
= w
->m_kwp
->absolutePos();
3229 QMouseEvent
fw(_mouse
->type(), _mouse
->pos()-p
, _mouse
->button(), _mouse
->buttons(), _mouse
->modifiers());
3230 static_cast<RenderWidget::EventPropagator
*>(static_cast<QWidget
*>(d
->m_mouseEventsTarget
))->sendEvent(&fw
);
3231 if (_mouse
->type() == QMouseEvent::MouseButtonPress
&& _mouse
->button() == Qt::RightButton
) {
3232 QContextMenuEvent
cme(QContextMenuEvent::Mouse
, p
);
3233 static_cast<RenderWidget::EventPropagator
*>(static_cast<QWidget
*>(d
->m_mouseEventsTarget
))->sendEvent(&cme
);
3235 swallowEvent
= true;
3237 targetNode
->dispatchEvent(me
,exceptioncode
,true);
3238 bool defaultHandled
= me
->defaultHandled();
3239 if (defaultHandled
|| me
->defaultPrevented())
3240 swallowEvent
= true;
3244 if (eventId
== EventImpl::MOUSEDOWN_EVENT
) {
3245 // Focus should be shifted on mouse down, not on a click. -dwh
3246 // Blur current focus node when a link/button is clicked; this
3247 // is expected by some sites that rely on onChange handlers running
3248 // from form fields before the button click is processed.
3249 DOM::NodeImpl
* nodeImpl
= targetNode
;
3250 for ( ; nodeImpl
&& !nodeImpl
->isFocusable(); nodeImpl
= nodeImpl
->parentNode());
3251 if (nodeImpl
&& nodeImpl
->isMouseFocusable())
3252 m_part
->xmlDocImpl()->setFocusNode(nodeImpl
);
3253 else if (!nodeImpl
|| !nodeImpl
->focused())
3254 m_part
->xmlDocImpl()->setFocusNode(0);
3258 return swallowEvent
;
3261 void KHTMLView::setIgnoreWheelEvents( bool e
)
3263 d
->ignoreWheelEvents
= e
;
3266 #ifndef QT_NO_WHEELEVENT
3268 void KHTMLView::wheelEvent(QWheelEvent
* e
)
3270 if (d
->accessKeysEnabled
&& d
->accessKeysPreActivate
) d
->accessKeysPreActivate
=false;
3272 if ( ( e
->modifiers() & Qt::ControlModifier
) == Qt::ControlModifier
)
3274 emit
zoomView( - e
->delta() );
3277 else if (d
->firstRelayout
)
3281 else if( ( (e
->orientation() == Qt::Vertical
&&
3282 ((d
->ignoreWheelEvents
&& !verticalScrollBar()->isVisible())
3283 || e
->delta() > 0 && contentsY() <= 0
3284 || e
->delta() < 0 && contentsY() >= contentsHeight() - visibleHeight()))
3286 (e
->orientation() == Qt::Horizontal
&&
3287 ((d
->ignoreWheelEvents
&& !horizontalScrollBar()->isVisible())
3288 || e
->delta() > 0 && contentsX() <=0
3289 || e
->delta() < 0 && contentsX() >= contentsWidth() - visibleWidth())))
3290 && m_part
->parentPart())
3292 if ( m_part
->parentPart()->view() )
3293 m_part
->parentPart()->view()->wheelEvent( e
);
3298 DOM::NodeImpl::MouseEvent
mev( e
->buttons(), DOM::NodeImpl::MouseWheel
);
3299 m_part
->xmlDocImpl()->prepareMouseEvent( false, e
->x(), e
->y(), &mev
);
3301 MouseEventImpl::Orientation o
= MouseEventImpl::OVertical
;
3302 if (e
->orientation() == Qt::Horizontal
)
3303 o
= MouseEventImpl::OHorizontal
;
3305 QMouseEvent
_mouse(QEvent::MouseMove
, e
->pos(), Qt::NoButton
, e
->buttons(), e
->modifiers());
3306 bool swallow
= dispatchMouseEvent(EventImpl::KHTML_MOUSEWHEEL_EVENT
,mev
.innerNode
.handle(),mev
.innerNonSharedNode
.handle(),
3307 true,-e
->delta()/40,&_mouse
,true,DOM::NodeImpl::MouseWheel
,o
);
3311 d
->scrollBarMoved
= true;
3312 QScrollArea::wheelEvent( e
);
3318 void KHTMLView::dragEnterEvent( QDragEnterEvent
* ev
)
3320 // Handle drops onto frames (#16820)
3321 // Drops on the main html part is handled by Konqueror (and shouldn't do anything
3322 // in e.g. kmail, so not handled here).
3323 if ( m_part
->parentPart() )
3325 QApplication::sendEvent(m_part
->parentPart()->widget(), ev
);
3328 QScrollArea::dragEnterEvent( ev
);
3331 void KHTMLView::dropEvent( QDropEvent
*ev
)
3333 // Handle drops onto frames (#16820)
3334 // Drops on the main html part is handled by Konqueror (and shouldn't do anything
3335 // in e.g. kmail, so not handled here).
3336 if ( m_part
->parentPart() )
3338 QApplication::sendEvent(m_part
->parentPart()->widget(), ev
);
3341 QScrollArea::dropEvent( ev
);
3344 void KHTMLView::focusInEvent( QFocusEvent
*e
)
3346 #ifndef KHTML_NO_TYPE_AHEAD_FIND
3347 m_part
->enableFindAheadActions( true );
3349 DOM::NodeImpl
* fn
= m_part
->xmlDocImpl() ? m_part
->xmlDocImpl()->focusNode() : 0;
3350 if (fn
&& fn
->renderer() && fn
->renderer()->isWidget() &&
3351 (e
->reason() != Qt::MouseFocusReason
) &&
3352 static_cast<khtml::RenderWidget
*>(fn
->renderer())->widget())
3353 static_cast<khtml::RenderWidget
*>(fn
->renderer())->widget()->setFocus();
3354 #ifndef KHTML_NO_CARET
3355 // Restart blink frequency timer if it has been killed, but only on
3357 if (d
->m_caretViewContext
&&
3358 d
->m_caretViewContext
->freqTimerId
== -1 &&
3360 if (m_part
->isCaretMode()
3361 || m_part
->isEditable()
3362 || (fn
&& fn
->renderer()
3363 && fn
->renderer()->style()->userInput()
3365 d
->m_caretViewContext
->freqTimerId
= startTimer(500);
3366 d
->m_caretViewContext
->visible
= true;
3370 #endif // KHTML_NO_CARET
3371 QScrollArea::focusInEvent( e
);
3374 void KHTMLView::focusOutEvent( QFocusEvent
*e
)
3376 m_part
->stopAutoScroll();
3378 #ifndef KHTML_NO_TYPE_AHEAD_FIND
3379 if(d
->typeAheadActivated
)
3383 m_part
->enableFindAheadActions( false );
3384 #endif // KHTML_NO_TYPE_AHEAD_FIND
3386 #ifndef KHTML_NO_CARET
3387 if (d
->m_caretViewContext
) {
3388 switch (d
->m_caretViewContext
->displayNonFocused
) {
3389 case KHTMLPart::CaretInvisible
:
3392 case KHTMLPart::CaretVisible
: {
3393 if (d
->m_caretViewContext
->freqTimerId
!= -1)
3394 killTimer(d
->m_caretViewContext
->freqTimerId
);
3395 d
->m_caretViewContext
->freqTimerId
= -1;
3396 NodeImpl
*caretNode
= m_part
->xmlDocImpl()->focusNode();
3397 if (!d
->m_caretViewContext
->visible
&& (m_part
->isCaretMode()
3398 || m_part
->isEditable()
3399 || (caretNode
&& caretNode
->renderer()
3400 && caretNode
->renderer()->style()->userInput()
3402 d
->m_caretViewContext
->visible
= true;
3407 case KHTMLPart::CaretBlink
:
3408 // simply leave as is
3412 #endif // KHTML_NO_CARET
3414 if ( d
->cursor_icon_widget
)
3415 d
->cursor_icon_widget
->hide();
3417 QScrollArea::focusOutEvent( e
);
3420 void KHTMLView::scrollContentsBy( int dx
, int dy
)
3422 if ( !d
->firstRelayout
&& !d
->complete
&& m_part
->xmlDocImpl() &&
3423 d
->layoutSchedulingEnabled
) {
3424 // contents scroll while we are not complete: we need to check our layout *now*
3425 khtml::RenderCanvas
* root
= static_cast<khtml::RenderCanvas
*>( m_part
->xmlDocImpl()->renderer() );
3426 if (root
&& root
->needsLayout()) {
3427 unscheduleRelayout();
3431 if (!d
->scrollingSelf
) {
3432 d
->scrollBarMoved
= true;
3433 d
->contentsMoving
= true;
3434 // ensure quick reset of contentsMoving flag
3435 scheduleRepaint(0, 0, 0, 0);
3438 if (m_part
->xmlDocImpl() && m_part
->xmlDocImpl()->documentElement())
3439 m_part
->xmlDocImpl()->documentElement()->dispatchHTMLEvent(EventImpl::SCROLL_EVENT
, true, false);
3441 d
->contentsX
= QApplication::isRightToLeft() ?
3442 horizontalScrollBar()->maximum()-horizontalScrollBar()->value() : horizontalScrollBar()->value();
3443 d
->contentsY
= verticalScrollBar()->value();
3445 if (d
->useSlowRepaints
) {
3446 widget()->blockSignals( true );
3447 widget()->move( widget()->pos().x() + dx
, widget()->pos().y() +dy
);
3448 widget()->blockSignals( false );
3449 widget()->repaint();
3452 QScrollArea::scrollContentsBy(dx
, dy
);
3455 void KHTMLView::timerEvent ( QTimerEvent
*e
)
3457 // kDebug() << "timer event " << e->timerId() << endl;
3458 if ( e
->timerId() == d
->scrollTimerId
) {
3459 if( d
->scrollSuspended
)
3461 switch (d
->scrollDirection
) {
3462 case KHTMLViewPrivate::ScrollDown
:
3463 if (contentsY() + visibleHeight () >= contentsHeight())
3464 d
->newScrollTimer(this, 0);
3466 verticalScrollBar()->setValue( verticalScrollBar()->value() +d
->scrollBy
);
3468 case KHTMLViewPrivate::ScrollUp
:
3469 if (contentsY() <= 0)
3470 d
->newScrollTimer(this, 0);
3472 verticalScrollBar()->setValue( verticalScrollBar()->value() -d
->scrollBy
);
3474 case KHTMLViewPrivate::ScrollRight
:
3475 if (contentsX() + visibleWidth () >= contentsWidth())
3476 d
->newScrollTimer(this, 0);
3478 horizontalScrollBar()->setValue( horizontalScrollBar()->value() +d
->scrollBy
);
3480 case KHTMLViewPrivate::ScrollLeft
:
3481 if (contentsX() <= 0)
3482 d
->newScrollTimer(this, 0);
3484 horizontalScrollBar()->setValue( horizontalScrollBar()->value() -d
->scrollBy
);
3489 else if ( e
->timerId() == d
->layoutTimerId
) {
3490 d
->dirtyLayout
= true;
3492 if (d
->firstRelayout
) {
3493 d
->firstRelayout
= false;
3494 verticalScrollBar()->setEnabled( true );
3495 horizontalScrollBar()->setEnabled( true );
3498 #ifndef KHTML_NO_CARET
3499 else if (d
->m_caretViewContext
3500 && e
->timerId() == d
->m_caretViewContext
->freqTimerId
) {
3501 d
->m_caretViewContext
->visible
= !d
->m_caretViewContext
->visible
;
3502 if (d
->m_caretViewContext
->displayed
) {
3503 updateContents(d
->m_caretViewContext
->x
, d
->m_caretViewContext
->y
,
3504 d
->m_caretViewContext
->width
,
3505 d
->m_caretViewContext
->height
);
3507 // if (d->m_caretViewContext->visible) cout << "|" << flush;
3508 // else cout << "" << flush;
3513 d
->contentsMoving
= false;
3514 if( m_part
->xmlDocImpl() ) {
3515 DOM::DocumentImpl
*document
= m_part
->xmlDocImpl();
3516 khtml::RenderCanvas
* root
= static_cast<khtml::RenderCanvas
*>(document
->renderer());
3518 if ( root
&& root
->needsLayout() ) {
3519 if (d
->repaintTimerId
)
3520 killTimer(d
->repaintTimerId
);
3521 d
->repaintTimerId
= 0;
3527 // setStaticBackground(d->useSlowRepaints); ?? ### FIXME
3529 // kDebug() << "scheduled repaint "<< d->repaintTimerId << endl;
3530 if (d
->repaintTimerId
)
3531 killTimer(d
->repaintTimerId
);
3532 d
->repaintTimerId
= 0;
3535 QVector
<QRect
> rects
= d
->updateRegion
.rects();
3537 d
->updateRegion
= QRegion();
3540 updateRegion
= rects
[0];
3542 for ( int i
= 1; i
< rects
.size(); ++i
) {
3543 QRect newRegion
= updateRegion
.unite(rects
[i
]);
3544 if (2*newRegion
.height() > 3*updateRegion
.height() )
3546 repaintContents( updateRegion
);
3547 updateRegion
= rects
[i
];
3550 updateRegion
= newRegion
;
3553 if ( !updateRegion
.isNull() )
3554 repaintContents( updateRegion
);
3556 // As widgets can only be accurately positioned during painting, every layout might
3557 // dissociate a widget from its RenderWidget. E.g: if a RenderWidget was visible before layout, but the layout
3558 // pushed it out of the viewport, it will not be repainted, and consequently it's associated widget won't be repositioned.
3559 // 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.
3561 if (d
->dirtyLayout
&& !d
->visibleWidgets
.isEmpty()) {
3563 d
->dirtyLayout
= false;
3565 QRect
visibleRect(contentsX(), contentsY(), visibleWidth(), visibleHeight());
3566 QList
<RenderWidget
*> toRemove
;
3567 for (Q3PtrDictIterator
<QWidget
> it(d
->visibleWidgets
); it
.current(); ++it
) {
3570 RenderWidget
* rw
= static_cast<RenderWidget
*>( it
.currentKey() );
3571 if (!rw
->absolutePosition(xp
, yp
) ||
3572 !visibleRect
.intersects(QRect(xp
, yp
, w
->width(), w
->height())))
3573 toRemove
.append(rw
);
3576 foreach (RenderWidget
* r
, toRemove
)
3577 if ( (w
= d
->visibleWidgets
.take(r
) ) )
3578 w
->move( 0, -500000);
3581 emit
repaintAccessKeys();
3582 if (d
->emitCompletedAfterRepaint
) {
3583 bool full
= d
->emitCompletedAfterRepaint
== KHTMLViewPrivate::CSFull
;
3584 d
->emitCompletedAfterRepaint
= KHTMLViewPrivate::CSNone
;
3586 emit m_part
->completed();
3588 emit m_part
->completed(true);
3592 void KHTMLView::scheduleRelayout(khtml::RenderObject
* /*clippedObj*/)
3594 if (!d
->layoutSchedulingEnabled
|| d
->layoutTimerId
)
3597 d
->layoutTimerId
= startTimer( m_part
->xmlDocImpl() && m_part
->xmlDocImpl()->parsing()
3601 void KHTMLView::unscheduleRelayout()
3603 if (!d
->layoutTimerId
)
3606 killTimer(d
->layoutTimerId
);
3607 d
->layoutTimerId
= 0;
3610 void KHTMLView::unscheduleRepaint()
3612 if (!d
->repaintTimerId
)
3615 killTimer(d
->repaintTimerId
);
3616 d
->repaintTimerId
= 0;
3619 void KHTMLView::scheduleRepaint(int x
, int y
, int w
, int h
, bool asap
)
3621 bool parsing
= !m_part
->xmlDocImpl() || m_part
->xmlDocImpl()->parsing();
3623 // kDebug() << "parsing " << parsing << endl;
3624 // kDebug() << "complete " << d->complete << endl;
3626 int time
= parsing
? 300 : (!asap
? ( !d
->complete
? 100 : 20 ) : 0);
3628 #ifdef DEBUG_FLICKER
3630 p
.begin( viewport() );
3633 contentsToViewport( x
, y
, vx
, vy
);
3634 p
.fillRect( vx
, vy
, w
, h
, Qt::red
);
3638 d
->updateRegion
= d
->updateRegion
.unite(QRect(x
,y
,w
,h
));
3640 if (asap
&& !parsing
)
3641 unscheduleRepaint();
3643 if ( !d
->repaintTimerId
)
3644 d
->repaintTimerId
= startTimer( time
);
3646 // kDebug() << "starting timer " << time << endl;
3649 void KHTMLView::complete( bool pendingAction
)
3651 // kDebug() << "KHTMLView::complete()" << endl;
3655 // is there a relayout pending?
3656 if (d
->layoutTimerId
)
3658 // kDebug() << "requesting relayout now" << endl;
3660 killTimer(d
->layoutTimerId
);
3661 d
->layoutTimerId
= startTimer( 0 );
3662 d
->emitCompletedAfterRepaint
= pendingAction
?
3663 KHTMLViewPrivate::CSActionPending
: KHTMLViewPrivate::CSFull
;
3666 // is there a repaint pending?
3667 if (d
->repaintTimerId
)
3669 // kDebug() << "requesting repaint now" << endl;
3671 killTimer(d
->repaintTimerId
);
3672 d
->repaintTimerId
= startTimer( 20 );
3673 d
->emitCompletedAfterRepaint
= pendingAction
?
3674 KHTMLViewPrivate::CSActionPending
: KHTMLViewPrivate::CSFull
;
3677 if (!d
->emitCompletedAfterRepaint
)
3680 emit m_part
->completed();
3682 emit m_part
->completed(true);
3687 void KHTMLView::slotMouseScrollTimer()
3689 horizontalScrollBar()->setValue( horizontalScrollBar()->value() +d
->m_mouseScroll_byX
);
3690 verticalScrollBar()->setValue( verticalScrollBar()->value() +d
->m_mouseScroll_byY
);
3693 #ifndef KHTML_NO_CARET
3695 // ### the dependencies on static functions are a nightmare. just be
3696 // hacky and include the implementation here. Clean me up, please.
3698 #include "khtml_caret.cpp"
3700 void KHTMLView::initCaret(bool keepSelection
)
3702 #if DEBUG_CARETMODE > 0
3703 kDebug(6200) << "begin initCaret" << endl
;
3705 // save caretMoved state as moveCaretTo changes it
3706 if (m_part
->xmlDocImpl()) {
3708 ElementImpl
*listitem
= m_part
->xmlDocImpl()->getElementById("__test_element__");
3709 if (listitem
) dumpLineBoxes(static_cast<RenderFlow
*>(listitem
->renderer()));
3711 d
->caretViewContext();
3712 bool cmoved
= d
->m_caretViewContext
->caretMoved
;
3713 if (m_part
->d
->caretNode().isNull()) {
3714 // set to document, position will be sanitized anyway
3715 m_part
->d
->caretNode() = m_part
->document();
3716 m_part
->d
->caretOffset() = 0L;
3717 // This sanity check is necessary for the not so unlikely case that
3718 // setEditable or setCaretMode is called before any render objects have
3720 if (!m_part
->d
->caretNode().handle()->renderer()) return;
3722 // kDebug(6200) << "d->m_selectionStart " << m_part->d->m_selectionStart.handle()
3723 // << " d->m_selectionEnd " << m_part->d->m_selectionEnd.handle() << endl;
3724 // ### does not repaint the selection on keepSelection!=false
3725 moveCaretTo(m_part
->d
->caretNode().handle(), m_part
->d
->caretOffset(), !keepSelection
);
3726 // kDebug(6200) << "d->m_selectionStart " << m_part->d->m_selectionStart.handle()
3727 // << " d->m_selectionEnd " << m_part->d->m_selectionEnd.handle() << endl;
3728 d
->m_caretViewContext
->caretMoved
= cmoved
;
3730 #if DEBUG_CARETMODE > 0
3731 kDebug(6200) << "end initCaret" << endl
;
3735 bool KHTMLView::caretOverrides() const
3737 bool cm
= m_part
->isCaretMode();
3738 bool dm
= m_part
->isEditable();
3739 return cm
&& !dm
? false
3740 : (dm
|| m_part
->d
->caretNode().handle()->contentEditable())
3741 && d
->editorContext()->override
;
3744 void KHTMLView::ensureNodeHasFocus(NodeImpl
*node
)
3746 if (m_part
->isCaretMode() || m_part
->isEditable()) return;
3747 if (node
->focused()) return;
3749 // Find first ancestor whose "user-input" is "enabled"
3750 NodeImpl
*firstAncestor
= 0;
3752 if (node
->renderer()
3753 && node
->renderer()->style()->userInput() != UI_ENABLED
)
3755 firstAncestor
= node
;
3756 node
= node
->parentNode();
3759 if (!node
) firstAncestor
= 0;
3761 DocumentImpl
*doc
= m_part
->xmlDocImpl();
3762 // ensure that embedded widgets don't lose their focus
3763 if (!firstAncestor
&& doc
->focusNode() && doc
->focusNode()->renderer()
3764 && doc
->focusNode()->renderer()->isWidget())
3767 // Set focus node on the document
3768 #if DEBUG_CARETMODE > 1
3769 kDebug(6200) << k_funcinfo
<< "firstAncestor " << firstAncestor
<< ": "
3770 << (firstAncestor
? firstAncestor
->nodeName().string() : QString()) << endl
;
3772 doc
->setFocusNode(firstAncestor
);
3773 emit m_part
->nodeActivated(Node(firstAncestor
));
3776 void KHTMLView::recalcAndStoreCaretPos(CaretBox
*hintBox
)
3778 if (!m_part
|| m_part
->d
->caretNode().isNull()) return;
3779 d
->caretViewContext();
3780 NodeImpl
*caretNode
= m_part
->d
->caretNode().handle();
3781 #if DEBUG_CARETMODE > 0
3782 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
;
3784 caretNode
->getCaret(m_part
->d
->caretOffset(), caretOverrides(),
3785 d
->m_caretViewContext
->x
, d
->m_caretViewContext
->y
,
3786 d
->m_caretViewContext
->width
,
3787 d
->m_caretViewContext
->height
);
3789 if (hintBox
&& d
->m_caretViewContext
->x
== -1) {
3790 #if DEBUG_CARETMODE > 1
3791 kDebug(6200) << "using hint inline box coordinates" << endl
;
3793 RenderObject
*r
= caretNode
->renderer();
3794 const QFontMetrics
&fm
= r
->style()->fontMetrics();
3796 r
->containingBlock()->absolutePosition(absx
, absy
,
3797 false); // ### what about fixed?
3798 d
->m_caretViewContext
->x
= absx
+ hintBox
->xPos();
3799 d
->m_caretViewContext
->y
= absy
+ hintBox
->yPos();
3800 // + hintBox->baseline() - fm.ascent();
3801 d
->m_caretViewContext
->width
= 1;
3802 // ### firstline not regarded. But I think it can be safely neglected
3803 // as hint boxes are only used for empty lines.
3804 d
->m_caretViewContext
->height
= fm
.height();
3807 #if DEBUG_CARETMODE > 4
3808 // kDebug(6200) << "freqTimerId: "<<d->m_caretViewContext->freqTimerId<<endl;
3810 #if DEBUG_CARETMODE > 0
3811 kDebug(6200) << "caret: ofs="<<m_part
->d
->caretOffset()<<" "
3812 <<" x="<<d
->m_caretViewContext
->x
<<" y="<<d
->m_caretViewContext
->y
3813 <<" h="<<d
->m_caretViewContext
->height
<<endl
;
3817 void KHTMLView::caretOn()
3819 if (d
->m_caretViewContext
) {
3820 if (d
->m_caretViewContext
->freqTimerId
!= -1)
3821 killTimer(d
->m_caretViewContext
->freqTimerId
);
3823 if (hasFocus() || d
->m_caretViewContext
->displayNonFocused
3824 == KHTMLPart::CaretBlink
) {
3825 d
->m_caretViewContext
->freqTimerId
= startTimer(500);
3827 d
->m_caretViewContext
->freqTimerId
= -1;
3830 d
->m_caretViewContext
->visible
= true;
3831 if ((d
->m_caretViewContext
->displayed
= (hasFocus()
3832 || d
->m_caretViewContext
->displayNonFocused
3833 != KHTMLPart::CaretInvisible
))) {
3834 updateContents(d
->m_caretViewContext
->x
, d
->m_caretViewContext
->y
,
3835 d
->m_caretViewContext
->width
,
3836 d
->m_caretViewContext
->height
);
3838 // kDebug(6200) << "caret on" << endl;
3842 void KHTMLView::caretOff()
3844 if (d
->m_caretViewContext
) {
3845 if (d
->m_caretViewContext
->freqTimerId
!= -1)
3846 killTimer(d
->m_caretViewContext
->freqTimerId
);
3847 d
->m_caretViewContext
->freqTimerId
= -1;
3848 d
->m_caretViewContext
->displayed
= false;
3849 if (d
->m_caretViewContext
->visible
) {
3850 d
->m_caretViewContext
->visible
= false;
3851 updateContents(d
->m_caretViewContext
->x
, d
->m_caretViewContext
->y
,
3852 d
->m_caretViewContext
->width
,
3853 d
->m_caretViewContext
->height
);
3855 // kDebug(6200) << "caret off" << endl;
3859 void KHTMLView::showCaret(bool forceRepaint
)
3861 if (d
->m_caretViewContext
) {
3862 d
->m_caretViewContext
->displayed
= true;
3863 if (d
->m_caretViewContext
->visible
) {
3864 if (!forceRepaint
) {
3865 updateContents(d
->m_caretViewContext
->x
, d
->m_caretViewContext
->y
,
3866 d
->m_caretViewContext
->width
,
3867 d
->m_caretViewContext
->height
);
3869 repaintContents(d
->m_caretViewContext
->x
, d
->m_caretViewContext
->y
,
3870 d
->m_caretViewContext
->width
,
3871 d
->m_caretViewContext
->height
);
3874 // kDebug(6200) << "caret shown" << endl;
3878 bool KHTMLView::foldSelectionToCaret(NodeImpl
*startNode
, long startOffset
,
3879 NodeImpl
*endNode
, long endOffset
)
3881 m_part
->d
->m_selectionStart
= m_part
->d
->m_selectionEnd
= m_part
->d
->caretNode();
3882 m_part
->d
->m_startOffset
= m_part
->d
->m_endOffset
= m_part
->d
->caretOffset();
3883 m_part
->d
->m_extendAtEnd
= true;
3885 bool folded
= startNode
!= endNode
|| startOffset
!= endOffset
;
3887 // Only clear the selection if there has been one.
3889 m_part
->xmlDocImpl()->clearSelection();
3895 void KHTMLView::hideCaret()
3897 if (d
->m_caretViewContext
) {
3898 if (d
->m_caretViewContext
->visible
) {
3899 // kDebug(6200) << "redraw caret hidden" << endl;
3900 d
->m_caretViewContext
->visible
= false;
3901 // force repaint, otherwise the event won't be handled
3902 // before the focus leaves the window
3903 repaintContents(d
->m_caretViewContext
->x
, d
->m_caretViewContext
->y
,
3904 d
->m_caretViewContext
->width
,
3905 d
->m_caretViewContext
->height
);
3906 d
->m_caretViewContext
->visible
= true;
3908 d
->m_caretViewContext
->displayed
= false;
3909 // kDebug(6200) << "caret hidden" << endl;
3913 int KHTMLView::caretDisplayPolicyNonFocused() const
3915 if (d
->m_caretViewContext
)
3916 return d
->m_caretViewContext
->displayNonFocused
;
3918 return KHTMLPart::CaretInvisible
;
3921 void KHTMLView::setCaretDisplayPolicyNonFocused(int policy
)
3923 d
->caretViewContext();
3924 // int old = d->m_caretViewContext->displayNonFocused;
3925 d
->m_caretViewContext
->displayNonFocused
= (KHTMLPart::CaretDisplayPolicy
)policy
;
3927 // make change immediately take effect if not focused
3929 switch (d
->m_caretViewContext
->displayNonFocused
) {
3930 case KHTMLPart::CaretInvisible
:
3933 case KHTMLPart::CaretBlink
:
3934 if (d
->m_caretViewContext
->freqTimerId
!= -1) break;
3935 d
->m_caretViewContext
->freqTimerId
= startTimer(500);
3937 case KHTMLPart::CaretVisible
:
3938 d
->m_caretViewContext
->displayed
= true;
3945 bool KHTMLView::placeCaret(CaretBox
*hintBox
)
3947 CaretViewContext
*cv
= d
->caretViewContext();
3949 NodeImpl
*caretNode
= m_part
->d
->caretNode().handle();
3950 // ### why is it sometimes null?
3951 if (!caretNode
|| !caretNode
->renderer()) return false;
3952 ensureNodeHasFocus(caretNode
);
3953 if (m_part
->isCaretMode() || m_part
->isEditable()
3954 || caretNode
->renderer()->style()->userInput() == UI_ENABLED
) {
3955 recalcAndStoreCaretPos(hintBox
);
3965 void KHTMLView::ensureCaretVisible()
3967 CaretViewContext
*cv
= d
->m_caretViewContext
;
3969 ensureVisible(cv
->x
, cv
->y
, cv
->width
, cv
->height
);
3970 d
->scrollBarMoved
= false;
3973 bool KHTMLView::extendSelection(NodeImpl
*oldStartSel
, long oldStartOfs
,
3974 NodeImpl
*oldEndSel
, long oldEndOfs
)
3976 bool changed
= false;
3977 if (m_part
->d
->m_selectionStart
== m_part
->d
->m_selectionEnd
3978 && m_part
->d
->m_startOffset
== m_part
->d
->m_endOffset
) {
3979 changed
= foldSelectionToCaret(oldStartSel
, oldStartOfs
, oldEndSel
, oldEndOfs
);
3980 m_part
->d
->m_extendAtEnd
= true;
3982 changed
= m_part
->d
->m_selectionStart
.handle() != oldStartSel
3983 || m_part
->d
->m_startOffset
!= oldStartOfs
3984 || m_part
->d
->m_selectionEnd
.handle() != oldEndSel
3985 || m_part
->d
->m_endOffset
!= oldEndOfs
;
3986 if (!changed
) break;
3988 // determine start position -- caret position is always at end.
3989 NodeImpl
*startNode
;
3991 if (m_part
->d
->m_extendAtEnd
) {
3992 startNode
= m_part
->d
->m_selectionStart
.handle();
3993 startOffset
= m_part
->d
->m_startOffset
;
3995 startNode
= m_part
->d
->m_selectionEnd
.handle();
3996 startOffset
= m_part
->d
->m_endOffset
;
3997 m_part
->d
->m_selectionEnd
= m_part
->d
->m_selectionStart
;
3998 m_part
->d
->m_endOffset
= m_part
->d
->m_startOffset
;
3999 m_part
->d
->m_extendAtEnd
= true;
4002 bool swapNeeded
= false;
4003 if (!m_part
->d
->m_selectionEnd
.isNull() && startNode
) {
4004 swapNeeded
= RangeImpl::compareBoundaryPoints(startNode
, startOffset
,
4005 m_part
->d
->m_selectionEnd
.handle(),
4006 m_part
->d
->m_endOffset
) >= 0;
4009 m_part
->d
->m_selectionStart
= startNode
;
4010 m_part
->d
->m_startOffset
= startOffset
;
4013 m_part
->xmlDocImpl()->setSelection(m_part
->d
->m_selectionEnd
.handle(),
4014 m_part
->d
->m_endOffset
, m_part
->d
->m_selectionStart
.handle(),
4015 m_part
->d
->m_startOffset
);
4017 m_part
->xmlDocImpl()->setSelection(m_part
->d
->m_selectionStart
.handle(),
4018 m_part
->d
->m_startOffset
, m_part
->d
->m_selectionEnd
.handle(),
4019 m_part
->d
->m_endOffset
);
4021 } while(false);/*end if*/
4025 void KHTMLView::updateSelection(NodeImpl
*oldStartSel
, long oldStartOfs
,
4026 NodeImpl
*oldEndSel
, long oldEndOfs
)
4028 if (m_part
->d
->m_selectionStart
== m_part
->d
->m_selectionEnd
4029 && m_part
->d
->m_startOffset
== m_part
->d
->m_endOffset
) {
4030 if (foldSelectionToCaret(oldStartSel
, oldStartOfs
, oldEndSel
, oldEndOfs
)) {
4031 m_part
->emitSelectionChanged();
4033 m_part
->d
->m_extendAtEnd
= true;
4035 // check if the extending end has passed the immobile end
4036 if (!m_part
->d
->m_selectionEnd
.isNull() && !m_part
->d
->m_selectionEnd
.isNull()) {
4037 bool swapNeeded
= RangeImpl::compareBoundaryPoints(
4038 m_part
->d
->m_selectionStart
.handle(), m_part
->d
->m_startOffset
,
4039 m_part
->d
->m_selectionEnd
.handle(), m_part
->d
->m_endOffset
) >= 0;
4041 DOM::Node tmpNode
= m_part
->d
->m_selectionStart
;
4042 long tmpOffset
= m_part
->d
->m_startOffset
;
4043 m_part
->d
->m_selectionStart
= m_part
->d
->m_selectionEnd
;
4044 m_part
->d
->m_startOffset
= m_part
->d
->m_endOffset
;
4045 m_part
->d
->m_selectionEnd
= tmpNode
;
4046 m_part
->d
->m_endOffset
= tmpOffset
;
4047 m_part
->d
->m_startBeforeEnd
= true;
4048 m_part
->d
->m_extendAtEnd
= !m_part
->d
->m_extendAtEnd
;
4052 m_part
->xmlDocImpl()->setSelection(m_part
->d
->m_selectionStart
.handle(),
4053 m_part
->d
->m_startOffset
, m_part
->d
->m_selectionEnd
.handle(),
4054 m_part
->d
->m_endOffset
);
4055 m_part
->emitSelectionChanged();
4059 void KHTMLView::caretKeyPressEvent(QKeyEvent
*_ke
)
4061 NodeImpl
*oldStartSel
= m_part
->d
->m_selectionStart
.handle();
4062 long oldStartOfs
= m_part
->d
->m_startOffset
;
4063 NodeImpl
*oldEndSel
= m_part
->d
->m_selectionEnd
.handle();
4064 long oldEndOfs
= m_part
->d
->m_endOffset
;
4066 NodeImpl
*oldCaretNode
= m_part
->d
->caretNode().handle();
4067 long oldOffset
= m_part
->d
->caretOffset();
4069 bool ctrl
= _ke
->modifiers() & Qt::ControlModifier
;
4071 // FIXME: this is that widely indented because I will write ifs around it.
4072 switch(_ke
->key()) {
4077 moveCaretNextLine(1);
4081 moveCaretPrevLine(1);
4085 moveCaretBy(false, ctrl
? CaretByWord
: CaretByCharacter
, 1);
4089 moveCaretBy(true, ctrl
? CaretByWord
: CaretByCharacter
, 1);
4092 case Qt::Key_PageDown
:
4093 moveCaretNextPage();
4096 case Qt::Key_PageUp
:
4097 moveCaretPrevPage();
4102 moveCaretToDocumentBoundary(false);
4104 moveCaretToLineBegin();
4109 moveCaretToDocumentBoundary(true);
4111 moveCaretToLineEnd();
4116 if ((m_part
->d
->caretNode().handle() != oldCaretNode
4117 || m_part
->d
->caretOffset() != oldOffset
)
4118 // node should never be null, but faulty conditions may cause it to be
4119 && !m_part
->d
->caretNode().isNull()) {
4121 d
->m_caretViewContext
->caretMoved
= true;
4123 if (_ke
->modifiers() & Qt::ShiftModifier
) { // extend selection
4124 updateSelection(oldStartSel
, oldStartOfs
, oldEndSel
, oldEndOfs
);
4125 } else { // clear any selection
4126 if (foldSelectionToCaret(oldStartSel
, oldStartOfs
, oldEndSel
, oldEndOfs
))
4127 m_part
->emitSelectionChanged();
4130 m_part
->emitCaretPositionChanged(m_part
->d
->caretNode(), m_part
->d
->caretOffset());
4136 bool KHTMLView::moveCaretTo(NodeImpl
*node
, long offset
, bool clearSel
)
4138 if (!node
) return false;
4139 ElementImpl
*baseElem
= determineBaseElement(node
);
4140 RenderFlow
*base
= static_cast<RenderFlow
*>(baseElem
? baseElem
->renderer() : 0);
4141 if (!node
) return false;
4143 // need to find out the node's inline box. If there is none, this function
4144 // will snap to the next node that has one. This is necessary to make the
4145 // caret visible in any case.
4146 CaretBoxLineDeleter cblDeleter
;
4149 CaretBoxIterator cbit
;
4150 CaretBoxLine
*cbl
= findCaretBoxLine(node
, offset
, &cblDeleter
, base
, r_ofs
, cbit
);
4152 kWarning() << "KHTMLView::moveCaretTo - findCaretBoxLine() returns NULL" << endl
;
4156 #if DEBUG_CARETMODE > 3
4157 if (cbl
) kDebug(6200) << cbl
->information() << endl
;
4159 CaretBox
*box
= *cbit
;
4160 if (cbit
!= cbl
->end() && box
->object() != node
->renderer()) {
4161 if (box
->object()->element()) {
4162 mapRenderPosToDOMPos(box
->object(), r_ofs
, box
->isOutside(),
4163 box
->isOutsideEnd(), node
, offset
);
4164 //if (!outside) offset = node->minOffset();
4165 #if DEBUG_CARETMODE > 1
4166 kDebug(6200) << "set new node " << node
->nodeName().string() << "@" << node
<< endl
;
4168 } else { // box has no associated element -> do not use
4169 // this case should actually never happen.
4171 kError(6200) << "Box contains no node! Crash imminent" << endl
;
4175 NodeImpl
*oldStartSel
= m_part
->d
->m_selectionStart
.handle();
4176 long oldStartOfs
= m_part
->d
->m_startOffset
;
4177 NodeImpl
*oldEndSel
= m_part
->d
->m_selectionEnd
.handle();
4178 long oldEndOfs
= m_part
->d
->m_endOffset
;
4180 // test for position change
4181 bool posChanged
= m_part
->d
->caretNode().handle() != node
4182 || m_part
->d
->caretOffset() != offset
;
4183 bool selChanged
= false;
4185 m_part
->d
->caretNode() = node
;
4186 m_part
->d
->caretOffset() = offset
;
4187 if (clearSel
|| !oldStartSel
|| !oldEndSel
) {
4188 selChanged
= foldSelectionToCaret(oldStartSel
, oldStartOfs
, oldEndSel
, oldEndOfs
);
4190 //kDebug(6200) << "moveToCaret: extendSelection: m_extendAtEnd " << m_part->d->m_extendAtEnd << endl;
4191 //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;
4192 selChanged
= extendSelection(oldStartSel
, oldStartOfs
, oldEndSel
, oldEndOfs
);
4193 //kDebug(6200) << "after extendSelection: m_extendAtEnd " << m_part->d->m_extendAtEnd << endl;
4194 //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;
4197 d
->caretViewContext()->caretMoved
= true;
4199 bool visible_caret
= placeCaret(box
);
4201 // FIXME: if the old position was !visible_caret, and the new position is
4202 // also, then two caretPositionChanged signals with a null Node are
4203 // emitted in series.
4205 m_part
->emitCaretPositionChanged(visible_caret
? node
: 0, offset
);
4211 void KHTMLView::moveCaretByLine(bool next
, int count
)
4213 Node
&caretNodeRef
= m_part
->d
->caretNode();
4214 if (caretNodeRef
.isNull()) return;
4216 NodeImpl
*caretNode
= caretNodeRef
.handle();
4217 // kDebug(6200) << ": caretNode=" << caretNode << endl;
4218 long offset
= m_part
->d
->caretOffset();
4220 CaretViewContext
*cv
= d
->caretViewContext();
4222 ElementImpl
*baseElem
= determineBaseElement(caretNode
);
4223 LinearDocument
ld(m_part
, caretNode
, offset
, LeafsOnly
, baseElem
);
4225 ErgonomicEditableLineIterator
it(ld
.current(), cv
->origX
);
4227 // move count lines vertically
4228 while (count
> 0 && it
!= ld
.end() && it
!= ld
.preBegin()) {
4230 if (next
) ++it
; else --it
;
4233 // Nothing? Then leave everything as is.
4234 if (it
== ld
.end() || it
== ld
.preBegin()) return;
4237 CaretBox
*caretBox
= nearestCaretBox(it
, d
->m_caretViewContext
, x
, absx
, absy
);
4239 placeCaretOnLine(caretBox
, x
, absx
, absy
);
4242 void KHTMLView::placeCaretOnLine(CaretBox
*caretBox
, int x
, int absx
, int absy
)
4244 // paranoia sanity check
4245 if (!caretBox
) return;
4247 RenderObject
*caretRender
= caretBox
->object();
4249 #if DEBUG_CARETMODE > 0
4250 kDebug(6200) << "got valid caretBox " << caretBox
<< endl
;
4251 kDebug(6200) << "xPos: " << caretBox
->xPos() << " yPos: " << caretBox
->yPos()
4252 << " width: " << caretBox
->width() << " height: " << caretBox
->height() << endl
;
4253 InlineTextBox
*tb
= static_cast<InlineTextBox
*>(caretBox
->inlineBox());
4254 if (caretBox
->isInlineTextBox()) { kDebug(6200) << "contains \"" << QString(static_cast<RenderText
*>(tb
->object())->str
->s
+ tb
->m_start
, tb
->m_len
) << "\"" << endl
;}
4256 // inquire height of caret
4257 int caretHeight
= caretBox
->height();
4258 bool isText
= caretBox
->isInlineTextBox();
4259 int yOfs
= 0; // y-offset for text nodes
4261 // text boxes need extrawurst
4262 RenderText
*t
= static_cast<RenderText
*>(caretRender
);
4263 const QFontMetrics
&fm
= t
->metrics(caretBox
->inlineBox()->m_firstLine
);
4264 caretHeight
= fm
.height();
4265 yOfs
= caretBox
->inlineBox()->baseline() - fm
.ascent();
4270 // set new caret node
4271 NodeImpl
*caretNode
;
4272 long &offset
= m_part
->d
->caretOffset();
4273 mapRenderPosToDOMPos(caretRender
, offset
, caretBox
->isOutside(),
4274 caretBox
->isOutsideEnd(), caretNode
, offset
);
4276 // set all variables not needing special treatment
4277 d
->m_caretViewContext
->y
= caretBox
->yPos() + yOfs
;
4278 d
->m_caretViewContext
->height
= caretHeight
;
4279 d
->m_caretViewContext
->width
= 1; // FIXME: regard override
4281 int xPos
= caretBox
->xPos();
4282 int caretBoxWidth
= caretBox
->width();
4283 d
->m_caretViewContext
->x
= xPos
;
4285 if (!caretBox
->isOutside()) {
4286 // before or at beginning of inline box -> place at beginning
4289 r_ofs
= caretBox
->minOffset();
4290 // somewhere within this block
4291 } else if (x
> xPos
&& x
<= xPos
+ caretBoxWidth
) {
4292 if (isText
) { // find out where exactly
4293 r_ofs
= static_cast<InlineTextBox
*>(caretBox
->inlineBox())
4294 ->offsetForPoint(x
, d
->m_caretViewContext
->x
);
4295 #if DEBUG_CARETMODE > 2
4296 kDebug(6200) << "deviation from origX " << d
->m_caretViewContext
->x
- x
<< endl
;
4299 } else { // snap to nearest end
4300 if (xPos
+ caretBoxWidth
- x
< x
- xPos
) {
4301 d
->m_caretViewContext
->x
= xPos
+ caretBoxWidth
;
4302 r_ofs
= caretNode
? caretNode
->maxOffset() : 1;
4304 d
->m_caretViewContext
->x
= xPos
;
4305 r_ofs
= caretNode
? caretNode
->minOffset() : 0;
4309 } else { // after the inline box -> place at end
4310 d
->m_caretViewContext
->x
= xPos
+ caretBoxWidth
;
4311 r_ofs
= caretBox
->maxOffset();
4315 #if DEBUG_CARETMODE > 0
4316 kDebug(6200) << "new offset: " << offset
<< endl
;
4319 m_part
->d
->caretNode() = caretNode
;
4320 m_part
->d
->caretOffset() = offset
;
4322 d
->m_caretViewContext
->x
+= absx
;
4323 d
->m_caretViewContext
->y
+= absy
;
4325 #if DEBUG_CARETMODE > 1
4326 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
;
4329 ensureVisible(d
->m_caretViewContext
->x
, d
->m_caretViewContext
->y
,
4330 d
->m_caretViewContext
->width
, d
->m_caretViewContext
->height
);
4331 d
->scrollBarMoved
= false;
4333 ensureNodeHasFocus(caretNode
);
4337 void KHTMLView::moveCaretToLineBoundary(bool end
)
4339 Node
&caretNodeRef
= m_part
->d
->caretNode();
4340 if (caretNodeRef
.isNull()) return;
4342 NodeImpl
*caretNode
= caretNodeRef
.handle();
4343 // kDebug(6200) << ": caretNode=" << caretNode << endl;
4344 long offset
= m_part
->d
->caretOffset();
4346 ElementImpl
*baseElem
= determineBaseElement(caretNode
);
4347 LinearDocument
ld(m_part
, caretNode
, offset
, LeafsOnly
, baseElem
);
4349 EditableLineIterator it
= ld
.current();
4350 if (it
== ld
.end()) return; // should not happen, but who knows
4352 EditableCaretBoxIterator
fbit(it
, end
);
4353 Q_ASSERT(fbit
!= (*it
)->end() && fbit
!= (*it
)->preBegin());
4354 CaretBox
*b
= *fbit
;
4356 RenderObject
*cb
= b
->containingBlock();
4359 if (cb
) cb
->absolutePosition(absx
,absy
);
4360 else absx
= absy
= 0;
4362 int x
= b
->xPos() + (end
&& !b
->isOutside() ? b
->width() : 0);
4363 d
->m_caretViewContext
->origX
= absx
+ x
;
4364 placeCaretOnLine(b
, x
, absx
, absy
);
4367 void KHTMLView::moveCaretToDocumentBoundary(bool end
)
4369 Node
&caretNodeRef
= m_part
->d
->caretNode();
4370 if (caretNodeRef
.isNull()) return;
4372 NodeImpl
*caretNode
= caretNodeRef
.handle();
4373 // kDebug(6200) << ": caretNode=" << caretNode << endl;
4374 long offset
= m_part
->d
->caretOffset();
4376 ElementImpl
*baseElem
= determineBaseElement(caretNode
);
4377 LinearDocument
ld(m_part
, caretNode
, offset
, IndicatedFlows
, baseElem
);
4379 EditableLineIterator
it(end
? ld
.preEnd() : ld
.begin(), end
);
4380 if (it
== ld
.end() || it
== ld
.preBegin()) return; // should not happen, but who knows
4382 EditableCaretBoxIterator fbit
= it
;
4383 Q_ASSERT(fbit
!= (*it
)->end() && fbit
!= (*it
)->preBegin());
4384 CaretBox
*b
= *fbit
;
4386 RenderObject
*cb
= (*it
)->containingBlock();
4389 if (cb
) cb
->absolutePosition(absx
, absy
);
4390 else absx
= absy
= 0;
4392 int x
= b
->xPos()/* + (end ? b->width() : 0) reactivate for rtl*/;
4393 d
->m_caretViewContext
->origX
= absx
+ x
;
4394 placeCaretOnLine(b
, x
, absx
, absy
);
4397 void KHTMLView::moveCaretBy(bool next
, CaretMovement cmv
, int count
)
4399 if (!m_part
) return;
4400 Node
&caretNodeRef
= m_part
->d
->caretNode();
4401 if (caretNodeRef
.isNull()) return;
4403 NodeImpl
*caretNode
= caretNodeRef
.handle();
4404 // kDebug(6200) << ": caretNode=" << caretNode << endl;
4405 long &offset
= m_part
->d
->caretOffset();
4407 ElementImpl
*baseElem
= determineBaseElement(caretNode
);
4408 CaretAdvancePolicy advpol
= cmv
!= CaretByWord
? IndicatedFlows
: LeafsOnly
;
4409 LinearDocument
ld(m_part
, caretNode
, offset
, advpol
, baseElem
);
4411 EditableCharacterIterator
it(&ld
);
4412 while (!it
.isEnd() && count
> 0) {
4414 if (cmv
== CaretByCharacter
) {
4417 } else if (cmv
== CaretByWord
) {
4418 if (next
) moveItToNextWord(it
);
4419 else moveItToPrevWord(it
);
4421 //kDebug(6200) << "movecaret" << endl;
4423 CaretBox
*hintBox
= 0; // make gcc uninit warning disappear
4425 NodeImpl
*node
= caretNodeRef
.handle();
4426 hintBox
= it
.caretBox();
4427 //kDebug(6200) << "hintBox = " << hintBox << endl;
4428 //kDebug(6200) << " outside " << hintBox->isOutside() << " outsideEnd " << hintBox->isOutsideEnd() << " r " << it.renderer() << " ofs " << it.offset() << " cb " << hintBox->containingBlock() << endl;
4429 mapRenderPosToDOMPos(it
.renderer(), it
.offset(), hintBox
->isOutside(),
4430 hintBox
->isOutsideEnd(), node
, offset
);
4431 //kDebug(6200) << "mapRTD" << endl;
4432 caretNodeRef
= node
;
4433 #if DEBUG_CARETMODE > 2
4434 kDebug(6200) << "set by valid node " << node
<< " " << (node
?node
->nodeName().string():QString()) << " offset: " << offset
<< endl
;
4437 offset
= next
? caretNode
->maxOffset() : caretNode
->minOffset();
4438 #if DEBUG_CARETMODE > 0
4439 kDebug(6200) << "set by INvalid node. offset: " << offset
<< endl
;
4442 placeCaretOnChar(hintBox
);
4445 void KHTMLView::placeCaretOnChar(CaretBox
*hintBox
)
4448 recalcAndStoreCaretPos(hintBox
);
4449 ensureVisible(d
->m_caretViewContext
->x
, d
->m_caretViewContext
->y
,
4450 d
->m_caretViewContext
->width
, d
->m_caretViewContext
->height
);
4451 d
->m_caretViewContext
->origX
= d
->m_caretViewContext
->x
;
4452 d
->scrollBarMoved
= false;
4453 #if DEBUG_CARETMODE > 3
4454 //if (caretNode->isTextNode()) kDebug(6200) << "text[0] = " << (int)*((TextImpl *)caretNode)->data().unicode() << " text :\"" << ((TextImpl *)caretNode)->data().string() << "\"" << endl;
4456 ensureNodeHasFocus(m_part
->d
->caretNode().handle());
4460 void KHTMLView::moveCaretByPage(bool next
)
4462 Node
&caretNodeRef
= m_part
->d
->caretNode();
4463 if (caretNodeRef
.isNull()) return;
4465 NodeImpl
*caretNode
= caretNodeRef
.handle();
4466 // kDebug(6200) << ": caretNode=" << caretNode << endl;
4467 long offset
= m_part
->d
->caretOffset();
4469 int offs
= (viewport()->height() < 30) ? viewport()->height() : 30;
4470 // Minimum distance the caret must be moved
4471 int mindist
= viewport()->height() - offs
;
4473 CaretViewContext
*cv
= d
->caretViewContext();
4474 // int y = cv->y; // we always measure the top border
4476 ElementImpl
*baseElem
= determineBaseElement(caretNode
);
4477 LinearDocument
ld(m_part
, caretNode
, offset
, LeafsOnly
, baseElem
);
4479 ErgonomicEditableLineIterator
it(ld
.current(), cv
->origX
);
4481 moveIteratorByPage(ld
, it
, mindist
, next
);
4484 CaretBox
*caretBox
= nearestCaretBox(it
, d
->m_caretViewContext
, x
, absx
, absy
);
4486 placeCaretOnLine(caretBox
, x
, absx
, absy
);
4489 void KHTMLView::moveCaretPrevWord()
4491 moveCaretBy(false, CaretByWord
, 1);
4494 void KHTMLView::moveCaretNextWord()
4496 moveCaretBy(true, CaretByWord
, 1);
4499 void KHTMLView::moveCaretPrevLine(int n
)
4501 moveCaretByLine(false, n
);
4504 void KHTMLView::moveCaretNextLine(int n
)
4506 moveCaretByLine(true, n
);
4509 void KHTMLView::moveCaretPrevPage()
4511 moveCaretByPage(false);
4514 void KHTMLView::moveCaretNextPage()
4516 moveCaretByPage(true);
4519 void KHTMLView::moveCaretToLineBegin()
4521 moveCaretToLineBoundary(false);
4524 void KHTMLView::moveCaretToLineEnd()
4526 moveCaretToLineBoundary(true);
4529 #endif // KHTML_NO_CARET
4531 #undef DEBUG_CARETMODE