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.
4624 lines
150 KiB
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
|
|
|
|
TDEApplication::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 ) {
|
|
tqDebug( "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, palette().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, palette().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, palette().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, palette().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 repaint" << 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( tqt_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( tqt_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
|
|
&& !(TDEApplication::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 && !(TDEApplication::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) {
|
|
//tqDebug("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(repaint()));
|
|
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.startsWith( "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(TDE_VERSION_MAJOR).arg(TDE_VERSION_MINOR).arg(TDE_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->repaint();
|
|
// 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->device());
|
|
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();
|
|
}
|
|
// tqDebug("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 repaint "<< d->repaintTimerId << endl;
|
|
killTimer(d->repaintTimerId);
|
|
d->repaintTimerId = 0;
|
|
|
|
TQRect updateRegion;
|
|
TQMemArray<TQRect> rects = d->updateRegion.rects();
|
|
|
|
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 repaint pending?
|
|
if (d->repaintTimerId)
|
|
{
|
|
// kdDebug() << "requesting repaint 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 repaint the selection on keepSelection!=false
|
|
moveCaretTo(m_part->d->caretNode().handle(), m_part->d->caretOffset(), !keepSelection);
|
|
// kdDebug(6200) << "d->m_selectionStart " << m_part->d->m_selectionStart.handle()
|
|
// << " d->m_selectionEnd " << m_part->d->m_selectionEnd.handle() << endl;
|
|
d->m_caretViewContext->caretMoved = cmoved;
|
|
}/*end if*/
|
|
#if DEBUG_CARETMODE > 0
|
|
kdDebug(6200) << "end initCaret" << endl;
|
|
#endif
|
|
}
|
|
|
|
bool KHTMLView::caretOverrides() const
|
|
{
|
|
bool cm = m_part->isCaretMode();
|
|
bool dm = m_part->isEditable();
|
|
return cm && !dm ? false
|
|
: (dm || m_part->d->caretNode().handle()->contentEditable())
|
|
&& d->editorContext()->override;
|
|
}
|
|
|
|
void KHTMLView::ensureNodeHasFocus(NodeImpl *node)
|
|
{
|
|
if (m_part->isCaretMode() || m_part->isEditable()) return;
|
|
if (node->focused()) return;
|
|
|
|
// Find first ancestor whose "user-input" is "enabled"
|
|
NodeImpl *firstAncestor = 0;
|
|
while (node) {
|
|
if (node->renderer()
|
|
&& node->renderer()->style()->userInput() != UI_ENABLED)
|
|
break;
|
|
firstAncestor = node;
|
|
node = node->parentNode();
|
|
}/*wend*/
|
|
|
|
if (!node) firstAncestor = 0;
|
|
|
|
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 repaint, otherwise the event won't be handled
|
|
// before the focus leaves the window
|
|
repaintContents(d->m_caretViewContext->x, d->m_caretViewContext->y,
|
|
d->m_caretViewContext->width,
|
|
d->m_caretViewContext->height);
|
|
d->m_caretViewContext->visible = true;
|
|
}/*end if*/
|
|
d->m_caretViewContext->displayed = false;
|
|
// kdDebug(6200) << "caret hidden" << endl;
|
|
}/*end if*/
|
|
}
|
|
|
|
int KHTMLView::caretDisplayPolicyNonFocused() const
|
|
{
|
|
if (d->m_caretViewContext)
|
|
return d->m_caretViewContext->displayNonFocused;
|
|
else
|
|
return KHTMLPart::CaretInvisible;
|
|
}
|
|
|
|
void KHTMLView::setCaretDisplayPolicyNonFocused(int policy)
|
|
{
|
|
d->caretViewContext();
|
|
// int old = d->m_caretViewContext->displayNonFocused;
|
|
d->m_caretViewContext->displayNonFocused = (KHTMLPart::CaretDisplayPolicy)policy;
|
|
|
|
// make change immediately take effect if not focused
|
|
if (!hasFocus()) {
|
|
switch (d->m_caretViewContext->displayNonFocused) {
|
|
case KHTMLPart::CaretInvisible:
|
|
hideCaret();
|
|
break;
|
|
case KHTMLPart::CaretBlink:
|
|
if (d->m_caretViewContext->freqTimerId != -1) break;
|
|
d->m_caretViewContext->freqTimerId = startTimer(500);
|
|
// fall through
|
|
case KHTMLPart::CaretVisible:
|
|
d->m_caretViewContext->displayed = true;
|
|
showCaret();
|
|
break;
|
|
}/*end switch*/
|
|
}/*end if*/
|
|
}
|
|
|
|
bool KHTMLView::placeCaret(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().unicode() << " 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
|