1/* This file is part of the KDE project
2 *
3 * Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
4 * 1999 Lars Knoll <knoll@kde.org>
5 * 1999 Antti Koivisto <koivisto@kde.org>
6 * 2000-2004 Dirk Mueller <mueller@kde.org>
7 * 2003 Leo Savernik <l.savernik@aon.at>
8 * 2003-2008 Apple Computer, Inc.
9 * 2008 Allan Sandfeld Jensen <kde@carewolf.com>
10 * 2006-2008 Germain Garand <germain@ebooksfrance.org>
11 *
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Library General Public
14 * License as published by the Free Software Foundation; either
15 * version 2 of the License, or (at your option) any later version.
16 *
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Library General Public License for more details.
21 *
22 * You should have received a copy of the GNU Library General Public License
23 * along with this library; see the file COPYING.LIB. If not, write to
24 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25 * Boston, MA 02110-1301, USA.
26 */
27
28
29#include "khtmlview.h"
30
31#include "khtmlview.moc"
32
33#include "khtml_part.h"
34#include "khtml_events.h"
35#ifdef Q_WS_X11
36#include <qx11info_x11.h>
37#endif
38
39#include "html/html_documentimpl.h"
40#include "html/html_inlineimpl.h"
41#include "html/html_formimpl.h"
42#include "html/htmltokenizer.h"
43#include "editing/editor.h"
44#include "rendering/render_arena.h"
45#include "rendering/render_canvas.h"
46#include "rendering/render_frames.h"
47#include "rendering/render_replaced.h"
48#include "rendering/render_form.h"
49#include "rendering/render_layer.h"
50#include "rendering/render_line.h"
51#include "rendering/render_table.h"
52// removeme
53#define protected public
54#include "rendering/render_text.h"
55#undef protected
56#include "xml/dom2_eventsimpl.h"
57#include "css/cssstyleselector.h"
58#include "css/csshelper.h"
59#include "misc/helper.h"
60#include "misc/loader.h"
61#include "khtml_settings.h"
62#include "khtml_printsettings.h"
63
64#include "khtmlpart_p.h"
65
66#include <kcursor.h>
67#include <kdebug.h>
68#include <kglobalsettings.h>
69#include <kdialog.h>
70#include <kiconloader.h>
71#include <klocale.h>
72#include <knotification.h>
73#include <kdeprintdialog.h>
74#include <kconfig.h>
75#include <kstandarddirs.h>
76#include <kstandardshortcut.h>
77#include <kstringhandler.h>
78#include <kconfiggroup.h>
79
80#include <QtGui/QBitmap>
81#include <QtGui/QLabel>
82#include <QtCore/QObject>
83#include <QtGui/QPainter>
84#include <QtCore/QHash>
85#include <QtGui/QToolTip>
86#include <QtCore/QString>
87#include <QtGui/QTextDocument>
88#include <QtCore/QTimer>
89#include <QtCore/QAbstractEventDispatcher>
90#include <QtCore/QVector>
91#include <QtGui/QAbstractScrollArea>
92#include <QtGui/QPrinter>
93#include <QtGui/QPrintDialog>
94
95//#define DEBUG_FLICKER
96
97#include <limits.h>
98#ifdef Q_WS_X11
99#include <X11/Xlib.h>
100#include <fixx11h.h>
101#elif defined(Q_WS_WIN)
102#include <windows.h>
103#endif
104
105#if 0
106namespace khtml {
107 void dumpLineBoxes(RenderFlow *flow);
108}
109#endif
110
111using namespace DOM;
112using namespace khtml;
113
114#ifndef NDEBUG
115static const int sFirstLayoutDelay = 520;
116static const int sParsingLayoutsInterval = 380;
117static const int sLayoutAttemptDelay = 300;
118#else
119static const int sFirstLayoutDelay = 280;
120static const int sParsingLayoutsInterval = 320;
121static const int sLayoutAttemptDelay = 200;
122#endif
123static const int sLayoutAttemptIncrement = 20;
124static const int sParsingLayoutsIncrement = 60;
125
126static const int sSmoothScrollTime = 128;
127static const int sSmoothScrollTick = 16;
128static const int sSmoothScrollMinStaticPixels = 320*200;
129
130static const int sMaxMissedDeadlines = 12;
131static const int sWayTooMany = -1;
132
133class KHTMLViewPrivate {
134 friend class KHTMLView;
135public:
136
137 enum PseudoFocusNodes {
138 PFNone,
139 PFTop,
140 PFBottom
141 };
142
143 enum StaticBackgroundState {
144 SBNone = 0,
145 SBPartial,
146 SBFull
147 };
148
149 enum CompletedState {
150 CSNone = 0,
151 CSFull,
152 CSActionPending
153 };
154
155 KHTMLViewPrivate(KHTMLView* v)
156 : underMouse( 0 ), underMouseNonShared( 0 ), oldUnderMouse( 0 )
157 {
158 postponed_autorepeat = NULL;
159 scrollingFromWheelTimerId = 0;
160 smoothScrollMode = KHTMLView::SSMWhenEfficient;
161
162 reset();
163 vpolicy = Qt::ScrollBarAsNeeded;
164 hpolicy = Qt::ScrollBarAsNeeded;
165 formCompletions=0;
166 prevScrollbarVisible = true;
167
168 possibleTripleClick = false;
169 emitCompletedAfterRepaint = CSNone;
170 cursorIconWidget = 0;
171 cursorIconType = KHTMLView::LINK_NORMAL;
172 m_mouseScrollTimer = 0;
173 m_mouseScrollIndicator = 0;
174 contentsX = 0;
175 contentsY = 0;
176 view = v;
177 }
178 ~KHTMLViewPrivate()
179 {
180 delete formCompletions;
181 delete postponed_autorepeat;
182 if (underMouse)
183 underMouse->deref();
184 if (underMouseNonShared)
185 underMouseNonShared->deref();
186 if (oldUnderMouse)
187 oldUnderMouse->deref();
188
189 delete cursorIconWidget;
190 delete m_mouseScrollTimer;
191 delete m_mouseScrollIndicator;
192 }
193 void reset()
194 {
195 if (underMouse)
196 underMouse->deref();
197 underMouse = 0;
198 if (underMouseNonShared)
199 underMouseNonShared->deref();
200 underMouseNonShared = 0;
201 if (oldUnderMouse)
202 oldUnderMouse->deref();
203 oldUnderMouse = 0;
204 linkPressed = false;
205 staticWidget = SBNone;
206 fixedObjectsCount = 0;
207 staticObjectsCount = 0;
208 tabMovePending = false;
209 lastTabbingDirection = true;
210 pseudoFocusNode = PFNone;
211 zoomLevel = 100;
212#ifndef KHTML_NO_SCROLLBARS
213 //We don't turn off the toolbars here
214 //since if the user turns them
215 //off, then chances are they want them turned
216 //off always - even after a reset.
217#else
218 vpolicy = ScrollBarAlwaysOff;
219 hpolicy = ScrollBarAlwaysOff;
220#endif
221 scrollBarMoved = false;
222 contentsMoving = false;
223 ignoreWheelEvents = false;
224 scrollingFromWheel = QPoint(-1,-1);
225 borderX = 30;
226 borderY = 30;
227 steps = 0;
228 dx = dy = 0;
229 paged = false;
230 clickX = -1;
231 clickY = -1;
232 clickCount = 0;
233 isDoubleClick = false;
234 scrollingSelf = false;
235 delete postponed_autorepeat;
236 postponed_autorepeat = NULL;
237 layoutTimerId = 0;
238 repaintTimerId = 0;
239 scrollTimerId = 0;
240 scrollSuspended = false;
241 scrollSuspendPreActivate = false;
242 smoothScrolling = false;
243 smoothScrollModeIsDefault = true;
244 shouldSmoothScroll = false;
245 smoothScrollMissedDeadlines = 0;
246 hasFrameset = false;
247 complete = false;
248 firstLayoutPending = true;
249#ifdef SPEED_DEBUG
250 firstRepaintPending = true;
251#endif
252 needsFullRepaint = true;
253 dirtyLayout = false;
254 layoutSchedulingEnabled = true;
255 painting = false;
256 layoutCounter = 0;
257 layoutAttemptCounter = 0;
258 scheduledLayoutCounter = 0;
259 updateRegion = QRegion();
260 m_dialogsAllowed = true;
261 accessKeysActivated = false;
262 accessKeysPreActivate = false;
263
264 // the view might have been built before the part it will be assigned to,
265 // so exceptionally, we need to directly ref/deref KHTMLGlobal to
266 // account for this transitory case.
267 KHTMLGlobal::ref();
268 accessKeysEnabled = KHTMLGlobal::defaultHTMLSettings()->accessKeysEnabled();
269 KHTMLGlobal::deref();
270
271 emitCompletedAfterRepaint = CSNone;
272 m_mouseEventsTarget = 0;
273 m_clipHolder = 0;
274 }
275 void newScrollTimer(QWidget *view, int tid)
276 {
277 //kDebug(6000) << "newScrollTimer timer " << tid;
278 view->killTimer(scrollTimerId);
279 scrollTimerId = tid;
280 scrollSuspended = false;
281 }
282 enum ScrollDirection { ScrollLeft, ScrollRight, ScrollUp, ScrollDown };
283
284 void adjustScroller(QWidget *view, ScrollDirection direction, ScrollDirection oppositedir)
285 {
286 static const struct { int msec, pixels; } timings [] = {
287 {320,1}, {224,1}, {160,1}, {112,1}, {80,1}, {56,1}, {40,1},
288 {28,1}, {20,1}, {20,2}, {20,3}, {20,4}, {20,6}, {20,8}, {0,0}
289 };
290 if (!scrollTimerId ||
291 (static_cast<int>(scrollDirection) != direction &&
292 (static_cast<int>(scrollDirection) != oppositedir || scrollSuspended))) {
293 scrollTiming = 6;
294 scrollBy = timings[scrollTiming].pixels;
295 scrollDirection = direction;
296 newScrollTimer(view, view->startTimer(timings[scrollTiming].msec));
297 } else if (scrollDirection == direction &&
298 timings[scrollTiming+1].msec && !scrollSuspended) {
299 scrollBy = timings[++scrollTiming].pixels;
300 newScrollTimer(view, view->startTimer(timings[scrollTiming].msec));
301 } else if (scrollDirection == oppositedir) {
302 if (scrollTiming) {
303 scrollBy = timings[--scrollTiming].pixels;
304 newScrollTimer(view, view->startTimer(timings[scrollTiming].msec));
305 }
306 }
307 scrollSuspended = false;
308 }
309
310 bool haveZoom() const { return zoomLevel != 100; }
311
312 void startScrolling()
313 {
314 smoothScrolling = true;
315 smoothScrollTimer.start(sSmoothScrollTick);
316 shouldSmoothScroll = false;
317 }
318
319 void stopScrolling()
320 {
321 smoothScrollTimer.stop();
322 dx = dy = 0;
323 steps = 0;
324 updateContentsXY();
325 smoothScrolling = false;
326 shouldSmoothScroll = false;
327 }
328
329 void updateContentsXY()
330 {
331 contentsX = QApplication::isRightToLeft() ?
332 view->horizontalScrollBar()->maximum()-view->horizontalScrollBar()->value() : view->horizontalScrollBar()->value();
333 contentsY = view->verticalScrollBar()->value();
334 }
335 void scrollAccessKeys(int dx, int dy)
336 {
337 QList<QLabel*> wl = qFindChildren<QLabel*>(view->widget(), "KHTMLAccessKey");
338 foreach(QLabel* w, wl) {
339 w->move( w->pos() + QPoint(dx, dy) );
340 }
341 }
342 void scrollExternalWidgets(int dx, int dy)
343 {
344 if (visibleWidgets.isEmpty())
345 return;
346
347 QHashIterator<void*, QWidget*> it(visibleWidgets);
348 while (it.hasNext()) {
349 it.next();
350 it.value()->move( it.value()->pos() + QPoint(dx, dy) );
351 }
352 }
353
354 NodeImpl *underMouse;
355 NodeImpl *underMouseNonShared;
356 NodeImpl *oldUnderMouse;
357
358 // Do not adjust bitfield enums sizes.
359 // They are oversized because they are signed on some platforms.
360 bool tabMovePending:1;
361 bool lastTabbingDirection:1;
362 PseudoFocusNodes pseudoFocusNode:3;
363 bool scrollBarMoved:1;
364 bool contentsMoving:1;
365
366 Qt::ScrollBarPolicy vpolicy;
367 Qt::ScrollBarPolicy hpolicy;
368 bool prevScrollbarVisible:1;
369 bool linkPressed:1;
370 bool ignoreWheelEvents:1;
371 StaticBackgroundState staticWidget: 3;
372 int staticObjectsCount;
373 int fixedObjectsCount;
374
375 int zoomLevel;
376 int borderX, borderY;
377 int dx, dy;
378 int steps;
379 KConfig *formCompletions;
380
381 int clickX, clickY, clickCount;
382 bool isDoubleClick;
383
384 bool paged;
385
386 bool scrollingSelf;
387 int contentsX, contentsY;
388 int layoutTimerId;
389 QKeyEvent* postponed_autorepeat;
390
391 int repaintTimerId;
392 int scrollTimerId;
393 int scrollTiming;
394 int scrollBy;
395 ScrollDirection scrollDirection :3;
396 bool scrollSuspended :1;
397 bool scrollSuspendPreActivate :1;
398 KHTMLView::SmoothScrollingMode smoothScrollMode :3;
399 bool smoothScrolling :1;
400 bool smoothScrollModeIsDefault :1;
401 bool shouldSmoothScroll :1;
402 bool hasFrameset :1;
403 bool complete :1;
404 bool firstLayoutPending :1;
405#ifdef SPEED_DEBUG
406 bool firstRepaintPending :1;
407#endif
408 bool layoutSchedulingEnabled :1;
409 bool needsFullRepaint :1;
410 bool painting :1;
411 bool possibleTripleClick :1;
412 bool dirtyLayout :1;
413 bool m_dialogsAllowed :1;
414 short smoothScrollMissedDeadlines;
415 int layoutCounter;
416 int layoutAttemptCounter;
417 int scheduledLayoutCounter;
418 QRegion updateRegion;
419 QTimer smoothScrollTimer;
420 QTime smoothScrollStopwatch;
421 QHash<void*, QWidget*> visibleWidgets;
422 bool accessKeysEnabled;
423 bool accessKeysActivated;
424 bool accessKeysPreActivate;
425 CompletedState emitCompletedAfterRepaint;
426
427 QLabel* cursorIconWidget;
428 KHTMLView::LinkCursor cursorIconType;
429
430 // scrolling activated by MMB
431 short m_mouseScroll_byX;
432 short m_mouseScroll_byY;
433 QPoint scrollingFromWheel;
434 int scrollingFromWheelTimerId;
435 QTimer *m_mouseScrollTimer;
436 QWidget *m_mouseScrollIndicator;
437 QPointer<QWidget> m_mouseEventsTarget;
438 QStack<QRegion>* m_clipHolder;
439 KHTMLView* view;
440};
441
442#ifndef QT_NO_TOOLTIP
443
444/** calculates the client-side image map rectangle for the given image element
445 * @param img image element
446 * @param scrollOfs scroll offset of viewport in content coordinates
447 * @param p position to be probed in viewport coordinates
448 * @param r returns the bounding rectangle in content coordinates
449 * @param s returns the title string
450 * @return true if an appropriate area was found -- only in this case r and
451 * s are valid, false otherwise
452 */
453static bool findImageMapRect(HTMLImageElementImpl *img, const QPoint &scrollOfs,
454 const QPoint &p, QRect &r, QString &s)
455{
456 HTMLMapElementImpl* map;
457 if (img && img->document()->isHTMLDocument() &&
458 (map = static_cast<HTMLDocumentImpl*>(img->document())->getMap(img->imageMap()))) {
459 RenderObject::NodeInfo info(true, false);
460 RenderObject *rend = img->renderer();
461 int ax, ay;
462 if (!rend || !rend->absolutePosition(ax, ay))
463 return false;
464 // we're a client side image map
465 bool inside = map->mapMouseEvent(p.x() - ax + scrollOfs.x(),
466 p.y() - ay + scrollOfs.y(), rend->contentWidth(),
467 rend->contentHeight(), info);
468 if (inside && info.URLElement()) {
469 HTMLAreaElementImpl *area = static_cast<HTMLAreaElementImpl *>(info.URLElement());
470 Q_ASSERT(area->id() == ID_AREA);
471 s = area->getAttribute(ATTR_TITLE).string();
472 QRegion reg = area->cachedRegion();
473 if (!s.isEmpty() && !reg.isEmpty()) {
474 r = reg.boundingRect();
475 r.translate(ax, ay);
476 return true;
477 }
478 }
479 }
480 return false;
481}
482
483bool KHTMLView::event( QEvent* e )
484{
485 switch ( e->type() ) {
486 case QEvent::ToolTip: {
487 QHelpEvent *he = static_cast<QHelpEvent*>(e);
488 QPoint p = he->pos();
489
490 DOM::NodeImpl *node = d->underMouseNonShared;
491 QRect region;
492 while ( node ) {
493 if ( node->isElementNode() ) {
494 DOM::ElementImpl *e = static_cast<DOM::ElementImpl*>( node );
495 QRect r;
496 QString s;
497 bool found = false;
498 // for images, check if it is part of a client-side image map,
499 // and query the <area>s' title attributes, too
500 if (e->id() == ID_IMG && !e->getAttribute( ATTR_USEMAP ).isEmpty()) {
501 found = findImageMapRect(static_cast<HTMLImageElementImpl *>(e),
502 viewportToContents(QPoint(0, 0)), p, r, s);
503 }
504 if (!found) {
505 s = e->getAttribute( ATTR_TITLE ).string();
506 r = node->getRect();
507 }
508 region |= QRect( contentsToViewport( r.topLeft() ), r.size() );
509 if ( !s.isEmpty() ) {
510 QToolTip::showText( he->globalPos(),
511 Qt::convertFromPlainText( s, Qt::WhiteSpaceNormal ),
512 widget(), region );
513 break;
514 }
515 }
516 node = node->parentNode();
517 }
518 // Qt makes tooltip events happen nearly immediately when a preceding one was processed in the past few seconds.
519 // We don't want that feature to apply to web tootlips however, as it clashes with dhtml menus.
520 // So we'll just pretend we did not process that event.
521 return false;
522 }
523
524 case QEvent::DragEnter:
525 case QEvent::DragMove:
526 case QEvent::DragLeave:
527 case QEvent::Drop:
528 // In Qt4, one needs to both call accept() on the DND event and return
529 // true on ::event for the candidate widget for the drop to be possible.
530 // Apps hosting us, such as konq, can do the former but not the later.
531 // We will do the second bit, as it's a no-op unless someone else explicitly
532 // accepts the event. We need to skip the scrollarea to do that,
533 // since it will just skip the events, both killing the drop, and
534 // not permitting us to forward it up the part hiearchy in our dragEnterEvent,
535 // etc. handlers
536 return QWidget::event(e);
537 case QEvent::StyleChange:
538 case QEvent::LayoutRequest: {
539 updateScrollBars();
540 return QAbstractScrollArea::event(e);
541 }
542 case QEvent::PaletteChange:
543 slotPaletteChanged();
544 return QScrollArea::event(e);
545 default:
546 return QScrollArea::event(e);
547 }
548}
549#endif
550
551KHTMLView::KHTMLView( KHTMLPart *part, QWidget *parent )
552 : QScrollArea( parent ), d( new KHTMLViewPrivate( this ) )
553{
554 m_medium = "screen";
555
556 m_part = part;
557
558 QScrollArea::setVerticalScrollBarPolicy(d->vpolicy);
559 QScrollArea::setHorizontalScrollBarPolicy(d->hpolicy);
560
561 init();
562 widget()->setMouseTracking(true);
563}
564
565KHTMLView::~KHTMLView()
566{
567 closeChildDialogs();
568 if (m_part)
569 {
570 DOM::DocumentImpl *doc = m_part->xmlDocImpl();
571 if (doc)
572 doc->detach();
573 }
574 delete d;
575}
576
577void KHTMLView::setPart(KHTMLPart *part)
578{
579 assert(part && !m_part);
580 m_part = part;
581}
582
583void KHTMLView::init()
584{
585 // Do not access the part here. It might not be fully constructed.
586
587 setFrameStyle(QFrame::NoFrame);
588 setFocusPolicy(Qt::StrongFocus);
589 viewport()->setFocusProxy(this);
590
591 _marginWidth = -1; // undefined
592 _marginHeight = -1;
593 _width = 0;
594 _height = 0;
595
596 installEventFilter(this);
597
598 setAcceptDrops(true);
599 if (!widget())
600 setWidget( new QWidget(this) );
601 widget()->setAttribute( Qt::WA_NoSystemBackground );
602
603 // Do *not* remove this attribute frivolously.
604 // You might not notice a change of behaviour in Debug builds
605 // but removing opaque events will make QWidget::scroll fail horribly
606 // in Release builds.
607 widget()->setAttribute( Qt::WA_OpaquePaintEvent );
608
609 verticalScrollBar()->setCursor( Qt::ArrowCursor );
610 horizontalScrollBar()->setCursor( Qt::ArrowCursor );
611
612 connect(&d->smoothScrollTimer, SIGNAL(timeout()), this, SLOT(scrollTick()));
613}
614
615void KHTMLView::resizeContentsToViewport()
616{
617 QSize s = viewport()->size();
618 resizeContents(s.width(), s.height());
619}
620
621
622// called by KHTMLPart::clear()
623void KHTMLView::clear()
624{
625 if (d->accessKeysEnabled && d->accessKeysActivated)
626 accessKeysTimeout();
627 viewport()->unsetCursor();
628 if ( d->cursorIconWidget )
629 d->cursorIconWidget->hide();
630 if (d->smoothScrolling)
631 d->stopScrolling();
632 d->reset();
633 QAbstractEventDispatcher::instance()->unregisterTimers(this);
634 emit cleared();
635
636 QScrollArea::setHorizontalScrollBarPolicy(d->hpolicy);
637 QScrollArea::setVerticalScrollBarPolicy(d->vpolicy);
638 verticalScrollBar()->setEnabled( false );
639 horizontalScrollBar()->setEnabled( false );
640
641}
642
643void KHTMLView::hideEvent(QHideEvent* e)
644{
645 QScrollArea::hideEvent(e);
646}
647
648void KHTMLView::showEvent(QShowEvent* e)
649{
650 QScrollArea::showEvent(e);
651}
652
653void KHTMLView::setMouseEventsTarget( QWidget* w )
654{
655 d->m_mouseEventsTarget = w;
656}
657
658QWidget* KHTMLView::mouseEventsTarget() const
659{
660 return d->m_mouseEventsTarget;
661}
662
663void KHTMLView::setClipHolder( QStack<QRegion>* ch )
664{
665 d->m_clipHolder = ch;
666}
667
668QStack<QRegion>* KHTMLView::clipHolder() const
669{
670 return d->m_clipHolder;
671}
672
673int KHTMLView::contentsWidth() const
674{
675 return widget() ? widget()->width() : 0;
676}
677
678int KHTMLView::contentsHeight() const
679{
680 return widget() ? widget()->height() : 0;
681}
682
683void KHTMLView::resizeContents(int w, int h)
684{
685 if (!widget())
686 return;
687 widget()->resize(w, h);
688 if (!widget()->isVisible())
689 updateScrollBars();
690}
691
692int KHTMLView::contentsX() const
693{
694 return d->contentsX;
695}
696
697int KHTMLView::contentsY() const
698{
699 return d->contentsY;
700}
701
702int KHTMLView::visibleWidth() const
703{
704 if (m_kwp->isRedirected()) {
705 // our RenderWidget knows better
706 if (RenderWidget* rw = m_kwp->renderWidget()) {
707 int ret = rw->width()-rw->paddingLeft()-rw->paddingRight()-rw->borderLeft()-rw->borderRight();
708 if (verticalScrollBar()->isVisible()) {
709 ret -= verticalScrollBar()->sizeHint().width();
710 ret = qMax(0, ret);
711 }
712 return ret;
713 }
714 }
715 return viewport()->width();
716}
717
718int KHTMLView::visibleHeight() const
719{
720 if (m_kwp->isRedirected()) {
721 // our RenderWidget knows better
722 if (RenderWidget* rw = m_kwp->renderWidget()) {
723 int ret = rw->height()-rw->paddingBottom()-rw->paddingTop()-rw->borderTop()-rw->borderBottom();
724 if (horizontalScrollBar()->isVisible()) {
725 ret -= horizontalScrollBar()->sizeHint().height();
726 ret = qMax(0, ret);
727 }
728 return ret;
729 }
730 }
731 return viewport()->height();
732}
733
734void KHTMLView::setContentsPos( int x, int y)
735{
736 horizontalScrollBar()->setValue( QApplication::isRightToLeft() ?
737 horizontalScrollBar()->maximum()-x : x );
738 verticalScrollBar()->setValue( y );
739}
740
741void KHTMLView::scrollBy(int x, int y)
742{
743 if (d->scrollTimerId)
744 d->newScrollTimer(this, 0);
745 horizontalScrollBar()->setValue( horizontalScrollBar()->value()+x );
746 verticalScrollBar()->setValue( verticalScrollBar()->value()+y );
747}
748
749QPoint KHTMLView::contentsToViewport(const QPoint& p) const
750{
751 return QPoint(p.x()-contentsX(), p.y()-contentsY());
752}
753
754void KHTMLView::contentsToViewport(int x, int y, int& cx, int& cy) const
755{
756 QPoint p(x,y);
757 p = contentsToViewport(p);
758 cx = p.x();
759 cy = p.y();
760}
761
762QPoint KHTMLView::viewportToContents(const QPoint& p) const
763{
764 return QPoint(p.x()+contentsX(), p.y()+contentsY());
765}
766
767void KHTMLView::viewportToContents(int x, int y, int& cx, int& cy) const
768{
769 QPoint p(x,y);
770 p = viewportToContents(p);
771 cx = p.x();
772 cy = p.y();
773}
774
775void KHTMLView::updateContents(int x, int y, int w, int h)
776{
777 applyTransforms(x, y, w, h);
778 if (m_kwp->isRedirected()) {
779 QPoint off = m_kwp->absolutePos();
780 KHTMLView* pview = m_part->parentPart()->view();
781 pview->updateContents(x+off.x(), y+off.y(), w, h);
782 } else
783 widget()->update(x, y, w, h);
784}
785
786void KHTMLView::updateContents( const QRect& r )
787{
788 updateContents( r.x(), r.y(), r.width(), r.height() );
789}
790
791void KHTMLView::repaintContents(int x, int y, int w, int h)
792{
793 applyTransforms(x, y, w, h);
794 if (m_kwp->isRedirected()) {
795 QPoint off = m_kwp->absolutePos();
796 KHTMLView* pview = m_part->parentPart()->view();
797 pview->repaintContents(x+off.x(), y+off.y(), w, h);
798 } else
799 widget()->repaint(x, y, w, h);
800}
801
802void KHTMLView::repaintContents( const QRect& r )
803{
804 repaintContents( r.x(), r.y(), r.width(), r.height() );
805}
806
807void KHTMLView::applyTransforms( int& x, int& y, int& w, int& h) const
808{
809 if (d->haveZoom()) {
810 const int z = d->zoomLevel;
811 x = x*z/100;
812 y = y*z/100;
813 w = w*z/100;
814 h = h*z/100;
815 }
816 x -= contentsX();
817 y -= contentsY();
818}
819
820void KHTMLView::revertTransforms( int& x, int& y, int& w, int& h) const
821{
822 x += contentsX();
823 y += contentsY();
824 if (d->haveZoom()) {
825 const int z = d->zoomLevel;
826 x = x*100/z;
827 y = y*100/z;
828 w = w*100/z;
829 h = h*100/z;
830 }
831}
832
833void KHTMLView::revertTransforms( int& x, int& y ) const
834{
835 int dummy = 0;
836 revertTransforms(x, y, dummy, dummy);
837}
838
839void KHTMLView::resizeEvent (QResizeEvent* /*e*/)
840{
841 updateScrollBars();
842
843 // If we didn't load anything, make white area as big as the view
844 if (!m_part->xmlDocImpl())
845 resizeContentsToViewport();
846
847 // Viewport-dependent media queries may cause us to need completely different style information.
848 if (m_part->xmlDocImpl() && m_part->xmlDocImpl()->styleSelector()->affectedByViewportChange()) {
849 m_part->xmlDocImpl()->updateStyleSelector();
850 }
851
852 if (d->layoutSchedulingEnabled)
853 layout();
854
855 QApplication::sendPostedEvents(viewport(), QEvent::Paint);
856
857 if ( m_part && m_part->xmlDocImpl() ) {
858 if (m_part->parentPart()) {
859 // sub-frame : queue the resize event until our toplevel is done layouting
860 khtml::ChildFrame *cf = m_part->parentPart()->frame( m_part );
861 if (cf && !cf->m_partContainerElement.isNull())
862 cf->m_partContainerElement.data()->postResizeEvent();
863 } else {
864 // toplevel : dispatch sub-frames'resize events before our own
865 HTMLPartContainerElementImpl::sendPostedResizeEvents();
866 m_part->xmlDocImpl()->dispatchWindowEvent( EventImpl::RESIZE_EVENT, false, false );
867 }
868 }
869}
870
871void KHTMLView::paintEvent( QPaintEvent *e )
872{
873 QRect r = e->rect();
874 QRect v(contentsX(), contentsY(), visibleWidth(), visibleHeight());
875 QPoint off(contentsX(),contentsY());
876 r.translate(off);
877 r = r.intersect(v);
878 if (!r.isValid() || r.isEmpty()) return;
879
880 QPainter p(widget());
881 p.translate(-off);
882
883 if (d->haveZoom()) {
884 p.scale( d->zoomLevel/100., d->zoomLevel/100.);
885
886 r.setX(r.x()*100/d->zoomLevel);
887 r.setY(r.y()*100/d->zoomLevel);
888 r.setWidth(r.width()*100/d->zoomLevel);
889 r.setHeight(r.height()*100/d->zoomLevel);
890 r.adjust(-1,-1,1,1);
891 }
892 p.setClipRect(r);
893
894 int ex = r.x();
895 int ey = r.y();
896 int ew = r.width();
897 int eh = r.height();
898
899 if(!m_part || !m_part->xmlDocImpl() || !m_part->xmlDocImpl()->renderer()) {
900 p.fillRect(ex, ey, ew, eh, palette().brush(QPalette::Active, QPalette::Base));
901 return;
902 } else if ( d->complete && static_cast<RenderCanvas*>(m_part->xmlDocImpl()->renderer())->needsLayout() ) {
903 // an external update request happens while we have a layout scheduled
904 unscheduleRelayout();
905 layout();
906 } else if (m_part->xmlDocImpl()->tokenizer()) {
907 m_part->xmlDocImpl()->tokenizer()->setNormalYieldDelay();
908 }
909
910 if (d->painting) {
911 kDebug( 6000 ) << "WARNING: paintEvent reentered! ";
912 kDebug( 6000 ) << kBacktrace();
913 return;
914 }
915 d->painting = true;
916
917 m_part->xmlDocImpl()->renderer()->layer()->paint(&p, r);
918
919 if (d->hasFrameset) {
920 NodeImpl *body = static_cast<HTMLDocumentImpl*>(m_part->xmlDocImpl())->body();
921 if(body && body->renderer() && body->id() == ID_FRAMESET)
922 static_cast<RenderFrameSet*>(body->renderer())->paintFrameSetRules(&p, r);
923 else
924 d->hasFrameset = false;
925 }
926
927 khtml::DrawContentsEvent event( &p, ex, ey, ew, eh );
928 QApplication::sendEvent( m_part, &event );
929
930 if (d->contentsMoving && !d->smoothScrolling && widget()->underMouse()) {
931 QMouseEvent *tempEvent = new QMouseEvent( QEvent::MouseMove, widget()->mapFromGlobal( QCursor::pos() ),
932 Qt::NoButton, Qt::NoButton, Qt::NoModifier );
933 QApplication::postEvent(widget(), tempEvent);
934 }
935#ifdef SPEED_DEBUG
936 if (d->firstRepaintPending && !m_part->parentPart()) {
937 kDebug(6080) << "FIRST PAINT:" << m_part->d->m_parsetime.elapsed();
938 }
939 d->firstRepaintPending = false;
940#endif
941 d->painting = false;
942}
943
944void KHTMLView::setMarginWidth(int w)
945{
946 // make it update the rendering area when set
947 _marginWidth = w;
948}
949
950void KHTMLView::setMarginHeight(int h)
951{
952 // make it update the rendering area when set
953 _marginHeight = h;
954}
955
956void KHTMLView::layout()
957{
958 if( m_part && m_part->xmlDocImpl() ) {
959 DOM::DocumentImpl *document = m_part->xmlDocImpl();
960
961 khtml::RenderCanvas* canvas = static_cast<khtml::RenderCanvas *>(document->renderer());
962 if ( !canvas ) return;
963
964 d->layoutSchedulingEnabled=false;
965 d->dirtyLayout = true;
966
967 // the reference object for the overflow property on canvas
968 RenderObject * ref = 0;
969 RenderObject* root = document->documentElement() ? document->documentElement()->renderer() : 0;
970
971 if (document->isHTMLDocument()) {
972 NodeImpl *body = static_cast<HTMLDocumentImpl*>(document)->body();
973 if(body && body->renderer() && body->id() == ID_FRAMESET) {
974 QScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
975 QScrollArea::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
976 body->renderer()->setNeedsLayout(true);
977 d->hasFrameset = true;
978 }
979 else if (root) // only apply body's overflow to canvas if root has a visible overflow
980 ref = (!body || root->style()->hidesOverflow()) ? root : body->renderer();
981 } else {
982 ref = root;
983 }
984 if (ref) {
985 if( ref->style()->overflowX() == OHIDDEN ) {
986 if (d->hpolicy == Qt::ScrollBarAsNeeded) QScrollArea::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
987 } else if (ref->style()->overflowX() == OSCROLL ) {
988 if (d->hpolicy == Qt::ScrollBarAsNeeded) QScrollArea::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
989 } else if (horizontalScrollBarPolicy() != d->hpolicy) {
990 QScrollArea::setHorizontalScrollBarPolicy(d->hpolicy);
991 }
992 if ( ref->style()->overflowY() == OHIDDEN ) {
993 if (d->vpolicy == Qt::ScrollBarAsNeeded) QScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
994 } else if (ref->style()->overflowY() == OSCROLL ) {
995 if (d->vpolicy == Qt::ScrollBarAsNeeded) QScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
996 } else if (verticalScrollBarPolicy() != d->vpolicy) {
997 QScrollArea::setVerticalScrollBarPolicy(d->vpolicy);
998 }
999 }
1000 d->needsFullRepaint = d->firstLayoutPending;
1001 if (_height != visibleHeight() || _width != visibleWidth()) {;
1002 d->needsFullRepaint = true;
1003 _height = visibleHeight();
1004 _width = visibleWidth();
1005 }
1006
1007 canvas->layout();
1008
1009 emit finishedLayout();
1010 if (d->firstLayoutPending) {
1011 // make sure firstLayoutPending is set to false now in case this layout
1012 // wasn't scheduled
1013 d->firstLayoutPending = false;
1014 verticalScrollBar()->setEnabled( true );
1015 horizontalScrollBar()->setEnabled( true );
1016 }
1017 d->layoutCounter++;
1018
1019 if (d->accessKeysEnabled && d->accessKeysActivated) {
1020 emit hideAccessKeys();
1021 displayAccessKeys();
1022 }
1023 }
1024 else
1025 _width = visibleWidth();
1026
1027 if (d->layoutTimerId)
1028 killTimer(d->layoutTimerId);
1029 d->layoutTimerId = 0;
1030 d->layoutSchedulingEnabled=true;
1031}
1032
1033void KHTMLView::closeChildDialogs()
1034{
1035 QList<QDialog *> dlgs = findChildren<QDialog *>();
1036 foreach (QDialog *dlg, dlgs)
1037 {
1038 KDialog* dlgbase = dynamic_cast<KDialog*>( dlg );
1039 if ( dlgbase ) {
1040 if ( dlgbase->testAttribute( Qt::WA_ShowModal ) ) {
1041 kDebug(6000) << "closeChildDialogs: closing dialog " << dlgbase;
1042 // close() ends up calling QButton::animateClick, which isn't immediate
1043 // we need something the exits the event loop immediately (#49068)
1044 dlgbase->reject();
1045 }
1046 }
1047 else
1048 {
1049 kWarning() << "closeChildDialogs: not a KDialog! Don't use QDialogs in KDE! " << static_cast<QWidget*>(dlg);
1050 static_cast<QWidget*>(dlg)->hide();
1051 }
1052 }
1053 d->m_dialogsAllowed = false;
1054}
1055
1056bool KHTMLView::dialogsAllowed() {
1057 bool allowed = d->m_dialogsAllowed;
1058 KHTMLPart* p = m_part->parentPart();
1059 if (p && p->view())
1060 allowed &= p->view()->dialogsAllowed();
1061 return allowed;
1062}
1063
1064void KHTMLView::closeEvent( QCloseEvent* ev )
1065{
1066 closeChildDialogs();
1067 QScrollArea::closeEvent( ev );
1068}
1069
1070void KHTMLView::setZoomLevel(int percent)
1071{
1072 percent = percent < 20 ? 20 : (percent > 800 ? 800 : percent);
1073 int oldpercent = d->zoomLevel;
1074 d->zoomLevel = percent;
1075 if (percent != oldpercent) {
1076 if (d->layoutSchedulingEnabled)
1077 layout();
1078 widget()->update();
1079 }
1080}
1081
1082int KHTMLView::zoomLevel() const
1083{
1084 return d->zoomLevel;
1085}
1086
1087void KHTMLView::setSmoothScrollingMode( SmoothScrollingMode m )
1088{
1089 d->smoothScrollMode = m;
1090 d->smoothScrollModeIsDefault = false;
1091 if (d->smoothScrolling && !m)
1092 d->stopScrolling();
1093}
1094
1095void KHTMLView::setSmoothScrollingModeDefault( SmoothScrollingMode m )
1096{
1097 // check for manual override
1098 if (!d->smoothScrollModeIsDefault)
1099 return;
1100 d->smoothScrollMode = m;
1101 if (d->smoothScrolling && !m)
1102 d->stopScrolling();
1103}
1104
1105KHTMLView::SmoothScrollingMode KHTMLView::smoothScrollingMode( ) const
1106{
1107 return d->smoothScrollMode;
1108}
1109
1110//
1111// Event Handling
1112//
1113/////////////////
1114
1115void KHTMLView::mousePressEvent( QMouseEvent *_mouse )
1116{
1117 if (!m_part->xmlDocImpl()) return;
1118 if (d->possibleTripleClick && ( _mouse->button() & Qt::MouseButtonMask ) == Qt::LeftButton)
1119 {
1120 mouseDoubleClickEvent( _mouse ); // it handles triple clicks too
1121 return;
1122 }
1123
1124 int xm = _mouse->x();
1125 int ym = _mouse->y();
1126 revertTransforms(xm, ym);
1127
1128 // kDebug( 6000 ) << "mousePressEvent: viewport=("<<_mouse->x()-contentsX()<<"/"<<_mouse->y()-contentsY()<<"), contents=(" << xm << "/" << ym << ")\n";
1129
1130 d->isDoubleClick = false;
1131
1132 DOM::NodeImpl::MouseEvent mev( _mouse->buttons(), DOM::NodeImpl::MousePress );
1133 m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev );
1134
1135 //kDebug(6000) << "innerNode="<<mev.innerNode.nodeName().string();
1136
1137 if ( (_mouse->button() == Qt::MidButton) &&
1138 !m_part->d->m_bOpenMiddleClick && !d->m_mouseScrollTimer &&
1139 mev.url.isNull() && (mev.innerNode.elementId() != ID_INPUT) ) {
1140 QPoint point = mapFromGlobal( _mouse->globalPos() );
1141
1142 d->m_mouseScroll_byX = 0;
1143 d->m_mouseScroll_byY = 0;
1144
1145 d->m_mouseScrollTimer = new QTimer( this );
1146 connect( d->m_mouseScrollTimer, SIGNAL(timeout()), this, SLOT(slotMouseScrollTimer()) );
1147
1148 if ( !d->m_mouseScrollIndicator ) {
1149 QPixmap pixmap( 48, 48 ), icon;
1150 pixmap.fill( QColor( qRgba( 127, 127, 127, 127 ) ) );
1151
1152 QPainter p( &pixmap );
1153 QStyleOption option;
1154
1155 option.rect.setRect( 16, 0, 16, 16 );
1156 QApplication::style()->drawPrimitive( QStyle::PE_IndicatorArrowUp, &option, &p );
1157 option.rect.setRect( 0, 16, 16, 16 );
1158 QApplication::style()->drawPrimitive( QStyle::PE_IndicatorArrowLeft, &option, &p );
1159 option.rect.setRect( 16, 32, 16, 16 );
1160 QApplication::style()->drawPrimitive( QStyle::PE_IndicatorArrowDown, &option, &p );
1161 option.rect.setRect( 32, 16, 16, 16 );
1162 QApplication::style()->drawPrimitive( QStyle::PE_IndicatorArrowRight, &option, &p );
1163 p.drawEllipse( 23, 23, 2, 2 );
1164
1165 d->m_mouseScrollIndicator = new QWidget( this );
1166 d->m_mouseScrollIndicator->setFixedSize( 48, 48 );
1167 QPalette palette;
1168 palette.setBrush( d->m_mouseScrollIndicator->backgroundRole(), QBrush( pixmap ) );
1169 d->m_mouseScrollIndicator->setPalette( palette );
1170 }
1171 d->m_mouseScrollIndicator->move( point.x()-24, point.y()-24 );
1172
1173 bool hasHorBar = visibleWidth() < contentsWidth();
1174 bool hasVerBar = visibleHeight() < contentsHeight();
1175
1176 KConfigGroup cg( KGlobal::config(), "HTML Settings" );
1177 if ( cg.readEntry( "ShowMouseScrollIndicator", true ) ) {
1178 d->m_mouseScrollIndicator->show();
1179 d->m_mouseScrollIndicator->unsetCursor();
1180
1181 QBitmap mask = d->m_mouseScrollIndicator->palette().brush(d->m_mouseScrollIndicator->backgroundRole()).texture().createHeuristicMask( true );
1182
1183 if ( hasHorBar && !hasVerBar ) {
1184 QBitmap bm( 16, 16 );
1185 bm.clear();
1186 QPainter painter( &mask );
1187 painter.drawPixmap( QRectF( 16, 0, bm.width(), bm.height() ), bm, bm.rect() );
1188 painter.drawPixmap( QRectF( 16, 32, bm.width(), bm.height() ), bm, bm.rect() );
1189 d->m_mouseScrollIndicator->setCursor( Qt::SizeHorCursor );
1190 }
1191 else if ( !hasHorBar && hasVerBar ) {
1192 QBitmap bm( 16, 16 );
1193 bm.clear();
1194 QPainter painter( &mask );
1195 painter.drawPixmap( QRectF( 0, 16, bm.width(), bm.height() ), bm, bm.rect() );
1196 painter.drawPixmap( QRectF( 32, 16, bm.width(), bm.height() ), bm, bm.rect() );
1197 d->m_mouseScrollIndicator->setCursor( Qt::SizeVerCursor );
1198 }
1199 else
1200 d->m_mouseScrollIndicator->setCursor( Qt::SizeAllCursor );
1201
1202 d->m_mouseScrollIndicator->setMask( mask );
1203 }
1204 else {
1205 if ( hasHorBar && !hasVerBar )
1206 viewport()->setCursor( Qt::SizeHorCursor );
1207 else if ( !hasHorBar && hasVerBar )
1208 viewport()->setCursor( Qt::SizeVerCursor );
1209 else
1210 viewport()->setCursor( Qt::SizeAllCursor );
1211 }
1212
1213 return;
1214 }
1215 else if ( d->m_mouseScrollTimer ) {
1216 delete d->m_mouseScrollTimer;
1217 d->m_mouseScrollTimer = 0;
1218
1219 if ( d->m_mouseScrollIndicator )
1220 d->m_mouseScrollIndicator->hide();
1221 }
1222
1223 if (d->clickCount > 0 &&
1224 QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() <= QApplication::startDragDistance())
1225 d->clickCount++;
1226 else {
1227 d->clickCount = 1;
1228 d->clickX = xm;
1229 d->clickY = ym;
1230 }
1231
1232 bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEDOWN_EVENT,mev.innerNode.handle(),mev.innerNonSharedNode.handle(),true,
1233 d->clickCount,_mouse,true,DOM::NodeImpl::MousePress);
1234
1235 if (!swallowEvent) {
1236 emit m_part->nodeActivated(mev.innerNode);
1237
1238 khtml::MousePressEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode );
1239 QApplication::sendEvent( m_part, &event );
1240 // we might be deleted after this
1241 }
1242}
1243
1244void KHTMLView::mouseDoubleClickEvent( QMouseEvent *_mouse )
1245{
1246 if(!m_part->xmlDocImpl()) return;
1247
1248 int xm = _mouse->x();
1249 int ym = _mouse->y();
1250 revertTransforms(xm, ym);
1251
1252 // kDebug( 6000 ) << "mouseDblClickEvent: x=" << xm << ", y=" << ym;
1253
1254 d->isDoubleClick = true;
1255
1256 DOM::NodeImpl::MouseEvent mev( _mouse->buttons(), DOM::NodeImpl::MouseDblClick );
1257 m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev );
1258
1259 // We do the same thing as mousePressEvent() here, since the DOM does not treat
1260 // single and double-click events as separate (only the detail, i.e. number of clicks differs)
1261 if (d->clickCount > 0 &&
1262 QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() <= QApplication::startDragDistance())
1263 d->clickCount++;
1264 else { // shouldn't happen, if Qt has the same criterias for double clicks.
1265 d->clickCount = 1;
1266 d->clickX = xm;
1267 d->clickY = ym;
1268 }
1269 bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEDOWN_EVENT,mev.innerNode.handle(),mev.innerNonSharedNode.handle(),true,
1270 d->clickCount,_mouse,true,DOM::NodeImpl::MouseDblClick);
1271
1272 if (!swallowEvent) {
1273 khtml::MouseDoubleClickEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode, d->clickCount );
1274 QApplication::sendEvent( m_part, &event );
1275 }
1276
1277 d->possibleTripleClick=true;
1278 QTimer::singleShot(QApplication::doubleClickInterval(),this,SLOT(tripleClickTimeout()));
1279}
1280
1281void KHTMLView::tripleClickTimeout()
1282{
1283 d->possibleTripleClick = false;
1284 d->clickCount = 0;
1285}
1286
1287static bool targetOpensNewWindow(KHTMLPart *part, QString target)
1288{
1289 if (!target.isEmpty() && (target.toLower() != "_top") &&
1290 (target.toLower() != "_self") && (target.toLower() != "_parent")) {
1291 if (target.toLower() == "_blank")
1292 return true;
1293 else {
1294 while (part->parentPart())
1295 part = part->parentPart();
1296 if (!part->frameExists(target))
1297 return true;
1298 }
1299 }
1300 return false;
1301}
1302
1303void KHTMLView::mouseMoveEvent( QMouseEvent * _mouse )
1304{
1305 if ( d->m_mouseScrollTimer ) {
1306 QPoint point = mapFromGlobal( _mouse->globalPos() );
1307
1308 int deltaX = point.x() - d->m_mouseScrollIndicator->x() - 24;
1309 int deltaY = point.y() - d->m_mouseScrollIndicator->y() - 24;
1310
1311 (deltaX > 0) ? d->m_mouseScroll_byX = 1 : d->m_mouseScroll_byX = -1;
1312 (deltaY > 0) ? d->m_mouseScroll_byY = 1 : d->m_mouseScroll_byY = -1;
1313
1314 double adX = qAbs(deltaX)/30.0;
1315 double adY = qAbs(deltaY)/30.0;
1316
1317 d->m_mouseScroll_byX = qMax(qMin(d->m_mouseScroll_byX * int(adX*adX), SHRT_MAX), SHRT_MIN);
1318 d->m_mouseScroll_byY = qMax(qMin(d->m_mouseScroll_byY * int(adY*adY), SHRT_MAX), SHRT_MIN);
1319
1320 if (d->m_mouseScroll_byX == 0 && d->m_mouseScroll_byY == 0) {
1321 d->m_mouseScrollTimer->stop();
1322 }
1323 else if (!d->m_mouseScrollTimer->isActive()) {
1324 d->m_mouseScrollTimer->start( 20 );
1325 }
1326 }
1327
1328 if(!m_part->xmlDocImpl()) return;
1329
1330 int xm = _mouse->x();
1331 int ym = _mouse->y();
1332 revertTransforms(xm, ym);
1333
1334 DOM::NodeImpl::MouseEvent mev( _mouse->buttons(), DOM::NodeImpl::MouseMove );
1335 // Do not modify :hover/:active state while mouse is pressed.
1336 m_part->xmlDocImpl()->prepareMouseEvent( _mouse->buttons() /*readonly ?*/, xm, ym, &mev );
1337
1338 // kDebug(6000) << "mouse move: " << _mouse->pos()
1339 // << " button " << _mouse->button()
1340 // << " state " << _mouse->state() << endl;
1341
1342 DOM::NodeImpl* target = mev.innerNode.handle();
1343 DOM::NodeImpl* fn = m_part->xmlDocImpl()->focusNode();
1344
1345 // a widget may be the real target of this event (e.g. if a scrollbar's slider is being moved)
1346 if (d->m_mouseEventsTarget && fn && fn->renderer() && fn->renderer()->isWidget())
1347 target = fn;
1348
1349 bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEMOVE_EVENT,target,mev.innerNonSharedNode.handle(),false,
1350 0,_mouse,true,DOM::NodeImpl::MouseMove);
1351
1352 if (d->clickCount > 0 &&
1353 QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() > QApplication::startDragDistance()) {
1354 d->clickCount = 0; // moving the mouse outside the threshold invalidates the click
1355 }
1356
1357 khtml::RenderObject* r = target ? target->renderer() : 0;
1358 bool setCursor = true;
1359 bool forceDefault = false;
1360 if (r && r->isWidget()) {
1361 RenderWidget* rw = static_cast<RenderWidget*>(r);
1362 KHTMLWidget* kw = qobject_cast<KHTMLView*>(rw->widget())? dynamic_cast<KHTMLWidget*>(rw->widget()) : 0;
1363 if (kw && kw->m_kwp->isRedirected())
1364 setCursor = false;
1365 else if (QLineEdit* le = qobject_cast<QLineEdit*>(rw->widget())) {
1366 QList<QWidget*> wl = qFindChildren<QWidget *>( le, "KLineEditButton" );
1367 // force arrow cursor above lineedit clear button
1368 foreach (QWidget*w, wl) {
1369 if (w->underMouse()) {
1370 forceDefault = true;
1371 break;
1372 }
1373 }
1374 }
1375 else if (QTextEdit* te = qobject_cast<QTextEdit*>(rw->widget())) {
1376 if (te->verticalScrollBar()->underMouse() || te->horizontalScrollBar()->underMouse())
1377 forceDefault = true;
1378 }
1379 }
1380 khtml::RenderStyle* style = (r && r->style()) ? r->style() : 0;
1381 QCursor c;
1382 LinkCursor linkCursor = LINK_NORMAL;
1383 switch (!forceDefault ? (style ? style->cursor() : CURSOR_AUTO) : CURSOR_DEFAULT) {
1384 case CURSOR_AUTO:
1385 if ( r && r->isText() && ((m_part->d->m_bMousePressed && m_part->d->editor_context.m_beganSelectingText) ||
1386 !r->isPointInsideSelection(xm, ym, m_part->caret())) )
1387 c = QCursor(Qt::IBeamCursor);
1388 if ( mev.url.length() && m_part->settings()->changeCursor() ) {
1389 c = m_part->urlCursor();
1390 if (mev.url.string().startsWith("mailto:") && mev.url.string().indexOf('@')>0)
1391 linkCursor = LINK_MAILTO;
1392 else
1393 if ( targetOpensNewWindow( m_part, mev.target.string() ) )
1394 linkCursor = LINK_NEWWINDOW;
1395 }
1396
1397 if (r && r->isFrameSet() && !static_cast<RenderFrameSet*>(r)->noResize())
1398 c = QCursor(static_cast<RenderFrameSet*>(r)->cursorShape());
1399
1400 break;
1401 case CURSOR_CROSS:
1402 c = QCursor(Qt::CrossCursor);
1403 break;
1404 case CURSOR_POINTER:
1405 c = m_part->urlCursor();
1406 if (mev.url.string().startsWith("mailto:") && mev.url.string().indexOf('@')>0)
1407 linkCursor = LINK_MAILTO;
1408 else
1409 if ( targetOpensNewWindow( m_part, mev.target.string() ) )
1410 linkCursor = LINK_NEWWINDOW;
1411 break;
1412 case CURSOR_PROGRESS:
1413 c = QCursor(Qt::BusyCursor); // working_cursor
1414 break;
1415 case CURSOR_MOVE:
1416 case CURSOR_ALL_SCROLL:
1417 c = QCursor(Qt::SizeAllCursor);
1418 break;
1419 case CURSOR_E_RESIZE:
1420 case CURSOR_W_RESIZE:
1421 case CURSOR_EW_RESIZE:
1422 c = QCursor(Qt::SizeHorCursor);
1423 break;
1424 case CURSOR_N_RESIZE:
1425 case CURSOR_S_RESIZE:
1426 case CURSOR_NS_RESIZE:
1427 c = QCursor(Qt::SizeVerCursor);
1428 break;
1429 case CURSOR_NE_RESIZE:
1430 case CURSOR_SW_RESIZE:
1431 case CURSOR_NESW_RESIZE:
1432 c = QCursor(Qt::SizeBDiagCursor);
1433 break;
1434 case CURSOR_NW_RESIZE:
1435 case CURSOR_SE_RESIZE:
1436 case CURSOR_NWSE_RESIZE:
1437 c = QCursor(Qt::SizeFDiagCursor);
1438 break;
1439 case CURSOR_TEXT:
1440 c = QCursor(Qt::IBeamCursor);
1441 break;
1442 case CURSOR_WAIT:
1443 c = QCursor(Qt::WaitCursor);
1444 break;
1445 case CURSOR_HELP:
1446 c = QCursor(Qt::WhatsThisCursor);
1447 break;
1448 case CURSOR_DEFAULT:
1449 break;
1450 case CURSOR_NONE:
1451 case CURSOR_NOT_ALLOWED:
1452 c = QCursor(Qt::ForbiddenCursor);
1453 break;
1454 case CURSOR_ROW_RESIZE:
1455 c = QCursor(Qt::SplitVCursor);
1456 break;
1457 case CURSOR_COL_RESIZE:
1458 c = QCursor(Qt::SplitHCursor);
1459 break;
1460 case CURSOR_VERTICAL_TEXT:
1461 case CURSOR_CONTEXT_MENU:
1462 case CURSOR_NO_DROP:
1463 case CURSOR_CELL:
1464 case CURSOR_COPY:
1465 case CURSOR_ALIAS:
1466 c = QCursor(Qt::ArrowCursor);
1467 break;
1468 }
1469
1470 if (!setCursor && style && style->cursor() != CURSOR_AUTO)
1471 setCursor = true;
1472
1473 QWidget* vp = viewport();
1474 for (KHTMLPart* p = m_part; p; p = p->parentPart())
1475 if (!p->parentPart())
1476 vp = p->view()->viewport();
1477 if ( setCursor && vp->cursor().handle() != c.handle() ) {
1478 if( c.shape() == Qt::ArrowCursor) {
1479 for (KHTMLPart* p = m_part; p; p = p->parentPart())
1480 p->view()->viewport()->unsetCursor();
1481 }
1482 else {
1483 vp->setCursor( c );
1484 }
1485 }
1486
1487 if ( linkCursor!=LINK_NORMAL && isVisible() && hasFocus() ) {
1488#ifdef Q_WS_X11
1489
1490 if( !d->cursorIconWidget ) {
1491#ifdef Q_WS_X11
1492 d->cursorIconWidget = new QLabel( 0, Qt::X11BypassWindowManagerHint );
1493 XSetWindowAttributes attr;
1494 attr.save_under = True;
1495 XChangeWindowAttributes( QX11Info::display(), d->cursorIconWidget->winId(), CWSaveUnder, &attr );
1496#else
1497 d->cursorIconWidget = new QLabel( NULL, NULL );
1498 //TODO
1499#endif
1500 }
1501
1502 // Update the pixmap if need be.
1503 if (linkCursor != d->cursorIconType) {
1504 d->cursorIconType = linkCursor;
1505 QString cursorIcon;
1506 switch (linkCursor)
1507 {
1508 case LINK_MAILTO: cursorIcon = "mail-message-new"; break;
1509 case LINK_NEWWINDOW: cursorIcon = "window-new"; break;
1510 default: cursorIcon = "dialog-error"; break;
1511 }
1512
1513 QPixmap icon_pixmap = KHTMLGlobal::iconLoader()->loadIcon( cursorIcon, KIconLoader::Small, 0, KIconLoader::DefaultState, QStringList(), 0, true );
1514
1515 d->cursorIconWidget->resize( icon_pixmap.width(), icon_pixmap.height());
1516 d->cursorIconWidget->setMask( icon_pixmap.createMaskFromColor(Qt::transparent));
1517 d->cursorIconWidget->setPixmap( icon_pixmap);
1518 d->cursorIconWidget->update();
1519 }
1520
1521 QPoint c_pos = QCursor::pos();
1522 d->cursorIconWidget->move( c_pos.x() + 15, c_pos.y() + 15 );
1523#ifdef Q_WS_X11
1524 XRaiseWindow( QX11Info::display(), d->cursorIconWidget->winId());
1525 QApplication::flush();
1526#elif defined(Q_WS_WIN)
1527 SetWindowPos( d->cursorIconWidget->winId(), HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE );
1528#else
1529 //TODO?
1530#endif
1531 d->cursorIconWidget->show();
1532#endif
1533 }
1534 else if ( d->cursorIconWidget )
1535 d->cursorIconWidget->hide();
1536
1537 if (r && r->isWidget()) {
1538 _mouse->ignore();
1539 }
1540
1541 if (!swallowEvent) {
1542 khtml::MouseMoveEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode );
1543 QApplication::sendEvent( m_part, &event );
1544 }
1545}
1546
1547void KHTMLView::mouseReleaseEvent( QMouseEvent * _mouse )
1548{
1549 bool swallowEvent = false;
1550
1551 int xm = _mouse->x();
1552 int ym = _mouse->y();
1553 revertTransforms(xm, ym);
1554
1555 DOM::NodeImpl::MouseEvent mev( _mouse->buttons(), DOM::NodeImpl::MouseRelease );
1556
1557 if ( m_part->xmlDocImpl() )
1558 {
1559 m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev );
1560
1561 DOM::NodeImpl* target = mev.innerNode.handle();
1562 DOM::NodeImpl* fn = m_part->xmlDocImpl()->focusNode();
1563
1564 // a widget may be the real target of this event (e.g. if a scrollbar's slider is being moved)
1565 if (d->m_mouseEventsTarget && fn && fn->renderer() && fn->renderer()->isWidget())
1566 target = fn;
1567
1568 swallowEvent = dispatchMouseEvent(EventImpl::MOUSEUP_EVENT,target,mev.innerNonSharedNode.handle(),true,
1569 d->clickCount,_mouse,false,DOM::NodeImpl::MouseRelease);
1570
1571 // clear our sticky event target on any mouseRelease event
1572 if (d->m_mouseEventsTarget)
1573 d->m_mouseEventsTarget = 0;
1574
1575 if (d->clickCount > 0 &&
1576 QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() <= QApplication::startDragDistance()) {
1577 QMouseEvent me(d->isDoubleClick ? QEvent::MouseButtonDblClick : QEvent::MouseButtonRelease,
1578 _mouse->pos(), _mouse->button(), _mouse->buttons(), _mouse->modifiers());
1579 dispatchMouseEvent(EventImpl::CLICK_EVENT, mev.innerNode.handle(),mev.innerNonSharedNode.handle(),true,
1580 d->clickCount, &me, true, DOM::NodeImpl::MouseRelease);
1581 }
1582
1583 khtml::RenderObject* r = target ? target->renderer() : 0;
1584 if (r && r->isWidget())
1585 _mouse->ignore();
1586 }
1587
1588 if (!swallowEvent) {
1589 khtml::MouseReleaseEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode );
1590 QApplication::sendEvent( m_part, &event );
1591 }
1592}
1593
1594// returns true if event should be swallowed
1595bool KHTMLView::dispatchKeyEvent( QKeyEvent *_ke )
1596{
1597 if (!m_part->xmlDocImpl())
1598 return false;
1599 // Pressing and releasing a key should generate keydown, keypress and keyup events
1600 // Holding it down should generated keydown, keypress (repeatedly) and keyup events
1601 // The problem here is that Qt generates two autorepeat events (keyrelease+keypress)
1602 // for autorepeating, while DOM wants only one autorepeat event (keypress), so one
1603 // of the Qt events shouldn't be passed to DOM, but it should be still filtered
1604 // out if DOM would filter the autorepeat event. Additional problem is that Qt keyrelease
1605 // events don't have text() set (Qt bug?), so DOM often would ignore the keypress event
1606 // if it was created using Qt keyrelease, but Qt autorepeat keyrelease comes
1607 // before Qt autorepeat keypress (i.e. problem whether to filter it out or not).
1608 // The solution is to filter out and postpone the Qt autorepeat keyrelease until
1609 // the following Qt keypress event comes. If DOM accepts the DOM keypress event,
1610 // the postponed event will be simply discarded. If not, it will be passed to keyPressEvent()
1611 // again, and here it will be ignored.
1612 //
1613 // Qt: Press | Release(autorepeat) Press(autorepeat) etc. | Release
1614 // DOM: Down + Press | (nothing) Press | Up
1615
1616 // It's also possible to get only Releases. E.g. the release of alt-tab,
1617 // or when the keypresses get captured by an accel.
1618
1619 if( _ke == d->postponed_autorepeat ) // replayed event
1620 {
1621 return false;
1622 }
1623
1624 if( _ke->type() == QEvent::KeyPress )
1625 {
1626 if( !_ke->isAutoRepeat())
1627 {
1628 bool ret = dispatchKeyEventHelper( _ke, false ); // keydown
1629 // don't send keypress even if keydown was blocked, like IE (and unlike Mozilla)
1630 if( !ret && dispatchKeyEventHelper( _ke, true )) // keypress
1631 ret = true;
1632 return ret;
1633 }
1634 else // autorepeat
1635 {
1636 bool ret = dispatchKeyEventHelper( _ke, true ); // keypress
1637 if( !ret && d->postponed_autorepeat )
1638 keyPressEvent( d->postponed_autorepeat );
1639 delete d->postponed_autorepeat;
1640 d->postponed_autorepeat = NULL;
1641 return ret;
1642 }
1643 }
1644 else // QEvent::KeyRelease
1645 {
1646 // Discard postponed "autorepeat key-release" events that didn't see
1647 // a keypress after them (e.g. due to QAccel)
1648 delete d->postponed_autorepeat;
1649 d->postponed_autorepeat = 0;
1650
1651 if( !_ke->isAutoRepeat()) {
1652 return dispatchKeyEventHelper( _ke, false ); // keyup
1653 }
1654 else
1655 {
1656 d->postponed_autorepeat = new QKeyEvent( _ke->type(), _ke->key(), _ke->modifiers(),
1657 _ke->text(), _ke->isAutoRepeat(), _ke->count());
1658 if( _ke->isAccepted())
1659 d->postponed_autorepeat->accept();
1660 else
1661 d->postponed_autorepeat->ignore();
1662 return true;
1663 }
1664 }
1665}
1666
1667// returns true if event should be swallowed
1668bool KHTMLView::dispatchKeyEventHelper( QKeyEvent *_ke, bool keypress )
1669{
1670 DOM::NodeImpl* keyNode = m_part->xmlDocImpl()->focusNode();
1671 if (keyNode) {
1672 return keyNode->dispatchKeyEvent(_ke, keypress);
1673 } else { // no focused node, send to document
1674 return m_part->xmlDocImpl()->dispatchKeyEvent(_ke, keypress);
1675 }
1676}
1677
1678void KHTMLView::keyPressEvent( QKeyEvent *_ke )
1679{
1680 // If CTRL was hit, be prepared for access keys
1681 if (d->accessKeysEnabled && _ke->key() == Qt::Key_Control && !(_ke->modifiers() & ~Qt::ControlModifier) && !d->accessKeysActivated)
1682 {
1683 d->accessKeysPreActivate=true;
1684 _ke->accept();
1685 return;
1686 }
1687
1688 if (_ke->key() == Qt::Key_Shift && !(_ke->modifiers() & ~Qt::ShiftModifier))
1689 d->scrollSuspendPreActivate=true;
1690
1691 // accesskey handling needs to be done before dispatching, otherwise e.g. lineedits
1692 // may eat the event
1693
1694 if (d->accessKeysEnabled && d->accessKeysActivated)
1695 {
1696 int state = ( _ke->modifiers() & ( Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier ));
1697 if ( state==0 || state==Qt::ShiftModifier ) {
1698 if (_ke->key() != Qt::Key_Shift)
1699 accessKeysTimeout();
1700 handleAccessKey( _ke );
1701 _ke->accept();
1702 return;
1703 }
1704 accessKeysTimeout();
1705 _ke->accept();
1706 return;
1707 }
1708
1709 if ( dispatchKeyEvent( _ke )) {
1710 // If either keydown or keypress was accepted by a widget, or canceled by JS, stop here.
1711 _ke->accept();
1712 return;
1713 }
1714
1715 int offs = (viewport()->height() < 30) ? viewport()->height() : 30; // ### ??
1716 if (_ke->modifiers() & Qt::ShiftModifier)
1717 switch(_ke->key())
1718 {
1719 case Qt::Key_Space:
1720 verticalScrollBar()->setValue( verticalScrollBar()->value() -viewport()->height() + offs );
1721 if(d->scrollSuspended)
1722 d->newScrollTimer(this, 0);
1723 break;
1724
1725 case Qt::Key_Down:
1726 case Qt::Key_J:
1727 d->adjustScroller(this, KHTMLViewPrivate::ScrollDown, KHTMLViewPrivate::ScrollUp);
1728 break;
1729
1730 case Qt::Key_Up:
1731 case Qt::Key_K:
1732 d->adjustScroller(this, KHTMLViewPrivate::ScrollUp, KHTMLViewPrivate::ScrollDown);
1733 break;
1734
1735 case Qt::Key_Left:
1736 case Qt::Key_H:
1737 d->adjustScroller(this, KHTMLViewPrivate::ScrollLeft, KHTMLViewPrivate::ScrollRight);
1738 break;
1739
1740 case Qt::Key_Right:
1741 case Qt::Key_L:
1742 d->adjustScroller(this, KHTMLViewPrivate::ScrollRight, KHTMLViewPrivate::ScrollLeft);
1743 break;
1744 }
1745 else
1746 switch ( _ke->key() )
1747 {
1748 case Qt::Key_Down:
1749 case Qt::Key_J:
1750 if (!d->scrollTimerId || d->scrollSuspended)
1751 verticalScrollBar()->setValue( verticalScrollBar()->value()+10 );
1752 if (d->scrollTimerId)
1753 d->newScrollTimer(this, 0);
1754 break;
1755
1756 case Qt::Key_Space:
1757 case Qt::Key_PageDown:
1758 d->shouldSmoothScroll = true;
1759 verticalScrollBar()->setValue( verticalScrollBar()->value() +viewport()->height() - offs );
1760 if(d->scrollSuspended)
1761 d->newScrollTimer(this, 0);
1762 break;
1763
1764 case Qt::Key_Up:
1765 case Qt::Key_K:
1766 if (!d->scrollTimerId || d->scrollSuspended)
1767 verticalScrollBar()->setValue( verticalScrollBar()->value()-10 );
1768 if (d->scrollTimerId)
1769 d->newScrollTimer(this, 0);
1770 break;
1771
1772 case Qt::Key_PageUp:
1773 d->shouldSmoothScroll = true;
1774 verticalScrollBar()->setValue( verticalScrollBar()->value() -viewport()->height() + offs );
1775 if(d->scrollSuspended)
1776 d->newScrollTimer(this, 0);
1777 break;
1778 case Qt::Key_Right:
1779 case Qt::Key_L:
1780 if (!d->scrollTimerId || d->scrollSuspended)
1781 horizontalScrollBar()->setValue( horizontalScrollBar()->value()+10 );
1782 if (d->scrollTimerId)
1783 d->newScrollTimer(this, 0);
1784 break;
1785
1786 case Qt::Key_Left:
1787 case Qt::Key_H:
1788 if (!d->scrollTimerId || d->scrollSuspended)
1789 horizontalScrollBar()->setValue( horizontalScrollBar()->value()-10 );
1790 if (d->scrollTimerId)
1791 d->newScrollTimer(this, 0);
1792 break;
1793 case Qt::Key_Enter:
1794 case Qt::Key_Return:
1795 // ### FIXME:
1796 // or even better to HTMLAnchorElementImpl::event()
1797 if (m_part->xmlDocImpl()) {
1798 NodeImpl *n = m_part->xmlDocImpl()->focusNode();
1799 if (n)
1800 n->setActive();
1801 }
1802 break;
1803 case Qt::Key_Home:
1804 verticalScrollBar()->setValue( 0 );
1805 horizontalScrollBar()->setValue( 0 );
1806 if(d->scrollSuspended)
1807 d->newScrollTimer(this, 0);
1808 break;
1809 case Qt::Key_End:
1810 verticalScrollBar()->setValue( contentsHeight() - visibleHeight() );
1811 if(d->scrollSuspended)
1812 d->newScrollTimer(this, 0);
1813 break;
1814 case Qt::Key_Shift:
1815 // what are you doing here?
1816 _ke->ignore();
1817 return;
1818 default:
1819 if (d->scrollTimerId)
1820 d->newScrollTimer(this, 0);
1821 _ke->ignore();
1822 return;
1823 }
1824
1825 _ke->accept();
1826}
1827
1828void KHTMLView::keyReleaseEvent(QKeyEvent *_ke)
1829{
1830 if( d->scrollSuspendPreActivate && _ke->key() != Qt::Key_Shift )
1831 d->scrollSuspendPreActivate = false;
1832 if( _ke->key() == Qt::Key_Shift && d->scrollSuspendPreActivate && !(_ke->modifiers() & Qt::ShiftModifier))
1833 if (d->scrollTimerId) {
1834 d->scrollSuspended = !d->scrollSuspended;
1835 if (d->scrollSuspended)
1836 d->stopScrolling();
1837 }
1838
1839 if (d->accessKeysEnabled)
1840 {
1841 if (d->accessKeysPreActivate && _ke->key() != Qt::Key_Control)
1842 d->accessKeysPreActivate=false;
1843 if (d->accessKeysPreActivate && !(_ke->modifiers() & Qt::ControlModifier))
1844 {
1845 displayAccessKeys();
1846 m_part->setStatusBarText(i18n("Access Keys activated"),KHTMLPart::BarOverrideText);
1847 d->accessKeysActivated = true;
1848 d->accessKeysPreActivate = false;
1849 _ke->accept();
1850 return;
1851 }
1852 else if (d->accessKeysActivated)
1853 {
1854 accessKeysTimeout();
1855 _ke->accept();
1856 return;
1857 }
1858 }
1859
1860 // Send keyup event
1861 if ( dispatchKeyEvent( _ke ) )
1862 {
1863 _ke->accept();
1864 return;
1865 }
1866
1867 QScrollArea::keyReleaseEvent(_ke);
1868}
1869
1870bool KHTMLView::focusNextPrevChild( bool next )
1871{
1872 // Now try to find the next child
1873 if (m_part->xmlDocImpl() && focusNextPrevNode(next))
1874 {
1875 if (m_part->xmlDocImpl()->focusNode())
1876 kDebug() << "focusNode.name: "
1877 << m_part->xmlDocImpl()->focusNode()->nodeName().string() << endl;
1878 return true; // focus node found
1879 }
1880
1881 // If we get here, pass tabbing control up to the next/previous child in our parent
1882 d->pseudoFocusNode = KHTMLViewPrivate::PFNone;
1883 if (m_part->parentPart() && m_part->parentPart()->view())
1884 return m_part->parentPart()->view()->focusNextPrevChild(next);
1885
1886 return QWidget::focusNextPrevChild(next);
1887}
1888
1889void KHTMLView::doAutoScroll()
1890{
1891 QPoint pos = QCursor::pos();
1892 QPoint off;
1893 KHTMLView* v = m_kwp->isRedirected() ? m_kwp->rootViewPos(off) : this;
1894 pos = v->viewport()->mapFromGlobal( pos );
1895 pos -= off;
1896 int xm, ym;
1897 viewportToContents(pos.x(), pos.y(), xm, ym); // ###
1898
1899 pos = QPoint(pos.x() - viewport()->x(), pos.y() - viewport()->y());
1900 if ( (pos.y() < 0) || (pos.y() > visibleHeight()) ||
1901 (pos.x() < 0) || (pos.x() > visibleWidth()) )
1902 {
1903 ensureVisible( xm, ym, 0, 5 );
1904
1905#ifndef KHTML_NO_SELECTION
1906 // extend the selection while scrolling
1907 DOM::Node innerNode;
1908 if (m_part->isExtendingSelection()) {
1909 RenderObject::NodeInfo renderInfo(true/*readonly*/, false/*active*/);
1910 m_part->xmlDocImpl()->renderer()->layer()
1911 ->nodeAtPoint(renderInfo, xm, ym);
1912 innerNode = renderInfo.innerNode();
1913 }/*end if*/
1914
1915 if (innerNode.handle() && innerNode.handle()->renderer()
1916 && innerNode.handle()->renderer()->shouldSelect()) {
1917 m_part->extendSelectionTo(xm, ym, innerNode);
1918 }/*end if*/
1919#endif // KHTML_NO_SELECTION
1920 }
1921}
1922
1923// KHTML defines its own stacking order for any object and thus takes
1924// control of widget painting whenever it can. This is called "redirection".
1925//
1926// Redirected widgets are placed off screen. When they are declared as a child of our view (ChildPolished event),
1927// an event filter is installed, so as to catch any paint event and translate them as update() of the view's main widget.
1928//
1929// Painting also happens spontaneously within widgets. In this case, the widget would update() parts of itself.
1930// While this ordinarily results in a paintEvent being schedduled, it is not the case with off screen widgets.
1931// Thus update() is monitored by using the mechanism that deffers any update call happening during a paint event,
1932// transforming it into a posted UpdateLater event. Hence the need to set Qt::WA_WState_InPaintEvent on redirected widgets.
1933//
1934// Once the UpdateLater event has been received, Qt::WA_WState_InPaintEvent is removed and the process continues
1935// with the update of the corresponding rect on the view. That in turn will make our painting subsystem render()
1936// the widget at the correct stacking position.
1937//
1938// For non-redirected (e.g. external) widgets, z-order is honoured through masking. cf.RenderLayer::updateWidgetMasks
1939
1940static void handleWidget(QWidget* w, KHTMLView* view, bool recurse=true)
1941{
1942 if (w->isWindow())
1943 return;
1944
1945 if (!qobject_cast<QFrame*>(w))
1946 w->setAttribute( Qt::WA_NoSystemBackground );
1947
1948 w->setAttribute(Qt::WA_WState_InPaintEvent);
1949
1950 if (!(w->objectName() == "KLineEditButton"))
1951 w->setAttribute(Qt::WA_OpaquePaintEvent);
1952
1953 w->installEventFilter(view);
1954
1955 if (!recurse)
1956 return;
1957 if (qobject_cast<KHTMLView*>(w)) {
1958 handleWidget(static_cast<KHTMLView*>(w)->widget(), view, false);
1959 handleWidget(static_cast<KHTMLView*>(w)->horizontalScrollBar(), view, false);
1960 handleWidget(static_cast<KHTMLView*>(w)->verticalScrollBar(), view, false);
1961 return;
1962 }
1963
1964 QObjectList children = w->children();
1965 foreach (QObject* object, children) {
1966 QWidget *widget = qobject_cast<QWidget*>(object);
1967 if (widget)
1968 handleWidget(widget, view);
1969 }
1970}
1971
1972class KHTMLBackingStoreHackWidget : public QWidget
1973{
1974public:
1975 void publicEvent(QEvent *e)
1976 {
1977 QWidget::event(e);
1978 }
1979};
1980
1981bool KHTMLView::viewportEvent ( QEvent * e )
1982{
1983 switch (e->type()) {
1984 // those must not be dispatched to the specialized handlers
1985 // as widgetEvent() already took care of that
1986 case QEvent::MouseButtonPress:
1987 case QEvent::MouseButtonRelease:
1988 case QEvent::MouseButtonDblClick:
1989 case QEvent::MouseMove:
1990#ifndef QT_NO_WHEELEVENT
1991 case QEvent::Wheel:
1992#endif
1993 case QEvent::ContextMenu:
1994 case QEvent::DragEnter:
1995 case QEvent::DragMove:
1996 case QEvent::DragLeave:
1997 case QEvent::Drop:
1998 return false;
1999 default:
2000 break;
2001 }
2002 return QScrollArea::viewportEvent(e);
2003}
2004
2005static void setInPaintEventFlag(QWidget* w, bool b = true, bool recurse=true)
2006{
2007 w->setAttribute(Qt::WA_WState_InPaintEvent, b);
2008
2009 if (!recurse)
2010 return;
2011 if (qobject_cast<KHTMLView*>(w)) {
2012 setInPaintEventFlag(static_cast<KHTMLView*>(w)->widget(), b, false);
2013 setInPaintEventFlag(static_cast<KHTMLView*>(w)->horizontalScrollBar(), b, false);
2014 setInPaintEventFlag(static_cast<KHTMLView*>(w)->verticalScrollBar(), b, false);
2015 return;
2016 }
2017
2018 foreach(QObject* cw, w->children()) {
2019 if (cw->isWidgetType() && ! static_cast<QWidget*>(cw)->isWindow()
2020 && !(static_cast<QWidget*>(cw)->windowModality() & Qt::ApplicationModal)) {
2021 setInPaintEventFlag(static_cast<QWidget*>(cw), b);
2022 }
2023 }
2024}
2025
2026bool KHTMLView::eventFilter(QObject *o, QEvent *e)
2027{
2028 if ( e->type() == QEvent::ShortcutOverride ) {
2029 QKeyEvent* ke = (QKeyEvent*) e;
2030 if (m_part->isEditable() || m_part->isCaretMode()
2031 || (m_part->xmlDocImpl() && m_part->xmlDocImpl()->focusNode()
2032 && m_part->xmlDocImpl()->focusNode()->isContentEditable())) {
2033 if ( (ke->modifiers() & Qt::ControlModifier) || (ke->modifiers() & Qt::ShiftModifier) ) {
2034 switch ( ke->key() ) {
2035 case Qt::Key_Left:
2036 case Qt::Key_Right:
2037 case Qt::Key_Up:
2038 case Qt::Key_Down:
2039 case Qt::Key_Home:
2040 case Qt::Key_End:
2041 ke->accept();
2042 return true;
2043 default:
2044 break;
2045 }
2046 }
2047 }
2048 }
2049
2050 if ( e->type() == QEvent::Leave ) {
2051 if ( d->cursorIconWidget )
2052 d->cursorIconWidget->hide();
2053 m_part->resetHoverText();
2054 }
2055
2056 QWidget *view = widget();
2057 if (o == view) {
2058 if (widgetEvent(e))
2059 return true;
2060 else if (e->type() == QEvent::Resize) {
2061 updateScrollBars();
2062 return false;
2063 }
2064 } else if (o->isWidgetType()) {
2065 QWidget *v = static_cast<QWidget *>(o);
2066 QWidget *c = v;
2067 while (v && v != view) {
2068 c = v;
2069 v = v->parentWidget();
2070 }
2071 KHTMLWidget* k = dynamic_cast<KHTMLWidget*>(c);
2072 if (v && k && k->m_kwp->isRedirected()) {
2073 bool block = false;
2074 bool isUpdate = false;
2075 QWidget *w = static_cast<QWidget *>(o);
2076 switch(e->type()) {
2077 case QEvent::UpdateRequest: {
2078 // implicitly call qt_syncBackingStore(w)
2079 static_cast<KHTMLBackingStoreHackWidget *>(w)->publicEvent(e);
2080 block = true;
2081 break;
2082 }
2083 case QEvent::UpdateLater:
2084 isUpdate = true;
2085 // no break;
2086 case QEvent::Paint:
2087 if (!allowWidgetPaintEvents) {
2088 // eat the event. Like this we can control exactly when the widget
2089 // gets repainted.
2090 block = true;
2091 int x = 0, y = 0;
2092 QWidget *v = w;
2093 while (v && v->parentWidget() != view) {
2094 x += v->x();
2095 y += v->y();
2096 v = v->parentWidget();
2097 }
2098
2099 QPoint ap = k->m_kwp->absolutePos();
2100 x += ap.x();
2101 y += ap.y();
2102
2103 QRect pr = isUpdate ? static_cast<QUpdateLaterEvent*>(e)->region().boundingRect() : static_cast<QPaintEvent*>(e)->rect();
2104 bool asap = !d->contentsMoving && qobject_cast<QAbstractScrollArea*>(c);
2105
2106 if (isUpdate) {
2107 setInPaintEventFlag(w, false);
2108 if (asap)
2109 w->repaint(static_cast<QUpdateLaterEvent*>(e)->region());
2110 else
2111 w->update(static_cast<QUpdateLaterEvent*>(e)->region());
2112 setInPaintEventFlag(w);
2113 }
2114
2115 // QScrollView needs fast repaints
2116 if ( asap && !isUpdate && !d->painting && m_part->xmlDocImpl() && m_part->xmlDocImpl()->renderer() &&
2117 !static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer())->needsLayout() ) {
2118 repaintContents(x + pr.x(), y + pr.y(),
2119 pr.width(), pr.height()+1); // ### investigate that +1 (shows up when
2120 // updating e.g a textarea's blinking cursor)
2121 } else if (!d->painting) {
2122 scheduleRepaint(x + pr.x(), y + pr.y(),
2123 pr.width(), pr.height()+1, asap);
2124 }
2125 }
2126 break;
2127 case QEvent::MouseMove:
2128 case QEvent::MouseButtonPress:
2129 case QEvent::MouseButtonRelease:
2130 case QEvent::MouseButtonDblClick: {
2131
2132 if (0 && w->parentWidget() == view && !qobject_cast<QScrollBar*>(w) && !::qobject_cast<QScrollBar *>(w)) {
2133 QMouseEvent *me = static_cast<QMouseEvent *>(e);
2134 QPoint pt = w->mapTo( view, me->pos());
2135 QMouseEvent me2(me->type(), pt, me->button(), me->buttons(), me->modifiers());
2136
2137 if (e->type() == QEvent::MouseMove)
2138 mouseMoveEvent(&me2);
2139 else if(e->type() == QEvent::MouseButtonPress)
2140 mousePressEvent(&me2);
2141 else if(e->type() == QEvent::MouseButtonRelease)
2142 mouseReleaseEvent(&me2);
2143 else
2144 mouseDoubleClickEvent(&me2);
2145 block = true;
2146 }
2147 break;
2148 }
2149 case QEvent::KeyPress:
2150 case QEvent::KeyRelease:
2151 if (w->parentWidget() == view && !qobject_cast<QScrollBar*>(w)) {
2152 QKeyEvent *ke = static_cast<QKeyEvent *>(e);
2153 if (e->type() == QEvent::KeyPress) {
2154 keyPressEvent(ke);
2155 ke->accept();
2156 } else{
2157 keyReleaseEvent(ke);
2158 ke->accept();
2159 }
2160 block = true;
2161 }
2162
2163 if (qobject_cast<KUrlRequester*>(w->parentWidget()) &&
2164 e->type() == QEvent::KeyPress) {
2165 // Since keypress events on the upload widget will
2166 // be forwarded to the lineedit anyway,
2167 // block the original copy at this level to prevent
2168 // double-emissions of events it doesn't accept
2169 e->ignore();
2170 block = true;
2171 }
2172
2173 break;
2174 case QEvent::FocusIn:
2175 case QEvent::FocusOut: {
2176 QPoint dummy;
2177 KHTMLView* root = m_kwp->rootViewPos(dummy);
2178 if (!root)
2179 root = this;
2180 block = static_cast<QFocusEvent*>(e)->reason() != Qt::MouseFocusReason || root->underMouse();
2181 break;
2182 }
2183 default:
2184 break;
2185 }
2186 if (block) {
2187 //qDebug("eating event");
2188 return true;
2189 }
2190 }
2191 }
2192
2193// kDebug(6000) <<"passing event on to sv event filter object=" << o->className() << " event=" << e->type();
2194 return QScrollArea::eventFilter(o, e);
2195}
2196
2197bool KHTMLView::widgetEvent(QEvent* e)
2198{
2199 switch (e->type()) {
2200 case QEvent::MouseButtonPress:
2201 case QEvent::MouseButtonRelease:
2202 case QEvent::MouseButtonDblClick:
2203 case QEvent::MouseMove:
2204 case QEvent::Paint:
2205#ifndef QT_NO_WHEELEVENT
2206 case QEvent::Wheel:
2207#endif
2208 case QEvent::ContextMenu:
2209 case QEvent::DragEnter:
2210 case QEvent::DragMove:
2211 case QEvent::DragLeave:
2212 case QEvent::Drop:
2213 return QFrame::event(e);
2214 case QEvent::ChildPolished: {
2215 // we need to install an event filter on all children of the widget() to
2216 // be able to get correct stacking of children within the document.
2217 QObject *c = static_cast<QChildEvent *>(e)->child();
2218 if (c->isWidgetType()) {
2219 QWidget *w = static_cast<QWidget *>(c);
2220 // don't install the event filter on toplevels
2221 if (!(w->windowFlags() & Qt::Window) && !(w->windowModality() & Qt::ApplicationModal)) {
2222 KHTMLWidget* k = dynamic_cast<KHTMLWidget*>(w);
2223 if (k && k->m_kwp->isRedirected()) {
2224 w->unsetCursor();
2225 handleWidget(w, this);
2226 }
2227 }
2228 }
2229 break;
2230 }
2231 case QEvent::Move: {
2232 if (static_cast<QMoveEvent*>(e)->pos() != QPoint(0,0)) {
2233 widget()->move(0,0);
2234 updateScrollBars();
2235 return true;
2236 }
2237 break;
2238 }
2239 default:
2240 break;
2241 }
2242 return false;
2243}
2244
2245bool KHTMLView::hasLayoutPending()
2246{
2247 return d->layoutTimerId && !d->firstLayoutPending;
2248}
2249
2250DOM::NodeImpl *KHTMLView::nodeUnderMouse() const
2251{
2252 return d->underMouse;
2253}
2254
2255DOM::NodeImpl *KHTMLView::nonSharedNodeUnderMouse() const
2256{
2257 return d->underMouseNonShared;
2258}
2259
2260bool KHTMLView::scrollTo(const QRect &bounds)
2261{
2262 d->scrollingSelf = true; // so scroll events get ignored
2263
2264 int x, y, xe, ye;
2265 x = bounds.left();
2266 y = bounds.top();
2267 xe = bounds.right();
2268 ye = bounds.bottom();
2269
2270 //kDebug(6000)<<"scrolling coords: x="<<x<<" y="<<y<<" width="<<xe-x<<" height="<<ye-y;
2271
2272 int deltax;
2273 int deltay;
2274
2275 int curHeight = visibleHeight();
2276 int curWidth = visibleWidth();
2277
2278 if (ye-y>curHeight-d->borderY)
2279 ye = y + curHeight - d->borderY;
2280
2281 if (xe-x>curWidth-d->borderX)
2282 xe = x + curWidth - d->borderX;
2283
2284 // is xpos of target left of the view's border?
2285 if (x < contentsX() + d->borderX )
2286 deltax = x - contentsX() - d->borderX;
2287 // is xpos of target right of the view's right border?
2288 else if (xe + d->borderX > contentsX() + curWidth)
2289 deltax = xe + d->borderX - ( contentsX() + curWidth );
2290 else
2291 deltax = 0;
2292
2293 // is ypos of target above upper border?
2294 if (y < contentsY() + d->borderY)
2295 deltay = y - contentsY() - d->borderY;
2296 // is ypos of target below lower border?
2297 else if (ye + d->borderY > contentsY() + curHeight)
2298 deltay = ye + d->borderY - ( contentsY() + curHeight );
2299 else
2300 deltay = 0;
2301
2302 int maxx = curWidth-d->borderX;
2303 int maxy = curHeight-d->borderY;
2304
2305 int scrollX, scrollY;
2306
2307 scrollX = deltax > 0 ? (deltax > maxx ? maxx : deltax) : deltax == 0 ? 0 : (deltax>-maxx ? deltax : -maxx);
2308 scrollY = deltay > 0 ? (deltay > maxy ? maxy : deltay) : deltay == 0 ? 0 : (deltay>-maxy ? deltay : -maxy);
2309
2310 if (contentsX() + scrollX < 0)
2311 scrollX = -contentsX();
2312 else if (contentsWidth() - visibleWidth() - contentsX() < scrollX)
2313 scrollX = contentsWidth() - visibleWidth() - contentsX();
2314
2315 if (contentsY() + scrollY < 0)
2316 scrollY = -contentsY();
2317 else if (contentsHeight() - visibleHeight() - contentsY() < scrollY)
2318 scrollY = contentsHeight() - visibleHeight() - contentsY();
2319
2320 horizontalScrollBar()->setValue( horizontalScrollBar()->value()+scrollX );
2321 verticalScrollBar()->setValue( verticalScrollBar()->value()+scrollY );
2322
2323 d->scrollingSelf = false;
2324
2325 if ( (abs(deltax)<=maxx) && (abs(deltay)<=maxy) )
2326 return true;
2327 else return false;
2328
2329}
2330
2331bool KHTMLView::focusNextPrevNode(bool next)
2332{
2333 // Sets the focus node of the document to be the node after (or if
2334 // next is false, before) the current focus node. Only nodes that
2335 // are selectable (i.e. for which isFocusable() returns true) are
2336 // taken into account, and the order used is that specified in the
2337 // HTML spec (see DocumentImpl::nextFocusNode() and
2338 // DocumentImpl::previousFocusNode() for details).
2339
2340 DocumentImpl *doc = m_part->xmlDocImpl();
2341 NodeImpl *oldFocusNode = doc->focusNode();
2342
2343 // See whether we're in the middle of a detach, or hiding of the
2344 // widget. In this case, we will just clear focus, being careful not to emit events
2345 // or update rendering. Doing this also prevents the code below from going bonkers with
2346 // oldFocusNode not actually being focusable, etc.
2347 if (oldFocusNode) {
2348 if ((oldFocusNode->renderer() && !oldFocusNode->renderer()->parent())
2349 || !oldFocusNode->isTabFocusable()) {
2350 doc->quietResetFocus();
2351 return true;
2352 }
2353 }
2354
2355#if 1
2356 // If the user has scrolled the document, then instead of picking
2357 // the next focusable node in the document, use the first one that
2358 // is within the visible area (if possible).
2359 if (d->scrollBarMoved)
2360 {
2361 NodeImpl *toFocus;
2362 if (next)
2363 toFocus = doc->nextFocusNode(oldFocusNode);
2364 else
2365 toFocus = doc->previousFocusNode(oldFocusNode);
2366
2367 if (!toFocus && oldFocusNode) {
2368 if (next)
2369 toFocus = doc->nextFocusNode(NULL);
2370 else
2371 toFocus = doc->previousFocusNode(NULL);
2372 }
2373
2374 while (toFocus && toFocus != oldFocusNode)
2375 {
2376
2377 QRect focusNodeRect = toFocus->getRect();
2378 if ((focusNodeRect.left() > contentsX()) && (focusNodeRect.right() < contentsX() + visibleWidth()) &&
2379 (focusNodeRect.top() > contentsY()) && (focusNodeRect.bottom() < contentsY() + visibleHeight())) {
2380 {
2381 QRect r = toFocus->getRect();
2382 ensureVisible( r.right(), r.bottom());
2383 ensureVisible( r.left(), r.top());
2384 d->scrollBarMoved = false;
2385 d->tabMovePending = false;
2386 d->lastTabbingDirection = next;
2387 d->pseudoFocusNode = KHTMLViewPrivate::PFNone;
2388 m_part->xmlDocImpl()->setFocusNode(toFocus);
2389 Node guard(toFocus);
2390 if (!toFocus->hasOneRef() )
2391 {
2392 emit m_part->nodeActivated(Node(toFocus));
2393 }
2394 return true;
2395 }
2396 }
2397 if (next)
2398 toFocus = doc->nextFocusNode(toFocus);
2399 else
2400 toFocus = doc->previousFocusNode(toFocus);
2401
2402 if (!toFocus && oldFocusNode)
2403 {
2404 if (next)
2405 {
2406 toFocus = doc->nextFocusNode(NULL);
2407 }
2408 else
2409 {
2410 toFocus = doc->previousFocusNode(NULL);
2411 }
2412 }
2413 }
2414
2415 d->scrollBarMoved = false;
2416 }
2417#endif
2418
2419 if (!oldFocusNode && d->pseudoFocusNode == KHTMLViewPrivate::PFNone)
2420 {
2421 ensureVisible(contentsX(), next?0:contentsHeight());
2422 d->scrollBarMoved = false;
2423 d->pseudoFocusNode = next?KHTMLViewPrivate::PFTop:KHTMLViewPrivate::PFBottom;
2424 return true;
2425 }
2426
2427 NodeImpl *newFocusNode = NULL;
2428
2429 if (d->tabMovePending && next != d->lastTabbingDirection)
2430 {
2431 //kDebug ( 6000 ) << " tab move pending and tabbing direction changed!\n";
2432 newFocusNode = oldFocusNode;
2433 }
2434 else if (next)
2435 {
2436 if (oldFocusNode || d->pseudoFocusNode == KHTMLViewPrivate::PFTop )
2437 newFocusNode = doc->nextFocusNode(oldFocusNode);
2438 }
2439 else
2440 {
2441 if (oldFocusNode || d->pseudoFocusNode == KHTMLViewPrivate::PFBottom )
2442 newFocusNode = doc->previousFocusNode(oldFocusNode);
2443 }
2444
2445 bool targetVisible = false;
2446 if (!newFocusNode)
2447 {
2448 if ( next )
2449 {
2450 targetVisible = scrollTo(QRect(contentsX()+visibleWidth()/2,contentsHeight()-d->borderY,0,0));
2451 }
2452 else
2453 {
2454 targetVisible = scrollTo(QRect(contentsX()+visibleWidth()/2,d->borderY,0,0));
2455 }
2456 }
2457 else
2458 {
2459 // if it's an editable element, activate the caret
2460 if (!m_part->isCaretMode() && newFocusNode->isContentEditable()) {
2461 kDebug(6200) << "show caret! fn: " << newFocusNode->nodeName().string() << endl;
2462 m_part->clearCaretRectIfNeeded();
2463 m_part->d->editor_context.m_selection.moveTo(Position(newFocusNode, 0L));
2464 m_part->setCaretVisible(true);
2465 } else {
2466 m_part->setCaretVisible(false);
2467 kDebug(6200) << "hide caret! fn: " << newFocusNode->nodeName().string() << endl;
2468 }
2469 m_part->notifySelectionChanged();
2470
2471 targetVisible = scrollTo(newFocusNode->getRect());
2472 }
2473
2474 if (targetVisible)
2475 {
2476 //kDebug ( 6000 ) << " target reached.\n";
2477 d->tabMovePending = false;
2478
2479 m_part->xmlDocImpl()->setFocusNode(newFocusNode);
2480 if (newFocusNode)
2481 {
2482 Node guard(newFocusNode);
2483 if (!newFocusNode->hasOneRef() )
2484 {
2485 emit m_part->nodeActivated(Node(newFocusNode));
2486 }
2487 return true;
2488 }
2489 else
2490 {
2491 d->pseudoFocusNode = next?KHTMLViewPrivate::PFBottom:KHTMLViewPrivate::PFTop;
2492 return false;
2493 }
2494 }
2495 else
2496 {
2497 if (!d->tabMovePending)
2498 d->lastTabbingDirection = next;
2499 d->tabMovePending = true;
2500 return true;
2501 }
2502}
2503
2504void KHTMLView::displayAccessKeys()
2505{
2506 QVector< QChar > taken;
2507 displayAccessKeys( NULL, this, taken, false );
2508 displayAccessKeys( NULL, this, taken, true );
2509}
2510
2511void KHTMLView::displayAccessKeys( KHTMLView* caller, KHTMLView* origview, QVector< QChar >& taken, bool use_fallbacks )
2512{
2513 QMap< ElementImpl*, QChar > fallbacks;
2514 if( use_fallbacks )
2515 fallbacks = buildFallbackAccessKeys();
2516 for( NodeImpl* n = m_part->xmlDocImpl(); n != NULL; n = n->traverseNextNode()) {
2517 if( n->isElementNode()) {
2518 ElementImpl* en = static_cast< ElementImpl* >( n );
2519 DOMString s = en->getAttribute( ATTR_ACCESSKEY );
2520 QString accesskey;
2521 if( s.length() == 1 ) {
2522 QChar a = s.string()[ 0 ].toUpper();
2523 if( qFind( taken.begin(), taken.end(), a ) == taken.end()) // !contains
2524 accesskey = a;
2525 }
2526 if( accesskey.isNull() && fallbacks.contains( en )) {
2527 QChar a = fallbacks[ en ].toUpper();
2528 if( qFind( taken.begin(), taken.end(), a ) == taken.end()) // !contains
2529 accesskey = QString( "<qt><i>" ) + a + "</i></qt>";
2530 }
2531 if( !accesskey.isNull()) {
2532 QRect rec=en->getRect();
2533 QLabel *lab=new QLabel(accesskey,widget());
2534 lab->setAttribute(Qt::WA_DeleteOnClose);
2535 lab->setObjectName("KHTMLAccessKey");
2536 connect( origview, SIGNAL(hideAccessKeys()), lab, SLOT(close()) );
2537 connect( this, SIGNAL(repaintAccessKeys()), lab, SLOT(repaint()));
2538 lab->setPalette(QToolTip::palette());
2539 lab->setLineWidth(2);
2540 lab->setFrameStyle(QFrame::Box | QFrame::Plain);
2541 lab->setMargin(3);
2542 lab->adjustSize();
2543 lab->setParent( widget() );
2544 lab->setAutoFillBackground(true);
2545 lab->move(
2546 qMin(rec.left()+rec.width()/2 - contentsX(), contentsWidth() - lab->width()),
2547 qMin(rec.top()+rec.height()/2 - contentsY(), contentsHeight() - lab->height()));
2548 lab->show();
2549 taken.append( accesskey[ 0 ] );
2550 }
2551 }
2552 }
2553 if( use_fallbacks )
2554 return;
2555
2556 QList<KParts::ReadOnlyPart*> frames = m_part->frames();
2557 foreach( KParts::ReadOnlyPart* cur, frames ) {
2558 if( !qobject_cast<KHTMLPart*>(cur) )
2559 continue;
2560 KHTMLPart* part = static_cast< KHTMLPart* >( cur );
2561 if( part->view() && part->view() != caller )
2562 part->view()->displayAccessKeys( this, origview, taken, use_fallbacks );
2563 }
2564
2565 // pass up to the parent
2566 if (m_part->parentPart() && m_part->parentPart()->view()
2567 && m_part->parentPart()->view() != caller)
2568 m_part->parentPart()->view()->displayAccessKeys( this, origview, taken, use_fallbacks );
2569}
2570
2571bool KHTMLView::isScrollingFromMouseWheel() const
2572{
2573 return d->scrollingFromWheel != QPoint(-1,-1);
2574}
2575
2576void KHTMLView::accessKeysTimeout()
2577{
2578 d->accessKeysActivated=false;
2579 d->accessKeysPreActivate = false;
2580 m_part->setStatusBarText(QString(), KHTMLPart::BarOverrideText);
2581 emit hideAccessKeys();
2582}
2583
2584// Handling of the HTML accesskey attribute.
2585bool KHTMLView::handleAccessKey( const QKeyEvent* ev )
2586{
2587// Qt interprets the keyevent also with the modifiers, and ev->text() matches that,
2588// but this code must act as if the modifiers weren't pressed
2589 QChar c;
2590 if( ev->key() >= Qt::Key_A && ev->key() <= Qt::Key_Z )
2591 c = 'A' + ev->key() - Qt::Key_A;
2592 else if( ev->key() >= Qt::Key_0 && ev->key() <= Qt::Key_9 )
2593 c = '0' + ev->key() - Qt::Key_0;
2594 else {
2595 // TODO fake XKeyEvent and XLookupString ?
2596 // This below seems to work e.g. for eacute though.
2597 if( ev->text().length() == 1 )
2598 c = ev->text()[ 0 ];
2599 }
2600 if( c.isNull())
2601 return false;
2602 return focusNodeWithAccessKey( c );
2603}
2604
2605bool KHTMLView::focusNodeWithAccessKey( QChar c, KHTMLView* caller )
2606{
2607 DocumentImpl *doc = m_part->xmlDocImpl();
2608 if( !doc )
2609 return false;
2610 ElementImpl* node = doc->findAccessKeyElement( c );
2611 if( !node ) {
2612 QList<KParts::ReadOnlyPart*> frames = m_part->frames();
2613 foreach( KParts::ReadOnlyPart* cur, frames ) {
2614 if( !qobject_cast<KHTMLPart*>(cur) )
2615 continue;
2616 KHTMLPart* part = static_cast< KHTMLPart* >( cur );
2617 if( part->view() && part->view() != caller
2618 && part->view()->focusNodeWithAccessKey( c, this ))
2619 return true;
2620 }
2621 // pass up to the parent
2622 if (m_part->parentPart() && m_part->parentPart()->view()
2623 && m_part->parentPart()->view() != caller
2624 && m_part->parentPart()->view()->focusNodeWithAccessKey( c, this ))
2625 return true;
2626 if( caller == NULL ) { // the active frame (where the accesskey was pressed)
2627 const QMap< ElementImpl*, QChar > fallbacks = buildFallbackAccessKeys();
2628 for( QMap< ElementImpl*, QChar >::ConstIterator it = fallbacks.begin();
2629 it != fallbacks.end();
2630 ++it )
2631 if( *it == c ) {
2632 node = it.key();
2633 break;
2634 }
2635 }
2636 if( node == NULL )
2637 return false;
2638 }
2639
2640 // Scroll the view as necessary to ensure that the new focus node is visible
2641
2642 QRect r = node->getRect();
2643 ensureVisible( r.right(), r.bottom());
2644 ensureVisible( r.left(), r.top());
2645
2646 Node guard( node );
2647 if( node->isFocusable()) {
2648 if (node->id()==ID_LABEL) {
2649 // if Accesskey is a label, give focus to the label's referrer.
2650 node=static_cast<ElementImpl *>(static_cast< HTMLLabelElementImpl* >( node )->getFormElement());
2651 if (!node) return true;
2652 guard = node;
2653 }
2654 // Set focus node on the document
2655 m_part->xmlDocImpl()->setFocusNode(node);
2656
2657 if( node != NULL && node->hasOneRef()) // deleted, only held by guard
2658 return true;
2659 emit m_part->nodeActivated(Node(node));
2660 if( node != NULL && node->hasOneRef())
2661 return true;
2662 }
2663
2664 switch( node->id()) {
2665 case ID_A:
2666 static_cast< HTMLAnchorElementImpl* >( node )->click();
2667 break;
2668 case ID_INPUT:
2669 static_cast< HTMLInputElementImpl* >( node )->click();
2670 break;
2671 case ID_BUTTON:
2672 static_cast< HTMLButtonElementImpl* >( node )->click();
2673 break;
2674 case ID_AREA:
2675 static_cast< HTMLAreaElementImpl* >( node )->click();
2676 break;
2677 case ID_TEXTAREA:
2678 break; // just focusing it is enough
2679 case ID_LEGEND:
2680 // TODO
2681 break;
2682 }
2683 return true;
2684}
2685
2686static QString getElementText( NodeImpl* start, bool after )
2687{
2688 QString ret; // nextSibling(), to go after e.g. </select>
2689 for( NodeImpl* n = after ? start->nextSibling() : start->traversePreviousNode();
2690 n != NULL;
2691 n = after ? n->traverseNextNode() : n->traversePreviousNode()) {
2692 if( n->isTextNode()) {
2693 if( after )
2694 ret += static_cast< TextImpl* >( n )->toString().string();
2695 else
2696 ret.prepend( static_cast< TextImpl* >( n )->toString().string());
2697 } else {
2698 switch( n->id()) {
2699 case ID_A:
2700 case ID_FONT:
2701 case ID_TT:
2702 case ID_U:
2703 case ID_B:
2704 case ID_I:
2705 case ID_S:
2706 case ID_STRIKE:
2707 case ID_BIG:
2708 case ID_SMALL:
2709 case ID_EM:
2710 case ID_STRONG:
2711 case ID_DFN:
2712 case ID_CODE:
2713 case ID_SAMP:
2714 case ID_KBD:
2715 case ID_VAR:
2716 case ID_CITE:
2717 case ID_ABBR:
2718 case ID_ACRONYM:
2719 case ID_SUB:
2720 case ID_SUP:
2721 case ID_SPAN:
2722 case ID_NOBR:
2723 case ID_WBR:
2724 break;
2725 case ID_TD:
2726 if( ret.trimmed().isEmpty())
2727 break;
2728 // fall through
2729 default:
2730 return ret.simplified();
2731 }
2732 }
2733 }
2734 return ret.simplified();
2735}
2736
2737static QMap< NodeImpl*, QString > buildLabels( NodeImpl* start )
2738{
2739 QMap< NodeImpl*, QString > ret;
2740 for( NodeImpl* n = start;
2741 n != NULL;
2742 n = n->traverseNextNode()) {
2743 if( n->id() == ID_LABEL ) {
2744 HTMLLabelElementImpl* label = static_cast< HTMLLabelElementImpl* >( n );
2745 NodeImpl* labelfor = label->getFormElement();
2746 if( labelfor )
2747 ret[ labelfor ] = label->innerText().string().simplified();
2748 }
2749 }
2750 return ret;
2751}
2752
2753namespace khtml {
2754struct AccessKeyData {
2755 ElementImpl* element;
2756 QString text;
2757 QString url;
2758 int priority; // 10(highest) - 0(lowest)
2759};
2760}
2761
2762QMap< ElementImpl*, QChar > KHTMLView::buildFallbackAccessKeys() const
2763{
2764 // build a list of all possible candidate elements that could use an accesskey
2765 QLinkedList< AccessKeyData > data; // Note: this has to be a list type that keep iterators valid
2766 // when other entries are removed
2767 QMap< NodeImpl*, QString > labels = buildLabels( m_part->xmlDocImpl());
2768 QMap< QString, QChar > hrefs;
2769
2770 for( NodeImpl* n = m_part->xmlDocImpl();
2771 n != NULL;
2772 n = n->traverseNextNode()) {
2773 if( n->isElementNode()) {
2774 ElementImpl* element = static_cast< ElementImpl* >( n );
2775 if( element->renderer() == NULL )
2776 continue; // not visible
2777 QString text;
2778 QString url;
2779 int priority = 0;
2780 bool ignore = false;
2781 bool text_after = false;
2782 bool text_before = false;
2783 switch( element->id()) {
2784 case ID_A:
2785 url = khtml::parseURL(element->getAttribute(ATTR_HREF)).string();
2786 if( url.isEmpty()) // doesn't have href, it's only an anchor
2787 continue;
2788 text = static_cast< HTMLElementImpl* >( element )->innerText().string().simplified();
2789 priority = 2;
2790 break;
2791 case ID_INPUT: {
2792 HTMLInputElementImpl* in = static_cast< HTMLInputElementImpl* >( element );
2793 switch( in->inputType()) {
2794 case HTMLInputElementImpl::SUBMIT:
2795 text = in->value().string();
2796 if( text.isEmpty())
2797 text = i18n( "Submit" );
2798 priority = 7;
2799 break;
2800 case HTMLInputElementImpl::IMAGE:
2801 text = in->altText().string();
2802 priority = 7;
2803 break;
2804 case HTMLInputElementImpl::BUTTON:
2805 text = in->value().string();
2806 priority = 5;
2807 break;
2808 case HTMLInputElementImpl::RESET:
2809 text = in->value().string();
2810 if( text.isEmpty())
2811 text = i18n( "Reset" );
2812 priority = 5;
2813 break;
2814 case HTMLInputElementImpl::HIDDEN:
2815 ignore = true;
2816 break;
2817 case HTMLInputElementImpl::CHECKBOX:
2818 case HTMLInputElementImpl::RADIO:
2819 text_after = true;
2820 priority = 5;
2821 break;
2822 case HTMLInputElementImpl::TEXT:
2823 case HTMLInputElementImpl::PASSWORD:
2824 case HTMLInputElementImpl::FILE:
2825 text_before = true;
2826 priority = 5;
2827 break;
2828 default:
2829 priority = 5;
2830 break;
2831 }
2832 break;
2833 }
2834 case ID_BUTTON:
2835 text = static_cast< HTMLElementImpl* >( element )->innerText().string().simplified();
2836 switch( static_cast< HTMLButtonElementImpl* >( element )->buttonType()) {
2837 case HTMLButtonElementImpl::SUBMIT:
2838 if( text.isEmpty())
2839 text = i18n( "Submit" );
2840 priority = 7;
2841 break;
2842 case HTMLButtonElementImpl::RESET:
2843 if( text.isEmpty())
2844 text = i18n( "Reset" );
2845 priority = 5;
2846 break;
2847 default:
2848 priority = 5;
2849 break;
2850 }
2851 break;
2852 case ID_SELECT: // these don't have accesskey attribute, but quick access may be handy
2853 text_before = true;
2854 text_after = true;
2855 priority = 5;
2856 break;
2857 case ID_FRAME:
2858 ignore = true;
2859 break;
2860 default:
2861 ignore = !element->isFocusable();
2862 priority = 2;
2863 break;
2864 }
2865 if( ignore )
2866 continue;
2867
2868 // build map of manually assigned accesskeys and their targets
2869 DOMString akey = element->getAttribute( ATTR_ACCESSKEY );
2870 if( akey.length() == 1 ) {
2871 hrefs[url] = akey.string()[ 0 ].toUpper();
2872 continue; // has accesskey set, ignore
2873 }
2874 if( text.isNull() && labels.contains( element ))
2875 text = labels[ element ];
2876 if( text.isNull() && text_before )
2877 text = getElementText( element, false );
2878 if( text.isNull() && text_after )
2879 text = getElementText( element, true );
2880 text = text.trimmed();
2881 // increase priority of items which have explicitly specified accesskeys in the config
2882 const QList< QPair< QString, QChar > > priorities
2883 = m_part->settings()->fallbackAccessKeysAssignments();
2884 for( QList< QPair< QString, QChar > >::ConstIterator it = priorities.begin();
2885 it != priorities.end();
2886 ++it ) {
2887 if( text == (*it).first )
2888 priority = 10;
2889 }
2890 AccessKeyData tmp = { element, text, url, priority };
2891 data.append( tmp );
2892 }
2893 }
2894
2895 QList< QChar > keys;
2896 for( char c = 'A'; c <= 'Z'; ++c )
2897 keys << c;
2898 for( char c = '0'; c <= '9'; ++c )
2899 keys << c;
2900 for( NodeImpl* n = m_part->xmlDocImpl();
2901 n != NULL;
2902 n = n->traverseNextNode()) {
2903 if( n->isElementNode()) {
2904 ElementImpl* en = static_cast< ElementImpl* >( n );
2905 DOMString s = en->getAttribute( ATTR_ACCESSKEY );
2906 if( s.length() == 1 ) {
2907 QChar c = s.string()[ 0 ].toUpper();
2908 keys.removeAll( c ); // remove manually assigned accesskeys
2909 }
2910 }
2911 }
2912
2913 QMap< ElementImpl*, QChar > ret;
2914 for( int priority = 10; priority >= 0; --priority ) {
2915 for( QLinkedList< AccessKeyData >::Iterator it = data.begin();
2916 it != data.end();
2917 ) {
2918 if( (*it).priority != priority ) {
2919 ++it;
2920 continue;
2921 }
2922 if( keys.isEmpty())
2923 break;
2924 QString text = (*it).text;
2925 QChar key;
2926 const QString url = (*it).url;
2927 // an identical link already has an accesskey assigned
2928 if( hrefs.contains( url ) ) {
2929 it = data.erase( it );
2930 continue;
2931 }
2932 if( !text.isEmpty()) {
2933 const QList< QPair< QString, QChar > > priorities
2934 = m_part->settings()->fallbackAccessKeysAssignments();
2935 for( QList< QPair< QString, QChar > >::ConstIterator it = priorities.begin();
2936 it != priorities.end();
2937 ++it )
2938 if( text == (*it).first && keys.contains( (*it).second )) {
2939 key = (*it).second;
2940 break;
2941 }
2942 }
2943 // try first to select the first character as the accesskey,
2944 // then first character of the following words,
2945 // and then simply the first free character
2946 if( key.isNull() && !text.isEmpty()) {
2947 const QStringList words = text.split( ' ' );
2948 for( QStringList::ConstIterator it = words.begin();
2949 it != words.end();
2950 ++it ) {
2951 if( keys.contains( (*it)[ 0 ].toUpper())) {
2952 key = (*it)[ 0 ].toUpper();
2953 break;
2954 }
2955 }
2956 }
2957 if( key.isNull() && !text.isEmpty()) {
2958 for( int i = 0; i < text.length(); ++i ) {
2959 if( keys.contains( text[ i ].toUpper())) {
2960 key = text[ i ].toUpper();
2961 break;
2962 }
2963 }
2964 }
2965 if( key.isNull())
2966 key = keys.front();
2967 ret[ (*it).element ] = key;
2968 keys.removeAll( key );
2969 it = data.erase( it );
2970 // assign the same accesskey also to other elements pointing to the same url
2971 if( !url.isEmpty() && !url.startsWith( "javascript:", Qt::CaseInsensitive )) {
2972 for( QLinkedList< AccessKeyData >::Iterator it2 = data.begin();
2973 it2 != data.end();
2974 ) {
2975 if( (*it2).url == url ) {
2976 ret[ (*it2).element ] = key;
2977 if( it == it2 )
2978 ++it;
2979 it2 = data.erase( it2 );
2980 } else
2981 ++it2;
2982 }
2983 }
2984 }
2985 }
2986 return ret;
2987}
2988
2989void KHTMLView::setMediaType( const QString &medium )
2990{
2991 m_medium = medium;
2992}
2993
2994QString KHTMLView::mediaType() const
2995{
2996 return m_medium;
2997}
2998
2999bool KHTMLView::pagedMode() const
3000{
3001 return d->paged;
3002}
3003
3004void KHTMLView::setWidgetVisible(RenderWidget* w, bool vis)
3005{
3006 if (vis) {
3007 d->visibleWidgets.insert(w, w->widget());
3008 }
3009 else
3010 d->visibleWidgets.remove(w);
3011}
3012
3013bool KHTMLView::needsFullRepaint() const
3014{
3015 return d->needsFullRepaint;
3016}
3017
3018namespace {
3019 class QPointerDeleter
3020 {
3021 public:
3022 explicit QPointerDeleter(QObject* o) : obj(o) {}
3023 ~QPointerDeleter() { delete obj; }
3024 private:
3025 const QPointer<QObject> obj;
3026 };
3027}
3028
3029void KHTMLView::print(bool quick)
3030{
3031 if(!m_part->xmlDocImpl()) return;
3032 khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer());
3033 if(!root) return;
3034
3035 QPointer<KHTMLPrintSettings> printSettings(new KHTMLPrintSettings); //XXX: doesn't save settings between prints like this
3036 const QPointerDeleter settingsDeleter(printSettings); //the printdialog takes ownership of the settings widget, thus this workaround to avoid double deletion
3037 QPrinter printer;
3038 QPointer<QPrintDialog> dialog = KdePrint::createPrintDialog(&printer, KdePrint::SystemSelectsPages, QList<QWidget*>() << printSettings.data(), this);
3039
3040 const QPointerDeleter dialogDeleter(dialog);
3041
3042 QString docname = m_part->xmlDocImpl()->URL().prettyUrl();
3043 if ( !docname.isEmpty() )
3044 docname = KStringHandler::csqueeze(docname, 80);
3045
3046 if(quick || (dialog->exec() && dialog)) { /*'this' and thus dialog might have been deleted while exec()!*/
3047 viewport()->setCursor( Qt::WaitCursor ); // only viewport(), no QApplication::, otherwise we get the busy cursor in kdeprint's dialogs
3048 // set up KPrinter
3049 printer.setFullPage(false);
3050 printer.setCreator(QString("KDE %1.%2.%3 HTML Library").arg(KDE_VERSION_MAJOR).arg(KDE_VERSION_MINOR).arg(KDE_VERSION_RELEASE));
3051 printer.setDocName(docname);
3052
3053 QPainter *p = new QPainter;
3054 p->begin( &printer );
3055 khtml::setPrintPainter( p );
3056
3057 m_part->xmlDocImpl()->setPaintDevice( &printer );
3058 QString oldMediaType = mediaType();
3059 setMediaType( "print" );
3060 // We ignore margin settings for html and body when printing
3061 // and use the default margins from the print-system
3062 // (In Qt 3.0.x the default margins are hardcoded in Qt)
3063 m_part->xmlDocImpl()->setPrintStyleSheet( printSettings->printFriendly() ?
3064 "* { background-image: none !important;"
3065 " background-color: white !important;"
3066 " color: black !important; }"
3067 "body { margin: 0px !important; }"
3068 "html { margin: 0px !important; }" :
3069 "body { margin: 0px !important; }"
3070 "html { margin: 0px !important; }"
3071 );
3072
3073 kDebug(6000) << "printing: physical page width = " << printer.width()
3074 << " height = " << printer.height() << endl;
3075 root->setStaticMode(true);
3076 root->setPagedMode(true);
3077 root->setWidth(printer.width());
3078// root->setHeight(printer.height());
3079 root->setPageTop(0);
3080 root->setPageBottom(0);
3081 d->paged = true;
3082
3083 m_part->xmlDocImpl()->styleSelector()->computeFontSizes(printer.logicalDpiY(), 100);
3084 m_part->xmlDocImpl()->updateStyleSelector();
3085 root->setPrintImages(printSettings->printImages());
3086 root->makePageBreakAvoidBlocks();
3087
3088 root->setNeedsLayoutAndMinMaxRecalc();
3089 root->layout();
3090
3091 // check sizes ask for action.. (scale or clip)
3092
3093 bool printHeader = printSettings->printHeader();
3094
3095 int headerHeight = 0;
3096 QFont headerFont("Sans Serif", 8);
3097
3098 QString headerLeft = KGlobal::locale()->formatDate(QDate::currentDate(),KLocale::ShortDate);
3099 QString headerMid = docname;
3100 QString headerRight;
3101
3102 if (printHeader)
3103 {
3104 p->setFont(headerFont);
3105 headerHeight = (p->fontMetrics().lineSpacing() * 3) / 2;
3106 }
3107
3108 // ok. now print the pages.
3109 kDebug(6000) << "printing: html page width = " << root->docWidth()
3110 << " height = " << root->docHeight() << endl;
3111 kDebug(6000) << "printing: margins left = " << printer.pageRect().left() - printer.paperRect().left()
3112 << " top = " << printer.pageRect().top() - printer.paperRect().top() << endl;
3113 kDebug(6000) << "printing: paper width = " << printer.width()
3114 << " height = " << printer.height() << endl;
3115 // if the width is too large to fit on the paper we just scale
3116 // the whole thing.
3117 int pageWidth = printer.width();
3118 int pageHeight = printer.height();
3119 p->setClipRect(0,0, pageWidth, pageHeight);
3120
3121 pageHeight -= headerHeight;
3122
3123#ifndef QT_NO_TRANSFORMATIONS
3124 bool scalePage = false;
3125 double scale = 0.0;
3126 if(root->docWidth() > printer.width()) {
3127 scalePage = true;
3128 scale = ((double) printer.width())/((double) root->docWidth());
3129 pageHeight = (int) (pageHeight/scale);
3130 pageWidth = (int) (pageWidth/scale);
3131 headerHeight = (int) (headerHeight/scale);
3132 }
3133#endif
3134 kDebug(6000) << "printing: scaled html width = " << pageWidth
3135 << " height = " << pageHeight << endl;
3136
3137 root->setHeight(pageHeight);
3138 root->setPageBottom(pageHeight);
3139 root->setNeedsLayout(true);
3140 root->layoutIfNeeded();
3141// m_part->slotDebugRenderTree();
3142
3143 // Squeeze header to make it it on the page.
3144 if (printHeader)
3145 {
3146 int available_width = printer.width() - 10 -
3147 2 * qMax(p->boundingRect(0, 0, printer.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerLeft).width(),
3148 p->boundingRect(0, 0, printer.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerRight).width());
3149 if (available_width < 150)
3150 available_width = 150;
3151 int mid_width;
3152 int squeeze = 120;
3153 do {
3154 headerMid = KStringHandler::csqueeze(docname, squeeze);
3155 mid_width = p->boundingRect(0, 0, printer.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerMid).width();
3156 squeeze -= 10;
3157 } while (mid_width > available_width);
3158 }
3159
3160 int top = 0;
3161 int bottom = 0;
3162 int page = 1;
3163 while(top < root->docHeight()) {
3164 if(top > 0) printer.newPage();
3165#ifndef QT_NO_TRANSFORMATIONS
3166 if (scalePage)
3167 p->scale(scale, scale);
3168#endif
3169 p->save();
3170 p->setClipRect(0, 0, pageWidth, headerHeight);
3171 if (printHeader)
3172 {
3173 int dy = p->fontMetrics().lineSpacing();
3174 p->setPen(Qt::black);
3175 p->setFont(headerFont);
3176
3177 headerRight = QString("#%1").arg(page);
3178
3179 p->drawText(0, 0, printer.width(), dy, Qt::AlignLeft, headerLeft);
3180 p->drawText(0, 0, printer.width(), dy, Qt::AlignHCenter, headerMid);
3181 p->drawText(0, 0, printer.width(), dy, Qt::AlignRight, headerRight);
3182 }
3183
3184 p->restore();
3185 p->translate(0, headerHeight-top);
3186
3187 bottom = top+pageHeight;
3188
3189 root->setPageTop(top);
3190 root->setPageBottom(bottom);
3191 root->setPageNumber(page);
3192
3193 root->layer()->paint(p, QRect(0, top, pageWidth, pageHeight));
3194 kDebug(6000) << "printed: page " << page <<" bottom At = " << bottom;
3195
3196 top = bottom;
3197 p->resetTransform();
3198 page++;
3199 }
3200
3201 p->end();
3202 delete p;
3203
3204 // and now reset the layout to the usual one...
3205 root->setPagedMode(false);
3206 root->setStaticMode(false);
3207 d->paged = false;
3208 khtml::setPrintPainter( 0 );
3209 setMediaType( oldMediaType );
3210 m_part->xmlDocImpl()->setPaintDevice( this );
3211 m_part->xmlDocImpl()->styleSelector()->computeFontSizes(m_part->xmlDocImpl()->logicalDpiY(), m_part->fontScaleFactor());
3212 m_part->xmlDocImpl()->updateStyleSelector();
3213 viewport()->unsetCursor();
3214 }
3215}
3216
3217void KHTMLView::slotPaletteChanged()
3218{
3219 if(!m_part->xmlDocImpl()) return;
3220 DOM::DocumentImpl *document = m_part->xmlDocImpl();
3221 if (!document->isHTMLDocument()) return;
3222 khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(document->renderer());
3223 if(!root) return;
3224 root->style()->resetPalette();
3225 NodeImpl *body = static_cast<HTMLDocumentImpl*>(document)->body();
3226 if(!body) return;
3227 body->setChanged(true);
3228 body->recalcStyle( NodeImpl::Force );
3229}
3230
3231void KHTMLView::paint(QPainter *p, const QRect &rc, int yOff, bool *more)
3232{
3233 if(!m_part->xmlDocImpl()) return;
3234 khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer());
3235 if(!root) return;
3236#ifdef SPEED_DEBUG
3237 d->firstRepaintPending = false;
3238#endif
3239
3240 QPaintDevice* opd = m_part->xmlDocImpl()->paintDevice();
3241 m_part->xmlDocImpl()->setPaintDevice(p->device());
3242 root->setPagedMode(true);
3243 root->setStaticMode(true);
3244 root->setWidth(rc.width());
3245
3246 // save()
3247 QRegion creg = p->clipRegion();
3248 QTransform t = p->worldTransform();
3249 QRect w = p->window();
3250 QRect v = p->viewport();
3251 bool vte = p->viewTransformEnabled();
3252 bool wme = p->worldMatrixEnabled();
3253
3254 p->setClipRect(rc);
3255 p->translate(rc.left(), rc.top());
3256 double scale = ((double) rc.width()/(double) root->docWidth());
3257 int height = (int) ((double) rc.height() / scale);
3258#ifndef QT_NO_TRANSFORMATIONS
3259 p->scale(scale, scale);
3260#endif
3261 root->setPageTop(yOff);
3262 root->setPageBottom(yOff+height);
3263
3264 root->layer()->paint(p, QRect(0, yOff, root->docWidth(), height));
3265 if (more)
3266 *more = yOff + height < root->docHeight();
3267
3268 // restore()
3269 p->setWorldTransform(t);
3270 p->setWindow(w);
3271 p->setViewport(v);
3272 p->setViewTransformEnabled( vte );
3273 p->setWorldMatrixEnabled( wme );
3274 if (!creg.isEmpty())
3275 p->setClipRegion( creg );
3276 else
3277 p->setClipRegion(QRegion(), Qt::NoClip);
3278
3279 root->setPagedMode(false);
3280 root->setStaticMode(false);
3281 m_part->xmlDocImpl()->setPaintDevice( opd );
3282}
3283
3284void KHTMLView::render(QPainter* p, const QRect& r, const QPoint& off)
3285{
3286#ifdef SPEED_DEBUG
3287 d->firstRepaintPending = false;
3288#endif
3289 QRect clip(off.x()+r.x(), off.y()+r.y(),r.width(),r.height());
3290 if(!m_part || !m_part->xmlDocImpl() || !m_part->xmlDocImpl()->renderer()) {
3291 p->fillRect(clip, palette().brush(QPalette::Active, QPalette::Base));
3292 return;
3293 }
3294 QPaintDevice* opd = m_part->xmlDocImpl()->paintDevice();
3295 m_part->xmlDocImpl()->setPaintDevice(p->device());
3296
3297 // save()
3298 QRegion creg = p->clipRegion();
3299 QTransform t = p->worldTransform();
3300 QRect w = p->window();
3301 QRect v = p->viewport();
3302 bool vte = p->viewTransformEnabled();
3303 bool wme = p->worldMatrixEnabled();
3304
3305 p->setClipRect(clip);
3306 QRect rect = r.translated(contentsX(),contentsY());
3307 p->translate(off.x()-contentsX(), off.y()-contentsY());
3308
3309 m_part->xmlDocImpl()->renderer()->layer()->paint(p, rect);
3310
3311 // restore()
3312 p->setWorldTransform(t);
3313 p->setWindow(w);
3314 p->setViewport(v);
3315 p->setViewTransformEnabled( vte );
3316 p->setWorldMatrixEnabled( wme );
3317 if (!creg.isEmpty())
3318 p->setClipRegion( creg );
3319 else
3320 p->setClipRegion(QRegion(), Qt::NoClip);
3321
3322 m_part->xmlDocImpl()->setPaintDevice( opd );
3323}
3324
3325void KHTMLView::setHasStaticBackground(bool partial)
3326{
3327 // full static iframe is irreversible for now
3328 if (d->staticWidget == KHTMLViewPrivate::SBFull && m_kwp->isRedirected())
3329 return;
3330
3331 d->staticWidget = partial ?
3332 KHTMLViewPrivate::SBPartial : KHTMLViewPrivate::SBFull;
3333}
3334
3335void KHTMLView::setHasNormalBackground()
3336{
3337 // full static iframe is irreversible for now
3338 if (d->staticWidget == KHTMLViewPrivate::SBFull && m_kwp->isRedirected())
3339 return;
3340
3341 d->staticWidget = KHTMLViewPrivate::SBNone;
3342}
3343
3344void KHTMLView::addStaticObject(bool fixed)
3345{
3346 if (fixed)
3347 d->fixedObjectsCount++;
3348 else
3349 d->staticObjectsCount++;
3350
3351 setHasStaticBackground( true /*partial*/ );
3352}
3353
3354void KHTMLView::removeStaticObject(bool fixed)
3355{
3356 if (fixed)
3357 d->fixedObjectsCount--;
3358 else
3359 d->staticObjectsCount--;
3360
3361 assert( d->fixedObjectsCount >= 0 && d->staticObjectsCount >= 0 );
3362
3363 if (!d->staticObjectsCount && !d->fixedObjectsCount)
3364 setHasNormalBackground();
3365 else
3366 setHasStaticBackground( true /*partial*/ );
3367}
3368
3369void KHTMLView::setVerticalScrollBarPolicy( Qt::ScrollBarPolicy policy )
3370{
3371#ifndef KHTML_NO_SCROLLBARS
3372 d->vpolicy = policy;
3373 QScrollArea::setVerticalScrollBarPolicy(policy);
3374#else
3375 Q_UNUSED( policy );
3376#endif
3377}
3378
3379void KHTMLView::setHorizontalScrollBarPolicy( Qt::ScrollBarPolicy policy )
3380{
3381#ifndef KHTML_NO_SCROLLBARS
3382 d->hpolicy = policy;
3383 QScrollArea::setHorizontalScrollBarPolicy(policy);
3384#else
3385 Q_UNUSED( policy );
3386#endif
3387}
3388
3389void KHTMLView::restoreScrollBar()
3390{
3391 int ow = visibleWidth();
3392 QScrollArea::setVerticalScrollBarPolicy(d->vpolicy);
3393 if (visibleWidth() != ow)
3394 layout();
3395 d->prevScrollbarVisible = verticalScrollBar()->isVisible();
3396}
3397
3398QStringList KHTMLView::formCompletionItems(const QString &name) const
3399{
3400 if (!m_part->settings()->isFormCompletionEnabled())
3401 return QStringList();
3402 if (!d->formCompletions)
3403 d->formCompletions = new KConfig(KStandardDirs::locateLocal("data", "khtml/formcompletions"));
3404 return d->formCompletions->group("").readEntry(name, QStringList());
3405}
3406
3407void KHTMLView::clearCompletionHistory(const QString& name)
3408{
3409 if (!d->formCompletions)
3410 {
3411 d->formCompletions = new KConfig(KStandardDirs::locateLocal("data", "khtml/formcompletions"));
3412 }
3413 d->formCompletions->group("").writeEntry(name, "");
3414 d->formCompletions->sync();
3415}
3416
3417void KHTMLView::addFormCompletionItem(const QString &name, const QString &value)
3418{
3419 if (!m_part->settings()->isFormCompletionEnabled())
3420 return;
3421 // don't store values that are all numbers or just numbers with
3422 // dashes or spaces as those are likely credit card numbers or
3423 // something similar
3424 bool cc_number(true);
3425 for ( int i = 0; i < value.length(); ++i)
3426 {
3427 QChar c(value[i]);
3428 if (!c.isNumber() && c != '-' && !c.isSpace())
3429 {
3430 cc_number = false;
3431 break;
3432 }
3433 }
3434 if (cc_number)
3435 return;
3436 QStringList items = formCompletionItems(name);
3437 if (!items.contains(value))
3438 items.prepend(value);
3439 while ((int)items.count() > m_part->settings()->maxFormCompletionItems())
3440 items.erase(items.isEmpty() ? items.end() : --items.end());
3441 d->formCompletions->group("").writeEntry(name, items);
3442}
3443
3444void KHTMLView::addNonPasswordStorableSite(const QString& host)
3445{
3446 if (!d->formCompletions) {
3447 d->formCompletions = new KConfig(KStandardDirs::locateLocal("data", "khtml/formcompletions"));
3448 }
3449
3450 KConfigGroup cg( d->formCompletions, "NonPasswordStorableSites");
3451 QStringList sites = cg.readEntry("Sites", QStringList());
3452 sites.append(host);
3453 cg.writeEntry("Sites", sites);
3454 cg.sync();
3455}
3456
3457
3458void KHTMLView::delNonPasswordStorableSite(const QString& host)
3459{
3460 if (!d->formCompletions) {
3461 d->formCompletions = new KConfig(KStandardDirs::locateLocal("data", "khtml/formcompletions"));
3462 }
3463
3464 KConfigGroup cg( d->formCompletions, "NonPasswordStorableSites");
3465 QStringList sites = cg.readEntry("Sites", QStringList());
3466 sites.removeOne(host);
3467 cg.writeEntry("Sites", sites);
3468 cg.sync();
3469}
3470
3471bool KHTMLView::nonPasswordStorableSite(const QString& host) const
3472{
3473 if (!d->formCompletions) {
3474 d->formCompletions = new KConfig(KStandardDirs::locateLocal("data", "khtml/formcompletions"));
3475 }
3476 QStringList sites = d->formCompletions->group( "NonPasswordStorableSites" ).readEntry("Sites", QStringList());
3477 return (sites.indexOf(host) != -1);
3478}
3479
3480// returns true if event should be swallowed
3481bool KHTMLView::dispatchMouseEvent(int eventId, DOM::NodeImpl *targetNode,
3482 DOM::NodeImpl *targetNodeNonShared, bool cancelable,
3483 int detail,QMouseEvent *_mouse, bool setUnder,
3484 int mouseEventType, int orient)
3485{
3486 // if the target node is a text node, dispatch on the parent node - rdar://4196646 (and #76948)
3487 if (targetNode && targetNode->isTextNode())
3488 targetNode = targetNode->parentNode();
3489
3490 if (d->underMouse)
3491 d->underMouse->deref();
3492 d->underMouse = targetNode;
3493 if (d->underMouse)
3494 d->underMouse->ref();
3495
3496 if (d->underMouseNonShared)
3497 d->underMouseNonShared->deref();
3498 d->underMouseNonShared = targetNodeNonShared;
3499 if (d->underMouseNonShared)
3500 d->underMouseNonShared->ref();
3501
3502 bool isWheelEvent = (mouseEventType == DOM::NodeImpl::MouseWheel);
3503
3504 int exceptioncode = 0;
3505 int pageX = _mouse->x();
3506 int pageY = _mouse->y();
3507 revertTransforms(pageX, pageY);
3508 int clientX = pageX - contentsX();
3509 int clientY = pageY - contentsY();
3510 int screenX = _mouse->globalX();
3511 int screenY = _mouse->globalY();
3512 int button = -1;
3513 switch (_mouse->button()) {
3514 case Qt::LeftButton:
3515 button = 0;
3516 break;
3517 case Qt::MidButton:
3518 button = 1;
3519 break;
3520 case Qt::RightButton:
3521 button = 2;
3522 break;
3523 default:
3524 break;
3525 }
3526 if (d->accessKeysEnabled && d->accessKeysPreActivate && button!=-1)
3527 d->accessKeysPreActivate=false;
3528
3529 bool ctrlKey = (_mouse->modifiers() & Qt::ControlModifier);
3530 bool altKey = (_mouse->modifiers() & Qt::AltModifier);
3531 bool shiftKey = (_mouse->modifiers() & Qt::ShiftModifier);
3532 bool metaKey = (_mouse->modifiers() & Qt::MetaModifier);
3533
3534 // mouseout/mouseover
3535 if (setUnder && d->oldUnderMouse != targetNode) {
3536 if (d->oldUnderMouse && d->oldUnderMouse->document() != m_part->xmlDocImpl()) {
3537 d->oldUnderMouse->deref();
3538 d->oldUnderMouse = 0;
3539 }
3540 // send mouseout event to the old node
3541 if (d->oldUnderMouse) {
3542 // send mouseout event to the old node
3543 MouseEventImpl *me = new MouseEventImpl(EventImpl::MOUSEOUT_EVENT,
3544 true,true,m_part->xmlDocImpl()->defaultView(),
3545 0,screenX,screenY,clientX,clientY,pageX, pageY,
3546 ctrlKey,altKey,shiftKey,metaKey,
3547 button,targetNode);
3548 me->ref();
3549 d->oldUnderMouse->dispatchEvent(me,exceptioncode,true);
3550 me->deref();
3551 }
3552 // send mouseover event to the new node
3553 if (targetNode) {
3554 MouseEventImpl *me = new MouseEventImpl(EventImpl::MOUSEOVER_EVENT,
3555 true,true,m_part->xmlDocImpl()->defaultView(),
3556 0,screenX,screenY,clientX,clientY,pageX, pageY,
3557 ctrlKey,altKey,shiftKey,metaKey,
3558 button,d->oldUnderMouse);
3559
3560 me->ref();
3561 targetNode->dispatchEvent(me,exceptioncode,true);
3562 me->deref();
3563 }
3564 if (d->oldUnderMouse)
3565 d->oldUnderMouse->deref();
3566 d->oldUnderMouse = targetNode;
3567 if (d->oldUnderMouse)
3568 d->oldUnderMouse->ref();
3569 }
3570
3571 bool swallowEvent = false;
3572
3573 if (targetNode) {
3574 // if the target node is a disabled widget, we don't want any full-blown mouse events
3575 if (targetNode->isGenericFormElement()
3576 && static_cast<HTMLGenericFormElementImpl*>(targetNode)->disabled())
3577 return true;
3578
3579 // send the actual event
3580 bool dblclick = ( eventId == EventImpl::CLICK_EVENT &&
3581 _mouse->type() == QEvent::MouseButtonDblClick );
3582 MouseEventImpl *me = new MouseEventImpl(static_cast<EventImpl::EventId>(eventId),
3583 true,cancelable,m_part->xmlDocImpl()->defaultView(),
3584 detail,screenX,screenY,clientX,clientY,pageX, pageY,
3585 ctrlKey,altKey,shiftKey,metaKey,
3586 button,0, isWheelEvent ? 0 : _mouse, dblclick,
3587 isWheelEvent ? static_cast<MouseEventImpl::Orientation>(orient) : MouseEventImpl::ONone );
3588 me->ref();
3589 if ( !d->m_mouseEventsTarget && RenderLayer::gScrollBar && eventId == EventImpl::MOUSEDOWN_EVENT )
3590 // button is pressed inside a layer scrollbar, so make it the target for future mousemove events until released
3591 d->m_mouseEventsTarget = RenderLayer::gScrollBar;
3592 if ( d->m_mouseEventsTarget && qobject_cast<QScrollBar*>(d->m_mouseEventsTarget) &&
3593 dynamic_cast<KHTMLWidget*>(static_cast<QWidget*>(d->m_mouseEventsTarget)) ) {
3594 // we have a sticky mouse event target and it is a layer's scrollbar. Forward events manually.
3595 // ### should use the dom
3596 KHTMLWidget*w = dynamic_cast<KHTMLWidget*>(static_cast<QWidget*>(d->m_mouseEventsTarget));
3597 QPoint p = w->m_kwp->absolutePos();
3598 QMouseEvent fw(_mouse->type(), QPoint(pageX, pageY)-p, _mouse->button(), _mouse->buttons(), _mouse->modifiers());
3599 static_cast<RenderWidget::EventPropagator *>(static_cast<QWidget*>(d->m_mouseEventsTarget))->sendEvent(&fw);
3600 if (_mouse->type() == QMouseEvent::MouseButtonPress && _mouse->button() == Qt::RightButton) {
3601 QContextMenuEvent cme(QContextMenuEvent::Mouse, p);
3602 static_cast<RenderWidget::EventPropagator *>(static_cast<QWidget*>(d->m_mouseEventsTarget))->sendEvent(&cme);
3603 d->m_mouseEventsTarget = 0;
3604 }
3605 swallowEvent = true;
3606 } else {
3607 targetNode->dispatchEvent(me,exceptioncode,true);
3608 bool defaultHandled = me->defaultHandled();
3609 if (defaultHandled || me->defaultPrevented())
3610 swallowEvent = true;
3611 }
3612 if (eventId == EventImpl::MOUSEDOWN_EVENT && !me->defaultPrevented()) {
3613 // Focus should be shifted on mouse down, not on a click. -dwh
3614 // Blur current focus node when a link/button is clicked; this
3615 // is expected by some sites that rely on onChange handlers running
3616 // from form fields before the button click is processed.
3617 DOM::NodeImpl* nodeImpl = targetNode;
3618 for ( ; nodeImpl && !nodeImpl->isFocusable(); nodeImpl = nodeImpl->parentNode())
3619 {}
3620 if (nodeImpl && nodeImpl->isMouseFocusable())
3621 m_part->xmlDocImpl()->setFocusNode(nodeImpl);
3622 else if (!nodeImpl || !nodeImpl->focused())
3623 m_part->xmlDocImpl()->setFocusNode(0);
3624 }
3625 me->deref();
3626 }
3627
3628 return swallowEvent;
3629}
3630
3631void KHTMLView::setIgnoreWheelEvents( bool e )
3632{
3633 d->ignoreWheelEvents = e;
3634}
3635
3636#ifndef QT_NO_WHEELEVENT
3637
3638void KHTMLView::wheelEvent(QWheelEvent* e)
3639{
3640 // check if we should reset the state of the indicator describing if
3641 // we are currently scrolling the view as a result of wheel events
3642 if (d->scrollingFromWheel != QPoint(-1,-1) && d->scrollingFromWheel != QCursor::pos())
3643 d->scrollingFromWheel = d->scrollingFromWheelTimerId ? QCursor::pos() : QPoint(-1,-1);
3644
3645 if (d->accessKeysEnabled && d->accessKeysPreActivate) d->accessKeysPreActivate=false;
3646
3647 if ( ( e->modifiers() & Qt::ControlModifier) == Qt::ControlModifier )
3648 {
3649 emit zoomView( - e->delta() );
3650 e->accept();
3651 }
3652 else if (d->firstLayoutPending)
3653 {
3654 e->accept();
3655 }
3656 else if( !m_kwp->isRedirected() &&
3657 ( (e->orientation() == Qt::Vertical &&
3658 ((d->ignoreWheelEvents && !verticalScrollBar()->isVisible())
3659 || (e->delta() > 0 && contentsY() <= 0)
3660 || (e->delta() < 0 && contentsY() >= contentsHeight() - visibleHeight())))
3661 ||
3662 (e->orientation() == Qt::Horizontal &&
3663 ((d->ignoreWheelEvents && !horizontalScrollBar()->isVisible())
3664 || (e->delta() > 0 && contentsX() <=0)
3665 || (e->delta() < 0 && contentsX() >= contentsWidth() - visibleWidth()))))
3666 && m_part->parentPart())
3667 {
3668 if ( m_part->parentPart()->view() )
3669 m_part->parentPart()->view()->wheelEvent( e );
3670 e->ignore();
3671 }
3672 else
3673 {
3674 int xm = e->x();
3675 int ym = e->y();
3676 revertTransforms(xm, ym);
3677
3678 DOM::NodeImpl::MouseEvent mev( e->buttons(), DOM::NodeImpl::MouseWheel );
3679 m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev );
3680
3681 MouseEventImpl::Orientation o = MouseEventImpl::OVertical;
3682 if (e->orientation() == Qt::Horizontal)
3683 o = MouseEventImpl::OHorizontal;
3684
3685 QMouseEvent _mouse(QEvent::MouseMove, e->pos(), Qt::NoButton, e->buttons(), e->modifiers());
3686 bool swallow = dispatchMouseEvent(EventImpl::KHTML_MOUSEWHEEL_EVENT,mev.innerNode.handle(),mev.innerNonSharedNode.handle(),
3687 true,-e->delta()/40,&_mouse,true,DOM::NodeImpl::MouseWheel,o);
3688
3689 if (swallow)
3690 return;
3691
3692 d->scrollBarMoved = true;
3693 d->scrollingFromWheel = QCursor::pos();
3694 if (d->smoothScrollMode != SSMDisabled)
3695 d->shouldSmoothScroll = true;
3696 if (d->scrollingFromWheelTimerId)
3697 killTimer(d->scrollingFromWheelTimerId);
3698 d->scrollingFromWheelTimerId = startTimer(400);
3699
3700 if (m_part->parentPart()) {
3701 // don't propagate if we are a sub-frame and our scrollbars are already at end of range
3702 bool h = (static_cast<QWheelEvent*>(e)->orientation() == Qt::Horizontal);
3703 bool d = (static_cast<QWheelEvent*>(e)->delta() < 0);
3704 QScrollBar* hsb = horizontalScrollBar();
3705 QScrollBar* vsb = verticalScrollBar();
3706 if ( (h && ((d && hsb->value() == hsb->maximum()) || (!d && hsb->value() == hsb->minimum()))) ||
3707 (!h && ((d && vsb->value() == vsb->maximum()) || (!d && vsb->value() == vsb->minimum()))) ) {
3708 e->accept();
3709 return;
3710 }
3711 }
3712 QScrollArea::wheelEvent( e );
3713 }
3714
3715}
3716#endif
3717
3718void KHTMLView::dragEnterEvent( QDragEnterEvent* ev )
3719{
3720 // Still overridden for BC reasons only...
3721 QScrollArea::dragEnterEvent( ev );
3722}
3723
3724void KHTMLView::dropEvent( QDropEvent *ev )
3725{
3726 // Still overridden for BC reasons only...
3727 QScrollArea::dropEvent( ev );
3728}
3729
3730void KHTMLView::focusInEvent( QFocusEvent *e )
3731{
3732 DOM::NodeImpl* fn = m_part->xmlDocImpl() ? m_part->xmlDocImpl()->focusNode() : 0;
3733 if (fn && fn->renderer() && fn->renderer()->isWidget() &&
3734 (e->reason() != Qt::MouseFocusReason) &&
3735 static_cast<khtml::RenderWidget*>(fn->renderer())->widget())
3736 static_cast<khtml::RenderWidget*>(fn->renderer())->widget()->setFocus();
3737 m_part->setSelectionVisible();
3738 QScrollArea::focusInEvent( e );
3739}
3740
3741void KHTMLView::focusOutEvent( QFocusEvent *e )
3742{
3743 if (m_part) {
3744 m_part->stopAutoScroll();
3745 m_part->setSelectionVisible(false);
3746 }
3747
3748 if ( d->cursorIconWidget )
3749 d->cursorIconWidget->hide();
3750
3751 QScrollArea::focusOutEvent( e );
3752}
3753
3754void KHTMLView::scrollContentsBy( int dx, int dy )
3755{
3756 if (!dx && !dy) return;
3757
3758 if ( !d->firstLayoutPending && !d->complete && m_part->xmlDocImpl() &&
3759 d->layoutSchedulingEnabled) {
3760 // contents scroll while we are not complete: we need to check our layout *now*
3761 khtml::RenderCanvas* root = static_cast<khtml::RenderCanvas *>( m_part->xmlDocImpl()->renderer() );
3762 if (root && root->needsLayout()) {
3763 unscheduleRelayout();
3764 layout();
3765 }
3766 }
3767
3768 if ( d->shouldSmoothScroll && d->smoothScrollMode != SSMDisabled && m_part->xmlDocImpl() &&
3769 m_part->xmlDocImpl()->renderer() && (d->smoothScrollMode != SSMWhenEfficient || d->smoothScrollMissedDeadlines != sWayTooMany)) {
3770
3771 bool doSmoothScroll = (!d->staticWidget || d->smoothScrollMode == SSMEnabled);
3772
3773 int numStaticPixels = 0;
3774 QRegion r = static_cast<RenderCanvas*>(m_part->xmlDocImpl()->renderer())->staticRegion();
3775
3776 // only do smooth scrolling if static region is relatively small
3777 if (!doSmoothScroll && d->staticWidget == KHTMLViewPrivate::SBPartial && r.rects().size() <= 10) {
3778 foreach(const QRect &rr, r.rects())
3779 numStaticPixels += rr.width()*rr.height();
3780 if ((numStaticPixels < sSmoothScrollMinStaticPixels) || (numStaticPixels*8 < visibleWidth()*visibleHeight()))
3781 doSmoothScroll = true;
3782 }
3783 if (doSmoothScroll) {
3784 setupSmoothScrolling(dx, dy);
3785 return;
3786 }
3787 }
3788
3789 if ( underMouse() && QToolTip::isVisible() )
3790 QToolTip::hideText();
3791
3792 if (!d->scrollingSelf) {
3793 d->scrollBarMoved = true;
3794 d->contentsMoving = true;
3795 // ensure quick reset of contentsMoving flag
3796 scheduleRepaint(0, 0, 0, 0);
3797 }
3798
3799 if (m_part->xmlDocImpl() && m_part->xmlDocImpl()->documentElement()) {
3800 m_part->xmlDocImpl()->documentElement()->dispatchHTMLEvent(EventImpl::SCROLL_EVENT, true, false);
3801 }
3802
3803 if (QApplication::isRightToLeft())
3804 dx = -dx;
3805
3806 if (!d->smoothScrolling) {
3807 d->updateContentsXY();
3808 } else {
3809 d->contentsX -= dx;
3810 d->contentsY -= dy;
3811 }
3812 if (widget()->pos() != QPoint(0,0)) {
3813 kDebug(6000) << "Static widget wasn't positioned at (0,0). This should NOT happen. Please report this event to developers.";
3814 kDebug(6000) << kBacktrace();
3815 widget()->move(0,0);
3816 }
3817
3818 QWidget *w = widget();
3819 QPoint off;
3820 if (m_kwp->isRedirected()) {
3821 // This is a redirected sub frame. Translate to root view context
3822 KHTMLView* v = m_kwp->rootViewPos( off );
3823 if (v)
3824 w = v->widget();
3825 off = viewport()->mapTo(this, off);
3826 }
3827
3828 if ( d->staticWidget ) {
3829
3830 // now remove from view the external widgets that must have completely
3831 // disappeared after dx/dy scroll delta is effective
3832 if (!d->visibleWidgets.isEmpty())
3833 checkExternalWidgetsPosition();
3834
3835 if ( d->staticWidget == KHTMLViewPrivate::SBPartial
3836 && m_part->xmlDocImpl() && m_part->xmlDocImpl()->renderer() ) {
3837 // static objects might be selectively repainted, like stones in flowing water
3838 QRegion r = static_cast<RenderCanvas*>(m_part->xmlDocImpl()->renderer())->staticRegion();
3839 r.translate( -contentsX(), -contentsY());
3840 QVector<QRect> ar = r.rects();
3841
3842 for (int i = 0; i < ar.size() ; ++i) {
3843 widget()->update( ar[i] );
3844 }
3845 r = QRegion(QRect(0, 0, visibleWidth(), visibleHeight())) - r;
3846 ar = r.rects();
3847 for (int i = 0; i < ar.size() ; ++i) {
3848 w->scroll( dx, dy, ar[i].translated(off) );
3849 }
3850 d->scrollExternalWidgets(dx, dy);
3851 } else {
3852 // we can't avoid a full update
3853 widget()->update();
3854 }
3855 if (d->accessKeysActivated)
3856 d->scrollAccessKeys(dx, dy);
3857
3858 return;
3859 }
3860
3861 if (m_kwp->isRedirected()) {
3862 const QRect rect(off.x(), off.y(), visibleWidth() * d->zoomLevel / 100, visibleHeight() * d->zoomLevel / 100);
3863 w->scroll(dx, dy, rect);
3864 if (d->zoomLevel != 100) {
3865 w->update(rect); // without this update we are getting bad rendering when an iframe is zoomed in
3866 }
3867 } else {
3868 widget()->scroll(dx, dy, widget()->rect() & viewport()->rect());
3869 }
3870
3871 d->scrollExternalWidgets(dx, dy);
3872 if (d->accessKeysActivated)
3873 d->scrollAccessKeys(dx, dy);
3874}
3875
3876void KHTMLView::setupSmoothScrolling(int dx, int dy)
3877{
3878 // old or minimum speed
3879 int ddx = qMax(d->steps ? abs(d->dx)/d->steps : 0,3);
3880 int ddy = qMax(d->steps ? abs(d->dy)/d->steps : 0,3);
3881
3882 // full scroll is remaining scroll plus new scroll
3883 d->dx = d->dx + dx;
3884 d->dy = d->dy + dy;
3885
3886 if (d->dx == 0 && d->dy == 0) {
3887 d->stopScrolling();
3888 return;
3889 }
3890
3891 d->steps = (sSmoothScrollTime-1)/sSmoothScrollTick + 1;
3892
3893 if (qMax(abs(d->dx), abs(d->dy)) / d->steps < qMax(ddx,ddy)) {
3894 // Don't move slower than average 4px/step in minimum one direction
3895 // This means fewer than normal steps
3896 d->steps = qMax((abs(d->dx)+ddx-1)/ddx, (abs(d->dy)+ddy-1)/ddy);
3897 if (d->steps < 1) d->steps = 1;
3898 }
3899
3900 d->smoothScrollStopwatch.start();
3901 if (!d->smoothScrolling) {
3902 d->startScrolling();
3903 scrollTick();
3904 }
3905}
3906
3907void KHTMLView::scrollTick() {
3908 if (d->dx == 0 && d->dy == 0) {
3909 d->stopScrolling();
3910 return;
3911 }
3912
3913 if (d->steps < 1) d->steps = 1;
3914 int takesteps = d->smoothScrollStopwatch.restart() / sSmoothScrollTick;
3915 int scroll_x = 0;
3916 int scroll_y = 0;
3917 if (takesteps < 1) takesteps = 1;
3918 if (takesteps > d->steps) takesteps = d->steps;
3919 for(int i = 0; i < takesteps; i++) {
3920 int ddx = (d->dx / (d->steps+1)) * 2;
3921 int ddy = (d->dy / (d->steps+1)) * 2;
3922
3923 // limit step to requested scrolling distance
3924 if (abs(ddx) > abs(d->dx)) ddx = d->dx;
3925 if (abs(ddy) > abs(d->dy)) ddy = d->dy;
3926
3927 // update remaining scroll
3928 d->dx -= ddx;
3929 d->dy -= ddy;
3930 scroll_x += ddx;
3931 scroll_y += ddy;
3932 d->steps--;
3933 }
3934
3935 d->shouldSmoothScroll = false;
3936 scrollContentsBy(scroll_x, scroll_y);
3937
3938 if (takesteps < 2) {
3939 d->smoothScrollMissedDeadlines = 0;
3940 } else {
3941 if (d->smoothScrollMissedDeadlines != sWayTooMany &&
3942 (!m_part->xmlDocImpl() || !m_part->xmlDocImpl()->parsing())) {
3943 d->smoothScrollMissedDeadlines++;
3944 if (d->smoothScrollMissedDeadlines >= sMaxMissedDeadlines) {
3945 // we missed many deadlines in a row!
3946 // time to signal we had enough..
3947 d->smoothScrollMissedDeadlines = sWayTooMany;
3948 }
3949 }
3950 }
3951}
3952
3953
3954void KHTMLView::addChild(QWidget * child, int x, int y)
3955{
3956 if (!child)
3957 return;
3958
3959 if (child->parent() != widget())
3960 child->setParent( widget() );
3961
3962 // ### handle pseudo-zooming of non-redirected widgets (e.g. just resize'em)
3963
3964 child->move(x-contentsX(), y-contentsY());
3965}
3966
3967void KHTMLView::timerEvent ( QTimerEvent *e )
3968{
3969// kDebug() << "timer event " << e->timerId();
3970 if ( e->timerId() == d->scrollTimerId ) {
3971 if( d->scrollSuspended )
3972 return;
3973 switch (d->scrollDirection) {
3974 case KHTMLViewPrivate::ScrollDown:
3975 if (contentsY() + visibleHeight () >= contentsHeight())
3976 d->newScrollTimer(this, 0);
3977 else
3978 verticalScrollBar()->setValue( verticalScrollBar()->value() +d->scrollBy );
3979 break;
3980 case KHTMLViewPrivate::ScrollUp:
3981 if (contentsY() <= 0)
3982 d->newScrollTimer(this, 0);
3983 else
3984 verticalScrollBar()->setValue( verticalScrollBar()->value() -d->scrollBy );
3985 break;
3986 case KHTMLViewPrivate::ScrollRight:
3987 if (contentsX() + visibleWidth () >= contentsWidth())
3988 d->newScrollTimer(this, 0);
3989 else
3990 horizontalScrollBar()->setValue( horizontalScrollBar()->value() +d->scrollBy );
3991 break;
3992 case KHTMLViewPrivate::ScrollLeft:
3993 if (contentsX() <= 0)
3994 d->newScrollTimer(this, 0);
3995 else
3996 horizontalScrollBar()->setValue( horizontalScrollBar()->value() -d->scrollBy );
3997 break;
3998 }
3999 return;
4000 }
4001 else if ( e->timerId() == d->scrollingFromWheelTimerId ) {
4002 killTimer( d->scrollingFromWheelTimerId );
4003 d->scrollingFromWheelTimerId = 0;
4004 } else if ( e->timerId() == d->layoutTimerId ) {
4005 if (d->firstLayoutPending && d->layoutAttemptCounter < 4
4006 && (!m_part->xmlDocImpl() || !m_part->xmlDocImpl()->readyForLayout())) {
4007 d->layoutAttemptCounter++;
4008 killTimer(d->layoutTimerId);
4009 d->layoutTimerId = 0;
4010 scheduleRelayout();
4011 return;
4012 }
4013 layout();
4014 d->scheduledLayoutCounter++;
4015 if (d->firstLayoutPending) {
4016 d->firstLayoutPending = false;
4017 verticalScrollBar()->setEnabled( true );
4018 horizontalScrollBar()->setEnabled( true );
4019 }
4020 }
4021
4022 d->contentsMoving = false;
4023 if( m_part->xmlDocImpl() ) {
4024 DOM::DocumentImpl *document = m_part->xmlDocImpl();
4025 khtml::RenderCanvas* root = static_cast<khtml::RenderCanvas *>(document->renderer());
4026
4027 if ( root && root->needsLayout() ) {
4028 if (d->repaintTimerId)
4029 killTimer(d->repaintTimerId);
4030 d->repaintTimerId = 0;
4031 scheduleRelayout();
4032 return;
4033 }
4034 }
4035
4036 if (d->repaintTimerId)
4037 killTimer(d->repaintTimerId);
4038 d->repaintTimerId = 0;
4039
4040 QRect updateRegion;
4041 const QVector<QRect> rects = d->updateRegion.rects();
4042
4043 d->updateRegion = QRegion();
4044
4045 if ( rects.size() )
4046 updateRegion = rects[0];
4047
4048 for ( int i = 1; i < rects.size(); ++i ) {
4049 QRect newRegion = updateRegion.unite(rects[i]);
4050 if (2*newRegion.height() > 3*updateRegion.height() )
4051 {
4052 repaintContents( updateRegion );
4053 updateRegion = rects[i];
4054 }
4055 else
4056 updateRegion = newRegion;
4057 }
4058
4059 if ( !updateRegion.isNull() )
4060 repaintContents( updateRegion );
4061
4062 // As widgets can only be accurately positioned during painting, every layout might
4063 // dissociate a widget from its RenderWidget. E.g: if a RenderWidget was visible before layout, but the layout
4064 // pushed it out of the viewport, it will not be repainted, and consequently it's associated widget won't be repositioned.
4065 // 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.
4066
4067 if (d->dirtyLayout && !d->visibleWidgets.isEmpty())
4068 checkExternalWidgetsPosition();
4069
4070 d->dirtyLayout = false;
4071
4072 emit repaintAccessKeys();
4073 if (d->emitCompletedAfterRepaint) {
4074 bool full = d->emitCompletedAfterRepaint == KHTMLViewPrivate::CSFull;
4075 d->emitCompletedAfterRepaint = KHTMLViewPrivate::CSNone;
4076 if ( full )
4077 emit m_part->completed();
4078 else
4079 emit m_part->completed(true);
4080 }
4081}
4082
4083void KHTMLView::checkExternalWidgetsPosition()
4084{
4085 QWidget* w;
4086 QRect visibleRect(contentsX(), contentsY(), visibleWidth(), visibleHeight());
4087 QList<RenderWidget*> toRemove;
4088 QHashIterator<void*, QWidget*> it(d->visibleWidgets);
4089 while (it.hasNext()) {
4090 int xp = 0, yp = 0;
4091 it.next();
4092 RenderWidget* rw = static_cast<RenderWidget*>( it.key() );
4093 if (!rw->absolutePosition(xp, yp) ||
4094 !visibleRect.intersects(QRect(xp, yp, it.value()->width(), it.value()->height())))
4095 toRemove.append(rw);
4096 }
4097 foreach (RenderWidget* r, toRemove)
4098 if ( (w = d->visibleWidgets.take(r) ) )
4099 w->move( 0, -500000);
4100}
4101
4102void KHTMLView::scheduleRelayout(khtml::RenderObject * /*clippedObj*/)
4103{
4104 if (!d->layoutSchedulingEnabled || d->layoutTimerId)
4105 return;
4106
4107 int time = 0;
4108 if (d->firstLayoutPending) {
4109 // Any repaint happening while we have no content blanks the viewport ("white flash").
4110 // Hence the need to delay the first layout as much as we can.
4111 // Only if the document gets stuck for too long in incomplete state will we allow the blanking.
4112 time = d->layoutAttemptCounter ?
4113 sLayoutAttemptDelay + sLayoutAttemptIncrement*d->layoutAttemptCounter : sFirstLayoutDelay;
4114 } else if (m_part->xmlDocImpl() && m_part->xmlDocImpl()->parsing()) {
4115 // Delay between successive layouts in parsing mode.
4116 // Increment reflects the decaying importance of visual feedback over time.
4117 time = qMin(2000, sParsingLayoutsInterval + d->scheduledLayoutCounter*sParsingLayoutsIncrement);
4118 }
4119 d->layoutTimerId = startTimer( time );
4120}
4121
4122void KHTMLView::unscheduleRelayout()
4123{
4124 if (!d->layoutTimerId)
4125 return;
4126
4127 killTimer(d->layoutTimerId);
4128 d->layoutTimerId = 0;
4129}
4130
4131void KHTMLView::unscheduleRepaint()
4132{
4133 if (!d->repaintTimerId)
4134 return;
4135
4136 killTimer(d->repaintTimerId);
4137 d->repaintTimerId = 0;
4138}
4139
4140void KHTMLView::scheduleRepaint(int x, int y, int w, int h, bool asap)
4141{
4142 bool parsing = !m_part->xmlDocImpl() || m_part->xmlDocImpl()->parsing();
4143
4144// kDebug() << "parsing " << parsing;
4145// kDebug() << "complete " << d->complete;
4146
4147 int time = parsing && !d->firstLayoutPending ? 150 : (!asap ? ( !d->complete ? 80 : 20 ) : 0);
4148
4149#ifdef DEBUG_FLICKER
4150 QPainter p;
4151 p.begin( viewport() );
4152
4153 int vx, vy;
4154 contentsToViewport( x, y, vx, vy );
4155 p.fillRect( vx, vy, w, h, Qt::red );
4156 p.end();
4157#endif
4158
4159 d->updateRegion = d->updateRegion.unite(QRect(x,y,w,h));
4160
4161 if (asap && !parsing)
4162 unscheduleRepaint();
4163
4164 if ( !d->repaintTimerId )
4165 d->repaintTimerId = startTimer( time );
4166
4167// kDebug() << "starting timer " << time;
4168}
4169
4170void KHTMLView::complete( bool pendingAction )
4171{
4172// kDebug() << "KHTMLView::complete()";
4173
4174 d->complete = true;
4175
4176 // is there a relayout pending?
4177 if (d->layoutTimerId)
4178 {
4179// kDebug() << "requesting relayout now";
4180 // do it now
4181 killTimer(d->layoutTimerId);
4182 d->layoutTimerId = startTimer( 0 );
4183 d->emitCompletedAfterRepaint = pendingAction ?
4184 KHTMLViewPrivate::CSActionPending : KHTMLViewPrivate::CSFull;
4185 }
4186
4187 // is there a repaint pending?
4188 if (d->repaintTimerId)
4189 {
4190// kDebug() << "requesting repaint now";
4191 // do it now
4192 killTimer(d->repaintTimerId);
4193 d->repaintTimerId = startTimer( 0 );
4194 d->emitCompletedAfterRepaint = pendingAction ?
4195 KHTMLViewPrivate::CSActionPending : KHTMLViewPrivate::CSFull;
4196 }
4197
4198 if (!d->emitCompletedAfterRepaint)
4199 {
4200 if (!pendingAction)
4201 emit m_part->completed();
4202 else
4203 emit m_part->completed(true);
4204 }
4205
4206}
4207
4208void KHTMLView::updateScrollBars()
4209{
4210 const QWidget *view = widget();
4211 if (!view)
4212 return;
4213
4214 QSize p = viewport()->size();
4215 QSize m = maximumViewportSize();
4216
4217 if (m.expandedTo(view->size()) == m)
4218 p = m; // no scroll bars needed
4219
4220 QSize v = view->size();
4221 horizontalScrollBar()->setRange(0, v.width() - p.width());
4222 horizontalScrollBar()->setPageStep(p.width());
4223 verticalScrollBar()->setRange(0, v.height() - p.height());
4224 verticalScrollBar()->setPageStep(p.height());
4225 if (!d->smoothScrolling) {
4226 d->updateContentsXY();
4227 }
4228}
4229
4230void KHTMLView::slotMouseScrollTimer()
4231{
4232 horizontalScrollBar()->setValue( horizontalScrollBar()->value() +d->m_mouseScroll_byX );
4233 verticalScrollBar()->setValue( verticalScrollBar()->value() +d->m_mouseScroll_byY);
4234}
4235
4236
4237static DOM::Position positionOfLineBoundary(const DOM::Position &pos, bool toEnd)
4238{
4239 Selection sel = pos;
4240 sel.expandUsingGranularity(Selection::LINE);
4241 return toEnd ? sel.end() : sel.start();
4242}
4243
4244inline static DOM::Position positionOfLineBegin(const DOM::Position &pos)
4245{
4246 return positionOfLineBoundary(pos, false);
4247}
4248
4249inline static DOM::Position positionOfLineEnd(const DOM::Position &pos)
4250{
4251 return positionOfLineBoundary(pos, true);
4252}
4253
4254bool KHTMLView::caretKeyPressEvent(QKeyEvent *_ke)
4255{
4256 EditorContext *ec = &m_part->d->editor_context;
4257 Selection &caret = ec->m_selection;
4258 Position old_pos = caret.caretPos();
4259 Position pos = old_pos;
4260 bool recalcXPos = true;
4261 bool handled = true;
4262
4263 bool ctrl = _ke->modifiers() & Qt::ControlModifier;
4264 bool shift = _ke->modifiers() & Qt::ShiftModifier;
4265
4266 switch(_ke->key()) {
4267
4268 // -- Navigational keys
4269 case Qt::Key_Down:
4270 pos = old_pos.nextLinePosition(caret.xPosForVerticalArrowNavigation(Selection::EXTENT));
4271 recalcXPos = false;
4272 break;
4273
4274 case Qt::Key_Up:
4275 pos = old_pos.previousLinePosition(caret.xPosForVerticalArrowNavigation(Selection::EXTENT));
4276 recalcXPos = false;
4277 break;
4278
4279 case Qt::Key_Left:
4280 pos = ctrl ? old_pos.previousWordPosition() : old_pos.previousCharacterPosition();
4281 break;
4282
4283 case Qt::Key_Right:
4284 pos = ctrl ? old_pos.nextWordPosition() : old_pos.nextCharacterPosition();
4285 break;
4286
4287 case Qt::Key_PageDown:
4288// moveCaretNextPage(); ###
4289 break;
4290
4291 case Qt::Key_PageUp:
4292// moveCaretPrevPage(); ###
4293 break;
4294
4295 case Qt::Key_Home:
4296 if (ctrl)
4297 /*moveCaretToDocumentBoundary(false)*/; // ###
4298 else
4299 pos = positionOfLineBegin(old_pos);
4300 break;
4301
4302 case Qt::Key_End:
4303 if (ctrl)
4304 /*moveCaretToDocumentBoundary(true)*/; // ###
4305 else
4306 pos = positionOfLineEnd(old_pos);
4307 break;
4308
4309 default:
4310 handled = false;
4311
4312 }/*end switch*/
4313
4314 if (pos != old_pos) {
4315 m_part->clearCaretRectIfNeeded();
4316
4317 caret.moveTo(shift ? caret.nonCaretPos() : pos, pos);
4318 int old_x = caret.xPosForVerticalArrowNavigation(Selection::CARETPOS);
4319
4320 m_part->selectionLayoutChanged();
4321
4322 // restore old x-position to prevent recalculation
4323 if (!recalcXPos)
4324 m_part->d->editor_context.m_xPosForVerticalArrowNavigation = old_x;
4325
4326 m_part->emitCaretPositionChanged(pos);
4327 // ### check when to emit it
4328 m_part->notifySelectionChanged();
4329
4330 }
4331
4332 if (handled) _ke->accept();
4333 return handled;
4334}
4335
4336#undef DEBUG_CARETMODE
4337