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