• Skip to content
  • Skip to link menu
KDE 3.5 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

KHTML

khtmlview.cpp

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

KHTML

Skip menu "KHTML"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • dcop
  • DNSSD
  • interfaces
  • Kate
  • kconf_update
  • KDECore
  • KDED
  • kdefx
  • KDEsu
  • kdeui
  • KDocTools
  • KHTML
  • KImgIO
  • KInit
  • kio
  • kioslave
  • KJS
  • KNewStuff
  • KParts
  • KUtils
Generated for API Reference by doxygen 1.5.9
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal