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