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