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-2004 Dirk Mueller <mueller@kde.org>
00007  *                     2003 Leo Savernik <l.savernik@aon.at>
00008  *
00009  * This library is free software; you can redistribute it and/or
00010  * modify it under the terms of the GNU Library General Public
00011  * License as published by the Free Software Foundation; either
00012  * version 2 of the License, or (at your option) any later version.
00013  *
00014  * This library is distributed in the hope that it will be useful,
00015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017  * Library General Public License for more details.
00018  *
00019  * You should have received a copy of the GNU Library General Public License
00020  * along with this library; see the file COPYING.LIB.  If not, write to
00021  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00022  * Boston, MA 02111-1307, USA.
00023  */
00024 
00025 
00026 #include "khtmlview.moc"
00027 
00028 #include "khtmlview.h"
00029 
00030 #include "khtml_part.h"
00031 #include "khtml_events.h"
00032 
00033 #include "html/html_documentimpl.h"
00034 #include "html/html_inlineimpl.h"
00035 #include "html/html_formimpl.h"
00036 #include "rendering/render_arena.h"
00037 #include "rendering/render_canvas.h"
00038 #include "rendering/render_frames.h"
00039 #include "rendering/render_replaced.h"
00040 #include "rendering/render_layer.h"
00041 #include "rendering/render_line.h"
00042 #include "rendering/render_table.h"
00043 // removeme
00044 #define protected public
00045 #include "rendering/render_text.h"
00046 #undef protected
00047 #include "xml/dom2_eventsimpl.h"
00048 #include "css/cssstyleselector.h"
00049 #include "misc/htmlhashes.h"
00050 #include "misc/helper.h"
00051 #include "khtml_settings.h"
00052 #include "khtml_printsettings.h"
00053 
00054 #include "khtmlpart_p.h"
00055 
00056 #ifndef KHTML_NO_CARET
00057 #include "khtml_caret_p.h"
00058 #include "xml/dom2_rangeimpl.h"
00059 #endif
00060 
00061 #include <kcursor.h>
00062 #include <ksimpleconfig.h>
00063 #include <kstringhandler.h>
00064 #include <kstandarddirs.h>
00065 #include <kprinter.h>
00066 #include <klocale.h>
00067 
00068 #include <qtooltip.h>
00069 #include <qpainter.h>
00070 #include <qpaintdevicemetrics.h>
00071 #include <qstylesheet.h>
00072 #include <kapplication.h>
00073 
00074 #include <kimageio.h>
00075 #include <kdebug.h>
00076 #include <kurldrag.h>
00077 #include <qobjectlist.h>
00078 #include <qtimer.h>
00079 #include <kdialogbase.h>
00080 #include <qptrdict.h>
00081 
00082 //#define DEBUG_NO_PAINT_BUFFER
00083 
00084 //#define DEBUG_FLICKER
00085 
00086 //#define DEBUG_PIXEL
00087 
00088 #define PAINT_BUFFER_HEIGHT 128
00089 
00090 using namespace DOM;
00091 using namespace khtml;
00092 class KHTMLToolTip;
00093 
00094 #ifndef QT_NO_TOOLTIP
00095 
00096 class KHTMLToolTip : public QToolTip
00097 {
00098 public:
00099     KHTMLToolTip(KHTMLView *view,  KHTMLViewPrivate* vp) : QToolTip(view->viewport())
00100     {
00101         m_view = view;
00102         m_viewprivate = vp;
00103     };
00104 
00105 protected:
00106     virtual void maybeTip(const QPoint &);
00107 
00108 private:
00109     KHTMLView *m_view;
00110     KHTMLViewPrivate* m_viewprivate;
00111 };
00112 
00113 #endif
00114 
00115 class KHTMLViewPrivate {
00116     friend class KHTMLToolTip;
00117 public:
00118     KHTMLViewPrivate()
00119         : underMouse( 0 )
00120     {
00121 #ifndef KHTML_NO_CARET
00122     m_caretViewContext = 0;
00123     m_editorContext = 0;
00124 #endif // KHTML_NO_CARET
00125         postponed_autorepeat = NULL;
00126         reset();
00127         tp=0;
00128         paintBuffer=0;
00129         vertPaintBuffer=0;
00130         formCompletions=0;
00131         prevScrollbarVisible = true;
00132     tooltip = 0;
00133         possibleTripleClick = false;
00134     }
00135     ~KHTMLViewPrivate()
00136     {
00137         delete formCompletions;
00138         delete tp; tp = 0;
00139         delete paintBuffer; paintBuffer =0;
00140         delete vertPaintBuffer;
00141         delete postponed_autorepeat;
00142         if (underMouse)
00143         underMouse->deref();
00144     delete tooltip;
00145 #ifndef KHTML_NO_CARET
00146     delete m_caretViewContext;
00147     delete m_editorContext;
00148 #endif // KHTML_NO_CARET
00149     }
00150     void reset()
00151     {
00152         if (underMouse)
00153         underMouse->deref();
00154     underMouse = 0;
00155         linkPressed = false;
00156         useSlowRepaints = false;
00157         originalNode = 0;
00158     borderTouched = false;
00159 #ifndef KHTML_NO_SCROLLBARS
00160         vmode = QScrollView::Auto;
00161         hmode = QScrollView::Auto;
00162 #else
00163         vmode = QScrollView::AlwaysOff;
00164         hmode = QScrollView::AlwaysOff;
00165 #endif
00166 #ifdef DEBUG_PIXEL
00167         timer.start();
00168         pixelbooth = 0;
00169         repaintbooth = 0;
00170 #endif
00171         scrollBarMoved = false;
00172         ignoreWheelEvents = false;
00173     borderX = 30;
00174     borderY = 30;
00175     clickX = -1;
00176     clickY = -1;
00177         prevMouseX = -1;
00178         prevMouseY = -1;
00179     clickCount = 0;
00180     isDoubleClick = false;
00181     scrollingSelf = false;
00182         delete postponed_autorepeat;
00183         postponed_autorepeat = NULL;
00184     layoutTimerId = 0;
00185         repaintTimerId = 0;
00186         scrollTimerId = 0;
00187         scrollSuspended = false;
00188         complete = false;
00189         firstRelayout = true;
00190         dirtyLayout = false;
00191         layoutSchedulingEnabled = true;
00192         updateRegion = QRegion();
00193         m_dialogsAllowed = true;
00194 #ifndef KHTML_NO_CARET
00195         if (m_caretViewContext) {
00196           m_caretViewContext->caretMoved = false;
00197       m_caretViewContext->keyReleasePending = false;
00198         }/*end if*/
00199 #endif // KHTML_NO_CARET
00200     }
00201     void newScrollTimer(QWidget *view, int tid)
00202     {
00203         //kdDebug(6000) << "newScrollTimer timer " << tid << endl;
00204         view->killTimer(scrollTimerId);
00205         scrollTimerId = tid;
00206         scrollSuspended = false;
00207     }
00208     enum ScrollDirection { ScrollLeft, ScrollRight, ScrollUp, ScrollDown };
00209 
00210     void adjustScroller(QWidget *view, ScrollDirection direction, ScrollDirection oppositedir)
00211     {
00212         static const struct { int msec, pixels; } timings [] = {
00213             {320,1}, {224,1}, {160,1}, {112,1}, {80,1}, {56,1}, {40,1},
00214             {28,1}, {20,1}, {20,2}, {20,3}, {20,4}, {20,6}, {20,8}, {0,0}
00215         };
00216         if (!scrollTimerId ||
00217             (scrollDirection != direction &&
00218              (scrollDirection != oppositedir || scrollSuspended))) {
00219             scrollTiming = 6;
00220             scrollBy = timings[scrollTiming].pixels;
00221             scrollDirection = direction;
00222             newScrollTimer(view, view->startTimer(timings[scrollTiming].msec));
00223         } else if (scrollDirection == direction &&
00224                    timings[scrollTiming+1].msec && !scrollSuspended) {
00225             scrollBy = timings[++scrollTiming].pixels;
00226             newScrollTimer(view, view->startTimer(timings[scrollTiming].msec));
00227         } else if (scrollDirection == oppositedir) {
00228             if (scrollTiming) {
00229                 scrollBy = timings[--scrollTiming].pixels;
00230                 newScrollTimer(view, view->startTimer(timings[scrollTiming].msec));
00231             }
00232         }
00233         scrollSuspended = false;
00234     }
00235 
00236 #ifndef KHTML_NO_CARET
00237 
00240     CaretViewContext *caretViewContext() {
00241       if (!m_caretViewContext) m_caretViewContext = new CaretViewContext();
00242       return m_caretViewContext;
00243     }
00247     EditorContext *editorContext() {
00248       if (!m_editorContext) m_editorContext = new EditorContext();
00249       return m_editorContext;
00250     }
00251 #endif // KHTML_NO_CARET
00252 
00253 #ifdef DEBUG_PIXEL
00254     QTime timer;
00255     unsigned int pixelbooth;
00256     unsigned int repaintbooth;
00257 #endif
00258 
00259     QPainter *tp;
00260     QPixmap  *paintBuffer;
00261     QPixmap  *vertPaintBuffer;
00262     NodeImpl *underMouse;
00263 
00264     // the node that was selected when enter was pressed
00265     NodeImpl *originalNode;
00266 
00267     bool borderTouched:1;
00268     bool borderStart:1;
00269     bool scrollBarMoved:1;
00270 
00271     QScrollView::ScrollBarMode vmode;
00272     QScrollView::ScrollBarMode hmode;
00273     bool prevScrollbarVisible;
00274     bool linkPressed;
00275     bool useSlowRepaints;
00276     bool ignoreWheelEvents;
00277 
00278     int borderX, borderY;
00279     KSimpleConfig *formCompletions;
00280 
00281     int clickX, clickY, clickCount;
00282     bool isDoubleClick;
00283 
00284     int prevMouseX, prevMouseY;
00285     bool scrollingSelf;
00286     int layoutTimerId;
00287     QKeyEvent* postponed_autorepeat;
00288 
00289     int repaintTimerId;
00290     int scrollTimerId;
00291     bool scrollSuspended;
00292     int scrollTiming;
00293     int scrollBy;
00294     ScrollDirection scrollDirection;
00295     bool complete;
00296     bool firstRelayout;
00297     bool layoutSchedulingEnabled;
00298     bool possibleTripleClick;
00299     bool dirtyLayout;
00300     bool m_dialogsAllowed;
00301     QRegion updateRegion;
00302     KHTMLToolTip *tooltip;
00303     QPtrDict<QWidget> visibleWidgets;
00304 #ifndef KHTML_NO_CARET
00305     CaretViewContext *m_caretViewContext;
00306     EditorContext *m_editorContext;
00307 #endif // KHTML_NO_CARET
00308 };
00309 
00310 #ifndef QT_NO_TOOLTIP
00311 
00312 void KHTMLToolTip::maybeTip(const QPoint& /*p*/)
00313 {
00314     DOM::NodeImpl *node = m_viewprivate->underMouse;
00315     QRect region;
00316     while ( node ) {
00317         if ( node->isElementNode() ) {
00318             QString s = static_cast<DOM::ElementImpl*>( node )->getAttribute( ATTR_TITLE ).string();
00319             region |= QRect( m_view->contentsToViewport( node->getRect().topLeft() ), node->getRect().size() );
00320             if ( !s.isEmpty() ) {
00321                 tip( region, QStyleSheet::convertFromPlainText( s, QStyleSheetItem::WhiteSpaceNormal ) );
00322                 break;
00323             }
00324         }
00325         node = node->parentNode();
00326     }
00327 }
00328 #endif
00329 
00330 KHTMLView::KHTMLView( KHTMLPart *part, QWidget *parent, const char *name)
00331     : QScrollView( parent, name, WResizeNoErase | WRepaintNoErase )
00332 {
00333     m_medium = "screen";
00334 
00335     m_part = part;
00336     d = new KHTMLViewPrivate;
00337     QScrollView::setVScrollBarMode(d->vmode);
00338     QScrollView::setHScrollBarMode(d->hmode);
00339     connect(kapp, SIGNAL(kdisplayPaletteChanged()), this, SLOT(slotPaletteChanged()));
00340     connect(this, SIGNAL(contentsMoving(int, int)), this, SLOT(slotScrollBarMoved()));
00341 
00342     // initialize QScrollView
00343     enableClipper(true);
00344     // hack to get unclipped painting on the viewport.
00345     static_cast<KHTMLView *>(static_cast<QWidget *>(viewport()))->setWFlags(WPaintUnclipped);
00346 
00347     setResizePolicy(Manual);
00348     viewport()->setMouseTracking(true);
00349     viewport()->setBackgroundMode(NoBackground);
00350 
00351     KImageIO::registerFormats();
00352 
00353 #ifndef QT_NO_TOOLTIP
00354     d->tooltip = new KHTMLToolTip( this, d );
00355 #endif
00356 
00357     init();
00358 
00359     viewport()->show();
00360 }
00361 
00362 KHTMLView::~KHTMLView()
00363 {
00364     closeChildDialogs();
00365     if (m_part)
00366     {
00367         //WABA: Is this Ok? Do I need to deref it as well?
00368         //Does this need to be done somewhere else?
00369         DOM::DocumentImpl *doc = m_part->xmlDocImpl();
00370         if (doc)
00371             doc->detach();
00372     }
00373     delete d; d = 0;
00374 }
00375 
00376 void KHTMLView::init()
00377 {
00378     if(!d->paintBuffer) d->paintBuffer = new QPixmap(PAINT_BUFFER_HEIGHT, PAINT_BUFFER_HEIGHT);
00379     if(!d->vertPaintBuffer)
00380         d->vertPaintBuffer = new QPixmap(10, PAINT_BUFFER_HEIGHT);
00381     if(!d->tp) d->tp = new QPainter();
00382 
00383     setFocusPolicy(QWidget::StrongFocus);
00384     viewport()->setFocusProxy(this);
00385 
00386     _marginWidth = -1; // undefined
00387     _marginHeight = -1;
00388     _width = 0;
00389     _height = 0;
00390 
00391     installEventFilter(this);
00392 
00393     setAcceptDrops(true);
00394     QSize s = viewportSize(4095, 4095);
00395     resizeContents(s.width(), s.height());
00396 }
00397 
00398 void KHTMLView::clear()
00399 {
00400     // work around QScrollview's unbelievable bugginess
00401     setStaticBackground(true);
00402 #ifndef KHTML_NO_CARET
00403     if (!m_part->isCaretMode() && !m_part->isEditable()) caretOff();
00404 #endif
00405 
00406     d->reset();
00407     killTimers();
00408     emit cleared();
00409 
00410     QScrollView::setHScrollBarMode(d->hmode);
00411     QScrollView::setVScrollBarMode(d->vmode);
00412 }
00413 
00414 void KHTMLView::hideEvent(QHideEvent* e)
00415 {
00416     QScrollView::hideEvent(e);
00417 }
00418 
00419 void KHTMLView::showEvent(QShowEvent* e)
00420 {
00421     QScrollView::showEvent(e);
00422 }
00423 
00424 void KHTMLView::resizeEvent (QResizeEvent* e)
00425 {
00426     QScrollView::resizeEvent(e);
00427 
00428     if ( m_part && m_part->xmlDocImpl() )
00429         m_part->xmlDocImpl()->dispatchWindowEvent( EventImpl::RESIZE_EVENT, false, false );
00430 }
00431 
00432 void KHTMLView::viewportResizeEvent (QResizeEvent* e)
00433 {
00434     QScrollView::viewportResizeEvent(e);
00435 
00436     //int w = visibleWidth();
00437     //int h = visibleHeight();
00438 
00439     if (d->layoutSchedulingEnabled)
00440         layout();
00441 #ifndef KHTML_NO_CARET
00442     else {
00443         hideCaret();
00444         recalcAndStoreCaretPos();
00445     showCaret();
00446     }/*end if*/
00447 #endif
00448 
00449     KApplication::sendPostedEvents(viewport(), QEvent::Paint);
00450 }
00451 
00452 // this is to get rid of a compiler virtual overload mismatch warning. do not remove
00453 void KHTMLView::drawContents( QPainter*)
00454 {
00455 }
00456 
00457 void KHTMLView::drawContents( QPainter *p, int ex, int ey, int ew, int eh )
00458 {
00459 #ifdef DEBUG_PIXEL
00460 
00461     if ( d->timer.elapsed() > 5000 ) {
00462         qDebug( "drawed %d pixels in %d repaints the last %d milliseconds",
00463                 d->pixelbooth, d->repaintbooth,  d->timer.elapsed() );
00464         d->timer.restart();
00465         d->pixelbooth = 0;
00466         d->repaintbooth = 0;
00467     }
00468     d->pixelbooth += ew*eh;
00469     d->repaintbooth++;
00470 #endif
00471 
00472     //kdDebug( 6000 ) << "drawContents this="<< this <<" x=" << ex << ",y=" << ey << ",w=" << ew << ",h=" << eh << endl;
00473     if(!m_part || !m_part->xmlDocImpl() || !m_part->xmlDocImpl()->renderer()) {
00474         p->fillRect(ex, ey, ew, eh, palette().active().brush(QColorGroup::Base));
00475         return;
00476     }
00477 
00478     QPoint pt = contentsToViewport(QPoint(ex, ey));
00479     QRegion cr = QRect(pt.x(), pt.y(), ew, eh);
00480 //     kdDebug(6000) << "clip rect: " << QRect(pt.x(), pt.y(), ew, eh) << endl;
00481     for (QPtrDictIterator<QWidget> it(d->visibleWidgets); it.current(); ++it) {
00482     QWidget *w = it.current();
00483     RenderWidget* rw = static_cast<RenderWidget*>( it.currentKey() );
00484     QScrollView *sv = ::qt_cast<QScrollView *>(w);
00485     if (sv || !rw->isFormElement()) {
00486 //      kdDebug(6000) << "    removing scrollview " << sv;
00487         int x, y;
00488         rw->absolutePosition(x, y);
00489         contentsToViewport(x, y, x, y);
00490         cr -= QRect(x, y, rw->width(), rw->height());
00491     }
00492     }
00493 
00494 #if 0
00495     // this is commonly the case with framesets. we still do
00496     // want to paint them, otherwise the widgets don't get placed.
00497     if (cr.isEmpty())
00498     return;
00499 #endif
00500 
00501 #ifndef DEBUG_NO_PAINT_BUFFER
00502     p->setClipRegion(cr);
00503 
00504     if (eh > PAINT_BUFFER_HEIGHT && ew <= 10) {
00505         if ( d->vertPaintBuffer->height() < visibleHeight() )
00506             d->vertPaintBuffer->resize(10, visibleHeight());
00507         d->tp->begin(d->vertPaintBuffer);
00508         d->tp->translate(-ex, -ey);
00509         d->tp->fillRect(ex, ey, ew, eh, palette().active().brush(QColorGroup::Base));
00510         m_part->xmlDocImpl()->renderer()->layer()->paint(d->tp, QRect(ex, ey, ew, eh));
00511         d->tp->end();
00512     p->drawPixmap(ex, ey, *d->vertPaintBuffer, 0, 0, ew, eh);
00513     }
00514     else {
00515         if ( d->paintBuffer->width() < visibleWidth() )
00516             d->paintBuffer->resize(visibleWidth(),PAINT_BUFFER_HEIGHT);
00517 
00518         int py=0;
00519         while (py < eh) {
00520             int ph = eh-py < PAINT_BUFFER_HEIGHT ? eh-py : PAINT_BUFFER_HEIGHT;
00521             d->tp->begin(d->paintBuffer);
00522             d->tp->translate(-ex, -ey-py);
00523             d->tp->fillRect(ex, ey+py, ew, ph, palette().active().brush(QColorGroup::Base));
00524             m_part->xmlDocImpl()->renderer()->layer()->paint(d->tp, QRect(ex, ey+py, ew, ph));
00525             d->tp->end();
00526 
00527         p->drawPixmap(ex, ey+py, *d->paintBuffer, 0, 0, ew, ph);
00528             py += PAINT_BUFFER_HEIGHT;
00529         }
00530     }
00531 #else // !DEBUG_NO_PAINT_BUFFER
00532 static int cnt=0;
00533     ex = contentsX(); ey = contentsY();
00534     ew = visibleWidth(); eh = visibleHeight();
00535     QRect pr(ex,ey,ew,eh);
00536     kdDebug() << "[" << ++cnt << "]" << " clip region: " << pr << endl;
00537 //  p->setClipRegion(QRect(0,0,ew,eh));
00538 //        p->translate(-ex, -ey);
00539         p->fillRect(ex, ey, ew, eh, palette().active().brush(QColorGroup::Base));
00540         m_part->xmlDocImpl()->renderer()->layer()->paint(p, pr);
00541 #endif // DEBUG_NO_PAINT_BUFFER
00542 
00543 #ifndef KHTML_NO_CARET
00544     if (d->m_caretViewContext && d->m_caretViewContext->visible) {
00545         QRect pos(d->m_caretViewContext->x, d->m_caretViewContext->y,
00546         d->m_caretViewContext->width, d->m_caretViewContext->height);
00547         if (pos.intersects(QRect(ex, ey, ew, eh))) {
00548             p->setRasterOp(XorROP);
00549         p->setPen(white);
00550         if (pos.width() == 1)
00551               p->drawLine(pos.topLeft(), pos.bottomRight());
00552         else {
00553           p->fillRect(pos, white);
00554         }/*end if*/
00555     }/*end if*/
00556     }/*end if*/
00557 #endif // KHTML_NO_CARET
00558 
00559 //    p->setPen(QPen(magenta,0,DashDotDotLine));
00560 //    p->drawRect(dbg_paint_rect);
00561 
00562     khtml::DrawContentsEvent event( p, ex, ey, ew, eh );
00563     QApplication::sendEvent( m_part, &event );
00564 
00565 }
00566 
00567 void KHTMLView::setMarginWidth(int w)
00568 {
00569     // make it update the rendering area when set
00570     _marginWidth = w;
00571 }
00572 
00573 void KHTMLView::setMarginHeight(int h)
00574 {
00575     // make it update the rendering area when set
00576     _marginHeight = h;
00577 }
00578 
00579 void KHTMLView::layout()
00580 {
00581     if( m_part && m_part->xmlDocImpl() ) {
00582         DOM::DocumentImpl *document = m_part->xmlDocImpl();
00583 
00584         khtml::RenderCanvas* root = static_cast<khtml::RenderCanvas *>(document->renderer());
00585         if ( !root ) return;
00586 
00587         d->layoutSchedulingEnabled=false;
00588 
00589         if (document->isHTMLDocument()) {
00590              NodeImpl *body = static_cast<HTMLDocumentImpl*>(document)->body();
00591              if(body && body->renderer() && body->id() == ID_FRAMESET) {
00592                  QScrollView::setVScrollBarMode(AlwaysOff);
00593                  QScrollView::setHScrollBarMode(AlwaysOff);
00594                  body->renderer()->setLayouted(false);
00595 //                  if (d->tooltip) {
00596 //                      delete d->tooltip;
00597 //                      d->tooltip = 0;
00598 //                  }
00599              }
00600              else if (!d->tooltip)
00601                  d->tooltip = new KHTMLToolTip( this, d );
00602         }
00603 
00604         _height = visibleHeight();
00605         _width = visibleWidth();
00606         //QTime qt;
00607         //qt.start();
00608         root->setMinMaxKnown(false);
00609         root->setLayouted(false);
00610         root->layout();
00611 #ifndef KHTML_NO_CARET
00612         hideCaret();
00613         if ((m_part->isCaretMode() || m_part->isEditable())
00614             && !d->complete && d->m_caretViewContext
00615                 && !d->m_caretViewContext->caretMoved) {
00616             initCaret();
00617         } else {
00618         recalcAndStoreCaretPos();
00619         showCaret();
00620         }/*end if*/
00621 #endif
00622     root->repaint();
00623         //kdDebug( 6000 ) << "TIME: layout() dt=" << qt.elapsed() << endl;
00624     }
00625     else
00626        _width = visibleWidth();
00627 
00628     killTimer(d->layoutTimerId);
00629     d->layoutTimerId = 0;
00630     d->layoutSchedulingEnabled=true;
00631 }
00632 
00633 void KHTMLView::closeChildDialogs()
00634 {
00635     QObjectList *dlgs = queryList("QDialog");
00636     for (QObject *dlg = dlgs->first(); dlg; dlg = dlgs->next())
00637     {
00638         KDialogBase* dlgbase = dynamic_cast<KDialogBase *>( dlg );
00639         if ( dlgbase ) {
00640             kdDebug(6000) << "closeChildDialogs: closing dialog " << dlgbase << endl;
00641             // close() ends up calling QButton::animateClick, which isn't immediate
00642             // we need something the exits the event loop immediately (#49068)
00643             dlgbase->cancel();
00644         }
00645         else
00646         {
00647             kdWarning() << "closeChildDialogs: not a KDialogBase! Don't use QDialogs in KDE! " << static_cast<QWidget*>(dlg) << endl;
00648             static_cast<QWidget*>(dlg)->hide();
00649         }
00650     }
00651     delete dlgs;
00652     d->m_dialogsAllowed = false;
00653 }
00654 
00655 bool KHTMLView::dialogsAllowed() {
00656     bool allowed = d->m_dialogsAllowed;
00657     KHTMLPart* p = m_part->parentPart();
00658     if (p && p->view())
00659         allowed &= p->view()->dialogsAllowed();
00660     return allowed;
00661 }
00662 
00663 void KHTMLView::closeEvent( QCloseEvent* ev )
00664 {
00665     closeChildDialogs();
00666     QScrollView::closeEvent( ev );
00667 }
00668 
00669 //
00670 // Event Handling
00671 //
00673 
00674 void KHTMLView::viewportMousePressEvent( QMouseEvent *_mouse )
00675 {
00676     if(!m_part->xmlDocImpl()) return;
00677     if (d->possibleTripleClick)
00678     {
00679         viewportMouseDoubleClickEvent( _mouse ); // it handles triple clicks too
00680         return;
00681     }
00682 
00683     int xm, ym;
00684     viewportToContents(_mouse->x(), _mouse->y(), xm, ym);
00685 
00686     //kdDebug( 6000 ) << "\nmousePressEvent: x=" << xm << ", y=" << ym << endl;
00687 
00688     d->isDoubleClick = false;
00689 
00690     DOM::NodeImpl::MouseEvent mev( _mouse->stateAfter(), DOM::NodeImpl::MousePress );
00691     m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev );
00692 
00693     if (d->clickCount > 0 &&
00694         QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() <= QApplication::startDragDistance())
00695     d->clickCount++;
00696     else {
00697     d->clickCount = 1;
00698     d->clickX = xm;
00699     d->clickY = ym;
00700     }
00701 
00702     bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEDOWN_EVENT,mev.innerNode.handle(),true,
00703                                            d->clickCount,_mouse,true,DOM::NodeImpl::MousePress);
00704 
00705     khtml::RenderObject* r = mev.innerNode.handle() ? mev.innerNode.handle()->renderer() : 0;
00706     if (r && r->isWidget())
00707     _mouse->ignore();
00708 
00709     if (!swallowEvent) {
00710     emit m_part->nodeActivated(mev.innerNode);
00711 
00712     khtml::MousePressEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode );
00713         QApplication::sendEvent( m_part, &event );
00714         // we might be deleted after this
00715     }
00716 }
00717 
00718 void KHTMLView::viewportMouseDoubleClickEvent( QMouseEvent *_mouse )
00719 {
00720     if(!m_part->xmlDocImpl()) return;
00721 
00722     int xm, ym;
00723     viewportToContents(_mouse->x(), _mouse->y(), xm, ym);
00724 
00725     kdDebug( 6000 ) << "mouseDblClickEvent: x=" << xm << ", y=" << ym << endl;
00726 
00727     d->isDoubleClick = true;
00728 
00729     DOM::NodeImpl::MouseEvent mev( _mouse->stateAfter(), DOM::NodeImpl::MouseDblClick );
00730     m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev );
00731 
00732     // We do the same thing as viewportMousePressEvent() here, since the DOM does not treat
00733     // single and double-click events as separate (only the detail, i.e. number of clicks differs)
00734     if (d->clickCount > 0 &&
00735         QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() <= QApplication::startDragDistance())
00736     d->clickCount++;
00737     else { // shouldn't happen, if Qt has the same criterias for double clicks.
00738     d->clickCount = 1;
00739     d->clickX = xm;
00740     d->clickY = ym;
00741     }
00742     bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEDOWN_EVENT,mev.innerNode.handle(),true,
00743                                            d->clickCount,_mouse,true,DOM::NodeImpl::MouseDblClick);
00744 
00745     khtml::RenderObject* r = mev.innerNode.handle() ? mev.innerNode.handle()->renderer() : 0;
00746     if (r && r->isWidget())
00747     _mouse->ignore();
00748 
00749     if (!swallowEvent) {
00750     khtml::MouseDoubleClickEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode, d->clickCount );
00751     QApplication::sendEvent( m_part, &event );
00752     }
00753 
00754     d->possibleTripleClick=true;
00755     QTimer::singleShot(QApplication::doubleClickInterval(),this,SLOT(tripleClickTimeout()));
00756 }
00757 
00758 void KHTMLView::tripleClickTimeout()
00759 {
00760     d->possibleTripleClick = false;
00761     d->clickCount = 0;
00762 }
00763 
00764 static inline void forwardPeripheralEvent(khtml::RenderWidget* r, QMouseEvent* me, int x, int y)
00765 {
00766     int absx = 0;
00767     int absy = 0;
00768     r->absolutePosition(absx, absy);
00769     QPoint p(x-absx, y-absy);
00770     QMouseEvent fw(me->type(), p, me->button(), me->state());
00771     QWidget* w = r->widget();
00772     if(w)
00773         static_cast<khtml::RenderWidget::EventPropagator*>(w)->sendEvent(&fw);
00774 }
00775 
00776 void KHTMLView::viewportMouseMoveEvent( QMouseEvent * _mouse )
00777 {
00778 
00779     if(!m_part->xmlDocImpl()) return;
00780 
00781     int xm, ym;
00782     viewportToContents(_mouse->x(), _mouse->y(), xm, ym);
00783 
00784     DOM::NodeImpl::MouseEvent mev( _mouse->stateAfter(), DOM::NodeImpl::MouseMove );
00785     // Do not modify :hover/:active state while mouse is pressed.
00786     m_part->xmlDocImpl()->prepareMouseEvent( _mouse->state() & Qt::MouseButtonMask /*readonly ?*/, xm, ym, &mev );
00787 
00788 //     kdDebug(6000) << "mouse move: " << _mouse->pos()
00789 //        << " button " << _mouse->button()
00790 //        << " state " << _mouse->state() << endl;
00791 
00792     bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEMOVE_EVENT,mev.innerNode.handle(),false,
00793                                            0,_mouse,true,DOM::NodeImpl::MouseMove);
00794 
00795     if (d->clickCount > 0 &&
00796         QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() > QApplication::startDragDistance()) {
00797     d->clickCount = 0;  // moving the mouse outside the threshold invalidates the click
00798     }
00799 
00800     // execute the scheduled script. This is to make sure the mouseover events come after the mouseout events
00801     m_part->executeScheduledScript();
00802 
00803     DOM::NodeImpl* fn = m_part->xmlDocImpl()->focusNode();
00804     if (fn && fn != mev.innerNode.handle() &&
00805         fn->renderer() && fn->renderer()->isWidget()) {
00806         forwardPeripheralEvent(static_cast<khtml::RenderWidget*>(fn->renderer()), _mouse, xm, ym);
00807     }
00808 
00809     khtml::RenderObject* r = mev.innerNode.handle() ? mev.innerNode.handle()->renderer() : 0;
00810     khtml::RenderStyle* style = (r && r->style()) ? r->style() : 0;
00811     QCursor c;
00812     switch ( style ? style->cursor() : CURSOR_AUTO) {
00813     case CURSOR_AUTO:
00814         if ( r && r->isText() )
00815             c = KCursor::ibeamCursor();
00816 
00817         if ( mev.url.length() && m_part->settings()->changeCursor() )
00818             c = m_part->urlCursor();
00819 
00820         if (r && r->isFrameSet() && !static_cast<RenderFrameSet*>(r)->noResize())
00821             c = QCursor(static_cast<RenderFrameSet*>(r)->cursorShape());
00822 
00823         break;
00824     case CURSOR_CROSS:
00825         c = KCursor::crossCursor();
00826         break;
00827     case CURSOR_POINTER:
00828         c = m_part->urlCursor();
00829         break;
00830     case CURSOR_PROGRESS:
00831         c = KCursor::workingCursor();
00832         break;
00833     case CURSOR_MOVE:
00834         c = KCursor::sizeAllCursor();
00835         break;
00836     case CURSOR_E_RESIZE:
00837     case CURSOR_W_RESIZE:
00838         c = KCursor::sizeHorCursor();
00839         break;
00840     case CURSOR_N_RESIZE:
00841     case CURSOR_S_RESIZE:
00842         c = KCursor::sizeVerCursor();
00843         break;
00844     case CURSOR_NE_RESIZE:
00845     case CURSOR_SW_RESIZE:
00846         c = KCursor::sizeBDiagCursor();
00847         break;
00848     case CURSOR_NW_RESIZE:
00849     case CURSOR_SE_RESIZE:
00850         c = KCursor::sizeFDiagCursor();
00851         break;
00852     case CURSOR_TEXT:
00853         c = KCursor::ibeamCursor();
00854         break;
00855     case CURSOR_WAIT:
00856         c = KCursor::waitCursor();
00857         break;
00858     case CURSOR_HELP:
00859         c = KCursor::whatsThisCursor();
00860         break;
00861     case CURSOR_DEFAULT:
00862         break;
00863     }
00864 
00865     if ( viewport()->cursor().handle() != c.handle() ) {
00866         if( c.handle() == KCursor::arrowCursor().handle()) {
00867             for (KHTMLPart* p = m_part; p; p = p->parentPart())
00868                 p->view()->viewport()->unsetCursor();
00869         }
00870         else {
00871             viewport()->setCursor( c );
00872     }
00873     }
00874     if (r && r->isWidget()) {
00875     _mouse->ignore();
00876     }
00877 
00878 
00879     d->prevMouseX = xm;
00880     d->prevMouseY = ym;
00881 
00882     if (!swallowEvent) {
00883         khtml::MouseMoveEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode );
00884         QApplication::sendEvent( m_part, &event );
00885     }
00886 }
00887 
00888 void KHTMLView::viewportMouseReleaseEvent( QMouseEvent * _mouse )
00889 {
00890     if ( !m_part->xmlDocImpl() ) return;
00891 
00892     int xm, ym;
00893     viewportToContents(_mouse->x(), _mouse->y(), xm, ym);
00894 
00895     DOM::NodeImpl::MouseEvent mev( _mouse->stateAfter(), DOM::NodeImpl::MouseRelease );
00896     m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev );
00897 
00898     bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEUP_EVENT,mev.innerNode.handle(),true,
00899                                            d->clickCount,_mouse,false,DOM::NodeImpl::MouseRelease);
00900 
00901     if (d->clickCount > 0 &&
00902         QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() <= QApplication::startDragDistance()) {
00903     QMouseEvent me(d->isDoubleClick ? QEvent::MouseButtonDblClick : QEvent::MouseButtonRelease,
00904                _mouse->pos(), _mouse->button(), _mouse->state());
00905     dispatchMouseEvent(EventImpl::CLICK_EVENT, mev.innerNode.handle(),true,
00906                            d->clickCount, &me, true, DOM::NodeImpl::MouseRelease);
00907     }
00908 
00909     DOM::NodeImpl* fn = m_part->xmlDocImpl()->focusNode();
00910     if (fn && fn != mev.innerNode.handle() &&
00911         fn->renderer() && fn->renderer()->isWidget()) {
00912         forwardPeripheralEvent(static_cast<khtml::RenderWidget*>(fn->renderer()), _mouse, xm, ym);
00913     }
00914 
00915     khtml::RenderObject* r = mev.innerNode.handle() ? mev.innerNode.handle()->renderer() : 0;
00916     if (r && r->isWidget())
00917     _mouse->ignore();
00918 
00919     if (!swallowEvent) {
00920     khtml::MouseReleaseEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode );
00921     QApplication::sendEvent( m_part, &event );
00922     }
00923 }
00924 
00925 // returns true if event should be swallowed
00926 bool KHTMLView::dispatchKeyEvent( QKeyEvent *_ke )
00927 {
00928     if (!m_part->xmlDocImpl())
00929         return false;
00930     // Pressing and releasing a key should generate keydown, keypress and keyup events
00931     // Holding it down should generated keydown, keypress (repeatedly) and keyup events
00932     // The problem here is that Qt generates two autorepeat events (keyrelease+keypress)
00933     // for autorepeating, while DOM wants only one autorepeat event (keypress), so one
00934     // of the Qt events shouldn't be passed to DOM, but it should be still filtered
00935     // out if DOM would filter the autorepeat event. Additional problem is that Qt keyrelease
00936     // events don't have text() set (Qt bug?), so DOM often would ignore the keypress event
00937     // if it was created using Qt keyrelease, but Qt autorepeat keyrelease comes
00938     // before Qt autorepeat keypress (i.e. problem whether to filter it out or not).
00939     // The solution is to filter out and postpone the Qt autorepeat keyrelease until
00940     // the following Qt keypress event comes. If DOM accepts the DOM keypress event,
00941     // the postponed event will be simply discarded. If not, it will be passed to keyPressEvent()
00942     // again, and here it will be ignored.
00943     //
00944     //  Qt:      Press      | Release(autorepeat) Press(autorepeat) etc. |   Release
00945     //  DOM:   Down + Press |      (nothing)           Press             |     Up
00946 
00947     // It's also possible to get only Releases. E.g. the release of alt-tab,
00948     // or when the keypresses get captured by an accel.
00949 
00950     if( _ke == d->postponed_autorepeat ) // replayed event
00951     {
00952         return false;
00953     }
00954 
00955     if( _ke->type() == QEvent::KeyPress )
00956     {
00957         if( !_ke->isAutoRepeat())
00958         {
00959             bool ret = dispatchKeyEventHelper( _ke, false ); // keydown
00960             if( dispatchKeyEventHelper( _ke, true )) // keypress
00961                 ret = true;
00962             return ret;
00963         }
00964         else // autorepeat
00965         {
00966             bool ret = dispatchKeyEventHelper( _ke, true ); // keypress
00967             if( !ret && d->postponed_autorepeat )
00968                 keyPressEvent( d->postponed_autorepeat );
00969             delete d->postponed_autorepeat;
00970             d->postponed_autorepeat = NULL;
00971             return ret;
00972         }
00973     }
00974     else // QEvent::KeyRelease
00975     {
00976         // Discard postponed "autorepeat key-release" events that didn't see
00977         // a keypress after them (e.g. due to QAccel)
00978         if ( d->postponed_autorepeat ) {
00979             delete d->postponed_autorepeat;
00980             d->postponed_autorepeat = 0;
00981         }
00982 
00983         if( !_ke->isAutoRepeat()) {
00984             return dispatchKeyEventHelper( _ke, false ); // keyup
00985         }
00986         else
00987         {
00988             d->postponed_autorepeat = new QKeyEvent( _ke->type(), _ke->key(), _ke->ascii(), _ke->state(),
00989                 _ke->text(), _ke->isAutoRepeat(), _ke->count());
00990             if( _ke->isAccepted())
00991                 d->postponed_autorepeat->accept();
00992             else
00993                 d->postponed_autorepeat->ignore();
00994             return true;
00995         }
00996     }
00997 }
00998 
00999 // returns true if event should be swallowed
01000 bool KHTMLView::dispatchKeyEventHelper( QKeyEvent *_ke, bool keypress )
01001 {
01002     DOM::NodeImpl* keyNode = m_part->xmlDocImpl()->focusNode();
01003     if (keyNode) {
01004         return keyNode->dispatchKeyEvent(_ke, keypress);
01005     } else { // no focused node, send to document
01006         return m_part->xmlDocImpl()->dispatchKeyEvent(_ke, keypress);
01007     }
01008 }
01009 
01010 void KHTMLView::keyPressEvent( QKeyEvent *_ke )
01011 {
01012 
01013 #ifndef KHTML_NO_CARET
01014     if (m_part->isEditable() || m_part->isCaretMode()
01015         || (m_part->xmlDocImpl() && m_part->xmlDocImpl()->focusNode()
01016         && m_part->xmlDocImpl()->focusNode()->contentEditable())) {
01017       d->caretViewContext()->keyReleasePending = true;
01018       caretKeyPressEvent(_ke);
01019       return;
01020     }
01021 #endif // KHTML_NO_CARET
01022 
01023     // accesskey handling needs to be done before dispatching, otherwise e.g. lineedits
01024     // may eat the event
01025     if( handleAccessKey( _ke )) {
01026         _ke->accept();
01027         return;
01028     }
01029 
01030     if ( dispatchKeyEvent( _ke )) {
01031         // If either keydown or keypress was accepted by a widget, or canceled by JS, stop here.
01032         _ke->accept();
01033         return;
01034     }
01035 
01036     int offs = (clipper()->height() < 30) ? clipper()->height() : 30;
01037     if (_ke->state() & Qt::ShiftButton)
01038       switch(_ke->key())
01039         {
01040         case Key_Space:
01041             if ( d->vmode == QScrollView::AlwaysOff )
01042                 _ke->accept();
01043             else {
01044                 scrollBy( 0, -clipper()->height() - offs );
01045                 if(d->scrollSuspended)
01046                     d->newScrollTimer(this, 0);
01047             }
01048             break;
01049 
01050         case Key_Down:
01051         case Key_J:
01052             d->adjustScroller(this, KHTMLViewPrivate::ScrollDown, KHTMLViewPrivate::ScrollUp);
01053             break;
01054 
01055         case Key_Up:
01056         case Key_K:
01057             d->adjustScroller(this, KHTMLViewPrivate::ScrollUp, KHTMLViewPrivate::ScrollDown);
01058             break;
01059 
01060         case Key_Left:
01061         case Key_H:
01062             d->adjustScroller(this, KHTMLViewPrivate::ScrollLeft, KHTMLViewPrivate::ScrollRight);
01063             break;
01064 
01065         case Key_Right:
01066         case Key_L:
01067             d->adjustScroller(this, KHTMLViewPrivate::ScrollRight, KHTMLViewPrivate::ScrollLeft);
01068             break;
01069         }
01070     else
01071         switch ( _ke->key() )
01072         {
01073         case Key_Down:
01074         case Key_J:
01075             if ( d->vmode == QScrollView::AlwaysOff )
01076                 _ke->accept();
01077             else {
01078                 if (!d->scrollTimerId || d->scrollSuspended)
01079                     scrollBy( 0, 10 );
01080                 if (d->scrollTimerId)
01081                     d->newScrollTimer(this, 0);
01082             }
01083             break;
01084 
01085         case Key_Space:
01086         case Key_Next:
01087             if ( d->vmode == QScrollView::AlwaysOff )
01088                 _ke->accept();
01089             else {
01090                 scrollBy( 0, clipper()->height() - offs );
01091                 if(d->scrollSuspended)
01092                     d->newScrollTimer(this, 0);
01093             }
01094             break;
01095 
01096         case Key_Up:
01097         case Key_K:
01098             if ( d->vmode == QScrollView::AlwaysOff )
01099                 _ke->accept();
01100             else {
01101                 if (!d->scrollTimerId || d->scrollSuspended)
01102                     scrollBy( 0, -10 );
01103                 if (d->scrollTimerId)
01104                     d->newScrollTimer(this, 0);
01105             }
01106             break;
01107 
01108         case Key_Prior:
01109             if ( d->vmode == QScrollView::AlwaysOff )
01110                 _ke->accept();
01111             else {
01112                 scrollBy( 0, -clipper()->height() + offs );
01113                 if(d->scrollSuspended)
01114                     d->newScrollTimer(this, 0);
01115             }
01116             break;
01117         case Key_Right:
01118         case Key_L:
01119             if ( d->hmode == QScrollView::AlwaysOff )
01120                 _ke->accept();
01121             else {
01122                 if (!d->scrollTimerId || d->scrollSuspended)
01123                     scrollBy( 10, 0 );
01124                 if (d->scrollTimerId)
01125                     d->newScrollTimer(this, 0);
01126             }
01127             break;
01128         case Key_Left:
01129         case Key_H:
01130             if ( d->hmode == QScrollView::AlwaysOff )
01131                 _ke->accept();
01132             else {
01133                 if (!d->scrollTimerId || d->scrollSuspended)
01134                     scrollBy( -10, 0 );
01135                 if (d->scrollTimerId)
01136                     d->newScrollTimer(this, 0);
01137             }
01138             break;
01139         case Key_Enter:
01140         case Key_Return:
01141         // ### FIXME:
01142         // or even better to HTMLAnchorElementImpl::event()
01143             if (m_part->xmlDocImpl()) {
01144         NodeImpl *n = m_part->xmlDocImpl()->focusNode();
01145         if (n)
01146             n->setActive();
01147         d->originalNode = n;
01148         }
01149             break;
01150         case Key_Home:
01151             if ( d->vmode == QScrollView::AlwaysOff )
01152                 _ke->accept();
01153             else {
01154                 setContentsPos( 0, 0 );
01155                 if(d->scrollSuspended)
01156                     d->newScrollTimer(this, 0);
01157             }
01158             break;
01159         case Key_End:
01160             if ( d->vmode == QScrollView::AlwaysOff )
01161                 _ke->accept();
01162             else {
01163                 setContentsPos( 0, contentsHeight() - visibleHeight() );
01164                 if(d->scrollSuspended)
01165                     d->newScrollTimer(this, 0);
01166             }
01167             break;
01168         case Key_Shift:
01169             // what are you doing here?
01170         _ke->ignore();
01171             return;
01172         case Key_Control:
01173             if (d->scrollTimerId)
01174                 d->scrollSuspended = !d->scrollSuspended;
01175             break;
01176         default:
01177             if (d->scrollTimerId)
01178                 d->newScrollTimer(this, 0);
01179         _ke->ignore();
01180             return;
01181         }
01182     _ke->accept();
01183 }
01184 
01185 void KHTMLView::keyReleaseEvent(QKeyEvent *_ke)
01186 {
01187     if (d->m_caretViewContext && d->m_caretViewContext->keyReleasePending) {
01188         //caretKeyReleaseEvent(_ke);
01189     d->m_caretViewContext->keyReleasePending = false;
01190     return;
01191     }
01192 
01193     // Send keyup event
01194     if ( dispatchKeyEvent( _ke ) )
01195     {
01196         _ke->accept();
01197         return;
01198     }
01199     QScrollView::keyReleaseEvent(_ke);
01200 }
01201 
01202 void KHTMLView::contentsContextMenuEvent ( QContextMenuEvent * /*ce*/ )
01203 {
01204 // ### what kind of c*** is that ?
01205 #if 0
01206     if (!m_part->xmlDocImpl()) return;
01207     int xm = _ce->x();
01208     int ym = _ce->y();
01209 
01210     DOM::NodeImpl::MouseEvent mev( _ce->state(), DOM::NodeImpl::MouseMove ); // ### not a mouse event!
01211     m_part->xmlDocImpl()->prepareMouseEvent( xm, ym, &mev );
01212 
01213     NodeImpl *targetNode = mev.innerNode.handle();
01214     if (targetNode && targetNode->renderer() && targetNode->renderer()->isWidget()) {
01215         int absx = 0;
01216         int absy = 0;
01217         targetNode->renderer()->absolutePosition(absx,absy);
01218         QPoint pos(xm-absx,ym-absy);
01219 
01220         QWidget *w = static_cast<RenderWidget*>(targetNode->renderer())->widget();
01221         QContextMenuEvent cme(_ce->reason(),pos,_ce->globalPos(),_ce->state());
01222         setIgnoreEvents(true);
01223         QApplication::sendEvent(w,&cme);
01224         setIgnoreEvents(false);
01225     }
01226 #endif
01227 }
01228 
01229 bool KHTMLView::focusNextPrevChild( bool next )
01230 {
01231     // Now try to find the next child
01232     if (m_part->xmlDocImpl()) {
01233         focusNextPrevNode(next);
01234         if (m_part->xmlDocImpl()->focusNode() != 0) {
01235       kdDebug() << "focusNode.name: "
01236       << m_part->xmlDocImpl()->focusNode()->nodeName().string() << endl;
01237             return true; // focus node found
01238         }
01239     }
01240 
01241     // If we get here, there is no next/previous child to go to, so pass up to the next/previous child in our parent
01242     if (m_part->parentPart() && m_part->parentPart()->view())
01243         return m_part->parentPart()->view()->focusNextPrevChild(next);
01244 
01245     return QWidget::focusNextPrevChild(next);
01246 }
01247 
01248 void KHTMLView::doAutoScroll()
01249 {
01250     QPoint pos = QCursor::pos();
01251     pos = viewport()->mapFromGlobal( pos );
01252 
01253     int xm, ym;
01254     viewportToContents(pos.x(), pos.y(), xm, ym);
01255 
01256     pos = QPoint(pos.x() - viewport()->x(), pos.y() - viewport()->y());
01257     if ( (pos.y() < 0) || (pos.y() > visibleHeight()) ||
01258          (pos.x() < 0) || (pos.x() > visibleWidth()) )
01259     {
01260         ensureVisible( xm, ym, 0, 5 );
01261 
01262 #ifndef KHTML_NO_SELECTION
01263         // extend the selection while scrolling
01264     DOM::Node innerNode;
01265     if (m_part->isExtendingSelection()) {
01266             RenderObject::NodeInfo renderInfo(true/*readonly*/, false/*active*/);
01267             m_part->xmlDocImpl()->renderer()->layer()
01268                 ->nodeAtPoint(renderInfo, xm, ym);
01269             innerNode = renderInfo.innerNode();
01270     }/*end if*/
01271 
01272         if (innerNode.handle() && innerNode.handle()->renderer()) {
01273             int absX, absY;
01274             innerNode.handle()->renderer()->absolutePosition(absX, absY);
01275 
01276             m_part->extendSelectionTo(xm, ym, absX, absY, innerNode);
01277         }/*end if*/
01278 #endif // KHTML_NO_SELECTION
01279     }
01280 }
01281 
01282 
01283 class HackWidget : public QWidget
01284 {
01285  public:
01286     inline void setNoErase() { setWFlags(getWFlags()|WRepaintNoErase); }
01287 };
01288 
01289 bool KHTMLView::eventFilter(QObject *o, QEvent *e)
01290 {
01291     if ( e->type() == QEvent::AccelOverride ) {
01292     QKeyEvent* ke = (QKeyEvent*) e;
01293 //kdDebug(6200) << "QEvent::AccelOverride" << endl;
01294     if (m_part->isEditable() || m_part->isCaretMode()
01295         || (m_part->xmlDocImpl() && m_part->xmlDocImpl()->focusNode()
01296         && m_part->xmlDocImpl()->focusNode()->contentEditable())) {
01297 //kdDebug(6200) << "editable/navigable" << endl;
01298         if ( (ke->state() & ControlButton) || (ke->state() & ShiftButton) ) {
01299         switch ( ke->key() ) {
01300         case Key_Left:
01301         case Key_Right:
01302         case Key_Up:
01303         case Key_Down:
01304         case Key_Home:
01305         case Key_End:
01306             ke->accept();
01307 //kdDebug(6200) << "eaten" << endl;
01308             return true;
01309         default:
01310             break;
01311         }
01312         }
01313     }
01314     }
01315 
01316     QWidget *view = viewport();
01317 
01318     if (o == view) {
01319     // we need to install an event filter on all children of the viewport to
01320     // be able to get correct stacking of children within the document.
01321     if(e->type() == QEvent::ChildInserted) {
01322         QObject *c = static_cast<QChildEvent *>(e)->child();
01323         if (c->isWidgetType()) {
01324         QWidget *w = static_cast<QWidget *>(c);
01325         // don't install the event filter on toplevels
01326         if (w->parentWidget(true) == view) {
01327             if (!strcmp(w->name(), "__khtml")) {
01328             w->installEventFilter(this);
01329             w->unsetCursor();
01330             w->setBackgroundMode( QWidget::NoBackground );
01331             static_cast<HackWidget *>(w)->setNoErase();
01332             if (w->children()) {
01333                 QObjectListIterator it(*w->children());
01334                 for (; it.current(); ++it) {
01335                 QWidget *widget = ::qt_cast<QWidget *>(it.current());
01336                 if (widget && !widget->isTopLevel()
01337                     && !::qt_cast<QScrollView *>(widget)) {
01338                     widget->setBackgroundMode( QWidget::NoBackground );
01339                     static_cast<HackWidget *>(widget)->setNoErase();
01340                     widget->installEventFilter(this);
01341                 }
01342                 }
01343             }
01344             }
01345         }
01346         }
01347     }
01348     } else if (o->isWidgetType()) {
01349     QWidget *v = static_cast<QWidget *>(o);
01350         QWidget *c = v;
01351     while (v && v != view) {
01352             c = v;
01353         v = v->parentWidget(true);
01354     }
01355 
01356     if (v && !strcmp(c->name(), "__khtml")) {
01357         bool block = false;
01358         QWidget *w = static_cast<QWidget *>(o);
01359         switch(e->type()) {
01360         case QEvent::Paint:
01361         if (!allowWidgetPaintEvents) {
01362             // eat the event. Like this we can control exactly when the widget
01363             // get's repainted.
01364             block = true;
01365             int x = 0, y = 0;
01366             QWidget *v = w;
01367             while (v && v != view) {
01368             x += v->x();
01369             y += v->y();
01370             v = v->parentWidget();
01371             }
01372             viewportToContents( x, y, x, y );
01373             QPaintEvent *pe = static_cast<QPaintEvent *>(e);
01374             scheduleRepaint(x + pe->rect().x(), y + pe->rect().y(),
01375                     pe->rect().width(), pe->rect().height());
01376         }
01377         break;
01378         case QEvent::MouseMove:
01379         case QEvent::MouseButtonPress:
01380         case QEvent::MouseButtonRelease:
01381         case QEvent::MouseButtonDblClick: {
01382         if (w->parentWidget() == view && !::qt_cast<QScrollBar *>(w)) {
01383             QMouseEvent *me = static_cast<QMouseEvent *>(e);
01384             QPoint pt = (me->pos() + w->pos());
01385             QMouseEvent me2(me->type(), pt, me->button(), me->state());
01386 
01387             if (e->type() == QEvent::MouseMove)
01388             viewportMouseMoveEvent(&me2);
01389             else if(e->type() == QEvent::MouseButtonPress)
01390             viewportMousePressEvent(&me2);
01391             else if(e->type() == QEvent::MouseButtonRelease)
01392             viewportMouseReleaseEvent(&me2);
01393             else
01394             viewportMouseDoubleClickEvent(&me2);
01395             block = true;
01396                 }
01397         break;
01398         }
01399         case QEvent::KeyPress:
01400         case QEvent::KeyRelease:
01401         if (w->parentWidget() == view && !::qt_cast<QScrollBar *>(w)) {
01402             QKeyEvent *ke = static_cast<QKeyEvent *>(e);
01403             if (e->type() == QEvent::KeyPress)
01404             keyPressEvent(ke);
01405             else
01406             keyReleaseEvent(ke);
01407             block = true;
01408         }
01409         default:
01410         break;
01411         }
01412         if (block) {
01413         //qDebug("eating event");
01414         return true;
01415         }
01416     }
01417     }
01418 
01419 //    kdDebug(6000) <<"passing event on to sv event filter object=" << o->className() << " event=" << e->type() << endl;
01420     return QScrollView::eventFilter(o, e);
01421 }
01422 
01423 
01424 DOM::NodeImpl *KHTMLView::nodeUnderMouse() const
01425 {
01426     return d->underMouse;
01427 }
01428 
01429 bool KHTMLView::scrollTo(const QRect &bounds)
01430 {
01431     d->scrollingSelf = true; // so scroll events get ignored
01432 
01433     int x, y, xe, ye;
01434     x = bounds.left();
01435     y = bounds.top();
01436     xe = bounds.right();
01437     ye = bounds.bottom();
01438 
01439     //kdDebug(6000)<<"scrolling coords: x="<<x<<" y="<<y<<" width="<<xe-x<<" height="<<ye-y<<endl;
01440 
01441     int deltax;
01442     int deltay;
01443 
01444     int curHeight = visibleHeight();
01445     int curWidth = visibleWidth();
01446 
01447     if (ye-y>curHeight-d->borderY)
01448     ye  = y + curHeight - d->borderY;
01449 
01450     if (xe-x>curWidth-d->borderX)
01451     xe = x + curWidth - d->borderX;
01452 
01453     // is xpos of target left of the view's border?
01454     if (x < contentsX() + d->borderX )
01455             deltax = x - contentsX() - d->borderX;
01456     // is xpos of target right of the view's right border?
01457     else if (xe + d->borderX > contentsX() + curWidth)
01458             deltax = xe + d->borderX - ( contentsX() + curWidth );
01459     else
01460         deltax = 0;
01461 
01462     // is ypos of target above upper border?
01463     if (y < contentsY() + d->borderY)
01464             deltay = y - contentsY() - d->borderY;
01465     // is ypos of target below lower border?
01466     else if (ye + d->borderY > contentsY() + curHeight)
01467             deltay = ye + d->borderY - ( contentsY() + curHeight );
01468     else
01469         deltay = 0;
01470 
01471     int maxx = curWidth-d->borderX;
01472     int maxy = curHeight-d->borderY;
01473 
01474     int scrollX,scrollY;
01475     
01476     scrollX = deltax > 0 ? (deltax > maxx ? maxx : deltax) : deltax == 0 ? 0 : (deltax>-maxx ? deltax : -maxx);
01477     scrollY = deltay > 0 ? (deltay > maxy ? maxy : deltay) : deltay == 0 ? 0 : (deltay>-maxy ? deltay : -maxy);
01478     
01479     if (contentsX() + scrollX < 0)
01480     scrollX = -contentsX();
01481     else if (contentsWidth() - visibleWidth() - contentsX() < scrollX)
01482     scrollX = contentsWidth() - visibleWidth() - contentsX();
01483 
01484     if (contentsY() + scrollY < 0)
01485     scrollY = -contentsY();
01486     else if (contentsHeight() - visibleHeight() - contentsY() < scrollY)
01487     scrollY = contentsHeight() - visibleHeight() - contentsY();
01488 
01489     scrollBy(scrollX, scrollY);
01490 
01491 
01492 
01493     // generate abs(scroll.)
01494     if (scrollX<0)
01495         scrollX=-scrollX;
01496     if (scrollY<0)
01497         scrollY=-scrollY;
01498 
01499     d->scrollingSelf = false;
01500 
01501     if ( (scrollX!=maxx) && (scrollY!=maxy) )
01502     return true;
01503     else return false;
01504 
01505 }
01506 
01507 void KHTMLView::focusNextPrevNode(bool next)
01508 {
01509     // Sets the focus node of the document to be the node after (or if next is false, before) the current focus node.
01510     // Only nodes that are selectable (i.e. for which isSelectable() returns true) are taken into account, and the order
01511     // used is that specified in the HTML spec (see DocumentImpl::nextFocusNode() and DocumentImpl::previousFocusNode()
01512     // for details).
01513 
01514     DocumentImpl *doc = m_part->xmlDocImpl();
01515     NodeImpl *oldFocusNode = doc->focusNode();
01516     NodeImpl *newFocusNode;
01517 
01518     // Find the next/previous node from the current one
01519     if (next)
01520         newFocusNode = doc->nextFocusNode(oldFocusNode);
01521     else
01522         newFocusNode = doc->previousFocusNode(oldFocusNode);
01523 
01524     // If there was previously no focus node and the user has scrolled the document, then instead of picking the first
01525     // focusable node in the document, use the first one that lies within the visible area (if possible).
01526     if (!oldFocusNode && newFocusNode && d->scrollBarMoved) {
01527 
01528       kdDebug(6000) << " searching for visible link" << endl;
01529 
01530         bool visible = false;
01531         NodeImpl *toFocus = newFocusNode;
01532         while (!visible && toFocus) {
01533             QRect focusNodeRect = toFocus->getRect();
01534             if ((focusNodeRect.left() > contentsX()) && (focusNodeRect.right() < contentsX() + visibleWidth()) &&
01535                 (focusNodeRect.top() > contentsY()) && (focusNodeRect.bottom() < contentsY() + visibleHeight())) {
01536                 // toFocus is visible in the contents area
01537                 visible = true;
01538             }
01539             else {
01540                 // toFocus is _not_ visible in the contents area, pick the next node
01541                 if (next)
01542                     toFocus = doc->nextFocusNode(toFocus);
01543                 else
01544                     toFocus = doc->previousFocusNode(toFocus);
01545             }
01546         }
01547 
01548         if (toFocus)
01549             newFocusNode = toFocus;
01550     }
01551 
01552     d->scrollBarMoved = false;
01553 
01554     if (!newFocusNode)
01555       {
01556         // No new focus node, scroll to bottom or top depending on next
01557         if (next)
01558             scrollTo(QRect(contentsX()+visibleWidth()/2,contentsHeight(),0,0));
01559         else
01560             scrollTo(QRect(contentsX()+visibleWidth()/2,0,0,0));
01561     }
01562     else
01563     // Scroll the view as necessary to ensure that the new focus node is visible
01564     {
01565 #ifndef KHTML_NO_CARET
01566         // if it's an editable element, activate the caret
01567         if (!m_part->isCaretMode() && !m_part->isEditable()
01568         && newFocusNode->contentEditable()) {
01569         d->caretViewContext();
01570         moveCaretTo(newFocusNode, 0L, true);
01571         } else {
01572         caretOff();
01573     }
01574 #endif // KHTML_NO_CARET
01575 
01576       if (oldFocusNode)
01577     {
01578       if (!scrollTo(newFocusNode->getRect()))
01579         return;
01580     }
01581       else
01582     {
01583       ensureVisible(contentsX(), next?0:contentsHeight());
01584       //return;
01585     }
01586 
01587     }
01588 
01589     // Set focus node on the document
01590     Node guard(newFocusNode);
01591     m_part->xmlDocImpl()->setFocusNode(newFocusNode);
01592     if( newFocusNode != NULL && newFocusNode->hasOneRef()) // deleted, only held by guard
01593         return;
01594     emit m_part->nodeActivated(Node(newFocusNode));
01595 }
01596 
01597 // Handling of the HTML accesskey attribute.
01598 bool KHTMLView::handleAccessKey( const QKeyEvent* ev )
01599 {
01600     const int mods = Qt::AltButton | Qt::ControlButton;
01601     if( ( ev->state() & mods ) != mods )
01602         return false;
01603 // Qt interprets the keyevent also with the modifiers, and ev->text() matches that,
01604 // but this code must act as if the modifiers weren't pressed
01605     QChar c;
01606     if( ev->key() >= Key_A && ev->key() <= Key_Z )
01607         c = 'A' + ev->key() - Key_A;
01608     else if( ev->key() >= Key_0 && ev->key() <= Key_9 )
01609         c = '0' + ev->key() - Key_0;
01610     else {
01611         // TODO fake XKeyEvent and XLookupString ?
01612         // This below seems to work e.g. for eacute though.
01613         if( ev->text().length() == 1 )
01614             c = ev->text()[ 0 ];
01615     }
01616     if( c.isNull())
01617         return false;
01618     return focusNodeWithAccessKey( c );
01619 }
01620 
01621 bool KHTMLView::focusNodeWithAccessKey( QChar c, KHTMLView* caller )
01622 {
01623     DocumentImpl *doc = m_part->xmlDocImpl();
01624     if( !doc )
01625         return false;
01626     ElementImpl* node = doc->findAccessKeyElement( c );
01627     if( !node ) {
01628         QPtrList<KParts::ReadOnlyPart> frames = m_part->frames();
01629         for( QPtrListIterator<KParts::ReadOnlyPart> it( frames );
01630              it != NULL;
01631              ++it ) {
01632             if( !(*it)->inherits( "KHTMLPart" ))
01633                 continue;
01634             KHTMLPart* part = static_cast< KHTMLPart* >( *it );
01635             if( part->view() && part->view() != caller
01636                 && part->view()->focusNodeWithAccessKey( c, this ))
01637                 return true;
01638         }
01639         // pass up to the parent
01640         if (m_part->parentPart() && m_part->parentPart()->view()
01641             && m_part->parentPart()->view() != caller )
01642             return m_part->parentPart()->view()->focusNodeWithAccessKey( c, this );
01643         return false;
01644     }
01645 
01646     // Scroll the view as necessary to ensure that the new focus node is visible
01647 #ifndef KHTML_NO_CARET
01648     // if it's an editable element, activate the caret
01649     if (!m_part->isCaretMode() && !m_part->isEditable()
01650     && node->contentEditable()) {
01651         d->caretViewContext();
01652         moveCaretTo(node, 0L, true);
01653     } else {
01654         caretOff();
01655     }
01656 #endif // KHTML_NO_CARET
01657 
01658     QRect r = node->getRect();
01659     ensureVisible( r.right(), r.bottom());
01660     ensureVisible( r.left(), r.top());
01661 
01662     Node guard( node );
01663     if( node->isSelectable()) {
01664         // Set focus node on the document
01665         m_part->xmlDocImpl()->setFocusNode(node);
01666         if( node != NULL && node->hasOneRef()) // deleted, only held by guard
01667             return true;
01668         emit m_part->nodeActivated(Node(node));
01669         if( node != NULL && node->hasOneRef())
01670             return true;
01671     }
01672     switch( node->id()) {
01673         case ID_A:
01674             static_cast< HTMLAnchorElementImpl* >( node )->click();
01675           break;
01676         case ID_INPUT:
01677             static_cast< HTMLInputElementImpl* >( node )->click();
01678           break;
01679         case ID_BUTTON:
01680             static_cast< HTMLButtonElementImpl* >( node )->click();
01681           break;
01682         case ID_AREA:
01683             static_cast< HTMLAreaElementImpl* >( node )->click();
01684           break;
01685         case ID_LABEL:
01686             // TODO should be click(), after it works for label
01687           break;
01688         case ID_TEXTAREA:
01689           break; // just focusing it is enough
01690         case ID_LEGEND:
01691             // TODO
01692           break;
01693     }
01694     return true;
01695 }
01696 
01697 void KHTMLView::setMediaType( const QString &medium )
01698 {
01699     m_medium = medium;
01700 }
01701 
01702 QString KHTMLView::mediaType() const
01703 {
01704     return m_medium;
01705 }
01706 
01707 void KHTMLView::setWidgetVisible(RenderWidget* w, bool vis)
01708 {
01709     if (vis) {
01710         d->visibleWidgets.replace(w, w->widget());
01711     }
01712     else
01713         d->visibleWidgets.remove(w);
01714 }
01715 
01716 void KHTMLView::print()
01717 {
01718     print( false );
01719 }
01720 
01721 void KHTMLView::print(bool quick)
01722 {
01723     if(!m_part->xmlDocImpl()) return;
01724     khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer());
01725     if(!root) return;
01726 
01727     // this only works on Unix - we assume 72dpi
01728     KPrinter *printer = new KPrinter(true, QPrinter::PrinterResolution);
01729     printer->addDialogPage(new KHTMLPrintSettings());
01730     QString docname = m_part->xmlDocImpl()->URL().prettyURL();
01731     if ( !docname.isEmpty() )
01732         docname = KStringHandler::csqueeze(docname, 80);
01733     if(quick || printer->setup(this, i18n("Print %1").arg(docname))) {
01734         viewport()->setCursor( waitCursor ); // only viewport(), no QApplication::, otherwise we get the busy cursor in kdeprint's dialogs
01735         // set up KPrinter
01736         printer->setFullPage(false);
01737         printer->setCreator(QString("KDE %1.%2.%3 HTML Library").arg(KDE_VERSION_MAJOR).arg(KDE_VERSION_MINOR).arg(KDE_VERSION_RELEASE));
01738         printer->setDocName(docname);
01739 
01740         QPainter *p = new QPainter;
01741         p->begin( printer );
01742         khtml::setPrintPainter( p );
01743 
01744         m_part->xmlDocImpl()->setPaintDevice( printer );
01745         QString oldMediaType = mediaType();
01746         setMediaType( "print" );
01747         // We ignore margin settings for html and body when printing
01748         // and use the default margins from the print-system
01749         // (In Qt 3.0.x the default margins are hardcoded in Qt)
01750         m_part->xmlDocImpl()->setPrintStyleSheet( printer->option("app-khtml-printfriendly") == "true" ?
01751                                                   "* { background-image: none !important;"
01752                                                   "    background-color: white !important;"
01753                                                   "    color: black !important; }"
01754                           "body { margin: 0px !important; }"
01755                           "html { margin: 0px !important; }" :
01756                           "body { margin: 0px !important; }"
01757                           "html { margin: 0px !important; }"
01758                           );
01759 
01760         QPaintDeviceMetrics metrics( printer );
01761 
01762         // this is a simple approximation... we layout the document
01763         // according to the width of the page, then just cut
01764         // pages without caring about the content. We should do better
01765         // in the future, but for the moment this is better than no
01766         // printing support
01767         kdDebug(6000) << "printing: physical page width = " << metrics.width()
01768                       << " height = " << metrics.height() << endl;
01769         root->setPrintingMode(true);
01770         root->setWidth(metrics.width());
01771 
01772         m_part->xmlDocImpl()->styleSelector()->computeFontSizes(&metrics, 100);
01773         m_part->xmlDocImpl()->updateStyleSelector();
01774         root->setPrintImages( printer->option("app-khtml-printimages") == "true");
01775         root->setMinMaxKnown( false );
01776         root->setLayouted( false );
01777         root->layout();
01778         khtml::RenderWidget::flushWidgetResizes(); // make sure widgets have their final size
01779 
01780         bool printHeader = (printer->option("app-khtml-printheader") == "true");
01781 
01782         int headerHeight = 0;
01783         QFont headerFont("helvetica", 8);
01784 
01785         QString headerLeft = KGlobal::locale()->formatDate(QDate::currentDate(),true);
01786         QString headerMid = docname;
01787         QString headerRight;
01788 
01789         if (printHeader)
01790         {
01791            p->setFont(headerFont);
01792            headerHeight = (p->fontMetrics().lineSpacing() * 3) / 2;
01793         }
01794 
01795         // ok. now print the pages.
01796         kdDebug(6000) << "printing: html page width = " << root->docWidth()
01797                       << " height = " << root->docHeight() << endl;
01798         kdDebug(6000) << "printing: margins left = " << printer->margins().width()
01799                       << " top = " << printer->margins().height() << endl;
01800         kdDebug(6000) << "printing: paper width = " << metrics.width()
01801                       << " height = " << metrics.height() << endl;
01802         // if the width is too large to fit on the paper we just scale
01803         // the whole thing.
01804         int pageHeight = metrics.height();
01805         int pageWidth = metrics.width();
01806         p->setClipRect(0,0, pageWidth, pageHeight);
01807 
01808         pageHeight -= headerHeight;
01809 
01810         bool scalePage = false;
01811         double scale = 0.0;
01812 #ifndef QT_NO_TRANSFORMATIONS
01813         if(root->docWidth() > metrics.width()) {
01814             scalePage = true;
01815             scale = ((double) metrics.width())/((double) root->docWidth());
01816             pageHeight = (int) (pageHeight/scale);
01817             pageWidth = (int) (pageWidth/scale);
01818             headerHeight = (int) (headerHeight/scale);
01819         }
01820 #endif
01821         kdDebug(6000) << "printing: scaled html width = " << pageWidth
01822                       << " height = " << pageHeight << endl;
01823 
01824         // Squeeze header to make it it on the page.
01825         if (printHeader)
01826         {
01827             int available_width = metrics.width() - 10 -
01828                 2 * kMax(p->boundingRect(0, 0, metrics.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerLeft).width(),
01829                          p->boundingRect(0, 0, metrics.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerRight).width());
01830             if (available_width < 150)
01831                available_width = 150;
01832             int mid_width;
01833             int squeeze = 120;
01834             do {
01835                 headerMid = KStringHandler::csqueeze(docname, squeeze);
01836                 mid_width = p->boundingRect(0, 0, metrics.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerMid).width();
01837                 squeeze -= 10;
01838             } while (mid_width > available_width);
01839         }
01840 
01841         int top = 0;
01842         int page = 1;
01843         while(top < root->docHeight()) {
01844             if(top > 0) printer->newPage();
01845             if (printHeader)
01846             {
01847                 int dy = p->fontMetrics().lineSpacing();
01848                 p->setPen(Qt::black);
01849                 p->setFont(headerFont);
01850 
01851                 headerRight = QString("#%1").arg(page);
01852 
01853                 p->drawText(0, 0, metrics.width(), dy, Qt::AlignLeft, headerLeft);
01854                 p->drawText(0, 0, metrics.width(), dy, Qt::AlignHCenter, headerMid);
01855                 p->drawText(0, 0, metrics.width(), dy, Qt::AlignRight, headerRight);
01856             }
01857 
01858 #ifndef QT_NO_TRANSFORMATIONS
01859             if (scalePage)
01860                 p->scale(scale, scale);
01861 #endif
01862             p->translate(0, headerHeight-top);
01863 
01864             root->setTruncatedAt(top+pageHeight);
01865 
01866             root->layer()->paint(p, QRect(0, top, pageWidth, pageHeight));
01867             if (top + pageHeight >= root->docHeight())
01868                 break; // Stop if we have printed everything
01869 
01870             top = root->truncatedAt();
01871             p->resetXForm();
01872             page++;
01873         }
01874 
01875         p->end();
01876         delete p;
01877 
01878         // and now reset the layout to the usual one...
01879         root->setPrintingMode(false);
01880         khtml::setPrintPainter( 0 );
01881         setMediaType( oldMediaType );
01882         m_part->xmlDocImpl()->setPaintDevice( this );
01883         m_part->xmlDocImpl()->styleSelector()->computeFontSizes(m_part->xmlDocImpl()->paintDeviceMetrics(), m_part->zoomFactor());
01884         m_part->xmlDocImpl()->updateStyleSelector();
01885         viewport()->unsetCursor();
01886     }
01887     delete printer;
01888 }
01889 
01890 void KHTMLView::slotPaletteChanged()
01891 {
01892     if(!m_part->xmlDocImpl()) return;
01893     DOM::DocumentImpl *document = m_part->xmlDocImpl();
01894     if (!document->isHTMLDocument()) return;
01895     khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(document->renderer());
01896     if(!root) return;
01897     root->style()->resetPalette();
01898     NodeImpl *body = static_cast<HTMLDocumentImpl*>(document)->body();
01899     if(!body) return;
01900     body->setChanged(true);
01901     body->recalcStyle( NodeImpl::Force );
01902 }
01903 
01904 void KHTMLView::paint(QPainter *p, const QRect &rc, int yOff, bool *more)
01905 {
01906     if(!m_part->xmlDocImpl()) return;
01907     khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer());
01908     if(!root) return;
01909 
01910     m_part->xmlDocImpl()->setPaintDevice(p->device());
01911     root->setPrintingMode(true);
01912     root->setWidth(rc.width());
01913 
01914     p->save();
01915     p->setClipRect(rc);
01916     p->translate(rc.left(), rc.top());
01917     double scale = ((double) rc.width()/(double) root->docWidth());
01918     int height = (int) ((double) rc.height() / scale);
01919 #ifndef QT_NO_TRANSFORMATIONS
01920     p->scale(scale, scale);
01921 #endif
01922 
01923     root->layer()->paint(p, QRect(0, yOff, root->docWidth(), height));
01924     if (more)
01925         *more = yOff + height < root->docHeight();
01926     p->restore();
01927 
01928     root->setPrintingMode(false);
01929     m_part->xmlDocImpl()->setPaintDevice( this );
01930 }
01931 
01932 
01933 void KHTMLView::useSlowRepaints()
01934 {
01935     d->useSlowRepaints = true;
01936     setStaticBackground(true);
01937 }
01938 
01939 
01940 void KHTMLView::setVScrollBarMode ( ScrollBarMode mode )
01941 {
01942 #ifndef KHTML_NO_SCROLLBARS
01943     d->vmode = mode;
01944     QScrollView::setVScrollBarMode(mode);
01945 #else
01946     Q_UNUSED( mode );
01947 #endif
01948 }
01949 
01950 void KHTMLView::setHScrollBarMode ( ScrollBarMode mode )
01951 {
01952 #ifndef KHTML_NO_SCROLLBARS
01953     d->hmode = mode;
01954     QScrollView::setHScrollBarMode(mode);
01955 #else
01956     Q_UNUSED( mode );
01957 #endif
01958 }
01959 
01960 void KHTMLView::restoreScrollBar()
01961 {
01962     int ow = visibleWidth();
01963     QScrollView::setVScrollBarMode(d->vmode);
01964     if (visibleWidth() != ow)
01965         layout();
01966     d->prevScrollbarVisible = verticalScrollBar()->isVisible();
01967 }
01968 
01969 QStringList KHTMLView::formCompletionItems(const QString &name) const
01970 {
01971     if (!m_part->settings()->isFormCompletionEnabled())
01972         return QStringList();
01973     if (!d->formCompletions)
01974         d->formCompletions = new KSimpleConfig(locateLocal("data", "khtml/formcompletions"));
01975     return d->formCompletions->readListEntry(name);
01976 }
01977 
01978 void KHTMLView::clearCompletionHistory(const QString& name)
01979 {
01980     if (!d->formCompletions)
01981     {
01982         d->formCompletions = new KSimpleConfig(locateLocal("data", "khtml/formcompletions"));
01983     }
01984     d->formCompletions->writeEntry(name, "");
01985     d->formCompletions->sync();
01986 }
01987 
01988 void KHTMLView::addFormCompletionItem(const QString &name, const QString &value)
01989 {
01990     if (!m_part->settings()->isFormCompletionEnabled())
01991         return;
01992     // don't store values that are all numbers or just numbers with
01993     // dashes or spaces as those are likely credit card numbers or
01994     // something similar
01995     bool cc_number(true);
01996     for (unsigned int i = 0; i < value.length(); ++i)
01997     {
01998       QChar c(value[i]);
01999       if (!c.isNumber() && c != '-' && !c.isSpace())
02000       {
02001         cc_number = false;
02002         break;
02003       }
02004     }
02005     if (cc_number)
02006       return;
02007     QStringList items = formCompletionItems(name);
02008     if (!items.contains(value))
02009         items.prepend(value);
02010     while ((int)items.count() > m_part->settings()->maxFormCompletionItems())
02011         items.remove(items.fromLast());
02012     d->formCompletions->writeEntry(name, items);
02013 }
02014 
02015 void KHTMLView::addNonPasswordStorableSite(const QString& host)
02016 {
02017     if (!d->formCompletions) {
02018         d->formCompletions = new KSimpleConfig(locateLocal("data", "khtml/formcompletions"));
02019     }
02020 
02021     d->formCompletions->setGroup("NonPasswordStorableSites");
02022     QStringList sites = d->formCompletions->readListEntry("Sites");
02023     sites.append(host);
02024     d->formCompletions->writeEntry("Sites", sites);
02025     d->formCompletions->sync();
02026     d->formCompletions->setGroup(QString::null);//reset
02027 }
02028 
02029 bool KHTMLView::nonPasswordStorableSite(const QString& host) const
02030 {
02031     if (!d->formCompletions) {
02032         d->formCompletions = new KSimpleConfig(locateLocal("data", "khtml/formcompletions"));
02033     }
02034     d->formCompletions->setGroup("NonPasswordStorableSites");
02035     QStringList sites =  d->formCompletions->readListEntry("Sites");
02036     d->formCompletions->setGroup(QString::null);//reset
02037 
02038     return (sites.find(host) != sites.end());
02039 }
02040 
02041 // returns true if event should be swallowed
02042 bool KHTMLView::dispatchMouseEvent(int eventId, DOM::NodeImpl *targetNode, bool cancelable,
02043                    int detail,QMouseEvent *_mouse, bool setUnder,
02044                    int mouseEventType)
02045 {
02046     if (d->underMouse)
02047     d->underMouse->deref();
02048     d->underMouse = targetNode;
02049     if (d->underMouse)
02050     d->underMouse->ref();
02051 
02052     int exceptioncode = 0;
02053     int pageX = 0;
02054     int pageY = 0;
02055     viewportToContents(_mouse->x(), _mouse->y(), pageX, pageY);
02056     int clientX = pageX - contentsX();
02057     int clientY = pageY - contentsY();
02058     int screenX = _mouse->globalX();
02059     int screenY = _mouse->globalY();
02060     int button = -1;
02061     switch (_mouse->button()) {
02062     case LeftButton:
02063         button = 0;
02064         break;
02065     case MidButton:
02066         button = 1;
02067         break;
02068     case RightButton:
02069         button = 2;
02070         break;
02071     default:
02072         break;
02073     }
02074     bool ctrlKey = (_mouse->state() & ControlButton);
02075     bool altKey = (_mouse->state() & AltButton);
02076     bool shiftKey = (_mouse->state() & ShiftButton);
02077     bool metaKey = (_mouse->state() & MetaButton);
02078 
02079     // mouseout/mouseover
02080     if (setUnder && (d->prevMouseX != pageX || d->prevMouseY != pageY)) {
02081 
02082         // ### this code sucks. we should save the oldUnder instead of calculating
02083         // it again. calculating is expensive! (Dirk)
02084         NodeImpl *oldUnder = 0;
02085     if (d->prevMouseX >= 0 && d->prevMouseY >= 0) {
02086         NodeImpl::MouseEvent mev( _mouse->stateAfter(), static_cast<NodeImpl::MouseEventType>(mouseEventType));
02087         m_part->xmlDocImpl()->prepareMouseEvent( true, d->prevMouseX, d->prevMouseY, &mev );
02088         oldUnder = mev.innerNode.handle();
02089     }
02090 //  qDebug("oldunder=%p (%s), target=%p (%s) x/y=%d/%d", oldUnder, oldUnder ? oldUnder->renderer()->renderName() : 0, targetNode,  targetNode ? targetNode->renderer()->renderName() : 0, _mouse->x(), _mouse->y());
02091     if (oldUnder != targetNode) {
02092         // send mouseout event to the old node
02093         if (oldUnder){
02094         oldUnder->ref();
02095         MouseEventImpl *me = new MouseEventImpl(EventImpl::MOUSEOUT_EVENT,
02096                             true,true,m_part->xmlDocImpl()->defaultView(),
02097                             0,screenX,screenY,clientX,clientY,pageX, pageY,
02098                             ctrlKey,altKey,shiftKey,metaKey,
02099                             button,targetNode);
02100         me->ref();
02101         oldUnder->dispatchEvent(me,exceptioncode,true);
02102         me->deref();
02103         }
02104 
02105         // send mouseover event to the new node
02106         if (targetNode) {
02107         MouseEventImpl *me = new MouseEventImpl(EventImpl::MOUSEOVER_EVENT,
02108                             true,true,m_part->xmlDocImpl()->defaultView(),
02109                             0,screenX,screenY,clientX,clientY,pageX, pageY,
02110                             ctrlKey,altKey,shiftKey,metaKey,
02111                             button,oldUnder);
02112 
02113         me->ref();
02114         targetNode->dispatchEvent(me,exceptioncode,true);
02115         me->deref();
02116         }
02117 
02118             if (oldUnder)
02119                 oldUnder->deref();
02120         }
02121     }
02122 
02123     bool swallowEvent = false;
02124 
02125     if (targetNode) {
02126         // send the actual event
02127         bool dblclick = ( eventId == EventImpl::CLICK_EVENT &&
02128                           _mouse->type() == QEvent::MouseButtonDblClick );
02129         MouseEventImpl *me = new MouseEventImpl(static_cast<EventImpl::EventId>(eventId),
02130                         true,cancelable,m_part->xmlDocImpl()->defaultView(),
02131                         detail,screenX,screenY,clientX,clientY,pageX, pageY,
02132                         ctrlKey,altKey,shiftKey,metaKey,
02133                         button,0, _mouse, dblclick );
02134         me->ref();
02135         targetNode->dispatchEvent(me,exceptioncode,true);
02136         if (me->defaultHandled() || me->defaultPrevented())
02137             swallowEvent = true;
02138         me->deref();
02139 
02140         if (eventId == EventImpl::MOUSEDOWN_EVENT) {
02141             if (targetNode->isSelectable())
02142                 m_part->xmlDocImpl()->setFocusNode(targetNode);
02143             else
02144                 m_part->xmlDocImpl()->setFocusNode(0);
02145         }
02146     }
02147 
02148     return swallowEvent;
02149 }
02150 
02151 void KHTMLView::setIgnoreWheelEvents( bool e )
02152 {
02153     d->ignoreWheelEvents = e;
02154 }
02155 
02156 #ifndef QT_NO_WHEELEVENT
02157 
02158 void KHTMLView::viewportWheelEvent(QWheelEvent* e)
02159 {
02160     if ( ( e->state() & ControlButton) == ControlButton )
02161     {
02162         emit zoomView( - e->delta() );
02163         e->accept();
02164     }
02165     else if ( ( (d->ignoreWheelEvents && !verticalScrollBar()->isVisible())
02166                 || e->delta() > 0 && contentsY() <= 0
02167                         || e->delta() < 0 && contentsY() >= contentsHeight() - visibleHeight())
02168                 && m_part->parentPart() ) {
02169        kdDebug(6000) << this << " cz " << contentsY() << " ch " << contentsHeight() << " vh " << visibleHeight() << endl;
02170         if ( m_part->parentPart()->view() )
02171             m_part->parentPart()->view()->wheelEvent( e );
02172         kdDebug(6000) << "sent" << endl;
02173         e->ignore();
02174     }
02175     else if ( d->vmode == QScrollView::AlwaysOff ) {
02176         e->accept();
02177     }
02178     else {
02179         d->scrollBarMoved = true;
02180         QScrollView::viewportWheelEvent( e );
02181 
02182         QMouseEvent *tempEvent = new QMouseEvent( QEvent::MouseMove, QPoint(-1,-1), QPoint(-1,-1), Qt::NoButton, e->state() );
02183         emit viewportMouseMoveEvent ( tempEvent );
02184         delete tempEvent;
02185     }
02186 
02187 }
02188 #endif
02189 
02190 void KHTMLView::dragEnterEvent( QDragEnterEvent* ev )
02191 {
02192     // Handle drops onto frames (#16820)
02193     // Drops on the main html part is handled by Konqueror (and shouldn't do anything
02194     // in e.g. kmail, so not handled here).
02195     if ( m_part->parentPart() )
02196     {
02197         QApplication::sendEvent(m_part->parentPart()->widget(), ev);
02198     return;
02199     }
02200     QScrollView::dragEnterEvent( ev );
02201 }
02202 
02203 void KHTMLView::dropEvent( QDropEvent *ev )
02204 {
02205     // Handle drops onto frames (#16820)
02206     // Drops on the main html part is handled by Konqueror (and shouldn't do anything
02207     // in e.g. kmail, so not handled here).
02208     if ( m_part->parentPart() )
02209     {
02210         QApplication::sendEvent(m_part->parentPart()->widget(), ev);
02211     return;
02212     }
02213     QScrollView::dropEvent( ev );
02214 }
02215 
02216 void KHTMLView::focusInEvent( QFocusEvent *e )
02217 {
02218 #ifndef KHTML_NO_CARET
02219     // Restart blink frequency timer if it has been killed, but only on
02220     // editable nodes
02221     if (d->m_caretViewContext &&
02222         d->m_caretViewContext->freqTimerId == -1 &&
02223         m_part->xmlDocImpl()) {
02224         NodeImpl *caretNode = m_part->xmlDocImpl()->focusNode();
02225         if (m_part->isCaretMode()
02226         || m_part->isEditable()
02227             || (caretNode && caretNode->renderer()
02228             && caretNode->renderer()->style()->userInput()
02229                 == UI_ENABLED)) {
02230             d->m_caretViewContext->freqTimerId = startTimer(500);
02231         d->m_caretViewContext->visible = true;
02232         }/*end if*/
02233     }/*end if*/
02234     showCaret();
02235 #endif // KHTML_NO_CARET
02236     QScrollView::focusInEvent( e );
02237 }
02238 
02239 void KHTMLView::focusOutEvent( QFocusEvent *e )
02240 {
02241     if(m_part) m_part->stopAutoScroll();
02242 
02243 #ifndef KHTML_NO_CARET
02244     if (d->m_caretViewContext) {
02245         switch (d->m_caretViewContext->displayNonFocused) {
02246     case KHTMLPart::CaretInvisible:
02247             hideCaret();
02248         break;
02249     case KHTMLPart::CaretVisible: {
02250         killTimer(d->m_caretViewContext->freqTimerId);
02251         d->m_caretViewContext->freqTimerId = -1;
02252             NodeImpl *caretNode = m_part->xmlDocImpl()->focusNode();
02253         if (!d->m_caretViewContext->visible && (m_part->isCaretMode()
02254         || m_part->isEditable()
02255             || (caretNode && caretNode->renderer()
02256             && caretNode->renderer()->style()->userInput()
02257                 == UI_ENABLED))) {
02258             d->m_caretViewContext->visible = true;
02259             showCaret(true);
02260         }/*end if*/
02261         break;
02262     }
02263     case KHTMLPart::CaretBlink:
02264         // simply leave as is
02265         break;
02266     }/*end switch*/
02267     }/*end if*/
02268 #endif // KHTML_NO_CARET
02269     QScrollView::focusOutEvent( e );
02270 }
02271 
02272 void KHTMLView::slotScrollBarMoved()
02273 {
02274     if (!d->scrollingSelf)
02275         d->scrollBarMoved = true;
02276 }
02277 
02278 void KHTMLView::timerEvent ( QTimerEvent *e )
02279 {
02280 //    kdDebug() << "timer event " << e->timerId() << endl;
02281     if ( e->timerId() == d->scrollTimerId ) {
02282         if( d->scrollSuspended )
02283             return;
02284         switch (d->scrollDirection) {
02285             case KHTMLViewPrivate::ScrollDown:
02286                 if (contentsY() + visibleHeight () >= contentsHeight())
02287                     d->newScrollTimer(this, 0);
02288                 else
02289                     scrollBy( 0, d->scrollBy );
02290                 break;
02291             case KHTMLViewPrivate::ScrollUp:
02292                 if (contentsY() <= 0)
02293                     d->newScrollTimer(this, 0);
02294                 else
02295                     scrollBy( 0, -d->scrollBy );
02296                 break;
02297             case KHTMLViewPrivate::ScrollRight:
02298                 if (contentsX() + visibleWidth () >= contentsWidth())
02299                     d->newScrollTimer(this, 0);
02300                 else
02301                     scrollBy( d->scrollBy, 0 );
02302                 break;
02303             case KHTMLViewPrivate::ScrollLeft:
02304                 if (contentsX() <= 0)
02305                     d->newScrollTimer(this, 0);
02306                 else
02307                     scrollBy( -d->scrollBy, 0 );
02308                 break;
02309         }
02310         return;
02311     }
02312     else if ( e->timerId() == d->layoutTimerId ) {
02313         d->firstRelayout = false;
02314         d->dirtyLayout = true;
02315         layout();
02316     }
02317 #ifndef KHTML_NO_CARET
02318     else if (d->m_caretViewContext
02319              && e->timerId() == d->m_caretViewContext->freqTimerId) {
02320         d->m_caretViewContext->visible = !d->m_caretViewContext->visible;
02321     if (d->m_caretViewContext->displayed) {
02322         updateContents(d->m_caretViewContext->x, d->m_caretViewContext->y,
02323             d->m_caretViewContext->width,
02324             d->m_caretViewContext->height);
02325     }/*end if*/
02326 //  if (d->m_caretViewContext->visible) cout << "|" << flush;
02327 //  else cout << "°" << flush;
02328     return;
02329     }
02330 #endif
02331 
02332     if( m_part->xmlDocImpl() ) {
02333     DOM::DocumentImpl *document = m_part->xmlDocImpl();
02334     khtml::RenderCanvas* root = static_cast<khtml::RenderCanvas *>(document->renderer());
02335 
02336     if ( root && !root->layouted() ) {
02337         killTimer(d->repaintTimerId);
02338         d->repaintTimerId = 0;
02339         scheduleRelayout();
02340         return;
02341     }
02342     }
02343 
02344     setStaticBackground(d->useSlowRepaints);
02345 
02346 //        kdDebug() << "scheduled repaint "<< d->repaintTimerId  << endl;
02347     killTimer(d->repaintTimerId);
02348     d->repaintTimerId = 0;
02349 
02350     QRegion updateRegion;
02351     QMemArray<QRect> rects = d->updateRegion.rects();
02352 
02353     d->updateRegion = QRegion();
02354 
02355     if ( rects.size() )
02356         updateRegion = rects[0];
02357 
02358     for ( unsigned i = 1; i < rects.size(); ++i ) {
02359         QRect obR = updateRegion.boundingRect();
02360         QRegion newRegion = updateRegion.unite(rects[i]);
02361         if (2*newRegion.boundingRect().height() > 3*obR.height() )
02362         {
02363             repaintContents( obR );
02364             updateRegion = rects[i];
02365         }
02366         else
02367             updateRegion = newRegion;
02368     }
02369 
02370     if ( !updateRegion.isNull() )
02371         repaintContents( updateRegion.boundingRect() );
02372 
02373     if (d->dirtyLayout && !d->visibleWidgets.isEmpty()) {
02374         QWidget* w;
02375         d->dirtyLayout = false;
02376 
02377         QRect visibleRect(contentsX(), contentsY(), visibleWidth(), visibleHeight());
02378         QPtrList<RenderWidget> toRemove;
02379         for (QPtrDictIterator<QWidget> it(d->visibleWidgets); it.current(); ++it) {
02380             int xp = 0, yp = 0;
02381             w = it.current();
02382             RenderWidget* rw = static_cast<RenderWidget*>( it.currentKey() );
02383             if (!rw->absolutePosition(xp, yp) ||
02384                 !visibleRect.intersects(QRect(xp, yp, w->width(), w->height())))
02385                 toRemove.append(rw);
02386         }
02387         for (RenderWidget* r = toRemove.first(); r; r = toRemove.next())
02388             if ( (w = d->visibleWidgets.take(r) ) )
02389                 addChild(w, 0, -500000);
02390     }
02391 }
02392 
02393 void KHTMLView::scheduleRelayout(khtml::RenderObject * /*clippedObj*/)
02394 {
02395     if (!d->layoutSchedulingEnabled || d->layoutTimerId)
02396         return;
02397 
02398     d->layoutTimerId = startTimer( m_part->xmlDocImpl() && m_part->xmlDocImpl()->parsing()
02399                              ? 1000 : 0 );
02400 }
02401 
02402 void KHTMLView::unscheduleRelayout()
02403 {
02404     if (!d->layoutTimerId)
02405         return;
02406 
02407     killTimer(d->layoutTimerId);
02408     d->layoutTimerId = 0;
02409 }
02410 
02411 void KHTMLView::unscheduleRepaint()
02412 {
02413     if (!d->repaintTimerId)
02414         return;
02415 
02416     killTimer(d->repaintTimerId);
02417     d->repaintTimerId = 0;
02418 }
02419 
02420 void KHTMLView::scheduleRepaint(int x, int y, int w, int h)
02421 {
02422     bool parsing = !m_part->xmlDocImpl() || m_part->xmlDocImpl()->parsing();
02423 
02424 //     kdDebug() << "parsing " << parsing << endl;
02425 //     kdDebug() << "complete " << d->complete << endl;
02426 
02427     int time = parsing ? 300 : ( !d->complete ? 100 : 20 );
02428 
02429 #ifdef DEBUG_FLICKER
02430     QPainter p;
02431     p.begin( viewport() );
02432 
02433     int vx, vy;
02434     contentsToViewport( x, y, vx, vy );
02435     p.fillRect( vx, vy, w, h, Qt::red );
02436     p.end();
02437 #endif
02438 
02439     d->updateRegion = d->updateRegion.unite(QRect(x,y,w,h));
02440 
02441     if ( !d->repaintTimerId )
02442         d->repaintTimerId = startTimer( time );
02443 
02444 //     kdDebug() << "starting timer " << time << endl;
02445 }
02446 
02447 void KHTMLView::complete()
02448 {
02449 //     kdDebug() << "KHTMLView::complete()" << endl;
02450 
02451     d->complete = true;
02452 
02453     // is there a relayout pending?
02454     if (d->layoutTimerId)
02455     {
02456 //         kdDebug() << "requesting relayout now" << endl;
02457         // do it now
02458         killTimer(d->layoutTimerId);
02459         d->layoutTimerId = startTimer( 0 );
02460     }
02461 
02462     // is there a repaint pending?
02463     if (d->repaintTimerId)
02464     {
02465 //         kdDebug() << "requesting repaint now" << endl;
02466         // do it now
02467         killTimer(d->repaintTimerId);
02468         d->repaintTimerId = startTimer( 20 );
02469     }
02470 }
02471 
02472 #ifndef KHTML_NO_CARET
02473 
02474 // ### the dependencies on static functions are a nightmare. just be
02475 // hacky and include the implementation here. Clean me up, please.
02476 
02477 #include "khtml_caret.cpp"
02478 
02479 void KHTMLView::initCaret(bool keepSelection)
02480 {
02481 #if DEBUG_CARETMODE > 0
02482   kdDebug(6200) << "begin initCaret" << endl;
02483 #endif
02484   // save caretMoved state as moveCaretTo changes it
02485   if (m_part->xmlDocImpl()) {
02486     d->caretViewContext();
02487     bool cmoved = d->m_caretViewContext->caretMoved;
02488     if (m_part->d->caretNode().isNull()) {
02489       // set to document, position will be sanitized anyway
02490       m_part->d->caretNode() = m_part->document();
02491       m_part->d->caretOffset() = 0L;
02492       // This sanity check is necessary for the not so unlikely case that
02493       // setEditable or setCaretMode is called before any render objects have
02494       // been created.
02495       if (!m_part->d->caretNode().handle()->renderer()) return;
02496     }/*end if*/
02497 //    kdDebug(6200) << "d->m_selectionStart " << m_part->d->m_selectionStart.handle()
02498 //          << " d->m_selectionEnd " << m_part->d->m_selectionEnd.handle() << endl;
02499     // ### does not repaint the selection on keepSelection!=false
02500     moveCaretTo(m_part->d->caretNode().handle(), m_part->d->caretOffset(), !keepSelection);
02501 //    kdDebug(6200) << "d->m_selectionStart " << m_part->d->m_selectionStart.handle()
02502 //          << " d->m_selectionEnd " << m_part->d->m_selectionEnd.handle() << endl;
02503     d->m_caretViewContext->caretMoved = cmoved;
02504   }/*end if*/
02505 #if DEBUG_CARETMODE > 0
02506   kdDebug(6200) << "end initCaret" << endl;
02507 #endif
02508 }
02509 
02510 bool KHTMLView::caretOverrides() const
02511 {
02512     bool cm = m_part->isCaretMode();
02513     bool dm = m_part->isEditable();
02514     return cm && !dm ? false
02515         : (dm || m_part->d->caretNode().handle()->contentEditable())
02516       && d->editorContext()->override;
02517 }
02518 
02519 void KHTMLView::ensureNodeHasFocus(NodeImpl *node)
02520 {
02521   if (m_part->isCaretMode() || m_part->isEditable()) return;
02522   if (node->focused()) return;
02523 
02524   // Find first ancestor whose "user-input" is "enabled"
02525   NodeImpl *firstAncestor = 0;
02526   while (node) {
02527     if (node->renderer()
02528        && node->renderer()->style()->userInput() != UI_ENABLED)
02529       break;
02530     firstAncestor = node;
02531     node = node->parentNode();
02532   }/*wend*/
02533 
02534   if (!node) firstAncestor = 0;
02535 
02536   DocumentImpl *doc = m_part->xmlDocImpl();
02537   // ensure that embedded widgets don't lose their focus
02538   if (!firstAncestor && doc->focusNode() && doc->focusNode()->renderer()
02539     && doc->focusNode()->renderer()->isWidget())
02540     return;
02541 
02542   // Set focus node on the document
02543 #if DEBUG_CARETMODE > 1
02544   kdDebug(6200) << k_funcinfo << "firstAncestor " << firstAncestor << ": "
02545     << (firstAncestor ? firstAncestor->nodeName().string() : QString::null) << endl;
02546 #endif
02547   doc->setFocusNode(firstAncestor);
02548   emit m_part->nodeActivated(Node(firstAncestor));
02549 }
02550 
02551 void KHTMLView::recalcAndStoreCaretPos(InlineBox *hintBox)
02552 {
02553     if (!m_part || m_part->d->caretNode().isNull()) return;
02554     d->caretViewContext();
02555     NodeImpl *caretNode = m_part->d->caretNode().handle();
02556 #if DEBUG_CARETMODE > 0
02557   kdDebug(6200) << "recalcAndStoreCaretPos: caretNode=" << caretNode << (caretNode ? " "+caretNode->nodeName().string() : QString::null) << " r@" << caretNode->renderer() << (caretNode->renderer() && caretNode->renderer()->isText() ? " \"" + QConstString(static_cast<RenderText *>(caretNode->renderer())->str->s, kMin(static_cast<RenderText *>(caretNode->renderer())->str->l, 15u)).string() + "\"" : QString::null) << endl;
02558 #endif
02559     caretNode->getCaret(m_part->d->caretOffset(),
02560                 caretOverrides(),
02561             d->m_caretViewContext->x, d->m_caretViewContext->y,
02562         d->m_caretViewContext->width,
02563         d->m_caretViewContext->height);
02564 
02565     if (hintBox && d->m_caretViewContext->x == -1) {
02566 #if DEBUG_CARETMODE > 1
02567         kdDebug(6200) << "using hint inline box coordinates" << endl;
02568 #endif
02569     RenderObject *r = caretNode->renderer();
02570     const QFontMetrics &fm = r->style()->fontMetrics();
02571         int absx, absy;
02572     r->containingBlock()->absolutePosition(absx, absy,
02573                         false); // ### what about fixed?
02574     d->m_caretViewContext->x = absx + hintBox->xPos();
02575     d->m_caretViewContext->y = absy + hintBox->yPos()
02576                 + hintBox->baseline() - fm.ascent();
02577     d->m_caretViewContext->width = 1;
02578     // ### firstline not regarded. But I think it can be safely neglected
02579     // as hint boxes are only used for empty lines.
02580     d->m_caretViewContext->height = fm.height();
02581     }/*end if*/
02582 
02583 #if DEBUG_CARETMODE > 4
02584 //    kdDebug(6200) << "freqTimerId: "<<d->m_caretViewContext->freqTimerId<<endl;
02585 #endif
02586 #if DEBUG_CARETMODE > 0
02587     kdDebug(6200) << "caret: ofs="<<m_part->d->caretOffset()<<" "
02588         <<" x="<<d->m_caretViewContext->x<<" y="<<d->m_caretViewContext->y
02589     <<" h="<<d->m_caretViewContext->height<<endl;
02590 #endif
02591 }
02592 
02593 void KHTMLView::caretOn()
02594 {
02595     if (d->m_caretViewContext) {
02596         killTimer(d->m_caretViewContext->freqTimerId);
02597 
02598     if (hasFocus() || d->m_caretViewContext->displayNonFocused
02599             == KHTMLPart::CaretBlink) {
02600             d->m_caretViewContext->freqTimerId = startTimer(500);
02601     } else {
02602         d->m_caretViewContext->freqTimerId = -1;
02603     }/*end if*/
02604 
02605         d->m_caretViewContext->visible = true;
02606         if ((d->m_caretViewContext->displayed = (hasFocus()
02607         || d->m_caretViewContext->displayNonFocused
02608             != KHTMLPart::CaretInvisible))) {
02609         updateContents(d->m_caretViewContext->x, d->m_caretViewContext->y,
02610                 d->m_caretViewContext->width,
02611             d->m_caretViewContext->height);
02612     }/*end if*/
02613 //        kdDebug(6200) << "caret on" << endl;
02614     }/*end if*/
02615 }
02616 
02617 void KHTMLView::caretOff()
02618 {
02619     if (d->m_caretViewContext) {
02620         killTimer(d->m_caretViewContext->freqTimerId);
02621     d->m_caretViewContext->freqTimerId = -1;
02622         d->m_caretViewContext->displayed = false;
02623         if (d->m_caretViewContext->visible) {
02624             d->m_caretViewContext->visible = false;
02625         updateContents(d->m_caretViewContext->x, d->m_caretViewContext->y,
02626                 d->m_caretViewContext->width,
02627                 d->m_caretViewContext->height);
02628     }/*end if*/
02629 //        kdDebug(6200) << "caret off" << endl;
02630     }/*end if*/
02631 }
02632 
02633 void KHTMLView::showCaret(bool forceRepaint)
02634 {
02635     if (d->m_caretViewContext) {
02636         d->m_caretViewContext->displayed = true;
02637         if (d->m_caretViewContext->visible) {
02638         if (!forceRepaint) {
02639             updateContents(d->m_caretViewContext->x, d->m_caretViewContext->y,
02640                 d->m_caretViewContext->width,
02641             d->m_caretViewContext->height);
02642             } else {
02643             repaintContents(d->m_caretViewContext->x, d->m_caretViewContext->y,
02644                 d->m_caretViewContext->width,
02645                 d->m_caretViewContext->height);
02646         }/*end if*/
02647     }/*end if*/
02648 //        kdDebug(6200) << "caret shown" << endl;
02649     }/*end if*/
02650 }
02651 
02652 bool KHTMLView::foldSelectionToCaret(NodeImpl *startNode, long startOffset,
02653                     NodeImpl *endNode, long endOffset)
02654 {
02655   m_part->d->m_selectionStart = m_part->d->m_selectionEnd = m_part->d->caretNode();
02656   m_part->d->m_startOffset = m_part->d->m_endOffset = m_part->d->caretOffset();
02657   m_part->d->m_extendAtEnd = true;
02658 
02659   bool folded = startNode != endNode || startOffset != endOffset;
02660 
02661   // Only clear the selection if there has been one.
02662   if (folded) {
02663     m_part->xmlDocImpl()->clearSelection();
02664   }/*end if*/
02665 
02666   return folded;
02667 }
02668 
02669 void KHTMLView::hideCaret()
02670 {
02671     if (d->m_caretViewContext) {
02672         if (d->m_caretViewContext->visible) {
02673 //            kdDebug(6200) << "redraw caret hidden" << endl;
02674         d->m_caretViewContext->visible = false;
02675         // force repaint, otherwise the event won't be handled
02676         // before the focus leaves the window
02677         repaintContents(d->m_caretViewContext->x, d->m_caretViewContext->y,
02678                 d->m_caretViewContext->width,
02679                 d->m_caretViewContext->height);
02680         d->m_caretViewContext->visible = true;
02681     }/*end if*/
02682         d->m_caretViewContext->displayed = false;
02683 //        kdDebug(6200) << "caret hidden" << endl;
02684     }/*end if*/
02685 }
02686 
02687 int KHTMLView::caretDisplayPolicyNonFocused() const
02688 {
02689   if (d->m_caretViewContext)
02690     return d->m_caretViewContext->displayNonFocused;
02691   else
02692     return KHTMLPart::CaretInvisible;
02693 }
02694 
02695 void KHTMLView::setCaretDisplayPolicyNonFocused(int policy)
02696 {
02697   d->caretViewContext();
02698 //  int old = d->m_caretViewContext->displayNonFocused;
02699   d->m_caretViewContext->displayNonFocused = (KHTMLPart::CaretDisplayPolicy)policy;
02700 
02701   // make change immediately take effect if not focused
02702   if (!hasFocus()) {
02703     switch (d->m_caretViewContext->displayNonFocused) {
02704       case KHTMLPart::CaretInvisible:
02705         hideCaret();
02706     break;
02707       case KHTMLPart::CaretBlink:
02708     if (d->m_caretViewContext->freqTimerId != -1) break;
02709     d->m_caretViewContext->freqTimerId = startTimer(500);
02710     // fall through
02711       case KHTMLPart::CaretVisible:
02712         d->m_caretViewContext->displayed = true;
02713         showCaret();
02714     break;
02715     }/*end switch*/
02716   }/*end if*/
02717 }
02718 
02719 bool KHTMLView::placeCaret(InlineBox *hintBox)
02720 {
02721   CaretViewContext *cv = d->caretViewContext();
02722   caretOff();
02723   NodeImpl *caretNode = m_part->d->caretNode().handle();
02724   // ### why is it sometimes null?
02725   if (!caretNode || !caretNode->renderer()) return false;
02726   ensureNodeHasFocus(caretNode);
02727   if (m_part->isCaretMode() || m_part->isEditable()
02728      || caretNode->renderer()->style()->userInput() == UI_ENABLED) {
02729     recalcAndStoreCaretPos(hintBox);
02730 
02731     cv->origX = cv->x;
02732 
02733     caretOn();
02734     return true;
02735   }/*end if*/
02736   return false;
02737 }
02738 
02739 void KHTMLView::ensureCaretVisible()
02740 {
02741   CaretViewContext *cv = d->m_caretViewContext;
02742   if (!cv) return;
02743   ensureVisible(cv->x, cv->y, cv->width, cv->height);
02744   d->scrollBarMoved = false;
02745 }
02746 
02747 bool KHTMLView::extendSelection(NodeImpl *oldStartSel, long oldStartOfs,
02748                 NodeImpl *oldEndSel, long oldEndOfs)
02749 {
02750   bool changed = false;
02751   if (m_part->d->m_selectionStart == m_part->d->m_selectionEnd
02752       && m_part->d->m_startOffset == m_part->d->m_endOffset) {
02753     changed = foldSelectionToCaret(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs);
02754     m_part->d->m_extendAtEnd = true;
02755   } else do {
02756     changed = m_part->d->m_selectionStart.handle() != oldStartSel
02757             || m_part->d->m_startOffset != oldStartOfs
02758         || m_part->d->m_selectionEnd.handle() != oldEndSel
02759         || m_part->d->m_endOffset != oldEndOfs;
02760     if (!changed) break;
02761 
02762     // determine start position -- caret position is always at end.
02763     NodeImpl *startNode;
02764     long startOffset;
02765     if (m_part->d->m_extendAtEnd) {
02766       startNode = m_part->d->m_selectionStart.handle();
02767       startOffset = m_part->d->m_startOffset;
02768     } else {
02769       startNode = m_part->d->m_selectionEnd.handle();
02770       startOffset = m_part->d->m_endOffset;
02771       m_part->d->m_selectionEnd = m_part->d->m_selectionStart;
02772       m_part->d->m_endOffset = m_part->d->m_startOffset;
02773     }/*end if*/
02774 
02775     bool swapNeeded = false;
02776     if (!m_part->d->m_selectionEnd.isNull() && startNode) {
02777       swapNeeded = RangeImpl::compareBoundaryPoints(startNode, startOffset,
02778                 m_part->d->m_selectionEnd.handle(),
02779             m_part->d->m_endOffset) >= 0;
02780     }/*end if*/
02781 
02782     m_part->d->m_selectionStart = startNode;
02783     m_part->d->m_startOffset = startOffset;
02784 
02785     if (swapNeeded) {
02786       m_part->xmlDocImpl()->setSelection(m_part->d->m_selectionEnd.handle(),
02787         m_part->d->m_endOffset, m_part->d->m_selectionStart.handle(),
02788         m_part->d->m_startOffset);
02789     } else {
02790       m_part->xmlDocImpl()->setSelection(m_part->d->m_selectionStart.handle(),
02791         m_part->d->m_startOffset, m_part->d->m_selectionEnd.handle(),
02792         m_part->d->m_endOffset);
02793     }/*end if*/
02794   } while(false);/*end if*/
02795   return changed;
02796 }
02797 
02798 void KHTMLView::updateSelection(NodeImpl *oldStartSel, long oldStartOfs,
02799                 NodeImpl *oldEndSel, long oldEndOfs)
02800 {
02801   if (m_part->d->m_selectionStart == m_part->d->m_selectionEnd
02802       && m_part->d->m_startOffset == m_part->d->m_endOffset) {
02803     if (foldSelectionToCaret(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs)) {
02804       m_part->emitSelectionChanged();
02805     }/*end if*/
02806     m_part->d->m_extendAtEnd = true;
02807   } else {
02808     // check if the extending end has passed the immobile end
02809     if (!m_part->d->m_selectionEnd.isNull() && !m_part->d->m_selectionEnd.isNull()) {
02810       bool swapNeeded = RangeImpl::compareBoundaryPoints(
02811                 m_part->d->m_selectionStart.handle(), m_part->d->m_startOffset,
02812             m_part->d->m_selectionEnd.handle(), m_part->d->m_endOffset) >= 0;
02813       if (swapNeeded) {
02814         DOM::Node tmpNode = m_part->d->m_selectionStart;
02815         long tmpOffset = m_part->d->m_startOffset;
02816         m_part->d->m_selectionStart = m_part->d->m_selectionEnd;
02817         m_part->d->m_startOffset = m_part->d->m_endOffset;
02818         m_part->d->m_selectionEnd = tmpNode;
02819         m_part->d->m_endOffset = tmpOffset;
02820         m_part->d->m_startBeforeEnd = true;
02821         m_part->d->m_extendAtEnd = !m_part->d->m_extendAtEnd;
02822       }/*end if*/
02823     }/*end if*/
02824 
02825     m_part->xmlDocImpl()->setSelection(m_part->d->m_selectionStart.handle(),
02826         m_part->d->m_startOffset, m_part->d->m_selectionEnd.handle(),
02827         m_part->d->m_endOffset);
02828     m_part->emitSelectionChanged();
02829   }/*end if*/
02830 }
02831 
02832 void KHTMLView::caretKeyPressEvent(QKeyEvent *_ke)
02833 {
02834   NodeImpl *oldStartSel = m_part->d->m_selectionStart.handle();
02835   long oldStartOfs = m_part->d->m_startOffset;
02836   NodeImpl *oldEndSel = m_part->d->m_selectionEnd.handle();
02837   long oldEndOfs = m_part->d->m_endOffset;
02838 
02839   NodeImpl *oldCaretNode = m_part->d->caretNode().handle();
02840   long oldOffset = m_part->d->caretOffset();
02841 
02842   bool ctrl = _ke->state() & ControlButton;
02843 
02844 // FIXME: this is that widely indented because I will write ifs around it.
02845       switch(_ke->key()) {
02846         case Key_Space:
02847           break;
02848 
02849         case Key_Down:
02850       moveCaretNextLine(1);
02851           break;
02852 
02853         case Key_Up:
02854       moveCaretPrevLine(1);
02855           break;
02856 
02857         case Key_Left:
02858       moveCaretBy(false, ctrl ? CaretByWord : CaretByCharacter, 1);
02859           break;
02860 
02861         case Key_Right:
02862       moveCaretBy(true, ctrl ? CaretByWord : CaretByCharacter, 1);
02863           break;
02864 
02865         case Key_Next:
02866       moveCaretNextPage();
02867           break;
02868 
02869         case Key_Prior:
02870       moveCaretPrevPage();
02871           break;
02872 
02873         case Key_Home:
02874       if (ctrl)
02875         moveCaretToDocumentBoundary(false);
02876       else
02877         moveCaretToLineBegin();
02878           break;
02879 
02880         case Key_End:
02881       if (ctrl)
02882         moveCaretToDocumentBoundary(true);
02883       else
02884         moveCaretToLineEnd();
02885           break;
02886 
02887       }/*end switch*/
02888 
02889   if ((m_part->d->caretNode().handle() != oldCaretNode
02890     || m_part->d->caretOffset() != oldOffset)
02891     // node should never be null, but faulty conditions may cause it to be
02892     && !m_part->d->caretNode().isNull()) {
02893 
02894     d->m_caretViewContext->caretMoved = true;
02895 
02896     if (_ke->state() & ShiftButton) {   // extend selection
02897       updateSelection(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs);
02898     } else {            // clear any selection
02899       if (foldSelectionToCaret(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs))
02900         m_part->emitSelectionChanged();
02901     }/*end if*/
02902 
02903     m_part->emitCaretPositionChanged(m_part->d->caretNode(), m_part->d->caretOffset());
02904   }/*end if*/
02905 
02906   _ke->accept();
02907 }
02908 
02909 bool KHTMLView::moveCaretTo(NodeImpl *node, long offset, bool clearSel)
02910 {
02911   sanitizeCaretState(node, offset);
02912   if (!node) return false;
02913 
02914   // need to find out the node's inline box. If there is none, this function
02915   // will snap to the next node that has one. This is necessary to make the
02916   // caret visible in any case.
02917   RenderArena arena;
02918   RenderFlow *cb;
02919   InlineBox *box = 0;
02920   findFlowBox(node, offset, &arena, cb, &box);
02921   if (box && box->object() != node->renderer()) {
02922     if (box->object()->element()) {
02923       node = box->object()->element();
02924       offset = node->minOffset();
02925 #if DEBUG_CARETMODE > 1
02926       kdDebug(6200) << "set new node " << node->nodeName().string() << "@" << node << endl;
02927 #endif
02928     } else {    // box has no associated element -> do not use
02929       // this case should actually never happen.
02930       box = 0;
02931       kdError(6200) << "Box contains no node! Crash imminent" << endl;
02932     }/*end if*/
02933   }
02934 
02935   NodeImpl *oldStartSel = m_part->d->m_selectionStart.handle();
02936   long oldStartOfs = m_part->d->m_startOffset;
02937   NodeImpl *oldEndSel = m_part->d->m_selectionEnd.handle();
02938   long oldEndOfs = m_part->d->m_endOffset;
02939 
02940   // test for position change
02941   bool posChanged = m_part->d->caretNode().handle() != node
02942         || m_part->d->caretOffset() != offset;
02943   bool selChanged = false;
02944 
02945   m_part->d->caretNode() = node;
02946   m_part->d->caretOffset() = offset;
02947   if (clearSel || !oldStartSel || !oldEndSel) {
02948     selChanged = foldSelectionToCaret(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs);
02949   } else {
02950     //kdDebug(6200) << "moveToCaret: extendSelection: m_extendAtEnd " << m_part->d->m_extendAtEnd << endl;
02951     //kdDebug(6200) << "selection: start(" << m_part->d->m_selectionStart.handle() << "," << m_part->d->m_startOffset << "), end(" << m_part->d->m_selectionEnd.handle() << "," << m_part->d->m_endOffset << "), caret(" << m_part->d->caretNode().handle() << "," << m_part->d->caretOffset() << ")" << endl;
02952     selChanged = extendSelection(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs);
02953     //kdDebug(6200) << "after extendSelection: m_extendAtEnd " << m_part->d->m_extendAtEnd << endl;
02954     //kdDebug(6200) << "selection: start(" << m_part->d->m_selectionStart.handle() << "," << m_part->d->m_startOffset << "), end(" << m_part->d->m_selectionEnd.handle() << "," << m_part->d->m_endOffset << "), caret(" << m_part->d->caretNode().handle() << "," << m_part->d->caretOffset() << ")" << endl;
02955   }/*end if*/
02956 
02957   d->caretViewContext()->caretMoved = true;
02958 
02959   bool visible_caret = placeCaret(box);
02960 
02961   // FIXME: if the old position was !visible_caret, and the new position is
02962   // also, then two caretPositionChanged signals with a null Node are
02963   // emitted in series.
02964   if (posChanged) {
02965     m_part->emitCaretPositionChanged(visible_caret ? node : 0, offset);
02966   }/*end if*/
02967 
02968   return selChanged;
02969 }
02970 
02971 void KHTMLView::moveCaretByLine(bool next, int count)
02972 {
02973   // FIXME: what if the node is removed whilst we access it?
02974   // Current solution: bail out
02975   Node &caretNodeRef = m_part->d->caretNode();
02976   if (caretNodeRef.isNull()) return;
02977 
02978   NodeImpl *caretNode = caretNodeRef.handle();
02979 //  kdDebug(6200) << ": caretNode=" << caretNode << endl;
02980   long offset = m_part->d->caretOffset();
02981 
02982   CaretViewContext *cv = d->caretViewContext();
02983 
02984   LinearDocument ld(m_part, caretNode, offset);
02985 
02986   ErgonomicEditableLineIterator it(ld.current(), cv->origX);
02987 
02988   // move count lines vertically
02989   while (count > 0 && it != ld.end() && it != ld.preBegin()) {
02990     count--;
02991     if (next) ++it; else --it;
02992   }/*wend*/
02993 
02994   // Nothing? Then leave everything as is.
02995   if (it == ld.end() || it == ld.preBegin()) return;
02996 
02997   int x, absx, absy;
02998   InlineBox *caretBox = nearestInlineBox(it, d->m_caretViewContext, x, absx, absy);
02999 
03000   placeCaretOnLine(caretBox, x, absx, absy);
03001 }
03002 
03003 void KHTMLView::placeCaretOnLine(InlineBox *caretBox, int x, int absx, int absy)
03004 {
03005   // paranoia sanity check
03006   if (!caretBox) return;
03007 
03008   RenderObject *caretRender = caretBox->object();
03009   NodeImpl *caretNode = caretRender->element();
03010 
03011 #if DEBUG_CARETMODE > 0
03012   kdDebug(6200) << "got valid caretBox " << caretBox << endl;
03013   kdDebug(6200) << "xPos: " << caretBox->xPos() << " yPos: " << caretBox->yPos()
03014         << " width: " << caretBox->width() << " height: " << caretBox->height() << endl;
03015   if (caretBox->isInlineTextBox()) { kdDebug(6200) << "contains \"" << QString(((RenderText *)((InlineTextBox *)caretBox)->object())->str->s + ((InlineTextBox *)caretBox)->m_start, ((InlineTextBox *)caretBox)->m_len) << "\"" << endl;}
03016 #endif
03017   // inquire height of caret
03018   int caretHeight = caretBox->height();
03019   bool isText = caretBox->isInlineTextBox();
03020   int yOfs = 0;     // y-offset for text nodes
03021   if (isText) {
03022     // text boxes need extrawurst
03023     RenderText *t = static_cast<RenderText *>(caretRender);
03024     const QFontMetrics &fm = t->metrics(caretBox->m_firstLine);
03025     caretHeight = fm.height();
03026     yOfs = caretBox->baseline() - fm.ascent();
03027   }/*end if*/
03028 
03029   caretOff();
03030 
03031   // set new caret node
03032   m_part->d->caretNode() = caretNode;
03033   long &offset = m_part->d->caretOffset();
03034 
03035   // set all variables not needing special treatment
03036   d->m_caretViewContext->y = caretBox->yPos() + yOfs;
03037   d->m_caretViewContext->height = caretHeight;
03038   d->m_caretViewContext->width = 1; // FIXME: regard override
03039 
03040   int xPos = caretBox->xPos();
03041   int caretBoxWidth = caretBox->width();
03042 
03043   // before or at beginning of inline box -> place at beginning
03044   if (x <= xPos) {
03045     d->m_caretViewContext->x = xPos;
03046     offset = caretBox->minOffset();
03047   // somewhere within this block
03048   } else if (x > xPos && x <= xPos + caretBoxWidth) {
03049     if (isText) { // find out where exactly
03050       offset = static_cast<InlineTextBox *>(caretBox)->offsetForPoint(x,
03051             d->m_caretViewContext->x);
03052 #if DEBUG_CARETMODE > 2
03053       kdDebug(6200) << "deviation from origX " << d->m_caretViewContext->x - x << endl;
03054 #endif
03055     } else {    // snap to nearest end
03056       if (xPos + caretBoxWidth - x < x - xPos) {
03057         d->m_caretViewContext->x = xPos + caretBoxWidth;
03058         offset = caretNode ? caretNode->maxOffset() : 1;
03059       } else {
03060         d->m_caretViewContext->x = xPos;
03061         offset = caretNode ? caretNode->minOffset() : 0;
03062       }/*end if*/
03063     }/*end if*/
03064   } else {      // after the inline box -> place at end
03065     d->m_caretViewContext->x = xPos + caretBoxWidth;
03066     offset = caretBox->maxOffset();
03067   }/*end if*/
03068 #if DEBUG_CARETMODE > 0
03069       kdDebug(6200) << "new offset: " << offset << endl;
03070 #endif
03071 
03072   d->m_caretViewContext->x += absx;
03073   d->m_caretViewContext->y += absy;
03074 
03075   ensureVisible(d->m_caretViewContext->x, d->m_caretViewContext->y,
03076     d->m_caretViewContext->width, d->m_caretViewContext->height);
03077   d->scrollBarMoved = false;
03078 
03079   ensureNodeHasFocus(caretNode);
03080   caretOn();
03081 }
03082 
03083 void KHTMLView::moveCaretToLineBoundary(bool end)
03084 {
03085   // FIXME: what if the node is removed whilst we access it?
03086   // Current solution: bail out
03087   Node &caretNodeRef = m_part->d->caretNode();
03088   if (caretNodeRef.isNull()) return;
03089 
03090   NodeImpl *caretNode = caretNodeRef.handle();
03091 //  kdDebug(6200) << ": caretNode=" << caretNode << endl;
03092   long offset = m_part->d->caretOffset();
03093 
03094   LinearDocument ld(m_part, caretNode, offset);
03095 
03096   EditableLineIterator it = ld.current();
03097   if (it == ld.end()) return;   // should not happen, but who knows
03098 
03099   EditableInlineBoxIterator fbit(it, end);
03100   InlineBox *b = *fbit;
03101   Q_ASSERT(b);
03102 
03103   RenderObject *cb = (*it)->object();
03104   int absx, absy;
03105 
03106   if (cb) cb->absolutePosition(absx,absy);
03107   else absx = absy = 0;
03108 
03109   int x = b->xPos() + (end ? b->width() : 0);
03110   d->m_caretViewContext->origX = absx + x;
03111   placeCaretOnLine(b, x, absx, absy);
03112 }
03113 
03114 void KHTMLView::moveCaretToDocumentBoundary(bool end)
03115 {
03116   // FIXME: what if the node is removed whilst we access it?
03117   // Current solution: bail out
03118   Node &caretNodeRef = m_part->d->caretNode();
03119   if (caretNodeRef.isNull()) return;
03120 
03121   NodeImpl *caretNode = caretNodeRef.handle();
03122 //  kdDebug(6200) << ": caretNode=" << caretNode << endl;
03123   long offset = m_part->d->caretOffset();
03124 
03125   LinearDocument ld(m_part, caretNode, offset);
03126 
03127   EditableLineIterator it(end ? ld.preEnd() : ld.begin(), end);
03128   if (it == ld.end() || it == ld.preBegin()) return;    // should not happen, but who knows
03129 
03130   EditableInlineBoxIterator fbit = it;
03131   InlineBox *b = *fbit;
03132   Q_ASSERT(b);
03133 
03134   RenderObject *cb = (*it)->object();
03135   int absx, absy;
03136 
03137   if (cb) cb->absolutePosition(absx, absy);
03138   else absx = absy = 0;
03139 
03140   int x = b->xPos()/* + (end ? b->width() : 0) reactivate for rtl*/;
03141   d->m_caretViewContext->origX = absx + x;
03142   placeCaretOnLine(b, x, absx, absy);
03143 }
03144 
03145 void KHTMLView::moveCaretBy(bool next, CaretMovement cmv, int count)
03146 {
03147   if (!m_part) return;
03148   // FIXME: what if the node is removed whilst we access it?
03149   // Current solution: bail out
03150   Node &caretNodeRef = m_part->d->caretNode();
03151   if (caretNodeRef.isNull()) return;
03152 
03153   NodeImpl *caretNode = caretNodeRef.handle();
03154 //  kdDebug(6200) << ": caretNode=" << caretNode << endl;
03155   long &offset = m_part->d->caretOffset();
03156 
03157   LinearDocument ld(m_part, caretNode, offset);
03158 
03159   EditableCharacterIterator it(&ld);
03160   InlineBox *hintBox = it.box();
03161   while (it.node() && count > 0) {
03162     count--;
03163     if (cmv == CaretByCharacter) {
03164       if (next) ++it;
03165       else --it;
03166     } else if (cmv == CaretByWord) {
03167       if (next) moveItToNextWord(it);
03168       else moveItToPrevWord(it);
03169     }/*end if*/
03170   }/*wend*/
03171   if (it.node()) {
03172     caretNodeRef = it.node();
03173     offset = it.offset();
03174     hintBox = it.box();
03175 #if DEBUG_CARETMODE > 2
03176     kdDebug(6200) << "set by valid node. offset: " << offset << endl;
03177 #endif
03178   } else {
03179     offset = next ? caretNode->maxOffset() : caretNode->minOffset();
03180 #if DEBUG_CARETMODE > 0
03181     kdDebug(6200) << "set by INvalid node. offset: " << offset << endl;
03182 #endif
03183   }/*end if*/
03184   placeCaretOnChar(hintBox);
03185 }
03186 
03187 void KHTMLView::placeCaretOnChar(InlineBox *hintBox)
03188 {
03189   caretOff();
03190   recalcAndStoreCaretPos(hintBox);
03191   ensureVisible(d->m_caretViewContext->x, d->m_caretViewContext->y,
03192     d->m_caretViewContext->width, d->m_caretViewContext->height);
03193   d->m_caretViewContext->origX = d->m_caretViewContext->x;
03194   d->scrollBarMoved = false;
03195 #if DEBUG_CARETMODE > 3
03196   //if (caretNode->isTextNode())  kdDebug(6200) << "text[0] = " << (int)*((TextImpl *)caretNode)->data().unicode() << " text :\"" << ((TextImpl *)caretNode)->data().string() << "\"" << endl;
03197 #endif
03198   ensureNodeHasFocus(m_part->d->caretNode().handle());
03199   caretOn();
03200 }
03201 
03202 void KHTMLView::moveCaretByPage(bool next)
03203 {
03204   // FIXME: what if the node is removed whilst we access it?
03205   // Current solution: bail out
03206   Node &caretNodeRef = m_part->d->caretNode();
03207   if (caretNodeRef.isNull()) return;
03208 
03209   NodeImpl *caretNode = caretNodeRef.handle();
03210 //  kdDebug(6200) << ": caretNode=" << caretNode << endl;
03211   long offset = m_part->d->caretOffset();
03212 
03213   int offs = (clipper()->height() < 30) ? clipper()->height() : 30;
03214   // Minimum distance the caret must be moved
03215   int mindist = clipper()->height() - offs;
03216 
03217   CaretViewContext *cv = d->caretViewContext();
03218 //  int y = cv->y;      // we always measure the top border
03219 
03220   LinearDocument ld(m_part, caretNode, offset);
03221 
03222   ErgonomicEditableLineIterator it(ld.current(), cv->origX);
03223 
03224   moveIteratorByPage(ld, it, mindist, next);
03225 
03226   int x, absx, absy;
03227   InlineBox *caretBox = nearestInlineBox(it, d->m_caretViewContext, x, absx, absy);
03228 
03229   placeCaretOnLine(caretBox, x, absx, absy);
03230 }
03231 
03232 void KHTMLView::moveCaretPrevWord()
03233 {
03234   moveCaretBy(false, CaretByWord, 1);
03235 }
03236 
03237 void KHTMLView::moveCaretNextWord()
03238 {
03239   moveCaretBy(true, CaretByWord, 1);
03240 }
03241 
03242 void KHTMLView::moveCaretPrevLine(int n)
03243 {
03244   moveCaretByLine(false, n);
03245 }
03246 
03247 void KHTMLView::moveCaretNextLine(int n)
03248 {
03249   moveCaretByLine(true, n);
03250 }
03251 
03252 void KHTMLView::moveCaretPrevPage()
03253 {
03254   moveCaretByPage(false);
03255 }
03256 
03257 void KHTMLView::moveCaretNextPage()
03258 {
03259   moveCaretByPage(true);
03260 }
03261 
03262 void KHTMLView::moveCaretToLineBegin()
03263 {
03264   moveCaretToLineBoundary(false);
03265 }
03266 
03267 void KHTMLView::moveCaretToLineEnd()
03268 {
03269   moveCaretToLineBoundary(true);
03270 }
03271 
03272 #endif // KHTML_NO_CARET
03273 
03274 #undef DEBUG_CARETMODE
KDE Logo
This file is part of the documentation for khtml Library Version 3.2.3.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Aug 4 05:28:56 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2003