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