khtml Library API Documentation

khtmlview.cpp

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