khtmlview.cpp

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