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