save previous node under mouse for mouseout/over
[kdelibs.git] / khtml / khtmlview.cpp
blob14a7634366667933397612c6097900a8d99b3c54
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"
34 #ifdef Q_WS_X11
35 #include <qx11info_x11.h>
36 #endif
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"
49 // removeme
50 #define protected public
51 #include "rendering/render_text.h"
52 #undef protected
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"
67 #endif
69 #include <kapplication.h>
70 #include <kcursor.h>
71 #include <kdebug.h>
72 #include <kglobalsettings.h>
73 #include <kdialog.h>
74 #include <kiconloader.h>
75 #include <klocale.h>
76 #include <knotification.h>
77 #include <kprinter.h>
78 #include <ksimpleconfig.h>
79 #include <kstandarddirs.h>
80 #include <kstandardshortcut.h>
81 #include <kstringhandler.h>
82 #include <kconfiggroup.h>
84 #include <qbitmap.h>
85 #include <qlabel.h>
86 #include <qobject.h>
87 #include <q3paintdevicemetrics.h>
88 #include <qpainter.h>
89 #include <q3ptrdict.h>
90 #include <qtooltip.h>
91 #include <qstring.h>
92 #include <QTextDocument>
93 #include <qtimer.h>
94 #include <QAbstractEventDispatcher>
95 #include <qvector.h>
96 #include <Q3ListBox>
97 #include <QAbstractScrollArea>
99 //#define DEBUG_FLICKER
101 //#define DEBUG_PIXEL
103 #ifdef Q_WS_X11
104 #include <X11/Xlib.h>
105 #include <fixx11h.h>
106 #elif defined(Q_WS_WIN)
107 #include <Windows.h>
108 #endif
110 #if 0
111 namespace khtml {
112 void dumpLineBoxes(RenderFlow *flow);
114 #endif
116 using namespace DOM;
117 using namespace khtml;
122 class KHTMLViewPrivate {
123 friend class KHTMLView;
124 public:
126 enum PseudoFocusNodes {
127 PFNone,
128 PFTop,
129 PFBottom
132 enum CompletedState {
133 CSNone = 0,
134 CSFull,
135 CSActionPending
138 KHTMLViewPrivate()
139 : underMouse( 0 ), underMouseNonShared( 0 ), oldUnderMouse( 0 ), visibleWidgets( 107 )
141 #ifndef KHTML_NO_CARET
142 m_caretViewContext = 0;
143 m_editorContext = 0;
144 #endif // KHTML_NO_CARET
145 postponed_autorepeat = NULL;
146 reset();
147 vpolicy = Qt::ScrollBarAsNeeded;
148 hpolicy = Qt::ScrollBarAsNeeded;
149 formCompletions=0;
150 prevScrollbarVisible = true;
152 possibleTripleClick = false;
153 emitCompletedAfterRepaint = CSNone;
154 cursor_icon_widget = NULL;
155 m_mouseScrollTimer = 0;
156 m_mouseScrollIndicator = 0;
158 ~KHTMLViewPrivate()
160 delete formCompletions;
161 delete postponed_autorepeat;
162 if (underMouse)
163 underMouse->deref();
164 if (underMouseNonShared)
165 underMouseNonShared->deref();
166 if (oldUnderMouse)
167 oldUnderMouse->deref();
169 #ifndef KHTML_NO_CARET
170 delete m_caretViewContext;
171 delete m_editorContext;
172 #endif // KHTML_NO_CARET
173 delete cursor_icon_widget;
174 delete m_mouseScrollTimer;
175 delete m_mouseScrollIndicator;
177 void reset()
179 if (underMouse)
180 underMouse->deref();
181 underMouse = 0;
182 if (underMouseNonShared)
183 underMouseNonShared->deref();
184 underMouseNonShared = 0;
185 if (oldUnderMouse)
186 oldUnderMouse->deref();
187 oldUnderMouse = 0;
188 linkPressed = false;
189 useSlowRepaints = false;
190 tabMovePending = false;
191 lastTabbingDirection = true;
192 pseudoFocusNode = PFNone;
193 #ifndef KHTML_NO_SCROLLBARS
194 //We don't turn off the toolbars here
195 //since if the user turns them
196 //off, then chances are they want them turned
197 //off always - even after a reset.
198 #else
199 vpolicy = ScrollBarAlwaysOff;
200 hpolicy = ScrollBarAlwaysOff;
201 #endif
202 #ifdef DEBUG_PIXEL
203 timer.start();
204 pixelbooth = 0;
205 repaintbooth = 0;
206 #endif
207 scrollBarMoved = false;
208 contentsMoving = false;
209 ignoreWheelEvents = false;
210 borderX = 30;
211 borderY = 30;
212 paged = false;
213 clickX = -1;
214 clickY = -1;
215 contentsX = 0;
216 contentsY = 0;
217 clickCount = 0;
218 isDoubleClick = false;
219 scrollingSelf = false;
220 delete postponed_autorepeat;
221 postponed_autorepeat = NULL;
222 layoutTimerId = 0;
223 repaintTimerId = 0;
224 scrollTimerId = 0;
225 scrollSuspended = false;
226 scrollSuspendPreActivate = false;
227 complete = false;
228 firstRelayout = true;
229 needsFullRepaint = true;
230 dirtyLayout = false;
231 layoutSchedulingEnabled = true;
232 painting = false;
233 updateRegion = QRegion();
234 m_dialogsAllowed = true;
235 #ifndef KHTML_NO_CARET
236 if (m_caretViewContext) {
237 m_caretViewContext->caretMoved = false;
238 m_caretViewContext->keyReleasePending = false;
239 }/*end if*/
240 #endif // KHTML_NO_CARET
241 #ifndef KHTML_NO_TYPE_AHEAD_FIND
242 typeAheadActivated = false;
243 #endif // KHTML_NO_TYPE_AHEAD_FIND
244 accessKeysActivated = false;
245 accessKeysPreActivate = false;
247 // We ref/deref to ensure defaultHTMLSettings is available
248 KHTMLFactory::ref();
249 accessKeysEnabled = KHTMLFactory::defaultHTMLSettings()->accessKeysEnabled();
250 KHTMLFactory::deref();
252 emitCompletedAfterRepaint = CSNone;
253 m_mouseEventsTarget = 0;
255 void newScrollTimer(QWidget *view, int tid)
257 //kDebug(6000) << "newScrollTimer timer " << tid << endl;
258 view->killTimer(scrollTimerId);
259 scrollTimerId = tid;
260 scrollSuspended = false;
262 enum ScrollDirection { ScrollLeft, ScrollRight, ScrollUp, ScrollDown };
264 void adjustScroller(QWidget *view, ScrollDirection direction, ScrollDirection oppositedir)
266 static const struct { int msec, pixels; } timings [] = {
267 {320,1}, {224,1}, {160,1}, {112,1}, {80,1}, {56,1}, {40,1},
268 {28,1}, {20,1}, {20,2}, {20,3}, {20,4}, {20,6}, {20,8}, {0,0}
270 if (!scrollTimerId ||
271 (static_cast<int>(scrollDirection) != direction &&
272 (static_cast<int>(scrollDirection) != oppositedir || scrollSuspended))) {
273 scrollTiming = 6;
274 scrollBy = timings[scrollTiming].pixels;
275 scrollDirection = direction;
276 newScrollTimer(view, view->startTimer(timings[scrollTiming].msec));
277 } else if (scrollDirection == direction &&
278 timings[scrollTiming+1].msec && !scrollSuspended) {
279 scrollBy = timings[++scrollTiming].pixels;
280 newScrollTimer(view, view->startTimer(timings[scrollTiming].msec));
281 } else if (scrollDirection == oppositedir) {
282 if (scrollTiming) {
283 scrollBy = timings[--scrollTiming].pixels;
284 newScrollTimer(view, view->startTimer(timings[scrollTiming].msec));
287 scrollSuspended = false;
290 #ifndef KHTML_NO_CARET
291 /** this function returns an instance of the caret view context. If none
292 * exists, it will be instantiated.
294 CaretViewContext *caretViewContext() {
295 if (!m_caretViewContext) m_caretViewContext = new CaretViewContext();
296 return m_caretViewContext;
298 /** this function returns an instance of the editor context. If none
299 * exists, it will be instantiated.
301 EditorContext *editorContext() {
302 if (!m_editorContext) m_editorContext = new EditorContext();
303 return m_editorContext;
305 #endif // KHTML_NO_CARET
307 #ifdef DEBUG_PIXEL
308 QTime timer;
309 unsigned int pixelbooth;
310 unsigned int repaintbooth;
311 #endif
313 NodeImpl *underMouse;
314 NodeImpl *underMouseNonShared;
315 NodeImpl *oldUnderMouse;
317 bool tabMovePending:1;
318 bool lastTabbingDirection:1;
319 PseudoFocusNodes pseudoFocusNode:2;
320 bool scrollBarMoved:1;
321 bool contentsMoving:1;
323 Qt::ScrollBarPolicy vpolicy;
324 Qt::ScrollBarPolicy hpolicy;
325 bool prevScrollbarVisible:1;
326 bool linkPressed:1;
327 bool useSlowRepaints:1;
328 bool ignoreWheelEvents:1;
330 int borderX, borderY;
331 KSimpleConfig *formCompletions;
333 bool paged;
335 int clickX, clickY, clickCount;
336 bool isDoubleClick;
338 int contentsX, contentsY;
339 bool scrollingSelf;
340 int layoutTimerId;
341 QKeyEvent* postponed_autorepeat;
343 int repaintTimerId;
344 int scrollTimerId;
345 int scrollTiming;
346 int scrollBy;
347 ScrollDirection scrollDirection :2;
348 bool scrollSuspended :1;
349 bool scrollSuspendPreActivate :1;
350 bool complete :1;
351 bool firstRelayout :1;
352 bool layoutSchedulingEnabled :1;
353 bool needsFullRepaint :1;
354 bool painting :1;
355 bool possibleTripleClick :1;
356 bool dirtyLayout :1;
357 bool m_dialogsAllowed :1;
358 QRegion updateRegion;
359 Q3PtrDict<QWidget> visibleWidgets;
360 #ifndef KHTML_NO_CARET
361 CaretViewContext *m_caretViewContext;
362 EditorContext *m_editorContext;
363 #endif // KHTML_NO_CARET
364 #ifndef KHTML_NO_TYPE_AHEAD_FIND
365 QString findString;
366 QTimer timer;
367 bool findLinksOnly;
368 bool typeAheadActivated;
369 #endif // KHTML_NO_TYPE_AHEAD_FIND
370 bool accessKeysEnabled;
371 bool accessKeysActivated;
372 bool accessKeysPreActivate;
373 CompletedState emitCompletedAfterRepaint;
375 QWidget* cursor_icon_widget;
377 // scrolling activated by MMB
378 short m_mouseScroll_byX;
379 short m_mouseScroll_byY;
380 QTimer *m_mouseScrollTimer;
381 QWidget *m_mouseScrollIndicator;
382 QPointer<QWidget> m_mouseEventsTarget;
385 #ifndef QT_NO_TOOLTIP
387 /** calculates the client-side image map rectangle for the given image element
388 * @param img image element
389 * @param scrollOfs scroll offset of viewport in content coordinates
390 * @param p position to be probed in viewport coordinates
391 * @param r returns the bounding rectangle in content coordinates
392 * @param s returns the title string
393 * @return true if an appropriate area was found -- only in this case r and
394 * s are valid, false otherwise
396 static bool findImageMapRect(HTMLImageElementImpl *img, const QPoint &scrollOfs,
397 const QPoint &p, QRect &r, QString &s)
399 HTMLMapElementImpl* map;
400 if (img && img->getDocument()->isHTMLDocument() &&
401 (map = static_cast<HTMLDocumentImpl*>(img->getDocument())->getMap(img->imageMap()))) {
402 RenderObject::NodeInfo info(true, false);
403 RenderObject *rend = img->renderer();
404 int ax, ay;
405 if (!rend || !rend->absolutePosition(ax, ay))
406 return false;
407 // we're a client side image map
408 bool inside = map->mapMouseEvent(p.x() - ax + scrollOfs.x(),
409 p.y() - ay + scrollOfs.y(), rend->contentWidth(),
410 rend->contentHeight(), info);
411 if (inside && info.URLElement()) {
412 HTMLAreaElementImpl *area = static_cast<HTMLAreaElementImpl *>(info.URLElement());
413 Q_ASSERT(area->id() == ID_AREA);
414 s = area->getAttribute(ATTR_TITLE).string();
415 QRegion reg = area->cachedRegion();
416 if (!s.isEmpty() && !reg.isEmpty()) {
417 r = reg.boundingRect();
418 r.translate(ax, ay);
419 return true;
423 return false;
426 bool KHTMLView::event( QEvent* e )
428 if ( e->type() == QEvent::ToolTip) {
429 QHelpEvent *he = static_cast<QHelpEvent*>(e);
430 QPoint p = he->pos();
432 DOM::NodeImpl *node = d->underMouseNonShared;
433 QRect region;
434 while ( node ) {
435 if ( node->isElementNode() ) {
436 DOM::ElementImpl *e = static_cast<DOM::ElementImpl*>( node );
437 QRect r;
438 QString s;
439 bool found = false;
440 // for images, check if it is part of a client-side image map,
441 // and query the <area>s' title attributes, too
442 if (e->id() == ID_IMG && !e->getAttribute( ATTR_USEMAP ).isEmpty()) {
443 found = findImageMapRect(static_cast<HTMLImageElementImpl *>(e),
444 viewportToContents(QPoint(0, 0)), p, r, s);
446 if (!found) {
447 s = e->getAttribute( ATTR_TITLE ).string();
448 r = node->getRect();
450 region |= QRect( contentsToViewport( r.topLeft() ), r.size() );
451 if ( !s.isEmpty() ) {
452 QToolTip::showText( viewport()->mapToGlobal(region.bottomLeft()),
453 Qt::convertFromPlainText( s, Qt::WhiteSpaceNormal ) );
454 break;
457 node = node->parentNode();
459 return true;
461 return QScrollArea::event(e);
463 #endif
465 KHTMLView::KHTMLView( KHTMLPart *part, QWidget *parent )
466 : QScrollArea( parent ), d( new KHTMLViewPrivate )
468 m_medium = "screen";
470 m_part = part;
471 QScrollArea::setVerticalScrollBarPolicy(d->vpolicy);
472 QScrollArea::setHorizontalScrollBarPolicy(d->hpolicy);
473 connect(KGlobalSettings::self(), SIGNAL(kdisplayPaletteChanged()), this, SLOT(slotPaletteChanged()));
475 #ifndef KHTML_NO_TYPE_AHEAD_FIND
476 connect(&d->timer, SIGNAL(timeout()), this, SLOT(findTimeout()));
477 #endif // KHTML_NO_TYPE_AHEAD_FIND
479 init();
480 widget()->setMouseTracking(true);
483 KHTMLView::~KHTMLView()
485 closeChildDialogs();
486 if (m_part)
488 DOM::DocumentImpl *doc = m_part->xmlDocImpl();
489 if (doc)
490 doc->detach();
492 delete d;
495 void KHTMLView::init()
497 setFocusPolicy(Qt::StrongFocus);
498 viewport()->setFocusProxy(this);
500 _marginWidth = -1; // undefined
501 _marginHeight = -1;
502 _width = 0;
503 _height = 0;
505 installEventFilter(this);
507 setAcceptDrops(true);
508 if (!widget())
509 setWidget( new QWidget(this) );
510 QSize s = viewport()->size();
511 resizeContents(s.width(), s.height());
513 // ### we'll enable redirection of khtmlview here
514 // when event and painting issues have been thoroughly worked out
515 // m_kwp->setIsRedirected(true);
518 void KHTMLView::clear()
520 #ifndef KHTML_NO_CARET
521 if (!m_part->isCaretMode() && !m_part->isEditable()) caretOff();
522 #endif
523 #ifndef KHTML_NO_TYPE_AHEAD_FIND
524 if( d->typeAheadActivated )
525 findTimeout();
526 #endif
527 if (d->accessKeysEnabled && d->accessKeysActivated)
528 accessKeysTimeout();
529 viewport()->unsetCursor();
530 if ( d->cursor_icon_widget )
531 d->cursor_icon_widget->hide();
532 d->reset();
533 QAbstractEventDispatcher::instance()->unregisterTimers(this);
534 emit cleared();
536 QScrollArea::setHorizontalScrollBarPolicy(d->hpolicy);
537 QScrollArea::setVerticalScrollBarPolicy(d->vpolicy);
538 verticalScrollBar()->setEnabled( false );
539 horizontalScrollBar()->setEnabled( false );
542 void KHTMLView::hideEvent(QHideEvent* e)
544 QScrollArea::hideEvent(e);
545 if ( m_part && m_part->xmlDocImpl() )
546 m_part->xmlDocImpl()->docLoader()->pauseAnimations();
549 void KHTMLView::showEvent(QShowEvent* e)
551 QScrollArea::showEvent(e);
552 if ( m_part && m_part->xmlDocImpl() )
553 m_part->xmlDocImpl()->docLoader()->resumeAnimations();
556 void KHTMLView::setMouseEventsTarget( QWidget* w )
558 d->m_mouseEventsTarget = w;
561 QWidget* KHTMLView::mouseEventsTarget() const
563 return d->m_mouseEventsTarget;
566 int KHTMLView::contentsWidth() const
568 return widget() ? widget()->width() : 0;
571 int KHTMLView::contentsHeight() const
573 return widget() ? widget()->height() : 0;
576 void KHTMLView::resizeContents(int w, int h)
578 if (!widget())
579 return;
580 widget()->resize(w, h);
583 int KHTMLView::contentsX() const
585 return d->contentsX;
588 int KHTMLView::contentsY() const
590 return d->contentsY;
593 int KHTMLView::visibleWidth() const
595 return viewport()->width();
598 int KHTMLView::visibleHeight() const
600 return viewport()->height();
603 void KHTMLView::setContentsPos( int x, int y)
605 horizontalScrollBar()->setValue( x );
606 verticalScrollBar()->setValue( y );
609 void KHTMLView::scrollBy(int x, int y)
611 horizontalScrollBar()->setValue( horizontalScrollBar()->value()+x );
612 verticalScrollBar()->setValue( verticalScrollBar()->value()+y );
615 QPoint KHTMLView::contentsToViewport(const QPoint& p) const
617 return QPoint(p.x()-contentsX(), p.y()-contentsY());
620 void KHTMLView::contentsToViewport(int x, int y, int& cx, int& cy) const
622 QPoint p(x,y);
623 p = contentsToViewport(p);
624 cx = p.x();
625 cy = p.y();
628 QPoint KHTMLView::viewportToContents(const QPoint& p) const
630 return QPoint(p.x()+contentsX(), p.y()+contentsY());
633 void KHTMLView::viewportToContents(int x, int y, int& cx, int& cy) const
635 QPoint p(x,y);
636 p = viewportToContents(p);
637 cx = p.x();
638 cy = p.y();
641 void KHTMLView::updateContents(int x, int y, int w, int h)
643 widget()->update(x, y, w, h);
646 void KHTMLView::updateContents( const QRect& r )
648 updateContents( r.x(), r.y(), r.width(), r.height() );
651 void KHTMLView::repaintContents(int x, int y, int w, int h)
653 widget()->repaint(x, y, w, h);
656 void KHTMLView::repaintContents( const QRect& r )
658 repaintContents( r.x(), r.y(), r.width(), r.height() );
661 void KHTMLView::resizeEvent (QResizeEvent* e)
663 int dw = e->oldSize().width() - e->size().width();
664 int dh = e->oldSize().height() - e->size().height();
666 // if we are shrinking the view, don't allow the content to overflow
667 // before the layout occurs - we don't know if we need scrollbars yet
668 dw = dw>0 ? qMax(0, contentsWidth()-dw) : contentsWidth();
669 dh = dh>0 ? qMax(0, contentsHeight()-dh) : contentsHeight();
671 resizeContents(dw, dh);
673 if (d->layoutSchedulingEnabled)
674 layout();
675 #ifndef KHTML_NO_CARET
676 else {
677 hideCaret();
678 recalcAndStoreCaretPos();
679 showCaret();
680 }/*end if*/
681 #endif
683 KApplication::sendPostedEvents(viewport(), QEvent::Paint);
685 if ( m_part && m_part->xmlDocImpl() )
686 m_part->xmlDocImpl()->dispatchWindowEvent( EventImpl::RESIZE_EVENT, false, false );
689 void KHTMLView::paintEvent( QPaintEvent *e )
691 QPainter p(widget());
693 QRect r = e->rect();
694 QRect v(contentsX(), contentsY(), visibleWidth(), visibleHeight());
696 r = r.intersect(v);
697 if (!r.isValid() || r.isEmpty()) return;
698 p.setClipRect(v);
700 int ex = r.x();
701 int ey = r.y();
702 int ew = r.width();
703 int eh = r.height();
705 if(!m_part || !m_part->xmlDocImpl() || !m_part->xmlDocImpl()->renderer()) {
706 p.fillRect(ex, ey, ew, eh, palette().brush(QPalette::Active, QPalette::Base));
707 return;
708 } else if ( d->complete && static_cast<RenderCanvas*>(m_part->xmlDocImpl()->renderer())->needsLayout() ) {
709 // an external update request happens while we have a layout scheduled
710 unscheduleRelayout();
711 layout();
714 if (d->painting) {
715 kDebug( 6000 ) << "WARNING: paintEvent reentered! " << endl;
716 kDebug( 6000 ) << kBacktrace() << endl;
717 return;
719 d->painting = true;
721 m_part->xmlDocImpl()->renderer()->layer()->paint(&p, r);
723 #ifndef KHTML_NO_CARET
724 if (d->m_caretViewContext && d->m_caretViewContext->visible) {
725 QRect pos(d->m_caretViewContext->x, d->m_caretViewContext->y,
726 d->m_caretViewContext->width, d->m_caretViewContext->height);
727 if (pos.intersects(QRect(ex, ey, ew, eh))) {
728 p.setCompositionMode(QPainter::CompositionMode_Xor);
729 p.setPen(Qt::white);
730 if (pos.width() == 1)
731 p.drawLine(pos.topLeft(), pos.bottomRight());
732 else {
733 p.fillRect(pos, Qt::white);
737 #endif // KHTML_NO_CARET
739 khtml::DrawContentsEvent event( &p, ex, ey, ew, eh );
740 QApplication::sendEvent( m_part, &event );
742 if (d->scrollingSelf || d->contentsMoving || r.contains(widget()->mapFromGlobal(QCursor::pos()))) {
743 QMouseEvent *tempEvent = new QMouseEvent( QEvent::MouseMove, widget()->mapFromGlobal( QCursor::pos() ),
744 Qt::NoButton, Qt::NoButton, Qt::NoModifier );
745 mouseMoveEvent( tempEvent );
746 delete tempEvent;
749 d->painting = false;
752 void KHTMLView::setMarginWidth(int w)
754 // make it update the rendering area when set
755 _marginWidth = w;
758 void KHTMLView::setMarginHeight(int h)
760 // make it update the rendering area when set
761 _marginHeight = h;
764 void KHTMLView::layout()
766 if( m_part && m_part->xmlDocImpl() ) {
767 DOM::DocumentImpl *document = m_part->xmlDocImpl();
769 khtml::RenderCanvas* canvas = static_cast<khtml::RenderCanvas *>(document->renderer());
770 if ( !canvas ) return;
772 d->layoutSchedulingEnabled=false;
774 // the reference object for the overflow property on canvas
775 RenderObject * ref = 0;
776 RenderObject* root = document->documentElement() ? document->documentElement()->renderer() : 0;
778 if (document->isHTMLDocument()) {
779 NodeImpl *body = static_cast<HTMLDocumentImpl*>(document)->body();
780 if(body && body->renderer() && body->id() == ID_FRAMESET) {
781 QScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
782 QScrollArea::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
783 body->renderer()->setNeedsLayout(true);
785 else if (root) // only apply body's overflow to canvas if root has a visible overflow
786 ref = (!body || root->style()->hidesOverflow()) ? root : body->renderer();
787 } else {
788 ref = root;
790 if (ref) {
791 if( ref->style()->overflowX() == OHIDDEN ) {
792 if (d->hpolicy == Qt::ScrollBarAsNeeded) QScrollArea::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
793 } else if (ref->style()->overflowX() == OSCROLL ) {
794 if (d->hpolicy == Qt::ScrollBarAsNeeded) QScrollArea::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
795 } else {
796 QScrollArea::setHorizontalScrollBarPolicy(d->hpolicy);
798 if ( ref->style()->overflowY() == OHIDDEN ) {
799 if (d->vpolicy == Qt::ScrollBarAsNeeded) QScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
800 } else if (ref->style()->overflowY() == OSCROLL ) {
801 if (d->vpolicy == Qt::ScrollBarAsNeeded) QScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
802 } else {
803 QScrollArea::setVerticalScrollBarPolicy(d->vpolicy);
806 d->needsFullRepaint = d->firstRelayout;
807 if (_height != visibleHeight() || _width != visibleWidth()) {;
808 d->needsFullRepaint = true;
809 _height = visibleHeight();
810 _width = visibleWidth();
813 canvas->layout();
815 emit finishedLayout();
816 if (d->firstRelayout) {
817 // make sure firstRelayout is set to false now in case this layout
818 // wasn't scheduled
819 d->firstRelayout = false;
820 verticalScrollBar()->setEnabled( true );
821 horizontalScrollBar()->setEnabled( true );
823 #ifndef KHTML_NO_CARET
824 hideCaret();
825 if ((m_part->isCaretMode() || m_part->isEditable())
826 && !d->complete && d->m_caretViewContext
827 && !d->m_caretViewContext->caretMoved) {
828 initCaret();
829 } else {
830 recalcAndStoreCaretPos();
831 showCaret();
832 }/*end if*/
833 #endif
834 if (d->accessKeysEnabled && d->accessKeysActivated) {
835 emit hideAccessKeys();
836 displayAccessKeys();
839 else
840 _width = visibleWidth();
842 if (d->layoutTimerId)
843 killTimer(d->layoutTimerId);
844 d->layoutTimerId = 0;
845 d->layoutSchedulingEnabled=true;
848 void KHTMLView::closeChildDialogs()
850 QList<QDialog *> dlgs = findChildren<QDialog *>();
851 foreach (QDialog *dlg, dlgs)
853 KDialog* dlgbase = dynamic_cast<KDialog*>( dlg );
854 if ( dlgbase ) {
855 if ( dlgbase->testAttribute( Qt::WA_ShowModal ) ) {
856 kDebug(6000) << "closeChildDialogs: closing dialog " << dlgbase << endl;
857 // close() ends up calling QButton::animateClick, which isn't immediate
858 // we need something the exits the event loop immediately (#49068)
859 dlgbase->reject();
862 else
864 kWarning() << "closeChildDialogs: not a KDialog! Don't use QDialogs in KDE! " << static_cast<QWidget*>(dlg) << endl;
865 static_cast<QWidget*>(dlg)->hide();
868 d->m_dialogsAllowed = false;
871 bool KHTMLView::dialogsAllowed() {
872 bool allowed = d->m_dialogsAllowed;
873 KHTMLPart* p = m_part->parentPart();
874 if (p && p->view())
875 allowed &= p->view()->dialogsAllowed();
876 return allowed;
879 void KHTMLView::closeEvent( QCloseEvent* ev )
881 closeChildDialogs();
882 QScrollArea::closeEvent( ev );
886 // Event Handling
888 /////////////////
890 void KHTMLView::mousePressEvent( QMouseEvent *_mouse )
892 if (!m_part->xmlDocImpl()) return;
893 if (d->possibleTripleClick && ( _mouse->button() & Qt::MouseButtonMask ) == Qt::LeftButton)
895 mouseDoubleClickEvent( _mouse ); // it handles triple clicks too
896 return;
899 int xm = _mouse->x();
900 int ym = _mouse->y();
902 // kDebug( 6000 ) << "mousePressEvent: viewport=("<<_mouse->x()-contentsX()<<"/"<<_mouse->y()-contentsY()<<"), contents=(" << xm << "/" << ym << ")\n";
904 d->isDoubleClick = false;
906 DOM::NodeImpl::MouseEvent mev( _mouse->buttons(), DOM::NodeImpl::MousePress );
907 m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev );
909 //kDebug(6000) << "innerNode="<<mev.innerNode.nodeName().string()<<endl;
911 if ( (_mouse->button() == Qt::MidButton) &&
912 !m_part->d->m_bOpenMiddleClick && !d->m_mouseScrollTimer &&
913 mev.url.isNull() && (mev.innerNode.elementId() != ID_INPUT) ) {
914 QPoint point = mapFromGlobal( _mouse->globalPos() );
916 d->m_mouseScroll_byX = 0;
917 d->m_mouseScroll_byY = 0;
919 d->m_mouseScrollTimer = new QTimer( this );
920 connect( d->m_mouseScrollTimer, SIGNAL(timeout()), this, SLOT(slotMouseScrollTimer()) );
922 if ( !d->m_mouseScrollIndicator ) {
923 QPixmap pixmap( 48, 48 ), icon;
924 pixmap.fill( QColor( qRgba( 127, 127, 127, 127 ) ) );
926 QPainter p( &pixmap );
927 icon = KHTMLFactory::iconLoader()->loadIcon( "1uparrow", K3Icon::Small );
928 p.drawPixmap( 16, 0, icon );
929 icon = KHTMLFactory::iconLoader()->loadIcon( "1leftarrow", K3Icon::Small );
930 p.drawPixmap( 0, 16, icon );
931 icon = KHTMLFactory::iconLoader()->loadIcon( "1downarrow", K3Icon::Small );
932 p.drawPixmap( 16, 32,icon );
933 icon = KHTMLFactory::iconLoader()->loadIcon( "1rightarrow", K3Icon::Small );
934 p.drawPixmap( 32, 16, icon );
935 p.drawEllipse( 23, 23, 2, 2 );
937 d->m_mouseScrollIndicator = new QWidget( this );
938 d->m_mouseScrollIndicator->setFixedSize( 48, 48 );
939 QPalette palette;
940 palette.setBrush( d->m_mouseScrollIndicator->backgroundRole(), QBrush( pixmap ) );
941 d->m_mouseScrollIndicator->setPalette( palette );
943 d->m_mouseScrollIndicator->move( point.x()-24, point.y()-24 );
945 bool hasHorBar = visibleWidth() < contentsWidth();
946 bool hasVerBar = visibleHeight() < contentsHeight();
948 KConfigGroup cg( KGlobal::config(), "HTML Settings" );
949 if ( cg.readEntry( "ShowMouseScrollIndicator", true ) ) {
950 d->m_mouseScrollIndicator->show();
951 d->m_mouseScrollIndicator->unsetCursor();
953 QBitmap mask = d->m_mouseScrollIndicator->palette().brush(d->m_mouseScrollIndicator->backgroundRole()).texture().createHeuristicMask( true );
955 if ( hasHorBar && !hasVerBar ) {
956 QBitmap bm( 16, 16 );
957 bm.clear();
958 QPainter painter( &mask );
959 painter.drawPixmap( QRectF( 16, 0, bm.width(), bm.height() ), bm, bm.rect() );
960 painter.drawPixmap( QRectF( 16, 32, bm.width(), bm.height() ), bm, bm.rect() );
961 d->m_mouseScrollIndicator->setCursor( Qt::SizeHorCursor );
963 else if ( !hasHorBar && hasVerBar ) {
964 QBitmap bm( 16, 16 );
965 bm.clear();
966 QPainter painter( &mask );
967 painter.drawPixmap( QRectF( 0, 16, bm.width(), bm.height() ), bm, bm.rect() );
968 painter.drawPixmap( QRectF( 32, 16, bm.width(), bm.height() ), bm, bm.rect() );
969 d->m_mouseScrollIndicator->setCursor( Qt::SizeVerCursor );
971 else
972 d->m_mouseScrollIndicator->setCursor( Qt::SizeAllCursor );
974 d->m_mouseScrollIndicator->setMask( mask );
976 else {
977 if ( hasHorBar && !hasVerBar )
978 viewport()->setCursor( Qt::SizeHorCursor );
979 else if ( !hasHorBar && hasVerBar )
980 viewport()->setCursor( Qt::SizeVerCursor );
981 else
982 viewport()->setCursor( Qt::SizeAllCursor );
985 return;
987 else if ( d->m_mouseScrollTimer ) {
988 delete d->m_mouseScrollTimer;
989 d->m_mouseScrollTimer = 0;
991 if ( d->m_mouseScrollIndicator )
992 d->m_mouseScrollIndicator->hide();
995 d->clickCount = 1;
996 d->clickX = xm;
997 d->clickY = ym;
999 bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEDOWN_EVENT,mev.innerNode.handle(),mev.innerNonSharedNode.handle(),true,
1000 d->clickCount,_mouse,true,DOM::NodeImpl::MousePress);
1002 khtml::RenderObject* r = mev.innerNode.handle() ? mev.innerNode.handle()->renderer() : 0;
1003 if (r && r->isWidget())
1004 _mouse->ignore();
1006 if (!swallowEvent) {
1007 emit m_part->nodeActivated(mev.innerNode);
1009 khtml::MousePressEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode );
1010 QApplication::sendEvent( m_part, &event );
1011 // we might be deleted after this
1015 void KHTMLView::mouseDoubleClickEvent( QMouseEvent *_mouse )
1017 if(!m_part->xmlDocImpl()) return;
1019 int xm = _mouse->x();
1020 int ym = _mouse->y();
1022 // kDebug( 6000 ) << "mouseDblClickEvent: x=" << xm << ", y=" << ym << endl;
1024 d->isDoubleClick = true;
1026 DOM::NodeImpl::MouseEvent mev( _mouse->buttons(), DOM::NodeImpl::MouseDblClick );
1027 m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev );
1029 // We do the same thing as mousePressEvent() here, since the DOM does not treat
1030 // single and double-click events as separate (only the detail, i.e. number of clicks differs)
1031 if (d->clickCount > 0 &&
1032 QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() <= QApplication::startDragDistance())
1033 d->clickCount++;
1034 else { // shouldn't happen, if Qt has the same criterias for double clicks.
1035 d->clickCount = 1;
1036 d->clickX = xm;
1037 d->clickY = ym;
1039 bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEDOWN_EVENT,mev.innerNode.handle(),mev.innerNonSharedNode.handle(),true,
1040 d->clickCount,_mouse,true,DOM::NodeImpl::MouseDblClick);
1042 khtml::RenderObject* r = mev.innerNode.handle() ? mev.innerNode.handle()->renderer() : 0;
1043 if (r && r->isWidget())
1044 _mouse->ignore();
1046 if (!swallowEvent) {
1047 khtml::MouseDoubleClickEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode, d->clickCount );
1048 QApplication::sendEvent( m_part, &event );
1051 d->possibleTripleClick=true;
1052 QTimer::singleShot(QApplication::doubleClickInterval(),this,SLOT(tripleClickTimeout()));
1055 void KHTMLView::tripleClickTimeout()
1057 d->possibleTripleClick = false;
1058 d->clickCount = 0;
1061 void KHTMLView::mouseMoveEvent( QMouseEvent * _mouse )
1063 if ( d->m_mouseScrollTimer ) {
1064 QPoint point = mapFromGlobal( _mouse->globalPos() );
1066 int deltaX = point.x() - d->m_mouseScrollIndicator->x() - 24;
1067 int deltaY = point.y() - d->m_mouseScrollIndicator->y() - 24;
1069 (deltaX > 0) ? d->m_mouseScroll_byX = 1 : d->m_mouseScroll_byX = -1;
1070 (deltaY > 0) ? d->m_mouseScroll_byY = 1 : d->m_mouseScroll_byY = -1;
1072 double adX = QABS(deltaX)/30.0;
1073 double adY = QABS(deltaY)/30.0;
1075 d->m_mouseScroll_byX = qMax(qMin(d->m_mouseScroll_byX * int(adX*adX), SHRT_MAX), SHRT_MIN);
1076 d->m_mouseScroll_byY = qMax(qMin(d->m_mouseScroll_byY * int(adY*adY), SHRT_MAX), SHRT_MIN);
1078 if (d->m_mouseScroll_byX == 0 && d->m_mouseScroll_byY == 0) {
1079 d->m_mouseScrollTimer->stop();
1081 else if (!d->m_mouseScrollTimer->isActive()) {
1082 d->m_mouseScrollTimer->start( 20 );
1086 if(!m_part->xmlDocImpl()) return;
1088 int xm = _mouse->x();
1089 int ym = _mouse->y();
1091 DOM::NodeImpl::MouseEvent mev( _mouse->buttons(), DOM::NodeImpl::MouseMove );
1092 // Do not modify :hover/:active state while mouse is pressed.
1093 m_part->xmlDocImpl()->prepareMouseEvent( _mouse->buttons() /*readonly ?*/, xm, ym, &mev );
1095 // kDebug(6000) << "mouse move: " << _mouse->pos()
1096 // << " button " << _mouse->button()
1097 // << " state " << _mouse->state() << endl;
1099 DOM::NodeImpl* target = mev.innerNode.handle();
1100 DOM::NodeImpl* fn = m_part->xmlDocImpl()->focusNode();
1102 // a widget may be the real target of this event (e.g. if a scrollbar's slider is being moved)
1103 if (d->m_mouseEventsTarget && fn && fn->renderer() && fn->renderer()->isWidget())
1104 target = fn;
1106 bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEMOVE_EVENT,target,mev.innerNonSharedNode.handle(),false,
1107 0,_mouse,true,DOM::NodeImpl::MouseMove);
1109 if (d->clickCount > 0 &&
1110 QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() > QApplication::startDragDistance()) {
1111 d->clickCount = 0; // moving the mouse outside the threshold invalidates the click
1114 // execute the scheduled script. This is to make sure the mouseover events come after the mouseout events
1115 m_part->executeScheduledScript();
1117 khtml::RenderObject* r = target ? target->renderer() : 0;
1118 khtml::RenderStyle* style = (r && r->style()) ? r->style() : 0;
1119 QCursor c;
1120 bool mailtoCursor = false;
1121 switch ( style ? style->cursor() : CURSOR_AUTO) {
1122 case CURSOR_AUTO:
1123 if ( r && r->isText() )
1124 c = KCursor::ibeamCursor();
1125 if ( mev.url.length() && m_part->settings()->changeCursor() ) {
1126 c = m_part->urlCursor();
1127 if (mev.url.string().startsWith("mailto:") && mev.url.string().indexOf('@')>0)
1128 mailtoCursor = true;
1131 if (r && r->isFrameSet() && !static_cast<RenderFrameSet*>(r)->noResize())
1132 c = QCursor(static_cast<RenderFrameSet*>(r)->cursorShape());
1134 break;
1135 case CURSOR_CROSS:
1136 c = KCursor::crossCursor();
1137 break;
1138 case CURSOR_POINTER:
1139 c = m_part->urlCursor();
1140 if (mev.url.string().startsWith("mailto:") && mev.url.string().indexOf('@')>0)
1141 mailtoCursor = true;
1142 break;
1143 case CURSOR_PROGRESS:
1144 c = KCursor::workingCursor();
1145 break;
1146 case CURSOR_MOVE:
1147 c = KCursor::sizeAllCursor();
1148 break;
1149 case CURSOR_E_RESIZE:
1150 case CURSOR_W_RESIZE:
1151 c = KCursor::sizeHorCursor();
1152 break;
1153 case CURSOR_N_RESIZE:
1154 case CURSOR_S_RESIZE:
1155 c = KCursor::sizeVerCursor();
1156 break;
1157 case CURSOR_NE_RESIZE:
1158 case CURSOR_SW_RESIZE:
1159 c = KCursor::sizeBDiagCursor();
1160 break;
1161 case CURSOR_NW_RESIZE:
1162 case CURSOR_SE_RESIZE:
1163 c = KCursor::sizeFDiagCursor();
1164 break;
1165 case CURSOR_TEXT:
1166 c = KCursor::ibeamCursor();
1167 break;
1168 case CURSOR_WAIT:
1169 c = KCursor::waitCursor();
1170 break;
1171 case CURSOR_HELP:
1172 c = KCursor::whatsThisCursor();
1173 break;
1174 case CURSOR_DEFAULT:
1175 break;
1178 if ( viewport()->cursor().handle() != c.handle() ) {
1179 if( c.handle() == KCursor::arrowCursor().handle()) {
1180 for (KHTMLPart* p = m_part; p; p = p->parentPart())
1181 p->view()->viewport()->unsetCursor();
1183 else {
1184 viewport()->setCursor( c );
1188 if ( mailtoCursor && isVisible() && hasFocus() ) {
1189 #ifdef Q_WS_X11
1190 if( !d->cursor_icon_widget ) {
1191 QPixmap icon_pixmap = KHTMLFactory::iconLoader()->loadIcon( "mail_generic", K3Icon::Small, 0, K3Icon::DefaultState, 0, true );
1192 #ifdef Q_WS_X11
1193 d->cursor_icon_widget = new QWidget( 0, Qt::WX11BypassWM );
1194 XSetWindowAttributes attr;
1195 attr.save_under = True;
1196 XChangeWindowAttributes( QX11Info::display(), d->cursor_icon_widget->winId(), CWSaveUnder, &attr );
1197 #else
1198 d->cursor_icon_widget = new QWidget( NULL, NULL );
1199 //TODO
1200 #endif
1201 d->cursor_icon_widget->resize( icon_pixmap.width(), icon_pixmap.height());
1202 if( !icon_pixmap.mask().isNull() )
1203 d->cursor_icon_widget->setMask( icon_pixmap.mask());
1204 else
1205 d->cursor_icon_widget->clearMask();
1206 QPalette palette;
1207 palette.setBrush( d->cursor_icon_widget->backgroundRole(), QBrush( icon_pixmap ) );
1208 d->cursor_icon_widget->setPalette( palette );
1209 d->cursor_icon_widget->erase();
1211 QPoint c_pos = QCursor::pos();
1212 d->cursor_icon_widget->move( c_pos.x() + 15, c_pos.y() + 15 );
1213 #ifdef Q_WS_X11
1214 XRaiseWindow( QX11Info::display(), d->cursor_icon_widget->winId());
1215 QApplication::flush();
1216 #elif defined(Q_WS_WIN)
1217 SetWindowPos( d->cursor_icon_widget->winId(), HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE );
1218 #else
1219 //TODO?
1220 #endif
1221 d->cursor_icon_widget->show();
1222 #endif
1224 else if ( d->cursor_icon_widget )
1225 d->cursor_icon_widget->hide();
1227 if (r && r->isWidget()) {
1228 _mouse->ignore();
1231 if (!swallowEvent) {
1232 khtml::MouseMoveEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode );
1233 QApplication::sendEvent( m_part, &event );
1237 void KHTMLView::mouseReleaseEvent( QMouseEvent * _mouse )
1239 bool swallowEvent = false;
1241 int xm = _mouse->x();
1242 int ym = _mouse->y();
1244 DOM::NodeImpl::MouseEvent mev( _mouse->buttons(), DOM::NodeImpl::MouseRelease );
1246 if ( m_part->xmlDocImpl() )
1248 m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev );
1250 DOM::NodeImpl* target = mev.innerNode.handle();
1251 DOM::NodeImpl* fn = m_part->xmlDocImpl()->focusNode();
1253 // a widget may be the real target of this event (e.g. if a scrollbar's slider is being moved)
1254 if (d->m_mouseEventsTarget && fn && fn->renderer() && fn->renderer()->isWidget())
1255 target = fn;
1257 swallowEvent = dispatchMouseEvent(EventImpl::MOUSEUP_EVENT,target,mev.innerNonSharedNode.handle(),true,
1258 d->clickCount,_mouse,false,DOM::NodeImpl::MouseRelease);
1260 // clear our sticky event target on any mouseRelease event
1261 if (d->m_mouseEventsTarget)
1262 d->m_mouseEventsTarget = 0;
1264 if (d->clickCount > 0 &&
1265 QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() <= QApplication::startDragDistance()) {
1266 QMouseEvent me(d->isDoubleClick ? QEvent::MouseButtonDblClick : QEvent::MouseButtonRelease,
1267 _mouse->pos(), _mouse->button(), _mouse->buttons(), _mouse->modifiers());
1268 dispatchMouseEvent(EventImpl::CLICK_EVENT, mev.innerNode.handle(),mev.innerNonSharedNode.handle(),true,
1269 d->clickCount, &me, true, DOM::NodeImpl::MouseRelease);
1272 khtml::RenderObject* r = target ? target->renderer() : 0;
1273 if (r && r->isWidget())
1274 _mouse->ignore();
1277 if (!swallowEvent) {
1278 khtml::MouseReleaseEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode );
1279 QApplication::sendEvent( m_part, &event );
1283 // returns true if event should be swallowed
1284 bool KHTMLView::dispatchKeyEvent( QKeyEvent *_ke )
1286 if (!m_part->xmlDocImpl())
1287 return false;
1288 // Pressing and releasing a key should generate keydown, keypress and keyup events
1289 // Holding it down should generated keydown, keypress (repeatedly) and keyup events
1290 // The problem here is that Qt generates two autorepeat events (keyrelease+keypress)
1291 // for autorepeating, while DOM wants only one autorepeat event (keypress), so one
1292 // of the Qt events shouldn't be passed to DOM, but it should be still filtered
1293 // out if DOM would filter the autorepeat event. Additional problem is that Qt keyrelease
1294 // events don't have text() set (Qt bug?), so DOM often would ignore the keypress event
1295 // if it was created using Qt keyrelease, but Qt autorepeat keyrelease comes
1296 // before Qt autorepeat keypress (i.e. problem whether to filter it out or not).
1297 // The solution is to filter out and postpone the Qt autorepeat keyrelease until
1298 // the following Qt keypress event comes. If DOM accepts the DOM keypress event,
1299 // the postponed event will be simply discarded. If not, it will be passed to keyPressEvent()
1300 // again, and here it will be ignored.
1302 // Qt: Press | Release(autorepeat) Press(autorepeat) etc. | Release
1303 // DOM: Down + Press | (nothing) Press | Up
1305 // It's also possible to get only Releases. E.g. the release of alt-tab,
1306 // or when the keypresses get captured by an accel.
1308 if( _ke == d->postponed_autorepeat ) // replayed event
1310 return false;
1313 if( _ke->type() == QEvent::KeyPress )
1315 if( !_ke->isAutoRepeat())
1317 bool ret = dispatchKeyEventHelper( _ke, false ); // keydown
1318 // don't send keypress even if keydown was blocked, like IE (and unlike Mozilla)
1319 if( !ret && dispatchKeyEventHelper( _ke, true )) // keypress
1320 ret = true;
1321 return ret;
1323 else // autorepeat
1325 bool ret = dispatchKeyEventHelper( _ke, true ); // keypress
1326 if( !ret && d->postponed_autorepeat )
1327 keyPressEvent( d->postponed_autorepeat );
1328 delete d->postponed_autorepeat;
1329 d->postponed_autorepeat = NULL;
1330 return ret;
1333 else // QEvent::KeyRelease
1335 // Discard postponed "autorepeat key-release" events that didn't see
1336 // a keypress after them (e.g. due to QAccel)
1337 if ( d->postponed_autorepeat ) {
1338 delete d->postponed_autorepeat;
1339 d->postponed_autorepeat = 0;
1342 if( !_ke->isAutoRepeat()) {
1343 return dispatchKeyEventHelper( _ke, false ); // keyup
1345 else
1347 d->postponed_autorepeat = new QKeyEvent( _ke->type(), _ke->key(), _ke->modifiers(),
1348 _ke->text(), _ke->isAutoRepeat(), _ke->count());
1349 if( _ke->isAccepted())
1350 d->postponed_autorepeat->accept();
1351 else
1352 d->postponed_autorepeat->ignore();
1353 return true;
1358 // returns true if event should be swallowed
1359 bool KHTMLView::dispatchKeyEventHelper( QKeyEvent *_ke, bool keypress )
1361 DOM::NodeImpl* keyNode = m_part->xmlDocImpl()->focusNode();
1362 if (keyNode) {
1363 return keyNode->dispatchKeyEvent(_ke, keypress);
1364 } else { // no focused node, send to document
1365 return m_part->xmlDocImpl()->dispatchKeyEvent(_ke, keypress);
1369 void KHTMLView::keyPressEvent( QKeyEvent *_ke )
1371 #ifndef KHTML_NO_TYPE_AHEAD_FIND
1372 if(d->typeAheadActivated)
1374 // type-ahead find aka find-as-you-type
1375 if(_ke->key() == Qt::Key_Backspace)
1377 d->findString = d->findString.left(d->findString.length() - 1);
1379 if(!d->findString.isEmpty())
1381 findAhead(false);
1383 else
1385 findTimeout();
1388 d->timer.setSingleShot(true);
1389 d->timer.start(3000);
1390 _ke->accept();
1391 return;
1393 else if(_ke->key() == Qt::Key_Escape)
1395 findTimeout();
1397 _ke->accept();
1398 return;
1400 else if(_ke->key() == Qt::Key_Space || !_ke->text().trimmed().isEmpty())
1402 d->findString += _ke->text();
1404 findAhead(true);
1406 d->timer.setSingleShot(true);
1407 d->timer.start(3000);
1408 _ke->accept();
1409 return;
1412 #endif // KHTML_NO_TYPE_AHEAD_FIND
1414 #ifndef KHTML_NO_CARET
1415 if (m_part->isEditable() || m_part->isCaretMode()
1416 || (m_part->xmlDocImpl() && m_part->xmlDocImpl()->focusNode()
1417 && m_part->xmlDocImpl()->focusNode()->contentEditable())) {
1418 d->caretViewContext()->keyReleasePending = true;
1419 caretKeyPressEvent(_ke);
1420 return;
1422 #endif // KHTML_NO_CARET
1424 // If CTRL was hit, be prepared for access keys
1425 if (d->accessKeysEnabled && _ke->key() == Qt::Key_Control && _ke->modifiers()==0 && !d->accessKeysActivated)
1427 d->accessKeysPreActivate=true;
1428 _ke->accept();
1429 return;
1432 if (_ke->key() == Qt::Key_Shift && _ke->modifiers()==0)
1433 d->scrollSuspendPreActivate=true;
1435 // accesskey handling needs to be done before dispatching, otherwise e.g. lineedits
1436 // may eat the event
1438 if (d->accessKeysEnabled && d->accessKeysActivated)
1440 int state = ( _ke->modifiers() & ( Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier ));
1441 if ( state==0 || state==Qt::ShiftModifier) {
1442 if (_ke->key() != Qt::Key_Shift) accessKeysTimeout();
1443 handleAccessKey( _ke );
1444 _ke->accept();
1445 return;
1447 accessKeysTimeout();
1450 if ( dispatchKeyEvent( _ke )) {
1451 // If either keydown or keypress was accepted by a widget, or canceled by JS, stop here.
1452 _ke->accept();
1453 return;
1456 int offs = (viewport()->height() < 30) ? viewport()->height() : 30; // ### ??
1457 if (_ke->modifiers() & Qt::ShiftModifier)
1458 switch(_ke->key())
1460 case Qt::Key_Space:
1461 verticalScrollBar()->setValue( verticalScrollBar()->value() -viewport()->height() + offs );
1462 if(d->scrollSuspended)
1463 d->newScrollTimer(this, 0);
1464 break;
1466 case Qt::Key_Down:
1467 case Qt::Key_J:
1468 d->adjustScroller(this, KHTMLViewPrivate::ScrollDown, KHTMLViewPrivate::ScrollUp);
1469 break;
1471 case Qt::Key_Up:
1472 case Qt::Key_K:
1473 d->adjustScroller(this, KHTMLViewPrivate::ScrollUp, KHTMLViewPrivate::ScrollDown);
1474 break;
1476 case Qt::Key_Left:
1477 case Qt::Key_H:
1478 d->adjustScroller(this, KHTMLViewPrivate::ScrollLeft, KHTMLViewPrivate::ScrollRight);
1479 break;
1481 case Qt::Key_Right:
1482 case Qt::Key_L:
1483 d->adjustScroller(this, KHTMLViewPrivate::ScrollRight, KHTMLViewPrivate::ScrollLeft);
1484 break;
1486 else
1487 switch ( _ke->key() )
1489 case Qt::Key_Down:
1490 case Qt::Key_J:
1491 if (!d->scrollTimerId || d->scrollSuspended)
1492 verticalScrollBar()->setValue( verticalScrollBar()->value()+10 );
1493 if (d->scrollTimerId)
1494 d->newScrollTimer(this, 0);
1495 break;
1497 case Qt::Key_Space:
1498 case Qt::Key_PageDown:
1499 verticalScrollBar()->setValue( verticalScrollBar()->value() +viewport()->height() - offs );
1500 if(d->scrollSuspended)
1501 d->newScrollTimer(this, 0);
1502 break;
1504 case Qt::Key_Up:
1505 case Qt::Key_K:
1506 if (!d->scrollTimerId || d->scrollSuspended)
1507 verticalScrollBar()->setValue( verticalScrollBar()->value()-10 );
1508 if (d->scrollTimerId)
1509 d->newScrollTimer(this, 0);
1510 break;
1512 case Qt::Key_PageUp:
1513 verticalScrollBar()->setValue( verticalScrollBar()->value() -viewport()->height() + offs );
1514 if(d->scrollSuspended)
1515 d->newScrollTimer(this, 0);
1516 break;
1517 case Qt::Key_Right:
1518 case Qt::Key_L:
1519 if (!d->scrollTimerId || d->scrollSuspended)
1520 horizontalScrollBar()->setValue( horizontalScrollBar()->value()+10 );
1521 if (d->scrollTimerId)
1522 d->newScrollTimer(this, 0);
1523 break;
1525 case Qt::Key_Left:
1526 case Qt::Key_H:
1527 if (!d->scrollTimerId || d->scrollSuspended)
1528 horizontalScrollBar()->setValue( horizontalScrollBar()->value()-10 );
1529 if (d->scrollTimerId)
1530 d->newScrollTimer(this, 0);
1531 break;
1532 case Qt::Key_Enter:
1533 case Qt::Key_Return:
1534 // ### FIXME:
1535 // or even better to HTMLAnchorElementImpl::event()
1536 if (m_part->xmlDocImpl()) {
1537 NodeImpl *n = m_part->xmlDocImpl()->focusNode();
1538 if (n)
1539 n->setActive();
1541 break;
1542 case Qt::Key_Home:
1543 verticalScrollBar()->setValue( 0 );
1544 horizontalScrollBar()->setValue( 0 );
1545 if(d->scrollSuspended)
1546 d->newScrollTimer(this, 0);
1547 break;
1548 case Qt::Key_End:
1549 verticalScrollBar()->setValue( contentsHeight() - visibleHeight() );
1550 if(d->scrollSuspended)
1551 d->newScrollTimer(this, 0);
1552 break;
1553 case Qt::Key_Shift:
1554 // what are you doing here?
1555 _ke->ignore();
1556 return;
1557 default:
1558 if (d->scrollTimerId)
1559 d->newScrollTimer(this, 0);
1560 _ke->ignore();
1561 return;
1564 _ke->accept();
1567 void KHTMLView::findTimeout()
1569 #ifndef KHTML_NO_TYPE_AHEAD_FIND
1570 d->typeAheadActivated = false;
1571 d->findString = "";
1572 m_part->setStatusBarText(i18n("Find stopped."), KHTMLPart::BarDefaultText);
1573 m_part->enableFindAheadActions( true );
1574 #endif // KHTML_NO_TYPE_AHEAD_FIND
1577 #ifndef KHTML_NO_TYPE_AHEAD_FIND
1578 void KHTMLView::startFindAhead( bool linksOnly )
1580 if( linksOnly )
1582 d->findLinksOnly = true;
1583 m_part->setStatusBarText(i18n("Starting -- find links as you type"),
1584 KHTMLPart::BarDefaultText);
1586 else
1588 d->findLinksOnly = false;
1589 m_part->setStatusBarText(i18n("Starting -- find text as you type"),
1590 KHTMLPart::BarDefaultText);
1593 m_part->findTextBegin();
1594 d->typeAheadActivated = true;
1595 // disable, so that the shortcut ( / or ' by default ) doesn't interfere
1596 m_part->enableFindAheadActions( false );
1597 d->timer.setSingleShot(true);
1598 d->timer.start(3000);
1601 void KHTMLView::findAhead(bool increase)
1603 QString status;
1604 QString text = d->findString.toLower();
1606 if(d->findLinksOnly)
1608 m_part->findText(d->findString, KHTMLPart::FindNoPopups |
1609 KHTMLPart::FindLinksOnly, this);
1610 if(m_part->findTextNext())
1612 status = i18n("Link found: \"%1\".", text);
1614 else
1616 if(increase) KNotification::beep();
1617 status = i18n("Link not found: \"%1\".", text);
1620 else
1622 m_part->findText(d->findString, KHTMLPart::FindNoPopups, this);
1623 if(m_part->findTextNext())
1625 status = i18n("Text found: \"%1\".", text);
1627 else
1629 if(increase) KNotification::beep();
1630 status = i18n("Text not found: \"%1\".", text);
1634 m_part->setStatusBarText(status, KHTMLPart::BarDefaultText);
1637 void KHTMLView::updateFindAheadTimeout()
1639 if( d->typeAheadActivated ) {
1640 d->timer.setSingleShot( true );
1641 d->timer.start( 3000 );
1645 #endif // KHTML_NO_TYPE_AHEAD_FIND
1647 void KHTMLView::keyReleaseEvent(QKeyEvent *_ke)
1649 #ifndef KHTML_NO_TYPE_AHEAD_FIND
1650 if(d->typeAheadActivated) {
1651 _ke->accept();
1652 return;
1654 #endif
1655 if (d->m_caretViewContext && d->m_caretViewContext->keyReleasePending) {
1656 //caretKeyReleaseEvent(_ke);
1657 d->m_caretViewContext->keyReleasePending = false;
1658 return;
1661 if( d->scrollSuspendPreActivate && _ke->key() != Qt::Key_Shift )
1662 d->scrollSuspendPreActivate = false;
1663 if( _ke->key() == Qt::Key_Shift && d->scrollSuspendPreActivate && _ke->modifiers() == Qt::ShiftModifier
1664 && !(QApplication::keyboardModifiers() & Qt::ShiftModifier))
1665 if (d->scrollTimerId)
1666 d->scrollSuspended = !d->scrollSuspended;
1668 if (d->accessKeysEnabled)
1670 if (d->accessKeysPreActivate && _ke->key() != Qt::Key_Control)
1671 d->accessKeysPreActivate=false;
1672 if (d->accessKeysPreActivate && _ke->modifiers() == Qt::ControlModifier &&
1673 !(QApplication::keyboardModifiers() & Qt::ControlModifier))
1675 displayAccessKeys();
1676 m_part->setStatusBarText(i18n("Access Keys activated"),KHTMLPart::BarOverrideText);
1677 d->accessKeysActivated = true;
1678 d->accessKeysPreActivate = false;
1679 _ke->accept();
1680 return;
1682 else if (d->accessKeysActivated)
1684 accessKeysTimeout();
1685 _ke->accept();
1686 return;
1690 // Send keyup event
1691 if ( dispatchKeyEvent( _ke ) )
1693 _ke->accept();
1694 return;
1697 QScrollArea::keyReleaseEvent(_ke);
1700 bool KHTMLView::focusNextPrevChild( bool next )
1702 // Now try to find the next child
1703 if (m_part->xmlDocImpl() && focusNextPrevNode(next))
1705 if (m_part->xmlDocImpl()->focusNode())
1706 kDebug() << "focusNode.name: "
1707 << m_part->xmlDocImpl()->focusNode()->nodeName().string() << endl;
1708 return true; // focus node found
1711 // If we get here, pass tabbing control up to the next/previous child in our parent
1712 d->pseudoFocusNode = KHTMLViewPrivate::PFNone;
1713 if (m_part->parentPart() && m_part->parentPart()->view())
1714 return m_part->parentPart()->view()->focusNextPrevChild(next);
1716 return QWidget::focusNextPrevChild(next);
1719 void KHTMLView::doAutoScroll()
1721 QPoint pos = QCursor::pos();
1722 pos = viewport()->mapFromGlobal( pos );
1724 int xm, ym;
1725 viewportToContents(pos.x(), pos.y(), xm, ym);
1727 pos = QPoint(pos.x() - viewport()->x(), pos.y() - viewport()->y());
1728 if ( (pos.y() < 0) || (pos.y() > visibleHeight()) ||
1729 (pos.x() < 0) || (pos.x() > visibleWidth()) )
1731 ensureVisible( xm, ym, 0, 5 );
1733 #ifndef KHTML_NO_SELECTION
1734 // extend the selection while scrolling
1735 DOM::Node innerNode;
1736 if (m_part->isExtendingSelection()) {
1737 RenderObject::NodeInfo renderInfo(true/*readonly*/, false/*active*/);
1738 m_part->xmlDocImpl()->renderer()->layer()
1739 ->nodeAtPoint(renderInfo, xm, ym);
1740 innerNode = renderInfo.innerNode();
1741 }/*end if*/
1743 if (innerNode.handle() && innerNode.handle()->renderer()) {
1744 int absX, absY;
1745 innerNode.handle()->renderer()->absolutePosition(absX, absY);
1747 m_part->extendSelectionTo(xm, ym, absX, absY, innerNode);
1748 }/*end if*/
1749 #endif // KHTML_NO_SELECTION
1753 static void handleWidget(QWidget* w, KHTMLView* view)
1755 if (w->isTopLevel())
1756 return;
1758 if (!qobject_cast<QFrame*>(w))
1759 w->setAttribute( Qt::WA_NoSystemBackground );
1760 w->setAttribute(Qt::WA_WState_InPaintEvent); // ### horrible - FIXME (needs Qt change to Widget::update)
1761 w->setAttribute(Qt::WA_OpaquePaintEvent);
1762 w->installEventFilter(view);
1764 QObjectList children = w->children();
1765 foreach (QObject* object, children) {
1766 QWidget *widget = qobject_cast<QWidget*>(object);
1767 if (widget)
1768 handleWidget(widget, view);
1772 class KHTMLBackingStoreHackWidget : public QWidget
1774 public:
1775 void publicEvent(QEvent *e)
1777 QWidget::event(e);
1781 bool KHTMLView::viewportEvent ( QEvent * e )
1783 switch (e->type()) {
1784 // those must not be dispatched to the specialized handlers
1785 // as widgetEvent() already took care of that
1786 case QEvent::MouseButtonPress:
1787 case QEvent::MouseButtonRelease:
1788 case QEvent::MouseButtonDblClick:
1789 case QEvent::MouseMove:
1790 #ifndef QT_NO_WHEELEVENT
1791 case QEvent::Wheel:
1792 #endif
1793 case QEvent::ContextMenu:
1794 case QEvent::DragEnter:
1795 case QEvent::DragMove:
1796 case QEvent::DragLeave:
1797 case QEvent::Drop:
1798 return false;
1799 case QEvent::Paint: {
1800 QRect r = static_cast<QPaintEvent*>(e)->rect();
1801 r.setX(r.x() +contentsX());
1802 r.setY(r.y() +contentsY());
1803 QPaintEvent pe(r);
1804 paintEvent(&pe);
1805 return true;
1807 default:
1808 break;
1810 return QScrollArea::viewportEvent(e);
1813 bool KHTMLView::eventFilter(QObject *o, QEvent *e)
1815 if ( e->type() == QEvent::ShortcutOverride ) {
1816 QKeyEvent* ke = (QKeyEvent*) e;
1817 if (m_part->isEditable() || m_part->isCaretMode()
1818 || (m_part->xmlDocImpl() && m_part->xmlDocImpl()->focusNode()
1819 && m_part->xmlDocImpl()->focusNode()->contentEditable())) {
1820 if ( (ke->modifiers() & Qt::ControlModifier) || (ke->modifiers() & Qt::ShiftModifier) ) {
1821 switch ( ke->key() ) {
1822 case Qt::Key_Left:
1823 case Qt::Key_Right:
1824 case Qt::Key_Up:
1825 case Qt::Key_Down:
1826 case Qt::Key_Home:
1827 case Qt::Key_End:
1828 ke->accept();
1829 return true;
1830 default:
1831 break;
1837 if ( e->type() == QEvent::Leave ) {
1838 if ( d->cursor_icon_widget )
1839 d->cursor_icon_widget->hide();
1840 m_part->resetHoverText();
1843 QWidget *view = widget();
1844 if (o == view) {
1845 if (widgetEvent(e))
1846 return true;
1847 } else if (o->isWidgetType()) {
1848 QWidget *v = static_cast<QWidget *>(o);
1849 QWidget *c = v;
1850 while (v && v != view) {
1851 c = v;
1852 v = v->parentWidget();
1854 KHTMLWidget* k = dynamic_cast<KHTMLWidget*>(c);
1855 if (v && k && k->m_kwp->isRedirected()) {
1856 bool block = false;
1857 bool isUpdate = false;
1858 QWidget *w = static_cast<QWidget *>(o);
1859 switch(e->type()) {
1860 case QEvent::UpdateRequest: {
1861 // implicitly call qt_syncBackingStore(w)
1862 static_cast<KHTMLBackingStoreHackWidget *>(w)->publicEvent(e);
1863 block = true;
1864 break;
1866 case QEvent::UpdateLater:
1867 isUpdate = true;
1868 // no break;
1869 case QEvent::Paint:
1870 if (!allowWidgetPaintEvents) {
1871 // eat the event. Like this we can control exactly when the widget
1872 // gets repainted.
1873 block = true;
1874 int x = 0, y = 0;
1875 QWidget *v = w;
1876 while (v && v->parentWidget() != view) {
1877 x += v->x();
1878 y += v->y();
1879 v = v->parentWidget();
1882 QPoint ap = k->m_kwp->absolutePos();
1883 x += ap.x();
1884 y += ap.y();
1886 QRect pr = isUpdate ? static_cast<QUpdateLaterEvent*>(e)->region().boundingRect() : static_cast<QPaintEvent *>(e)->rect();
1887 bool asap = !isUpdate && !d->contentsMoving && (qobject_cast<Q3ScrollView*>(c) || qobject_cast<QAbstractScrollArea*>(c));
1889 if (isUpdate) {
1890 // ### horrible - FIXME
1891 w->setAttribute(Qt::WA_WState_InPaintEvent, false);
1892 w->update(static_cast<QUpdateLaterEvent*>(e)->region());
1893 w->setAttribute(Qt::WA_WState_InPaintEvent);
1894 // implicitly call qt_syncBackingStore(w)
1895 QEvent fakeEvent(QEvent::UpdateRequest);
1896 static_cast<KHTMLBackingStoreHackWidget *>(w)->publicEvent(&fakeEvent);
1899 // QScrollView needs fast repaints
1900 if ( asap && !d->painting && m_part->xmlDocImpl() && m_part->xmlDocImpl()->renderer() &&
1901 !static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer())->needsLayout() ) {
1902 d->painting = true;
1903 repaintContents(x + pr.x(), y + pr.y(),
1904 pr.width(), pr.height()+1); // ### investigate that +1 (shows up when
1905 // updating e.g a textarea's blinking cursor)
1906 d->painting = false;
1907 } else if (!d->painting) {
1908 scheduleRepaint(x + pr.x(), y + pr.y(),
1909 pr.width(), pr.height()+1, asap);
1912 break;
1913 case QEvent::MouseMove:
1914 case QEvent::MouseButtonPress:
1915 case QEvent::MouseButtonRelease:
1916 case QEvent::MouseButtonDblClick: {
1918 if (0 && w->parentWidget() == view && !qobject_cast<QScrollBar*>(w) && !::qobject_cast<QScrollBar *>(w)) {
1919 QMouseEvent *me = static_cast<QMouseEvent *>(e);
1920 QPoint pt = w->mapTo( view, me->pos());
1921 QMouseEvent me2(me->type(), pt, me->button(), me->buttons(), me->modifiers());
1923 if (e->type() == QEvent::MouseMove)
1924 mouseMoveEvent(&me2);
1925 else if(e->type() == QEvent::MouseButtonPress)
1926 mousePressEvent(&me2);
1927 else if(e->type() == QEvent::MouseButtonRelease)
1928 mouseReleaseEvent(&me2);
1929 else
1930 mouseDoubleClickEvent(&me2);
1931 block = true;
1933 break;
1935 case QEvent::KeyPress:
1936 case QEvent::KeyRelease:
1937 if (w->parentWidget() == view && !qobject_cast<QScrollBar*>(w)) {
1938 QKeyEvent *ke = static_cast<QKeyEvent *>(e);
1939 if (e->type() == QEvent::KeyPress)
1940 keyPressEvent(ke);
1941 else
1942 keyReleaseEvent(ke);
1943 block = true;
1945 break;
1946 case QEvent::FocusIn:
1947 case QEvent::FocusOut:
1948 block = true;
1949 break;
1950 default:
1951 break;
1953 if (block) {
1954 //qDebug("eating event");
1955 return true;
1960 // kDebug(6000) <<"passing event on to sv event filter object=" << o->className() << " event=" << e->type() << endl;
1961 return QScrollArea::eventFilter(o, e);
1965 bool KHTMLView::widgetEvent(QEvent* e)
1967 switch (e->type()) {
1968 case QEvent::MouseButtonPress:
1969 case QEvent::MouseButtonRelease:
1970 case QEvent::MouseButtonDblClick:
1971 case QEvent::MouseMove:
1972 case QEvent::Paint:
1973 #ifndef QT_NO_WHEELEVENT
1974 case QEvent::Wheel:
1975 #endif
1976 case QEvent::ContextMenu:
1977 case QEvent::DragEnter:
1978 case QEvent::DragMove:
1979 case QEvent::DragLeave:
1980 case QEvent::Drop:
1981 return QFrame::event(e);
1982 case QEvent::ChildInserted: {
1983 // we need to install an event filter on all children of the widget() to
1984 // be able to get correct stacking of children within the document.
1985 QObject *c = static_cast<QChildEvent *>(e)->child();
1986 if (c->isWidgetType()) {
1987 QWidget *w = static_cast<QWidget *>(c);
1988 // don't install the event filter on toplevels
1989 if (w->parentWidget() == widget()) {
1990 KHTMLWidget* k = dynamic_cast<KHTMLWidget*>(w);
1991 if (k && k->m_kwp->isRedirected()) {
1992 w->unsetCursor();
1993 handleWidget(w, this);
1998 default:
1999 break;
2001 return false;
2004 DOM::NodeImpl *KHTMLView::nodeUnderMouse() const
2006 return d->underMouse;
2009 DOM::NodeImpl *KHTMLView::nonSharedNodeUnderMouse() const
2011 return d->underMouseNonShared;
2014 bool KHTMLView::scrollTo(const QRect &bounds)
2016 d->scrollingSelf = true; // so scroll events get ignored
2018 int x, y, xe, ye;
2019 x = bounds.left();
2020 y = bounds.top();
2021 xe = bounds.right();
2022 ye = bounds.bottom();
2024 //kDebug(6000)<<"scrolling coords: x="<<x<<" y="<<y<<" width="<<xe-x<<" height="<<ye-y<<endl;
2026 int deltax;
2027 int deltay;
2029 int curHeight = visibleHeight();
2030 int curWidth = visibleWidth();
2032 if (ye-y>curHeight-d->borderY)
2033 ye = y + curHeight - d->borderY;
2035 if (xe-x>curWidth-d->borderX)
2036 xe = x + curWidth - d->borderX;
2038 // is xpos of target left of the view's border?
2039 if (x < contentsX() + d->borderX )
2040 deltax = x - contentsX() - d->borderX;
2041 // is xpos of target right of the view's right border?
2042 else if (xe + d->borderX > contentsX() + curWidth)
2043 deltax = xe + d->borderX - ( contentsX() + curWidth );
2044 else
2045 deltax = 0;
2047 // is ypos of target above upper border?
2048 if (y < contentsY() + d->borderY)
2049 deltay = y - contentsY() - d->borderY;
2050 // is ypos of target below lower border?
2051 else if (ye + d->borderY > contentsY() + curHeight)
2052 deltay = ye + d->borderY - ( contentsY() + curHeight );
2053 else
2054 deltay = 0;
2056 int maxx = curWidth-d->borderX;
2057 int maxy = curHeight-d->borderY;
2059 int scrollX, scrollY;
2061 scrollX = deltax > 0 ? (deltax > maxx ? maxx : deltax) : deltax == 0 ? 0 : (deltax>-maxx ? deltax : -maxx);
2062 scrollY = deltay > 0 ? (deltay > maxy ? maxy : deltay) : deltay == 0 ? 0 : (deltay>-maxy ? deltay : -maxy);
2064 if (contentsX() + scrollX < 0)
2065 scrollX = -contentsX();
2066 else if (contentsWidth() - visibleWidth() - contentsX() < scrollX)
2067 scrollX = contentsWidth() - visibleWidth() - contentsX();
2069 if (contentsY() + scrollY < 0)
2070 scrollY = -contentsY();
2071 else if (contentsHeight() - visibleHeight() - contentsY() < scrollY)
2072 scrollY = contentsHeight() - visibleHeight() - contentsY();
2074 horizontalScrollBar()->setValue( horizontalScrollBar()->value()+scrollX );
2075 verticalScrollBar()->setValue( verticalScrollBar()->value()+scrollY );
2077 d->scrollingSelf = false;
2079 if ( (abs(deltax)<=maxx) && (abs(deltay)<=maxy) )
2080 return true;
2081 else return false;
2085 bool KHTMLView::focusNextPrevNode(bool next)
2087 // Sets the focus node of the document to be the node after (or if
2088 // next is false, before) the current focus node. Only nodes that
2089 // are selectable (i.e. for which isFocusable() returns true) are
2090 // taken into account, and the order used is that specified in the
2091 // HTML spec (see DocumentImpl::nextFocusNode() and
2092 // DocumentImpl::previousFocusNode() for details).
2094 DocumentImpl *doc = m_part->xmlDocImpl();
2095 NodeImpl *oldFocusNode = doc->focusNode();
2097 // See whether we're in the middle of detach. If so, we want to
2098 // clear focus... The document code will be careful to not
2099 // emit events in that case..
2100 if (oldFocusNode && oldFocusNode->renderer() &&
2101 !oldFocusNode->renderer()->parent()) {
2102 doc->setFocusNode(0);
2103 return true;
2106 #if 1
2107 // If the user has scrolled the document, then instead of picking
2108 // the next focusable node in the document, use the first one that
2109 // is within the visible area (if possible).
2110 if (d->scrollBarMoved)
2112 NodeImpl *toFocus;
2113 if (next)
2114 toFocus = doc->nextFocusNode(oldFocusNode);
2115 else
2116 toFocus = doc->previousFocusNode(oldFocusNode);
2118 if (!toFocus && oldFocusNode)
2119 if (next)
2120 toFocus = doc->nextFocusNode(NULL);
2121 else
2122 toFocus = doc->previousFocusNode(NULL);
2124 while (toFocus && toFocus != oldFocusNode)
2127 QRect focusNodeRect = toFocus->getRect();
2128 if ((focusNodeRect.left() > contentsX()) && (focusNodeRect.right() < contentsX() + visibleWidth()) &&
2129 (focusNodeRect.top() > contentsY()) && (focusNodeRect.bottom() < contentsY() + visibleHeight())) {
2131 QRect r = toFocus->getRect();
2132 ensureVisible( r.right(), r.bottom());
2133 ensureVisible( r.left(), r.top());
2134 d->scrollBarMoved = false;
2135 d->tabMovePending = false;
2136 d->lastTabbingDirection = next;
2137 d->pseudoFocusNode = KHTMLViewPrivate::PFNone;
2138 m_part->xmlDocImpl()->setFocusNode(toFocus);
2139 Node guard(toFocus);
2140 if (!toFocus->hasOneRef() )
2142 emit m_part->nodeActivated(Node(toFocus));
2144 return true;
2147 if (next)
2148 toFocus = doc->nextFocusNode(toFocus);
2149 else
2150 toFocus = doc->previousFocusNode(toFocus);
2152 if (!toFocus && oldFocusNode)
2153 if (next)
2154 toFocus = doc->nextFocusNode(NULL);
2155 else
2156 toFocus = doc->previousFocusNode(NULL);
2159 d->scrollBarMoved = false;
2161 #endif
2163 if (!oldFocusNode && d->pseudoFocusNode == KHTMLViewPrivate::PFNone)
2165 ensureVisible(contentsX(), next?0:contentsHeight());
2166 d->scrollBarMoved = false;
2167 d->pseudoFocusNode = next?KHTMLViewPrivate::PFTop:KHTMLViewPrivate::PFBottom;
2168 return true;
2171 NodeImpl *newFocusNode = NULL;
2173 if (d->tabMovePending && next != d->lastTabbingDirection)
2175 //kDebug ( 6000 ) << " tab move pending and tabbing direction changed!\n";
2176 newFocusNode = oldFocusNode;
2178 else if (next)
2180 if (oldFocusNode || d->pseudoFocusNode == KHTMLViewPrivate::PFTop )
2181 newFocusNode = doc->nextFocusNode(oldFocusNode);
2183 else
2185 if (oldFocusNode || d->pseudoFocusNode == KHTMLViewPrivate::PFBottom )
2186 newFocusNode = doc->previousFocusNode(oldFocusNode);
2189 bool targetVisible = false;
2190 if (!newFocusNode)
2192 if ( next )
2194 targetVisible = scrollTo(QRect(contentsX()+visibleWidth()/2,contentsHeight()-d->borderY,0,0));
2196 else
2198 targetVisible = scrollTo(QRect(contentsX()+visibleWidth()/2,d->borderY,0,0));
2201 else
2203 #ifndef KHTML_NO_CARET
2204 // if it's an editable element, activate the caret
2205 if (!m_part->isCaretMode() && !m_part->isEditable()
2206 && newFocusNode->contentEditable()) {
2207 d->caretViewContext();
2208 moveCaretTo(newFocusNode, 0L, true);
2209 } else {
2210 caretOff();
2212 #endif // KHTML_NO_CARET
2214 targetVisible = scrollTo(newFocusNode->getRect());
2217 if (targetVisible)
2219 //kDebug ( 6000 ) << " target reached.\n";
2220 d->tabMovePending = false;
2222 m_part->xmlDocImpl()->setFocusNode(newFocusNode);
2223 if (newFocusNode)
2225 Node guard(newFocusNode);
2226 if (!newFocusNode->hasOneRef() )
2228 emit m_part->nodeActivated(Node(newFocusNode));
2230 return true;
2232 else
2234 d->pseudoFocusNode = next?KHTMLViewPrivate::PFBottom:KHTMLViewPrivate::PFTop;
2235 return false;
2238 else
2240 if (!d->tabMovePending)
2241 d->lastTabbingDirection = next;
2242 d->tabMovePending = true;
2243 return true;
2247 void KHTMLView::displayAccessKeys()
2249 QVector< QChar > taken;
2250 displayAccessKeys( NULL, this, taken, false );
2251 displayAccessKeys( NULL, this, taken, true );
2254 void KHTMLView::displayAccessKeys( KHTMLView* caller, KHTMLView* origview, QVector< QChar >& taken, bool use_fallbacks )
2256 QMap< ElementImpl*, QChar > fallbacks;
2257 if( use_fallbacks )
2258 fallbacks = buildFallbackAccessKeys();
2259 for( NodeImpl* n = m_part->xmlDocImpl(); n != NULL; n = n->traverseNextNode()) {
2260 if( n->isElementNode()) {
2261 ElementImpl* en = static_cast< ElementImpl* >( n );
2262 DOMString s = en->getAttribute( ATTR_ACCESSKEY );
2263 QString accesskey;
2264 if( s.length() == 1 ) {
2265 QChar a = s.string()[ 0 ].toUpper();
2266 if( qFind( taken.begin(), taken.end(), a ) == taken.end()) // !contains
2267 accesskey = a;
2269 if( accesskey.isNull() && fallbacks.contains( en )) {
2270 QChar a = fallbacks[ en ].toUpper();
2271 if( qFind( taken.begin(), taken.end(), a ) == taken.end()) // !contains
2272 accesskey = QString( "<qt><i>" ) + a + "</i></qt>";
2274 if( !accesskey.isNull()) {
2275 QRect rec=en->getRect();
2276 QLabel *lab=new QLabel(accesskey,viewport(),Qt::WDestructiveClose);
2277 connect( origview, SIGNAL(hideAccessKeys()), lab, SLOT(close()) );
2278 connect( this, SIGNAL(repaintAccessKeys()), lab, SLOT(repaint()));
2279 lab->setPalette(QToolTip::palette());
2280 lab->setLineWidth(2);
2281 lab->setFrameStyle(QFrame::Box | QFrame::Plain);
2282 lab->setMargin(3);
2283 lab->adjustSize();
2284 lab->setParent( widget() );
2285 lab->move(
2286 qMin(rec.left()+rec.width()/2, contentsWidth() - lab->width()),
2287 qMin(rec.top()+rec.height()/2, contentsHeight() - lab->height()));
2288 lab->show();
2289 taken.append( accesskey[ 0 ] );
2293 if( use_fallbacks )
2294 return;
2296 QList<KParts::ReadOnlyPart*> frames = m_part->frames();
2297 foreach( KParts::ReadOnlyPart* cur, frames ) {
2298 if( !qobject_cast<KHTMLPart*>(cur) )
2299 continue;
2300 KHTMLPart* part = static_cast< KHTMLPart* >( cur );
2301 if( part->view() && part->view() != caller )
2302 part->view()->displayAccessKeys( this, origview, taken, use_fallbacks );
2305 // pass up to the parent
2306 if (m_part->parentPart() && m_part->parentPart()->view()
2307 && m_part->parentPart()->view() != caller)
2308 m_part->parentPart()->view()->displayAccessKeys( this, origview, taken, use_fallbacks );
2313 void KHTMLView::accessKeysTimeout()
2315 d->accessKeysActivated=false;
2316 d->accessKeysPreActivate = false;
2317 m_part->setStatusBarText(QString(), KHTMLPart::BarOverrideText);
2318 emit hideAccessKeys();
2321 // Handling of the HTML accesskey attribute.
2322 bool KHTMLView::handleAccessKey( const QKeyEvent* ev )
2324 // Qt interprets the keyevent also with the modifiers, and ev->text() matches that,
2325 // but this code must act as if the modifiers weren't pressed
2326 QChar c;
2327 if( ev->key() >= Qt::Key_A && ev->key() <= Qt::Key_Z )
2328 c = 'A' + ev->key() - Qt::Key_A;
2329 else if( ev->key() >= Qt::Key_0 && ev->key() <= Qt::Key_9 )
2330 c = '0' + ev->key() - Qt::Key_0;
2331 else {
2332 // TODO fake XKeyEvent and XLookupString ?
2333 // This below seems to work e.g. for eacute though.
2334 if( ev->text().length() == 1 )
2335 c = ev->text()[ 0 ];
2337 if( c.isNull())
2338 return false;
2339 return focusNodeWithAccessKey( c );
2342 bool KHTMLView::focusNodeWithAccessKey( QChar c, KHTMLView* caller )
2344 DocumentImpl *doc = m_part->xmlDocImpl();
2345 if( !doc )
2346 return false;
2347 ElementImpl* node = doc->findAccessKeyElement( c );
2348 if( !node ) {
2349 QList<KParts::ReadOnlyPart*> frames = m_part->frames();
2350 foreach( KParts::ReadOnlyPart* cur, frames ) {
2351 if( !qobject_cast<KHTMLPart*>(cur) )
2352 continue;
2353 KHTMLPart* part = static_cast< KHTMLPart* >( cur );
2354 if( part->view() && part->view() != caller
2355 && part->view()->focusNodeWithAccessKey( c, this ))
2356 return true;
2358 // pass up to the parent
2359 if (m_part->parentPart() && m_part->parentPart()->view()
2360 && m_part->parentPart()->view() != caller
2361 && m_part->parentPart()->view()->focusNodeWithAccessKey( c, this ))
2362 return true;
2363 if( caller == NULL ) { // the active frame (where the accesskey was pressed)
2364 QMap< ElementImpl*, QChar > fallbacks = buildFallbackAccessKeys();
2365 for( QMap< ElementImpl*, QChar >::ConstIterator it = fallbacks.begin();
2366 it != fallbacks.end();
2367 ++it )
2368 if( *it == c ) {
2369 node = it.key();
2370 break;
2373 if( node == NULL )
2374 return false;
2377 // Scroll the view as necessary to ensure that the new focus node is visible
2378 #ifndef KHTML_NO_CARET
2379 // if it's an editable element, activate the caret
2380 if (!m_part->isCaretMode() && !m_part->isEditable()
2381 && node->contentEditable()) {
2382 d->caretViewContext();
2383 moveCaretTo(node, 0L, true);
2384 } else {
2385 caretOff();
2387 #endif // KHTML_NO_CARET
2389 QRect r = node->getRect();
2390 ensureVisible( r.right(), r.bottom());
2391 ensureVisible( r.left(), r.top());
2393 Node guard( node );
2394 if( node->isFocusable()) {
2395 if (node->id()==ID_LABEL) {
2396 // if Accesskey is a label, give focus to the label's referrer.
2397 node=static_cast<ElementImpl *>(static_cast< HTMLLabelElementImpl* >( node )->getFormElement());
2398 if (!node) return true;
2399 guard = node;
2401 // Set focus node on the document
2402 #ifdef __GNUC__
2403 #warning "port QFocusEvent::setReason( QFocusEvent::Shortcut ); to qt4"
2404 #endif
2405 //QFocusEvent::setReason( QFocusEvent::Shortcut );
2406 m_part->xmlDocImpl()->setFocusNode(node);
2407 #ifdef __GNUC__
2408 #warning "port QFocusEvent::resetReason(); to qt4"
2409 #endif
2410 //QFocusEvent::resetReason();
2411 if( node != NULL && node->hasOneRef()) // deleted, only held by guard
2412 return true;
2413 emit m_part->nodeActivated(Node(node));
2414 if( node != NULL && node->hasOneRef())
2415 return true;
2418 switch( node->id()) {
2419 case ID_A:
2420 static_cast< HTMLAnchorElementImpl* >( node )->click();
2421 break;
2422 case ID_INPUT:
2423 static_cast< HTMLInputElementImpl* >( node )->click();
2424 break;
2425 case ID_BUTTON:
2426 static_cast< HTMLButtonElementImpl* >( node )->click();
2427 break;
2428 case ID_AREA:
2429 static_cast< HTMLAreaElementImpl* >( node )->click();
2430 break;
2431 case ID_TEXTAREA:
2432 break; // just focusing it is enough
2433 case ID_LEGEND:
2434 // TODO
2435 break;
2437 return true;
2440 static QString getElementText( NodeImpl* start, bool after )
2442 QString ret; // nextSibling(), to go after e.g. </select>
2443 for( NodeImpl* n = after ? start->nextSibling() : start->traversePreviousNode();
2444 n != NULL;
2445 n = after ? n->traverseNextNode() : n->traversePreviousNode()) {
2446 if( n->isTextNode()) {
2447 if( after )
2448 ret += static_cast< TextImpl* >( n )->toString().string();
2449 else
2450 ret.prepend( static_cast< TextImpl* >( n )->toString().string());
2451 } else {
2452 switch( n->id()) {
2453 case ID_A:
2454 case ID_FONT:
2455 case ID_TT:
2456 case ID_U:
2457 case ID_B:
2458 case ID_I:
2459 case ID_S:
2460 case ID_STRIKE:
2461 case ID_BIG:
2462 case ID_SMALL:
2463 case ID_EM:
2464 case ID_STRONG:
2465 case ID_DFN:
2466 case ID_CODE:
2467 case ID_SAMP:
2468 case ID_KBD:
2469 case ID_VAR:
2470 case ID_CITE:
2471 case ID_ABBR:
2472 case ID_ACRONYM:
2473 case ID_SUB:
2474 case ID_SUP:
2475 case ID_SPAN:
2476 case ID_NOBR:
2477 case ID_WBR:
2478 break;
2479 case ID_TD:
2480 if( ret.trimmed().isEmpty())
2481 break;
2482 // fall through
2483 default:
2484 return ret.simplified();
2488 return ret.simplified();
2491 static QMap< NodeImpl*, QString > buildLabels( NodeImpl* start )
2493 QMap< NodeImpl*, QString > ret;
2494 for( NodeImpl* n = start;
2495 n != NULL;
2496 n = n->traverseNextNode()) {
2497 if( n->id() == ID_LABEL ) {
2498 HTMLLabelElementImpl* label = static_cast< HTMLLabelElementImpl* >( n );
2499 NodeImpl* labelfor = label->getFormElement();
2500 if( labelfor )
2501 ret[ labelfor ] = label->innerText().string().simplified();
2504 return ret;
2507 namespace khtml {
2508 struct AccessKeyData {
2509 ElementImpl* element;
2510 QString text;
2511 QString url;
2512 int priority; // 10(highest) - 0(lowest)
2516 QMap< ElementImpl*, QChar > KHTMLView::buildFallbackAccessKeys() const
2518 // build a list of all possible candidate elements that could use an accesskey
2519 QList< AccessKeyData > data;
2520 QMap< NodeImpl*, QString > labels = buildLabels( m_part->xmlDocImpl());
2521 for( NodeImpl* n = m_part->xmlDocImpl();
2522 n != NULL;
2523 n = n->traverseNextNode()) {
2524 if( n->isElementNode()) {
2525 ElementImpl* element = static_cast< ElementImpl* >( n );
2526 if( element->getAttribute( ATTR_ACCESSKEY ).length() == 1 )
2527 continue; // has accesskey set, ignore
2528 if( element->renderer() == NULL )
2529 continue; // not visible
2530 QString text;
2531 QString url;
2532 int priority = 0;
2533 bool ignore = false;
2534 bool text_after = false;
2535 bool text_before = false;
2536 switch( element->id()) {
2537 case ID_A:
2538 url = khtml::parseURL(element->getAttribute(ATTR_HREF)).string();
2539 if( url.isEmpty()) // doesn't have href, it's only an anchor
2540 continue;
2541 text = static_cast< HTMLElementImpl* >( element )->innerText().string().simplified();
2542 priority = 2;
2543 break;
2544 case ID_INPUT: {
2545 HTMLInputElementImpl* in = static_cast< HTMLInputElementImpl* >( element );
2546 switch( in->inputType()) {
2547 case HTMLInputElementImpl::SUBMIT:
2548 text = in->value().string();
2549 if( text.isEmpty())
2550 text = i18n( "Submit" );
2551 priority = 7;
2552 break;
2553 case HTMLInputElementImpl::IMAGE:
2554 text = in->altText().string();
2555 priority = 7;
2556 break;
2557 case HTMLInputElementImpl::BUTTON:
2558 text = in->value().string();
2559 priority = 5;
2560 break;
2561 case HTMLInputElementImpl::RESET:
2562 text = in->value().string();
2563 if( text.isEmpty())
2564 text = i18n( "Reset" );
2565 priority = 5;
2566 break;
2567 case HTMLInputElementImpl::HIDDEN:
2568 ignore = true;
2569 break;
2570 case HTMLInputElementImpl::CHECKBOX:
2571 case HTMLInputElementImpl::RADIO:
2572 text_after = true;
2573 priority = 5;
2574 break;
2575 case HTMLInputElementImpl::TEXT:
2576 case HTMLInputElementImpl::PASSWORD:
2577 case HTMLInputElementImpl::FILE:
2578 text_before = true;
2579 priority = 5;
2580 break;
2581 default:
2582 priority = 5;
2583 break;
2585 break;
2587 case ID_BUTTON:
2588 text = static_cast< HTMLElementImpl* >( element )->innerText().string().simplified();
2589 switch( static_cast< HTMLButtonElementImpl* >( element )->buttonType()) {
2590 case HTMLButtonElementImpl::SUBMIT:
2591 if( text.isEmpty())
2592 text = i18n( "Submit" );
2593 priority = 7;
2594 break;
2595 case HTMLButtonElementImpl::RESET:
2596 if( text.isEmpty())
2597 text = i18n( "Reset" );
2598 priority = 5;
2599 break;
2600 default:
2601 priority = 5;
2602 break;
2603 break;
2605 case ID_SELECT: // these don't have accesskey attribute, but quick access may be handy
2606 text_before = true;
2607 text_after = true;
2608 priority = 5;
2609 break;
2610 case ID_FRAME:
2611 ignore = true;
2612 break;
2613 default:
2614 ignore = !element->isFocusable();
2615 priority = 2;
2616 break;
2618 if( ignore )
2619 continue;
2620 if( text.isNull() && labels.contains( element ))
2621 text = labels[ element ];
2622 if( text.isNull() && text_before )
2623 text = getElementText( element, false );
2624 if( text.isNull() && text_after )
2625 text = getElementText( element, true );
2626 text = text.trimmed();
2627 // increase priority of items which have explicitly specified accesskeys in the config
2628 QList< QPair< QString, QChar > > priorities
2629 = m_part->settings()->fallbackAccessKeysAssignments();
2630 for( QList< QPair< QString, QChar > >::ConstIterator it = priorities.begin();
2631 it != priorities.end();
2632 ++it ) {
2633 if( text == (*it).first )
2634 priority = 10;
2636 AccessKeyData tmp = { element, text, url, priority };
2637 data.append( tmp );
2641 QList< QChar > keys;
2642 for( char c = 'A'; c <= 'Z'; ++c )
2643 keys << c;
2644 for( char c = '0'; c <= '9'; ++c )
2645 keys << c;
2646 for( NodeImpl* n = m_part->xmlDocImpl();
2647 n != NULL;
2648 n = n->traverseNextNode()) {
2649 if( n->isElementNode()) {
2650 ElementImpl* en = static_cast< ElementImpl* >( n );
2651 DOMString s = en->getAttribute( ATTR_ACCESSKEY );
2652 if( s.length() == 1 ) {
2653 QChar c = s.string()[ 0 ].toUpper();
2654 keys.removeAll( c ); // remove manually assigned accesskeys
2659 QMap< ElementImpl*, QChar > ret;
2660 for( int priority = 10;
2661 priority >= 0;
2662 --priority ) {
2663 for( QList< AccessKeyData >::Iterator it = data.begin();
2664 it != data.end();
2666 if( (*it).priority != priority ) {
2667 ++it;
2668 continue;
2670 if( keys.isEmpty())
2671 break;
2672 QString text = (*it).text;
2673 QChar key;
2674 if( key.isNull() && !text.isEmpty()) {
2675 QList< QPair< QString, QChar > > priorities
2676 = m_part->settings()->fallbackAccessKeysAssignments();
2677 for( QList< QPair< QString, QChar > >::ConstIterator it = priorities.begin();
2678 it != priorities.end();
2679 ++it )
2680 if( text == (*it).first && keys.contains( (*it).second )) {
2681 key = (*it).second;
2682 break;
2685 // try first to select the first character as the accesskey,
2686 // then first character of the following words,
2687 // and then simply the first free character
2688 if( key.isNull() && !text.isEmpty()) {
2689 QStringList words = text.split( ' ' );
2690 for( QStringList::ConstIterator it = words.begin();
2691 it != words.end();
2692 ++it ) {
2693 if( keys.contains( (*it)[ 0 ].toUpper())) {
2694 key = (*it)[ 0 ].toUpper();
2695 break;
2699 if( key.isNull() && !text.isEmpty()) {
2700 for( int i = 0; i < text.length(); ++i ) {
2701 if( keys.contains( text[ i ].toUpper())) {
2702 key = text[ i ].toUpper();
2703 break;
2707 if( key.isNull())
2708 key = keys.front();
2709 ret[ (*it).element ] = key;
2710 keys.removeAll( key );
2711 QString url = (*it).url;
2712 it = data.erase( it );
2713 // assign the same accesskey also to other elements pointing to the same url
2714 if( !url.isEmpty() && !url.startsWith( "javascript:", Qt::CaseInsensitive )) {
2715 for( QList< AccessKeyData >::Iterator it2 = data.begin();
2716 it2 != data.end();
2718 if( (*it2).url == url ) {
2719 ret[ (*it2).element ] = key;
2720 if( it == it2 )
2721 ++it;
2722 it2 = data.erase( it2 );
2723 } else
2724 ++it2;
2729 return ret;
2732 void KHTMLView::setMediaType( const QString &medium )
2734 m_medium = medium;
2737 QString KHTMLView::mediaType() const
2739 return m_medium;
2742 bool KHTMLView::pagedMode() const
2744 return d->paged;
2747 void KHTMLView::setWidgetVisible(RenderWidget* w, bool vis)
2749 if (vis) {
2750 d->visibleWidgets.replace(w, w->widget());
2752 else
2753 d->visibleWidgets.remove(w);
2756 bool KHTMLView::needsFullRepaint() const
2758 return d->needsFullRepaint;
2761 void KHTMLView::print(bool quick)
2763 if(!m_part->xmlDocImpl()) return;
2764 khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer());
2765 if(!root) return;
2767 KPrinter *printer = new KPrinter(true, QPrinter::ScreenResolution);
2768 printer->addDialogPage(new KHTMLPrintSettings());
2769 QString docname = m_part->xmlDocImpl()->URL().prettyUrl();
2770 if ( !docname.isEmpty() )
2771 docname = KStringHandler::csqueeze(docname, 80);
2772 if(quick || printer->setup(this, i18n("Print %1", docname))) {
2773 viewport()->setCursor( Qt::WaitCursor ); // only viewport(), no QApplication::, otherwise we get the busy cursor in kdeprint's dialogs
2774 // set up KPrinter
2775 printer->setFullPage(false);
2776 printer->setCreator(QString("KDE %1.%2.%3 HTML Library").arg(KDE_VERSION_MAJOR).arg(KDE_VERSION_MINOR).arg(KDE_VERSION_RELEASE));
2777 printer->setDocName(docname);
2779 QPainter *p = new QPainter;
2780 p->begin( printer );
2781 khtml::setPrintPainter( p );
2783 m_part->xmlDocImpl()->setPaintDevice( printer );
2784 QString oldMediaType = mediaType();
2785 setMediaType( "print" );
2786 // We ignore margin settings for html and body when printing
2787 // and use the default margins from the print-system
2788 // (In Qt 3.0.x the default margins are hardcoded in Qt)
2789 m_part->xmlDocImpl()->setPrintStyleSheet( printer->option("app-khtml-printfriendly") == "true" ?
2790 "* { background-image: none !important;"
2791 " background-color: white !important;"
2792 " color: black !important; }"
2793 "body { margin: 0px !important; }"
2794 "html { margin: 0px !important; }" :
2795 "body { margin: 0px !important; }"
2796 "html { margin: 0px !important; }"
2799 Q3PaintDeviceMetrics metrics( printer );
2801 kDebug(6000) << "printing: physical page width = " << metrics.width()
2802 << " height = " << metrics.height() << endl;
2803 root->setStaticMode(true);
2804 root->setPagedMode(true);
2805 root->setWidth(metrics.width());
2806 // root->setHeight(metrics.height());
2807 root->setPageTop(0);
2808 root->setPageBottom(0);
2809 d->paged = true;
2811 m_part->xmlDocImpl()->styleSelector()->computeFontSizes(&metrics, 100);
2812 m_part->xmlDocImpl()->updateStyleSelector();
2813 root->setPrintImages( printer->option("app-khtml-printimages") == "true");
2814 root->makePageBreakAvoidBlocks();
2816 root->setNeedsLayoutAndMinMaxRecalc();
2817 root->layout();
2818 khtml::RenderWidget::flushWidgetResizes(); // make sure widgets have their final size
2820 // check sizes ask for action.. (scale or clip)
2822 bool printHeader = (printer->option("app-khtml-printheader") == "true");
2824 int headerHeight = 0;
2825 QFont headerFont("Sans Serif", 8);
2827 QString headerLeft = KGlobal::locale()->formatDate(QDate::currentDate(),true);
2828 QString headerMid = docname;
2829 QString headerRight;
2831 if (printHeader)
2833 p->setFont(headerFont);
2834 headerHeight = (p->fontMetrics().lineSpacing() * 3) / 2;
2837 // ok. now print the pages.
2838 kDebug(6000) << "printing: html page width = " << root->docWidth()
2839 << " height = " << root->docHeight() << endl;
2840 kDebug(6000) << "printing: margins left = " << printer->margins().width()
2841 << " top = " << printer->margins().height() << endl;
2842 kDebug(6000) << "printing: paper width = " << metrics.width()
2843 << " height = " << metrics.height() << endl;
2844 // if the width is too large to fit on the paper we just scale
2845 // the whole thing.
2846 int pageWidth = metrics.width();
2847 int pageHeight = metrics.height();
2848 p->setClipRect(0,0, pageWidth, pageHeight);
2850 pageHeight -= headerHeight;
2852 bool scalePage = false;
2853 double scale = 0.0;
2854 #ifndef QT_NO_TRANSFORMATIONS
2855 if(root->docWidth() > metrics.width()) {
2856 scalePage = true;
2857 scale = ((double) metrics.width())/((double) root->docWidth());
2858 pageHeight = (int) (pageHeight/scale);
2859 pageWidth = (int) (pageWidth/scale);
2860 headerHeight = (int) (headerHeight/scale);
2862 #endif
2863 kDebug(6000) << "printing: scaled html width = " << pageWidth
2864 << " height = " << pageHeight << endl;
2866 root->setHeight(pageHeight);
2867 root->setPageBottom(pageHeight);
2868 root->setNeedsLayout(true);
2869 root->layoutIfNeeded();
2870 // m_part->slotDebugRenderTree();
2872 // Squeeze header to make it it on the page.
2873 if (printHeader)
2875 int available_width = metrics.width() - 10 -
2876 2 * qMax(p->boundingRect(0, 0, metrics.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerLeft).width(),
2877 p->boundingRect(0, 0, metrics.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerRight).width());
2878 if (available_width < 150)
2879 available_width = 150;
2880 int mid_width;
2881 int squeeze = 120;
2882 do {
2883 headerMid = KStringHandler::csqueeze(docname, squeeze);
2884 mid_width = p->boundingRect(0, 0, metrics.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerMid).width();
2885 squeeze -= 10;
2886 } while (mid_width > available_width);
2889 int top = 0;
2890 int bottom = 0;
2891 int page = 1;
2892 while(top < root->docHeight()) {
2893 if(top > 0) printer->newPage();
2894 #ifdef __GNUC__
2895 #warning "This could not be tested when merge was done, suspect"
2896 #endif
2897 p->setClipRect(0, 0, pageWidth, headerHeight);
2898 if (printHeader)
2900 int dy = p->fontMetrics().lineSpacing();
2901 p->setPen(Qt::black);
2902 p->setFont(headerFont);
2904 headerRight = QString("#%1").arg(page);
2906 p->drawText(0, 0, metrics.width(), dy, Qt::AlignLeft, headerLeft);
2907 p->drawText(0, 0, metrics.width(), dy, Qt::AlignHCenter, headerMid);
2908 p->drawText(0, 0, metrics.width(), dy, Qt::AlignRight, headerRight);
2912 #ifndef QT_NO_TRANSFORMATIONS
2913 if (scalePage)
2914 p->scale(scale, scale);
2915 #endif
2917 #ifdef __GNUC__
2918 #warning "This could not be tested when merge was done, suspect"
2919 #endif
2920 p->setClipRect(0, (int)(headerHeight/scale), (int)(pageWidth/scale), (int)(pageHeight/scale));
2921 p->translate(0, headerHeight-top);
2923 bottom = top+pageHeight;
2925 root->setPageTop(top);
2926 root->setPageBottom(bottom);
2927 root->setPageNumber(page);
2929 root->layer()->paint(p, QRect(0, top, pageWidth, pageHeight));
2930 // m_part->xmlDocImpl()->renderer()->layer()->paint(p, QRect(0, top, pageWidth, pageHeight));
2931 // root->repaint();
2932 // p->flush();
2933 kDebug(6000) << "printed: page " << page <<" bottom At = " << bottom << endl;
2935 top = bottom;
2936 p->resetMatrix();
2937 page++;
2940 p->end();
2941 delete p;
2943 // and now reset the layout to the usual one...
2944 root->setPagedMode(false);
2945 root->setStaticMode(false);
2946 d->paged = false;
2947 khtml::setPrintPainter( 0 );
2948 setMediaType( oldMediaType );
2949 m_part->xmlDocImpl()->setPaintDevice( this );
2950 m_part->xmlDocImpl()->styleSelector()->computeFontSizes(m_part->xmlDocImpl()->paintDeviceMetrics(), m_part->zoomFactor());
2951 m_part->xmlDocImpl()->updateStyleSelector();
2952 viewport()->unsetCursor();
2954 delete printer;
2957 void KHTMLView::slotPaletteChanged()
2959 if(!m_part->xmlDocImpl()) return;
2960 DOM::DocumentImpl *document = m_part->xmlDocImpl();
2961 if (!document->isHTMLDocument()) return;
2962 khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(document->renderer());
2963 if(!root) return;
2964 root->style()->resetPalette();
2965 NodeImpl *body = static_cast<HTMLDocumentImpl*>(document)->body();
2966 if(!body) return;
2967 body->setChanged(true);
2968 body->recalcStyle( NodeImpl::Force );
2971 void KHTMLView::paint(QPainter *p, const QRect &rc, int yOff, bool *more)
2973 if(!m_part->xmlDocImpl()) return;
2974 khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer());
2975 if(!root) return;
2977 m_part->xmlDocImpl()->setPaintDevice(p->device());
2978 root->setPagedMode(true);
2979 root->setStaticMode(true);
2980 root->setWidth(rc.width());
2982 p->save();
2983 p->setClipRect(rc);
2984 p->translate(rc.left(), rc.top());
2985 double scale = ((double) rc.width()/(double) root->docWidth());
2986 int height = (int) ((double) rc.height() / scale);
2987 #ifndef QT_NO_TRANSFORMATIONS
2988 p->scale(scale, scale);
2989 #endif
2990 root->setPageTop(yOff);
2991 root->setPageBottom(yOff+height);
2993 root->layer()->paint(p, QRect(0, yOff, root->docWidth(), height));
2994 if (more)
2995 *more = yOff + height < root->docHeight();
2996 p->restore();
2998 root->setPagedMode(false);
2999 root->setStaticMode(false);
3000 m_part->xmlDocImpl()->setPaintDevice( this );
3004 void KHTMLView::useSlowRepaints()
3006 d->useSlowRepaints = true;
3007 // setStaticBackground(true); ### ?? FIXME
3011 void KHTMLView::setVerticalScrollBarPolicy( Qt::ScrollBarPolicy policy )
3013 #ifndef KHTML_NO_SCROLLBARS
3014 d->vpolicy = policy;
3015 QScrollArea::setVerticalScrollBarPolicy(policy);
3016 #else
3017 Q_UNUSED( policy );
3018 #endif
3021 void KHTMLView::setHorizontalScrollBarPolicy( Qt::ScrollBarPolicy policy )
3023 #ifndef KHTML_NO_SCROLLBARS
3024 d->hpolicy = policy;
3025 QScrollArea::setHorizontalScrollBarPolicy(policy);
3026 #else
3027 Q_UNUSED( policy );
3028 #endif
3031 void KHTMLView::restoreScrollBar()
3033 int ow = visibleWidth();
3034 QScrollArea::setVerticalScrollBarPolicy(d->vpolicy);
3035 if (visibleWidth() != ow)
3036 layout();
3037 d->prevScrollbarVisible = verticalScrollBar()->isVisible();
3040 QStringList KHTMLView::formCompletionItems(const QString &name) const
3042 if (!m_part->settings()->isFormCompletionEnabled())
3043 return QStringList();
3044 if (!d->formCompletions)
3045 d->formCompletions = new KSimpleConfig(KStandardDirs::locateLocal("data", "khtml/formcompletions"));
3046 return d->formCompletions->readEntry(name, QStringList());
3049 void KHTMLView::clearCompletionHistory(const QString& name)
3051 if (!d->formCompletions)
3053 d->formCompletions = new KSimpleConfig(KStandardDirs::locateLocal("data", "khtml/formcompletions"));
3055 d->formCompletions->writeEntry(name, "");
3056 d->formCompletions->sync();
3059 void KHTMLView::addFormCompletionItem(const QString &name, const QString &value)
3061 if (!m_part->settings()->isFormCompletionEnabled())
3062 return;
3063 // don't store values that are all numbers or just numbers with
3064 // dashes or spaces as those are likely credit card numbers or
3065 // something similar
3066 bool cc_number(true);
3067 for ( int i = 0; i < value.length(); ++i)
3069 QChar c(value[i]);
3070 if (!c.isNumber() && c != '-' && !c.isSpace())
3072 cc_number = false;
3073 break;
3076 if (cc_number)
3077 return;
3078 QStringList items = formCompletionItems(name);
3079 if (!items.contains(value))
3080 items.prepend(value);
3081 while ((int)items.count() > m_part->settings()->maxFormCompletionItems())
3082 items.erase(items.isEmpty() ? items.end() : --items.end());
3083 d->formCompletions->writeEntry(name, items);
3086 void KHTMLView::addNonPasswordStorableSite(const QString& host)
3088 if (!d->formCompletions) {
3089 d->formCompletions = new KSimpleConfig(KStandardDirs::locateLocal("data", "khtml/formcompletions"));
3092 d->formCompletions->setGroup("NonPasswordStorableSites");
3093 QStringList sites = d->formCompletions->readEntry("Sites", QStringList());
3094 sites.append(host);
3095 d->formCompletions->writeEntry("Sites", sites);
3096 d->formCompletions->sync();
3097 d->formCompletions->setGroup(QString());//reset
3100 bool KHTMLView::nonPasswordStorableSite(const QString& host) const
3102 if (!d->formCompletions) {
3103 d->formCompletions = new KSimpleConfig(KStandardDirs::locateLocal("data", "khtml/formcompletions"));
3105 d->formCompletions->setGroup("NonPasswordStorableSites");
3106 QStringList sites = d->formCompletions->readEntry("Sites", QStringList());
3107 d->formCompletions->setGroup(QString());//reset
3109 return (sites.indexOf(host) != -1);
3112 // returns true if event should be swallowed
3113 bool KHTMLView::dispatchMouseEvent(int eventId, DOM::NodeImpl *targetNode,
3114 DOM::NodeImpl *targetNodeNonShared, bool cancelable,
3115 int detail,QMouseEvent *_mouse, bool setUnder,
3116 int mouseEventType, int orient)
3118 // if the target node is a text node, dispatch on the parent node - rdar://4196646 (and #76948)
3119 if (targetNode && targetNode->isTextNode())
3120 targetNode = targetNode->parentNode();
3122 if (d->underMouse)
3123 d->underMouse->deref();
3124 d->underMouse = targetNode;
3125 if (d->underMouse)
3126 d->underMouse->ref();
3128 if (d->underMouseNonShared)
3129 d->underMouseNonShared->deref();
3130 d->underMouseNonShared = targetNodeNonShared;
3131 if (d->underMouseNonShared)
3132 d->underMouseNonShared->ref();
3134 bool isWheelEvent = (mouseEventType == DOM::NodeImpl::MouseWheel);
3136 int exceptioncode = 0;
3137 int pageX = _mouse->x();
3138 int pageY = _mouse->y();
3139 int clientX = pageX - contentsX();
3140 int clientY = pageY - contentsY();
3141 int screenX = _mouse->globalX();
3142 int screenY = _mouse->globalY();
3143 int button = -1;
3144 switch (_mouse->button()) {
3145 case Qt::LeftButton:
3146 button = 0;
3147 break;
3148 case Qt::MidButton:
3149 button = 1;
3150 break;
3151 case Qt::RightButton:
3152 button = 2;
3153 break;
3154 default:
3155 break;
3157 if (d->accessKeysEnabled && d->accessKeysPreActivate && button!=-1)
3158 d->accessKeysPreActivate=false;
3160 bool ctrlKey = (_mouse->modifiers() & Qt::ControlModifier);
3161 bool altKey = (_mouse->modifiers() & Qt::AltModifier);
3162 bool shiftKey = (_mouse->modifiers() & Qt::ShiftModifier);
3163 bool metaKey = (_mouse->modifiers() & Qt::MetaModifier);
3165 // mouseout/mouseover
3166 if (setUnder && d->oldUnderMouse != targetNode) {
3167 if (d->oldUnderMouse && d->oldUnderMouse->getDocument() != targetNode->getDocument()) {
3168 d->oldUnderMouse->deref();
3169 d->oldUnderMouse = 0;
3171 // send mouseout event to the old node
3172 if (d->oldUnderMouse) {
3173 // send mouseout event to the old node
3174 MouseEventImpl *me = new MouseEventImpl(EventImpl::MOUSEOUT_EVENT,
3175 true,true,m_part->xmlDocImpl()->defaultView(),
3176 0,screenX,screenY,clientX,clientY,pageX, pageY,
3177 ctrlKey,altKey,shiftKey,metaKey,
3178 button,targetNode);
3179 me->ref();
3180 d->oldUnderMouse->dispatchEvent(me,exceptioncode,true);
3181 me->deref();
3183 // send mouseover event to the new node
3184 if (targetNode) {
3185 MouseEventImpl *me = new MouseEventImpl(EventImpl::MOUSEOVER_EVENT,
3186 true,true,m_part->xmlDocImpl()->defaultView(),
3187 0,screenX,screenY,clientX,clientY,pageX, pageY,
3188 ctrlKey,altKey,shiftKey,metaKey,
3189 button,d->oldUnderMouse);
3191 me->ref();
3192 targetNode->dispatchEvent(me,exceptioncode,true);
3193 me->deref();
3195 if (d->oldUnderMouse)
3196 d->oldUnderMouse->deref();
3197 d->oldUnderMouse = targetNode;
3198 d->oldUnderMouse->ref();
3201 bool swallowEvent = false;
3203 if (targetNode) {
3204 // send the actual event
3205 bool dblclick = ( eventId == EventImpl::CLICK_EVENT &&
3206 _mouse->type() == QEvent::MouseButtonDblClick );
3207 MouseEventImpl *me = new MouseEventImpl(static_cast<EventImpl::EventId>(eventId),
3208 true,cancelable,m_part->xmlDocImpl()->defaultView(),
3209 detail,screenX,screenY,clientX,clientY,pageX, pageY,
3210 ctrlKey,altKey,shiftKey,metaKey,
3211 button,0, isWheelEvent ? 0 : _mouse, dblclick,
3212 isWheelEvent ? static_cast<MouseEventImpl::Orientation>(orient) : MouseEventImpl::ONone );
3213 me->ref();
3214 if ( !d->m_mouseEventsTarget && RenderLayer::gScrollBar && eventId == EventImpl::MOUSEDOWN_EVENT )
3215 // button is pressed inside a layer scrollbar, so make it the target for future mousemove events until released
3216 d->m_mouseEventsTarget = RenderLayer::gScrollBar;
3217 if ( d->m_mouseEventsTarget && qobject_cast<QScrollBar*>(d->m_mouseEventsTarget) &&
3218 dynamic_cast<KHTMLWidget*>(static_cast<QWidget*>(d->m_mouseEventsTarget)) ) {
3219 // we have a sticky mouse event target and it is a layer's scrollbar. Forward events manually.
3220 // ### should use the dom
3221 KHTMLWidget*w = dynamic_cast<KHTMLWidget*>(static_cast<QWidget*>(d->m_mouseEventsTarget));
3222 QPoint p = w->m_kwp->absolutePos();
3223 QMouseEvent fw(_mouse->type(), _mouse->pos()-p, _mouse->button(), _mouse->buttons(), _mouse->modifiers());
3224 static_cast<RenderWidget::EventPropagator *>(static_cast<QWidget*>(d->m_mouseEventsTarget))->sendEvent(&fw);
3225 if (_mouse->type() == QMouseEvent::MouseButtonPress && _mouse->button() == Qt::RightButton) {
3226 QContextMenuEvent cme(QContextMenuEvent::Mouse, p);
3227 static_cast<RenderWidget::EventPropagator *>(static_cast<QWidget*>(d->m_mouseEventsTarget))->sendEvent(&cme);
3229 swallowEvent = true;
3230 } else {
3231 targetNode->dispatchEvent(me,exceptioncode,true);
3232 bool defaultHandled = me->defaultHandled();
3233 if (defaultHandled || me->defaultPrevented())
3234 swallowEvent = true;
3236 me->deref();
3238 if (eventId == EventImpl::MOUSEDOWN_EVENT) {
3239 // Focus should be shifted on mouse down, not on a click. -dwh
3240 // Blur current focus node when a link/button is clicked; this
3241 // is expected by some sites that rely on onChange handlers running
3242 // from form fields before the button click is processed.
3243 DOM::NodeImpl* nodeImpl = targetNode;
3244 for ( ; nodeImpl && !nodeImpl->isFocusable(); nodeImpl = nodeImpl->parentNode());
3245 if (nodeImpl && nodeImpl->isMouseFocusable())
3246 m_part->xmlDocImpl()->setFocusNode(nodeImpl);
3247 else if (!nodeImpl || !nodeImpl->focused())
3248 m_part->xmlDocImpl()->setFocusNode(0);
3252 return swallowEvent;
3255 void KHTMLView::setIgnoreWheelEvents( bool e )
3257 d->ignoreWheelEvents = e;
3260 #ifndef QT_NO_WHEELEVENT
3262 void KHTMLView::wheelEvent(QWheelEvent* e)
3264 if (d->accessKeysEnabled && d->accessKeysPreActivate) d->accessKeysPreActivate=false;
3266 if ( ( e->modifiers() & Qt::ControlModifier) == Qt::ControlModifier )
3268 emit zoomView( - e->delta() );
3269 e->accept();
3271 else if (d->firstRelayout)
3273 e->accept();
3275 else if( ( (e->orientation() == Qt::Vertical &&
3276 ((d->ignoreWheelEvents && !verticalScrollBar()->isVisible())
3277 || e->delta() > 0 && contentsY() <= 0
3278 || e->delta() < 0 && contentsY() >= contentsHeight() - visibleHeight()))
3280 (e->orientation() == Qt::Horizontal &&
3281 ((d->ignoreWheelEvents && !horizontalScrollBar()->isVisible())
3282 || e->delta() > 0 && contentsX() <=0
3283 || e->delta() < 0 && contentsX() >= contentsWidth() - visibleWidth())))
3284 && m_part->parentPart())
3286 if ( m_part->parentPart()->view() )
3287 m_part->parentPart()->view()->wheelEvent( e );
3288 e->ignore();
3290 else
3292 DOM::NodeImpl::MouseEvent mev( e->buttons(), DOM::NodeImpl::MouseWheel );
3293 m_part->xmlDocImpl()->prepareMouseEvent( false, e->x(), e->y(), &mev );
3295 MouseEventImpl::Orientation o = MouseEventImpl::OVertical;
3296 if (e->orientation() == Qt::Horizontal)
3297 o = MouseEventImpl::OHorizontal;
3299 QMouseEvent _mouse(QEvent::MouseMove, e->pos(), Qt::NoButton, e->buttons(), e->modifiers());
3300 bool swallow = dispatchMouseEvent(EventImpl::KHTML_MOUSEWHEEL_EVENT,mev.innerNode.handle(),mev.innerNonSharedNode.handle(),
3301 true,-e->delta()/40,&_mouse,true,DOM::NodeImpl::MouseWheel,o);
3302 if (swallow)
3303 return;
3305 d->scrollBarMoved = true;
3306 QScrollArea::wheelEvent( e );
3310 #endif
3312 void KHTMLView::dragEnterEvent( QDragEnterEvent* ev )
3314 // Handle drops onto frames (#16820)
3315 // Drops on the main html part is handled by Konqueror (and shouldn't do anything
3316 // in e.g. kmail, so not handled here).
3317 if ( m_part->parentPart() )
3319 QApplication::sendEvent(m_part->parentPart()->widget(), ev);
3320 return;
3322 QScrollArea::dragEnterEvent( ev );
3325 void KHTMLView::dropEvent( QDropEvent *ev )
3327 // Handle drops onto frames (#16820)
3328 // Drops on the main html part is handled by Konqueror (and shouldn't do anything
3329 // in e.g. kmail, so not handled here).
3330 if ( m_part->parentPart() )
3332 QApplication::sendEvent(m_part->parentPart()->widget(), ev);
3333 return;
3335 QScrollArea::dropEvent( ev );
3338 void KHTMLView::focusInEvent( QFocusEvent *e )
3340 #ifndef KHTML_NO_TYPE_AHEAD_FIND
3341 m_part->enableFindAheadActions( true );
3342 #endif
3343 DOM::NodeImpl* fn = m_part->xmlDocImpl() ? m_part->xmlDocImpl()->focusNode() : 0;
3344 if (fn && fn->renderer() && fn->renderer()->isWidget() &&
3345 (e->reason() != Qt::MouseFocusReason) &&
3346 static_cast<khtml::RenderWidget*>(fn->renderer())->widget())
3347 static_cast<khtml::RenderWidget*>(fn->renderer())->widget()->setFocus();
3348 #ifndef KHTML_NO_CARET
3349 // Restart blink frequency timer if it has been killed, but only on
3350 // editable nodes
3351 if (d->m_caretViewContext &&
3352 d->m_caretViewContext->freqTimerId == -1 &&
3353 fn) {
3354 if (m_part->isCaretMode()
3355 || m_part->isEditable()
3356 || (fn && fn->renderer()
3357 && fn->renderer()->style()->userInput()
3358 == UI_ENABLED)) {
3359 d->m_caretViewContext->freqTimerId = startTimer(500);
3360 d->m_caretViewContext->visible = true;
3361 }/*end if*/
3362 }/*end if*/
3363 showCaret();
3364 #endif // KHTML_NO_CARET
3365 QScrollArea::focusInEvent( e );
3368 void KHTMLView::focusOutEvent( QFocusEvent *e )
3370 m_part->stopAutoScroll();
3372 #ifndef KHTML_NO_TYPE_AHEAD_FIND
3373 if(d->typeAheadActivated)
3375 findTimeout();
3377 m_part->enableFindAheadActions( false );
3378 #endif // KHTML_NO_TYPE_AHEAD_FIND
3380 #ifndef KHTML_NO_CARET
3381 if (d->m_caretViewContext) {
3382 switch (d->m_caretViewContext->displayNonFocused) {
3383 case KHTMLPart::CaretInvisible:
3384 hideCaret();
3385 break;
3386 case KHTMLPart::CaretVisible: {
3387 if (d->m_caretViewContext->freqTimerId != -1)
3388 killTimer(d->m_caretViewContext->freqTimerId);
3389 d->m_caretViewContext->freqTimerId = -1;
3390 NodeImpl *caretNode = m_part->xmlDocImpl()->focusNode();
3391 if (!d->m_caretViewContext->visible && (m_part->isCaretMode()
3392 || m_part->isEditable()
3393 || (caretNode && caretNode->renderer()
3394 && caretNode->renderer()->style()->userInput()
3395 == UI_ENABLED))) {
3396 d->m_caretViewContext->visible = true;
3397 showCaret(true);
3398 }/*end if*/
3399 break;
3401 case KHTMLPart::CaretBlink:
3402 // simply leave as is
3403 break;
3404 }/*end switch*/
3405 }/*end if*/
3406 #endif // KHTML_NO_CARET
3408 if ( d->cursor_icon_widget )
3409 d->cursor_icon_widget->hide();
3411 QScrollArea::focusOutEvent( e );
3414 void KHTMLView::scrollContentsBy( int dx, int dy )
3416 if ( !d->firstRelayout && !d->complete && m_part->xmlDocImpl() &&
3417 d->layoutSchedulingEnabled) {
3418 // contents scroll while we are not complete: we need to check our layout *now*
3419 khtml::RenderCanvas* root = static_cast<khtml::RenderCanvas *>( m_part->xmlDocImpl()->renderer() );
3420 if (root && root->needsLayout()) {
3421 unscheduleRelayout();
3422 layout();
3425 if (!d->scrollingSelf) {
3426 d->scrollBarMoved = true;
3427 d->contentsMoving = true;
3428 // ensure quick reset of contentsMoving flag
3429 scheduleRepaint(0, 0, 0, 0);
3432 if (m_part->xmlDocImpl() && m_part->xmlDocImpl()->documentElement())
3433 m_part->xmlDocImpl()->documentElement()->dispatchHTMLEvent(EventImpl::SCROLL_EVENT, true, false);
3435 d->contentsX = QApplication::isRightToLeft() ?
3436 horizontalScrollBar()->maximum()-horizontalScrollBar()->value() : horizontalScrollBar()->value();
3437 d->contentsY = verticalScrollBar()->value();
3439 if (d->useSlowRepaints) {
3440 widget()->blockSignals( true );
3441 widget()->move( widget()->pos().x() + dx, widget()->pos().y() +dy );
3442 widget()->blockSignals( false );
3443 widget()->repaint();
3444 return;
3446 QScrollArea::scrollContentsBy(dx, dy);
3449 void KHTMLView::timerEvent ( QTimerEvent *e )
3451 // kDebug() << "timer event " << e->timerId() << endl;
3452 if ( e->timerId() == d->scrollTimerId ) {
3453 if( d->scrollSuspended )
3454 return;
3455 switch (d->scrollDirection) {
3456 case KHTMLViewPrivate::ScrollDown:
3457 if (contentsY() + visibleHeight () >= contentsHeight())
3458 d->newScrollTimer(this, 0);
3459 else
3460 verticalScrollBar()->setValue( verticalScrollBar()->value() +d->scrollBy );
3461 break;
3462 case KHTMLViewPrivate::ScrollUp:
3463 if (contentsY() <= 0)
3464 d->newScrollTimer(this, 0);
3465 else
3466 verticalScrollBar()->setValue( verticalScrollBar()->value() -d->scrollBy );
3467 break;
3468 case KHTMLViewPrivate::ScrollRight:
3469 if (contentsX() + visibleWidth () >= contentsWidth())
3470 d->newScrollTimer(this, 0);
3471 else
3472 horizontalScrollBar()->setValue( horizontalScrollBar()->value() +d->scrollBy );
3473 break;
3474 case KHTMLViewPrivate::ScrollLeft:
3475 if (contentsX() <= 0)
3476 d->newScrollTimer(this, 0);
3477 else
3478 horizontalScrollBar()->setValue( horizontalScrollBar()->value() -d->scrollBy );
3479 break;
3481 return;
3483 else if ( e->timerId() == d->layoutTimerId ) {
3484 d->dirtyLayout = true;
3485 layout();
3486 if (d->firstRelayout) {
3487 d->firstRelayout = false;
3488 verticalScrollBar()->setEnabled( true );
3489 horizontalScrollBar()->setEnabled( true );
3492 #ifndef KHTML_NO_CARET
3493 else if (d->m_caretViewContext
3494 && e->timerId() == d->m_caretViewContext->freqTimerId) {
3495 d->m_caretViewContext->visible = !d->m_caretViewContext->visible;
3496 if (d->m_caretViewContext->displayed) {
3497 updateContents(d->m_caretViewContext->x, d->m_caretViewContext->y,
3498 d->m_caretViewContext->width,
3499 d->m_caretViewContext->height);
3500 }/*end if*/
3501 // if (d->m_caretViewContext->visible) cout << "|" << flush;
3502 // else cout << "" << flush;
3503 return;
3505 #endif
3507 d->contentsMoving = false;
3508 if( m_part->xmlDocImpl() ) {
3509 DOM::DocumentImpl *document = m_part->xmlDocImpl();
3510 khtml::RenderCanvas* root = static_cast<khtml::RenderCanvas *>(document->renderer());
3512 if ( root && root->needsLayout() ) {
3513 if (d->repaintTimerId)
3514 killTimer(d->repaintTimerId);
3515 d->repaintTimerId = 0;
3516 scheduleRelayout();
3517 return;
3521 // setStaticBackground(d->useSlowRepaints); ?? ### FIXME
3523 // kDebug() << "scheduled repaint "<< d->repaintTimerId << endl;
3524 if (d->repaintTimerId)
3525 killTimer(d->repaintTimerId);
3526 d->repaintTimerId = 0;
3528 QRect updateRegion;
3529 QVector<QRect> rects = d->updateRegion.rects();
3531 d->updateRegion = QRegion();
3533 if ( rects.size() )
3534 updateRegion = rects[0];
3536 for ( int i = 1; i < rects.size(); ++i ) {
3537 QRect newRegion = updateRegion.unite(rects[i]);
3538 if (2*newRegion.height() > 3*updateRegion.height() )
3540 repaintContents( updateRegion );
3541 updateRegion = rects[i];
3543 else
3544 updateRegion = newRegion;
3547 if ( !updateRegion.isNull() )
3548 repaintContents( updateRegion );
3550 // As widgets can only be accurately positioned during painting, every layout might
3551 // dissociate a widget from its RenderWidget. E.g: if a RenderWidget was visible before layout, but the layout
3552 // pushed it out of the viewport, it will not be repainted, and consequently it's associated widget won't be repositioned.
3553 // Thus we need to check each supposedly 'visible' widget at the end of layout, and remove it in case it's no more in sight.
3555 if (d->dirtyLayout && !d->visibleWidgets.isEmpty()) {
3556 QWidget* w;
3557 d->dirtyLayout = false;
3559 QRect visibleRect(contentsX(), contentsY(), visibleWidth(), visibleHeight());
3560 QList<RenderWidget*> toRemove;
3561 for (Q3PtrDictIterator<QWidget> it(d->visibleWidgets); it.current(); ++it) {
3562 int xp = 0, yp = 0;
3563 w = it.current();
3564 RenderWidget* rw = static_cast<RenderWidget*>( it.currentKey() );
3565 if (!rw->absolutePosition(xp, yp) ||
3566 !visibleRect.intersects(QRect(xp, yp, w->width(), w->height())))
3567 toRemove.append(rw);
3570 foreach (RenderWidget* r, toRemove)
3571 if ( (w = d->visibleWidgets.take(r) ) )
3572 w->move( 0, -500000);
3575 emit repaintAccessKeys();
3576 if (d->emitCompletedAfterRepaint) {
3577 bool full = d->emitCompletedAfterRepaint == KHTMLViewPrivate::CSFull;
3578 d->emitCompletedAfterRepaint = KHTMLViewPrivate::CSNone;
3579 if ( full )
3580 emit m_part->completed();
3581 else
3582 emit m_part->completed(true);
3586 void KHTMLView::scheduleRelayout(khtml::RenderObject * /*clippedObj*/)
3588 if (!d->layoutSchedulingEnabled || d->layoutTimerId)
3589 return;
3591 d->layoutTimerId = startTimer( m_part->xmlDocImpl() && m_part->xmlDocImpl()->parsing()
3592 ? 1000 : 0 );
3595 void KHTMLView::unscheduleRelayout()
3597 if (!d->layoutTimerId)
3598 return;
3600 killTimer(d->layoutTimerId);
3601 d->layoutTimerId = 0;
3604 void KHTMLView::unscheduleRepaint()
3606 if (!d->repaintTimerId)
3607 return;
3609 killTimer(d->repaintTimerId);
3610 d->repaintTimerId = 0;
3613 void KHTMLView::scheduleRepaint(int x, int y, int w, int h, bool asap)
3615 bool parsing = !m_part->xmlDocImpl() || m_part->xmlDocImpl()->parsing();
3617 // kDebug() << "parsing " << parsing << endl;
3618 // kDebug() << "complete " << d->complete << endl;
3620 int time = parsing ? 300 : (!asap ? ( !d->complete ? 100 : 20 ) : 0);
3622 #ifdef DEBUG_FLICKER
3623 QPainter p;
3624 p.begin( viewport() );
3626 int vx, vy;
3627 contentsToViewport( x, y, vx, vy );
3628 p.fillRect( vx, vy, w, h, Qt::red );
3629 p.end();
3630 #endif
3632 d->updateRegion = d->updateRegion.unite(QRect(x,y,w,h));
3634 if (asap && !parsing)
3635 unscheduleRepaint();
3637 if ( !d->repaintTimerId )
3638 d->repaintTimerId = startTimer( time );
3640 // kDebug() << "starting timer " << time << endl;
3643 void KHTMLView::complete( bool pendingAction )
3645 // kDebug() << "KHTMLView::complete()" << endl;
3647 d->complete = true;
3649 // is there a relayout pending?
3650 if (d->layoutTimerId)
3652 // kDebug() << "requesting relayout now" << endl;
3653 // do it now
3654 killTimer(d->layoutTimerId);
3655 d->layoutTimerId = startTimer( 0 );
3656 d->emitCompletedAfterRepaint = pendingAction ?
3657 KHTMLViewPrivate::CSActionPending : KHTMLViewPrivate::CSFull;
3660 // is there a repaint pending?
3661 if (d->repaintTimerId)
3663 // kDebug() << "requesting repaint now" << endl;
3664 // do it now
3665 killTimer(d->repaintTimerId);
3666 d->repaintTimerId = startTimer( 20 );
3667 d->emitCompletedAfterRepaint = pendingAction ?
3668 KHTMLViewPrivate::CSActionPending : KHTMLViewPrivate::CSFull;
3671 if (!d->emitCompletedAfterRepaint)
3673 if (!pendingAction)
3674 emit m_part->completed();
3675 else
3676 emit m_part->completed(true);
3681 void KHTMLView::slotMouseScrollTimer()
3683 horizontalScrollBar()->setValue( horizontalScrollBar()->value() +d->m_mouseScroll_byX );
3684 verticalScrollBar()->setValue( verticalScrollBar()->value() +d->m_mouseScroll_byY);
3687 #ifndef KHTML_NO_CARET
3689 // ### the dependencies on static functions are a nightmare. just be
3690 // hacky and include the implementation here. Clean me up, please.
3692 #include "khtml_caret.cpp"
3694 void KHTMLView::initCaret(bool keepSelection)
3696 #if DEBUG_CARETMODE > 0
3697 kDebug(6200) << "begin initCaret" << endl;
3698 #endif
3699 // save caretMoved state as moveCaretTo changes it
3700 if (m_part->xmlDocImpl()) {
3701 #if 0
3702 ElementImpl *listitem = m_part->xmlDocImpl()->getElementById("__test_element__");
3703 if (listitem) dumpLineBoxes(static_cast<RenderFlow *>(listitem->renderer()));
3704 #endif
3705 d->caretViewContext();
3706 bool cmoved = d->m_caretViewContext->caretMoved;
3707 if (m_part->d->caretNode().isNull()) {
3708 // set to document, position will be sanitized anyway
3709 m_part->d->caretNode() = m_part->document();
3710 m_part->d->caretOffset() = 0L;
3711 // This sanity check is necessary for the not so unlikely case that
3712 // setEditable or setCaretMode is called before any render objects have
3713 // been created.
3714 if (!m_part->d->caretNode().handle()->renderer()) return;
3715 }/*end if*/
3716 // kDebug(6200) << "d->m_selectionStart " << m_part->d->m_selectionStart.handle()
3717 // << " d->m_selectionEnd " << m_part->d->m_selectionEnd.handle() << endl;
3718 // ### does not repaint the selection on keepSelection!=false
3719 moveCaretTo(m_part->d->caretNode().handle(), m_part->d->caretOffset(), !keepSelection);
3720 // kDebug(6200) << "d->m_selectionStart " << m_part->d->m_selectionStart.handle()
3721 // << " d->m_selectionEnd " << m_part->d->m_selectionEnd.handle() << endl;
3722 d->m_caretViewContext->caretMoved = cmoved;
3723 }/*end if*/
3724 #if DEBUG_CARETMODE > 0
3725 kDebug(6200) << "end initCaret" << endl;
3726 #endif
3729 bool KHTMLView::caretOverrides() const
3731 bool cm = m_part->isCaretMode();
3732 bool dm = m_part->isEditable();
3733 return cm && !dm ? false
3734 : (dm || m_part->d->caretNode().handle()->contentEditable())
3735 && d->editorContext()->override;
3738 void KHTMLView::ensureNodeHasFocus(NodeImpl *node)
3740 if (m_part->isCaretMode() || m_part->isEditable()) return;
3741 if (node->focused()) return;
3743 // Find first ancestor whose "user-input" is "enabled"
3744 NodeImpl *firstAncestor = 0;
3745 while (node) {
3746 if (node->renderer()
3747 && node->renderer()->style()->userInput() != UI_ENABLED)
3748 break;
3749 firstAncestor = node;
3750 node = node->parentNode();
3751 }/*wend*/
3753 if (!node) firstAncestor = 0;
3755 DocumentImpl *doc = m_part->xmlDocImpl();
3756 // ensure that embedded widgets don't lose their focus
3757 if (!firstAncestor && doc->focusNode() && doc->focusNode()->renderer()
3758 && doc->focusNode()->renderer()->isWidget())
3759 return;
3761 // Set focus node on the document
3762 #if DEBUG_CARETMODE > 1
3763 kDebug(6200) << k_funcinfo << "firstAncestor " << firstAncestor << ": "
3764 << (firstAncestor ? firstAncestor->nodeName().string() : QString()) << endl;
3765 #endif
3766 doc->setFocusNode(firstAncestor);
3767 emit m_part->nodeActivated(Node(firstAncestor));
3770 void KHTMLView::recalcAndStoreCaretPos(CaretBox *hintBox)
3772 if (!m_part || m_part->d->caretNode().isNull()) return;
3773 d->caretViewContext();
3774 NodeImpl *caretNode = m_part->d->caretNode().handle();
3775 #if DEBUG_CARETMODE > 0
3776 kDebug(6200) << "recalcAndStoreCaretPos: caretNode=" << caretNode << (caretNode ? " "+caretNode->nodeName().string() : QString()) << " r@" << caretNode->renderer() << (caretNode->renderer() && caretNode->renderer()->isText() ? " \"" + QConstString(static_cast<RenderText *>(caretNode->renderer())->str->s, qMin(static_cast<RenderText *>(caretNode->renderer())->str->l, 15u)).string() + "\"" : QString()) << endl;
3777 #endif
3778 caretNode->getCaret(m_part->d->caretOffset(), caretOverrides(),
3779 d->m_caretViewContext->x, d->m_caretViewContext->y,
3780 d->m_caretViewContext->width,
3781 d->m_caretViewContext->height);
3783 if (hintBox && d->m_caretViewContext->x == -1) {
3784 #if DEBUG_CARETMODE > 1
3785 kDebug(6200) << "using hint inline box coordinates" << endl;
3786 #endif
3787 RenderObject *r = caretNode->renderer();
3788 const QFontMetrics &fm = r->style()->fontMetrics();
3789 int absx, absy;
3790 r->containingBlock()->absolutePosition(absx, absy,
3791 false); // ### what about fixed?
3792 d->m_caretViewContext->x = absx + hintBox->xPos();
3793 d->m_caretViewContext->y = absy + hintBox->yPos();
3794 // + hintBox->baseline() - fm.ascent();
3795 d->m_caretViewContext->width = 1;
3796 // ### firstline not regarded. But I think it can be safely neglected
3797 // as hint boxes are only used for empty lines.
3798 d->m_caretViewContext->height = fm.height();
3799 }/*end if*/
3801 #if DEBUG_CARETMODE > 4
3802 // kDebug(6200) << "freqTimerId: "<<d->m_caretViewContext->freqTimerId<<endl;
3803 #endif
3804 #if DEBUG_CARETMODE > 0
3805 kDebug(6200) << "caret: ofs="<<m_part->d->caretOffset()<<" "
3806 <<" x="<<d->m_caretViewContext->x<<" y="<<d->m_caretViewContext->y
3807 <<" h="<<d->m_caretViewContext->height<<endl;
3808 #endif
3811 void KHTMLView::caretOn()
3813 if (d->m_caretViewContext) {
3814 if (d->m_caretViewContext->freqTimerId != -1)
3815 killTimer(d->m_caretViewContext->freqTimerId);
3817 if (hasFocus() || d->m_caretViewContext->displayNonFocused
3818 == KHTMLPart::CaretBlink) {
3819 d->m_caretViewContext->freqTimerId = startTimer(500);
3820 } else {
3821 d->m_caretViewContext->freqTimerId = -1;
3822 }/*end if*/
3824 d->m_caretViewContext->visible = true;
3825 if ((d->m_caretViewContext->displayed = (hasFocus()
3826 || d->m_caretViewContext->displayNonFocused
3827 != KHTMLPart::CaretInvisible))) {
3828 updateContents(d->m_caretViewContext->x, d->m_caretViewContext->y,
3829 d->m_caretViewContext->width,
3830 d->m_caretViewContext->height);
3831 }/*end if*/
3832 // kDebug(6200) << "caret on" << endl;
3833 }/*end if*/
3836 void KHTMLView::caretOff()
3838 if (d->m_caretViewContext) {
3839 if (d->m_caretViewContext->freqTimerId != -1)
3840 killTimer(d->m_caretViewContext->freqTimerId);
3841 d->m_caretViewContext->freqTimerId = -1;
3842 d->m_caretViewContext->displayed = false;
3843 if (d->m_caretViewContext->visible) {
3844 d->m_caretViewContext->visible = false;
3845 updateContents(d->m_caretViewContext->x, d->m_caretViewContext->y,
3846 d->m_caretViewContext->width,
3847 d->m_caretViewContext->height);
3848 }/*end if*/
3849 // kDebug(6200) << "caret off" << endl;
3850 }/*end if*/
3853 void KHTMLView::showCaret(bool forceRepaint)
3855 if (d->m_caretViewContext) {
3856 d->m_caretViewContext->displayed = true;
3857 if (d->m_caretViewContext->visible) {
3858 if (!forceRepaint) {
3859 updateContents(d->m_caretViewContext->x, d->m_caretViewContext->y,
3860 d->m_caretViewContext->width,
3861 d->m_caretViewContext->height);
3862 } else {
3863 repaintContents(d->m_caretViewContext->x, d->m_caretViewContext->y,
3864 d->m_caretViewContext->width,
3865 d->m_caretViewContext->height);
3866 }/*end if*/
3867 }/*end if*/
3868 // kDebug(6200) << "caret shown" << endl;
3869 }/*end if*/
3872 bool KHTMLView::foldSelectionToCaret(NodeImpl *startNode, long startOffset,
3873 NodeImpl *endNode, long endOffset)
3875 m_part->d->m_selectionStart = m_part->d->m_selectionEnd = m_part->d->caretNode();
3876 m_part->d->m_startOffset = m_part->d->m_endOffset = m_part->d->caretOffset();
3877 m_part->d->m_extendAtEnd = true;
3879 bool folded = startNode != endNode || startOffset != endOffset;
3881 // Only clear the selection if there has been one.
3882 if (folded) {
3883 m_part->xmlDocImpl()->clearSelection();
3884 }/*end if*/
3886 return folded;
3889 void KHTMLView::hideCaret()
3891 if (d->m_caretViewContext) {
3892 if (d->m_caretViewContext->visible) {
3893 // kDebug(6200) << "redraw caret hidden" << endl;
3894 d->m_caretViewContext->visible = false;
3895 // force repaint, otherwise the event won't be handled
3896 // before the focus leaves the window
3897 repaintContents(d->m_caretViewContext->x, d->m_caretViewContext->y,
3898 d->m_caretViewContext->width,
3899 d->m_caretViewContext->height);
3900 d->m_caretViewContext->visible = true;
3901 }/*end if*/
3902 d->m_caretViewContext->displayed = false;
3903 // kDebug(6200) << "caret hidden" << endl;
3904 }/*end if*/
3907 int KHTMLView::caretDisplayPolicyNonFocused() const
3909 if (d->m_caretViewContext)
3910 return d->m_caretViewContext->displayNonFocused;
3911 else
3912 return KHTMLPart::CaretInvisible;
3915 void KHTMLView::setCaretDisplayPolicyNonFocused(int policy)
3917 d->caretViewContext();
3918 // int old = d->m_caretViewContext->displayNonFocused;
3919 d->m_caretViewContext->displayNonFocused = (KHTMLPart::CaretDisplayPolicy)policy;
3921 // make change immediately take effect if not focused
3922 if (!hasFocus()) {
3923 switch (d->m_caretViewContext->displayNonFocused) {
3924 case KHTMLPart::CaretInvisible:
3925 hideCaret();
3926 break;
3927 case KHTMLPart::CaretBlink:
3928 if (d->m_caretViewContext->freqTimerId != -1) break;
3929 d->m_caretViewContext->freqTimerId = startTimer(500);
3930 // fall through
3931 case KHTMLPart::CaretVisible:
3932 d->m_caretViewContext->displayed = true;
3933 showCaret();
3934 break;
3935 }/*end switch*/
3936 }/*end if*/
3939 bool KHTMLView::placeCaret(CaretBox *hintBox)
3941 CaretViewContext *cv = d->caretViewContext();
3942 caretOff();
3943 NodeImpl *caretNode = m_part->d->caretNode().handle();
3944 // ### why is it sometimes null?
3945 if (!caretNode || !caretNode->renderer()) return false;
3946 ensureNodeHasFocus(caretNode);
3947 if (m_part->isCaretMode() || m_part->isEditable()
3948 || caretNode->renderer()->style()->userInput() == UI_ENABLED) {
3949 recalcAndStoreCaretPos(hintBox);
3951 cv->origX = cv->x;
3953 caretOn();
3954 return true;
3955 }/*end if*/
3956 return false;
3959 void KHTMLView::ensureCaretVisible()
3961 CaretViewContext *cv = d->m_caretViewContext;
3962 if (!cv) return;
3963 ensureVisible(cv->x, cv->y, cv->width, cv->height);
3964 d->scrollBarMoved = false;
3967 bool KHTMLView::extendSelection(NodeImpl *oldStartSel, long oldStartOfs,
3968 NodeImpl *oldEndSel, long oldEndOfs)
3970 bool changed = false;
3971 if (m_part->d->m_selectionStart == m_part->d->m_selectionEnd
3972 && m_part->d->m_startOffset == m_part->d->m_endOffset) {
3973 changed = foldSelectionToCaret(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs);
3974 m_part->d->m_extendAtEnd = true;
3975 } else do {
3976 changed = m_part->d->m_selectionStart.handle() != oldStartSel
3977 || m_part->d->m_startOffset != oldStartOfs
3978 || m_part->d->m_selectionEnd.handle() != oldEndSel
3979 || m_part->d->m_endOffset != oldEndOfs;
3980 if (!changed) break;
3982 // determine start position -- caret position is always at end.
3983 NodeImpl *startNode;
3984 long startOffset;
3985 if (m_part->d->m_extendAtEnd) {
3986 startNode = m_part->d->m_selectionStart.handle();
3987 startOffset = m_part->d->m_startOffset;
3988 } else {
3989 startNode = m_part->d->m_selectionEnd.handle();
3990 startOffset = m_part->d->m_endOffset;
3991 m_part->d->m_selectionEnd = m_part->d->m_selectionStart;
3992 m_part->d->m_endOffset = m_part->d->m_startOffset;
3993 m_part->d->m_extendAtEnd = true;
3994 }/*end if*/
3996 bool swapNeeded = false;
3997 if (!m_part->d->m_selectionEnd.isNull() && startNode) {
3998 swapNeeded = RangeImpl::compareBoundaryPoints(startNode, startOffset,
3999 m_part->d->m_selectionEnd.handle(),
4000 m_part->d->m_endOffset) >= 0;
4001 }/*end if*/
4003 m_part->d->m_selectionStart = startNode;
4004 m_part->d->m_startOffset = startOffset;
4006 if (swapNeeded) {
4007 m_part->xmlDocImpl()->setSelection(m_part->d->m_selectionEnd.handle(),
4008 m_part->d->m_endOffset, m_part->d->m_selectionStart.handle(),
4009 m_part->d->m_startOffset);
4010 } else {
4011 m_part->xmlDocImpl()->setSelection(m_part->d->m_selectionStart.handle(),
4012 m_part->d->m_startOffset, m_part->d->m_selectionEnd.handle(),
4013 m_part->d->m_endOffset);
4014 }/*end if*/
4015 } while(false);/*end if*/
4016 return changed;
4019 void KHTMLView::updateSelection(NodeImpl *oldStartSel, long oldStartOfs,
4020 NodeImpl *oldEndSel, long oldEndOfs)
4022 if (m_part->d->m_selectionStart == m_part->d->m_selectionEnd
4023 && m_part->d->m_startOffset == m_part->d->m_endOffset) {
4024 if (foldSelectionToCaret(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs)) {
4025 m_part->emitSelectionChanged();
4026 }/*end if*/
4027 m_part->d->m_extendAtEnd = true;
4028 } else {
4029 // check if the extending end has passed the immobile end
4030 if (!m_part->d->m_selectionEnd.isNull() && !m_part->d->m_selectionEnd.isNull()) {
4031 bool swapNeeded = RangeImpl::compareBoundaryPoints(
4032 m_part->d->m_selectionStart.handle(), m_part->d->m_startOffset,
4033 m_part->d->m_selectionEnd.handle(), m_part->d->m_endOffset) >= 0;
4034 if (swapNeeded) {
4035 DOM::Node tmpNode = m_part->d->m_selectionStart;
4036 long tmpOffset = m_part->d->m_startOffset;
4037 m_part->d->m_selectionStart = m_part->d->m_selectionEnd;
4038 m_part->d->m_startOffset = m_part->d->m_endOffset;
4039 m_part->d->m_selectionEnd = tmpNode;
4040 m_part->d->m_endOffset = tmpOffset;
4041 m_part->d->m_startBeforeEnd = true;
4042 m_part->d->m_extendAtEnd = !m_part->d->m_extendAtEnd;
4043 }/*end if*/
4044 }/*end if*/
4046 m_part->xmlDocImpl()->setSelection(m_part->d->m_selectionStart.handle(),
4047 m_part->d->m_startOffset, m_part->d->m_selectionEnd.handle(),
4048 m_part->d->m_endOffset);
4049 m_part->emitSelectionChanged();
4050 }/*end if*/
4053 void KHTMLView::caretKeyPressEvent(QKeyEvent *_ke)
4055 NodeImpl *oldStartSel = m_part->d->m_selectionStart.handle();
4056 long oldStartOfs = m_part->d->m_startOffset;
4057 NodeImpl *oldEndSel = m_part->d->m_selectionEnd.handle();
4058 long oldEndOfs = m_part->d->m_endOffset;
4060 NodeImpl *oldCaretNode = m_part->d->caretNode().handle();
4061 long oldOffset = m_part->d->caretOffset();
4063 bool ctrl = _ke->modifiers() & Qt::ControlModifier;
4065 // FIXME: this is that widely indented because I will write ifs around it.
4066 switch(_ke->key()) {
4067 case Qt::Key_Space:
4068 break;
4070 case Qt::Key_Down:
4071 moveCaretNextLine(1);
4072 break;
4074 case Qt::Key_Up:
4075 moveCaretPrevLine(1);
4076 break;
4078 case Qt::Key_Left:
4079 moveCaretBy(false, ctrl ? CaretByWord : CaretByCharacter, 1);
4080 break;
4082 case Qt::Key_Right:
4083 moveCaretBy(true, ctrl ? CaretByWord : CaretByCharacter, 1);
4084 break;
4086 case Qt::Key_PageDown:
4087 moveCaretNextPage();
4088 break;
4090 case Qt::Key_PageUp:
4091 moveCaretPrevPage();
4092 break;
4094 case Qt::Key_Home:
4095 if (ctrl)
4096 moveCaretToDocumentBoundary(false);
4097 else
4098 moveCaretToLineBegin();
4099 break;
4101 case Qt::Key_End:
4102 if (ctrl)
4103 moveCaretToDocumentBoundary(true);
4104 else
4105 moveCaretToLineEnd();
4106 break;
4108 }/*end switch*/
4110 if ((m_part->d->caretNode().handle() != oldCaretNode
4111 || m_part->d->caretOffset() != oldOffset)
4112 // node should never be null, but faulty conditions may cause it to be
4113 && !m_part->d->caretNode().isNull()) {
4115 d->m_caretViewContext->caretMoved = true;
4117 if (_ke->modifiers() & Qt::ShiftModifier) { // extend selection
4118 updateSelection(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs);
4119 } else { // clear any selection
4120 if (foldSelectionToCaret(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs))
4121 m_part->emitSelectionChanged();
4122 }/*end if*/
4124 m_part->emitCaretPositionChanged(m_part->d->caretNode(), m_part->d->caretOffset());
4125 }/*end if*/
4127 _ke->accept();
4130 bool KHTMLView::moveCaretTo(NodeImpl *node, long offset, bool clearSel)
4132 if (!node) return false;
4133 ElementImpl *baseElem = determineBaseElement(node);
4134 RenderFlow *base = static_cast<RenderFlow *>(baseElem ? baseElem->renderer() : 0);
4135 if (!node) return false;
4137 // need to find out the node's inline box. If there is none, this function
4138 // will snap to the next node that has one. This is necessary to make the
4139 // caret visible in any case.
4140 CaretBoxLineDeleter cblDeleter;
4141 // RenderBlock *cb;
4142 long r_ofs;
4143 CaretBoxIterator cbit;
4144 CaretBoxLine *cbl = findCaretBoxLine(node, offset, &cblDeleter, base, r_ofs, cbit);
4145 if(!cbl) {
4146 kWarning() << "KHTMLView::moveCaretTo - findCaretBoxLine() returns NULL" << endl;
4147 return false;
4150 #if DEBUG_CARETMODE > 3
4151 if (cbl) kDebug(6200) << cbl->information() << endl;
4152 #endif
4153 CaretBox *box = *cbit;
4154 if (cbit != cbl->end() && box->object() != node->renderer()) {
4155 if (box->object()->element()) {
4156 mapRenderPosToDOMPos(box->object(), r_ofs, box->isOutside(),
4157 box->isOutsideEnd(), node, offset);
4158 //if (!outside) offset = node->minOffset();
4159 #if DEBUG_CARETMODE > 1
4160 kDebug(6200) << "set new node " << node->nodeName().string() << "@" << node << endl;
4161 #endif
4162 } else { // box has no associated element -> do not use
4163 // this case should actually never happen.
4164 box = 0;
4165 kError(6200) << "Box contains no node! Crash imminent" << endl;
4166 }/*end if*/
4169 NodeImpl *oldStartSel = m_part->d->m_selectionStart.handle();
4170 long oldStartOfs = m_part->d->m_startOffset;
4171 NodeImpl *oldEndSel = m_part->d->m_selectionEnd.handle();
4172 long oldEndOfs = m_part->d->m_endOffset;
4174 // test for position change
4175 bool posChanged = m_part->d->caretNode().handle() != node
4176 || m_part->d->caretOffset() != offset;
4177 bool selChanged = false;
4179 m_part->d->caretNode() = node;
4180 m_part->d->caretOffset() = offset;
4181 if (clearSel || !oldStartSel || !oldEndSel) {
4182 selChanged = foldSelectionToCaret(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs);
4183 } else {
4184 //kDebug(6200) << "moveToCaret: extendSelection: m_extendAtEnd " << m_part->d->m_extendAtEnd << endl;
4185 //kDebug(6200) << "selection: start(" << m_part->d->m_selectionStart.handle() << "," << m_part->d->m_startOffset << "), end(" << m_part->d->m_selectionEnd.handle() << "," << m_part->d->m_endOffset << "), caret(" << m_part->d->caretNode().handle() << "," << m_part->d->caretOffset() << ")" << endl;
4186 selChanged = extendSelection(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs);
4187 //kDebug(6200) << "after extendSelection: m_extendAtEnd " << m_part->d->m_extendAtEnd << endl;
4188 //kDebug(6200) << "selection: start(" << m_part->d->m_selectionStart.handle() << "," << m_part->d->m_startOffset << "), end(" << m_part->d->m_selectionEnd.handle() << "," << m_part->d->m_endOffset << "), caret(" << m_part->d->caretNode().handle() << "," << m_part->d->caretOffset() << ")" << endl;
4189 }/*end if*/
4191 d->caretViewContext()->caretMoved = true;
4193 bool visible_caret = placeCaret(box);
4195 // FIXME: if the old position was !visible_caret, and the new position is
4196 // also, then two caretPositionChanged signals with a null Node are
4197 // emitted in series.
4198 if (posChanged) {
4199 m_part->emitCaretPositionChanged(visible_caret ? node : 0, offset);
4200 }/*end if*/
4202 return selChanged;
4205 void KHTMLView::moveCaretByLine(bool next, int count)
4207 Node &caretNodeRef = m_part->d->caretNode();
4208 if (caretNodeRef.isNull()) return;
4210 NodeImpl *caretNode = caretNodeRef.handle();
4211 // kDebug(6200) << ": caretNode=" << caretNode << endl;
4212 long offset = m_part->d->caretOffset();
4214 CaretViewContext *cv = d->caretViewContext();
4216 ElementImpl *baseElem = determineBaseElement(caretNode);
4217 LinearDocument ld(m_part, caretNode, offset, LeafsOnly, baseElem);
4219 ErgonomicEditableLineIterator it(ld.current(), cv->origX);
4221 // move count lines vertically
4222 while (count > 0 && it != ld.end() && it != ld.preBegin()) {
4223 count--;
4224 if (next) ++it; else --it;
4225 }/*wend*/
4227 // Nothing? Then leave everything as is.
4228 if (it == ld.end() || it == ld.preBegin()) return;
4230 int x, absx, absy;
4231 CaretBox *caretBox = nearestCaretBox(it, d->m_caretViewContext, x, absx, absy);
4233 placeCaretOnLine(caretBox, x, absx, absy);
4236 void KHTMLView::placeCaretOnLine(CaretBox *caretBox, int x, int absx, int absy)
4238 // paranoia sanity check
4239 if (!caretBox) return;
4241 RenderObject *caretRender = caretBox->object();
4243 #if DEBUG_CARETMODE > 0
4244 kDebug(6200) << "got valid caretBox " << caretBox << endl;
4245 kDebug(6200) << "xPos: " << caretBox->xPos() << " yPos: " << caretBox->yPos()
4246 << " width: " << caretBox->width() << " height: " << caretBox->height() << endl;
4247 InlineTextBox *tb = static_cast<InlineTextBox *>(caretBox->inlineBox());
4248 if (caretBox->isInlineTextBox()) { kDebug(6200) << "contains \"" << QString(static_cast<RenderText *>(tb->object())->str->s + tb->m_start, tb->m_len) << "\"" << endl;}
4249 #endif
4250 // inquire height of caret
4251 int caretHeight = caretBox->height();
4252 bool isText = caretBox->isInlineTextBox();
4253 int yOfs = 0; // y-offset for text nodes
4254 if (isText) {
4255 // text boxes need extrawurst
4256 RenderText *t = static_cast<RenderText *>(caretRender);
4257 const QFontMetrics &fm = t->metrics(caretBox->inlineBox()->m_firstLine);
4258 caretHeight = fm.height();
4259 yOfs = caretBox->inlineBox()->baseline() - fm.ascent();
4260 }/*end if*/
4262 caretOff();
4264 // set new caret node
4265 NodeImpl *caretNode;
4266 long &offset = m_part->d->caretOffset();
4267 mapRenderPosToDOMPos(caretRender, offset, caretBox->isOutside(),
4268 caretBox->isOutsideEnd(), caretNode, offset);
4270 // set all variables not needing special treatment
4271 d->m_caretViewContext->y = caretBox->yPos() + yOfs;
4272 d->m_caretViewContext->height = caretHeight;
4273 d->m_caretViewContext->width = 1; // FIXME: regard override
4275 int xPos = caretBox->xPos();
4276 int caretBoxWidth = caretBox->width();
4277 d->m_caretViewContext->x = xPos;
4279 if (!caretBox->isOutside()) {
4280 // before or at beginning of inline box -> place at beginning
4281 long r_ofs = 0;
4282 if (x <= xPos) {
4283 r_ofs = caretBox->minOffset();
4284 // somewhere within this block
4285 } else if (x > xPos && x <= xPos + caretBoxWidth) {
4286 if (isText) { // find out where exactly
4287 r_ofs = static_cast<InlineTextBox *>(caretBox->inlineBox())
4288 ->offsetForPoint(x, d->m_caretViewContext->x);
4289 #if DEBUG_CARETMODE > 2
4290 kDebug(6200) << "deviation from origX " << d->m_caretViewContext->x - x << endl;
4291 #endif
4292 #if 0
4293 } else { // snap to nearest end
4294 if (xPos + caretBoxWidth - x < x - xPos) {
4295 d->m_caretViewContext->x = xPos + caretBoxWidth;
4296 r_ofs = caretNode ? caretNode->maxOffset() : 1;
4297 } else {
4298 d->m_caretViewContext->x = xPos;
4299 r_ofs = caretNode ? caretNode->minOffset() : 0;
4300 }/*end if*/
4301 #endif
4302 }/*end if*/
4303 } else { // after the inline box -> place at end
4304 d->m_caretViewContext->x = xPos + caretBoxWidth;
4305 r_ofs = caretBox->maxOffset();
4306 }/*end if*/
4307 offset = r_ofs;
4308 }/*end if*/
4309 #if DEBUG_CARETMODE > 0
4310 kDebug(6200) << "new offset: " << offset << endl;
4311 #endif
4313 m_part->d->caretNode() = caretNode;
4314 m_part->d->caretOffset() = offset;
4316 d->m_caretViewContext->x += absx;
4317 d->m_caretViewContext->y += absy;
4319 #if DEBUG_CARETMODE > 1
4320 kDebug(6200) << "new caret position: x " << d->m_caretViewContext->x << " y " << d->m_caretViewContext->y << " w " << d->m_caretViewContext->width << " h " << d->m_caretViewContext->height << " absx " << absx << " absy " << absy << endl;
4321 #endif
4323 ensureVisible(d->m_caretViewContext->x, d->m_caretViewContext->y,
4324 d->m_caretViewContext->width, d->m_caretViewContext->height);
4325 d->scrollBarMoved = false;
4327 ensureNodeHasFocus(caretNode);
4328 caretOn();
4331 void KHTMLView::moveCaretToLineBoundary(bool end)
4333 Node &caretNodeRef = m_part->d->caretNode();
4334 if (caretNodeRef.isNull()) return;
4336 NodeImpl *caretNode = caretNodeRef.handle();
4337 // kDebug(6200) << ": caretNode=" << caretNode << endl;
4338 long offset = m_part->d->caretOffset();
4340 ElementImpl *baseElem = determineBaseElement(caretNode);
4341 LinearDocument ld(m_part, caretNode, offset, LeafsOnly, baseElem);
4343 EditableLineIterator it = ld.current();
4344 if (it == ld.end()) return; // should not happen, but who knows
4346 EditableCaretBoxIterator fbit(it, end);
4347 Q_ASSERT(fbit != (*it)->end() && fbit != (*it)->preBegin());
4348 CaretBox *b = *fbit;
4350 RenderObject *cb = b->containingBlock();
4351 int absx, absy;
4353 if (cb) cb->absolutePosition(absx,absy);
4354 else absx = absy = 0;
4356 int x = b->xPos() + (end && !b->isOutside() ? b->width() : 0);
4357 d->m_caretViewContext->origX = absx + x;
4358 placeCaretOnLine(b, x, absx, absy);
4361 void KHTMLView::moveCaretToDocumentBoundary(bool end)
4363 Node &caretNodeRef = m_part->d->caretNode();
4364 if (caretNodeRef.isNull()) return;
4366 NodeImpl *caretNode = caretNodeRef.handle();
4367 // kDebug(6200) << ": caretNode=" << caretNode << endl;
4368 long offset = m_part->d->caretOffset();
4370 ElementImpl *baseElem = determineBaseElement(caretNode);
4371 LinearDocument ld(m_part, caretNode, offset, IndicatedFlows, baseElem);
4373 EditableLineIterator it(end ? ld.preEnd() : ld.begin(), end);
4374 if (it == ld.end() || it == ld.preBegin()) return; // should not happen, but who knows
4376 EditableCaretBoxIterator fbit = it;
4377 Q_ASSERT(fbit != (*it)->end() && fbit != (*it)->preBegin());
4378 CaretBox *b = *fbit;
4380 RenderObject *cb = (*it)->containingBlock();
4381 int absx, absy;
4383 if (cb) cb->absolutePosition(absx, absy);
4384 else absx = absy = 0;
4386 int x = b->xPos()/* + (end ? b->width() : 0) reactivate for rtl*/;
4387 d->m_caretViewContext->origX = absx + x;
4388 placeCaretOnLine(b, x, absx, absy);
4391 void KHTMLView::moveCaretBy(bool next, CaretMovement cmv, int count)
4393 if (!m_part) return;
4394 Node &caretNodeRef = m_part->d->caretNode();
4395 if (caretNodeRef.isNull()) return;
4397 NodeImpl *caretNode = caretNodeRef.handle();
4398 // kDebug(6200) << ": caretNode=" << caretNode << endl;
4399 long &offset = m_part->d->caretOffset();
4401 ElementImpl *baseElem = determineBaseElement(caretNode);
4402 CaretAdvancePolicy advpol = cmv != CaretByWord ? IndicatedFlows : LeafsOnly;
4403 LinearDocument ld(m_part, caretNode, offset, advpol, baseElem);
4405 EditableCharacterIterator it(&ld);
4406 while (!it.isEnd() && count > 0) {
4407 count--;
4408 if (cmv == CaretByCharacter) {
4409 if (next) ++it;
4410 else --it;
4411 } else if (cmv == CaretByWord) {
4412 if (next) moveItToNextWord(it);
4413 else moveItToPrevWord(it);
4414 }/*end if*/
4415 //kDebug(6200) << "movecaret" << endl;
4416 }/*wend*/
4417 CaretBox *hintBox = 0; // make gcc uninit warning disappear
4418 if (!it.isEnd()) {
4419 NodeImpl *node = caretNodeRef.handle();
4420 hintBox = it.caretBox();
4421 //kDebug(6200) << "hintBox = " << hintBox << endl;
4422 //kDebug(6200) << " outside " << hintBox->isOutside() << " outsideEnd " << hintBox->isOutsideEnd() << " r " << it.renderer() << " ofs " << it.offset() << " cb " << hintBox->containingBlock() << endl;
4423 mapRenderPosToDOMPos(it.renderer(), it.offset(), hintBox->isOutside(),
4424 hintBox->isOutsideEnd(), node, offset);
4425 //kDebug(6200) << "mapRTD" << endl;
4426 caretNodeRef = node;
4427 #if DEBUG_CARETMODE > 2
4428 kDebug(6200) << "set by valid node " << node << " " << (node?node->nodeName().string():QString()) << " offset: " << offset << endl;
4429 #endif
4430 } else {
4431 offset = next ? caretNode->maxOffset() : caretNode->minOffset();
4432 #if DEBUG_CARETMODE > 0
4433 kDebug(6200) << "set by INvalid node. offset: " << offset << endl;
4434 #endif
4435 }/*end if*/
4436 placeCaretOnChar(hintBox);
4439 void KHTMLView::placeCaretOnChar(CaretBox *hintBox)
4441 caretOff();
4442 recalcAndStoreCaretPos(hintBox);
4443 ensureVisible(d->m_caretViewContext->x, d->m_caretViewContext->y,
4444 d->m_caretViewContext->width, d->m_caretViewContext->height);
4445 d->m_caretViewContext->origX = d->m_caretViewContext->x;
4446 d->scrollBarMoved = false;
4447 #if DEBUG_CARETMODE > 3
4448 //if (caretNode->isTextNode()) kDebug(6200) << "text[0] = " << (int)*((TextImpl *)caretNode)->data().unicode() << " text :\"" << ((TextImpl *)caretNode)->data().string() << "\"" << endl;
4449 #endif
4450 ensureNodeHasFocus(m_part->d->caretNode().handle());
4451 caretOn();
4454 void KHTMLView::moveCaretByPage(bool next)
4456 Node &caretNodeRef = m_part->d->caretNode();
4457 if (caretNodeRef.isNull()) return;
4459 NodeImpl *caretNode = caretNodeRef.handle();
4460 // kDebug(6200) << ": caretNode=" << caretNode << endl;
4461 long offset = m_part->d->caretOffset();
4463 int offs = (viewport()->height() < 30) ? viewport()->height() : 30;
4464 // Minimum distance the caret must be moved
4465 int mindist = viewport()->height() - offs;
4467 CaretViewContext *cv = d->caretViewContext();
4468 // int y = cv->y; // we always measure the top border
4470 ElementImpl *baseElem = determineBaseElement(caretNode);
4471 LinearDocument ld(m_part, caretNode, offset, LeafsOnly, baseElem);
4473 ErgonomicEditableLineIterator it(ld.current(), cv->origX);
4475 moveIteratorByPage(ld, it, mindist, next);
4477 int x, absx, absy;
4478 CaretBox *caretBox = nearestCaretBox(it, d->m_caretViewContext, x, absx, absy);
4480 placeCaretOnLine(caretBox, x, absx, absy);
4483 void KHTMLView::moveCaretPrevWord()
4485 moveCaretBy(false, CaretByWord, 1);
4488 void KHTMLView::moveCaretNextWord()
4490 moveCaretBy(true, CaretByWord, 1);
4493 void KHTMLView::moveCaretPrevLine(int n)
4495 moveCaretByLine(false, n);
4498 void KHTMLView::moveCaretNextLine(int n)
4500 moveCaretByLine(true, n);
4503 void KHTMLView::moveCaretPrevPage()
4505 moveCaretByPage(false);
4508 void KHTMLView::moveCaretNextPage()
4510 moveCaretByPage(true);
4513 void KHTMLView::moveCaretToLineBegin()
4515 moveCaretToLineBoundary(false);
4518 void KHTMLView::moveCaretToLineEnd()
4520 moveCaretToLineBoundary(true);
4523 #endif // KHTML_NO_CARET
4525 #undef DEBUG_CARETMODE