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     DOM::NodeImpl* fn = m_part->xmlDocImpl() ? m_part->xmlDocImpl()->focusNode() : 0;
02627     if (fn && fn->renderer() && fn->renderer()->isWidget() && 
02628         (e->reason() != QFocusEvent::Mouse) &&
02629         static_cast<khtml::RenderWidget*>(fn->renderer())->widget())
02630         static_cast<khtml::RenderWidget*>(fn->renderer())->widget()->setFocus();
02631 #ifndef KHTML_NO_CARET
02632     // Restart blink frequency timer if it has been killed, but only on
02633     // editable nodes
02634     if (d->m_caretViewContext &&
02635         d->m_caretViewContext->freqTimerId == -1 &&
02636         fn) {
02637         if (m_part->isCaretMode()
02638         || m_part->isEditable()
02639                 || (fn && fn->renderer()
02640                         && fn->renderer()->style()->userInput()             
02641                 == UI_ENABLED)) {
02642             d->m_caretViewContext->freqTimerId = startTimer(500);
02643         d->m_caretViewContext->visible = true;
02644         }/*end if*/
02645     }/*end if*/
02646     showCaret();
02647 #endif // KHTML_NO_CARET
02648     QScrollView::focusInEvent( e );
02649 }
02650 
02651 void KHTMLView::focusOutEvent( QFocusEvent *e )
02652 {
02653     if(m_part) m_part->stopAutoScroll();
02654 
02655 #ifndef KHTML_NO_TYPE_AHEAD_FIND
02656     if(d->typeAheadActivated)
02657     {
02658         findTimeout();
02659     }
02660 #endif // KHTML_NO_TYPE_AHEAD_FIND
02661 
02662 #ifndef KHTML_NO_CARET
02663     if (d->m_caretViewContext) {
02664         switch (d->m_caretViewContext->displayNonFocused) {
02665     case KHTMLPart::CaretInvisible:
02666             hideCaret();
02667         break;
02668     case KHTMLPart::CaretVisible: {
02669         killTimer(d->m_caretViewContext->freqTimerId);
02670         d->m_caretViewContext->freqTimerId = -1;
02671             NodeImpl *caretNode = m_part->xmlDocImpl()->focusNode();
02672         if (!d->m_caretViewContext->visible && (m_part->isCaretMode()
02673         || m_part->isEditable()
02674             || (caretNode && caretNode->renderer()
02675             && caretNode->renderer()->style()->userInput()
02676                 == UI_ENABLED))) {
02677             d->m_caretViewContext->visible = true;
02678             showCaret(true);
02679         }/*end if*/
02680         break;
02681     }
02682     case KHTMLPart::CaretBlink:
02683         // simply leave as is
02684         break;
02685     }/*end switch*/
02686     }/*end if*/
02687 #endif // KHTML_NO_CARET
02688     QScrollView::focusOutEvent( e );
02689 }
02690 
02691 void KHTMLView::slotScrollBarMoved()
02692 {
02693     if ( !d->firstRelayout && !d->complete && m_part->xmlDocImpl() &&
02694           d->layoutSchedulingEnabled) {
02695         // contents scroll while we are not complete: we need to check our layout *now*
02696         khtml::RenderCanvas* root = static_cast<khtml::RenderCanvas *>( m_part->xmlDocImpl()->renderer() );
02697         if (root && root->needsLayout()) {
02698             unscheduleRelayout();
02699             layout();
02700         }
02701     }
02702     if (!d->scrollingSelf) {
02703         d->scrollBarMoved = true;
02704         d->contentsMoving = true;
02705         // ensure quick reset of contentsMoving flag
02706         scheduleRepaint(0, 0, 0, 0);
02707     }
02708 }
02709 
02710 void KHTMLView::timerEvent ( QTimerEvent *e )
02711 {
02712 //    kdDebug() << "timer event " << e->timerId() << endl;
02713     if ( e->timerId() == d->scrollTimerId ) {
02714         if( d->scrollSuspended )
02715             return;
02716         switch (d->scrollDirection) {
02717             case KHTMLViewPrivate::ScrollDown:
02718                 if (contentsY() + visibleHeight () >= contentsHeight())
02719                     d->newScrollTimer(this, 0);
02720                 else
02721                     scrollBy( 0, d->scrollBy );
02722                 break;
02723             case KHTMLViewPrivate::ScrollUp:
02724                 if (contentsY() <= 0)
02725                     d->newScrollTimer(this, 0);
02726                 else
02727                     scrollBy( 0, -d->scrollBy );
02728                 break;
02729             case KHTMLViewPrivate::ScrollRight:
02730                 if (contentsX() + visibleWidth () >= contentsWidth())
02731                     d->newScrollTimer(this, 0);
02732                 else
02733                     scrollBy( d->scrollBy, 0 );
02734                 break;
02735             case KHTMLViewPrivate::ScrollLeft:
02736                 if (contentsX() <= 0)
02737                     d->newScrollTimer(this, 0);
02738                 else
02739                     scrollBy( -d->scrollBy, 0 );
02740                 break;
02741         }
02742         return;
02743     }
02744     else if ( e->timerId() == d->layoutTimerId ) {
02745         d->firstRelayout = false;
02746         d->dirtyLayout = true;
02747         layout();
02748     }
02749 #ifndef KHTML_NO_CARET
02750     else if (d->m_caretViewContext
02751              && e->timerId() == d->m_caretViewContext->freqTimerId) {
02752         d->m_caretViewContext->visible = !d->m_caretViewContext->visible;
02753     if (d->m_caretViewContext->displayed) {
02754         updateContents(d->m_caretViewContext->x, d->m_caretViewContext->y,
02755             d->m_caretViewContext->width,
02756             d->m_caretViewContext->height);
02757     }/*end if*/
02758 //  if (d->m_caretViewContext->visible) cout << "|" << flush;
02759 //  else cout << "" << flush;
02760     return;
02761     }
02762 #endif
02763 
02764     d->contentsMoving = false;
02765     if( m_part->xmlDocImpl() ) {
02766     DOM::DocumentImpl *document = m_part->xmlDocImpl();
02767     khtml::RenderCanvas* root = static_cast<khtml::RenderCanvas *>(document->renderer());
02768 
02769     if ( root && root->needsLayout() ) {
02770         killTimer(d->repaintTimerId);
02771         d->repaintTimerId = 0;
02772         scheduleRelayout();
02773         return;
02774     }
02775     }
02776 
02777     setStaticBackground(d->useSlowRepaints);
02778 
02779 //        kdDebug() << "scheduled repaint "<< d->repaintTimerId  << endl;
02780     killTimer(d->repaintTimerId);
02781     d->repaintTimerId = 0;
02782 
02783     QRegion updateRegion;
02784     QMemArray<QRect> rects = d->updateRegion.rects();
02785 
02786     d->updateRegion = QRegion();
02787 
02788     if ( rects.size() )
02789         updateRegion = rects[0];
02790 
02791     for ( unsigned i = 1; i < rects.size(); ++i ) {
02792         QRect obR = updateRegion.boundingRect();
02793         QRegion newRegion = updateRegion.unite(rects[i]);
02794         if (2*newRegion.boundingRect().height() > 3*obR.height() )
02795         {
02796             repaintContents( obR );
02797             updateRegion = rects[i];
02798         }
02799         else
02800             updateRegion = newRegion;
02801     }
02802 
02803     if ( !updateRegion.isNull() )
02804         repaintContents( updateRegion.boundingRect() );
02805 
02806     if (d->dirtyLayout && !d->visibleWidgets.isEmpty()) {
02807         QWidget* w;
02808         d->dirtyLayout = false;
02809 
02810         QRect visibleRect(contentsX(), contentsY(), visibleWidth(), visibleHeight());
02811         QPtrList<RenderWidget> toRemove;
02812         for (QPtrDictIterator<QWidget> it(d->visibleWidgets); it.current(); ++it) {
02813             int xp = 0, yp = 0;
02814             w = it.current();
02815             RenderWidget* rw = static_cast<RenderWidget*>( it.currentKey() );
02816             if (!rw->absolutePosition(xp, yp) ||
02817                 !visibleRect.intersects(QRect(xp, yp, w->width(), w->height())))
02818                 toRemove.append(rw);
02819         }
02820         for (RenderWidget* r = toRemove.first(); r; r = toRemove.next())
02821             if ( (w = d->visibleWidgets.take(r) ) )
02822                 addChild(w, 0, -500000);
02823     }
02824     if (d->accessKeysActivated) emit repaintAccessKeys();
02825     if (d->emitCompletedAfterRepaint) {
02826         if (d->emitCompletedAfterRepaint == KHTMLViewPrivate::CSFull)
02827             emit m_part->completed();
02828         else
02829             emit m_part->completed(true);
02830         d->emitCompletedAfterRepaint = KHTMLViewPrivate::CSNone;
02831     }
02832 }
02833 
02834 void KHTMLView::scheduleRelayout(khtml::RenderObject * /*clippedObj*/)
02835 {
02836     if (!d->layoutSchedulingEnabled || d->layoutTimerId)
02837         return;
02838 
02839     d->layoutTimerId = startTimer( m_part->xmlDocImpl() && m_part->xmlDocImpl()->parsing()
02840                              ? 1000 : 0 );
02841 }
02842 
02843 void KHTMLView::unscheduleRelayout()
02844 {
02845     if (!d->layoutTimerId)
02846         return;
02847 
02848     killTimer(d->layoutTimerId);
02849     d->layoutTimerId = 0;
02850 }
02851 
02852 void KHTMLView::unscheduleRepaint()
02853 {
02854     if (!d->repaintTimerId)
02855         return;
02856 
02857     killTimer(d->repaintTimerId);
02858     d->repaintTimerId = 0;
02859 }
02860 
02861 void KHTMLView::scheduleRepaint(int x, int y, int w, int h, bool asap)
02862 {
02863     bool parsing = !m_part->xmlDocImpl() || m_part->xmlDocImpl()->parsing();
02864 
02865 //     kdDebug() << "parsing " << parsing << endl;
02866 //     kdDebug() << "complete " << d->complete << endl;
02867 
02868     int time = parsing ? 300 : (!asap ? ( !d->complete ? 100 : 20 ) : 0);
02869 
02870 #ifdef DEBUG_FLICKER
02871     QPainter p;
02872     p.begin( viewport() );
02873 
02874     int vx, vy;
02875     contentsToViewport( x, y, vx, vy );
02876     p.fillRect( vx, vy, w, h, Qt::red );
02877     p.end();
02878 #endif
02879 
02880     d->updateRegion = d->updateRegion.unite(QRect(x,y,w,h));
02881     
02882     if (asap && !parsing)
02883         unscheduleRelayout();
02884 
02885     if ( !d->repaintTimerId )
02886         d->repaintTimerId = startTimer( time );
02887 
02888 //     kdDebug() << "starting timer " << time << endl;
02889 }
02890 
02891 void KHTMLView::complete( bool pendingAction )
02892 {
02893 //     kdDebug() << "KHTMLView::complete()" << endl;
02894 
02895     d->complete = true;
02896 
02897     // is there a relayout pending?
02898     if (d->layoutTimerId)
02899     {
02900 //         kdDebug() << "requesting relayout now" << endl;
02901         // do it now
02902         killTimer(d->layoutTimerId);
02903         d->layoutTimerId = startTimer( 0 );
02904         d->emitCompletedAfterRepaint = pendingAction ?
02905             KHTMLViewPrivate::CSActionPending : KHTMLViewPrivate::CSFull;
02906     }
02907 
02908     // is there a repaint pending?
02909     if (d->repaintTimerId)
02910     {
02911 //         kdDebug() << "requesting repaint now" << endl;
02912         // do it now
02913         killTimer(d->repaintTimerId);
02914         d->repaintTimerId = startTimer( 20 );
02915         d->emitCompletedAfterRepaint = pendingAction ?
02916             KHTMLViewPrivate::CSActionPending : KHTMLViewPrivate::CSFull;
02917     }
02918 
02919     if (!d->emitCompletedAfterRepaint)
02920     {
02921         if (!pendingAction)
02922             emit m_part->completed();
02923         else
02924             emit m_part->completed(true);
02925     }
02926 
02927 }
02928 
02929 #ifndef KHTML_NO_CARET
02930 
02931 // ### the dependencies on static functions are a nightmare. just be
02932 // hacky and include the implementation here. Clean me up, please.
02933 
02934 #include "khtml_caret.cpp"
02935 
02936 void KHTMLView::initCaret(bool keepSelection)
02937 {
02938 #if DEBUG_CARETMODE > 0
02939   kdDebug(6200) << "begin initCaret" << endl;
02940 #endif
02941   // save caretMoved state as moveCaretTo changes it
02942   if (m_part->xmlDocImpl()) {
02943 #if 0
02944     ElementImpl *listitem = m_part->xmlDocImpl()->getElementById("__test_element__");
02945     if (listitem) dumpLineBoxes(static_cast<RenderFlow *>(listitem->renderer()));
02946 #endif
02947     d->caretViewContext();
02948     bool cmoved = d->m_caretViewContext->caretMoved;
02949     if (m_part->d->caretNode().isNull()) {
02950       // set to document, position will be sanitized anyway
02951       m_part->d->caretNode() = m_part->document();
02952       m_part->d->caretOffset() = 0L;
02953       // This sanity check is necessary for the not so unlikely case that
02954       // setEditable or setCaretMode is called before any render objects have
02955       // been created.
02956       if (!m_part->d->caretNode().handle()->renderer()) return;
02957     }/*end if*/
02958 //    kdDebug(6200) << "d->m_selectionStart " << m_part->d->m_selectionStart.handle()
02959 //          << " d->m_selectionEnd " << m_part->d->m_selectionEnd.handle() << endl;
02960     // ### does not repaint the selection on keepSelection!=false
02961     moveCaretTo(m_part->d->caretNode().handle(), m_part->d->caretOffset(), !keepSelection);
02962 //    kdDebug(6200) << "d->m_selectionStart " << m_part->d->m_selectionStart.handle()
02963 //          << " d->m_selectionEnd " << m_part->d->m_selectionEnd.handle() << endl;
02964     d->m_caretViewContext->caretMoved = cmoved;
02965   }/*end if*/
02966 #if DEBUG_CARETMODE > 0
02967   kdDebug(6200) << "end initCaret" << endl;
02968 #endif
02969 }
02970 
02971 bool KHTMLView::caretOverrides() const
02972 {
02973     bool cm = m_part->isCaretMode();
02974     bool dm = m_part->isEditable();
02975     return cm && !dm ? false
02976         : (dm || m_part->d->caretNode().handle()->contentEditable())
02977       && d->editorContext()->override;
02978 }
02979 
02980 void KHTMLView::ensureNodeHasFocus(NodeImpl *node)
02981 {
02982   if (m_part->isCaretMode() || m_part->isEditable()) return;
02983   if (node->focused()) return;
02984 
02985   // Find first ancestor whose "user-input" is "enabled"
02986   NodeImpl *firstAncestor = 0;
02987   while (node) {
02988     if (node->renderer()
02989        && node->renderer()->style()->userInput() != UI_ENABLED)
02990       break;
02991     firstAncestor = node;
02992     node = node->parentNode();
02993   }/*wend*/
02994 
02995   if (!node) firstAncestor = 0;
02996 
02997   DocumentImpl *doc = m_part->xmlDocImpl();
02998   // ensure that embedded widgets don't lose their focus
02999   if (!firstAncestor && doc->focusNode() && doc->focusNode()->renderer()
03000     && doc->focusNode()->renderer()->isWidget())
03001     return;
03002 
03003   // Set focus node on the document
03004 #if DEBUG_CARETMODE > 1
03005   kdDebug(6200) << k_funcinfo << "firstAncestor " << firstAncestor << ": "
03006     << (firstAncestor ? firstAncestor->nodeName().string() : QString::null) << endl;
03007 #endif
03008   doc->setFocusNode(firstAncestor);
03009   emit m_part->nodeActivated(Node(firstAncestor));
03010 }
03011 
03012 void KHTMLView::recalcAndStoreCaretPos(CaretBox *hintBox)
03013 {
03014     if (!m_part || m_part->d->caretNode().isNull()) return;
03015     d->caretViewContext();
03016     NodeImpl *caretNode = m_part->d->caretNode().handle();
03017 #if DEBUG_CARETMODE > 0
03018   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;
03019 #endif
03020     caretNode->getCaret(m_part->d->caretOffset(), caretOverrides(),
03021             d->m_caretViewContext->x, d->m_caretViewContext->y,
03022         d->m_caretViewContext->width,
03023         d->m_caretViewContext->height);
03024 
03025     if (hintBox && d->m_caretViewContext->x == -1) {
03026 #if DEBUG_CARETMODE > 1
03027         kdDebug(6200) << "using hint inline box coordinates" << endl;
03028 #endif
03029     RenderObject *r = caretNode->renderer();
03030     const QFontMetrics &fm = r->style()->fontMetrics();
03031         int absx, absy;
03032     r->containingBlock()->absolutePosition(absx, absy,
03033                         false); // ### what about fixed?
03034     d->m_caretViewContext->x = absx + hintBox->xPos();
03035     d->m_caretViewContext->y = absy + hintBox->yPos();
03036 //              + hintBox->baseline() - fm.ascent();
03037     d->m_caretViewContext->width = 1;
03038     // ### firstline not regarded. But I think it can be safely neglected
03039     // as hint boxes are only used for empty lines.
03040     d->m_caretViewContext->height = fm.height();
03041     }/*end if*/
03042 
03043 #if DEBUG_CARETMODE > 4
03044 //    kdDebug(6200) << "freqTimerId: "<<d->m_caretViewContext->freqTimerId<<endl;
03045 #endif
03046 #if DEBUG_CARETMODE > 0
03047     kdDebug(6200) << "caret: ofs="<<m_part->d->caretOffset()<<" "
03048         <<" x="<<d->m_caretViewContext->x<<" y="<<d->m_caretViewContext->y
03049     <<" h="<<d->m_caretViewContext->height<<endl;
03050 #endif
03051 }
03052 
03053 void KHTMLView::caretOn()
03054 {
03055     if (d->m_caretViewContext) {
03056         killTimer(d->m_caretViewContext->freqTimerId);
03057 
03058     if (hasFocus() || d->m_caretViewContext->displayNonFocused
03059             == KHTMLPart::CaretBlink) {
03060             d->m_caretViewContext->freqTimerId = startTimer(500);
03061     } else {
03062         d->m_caretViewContext->freqTimerId = -1;
03063     }/*end if*/
03064 
03065         d->m_caretViewContext->visible = true;
03066         if ((d->m_caretViewContext->displayed = (hasFocus()
03067         || d->m_caretViewContext->displayNonFocused
03068             != KHTMLPart::CaretInvisible))) {
03069         updateContents(d->m_caretViewContext->x, d->m_caretViewContext->y,
03070                 d->m_caretViewContext->width,
03071             d->m_caretViewContext->height);
03072     }/*end if*/
03073 //        kdDebug(6200) << "caret on" << endl;
03074     }/*end if*/
03075 }
03076 
03077 void KHTMLView::caretOff()
03078 {
03079     if (d->m_caretViewContext) {
03080         killTimer(d->m_caretViewContext->freqTimerId);
03081     d->m_caretViewContext->freqTimerId = -1;
03082         d->m_caretViewContext->displayed = false;
03083         if (d->m_caretViewContext->visible) {
03084             d->m_caretViewContext->visible = false;
03085         updateContents(d->m_caretViewContext->x, d->m_caretViewContext->y,
03086                 d->m_caretViewContext->width,
03087                 d->m_caretViewContext->height);
03088     }/*end if*/
03089 //        kdDebug(6200) << "caret off" << endl;
03090     }/*end if*/
03091 }
03092 
03093 void KHTMLView::showCaret(bool forceRepaint)
03094 {
03095     if (d->m_caretViewContext) {
03096         d->m_caretViewContext->displayed = true;
03097         if (d->m_caretViewContext->visible) {
03098         if (!forceRepaint) {
03099             updateContents(d->m_caretViewContext->x, d->m_caretViewContext->y,
03100                 d->m_caretViewContext->width,
03101             d->m_caretViewContext->height);
03102             } else {
03103             repaintContents(d->m_caretViewContext->x, d->m_caretViewContext->y,
03104                 d->m_caretViewContext->width,
03105                 d->m_caretViewContext->height);
03106         }/*end if*/
03107     }/*end if*/
03108 //        kdDebug(6200) << "caret shown" << endl;
03109     }/*end if*/
03110 }
03111 
03112 bool KHTMLView::foldSelectionToCaret(NodeImpl *startNode, long startOffset,
03113                     NodeImpl *endNode, long endOffset)
03114 {
03115   m_part->d->m_selectionStart = m_part->d->m_selectionEnd = m_part->d->caretNode();
03116   m_part->d->m_startOffset = m_part->d->m_endOffset = m_part->d->caretOffset();
03117   m_part->d->m_extendAtEnd = true;
03118 
03119   bool folded = startNode != endNode || startOffset != endOffset;
03120 
03121   // Only clear the selection if there has been one.
03122   if (folded) {
03123     m_part->xmlDocImpl()->clearSelection();
03124   }/*end if*/
03125 
03126   return folded;
03127 }
03128 
03129 void KHTMLView::hideCaret()
03130 {
03131     if (d->m_caretViewContext) {
03132         if (d->m_caretViewContext->visible) {
03133 //            kdDebug(6200) << "redraw caret hidden" << endl;
03134         d->m_caretViewContext->visible = false;
03135         // force repaint, otherwise the event won't be handled
03136         // before the focus leaves the window
03137         repaintContents(d->m_caretViewContext->x, d->m_caretViewContext->y,
03138                 d->m_caretViewContext->width,
03139                 d->m_caretViewContext->height);
03140         d->m_caretViewContext->visible = true;
03141     }/*end if*/
03142         d->m_caretViewContext->displayed = false;
03143 //        kdDebug(6200) << "caret hidden" << endl;
03144     }/*end if*/
03145 }
03146 
03147 int KHTMLView::caretDisplayPolicyNonFocused() const
03148 {
03149   if (d->m_caretViewContext)
03150     return d->m_caretViewContext->displayNonFocused;
03151   else
03152     return KHTMLPart::CaretInvisible;
03153 }
03154 
03155 void KHTMLView::setCaretDisplayPolicyNonFocused(int policy)
03156 {
03157   d->caretViewContext();
03158 //  int old = d->m_caretViewContext->displayNonFocused;
03159   d->m_caretViewContext->displayNonFocused = (KHTMLPart::CaretDisplayPolicy)policy;
03160 
03161   // make change immediately take effect if not focused
03162   if (!hasFocus()) {
03163     switch (d->m_caretViewContext->displayNonFocused) {
03164       case KHTMLPart::CaretInvisible:
03165         hideCaret();
03166     break;
03167       case KHTMLPart::CaretBlink:
03168     if (d->m_caretViewContext->freqTimerId != -1) break;
03169     d->m_caretViewContext->freqTimerId = startTimer(500);
03170     // fall through
03171       case KHTMLPart::CaretVisible:
03172         d->m_caretViewContext->displayed = true;
03173         showCaret();
03174     break;
03175     }/*end switch*/
03176   }/*end if*/
03177 }
03178 
03179 bool KHTMLView::placeCaret(CaretBox *hintBox)
03180 {
03181   CaretViewContext *cv = d->caretViewContext();
03182   caretOff();
03183   NodeImpl *caretNode = m_part->d->caretNode().handle();
03184   // ### why is it sometimes null?
03185   if (!caretNode || !caretNode->renderer()) return false;
03186   ensureNodeHasFocus(caretNode);
03187   if (m_part->isCaretMode() || m_part->isEditable()
03188      || caretNode->renderer()->style()->userInput() == UI_ENABLED) {
03189     recalcAndStoreCaretPos(hintBox);
03190 
03191     cv->origX = cv->x;
03192 
03193     caretOn();
03194     return true;
03195   }/*end if*/
03196   return false;
03197 }
03198 
03199 void KHTMLView::ensureCaretVisible()
03200 {
03201   CaretViewContext *cv = d->m_caretViewContext;
03202   if (!cv) return;
03203   ensureVisible(cv->x, cv->y, cv->width, cv->height);
03204   d->scrollBarMoved = false;
03205 }
03206 
03207 bool KHTMLView::extendSelection(NodeImpl *oldStartSel, long oldStartOfs,
03208                 NodeImpl *oldEndSel, long oldEndOfs)
03209 {
03210   bool changed = false;
03211   if (m_part->d->m_selectionStart == m_part->d->m_selectionEnd
03212       && m_part->d->m_startOffset == m_part->d->m_endOffset) {
03213     changed = foldSelectionToCaret(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs);
03214     m_part->d->m_extendAtEnd = true;
03215   } else do {
03216     changed = m_part->d->m_selectionStart.handle() != oldStartSel
03217             || m_part->d->m_startOffset != oldStartOfs
03218         || m_part->d->m_selectionEnd.handle() != oldEndSel
03219         || m_part->d->m_endOffset != oldEndOfs;
03220     if (!changed) break;
03221 
03222     // determine start position -- caret position is always at end.
03223     NodeImpl *startNode;
03224     long startOffset;
03225     if (m_part->d->m_extendAtEnd) {
03226       startNode = m_part->d->m_selectionStart.handle();
03227       startOffset = m_part->d->m_startOffset;
03228     } else {
03229       startNode = m_part->d->m_selectionEnd.handle();
03230       startOffset = m_part->d->m_endOffset;
03231       m_part->d->m_selectionEnd = m_part->d->m_selectionStart;
03232       m_part->d->m_endOffset = m_part->d->m_startOffset;
03233       m_part->d->m_extendAtEnd = true;
03234     }/*end if*/
03235 
03236     bool swapNeeded = false;
03237     if (!m_part->d->m_selectionEnd.isNull() && startNode) {
03238       swapNeeded = RangeImpl::compareBoundaryPoints(startNode, startOffset,
03239                 m_part->d->m_selectionEnd.handle(),
03240             m_part->d->m_endOffset) >= 0;
03241     }/*end if*/
03242 
03243     m_part->d->m_selectionStart = startNode;
03244     m_part->d->m_startOffset = startOffset;
03245 
03246     if (swapNeeded) {
03247       m_part->xmlDocImpl()->setSelection(m_part->d->m_selectionEnd.handle(),
03248         m_part->d->m_endOffset, m_part->d->m_selectionStart.handle(),
03249         m_part->d->m_startOffset);
03250     } else {
03251       m_part->xmlDocImpl()->setSelection(m_part->d->m_selectionStart.handle(),
03252         m_part->d->m_startOffset, m_part->d->m_selectionEnd.handle(),
03253         m_part->d->m_endOffset);
03254     }/*end if*/
03255   } while(false);/*end if*/
03256   return changed;
03257 }
03258 
03259 void KHTMLView::updateSelection(NodeImpl *oldStartSel, long oldStartOfs,
03260                 NodeImpl *oldEndSel, long oldEndOfs)
03261 {
03262   if (m_part->d->m_selectionStart == m_part->d->m_selectionEnd
03263       && m_part->d->m_startOffset == m_part->d->m_endOffset) {
03264     if (foldSelectionToCaret(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs)) {
03265       m_part->emitSelectionChanged();
03266     }/*end if*/
03267     m_part->d->m_extendAtEnd = true;
03268   } else {
03269     // check if the extending end has passed the immobile end
03270     if (!m_part->d->m_selectionEnd.isNull() && !m_part->d->m_selectionEnd.isNull()) {
03271       bool swapNeeded = RangeImpl::compareBoundaryPoints(
03272                 m_part->d->m_selectionStart.handle(), m_part->d->m_startOffset,
03273             m_part->d->m_selectionEnd.handle(), m_part->d->m_endOffset) >= 0;
03274       if (swapNeeded) {
03275         DOM::Node tmpNode = m_part->d->m_selectionStart;
03276         long tmpOffset = m_part->d->m_startOffset;
03277         m_part->d->m_selectionStart = m_part->d->m_selectionEnd;
03278         m_part->d->m_startOffset = m_part->d->m_endOffset;
03279         m_part->d->m_selectionEnd = tmpNode;
03280         m_part->d->m_endOffset = tmpOffset;
03281         m_part->d->m_startBeforeEnd = true;
03282         m_part->d->m_extendAtEnd = !m_part->d->m_extendAtEnd;
03283       }/*end if*/
03284     }/*end if*/
03285 
03286     m_part->xmlDocImpl()->setSelection(m_part->d->m_selectionStart.handle(),
03287         m_part->d->m_startOffset, m_part->d->m_selectionEnd.handle(),
03288         m_part->d->m_endOffset);
03289     m_part->emitSelectionChanged();
03290   }/*end if*/
03291 }
03292 
03293 void KHTMLView::caretKeyPressEvent(QKeyEvent *_ke)
03294 {
03295   NodeImpl *oldStartSel = m_part->d->m_selectionStart.handle();
03296   long oldStartOfs = m_part->d->m_startOffset;
03297   NodeImpl *oldEndSel = m_part->d->m_selectionEnd.handle();
03298   long oldEndOfs = m_part->d->m_endOffset;
03299 
03300   NodeImpl *oldCaretNode = m_part->d->caretNode().handle();
03301   long oldOffset = m_part->d->caretOffset();
03302 
03303   bool ctrl = _ke->state() & ControlButton;
03304 
03305 // FIXME: this is that widely indented because I will write ifs around it.
03306       switch(_ke->key()) {
03307         case Key_Space:
03308           break;
03309 
03310         case Key_Down:
03311       moveCaretNextLine(1);
03312           break;
03313 
03314         case Key_Up:
03315       moveCaretPrevLine(1);
03316           break;
03317 
03318         case Key_Left:
03319       moveCaretBy(false, ctrl ? CaretByWord : CaretByCharacter, 1);
03320           break;
03321 
03322         case Key_Right:
03323       moveCaretBy(true, ctrl ? CaretByWord : CaretByCharacter, 1);
03324           break;
03325 
03326         case Key_Next:
03327       moveCaretNextPage();
03328           break;
03329 
03330         case Key_Prior:
03331       moveCaretPrevPage();
03332           break;
03333 
03334         case Key_Home:
03335       if (ctrl)
03336         moveCaretToDocumentBoundary(false);
03337       else
03338         moveCaretToLineBegin();
03339           break;
03340 
03341         case Key_End:
03342       if (ctrl)
03343         moveCaretToDocumentBoundary(true);
03344       else
03345         moveCaretToLineEnd();
03346           break;
03347 
03348       }/*end switch*/
03349 
03350   if ((m_part->d->caretNode().handle() != oldCaretNode
03351     || m_part->d->caretOffset() != oldOffset)
03352     // node should never be null, but faulty conditions may cause it to be
03353     && !m_part->d->caretNode().isNull()) {
03354 
03355     d->m_caretViewContext->caretMoved = true;
03356 
03357     if (_ke->state() & ShiftButton) {   // extend selection
03358       updateSelection(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs);
03359     } else {            // clear any selection
03360       if (foldSelectionToCaret(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs))
03361         m_part->emitSelectionChanged();
03362     }/*end if*/
03363 
03364     m_part->emitCaretPositionChanged(m_part->d->caretNode(), m_part->d->caretOffset());
03365   }/*end if*/
03366 
03367   _ke->accept();
03368 }
03369 
03370 bool KHTMLView::moveCaretTo(NodeImpl *node, long offset, bool clearSel)
03371 {
03372   if (!node) return false;
03373   ElementImpl *baseElem = determineBaseElement(node);
03374   RenderFlow *base = static_cast<RenderFlow *>(baseElem ? baseElem->renderer() : 0);
03375   if (!node) return false;
03376 
03377   // need to find out the node's inline box. If there is none, this function
03378   // will snap to the next node that has one. This is necessary to make the
03379   // caret visible in any case.
03380   CaretBoxLineDeleter cblDeleter;
03381 //   RenderBlock *cb;
03382   long r_ofs;
03383   CaretBoxIterator cbit;
03384   CaretBoxLine *cbl = findCaretBoxLine(node, offset, &cblDeleter, base, r_ofs, cbit);
03385   if(!cbl) {
03386       kdWarning() << "KHTMLView::moveCaretTo - findCaretBoxLine() returns NULL" << endl;
03387       return false;
03388   }
03389 
03390 #if DEBUG_CARETMODE > 3
03391   if (cbl) kdDebug(6200) << cbl->information() << endl;
03392 #endif
03393   CaretBox *box = *cbit;
03394   if (cbit != cbl->end() && box->object() != node->renderer()) {
03395     if (box->object()->element()) {
03396       mapRenderPosToDOMPos(box->object(), r_ofs, box->isOutside(),
03397                 box->isOutsideEnd(), node, offset);
03398       //if (!outside) offset = node->minOffset();
03399 #if DEBUG_CARETMODE > 1
03400       kdDebug(6200) << "set new node " << node->nodeName().string() << "@" << node << endl;
03401 #endif
03402     } else {    // box has no associated element -> do not use
03403       // this case should actually never happen.
03404       box = 0;
03405       kdError(6200) << "Box contains no node! Crash imminent" << endl;
03406     }/*end if*/
03407   }
03408 
03409   NodeImpl *oldStartSel = m_part->d->m_selectionStart.handle();
03410   long oldStartOfs = m_part->d->m_startOffset;
03411   NodeImpl *oldEndSel = m_part->d->m_selectionEnd.handle();
03412   long oldEndOfs = m_part->d->m_endOffset;
03413 
03414   // test for position change
03415   bool posChanged = m_part->d->caretNode().handle() != node
03416         || m_part->d->caretOffset() != offset;
03417   bool selChanged = false;
03418 
03419   m_part->d->caretNode() = node;
03420   m_part->d->caretOffset() = offset;
03421   if (clearSel || !oldStartSel || !oldEndSel) {
03422     selChanged = foldSelectionToCaret(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs);
03423   } else {
03424     //kdDebug(6200) << "moveToCaret: extendSelection: m_extendAtEnd " << m_part->d->m_extendAtEnd << endl;
03425     //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;
03426     selChanged = extendSelection(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs);
03427     //kdDebug(6200) << "after extendSelection: m_extendAtEnd " << m_part->d->m_extendAtEnd << endl;
03428     //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;
03429   }/*end if*/
03430 
03431   d->caretViewContext()->caretMoved = true;
03432 
03433   bool visible_caret = placeCaret(box);
03434 
03435   // FIXME: if the old position was !visible_caret, and the new position is
03436   // also, then two caretPositionChanged signals with a null Node are
03437   // emitted in series.
03438   if (posChanged) {
03439     m_part->emitCaretPositionChanged(visible_caret ? node : 0, offset);
03440   }/*end if*/
03441 
03442   return selChanged;
03443 }
03444 
03445 void KHTMLView::moveCaretByLine(bool next, int count)
03446 {
03447   Node &caretNodeRef = m_part->d->caretNode();
03448   if (caretNodeRef.isNull()) return;
03449 
03450   NodeImpl *caretNode = caretNodeRef.handle();
03451 //  kdDebug(6200) << ": caretNode=" << caretNode << endl;
03452   long offset = m_part->d->caretOffset();
03453 
03454   CaretViewContext *cv = d->caretViewContext();
03455 
03456   ElementImpl *baseElem = determineBaseElement(caretNode);
03457   LinearDocument ld(m_part, caretNode, offset, LeafsOnly, baseElem);
03458 
03459   ErgonomicEditableLineIterator it(ld.current(), cv->origX);
03460 
03461   // move count lines vertically
03462   while (count > 0 && it != ld.end() && it != ld.preBegin()) {
03463     count--;
03464     if (next) ++it; else --it;
03465   }/*wend*/
03466 
03467   // Nothing? Then leave everything as is.
03468   if (it == ld.end() || it == ld.preBegin()) return;
03469 
03470   int x, absx, absy;
03471   CaretBox *caretBox = nearestCaretBox(it, d->m_caretViewContext, x, absx, absy);
03472 
03473   placeCaretOnLine(caretBox, x, absx, absy);
03474 }
03475 
03476 void KHTMLView::placeCaretOnLine(CaretBox *caretBox, int x, int absx, int absy)
03477 {
03478   // paranoia sanity check
03479   if (!caretBox) return;
03480 
03481   RenderObject *caretRender = caretBox->object();
03482 
03483 #if DEBUG_CARETMODE > 0
03484   kdDebug(6200) << "got valid caretBox " << caretBox << endl;
03485   kdDebug(6200) << "xPos: " << caretBox->xPos() << " yPos: " << caretBox->yPos()
03486         << " width: " << caretBox->width() << " height: " << caretBox->height() << endl;
03487   InlineTextBox *tb = static_cast<InlineTextBox *>(caretBox->inlineBox());
03488   if (caretBox->isInlineTextBox()) { kdDebug(6200) << "contains \"" << QString(static_cast<RenderText *>(tb->object())->str->s + tb->m_start, tb->m_len) << "\"" << endl;}
03489 #endif
03490   // inquire height of caret
03491   int caretHeight = caretBox->height();
03492   bool isText = caretBox->isInlineTextBox();
03493   int yOfs = 0;     // y-offset for text nodes
03494   if (isText) {
03495     // text boxes need extrawurst
03496     RenderText *t = static_cast<RenderText *>(caretRender);
03497     const QFontMetrics &fm = t->metrics(caretBox->inlineBox()->m_firstLine);
03498     caretHeight = fm.height();
03499     yOfs = caretBox->inlineBox()->baseline() - fm.ascent();
03500   }/*end if*/
03501 
03502   caretOff();
03503 
03504   // set new caret node
03505   NodeImpl *caretNode;
03506   long &offset = m_part->d->caretOffset();
03507   mapRenderPosToDOMPos(caretRender, offset, caretBox->isOutside(),
03508         caretBox->isOutsideEnd(), caretNode, offset);
03509 
03510   // set all variables not needing special treatment
03511   d->m_caretViewContext->y = caretBox->yPos() + yOfs;
03512   d->m_caretViewContext->height = caretHeight;
03513   d->m_caretViewContext->width = 1; // FIXME: regard override
03514 
03515   int xPos = caretBox->xPos();
03516   int caretBoxWidth = caretBox->width();
03517   d->m_caretViewContext->x = xPos;
03518 
03519   if (!caretBox->isOutside()) {
03520     // before or at beginning of inline box -> place at beginning
03521     long r_ofs = 0;
03522     if (x <= xPos) {
03523       r_ofs = caretBox->minOffset();
03524   // somewhere within this block
03525     } else if (x > xPos && x <= xPos + caretBoxWidth) {
03526       if (isText) { // find out where exactly
03527         r_ofs = static_cast<InlineTextBox *>(caretBox->inlineBox())
03528             ->offsetForPoint(x, d->m_caretViewContext->x);
03529 #if DEBUG_CARETMODE > 2
03530         kdDebug(6200) << "deviation from origX " << d->m_caretViewContext->x - x << endl;
03531 #endif
03532 #if 0
03533       } else {  // snap to nearest end
03534         if (xPos + caretBoxWidth - x < x - xPos) {
03535           d->m_caretViewContext->x = xPos + caretBoxWidth;
03536           r_ofs = caretNode ? caretNode->maxOffset() : 1;
03537         } else {
03538           d->m_caretViewContext->x = xPos;
03539           r_ofs = caretNode ? caretNode->minOffset() : 0;
03540         }/*end if*/
03541 #endif
03542       }/*end if*/
03543     } else {        // after the inline box -> place at end
03544       d->m_caretViewContext->x = xPos + caretBoxWidth;
03545       r_ofs = caretBox->maxOffset();
03546     }/*end if*/
03547     offset = r_ofs;
03548   }/*end if*/
03549 #if DEBUG_CARETMODE > 0
03550       kdDebug(6200) << "new offset: " << offset << endl;
03551 #endif
03552 
03553   m_part->d->caretNode() = caretNode;
03554   m_part->d->caretOffset() = offset;
03555 
03556   d->m_caretViewContext->x += absx;
03557   d->m_caretViewContext->y += absy;
03558 
03559 #if DEBUG_CARETMODE > 1
03560     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;
03561 #endif
03562 
03563   ensureVisible(d->m_caretViewContext->x, d->m_caretViewContext->y,
03564     d->m_caretViewContext->width, d->m_caretViewContext->height);
03565   d->scrollBarMoved = false;
03566 
03567   ensureNodeHasFocus(caretNode);
03568   caretOn();
03569 }
03570 
03571 void KHTMLView::moveCaretToLineBoundary(bool end)
03572 {
03573   Node &caretNodeRef = m_part->d->caretNode();
03574   if (caretNodeRef.isNull()) return;
03575 
03576   NodeImpl *caretNode = caretNodeRef.handle();
03577 //  kdDebug(6200) << ": caretNode=" << caretNode << endl;
03578   long offset = m_part->d->caretOffset();
03579 
03580   ElementImpl *baseElem = determineBaseElement(caretNode);
03581   LinearDocument ld(m_part, caretNode, offset, LeafsOnly, baseElem);
03582 
03583   EditableLineIterator it = ld.current();
03584   if (it == ld.end()) return;   // should not happen, but who knows
03585 
03586   EditableCaretBoxIterator fbit(it, end);
03587   Q_ASSERT(fbit != (*it)->end() && fbit != (*it)->preBegin());
03588   CaretBox *b = *fbit;
03589 
03590   RenderObject *cb = b->containingBlock();
03591   int absx, absy;
03592 
03593   if (cb) cb->absolutePosition(absx,absy);
03594   else absx = absy = 0;
03595 
03596   int x = b->xPos() + (end && !b->isOutside() ? b->width() : 0);
03597   d->m_caretViewContext->origX = absx + x;
03598   placeCaretOnLine(b, x, absx, absy);
03599 }
03600 
03601 void KHTMLView::moveCaretToDocumentBoundary(bool end)
03602 {
03603   Node &caretNodeRef = m_part->d->caretNode();
03604   if (caretNodeRef.isNull()) return;
03605 
03606   NodeImpl *caretNode = caretNodeRef.handle();
03607 //  kdDebug(6200) << ": caretNode=" << caretNode << endl;
03608   long offset = m_part->d->caretOffset();
03609 
03610   ElementImpl *baseElem = determineBaseElement(caretNode);
03611   LinearDocument ld(m_part, caretNode, offset, IndicatedFlows, baseElem);
03612 
03613   EditableLineIterator it(end ? ld.preEnd() : ld.begin(), end);
03614   if (it == ld.end() || it == ld.preBegin()) return;    // should not happen, but who knows
03615 
03616   EditableCaretBoxIterator fbit = it;
03617   Q_ASSERT(fbit != (*it)->end() && fbit != (*it)->preBegin());
03618   CaretBox *b = *fbit;
03619 
03620   RenderObject *cb = (*it)->containingBlock();
03621   int absx, absy;
03622 
03623   if (cb) cb->absolutePosition(absx, absy);
03624   else absx = absy = 0;
03625 
03626   int x = b->xPos()/* + (end ? b->width() : 0) reactivate for rtl*/;
03627   d->m_caretViewContext->origX = absx + x;
03628   placeCaretOnLine(b, x, absx, absy);
03629 }
03630 
03631 void KHTMLView::moveCaretBy(bool next, CaretMovement cmv, int count)
03632 {
03633   if (!m_part) return;
03634   Node &caretNodeRef = m_part->d->caretNode();
03635   if (caretNodeRef.isNull()) return;
03636 
03637   NodeImpl *caretNode = caretNodeRef.handle();
03638 //  kdDebug(6200) << ": caretNode=" << caretNode << endl;
03639   long &offset = m_part->d->caretOffset();
03640 
03641   ElementImpl *baseElem = determineBaseElement(caretNode);
03642   CaretAdvancePolicy advpol = cmv != CaretByWord ? IndicatedFlows : LeafsOnly;
03643   LinearDocument ld(m_part, caretNode, offset, advpol, baseElem);
03644 
03645   EditableCharacterIterator it(&ld);
03646   while (!it.isEnd() && count > 0) {
03647     count--;
03648     if (cmv == CaretByCharacter) {
03649       if (next) ++it;
03650       else --it;
03651     } else if (cmv == CaretByWord) {
03652       if (next) moveItToNextWord(it);
03653       else moveItToPrevWord(it);
03654     }/*end if*/
03655 //kdDebug(6200) << "movecaret" << endl;
03656   }/*wend*/
03657   CaretBox *hintBox = 0;    // make gcc uninit warning disappear
03658   if (!it.isEnd()) {
03659     NodeImpl *node = caretNodeRef.handle();
03660     hintBox = it.caretBox();
03661 //kdDebug(6200) << "hintBox = " << hintBox << endl;
03662 //kdDebug(6200) << " outside " << hintBox->isOutside() << " outsideEnd " << hintBox->isOutsideEnd() << " r " << it.renderer() << " ofs " << it.offset() << " cb " << hintBox->containingBlock() << endl;
03663     mapRenderPosToDOMPos(it.renderer(), it.offset(), hintBox->isOutside(),
03664             hintBox->isOutsideEnd(), node, offset);
03665 //kdDebug(6200) << "mapRTD" << endl;
03666     caretNodeRef = node;
03667 #if DEBUG_CARETMODE > 2
03668     kdDebug(6200) << "set by valid node " << node << " " << (node?node->nodeName().string():QString::null) << " offset: " << offset << endl;
03669 #endif
03670   } else {
03671     offset = next ? caretNode->maxOffset() : caretNode->minOffset();
03672 #if DEBUG_CARETMODE > 0
03673     kdDebug(6200) << "set by INvalid node. offset: " << offset << endl;
03674 #endif
03675   }/*end if*/
03676   placeCaretOnChar(hintBox);
03677 }
03678 
03679 void KHTMLView::placeCaretOnChar(CaretBox *hintBox)
03680 {
03681   caretOff();
03682   recalcAndStoreCaretPos(hintBox);
03683   ensureVisible(d->m_caretViewContext->x, d->m_caretViewContext->y,
03684     d->m_caretViewContext->width, d->m_caretViewContext->height);
03685   d->m_caretViewContext->origX = d->m_caretViewContext->x;
03686   d->scrollBarMoved = false;
03687 #if DEBUG_CARETMODE > 3
03688   //if (caretNode->isTextNode())  kdDebug(6200) << "text[0] = " << (int)*((TextImpl *)caretNode)->data().unicode() << " text :\"" << ((TextImpl *)caretNode)->data().string() << "\"" << endl;
03689 #endif
03690   ensureNodeHasFocus(m_part->d->caretNode().handle());
03691   caretOn();
03692 }
03693 
03694 void KHTMLView::moveCaretByPage(bool next)
03695 {
03696   Node &caretNodeRef = m_part->d->caretNode();
03697 
03698   NodeImpl *caretNode = caretNodeRef.handle();
03699 //  kdDebug(6200) << ": caretNode=" << caretNode << endl;
03700   long offset = m_part->d->caretOffset();
03701 
03702   int offs = (clipper()->height() < 30) ? clipper()->height() : 30;
03703   // Minimum distance the caret must be moved
03704   int mindist = clipper()->height() - offs;
03705 
03706   CaretViewContext *cv = d->caretViewContext();
03707 //  int y = cv->y;      // we always measure the top border
03708 
03709   ElementImpl *baseElem = determineBaseElement(caretNode);
03710   LinearDocument ld(m_part, caretNode, offset, LeafsOnly, baseElem);
03711 
03712   ErgonomicEditableLineIterator it(ld.current(), cv->origX);
03713 
03714   moveIteratorByPage(ld, it, mindist, next);
03715 
03716   int x, absx, absy;
03717   CaretBox *caretBox = nearestCaretBox(it, d->m_caretViewContext, x, absx, absy);
03718 
03719   placeCaretOnLine(caretBox, x, absx, absy);
03720 }
03721 
03722 void KHTMLView::moveCaretPrevWord()
03723 {
03724   moveCaretBy(false, CaretByWord, 1);
03725 }
03726 
03727 void KHTMLView::moveCaretNextWord()
03728 {
03729   moveCaretBy(true, CaretByWord, 1);
03730 }
03731 
03732 void KHTMLView::moveCaretPrevLine(int n)
03733 {
03734   moveCaretByLine(false, n);
03735 }
03736 
03737 void KHTMLView::moveCaretNextLine(int n)
03738 {
03739   moveCaretByLine(true, n);
03740 }
03741 
03742 void KHTMLView::moveCaretPrevPage()
03743 {
03744   moveCaretByPage(false);
03745 }
03746 
03747 void KHTMLView::moveCaretNextPage()
03748 {
03749   moveCaretByPage(true);
03750 }
03751 
03752 void KHTMLView::moveCaretToLineBegin()
03753 {
03754   moveCaretToLineBoundary(false);
03755 }
03756 
03757 void KHTMLView::moveCaretToLineEnd()
03758 {
03759   moveCaretToLineBoundary(true);
03760 }
03761 
03762 #endif // KHTML_NO_CARET
03763 
03764 #undef DEBUG_CARETMODE
KDE Logo
This file is part of the documentation for khtml Library Version 3.3.90.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Mar 30 10:22:20 2005 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003