• Skip to content
  • Skip to link menu
KDE 4.1 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KHTML

khtmlview.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE project
00002  *
00003  * Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
00004  *                     1999 Lars Knoll <knoll@kde.org>
00005  *                     1999 Antti Koivisto <koivisto@kde.org>
00006  *                     2000-2004 Dirk Mueller <mueller@kde.org>
00007  *                     2003 Leo Savernik <l.savernik@aon.at>
00008  *                     2003-2008 Apple Computer, Inc.
00009  *                     2008 Allan Sandfeld Jensen <kde@carewolf.com>
00010  *                     2006-2008 Germain Garand <germain@ebooksfrance.org>
00011  *
00012  * This library is free software; you can redistribute it and/or
00013  * modify it under the terms of the GNU Library General Public
00014  * License as published by the Free Software Foundation; either
00015  * version 2 of the License, or (at your option) any later version.
00016  *
00017  * This library is distributed in the hope that it will be useful,
00018  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00019  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00020  * Library General Public License for more details.
00021  *
00022  * You should have received a copy of the GNU Library General Public License
00023  * along with this library; see the file COPYING.LIB.  If not, write to
00024  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00025  * Boston, MA 02110-1301, USA.
00026  */
00027 
00028 
00029 #include "khtmlview.h"
00030 
00031 #include "khtmlview.moc"
00032 
00033 #include "khtml_part.h"
00034 #include "khtml_events.h"
00035 #ifdef Q_WS_X11
00036 #include <qx11info_x11.h>
00037 #endif
00038 
00039 #include "html/html_documentimpl.h"
00040 #include "html/html_inlineimpl.h"
00041 #include "html/html_formimpl.h"
00042 #include "editing/editor.h"
00043 #include "rendering/render_arena.h"
00044 #include "rendering/render_canvas.h"
00045 #include "rendering/render_frames.h"
00046 #include "rendering/render_replaced.h"
00047 #include "rendering/render_form.h"
00048 #include "rendering/render_layer.h"
00049 #include "rendering/render_line.h"
00050 #include "rendering/render_table.h"
00051 // removeme
00052 #define protected public
00053 #include "rendering/render_text.h"
00054 #undef protected
00055 #include "xml/dom2_eventsimpl.h"
00056 #include "css/cssstyleselector.h"
00057 #include "css/csshelper.h"
00058 #include "misc/htmlhashes.h"
00059 #include "misc/helper.h"
00060 #include "misc/loader.h"
00061 #include "khtml_settings.h"
00062 #include "khtml_printsettings.h"
00063 
00064 #include "khtmlpart_p.h"
00065 
00066 #include <kcursor.h>
00067 #include <kdebug.h>
00068 #include <kglobalsettings.h>
00069 #include <kdialog.h>
00070 #include <kiconloader.h>
00071 #include <klocale.h>
00072 #include <knotification.h>
00073 #include <kdeprintdialog.h>
00074 #include <kconfig.h>
00075 #include <kstandarddirs.h>
00076 #include <kstandardshortcut.h>
00077 #include <kstringhandler.h>
00078 #include <kconfiggroup.h>
00079 
00080 #include <QtGui/QBitmap>
00081 #include <QtGui/QLabel>
00082 #include <QtCore/QObject>
00083 #include <QtGui/QPainter>
00084 #include <QtCore/QHash>
00085 #include <QtGui/QToolTip>
00086 #include <QtCore/QString>
00087 #include <QtGui/QTextDocument>
00088 #include <QtCore/QTimer>
00089 #include <QtCore/QAbstractEventDispatcher>
00090 #include <QtCore/QVector>
00091 #include <QtGui/QAbstractScrollArea>
00092 #include <QtGui/QPrinter>
00093 #include <QtGui/QPrintDialog>
00094 
00095 //#define DEBUG_FLICKER
00096 
00097 //#define DEBUG_PIXEL
00098 #define FIX_QT_BROKEN_QWIDGET_SCROLL
00099 
00100 #include <limits.h>
00101 #ifdef Q_WS_X11
00102 #include <X11/Xlib.h>
00103 #include <fixx11h.h>
00104 #elif defined(Q_WS_WIN)
00105 #include <windows.h>
00106 #endif
00107 
00108 #if 0
00109 namespace khtml {
00110     void dumpLineBoxes(RenderFlow *flow);
00111 }
00112 #endif
00113 
00114 using namespace DOM;
00115 using namespace khtml;
00116 
00117 #ifndef NDEBUG
00118 static const int sFirstLayoutDelay = 760;
00119 static const int sParsingLayoutsInterval = 420;
00120 static const int sLayoutAttemptDelay = 400;
00121 #else
00122 static const int sFirstLayoutDelay = 540;
00123 static const int sParsingLayoutsInterval = 360;
00124 static const int sLayoutAttemptDelay = 340;
00125 #endif
00126 static const int sLayoutAttemptIncrement = 20;
00127 static const int sParsingLayoutsIncrement = 60;
00128 
00129 static const int sSmoothScrollTime = 140;
00130 static const int sSmoothScrollTick = 14;
00131 
00132 class KHTMLViewPrivate {
00133     friend class KHTMLView;
00134 public:
00135 
00136     enum PseudoFocusNodes {
00137     PFNone,
00138     PFTop,
00139     PFBottom
00140     };
00141 
00142     enum StaticBackgroundState {
00143          SBNone = 0,
00144          SBPartial,
00145          SBFull
00146     };
00147 
00148     enum CompletedState {
00149         CSNone = 0,
00150         CSFull,
00151         CSActionPending
00152     };
00153 
00154     KHTMLViewPrivate(KHTMLView* v)
00155         : underMouse( 0 ), underMouseNonShared( 0 ), oldUnderMouse( 0 )
00156     {
00157         postponed_autorepeat = NULL;
00158         scrollingFromWheelTimerId = 0;
00159         smoothScrollMode = KHTMLView::SSMWhenEfficient;
00160         reset();
00161         vpolicy = Qt::ScrollBarAsNeeded;
00162     hpolicy = Qt::ScrollBarAsNeeded;
00163         formCompletions=0;
00164         prevScrollbarVisible = true;
00165 
00166         possibleTripleClick = false;
00167         emitCompletedAfterRepaint = CSNone;
00168         cursorIconWidget = 0;
00169         cursorIconType   = KHTMLView::LINK_NORMAL;
00170         m_mouseScrollTimer = 0;
00171         m_mouseScrollIndicator = 0;
00172         contentsX = 0;
00173         contentsY = 0;
00174         view = v;
00175     }
00176     ~KHTMLViewPrivate()
00177     {
00178         delete formCompletions;
00179         delete postponed_autorepeat;
00180         if (underMouse)
00181         underMouse->deref();
00182         if (underMouseNonShared)
00183         underMouseNonShared->deref();
00184         if (oldUnderMouse)
00185             oldUnderMouse->deref();
00186 
00187         delete cursorIconWidget;
00188         delete m_mouseScrollTimer;
00189         delete m_mouseScrollIndicator;
00190     }
00191     void reset()
00192     {
00193         if (underMouse)
00194         underMouse->deref();
00195     underMouse = 0;
00196         if (underMouseNonShared)
00197         underMouseNonShared->deref();
00198     underMouseNonShared = 0;
00199     if (oldUnderMouse)
00200         oldUnderMouse->deref();
00201         oldUnderMouse = 0;
00202         linkPressed = false;
00203         staticWidget = SBNone;
00204         fixedObjectsCount = 0;
00205         staticObjectsCount = 0;
00206     tabMovePending = false;
00207     lastTabbingDirection = true;
00208     pseudoFocusNode = PFNone;
00209     zoomLevel = 100;
00210 #ifndef KHTML_NO_SCROLLBARS
00211         //We don't turn off the toolbars here
00212     //since if the user turns them
00213     //off, then chances are they want them turned
00214     //off always - even after a reset.
00215 #else
00216         vpolicy = ScrollBarAlwaysOff;
00217         hpolicy = ScrollBarAlwaysOff;
00218 #endif
00219 #ifdef DEBUG_PIXEL
00220         timer.start();
00221         pixelbooth = 0;
00222         repaintbooth = 0;
00223 #endif
00224         scrollBarMoved = false;
00225         contentsMoving = false;
00226         ignoreWheelEvents = false;
00227         scrollingFromWheel = QPoint(-1,-1);
00228     borderX = 30;
00229     borderY = 30;
00230     dx = dy = ddx = ddy = rdx = rdy = dddx = dddy = 0;
00231         paged = false;
00232     clickX = -1;
00233     clickY = -1;
00234     clickCount = 0;
00235     isDoubleClick = false;
00236     scrollingSelf = false;
00237         delete postponed_autorepeat;
00238         postponed_autorepeat = NULL;
00239     layoutTimerId = 0;
00240         repaintTimerId = 0;
00241         scrollTimerId = 0;
00242         scrollSuspended = false;
00243         scrollSuspendPreActivate = false;
00244         smoothScrolling = false;
00245         smoothScrollModeIsDefault = true;
00246         shouldSmoothScroll = false;
00247         hasFrameset = false;
00248 #ifdef FIX_QT_BROKEN_QWIDGET_SCROLL
00249         oldVScrollUpdatesEnabled = true;
00250         oldHScrollUpdatesEnabled = true;
00251         oldHScrollOpaquePE = false;
00252         oldVScrollOpaquePE = false;
00253         brokenQWidgetScroll = false;
00254         shouldBeBlitting = false;
00255 #endif
00256         complete = false;
00257         firstLayoutPending = true;
00258         firstRepaintPending = true;
00259         needsFullRepaint = true;
00260         dirtyLayout = false;
00261         layoutSchedulingEnabled = true;
00262         painting = false;
00263         layoutCounter = 0;
00264         layoutAttemptCounter = 0;
00265         scheduledLayoutCounter = 0;
00266         updateRegion = QRegion();
00267         m_dialogsAllowed = true;
00268 #ifndef KHTML_NO_TYPE_AHEAD_FIND
00269         typeAheadActivated = false;
00270 #endif // KHTML_NO_TYPE_AHEAD_FIND
00271     accessKeysActivated = false;
00272     accessKeysPreActivate = false;
00273 
00274         // the view might have been built before the part it will be assigned to,
00275         // so exceptionally, we need to directly ref/deref KHTMLGlobal to
00276         // account for this transitory case.
00277         KHTMLGlobal::ref();
00278         accessKeysEnabled = KHTMLGlobal::defaultHTMLSettings()->accessKeysEnabled();
00279         KHTMLGlobal::deref();
00280 
00281         emitCompletedAfterRepaint = CSNone;
00282         m_mouseEventsTarget = 0;
00283         m_clipHolder = 0;
00284     }
00285     void newScrollTimer(QWidget *view, int tid)
00286     {
00287         //kDebug(6000) << "newScrollTimer timer " << tid;
00288         view->killTimer(scrollTimerId);
00289         scrollTimerId = tid;
00290         scrollSuspended = false;
00291     }
00292     enum ScrollDirection { ScrollLeft, ScrollRight, ScrollUp, ScrollDown };
00293 
00294     void adjustScroller(QWidget *view, ScrollDirection direction, ScrollDirection oppositedir)
00295     {
00296         static const struct { int msec, pixels; } timings [] = {
00297             {320,1}, {224,1}, {160,1}, {112,1}, {80,1}, {56,1}, {40,1},
00298             {28,1}, {20,1}, {20,2}, {20,3}, {20,4}, {20,6}, {20,8}, {0,0}
00299         };
00300         if (!scrollTimerId ||
00301             (static_cast<int>(scrollDirection) != direction &&
00302              (static_cast<int>(scrollDirection) != oppositedir || scrollSuspended))) {
00303             scrollTiming = 6;
00304             scrollBy = timings[scrollTiming].pixels;
00305             scrollDirection = direction;
00306             newScrollTimer(view, view->startTimer(timings[scrollTiming].msec));
00307         } else if (scrollDirection == direction &&
00308                    timings[scrollTiming+1].msec && !scrollSuspended) {
00309             scrollBy = timings[++scrollTiming].pixels;
00310             newScrollTimer(view, view->startTimer(timings[scrollTiming].msec));
00311         } else if (scrollDirection == oppositedir) {
00312             if (scrollTiming) {
00313                 scrollBy = timings[--scrollTiming].pixels;
00314                 newScrollTimer(view, view->startTimer(timings[scrollTiming].msec));
00315             }
00316         }
00317         scrollSuspended = false;
00318     }
00319 
00320     bool haveZoom() const { return zoomLevel != 100; }
00321 
00322     void startScrolling()
00323     {
00324         smoothScrolling = true;
00325         smoothScrollTimer.start(sSmoothScrollTick);
00326         shouldSmoothScroll = false;
00327 #ifdef FIX_QT_BROKEN_QWIDGET_SCROLL
00328         if (view->horizontalScrollBar()->isVisible() && view->verticalScrollBar()->isVisible()) {
00329             if (!dx) {
00330                 oldHScrollOpaquePE = view->horizontalScrollBar()->parentWidget()->testAttribute( Qt::WA_OpaquePaintEvent );
00331                 view->horizontalScrollBar()->parentWidget()->setAttribute( Qt::WA_OpaquePaintEvent );
00332                 oldHScrollUpdatesEnabled = view->horizontalScrollBar()->parentWidget()->updatesEnabled();
00333                 view->horizontalScrollBar()->parentWidget()->setUpdatesEnabled( false );            
00334             }
00335             if (!dy) {
00336                 oldVScrollOpaquePE = view->verticalScrollBar()->parentWidget()->testAttribute( Qt::WA_OpaquePaintEvent );
00337                 view->verticalScrollBar()->parentWidget()->setAttribute( Qt::WA_OpaquePaintEvent );
00338                 oldVScrollUpdatesEnabled = view->verticalScrollBar()->parentWidget()->updatesEnabled();
00339                 view->verticalScrollBar()->parentWidget()->setUpdatesEnabled( false );
00340             }
00341         }
00342 #endif
00343     }
00344 
00345     void stopScrolling()
00346     {
00347         smoothScrollTimer.stop();
00348         dx = dy = 0;
00349         ddx = ddy = 0;
00350         rdx = rdy = 0;
00351         dddx = dddy = 0;
00352         updateContentsXY();
00353         smoothScrolling = false;
00354         shouldSmoothScroll = false;
00355 #ifdef FIX_QT_BROKEN_QWIDGET_SCROLL
00356         if (!oldHScrollOpaquePE && view->horizontalScrollBar()->parentWidget()->testAttribute( Qt::WA_OpaquePaintEvent ))
00357             view->horizontalScrollBar()->parentWidget()->setAttribute( Qt::WA_OpaquePaintEvent, false );
00358         if (!oldVScrollOpaquePE && view->verticalScrollBar()->parentWidget()->testAttribute( Qt::WA_OpaquePaintEvent ))
00359             view->verticalScrollBar()->parentWidget()->setAttribute( Qt::WA_OpaquePaintEvent, false );
00360         if (!view->horizontalScrollBar()->parentWidget()->updatesEnabled() && oldHScrollUpdatesEnabled)
00361             view->horizontalScrollBar()->parentWidget()->setUpdatesEnabled( true );
00362         if (!view->verticalScrollBar()->parentWidget()->updatesEnabled() && oldVScrollUpdatesEnabled)
00363             view->verticalScrollBar()->parentWidget()->setUpdatesEnabled( true );
00364 #endif
00365     }
00366 
00367     void updateContentsXY()
00368     {
00369         contentsX = QApplication::isRightToLeft() ?
00370                         view->horizontalScrollBar()->maximum()-view->horizontalScrollBar()->value() : view->horizontalScrollBar()->value();
00371         contentsY = view->verticalScrollBar()->value();
00372     }
00373 
00374     void scrollExternalWidgets(int dx, int dy)
00375     {
00376         if (visibleWidgets.isEmpty())
00377             return;
00378 
00379         QHashIterator<void*, QWidget*> it(visibleWidgets);
00380         while (it.hasNext()) {
00381             it.next();
00382             it.value()->move( it.value()->pos() + QPoint(dx, dy) );
00383         }
00384     }
00385 
00386 #ifdef DEBUG_PIXEL
00387     QTime timer;
00388     unsigned int pixelbooth;
00389     unsigned int repaintbooth;
00390 #endif
00391 
00392     NodeImpl *underMouse;
00393     NodeImpl *underMouseNonShared;
00394     NodeImpl *oldUnderMouse;
00395 
00396     // Do not adjust bitfield enums sizes.
00397     // They are oversized because they are signed on some platforms.
00398     bool tabMovePending:1;
00399     bool lastTabbingDirection:1;
00400     PseudoFocusNodes pseudoFocusNode:3;
00401     bool scrollBarMoved:1;
00402     bool contentsMoving:1;
00403 
00404     Qt::ScrollBarPolicy vpolicy;
00405     Qt::ScrollBarPolicy hpolicy;
00406     bool prevScrollbarVisible:1;
00407     bool linkPressed:1;
00408     bool ignoreWheelEvents:1;
00409     StaticBackgroundState staticWidget: 3;
00410     int staticObjectsCount;
00411     int fixedObjectsCount;
00412 
00413     int zoomLevel;
00414     int borderX, borderY;
00415     int dx, dy, ddx, ddy, rdx, rdy, dddx, dddy;
00416     KConfig *formCompletions;
00417 
00418     int clickX, clickY, clickCount;
00419     bool isDoubleClick;
00420 
00421     bool paged;
00422 
00423     bool scrollingSelf;
00424     int contentsX, contentsY;
00425     int layoutTimerId;
00426     QKeyEvent* postponed_autorepeat;
00427 
00428     int repaintTimerId;
00429     int scrollTimerId;
00430     int scrollTiming;
00431     int scrollBy;
00432     ScrollDirection scrollDirection     :3;
00433     bool scrollSuspended            :1;
00434     bool scrollSuspendPreActivate       :1;
00435     KHTMLView::SmoothScrollingMode smoothScrollMode :3;
00436     bool smoothScrolling                          :1;
00437     bool smoothScrollModeIsDefault                :1;
00438     bool shouldSmoothScroll                       :1;
00439     bool hasFrameset                              :1;
00440 #ifdef FIX_QT_BROKEN_QWIDGET_SCROLL
00441     bool oldHScrollUpdatesEnabled                 :1;
00442     bool oldVScrollUpdatesEnabled                 :1;
00443     bool oldHScrollOpaquePE                       :1;
00444     bool oldVScrollOpaquePE                       :1;
00445     bool brokenQWidgetScroll                      :1;
00446     bool shouldBeBlitting                         :1;
00447 #endif
00448     bool complete               :1;
00449     bool firstLayoutPending         :1;
00450     bool firstRepaintPending                    :1;
00451     bool layoutSchedulingEnabled        :1;
00452     bool needsFullRepaint           :1;
00453     bool painting               :1;
00454     bool possibleTripleClick            :1;
00455     bool dirtyLayout                           :1;
00456     bool m_dialogsAllowed           :1;
00457     int layoutCounter;
00458     int layoutAttemptCounter;
00459     int scheduledLayoutCounter;
00460     QRegion updateRegion;
00461     QTimer smoothScrollTimer;
00462     QHash<void*, QWidget*> visibleWidgets;
00463 #ifndef KHTML_NO_TYPE_AHEAD_FIND
00464     QString findString;
00465     QTimer timer;
00466     bool findLinksOnly;
00467     bool typeAheadActivated;
00468 #endif // KHTML_NO_TYPE_AHEAD_FIND
00469     bool accessKeysEnabled;
00470     bool accessKeysActivated;
00471     bool accessKeysPreActivate;
00472     CompletedState emitCompletedAfterRepaint;
00473 
00474     QLabel*               cursorIconWidget;
00475     KHTMLView::LinkCursor cursorIconType;
00476 
00477     // scrolling activated by MMB
00478     short m_mouseScroll_byX;
00479     short m_mouseScroll_byY;
00480     QPoint scrollingFromWheel;
00481     int scrollingFromWheelTimerId;
00482     QTimer *m_mouseScrollTimer;
00483     QWidget *m_mouseScrollIndicator;
00484     QPointer<QWidget> m_mouseEventsTarget;
00485     QStack<QRegion>* m_clipHolder;
00486     KHTMLView* view;
00487 };
00488 
00489 #ifndef QT_NO_TOOLTIP
00490 
00500 static bool findImageMapRect(HTMLImageElementImpl *img, const QPoint &scrollOfs,
00501             const QPoint &p, QRect &r, QString &s)
00502 {
00503     HTMLMapElementImpl* map;
00504     if (img && img->document()->isHTMLDocument() &&
00505         (map = static_cast<HTMLDocumentImpl*>(img->document())->getMap(img->imageMap()))) {
00506         RenderObject::NodeInfo info(true, false);
00507         RenderObject *rend = img->renderer();
00508         int ax, ay;
00509         if (!rend || !rend->absolutePosition(ax, ay))
00510             return false;
00511         // we're a client side image map
00512         bool inside = map->mapMouseEvent(p.x() - ax + scrollOfs.x(),
00513                 p.y() - ay + scrollOfs.y(), rend->contentWidth(),
00514                 rend->contentHeight(), info);
00515         if (inside && info.URLElement()) {
00516             HTMLAreaElementImpl *area = static_cast<HTMLAreaElementImpl *>(info.URLElement());
00517             Q_ASSERT(area->id() == ID_AREA);
00518             s = area->getAttribute(ATTR_TITLE).string();
00519             QRegion reg = area->cachedRegion();
00520             if (!s.isEmpty() && !reg.isEmpty()) {
00521                 r = reg.boundingRect();
00522                 r.translate(ax, ay);
00523                 return true;
00524             }
00525         }
00526     }
00527     return false;
00528 }
00529 
00530 bool KHTMLView::event( QEvent* e )
00531 {
00532     switch ( e->type() ) {
00533     case QEvent::ToolTip: {
00534         QHelpEvent *he = static_cast<QHelpEvent*>(e);
00535         QPoint     p   = he->pos();
00536 
00537         DOM::NodeImpl *node = d->underMouseNonShared;
00538         QRect region;
00539         while ( node ) {
00540             if ( node->isElementNode() ) {
00541                 DOM::ElementImpl *e = static_cast<DOM::ElementImpl*>( node );
00542                 QRect r;
00543                 QString s;
00544                 bool found = false;
00545                 // for images, check if it is part of a client-side image map,
00546                 // and query the <area>s' title attributes, too
00547                 if (e->id() == ID_IMG && !e->getAttribute( ATTR_USEMAP ).isEmpty()) {
00548                     found = findImageMapRect(static_cast<HTMLImageElementImpl *>(e),
00549                                 viewportToContents(QPoint(0, 0)), p, r, s);
00550                 }
00551                 if (!found) {
00552                     s = e->getAttribute( ATTR_TITLE ).string();
00553                     r = node->getRect();
00554                 }
00555                 region |= QRect( contentsToViewport( r.topLeft() ), r.size() );
00556                 if ( !s.isEmpty() ) {
00557                     QToolTip::showText( viewport()->mapToGlobal(region.bottomLeft()),
00558                         Qt::convertFromPlainText( s, Qt::WhiteSpaceNormal ) );
00559                     break;
00560                 }
00561             }
00562             node = node->parentNode();
00563         }
00564         return true;
00565     }
00566 
00567     case QEvent::DragEnter:
00568     case QEvent::DragMove:
00569     case QEvent::DragLeave:
00570     case QEvent::Drop:
00571       // In Qt4, one needs to both call accept() on the DND event and return
00572       // true on ::event for the candidate widget for the drop to be possible.
00573       // Apps hosting us, such as konq, can do the former but not the later.
00574       // We will do the second bit, as it's a no-op unless someone else explicitly
00575       // accepts the event. We need to skip the scrollarea to do that,
00576       // since it will just skip the events, both killing the drop, and
00577       // not permitting us to forward it up the part hiearchy in our dragEnterEvent,
00578       // etc. handlers
00579       return QWidget::event(e);
00580     case QEvent::StyleChange:
00581     case QEvent::LayoutRequest: {
00582          updateScrollBars();
00583          return QAbstractScrollArea::event(e);
00584     }
00585     default:
00586       return QScrollArea::event(e);
00587     }
00588 }
00589 #endif
00590 
00591 KHTMLView::KHTMLView( KHTMLPart *part, QWidget *parent )
00592     : QScrollArea( parent ), d( new KHTMLViewPrivate( this ) )
00593 {
00594     m_medium = "screen";
00595 
00596     m_part = part;
00597 
00598     QScrollArea::setVerticalScrollBarPolicy(d->vpolicy);
00599     QScrollArea::setHorizontalScrollBarPolicy(d->hpolicy);
00600     connect(KGlobalSettings::self(), SIGNAL(kdisplayPaletteChanged()), this, SLOT(slotPaletteChanged()));
00601 
00602 #ifndef KHTML_NO_TYPE_AHEAD_FIND
00603     connect(&d->timer, SIGNAL(timeout()), this, SLOT(findTimeout()));
00604 #endif // KHTML_NO_TYPE_AHEAD_FIND
00605 
00606     init();
00607     widget()->setMouseTracking(true);
00608 }
00609 
00610 KHTMLView::~KHTMLView()
00611 {
00612     closeChildDialogs();
00613     if (m_part)
00614     {
00615         DOM::DocumentImpl *doc = m_part->xmlDocImpl();
00616         if (doc)
00617             doc->detach();
00618     }
00619     delete d;
00620 }
00621 
00622 void KHTMLView::setPart(KHTMLPart *part)
00623 {
00624     assert(part && !m_part);
00625     m_part = part;
00626 }
00627 
00628 void KHTMLView::init()
00629 {
00630     // Do not access the part here. It might not be fully constructed.
00631 
00632     setFrameStyle(QFrame::NoFrame);
00633     setFocusPolicy(Qt::StrongFocus);
00634     viewport()->setFocusProxy(this);
00635 
00636     _marginWidth = -1; // undefined
00637     _marginHeight = -1;
00638     _width = 0;
00639     _height = 0;
00640 
00641     installEventFilter(this);
00642 
00643     setAcceptDrops(true);
00644     if (!widget())
00645         setWidget( new QWidget(this) );
00646     widget()->setAttribute( Qt::WA_NoSystemBackground );
00647 
00648     verticalScrollBar()->setCursor( Qt::ArrowCursor );
00649     horizontalScrollBar()->setCursor( Qt::ArrowCursor );
00650 
00651     connect(&d->smoothScrollTimer, SIGNAL(timeout()), this, SLOT(scrollTick()));
00652 }
00653 
00654 void KHTMLView::resizeContentsToViewport()
00655 {
00656     QSize s = viewport()->size();
00657     resizeContents(s.width(), s.height());
00658 }
00659 
00660 
00661 // called by KHTMLPart::clear()
00662 void KHTMLView::clear()
00663 {
00664 #ifndef KHTML_NO_TYPE_AHEAD_FIND
00665     if( d->typeAheadActivated )
00666         findTimeout();
00667 #endif
00668     if (d->accessKeysEnabled && d->accessKeysActivated)
00669         accessKeysTimeout();
00670     viewport()->unsetCursor();
00671     if ( d->cursorIconWidget )
00672         d->cursorIconWidget->hide();
00673     if (d->smoothScrolling)
00674         d->stopScrolling();
00675     d->reset();
00676     QAbstractEventDispatcher::instance()->unregisterTimers(this);
00677     emit cleared();
00678 
00679     QScrollArea::setHorizontalScrollBarPolicy(d->hpolicy);
00680     QScrollArea::setVerticalScrollBarPolicy(d->vpolicy);
00681     verticalScrollBar()->setEnabled( false );
00682     horizontalScrollBar()->setEnabled( false );
00683 
00684 }
00685 
00686 void KHTMLView::hideEvent(QHideEvent* e)
00687 {
00688     QScrollArea::hideEvent(e);
00689     if ( m_part && m_part->xmlDocImpl() )
00690         m_part->xmlDocImpl()->docLoader()->pauseAnimations();
00691 }
00692 
00693 void KHTMLView::showEvent(QShowEvent* e)
00694 {
00695     QScrollArea::showEvent(e);
00696     if ( m_part && m_part->xmlDocImpl() )
00697         m_part->xmlDocImpl()->docLoader()->resumeAnimations();
00698 }
00699 
00700 void KHTMLView::setMouseEventsTarget( QWidget* w )
00701 {
00702     d->m_mouseEventsTarget = w;
00703 }
00704 
00705 QWidget* KHTMLView::mouseEventsTarget() const
00706 {
00707     return d->m_mouseEventsTarget;
00708 }
00709 
00710 void KHTMLView::setClipHolder( QStack<QRegion>* ch )
00711 {
00712     d->m_clipHolder = ch;
00713 }
00714 
00715 QStack<QRegion>* KHTMLView::clipHolder() const
00716 {
00717     return d->m_clipHolder;
00718 }
00719 
00720 int KHTMLView::contentsWidth() const
00721 {
00722     return widget() ? widget()->width() : 0;
00723 }
00724 
00725 int KHTMLView::contentsHeight() const
00726 {
00727     return widget() ? widget()->height() : 0;
00728 }
00729 
00730 void KHTMLView::resizeContents(int w, int h)
00731 {
00732     if (!widget())
00733         return;
00734     widget()->resize(w, h);
00735     if (!widget()->isVisible())
00736         updateScrollBars();
00737 #ifdef FIX_QT_BROKEN_QWIDGET_SCROLL
00738     if (!horizontalScrollBar()->isVisible() || !verticalScrollBar()->isVisible())
00739         d->brokenQWidgetScroll = false;
00740 #endif
00741 }
00742 
00743 int KHTMLView::contentsX() const
00744 {
00745     return d->contentsX;
00746 }
00747 
00748 int KHTMLView::contentsY() const
00749 {
00750     return d->contentsY;
00751 }
00752 
00753 int KHTMLView::visibleWidth() const
00754 {
00755     if (m_kwp->isRedirected()) {
00756         // our RenderWidget knows better
00757         if (RenderWidget* rw = m_kwp->renderWidget()) {
00758             int ret = rw->width()-rw->paddingLeft()-rw->paddingRight()-rw->borderLeft()-rw->borderRight();
00759             if (verticalScrollBar()->isVisible()) {
00760                 ret -= style()->pixelMetric(QStyle::PM_ScrollBarExtent);
00761                 int lhs = style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing);
00762                 if (lhs > 0)
00763                     ret -= lhs;
00764                 ret = qMax(0, ret);
00765             }
00766             return ret;
00767         }
00768     }
00769     return viewport()->width();
00770 }
00771 
00772 int KHTMLView::visibleHeight() const
00773 {
00774     if (m_kwp->isRedirected()) {
00775         // our RenderWidget knows better
00776         if (RenderWidget* rw = m_kwp->renderWidget()) {
00777             int ret = rw->height()-rw->paddingBottom()-rw->paddingTop()-rw->borderTop()-rw->borderBottom();
00778             if (horizontalScrollBar()->isVisible()) {
00779                 ret -= style()->pixelMetric(QStyle::PM_ScrollBarExtent);
00780                 int lvs = style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing);
00781                 if (lvs > 0)
00782                     ret -= lvs;
00783                 ret = qMax(0, ret);
00784             }
00785             return ret;
00786         }
00787     }
00788     return viewport()->height();
00789 }
00790 
00791 void KHTMLView::setContentsPos( int x, int y)
00792 {
00793    horizontalScrollBar()->setValue( QApplication::isRightToLeft() ?
00794                            horizontalScrollBar()->maximum()-x : x );
00795    verticalScrollBar()->setValue( y );
00796 }
00797 
00798 void KHTMLView::scrollBy(int x, int y)
00799 {
00800    horizontalScrollBar()->setValue( horizontalScrollBar()->value()+x );
00801    verticalScrollBar()->setValue( verticalScrollBar()->value()+y );
00802 }
00803 
00804 QPoint KHTMLView::contentsToViewport(const QPoint& p) const
00805 {
00806     return QPoint(p.x()-contentsX(), p.y()-contentsY());
00807 }
00808 
00809 void KHTMLView::contentsToViewport(int x, int y, int& cx, int& cy) const
00810 {
00811     QPoint p(x,y);
00812     p = contentsToViewport(p);
00813     cx = p.x();
00814     cy = p.y();
00815 }
00816 
00817 QPoint KHTMLView::viewportToContents(const QPoint& p) const
00818 {
00819     return QPoint(p.x()+contentsX(), p.y()+contentsY());
00820 }
00821 
00822 void KHTMLView::viewportToContents(int x, int y, int& cx, int& cy) const
00823 {
00824     QPoint p(x,y);
00825     p = viewportToContents(p);
00826     cx = p.x();
00827     cy = p.y();
00828 }
00829 
00830 void KHTMLView::updateContents(int x, int y, int w, int h)
00831 {
00832     applyTransforms(x, y, w, h);
00833     if (m_kwp->isRedirected()) {
00834         QPoint off = m_kwp->absolutePos();
00835         KHTMLView* pview = m_part->parentPart()->view();
00836         pview->updateContents(x+off.x(), y+off.y(), w, h);
00837     } else
00838         widget()->update(x, y, w, h);
00839 }
00840 
00841 void KHTMLView::updateContents( const QRect& r )
00842 {
00843     updateContents( r.x(), r.y(), r.width(), r.height() );
00844 }
00845 
00846 void KHTMLView::repaintContents(int x, int y, int w, int h)
00847 {
00848     applyTransforms(x, y, w, h);
00849     if (m_kwp->isRedirected()) {
00850         QPoint off = m_kwp->absolutePos();
00851         KHTMLView* pview = m_part->parentPart()->view();
00852         pview->repaintContents(x+off.x(), y+off.y(), w, h);
00853     } else
00854         widget()->repaint(x, y, w, h);
00855 }
00856 
00857 void KHTMLView::repaintContents( const QRect& r )
00858 {
00859     repaintContents( r.x(), r.y(), r.width(), r.height() );
00860 }
00861 
00862 void KHTMLView::applyTransforms( int& x, int& y, int& w, int& h) const
00863 {
00864     x -= contentsX();
00865     y -= contentsY();
00866     if (d->haveZoom()) {
00867         const int z = d->zoomLevel;
00868         x = x*z/100;
00869         y = y*z/100;
00870         w = w*z/100;
00871         h = h*z/100;
00872     }
00873 }
00874 
00875 void KHTMLView::revertTransforms( int& x, int& y, int& w, int& h) const
00876 {
00877     x += contentsX();
00878     y += contentsY();
00879     if (d->haveZoom()) {
00880         const int z = d->zoomLevel;
00881         x = x*100/z;
00882         y = y*100/z;
00883         w = w*100/z;
00884         h = h*100/z;
00885     }
00886 }
00887 
00888 void KHTMLView::revertTransforms( int& x, int& y ) const
00889 {
00890     int dummy = 0;
00891     revertTransforms(x, y, dummy, dummy);
00892 }
00893 
00894 void KHTMLView::resizeEvent (QResizeEvent* /*e*/)
00895 {
00896     updateScrollBars();
00897 
00898     // If we didn't load anything, make white area as big as the view
00899     if (!m_part->xmlDocImpl())
00900         resizeContentsToViewport();
00901 
00902     // Viewport-dependent media queries may cause us to need completely different style information.
00903     if (m_part->xmlDocImpl() && m_part->xmlDocImpl()->styleSelector()->affectedByViewportChange()) {
00904          m_part->xmlDocImpl()->updateStyleSelector();
00905     }
00906 
00907     if (d->layoutSchedulingEnabled)
00908         layout();
00909 
00910     QApplication::sendPostedEvents(viewport(), QEvent::Paint);
00911 
00912     if ( m_part && m_part->xmlDocImpl() )
00913         m_part->xmlDocImpl()->dispatchWindowEvent( EventImpl::RESIZE_EVENT, false, false );
00914 }
00915 
00916 void KHTMLView::paintEvent( QPaintEvent *e )
00917 {
00918     QPainter p(widget());
00919 
00920     QRect r = e->rect();
00921     QRect v(contentsX(), contentsY(), visibleWidth(), visibleHeight());
00922     QPoint off(contentsX(),contentsY());
00923     p.translate(-off);
00924     r.translate(off);
00925 
00926     r = r.intersect(v);
00927 
00928     if (!r.isValid() || r.isEmpty()) return;
00929 
00930 #ifdef FIX_QT_BROKEN_QWIDGET_SCROLL
00931     if (d->shouldBeBlitting && r.width() == v.width() && r.height() == v.height()) {
00932         d->brokenQWidgetScroll = true;
00933     }
00934     d->shouldBeBlitting = false;
00935 #endif
00936 
00937     if (d->haveZoom()) {
00938         p.scale( d->zoomLevel/100., d->zoomLevel/100.);
00939 
00940         r.setX(r.x()*100/d->zoomLevel);
00941         r.setY(r.y()*100/d->zoomLevel);
00942         r.setWidth(r.width()*100/d->zoomLevel);
00943         r.setHeight(r.height()*100/d->zoomLevel);
00944         r.adjust(-1,-1,1,1);
00945     }
00946     p.setClipRect(r);
00947 
00948     int ex = r.x();
00949     int ey = r.y();
00950     int ew = r.width();
00951     int eh = r.height();
00952 
00953     if(!m_part || !m_part->xmlDocImpl() || !m_part->xmlDocImpl()->renderer()) {
00954         p.fillRect(ex, ey, ew, eh, palette().brush(QPalette::Active, QPalette::Base));
00955         return;
00956     } else if ( d->complete && static_cast<RenderCanvas*>(m_part->xmlDocImpl()->renderer())->needsLayout() ) {
00957         // an external update request happens while we have a layout scheduled
00958         unscheduleRelayout();
00959         layout();
00960     }
00961 
00962     if (d->painting) {
00963         kDebug( 6000 ) << "WARNING: paintEvent reentered! ";
00964         kDebug( 6000 ) << kBacktrace();
00965         return;
00966     }
00967     d->painting = true;
00968 
00969     m_part->xmlDocImpl()->renderer()->layer()->paint(&p, r);
00970 
00971     if (d->hasFrameset) {
00972         NodeImpl *body = static_cast<HTMLDocumentImpl*>(m_part->xmlDocImpl())->body();
00973         if(body && body->renderer() && body->id() == ID_FRAMESET)
00974             static_cast<RenderFrameSet*>(body->renderer())->paintFrameSetRules(&p, r);
00975         else
00976             d->hasFrameset = false;
00977     }
00978 
00979     khtml::DrawContentsEvent event( &p, ex, ey, ew, eh );
00980     QApplication::sendEvent( m_part, &event );
00981 
00982     if (d->contentsMoving && !d->smoothScrolling && widget()->underMouse()) {
00983         QMouseEvent *tempEvent = new QMouseEvent( QEvent::MouseMove, widget()->mapFromGlobal( QCursor::pos() ),
00984                                               Qt::NoButton, Qt::NoButton, Qt::NoModifier );
00985         QApplication::postEvent(widget(), tempEvent);
00986     }
00987 
00988     d->painting = false;
00989     d->firstRepaintPending = false;
00990 }
00991 
00992 void KHTMLView::setMarginWidth(int w)
00993 {
00994     // make it update the rendering area when set
00995     _marginWidth = w;
00996 }
00997 
00998 void KHTMLView::setMarginHeight(int h)
00999 {
01000     // make it update the rendering area when set
01001     _marginHeight = h;
01002 }
01003 
01004 void KHTMLView::layout()
01005 {
01006     if( m_part && m_part->xmlDocImpl() ) {
01007         DOM::DocumentImpl *document = m_part->xmlDocImpl();
01008 
01009         khtml::RenderCanvas* canvas = static_cast<khtml::RenderCanvas *>(document->renderer());
01010         if ( !canvas ) return;
01011 
01012         d->layoutSchedulingEnabled=false;
01013         d->dirtyLayout = true;
01014 
01015         // the reference object for the overflow property on canvas
01016         RenderObject * ref = 0;
01017         RenderObject* root = document->documentElement() ? document->documentElement()->renderer() : 0;
01018 
01019         if (document->isHTMLDocument()) {
01020              NodeImpl *body = static_cast<HTMLDocumentImpl*>(document)->body();
01021              if(body && body->renderer() && body->id() == ID_FRAMESET) {
01022                  QScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
01023                  QScrollArea::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
01024                  body->renderer()->setNeedsLayout(true);
01025                  d->hasFrameset = true;
01026              }
01027              else if (root) // only apply body's overflow to canvas if root has a visible overflow
01028                      ref = (!body || root->style()->hidesOverflow()) ? root : body->renderer();
01029         } else {
01030             ref = root;
01031         }
01032         if (ref) {
01033             if( ref->style()->overflowX() == OHIDDEN ) {
01034                 if (d->hpolicy == Qt::ScrollBarAsNeeded) QScrollArea::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
01035             } else if (ref->style()->overflowX() == OSCROLL ) {
01036                 if (d->hpolicy == Qt::ScrollBarAsNeeded) QScrollArea::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
01037             } else if (horizontalScrollBarPolicy() != d->hpolicy) {
01038                 QScrollArea::setHorizontalScrollBarPolicy(d->hpolicy);
01039             }
01040             if ( ref->style()->overflowY() == OHIDDEN ) {
01041                 if (d->vpolicy == Qt::ScrollBarAsNeeded) QScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
01042             } else if (ref->style()->overflowY() == OSCROLL ) {
01043                 if (d->vpolicy == Qt::ScrollBarAsNeeded) QScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
01044             } else if (verticalScrollBarPolicy() != d->vpolicy) {
01045                 QScrollArea::setVerticalScrollBarPolicy(d->vpolicy);
01046             }
01047         }
01048         d->needsFullRepaint = d->firstLayoutPending;
01049         if (_height !=  visibleHeight() || _width != visibleWidth()) {;
01050             d->needsFullRepaint = true;
01051             _height = visibleHeight();
01052             _width = visibleWidth();
01053         }
01054 
01055         canvas->layout();
01056 
01057         emit finishedLayout();
01058         if (d->firstLayoutPending) {
01059             // make sure firstLayoutPending is set to false now in case this layout
01060             // wasn't scheduled
01061             d->firstLayoutPending = false;
01062             verticalScrollBar()->setEnabled( true );
01063             horizontalScrollBar()->setEnabled( true );
01064         }
01065         d->layoutCounter++;
01066 
01067         if (d->accessKeysEnabled && d->accessKeysActivated) {
01068             emit hideAccessKeys();
01069             displayAccessKeys();
01070         }
01071     }
01072     else
01073        _width = visibleWidth();
01074 
01075     if (d->layoutTimerId)
01076         killTimer(d->layoutTimerId);
01077     d->layoutTimerId = 0;
01078     d->layoutSchedulingEnabled=true;
01079 }
01080 
01081 void KHTMLView::closeChildDialogs()
01082 {
01083     QList<QDialog *> dlgs = findChildren<QDialog *>();
01084     foreach (QDialog *dlg, dlgs)
01085     {
01086         KDialog* dlgbase = dynamic_cast<KDialog*>( dlg );
01087         if ( dlgbase ) {
01088             if ( dlgbase->testAttribute( Qt::WA_ShowModal ) ) {
01089                 kDebug(6000) << "closeChildDialogs: closing dialog " << dlgbase;
01090                 // close() ends up calling QButton::animateClick, which isn't immediate
01091                 // we need something the exits the event loop immediately (#49068)
01092                 dlgbase->reject();
01093             }
01094         }
01095         else
01096         {
01097             kWarning() << "closeChildDialogs: not a KDialog! Don't use QDialogs in KDE! " << static_cast<QWidget*>(dlg);
01098             static_cast<QWidget*>(dlg)->hide();
01099         }
01100     }
01101     d->m_dialogsAllowed = false;
01102 }
01103 
01104 bool KHTMLView::dialogsAllowed() {
01105     bool allowed = d->m_dialogsAllowed;
01106     KHTMLPart* p = m_part->parentPart();
01107     if (p && p->view())
01108         allowed &= p->view()->dialogsAllowed();
01109     return allowed;
01110 }
01111 
01112 void KHTMLView::closeEvent( QCloseEvent* ev )
01113 {
01114     closeChildDialogs();
01115     QScrollArea::closeEvent( ev );
01116 }
01117 
01118 void KHTMLView::setZoomLevel(int percent)
01119 {
01120     percent = percent < 20 ? 20 : (percent > 800 ? 800 : percent);
01121     int oldpercent = d->zoomLevel;
01122     d->zoomLevel = percent;
01123     if (percent != oldpercent) {
01124         if (d->layoutSchedulingEnabled)
01125             layout();
01126         widget()->update();
01127     }
01128 }
01129 
01130 int KHTMLView::zoomLevel() const
01131 {
01132     return d->zoomLevel;
01133 }
01134 
01135 void KHTMLView::setSmoothScrollingMode( SmoothScrollingMode m )
01136 {
01137     d->smoothScrollMode = m;
01138     d->smoothScrollModeIsDefault = false;
01139     if (d->smoothScrolling && !m)
01140         d->stopScrolling();
01141 }
01142 
01143 void KHTMLView::setSmoothScrollingModeDefault( SmoothScrollingMode m )
01144 {
01145     // check for manual override
01146     if (!d->smoothScrollModeIsDefault)
01147         return;
01148     d->smoothScrollMode = m;
01149     if (d->smoothScrolling && !m)
01150         d->stopScrolling();
01151 }
01152 
01153 KHTMLView::SmoothScrollingMode KHTMLView::smoothScrollingMode( ) const
01154 {
01155     return d->smoothScrollMode;
01156 }
01157 
01158 //
01159 // Event Handling
01160 //
01162 
01163 void KHTMLView::mousePressEvent( QMouseEvent *_mouse )
01164 {
01165     if (!m_part->xmlDocImpl()) return;
01166     if (d->possibleTripleClick && ( _mouse->button() & Qt::MouseButtonMask ) == Qt::LeftButton)
01167     {
01168         mouseDoubleClickEvent( _mouse ); // it handles triple clicks too
01169         return;
01170     }
01171 
01172     int xm = _mouse->x();
01173     int ym = _mouse->y();
01174     revertTransforms(xm, ym);
01175 
01176     // kDebug( 6000 ) << "mousePressEvent: viewport=("<<_mouse->x()-contentsX()<<"/"<<_mouse->y()-contentsY()<<"), contents=(" << xm << "/" << ym << ")\n";
01177 
01178     d->isDoubleClick = false;
01179 
01180     DOM::NodeImpl::MouseEvent mev( _mouse->buttons(), DOM::NodeImpl::MousePress );
01181     m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev );
01182 
01183     //kDebug(6000) << "innerNode="<<mev.innerNode.nodeName().string();
01184 
01185     if ( (_mouse->button() == Qt::MidButton) &&
01186           !m_part->d->m_bOpenMiddleClick && !d->m_mouseScrollTimer &&
01187           mev.url.isNull() && (mev.innerNode.elementId() != ID_INPUT) ) {
01188         QPoint point = mapFromGlobal( _mouse->globalPos() );
01189 
01190         d->m_mouseScroll_byX = 0;
01191         d->m_mouseScroll_byY = 0;
01192 
01193         d->m_mouseScrollTimer = new QTimer( this );
01194         connect( d->m_mouseScrollTimer, SIGNAL(timeout()), this, SLOT(slotMouseScrollTimer()) );
01195 
01196         if ( !d->m_mouseScrollIndicator ) {
01197             QPixmap pixmap( 48, 48 ), icon;
01198             pixmap.fill( QColor( qRgba( 127, 127, 127, 127 ) ) );
01199 
01200             QPainter p( &pixmap );
01201             QStyleOption option;
01202 
01203             option.rect.setRect( 16, 0, 16, 16 );
01204             QApplication::style()->drawPrimitive( QStyle::PE_IndicatorArrowUp, &option, &p );
01205             option.rect.setRect( 0, 16, 16, 16 );
01206             QApplication::style()->drawPrimitive( QStyle::PE_IndicatorArrowLeft, &option, &p );
01207             option.rect.setRect( 16, 32, 16, 16 );
01208             QApplication::style()->drawPrimitive( QStyle::PE_IndicatorArrowDown, &option, &p );
01209             option.rect.setRect( 32, 16, 16, 16 );
01210             QApplication::style()->drawPrimitive( QStyle::PE_IndicatorArrowRight, &option, &p );
01211             p.drawEllipse( 23, 23, 2, 2 );
01212 
01213             d->m_mouseScrollIndicator = new QWidget( this );
01214             d->m_mouseScrollIndicator->setFixedSize( 48, 48 );
01215             QPalette palette;
01216             palette.setBrush( d->m_mouseScrollIndicator->backgroundRole(), QBrush( pixmap ) );
01217             d->m_mouseScrollIndicator->setPalette( palette );
01218         }
01219         d->m_mouseScrollIndicator->move( point.x()-24, point.y()-24 );
01220 
01221         bool hasHorBar = visibleWidth() < contentsWidth();
01222         bool hasVerBar = visibleHeight() < contentsHeight();
01223 
01224         KConfigGroup cg( KGlobal::config(), "HTML Settings" );
01225         if ( cg.readEntry( "ShowMouseScrollIndicator", true ) ) {
01226             d->m_mouseScrollIndicator->show();
01227             d->m_mouseScrollIndicator->unsetCursor();
01228 
01229             QBitmap mask = d->m_mouseScrollIndicator->palette().brush(d->m_mouseScrollIndicator->backgroundRole()).texture().createHeuristicMask( true );
01230 
01231         if ( hasHorBar && !hasVerBar ) {
01232                 QBitmap bm( 16, 16 );
01233                 bm.clear();
01234                 QPainter painter( &mask );
01235                 painter.drawPixmap( QRectF( 16, 0, bm.width(), bm.height() ), bm, bm.rect() );
01236                 painter.drawPixmap( QRectF( 16, 32, bm.width(), bm.height() ), bm, bm.rect() );
01237                 d->m_mouseScrollIndicator->setCursor( Qt::SizeHorCursor );
01238             }
01239             else if ( !hasHorBar && hasVerBar ) {
01240                 QBitmap bm( 16, 16 );
01241                 bm.clear();
01242                 QPainter painter( &mask );
01243                 painter.drawPixmap( QRectF( 0, 16, bm.width(), bm.height() ), bm, bm.rect() );
01244                 painter.drawPixmap( QRectF( 32, 16, bm.width(), bm.height() ), bm, bm.rect() );
01245                 d->m_mouseScrollIndicator->setCursor( Qt::SizeVerCursor );
01246             }
01247             else
01248                 d->m_mouseScrollIndicator->setCursor( Qt::SizeAllCursor );
01249 
01250             d->m_mouseScrollIndicator->setMask( mask );
01251         }
01252         else {
01253             if ( hasHorBar && !hasVerBar )
01254                 viewport()->setCursor( Qt::SizeHorCursor );
01255             else if ( !hasHorBar && hasVerBar )
01256                 viewport()->setCursor( Qt::SizeVerCursor );
01257             else
01258                 viewport()->setCursor( Qt::SizeAllCursor );
01259         }
01260 
01261         return;
01262     }
01263     else if ( d->m_mouseScrollTimer ) {
01264         delete d->m_mouseScrollTimer;
01265         d->m_mouseScrollTimer = 0;
01266 
01267         if ( d->m_mouseScrollIndicator )
01268             d->m_mouseScrollIndicator->hide();
01269     }
01270 
01271     if (d->clickCount > 0 &&
01272         QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() <= QApplication::startDragDistance())
01273         d->clickCount++;
01274     else {
01275         d->clickCount = 1;
01276         d->clickX = xm;
01277         d->clickY = ym;
01278     }
01279 
01280     bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEDOWN_EVENT,mev.innerNode.handle(),mev.innerNonSharedNode.handle(),true,
01281                                            d->clickCount,_mouse,true,DOM::NodeImpl::MousePress);
01282 
01283     if (!swallowEvent) {
01284     emit m_part->nodeActivated(mev.innerNode);
01285 
01286     khtml::MousePressEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode );
01287         QApplication::sendEvent( m_part, &event );
01288         // we might be deleted after this
01289     }
01290 }
01291 
01292 void KHTMLView::mouseDoubleClickEvent( QMouseEvent *_mouse )
01293 {
01294     if(!m_part->xmlDocImpl()) return;
01295 
01296     int xm = _mouse->x();
01297     int ym = _mouse->y();
01298     revertTransforms(xm, ym);
01299 
01300     // kDebug( 6000 ) << "mouseDblClickEvent: x=" << xm << ", y=" << ym;
01301 
01302     d->isDoubleClick = true;
01303 
01304     DOM::NodeImpl::MouseEvent mev( _mouse->buttons(), DOM::NodeImpl::MouseDblClick );
01305     m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev );
01306 
01307     // We do the same thing as mousePressEvent() here, since the DOM does not treat
01308     // single and double-click events as separate (only the detail, i.e. number of clicks differs)
01309     if (d->clickCount > 0 &&
01310         QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() <= QApplication::startDragDistance())
01311     d->clickCount++;
01312     else { // shouldn't happen, if Qt has the same criterias for double clicks.
01313     d->clickCount = 1;
01314     d->clickX = xm;
01315     d->clickY = ym;
01316     }
01317     bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEDOWN_EVENT,mev.innerNode.handle(),mev.innerNonSharedNode.handle(),true,
01318                                            d->clickCount,_mouse,true,DOM::NodeImpl::MouseDblClick);
01319 
01320     khtml::RenderObject* r = mev.innerNode.handle() ? mev.innerNode.handle()->renderer() : 0;
01321 
01322     if (!swallowEvent) {
01323     khtml::MouseDoubleClickEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode, d->clickCount );
01324     QApplication::sendEvent( m_part, &event );
01325     }
01326 
01327     d->possibleTripleClick=true;
01328     QTimer::singleShot(QApplication::doubleClickInterval(),this,SLOT(tripleClickTimeout()));
01329 }
01330 
01331 void KHTMLView::tripleClickTimeout()
01332 {
01333     d->possibleTripleClick = false;
01334     d->clickCount = 0;
01335 }
01336 
01337 static bool targetOpensNewWindow(KHTMLPart *part, QString target)
01338 {
01339     if (!target.isEmpty() && (target.toLower() != "_top") &&
01340        (target.toLower() != "_self") && (target.toLower() != "_parent")) {
01341         if (target.toLower() == "_blank")
01342             return true;
01343         else {
01344             while (part->parentPart())
01345                 part = part->parentPart();
01346             if (!part->frameExists(target))
01347                 return true;
01348         }
01349     }
01350     return false;
01351 }
01352 
01353 void KHTMLView::mouseMoveEvent( QMouseEvent * _mouse )
01354 {
01355     if ( d->m_mouseScrollTimer ) {
01356         QPoint point = mapFromGlobal( _mouse->globalPos() );
01357 
01358         int deltaX = point.x() - d->m_mouseScrollIndicator->x() - 24;
01359         int deltaY = point.y() - d->m_mouseScrollIndicator->y() - 24;
01360 
01361         (deltaX > 0) ? d->m_mouseScroll_byX = 1 : d->m_mouseScroll_byX = -1;
01362         (deltaY > 0) ? d->m_mouseScroll_byY = 1 : d->m_mouseScroll_byY = -1;
01363 
01364         double adX = qAbs(deltaX)/30.0;
01365         double adY = qAbs(deltaY)/30.0;
01366 
01367         d->m_mouseScroll_byX = qMax(qMin(d->m_mouseScroll_byX * int(adX*adX), SHRT_MAX), SHRT_MIN);
01368         d->m_mouseScroll_byY = qMax(qMin(d->m_mouseScroll_byY * int(adY*adY), SHRT_MAX), SHRT_MIN);
01369 
01370         if (d->m_mouseScroll_byX == 0 && d->m_mouseScroll_byY == 0) {
01371             d->m_mouseScrollTimer->stop();
01372         }
01373         else if (!d->m_mouseScrollTimer->isActive()) {
01374             d->m_mouseScrollTimer->start( 20 );
01375         }
01376     }
01377 
01378     if(!m_part->xmlDocImpl()) return;
01379 
01380     int xm = _mouse->x();
01381     int ym = _mouse->y();
01382     revertTransforms(xm, ym);
01383 
01384     DOM::NodeImpl::MouseEvent mev( _mouse->buttons(), DOM::NodeImpl::MouseMove );
01385     // Do not modify :hover/:active state while mouse is pressed.
01386     m_part->xmlDocImpl()->prepareMouseEvent( _mouse->buttons() /*readonly ?*/, xm, ym, &mev );
01387 
01388     // kDebug(6000) << "mouse move: " << _mouse->pos()
01389     //        << " button " << _mouse->button()
01390     //        << " state " << _mouse->state() << endl;
01391 
01392     DOM::NodeImpl* target = mev.innerNode.handle();
01393     DOM::NodeImpl* fn = m_part->xmlDocImpl()->focusNode();
01394 
01395     // a widget may be the real target of this event (e.g. if a scrollbar's slider is being moved)
01396     if (d->m_mouseEventsTarget && fn && fn->renderer() && fn->renderer()->isWidget())
01397        target = fn;
01398 
01399     bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEMOVE_EVENT,target,mev.innerNonSharedNode.handle(),false,
01400                                            0,_mouse,true,DOM::NodeImpl::MouseMove);
01401 
01402     if (d->clickCount > 0 &&
01403         QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() > QApplication::startDragDistance()) {
01404     d->clickCount = 0;  // moving the mouse outside the threshold invalidates the click
01405     }
01406 
01407     khtml::RenderObject* r = target ? target->renderer() : 0;
01408     bool setCursor = true;
01409     if (r && r->isWidget()) {
01410         RenderWidget* rw = static_cast<RenderWidget*>(r);
01411         KHTMLWidget* kw = qobject_cast<KHTMLView*>(rw->widget())? dynamic_cast<KHTMLWidget*>(rw->widget()) : 0;
01412         if (kw && kw->m_kwp->isRedirected())
01413             setCursor = false;
01414     }
01415     khtml::RenderStyle* style = (r && r->style()) ? r->style() : 0;
01416     QCursor c;
01417     LinkCursor linkCursor = LINK_NORMAL;
01418     switch ( style ? style->cursor() : CURSOR_AUTO) {
01419     case CURSOR_AUTO:
01420         if ( r && r->isText() && !r->isPointInsideSelection(xm, ym, m_part->caret()) )
01421             c = QCursor(Qt::IBeamCursor);
01422         if ( mev.url.length() && m_part->settings()->changeCursor() ) {
01423             c = m_part->urlCursor();
01424         if (mev.url.string().startsWith("mailto:") && mev.url.string().indexOf('@')>0)
01425               linkCursor = LINK_MAILTO;
01426             else
01427               if ( targetOpensNewWindow( m_part, mev.target.string() ) )
01428             linkCursor = LINK_NEWWINDOW;
01429         }
01430 
01431         if (r && r->isFrameSet() && !static_cast<RenderFrameSet*>(r)->noResize())
01432             c = QCursor(static_cast<RenderFrameSet*>(r)->cursorShape());
01433 
01434         break;
01435     case CURSOR_CROSS:
01436         c = QCursor(Qt::CrossCursor);
01437         break;
01438     case CURSOR_POINTER:
01439         c = m_part->urlCursor();
01440     if (mev.url.string().startsWith("mailto:") && mev.url.string().indexOf('@')>0)
01441           linkCursor = LINK_MAILTO;
01442         else
01443           if ( targetOpensNewWindow( m_part, mev.target.string() ) )
01444         linkCursor = LINK_NEWWINDOW;
01445         break;
01446     case CURSOR_PROGRESS:
01447         c = QCursor(Qt::BusyCursor); // working_cursor
01448         break;
01449     case CURSOR_MOVE:
01450     case CURSOR_ALL_SCROLL:
01451         c = QCursor(Qt::SizeAllCursor);
01452         break;
01453     case CURSOR_E_RESIZE:
01454     case CURSOR_W_RESIZE:
01455     case CURSOR_EW_RESIZE:
01456         c = QCursor(Qt::SizeHorCursor);
01457         break;
01458     case CURSOR_N_RESIZE:
01459     case CURSOR_S_RESIZE:
01460     case CURSOR_NS_RESIZE:
01461         c = QCursor(Qt::SizeVerCursor);
01462         break;
01463     case CURSOR_NE_RESIZE:
01464     case CURSOR_SW_RESIZE:
01465     case CURSOR_NESW_RESIZE:
01466         c = QCursor(Qt::SizeBDiagCursor);
01467         break;
01468     case CURSOR_NW_RESIZE:
01469     case CURSOR_SE_RESIZE:
01470     case CURSOR_NWSE_RESIZE:
01471         c = QCursor(Qt::SizeFDiagCursor);
01472         break;
01473     case CURSOR_TEXT:
01474         c = QCursor(Qt::IBeamCursor);
01475         break;
01476     case CURSOR_WAIT:
01477         c = QCursor(Qt::WaitCursor);
01478         break;
01479     case CURSOR_HELP:
01480         c = QCursor(Qt::WhatsThisCursor);
01481         break;
01482     case CURSOR_DEFAULT:
01483         break;
01484     case CURSOR_NONE:
01485     case CURSOR_NOT_ALLOWED:
01486         c = QCursor(Qt::ForbiddenCursor);
01487         break;
01488     case CURSOR_ROW_RESIZE:
01489         c = QCursor(Qt::SplitVCursor);
01490         break;
01491     case CURSOR_COL_RESIZE:
01492         c = QCursor(Qt::SplitHCursor);
01493         break;
01494     case CURSOR_VERTICAL_TEXT:
01495     case CURSOR_CONTEXT_MENU:
01496     case CURSOR_NO_DROP:
01497     case CURSOR_CELL:
01498     case CURSOR_COPY:
01499     case CURSOR_ALIAS:
01500         c = QCursor(Qt::ArrowCursor);
01501         break;
01502     }
01503 
01504     if (!setCursor && style && style->cursor() != CURSOR_AUTO)
01505         setCursor = true;
01506 
01507     QWidget* vp = viewport();
01508     for (KHTMLPart* p = m_part; p; p = p->parentPart())
01509         if (!p->parentPart())
01510             vp = p->view()->viewport();
01511     if ( setCursor && vp->cursor().handle() != c.handle() ) {
01512         if( c.shape() == Qt::ArrowCursor) {
01513             for (KHTMLPart* p = m_part; p; p = p->parentPart())
01514                 p->view()->viewport()->unsetCursor();
01515         }
01516         else {
01517             vp->setCursor( c );
01518         }
01519     }
01520 
01521     if ( linkCursor!=LINK_NORMAL && isVisible() && hasFocus() ) {
01522 #ifdef Q_WS_X11
01523 
01524         if( !d->cursorIconWidget ) {
01525 #ifdef Q_WS_X11
01526             d->cursorIconWidget = new QLabel( 0, Qt::X11BypassWindowManagerHint );
01527             XSetWindowAttributes attr;
01528             attr.save_under = True;
01529             XChangeWindowAttributes( QX11Info::display(), d->cursorIconWidget->winId(), CWSaveUnder, &attr );
01530 #else
01531             d->cursorIconWidget = new QLabel( NULL, NULL );
01532             //TODO
01533 #endif
01534         }
01535 
01536         // Update the pixmap if need be.
01537         if (linkCursor != d->cursorIconType) {
01538             d->cursorIconType = linkCursor;
01539             QString cursorIcon;
01540             switch (linkCursor)
01541             {
01542               case LINK_MAILTO:     cursorIcon = "mail-message-new"; break;
01543               case LINK_NEWWINDOW:  cursorIcon = "window-new";       break;
01544               default:              cursorIcon = "dialog-error";     break;
01545             }
01546 
01547             QPixmap icon_pixmap = KHTMLGlobal::iconLoader()->loadIcon( cursorIcon, KIconLoader::Small, 0, KIconLoader::DefaultState, QStringList(), 0, true );
01548 
01549             d->cursorIconWidget->resize( icon_pixmap.width(), icon_pixmap.height());
01550             d->cursorIconWidget->setMask( icon_pixmap.createMaskFromColor(Qt::transparent));
01551             d->cursorIconWidget->setPixmap( icon_pixmap);
01552             d->cursorIconWidget->update();
01553         }
01554 
01555         QPoint c_pos = QCursor::pos();
01556         d->cursorIconWidget->move( c_pos.x() + 15, c_pos.y() + 15 );
01557 #ifdef Q_WS_X11
01558         XRaiseWindow( QX11Info::display(), d->cursorIconWidget->winId());
01559         QApplication::flush();
01560 #elif defined(Q_WS_WIN)
01561         SetWindowPos( d->cursorIconWidget->winId(), HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE );
01562 #else
01563         //TODO?
01564 #endif
01565         d->cursorIconWidget->show();
01566 #endif
01567     }
01568     else if ( d->cursorIconWidget )
01569         d->cursorIconWidget->hide();
01570 
01571     if (r && r->isWidget()) {
01572     _mouse->ignore();
01573     }
01574 
01575     if (!swallowEvent) {
01576         khtml::MouseMoveEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode );
01577         QApplication::sendEvent( m_part, &event );
01578     }
01579 }
01580 
01581 void KHTMLView::mouseReleaseEvent( QMouseEvent * _mouse )
01582 {
01583     bool swallowEvent = false;
01584 
01585     int xm = _mouse->x();
01586     int ym = _mouse->y();
01587     revertTransforms(xm, ym);
01588 
01589     DOM::NodeImpl::MouseEvent mev( _mouse->buttons(), DOM::NodeImpl::MouseRelease );
01590 
01591     if ( m_part->xmlDocImpl() )
01592     {
01593         m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev );
01594 
01595         DOM::NodeImpl* target = mev.innerNode.handle();
01596         DOM::NodeImpl* fn = m_part->xmlDocImpl()->focusNode();
01597 
01598         // a widget may be the real target of this event (e.g. if a scrollbar's slider is being moved)
01599         if (d->m_mouseEventsTarget && fn && fn->renderer() && fn->renderer()->isWidget())
01600             target = fn;
01601 
01602         swallowEvent = dispatchMouseEvent(EventImpl::MOUSEUP_EVENT,target,mev.innerNonSharedNode.handle(),true,
01603                                           d->clickCount,_mouse,false,DOM::NodeImpl::MouseRelease);
01604 
01605         // clear our sticky event target on any mouseRelease event
01606         if (d->m_mouseEventsTarget)
01607             d->m_mouseEventsTarget = 0;
01608 
01609         if (d->clickCount > 0 &&
01610             QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() <= QApplication::startDragDistance()) {
01611             QMouseEvent me(d->isDoubleClick ? QEvent::MouseButtonDblClick : QEvent::MouseButtonRelease,
01612                            _mouse->pos(), _mouse->button(), _mouse->buttons(), _mouse->modifiers());
01613             dispatchMouseEvent(EventImpl::CLICK_EVENT, mev.innerNode.handle(),mev.innerNonSharedNode.handle(),true,
01614                                d->clickCount, &me, true, DOM::NodeImpl::MouseRelease);
01615         }
01616 
01617         khtml::RenderObject* r = target ? target->renderer() : 0;
01618         if (r && r->isWidget())
01619             _mouse->ignore();
01620     }
01621 
01622     if (!swallowEvent) {
01623     khtml::MouseReleaseEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode );
01624     QApplication::sendEvent( m_part, &event );
01625     }
01626 }
01627 
01628 // returns true if event should be swallowed
01629 bool KHTMLView::dispatchKeyEvent( QKeyEvent *_ke )
01630 {
01631     if (!m_part->xmlDocImpl())
01632         return false;
01633     // Pressing and releasing a key should generate keydown, keypress and keyup events
01634     // Holding it down should generated keydown, keypress (repeatedly) and keyup events
01635     // The problem here is that Qt generates two autorepeat events (keyrelease+keypress)
01636     // for autorepeating, while DOM wants only one autorepeat event (keypress), so one
01637     // of the Qt events shouldn't be passed to DOM, but it should be still filtered
01638     // out if DOM would filter the autorepeat event. Additional problem is that Qt keyrelease
01639     // events don't have text() set (Qt bug?), so DOM often would ignore the keypress event
01640     // if it was created using Qt keyrelease, but Qt autorepeat keyrelease comes
01641     // before Qt autorepeat keypress (i.e. problem whether to filter it out or not).
01642     // The solution is to filter out and postpone the Qt autorepeat keyrelease until
01643     // the following Qt keypress event comes. If DOM accepts the DOM keypress event,
01644     // the postponed event will be simply discarded. If not, it will be passed to keyPressEvent()
01645     // again, and here it will be ignored.
01646     //
01647     //  Qt:      Press      | Release(autorepeat) Press(autorepeat) etc. |   Release
01648     //  DOM:   Down + Press |      (nothing)           Press             |     Up
01649 
01650     // It's also possible to get only Releases. E.g. the release of alt-tab,
01651     // or when the keypresses get captured by an accel.
01652 
01653     if( _ke == d->postponed_autorepeat ) // replayed event
01654     {
01655         return false;
01656     }
01657 
01658     if( _ke->type() == QEvent::KeyPress )
01659     {
01660         if( !_ke->isAutoRepeat())
01661         {
01662             bool ret = dispatchKeyEventHelper( _ke, false ); // keydown
01663             // don't send keypress even if keydown was blocked, like IE (and unlike Mozilla)
01664             if( !ret && dispatchKeyEventHelper( _ke, true )) // keypress
01665                 ret = true;
01666             return ret;
01667         }
01668         else // autorepeat
01669         {
01670             bool ret = dispatchKeyEventHelper( _ke, true ); // keypress
01671             if( !ret && d->postponed_autorepeat )
01672                 keyPressEvent( d->postponed_autorepeat );
01673             delete d->postponed_autorepeat;
01674             d->postponed_autorepeat = NULL;
01675             return ret;
01676         }
01677     }
01678     else // QEvent::KeyRelease
01679     {
01680         // Discard postponed "autorepeat key-release" events that didn't see
01681         // a keypress after them (e.g. due to QAccel)
01682         if ( d->postponed_autorepeat ) {
01683             delete d->postponed_autorepeat;
01684             d->postponed_autorepeat = 0;
01685         }
01686 
01687         if( !_ke->isAutoRepeat()) {
01688             return dispatchKeyEventHelper( _ke, false ); // keyup
01689         }
01690         else
01691         {
01692             d->postponed_autorepeat = new QKeyEvent( _ke->type(), _ke->key(), _ke->modifiers(),
01693                 _ke->text(), _ke->isAutoRepeat(), _ke->count());
01694             if( _ke->isAccepted())
01695                 d->postponed_autorepeat->accept();
01696             else
01697                 d->postponed_autorepeat->ignore();
01698             return true;
01699         }
01700     }
01701 }
01702 
01703 // returns true if event should be swallowed
01704 bool KHTMLView::dispatchKeyEventHelper( QKeyEvent *_ke, bool keypress )
01705 {
01706     DOM::NodeImpl* keyNode = m_part->xmlDocImpl()->focusNode();
01707     if (keyNode) {
01708         return keyNode->dispatchKeyEvent(_ke, keypress);
01709     } else { // no focused node, send to document
01710         return m_part->xmlDocImpl()->dispatchKeyEvent(_ke, keypress);
01711     }
01712 }
01713 
01714 void KHTMLView::keyPressEvent( QKeyEvent *_ke )
01715 {
01716 #ifndef KHTML_NO_TYPE_AHEAD_FIND
01717     if(d->typeAheadActivated)
01718     {
01719         // type-ahead find aka find-as-you-type
01720         if(_ke->key() == Qt::Key_Backspace)
01721         {
01722             d->findString = d->findString.left(d->findString.length() - 1);
01723 
01724             if(!d->findString.isEmpty())
01725             {
01726                 findAhead(false);
01727             }
01728             else
01729             {
01730                 findTimeout();
01731             }
01732 
01733             d->timer.setSingleShot(true);
01734             d->timer.start(3000);
01735             _ke->accept();
01736             return;
01737         }
01738         else if(_ke->key() == Qt::Key_Escape)
01739         {
01740             findTimeout();
01741 
01742             _ke->accept();
01743             return;
01744         }
01745         else if(_ke->key() == Qt::Key_Space || !_ke->text().trimmed().isEmpty())
01746         {
01747             d->findString += _ke->text();
01748 
01749             findAhead(true);
01750 
01751             d->timer.setSingleShot(true);
01752             d->timer.start(3000);
01753             _ke->accept();
01754             return;
01755         }
01756     }
01757 #endif // KHTML_NO_TYPE_AHEAD_FIND
01758 
01759 
01760     bool is_editable = m_part->isEditable() || (m_part->xmlDocImpl() && m_part->xmlDocImpl()->focusNode()
01761         && m_part->xmlDocImpl()->focusNode()->isContentEditable());
01762     if (is_editable || m_part->isCaretMode()) {
01763         m_part->d->editor_context.m_keyReleasePending = true;
01764         if (caretKeyPressEvent(_ke)) return;
01765         if (is_editable && m_part->editor()->handleKeyEvent(_ke))
01766             return;
01767     }
01768 
01769     // If CTRL was hit, be prepared for access keys
01770     if (d->accessKeysEnabled && _ke->key() == Qt::Key_Control && !(_ke->modifiers() & ~Qt::ControlModifier) && !d->accessKeysActivated)
01771     {
01772         d->accessKeysPreActivate=true;
01773         _ke->accept();
01774         return;
01775     }
01776 
01777     if (_ke->key() == Qt::Key_Shift && !(_ke->modifiers() & ~Qt::ShiftModifier))
01778         d->scrollSuspendPreActivate=true;
01779 
01780     // accesskey handling needs to be done before dispatching, otherwise e.g. lineedits
01781     // may eat the event
01782 
01783     if (d->accessKeysEnabled && d->accessKeysActivated)
01784     {
01785         int state = ( _ke->modifiers() & ( Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier ));
01786         if ( state==0 || state==Qt::ShiftModifier) {
01787     if (_ke->key() != Qt::Key_Shift) accessKeysTimeout();
01788         handleAccessKey( _ke );
01789         _ke->accept();
01790         return;
01791         }
01792     accessKeysTimeout();
01793     }
01794 
01795     if ( dispatchKeyEvent( _ke )) {
01796         // If either keydown or keypress was accepted by a widget, or canceled by JS, stop here.
01797         _ke->accept();
01798         return;
01799     }
01800 
01801     int offs = (viewport()->height() < 30) ? viewport()->height() : 30; // ### ??
01802     if (_ke->modifiers() & Qt::ShiftModifier)
01803       switch(_ke->key())
01804         {
01805         case Qt::Key_Space:
01806             verticalScrollBar()->setValue( verticalScrollBar()->value() -viewport()->height() + offs );
01807             if(d->scrollSuspended)
01808                 d->newScrollTimer(this, 0);
01809             break;
01810 
01811         case Qt::Key_Down:
01812         case Qt::Key_J:
01813             d->adjustScroller(this, KHTMLViewPrivate::ScrollDown, KHTMLViewPrivate::ScrollUp);
01814             break;
01815 
01816         case Qt::Key_Up:
01817         case Qt::Key_K:
01818             d->adjustScroller(this, KHTMLViewPrivate::ScrollUp, KHTMLViewPrivate::ScrollDown);
01819             break;
01820 
01821         case Qt::Key_Left:
01822         case Qt::Key_H:
01823             d->adjustScroller(this, KHTMLViewPrivate::ScrollLeft, KHTMLViewPrivate::ScrollRight);
01824             break;
01825 
01826         case Qt::Key_Right:
01827         case Qt::Key_L:
01828             d->adjustScroller(this, KHTMLViewPrivate::ScrollRight, KHTMLViewPrivate::ScrollLeft);
01829             break;
01830         }
01831     else
01832         switch ( _ke->key() )
01833         {
01834         case Qt::Key_Down:
01835         case Qt::Key_J:
01836             if (!d->scrollTimerId || d->scrollSuspended)
01837                 verticalScrollBar()->setValue( verticalScrollBar()->value()+10 );
01838             if (d->scrollTimerId)
01839                 d->newScrollTimer(this, 0);
01840             break;
01841 
01842         case Qt::Key_Space:
01843         case Qt::Key_PageDown:
01844         d->shouldSmoothScroll = true;
01845             verticalScrollBar()->setValue( verticalScrollBar()->value() +viewport()->height() - offs );
01846             if(d->scrollSuspended)
01847                 d->newScrollTimer(this, 0);
01848             break;
01849 
01850         case Qt::Key_Up:
01851         case Qt::Key_K:
01852             if (!d->scrollTimerId || d->scrollSuspended)
01853                 verticalScrollBar()->setValue( verticalScrollBar()->value()-10 );
01854             if (d->scrollTimerId)
01855                 d->newScrollTimer(this, 0);
01856             break;
01857 
01858         case Qt::Key_PageUp:
01859         d->shouldSmoothScroll = true;
01860             verticalScrollBar()->setValue( verticalScrollBar()->value() -viewport()->height() + offs );
01861             if(d->scrollSuspended)
01862                 d->newScrollTimer(this, 0);
01863             break;
01864         case Qt::Key_Right:
01865         case Qt::Key_L:
01866             if (!d->scrollTimerId || d->scrollSuspended)
01867                  horizontalScrollBar()->setValue( horizontalScrollBar()->value()+10 );
01868             if (d->scrollTimerId)
01869                 d->newScrollTimer(this, 0);
01870             break;
01871 
01872         case Qt::Key_Left:
01873         case Qt::Key_H:
01874             if (!d->scrollTimerId || d->scrollSuspended)
01875                 horizontalScrollBar()->setValue( horizontalScrollBar()->value()-10 );
01876             if (d->scrollTimerId)
01877                 d->newScrollTimer(this, 0);
01878             break;
01879         case Qt::Key_Enter:
01880         case Qt::Key_Return:
01881         // ### FIXME:
01882         // or even better to HTMLAnchorElementImpl::event()
01883             if (m_part->xmlDocImpl()) {
01884         NodeImpl *n = m_part->xmlDocImpl()->focusNode();
01885         if (n)
01886             n->setActive();
01887         }
01888             break;
01889         case Qt::Key_Home:
01890             verticalScrollBar()->setValue( 0 );
01891             horizontalScrollBar()->setValue( 0 );
01892             if(d->scrollSuspended)
01893                 d->newScrollTimer(this, 0);
01894             break;
01895         case Qt::Key_End:
01896             verticalScrollBar()->setValue( contentsHeight() - visibleHeight() );
01897             if(d->scrollSuspended)
01898                 d->newScrollTimer(this, 0);
01899             break;
01900         case Qt::Key_Shift:
01901             // what are you doing here?
01902         _ke->ignore();
01903             return;
01904         default:
01905             if (d->scrollTimerId)
01906                 d->newScrollTimer(this, 0);
01907         _ke->ignore();
01908             return;
01909         }
01910 
01911     _ke->accept();
01912 }
01913 
01914 void KHTMLView::findTimeout()
01915 {
01916 #ifndef KHTML_NO_TYPE_AHEAD_FIND
01917     d->typeAheadActivated = false;
01918     d->findString = "";
01919     m_part->setStatusBarText(i18n("Find stopped."), KHTMLPart::BarDefaultText);
01920     m_part->enableFindAheadActions( true );
01921 #endif // KHTML_NO_TYPE_AHEAD_FIND
01922 }
01923 
01924 #ifndef KHTML_NO_TYPE_AHEAD_FIND
01925 void KHTMLView::startFindAhead( bool linksOnly )
01926 {
01927     if( linksOnly )
01928     {
01929         d->findLinksOnly = true;
01930         m_part->setStatusBarText(i18n("Starting -- find links as you type"),
01931                                  KHTMLPart::BarDefaultText);
01932     }
01933     else
01934     {
01935         d->findLinksOnly = false;
01936         m_part->setStatusBarText(i18n("Starting -- find text as you type"),
01937                                  KHTMLPart::BarDefaultText);
01938     }
01939 
01940     m_part->findTextBegin();
01941     d->typeAheadActivated = true;
01942         // disable, so that the shortcut ( / or ' by default ) doesn't interfere
01943     m_part->enableFindAheadActions( false );
01944     d->timer.setSingleShot(true);
01945     d->timer.start(3000);
01946 }
01947 
01948 void KHTMLView::findAhead(bool increase)
01949 {
01950     QString status;
01951     QString text = d->findString.toLower();
01952 
01953     if(d->findLinksOnly)
01954     {
01955         m_part->findText(d->findString, KHTMLPart::FindNoPopups |
01956                          KHTMLPart::FindLinksOnly, this);
01957         if(m_part->findTextNext())
01958         {
01959             status = i18n("Link found: \"%1\".", Qt::escape(text));
01960         }
01961         else
01962         {
01963             if(increase) KNotification::beep();
01964             status = i18n("Link not found: \"%1\".", Qt::escape(text));
01965         }
01966     }
01967     else
01968     {
01969         m_part->findText(d->findString, KHTMLPart::FindNoPopups, this);
01970         if(m_part->findTextNext())
01971         {
01972             status = i18n("Text found: \"%1\".", Qt::escape(text));
01973         }
01974         else
01975         {
01976             if(increase) KNotification::beep();
01977             status = i18n("Text not found: \"%1\".", Qt::escape(text));
01978         }
01979     }
01980 
01981     // Note: we need to escape -twice-: the above just escape for i18n, now we need to do it for Qt, too.
01982     m_part->setStatusBarText(Qt::escape(status), KHTMLPart::BarDefaultText);
01983 }
01984 
01985 void KHTMLView::updateFindAheadTimeout()
01986 {
01987     if( d->typeAheadActivated ) {
01988         d->timer.setSingleShot( true );
01989         d->timer.start( 3000 );
01990     }
01991 }
01992 
01993 #endif // KHTML_NO_TYPE_AHEAD_FIND
01994 
01995 void KHTMLView::keyReleaseEvent(QKeyEvent *_ke)
01996 {
01997 #ifndef KHTML_NO_TYPE_AHEAD_FIND
01998     if(d->typeAheadActivated) {
01999         _ke->accept();
02000         return;
02001     }
02002 #endif
02003     if (m_part->d->editor_context.m_keyReleasePending) {
02004         //caretKeyReleaseEvent(_ke);
02005     m_part->d->editor_context.m_keyReleasePending = false;
02006     return;
02007     }
02008 
02009     if( d->scrollSuspendPreActivate && _ke->key() != Qt::Key_Shift )
02010         d->scrollSuspendPreActivate = false;
02011     if( _ke->key() == Qt::Key_Shift && d->scrollSuspendPreActivate && !(_ke->modifiers() & Qt::ShiftModifier))
02012         if (d->scrollTimerId) {
02013                 d->scrollSuspended = !d->scrollSuspended;
02014                 if (d->scrollSuspended)
02015                     d->stopScrolling();
02016         }
02017 
02018     if (d->accessKeysEnabled)
02019     {
02020         if (d->accessKeysPreActivate && _ke->key() != Qt::Key_Control)
02021             d->accessKeysPreActivate=false;
02022         if (d->accessKeysPreActivate && !(_ke->modifiers() & Qt::ControlModifier))
02023         {
02024         displayAccessKeys();
02025         m_part->setStatusBarText(i18n("Access Keys activated"),KHTMLPart::BarOverrideText);
02026         d->accessKeysActivated = true;
02027         d->accessKeysPreActivate = false;
02028             _ke->accept();
02029             return;
02030         }
02031     else if (d->accessKeysActivated)
02032         {
02033             accessKeysTimeout();
02034             _ke->accept();
02035             return;
02036         }
02037     }
02038 
02039     // Send keyup event
02040     if ( dispatchKeyEvent( _ke ) )
02041     {
02042         _ke->accept();
02043         return;
02044     }
02045 
02046     QScrollArea::keyReleaseEvent(_ke);
02047 }
02048 
02049 bool KHTMLView::focusNextPrevChild( bool next )
02050 {
02051     // Now try to find the next child
02052     if (m_part->xmlDocImpl() && focusNextPrevNode(next))
02053     {
02054     if (m_part->xmlDocImpl()->focusNode())
02055         kDebug() << "focusNode.name: "
02056               << m_part->xmlDocImpl()->focusNode()->nodeName().string() << endl;
02057     return true; // focus node found
02058     }
02059 
02060     // If we get here, pass tabbing control up to the next/previous child in our parent
02061     d->pseudoFocusNode = KHTMLViewPrivate::PFNone;
02062     if (m_part->parentPart() && m_part->parentPart()->view())
02063         return m_part->parentPart()->view()->focusNextPrevChild(next);
02064 
02065     return QWidget::focusNextPrevChild(next);
02066 }
02067 
02068 void KHTMLView::doAutoScroll()
02069 {
02070     QPoint pos = QCursor::pos();
02071     QPoint off;
02072     KHTMLView* v = m_kwp->isRedirected() ? m_kwp->rootViewPos(off) : this;
02073     pos = v->viewport()->mapFromGlobal( pos );
02074     pos -= off;
02075     int xm, ym;
02076     viewportToContents(pos.x(), pos.y(), xm, ym); // ###
02077 
02078     pos = QPoint(pos.x() - viewport()->x(), pos.y() - viewport()->y());
02079     if ( (pos.y() < 0) || (pos.y() > visibleHeight()) ||
02080          (pos.x() < 0) || (pos.x() > visibleWidth()) )
02081     {
02082         ensureVisible( xm, ym, 0, 5 );
02083 
02084 #ifndef KHTML_NO_SELECTION
02085         // extend the selection while scrolling
02086     DOM::Node innerNode;
02087     if (m_part->isExtendingSelection()) {
02088             RenderObject::NodeInfo renderInfo(true/*readonly*/, false/*active*/);
02089             m_part->xmlDocImpl()->renderer()->layer()
02090                 ->nodeAtPoint(renderInfo, xm, ym);
02091             innerNode = renderInfo.innerNode();
02092     }/*end if*/
02093 
02094         if (innerNode.handle() && innerNode.handle()->renderer()
02095              && innerNode.handle()->renderer()->shouldSelect()) {
02096             m_part->extendSelectionTo(xm, ym, innerNode);
02097         }/*end if*/
02098 #endif // KHTML_NO_SELECTION
02099     }
02100 }
02101 
02102 // KHTML defines its own stacking order for any object and thus takes
02103 // control of widget painting whenever it can. This is called "redirection".
02104 //
02105 // Redirected widgets are placed off screen. When they are declared as a child of our view (ChildPolished event),
02106 // an event filter is installed, so as to catch any paint event and translate them as update() of the view's main widget.
02107 //
02108 // Painting also happens spontaneously within widgets. In this case, the widget would update() parts of itself.
02109 // While this ordinarily results in a paintEvent being schedduled, it is not the case with off screen widgets.
02110 // Thus update() is monitored by using the mechanism that deffers any update call happening during a paint event,
02111 // transforming it into a posted UpdateLater event. Hence the need to set Qt::WA_WState_InPaintEvent on redirected widgets.
02112 //
02113 // Once the UpdateLater event has been received, Qt::WA_WState_InPaintEvent is removed and the process continues
02114 // with the update of the corresponding rect on the view. That in turn will make our painting subsystem render()
02115 // the widget at the correct stacking position.
02116 //
02117 // For non-redirected (e.g. external) widgets, z-order is honoured through masking. cf.RenderLayer::updateWidgetMasks
02118 
02119 static void handleWidget(QWidget* w, KHTMLView* view, bool recurse=true)
02120 {
02121     if (w->isWindow())
02122         return;
02123 
02124     if (!qobject_cast<QFrame*>(w))
02125     w->setAttribute( Qt::WA_NoSystemBackground );
02126 
02127     w->setAttribute(Qt::WA_WState_InPaintEvent);
02128     w->setAttribute(Qt::WA_OpaquePaintEvent);
02129     w->installEventFilter(view);
02130 
02131     if (!recurse)
02132         return;
02133     if (qobject_cast<KHTMLView*>(w)) {
02134         handleWidget(static_cast<KHTMLView*>(w)->widget(), view, false);
02135         handleWidget(static_cast<KHTMLView*>(w)->horizontalScrollBar(), view, false);
02136         handleWidget(static_cast<KHTMLView*>(w)->verticalScrollBar(), view, false);
02137         return;
02138     }
02139 
02140     QObjectList children = w->children();
02141     foreach (QObject* object, children) {
02142     QWidget *widget = qobject_cast<QWidget*>(object);
02143     if (widget)
02144         handleWidget(widget, view);
02145     }
02146 }
02147 
02148 class KHTMLBackingStoreHackWidget : public QWidget
02149 {
02150 public:
02151     void publicEvent(QEvent *e)
02152     {
02153         QWidget::event(e);
02154     }
02155 };
02156 
02157 bool  KHTMLView::viewportEvent ( QEvent * e )
02158 {
02159     switch (e->type()) {
02160       // those must not be dispatched to the specialized handlers
02161       // as widgetEvent() already took care of that
02162       case QEvent::MouseButtonPress:
02163       case QEvent::MouseButtonRelease:
02164       case QEvent::MouseButtonDblClick:
02165       case QEvent::MouseMove:
02166 #ifndef QT_NO_WHEELEVENT
02167       case QEvent::Wheel:
02168 #endif
02169       case QEvent::ContextMenu:
02170       case QEvent::DragEnter:
02171       case QEvent::DragMove:
02172       case QEvent::DragLeave:
02173       case QEvent::Drop:
02174         return false;
02175       case QEvent::Paint: {
02176           QRect r = static_cast<QPaintEvent*>(e)->rect();
02177           r.setX(r.x() +contentsX());
02178           r.setY(r.y() +contentsY());
02179           QPaintEvent pe(r);
02180           paintEvent(&pe);
02181           return true;
02182       }
02183       default:
02184         break;
02185     }
02186     return QScrollArea::viewportEvent(e);
02187 }
02188 
02189 static void setInPaintEventFlag(QWidget* w, bool b = true, bool recurse=true)
02190 {
02191       w->setAttribute(Qt::WA_WState_InPaintEvent, b);
02192 
02193       if (!recurse)
02194           return;
02195       if (qobject_cast<KHTMLView*>(w)) {
02196           setInPaintEventFlag(static_cast<KHTMLView*>(w)->widget(), b, false);
02197           setInPaintEventFlag(static_cast<KHTMLView*>(w)->horizontalScrollBar(), b, false);
02198           setInPaintEventFlag(static_cast<KHTMLView*>(w)->verticalScrollBar(), b, false);
02199           return;
02200       }
02201 
02202       foreach(QObject* cw, w->children()) {
02203           if (cw->isWidgetType() && ! static_cast<QWidget*>(cw)->isWindow()
02204                                  && !(static_cast<QWidget*>(cw)->windowModality() & Qt::ApplicationModal)) {
02205               setInPaintEventFlag(static_cast<QWidget*>(cw), b);
02206           }
02207       }
02208 }
02209 
02210 bool KHTMLView::eventFilter(QObject *o, QEvent *e)
02211 {
02212     if ( e->type() == QEvent::ShortcutOverride ) {
02213     QKeyEvent* ke = (QKeyEvent*) e;
02214     if (m_part->isEditable() || m_part->isCaretMode()
02215         || (m_part->xmlDocImpl() && m_part->xmlDocImpl()->focusNode()
02216         && m_part->xmlDocImpl()->focusNode()->isContentEditable())) {
02217         if ( (ke->modifiers() & Qt::ControlModifier) || (ke->modifiers() & Qt::ShiftModifier) ) {
02218         switch ( ke->key() ) {
02219         case Qt::Key_Left:
02220         case Qt::Key_Right:
02221         case Qt::Key_Up:
02222         case Qt::Key_Down:
02223         case Qt::Key_Home:
02224         case Qt::Key_End:
02225             ke->accept();
02226             return true;
02227         default:
02228             break;
02229         }
02230         }
02231     }
02232     }
02233 
02234     if ( e->type() == QEvent::Leave ) {
02235       if ( d->cursorIconWidget )
02236         d->cursorIconWidget->hide();
02237       m_part->resetHoverText();
02238     }
02239 
02240     QWidget *view = widget();
02241     if (o == view) {
02242         if (widgetEvent(e))
02243             return true;
02244         else if (e->type() == QEvent::Resize) {
02245             updateScrollBars();
02246             return false;
02247         }
02248     } else if (o->isWidgetType()) {
02249     QWidget *v = static_cast<QWidget *>(o);
02250         QWidget *c = v;
02251     while (v && v != view) {
02252             c = v;
02253         v = v->parentWidget();
02254     }
02255     KHTMLWidget* k = dynamic_cast<KHTMLWidget*>(c);
02256     if (v && k && k->m_kwp->isRedirected()) {
02257         bool block = false;
02258         bool isUpdate = false;
02259         QWidget *w = static_cast<QWidget *>(o);
02260         switch(e->type()) {
02261         case QEvent::UpdateRequest: {
02262                 // implicitly call qt_syncBackingStore(w)
02263                 static_cast<KHTMLBackingStoreHackWidget *>(w)->publicEvent(e);
02264                 block = true;
02265                 break;
02266             }
02267             case QEvent::UpdateLater:
02268                 isUpdate = true;
02269                 // no break;
02270         case QEvent::Paint:
02271         if (!allowWidgetPaintEvents) {
02272             // eat the event. Like this we can control exactly when the widget
02273             // gets repainted.
02274             block = true;
02275             int x = 0, y = 0;
02276                     QWidget *v = w;
02277                     while (v && v->parentWidget() != view) {
02278                         x += v->x();
02279                         y += v->y();
02280                         v = v->parentWidget();
02281                     }
02282 
02283                     QPoint ap = k->m_kwp->absolutePos();
02284             x += ap.x();
02285             y += ap.y();
02286 
02287             QRect pr = isUpdate ? static_cast<QUpdateLaterEvent*>(e)->region().boundingRect() : static_cast<QPaintEvent*>(e)->rect();
02288                     bool asap = !d->contentsMoving && qobject_cast<QAbstractScrollArea*>(c);
02289 
02290                     if (isUpdate) {
02291                         setInPaintEventFlag(w, false);
02292                         if (asap)
02293                             w->repaint(static_cast<QUpdateLaterEvent*>(e)->region());
02294                         else
02295                             w->update(static_cast<QUpdateLaterEvent*>(e)->region());
02296                         setInPaintEventFlag(w);
02297                     }
02298 
02299             // QScrollView needs fast repaints
02300             if ( asap && !isUpdate && !d->painting && m_part->xmlDocImpl() && m_part->xmlDocImpl()->renderer() &&
02301                  !static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer())->needsLayout() ) {
02302                 repaintContents(x + pr.x(), y + pr.y(),
02303                                             pr.width(), pr.height()+1); // ### investigate that +1 (shows up when
02304                                                                         // updating e.g a textarea's blinking cursor)
02305                     } else if (!d->painting) {
02306                 scheduleRepaint(x + pr.x(), y + pr.y(),
02307                     pr.width(), pr.height()+1, asap);
02308                     }
02309         }
02310         break;
02311         case QEvent::MouseMove:
02312         case QEvent::MouseButtonPress:
02313         case QEvent::MouseButtonRelease:
02314         case QEvent::MouseButtonDblClick: {
02315 
02316         if (0 && w->parentWidget() == view && !qobject_cast<QScrollBar*>(w) && !::qobject_cast<QScrollBar *>(w)) {
02317             QMouseEvent *me = static_cast<QMouseEvent *>(e);
02318             QPoint pt = w->mapTo( view, me->pos());
02319             QMouseEvent me2(me->type(), pt, me->button(), me->buttons(), me->modifiers());
02320 
02321             if (e->type() == QEvent::MouseMove)
02322             mouseMoveEvent(&me2);
02323             else if(e->type() == QEvent::MouseButtonPress)
02324             mousePressEvent(&me2);
02325             else if(e->type() == QEvent::MouseButtonRelease)
02326             mouseReleaseEvent(&me2);
02327             else
02328             mouseDoubleClickEvent(&me2);
02329             block = true;
02330                 }
02331         break;
02332         }
02333         case QEvent::KeyPress:
02334         case QEvent::KeyRelease:
02335         if (w->parentWidget() == view && !qobject_cast<QScrollBar*>(w)) {
02336             QKeyEvent *ke = static_cast<QKeyEvent *>(e);
02337             if (e->type() == QEvent::KeyPress)
02338             keyPressEvent(ke);
02339             else
02340             keyReleaseEvent(ke);
02341             block = true;
02342         }
02343         break;
02344             case QEvent::FocusIn:
02345             case QEvent::FocusOut:
02346                 block = true;
02347                 break;
02348         default:
02349         break;
02350         }
02351         if (block) {
02352         //qDebug("eating event");
02353         return true;
02354         }
02355     }
02356     }
02357 
02358 //    kDebug(6000) <<"passing event on to sv event filter object=" << o->className() << " event=" << e->type();
02359     return QScrollArea::eventFilter(o, e);
02360 }
02361 
02362 bool KHTMLView::widgetEvent(QEvent* e)
02363 {
02364     switch (e->type()) {
02365       case QEvent::MouseButtonPress:
02366       case QEvent::MouseButtonRelease:
02367       case QEvent::MouseButtonDblClick:
02368       case QEvent::MouseMove:
02369       case QEvent::Paint:
02370 #ifndef QT_NO_WHEELEVENT
02371       case QEvent::Wheel:
02372 #endif
02373       case QEvent::ContextMenu:
02374       case QEvent::DragEnter:
02375       case QEvent::DragMove:
02376       case QEvent::DragLeave:
02377       case QEvent::Drop:
02378         return QFrame::event(e);
02379       case QEvent::ChildPolished: {
02380         // we need to install an event filter on all children of the widget() to
02381         // be able to get correct stacking of children within the document.
02382         QObject *c = static_cast<QChildEvent *>(e)->child();
02383         if (c->isWidgetType()) {
02384             QWidget *w = static_cast<QWidget *>(c);
02385         // don't install the event filter on toplevels
02386         if (!(w->windowFlags() & Qt::Window) && !(w->windowModality() & Qt::ApplicationModal)) {
02387             KHTMLWidget* k = dynamic_cast<KHTMLWidget*>(w);
02388             if (k && k->m_kwp->isRedirected()) {
02389                 w->unsetCursor();
02390             handleWidget(w, this);
02391                 }
02392             }
02393         }
02394       }
02395       case QEvent::Move: {
02396           if (static_cast<QMoveEvent*>(e)->pos() != QPoint(0,0)) {
02397               widget()->move(0,0);
02398               updateScrollBars();
02399               return true;
02400           }
02401       }
02402       default:
02403         break;
02404     }
02405     return false;
02406 }
02407 
02408 DOM::NodeImpl *KHTMLView::nodeUnderMouse() const
02409 {
02410     return d->underMouse;
02411 }
02412 
02413 DOM::NodeImpl *KHTMLView::nonSharedNodeUnderMouse() const
02414 {
02415     return d->underMouseNonShared;
02416 }
02417 
02418 bool KHTMLView::scrollTo(const QRect &bounds)
02419 {
02420     d->scrollingSelf = true; // so scroll events get ignored
02421 
02422     int x, y, xe, ye;
02423     x = bounds.left();
02424     y = bounds.top();
02425     xe = bounds.right();
02426     ye = bounds.bottom();
02427 
02428     //kDebug(6000)<<"scrolling coords: x="<<x<<" y="<<y<<" width="<<xe-x<<" height="<<ye-y;
02429 
02430     int deltax;
02431     int deltay;
02432 
02433     int curHeight = visibleHeight();
02434     int curWidth = visibleWidth();
02435 
02436     if (ye-y>curHeight-d->borderY)
02437     ye  = y + curHeight - d->borderY;
02438 
02439     if (xe-x>curWidth-d->borderX)
02440     xe = x + curWidth - d->borderX;
02441 
02442     // is xpos of target left of the view's border?
02443     if (x < contentsX() + d->borderX )
02444             deltax = x - contentsX() - d->borderX;
02445     // is xpos of target right of the view's right border?
02446     else if (xe + d->borderX > contentsX() + curWidth)
02447             deltax = xe + d->borderX - ( contentsX() + curWidth );
02448     else
02449         deltax = 0;
02450 
02451     // is ypos of target above upper border?
02452     if (y < contentsY() + d->borderY)
02453             deltay = y - contentsY() - d->borderY;
02454     // is ypos of target below lower border?
02455     else if (ye + d->borderY > contentsY() + curHeight)
02456             deltay = ye + d->borderY - ( contentsY() + curHeight );
02457     else
02458         deltay = 0;
02459 
02460     int maxx = curWidth-d->borderX;
02461     int maxy = curHeight-d->borderY;
02462 
02463     int scrollX, scrollY;
02464 
02465     scrollX = deltax > 0 ? (deltax > maxx ? maxx : deltax) : deltax == 0 ? 0 : (deltax>-maxx ? deltax : -maxx);
02466     scrollY = deltay > 0 ? (deltay > maxy ? maxy : deltay) : deltay == 0 ? 0 : (deltay>-maxy ? deltay : -maxy);
02467 
02468     if (contentsX() + scrollX < 0)
02469     scrollX = -contentsX();
02470     else if (contentsWidth() - visibleWidth() - contentsX() < scrollX)
02471     scrollX = contentsWidth() - visibleWidth() - contentsX();
02472 
02473     if (contentsY() + scrollY < 0)
02474     scrollY = -contentsY();
02475     else if (contentsHeight() - visibleHeight() - contentsY() < scrollY)
02476     scrollY = contentsHeight() - visibleHeight() - contentsY();
02477 
02478     horizontalScrollBar()->setValue( horizontalScrollBar()->value()+scrollX );
02479     verticalScrollBar()->setValue( verticalScrollBar()->value()+scrollY );
02480 
02481     d->scrollingSelf = false;
02482 
02483     if ( (abs(deltax)<=maxx) && (abs(deltay)<=maxy) )
02484     return true;
02485     else return false;
02486 
02487 }
02488 
02489 bool KHTMLView::focusNextPrevNode(bool next)
02490 {
02491     // Sets the focus node of the document to be the node after (or if
02492     // next is false, before) the current focus node.  Only nodes that
02493     // are selectable (i.e. for which isFocusable() returns true) are
02494     // taken into account, and the order used is that specified in the
02495     // HTML spec (see DocumentImpl::nextFocusNode() and
02496     // DocumentImpl::previousFocusNode() for details).
02497 
02498     DocumentImpl *doc = m_part->xmlDocImpl();
02499     NodeImpl *oldFocusNode = doc->focusNode();
02500 
02501     // See whether we're in the middle of a detach, or hiding of the
02502     // widget. In this case, we will just clear focus, being careful not to emit events
02503     // or update rendering. Doing this also prevents the code below from going bonkers with
02504     // oldFocusNode not actually being focusable, etc.
02505     if (oldFocusNode) {
02506     if (oldFocusNode->renderer() && !oldFocusNode->renderer()->parent()
02507           || !oldFocusNode->isTabFocusable()) {
02508         doc->quietResetFocus();
02509         return true;
02510     }
02511     }
02512 
02513 #if 1
02514     // If the user has scrolled the document, then instead of picking
02515     // the next focusable node in the document, use the first one that
02516     // is within the visible area (if possible).
02517     if (d->scrollBarMoved)
02518     {
02519     NodeImpl *toFocus;
02520     if (next)
02521         toFocus = doc->nextFocusNode(oldFocusNode);
02522     else
02523         toFocus = doc->previousFocusNode(oldFocusNode);
02524 
02525     if (!toFocus && oldFocusNode)
02526         if (next)
02527         toFocus = doc->nextFocusNode(NULL);
02528         else
02529         toFocus = doc->previousFocusNode(NULL);
02530 
02531     while (toFocus && toFocus != oldFocusNode)
02532     {
02533 
02534         QRect focusNodeRect = toFocus->getRect();
02535         if ((focusNodeRect.left() > contentsX()) && (focusNodeRect.right() < contentsX() + visibleWidth()) &&
02536         (focusNodeRect.top() > contentsY()) && (focusNodeRect.bottom() < contentsY() + visibleHeight())) {
02537         {
02538             QRect r = toFocus->getRect();
02539             ensureVisible( r.right(), r.bottom());
02540             ensureVisible( r.left(), r.top());
02541             d->scrollBarMoved = false;
02542             d->tabMovePending = false;
02543             d->lastTabbingDirection = next;
02544             d->pseudoFocusNode = KHTMLViewPrivate::PFNone;
02545             m_part->xmlDocImpl()->setFocusNode(toFocus);
02546             Node guard(toFocus);
02547             if (!toFocus->hasOneRef() )
02548             {
02549             emit m_part->nodeActivated(Node(toFocus));
02550             }
02551             return true;
02552         }
02553         }
02554         if (next)
02555         toFocus = doc->nextFocusNode(toFocus);
02556         else
02557         toFocus = doc->previousFocusNode(toFocus);
02558 
02559         if (!toFocus && oldFocusNode)
02560         if (next)
02561             toFocus = doc->nextFocusNode(NULL);
02562         else
02563             toFocus = doc->previousFocusNode(NULL);
02564     }
02565 
02566     d->scrollBarMoved = false;
02567     }
02568 #endif
02569 
02570     if (!oldFocusNode && d->pseudoFocusNode == KHTMLViewPrivate::PFNone)
02571     {
02572     ensureVisible(contentsX(), next?0:contentsHeight());
02573     d->scrollBarMoved = false;
02574     d->pseudoFocusNode = next?KHTMLViewPrivate::PFTop:KHTMLViewPrivate::PFBottom;
02575     return true;
02576     }
02577 
02578     NodeImpl *newFocusNode = NULL;
02579 
02580     if (d->tabMovePending && next != d->lastTabbingDirection)
02581     {
02582     //kDebug ( 6000 ) << " tab move pending and tabbing direction changed!\n";
02583     newFocusNode = oldFocusNode;
02584     }
02585     else if (next)
02586     {
02587     if (oldFocusNode || d->pseudoFocusNode == KHTMLViewPrivate::PFTop )
02588         newFocusNode = doc->nextFocusNode(oldFocusNode);
02589     }
02590     else
02591     {
02592     if (oldFocusNode || d->pseudoFocusNode == KHTMLViewPrivate::PFBottom )
02593         newFocusNode = doc->previousFocusNode(oldFocusNode);
02594     }
02595 
02596     bool targetVisible = false;
02597     if (!newFocusNode)
02598     {
02599     if ( next )
02600     {
02601         targetVisible = scrollTo(QRect(contentsX()+visibleWidth()/2,contentsHeight()-d->borderY,0,0));
02602     }
02603     else
02604     {
02605         targetVisible = scrollTo(QRect(contentsX()+visibleWidth()/2,d->borderY,0,0));
02606     }
02607     }
02608     else
02609     {
02610         // if it's an editable element, activate the caret
02611         if (!m_part->isCaretMode() && newFocusNode->isContentEditable()) {
02612             kDebug(6200) << "show caret! fn: " << newFocusNode->nodeName().string() << endl;
02613             m_part->clearCaretRectIfNeeded();
02614             m_part->d->editor_context.m_selection.moveTo(Position(newFocusNode, 0L));
02615             m_part->setCaretVisible(true);
02616         } else {
02617            m_part->setCaretVisible(false);
02618            kDebug(6200) << "hide caret! fn: " << newFocusNode->nodeName().string() << endl;
02619     }
02620         m_part->notifySelectionChanged();
02621 
02622     targetVisible = scrollTo(newFocusNode->getRect());
02623     }
02624 
02625     if (targetVisible)
02626     {
02627     //kDebug ( 6000 ) << " target reached.\n";
02628     d->tabMovePending = false;
02629 
02630     m_part->xmlDocImpl()->setFocusNode(newFocusNode);
02631     if (newFocusNode)
02632     {
02633         Node guard(newFocusNode);
02634         if (!newFocusNode->hasOneRef() )
02635         {
02636         emit m_part->nodeActivated(Node(newFocusNode));
02637         }
02638         return true;
02639     }
02640     else
02641     {
02642         d->pseudoFocusNode = next?KHTMLViewPrivate::PFBottom:KHTMLViewPrivate::PFTop;
02643         return false;
02644     }
02645     }
02646     else
02647     {
02648     if (!d->tabMovePending)
02649         d->lastTabbingDirection = next;
02650     d->tabMovePending = true;
02651     return true;
02652     }
02653 }
02654 
02655 void KHTMLView::displayAccessKeys()
02656 {
02657     QVector< QChar > taken;
02658     displayAccessKeys( NULL, this, taken, false );
02659     displayAccessKeys( NULL, this, taken, true );
02660 }
02661 
02662 void KHTMLView::displayAccessKeys( KHTMLView* caller, KHTMLView* origview, QVector< QChar >& taken, bool use_fallbacks )
02663 {
02664     QMap< ElementImpl*, QChar > fallbacks;
02665     if( use_fallbacks )
02666         fallbacks = buildFallbackAccessKeys();
02667     for( NodeImpl* n = m_part->xmlDocImpl(); n != NULL; n = n->traverseNextNode()) {
02668         if( n->isElementNode()) {
02669             ElementImpl* en = static_cast< ElementImpl* >( n );
02670             DOMString s = en->getAttribute( ATTR_ACCESSKEY );
02671             QString accesskey;
02672             if( s.length() == 1 ) {
02673                 QChar a = s.string()[ 0 ].toUpper();
02674                 if( qFind( taken.begin(), taken.end(), a ) == taken.end()) // !contains
02675                     accesskey = a;
02676             }
02677             if( accesskey.isNull() && fallbacks.contains( en )) {
02678                 QChar a = fallbacks[ en ].toUpper();
02679                 if( qFind( taken.begin(), taken.end(), a ) == taken.end()) // !contains
02680                     accesskey = QString( "<qt><i>" ) + a + "</i></qt>";
02681             }
02682             if( !accesskey.isNull()) {
02683             QRect rec=en->getRect();
02684             QLabel *lab=new QLabel(accesskey,viewport());
02685             lab->setAttribute(Qt::WA_DeleteOnClose);
02686             connect( origview, SIGNAL(hideAccessKeys()), lab, SLOT(close()) );
02687             connect( this, SIGNAL(repaintAccessKeys()), lab, SLOT(repaint()));
02688             lab->setPalette(QToolTip::palette());
02689             lab->setLineWidth(2);
02690             lab->setFrameStyle(QFrame::Box | QFrame::Plain);
02691             lab->setMargin(3);
02692             lab->adjustSize();
02693             lab->setParent( widget() );
02694         lab->setAutoFillBackground(true);
02695             lab->move(
02696             qMin(rec.left()+rec.width()/2 - contentsX(), contentsWidth() - lab->width()),
02697             qMin(rec.top()+rec.height()/2 - contentsY(), contentsHeight() - lab->height()));
02698             lab->show();
02699                 taken.append( accesskey[ 0 ] );
02700         }
02701         }
02702     }
02703     if( use_fallbacks )
02704         return;
02705 
02706     QList<KParts::ReadOnlyPart*> frames = m_part->frames();
02707     foreach( KParts::ReadOnlyPart* cur, frames ) {
02708         if( !qobject_cast<KHTMLPart*>(cur) )
02709             continue;
02710         KHTMLPart* part = static_cast< KHTMLPart* >( cur );
02711         if( part->view() && part->view() != caller )
02712             part->view()->displayAccessKeys( this, origview, taken, use_fallbacks );
02713     }
02714 
02715     // pass up to the parent
02716     if (m_part->parentPart() && m_part->parentPart()->view()
02717         && m_part->parentPart()->view() != caller)
02718         m_part->parentPart()->view()->displayAccessKeys( this, origview, taken, use_fallbacks );
02719 }
02720 
02721 bool KHTMLView::isScrollingFromMouseWheel() const
02722 {
02723     return d->scrollingFromWheel != QPoint(-1,-1);
02724 }
02725 
02726 void KHTMLView::accessKeysTimeout()
02727 {
02728 d->accessKeysActivated=false;
02729 d->accessKeysPreActivate = false;
02730 m_part->setStatusBarText(QString(), KHTMLPart::BarOverrideText);
02731 emit hideAccessKeys();
02732 }
02733 
02734 // Handling of the HTML accesskey attribute.
02735 bool KHTMLView::handleAccessKey( const QKeyEvent* ev )
02736 {
02737 // Qt interprets the keyevent also with the modifiers, and ev->text() matches that,
02738 // but this code must act as if the modifiers weren't pressed
02739     QChar c;
02740     if( ev->key() >= Qt::Key_A && ev->key() <= Qt::Key_Z )
02741         c = 'A' + ev->key() - Qt::Key_A;
02742     else if( ev->key() >= Qt::Key_0 && ev->key() <= Qt::Key_9 )
02743         c = '0' + ev->key() - Qt::Key_0;
02744     else {
02745         // TODO fake XKeyEvent and XLookupString ?
02746         // This below seems to work e.g. for eacute though.
02747         if( ev->text().length() == 1 )
02748             c = ev->text()[ 0 ];
02749     }
02750     if( c.isNull())
02751         return false;
02752     return focusNodeWithAccessKey( c );
02753 }
02754 
02755 bool KHTMLView::focusNodeWithAccessKey( QChar c, KHTMLView* caller )
02756 {
02757     DocumentImpl *doc = m_part->xmlDocImpl();
02758     if( !doc )
02759         return false;
02760     ElementImpl* node = doc->findAccessKeyElement( c );
02761     if( !node ) {
02762         QList<KParts::ReadOnlyPart*> frames = m_part->frames();
02763         foreach( KParts::ReadOnlyPart* cur, frames ) {
02764             if( !qobject_cast<KHTMLPart*>(cur) )
02765                 continue;
02766             KHTMLPart* part = static_cast< KHTMLPart* >( cur );
02767             if( part->view() && part->view() != caller
02768                 && part->view()->focusNodeWithAccessKey( c, this ))
02769                 return true;
02770         }
02771         // pass up to the parent
02772         if (m_part->parentPart() && m_part->parentPart()->view()
02773             && m_part->parentPart()->view() != caller
02774             && m_part->parentPart()->view()->focusNodeWithAccessKey( c, this ))
02775             return true;
02776         if( caller == NULL ) { // the active frame (where the accesskey was pressed)
02777             QMap< ElementImpl*, QChar > fallbacks = buildFallbackAccessKeys();
02778             for( QMap< ElementImpl*, QChar >::ConstIterator it = fallbacks.begin();
02779                  it != fallbacks.end();
02780                  ++it )
02781                 if( *it == c ) {
02782                     node = it.key();
02783                     break;
02784                 }
02785         }
02786         if( node == NULL )
02787             return false;
02788     }
02789 
02790     // Scroll the view as necessary to ensure that the new focus node is visible
02791 
02792     QRect r = node->getRect();
02793     ensureVisible( r.right(), r.bottom());
02794     ensureVisible( r.left(), r.top());
02795 
02796     Node guard( node );
02797     if( node->isFocusable()) {
02798     if (node->id()==ID_LABEL) {
02799         // if Accesskey is a label, give focus to the label's referrer.
02800         node=static_cast<ElementImpl *>(static_cast< HTMLLabelElementImpl* >( node )->getFormElement());
02801         if (!node) return true;
02802             guard = node;
02803     }
02804         // Set focus node on the document
02805 #ifdef __GNUC__
02806 #warning "port QFocusEvent::setReason( QFocusEvent::Shortcut ); to qt4"
02807 #endif
02808         //QFocusEvent::setReason( QFocusEvent::Shortcut );
02809         m_part->xmlDocImpl()->setFocusNode(node);
02810 #ifdef __GNUC__
02811 #warning "port QFocusEvent::resetReason(); to qt4"
02812 #endif
02813         //QFocusEvent::resetReason();
02814         if( node != NULL && node->hasOneRef()) // deleted, only held by guard
02815             return true;
02816         emit m_part->nodeActivated(Node(node));
02817         if( node != NULL && node->hasOneRef())
02818             return true;
02819     }
02820 
02821     switch( node->id()) {
02822         case ID_A:
02823             static_cast< HTMLAnchorElementImpl* >( node )->click();
02824           break;
02825         case ID_INPUT:
02826             static_cast< HTMLInputElementImpl* >( node )->click();
02827           break;
02828         case ID_BUTTON:
02829             static_cast< HTMLButtonElementImpl* >( node )->click();
02830           break;
02831         case ID_AREA:
02832             static_cast< HTMLAreaElementImpl* >( node )->click();
02833           break;
02834         case ID_TEXTAREA:
02835       break; // just focusing it is enough
02836         case ID_LEGEND:
02837             // TODO
02838           break;
02839     }
02840     return true;
02841 }
02842 
02843 static QString getElementText( NodeImpl* start, bool after )
02844 {
02845     QString ret;             // nextSibling(), to go after e.g. </select>
02846     for( NodeImpl* n = after ? start->nextSibling() : start->traversePreviousNode();
02847          n != NULL;
02848          n = after ? n->traverseNextNode() : n->traversePreviousNode()) {
02849         if( n->isTextNode()) {
02850             if( after )
02851                 ret += static_cast< TextImpl* >( n )->toString().string();
02852             else
02853                 ret.prepend( static_cast< TextImpl* >( n )->toString().string());
02854         } else {
02855             switch( n->id()) {
02856                 case ID_A:
02857                 case ID_FONT:
02858                 case ID_TT:
02859                 case ID_U:
02860                 case ID_B:
02861                 case ID_I:
02862                 case ID_S:
02863                 case ID_STRIKE:
02864                 case ID_BIG:
02865                 case ID_SMALL:
02866                 case ID_EM:
02867                 case ID_STRONG:
02868                 case ID_DFN:
02869                 case ID_CODE:
02870                 case ID_SAMP:
02871                 case ID_KBD:
02872                 case ID_VAR:
02873                 case ID_CITE:
02874                 case ID_ABBR:
02875                 case ID_ACRONYM:
02876                 case ID_SUB:
02877                 case ID_SUP:
02878                 case ID_SPAN:
02879                 case ID_NOBR:
02880                 case ID_WBR:
02881                     break;
02882                 case ID_TD:
02883                     if( ret.trimmed().isEmpty())
02884                         break;
02885                     // fall through
02886                 default:
02887                     return ret.simplified();
02888             }
02889         }
02890     }
02891     return ret.simplified();
02892 }
02893 
02894 static QMap< NodeImpl*, QString > buildLabels( NodeImpl* start )
02895 {
02896     QMap< NodeImpl*, QString > ret;
02897     for( NodeImpl* n = start;
02898          n != NULL;
02899          n = n->traverseNextNode()) {
02900         if( n->id() == ID_LABEL ) {
02901             HTMLLabelElementImpl* label = static_cast< HTMLLabelElementImpl* >( n );
02902             NodeImpl* labelfor = label->getFormElement();
02903             if( labelfor )
02904                 ret[ labelfor ] = label->innerText().string().simplified();
02905         }
02906     }
02907     return ret;
02908 }
02909 
02910 namespace khtml {
02911 struct AccessKeyData {
02912     ElementImpl* element;
02913     QString text;
02914     QString url;
02915     int priority; // 10(highest) - 0(lowest)
02916 };
02917 }
02918 
02919 QMap< ElementImpl*, QChar > KHTMLView::buildFallbackAccessKeys() const
02920 {
02921     // build a list of all possible candidate elements that could use an accesskey
02922     QLinkedList< AccessKeyData > data; // Note: this has to be a list type that keep iterators valid
02923                                        // when other entries are removed
02924     QMap< NodeImpl*, QString > labels = buildLabels( m_part->xmlDocImpl());
02925     for( NodeImpl* n = m_part->xmlDocImpl();
02926          n != NULL;
02927          n = n->traverseNextNode()) {
02928         if( n->isElementNode()) {
02929             ElementImpl* element = static_cast< ElementImpl* >( n );
02930             if( element->getAttribute( ATTR_ACCESSKEY ).length() == 1 )
02931                 continue; // has accesskey set, ignore
02932             if( element->renderer() == NULL )
02933                 continue; // not visible
02934             QString text;
02935             QString url;
02936             int priority = 0;
02937             bool ignore = false;
02938             bool text_after = false;
02939             bool text_before = false;
02940             switch( element->id()) {
02941                 case ID_A:
02942                     url = khtml::parseURL(element->getAttribute(ATTR_HREF)).string();
02943                     if( url.isEmpty()) // doesn't have href, it's only an anchor
02944                         continue;
02945                     text = static_cast< HTMLElementImpl* >( element )->innerText().string().simplified();
02946                     priority = 2;
02947                     break;
02948                 case ID_INPUT: {
02949                     HTMLInputElementImpl* in = static_cast< HTMLInputElementImpl* >( element );
02950                     switch( in->inputType()) {
02951                         case HTMLInputElementImpl::SUBMIT:
02952                             text = in->value().string();
02953                             if( text.isEmpty())
02954                                 text = i18n( "Submit" );
02955                             priority = 7;
02956                             break;
02957                         case HTMLInputElementImpl::IMAGE:
02958                             text = in->altText().string();
02959                             priority = 7;
02960                             break;
02961                         case HTMLInputElementImpl::BUTTON:
02962                             text = in->value().string();
02963                             priority = 5;
02964                             break;
02965                         case HTMLInputElementImpl::RESET:
02966                             text = in->value().string();
02967                             if( text.isEmpty())
02968                                 text = i18n( "Reset" );
02969                             priority = 5;
02970                             break;
02971                         case HTMLInputElementImpl::HIDDEN:
02972                             ignore = true;
02973                             break;
02974                         case HTMLInputElementImpl::CHECKBOX:
02975                         case HTMLInputElementImpl::RADIO:
02976                             text_after = true;
02977                             priority = 5;
02978                             break;
02979                         case HTMLInputElementImpl::TEXT:
02980                         case HTMLInputElementImpl::PASSWORD:
02981                         case HTMLInputElementImpl::FILE:
02982                             text_before = true;
02983                             priority = 5;
02984                             break;
02985                         default:
02986                             priority = 5;
02987                             break;
02988                     }
02989                     break;
02990                 }
02991                 case ID_BUTTON:
02992                     text = static_cast< HTMLElementImpl* >( element )->innerText().string().simplified();
02993                     switch( static_cast< HTMLButtonElementImpl* >( element )->buttonType()) {
02994                         case HTMLButtonElementImpl::SUBMIT:
02995                             if( text.isEmpty())
02996                                 text = i18n( "Submit" );
02997                             priority = 7;
02998                             break;
02999                         case HTMLButtonElementImpl::RESET:
03000                             if( text.isEmpty())
03001                                 text = i18n( "Reset" );
03002                             priority = 5;
03003                             break;
03004                         default:
03005                             priority = 5;
03006                             break;
03007                     }
03008                     break;
03009                 case ID_SELECT: // these don't have accesskey attribute, but quick access may be handy
03010                     text_before = true;
03011                     text_after = true;
03012                     priority = 5;
03013                     break;
03014                 case ID_FRAME:
03015                     ignore = true;
03016                     break;
03017                 default:
03018                     ignore = !element->isFocusable();
03019                     priority = 2;
03020                     break;
03021             }
03022             if( ignore )
03023                 continue;
03024             if( text.isNull() && labels.contains( element ))
03025                 text = labels[ element ];
03026             if( text.isNull() && text_before )
03027                 text = getElementText( element, false );
03028             if( text.isNull() && text_after )
03029                 text = getElementText( element, true );
03030             text = text.trimmed();
03031             // increase priority of items which have explicitly specified accesskeys in the config
03032             QList< QPair< QString, QChar > > priorities
03033                 = m_part->settings()->fallbackAccessKeysAssignments();
03034             for( QList< QPair< QString, QChar > >::ConstIterator it = priorities.begin();
03035                  it != priorities.end();
03036                  ++it ) {
03037                 if( text == (*it).first )
03038                     priority = 10;
03039             }
03040             AccessKeyData tmp = { element, text, url, priority };
03041             data.append( tmp );
03042         }
03043     }
03044 
03045     QList< QChar > keys;
03046     for( char c = 'A'; c <= 'Z'; ++c )
03047         keys << c;
03048     for( char c = '0'; c <= '9'; ++c )
03049         keys << c;
03050     for( NodeImpl* n = m_part->xmlDocImpl();
03051          n != NULL;
03052          n = n->traverseNextNode()) {
03053         if( n->isElementNode()) {
03054             ElementImpl* en = static_cast< ElementImpl* >( n );
03055             DOMString s = en->getAttribute( ATTR_ACCESSKEY );
03056             if( s.length() == 1 ) {
03057                 QChar c = s.string()[ 0 ].toUpper();
03058                 keys.removeAll( c ); // remove manually assigned accesskeys
03059             }
03060         }
03061     }
03062 
03063     QMap< ElementImpl*, QChar > ret;
03064     for( int priority = 10; priority >= 0; --priority ) {
03065         for( QLinkedList< AccessKeyData >::Iterator it = data.begin();
03066              it != data.end();
03067              ) {
03068             if( (*it).priority != priority ) {
03069                 ++it;
03070                 continue;
03071             }
03072             if( keys.isEmpty())
03073                 break;
03074             QString text = (*it).text;
03075             QChar key;
03076             if( key.isNull() && !text.isEmpty()) {
03077                 QList< QPair< QString, QChar > > priorities
03078                     = m_part->settings()->fallbackAccessKeysAssignments();
03079                 for( QList< QPair< QString, QChar > >::ConstIterator it = priorities.begin();
03080                      it != priorities.end();
03081                      ++it )
03082                     if( text == (*it).first && keys.contains( (*it).second )) {
03083                         key = (*it).second;
03084                         break;
03085                     }
03086             }
03087             // try first to select the first character as the accesskey,
03088             // then first character of the following words,
03089             // and then simply the first free character
03090             if( key.isNull() && !text.isEmpty()) {
03091                 const QStringList words = text.split( ' ' );
03092                 for( QStringList::ConstIterator it = words.begin();
03093                      it != words.end();
03094                      ++it ) {
03095                     if( keys.contains( (*it)[ 0 ].toUpper())) {
03096                         key = (*it)[ 0 ].toUpper();
03097                         break;
03098                     }
03099                 }
03100             }
03101             if( key.isNull() && !text.isEmpty()) {
03102                 for( int i = 0; i < text.length(); ++i ) {
03103                     if( keys.contains( text[ i ].toUpper())) {
03104                         key = text[ i ].toUpper();
03105                         break;
03106                     }
03107                 }
03108             }
03109             if( key.isNull())
03110                 key = keys.front();
03111             ret[ (*it).element ] = key;
03112             keys.removeAll( key );
03113             QString url = (*it).url;
03114             it = data.erase( it );
03115             // assign the same accesskey also to other elements pointing to the same url
03116             if( !url.isEmpty() && !url.startsWith( "javascript:", Qt::CaseInsensitive )) {
03117                 for( QLinkedList< AccessKeyData >::Iterator it2 = data.begin();
03118                      it2 != data.end();
03119                      ) {
03120                     if( (*it2).url == url ) {
03121                         ret[ (*it2).element ] = key;
03122                         if( it == it2 )
03123                             ++it;
03124                         it2 = data.erase( it2 );
03125                     } else
03126                         ++it2;
03127                 }
03128             }
03129         }
03130     }
03131     return ret;
03132 }
03133 
03134 void KHTMLView::setMediaType( const QString &medium )
03135 {
03136     m_medium = medium;
03137 }
03138 
03139 QString KHTMLView::mediaType() const
03140 {
03141     return m_medium;
03142 }
03143 
03144 bool KHTMLView::pagedMode() const
03145 {
03146     return d->paged;
03147 }
03148 
03149 void KHTMLView::setWidgetVisible(RenderWidget* w, bool vis)
03150 {
03151     if (vis) {
03152         d->visibleWidgets.insert(w, w->widget());
03153     }
03154     else
03155         d->visibleWidgets.remove(w);
03156 }
03157 
03158 bool KHTMLView::needsFullRepaint() const
03159 {
03160     return d->needsFullRepaint;
03161 }
03162 
03163 namespace {
03164    class QPointerDeleter
03165    {
03166    public:
03167        explicit QPointerDeleter(QObject* o) : obj(o) {}
03168        ~QPointerDeleter() { delete obj; }
03169    private:
03170        const QPointer<QObject> obj; 
03171    }; 
03172 }
03173 
03174 void KHTMLView::print(bool quick)
03175 {
03176     if(!m_part->xmlDocImpl()) return;
03177     khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer());
03178     if(!root) return;
03179 
03180     QPointer<KHTMLPrintSettings> printSettings(new KHTMLPrintSettings); //XXX: doesn't save settings between prints like this
03181     const QPointerDeleter settingsDeleter(printSettings); //the printdialog takes ownership of the settings widget, thus this workaround to avoid double deletion
03182     QPrinter printer;
03183     QPointer<QPrintDialog> dialog = KdePrint::createPrintDialog(&printer, QList<QWidget*>() << printSettings, this);
03184     const QPointerDeleter dialogDeleter(dialog);
03185 
03186     QString docname = m_part->xmlDocImpl()->URL().prettyUrl();
03187     if ( !docname.isEmpty() )
03188         docname = KStringHandler::csqueeze(docname, 80);
03189 
03190     if(quick || (dialog->exec() && dialog)) { /*'this' and thus dialog might have been deleted while exec()!*/
03191         viewport()->setCursor( Qt::WaitCursor ); // only viewport(), no QApplication::, otherwise we get the busy cursor in kdeprint's dialogs
03192         // set up KPrinter
03193         printer.setFullPage(false);
03194         printer.setCreator(QString("KDE %1.%2.%3 HTML Library").arg(KDE_VERSION_MAJOR).arg(KDE_VERSION_MINOR).arg(KDE_VERSION_RELEASE));
03195         printer.setDocName(docname);
03196 
03197         QPainter *p = new QPainter;
03198         p->begin( &printer );
03199         khtml::setPrintPainter( p );
03200 
03201         m_part->xmlDocImpl()->setPaintDevice( &printer );
03202         QString oldMediaType = mediaType();
03203         setMediaType( "print" );
03204         // We ignore margin settings for html and body when printing
03205         // and use the default margins from the print-system
03206         // (In Qt 3.0.x the default margins are hardcoded in Qt)
03207         m_part->xmlDocImpl()->setPrintStyleSheet( printSettings->printFriendly() ?
03208                                                   "* { background-image: none !important;"
03209                                                   "    background-color: white !important;"
03210                                                   "    color: black !important; }"
03211                           "body { margin: 0px !important; }"
03212                           "html { margin: 0px !important; }" :
03213                           "body { margin: 0px !important; }"
03214                           "html { margin: 0px !important; }"
03215                           );
03216 
03217         kDebug(6000) << "printing: physical page width = " << printer.width()
03218                       << " height = " << printer.height() << endl;
03219         root->setStaticMode(true);
03220         root->setPagedMode(true);
03221         root->setWidth(printer.width());
03222 //         root->setHeight(printer.height());
03223         root->setPageTop(0);
03224         root->setPageBottom(0);
03225         d->paged = true;
03226 
03227         m_part->xmlDocImpl()->styleSelector()->computeFontSizes(printer.logicalDpiY(), 100);
03228         m_part->xmlDocImpl()->updateStyleSelector();
03229         root->setPrintImages(printSettings->printImages());
03230         root->makePageBreakAvoidBlocks();
03231 
03232         root->setNeedsLayoutAndMinMaxRecalc();
03233         root->layout();
03234 
03235         // check sizes ask for action.. (scale or clip)
03236 
03237         bool printHeader = printSettings->printHeader();
03238 
03239         int headerHeight = 0;
03240         QFont headerFont("Sans Serif", 8);
03241 
03242         QString headerLeft = KGlobal::locale()->formatDate(QDate::currentDate(),KLocale::ShortDate);
03243         QString headerMid = docname;
03244         QString headerRight;
03245 
03246         if (printHeader)
03247         {
03248            p->setFont(headerFont);
03249            headerHeight = (p->fontMetrics().lineSpacing() * 3) / 2;
03250         }
03251 
03252         // ok. now print the pages.
03253         kDebug(6000) << "printing: html page width = " << root->docWidth()
03254                       << " height = " << root->docHeight() << endl;
03255         kDebug(6000) << "printing: margins left = " << printer.pageRect().left() - printer.paperRect().left()
03256                       << " top = " << printer.pageRect().top() - printer.paperRect().top() << endl;
03257         kDebug(6000) << "printing: paper width = " << printer.width()
03258                       << " height = " << printer.height() << endl;
03259         // if the width is too large to fit on the paper we just scale
03260         // the whole thing.
03261         int pageWidth = printer.width();
03262         int pageHeight = printer.height();
03263         p->setClipRect(0,0, pageWidth, pageHeight);
03264 
03265         pageHeight -= headerHeight;
03266 
03267         bool scalePage = false;
03268         double scale = 0.0;
03269 #ifndef QT_NO_TRANSFORMATIONS
03270         if(root->docWidth() > printer.width()) {
03271             scalePage = true;
03272             scale = ((double) printer.width())/((double) root->docWidth());
03273             pageHeight = (int) (pageHeight/scale);
03274             pageWidth = (int) (pageWidth/scale);
03275             headerHeight = (int) (headerHeight/scale);
03276         }
03277 #endif
03278         kDebug(6000) << "printing: scaled html width = " << pageWidth
03279                       << " height = " << pageHeight << endl;
03280 
03281         root->setHeight(pageHeight);
03282         root->setPageBottom(pageHeight);
03283         root->setNeedsLayout(true);
03284         root->layoutIfNeeded();
03285 //         m_part->slotDebugRenderTree();
03286 
03287         // Squeeze header to make it it on the page.
03288         if (printHeader)
03289         {
03290             int available_width = printer.width() - 10 -
03291                 2 * qMax(p->boundingRect(0, 0, printer.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerLeft).width(),
03292                          p->boundingRect(0, 0, printer.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerRight).width());
03293             if (available_width < 150)
03294                available_width = 150;
03295             int mid_width;
03296             int squeeze = 120;
03297             do {
03298                 headerMid = KStringHandler::csqueeze(docname, squeeze);
03299                 mid_width = p->boundingRect(0, 0, printer.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerMid).width();
03300                 squeeze -= 10;
03301             } while (mid_width > available_width);
03302         }
03303 
03304         int top = 0;
03305         int bottom = 0;
03306         int page = 1;
03307         while(top < root->docHeight()) {
03308             if(top > 0) printer.newPage();
03309             p->save();
03310             p->setClipRect(0, 0, pageWidth, headerHeight);
03311             if (printHeader)
03312             {
03313                 int dy = p->fontMetrics().lineSpacing();
03314                 p->setPen(Qt::black);
03315                 p->setFont(headerFont);
03316 
03317                 headerRight = QString("#%1").arg(page);
03318 
03319                 p->drawText(0, 0, printer.width(), dy, Qt::AlignLeft, headerLeft);
03320                 p->drawText(0, 0, printer.width(), dy, Qt::AlignHCenter, headerMid);
03321                 p->drawText(0, 0, printer.width(), dy, Qt::AlignRight, headerRight);
03322             }
03323 
03324 #ifndef QT_NO_TRANSFORMATIONS
03325             if (scalePage)
03326                 p->scale(scale, scale);
03327 #endif
03328             p->restore();
03329             p->translate(0, headerHeight-top);
03330 
03331             bottom = top+pageHeight;
03332 
03333             root->setPageTop(top);
03334             root->setPageBottom(bottom);
03335             root->setPageNumber(page);
03336 
03337             root->layer()->paint(p, QRect(0, top, pageWidth, pageHeight));
03338             kDebug(6000) << "printed: page " << page <<" bottom At = " << bottom;
03339 
03340             top = bottom;
03341             p->resetTransform();
03342             page++;
03343         }
03344 
03345         p->end();
03346         delete p;
03347 
03348         // and now reset the layout to the usual one...
03349         root->setPagedMode(false);
03350         root->setStaticMode(false);
03351         d->paged = false;
03352         khtml::setPrintPainter( 0 );
03353         setMediaType( oldMediaType );
03354         m_part->xmlDocImpl()->setPaintDevice( this );
03355         m_part->xmlDocImpl()->styleSelector()->computeFontSizes(m_part->xmlDocImpl()->logicalDpiY(), m_part->fontScaleFactor());
03356         m_part->xmlDocImpl()->updateStyleSelector();
03357         viewport()->unsetCursor();
03358     }
03359 }
03360 
03361 void KHTMLView::slotPaletteChanged()
03362 {
03363     if(!m_part->xmlDocImpl()) return;
03364     DOM::DocumentImpl *document = m_part->xmlDocImpl();
03365     if (!document->isHTMLDocument()) return;
03366     khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(document->renderer());
03367     if(!root) return;
03368     root->style()->resetPalette();
03369     NodeImpl *body = static_cast<HTMLDocumentImpl*>(document)->body();
03370     if(!body) return;
03371     body->setChanged(true);
03372     body->recalcStyle( NodeImpl::Force );
03373 }
03374 
03375 void KHTMLView::paint(QPainter *p, const QRect &rc, int yOff, bool *more)
03376 {
03377     if(!m_part->xmlDocImpl()) return;
03378     khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer());
03379     if(!root) return;
03380     d->firstRepaintPending = false;
03381 
03382     QPaintDevice* opd = m_part->xmlDocImpl()->paintDevice();
03383     m_part->xmlDocImpl()->setPaintDevice(p->device());
03384     root->setPagedMode(true);
03385     root->setStaticMode(true);
03386     root->setWidth(rc.width());
03387 
03388     // save()
03389     QRegion creg = p->clipRegion();
03390     QTransform t = p->worldTransform();
03391     QRect w = p->window();
03392     QRect v = p->viewport();
03393     bool vte = p->viewTransformEnabled();
03394     bool wme = p->worldMatrixEnabled();
03395 
03396     p->setClipRect(rc);
03397     p->translate(rc.left(), rc.top());
03398     double scale = ((double) rc.width()/(double) root->docWidth());
03399     int height = (int) ((double) rc.height() / scale);
03400 #ifndef QT_NO_TRANSFORMATIONS
03401     p->scale(scale, scale);
03402 #endif
03403     root->setPageTop(yOff);
03404     root->setPageBottom(yOff+height);
03405 
03406     root->layer()->paint(p, QRect(0, yOff, root->docWidth(), height));
03407     if (more)
03408         *more = yOff + height < root->docHeight();
03409 
03410     // restore()
03411     p->setWorldTransform(t);
03412     p->setWindow(w);
03413     p->setViewport(v);
03414     p->setViewTransformEnabled( vte );
03415     p->setWorldMatrixEnabled( wme );
03416     if (!creg.isEmpty())
03417         p->setClipRegion( creg );
03418     else
03419         p->setClipRegion(QRegion(), Qt::NoClip);
03420 
03421     root->setPagedMode(false);
03422     root->setStaticMode(false);
03423     m_part->xmlDocImpl()->setPaintDevice( opd );
03424 }
03425 
03426 void KHTMLView::render(QPainter* p, const QRect& r, const QPoint& off)
03427 {
03428     d->firstRepaintPending = false;
03429     QRect clip(off.x()+r.x(), off.y()+r.y(),r.width(),r.height());
03430     if(!m_part || !m_part->xmlDocImpl() || !m_part->xmlDocImpl()->renderer()) {
03431         p->fillRect(clip, palette().brush(QPalette::Active, QPalette::Base));
03432         return;
03433     }
03434     QPaintDevice* opd = m_part->xmlDocImpl()->paintDevice();
03435     m_part->xmlDocImpl()->setPaintDevice(p->device());
03436 
03437     // save()
03438     QRegion creg = p->clipRegion();
03439     QTransform t = p->worldTransform();
03440     QRect w = p->window();
03441     QRect v = p->viewport();
03442     bool vte = p->viewTransformEnabled();
03443     bool wme = p->worldMatrixEnabled();
03444 
03445     p->setClipRect(clip);
03446     QRect rect = r.translated(contentsX(),contentsY());
03447     p->translate(off.x()-contentsX(), off.y()-contentsY());
03448 
03449     m_part->xmlDocImpl()->renderer()->layer()->paint(p, rect);
03450 
03451     // restore()
03452     p->setWorldTransform(t);
03453     p->setWindow(w);
03454     p->setViewport(v);
03455     p->setViewTransformEnabled( vte );
03456     p->setWorldMatrixEnabled( wme );
03457     if (!creg.isEmpty())
03458         p->setClipRegion( creg );
03459     else
03460         p->setClipRegion(QRegion(), Qt::NoClip);
03461 
03462     m_part->xmlDocImpl()->setPaintDevice( opd );
03463 }
03464 
03465 void KHTMLView::setHasStaticBackground(bool partial)
03466 {
03467     // full static iframe is irreversible for now
03468     if (d->staticWidget == KHTMLViewPrivate::SBFull && m_kwp->isRedirected())
03469         return;
03470 
03471     d->staticWidget = partial ?
03472                           KHTMLViewPrivate::SBPartial : KHTMLViewPrivate::SBFull;
03473 }
03474 
03475 void KHTMLView::setHasNormalBackground()
03476 {
03477     // full static iframe is irreversible for now
03478     if (d->staticWidget == KHTMLViewPrivate::SBFull && m_kwp->isRedirected())
03479         return;
03480 
03481     d->staticWidget = KHTMLViewPrivate::SBNone;
03482 }
03483 
03484 void KHTMLView::addStaticObject(bool fixed)
03485 {
03486     if (fixed)
03487         d->fixedObjectsCount++;
03488     else
03489         d->staticObjectsCount++;
03490 
03491     setHasStaticBackground( true /*partial*/ );
03492 }
03493 
03494 void KHTMLView::removeStaticObject(bool fixed)
03495 {
03496     if (fixed)
03497         d->fixedObjectsCount--;
03498     else
03499         d->staticObjectsCount--;
03500 
03501     assert( d->fixedObjectsCount >= 0 && d->staticObjectsCount >= 0 );
03502 
03503     if (!d->staticObjectsCount && !d->fixedObjectsCount)
03504         setHasNormalBackground();
03505     else
03506         setHasStaticBackground( true /*partial*/ );
03507 }
03508 
03509 void KHTMLView::setVerticalScrollBarPolicy( Qt::ScrollBarPolicy policy )
03510 {
03511 #ifndef KHTML_NO_SCROLLBARS
03512     d->vpolicy = policy;
03513     QScrollArea::setVerticalScrollBarPolicy(policy);
03514 #else
03515     Q_UNUSED( policy );
03516 #endif
03517 }
03518 
03519 void KHTMLView::setHorizontalScrollBarPolicy( Qt::ScrollBarPolicy policy )
03520 {
03521 #ifndef KHTML_NO_SCROLLBARS
03522     d->hpolicy = policy;
03523     QScrollArea::setHorizontalScrollBarPolicy(policy);
03524 #else
03525     Q_UNUSED( policy );
03526 #endif
03527 }
03528 
03529 void KHTMLView::restoreScrollBar()
03530 {
03531     int ow = visibleWidth();
03532     QScrollArea::setVerticalScrollBarPolicy(d->vpolicy);
03533     if (visibleWidth() != ow)
03534         layout();
03535     d->prevScrollbarVisible = verticalScrollBar()->isVisible();
03536 }
03537 
03538 QStringList KHTMLView::formCompletionItems(const QString &name) const
03539 {
03540     if (!m_part->settings()->isFormCompletionEnabled())
03541         return QStringList();
03542     if (!d->formCompletions)
03543         d->formCompletions = new KConfig(KStandardDirs::locateLocal("data", "khtml/formcompletions"));
03544     return d->formCompletions->group("").readEntry(name, QStringList());
03545 }
03546 
03547 void KHTMLView::clearCompletionHistory(const QString& name)
03548 {
03549     if (!d->formCompletions)
03550     {
03551         d->formCompletions = new KConfig(KStandardDirs::locateLocal("data", "khtml/formcompletions"));
03552     }
03553     d->formCompletions->group("").writeEntry(name, "");
03554     d->formCompletions->sync();
03555 }
03556 
03557 void KHTMLView::addFormCompletionItem(const QString &name, const QString &value)
03558 {
03559     if (!m_part->settings()->isFormCompletionEnabled())
03560         return;
03561     // don't store values that are all numbers or just numbers with
03562     // dashes or spaces as those are likely credit card numbers or
03563     // something similar
03564     bool cc_number(true);
03565     for ( int i = 0; i < value.length(); ++i)
03566     {
03567       QChar c(value[i]);
03568       if (!c.isNumber() && c != '-' && !c.isSpace())
03569       {
03570         cc_number = false;
03571         break;
03572       }
03573     }
03574     if (cc_number)
03575       return;
03576     QStringList items = formCompletionItems(name);
03577     if (!items.contains(value))
03578         items.prepend(value);
03579     while ((int)items.count() > m_part->settings()->maxFormCompletionItems())
03580         items.erase(items.isEmpty() ? items.end() : --items.end());
03581     d->formCompletions->group("").writeEntry(name, items);
03582 }
03583 
03584 void KHTMLView::addNonPasswordStorableSite(const QString& host)
03585 {
03586     if (!d->formCompletions) {
03587         d->formCompletions = new KConfig(KStandardDirs::locateLocal("data", "khtml/formcompletions"));
03588     }
03589 
03590     KConfigGroup cg( d->formCompletions, "NonPasswordStorableSites");
03591     QStringList sites = cg.readEntry("Sites", QStringList());
03592     sites.append(host);
03593     cg.writeEntry("Sites", sites);
03594     cg.sync();
03595 }
03596 
03597 bool KHTMLView::nonPasswordStorableSite(const QString& host) const
03598 {
03599     if (!d->formCompletions) {
03600         d->formCompletions = new KConfig(KStandardDirs::locateLocal("data", "khtml/formcompletions"));
03601     }
03602     QStringList sites =  d->formCompletions->group( "NonPasswordStorableSites" ).readEntry("Sites", QStringList());
03603     return (sites.indexOf(host) != -1);
03604 }
03605 
03606 // returns true if event should be swallowed
03607 bool KHTMLView::dispatchMouseEvent(int eventId, DOM::NodeImpl *targetNode,
03608                    DOM::NodeImpl *targetNodeNonShared, bool cancelable,
03609                    int detail,QMouseEvent *_mouse, bool setUnder,
03610                    int mouseEventType, int orient)
03611 {
03612     // if the target node is a text node, dispatch on the parent node - rdar://4196646 (and #76948)
03613     if (targetNode && targetNode->isTextNode())
03614         targetNode = targetNode->parentNode();
03615 
03616     if (d->underMouse)
03617     d->underMouse->deref();
03618     d->underMouse = targetNode;
03619     if (d->underMouse)
03620     d->underMouse->ref();
03621 
03622     if (d->underMouseNonShared)
03623     d->underMouseNonShared->deref();
03624     d->underMouseNonShared = targetNodeNonShared;
03625     if (d->underMouseNonShared)
03626     d->underMouseNonShared->ref();
03627 
03628     bool isWheelEvent = (mouseEventType == DOM::NodeImpl::MouseWheel);
03629 
03630     int exceptioncode = 0;
03631     int pageX = _mouse->x();
03632     int pageY = _mouse->y();
03633     revertTransforms(pageX, pageY);
03634     int clientX = pageX - contentsX();
03635     int clientY = pageY - contentsY();
03636     int screenX = _mouse->globalX();
03637     int screenY = _mouse->globalY();
03638     int button = -1;
03639     switch (_mouse->button()) {
03640     case Qt::LeftButton:
03641         button = 0;
03642         break;
03643     case Qt::MidButton:
03644         button = 1;
03645         break;
03646     case Qt::RightButton:
03647         button = 2;
03648         break;
03649     default:
03650         break;
03651     }
03652     if (d->accessKeysEnabled && d->accessKeysPreActivate && button!=-1)
03653         d->accessKeysPreActivate=false;
03654 
03655     bool ctrlKey = (_mouse->modifiers() & Qt::ControlModifier);
03656     bool altKey = (_mouse->modifiers() & Qt::AltModifier);
03657     bool shiftKey = (_mouse->modifiers() & Qt::ShiftModifier);
03658     bool metaKey = (_mouse->modifiers() & Qt::MetaModifier);
03659 
03660     // mouseout/mouseover
03661     if (setUnder && d->oldUnderMouse != targetNode) {
03662         if (d->oldUnderMouse && d->oldUnderMouse->document() != m_part->xmlDocImpl()) {
03663             d->oldUnderMouse->deref();
03664             d->oldUnderMouse = 0;
03665         }
03666         // send mouseout event to the old node
03667         if (d->oldUnderMouse) {
03668         // send mouseout event to the old node
03669             MouseEventImpl *me = new MouseEventImpl(EventImpl::MOUSEOUT_EVENT,
03670                             true,true,m_part->xmlDocImpl()->defaultView(),
03671                             0,screenX,screenY,clientX,clientY,pageX, pageY,
03672                             ctrlKey,altKey,shiftKey,metaKey,
03673                             button,targetNode);
03674             me->ref();
03675             d->oldUnderMouse->dispatchEvent(me,exceptioncode,true);
03676             me->deref();
03677         }
03678         // send mouseover event to the new node
03679     if (targetNode) {
03680         MouseEventImpl *me = new MouseEventImpl(EventImpl::MOUSEOVER_EVENT,
03681                             true,true,m_part->xmlDocImpl()->defaultView(),
03682                             0,screenX,screenY,clientX,clientY,pageX, pageY,
03683                             ctrlKey,altKey,shiftKey,metaKey,
03684                             button,d->oldUnderMouse);
03685 
03686             me->ref();
03687             targetNode->dispatchEvent(me,exceptioncode,true);
03688         me->deref();
03689     }
03690     if (d->oldUnderMouse)
03691         d->oldUnderMouse->deref();
03692         d->oldUnderMouse = targetNode;
03693         if (d->oldUnderMouse)
03694             d->oldUnderMouse->ref();
03695     }
03696 
03697     bool swallowEvent = false;
03698 
03699     if (targetNode) {
03700     // if the target node is a disabled widget, we don't want any full-blown mouse events
03701     if (targetNode->isGenericFormElement()
03702          && static_cast<HTMLGenericFormElementImpl*>(targetNode)->disabled())
03703         return true;
03704 
03705         // send the actual event
03706         bool dblclick = ( eventId == EventImpl::CLICK_EVENT &&
03707                           _mouse->type() == QEvent::MouseButtonDblClick );
03708         MouseEventImpl *me = new MouseEventImpl(static_cast<EventImpl::EventId>(eventId),
03709                         true,cancelable,m_part->xmlDocImpl()->defaultView(),
03710                         detail,screenX,screenY,clientX,clientY,pageX, pageY,
03711                         ctrlKey,altKey,shiftKey,metaKey,
03712                         button,0, isWheelEvent ? 0 : _mouse, dblclick,
03713                         isWheelEvent ? static_cast<MouseEventImpl::Orientation>(orient) : MouseEventImpl::ONone );
03714         me->ref();
03715         if ( !d->m_mouseEventsTarget && RenderLayer::gScrollBar && eventId == EventImpl::MOUSEDOWN_EVENT )
03716             // button is pressed inside a layer scrollbar, so make it the target for future mousemove events until released
03717             d->m_mouseEventsTarget = RenderLayer::gScrollBar;
03718         if ( d->m_mouseEventsTarget && qobject_cast<QScrollBar*>(d->m_mouseEventsTarget) &&
03719              dynamic_cast<KHTMLWidget*>(static_cast<QWidget*>(d->m_mouseEventsTarget)) ) {
03720             // we have a sticky mouse event target and it is a layer's scrollbar. Forward events manually.
03721             // ### should use the dom
03722             KHTMLWidget*w = dynamic_cast<KHTMLWidget*>(static_cast<QWidget*>(d->m_mouseEventsTarget));
03723             QPoint p = w->m_kwp->absolutePos();
03724             QMouseEvent fw(_mouse->type(), QPoint(pageX, pageY)-p, _mouse->button(), _mouse->buttons(), _mouse->modifiers());
03725             static_cast<RenderWidget::EventPropagator *>(static_cast<QWidget*>(d->m_mouseEventsTarget))->sendEvent(&fw);
03726             if (_mouse->type() == QMouseEvent::MouseButtonPress && _mouse->button() == Qt::RightButton) {
03727                 QContextMenuEvent cme(QContextMenuEvent::Mouse, p);
03728                 static_cast<RenderWidget::EventPropagator *>(static_cast<QWidget*>(d->m_mouseEventsTarget))->sendEvent(&cme);
03729                 d->m_mouseEventsTarget = 0;
03730             }
03731             swallowEvent = true;
03732         } else {
03733             targetNode->dispatchEvent(me,exceptioncode,true);
03734         bool defaultHandled = me->defaultHandled();
03735             if (defaultHandled || me->defaultPrevented())
03736                 swallowEvent = true;
03737         }
03738         me->deref();
03739 
03740         if (eventId == EventImpl::MOUSEDOWN_EVENT) {
03741             // Focus should be shifted on mouse down, not on a click.  -dwh
03742             // Blur current focus node when a link/button is clicked; this
03743             // is expected by some sites that rely on onChange handlers running
03744             // from form fields before the button click is processed.
03745             DOM::NodeImpl* nodeImpl = targetNode;
03746             for ( ; nodeImpl && !nodeImpl->isFocusable(); nodeImpl = nodeImpl->parentNode());
03747             if (nodeImpl && nodeImpl->isMouseFocusable())
03748                 m_part->xmlDocImpl()->setFocusNode(nodeImpl);
03749             else if (!nodeImpl || !nodeImpl->focused())
03750                 m_part->xmlDocImpl()->setFocusNode(0);
03751         }
03752     }
03753 
03754     return swallowEvent;
03755 }
03756 
03757 void KHTMLView::setIgnoreWheelEvents( bool e )
03758 {
03759     d->ignoreWheelEvents = e;
03760 }
03761 
03762 #ifndef QT_NO_WHEELEVENT
03763 
03764 void KHTMLView::wheelEvent(QWheelEvent* e)
03765 {
03766     // check if we should reset the state of the indicator describing if
03767     // we are currently scrolling the view as a result of wheel events
03768     if (d->scrollingFromWheel != QPoint(-1,-1) && d->scrollingFromWheel != QCursor::pos())
03769         d->scrollingFromWheel = d->scrollingFromWheelTimerId ? QCursor::pos() : QPoint(-1,-1);
03770 
03771     if (d->accessKeysEnabled && d->accessKeysPreActivate) d->accessKeysPreActivate=false;
03772 
03773     if ( ( e->modifiers() & Qt::ControlModifier) == Qt::ControlModifier )
03774     {
03775         emit zoomView( - e->delta() );
03776         e->accept();
03777     }
03778     else if (d->firstLayoutPending)
03779     {
03780         e->accept();
03781     }
03782     else if( !m_kwp->isRedirected() &&
03783              (   (e->orientation() == Qt::Vertical &&
03784                    ((d->ignoreWheelEvents && !verticalScrollBar()->isVisible())
03785                      || e->delta() > 0 && contentsY() <= 0
03786                      || e->delta() < 0 && contentsY() >= contentsHeight() - visibleHeight()))
03787               ||
03788                  (e->orientation() == Qt::Horizontal &&
03789                     ((d->ignoreWheelEvents && !horizontalScrollBar()->isVisible())
03790                      || e->delta() > 0 && contentsX() <=0
03791                      || e->delta() < 0 && contentsX() >= contentsWidth() - visibleWidth())))
03792             && m_part->parentPart())
03793     {
03794         if ( m_part->parentPart()->view() )
03795             m_part->parentPart()->view()->wheelEvent( e );
03796         e->ignore();
03797     }
03798     else
03799     {
03800         int xm = e->x();
03801         int ym = e->y();
03802         revertTransforms(xm, ym);
03803 
03804         DOM::NodeImpl::MouseEvent mev( e->buttons(), DOM::NodeImpl::MouseWheel );
03805         m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev );
03806 
03807         MouseEventImpl::Orientation o = MouseEventImpl::OVertical;
03808         if (e->orientation() == Qt::Horizontal)
03809             o = MouseEventImpl::OHorizontal;
03810 
03811         QMouseEvent _mouse(QEvent::MouseMove, QPoint(xm,ym), Qt::NoButton, e->buttons(), e->modifiers());
03812         bool swallow = dispatchMouseEvent(EventImpl::KHTML_MOUSEWHEEL_EVENT,mev.innerNode.handle(),mev.innerNonSharedNode.handle(),
03813                                                true,-e->delta()/40,&_mouse,true,DOM::NodeImpl::MouseWheel,o);
03814 
03815         if (swallow)
03816             return;
03817 
03818         d->scrollBarMoved = true;
03819         d->scrollingFromWheel = QCursor::pos();
03820         if (d->smoothScrollMode != SSMDisabled)
03821             d->shouldSmoothScroll = true;
03822         if (d->scrollingFromWheelTimerId)
03823             killTimer(d->scrollingFromWheelTimerId);
03824         d->scrollingFromWheelTimerId = startTimer(400);
03825 
03826         if (m_part->parentPart()) {
03827             // don't propagate if we are a sub-frame and our scrollbars are already at end of range
03828             bool h = (static_cast<QWheelEvent*>(e)->orientation() == Qt::Horizontal);
03829             bool d = (static_cast<QWheelEvent*>(e)->delta() < 0);
03830             QScrollBar* hsb = horizontalScrollBar();
03831             QScrollBar* vsb = verticalScrollBar();
03832             if ( h && (d && hsb->value() == hsb->maximum() || !d && hsb->value() == hsb->minimum()) ||
03833                 !h && (d && vsb->value() == vsb->maximum() || !d && vsb->value() == vsb->minimum()) ) {
03834                 e->accept();
03835                 return;
03836             }
03837         }
03838         QScrollArea::wheelEvent( e );
03839     }
03840 
03841 }
03842 #endif
03843 
03844 void KHTMLView::dragEnterEvent( QDragEnterEvent* ev )
03845 {
03846     // Still overriden for BC reasons only...
03847     QScrollArea::dragEnterEvent( ev );
03848 }
03849 
03850 void KHTMLView::dropEvent( QDropEvent *ev )
03851 {
03852     // Still overriden for BC reasons only...
03853     QScrollArea::dropEvent( ev );
03854 }
03855 
03856 void KHTMLView::focusInEvent( QFocusEvent *e )
03857 {
03858 #ifndef KHTML_NO_TYPE_AHEAD_FIND
03859     m_part->enableFindAheadActions( true );
03860 #endif
03861     DOM::NodeImpl* fn = m_part->xmlDocImpl() ? m_part->xmlDocImpl()->focusNode() : 0;
03862     if (fn && fn->renderer() && fn->renderer()->isWidget() &&
03863         (e->reason() != Qt::MouseFocusReason) &&
03864         static_cast<khtml::RenderWidget*>(fn->renderer())->widget())
03865         static_cast<khtml::RenderWidget*>(fn->renderer())->widget()->setFocus();
03866     m_part->setSelectionVisible();
03867     QScrollArea::focusInEvent( e );
03868 }
03869 
03870 void KHTMLView::focusOutEvent( QFocusEvent *e )
03871 {
03872     m_part->stopAutoScroll();
03873 
03874 #ifndef KHTML_NO_TYPE_AHEAD_FIND
03875     if(d->typeAheadActivated)
03876     {
03877         findTimeout();
03878     }
03879     m_part->enableFindAheadActions( false );
03880 #endif // KHTML_NO_TYPE_AHEAD_FIND
03881 
03882     m_part->setSelectionVisible(false);
03883 
03884     if ( d->cursorIconWidget )
03885         d->cursorIconWidget->hide();
03886 
03887     QScrollArea::focusOutEvent( e );
03888 }
03889 
03890 void KHTMLView::scrollContentsBy( int dx, int dy )
03891 {
03892     if (!dx && !dy) return;
03893 
03894     if ( !d->firstLayoutPending && !d->complete && m_part->xmlDocImpl() &&
03895           d->layoutSchedulingEnabled) {
03896         // contents scroll while we are not complete: we need to check our layout *now*
03897         khtml::RenderCanvas* root = static_cast<khtml::RenderCanvas *>( m_part->xmlDocImpl()->renderer() );
03898         if (root && root->needsLayout()) {
03899             unscheduleRelayout();
03900             layout();
03901         }
03902         if (d->smoothScrollMode == KHTMLView::SSMWhenEfficient && m_part->xmlDocImpl()->parsing())
03903             d->shouldSmoothScroll = false;
03904     }
03905 
03906     if ( d->smoothScrollMode != SSMDisabled &&
03907           (!d->staticWidget||d->smoothScrollMode == SSMEnabled) && d->shouldSmoothScroll ) {
03908         setupSmoothScrolling(dx, dy);
03909         return;
03910     }
03911 
03912     if (!d->scrollingSelf) {
03913         d->scrollBarMoved = true;
03914         d->contentsMoving = true;
03915         // ensure quick reset of contentsMoving flag
03916         scheduleRepaint(0, 0, 0, 0);
03917     }
03918 
03919     if (m_part->xmlDocImpl() && m_part->xmlDocImpl()->documentElement()) {
03920         // ### FIXME: there is something wrong with this event.
03921         // With a capturing listener on document and window, window's should fire first, then document's.
03922         // Also, this doesn't work: <body onload="document.onscroll=function() {alert('ok')}"><div style=height:2000>
03923         m_part->xmlDocImpl()->documentElement()->dispatchWindowEvent(EventImpl::SCROLL_EVENT, false, false);
03924     }
03925 
03926     if (QApplication::isRightToLeft())
03927         dx = -dx;
03928 
03929     if (!d->smoothScrolling) {
03930         d->updateContentsXY();
03931     } else {
03932         d->contentsX -= dx;
03933         d->contentsY -= dy;
03934     }
03935     if (widget()->pos() != QPoint(0,0)) {
03936          kDebug(6000) << "Static widget wasn't positioned at (0,0). This should NOT happen. Please report this event to developers.";
03937          kDebug(6000) <<  kBacktrace();
03938          widget()->move(0,0);
03939     }
03940 
03941     QWidget *w = widget();
03942     QPoint off;
03943     if (m_kwp->isRedirected()) {
03944         // This is a redirected sub frame. Translate to root view context
03945         KHTMLView* v = m_kwp->rootViewPos( off );
03946         if (v)
03947             w = v->widget();
03948         off = viewport()->mapTo(this, off);
03949     }
03950 
03951 #ifdef FIX_QT_BROKEN_QWIDGET_SCROLL
03952     bool hideScrollBars = false;
03953     if (horizontalScrollBar()->isVisible() && verticalScrollBar()->isVisible()) {
03954         if (!d->brokenQWidgetScroll) {
03955             d->shouldBeBlitting = true;
03956         } else {
03957             hideScrollBars = true;
03958         }
03959     }
03960 #endif
03961 
03962     if ( d->staticWidget ) {
03963 
03964         // now remove from view the external widgets that must have completely
03965         // disappeared after dx/dy scroll delta is effective
03966         if (!d->visibleWidgets.isEmpty())
03967             checkExternalWidgetsPosition();
03968 
03969         if ( d->staticWidget == KHTMLViewPrivate::SBPartial
03970                                 && m_part->xmlDocImpl() && m_part->xmlDocImpl()->renderer() ) {
03971 #ifdef FIX_QT_BROKEN_QWIDGET_SCROLL
03972             if (hideScrollBars) {
03973                 horizontalScrollBar()->parentWidget()->lower();
03974                 verticalScrollBar()->parentWidget()->lower();
03975             }
03976 #endif
03977             // static objects might be selectively repainted, like stones in flowing water
03978             QRegion r = static_cast<RenderCanvas*>(m_part->xmlDocImpl()->renderer())->staticRegion();
03979             r.translate( -contentsX(), -contentsY());
03980             QVector<QRect> ar = r.rects();
03981 #ifdef FIX_QT_BROKEN_QWIDGET_SCROLL
03982             if (ar.size() == 1 && ar[0].width() >= visibleWidth() && ar[0].height() >= visibleHeight())
03983                 d->shouldBeBlitting = false;
03984 #endif
03985             for (int i = 0; i < ar.size() ; ++i) {
03986                 widget()->update( ar[i] );
03987             }
03988             r = QRegion(QRect(0, 0, visibleWidth(), visibleHeight())) - r;
03989             ar = r.rects();
03990             for (int i = 0; i < ar.size() ; ++i) {
03991                 w->scroll( dx, dy, ar[i].translated(off) );
03992             }
03993 #ifdef FIX_QT_BROKEN_QWIDGET_SCROLL
03994            if (hideScrollBars) {
03995                 horizontalScrollBar()->parentWidget()->raise();
03996                 verticalScrollBar()->parentWidget()->raise();
03997            }
03998 #endif
03999             d->scrollExternalWidgets(dx, dy);
04000         } else {
04001 #ifdef FIX_QT_BROKEN_QWIDGET_SCROLL
04002             d->shouldBeBlitting = false;
04003 #endif
04004             // we can't avoid a full update
04005             widget()->update();
04006         }
04007         return;
04008     }
04009 
04010 #ifdef FIX_QT_BROKEN_QWIDGET_SCROLL
04011     if (hideScrollBars) {
04012         horizontalScrollBar()->parentWidget()->lower();
04013         verticalScrollBar()->parentWidget()->lower();
04014     }
04015 #endif
04016 
04017     if (m_kwp->isRedirected()) {
04018         w->scroll(dx, dy, QRect(off.x(), off.y(), visibleWidth(), visibleHeight()));
04019     }  else {
04020         widget()->scroll(dx, dy, widget()->rect() & viewport()->rect());
04021     }
04022 
04023 #ifdef FIX_QT_BROKEN_QWIDGET_SCROLL
04024     if (hideScrollBars) {
04025         horizontalScrollBar()->parentWidget()->raise();
04026         verticalScrollBar()->parentWidget()->raise();
04027     }
04028 #endif
04029 
04030     d->scrollExternalWidgets(dx, dy);
04031 }
04032 
04033 void KHTMLView::setupSmoothScrolling(int dx, int dy)
04034 {
04035     // full scroll is remaining scroll plus new scroll
04036     d->dx = d->dx + dx;
04037     d->dy = d->dy + dy;
04038 
04039     if (d->dx == 0 && d->dy == 0) return;
04040 
04041     int steps = sSmoothScrollTime/sSmoothScrollTick;
04042 
04043     // average step size (stored in 1/16 px/step)
04044     d->ddx = (d->dx*16)/(steps+1);
04045     d->ddy = (d->dy*16)/(steps+1);
04046 
04047     if (abs(d->ddx) < 64 && abs(d->ddy) < 64) {
04048     // Don't move slower than average 4px/step in minimum one direction
04049     if (d->ddx > 0) d->ddx = qMax(d->ddx, 64);
04050     if (d->ddy > 0) d->ddy = qMax(d->ddy, 64);
04051     if (d->ddx < 0) d->ddx = qMin(d->ddx, -64);
04052     if (d->ddy < 0) d->ddy = qMin(d->ddy, -64);
04053     // This means fewer than normal steps
04054     steps = qMax(d->ddx ? (d->dx*16)/d->ddx : 0, d->ddy ? (d->dy*16)/d->ddy : 0);
04055     if (steps < 1) steps = 1;
04056     d->ddx = (d->dx*16)/(steps+1);
04057     d->ddy = (d->dy*16)/(steps+1);
04058     }
04059 
04060     // step size starts at double average speed and ends at 0
04061     d->ddx *= 2;
04062     d->ddy *= 2;
04063 
04064     // deacceleration speed
04065     d->dddx = (d->ddx+1)/steps;
04066     d->dddy = (d->ddy+1)/steps;
04067 
04068     if (!d->smoothScrolling) {
04069         d->startScrolling();
04070         scrollTick();
04071     }
04072 }
04073 
04074 void KHTMLView::scrollTick() {
04075     if (d->dx == 0 && d->dy == 0) {
04076         d->stopScrolling();
04077         return;
04078     }
04079 
04080     // step size + remaining partial step
04081     int tddx = d->ddx + d->rdx;
04082     int tddy = d->ddy + d->rdy;
04083 
04084     // don't go under 1px/step
04085     if (tddx > 0 && tddx < 16) tddx = 16;
04086     if (tddy > 0 && tddy < 16) tddy = 16;
04087     if (tddx < 0 && tddx > -16) tddx = -16;
04088     if (tddy < 0 && tddy > -16) tddy = -16;
04089 
04090     // full pixel steps to scroll in this step
04091     int ddx = tddx / 16;
04092     int ddy = tddy / 16;
04093     // remaining partial step (this is especially needed for 1.x sized steps)
04094     d->rdx = tddx % 16;
04095     d->rdy = tddy % 16;
04096 
04097     // limit step to requested scrolling distance
04098     if (abs(ddx) > abs(d->dx)) ddx = d->dx;
04099     if (abs(ddy) > abs(d->dy)) ddy = d->dy;
04100 
04101     // Don't stop if deaccelerated too fast
04102     if (!ddx) ddx = d->dx;
04103     if (!ddy) ddy = d->dy;
04104 
04105     // update remaining scroll
04106     d->dx -= ddx;
04107     d->dy -= ddy;
04108 
04109     d->shouldSmoothScroll = false;
04110     scrollContentsBy(ddx, ddy);
04111 
04112     // update scrolling speed
04113     int dddx = d->dddx;
04114     int dddy = d->dddy;
04115     // don't change direction
04116     if (abs(dddx) > abs(d->ddx)) dddx = d->ddx;
04117     if (abs(dddy) > abs(d->ddy)) dddy = d->ddy;
04118 
04119     d->ddx -= dddx;
04120     d->ddy -= dddy;
04121 }
04122 
04123 
04124 void KHTMLView::addChild(QWidget * child, int x, int y)
04125 {
04126     if (!child)
04127         return;
04128 
04129     if (child->parent() != widget())
04130         child->setParent( widget() );
04131 
04132     // ### handle pseudo-zooming of non-redirected widgets (e.g. just resize'em)
04133 
04134     child->move(x-contentsX(), y-contentsY());
04135 }
04136 
04137 void KHTMLView::timerEvent ( QTimerEvent *e )
04138 {
04139 //    kDebug() << "timer event " << e->timerId();
04140     if ( e->timerId() == d->scrollTimerId ) {
04141         if( d->scrollSuspended )
04142             return;
04143         switch (d->scrollDirection) {
04144             case KHTMLViewPrivate::ScrollDown:
04145                 if (contentsY() + visibleHeight () >= contentsHeight())
04146                     d->newScrollTimer(this, 0);
04147                 else
04148                     verticalScrollBar()->setValue( verticalScrollBar()->value() +d->scrollBy );
04149                 break;
04150             case KHTMLViewPrivate::ScrollUp:
04151                 if (contentsY() <= 0)
04152                     d->newScrollTimer(this, 0);
04153                 else
04154                     verticalScrollBar()->setValue( verticalScrollBar()->value() -d->scrollBy );
04155                 break;
04156             case KHTMLViewPrivate::ScrollRight:
04157                 if (contentsX() + visibleWidth () >= contentsWidth())
04158                     d->newScrollTimer(this, 0);
04159                 else
04160                     horizontalScrollBar()->setValue( horizontalScrollBar()->value() +d->scrollBy );
04161                 break;
04162             case KHTMLViewPrivate::ScrollLeft:
04163                 if (contentsX() <= 0)
04164                     d->newScrollTimer(this, 0);
04165                 else
04166                     horizontalScrollBar()->setValue( horizontalScrollBar()->value() -d->scrollBy );
04167                 break;
04168         }
04169         return;
04170     }
04171     else if ( e->timerId() == d->scrollingFromWheelTimerId ) {
04172         killTimer( d->scrollingFromWheelTimerId );
04173         d->scrollingFromWheelTimerId = 0;
04174     } else if ( e->timerId() == d->layoutTimerId ) {
04175         if (d->firstLayoutPending && d->layoutAttemptCounter < 4
04176                            && (!m_part->xmlDocImpl() || !m_part->xmlDocImpl()->readyForLayout())) {
04177             d->layoutAttemptCounter++;
04178             killTimer(d->layoutTimerId);
04179             d->layoutTimerId = 0;
04180             scheduleRelayout();
04181             return;
04182         }
04183         layout();
04184         d->scheduledLayoutCounter++;
04185         if (d->firstLayoutPending) {
04186             d->firstLayoutPending = false;
04187             verticalScrollBar()->setEnabled( true );
04188             horizontalScrollBar()->setEnabled( true );
04189         }
04190     }
04191 
04192     d->contentsMoving = false;
04193     if( m_part->xmlDocImpl() ) {
04194     DOM::DocumentImpl *document = m_part->xmlDocImpl();
04195     khtml::RenderCanvas* root = static_cast<khtml::RenderCanvas *>(document->renderer());
04196 
04197     if ( root && root->needsLayout() ) {
04198         if (d->repaintTimerId)
04199             killTimer(d->repaintTimerId);
04200         d->repaintTimerId = 0;
04201         scheduleRelayout();
04202         return;
04203     }
04204     }
04205 
04206     if (d->repaintTimerId)
04207         killTimer(d->repaintTimerId);
04208     d->repaintTimerId = 0;
04209 
04210     QRect updateRegion;
04211     const QVector<QRect> rects = d->updateRegion.rects();
04212 
04213     d->updateRegion = QRegion();
04214 
04215     if ( rects.size() )
04216         updateRegion = rects[0];
04217 
04218     for ( int i = 1; i < rects.size(); ++i ) {
04219         QRect newRegion = updateRegion.unite(rects[i]);
04220         if (2*newRegion.height() > 3*updateRegion.height() )
04221         {
04222             repaintContents( updateRegion );
04223             updateRegion = rects[i];
04224         }
04225         else
04226             updateRegion = newRegion;
04227     }
04228 
04229     if ( !updateRegion.isNull() )
04230         repaintContents( updateRegion );
04231 
04232     // As widgets can only be accurately positioned during painting, every layout might
04233     // dissociate a widget from its RenderWidget. E.g: if a RenderWidget was visible before layout, but the layout
04234     // pushed it out of the viewport, it will not be repainted, and consequently it's associated widget won't be repositioned.
04235     // 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.
04236 
04237     if (d->dirtyLayout && !d->visibleWidgets.isEmpty())
04238         checkExternalWidgetsPosition();
04239 
04240     d->dirtyLayout = false;
04241 
04242     emit repaintAccessKeys();
04243     if (d->emitCompletedAfterRepaint) {
04244         bool full = d->emitCompletedAfterRepaint == KHTMLViewPrivate::CSFull;
04245         d->emitCompletedAfterRepaint = KHTMLViewPrivate::CSNone;
04246         if ( full )
04247             emit m_part->completed();
04248         else
04249             emit m_part->completed(true);
04250     }
04251 }
04252 
04253 void KHTMLView::checkExternalWidgetsPosition()
04254 {
04255     QWidget* w;
04256     QRect visibleRect(contentsX(), contentsY(), visibleWidth(), visibleHeight());
04257     QList<RenderWidget*> toRemove;
04258     QHashIterator<void*, QWidget*> it(d->visibleWidgets);
04259     while (it.hasNext()) {
04260         int xp = 0, yp = 0;
04261         it.next();
04262         RenderWidget* rw = static_cast<RenderWidget*>( it.key() );
04263         if (!rw->absolutePosition(xp, yp) ||
04264             !visibleRect.intersects(QRect(xp, yp, it.value()->width(), it.value()->height())))
04265             toRemove.append(rw);
04266     }
04267     foreach (RenderWidget* r, toRemove)
04268         if ( (w = d->visibleWidgets.take(r) ) )
04269             w->move( 0, -500000);
04270 }
04271 
04272 void KHTMLView::scheduleRelayout(khtml::RenderObject * /*clippedObj*/)
04273 {
04274     if (!d->layoutSchedulingEnabled || d->layoutTimerId)
04275         return;
04276 
04277     int time = 0;
04278     if (d->firstLayoutPending) {
04279         // Any repaint happening while we have no content blanks the viewport ("white flash").
04280         // Hence the need to delay the first layout as much as we can.
04281         // Only if the document gets stuck for too long in incomplete state will we allow the blanking.
04282         time = d->layoutAttemptCounter ?
04283                sLayoutAttemptDelay + sLayoutAttemptIncrement*d->layoutAttemptCounter : sFirstLayoutDelay;
04284     } else if (m_part->xmlDocImpl() && m_part->xmlDocImpl()->parsing()) {
04285         // Delay between successive layouts in parsing mode.
04286         // Increment reflects the decaying importance of visual feedback over time.
04287         time = qMin(2000, sParsingLayoutsInterval + d->scheduledLayoutCounter*sParsingLayoutsIncrement);
04288     }
04289     d->layoutTimerId = startTimer( time );
04290 }
04291 
04292 void KHTMLView::unscheduleRelayout()
04293 {
04294     if (!d->layoutTimerId)
04295         return;
04296 
04297     killTimer(d->layoutTimerId);
04298     d->layoutTimerId = 0;
04299 }
04300 
04301 void KHTMLView::unscheduleRepaint()
04302 {
04303     if (!d->repaintTimerId)
04304         return;
04305 
04306     killTimer(d->repaintTimerId);
04307     d->repaintTimerId = 0;
04308 }
04309 
04310 void KHTMLView::scheduleRepaint(int x, int y, int w, int h, bool asap)
04311 {
04312     bool parsing = !m_part->xmlDocImpl() || m_part->xmlDocImpl()->parsing();
04313 
04314 //     kDebug() << "parsing " << parsing;
04315 //     kDebug() << "complete " << d->complete;
04316 
04317     int time = parsing && !d->firstLayoutPending ? 150 : (!asap ? ( !d->complete ? 80 : 20 ) : 0);
04318 
04319 #ifdef DEBUG_FLICKER
04320     QPainter p;
04321     p.begin( viewport() );
04322 
04323     int vx, vy;
04324     contentsToViewport( x, y, vx, vy );
04325     p.fillRect( vx, vy, w, h, Qt::red );
04326     p.end();
04327 #endif
04328 
04329     d->updateRegion = d->updateRegion.unite(QRect(x,y,w,h));
04330 
04331     if (asap && !parsing)
04332         unscheduleRepaint();
04333 
04334     if ( !d->repaintTimerId )
04335         d->repaintTimerId = startTimer( time );
04336 
04337 //     kDebug() << "starting timer " << time;
04338 }
04339 
04340 void KHTMLView::complete( bool pendingAction )
04341 {
04342 //     kDebug() << "KHTMLView::complete()";
04343 
04344     d->complete = true;
04345 
04346     // is there a relayout pending?
04347     if (d->layoutTimerId)
04348     {
04349 //         kDebug() << "requesting relayout now";
04350         // do it now
04351         killTimer(d->layoutTimerId);
04352         d->layoutTimerId = startTimer( 0 );
04353         d->emitCompletedAfterRepaint = pendingAction ?
04354             KHTMLViewPrivate::CSActionPending : KHTMLViewPrivate::CSFull;
04355     }
04356 
04357     // is there a repaint pending?
04358     if (d->repaintTimerId)
04359     {
04360 //         kDebug() << "requesting repaint now";
04361         // do it now
04362         killTimer(d->repaintTimerId);
04363         d->repaintTimerId = startTimer( 0 );
04364         d->emitCompletedAfterRepaint = pendingAction ?
04365             KHTMLViewPrivate::CSActionPending : KHTMLViewPrivate::CSFull;
04366     }
04367 
04368     if (!d->emitCompletedAfterRepaint)
04369     {
04370         if (!pendingAction)
04371         emit m_part->completed();
04372         else
04373             emit m_part->completed(true);
04374     }
04375 
04376 }
04377 
04378 void KHTMLView::updateScrollBars()
04379 {
04380     const QWidget *view = widget();
04381     if (!view)
04382         return;
04383 
04384     QSize p = viewport()->size();
04385     QSize m = maximumViewportSize();
04386 
04387     if (m.expandedTo(view->size()) == m)
04388         p = m; // no scroll bars needed
04389 
04390     QSize v = view->size();
04391     horizontalScrollBar()->setRange(0, v.width() - p.width());
04392     horizontalScrollBar()->setPageStep(p.width());
04393     verticalScrollBar()->setRange(0, v.height() - p.height());
04394     verticalScrollBar()->setPageStep(p.height());
04395     if (!d->smoothScrolling) {
04396         d->updateContentsXY();
04397     }
04398 }
04399 
04400 void KHTMLView::slotMouseScrollTimer()
04401 {
04402      horizontalScrollBar()->setValue( horizontalScrollBar()->value() +d->m_mouseScroll_byX );
04403      verticalScrollBar()->setValue( verticalScrollBar()->value() +d->m_mouseScroll_byY);
04404 }
04405 
04406 
04407 static DOM::Position positionOfLineBoundary(const DOM::Position &pos, bool toEnd)
04408 {
04409     Selection sel = pos;
04410     sel.expandUsingGranularity(Selection::LINE);
04411     return toEnd ? sel.end() : sel.start();
04412 }
04413 
04414 inline static DOM::Position positionOfLineBegin(const DOM::Position &pos)
04415 {
04416     return positionOfLineBoundary(pos, false);
04417 }
04418 
04419 inline static DOM::Position positionOfLineEnd(const DOM::Position &pos)
04420 {
04421     return positionOfLineBoundary(pos, true);
04422 }
04423 
04424 bool KHTMLView::caretKeyPressEvent(QKeyEvent *_ke)
04425 {
04426   EditorContext *ec = &m_part->d->editor_context;
04427   Selection &caret = ec->m_selection;
04428   Position old_pos = caret.caretPos();
04429   Position pos = old_pos;
04430   bool recalcXPos = true;
04431   bool handled = true;
04432 
04433   bool ctrl = _ke->modifiers() & Qt::ControlModifier;
04434   bool shift = _ke->modifiers() & Qt::ShiftModifier;
04435 
04436   switch(_ke->key()) {
04437 
04438     // -- Navigational keys
04439     case Qt::Key_Down:
04440       pos = old_pos.nextLinePosition(caret.xPosForVerticalArrowNavigation(Selection::EXTENT));
04441       recalcXPos = false;
04442       break;
04443 
04444     case Qt::Key_Up:
04445       pos = old_pos.previousLinePosition(caret.xPosForVerticalArrowNavigation(Selection::EXTENT));
04446       recalcXPos = false;
04447       break;
04448 
04449     case Qt::Key_Left:
04450       pos = ctrl ? old_pos.previousWordPosition() : old_pos.previousCharacterPosition();
04451       break;
04452 
04453     case Qt::Key_Right:
04454       pos = ctrl ? old_pos.nextWordPosition() : old_pos.nextCharacterPosition();
04455       break;
04456 
04457     case Qt::Key_PageDown:
04458 //       moveCaretNextPage(); ###
04459       break;
04460 
04461     case Qt::Key_PageUp:
04462 //       moveCaretPrevPage(); ###
04463       break;
04464 
04465     case Qt::Key_Home:
04466       if (ctrl)
04467         /*moveCaretToDocumentBoundary(false)*/; // ###
04468       else
04469         pos = positionOfLineBegin(old_pos);
04470       break;
04471 
04472     case Qt::Key_End:
04473       if (ctrl)
04474         /*moveCaretToDocumentBoundary(true)*/; // ###
04475       else
04476         pos = positionOfLineEnd(old_pos);
04477       break;
04478 
04479     default:
04480       handled = false;
04481 
04482   }/*end switch*/
04483 
04484   if (pos != old_pos) {
04485     m_part->clearCaretRectIfNeeded();
04486 
04487     caret.moveTo(shift ? caret.nonCaretPos() : pos, pos);
04488     int old_x = caret.xPosForVerticalArrowNavigation(Selection::CARETPOS);
04489 
04490     m_part->selectionLayoutChanged();
04491 
04492     // restore old x-position to prevent recalculation
04493     if (!recalcXPos)
04494       m_part->d->editor_context.m_xPosForVerticalArrowNavigation = old_x;
04495 
04496     m_part->emitCaretPositionChanged(pos);
04497     // ### check when to emit it
04498     m_part->notifySelectionChanged();
04499 
04500   }
04501 
04502   if (handled) _ke->accept();
04503   return handled;
04504 }
04505 
04506 #undef DEBUG_CARETMODE

KHTML

Skip menu "KHTML"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • KIO
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.6
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal