khtmlview.cpp

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