diff --git a/khtml/khtmlview.cpp b/khtml/khtmlview.cpp index 8dea439694..937132dc69 100644 --- a/khtml/khtmlview.cpp +++ b/khtml/khtmlview.cpp @@ -1,4892 +1,4905 @@ /* This file is part of the KDE project * * Copyright (C) 1998, 1999 Torben Weis * 1999 Lars Knoll * 1999 Antti Koivisto * 2000-2004 Dirk Mueller * 2003 Leo Savernik * 2003-2004 Apple Computer, Inc. * 2006 Germain Garand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "khtmlview.h" #include "khtmlview.moc" #include "khtml_part.h" #include "khtml_events.h" #ifdef Q_WS_X11 #include #endif #include "html/html_documentimpl.h" #include "html/html_inlineimpl.h" #include "html/html_formimpl.h" #include "rendering/render_arena.h" #include "rendering/render_canvas.h" #include "rendering/render_frames.h" #include "rendering/render_replaced.h" #include "rendering/render_form.h" #include "rendering/render_layer.h" #include "rendering/render_line.h" #include "rendering/render_table.h" // removeme #define protected public #include "rendering/render_text.h" #undef protected #include "xml/dom2_eventsimpl.h" #include "css/cssstyleselector.h" #include "css/csshelper.h" #include "misc/htmlhashes.h" #include "misc/helper.h" #include "misc/loader.h" #include "khtml_settings.h" #include "khtml_printsettings.h" #include "khtmlpart_p.h" #ifndef KHTML_NO_CARET #include "khtml_caret_p.h" #include "xml/dom2_rangeimpl.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#define DEBUG_FLICKER //#define DEBUG_PIXEL #include #ifdef Q_WS_X11 #include #include #elif defined(Q_WS_WIN) #include #endif #if 0 namespace khtml { void dumpLineBoxes(RenderFlow *flow); } #endif using namespace DOM; using namespace khtml; class KHTMLViewPrivate { friend class KHTMLView; public: enum PseudoFocusNodes { PFNone, PFTop, PFBottom }; enum CompletedState { CSNone = 0, CSFull, CSActionPending }; KHTMLViewPrivate() : underMouse( 0 ), underMouseNonShared( 0 ), oldUnderMouse( 0 ) { #ifndef KHTML_NO_CARET m_caretViewContext = 0; m_editorContext = 0; #endif // KHTML_NO_CARET postponed_autorepeat = NULL; scrollingFromWheelTimerId = 0; reset(); vpolicy = Qt::ScrollBarAsNeeded; hpolicy = Qt::ScrollBarAsNeeded; formCompletions=0; prevScrollbarVisible = true; possibleTripleClick = false; emitCompletedAfterRepaint = CSNone; cursor_icon_widget = NULL; m_mouseScrollTimer = 0; m_mouseScrollIndicator = 0; } ~KHTMLViewPrivate() { delete formCompletions; delete postponed_autorepeat; if (underMouse) underMouse->deref(); if (underMouseNonShared) underMouseNonShared->deref(); if (oldUnderMouse) oldUnderMouse->deref(); #ifndef KHTML_NO_CARET delete m_caretViewContext; delete m_editorContext; #endif // KHTML_NO_CARET delete cursor_icon_widget; delete m_mouseScrollTimer; delete m_mouseScrollIndicator; } void reset() { if (underMouse) underMouse->deref(); underMouse = 0; if (underMouseNonShared) underMouseNonShared->deref(); underMouseNonShared = 0; if (oldUnderMouse) oldUnderMouse->deref(); oldUnderMouse = 0; linkPressed = false; staticWidget = false; tabMovePending = false; lastTabbingDirection = true; pseudoFocusNode = PFNone; zoomLevel = 100; #ifndef KHTML_NO_SCROLLBARS //We don't turn off the toolbars here //since if the user turns them //off, then chances are they want them turned //off always - even after a reset. #else vpolicy = ScrollBarAlwaysOff; hpolicy = ScrollBarAlwaysOff; #endif #ifdef DEBUG_PIXEL timer.start(); pixelbooth = 0; repaintbooth = 0; #endif scrollBarMoved = false; contentsMoving = false; ignoreWheelEvents = false; scrollingFromWheel = QPoint(-1,-1); borderX = 30; borderY = 30; paged = false; clickX = -1; clickY = -1; contentsX = 0; contentsY = 0; clickCount = 0; isDoubleClick = false; scrollingSelf = false; delete postponed_autorepeat; postponed_autorepeat = NULL; layoutTimerId = 0; repaintTimerId = 0; scrollTimerId = 0; scrollSuspended = false; scrollSuspendPreActivate = false; complete = false; firstRelayout = true; needsFullRepaint = true; dirtyLayout = false; layoutSchedulingEnabled = true; painting = false; updateRegion = QRegion(); m_dialogsAllowed = true; #ifndef KHTML_NO_CARET if (m_caretViewContext) { m_caretViewContext->caretMoved = false; m_caretViewContext->keyReleasePending = false; }/*end if*/ #endif // KHTML_NO_CARET #ifndef KHTML_NO_TYPE_AHEAD_FIND typeAheadActivated = false; #endif // KHTML_NO_TYPE_AHEAD_FIND accessKeysActivated = false; accessKeysPreActivate = false; // the view might have been built before the part it will be assigned to, // so exceptionally, we need to directly ref/deref KHTMLGlobal to // account for this transitory case. KHTMLGlobal::ref(); accessKeysEnabled = KHTMLGlobal::defaultHTMLSettings()->accessKeysEnabled(); KHTMLGlobal::deref(); emitCompletedAfterRepaint = CSNone; m_mouseEventsTarget = 0; m_clipHolder = 0; } void newScrollTimer(QWidget *view, int tid) { //kDebug(6000) << "newScrollTimer timer " << tid; view->killTimer(scrollTimerId); scrollTimerId = tid; scrollSuspended = false; } enum ScrollDirection { ScrollLeft, ScrollRight, ScrollUp, ScrollDown }; void adjustScroller(QWidget *view, ScrollDirection direction, ScrollDirection oppositedir) { static const struct { int msec, pixels; } timings [] = { {320,1}, {224,1}, {160,1}, {112,1}, {80,1}, {56,1}, {40,1}, {28,1}, {20,1}, {20,2}, {20,3}, {20,4}, {20,6}, {20,8}, {0,0} }; if (!scrollTimerId || (static_cast(scrollDirection) != direction && (static_cast(scrollDirection) != oppositedir || scrollSuspended))) { scrollTiming = 6; scrollBy = timings[scrollTiming].pixels; scrollDirection = direction; newScrollTimer(view, view->startTimer(timings[scrollTiming].msec)); } else if (scrollDirection == direction && timings[scrollTiming+1].msec && !scrollSuspended) { scrollBy = timings[++scrollTiming].pixels; newScrollTimer(view, view->startTimer(timings[scrollTiming].msec)); } else if (scrollDirection == oppositedir) { if (scrollTiming) { scrollBy = timings[--scrollTiming].pixels; newScrollTimer(view, view->startTimer(timings[scrollTiming].msec)); } } scrollSuspended = false; } bool haveZoom() const { return zoomLevel != 100; } #ifndef KHTML_NO_CARET /** this function returns an instance of the caret view context. If none * exists, it will be instantiated. */ CaretViewContext *caretViewContext() { if (!m_caretViewContext) m_caretViewContext = new CaretViewContext(); return m_caretViewContext; } /** this function returns an instance of the editor context. If none * exists, it will be instantiated. */ EditorContext *editorContext() { if (!m_editorContext) m_editorContext = new EditorContext(); return m_editorContext; } #endif // KHTML_NO_CARET #ifdef DEBUG_PIXEL QTime timer; unsigned int pixelbooth; unsigned int repaintbooth; #endif NodeImpl *underMouse; NodeImpl *underMouseNonShared; NodeImpl *oldUnderMouse; bool tabMovePending:1; bool lastTabbingDirection:1; PseudoFocusNodes pseudoFocusNode:2; bool scrollBarMoved:1; bool contentsMoving:1; Qt::ScrollBarPolicy vpolicy; Qt::ScrollBarPolicy hpolicy; bool prevScrollbarVisible:1; bool linkPressed:1; bool staticWidget:1; bool ignoreWheelEvents:1; int zoomLevel; int borderX, borderY; KConfig *formCompletions; int clickX, clickY, clickCount; bool isDoubleClick; bool paged; bool scrollingSelf; int contentsX, contentsY; int layoutTimerId; QKeyEvent* postponed_autorepeat; int repaintTimerId; int scrollTimerId; int scrollTiming; int scrollBy; ScrollDirection scrollDirection :2; bool scrollSuspended :1; bool scrollSuspendPreActivate :1; bool complete :1; bool firstRelayout :1; bool layoutSchedulingEnabled :1; bool needsFullRepaint :1; bool painting :1; bool possibleTripleClick :1; bool dirtyLayout :1; bool m_dialogsAllowed :1; QRegion updateRegion; QHash visibleWidgets; #ifndef KHTML_NO_CARET CaretViewContext *m_caretViewContext; EditorContext *m_editorContext; #endif // KHTML_NO_CARET #ifndef KHTML_NO_TYPE_AHEAD_FIND QString findString; QTimer timer; bool findLinksOnly; bool typeAheadActivated; #endif // KHTML_NO_TYPE_AHEAD_FIND bool accessKeysEnabled; bool accessKeysActivated; bool accessKeysPreActivate; CompletedState emitCompletedAfterRepaint; QWidget* cursor_icon_widget; // scrolling activated by MMB short m_mouseScroll_byX; short m_mouseScroll_byY; QPoint scrollingFromWheel; int scrollingFromWheelTimerId; QTimer *m_mouseScrollTimer; QWidget *m_mouseScrollIndicator; QPointer m_mouseEventsTarget; QStack* m_clipHolder; }; #ifndef QT_NO_TOOLTIP /** calculates the client-side image map rectangle for the given image element * @param img image element * @param scrollOfs scroll offset of viewport in content coordinates * @param p position to be probed in viewport coordinates * @param r returns the bounding rectangle in content coordinates * @param s returns the title string * @return true if an appropriate area was found -- only in this case r and * s are valid, false otherwise */ static bool findImageMapRect(HTMLImageElementImpl *img, const QPoint &scrollOfs, const QPoint &p, QRect &r, QString &s) { HTMLMapElementImpl* map; if (img && img->getDocument()->isHTMLDocument() && (map = static_cast(img->getDocument())->getMap(img->imageMap()))) { RenderObject::NodeInfo info(true, false); RenderObject *rend = img->renderer(); int ax, ay; if (!rend || !rend->absolutePosition(ax, ay)) return false; // we're a client side image map bool inside = map->mapMouseEvent(p.x() - ax + scrollOfs.x(), p.y() - ay + scrollOfs.y(), rend->contentWidth(), rend->contentHeight(), info); if (inside && info.URLElement()) { HTMLAreaElementImpl *area = static_cast(info.URLElement()); Q_ASSERT(area->id() == ID_AREA); s = area->getAttribute(ATTR_TITLE).string(); QRegion reg = area->cachedRegion(); if (!s.isEmpty() && !reg.isEmpty()) { r = reg.boundingRect(); r.translate(ax, ay); return true; } } } return false; } bool KHTMLView::event( QEvent* e ) { switch ( e->type() ) { case QEvent::ToolTip: { QHelpEvent *he = static_cast(e); QPoint p = he->pos(); DOM::NodeImpl *node = d->underMouseNonShared; QRect region; while ( node ) { if ( node->isElementNode() ) { DOM::ElementImpl *e = static_cast( node ); QRect r; QString s; bool found = false; // for images, check if it is part of a client-side image map, // and query the s' title attributes, too if (e->id() == ID_IMG && !e->getAttribute( ATTR_USEMAP ).isEmpty()) { found = findImageMapRect(static_cast(e), viewportToContents(QPoint(0, 0)), p, r, s); } if (!found) { s = e->getAttribute( ATTR_TITLE ).string(); r = node->getRect(); } region |= QRect( contentsToViewport( r.topLeft() ), r.size() ); if ( !s.isEmpty() ) { QToolTip::showText( viewport()->mapToGlobal(region.bottomLeft()), Qt::convertFromPlainText( s, Qt::WhiteSpaceNormal ) ); break; } } node = node->parentNode(); } return true; } case QEvent::DragEnter: case QEvent::DragMove: case QEvent::DragLeave: case QEvent::Drop: // In Qt4, one needs to both call accept() on the DND event and return // true on ::event for the candidate widget for the drop to be possible. // Apps hosting us, such as konq, can do the former but not the later. // We will do the second bit, as it's a no-op unless someone else explicitly // accepts the event. We need to skip the scrollarea to do that, // since it will just skip the events, both killing the drop, and // not permitting us to forward it up the part hiearchy in our dragEnterEvent, // etc. handlers return QWidget::event(e); + case QEvent::StyleChange: + case QEvent::LayoutRequest: { + bool ret = QScrollArea::event(e); + if (d->staticWidget && widget()->pos() != QPoint(0,0)) + widget()->move(0,0); + return ret; + } default: return QScrollArea::event(e); } } #endif KHTMLView::KHTMLView( KHTMLPart *part, QWidget *parent ) : QScrollArea( parent ), d( new KHTMLViewPrivate ) { m_medium = "screen"; m_part = part; QScrollArea::setVerticalScrollBarPolicy(d->vpolicy); QScrollArea::setHorizontalScrollBarPolicy(d->hpolicy); connect(KGlobalSettings::self(), SIGNAL(kdisplayPaletteChanged()), this, SLOT(slotPaletteChanged())); #ifndef KHTML_NO_TYPE_AHEAD_FIND connect(&d->timer, SIGNAL(timeout()), this, SLOT(findTimeout())); #endif // KHTML_NO_TYPE_AHEAD_FIND init(); widget()->setMouseTracking(true); QTimer::singleShot(0, this, SLOT(delayedInit())); } KHTMLView::~KHTMLView() { closeChildDialogs(); if (m_part) { DOM::DocumentImpl *doc = m_part->xmlDocImpl(); if (doc) doc->detach(); } delete d; } void KHTMLView::setPart(KHTMLPart *part) { assert(part && !m_part); m_part = part; } void KHTMLView::init() { // Do not access the part here. It might not be fully constructed. setFrameStyle(QFrame::NoFrame); setFocusPolicy(Qt::StrongFocus); viewport()->setFocusProxy(this); _marginWidth = -1; // undefined _marginHeight = -1; _width = 0; _height = 0; installEventFilter(this); setAcceptDrops(true); if (!widget()) setWidget( new QWidget(this) ); widget()->setAttribute( Qt::WA_NoSystemBackground ); } void KHTMLView::delayedInit() { QSize s = viewport()->size(); resizeContents(s.width(), s.height()); } // called by KHTMLPart::clear() void KHTMLView::clear() { #ifndef KHTML_NO_CARET if (!m_part->isCaretMode() && !m_part->isEditable()) caretOff(); #endif #ifndef KHTML_NO_TYPE_AHEAD_FIND if( d->typeAheadActivated ) findTimeout(); #endif if (d->accessKeysEnabled && d->accessKeysActivated) accessKeysTimeout(); viewport()->unsetCursor(); if ( d->cursor_icon_widget ) d->cursor_icon_widget->hide(); d->reset(); QAbstractEventDispatcher::instance()->unregisterTimers(this); emit cleared(); QScrollArea::setHorizontalScrollBarPolicy(d->hpolicy); QScrollArea::setVerticalScrollBarPolicy(d->vpolicy); verticalScrollBar()->setEnabled( false ); horizontalScrollBar()->setEnabled( false ); if (m_kwp->isRedirected()) setHasStaticBackground(); } void KHTMLView::hideEvent(QHideEvent* e) { QScrollArea::hideEvent(e); if ( m_part && m_part->xmlDocImpl() ) m_part->xmlDocImpl()->docLoader()->pauseAnimations(); } void KHTMLView::showEvent(QShowEvent* e) { QScrollArea::showEvent(e); if ( m_part && m_part->xmlDocImpl() ) m_part->xmlDocImpl()->docLoader()->resumeAnimations(); } void KHTMLView::setMouseEventsTarget( QWidget* w ) { d->m_mouseEventsTarget = w; } QWidget* KHTMLView::mouseEventsTarget() const { return d->m_mouseEventsTarget; } void KHTMLView::setClipHolder( QStack* ch ) { d->m_clipHolder = ch; } QStack* KHTMLView::clipHolder() const { return d->m_clipHolder; } int KHTMLView::contentsWidth() const { return widget() ? widget()->width() : 0; } int KHTMLView::contentsHeight() const { return widget() ? widget()->height() : 0; } void KHTMLView::resizeContents(int w, int h) { if (!widget()) return; widget()->resize(w, h); } int KHTMLView::contentsX() const { return d->contentsX; } int KHTMLView::contentsY() const { return d->contentsY; } int KHTMLView::visibleWidth() const { if (m_kwp->isRedirected()) { // our RenderWidget knows better if (RenderWidget* rw = m_kwp->renderWidget()) { int ret = rw->width()-rw->paddingLeft()-rw->paddingRight()-rw->borderLeft()-rw->borderRight(); if (verticalScrollBar()->isVisible()) { ret -= style()->pixelMetric(QStyle::PM_ScrollBarExtent); int lhs = style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing); if (lhs > 0) ret -= lhs; ret = qMax(0, ret); } return ret; } } return viewport()->width(); } int KHTMLView::visibleHeight() const { if (m_kwp->isRedirected()) { // our RenderWidget knows better if (RenderWidget* rw = m_kwp->renderWidget()) { int ret = rw->height()-rw->paddingBottom()-rw->paddingTop()-rw->borderTop()-rw->borderBottom(); if (horizontalScrollBar()->isVisible()) { ret -= style()->pixelMetric(QStyle::PM_ScrollBarExtent); int lvs = style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing); if (lvs > 0) ret -= lvs; ret = qMax(0, ret); } return ret; } } return viewport()->height(); } void KHTMLView::setContentsPos( int x, int y) { horizontalScrollBar()->setValue( x ); verticalScrollBar()->setValue( y ); } void KHTMLView::scrollBy(int x, int y) { horizontalScrollBar()->setValue( horizontalScrollBar()->value()+x ); verticalScrollBar()->setValue( verticalScrollBar()->value()+y ); } QPoint KHTMLView::contentsToViewport(const QPoint& p) const { return QPoint(p.x()-contentsX(), p.y()-contentsY()); } void KHTMLView::contentsToViewport(int x, int y, int& cx, int& cy) const { QPoint p(x,y); p = contentsToViewport(p); cx = p.x(); cy = p.y(); } QPoint KHTMLView::viewportToContents(const QPoint& p) const { return QPoint(p.x()+contentsX(), p.y()+contentsY()); } void KHTMLView::viewportToContents(int x, int y, int& cx, int& cy) const { QPoint p(x,y); p = viewportToContents(p); cx = p.x(); cy = p.y(); } void KHTMLView::updateContents(int x, int y, int w, int h) { applyTransforms(x, y, w, h); if (m_kwp->isRedirected()) { QPoint off = m_kwp->absolutePos(); KHTMLView* pview = m_part->parentPart()->view(); pview->updateContents(x+off.x(), y+off.y(), w, h); } else widget()->update(x, y, w, h); } void KHTMLView::updateContents( const QRect& r ) { updateContents( r.x(), r.y(), r.width(), r.height() ); } void KHTMLView::repaintContents(int x, int y, int w, int h) { applyTransforms(x, y, w, h); if (m_kwp->isRedirected()) { QPoint off = m_kwp->absolutePos(); KHTMLView* pview = m_part->parentPart()->view(); pview->repaintContents(x+off.x(), y+off.y(), w, h); } else widget()->repaint(x, y, w, h); } void KHTMLView::repaintContents( const QRect& r ) { repaintContents( r.x(), r.y(), r.width(), r.height() ); } void KHTMLView::applyTransforms( int& x, int& y, int& w, int& h) const { if (d->staticWidget) { x -= contentsX(); y -= contentsY(); } if (d->haveZoom()) { const int z = d->zoomLevel; x = x*z/100; y = y*z/100; w = w*z/100; h = h*z/100; } } void KHTMLView::revertTransforms( int& x, int& y, int& w, int& h) const { if (d->staticWidget) { x += contentsX(); y += contentsY(); } if (d->haveZoom()) { const int z = d->zoomLevel; x = x*100/z; y = y*100/z; w = w*100/z; h = h*100/z; } } void KHTMLView::revertTransforms( int& x, int& y ) const { int dummy = 0; revertTransforms(x, y, dummy, dummy); } void KHTMLView::resizeEvent (QResizeEvent* e) { QScrollArea::resizeEvent( e ); if (d->layoutSchedulingEnabled) layout(); #ifndef KHTML_NO_CARET else { hideCaret(); recalcAndStoreCaretPos(); showCaret(); }/*end if*/ #endif if (d->staticWidget && widget()->pos() != QPoint(0,0)) widget()->move(0,0); QApplication::sendPostedEvents(viewport(), QEvent::Paint); if ( m_part && m_part->xmlDocImpl() ) m_part->xmlDocImpl()->dispatchWindowEvent( EventImpl::RESIZE_EVENT, false, false ); } void KHTMLView::paintEvent( QPaintEvent *e ) { QPainter p(widget()); QRect r = e->rect(); QRect v(contentsX(), contentsY(), visibleWidth(), visibleHeight()); if (d->staticWidget) { QPoint off(contentsX(),contentsY()); p.translate(-off); r.translate(off); } r = r.intersect(v); if (!r.isValid() || r.isEmpty()) return; if (d->haveZoom()) { p.scale( d->zoomLevel/100., d->zoomLevel/100.); r.setX(r.x()*100/d->zoomLevel); r.setY(r.y()*100/d->zoomLevel); r.setWidth(r.width()*100/d->zoomLevel); r.setHeight(r.height()*100/d->zoomLevel); r.adjust(-1,-1,1,1); } p.setClipRect(r); int ex = r.x(); int ey = r.y(); int ew = r.width(); int eh = r.height(); if(!m_part || !m_part->xmlDocImpl() || !m_part->xmlDocImpl()->renderer()) { p.fillRect(ex, ey, ew, eh, palette().brush(QPalette::Active, QPalette::Base)); return; } else if ( d->complete && static_cast(m_part->xmlDocImpl()->renderer())->needsLayout() ) { // an external update request happens while we have a layout scheduled unscheduleRelayout(); layout(); } if (d->painting) { kDebug( 6000 ) << "WARNING: paintEvent reentered! "; kDebug( 6000 ) << kBacktrace(); return; } d->painting = true; m_part->xmlDocImpl()->renderer()->layer()->paint(&p, r); #ifndef KHTML_NO_CARET if (d->m_caretViewContext && d->m_caretViewContext->visible) { QRect pos(d->m_caretViewContext->x, d->m_caretViewContext->y, d->m_caretViewContext->width, d->m_caretViewContext->height); if (pos.intersects(QRect(ex, ey, ew, eh))) { p.setCompositionMode(QPainter::CompositionMode_Xor); p.setPen(Qt::white); if (pos.width() == 1) p.drawLine(pos.topLeft(), pos.bottomRight()); else { p.fillRect(pos, Qt::white); } } } #endif // KHTML_NO_CARET khtml::DrawContentsEvent event( &p, ex, ey, ew, eh ); QApplication::sendEvent( m_part, &event ); if (d->contentsMoving && widget()->underMouse()) { QMouseEvent *tempEvent = new QMouseEvent( QEvent::MouseMove, widget()->mapFromGlobal( QCursor::pos() ), Qt::NoButton, Qt::NoButton, Qt::NoModifier ); QApplication::postEvent(widget(), tempEvent); } d->painting = false; } void KHTMLView::setMarginWidth(int w) { // make it update the rendering area when set _marginWidth = w; } void KHTMLView::setMarginHeight(int h) { // make it update the rendering area when set _marginHeight = h; } void KHTMLView::layout() { if( m_part && m_part->xmlDocImpl() ) { DOM::DocumentImpl *document = m_part->xmlDocImpl(); khtml::RenderCanvas* canvas = static_cast(document->renderer()); if ( !canvas ) return; d->layoutSchedulingEnabled=false; // the reference object for the overflow property on canvas RenderObject * ref = 0; RenderObject* root = document->documentElement() ? document->documentElement()->renderer() : 0; if (document->isHTMLDocument()) { NodeImpl *body = static_cast(document)->body(); if(body && body->renderer() && body->id() == ID_FRAMESET) { QScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); QScrollArea::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); body->renderer()->setNeedsLayout(true); } else if (root) // only apply body's overflow to canvas if root has a visible overflow ref = (!body || root->style()->hidesOverflow()) ? root : body->renderer(); } else { ref = root; } if (ref) { if( ref->style()->overflowX() == OHIDDEN ) { if (d->hpolicy == Qt::ScrollBarAsNeeded) QScrollArea::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); } else if (ref->style()->overflowX() == OSCROLL ) { if (d->hpolicy == Qt::ScrollBarAsNeeded) QScrollArea::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); } else if (horizontalScrollBarPolicy() != d->hpolicy) { QScrollArea::setHorizontalScrollBarPolicy(d->hpolicy); } if ( ref->style()->overflowY() == OHIDDEN ) { if (d->vpolicy == Qt::ScrollBarAsNeeded) QScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); } else if (ref->style()->overflowY() == OSCROLL ) { if (d->vpolicy == Qt::ScrollBarAsNeeded) QScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); } else if (verticalScrollBarPolicy() != d->vpolicy) { QScrollArea::setVerticalScrollBarPolicy(d->vpolicy); } } d->needsFullRepaint = d->firstRelayout; if (_height != visibleHeight() || _width != visibleWidth()) {; d->needsFullRepaint = true; _height = visibleHeight(); _width = visibleWidth(); } canvas->layout(); emit finishedLayout(); if (d->firstRelayout) { // make sure firstRelayout is set to false now in case this layout // wasn't scheduled d->firstRelayout = false; verticalScrollBar()->setEnabled( true ); horizontalScrollBar()->setEnabled( true ); } #ifndef KHTML_NO_CARET hideCaret(); if ((m_part->isCaretMode() || m_part->isEditable()) && !d->complete && d->m_caretViewContext && !d->m_caretViewContext->caretMoved) { initCaret(); } else { recalcAndStoreCaretPos(); showCaret(); }/*end if*/ #endif if (d->accessKeysEnabled && d->accessKeysActivated) { emit hideAccessKeys(); displayAccessKeys(); } } else _width = visibleWidth(); if (d->layoutTimerId) killTimer(d->layoutTimerId); d->layoutTimerId = 0; d->layoutSchedulingEnabled=true; } void KHTMLView::closeChildDialogs() { QList dlgs = findChildren(); foreach (QDialog *dlg, dlgs) { KDialog* dlgbase = dynamic_cast( dlg ); if ( dlgbase ) { if ( dlgbase->testAttribute( Qt::WA_ShowModal ) ) { kDebug(6000) << "closeChildDialogs: closing dialog " << dlgbase; // close() ends up calling QButton::animateClick, which isn't immediate // we need something the exits the event loop immediately (#49068) dlgbase->reject(); } } else { kWarning() << "closeChildDialogs: not a KDialog! Don't use QDialogs in KDE! " << static_cast(dlg); static_cast(dlg)->hide(); } } d->m_dialogsAllowed = false; } bool KHTMLView::dialogsAllowed() { bool allowed = d->m_dialogsAllowed; KHTMLPart* p = m_part->parentPart(); if (p && p->view()) allowed &= p->view()->dialogsAllowed(); return allowed; } void KHTMLView::closeEvent( QCloseEvent* ev ) { closeChildDialogs(); QScrollArea::closeEvent( ev ); } void KHTMLView::setZoomLevel(int percent) { percent = percent < 20 ? 20 : (percent > 800 ? 800 : percent); int oldpercent = d->zoomLevel; d->zoomLevel = percent; if (percent != oldpercent) { if (d->layoutSchedulingEnabled) layout(); widget()->update(); } } int KHTMLView::zoomLevel() const { return d->zoomLevel; } // // Event Handling // ///////////////// void KHTMLView::mousePressEvent( QMouseEvent *_mouse ) { if (!m_part->xmlDocImpl()) return; if (d->possibleTripleClick && ( _mouse->button() & Qt::MouseButtonMask ) == Qt::LeftButton) { mouseDoubleClickEvent( _mouse ); // it handles triple clicks too return; } int xm = _mouse->x(); int ym = _mouse->y(); revertTransforms(xm, ym); // kDebug( 6000 ) << "mousePressEvent: viewport=("<<_mouse->x()-contentsX()<<"/"<<_mouse->y()-contentsY()<<"), contents=(" << xm << "/" << ym << ")\n"; d->isDoubleClick = false; DOM::NodeImpl::MouseEvent mev( _mouse->buttons(), DOM::NodeImpl::MousePress ); m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev ); //kDebug(6000) << "innerNode="<button() == Qt::MidButton) && !m_part->d->m_bOpenMiddleClick && !d->m_mouseScrollTimer && mev.url.isNull() && (mev.innerNode.elementId() != ID_INPUT) ) { QPoint point = mapFromGlobal( _mouse->globalPos() ); d->m_mouseScroll_byX = 0; d->m_mouseScroll_byY = 0; d->m_mouseScrollTimer = new QTimer( this ); connect( d->m_mouseScrollTimer, SIGNAL(timeout()), this, SLOT(slotMouseScrollTimer()) ); if ( !d->m_mouseScrollIndicator ) { QPixmap pixmap( 48, 48 ), icon; pixmap.fill( QColor( qRgba( 127, 127, 127, 127 ) ) ); QPainter p( &pixmap ); QStyleOption option; option.rect.setRect( 16, 0, 16, 16 ); QApplication::style()->drawPrimitive( QStyle::PE_IndicatorArrowUp, &option, &p ); option.rect.setRect( 0, 16, 16, 16 ); QApplication::style()->drawPrimitive( QStyle::PE_IndicatorArrowLeft, &option, &p ); option.rect.setRect( 16, 32, 16, 16 ); QApplication::style()->drawPrimitive( QStyle::PE_IndicatorArrowDown, &option, &p ); option.rect.setRect( 32, 16, 16, 16 ); QApplication::style()->drawPrimitive( QStyle::PE_IndicatorArrowRight, &option, &p ); p.drawEllipse( 23, 23, 2, 2 ); d->m_mouseScrollIndicator = new QWidget( this ); d->m_mouseScrollIndicator->setFixedSize( 48, 48 ); QPalette palette; palette.setBrush( d->m_mouseScrollIndicator->backgroundRole(), QBrush( pixmap ) ); d->m_mouseScrollIndicator->setPalette( palette ); } d->m_mouseScrollIndicator->move( point.x()-24, point.y()-24 ); bool hasHorBar = visibleWidth() < contentsWidth(); bool hasVerBar = visibleHeight() < contentsHeight(); KConfigGroup cg( KGlobal::config(), "HTML Settings" ); if ( cg.readEntry( "ShowMouseScrollIndicator", true ) ) { d->m_mouseScrollIndicator->show(); d->m_mouseScrollIndicator->unsetCursor(); QBitmap mask = d->m_mouseScrollIndicator->palette().brush(d->m_mouseScrollIndicator->backgroundRole()).texture().createHeuristicMask( true ); if ( hasHorBar && !hasVerBar ) { QBitmap bm( 16, 16 ); bm.clear(); QPainter painter( &mask ); painter.drawPixmap( QRectF( 16, 0, bm.width(), bm.height() ), bm, bm.rect() ); painter.drawPixmap( QRectF( 16, 32, bm.width(), bm.height() ), bm, bm.rect() ); d->m_mouseScrollIndicator->setCursor( Qt::SizeHorCursor ); } else if ( !hasHorBar && hasVerBar ) { QBitmap bm( 16, 16 ); bm.clear(); QPainter painter( &mask ); painter.drawPixmap( QRectF( 0, 16, bm.width(), bm.height() ), bm, bm.rect() ); painter.drawPixmap( QRectF( 32, 16, bm.width(), bm.height() ), bm, bm.rect() ); d->m_mouseScrollIndicator->setCursor( Qt::SizeVerCursor ); } else d->m_mouseScrollIndicator->setCursor( Qt::SizeAllCursor ); d->m_mouseScrollIndicator->setMask( mask ); } else { if ( hasHorBar && !hasVerBar ) viewport()->setCursor( Qt::SizeHorCursor ); else if ( !hasHorBar && hasVerBar ) viewport()->setCursor( Qt::SizeVerCursor ); else viewport()->setCursor( Qt::SizeAllCursor ); } return; } else if ( d->m_mouseScrollTimer ) { delete d->m_mouseScrollTimer; d->m_mouseScrollTimer = 0; if ( d->m_mouseScrollIndicator ) d->m_mouseScrollIndicator->hide(); } d->clickCount = 1; d->clickX = xm; d->clickY = ym; bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEDOWN_EVENT,mev.innerNode.handle(),mev.innerNonSharedNode.handle(),true, d->clickCount,_mouse,true,DOM::NodeImpl::MousePress); if (!swallowEvent) { emit m_part->nodeActivated(mev.innerNode); khtml::MousePressEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode ); QApplication::sendEvent( m_part, &event ); // we might be deleted after this } } void KHTMLView::mouseDoubleClickEvent( QMouseEvent *_mouse ) { if(!m_part->xmlDocImpl()) return; int xm = _mouse->x(); int ym = _mouse->y(); revertTransforms(xm, ym); // kDebug( 6000 ) << "mouseDblClickEvent: x=" << xm << ", y=" << ym; d->isDoubleClick = true; DOM::NodeImpl::MouseEvent mev( _mouse->buttons(), DOM::NodeImpl::MouseDblClick ); m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev ); // We do the same thing as mousePressEvent() here, since the DOM does not treat // single and double-click events as separate (only the detail, i.e. number of clicks differs) if (d->clickCount > 0 && QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() <= QApplication::startDragDistance()) d->clickCount++; else { // shouldn't happen, if Qt has the same criterias for double clicks. d->clickCount = 1; d->clickX = xm; d->clickY = ym; } bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEDOWN_EVENT,mev.innerNode.handle(),mev.innerNonSharedNode.handle(),true, d->clickCount,_mouse,true,DOM::NodeImpl::MouseDblClick); khtml::RenderObject* r = mev.innerNode.handle() ? mev.innerNode.handle()->renderer() : 0; if (r && r->isWidget()) _mouse->ignore(); if (!swallowEvent) { khtml::MouseDoubleClickEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode, d->clickCount ); QApplication::sendEvent( m_part, &event ); } d->possibleTripleClick=true; QTimer::singleShot(QApplication::doubleClickInterval(),this,SLOT(tripleClickTimeout())); } void KHTMLView::tripleClickTimeout() { d->possibleTripleClick = false; d->clickCount = 0; } static bool targetOpensNewWindow(KHTMLPart *part, QString target) { if (!target.isEmpty() && (target.toLower() != "_top") && (target.toLower() != "_self") && (target.toLower() != "_parent")) { if (target.toLower() == "_blank") return true; else { while (part->parentPart()) part = part->parentPart(); if (!part->frameExists(target)) return true; } } return false; } void KHTMLView::mouseMoveEvent( QMouseEvent * _mouse ) { if ( d->m_mouseScrollTimer ) { QPoint point = mapFromGlobal( _mouse->globalPos() ); int deltaX = point.x() - d->m_mouseScrollIndicator->x() - 24; int deltaY = point.y() - d->m_mouseScrollIndicator->y() - 24; (deltaX > 0) ? d->m_mouseScroll_byX = 1 : d->m_mouseScroll_byX = -1; (deltaY > 0) ? d->m_mouseScroll_byY = 1 : d->m_mouseScroll_byY = -1; double adX = qAbs(deltaX)/30.0; double adY = qAbs(deltaY)/30.0; d->m_mouseScroll_byX = qMax(qMin(d->m_mouseScroll_byX * int(adX*adX), SHRT_MAX), SHRT_MIN); d->m_mouseScroll_byY = qMax(qMin(d->m_mouseScroll_byY * int(adY*adY), SHRT_MAX), SHRT_MIN); if (d->m_mouseScroll_byX == 0 && d->m_mouseScroll_byY == 0) { d->m_mouseScrollTimer->stop(); } else if (!d->m_mouseScrollTimer->isActive()) { d->m_mouseScrollTimer->start( 20 ); } } if(!m_part->xmlDocImpl()) return; int xm = _mouse->x(); int ym = _mouse->y(); revertTransforms(xm, ym); DOM::NodeImpl::MouseEvent mev( _mouse->buttons(), DOM::NodeImpl::MouseMove ); // Do not modify :hover/:active state while mouse is pressed. m_part->xmlDocImpl()->prepareMouseEvent( _mouse->buttons() /*readonly ?*/, xm, ym, &mev ); // kDebug(6000) << "mouse move: " << _mouse->pos() // << " button " << _mouse->button() // << " state " << _mouse->state() << endl; DOM::NodeImpl* target = mev.innerNode.handle(); DOM::NodeImpl* fn = m_part->xmlDocImpl()->focusNode(); // a widget may be the real target of this event (e.g. if a scrollbar's slider is being moved) if (d->m_mouseEventsTarget && fn && fn->renderer() && fn->renderer()->isWidget()) target = fn; bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEMOVE_EVENT,target,mev.innerNonSharedNode.handle(),false, 0,_mouse,true,DOM::NodeImpl::MouseMove); if (d->clickCount > 0 && QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() > QApplication::startDragDistance()) { d->clickCount = 0; // moving the mouse outside the threshold invalidates the click } // execute the scheduled script. This is to make sure the mouseover events come after the mouseout events m_part->executeScheduledScript(); khtml::RenderObject* r = target ? target->renderer() : 0; bool setCursor = true; if (r && r->isWidget()) { RenderWidget* rw = static_cast(r); KHTMLWidget* kw = qobject_cast(rw->widget())? dynamic_cast(rw->widget()) : 0; if (kw && kw->m_kwp->isRedirected()) setCursor = false; } khtml::RenderStyle* style = (r && r->style()) ? r->style() : 0; QCursor c; LinkCursor linkCursor = LINK_NORMAL; switch ( style ? style->cursor() : CURSOR_AUTO) { case CURSOR_AUTO: if ( r && r->isText() ) c = QCursor(Qt::IBeamCursor); if ( mev.url.length() && m_part->settings()->changeCursor() ) { c = m_part->urlCursor(); if (mev.url.string().startsWith("mailto:") && mev.url.string().indexOf('@')>0) linkCursor = LINK_MAILTO; else if ( targetOpensNewWindow( m_part, mev.target.string() ) ) linkCursor = LINK_NEWWINDOW; } if (r && r->isFrameSet() && !static_cast(r)->noResize()) c = QCursor(static_cast(r)->cursorShape()); break; case CURSOR_CROSS: c = QCursor(Qt::CrossCursor); break; case CURSOR_POINTER: c = m_part->urlCursor(); if (mev.url.string().startsWith("mailto:") && mev.url.string().indexOf('@')>0) linkCursor = LINK_MAILTO; else if ( targetOpensNewWindow( m_part, mev.target.string() ) ) linkCursor = LINK_NEWWINDOW; break; case CURSOR_PROGRESS: c = QCursor(Qt::BusyCursor); // working_cursor break; case CURSOR_MOVE: case CURSOR_ALL_SCROLL: c = QCursor(Qt::SizeAllCursor); break; case CURSOR_E_RESIZE: case CURSOR_W_RESIZE: case CURSOR_EW_RESIZE: c = QCursor(Qt::SizeHorCursor); break; case CURSOR_N_RESIZE: case CURSOR_S_RESIZE: case CURSOR_NS_RESIZE: c = QCursor(Qt::SizeVerCursor); break; case CURSOR_NE_RESIZE: case CURSOR_SW_RESIZE: case CURSOR_NESW_RESIZE: c = QCursor(Qt::SizeBDiagCursor); break; case CURSOR_NW_RESIZE: case CURSOR_SE_RESIZE: case CURSOR_NWSE_RESIZE: c = QCursor(Qt::SizeFDiagCursor); break; case CURSOR_TEXT: c = QCursor(Qt::IBeamCursor); break; case CURSOR_WAIT: c = QCursor(Qt::WaitCursor); break; case CURSOR_HELP: c = QCursor(Qt::WhatsThisCursor); break; case CURSOR_DEFAULT: break; case CURSOR_NONE: case CURSOR_NOT_ALLOWED: c = QCursor(Qt::ForbiddenCursor); break; case CURSOR_ROW_RESIZE: c = QCursor(Qt::SplitVCursor); break; case CURSOR_COL_RESIZE: c = QCursor(Qt::SplitHCursor); break; case CURSOR_VERTICAL_TEXT: case CURSOR_CONTEXT_MENU: case CURSOR_NO_DROP: case CURSOR_CELL: case CURSOR_COPY: case CURSOR_ALIAS: c = QCursor(Qt::ArrowCursor); break; } if (!setCursor && style && style->cursor() != CURSOR_AUTO) setCursor = true; QWidget* vp = viewport(); for (KHTMLPart* p = m_part; p; p = p->parentPart()) if (!p->parentPart()) vp = p->view()->viewport(); if ( setCursor && vp->cursor().handle() != c.handle() ) { if( c.shape() == Qt::ArrowCursor) { for (KHTMLPart* p = m_part; p; p = p->parentPart()) p->view()->viewport()->unsetCursor(); } else { vp->setCursor( c ); } } if ( linkCursor!=LINK_NORMAL && isVisible() && hasFocus() ) { #ifdef Q_WS_X11 QString cursorIcon; switch (linkCursor) { case LINK_MAILTO: cursorIcon = "mail-message-new"; break; case LINK_NEWWINDOW: cursorIcon = "window-new"; break; default: cursorIcon = "dialog-error"; break; } QPixmap icon_pixmap = KHTMLGlobal::iconLoader()->loadIcon( cursorIcon, KIconLoader::Small, 0, KIconLoader::DefaultState, QStringList(), 0, true ); #if 0 if (d->cursor_icon_widget) { const QPixmap *pm = d->cursor_icon_widget->backgroundPixmap(); if (!pm || pm->serialNumber()!=icon_pixmap.serialNumber()) { delete d->cursor_icon_widget; d->cursor_icon_widget = NULL; } } #endif if( !d->cursor_icon_widget ) { #ifdef Q_WS_X11 d->cursor_icon_widget = new QWidget( 0, Qt::X11BypassWindowManagerHint ); XSetWindowAttributes attr; attr.save_under = True; XChangeWindowAttributes( QX11Info::display(), d->cursor_icon_widget->winId(), CWSaveUnder, &attr ); #else d->cursor_icon_widget = new QWidget( NULL, NULL ); //TODO #endif d->cursor_icon_widget->resize( icon_pixmap.width(), icon_pixmap.height()); if( !icon_pixmap.mask().isNull() ) d->cursor_icon_widget->setMask( icon_pixmap.mask()); else d->cursor_icon_widget->clearMask(); QPalette palette; palette.setBrush( d->cursor_icon_widget->backgroundRole(), QBrush( icon_pixmap ) ); d->cursor_icon_widget->setPalette( palette ); d->cursor_icon_widget->update(); } QPoint c_pos = QCursor::pos(); d->cursor_icon_widget->move( c_pos.x() + 15, c_pos.y() + 15 ); #ifdef Q_WS_X11 XRaiseWindow( QX11Info::display(), d->cursor_icon_widget->winId()); QApplication::flush(); #elif defined(Q_WS_WIN) SetWindowPos( d->cursor_icon_widget->winId(), HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE ); #else //TODO? #endif d->cursor_icon_widget->show(); #endif } else if ( d->cursor_icon_widget ) d->cursor_icon_widget->hide(); if (r && r->isWidget()) { _mouse->ignore(); } if (!swallowEvent) { khtml::MouseMoveEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode ); QApplication::sendEvent( m_part, &event ); } } void KHTMLView::mouseReleaseEvent( QMouseEvent * _mouse ) { bool swallowEvent = false; int xm = _mouse->x(); int ym = _mouse->y(); revertTransforms(xm, ym); DOM::NodeImpl::MouseEvent mev( _mouse->buttons(), DOM::NodeImpl::MouseRelease ); if ( m_part->xmlDocImpl() ) { m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev ); DOM::NodeImpl* target = mev.innerNode.handle(); DOM::NodeImpl* fn = m_part->xmlDocImpl()->focusNode(); // a widget may be the real target of this event (e.g. if a scrollbar's slider is being moved) if (d->m_mouseEventsTarget && fn && fn->renderer() && fn->renderer()->isWidget()) target = fn; swallowEvent = dispatchMouseEvent(EventImpl::MOUSEUP_EVENT,target,mev.innerNonSharedNode.handle(),true, d->clickCount,_mouse,false,DOM::NodeImpl::MouseRelease); // clear our sticky event target on any mouseRelease event if (d->m_mouseEventsTarget) d->m_mouseEventsTarget = 0; if (d->clickCount > 0 && QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() <= QApplication::startDragDistance()) { QMouseEvent me(d->isDoubleClick ? QEvent::MouseButtonDblClick : QEvent::MouseButtonRelease, _mouse->pos(), _mouse->button(), _mouse->buttons(), _mouse->modifiers()); dispatchMouseEvent(EventImpl::CLICK_EVENT, mev.innerNode.handle(),mev.innerNonSharedNode.handle(),true, d->clickCount, &me, true, DOM::NodeImpl::MouseRelease); } khtml::RenderObject* r = target ? target->renderer() : 0; if (r && r->isWidget()) _mouse->ignore(); } if (!swallowEvent) { khtml::MouseReleaseEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode ); QApplication::sendEvent( m_part, &event ); } } // returns true if event should be swallowed bool KHTMLView::dispatchKeyEvent( QKeyEvent *_ke ) { if (!m_part->xmlDocImpl()) return false; // Pressing and releasing a key should generate keydown, keypress and keyup events // Holding it down should generated keydown, keypress (repeatedly) and keyup events // The problem here is that Qt generates two autorepeat events (keyrelease+keypress) // for autorepeating, while DOM wants only one autorepeat event (keypress), so one // of the Qt events shouldn't be passed to DOM, but it should be still filtered // out if DOM would filter the autorepeat event. Additional problem is that Qt keyrelease // events don't have text() set (Qt bug?), so DOM often would ignore the keypress event // if it was created using Qt keyrelease, but Qt autorepeat keyrelease comes // before Qt autorepeat keypress (i.e. problem whether to filter it out or not). // The solution is to filter out and postpone the Qt autorepeat keyrelease until // the following Qt keypress event comes. If DOM accepts the DOM keypress event, // the postponed event will be simply discarded. If not, it will be passed to keyPressEvent() // again, and here it will be ignored. // // Qt: Press | Release(autorepeat) Press(autorepeat) etc. | Release // DOM: Down + Press | (nothing) Press | Up // It's also possible to get only Releases. E.g. the release of alt-tab, // or when the keypresses get captured by an accel. if( _ke == d->postponed_autorepeat ) // replayed event { return false; } if( _ke->type() == QEvent::KeyPress ) { if( !_ke->isAutoRepeat()) { bool ret = dispatchKeyEventHelper( _ke, false ); // keydown // don't send keypress even if keydown was blocked, like IE (and unlike Mozilla) if( !ret && dispatchKeyEventHelper( _ke, true )) // keypress ret = true; return ret; } else // autorepeat { bool ret = dispatchKeyEventHelper( _ke, true ); // keypress if( !ret && d->postponed_autorepeat ) keyPressEvent( d->postponed_autorepeat ); delete d->postponed_autorepeat; d->postponed_autorepeat = NULL; return ret; } } else // QEvent::KeyRelease { // Discard postponed "autorepeat key-release" events that didn't see // a keypress after them (e.g. due to QAccel) if ( d->postponed_autorepeat ) { delete d->postponed_autorepeat; d->postponed_autorepeat = 0; } if( !_ke->isAutoRepeat()) { return dispatchKeyEventHelper( _ke, false ); // keyup } else { d->postponed_autorepeat = new QKeyEvent( _ke->type(), _ke->key(), _ke->modifiers(), _ke->text(), _ke->isAutoRepeat(), _ke->count()); if( _ke->isAccepted()) d->postponed_autorepeat->accept(); else d->postponed_autorepeat->ignore(); return true; } } } // returns true if event should be swallowed bool KHTMLView::dispatchKeyEventHelper( QKeyEvent *_ke, bool keypress ) { DOM::NodeImpl* keyNode = m_part->xmlDocImpl()->focusNode(); if (keyNode) { return keyNode->dispatchKeyEvent(_ke, keypress); } else { // no focused node, send to document return m_part->xmlDocImpl()->dispatchKeyEvent(_ke, keypress); } } void KHTMLView::keyPressEvent( QKeyEvent *_ke ) { #ifndef KHTML_NO_TYPE_AHEAD_FIND if(d->typeAheadActivated) { // type-ahead find aka find-as-you-type if(_ke->key() == Qt::Key_Backspace) { d->findString = d->findString.left(d->findString.length() - 1); if(!d->findString.isEmpty()) { findAhead(false); } else { findTimeout(); } d->timer.setSingleShot(true); d->timer.start(3000); _ke->accept(); return; } else if(_ke->key() == Qt::Key_Escape) { findTimeout(); _ke->accept(); return; } else if(_ke->key() == Qt::Key_Space || !_ke->text().trimmed().isEmpty()) { d->findString += _ke->text(); findAhead(true); d->timer.setSingleShot(true); d->timer.start(3000); _ke->accept(); return; } } #endif // KHTML_NO_TYPE_AHEAD_FIND #ifndef KHTML_NO_CARET if (m_part->isEditable() || m_part->isCaretMode() || (m_part->xmlDocImpl() && m_part->xmlDocImpl()->focusNode() && m_part->xmlDocImpl()->focusNode()->contentEditable())) { d->caretViewContext()->keyReleasePending = true; caretKeyPressEvent(_ke); return; } #endif // KHTML_NO_CARET // If CTRL was hit, be prepared for access keys if (d->accessKeysEnabled && _ke->key() == Qt::Key_Control && _ke->modifiers()==0 && !d->accessKeysActivated) { d->accessKeysPreActivate=true; _ke->accept(); return; } if (_ke->key() == Qt::Key_Shift && _ke->modifiers()==0) d->scrollSuspendPreActivate=true; // accesskey handling needs to be done before dispatching, otherwise e.g. lineedits // may eat the event if (d->accessKeysEnabled && d->accessKeysActivated) { int state = ( _ke->modifiers() & ( Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier )); if ( state==0 || state==Qt::ShiftModifier) { if (_ke->key() != Qt::Key_Shift) accessKeysTimeout(); handleAccessKey( _ke ); _ke->accept(); return; } accessKeysTimeout(); } if ( dispatchKeyEvent( _ke )) { // If either keydown or keypress was accepted by a widget, or canceled by JS, stop here. _ke->accept(); return; } int offs = (viewport()->height() < 30) ? viewport()->height() : 30; // ### ?? if (_ke->modifiers() & Qt::ShiftModifier) switch(_ke->key()) { case Qt::Key_Space: verticalScrollBar()->setValue( verticalScrollBar()->value() -viewport()->height() + offs ); if(d->scrollSuspended) d->newScrollTimer(this, 0); break; case Qt::Key_Down: case Qt::Key_J: d->adjustScroller(this, KHTMLViewPrivate::ScrollDown, KHTMLViewPrivate::ScrollUp); break; case Qt::Key_Up: case Qt::Key_K: d->adjustScroller(this, KHTMLViewPrivate::ScrollUp, KHTMLViewPrivate::ScrollDown); break; case Qt::Key_Left: case Qt::Key_H: d->adjustScroller(this, KHTMLViewPrivate::ScrollLeft, KHTMLViewPrivate::ScrollRight); break; case Qt::Key_Right: case Qt::Key_L: d->adjustScroller(this, KHTMLViewPrivate::ScrollRight, KHTMLViewPrivate::ScrollLeft); break; } else switch ( _ke->key() ) { case Qt::Key_Down: case Qt::Key_J: if (!d->scrollTimerId || d->scrollSuspended) verticalScrollBar()->setValue( verticalScrollBar()->value()+10 ); if (d->scrollTimerId) d->newScrollTimer(this, 0); break; case Qt::Key_Space: case Qt::Key_PageDown: verticalScrollBar()->setValue( verticalScrollBar()->value() +viewport()->height() - offs ); if(d->scrollSuspended) d->newScrollTimer(this, 0); break; case Qt::Key_Up: case Qt::Key_K: if (!d->scrollTimerId || d->scrollSuspended) verticalScrollBar()->setValue( verticalScrollBar()->value()-10 ); if (d->scrollTimerId) d->newScrollTimer(this, 0); break; case Qt::Key_PageUp: verticalScrollBar()->setValue( verticalScrollBar()->value() -viewport()->height() + offs ); if(d->scrollSuspended) d->newScrollTimer(this, 0); break; case Qt::Key_Right: case Qt::Key_L: if (!d->scrollTimerId || d->scrollSuspended) horizontalScrollBar()->setValue( horizontalScrollBar()->value()+10 ); if (d->scrollTimerId) d->newScrollTimer(this, 0); break; case Qt::Key_Left: case Qt::Key_H: if (!d->scrollTimerId || d->scrollSuspended) horizontalScrollBar()->setValue( horizontalScrollBar()->value()-10 ); if (d->scrollTimerId) d->newScrollTimer(this, 0); break; case Qt::Key_Enter: case Qt::Key_Return: // ### FIXME: // or even better to HTMLAnchorElementImpl::event() if (m_part->xmlDocImpl()) { NodeImpl *n = m_part->xmlDocImpl()->focusNode(); if (n) n->setActive(); } break; case Qt::Key_Home: verticalScrollBar()->setValue( 0 ); horizontalScrollBar()->setValue( 0 ); if(d->scrollSuspended) d->newScrollTimer(this, 0); break; case Qt::Key_End: verticalScrollBar()->setValue( contentsHeight() - visibleHeight() ); if(d->scrollSuspended) d->newScrollTimer(this, 0); break; case Qt::Key_Shift: // what are you doing here? _ke->ignore(); return; default: if (d->scrollTimerId) d->newScrollTimer(this, 0); _ke->ignore(); return; } _ke->accept(); } void KHTMLView::findTimeout() { #ifndef KHTML_NO_TYPE_AHEAD_FIND d->typeAheadActivated = false; d->findString = ""; m_part->setStatusBarText(i18n("Find stopped."), KHTMLPart::BarDefaultText); m_part->enableFindAheadActions( true ); #endif // KHTML_NO_TYPE_AHEAD_FIND } #ifndef KHTML_NO_TYPE_AHEAD_FIND void KHTMLView::startFindAhead( bool linksOnly ) { if( linksOnly ) { d->findLinksOnly = true; m_part->setStatusBarText(i18n("Starting -- find links as you type"), KHTMLPart::BarDefaultText); } else { d->findLinksOnly = false; m_part->setStatusBarText(i18n("Starting -- find text as you type"), KHTMLPart::BarDefaultText); } m_part->findTextBegin(); d->typeAheadActivated = true; // disable, so that the shortcut ( / or ' by default ) doesn't interfere m_part->enableFindAheadActions( false ); d->timer.setSingleShot(true); d->timer.start(3000); } void KHTMLView::findAhead(bool increase) { QString status; QString text = d->findString.toLower(); if(d->findLinksOnly) { m_part->findText(d->findString, KHTMLPart::FindNoPopups | KHTMLPart::FindLinksOnly, this); if(m_part->findTextNext()) { status = i18n("Link found: \"%1\".", text); } else { if(increase) KNotification::beep(); status = i18n("Link not found: \"%1\".", text); } } else { m_part->findText(d->findString, KHTMLPart::FindNoPopups, this); if(m_part->findTextNext()) { status = i18n("Text found: \"%1\".", text); } else { if(increase) KNotification::beep(); status = i18n("Text not found: \"%1\".", text); } } m_part->setStatusBarText(status, KHTMLPart::BarDefaultText); } void KHTMLView::updateFindAheadTimeout() { if( d->typeAheadActivated ) { d->timer.setSingleShot( true ); d->timer.start( 3000 ); } } #endif // KHTML_NO_TYPE_AHEAD_FIND void KHTMLView::keyReleaseEvent(QKeyEvent *_ke) { #ifndef KHTML_NO_TYPE_AHEAD_FIND if(d->typeAheadActivated) { _ke->accept(); return; } #endif if (d->m_caretViewContext && d->m_caretViewContext->keyReleasePending) { //caretKeyReleaseEvent(_ke); d->m_caretViewContext->keyReleasePending = false; return; } if( d->scrollSuspendPreActivate && _ke->key() != Qt::Key_Shift ) d->scrollSuspendPreActivate = false; if( _ke->key() == Qt::Key_Shift && d->scrollSuspendPreActivate && _ke->modifiers() == Qt::ShiftModifier && !(QApplication::keyboardModifiers() & Qt::ShiftModifier)) if (d->scrollTimerId) d->scrollSuspended = !d->scrollSuspended; if (d->accessKeysEnabled) { if (d->accessKeysPreActivate && _ke->key() != Qt::Key_Control) d->accessKeysPreActivate=false; if (d->accessKeysPreActivate && _ke->modifiers() == Qt::ControlModifier && !(QApplication::keyboardModifiers() & Qt::ControlModifier)) { displayAccessKeys(); m_part->setStatusBarText(i18n("Access Keys activated"),KHTMLPart::BarOverrideText); d->accessKeysActivated = true; d->accessKeysPreActivate = false; _ke->accept(); return; } else if (d->accessKeysActivated) { accessKeysTimeout(); _ke->accept(); return; } } // Send keyup event if ( dispatchKeyEvent( _ke ) ) { _ke->accept(); return; } QScrollArea::keyReleaseEvent(_ke); } bool KHTMLView::focusNextPrevChild( bool next ) { // Now try to find the next child if (m_part->xmlDocImpl() && focusNextPrevNode(next)) { if (m_part->xmlDocImpl()->focusNode()) kDebug() << "focusNode.name: " << m_part->xmlDocImpl()->focusNode()->nodeName().string() << endl; return true; // focus node found } // If we get here, pass tabbing control up to the next/previous child in our parent d->pseudoFocusNode = KHTMLViewPrivate::PFNone; if (m_part->parentPart() && m_part->parentPart()->view()) return m_part->parentPart()->view()->focusNextPrevChild(next); return QWidget::focusNextPrevChild(next); } void KHTMLView::doAutoScroll() { QPoint pos = QCursor::pos(); pos = viewport()->mapFromGlobal( pos ); int xm, ym; viewportToContents(pos.x(), pos.y(), xm, ym); pos = QPoint(pos.x() - viewport()->x(), pos.y() - viewport()->y()); if ( (pos.y() < 0) || (pos.y() > visibleHeight()) || (pos.x() < 0) || (pos.x() > visibleWidth()) ) { ensureVisible( xm, ym, 0, 5 ); #ifndef KHTML_NO_SELECTION // extend the selection while scrolling DOM::Node innerNode; if (m_part->isExtendingSelection()) { RenderObject::NodeInfo renderInfo(true/*readonly*/, false/*active*/); m_part->xmlDocImpl()->renderer()->layer() ->nodeAtPoint(renderInfo, xm, ym); innerNode = renderInfo.innerNode(); }/*end if*/ if (innerNode.handle() && innerNode.handle()->renderer()) { int absX, absY; innerNode.handle()->renderer()->absolutePosition(absX, absY); m_part->extendSelectionTo(xm, ym, absX, absY, innerNode); }/*end if*/ #endif // KHTML_NO_SELECTION } } // KHTML defines its own stacking order for any object and thus takes // control of widget painting whenever it can. This is called "redirection". // // Redirected widgets are placed off screen. When they are declared as a child of our view (ChildPolished event), // an event filter is installed, so as to catch any paint event and translate them as update() of the view's main widget. // // Painting also happens spontaneously within widgets. In this case, the widget would update() parts of itself. // While this ordinarily results in a paintEvent being schedduled, it is not the case with off screen widgets. // Thus update() is monitored by using the mechanism that deffers any update call happening during a paint event, // transforming it into a posted UpdateLater event. Hence the need to set Qt::WA_WState_InPaintEvent on redirected widgets. // // Once the UpdateLater event has been received, Qt::WA_WState_InPaintEvent is removed and the process continues // with the update of the corresponding rect on the view. That in turn will make our painting subsystem render() // the widget at the correct stacking position. // // For non-redirected (e.g. external) widgets, z-order is honoured through masking. cf.RenderLayer::updateWidgetMasks static void handleWidget(QWidget* w, KHTMLView* view, bool recurse=true) { if (w->isWindow()) return; if (!qobject_cast(w)) w->setAttribute( Qt::WA_NoSystemBackground ); w->setAttribute(Qt::WA_WState_InPaintEvent); w->setAttribute(Qt::WA_OpaquePaintEvent); w->installEventFilter(view); if (!recurse) return; if (qobject_cast(w)) { handleWidget(static_cast(w)->widget(), view, false); handleWidget(static_cast(w)->horizontalScrollBar(), view, false); handleWidget(static_cast(w)->verticalScrollBar(), view, false); return; } QObjectList children = w->children(); foreach (QObject* object, children) { QWidget *widget = qobject_cast(object); if (widget) handleWidget(widget, view); } } class KHTMLBackingStoreHackWidget : public QWidget { public: void publicEvent(QEvent *e) { QWidget::event(e); } }; bool KHTMLView::viewportEvent ( QEvent * e ) { switch (e->type()) { // those must not be dispatched to the specialized handlers // as widgetEvent() already took care of that case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: case QEvent::MouseButtonDblClick: case QEvent::MouseMove: #ifndef QT_NO_WHEELEVENT case QEvent::Wheel: #endif case QEvent::ContextMenu: case QEvent::DragEnter: case QEvent::DragMove: case QEvent::DragLeave: case QEvent::Drop: return false; case QEvent::Paint: { QRect r = static_cast(e)->rect(); r.setX(r.x() +contentsX()); r.setY(r.y() +contentsY()); QPaintEvent pe(r); paintEvent(&pe); return true; } default: break; } return QScrollArea::viewportEvent(e); } static void setInPaintEventFlag(QWidget* w, bool b = true, bool recurse=true) { w->setAttribute(Qt::WA_WState_InPaintEvent, b); if (!recurse) return; if (qobject_cast(w)) { setInPaintEventFlag(static_cast(w)->widget(), b, false); setInPaintEventFlag(static_cast(w)->horizontalScrollBar(), b, false); setInPaintEventFlag(static_cast(w)->verticalScrollBar(), b, false); return; } foreach(QObject* cw, w->children()) { if (cw->isWidgetType() && ! static_cast(cw)->isWindow() && !(static_cast(cw)->windowModality() & Qt::ApplicationModal)) { setInPaintEventFlag(static_cast(cw), b); } } } bool KHTMLView::eventFilter(QObject *o, QEvent *e) { if ( e->type() == QEvent::ShortcutOverride ) { QKeyEvent* ke = (QKeyEvent*) e; if (m_part->isEditable() || m_part->isCaretMode() || (m_part->xmlDocImpl() && m_part->xmlDocImpl()->focusNode() && m_part->xmlDocImpl()->focusNode()->contentEditable())) { if ( (ke->modifiers() & Qt::ControlModifier) || (ke->modifiers() & Qt::ShiftModifier) ) { switch ( ke->key() ) { case Qt::Key_Left: case Qt::Key_Right: case Qt::Key_Up: case Qt::Key_Down: case Qt::Key_Home: case Qt::Key_End: ke->accept(); return true; default: break; } } } } if ( e->type() == QEvent::Leave ) { if ( d->cursor_icon_widget ) d->cursor_icon_widget->hide(); m_part->resetHoverText(); } QWidget *view = widget(); if (o == view) { if (widgetEvent(e)) return true; + else if ( e->type() == QEvent::Resize ) { + bool ret = QScrollArea::eventFilter(o, e); + if (d->staticWidget && view->pos() != QPoint(0,0)) + view->move(0,0); + return ret; + } } else if (o->isWidgetType()) { QWidget *v = static_cast(o); QWidget *c = v; while (v && v != view) { c = v; v = v->parentWidget(); } KHTMLWidget* k = dynamic_cast(c); if (v && k && k->m_kwp->isRedirected()) { bool block = false; bool isUpdate = false; QWidget *w = static_cast(o); switch(e->type()) { case QEvent::UpdateRequest: { // implicitly call qt_syncBackingStore(w) static_cast(w)->publicEvent(e); block = true; break; } case QEvent::UpdateLater: isUpdate = true; // no break; case QEvent::Paint: if (!allowWidgetPaintEvents) { // eat the event. Like this we can control exactly when the widget // gets repainted. block = true; int x = 0, y = 0; QWidget *v = w; while (v && v->parentWidget() != view) { x += v->x(); y += v->y(); v = v->parentWidget(); } QPoint ap = k->m_kwp->absolutePos(); x += ap.x(); y += ap.y(); QRect pr = isUpdate ? static_cast(e)->region().boundingRect() : static_cast(e)->rect(); bool asap = !isUpdate && !d->contentsMoving && qobject_cast(c); if (isUpdate) { setInPaintEventFlag(w, false); w->update(static_cast(e)->region()); setInPaintEventFlag(w); // implicitly call qt_syncBackingStore(w) QEvent fakeEvent(QEvent::UpdateRequest); static_cast(w)->publicEvent(&fakeEvent); } // QScrollView needs fast repaints if ( asap && !d->painting && m_part->xmlDocImpl() && m_part->xmlDocImpl()->renderer() && !static_cast(m_part->xmlDocImpl()->renderer())->needsLayout() ) { repaintContents(x + pr.x(), y + pr.y(), pr.width(), pr.height()+1); // ### investigate that +1 (shows up when // updating e.g a textarea's blinking cursor) } else if (!d->painting) { scheduleRepaint(x + pr.x(), y + pr.y(), pr.width(), pr.height()+1, asap); } } break; case QEvent::MouseMove: case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: case QEvent::MouseButtonDblClick: { if (0 && w->parentWidget() == view && !qobject_cast(w) && !::qobject_cast(w)) { QMouseEvent *me = static_cast(e); QPoint pt = w->mapTo( view, me->pos()); QMouseEvent me2(me->type(), pt, me->button(), me->buttons(), me->modifiers()); if (e->type() == QEvent::MouseMove) mouseMoveEvent(&me2); else if(e->type() == QEvent::MouseButtonPress) mousePressEvent(&me2); else if(e->type() == QEvent::MouseButtonRelease) mouseReleaseEvent(&me2); else mouseDoubleClickEvent(&me2); block = true; } break; } case QEvent::KeyPress: case QEvent::KeyRelease: if (w->parentWidget() == view && !qobject_cast(w)) { QKeyEvent *ke = static_cast(e); if (e->type() == QEvent::KeyPress) keyPressEvent(ke); else keyReleaseEvent(ke); block = true; } break; case QEvent::FocusIn: case QEvent::FocusOut: block = true; break; default: break; } if (block) { //qDebug("eating event"); return true; } } } // kDebug(6000) <<"passing event on to sv event filter object=" << o->className() << " event=" << e->type(); return QScrollArea::eventFilter(o, e); } bool KHTMLView::widgetEvent(QEvent* e) { switch (e->type()) { case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: case QEvent::MouseButtonDblClick: case QEvent::MouseMove: case QEvent::Paint: #ifndef QT_NO_WHEELEVENT case QEvent::Wheel: #endif case QEvent::ContextMenu: case QEvent::DragEnter: case QEvent::DragMove: case QEvent::DragLeave: case QEvent::Drop: return QFrame::event(e); case QEvent::ChildPolished: { // we need to install an event filter on all children of the widget() to // be able to get correct stacking of children within the document. QObject *c = static_cast(e)->child(); if (c->isWidgetType()) { QWidget *w = static_cast(c); // don't install the event filter on toplevels if (!(w->windowFlags() & Qt::Window) && !(w->windowModality() & Qt::ApplicationModal)) { KHTMLWidget* k = dynamic_cast(w); if (k && k->m_kwp->isRedirected()) { w->unsetCursor(); handleWidget(w, this); } } } } default: break; } return false; } DOM::NodeImpl *KHTMLView::nodeUnderMouse() const { return d->underMouse; } DOM::NodeImpl *KHTMLView::nonSharedNodeUnderMouse() const { return d->underMouseNonShared; } bool KHTMLView::scrollTo(const QRect &bounds) { d->scrollingSelf = true; // so scroll events get ignored int x, y, xe, ye; x = bounds.left(); y = bounds.top(); xe = bounds.right(); ye = bounds.bottom(); //kDebug(6000)<<"scrolling coords: x="<pseudoFocusNode == KHTMLViewPrivate::PFTop ) newFocusNode = doc->nextFocusNode(oldFocusNode); } else { if (oldFocusNode || d->pseudoFocusNode == KHTMLViewPrivate::PFBottom ) newFocusNode = doc->previousFocusNode(oldFocusNode); } bool targetVisible = false; if (!newFocusNode) { if ( next ) { targetVisible = scrollTo(QRect(contentsX()+visibleWidth()/2,contentsHeight()-d->borderY,0,0)); } else { targetVisible = scrollTo(QRect(contentsX()+visibleWidth()/2,d->borderY,0,0)); } } else { #ifndef KHTML_NO_CARET // if it's an editable element, activate the caret if (!m_part->isCaretMode() && !m_part->isEditable() && newFocusNode->contentEditable()) { d->caretViewContext(); moveCaretTo(newFocusNode, 0L, true); } else { caretOff(); } #endif // KHTML_NO_CARET targetVisible = scrollTo(newFocusNode->getRect()); } if (targetVisible) { //kDebug ( 6000 ) << " target reached.\n"; d->tabMovePending = false; m_part->xmlDocImpl()->setFocusNode(newFocusNode); if (newFocusNode) { Node guard(newFocusNode); if (!newFocusNode->hasOneRef() ) { emit m_part->nodeActivated(Node(newFocusNode)); } return true; } else { d->pseudoFocusNode = next?KHTMLViewPrivate::PFBottom:KHTMLViewPrivate::PFTop; return false; } } else { if (!d->tabMovePending) d->lastTabbingDirection = next; d->tabMovePending = true; return true; } } void KHTMLView::displayAccessKeys() { QVector< QChar > taken; displayAccessKeys( NULL, this, taken, false ); displayAccessKeys( NULL, this, taken, true ); } void KHTMLView::displayAccessKeys( KHTMLView* caller, KHTMLView* origview, QVector< QChar >& taken, bool use_fallbacks ) { QMap< ElementImpl*, QChar > fallbacks; if( use_fallbacks ) fallbacks = buildFallbackAccessKeys(); for( NodeImpl* n = m_part->xmlDocImpl(); n != NULL; n = n->traverseNextNode()) { if( n->isElementNode()) { ElementImpl* en = static_cast< ElementImpl* >( n ); DOMString s = en->getAttribute( ATTR_ACCESSKEY ); QString accesskey; if( s.length() == 1 ) { QChar a = s.string()[ 0 ].toUpper(); if( qFind( taken.begin(), taken.end(), a ) == taken.end()) // !contains accesskey = a; } if( accesskey.isNull() && fallbacks.contains( en )) { QChar a = fallbacks[ en ].toUpper(); if( qFind( taken.begin(), taken.end(), a ) == taken.end()) // !contains accesskey = QString( "" ) + a + ""; } if( !accesskey.isNull()) { QRect rec=en->getRect(); QLabel *lab=new QLabel(accesskey,viewport()); lab->setAttribute(Qt::WA_DeleteOnClose); connect( origview, SIGNAL(hideAccessKeys()), lab, SLOT(close()) ); connect( this, SIGNAL(repaintAccessKeys()), lab, SLOT(repaint())); lab->setPalette(QToolTip::palette()); lab->setLineWidth(2); lab->setFrameStyle(QFrame::Box | QFrame::Plain); lab->setMargin(3); lab->adjustSize(); lab->setParent( widget() ); lab->move( qMin(rec.left()+rec.width()/2, contentsWidth() - lab->width()), qMin(rec.top()+rec.height()/2, contentsHeight() - lab->height())); lab->show(); taken.append( accesskey[ 0 ] ); } } } if( use_fallbacks ) return; QList frames = m_part->frames(); foreach( KParts::ReadOnlyPart* cur, frames ) { if( !qobject_cast(cur) ) continue; KHTMLPart* part = static_cast< KHTMLPart* >( cur ); if( part->view() && part->view() != caller ) part->view()->displayAccessKeys( this, origview, taken, use_fallbacks ); } // pass up to the parent if (m_part->parentPart() && m_part->parentPart()->view() && m_part->parentPart()->view() != caller) m_part->parentPart()->view()->displayAccessKeys( this, origview, taken, use_fallbacks ); } bool KHTMLView::isScrollingFromMouseWheel() const { return d->scrollingFromWheel != QPoint(-1,-1); } void KHTMLView::accessKeysTimeout() { d->accessKeysActivated=false; d->accessKeysPreActivate = false; m_part->setStatusBarText(QString(), KHTMLPart::BarOverrideText); emit hideAccessKeys(); } // Handling of the HTML accesskey attribute. bool KHTMLView::handleAccessKey( const QKeyEvent* ev ) { // Qt interprets the keyevent also with the modifiers, and ev->text() matches that, // but this code must act as if the modifiers weren't pressed QChar c; if( ev->key() >= Qt::Key_A && ev->key() <= Qt::Key_Z ) c = 'A' + ev->key() - Qt::Key_A; else if( ev->key() >= Qt::Key_0 && ev->key() <= Qt::Key_9 ) c = '0' + ev->key() - Qt::Key_0; else { // TODO fake XKeyEvent and XLookupString ? // This below seems to work e.g. for eacute though. if( ev->text().length() == 1 ) c = ev->text()[ 0 ]; } if( c.isNull()) return false; return focusNodeWithAccessKey( c ); } bool KHTMLView::focusNodeWithAccessKey( QChar c, KHTMLView* caller ) { DocumentImpl *doc = m_part->xmlDocImpl(); if( !doc ) return false; ElementImpl* node = doc->findAccessKeyElement( c ); if( !node ) { QList frames = m_part->frames(); foreach( KParts::ReadOnlyPart* cur, frames ) { if( !qobject_cast(cur) ) continue; KHTMLPart* part = static_cast< KHTMLPart* >( cur ); if( part->view() && part->view() != caller && part->view()->focusNodeWithAccessKey( c, this )) return true; } // pass up to the parent if (m_part->parentPart() && m_part->parentPart()->view() && m_part->parentPart()->view() != caller && m_part->parentPart()->view()->focusNodeWithAccessKey( c, this )) return true; if( caller == NULL ) { // the active frame (where the accesskey was pressed) QMap< ElementImpl*, QChar > fallbacks = buildFallbackAccessKeys(); for( QMap< ElementImpl*, QChar >::ConstIterator it = fallbacks.begin(); it != fallbacks.end(); ++it ) if( *it == c ) { node = it.key(); break; } } if( node == NULL ) return false; } // Scroll the view as necessary to ensure that the new focus node is visible #ifndef KHTML_NO_CARET // if it's an editable element, activate the caret if (!m_part->isCaretMode() && !m_part->isEditable() && node->contentEditable()) { d->caretViewContext(); moveCaretTo(node, 0L, true); } else { caretOff(); } #endif // KHTML_NO_CARET QRect r = node->getRect(); ensureVisible( r.right(), r.bottom()); ensureVisible( r.left(), r.top()); Node guard( node ); if( node->isFocusable()) { if (node->id()==ID_LABEL) { // if Accesskey is a label, give focus to the label's referrer. node=static_cast(static_cast< HTMLLabelElementImpl* >( node )->getFormElement()); if (!node) return true; guard = node; } // Set focus node on the document #ifdef __GNUC__ #warning "port QFocusEvent::setReason( QFocusEvent::Shortcut ); to qt4" #endif //QFocusEvent::setReason( QFocusEvent::Shortcut ); m_part->xmlDocImpl()->setFocusNode(node); #ifdef __GNUC__ #warning "port QFocusEvent::resetReason(); to qt4" #endif //QFocusEvent::resetReason(); if( node != NULL && node->hasOneRef()) // deleted, only held by guard return true; emit m_part->nodeActivated(Node(node)); if( node != NULL && node->hasOneRef()) return true; } switch( node->id()) { case ID_A: static_cast< HTMLAnchorElementImpl* >( node )->click(); break; case ID_INPUT: static_cast< HTMLInputElementImpl* >( node )->click(); break; case ID_BUTTON: static_cast< HTMLButtonElementImpl* >( node )->click(); break; case ID_AREA: static_cast< HTMLAreaElementImpl* >( node )->click(); break; case ID_TEXTAREA: break; // just focusing it is enough case ID_LEGEND: // TODO break; } return true; } static QString getElementText( NodeImpl* start, bool after ) { QString ret; // nextSibling(), to go after e.g. for( NodeImpl* n = after ? start->nextSibling() : start->traversePreviousNode(); n != NULL; n = after ? n->traverseNextNode() : n->traversePreviousNode()) { if( n->isTextNode()) { if( after ) ret += static_cast< TextImpl* >( n )->toString().string(); else ret.prepend( static_cast< TextImpl* >( n )->toString().string()); } else { switch( n->id()) { case ID_A: case ID_FONT: case ID_TT: case ID_U: case ID_B: case ID_I: case ID_S: case ID_STRIKE: case ID_BIG: case ID_SMALL: case ID_EM: case ID_STRONG: case ID_DFN: case ID_CODE: case ID_SAMP: case ID_KBD: case ID_VAR: case ID_CITE: case ID_ABBR: case ID_ACRONYM: case ID_SUB: case ID_SUP: case ID_SPAN: case ID_NOBR: case ID_WBR: break; case ID_TD: if( ret.trimmed().isEmpty()) break; // fall through default: return ret.simplified(); } } } return ret.simplified(); } static QMap< NodeImpl*, QString > buildLabels( NodeImpl* start ) { QMap< NodeImpl*, QString > ret; for( NodeImpl* n = start; n != NULL; n = n->traverseNextNode()) { if( n->id() == ID_LABEL ) { HTMLLabelElementImpl* label = static_cast< HTMLLabelElementImpl* >( n ); NodeImpl* labelfor = label->getFormElement(); if( labelfor ) ret[ labelfor ] = label->innerText().string().simplified(); } } return ret; } namespace khtml { struct AccessKeyData { ElementImpl* element; QString text; QString url; int priority; // 10(highest) - 0(lowest) }; } QMap< ElementImpl*, QChar > KHTMLView::buildFallbackAccessKeys() const { // build a list of all possible candidate elements that could use an accesskey QList< AccessKeyData > data; QMap< NodeImpl*, QString > labels = buildLabels( m_part->xmlDocImpl()); for( NodeImpl* n = m_part->xmlDocImpl(); n != NULL; n = n->traverseNextNode()) { if( n->isElementNode()) { ElementImpl* element = static_cast< ElementImpl* >( n ); if( element->getAttribute( ATTR_ACCESSKEY ).length() == 1 ) continue; // has accesskey set, ignore if( element->renderer() == NULL ) continue; // not visible QString text; QString url; int priority = 0; bool ignore = false; bool text_after = false; bool text_before = false; switch( element->id()) { case ID_A: url = khtml::parseURL(element->getAttribute(ATTR_HREF)).string(); if( url.isEmpty()) // doesn't have href, it's only an anchor continue; text = static_cast< HTMLElementImpl* >( element )->innerText().string().simplified(); priority = 2; break; case ID_INPUT: { HTMLInputElementImpl* in = static_cast< HTMLInputElementImpl* >( element ); switch( in->inputType()) { case HTMLInputElementImpl::SUBMIT: text = in->value().string(); if( text.isEmpty()) text = i18n( "Submit" ); priority = 7; break; case HTMLInputElementImpl::IMAGE: text = in->altText().string(); priority = 7; break; case HTMLInputElementImpl::BUTTON: text = in->value().string(); priority = 5; break; case HTMLInputElementImpl::RESET: text = in->value().string(); if( text.isEmpty()) text = i18n( "Reset" ); priority = 5; break; case HTMLInputElementImpl::HIDDEN: ignore = true; break; case HTMLInputElementImpl::CHECKBOX: case HTMLInputElementImpl::RADIO: text_after = true; priority = 5; break; case HTMLInputElementImpl::TEXT: case HTMLInputElementImpl::PASSWORD: case HTMLInputElementImpl::FILE: text_before = true; priority = 5; break; default: priority = 5; break; } break; } case ID_BUTTON: text = static_cast< HTMLElementImpl* >( element )->innerText().string().simplified(); switch( static_cast< HTMLButtonElementImpl* >( element )->buttonType()) { case HTMLButtonElementImpl::SUBMIT: if( text.isEmpty()) text = i18n( "Submit" ); priority = 7; break; case HTMLButtonElementImpl::RESET: if( text.isEmpty()) text = i18n( "Reset" ); priority = 5; break; default: priority = 5; break; break; } case ID_SELECT: // these don't have accesskey attribute, but quick access may be handy text_before = true; text_after = true; priority = 5; break; case ID_FRAME: ignore = true; break; default: ignore = !element->isFocusable(); priority = 2; break; } if( ignore ) continue; if( text.isNull() && labels.contains( element )) text = labels[ element ]; if( text.isNull() && text_before ) text = getElementText( element, false ); if( text.isNull() && text_after ) text = getElementText( element, true ); text = text.trimmed(); // increase priority of items which have explicitly specified accesskeys in the config QList< QPair< QString, QChar > > priorities = m_part->settings()->fallbackAccessKeysAssignments(); for( QList< QPair< QString, QChar > >::ConstIterator it = priorities.begin(); it != priorities.end(); ++it ) { if( text == (*it).first ) priority = 10; } AccessKeyData tmp = { element, text, url, priority }; data.append( tmp ); } } QList< QChar > keys; for( char c = 'A'; c <= 'Z'; ++c ) keys << c; for( char c = '0'; c <= '9'; ++c ) keys << c; for( NodeImpl* n = m_part->xmlDocImpl(); n != NULL; n = n->traverseNextNode()) { if( n->isElementNode()) { ElementImpl* en = static_cast< ElementImpl* >( n ); DOMString s = en->getAttribute( ATTR_ACCESSKEY ); if( s.length() == 1 ) { QChar c = s.string()[ 0 ].toUpper(); keys.removeAll( c ); // remove manually assigned accesskeys } } } QMap< ElementImpl*, QChar > ret; for( int priority = 10; priority >= 0; --priority ) { for( QList< AccessKeyData >::Iterator it = data.begin(); it != data.end(); ) { if( (*it).priority != priority ) { ++it; continue; } if( keys.isEmpty()) break; QString text = (*it).text; QChar key; if( key.isNull() && !text.isEmpty()) { QList< QPair< QString, QChar > > priorities = m_part->settings()->fallbackAccessKeysAssignments(); for( QList< QPair< QString, QChar > >::ConstIterator it = priorities.begin(); it != priorities.end(); ++it ) if( text == (*it).first && keys.contains( (*it).second )) { key = (*it).second; break; } } // try first to select the first character as the accesskey, // then first character of the following words, // and then simply the first free character if( key.isNull() && !text.isEmpty()) { QStringList words = text.split( ' ' ); for( QStringList::ConstIterator it = words.begin(); it != words.end(); ++it ) { if( keys.contains( (*it)[ 0 ].toUpper())) { key = (*it)[ 0 ].toUpper(); break; } } } if( key.isNull() && !text.isEmpty()) { for( int i = 0; i < text.length(); ++i ) { if( keys.contains( text[ i ].toUpper())) { key = text[ i ].toUpper(); break; } } } if( key.isNull()) key = keys.front(); ret[ (*it).element ] = key; keys.removeAll( key ); QString url = (*it).url; it = data.erase( it ); // assign the same accesskey also to other elements pointing to the same url if( !url.isEmpty() && !url.startsWith( "javascript:", Qt::CaseInsensitive )) { for( QList< AccessKeyData >::Iterator it2 = data.begin(); it2 != data.end(); ) { if( (*it2).url == url ) { ret[ (*it2).element ] = key; if( it == it2 ) ++it; it2 = data.erase( it2 ); } else ++it2; } } } } return ret; } void KHTMLView::setMediaType( const QString &medium ) { m_medium = medium; } QString KHTMLView::mediaType() const { return m_medium; } bool KHTMLView::pagedMode() const { return d->paged; } void KHTMLView::setWidgetVisible(RenderWidget* w, bool vis) { if (vis) { d->visibleWidgets.insert(w, w->widget()); } else d->visibleWidgets.remove(w); } bool KHTMLView::needsFullRepaint() const { return d->needsFullRepaint; } void KHTMLView::print(bool quick) { if(!m_part->xmlDocImpl()) return; khtml::RenderCanvas *root = static_cast(m_part->xmlDocImpl()->renderer()); if(!root) return; KHTMLPrintSettings printSettings; //XXX: doesn't save settings between prints like this QPrinter printer; QPrintDialog *dialog = KdePrint::createPrintDialog(&printer, QList() << &printSettings, this); QString docname = m_part->xmlDocImpl()->URL().prettyUrl(); if ( !docname.isEmpty() ) docname = KStringHandler::csqueeze(docname, 80); if(quick || dialog->exec()) { viewport()->setCursor( Qt::WaitCursor ); // only viewport(), no QApplication::, otherwise we get the busy cursor in kdeprint's dialogs // set up KPrinter printer.setFullPage(false); printer.setCreator(QString("KDE %1.%2.%3 HTML Library").arg(KDE_VERSION_MAJOR).arg(KDE_VERSION_MINOR).arg(KDE_VERSION_RELEASE)); printer.setDocName(docname); QPainter *p = new QPainter; p->begin( &printer ); khtml::setPrintPainter( p ); m_part->xmlDocImpl()->setPaintDevice( &printer ); QString oldMediaType = mediaType(); setMediaType( "print" ); // We ignore margin settings for html and body when printing // and use the default margins from the print-system // (In Qt 3.0.x the default margins are hardcoded in Qt) m_part->xmlDocImpl()->setPrintStyleSheet( printSettings.printFriendly() ? "* { background-image: none !important;" " background-color: white !important;" " color: black !important; }" "body { margin: 0px !important; }" "html { margin: 0px !important; }" : "body { margin: 0px !important; }" "html { margin: 0px !important; }" ); kDebug(6000) << "printing: physical page width = " << printer.width() << " height = " << printer.height() << endl; root->setStaticMode(true); root->setPagedMode(true); root->setWidth(printer.width()); // root->setHeight(printer.height()); root->setPageTop(0); root->setPageBottom(0); d->paged = true; m_part->xmlDocImpl()->styleSelector()->computeFontSizes(printer.logicalDpiY(), 100); m_part->xmlDocImpl()->updateStyleSelector(); root->setPrintImages(printSettings.printImages()); root->makePageBreakAvoidBlocks(); root->setNeedsLayoutAndMinMaxRecalc(); root->layout(); khtml::RenderWidget::flushWidgetResizes(); // make sure widgets have their final size // check sizes ask for action.. (scale or clip) bool printHeader = printSettings.printHeader(); int headerHeight = 0; QFont headerFont("Sans Serif", 8); QString headerLeft = KGlobal::locale()->formatDate(QDate::currentDate(),KLocale::ShortDate); QString headerMid = docname; QString headerRight; if (printHeader) { p->setFont(headerFont); headerHeight = (p->fontMetrics().lineSpacing() * 3) / 2; } // ok. now print the pages. kDebug(6000) << "printing: html page width = " << root->docWidth() << " height = " << root->docHeight() << endl; kDebug(6000) << "printing: margins left = " << printer.pageRect().left() - printer.paperRect().left() << " top = " << printer.pageRect().top() - printer.paperRect().top() << endl; kDebug(6000) << "printing: paper width = " << printer.width() << " height = " << printer.height() << endl; // if the width is too large to fit on the paper we just scale // the whole thing. int pageWidth = printer.width(); int pageHeight = printer.height(); p->setClipRect(0,0, pageWidth, pageHeight); pageHeight -= headerHeight; bool scalePage = false; double scale = 0.0; #ifndef QT_NO_TRANSFORMATIONS if(root->docWidth() > printer.width()) { scalePage = true; scale = ((double) printer.width())/((double) root->docWidth()); pageHeight = (int) (pageHeight/scale); pageWidth = (int) (pageWidth/scale); headerHeight = (int) (headerHeight/scale); } #endif kDebug(6000) << "printing: scaled html width = " << pageWidth << " height = " << pageHeight << endl; root->setHeight(pageHeight); root->setPageBottom(pageHeight); root->setNeedsLayout(true); root->layoutIfNeeded(); // m_part->slotDebugRenderTree(); // Squeeze header to make it it on the page. if (printHeader) { int available_width = printer.width() - 10 - 2 * qMax(p->boundingRect(0, 0, printer.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerLeft).width(), p->boundingRect(0, 0, printer.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerRight).width()); if (available_width < 150) available_width = 150; int mid_width; int squeeze = 120; do { headerMid = KStringHandler::csqueeze(docname, squeeze); mid_width = p->boundingRect(0, 0, printer.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerMid).width(); squeeze -= 10; } while (mid_width > available_width); } int top = 0; int bottom = 0; int page = 1; while(top < root->docHeight()) { if(top > 0) printer.newPage(); p->save(); p->setClipRect(0, 0, pageWidth, headerHeight); if (printHeader) { int dy = p->fontMetrics().lineSpacing(); p->setPen(Qt::black); p->setFont(headerFont); headerRight = QString("#%1").arg(page); p->drawText(0, 0, printer.width(), dy, Qt::AlignLeft, headerLeft); p->drawText(0, 0, printer.width(), dy, Qt::AlignHCenter, headerMid); p->drawText(0, 0, printer.width(), dy, Qt::AlignRight, headerRight); } #ifndef QT_NO_TRANSFORMATIONS if (scalePage) p->scale(scale, scale); #endif p->restore(); p->translate(0, headerHeight-top); bottom = top+pageHeight; root->setPageTop(top); root->setPageBottom(bottom); root->setPageNumber(page); root->layer()->paint(p, QRect(0, top, pageWidth, pageHeight)); kDebug(6000) << "printed: page " << page <<" bottom At = " << bottom; top = bottom; p->resetTransform(); page++; } p->end(); delete p; // and now reset the layout to the usual one... root->setPagedMode(false); root->setStaticMode(false); d->paged = false; khtml::setPrintPainter( 0 ); setMediaType( oldMediaType ); m_part->xmlDocImpl()->setPaintDevice( this ); m_part->xmlDocImpl()->styleSelector()->computeFontSizes(m_part->xmlDocImpl()->logicalDpiY(), m_part->fontScaleFactor()); m_part->xmlDocImpl()->updateStyleSelector(); viewport()->unsetCursor(); } } void KHTMLView::slotPaletteChanged() { if(!m_part->xmlDocImpl()) return; DOM::DocumentImpl *document = m_part->xmlDocImpl(); if (!document->isHTMLDocument()) return; khtml::RenderCanvas *root = static_cast(document->renderer()); if(!root) return; root->style()->resetPalette(); NodeImpl *body = static_cast(document)->body(); if(!body) return; body->setChanged(true); body->recalcStyle( NodeImpl::Force ); } void KHTMLView::paint(QPainter *p, const QRect &rc, int yOff, bool *more) { if(!m_part->xmlDocImpl()) return; khtml::RenderCanvas *root = static_cast(m_part->xmlDocImpl()->renderer()); if(!root) return; QPaintDevice* opd = m_part->xmlDocImpl()->paintDevice(); m_part->xmlDocImpl()->setPaintDevice(p->device()); root->setPagedMode(true); root->setStaticMode(true); root->setWidth(rc.width()); // save() QRegion creg = p->clipRegion(); QTransform t = p->worldTransform(); QRect w = p->window(); QRect v = p->viewport(); bool vte = p->viewTransformEnabled(); bool wme = p->worldMatrixEnabled(); p->setClipRect(rc); p->translate(rc.left(), rc.top()); double scale = ((double) rc.width()/(double) root->docWidth()); int height = (int) ((double) rc.height() / scale); #ifndef QT_NO_TRANSFORMATIONS p->scale(scale, scale); #endif root->setPageTop(yOff); root->setPageBottom(yOff+height); root->layer()->paint(p, QRect(0, yOff, root->docWidth(), height)); if (more) *more = yOff + height < root->docHeight(); // restore() p->setWorldTransform(t); p->setWindow(w); p->setViewport(v); p->setViewTransformEnabled( vte ); p->setWorldMatrixEnabled( wme ); if (!creg.isEmpty()) p->setClipRegion( creg ); else p->setClipRegion(QRegion(), Qt::NoClip); root->setPagedMode(false); root->setStaticMode(false); m_part->xmlDocImpl()->setPaintDevice( opd ); } void KHTMLView::render(QPainter* p, const QRect& r, const QPoint& off) { QRect clip(off.x()+r.x(), off.y()+r.y(),r.width(),r.height()); if(!m_part || !m_part->xmlDocImpl() || !m_part->xmlDocImpl()->renderer()) { p->fillRect(clip, palette().brush(QPalette::Active, QPalette::Base)); return; } QPaintDevice* opd = m_part->xmlDocImpl()->paintDevice(); m_part->xmlDocImpl()->setPaintDevice(p->device()); // save() QRegion creg = p->clipRegion(); QTransform t = p->worldTransform(); QRect w = p->window(); QRect v = p->viewport(); bool vte = p->viewTransformEnabled(); bool wme = p->worldMatrixEnabled(); p->setClipRect(clip); QRect rect = r.translated(contentsX(),contentsY()); p->translate(off.x()-contentsX(), off.y()-contentsY()); m_part->xmlDocImpl()->renderer()->layer()->paint(p, rect); // restore() p->setWorldTransform(t); p->setWindow(w); p->setViewport(v); p->setViewTransformEnabled( vte ); p->setWorldMatrixEnabled( wme ); if (!creg.isEmpty()) p->setClipRegion( creg ); else p->setClipRegion(QRegion(), Qt::NoClip); m_part->xmlDocImpl()->setPaintDevice( opd ); } void KHTMLView::setHasStaticBackground() { if (!d->staticWidget) widget()->move(0,0); d->staticWidget = true; } void KHTMLView::setVerticalScrollBarPolicy( Qt::ScrollBarPolicy policy ) { #ifndef KHTML_NO_SCROLLBARS d->vpolicy = policy; QScrollArea::setVerticalScrollBarPolicy(policy); #else Q_UNUSED( policy ); #endif } void KHTMLView::setHorizontalScrollBarPolicy( Qt::ScrollBarPolicy policy ) { #ifndef KHTML_NO_SCROLLBARS d->hpolicy = policy; QScrollArea::setHorizontalScrollBarPolicy(policy); #else Q_UNUSED( policy ); #endif } void KHTMLView::restoreScrollBar() { int ow = visibleWidth(); QScrollArea::setVerticalScrollBarPolicy(d->vpolicy); if (visibleWidth() != ow) layout(); d->prevScrollbarVisible = verticalScrollBar()->isVisible(); } QStringList KHTMLView::formCompletionItems(const QString &name) const { if (!m_part->settings()->isFormCompletionEnabled()) return QStringList(); if (!d->formCompletions) d->formCompletions = new KConfig(KStandardDirs::locateLocal("data", "khtml/formcompletions")); return d->formCompletions->group("").readEntry(name, QStringList()); } void KHTMLView::clearCompletionHistory(const QString& name) { if (!d->formCompletions) { d->formCompletions = new KConfig(KStandardDirs::locateLocal("data", "khtml/formcompletions")); } d->formCompletions->group("").writeEntry(name, ""); d->formCompletions->sync(); } void KHTMLView::addFormCompletionItem(const QString &name, const QString &value) { if (!m_part->settings()->isFormCompletionEnabled()) return; // don't store values that are all numbers or just numbers with // dashes or spaces as those are likely credit card numbers or // something similar bool cc_number(true); for ( int i = 0; i < value.length(); ++i) { QChar c(value[i]); if (!c.isNumber() && c != '-' && !c.isSpace()) { cc_number = false; break; } } if (cc_number) return; QStringList items = formCompletionItems(name); if (!items.contains(value)) items.prepend(value); while ((int)items.count() > m_part->settings()->maxFormCompletionItems()) items.erase(items.isEmpty() ? items.end() : --items.end()); d->formCompletions->group("").writeEntry(name, items); } void KHTMLView::addNonPasswordStorableSite(const QString& host) { if (!d->formCompletions) { d->formCompletions = new KConfig(KStandardDirs::locateLocal("data", "khtml/formcompletions")); } KConfigGroup cg( d->formCompletions, "NonPasswordStorableSites"); QStringList sites = cg.readEntry("Sites", QStringList()); sites.append(host); cg.writeEntry("Sites", sites); cg.sync(); } bool KHTMLView::nonPasswordStorableSite(const QString& host) const { if (!d->formCompletions) { d->formCompletions = new KConfig(KStandardDirs::locateLocal("data", "khtml/formcompletions")); } QStringList sites = d->formCompletions->group( "NonPasswordStorableSites" ).readEntry("Sites", QStringList()); return (sites.indexOf(host) != -1); } // returns true if event should be swallowed bool KHTMLView::dispatchMouseEvent(int eventId, DOM::NodeImpl *targetNode, DOM::NodeImpl *targetNodeNonShared, bool cancelable, int detail,QMouseEvent *_mouse, bool setUnder, int mouseEventType, int orient) { // if the target node is a text node, dispatch on the parent node - rdar://4196646 (and #76948) if (targetNode && targetNode->isTextNode()) targetNode = targetNode->parentNode(); if (d->underMouse) d->underMouse->deref(); d->underMouse = targetNode; if (d->underMouse) d->underMouse->ref(); if (d->underMouseNonShared) d->underMouseNonShared->deref(); d->underMouseNonShared = targetNodeNonShared; if (d->underMouseNonShared) d->underMouseNonShared->ref(); bool isWheelEvent = (mouseEventType == DOM::NodeImpl::MouseWheel); int exceptioncode = 0; int pageX = _mouse->x(); int pageY = _mouse->y(); revertTransforms(pageX, pageY); int clientX = pageX - contentsX(); int clientY = pageY - contentsY(); int screenX = _mouse->globalX(); int screenY = _mouse->globalY(); int button = -1; switch (_mouse->button()) { case Qt::LeftButton: button = 0; break; case Qt::MidButton: button = 1; break; case Qt::RightButton: button = 2; break; default: break; } if (d->accessKeysEnabled && d->accessKeysPreActivate && button!=-1) d->accessKeysPreActivate=false; bool ctrlKey = (_mouse->modifiers() & Qt::ControlModifier); bool altKey = (_mouse->modifiers() & Qt::AltModifier); bool shiftKey = (_mouse->modifiers() & Qt::ShiftModifier); bool metaKey = (_mouse->modifiers() & Qt::MetaModifier); // mouseout/mouseover if (setUnder && d->oldUnderMouse != targetNode) { if (d->oldUnderMouse && d->oldUnderMouse->getDocument() != m_part->xmlDocImpl()) { d->oldUnderMouse->deref(); d->oldUnderMouse = 0; } // send mouseout event to the old node if (d->oldUnderMouse) { // send mouseout event to the old node MouseEventImpl *me = new MouseEventImpl(EventImpl::MOUSEOUT_EVENT, true,true,m_part->xmlDocImpl()->defaultView(), 0,screenX,screenY,clientX,clientY,pageX, pageY, ctrlKey,altKey,shiftKey,metaKey, button,targetNode); me->ref(); d->oldUnderMouse->dispatchEvent(me,exceptioncode,true); me->deref(); } // send mouseover event to the new node if (targetNode) { MouseEventImpl *me = new MouseEventImpl(EventImpl::MOUSEOVER_EVENT, true,true,m_part->xmlDocImpl()->defaultView(), 0,screenX,screenY,clientX,clientY,pageX, pageY, ctrlKey,altKey,shiftKey,metaKey, button,d->oldUnderMouse); me->ref(); targetNode->dispatchEvent(me,exceptioncode,true); me->deref(); } if (d->oldUnderMouse) d->oldUnderMouse->deref(); d->oldUnderMouse = targetNode; if (d->oldUnderMouse) d->oldUnderMouse->ref(); } bool swallowEvent = false; if (targetNode) { // send the actual event bool dblclick = ( eventId == EventImpl::CLICK_EVENT && _mouse->type() == QEvent::MouseButtonDblClick ); MouseEventImpl *me = new MouseEventImpl(static_cast(eventId), true,cancelable,m_part->xmlDocImpl()->defaultView(), detail,screenX,screenY,clientX,clientY,pageX, pageY, ctrlKey,altKey,shiftKey,metaKey, button,0, isWheelEvent ? 0 : _mouse, dblclick, isWheelEvent ? static_cast(orient) : MouseEventImpl::ONone ); me->ref(); if ( !d->m_mouseEventsTarget && RenderLayer::gScrollBar && eventId == EventImpl::MOUSEDOWN_EVENT ) // button is pressed inside a layer scrollbar, so make it the target for future mousemove events until released d->m_mouseEventsTarget = RenderLayer::gScrollBar; if ( d->m_mouseEventsTarget && qobject_cast(d->m_mouseEventsTarget) && dynamic_cast(static_cast(d->m_mouseEventsTarget)) ) { // we have a sticky mouse event target and it is a layer's scrollbar. Forward events manually. // ### should use the dom KHTMLWidget*w = dynamic_cast(static_cast(d->m_mouseEventsTarget)); QPoint p = w->m_kwp->absolutePos(); QMouseEvent fw(_mouse->type(), _mouse->pos()-p, _mouse->button(), _mouse->buttons(), _mouse->modifiers()); static_cast(static_cast(d->m_mouseEventsTarget))->sendEvent(&fw); if (_mouse->type() == QMouseEvent::MouseButtonPress && _mouse->button() == Qt::RightButton) { QContextMenuEvent cme(QContextMenuEvent::Mouse, p); static_cast(static_cast(d->m_mouseEventsTarget))->sendEvent(&cme); } swallowEvent = true; } else { targetNode->dispatchEvent(me,exceptioncode,true); bool defaultHandled = me->defaultHandled(); if (defaultHandled || me->defaultPrevented()) swallowEvent = true; } me->deref(); if (eventId == EventImpl::MOUSEDOWN_EVENT) { // Focus should be shifted on mouse down, not on a click. -dwh // Blur current focus node when a link/button is clicked; this // is expected by some sites that rely on onChange handlers running // from form fields before the button click is processed. DOM::NodeImpl* nodeImpl = targetNode; for ( ; nodeImpl && !nodeImpl->isFocusable(); nodeImpl = nodeImpl->parentNode()); if (nodeImpl && nodeImpl->isMouseFocusable()) m_part->xmlDocImpl()->setFocusNode(nodeImpl); else if (!nodeImpl || !nodeImpl->focused()) m_part->xmlDocImpl()->setFocusNode(0); } } return swallowEvent; } void KHTMLView::setIgnoreWheelEvents( bool e ) { d->ignoreWheelEvents = e; } #ifndef QT_NO_WHEELEVENT void KHTMLView::wheelEvent(QWheelEvent* e) { // check if we should reset the state of the indicator describing if // we are currently scrolling the view as a result of wheel events if (d->scrollingFromWheel != QPoint(-1,-1) && d->scrollingFromWheel != QCursor::pos()) d->scrollingFromWheel = d->scrollingFromWheelTimerId ? QCursor::pos() : QPoint(-1,-1); if (d->accessKeysEnabled && d->accessKeysPreActivate) d->accessKeysPreActivate=false; if ( ( e->modifiers() & Qt::ControlModifier) == Qt::ControlModifier ) { emit zoomView( - e->delta() ); e->accept(); } else if (d->firstRelayout) { e->accept(); } else if( !m_kwp->isRedirected() && ( (e->orientation() == Qt::Vertical && ((d->ignoreWheelEvents && !verticalScrollBar()->isVisible()) || e->delta() > 0 && contentsY() <= 0 || e->delta() < 0 && contentsY() >= contentsHeight() - visibleHeight())) || (e->orientation() == Qt::Horizontal && ((d->ignoreWheelEvents && !horizontalScrollBar()->isVisible()) || e->delta() > 0 && contentsX() <=0 || e->delta() < 0 && contentsX() >= contentsWidth() - visibleWidth()))) && m_part->parentPart()) { if ( m_part->parentPart()->view() ) m_part->parentPart()->view()->wheelEvent( e ); e->ignore(); } else { int xm = e->x(); int ym = e->y(); revertTransforms(xm, ym); DOM::NodeImpl::MouseEvent mev( e->buttons(), DOM::NodeImpl::MouseWheel ); m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev ); MouseEventImpl::Orientation o = MouseEventImpl::OVertical; if (e->orientation() == Qt::Horizontal) o = MouseEventImpl::OHorizontal; QMouseEvent _mouse(QEvent::MouseMove, QPoint(xm,ym), Qt::NoButton, e->buttons(), e->modifiers()); bool swallow = dispatchMouseEvent(EventImpl::KHTML_MOUSEWHEEL_EVENT,mev.innerNode.handle(),mev.innerNonSharedNode.handle(), true,-e->delta()/40,&_mouse,true,DOM::NodeImpl::MouseWheel,o); if (swallow) return; d->scrollBarMoved = true; d->scrollingFromWheel = QCursor::pos(); if (d->scrollingFromWheelTimerId) killTimer(d->scrollingFromWheelTimerId); d->scrollingFromWheelTimerId = startTimer(400); if (m_part->parentPart()) { // don't propagate if we are a sub-frame and our scrollbars are already at end of range bool h = (static_cast(e)->orientation() == Qt::Horizontal); bool d = (static_cast(e)->delta() < 0); QScrollBar* hsb = horizontalScrollBar(); QScrollBar* vsb = verticalScrollBar(); if ( h && (d && hsb->value() == hsb->maximum() || !d && hsb->value() == hsb->minimum()) || !h && (d && vsb->value() == vsb->maximum() || !d && vsb->value() == vsb->minimum()) ) { e->accept(); return; } } QScrollArea::wheelEvent( e ); } } #endif void KHTMLView::dragEnterEvent( QDragEnterEvent* ev ) { // Still overriden for BC reasons only... QScrollArea::dragEnterEvent( ev ); } void KHTMLView::dropEvent( QDropEvent *ev ) { // Still overriden for BC reasons only... QScrollArea::dropEvent( ev ); } void KHTMLView::focusInEvent( QFocusEvent *e ) { #ifndef KHTML_NO_TYPE_AHEAD_FIND m_part->enableFindAheadActions( true ); #endif DOM::NodeImpl* fn = m_part->xmlDocImpl() ? m_part->xmlDocImpl()->focusNode() : 0; if (fn && fn->renderer() && fn->renderer()->isWidget() && (e->reason() != Qt::MouseFocusReason) && static_cast(fn->renderer())->widget()) static_cast(fn->renderer())->widget()->setFocus(); #ifndef KHTML_NO_CARET // Restart blink frequency timer if it has been killed, but only on // editable nodes if (d->m_caretViewContext && d->m_caretViewContext->freqTimerId == -1 && fn) { if (m_part->isCaretMode() || m_part->isEditable() || (fn && fn->renderer() && fn->renderer()->style()->userInput() == UI_ENABLED)) { d->m_caretViewContext->freqTimerId = startTimer(500); d->m_caretViewContext->visible = true; }/*end if*/ }/*end if*/ showCaret(); #endif // KHTML_NO_CARET QScrollArea::focusInEvent( e ); } void KHTMLView::focusOutEvent( QFocusEvent *e ) { m_part->stopAutoScroll(); #ifndef KHTML_NO_TYPE_AHEAD_FIND if(d->typeAheadActivated) { findTimeout(); } m_part->enableFindAheadActions( false ); #endif // KHTML_NO_TYPE_AHEAD_FIND #ifndef KHTML_NO_CARET if (d->m_caretViewContext) { switch (d->m_caretViewContext->displayNonFocused) { case KHTMLPart::CaretInvisible: hideCaret(); break; case KHTMLPart::CaretVisible: { if (d->m_caretViewContext->freqTimerId != -1) killTimer(d->m_caretViewContext->freqTimerId); d->m_caretViewContext->freqTimerId = -1; NodeImpl *caretNode = m_part->xmlDocImpl()->focusNode(); if (!d->m_caretViewContext->visible && (m_part->isCaretMode() || m_part->isEditable() || (caretNode && caretNode->renderer() && caretNode->renderer()->style()->userInput() == UI_ENABLED))) { d->m_caretViewContext->visible = true; showCaret(true); }/*end if*/ break; } case KHTMLPart::CaretBlink: // simply leave as is break; }/*end switch*/ }/*end if*/ #endif // KHTML_NO_CARET if ( d->cursor_icon_widget ) d->cursor_icon_widget->hide(); QScrollArea::focusOutEvent( e ); } void KHTMLView::scrollContentsBy( int dx, int dy ) { if ( !d->firstRelayout && !d->complete && m_part->xmlDocImpl() && d->layoutSchedulingEnabled) { // contents scroll while we are not complete: we need to check our layout *now* khtml::RenderCanvas* root = static_cast( m_part->xmlDocImpl()->renderer() ); if (root && root->needsLayout()) { unscheduleRelayout(); layout(); } } if (!d->scrollingSelf) { d->scrollBarMoved = true; d->contentsMoving = true; // ensure quick reset of contentsMoving flag scheduleRepaint(0, 0, 0, 0); } if ((dx || dy) && m_part->xmlDocImpl() && m_part->xmlDocImpl()->documentElement()) m_part->xmlDocImpl()->documentElement()->dispatchHTMLEvent(EventImpl::SCROLL_EVENT, true, false); d->contentsX = QApplication::isRightToLeft() ? horizontalScrollBar()->maximum()-horizontalScrollBar()->value() : horizontalScrollBar()->value(); d->contentsY = verticalScrollBar()->value(); if ( d->staticWidget ) { if (widget()->pos() != QPoint(0,0)) widget()->move(0,0); widget()->update(); return; } QScrollArea::scrollContentsBy(dx, dy); } void KHTMLView::addChild(QWidget * child, int x, int y) { if (!child) return; if (child->parent() != widget()) child->setParent( widget() ); // ### handle pseudo-zooming of non-redirected widgets (e.g. just resize'em) if (!d->staticWidget) child->move(x, y); else child->move(x-contentsX(), y-contentsY()); } void KHTMLView::timerEvent ( QTimerEvent *e ) { // kDebug() << "timer event " << e->timerId(); if ( e->timerId() == d->scrollTimerId ) { if( d->scrollSuspended ) return; switch (d->scrollDirection) { case KHTMLViewPrivate::ScrollDown: if (contentsY() + visibleHeight () >= contentsHeight()) d->newScrollTimer(this, 0); else verticalScrollBar()->setValue( verticalScrollBar()->value() +d->scrollBy ); break; case KHTMLViewPrivate::ScrollUp: if (contentsY() <= 0) d->newScrollTimer(this, 0); else verticalScrollBar()->setValue( verticalScrollBar()->value() -d->scrollBy ); break; case KHTMLViewPrivate::ScrollRight: if (contentsX() + visibleWidth () >= contentsWidth()) d->newScrollTimer(this, 0); else horizontalScrollBar()->setValue( horizontalScrollBar()->value() +d->scrollBy ); break; case KHTMLViewPrivate::ScrollLeft: if (contentsX() <= 0) d->newScrollTimer(this, 0); else horizontalScrollBar()->setValue( horizontalScrollBar()->value() -d->scrollBy ); break; } return; } else if ( e->timerId() == d->scrollingFromWheelTimerId ) { killTimer( d->scrollingFromWheelTimerId ); d->scrollingFromWheelTimerId = 0; } else if ( e->timerId() == d->layoutTimerId ) { d->dirtyLayout = true; layout(); if (d->firstRelayout) { d->firstRelayout = false; verticalScrollBar()->setEnabled( true ); horizontalScrollBar()->setEnabled( true ); } } #ifndef KHTML_NO_CARET else if (d->m_caretViewContext && e->timerId() == d->m_caretViewContext->freqTimerId) { d->m_caretViewContext->visible = !d->m_caretViewContext->visible; if (d->m_caretViewContext->displayed) { updateContents(d->m_caretViewContext->x, d->m_caretViewContext->y, d->m_caretViewContext->width, d->m_caretViewContext->height); }/*end if*/ // if (d->m_caretViewContext->visible) cout << "|" << flush; // else cout << "" << flush; return; } #endif d->contentsMoving = false; if( m_part->xmlDocImpl() ) { DOM::DocumentImpl *document = m_part->xmlDocImpl(); khtml::RenderCanvas* root = static_cast(document->renderer()); if ( root && root->needsLayout() ) { if (d->repaintTimerId) killTimer(d->repaintTimerId); d->repaintTimerId = 0; scheduleRelayout(); return; } } if (d->repaintTimerId) killTimer(d->repaintTimerId); d->repaintTimerId = 0; QRect updateRegion; QVector rects = d->updateRegion.rects(); d->updateRegion = QRegion(); if ( rects.size() ) updateRegion = rects[0]; for ( int i = 1; i < rects.size(); ++i ) { QRect newRegion = updateRegion.unite(rects[i]); if (2*newRegion.height() > 3*updateRegion.height() ) { repaintContents( updateRegion ); updateRegion = rects[i]; } else updateRegion = newRegion; } if ( !updateRegion.isNull() ) repaintContents( updateRegion ); // As widgets can only be accurately positioned during painting, every layout might // dissociate a widget from its RenderWidget. E.g: if a RenderWidget was visible before layout, but the layout // pushed it out of the viewport, it will not be repainted, and consequently it's associated widget won't be repositioned. // Thus we need to check each supposedly 'visible' widget at the end of layout, and remove it in case it's no more in sight. if (d->dirtyLayout && !d->visibleWidgets.isEmpty()) { QWidget* w; d->dirtyLayout = false; QRect visibleRect(contentsX(), contentsY(), visibleWidth(), visibleHeight()); QList toRemove; QHashIterator it(d->visibleWidgets); while (it.hasNext()) { int xp = 0, yp = 0; it.next(); RenderWidget* rw = static_cast( it.key() ); if (!rw->absolutePosition(xp, yp) || !visibleRect.intersects(QRect(xp, yp, it.value()->width(), it.value()->height()))) toRemove.append(rw); } foreach (RenderWidget* r, toRemove) if ( (w = d->visibleWidgets.take(r) ) ) w->move( 0, -500000); } emit repaintAccessKeys(); if (d->emitCompletedAfterRepaint) { bool full = d->emitCompletedAfterRepaint == KHTMLViewPrivate::CSFull; d->emitCompletedAfterRepaint = KHTMLViewPrivate::CSNone; if ( full ) emit m_part->completed(); else emit m_part->completed(true); } } void KHTMLView::scheduleRelayout(khtml::RenderObject * /*clippedObj*/) { if (!d->layoutSchedulingEnabled || d->layoutTimerId) return; d->layoutTimerId = startTimer( m_part->xmlDocImpl() && m_part->xmlDocImpl()->parsing() ? 1000 : 0 ); } void KHTMLView::unscheduleRelayout() { if (!d->layoutTimerId) return; killTimer(d->layoutTimerId); d->layoutTimerId = 0; } void KHTMLView::unscheduleRepaint() { if (!d->repaintTimerId) return; killTimer(d->repaintTimerId); d->repaintTimerId = 0; } void KHTMLView::scheduleRepaint(int x, int y, int w, int h, bool asap) { bool parsing = !m_part->xmlDocImpl() || m_part->xmlDocImpl()->parsing(); // kDebug() << "parsing " << parsing; // kDebug() << "complete " << d->complete; int time = parsing ? 300 : (!asap ? ( !d->complete ? 100 : 20 ) : 0); #ifdef DEBUG_FLICKER QPainter p; p.begin( viewport() ); int vx, vy; contentsToViewport( x, y, vx, vy ); p.fillRect( vx, vy, w, h, Qt::red ); p.end(); #endif d->updateRegion = d->updateRegion.unite(QRect(x,y,w,h)); if (asap && !parsing) unscheduleRepaint(); if ( !d->repaintTimerId ) d->repaintTimerId = startTimer( time ); // kDebug() << "starting timer " << time; } void KHTMLView::complete( bool pendingAction ) { // kDebug() << "KHTMLView::complete()"; d->complete = true; // is there a relayout pending? if (d->layoutTimerId) { // kDebug() << "requesting relayout now"; // do it now killTimer(d->layoutTimerId); d->layoutTimerId = startTimer( 0 ); d->emitCompletedAfterRepaint = pendingAction ? KHTMLViewPrivate::CSActionPending : KHTMLViewPrivate::CSFull; } // is there a repaint pending? if (d->repaintTimerId) { // kDebug() << "requesting repaint now"; // do it now killTimer(d->repaintTimerId); d->repaintTimerId = startTimer( 20 ); d->emitCompletedAfterRepaint = pendingAction ? KHTMLViewPrivate::CSActionPending : KHTMLViewPrivate::CSFull; } if (!d->emitCompletedAfterRepaint) { if (!pendingAction) emit m_part->completed(); else emit m_part->completed(true); } } void KHTMLView::slotMouseScrollTimer() { horizontalScrollBar()->setValue( horizontalScrollBar()->value() +d->m_mouseScroll_byX ); verticalScrollBar()->setValue( verticalScrollBar()->value() +d->m_mouseScroll_byY); } #ifndef KHTML_NO_CARET // ### the dependencies on static functions are a nightmare. just be // hacky and include the implementation here. Clean me up, please. #include "khtml_caret.cpp" void KHTMLView::initCaret(bool keepSelection) { #if DEBUG_CARETMODE > 0 kDebug(6200) << "begin initCaret"; #endif // save caretMoved state as moveCaretTo changes it if (m_part->xmlDocImpl()) { #if 0 ElementImpl *listitem = m_part->xmlDocImpl()->getElementById("__test_element__"); if (listitem) dumpLineBoxes(static_cast(listitem->renderer())); #endif d->caretViewContext(); bool cmoved = d->m_caretViewContext->caretMoved; if (m_part->d->caretNode().isNull()) { // set to document, position will be sanitized anyway m_part->d->caretNode() = m_part->document(); m_part->d->caretOffset() = 0L; // This sanity check is necessary for the not so unlikely case that // setEditable or setCaretMode is called before any render objects have // been created. if (!m_part->d->caretNode().handle()->renderer()) return; }/*end if*/ // kDebug(6200) << "d->m_selectionStart " << m_part->d->m_selectionStart.handle() // << " d->m_selectionEnd " << m_part->d->m_selectionEnd.handle() << endl; // ### does not repaint the selection on keepSelection!=false moveCaretTo(m_part->d->caretNode().handle(), m_part->d->caretOffset(), !keepSelection); // kDebug(6200) << "d->m_selectionStart " << m_part->d->m_selectionStart.handle() // << " d->m_selectionEnd " << m_part->d->m_selectionEnd.handle() << endl; d->m_caretViewContext->caretMoved = cmoved; }/*end if*/ #if DEBUG_CARETMODE > 0 kDebug(6200) << "end initCaret"; #endif } bool KHTMLView::caretOverrides() const { bool cm = m_part->isCaretMode(); bool dm = m_part->isEditable(); return cm && !dm ? false : (dm || m_part->d->caretNode().handle()->contentEditable()) && d->editorContext()->override; } void KHTMLView::ensureNodeHasFocus(NodeImpl *node) { if (m_part->isCaretMode() || m_part->isEditable()) return; if (node->focused()) return; // Find first ancestor whose "user-input" is "enabled" NodeImpl *firstAncestor = 0; while (node) { if (node->renderer() && node->renderer()->style()->userInput() != UI_ENABLED) break; firstAncestor = node; node = node->parentNode(); }/*wend*/ if (!node) firstAncestor = 0; DocumentImpl *doc = m_part->xmlDocImpl(); // ensure that embedded widgets don't lose their focus if (!firstAncestor && doc->focusNode() && doc->focusNode()->renderer() && doc->focusNode()->renderer()->isWidget()) return; // Set focus node on the document #if DEBUG_CARETMODE > 1 kDebug(6200) << "firstAncestor " << firstAncestor << ": " << (firstAncestor ? firstAncestor->nodeName().string() : QString()) << endl; #endif doc->setFocusNode(firstAncestor); emit m_part->nodeActivated(Node(firstAncestor)); } void KHTMLView::recalcAndStoreCaretPos(CaretBox *hintBox) { if (!m_part || m_part->d->caretNode().isNull()) return; d->caretViewContext(); NodeImpl *caretNode = m_part->d->caretNode().handle(); #if DEBUG_CARETMODE > 0 kDebug(6200) << "recalcAndStoreCaretPos: caretNode=" << caretNode << (caretNode ? " "+caretNode->nodeName().string() : QString()) << " r@" << caretNode->renderer() << (caretNode->renderer() && caretNode->renderer()->isText() ? " \"" + QConstString(static_cast(caretNode->renderer())->str->s, qMin(static_cast(caretNode->renderer())->str->l, 15u)).string() + "\"" : QString()); #endif caretNode->getCaret(m_part->d->caretOffset(), caretOverrides(), d->m_caretViewContext->x, d->m_caretViewContext->y, d->m_caretViewContext->width, d->m_caretViewContext->height); if (hintBox && d->m_caretViewContext->x == -1) { #if DEBUG_CARETMODE > 1 kDebug(6200) << "using hint inline box coordinates"; #endif RenderObject *r = caretNode->renderer(); const QFontMetrics &fm = r->style()->fontMetrics(); int absx, absy; r->containingBlock()->absolutePosition(absx, absy, false); // ### what about fixed? d->m_caretViewContext->x = absx + hintBox->xPos(); d->m_caretViewContext->y = absy + hintBox->yPos(); // + hintBox->baseline() - fm.ascent(); d->m_caretViewContext->width = 1; // ### firstline not regarded. But I think it can be safely neglected // as hint boxes are only used for empty lines. d->m_caretViewContext->height = fm.height(); }/*end if*/ #if DEBUG_CARETMODE > 4 // kDebug(6200) << "freqTimerId: "<m_caretViewContext->freqTimerId; #endif #if DEBUG_CARETMODE > 0 kDebug(6200) << "caret: ofs="<d->caretOffset()<<" " <<" x="<m_caretViewContext->x<<" y="<m_caretViewContext->y <<" h="<m_caretViewContext->height<m_caretViewContext) { if (d->m_caretViewContext->freqTimerId != -1) killTimer(d->m_caretViewContext->freqTimerId); if (hasFocus() || d->m_caretViewContext->displayNonFocused == KHTMLPart::CaretBlink) { d->m_caretViewContext->freqTimerId = startTimer(500); } else { d->m_caretViewContext->freqTimerId = -1; }/*end if*/ d->m_caretViewContext->visible = true; if ((d->m_caretViewContext->displayed = (hasFocus() || d->m_caretViewContext->displayNonFocused != KHTMLPart::CaretInvisible))) { updateContents(d->m_caretViewContext->x, d->m_caretViewContext->y, d->m_caretViewContext->width, d->m_caretViewContext->height); }/*end if*/ // kDebug(6200) << "caret on"; }/*end if*/ } void KHTMLView::caretOff() { if (d->m_caretViewContext) { if (d->m_caretViewContext->freqTimerId != -1) killTimer(d->m_caretViewContext->freqTimerId); d->m_caretViewContext->freqTimerId = -1; d->m_caretViewContext->displayed = false; if (d->m_caretViewContext->visible) { d->m_caretViewContext->visible = false; updateContents(d->m_caretViewContext->x, d->m_caretViewContext->y, d->m_caretViewContext->width, d->m_caretViewContext->height); }/*end if*/ // kDebug(6200) << "caret off"; }/*end if*/ } void KHTMLView::showCaret(bool forceRepaint) { if (d->m_caretViewContext) { d->m_caretViewContext->displayed = true; if (d->m_caretViewContext->visible) { if (!forceRepaint) { updateContents(d->m_caretViewContext->x, d->m_caretViewContext->y, d->m_caretViewContext->width, d->m_caretViewContext->height); } else { repaintContents(d->m_caretViewContext->x, d->m_caretViewContext->y, d->m_caretViewContext->width, d->m_caretViewContext->height); }/*end if*/ }/*end if*/ // kDebug(6200) << "caret shown"; }/*end if*/ } bool KHTMLView::foldSelectionToCaret(NodeImpl *startNode, long startOffset, NodeImpl *endNode, long endOffset) { m_part->d->m_selectionStart = m_part->d->m_selectionEnd = m_part->d->caretNode(); m_part->d->m_startOffset = m_part->d->m_endOffset = m_part->d->caretOffset(); m_part->d->m_extendAtEnd = true; bool folded = startNode != endNode || startOffset != endOffset; // Only clear the selection if there has been one. if (folded) { m_part->xmlDocImpl()->clearSelection(); }/*end if*/ return folded; } void KHTMLView::hideCaret() { if (d->m_caretViewContext) { if (d->m_caretViewContext->visible) { // kDebug(6200) << "redraw caret hidden"; d->m_caretViewContext->visible = false; // force repaint, otherwise the event won't be handled // before the focus leaves the window repaintContents(d->m_caretViewContext->x, d->m_caretViewContext->y, d->m_caretViewContext->width, d->m_caretViewContext->height); d->m_caretViewContext->visible = true; }/*end if*/ d->m_caretViewContext->displayed = false; // kDebug(6200) << "caret hidden"; }/*end if*/ } int KHTMLView::caretDisplayPolicyNonFocused() const { if (d->m_caretViewContext) return d->m_caretViewContext->displayNonFocused; else return KHTMLPart::CaretInvisible; } void KHTMLView::setCaretDisplayPolicyNonFocused(int policy) { d->caretViewContext(); // int old = d->m_caretViewContext->displayNonFocused; d->m_caretViewContext->displayNonFocused = (KHTMLPart::CaretDisplayPolicy)policy; // make change immediately take effect if not focused if (!hasFocus()) { switch (d->m_caretViewContext->displayNonFocused) { case KHTMLPart::CaretInvisible: hideCaret(); break; case KHTMLPart::CaretBlink: if (d->m_caretViewContext->freqTimerId != -1) break; d->m_caretViewContext->freqTimerId = startTimer(500); // fall through case KHTMLPart::CaretVisible: d->m_caretViewContext->displayed = true; showCaret(); break; }/*end switch*/ }/*end if*/ } bool KHTMLView::placeCaret(CaretBox *hintBox) { CaretViewContext *cv = d->caretViewContext(); caretOff(); NodeImpl *caretNode = m_part->d->caretNode().handle(); // ### why is it sometimes null? if (!caretNode || !caretNode->renderer()) return false; ensureNodeHasFocus(caretNode); if (m_part->isCaretMode() || m_part->isEditable() || caretNode->renderer()->style()->userInput() == UI_ENABLED) { recalcAndStoreCaretPos(hintBox); cv->origX = cv->x; caretOn(); return true; }/*end if*/ return false; } void KHTMLView::ensureCaretVisible() { CaretViewContext *cv = d->m_caretViewContext; if (!cv) return; ensureVisible(cv->x, cv->y, cv->width, cv->height); d->scrollBarMoved = false; } bool KHTMLView::extendSelection(NodeImpl *oldStartSel, long oldStartOfs, NodeImpl *oldEndSel, long oldEndOfs) { bool changed = false; if (m_part->d->m_selectionStart == m_part->d->m_selectionEnd && m_part->d->m_startOffset == m_part->d->m_endOffset) { changed = foldSelectionToCaret(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs); m_part->d->m_extendAtEnd = true; } else do { changed = m_part->d->m_selectionStart.handle() != oldStartSel || m_part->d->m_startOffset != oldStartOfs || m_part->d->m_selectionEnd.handle() != oldEndSel || m_part->d->m_endOffset != oldEndOfs; if (!changed) break; // determine start position -- caret position is always at end. NodeImpl *startNode; long startOffset; if (m_part->d->m_extendAtEnd) { startNode = m_part->d->m_selectionStart.handle(); startOffset = m_part->d->m_startOffset; } else { startNode = m_part->d->m_selectionEnd.handle(); startOffset = m_part->d->m_endOffset; m_part->d->m_selectionEnd = m_part->d->m_selectionStart; m_part->d->m_endOffset = m_part->d->m_startOffset; m_part->d->m_extendAtEnd = true; }/*end if*/ bool swapNeeded = false; if (!m_part->d->m_selectionEnd.isNull() && startNode) { swapNeeded = RangeImpl::compareBoundaryPoints(startNode, startOffset, m_part->d->m_selectionEnd.handle(), m_part->d->m_endOffset) >= 0; }/*end if*/ m_part->d->m_selectionStart = startNode; m_part->d->m_startOffset = startOffset; if (swapNeeded) { m_part->xmlDocImpl()->setSelection(m_part->d->m_selectionEnd.handle(), m_part->d->m_endOffset, m_part->d->m_selectionStart.handle(), m_part->d->m_startOffset); } else { m_part->xmlDocImpl()->setSelection(m_part->d->m_selectionStart.handle(), m_part->d->m_startOffset, m_part->d->m_selectionEnd.handle(), m_part->d->m_endOffset); }/*end if*/ } while(false);/*end if*/ return changed; } void KHTMLView::updateSelection(NodeImpl *oldStartSel, long oldStartOfs, NodeImpl *oldEndSel, long oldEndOfs) { if (m_part->d->m_selectionStart == m_part->d->m_selectionEnd && m_part->d->m_startOffset == m_part->d->m_endOffset) { if (foldSelectionToCaret(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs)) { m_part->emitSelectionChanged(); }/*end if*/ m_part->d->m_extendAtEnd = true; } else { // check if the extending end has passed the immobile end if (!m_part->d->m_selectionEnd.isNull() && !m_part->d->m_selectionEnd.isNull()) { bool swapNeeded = RangeImpl::compareBoundaryPoints( m_part->d->m_selectionStart.handle(), m_part->d->m_startOffset, m_part->d->m_selectionEnd.handle(), m_part->d->m_endOffset) >= 0; if (swapNeeded) { DOM::Node tmpNode = m_part->d->m_selectionStart; long tmpOffset = m_part->d->m_startOffset; m_part->d->m_selectionStart = m_part->d->m_selectionEnd; m_part->d->m_startOffset = m_part->d->m_endOffset; m_part->d->m_selectionEnd = tmpNode; m_part->d->m_endOffset = tmpOffset; m_part->d->m_startBeforeEnd = true; m_part->d->m_extendAtEnd = !m_part->d->m_extendAtEnd; }/*end if*/ }/*end if*/ m_part->xmlDocImpl()->setSelection(m_part->d->m_selectionStart.handle(), m_part->d->m_startOffset, m_part->d->m_selectionEnd.handle(), m_part->d->m_endOffset); m_part->emitSelectionChanged(); }/*end if*/ } void KHTMLView::caretKeyPressEvent(QKeyEvent *_ke) { NodeImpl *oldStartSel = m_part->d->m_selectionStart.handle(); long oldStartOfs = m_part->d->m_startOffset; NodeImpl *oldEndSel = m_part->d->m_selectionEnd.handle(); long oldEndOfs = m_part->d->m_endOffset; NodeImpl *oldCaretNode = m_part->d->caretNode().handle(); long oldOffset = m_part->d->caretOffset(); bool ctrl = _ke->modifiers() & Qt::ControlModifier; // FIXME: this is that widely indented because I will write ifs around it. switch(_ke->key()) { case Qt::Key_Space: break; case Qt::Key_Down: moveCaretNextLine(1); break; case Qt::Key_Up: moveCaretPrevLine(1); break; case Qt::Key_Left: moveCaretBy(false, ctrl ? CaretByWord : CaretByCharacter, 1); break; case Qt::Key_Right: moveCaretBy(true, ctrl ? CaretByWord : CaretByCharacter, 1); break; case Qt::Key_PageDown: moveCaretNextPage(); break; case Qt::Key_PageUp: moveCaretPrevPage(); break; case Qt::Key_Home: if (ctrl) moveCaretToDocumentBoundary(false); else moveCaretToLineBegin(); break; case Qt::Key_End: if (ctrl) moveCaretToDocumentBoundary(true); else moveCaretToLineEnd(); break; }/*end switch*/ if ((m_part->d->caretNode().handle() != oldCaretNode || m_part->d->caretOffset() != oldOffset) // node should never be null, but faulty conditions may cause it to be && !m_part->d->caretNode().isNull()) { d->m_caretViewContext->caretMoved = true; if (_ke->modifiers() & Qt::ShiftModifier) { // extend selection updateSelection(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs); } else { // clear any selection if (foldSelectionToCaret(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs)) m_part->emitSelectionChanged(); }/*end if*/ m_part->emitCaretPositionChanged(m_part->d->caretNode(), m_part->d->caretOffset()); }/*end if*/ _ke->accept(); } bool KHTMLView::moveCaretTo(NodeImpl *node, long offset, bool clearSel) { if (!node) return false; ElementImpl *baseElem = determineBaseElement(node); RenderFlow *base = static_cast(baseElem ? baseElem->renderer() : 0); if (!node) return false; // need to find out the node's inline box. If there is none, this function // will snap to the next node that has one. This is necessary to make the // caret visible in any case. CaretBoxLineDeleter cblDeleter; // RenderBlock *cb; long r_ofs; CaretBoxIterator cbit; CaretBoxLine *cbl = findCaretBoxLine(node, offset, &cblDeleter, base, r_ofs, cbit); if(!cbl) { kWarning() << "KHTMLView::moveCaretTo - findCaretBoxLine() returns NULL"; return false; } #if DEBUG_CARETMODE > 3 if (cbl) kDebug(6200) << cbl->information(); #endif CaretBox *box = *cbit; if (cbit != cbl->end() && box->object() != node->renderer()) { if (box->object()->element()) { mapRenderPosToDOMPos(box->object(), r_ofs, box->isOutside(), box->isOutsideEnd(), node, offset); //if (!outside) offset = node->minOffset(); #if DEBUG_CARETMODE > 1 kDebug(6200) << "set new node " << node->nodeName().string() << "@" << node; #endif } else { // box has no associated element -> do not use // this case should actually never happen. box = 0; kError(6200) << "Box contains no node! Crash imminent" << endl; }/*end if*/ } NodeImpl *oldStartSel = m_part->d->m_selectionStart.handle(); long oldStartOfs = m_part->d->m_startOffset; NodeImpl *oldEndSel = m_part->d->m_selectionEnd.handle(); long oldEndOfs = m_part->d->m_endOffset; // test for position change bool posChanged = m_part->d->caretNode().handle() != node || m_part->d->caretOffset() != offset; bool selChanged = false; m_part->d->caretNode() = node; m_part->d->caretOffset() = offset; if (clearSel || !oldStartSel || !oldEndSel) { selChanged = foldSelectionToCaret(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs); } else { //kDebug(6200) << "moveToCaret: extendSelection: m_extendAtEnd " << m_part->d->m_extendAtEnd; //kDebug(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() << ")"; selChanged = extendSelection(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs); //kDebug(6200) << "after extendSelection: m_extendAtEnd " << m_part->d->m_extendAtEnd; //kDebug(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() << ")"; }/*end if*/ d->caretViewContext()->caretMoved = true; bool visible_caret = placeCaret(box); // FIXME: if the old position was !visible_caret, and the new position is // also, then two caretPositionChanged signals with a null Node are // emitted in series. if (posChanged) { m_part->emitCaretPositionChanged(visible_caret ? node : 0, offset); }/*end if*/ return selChanged; } void KHTMLView::moveCaretByLine(bool next, int count) { Node &caretNodeRef = m_part->d->caretNode(); if (caretNodeRef.isNull()) return; NodeImpl *caretNode = caretNodeRef.handle(); // kDebug(6200) << ": caretNode=" << caretNode; long offset = m_part->d->caretOffset(); CaretViewContext *cv = d->caretViewContext(); ElementImpl *baseElem = determineBaseElement(caretNode); LinearDocument ld(m_part, caretNode, offset, LeafsOnly, baseElem); ErgonomicEditableLineIterator it(ld.current(), cv->origX); // move count lines vertically while (count > 0 && it != ld.end() && it != ld.preBegin()) { count--; if (next) ++it; else --it; }/*wend*/ // Nothing? Then leave everything as is. if (it == ld.end() || it == ld.preBegin()) return; int x, absx, absy; CaretBox *caretBox = nearestCaretBox(it, d->m_caretViewContext, x, absx, absy); placeCaretOnLine(caretBox, x, absx, absy); } void KHTMLView::placeCaretOnLine(CaretBox *caretBox, int x, int absx, int absy) { // paranoia sanity check if (!caretBox) return; RenderObject *caretRender = caretBox->object(); #if DEBUG_CARETMODE > 0 kDebug(6200) << "got valid caretBox " << caretBox; kDebug(6200) << "xPos: " << caretBox->xPos() << " yPos: " << caretBox->yPos() << " width: " << caretBox->width() << " height: " << caretBox->height() << endl; InlineTextBox *tb = static_cast(caretBox->inlineBox()); if (caretBox->isInlineTextBox()) { kDebug(6200) << "contains \"" << QString(static_cast(tb->object())->str->s + tb->m_start, tb->m_len) << "\"";} #endif // inquire height of caret int caretHeight = caretBox->height(); bool isText = caretBox->isInlineTextBox(); int yOfs = 0; // y-offset for text nodes if (isText) { // text boxes need extrawurst RenderText *t = static_cast(caretRender); const QFontMetrics &fm = t->metrics(caretBox->inlineBox()->m_firstLine); caretHeight = fm.height(); yOfs = caretBox->inlineBox()->baseline() - fm.ascent(); }/*end if*/ caretOff(); // set new caret node NodeImpl *caretNode; long &offset = m_part->d->caretOffset(); mapRenderPosToDOMPos(caretRender, offset, caretBox->isOutside(), caretBox->isOutsideEnd(), caretNode, offset); // set all variables not needing special treatment d->m_caretViewContext->y = caretBox->yPos() + yOfs; d->m_caretViewContext->height = caretHeight; d->m_caretViewContext->width = 1; // FIXME: regard override int xPos = caretBox->xPos(); int caretBoxWidth = caretBox->width(); d->m_caretViewContext->x = xPos; if (!caretBox->isOutside()) { // before or at beginning of inline box -> place at beginning long r_ofs = 0; if (x <= xPos) { r_ofs = caretBox->minOffset(); // somewhere within this block } else if (x > xPos && x <= xPos + caretBoxWidth) { if (isText) { // find out where exactly r_ofs = static_cast(caretBox->inlineBox()) ->offsetForPoint(x, d->m_caretViewContext->x); #if DEBUG_CARETMODE > 2 kDebug(6200) << "deviation from origX " << d->m_caretViewContext->x - x; #endif #if 0 } else { // snap to nearest end if (xPos + caretBoxWidth - x < x - xPos) { d->m_caretViewContext->x = xPos + caretBoxWidth; r_ofs = caretNode ? caretNode->maxOffset() : 1; } else { d->m_caretViewContext->x = xPos; r_ofs = caretNode ? caretNode->minOffset() : 0; }/*end if*/ #endif }/*end if*/ } else { // after the inline box -> place at end d->m_caretViewContext->x = xPos + caretBoxWidth; r_ofs = caretBox->maxOffset(); }/*end if*/ offset = r_ofs; }/*end if*/ #if DEBUG_CARETMODE > 0 kDebug(6200) << "new offset: " << offset; #endif m_part->d->caretNode() = caretNode; m_part->d->caretOffset() = offset; d->m_caretViewContext->x += absx; d->m_caretViewContext->y += absy; #if DEBUG_CARETMODE > 1 kDebug(6200) << "new caret position: x " << d->m_caretViewContext->x << " y " << d->m_caretViewContext->y << " w " << d->m_caretViewContext->width << " h " << d->m_caretViewContext->height << " absx " << absx << " absy " << absy; #endif ensureVisible(d->m_caretViewContext->x, d->m_caretViewContext->y, d->m_caretViewContext->width, d->m_caretViewContext->height); d->scrollBarMoved = false; ensureNodeHasFocus(caretNode); caretOn(); } void KHTMLView::moveCaretToLineBoundary(bool end) { Node &caretNodeRef = m_part->d->caretNode(); if (caretNodeRef.isNull()) return; NodeImpl *caretNode = caretNodeRef.handle(); // kDebug(6200) << ": caretNode=" << caretNode; long offset = m_part->d->caretOffset(); ElementImpl *baseElem = determineBaseElement(caretNode); LinearDocument ld(m_part, caretNode, offset, LeafsOnly, baseElem); EditableLineIterator it = ld.current(); if (it == ld.end()) return; // should not happen, but who knows EditableCaretBoxIterator fbit(it, end); Q_ASSERT(fbit != (*it)->end() && fbit != (*it)->preBegin()); CaretBox *b = *fbit; RenderObject *cb = b->containingBlock(); int absx, absy; if (cb) cb->absolutePosition(absx,absy); else absx = absy = 0; int x = b->xPos() + (end && !b->isOutside() ? b->width() : 0); d->m_caretViewContext->origX = absx + x; placeCaretOnLine(b, x, absx, absy); } void KHTMLView::moveCaretToDocumentBoundary(bool end) { Node &caretNodeRef = m_part->d->caretNode(); if (caretNodeRef.isNull()) return; NodeImpl *caretNode = caretNodeRef.handle(); // kDebug(6200) << ": caretNode=" << caretNode; long offset = m_part->d->caretOffset(); ElementImpl *baseElem = determineBaseElement(caretNode); LinearDocument ld(m_part, caretNode, offset, IndicatedFlows, baseElem); EditableLineIterator it(end ? ld.preEnd() : ld.begin(), end); if (it == ld.end() || it == ld.preBegin()) return; // should not happen, but who knows EditableCaretBoxIterator fbit = it; Q_ASSERT(fbit != (*it)->end() && fbit != (*it)->preBegin()); CaretBox *b = *fbit; RenderObject *cb = (*it)->containingBlock(); int absx, absy; if (cb) cb->absolutePosition(absx, absy); else absx = absy = 0; int x = b->xPos()/* + (end ? b->width() : 0) reactivate for rtl*/; d->m_caretViewContext->origX = absx + x; placeCaretOnLine(b, x, absx, absy); } void KHTMLView::moveCaretBy(bool next, CaretMovement cmv, int count) { if (!m_part) return; Node &caretNodeRef = m_part->d->caretNode(); if (caretNodeRef.isNull()) return; NodeImpl *caretNode = caretNodeRef.handle(); // kDebug(6200) << ": caretNode=" << caretNode; long &offset = m_part->d->caretOffset(); ElementImpl *baseElem = determineBaseElement(caretNode); CaretAdvancePolicy advpol = cmv != CaretByWord ? IndicatedFlows : LeafsOnly; LinearDocument ld(m_part, caretNode, offset, advpol, baseElem); EditableCharacterIterator it(&ld); while (!it.isEnd() && count > 0) { count--; if (cmv == CaretByCharacter) { if (next) ++it; else --it; } else if (cmv == CaretByWord) { if (next) moveItToNextWord(it); else moveItToPrevWord(it); }/*end if*/ //kDebug(6200) << "movecaret"; }/*wend*/ CaretBox *hintBox = 0; // make gcc uninit warning disappear if (!it.isEnd()) { NodeImpl *node = caretNodeRef.handle(); hintBox = it.caretBox(); //kDebug(6200) << "hintBox = " << hintBox; //kDebug(6200) << " outside " << hintBox->isOutside() << " outsideEnd " << hintBox->isOutsideEnd() << " r " << it.renderer() << " ofs " << it.offset() << " cb " << hintBox->containingBlock(); mapRenderPosToDOMPos(it.renderer(), it.offset(), hintBox->isOutside(), hintBox->isOutsideEnd(), node, offset); //kDebug(6200) << "mapRTD"; caretNodeRef = node; #if DEBUG_CARETMODE > 2 kDebug(6200) << "set by valid node " << node << " " << (node?node->nodeName().string():QString()) << " offset: " << offset; #endif } else { offset = next ? caretNode->maxOffset() : caretNode->minOffset(); #if DEBUG_CARETMODE > 0 kDebug(6200) << "set by INvalid node. offset: " << offset; #endif }/*end if*/ placeCaretOnChar(hintBox); } void KHTMLView::placeCaretOnChar(CaretBox *hintBox) { caretOff(); recalcAndStoreCaretPos(hintBox); ensureVisible(d->m_caretViewContext->x, d->m_caretViewContext->y, d->m_caretViewContext->width, d->m_caretViewContext->height); d->m_caretViewContext->origX = d->m_caretViewContext->x; d->scrollBarMoved = false; #if DEBUG_CARETMODE > 3 //if (caretNode->isTextNode()) kDebug(6200) << "text[0] = " << (int)*((TextImpl *)caretNode)->data().unicode() << " text :\"" << ((TextImpl *)caretNode)->data().string() << "\""; #endif ensureNodeHasFocus(m_part->d->caretNode().handle()); caretOn(); } void KHTMLView::moveCaretByPage(bool next) { Node &caretNodeRef = m_part->d->caretNode(); if (caretNodeRef.isNull()) return; NodeImpl *caretNode = caretNodeRef.handle(); // kDebug(6200) << ": caretNode=" << caretNode; long offset = m_part->d->caretOffset(); int offs = (viewport()->height() < 30) ? viewport()->height() : 30; // Minimum distance the caret must be moved int mindist = viewport()->height() - offs; CaretViewContext *cv = d->caretViewContext(); // int y = cv->y; // we always measure the top border ElementImpl *baseElem = determineBaseElement(caretNode); LinearDocument ld(m_part, caretNode, offset, LeafsOnly, baseElem); ErgonomicEditableLineIterator it(ld.current(), cv->origX); moveIteratorByPage(ld, it, mindist, next); int x, absx, absy; CaretBox *caretBox = nearestCaretBox(it, d->m_caretViewContext, x, absx, absy); placeCaretOnLine(caretBox, x, absx, absy); } void KHTMLView::moveCaretPrevWord() { moveCaretBy(false, CaretByWord, 1); } void KHTMLView::moveCaretNextWord() { moveCaretBy(true, CaretByWord, 1); } void KHTMLView::moveCaretPrevLine(int n) { moveCaretByLine(false, n); } void KHTMLView::moveCaretNextLine(int n) { moveCaretByLine(true, n); } void KHTMLView::moveCaretPrevPage() { moveCaretByPage(false); } void KHTMLView::moveCaretNextPage() { moveCaretByPage(true); } void KHTMLView::moveCaretToLineBegin() { moveCaretToLineBoundary(false); } void KHTMLView::moveCaretToLineEnd() { moveCaretToLineBoundary(true); } #endif // KHTML_NO_CARET #undef DEBUG_CARETMODE