khtmlview.cpp

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