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