You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdelibs/khtml/khtmlview.cpp

4624 lines
150 KiB

/* This file is part of the KDE project
*
* Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
* 1999 Lars Knoll <knoll@kde.org>
* 1999 Antti Koivisto <koivisto@kde.org>
* 2000-2004 Dirk Mueller <mueller@kde.org>
* 2003 Leo Savernik <l.savernik@aon.at>
* 2003-2004 Apple Computer, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "khtmlview.moc"
#include "khtmlview.h"
#include "khtml_part.h"
#include "khtml_events.h"
#include "html/html_documentimpl.h"
#include "html/html_inlineimpl.h"
#include "html/html_formimpl.h"
#include "rendering/render_arena.h"
#include "rendering/render_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 "css/csshelper.h"
#include "misc/htmlhashes.h"
#include "misc/helper.h"
#include "misc/loader.h"
#include "khtml_settings.h"
#include "khtml_printsettings.h"
#include "khtmlpart_p.h"
#ifndef KHTML_NO_CARET
#include "khtml_caret_p.h"
#include "xml/dom2_rangeimpl.h"
#endif
#include <kapplication.h>
#include <kcursor.h>
#include <kdebug.h>
#include <kdialogbase.h>
#include <kiconloader.h>
#include <kimageio.h>
#include <klocale.h>
#include <knotifyclient.h>
#include <kprinter.h>
#include <ksimpleconfig.h>
#include <kstandarddirs.h>
#include <kstdaccel.h>
#include <kstringhandler.h>
#include <kurldrag.h>
#include <tqbitmap.h>
#include <tqlabel.h>
#include <tqobjectlist.h>
#include <tqpaintdevicemetrics.h>
#include <tqpainter.h>
#include <tqptrdict.h>
#include <tqtooltip.h>
#include <tqstring.h>
#include <tqstylesheet.h>
#include <tqtimer.h>
#include <tqvaluevector.h>
//#define DEBUG_NO_PAINT_BUFFER
//#define DEBUG_FLICKER
//#define DEBUG_PIXEL
#ifdef Q_WS_X11
#include <X11/Xlib.h>
#include <fixx11h.h>
#endif
#define PAINT_BUFFER_HEIGHT 128
#if 0
namespace khtml {
void dumpLineBoxes(RenderFlow *flow);
}
#endif
using namespace DOM;
using namespace khtml;
class KHTMLToolTip;
#ifndef QT_NO_TOOLTIP
class KHTMLToolTip : public TQToolTip
{
public:
KHTMLToolTip(KHTMLView *view, KHTMLViewPrivate* vp) : TQToolTip(view->viewport())
{
m_view = view;
m_viewprivate = vp;
};
protected:
virtual void maybeTip(const TQPoint &);
private:
KHTMLView *m_view;
KHTMLViewPrivate* m_viewprivate;
};
#endif
class KHTMLViewPrivate {
friend class KHTMLToolTip;
public:
enum PseudoFocusNodes {
PFNone,
PFTop,
PFBottom
};
enum CompletedState {
CSNone = 0,
CSFull,
CSActionPending
};
KHTMLViewPrivate()
: underMouse( 0 ), underMouseNonShared( 0 ), visibleWidgets( 107 )
#ifndef NO_SMOOTH_SCROLL_HACK
, dx(0), dy(0), ddx(0), ddy(0), rdx(0), rdy(0), scrolling(false)
#endif
{
#ifndef KHTML_NO_CARET
m_caretViewContext = 0;
m_editorContext = 0;
#endif // KHTML_NO_CARET
postponed_autorepeat = NULL;
reset();
vmode = TQScrollView::Auto;
hmode = TQScrollView::Auto;
tp=0;
paintBuffer=0;
vertPaintBuffer=0;
formCompletions=0;
prevScrollbarVisible = true;
tooltip = 0;
possibleTripleClick = false;
emitCompletedAfterRepaint = CSNone;
cursor_icon_widget = NULL;
m_mouseScrollTimer = 0;
m_mouseScrollIndicator = 0;
}
~KHTMLViewPrivate()
{
delete formCompletions;
delete tp; tp = 0;
delete paintBuffer; paintBuffer =0;
delete vertPaintBuffer;
delete postponed_autorepeat;
if (underMouse)
underMouse->deref();
if (underMouseNonShared)
underMouseNonShared->deref();
delete tooltip;
#ifndef KHTML_NO_CARET
delete m_caretViewContext;
delete m_editorContext;
#endif // KHTML_NO_CARET
delete cursor_icon_widget;
delete m_mouseScrollTimer;
delete m_mouseScrollIndicator;
}
void reset()
{
if (underMouse)
underMouse->deref();
underMouse = 0;
if (underMouseNonShared)
underMouseNonShared->deref();
underMouseNonShared = 0;
linkPressed = false;
useSlowRepaints = false;
tabMovePending = false;
lastTabbingDirection = true;
pseudoFocusNode = PFNone;
#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
vmode = TQScrollView::AlwaysOff;
hmode = TQScrollView::AlwaysOff;
#endif
#ifdef DEBUG_PIXEL
timer.start();
pixelbooth = 0;
repaintbooth = 0;
#endif
scrollBarMoved = false;
contentsMoving = false;
ignoreWheelEvents = false;
borderX = 30;
borderY = 30;
paged = false;
clickX = -1;
clickY = -1;
prevMouseX = -1;
prevMouseY = -1;
clickCount = 0;
isDoubleClick = false;
scrollingSelf = false;
delete postponed_autorepeat;
postponed_autorepeat = NULL;
layoutTimerId = 0;
repaintTimerId = 0;
scrollTimerId = 0;
scrollSuspended = false;
scrollSuspendPreActivate = false;
complete = false;
firstRelayout = true;
needsFullRepaint = true;
dirtyLayout = false;
layoutSchedulingEnabled = true;
painting = false;
updateRegion = TQRegion();
m_dialogsAllowed = true;
#ifndef KHTML_NO_CARET
if (m_caretViewContext) {
m_caretViewContext->caretMoved = false;
m_caretViewContext->keyReleasePending = false;
}/*end if*/
#endif // KHTML_NO_CARET
#ifndef KHTML_NO_TYPE_AHEAD_FIND
typeAheadActivated = false;
#endif // KHTML_NO_TYPE_AHEAD_FIND
accessKeysActivated = false;
accessKeysPreActivate = false;
// We ref/deref to ensure defaultHTMLSettings is available
KHTMLFactory::ref();
accessKeysEnabled = KHTMLFactory::defaultHTMLSettings()->accessKeysEnabled();
KHTMLFactory::deref();
emitCompletedAfterRepaint = CSNone;
}
void newScrollTimer(TQWidget *view, int tid)
{
//kdDebug(6000) << "newScrollTimer timer " << tid << endl;
view->killTimer(scrollTimerId);
scrollTimerId = tid;
scrollSuspended = false;
}
enum ScrollDirection { ScrollLeft, ScrollRight, ScrollUp, ScrollDown };
void adjustScroller(TQWidget *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<int>(scrollDirection) != direction &&
(static_cast<int>(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;
}
#ifndef KHTML_NO_CARET
/** this function returns an instance of the caret view context. If none
* exists, it will be instantiated.
*/
CaretViewContext *caretViewContext() {
if (!m_caretViewContext) m_caretViewContext = new CaretViewContext();
return m_caretViewContext;
}
/** this function returns an instance of the editor context. If none
* exists, it will be instantiated.
*/
EditorContext *editorContext() {
if (!m_editorContext) m_editorContext = new EditorContext();
return m_editorContext;
}
#endif // KHTML_NO_CARET
#ifdef DEBUG_PIXEL
TQTime timer;
unsigned int pixelbooth;
unsigned int repaintbooth;
#endif
TQPainter *tp;
TQPixmap *paintBuffer;
TQPixmap *vertPaintBuffer;
NodeImpl *underMouse;
NodeImpl *underMouseNonShared;
bool tabMovePending:1;
bool lastTabbingDirection:1;
PseudoFocusNodes pseudoFocusNode:2;
bool scrollBarMoved:1;
bool contentsMoving:1;
TQScrollView::ScrollBarMode vmode;
TQScrollView::ScrollBarMode hmode;
bool prevScrollbarVisible:1;
bool linkPressed:1;
bool useSlowRepaints:1;
bool ignoreWheelEvents:1;
int borderX, borderY;
KSimpleConfig *formCompletions;
bool paged;
int clickX, clickY, clickCount;
bool isDoubleClick;
int prevMouseX, prevMouseY;
bool scrollingSelf;
int layoutTimerId;
TQKeyEvent* postponed_autorepeat;
int repaintTimerId;
int scrollTimerId;
int scrollTiming;
int scrollBy;
ScrollDirection scrollDirection :2;
bool scrollSuspended :1;
bool scrollSuspendPreActivate :1;
bool complete :1;
bool firstRelayout :1;
bool layoutSchedulingEnabled :1;
bool needsFullRepaint :1;
bool painting :1;
bool possibleTripleClick :1;
bool dirtyLayout :1;
bool m_dialogsAllowed :1;
TQRegion updateRegion;
KHTMLToolTip *tooltip;
TQPtrDict<TQWidget> visibleWidgets;
#ifndef KHTML_NO_CARET
CaretViewContext *m_caretViewContext;
EditorContext *m_editorContext;
#endif // KHTML_NO_CARET
#ifndef KHTML_NO_TYPE_AHEAD_FIND
TQString findString;
TQTimer timer;
bool findLinksOnly;
bool typeAheadActivated;
#endif // KHTML_NO_TYPE_AHEAD_FIND
bool accessKeysEnabled;
bool accessKeysActivated;
bool accessKeysPreActivate;
CompletedState emitCompletedAfterRepaint;
TQWidget* cursor_icon_widget;
// scrolling activated by MMB
short m_mouseScroll_byX;
short m_mouseScroll_byY;
TQTimer *m_mouseScrollTimer;
TQWidget *m_mouseScrollIndicator;
#ifndef NO_SMOOTH_SCROLL_HACK
TQTimer timer2;
int dx;
int dy;
// Step size * 16 and residual to avoid huge difference between 1px/step and 2px/step
int ddx;
int ddy;
int rdx;
int rdy;
bool scrolling;
#endif
};
#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 TQPoint &scrollOfs,
const TQPoint &p, TQRect &r, TQString &s)
{
HTMLMapElementImpl* map;
if (img && img->getDocument()->isHTMLDocument() &&
(map = static_cast<HTMLDocumentImpl*>(img->getDocument())->getMap(img->imageMap()))) {
RenderObject::NodeInfo info(true, false);
RenderObject *rend = img->renderer();
int ax, ay;
if (!rend || !rend->absolutePosition(ax, ay))
return false;
// we're a client side image map
bool inside = map->mapMouseEvent(p.x() - ax + scrollOfs.x(),
p.y() - ay + scrollOfs.y(), rend->contentWidth(),
rend->contentHeight(), info);
if (inside && info.URLElement()) {
HTMLAreaElementImpl *area = static_cast<HTMLAreaElementImpl *>(info.URLElement());
Q_ASSERT(area->id() == ID_AREA);
s = area->getAttribute(ATTR_TITLE).string();
TQRegion reg = area->cachedRegion();
if (!s.isEmpty() && !reg.isEmpty()) {
r = reg.boundingRect();
r.moveBy(ax, ay);
return true;
}
}
}
return false;
}
void KHTMLToolTip::maybeTip(const TQPoint& p)
{
DOM::NodeImpl *node = m_viewprivate->underMouseNonShared;
TQRect region;
while ( node ) {
if ( node->isElementNode() ) {
DOM::ElementImpl *e = static_cast<DOM::ElementImpl*>( node );
TQRect r;
TQString s;
bool found = false;
// for images, check if it is part of a client-side image map,
// and query the <area>s' title attributes, too
if (e->id() == ID_IMG && !e->getAttribute( ATTR_USEMAP ).isEmpty()) {
found = findImageMapRect(static_cast<HTMLImageElementImpl *>(e),
m_view->viewportToContents(TQPoint(0, 0)), p, r, s);
}
if (!found) {
s = e->getAttribute( ATTR_TITLE ).string();
r = node->getRect();
}
region |= TQRect( m_view->contentsToViewport( r.topLeft() ), r.size() );
if ( !s.isEmpty() ) {
tip( region, TQStyleSheet::convertFromPlainText( s, TQStyleSheetItem::WhiteSpaceNormal ) );
break;
}
}
node = node->parentNode();
}
}
#endif
KHTMLView::KHTMLView( KHTMLPart *part, TQWidget *parent, const char *name)
: TQScrollView( parent, name, (WFlags)(WResizeNoErase | WRepaintNoErase) )
{
m_medium = "screen";
m_part = part;
d = new KHTMLViewPrivate;
TQScrollView::setVScrollBarMode(d->vmode);
TQScrollView::setHScrollBarMode(d->hmode);
connect(kapp, TQT_SIGNAL(kdisplayPaletteChanged()), this, TQT_SLOT(slotPaletteChanged()));
connect(this, TQT_SIGNAL(contentsMoving(int, int)), this, TQT_SLOT(slotScrollBarMoved()));
// initialize QScrollView
enableClipper(true);
// hack to get unclipped painting on the viewport.
static_cast<KHTMLView *>(TQT_TQWIDGET(viewport()))->setWFlags(WPaintUnclipped);
setResizePolicy(Manual);
viewport()->setMouseTracking(true);
viewport()->setBackgroundMode(NoBackground);
KImageIO::registerFormats();
#ifndef QT_NO_TOOLTIP
d->tooltip = new KHTMLToolTip( this, d );
#endif
#ifndef KHTML_NO_TYPE_AHEAD_FIND
connect(&d->timer, TQT_SIGNAL(timeout()), this, TQT_SLOT(findTimeout()));
#endif // KHTML_NO_TYPE_AHEAD_FIND
init();
viewport()->show();
#ifndef NO_SMOOTH_SCROLL_HACK
#define timer timer2
connect(&d->timer, TQT_SIGNAL(timeout()), this, TQT_SLOT(scrollTick()));
#undef timer
#endif
}
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 TQPixmap(PAINT_BUFFER_HEIGHT, PAINT_BUFFER_HEIGHT);
if(!d->vertPaintBuffer)
d->vertPaintBuffer = new TQPixmap(10, PAINT_BUFFER_HEIGHT);
if(!d->tp) d->tp = new TQPainter();
setFocusPolicy(TQ_StrongFocus);
viewport()->setFocusProxy(this);
_marginWidth = -1; // undefined
_marginHeight = -1;
_width = 0;
_height = 0;
installEventFilter(this);
setAcceptDrops(true);
TQSize 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
#ifndef KHTML_NO_TYPE_AHEAD_FIND
if( d->typeAheadActivated )
findTimeout();
#endif
if (d->accessKeysEnabled && d->accessKeysActivated)
accessKeysTimeout();
viewport()->unsetCursor();
if ( d->cursor_icon_widget )
d->cursor_icon_widget->hide();
d->reset();
TQT_TQOBJECT(this)->killTimers();
emit cleared();
TQScrollView::setHScrollBarMode(d->hmode);
TQScrollView::setVScrollBarMode(d->vmode);
verticalScrollBar()->setEnabled( false );
horizontalScrollBar()->setEnabled( false );
}
void KHTMLView::hideEvent(TQHideEvent* e)
{
TQScrollView::hideEvent(e);
if ( m_part && m_part->xmlDocImpl() )
m_part->xmlDocImpl()->docLoader()->pauseAnimations();
}
void KHTMLView::showEvent(TQShowEvent* e)
{
TQScrollView::showEvent(e);
if ( m_part && m_part->xmlDocImpl() )
m_part->xmlDocImpl()->docLoader()->resumeAnimations();
}
void KHTMLView::resizeEvent (TQResizeEvent* e)
{
int dw = e->oldSize().width() - e->size().width();
int dh = e->oldSize().height() - e->size().height();
// if we are shrinking the view, don't allow the content to overflow
// before the layout occurs - we don't know if we need scrollbars yet
dw = dw>0 ? kMax(0, contentsWidth()-dw) : contentsWidth();
dh = dh>0 ? kMax(0, contentsHeight()-dh) : contentsHeight();
resizeContents(dw, dh);
TQScrollView::resizeEvent(e);
if ( m_part && m_part->xmlDocImpl() )
m_part->xmlDocImpl()->dispatchWindowEvent( EventImpl::RESIZE_EVENT, false, false );
}
void KHTMLView::viewportResizeEvent (TQResizeEvent* e)
{
TQScrollView::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(), TQEvent::Paint);
}
// this is to get rid of a compiler virtual overload mismatch warning. do not remove
void KHTMLView::drawContents( TQPainter*)
{
}
void KHTMLView::drawContents( TQPainter *p, int ex, int ey, int ew, int eh )
{
#ifdef DEBUG_PIXEL
if ( d->timer.elapsed() > 5000 ) {
qDebug( "drawed %d pixels in %d repaints the last %d milliseconds",
d->pixelbooth, d->repaintbooth, d->timer.elapsed() );
d->timer.restart();
d->pixelbooth = 0;
d->repaintbooth = 0;
}
d->pixelbooth += ew*eh;
d->repaintbooth++;
#endif
//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, tqpalette().active().brush(TQColorGroup::Base));
return;
} else if ( d->complete && static_cast<RenderCanvas*>(m_part->xmlDocImpl()->renderer())->needsLayout() ) {
// an external update request happens while we have a layout scheduled
unscheduleRelayout();
layout();
}
if (d->painting) {
kdDebug( 6000 ) << "WARNING: drawContents reentered! " << endl;
return;
}
d->painting = true;
TQPoint pt = contentsToViewport(TQPoint(ex, ey));
TQRegion cr = TQRect(pt.x(), pt.y(), ew, eh);
// kdDebug(6000) << "clip rect: " << TQRect(pt.x(), pt.y(), ew, eh) << endl;
for (TQPtrDictIterator<TQWidget> it(d->visibleWidgets); it.current(); ++it) {
TQWidget *w = it.current();
RenderWidget* rw = static_cast<RenderWidget*>( it.currentKey() );
if (w && rw && !rw->isKHTMLWidget()) {
int x, y;
rw->absolutePosition(x, y);
contentsToViewport(x, y, x, y);
int pbx = rw->borderLeft()+rw->paddingLeft();
int pby = rw->borderTop()+rw->paddingTop();
TQRect g = TQRect(x+pbx, y+pby,
rw->width()-pbx-rw->borderRight()-rw->paddingRight(),
rw->height()-pby-rw->borderBottom()-rw->paddingBottom());
if ( !rw->isFrame() && ((g.top() > pt.y()+eh) || (g.bottom() <= pt.y()) ||
(g.right() <= pt.x()) || (g.left() > pt.x()+ew) ))
continue;
RenderLayer* rl = rw->needsMask() ? rw->enclosingStackingContext() : 0;
TQRegion mask = rl ? rl->getMask() : TQRegion();
if (!mask.isNull()) {
TQPoint o(0,0);
o = contentsToViewport(o);
mask.translate(o.x(),o.y());
mask = mask.intersect( TQRect(g.x(),g.y(),g.width(),g.height()) );
cr -= mask;
} else {
cr -= g;
}
}
}
#if 0
// this is commonly the case with framesets. we still do
// want to paint them, otherwise the widgets don't get placed.
if (cr.isEmpty()) {
d->painting = false;
return;
}
#endif
#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, tqpalette().active().brush(TQColorGroup::Base));
m_part->xmlDocImpl()->renderer()->layer()->paint(d->tp, TQRect(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, tqpalette().active().brush(TQColorGroup::Base));
m_part->xmlDocImpl()->renderer()->layer()->paint(d->tp, TQRect(ex, ey+py, ew, ph));
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();
TQRect pr(ex,ey,ew,eh);
kdDebug() << "[" << ++cnt << "]" << " clip region: " << pr << endl;
// p->setClipRegion(TQRect(0,0,ew,eh));
// p->translate(-ex, -ey);
p->fillRect(ex, ey, ew, eh, tqpalette().active().brush(TQColorGroup::Base));
m_part->xmlDocImpl()->renderer()->layer()->paint(p, pr);
#endif // DEBUG_NO_PAINT_BUFFER
#ifndef KHTML_NO_CARET
if (d->m_caretViewContext && d->m_caretViewContext->visible) {
TQRect pos(d->m_caretViewContext->x, d->m_caretViewContext->y,
d->m_caretViewContext->width, d->m_caretViewContext->height);
if (pos.intersects(TQRect(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(TQPen(magenta,0,DashDotDotLine));
// p->drawRect(dbg_paint_rect);
khtml::DrawContentsEvent event( p, ex, ey, ew, eh );
TQApplication::sendEvent( m_part, &event );
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<khtml::RenderCanvas *>(document->renderer());
if ( !canvas ) return;
d->layoutSchedulingEnabled=false;
// the reference object for the overflow property on canvas
RenderObject * ref = 0;
RenderObject* root = document->documentElement() ? document->documentElement()->renderer() : 0;
if (document->isHTMLDocument()) {
NodeImpl *body = static_cast<HTMLDocumentImpl*>(document)->body();
if(body && body->renderer() && body->id() == ID_FRAMESET) {
TQScrollView::setVScrollBarMode(AlwaysOff);
TQScrollView::setHScrollBarMode(AlwaysOff);
body->renderer()->setNeedsLayout(true);
// if (d->tooltip) {
// delete d->tooltip;
// d->tooltip = 0;
// }
}
else {
if (!d->tooltip)
d->tooltip = new KHTMLToolTip( this, d );
// only apply body's overflow to canvas if root as a visible overflow
if (root)
ref = (!body || root->style()->hidesOverflow()) ? root : body->renderer();
}
} else {
ref = root;
}
if (ref) {
if( ref->style()->overflowX() == OHIDDEN ) {
if (d->hmode == Auto) TQScrollView::setHScrollBarMode(AlwaysOff);
} else if (ref->style()->overflowX() == OSCROLL ) {
if (d->hmode == Auto) TQScrollView::setHScrollBarMode(AlwaysOn);
} else {
if (TQScrollView::hScrollBarMode() == AlwaysOff) TQScrollView::setHScrollBarMode(d->hmode);
} if ( ref->style()->overflowY() == OHIDDEN ) {
if (d->vmode == Auto) TQScrollView::setVScrollBarMode(AlwaysOff);
} else if (ref->style()->overflowY() == OSCROLL ) {
if (d->vmode == Auto) TQScrollView::setVScrollBarMode(AlwaysOn);
} else {
if (TQScrollView::vScrollBarMode() == AlwaysOff) TQScrollView::setVScrollBarMode(d->vmode);
}
}
d->needsFullRepaint = d->firstRelayout;
if (_height != visibleHeight() || _width != visibleWidth()) {;
d->needsFullRepaint = true;
_height = visibleHeight();
_width = visibleWidth();
}
//TQTime qt;
//qt.start();
canvas->layout();
emit finishedLayout();
if (d->firstRelayout) {
// make sure firstRelayout is set to false now in case this layout
// wasn't scheduled
d->firstRelayout = false;
verticalScrollBar()->setEnabled( true );
horizontalScrollBar()->setEnabled( true );
}
#if 0
ElementImpl *listitem = m_part->xmlDocImpl()->getElementById("__test_element__");
if (listitem) kdDebug(6000) << "after layout, before tqrepaint" << endl;
if (listitem) dumpLineBoxes(static_cast<RenderFlow *>(listitem->renderer()));
#endif
#ifndef KHTML_NO_CARET
hideCaret();
if ((m_part->isCaretMode() || m_part->isEditable())
&& !d->complete && d->m_caretViewContext
&& !d->m_caretViewContext->caretMoved) {
initCaret();
} else {
recalcAndStoreCaretPos();
showCaret();
}/*end if*/
#endif
if (d->accessKeysEnabled && d->accessKeysActivated) {
emit hideAccessKeys();
displayAccessKeys();
}
//kdDebug( 6000 ) << "TIME: layout() dt=" << qt.elapsed() << endl;
}
else
_width = visibleWidth();
killTimer(d->layoutTimerId);
d->layoutTimerId = 0;
d->layoutSchedulingEnabled=true;
}
void KHTMLView::closeChildDialogs()
{
TQObjectList *dlgs = queryList(TQDIALOG_OBJECT_NAME_STRING);
for (TQObject *dlg = dlgs->first(); dlg; dlg = dlgs->next())
{
KDialogBase* dlgbase = dynamic_cast<KDialogBase *>( dlg );
if ( dlgbase ) {
if ( dlgbase->testWFlags( WShowModal ) ) {
kdDebug(6000) << "closeChildDialogs: closing dialog " << dlgbase << endl;
// close() ends up calling TQButton::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! " << TQT_TQWIDGET(dlg) << endl;
TQT_TQWIDGET(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( TQCloseEvent* ev )
{
closeChildDialogs();
TQScrollView::closeEvent( ev );
}
//
// Event Handling
//
/////////////////
void KHTMLView::viewportMousePressEvent( TQMouseEvent *_mouse )
{
if (!m_part->xmlDocImpl()) return;
if (d->possibleTripleClick && ( _mouse->button() & Qt::MouseButtonMask ) == Qt::LeftButton)
{
viewportMouseDoubleClickEvent( _mouse ); // it handles triple clicks too
return;
}
int xm, ym;
viewportToContents(_mouse->x(), _mouse->y(), xm, ym);
//kdDebug( 6000 ) << "mousePressEvent: viewport=("<<_mouse->x()<<"/"<<_mouse->y()<<"), contents=(" << xm << "/" << ym << ")\n";
d->isDoubleClick = false;
DOM::NodeImpl::MouseEvent mev( _mouse->stateAfter(), DOM::NodeImpl::MousePress );
m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev );
//kdDebug(6000) << "innerNode="<<mev.innerNode.nodeName().string()<<endl;
if ( (_mouse->button() == Qt::MidButton) &&
!m_part->d->m_bOpenMiddleClick && !d->m_mouseScrollTimer &&
mev.url.isNull() && (mev.innerNode.elementId() != ID_INPUT) ) {
TQPoint point = mapFromGlobal( _mouse->globalPos() );
d->m_mouseScroll_byX = 0;
d->m_mouseScroll_byY = 0;
d->m_mouseScrollTimer = new TQTimer( this );
connect( d->m_mouseScrollTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotMouseScrollTimer()) );
if ( !d->m_mouseScrollIndicator ) {
TQPixmap pixmap, icon;
pixmap.resize( 48, 48 );
pixmap.fill( TQColor( tqRgba( 127, 127, 127, 127 ) ) );
TQPainter p( &pixmap );
icon = KGlobal::iconLoader()->loadIcon( "1uparrow", KIcon::Small );
p.drawPixmap( 16, 0, icon );
icon = KGlobal::iconLoader()->loadIcon( "1leftarrow", KIcon::Small );
p.drawPixmap( 0, 16, icon );
icon = KGlobal::iconLoader()->loadIcon( "1downarrow", KIcon::Small );
p.drawPixmap( 16, 32,icon );
icon = KGlobal::iconLoader()->loadIcon( "1rightarrow", KIcon::Small );
p.drawPixmap( 32, 16, icon );
p.drawEllipse( 23, 23, 2, 2 );
d->m_mouseScrollIndicator = new TQWidget( this, 0 );
d->m_mouseScrollIndicator->setFixedSize( 48, 48 );
d->m_mouseScrollIndicator->setPaletteBackgroundPixmap( pixmap );
}
d->m_mouseScrollIndicator->move( point.x()-24, point.y()-24 );
bool hasHorBar = visibleWidth() < contentsWidth();
bool hasVerBar = visibleHeight() < contentsHeight();
KConfig *config = KGlobal::config();
KConfigGroupSaver saver( config, "HTML Settings" );
if ( config->readBoolEntry( "ShowMouseScrollIndicator", true ) ) {
d->m_mouseScrollIndicator->show();
d->m_mouseScrollIndicator->unsetCursor();
TQBitmap mask = d->m_mouseScrollIndicator->paletteBackgroundPixmap()->createHeuristicMask( true );
if ( hasHorBar && !hasVerBar ) {
TQBitmap bm( 16, 16, true );
bitBlt( &mask, 16, 0, &bm, 0, 0, -1, -1 );
bitBlt( &mask, 16, 32, &bm, 0, 0, -1, -1 );
d->m_mouseScrollIndicator->setCursor( KCursor::SizeHorCursor );
}
else if ( !hasHorBar && hasVerBar ) {
TQBitmap bm( 16, 16, true );
bitBlt( &mask, 0, 16, &bm, 0, 0, -1, -1 );
bitBlt( &mask, 32, 16, &bm, 0, 0, -1, -1 );
d->m_mouseScrollIndicator->setCursor( KCursor::SizeVerCursor );
}
else
d->m_mouseScrollIndicator->setCursor( KCursor::SizeAllCursor );
d->m_mouseScrollIndicator->setMask( mask );
}
else {
if ( hasHorBar && !hasVerBar )
viewport()->setCursor( KCursor::SizeHorCursor );
else if ( !hasHorBar && hasVerBar )
viewport()->setCursor( KCursor::SizeVerCursor );
else
viewport()->setCursor( KCursor::SizeAllCursor );
}
return;
}
else if ( d->m_mouseScrollTimer ) {
delete d->m_mouseScrollTimer;
d->m_mouseScrollTimer = 0;
if ( d->m_mouseScrollIndicator )
d->m_mouseScrollIndicator->hide();
}
d->clickCount = 1;
d->clickX = xm;
d->clickY = ym;
bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEDOWN_EVENT,mev.innerNode.handle(),mev.innerNonSharedNode.handle(),true,
d->clickCount,_mouse,true,DOM::NodeImpl::MousePress);
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 );
TQApplication::sendEvent( m_part, &event );
// we might be deleted after this
}
}
void KHTMLView::viewportMouseDoubleClickEvent( TQMouseEvent *_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 &&
TQPoint(d->clickX-xm,d->clickY-ym).manhattanLength() <= TQApplication::startDragDistance())
d->clickCount++;
else { // shouldn't happen, if Qt has the same criterias for double clicks.
d->clickCount = 1;
d->clickX = xm;
d->clickY = ym;
}
bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEDOWN_EVENT,mev.innerNode.handle(),mev.innerNonSharedNode.handle(),true,
d->clickCount,_mouse,true,DOM::NodeImpl::MouseDblClick);
khtml::RenderObject* r = mev.innerNode.handle() ? mev.innerNode.handle()->renderer() : 0;
if (r && r->isWidget())
_mouse->ignore();
if (!swallowEvent) {
khtml::MouseDoubleClickEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode, d->clickCount );
TQApplication::sendEvent( m_part, &event );
}
d->possibleTripleClick=true;
TQTimer::singleShot(TQApplication::doubleClickInterval(),this,TQT_SLOT(tripleClickTimeout()));
}
void KHTMLView::tripleClickTimeout()
{
d->possibleTripleClick = false;
d->clickCount = 0;
}
static inline void forwardPeripheralEvent(khtml::RenderWidget* r, TQMouseEvent* me, int x, int y)
{
int absx = 0;
int absy = 0;
r->absolutePosition(absx, absy);
TQPoint p(x-absx, y-absy);
TQMouseEvent fw(me->type(), p, me->button(), me->state());
TQWidget* w = r->widget();
TQScrollView* sc = ::tqqt_cast<TQScrollView*>(w);
if (sc && !::tqqt_cast<TQListBox*>(w))
static_cast<khtml::RenderWidget::ScrollViewEventPropagator*>(sc)->sendEvent(TQT_TQEVENT(&fw));
else if(w)
static_cast<khtml::RenderWidget::EventPropagator*>(w)->sendEvent(TQT_TQEVENT(&fw));
}
static bool targetOpensNewWindow(KHTMLPart *part, TQString target)
{
if (!target.isEmpty() && (target.lower() != "_top") &&
(target.lower() != "_self") && (target.lower() != "_parent")) {
if (target.lower() == "_blank")
return true;
else {
while (part->parentPart())
part = part->parentPart();
if (!part->frameExists(target))
return true;
}
}
return false;
}
void KHTMLView::viewportMouseMoveEvent( TQMouseEvent * _mouse )
{
if ( d->m_mouseScrollTimer ) {
TQPoint 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 = TQABS(deltaX)/30.0;
double adY = TQABS(deltaY)/30.0;
d->m_mouseScroll_byX = kMax(kMin(d->m_mouseScroll_byX * int(adX*adX), SHRT_MAX), SHRT_MIN);
d->m_mouseScroll_byY = kMax(kMin(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->changeInterval( 20 );
}
}
if(!m_part->xmlDocImpl()) return;
int xm, ym;
viewportToContents(_mouse->x(), _mouse->y(), xm, ym);
DOM::NodeImpl::MouseEvent mev( _mouse->stateAfter(), DOM::NodeImpl::MouseMove );
// Do not modify :hover/:active state while mouse is pressed.
m_part->xmlDocImpl()->prepareMouseEvent( _mouse->state() & Qt::MouseButtonMask /*readonly ?*/, 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(),mev.innerNonSharedNode.handle(),false,
0,_mouse,true,DOM::NodeImpl::MouseMove);
if (d->clickCount > 0 &&
TQPoint(d->clickX-xm,d->clickY-ym).manhattanLength() > TQApplication::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<khtml::RenderWidget*>(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;
TQCursor c;
bool mailtoCursor = false;
bool newWindowCursor = false;
switch ( style ? style->cursor() : CURSOR_AUTO) {
case CURSOR_AUTO:
if ( r && r->isText() )
c = KCursor::ibeamCursor();
if ( mev.url.length() && m_part->settings()->changeCursor() ) {
c = m_part->urlCursor();
if (mev.url.string().startsWith("mailto:") && mev.url.string().find('@')>0)
mailtoCursor = true;
else
newWindowCursor = targetOpensNewWindow( m_part, mev.target.string() );
}
if (r && r->isFrameSet() && !static_cast<RenderFrameSet*>(r)->noResize())
c = TQCursor(static_cast<RenderFrameSet*>(r)->cursorShape());
break;
case CURSOR_CROSS:
c = KCursor::crossCursor();
break;
case CURSOR_POINTER:
c = m_part->urlCursor();
if (mev.url.string().startsWith("mailto:") && mev.url.string().find('@')>0)
mailtoCursor = true;
else
newWindowCursor = targetOpensNewWindow( m_part, mev.target.string() );
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 ( ( mailtoCursor || newWindowCursor ) && isVisible() && hasFocus() ) {
#ifdef Q_WS_X11
TQPixmap icon_pixmap = KGlobal::iconLoader()->loadIcon( mailtoCursor ? "mail_generic" : "window_new", KIcon::Small, 0, KIcon::DefaultState, 0, true );
if (d->cursor_icon_widget) {
const TQPixmap *pm = d->cursor_icon_widget->backgroundPixmap();
if (!pm || pm->serialNumber()!=icon_pixmap.serialNumber()) {
delete d->cursor_icon_widget;
d->cursor_icon_widget = 0;
}
}
if( !d->cursor_icon_widget ) {
d->cursor_icon_widget = new TQWidget( NULL, NULL, WX11BypassWM );
XSetWindowAttributes attr;
attr.save_under = True;
XChangeWindowAttributes( qt_xdisplay(), d->cursor_icon_widget->winId(), CWSaveUnder, &attr );
d->cursor_icon_widget->resize( icon_pixmap.width(), icon_pixmap.height());
if( icon_pixmap.mask() )
d->cursor_icon_widget->setMask( *icon_pixmap.mask());
else
d->cursor_icon_widget->clearMask();
d->cursor_icon_widget->setBackgroundPixmap( icon_pixmap );
d->cursor_icon_widget->erase();
}
TQPoint c_pos = TQCursor::pos();
d->cursor_icon_widget->move( c_pos.x() + 15, c_pos.y() + 15 );
XRaiseWindow( qt_xdisplay(), d->cursor_icon_widget->winId());
TQApplication::flushX();
d->cursor_icon_widget->show();
#endif
}
else if ( d->cursor_icon_widget )
d->cursor_icon_widget->hide();
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 );
TQApplication::sendEvent( m_part, &event );
}
}
void KHTMLView::viewportMouseReleaseEvent( TQMouseEvent * _mouse )
{
bool swallowEvent = false;
int xm, ym;
viewportToContents(_mouse->x(), _mouse->y(), xm, ym);
DOM::NodeImpl::MouseEvent mev( _mouse->stateAfter(), DOM::NodeImpl::MouseRelease );
if ( m_part->xmlDocImpl() )
{
m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev );
swallowEvent = dispatchMouseEvent(EventImpl::MOUSEUP_EVENT,mev.innerNode.handle(),mev.innerNonSharedNode.handle(),true,
d->clickCount,_mouse,false,DOM::NodeImpl::MouseRelease);
if (d->clickCount > 0 &&
TQPoint(d->clickX-xm,d->clickY-ym).manhattanLength() <= TQApplication::startDragDistance()) {
TQMouseEvent me(d->isDoubleClick ? TQEvent::MouseButtonDblClick : TQEvent::MouseButtonRelease,
_mouse->pos(), _mouse->button(), _mouse->state());
dispatchMouseEvent(EventImpl::CLICK_EVENT, mev.innerNode.handle(),mev.innerNonSharedNode.handle(),true,
d->clickCount, &me, true, DOM::NodeImpl::MouseRelease);
}
DOM::NodeImpl* fn = m_part->xmlDocImpl()->focusNode();
if (fn && fn != mev.innerNode.handle() &&
fn->renderer() && fn->renderer()->isWidget() &&
_mouse->button() != Qt::MidButton) {
forwardPeripheralEvent(static_cast<khtml::RenderWidget*>(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 );
TQApplication::sendEvent( m_part, &event );
}
}
// returns true if event should be swallowed
bool KHTMLView::dispatchKeyEvent( TQKeyEvent *_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() == TQEvent::KeyPress )
{
if( !_ke->isAutoRepeat())
{
bool ret = dispatchKeyEventHelper( _ke, false ); // keydown
// don't send keypress even if keydown was blocked, like IE (and unlike Mozilla)
if( !ret && dispatchKeyEventHelper( _ke, true )) // keypress
ret = true;
return ret;
}
else // autorepeat
{
bool ret = dispatchKeyEventHelper( _ke, true ); // keypress
if( !ret && d->postponed_autorepeat )
keyPressEvent( d->postponed_autorepeat );
delete d->postponed_autorepeat;
d->postponed_autorepeat = NULL;
return ret;
}
}
else // TQEvent::KeyRelease
{
// Discard postponed "autorepeat key-release" events that didn't see
// a keypress after them (e.g. due to TQAccel)
if ( d->postponed_autorepeat ) {
delete d->postponed_autorepeat;
d->postponed_autorepeat = 0;
}
if( !_ke->isAutoRepeat()) {
return dispatchKeyEventHelper( _ke, false ); // keyup
}
else
{
d->postponed_autorepeat = new TQKeyEvent( _ke->type(), _ke->key(), _ke->ascii(), _ke->state(),
_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( TQKeyEvent *_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( TQKeyEvent *_ke )
{
#ifndef KHTML_NO_TYPE_AHEAD_FIND
if(d->typeAheadActivated)
{
// type-ahead find aka find-as-you-type
if(_ke->key() == Key_BackSpace)
{
d->findString = d->findString.left(d->findString.length() - 1);
if(!d->findString.isEmpty())
{
findAhead(false);
}
else
{
findTimeout();
}
d->timer.start(3000, true);
_ke->accept();
return;
}
else if(_ke->key() == Key_Escape)
{
findTimeout();
_ke->accept();
return;
}
else if(_ke->key() == Key_Space || !TQString(_ke->text()).stripWhiteSpace().isEmpty())
{
d->findString += _ke->text();
findAhead(true);
d->timer.start(3000, true);
_ke->accept();
return;
}
}
#endif // KHTML_NO_TYPE_AHEAD_FIND
#ifndef KHTML_NO_CARET
if (m_part->isEditable() || m_part->isCaretMode()
|| (m_part->xmlDocImpl() && m_part->xmlDocImpl()->focusNode()
&& m_part->xmlDocImpl()->focusNode()->contentEditable())) {
d->caretViewContext()->keyReleasePending = true;
caretKeyPressEvent(_ke);
return;
}
#endif // KHTML_NO_CARET
// If CTRL was hit, be prepared for access keys
if (d->accessKeysEnabled && _ke->key() == Key_Control && _ke->state()==0 && !d->accessKeysActivated)
{
d->accessKeysPreActivate=true;
_ke->accept();
return;
}
if (_ke->key() == Key_Shift && _ke->state()==0)
d->scrollSuspendPreActivate=true;
// accesskey handling needs to be done before dispatching, otherwise e.g. lineedits
// may eat the event
if (d->accessKeysEnabled && d->accessKeysActivated)
{
int state = ( _ke->state() & ( ShiftButton | ControlButton | AltButton | MetaButton ));
if ( state==0 || state==ShiftButton) {
if (_ke->key() != Key_Shift) accessKeysTimeout();
handleAccessKey( _ke );
_ke->accept();
return;
}
accessKeysTimeout();
}
if ( dispatchKeyEvent( _ke )) {
// If either keydown or keypress was accepted by a widget, or canceled by JS, stop here.
_ke->accept();
return;
}
int offs = (clipper()->height() < 30) ? clipper()->height() : 30;
if (_ke->state() & TQt::ShiftButton)
switch(_ke->key())
{
case Key_Space:
scrollBy( 0, -clipper()->height() + offs );
if(d->scrollSuspended)
d->newScrollTimer(this, 0);
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->scrollTimerId || d->scrollSuspended)
scrollBy( 0, 10 * _ke->count() );
if (d->scrollTimerId)
d->newScrollTimer(this, 0);
break;
case Key_Space:
case Key_Next:
scrollBy( 0, clipper()->height() - offs );
if(d->scrollSuspended)
d->newScrollTimer(this, 0);
break;
case Key_Up:
case Key_K:
if (!d->scrollTimerId || d->scrollSuspended)
scrollBy( 0, -10 * _ke->count());
if (d->scrollTimerId)
d->newScrollTimer(this, 0);
break;
case Key_Prior:
scrollBy( 0, -clipper()->height() + offs );
if(d->scrollSuspended)
d->newScrollTimer(this, 0);
break;
case Key_Right:
case Key_L:
if (!d->scrollTimerId || d->scrollSuspended)
scrollBy( 10 * _ke->count(), 0 );
if (d->scrollTimerId)
d->newScrollTimer(this, 0);
break;
case Key_Left:
case Key_H:
if (!d->scrollTimerId || d->scrollSuspended)
scrollBy( -10 * _ke->count(), 0 );
if (d->scrollTimerId)
d->newScrollTimer(this, 0);
break;
case Key_Enter:
case Key_Return:
// ### FIXME:
// or even better to HTMLAnchorElementImpl::event()
if (m_part->xmlDocImpl()) {
NodeImpl *n = m_part->xmlDocImpl()->focusNode();
if (n)
n->setActive();
}
break;
case Key_Home:
setContentsPos( 0, 0 );
if(d->scrollSuspended)
d->newScrollTimer(this, 0);
break;
case Key_End:
setContentsPos( 0, contentsHeight() - visibleHeight() );
if(d->scrollSuspended)
d->newScrollTimer(this, 0);
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::findTimeout()
{
#ifndef KHTML_NO_TYPE_AHEAD_FIND
d->typeAheadActivated = false;
d->findString = "";
m_part->setStatusBarText(i18n("Find stopped."), KHTMLPart::BarDefaultText);
m_part->enableFindAheadActions( true );
#endif // KHTML_NO_TYPE_AHEAD_FIND
}
#ifndef KHTML_NO_TYPE_AHEAD_FIND
void KHTMLView::startFindAhead( bool linksOnly )
{
if( linksOnly )
{
d->findLinksOnly = true;
m_part->setStatusBarText(i18n("Starting -- find links as you type"),
KHTMLPart::BarDefaultText);
}
else
{
d->findLinksOnly = false;
m_part->setStatusBarText(i18n("Starting -- find text as you type"),
KHTMLPart::BarDefaultText);
}
m_part->findTextBegin();
d->typeAheadActivated = true;
// disable, so that the shortcut ( / or ' by default ) doesn't interfere
m_part->enableFindAheadActions( false );
d->timer.start(3000, true);
}
void KHTMLView::findAhead(bool increase)
{
TQString status;
if(d->findLinksOnly)
{
m_part->findText(d->findString, KHTMLPart::FindNoPopups |
KHTMLPart::FindLinksOnly, this);
if(m_part->findTextNext())
{
status = i18n("Link found: \"%1\".");
}
else
{
if(increase) KNotifyClient::beep();
status = i18n("Link not found: \"%1\".");
}
}
else
{
m_part->findText(d->findString, KHTMLPart::FindNoPopups, this);
if(m_part->findTextNext())
{
status = i18n("Text found: \"%1\".");
}
else
{
if(increase) KNotifyClient::beep();
status = i18n("Text not found: \"%1\".");
}
}
m_part->setStatusBarText(status.arg(d->findString.lower()),
KHTMLPart::BarDefaultText);
}
void KHTMLView::updateFindAheadTimeout()
{
if( d->typeAheadActivated )
d->timer.start( 3000, true );
}
#endif // KHTML_NO_TYPE_AHEAD_FIND
void KHTMLView::keyReleaseEvent(TQKeyEvent *_ke)
{
#ifndef KHTML_NO_TYPE_AHEAD_FIND
if(d->typeAheadActivated) {
_ke->accept();
return;
}
#endif
if (d->m_caretViewContext && d->m_caretViewContext->keyReleasePending) {
//caretKeyReleaseEvent(_ke);
d->m_caretViewContext->keyReleasePending = false;
return;
}
if( d->scrollSuspendPreActivate && _ke->key() != Key_Shift )
d->scrollSuspendPreActivate = false;
if( _ke->key() == Key_Shift && d->scrollSuspendPreActivate && _ke->state() == TQt::ShiftButton
&& !(KApplication::keyboardMouseState() & TQt::ShiftButton))
{
if (d->scrollTimerId)
{
d->scrollSuspended = !d->scrollSuspended;
#ifndef NO_SMOOTH_SCROLL_HACK
if( d->scrollSuspended )
stopScrolling();
#endif
}
}
if (d->accessKeysEnabled)
{
if (d->accessKeysPreActivate && _ke->key() != Key_Control)
d->accessKeysPreActivate=false;
if (d->accessKeysPreActivate && _ke->state() == TQt::ControlButton && !(KApplication::keyboardMouseState() & TQt::ControlButton))
{
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;
}
TQScrollView::keyReleaseEvent(_ke);
}
void KHTMLView::contentsContextMenuEvent ( TQContextMenuEvent * /*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);
TQPoint pos(xm-absx,ym-absy);
TQWidget *w = static_cast<RenderWidget*>(targetNode->renderer())->widget();
TQContextMenuEvent cme(_ce->reason(),pos,_ce->globalPos(),_ce->state());
setIgnoreEvents(true);
TQApplication::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())
kdDebug() << "focusNode.name: "
<< m_part->xmlDocImpl()->focusNode()->nodeName().string() << endl;
return true; // focus node found
}
// If we get here, pass tabbing control up to the next/previous child in our parent
d->pseudoFocusNode = KHTMLViewPrivate::PFNone;
if (m_part->parentPart() && m_part->parentPart()->view())
return m_part->parentPart()->view()->focusNextPrevChild(next);
return TQWidget::focusNextPrevChild(next);
}
void KHTMLView::doAutoScroll()
{
TQPoint pos = TQCursor::pos();
pos = viewport()->mapFromGlobal( pos );
int xm, ym;
viewportToContents(pos.x(), pos.y(), xm, ym);
pos = TQPoint(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
}
}
class HackWidget : public TQWidget
{
public:
inline void setNoErase() { setWFlags(getWFlags()|WRepaintNoErase); }
};
bool KHTMLView::eventFilter(TQObject *o, TQEvent *e)
{
if ( e->type() == TQEvent::AccelOverride ) {
TQKeyEvent* ke = (TQKeyEvent*) e;
//kdDebug(6200) << "TQEvent::AccelOverride" << 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;
}
}
}
}
if ( e->type() == TQEvent::Leave ) {
if ( d->cursor_icon_widget )
d->cursor_icon_widget->hide();
m_part->resetHoverText();
}
TQWidget *view = viewport();
if (TQT_BASE_OBJECT(o) == TQT_BASE_OBJECT(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() == TQEvent::ChildInserted) {
TQObject *c = TQT_TQOBJECT(TQT_TQCHILDEVENT(e)->child());
if (c->isWidgetType()) {
TQWidget *w = TQT_TQWIDGET(c);
// don't install the event filter on toplevels
if (w->parentWidget(true) == view) {
if (!strcmp(w->name(), "__khtml")) {
w->installEventFilter(this);
w->unsetCursor();
if (!::tqqt_cast<TQFrame*>(w))
w->setBackgroundMode( TQWidget::NoBackground );
static_cast<HackWidget *>(w)->setNoErase();
if (!w->childrenListObject().isEmpty()) {
TQObjectListIterator it(w->childrenListObject());
for (; it.current(); ++it) {
TQWidget *widget = ::tqqt_cast<TQWidget *>(it.current());
if (widget && !widget->isTopLevel()) {
if (!::tqqt_cast<TQFrame*>(w))
widget->setBackgroundMode( TQWidget::NoBackground );
static_cast<HackWidget *>(widget)->setNoErase();
widget->installEventFilter(this);
}
}
}
}
}
}
}
} else if (o->isWidgetType()) {
TQWidget *v = TQT_TQWIDGET(o);
TQWidget *c = v;
while (v && v != view) {
c = v;
v = v->parentWidget(true);
}
if (v && !strcmp(c->name(), "__khtml")) {
bool block = false;
TQWidget *w = TQT_TQWIDGET(o);
switch(e->type()) {
case TQEvent::Paint:
if (!allowWidgetPaintEvents) {
// eat the event. Like this we can control exactly when the widget
// get's repainted.
block = true;
int x = 0, y = 0;
TQWidget *v = w;
while (v && v != view) {
x += v->x();
y += v->y();
v = v->parentWidget();
}
viewportToContents( x, y, x, y );
TQPaintEvent *pe = TQT_TQPAINTEVENT(e);
bool asap = !d->contentsMoving && ::tqqt_cast<TQScrollView *>(c);
// TQScrollView needs fast repaints
if ( asap && !d->painting && m_part->xmlDocImpl() && m_part->xmlDocImpl()->renderer() &&
!static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer())->needsLayout() ) {
repaintContents(x + pe->rect().x(), y + pe->rect().y(),
pe->rect().width(), pe->rect().height(), true);
} else {
scheduleRepaint(x + pe->rect().x(), y + pe->rect().y(),
pe->rect().width(), pe->rect().height(), asap);
}
}
break;
case TQEvent::MouseMove:
case TQEvent::MouseButtonPress:
case TQEvent::MouseButtonRelease:
case TQEvent::MouseButtonDblClick: {
if ( (w->parentWidget() == view || ::tqqt_cast<TQScrollView*>(c)) && !::tqqt_cast<TQScrollBar *>(w)) {
TQMouseEvent *me = TQT_TQMOUSEEVENT(e);
TQPoint pt = w->mapTo( view, me->pos());
TQMouseEvent me2(me->type(), pt, me->button(), me->state());
if (e->type() == TQEvent::MouseMove)
viewportMouseMoveEvent(&me2);
else if(e->type() == TQEvent::MouseButtonPress)
viewportMousePressEvent(&me2);
else if(e->type() == TQEvent::MouseButtonRelease)
viewportMouseReleaseEvent(&me2);
else
viewportMouseDoubleClickEvent(&me2);
block = true;
}
break;
}
case TQEvent::KeyPress:
case TQEvent::KeyRelease:
if (w->parentWidget() == view && !::tqqt_cast<TQScrollBar *>(w)) {
TQKeyEvent *ke = TQT_TQKEYEVENT(e);
if (e->type() == TQEvent::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 object=" << o->className() << " event=" << e->type() << endl;
return TQScrollView::eventFilter(o, e);
}
DOM::NodeImpl *KHTMLView::nodeUnderMouse() const
{
return d->underMouse;
}
DOM::NodeImpl *KHTMLView::nonSharedNodeUnderMouse() const
{
return d->underMouseNonShared;
}
bool KHTMLView::scrollTo(const TQRect &bounds)
{
d->scrollingSelf = true; // so scroll events get ignored
int x, y, xe, ye;
x = bounds.left();
y = bounds.top();
xe = bounds.right();
ye = bounds.bottom();
//kdDebug(6000)<<"scrolling coords: x="<<x<<" y="<<y<<" width="<<xe-x<<" height="<<ye-y<<endl;
int deltax;
int deltay;
int curHeight = visibleHeight();
int curWidth = visibleWidth();
if (ye-y>curHeight-d->borderY)
ye = y + curHeight - d->borderY;
if (xe-x>curWidth-d->borderX)
xe = x + curWidth - d->borderX;
// is xpos of target left of the view's border?
if (x < contentsX() + d->borderX )
deltax = x - contentsX() - d->borderX;
// is xpos of target right of the view's right border?
else if (xe + d->borderX > contentsX() + curWidth)
deltax = xe + d->borderX - ( contentsX() + curWidth );
else
deltax = 0;
// is ypos of target above upper border?
if (y < contentsY() + d->borderY)
deltay = y - contentsY() - d->borderY;
// is ypos of target below lower border?
else if (ye + d->borderY > contentsY() + curHeight)
deltay = ye + d->borderY - ( contentsY() + curHeight );
else
deltay = 0;
int maxx = curWidth-d->borderX;
int maxy = curHeight-d->borderY;
int scrollX,scrollY;
scrollX = deltax > 0 ? (deltax > maxx ? maxx : deltax) : deltax == 0 ? 0 : (deltax>-maxx ? deltax : -maxx);
scrollY = deltay > 0 ? (deltay > maxy ? maxy : deltay) : deltay == 0 ? 0 : (deltay>-maxy ? deltay : -maxy);
if (contentsX() + scrollX < 0)
scrollX = -contentsX();
else if (contentsWidth() - visibleWidth() - contentsX() < scrollX)
scrollX = contentsWidth() - visibleWidth() - contentsX();
if (contentsY() + scrollY < 0)
scrollY = -contentsY();
else if (contentsHeight() - visibleHeight() - contentsY() < scrollY)
scrollY = contentsHeight() - visibleHeight() - contentsY();
scrollBy(scrollX, scrollY);
d->scrollingSelf = false;
if ( (abs(deltax)<=maxx) && (abs(deltay)<=maxy) )
return true;
else return false;
}
bool KHTMLView::focusNextPrevNode(bool next)
{
// Sets the focus node of the document to be the node after (or if
// next is false, before) the current focus node. Only nodes that
// are selectable (i.e. for which isFocusable() returns true) are
// taken into account, and the order used is that specified in the
// HTML spec (see DocumentImpl::nextFocusNode() and
// DocumentImpl::previousFocusNode() for details).
DocumentImpl *doc = m_part->xmlDocImpl();
NodeImpl *oldFocusNode = doc->focusNode();
// See whether we're in the middle of detach. If so, we want to
// clear focus... The document code will be careful to not
// emit events in that case..
if (oldFocusNode && oldFocusNode->renderer() &&
!oldFocusNode->renderer()->parent()) {
doc->setFocusNode(0);
return true;
}
#if 1
// If the user has scrolled the document, then instead of picking
// the next focusable node in the document, use the first one that
// is within the visible area (if possible).
if (d->scrollBarMoved)
{
NodeImpl *toFocus;
if (next)
toFocus = doc->nextFocusNode(oldFocusNode);
else
toFocus = doc->previousFocusNode(oldFocusNode);
if (!toFocus && oldFocusNode)
if (next)
toFocus = doc->nextFocusNode(NULL);
else
toFocus = doc->previousFocusNode(NULL);
while (toFocus && toFocus != oldFocusNode)
{
TQRect focusNodeRect = toFocus->getRect();
if ((focusNodeRect.left() > contentsX()) && (focusNodeRect.right() < contentsX() + visibleWidth()) &&
(focusNodeRect.top() > contentsY()) && (focusNodeRect.bottom() < contentsY() + visibleHeight())) {
{
TQRect r = toFocus->getRect();
ensureVisible( r.right(), r.bottom());
ensureVisible( r.left(), r.top());
d->scrollBarMoved = false;
d->tabMovePending = false;
d->lastTabbingDirection = next;
d->pseudoFocusNode = KHTMLViewPrivate::PFNone;
m_part->xmlDocImpl()->setFocusNode(toFocus);
Node guard(toFocus);
if (!toFocus->hasOneRef() )
{
emit m_part->nodeActivated(Node(toFocus));
}
return true;
}
}
if (next)
toFocus = doc->nextFocusNode(toFocus);
else
toFocus = doc->previousFocusNode(toFocus);
if (!toFocus && oldFocusNode)
if (next)
toFocus = doc->nextFocusNode(NULL);
else
toFocus = doc->previousFocusNode(NULL);
}
d->scrollBarMoved = false;
}
#endif
if (!oldFocusNode && d->pseudoFocusNode == KHTMLViewPrivate::PFNone)
{
ensureVisible(contentsX(), next?0:contentsHeight());
d->scrollBarMoved = false;
d->pseudoFocusNode = next?KHTMLViewPrivate::PFTop:KHTMLViewPrivate::PFBottom;
return true;
}
NodeImpl *newFocusNode = NULL;
if (d->tabMovePending && next != d->lastTabbingDirection)
{
//kdDebug ( 6000 ) << " tab move pending and tabbing direction changed!\n";
newFocusNode = oldFocusNode;
}
else if (next)
{
if (oldFocusNode || d->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(TQRect(contentsX()+visibleWidth()/2,contentsHeight()-d->borderY,0,0));
}
else
{
targetVisible = scrollTo(TQRect(contentsX()+visibleWidth()/2,d->borderY,0,0));
}
}
else
{
#ifndef KHTML_NO_CARET
// if it's an editable element, activate the caret
if (!m_part->isCaretMode() && !m_part->isEditable()
&& newFocusNode->contentEditable()) {
d->caretViewContext();
moveCaretTo(newFocusNode, 0L, true);
} else {
caretOff();
}
#endif // KHTML_NO_CARET
targetVisible = scrollTo(newFocusNode->getRect());
}
if (targetVisible)
{
//kdDebug ( 6000 ) << " target reached.\n";
d->tabMovePending = false;
m_part->xmlDocImpl()->setFocusNode(newFocusNode);
if (newFocusNode)
{
Node guard(newFocusNode);
if (!newFocusNode->hasOneRef() )
{
emit m_part->nodeActivated(Node(newFocusNode));
}
return true;
}
else
{
d->pseudoFocusNode = next?KHTMLViewPrivate::PFBottom:KHTMLViewPrivate::PFTop;
return false;
}
}
else
{
if (!d->tabMovePending)
d->lastTabbingDirection = next;
d->tabMovePending = true;
return true;
}
}
void KHTMLView::displayAccessKeys()
{
TQValueVector< TQChar > taken;
displayAccessKeys( NULL, this, taken, false );
displayAccessKeys( NULL, this, taken, true );
}
void KHTMLView::displayAccessKeys( KHTMLView* caller, KHTMLView* origview, TQValueVector< TQChar >& taken, bool use_fallbacks )
{
TQMap< ElementImpl*, TQChar > fallbacks;
if( use_fallbacks )
fallbacks = buildFallbackAccessKeys();
for( NodeImpl* n = m_part->xmlDocImpl(); n != NULL; n = n->traverseNextNode()) {
if( n->isElementNode()) {
ElementImpl* en = static_cast< ElementImpl* >( n );
DOMString s = en->getAttribute( ATTR_ACCESSKEY );
TQString accesskey;
if( s.length() == 1 ) {
TQChar a = s.string()[ 0 ].upper();
if( tqFind( taken.begin(), taken.end(), a ) == taken.end()) // !contains
accesskey = a;
}
if( accesskey.isNull() && fallbacks.contains( en )) {
TQChar a = fallbacks[ en ].upper();
if( tqFind( taken.begin(), taken.end(), a ) == taken.end()) // !contains
accesskey = TQString( "<qt><i>" ) + a + "</i></qt>";
}
if( !accesskey.isNull()) {
TQRect rec=en->getRect();
TQLabel *lab=new TQLabel(accesskey,viewport(),0,(WFlags)WDestructiveClose);
connect( origview, TQT_SIGNAL(hideAccessKeys()), lab, TQT_SLOT(close()) );
connect( this, TQT_SIGNAL(repaintAccessKeys()), lab, TQT_SLOT(tqrepaint()));
lab->setPalette(TQToolTip::palette());
lab->setLineWidth(2);
lab->setFrameStyle(TQFrame::Box | TQFrame::Plain);
lab->setMargin(3);
lab->adjustSize();
addChild(lab,
KMIN(rec.left()+rec.width()/2, contentsWidth() - lab->width()),
KMIN(rec.top()+rec.height()/2, contentsHeight() - lab->height()));
showChild(lab);
taken.append( accesskey[ 0 ] );
}
}
}
if( use_fallbacks )
return;
TQPtrList<KParts::ReadOnlyPart> frames = m_part->frames();
for( TQPtrListIterator<KParts::ReadOnlyPart> it( frames );
it != NULL;
++it ) {
if( !(*it)->inherits( "KHTMLPart" ))
continue;
KHTMLPart* part = static_cast< KHTMLPart* >( *it );
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 );
}
void KHTMLView::accessKeysTimeout()
{
d->accessKeysActivated=false;
d->accessKeysPreActivate = false;
m_part->setStatusBarText(TQString::null, KHTMLPart::BarOverrideText);
emit hideAccessKeys();
}
// Handling of the HTML accesskey attribute.
bool KHTMLView::handleAccessKey( const TQKeyEvent* 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
TQChar c;
if( ev->key() >= Key_A && ev->key() <= Key_Z )
c = 'A' + ev->key() - Key_A;
else if( ev->key() >= Key_0 && ev->key() <= Key_9 )
c = '0' + ev->key() - 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( TQChar c, KHTMLView* caller )
{
DocumentImpl *doc = m_part->xmlDocImpl();
if( !doc )
return false;
ElementImpl* node = doc->findAccessKeyElement( c );
if( !node ) {
TQPtrList<KParts::ReadOnlyPart> frames = m_part->frames();
for( TQPtrListIterator<KParts::ReadOnlyPart> it( frames );
it != NULL;
++it ) {
if( !(*it)->inherits( "KHTMLPart" ))
continue;
KHTMLPart* part = static_cast< KHTMLPart* >( *it );
if( part->view() && part->view() != caller
&& part->view()->focusNodeWithAccessKey( c, this ))
return true;
}
// pass up to the parent
if (m_part->parentPart() && m_part->parentPart()->view()
&& m_part->parentPart()->view() != caller
&& m_part->parentPart()->view()->focusNodeWithAccessKey( c, this ))
return true;
if( caller == NULL ) { // the active frame (where the accesskey was pressed)
TQMap< ElementImpl*, TQChar > fallbacks = buildFallbackAccessKeys();
for( TQMap< ElementImpl*, TQChar >::ConstIterator it = fallbacks.begin();
it != fallbacks.end();
++it )
if( *it == c ) {
node = it.key();
break;
}
}
if( node == NULL )
return false;
}
// Scroll the view as necessary to ensure that the new focus node is visible
#ifndef KHTML_NO_CARET
// if it's an editable element, activate the caret
if (!m_part->isCaretMode() && !m_part->isEditable()
&& node->contentEditable()) {
d->caretViewContext();
moveCaretTo(node, 0L, true);
} else {
caretOff();
}
#endif // KHTML_NO_CARET
TQRect 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<ElementImpl *>(static_cast< HTMLLabelElementImpl* >( node )->getFormElement());
if (!node) return true;
guard = node;
}
// Set focus node on the document
#ifdef USE_QT4
m_part->xmlDocImpl()->setFocusNode(node);
#else // USE_QT4
TQFocusEvent::setReason( TQFocusEvent::Shortcut );
m_part->xmlDocImpl()->setFocusNode(node);
TQFocusEvent::resetReason();
#endif // USE_QT4
if( node != NULL && node->hasOneRef()) // deleted, only held by guard
return true;
emit m_part->nodeActivated(Node(node));
if( node != NULL && node->hasOneRef())
return true;
}
switch( node->id()) {
case ID_A:
static_cast< HTMLAnchorElementImpl* >( node )->click();
break;
case ID_INPUT:
static_cast< HTMLInputElementImpl* >( node )->click();
break;
case ID_BUTTON:
static_cast< HTMLButtonElementImpl* >( node )->click();
break;
case ID_AREA:
static_cast< HTMLAreaElementImpl* >( node )->click();
break;
case ID_TEXTAREA:
break; // just focusing it is enough
case ID_LEGEND:
// TODO
break;
}
return true;
}
static TQString getElementText( NodeImpl* start, bool after )
{
TQString ret; // nextSibling(), to go after e.g. </select>
for( NodeImpl* n = after ? start->nextSibling() : start->traversePreviousNode();
n != NULL;
n = after ? n->traverseNextNode() : n->traversePreviousNode()) {
if( n->isTextNode()) {
if( after )
ret += static_cast< TextImpl* >( n )->toString().string();
else
ret.prepend( static_cast< TextImpl* >( n )->toString().string());
} else {
switch( n->id()) {
case ID_A:
case ID_FONT:
case ID_TT:
case ID_U:
case ID_B:
case ID_I:
case ID_S:
case ID_STRIKE:
case ID_BIG:
case ID_SMALL:
case ID_EM:
case ID_STRONG:
case ID_DFN:
case ID_CODE:
case ID_SAMP:
case ID_KBD:
case ID_VAR:
case ID_CITE:
case ID_ABBR:
case ID_ACRONYM:
case ID_SUB:
case ID_SUP:
case ID_SPAN:
case ID_NOBR:
case ID_WBR:
break;
case ID_TD:
if( ret.stripWhiteSpace().isEmpty())
break;
// fall through
default:
return ret.simplifyWhiteSpace();
}
}
}
return ret.simplifyWhiteSpace();
}
static TQMap< NodeImpl*, TQString > buildLabels( NodeImpl* start )
{
TQMap< NodeImpl*, TQString > ret;
for( NodeImpl* n = start;
n != NULL;
n = n->traverseNextNode()) {
if( n->id() == ID_LABEL ) {
HTMLLabelElementImpl* label = static_cast< HTMLLabelElementImpl* >( n );
NodeImpl* labelfor = label->getFormElement();
if( labelfor )
ret[ labelfor ] = label->innerText().string().simplifyWhiteSpace();
}
}
return ret;
}
namespace khtml {
struct AccessKeyData {
ElementImpl* element;
TQString text;
TQString url;
int priority; // 10(highest) - 0(lowest)
};
}
TQMap< ElementImpl*, TQChar > KHTMLView::buildFallbackAccessKeys() const
{
// build a list of all possible candidate elements that could use an accesskey
TQValueList< AccessKeyData > data;
TQMap< NodeImpl*, TQString > labels = buildLabels( m_part->xmlDocImpl());
for( NodeImpl* n = m_part->xmlDocImpl();
n != NULL;
n = n->traverseNextNode()) {
if( n->isElementNode()) {
ElementImpl* element = static_cast< ElementImpl* >( n );
if( element->getAttribute( ATTR_ACCESSKEY ).length() == 1 )
continue; // has accesskey set, ignore
if( element->renderer() == NULL )
continue; // not visible
TQString text;
TQString url;
int priority = 0;
bool ignore = false;
bool text_after = false;
bool text_before = false;
switch( element->id()) {
case ID_A:
url = khtml::parseURL(element->getAttribute(ATTR_HREF)).string();
if( url.isEmpty()) // doesn't have href, it's only an anchor
continue;
text = static_cast< HTMLElementImpl* >( element )->innerText().string().simplifyWhiteSpace();
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().simplifyWhiteSpace();
switch( static_cast< HTMLButtonElementImpl* >( element )->buttonType()) {
case HTMLButtonElementImpl::SUBMIT:
if( text.isEmpty())
text = i18n( "Submit" );
priority = 7;
break;
case HTMLButtonElementImpl::RESET:
if( text.isEmpty())
text = i18n( "Reset" );
priority = 5;
break;
default:
priority = 5;
break;
break;
}
case ID_SELECT: // these don't have accesskey attribute, but quick access may be handy
text_before = true;
text_after = true;
priority = 5;
break;
case ID_FRAME:
ignore = true;
break;
default:
ignore = !element->isFocusable();
priority = 2;
break;
}
if( ignore )
continue;
if( text.isNull() && labels.contains( element ))
text = labels[ element ];
if( text.isNull() && text_before )
text = getElementText( element, false );
if( text.isNull() && text_after )
text = getElementText( element, true );
text = text.stripWhiteSpace();
// increase priority of items which have explicitly specified accesskeys in the config
TQValueList< TQPair< TQString, TQChar > > priorities
= m_part->settings()->fallbackAccessKeysAssignments();
for( TQValueList< TQPair< TQString, TQChar > >::ConstIterator it = priorities.begin();
it != priorities.end();
++it ) {
if( text == (*it).first )
priority = 10;
}
AccessKeyData tmp = { element, text, url, priority };
data.append( tmp );
}
}
TQValueList< TQChar > keys;
for( char c = 'A'; c <= 'Z'; ++c )
keys << c;
for( char c = '0'; c <= '9'; ++c )
keys << c;
for( NodeImpl* n = m_part->xmlDocImpl();
n != NULL;
n = n->traverseNextNode()) {
if( n->isElementNode()) {
ElementImpl* en = static_cast< ElementImpl* >( n );
DOMString s = en->getAttribute( ATTR_ACCESSKEY );
if( s.length() == 1 ) {
TQChar c = s.string()[ 0 ].upper();
keys.remove( c ); // remove manually assigned accesskeys
}
}
}
TQMap< ElementImpl*, TQChar > ret;
for( int priority = 10;
priority >= 0;
--priority ) {
for( TQValueList< AccessKeyData >::Iterator it = data.begin();
it != data.end();
) {
if( (*it).priority != priority ) {
++it;
continue;
}
if( keys.isEmpty())
break;
TQString text = (*it).text;
TQChar key;
if( key.isNull() && !text.isEmpty()) {
TQValueList< TQPair< TQString, TQChar > > priorities
= m_part->settings()->fallbackAccessKeysAssignments();
for( TQValueList< TQPair< TQString, TQChar > >::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()) {
TQStringList words = TQStringList::split( ' ', text );
for( TQStringList::ConstIterator it = words.begin();
it != words.end();
++it ) {
if( keys.contains( (*it)[ 0 ].upper())) {
key = (*it)[ 0 ].upper();
break;
}
}
}
if( key.isNull() && !text.isEmpty()) {
for( unsigned int i = 0;
i < text.length();
++i ) {
if( keys.contains( text[ i ].upper())) {
key = text[ i ].upper();
break;
}
}
}
if( key.isNull())
key = keys.front();
ret[ (*it).element ] = key;
keys.remove( key );
TQString url = (*it).url;
it = data.remove( it );
// assign the same accesskey also to other elements pointing to the same url
if( !url.isEmpty() && !url.tqstartsWith( "javascript:", false )) {
for( TQValueList< AccessKeyData >::Iterator it2 = data.begin();
it2 != data.end();
) {
if( (*it2).url == url ) {
ret[ (*it2).element ] = key;
if( it == it2 )
++it;
it2 = data.remove( it2 );
} else
++it2;
}
}
}
}
return ret;
}
void KHTMLView::setMediaType( const TQString &medium )
{
m_medium = medium;
}
TQString KHTMLView::mediaType() const
{
return m_medium;
}
bool KHTMLView::pagedMode() const
{
return d->paged;
}
void KHTMLView::setWidgetVisible(RenderWidget* w, bool vis)
{
if (vis) {
d->visibleWidgets.replace(w, w->widget());
}
else
d->visibleWidgets.remove(w);
}
bool KHTMLView::needsFullRepaint() const
{
return d->needsFullRepaint;
}
void KHTMLView::print()
{
print( false );
}
void KHTMLView::print(bool quick)
{
if(!m_part->xmlDocImpl()) return;
khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer());
if(!root) return;
KPrinter *printer = new KPrinter(true, TQPrinter::ScreenResolution);
printer->addDialogPage(new KHTMLPrintSettings());
TQString docname = m_part->xmlDocImpl()->URL().prettyURL();
if ( !docname.isEmpty() )
docname = KStringHandler::csqueeze(docname, 80);
if(quick || printer->setup(this, i18n("Print %1").arg(docname))) {
viewport()->setCursor( tqwaitCursor ); // only viewport(), no TQApplication::, otherwise we get the busy cursor in tdeprint's dialogs
// set up KPrinter
printer->setFullPage(false);
printer->setCreator(TQString("KDE %1.%2.%3 HTML Library").arg(KDE_VERSION_MAJOR).arg(KDE_VERSION_MINOR).arg(KDE_VERSION_RELEASE));
printer->setDocName(docname);
TQPainter *p = new TQPainter;
p->begin( printer );
khtml::setPrintPainter( p );
m_part->xmlDocImpl()->setPaintDevice( printer );
TQString 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; }"
);
TQPaintDeviceMetrics metrics( printer );
kdDebug(6000) << "printing: physical page width = " << metrics.width()
<< " height = " << metrics.height() << endl;
root->setStaticMode(true);
root->setPagedMode(true);
root->setWidth(metrics.width());
// root->setHeight(metrics.height());
root->setPageTop(0);
root->setPageBottom(0);
d->paged = true;
m_part->xmlDocImpl()->styleSelector()->computeFontSizes(&metrics, 100);
m_part->xmlDocImpl()->updateStyleSelector();
root->setPrintImages( printer->option("app-khtml-printimages") == "true");
root->makePageBreakAvoidBlocks();
root->setNeedsLayoutAndMinMaxRecalc();
root->layout();
khtml::RenderWidget::flushWidgetResizes(); // make sure widgets have their final size
// check sizes ask for action.. (scale or clip)
bool printHeader = (printer->option("app-khtml-printheader") == "true");
int headerHeight = 0;
TQFont headerFont("Sans Serif", 8);
TQString headerLeft = KGlobal::locale()->formatDate(TQDate::currentDate(),true);
TQString headerMid = docname;
TQString 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 pageWidth = metrics.width();
int pageHeight = metrics.height();
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;
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 = 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 bottom = 0;
int page = 1;
while(top < root->docHeight()) {
if(top > 0) printer->newPage();
p->setClipRect(0, 0, pageWidth, headerHeight, TQPainter::CoordDevice);
if (printHeader)
{
int dy = p->fontMetrics().lineSpacing();
p->setPen(Qt::black);
p->setFont(headerFont);
headerRight = TQString("#%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->setClipRect(0, headerHeight, pageWidth, pageHeight, TQPainter::CoordDevice);
p->translate(0, headerHeight-top);
bottom = top+pageHeight;
root->setPageTop(top);
root->setPageBottom(bottom);
root->setPageNumber(page);
root->layer()->paint(p, TQRect(0, top, pageWidth, pageHeight));
// m_part->xmlDocImpl()->renderer()->layer()->paint(p, TQRect(0, top, pageWidth, pageHeight));
// root->tqrepaint();
// p->flush();
kdDebug(6000) << "printed: page " << page <<" bottom At = " << bottom << endl;
top = bottom;
p->resetXForm();
page++;
}
p->end();
delete p;
// and now reset the layout to the usual one...
root->setPagedMode(false);
root->setStaticMode(false);
d->paged = false;
khtml::setPrintPainter( 0 );
setMediaType( oldMediaType );
m_part->xmlDocImpl()->setPaintDevice( TQT_TQPAINTDEVICE(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<khtml::RenderCanvas *>(document->renderer());
if(!root) return;
root->style()->resetPalette();
NodeImpl *body = static_cast<HTMLDocumentImpl*>(document)->body();
if(!body) return;
body->setChanged(true);
body->recalcStyle( NodeImpl::Force );
}
void KHTMLView::paint(TQPainter *p, const TQRect &rc, int yOff, bool *more)
{
if(!m_part->xmlDocImpl()) return;
khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer());
if(!root) return;
m_part->xmlDocImpl()->setPaintDevice(p->tqdevice());
root->setPagedMode(true);
root->setStaticMode(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->setPageTop(yOff);
root->setPageBottom(yOff+height);
root->layer()->paint(p, TQRect(0, yOff, root->docWidth(), height));
if (more)
*more = yOff + height < root->docHeight();
p->restore();
root->setPagedMode(false);
root->setStaticMode(false);
m_part->xmlDocImpl()->setPaintDevice( TQT_TQPAINTDEVICE(this) );
}
void KHTMLView::useSlowRepaints()
{
d->useSlowRepaints = true;
setStaticBackground(true);
}
void KHTMLView::setVScrollBarMode ( ScrollBarMode mode )
{
#ifndef KHTML_NO_SCROLLBARS
d->vmode = mode;
TQScrollView::setVScrollBarMode(mode);
#else
Q_UNUSED( mode );
#endif
}
void KHTMLView::setHScrollBarMode ( ScrollBarMode mode )
{
#ifndef KHTML_NO_SCROLLBARS
d->hmode = mode;
TQScrollView::setHScrollBarMode(mode);
#else
Q_UNUSED( mode );
#endif
}
void KHTMLView::restoreScrollBar()
{
int ow = visibleWidth();
TQScrollView::setVScrollBarMode(d->vmode);
if (visibleWidth() != ow)
layout();
d->prevScrollbarVisible = verticalScrollBar()->isVisible();
}
TQStringList KHTMLView::formCompletionItems(const TQString &name) const
{
if (!m_part->settings()->isFormCompletionEnabled())
return TQStringList();
if (!d->formCompletions)
d->formCompletions = new KSimpleConfig(locateLocal("data", "khtml/formcompletions"));
return d->formCompletions->readListEntry(name);
}
void KHTMLView::clearCompletionHistory(const TQString& name)
{
if (!d->formCompletions)
{
d->formCompletions = new KSimpleConfig(locateLocal("data", "khtml/formcompletions"));
}
d->formCompletions->writeEntry(name, "");
d->formCompletions->sync();
}
void KHTMLView::addFormCompletionItem(const TQString &name, const TQString &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)
{
TQChar c(value[i]);
if (!c.isNumber() && c != '-' && !c.isSpace())
{
cc_number = false;
break;
}
}
if (cc_number)
return;
TQStringList 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);
}
void KHTMLView::removeFormCompletionItem(const TQString &name, const TQString &value)
{
if (!m_part->settings()->isFormCompletionEnabled())
return;
TQStringList items = formCompletionItems(name);
if (items.remove(value))
d->formCompletions->writeEntry(name, items);
}
void KHTMLView::addNonPasswordStorableSite(const TQString& host)
{
if (!d->formCompletions) {
d->formCompletions = new KSimpleConfig(locateLocal("data", "khtml/formcompletions"));
}
d->formCompletions->setGroup("NonPasswordStorableSites");
TQStringList sites = d->formCompletions->readListEntry("Sites");
sites.append(host);
d->formCompletions->writeEntry("Sites", sites);
d->formCompletions->sync();
d->formCompletions->setGroup(TQString::null);//reset
}
bool KHTMLView::nonPasswordStorableSite(const TQString& host) const
{
if (!d->formCompletions) {
d->formCompletions = new KSimpleConfig(locateLocal("data", "khtml/formcompletions"));
}
d->formCompletions->setGroup("NonPasswordStorableSites");
TQStringList sites = d->formCompletions->readListEntry("Sites");
d->formCompletions->setGroup(TQString::null);//reset
return (sites.find(host) != sites.end());
}
// returns true if event should be swallowed
bool KHTMLView::dispatchMouseEvent(int eventId, DOM::NodeImpl *targetNode,
DOM::NodeImpl *targetNodeNonShared, bool cancelable,
int detail,TQMouseEvent *_mouse, bool setUnder,
int mouseEventType)
{
// 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();
int exceptioncode = 0;
int pageX = 0;
int pageY = 0;
viewportToContents(_mouse->x(), _mouse->y(), 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->state() & ControlButton);
bool altKey = (_mouse->state() & AltButton);
bool shiftKey = (_mouse->state() & ShiftButton);
bool metaKey = (_mouse->state() & MetaButton);
// mouseout/mouseover
if (setUnder && (d->prevMouseX != pageX || d->prevMouseY != pageY)) {
// ### 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<NodeImpl::MouseEventType>(mouseEventType));
m_part->xmlDocImpl()->prepareMouseEvent( true, d->prevMouseX, d->prevMouseY, &mev );
oldUnder = mev.innerNode.handle();
if (oldUnder && oldUnder->isTextNode())
oldUnder = oldUnder->parentNode();
}
// 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,pageX, pageY,
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,pageX, pageY,
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
bool dblclick = ( eventId == EventImpl::CLICK_EVENT &&
_mouse->type() == TQEvent::MouseButtonDblClick );
MouseEventImpl *me = new MouseEventImpl(static_cast<EventImpl::EventId>(eventId),
true,cancelable,m_part->xmlDocImpl()->defaultView(),
detail,screenX,screenY,clientX,clientY,pageX, pageY,
ctrlKey,altKey,shiftKey,metaKey,
button,0, _mouse, dblclick );
me->ref();
targetNode->dispatchEvent(me,exceptioncode,true);
bool defaultHandled = me->defaultHandled();
if (defaultHandled || me->defaultPrevented())
swallowEvent = true;
me->deref();
if (eventId == EventImpl::MOUSEDOWN_EVENT) {
// Focus should be shifted on mouse down, not on a click. -dwh
// Blur current focus node when a link/button is clicked; this
// is expected by some sites that rely on onChange handlers running
// from form fields before the button click is processed.
DOM::NodeImpl* nodeImpl = targetNode;
for ( ; nodeImpl && !nodeImpl->isFocusable(); nodeImpl = nodeImpl->parentNode());
if (nodeImpl && nodeImpl->isMouseFocusable())
m_part->xmlDocImpl()->setFocusNode(nodeImpl);
else if (!nodeImpl || !nodeImpl->focused())
m_part->xmlDocImpl()->setFocusNode(0);
}
}
return swallowEvent;
}
void KHTMLView::setIgnoreWheelEvents( bool e )
{
d->ignoreWheelEvents = e;
}
#ifndef QT_NO_WHEELEVENT
void KHTMLView::viewportWheelEvent(TQWheelEvent* e)
{
if (d->accessKeysEnabled && d->accessKeysPreActivate) d->accessKeysPreActivate=false;
if ( ( e->state() & ControlButton) == ControlButton )
{
emit zoomView( - e->delta() );
e->accept();
}
else if (d->firstRelayout)
{
e->accept();
}
else if( ( (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
{
d->scrollBarMoved = true;
#ifndef NO_SMOOTH_SCROLL_HACK
scrollViewWheelEvent( e );
#else
TQScrollView::viewportWheelEvent( e );
#endif
TQMouseEvent *tempEvent = new TQMouseEvent( TQEvent::MouseMove, TQPoint(-1,-1), TQPoint(-1,-1), Qt::NoButton, e->state() );
emit viewportMouseMoveEvent ( tempEvent );
delete tempEvent;
}
}
#endif
void KHTMLView::dragEnterEvent( TQDragEnterEvent* 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() )
{
TQApplication::sendEvent(m_part->parentPart()->widget(), ev);
return;
}
TQScrollView::dragEnterEvent( ev );
}
void KHTMLView::dropEvent( TQDropEvent *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() )
{
TQApplication::sendEvent(m_part->parentPart()->widget(), ev);
return;
}
TQScrollView::dropEvent( ev );
}
void KHTMLView::focusInEvent( TQFocusEvent *e )
{
#ifndef KHTML_NO_TYPE_AHEAD_FIND
m_part->enableFindAheadActions( true );
#endif
DOM::NodeImpl* fn = m_part->xmlDocImpl() ? m_part->xmlDocImpl()->focusNode() : 0;
if (fn && fn->renderer() && fn->renderer()->isWidget() &&
(e->reason() != TQFocusEvent::Mouse) &&
static_cast<khtml::RenderWidget*>(fn->renderer())->widget())
static_cast<khtml::RenderWidget*>(fn->renderer())->widget()->setFocus();
#ifndef KHTML_NO_CARET
// Restart blink frequency timer if it has been killed, but only on
// editable nodes
if (d->m_caretViewContext &&
d->m_caretViewContext->freqTimerId == -1 &&
fn) {
if (m_part->isCaretMode()
|| m_part->isEditable()
|| (fn && fn->renderer()
&& fn->renderer()->style()->userInput()
== UI_ENABLED)) {
d->m_caretViewContext->freqTimerId = startTimer(500);
d->m_caretViewContext->visible = true;
}/*end if*/
}/*end if*/
showCaret();
#endif // KHTML_NO_CARET
TQScrollView::focusInEvent( e );
}
void KHTMLView::focusOutEvent( TQFocusEvent *e )
{
if(m_part) m_part->stopAutoScroll();
#ifndef KHTML_NO_TYPE_AHEAD_FIND
if(d->typeAheadActivated)
{
findTimeout();
}
m_part->enableFindAheadActions( false );
#endif // KHTML_NO_TYPE_AHEAD_FIND
#ifndef KHTML_NO_CARET
if (d->m_caretViewContext) {
switch (d->m_caretViewContext->displayNonFocused) {
case KHTMLPart::CaretInvisible:
hideCaret();
break;
case KHTMLPart::CaretVisible: {
killTimer(d->m_caretViewContext->freqTimerId);
d->m_caretViewContext->freqTimerId = -1;
NodeImpl *caretNode = m_part->xmlDocImpl()->focusNode();
if (!d->m_caretViewContext->visible && (m_part->isCaretMode()
|| m_part->isEditable()
|| (caretNode && caretNode->renderer()
&& caretNode->renderer()->style()->userInput()
== UI_ENABLED))) {
d->m_caretViewContext->visible = true;
showCaret(true);
}/*end if*/
break;
}
case KHTMLPart::CaretBlink:
// simply leave as is
break;
}/*end switch*/
}/*end if*/
#endif // KHTML_NO_CARET
if ( d->cursor_icon_widget )
d->cursor_icon_widget->hide();
TQScrollView::focusOutEvent( e );
}
void KHTMLView::slotScrollBarMoved()
{
if ( !d->firstRelayout && !d->complete && m_part->xmlDocImpl() &&
d->layoutSchedulingEnabled) {
// contents scroll while we are not complete: we need to check our layout *now*
khtml::RenderCanvas* root = static_cast<khtml::RenderCanvas *>( m_part->xmlDocImpl()->renderer() );
if (root && root->needsLayout()) {
unscheduleRelayout();
layout();
}
}
if (!d->scrollingSelf) {
d->scrollBarMoved = true;
d->contentsMoving = true;
// ensure quick reset of contentsMoving flag
scheduleRepaint(0, 0, 0, 0);
}
if (m_part->xmlDocImpl() && m_part->xmlDocImpl()->documentElement())
m_part->xmlDocImpl()->documentElement()->dispatchHTMLEvent(EventImpl::SCROLL_EVENT, true, false);
}
void KHTMLView::timerEvent ( TQTimerEvent *e )
{
// kdDebug() << "timer event " << e->timerId() << endl;
if ( e->timerId() == d->scrollTimerId ) {
if( d->scrollSuspended )
return;
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->dirtyLayout = true;
layout();
if (d->firstRelayout) {
d->firstRelayout = false;
verticalScrollBar()->setEnabled( true );
horizontalScrollBar()->setEnabled( true );
}
}
#ifndef KHTML_NO_CARET
else if (d->m_caretViewContext
&& e->timerId() == d->m_caretViewContext->freqTimerId) {
d->m_caretViewContext->visible = !d->m_caretViewContext->visible;
if (d->m_caretViewContext->displayed) {
updateContents(d->m_caretViewContext->x, d->m_caretViewContext->y,
d->m_caretViewContext->width,
d->m_caretViewContext->height);
}/*end if*/
// if (d->m_caretViewContext->visible) cout << "|" << flush;
// else cout << "" << flush;
return;
}
#endif
d->contentsMoving = false;
if( m_part->xmlDocImpl() ) {
DOM::DocumentImpl *document = m_part->xmlDocImpl();
khtml::RenderCanvas* root = static_cast<khtml::RenderCanvas *>(document->renderer());
if ( root && root->needsLayout() ) {
killTimer(d->repaintTimerId);
d->repaintTimerId = 0;
scheduleRelayout();
return;
}
}
setStaticBackground(d->useSlowRepaints);
// kdDebug() << "scheduled tqrepaint "<< d->repaintTimerId << endl;
killTimer(d->repaintTimerId);
d->repaintTimerId = 0;
TQRect updateRegion;
TQMemArray<TQRect> rects = d->updateRegion.tqrects();
d->updateRegion = TQRegion();
if ( rects.size() )
updateRegion = rects[0];
for ( unsigned i = 1; i < rects.size(); ++i ) {
TQRect newRegion = updateRegion.unite(rects[i]);
if (2*newRegion.height() > 3*updateRegion.height() )
{
repaintContents( updateRegion );
updateRegion = rects[i];
}
else
updateRegion = newRegion;
}
if ( !updateRegion.isNull() )
repaintContents( updateRegion );
// As widgets can only be accurately positioned during painting, every layout might
// dissociate a widget from its RenderWidget. E.g: if a RenderWidget was visible before layout, but the layout
// pushed it out of the viewport, it will not be repainted, and consequently it's assocoated widget won't be repositioned!
// Thus we need to check each supposedly 'visible' widget at the end of each layout, and remove it in case it's no more in sight.
if (d->dirtyLayout && !d->visibleWidgets.isEmpty()) {
TQWidget* w;
d->dirtyLayout = false;
TQRect visibleRect(contentsX(), contentsY(), visibleWidth(), visibleHeight());
TQPtrList<RenderWidget> toRemove;
for (TQPtrDictIterator<TQWidget> it(d->visibleWidgets); it.current(); ++it) {
int xp = 0, yp = 0;
w = it.current();
RenderWidget* rw = static_cast<RenderWidget*>( it.currentKey() );
if (!rw->absolutePosition(xp, yp) ||
!visibleRect.intersects(TQRect(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);
}
emit repaintAccessKeys();
if (d->emitCompletedAfterRepaint) {
bool full = d->emitCompletedAfterRepaint == KHTMLViewPrivate::CSFull;
d->emitCompletedAfterRepaint = KHTMLViewPrivate::CSNone;
if ( full )
emit m_part->completed();
else
emit m_part->completed(true);
}
}
void KHTMLView::scheduleRelayout(khtml::RenderObject * /*clippedObj*/)
{
if (!d->layoutSchedulingEnabled || d->layoutTimerId)
return;
d->layoutTimerId = startTimer( m_part->xmlDocImpl() && m_part->xmlDocImpl()->parsing()
? 1000 : 0 );
}
void KHTMLView::unscheduleRelayout()
{
if (!d->layoutTimerId)
return;
killTimer(d->layoutTimerId);
d->layoutTimerId = 0;
}
void KHTMLView::unscheduleRepaint()
{
if (!d->repaintTimerId)
return;
killTimer(d->repaintTimerId);
d->repaintTimerId = 0;
}
void KHTMLView::scheduleRepaint(int x, int y, int w, int h, bool asap)
{
bool parsing = !m_part->xmlDocImpl() || m_part->xmlDocImpl()->parsing();
// kdDebug() << "parsing " << parsing << endl;
// kdDebug() << "complete " << d->complete << endl;
int time = parsing ? 300 : (!asap ? ( !d->complete ? 100 : 20 ) : 0);
#ifdef DEBUG_FLICKER
TQPainter p;
p.begin( viewport() );
int vx, vy;
contentsToViewport( x, y, vx, vy );
p.fillRect( vx, vy, w, h, TQt::red );
p.end();
#endif
d->updateRegion = d->updateRegion.unite(TQRect(x,y,w,h));
if (asap && !parsing)
unscheduleRepaint();
if ( !d->repaintTimerId )
d->repaintTimerId = startTimer( time );
// kdDebug() << "starting timer " << time << endl;
}
void KHTMLView::complete( bool pendingAction )
{
// 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 );
d->emitCompletedAfterRepaint = pendingAction ?
KHTMLViewPrivate::CSActionPending : KHTMLViewPrivate::CSFull;
}
// is there a tqrepaint pending?
if (d->repaintTimerId)
{
// kdDebug() << "requesting tqrepaint now" << endl;
// do it now
killTimer(d->repaintTimerId);
d->repaintTimerId = startTimer( 20 );
d->emitCompletedAfterRepaint = pendingAction ?
KHTMLViewPrivate::CSActionPending : KHTMLViewPrivate::CSFull;
}
if (!d->emitCompletedAfterRepaint)
{
if (!pendingAction)
emit m_part->completed();
else
emit m_part->completed(true);
}
}
void KHTMLView::slotMouseScrollTimer()
{
scrollBy( d->m_mouseScroll_byX, d->m_mouseScroll_byY );
}
#ifndef KHTML_NO_CARET
// ### the dependencies on static functions are a nightmare. just be
// hacky and include the implementation here. Clean me up, please.
#include "khtml_caret.cpp"
void KHTMLView::initCaret(bool keepSelection)
{
#if DEBUG_CARETMODE > 0
kdDebug(6200) << "begin initCaret" << endl;
#endif
// save caretMoved state as moveCaretTo changes it
if (m_part->xmlDocImpl()) {
#if 0
ElementImpl *listitem = m_part->xmlDocImpl()->getElementById("__test_element__");
if (listitem) dumpLineBoxes(static_cast<RenderFlow *>(listitem->renderer()));
#endif
d->caretViewContext();
bool cmoved = d->m_caretViewContext->caretMoved;
if (m_part->d->caretNode().isNull()) {
// set to document, position will be sanitized anyway
m_part->d->caretNode() = m_part->document();
m_part->d->caretOffset() = 0L;
// This sanity check is necessary for the not so unlikely case that
// setEditable or setCaretMode is called before any render objects have
// been created.
if (!m_part->d->caretNode().handle()->renderer()) return;
}/*end if*/
// kdDebug(6200) << "d->m_selectionStart " << m_part->d->m_selectionStart.handle()
// << " d->m_selectionEnd " << m_part->d->m_selectionEnd.handle() << endl;
// ### does not tqrepaint 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;
DocumentImpl *doc = m_part->xmlDocImpl();
// ensure that embedded widgets don't lose their focus
if (!firstAncestor && doc->focusNode() && doc->focusNode()->renderer()
&& doc->focusNode()->renderer()->isWidget())
return;
// Set focus node on the document
#if DEBUG_CARETMODE > 1
kdDebug(6200) << k_funcinfo << "firstAncestor " << firstAncestor << ": "
<< (firstAncestor ? firstAncestor->nodeName().string() : TQString::null) << endl;
#endif
doc->setFocusNode(firstAncestor);
emit m_part->nodeActivated(Node(firstAncestor));
}
void KHTMLView::recalcAndStoreCaretPos(CaretBox *hintBox)
{
if (!m_part || m_part->d->caretNode().isNull()) return;
d->caretViewContext();
NodeImpl *caretNode = m_part->d->caretNode().handle();
#if DEBUG_CARETMODE > 0
kdDebug(6200) << "recalcAndStoreCaretPos: caretNode=" << caretNode << (caretNode ? " "+caretNode->nodeName().string() : TQString::null) << " r@" << caretNode->renderer() << (caretNode->renderer() && caretNode->renderer()->isText() ? " \"" + TQConstString(static_cast<RenderText *>(caretNode->renderer())->str->s, kMin(static_cast<RenderText *>(caretNode->renderer())->str->l, 15u)).string() + "\"" : TQString::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 TQFontMetrics &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: "<<d->m_caretViewContext->freqTimerId<<endl;
#endif
#if DEBUG_CARETMODE > 0
kdDebug(6200) << "caret: ofs="<<m_part->d->caretOffset()<<" "
<<" x="<<d->m_caretViewContext->x<<" y="<<d->m_caretViewContext->y
<<" h="<<d->m_caretViewContext->height<<endl;
#endif
}
void KHTMLView::caretOn()
{
if (d->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 tqrepaint, 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(CaretBox *hintBox)
{
CaretViewContext *cv = d->caretViewContext();
caretOff();
NodeImpl *caretNode = m_part->d->caretNode().handle();
// ### why is it sometimes null?
if (!caretNode || !caretNode->renderer()) return false;
ensureNodeHasFocus(caretNode);
if (m_part->isCaretMode() || m_part->isEditable()
|| caretNode->renderer()->style()->userInput() == UI_ENABLED) {
recalcAndStoreCaretPos(hintBox);
cv->origX = cv->x;
caretOn();
return true;
}/*end if*/
return false;
}
void KHTMLView::ensureCaretVisible()
{
CaretViewContext *cv = d->m_caretViewContext;
if (!cv) return;
ensureVisible(cv->x, cv->y, cv->width, cv->height);
d->scrollBarMoved = false;
}
bool KHTMLView::extendSelection(NodeImpl *oldStartSel, long oldStartOfs,
NodeImpl *oldEndSel, long oldEndOfs)
{
bool changed = false;
if (m_part->d->m_selectionStart == m_part->d->m_selectionEnd
&& m_part->d->m_startOffset == m_part->d->m_endOffset) {
changed = foldSelectionToCaret(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs);
m_part->d->m_extendAtEnd = true;
} else do {
changed = m_part->d->m_selectionStart.handle() != oldStartSel
|| m_part->d->m_startOffset != oldStartOfs
|| m_part->d->m_selectionEnd.handle() != oldEndSel
|| m_part->d->m_endOffset != oldEndOfs;
if (!changed) break;
// determine start position -- caret position is always at end.
NodeImpl *startNode;
long startOffset;
if (m_part->d->m_extendAtEnd) {
startNode = m_part->d->m_selectionStart.handle();
startOffset = m_part->d->m_startOffset;
} else {
startNode = m_part->d->m_selectionEnd.handle();
startOffset = m_part->d->m_endOffset;
m_part->d->m_selectionEnd = m_part->d->m_selectionStart;
m_part->d->m_endOffset = m_part->d->m_startOffset;
m_part->d->m_extendAtEnd = true;
}/*end if*/
bool swapNeeded = false;
if (!m_part->d->m_selectionEnd.isNull() && startNode) {
swapNeeded = RangeImpl::compareBoundaryPoints(startNode, startOffset,
m_part->d->m_selectionEnd.handle(),
m_part->d->m_endOffset) >= 0;
}/*end if*/
m_part->d->m_selectionStart = startNode;
m_part->d->m_startOffset = startOffset;
if (swapNeeded) {
m_part->xmlDocImpl()->setSelection(m_part->d->m_selectionEnd.handle(),
m_part->d->m_endOffset, m_part->d->m_selectionStart.handle(),
m_part->d->m_startOffset);
} else {
m_part->xmlDocImpl()->setSelection(m_part->d->m_selectionStart.handle(),
m_part->d->m_startOffset, m_part->d->m_selectionEnd.handle(),
m_part->d->m_endOffset);
}/*end if*/
} while(false);/*end if*/
return changed;
}
void KHTMLView::updateSelection(NodeImpl *oldStartSel, long oldStartOfs,
NodeImpl *oldEndSel, long oldEndOfs)
{
if (m_part->d->m_selectionStart == m_part->d->m_selectionEnd
&& m_part->d->m_startOffset == m_part->d->m_endOffset) {
if (foldSelectionToCaret(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs)) {
m_part->emitSelectionChanged();
}/*end if*/
m_part->d->m_extendAtEnd = true;
} else {
// check if the extending end has passed the immobile end
if (!m_part->d->m_selectionEnd.isNull() && !m_part->d->m_selectionEnd.isNull()) {
bool swapNeeded = RangeImpl::compareBoundaryPoints(
m_part->d->m_selectionStart.handle(), m_part->d->m_startOffset,
m_part->d->m_selectionEnd.handle(), m_part->d->m_endOffset) >= 0;
if (swapNeeded) {
DOM::Node tmpNode = m_part->d->m_selectionStart;
long tmpOffset = m_part->d->m_startOffset;
m_part->d->m_selectionStart = m_part->d->m_selectionEnd;
m_part->d->m_startOffset = m_part->d->m_endOffset;
m_part->d->m_selectionEnd = tmpNode;
m_part->d->m_endOffset = tmpOffset;
m_part->d->m_startBeforeEnd = true;
m_part->d->m_extendAtEnd = !m_part->d->m_extendAtEnd;
}/*end if*/
}/*end if*/
m_part->xmlDocImpl()->setSelection(m_part->d->m_selectionStart.handle(),
m_part->d->m_startOffset, m_part->d->m_selectionEnd.handle(),
m_part->d->m_endOffset);
m_part->emitSelectionChanged();
}/*end if*/
}
void KHTMLView::caretKeyPressEvent(TQKeyEvent *_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)
{
if (!node) return false;
ElementImpl *baseElem = determineBaseElement(node);
RenderFlow *base = static_cast<RenderFlow *>(baseElem ? baseElem->renderer() : 0);
if (!node) return false;
// need to find out the node's inline box. If there is none, this function
// will snap to the next node that has one. This is necessary to make the
// caret visible in any case.
CaretBoxLineDeleter cblDeleter;
// RenderBlock *cb;
long r_ofs;
CaretBoxIterator cbit;
CaretBoxLine *cbl = findCaretBoxLine(node, offset, &cblDeleter, base, r_ofs, cbit);
if(!cbl) {
kdWarning() << "KHTMLView::moveCaretTo - findCaretBoxLine() returns NULL" << endl;
return false;
}
#if DEBUG_CARETMODE > 3
if (cbl) kdDebug(6200) << cbl->information() << endl;
#endif
CaretBox *box = *cbit;
if (cbit != cbl->end() && box->object() != node->renderer()) {
if (box->object()->element()) {
mapRenderPosToDOMPos(box->object(), r_ofs, box->isOutside(),
box->isOutsideEnd(), node, offset);
//if (!outside) offset = node->minOffset();
#if DEBUG_CARETMODE > 1
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)
{
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();
ElementImpl *baseElem = determineBaseElement(caretNode);
LinearDocument ld(m_part, caretNode, offset, LeafsOnly, baseElem);
ErgonomicEditableLineIterator it(ld.current(), cv->origX);
// move count lines vertically
while (count > 0 && it != ld.end() && it != ld.preBegin()) {
count--;
if (next) ++it; else --it;
}/*wend*/
// Nothing? Then leave everything as is.
if (it == ld.end() || it == ld.preBegin()) return;
int x, absx, absy;
CaretBox *caretBox = nearestCaretBox(it, d->m_caretViewContext, x, absx, absy);
placeCaretOnLine(caretBox, x, absx, absy);
}
void KHTMLView::placeCaretOnLine(CaretBox *caretBox, int x, int absx, int absy)
{
// paranoia sanity check
if (!caretBox) return;
RenderObject *caretRender = caretBox->object();
#if DEBUG_CARETMODE > 0
kdDebug(6200) << "got valid caretBox " << caretBox << endl;
kdDebug(6200) << "xPos: " << caretBox->xPos() << " yPos: " << caretBox->yPos()
<< " width: " << caretBox->width() << " height: " << caretBox->height() << endl;
InlineTextBox *tb = static_cast<InlineTextBox *>(caretBox->inlineBox());
if (caretBox->isInlineTextBox()) { kdDebug(6200) << "contains \"" << TQString(static_cast<RenderText *>(tb->object())->str->s + tb->m_start, tb->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<RenderText *>(caretRender);
const TQFontMetrics &fm = t->metrics(caretBox->inlineBox()->m_firstLine);
caretHeight = fm.height();
yOfs = caretBox->inlineBox()->baseline() - fm.ascent();
}/*end if*/
caretOff();
// set new caret node
NodeImpl *caretNode;
long &offset = m_part->d->caretOffset();
mapRenderPosToDOMPos(caretRender, offset, caretBox->isOutside(),
caretBox->isOutsideEnd(), caretNode, offset);
// set all variables not needing special treatment
d->m_caretViewContext->y = caretBox->yPos() + yOfs;
d->m_caretViewContext->height = caretHeight;
d->m_caretViewContext->width = 1; // FIXME: regard override
int xPos = caretBox->xPos();
int caretBoxWidth = caretBox->width();
d->m_caretViewContext->x = xPos;
if (!caretBox->isOutside()) {
// before or at beginning of inline box -> place at beginning
long r_ofs = 0;
if (x <= xPos) {
r_ofs = caretBox->minOffset();
// somewhere within this block
} else if (x > xPos && x <= xPos + caretBoxWidth) {
if (isText) { // find out where exactly
r_ofs = static_cast<InlineTextBox *>(caretBox->inlineBox())
->offsetForPoint(x, d->m_caretViewContext->x);
#if DEBUG_CARETMODE > 2
kdDebug(6200) << "deviation from origX " << d->m_caretViewContext->x - x << endl;
#endif
#if 0
} else { // snap to nearest end
if (xPos + caretBoxWidth - x < x - xPos) {
d->m_caretViewContext->x = xPos + caretBoxWidth;
r_ofs = caretNode ? caretNode->maxOffset() : 1;
} else {
d->m_caretViewContext->x = xPos;
r_ofs = caretNode ? caretNode->minOffset() : 0;
}/*end if*/
#endif
}/*end if*/
} else { // after the inline box -> place at end
d->m_caretViewContext->x = xPos + caretBoxWidth;
r_ofs = caretBox->maxOffset();
}/*end if*/
offset = r_ofs;
}/*end if*/
#if DEBUG_CARETMODE > 0
kdDebug(6200) << "new offset: " << offset << endl;
#endif
m_part->d->caretNode() = caretNode;
m_part->d->caretOffset() = offset;
d->m_caretViewContext->x += absx;
d->m_caretViewContext->y += absy;
#if DEBUG_CARETMODE > 1
kdDebug(6200) << "new caret position: x " << d->m_caretViewContext->x << " y " << d->m_caretViewContext->y << " w " << d->m_caretViewContext->width << " h " << d->m_caretViewContext->height << " absx " << absx << " absy " << absy << endl;
#endif
ensureVisible(d->m_caretViewContext->x, d->m_caretViewContext->y,
d->m_caretViewContext->width, d->m_caretViewContext->height);
d->scrollBarMoved = false;
ensureNodeHasFocus(caretNode);
caretOn();
}
void KHTMLView::moveCaretToLineBoundary(bool end)
{
Node &caretNodeRef = m_part->d->caretNode();
if (caretNodeRef.isNull()) return;
NodeImpl *caretNode = caretNodeRef.handle();
// kdDebug(6200) << ": caretNode=" << caretNode << endl;
long offset = m_part->d->caretOffset();
ElementImpl *baseElem = determineBaseElement(caretNode);
LinearDocument ld(m_part, caretNode, offset, LeafsOnly, baseElem);
EditableLineIterator it = ld.current();
if (it == ld.end()) return; // should not happen, but who knows
EditableCaretBoxIterator fbit(it, end);
Q_ASSERT(fbit != (*it)->end() && fbit != (*it)->preBegin());
CaretBox *b = *fbit;
RenderObject *cb = b->containingBlock();
int absx, absy;
if (cb) cb->absolutePosition(absx,absy);
else absx = absy = 0;
int x = b->xPos() + (end && !b->isOutside() ? b->width() : 0);
d->m_caretViewContext->origX = absx + x;
placeCaretOnLine(b, x, absx, absy);
}
void KHTMLView::moveCaretToDocumentBoundary(bool end)
{
Node &caretNodeRef = m_part->d->caretNode();
if (caretNodeRef.isNull()) return;
NodeImpl *caretNode = caretNodeRef.handle();
// kdDebug(6200) << ": caretNode=" << caretNode << endl;
long offset = m_part->d->caretOffset();
ElementImpl *baseElem = determineBaseElement(caretNode);
LinearDocument ld(m_part, caretNode, offset, IndicatedFlows, baseElem);
EditableLineIterator it(end ? ld.preEnd() : ld.begin(), end);
if (it == ld.end() || it == ld.preBegin()) return; // should not happen, but who knows
EditableCaretBoxIterator fbit = it;
Q_ASSERT(fbit != (*it)->end() && fbit != (*it)->preBegin());
CaretBox *b = *fbit;
RenderObject *cb = (*it)->containingBlock();
int absx, absy;
if (cb) cb->absolutePosition(absx, absy);
else absx = absy = 0;
int x = b->xPos()/* + (end ? b->width() : 0) reactivate for rtl*/;
d->m_caretViewContext->origX = absx + x;
placeCaretOnLine(b, x, absx, absy);
}
void KHTMLView::moveCaretBy(bool next, CaretMovement cmv, int count)
{
if (!m_part) return;
Node &caretNodeRef = m_part->d->caretNode();
if (caretNodeRef.isNull()) return;
NodeImpl *caretNode = caretNodeRef.handle();
// kdDebug(6200) << ": caretNode=" << caretNode << endl;
long &offset = m_part->d->caretOffset();
ElementImpl *baseElem = determineBaseElement(caretNode);
CaretAdvancePolicy advpol = cmv != CaretByWord ? IndicatedFlows : LeafsOnly;
LinearDocument ld(m_part, caretNode, offset, advpol, baseElem);
EditableCharacterIterator it(&ld);
while (!it.isEnd() && count > 0) {
count--;
if (cmv == CaretByCharacter) {
if (next) ++it;
else --it;
} else if (cmv == CaretByWord) {
if (next) moveItToNextWord(it);
else moveItToPrevWord(it);
}/*end if*/
//kdDebug(6200) << "movecaret" << endl;
}/*wend*/
CaretBox *hintBox = 0; // make gcc uninit warning disappear
if (!it.isEnd()) {
NodeImpl *node = caretNodeRef.handle();
hintBox = it.caretBox();
//kdDebug(6200) << "hintBox = " << hintBox << endl;
//kdDebug(6200) << " outside " << hintBox->isOutside() << " outsideEnd " << hintBox->isOutsideEnd() << " r " << it.renderer() << " ofs " << it.offset() << " cb " << hintBox->containingBlock() << endl;
mapRenderPosToDOMPos(it.renderer(), it.offset(), hintBox->isOutside(),
hintBox->isOutsideEnd(), node, offset);
//kdDebug(6200) << "mapRTD" << endl;
caretNodeRef = node;
#if DEBUG_CARETMODE > 2
kdDebug(6200) << "set by valid node " << node << " " << (node?node->nodeName().string():TQString::null) << " 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(CaretBox *hintBox)
{
caretOff();
recalcAndStoreCaretPos(hintBox);
ensureVisible(d->m_caretViewContext->x, d->m_caretViewContext->y,
d->m_caretViewContext->width, d->m_caretViewContext->height);
d->m_caretViewContext->origX = d->m_caretViewContext->x;
d->scrollBarMoved = false;
#if DEBUG_CARETMODE > 3
//if (caretNode->isTextNode()) kdDebug(6200) << "text[0] = " << (int)*((TextImpl *)caretNode)->data().tqunicode() << " text :\"" << ((TextImpl *)caretNode)->data().string() << "\"" << endl;
#endif
ensureNodeHasFocus(m_part->d->caretNode().handle());
caretOn();
}
void KHTMLView::moveCaretByPage(bool next)
{
Node &caretNodeRef = m_part->d->caretNode();
if (caretNodeRef.isNull()) return;
NodeImpl *caretNode = caretNodeRef.handle();
// 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
ElementImpl *baseElem = determineBaseElement(caretNode);
LinearDocument ld(m_part, caretNode, offset, LeafsOnly, baseElem);
ErgonomicEditableLineIterator it(ld.current(), cv->origX);
moveIteratorByPage(ld, it, mindist, next);
int x, absx, absy;
CaretBox *caretBox = nearestCaretBox(it, d->m_caretViewContext, x, absx, absy);
placeCaretOnLine(caretBox, x, absx, absy);
}
void KHTMLView::moveCaretPrevWord()
{
moveCaretBy(false, CaretByWord, 1);
}
void KHTMLView::moveCaretNextWord()
{
moveCaretBy(true, CaretByWord, 1);
}
void KHTMLView::moveCaretPrevLine(int n)
{
moveCaretByLine(false, n);
}
void KHTMLView::moveCaretNextLine(int n)
{
moveCaretByLine(true, n);
}
void KHTMLView::moveCaretPrevPage()
{
moveCaretByPage(false);
}
void KHTMLView::moveCaretNextPage()
{
moveCaretByPage(true);
}
void KHTMLView::moveCaretToLineBegin()
{
moveCaretToLineBoundary(false);
}
void KHTMLView::moveCaretToLineEnd()
{
moveCaretToLineBoundary(true);
}
#endif // KHTML_NO_CARET
#ifndef NO_SMOOTH_SCROLL_HACK
#define timer timer2
// All scrolls must be completed within 240ms of last keypress
static const int SCROLL_TIME = 240;
// Each step is 20 ms == 50 frames/second
static const int SCROLL_TICK = 20;
void KHTMLView::scrollBy(int dx, int dy)
{
KConfigGroup cfg( KGlobal::config(), "KDE" );
if( !cfg.readBoolEntry( "SmoothScrolling", false )) {
TQScrollView::scrollBy( dx, dy );
return;
}
// scrolling destination
int full_dx = d->dx + dx;
int full_dy = d->dy + dy;
// scrolling speed
int ddx = 0;
int ddy = 0;
int steps = SCROLL_TIME/SCROLL_TICK;
ddx = (full_dx*16)/steps;
ddy = (full_dy*16)/steps;
// don't go under 1px/step
if (ddx > 0 && ddx < 16) ddx = 16;
if (ddy > 0 && ddy < 16) ddy = 16;
if (ddx < 0 && ddx > -16) ddx = -16;
if (ddy < 0 && ddy > -16) ddy = -16;
d->dx = full_dx;
d->dy = full_dy;
d->ddx = ddx;
d->ddy = ddy;
if (!d->scrolling) {
scrollTick();
startScrolling();
}
}
void KHTMLView::scrollTick() {
if (d->dx == 0 && d->dy == 0) {
stopScrolling();
return;
}
int tddx = d->ddx + d->rdx;
int tddy = d->ddy + d->rdy;
int ddx = tddx / 16;
int ddy = tddy / 16;
d->rdx = tddx % 16;
d->rdy = tddy % 16;
if (d->dx > 0 && ddx > d->dx) ddx = d->dx;
else
if (d->dx < 0 && ddx < d->dx) ddx = d->dx;
if (d->dy > 0 && ddy > d->dy) ddy = d->dy;
else
if (d->dy < 0 && ddy < d->dy) ddy = d->dy;
d->dx -= ddx;
d->dy -= ddy;
// TQScrollView::setContentsPos( contentsX() + ddx, contentsY() + ddy);
kapp->syncX();
TQScrollView::scrollBy(ddx, ddy);
// Unaccelerated X can get seriously overloaded by scrolling and for some reason
// will send KeyPress events only infrequently. This should help to reduce
// the load.
kapp->syncX();
}
void KHTMLView::startScrolling()
{
d->scrolling = true;
d->timer.start(SCROLL_TICK, false);
}
void KHTMLView::stopScrolling()
{
d->timer.stop();
d->dx = d->dy = 0;
d->scrolling = false;
}
// Overloaded from TQScrollView and TQScrollBar
void KHTMLView::scrollViewWheelEvent( TQWheelEvent *e )
{
int pageStep = verticalScrollBar()->pageStep();
int lineStep = verticalScrollBar()->lineStep();
int step = TQMIN( TQApplication::wheelScrollLines()*lineStep, pageStep );
if ( ( e->state() & ControlButton ) || ( e->state() & ShiftButton ) )
step = pageStep;
if(e->orientation() == Qt::Horizontal)
scrollBy(-((e->delta()*step)/120), 0);
else if(e->orientation() == Qt::Vertical)
scrollBy(0,-((e->delta()*step)/120));
e->accept();
}
#undef timer
#endif // NO_SMOOTH_SCROLL_HACK
#undef DEBUG_CARETMODE