khtmlview.cpp

00001 /* This file is part of the KDE project
00002  *
00003  * Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
00004  *                     1999 Lars Knoll <knoll@kde.org>
00005  *                     1999 Antti Koivisto <koivisto@kde.org>
00006  *                     2000-2004 Dirk Mueller <mueller@kde.org>
00007  *                     2003 Leo Savernik <l.savernik@aon.at>
00008  *                     2003-2004 Apple Computer, Inc.
00009  *
00010  * This library is free software; you can redistribute it and/or
00011  * modify it under the terms of the GNU Library General Public
00012  * License as published by the Free Software Foundation; either
00013  * version 2 of the License, or (at your option) any later version.
00014  *
00015  * This library is distributed in the hope that it will be useful,
00016  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00018  * Library General Public License for more details.
00019  *
00020  * You should have received a copy of the GNU Library General Public License
00021  * along with this library; see the file COPYING.LIB.  If not, write to
00022  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00023  * Boston, MA 02110-1301, USA.
00024  */
00025 
00026 
00027 #include "khtmlview.moc"
00028 
00029 #include "khtmlview.h"
00030 
00031 #include "khtml_part.h"
00032 #include "khtml_events.h"
00033 
00034 #include "html/html_documentimpl.h"
00035 #include "html/html_inlineimpl.h"
00036 #include "html/html_formimpl.h"
00037 #include "rendering/render_arena.h"
00038 #include "rendering/render_canvas.h"
00039 #include "rendering/render_frames.h"
00040 #include "rendering/render_replaced.h"
00041 #include "rendering/render_layer.h"
00042 #include "rendering/render_line.h"
00043 #include "rendering/render_table.h"
00044 // removeme
00045 #define protected public
00046 #include "rendering/render_text.h"
00047 #undef protected
00048 #include "xml/dom2_eventsimpl.h"
00049 #include "css/cssstyleselector.h"
00050 #include "css/csshelper.h"
00051 #include "misc/htmlhashes.h"
00052 #include "misc/helper.h"
00053 #include "khtml_settings.h"
00054 #include "khtml_printsettings.h"
00055 
00056 #include "khtmlpart_p.h"
00057 
00058 #ifndef KHTML_NO_CARET
00059 #include "khtml_caret_p.h"
00060 #include "xml/dom2_rangeimpl.h"
00061 #endif
00062 
00063 #include <kapplication.h>
00064 #include <kcursor.h>
00065 #include <kdebug.h>
00066 #include <kdialogbase.h>
00067 #include <kiconloader.h>
00068 #include <kimageio.h>
00069 #include <klocale.h>
00070 #include <knotifyclient.h>
00071 #include <kprinter.h>
00072 #include <ksimpleconfig.h>
00073 #include <kstandarddirs.h>
00074 #include <kstdaccel.h>
00075 #include <kstringhandler.h>
00076 #include <kurldrag.h>
00077 
00078 #include <qbitmap.h>
00079 #include <qlabel.h>
00080 #include <qobjectlist.h>
00081 #include <qpaintdevicemetrics.h>
00082 #include <qpainter.h>
00083 #include <qptrdict.h>
00084 #include <qtooltip.h>
00085 #include <qstring.h>
00086 #include <qstylesheet.h>
00087 #include <qtimer.h>
00088 #include <qvaluevector.h>
00089 
00090 //#define DEBUG_NO_PAINT_BUFFER
00091 
00092 //#define DEBUG_FLICKER
00093 
00094 //#define DEBUG_PIXEL
00095 
00096 #ifdef Q_WS_X11
00097 #include <X11/Xlib.h>
00098 #include <fixx11h.h>
00099 #endif
00100 
00101 #define PAINT_BUFFER_HEIGHT 128
00102 
00103 #if 0
00104 namespace khtml {
00105     void dumpLineBoxes(RenderFlow *flow);
00106 }
00107 #endif
00108 
00109 using namespace DOM;
00110 using namespace khtml;
00111 class KHTMLToolTip;
00112 
00113 
00114 #ifndef QT_NO_TOOLTIP
00115 
00116 class KHTMLToolTip : public QToolTip
00117 {
00118 public:
00119     KHTMLToolTip(KHTMLView *view,  KHTMLViewPrivate* vp) : QToolTip(view->viewport())
00120     {
00121         m_view = view;
00122         m_viewprivate = vp;
00123     };
00124 
00125 protected:
00126     virtual void maybeTip(const QPoint &);
00127 
00128 private:
00129     KHTMLView *m_view;
00130     KHTMLViewPrivate* m_viewprivate;
00131 };
00132 
00133 #endif
00134 
00135 class KHTMLViewPrivate {
00136     friend class KHTMLToolTip;
00137 public:
00138 
00139     enum PseudoFocusNodes {
00140     PFNone,
00141     PFTop,
00142     PFBottom
00143     };
00144 
00145     enum CompletedState {
00146         CSNone = 0,
00147         CSFull,
00148         CSActionPending
00149     };
00150 
00151     KHTMLViewPrivate()
00152         : underMouse( 0 ), underMouseNonShared( 0 ), visibleWidgets( 107 )
00153     {
00154 #ifndef KHTML_NO_CARET
00155     m_caretViewContext = 0;
00156     m_editorContext = 0;
00157 #endif // KHTML_NO_CARET
00158         postponed_autorepeat = NULL;
00159         reset();
00160         vmode = QScrollView::Auto;
00161     hmode = QScrollView::Auto;
00162         tp=0;
00163         paintBuffer=0;
00164         vertPaintBuffer=0;
00165         formCompletions=0;
00166         prevScrollbarVisible = true;
00167     tooltip = 0;
00168         possibleTripleClick = false;
00169         emitCompletedAfterRepaint = CSNone;
00170     cursor_icon_widget = NULL;
00171         m_mouseScrollTimer = 0;
00172         m_mouseScrollIndicator = 0;
00173     }
00174     ~KHTMLViewPrivate()
00175     {
00176         delete formCompletions;
00177         delete tp; tp = 0;
00178         delete paintBuffer; paintBuffer =0;
00179         delete vertPaintBuffer;
00180         delete postponed_autorepeat;
00181         if (underMouse)
00182         underMouse->deref();
00183         if (underMouseNonShared)
00184         underMouseNonShared->deref();
00185     delete tooltip;
00186 #ifndef KHTML_NO_CARET
00187     delete m_caretViewContext;
00188     delete m_editorContext;
00189 #endif // KHTML_NO_CARET
00190         delete cursor_icon_widget;
00191         delete m_mouseScrollTimer;
00192         delete m_mouseScrollIndicator;
00193     }
00194     void reset()
00195     {
00196         if (underMouse)
00197         underMouse->deref();
00198     underMouse = 0;
00199         if (underMouseNonShared)
00200         underMouseNonShared->deref();
00201     underMouseNonShared = 0;
00202         linkPressed = false;
00203         useSlowRepaints = false;
00204     tabMovePending = false;
00205     lastTabbingDirection = true;
00206     pseudoFocusNode = PFNone;
00207 #ifndef KHTML_NO_SCROLLBARS
00208         //We don't turn off the toolbars here
00209     //since if the user turns them
00210     //off, then chances are they want them turned
00211     //off always - even after a reset.
00212 #else
00213         vmode = QScrollView::AlwaysOff;
00214         hmode = QScrollView::AlwaysOff;
00215 #endif
00216 #ifdef DEBUG_PIXEL
00217         timer.start();
00218         pixelbooth = 0;
00219         repaintbooth = 0;
00220 #endif
00221         scrollBarMoved = false;
00222         contentsMoving = false;
00223         ignoreWheelEvents = false;
00224     borderX = 30;
00225     borderY = 30;
00226         paged = false;
00227     clickX = -1;
00228     clickY = -1;
00229         prevMouseX = -1;
00230         prevMouseY = -1;
00231     clickCount = 0;
00232     isDoubleClick = false;
00233     scrollingSelf = false;
00234         delete postponed_autorepeat;
00235         postponed_autorepeat = NULL;
00236     layoutTimerId = 0;
00237         repaintTimerId = 0;
00238         scrollTimerId = 0;
00239         scrollSuspended = false;
00240         scrollSuspendPreActivate = false;
00241         complete = false;
00242         firstRelayout = true;
00243         needsFullRepaint = true;
00244         dirtyLayout = false;
00245         layoutSchedulingEnabled = true;
00246         painting = false;
00247         updateRegion = QRegion();
00248         m_dialogsAllowed = true;
00249 #ifndef KHTML_NO_CARET
00250         if (m_caretViewContext) {
00251           m_caretViewContext->caretMoved = false;
00252       m_caretViewContext->keyReleasePending = false;
00253         }/*end if*/
00254 #endif // KHTML_NO_CARET
00255 #ifndef KHTML_NO_TYPE_AHEAD_FIND
00256         typeAheadActivated = false;
00257 #endif // KHTML_NO_TYPE_AHEAD_FIND
00258     accessKeysActivated = false;
00259     accessKeysPreActivate = false;
00260 
00261         // We ref/deref to ensure defaultHTMLSettings is available
00262         KHTMLFactory::ref();
00263         accessKeysEnabled = KHTMLFactory::defaultHTMLSettings()->accessKeysEnabled();
00264         KHTMLFactory::deref();
00265 
00266         emitCompletedAfterRepaint = CSNone;
00267     }
00268     void newScrollTimer(QWidget *view, int tid)
00269     {
00270         //kdDebug(6000) << "newScrollTimer timer " << tid << endl;
00271         view->killTimer(scrollTimerId);
00272         scrollTimerId = tid;
00273         scrollSuspended = false;
00274     }
00275     enum ScrollDirection { ScrollLeft, ScrollRight, ScrollUp, ScrollDown };
00276 
00277     void adjustScroller(QWidget *view, ScrollDirection direction, ScrollDirection oppositedir)
00278     {
00279         static const struct { int msec, pixels; } timings [] = {
00280             {320,1}, {224,1}, {160,1}, {112,1}, {80,1}, {56,1}, {40,1},
00281             {28,1}, {20,1}, {20,2}, {20,3}, {20,4}, {20,6}, {20,8}, {0,0}
00282         };
00283         if (!scrollTimerId ||
00284             (static_cast<int>(scrollDirection) != direction &&
00285              (static_cast<int>(scrollDirection) != oppositedir || scrollSuspended))) {
00286             scrollTiming = 6;
00287             scrollBy = timings[scrollTiming].pixels;
00288             scrollDirection = direction;
00289             newScrollTimer(view, view->startTimer(timings[scrollTiming].msec));
00290         } else if (scrollDirection == direction &&
00291                    timings[scrollTiming+1].msec && !scrollSuspended) {
00292             scrollBy = timings[++scrollTiming].pixels;
00293             newScrollTimer(view, view->startTimer(timings[scrollTiming].msec));
00294         } else if (scrollDirection == oppositedir) {
00295             if (scrollTiming) {
00296                 scrollBy = timings[--scrollTiming].pixels;
00297                 newScrollTimer(view, view->startTimer(timings[scrollTiming].msec));
00298             }
00299         }
00300         scrollSuspended = false;
00301     }
00302 
00303 #ifndef KHTML_NO_CARET
00304 
00307     CaretViewContext *caretViewContext() {
00308       if (!m_caretViewContext) m_caretViewContext = new CaretViewContext();
00309       return m_caretViewContext;
00310     }
00314     EditorContext *editorContext() {
00315       if (!m_editorContext) m_editorContext = new EditorContext();
00316       return m_editorContext;
00317     }
00318 #endif // KHTML_NO_CARET
00319 
00320 #ifdef DEBUG_PIXEL
00321     QTime timer;
00322     unsigned int pixelbooth;
00323     unsigned int repaintbooth;
00324 #endif
00325 
00326     QPainter *tp;
00327     QPixmap  *paintBuffer;
00328     QPixmap  *vertPaintBuffer;
00329     NodeImpl *underMouse;
00330     NodeImpl *underMouseNonShared;
00331 
00332     bool tabMovePending:1;
00333     bool lastTabbingDirection:1;
00334     PseudoFocusNodes pseudoFocusNode:2;
00335     bool scrollBarMoved:1;
00336     bool contentsMoving:1;
00337 
00338     QScrollView::ScrollBarMode vmode;
00339     QScrollView::ScrollBarMode hmode;
00340     bool prevScrollbarVisible:1;
00341     bool linkPressed:1;
00342     bool useSlowRepaints:1;
00343     bool ignoreWheelEvents:1;
00344 
00345     int borderX, borderY;
00346     KSimpleConfig *formCompletions;
00347 
00348     bool paged;
00349 
00350     int clickX, clickY, clickCount;
00351     bool isDoubleClick;
00352 
00353     int prevMouseX, prevMouseY;
00354     bool scrollingSelf;
00355     int layoutTimerId;
00356     QKeyEvent* postponed_autorepeat;
00357 
00358     int repaintTimerId;
00359     int scrollTimerId;
00360     int scrollTiming;
00361     int scrollBy;
00362     ScrollDirection scrollDirection     :2;
00363     bool scrollSuspended            :1;
00364     bool scrollSuspendPreActivate       :1;
00365     bool complete               :1;
00366     bool firstRelayout              :1;
00367     bool layoutSchedulingEnabled        :1;
00368     bool needsFullRepaint           :1;
00369     bool painting               :1;
00370     bool possibleTripleClick            :1;
00371     bool dirtyLayout                           :1;
00372     bool m_dialogsAllowed           :1;
00373     QRegion updateRegion;
00374     KHTMLToolTip *tooltip;
00375     QPtrDict<QWidget> visibleWidgets;
00376 #ifndef KHTML_NO_CARET
00377     CaretViewContext *m_caretViewContext;
00378     EditorContext *m_editorContext;
00379 #endif // KHTML_NO_CARET
00380 #ifndef KHTML_NO_TYPE_AHEAD_FIND
00381     QString findString;
00382     QTimer timer;
00383     bool findLinksOnly;
00384     bool typeAheadActivated;
00385 #endif // KHTML_NO_TYPE_AHEAD_FIND
00386     bool accessKeysEnabled;
00387     bool accessKeysActivated;
00388     bool accessKeysPreActivate;
00389     CompletedState emitCompletedAfterRepaint;
00390 
00391     QWidget* cursor_icon_widget;
00392 
00393     // scrolling activated by MMB
00394     short m_mouseScroll_byX;
00395     short m_mouseScroll_byY;
00396     QTimer *m_mouseScrollTimer;
00397     QWidget *m_mouseScrollIndicator;
00398 };
00399 
00400 #ifndef QT_NO_TOOLTIP
00401 
00411 static bool findImageMapRect(HTMLImageElementImpl *img, const QPoint &scrollOfs,
00412             const QPoint &p, QRect &r, QString &s)
00413 {
00414     HTMLMapElementImpl* map;
00415     if (img && img->getDocument()->isHTMLDocument() &&
00416         (map = static_cast<HTMLDocumentImpl*>(img->getDocument())->getMap(img->imageMap()))) {
00417         RenderObject::NodeInfo info(true, false);
00418         RenderObject *rend = img->renderer();
00419         int ax, ay;
00420         if (!rend || !rend->absolutePosition(ax, ay))
00421             return false;
00422         // we're a client side image map
00423         bool inside = map->mapMouseEvent(p.x() - ax + scrollOfs.x(),
00424                 p.y() - ay + scrollOfs.y(), rend->contentWidth(),
00425                 rend->contentHeight(), info);
00426         if (inside && info.URLElement()) {
00427             HTMLAreaElementImpl *area = static_cast<HTMLAreaElementImpl *>(info.URLElement());
00428             Q_ASSERT(area->id() == ID_AREA);
00429             s = area->getAttribute(ATTR_TITLE).string();
00430             QRegion reg = area->cachedRegion();
00431             if (!s.isEmpty() && !reg.isEmpty()) {
00432                 r = reg.boundingRect();
00433                 r.moveBy(ax, ay);
00434                 return true;
00435             }
00436         }
00437     }
00438     return false;
00439 }
00440 
00441 void KHTMLToolTip::maybeTip(const QPoint& p)
00442 {
00443     DOM::NodeImpl *node = m_viewprivate->underMouseNonShared;
00444     QRect region;
00445     while ( node ) {
00446         if ( node->isElementNode() ) {
00447             DOM::ElementImpl *e = static_cast<DOM::ElementImpl*>( node );
00448             QRect r;
00449             QString s;
00450             bool found = false;
00451             // for images, check if it is part of a client-side image map,
00452             // and query the <area>s' title attributes, too
00453             if (e->id() == ID_IMG && !e->getAttribute( ATTR_USEMAP ).isEmpty()) {
00454                 found = findImageMapRect(static_cast<HTMLImageElementImpl *>(e),
00455                             m_view->viewportToContents(QPoint(0, 0)), p, r, s);
00456             }
00457             if (!found) {
00458                 s = e->getAttribute( ATTR_TITLE ).string();
00459                 r = node->getRect();
00460             }
00461             region |= QRect( m_view->contentsToViewport( r.topLeft() ), r.size() );
00462             if ( !s.isEmpty() ) {
00463                 tip( region, QStyleSheet::convertFromPlainText( s, QStyleSheetItem::WhiteSpaceNormal ) );
00464                 break;
00465             }
00466         }
00467         node = node->parentNode();
00468     }
00469 }
00470 #endif
00471 
00472 KHTMLView::KHTMLView( KHTMLPart *part, QWidget *parent, const char *name)
00473     : QScrollView( parent, name, WResizeNoErase | WRepaintNoErase )
00474 {
00475     m_medium = "screen";
00476 
00477     m_part = part;
00478     d = new KHTMLViewPrivate;
00479     QScrollView::setVScrollBarMode(d->vmode);
00480     QScrollView::setHScrollBarMode(d->hmode);
00481     connect(kapp, SIGNAL(kdisplayPaletteChanged()), this, SLOT(slotPaletteChanged()));
00482     connect(this, SIGNAL(contentsMoving(int, int)), this, SLOT(slotScrollBarMoved()));
00483 
00484     // initialize QScrollView
00485     enableClipper(true);
00486     // hack to get unclipped painting on the viewport.
00487     static_cast<KHTMLView *>(static_cast<QWidget *>(viewport()))->setWFlags(WPaintUnclipped);
00488 
00489     setResizePolicy(Manual);
00490     viewport()->setMouseTracking(true);
00491     viewport()->setBackgroundMode(NoBackground);
00492 
00493     KImageIO::registerFormats();
00494 
00495 #ifndef QT_NO_TOOLTIP
00496     d->tooltip = new KHTMLToolTip( this, d );
00497 #endif
00498 
00499 #ifndef KHTML_NO_TYPE_AHEAD_FIND
00500     connect(&d->timer, SIGNAL(timeout()), this, SLOT(findTimeout()));
00501 #endif // KHTML_NO_TYPE_AHEAD_FIND
00502 
00503     init();
00504 
00505     viewport()->show();
00506 }
00507 
00508 KHTMLView::~KHTMLView()
00509 {
00510     closeChildDialogs();
00511     if (m_part)
00512     {
00513         //WABA: Is this Ok? Do I need to deref it as well?
00514         //Does this need to be done somewhere else?
00515         DOM::DocumentImpl *doc = m_part->xmlDocImpl();
00516         if (doc)
00517             doc->detach();
00518     }
00519     delete d; d = 0;
00520 }
00521 
00522 void KHTMLView::init()
00523 {
00524     if(!d->paintBuffer) d->paintBuffer = new QPixmap(PAINT_BUFFER_HEIGHT, PAINT_BUFFER_HEIGHT);
00525     if(!d->vertPaintBuffer)
00526         d->vertPaintBuffer = new QPixmap(10, PAINT_BUFFER_HEIGHT);
00527     if(!d->tp) d->tp = new QPainter();
00528 
00529     setFocusPolicy(QWidget::StrongFocus);
00530     viewport()->setFocusProxy(this);
00531 
00532     _marginWidth = -1; // undefined
00533     _marginHeight = -1;
00534     _width = 0;
00535     _height = 0;
00536 
00537     installEventFilter(this);
00538 
00539     setAcceptDrops(true);
00540     QSize s = viewportSize(4095, 4095);
00541     resizeContents(s.width(), s.height());
00542 }
00543 
00544 void KHTMLView::clear()
00545 {
00546     // work around QScrollview's unbelievable bugginess
00547     setStaticBackground(true);
00548 #ifndef KHTML_NO_CARET
00549     if (!m_part->isCaretMode() && !m_part->isEditable()) caretOff();
00550 #endif
00551 
00552 #ifndef KHTML_NO_TYPE_AHEAD_FIND
00553     if( d->typeAheadActivated )
00554         findTimeout();
00555 #endif
00556     if (d->accessKeysEnabled && d->accessKeysActivated)
00557         accessKeysTimeout();
00558     viewport()->unsetCursor();
00559     if ( d->cursor_icon_widget )
00560         d->cursor_icon_widget->hide();
00561     d->reset();
00562     killTimers();
00563     emit cleared();
00564 
00565     QScrollView::setHScrollBarMode(d->hmode);
00566     QScrollView::setVScrollBarMode(d->vmode);
00567     verticalScrollBar()->setEnabled( false );
00568     horizontalScrollBar()->setEnabled( false );
00569 }
00570 
00571 void KHTMLView::hideEvent(QHideEvent* e)
00572 {
00573     QScrollView::hideEvent(e);
00574 }
00575 
00576 void KHTMLView::showEvent(QShowEvent* e)
00577 {
00578     QScrollView::showEvent(e);
00579 }
00580 
00581 void KHTMLView::resizeEvent (QResizeEvent* e)
00582 {
00583     int dw = e->oldSize().width() - e->size().width();
00584     int dh = e->oldSize().height() - e->size().height();
00585 
00586     // if we are shrinking the view, don't allow the content to overflow
00587     // before the layout occurs - we don't know if we need scrollbars yet
00588     dw = dw>0 ? kMax(0, contentsWidth()-dw) : contentsWidth();
00589     dh = dh>0 ? kMax(0, contentsHeight()-dh) : contentsHeight();
00590 
00591     resizeContents(dw, dh);
00592 
00593     QScrollView::resizeEvent(e);
00594 
00595     if ( m_part && m_part->xmlDocImpl() )
00596         m_part->xmlDocImpl()->dispatchWindowEvent( EventImpl::RESIZE_EVENT, false, false );
00597 }
00598 
00599 void KHTMLView::viewportResizeEvent (QResizeEvent* e)
00600 {
00601     QScrollView::viewportResizeEvent(e);
00602 
00603     //int w = visibleWidth();
00604     //int h = visibleHeight();
00605 
00606     if (d->layoutSchedulingEnabled)
00607         layout();
00608 #ifndef KHTML_NO_CARET
00609     else {
00610         hideCaret();
00611         recalcAndStoreCaretPos();
00612     showCaret();
00613     }/*end if*/
00614 #endif
00615 
00616     KApplication::sendPostedEvents(viewport(), QEvent::Paint);
00617 }
00618 
00619 // this is to get rid of a compiler virtual overload mismatch warning. do not remove
00620 void KHTMLView::drawContents( QPainter*)
00621 {
00622 }
00623 
00624 void KHTMLView::drawContents( QPainter *p, int ex, int ey, int ew, int eh )
00625 {
00626 #ifdef DEBUG_PIXEL
00627 
00628     if ( d->timer.elapsed() > 5000 ) {
00629         qDebug( "drawed %d pixels in %d repaints the last %d milliseconds",
00630                 d->pixelbooth, d->repaintbooth,  d->timer.elapsed() );
00631         d->timer.restart();
00632         d->pixelbooth = 0;
00633         d->repaintbooth = 0;
00634     }
00635     d->pixelbooth += ew*eh;
00636     d->repaintbooth++;
00637 #endif
00638 
00639     //kdDebug( 6000 ) << "drawContents this="<< this <<" x=" << ex << ",y=" << ey << ",w=" << ew << ",h=" << eh << endl;
00640     if(!m_part || !m_part->xmlDocImpl() || !m_part->xmlDocImpl()->renderer()) {
00641         p->fillRect(ex, ey, ew, eh, palette().active().brush(QColorGroup::Base));
00642         return;
00643     } else if ( d->complete && static_cast<RenderCanvas*>(m_part->xmlDocImpl()->renderer())->needsLayout() ) {
00644         // an external update request happens while we have a layout scheduled
00645         unscheduleRelayout();
00646         layout();
00647     }
00648 
00649     if (d->painting) {
00650         kdDebug( 6000 ) << "WARNING: drawContents reentered! " << endl;
00651         return;
00652     }
00653     d->painting = true;
00654 
00655     QPoint pt = contentsToViewport(QPoint(ex, ey));
00656     QRegion cr = QRect(pt.x(), pt.y(), ew, eh);
00657 
00658     // kdDebug(6000) << "clip rect: " << QRect(pt.x(), pt.y(), ew, eh) << endl;
00659     for (QPtrDictIterator<QWidget> it(d->visibleWidgets); it.current(); ++it) {
00660     QWidget *w = it.current();
00661     RenderWidget* rw = static_cast<RenderWidget*>( it.currentKey() );
00662     if (w && rw && !rw->isKHTMLWidget()) { 
00663             int x, y;
00664             rw->absolutePosition(x, y);
00665             contentsToViewport(x, y, x, y);
00666             int pbx = rw->borderLeft()+rw->paddingLeft();
00667             int pby = rw->borderTop()+rw->paddingTop();
00668             QRect g = QRect(x+pbx, y+pby, 
00669                             rw->width()-pbx-rw->borderRight()-rw->paddingRight(), 
00670                             rw->height()-pby-rw->borderBottom()-rw->paddingBottom());
00671             if ( !rw->isFrame() && ((g.top() > pt.y()+eh) || (g.bottom() <= pt.y()) ||
00672                                     (g.right() <= pt.x()) || (g.left() > pt.x()+ew) ))
00673                 continue;
00674             RenderLayer* rl = rw->needsMask() ? rw->enclosingStackingContext() : 0;
00675             QRegion mask = rl ? rl->getMask() : QRegion();
00676             if (!mask.isNull()) {
00677                 QPoint o(0,0);
00678                 o = contentsToViewport(o);
00679                 mask.translate(o.x(),o.y());
00680                 mask = mask.intersect( QRect(g.x(),g.y(),g.width(),g.height()) );
00681                 cr -= mask;
00682             } else {
00683                 cr -= g;
00684             }
00685         }
00686     }
00687 
00688 #if 0
00689     // this is commonly the case with framesets. we still do
00690     // want to paint them, otherwise the widgets don't get placed.
00691     if (cr.isEmpty()) {
00692         d->painting = false;
00693     return;
00694     }
00695 #endif
00696 
00697 #ifndef DEBUG_NO_PAINT_BUFFER
00698     p->setClipRegion(cr);
00699 
00700     if (eh > PAINT_BUFFER_HEIGHT && ew <= 10) {
00701         if ( d->vertPaintBuffer->height() < visibleHeight() )
00702             d->vertPaintBuffer->resize(10, visibleHeight());
00703         d->tp->begin(d->vertPaintBuffer);
00704         d->tp->translate(-ex, -ey);
00705         d->tp->fillRect(ex, ey, ew, eh, palette().active().brush(QColorGroup::Base));
00706         m_part->xmlDocImpl()->renderer()->layer()->paint(d->tp, QRect(ex, ey, ew, eh));
00707         d->tp->end();
00708     p->drawPixmap(ex, ey, *d->vertPaintBuffer, 0, 0, ew, eh);
00709     }
00710     else {
00711         if ( d->paintBuffer->width() < visibleWidth() )
00712             d->paintBuffer->resize(visibleWidth(),PAINT_BUFFER_HEIGHT);
00713 
00714         int py=0;
00715         while (py < eh) {
00716             int ph = eh-py < PAINT_BUFFER_HEIGHT ? eh-py : PAINT_BUFFER_HEIGHT;
00717             d->tp->begin(d->paintBuffer);
00718             d->tp->translate(-ex, -ey-py);
00719             d->tp->fillRect(ex, ey+py, ew, ph, palette().active().brush(QColorGroup::Base));
00720             m_part->xmlDocImpl()->renderer()->layer()->paint(d->tp, QRect(ex, ey+py, ew, ph));
00721             d->tp->end();
00722 
00723         p->drawPixmap(ex, ey+py, *d->paintBuffer, 0, 0, ew, ph);
00724             py += PAINT_BUFFER_HEIGHT;
00725         }
00726     }
00727 #else // !DEBUG_NO_PAINT_BUFFER
00728 static int cnt=0;
00729     ex = contentsX(); ey = contentsY();
00730     ew = visibleWidth(); eh = visibleHeight();
00731     QRect pr(ex,ey,ew,eh);
00732     kdDebug() << "[" << ++cnt << "]" << " clip region: " << pr << endl;
00733 //  p->setClipRegion(QRect(0,0,ew,eh));
00734 //        p->translate(-ex, -ey);
00735         p->fillRect(ex, ey, ew, eh, palette().active().brush(QColorGroup::Base));
00736         m_part->xmlDocImpl()->renderer()->layer()->paint(p, pr);
00737 #endif // DEBUG_NO_PAINT_BUFFER
00738 
00739 #ifndef KHTML_NO_CARET
00740     if (d->m_caretViewContext && d->m_caretViewContext->visible) {
00741         QRect pos(d->m_caretViewContext->x, d->m_caretViewContext->y,
00742         d->m_caretViewContext->width, d->m_caretViewContext->height);
00743         if (pos.intersects(QRect(ex, ey, ew, eh))) {
00744             p->setRasterOp(XorROP);
00745         p->setPen(white);
00746         if (pos.width() == 1)
00747               p->drawLine(pos.topLeft(), pos.bottomRight());
00748         else {
00749           p->fillRect(pos, white);
00750         }/*end if*/
00751     }/*end if*/
00752     }/*end if*/
00753 #endif // KHTML_NO_CARET
00754 
00755 //    p->setPen(QPen(magenta,0,DashDotDotLine));
00756 //    p->drawRect(dbg_paint_rect);
00757 
00758     khtml::DrawContentsEvent event( p, ex, ey, ew, eh );
00759     QApplication::sendEvent( m_part, &event );
00760 
00761     d->painting = false;
00762 }
00763 
00764 void KHTMLView::setMarginWidth(int w)
00765 {
00766     // make it update the rendering area when set
00767     _marginWidth = w;
00768 }
00769 
00770 void KHTMLView::setMarginHeight(int h)
00771 {
00772     // make it update the rendering area when set
00773     _marginHeight = h;
00774 }
00775 
00776 void KHTMLView::layout()
00777 {
00778     if( m_part && m_part->xmlDocImpl() ) {
00779         DOM::DocumentImpl *document = m_part->xmlDocImpl();
00780 
00781         khtml::RenderCanvas* canvas = static_cast<khtml::RenderCanvas *>(document->renderer());
00782         if ( !canvas ) return;
00783 
00784         d->layoutSchedulingEnabled=false;
00785 
00786         // the reference object for the overflow property on canvas 
00787         RenderObject * ref = 0;
00788         RenderObject* root = document->documentElement() ? document->documentElement()->renderer() : 0;
00789 
00790         if (document->isHTMLDocument()) {
00791              NodeImpl *body = static_cast<HTMLDocumentImpl*>(document)->body();
00792              if(body && body->renderer() && body->id() == ID_FRAMESET) {
00793                  QScrollView::setVScrollBarMode(AlwaysOff);
00794                  QScrollView::setHScrollBarMode(AlwaysOff);
00795                  body->renderer()->setNeedsLayout(true);
00796 //                  if (d->tooltip) {
00797 //                      delete d->tooltip;
00798 //                      d->tooltip = 0;
00799 //                  }
00800              }
00801              else {
00802                  if (!d->tooltip)
00803                      d->tooltip = new KHTMLToolTip( this, d );
00804                  // only apply body's overflow to canvas if root as a visible overflow
00805                  if (root) 
00806                      ref = (!body || root->style()->hidesOverflow()) ? root : body->renderer();
00807              }
00808         } else {
00809             ref = root;
00810         }
00811         
00812         if (ref) {
00813             if( ref->style()->overflow() == OHIDDEN ) {
00814                 if (d->vmode == Auto) QScrollView::setVScrollBarMode(AlwaysOff);
00815                 if (d->hmode == Auto) QScrollView::setHScrollBarMode(AlwaysOff);
00816             } else {
00817                 if (QScrollView::vScrollBarMode() == AlwaysOff) QScrollView::setVScrollBarMode(d->vmode);
00818                 if (QScrollView::hScrollBarMode() == AlwaysOff) QScrollView::setHScrollBarMode(d->hmode);
00819             }            
00820         }
00821         d->needsFullRepaint = d->firstRelayout;
00822         if (_height !=  visibleHeight() || _width != visibleWidth()) {;
00823             d->needsFullRepaint = true;
00824             _height = visibleHeight();
00825             _width = visibleWidth();
00826         }
00827         //QTime qt;
00828         //qt.start();
00829         canvas->layout();
00830 
00831         emit finishedLayout();
00832         if (d->firstRelayout) {
00833             // make sure firstRelayout is set to false now in case this layout
00834             // wasn't scheduled
00835             d->firstRelayout = false;
00836             verticalScrollBar()->setEnabled( true );
00837             horizontalScrollBar()->setEnabled( true );
00838         }
00839 #if 0
00840     ElementImpl *listitem = m_part->xmlDocImpl()->getElementById("__test_element__");
00841     if (listitem) kdDebug(6000) << "after layout, before repaint" << endl;
00842     if (listitem) dumpLineBoxes(static_cast<RenderFlow *>(listitem->renderer()));
00843 #endif
00844 #ifndef KHTML_NO_CARET
00845         hideCaret();
00846         if ((m_part->isCaretMode() || m_part->isEditable())
00847             && !d->complete && d->m_caretViewContext
00848                 && !d->m_caretViewContext->caretMoved) {
00849             initCaret();
00850         } else {
00851         recalcAndStoreCaretPos();
00852         showCaret();
00853         }/*end if*/
00854 #endif
00855         if (d->accessKeysEnabled && d->accessKeysActivated) {
00856             emit hideAccessKeys();
00857             displayAccessKeys();
00858         }
00859         //kdDebug( 6000 ) << "TIME: layout() dt=" << qt.elapsed() << endl;
00860     }
00861     else
00862        _width = visibleWidth();
00863 
00864     killTimer(d->layoutTimerId);
00865     d->layoutTimerId = 0;
00866     d->layoutSchedulingEnabled=true;
00867 }
00868 
00869 void KHTMLView::closeChildDialogs()
00870 {
00871     QObjectList *dlgs = queryList("QDialog");
00872     for (QObject *dlg = dlgs->first(); dlg; dlg = dlgs->next())
00873     {
00874         KDialogBase* dlgbase = dynamic_cast<KDialogBase *>( dlg );
00875         if ( dlgbase ) {
00876             if ( dlgbase->testWFlags( WShowModal ) ) {
00877                 kdDebug(6000) << "closeChildDialogs: closing dialog " << dlgbase << endl;
00878                 // close() ends up calling QButton::animateClick, which isn't immediate
00879                 // we need something the exits the event loop immediately (#49068)
00880                 dlgbase->cancel();
00881             }
00882         }
00883         else
00884         {
00885             kdWarning() << "closeChildDialogs: not a KDialogBase! Don't use QDialogs in KDE! " << static_cast<QWidget*>(dlg) << endl;
00886             static_cast<QWidget*>(dlg)->hide();
00887         }
00888     }
00889     delete dlgs;
00890     d->m_dialogsAllowed = false;
00891 }
00892 
00893 bool KHTMLView::dialogsAllowed() {
00894     bool allowed = d->m_dialogsAllowed;
00895     KHTMLPart* p = m_part->parentPart();
00896     if (p && p->view())
00897         allowed &= p->view()->dialogsAllowed();
00898     return allowed;
00899 }
00900 
00901 void KHTMLView::closeEvent( QCloseEvent* ev )
00902 {
00903     closeChildDialogs();
00904     QScrollView::closeEvent( ev );
00905 }
00906 
00907 //
00908 // Event Handling
00909 //
00911 
00912 void KHTMLView::viewportMousePressEvent( QMouseEvent *_mouse )
00913 {
00914     if (!m_part->xmlDocImpl()) return;
00915     if (d->possibleTripleClick && ( _mouse->button() & MouseButtonMask ) == LeftButton)
00916     {
00917         viewportMouseDoubleClickEvent( _mouse ); // it handles triple clicks too
00918         return;
00919     }
00920 
00921     int xm, ym;
00922     viewportToContents(_mouse->x(), _mouse->y(), xm, ym);
00923     //kdDebug( 6000 ) << "mousePressEvent: viewport=("<<_mouse->x()<<"/"<<_mouse->y()<<"), contents=(" << xm << "/" << ym << ")\n";
00924 
00925     d->isDoubleClick = false;
00926 
00927     DOM::NodeImpl::MouseEvent mev( _mouse->stateAfter(), DOM::NodeImpl::MousePress );
00928     m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev );
00929 
00930     //kdDebug(6000) << "innerNode="<<mev.innerNode.nodeName().string()<<endl;
00931 
00932     if ( (_mouse->button() == MidButton) &&
00933           !m_part->d->m_bOpenMiddleClick && !d->m_mouseScrollTimer &&
00934           mev.url.isNull() && (mev.innerNode.elementId() != ID_INPUT) ) {
00935         QPoint point = mapFromGlobal( _mouse->globalPos() );
00936 
00937         d->m_mouseScroll_byX = 0;
00938         d->m_mouseScroll_byY = 0;
00939 
00940         d->m_mouseScrollTimer = new QTimer( this );
00941         connect( d->m_mouseScrollTimer, SIGNAL(timeout()), this, SLOT(slotMouseScrollTimer()) );
00942 
00943         if ( !d->m_mouseScrollIndicator ) {
00944             QPixmap pixmap, icon;
00945             pixmap.resize( 48, 48 );
00946             pixmap.fill( QColor( qRgba( 127, 127, 127, 127 ) ) );
00947 
00948             QPainter p( &pixmap );
00949             icon = KGlobal::iconLoader()->loadIcon( "1uparrow", KIcon::Small );
00950             p.drawPixmap( 16, 0, icon );
00951             icon = KGlobal::iconLoader()->loadIcon( "1leftarrow", KIcon::Small );
00952             p.drawPixmap( 0, 16, icon );
00953             icon = KGlobal::iconLoader()->loadIcon( "1downarrow", KIcon::Small );
00954             p.drawPixmap( 16, 32,icon  );
00955             icon = KGlobal::iconLoader()->loadIcon( "1rightarrow", KIcon::Small );
00956             p.drawPixmap( 32, 16, icon );
00957             p.drawEllipse( 23, 23, 2, 2 );
00958 
00959             d->m_mouseScrollIndicator = new QWidget( this, 0 );
00960             d->m_mouseScrollIndicator->setFixedSize( 48, 48 );
00961             d->m_mouseScrollIndicator->setPaletteBackgroundPixmap( pixmap );
00962         }
00963         d->m_mouseScrollIndicator->move( point.x()-24, point.y()-24 );
00964 
00965         bool hasHorBar = visibleWidth() < contentsWidth();
00966         bool hasVerBar = visibleHeight() < contentsHeight();
00967 
00968         KConfig *config = KGlobal::config();
00969         KConfigGroupSaver saver( config, "HTML Settings" );
00970         if ( config->readBoolEntry( "ShowMouseScrollIndicator", true ) ) {
00971             d->m_mouseScrollIndicator->show();
00972             d->m_mouseScrollIndicator->unsetCursor();
00973 
00974             QBitmap mask = d->m_mouseScrollIndicator->paletteBackgroundPixmap()->createHeuristicMask( true );
00975 
00976         if ( hasHorBar && !hasVerBar ) {
00977                 QBitmap bm( 16, 16, true );
00978                 bitBlt( &mask, 16,  0, &bm, 0, 0, -1, -1 );
00979                 bitBlt( &mask, 16, 32, &bm, 0, 0, -1, -1 );
00980                 d->m_mouseScrollIndicator->setCursor( KCursor::SizeHorCursor );
00981             }
00982             else if ( !hasHorBar && hasVerBar ) {
00983                 QBitmap bm( 16, 16, true );
00984                 bitBlt( &mask,  0, 16, &bm, 0, 0, -1, -1 );
00985                 bitBlt( &mask, 32, 16, &bm, 0, 0, -1, -1 );
00986                 d->m_mouseScrollIndicator->setCursor( KCursor::SizeVerCursor );
00987             }
00988             else
00989                 d->m_mouseScrollIndicator->setCursor( KCursor::SizeAllCursor );
00990 
00991             d->m_mouseScrollIndicator->setMask( mask );
00992         }
00993         else {
00994             if ( hasHorBar && !hasVerBar )
00995                 viewport()->setCursor( KCursor::SizeHorCursor );
00996             else if ( !hasHorBar && hasVerBar )
00997                 viewport()->setCursor( KCursor::SizeVerCursor );
00998             else
00999                 viewport()->setCursor( KCursor::SizeAllCursor );
01000         }
01001 
01002         return;
01003     }
01004     else if ( d->m_mouseScrollTimer ) {
01005         delete d->m_mouseScrollTimer;
01006         d->m_mouseScrollTimer = 0;
01007 
01008         if ( d->m_mouseScrollIndicator )
01009             d->m_mouseScrollIndicator->hide();
01010     }
01011 
01012     d->clickCount = 1;
01013     d->clickX = xm;
01014     d->clickY = ym;
01015 
01016     bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEDOWN_EVENT,mev.innerNode.handle(),mev.innerNonSharedNode.handle(),true,
01017                                            d->clickCount,_mouse,true,DOM::NodeImpl::MousePress);
01018 
01019     khtml::RenderObject* r = mev.innerNode.handle() ? mev.innerNode.handle()->renderer() : 0;
01020     if (r && r->isWidget())
01021     _mouse->ignore();
01022 
01023     if (!swallowEvent) {
01024     emit m_part->nodeActivated(mev.innerNode);
01025 
01026     khtml::MousePressEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode );
01027         QApplication::sendEvent( m_part, &event );
01028         // we might be deleted after this
01029     }
01030 }
01031 
01032 void KHTMLView::viewportMouseDoubleClickEvent( QMouseEvent *_mouse )
01033 {
01034     if(!m_part->xmlDocImpl()) return;
01035 
01036     int xm, ym;
01037     viewportToContents(_mouse->x(), _mouse->y(), xm, ym);
01038 
01039     kdDebug( 6000 ) << "mouseDblClickEvent: x=" << xm << ", y=" << ym << endl;
01040 
01041     d->isDoubleClick = true;
01042 
01043     DOM::NodeImpl::MouseEvent mev( _mouse->stateAfter(), DOM::NodeImpl::MouseDblClick );
01044     m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev );
01045 
01046     // We do the same thing as viewportMousePressEvent() here, since the DOM does not treat
01047     // single and double-click events as separate (only the detail, i.e. number of clicks differs)
01048     if (d->clickCount > 0 &&
01049         QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() <= QApplication::startDragDistance())
01050     d->clickCount++;
01051     else { // shouldn't happen, if Qt has the same criterias for double clicks.
01052     d->clickCount = 1;
01053     d->clickX = xm;
01054     d->clickY = ym;
01055     }
01056     bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEDOWN_EVENT,mev.innerNode.handle(),mev.innerNonSharedNode.handle(),true,
01057                                            d->clickCount,_mouse,true,DOM::NodeImpl::MouseDblClick);
01058 
01059     khtml::RenderObject* r = mev.innerNode.handle() ? mev.innerNode.handle()->renderer() : 0;
01060     if (r && r->isWidget())
01061     _mouse->ignore();
01062 
01063     if (!swallowEvent) {
01064     khtml::MouseDoubleClickEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode, d->clickCount );
01065     QApplication::sendEvent( m_part, &event );
01066     }
01067 
01068     d->possibleTripleClick=true;
01069     QTimer::singleShot(QApplication::doubleClickInterval(),this,SLOT(tripleClickTimeout()));
01070 }
01071 
01072 void KHTMLView::tripleClickTimeout()
01073 {
01074     d->possibleTripleClick = false;
01075     d->clickCount = 0;
01076 }
01077 
01078 static inline void forwardPeripheralEvent(khtml::RenderWidget* r, QMouseEvent* me, int x, int y)
01079 {
01080     int absx = 0;
01081     int absy = 0;
01082     r->absolutePosition(absx, absy);
01083     QPoint p(x-absx, y-absy);
01084     QMouseEvent fw(me->type(), p, me->button(), me->state());
01085     QWidget* w = r->widget();
01086     QScrollView* sc = ::qt_cast<QScrollView*>(w);
01087     if (sc && !::qt_cast<QListBox*>(w))
01088         static_cast<khtml::RenderWidget::ScrollViewEventPropagator*>(sc)->sendEvent(&fw);
01089     else if(w)
01090         static_cast<khtml::RenderWidget::EventPropagator*>(w)->sendEvent(&fw);
01091 }
01092 
01093 void KHTMLView::viewportMouseMoveEvent( QMouseEvent * _mouse )
01094 {
01095     if ( d->m_mouseScrollTimer ) {
01096         QPoint point = mapFromGlobal( _mouse->globalPos() );
01097 
01098         int deltaX = point.x() - d->m_mouseScrollIndicator->x() - 24;
01099         int deltaY = point.y() - d->m_mouseScrollIndicator->y() - 24;
01100 
01101         (deltaX > 0) ? d->m_mouseScroll_byX = 1 : d->m_mouseScroll_byX = -1;
01102         (deltaY > 0) ? d->m_mouseScroll_byY = 1 : d->m_mouseScroll_byY = -1;
01103 
01104         double adX = QABS(deltaX)/30.0;
01105         double adY = QABS(deltaY)/30.0;
01106 
01107         d->m_mouseScroll_byX = kMax(kMin(d->m_mouseScroll_byX * int(adX*adX), SHRT_MAX), SHRT_MIN);
01108         d->m_mouseScroll_byY = kMax(kMin(d->m_mouseScroll_byY * int(adY*adY), SHRT_MAX), SHRT_MIN);
01109 
01110         if (d->m_mouseScroll_byX == 0 && d->m_mouseScroll_byY == 0) {
01111             d->m_mouseScrollTimer->stop();
01112         }
01113         else if (!d->m_mouseScrollTimer->isActive()) {
01114             d->m_mouseScrollTimer->changeInterval( 20 );
01115         }
01116     }
01117 
01118     if(!m_part->xmlDocImpl()) return;
01119 
01120     int xm, ym;
01121     viewportToContents(_mouse->x(), _mouse->y(), xm, ym);
01122 
01123     DOM::NodeImpl::MouseEvent mev( _mouse->