Index: trunk/kdelibs/khtml/khtmlview.cpp
===================================================================
--- trunk/kdelibs/khtml/khtmlview.cpp (revision 261509)
+++ trunk/kdelibs/khtml/khtmlview.cpp (revision 261510)
@@ -1,2979 +1,2979 @@
/* This file is part of the KDE project
*
* Copyright (C) 1998, 1999 Torben Weis
* 1999 Lars Knoll
* 1999 Antti Koivisto
* 2000 Dirk Mueller
* 2003 Leo Savernik
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include "khtmlview.moc"
#include "khtmlview.h"
#include "khtml_part.h"
#include "khtml_events.h"
#include "html/html_documentimpl.h"
#include "html/html_inlineimpl.h"
#include "rendering/render_arena.h"
#include "rendering/render_canvas.h"
#include "rendering/render_frames.h"
#include "rendering/render_replaced.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/htmlhashes.h"
#include "misc/helper.h"
#include "khtml_settings.h"
#include "khtml_printsettings.h"
#include "khtmlpart_p.h"
#ifndef KHTML_NO_CARET
#include "khtml_caret_p.h"
#include "xml/dom2_rangeimpl.h"
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//#define DEBUG_NO_PAINT_BUFFER
#define PAINT_BUFFER_HEIGHT 128
using namespace DOM;
using namespace khtml;
class KHTMLToolTip;
#ifndef QT_NO_TOOLTIP
class KHTMLToolTip : public QToolTip
{
public:
KHTMLToolTip(KHTMLView *view, KHTMLViewPrivate* vp) : QToolTip(view->viewport())
{
m_view = view;
m_viewprivate = vp;
};
protected:
virtual void maybeTip(const QPoint &);
private:
KHTMLView *m_view;
KHTMLViewPrivate* m_viewprivate;
};
#endif
class KHTMLViewPrivate {
friend class KHTMLToolTip;
public:
KHTMLViewPrivate()
: underMouse( 0 ), lastKeyNode(0)
{
#ifndef KHTML_NO_CARET
m_caretViewContext = 0;
m_editorContext = 0;
#endif // KHTML_NO_CARET
reset();
tp=0;
paintBuffer=0;
vertPaintBuffer=0;
formCompletions=0;
prevScrollbarVisible = true;
tooltip = 0;
possibleTripleClick = false;
}
~KHTMLViewPrivate()
{
delete formCompletions;
delete tp; tp = 0;
delete paintBuffer; paintBuffer =0;
delete vertPaintBuffer;
if (underMouse)
underMouse->deref();
delete tooltip;
#ifndef KHTML_NO_CARET
delete m_caretViewContext;
delete m_editorContext;
#endif // KHTML_NO_CARET
}
void reset()
{
if (underMouse)
underMouse->deref();
underMouse = 0;
if (lastKeyNode)
lastKeyNode->deref();
lastKeyNode = 0;
lastKeyPress = 0;
linkPressed = false;
useSlowRepaints = false;
originalNode = 0;
borderTouched = false;
#ifndef KHTML_NO_SCROLLBARS
vmode = QScrollView::Auto;
hmode = QScrollView::Auto;
#else
vmode = QScrollView::AlwaysOff;
hmode = QScrollView::AlwaysOff;
#endif
scrollBarMoved = false;
ignoreWheelEvents = false;
borderX = 30;
borderY = 30;
clickX = -1;
clickY = -1;
prevMouseX = -1;
prevMouseY = -1;
clickCount = 0;
isDoubleClick = false;
scrollingSelf = false;
layoutTimerId = 0;
repaintTimerId = 0;
scrollTimerId = 0;
complete = false;
firstRelayout = true;
dirtyLayout = false;
layoutSchedulingEnabled = true;
repaintLayout = false;
updateRect = QRect();
m_dialogsAllowed = true;
#ifndef KHTML_NO_CARET
if (m_caretViewContext) {
m_caretViewContext->caretMoved = false;
}/*end if*/
#endif // KHTML_NO_CARET
}
void newScrollTimer(QWidget *view, int tid)
{
kdDebug() << "newScrollTimer timer" << tid << endl;
view->killTimer(scrollTimerId);
scrollTimerId = tid;
}
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 ||
(scrollDirection != direction &&
scrollDirection != oppositedir)) {
scrollTiming = 6;
scrollBy = timings[scrollTiming].pixels;
scrollDirection = direction;
newScrollTimer(view, view->startTimer(timings[scrollTiming].msec));
} else if (scrollDirection == direction &&
timings[scrollTiming+1].msec) {
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));
}
}
}
#ifndef KHTML_NO_CARET
/** this function returns an instance of the caret view context. If none
* exists, it will be instantiated.
*/
CaretViewContext *caretViewContext() {
if (!m_caretViewContext) m_caretViewContext = new CaretViewContext();
return m_caretViewContext;
}
/** this function returns an instance of the editor context. If none
* exists, it will be instantiated.
*/
EditorContext *editorContext() {
if (!m_editorContext) m_editorContext = new EditorContext();
return m_editorContext;
}
#endif // KHTML_NO_CARET
QPainter *tp;
QPixmap *paintBuffer;
QPixmap *vertPaintBuffer;
NodeImpl *underMouse;
// the node that was selected when enter was pressed
NodeImpl *originalNode;
bool borderTouched:1;
bool borderStart:1;
bool scrollBarMoved:1;
QScrollView::ScrollBarMode vmode;
QScrollView::ScrollBarMode hmode;
bool prevScrollbarVisible;
bool linkPressed;
bool useSlowRepaints;
bool ignoreWheelEvents;
int borderX, borderY;
KSimpleConfig *formCompletions;
int clickX, clickY, clickCount;
bool isDoubleClick;
int prevMouseX, prevMouseY;
bool scrollingSelf;
int layoutTimerId;
NodeImpl *lastKeyNode;
int lastKeyPress;
int repaintTimerId;
int scrollTimerId;
int scrollTiming;
int scrollBy;
ScrollDirection scrollDirection;
bool complete;
bool firstRelayout;
bool layoutSchedulingEnabled;
bool possibleTripleClick;
bool dirtyLayout;
bool repaintLayout;
bool m_dialogsAllowed;
QRect updateRect;
KHTMLToolTip *tooltip;
QPtrDict visibleWidgets;
#ifndef KHTML_NO_CARET
CaretViewContext *m_caretViewContext;
EditorContext *m_editorContext;
#endif // KHTML_NO_CARET
};
#ifndef QT_NO_TOOLTIP
void KHTMLToolTip::maybeTip(const QPoint& /*p*/)
{
DOM::NodeImpl *node = m_viewprivate->underMouse;
QRect region;
while ( node ) {
if ( node->isElementNode() ) {
QString s = static_cast( node )->getAttribute( ATTR_TITLE ).string();
region |= QRect( m_view->contentsToViewport( node->getRect().topLeft() ), node->getRect().size() );
if ( !s.isEmpty() ) {
tip( region, QStyleSheet::convertFromPlainText( s, QStyleSheetItem::WhiteSpaceNormal ) );
break;
}
}
node = node->parentNode();
}
}
#endif
KHTMLView::KHTMLView( KHTMLPart *part, QWidget *parent, const char *name)
: QScrollView( parent, name, WResizeNoErase | WRepaintNoErase )
{
m_medium = "screen";
m_part = part;
d = new KHTMLViewPrivate;
QScrollView::setVScrollBarMode(d->vmode);
QScrollView::setHScrollBarMode(d->hmode);
connect(kapp, SIGNAL(kdisplayPaletteChanged()), this, SLOT(slotPaletteChanged()));
connect(this, SIGNAL(contentsMoving(int, int)), this, SLOT(slotScrollBarMoved()));
// initialize QScrollView
enableClipper(true);
// hack to get unclipped painting on the viewport.
static_cast(static_cast(viewport()))->setWFlags(WPaintUnclipped);
setResizePolicy(Manual);
viewport()->setMouseTracking(true);
viewport()->setBackgroundMode(NoBackground);
KImageIO::registerFormats();
#ifndef QT_NO_TOOLTIP
d->tooltip = new KHTMLToolTip( this, d );
#endif
init();
viewport()->show();
}
KHTMLView::~KHTMLView()
{
closeChildDialogs();
if (m_part)
{
//WABA: Is this Ok? Do I need to deref it as well?
//Does this need to be done somewhere else?
DOM::DocumentImpl *doc = m_part->xmlDocImpl();
if (doc)
doc->detach();
}
delete d; d = 0;
}
void KHTMLView::init()
{
if(!d->paintBuffer) d->paintBuffer = new QPixmap(PAINT_BUFFER_HEIGHT, PAINT_BUFFER_HEIGHT);
if(!d->vertPaintBuffer)
d->vertPaintBuffer = new QPixmap(10, PAINT_BUFFER_HEIGHT);
if(!d->tp) d->tp = new QPainter();
setFocusPolicy(QWidget::StrongFocus);
viewport()->setFocusPolicy( QWidget::WheelFocus );
viewport()->setFocusProxy(this);
_marginWidth = -1; // undefined
_marginHeight = -1;
_width = 0;
_height = 0;
installEventFilter(this);
setAcceptDrops(true);
QSize s = viewportSize(4095, 4095);
resizeContents(s.width(), s.height());
}
void KHTMLView::clear()
{
// work around QScrollview's unbelievable bugginess
setStaticBackground(true);
#ifndef KHTML_NO_CARET
if (!m_part->isCaretMode() && !m_part->isEditable()) caretOff();
#endif
d->reset();
killTimers();
emit cleared();
QScrollView::setHScrollBarMode(d->hmode);
QScrollView::setVScrollBarMode(d->vmode);
}
void KHTMLView::hideEvent(QHideEvent* e)
{
QScrollView::hideEvent(e);
}
void KHTMLView::showEvent(QShowEvent* e)
{
QScrollView::showEvent(e);
}
void KHTMLView::resizeEvent (QResizeEvent* e)
{
QScrollView::resizeEvent(e);
if ( m_part && m_part->xmlDocImpl() )
m_part->xmlDocImpl()->dispatchWindowEvent( EventImpl::RESIZE_EVENT, false, false );
}
void KHTMLView::viewportResizeEvent (QResizeEvent* e)
{
QScrollView::viewportResizeEvent(e);
//int w = visibleWidth();
//int h = visibleHeight();
if (d->layoutSchedulingEnabled)
layout();
#ifndef KHTML_NO_CARET
else {
hideCaret();
recalcAndStoreCaretPos();
showCaret();
}/*end if*/
#endif
KApplication::sendPostedEvents(viewport(), QEvent::Paint);
}
// this is to get rid of a compiler virtual overload mismatch warning. do not remove
void KHTMLView::drawContents( QPainter*)
{
}
void KHTMLView::drawContents( QPainter *p, int ex, int ey, int ew, int eh )
{
// kdDebug( 6000 ) << "drawContents this="<< this <<" x=" << ex << ",y=" << ey << ",w=" << ew << ",h=" << eh << endl;
if(!m_part || !m_part->xmlDocImpl() || !m_part->xmlDocImpl()->renderer()) {
p->fillRect(ex, ey, ew, eh, palette().active().brush(QColorGroup::Base));
return;
}
// QRect dbg_paint_rect(ex,ey,ew,eh);
QPoint pt = contentsToViewport(QPoint(ex, ey));
QRegion cr = QRect(pt.x(), pt.y(), ew, eh);
// kdDebug(6000) << "clip rect: " << QRect(pt.x(), pt.y(), ew, eh) << endl;
for (QPtrDictIterator it(d->visibleWidgets); it.current(); ++it) {
QWidget *w = it.current();
RenderWidget* rw = static_cast( it.currentKey() );
QScrollView *sv = ::qt_cast(w);
if (sv || !rw->isFormElement()) {
// kdDebug(6000) << " removing scrollview " << sv;
cr -= w->geometry();
}
}
if (cr.isEmpty())
return;
#ifndef DEBUG_NO_PAINT_BUFFER
p->setClipRegion(cr);
if (eh > PAINT_BUFFER_HEIGHT && ew <= 10) {
if ( d->vertPaintBuffer->height() < visibleHeight() )
d->vertPaintBuffer->resize(10, visibleHeight());
d->tp->begin(d->vertPaintBuffer);
d->tp->translate(-ex, -ey);
d->tp->fillRect(ex, ey, ew, eh, palette().active().brush(QColorGroup::Base));
m_part->xmlDocImpl()->renderer()->layer()->paint(d->tp, QRect(ex, ey, ew, eh));
d->tp->end();
p->drawPixmap(ex, ey, *d->vertPaintBuffer, 0, 0, ew, eh);
}
else {
if ( d->paintBuffer->width() < visibleWidth() )
d->paintBuffer->resize(visibleWidth(),PAINT_BUFFER_HEIGHT);
int py=0;
while (py < eh) {
int ph = eh-py < PAINT_BUFFER_HEIGHT ? eh-py : PAINT_BUFFER_HEIGHT;
d->tp->begin(d->paintBuffer);
d->tp->translate(-ex, -ey-py);
d->tp->fillRect(ex, ey+py, ew, ph, palette().active().brush(QColorGroup::Base));
m_part->xmlDocImpl()->renderer()->layer()->paint(d->tp, QRect(ex, ey+py, ew, ph));
#ifdef BOX_DEBUG
if (m_part->xmlDocImpl()->focusNode())
{
d->tp->setBrush(Qt::NoBrush);
d->tp->drawRect(m_part->xmlDocImpl()->focusNode()->getRect());
}
#endif
d->tp->end();
p->drawPixmap(ex, ey+py, *d->paintBuffer, 0, 0, ew, ph);
py += PAINT_BUFFER_HEIGHT;
}
}
#else // !DEBUG_NO_PAINT_BUFFER
static int cnt=0;
ex = contentsX(); ey = contentsY();
ew = visibleWidth(); eh = visibleHeight();
kdDebug() << "[" << ++cnt << "]" << " clip region: " << QRect(ex,ey,ew,eh) << endl;
// p->setClipRegion(QRect(0,0,ew,eh));
// p->translate(-ex, -ey);
p->fillRect(ex, ey, ew, eh, palette().active().brush(QColorGroup::Base));
m_part->xmlDocImpl()->renderer()->layer()->paint(p, ex, ey, ew, eh, 0, 0);
#endif // DEBUG_NO_PAINT_BUFFER
#ifndef KHTML_NO_CARET
if (d->m_caretViewContext && d->m_caretViewContext->visible) {
QRect pos(d->m_caretViewContext->x, d->m_caretViewContext->y,
d->m_caretViewContext->width, d->m_caretViewContext->height);
if (pos.intersects(QRect(ex, ey, ew, eh))) {
p->setRasterOp(XorROP);
p->setPen(white);
if (pos.width() == 1)
p->drawLine(pos.topLeft(), pos.bottomRight());
else {
p->fillRect(pos, white);
}/*end if*/
}/*end if*/
}/*end if*/
#endif // KHTML_NO_CARET
// p->setPen(QPen(magenta,0,DashDotDotLine));
// p->drawRect(dbg_paint_rect);
khtml::DrawContentsEvent event( p, ex, ey, ew, eh );
QApplication::sendEvent( m_part, &event );
}
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()
{
d->layoutSchedulingEnabled=false;
if( m_part && m_part->xmlDocImpl() ) {
DOM::DocumentImpl *document = m_part->xmlDocImpl();
khtml::RenderCanvas* root = static_cast(document->renderer());
if ( !root ) return;
if (document->isHTMLDocument()) {
NodeImpl *body = static_cast(document)->body();
if(body && body->renderer() && body->id() == ID_FRAMESET) {
QScrollView::setVScrollBarMode(AlwaysOff);
QScrollView::setHScrollBarMode(AlwaysOff);
body->renderer()->setLayouted(false);
// if (d->tooltip) {
// delete d->tooltip;
// d->tooltip = 0;
// }
}
else if (!d->tooltip)
d->tooltip = new KHTMLToolTip( this, d );
}
_height = visibleHeight();
_width = visibleWidth();
//QTime qt;
//qt.start();
root->setMinMaxKnown(false);
root->setLayouted(false);
root->layout();
#ifndef KHTML_NO_CARET
hideCaret();
if ((m_part->isCaretMode() || m_part->isEditable())
&& !d->complete && d->m_caretViewContext
&& !d->m_caretViewContext->caretMoved) {
initCaret();
} else {
recalcAndStoreCaretPos();
showCaret();
}/*end if*/
#endif
if( d->repaintLayout )
root->repaint();
//kdDebug( 6000 ) << "TIME: layout() dt=" << qt.elapsed() << endl;
}
else
_width = visibleWidth();
killTimer(d->layoutTimerId);
d->layoutTimerId = 0;
d->layoutSchedulingEnabled=true;
d->repaintLayout = false;
}
void KHTMLView::closeChildDialogs()
{
QObjectList *dlgs = queryList("QDialog");
for (QObject *dlg = dlgs->first(); dlg; dlg = dlgs->next())
{
KDialogBase* dlgbase = dynamic_cast( dlg );
if ( dlgbase ) {
kdDebug(6000) << "closeChildDialogs: closing dialog " << dlgbase << endl;
// close() ends up calling QButton::animateClick, which isn't immediate
// we need something the exits the event loop immediately (#49068)
dlgbase->cancel();
}
else
{
kdWarning() << "closeChildDialogs: not a KDialogBase! Don't use QDialogs in KDE! " << static_cast(dlg) << endl;
static_cast(dlg)->hide();
}
}
delete dlgs;
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();
QScrollView::closeEvent( ev );
}
//
// Event Handling
//
/////////////////
void KHTMLView::viewportMousePressEvent( QMouseEvent *_mouse )
{
if(!m_part->xmlDocImpl()) return;
if (d->possibleTripleClick)
{
viewportMouseDoubleClickEvent( _mouse ); // it handles triple clicks too
return;
}
int xm, ym;
viewportToContents(_mouse->x(), _mouse->y(), xm, ym);
//kdDebug( 6000 ) << "\nmousePressEvent: x=" << xm << ", y=" << ym << endl;
d->isDoubleClick = false;
DOM::NodeImpl::MouseEvent mev( _mouse->stateAfter(), DOM::NodeImpl::MousePress );
m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev );
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(),true,
d->clickCount,_mouse,true,DOM::NodeImpl::MousePress);
if (mev.innerNode.handle())
mev.innerNode.handle()->setPressed();
khtml::RenderObject* r = mev.innerNode.handle() ? mev.innerNode.handle()->renderer() : 0;
if (r && r->isWidget())
_mouse->ignore();
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::viewportMouseDoubleClickEvent( QMouseEvent *_mouse )
{
if(!m_part->xmlDocImpl()) return;
int xm, ym;
viewportToContents(_mouse->x(), _mouse->y(), xm, ym);
kdDebug( 6000 ) << "mouseDblClickEvent: x=" << xm << ", y=" << ym << endl;
d->isDoubleClick = true;
DOM::NodeImpl::MouseEvent mev( _mouse->stateAfter(), DOM::NodeImpl::MouseDblClick );
m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev );
// We do the same thing as viewportMousePressEvent() here, since the DOM does not treat
// single and double-click events as separate (only the detail, i.e. number of clicks differs)
if (d->clickCount > 0 && d->clickX == xm && d->clickY == ym) // ### support mouse threshold
d->clickCount++;
else {
d->clickCount = 1;
d->clickX = xm;
d->clickY = ym;
}
kdDebug() << "KHTMLView::viewportMouseDoubleClickEvent clickCount=" << d->clickCount << endl;
bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEDOWN_EVENT,mev.innerNode.handle(),true,
d->clickCount,_mouse,true,DOM::NodeImpl::MouseDblClick);
if (mev.innerNode.handle())
mev.innerNode.handle()->setPressed();
khtml::RenderObject* r = mev.innerNode.handle() ? mev.innerNode.handle()->renderer() : 0;
if (r && r->isWidget())
_mouse->ignore();
if (!swallowEvent) {
khtml::MouseDoubleClickEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode, d->clickCount );
QApplication::sendEvent( m_part, &event );
}
d->possibleTripleClick=true;
QTimer::singleShot(QApplication::doubleClickInterval(),this,SLOT(tripleClickTimeout()));
}
void KHTMLView::tripleClickTimeout()
{
d->possibleTripleClick = false;
d->clickCount = 0;
}
static inline void forwardPeripheralEvent(khtml::RenderWidget* r, QMouseEvent* me, int x, int y)
{
int absx = 0;
int absy = 0;
r->absolutePosition(absx, absy);
QPoint p(x-absx, y-absy);
QMouseEvent fw(me->type(), p, me->button(), me->state());
QWidget* w = r->widget();
if(w)
static_cast(w)->sendEvent(&fw);
}
void KHTMLView::viewportMouseMoveEvent( QMouseEvent * _mouse )
{
if(!m_part->xmlDocImpl()) return;
int xm, ym;
viewportToContents(_mouse->x(), _mouse->y(), xm, ym);
DOM::NodeImpl::MouseEvent mev( _mouse->stateAfter(), DOM::NodeImpl::MouseMove );
m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev );
// kdDebug(6000) << "mouse move: " << _mouse->pos()
// << " button " << _mouse->button()
// << " state " << _mouse->state() << endl;
bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEMOVE_EVENT,mev.innerNode.handle(),false,
0,_mouse,true,DOM::NodeImpl::MouseMove);
if (d->clickCount > 0 &&
QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() > QApplication::startDragDistance()) {
d->clickCount = 0; // moving the mouse outside the threshold invalidates the click
}
// execute the scheduled script. This is to make sure the mouseover events come after the mouseout events
m_part->executeScheduledScript();
DOM::NodeImpl* fn = m_part->xmlDocImpl()->focusNode();
if (fn && fn != mev.innerNode.handle() &&
fn->renderer() && fn->renderer()->isWidget()) {
forwardPeripheralEvent(static_cast(fn->renderer()), _mouse, xm, ym);
}
khtml::RenderObject* r = mev.innerNode.handle() ? mev.innerNode.handle()->renderer() : 0;
khtml::RenderStyle* style = (r && r->style()) ? r->style() : 0;
QCursor c;
switch ( style ? style->cursor() : CURSOR_AUTO) {
case CURSOR_AUTO:
if ( mev.url.length() && m_part->settings()->changeCursor() )
c = m_part->urlCursor();
if (r && r->isFrameSet() && !static_cast(r)->noResize())
c = QCursor(static_cast(r)->cursorShape());
break;
case CURSOR_CROSS:
c = KCursor::crossCursor();
break;
case CURSOR_POINTER:
c = m_part->urlCursor();
break;
case CURSOR_PROGRESS:
c = KCursor::workingCursor();
break;
case CURSOR_MOVE:
c = KCursor::sizeAllCursor();
break;
case CURSOR_E_RESIZE:
case CURSOR_W_RESIZE:
c = KCursor::sizeHorCursor();
break;
case CURSOR_N_RESIZE:
case CURSOR_S_RESIZE:
c = KCursor::sizeVerCursor();
break;
case CURSOR_NE_RESIZE:
case CURSOR_SW_RESIZE:
c = KCursor::sizeBDiagCursor();
break;
case CURSOR_NW_RESIZE:
case CURSOR_SE_RESIZE:
c = KCursor::sizeFDiagCursor();
break;
case CURSOR_TEXT:
c = KCursor::ibeamCursor();
break;
case CURSOR_WAIT:
c = KCursor::waitCursor();
break;
case CURSOR_HELP:
c = KCursor::whatsThisCursor();
break;
case CURSOR_DEFAULT:
break;
}
if ( viewport()->cursor().handle() != c.handle() ) {
if( c.handle() == KCursor::arrowCursor().handle()) {
for (KHTMLPart* p = m_part; p; p = p->parentPart())
p->view()->viewport()->unsetCursor();
}
else {
viewport()->setCursor( c );
}
}
if (r && r->isWidget()) {
_mouse->ignore();
}
d->prevMouseX = xm;
d->prevMouseY = ym;
if (!swallowEvent) {
khtml::MouseMoveEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode );
QApplication::sendEvent( m_part, &event );
}
}
void KHTMLView::viewportMouseReleaseEvent( QMouseEvent * _mouse )
{
if ( !m_part->xmlDocImpl() ) return;
int xm, ym;
viewportToContents(_mouse->x(), _mouse->y(), xm, ym);
DOM::NodeImpl::MouseEvent mev( _mouse->stateAfter(), DOM::NodeImpl::MouseRelease );
m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev );
bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEUP_EVENT,mev.innerNode.handle(),true,
d->clickCount,_mouse,false,DOM::NodeImpl::MouseRelease);
if (d->clickCount > 0 &&
QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() <= QApplication::startDragDistance())
dispatchMouseEvent(EventImpl::CLICK_EVENT,mev.innerNode.handle(),true,
d->clickCount,_mouse,true,DOM::NodeImpl::MouseRelease);
if (mev.innerNode.handle())
mev.innerNode.handle()->setPressed(false);
DOM::NodeImpl* fn = m_part->xmlDocImpl()->focusNode();
if (fn && fn != mev.innerNode.handle() &&
fn->renderer() && fn->renderer()->isWidget()) {
forwardPeripheralEvent(static_cast(fn->renderer()), _mouse, xm, ym);
}
khtml::RenderObject* r = mev.innerNode.handle() ? mev.innerNode.handle()->renderer() : 0;
if (r && r->isWidget())
_mouse->ignore();
if (!swallowEvent) {
khtml::MouseReleaseEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode );
QApplication::sendEvent( m_part, &event );
}
}
void KHTMLView::keyPressEvent( QKeyEvent *_ke )
{
#ifndef KHTML_NO_CARET
if (m_part->isEditable() || m_part->isCaretMode()
|| (m_part->xmlDocImpl() && m_part->xmlDocImpl()->focusNode()
&& m_part->xmlDocImpl()->focusNode()->contentEditable())) {
caretKeyPressEvent(_ke);
return;
}
#endif // KHTML_NO_CARET
if (d->lastKeyNode)
d->lastKeyNode->deref();
d->lastKeyNode = 0;
d->lastKeyPress = _ke->key();
if (m_part->xmlDocImpl()) {
d->lastKeyNode = m_part->xmlDocImpl()->focusNode();
if (d->lastKeyNode) {
d->lastKeyNode->ref();
if (d->lastKeyNode->dispatchKeyEvent(_ke)) {
_ke->accept();
return;
}
}
if (!_ke->text().isNull() && m_part->xmlDocImpl()->getHTMLEventListener(EventImpl::KHTML_KEYDOWN_EVENT)) {
// If listener returned false, stop here. Otherwise handle standard keys (#60403).
if (!m_part->xmlDocImpl()->documentElement()->dispatchKeyEvent(_ke)) {
_ke->accept();
return;
}
}
}
int offs = (clipper()->height() < 30) ? clipper()->height() : 30;
if (_ke->state() & Qt::ShiftButton)
switch(_ke->key())
{
case Key_Space:
if ( d->vmode == QScrollView::AlwaysOff )
_ke->accept();
else
scrollBy( 0, -clipper()->height() - offs );
break;
case Key_Down:
case Key_J:
d->adjustScroller(this, KHTMLViewPrivate::ScrollDown, KHTMLViewPrivate::ScrollUp);
break;
case Key_Up:
case Key_K:
d->adjustScroller(this, KHTMLViewPrivate::ScrollUp, KHTMLViewPrivate::ScrollDown);
break;
case Key_Left:
case Key_H:
d->adjustScroller(this, KHTMLViewPrivate::ScrollLeft, KHTMLViewPrivate::ScrollRight);
break;
case Key_Right:
case Key_L:
d->adjustScroller(this, KHTMLViewPrivate::ScrollRight, KHTMLViewPrivate::ScrollLeft);
break;
}
else
switch ( _ke->key() )
{
case Key_Down:
case Key_J:
if ( d->vmode == QScrollView::AlwaysOff )
_ke->accept();
else {
if (d->scrollTimerId)
d->newScrollTimer(this, 0);
else
scrollBy( 0, 10 );
}
break;
case Key_Space:
case Key_Next:
if ( d->vmode == QScrollView::AlwaysOff )
_ke->accept();
else
scrollBy( 0, clipper()->height() - offs );
break;
case Key_Up:
case Key_K:
if ( d->vmode == QScrollView::AlwaysOff )
_ke->accept();
else {
if (d->scrollTimerId)
d->newScrollTimer(this, 0);
else
scrollBy( 0, -10 );
}
break;
case Key_Prior:
if ( d->vmode == QScrollView::AlwaysOff )
_ke->accept();
else
scrollBy( 0, -clipper()->height() + offs );
break;
case Key_Right:
case Key_L:
if ( d->hmode == QScrollView::AlwaysOff )
_ke->accept();
else {
if (d->scrollTimerId)
d->newScrollTimer(this, 0);
else
scrollBy( 10, 0 );
}
break;
case Key_Left:
case Key_H:
if ( d->hmode == QScrollView::AlwaysOff )
_ke->accept();
else {
if (d->scrollTimerId)
d->newScrollTimer(this, 0);
else
scrollBy( -10, 0 );
}
break;
case Key_Enter:
case Key_Return:
// ### FIXME:
// move this code to HTMLAnchorElementImpl::setPressed(false),
// or even better to HTMLAnchorElementImpl::event()
if (m_part->xmlDocImpl()) {
NodeImpl *n = m_part->xmlDocImpl()->focusNode();
if (n)
n->setActive();
d->originalNode = n;
}
break;
case Key_Home:
if ( d->vmode == QScrollView::AlwaysOff )
_ke->accept();
else
setContentsPos( 0, 0 );
break;
case Key_End:
if ( d->vmode == QScrollView::AlwaysOff )
_ke->accept();
else
setContentsPos( 0, contentsHeight() - visibleHeight() );
break;
case 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(m_part->xmlDocImpl()) {
NodeImpl *fn = m_part->xmlDocImpl()->focusNode();
if (fn && fn->dispatchKeyEvent(_ke)) {
_ke->accept();
}
if (fn && d->lastKeyNode == fn && d->lastKeyPress == _ke->key()
&& !_ke->isAutoRepeat()) {
d->lastKeyNode->deref();
d->lastKeyNode = 0;
d->lastKeyPress = 0;
QKeyEvent k(_ke->type(), _ke->key(), _ke->ascii(), _ke->state(),
_ke->text(), true, _ke->count());
fn->dispatchKeyEvent(&k);
}
}
if (!_ke->isAccepted())
QScrollView::keyReleaseEvent(_ke);
}
void KHTMLView::contentsContextMenuEvent ( QContextMenuEvent * /*ce*/ )
{
// ### what kind of c*** is that ?
#if 0
if (!m_part->xmlDocImpl()) return;
int xm = _ce->x();
int ym = _ce->y();
DOM::NodeImpl::MouseEvent mev( _ce->state(), DOM::NodeImpl::MouseMove ); // ### not a mouse event!
m_part->xmlDocImpl()->prepareMouseEvent( xm, ym, &mev );
NodeImpl *targetNode = mev.innerNode.handle();
if (targetNode && targetNode->renderer() && targetNode->renderer()->isWidget()) {
int absx = 0;
int absy = 0;
targetNode->renderer()->absolutePosition(absx,absy);
QPoint pos(xm-absx,ym-absy);
QWidget *w = static_cast(targetNode->renderer())->widget();
QContextMenuEvent cme(_ce->reason(),pos,_ce->globalPos(),_ce->state());
setIgnoreEvents(true);
QApplication::sendEvent(w,&cme);
setIgnoreEvents(false);
}
#endif
}
bool KHTMLView::focusNextPrevChild( bool next )
{
// Now try to find the next child
if (m_part->xmlDocImpl()) {
focusNextPrevNode(next);
if (m_part->xmlDocImpl()->focusNode() != 0) {
kdDebug() << "focusNode.name: "
<< m_part->xmlDocImpl()->focusNode()->nodeName().string() << endl;
return true; // focus node found
}
}
// If we get here, there is no next/previous child to go to, so pass up to the next/previous child in our parent
if (m_part->parentPart() && m_part->parentPart()->view())
return m_part->parentPart()->view()->focusNextPrevChild(next);
return QWidget::focusNextPrevChild(next);
}
void KHTMLView::doAutoScroll()
{
QPoint pos = QCursor::pos();
pos = viewport()->mapFromGlobal( pos );
int xm, ym;
viewportToContents(pos.x(), pos.y(), xm, ym);
pos = QPoint(pos.x() - viewport()->x(), pos.y() - viewport()->y());
if ( (pos.y() < 0) || (pos.y() > visibleHeight()) ||
(pos.x() < 0) || (pos.x() > visibleWidth()) )
{
ensureVisible( xm, ym, 0, 5 );
#ifndef KHTML_NO_SELECTION
// extend the selection while scrolling
DOM::Node innerNode;
if (m_part->isExtendingSelection()) {
RenderObject::NodeInfo renderInfo(true/*readonly*/, false/*active*/);
m_part->xmlDocImpl()->renderer()->layer()
->nodeAtPoint(renderInfo, xm, ym);
innerNode = renderInfo.innerNode();
}/*end if*/
if (innerNode.handle() && innerNode.handle()->renderer()) {
int absX, absY;
innerNode.handle()->renderer()->absolutePosition(absX, absY);
m_part->extendSelectionTo(xm, ym, absX, absY, innerNode);
}/*end if*/
#endif // KHTML_NO_SELECTION
}
}
bool KHTMLView::eventFilter(QObject *o, QEvent *e)
{
if ( e->type() == QEvent::AccelOverride ) {
QKeyEvent* ke = (QKeyEvent*) e;
//kdDebug(6200) << "QEvent::AccelAvailable" << endl;
if (m_part->isEditable() || m_part->isCaretMode()
|| (m_part->xmlDocImpl() && m_part->xmlDocImpl()->focusNode()
&& m_part->xmlDocImpl()->focusNode()->contentEditable())) {
//kdDebug(6200) << "editable/navigable" << endl;
if ( (ke->state() & ControlButton) || (ke->state() & ShiftButton) ) {
switch ( ke->key() ) {
case Key_Left:
case Key_Right:
case Key_Up:
case Key_Down:
case Key_Home:
case Key_End:
ke->accept();
//kdDebug(6200) << "eaten" << endl;
return true;
default:
break;
}/*end switch*/
}/*end if*/
}/*end if*/
}/*end if*/
QWidget *view = viewport();
if (o == view) {
// we need to install an event filter on all children of the viewport to
// be able to get correct stacking of children within the document.
if(e->type() == QEvent::ChildInserted) {
QObject *c = static_cast(e)->child();
if (c->isWidgetType()) {
QWidget *w = static_cast(c);
// don't install the event filter on toplevels
if (w->parentWidget(true) == view) {
if (!::qt_cast(w)) {
w->installEventFilter(this);
w->unsetCursor();
}
}
}
}
} else if (o->isWidgetType()) {
QWidget *v = static_cast(o);
while (v && v != view) {
v = v->parentWidget(true);
}
if (v) {
bool block = false;
QWidget *w = static_cast(o);
switch(e->type()) {
case QEvent::Paint:
if (!allowWidgetPaintEvents) {
// eat the event. Like this we can control exactly when the widget
// get's repainted.
block = true;
int x, y;
viewportToContents( w->x(), w->y(), x, y );
QPaintEvent *pe = static_cast(e);
scheduleRepaint(x + pe->rect().x(), y + pe->rect().y(),
pe->rect().width(), pe->rect().height());
}
break;
case QEvent::Wheel:
// don't allow the widget to react to wheel event unless its
// currently focused. this avoids accidentally changing a select box
// or something while wheeling a webpage.
if (qApp->focusWidget() != w &&
w->focusPolicy() <= QWidget::StrongFocus) {
static_cast(e)->ignore();
QApplication::sendEvent(this, e);
block = true;
}
break;
case QEvent::MouseMove:
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::MouseButtonDblClick: {
if (!::qt_cast(w)) {
QMouseEvent *me = static_cast(e);
QPoint pt = (me->pos() + w->pos());
QMouseEvent me2(me->type(), pt, me->button(), me->state());
if (e->type() == QEvent::MouseMove)
viewportMouseMoveEvent(&me2);
else if(e->type() == QEvent::MouseButtonPress)
viewportMousePressEvent(&me2);
else if(e->type() == QEvent::MouseButtonRelease)
viewportMouseReleaseEvent(&me2);
else
viewportMouseDoubleClickEvent(&me2);
block = true;
}
break;
}
case QEvent::KeyPress:
case QEvent::KeyRelease:
if (!::qt_cast(w)) {
QKeyEvent *ke = static_cast(e);
if (e->type() == QEvent::KeyPress)
keyPressEvent(ke);
else
keyReleaseEvent(ke);
block = true;
}
default:
break;
}
if (block) {
//qDebug("eating event");
return true;
}
}
}
// kdDebug(6000) <<"passing event on to sv event filter " << o << endl;
return QScrollView::eventFilter(o, e);
}
DOM::NodeImpl *KHTMLView::nodeUnderMouse() const
{
return d->underMouse;
}
bool KHTMLView::scrollTo(const QRect &bounds)
{
d->scrollingSelf = true; // so scroll events get ignored
int x, y, xe, ye;
x = bounds.left();
y = bounds.top();
xe = bounds.right();
ye = bounds.bottom();
//kdDebug(6000)<<"scrolling coords: x="<getRect();
if ((focusNodeRect.left() > contentsX()) && (focusNodeRect.right() < contentsX() + visibleWidth()) &&
(focusNodeRect.top() > contentsY()) && (focusNodeRect.bottom() < contentsY() + visibleHeight())) {
// toFocus is visible in the contents area
visible = true;
}
else {
// toFocus is _not_ visible in the contents area, pick the next node
if (next)
toFocus = doc->nextFocusNode(toFocus);
else
toFocus = doc->previousFocusNode(toFocus);
}
}
if (toFocus)
newFocusNode = toFocus;
}
d->scrollBarMoved = false;
if (!newFocusNode)
{
// No new focus node, scroll to bottom or top depending on next
if (next)
scrollTo(QRect(contentsX()+visibleWidth()/2,contentsHeight(),0,0));
else
scrollTo(QRect(contentsX()+visibleWidth()/2,0,0,0));
}
else
// Scroll the view as necessary to ensure that the new focus node is visible
{
#ifndef KHTML_NO_CARET
// if it's an editable element, activate the caret
if (!m_part->isCaretMode() && !m_part->isEditable()
&& newFocusNode->contentEditable()) {
d->caretViewContext();
moveCaretTo(newFocusNode, 0L, true);
} else {
caretOff();
}
#endif // KHTML_NO_CARET
if (oldFocusNode)
{
if (!scrollTo(newFocusNode->getRect()))
return;
}
else
{
ensureVisible(contentsX(), next?0:contentsHeight());
//return;
}
}
// Set focus node on the document
m_part->xmlDocImpl()->setFocusNode(newFocusNode);
emit m_part->nodeActivated(Node(newFocusNode));
}
void KHTMLView::setMediaType( const QString &medium )
{
m_medium = medium;
}
QString KHTMLView::mediaType() const
{
return m_medium;
}
void KHTMLView::setWidgetVisible(RenderWidget* w, bool vis)
{
if (vis) {
d->visibleWidgets.replace(w, w->widget());
}
else
d->visibleWidgets.remove(w);
}
void KHTMLView::print()
{
print( false );
}
void KHTMLView::print(bool quick)
{
if(!m_part->xmlDocImpl()) return;
khtml::RenderCanvas *root = static_cast(m_part->xmlDocImpl()->renderer());
if(!root) return;
// this only works on Unix - we assume 72dpi
KPrinter *printer = new KPrinter(true, QPrinter::PrinterResolution);
printer->addDialogPage(new KHTMLPrintSettings());
QString docname = m_part->xmlDocImpl()->URL();
if ( !docname.isEmpty() )
docname = KStringHandler::csqueeze(docname, 80);
if(quick || printer->setup(this, i18n("Print %1").arg(docname))) {
viewport()->setCursor( waitCursor ); // only viewport(), no QApplication::, otherwise we get the busy cursor in kdeprint's dialogs
// set up KPrinter
printer->setFullPage(false);
- printer->setCreator("KDE 3.0 HTML Library");
+ printer->setCreator(QString("KDE %1.%2.%3 HTML Library").arg(KDE_VERSION_MAJOR).arg(KDE_VERSION_MINOR).arg(KDE_VERSION_RELEASE));
printer->setDocName(docname);
QPainter *p = new QPainter;
p->begin( printer );
khtml::setPrintPainter( p );
m_part->xmlDocImpl()->setPaintDevice( printer );
QString oldMediaType = mediaType();
setMediaType( "print" );
// We ignore margin settings for html and body when printing
// and use the default margins from the print-system
// (In Qt 3.0.x the default margins are hardcoded in Qt)
m_part->xmlDocImpl()->setPrintStyleSheet( printer->option("app-khtml-printfriendly") == "true" ?
"* { background-image: none !important;"
" background-color: white !important;"
" color: black !important; }"
"body { margin: 0px !important; }"
"html { margin: 0px !important; }" :
"body { margin: 0px !important; }"
"html { margin: 0px !important; }"
);
QPaintDeviceMetrics metrics( printer );
// this is a simple approximation... we layout the document
// according to the width of the page, then just cut
// pages without caring about the content. We should do better
// in the future, but for the moment this is better than no
// printing support
kdDebug(6000) << "printing: physical page width = " << metrics.width()
<< " height = " << metrics.height() << endl;
root->setPrintingMode(true);
root->setWidth(metrics.width());
m_part->xmlDocImpl()->styleSelector()->computeFontSizes(&metrics, 100);
m_part->xmlDocImpl()->updateStyleSelector();
root->setPrintImages( printer->option("app-khtml-printimages") == "true");
root->setLayouted( false );
root->setMinMaxKnown( false );
root->layout();
bool printHeader = (printer->option("app-khtml-printheader") == "true");
int headerHeight = 0;
QFont headerFont("helvetica", 8);
QString headerLeft = KGlobal::locale()->formatDate(QDate::currentDate(),true);
QString headerMid = docname;
QString headerRight;
if (printHeader)
{
p->setFont(headerFont);
headerHeight = (p->fontMetrics().lineSpacing() * 3) / 2;
}
// ok. now print the pages.
kdDebug(6000) << "printing: html page width = " << root->docWidth()
<< " height = " << root->docHeight() << endl;
kdDebug(6000) << "printing: margins left = " << printer->margins().width()
<< " top = " << printer->margins().height() << endl;
kdDebug(6000) << "printing: paper width = " << metrics.width()
<< " height = " << metrics.height() << endl;
// if the width is too large to fit on the paper we just scale
// the whole thing.
int pageHeight = metrics.height();
int pageWidth = metrics.width();
p->setClipRect(0,0, pageWidth, pageHeight);
pageHeight -= headerHeight;
bool scalePage = false;
double scale = 0.0;
#ifndef QT_NO_TRANSFORMATIONS
if(root->docWidth() > metrics.width()) {
scalePage = true;
scale = ((double) metrics.width())/((double) root->docWidth());
pageHeight = (int) (pageHeight/scale);
pageWidth = (int) (pageWidth/scale);
headerHeight = (int) (headerHeight/scale);
}
#endif
kdDebug(6000) << "printing: scaled html width = " << pageWidth
<< " height = " << pageHeight << endl;
// Squeeze header to make it it on the page.
if (printHeader)
{
int available_width = metrics.width() - 10 -
2 * kMax(p->boundingRect(0, 0, metrics.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerLeft).width(),
p->boundingRect(0, 0, metrics.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, metrics.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerMid).width();
squeeze -= 10;
} while (mid_width > available_width);
}
int top = 0;
int page = 1;
while(top < root->docHeight()) {
if(top > 0) printer->newPage();
if (printHeader)
{
int dy = p->fontMetrics().lineSpacing();
p->setPen(Qt::black);
p->setFont(headerFont);
headerRight = QString("#%1").arg(page);
p->drawText(0, 0, metrics.width(), dy, Qt::AlignLeft, headerLeft);
p->drawText(0, 0, metrics.width(), dy, Qt::AlignHCenter, headerMid);
p->drawText(0, 0, metrics.width(), dy, Qt::AlignRight, headerRight);
}
#ifndef QT_NO_TRANSFORMATIONS
if (scalePage)
p->scale(scale, scale);
#endif
p->translate(0, headerHeight-top);
root->setTruncatedAt(top+pageHeight);
root->layer()->paint(p, QRect(0, top, pageWidth, pageHeight));
if (top + pageHeight >= root->docHeight())
break; // Stop if we have printed everything
top = root->truncatedAt();
p->resetXForm();
page++;
}
p->end();
delete p;
// and now reset the layout to the usual one...
root->setPrintingMode(false);
khtml::setPrintPainter( 0 );
setMediaType( oldMediaType );
m_part->xmlDocImpl()->setPaintDevice( this );
m_part->xmlDocImpl()->styleSelector()->computeFontSizes(m_part->xmlDocImpl()->paintDeviceMetrics(), m_part->zoomFactor());
m_part->xmlDocImpl()->updateStyleSelector();
viewport()->unsetCursor();
}
delete printer;
}
void KHTMLView::slotPaletteChanged()
{
if(!m_part->xmlDocImpl()) return;
DOM::DocumentImpl *document = m_part->xmlDocImpl();
if (!document->isHTMLDocument()) return;
khtml::RenderCanvas *root = static_cast(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;
m_part->xmlDocImpl()->setPaintDevice(p->device());
root->setPrintingMode(true);
root->setWidth(rc.width());
p->save();
p->setClipRect(rc);
p->translate(rc.left(), rc.top());
double scale = ((double) rc.width()/(double) root->docWidth());
int height = (int) ((double) rc.height() / scale);
#ifndef QT_NO_TRANSFORMATIONS
p->scale(scale, scale);
#endif
root->layer()->paint(p, QRect(0, yOff, root->docWidth(), height));
if (more)
*more = yOff + height < root->docHeight();
p->restore();
root->setPrintingMode(false);
m_part->xmlDocImpl()->setPaintDevice( this );
}
void KHTMLView::useSlowRepaints()
{
kdDebug(0) << "slow repaints requested" << endl;
d->useSlowRepaints = true;
setStaticBackground(true);
}
void KHTMLView::setVScrollBarMode ( ScrollBarMode mode )
{
#ifndef KHTML_NO_SCROLLBARS
d->vmode = mode;
QScrollView::setVScrollBarMode(mode);
#else
Q_UNUSED( mode );
#endif
}
void KHTMLView::setHScrollBarMode ( ScrollBarMode mode )
{
#ifndef KHTML_NO_SCROLLBARS
d->hmode = mode;
QScrollView::setHScrollBarMode(mode);
#else
Q_UNUSED( mode );
#endif
}
void KHTMLView::restoreScrollBar()
{
int ow = visibleWidth();
QScrollView::setVScrollBarMode(d->vmode);
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 KSimpleConfig(locateLocal("data", "khtml/formcompletions"));
return d->formCompletions->readListEntry(name);
}
void KHTMLView::clearCompletionHistory(const QString& name)
{
if (!d->formCompletions)
{
d->formCompletions = new KSimpleConfig(locateLocal("data", "khtml/formcompletions"));
}
d->formCompletions->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 (unsigned int i = 0; i < value.length(); ++i)
{
QChar c(value[i]);
if (!c.isNumber() && c != '-' && !c.isSpace())
{
cc_number = false;
break;
}
}
if (cc_number)
return;
QStringList items = formCompletionItems(name);
if (!items.contains(value))
items.prepend(value);
while ((int)items.count() > m_part->settings()->maxFormCompletionItems())
items.remove(items.fromLast());
d->formCompletions->writeEntry(name, items);
}
bool KHTMLView::dispatchMouseEvent(int eventId, DOM::NodeImpl *targetNode, bool cancelable,
int detail,QMouseEvent *_mouse, bool setUnder,
int mouseEventType)
{
if (d->underMouse)
d->underMouse->deref();
d->underMouse = targetNode;
if (d->underMouse)
d->underMouse->ref();
int exceptioncode = 0;
int mx, my;
viewportToContents(_mouse->x(), _mouse->y(), mx, my);
// clientX and clientY are in viewport coordinates
// At least the JS code wants event.[xy]/event.client[XY] to be in viewport coords.
// [that's not the same as _mouse->[xy](), since we use the clipper]
int clientX = mx - contentsX();
int clientY = my - contentsY();
int screenX = _mouse->globalX();
int screenY = _mouse->globalY();
int button = -1;
switch (_mouse->button()) {
case LeftButton:
button = 0;
break;
case MidButton:
button = 1;
break;
case RightButton:
button = 2;
break;
default:
break;
}
bool ctrlKey = (_mouse->state() & ControlButton);
bool altKey = (_mouse->state() & AltButton);
bool shiftKey = (_mouse->state() & ShiftButton);
bool metaKey = (_mouse->state() & MetaButton);
// mouseout/mouseover
if (setUnder && (d->prevMouseX != mx || d->prevMouseY != my)) {
// ### this code sucks. we should save the oldUnder instead of calculating
// it again. calculating is expensive! (Dirk)
NodeImpl *oldUnder = 0;
if (d->prevMouseX >= 0 && d->prevMouseY >= 0) {
NodeImpl::MouseEvent mev( _mouse->stateAfter(), static_cast(mouseEventType));
m_part->xmlDocImpl()->prepareMouseEvent( true, d->prevMouseX, d->prevMouseY, &mev );
oldUnder = mev.innerNode.handle();
}
// qDebug("oldunder=%p (%s), target=%p (%s) x/y=%d/%d", oldUnder, oldUnder ? oldUnder->renderer()->renderName() : 0, targetNode, targetNode ? targetNode->renderer()->renderName() : 0, _mouse->x(), _mouse->y());
if (oldUnder != targetNode) {
// send mouseout event to the old node
if (oldUnder){
oldUnder->ref();
MouseEventImpl *me = new MouseEventImpl(EventImpl::MOUSEOUT_EVENT,
true,true,m_part->xmlDocImpl()->defaultView(),
0,screenX,screenY,clientX,clientY,
ctrlKey,altKey,shiftKey,metaKey,
button,targetNode);
me->ref();
oldUnder->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,
ctrlKey,altKey,shiftKey,metaKey,
button,oldUnder);
me->ref();
targetNode->dispatchEvent(me,exceptioncode,true);
me->deref();
}
if (oldUnder)
oldUnder->deref();
}
}
bool swallowEvent = false;
if (targetNode) {
// send the actual event
MouseEventImpl *me = new MouseEventImpl(static_cast(eventId),
true,cancelable,m_part->xmlDocImpl()->defaultView(),
detail,screenX,screenY,clientX,clientY,
ctrlKey,altKey,shiftKey,metaKey,
button,0, _mouse);
me->ref();
targetNode->dispatchEvent(me,exceptioncode,true);
if (me->defaultHandled() || me->defaultPrevented())
swallowEvent = true;
me->deref();
if (eventId == EventImpl::MOUSEDOWN_EVENT) {
if (targetNode->isSelectable())
m_part->xmlDocImpl()->setFocusNode(targetNode);
else
m_part->xmlDocImpl()->setFocusNode(0);
}
}
return swallowEvent;
}
void KHTMLView::setIgnoreWheelEvents( bool e )
{
d->ignoreWheelEvents = e;
}
#ifndef QT_NO_WHEELEVENT
void KHTMLView::viewportWheelEvent(QWheelEvent* e)
{
if ( ( e->state() & ShiftButton ) == ShiftButton )
{
emit zoomView( e->delta() );
e->accept();
}
else if ( ( (d->ignoreWheelEvents && !verticalScrollBar()->isVisible())
|| e->delta() > 0 && contentsY() <= 0
|| e->delta() < 0 && contentsY() >= contentsHeight() - visibleHeight())
&& m_part->parentPart() ) {
kdDebug(6000) << this << " cz " << contentsY() << " ch " << contentsHeight() << " vh " << visibleHeight() << endl;
if ( m_part->parentPart()->view() )
m_part->parentPart()->view()->wheelEvent( e );
kdDebug(6000) << "sent" << endl;
e->ignore();
}
else if ( d->vmode == QScrollView::AlwaysOff ) {
e->accept();
}
else {
d->scrollBarMoved = true;
QScrollView::viewportWheelEvent( e );
QMouseEvent *tempEvent = new QMouseEvent( QEvent::MouseMove, QPoint(-1,-1), QPoint(-1,-1), Qt::NoButton, e->state() );
emit viewportMouseMoveEvent ( tempEvent );
delete tempEvent;
}
}
#endif
void KHTMLView::dragEnterEvent( QDragEnterEvent* ev )
{
// Handle drops onto frames (#16820)
// Drops on the main html part is handled by Konqueror (and shouldn't do anything
// in e.g. kmail, so not handled here).
if ( m_part->parentPart() )
{
QApplication::sendEvent(m_part->parentPart()->widget(), ev);
return;
}
QScrollView::dragEnterEvent( ev );
}
void KHTMLView::dropEvent( QDropEvent *ev )
{
// Handle drops onto frames (#16820)
// Drops on the main html part is handled by Konqueror (and shouldn't do anything
// in e.g. kmail, so not handled here).
if ( m_part->parentPart() )
{
QApplication::sendEvent(m_part->parentPart()->widget(), ev);
return;
}
QScrollView::dropEvent( ev );
}
void KHTMLView::focusInEvent( QFocusEvent *e )
{
#ifndef KHTML_NO_CARET
// Restart blink frequency timer if it has been killed, but only on
// editable nodes
if (d->m_caretViewContext &&
d->m_caretViewContext->freqTimerId == -1 &&
m_part->xmlDocImpl()) {
NodeImpl *caretNode = m_part->xmlDocImpl()->focusNode();
if (m_part->isCaretMode()
|| m_part->isEditable()
|| (caretNode && caretNode->renderer()
&& caretNode->renderer()->style()->userInput()
== UI_ENABLED)) {
d->m_caretViewContext->freqTimerId = startTimer(500);
d->m_caretViewContext->visible = true;
}/*end if*/
}/*end if*/
showCaret();
#endif // KHTML_NO_CARET
QScrollView::focusInEvent( e );
}
void KHTMLView::focusOutEvent( QFocusEvent *e )
{
if(m_part) m_part->stopAutoScroll();
#ifndef KHTML_NO_CARET
if (d->m_caretViewContext) {
switch (d->m_caretViewContext->displayNonFocused) {
case KHTMLPart::CaretInvisible:
hideCaret();
break;
case KHTMLPart::CaretVisible: {
killTimer(d->m_caretViewContext->freqTimerId);
d->m_caretViewContext->freqTimerId = -1;
NodeImpl *caretNode = m_part->xmlDocImpl()->focusNode();
if (!d->m_caretViewContext->visible && (m_part->isCaretMode()
|| m_part->isEditable()
|| (caretNode && caretNode->renderer()
&& caretNode->renderer()->style()->userInput()
== UI_ENABLED))) {
d->m_caretViewContext->visible = true;
showCaret(true);
}/*end if*/
break;
}
case KHTMLPart::CaretBlink:
// simply leave as is
break;
}/*end switch*/
}/*end if*/
#endif // KHTML_NO_CARET
QScrollView::focusOutEvent( e );
}
void KHTMLView::slotScrollBarMoved()
{
if (!d->scrollingSelf)
d->scrollBarMoved = true;
}
void KHTMLView::timerEvent ( QTimerEvent *e )
{
// kdDebug() << "timer event " << e->timerId() << endl;
if ( e->timerId() == d->scrollTimerId ) {
switch (d->scrollDirection) {
case KHTMLViewPrivate::ScrollDown:
if (contentsY() + visibleHeight () >= contentsHeight())
d->newScrollTimer(this, 0);
else
scrollBy( 0, d->scrollBy );
break;
case KHTMLViewPrivate::ScrollUp:
if (contentsY() <= 0)
d->newScrollTimer(this, 0);
else
scrollBy( 0, -d->scrollBy );
break;
case KHTMLViewPrivate::ScrollRight:
if (contentsX() + visibleWidth () >= contentsWidth())
d->newScrollTimer(this, 0);
else
scrollBy( d->scrollBy, 0 );
break;
case KHTMLViewPrivate::ScrollLeft:
if (contentsX() <= 0)
d->newScrollTimer(this, 0);
else
scrollBy( -d->scrollBy, 0 );
break;
}
return;
}
else if ( e->timerId() == d->layoutTimerId ) {
d->firstRelayout = false;
d->dirtyLayout = true;
layout();
}
#ifndef KHTML_NO_CARET
else if (d->m_caretViewContext
&& e->timerId() == d->m_caretViewContext->freqTimerId) {
d->m_caretViewContext->visible = !d->m_caretViewContext->visible;
if (d->m_caretViewContext->displayed) {
updateContents(d->m_caretViewContext->x, d->m_caretViewContext->y,
d->m_caretViewContext->width,
d->m_caretViewContext->height);
}/*end if*/
// if (d->m_caretViewContext->visible) cout << "|" << flush;
// else cout << "°" << flush;
return;
}
#endif
if( m_part->xmlDocImpl() ) {
DOM::DocumentImpl *document = m_part->xmlDocImpl();
khtml::RenderCanvas* root = static_cast(document->renderer());
if ( root && !root->layouted() ) {
killTimer(d->repaintTimerId);
d->repaintTimerId = 0;
scheduleRelayout(false);
return;
}
}
setStaticBackground(d->useSlowRepaints);
// kdDebug() << "scheduled repaint "<< d->repaintTimerId << endl;
killTimer(d->repaintTimerId);
d->repaintTimerId = 0;
updateContents( d->updateRect );
d->updateRect = QRect();
if (d->dirtyLayout && !d->visibleWidgets.isEmpty()) {
QWidget* w;
d->dirtyLayout = false;
QRect visibleRect(contentsX(), contentsY(), visibleWidth(), visibleHeight());
QPtrList toRemove;
for (QPtrDictIterator it(d->visibleWidgets); it.current(); ++it) {
int xp = 0, yp = 0;
w = it.current();
RenderWidget* rw = static_cast( it.currentKey() );
if (!rw->absolutePosition(xp, yp) ||
!visibleRect.intersects(QRect(xp, yp, w->width(), w->height())))
toRemove.append(rw);
}
for (RenderWidget* r = toRemove.first(); r; r = toRemove.next())
if ( (w = d->visibleWidgets.take(r) ) )
addChild(w, 0, -500000);
}
}
void KHTMLView::scheduleRelayout(bool dorepaint)
{
if (!d->layoutSchedulingEnabled || d->layoutTimerId)
return;
d->layoutTimerId = startTimer( m_part->xmlDocImpl() && m_part->xmlDocImpl()->parsing()
? 1000 : 0 );
d->repaintLayout = dorepaint;
}
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 parsing = !m_part->xmlDocImpl() || m_part->xmlDocImpl()->parsing();
// kdDebug() << "parsing " << parsing << endl;
// kdDebug() << "complete " << d->complete << endl;
int time;
// if complete...
if (d->complete)
// ...repaint immediately
time = 20;
else
{
if (parsing)
// not complete and still parsing
time = 300;
else
// not complete, not parsing, extend the timer if it exists
// otherwise, repaint immediately
time = d->repaintTimerId ? 400 : 20;
}
if (d->repaintTimerId) {
killTimer(d->repaintTimerId);
d->updateRect = d->updateRect.unite(QRect(x,y,w,h));
} else
d->updateRect = QRect(x,y,w,h);
// kdDebug(6000) << "scheduled repaint for " << d->updateRect << endl;
d->repaintTimerId = startTimer( time );
// kdDebug() << "starting timer " << time << endl;
}
void KHTMLView::complete()
{
// kdDebug() << "KHTMLView::complete()" << endl;
d->complete = true;
// is there a relayout pending?
if (d->layoutTimerId)
{
// kdDebug() << "requesting relayout now" << endl;
// do it now
killTimer(d->layoutTimerId);
d->layoutTimerId = startTimer( 0 );
}
// is there a repaint pending?
if (d->repaintTimerId)
{
// kdDebug() << "requesting repaint now" << endl;
// do it now
killTimer(d->repaintTimerId);
d->repaintTimerId = startTimer( 20 );
}
}
#ifndef KHTML_NO_CARET
// ### the dependencies on static functions are a nightmare. just be
// hacky and include the implementation here. Clean me up, please.
#include "khtml_caret.cpp"
void KHTMLView::initCaret(bool keepSelection)
{
#if DEBUG_CARETMODE > 0
kdDebug(6200) << "begin initCaret" << endl;
#endif
// save caretMoved state as moveCaretTo changes it
if (m_part->xmlDocImpl()) {
d->caretViewContext();
bool cmoved = d->m_caretViewContext->caretMoved;
if (m_part->d->caretNode().isNull()) {
// set to document, position will be sanitized anyway
m_part->d->caretNode() = m_part->document();
m_part->d->caretOffset() = 0L;
// This sanity check is necessary for the not so unlikely case that
// setEditable or setCaretMode is called before any render objects have
// been created.
if (!m_part->d->caretNode().handle()->renderer()) return;
}/*end if*/
// kdDebug(6200) << "d->m_selectionStart " << m_part->d->m_selectionStart.handle()
// << " d->m_selectionEnd " << m_part->d->m_selectionEnd.handle() << endl;
// ### does not repaint the selection on keepSelection!=false
moveCaretTo(m_part->d->caretNode().handle(), m_part->d->caretOffset(), !keepSelection);
// kdDebug(6200) << "d->m_selectionStart " << m_part->d->m_selectionStart.handle()
// << " d->m_selectionEnd " << m_part->d->m_selectionEnd.handle() << endl;
d->m_caretViewContext->caretMoved = cmoved;
}/*end if*/
#if DEBUG_CARETMODE > 0
kdDebug(6200) << "end initCaret" << endl;
#endif
}
bool KHTMLView::caretOverrides() const
{
bool cm = m_part->isCaretMode();
bool dm = m_part->isEditable();
return cm && !dm ? false
: (dm || m_part->d->caretNode().handle()->contentEditable())
&& d->editorContext()->override;
}
void KHTMLView::ensureNodeHasFocus(NodeImpl *node)
{
if (m_part->isCaretMode() || m_part->isEditable()) return;
if (node->focused()) return;
// Find first ancestor whose "user-input" is "enabled"
NodeImpl *firstAncestor = 0;
while (node) {
if (node->renderer()
&& node->renderer()->style()->userInput() != UI_ENABLED)
break;
firstAncestor = node;
node = node->parentNode();
}/*wend*/
if (!node) firstAncestor = 0;
// Set focus node on the document
m_part->xmlDocImpl()->setFocusNode(firstAncestor);
emit m_part->nodeActivated(Node(firstAncestor));
}
void KHTMLView::recalcAndStoreCaretPos(InlineBox *hintBox)
{
if (!m_part || m_part->d->caretNode().isNull()) return;
d->caretViewContext();
NodeImpl *caretNode = m_part->d->caretNode().handle();
#if DEBUG_CARETMODE > 0
kdDebug(6200) << "recalcAndStoreCaretPos: caretNode=" << caretNode << (caretNode ? " "+caretNode->nodeName().string() : QString::null) << " r@" << caretNode->renderer() << (caretNode->renderer() && caretNode->renderer()->isText() ? " \"" + QConstString(static_cast(caretNode->renderer())->str->s, kMin(static_cast(caretNode->renderer())->str->l, 15)).string() + "\"" : QString::null) << endl;
#endif
caretNode->getCaret(m_part->d->caretOffset(),
caretOverrides(),
d->m_caretViewContext->x, d->m_caretViewContext->y,
d->m_caretViewContext->width,
d->m_caretViewContext->height);
if (hintBox && d->m_caretViewContext->x == -1) {
#if DEBUG_CARETMODE > 1
kdDebug(6200) << "using hint inline box coordinates" << endl;
#endif
RenderObject *r = caretNode->renderer();
const QFontMetrics &fm = r->style()->fontMetrics();
int absx, absy;
r->containingBlock()->absolutePosition(absx, absy,
false); // ### what about fixed?
d->m_caretViewContext->x = absx + hintBox->xPos();
d->m_caretViewContext->y = absy + hintBox->yPos()
+ hintBox->baseline() - fm.ascent();
d->m_caretViewContext->width = 1;
// ### firstline not regarded. But I think it can be safely neglected
// as hint boxes are only used for empty lines.
d->m_caretViewContext->height = fm.height();
}/*end if*/
#if DEBUG_CARETMODE > 4
// kdDebug(6200) << "freqTimerId: "<m_caretViewContext->freqTimerId< 0
kdDebug(6200) << "caret: ofs="<d->caretOffset()<<" "
<<" x="<m_caretViewContext->x<<" y="<m_caretViewContext->y
<<" h="<m_caretViewContext->height<m_caretViewContext) {
killTimer(d->m_caretViewContext->freqTimerId);
if (hasFocus() || d->m_caretViewContext->displayNonFocused
== KHTMLPart::CaretBlink) {
d->m_caretViewContext->freqTimerId = startTimer(500);
} else {
d->m_caretViewContext->freqTimerId = -1;
}/*end if*/
d->m_caretViewContext->visible = true;
if ((d->m_caretViewContext->displayed = (hasFocus()
|| d->m_caretViewContext->displayNonFocused
!= KHTMLPart::CaretInvisible))) {
updateContents(d->m_caretViewContext->x, d->m_caretViewContext->y,
d->m_caretViewContext->width,
d->m_caretViewContext->height);
}/*end if*/
// kdDebug(6200) << "caret on" << endl;
}/*end if*/
}
void KHTMLView::caretOff()
{
if (d->m_caretViewContext) {
killTimer(d->m_caretViewContext->freqTimerId);
d->m_caretViewContext->freqTimerId = -1;
d->m_caretViewContext->displayed = false;
if (d->m_caretViewContext->visible) {
d->m_caretViewContext->visible = false;
updateContents(d->m_caretViewContext->x, d->m_caretViewContext->y,
d->m_caretViewContext->width,
d->m_caretViewContext->height);
}/*end if*/
// kdDebug(6200) << "caret off" << endl;
}/*end if*/
}
void KHTMLView::showCaret(bool forceRepaint)
{
if (d->m_caretViewContext) {
d->m_caretViewContext->displayed = true;
if (d->m_caretViewContext->visible) {
if (!forceRepaint) {
updateContents(d->m_caretViewContext->x, d->m_caretViewContext->y,
d->m_caretViewContext->width,
d->m_caretViewContext->height);
} else {
repaintContents(d->m_caretViewContext->x, d->m_caretViewContext->y,
d->m_caretViewContext->width,
d->m_caretViewContext->height);
}/*end if*/
}/*end if*/
// kdDebug(6200) << "caret shown" << endl;
}/*end if*/
}
bool KHTMLView::foldSelectionToCaret(NodeImpl *startNode, long startOffset,
NodeImpl *endNode, long endOffset)
{
m_part->d->m_selectionStart = m_part->d->m_selectionEnd = m_part->d->caretNode();
m_part->d->m_startOffset = m_part->d->m_endOffset = m_part->d->caretOffset();
m_part->d->m_extendAtEnd = true;
bool folded = startNode != endNode || startOffset != endOffset;
// Only clear the selection if there has been one.
if (folded) {
m_part->xmlDocImpl()->clearSelection();
}/*end if*/
return folded;
}
void KHTMLView::hideCaret()
{
if (d->m_caretViewContext) {
if (d->m_caretViewContext->visible) {
// kdDebug(6200) << "redraw caret hidden" << endl;
d->m_caretViewContext->visible = false;
// force repaint, otherwise the event won't be handled
// before the focus leaves the window
repaintContents(d->m_caretViewContext->x, d->m_caretViewContext->y,
d->m_caretViewContext->width,
d->m_caretViewContext->height);
d->m_caretViewContext->visible = true;
}/*end if*/
d->m_caretViewContext->displayed = false;
// kdDebug(6200) << "caret hidden" << endl;
}/*end if*/
}
int KHTMLView::caretDisplayPolicyNonFocused() const
{
if (d->m_caretViewContext)
return d->m_caretViewContext->displayNonFocused;
else
return KHTMLPart::CaretInvisible;
}
void KHTMLView::setCaretDisplayPolicyNonFocused(int policy)
{
d->caretViewContext();
// int old = d->m_caretViewContext->displayNonFocused;
d->m_caretViewContext->displayNonFocused = (KHTMLPart::CaretDisplayPolicy)policy;
// make change immediately take effect if not focused
if (!hasFocus()) {
switch (d->m_caretViewContext->displayNonFocused) {
case KHTMLPart::CaretInvisible:
hideCaret();
break;
case KHTMLPart::CaretBlink:
if (d->m_caretViewContext->freqTimerId != -1) break;
d->m_caretViewContext->freqTimerId = startTimer(500);
// fall through
case KHTMLPart::CaretVisible:
d->m_caretViewContext->displayed = true;
showCaret();
break;
}/*end switch*/
}/*end if*/
}
bool KHTMLView::placeCaret(InlineBox *hintBox)
{
CaretViewContext *cv = d->caretViewContext();
caretOff();
recalcAndStoreCaretPos(hintBox);
cv->origX = cv->x;
NodeImpl *caretNode = m_part->d->caretNode().handle();
// ### why is it sometimes null?
if (!caretNode || !caretNode->renderer()) return false;
ensureNodeHasFocus(caretNode);
if (m_part->isCaretMode() || m_part->isEditable()
|| caretNode->renderer()->style()->userInput() == UI_ENABLED) {
caretOn();
return true;
}/*end if*/
return false;
}
bool KHTMLView::extendSelection(NodeImpl *oldStartSel, long oldStartOfs,
NodeImpl *oldEndSel, long oldEndOfs)
{
bool changed = false;
if (m_part->d->m_selectionStart == m_part->d->m_selectionEnd
&& m_part->d->m_startOffset == m_part->d->m_endOffset) {
changed = foldSelectionToCaret(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs);
m_part->d->m_extendAtEnd = true;
} else do {
changed = m_part->d->m_selectionStart.handle() != oldStartSel
|| m_part->d->m_startOffset != oldStartOfs
|| m_part->d->m_selectionEnd.handle() != oldEndSel
|| m_part->d->m_endOffset != oldEndOfs;
if (!changed) break;
// determine start position -- caret position is always at end.
NodeImpl *startNode;
long startOffset;
if (m_part->d->m_extendAtEnd) {
startNode = m_part->d->m_selectionStart.handle();
startOffset = m_part->d->m_startOffset;
} else {
startNode = m_part->d->m_selectionEnd.handle();
startOffset = m_part->d->m_endOffset;
m_part->d->m_selectionEnd = m_part->d->m_selectionStart;
m_part->d->m_endOffset = m_part->d->m_startOffset;
}/*end if*/
bool swapNeeded = false;
if (!m_part->d->m_selectionEnd.isNull() && startNode) {
swapNeeded = RangeImpl::compareBoundaryPoints(startNode, startOffset,
m_part->d->m_selectionEnd.handle(),
m_part->d->m_endOffset) >= 0;
}/*end if*/
m_part->d->m_selectionStart = startNode;
m_part->d->m_startOffset = startOffset;
if (swapNeeded) {
m_part->xmlDocImpl()->setSelection(m_part->d->m_selectionEnd.handle(),
m_part->d->m_endOffset, m_part->d->m_selectionStart.handle(),
m_part->d->m_startOffset);
} else {
m_part->xmlDocImpl()->setSelection(m_part->d->m_selectionStart.handle(),
m_part->d->m_startOffset, m_part->d->m_selectionEnd.handle(),
m_part->d->m_endOffset);
}/*end if*/
} while(false);/*end if*/
return changed;
}
void KHTMLView::updateSelection(NodeImpl *oldStartSel, long oldStartOfs,
NodeImpl *oldEndSel, long oldEndOfs)
{
if (m_part->d->m_selectionStart == m_part->d->m_selectionEnd
&& m_part->d->m_startOffset == m_part->d->m_endOffset) {
if (foldSelectionToCaret(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs)) {
m_part->emitSelectionChanged();
}/*end if*/
m_part->d->m_extendAtEnd = true;
} else {
// check if the extending end has passed the immobile end
if (!m_part->d->m_selectionEnd.isNull() && !m_part->d->m_selectionEnd.isNull()) {
bool swapNeeded = RangeImpl::compareBoundaryPoints(
m_part->d->m_selectionStart.handle(), m_part->d->m_startOffset,
m_part->d->m_selectionEnd.handle(), m_part->d->m_endOffset) >= 0;
if (swapNeeded) {
DOM::Node tmpNode = m_part->d->m_selectionStart;
long tmpOffset = m_part->d->m_startOffset;
m_part->d->m_selectionStart = m_part->d->m_selectionEnd;
m_part->d->m_startOffset = m_part->d->m_endOffset;
m_part->d->m_selectionEnd = tmpNode;
m_part->d->m_endOffset = tmpOffset;
m_part->d->m_startBeforeEnd = true;
m_part->d->m_extendAtEnd = !m_part->d->m_extendAtEnd;
}/*end if*/
}/*end if*/
m_part->xmlDocImpl()->setSelection(m_part->d->m_selectionStart.handle(),
m_part->d->m_startOffset, m_part->d->m_selectionEnd.handle(),
m_part->d->m_endOffset);
m_part->emitSelectionChanged();
}/*end if*/
}
void KHTMLView::caretKeyPressEvent(QKeyEvent *_ke)
{
NodeImpl *oldStartSel = m_part->d->m_selectionStart.handle();
long oldStartOfs = m_part->d->m_startOffset;
NodeImpl *oldEndSel = m_part->d->m_selectionEnd.handle();
long oldEndOfs = m_part->d->m_endOffset;
NodeImpl *oldCaretNode = m_part->d->caretNode().handle();
long oldOffset = m_part->d->caretOffset();
bool ctrl = _ke->state() & ControlButton;
// FIXME: this is that widely indented because I will write ifs around it.
switch(_ke->key()) {
case Key_Space:
break;
case Key_Down:
moveCaretNextLine(1);
break;
case Key_Up:
moveCaretPrevLine(1);
break;
case Key_Left:
moveCaretBy(false, ctrl ? CaretByWord : CaretByCharacter, 1);
break;
case Key_Right:
moveCaretBy(true, ctrl ? CaretByWord : CaretByCharacter, 1);
break;
case Key_Next:
moveCaretNextPage();
break;
case Key_Prior:
moveCaretPrevPage();
break;
case Key_Home:
if (ctrl)
moveCaretToDocumentBoundary(false);
else
moveCaretToLineBegin();
break;
case Key_End:
if (ctrl)
moveCaretToDocumentBoundary(true);
else
moveCaretToLineEnd();
break;
}/*end switch*/
if ((m_part->d->caretNode().handle() != oldCaretNode
|| m_part->d->caretOffset() != oldOffset)
// node should never be null, but faulty conditions may cause it to be
&& !m_part->d->caretNode().isNull()) {
d->m_caretViewContext->caretMoved = true;
if (_ke->state() & ShiftButton) { // extend selection
updateSelection(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs);
} else { // clear any selection
if (foldSelectionToCaret(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs))
m_part->emitSelectionChanged();
}/*end if*/
m_part->emitCaretPositionChanged(m_part->d->caretNode(), m_part->d->caretOffset());
}/*end if*/
_ke->accept();
}
bool KHTMLView::moveCaretTo(NodeImpl *node, long offset, bool clearSel)
{
sanitizeCaretState(node, offset);
if (!node) return false;
// need to find out the node's inline box. If there is none, this function
// will snap to the next node that has one. This is necessary to make the
// caret visible in any case.
RenderArena arena;
RenderFlow *cb;
InlineBox *box = 0;
findFlowBox(node, offset, &arena, cb, &box);
if (box && box->object() != node->renderer()) {
if (box->object()->element()) {
node = box->object()->element();
offset = node->minOffset();
#if DEBUG_CARETMODE > 1
kdDebug(6200) << "set new node " << node->nodeName().string() << "@" << node << endl;
#endif
} else { // box has no associated element -> do not use
// this case should actually never happen.
box = 0;
kdError(6200) << "Box contains no node! Crash imminent" << endl;
}/*end if*/
}
NodeImpl *oldStartSel = m_part->d->m_selectionStart.handle();
long oldStartOfs = m_part->d->m_startOffset;
NodeImpl *oldEndSel = m_part->d->m_selectionEnd.handle();
long oldEndOfs = m_part->d->m_endOffset;
// test for position change
bool posChanged = m_part->d->caretNode().handle() != node
|| m_part->d->caretOffset() != offset;
bool selChanged = false;
m_part->d->caretNode() = node;
m_part->d->caretOffset() = offset;
if (clearSel || !oldStartSel || !oldEndSel) {
selChanged = foldSelectionToCaret(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs);
} else {
//kdDebug(6200) << "moveToCaret: extendSelection: m_extendAtEnd " << m_part->d->m_extendAtEnd << endl;
//kdDebug(6200) << "selection: start(" << m_part->d->m_selectionStart.handle() << "," << m_part->d->m_startOffset << "), end(" << m_part->d->m_selectionEnd.handle() << "," << m_part->d->m_endOffset << "), caret(" << m_part->d->caretNode().handle() << "," << m_part->d->caretOffset() << ")" << endl;
selChanged = extendSelection(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs);
//kdDebug(6200) << "after extendSelection: m_extendAtEnd " << m_part->d->m_extendAtEnd << endl;
//kdDebug(6200) << "selection: start(" << m_part->d->m_selectionStart.handle() << "," << m_part->d->m_startOffset << "), end(" << m_part->d->m_selectionEnd.handle() << "," << m_part->d->m_endOffset << "), caret(" << m_part->d->caretNode().handle() << "," << m_part->d->caretOffset() << ")" << endl;
}/*end if*/
d->caretViewContext()->caretMoved = true;
bool visible_caret = placeCaret(box);
// FIXME: if the old position was !visible_caret, and the new position is
// also, then two caretPositionChanged signals with a null Node are
// emitted in series.
if (posChanged) {
m_part->emitCaretPositionChanged(visible_caret ? node : 0, offset);
}/*end if*/
return selChanged;
}
void KHTMLView::moveCaretByLine(bool next, int count)
{
// FIXME: what if the node is removed whilst we access it?
// Current solution: bail out
Node &caretNodeRef = m_part->d->caretNode();
if (caretNodeRef.isNull()) return;
NodeImpl *caretNode = caretNodeRef.handle();
// kdDebug(6200) << ": caretNode=" << caretNode << endl;
long offset = m_part->d->caretOffset();
CaretViewContext *cv = d->caretViewContext();
LinearDocument ld(m_part, caretNode, offset);
ErgonomicEditableLineIterator it(ld.current(), cv->origX);
// move count lines vertically
while (count > 0 && it != ld.end() && it != ld.preBegin()) {
count--;
if (next) ++it; else --it;
}/*wend*/
// Nothing? Then leave everything as is.
if (it == ld.end() || it == ld.preBegin()) return;
int x, absx, absy;
InlineBox *caretBox = nearestInlineBox(it, d->m_caretViewContext, x, absx, absy);
placeCaretOnLine(caretBox, x, absx, absy);
}
void KHTMLView::placeCaretOnLine(InlineBox *caretBox, int x, int absx, int absy)
{
// paranoia sanity check
if (!caretBox) return;
RenderObject *caretRender = caretBox->object();
NodeImpl *caretNode = caretRender->element();
#if DEBUG_CARETMODE > 0
kdDebug(6200) << "got valid caretBox " << caretBox << endl;
kdDebug(6200) << "xPos: " << caretBox->xPos() << " yPos: " << caretBox->yPos()
<< " width: " << caretBox->width() << " height: " << caretBox->height() << endl;
if (caretBox->isInlineTextBox()) { kdDebug(6200) << "contains \"" << QString(((RenderText *)((InlineTextBox *)caretBox)->object())->str->s + ((InlineTextBox *)caretBox)->m_start, ((InlineTextBox *)caretBox)->m_len) << "\"" << endl;}
#endif
// inquire height of caret
int caretHeight = caretBox->height();
bool isText = caretBox->isInlineTextBox();
int yOfs = 0; // y-offset for text nodes
if (isText) {
// text boxes need extrawurst
RenderText *t = static_cast(caretRender);
const QFontMetrics &fm = t->metrics(caretBox->m_firstLine);
caretHeight = fm.height();
yOfs = caretBox->baseline() - fm.ascent();
}/*end if*/
caretOff();
// set new caret node
m_part->d->caretNode() = caretNode;
long &offset = m_part->d->caretOffset();
// set all variables not needing special treatment
d->m_caretViewContext->y = caretBox->yPos() + yOfs;
d->m_caretViewContext->height = caretHeight;
d->m_caretViewContext->width = 1; // FIXME: regard override
int xPos = caretBox->xPos();
int caretBoxWidth = caretBox->width();
// before or at beginning of inline box -> place at beginning
if (x <= xPos) {
d->m_caretViewContext->x = xPos;
offset = caretBox->minOffset();
// somewhere within this block
} else if (x > xPos && x <= xPos + caretBoxWidth) {
if (isText) { // find out where exactly
offset = static_cast(caretBox)->offsetForPoint(x,
d->m_caretViewContext->x);
#if DEBUG_CARETMODE > 2
kdDebug(6200) << "deviation from origX " << d->m_caretViewContext->x - x << endl;
#endif
} else { // snap to nearest end
if (xPos + caretBoxWidth - x < x - xPos) {
d->m_caretViewContext->x = xPos + caretBoxWidth;
offset = caretNode ? caretNode->maxOffset() : 1;
} else {
d->m_caretViewContext->x = xPos;
offset = caretNode ? caretNode->minOffset() : 0;
}/*end if*/
}/*end if*/
} else { // after the inline box -> place at end
d->m_caretViewContext->x = xPos + caretBoxWidth;
offset = caretBox->maxOffset();
}/*end if*/
#if DEBUG_CARETMODE > 0
kdDebug(6200) << "new offset: " << offset << endl;
#endif
d->m_caretViewContext->x += absx;
d->m_caretViewContext->y += absy;
ensureVisible(d->m_caretViewContext->x, d->m_caretViewContext->y,
d->m_caretViewContext->width, d->m_caretViewContext->height);
d->scrollBarMoved = false;
ensureNodeHasFocus(caretNode);
caretOn();
}
void KHTMLView::moveCaretToLineBoundary(bool end)
{
// FIXME: what if the node is removed whilst we access it?
// Current solution: bail out
Node &caretNodeRef = m_part->d->caretNode();
if (caretNodeRef.isNull()) return;
NodeImpl *caretNode = caretNodeRef.handle();
// kdDebug(6200) << ": caretNode=" << caretNode << endl;
long offset = m_part->d->caretOffset();
LinearDocument ld(m_part, caretNode, offset);
EditableLineIterator it = ld.current();
if (it == ld.end()) return; // should not happen, but who knows
EditableInlineBoxIterator fbit(it, end);
InlineBox *b = *fbit;
Q_ASSERT(b);
RenderObject *cb = (*it)->object();
int absx, absy;
if (cb) cb->absolutePosition(absx,absy);
else absx = absy = 0;
int x = b->xPos() + (end ? b->width() : 0);
d->m_caretViewContext->origX = absx + x;
placeCaretOnLine(b, x, absx, absy);
}
void KHTMLView::moveCaretToDocumentBoundary(bool end)
{
// FIXME: what if the node is removed whilst we access it?
// Current solution: bail out
Node &caretNodeRef = m_part->d->caretNode();
if (caretNodeRef.isNull()) return;
NodeImpl *caretNode = caretNodeRef.handle();
// kdDebug(6200) << ": caretNode=" << caretNode << endl;
long offset = m_part->d->caretOffset();
LinearDocument ld(m_part, caretNode, offset);
EditableLineIterator it(end ? ld.preEnd() : ld.begin(), end);
if (it == ld.end() || it == ld.preBegin()) return; // should not happen, but who knows
EditableInlineBoxIterator fbit = it;
InlineBox *b = *fbit;
Q_ASSERT(b);
RenderObject *cb = (*it)->object();
int absx, absy;
if (cb) cb->absolutePosition(absx, absy);
else absx = absy = 0;
int x = b->xPos()/* + (end ? b->width() : 0) reactivate for rtl*/;
d->m_caretViewContext->origX = absx + x;
placeCaretOnLine(b, x, absx, absy);
}
void KHTMLView::moveCaretBy(bool next, CaretMovement cmv, int count)
{
if (!m_part) return;
// FIXME: what if the node is removed whilst we access it?
// Current solution: bail out
Node &caretNodeRef = m_part->d->caretNode();
if (caretNodeRef.isNull()) return;
NodeImpl *caretNode = caretNodeRef.handle();
// kdDebug(6200) << ": caretNode=" << caretNode << endl;
long &offset = m_part->d->caretOffset();
LinearDocument ld(m_part, caretNode, offset);
EditableCharacterIterator it(&ld);
InlineBox *hintBox = it.box();
while (it.node() && count > 0) {
count--;
if (cmv == CaretByCharacter) {
if (next) ++it;
else --it;
} else if (cmv == CaretByWord) {
if (next) moveItToNextWord(it);
else moveItToPrevWord(it);
}/*end if*/
}/*wend*/
if (it.node()) {
caretNodeRef = it.node();
offset = it.offset();
hintBox = it.box();
#if DEBUG_CARETMODE > 2
kdDebug(6200) << "set by valid node. offset: " << offset << endl;
#endif
} else {
offset = next ? caretNode->maxOffset() : caretNode->minOffset();
#if DEBUG_CARETMODE > 0
kdDebug(6200) << "set by INvalid node. offset: " << offset << endl;
#endif
}/*end if*/
placeCaretOnChar(hintBox);
}
void KHTMLView::placeCaretOnChar(InlineBox *hintBox)
{
caretOff();
recalcAndStoreCaretPos(hintBox);
ensureVisible(d->m_caretViewContext->x, d->m_caretViewContext->y,
d->m_caretViewContext->width, d->m_caretViewContext->height);
d->m_caretViewContext->origX = d->m_caretViewContext->x;
d->scrollBarMoved = false;
#if DEBUG_CARETMODE > 3
//if (caretNode->isTextNode()) kdDebug(6200) << "text[0] = " << (int)*((TextImpl *)caretNode)->data().unicode() << " text :\"" << ((TextImpl *)caretNode)->data().string() << "\"" << endl;
#endif
ensureNodeHasFocus(m_part->d->caretNode().handle());
caretOn();
}
void KHTMLView::moveCaretByPage(bool next)
{
// FIXME: what if the node is removed whilst we access it?
// Current solution: bail out
Node &caretNodeRef = m_part->d->caretNode();
if (caretNodeRef.isNull()) return;
NodeImpl *caretNode = caretNodeRef.handle();
// kdDebug(6200) << ": caretNode=" << caretNode << endl;
long offset = m_part->d->caretOffset();
int offs = (clipper()->height() < 30) ? clipper()->height() : 30;
// Minimum distance the caret must be moved
int mindist = clipper()->height() - offs;
CaretViewContext *cv = d->caretViewContext();
// int y = cv->y; // we always measure the top border
LinearDocument ld(m_part, caretNode, offset);
ErgonomicEditableLineIterator it(ld.current(), cv->origX);
moveIteratorByPage(ld, it, mindist, next);
int x, absx, absy;
InlineBox *caretBox = nearestInlineBox(it, d->m_caretViewContext, x, absx, absy);
placeCaretOnLine(caretBox, x, absx, absy);
}
void KHTMLView::moveCaretPrevWord()
{
moveCaretBy(false, CaretByWord, 1);
}
void KHTMLView::moveCaretNextWord()
{
moveCaretBy(true, CaretByWord, 1);
}
void KHTMLView::moveCaretPrevLine(int n)
{
moveCaretByLine(false, n);
}
void KHTMLView::moveCaretNextLine(int n)
{
moveCaretByLine(true, n);
}
void KHTMLView::moveCaretPrevPage()
{
moveCaretByPage(false);
}
void KHTMLView::moveCaretNextPage()
{
moveCaretByPage(true);
}
void KHTMLView::moveCaretToLineBegin()
{
moveCaretToLineBoundary(false);
}
void KHTMLView::moveCaretToLineEnd()
{
moveCaretToLineBoundary(true);
}
#endif // KHTML_NO_CARET
#undef DEBUG_CARETMODE