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