khtmlview.cpp

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