khtml Library API Documentation

khtmlview.cpp

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