khtml Library API Documentation

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 Dirk Mueller <mueller@kde.org>
00007  *
00008  * This library is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU Library General Public
00010  * License as published by the Free Software Foundation; either
00011  * version 2 of the License, or (at your option) any later version.
00012  *
00013  * This library is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016  * Library General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU Library General Public License
00019  * along with this library; see the file COPYING.LIB.  If not, write to
00020  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00021  * Boston, MA 02111-1307, USA.
00022  */
00023 #include "khtmlview.moc"
00024 
00025 #include "khtmlview.h"
00026 
00027 #include "khtml_part.h"
00028 #include "khtml_events.h"
00029 
00030 #include "html/html_documentimpl.h"
00031 #include "html/html_inlineimpl.h"
00032 #include "rendering/render_root.h"
00033 #include "rendering/render_frames.h"
00034 #include "rendering/render_replaced.h"
00035 #include "xml/dom2_eventsimpl.h"
00036 #include "css/cssstyleselector.h"
00037 #include "misc/htmlhashes.h"
00038 #include "misc/helper.h"
00039 #include "khtml_settings.h"
00040 #include "khtml_printsettings.h"
00041 
00042 #include <kcursor.h>
00043 #include <ksimpleconfig.h>
00044 #include <kstringhandler.h>
00045 #include <kstandarddirs.h>
00046 #include <kprinter.h>
00047 #include <klocale.h>
00048 
00049 #include <qtooltip.h>
00050 #include <qpainter.h>
00051 #include <qpaintdevicemetrics.h>
00052 #include <qstylesheet.h>
00053 #include <qobjectlist.h>
00054 #include <kapplication.h>
00055 
00056 #include <kimageio.h>
00057 #include <assert.h>
00058 #include <kdebug.h>
00059 #include <kurldrag.h>
00060 #include <qobjectlist.h>
00061 #include <qtimer.h>
00062 #include <kdialogbase.h>
00063 
00064 #define PAINT_BUFFER_HEIGHT 128
00065 
00066 using namespace DOM;
00067 using namespace khtml;
00068 class KHTMLToolTip;
00069 
00070 #ifndef QT_NO_TOOLTIP
00071 
00072 class KHTMLToolTip : public QToolTip
00073 {
00074 public:
00075     KHTMLToolTip(KHTMLView *view,  KHTMLViewPrivate* vp) : QToolTip(view->viewport())
00076     {
00077         m_view = view;
00078         m_viewprivate = vp;
00079     };
00080 
00081 protected:
00082     virtual void maybeTip(const QPoint &);
00083 
00084 private:
00085     KHTMLView *m_view;
00086     KHTMLViewPrivate* m_viewprivate;
00087 };
00088 
00089 #endif
00090 
00091 class KHTMLViewPrivate {
00092     friend class KHTMLToolTip;
00093 public:
00094     KHTMLViewPrivate()
00095     {
00096         underMouse = 0;
00097         reset();
00098         tp=0;
00099         paintBuffer=0;
00100         vertPaintBuffer=0;
00101         formCompletions=0;
00102         prevScrollbarVisible = true;
00103         timerId = 0;
00104         repaintTimerId = 0;
00105         scrollTimerId = 0;
00106         complete = false;
00107         tooltip = 0;
00108         possibleTripleClick = false;
00109     }
00110     ~KHTMLViewPrivate()
00111     {
00112         delete formCompletions;
00113         delete tp; tp = 0;
00114         delete paintBuffer; paintBuffer =0;
00115         delete vertPaintBuffer;
00116         if (underMouse)
00117             underMouse->deref();
00118         delete tooltip;
00119     }
00120     void reset()
00121     {
00122         if (underMouse)
00123             underMouse->deref();
00124         underMouse = 0;
00125         linkPressed = false;
00126         useSlowRepaints = false;
00127         originalNode = 0;
00128         borderTouched = false;
00129 #ifndef KHTML_NO_SCROLLBARS
00130         vmode = QScrollView::Auto;
00131         hmode = QScrollView::Auto;
00132 #else
00133         vmode = QScrollView::AlwaysOff;
00134         hmode = QScrollView::AlwaysOff;
00135 #endif
00136         scrollBarMoved = false;
00137         ignoreWheelEvents = false;
00138         borderX = 30;
00139         borderY = 30;
00140         clickX = -1;
00141         clickY = -1;
00142         prevMouseX = -1;
00143         prevMouseY = -1;
00144         clickCount = 0;
00145         isDoubleClick = false;
00146         scrollingSelf = false;
00147         timerId = 0;
00148         repaintTimerId = 0;
00149         scrollTimerId = 0;
00150         complete = false;
00151         firstRelayout = true;
00152         dirtyLayout = false;
00153         layoutSchedulingEnabled = true;
00154         updateRect = QRect();
00155         m_dialogsAllowed = true;
00156     }
00157     void newScrollTimer(QWidget *view, int tid)
00158     {
00159         kdDebug() << "newScrollTimer timer" << tid << endl;
00160         view->killTimer(scrollTimerId);
00161         scrollTimerId = tid;
00162     }
00163     enum ScrollDirection { ScrollLeft, ScrollRight, ScrollUp, ScrollDown };
00164 
00165     void adjustScroller(QWidget *view, ScrollDirection direction, ScrollDirection oppositedir)
00166     {
00167         static const struct { int msec, pixels; } timings [] = {
00168             {100,1}, {50,1}, {30,1}, {20,1}, {20,2}, {20,4}, {20,6}, {0,0}
00169         };
00170         if (!scrollTimerId ||
00171             (scrollDirection != direction &&
00172              scrollDirection != oppositedir)) {
00173             scrollTiming = 2;
00174             scrollBy = timings[scrollTiming].pixels;
00175             scrollDirection = direction;
00176             newScrollTimer(view, view->startTimer(timings[scrollTiming].msec));
00177         } else if (scrollDirection == direction &&
00178                    timings[scrollTiming+1].msec) {
00179             scrollBy = timings[++scrollTiming].pixels;
00180             newScrollTimer(view, view->startTimer(timings[scrollTiming].msec));
00181         } else if (scrollDirection == oppositedir) {
00182             if (scrollTiming) {
00183                 scrollBy = timings[--scrollTiming].pixels;
00184                 newScrollTimer(view, view->startTimer(timings[scrollTiming].msec));
00185             } else
00186                 newScrollTimer(view, 0);
00187         }
00188     }
00189 
00190     QPainter *tp;
00191     QPixmap  *paintBuffer;
00192     QPixmap  *vertPaintBuffer;
00193     NodeImpl *underMouse;
00194 
00195     // the node that was selected when enter was pressed
00196     NodeImpl *originalNode;
00197 
00198     bool borderTouched:1;
00199     bool borderStart:1;
00200     bool scrollBarMoved:1;
00201 
00202     QScrollView::ScrollBarMode vmode;
00203     QScrollView::ScrollBarMode hmode;
00204     bool prevScrollbarVisible;
00205     bool linkPressed;
00206     bool useSlowRepaints;
00207     bool ignoreWheelEvents;
00208 
00209     int borderX, borderY;
00210     KSimpleConfig *formCompletions;
00211 
00212     int clickX, clickY, clickCount;
00213     bool isDoubleClick;
00214 
00215     int prevMouseX, prevMouseY;
00216     bool scrollingSelf;
00217     int timerId;
00218 
00219     int repaintTimerId;
00220     int scrollTimerId;
00221     int scrollTiming;
00222     int scrollBy;
00223     ScrollDirection scrollDirection;
00224     bool complete;
00225     bool firstRelayout;
00226     bool layoutSchedulingEnabled;
00227     bool possibleTripleClick;
00228     bool dirtyLayout;
00229     bool m_dialogsAllowed;
00230     QRect updateRect;
00231     KHTMLToolTip *tooltip;
00232     QPtrDict<QWidget> visibleWidgets;
00233 };
00234 
00235 #ifndef QT_NO_TOOLTIP
00236 
00237 void KHTMLToolTip::maybeTip(const QPoint& p)
00238 {
00239     DOM::NodeImpl *node = m_viewprivate->underMouse;
00240     QRect region;
00241     while ( node ) {
00242         if ( node->isElementNode() ) {
00243             QString s = static_cast<DOM::ElementImpl*>( node )->getAttribute( ATTR_TITLE ).string();
00244             region |= QRect( m_view->contentsToViewport( node->getRect().topLeft() ), node->getRect().size() );
00245             if ( !s.isEmpty() ) {
00246                 tip( region, QStyleSheet::convertFromPlainText( s, QStyleSheetItem::WhiteSpaceNormal ) );
00247                 break;
00248             }
00249         }
00250         node = node->parentNode();
00251     }
00252 }
00253 #endif
00254 
00255 KHTMLView::KHTMLView( KHTMLPart *part, QWidget *parent, const char *name)
00256     : QScrollView( parent, name, WResizeNoErase | WRepaintNoErase | WPaintUnclipped )
00257 {
00258     m_medium = "screen";
00259 
00260     m_part = part;
00261     d = new KHTMLViewPrivate;
00262     QScrollView::setVScrollBarMode(d->vmode);
00263     QScrollView::setHScrollBarMode(d->hmode);
00264     connect(kapp, SIGNAL(kdisplayPaletteChanged()), this, SLOT(slotPaletteChanged()));
00265     connect(this, SIGNAL(contentsMoving(int, int)), this, SLOT(slotScrollBarMoved()));
00266 
00267     // initialize QScrollview
00268     enableClipper(true);
00269     setResizePolicy(Manual);
00270     viewport()->setMouseTracking(true);
00271     viewport()->setBackgroundMode(NoBackground);
00272 
00273     KImageIO::registerFormats();
00274 
00275 #ifndef QT_NO_TOOLTIP
00276     d->tooltip = new KHTMLToolTip( this, d );
00277 #endif
00278 
00279     init();
00280 
00281     viewport()->show();
00282 }
00283 
00284 KHTMLView::~KHTMLView()
00285 {
00286     closeChildDialogs();
00287     if (m_part)
00288     {
00289         //WABA: Is this Ok? Do I need to deref it as well?
00290         //Does this need to be done somewhere else?
00291         DOM::DocumentImpl *doc = m_part->xmlDocImpl();
00292         if (doc)
00293             doc->detach();
00294     }
00295     delete d; d = 0;
00296 }
00297 
00298 void KHTMLView::init()
00299 {
00300     if(!d->paintBuffer) d->paintBuffer = new QPixmap(PAINT_BUFFER_HEIGHT, PAINT_BUFFER_HEIGHT);
00301     if(!d->vertPaintBuffer)
00302         d->vertPaintBuffer = new QPixmap(10, PAINT_BUFFER_HEIGHT);
00303     if(!d->tp) d->tp = new QPainter();
00304 
00305     setFocusPolicy(QWidget::StrongFocus);
00306     viewport()->setFocusPolicy( QWidget::WheelFocus );
00307     viewport()->setFocusProxy(this);
00308 
00309     _marginWidth = -1; // undefined
00310     _marginHeight = -1;
00311     _width = 0;
00312     _height = 0;
00313 
00314     setAcceptDrops(true);
00315     resizeContents(visibleWidth(), visibleHeight());
00316 }
00317 
00318 void KHTMLView::clear()
00319 {
00320     // work around QScrollview's unbelievable bugginess
00321     setStaticBackground(true);
00322 
00323     d->reset();
00324     killTimers();
00325     emit cleared();
00326 
00327     QScrollView::setHScrollBarMode(d->hmode);
00328     if (d->vmode==Auto)
00329         QScrollView::setVScrollBarMode(d->prevScrollbarVisible?AlwaysOn:Auto);
00330     else
00331         QScrollView::setVScrollBarMode(d->vmode);
00332 }
00333 
00334 void KHTMLView::hideEvent(QHideEvent* e)
00335 {
00336     QScrollView::hideEvent(e);
00337 }
00338 
00339 void KHTMLView::showEvent(QShowEvent* e)
00340 {
00341     QScrollView::showEvent(e);
00342 }
00343 
00344 void KHTMLView::resizeEvent (QResizeEvent* e)
00345 {
00346     QScrollView::resizeEvent(e);
00347 
00348     if ( m_part && m_part->xmlDocImpl() )
00349         m_part->xmlDocImpl()->dispatchWindowEvent( EventImpl::RESIZE_EVENT, false, false );
00350 }
00351 
00352 void KHTMLView::viewportResizeEvent (QResizeEvent* e)
00353 {
00354     QScrollView::viewportResizeEvent(e);
00355 
00356     //int w = visibleWidth();
00357     //int h = visibleHeight();
00358 
00359     if (d->layoutSchedulingEnabled)
00360         layout();
00361 
00362     KApplication::sendPostedEvents(viewport(), QEvent::Paint);
00363 }
00364 
00365 // this is to get rid of a compiler virtual overload mismatch warning. do not remove
00366 void KHTMLView::drawContents( QPainter*)
00367 {
00368 }
00369 
00370 void KHTMLView::drawContents( QPainter *p, int ex, int ey, int ew, int eh )
00371 {
00372     //kdDebug( 6000 ) << "drawContents x=" << ex << ",y=" << ey << ",w=" << ew << ",h=" << eh << endl;
00373     if(!m_part->xmlDocImpl() || !m_part->xmlDocImpl()->renderer()) {
00374         p->fillRect(ex, ey, ew, eh, palette().active().brush(QColorGroup::Base));
00375         return;
00376     }
00377     if (eh > PAINT_BUFFER_HEIGHT && ew <= 10) {
00378         if ( d->vertPaintBuffer->height() < visibleHeight() )
00379             d->vertPaintBuffer->resize(10, visibleHeight());
00380         d->tp->begin(d->vertPaintBuffer);
00381         d->tp->translate(-ex, -ey);
00382         d->tp->fillRect(ex, ey, ew, eh, palette().active().brush(QColorGroup::Base));
00383         m_part->xmlDocImpl()->renderer()->paint(d->tp, ex, ey, ew, eh, 0, 0);
00384         d->tp->end();
00385         p->drawPixmap(ex, ey, *d->vertPaintBuffer, 0, 0, ew, eh);
00386     }
00387     else {
00388         if ( d->paintBuffer->width() < visibleWidth() )
00389             d->paintBuffer->resize(visibleWidth(),PAINT_BUFFER_HEIGHT);
00390 
00391         int py=0;
00392         while (py < eh) {
00393             int ph = eh-py < PAINT_BUFFER_HEIGHT ? eh-py : PAINT_BUFFER_HEIGHT;
00394             d->tp->begin(d->paintBuffer);
00395             d->tp->translate(-ex, -ey-py);
00396             d->tp->fillRect(ex, ey+py, ew, ph, palette().active().brush(QColorGroup::Base));
00397             m_part->xmlDocImpl()->renderer()->paint(d->tp, ex, ey+py, ew, ph, 0, 0);
00398 #ifdef BOX_DEBUG
00399             if (m_part->xmlDocImpl()->focusNode())
00400             {
00401                 d->tp->setBrush(Qt::NoBrush);
00402                 d->tp->drawRect(m_part->xmlDocImpl()->focusNode()->getRect());
00403             }
00404 #endif
00405             d->tp->end();
00406 
00407             p->drawPixmap(ex, ey+py, *d->paintBuffer, 0, 0, ew, ph);
00408             py += PAINT_BUFFER_HEIGHT;
00409         }
00410     }
00411 
00412     khtml::DrawContentsEvent event( p, ex, ey, ew, eh );
00413     QApplication::sendEvent( m_part, &event );
00414 
00415 }
00416 
00417 void KHTMLView::setMarginWidth(int w)
00418 {
00419     // make it update the rendering area when set
00420     _marginWidth = w;
00421 }
00422 
00423 void KHTMLView::setMarginHeight(int h)
00424 {
00425     // make it update the rendering area when set
00426     _marginHeight = h;
00427 }
00428 
00429 void KHTMLView::layout()
00430 {
00431     if( m_part->xmlDocImpl() ) {
00432         DOM::DocumentImpl *document = m_part->xmlDocImpl();
00433 
00434         khtml::RenderRoot* root = static_cast<khtml::RenderRoot *>(document->renderer());
00435         if ( !root ) return;
00436 
00437          if (document->isHTMLDocument()) {
00438              NodeImpl *body = static_cast<HTMLDocumentImpl*>(document)->body();
00439              if(body && body->renderer() && body->id() == ID_FRAMESET) {
00440                  QScrollView::setVScrollBarMode(AlwaysOff);
00441                  QScrollView::setHScrollBarMode(AlwaysOff);
00442                  body->renderer()->setLayouted(false);
00443 //                  if (d->tooltip) {
00444 //                      delete d->tooltip;
00445 //                      d->tooltip = 0;
00446 //                  }
00447              }
00448              else if (!d->tooltip)
00449                  d->tooltip = new KHTMLToolTip( this, d );
00450          }
00451 
00452         _height = visibleHeight();
00453         _width = visibleWidth();
00454         //QTime qt;
00455         //qt.start();
00456         root->setMinMaxKnown(false);
00457         root->setLayouted(false);
00458         root->layout();
00459         //kdDebug( 6000 ) << "TIME: layout() dt=" << qt.elapsed() << endl;
00460     }
00461     else
00462        _width = visibleWidth();
00463 }
00464 
00465 void KHTMLView::closeChildDialogs()
00466 {
00467     QObjectList *dlgs = queryList("QDialog");
00468     for (QObject *dlg = dlgs->first(); dlg; dlg = dlgs->next())
00469     {
00470         KDialogBase* dlgbase = dynamic_cast<KDialogBase *>( dlg );
00471         if ( dlgbase ) {
00472             kdDebug(6000) << "closeChildDialogs: closing dialog " << dlgbase << endl;
00473             // close() ends up calling QButton::animateClick, which isn't immediate
00474             // we need something the exits the event loop immediately (#49068)
00475             dlgbase->cancel();
00476         }
00477         else
00478         {
00479             kdWarning() << "closeChildDialogs: not a KDialogBase! Don't use QDialogs in KDE! " << static_cast<QWidget*>(dlg) << endl;
00480             static_cast<QWidget*>(dlg)->hide();
00481         }
00482     }
00483     delete dlgs;
00484     d->m_dialogsAllowed = false;
00485 }
00486 
00487 bool KHTMLView::dialogsAllowed() {
00488     bool allowed = d->m_dialogsAllowed;
00489     KHTMLPart* p = m_part->parentPart();
00490     if (p && p->view())
00491         allowed &= p->view()->dialogsAllowed();
00492     return allowed;
00493 }
00494 
00495 void KHTMLView::closeEvent( QCloseEvent* ev )
00496 {
00497     closeChildDialogs();
00498     QScrollView::closeEvent( ev );
00499 }
00500 
00501 //
00502 // Event Handling
00503 //
00505 
00506 void KHTMLView::viewportMousePressEvent( QMouseEvent *_mouse )
00507 {
00508     if(!m_part->xmlDocImpl()) return;
00509     if (d->possibleTripleClick)
00510     {
00511         viewportMouseDoubleClickEvent( _mouse ); // it handles triple clicks too
00512         return;
00513     }
00514 
00515     int xm, ym;
00516     viewportToContents(_mouse->x(), _mouse->y(), xm, ym);
00517 
00518     //kdDebug( 6000 ) << "\nmousePressEvent: x=" << xm << ", y=" << ym << endl;
00519 
00520     d->isDoubleClick = false;
00521 
00522     DOM::NodeImpl::MouseEvent mev( _mouse->stateAfter(), DOM::NodeImpl::MousePress );
00523     m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev );
00524 
00525     // Qt bug: sometimes Qt sends us events that should be sent
00526     // to the widget instead
00527     if (  mev.innerNode.handle() && mev.innerNode.handle()->renderer() &&
00528          mev.innerNode.handle()->renderer()->isWidget() )
00529         return;
00530 
00531     if (d->clickCount > 0 &&
00532         QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() <= QApplication::startDragDistance())
00533         d->clickCount++;
00534     else {
00535         d->clickCount = 1;
00536         d->clickX = xm;
00537         d->clickY = ym;
00538     }
00539 
00540     bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEDOWN_EVENT,mev.innerNode.handle(),true,
00541                                            d->clickCount,_mouse,true,DOM::NodeImpl::MousePress);
00542     if (mev.innerNode.handle())
00543         mev.innerNode.handle()->setPressed();
00544 
00545     if (!swallowEvent) {
00546         khtml::MousePressEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode );
00547         QApplication::sendEvent( m_part, &event );
00548 
00549         emit m_part->nodeActivated(mev.innerNode);
00550     }
00551 }
00552 
00553 void KHTMLView::viewportMouseDoubleClickEvent( QMouseEvent *_mouse )
00554 {
00555     if(!m_part->xmlDocImpl()) return;
00556 
00557     int xm, ym;
00558     viewportToContents(_mouse->x(), _mouse->y(), xm, ym);
00559 
00560     kdDebug( 6000 ) << "mouseDblClickEvent: x=" << xm << ", y=" << ym << endl;
00561 
00562     d->isDoubleClick = true;
00563 
00564     DOM::NodeImpl::MouseEvent mev( _mouse->stateAfter(), DOM::NodeImpl::MouseDblClick );
00565     m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev );
00566 
00567     // Qt bug: sometimes Qt sends us events that should be sent
00568     // to the widget instead
00569     if (  mev.innerNode.handle() && mev.innerNode.handle()->renderer() &&
00570          mev.innerNode.handle()->renderer()->isWidget() )
00571         return;
00572 
00573     // We do the same thing as viewportMousePressEvent() here, since the DOM does not treat
00574     // single and double-click events as separate (only the detail, i.e. number of clicks differs)
00575     if (d->clickCount > 0 && d->clickX == xm && d->clickY == ym) // ### support mouse threshold
00576         d->clickCount++;
00577     else {
00578         d->clickCount = 1;
00579         d->clickX = xm;
00580         d->clickY = ym;
00581     }
00582     kdDebug() << "KHTMLView::viewportMouseDoubleClickEvent clickCount=" << d->clickCount << endl;
00583     bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEDOWN_EVENT,mev.innerNode.handle(),true,
00584                                            d->clickCount,_mouse,true,DOM::NodeImpl::MouseDblClick);
00585 
00586     if (mev.innerNode.handle())
00587         mev.innerNode.handle()->setPressed();
00588 
00589     if (!swallowEvent) {
00590         khtml::MouseDoubleClickEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode, d->clickCount );
00591         QApplication::sendEvent( m_part, &event );
00592     }
00593 
00594     d->possibleTripleClick=true;
00595     QTimer::singleShot(QApplication::doubleClickInterval(),this,SLOT(tripleClickTimeout()));
00596 }
00597 
00598 void KHTMLView::tripleClickTimeout()
00599 {
00600     d->possibleTripleClick = false;
00601     d->clickCount = 0;
00602 }
00603 
00604 void KHTMLView::viewportMouseMoveEvent( QMouseEvent * _mouse )
00605 {
00606 
00607     if(!m_part->xmlDocImpl()) return;
00608 
00609     int xm, ym;
00610     viewportToContents(_mouse->x(), _mouse->y(), xm, ym);
00611 
00612     DOM::NodeImpl::MouseEvent mev( _mouse->stateAfter(), DOM::NodeImpl::MouseMove );
00613     m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev );
00614 
00615     // Qt bug: sometimes Qt sends us events that should be sent
00616     // to the widget instead
00617     if (  mev.innerNode.handle() && mev.innerNode.handle()->renderer() &&
00618          mev.innerNode.handle()->renderer()->isWidget() )
00619         return;
00620 
00621     bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEMOVE_EVENT,mev.innerNode.handle(),false,
00622                                            0,_mouse,true,DOM::NodeImpl::MouseMove);
00623 
00624     if (d->clickCount > 0 &&
00625         QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() > QApplication::startDragDistance()) {
00626         d->clickCount = 0;  // moving the mouse outside the threshold invalidates the click
00627     }
00628 
00629     // execute the scheduled script. This is to make sure the mouseover events come after the mouseout events
00630     m_part->executeScheduledScript();
00631 
00632     khtml::RenderObject* r = mev.innerNode.handle() ? mev.innerNode.handle()->renderer() : 0;
00633     khtml::RenderStyle* style = (r && r->style()) ? r->style() : 0;
00634     QCursor c;
00635     switch ( style ? style->cursor() : CURSOR_AUTO) {
00636     case CURSOR_AUTO:
00637         if ( mev.url.length() && m_part->settings()->changeCursor() )
00638             c = m_part->urlCursor();
00639 
00640         if (r && r->isFrameSet() && !static_cast<RenderFrameSet*>(r)->noResize())
00641             c = QCursor(static_cast<RenderFrameSet*>(r)->cursorShape());
00642 
00643         break;
00644     case CURSOR_CROSS:
00645         c = KCursor::crossCursor();
00646         break;
00647     case CURSOR_POINTER:
00648         c = m_part->urlCursor();
00649         break;
00650     case CURSOR_PROGRESS:
00651         c = KCursor::workingCursor();
00652         break;
00653     case CURSOR_MOVE:
00654         c = KCursor::sizeAllCursor();
00655         break;
00656     case CURSOR_E_RESIZE:
00657     case CURSOR_W_RESIZE:
00658         c = KCursor::sizeHorCursor();
00659         break;
00660     case CURSOR_N_RESIZE:
00661     case CURSOR_S_RESIZE:
00662         c = KCursor::sizeVerCursor();
00663         break;
00664     case CURSOR_NE_RESIZE:
00665     case CURSOR_SW_RESIZE:
00666         c = KCursor::sizeBDiagCursor();
00667         break;
00668     case CURSOR_NW_RESIZE:
00669     case CURSOR_SE_RESIZE:
00670         c = KCursor::sizeFDiagCursor();
00671         break;
00672     case CURSOR_TEXT:
00673         c = KCursor::ibeamCursor();
00674         break;
00675     case CURSOR_WAIT:
00676         c = KCursor::waitCursor();
00677         break;
00678     case CURSOR_HELP:
00679         c = KCursor::whatsThisCursor();
00680         break;
00681     case CURSOR_DEFAULT:
00682         break;
00683     }
00684 
00685     if ( viewport()->cursor().handle() != c.handle() ) {
00686         if( c.handle() == KCursor::arrowCursor().handle()) {
00687             for (KHTMLPart* p = m_part; p; p = p->parentPart())
00688                 p->view()->viewport()->unsetCursor();
00689         }
00690         else
00691             viewport()->setCursor( c );
00692     }
00693 
00694     d->prevMouseX = xm;
00695     d->prevMouseY = ym;
00696 
00697     if (!swallowEvent) {
00698         khtml::MouseMoveEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode );
00699         QApplication::sendEvent( m_part, &event );
00700     }
00701 }
00702 
00703 void KHTMLView::viewportMouseReleaseEvent( QMouseEvent * _mouse )
00704 {
00705     if ( !m_part->xmlDocImpl() ) return;
00706 
00707     int xm, ym;
00708     viewportToContents(_mouse->x(), _mouse->y(), xm, ym);
00709 
00710     //kdDebug( 6000 ) << "\nmouseReleaseEvent: x=" << xm << ", y=" << ym << endl;
00711 
00712     DOM::NodeImpl::MouseEvent mev( _mouse->stateAfter(), DOM::NodeImpl::MouseRelease );
00713     m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev );
00714 
00715     // Qt bug: sometimes Qt sends us events that should be sent
00716     // to the widget instead
00717     if (  mev.innerNode.handle() && mev.innerNode.handle()->renderer() &&
00718          mev.innerNode.handle()->renderer()->isWidget() )
00719         return;
00720 
00721     bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEUP_EVENT,mev.innerNode.handle(),true,
00722                                            d->clickCount,_mouse,false,DOM::NodeImpl::MouseRelease);
00723 
00724     if (d->clickCount > 0 &&
00725         QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() <= QApplication::startDragDistance())
00726         dispatchMouseEvent(EventImpl::CLICK_EVENT,mev.innerNode.handle(),true,
00727                            d->clickCount,_mouse,true,DOM::NodeImpl::MouseRelease);
00728 
00729     if (mev.innerNode.handle())
00730         mev.innerNode.handle()->setPressed(false);
00731 
00732     if (!swallowEvent) {
00733         khtml::MouseReleaseEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode );
00734         QApplication::sendEvent( m_part, &event );
00735     }
00736 }
00737 
00738 void KHTMLView::keyPressEvent( QKeyEvent *_ke )
00739 {
00740 
00741     if (m_part->xmlDocImpl()) {
00742         if (m_part->xmlDocImpl()->focusNode())
00743             if (m_part->xmlDocImpl()->focusNode()->dispatchKeyEvent(_ke))
00744             {
00745                 _ke->accept();
00746                 return;
00747             }
00748         if (!_ke->text().isNull() && m_part->xmlDocImpl()->getHTMLEventListener(EventImpl::KHTML_KEYDOWN_EVENT))
00749             if (m_part->xmlDocImpl()->documentElement()->dispatchKeyEvent(_ke))
00750             {
00751                 _ke->accept();
00752                 return;
00753             }
00754     }
00755     int offs = (clipper()->height() < 30) ? clipper()->height() : 30;
00756     if (_ke->state() & Qt::ShiftButton)
00757       switch(_ke->key())
00758         {
00759         case Key_Space:
00760             if ( d->vmode == QScrollView::AlwaysOff )
00761                 _ke->accept();
00762             else
00763                 scrollBy( 0, -clipper()->height() - offs );
00764             break;
00765 
00766         case Key_Down:
00767         case Key_J:
00768             d->adjustScroller(this, KHTMLViewPrivate::ScrollDown, KHTMLViewPrivate::ScrollUp);
00769             break;
00770 
00771         case Key_Up:
00772         case Key_K:
00773             d->adjustScroller(this, KHTMLViewPrivate::ScrollUp, KHTMLViewPrivate::ScrollDown);
00774             break;
00775 
00776         case Key_Left:
00777         case Key_H:
00778             d->adjustScroller(this, KHTMLViewPrivate::ScrollLeft, KHTMLViewPrivate::ScrollRight);
00779             break;
00780 
00781         case Key_Right:
00782         case Key_L:
00783             d->adjustScroller(this, KHTMLViewPrivate::ScrollRight, KHTMLViewPrivate::ScrollLeft);
00784             break;
00785         }
00786     else
00787         switch ( _ke->key() )
00788         {
00789         case Key_Down:
00790         case Key_J:
00791             if ( d->vmode == QScrollView::AlwaysOff )
00792                 _ke->accept();
00793             else {
00794                 if (d->scrollTimerId)
00795                     d->newScrollTimer(this, 0);
00796                 else
00797                     scrollBy( 0, 10 );
00798             }
00799             break;
00800 
00801         case Key_Space:
00802         case Key_Next:
00803             if ( d->vmode == QScrollView::AlwaysOff )
00804                 _ke->accept();
00805             else
00806                 scrollBy( 0, clipper()->height() - offs );
00807             break;
00808 
00809         case Key_Up:
00810         case Key_K:
00811             if ( d->vmode == QScrollView::AlwaysOff )
00812                 _ke->accept();
00813             else {
00814                 if (d->scrollTimerId)
00815                     d->newScrollTimer(this, 0);
00816                 else
00817                     scrollBy( 0, -10 );
00818             }
00819             break;
00820 
00821         case Key_Prior:
00822             if ( d->vmode == QScrollView::AlwaysOff )
00823                 _ke->accept();
00824             else
00825                 scrollBy( 0, -clipper()->height() + offs );
00826             break;
00827         case Key_Right:
00828         case Key_L:
00829             if ( d->hmode == QScrollView::AlwaysOff )
00830                 _ke->accept();
00831             else {
00832                 if (d->scrollTimerId)
00833                     d->newScrollTimer(this, 0);
00834                 else
00835                     scrollBy( 10, 0 );
00836             }
00837             break;
00838         case Key_Left:
00839         case Key_H:
00840             if ( d->hmode == QScrollView::AlwaysOff )
00841                 _ke->accept();
00842             else {
00843                 if (d->scrollTimerId)
00844                     d->newScrollTimer(this, 0);
00845                 else
00846                     scrollBy( -10, 0 );
00847             }
00848             break;
00849         case Key_Enter:
00850         case Key_Return:
00851             // ### FIXME:
00852             // move this code to HTMLAnchorElementImpl::setPressed(false),
00853             // or even better to HTMLAnchorElementImpl::event()
00854             if (m_part->xmlDocImpl()) {
00855                 NodeImpl *n = m_part->xmlDocImpl()->focusNode();
00856                 if (n)
00857                     n->setActive();
00858                 d->originalNode = n;
00859             }
00860             break;
00861         case Key_Home:
00862             if ( d->vmode == QScrollView::AlwaysOff )
00863                 _ke->accept();
00864             else
00865                 setContentsPos( 0, 0 );
00866             break;
00867         case Key_End:
00868             if ( d->vmode == QScrollView::AlwaysOff )
00869                 _ke->accept();
00870             else
00871                 setContentsPos( 0, contentsHeight() - visibleHeight() );
00872             break;
00873         case Key_Shift:
00874             // what are you doing here?
00875             _ke->ignore();
00876             return;
00877         default:
00878             if (d->scrollTimerId)
00879                 d->newScrollTimer(this, 0);
00880             _ke->ignore();
00881             return;
00882         }
00883     _ke->accept();
00884 }
00885 
00886 void KHTMLView::keyReleaseEvent(QKeyEvent *_ke)
00887 {
00888     if(m_part->xmlDocImpl() && m_part->xmlDocImpl()->focusNode()) {
00889         // Qt is damn buggy. we receive release events from our child
00890         // widgets. therefore, do not support keyrelease event on generic
00891         // nodes for now until we found  a workaround for the Qt bugs. (Dirk)
00892 //         if (m_part->xmlDocImpl()->focusNode()->dispatchKeyEvent(_ke)) {
00893 //             _ke->accept();
00894 //             return;
00895 //         }
00896 //        QScrollView::keyReleaseEvent(_ke);
00897         Q_UNUSED(_ke);
00898     }
00899 }
00900 
00901 void KHTMLView::contentsContextMenuEvent ( QContextMenuEvent * /*ce*/ )
00902 {
00903 // ### what kind of c*** is that ?
00904 #if 0
00905     if (!m_part->xmlDocImpl()) return;
00906     int xm = _ce->x();
00907     int ym = _ce->y();
00908 
00909     DOM::NodeImpl::MouseEvent mev( _ce->state(), DOM::NodeImpl::MouseMove ); // ### not a mouse event!
00910     m_part->xmlDocImpl()->prepareMouseEvent( xm, ym, &mev );
00911 
00912     NodeImpl *targetNode = mev.innerNode.handle();
00913     if (targetNode && targetNode->renderer() && targetNode->renderer()->isWidget()) {
00914         int absx = 0;
00915         int absy = 0;
00916         targetNode->renderer()->absolutePosition(absx,absy);
00917         QPoint pos(xm-absx,ym-absy);
00918 
00919         QWidget *w = static_cast<RenderWidget*>(targetNode->renderer())->widget();
00920         QContextMenuEvent cme(_ce->reason(),pos,_ce->globalPos(),_ce->state());
00921         setIgnoreEvents(true);
00922         QApplication::sendEvent(w,&cme);
00923         setIgnoreEvents(false);
00924     }
00925 #endif
00926 }
00927 
00928 bool KHTMLView::focusNextPrevChild( bool next )
00929 {
00930     // Now try to find the next child
00931     if (m_part->xmlDocImpl()) {
00932         focusNextPrevNode(next);
00933         if (m_part->xmlDocImpl()->focusNode() != 0)
00934             return true; // focus node found
00935     }
00936 
00937     // If we get here, there is no next/previous child to go to, so pass up to the next/previous child in our parent
00938     if (m_part->parentPart() && m_part->parentPart()->view()) {
00939         return m_part->parentPart()->view()->focusNextPrevChild(next);
00940     }
00941 
00942     return QWidget::focusNextPrevChild(next);
00943 }
00944 
00945 void KHTMLView::doAutoScroll()
00946 {
00947     QPoint pos = QCursor::pos();
00948     pos = viewport()->mapFromGlobal( pos );
00949 
00950     int xm, ym;
00951     viewportToContents(pos.x(), pos.y(), xm, ym);
00952 
00953     pos = QPoint(pos.x() - viewport()->x(), pos.y() - viewport()->y());
00954     if ( (pos.y() < 0) || (pos.y() > visibleHeight()) ||
00955          (pos.x() < 0) || (pos.x() > visibleWidth()) )
00956     {
00957         ensureVisible( xm, ym, 0, 5 );
00958     }
00959 }
00960 
00961 DOM::NodeImpl *KHTMLView::nodeUnderMouse() const
00962 {
00963     return d->underMouse;
00964 }
00965 
00966 bool KHTMLView::scrollTo(const QRect &bounds)
00967 {
00968     d->scrollingSelf = true; // so scroll events get ignored
00969 
00970     int x, y, xe, ye;
00971     x = bounds.left();
00972     y = bounds.top();
00973     xe = bounds.right();
00974     ye = bounds.bottom();
00975 
00976     //kdDebug(6000)<<"scrolling coords: x="<<x<<" y="<<y<<" width="<<xe-x<<" height="<<ye-y<<endl;
00977 
00978     int deltax;
00979     int deltay;
00980 
00981     int curHeight = visibleHeight();
00982     int curWidth = visibleWidth();
00983 
00984     if (ye-y>curHeight-d->borderY)
00985         ye  = y + curHeight - d->borderY;
00986 
00987     if (xe-x>curWidth-d->borderX)
00988         xe = x + curWidth - d->borderX;
00989 
00990     // is xpos of target left of the view's border?
00991     if (x < contentsX() + d->borderX )
00992             deltax = x - contentsX() - d->borderX;
00993     // is xpos of target right of the view's right border?
00994     else if (xe + d->borderX > contentsX() + curWidth)
00995             deltax = xe + d->borderX - ( contentsX() + curWidth );
00996     else
00997         deltax = 0;
00998 
00999     // is ypos of target above upper border?
01000     if (y < contentsY() + d->borderY)
01001             deltay = y - contentsY() - d->borderY;
01002     // is ypos of target below lower border?
01003     else if (ye + d->borderY > contentsY() + curHeight)
01004             deltay = ye + d->borderY - ( contentsY() + curHeight );
01005     else
01006         deltay = 0;
01007 
01008     int maxx = curWidth-d->borderX;
01009     int maxy = curHeight-d->borderY;
01010 
01011     int scrollX,scrollY;
01012 
01013     scrollX = deltax > 0 ? (deltax > maxx ? maxx : deltax) : deltax == 0 ? 0 : (deltax>-maxx ? deltax : -maxx);
01014     scrollY = deltay > 0 ? (deltay > maxy ? maxy : deltay) : deltay == 0 ? 0 : (deltay>-maxy ? deltay : -maxy);
01015 
01016     if (contentsX() + scrollX < 0)
01017         scrollX = -contentsX();
01018     else if (contentsWidth() - visibleWidth() - contentsX() < scrollX)
01019         scrollX = contentsWidth() - visibleWidth() - contentsX();
01020 
01021     if (contentsY() + scrollY < 0)
01022         scrollY = -contentsY();
01023     else if (contentsHeight() - visibleHeight() - contentsY() < scrollY)
01024         scrollY = contentsHeight() - visibleHeight() - contentsY();
01025 
01026     scrollBy(scrollX, scrollY);
01027 
01028 
01029 
01030     // generate abs(scroll.)
01031     if (scrollX<0)
01032         scrollX=-scrollX;
01033     if (scrollY<0)
01034         scrollY=-scrollY;
01035 
01036     d->scrollingSelf = false;
01037 
01038     if ( (scrollX!=maxx) && (scrollY!=maxy) )
01039         return true;
01040     else return false;
01041 
01042 }
01043 
01044 void KHTMLView::focusNextPrevNode(bool next)
01045 {
01046     // Sets the focus node of the document to be the node after (or if next is false, before) the current focus node.
01047     // Only nodes that are selectable (i.e. for which isSelectable() returns true) are taken into account, and the order
01048     // used is that specified in the HTML spec (see DocumentImpl::nextFocusNode() and DocumentImpl::previousFocusNode()
01049     // for details).
01050 
01051     DocumentImpl *doc = m_part->xmlDocImpl();
01052     NodeImpl *oldFocusNode = doc->focusNode();
01053     NodeImpl *newFocusNode;
01054 
01055     // Find the next/previous node from the current one
01056     if (next)
01057         newFocusNode = doc->nextFocusNode(oldFocusNode);
01058     else
01059         newFocusNode = doc->previousFocusNode(oldFocusNode);
01060 
01061     // If there was previously no focus node and the user has scrolled the document, then instead of picking the first
01062     // focusable node in the document, use the first one that lies within the visible area (if possible).
01063     if (!oldFocusNode && newFocusNode && d->scrollBarMoved) {
01064 
01065       kdDebug(6000) << " searching for visible link" << endl;
01066 
01067         bool visible = false;
01068         NodeImpl *toFocus = newFocusNode;
01069         while (!visible && toFocus) {
01070             QRect focusNodeRect = toFocus->getRect();
01071             if ((focusNodeRect.left() > contentsX()) && (focusNodeRect.right() < contentsX() + visibleWidth()) &&
01072                 (focusNodeRect.top() > contentsY()) && (focusNodeRect.bottom() < contentsY() + visibleHeight())) {
01073                 // toFocus is visible in the contents area
01074                 visible = true;
01075             }
01076             else {
01077                 // toFocus is _not_ visible in the contents area, pick the next node
01078                 if (next)
01079                     toFocus = doc->nextFocusNode(toFocus);
01080                 else
01081                     toFocus = doc->previousFocusNode(toFocus);
01082             }
01083         }
01084 
01085         if (toFocus)
01086             newFocusNode = toFocus;
01087     }
01088 
01089     d->scrollBarMoved = false;
01090 
01091     if (!newFocusNode)
01092       {
01093         // No new focus node, scroll to bottom or top depending on next
01094         if (next)
01095             scrollTo(QRect(contentsX()+visibleWidth()/2,contentsHeight(),0,0));
01096         else
01097             scrollTo(QRect(contentsX()+visibleWidth()/2,0,0,0));
01098     }
01099     else
01100     // Scroll the view as necessary to ensure that the new focus node is visible
01101     {
01102       if (oldFocusNode)
01103         {
01104           if (!scrollTo(newFocusNode->getRect()))
01105             return;
01106         }
01107       else
01108         {
01109           ensureVisible(contentsX(), next?0:contentsHeight());
01110           //return;
01111         }
01112     }
01113     // Set focus node on the document
01114     m_part->xmlDocImpl()->setFocusNode(newFocusNode);
01115     emit m_part->nodeActivated(Node(newFocusNode));
01116 
01117 #if 0
01118     if (newFocusNode) {
01119 
01120         // this does not belong here. it should run a query on the tree (Dirk)
01121         // I'll fix this very particular part of the code soon when I cleaned
01122         // up the positioning code
01123         // If the newly focussed node is a link, notify the part
01124 
01125         HTMLAnchorElementImpl *anchor = 0;
01126         if ((newFocusNode->id() == ID_A || newFocusNode->id() == ID_AREA))
01127             anchor = static_cast<HTMLAnchorElementImpl *>(newFocusNode);
01128 
01129         if (anchor && !anchor->areaHref().isNull())
01130             m_part->overURL(anchor->areaHref().string(), 0);
01131         else
01132             m_part->overURL(QString(), 0);
01133     }
01134 #endif
01135 }
01136 
01137 void KHTMLView::setMediaType( const QString &medium )
01138 {
01139     m_medium = medium;
01140 }
01141 
01142 QString KHTMLView::mediaType() const
01143 {
01144     return m_medium;
01145 }
01146 
01147 void KHTMLView::setWidgetVisible(RenderWidget* w, bool vis)
01148 {
01149     if (vis) {
01150         assert(w->widget());
01151         d->visibleWidgets.replace(w, w->widget());
01152     }
01153     else
01154         d->visibleWidgets.remove(w);
01155 }
01156 
01157 void KHTMLView::print()
01158 {
01159     if(!m_part->xmlDocImpl()) return;
01160     khtml::RenderRoot *root = static_cast<khtml::RenderRoot *>(m_part->xmlDocImpl()->renderer());
01161     if(!root) return;
01162 
01163     // this only works on Unix - we assume 72dpi
01164     KPrinter *printer = new KPrinter(QPrinter::PrinterResolution);
01165     printer->addDialogPage(new KHTMLPrintSettings());
01166     if(printer->setup(this)) {
01167         viewport()->setCursor( waitCursor ); // only viewport(), no QApplication::, otherwise we get the busy cursor in kdeprint's dialogs
01168         // set up KPrinter
01169         printer->setFullPage(false);
01170         printer->setCreator("KDE 3.0 HTML Library");
01171         QString docname = m_part->xmlDocImpl()->URL();
01172         if ( !docname.isEmpty() )
01173             printer->setDocName(KStringHandler::csqueeze(docname, 80));
01174 
01175         QPainter *p = new QPainter;
01176         p->begin( printer );
01177         khtml::setPrintPainter( p );
01178 
01179         m_part->xmlDocImpl()->setPaintDevice( printer );
01180         QString oldMediaType = mediaType();
01181         setMediaType( "print" );
01182         // We ignore margin settings for html and body when printing
01183         // and use the default margins from the print-system
01184         // (In Qt 3.0.x the default margins are hardcoded in Qt)
01185         m_part->xmlDocImpl()->setPrintStyleSheet( printer->option("app-khtml-printfriendly") == "true" ?
01186                                                   "* { background-image: none !important;"
01187                                                   "    background-color: white !important;"
01188                                                   "    color: black !important; }"
01189                                                   "body { margin: 0px !important; }"
01190                                                   "html { margin: 0px !important; }" :
01191                                                   "body { margin: 0px !important; }"
01192                                                   "html { margin: 0px !important; }"
01193                                                   );
01194 
01195         QPaintDeviceMetrics metrics( printer );
01196 
01197         // this is a simple approximation... we layout the document
01198         // according to the width of the page, then just cut
01199         // pages without caring about the content. We should do better
01200         // in the future, but for the moment this is better than no
01201         // printing support
01202         kdDebug(6000) << "printing: physical page width = " << metrics.width()
01203                       << " height = " << metrics.height() << endl;
01204         root->setPrintingMode(true);
01205         root->setWidth(metrics.width());
01206 
01207         m_part->xmlDocImpl()->styleSelector()->computeFontSizes(&metrics, 100);
01208         m_part->xmlDocImpl()->updateStyleSelector();
01209         root->setPrintImages( printer->option("app-khtml-printimages") == "true");
01210         root->setLayouted( false );
01211         root->setMinMaxKnown( false );
01212         root->layout();
01213 
01214         bool printHeader = (printer->option("app-khtml-printheader") == "true");
01215 
01216         int headerHeight = 0;
01217         QFont headerFont("helvetica", 8);
01218 
01219         QString headerLeft = KGlobal::locale()->formatDate(QDate::currentDate(),true);
01220         QString headerMid = docname;
01221         QString headerRight;
01222 
01223         if (printHeader)
01224         {
01225            p->setFont(headerFont);
01226            headerHeight = (p->fontMetrics().lineSpacing() * 3) / 2;
01227         }
01228 
01229         // ok. now print the pages.
01230         kdDebug(6000) << "printing: html page width = " << root->docWidth()
01231                       << " height = " << root->docHeight() << endl;
01232         kdDebug(6000) << "printing: margins left = " << printer->margins().width()
01233                       << " top = " << printer->margins().height() << endl;
01234         kdDebug(6000) << "printing: paper width = " << metrics.width()
01235                       << " height = " << metrics.height() << endl;
01236         // if the width is too large to fit on the paper we just scale
01237         // the whole thing.
01238         int pageHeight = metrics.height();
01239         int pageWidth = metrics.width();
01240         p->setClipRect(0,0, pageWidth, pageHeight);
01241 
01242         pageHeight -= headerHeight;
01243 
01244         bool scalePage = false;
01245         double scale = 0.0;
01246 #ifndef QT_NO_TRANSFORMATIONS
01247         if(root->docWidth() > metrics.width()) {
01248             scalePage = true;
01249             scale = ((double) metrics.width())/((double) root->docWidth());
01250             pageHeight = (int) (pageHeight/scale);
01251             pageWidth = (int) (pageWidth/scale);
01252             headerHeight = (int) (headerHeight/scale);
01253         }
01254 #endif
01255         kdDebug(6000) << "printing: scaled html width = " << pageWidth
01256                       << " height = " << pageHeight << endl;
01257 
01258         // Squeeze header to make it it on the page.
01259         if (printHeader)
01260         {
01261             int available_width = metrics.width() - 10 -
01262                 2 * QMAX(p->boundingRect(0, 0, metrics.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerLeft).width(),
01263                          p->boundingRect(0, 0, metrics.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerRight).width());
01264             if (available_width < 150)
01265                available_width = 150;
01266             int mid_width;
01267             int squeeze = 120;
01268             do {
01269                 headerMid = KStringHandler::csqueeze(docname, squeeze);
01270                 mid_width = p->boundingRect(0, 0, metrics.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerMid).width();
01271                 squeeze -= 10;
01272             } while (mid_width > available_width);
01273         }
01274 
01275         int top = 0;
01276         int page = 1;
01277         while(top < root->docHeight()) {
01278             if(top > 0) printer->newPage();
01279             if (printHeader)
01280             {
01281                 int dy = p->fontMetrics().lineSpacing();
01282                 p->setPen(Qt::black);
01283                 p->setFont(headerFont);
01284 
01285                 headerRight = QString("#%1").arg(page);
01286 
01287                 p->drawText(0, 0, metrics.width(), dy, Qt::AlignLeft, headerLeft);
01288                 p->drawText(0, 0, metrics.width(), dy, Qt::AlignHCenter, headerMid);
01289                 p->drawText(0, 0, metrics.width(), dy, Qt::AlignRight, headerRight);
01290             }
01291 
01292 #ifndef QT_NO_TRANSFORMATIONS
01293             if (scalePage)
01294                 p->scale(scale, scale);
01295 #endif
01296             p->translate(0, headerHeight-top);
01297 
01298             root->setTruncatedAt(top+pageHeight);
01299 
01300             root->paint(p, 0, top, pageWidth, pageHeight, 0, 0);
01301             if (top + pageHeight >= root->docHeight())
01302                 break; // Stop if we have printed everything
01303 
01304             top = root->truncatedAt();
01305             p->resetXForm();
01306             page++;
01307         }
01308 
01309         p->end();
01310         delete p;
01311 
01312         // and now reset the layout to the usual one...
01313         root->setPrintingMode(false);
01314         khtml::setPrintPainter( 0 );
01315         setMediaType( oldMediaType );
01316         m_part->xmlDocImpl()->setPaintDevice( this );
01317         m_part->xmlDocImpl()->styleSelector()->computeFontSizes(m_part->xmlDocImpl()->paintDeviceMetrics(), m_part->zoomFactor());
01318         m_part->xmlDocImpl()->updateStyleSelector();
01319         viewport()->unsetCursor();
01320     }
01321     delete printer;
01322 }
01323 
01324 void KHTMLView::slotPaletteChanged()
01325 {
01326     if(!m_part->xmlDocImpl()) return;
01327     DOM::DocumentImpl *document = m_part->xmlDocImpl();
01328     if (!document->isHTMLDocument()) return;
01329     khtml::RenderRoot *root = static_cast<khtml::RenderRoot *>(document->renderer());
01330     if(!root) return;
01331     root->style()->resetPalette();
01332     NodeImpl *body = static_cast<HTMLDocumentImpl*>(document)->body();
01333     if(!body) return;
01334     body->setChanged(true);
01335     body->recalcStyle( NodeImpl::Force );
01336 }
01337 
01338 void KHTMLView::paint(QPainter *p, const QRect &rc, int yOff, bool *more)
01339 {
01340     if(!m_part->xmlDocImpl()) return;
01341     khtml::RenderRoot *root = static_cast<khtml::RenderRoot *>(m_part->xmlDocImpl()->renderer());
01342     if(!root) return;
01343 
01344     m_part->xmlDocImpl()->setPaintDevice(p->device());
01345     root->setPrintingMode(true);
01346     root->setWidth(rc.width());
01347 
01348     p->save();
01349     p->setClipRect(rc);
01350     p->translate(rc.left(), rc.top());
01351     double scale = ((double) rc.width()/(double) root->docWidth());
01352     int height = (int) ((double) rc.height() / scale);
01353 #ifndef QT_NO_TRANSFORMATIONS
01354     p->scale(scale, scale);
01355 #endif
01356 
01357     root->paint(p, 0, yOff, root->docWidth(), height, 0, 0);
01358     if (more)
01359         *more = yOff + height < root->docHeight();
01360     p->restore();
01361 
01362     root->setPrintingMode(false);
01363     m_part->xmlDocImpl()->setPaintDevice( this );
01364 }
01365 
01366 
01367 void KHTMLView::useSlowRepaints()
01368 {
01369     kdDebug(0) << "slow repaints requested" << endl;
01370     d->useSlowRepaints = true;
01371     setStaticBackground(true);
01372 }
01373 
01374 
01375 void KHTMLView::setVScrollBarMode ( ScrollBarMode mode )
01376 {
01377 #ifndef KHTML_NO_SCROLLBARS
01378     d->vmode = mode;
01379     QScrollView::setVScrollBarMode(mode);
01380 #else
01381     Q_UNUSED( mode );
01382 #endif
01383 }
01384 
01385 void KHTMLView::setHScrollBarMode ( ScrollBarMode mode )
01386 {
01387 #ifndef KHTML_NO_SCROLLBARS
01388     d->hmode = mode;
01389     QScrollView::setHScrollBarMode(mode);
01390 #else
01391     Q_UNUSED( mode );
01392 #endif
01393 }
01394 
01395 void KHTMLView::restoreScrollBar()
01396 {
01397     int ow = visibleWidth();
01398     QScrollView::setVScrollBarMode(d->vmode);
01399     if (visibleWidth() != ow)
01400         layout();
01401     d->prevScrollbarVisible = verticalScrollBar()->isVisible();
01402 }
01403 
01404 QStringList KHTMLView::formCompletionItems(const QString &name) const
01405 {
01406     if (!m_part->settings()->isFormCompletionEnabled())
01407         return QStringList();
01408     if (!d->formCompletions)
01409         d->formCompletions = new KSimpleConfig(locateLocal("data", "khtml/formcompletions"));
01410     return d->formCompletions->readListEntry(name);
01411 }
01412 
01413 void KHTMLView::clearCompletionHistory(const QString& name)
01414 {
01415     if (!d->formCompletions)
01416     {
01417         d->formCompletions = new KSimpleConfig(locateLocal("data", "khtml/formcompletions"));
01418     }
01419     d->formCompletions->writeEntry(name, "");
01420     d->formCompletions->sync();
01421 }
01422 
01423 void KHTMLView::addFormCompletionItem(const QString &name, const QString &value)
01424 {
01425     if (!m_part->settings()->isFormCompletionEnabled())
01426         return;
01427     // don't store values that are all numbers or just numbers with
01428     // dashes or spaces as those are likely credit card numbers or
01429     // something similar
01430     bool cc_number(true);
01431     for (unsigned int i = 0; i < value.length(); ++i)
01432     {
01433       QChar c(value[i]);
01434       if (!c.isNumber() && c != '-' && !c.isSpace())
01435       {
01436         cc_number = false;
01437         break;
01438       }
01439     }
01440     if (cc_number)
01441       return;
01442     QStringList items = formCompletionItems(name);
01443     if (!items.contains(value))
01444         items.prepend(value);
01445     while ((int)items.count() > m_part->settings()->maxFormCompletionItems())
01446         items.remove(items.fromLast());
01447     d->formCompletions->writeEntry(name, items);
01448 }
01449 
01450 bool KHTMLView::dispatchMouseEvent(int eventId, DOM::NodeImpl *targetNode, bool cancelable,
01451                                    int detail,QMouseEvent *_mouse, bool setUnder,
01452                                    int mouseEventType)
01453 {
01454     if (d->underMouse)
01455         d->underMouse->deref();
01456     d->underMouse = targetNode;
01457     if (d->underMouse)
01458         d->underMouse->ref();
01459 
01460     int exceptioncode = 0;
01461     int mx, my;
01462     viewportToContents(_mouse->x(), _mouse->y(), mx, my);
01463     // clientX and clientY are in viewport coordinates
01464     // At least the JS code wants event.[xy]/event.client[XY] to be in viewport coords.
01465     // [that's not the same as _mouse->[xy](), since we use the clipper]
01466     int clientX = mx - contentsX();
01467     int clientY = my - contentsY();
01468     int screenX = _mouse->globalX();
01469     int screenY = _mouse->globalY();
01470     int button = -1;
01471     switch (_mouse->button()) {
01472         case LeftButton:
01473             button = 0;
01474             break;
01475         case MidButton:
01476             button = 1;
01477             break;
01478         case RightButton:
01479             button = 2;
01480             break;
01481         default:
01482             break;
01483     }
01484     bool ctrlKey = (_mouse->state() & ControlButton);
01485     bool altKey = (_mouse->state() & AltButton);
01486     bool shiftKey = (_mouse->state() & ShiftButton);
01487     bool metaKey = (_mouse->state() & MetaButton);
01488 
01489     // mouseout/mouseover
01490     if (setUnder && (d->prevMouseX != mx || d->prevMouseY != my)) {
01491 
01492         // ### this code sucks. we should save the oldUnder instead of calculating
01493         // it again. calculating is expensive! (Dirk)
01494         NodeImpl *oldUnder = 0;
01495         if (d->prevMouseX >= 0 && d->prevMouseY >= 0) {
01496             NodeImpl::MouseEvent mev( _mouse->stateAfter(), static_cast<NodeImpl::MouseEventType>(mouseEventType));
01497             m_part->xmlDocImpl()->prepareMouseEvent( true, d->prevMouseX, d->prevMouseY, &mev );
01498             oldUnder = mev.innerNode.handle();
01499         }
01500         if (oldUnder != targetNode) {
01501             // send mouseout event to the old node
01502             if (oldUnder){
01503                 oldUnder->ref();
01504                 MouseEventImpl *me = new MouseEventImpl(EventImpl::MOUSEOUT_EVENT,
01505                                                         true,true,m_part->xmlDocImpl()->defaultView(),
01506                                                         0,screenX,screenY,clientX,clientY,
01507                                                         ctrlKey,altKey,shiftKey,metaKey,
01508                                                         button,targetNode);
01509                 me->ref();
01510                 oldUnder->dispatchEvent(me,exceptioncode,true);
01511                 me->deref();
01512             }
01513 
01514             // send mouseover event to the new node
01515             if (targetNode) {
01516                 MouseEventImpl *me = new MouseEventImpl(EventImpl::MOUSEOVER_EVENT,
01517                                                         true,true,m_part->xmlDocImpl()->defaultView(),
01518                                                         0,screenX,screenY,clientX,clientY,
01519                                                         ctrlKey,altKey,shiftKey,metaKey,
01520                                                         button,oldUnder);
01521 
01522                 me->ref();
01523                 targetNode->dispatchEvent(me,exceptioncode,true);
01524                 me->deref();
01525             }
01526 
01527             if (oldUnder)
01528                 oldUnder->deref();
01529         }
01530     }
01531 
01532     bool swallowEvent = false;
01533 
01534     if (targetNode) {
01535         // send the actual event
01536         MouseEventImpl *me = new MouseEventImpl(static_cast<EventImpl::EventId>(eventId),
01537                                                 true,cancelable,m_part->xmlDocImpl()->defaultView(),
01538                                                 detail,screenX,screenY,clientX,clientY,
01539                                                 ctrlKey,altKey,shiftKey,metaKey,
01540                                                 button,0);
01541         me->ref();
01542         targetNode->dispatchEvent(me,exceptioncode,true);
01543         if (me->defaultHandled() || me->defaultPrevented())
01544             swallowEvent = true;
01545         me->deref();
01546 
01547         if( eventId == EventImpl::MOUSEDOWN_EVENT ) {
01548             if (targetNode->isSelectable())
01549                 m_part->xmlDocImpl()->setFocusNode(targetNode);
01550             else
01551                 m_part->xmlDocImpl()->setFocusNode(0);
01552         }
01553     }
01554 
01555     return swallowEvent;
01556 }
01557 
01558 void KHTMLView::setIgnoreWheelEvents( bool e )
01559 {
01560     d->ignoreWheelEvents = e;
01561 }
01562 
01563 #ifndef QT_NO_WHEELEVENT
01564 
01565 void KHTMLView::viewportWheelEvent(QWheelEvent* e)
01566 {
01567     if ( ( e->state() & ShiftButton ) == ShiftButton )
01568     {
01569         emit zoomView( e->delta() );
01570         e->accept();
01571     }
01572     else if ( d->ignoreWheelEvents && !verticalScrollBar()->isVisible()
01573                 && m_part->parentPart() ) {
01574         if ( m_part->parentPart()->view() )
01575             m_part->parentPart()->view()->wheelEvent( e );
01576         e->ignore();
01577     }
01578     else if ( d->vmode == QScrollView::AlwaysOff ) {
01579         e->accept();
01580     }
01581     else {
01582         d->scrollBarMoved = true;
01583         QScrollView::viewportWheelEvent( e );
01584 
01585         QMouseEvent *tempEvent = new QMouseEvent( QEvent::MouseMove, QPoint(-1,-1), QPoint(-1,-1), Qt::NoButton, e->state() );
01586         emit viewportMouseMoveEvent ( tempEvent );
01587         delete tempEvent;
01588     }
01589 
01590 }
01591 #endif
01592 
01593 void KHTMLView::dragEnterEvent( QDragEnterEvent* ev )
01594 {
01595     // Handle drops onto frames (#16820)
01596     // Drops on the main html part is handled by Konqueror (and shouldn't do anything
01597     // in e.g. kmail, so not handled here).
01598     if ( m_part->parentPart() )
01599     {
01600         // Duplicated from KonqView::eventFilter
01601         if ( QUriDrag::canDecode( ev ) )
01602         {
01603             KURL::List lstDragURLs;
01604             bool ok = KURLDrag::decode( ev, lstDragURLs );
01605             QObjectList *children = this->queryList( "QWidget" );
01606 
01607             if ( ok &&
01608                  !lstDragURLs.first().url().contains( "javascript:", false ) && // ### this looks like a hack to me
01609                  ev->source() != this &&
01610                  children &&
01611                  children->findRef( ev->source() ) == -1 )
01612                 ev->acceptAction();
01613 
01614             delete children;
01615         }
01616     }
01617     QScrollView::dragEnterEvent( ev );
01618 }
01619 
01620 void KHTMLView::dropEvent( QDropEvent *ev )
01621 {
01622     // Handle drops onto frames (#16820)
01623     // Drops on the main html part is handled by Konqueror (and shouldn't do anything
01624     // in e.g. kmail, so not handled here).
01625     if ( m_part->parentPart() )
01626     {
01627         KURL::List lstDragURLs;
01628         bool ok = KURLDrag::decode( ev, lstDragURLs );
01629 
01630         KHTMLPart* part = m_part->parentPart();
01631         while ( part && part->parentPart() )
01632             part = part->parentPart();
01633         KParts::BrowserExtension *ext = part->browserExtension();
01634         if ( ok && ext && lstDragURLs.first().isValid() )
01635             emit ext->openURLRequest( lstDragURLs.first() );
01636     }
01637     QScrollView::dropEvent( ev );
01638 }
01639 
01640 void KHTMLView::focusOutEvent( QFocusEvent *e )
01641 {
01642     if(m_part) m_part->stopAutoScroll();
01643     QScrollView::focusOutEvent( e );
01644 }
01645 
01646 void KHTMLView::slotScrollBarMoved()
01647 {
01648     if (!d->scrollingSelf)
01649         d->scrollBarMoved = true;
01650 }
01651 
01652 void KHTMLView::timerEvent ( QTimerEvent *e )
01653 {
01654 //    kdDebug() << "timer event " << e->timerId() << endl;
01655     if (e->timerId() == d->scrollTimerId) {
01656         switch (d->scrollDirection) {
01657             case KHTMLViewPrivate::ScrollDown:
01658                 if (contentsY() + visibleHeight () >= contentsHeight())
01659                     d->newScrollTimer(this, 0);
01660                 else
01661                     scrollBy( 0, d->scrollBy );
01662                 break;
01663             case KHTMLViewPrivate::ScrollUp:
01664                 if (contentsY() <= 0)
01665                     d->newScrollTimer(this, 0);
01666                 else
01667                     scrollBy( 0, -d->scrollBy );
01668                 break;
01669             case KHTMLViewPrivate::ScrollRight:
01670                 if (contentsX() + visibleWidth () >= contentsWidth())
01671                     d->newScrollTimer(this, 0);
01672                 else
01673                     scrollBy( d->scrollBy, 0 );
01674                 break;
01675             case KHTMLViewPrivate::ScrollLeft:
01676                 if (contentsX() <= 0)
01677                     d->newScrollTimer(this, 0);
01678                 else
01679                     scrollBy( -d->scrollBy, 0 );
01680                 break;
01681         }
01682         return;
01683     }
01684     if (e->timerId()==d->timerId)
01685     {
01686         d->firstRelayout = false;
01687         killTimer(d->timerId);
01688 
01689         d->dirtyLayout = true;
01690         d->layoutSchedulingEnabled=false;
01691         layout();
01692         d->layoutSchedulingEnabled=true;
01693 
01694         d->timerId = 0;
01695 
01696 
01697         //scheduleRepaint(contentsX(),contentsY(),visibleWidth(),visibleHeight());
01698         d->updateRect = QRect(contentsX(),contentsY(),visibleWidth(),visibleHeight());
01699     }
01700 
01701     if( m_part->xmlDocImpl() ) {
01702         DOM::DocumentImpl *document = m_part->xmlDocImpl();
01703         khtml::RenderRoot* root = static_cast<khtml::RenderRoot *>(document->renderer());
01704 
01705         if ( !root->layouted() ) {
01706             killTimer(d->repaintTimerId);
01707             d->repaintTimerId = 0;
01708             scheduleRelayout();
01709             return;
01710         }
01711     }
01712 
01713     setStaticBackground(d->useSlowRepaints);
01714 
01715 //        kdDebug() << "scheduled repaint "<< d->repaintTimerId  << endl;
01716     killTimer(d->repaintTimerId);
01717     updateContents( d->updateRect );
01718 
01719     if (d->dirtyLayout && !d->visibleWidgets.isEmpty()) {
01720         d->dirtyLayout = false;
01721 
01722         QRect visibleRect(contentsX(), contentsY(), visibleWidth(), visibleHeight());
01723         QPtrList<RenderWidget> toRemove;
01724         QWidget* w;
01725         for (QPtrDictIterator<QWidget> it(d->visibleWidgets); it.current(); ++it) {
01726             int xp = 0, yp = 0;
01727             w = it.current();
01728             RenderWidget* rw = static_cast<RenderWidget*>( it.currentKey() );
01729             if (!rw->absolutePosition(xp, yp) ||
01730                 !visibleRect.intersects(QRect(xp, yp, w->width(), w->height())))
01731                 toRemove.append(rw);
01732         }
01733         for (RenderWidget* r = toRemove.first(); r; r = toRemove.next())
01734             if ( (w = d->visibleWidgets.take(r) ) )
01735                 addChild(w, 0, -500000);
01736     }
01737 
01738     d->repaintTimerId = 0;
01739 }
01740 
01741 void KHTMLView::scheduleRelayout()
01742 {
01743     if (!d->layoutSchedulingEnabled || d->timerId)
01744         return;
01745 
01746     d->timerId = startTimer( m_part->xmlDocImpl() && m_part->xmlDocImpl()->parsing()
01747                              ? 1000 : 0 );
01748 }
01749 
01750 void KHTMLView::scheduleRepaint(int x, int y, int w, int h)
01751 {
01752 
01753     //kdDebug() << "scheduleRepaint(" << x << "," << y << "," << w << "," << h << ")" << endl;
01754 
01755 
01756     bool parsing = false;
01757     if( m_part->xmlDocImpl() ) {
01758         parsing = m_part->xmlDocImpl()->parsing();
01759     }
01760 
01761 //     kdDebug() << "parsing " << parsing << endl;
01762 //     kdDebug() << "complete " << d->complete << endl;
01763 
01764     int time;
01765 
01766     // if complete...
01767     if (d->complete)
01768         // ...repaint immediatly
01769         time = 0;
01770     else
01771     {
01772         if (parsing)
01773             // not complete and still parsing
01774             time = 300;
01775         else
01776             // not complete, not parsing, extend the timer if it exists
01777             // otherwise, repaint immediatly
01778             time = d->repaintTimerId ? 400 : 0;
01779     }
01780 
01781     if (d->repaintTimerId) {
01782         killTimer(d->repaintTimerId);
01783         d->updateRect = d->updateRect.unite(QRect(x,y,w,h));
01784     } else
01785         d->updateRect = QRect(x,y,w,h);
01786 
01787     d->repaintTimerId = startTimer( time );
01788 
01789 //     kdDebug() << "starting timer " << time << endl;
01790 }
01791 
01792 void KHTMLView::complete()
01793 {
01794 //     kdDebug() << "KHTMLView::complete()" << endl;
01795 
01796     d->complete = true;
01797 
01798     // is there a relayout pending?
01799     if (d->timerId)
01800     {
01801 //         kdDebug() << "requesting relayout now" << endl;
01802         // do it now
01803         killTimer(d->timerId);
01804         d->timerId = startTimer( 0 );
01805     }
01806 
01807     // is there a repaint pending?
01808     if (d->repaintTimerId)
01809     {
01810 //         kdDebug() << "requesting repaint now" << endl;
01811         // do it now
01812         killTimer(d->repaintTimerId);
01813         d->repaintTimerId = startTimer( 1 );
01814     }
01815 }
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.5.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Jan 28 13:34:09 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001