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