| /* This file is part of the KDE project |
| * |
| * Copyright (C) 1998, 1999 Torben Weis <weis@kde.org> |
| * 1999 Lars Knoll <knoll@kde.org> |
| * 1999 Antti Koivisto <koivisto@kde.org> |
| * 2000 Dirk Mueller <mueller@kde.org> |
| * Copyright (C) 2004 Apple Computer, Inc. |
| * |
| * 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., 59 Temple Place - Suite 330, |
| * Boston, MA 02111-1307, USA. |
| */ |
| #include "khtmlview.moc" |
| |
| #include "khtmlview.h" |
| |
| #include "khtml_part.h" |
| #include "khtml_events.h" |
| |
| #include "html/html_documentimpl.h" |
| #include "html/html_inlineimpl.h" |
| #include "html/html_formimpl.h" |
| #include "rendering/render_arena.h" |
| #include "rendering/render_object.h" |
| #include "rendering/render_canvas.h" |
| #include "rendering/render_style.h" |
| #include "rendering/render_replaced.h" |
| #include "rendering/render_line.h" |
| #include "rendering/render_text.h" |
| #include "xml/dom_nodeimpl.h" |
| #include "xml/dom2_eventsimpl.h" |
| #include "xml/EventNames.h" |
| #include "css/cssstyleselector.h" |
| #include "misc/helper.h" |
| #include "khtml_settings.h" |
| #include "khtml_printsettings.h" |
| #include "khtmlpart_p.h" |
| |
| #include <kcursor.h> |
| #include <ksimpleconfig.h> |
| #include <kstandarddirs.h> |
| #include <kprinter.h> |
| |
| #include <qtooltip.h> |
| #include <qpainter.h> |
| #include <qpaintdevicemetrics.h> |
| #include <kapplication.h> |
| |
| #include <kimageio.h> |
| #include <assert.h> |
| #include <kdebug.h> |
| #include <kurldrag.h> |
| #include <qobjectlist.h> |
| |
| #if APPLE_CHANGES |
| #include "KWQAccObjectCache.h" |
| #endif |
| |
| #define PAINT_BUFFER_HEIGHT 128 |
| |
| // #define INSTRUMENT_LAYOUT_SCHEDULING 1 |
| |
| using namespace DOM; |
| using namespace EventNames; |
| using namespace HTMLNames; |
| using namespace khtml; |
| |
| class KHTMLToolTip; |
| |
| #ifndef QT_NO_TOOLTIP |
| |
| class KHTMLToolTip : public QToolTip |
| { |
| public: |
| KHTMLToolTip(KHTMLView *view, KHTMLViewPrivate* vp) : QToolTip(view->viewport()) |
| { |
| m_view = view; |
| m_viewprivate = vp; |
| }; |
| |
| protected: |
| virtual void maybeTip(const QPoint &); |
| |
| private: |
| |
| KHTMLView *m_view; |
| KHTMLViewPrivate* m_viewprivate; |
| }; |
| |
| #endif |
| |
| class KHTMLViewPrivate { |
| friend class KHTMLToolTip; |
| public: |
| KHTMLViewPrivate() |
| { |
| repaintRects = 0; |
| underMouse = 0; |
| clickNode = 0; |
| reset(); |
| #if !APPLE_CHANGES |
| tp=0; |
| paintBuffer=0; |
| formCompletions=0; |
| #endif |
| layoutTimerId = 0; |
| delayedLayout = false; |
| mousePressed = false; |
| #ifndef QT_NO_TOOLTIP |
| tooltip = 0; |
| #endif |
| doFullRepaint = true; |
| isTransparent = false; |
| #if APPLE_CHANGES |
| vmode = hmode = QScrollView::Auto; |
| firstLayout = true; |
| needToInitScrollBars = true; |
| #else |
| prevScrollbarVisible = true; |
| #endif |
| } |
| ~KHTMLViewPrivate() |
| { |
| #if !APPLE_CHANGES |
| delete formCompletions; |
| delete tp; tp = 0; |
| delete paintBuffer; paintBuffer =0; |
| #endif |
| |
| if (underMouse) |
| underMouse->deref(); |
| if (clickNode) |
| clickNode->deref(); |
| #ifndef QT_NO_TOOLTIP |
| delete tooltip; |
| #endif |
| delete repaintRects; |
| } |
| void reset() |
| { |
| if (underMouse) |
| underMouse->deref(); |
| underMouse = 0; |
| linkPressed = false; |
| useSlowRepaints = false; |
| dragTarget.reset(); |
| borderTouched = false; |
| #if !APPLE_CHANGES |
| #ifndef KHTML_NO_SCROLLBARS |
| vmode = QScrollView::Auto; |
| hmode = QScrollView::Auto; |
| #else |
| vmode = QScrollView::AlwaysOff; |
| hmode = QScrollView::AlwaysOff; |
| #endif |
| #endif |
| scrollBarMoved = false; |
| ignoreWheelEvents = false; |
| borderX = 30; |
| borderY = 30; |
| #if !APPLE_CHANGES |
| clickX = -1; |
| clickY = -1; |
| #endif |
| prevMouseX = -1; |
| prevMouseY = -1; |
| clickCount = 0; |
| if (clickNode) |
| clickNode->deref(); |
| clickNode = 0; |
| scrollingSelf = false; |
| layoutTimerId = 0; |
| delayedLayout = false; |
| mousePressed = false; |
| doFullRepaint = true; |
| layoutSchedulingEnabled = true; |
| layoutSuppressed = false; |
| layoutCount = 0; |
| #if APPLE_CHANGES |
| firstLayout = true; |
| #endif |
| if (repaintRects) |
| repaintRects->clear(); |
| } |
| |
| #if !APPLE_CHANGES |
| QPainter *tp; |
| QPixmap *paintBuffer; |
| #endif |
| NodeImpl *underMouse; |
| |
| bool borderTouched:1; |
| bool borderStart:1; |
| bool scrollBarMoved:1; |
| bool doFullRepaint:1; |
| |
| QScrollView::ScrollBarMode vmode; |
| QScrollView::ScrollBarMode hmode; |
| #if !APPLE_CHANGES |
| bool prevScrollbarVisible; |
| #endif |
| bool linkPressed; |
| bool useSlowRepaints; |
| bool ignoreWheelEvents; |
| |
| int borderX, borderY; |
| #if !APPLE_CHANGES |
| KSimpleConfig *formCompletions; |
| |
| int clickX, clickY; |
| #endif |
| int clickCount; |
| NodeImpl *clickNode; |
| |
| int prevMouseX, prevMouseY; |
| bool scrollingSelf; |
| int layoutTimerId; |
| bool delayedLayout; |
| |
| bool layoutSchedulingEnabled; |
| bool layoutSuppressed; |
| int layoutCount; |
| |
| #if APPLE_CHANGES |
| bool firstLayout; |
| bool needToInitScrollBars; |
| #endif |
| bool mousePressed; |
| bool isTransparent; |
| #ifndef QT_NO_TOOLTIP |
| KHTMLToolTip *tooltip; |
| #endif |
| |
| // Used by objects during layout to communicate repaints that need to take place only |
| // after all layout has been completed. |
| QPtrList<RenderObject::RepaintInfo>* repaintRects; |
| |
| SharedPtr<NodeImpl> dragTarget; |
| }; |
| |
| #ifndef QT_NO_TOOLTIP |
| |
| void KHTMLToolTip::maybeTip(const QPoint& /*p*/) |
| { |
| DOM::NodeImpl *node = m_viewprivate->underMouse; |
| while ( node ) { |
| if ( node->isElementNode() ) { |
| AtomicString s = static_cast<DOM::ElementImpl*>( node )->getAttribute(ATTR_TITLE); |
| if (!s.isEmpty()) { |
| QRect r( m_view->contentsToViewport( node->getRect().topLeft() ), node->getRect().size() ); |
| tip( r, s.qstring() ); |
| } |
| break; |
| } |
| node = node->parentNode(); |
| } |
| } |
| #endif |
| |
| KHTMLView::KHTMLView( KHTMLPart *part, QWidget *parent, const char *name) |
| : QScrollView( parent, name, WResizeNoErase | WRepaintNoErase | WPaintUnclipped ), |
| _refCount(1) |
| { |
| m_medium = "screen"; |
| |
| m_part = part; |
| #if APPLE_CHANGES |
| m_part->ref(); |
| #endif |
| d = new KHTMLViewPrivate; |
| |
| #if !APPLE_CHANGES |
| QScrollView::setVScrollBarMode(d->vmode); |
| QScrollView::setHScrollBarMode(d->hmode); |
| #endif |
| |
| connect(kapp, SIGNAL(kdisplayPaletteChanged()), this, SLOT(slotPaletteChanged())); |
| connect(this, SIGNAL(contentsMoving(int, int)), this, SLOT(slotScrollBarMoved())); |
| |
| // initialize QScrollview |
| enableClipper(true); |
| viewport()->setMouseTracking(true); |
| viewport()->setBackgroundMode(NoBackground); |
| |
| KImageIO::registerFormats(); |
| |
| #ifndef QT_NO_TOOLTIP |
| d->tooltip = new KHTMLToolTip( this, d ); |
| #endif |
| |
| init(); |
| |
| viewport()->show(); |
| } |
| |
| KHTMLView::~KHTMLView() |
| { |
| #if APPLE_CHANGES |
| resetScrollBars(); |
| #endif |
| |
| assert(_refCount == 0); |
| |
| if (m_part) |
| { |
| //WABA: Is this Ok? Do I need to deref it as well? |
| //Does this need to be done somewhere else? |
| DOM::DocumentImpl *doc = m_part->xmlDocImpl(); |
| if (doc) |
| doc->detach(); |
| |
| #if APPLE_CHANGES |
| m_part->deref(); |
| #endif |
| } |
| |
| delete d; d = 0; |
| } |
| |
| void KHTMLView::clearPart() |
| { |
| if (m_part){ |
| m_part->deref(); |
| m_part = 0; |
| } |
| } |
| |
| #if APPLE_CHANGES |
| void KHTMLView::resetScrollBars() |
| { |
| // Reset the document's scrollbars back to our defaults before we yield the floor. |
| d->firstLayout = true; |
| suppressScrollBars(true); |
| QScrollView::setVScrollBarMode(d->vmode); |
| QScrollView::setHScrollBarMode(d->hmode); |
| suppressScrollBars(false); |
| } |
| #endif |
| |
| void KHTMLView::init() |
| { |
| #if !APPLE_CHANGES |
| if(!d->paintBuffer) d->paintBuffer = new QPixmap(PAINT_BUFFER_HEIGHT, PAINT_BUFFER_HEIGHT); |
| if(!d->tp) d->tp = new QPainter(); |
| #endif |
| |
| setFocusPolicy(QWidget::StrongFocus); |
| viewport()->setFocusPolicy( QWidget::WheelFocus ); |
| viewport()->setFocusProxy(this); |
| |
| _marginWidth = -1; // undefined |
| _marginHeight = -1; |
| _width = 0; |
| _height = 0; |
| |
| setAcceptDrops(true); |
| |
| #if !APPLE_CHANGES |
| resizeContents(visibleWidth(), visibleHeight()); |
| #endif |
| } |
| |
| void KHTMLView::clear() |
| { |
| // viewport()->erase(); |
| |
| setStaticBackground(false); |
| |
| m_part->clearSelection(); |
| |
| d->reset(); |
| |
| #ifdef INSTRUMENT_LAYOUT_SCHEDULING |
| if (d->layoutTimerId && m_part->xmlDocImpl() && !m_part->xmlDocImpl()->ownerElement()) |
| printf("Killing the layout timer from a clear at %d\n", m_part->xmlDocImpl()->elapsedTime()); |
| #endif |
| |
| killTimers(); |
| emit cleared(); |
| |
| #if APPLE_CHANGES |
| suppressScrollBars(true); |
| #else |
| QScrollView::setHScrollBarMode(d->hmode); |
| if (d->vmode==Auto) |
| QScrollView::setVScrollBarMode(d->prevScrollbarVisible?AlwaysOn:Auto); |
| else |
| QScrollView::setVScrollBarMode(d->vmode); |
| resizeContents(visibleWidth(), visibleHeight()); |
| #endif |
| } |
| |
| void KHTMLView::hideEvent(QHideEvent* e) |
| { |
| // viewport()->setBackgroundMode(PaletteBase); |
| QScrollView::hideEvent(e); |
| } |
| |
| void KHTMLView::showEvent(QShowEvent* e) |
| { |
| // viewport()->setBackgroundMode(NoBackground); |
| QScrollView::showEvent(e); |
| } |
| |
| void KHTMLView::resizeEvent (QResizeEvent* e) |
| { |
| QScrollView::resizeEvent(e); |
| |
| #if !APPLE_CHANGES |
| int w = visibleWidth(); |
| int h = visibleHeight(); |
| |
| layout(); |
| |
| // this is to make sure we get the right width even if the scrolbar has dissappeared |
| // due to the size change. |
| if(visibleHeight() != h || visibleWidth() != w) |
| layout(); |
| #endif |
| if ( m_part && m_part->xmlDocImpl() ) |
| m_part->xmlDocImpl()->dispatchWindowEvent( EventNames::resizeEvent, false, false ); |
| |
| KApplication::sendPostedEvents(viewport(), QEvent::Paint); |
| } |
| |
| #if APPLE_CHANGES |
| void KHTMLView::initScrollBars() |
| { |
| if (!d->needToInitScrollBars) |
| return; |
| d->needToInitScrollBars = false; |
| setScrollBarsMode(hScrollBarMode()); |
| } |
| #endif |
| |
| #if !APPLE_CHANGES |
| |
| // this is to get rid of a compiler virtual overload mismatch warning. do not remove |
| void KHTMLView::drawContents( QPainter*) |
| { |
| } |
| |
| void KHTMLView::drawContents( QPainter *p, int ex, int ey, int ew, int eh ) |
| { |
| //kdDebug( 6000 ) << "drawContents x=" << ex << ",y=" << ey << ",w=" << ew << ",h=" << eh << endl; |
| if(!m_part->xmlDocImpl() || !m_part->xmlDocImpl()->renderer()) { |
| p->fillRect(ex, ey, ew, eh, palette().normal().brush(QColorGroup::Base)); |
| return; |
| } |
| if ( d->paintBuffer->width() < visibleWidth() ) |
| d->paintBuffer->resize(visibleWidth(),PAINT_BUFFER_HEIGHT); |
| |
| int py=0; |
| while (py < eh) { |
| int ph = eh-py < PAINT_BUFFER_HEIGHT ? eh-py : PAINT_BUFFER_HEIGHT; |
| d->tp->begin(d->paintBuffer); |
| d->tp->translate(-ex, -ey-py); |
| d->tp->fillRect(ex, ey+py, ew, ph, palette().normal().brush(QColorGroup::Base)); |
| m_part->xmlDocImpl()->renderer()->print(d->tp, ex, ey+py, ew, ph, 0, 0); |
| #ifdef BOX_DEBUG |
| if (m_part->xmlDocImpl()->focusNode()) |
| { |
| d->tp->setBrush(Qt::NoBrush); |
| d->tp->drawRect(m_part->xmlDocImpl()->focusNode()->getRect()); |
| } |
| #endif |
| d->tp->end(); |
| |
| p->drawPixmap(ex, ey+py, *d->paintBuffer, 0, 0, ew, ph); |
| py += PAINT_BUFFER_HEIGHT; |
| } |
| |
| // EDIT FIXME: KDE needs to draw the caret here. |
| |
| khtml::DrawContentsEvent event( p, ex, ey, ew, eh ); |
| QApplication::sendEvent( m_part, &event ); |
| |
| } |
| |
| #endif // !APPLE_CHANGES |
| |
| 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::adjustViewSize() |
| { |
| if( m_part->xmlDocImpl() ) { |
| DOM::DocumentImpl *document = m_part->xmlDocImpl(); |
| |
| khtml::RenderCanvas* root = static_cast<khtml::RenderCanvas *>(document->renderer()); |
| if ( !root ) |
| return; |
| |
| int docw = root->docWidth(); |
| int doch = root->docHeight(); |
| |
| resizeContents(docw, doch); |
| } |
| } |
| |
| void KHTMLView::applyOverflowToViewport(khtml::RenderObject* o, ScrollBarMode& hMode, ScrollBarMode& vMode) |
| { |
| // Handle the overflow:hidden/scroll case for the body/html elements. WinIE treats |
| // overflow:hidden and overflow:scroll on <body> as applying to the document's |
| // scrollbars. The CSS2.1 draft states that HTML UAs should use the <html> or <body> element and XML/XHTML UAs should |
| // use the root element. |
| switch(o->style()->overflow()) { |
| case OHIDDEN: |
| hMode = vMode = AlwaysOff; |
| break; |
| case OSCROLL: |
| hMode = vMode = AlwaysOn; |
| break; |
| case OAUTO: |
| hMode = vMode = Auto; |
| break; |
| default: |
| // Don't set it at all. |
| ; |
| } |
| } |
| |
| bool KHTMLView::inLayout() const |
| { |
| return d->layoutSuppressed; |
| } |
| |
| int KHTMLView::layoutCount() const |
| { |
| return d->layoutCount; |
| } |
| |
| bool KHTMLView::needsFullRepaint() const |
| { |
| return d->doFullRepaint; |
| } |
| |
| void KHTMLView::addRepaintInfo(RenderObject* o, const QRect& r) |
| { |
| if (!d->repaintRects) { |
| d->repaintRects = new QPtrList<RenderObject::RepaintInfo>; |
| d->repaintRects->setAutoDelete(true); |
| } |
| |
| d->repaintRects->append(new RenderObject::RepaintInfo(o, r)); |
| } |
| |
| void KHTMLView::layout() |
| { |
| if (d->layoutSuppressed) |
| return; |
| |
| killTimer(d->layoutTimerId); |
| d->layoutTimerId = 0; |
| d->delayedLayout = false; |
| |
| if (!m_part) { |
| // FIXME: Do we need to set _width here? |
| // FIXME: Should we set _height here too? |
| _width = visibleWidth(); |
| return; |
| } |
| |
| DOM::DocumentImpl* document = m_part->xmlDocImpl(); |
| if (!document) { |
| // FIXME: Should we set _height here too? |
| _width = visibleWidth(); |
| return; |
| } |
| |
| d->layoutSchedulingEnabled = false; |
| |
| // Always ensure our style info is up-to-date. This can happen in situations where |
| // the layout beats any sort of style recalc update that needs to occur. |
| if (document->hasChangedChild()) |
| document->recalcStyle(); |
| |
| khtml::RenderCanvas* root = static_cast<khtml::RenderCanvas*>(document->renderer()); |
| if (!root) { |
| // FIXME: Do we need to set _width or _height here? |
| d->layoutSchedulingEnabled = true; |
| return; |
| } |
| |
| ScrollBarMode hMode = d->hmode; |
| ScrollBarMode vMode = d->vmode; |
| |
| RenderObject* rootRenderer = document->documentElement() ? document->documentElement()->renderer() : 0; |
| if (document->isHTMLDocument()) { |
| NodeImpl *body = static_cast<HTMLDocumentImpl*>(document)->body(); |
| if (body && body->renderer()) { |
| if (body->hasTagName(framesetTag)) { |
| body->renderer()->setNeedsLayout(true); |
| vMode = AlwaysOff; |
| hMode = AlwaysOff; |
| } |
| else if (body->hasTagName(bodyTag)) { |
| RenderObject* o = (rootRenderer->style()->overflow() == OVISIBLE) ? body->renderer() : rootRenderer; |
| applyOverflowToViewport(o, hMode, vMode); // Only applies to HTML UAs, not to XML/XHTML UAs |
| } |
| } |
| } |
| else if (rootRenderer) |
| applyOverflowToViewport(rootRenderer, hMode, vMode); // XML/XHTML UAs use the root element. |
| |
| #ifdef INSTRUMENT_LAYOUT_SCHEDULING |
| if (d->firstLayout && !document->ownerElement()) |
| printf("Elapsed time before first layout: %d\n", document->elapsedTime()); |
| #endif |
| |
| d->doFullRepaint = d->firstLayout || root->printingMode(); |
| if (d->repaintRects) |
| d->repaintRects->clear(); |
| |
| #if APPLE_CHANGES |
| // Now set our scrollbar state for the layout. |
| ScrollBarMode currentHMode = hScrollBarMode(); |
| ScrollBarMode currentVMode = vScrollBarMode(); |
| |
| bool didFirstLayout = false; |
| if (d->firstLayout || (hMode != currentHMode || vMode != currentVMode)) { |
| suppressScrollBars(true); |
| if (d->firstLayout) { |
| d->firstLayout = false; |
| didFirstLayout = true; |
| |
| // Set the initial vMode to AlwaysOn if we're auto. |
| if (vMode == Auto) |
| QScrollView::setVScrollBarMode(AlwaysOn); // This causes a vertical scrollbar to appear. |
| // Set the initial hMode to AlwaysOff if we're auto. |
| if (hMode == Auto) |
| QScrollView::setHScrollBarMode(AlwaysOff); // This causes a horizontal scrollbar to disappear. |
| } |
| |
| if (hMode == vMode) |
| QScrollView::setScrollBarsMode(hMode); |
| else { |
| QScrollView::setHScrollBarMode(hMode); |
| QScrollView::setVScrollBarMode(vMode); |
| } |
| |
| suppressScrollBars(false, true); |
| } |
| #else |
| QScrollView::setHScrollBarMode(hMode); |
| QScrollView::setVScrollBarMode(vMode); |
| #endif |
| |
| int oldHeight = _height; |
| int oldWidth = _width; |
| |
| _height = visibleHeight(); |
| _width = visibleWidth(); |
| |
| if (oldHeight != _height || oldWidth != _width) |
| d->doFullRepaint = true; |
| |
| RenderLayer* layer = root->layer(); |
| |
| if (!d->doFullRepaint) { |
| layer->computeRepaintRects(); |
| root->repaintObjectsBeforeLayout(); |
| } |
| |
| root->layout(); |
| |
| m_part->invalidateSelection(); |
| |
| //kdDebug( 6000 ) << "TIME: layout() dt=" << qt.elapsed() << endl; |
| |
| d->layoutSchedulingEnabled=true; |
| d->layoutSuppressed = false; |
| |
| if (!root->printingMode()) |
| resizeContents(layer->width(), layer->height()); |
| |
| // Now update the positions of all layers. |
| layer->updateLayerPositions(d->doFullRepaint); |
| |
| #if APPLE_CHANGES |
| // We update our widget positions right after doing a layout. |
| root->updateWidgetPositions(); |
| #endif |
| |
| if (d->repaintRects && !d->repaintRects->isEmpty()) { |
| // FIXME: Could optimize this and have objects removed from this list |
| // if they ever do full repaints. |
| RenderObject::RepaintInfo* r; |
| QPtrListIterator<RenderObject::RepaintInfo> it(*d->repaintRects); |
| for ( ; (r = it.current()); ++it) |
| r->m_object->repaintRectangle(r->m_repaintRect); |
| d->repaintRects->clear(); |
| } |
| |
| d->layoutCount++; |
| #if APPLE_CHANGES |
| if (KWQAccObjectCache::accessibilityEnabled()) |
| root->document()->getAccObjectCache()->postNotification(root, "AXLayoutComplete"); |
| #endif |
| |
| #if APPLE_CHANGES |
| updateDashboardRegions(); |
| #endif |
| |
| if (root->needsLayout()) { |
| //qDebug("needs layout, delaying repaint"); |
| scheduleRelayout(); |
| return; |
| } |
| setStaticBackground(d->useSlowRepaints); |
| |
| #if APPLE_CHANGES |
| if (didFirstLayout) { |
| m_part->didFirstLayout(); |
| } |
| #endif |
| } |
| |
| #if APPLE_CHANGES |
| void KHTMLView::updateDashboardRegions() |
| { |
| DOM::DocumentImpl* document = m_part->xmlDocImpl(); |
| if (document->hasDashboardRegions()) { |
| QValueList<DashboardRegionValue> newRegions = document->renderer()->computeDashboardRegions(); |
| QValueList<DashboardRegionValue> currentRegions = document->dashboardRegions(); |
| document->setDashboardRegions(newRegions); |
| KWQ(m_part)->dashboardRegionsChanged(); |
| } |
| } |
| #endif |
| |
| // |
| // Event Handling |
| // |
| ///////////////// |
| |
| void KHTMLView::viewportMousePressEvent( QMouseEvent *_mouse ) |
| { |
| if(!m_part->xmlDocImpl()) return; |
| |
| SharedPtr<KHTMLView> protector(this); |
| |
| int xm, ym; |
| viewportToContents(_mouse->x(), _mouse->y(), xm, ym); |
| |
| //kdDebug( 6000 ) << "\nmousePressEvent: x=" << xm << ", y=" << ym << endl; |
| |
| d->mousePressed = true; |
| |
| DOM::NodeImpl::MouseEvent mev( _mouse->stateAfter(), DOM::NodeImpl::MousePress ); |
| m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev ); |
| |
| #if !APPLE_CHANGES |
| if (d->clickCount > 0 && |
| QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() <= QApplication::startDragDistance()) |
| d->clickCount++; |
| else { |
| d->clickCount = 1; |
| d->clickX = xm; |
| d->clickY = ym; |
| } |
| #else |
| if (KWQ(m_part)->passSubframeEventToSubframe(mev)) { |
| invalidateClick(); |
| return; |
| } |
| |
| d->clickCount = _mouse->clickCount(); |
| if (d->clickNode) |
| d->clickNode->deref(); |
| d->clickNode = mev.innerNode.get(); |
| if (d->clickNode) |
| d->clickNode->ref(); |
| #endif |
| |
| bool swallowEvent = dispatchMouseEvent(mousedownEvent,mev.innerNode.get(),true, |
| d->clickCount,_mouse,true,DOM::NodeImpl::MousePress); |
| |
| if (!swallowEvent) { |
| khtml::MousePressEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode.get() ); |
| QApplication::sendEvent( m_part, &event ); |
| #if APPLE_CHANGES |
| // Many AK widgets run their own event loops and consume events while the mouse is down. |
| // When they finish, currentEvent is the mouseUp that they exited on. We need to update |
| // the khtml state with this mouseUp, which khtml never saw. |
| // If this event isn't a mouseUp, we assume that the mouseUp will be coming later. There |
| // is a hole here if the widget consumes the mouseUp and subsequent events. |
| if (KWQ(m_part)->lastEventIsMouseUp()) { |
| d->mousePressed = false; |
| } |
| #endif |
| #if !KHTML_NO_CPLUSPLUS_DOM |
| emit m_part->nodeActivated(mev.innerNode.get()); |
| #endif |
| } |
| } |
| |
| void KHTMLView::viewportMouseDoubleClickEvent( QMouseEvent *_mouse ) |
| { |
| if(!m_part->xmlDocImpl()) return; |
| |
| SharedPtr<KHTMLView> protector(this); |
| |
| int xm, ym; |
| viewportToContents(_mouse->x(), _mouse->y(), xm, ym); |
| |
| //kdDebug( 6000 ) << "mouseDblClickEvent: x=" << xm << ", y=" << ym << endl; |
| |
| #if APPLE_CHANGES |
| // We get this instead of a second mouse-up |
| d->mousePressed = false; |
| #endif |
| |
| DOM::NodeImpl::MouseEvent mev( _mouse->stateAfter(), DOM::NodeImpl::MouseDblClick ); |
| m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev ); |
| |
| #if APPLE_CHANGES |
| if (KWQ(m_part)->passSubframeEventToSubframe(mev)) |
| return; |
| |
| d->clickCount = _mouse->clickCount(); |
| bool swallowEvent = dispatchMouseEvent(mouseupEvent,mev.innerNode.get(),true, |
| d->clickCount,_mouse,false,DOM::NodeImpl::MouseRelease); |
| |
| if (mev.innerNode == d->clickNode) |
| dispatchMouseEvent(clickEvent,mev.innerNode.get(),true, |
| d->clickCount,_mouse,true,DOM::NodeImpl::MouseRelease); |
| |
| // Qt delivers a release event AND a double click event. |
| if (!swallowEvent) { |
| khtml::MouseReleaseEvent event1( _mouse, xm, ym, mev.url, mev.target, mev.innerNode.get() ); |
| QApplication::sendEvent( m_part, &event1 ); |
| |
| khtml::MouseDoubleClickEvent event2( _mouse, xm, ym, mev.url, mev.target, mev.innerNode.get() ); |
| QApplication::sendEvent( m_part, &event2 ); |
| } |
| #else |
| // We do the same thing as viewportMousePressEvent() here, since the DOM does not treat |
| // single and double-click events as separate (only the detail, i.e. number of clicks differs) |
| // In other words an even detail value for a mouse click event means a double click, and an |
| // odd detail value means a single click |
| bool swallowEvent = dispatchMouseEvent(mousedownEvent,mev.innerNode.get(),true, |
| d->clickCount,_mouse,true,DOM::NodeImpl::MouseDblClick); |
| |
| if (!swallowEvent) { |
| khtml::MouseDoubleClickEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode.get() ); |
| QApplication::sendEvent( m_part, &event ); |
| |
| // ### |
| //if ( url.length() ) |
| //emit doubleClick( url.qstring(), _mouse->button() ); |
| } |
| #endif |
| |
| invalidateClick(); |
| } |
| |
| static bool isSubmitImage(DOM::NodeImpl *node) |
| { |
| return node && node->hasTagName(inputTag) && static_cast<HTMLInputElementImpl*>(node)->inputType() == HTMLInputElementImpl::IMAGE; |
| } |
| |
| void KHTMLView::viewportMouseMoveEvent( QMouseEvent * _mouse ) |
| { |
| // in Radar 3703768 we saw frequent crashes apparently due to the |
| // part being null here, which seems impossible, so check for nil |
| // but also assert so that we can try to figure this out in debug |
| // builds, if it happens. |
| assert(m_part); |
| if(!m_part || !m_part->xmlDocImpl()) return; |
| |
| int xm, ym; |
| viewportToContents(_mouse->x(), _mouse->y(), xm, ym); |
| |
| // Treat mouse move events while the mouse is pressed as "read-only" in prepareMouseEvent |
| // if we are allowed to select. |
| // This means that :hover and :active freeze in the state they were in when the mouse |
| // was pressed, rather than updating for nodes the mouse moves over as you hold the mouse down. |
| DOM::NodeImpl::MouseEvent mev( _mouse->stateAfter(), DOM::NodeImpl::MouseMove ); |
| m_part->xmlDocImpl()->prepareMouseEvent(d->mousePressed && m_part->mouseDownMayStartSelect(), d->mousePressed, xm, ym, &mev ); |
| #if APPLE_CHANGES |
| if (KWQ(m_part)->passSubframeEventToSubframe(mev)) |
| return; |
| #endif |
| |
| bool swallowEvent = dispatchMouseEvent(mousemoveEvent,mev.innerNode.get(),false, |
| 0,_mouse,true,DOM::NodeImpl::MouseMove); |
| |
| #if !APPLE_CHANGES |
| 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 |
| } |
| #endif |
| |
| // execute the scheduled script. This is to make sure the mouseover events come after the mouseout events |
| m_part->executeScheduledScript(); |
| |
| NodeImpl *node = mev.innerNode.get(); |
| RenderObject *renderer = node ? node->renderer() : 0; |
| RenderStyle *style = renderer ? renderer->style() : 0; |
| |
| QCursor c; |
| if (style && style->cursor() == CURSOR_AUTO && style->cursorImage() |
| && !(style->cursorImage()->pixmap().isNull())) { |
| /* First of all it works: Check out http://www.iam.unibe.ch/~schlpbch/cursor.html |
| * |
| * But, I don't know what exactly we have to do here: rescale to 32*32, change to monochrome.. |
| */ |
| //kdDebug( 6000 ) << "using custom cursor" << endl; |
| const QPixmap p = style->cursorImage()->pixmap(); |
| // ### fix |
| c = QCursor(p); |
| } |
| |
| switch ( style ? style->cursor() : CURSOR_AUTO ) { |
| case CURSOR_AUTO: |
| if ( d->mousePressed && m_part->hasSelection() ) |
| // during selection, use an IBeam no matter what we're over |
| c = KCursor::ibeamCursor(); |
| else if ( (!mev.url.isNull() || isSubmitImage(node)) && m_part->settings()->changeCursor() ) |
| c = m_part->urlCursor(); |
| else if ( (node && node->isContentEditable()) || (renderer && renderer->isText() && renderer->canSelect()) ) |
| c = KCursor::ibeamCursor(); |
| break; |
| case CURSOR_CROSS: |
| c = KCursor::crossCursor(); |
| break; |
| case CURSOR_POINTER: |
| c = m_part->urlCursor(); |
| break; |
| case CURSOR_MOVE: |
| c = KCursor::sizeAllCursor(); |
| break; |
| case CURSOR_E_RESIZE: |
| #if APPLE_CHANGES |
| c = KCursor::eastResizeCursor(); |
| break; |
| #endif |
| case CURSOR_W_RESIZE: |
| #if APPLE_CHANGES |
| c = KCursor::westResizeCursor(); |
| #else |
| c = KCursor::sizeHorCursor(); |
| #endif |
| break; |
| case CURSOR_N_RESIZE: |
| #if APPLE_CHANGES |
| c = KCursor::northResizeCursor(); |
| break; |
| #endif |
| case CURSOR_S_RESIZE: |
| #if APPLE_CHANGES |
| c = KCursor::southResizeCursor(); |
| #else |
| c = KCursor::sizeVerCursor(); |
| #endif |
| break; |
| case CURSOR_NE_RESIZE: |
| #if APPLE_CHANGES |
| c = KCursor::northEastResizeCursor(); |
| break; |
| #endif |
| case CURSOR_SW_RESIZE: |
| #if APPLE_CHANGES |
| c = KCursor::southWestResizeCursor(); |
| #else |
| c = KCursor::sizeBDiagCursor(); |
| #endif |
| break; |
| case CURSOR_NW_RESIZE: |
| #if APPLE_CHANGES |
| c = KCursor::northWestResizeCursor(); |
| break; |
| #endif |
| case CURSOR_SE_RESIZE: |
| #if APPLE_CHANGES |
| c = KCursor::southEastResizeCursor(); |
| #else |
| c = KCursor::sizeFDiagCursor(); |
| #endif |
| break; |
| case CURSOR_TEXT: |
| c = KCursor::ibeamCursor(); |
| break; |
| case CURSOR_WAIT: |
| c = KCursor::waitCursor(); |
| break; |
| case CURSOR_HELP: |
| c = KCursor::whatsThisCursor(); |
| break; |
| case CURSOR_DEFAULT: |
| break; |
| } |
| |
| QWidget *vp = viewport(); |
| if ( vp->cursor().handle() != c.handle() ) { |
| if( c.handle() == KCursor::arrowCursor().handle()) |
| vp->unsetCursor(); |
| else |
| vp->setCursor( c ); |
| } |
| d->prevMouseX = xm; |
| d->prevMouseY = ym; |
| |
| if (!swallowEvent) { |
| khtml::MouseMoveEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode.get() ); |
| QApplication::sendEvent( m_part, &event ); |
| } |
| } |
| |
| void KHTMLView::resetCursor() |
| { |
| viewport()->unsetCursor(); |
| } |
| |
| void KHTMLView::invalidateClick() |
| { |
| d->clickCount = 0; |
| if (d->clickNode) { |
| d->clickNode->deref(); |
| d->clickNode = 0; |
| } |
| } |
| |
| void KHTMLView::viewportMouseReleaseEvent( QMouseEvent * _mouse ) |
| { |
| if ( !m_part->xmlDocImpl() ) return; |
| |
| SharedPtr<KHTMLView> protector(this); |
| |
| int xm, ym; |
| viewportToContents(_mouse->x(), _mouse->y(), xm, ym); |
| |
| d->mousePressed = false; |
| |
| //kdDebug( 6000 ) << "\nmouseReleaseEvent: x=" << xm << ", y=" << ym << endl; |
| |
| DOM::NodeImpl::MouseEvent mev( _mouse->stateAfter(), DOM::NodeImpl::MouseRelease ); |
| m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev ); |
| |
| #if APPLE_CHANGES |
| if (KWQ(m_part)->passSubframeEventToSubframe(mev)) |
| return; |
| #endif |
| |
| bool swallowEvent = dispatchMouseEvent(mouseupEvent,mev.innerNode.get(),true, |
| d->clickCount,_mouse,false,DOM::NodeImpl::MouseRelease); |
| |
| if (d->clickCount > 0 && mev.innerNode == d->clickNode |
| #if !APPLE_CHANGES |
| && QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() <= QApplication::startDragDistance() |
| #endif |
| ) |
| dispatchMouseEvent(clickEvent,mev.innerNode.get(),true, |
| d->clickCount,_mouse,true,DOM::NodeImpl::MouseRelease); |
| |
| if (!swallowEvent) { |
| khtml::MouseReleaseEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode.get() ); |
| QApplication::sendEvent( m_part, &event ); |
| } |
| |
| invalidateClick(); |
| } |
| |
| void KHTMLView::keyPressEvent( QKeyEvent *_ke ) |
| { |
| if (m_part->xmlDocImpl() && m_part->xmlDocImpl()->focusNode()) { |
| if (m_part->xmlDocImpl()->focusNode()->dispatchKeyEvent(_ke)) |
| { |
| _ke->accept(); |
| return; |
| } |
| } |
| |
| #if !APPLE_CHANGES |
| int offs = (clipper()->height() < 30) ? clipper()->height() : 30; |
| if (_ke->state()&ShiftButton) |
| switch(_ke->key()) |
| { |
| case Key_Space: |
| if ( d->vmode == QScrollView::AlwaysOff ) |
| _ke->accept(); |
| else |
| scrollBy( 0, -clipper()->height() - offs ); |
| break; |
| } |
| else |
| switch ( _ke->key() ) |
| { |
| case Key_Down: |
| case Key_J: |
| if ( d->vmode == QScrollView::AlwaysOff ) |
| _ke->accept(); |
| else |
| scrollBy( 0, 10 ); |
| break; |
| |
| case Key_Space: |
| case Key_Next: |
| if ( d->vmode == QScrollView::AlwaysOff ) |
| _ke->accept(); |
| else |
| scrollBy( 0, clipper()->height() - offs ); |
| break; |
| |
| case Key_Up: |
| case Key_K: |
| if ( d->vmode == QScrollView::AlwaysOff ) |
| _ke->accept(); |
| else |
| scrollBy( 0, -10 ); |
| break; |
| |
| case Key_Prior: |
| if ( d->vmode == QScrollView::AlwaysOff ) |
| _ke->accept(); |
| else |
| scrollBy( 0, -clipper()->height() + offs ); |
| break; |
| case Key_Right: |
| case Key_L: |
| if ( d->hmode == QScrollView::AlwaysOff ) |
| _ke->accept(); |
| else |
| scrollBy( 10, 0 ); |
| break; |
| case Key_Left: |
| case Key_H: |
| if ( d->hmode == QScrollView::AlwaysOff ) |
| _ke->accept(); |
| else |
| scrollBy( -10, 0 ); |
| break; |
| case Key_Enter: |
| case 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 Key_Home: |
| if ( d->vmode == QScrollView::AlwaysOff ) |
| _ke->accept(); |
| else |
| setContentsPos( 0, 0 ); |
| break; |
| case Key_End: |
| if ( d->vmode == QScrollView::AlwaysOff ) |
| _ke->accept(); |
| else |
| setContentsPos( 0, contentsHeight() - visibleHeight() ); |
| break; |
| default: |
| _ke->ignore(); |
| return; |
| } |
| _ke->accept(); |
| #endif |
| } |
| |
| void KHTMLView::keyReleaseEvent(QKeyEvent *_ke) |
| { |
| if(m_part->xmlDocImpl() && m_part->xmlDocImpl()->focusNode()) { |
| // Qt is damn buggy. we receive release events from our child |
| // widgets. therefore, do not support keyrelease event on generic |
| // nodes for now until we found a workaround for the Qt bugs. (Dirk) |
| // if (m_part->xmlDocImpl()->focusNode()->dispatchKeyEvent(_ke)) { |
| // _ke->accept(); |
| // return; |
| // } |
| // QScrollView::keyReleaseEvent(_ke); |
| Q_UNUSED(_ke); |
| } |
| } |
| |
| void KHTMLView::contentsContextMenuEvent ( QContextMenuEvent *_ce ) |
| { |
| // ### what kind of c*** is that ? |
| #if 0 |
| if (!m_part->xmlDocImpl()) return; |
| int xm = _ce->x(); |
| int ym = _ce->y(); |
| |
| DOM::NodeImpl::MouseEvent mev( _ce->state(), DOM::NodeImpl::MouseMove ); // ### not a mouse event! |
| m_part->xmlDocImpl()->prepareMouseEvent( xm, ym, &mev ); |
| |
| NodeImpl *targetNode = mev.innerNode.get(); |
| if (targetNode && targetNode->renderer() && targetNode->renderer()->isWidget()) { |
| int absx = 0; |
| int absy = 0; |
| targetNode->renderer()->absolutePosition(absx,absy); |
| QPoint pos(xm-absx,ym-absy); |
| |
| QWidget *w = static_cast<RenderWidget*>(targetNode->renderer())->widget(); |
| QContextMenuEvent cme(_ce->reason(),pos,_ce->globalPos(),_ce->state()); |
| setIgnoreEvents(true); |
| QApplication::sendEvent(w,&cme); |
| setIgnoreEvents(false); |
| } |
| #endif |
| } |
| |
| bool KHTMLView::dispatchDragEvent(const AtomicString &eventType, DOM::NodeImpl *dragTarget, const QPoint &loc, DOM::ClipboardImpl *clipboard) |
| { |
| int clientX, clientY; |
| viewportToContents(loc.x(), loc.y(), clientX, clientY); |
| #if APPLE_CHANGES |
| QPoint screenLoc = viewportToGlobal(loc); |
| int screenX = screenLoc.x(); |
| int screenY = screenLoc.y(); |
| #else |
| #warning Need implementation of converting event location to screen location |
| int screenX = loc.x(); |
| int screenY = loc.y(); |
| #endif |
| bool ctrlKey = 0; // FIXME - set up modifiers, grab from AK or CG |
| bool altKey = 0; |
| bool shiftKey = 0; |
| bool metaKey = 0; |
| |
| MouseEventImpl *me = new MouseEventImpl(eventType, |
| true, true, m_part->xmlDocImpl()->defaultView(), |
| 0, screenX, screenY, clientX, clientY, |
| ctrlKey, altKey, shiftKey, metaKey, |
| 0, 0, clipboard); |
| me->ref(); |
| int exceptioncode = 0; |
| dragTarget->dispatchEvent(me, exceptioncode, true); |
| bool accept = me->defaultPrevented(); |
| me->deref(); |
| return accept; |
| } |
| |
| bool KHTMLView::updateDragAndDrop(const QPoint &loc, DOM::ClipboardImpl *clipboard) |
| { |
| bool accept = false; |
| int xm, ym; |
| viewportToContents(loc.x(), loc.y(), xm, ym); |
| DOM::NodeImpl::MouseEvent mev(0, DOM::NodeImpl::MouseMove); |
| m_part->xmlDocImpl()->prepareMouseEvent(true, xm, ym, &mev); |
| NodeImpl *newTarget = mev.innerNode.get(); |
| |
| // Drag events should never go to text nodes (following IE, and proper mouseover/out dispatch) |
| if (newTarget && newTarget->isTextNode()) { |
| newTarget = newTarget->parentNode(); |
| } |
| |
| if (d->dragTarget != newTarget) { |
| // note this ordering is explicitly chosen to match WinIE |
| if (newTarget) { |
| accept = dispatchDragEvent(dragenterEvent, newTarget, loc, clipboard); |
| } |
| if (!d->dragTarget.isNull()) { |
| dispatchDragEvent(dragleaveEvent, d->dragTarget.get(), loc, clipboard); |
| } |
| } else if (newTarget) { |
| accept = dispatchDragEvent(dragoverEvent, newTarget, loc, clipboard); |
| } |
| d->dragTarget.reset(newTarget); |
| |
| return accept; |
| } |
| |
| void KHTMLView::cancelDragAndDrop(const QPoint &loc, DOM::ClipboardImpl *clipboard) |
| { |
| if (!d->dragTarget.isNull()) { |
| dispatchDragEvent(dragleaveEvent, d->dragTarget.get(), loc, clipboard); |
| } |
| d->dragTarget.reset(); |
| } |
| |
| bool KHTMLView::performDragAndDrop(const QPoint &loc, DOM::ClipboardImpl *clipboard) |
| { |
| bool accept = false; |
| if (!d->dragTarget.isNull()) { |
| accept = dispatchDragEvent(dropEvent, d->dragTarget.get(), loc, clipboard); |
| } |
| d->dragTarget.reset(); |
| return accept; |
| } |
| |
| #if !APPLE_CHANGES |
| |
| bool KHTMLView::focusNextPrevChild( bool next ) |
| { |
| // Now try to find the next child |
| if (m_part->xmlDocImpl()) { |
| focusNextPrevNode(next); |
| if (m_part->xmlDocImpl()->focusNode() != 0) |
| return true; // focus node found |
| } |
| |
| // If we get here, there is no next/previous child to go to, so pass up to the next/previous child in our parent |
| 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 ); |
| } |
| } |
| |
| #endif |
| |
| DOM::NodeImpl *KHTMLView::nodeUnderMouse() const |
| { |
| return d->underMouse; |
| } |
| |
| 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(); |
| |
| //kdDebug(6000)<<"scrolling coords: x="<<x<<" y="<<y<<" width="<<xe-x<<" height="<<ye-y<<endl; |
| |
| int deltax; |
| int deltay; |
| |
| int curHeight = visibleHeight(); |
| int curWidth = visibleWidth(); |
| |
| if (ye-y>curHeight-d->borderY) |
| ye = y + curHeight - d->borderY; |
| |
| if (xe-x>curWidth-d->borderX) |
| xe = x + curWidth - d->borderX; |
| |
| // is xpos of target left of the view's border? |
| if (x < contentsX() + d->borderX ) |
| deltax = x - contentsX() - d->borderX; |
| // is xpos of target right of the view's right border? |
| else if (xe + d->borderX > contentsX() + curWidth) |
| deltax = xe + d->borderX - ( contentsX() + curWidth ); |
| else |
| deltax = 0; |
| |
| // is ypos of target above upper border? |
| if (y < contentsY() + d->borderY) |
| deltay = y - contentsY() - d->borderY; |
| // is ypos of target below lower border? |
| else if (ye + d->borderY > contentsY() + curHeight) |
| deltay = ye + d->borderY - ( contentsY() + curHeight ); |
| else |
| deltay = 0; |
| |
| int maxx = curWidth-d->borderX; |
| int maxy = curHeight-d->borderY; |
| |
| int scrollX,scrollY; |
| |
| scrollX = deltax > 0 ? (deltax > maxx ? maxx : deltax) : deltax == 0 ? 0 : (deltax>-maxx ? deltax : -maxx); |
| scrollY = deltay > 0 ? (deltay > maxy ? maxy : deltay) : deltay == 0 ? 0 : (deltay>-maxy ? deltay : -maxy); |
| |
| if (contentsX() + scrollX < 0) |
| scrollX = -contentsX(); |
| else if (contentsWidth() - visibleWidth() - contentsX() < scrollX) |
| scrollX = contentsWidth() - visibleWidth() - contentsX(); |
| |
| if (contentsY() + scrollY < 0) |
| scrollY = -contentsY(); |
| else if (contentsHeight() - visibleHeight() - contentsY() < scrollY) |
| scrollY = contentsHeight() - visibleHeight() - contentsY(); |
| |
| scrollBy(scrollX, scrollY); |
| |
| |
| |
| // generate abs(scroll.) |
| if (scrollX<0) |
| scrollX=-scrollX; |
| if (scrollY<0) |
| scrollY=-scrollY; |
| |
| d->scrollingSelf = false; |
| |
| if ( (scrollX!=maxx) && (scrollY!=maxy) ) |
| return true; |
| else return false; |
| |
| } |
| |
| void KHTMLView::focusNextPrevNode(bool next) |
| { |
| // Sets the focus node of the document to be the node after (or if next is false, before) the current focus node. |
| // Only nodes that are selectable (i.e. for which isSelectable() returns true) are taken into account, and the order |
| // used is that specified in the HTML spec (see DocumentImpl::nextFocusNode() and DocumentImpl::previousFocusNode() |
| // for details). |
| |
| DocumentImpl *doc = m_part->xmlDocImpl(); |
| NodeImpl *oldFocusNode = doc->focusNode(); |
| NodeImpl *newFocusNode; |
| |
| // Find the next/previous node from the current one |
| if (next) |
| newFocusNode = doc->nextFocusNode(oldFocusNode); |
| else |
| newFocusNode = doc->previousFocusNode(oldFocusNode); |
| |
| // If there was previously no focus node and the user has scrolled the document, then instead of picking the first |
| // focusable node in the document, use the first one that lies within the visible area (if possible). |
| if (!oldFocusNode && newFocusNode && d->scrollBarMoved) { |
| |
| kdDebug(6000) << " searching for visible link" << endl; |
| |
| bool visible = false; |
| NodeImpl *toFocus = newFocusNode; |
| while (!visible && toFocus) { |
| QRect focusNodeRect = toFocus->getRect(); |
| if ((focusNodeRect.left() > contentsX()) && (focusNodeRect.right() < contentsX() + visibleWidth()) && |
| (focusNodeRect.top() > contentsY()) && (focusNodeRect.bottom() < contentsY() + visibleHeight())) { |
| // toFocus is visible in the contents area |
| visible = true; |
| } |
| else { |
| // toFocus is _not_ visible in the contents area, pick the next node |
| if (next) |
| toFocus = doc->nextFocusNode(toFocus); |
| else |
| toFocus = doc->previousFocusNode(toFocus); |
| } |
| } |
| |
| if (toFocus) |
| newFocusNode = toFocus; |
| } |
| |
| d->scrollBarMoved = false; |
| |
| if (!newFocusNode) |
| { |
| // No new focus node, scroll to bottom or top depending on next |
| if (next) |
| scrollTo(QRect(contentsX()+visibleWidth()/2,contentsHeight(),0,0)); |
| else |
| scrollTo(QRect(contentsX()+visibleWidth()/2,0,0,0)); |
| } |
| else { |
| // EDIT FIXME: if it's an editable element, activate the caret |
| // otherwise, hide it |
| if (newFocusNode->isContentEditable()) { |
| // make caret visible |
| } |
| else { |
| // hide caret |
| } |
| |
| // Scroll the view as necessary to ensure that the new focus node is visible |
| if (oldFocusNode) { |
| if (!scrollTo(newFocusNode->getRect())) |
| return; |
| } |
| else { |
| ensureVisible(contentsX(), next ? 0: contentsHeight()); |
| } |
| } |
| // Set focus node on the document |
| m_part->xmlDocImpl()->setFocusNode(newFocusNode); |
| #if !KHTML_NO_CPLUSPLUS_DOM |
| emit m_part->nodeActivated(newFocusNode); |
| #endif |
| |
| #if 0 |
| if (newFocusNode) { |
| |
| // this does not belong here. it should run a query on the tree (Dirk) |
| // I'll fix this very particular part of the code soon when I cleaned |
| // up the positioning code |
| // If the newly focussed node is a link, notify the part |
| |
| HTMLAnchorElementImpl *anchor = 0; |
| if ((newFocusNode->id() == ID_A || newFocusNode->id() == ID_AREA)) |
| anchor = static_cast<HTMLAnchorElementImpl *>(newFocusNode); |
| |
| if (anchor && !anchor->areaHref().isNull()) |
| m_part->overURL(anchor->areaHref().qstring(), 0); |
| else |
| m_part->overURL(QString(), 0); |
| } |
| #endif |
| } |
| |
| void KHTMLView::setMediaType( const QString &medium ) |
| { |
| m_medium = medium; |
| } |
| |
| QString KHTMLView::mediaType() const |
| { |
| #if APPLE_CHANGES |
| // See if we have an override type. |
| QString overrideType = KWQ(m_part)->overrideMediaType(); |
| if (!overrideType.isNull()) |
| return overrideType; |
| #endif |
| return m_medium; |
| } |
| |
| #if !APPLE_CHANGES |
| |
| void KHTMLView::print() |
| { |
| if(!m_part->xmlDocImpl()) return; |
| khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer()); |
| if(!root) return; |
| |
| // this only works on Unix - we assume 72dpi |
| KPrinter *printer = new KPrinter(QPrinter::PrinterResolution); |
| printer->addDialogPage(new KHTMLPrintSettings()); |
| if(printer->setup(this)) { |
| viewport()->setCursor( waitCursor ); // only viewport(), no QApplication::, otherwise we get the busy cursor in kdeprint's dialogs |
| // set up KPrinter |
| printer->setFullPage(false); |
| printer->setCreator("KDE 3.0 HTML Library"); |
| QString docname = m_part->xmlDocImpl()->URL(); |
| if ( !docname.isEmpty() ) |
| 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( printer->option("kde-khtml-printfriendly") == "true" ? |
| "* { 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; }" |
| ); |
| |
| |
| QPaintDeviceMetrics metrics( printer ); |
| |
| // this is a simple approximation... we layout the document |
| // according to the width of the page, then just cut |
| // pages without caring about the content. We should do better |
| // in the future, but for the moment this is better than no |
| // printing support |
| kdDebug(6000) << "printing: physical page width = " << metrics.width() |
| << " height = " << metrics.height() << endl; |
| root->setPrintingMode(true); |
| root->setWidth(metrics.width()); |
| |
| m_part->xmlDocImpl()->styleSelector()->computeFontSizes(&metrics); |
| m_part->xmlDocImpl()->updateStyleSelector(); |
| root->setPrintImages( printer->option("kde-khtml-printimages") == "true"); |
| root->setNeedsLayoutAndMinMaxRecalc(); |
| root->layout(); |
| |
| // ok. now print the pages. |
| kdDebug(6000) << "printing: html page width = " << root->docWidth() |
| << " height = " << root->docHeight() << endl; |
| kdDebug(6000) << "printing: margins left = " << printer->margins().width() |
| << " top = " << printer->margins().height() << endl; |
| kdDebug(6000) << "printing: paper width = " << metrics.width() |
| << " height = " << metrics.height() << endl; |
| // if the width is too large to fit on the paper we just scale |
| // the whole thing. |
| int pageHeight = metrics.height(); |
| int pageWidth = metrics.width(); |
| p->setClipRect(0,0, pageWidth, pageHeight); |
| if(root->docWidth() > metrics.width()) { |
| double scale = ((double) metrics.width())/((double) root->docWidth()); |
| #ifndef QT_NO_TRANSFORMATIONS |
| p->scale(scale, scale); |
| #endif |
| pageHeight = (int) (pageHeight/scale); |
| pageWidth = (int) (pageWidth/scale); |
| } |
| kdDebug(6000) << "printing: scaled html width = " << pageWidth |
| << " height = " << pageHeight << endl; |
| int top = 0; |
| while(top < root->docHeight()) { |
| if(top > 0) printer->newPage(); |
| root->setTruncatedAt(top+pageHeight); |
| |
| root->print(p, 0, top, pageWidth, pageHeight, 0, 0); |
| if (top + pageHeight >= root->docHeight()) |
| break; // Stop if we have printed everything |
| |
| p->translate(0, top - root->truncatedAt()); |
| top = root->truncatedAt(); |
| } |
| |
| p->end(); |
| delete p; |
| |
| // and now reset the layout to the usual one... |
| root->setPrintingMode(false); |
| khtml::setPrintPainter( 0 ); |
| setMediaType( oldMediaType ); |
| m_part->xmlDocImpl()->setPaintDevice( this ); |
| m_part->xmlDocImpl()->styleSelector()->computeFontSizes(m_part->xmlDocImpl()->paintDeviceMetrics()); |
| m_part->xmlDocImpl()->updateStyleSelector(); |
| viewport()->unsetCursor(); |
| } |
| delete printer; |
| } |
| |
| void KHTMLView::slotPaletteChanged() |
| { |
| if(!m_part->xmlDocImpl()) return; |
| DOM::DocumentImpl *document = m_part->xmlDocImpl(); |
| if (!document->isHTMLDocument()) return; |
| khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(document->renderer()); |
| if(!root) return; |
| root->style()->resetPalette(); |
| NodeImpl *body = static_cast<HTMLDocumentImpl*>(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<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer()); |
| if(!root) return; |
| |
| m_part->xmlDocImpl()->setPaintDevice(p->device()); |
| root->setPrintingMode(true); |
| root->setWidth(rc.width()); |
| |
| p->save(); |
| 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->print(p, 0, yOff, root->docWidth(), height, 0, 0); |
| if (more) |
| *more = yOff + height < root->docHeight(); |
| p->restore(); |
| |
| root->setPrintingMode(false); |
| m_part->xmlDocImpl()->setPaintDevice( this ); |
| } |
| |
| #endif // !APPLE_CHANGES |
| |
| void KHTMLView::useSlowRepaints() |
| { |
| kdDebug(0) << "slow repaints requested" << endl; |
| d->useSlowRepaints = true; |
| setStaticBackground(true); |
| } |
| |
| void KHTMLView::setScrollBarsMode ( ScrollBarMode mode ) |
| { |
| #ifndef KHTML_NO_SCROLLBARS |
| d->vmode = mode; |
| d->hmode = mode; |
| |
| #if APPLE_CHANGES |
| QScrollView::setScrollBarsMode(mode); |
| #else |
| QScrollView::setVScrollBarMode(mode); |
| QScrollView::setHScrollBarMode(mode); |
| #endif |
| #else |
| Q_UNUSED( mode ); |
| #endif |
| } |
| |
| void KHTMLView::setVScrollBarMode ( ScrollBarMode mode ) |
| { |
| #ifndef KHTML_NO_SCROLLBARS |
| d->vmode = mode; |
| QScrollView::setVScrollBarMode(mode); |
| #else |
| Q_UNUSED( mode ); |
| #endif |
| } |
| |
| void KHTMLView::setHScrollBarMode ( ScrollBarMode mode ) |
| { |
| #ifndef KHTML_NO_SCROLLBARS |
| d->hmode = mode; |
| QScrollView::setHScrollBarMode(mode); |
| #else |
| Q_UNUSED( mode ); |
| #endif |
| } |
| |
| void KHTMLView::restoreScrollBar ( ) |
| { |
| #if APPLE_CHANGES |
| suppressScrollBars(false); |
| #else |
| int ow = visibleWidth(); |
| QScrollView::setVScrollBarMode(d->vmode); |
| if (visibleWidth() != ow) |
| layout(); |
| d->prevScrollbarVisible = verticalScrollBar()->isVisible(); |
| #endif |
| } |
| |
| #if !APPLE_CHANGES |
| |
| QStringList KHTMLView::formCompletionItems(const QString &name) const |
| { |
| if (!m_part->settings()->isFormCompletionEnabled()) |
| return QStringList(); |
| if (!d->formCompletions) |
| d->formCompletions = new KSimpleConfig(locateLocal("data", "khtml/formcompletions")); |
| return d->formCompletions->readListEntry(name); |
| } |
| |
| 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 (unsigned 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.remove(items.fromLast()); |
| d->formCompletions->writeEntry(name, items); |
| } |
| |
| #endif |
| |
| bool KHTMLView::dispatchMouseEvent(const AtomicString &eventType, DOM::NodeImpl *targetNode, bool cancelable, |
| int detail,QMouseEvent *_mouse, bool setUnder, |
| int mouseEventType) |
| { |
| // if the target node is a text node, dispatch on the parent node - rdar://4196646 |
| if (targetNode && targetNode->isTextNode()) |
| targetNode = targetNode->parentNode(); |
| if (d->underMouse) |
| d->underMouse->deref(); |
| d->underMouse = targetNode; |
| if (d->underMouse) |
| d->underMouse->ref(); |
| |
| // mouseout/mouseover |
| if (setUnder) { |
| int clientX, clientY; |
| viewportToContents(_mouse->x(), _mouse->y(), clientX, clientY); |
| if (d->prevMouseX != clientX || d->prevMouseY != clientY) { |
| // ### this code sucks. we should save the oldUnder instead of calculating |
| // it again. calculating is expensive! (Dirk) |
| // Also, there's no guarantee that the old under node is even around any more, |
| // so we could be sending a mouseout to a node that never got a mouseover. |
| SharedPtr<NodeImpl> oldUnder; |
| if (d->prevMouseX >= 0 && d->prevMouseY >= 0) { |
| NodeImpl::MouseEvent mev( _mouse->stateAfter(), static_cast<NodeImpl::MouseEventType>(mouseEventType)); |
| m_part->xmlDocImpl()->prepareMouseEvent( true, d->prevMouseX, d->prevMouseY, &mev ); |
| oldUnder = mev.innerNode; |
| } |
| if (oldUnder != targetNode) { |
| // send mouseout event to the old node |
| if (oldUnder) |
| oldUnder->dispatchMouseEvent(_mouse, mouseoutEvent); |
| // send mouseover event to the new node |
| if (targetNode) |
| targetNode->dispatchMouseEvent(_mouse, mouseoverEvent); |
| } |
| } |
| } |
| |
| bool swallowEvent = false; |
| |
| if (targetNode) |
| swallowEvent = targetNode->dispatchMouseEvent(_mouse, eventType, detail); |
| |
| if (!swallowEvent && eventType == mousedownEvent) { |
| // 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* node = targetNode; |
| for ( ; node && !node->isFocusable(); node = node->parentNode()); |
| // If focus shift is blocked, we eat the event. Note we should never clear swallowEvent |
| // if the page already set it (e.g., by canceling default behavior). |
| if (node && node->isMouseFocusable()) { |
| if (!m_part->xmlDocImpl()->setFocusNode(node)) |
| swallowEvent = true; |
| } else if (!node || !node->focused()) { |
| if (!m_part->xmlDocImpl()->setFocusNode(0)) |
| swallowEvent = true; |
| } |
| } |
| |
| return swallowEvent; |
| } |
| |
| void KHTMLView::setIgnoreWheelEvents( bool e ) |
| { |
| d->ignoreWheelEvents = e; |
| } |
| |
| #ifndef QT_NO_WHEELEVENT |
| |
| void KHTMLView::viewportWheelEvent(QWheelEvent* e) |
| { |
| DocumentImpl *doc = m_part->xmlDocImpl(); |
| if (doc) { |
| RenderObject *docRenderer = doc->renderer(); |
| if (docRenderer) { |
| int x, y; |
| viewportToContents(e->x(), e->y(), x, y); |
| |
| RenderObject::NodeInfo hitTestResult(true, false); |
| doc->renderer()->layer()->hitTest(hitTestResult, x, y); |
| NodeImpl *node = hitTestResult.innerNode(); |
| |
| if (node) { |
| node->dispatchWheelEvent(e); |
| if (e->isAccepted()) |
| return; |
| } |
| } |
| } |
| |
| #if !APPLE_CHANGES |
| if ( d->ignoreWheelEvents && !verticalScrollBar()->isVisible() && m_part->parentPart() ) { |
| if ( m_part->parentPart()->view() ) |
| m_part->parentPart()->view()->wheelEvent( e ); |
| e->ignore(); |
| } |
| else if ( d->vmode == QScrollView::AlwaysOff ) { |
| e->accept(); |
| } |
| else { |
| d->scrollBarMoved = true; |
| QScrollView::viewportWheelEvent( e ); |
| } |
| #endif |
| } |
| |
| #endif |
| |
| #if !APPLE_CHANGES |
| void KHTMLView::dragEnterEvent( QDragEnterEvent* ev ) |
| { |
| // Handle drops onto frames (#16820) |
| // Drops on the main html part is handled by Konqueror (and shouldn't do anything |
| // in e.g. kmail, so not handled here). |
| if ( m_part->parentPart() ) |
| { |
| // Duplicated from KonqView::eventFilter |
| if ( QUriDrag::canDecode( ev ) ) |
| { |
| KURL::List lstDragURLs; |
| bool ok = KURLDrag::decode( ev, lstDragURLs ); |
| QObjectList *children = this->queryList( "QWidget" ); |
| |
| if ( ok && |
| !lstDragURLs.first().url().contains( "javascript:", false ) && // ### this looks like a hack to me |
| ev->source() != this && |
| children && |
| children->findRef( ev->source() ) == -1 ) |
| ev->acceptAction(); |
| |
| delete children; |
| } |
| } |
| QScrollView::dragEnterEvent( ev ); |
| } |
| |
| void KHTMLView::dropEvent( QDropEvent *ev ) |
| { |
| // Handle drops onto frames (#16820) |
| // Drops on the main html part is handled by Konqueror (and shouldn't do anything |
| // in e.g. kmail, so not handled here). |
| if ( m_part->parentPart() ) |
| { |
| KURL::List lstDragURLs; |
| bool ok = KURLDrag::decode( ev, lstDragURLs ); |
| |
| KHTMLPart* part = m_part->parentPart(); |
| while ( part && part->parentPart() ) |
| part = part->parentPart(); |
| KParts::BrowserExtension *ext = part->browserExtension(); |
| if ( ok && ext && lstDragURLs.first().isValid() ) |
| emit ext->openURLRequest( lstDragURLs.first() ); |
| } |
| QScrollView::dropEvent( ev ); |
| } |
| #endif // !APPLE_CHANGES |
| |
| void KHTMLView::focusInEvent( QFocusEvent *e ) |
| { |
| m_part->setCaretVisible(); |
| QScrollView::focusInEvent( e ); |
| } |
| |
| void KHTMLView::focusOutEvent( QFocusEvent *e ) |
| { |
| m_part->stopAutoScroll(); |
| m_part->setCaretVisible(false); |
| QScrollView::focusOutEvent( e ); |
| } |
| |
| void KHTMLView::slotScrollBarMoved() |
| { |
| if (!d->scrollingSelf) |
| d->scrollBarMoved = true; |
| } |
| |
| void KHTMLView::repaintRectangle(const QRect& r, bool immediate) |
| { |
| updateContents(r, immediate); |
| } |
| |
| void KHTMLView::timerEvent ( QTimerEvent *e ) |
| { |
| if (e->timerId()==d->layoutTimerId) { |
| #ifdef INSTRUMENT_LAYOUT_SCHEDULING |
| if (m_part->xmlDocImpl() && !m_part->xmlDocImpl()->ownerElement()) |
| printf("Layout timer fired at %d\n", m_part->xmlDocImpl()->elapsedTime()); |
| #endif |
| layout(); |
| } |
| } |
| |
| void KHTMLView::scheduleRelayout() |
| { |
| if (!d->layoutSchedulingEnabled) |
| return; |
| |
| if (!m_part->xmlDocImpl() || !m_part->xmlDocImpl()->shouldScheduleLayout()) |
| return; |
| |
| int delay = m_part->xmlDocImpl()->minimumLayoutDelay(); |
| if (d->layoutTimerId && d->delayedLayout && !delay) |
| unscheduleRelayout(); |
| if (d->layoutTimerId) |
| return; |
| |
| d->delayedLayout = delay != 0; |
| |
| #ifdef INSTRUMENT_LAYOUT_SCHEDULING |
| if (!m_part->xmlDocImpl()->ownerElement()) |
| printf("Scheduling layout for %d\n", delay); |
| #endif |
| |
| d->layoutTimerId = startTimer(delay); |
| } |
| |
| bool KHTMLView::layoutPending() |
| { |
| return d->layoutTimerId; |
| } |
| |
| bool KHTMLView::haveDelayedLayoutScheduled() |
| { |
| return d->layoutTimerId && d->delayedLayout; |
| } |
| |
| void KHTMLView::unscheduleRelayout() |
| { |
| if (!d->layoutTimerId) |
| return; |
| |
| #ifdef INSTRUMENT_LAYOUT_SCHEDULING |
| if (m_part->xmlDocImpl() && !m_part->xmlDocImpl()->ownerElement()) |
| printf("Layout timer unscheduled at %d\n", m_part->xmlDocImpl()->elapsedTime()); |
| #endif |
| |
| killTimer(d->layoutTimerId); |
| d->layoutTimerId = 0; |
| d->delayedLayout = false; |
| } |
| |
| bool KHTMLView::isTransparent() const |
| { |
| return d->isTransparent; |
| } |
| |
| void KHTMLView::setTransparent(bool isTransparent) |
| { |
| d->isTransparent = isTransparent; |
| } |
| |