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