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