khtml Library API Documentation

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 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., 59 Temple Place - Suite 330,
00023  * Boston, MA 02111-1307, 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 "misc/htmlhashes.h"
00051 #include "misc/helper.h"
00052 #include "khtml_settings.h"
00053 #include "khtml_printsettings.h"
00054 
00055 #include "khtmlpart_p.h"
00056 
00057 #ifndef KHTML_NO_CARET
00058 #include "khtml_caret_p.h"
00059 #include "xml/dom2_rangeimpl.h"
00060 #endif
00061 
00062 #include <kcursor.h>
00063 #include <knotifyclient.h>
00064 #include <ksimpleconfig.h>
00065 #include <kstringhandler.h>
00066 #include <kstandarddirs.h>
00067 #include <kprinter.h>
00068 #include <klocale.h>
00069 #include <kstdaccel.h>
00070 
00071 #include <qtooltip.h>
00072 #include <qpainter.h>
00073 #include <qlabel.h>
00074 #include <qpaintdevicemetrics.h>
00075 #include <qstylesheet.h>
00076 #include <kapplication.h>
00077 
00078 #include <kimageio.h>
00079 #include <kdebug.h>
00080 #include <kurldrag.h>
00081 #include <qobjectlist.h>
00082 #include <qtimer.h>
00083 #include <kdialogbase.h>
00084 #include <qptrdict.h>
00085 
00086 
00087 //#define DEBUG_NO_PAINT_BUFFER
00088 
00089 //#define DEBUG_FLICKER
00090 
00091 //#define DEBUG_PIXEL
00092 
00093 #define PAINT_BUFFER_HEIGHT 128
00094 
00095 #if 0
00096 namespace khtml {
00097     void dumpLineBoxes(RenderFlow *flow);
00098 }
00099 #endif
00100 
00101 using namespace DOM;
00102 using namespace khtml;
00103 class KHTMLToolTip;
00104 
00105 
00106 #ifndef QT_NO_TOOLTIP
00107 
00108 class KHTMLToolTip : public QToolTip
00109 {
00110 public:
00111     KHTMLToolTip(KHTMLView *view,  KHTMLViewPrivate* vp) : QToolTip(view->viewport())
00112     {
00113         m_view = view;
00114         m_viewprivate = vp;
00115     };
00116 
00117 protected:
00118     virtual void maybeTip(const QPoint &);
00119 
00120 private:
00121     KHTMLView *m_view;
00122     KHTMLViewPrivate* m_viewprivate;
00123 };
00124 
00125 #endif
00126 
00127 class KHTMLViewPrivate {
00128     friend class KHTMLToolTip;
00129 public:
00130 
00131     enum PseudoFocusNodes {
00132     PFNone,
00133     PFTop,
00134     PFBottom
00135     };
00136 
00137     enum CompletedState {
00138         CSNone = 0,
00139         CSFull,
00140         CSActionPending
00141     };
00142 
00143     KHTMLViewPrivate()
00144         : underMouse( 0 ), underMouseNonShared( 0 )
00145     {
00146 #ifndef KHTML_NO_CARET
00147     m_caretViewContext = 0;
00148     m_editorContext = 0;
00149 #endif // KHTML_NO_CARET
00150         postponed_autorepeat = NULL;
00151         reset();
00152         tp=0;
00153         paintBuffer=0;
00154         vertPaintBuffer=0;
00155         formCompletions=0;
00156         prevScrollbarVisible = true;
00157     tooltip = 0;
00158         possibleTripleClick = false;
00159         emitCompletedAfterRepaint = CSNone;
00160     }
00161     ~KHTMLViewPrivate()
00162     {
00163         delete formCompletions;
00164         delete tp; tp = 0;
00165         delete paintBuffer; paintBuffer =0;
00166         delete vertPaintBuffer;
00167         delete postponed_autorepeat;
00168         if (underMouse)
00169         underMouse->deref();
00170         if (underMouseNonShared)
00171         underMouseNonShared->deref();
00172     delete tooltip;
00173 #ifndef KHTML_NO_CARET
00174     delete m_caretViewContext;
00175     delete m_editorContext;
00176 #endif // KHTML_NO_CARET
00177     }
00178     void reset()
00179     {
00180         if (underMouse)
00181         underMouse->deref();
00182     underMouse = 0;
00183         if (underMouseNonShared)
00184         underMouseNonShared->deref();
00185     underMouseNonShared = 0;
00186         linkPressed = false;
00187         useSlowRepaints = false;
00188     tabMovePending = false;
00189     lastTabbingDirection = true;
00190     pseudoFocusNode = PFNone;
00191 #ifndef KHTML_NO_SCROLLBARS
00192         vmode = QScrollView::Auto;
00193         hmode = QScrollView::Auto;
00194 #else
00195         vmode = QScrollView::AlwaysOff;
00196         hmode = QScrollView::AlwaysOff;
00197 #endif
00198 #ifdef DEBUG_PIXEL
00199         timer.start();
00200         pixelbooth = 0;
00201         repaintbooth = 0;
00202 #endif
00203         scrollBarMoved = false;
00204         contentsMoving = false;
00205         ignoreWheelEvents = false;
00206     borderX = 30;
00207     borderY = 30;
00208     clickX = -1;
00209     clickY = -1;
00210         prevMouseX = -1;
00211         prevMouseY = -1;
00212     clickCount = 0;
00213     isDoubleClick = false;
00214     scrollingSelf = false;
00215         delete postponed_autorepeat;
00216         postponed_autorepeat = NULL;
00217     layoutTimerId = 0;
00218         repaintTimerId = 0;
00219         scrollTimerId = 0;
00220         scrollSuspended = false;
00221         scrollSuspendPreActivate = false;
00222         complete = false;
00223         firstRelayout = true;
00224         dirtyLayout = false;
00225         layoutSchedulingEnabled = true;
00226         painting = false;
00227         updateRegion = QRegion();
00228         m_dialogsAllowed = true;
00229 #ifndef KHTML_NO_CARET
00230         if (m_caretViewContext) {
00231           m_caretViewContext->caretMoved = false;
00232       m_caretViewContext->keyReleasePending = false;
00233         }/*end if*/
00234 #endif // KHTML_NO_CARET
00235 #ifndef KHTML_NO_TYPE_AHEAD_FIND
00236         typeAheadActivated = false;
00237 #endif // KHTML_NO_TYPE_AHEAD_FIND
00238     accessKeysActivated = false;
00239     accessKeysPreActivate = false;
00240         emitCompletedAfterRepaint = CSNone;
00241     }
00242     void newScrollTimer(QWidget *view, int tid)
00243     {
00244         //kdDebug(6000) << "newScrollTimer timer " << tid << endl;
00245         view->killTimer(scrollTimerId);
00246         scrollTimerId = tid;
00247         scrollSuspended = false;
00248     }
00249     enum ScrollDirection { ScrollLeft, ScrollRight, ScrollUp, ScrollDown };
00250 
00251     void adjustScroller(QWidget *view, ScrollDirection direction, ScrollDirection oppositedir)
00252     {
00253         static const struct { int msec, pixels; } timings [] = {
00254             {320,1}, {224,1}, {160,1}, {112,1}, {80,1}, {56,1}, {40,1},
00255             {28,1}, {20,1}, {20,2}, {20,3}, {20,4}, {20,6}, {20,8}, {0,0}
00256         };
00257         if (!scrollTimerId ||
00258             (scrollDirection != direction &&
00259              (scrollDirection != oppositedir || scrollSuspended))) {
00260             scrollTiming = 6;
00261             scrollBy = timings[scrollTiming].pixels;
00262             scrollDirection = direction;
00263             newScrollTimer(view, view->startTimer(timings[scrollTiming].msec));
00264         } else if (scrollDirection == direction &&
00265                    timings[scrollTiming+1].msec && !scrollSuspended) {
00266             scrollBy = timings[++scrollTiming].pixels;
00267             newScrollTimer(view, view->startTimer(timings[scrollTiming].msec));
00268         } else if (scrollDirection == oppositedir) {
00269             if (scrollTiming) {
00270                 scrollBy = timings[--scrollTiming].pixels;
00271                 newScrollTimer(view, view->startTimer(timings[scrollTiming].msec));
00272             }
00273         }
00274         scrollSuspended = false;
00275     }
00276 
00277 #ifndef KHTML_NO_CARET
00278 
00281     CaretViewContext *caretViewContext() {
00282       if (!m_caretViewContext) m_caretViewContext = new CaretViewContext();
00283       return m_caretViewContext;
00284     }
00288     EditorContext *editorContext() {
00289       if (!m_editorContext) m_editorContext = new EditorContext();
00290       return m_editorContext;
00291     }
00292 #endif // KHTML_NO_CARET
00293 
00294 #ifdef DEBUG_PIXEL
00295     QTime timer;
00296     unsigned int pixelbooth;
00297     unsigned int repaintbooth;
00298 #endif
00299 
00300     QPainter *tp;
00301     QPixmap  *paintBuffer;
00302     QPixmap  *vertPaintBuffer;
00303     NodeImpl *underMouse;
00304     NodeImpl *underMouseNonShared;
00305 
00306     bool tabMovePending:1;
00307     bool lastTabbingDirection:1;
00308     PseudoFocusNodes pseudoFocusNode:2;
00309     bool scrollBarMoved:1;
00310     bool contentsMoving:1;
00311 
00312     QScrollView::ScrollBarMode vmode;
00313     QScrollView::ScrollBarMode hmode;
00314     bool prevScrollbarVisible;
00315     bool linkPressed;
00316     bool useSlowRepaints;
00317     bool ignoreWheelEvents;
00318 
00319     int borderX, borderY;
00320     KSimpleConfig *formCompletions;
00321 
00322     int clickX, clickY, clickCount;
00323     bool isDoubleClick;
00324 
00325     int prevMouseX, prevMouseY;
00326     bool scrollingSelf;
00327     int layoutTimerId;
00328     QKeyEvent* postponed_autorepeat;
00329 
00330     int repaintTimerId;
00331     int scrollTimerId;
00332     int scrollTiming;
00333     int scrollBy;
00334     ScrollDirection scrollDirection;
00335     bool scrollSuspended;
00336     bool scrollSuspendPreActivate;
00337     bool complete;
00338     bool firstRelayout;
00339     bool layoutSchedulingEnabled;
00340     bool painting;
00341     bool possibleTripleClick;
00342     bool dirtyLayout;
00343     bool m_dialogsAllowed;
00344     QRegion updateRegion;
00345     KHTMLToolTip *tooltip;
00346     QPtrDict<QWidget> visibleWidgets;
00347 #ifndef KHTML_NO_CARET
00348     CaretViewContext *m_caretViewContext;
00349     EditorContext *m_editorContext;
00350 #endif // KHTML_NO_CARET
00351 #ifndef KHTML_NO_TYPE_AHEAD_FIND
00352     QString findString;
00353     QTimer timer;
00354     bool findLinksOnly;
00355     bool typeAheadActivated;
00356 #endif // KHTML_NO_TYPE_AHEAD_FIND
00357     bool accessKeysActivated;
00358     bool accessKeysPreActivate;
00359     CompletedState emitCompletedAfterRepaint;
00360 };
00361 
00362 #ifndef QT_NO_TOOLTIP
00363 
00373 static bool findImageMapRect(HTMLImageElementImpl *img, const QPoint &scrollOfs,
00374             const QPoint &p, QRect &r, QString &s)
00375 {
00376     HTMLMapElementImpl* map;
00377     if (img && img->getDocument()->isHTMLDocument() &&
00378         (map = static_cast<HTMLDocumentImpl*>(img->getDocument())->getMap(img->imageMap()))) {
00379         RenderObject::NodeInfo info(true, false);
00380         RenderObject *rend = img->renderer();
00381         int ax, ay;
00382         if (!rend || !rend->absolutePosition(ax, ay))
00383             return false;
00384         // we're a client side image map
00385         bool inside = map->mapMouseEvent(p.x() - ax + scrollOfs.x(),
00386                 p.y() - ay + scrollOfs.y(), rend->contentWidth(),
00387                 rend->contentHeight(), info);
00388         if (inside && info.URLElement()) {
00389             HTMLAreaElementImpl *area = static_cast<HTMLAreaElementImpl *>(info.URLElement());
00390             Q_ASSERT(area->id() == ID_AREA);
00391             s = area->getAttribute(ATTR_TITLE).string();
00392             QRegion reg = area->cachedRegion();
00393             if (!s.isEmpty() && !reg.isEmpty()) {
00394                 r = reg.boundingRect();
00395                 r.moveBy(ax, ay);
00396                 return true;
00397             }
00398         }
00399     }
00400     return false;
00401 }
00402 
00403 void KHTMLToolTip::maybeTip(const QPoint& p)
00404 {
00405     DOM::NodeImpl *node = m_viewprivate->underMouseNonShared;
00406     QRect region;
00407     while ( node ) {
00408         if ( node->isElementNode() ) {
00409             DOM::ElementImpl *e = static_cast<DOM::ElementImpl*>( node );
00410             QRect r;
00411             QString s;
00412             bool found = false;
00413             // for images, check if it is part of a client-side image map,
00414             // and query the <area>s' title attributes, too
00415             if (e->id() == ID_IMG && !e->getAttribute( ATTR_USEMAP ).isEmpty()) {
00416                 found = findImageMapRect(static_cast<HTMLImageElementImpl *>(e),
00417                             m_view->viewportToContents(QPoint(0, 0)), p, r, s);
00418             }
00419             if (!found) {
00420                 s = e->getAttribute( ATTR_TITLE ).string();
00421                 r = node->getRect();
00422             }
00423             region |= QRect( m_view->contentsToViewport( r.topLeft() ), r.size() );
00424             if ( !s.isEmpty() ) {
00425                 tip( region, QStyleSheet::convertFromPlainText( s, QStyleSheetItem::WhiteSpaceNormal ) );
00426                 break;
00427             }
00428         }
00429         node = node->parentNode();
00430     }
00431 }
00432 #endif
00433 
00434 KHTMLView::KHTMLView( KHTMLPart *part, QWidget *parent, const char *name)
00435     : QScrollView( parent, name, WResizeNoErase | WRepaintNoErase )
00436 {
00437     m_medium = "screen";
00438 
00439     m_part = part;
00440     d = new KHTMLViewPrivate;
00441     QScrollView::setVScrollBarMode(d->vmode);
00442     QScrollView::setHScrollBarMode(d->hmode);
00443     connect(kapp, SIGNAL(kdisplayPaletteChanged()), this, SLOT(slotPaletteChanged()));
00444     connect(this, SIGNAL(contentsMoving(int, int)), this, SLOT(slotScrollBarMoved()));
00445 
00446     // initialize QScrollView
00447     enableClipper(true);
00448     // hack to get unclipped painting on the viewport.
00449     static_cast<KHTMLView *>(static_cast<QWidget *>(viewport()))->setWFlags(WPaintUnclipped);
00450 
00451     setResizePolicy(Manual);
00452     viewport()->setMouseTracking(true);
00453     viewport()->setBackgroundMode(NoBackground);
00454 
00455     KImageIO::registerFormats();
00456 
00457 #ifndef QT_NO_TOOLTIP
00458     d->tooltip = new KHTMLToolTip( this, d );
00459 #endif
00460 
00461 #ifndef KHTML_NO_TYPE_AHEAD_FIND
00462     connect(&d->timer, SIGNAL(timeout()), this, SLOT(findTimeout()));
00463 #endif // KHTML_NO_TYPE_AHEAD_FIND
00464 
00465     init();
00466 
00467     viewport()->show();
00468 }
00469 
00470 KHTMLView::~KHTMLView()
00471 {
00472     closeChildDialogs();
00473     if (m_part)
00474     {
00475         //WABA: Is this Ok? Do I need to deref it as well?
00476         //Does this need to be done somewhere else?
00477         DOM::DocumentImpl *doc = m_part->xmlDocImpl();
00478         if (doc)
00479             doc->detach();
00480     }
00481     delete d; d = 0;
00482 }
00483 
00484 void KHTMLView::init()
00485 {
00486     if(!d->paintBuffer) d->paintBuffer = new QPixmap(PAINT_BUFFER_HEIGHT, PAINT_BUFFER_HEIGHT);
00487     if(!d->vertPaintBuffer)
00488         d->vertPaintBuffer = new QPixmap(10, PAINT_BUFFER_HEIGHT);
00489     if(!d->tp) d->tp = new QPainter();
00490 
00491     setFocusPolicy(QWidget::StrongFocus);
00492     viewport()->setFocusProxy(this);
00493 
00494     _marginWidth = -1; // undefined
00495     _marginHeight = -1;
00496     _width = 0;
00497     _height = 0;
00498 
00499     installEventFilter(this);
00500 
00501     setAcceptDrops(true);
00502     QSize s = viewportSize(4095, 4095);
00503     resizeContents(s.width(), s.height());
00504 }
00505 
00506 void KHTMLView::clear()
00507 {
00508     // work around QScrollview's unbelievable bugginess
00509     setStaticBackground(true);
00510 #ifndef KHTML_NO_CARET
00511     if (!m_part->isCaretMode() && !m_part->isEditable()) caretOff();
00512 #endif
00513 
00514     if( d->typeAheadActivated )
00515         findTimeout();
00516     if (d->accessKeysActivated)
00517         accessKeysTimeout();
00518     d->reset();
00519     killTimers();
00520     emit cleared();
00521 
00522     QScrollView::setHScrollBarMode(d->hmode);
00523     QScrollView::setVScrollBarMode(d->vmode);
00524 }
00525 
00526 void KHTMLView::hideEvent(QHideEvent* e)
00527 {
00528     QScrollView::hideEvent(e);
00529 }
00530 
00531 void KHTMLView::showEvent(QShowEvent* e)
00532 {
00533     QScrollView::showEvent(e);
00534 }
00535 
00536 void KHTMLView::resizeEvent (QResizeEvent* e)
00537 {
00538     int dw = e->oldSize().width() - e->size().width();
00539     int dh = e->oldSize().height() - e->size().height();
00540 
00541     // if we are shrinking the view, don't allow the content to overflow
00542     // before the layout occurs - we don't know if we need scrollbars yet
00543     dw = dw>0 ? kMax(0, contentsWidth()-dw) : contentsWidth();
00544     dh = dh>0 ? kMax(0, contentsHeight()-dh) : contentsHeight();
00545 
00546     resizeContents(dw, dh);
00547 
00548     QScrollView::resizeEvent(e);
00549 
00550     if ( m_part && m_part->xmlDocImpl() )
00551         m_part->xmlDocImpl()->dispatchWindowEvent( EventImpl::RESIZE_EVENT, false, false );
00552 }
00553 
00554 void KHTMLView::viewportResizeEvent (QResizeEvent* e)
00555 {
00556     QScrollView::viewportResizeEvent(e);
00557 
00558     //int w = visibleWidth();
00559     //int h = visibleHeight();
00560 
00561     if (d->layoutSchedulingEnabled)
00562         layout();
00563 #ifndef KHTML_NO_CARET
00564     else {
00565         hideCaret();
00566         recalcAndStoreCaretPos();
00567     showCaret();
00568     }/*end if*/
00569 #endif
00570 
00571     KApplication::sendPostedEvents(viewport(), QEvent::Paint);
00572 }
00573 
00574 // this is to get rid of a compiler virtual overload mismatch warning. do not remove
00575 void KHTMLView::drawContents( QPainter*)
00576 {
00577 }
00578 
00579 void KHTMLView::drawContents( QPainter *p, int ex, int ey, int ew, int eh )
00580 {
00581 #ifdef DEBUG_PIXEL
00582 
00583     if ( d->timer.elapsed() > 5000 ) {
00584         qDebug( "drawed %d pixels in %d repaints the last %d milliseconds",
00585                 d->pixelbooth, d->repaintbooth,  d->timer.elapsed() );
00586         d->timer.restart();
00587         d->pixelbooth = 0;
00588         d->repaintbooth = 0;
00589     }
00590     d->pixelbooth += ew*eh;
00591     d->repaintbooth++;
00592 #endif
00593 
00594     //kdDebug( 6000 ) << "drawContents this="<< this <<" x=" << ex << ",y=" << ey << ",w=" << ew << ",h=" << eh << endl;
00595     if(!m_part || !m_part->xmlDocImpl() || !m_part->xmlDocImpl()->renderer()) {
00596         p->fillRect(ex, ey, ew, eh, palette().active().brush(QColorGroup::Base));
00597         return;
00598     }
00599 
00600     if (d->painting) {
00601         kdDebug( 6000 ) << "WARNING: drawContents reentered! " << endl;
00602         return;
00603     }
00604     d->painting = true;
00605 
00606     QPoint pt = contentsToViewport(QPoint(ex, ey));
00607     QRegion cr = QRect(pt.x(), pt.y(), ew, eh);
00608 //     kdDebug(6000) << "clip rect: " << QRect(pt.x(), pt.y(), ew, eh) << endl;
00609     for (QPtrDictIterator<QWidget> it(d->visibleWidgets); it.current(); ++it) {
00610     QWidget *w = it.current();
00611     RenderWidget* rw = static_cast<RenderWidget*>( it.currentKey() );
00612         if (strcmp(w->name(), "__khtml")) {
00613             int x, y;
00614             rw->absolutePosition(x, y);
00615             contentsToViewport(x, y, x, y);
00616             cr -= QRect(x, y, rw->width(), rw->height());
00617         }
00618     }
00619 
00620 #if 0
00621     // this is commonly the case with framesets. we still do
00622     // want to paint them, otherwise the widgets don't get placed.
00623     if (cr.isEmpty()) {
00624         d->painting = false;
00625     return;
00626     }
00627 #endif
00628 
00629 #ifndef DEBUG_NO_PAINT_BUFFER
00630     p->setClipRegion(cr);
00631 
00632     if (eh > PAINT_BUFFER_HEIGHT && ew <= 10) {
00633         if ( d->vertPaintBuffer->height() < visibleHeight() )
00634             d->vertPaintBuffer->resize(10, visibleHeight());
00635         d->tp->begin(d->vertPaintBuffer);
00636         d->tp->translate(-ex, -ey);
00637         d->tp->fillRect(ex, ey, ew, eh, palette().active().brush(QColorGroup::Base));
00638         m_part->xmlDocImpl()->renderer()->layer()->paint(d->tp, QRect(ex, ey, ew, eh));
00639         d->tp->end();
00640     p->drawPixmap(ex, ey, *d->vertPaintBuffer, 0, 0, ew, eh);
00641     }
00642     else {
00643         if ( d->paintBuffer->width() < visibleWidth() )
00644             d->paintBuffer->resize(visibleWidth(),PAINT_BUFFER_HEIGHT);
00645 
00646         int py=0;
00647         while (py < eh) {
00648             int ph = eh-py < PAINT_BUFFER_HEIGHT ? eh-py : PAINT_BUFFER_HEIGHT;
00649             d->tp->begin(d->paintBuffer);
00650             d->tp->translate(-ex, -ey-py);
00651             d->tp->fillRect(ex, ey+py, ew, ph, palette().active().brush(QColorGroup::Base));
00652             m_part->xmlDocImpl()->renderer()->layer()->paint(d->tp, QRect(ex, ey+py, ew, ph));
00653             d->tp->end();
00654 
00655         p->drawPixmap(ex, ey+py, *d->paintBuffer, 0, 0, ew, ph);
00656             py += PAINT_BUFFER_HEIGHT;
00657         }
00658     }
00659 #else // !DEBUG_NO_PAINT_BUFFER
00660 static int cnt=0;
00661     ex = contentsX(); ey = contentsY();
00662     ew = visibleWidth(); eh = visibleHeight();
00663     QRect pr(ex,ey,ew,eh);
00664     kdDebug() << "[" << ++cnt << "]" << " clip region: " << pr << endl;
00665 //  p->setClipRegion(QRect(0,0,ew,eh));
00666 //        p->translate(-ex, -ey);
00667         p->fillRect(ex, ey, ew, eh, palette().active().brush(QColorGroup::Base));
00668         m_part->xmlDocImpl()->renderer()->layer()->paint(p, pr);
00669 #endif // DEBUG_NO_PAINT_BUFFER
00670 
00671 #ifndef KHTML_NO_CARET
00672     if (d->m_caretViewContext && d->m_caretViewContext->visible) {
00673         QRect pos(d->m_caretViewContext->x, d->m_caretViewContext->y,
00674         d->m_caretViewContext->width, d->m_caretViewContext->height);
00675         if (pos.intersects(QRect(ex, ey, ew, eh))) {
00676             p->setRasterOp(XorROP);
00677         p->setPen(white);
00678         if (pos.width() == 1)
00679               p->drawLine(pos.topLeft(), pos.bottomRight());
00680         else {
00681           p->fillRect(pos, white);
00682         }/*end if*/
00683     }/*end if*/
00684     }/*end if*/
00685 #endif // KHTML_NO_CARET
00686 
00687 //    p->setPen(QPen(magenta,0,DashDotDotLine));
00688 //    p->drawRect(dbg_paint_rect);
00689 
00690     khtml::DrawContentsEvent event( p, ex, ey, ew, eh );
00691     QApplication::sendEvent( m_part, &event );
00692     
00693     d->painting = false;
00694 }
00695 
00696 void KHTMLView::setMarginWidth(int w)
00697 {
00698     // make it update the rendering area when set
00699     _marginWidth = w;
00700 }
00701 
00702 void KHTMLView::setMarginHeight(int h)
00703 {
00704     // make it update the rendering area when set
00705     _marginHeight = h;
00706 }
00707 
00708 void KHTMLView::layout()
00709 {
00710     if( m_part && m_part->xmlDocImpl() ) {
00711         DOM::DocumentImpl *document = m_part->xmlDocImpl();
00712 
00713         khtml::RenderCanvas* root = static_cast<khtml::RenderCanvas *>(document->renderer());
00714         if ( !root ) return;
00715 
00716         d->layoutSchedulingEnabled=false;
00717 
00718         if (document->isHTMLDocument()) {
00719              NodeImpl *body = static_cast<HTMLDocumentImpl*>(document)->body();
00720              if(body && body->renderer() && body->id() == ID_FRAMESET) {
00721                  QScrollView::setVScrollBarMode(AlwaysOff);
00722                  QScrollView::setHScrollBarMode(AlwaysOff);
00723                  body->renderer()->setNeedsLayout(true);
00724 //                  if (d->tooltip) {
00725 //                      delete d->tooltip;
00726 //                      d->tooltip = 0;
00727 //                  }
00728              }
00729              else if (!d->tooltip)
00730                  d->tooltip = new KHTMLToolTip( this, d );
00731         }
00732 
00733         _height = visibleHeight();
00734         _width = visibleWidth();
00735         //QTime qt;
00736         //qt.start();
00737         root->setNeedsLayoutAndMinMaxRecalc();
00738         root->layout();
00739 
00740         emit finishedLayout();
00741 #if 0
00742     ElementImpl *listitem = m_part->xmlDocImpl()->getElementById("__test_element__");
00743     if (listitem) kdDebug(6000) << "after layout, before repaint" << endl;
00744     if (listitem) dumpLineBoxes(static_cast<RenderFlow *>(listitem->renderer()));
00745 #endif
00746 #ifndef KHTML_NO_CARET
00747         hideCaret();
00748         if ((m_part->isCaretMode() || m_part->isEditable())
00749             && !d->complete && d->m_caretViewContext
00750                 && !d->m_caretViewContext->caretMoved) {
00751             initCaret();
00752         } else {
00753         recalcAndStoreCaretPos();
00754         showCaret();
00755         }/*end if*/
00756 #endif
00757     root->repaint();
00758         if (d->accessKeysActivated) {
00759             emit hideAccessKeys();
00760             displayAccessKeys();
00761         }
00762         //kdDebug( 6000 ) << "TIME: layout() dt=" << qt.elapsed() << endl;
00763     }
00764     else
00765        _width = visibleWidth();
00766 
00767     killTimer(d->layoutTimerId);
00768     d->layoutTimerId = 0;
00769     d->layoutSchedulingEnabled=true;
00770 }
00771 
00772 void KHTMLView::closeChildDialogs()
00773 {
00774     QObjectList *dlgs = queryList("QDialog");
00775     for (QObject *dlg = dlgs->first(); dlg; dlg = dlgs->next())
00776     {
00777         KDialogBase* dlgbase = dynamic_cast<KDialogBase *>( dlg );
00778         if ( dlgbase ) {
00779             kdDebug(6000) << "closeChildDialogs: closing dialog " << dlgbase << endl;
00780             // close() ends up calling QButton::animateClick, which isn't immediate
00781             // we need something the exits the event loop immediately (#49068)
00782             dlgbase->cancel();
00783         }
00784         else
00785         {
00786             kdWarning() << "closeChildDialogs: not a KDialogBase! Don't use QDialogs in KDE! " << static_cast<QWidget*>(dlg) << endl;
00787             static_cast<QWidget*>(dlg)->hide();
00788         }
00789     }
00790     delete dlgs;
00791     d->m_dialogsAllowed = false;
00792 }
00793 
00794 bool KHTMLView::dialogsAllowed() {
00795     bool allowed = d->m_dialogsAllowed;
00796     KHTMLPart* p = m_part->parentPart();
00797     if (p && p->view())
00798         allowed &= p->view()->dialogsAllowed();
00799     return allowed;
00800 }
00801 
00802 void KHTMLView::closeEvent( QCloseEvent* ev )
00803 {
00804     closeChildDialogs();
00805     QScrollView::closeEvent( ev );
00806 }
00807 
00808 //
00809 // Event Handling
00810 //
00812 
00813 void KHTMLView::viewportMousePressEvent( QMouseEvent *_mouse )
00814 {
00815     if(!m_part->xmlDocImpl()) return;
00816     if (d->possibleTripleClick)
00817     {
00818         viewportMouseDoubleClickEvent( _mouse ); // it handles triple clicks too
00819         return;
00820     }
00821 
00822     int xm, ym;
00823     viewportToContents(_mouse->x(), _mouse->y(), xm, ym);
00824     //kdDebug( 6000 ) << "mousePressEvent: viewport=("<<_mouse->x()<<"/"<<_mouse->y()<<"), contents=(" << xm << "/" << ym << ")\n";
00825 
00826     d->isDoubleClick = false;
00827 
00828     DOM::NodeImpl::MouseEvent mev( _mouse->stateAfter(), DOM::NodeImpl::MousePress );
00829     m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev );
00830 
00831     //kdDebug(6000) << "innerNode="<<mev.innerNode.nodeName().string()<<endl;
00832 
00833     if (d->clickCount > 0 &&
00834         QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() <= QApplication::startDragDistance())
00835     d->clickCount++;
00836     else {
00837     d->clickCount = 1;
00838     d->clickX = xm;
00839     d->clickY = ym;
00840     }
00841 
00842     bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEDOWN_EVENT,mev.innerNode.handle(),mev.innerNonSharedNode.handle(),true,
00843                                            d->clickCount,_mouse,true,DOM::NodeImpl::MousePress);
00844 
00845     khtml::RenderObject* r = mev.innerNode.handle() ? mev.innerNode.handle()->renderer() : 0;
00846     if (r && r->isWidget())
00847     _mouse->ignore();
00848 
00849     if (!swallowEvent) {
00850     emit m_part->nodeActivated(mev.innerNode);
00851 
00852     khtml::MousePressEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode );
00853         QApplication::sendEvent( m_part, &event );
00854         // we might be deleted after this
00855     }
00856 }
00857 
00858 void KHTMLView::viewportMouseDoubleClickEvent( QMouseEvent *_mouse )
00859 {
00860     if(!m_part->xmlDocImpl()) return;
00861 
00862     int xm, ym;
00863     viewportToContents(_mouse->x(), _mouse->y(), xm, ym);
00864 
00865     kdDebug( 6000 ) << "mouseDblClickEvent: x=" << xm << ", y=" << ym << endl;
00866 
00867     d->isDoubleClick = true;
00868 
00869     DOM::NodeImpl::MouseEvent mev( _mouse->stateAfter(), DOM::NodeImpl::MouseDblClick );
00870     m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev );
00871 
00872     // We do the same thing as viewportMousePressEvent() here, since the DOM does not treat
00873     // single and double-click events as separate (only the detail, i.e. number of clicks differs)
00874     if (d->clickCount > 0 &&
00875         QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() <= QApplication::startDragDistance())
00876     d->clickCount++;
00877     else { // shouldn't happen, if Qt has the same criterias for double clicks.
00878     d->clickCount = 1;
00879     d->clickX = xm;
00880     d->clickY = ym;
00881     }
00882     bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEDOWN_EVENT,mev.innerNode.handle(),mev.innerNonSharedNode.handle(),true,
00883                                            d->clickCount,_mouse,true,DOM::NodeImpl::MouseDblClick);
00884 
00885     khtml::RenderObject* r = mev.innerNode.handle() ? mev.innerNode.handle()->renderer() : 0;
00886     if (r && r->isWidget())
00887     _mouse->ignore();
00888 
00889     if (!swallowEvent) {
00890     khtml::MouseDoubleClickEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode, d->clickCount );
00891     QApplication::sendEvent( m_part, &event );
00892     }
00893 
00894     d->possibleTripleClick=true;
00895     QTimer::singleShot(QApplication::doubleClickInterval(),this,SLOT(tripleClickTimeout()));
00896 }
00897 
00898 void KHTMLView::tripleClickTimeout()
00899 {
00900     d->possibleTripleClick = false;
00901     d->clickCount = 0;
00902 }
00903 
00904 static inline void forwardPeripheralEvent(khtml::RenderWidget* r, QMouseEvent* me, int x, int y)
00905 {
00906     int absx = 0;
00907     int absy = 0;
00908     r->absolutePosition(absx, absy);
00909     QPoint p(x-absx, y-absy);
00910     QMouseEvent fw(me->type(), p, me->button(), me->state());
00911     QWidget* w = r->widget();
00912     if(w)
00913         static_cast<khtml::RenderWidget::EventPropagator*>(w)->sendEvent(&fw);
00914 }
00915 
00916 void KHTMLView::viewportMouseMoveEvent( QMouseEvent * _mouse )
00917 {
00918 
00919     if(!m_part->xmlDocImpl()) return;
00920 
00921     int xm, ym;
00922     viewportToContents(_mouse->x(), _mouse->y(), xm, ym);
00923 
00924     DOM::NodeImpl::MouseEvent mev( _mouse->stateAfter(), DOM::NodeImpl::MouseMove );
00925     // Do not modify :hover/:active state while mouse is pressed.
00926     m_part->xmlDocImpl()->prepareMouseEvent( _mouse->state() & Qt::MouseButtonMask /*readonly ?*/, xm, ym, &mev );
00927 
00928 //     kdDebug(6000) << "mouse move: " << _mouse->pos()
00929 //        << " button " << _mouse->button()
00930 //        << " state " << _mouse->state() << endl;
00931 
00932     bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEMOVE_EVENT,mev.innerNode.handle(),mev.innerNonSharedNode.handle(),false,
00933                                            0,_mouse,true,DOM::NodeImpl::MouseMove);
00934 
00935     if (d->clickCount > 0 &&
00936         QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() > QApplication::startDragDistance()) {
00937     d->clickCount = 0;  // moving the mouse outside the threshold invalidates the click
00938     }
00939 
00940     // execute the scheduled script. This is to make sure the mouseover events come after the mouseout events
00941     m_part->executeScheduledScript();
00942 
00943     DOM::NodeImpl* fn = m_part->xmlDocImpl()->focusNode();
00944     if (fn && fn != mev.innerNode.handle() &&
00945         fn->renderer() && fn->renderer()->isWidget()) {
00946         forwardPeripheralEvent(static_cast<khtml::RenderWidget*>(fn->renderer()), _mouse, xm, ym);
00947     }
00948 
00949     khtml::RenderObject* r = mev.innerNode.handle() ? mev.innerNode.handle()->renderer() : 0;
00950     khtml::RenderStyle* style = (r && r->style()) ? r->style() : 0;
00951     QCursor c;
00952     switch ( style ? style->cursor() : CURSOR_AUTO) {
00953     case CURSOR_AUTO:
00954         if ( r && r->isText() )
00955             c = KCursor::ibeamCursor();
00956 
00957         if ( mev.url.length() && m_part->settings()->changeCursor() )
00958             c = m_part->urlCursor();
00959 
00960         if (r && r->isFrameSet() && !static_cast<RenderFrameSet*>(r)->noResize())
00961             c = QCursor(static_cast<RenderFrameSet*>(r)->cursorShape());
00962 
00963         break;
00964     case CURSOR_CROSS:
00965         c = KCursor::crossCursor();
00966         break;
00967     case CURSOR_POINTER:
00968         c = m_part->urlCursor();
00969         break;
00970     case CURSOR_PROGRESS:
00971         c = KCursor::workingCursor();
00972         break;
00973     case CURSOR_MOVE:
00974         c = KCursor::sizeAllCursor();
00975         break;
00976     case CURSOR_E_RESIZE:
00977     case CURSOR_W_RESIZE:
00978         c = KCursor::sizeHorCursor();
00979         break;
00980     case CURSOR_N_RESIZE:
00981     case CURSOR_S_RESIZE:
00982         c = KCursor::sizeVerCursor();
00983         break;
00984     case CURSOR_NE_RESIZE:
00985     case CURSOR_SW_RESIZE:
00986         c = KCursor::sizeBDiagCursor();
00987         break;
00988     case CURSOR_NW_RESIZE:
00989     case CURSOR_SE_RESIZE:
00990         c = KCursor::sizeFDiagCursor();
00991         break;
00992     case CURSOR_TEXT:
00993         c = KCursor::ibeamCursor();
00994         break;
00995     case CURSOR_WAIT:
00996         c = KCursor::waitCursor();
00997         break;
00998     case CURSOR_HELP:
00999         c = KCursor::whatsThisCursor();
01000         break;
01001     case CURSOR_DEFAULT:
01002         break;
01003     }
01004 
01005     if ( viewport()->cursor().handle() != c.handle() ) {
01006         if( c.handle() == KCursor::arrowCursor().handle()) {
01007             for (KHTMLPart* p = m_part; p; p = p->parentPart())
01008                 p->view()->viewport()->unsetCursor();
01009         }
01010         else {
01011             viewport()->setCursor( c );
01012     }
01013     }
01014     if (r && r->isWidget()) {
01015     _mouse->ignore();
01016     }
01017 
01018 
01019     d->prevMouseX = xm;
01020     d->prevMouseY = ym;
01021 
01022     if (!swallowEvent) {
01023         khtml::MouseMoveEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode );
01024         QApplication::sendEvent( m_part, &event );
01025     }
01026 }
01027 
01028 void KHTMLView::viewportMouseReleaseEvent( QMouseEvent * _mouse )
01029 {
01030     if ( !m_part->xmlDocImpl() ) return;
01031 
01032     int xm, ym;
01033     viewportToContents(_mouse->x(), _mouse->y(), xm, ym);
01034 
01035     DOM::NodeImpl::MouseEvent mev( _mouse->stateAfter(), DOM::NodeImpl::MouseRelease );
01036     m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev );
01037 
01038     bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEUP_EVENT,mev.innerNode.handle(),mev.innerNonSharedNode.handle(),true,
01039                                            d->clickCount,_mouse,false,DOM::NodeImpl::MouseRelease);
01040 
01041     if (d->clickCount > 0 &&
01042         QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() <= QApplication::startDragDistance()) {
01043     QMouseEvent me(d->isDoubleClick ? QEvent::MouseButtonDblClick : QEvent::MouseButtonRelease,
01044                _mouse->pos(), _mouse->button(), _mouse->state());
01045     dispatchMouseEvent(EventImpl::CLICK_EVENT, mev.innerNode.handle(),mev.innerNonSharedNode.handle(),true,
01046                            d->clickCount, &me, true, DOM::NodeImpl::MouseRelease);
01047     }
01048 
01049     DOM::NodeImpl* fn = m_part->xmlDocImpl()->focusNode();
01050     if (fn && fn != mev.innerNode.handle() &&
01051         fn->renderer() && fn->renderer()->isWidget()) {
01052         forwardPeripheralEvent(static_cast<khtml::RenderWidget*>(fn->renderer()), _mouse, xm, ym);
01053     }
01054 
01055     khtml::RenderObject* r = mev.innerNode.handle() ? mev.innerNode.handle()->renderer() : 0;
01056     if (r && r->isWidget())
01057     _mouse->ignore();
01058 
01059     if (!swallowEvent) {
01060     khtml::MouseReleaseEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode );
01061     QApplication::sendEvent( m_part, &event );
01062     }
01063 }
01064 
01065 // returns true if event should be swallowed
01066 bool KHTMLView::dispatchKeyEvent( QKeyEvent *_ke )
01067 {
01068     if (!m_part->xmlDocImpl())
01069         return false;
01070     // Pressing and releasing a key should generate keydown, keypress and keyup events
01071     // Holding it down should generated keydown, keypress (repeatedly) and keyup events
01072     // The problem here is that Qt generates two autorepeat events (keyrelease+keypress)
01073     // for autorepeating, while DOM wants only one autorepeat event (keypress), so one
01074     // of the Qt events shouldn't be passed to DOM, but it should be still filtered
01075     // out if DOM would filter the autorepeat event. Additional problem is that Qt keyrelease
01076     // events don't have text() set (Qt bug?), so DOM often would ignore the keypress event
01077     // if it was created using Qt keyrelease, but Qt autorepeat keyrelease comes
01078     // before Qt autorepeat keypress (i.e. problem whether to filter it out or not).
01079     // The solution is to filter out and postpone the Qt autorepeat keyrelease until
01080     // the following Qt keypress event comes. If DOM accepts the DOM keypress event,
01081     // the postponed event will be simply discarded. If not, it will be passed to keyPressEvent()
01082     // again, and here it will be ignored.
01083     //
01084     //  Qt:      Press      | Release(autorepeat) Press(autorepeat) etc. |   Release
01085     //  DOM:   Down + Press |      (nothing)           Press             |     Up
01086 
01087     // It's also possible to get only Releases. E.g. the release of alt-tab,
01088     // or when the keypresses get captured by an accel.
01089 
01090     if( _ke == d->postponed_autorepeat ) // replayed event
01091     {
01092         return false;
01093     }
01094 
01095     if( _ke->type() == QEvent::KeyPress )
01096     {
01097         if( !_ke->isAutoRepeat())
01098         {
01099             bool ret = dispatchKeyEventHelper( _ke, false ); // keydown
01100             if( dispatchKeyEventHelper( _ke, true )) // keypress
01101                 ret = true;
01102             return ret;
01103         }
01104         else // autorepeat
01105         {
01106             bool ret = dispatchKeyEventHelper( _ke, true ); // keypress
01107             if( !ret && d->postponed_autorepeat )
01108                 keyPressEvent( d->postponed_autorepeat );
01109             delete d->postponed_autorepeat;
01110             d->postponed_autorepeat = NULL;
01111             return ret;
01112         }
01113     }
01114     else // QEvent::KeyRelease
01115     {
01116         // Discard postponed "autorepeat key-release" events that didn't see
01117         // a keypress after them (e.g. due to QAccel)
01118         if ( d->postponed_autorepeat ) {
01119             delete d->postponed_autorepeat;
01120             d->postponed_autorepeat = 0;
01121         }
01122 
01123         if( !_ke->isAutoRepeat()) {
01124             return dispatchKeyEventHelper( _ke, false ); // keyup
01125         }
01126         else
01127         {
01128             d->postponed_autorepeat = new QKeyEvent( _ke->type(), _ke->key(), _ke->ascii(), _ke->state(),
01129                 _ke->text(), _ke->isAutoRepeat(), _ke->count());
01130             if( _ke->isAccepted())
01131                 d->postponed_autorepeat->accept();
01132             else
01133                 d->postponed_autorepeat->ignore();
01134             return true;
01135         }
01136     }
01137 }
01138 
01139 // returns true if event should be swallowed
01140 bool KHTMLView::dispatchKeyEventHelper( QKeyEvent *_ke, bool keypress )
01141 {
01142     DOM::NodeImpl* keyNode = m_part->xmlDocImpl()->focusNode();
01143     if (keyNode) {
01144         return keyNode->dispatchKeyEvent(_ke, keypress);
01145     } else { // no focused node, send to document
01146         return m_part->xmlDocImpl()->dispatchKeyEvent(_ke, keypress);
01147     }
01148 }
01149 
01150 void KHTMLView::keyPressEvent( QKeyEvent *_ke )
01151 {
01152 
01153 #ifndef KHTML_NO_CARET
01154     if (m_part->isEditable() || m_part->isCaretMode()
01155         || (m_part->xmlDocImpl() && m_part->xmlDocImpl()->focusNode()
01156         && m_part->xmlDocImpl()->focusNode()->contentEditable())) {
01157       d->caretViewContext()->keyReleasePending = true;
01158       caretKeyPressEvent(_ke);
01159       return;
01160     }
01161 #endif // KHTML_NO_CARET
01162 
01163     // If CTRL was hit, be prepared for access keys
01164     if (_ke->key() == Key_Control && _ke->state()==0 && !d->accessKeysActivated) d->accessKeysPreActivate=true;
01165 
01166     if (_ke->key() == Key_Shift && _ke->state()==0)
01167         d->scrollSuspendPreActivate=true;
01168 
01169     // accesskey handling needs to be done before dispatching, otherwise e.g. lineedits
01170     // may eat the event
01171 
01172     if (d->accessKeysActivated)
01173     {
01174         if (_ke->state()==0 || _ke->state()==ShiftButton) {
01175     if (_ke->key() != Key_Shift) accessKeysTimeout();
01176         handleAccessKey( _ke );
01177         _ke->accept();
01178         return;
01179         }
01180     accessKeysTimeout();
01181     }
01182 
01183     if ( dispatchKeyEvent( _ke )) {
01184         // If either keydown or keypress was accepted by a widget, or canceled by JS, stop here.
01185         _ke->accept();
01186         return;
01187     }
01188 
01189 #ifndef KHTML_NO_TYPE_AHEAD_FIND
01190     if(d->typeAheadActivated)
01191     {
01192         // type-ahead find aka find-as-you-type
01193         if(_ke->key() == Key_BackSpace)
01194         {
01195             d->findString = d->findString.left(d->findString.length() - 1);
01196 
01197             if(!d->findString.isEmpty())
01198             {
01199                 findAhead(false);
01200             }
01201             else
01202             {
01203                 findTimeout();
01204             }
01205 
01206             d->timer.start(3000, true);
01207             _ke->accept();
01208             return;
01209         }
01210         else if(_ke->key() == KStdAccel::findNext())
01211         { // part doesn't get this key event because of the keyboard grab
01212             m_part->findTextNext();
01213             d->timer.start(3000, true);
01214             _ke->accept();
01215             return;
01216         }
01217         else if(_ke->key() == Key_Escape)
01218         {
01219             findTimeout();
01220 
01221             _ke->accept();
01222             return;
01223         }
01224         else if(_ke->text().isEmpty() == false)
01225         {
01226             d->findString += _ke->text();
01227 
01228             findAhead(true);
01229 
01230             d->timer.start(3000, true);
01231             _ke->accept();
01232             return;
01233         }
01234     }
01235     else if(_ke->key() == '\'' || _ke->key() == '/')
01236     {
01237         if(_ke->key() == '\'')
01238         {
01239             d->findLinksOnly = true;
01240             m_part->setStatusBarText(i18n("Starting -- find links as you type"),
01241                                      KHTMLPart::BarDefaultText);
01242         }
01243         else if(_ke->key() == '/')
01244         {
01245             d->findLinksOnly = false;
01246             m_part->setStatusBarText(i18n("Starting -- find text as you type"),
01247                                      KHTMLPart::BarDefaultText);
01248         }
01249 
01250         m_part->findTextBegin();
01251         d->typeAheadActivated = true;
01252         d->timer.start(3000, true);
01253         grabKeyboard();
01254         _ke->accept();
01255         return;
01256     }
01257 #endif // KHTML_NO_TYPE_AHEAD_FIND
01258 
01259     int offs = (clipper()->height() < 30) ? clipper()->height() : 30;
01260     if (_ke->state() & Qt::ShiftButton)
01261       switch(_ke->key())
01262         {
01263         case Key_Space:
01264             if ( d->vmode == QScrollView::AlwaysOff )
01265                 _ke->accept();
01266             else {
01267                 scrollBy( 0, -clipper()->height() - offs );
01268                 if(d->scrollSuspended)
01269                     d->newScrollTimer(this, 0);
01270             }
01271             break;
01272 
01273         case Key_Down:
01274         case Key_J:
01275             d->adjustScroller(this, KHTMLViewPrivate::ScrollDown, KHTMLViewPrivate::ScrollUp);
01276             break;
01277 
01278         case Key_Up:
01279         case Key_K:
01280             d->adjustScroller(this, KHTMLViewPrivate::ScrollUp, KHTMLViewPrivate::ScrollDown);
01281             break;
01282 
01283         case Key_Left:
01284         case Key_H:
01285             d->adjustScroller(this, KHTMLViewPrivate::ScrollLeft, KHTMLViewPrivate::ScrollRight);
01286             break;
01287 
01288         case Key_Right:
01289         case Key_L:
01290             d->adjustScroller(this, KHTMLViewPrivate::ScrollRight, KHTMLViewPrivate::ScrollLeft);
01291             break;
01292         }
01293     else
01294         switch ( _ke->key() )
01295         {
01296         case Key_Down:
01297         case Key_J:
01298             if ( d->vmode == QScrollView::AlwaysOff )
01299                 _ke->accept();
01300             else {
01301                 if (!d->scrollTimerId || d->scrollSuspended)
01302                     scrollBy( 0, 10 );
01303                 if (d->scrollTimerId)
01304                     d->newScrollTimer(this, 0);
01305             }
01306             break;
01307 
01308         case Key_Space:
01309         case Key_Next:
01310             if ( d->vmode == QScrollView::AlwaysOff )
01311                 _ke->accept();
01312             else {
01313                 scrollBy( 0, clipper()->height() - offs );
01314                 if(d->scrollSuspended)
01315                     d->newScrollTimer(this, 0);
01316             }
01317             break;
01318 
01319         case Key_Up:
01320         case Key_K:
01321             if ( d->vmode == QScrollView::AlwaysOff )
01322                 _ke->accept();
01323             else {
01324                 if (!d->scrollTimerId || d->scrollSuspended)
01325                     scrollBy( 0, -10 );
01326                 if (d->scrollTimerId)
01327                     d->newScrollTimer(this, 0);
01328             }
01329             break;
01330 
01331         case Key_Prior:
01332             if ( d->vmode == QScrollView::AlwaysOff )
01333                 _ke->accept();
01334             else {
01335                 scrollBy( 0, -clipper()->height() + offs );
01336                 if(d->scrollSuspended)
01337                     d->newScrollTimer(this, 0);
01338             }
01339             break;
01340         case Key_Right:
01341         case Key_L:
01342             if ( d->hmode == QScrollView::AlwaysOff )
01343                 _ke->accept();
01344             else {
01345                 if (!d->scrollTimerId || d->scrollSuspended)
01346                     scrollBy( 10, 0 );
01347                 if (d->scrollTimerId)
01348                     d->newScrollTimer(this, 0);
01349             }
01350             break;
01351         case Key_Left:
01352         case Key_H:
01353             if ( d->hmode == QScrollView::AlwaysOff )
01354                 _ke->accept();
01355             else {
01356                 if (!d->scrollTimerId || d->scrollSuspended)
01357                     scrollBy( -10, 0 );
01358                 if (d->scrollTimerId)
01359                     d->newScrollTimer(this, 0);
01360             }
01361             break;
01362         case Key_Enter:
01363         case Key_Return:
01364         // ### FIXME:
01365         // or even better to HTMLAnchorElementImpl::event()
01366             if (m_part->xmlDocImpl()) {
01367         NodeImpl *n = m_part->xmlDocImpl()->focusNode();
01368         if (n)
01369             n->setActive();
01370         }
01371             break;
01372         case Key_Home:
01373             if ( d->vmode == QScrollView::AlwaysOff )
01374                 _ke->accept();
01375             else {
01376                 setContentsPos( 0, 0 );
01377                 if(d->scrollSuspended)
01378                     d->newScrollTimer(this, 0);
01379             }
01380             break;
01381         case Key_End:
01382             if ( d->vmode == QScrollView::AlwaysOff )
01383                 _ke->accept();
01384             else {
01385                 setContentsPos( 0, contentsHeight() - visibleHeight() );
01386                 if(d->scrollSuspended)
01387                     d->newScrollTimer(this, 0);
01388             }
01389             break;
01390         case Key_Shift:
01391             // what are you doing here?
01392         _ke->ignore();
01393             return;
01394         default:
01395             if (d->scrollTimerId)
01396                 d->newScrollTimer(this, 0);
01397         _ke->ignore();
01398             return;
01399         }
01400 
01401     _ke->accept();
01402 }
01403 
01404 #ifndef KHTML_NO_TYPE_AHEAD_FIND
01405 
01406 void KHTMLView::findTimeout()
01407 {
01408     d->typeAheadActivated = false;
01409     d->findString = "";
01410     releaseKeyboard();
01411     m_part->setStatusBarText(i18n("Find stopped."), KHTMLPart::BarDefaultText);
01412 }
01413 
01414 void KHTMLView::findAhead(bool increase)
01415 {
01416     QString status;
01417 
01418     if(d->findLinksOnly)
01419     {
01420         m_part->findText(d->findString, KHTMLPart::FindNoPopups |
01421                          KHTMLPart::FindLinksOnly, this);
01422         if(m_part->findTextNext())
01423         {
01424             status = i18n("Link found: \"%1\".");
01425         }
01426         else
01427         {
01428             if(increase) KNotifyClient::beep();
01429             status = i18n("Link not found: \"%1\".");
01430         }
01431     }
01432     else
01433     {
01434         m_part->findText(d->findString, KHTMLPart::FindNoPopups, this);
01435         if(m_part->findTextNext())
01436         {
01437             status = i18n("Text found: \"%1\".");
01438         }
01439         else
01440         {
01441             if(increase) KNotifyClient::beep();
01442             status = i18n("Text not found: \"%1\".");
01443         }
01444     }
01445 
01446     m_part->setStatusBarText(status.arg(d->findString.lower()),
01447                              KHTMLPart::BarDefaultText);
01448 }
01449 
01450 #endif // KHTML_NO_TYPE_AHEAD_FIND
01451 
01452 void KHTMLView::keyReleaseEvent(QKeyEvent *_ke)
01453 {
01454     if (d->m_caretViewContext && d->m_caretViewContext->keyReleasePending) {
01455         //caretKeyReleaseEvent(_ke);
01456     d->m_caretViewContext->keyReleasePending = false;
01457     return;
01458     }
01459 
01460     if (d->accessKeysPreActivate && _ke->key() != Key_Control) d->accessKeysPreActivate=false;
01461     if (_ke->key() == Key_Control &&  d->accessKeysPreActivate && _ke->state() == Qt::ControlButton && !(KApplication::keyboardModifiers() & KApplication::ControlModifier))
01462     {
01463         displayAccessKeys();
01464         m_part->setStatusBarText(i18n("Access Keys activated"),KHTMLPart::BarOverrideText);
01465         d->accessKeysActivated = true;
01466         d->accessKeysPreActivate = false;
01467     }
01468     else if (d->accessKeysActivated) accessKeysTimeout();
01469 
01470     if( d->scrollSuspendPreActivate && _ke->key() != Key_Shift )
01471         d->scrollSuspendPreActivate = false;
01472     if( _ke->key() == Key_Shift && d->scrollSuspendPreActivate && _ke->state() == Qt::ShiftButton
01473         && !(KApplication::keyboardModifiers() & KApplication::ShiftModifier))
01474         if (d->scrollTimerId)
01475                 d->scrollSuspended = !d->scrollSuspended;
01476 
01477     // Send keyup event
01478     if ( dispatchKeyEvent( _ke ) )
01479     {
01480         _ke->accept();
01481         return;
01482     }
01483 
01484     QScrollView::keyReleaseEvent(_ke);
01485 }
01486 
01487 void KHTMLView::contentsContextMenuEvent ( QContextMenuEvent * /*ce*/ )
01488 {
01489 // ### what kind of c*** is that ?
01490 #if 0
01491     if (!m_part->xmlDocImpl()) return;
01492     int xm = _ce->x();
01493     int ym = _ce->y();
01494 
01495     DOM::NodeImpl::MouseEvent mev( _ce->state(), DOM::NodeImpl::MouseMove ); // ### not a mouse event!
01496     m_part->xmlDocImpl()->prepareMouseEvent( xm, ym, &mev );
01497 
01498     NodeImpl *targetNode = mev.innerNode.handle();
01499     if (targetNode && targetNode->renderer() && targetNode->renderer()->isWidget()) {
01500         int absx = 0;
01501         int absy = 0;
01502         targetNode->renderer()->absolutePosition(absx,absy);
01503         QPoint pos(xm-absx,ym-absy);
01504 
01505         QWidget *w = static_cast<RenderWidget*>(targetNode->renderer())->widget();
01506         QContextMenuEvent cme(_ce->reason(),pos,_ce->globalPos(),_ce->state());
01507         setIgnoreEvents(true);
01508         QApplication::sendEvent(w,&cme);
01509         setIgnoreEvents(false);
01510     }
01511 #endif
01512 }
01513 
01514 bool KHTMLView::focusNextPrevChild( bool next )
01515 {
01516     // Now try to find the next child
01517     if (m_part->xmlDocImpl() && focusNextPrevNode(next))
01518     {
01519     if (m_part->xmlDocImpl()->focusNode())
01520         kdDebug() << "focusNode.name: "
01521               << m_part->xmlDocImpl()->focusNode()->nodeName().string() << endl;
01522     return true; // focus node found
01523     }
01524 
01525     // If we get here, pass tabbing control up to the next/previous child in our parent
01526     d->pseudoFocusNode = KHTMLViewPrivate::PFNone;
01527     if (m_part->parentPart() && m_part->parentPart()->view())
01528         return m_part->parentPart()->view()->focusNextPrevChild(next);
01529 
01530     return QWidget::focusNextPrevChild(next);
01531 }
01532 
01533 void KHTMLView::doAutoScroll()
01534 {
01535     QPoint pos = QCursor::pos();
01536     pos = viewport()->mapFromGlobal( pos );
01537 
01538     int xm, ym;
01539     viewportToContents(pos.x(), pos.y(), xm, ym);
01540 
01541     pos = QPoint(pos.x() - viewport()->x(), pos.y() - viewport()->y());
01542     if ( (pos.y() < 0) || (pos.y() > visibleHeight()) ||
01543          (pos.x() < 0) || (pos.x() > visibleWidth()) )
01544     {
01545         ensureVisible( xm, ym, 0, 5 );
01546 
01547 #ifndef KHTML_NO_SELECTION
01548         // extend the selection while scrolling
01549     DOM::Node innerNode;
01550     if (m_part->isExtendingSelection()) {
01551             RenderObject::NodeInfo renderInfo(true/*readonly*/, false/*active*/);
01552             m_part->xmlDocImpl()->renderer()->layer()
01553                 ->nodeAtPoint(renderInfo, xm, ym);
01554             innerNode = renderInfo.innerNode();
01555     }/*end if*/
01556 
01557         if (innerNode.handle() && innerNode.handle()->renderer()) {
01558             int absX, absY;
01559             innerNode.handle()->renderer()->absolutePosition(absX, absY);
01560 
01561             m_part->extendSelectionTo(xm, ym, absX, absY, innerNode);
01562         }/*end if*/
01563 #endif // KHTML_NO_SELECTION
01564     }
01565 }
01566 
01567 
01568 class HackWidget : public QWidget
01569 {
01570  public:
01571     inline void setNoErase() { setWFlags(getWFlags()|WRepaintNoErase); }
01572 };
01573 
01574 bool KHTMLView::eventFilter(QObject *o, QEvent *e)
01575 {
01576     if ( e->type() == QEvent::AccelOverride ) {
01577     QKeyEvent* ke = (QKeyEvent*) e;
01578 //kdDebug(6200) << "QEvent::AccelOverride" << endl;
01579     if (m_part->isEditable() || m_part->isCaretMode()
01580         || (m_part->xmlDocImpl() && m_part->xmlDocImpl()->focusNode()
01581         && m_part->xmlDocImpl()->focusNode()->contentEditable())) {
01582 //kdDebug(6200) << "editable/navigable" << endl;
01583         if ( (ke->state() & ControlButton) || (ke->state() & ShiftButton) ) {
01584         switch ( ke->key() ) {
01585         case Key_Left:
01586         case Key_Right:
01587         case Key_Up:
01588         case Key_Down:
01589         case Key_Home:
01590         case Key_End:
01591             ke->accept();
01592 //kdDebug(6200) << "eaten" << endl;
01593             return true;
01594         default:
01595             break;
01596         }
01597         }
01598     }
01599     }
01600 
01601     QWidget *view = viewport();
01602 
01603     if (o == view) {
01604     // we need to install an event filter on all children of the viewport to
01605     // be able to get correct stacking of children within the document.
01606     if(e->type() == QEvent::ChildInserted) {
01607         QObject *c = static_cast<QChildEvent *>(e)->child();
01608         if (c->isWidgetType()) {
01609         QWidget *w = static_cast<QWidget *>(c);
01610         // don't install the event filter on toplevels
01611         if (w->parentWidget(true) == view) {
01612             if (!strcmp(w->name(), "__khtml")) {
01613             w->installEventFilter(this);
01614             w->unsetCursor();
01615             if (!::qt_cast<QFrame*>(w))
01616                 w->setBackgroundMode( QWidget::NoBackground );
01617             static_cast<HackWidget *>(w)->setNoErase();
01618             if (w->children()) {
01619                 QObjectListIterator it(*w->children());
01620                 for (; it.current(); ++it) {
01621                 QWidget *widget = ::qt_cast<QWidget *>(it.current());
01622                 if (widget && !widget->isTopLevel()) {
01623                     if (!::qt_cast<QFrame*>(w))
01624                         widget->setBackgroundMode( QWidget::NoBackground );
01625                     static_cast<HackWidget *>(widget)->setNoErase();
01626                     widget->installEventFilter(this);
01627                 }
01628                 }
01629             }
01630             }
01631         }
01632         }
01633     }
01634     } else if (o->isWidgetType()) {
01635     QWidget *v = static_cast<QWidget *>(o);
01636         QWidget *c = v;
01637     while (v && v != view) {
01638             c = v;
01639         v = v->parentWidget(true);
01640     }
01641 
01642     if (v && !strcmp(c->name(), "__khtml")) {
01643         bool block = false;
01644         QWidget *w = static_cast<QWidget *>(o);
01645         switch(e->type()) {
01646         case QEvent::Paint:
01647         if (!allowWidgetPaintEvents) {
01648             // eat the event. Like this we can control exactly when the widget
01649             // get's repainted.
01650             block = true;
01651             int x = 0, y = 0;
01652             QWidget *v = w;
01653             while (v && v != view) {
01654             x += v->x();
01655             y += v->y();
01656             v = v->parentWidget();
01657             }
01658             viewportToContents( x, y, x, y );
01659             QPaintEvent *pe = static_cast<QPaintEvent *>(e);
01660             bool asap = !d->contentsMoving && ::qt_cast<QScrollView *>(c);
01661             
01662             // QScrollView needs fast repaints
01663             if ( asap && !d->painting && m_part->xmlDocImpl() && m_part->xmlDocImpl()->renderer() &&
01664                  !static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer())->needsLayout() ) {
01665                 repaintContents(x + pe->rect().x(), y + pe->rect().y(),
01666                                             pe->rect().width(), pe->rect().height(), true);
01667                     } else {
01668                 scheduleRepaint(x + pe->rect().x(), y + pe->rect().y(),
01669                     pe->rect().width(), pe->rect().height(), asap);
01670                     }
01671         }
01672         break;
01673         case QEvent::MouseMove:
01674         case QEvent::MouseButtonPress:
01675         case QEvent::MouseButtonRelease:
01676         case QEvent::MouseButtonDblClick: {
01677         if (w->parentWidget() == view && !::qt_cast<QScrollBar *>(w)) {
01678             QMouseEvent *me = static_cast<QMouseEvent *>(e);
01679             QPoint pt = (me->pos() + w->pos());
01680             QMouseEvent me2(me->type(), pt, me->button(), me->state());
01681 
01682             if (e->type() == QEvent::MouseMove)
01683             viewportMouseMoveEvent(&me2);
01684             else if(e->type() == QEvent::MouseButtonPress)
01685             viewportMousePressEvent(&me2);
01686             else if(e->type() == QEvent::MouseButtonRelease)
01687             viewportMouseReleaseEvent(&me2);
01688             else
01689             viewportMouseDoubleClickEvent(&me2);
01690             block = true;
01691                 }
01692         break;
01693         }
01694         case QEvent::KeyPress:
01695         case QEvent::KeyRelease:
01696         if (w->parentWidget() == view && !::qt_cast<QScrollBar *>(w)) {
01697             QKeyEvent *ke = static_cast<QKeyEvent *>(e);
01698             if (e->type() == QEvent::KeyPress)
01699             keyPressEvent(ke);
01700             else
01701             keyReleaseEvent(ke);
01702             block = true;
01703         }
01704         default:
01705         break;
01706         }
01707         if (block) {
01708         //qDebug("eating event");
01709         return true;
01710         }
01711     }
01712     }
01713 
01714 //    kdDebug(6000) <<"passing event on to sv event filter object=" << o->className() << " event=" << e->type() << endl;
01715     return QScrollView::eventFilter(o, e);
01716 }
01717 
01718 
01719 DOM::NodeImpl *KHTMLView::nodeUnderMouse() const
01720 {
01721     return d->underMouse;
01722 }
01723 
01724 DOM::NodeImpl *KHTMLView::nonSharedNodeUnderMouse() const
01725 {
01726     return d->underMouseNonShared;
01727 }
01728 
01729 bool KHTMLView::scrollTo(const QRect &bounds)
01730 {
01731     d->scrollingSelf = true; // so scroll events get ignored
01732 
01733     int x, y, xe, ye;
01734     x = bounds.left();
01735     y = bounds.top();
01736     xe = bounds.right();
01737     ye = bounds.bottom();
01738 
01739     //kdDebug(6000)<<"scrolling coords: x="<<x<<" y="<<y<<" width="<<xe-x<<" height="<<ye-y<<endl;
01740 
01741     int deltax;
01742     int deltay;
01743 
01744     int curHeight = visibleHeight();
01745     int curWidth = visibleWidth();
01746 
01747     if (ye-y>curHeight-d->borderY)
01748     ye  = y + curHeight - d->borderY;
01749 
01750     if (xe-x>curWidth-d->borderX)
01751     xe = x + curWidth - d->borderX;
01752 
01753     // is xpos of target left of the view's border?
01754     if (x < contentsX() + d->borderX )
01755             deltax = x - contentsX() - d->borderX;
01756     // is xpos of target right of the view's right border?
01757     else if (xe + d->borderX > contentsX() + curWidth)
01758             deltax = xe + d->borderX - ( contentsX() + curWidth );
01759     else
01760         deltax = 0;
01761 
01762     // is ypos of target above upper border?
01763     if (y < contentsY() + d->borderY)
01764             deltay = y - contentsY() - d->borderY;
01765     // is ypos of target below lower border?
01766     else if (ye + d->borderY > contentsY() + curHeight)
01767             deltay = ye + d->borderY - ( contentsY() + curHeight );
01768     else
01769         deltay = 0;
01770 
01771     int maxx = curWidth-d->borderX;
01772     int maxy = curHeight-d->borderY;
01773 
01774     int scrollX,scrollY;
01775 
01776     scrollX = deltax > 0 ? (deltax > maxx ? maxx : deltax) : deltax == 0 ? 0 : (deltax>-maxx ? deltax : -maxx);
01777     scrollY = deltay > 0 ? (deltay > maxy ? maxy : deltay) : deltay == 0 ? 0 : (deltay>-maxy ? deltay : -maxy);
01778 
01779     if (contentsX() + scrollX < 0)
01780     scrollX = -contentsX();
01781     else if (contentsWidth() - visibleWidth() - contentsX() < scrollX)
01782     scrollX = contentsWidth() - visibleWidth() - contentsX();
01783 
01784     if (contentsY() + scrollY < 0)
01785     scrollY = -contentsY();
01786     else if (contentsHeight() - visibleHeight() - contentsY() < scrollY)
01787     scrollY = contentsHeight() - visibleHeight() - contentsY();
01788 
01789     scrollBy(scrollX, scrollY);
01790 
01791     d->scrollingSelf = false;
01792 
01793     if ( (abs(deltax)<=maxx) && (abs(deltay)<=maxy) )
01794     return true;
01795     else return false;
01796 
01797 }
01798 
01799 bool KHTMLView::focusNextPrevNode(bool next)
01800 {
01801     // Sets the focus node of the document to be the node after (or if
01802     // next is false, before) the current focus node.  Only nodes that
01803     // are selectable (i.e. for which isFocusable() returns true) are
01804     // taken into account, and the order used is that specified in the
01805     // HTML spec (see DocumentImpl::nextFocusNode() and
01806     // DocumentImpl::previousFocusNode() for details).
01807 
01808     DocumentImpl *doc = m_part->xmlDocImpl();
01809     NodeImpl *oldFocusNode = doc->focusNode();
01810 
01811 #if 1
01812     // If the user has scrolled the document, then instead of picking
01813     // the next focusable node in the document, use the first one that
01814     // is within the visible area (if possible).
01815     if (d->scrollBarMoved)
01816     {
01817     NodeImpl *toFocus;
01818     if (next)
01819         toFocus = doc->nextFocusNode(oldFocusNode);
01820     else
01821         toFocus = doc->previousFocusNode(oldFocusNode);
01822 
01823     if (!toFocus && oldFocusNode)
01824         if (next)
01825         toFocus = doc->nextFocusNode(NULL);
01826         else
01827         toFocus = doc->previousFocusNode(NULL);
01828 
01829     while (toFocus && toFocus != oldFocusNode)
01830     {
01831 
01832         QRect focusNodeRect = toFocus->getRect();
01833         if ((focusNodeRect.left() > contentsX()) && (focusNodeRect.right() < contentsX() + visibleWidth()) &&
01834         (focusNodeRect.top() > contentsY()) && (focusNodeRect.bottom() < contentsY() + visibleHeight())) {
01835         {
01836             QRect r = toFocus->getRect();
01837             ensureVisible( r.right(), r.bottom());
01838             ensureVisible( r.left(), r.top());
01839             d->scrollBarMoved = false;
01840             d->tabMovePending = false;
01841             d->lastTabbingDirection = next;
01842             d->pseudoFocusNode = KHTMLViewPrivate::PFNone;
01843             m_part->xmlDocImpl()->setFocusNode(toFocus);
01844             Node guard(toFocus);
01845             if (!toFocus->hasOneRef() )
01846             {
01847             emit m_part->nodeActivated(Node(toFocus));
01848             }
01849             return true;
01850         }
01851         }
01852         if (next)
01853         toFocus = doc->nextFocusNode(toFocus);
01854         else
01855         toFocus = doc->previousFocusNode(toFocus);
01856 
01857         if (!toFocus && oldFocusNode)
01858         if (next)
01859             toFocus = doc->nextFocusNode(NULL);
01860         else
01861             toFocus = doc->previousFocusNode(NULL);
01862     }
01863 
01864     d->scrollBarMoved = false;
01865     }
01866 #endif
01867 
01868     if (!oldFocusNode && d->pseudoFocusNode == KHTMLViewPrivate::PFNone)
01869     {
01870     ensureVisible(contentsX(), next?0:contentsHeight());
01871     d->scrollBarMoved = false;
01872     d->pseudoFocusNode = next?KHTMLViewPrivate::PFTop:KHTMLViewPrivate::PFBottom;
01873     return true;
01874     }
01875 
01876     NodeImpl *newFocusNode = NULL;
01877 
01878     if (d->tabMovePending && next != d->lastTabbingDirection)
01879     {
01880     //kdDebug ( 6000 ) << " tab move pending and tabbing direction changed!\n";
01881     newFocusNode = oldFocusNode;
01882     }
01883     else if (next)
01884     {
01885     if (oldFocusNode || d->pseudoFocusNode == KHTMLViewPrivate::PFTop )
01886         newFocusNode = doc->nextFocusNode(oldFocusNode);
01887     }
01888     else
01889     {
01890     if (oldFocusNode || d->pseudoFocusNode == KHTMLViewPrivate::PFBottom )
01891         newFocusNode = doc->previousFocusNode(oldFocusNode);
01892     }
01893 
01894     bool targetVisible = false;
01895     if (!newFocusNode)
01896     {
01897     if ( next )
01898     {
01899         targetVisible = scrollTo(QRect(contentsX()+visibleWidth()/2,contentsHeight()-d->borderY,0,0));
01900     }
01901     else
01902     {
01903         targetVisible = scrollTo(QRect(contentsX()+visibleWidth()/2,d->borderY,0,0));
01904     }
01905     }
01906     else
01907     {
01908 #ifndef KHTML_NO_CARET
01909         // if it's an editable element, activate the caret
01910         if (!m_part->isCaretMode() && !m_part->isEditable()
01911         && newFocusNode->contentEditable()) {
01912         d->caretViewContext();
01913         moveCaretTo(newFocusNode, 0L, true);
01914         } else {
01915         caretOff();
01916     }
01917 #endif // KHTML_NO_CARET
01918 
01919     targetVisible = scrollTo(newFocusNode->getRect());
01920     }
01921 
01922     if (targetVisible)
01923     {
01924     //kdDebug ( 6000 ) << " target reached.\n";
01925     d->tabMovePending = false;
01926 
01927     m_part->xmlDocImpl()->setFocusNode(newFocusNode);
01928     if (newFocusNode)
01929     {
01930         Node guard(newFocusNode);
01931         if (!newFocusNode->hasOneRef() )
01932         {
01933         emit m_part->nodeActivated(Node(newFocusNode));
01934         }
01935         return true;
01936     }
01937     else
01938     {
01939         d->pseudoFocusNode = next?KHTMLViewPrivate::PFBottom:KHTMLViewPrivate::PFTop;
01940         return false;
01941     }
01942     }
01943     else
01944     {
01945     if (!d->tabMovePending)
01946         d->lastTabbingDirection = next;
01947     d->tabMovePending = true;
01948     return true;
01949     }
01950 }
01951 
01952 void KHTMLView::displayAccessKeys()
01953 {
01954     for( NodeImpl* n = m_part->xmlDocImpl(); n != NULL; n = n->traverseNextNode()) {
01955         if( n->isElementNode()) {
01956             ElementImpl* en = static_cast< ElementImpl* >( n );
01957             DOMString s = en->getAttribute( ATTR_ACCESSKEY );
01958             if( s.length() == 1) {
01959             QRect rec=en->getRect();
01960             QLabel *lab=new QLabel(s.string(),viewport(),0,Qt::WDestructiveClose);
01961             connect( this, SIGNAL(hideAccessKeys()), lab, SLOT(close()) );
01962             connect( this, SIGNAL(repaintAccessKeys()), lab, SLOT(repaint()));
01963             lab->setPalette(QToolTip::palette());
01964             lab->setLineWidth(2);
01965             lab->setFrameStyle(QFrame::Box | QFrame::Plain);
01966             lab->setMargin(3);
01967             lab->adjustSize();
01968             addChild(lab,
01969                     KMIN(rec.left()+rec.width()/2, contentsWidth() - lab->width()),
01970                     KMIN(rec.top()+rec.height()/2, contentsHeight() - lab->height()));
01971             showChild(lab);
01972         }
01973         }
01974     }
01975 }
01976 
01977 void KHTMLView::accessKeysTimeout()
01978 {
01979 d->accessKeysActivated=false;
01980 d->accessKeysPreActivate = false;
01981 m_part->setStatusBarText(QString::null, KHTMLPart::BarOverrideText);
01982 emit hideAccessKeys();
01983 }
01984 
01985 // Handling of the HTML accesskey attribute.
01986 bool KHTMLView::handleAccessKey( const QKeyEvent* ev )
01987 {
01988 // Qt interprets the keyevent also with the modifiers, and ev->text() matches that,
01989 // but this code must act as if the modifiers weren't pressed
01990     QChar c;
01991     if( ev->key() >= Key_A && ev->key() <= Key_Z )
01992         c = 'A' + ev->key() - Key_A;
01993     else if( ev->key() >= Key_0 && ev->key() <= Key_9 )
01994         c = '0' + ev->key() - Key_0;
01995     else {
01996         // TODO fake XKeyEvent and XLookupString ?
01997         // This below seems to work e.g. for eacute though.
01998         if( ev->text().length() == 1 )
01999             c = ev->text()[ 0 ];
02000     }
02001     if( c.isNull())
02002         return false;
02003     return focusNodeWithAccessKey( c );
02004 }
02005 
02006 bool KHTMLView::focusNodeWithAccessKey( QChar c, KHTMLView* caller )
02007 {
02008     DocumentImpl *doc = m_part->xmlDocImpl();
02009     if( !doc )
02010         return false;
02011     ElementImpl* node = doc->findAccessKeyElement( c );
02012     if( !node ) {
02013         QPtrList<KParts::ReadOnlyPart> frames = m_part->frames();
02014         for( QPtrListIterator<KParts::ReadOnlyPart> it( frames );
02015              it != NULL;
02016              ++it ) {
02017             if( !(*it)->inherits( "KHTMLPart" ))
02018                 continue;
02019             KHTMLPart* part = static_cast< KHTMLPart* >( *it );
02020             if( part->view() && part->view() != caller
02021                 && part->view()->focusNodeWithAccessKey( c, this ))
02022                 return true;
02023         }
02024         // pass up to the parent
02025         if (m_part->parentPart() && m_part->parentPart()->view()
02026             && m_part->parentPart()->view() != caller )
02027             return m_part->parentPart()->view()->focusNodeWithAccessKey( c, this );
02028         return false;
02029     }
02030 
02031     // Scroll the view as necessary to ensure that the new focus node is visible
02032 #ifndef KHTML_NO_CARET
02033     // if it's an editable element, activate the caret
02034     if (!m_part->isCaretMode() && !m_part->isEditable()
02035     && node->contentEditable()) {
02036         d->caretViewContext();
02037         moveCaretTo(node, 0L, true);
02038     } else {
02039         caretOff();
02040     }
02041 #endif // KHTML_NO_CARET
02042 
02043     QRect r = node->getRect();
02044     ensureVisible( r.right(), r.bottom());
02045     ensureVisible( r.left(), r.top());
02046 
02047     Node guard( node );
02048     if( node->isFocusable()) {
02049     if (node->id()==ID_LABEL) {
02050         // if Accesskey is a label, give focus to the label's referrer.
02051         node=static_cast<ElementImpl *>(static_cast< HTMLLabelElementImpl* >( node )->getFormElement());
02052         if (!node) return true;
02053             guard = node;
02054     }
02055         // Set focus node on the document
02056         m_part->xmlDocImpl()->setFocusNode(node);
02057         if( node != NULL && node->hasOneRef()) // deleted, only held by guard
02058             return true;
02059         emit m_part->nodeActivated(Node(node));
02060         if( node != NULL && node->hasOneRef())
02061             return true;
02062     }
02063 
02064     switch( node->id()) {
02065         case ID_A:
02066             static_cast< HTMLAnchorElementImpl* >( node )->click();
02067           break;
02068         case ID_INPUT:
02069             static_cast< HTMLInputElementImpl* >( node )->click();
02070           break;
02071         case ID_BUTTON:
02072             static_cast< HTMLButtonElementImpl* >( node )->click();
02073           break;
02074         case ID_AREA:
02075             static_cast< HTMLAreaElementImpl* >( node )->click();
02076           break;
02077         case ID_TEXTAREA:
02078       break; // just focusing it is enough
02079         case ID_LEGEND:
02080             // TODO
02081           break;
02082     }
02083     return true;
02084 }
02085 
02086 void KHTMLView::setMediaType( const QString &medium )
02087 {
02088     m_medium = medium;
02089 }
02090 
02091 QString KHTMLView::mediaType() const
02092 {
02093     return m_medium;
02094 }
02095 
02096 void KHTMLView::setWidgetVisible(RenderWidget* w, bool vis)
02097 {
02098     if (vis) {
02099         d->visibleWidgets.replace(w, w->widget());
02100     }
02101     else
02102         d->visibleWidgets.remove(w);
02103 }
02104 
02105 void KHTMLView::print()
02106 {
02107     print( false );
02108 }
02109 
02110 void KHTMLView::print(bool quick)
02111 {
02112     if(!m_part->xmlDocImpl()) return;
02113     khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer());
02114     if(!root) return;
02115 
02116     // this only works on Unix - we assume 72dpi
02117     KPrinter *printer = new KPrinter(true, QPrinter::PrinterResolution);
02118     printer->addDialogPage(new KHTMLPrintSettings());
02119     QString docname = m_part->xmlDocImpl()->URL().prettyURL();
02120     if ( !docname.isEmpty() )
02121         docname = KStringHandler::csqueeze(docname, 80);
02122     if(quick || printer->setup(this, i18n("Print %1").arg(docname))) {
02123         viewport()->setCursor( waitCursor ); // only viewport(), no QApplication::, otherwise we get the busy cursor in kdeprint's dialogs
02124         // set up KPrinter
02125         printer->setFullPage(false);
02126         printer->setCreator(QString("KDE %1.%2.%3 HTML Library").arg(KDE_VERSION_MAJOR).arg(KDE_VERSION_MINOR).arg(KDE_VERSION_RELEASE));
02127         printer->setDocName(docname);
02128 
02129         QPainter *p = new QPainter;
02130         p->begin( printer );
02131         khtml::setPrintPainter( p );
02132 
02133         m_part->xmlDocImpl()->setPaintDevice( printer );
02134         QString oldMediaType = mediaType();
02135         setMediaType( "print" );
02136         // We ignore margin settings for html and body when printing
02137         // and use the default margins from the print-system
02138         // (In Qt 3.0.x the default margins are hardcoded in Qt)
02139         m_part->xmlDocImpl()->setPrintStyleSheet( printer->option("app-khtml-printfriendly") == "true" ?
02140                                                   "* { background-image: none !important;"
02141                                                   "    background-color: white !important;"
02142                                                   "    color: black !important; }"
02143                           "body { margin: 0px !important; }"
02144                           "html { margin: 0px !important; }" :
02145                           "body { margin: 0px !important; }"
02146                           "html { margin: 0px !important; }"
02147                           );
02148 
02149         QPaintDeviceMetrics metrics( printer );
02150 
02151         // this is a simple approximation... we layout the document
02152         // according to the width of the page, then just cut
02153         // pages without caring about the content. We should do better
02154         // in the future, but for the moment this is better than no
02155         // printing support
02156         kdDebug(6000) << "printing: physical page width = " << metrics.width()
02157                       << " height = " << metrics.height() << endl;
02158         root->setPrintingMode(true);
02159         root->setWidth(metrics.width());
02160 
02161         m_part->xmlDocImpl()->styleSelector()->computeFontSizes(&metrics, 100);
02162         m_part->xmlDocImpl()->updateStyleSelector();
02163         root->setPrintImages( printer->option("app-khtml-printimages") == "true");
02164         root->setNeedsLayoutAndMinMaxRecalc();
02165         root->layout();
02166         khtml::RenderWidget::flushWidgetResizes(); // make sure widgets have their final size
02167 
02168         bool printHeader = (printer->option("app-khtml-printheader") == "true");
02169 
02170         int headerHeight = 0;
02171         QFont headerFont("helvetica", 8);
02172 
02173         QString headerLeft = KGlobal::locale()->formatDate(QDate::currentDate(),true);
02174         QString headerMid = docname;
02175         QString headerRight;
02176 
02177         if (printHeader)
02178         {
02179            p->setFont(headerFont);
02180            headerHeight = (p->fontMetrics().lineSpacing() * 3) / 2;
02181         }
02182 
02183         // ok. now print the pages.
02184         kdDebug(6000) << "printing: html page width = " << root->docWidth()
02185                       << " height = " << root->docHeight() << endl;
02186         kdDebug(6000) << "printing: margins left = " << printer->margins().width()
02187                       << " top = " << printer->margins().height() << endl;
02188         kdDebug(6000) << "printing: paper width = " << metrics.width()
02189                       << " height = " << metrics.height() << endl;
02190         // if the width is too large to fit on the paper we just scale
02191         // the whole thing.
02192         int pageHeight = metrics.height();
02193         int pageWidth = metrics.width();
02194         p->setClipRect(0,0, pageWidth, pageHeight);
02195 
02196         pageHeight -= headerHeight;
02197 
02198         bool scalePage = false;
02199         double scale = 0.0;
02200 #ifndef QT_NO_TRANSFORMATIONS
02201         if(root->docWidth() > metrics.width()) {
02202             scalePage = true;
02203             scale = ((double) metrics.width())/((double) root->docWidth());
02204             pageHeight = (int) (pageHeight/scale);
02205             pageWidth = (int) (pageWidth/scale);
02206             headerHeight = (int) (headerHeight/scale);
02207         }
02208 #endif
02209         kdDebug(6000) << "printing: scaled html width = " << pageWidth
02210                       << " height = " << pageHeight << endl;
02211 
02212         // Squeeze header to make it it on the page.
02213         if (printHeader)
02214         {
02215             int available_width = metrics.width() - 10 -
02216                 2 * kMax(p->boundingRect(0, 0, metrics.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerLeft).width(),
02217                          p->boundingRect(0, 0, metrics.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerRight).width());
02218             if (available_width < 150)
02219                available_width = 150;
02220             int mid_width;
02221             int squeeze = 120;
02222             do {
02223                 headerMid = KStringHandler::csqueeze(docname, squeeze);
02224                 mid_width = p->boundingRect(0, 0, metrics.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerMid).width();
02225                 squeeze -= 10;
02226             } while (mid_width > available_width);
02227         }
02228 
02229         int top = 0;
02230         int page = 1;
02231         int bottom = 0;
02232         int oldbottom = 0;
02233         while(top < root->docHeight()) {
02234             if(top > 0) printer->newPage();
02235             if (printHeader)
02236             {
02237                 int dy = p->fontMetrics().lineSpacing();
02238                 p->setPen(Qt::black);
02239                 p->setFont(headerFont);
02240 
02241                 headerRight = QString("#%1").arg(page);
02242 
02243                 p->drawText(0, 0, metrics.width(), dy, Qt::AlignLeft, headerLeft);
02244                 p->drawText(0, 0, metrics.width(), dy, Qt::AlignHCenter, headerMid);
02245                 p->drawText(0, 0, metrics.width(), dy, Qt::AlignRight, headerRight);
02246             }
02247 
02248 #ifndef QT_NO_TRANSFORMATIONS
02249             if (scalePage)
02250                 p->scale(scale, scale);
02251 #endif
02252             p->translate(0, headerHeight-top);
02253 
02254             oldbottom = top+pageHeight;
02255             root->setTruncatedAt(oldbottom);
02256 
02257             root->layer()->paint(p, QRect(0, top, pageWidth, pageHeight));
02258             bottom = root->bestTruncatedAt();
02259             kdDebug(6000) << "printed: page " << page <<" truncatedAt = " << oldbottom
02260                           << " bestTruncatedAt = " << bottom << endl;
02261             if (bottom == 0) bottom = oldbottom;
02262 
02263             if (bottom >= root->docHeight())
02264                 break; // Stop if we have printed everything
02265 
02266             top = bottom;
02267             p->resetXForm();
02268             page++;
02269         }
02270 
02271         p->end();
02272         delete p;
02273 
02274         // and now reset the layout to the usual one...
02275         root->setPrintingMode(false);
02276         khtml::setPrintPainter( 0 );
02277         setMediaType( oldMediaType );
02278         m_part->xmlDocImpl()->setPaintDevice( this );
02279         m_part->xmlDocImpl()->styleSelector()->computeFontSizes(m_part->xmlDocImpl()->paintDeviceMetrics(), m_part->zoomFactor());
02280         m_part->xmlDocImpl()->updateStyleSelector();
02281         viewport()->unsetCursor();
02282     }
02283     delete printer;
02284 }
02285 
02286 void KHTMLView::slotPaletteChanged()
02287 {
02288     if(!m_part->xmlDocImpl()) return;
02289     DOM::DocumentImpl *document = m_part->xmlDocImpl();
02290     if (!document->isHTMLDocument()) return;
02291     khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(document->renderer());
02292     if(!root) return;
02293     root->style()->resetPalette();
02294     NodeImpl *body = static_cast<HTMLDocumentImpl*>(document)->body();
02295     if(!body) return;
02296     body->setChanged(true);
02297     body->recalcStyle( NodeImpl::Force );
02298 }
02299 
02300 void KHTMLView::paint(QPainter *p, const QRect &rc, int yOff, bool *more)
02301 {
02302     if(!m_part->xmlDocImpl()) return;
02303     khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer());
02304     if(!root) return;
02305 
02306     m_part->xmlDocImpl()->setPaintDevice(p->device());
02307     root->setPrintingMode(true);
02308     root->setWidth(rc.width());
02309 
02310     p->save();
02311     p->setClipRect(rc);
02312     p->translate(rc.left(), rc.top());
02313     double scale = ((double) rc.width()/(double) root->docWidth());
02314     int height = (int) ((double) rc.height() / scale);
02315 #ifndef QT_NO_TRANSFORMATIONS
02316     p->scale(scale, scale);
02317 #endif
02318 
02319     root->layer()->paint(p, QRect(0, yOff, root->docWidth(), height));
02320     if (more)
02321         *more = yOff + height < root->docHeight();
02322     p->restore();
02323 
02324     root->setPrintingMode(false);
02325     m_part->xmlDocImpl()->setPaintDevice( this );
02326 }
02327 
02328 
02329 void KHTMLView::useSlowRepaints()
02330 {
02331     d->useSlowRepaints = true;
02332     setStaticBackground(true);
02333 }
02334 
02335 
02336 void KHTMLView::setVScrollBarMode ( ScrollBarMode mode )
02337 {
02338 #ifndef KHTML_NO_SCROLLBARS
02339     d->vmode = mode;
02340     QScrollView::setVScrollBarMode(mode);
02341 #else
02342     Q_UNUSED( mode );
02343 #endif
02344 }
02345 
02346 void KHTMLView::setHScrollBarMode ( ScrollBarMode mode )
02347 {
02348 #ifndef KHTML_NO_SCROLLBARS
02349     d->hmode = mode;
02350     QScrollView::setHScrollBarMode(mode);
02351 #else
02352     Q_UNUSED( mode );
02353 #endif
02354 }
02355 
02356 void KHTMLView::restoreScrollBar()
02357 {
02358     int ow = visibleWidth();
02359     QScrollView::setVScrollBarMode(d->vmode);
02360     if (visibleWidth() != ow)
02361         layout();
02362     d->prevScrollbarVisible = verticalScrollBar()->isVisible();
02363 }
02364 
02365 QStringList KHTMLView::formCompletionItems(const QString &name) const
02366 {
02367     if (!m_part->settings()->isFormCompletionEnabled())
02368         return QStringList();
02369     if (!d->formCompletions)
02370         d->formCompletions = new KSimpleConfig(locateLocal("data", "khtml/formcompletions"));
02371     return d->formCompletions->readListEntry(name);
02372 }
02373 
02374 void KHTMLView::clearCompletionHistory(const QString& name)
02375 {
02376     if (!d->formCompletions)
02377     {
02378         d->formCompletions = new KSimpleConfig(locateLocal("data", "khtml/formcompletions"));
02379     }
02380     d->formCompletions->writeEntry(name, "");
02381     d->formCompletions->sync();
02382 }
02383 
02384 void KHTMLView::addFormCompletionItem(const QString &name, const QString &value)
02385 {
02386     if (!m_part->settings()->isFormCompletionEnabled())
02387         return;
02388     // don't store values that are all numbers or just numbers with
02389     // dashes or spaces as those are likely credit card numbers or
02390     // something similar
02391     bool cc_number(true);
02392     for (unsigned int i = 0; i < value.length(); ++i)
02393     {
02394       QChar c(value[i]);
02395       if (!c.isNumber() && c != '-' && !c.isSpace())
02396       {
02397         cc_number = false;
02398         break;
02399       }
02400     }
02401     if (cc_number)
02402       return;
02403     QStringList items = formCompletionItems(name);
02404     if (!items.contains(value))
02405         items.prepend(value);
02406     while ((int)items.count() > m_part->settings()->maxFormCompletionItems())
02407         items.remove(items.fromLast());
02408     d->formCompletions->writeEntry(name, items);
02409 }
02410 
02411 void KHTMLView::addNonPasswordStorableSite(const QString& host)
02412 {
02413     if (!d->formCompletions) {
02414         d->formCompletions = new KSimpleConfig(locateLocal("data", "khtml/formcompletions"));
02415     }
02416 
02417     d->formCompletions->setGroup("NonPasswordStorableSites");
02418     QStringList sites = d->formCompletions->readListEntry("Sites");
02419     sites.append(host);
02420     d->formCompletions->writeEntry("Sites", sites);
02421     d->formCompletions->sync();
02422     d->formCompletions->setGroup(QString::null);//reset
02423 }
02424 
02425 bool KHTMLView::nonPasswordStorableSite(const QString& host) const
02426 {
02427     if (!d->formCompletions) {
02428         d->formCompletions = new KSimpleConfig(locateLocal("data", "khtml/formcompletions"));
02429     }
02430     d->formCompletions->setGroup("NonPasswordStorableSites");
02431     QStringList sites =  d->formCompletions->readListEntry("Sites");
02432     d->formCompletions->setGroup(QString::null);//reset
02433 
02434     return (sites.find(host) != sites.end());
02435 }
02436 
02437 // returns true if event should be swallowed
02438 bool KHTMLView::dispatchMouseEvent(int eventId, DOM::NodeImpl *targetNode,
02439                    DOM::NodeImpl *targetNodeNonShared, bool cancelable,
02440                    int detail,QMouseEvent *_mouse, bool setUnder,
02441                    int mouseEventType)
02442 {
02443     if (d->underMouse)
02444     d->underMouse->deref();
02445     d->underMouse = targetNode;
02446     if (d->underMouse)
02447     d->underMouse->ref();
02448 
02449     if (d->underMouseNonShared)
02450     d->underMouseNonShared->deref();
02451     d->underMouseNonShared = targetNodeNonShared;
02452     if (d->underMouseNonShared)
02453     d->underMouseNonShared->ref();
02454 
02455     int exceptioncode = 0;
02456     int pageX = 0;
02457     int pageY = 0;
02458     viewportToContents(_mouse->x(), _mouse->y(), pageX, pageY);
02459     int clientX = pageX - contentsX();
02460     int clientY = pageY - contentsY();
02461     int screenX = _mouse->globalX();
02462     int screenY = _mouse->globalY();
02463     int button = -1;
02464     switch (_mouse->button()) {
02465     case LeftButton:
02466         button = 0;
02467         break;
02468     case MidButton:
02469         button = 1;
02470         break;
02471     case RightButton:
02472         button = 2;
02473         break;
02474     default:
02475         break;
02476     }
02477     if (d->accessKeysPreActivate && button!=-1)
02478         d->accessKeysPreActivate=false;
02479 
02480     bool ctrlKey = (_mouse->state() & ControlButton);
02481     bool altKey = (_mouse->state() & AltButton);
02482     bool shiftKey = (_mouse->state() & ShiftButton);
02483     bool metaKey = (_mouse->state() & MetaButton);
02484 
02485     // mouseout/mouseover
02486     if (setUnder && (d->prevMouseX != pageX || d->prevMouseY != pageY)) {
02487 
02488         // ### this code sucks. we should save the oldUnder instead of calculating
02489         // it again. calculating is expensive! (Dirk)
02490         NodeImpl *oldUnder = 0;
02491     if (d->prevMouseX >= 0 && d->prevMouseY >= 0) {
02492         NodeImpl::MouseEvent mev( _mouse->stateAfter(), static_cast<NodeImpl::MouseEventType>(mouseEventType));
02493         m_part->xmlDocImpl()->prepareMouseEvent( true, d->prevMouseX, d->prevMouseY, &mev );
02494         oldUnder = mev.innerNode.handle();
02495     }
02496 //  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());
02497     if (oldUnder != targetNode) {
02498         // send mouseout event to the old node
02499         if (oldUnder){
02500         oldUnder->ref();
02501         MouseEventImpl *me = new MouseEventImpl(EventImpl::MOUSEOUT_EVENT,
02502                             true,true,m_part->xmlDocImpl()->defaultView(),
02503                             0,screenX,screenY,clientX,clientY,pageX, pageY,
02504                             ctrlKey,altKey,shiftKey,metaKey,
02505                             button,targetNode);
02506         me->ref();
02507         oldUnder->dispatchEvent(me,exceptioncode,true);
02508         me->deref();
02509         }
02510 
02511         // send mouseover event to the new node
02512         if (targetNode) {
02513         MouseEventImpl *me = new MouseEventImpl(EventImpl::MOUSEOVER_EVENT,
02514                             true,true,m_part->xmlDocImpl()->defaultView(),
02515                             0,screenX,screenY,clientX,clientY,pageX, pageY,
02516                             ctrlKey,altKey,shiftKey,metaKey,
02517                             button,oldUnder);
02518 
02519         me->ref();
02520         targetNode->dispatchEvent(me,exceptioncode,true);
02521         me->deref();
02522         }
02523 
02524             if (oldUnder)
02525                 oldUnder->deref();
02526         }
02527     }
02528 
02529     bool swallowEvent = false;
02530 
02531     if (targetNode) {
02532         // send the actual event
02533         bool dblclick = ( eventId == EventImpl::CLICK_EVENT &&
02534                           _mouse->type() == QEvent::MouseButtonDblClick );
02535         MouseEventImpl *me = new MouseEventImpl(static_cast<EventImpl::EventId>(eventId),
02536                         true,cancelable,m_part->xmlDocImpl()->defaultView(),
02537                         detail,screenX,screenY,clientX,clientY,pageX, pageY,
02538                         ctrlKey,altKey,shiftKey,metaKey,
02539                         button,0, _mouse, dblclick );
02540         me->ref();
02541         targetNode->dispatchEvent(me,exceptioncode,true);
02542         if (me->defaultHandled() || me->defaultPrevented())
02543             swallowEvent = true;
02544         me->deref();
02545 
02546         if (eventId == EventImpl::MOUSEDOWN_EVENT) {
02547             if (targetNode->isFocusable())
02548                 m_part->xmlDocImpl()->setFocusNode(targetNode);
02549             else
02550                 m_part->xmlDocImpl()->setFocusNode(0);
02551         }
02552     }
02553 
02554     return swallowEvent;
02555 }
02556 
02557 void KHTMLView::setIgnoreWheelEvents( bool e )
02558 {
02559     d->ignoreWheelEvents = e;
02560 }
02561 
02562 #ifndef QT_NO_WHEELEVENT
02563 
02564 void KHTMLView::viewportWheelEvent(QWheelEvent* e)
02565 {
02566     if (d->accessKeysPreActivate) d->accessKeysPreActivate=false;
02567 
02568     if ( ( e->state() & ControlButton) == ControlButton )
02569     {
02570         emit zoomView( - e->delta() );
02571         e->accept();
02572     }
02573     else if ( ( (d->ignoreWheelEvents && !verticalScrollBar()->isVisible())
02574                 || e->delta() > 0 && contentsY() <= 0
02575                         || e->delta() < 0 && contentsY() >= contentsHeight() - visibleHeight())
02576                 && m_part->parentPart() ) {
02577        kdDebug(6000) << this << " cz " << contentsY() << " ch " << contentsHeight() << " vh " << visibleHeight() << endl;
02578         if ( m_part->parentPart()->view() )
02579             m_part->parentPart()->view()->wheelEvent( e );
02580         kdDebug(6000) << "sent" << endl;
02581         e->ignore();
02582     }
02583     else if ( d->vmode == QScrollView::AlwaysOff ) {
02584         e->accept();
02585     }
02586     else {
02587         d->scrollBarMoved = true;
02588         QScrollView::viewportWheelEvent( e );
02589 
02590         QMouseEvent *tempEvent = new QMouseEvent( QEvent::MouseMove, QPoint(-1,-1), QPoint(-1,-1), Qt::NoButton, e->state() );
02591         emit viewportMouseMoveEvent ( tempEvent );
02592         delete tempEvent;
02593     }
02594 
02595 }
02596 #endif
02597 
02598 void KHTMLView::dragEnterEvent( QDragEnterEvent* ev )
02599 {
02600     // Handle drops onto frames (#16820)
02601     // Drops on the main html part is handled by Konqueror (and shouldn't do anything
02602     // in e.g. kmail, so not handled here).
02603     if ( m_part->parentPart() )
02604     {
02605         QApplication::sendEvent(m_part->parentPart()->widget(), ev);
02606     return;
02607     }
02608     QScrollView::dragEnterEvent( ev );
02609 }
02610 
02611 void KHTMLView::dropEvent( QDropEvent *ev )
02612 {
02613     // Handle drops onto frames (#16820)
02614     // Drops on the main html part is handled by Konqueror (and shouldn't do anything
02615     // in e.g. kmail, so not handled here).
02616     if ( m_part->parentPart() )
02617     {
02618         QApplication::sendEvent(m_part->parentPart()->widget(), ev);
02619     return;
02620     }
02621     QScrollView::dropEvent( ev );
02622 }
02623 
02624 void KHTMLView::focusInEvent( QFocusEvent *e )
02625 {
02626 #ifndef KHTML_NO_CARET
02627     // Restart blink frequency timer if it has been killed, but only on
02628     // editable nodes
02629     if (d->m_caretViewContext &&
02630         d->m_caretViewContext->freqTimerId == -1 &&
02631         m_part->xmlDocImpl()) {
02632         NodeImpl *caretNode = m_part->xmlDocImpl()->focusNode();
02633         if (m_part->isCaretMode()
02634         || m_part->isEditable()
02635             || (caretNode && caretNode->renderer()
02636             && caretNode->renderer()->style()->userInput()
02637                 == UI_ENABLED)) {
02638             d->m_caretViewContext->freqTimerId = startTimer(500);
02639         d->m_caretViewContext->visible = true;
02640         }/*end if*/
02641     }/*end if*/
02642     showCaret();
02643 #endif // KHTML_NO_CARET
02644     QScrollView::focusInEvent( e );
02645 }
02646 
02647 void KHTMLView::focusOutEvent( QFocusEvent *e )
02648 {
02649     if(m_part) m_part->stopAutoScroll();
02650 
02651 #ifndef KHTML_NO_TYPE_AHEAD_FIND
02652     if(d->typeAheadActivated)
02653     {
02654         findTimeout();
02655     }
02656 #endif // KHTML_NO_TYPE_AHEAD_FIND
02657 
02658 #ifndef KHTML_NO_CARET
02659     if (d->m_caretViewContext) {
02660         switch (d->m_caretViewContext->displayNonFocused) {
02661     case KHTMLPart::CaretInvisible:
02662             hideCaret();
02663         break;
02664     case KHTMLPart::CaretVisible: {
02665         killTimer(d->m_caretViewContext->freqTimerId);
02666         d->m_caretViewContext->freqTimerId = -1;
02667             NodeImpl *caretNode = m_part->xmlDocImpl()->focusNode();
02668         if (!d->m_caretViewContext->visible && (m_part->isCaretMode()
02669         || m_part->isEditable()
02670             || (caretNode && caretNode->renderer()
02671             && caretNode->renderer()->style()->userInput()
02672                 == UI_ENABLED))) {
02673             d->m_caretViewContext->visible = true;
02674             showCaret(true);
02675         }/*end if*/
02676         break;
02677     }
02678     case KHTMLPart::CaretBlink:
02679         // simply leave as is
02680         break;
02681     }/*end switch*/
02682     }/*end if*/
02683 #endif // KHTML_NO_CARET
02684     QScrollView::focusOutEvent( e );
02685 }
02686 
02687 void KHTMLView::slotScrollBarMoved()
02688 {
02689     if ( !d->firstRelayout && !d->complete && m_part->xmlDocImpl() &&
02690           d->layoutSchedulingEnabled) {
02691         // contents scroll while we are not complete: we need to check our layout *now*
02692         khtml::RenderCanvas* root = static_cast<khtml::RenderCanvas *>( m_part->xmlDocImpl()->renderer() );
02693         if (root && root->needsLayout()) {
02694             unscheduleRelayout();
02695             layout();
02696         }
02697     }
02698     if (!d->scrollingSelf) {
02699         d->scrollBarMoved = true;
02700         d->contentsMoving = true;
02701         // ensure quick reset of contentsMoving flag
02702         scheduleRepaint(0, 0, 0, 0);
02703     }
02704 }
02705 
02706 void KHTMLView::timerEvent ( QTimerEvent *e )
02707 {
02708 //    kdDebug() << "timer event " << e->timerId() << endl;
02709     if ( e->timerId() == d->scrollTimerId ) {
02710         if( d->scrollSuspended )
02711             return;
02712         switch (d->scrollDirection) {
02713             case KHTMLViewPrivate::ScrollDown:
02714                 if (contentsY() + visibleHeight () >= contentsHeight())
02715                     d->newScrollTimer(this, 0);
02716                 else
02717                     scrollBy( 0, d->scrollBy );
02718                 break;
02719             case KHTMLViewPrivate::ScrollUp:
02720                 if (contentsY() <= 0)
02721                     d->newScrollTimer(this, 0);
02722                 else
02723                     scrollBy( 0, -d->scrollBy );
02724                 break;
02725             case KHTMLViewPrivate::ScrollRight:
02726                 if (contentsX() + visibleWidth () >= contentsWidth())
02727                     d->newScrollTimer(this, 0);
02728                 else
02729                     scrollBy( d->scrollBy, 0 );
02730                 break;
02731             case KHTMLViewPrivate::ScrollLeft:
02732                 if (contentsX() <= 0)
02733                     d->newScrollTimer(this, 0);
02734                 else
02735                     scrollBy( -d->scrollBy, 0 );
02736                 break;
02737         }
02738         return;
02739     }
02740     else if ( e->timerId() == d->layoutTimerId ) {
02741         d->firstRelayout = false;
02742         d->dirtyLayout = true;
02743         layout();
02744     }
02745 #ifndef KHTML_NO_CARET
02746     else if (d->m_caretViewContext
02747              && e->timerId() == d->m_caretViewContext->freqTimerId) {
02748         d->m_caretViewContext->visible = !d->m_caretViewContext->visible;
02749     if (d->m_caretViewContext->displayed) {
02750         updateContents(d->m_caretViewContext->x, d->m_caretViewContext->y,
02751             d->m_caretViewContext->width,
02752             d->m_caretViewContext->height);
02753     }/*end if*/
02754 //  if (d->m_caretViewContext->visible) cout << "|" << flush;
02755 //  else cout << "" << flush;
02756     return;
02757     }
02758 #endif
02759 
02760     d->contentsMoving = false;
02761     if( m_part->xmlDocImpl() ) {
02762     DOM::DocumentImpl *document = m_part->xmlDocImpl();
02763     khtml::RenderCanvas* root = static_cast<khtml::RenderCanvas *>(document->renderer());
02764 
02765     if ( root && root->needsLayout() ) {
02766         killTimer(d->repaintTimerId);
02767         d->repaintTimerId = 0;
02768         scheduleRelayout();
02769         return;
02770     }
02771     }
02772 
02773     setStaticBackground(d->useSlowRepaints);
02774 
02775 //        kdDebug() << "scheduled repaint "<< d->repaintTimerId  << endl;
02776     killTimer(d->repaintTimerId);
02777     d->repaintTimerId = 0;
02778 
02779     QRegion updateRegion;
02780     QMemArray<QRect> rects = d->updateRegion.rects();
02781 
02782     d->updateRegion = QRegion();
02783 
02784     if ( rects.size() )
02785         updateRegion = rects[0];
02786 
02787     for ( unsigned i = 1; i < rects.size(); ++i ) {
02788         QRect obR = updateRegion.boundingRect();
02789         QRegion newRegion = updateRegion.unite(rects[i]);
02790         if (2*newRegion.boundingRect().height() > 3*obR.height() )
02791         {
02792             repaintContents( obR );
02793             updateRegion = rects[i];
02794         }
02795         else
02796             updateRegion = newRegion;
02797     }
02798 
02799     if ( !updateRegion.isNull() )
02800         repaintContents( updateRegion.boundingRect() );
02801 
02802     if (d->dirtyLayout && !d->visibleWidgets.isEmpty()) {
02803         QWidget* w;
02804         d->dirtyLayout = false;
02805 
02806         QRect visibleRect(contentsX(), contentsY(), visibleWidth(), visibleHeight());
02807         QPtrList<RenderWidget> toRemove;
02808         for (QPtrDictIterator<QWidget> it(d->visibleWidgets); it.current(); ++it) {
02809             int xp = 0, yp = 0;
02810             w = it.current();
02811             RenderWidget* rw = static_cast<RenderWidget*>( it.currentKey() );
02812             if (!rw->absolutePosition(xp, yp) ||
02813                 !visibleRect.intersects(QRect(xp, yp, w->width(), w->height())))
02814                 toRemove.append(rw);
02815         }
02816         for (RenderWidget* r = toRemove.first(); r; r = toRemove.next())
02817             if ( (w = d->visibleWidgets.take(r) ) )
02818                 addChild(w, 0, -500000);
02819     }
02820     if (d->accessKeysActivated) emit repaintAccessKeys();
02821     if (d->emitCompletedAfterRepaint) {
02822         if (d->emitCompletedAfterRepaint == KHTMLViewPrivate::CSFull)
02823             emit m_part->completed();
02824         else
02825             emit m_part->completed(true);
02826         d->emitCompletedAfterRepaint = KHTMLViewPrivate::CSNone;
02827     }
02828 }
02829 
02830 void KHTMLView::scheduleRelayout(khtml::RenderObject * /*clippedObj*/)
02831 {
02832     if (!d->layoutSchedulingEnabled || d->layoutTimerId)
02833         return;
02834 
02835     d->layoutTimerId = startTimer( m_part->xmlDocImpl() && m_part->xmlDocImpl()->parsing()
02836                              ? 1000 : 0 );
02837 }
02838 
02839 void KHTMLView::unscheduleRelayout()
02840 {
02841     if (!d->layoutTimerId)
02842         return;
02843 
02844     killTimer(d->layoutTimerId);
02845     d->layoutTimerId = 0;
02846 }
02847 
02848 void KHTMLView::unscheduleRepaint()
02849 {
02850     if (!d->repaintTimerId)
02851         return;
02852 
02853     killTimer(d->repaintTimerId);
02854     d->repaintTimerId = 0;
02855 }
02856 
02857 void KHTMLView::scheduleRepaint(int x, int y, int w, int h, bool asap)
02858 {
02859     bool parsing = !m_part->xmlDocImpl() || m_part->xmlDocImpl()->parsing();
02860 
02861 //     kdDebug() << "parsing " << parsing << endl;
02862 //     kdDebug() << "complete " << d->complete << endl;
02863 
02864     int time = parsing ? 300 : (!asap ? ( !d->complete ? 100 : 20 ) : 0);
02865 
02866 #ifdef DEBUG_FLICKER
02867     QPainter p;
02868     p.begin( viewport() );
02869 
02870     int vx, vy;
02871     contentsToViewport( x, y, vx, vy );
02872     p.fillRect( vx, vy, w, h, Qt::red );
02873     p.end();
02874 #endif
02875 
02876     d->updateRegion = d->updateRegion.unite(QRect(x,y,w,h));
02877     
02878     if (asap && !parsing)
02879         unscheduleRelayout();
02880 
02881     if ( !d->repaintTimerId )
02882         d->repaintTimerId = startTimer( time );
02883 
02884 //     kdDebug() << "starting timer " << time << endl;
02885 }
02886 
02887 void KHTMLView::complete( bool pendingAction )
02888 {
02889 //     kdDebug() << "KHTMLView::complete()" << endl;
02890 
02891     d->complete = true;
02892 
02893     // is there a relayout pending?
02894     if (d->layoutTimerId)
02895     {
02896 //         kdDebug() << "requesting relayout now" << endl;
02897         // do it now
02898         killTimer(d->layoutTimerId);
02899         d->layoutTimerId = startTimer( 0 );
02900         d->emitCompletedAfterRepaint = pendingAction ?
02901             KHTMLViewPrivate::CSActionPending : KHTMLViewPrivate::CSFull;
02902     }
02903 
02904     // is there a repaint pending?
02905     if (d->repaintTimerId)
02906     {
02907 //         kdDebug() << "requesting repaint now" << endl;
02908         // do it now
02909         killTimer(d->repaintTimerId);
02910         d->repaintTimerId = startTimer( 20 );
02911         d->emitCompletedAfterRepaint = pendingAction ?
02912             KHTMLViewPrivate::CSActionPending : KHTMLViewPrivate::CSFull;
02913     }
02914 
02915     if (!d->emitCompletedAfterRepaint)
02916     {
02917         if (!pendingAction)
02918             emit m_part->completed();
02919         else
02920             emit m_part->completed(true);
02921     }
02922 
02923 }
02924 
02925 #ifndef KHTML_NO_CARET
02926 
02927 // ### the dependencies on static functions are a nightmare. just be
02928 // hacky and include the implementation here. Clean me up, please.
02929 
02930 #include "khtml_caret.cpp"
02931 
02932 void KHTMLView::initCaret(bool keepSelection)
02933 {
02934 #if DEBUG_CARETMODE > 0
02935   kdDebug(6200) << "begin initCaret" << endl;
02936 #endif
02937   // save caretMoved state as moveCaretTo changes it
02938   if (m_part->xmlDocImpl()) {
02939 #if 0
02940     ElementImpl *listitem = m_part->xmlDocImpl()->getElementById("__test_element__");
02941     if (listitem) dumpLineBoxes(static_cast<RenderFlow *>(listitem->renderer()));
02942 #endif
02943     d->caretViewContext();
02944     bool cmoved = d->m_caretViewContext->caretMoved;
02945     if (m_part->d->caretNode().isNull()) {
02946       // set to document, position will be sanitized anyway
02947       m_part->d->caretNode() = m_part->document();
02948       m_part->d->caretOffset() = 0L;
02949       // This sanity check is necessary for the not so unlikely case that
02950       // setEditable or setCaretMode is called before any render objects have
02951       // been created.
02952       if (!m_part->d->caretNode().handle()->renderer()) return;
02953     }/*end if*/
02954 //    kdDebug(6200) << "d->m_selectionStart " << m_part->d->m_selectionStart.handle()
02955 //          << " d->m_selectionEnd " << m_part->d->m_selectionEnd.handle() << endl;
02956     // ### does not repaint the selection on keepSelection!=false
02957     moveCaretTo(m_part->d->caretNode().handle(), m_part->d->caretOffset(), !keepSelection);
02958 //    kdDebug(6200) << "d->m_selectionStart " << m_part->d->m_selectionStart.handle()
02959 //          << " d->m_selectionEnd " << m_part->d->m_selectionEnd.handle() << endl;
02960     d->m_caretViewContext->caretMoved = cmoved;
02961   }/*end if*/
02962 #if DEBUG_CARETMODE > 0
02963   kdDebug(6200) << "end initCaret" << endl;
02964 #endif
02965 }
02966 
02967 bool KHTMLView::caretOverrides() const
02968 {
02969     bool cm = m_part->isCaretMode();
02970     bool dm = m_part->isEditable();
02971     return cm && !dm ? false
02972         : (dm || m_part->d->caretNode().handle()->contentEditable())
02973       && d->editorContext()->override;
02974 }
02975 
02976 void KHTMLView::ensureNodeHasFocus(NodeImpl *node)
02977 {
02978   if (m_part->isCaretMode() || m_part->isEditable()) return;
02979   if (node->focused()) return;
02980 
02981   // Find first ancestor whose "user-input" is "enabled"
02982   NodeImpl *firstAncestor = 0;
02983   while (node) {
02984     if (node->renderer()
02985        && node->renderer()->style()->userInput() != UI_ENABLED)
02986       break;
02987     firstAncestor = node;
02988     node = node->parentNode();
02989   }/*wend*/
02990 
02991   if (!node) firstAncestor = 0;
02992 
02993   DocumentImpl *doc = m_part->xmlDocImpl();
02994   // ensure that embedded widgets don't lose their focus
02995   if (!firstAncestor && doc->focusNode() && doc->focusNode()->renderer()
02996     && doc->focusNode()->renderer()->isWidget())
02997     return;
02998 
02999   // Set focus node on the document
03000 #if DEBUG_CARETMODE > 1
03001   kdDebug(6200) << k_funcinfo << "firstAncestor " << firstAncestor << ": "
03002     << (firstAncestor ? firstAncestor->nodeName().string() : QString::null) << endl;
03003 #endif
03004   doc->setFocusNode(firstAncestor);
03005   emit m_part->nodeActivated(Node(firstAncestor));
03006 }
03007 
03008 void KHTMLView::recalcAndStoreCaretPos(CaretBox *hintBox)
03009 {
03010     if (!m_part || m_part->d->caretNode().isNull()) return;
03011     d->caretViewContext();
03012     NodeImpl *caretNode = m_part->d->caretNode().handle();
03013 #if DEBUG_CARETMODE > 0
03014   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;
03015 #endif
03016     caretNode->getCaret(m_part->d->caretOffset(), caretOverrides(),
03017             d->m_caretViewContext->x, d->m_caretViewContext->y,
03018         d->m_caretViewContext->width,
03019         d->m_caretViewContext->height);
03020 
03021     if (hintBox && d->m_caretViewContext->x == -1) {
03022 #if DEBUG_CARETMODE > 1
03023         kdDebug(6200) << "using hint inline box coordinates" << endl;
03024 #endif
03025     RenderObject *r = caretNode->renderer();
03026     const QFontMetrics &fm = r->style()->fontMetrics();
03027         int absx, absy;
03028     r->containingBlock()->absolutePosition(absx, absy,
03029                         false); // ### what about fixed?
03030     d->m_caretViewContext->x = absx + hintBox->xPos();
03031     d->m_caretViewContext->y = absy + hintBox->yPos();
03032 //              + hintBox->baseline() - fm.ascent();
03033     d->m_caretViewContext->width = 1;
03034     // ### firstline not regarded. But I think it can be safely neglected
03035     // as hint boxes are only used for empty lines.
03036     d->m_caretViewContext->height = fm.height();
03037     }/*end if*/
03038 
03039 #if DEBUG_CARETMODE > 4
03040 //    kdDebug(6200) << "freqTimerId: "<<d->m_caretViewContext->freqTimerId<<endl;
03041 #endif
03042 #if DEBUG_CARETMODE > 0
03043     kdDebug(6200) << "caret: ofs="<<m_part->d->caretOffset()<<" "
03044         <<" x="<<d->m_caretViewContext->x<<" y="<<d->m_caretViewContext->y
03045     <<" h="<<d->m_caretViewContext->height<<endl;
03046 #endif
03047 }
03048 
03049 void KHTMLView::caretOn()
03050 {
03051     if (d->m_caretViewContext) {
03052         killTimer(d->m_caretViewContext->freqTimerId);
03053 
03054     if (hasFocus() || d->m_caretViewContext->displayNonFocused
03055             == KHTMLPart::CaretBlink) {
03056             d->m_caretViewContext->freqTimerId = startTimer(500);
03057     } else {
03058         d->m_caretViewContext->freqTimerId = -1;
03059     }/*end if*/
03060 
03061         d->m_caretViewContext->visible = true;
03062         if ((d->m_caretViewContext->displayed = (hasFocus()
03063         || d->m_caretViewContext->displayNonFocused
03064             != KHTMLPart::CaretInvisible))) {
03065         updateContents(d->m_caretViewContext->x, d->m_caretViewContext->y,
03066                 d->m_caretViewContext->width,
03067             d->m_caretViewContext->height);
03068     }/*end if*/
03069 //        kdDebug(6200) << "caret on" << endl;
03070     }/*end if*/
03071 }
03072 
03073 void KHTMLView::caretOff()
03074 {
03075     if (d->m_caretViewContext) {
03076         killTimer(d->m_caretViewContext->freqTimerId);
03077     d->m_caretViewContext->freqTimerId = -1;
03078         d->m_caretViewContext->displayed = false;
03079         if (d->m_caretViewContext->visible) {
03080             d->m_caretViewContext->visible = false;
03081         updateContents(d->m_caretViewContext->x, d->m_caretViewContext->y,
03082                 d->m_caretViewContext->width,
03083                 d->m_caretViewContext->height);
03084     }/*end if*/
03085 //        kdDebug(6200) << "caret off" << endl;
03086     }/*end if*/
03087 }
03088 
03089 void KHTMLView::showCaret(bool forceRepaint)
03090 {
03091     if (d->m_caretViewContext) {
03092         d->m_caretViewContext->displayed = true;
03093         if (d->m_caretViewContext->visible) {
03094         if (!forceRepaint) {
03095             updateContents(d->m_caretViewContext->x, d->m_caretViewContext->y,
03096                 d->m_caretViewContext->width,
03097             d->m_caretViewContext->height);
03098             } else {
03099             repaintContents(d->m_caretViewContext->x, d->m_caretViewContext->y,
03100                 d->m_caretViewContext->width,
03101                 d->m_caretViewContext->height);
03102         }/*end if*/
03103     }/*end if*/
03104 //        kdDebug(6200) << "caret shown" << endl;
03105     }/*end if*/
03106 }
03107 
03108 bool KHTMLView::foldSelectionToCaret(NodeImpl *startNode, long startOffset,
03109                     NodeImpl *endNode, long endOffset)
03110 {
03111   m_part->d->m_selectionStart = m_part->d->m_selectionEnd = m_part->d->caretNode();
03112   m_part->d->m_startOffset = m_part->d->m_endOffset = m_part->d->caretOffset();
03113   m_part->d->m_extendAtEnd = true;
03114 
03115   bool folded = startNode != endNode || startOffset != endOffset;
03116 
03117   // Only clear the selection if there has been one.
03118   if (folded) {
03119     m_part->xmlDocImpl()->clearSelection();
03120   }/*end if*/
03121 
03122   return folded;
03123 }
03124 
03125 void KHTMLView::hideCaret()
03126 {
03127     if (d->m_caretViewContext) {
03128         if (d->m_caretViewContext->visible) {
03129 //            kdDebug(6200) << "redraw caret hidden" << endl;
03130         d->m_caretViewContext->visible = false;
03131         // force repaint, otherwise the event won't be handled
03132         // before the focus leaves the window
03133         repaintContents(d->m_caretViewContext->x, d->m_caretViewContext->y,
03134                 d->m_caretViewContext->width,
03135                 d->m_caretViewContext->height);
03136         d->m_caretViewContext->visible = true;
03137     }/*end if*/
03138         d->m_caretViewContext->displayed = false;
03139 //        kdDebug(6200) << "caret hidden" << endl;
03140     }/*end if*/
03141 }
03142 
03143 int KHTMLView::caretDisplayPolicyNonFocused() const
03144 {
03145   if (d->m_caretViewContext)
03146     return d->m_caretViewContext->displayNonFocused;
03147   else
03148     return KHTMLPart::CaretInvisible;
03149 }
03150 
03151 void KHTMLView::setCaretDisplayPolicyNonFocused(int policy)
03152 {
03153   d->caretViewContext();
03154 //  int old = d->m_caretViewContext->displayNonFocused;
03155   d->m_caretViewContext->displayNonFocused = (KHTMLPart::CaretDisplayPolicy)policy;
03156 
03157   // make change immediately take effect if not focused
03158   if (!hasFocus()) {
03159     switch (d->m_caretViewContext->displayNonFocused) {
03160       case KHTMLPart::CaretInvisible:
03161         hideCaret();
03162     break;
03163       case KHTMLPart::CaretBlink:
03164     if (d->m_caretViewContext->freqTimerId != -1) break;
03165     d->m_caretViewContext->freqTimerId = startTimer(500);
03166     // fall through
03167       case KHTMLPart::CaretVisible:
03168         d->m_caretViewContext->displayed = true;
03169         showCaret();
03170     break;
03171     }/*end switch*/
03172   }/*end if*/
03173 }
03174 
03175 bool KHTMLView::placeCaret(CaretBox *hintBox)
03176 {
03177   CaretViewContext *cv = d->caretViewContext();
03178   caretOff();
03179   NodeImpl *caretNode = m_part->d->caretNode().handle();
03180   // ### why is it sometimes null?
03181   if (!caretNode || !caretNode->renderer()) return false;
03182   ensureNodeHasFocus(caretNode);
03183   if (m_part->isCaretMode() || m_part->isEditable()
03184      || caretNode->renderer()->style()->userInput() == UI_ENABLED) {
03185     recalcAndStoreCaretPos(hintBox);
03186 
03187     cv->origX = cv->x;
03188 
03189     caretOn();
03190     return true;
03191   }/*end if*/
03192   return false;
03193 }
03194 
03195 void KHTMLView::ensureCaretVisible()
03196 {
03197   CaretViewContext *cv = d->m_caretViewContext;
03198   if (!cv) return;
03199   ensureVisible(cv->x, cv->y, cv->width, cv->height);
03200   d->scrollBarMoved = false;
03201 }
03202 
03203 bool KHTMLView::extendSelection(NodeImpl *oldStartSel, long oldStartOfs,
03204                 NodeImpl *oldEndSel, long oldEndOfs)
03205 {
03206   bool changed = false;
03207   if (m_part->d->m_selectionStart == m_part->d->m_selectionEnd
03208       && m_part->d->m_startOffset == m_part->d->m_endOffset) {
03209     changed = foldSelectionToCaret(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs);
03210     m_part->d->m_extendAtEnd = true;
03211   } else do {
03212     changed = m_part->d->m_selectionStart.handle() != oldStartSel
03213             || m_part->d->m_startOffset != oldStartOfs
03214         || m_part->d->m_selectionEnd.handle() != oldEndSel
03215         || m_part->d->m_endOffset != oldEndOfs;
03216     if (!changed) break;
03217 
03218     // determine start position -- caret position is always at end.
03219     NodeImpl *startNode;
03220     long startOffset;
03221     if (m_part->d->m_extendAtEnd) {
03222       startNode = m_part->d->m_selectionStart.handle();
03223       startOffset = m_part->d->m_startOffset;
03224     } else {
03225       startNode = m_part->d->m_selectionEnd.handle();
03226       startOffset = m_part->d->m_endOffset;
03227       m_part->d->m_selectionEnd = m_part->d->m_selectionStart;
03228       m_part->d->m_endOffset = m_part->d->m_startOffset;
03229       m_part->d->m_extendAtEnd = true;
03230     }/*end if*/
03231 
03232     bool swapNeeded = false;
03233     if (!m_part->d->m_selectionEnd.isNull() && startNode) {
03234       swapNeeded = RangeImpl::compareBoundaryPoints(startNode, startOffset,
03235                 m_part->d->m_selectionEnd.handle(),
03236             m_part->d->m_endOffset) >= 0;
03237     }/*end if*/
03238 
03239     m_part->d->m_selectionStart = startNode;
03240     m_part->d->m_startOffset = startOffset;
03241 
03242     if (swapNeeded) {
03243       m_part->xmlDocImpl()->setSelection(m_part->d->m_selectionEnd.handle(),
03244         m_part->d->m_endOffset, m_part->d->m_selectionStart.handle(),
03245         m_part->d->m_startOffset);
03246     } else {
03247       m_part->xmlDocImpl()->setSelection(m_part->d->m_selectionStart.handle(),
03248         m_part->d->m_startOffset, m_part->d->m_selectionEnd.handle(),
03249         m_part->d->m_endOffset);
03250     }/*end if*/
03251   } while(false);/*end if*/
03252   return changed;
03253 }
03254 
03255 void KHTMLView::updateSelection(NodeImpl *oldStartSel, long oldStartOfs,
03256                 NodeImpl *oldEndSel, long oldEndOfs)
03257 {
03258   if (m_part->d->m_selectionStart == m_part->d->m_selectionEnd
03259       && m_part->d->m_startOffset == m_part->d->m_endOffset) {
03260     if (foldSelectionToCaret(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs)) {
03261       m_part->emitSelectionChanged();
03262     }/*end if*/
03263     m_part->d->m_extendAtEnd = true;
03264   } else {
03265     // check if the extending end has passed the immobile end
03266     if (!m_part->d->m_selectionEnd.isNull() && !m_part->d->m_selectionEnd.isNull()) {
03267       bool swapNeeded = RangeImpl::compareBoundaryPoints(
03268                 m_part->d->m_selectionStart.handle(), m_part->d->m_startOffset,
03269             m_part->d->m_selectionEnd.handle(), m_part->d->m_endOffset) >= 0;
03270       if (swapNeeded) {
03271         DOM::Node tmpNode = m_part->d->m_selectionStart;
03272         long tmpOffset = m_part->d->m_startOffset;
03273         m_part->d->m_selectionStart = m_part->d->m_selectionEnd;
03274         m_part->d->m_startOffset = m_part->d->m_endOffset;
03275         m_part->d->m_selectionEnd = tmpNode;
03276         m_part->d->m_endOffset = tmpOffset;
03277         m_part->d->m_startBeforeEnd = true;
03278         m_part->d->m_extendAtEnd = !m_part->d->m_extendAtEnd;
03279       }/*end if*/
03280     }/*end if*/
03281 
03282     m_part->xmlDocImpl()->setSelection(m_part->d->m_selectionStart.handle(),
03283         m_part->d->m_startOffset, m_part->d->m_selectionEnd.handle(),
03284         m_part->d->m_endOffset);
03285     m_part->emitSelectionChanged();
03286   }/*end if*/
03287 }
03288 
03289 void KHTMLView::caretKeyPressEvent(QKeyEvent *_ke)
03290 {
03291   NodeImpl *oldStartSel = m_part->d->m_selectionStart.handle();
03292   long oldStartOfs = m_part->d->m_startOffset;
03293   NodeImpl *oldEndSel = m_part->d->m_selectionEnd.handle();
03294   long oldEndOfs = m_part->d->m_endOffset;
03295 
03296   NodeImpl *oldCaretNode = m_part->d->caretNode().handle();
03297   long oldOffset = m_part->d->caretOffset();
03298 
03299   bool ctrl = _ke->state() & ControlButton;
03300 
03301 // FIXME: this is that widely indented because I will write ifs around it.
03302       switch(_ke->key()) {
03303         case Key_Space:
03304           break;
03305 
03306         case Key_Down:
03307       moveCaretNextLine(1);
03308           break;
03309 
03310         case Key_Up:
03311       moveCaretPrevLine(1);
03312           break;
03313 
03314         case Key_Left:
03315       moveCaretBy(false, ctrl ? CaretByWord : CaretByCharacter, 1);
03316           break;
03317 
03318         case Key_Right:
03319       moveCaretBy(true, ctrl ? CaretByWord : CaretByCharacter, 1);
03320           break;
03321 
03322         case Key_Next:
03323       moveCaretNextPage();
03324           break;
03325 
03326         case Key_Prior:
03327       moveCaretPrevPage();
03328           break;
03329 
03330         case Key_Home:
03331       if (ctrl)
03332         moveCaretToDocumentBoundary(false);
03333       else
03334         moveCaretToLineBegin();
03335           break;
03336 
03337         case Key_End:
03338       if (ctrl)
03339         moveCaretToDocumentBoundary(true);
03340       else
03341         moveCaretToLineEnd();
03342           break;
03343 
03344       }/*end switch*/
03345 
03346   if ((m_part->d->caretNode().handle() != oldCaretNode
03347     || m_part->d->caretOffset() != oldOffset)
03348     // node should never be null, but faulty conditions may cause it to be
03349     && !m_part->d->caretNode().isNull()) {
03350 
03351     d->m_caretViewContext->caretMoved = true;
03352 
03353     if (_ke->state() & ShiftButton) {   // extend selection
03354       updateSelection(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs);
03355     } else {            // clear any selection
03356       if (foldSelectionToCaret(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs))
03357         m_part->emitSelectionChanged();
03358     }/*end if*/
03359 
03360     m_part->emitCaretPositionChanged(m_part->d->caretNode(), m_part->d->caretOffset());
03361   }/*end if*/
03362 
03363   _ke->accept();
03364 }
03365 
03366 bool KHTMLView::moveCaretTo(NodeImpl *node, long offset, bool clearSel)
03367 {
03368   if (!node) return false;
03369   ElementImpl *baseElem = determineBaseElement(node);
03370   RenderFlow *base = static_cast<RenderFlow *>(baseElem ? baseElem->renderer() : 0);
03371   if (!node) return false;
03372 
03373   // need to find out the node's inline box. If there is none, this function
03374   // will snap to the next node that has one. This is necessary to make the
03375   // caret visible in any case.
03376   CaretBoxLineDeleter cblDeleter;
03377 //   RenderBlock *cb;
03378   long r_ofs;
03379   CaretBoxIterator cbit;
03380   CaretBoxLine *cbl = findCaretBoxLine(node, offset, &cblDeleter, base, r_ofs, cbit);
03381   if(!cbl) {
03382       kdWarning() << "KHTMLView::moveCaretTo - findCaretBoxLine() returns NULL" << endl;
03383       return false;
03384   }
03385 
03386 #if DEBUG_CARETMODE > 3
03387   if (cbl) kdDebug(6200) << cbl->information() << endl;
03388 #endif
03389   CaretBox *box = *cbit;
03390   if (cbit != cbl->end() && box->object() != node->renderer()) {
03391     if (box->object()->element()) {
03392       mapRenderPosToDOMPos(box->object(), r_ofs, box->isOutside(),
03393                 box->isOutsideEnd(), node, offset);
03394       //if (!outside) offset = node->minOffset();
03395 #if DEBUG_CARETMODE > 1
03396       kdDebug(6200) << "set new node " << node->nodeName().string() << "@" << node << endl;
#endif
    } else {    // box has no associated element -> do not use
      // this case should actually never happen.
      box = 0;
      kdError(6200) << "Box contains no node! Crash imminent" << endl;
03397     }/*end if*/
03398   }
03399 
03400   NodeImpl *oldStartSel = m_part->d->m_selectionStart.handle();
03401   long oldStartOfs = m_part->d->m_startOffset;
03402   NodeImpl *oldEndSel = m_part->d->m_selectionEnd.handle();
03403   long oldEndOfs = m_part->d->m_endOffset;
03404 
03405   // test for position change
03406   bool posChanged = m_part->d->caretNode().handle() != node
03407         || m_part->d->caretOffset() != offset;
03408   bool selChanged = false;
03409 
03410   m_part->d->caretNode() = node;
03411   m_part->d->caretOffset() = offset;
03412   if (clearSel || !oldStartSel || !oldEndSel) {
03413     selChanged = foldSelectionToCaret(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs);
03414   } else {
03415     //kdDebug(6200) << "moveToCaret: extendSelection: m_extendAtEnd " << m_part->d->m_extendAtEnd << endl;
03416     //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;
03417     selChanged = extendSelection(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs);
03418     //kdDebug(6200) << "after extendSelection: m_extendAtEnd " << m_part->d->m_extendAtEnd << endl;
03419     //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;
03420   }/*end if*/
03421 
03422   d->caretViewContext()->caretMoved = true;
03423 
03424   bool visible_caret = placeCaret(box);
03425 
03426   // FIXME: if the old position was !visible_caret, and the new position is
03427   // also, then two caretPositionChanged signals with a null Node are
03428   // emitted in series.
03429   if (posChanged) {
03430     m_part->emitCaretPositionChanged(visible_caret ? node : 0, offset);
03431   }/*end if*/
03432 
03433   return selChanged;
03434 }
03435 
03436 void KHTMLView::moveCaretByLine(bool next, int count)
03437 {
03438   Node &caretNodeRef = m_part->d->caretNode();
03439   if (caretNodeRef.isNull()) return;
03440 
03441   NodeImpl *caretNode = caretNodeRef.handle();
03442 //  kdDebug(6200) << ": caretNode=" << caretNode << endl;
03443   long offset = m_part->d->caretOffset();
03444 
03445   CaretViewContext *cv = d->caretViewContext();
03446 
03447   ElementImpl *baseElem = determineBaseElement(caretNode);
03448   LinearDocument ld(m_part, caretNode, offset, LeafsOnly, baseElem);
03449 
03450   ErgonomicEditableLineIterator it(ld.current(), cv->origX);
03451 
03452   // move count lines vertically
03453   while (count > 0 && it != ld.end() && it != ld.preBegin()) {
03454     count--;
03455     if (next) ++it; else --it;
03456   }/*wend*/
03457 
03458   // Nothing? Then leave everything as is.
03459   if (it == ld.end() || it == ld.preBegin()) return;
03460 
03461   int x, absx, absy;
03462   CaretBox *caretBox = nearestCaretBox(it, d->m_caretViewContext, x, absx, absy);
03463 
03464   placeCaretOnLine(caretBox, x, absx, absy);
03465 }
03466 
03467 void KHTMLView::placeCaretOnLine(CaretBox *caretBox, int x, int absx, int absy)
03468 {
03469   // paranoia sanity check
03470   if (!caretBox) return;
03471 
03472   RenderObject *caretRender = caretBox->object();
03473 
03474 #if DEBUG_CARETMODE > 0
03475   kdDebug(6200) << "got valid caretBox " << caretBox << endl;
03476   kdDebug(6200) << "xPos: " << caretBox->xPos() << " yPos: " << caretBox->yPos()
03477         << " width: " << caretBox->width() << " height: " << caretBox->height() << endl;
03478   InlineTextBox *tb = static_cast<InlineTextBox *>(caretBox->inlineBox());
03479   if (caretBox->isInlineTextBox()) { kdDebug(6200) << "contains \"" << QString(static_cast<RenderText *>(tb->object())->str->s + tb->m_start, tb->m_len) << "\"" << endl;}
03480 #endif
03481   // inquire height of caret
03482   int caretHeight = caretBox->height();
03483   bool isText = caretBox->isInlineTextBox();
03484   int yOfs = 0;     // y-offset for text nodes
03485   if (isText) {
03486     // text boxes need extrawurst
03487     RenderText *t = static_cast<RenderText *>(caretRender);
03488     const QFontMetrics &fm = t->metrics(caretBox->inlineBox()->m_firstLine);
03489     caretHeight = fm.height();
03490     yOfs = caretBox->inlineBox()->baseline() - fm.ascent();
03491   }/*end if*/
03492 
03493   caretOff();
03494 
03495   // set new caret node
03496   NodeImpl *caretNode;
03497   long &offset = m_part->d->caretOffset();
03498   mapRenderPosToDOMPos(caretRender, offset, caretBox->isOutside(),
03499         caretBox->isOutsideEnd(), caretNode, offset);
03500 
03501   // set all variables not needing special treatment
03502   d->m_caretViewContext->y = caretBox->yPos() + yOfs;
03503   d->m_caretViewContext->height = caretHeight;
03504   d->m_caretViewContext->width = 1; // FIXME: regard override
03505 
03506   int xPos = caretBox->xPos();
03507   int caretBoxWidth = caretBox->width();
03508   d->m_caretViewContext->x = xPos;
03509 
03510   if (!caretBox->isOutside()) {
03511     // before or at beginning of inline box -> place at beginning
03512     long r_ofs = 0;
03513     if (x <= xPos) {
03514       r_ofs = caretBox->minOffset();
03515   // somewhere within this block
03516     } else if (x > xPos && x <= xPos + caretBoxWidth) {
03517       if (isText) { // find out where exactly
03518         r_ofs = static_cast<InlineTextBox *>(caretBox->inlineBox())
03519             ->offsetForPoint(x, d->m_caretViewContext->x);
03520 #if DEBUG_CARETMODE > 2
03521         kdDebug(6200) << "deviation from origX " << d->m_caretViewContext->x - x << endl;
03522 #endif
03523 #if 0
03524       } else {  // snap to nearest end
03525         if (xPos + caretBoxWidth - x < x - xPos) {
03526           d->m_caretViewContext->x = xPos + caretBoxWidth;
03527           r_ofs = caretNode ? caretNode->maxOffset() : 1;
03528         } else {
03529           d->m_caretViewContext->x = xPos;
03530           r_ofs = caretNode ? caretNode->minOffset() : 0;
03531         }/*end if*/
03532 #endif
03533       }/*end if*/
03534     } else {        // after the inline box -> place at end
03535       d->m_caretViewContext->x = xPos + caretBoxWidth;
03536       r_ofs = caretBox->maxOffset();
03537     }/*end if*/
03538     offset = r_ofs;
03539   }/*end if*/
03540 #if DEBUG_CARETMODE > 0
03541       kdDebug(6200) << "new offset: " << offset << endl;
03542 #endif
03543 
03544   m_part->d->caretNode() = caretNode;
03545   m_part->d->caretOffset() = offset;
03546 
03547   d->m_caretViewContext->x += absx;
03548   d->m_caretViewContext->y += absy;
03549 
03550 #if DEBUG_CARETMODE > 1
03551     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;
03552 #endif
03553 
03554   ensureVisible(d->m_caretViewContext->x, d->m_caretViewContext->y,
03555     d->m_caretViewContext->width, d->m_caretViewContext->height);
03556   d->scrollBarMoved = false;
03557 
03558   ensureNodeHasFocus(caretNode);
03559   caretOn();
03560 }
03561 
03562 void KHTMLView::moveCaretToLineBoundary(bool end)
03563 {
03564   Node &caretNodeRef = m_part->d->caretNode();
03565   if (caretNodeRef.isNull()) return;
03566 
03567   NodeImpl *caretNode = caretNodeRef.handle();
03568 //  kdDebug(6200) << ": caretNode=" << caretNode << endl;
03569   long offset = m_part->d->caretOffset();
03570 
03571   ElementImpl *baseElem = determineBaseElement(caretNode);
03572   LinearDocument ld(m_part, caretNode, offset, LeafsOnly, baseElem);
03573 
03574   EditableLineIterator it = ld.current();
03575   if (it == ld.end()) return;   // should not happen, but who knows
03576 
03577   EditableCaretBoxIterator fbit(it, end);
03578   Q_ASSERT(fbit != (*it)->end() && fbit != (*it)->preBegin());
03579   CaretBox *b = *fbit;
03580 
03581   RenderObject *cb = b->containingBlock();
03582   int absx, absy;
03583 
03584   if (cb) cb->absolutePosition(absx,absy);
03585   else absx = absy = 0;
03586 
03587   int x = b->xPos() + (end && !b->isOutside() ? b->width() : 0);
03588   d->m_caretViewContext->origX = absx + x;
03589   placeCaretOnLine(b, x, absx, absy);
03590 }
03591 
03592 void KHTMLView::moveCaretToDocumentBoundary(bool end)
03593 {
03594   Node &caretNodeRef = m_part->d->caretNode();
03595   if (caretNodeRef.isNull()) return;
03596 
03597   NodeImpl *caretNode = caretNodeRef.handle();
03598 //  kdDebug(6200) << ": caretNode=" << caretNode << endl;
03599   long offset = m_part->d->caretOffset();
03600 
03601   ElementImpl *baseElem = determineBaseElement(caretNode);
03602   LinearDocument ld(m_part, caretNode, offset, IndicatedFlows, baseElem);
03603 
03604   EditableLineIterator it(end ? ld.preEnd() : ld.begin(), end);
03605   if (it == ld.end() || it == ld.preBegin()) return;    // should not happen, but who knows
03606 
03607   EditableCaretBoxIterator fbit = it;
03608   Q_ASSERT(fbit != (*it)->end() && fbit != (*it)->preBegin());
03609   CaretBox *b = *fbit;
03610 
03611   RenderObject *cb = (*it)->containingBlock();
03612   int absx, absy;
03613 
03614   if (cb) cb->absolutePosition(absx, absy);
03615   else absx = absy = 0;
03616 
03617   int x = b->xPos()/* + (end ? b->width() : 0) reactivate for rtl*/;
03618   d->m_caretViewContext->origX = absx + x;
03619   placeCaretOnLine(b, x, absx, absy);
03620 }
03621 
03622 void KHTMLView::moveCaretBy(bool next, CaretMovement cmv, int count)
03623 {
03624   if (!m_part) return;
03625   Node &caretNodeRef = m_part->d->caretNode();
03626   if (caretNodeRef.isNull()) return;
03627 
03628   NodeImpl *caretNode = caretNodeRef.handle();
03629 //  kdDebug(6200) << ": caretNode=" << caretNode << endl;
03630   long &offset = m_part->d->caretOffset();
03631 
03632   ElementImpl *baseElem = determineBaseElement(caretNode);
03633   CaretAdvancePolicy advpol = cmv != CaretByWord ? IndicatedFlows : LeafsOnly;
03634   LinearDocument ld(m_part, caretNode, offset, advpol, baseElem);
03635 
03636   EditableCharacterIterator it(&ld);
03637   while (!it.isEnd() && count > 0) {
03638     count--;
03639     if (cmv == CaretByCharacter) {
03640       if (next) ++it;
03641       else --it;
03642     } else if (cmv == CaretByWord) {
03643       if (next) moveItToNextWord(it);
03644       else moveItToPrevWord(it);
03645     }/*end if*/
03646 //kdDebug(6200) << "movecaret" << endl;
03647   }/*wend*/
03648   CaretBox *hintBox = 0;    // make gcc uninit warning disappear
03649   if (!it.isEnd()) {
03650     NodeImpl *node = caretNodeRef.handle();
03651     hintBox = it.caretBox();
03652 //kdDebug(6200) << "hintBox = " << hintBox << endl;
03653 //kdDebug(6200) << " outside " << hintBox->isOutside() << " outsideEnd " << hintBox->isOutsideEnd() << " r " << it.renderer() << " ofs " << it.offset() << " cb " << hintBox->containingBlock() << endl;
03654     mapRenderPosToDOMPos(it.renderer(), it.offset(), hintBox->isOutside(),
03655             hintBox->isOutsideEnd(), node, offset);
03656 //kdDebug(6200) << "mapRTD" << endl;
03657     caretNodeRef = node;
03658 #if DEBUG_CARETMODE > 2
03659     kdDebug(6200) << "set by valid node " << node << " " << (node?node->nodeName().string():QString::null) << " offset: " << offset << endl;
03660 #endif
03661   } else {
03662     offset = next ? caretNode->maxOffset() : caretNode->minOffset();
03663 #if DEBUG_CARETMODE > 0
03664     kdDebug(6200) << "set by INvalid node. offset: " << offset << endl;
03665 #endif
03666   }/*end if*/
03667   placeCaretOnChar(hintBox);
03668 }
03669 
03670 void KHTMLView::placeCaretOnChar(CaretBox *hintBox)
03671 {
03672   caretOff();
03673   recalcAndStoreCaretPos(hintBox);
03674   ensureVisible(d->m_caretViewContext->x, d->m_caretViewContext->y,
03675     d->m_caretViewContext->width, d->m_caretViewContext->height);
03676   d->m_caretViewContext->origX = d->m_caretViewContext->x;
03677   d->scrollBarMoved = false;
03678 #if DEBUG_CARETMODE > 3
03679   //if (caretNode->isTextNode())  kdDebug(6200) << "text[0] = " << (int)*((TextImpl *)caretNode)->data().unicode() << " text :\"" << ((TextImpl *)caretNode)->data().string() << "\"" << endl;
03680 #endif
03681   ensureNodeHasFocus(m_part->d->caretNode().handle());
03682   caretOn();
03683 }
03684 
03685 void KHTMLView::moveCaretByPage(bool next)
03686 {
03687   Node &caretNodeRef = m_part->d->caretNode();
03688 
03689   NodeImpl *caretNode = caretNodeRef.handle();
03690 //  kdDebug(6200) << ": caretNode=" << caretNode << endl;
03691   long offset = m_part->d->caretOffset();
03692 
03693   int offs = (clipper()->height() < 30) ? clipper()->height() : 30;
03694   // Minimum distance the caret must be moved
03695   int mindist = clipper()->height() - offs;
03696 
03697   CaretViewContext *cv = d->caretViewContext();
03698 //  int y = cv->y;      // we always measure the top border
03699 
03700   ElementImpl *baseElem = determineBaseElement(caretNode);
03701   LinearDocument ld(m_part, caretNode, offset, LeafsOnly, baseElem);
03702 
03703   ErgonomicEditableLineIterator it(ld.current(), cv->origX);
03704 
03705   moveIteratorByPage(ld, it, mindist, next);
03706 
03707   int x, absx, absy;
03708   CaretBox *caretBox = nearestCaretBox(it, d->m_caretViewContext, x, absx, absy);
03709 
03710   placeCaretOnLine(caretBox, x, absx, absy);
03711 }
03712 
03713 void KHTMLView::moveCaretPrevWord()
03714 {
03715   moveCaretBy(false, CaretByWord, 1);
03716 }
03717 
03718 void KHTMLView::moveCaretNextWord()
03719 {
03720   moveCaretBy(true, CaretByWord, 1);
03721 }
03722 
03723 void KHTMLView::moveCaretPrevLine(int n)
03724 {
03725   moveCaretByLine(false, n);
03726 }
03727 
03728 void KHTMLView::moveCaretNextLine(int n)
03729 {
03730   moveCaretByLine(true, n);
03731 }
03732 
03733 void KHTMLView::moveCaretPrevPage()
03734 {
03735   moveCaretByPage(false);
03736 }
03737 
03738 void KHTMLView::moveCaretNextPage()
03739 {
03740   moveCaretByPage(true);
03741 }
03742 
03743 void KHTMLView::moveCaretToLineBegin()
03744 {
03745   moveCaretToLineBoundary(false);
03746 }
03747 
03748 void KHTMLView::moveCaretToLineEnd()
03749 {
03750   moveCaretToLineBoundary(true);
03751 }
03752 
03753 #endif // KHTML_NO_CARET
03754 
03755 #undef DEBUG_CARETMODE
03756 
KDE Logo
This file is part of the documentation for khtml Library Version 3.3.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Jul 22 10:18:34 2005 by doxygen 1.3.6 written by Dimitri van Heesch, © 1997-2003