diff --git a/src/khtmlview.cpp b/src/khtmlview.cpp
index cd69bb4..a82afca 100644
--- a/src/khtmlview.cpp
+++ b/src/khtmlview.cpp
@@ -1,4523 +1,4529 @@
/* 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-2008 Apple Computer, Inc.
* 2008 Allan Sandfeld Jensen
* 2006-2008 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 "khtml_part.h"
#include "khtml_events.h"
#include
#if HAVE_X11
#include
#endif
#include "html/html_documentimpl.h"
#include "html/html_inlineimpl.h"
#include "html/html_formimpl.h"
#include "html/htmltokenizer.h"
#include "editing/editor.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 "misc/loader.h"
#include "khtml_settings.h"
#include "khtml_printsettings.h"
#include "khtmlpart_p.h"
#include
#include "khtml_debug.h"
#include
#include
#include
#include <../khtml_version.h>
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//#define DEBUG_FLICKER
#include
#if HAVE_X11
#include
#include
#elif defined(Q_OS_WIN)
#include
#endif
#if 0
namespace khtml
{
void dumpLineBoxes(RenderFlow *flow);
}
#endif
using namespace DOM;
using namespace khtml;
#ifndef NDEBUG
static const int sFirstLayoutDelay = 520;
static const int sParsingLayoutsInterval = 380;
static const int sLayoutAttemptDelay = 300;
#else
static const int sFirstLayoutDelay = 280;
static const int sParsingLayoutsInterval = 320;
static const int sLayoutAttemptDelay = 200;
#endif
static const int sLayoutAttemptIncrement = 20;
static const int sParsingLayoutsIncrement = 60;
static const int sSmoothScrollTime = 128;
static const int sSmoothScrollTick = 16;
static const int sSmoothScrollMinStaticPixels = 320 * 200;
static const int sMaxMissedDeadlines = 12;
static const int sWayTooMany = -1;
class KHTMLViewPrivate
{
friend class KHTMLView;
public:
enum PseudoFocusNodes {
PFNone,
PFTop,
PFBottom
};
enum StaticBackgroundState {
SBNone = 0,
SBPartial,
SBFull
};
enum CompletedState {
CSNone = 0,
CSFull,
CSActionPending
};
KHTMLViewPrivate(KHTMLView *v)
: underMouse(nullptr), underMouseNonShared(nullptr), oldUnderMouse(nullptr)
{
postponed_autorepeat = nullptr;
scrollingFromWheelTimerId = 0;
smoothScrollMode = KHTMLView::SSMWhenEfficient;
reset();
vpolicy = Qt::ScrollBarAsNeeded;
hpolicy = Qt::ScrollBarAsNeeded;
formCompletions = nullptr;
prevScrollbarVisible = true;
possibleTripleClick = false;
emitCompletedAfterRepaint = CSNone;
cursorIconWidget = nullptr;
cursorIconType = KHTMLView::LINK_NORMAL;
m_mouseScrollTimer = nullptr;
m_mouseScrollIndicator = nullptr;
contentsX = 0;
contentsY = 0;
view = v;
}
~KHTMLViewPrivate()
{
delete formCompletions;
delete postponed_autorepeat;
if (underMouse) {
underMouse->deref();
}
if (underMouseNonShared) {
underMouseNonShared->deref();
}
if (oldUnderMouse) {
oldUnderMouse->deref();
}
delete cursorIconWidget;
delete m_mouseScrollTimer;
delete m_mouseScrollIndicator;
}
void reset()
{
if (underMouse) {
underMouse->deref();
}
underMouse = nullptr;
if (underMouseNonShared) {
underMouseNonShared->deref();
}
underMouseNonShared = nullptr;
if (oldUnderMouse) {
oldUnderMouse->deref();
}
oldUnderMouse = nullptr;
linkPressed = false;
staticWidget = SBNone;
fixedObjectsCount = 0;
staticObjectsCount = 0;
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
scrollBarMoved = false;
contentsMoving = false;
ignoreWheelEvents = false;
scrollingFromWheel = QPoint(-1, -1);
borderX = 30;
borderY = 30;
steps = 0;
dx = dy = 0;
paged = false;
clickX = -1;
clickY = -1;
clickCount = 0;
isDoubleClick = false;
scrollingSelf = false;
delete postponed_autorepeat;
postponed_autorepeat = nullptr;
layoutTimerId = 0;
repaintTimerId = 0;
scrollTimerId = 0;
scrollSuspended = false;
scrollSuspendPreActivate = false;
smoothScrolling = false;
smoothScrollModeIsDefault = true;
shouldSmoothScroll = false;
smoothScrollMissedDeadlines = 0;
hasFrameset = false;
complete = false;
firstLayoutPending = true;
#ifdef SPEED_DEBUG
firstRepaintPending = true;
#endif
needsFullRepaint = true;
dirtyLayout = false;
layoutSchedulingEnabled = true;
painting = false;
layoutCounter = 0;
layoutAttemptCounter = 0;
scheduledLayoutCounter = 0;
updateRegion = QRegion();
m_dialogsAllowed = true;
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 = nullptr;
m_clipHolder = nullptr;
}
void newScrollTimer(QWidget *view, int tid)
{
//qCDebug(KHTML_LOG) << "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;
}
void startScrolling()
{
smoothScrolling = true;
smoothScrollTimer.start(sSmoothScrollTick);
shouldSmoothScroll = false;
}
void stopScrolling()
{
smoothScrollTimer.stop();
dx = dy = 0;
steps = 0;
updateContentsXY();
smoothScrolling = false;
shouldSmoothScroll = false;
}
void updateContentsXY()
{
contentsX = QApplication::isRightToLeft() ?
view->horizontalScrollBar()->maximum() - view->horizontalScrollBar()->value() : view->horizontalScrollBar()->value();
contentsY = view->verticalScrollBar()->value();
}
void scrollAccessKeys(int dx, int dy)
{
QList wl = view->widget()->findChildren("KHTMLAccessKey");
foreach (QLabel *w, wl) {
w->move(w->pos() + QPoint(dx, dy));
}
}
void scrollExternalWidgets(int dx, int dy)
{
if (visibleWidgets.isEmpty()) {
return;
}
QHashIterator it(visibleWidgets);
while (it.hasNext()) {
it.next();
it.value()->move(it.value()->pos() + QPoint(dx, dy));
}
}
NodeImpl *underMouse;
NodeImpl *underMouseNonShared;
NodeImpl *oldUnderMouse;
// Do not adjust bitfield enums sizes.
// They are oversized because they are signed on some platforms.
bool tabMovePending: 1;
bool lastTabbingDirection: 1;
PseudoFocusNodes pseudoFocusNode: 3;
bool scrollBarMoved: 1;
bool contentsMoving: 1;
Qt::ScrollBarPolicy vpolicy;
Qt::ScrollBarPolicy hpolicy;
bool prevScrollbarVisible: 1;
bool linkPressed: 1;
bool ignoreWheelEvents: 1;
StaticBackgroundState staticWidget: 3;
int staticObjectsCount;
int fixedObjectsCount;
int zoomLevel;
int borderX, borderY;
int dx, dy;
int steps;
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 : 3;
bool scrollSuspended : 1;
bool scrollSuspendPreActivate : 1;
KHTMLView::SmoothScrollingMode smoothScrollMode : 3;
bool smoothScrolling : 1;
bool smoothScrollModeIsDefault : 1;
bool shouldSmoothScroll : 1;
bool hasFrameset : 1;
bool complete : 1;
bool firstLayoutPending : 1;
#ifdef SPEED_DEBUG
bool firstRepaintPending : 1;
#endif
bool layoutSchedulingEnabled : 1;
bool needsFullRepaint : 1;
bool painting : 1;
bool possibleTripleClick : 1;
bool dirtyLayout : 1;
bool m_dialogsAllowed : 1;
short smoothScrollMissedDeadlines;
int layoutCounter;
int layoutAttemptCounter;
int scheduledLayoutCounter;
QRegion updateRegion;
QTimer smoothScrollTimer;
QTime smoothScrollStopwatch;
QHash visibleWidgets;
bool accessKeysEnabled;
bool accessKeysActivated;
bool accessKeysPreActivate;
CompletedState emitCompletedAfterRepaint;
QLabel *cursorIconWidget;
KHTMLView::LinkCursor cursorIconType;
// 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;
KHTMLView *view;
};
#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->document()->isHTMLDocument() &&
(map = static_cast(img->document())->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().trimmed();
r = node->getRect();
}
region |= QRect(contentsToViewport(r.topLeft()), r.size());
if (!s.isEmpty()) {
QToolTip::showText(he->globalPos(),
Qt::convertFromPlainText(s, Qt::WhiteSpaceNormal),
widget(), region);
break;
}
}
node = node->parentNode();
}
// Qt makes tooltip events happen nearly immediately when a preceding one was processed in the past few seconds.
// We don't want that feature to apply to web tootlips however, as it clashes with dhtml menus.
// So we'll just pretend we did not process that event.
return false;
}
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: {
updateScrollBars();
return QAbstractScrollArea::event(e);
}
case QEvent::PaletteChange:
slotPaletteChanged();
return QScrollArea::event(e);
default:
return QScrollArea::event(e);
}
}
#endif
KHTMLView::KHTMLView(KHTMLPart *part, QWidget *parent)
: QScrollArea(parent), d(new KHTMLViewPrivate(this))
{
m_medium = "screen";
m_part = part;
QScrollArea::setVerticalScrollBarPolicy(d->vpolicy);
QScrollArea::setHorizontalScrollBarPolicy(d->hpolicy);
initWidget();
widget()->setMouseTracking(true);
}
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::initWidget()
{
// 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);
// Do *not* remove this attribute frivolously.
// You might not notice a change of behaviour in Debug builds
// but removing opaque events will make QWidget::scroll fail horribly
// in Release builds.
widget()->setAttribute(Qt::WA_OpaquePaintEvent);
verticalScrollBar()->setCursor(Qt::ArrowCursor);
horizontalScrollBar()->setCursor(Qt::ArrowCursor);
connect(&d->smoothScrollTimer, SIGNAL(timeout()), this, SLOT(scrollTick()));
}
void KHTMLView::resizeContentsToViewport()
{
QSize s = viewport()->size();
resizeContents(s.width(), s.height());
}
// called by KHTMLPart::clear()
void KHTMLView::clear()
{
if (d->accessKeysEnabled && d->accessKeysActivated) {
accessKeysTimeout();
}
viewport()->unsetCursor();
if (d->cursorIconWidget) {
d->cursorIconWidget->hide();
}
if (d->smoothScrolling) {
d->stopScrolling();
}
d->reset();
QAbstractEventDispatcher::instance()->unregisterTimers(this);
emit cleared();
QScrollArea::setHorizontalScrollBarPolicy(d->hpolicy);
QScrollArea::setVerticalScrollBarPolicy(d->vpolicy);
verticalScrollBar()->setEnabled(false);
horizontalScrollBar()->setEnabled(false);
}
void KHTMLView::hideEvent(QHideEvent *e)
{
QScrollArea::hideEvent(e);
}
void KHTMLView::showEvent(QShowEvent *e)
{
QScrollArea::showEvent(e);
}
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);
if (!widget()->isVisible()) {
updateScrollBars();
}
}
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 -= verticalScrollBar()->sizeHint().width();
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 -= horizontalScrollBar()->sizeHint().height();
ret = qMax(0, ret);
}
return ret;
}
}
return viewport()->height();
}
void KHTMLView::setContentsPos(int x, int y)
{
horizontalScrollBar()->setValue(QApplication::isRightToLeft() ?
horizontalScrollBar()->maximum() - x : x);
verticalScrollBar()->setValue(y);
}
void KHTMLView::scrollBy(int x, int y)
{
if (d->scrollTimerId) {
d->newScrollTimer(this, 0);
}
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->haveZoom()) {
const int z = d->zoomLevel;
x = x * z / 100;
y = y * z / 100;
w = w * z / 100;
h = h * z / 100;
}
x -= contentsX();
y -= contentsY();
}
void KHTMLView::revertTransforms(int &x, int &y, int &w, int &h) const
{
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*/)
{
updateScrollBars();
// If we didn't load anything, make white area as big as the view
if (!m_part->xmlDocImpl()) {
resizeContentsToViewport();
}
// Viewport-dependent media queries may cause us to need completely different style information.
if (m_part->xmlDocImpl() && m_part->xmlDocImpl()->styleSelector()->affectedByViewportChange()) {
m_part->xmlDocImpl()->updateStyleSelector();
}
if (d->layoutSchedulingEnabled) {
layout();
}
QApplication::sendPostedEvents(viewport(), QEvent::Paint);
if (m_part && m_part->xmlDocImpl()) {
if (m_part->parentPart()) {
// sub-frame : queue the resize event until our toplevel is done layouting
khtml::ChildFrame *cf = m_part->parentPart()->frame(m_part);
if (cf && !cf->m_partContainerElement.isNull()) {
cf->m_partContainerElement.data()->postResizeEvent();
}
} else {
// toplevel : dispatch sub-frames'resize events before our own
HTMLPartContainerElementImpl::sendPostedResizeEvents();
m_part->xmlDocImpl()->dispatchWindowEvent(EventImpl::RESIZE_EVENT, false, false);
}
}
}
void KHTMLView::paintEvent(QPaintEvent *e)
{
QRect r = e->rect();
QRect v(contentsX(), contentsY(), visibleWidth(), visibleHeight());
QPoint off(contentsX(), contentsY());
r.translate(off);
r = r.intersected(v);
if (!r.isValid() || r.isEmpty()) {
return;
}
QPainter p(widget());
p.translate(-off);
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();
} else if (m_part->xmlDocImpl()->tokenizer()) {
m_part->xmlDocImpl()->tokenizer()->setNormalYieldDelay();
}
if (d->painting) {
// qCDebug(KHTML_LOG) << "WARNING: paintEvent reentered! ";
return;
}
d->painting = true;
m_part->xmlDocImpl()->renderer()->layer()->paint(&p, r);
if (d->hasFrameset) {
NodeImpl *body = static_cast(m_part->xmlDocImpl())->body();
if (body && body->renderer() && body->id() == ID_FRAMESET) {
static_cast(body->renderer())->paintFrameSetRules(&p, r);
} else {
d->hasFrameset = false;
}
}
khtml::DrawContentsEvent event(&p, ex, ey, ew, eh);
QApplication::sendEvent(m_part, &event);
if (d->contentsMoving && !d->smoothScrolling && widget()->underMouse()) {
QMouseEvent *tempEvent = new QMouseEvent(QEvent::MouseMove, widget()->mapFromGlobal(QCursor::pos()),
Qt::NoButton, Qt::NoButton, Qt::NoModifier);
QApplication::postEvent(widget(), tempEvent);
}
#ifdef SPEED_DEBUG
if (d->firstRepaintPending && !m_part->parentPart()) {
qCDebug(KHTML_LOG) << "FIRST PAINT:" << m_part->d->m_parsetime.elapsed();
}
d->firstRepaintPending = false;
#endif
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;
d->dirtyLayout = true;
// the reference object for the overflow property on canvas
RenderObject *ref = nullptr;
RenderObject *root = document->documentElement() ? document->documentElement()->renderer() : nullptr;
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);
d->hasFrameset = 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->firstLayoutPending;
if (_height != visibleHeight() || _width != visibleWidth()) {
;
d->needsFullRepaint = true;
_height = visibleHeight();
_width = visibleWidth();
}
canvas->layout();
emit finishedLayout();
if (d->firstLayoutPending) {
// make sure firstLayoutPending is set to false now in case this layout
// wasn't scheduled
d->firstLayoutPending = false;
verticalScrollBar()->setEnabled(true);
horizontalScrollBar()->setEnabled(true);
}
d->layoutCounter++;
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) {
if (dlg->testAttribute(Qt::WA_ShowModal)) {
// qCDebug(KHTML_LOG) << "closeChildDialogs: closing dialog " << dlg;
// close() ends up calling QButton::animateClick, which isn't immediate
// we need something the exits the event loop immediately (#49068)
dlg->reject();
}
}
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;
}
void KHTMLView::setSmoothScrollingMode(SmoothScrollingMode m)
{
d->smoothScrollMode = m;
d->smoothScrollModeIsDefault = false;
if (d->smoothScrolling && !m) {
d->stopScrolling();
}
}
void KHTMLView::setSmoothScrollingModeDefault(SmoothScrollingMode m)
{
// check for manual override
if (!d->smoothScrollModeIsDefault) {
return;
}
d->smoothScrollMode = m;
if (d->smoothScrolling && !m) {
d->stopScrolling();
}
}
KHTMLView::SmoothScrollingMode KHTMLView::smoothScrollingMode() const
{
return d->smoothScrollMode;
}
//
// 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);
// qCDebug(KHTML_LOG) << "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);
//qCDebug(KHTML_LOG) << "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(KSharedConfig::openConfig(), "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 = nullptr;
if (d->m_mouseScrollIndicator) {
d->m_mouseScrollIndicator->hide();
}
}
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;
}
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);
// qCDebug(KHTML_LOG) << "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);
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);
// qCDebug(KHTML_LOG) << "mouse move: " << _mouse->pos()
// << " button " << _mouse->button()
// << " state " << _mouse->state();
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
}
khtml::RenderObject *r = target ? target->renderer() : nullptr;
bool setCursor = true;
bool forceDefault = false;
if (r && r->isWidget()) {
RenderWidget *rw = static_cast(r);
KHTMLWidget *kw = qobject_cast(rw->widget()) ? dynamic_cast(rw->widget()) : nullptr;
if (kw && kw->m_kwp->isRedirected()) {
setCursor = false;
} else if (QLineEdit *le = qobject_cast(rw->widget())) {
QList wl = le->findChildren("KLineEditButton");
// force arrow cursor above lineedit clear button
foreach (QWidget *w, wl) {
if (w->underMouse()) {
forceDefault = true;
break;
}
}
} else if (QTextEdit *te = qobject_cast(rw->widget())) {
if (te->verticalScrollBar()->underMouse() || te->horizontalScrollBar()->underMouse()) {
forceDefault = true;
}
}
}
khtml::RenderStyle *style = (r && r->style()) ? r->style() : nullptr;
QCursor c;
LinkCursor linkCursor = LINK_NORMAL;
switch (!forceDefault ? (style ? style->cursor() : CURSOR_AUTO) : CURSOR_DEFAULT) {
case CURSOR_AUTO:
if (r && r->isText() && ((m_part->d->m_bMousePressed && m_part->d->editor_context.m_beganSelectingText) ||
!r->isPointInsideSelection(xm, ym, m_part->caret()))) {
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().shape() != c.shape() || c.shape() == Qt::BitmapCursor)) {
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()) {
#if HAVE_X11
if (!d->cursorIconWidget) {
#if HAVE_X11
d->cursorIconWidget = new QLabel(nullptr, Qt::X11BypassWindowManagerHint);
XSetWindowAttributes attr;
attr.save_under = True;
XChangeWindowAttributes(QX11Info::display(), d->cursorIconWidget->winId(), CWSaveUnder, &attr);
#else
d->cursorIconWidget = new QLabel(NULL, NULL);
//TODO
#endif
}
// Update the pixmap if need be.
if (linkCursor != d->cursorIconType) {
d->cursorIconType = linkCursor;
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(), nullptr, true);
d->cursorIconWidget->resize(icon_pixmap.width(), icon_pixmap.height());
d->cursorIconWidget->setMask(icon_pixmap.createMaskFromColor(Qt::transparent));
d->cursorIconWidget->setPixmap(icon_pixmap);
d->cursorIconWidget->update();
}
QPoint c_pos = QCursor::pos();
d->cursorIconWidget->move(c_pos.x() + 15, c_pos.y() + 15);
#if HAVE_X11
XRaiseWindow(QX11Info::display(), d->cursorIconWidget->winId());
QApplication::flush();
#elif defined(Q_OS_WIN)
SetWindowPos(d->cursorIconWidget->winId(), HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
#else
//TODO?
#endif
d->cursorIconWidget->show();
#endif
} else if (d->cursorIconWidget) {
d->cursorIconWidget->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 = nullptr;
}
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() : nullptr;
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 = nullptr;
return ret;
}
} else { // QEvent::KeyRelease
// Discard postponed "autorepeat key-release" events that didn't see
// a keypress after them (e.g. due to QAccel)
delete d->postponed_autorepeat;
d->postponed_autorepeat = nullptr;
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)
{
// If CTRL was hit, be prepared for access keys
if (d->accessKeysEnabled && _ke->key() == Qt::Key_Control && !(_ke->modifiers() & ~Qt::ControlModifier) && !d->accessKeysActivated) {
d->accessKeysPreActivate = true;
_ke->accept();
return;
}
if (_ke->key() == Qt::Key_Shift && !(_ke->modifiers() & ~Qt::ShiftModifier)) {
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();
_ke->accept();
return;
}
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:
d->shouldSmoothScroll = true;
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:
d->shouldSmoothScroll = true;
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::keyReleaseEvent(QKeyEvent *_ke)
{
if (d->scrollSuspendPreActivate && _ke->key() != Qt::Key_Shift) {
d->scrollSuspendPreActivate = false;
}
if (_ke->key() == Qt::Key_Shift && d->scrollSuspendPreActivate && !(_ke->modifiers() & Qt::ShiftModifier))
if (d->scrollTimerId) {
d->scrollSuspended = !d->scrollSuspended;
if (d->scrollSuspended) {
d->stopScrolling();
}
}
if (d->accessKeysEnabled) {
if (d->accessKeysPreActivate && _ke->key() != Qt::Key_Control) {
d->accessKeysPreActivate = false;
}
if (d->accessKeysPreActivate && !(_ke->modifiers() & 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())
// qCDebug(KHTML_LOG) << "focusNode.name: "
// << m_part->xmlDocImpl()->focusNode()->nodeName().string();
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();
QPoint off;
KHTMLView *v = m_kwp->isRedirected() ? m_kwp->rootViewPos(off) : this;
pos = v->viewport()->mapFromGlobal(pos);
pos -= off;
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()
&& innerNode.handle()->renderer()->shouldSelect()) {
m_part->extendSelectionTo(xm, ym, 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);
if (!(w->objectName() == "KLineEditButton")) {
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;
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()->isContentEditable())) {
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->cursorIconWidget) {
d->cursorIconWidget->hide();
}
m_part->resetHoverText();
}
QWidget *view = widget();
if (o == view) {
if (widgetEvent(e)) {
return true;
} else if (e->type() == QEvent::Resize) {
updateScrollBars();
return false;
}
} 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;
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
bool isUpdate = false;
#endif
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:
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
isUpdate = true;
#endif
// 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();
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
QRect pr = isUpdate ? static_cast(e)->region().boundingRect() : static_cast(e)->rect();
bool asap = !d->contentsMoving && qobject_cast(c);
if (isUpdate) {
setInPaintEventFlag(w, false);
if (asap) {
w->repaint(static_cast(e)->region());
} else {
w->update(static_cast(e)->region());
}
setInPaintEventFlag(w);
}
// QScrollView needs fast repaints
if (asap && !isUpdate && !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);
}
#endif
}
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);
ke->accept();
} else {
keyReleaseEvent(ke);
ke->accept();
}
block = true;
}
if (qobject_cast(w->parentWidget()) &&
e->type() == QEvent::KeyPress) {
// Since keypress events on the upload widget will
// be forwarded to the lineedit anyway,
// block the original copy at this level to prevent
// double-emissions of events it doesn't accept
e->ignore();
block = true;
}
break;
case QEvent::FocusIn:
case QEvent::FocusOut: {
QPoint dummy;
KHTMLView *root = m_kwp->rootViewPos(dummy);
if (!root) {
root = this;
}
block = static_cast(e)->reason() != Qt::MouseFocusReason || root->underMouse();
break;
}
default:
break;
}
if (block) {
//qDebug("eating event");
return true;
}
}
}
// qCDebug(KHTML_LOG) <<"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);
}
}
}
break;
}
case QEvent::Move: {
if (static_cast(e)->pos() != QPoint(0, 0)) {
widget()->move(0, 0);
updateScrollBars();
return true;
}
break;
}
default:
break;
}
return false;
}
bool KHTMLView::hasLayoutPending()
{
return d->layoutTimerId && !d->firstLayoutPending;
}
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();
//qCDebug(KHTML_LOG)<<"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 {
// if it's an editable element, activate the caret
if (!m_part->isCaretMode() && newFocusNode->isContentEditable()) {
// qCDebug(KHTML_LOG) << "show caret! fn: " << newFocusNode->nodeName().string();
m_part->clearCaretRectIfNeeded();
m_part->d->editor_context.m_selection.moveTo(Position(newFocusNode, 0L));
m_part->setCaretVisible(true);
} else {
m_part->setCaretVisible(false);
// qCDebug(KHTML_LOG) << "hide caret! fn: " << newFocusNode->nodeName().string();
}
m_part->notifySelectionChanged();
targetVisible = scrollTo(newFocusNode->getRect());
}
if (targetVisible) {
//qCDebug(KHTML_LOG) << " 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(nullptr, this, taken, false);
displayAccessKeys(nullptr, 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 != nullptr; 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, widget());
lab->setAttribute(Qt::WA_DeleteOnClose);
lab->setObjectName("KHTMLAccessKey");
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->setContentsMargins(3, 3, 3, 3);
lab->adjustSize();
lab->setParent(widget());
lab->setAutoFillBackground(true);
lab->move(
qMin(rec.left() + rec.width() / 2 - contentsX(), contentsWidth() - lab->width()),
qMin(rec.top() + rec.height() / 2 - contentsY(), 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 == nullptr) { // the active frame (where the accesskey was pressed)
const 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 == nullptr) {
return false;
}
}
// Scroll the view as necessary to ensure that the new focus node is visible
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
m_part->xmlDocImpl()->setFocusNode(node);
if (node != nullptr && node->hasOneRef()) { // deleted, only held by guard
return true;
}
emit m_part->nodeActivated(Node(node));
if (node != nullptr && 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 != nullptr;
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 != nullptr;
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
QLinkedList< AccessKeyData > data; // Note: this has to be a list type that keep iterators valid
// when other entries are removed
QMap< NodeImpl *, QString > labels = buildLabels(m_part->xmlDocImpl());
QMap< QString, QChar > hrefs;
for (NodeImpl *n = m_part->xmlDocImpl();
n != nullptr;
n = n->traverseNextNode()) {
if (n->isElementNode()) {
ElementImpl *element = static_cast< ElementImpl * >(n);
if (element->renderer() == nullptr) {
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 = element->getAttribute(ATTR_HREF).trimSpaces().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;
}
// build map of manually assigned accesskeys and their targets
DOMString akey = element->getAttribute(ATTR_ACCESSKEY);
if (akey.length() == 1) {
hrefs[url] = akey.string()[ 0 ].toUpper();
continue; // has accesskey set, ignore
}
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
const 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 != nullptr;
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 (QLinkedList< AccessKeyData >::Iterator it = data.begin();
it != data.end();
) {
if ((*it).priority != priority) {
++it;
continue;
}
if (keys.isEmpty()) {
break;
}
QString text = (*it).text;
QChar key;
const QString url = (*it).url;
// an identical link already has an accesskey assigned
if (hrefs.contains(url)) {
it = data.erase(it);
continue;
}
if (!text.isEmpty()) {
const 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()) {
const 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);
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 (QLinkedList< 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;
}
namespace
{
class QPointerDeleter
{
public:
explicit QPointerDeleter(QObject *o) : obj(o) {}
~QPointerDeleter()
{
delete obj;
}
private:
const QPointer obj;
};
}
void KHTMLView::print(bool quick)
+{
+ QPrinter printer;
+ print(&printer, quick);
+}
+
+void KHTMLView::print(QPrinter *_printer, bool quick)
{
if (!m_part->xmlDocImpl()) {
return;
}
khtml::RenderCanvas *root = static_cast(m_part->xmlDocImpl()->renderer());
if (!root) {
return;
}
- QPrinter printer;
+ QPrinter &printer = *_printer;
QPointer dialog(new QPrintDialog(&printer, this));
QPointer printSettings(new KHTMLPrintSettings(dialog)); //XXX: doesn't save settings between prints like this
dialog->setOptionTabs(QList() << printSettings.data());
const QPointerDeleter dialogDeleter(dialog);
QString docname = m_part->xmlDocImpl()->URL().toDisplayString();
if (!docname.isEmpty()) {
docname = KStringHandler::csqueeze(docname, 80);
}
if (quick || (dialog->exec() && dialog)) { /*'this' and thus dialog might have been deleted while 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(KHTML_VERSION_MAJOR).arg(KHTML_VERSION_MINOR).arg(KHTML_VERSION_PATCH));
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; }"
);
// qCDebug(KHTML_LOG) << "printing: physical page width = " << printer.width()
// << " height = " << printer.height();
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();
// check sizes ask for action.. (scale or clip)
bool printHeader = printSettings->printHeader();
int headerHeight = 0;
QFont headerFont("Sans Serif", 8);
QString headerLeft = QDate::currentDate().toString(Qt::DefaultLocaleShortDate);
QString headerMid = docname;
QString headerRight;
if (printHeader) {
p->setFont(headerFont);
headerHeight = (p->fontMetrics().lineSpacing() * 3) / 2;
}
// ok. now print the pages.
// qCDebug(KHTML_LOG) << "printing: html page width = " << root->docWidth()
// << " height = " << root->docHeight();
// qCDebug(KHTML_LOG) << "printing: margins left = " << printer.pageRect().left() - printer.paperRect().left()
// << " top = " << printer.pageRect().top() - printer.paperRect().top();
// qCDebug(KHTML_LOG) << "printing: paper width = " << printer.width()
// << " height = " << printer.height();
// 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;
#ifndef QT_NO_TRANSFORMATIONS
bool scalePage = false;
double scale = 0.0;
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
// qCDebug(KHTML_LOG) << "printing: scaled html width = " << pageWidth
// << " height = " << pageHeight;
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();
}
#ifndef QT_NO_TRANSFORMATIONS
if (scalePage) {
p->scale(scale, scale);
}
#endif
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);
}
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));
// qCDebug(KHTML_LOG) << "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(nullptr);
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;
}
#ifdef SPEED_DEBUG
d->firstRepaintPending = false;
#endif
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)
{
#ifdef SPEED_DEBUG
d->firstRepaintPending = false;
#endif
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(bool partial)
{
// full static iframe is irreversible for now
if (d->staticWidget == KHTMLViewPrivate::SBFull && m_kwp->isRedirected()) {
return;
}
d->staticWidget = partial ?
KHTMLViewPrivate::SBPartial : KHTMLViewPrivate::SBFull;
}
void KHTMLView::setHasNormalBackground()
{
// full static iframe is irreversible for now
if (d->staticWidget == KHTMLViewPrivate::SBFull && m_kwp->isRedirected()) {
return;
}
d->staticWidget = KHTMLViewPrivate::SBNone;
}
void KHTMLView::addStaticObject(bool fixed)
{
if (fixed) {
d->fixedObjectsCount++;
} else {
d->staticObjectsCount++;
}
setHasStaticBackground(true /*partial*/);
}
void KHTMLView::removeStaticObject(bool fixed)
{
if (fixed) {
d->fixedObjectsCount--;
} else {
d->staticObjectsCount--;
}
assert(d->fixedObjectsCount >= 0 && d->staticObjectsCount >= 0);
if (!d->staticObjectsCount && !d->fixedObjectsCount) {
setHasNormalBackground();
} else {
setHasStaticBackground(true /*partial*/);
}
}
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(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + "khtml/formcompletions");
}
return d->formCompletions->group("").readEntry(name, QStringList());
}
void KHTMLView::clearCompletionHistory(const QString &name)
{
if (!d->formCompletions) {
d->formCompletions = new KConfig(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + "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(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + "khtml/formcompletions");
}
KConfigGroup cg(d->formCompletions, "NonPasswordStorableSites");
QStringList sites = cg.readEntry("Sites", QStringList());
sites.append(host);
cg.writeEntry("Sites", sites);
cg.sync();
}
void KHTMLView::delNonPasswordStorableSite(const QString &host)
{
if (!d->formCompletions) {
d->formCompletions = new KConfig(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + "khtml/formcompletions");
}
KConfigGroup cg(d->formCompletions, "NonPasswordStorableSites");
QStringList sites = cg.readEntry("Sites", QStringList());
sites.removeOne(host);
cg.writeEntry("Sites", sites);
cg.sync();
}
bool KHTMLView::nonPasswordStorableSite(const QString &host) const
{
if (!d->formCompletions) {
d->formCompletions = new KConfig(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + "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->document() != m_part->xmlDocImpl()) {
d->oldUnderMouse->deref();
d->oldUnderMouse = nullptr;
}
// 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) {
// if the target node is a disabled widget, we don't want any full-blown mouse events
if (targetNode->isGenericFormElement()
&& static_cast(targetNode)->disabled()) {
return true;
}
// 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, nullptr, isWheelEvent ? nullptr : _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(), QPoint(pageX, pageY) - 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);
d->m_mouseEventsTarget = nullptr;
}
swallowEvent = true;
} else {
targetNode->dispatchEvent(me, exceptioncode, true);
bool defaultHandled = me->defaultHandled();
if (defaultHandled || me->defaultPrevented()) {
swallowEvent = true;
}
}
if (eventId == EventImpl::MOUSEDOWN_EVENT && !me->defaultPrevented()) {
// 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(nullptr);
}
}
me->deref();
}
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->firstLayoutPending) {
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, e->pos(), 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->smoothScrollMode != SSMDisabled) {
d->shouldSmoothScroll = true;
}
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 overridden for BC reasons only...
QScrollArea::dragEnterEvent(ev);
}
void KHTMLView::dropEvent(QDropEvent *ev)
{
// Still overridden for BC reasons only...
QScrollArea::dropEvent(ev);
}
void KHTMLView::focusInEvent(QFocusEvent *e)
{
DOM::NodeImpl *fn = m_part->xmlDocImpl() ? m_part->xmlDocImpl()->focusNode() : nullptr;
if (fn && fn->renderer() && fn->renderer()->isWidget() &&
(e->reason() != Qt::MouseFocusReason) &&
static_cast(fn->renderer())->widget()) {
static_cast(fn->renderer())->widget()->setFocus();
}
m_part->setSelectionVisible();
QScrollArea::focusInEvent(e);
}
void KHTMLView::focusOutEvent(QFocusEvent *e)
{
if (m_part) {
m_part->stopAutoScroll();
m_part->setSelectionVisible(false);
}
if (d->cursorIconWidget) {
d->cursorIconWidget->hide();
}
QScrollArea::focusOutEvent(e);
}
void KHTMLView::scrollContentsBy(int dx, int dy)
{
if (!dx && !dy) {
return;
}
if (!d->firstLayoutPending && !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->shouldSmoothScroll && d->smoothScrollMode != SSMDisabled && m_part->xmlDocImpl() &&
m_part->xmlDocImpl()->renderer() && (d->smoothScrollMode != SSMWhenEfficient || d->smoothScrollMissedDeadlines != sWayTooMany)) {
bool doSmoothScroll = (!d->staticWidget || d->smoothScrollMode == SSMEnabled);
int numStaticPixels = 0;
QRegion r = static_cast(m_part->xmlDocImpl()->renderer())->staticRegion();
// only do smooth scrolling if static region is relatively small
if (!doSmoothScroll && d->staticWidget == KHTMLViewPrivate::SBPartial && r.rects().size() <= 10) {
foreach (const QRect &rr, r.rects()) {
numStaticPixels += rr.width() * rr.height();
}
if ((numStaticPixels < sSmoothScrollMinStaticPixels) || (numStaticPixels * 8 < visibleWidth()*visibleHeight())) {
doSmoothScroll = true;
}
}
if (doSmoothScroll) {
setupSmoothScrolling(dx, dy);
return;
}
}
if (underMouse() && QToolTip::isVisible()) {
QToolTip::hideText();
}
if (!d->scrollingSelf) {
d->scrollBarMoved = true;
d->contentsMoving = true;
// ensure quick reset of contentsMoving flag
scheduleRepaint(0, 0, 0, 0);
}
if (m_part->xmlDocImpl() && m_part->xmlDocImpl()->documentElement()) {
m_part->xmlDocImpl()->documentElement()->dispatchHTMLEvent(EventImpl::SCROLL_EVENT, true, false);
}
if (QApplication::isRightToLeft()) {
dx = -dx;
}
if (!d->smoothScrolling) {
d->updateContentsXY();
} else {
d->contentsX -= dx;
d->contentsY -= dy;
}
if (widget()->pos() != QPoint(0, 0)) {
// qCDebug(KHTML_LOG) << "Static widget wasn't positioned at (0,0). This should NOT happen. Please report this event to developers.";
widget()->move(0, 0);
}
QWidget *w = widget();
QPoint off;
if (m_kwp->isRedirected()) {
// This is a redirected sub frame. Translate to root view context
KHTMLView *v = m_kwp->rootViewPos(off);
if (v) {
w = v->widget();
}
off = viewport()->mapTo(this, off);
}
if (d->staticWidget) {
// now remove from view the external widgets that must have completely
// disappeared after dx/dy scroll delta is effective
if (!d->visibleWidgets.isEmpty()) {
checkExternalWidgetsPosition();
}
if (d->staticWidget == KHTMLViewPrivate::SBPartial
&& m_part->xmlDocImpl() && m_part->xmlDocImpl()->renderer()) {
// static objects might be selectively repainted, like stones in flowing water
QRegion r = static_cast(m_part->xmlDocImpl()->renderer())->staticRegion();
r.translate(-contentsX(), -contentsY());
QVector ar = r.rects();
for (int i = 0; i < ar.size(); ++i) {
widget()->update(ar[i]);
}
r = QRegion(QRect(0, 0, visibleWidth(), visibleHeight())) - r;
ar = r.rects();
for (int i = 0; i < ar.size(); ++i) {
w->scroll(dx, dy, ar[i].translated(off));
}
d->scrollExternalWidgets(dx, dy);
} else {
// we can't avoid a full update
widget()->update();
}
if (d->accessKeysActivated) {
d->scrollAccessKeys(dx, dy);
}
return;
}
if (m_kwp->isRedirected()) {
const QRect rect(off.x(), off.y(), visibleWidth() * d->zoomLevel / 100, visibleHeight() * d->zoomLevel / 100);
w->scroll(dx, dy, rect);
if (d->zoomLevel != 100) {
w->update(rect); // without this update we are getting bad rendering when an iframe is zoomed in
}
} else {
widget()->scroll(dx, dy, widget()->rect() & viewport()->rect());
}
d->scrollExternalWidgets(dx, dy);
if (d->accessKeysActivated) {
d->scrollAccessKeys(dx, dy);
}
}
void KHTMLView::setupSmoothScrolling(int dx, int dy)
{
// old or minimum speed
int ddx = qMax(d->steps ? abs(d->dx) / d->steps : 0, 3);
int ddy = qMax(d->steps ? abs(d->dy) / d->steps : 0, 3);
// full scroll is remaining scroll plus new scroll
d->dx = d->dx + dx;
d->dy = d->dy + dy;
if (d->dx == 0 && d->dy == 0) {
d->stopScrolling();
return;
}
d->steps = (sSmoothScrollTime - 1) / sSmoothScrollTick + 1;
if (qMax(abs(d->dx), abs(d->dy)) / d->steps < qMax(ddx, ddy)) {
// Don't move slower than average 4px/step in minimum one direction
// This means fewer than normal steps
d->steps = qMax((abs(d->dx) + ddx - 1) / ddx, (abs(d->dy) + ddy - 1) / ddy);
if (d->steps < 1) {
d->steps = 1;
}
}
d->smoothScrollStopwatch.start();
if (!d->smoothScrolling) {
d->startScrolling();
scrollTick();
}
}
void KHTMLView::scrollTick()
{
if (d->dx == 0 && d->dy == 0) {
d->stopScrolling();
return;
}
if (d->steps < 1) {
d->steps = 1;
}
int takesteps = d->smoothScrollStopwatch.restart() / sSmoothScrollTick;
int scroll_x = 0;
int scroll_y = 0;
if (takesteps < 1) {
takesteps = 1;
}
if (takesteps > d->steps) {
takesteps = d->steps;
}
for (int i = 0; i < takesteps; i++) {
int ddx = (d->dx / (d->steps + 1)) * 2;
int ddy = (d->dy / (d->steps + 1)) * 2;
// limit step to requested scrolling distance
if (abs(ddx) > abs(d->dx)) {
ddx = d->dx;
}
if (abs(ddy) > abs(d->dy)) {
ddy = d->dy;
}
// update remaining scroll
d->dx -= ddx;
d->dy -= ddy;
scroll_x += ddx;
scroll_y += ddy;
d->steps--;
}
d->shouldSmoothScroll = false;
scrollContentsBy(scroll_x, scroll_y);
if (takesteps < 2) {
d->smoothScrollMissedDeadlines = 0;
} else {
if (d->smoothScrollMissedDeadlines != sWayTooMany &&
(!m_part->xmlDocImpl() || !m_part->xmlDocImpl()->parsing())) {
d->smoothScrollMissedDeadlines++;
if (d->smoothScrollMissedDeadlines >= sMaxMissedDeadlines) {
// we missed many deadlines in a row!
// time to signal we had enough..
d->smoothScrollMissedDeadlines = sWayTooMany;
}
}
}
}
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)
child->move(x - contentsX(), y - contentsY());
}
void KHTMLView::timerEvent(QTimerEvent *e)
{
// qCDebug(KHTML_LOG) << "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) {
if (d->firstLayoutPending && d->layoutAttemptCounter < 4
&& (!m_part->xmlDocImpl() || !m_part->xmlDocImpl()->readyForLayout())) {
d->layoutAttemptCounter++;
killTimer(d->layoutTimerId);
d->layoutTimerId = 0;
scheduleRelayout();
return;
}
layout();
d->scheduledLayoutCounter++;
if (d->firstLayoutPending) {
d->firstLayoutPending = false;
verticalScrollBar()->setEnabled(true);
horizontalScrollBar()->setEnabled(true);
}
}
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;
const 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.united(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()) {
checkExternalWidgetsPosition();
}
d->dirtyLayout = false;
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::checkExternalWidgetsPosition()
{
QWidget *w;
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);
}
}
void KHTMLView::scheduleRelayout(khtml::RenderObject * /*clippedObj*/)
{
if (!d->layoutSchedulingEnabled || d->layoutTimerId) {
return;
}
int time = 0;
if (d->firstLayoutPending) {
// Any repaint happening while we have no content blanks the viewport ("white flash").
// Hence the need to delay the first layout as much as we can.
// Only if the document gets stuck for too long in incomplete state will we allow the blanking.
time = d->layoutAttemptCounter ?
sLayoutAttemptDelay + sLayoutAttemptIncrement * d->layoutAttemptCounter : sFirstLayoutDelay;
} else if (m_part->xmlDocImpl() && m_part->xmlDocImpl()->parsing()) {
// Delay between successive layouts in parsing mode.
// Increment reflects the decaying importance of visual feedback over time.
time = qMin(2000, sParsingLayoutsInterval + d->scheduledLayoutCounter * sParsingLayoutsIncrement);
}
d->layoutTimerId = startTimer(time);
}
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();
// qCDebug(KHTML_LOG) << "parsing " << parsing;
// qCDebug(KHTML_LOG) << "complete " << d->complete;
int time = parsing && !d->firstLayoutPending ? 150 : (!asap ? (!d->complete ? 80 : 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.united(QRect(x, y, w, h));
if (asap && !parsing) {
unscheduleRepaint();
}
if (!d->repaintTimerId) {
d->repaintTimerId = startTimer(time);
}
// qCDebug(KHTML_LOG) << "starting timer " << time;
}
void KHTMLView::complete(bool pendingAction)
{
// qCDebug(KHTML_LOG) << "KHTMLView::complete()";
d->complete = true;
// is there a relayout pending?
if (d->layoutTimerId) {
// qCDebug(KHTML_LOG) << "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) {
// qCDebug(KHTML_LOG) << "requesting repaint now";
// do it now
killTimer(d->repaintTimerId);
d->repaintTimerId = startTimer(0);
d->emitCompletedAfterRepaint = pendingAction ?
KHTMLViewPrivate::CSActionPending : KHTMLViewPrivate::CSFull;
}
if (!d->emitCompletedAfterRepaint) {
if (!pendingAction) {
emit m_part->completed();
} else {
emit m_part->completed(true);
}
}
}
void KHTMLView::updateScrollBars()
{
const QWidget *view = widget();
if (!view) {
return;
}
QSize p = viewport()->size();
QSize m = maximumViewportSize();
if (m.expandedTo(view->size()) == m) {
p = m; // no scroll bars needed
}
QSize v = view->size();
horizontalScrollBar()->setRange(0, v.width() - p.width());
horizontalScrollBar()->setPageStep(p.width());
verticalScrollBar()->setRange(0, v.height() - p.height());
verticalScrollBar()->setPageStep(p.height());
if (!d->smoothScrolling) {
d->updateContentsXY();
}
}
void KHTMLView::slotMouseScrollTimer()
{
horizontalScrollBar()->setValue(horizontalScrollBar()->value() + d->m_mouseScroll_byX);
verticalScrollBar()->setValue(verticalScrollBar()->value() + d->m_mouseScroll_byY);
}
static DOM::Position positionOfLineBoundary(const DOM::Position &pos, bool toEnd)
{
Selection sel = pos;
sel.expandUsingGranularity(Selection::LINE);
return toEnd ? sel.end() : sel.start();
}
inline static DOM::Position positionOfLineBegin(const DOM::Position &pos)
{
return positionOfLineBoundary(pos, false);
}
inline static DOM::Position positionOfLineEnd(const DOM::Position &pos)
{
return positionOfLineBoundary(pos, true);
}
bool KHTMLView::caretKeyPressEvent(QKeyEvent *_ke)
{
EditorContext *ec = &m_part->d->editor_context;
Selection &caret = ec->m_selection;
Position old_pos = caret.caretPos();
Position pos = old_pos;
bool recalcXPos = true;
bool handled = true;
bool ctrl = _ke->modifiers() & Qt::ControlModifier;
bool shift = _ke->modifiers() & Qt::ShiftModifier;
switch (_ke->key()) {
// -- Navigational keys
case Qt::Key_Down:
pos = old_pos.nextLinePosition(caret.xPosForVerticalArrowNavigation(Selection::EXTENT));
recalcXPos = false;
break;
case Qt::Key_Up:
pos = old_pos.previousLinePosition(caret.xPosForVerticalArrowNavigation(Selection::EXTENT));
recalcXPos = false;
break;
case Qt::Key_Left:
pos = ctrl ? old_pos.previousWordPosition() : old_pos.previousCharacterPosition();
break;
case Qt::Key_Right:
pos = ctrl ? old_pos.nextWordPosition() : old_pos.nextCharacterPosition();
break;
case Qt::Key_PageDown:
// moveCaretNextPage(); ###
break;
case Qt::Key_PageUp:
// moveCaretPrevPage(); ###
break;
case Qt::Key_Home:
if (ctrl)
/*moveCaretToDocumentBoundary(false)*/; // ###
else {
pos = positionOfLineBegin(old_pos);
}
break;
case Qt::Key_End:
if (ctrl)
/*moveCaretToDocumentBoundary(true)*/; // ###
else {
pos = positionOfLineEnd(old_pos);
}
break;
default:
handled = false;
}/*end switch*/
if (pos != old_pos) {
m_part->clearCaretRectIfNeeded();
caret.moveTo(shift ? caret.nonCaretPos() : pos, pos);
int old_x = caret.xPosForVerticalArrowNavigation(Selection::CARETPOS);
m_part->selectionLayoutChanged();
// restore old x-position to prevent recalculation
if (!recalcXPos) {
m_part->d->editor_context.m_xPosForVerticalArrowNavigation = old_x;
}
m_part->emitCaretPositionChanged(pos);
// ### check when to emit it
m_part->notifySelectionChanged();
}
if (handled) {
_ke->accept();
}
return handled;
}
#undef DEBUG_CARETMODE
diff --git a/src/khtmlview.h b/src/khtmlview.h
index 77debe6..76fda45 100644
--- a/src/khtmlview.h
+++ b/src/khtmlview.h
@@ -1,549 +1,559 @@
/* This file is part of the KDE project
Copyright (C) 1997 Martin Jones (mjones@kde.org)
(C) 1998 Waldo Bastian (bastian@kde.org)
(C) 1998, 1999 Torben Weis (weis@kde.org)
(C) 1999 Lars Knoll (knoll@kde.org)
(C) 1999 Antti Koivisto (koivisto@kde.org)
(C) 2006 Germain Garand (germain@ebooksfrance.org)
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.
*/
#ifndef KHTMLVIEW_H
#define KHTMLVIEW_H
#include
// qt includes and classes
#include
class QPainter;
+class QPrinter;
class QRect;
template< typename T > class QVector;
template class QStack;
namespace DOM
{
class HTMLDocumentImpl;
class DocumentImpl;
class ElementImpl;
class HTMLTitleElementImpl;
class HTMLGenericFormElementImpl;
class HTMLFormElementImpl;
class HTMLAnchorElementImpl;
class HTMLInputElementImpl;
class NodeImpl;
class CSSProperty;
}
namespace KJS
{
class WindowFunc;
class ExternalFunc;
}
namespace khtml
{
class RenderObject;
class RenderCanvas;
class RenderLineEdit;
class RenderPartObject;
class RenderWidget;
class RenderLayer;
class RenderBox;
class CSSStyleSelector;
class LineEditWidget;
class CaretBox;
class HTMLTokenizer;
class KHTMLWidgetPrivate;
class KHTMLWidget
{
public:
KHTMLWidget();
~KHTMLWidget();
KHTMLWidgetPrivate *m_kwp;
};
void applyRule(DOM::CSSProperty *prop);
}
class KHTMLPart;
class KHTMLViewPrivate;
namespace khtml
{
}
/**
* Renders and displays HTML in a QScrollArea.
*
* Suitable for use as an application's main view.
**/
class KHTML_EXPORT KHTMLView : public QScrollArea, public khtml::KHTMLWidget
{
Q_OBJECT
friend class DOM::HTMLDocumentImpl;
friend class DOM::HTMLTitleElementImpl;
friend class DOM::HTMLGenericFormElementImpl;
friend class DOM::HTMLFormElementImpl;
friend class DOM::HTMLAnchorElementImpl;
friend class DOM::HTMLInputElementImpl;
friend class DOM::NodeImpl;
friend class DOM::ElementImpl;
friend class DOM::DocumentImpl;
friend class KHTMLPart;
friend class KHTMLFind;
friend class StorePass;
friend class khtml::RenderCanvas;
friend class khtml::RenderObject;
friend class khtml::RenderLineEdit;
friend class khtml::RenderPartObject;
friend class khtml::RenderWidget;
friend class khtml::KHTMLWidgetPrivate;
friend class khtml::RenderLayer;
friend class khtml::RenderBox;
friend class khtml::CSSStyleSelector;
friend class khtml::LineEditWidget;
friend class khtml::HTMLTokenizer;
friend class KJS::WindowFunc;
friend class KJS::ExternalFunc;
friend void khtml::applyRule(DOM::CSSProperty *prop);
public:
/**
* Constructs a KHTMLView.
*/
KHTMLView(KHTMLPart *part, QWidget *parent);
virtual ~KHTMLView();
/**
* Returns a pointer to the KHTMLPart that is
* rendering the page.
**/
KHTMLPart *part() const
{
return m_part;
}
int frameWidth() const
{
return _width;
}
/**
* Sets a margin in x direction.
*/
void setMarginWidth(int x);
/**
* Returns the margin width.
*
* A return value of -1 means the default value will be used.
*/
int marginWidth() const
{
return _marginWidth;
}
/*
* Sets a margin in y direction.
*/
void setMarginHeight(int y);
/**
* Returns the margin height.
*
* A return value of -1 means the default value will be used.
*/
int marginHeight()
{
return _marginHeight;
}
/**
* Sets vertical scrollbar mode.
*
* WARNING: do not call this method on a base class pointer unless you
* specifically want QAbstractScrollArea's variant (not recommended).
* QAbstractScrollArea::setVerticalScrollBarPolicy is *not* virtual.
*/
virtual void setVerticalScrollBarPolicy(Qt::ScrollBarPolicy policy);
/**
* Sets horizontal scrollbar mode.
*
* WARNING: do not call this method on a base class pointer unless you
* specifically want QAbstractScrollArea's variant (not recommended).
* QAbstractScrollArea::setHorizontalScrollBarPolicy is *not* virtual.
*/
virtual void setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy policy);
/**
* Prints the HTML document.
* @param quick if true, fully automated printing, without print dialog
*/
void print(bool quick = false);
+ /**
+ * Prints the HTML document.
+ * @param printer shared printer instance
+ * @param quick if true, fully automated printing, without print dialog
+ *
+ * @since 5.64
+ */
+ void print(QPrinter *printer, bool quick = false);
+
/**
* Display all accesskeys in small tooltips
*/
void displayAccessKeys();
/**
* Returns the contents area's width
*/
int contentsWidth() const;
/**
* Returns the contents area's height
*/
int contentsHeight() const;
/**
* Returns the x coordinate of the contents area point
* that is currently located at the top left in the viewport
*/
int contentsX() const;
/**
* Returns the y coordinate of the contents area point
* that is currently located at the top left in the viewport
*/
int contentsY() const;
/**
* Returns the width of the viewport
*/
int visibleWidth() const;
/**
* Returns the height of the viewport
*/
int visibleHeight() const;
/**
* Place the contents area point x/y
* at the top left of the viewport
*/
void setContentsPos(int x, int y);
/**
* Returns a point translated to viewport coordinates
* @param p the contents area point to translate
*
*/
QPoint contentsToViewport(const QPoint &p) const;
/**
* Returns a point translated to contents area coordinates
* @param p the viewport point to translate
*
*/
QPoint viewportToContents(const QPoint &p) const;
/**
* Returns a point translated to contents area coordinates
* @param x x coordinate of viewport point to translate
* @param y y coordinate of viewport point to translate
* @param cx resulting x coordinate
* @param cy resulting y coordinate
*
*/
void viewportToContents(int x, int y, int &cx, int &cy) const;
/**
* Returns a point translated to viewport coordinates
* @param x x coordinate of contents area point to translate
* @param y y coordinate of contents area point to translate
* @param cx resulting x coordinate
* @param cy resulting y coordinate
*
*/
void contentsToViewport(int x, int y, int &cx, int &cy) const;
/**
* Scrolls the content area by a given amount
* @param x x offset
* @param y y offset
*/
void scrollBy(int x, int y);
/**
* Requests an update of the content area
* @param r the content area rectangle to update
*/
void updateContents(const QRect &r);
void updateContents(int x, int y, int w, int h);
void addChild(QWidget *child, int dx, int dy);
/**
* Requests an immediate repaint of the content area
* @param r the content area rectangle to repaint
*/
void repaintContents(const QRect &r);
void repaintContents(int x, int y, int w, int h);
/**
* Apply a zoom level to the content area
* @param percent a zoom level expressed as a percentage
*/
void setZoomLevel(int percent);
/**
* Retrieve the current zoom level
*
*/
int zoomLevel() const;
/**
* Smooth Scrolling Mode enumeration
* @li SSMDisabled smooth scrolling is disabled
* @li SSMWhenEfficient only use smooth scrolling on pages that do not require a full repaint of the content area when scrolling
* @li SSMAlways smooth scrolling is performed unconditionally
*/
enum SmoothScrollingMode { SSMDisabled = 0, SSMWhenEfficient, SSMEnabled };
/**
* Set the smooth scrolling mode.
*
* Smooth scrolling mode is normally controlled by the configuration file's SmoothScrolling key.
* Using this setter will override the configuration file's settings.
*
* @since 4.1
*/
void setSmoothScrollingMode(SmoothScrollingMode m);
/**
* Retrieve the current smooth scrolling mode
*
* @since 4.1
*/
SmoothScrollingMode smoothScrollingMode() const;
public Q_SLOTS:
/**
* Resize the contents area
* @param w the new width
* @param h the new height
*/
virtual void resizeContents(int w, int h);
/**
* ensure the display is up to date
*/
void layout();
Q_SIGNALS:
/**
* This signal is used for internal layouting. Don't use it to check if rendering finished.
* Use @ref KHTMLPart completed() signal instead.
*/
void finishedLayout();
void cleared();
void zoomView(int);
void hideAccessKeys();
void repaintAccessKeys();
void findAheadActive(bool);
protected:
void clear();
bool event(QEvent *event) override;
void paintEvent(QPaintEvent *) override;
void resizeEvent(QResizeEvent *event) override;
void showEvent(QShowEvent *) override;
void hideEvent(QHideEvent *) override;
bool focusNextPrevChild(bool next) override;
void mousePressEvent(QMouseEvent *) override;
void focusInEvent(QFocusEvent *) override;
void focusOutEvent(QFocusEvent *) override;
void mouseDoubleClickEvent(QMouseEvent *) override;
void mouseMoveEvent(QMouseEvent *) override;
void mouseReleaseEvent(QMouseEvent *) override;
#ifndef QT_NO_WHEELEVENT
void wheelEvent(QWheelEvent *) override;
#endif
void dragEnterEvent(QDragEnterEvent *) override;
void dropEvent(QDropEvent *) override;
void closeEvent(QCloseEvent *) override;
virtual bool widgetEvent(QEvent *);
bool viewportEvent(QEvent *e) override;
bool eventFilter(QObject *, QEvent *) override;
void scrollContentsBy(int dx, int dy) override;
void keyPressEvent(QKeyEvent *_ke) override;
void keyReleaseEvent(QKeyEvent *_ke) override;
void doAutoScroll();
void timerEvent(QTimerEvent *) override;
void setSmoothScrollingModeDefault(SmoothScrollingMode m);
protected Q_SLOTS:
void slotPaletteChanged();
private Q_SLOTS:
void tripleClickTimeout();
void accessKeysTimeout();
void scrollTick();
/**
* @internal
* used for autoscrolling with MMB
*/
void slotMouseScrollTimer();
private:
void resizeContentsToViewport();
void scheduleRelayout(khtml::RenderObject *clippedObj = nullptr);
void unscheduleRelayout();
bool hasLayoutPending();
void scheduleRepaint(int x, int y, int w, int h, bool asap = false);
void unscheduleRepaint();
bool needsFullRepaint() const;
void closeChildDialogs();
bool dialogsAllowed();
void setMouseEventsTarget(QWidget *w);
QWidget *mouseEventsTarget() const;
QStack *clipHolder() const;
void setClipHolder(QStack *ch);
void setPart(KHTMLPart *part);
/**
* Paints the HTML document to a QPainter.
* The document will be scaled to match the width of
* rc and clipped to fit in the height.
* yOff determines the vertical offset in the document to start with.
* more, if nonzero will be set to true if the documents extends
* beyond the rc or false if everything below yOff was painted.
**/
void paint(QPainter *p, const QRect &rc, int yOff = 0, bool *more = nullptr);
void render(QPainter *p, const QRect &r, const QPoint &off);
/**
* Get/set the CSS Media Type.
*
* Media type is set to "screen" for on-screen rendering and "print"
* during printing. Other media types lack the proper support in the
* renderer and are not activated. The DOM tree and the parser itself,
* however, properly handle other media types. To make them actually work
* you only need to enable the media type in the view and if necessary
* add the media type dependent changes to the renderer.
*/
void setMediaType(const QString &medium);
QString mediaType() const;
bool pagedMode() const;
bool scrollTo(const QRect &);
bool focusNextPrevNode(bool next);
bool handleAccessKey(const QKeyEvent *ev);
bool focusNodeWithAccessKey(QChar c, KHTMLView *caller = nullptr);
QMap< DOM::ElementImpl *, QChar > buildFallbackAccessKeys() const;
void displayAccessKeys(KHTMLView *caller, KHTMLView *origview, QVector< QChar > &taken, bool use_fallbacks);
bool isScrollingFromMouseWheel() const;
void setHasStaticBackground(bool partial = false);
void setHasNormalBackground();
void addStaticObject(bool fixed);
void removeStaticObject(bool fixed);
void applyTransforms(int &x, int &y, int &w, int &h) const;
void revertTransforms(int &x, int &y, int &w, int &h) const;
void revertTransforms(int &x, int &y) const;
void checkExternalWidgetsPosition();
void setIgnoreWheelEvents(bool e);
void initWidget();
DOM::NodeImpl *nodeUnderMouse() const;
DOM::NodeImpl *nonSharedNodeUnderMouse() const;
void restoreScrollBar();
QStringList formCompletionItems(const QString &name) const;
void clearCompletionHistory(const QString &name);
void addFormCompletionItem(const QString &name, const QString &value);
void addNonPasswordStorableSite(const QString &host);
void delNonPasswordStorableSite(const QString &host);
bool nonPasswordStorableSite(const QString &host) const;
bool dispatchMouseEvent(int eventId, DOM::NodeImpl *targetNode,
DOM::NodeImpl *targetNodeNonShared, bool cancelable,
int detail, QMouseEvent *_mouse, bool setUnder,
int mouseEventType, int orientation = 0);
bool dispatchKeyEvent(QKeyEvent *_ke);
bool dispatchKeyEventHelper(QKeyEvent *_ke, bool generate_keypress);
void complete(bool pendingAction);
void updateScrollBars();
void setupSmoothScrolling(int dx, int dy);
/**
* Returns the current caret policy when the view is not focused.
* @return a KHTMLPart::CaretDisplay value
*/
int caretDisplayPolicyNonFocused() const;
/**
* Sets the caret display policy when the view is not focused.
* @param policy new display policy as
* defined by KHTMLPart::CaretDisplayPolicy
*/
void setCaretDisplayPolicyNonFocused(int policy);
// -- caret event handler
/**
* Evaluates key presses for caret navigation on editable nodes.
* @return true if event has been handled
*/
bool caretKeyPressEvent(QKeyEvent *);
// ------------------------------------- member variables ------------------------------------
private:
friend class KHTMLViewPrivate;
enum LinkCursor { LINK_NORMAL, LINK_MAILTO, LINK_NEWWINDOW };
void setWidgetVisible(::khtml::RenderWidget *, bool visible);
int _width;
int _height;
int _marginWidth;
int _marginHeight;
KHTMLPart *m_part;
KHTMLViewPrivate *const d;
QString m_medium; // media type
};
#endif