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     if (!scrollTo(node->getRect()))
01659     return true;
01660 
01661     Node guard( node );
01662     if( node->isSelectable()) {
01663         // Set focus node on the document
01664         m_part->xmlDocImpl()->setFocusNode(node);
01665         if( node != NULL && node->hasOneRef()) // deleted, only held by guard
01666             return true;
01667         emit m_part->nodeActivated(Node(node));
01668         if( node != NULL && node->hasOneRef())
01669             return true;
01670     }
01671     switch( node->id()) {
01672         case ID_A:
01673             static_cast< HTMLAnchorElementImpl* >( node )->click();
01674           break;
01675         case ID_INPUT:
01676             static_cast< HTMLInputElementImpl* >( node )->click();
01677           break;
01678         case ID_BUTTON:
01679             static_cast< HTMLButtonElementImpl* >( node )->click();
01680           break;
01681         case ID_AREA:
01682             static_cast< HTMLAreaElementImpl* >( node )->click();
01683           break;
01684         case ID_LABEL:
01685             // TODO should be click(), after it works for label
01686           break;
01687         case ID_TEXTAREA:
01688           break; // just focusing it is enough
01689         case ID_LEGEND:
01690             // TODO
01691           break;
01692     }
01693     return true;
01694 }
01695 
01696 void KHTMLView::setMediaType( const QString &medium )
01697 {
01698     m_medium = medium;
01699 }
01700 
01701 QString KHTMLView::mediaType() const
01702 {
01703     return m_medium;
01704 }
01705 
01706 void KHTMLView::setWidgetVisible(RenderWidget* w, bool vis)
01707 {
01708     if (vis) {
01709         d->visibleWidgets.replace(w, w->widget());
01710     }
01711     else
01712         d->visibleWidgets.remove(w);
01713 }
01714 
01715 void KHTMLView::print()
01716 {
01717     print( false );
01718 }
01719 
01720 void KHTMLView::print(bool quick)
01721 {
01722     if(!m_part->xmlDocImpl()) return;
01723     khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer());
01724     if(!root) return;
01725 
01726     // this only works on Unix - we assume 72dpi
01727     KPrinter *printer = new KPrinter(true, QPrinter::PrinterResolution);
01728     printer->addDialogPage(new KHTMLPrintSettings());
01729     QString docname = m_part->xmlDocImpl()->URL().prettyURL();
01730     if ( !docname.isEmpty() )
01731         docname = KStringHandler::csqueeze(docname, 80);
01732     if(quick || printer->setup(this, i18n("Print %1").arg(docname))) {
01733         viewport()->setCursor( waitCursor ); // only viewport(), no QApplication::, otherwise we get the busy cursor in kdeprint's dialogs
01734         // set up KPrinter
01735         printer->setFullPage(false);
01736         printer->setCreator(QString("KDE %1.%2.%3 HTML Library").arg(KDE_VERSION_MAJOR).arg(KDE_VERSION_MINOR).arg(KDE_VERSION_RELEASE));
01737         printer->setDocName(docname);
01738 
01739         QPainter *p = new QPainter;
01740         p->begin( printer );
01741         khtml::setPrintPainter( p );
01742 
01743         m_part->xmlDocImpl()->setPaintDevice( printer );
01744         QString oldMediaType = mediaType();
01745         setMediaType( "print" );
01746         // We ignore margin settings for html and body when printing
01747         // and use the default margins from the print-system
01748         // (In Qt 3.0.x the default margins are hardcoded in Qt)
01749         m_part->xmlDocImpl()->setPrintStyleSheet( printer->option("app-khtml-printfriendly") == "true" ?
01750                                                   "* { background-image: none !important;"
01751                                                   "    background-color: white !important;"
01752                                                   "    color: black !important; }"
01753                           "body { margin: 0px !important; }"
01754                           "html { margin: 0px !important; }" :
01755                           "body { margin: 0px !important; }"
01756                           "html { margin: 0px !important; }"
01757                           );
01758 
01759         QPaintDeviceMetrics metrics( printer );
01760 
01761         // this is a simple approximation... we layout the document
01762         // according to the width of the page, then just cut
01763         // pages without caring about the content. We should do better
01764         // in the future, but for the moment this is better than no
01765         // printing support
01766         kdDebug(6000) << "printing: physical page width = " << metrics.width()
01767                       << " height = " << metrics.height() << endl;
01768         root->setPrintingMode(true);
01769         root->setWidth(metrics.width());
01770 
01771         m_part->xmlDocImpl()->styleSelector()->computeFontSizes(&metrics, 100);
01772         m_part->xmlDocImpl()->updateStyleSelector();
01773         root->setPrintImages( printer->option("app-khtml-printimages") == "true");
01774         root->setMinMaxKnown( false );
01775         root->setLayouted( false );
01776         root->layout();
01777 
01778         bool printHeader = (printer->option("app-khtml-printheader") == "true");
01779 
01780         int headerHeight = 0;
01781         QFont headerFont("helvetica", 8);
01782 
01783         QString headerLeft = KGlobal::locale()->formatDate(QDate::currentDate(),true);
01784         QString headerMid = docname;
01785         QString headerRight;
01786 
01787         if (printHeader)
01788         {
01789            p->setFont(headerFont);
01790            headerHeight = (p->fontMetrics().lineSpacing() * 3) / 2;
01791         }
01792 
01793         // ok. now print the pages.
01794         kdDebug(6000) << "printing: html page width = " << root->docWidth()
01795                       << " height = " << root->docHeight() << endl;
01796         kdDebug(6000) << "printing: margins left = " << printer->margins().width()
01797                       << " top = " << printer->margins().height() << endl;
01798         kdDebug(6000) << "printing: paper width = " << metrics.width()
01799                       << " height = " << metrics.height() << endl;
01800         // if the width is too large to fit on the paper we just scale
01801         // the whole thing.
01802         int pageHeight = metrics.height();
01803         int pageWidth = metrics.width();
01804         p->setClipRect(0,0, pageWidth, pageHeight);
01805 
01806         pageHeight -= headerHeight;
01807 
01808         bool scalePage = false;
01809         double scale = 0.0;
01810 #ifndef QT_NO_TRANSFORMATIONS
01811         if(root->docWidth() > metrics.width()) {
01812             scalePage = true;
01813             scale = ((double) metrics.width())/((double) root->docWidth());
01814             pageHeight = (int) (pageHeight/scale);
01815             pageWidth = (int) (pageWidth/scale);
01816             headerHeight = (int) (headerHeight/scale);
01817         }
01818 #endif
01819         kdDebug(6000) << "printing: scaled html width = " << pageWidth
01820                       << " height = " << pageHeight << endl;
01821 
01822         // Squeeze header to make it it on the page.
01823         if (printHeader)
01824         {
01825             int available_width = metrics.width() - 10 -
01826                 2 * kMax(p->boundingRect(0, 0, metrics.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerLeft).width(),
01827                          p->boundingRect(0, 0, metrics.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerRight).width());
01828             if (available_width < 150)
01829                available_width = 150;
01830             int mid_width;
01831             int squeeze = 120;
01832             do {
01833                 headerMid = KStringHandler::csqueeze(docname, squeeze);
01834                 mid_width = p->boundingRect(0, 0, metrics.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerMid).width();
01835                 squeeze -= 10;
01836             } while (mid_width > available_width);
01837         }
01838 
01839         int top = 0;
01840         int page = 1;
01841         while(top < root->docHeight()) {
01842             if(top > 0) printer->newPage();
01843             if (printHeader)
01844             {
01845                 int dy = p->fontMetrics().lineSpacing();
01846                 p->setPen(Qt::black);
01847                 p->setFont(headerFont);
01848 
01849                 headerRight = QString("#%1").arg(page);
01850 
01851                 p->drawText(0, 0, metrics.width(), dy, Qt::AlignLeft, headerLeft);
01852                 p->drawText(0, 0, metrics.width(), dy, Qt::AlignHCenter, headerMid);
01853                 p->drawText(0, 0, metrics.width(), dy, Qt::AlignRight, headerRight);
01854             }
01855 
01856 #ifndef QT_NO_TRANSFORMATIONS
01857             if (scalePage)
01858                 p->scale(scale, scale);
01859 #endif
01860             p->translate(0, headerHeight-top);
01861 
01862             root->setTruncatedAt(top+pageHeight);
01863 
01864             root->layer()->paint(p, QRect(0, top, pageWidth, pageHeight));
01865             if (top + pageHeight >= root->docHeight())
01866                 break; // Stop if we have printed everything
01867 
01868             top = root->truncatedAt();
01869             p->resetXForm();
01870             page++;
01871         }
01872 
01873         p->end();
01874         delete p;
01875 
01876         // and now reset the layout to the usual one...
01877         root->setPrintingMode(false);
01878         khtml::setPrintPainter( 0 );
01879         setMediaType( oldMediaType );
01880         m_part->xmlDocImpl()->setPaintDevice( this );
01881         m_part->xmlDocImpl()->styleSelector()->computeFontSizes(m_part->xmlDocImpl()->paintDeviceMetrics(), m_part->zoomFactor());
01882         m_part->xmlDocImpl()->updateStyleSelector();
01883         viewport()->unsetCursor();
01884     }
01885     delete printer;
01886 }
01887 
01888 void KHTMLView::slotPaletteChanged()
01889 {
01890     if(!m_part->xmlDocImpl()) return;
01891     DOM::DocumentImpl *document = m_part->xmlDocImpl();
01892     if (!document->isHTMLDocument()) return;
01893     khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(document->renderer());
01894     if(!root) return;
01895     root->style()->resetPalette();
01896     NodeImpl *body = static_cast<HTMLDocumentImpl*>(document)->body();
01897     if(!body) return;
01898     body->setChanged(true);
01899     body->recalcStyle( NodeImpl::Force );
01900 }
01901 
01902 void KHTMLView::paint(QPainter *p, const QRect &rc, int yOff, bool *more)
01903 {
01904     if(!m_part->xmlDocImpl()) return;
01905     khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer());
01906     if(!root) return;
01907 
01908     m_part->xmlDocImpl()->setPaintDevice(p->device());
01909     root->setPrintingMode(true);
01910     root->setWidth(rc.width());
01911 
01912     p->save();
01913     p->setClipRect(rc);
01914     p->translate(rc.left(), rc.top());
01915     double scale = ((double) rc.width()/(double) root->docWidth());
01916     int height = (int) ((double) rc.height() / scale);
01917 #ifndef QT_NO_TRANSFORMATIONS
01918     p->scale(scale, scale);
01919 #endif
01920 
01921     root->layer()->paint(p, QRect(0, yOff, root->docWidth(), height));
01922     if (more)
01923         *more = yOff + height < root->docHeight();
01924     p->restore();
01925 
01926     root->setPrintingMode(false);
01927     m_part->xmlDocImpl()->setPaintDevice( this );
01928 }
01929 
01930 
01931 void KHTMLView::useSlowRepaints()
01932 {
01933     d->useSlowRepaints = true;
01934     setStaticBackground(true);
01935 }
01936 
01937 
01938 void KHTMLView::setVScrollBarMode ( ScrollBarMode mode )
01939 {
01940 #ifndef KHTML_NO_SCROLLBARS
01941     d->vmode = mode;
01942     QScrollView::setVScrollBarMode(mode);
01943 #else
01944     Q_UNUSED( mode );
01945 #endif
01946 }
01947 
01948 void KHTMLView::setHScrollBarMode ( ScrollBarMode mode )
01949 {
01950 #ifndef KHTML_NO_SCROLLBARS
01951     d->hmode = mode;
01952     QScrollView::setHScrollBarMode(mode);
01953 #else
01954     Q_UNUSED( mode );
01955 #endif
01956 }
01957 
01958 void KHTMLView::restoreScrollBar()
01959 {
01960     int ow = visibleWidth();
01961     QScrollView::setVScrollBarMode(d->vmode);
01962     if (visibleWidth() != ow)
01963         layout();
01964     d->prevScrollbarVisible = verticalScrollBar()->isVisible();
01965 }
01966 
01967 QStringList KHTMLView::formCompletionItems(const QString &name) const
01968 {
01969     if (!m_part->settings()->isFormCompletionEnabled())
01970         return QStringList();
01971     if (!d->formCompletions)
01972         d->formCompletions = new KSimpleConfig(locateLocal("data", "khtml/formcompletions"));
01973     return d->formCompletions->readListEntry(name);
01974 }
01975 
01976 void KHTMLView::clearCompletionHistory(const QString& name)
01977 {
01978     if (!d->formCompletions)
01979     {
01980         d->formCompletions = new KSimpleConfig(locateLocal("data", "khtml/formcompletions"));
01981     }
01982     d->formCompletions->writeEntry(name, "");
01983     d->formCompletions->sync();
01984 }
01985 
01986 void KHTMLView::addFormCompletionItem(const QString &name, const QString &value)
01987 {
01988     if (!m_part->settings()->isFormCompletionEnabled())
01989         return;
01990     // don't store values that are all numbers or just numbers with
01991     // dashes or spaces as those are likely credit card numbers or
01992     // something similar
01993     bool cc_number(true);
01994     for (unsigned int i = 0; i < value.length(); ++i)
01995     {
01996       QChar c(value[i]);
01997       if (!c.isNumber() && c != '-' && !c.isSpace())
01998       {
01999         cc_number = false;
02000         break;
02001       }
02002     }
02003     if (cc_number)
02004       return;
02005     QStringList items = formCompletionItems(name);
02006     if (!items.contains(value))
02007         items.prepend(value);
02008     while ((int)items.count() > m_part->settings()->maxFormCompletionItems())
02009         items.remove(items.fromLast());
02010     d->formCompletions->writeEntry(name, items);
02011 }
02012 
02013 void KHTMLView::addNonPasswordStorableSite(const QString& host)
02014 {
02015     if (!d->formCompletions) {
02016         d->formCompletions = new KSimpleConfig(locateLocal("data", "khtml/formcompletions"));
02017     }
02018 
02019     d->formCompletions->setGroup("NonPasswordStorableSites");
02020     QStringList sites = d->formCompletions->readListEntry("Sites");
02021     sites.append(host);
02022     d->formCompletions->writeEntry("Sites", sites);
02023     d->formCompletions->sync();
02024     d->formCompletions->setGroup(QString::null);//reset
02025 }
02026 
02027 bool KHTMLView::nonPasswordStorableSite(const QString& host) const
02028 {
02029     if (!d->formCompletions) {
02030         d->formCompletions = new KSimpleConfig(locateLocal("data", "khtml/formcompletions"));
02031     }
02032     d->formCompletions->setGroup("NonPasswordStorableSites");
02033     QStringList sites =  d->formCompletions->readListEntry("Sites");
02034     d->formCompletions->setGroup(QString::null);//reset
02035 
02036     return (sites.find(host) != sites.end());
02037 }
02038 
02039 // returns true if event should be swallowed
02040 bool KHTMLView::dispatchMouseEvent(int eventId, DOM::NodeImpl *targetNode, bool cancelable,
02041                    int detail,QMouseEvent *_mouse, bool setUnder,
02042                    int mouseEventType)
02043 {
02044     if (d->underMouse)
02045     d->underMouse->deref();
02046     d->underMouse = targetNode;
02047     if (d->underMouse)
02048     d->underMouse->ref();
02049 
02050     int exceptioncode = 0;
02051     int pageX = 0;
02052     int pageY = 0;
02053     viewportToContents(_mouse->x(), _mouse->y(), pageX, pageY);
02054     int clientX = pageX - contentsX();
02055     int clientY = pageY - contentsY();
02056     int screenX = _mouse->globalX();
02057     int screenY = _mouse->globalY();
02058     int button = -1;
02059     switch (_mouse->button()) {
02060     case LeftButton:
02061         button = 0;
02062         break;
02063     case MidButton:
02064         button = 1;
02065         break;
02066     case RightButton:
02067         button = 2;
02068         break;
02069     default:
02070         break;
02071     }
02072     bool ctrlKey = (_mouse->state() & ControlButton);
02073     bool altKey = (_mouse->state() & AltButton);
02074     bool shiftKey = (_mouse->state() & ShiftButton);
02075     bool metaKey = (_mouse->state() & MetaButton);
02076 
02077     // mouseout/mouseover
02078     if (setUnder && (d->prevMouseX != pageX || d->prevMouseY != pageY)) {
02079 
02080         // ### this code sucks. we should save the oldUnder instead of calculating
02081         // it again. calculating is expensive! (Dirk)
02082         NodeImpl *oldUnder = 0;
02083     if (d->prevMouseX >= 0 && d->prevMouseY >= 0) {
02084         NodeImpl::MouseEvent mev( _mouse->stateAfter(), static_cast<NodeImpl::MouseEventType>(mouseEventType));
02085         m_part->xmlDocImpl()->prepareMouseEvent( true, d->prevMouseX, d->prevMouseY, &mev );
02086         oldUnder = mev.innerNode.handle();
02087     }
02088 //  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());
02089     if (oldUnder != targetNode) {
02090         // send mouseout event to the old node
02091         if (oldUnder){
02092         oldUnder->ref();
02093         MouseEventImpl *me = new MouseEventImpl(EventImpl::MOUSEOUT_EVENT,
02094                             true,true,m_part->xmlDocImpl()->defaultView(),
02095                             0,screenX,screenY,clientX,clientY,pageX, pageY,
02096                             ctrlKey,altKey,shiftKey,metaKey,
02097                             button,targetNode);
02098         me->ref();
02099         oldUnder->dispatchEvent(me,exceptioncode,true);
02100         me->deref();
02101         }
02102 
02103         // send mouseover event to the new node
02104         if (targetNode) {
02105         MouseEventImpl *me = new MouseEventImpl(EventImpl::MOUSEOVER_EVENT,
02106                             true,true,m_part->xmlDocImpl()->defaultView(),
02107                             0,screenX,screenY,clientX,clientY,pageX, pageY,
02108                             ctrlKey,altKey,shiftKey,metaKey,
02109                             button,oldUnder);
02110 
02111         me->ref();
02112         targetNode->dispatchEvent(me,exceptioncode,true);
02113         me->deref();
02114         }
02115 
02116             if (oldUnder)
02117                 oldUnder->deref();
02118         }
02119     }
02120 
02121     bool swallowEvent = false;
02122 
02123     if (targetNode) {
02124         // send the actual event
02125         bool dblclick = ( eventId == EventImpl::CLICK_EVENT &&
02126                           _mouse->type() == QEvent::MouseButtonDblClick );
02127         MouseEventImpl *me = new MouseEventImpl(static_cast<EventImpl::EventId>(eventId),
02128                         true,cancelable,m_part->xmlDocImpl()->defaultView(),
02129                         detail,screenX,screenY,clientX,clientY,pageX, pageY,
02130                         ctrlKey,altKey,shiftKey,metaKey,
02131                         button,0, _mouse, dblclick );
02132         me->ref();
02133         targetNode->dispatchEvent(me,exceptioncode,true);
02134         if (me->defaultHandled() || me->defaultPrevented())
02135             swallowEvent = true;
02136         me->deref();
02137 
02138         if (eventId == EventImpl::MOUSEDOWN_EVENT) {
02139             if (targetNode->isSelectable())
02140                 m_part->xmlDocImpl()->setFocusNode(targetNode);
02141             else
02142                 m_part->xmlDocImpl()->setFocusNode(0);
02143         }
02144     }
02145 
02146     return swallowEvent;
02147 }
02148 
02149 void KHTMLView::setIgnoreWheelEvents( bool e )
02150 {
02151     d->ignoreWheelEvents = e;
02152 }
02153 
02154 #ifndef QT_NO_WHEELEVENT
02155 
02156 void KHTMLView::viewportWheelEvent(QWheelEvent* e)
02157 {
02158     if ( ( e->state() & ControlButton) == ControlButton )
02159     {
02160         emit zoomView( - e->delta() );
02161         e->accept();
02162     }
02163     else if ( ( (d->ignoreWheelEvents && !verticalScrollBar()->isVisible())
02164                 || e->delta() > 0 && contentsY() <= 0
02165                         || e->delta() < 0 && contentsY() >= contentsHeight() - visibleHeight())
02166                 && m_part->parentPart() ) {
02167        kdDebug(6000) << this << " cz " << contentsY() << " ch " << contentsHeight() << " vh " << visibleHeight() << endl;
02168         if ( m_part->parentPart()->view() )
02169             m_part->parentPart()->view()->wheelEvent( e );
02170         kdDebug(6000) << "sent" << endl;
02171         e->ignore();
02172     }
02173     else if ( d->vmode == QScrollView::AlwaysOff ) {
02174         e->accept();
02175     }
02176     else {
02177         d->scrollBarMoved = true;
02178         QScrollView::viewportWheelEvent( e );
02179 
02180         QMouseEvent *tempEvent = new QMouseEvent( QEvent::MouseMove, QPoint(-1,-1), QPoint(-1,-1), Qt::NoButton, e->state() );
02181         emit viewportMouseMoveEvent ( tempEvent );
02182         delete tempEvent;
02183     }
02184 
02185 }
02186 #endif
02187 
02188 void KHTMLView::dragEnterEvent( QDragEnterEvent* ev )
02189 {
02190     // Handle drops onto frames (#16820)
02191     // Drops on the main html part is handled by Konqueror (and shouldn't do anything
02192     // in e.g. kmail, so not handled here).
02193     if ( m_part->parentPart() )
02194     {
02195         QApplication::sendEvent(m_part->parentPart()->widget(), ev);
02196     return;
02197     }
02198     QScrollView::dragEnterEvent( ev );
02199 }
02200 
02201 void KHTMLView::dropEvent( QDropEvent *ev )
02202 {
02203     // Handle drops onto frames (#16820)
02204     // Drops on the main html part is handled by Konqueror (and shouldn't do anything
02205     // in e.g. kmail, so not handled here).
02206     if ( m_part->parentPart() )
02207     {
02208         QApplication::sendEvent(m_part->parentPart()->widget(), ev);
02209     return;
02210     }
02211     QScrollView::dropEvent( ev );
02212 }
02213 
02214 void KHTMLView::focusInEvent( QFocusEvent *e )
02215 {
02216 #ifndef KHTML_NO_CARET
02217     // Restart blink frequency timer if it has been killed, but only on
02218     // editable nodes
02219     if (d->m_caretViewContext &&
02220         d->m_caretViewContext->freqTimerId == -1 &&
02221         m_part->xmlDocImpl()) {
02222         NodeImpl *caretNode = m_part->xmlDocImpl()->focusNode();
02223         if (m_part->isCaretMode()
02224         || m_part->isEditable()
02225             || (caretNode && caretNode->renderer()
02226             && caretNode->renderer()->style()->userInput()
02227                 == UI_ENABLED)) {
02228             d->m_caretViewContext->freqTimerId = startTimer(500);
02229         d->m_caretViewContext->visible = true;
02230         }/*end if*/
02231     }/*end if*/
02232     showCaret();
02233 #endif // KHTML_NO_CARET
02234     QScrollView::focusInEvent( e );
02235 }
02236 
02237 void KHTMLView::focusOutEvent( QFocusEvent *e )
02238 {
02239     if(m_part) m_part->stopAutoScroll();
02240 
02241 #ifndef KHTML_NO_CARET
02242     if (d->m_caretViewContext) {
02243         switch (d->m_caretViewContext->displayNonFocused) {
02244     case KHTMLPart::CaretInvisible:
02245             hideCaret();
02246         break;
02247     case KHTMLPart::CaretVisible: {
02248         killTimer(d->m_caretViewContext->freqTimerId);
02249         d->m_caretViewContext->freqTimerId = -1;
02250             NodeImpl *caretNode = m_part->xmlDocImpl()->focusNode();
02251         if (!d->m_caretViewContext->visible && (m_part->isCaretMode()
02252         || m_part->isEditable()
02253             || (caretNode && caretNode->renderer()
02254             && caretNode->renderer()->style()->userInput()
02255                 == UI_ENABLED))) {
02256             d->m_caretViewContext->visible = true;
02257             showCaret(true);
02258         }/*end if*/
02259         break;
02260     }
02261     case KHTMLPart::CaretBlink:
02262         // simply leave as is
02263         break;
02264     }/*end switch*/
02265     }/*end if*/
02266 #endif // KHTML_NO_CARET
02267     QScrollView::focusOutEvent( e );
02268 }
02269 
02270 void KHTMLView::slotScrollBarMoved()
02271 {
02272     if (!d->scrollingSelf)
02273         d->scrollBarMoved = true;
02274 }
02275 
02276 void KHTMLView::timerEvent ( QTimerEvent *e )
02277 {
02278 //    kdDebug() << "timer event " << e->timerId() << endl;
02279     if ( e->timerId() == d->scrollTimerId ) {
02280         if( d->scrollSuspended )
02281             return;
02282         switch (d->scrollDirection) {
02283             case KHTMLViewPrivate::ScrollDown:
02284                 if (contentsY() + visibleHeight () >= contentsHeight())
02285                     d->newScrollTimer(this, 0);
02286                 else
02287                     scrollBy( 0, d->scrollBy );
02288                 break;
02289             case KHTMLViewPrivate::ScrollUp:
02290                 if (contentsY() <= 0)
02291                     d->newScrollTimer(this, 0);
02292                 else
02293                     scrollBy( 0, -d->scrollBy );
02294                 break;
02295             case KHTMLViewPrivate::ScrollRight:
02296                 if (contentsX() + visibleWidth () >= contentsWidth())
02297                     d->newScrollTimer(this, 0);
02298                 else
02299                     scrollBy( d->scrollBy, 0 );
02300                 break;
02301             case KHTMLViewPrivate::ScrollLeft:
02302                 if (contentsX() <= 0)
02303                     d->newScrollTimer(this, 0);
02304                 else
02305                     scrollBy( -d->scrollBy, 0 );
02306                 break;
02307         }
02308         return;
02309     }
02310     else if ( e->timerId() == d->layoutTimerId ) {
02311         d->firstRelayout = false;
02312         d->dirtyLayout = true;
02313         layout();
02314     }
02315 #ifndef KHTML_NO_CARET
02316     else if (d->m_caretViewContext
02317              && e->timerId() == d->m_caretViewContext->freqTimerId) {
02318         d->m_caretViewContext->visible = !d->m_caretViewContext->visible;
02319     if (d->m_caretViewContext->displayed) {
02320         updateContents(d->m_caretViewContext->x, d->m_caretViewContext->y,
02321             d->m_caretViewContext->width,
02322             d->m_caretViewContext->height);
02323     }/*end if*/
02324 //  if (d->m_caretViewContext->visible) cout << "|" << flush;
02325 //  else cout << "°" << flush;
02326     return;
02327     }
02328 #endif
02329 
02330     if( m_part->xmlDocImpl() ) {
02331     DOM::DocumentImpl *document = m_part->xmlDocImpl();
02332     khtml::RenderCanvas* root = static_cast<khtml::RenderCanvas *>(document->renderer());
02333 
02334     if ( root && !root->layouted() ) {
02335         killTimer(d->repaintTimerId);
02336         d->repaintTimerId = 0;
02337         scheduleRelayout();
02338         return;
02339     }
02340     }
02341 
02342     setStaticBackground(d->useSlowRepaints);
02343 
02344 //        kdDebug() << "scheduled repaint "<< d->repaintTimerId  << endl;
02345     killTimer(d->repaintTimerId);
02346     d->repaintTimerId = 0;
02347 
02348     QRegion updateRegion;
02349     QMemArray<QRect> rects = d->updateRegion.rects();
02350 
02351     d->updateRegion = QRegion();
02352 
02353     if ( rects.size() )
02354         updateRegion = rects[0];
02355 
02356     for ( unsigned i = 1; i < rects.size(); ++i ) {
02357         QRect obR = updateRegion.boundingRect();
02358         QRegion newRegion = updateRegion.unite(rects[i]);
02359         if (2*newRegion.boundingRect().height() > 3*obR.height() )
02360         {
02361             repaintContents( obR );
02362             updateRegion = rects[i];
02363         }
02364         else
02365             updateRegion = newRegion;
02366     }
02367 
02368     if ( !updateRegion.isNull() )
02369         repaintContents( updateRegion.boundingRect() );
02370 
02371     if (d->dirtyLayout && !d->visibleWidgets.isEmpty()) {
02372         QWidget* w;
02373         d->dirtyLayout = false;
02374 
02375         QRect visibleRect(contentsX(), contentsY(), visibleWidth(), visibleHeight());
02376         QPtrList<RenderWidget> toRemove;
02377         for (QPtrDictIterator<QWidget> it(d->visibleWidgets); it.current(); ++it) {
02378             int xp = 0, yp = 0;
02379             w = it.current();
02380             RenderWidget* rw = static_cast<RenderWidget*>( it.currentKey() );
02381             if (!rw->absolutePosition(xp, yp) ||
02382                 !visibleRect.intersects(QRect(xp, yp, w->width(), w->height())))
02383                 toRemove.append(rw);
02384         }
02385         for (RenderWidget* r = toRemove.first(); r; r = toRemove.next())
02386             if ( (w = d->visibleWidgets.take(r) ) )
02387                 addChild(w, 0, -500000);
02388     }
02389 }
02390 
02391 void KHTMLView::scheduleRelayout(khtml::RenderObject * /*clippedObj*/)
02392 {
02393     if (!d->layoutSchedulingEnabled || d->layoutTimerId)
02394         return;
02395 
02396     d->layoutTimerId = startTimer( m_part->xmlDocImpl() && m_part->xmlDocImpl()->parsing()
02397                              ? 1000 : 0 );
02398 }
02399 
02400 void KHTMLView::unscheduleRelayout()
02401 {
02402     if (!d->layoutTimerId)
02403         return;
02404 
02405     killTimer(d->layoutTimerId);
02406     d->layoutTimerId = 0;
02407 }
02408 
02409 void KHTMLView::unscheduleRepaint()
02410 {
02411     if (!d->repaintTimerId)
02412         return;
02413 
02414     killTimer(d->repaintTimerId);
02415     d->repaintTimerId = 0;
02416 }
02417 
02418 void KHTMLView::scheduleRepaint(int x, int y, int w, int h)
02419 {
02420     bool parsing = !m_part->xmlDocImpl() || m_part->xmlDocImpl()->parsing();
02421 
02422 //     kdDebug() << "parsing " << parsing << endl;
02423 //     kdDebug() << "complete " << d->complete << endl;
02424 
02425     int time = parsing ? 300 : ( !d->complete ? 100 : 20 );
02426 
02427 #ifdef DEBUG_FLICKER
02428     QPainter p;
02429     p.begin( viewport() );
02430 
02431     int vx, vy;
02432     contentsToViewport( x, y, vx, vy );
02433     p.fillRect( vx, vy, w, h, Qt::red );
02434     p.end();
02435 #endif
02436 
02437     d->updateRegion = d->updateRegion.unite(QRect(x,y,w,h));
02438 
02439     if ( !d->repaintTimerId )
02440         d->repaintTimerId = startTimer( time );
02441 
02442 //     kdDebug() << "starting timer " << time << endl;
02443 }
02444 
02445 void KHTMLView::complete()
02446 {
02447 //     kdDebug() << "KHTMLView::complete()" << endl;
02448 
02449     d->complete = true;
02450 
02451     // is there a relayout pending?
02452     if (d->layoutTimerId)
02453     {
02454 //         kdDebug() << "requesting relayout now" << endl;
02455         // do it now
02456         killTimer(d->layoutTimerId);
02457         d->layoutTimerId = startTimer( 0 );
02458     }
02459 
02460     // is there a repaint pending?
02461     if (d->repaintTimerId)
02462     {
02463 //         kdDebug() << "requesting repaint now" << endl;
02464         // do it now
02465         killTimer(d->repaintTimerId);
02466         d->repaintTimerId = startTimer( 20 );
02467     }
02468 }
02469 
02470 #ifndef KHTML_NO_CARET
02471 
02472 // ### the dependencies on static functions are a nightmare. just be
02473 // hacky and include the implementation here. Clean me up, please.
02474 
02475 #include "khtml_caret.cpp"
02476 
02477 void KHTMLView::initCaret(bool keepSelection)
02478 {
02479 #if DEBUG_CARETMODE > 0
02480   kdDebug(6200) << "begin initCaret" << endl;
02481 #endif
02482   // save caretMoved state as moveCaretTo changes it
02483   if (m_part->xmlDocImpl()) {
02484     d->caretViewContext();
02485     bool cmoved = d->m_caretViewContext->caretMoved;
02486     if (m_part->d->caretNode().isNull()) {
02487       // set to document, position will be sanitized anyway
02488       m_part->d->caretNode() = m_part->document();
02489       m_part->d->caretOffset() = 0L;
02490       // This sanity check is necessary for the not so unlikely case that
02491       // setEditable or setCaretMode is called before any render objects have
02492       // been created.
02493       if (!m_part->d->caretNode().handle()->renderer()) return;
02494     }/*end if*/
02495 //    kdDebug(6200) << "d->m_selectionStart " << m_part->d->m_selectionStart.handle()
02496 //          << " d->m_selectionEnd " << m_part->d->m_selectionEnd.handle() << endl;
02497     // ### does not repaint the selection on keepSelection!=false
02498     moveCaretTo(m_part->d->caretNode().handle(), m_part->d->caretOffset(), !keepSelection);
02499 //    kdDebug(6200) << "d->m_selectionStart " << m_part->d->m_selectionStart.handle()
02500 //          << " d->m_selectionEnd " << m_part->d->m_selectionEnd.handle() << endl;
02501     d->m_caretViewContext->caretMoved = cmoved;
02502   }/*end if*/
02503 #if DEBUG_CARETMODE > 0
02504   kdDebug(6200) << "end initCaret" << endl;
02505 #endif
02506 }
02507 
02508 bool KHTMLView::caretOverrides() const
02509 {
02510     bool cm = m_part->isCaretMode();
02511     bool dm = m_part->isEditable();
02512     return cm && !dm ? false
02513         : (dm || m_part->d->caretNode().handle()->contentEditable())
02514       && d->editorContext()->override;
02515 }
02516 
02517 void KHTMLView::ensureNodeHasFocus(NodeImpl *node)
02518 {
02519   if (m_part->isCaretMode() || m_part->isEditable()) return;
02520   if (node->focused()) return;
02521 
02522   // Find first ancestor whose "user-input" is "enabled"
02523   NodeImpl *firstAncestor = 0;
02524   while (node) {
02525     if (node->renderer()
02526        && node->renderer()->style()->userInput() != UI_ENABLED)
02527       break;
02528     firstAncestor = node;
02529     node = node->parentNode();
02530   }/*wend*/
02531 
02532   if (!node) firstAncestor = 0;
02533 
02534   DocumentImpl *doc = m_part->xmlDocImpl();
02535   // ensure that embedded widgets don't lose their focus
02536   if (!firstAncestor && doc->focusNode() && doc->focusNode()->renderer()
02537     && doc->focusNode()->renderer()->isWidget())
02538     return;
02539 
02540   // Set focus node on the document
02541 #if DEBUG_CARETMODE > 1
02542   kdDebug(6200) << k_funcinfo << "firstAncestor " << firstAncestor << ": "
02543     << (firstAncestor ? firstAncestor->nodeName().string() : QString::null) << endl;
02544 #endif
02545   doc->setFocusNode(firstAncestor);
02546   emit m_part->nodeActivated(Node(firstAncestor));
02547 }
02548 
02549 void KHTMLView::recalcAndStoreCaretPos(InlineBox *hintBox)
02550 {
02551     if (!m_part || m_part->d->caretNode().isNull()) return;
02552     d->caretViewContext();
02553     NodeImpl *caretNode = m_part->d->caretNode().handle();
02554 #if DEBUG_CARETMODE > 0
02555   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;
02556 #endif
02557     caretNode->getCaret(m_part->d->caretOffset(),
02558                 caretOverrides(),
02559             d->m_caretViewContext->x, d->m_caretViewContext->y,
02560         d->m_caretViewContext->width,
02561         d->m_caretViewContext->height);
02562 
02563     if (hintBox && d->m_caretViewContext->x == -1) {
02564 #if DEBUG_CARETMODE > 1
02565         kdDebug(6200) << "using hint inline box coordinates" << endl;
02566 #endif
02567     RenderObject *r = caretNode->renderer();
02568     const QFontMetrics &fm = r->style()->fontMetrics();
02569         int absx, absy;
02570     r->containingBlock()->absolutePosition(absx, absy,
02571                         false); // ### what about fixed?
02572     d->m_caretViewContext->x = absx + hintBox->xPos();
02573     d->m_caretViewContext->y = absy + hintBox->yPos()
02574                 + hintBox->baseline() - fm.ascent();
02575     d->m_caretViewContext->width = 1;
02576     // ### firstline not regarded. But I think it can be safely neglected
02577     // as hint boxes are only used for empty lines.
02578     d->m_caretViewContext->height = fm.height();
02579     }/*end if*/
02580 
02581 #if DEBUG_CARETMODE > 4
02582 //    kdDebug(6200) << "freqTimerId: "<<d->m_caretViewContext->freqTimerId<<endl;
02583 #endif
02584 #if DEBUG_CARETMODE > 0
02585     kdDebug(6200) << "caret: ofs="<<m_part->d->caretOffset()<<" "
02586         <<" x="<<d->m_caretViewContext->x<<" y="<<d->m_caretViewContext->y
02587     <<" h="<<d->m_caretViewContext->height<<endl;
02588 #endif
02589 }
02590 
02591 void KHTMLView::caretOn()
02592 {
02593     if (d->m_caretViewContext) {
02594         killTimer(d->m_caretViewContext->freqTimerId);
02595 
02596     if (hasFocus() || d->m_caretViewContext->displayNonFocused
02597             == KHTMLPart::CaretBlink) {
02598             d->m_caretViewContext->freqTimerId = startTimer(500);
02599     } else {
02600         d->m_caretViewContext->freqTimerId = -1;
02601     }/*end if*/
02602 
02603         d->m_caretViewContext->visible = true;
02604         if ((d->m_caretViewContext->displayed = (hasFocus()
02605         || d->m_caretViewContext->displayNonFocused
02606             != KHTMLPart::CaretInvisible))) {
02607         updateContents(d->m_caretViewContext->x, d->m_caretViewContext->y,
02608                 d->m_caretViewContext->width,
02609             d->m_caretViewContext->height);
02610     }/*end if*/
02611 //        kdDebug(6200) << "caret on" << endl;
02612     }/*end if*/
02613 }
02614 
02615 void KHTMLView::caretOff()
02616 {
02617     if (d->m_caretViewContext) {
02618         killTimer(d->m_caretViewContext->freqTimerId);
02619     d->m_caretViewContext->freqTimerId = -1;
02620         d->m_caretViewContext->displayed = false;
02621         if (d->m_caretViewContext->visible) {
02622             d->m_caretViewContext->visible = false;
02623         updateContents(d->m_caretViewContext->x, d->m_caretViewContext->y,
02624                 d->m_caretViewContext->width,
02625                 d->m_caretViewContext->height);
02626     }/*end if*/
02627 //        kdDebug(6200) << "caret off" << endl;
02628     }/*end if*/
02629 }
02630 
02631 void KHTMLView::showCaret(bool forceRepaint)
02632 {
02633     if (d->m_caretViewContext) {
02634         d->m_caretViewContext->displayed = true;
02635         if (d->m_caretViewContext->visible) {
02636         if (!forceRepaint) {
02637             updateContents(d->m_caretViewContext->x, d->m_caretViewContext->y,
02638                 d->m_caretViewContext->width,
02639             d->m_caretViewContext->height);
02640             } else {
02641             repaintContents(d->m_caretViewContext->x, d->m_caretViewContext->y,
02642                 d->m_caretViewContext->width,
02643                 d->m_caretViewContext->height);
02644         }/*end if*/
02645     }/*end if*/
02646 //        kdDebug(6200) << "caret shown" << endl;
02647     }/*end if*/
02648 }
02649 
02650 bool KHTMLView::foldSelectionToCaret(NodeImpl *startNode, long startOffset,
02651                     NodeImpl *endNode, long endOffset)
02652 {
02653   m_part->d->m_selectionStart = m_part->d->m_selectionEnd = m_part->d->caretNode();
02654   m_part->d->m_startOffset = m_part->d->m_endOffset = m_part->d->caretOffset();
02655   m_part->d->m_extendAtEnd = true;
02656 
02657   bool folded = startNode != endNode || startOffset != endOffset;
02658 
02659   // Only clear the selection if there has been one.
02660   if (folded) {
02661     m_part->xmlDocImpl()->clearSelection();
02662   }/*end if*/
02663 
02664   return folded;
02665 }
02666 
02667 void KHTMLView::hideCaret()
02668 {
02669     if (d->m_caretViewContext) {
02670         if (d->m_caretViewContext->visible) {
02671 //            kdDebug(6200) << "redraw caret hidden" << endl;
02672         d->m_caretViewContext->visible = false;
02673         // force repaint, otherwise the event won't be handled
02674         // before the focus leaves the window
02675         repaintContents(d->m_caretViewContext->x, d->m_caretViewContext->y,
02676                 d->m_caretViewContext->width,
02677                 d->m_caretViewContext->height);
02678         d->m_caretViewContext->visible = true;
02679     }/*end if*/
02680         d->m_caretViewContext->displayed = false;
02681 //        kdDebug(6200) << "caret hidden" << endl;
02682     }/*end if*/
02683 }
02684 
02685 int KHTMLView::caretDisplayPolicyNonFocused() const
02686 {
02687   if (d->m_caretViewContext)
02688     return d->m_caretViewContext->displayNonFocused;
02689   else
02690     return KHTMLPart::CaretInvisible;
02691 }
02692 
02693 void KHTMLView::setCaretDisplayPolicyNonFocused(int policy)
02694 {
02695   d->caretViewContext();
02696 //  int old = d->m_caretViewContext->displayNonFocused;
02697   d->m_caretViewContext->displayNonFocused = (KHTMLPart::CaretDisplayPolicy)policy;
02698 
02699   // make change immediately take effect if not focused
02700   if (!hasFocus()) {
02701     switch (d->m_caretViewContext->displayNonFocused) {
02702       case KHTMLPart::CaretInvisible:
02703         hideCaret();
02704     break;
02705       case KHTMLPart::CaretBlink:
02706     if (d->m_caretViewContext->freqTimerId != -1) break;
02707     d->m_caretViewContext->freqTimerId = startTimer(500);
02708     // fall through
02709       case KHTMLPart::CaretVisible:
02710         d->m_caretViewContext->displayed = true;
02711         showCaret();
02712     break;
02713     }/*end switch*/
02714   }/*end if*/
02715 }
02716 
02717 bool KHTMLView::placeCaret(InlineBox *hintBox)
02718 {
02719   CaretViewContext *cv = d->caretViewContext();
02720   caretOff();
02721   NodeImpl *caretNode = m_part->d->caretNode().handle();
02722   // ### why is it sometimes null?
02723   if (!caretNode || !caretNode->renderer()) return false;
02724   ensureNodeHasFocus(caretNode);
02725   if (m_part->isCaretMode() || m_part->isEditable()
02726      || caretNode->renderer()->style()->userInput() == UI_ENABLED) {
02727     recalcAndStoreCaretPos(hintBox);
02728 
02729     cv->origX = cv->x;
02730 
02731     caretOn();
02732     return true;
02733   }/*end if*/
02734   return false;
02735 }
02736 
02737 void KHTMLView::ensureCaretVisible()
02738 {
02739   CaretViewContext *cv = d->m_caretViewContext;
02740   if (!cv) return;
02741   ensureVisible(cv->x, cv->y, cv->width, cv->height);
02742   d->scrollBarMoved = false;
02743 }
02744 
02745 bool KHTMLView::extendSelection(NodeImpl *oldStartSel, long oldStartOfs,
02746                 NodeImpl *oldEndSel, long oldEndOfs)
02747 {
02748   bool changed = false;
02749   if (m_part->d->m_selectionStart == m_part->d->m_selectionEnd
02750       && m_part->d->m_startOffset == m_part->d->m_endOffset) {
02751     changed = foldSelectionToCaret(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs);
02752     m_part->d->m_extendAtEnd = true;
02753   } else do {
02754     changed = m_part->d->m_selectionStart.handle() != oldStartSel
02755             || m_part->d->m_startOffset != oldStartOfs
02756         || m_part->d->m_selectionEnd.handle() != oldEndSel
02757         || m_part->d->m_endOffset != oldEndOfs;
02758     if (!changed) break;
02759 
02760     // determine start position -- caret position is always at end.
02761     NodeImpl *startNode;
02762     long startOffset;
02763     if (m_part->d->m_extendAtEnd) {
02764       startNode = m_part->d->m_selectionStart.handle();
02765       startOffset = m_part->d->m_startOffset;
02766     } else {
02767       startNode = m_part->d->m_selectionEnd.handle();
02768       startOffset = m_part->d->m_endOffset;
02769       m_part->d->m_selectionEnd = m_part->d->m_selectionStart;
02770       m_part->d->m_endOffset = m_part->d->m_startOffset;
02771     }/*end if*/
02772 
02773     bool swapNeeded = false;
02774     if (!m_part->d->m_selectionEnd.isNull() && startNode) {
02775       swapNeeded = RangeImpl::compareBoundaryPoints(startNode, startOffset,
02776                 m_part->d->m_selectionEnd.handle(),
02777             m_part->d->m_endOffset) >= 0;
02778     }/*end if*/
02779 
02780     m_part->d->m_selectionStart = startNode;
02781     m_part->d->m_startOffset = startOffset;
02782 
02783     if (swapNeeded) {
02784       m_part->xmlDocImpl()->setSelection(m_part->d->m_selectionEnd.handle(),
02785         m_part->d->m_endOffset, m_part->d->m_selectionStart.handle(),
02786         m_part->d->m_startOffset);
02787     } else {
02788       m_part->xmlDocImpl()->setSelection(m_part->d->m_selectionStart.handle(),
02789         m_part->d->m_startOffset, m_part->d->m_selectionEnd.handle(),
02790         m_part->d->m_endOffset);
02791     }/*end if*/
02792   } while(false);/*end if*/
02793   return changed;
02794 }
02795 
02796 void KHTMLView::updateSelection(NodeImpl *oldStartSel, long oldStartOfs,
02797                 NodeImpl *oldEndSel, long oldEndOfs)
02798 {
02799   if (m_part->d->m_selectionStart == m_part->d->m_selectionEnd
02800       && m_part->d->m_startOffset == m_part->d->m_endOffset) {
02801     if (foldSelectionToCaret(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs)) {
02802       m_part->emitSelectionChanged();
02803     }/*end if*/
02804     m_part->d->m_extendAtEnd = true;
02805   } else {
02806     // check if the extending end has passed the immobile end
02807     if (!m_part->d->m_selectionEnd.isNull() && !m_part->d->m_selectionEnd.isNull()) {
02808       bool swapNeeded = RangeImpl::compareBoundaryPoints(
02809                 m_part->d->m_selectionStart.handle(), m_part->d->m_startOffset,
02810             m_part->d->m_selectionEnd.handle(), m_part->d->m_endOffset) >= 0;
02811       if (swapNeeded) {
02812         DOM::Node tmpNode = m_part->d->m_selectionStart;
02813         long tmpOffset = m_part->d->m_startOffset;
02814         m_part->d->m_selectionStart = m_part->d->m_selectionEnd;
02815         m_part->d->m_startOffset = m_part->d->m_endOffset;
02816         m_part->d->m_selectionEnd = tmpNode;
02817         m_part->d->m_endOffset = tmpOffset;
02818         m_part->d->m_startBeforeEnd = true;
02819         m_part->d->m_extendAtEnd = !m_part->d->m_extendAtEnd;
02820       }/*end if*/
02821     }/*end if*/
02822 
02823     m_part->xmlDocImpl()->setSelection(m_part->d->m_selectionStart.handle(),
02824         m_part->d->m_startOffset, m_part->d->m_selectionEnd.handle(),
02825         m_part->d->m_endOffset);
02826     m_part->emitSelectionChanged();
02827   }/*end if*/
02828 }
02829 
02830 void KHTMLView::caretKeyPressEvent(QKeyEvent *_ke)
02831 {
02832   NodeImpl *oldStartSel = m_part->d->m_selectionStart.handle();
02833   long oldStartOfs = m_part->d->m_startOffset;
02834   NodeImpl *oldEndSel = m_part->d->m_selectionEnd.handle();
02835   long oldEndOfs = m_part->d->m_endOffset;
02836 
02837   NodeImpl *oldCaretNode = m_part->d->caretNode().handle();
02838   long oldOffset = m_part->d->caretOffset();
02839 
02840   bool ctrl = _ke->state() & ControlButton;
02841 
02842 // FIXME: this is that widely indented because I will write ifs around it.
02843       switch(_ke->key()) {
02844         case Key_Space:
02845           break;
02846 
02847         case Key_Down:
02848       moveCaretNextLine(1);
02849           break;
02850 
02851         case Key_Up:
02852       moveCaretPrevLine(1);
02853           break;
02854 
02855         case Key_Left:
02856       moveCaretBy(false, ctrl ? CaretByWord : CaretByCharacter, 1);
02857           break;
02858 
02859         case Key_Right:
02860       moveCaretBy(true, ctrl ? CaretByWord : CaretByCharacter, 1);
02861           break;
02862 
02863         case Key_Next:
02864       moveCaretNextPage();
02865           break;
02866 
02867         case Key_Prior:
02868       moveCaretPrevPage();
02869           break;
02870 
02871         case Key_Home:
02872       if (ctrl)
02873         moveCaretToDocumentBoundary(false);
02874       else
02875         moveCaretToLineBegin();
02876           break;
02877 
02878         case Key_End:
02879       if (ctrl)
02880         moveCaretToDocumentBoundary(true);
02881       else
02882         moveCaretToLineEnd();
02883           break;
02884 
02885       }/*end switch*/
02886 
02887   if ((m_part->d->caretNode().handle() != oldCaretNode
02888     || m_part->d->caretOffset() != oldOffset)
02889     // node should never be null, but faulty conditions may cause it to be
02890     && !m_part->d->caretNode().isNull()) {
02891 
02892     d->m_caretViewContext->caretMoved = true;
02893 
02894     if (_ke->state() & ShiftButton) {   // extend selection
02895       updateSelection(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs);
02896     } else {            // clear any selection
02897       if (foldSelectionToCaret(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs))
02898         m_part->emitSelectionChanged();
02899     }/*end if*/
02900 
02901     m_part->emitCaretPositionChanged(m_part->d->caretNode(), m_part->d->caretOffset());
02902   }/*end if*/
02903 
02904   _ke->accept();
02905 }
02906 
02907 bool KHTMLView::moveCaretTo(NodeImpl *node, long offset, bool clearSel)
02908 {
02909   sanitizeCaretState(node, offset);
02910   if (!node) return false;
02911 
02912   // need to find out the node's inline box. If there is none, this function
02913   // will snap to the next node that has one. This is necessary to make the
02914   // caret visible in any case.
02915   RenderArena arena;
02916   RenderFlow *cb;
02917   InlineBox *box = 0;
02918   findFlowBox(node, offset, &arena, cb, &box);
02919   if (box && box->object() != node->renderer()) {
02920     if (box->object()->element()) {
02921       node = box->object()->element();
02922       offset = node->minOffset();
02923 #if DEBUG_CARETMODE > 1
02924       kdDebug(6200) << "set new node " << node->nodeName().string() << "@" << node << endl;
#endif
    } else {    // box has no associated element -> do not use
      // this case should actually never happen.
      box = 0;
      kdError(6200) << "Box contains no node! Crash imminent" << endl;
02925     }/*end if*/
02926   }
02927 
02928   NodeImpl *oldStartSel = m_part->d->m_selectionStart.handle();
02929   long oldStartOfs = m_part->d->m_startOffset;
02930   NodeImpl *oldEndSel = m_part->d->m_selectionEnd.handle();
02931   long oldEndOfs = m_part->d->m_endOffset;
02932 
02933   // test for position change
02934   bool posChanged = m_part->d->caretNode().handle() != node
02935         || m_part->d->caretOffset() != offset;
02936   bool selChanged = false;
02937 
02938   m_part->d->caretNode() = node;
02939   m_part->d->caretOffset() = offset;
02940   if (clearSel || !oldStartSel || !oldEndSel) {
02941     selChanged = foldSelectionToCaret(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs);
02942   } else {
02943     //kdDebug(6200) << "moveToCaret: extendSelection: m_extendAtEnd " << m_part->d->m_extendAtEnd << endl;
02944     //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;
02945     selChanged = extendSelection(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs);
02946     //kdDebug(6200) << "after extendSelection: m_extendAtEnd " << m_part->d->m_extendAtEnd << endl;
02947     //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;
02948   }/*end if*/
02949 
02950   d->caretViewContext()->caretMoved = true;
02951 
02952   bool visible_caret = placeCaret(box);
02953 
02954   // FIXME: if the old position was !visible_caret, and the new position is
02955   // also, then two caretPositionChanged signals with a null Node are
02956   // emitted in series.
02957   if (posChanged) {
02958     m_part->emitCaretPositionChanged(visible_caret ? node : 0, offset);
02959   }/*end if*/
02960 
02961   return selChanged;
02962 }
02963 
02964 void KHTMLView::moveCaretByLine(bool next, int count)
02965 {
02966   // FIXME: what if the node is removed whilst we access it?
02967   // Current solution: bail out
02968   Node &caretNodeRef = m_part->d->caretNode();
02969   if (caretNodeRef.isNull()) return;
02970 
02971   NodeImpl *caretNode = caretNodeRef.handle();
02972 //  kdDebug(6200) << ": caretNode=" << caretNode << endl;
02973   long offset = m_part->d->caretOffset();
02974 
02975   CaretViewContext *cv = d->caretViewContext();
02976 
02977   LinearDocument ld(m_part, caretNode, offset);
02978 
02979   ErgonomicEditableLineIterator it(ld.current(), cv->origX);
02980 
02981   // move count lines vertically
02982   while (count > 0 && it != ld.end() && it != ld.preBegin()) {
02983     count--;
02984     if (next) ++it; else --it;
02985   }/*wend*/
02986 
02987   // Nothing? Then leave everything as is.
02988   if (it == ld.end() || it == ld.preBegin()) return;
02989 
02990   int x, absx, absy;
02991   InlineBox *caretBox = nearestInlineBox(it, d->m_caretViewContext, x, absx, absy);
02992 
02993   placeCaretOnLine(caretBox, x, absx, absy);
02994 }
02995 
02996 void KHTMLView::placeCaretOnLine(InlineBox *caretBox, int x, int absx, int absy)
02997 {
02998   // paranoia sanity check
02999   if (!caretBox) return;
03000 
03001   RenderObject *caretRender = caretBox->object();
03002   NodeImpl *caretNode = caretRender->element();
03003 
03004 #if DEBUG_CARETMODE > 0
03005   kdDebug(6200) << "got valid caretBox " << caretBox << endl;
03006   kdDebug(6200) << "xPos: " << caretBox->xPos() << " yPos: " << caretBox->yPos()
03007         << " width: " << caretBox->width() << " height: " << caretBox->height() << endl;
03008   if (caretBox->isInlineTextBox()) { kdDebug(6200) << "contains \"" << QString(((RenderText *)((InlineTextBox *)caretBox)->object())->str->s + ((InlineTextBox *)caretBox)->m_start, ((InlineTextBox *)caretBox)->m_len) << "\"" << endl;}
03009 #endif
03010   // inquire height of caret
03011   int caretHeight = caretBox->height();
03012   bool isText = caretBox->isInlineTextBox();
03013   int yOfs = 0;     // y-offset for text nodes
03014   if (isText) {
03015     // text boxes need extrawurst
03016     RenderText *t = static_cast<RenderText *>(caretRender);
03017     const QFontMetrics &fm = t->metrics(caretBox->m_firstLine);
03018     caretHeight = fm.height();
03019     yOfs = caretBox->baseline() - fm.ascent();
03020   }/*end if*/
03021 
03022   caretOff();
03023 
03024   // set new caret node
03025   m_part->d->caretNode() = caretNode;
03026   long &offset = m_part->d->caretOffset();
03027 
03028   // set all variables not needing special treatment
03029   d->m_caretViewContext->y = caretBox->yPos() + yOfs;
03030   d->m_caretViewContext->height = caretHeight;
03031   d->m_caretViewContext->width = 1; // FIXME: regard override
03032 
03033   int xPos = caretBox->xPos();
03034   int caretBoxWidth = caretBox->width();
03035 
03036   // before or at beginning of inline box -> place at beginning
03037   if (x <= xPos) {
03038     d->m_caretViewContext->x = xPos;
03039     offset = caretBox->minOffset();
03040   // somewhere within this block
03041   } else if (x > xPos && x <= xPos + caretBoxWidth) {
03042     if (isText) { // find out where exactly
03043       offset = static_cast<InlineTextBox *>(caretBox)->offsetForPoint(x,
03044             d->m_caretViewContext->x);
03045 #if DEBUG_CARETMODE > 2
03046       kdDebug(6200) << "deviation from origX " << d->m_caretViewContext->x - x << endl;
03047 #endif
03048     } else {    // snap to nearest end
03049       if (xPos + caretBoxWidth - x < x - xPos) {
03050         d->m_caretViewContext->x = xPos + caretBoxWidth;
03051         offset = caretNode ? caretNode->maxOffset() : 1;
03052       } else {
03053         d->m_caretViewContext->x = xPos;
03054         offset = caretNode ? caretNode->minOffset() : 0;
03055       }/*end if*/
03056     }/*end if*/
03057   } else {      // after the inline box -> place at end
03058     d->m_caretViewContext->x = xPos + caretBoxWidth;
03059     offset = caretBox->maxOffset();
03060   }/*end if*/
03061 #if DEBUG_CARETMODE > 0
03062       kdDebug(6200) << "new offset: " << offset << endl;
03063 #endif
03064 
03065   d->m_caretViewContext->x += absx;
03066   d->m_caretViewContext->y += absy;
03067 
03068   ensureVisible(d->m_caretViewContext->x, d->m_caretViewContext->y,
03069     d->m_caretViewContext->width, d->m_caretViewContext->height);
03070   d->scrollBarMoved = false;
03071 
03072   ensureNodeHasFocus(caretNode);
03073   caretOn();
03074 }
03075 
03076 void KHTMLView::moveCaretToLineBoundary(bool end)
03077 {
03078   // FIXME: what if the node is removed whilst we access it?
03079   // Current solution: bail out
03080   Node &caretNodeRef = m_part->d->caretNode();
03081   if (caretNodeRef.isNull()) return;
03082 
03083   NodeImpl *caretNode = caretNodeRef.handle();
03084 //  kdDebug(6200) << ": caretNode=" << caretNode << endl;
03085   long offset = m_part->d->caretOffset();
03086 
03087   LinearDocument ld(m_part, caretNode, offset);
03088 
03089   EditableLineIterator it = ld.current();
03090   if (it == ld.end()) return;   // should not happen, but who knows
03091 
03092   EditableInlineBoxIterator fbit(it, end);
03093   InlineBox *b = *fbit;
03094   Q_ASSERT(b);
03095 
03096   RenderObject *cb = (*it)->object();
03097   int absx, absy;
03098 
03099   if (cb) cb->absolutePosition(absx,absy);
03100   else absx = absy = 0;
03101 
03102   int x = b->xPos() + (end ? b->width() : 0);
03103   d->m_caretViewContext->origX = absx + x;
03104   placeCaretOnLine(b, x, absx, absy);
03105 }
03106 
03107 void KHTMLView::moveCaretToDocumentBoundary(bool end)
03108 {
03109   // FIXME: what if the node is removed whilst we access it?
03110   // Current solution: bail out
03111   Node &caretNodeRef = m_part->d->caretNode();
03112   if (caretNodeRef.isNull()) return;
03113 
03114   NodeImpl *caretNode = caretNodeRef.handle();
03115 //  kdDebug(6200) << ": caretNode=" << caretNode << endl;
03116   long offset = m_part->d->caretOffset();
03117 
03118   LinearDocument ld(m_part, caretNode, offset);
03119 
03120   EditableLineIterator it(end ? ld.preEnd() : ld.begin(), end);
03121   if (it == ld.end() || it == ld.preBegin()) return;    // should not happen, but who knows
03122 
03123   EditableInlineBoxIterator fbit = it;
03124   InlineBox *b = *fbit;
03125   Q_ASSERT(b);
03126 
03127   RenderObject *cb = (*it)->object();
03128   int absx, absy;
03129 
03130   if (cb) cb->absolutePosition(absx, absy);
03131   else absx = absy = 0;
03132 
03133   int x = b->xPos()/* + (end ? b->width() : 0) reactivate for rtl*/;
03134   d->m_caretViewContext->origX = absx + x;
03135   placeCaretOnLine(b, x, absx, absy);
03136 }
03137 
03138 void KHTMLView::moveCaretBy(bool next, CaretMovement cmv, int count)
03139 {
03140   if (!m_part) return;
03141   // FIXME: what if the node is removed whilst we access it?
03142   // Current solution: bail out
03143   Node &caretNodeRef = m_part->d->caretNode();
03144   if (caretNodeRef.isNull()) return;
03145 
03146   NodeImpl *caretNode = caretNodeRef.handle();
03147 //  kdDebug(6200) << ": caretNode=" << caretNode << endl;
03148   long &offset = m_part->d->caretOffset();
03149 
03150   LinearDocument ld(m_part, caretNode, offset);
03151 
03152   EditableCharacterIterator it(&ld);
03153   InlineBox *hintBox = it.box();
03154   while (it.node() && count > 0) {
03155     count--;
03156     if (cmv == CaretByCharacter) {
03157       if (next) ++it;
03158       else --it;
03159     } else if (cmv == CaretByWord) {
03160       if (next) moveItToNextWord(it);
03161       else moveItToPrevWord(it);
03162     }/*end if*/
03163   }/*wend*/
03164   if (it.node()) {
03165     caretNodeRef = it.node();
03166     offset = it.offset();
03167     hintBox = it.box();
03168 #if DEBUG_CARETMODE > 2
03169     kdDebug(6200) << "set by valid node. offset: " << offset << endl;
03170 #endif
03171   } else {
03172     offset = next ? caretNode->maxOffset() : caretNode->minOffset();
03173 #if DEBUG_CARETMODE > 0
03174     kdDebug(6200) << "set by INvalid node. offset: " << offset << endl;
03175 #endif
03176   }/*end if*/
03177   placeCaretOnChar(hintBox);
03178 }
03179 
03180 void KHTMLView::placeCaretOnChar(InlineBox *hintBox)
03181 {
03182   caretOff();
03183   recalcAndStoreCaretPos(hintBox);
03184   ensureVisible(d->m_caretViewContext->x, d->m_caretViewContext->y,
03185     d->m_caretViewContext->width, d->m_caretViewContext->height);
03186   d->m_caretViewContext->origX = d->m_caretViewContext->x;
03187   d->scrollBarMoved = false;
03188 #if DEBUG_CARETMODE > 3
03189   //if (caretNode->isTextNode())  kdDebug(6200) << "text[0] = " << (int)*((TextImpl *)caretNode)->data().unicode() << " text :\"" << ((TextImpl *)caretNode)->data().string() << "\"" << endl;
03190 #endif
03191   ensureNodeHasFocus(m_part->d->caretNode().handle());
03192   caretOn();
03193 }
03194 
03195 void KHTMLView::moveCaretByPage(bool next)
03196 {
03197   // FIXME: what if the node is removed whilst we access it?
03198   // Current solution: bail out
03199   Node &caretNodeRef = m_part->d->caretNode();
03200   if (caretNodeRef.isNull()) return;
03201 
03202   NodeImpl *caretNode = caretNodeRef.handle();
03203 //  kdDebug(6200) << ": caretNode=" << caretNode << endl;
03204   long offset = m_part->d->caretOffset();
03205 
03206   int offs = (clipper()->height() < 30) ? clipper()->height() : 30;
03207   // Minimum distance the caret must be moved
03208   int mindist = clipper()->height() - offs;
03209 
03210   CaretViewContext *cv = d->caretViewContext();
03211 //  int y = cv->y;      // we always measure the top border
03212 
03213   LinearDocument ld(m_part, caretNode, offset);
03214 
03215   ErgonomicEditableLineIterator it(ld.current(), cv->origX);
03216 
03217   moveIteratorByPage(ld, it, mindist, next);
03218 
03219   int x, absx, absy;
03220   InlineBox *caretBox = nearestInlineBox(it, d->m_caretViewContext, x, absx, absy);
03221 
03222   placeCaretOnLine(caretBox, x, absx, absy);
03223 }
03224 
03225 void KHTMLView::moveCaretPrevWord()
03226 {
03227   moveCaretBy(false, CaretByWord, 1);
03228 }
03229 
03230 void KHTMLView::moveCaretNextWord()
03231 {
03232   moveCaretBy(true, CaretByWord, 1);
03233 }
03234 
03235 void KHTMLView::moveCaretPrevLine(int n)
03236 {
03237   moveCaretByLine(false, n);
03238 }
03239 
03240 void KHTMLView::moveCaretNextLine(int n)
03241 {
03242   moveCaretByLine(true, n);
03243 }
03244 
03245 void KHTMLView::moveCaretPrevPage()
03246 {
03247   moveCaretByPage(false);
03248 }
03249 
03250 void KHTMLView::moveCaretNextPage()
03251 {
03252   moveCaretByPage(true);
03253 }
03254 
03255 void KHTMLView::moveCaretToLineBegin()
03256 {
03257   moveCaretToLineBoundary(false);
03258 }
03259 
03260 void KHTMLView::moveCaretToLineEnd()
03261 {
03262   moveCaretToLineBoundary(true);
03263 }
03264 
03265 #endif // KHTML_NO_CARET
03266 
03267 #undef DEBUG_CARETMODE
03268 
KDE Logo
This file is part of the documentation for khtml Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Apr 21 18:45:04 2004 by doxygen 1.3.6-20040222 written by Dimitri van Heesch, © 1997-2003