summaryrefslogtreecommitdiffstats
path: root/khtml/rendering
diff options
context:
space:
mode:
Diffstat (limited to 'khtml/rendering')
-rw-r--r--khtml/rendering/Makefile.am57
-rw-r--r--khtml/rendering/bidi.cpp2253
-rw-r--r--khtml/rendering/bidi.h106
-rw-r--r--khtml/rendering/break_lines.cpp126
-rw-r--r--khtml/rendering/break_lines.h163
-rw-r--r--khtml/rendering/counter_tree.cpp222
-rw-r--r--khtml/rendering/counter_tree.h114
-rw-r--r--khtml/rendering/enumerate.cpp411
-rw-r--r--khtml/rendering/enumerate.h66
-rw-r--r--khtml/rendering/font.cpp502
-rw-r--r--khtml/rendering/font.h188
-rw-r--r--khtml/rendering/img-loading.pngbin0 -> 318 bytes
-rw-r--r--khtml/rendering/loading_icon.cpp25
-rw-r--r--khtml/rendering/render_applet.cpp145
-rw-r--r--khtml/rendering/render_applet.h60
-rw-r--r--khtml/rendering/render_arena.cpp146
-rw-r--r--khtml/rendering/render_arena.h72
-rw-r--r--khtml/rendering/render_block.cpp3223
-rw-r--r--khtml/rendering/render_block.h378
-rw-r--r--khtml/rendering/render_body.cpp121
-rw-r--r--khtml/rendering/render_body.h56
-rw-r--r--khtml/rendering/render_box.cpp2325
-rw-r--r--khtml/rendering/render_box.h213
-rw-r--r--khtml/rendering/render_br.cpp79
-rw-r--r--khtml/rendering/render_br.h77
-rw-r--r--khtml/rendering/render_canvas.cpp780
-rw-r--r--khtml/rendering/render_canvas.h250
-rw-r--r--khtml/rendering/render_container.cpp597
-rw-r--r--khtml/rendering/render_container.h85
-rw-r--r--khtml/rendering/render_flow.cpp412
-rw-r--r--khtml/rendering/render_flow.h96
-rw-r--r--khtml/rendering/render_form.cpp1898
-rw-r--r--khtml/rendering/render_form.h509
-rw-r--r--khtml/rendering/render_frames.cpp1010
-rw-r--r--khtml/rendering/render_frames.h172
-rw-r--r--khtml/rendering/render_generated.cpp392
-rw-r--r--khtml/rendering/render_generated.h125
-rw-r--r--khtml/rendering/render_image.cpp604
-rw-r--r--khtml/rendering/render_image.h105
-rw-r--r--khtml/rendering/render_inline.cpp935
-rw-r--r--khtml/rendering/render_inline.h94
-rw-r--r--khtml/rendering/render_layer.cpp1830
-rw-r--r--khtml/rendering/render_layer.h342
-rw-r--r--khtml/rendering/render_line.cpp996
-rw-r--r--khtml/rendering/render_line.h310
-rw-r--r--khtml/rendering/render_list.cpp586
-rw-r--r--khtml/rendering/render_list.h140
-rw-r--r--khtml/rendering/render_object.cpp2325
-rw-r--r--khtml/rendering/render_object.h865
-rw-r--r--khtml/rendering/render_replaced.cpp939
-rw-r--r--khtml/rendering/render_replaced.h169
-rw-r--r--khtml/rendering/render_style.cpp1300
-rw-r--r--khtml/rendering/render_style.h1517
-rw-r--r--khtml/rendering/render_table.cpp3070
-rw-r--r--khtml/rendering/render_table.h524
-rw-r--r--khtml/rendering/render_text.cpp1546
-rw-r--r--khtml/rendering/render_text.h345
-rw-r--r--khtml/rendering/table_layout.cpp1193
-rw-r--r--khtml/rendering/table_layout.h112
-rw-r--r--khtml/rendering/table_layout.txt74
60 files changed, 37375 insertions, 0 deletions
diff --git a/khtml/rendering/Makefile.am b/khtml/rendering/Makefile.am
new file mode 100644
index 000000000..4389b29f8
--- /dev/null
+++ b/khtml/rendering/Makefile.am
@@ -0,0 +1,57 @@
+# This file is part of the KDE libraries
+# Copyright (C) 1997 Martin Jones (mjones@kde.org)
+# (C) 1997 Torben Weis (weis@kde.org)
+
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Library General Public License for more details.
+
+# You should have received a copy of the GNU Library General Public License
+# along with this library; see the file COPYING.LIB. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301, USA.
+
+KDE_CXXFLAGS = $(WOVERLOADED_VIRTUAL)
+
+noinst_LTLIBRARIES = libkhtmlrender.la
+libkhtmlrender_la_SOURCES = \
+ bidi.cpp break_lines.cpp render_block.cpp render_inline.cpp \
+ render_style.cpp render_object.cpp render_container.cpp render_box.cpp \
+ render_flow.cpp render_text.cpp render_arena.cpp render_layer.cpp \
+ render_image.cpp render_table.cpp table_layout.cpp \
+ render_replaced.cpp render_form.cpp render_list.cpp \
+ render_canvas.cpp render_frames.cpp render_br.cpp \
+ render_body.cpp font.cpp render_line.cpp render_generated.cpp \
+ enumerate.cpp counter_tree.cpp
+
+libkhtmlrender_la_METASOURCES = AUTO
+
+noinst_HEADERS = \
+ bidi.h break_lines.h \
+ render_arena.h render_layer.h \
+ render_style.h render_object.h render_container.h render_box.h \
+ render_flow.h render_text.h render_table.h render_replaced.h \
+ render_form.h render_list.h render_canvas.h render_frames.h \
+ render_br.h render_applet.h font.h table_layout.h render_line.h \
+ render_generated.h enumerate.h
+
+INCLUDES = -I$(top_srcdir)/kimgio -I$(top_srcdir)/kio -I$(top_srcdir)/dcop \
+ -I$(top_srcdir)/kfile -I$(top_srcdir)/khtml -I$(top_srcdir)/kutils -I$(top_srcdir) $(all_includes)
+
+SRCDOC_DEST=$(kde_htmldir)/en/kdelibs/khtml
+
+## generate lib documentation
+srcdoc:
+ $(mkinstalldirs) $(SRCDOC_DEST)
+ kdoc -H -d $(SRCDOC_DEST) kdecore -lqt
+
+## maintainer: regen loading icon
+loading-icon:
+ bin2c -sploading_icon $(srcdir)/img-loading.png > $(srcdir)/loading_icon.cpp
+
diff --git a/khtml/rendering/bidi.cpp b/khtml/rendering/bidi.cpp
new file mode 100644
index 000000000..a220641b2
--- /dev/null
+++ b/khtml/rendering/bidi.cpp
@@ -0,0 +1,2253 @@
+/**
+ * This file is part of the html renderer for KDE.
+ *
+ * Copyright (C) 2000-2003 Lars Knoll (knoll@kde.org)
+ * (C) 2003-2005 Apple Computer, Inc.
+ * (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com)
+ *
+ * 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 "rendering/bidi.h"
+#include "rendering/break_lines.h"
+#include "rendering/render_block.h"
+#include "rendering/render_text.h"
+#include "rendering/render_arena.h"
+#include "rendering/render_layer.h"
+#include "rendering/render_canvas.h"
+#include "xml/dom_docimpl.h"
+
+#include "kdebug.h"
+#include "qdatetime.h"
+#include "qfontmetrics.h"
+
+#define BIDI_DEBUG 0
+//#define DEBUG_LINEBREAKS
+//#define PAGE_DEBUG
+
+namespace khtml {
+
+
+// an iterator which goes through a BidiParagraph
+struct BidiIterator
+{
+ BidiIterator() : par(0), obj(0), pos(0) {}
+ BidiIterator(RenderBlock *_par, RenderObject *_obj, unsigned int _pos) : par(_par), obj(_obj), pos(_pos) {}
+
+ void increment( BidiState &bidi );
+
+ bool atEnd() const;
+
+ const QChar &current() const;
+ QChar::Direction direction() const;
+
+ RenderBlock *par;
+ RenderObject *obj;
+ unsigned int pos;
+};
+
+
+struct BidiStatus {
+ BidiStatus() : eor(QChar::DirON), lastStrong(QChar::DirON), last(QChar::DirON) {}
+
+ QChar::Direction eor;
+ QChar::Direction lastStrong;
+ QChar::Direction last;
+};
+
+struct BidiState {
+ BidiState() : context(0) {}
+
+ BidiIterator sor;
+ BidiIterator eor;
+ BidiIterator last;
+ BidiIterator current;
+ BidiContext *context;
+ BidiStatus status;
+};
+
+// Used to track a list of chained bidi runs.
+static BidiRun* sFirstBidiRun;
+static BidiRun* sLastBidiRun;
+static int sBidiRunCount;
+static BidiRun* sCompactFirstBidiRun;
+static BidiRun* sCompactLastBidiRun;
+static int sCompactBidiRunCount;
+static bool sBuildingCompactRuns;
+
+// Midpoint globals. The goal is not to do any allocation when dealing with
+// these midpoints, so we just keep an array around and never clear it. We track
+// the number of items and position using the two other variables.
+static QMemArray<BidiIterator> *smidpoints;
+static uint sNumMidpoints;
+static uint sCurrMidpoint;
+static bool betweenMidpoints;
+
+static bool isLineEmpty = true;
+static bool previousLineBrokeAtBR = true;
+static QChar::Direction dir;
+static bool adjustEmbedding;
+static bool emptyRun = true;
+static int numSpaces;
+
+static void embed( QChar::Direction d, BidiState &bidi );
+static void appendRun( BidiState &bidi );
+
+static int getBPMWidth(int childValue, Length cssUnit)
+{
+ if (!cssUnit.isVariable())
+ return (cssUnit.isFixed() ? cssUnit.value() : childValue);
+ return 0;
+}
+
+static int getBorderPaddingMargin(RenderObject* child, bool endOfInline)
+{
+ RenderStyle* cstyle = child->style();
+ int result = 0;
+ bool leftSide = (cstyle->direction() == LTR) ? !endOfInline : endOfInline;
+ result += getBPMWidth((leftSide ? child->marginLeft() : child->marginRight()),
+ (leftSide ? cstyle->marginLeft() :
+ cstyle->marginRight()));
+ result += getBPMWidth((leftSide ? child->paddingLeft() : child->paddingRight()),
+ (leftSide ? cstyle->paddingLeft() :
+ cstyle->paddingRight()));
+ result += leftSide ? child->borderLeft() : child->borderRight();
+ return result;
+}
+
+static int inlineWidth(RenderObject* child, bool start = true, bool end = true)
+{
+ int extraWidth = 0;
+ RenderObject* parent = child->parent();
+ while (parent->isInline() && !parent->isInlineBlockOrInlineTable()) {
+ if (start && parent->firstChild() == child)
+ extraWidth += getBorderPaddingMargin(parent, false);
+ if (end && parent->lastChild() == child)
+ extraWidth += getBorderPaddingMargin(parent, true);
+ child = parent;
+ parent = child->parent();
+ }
+ return extraWidth;
+}
+
+#ifndef NDEBUG
+static bool inBidiRunDetach;
+#endif
+
+void BidiRun::detach(RenderArena* renderArena)
+{
+#ifndef NDEBUG
+ inBidiRunDetach = true;
+#endif
+ delete this;
+#ifndef NDEBUG
+ inBidiRunDetach = false;
+#endif
+
+ // Recover the size left there for us by operator delete and free the memory.
+ renderArena->free(*(size_t *)this, this);
+}
+
+void* BidiRun::operator new(size_t sz, RenderArena* renderArena) throw()
+{
+ return renderArena->allocate(sz);
+}
+
+void BidiRun::operator delete(void* ptr, size_t sz)
+{
+ assert(inBidiRunDetach);
+
+ // Stash size where detach can find it.
+ *(size_t*)ptr = sz;
+}
+
+static void deleteBidiRuns(RenderArena* arena)
+{
+ if (!sFirstBidiRun)
+ return;
+
+ BidiRun* curr = sFirstBidiRun;
+ while (curr) {
+ BidiRun* s = curr->nextRun;
+ curr->detach(arena);
+ curr = s;
+ }
+
+ sFirstBidiRun = 0;
+ sLastBidiRun = 0;
+ sBidiRunCount = 0;
+}
+
+// ---------------------------------------------------------------------
+
+/* a small helper class used internally to resolve Bidi embedding levels.
+ Each line of text caches the embedding level at the start of the line for faster
+ relayouting
+*/
+BidiContext::BidiContext(unsigned char l, QChar::Direction e, BidiContext *p, bool o)
+ : level(l) , override(o), dir(e)
+{
+ parent = p;
+ if(p) {
+ p->ref();
+ basicDir = p->basicDir;
+ } else
+ basicDir = e;
+ count = 0;
+}
+
+BidiContext::~BidiContext()
+{
+ if(parent) parent->deref();
+}
+
+void BidiContext::ref() const
+{
+ count++;
+}
+
+void BidiContext::deref() const
+{
+ count--;
+ if(count <= 0) delete this;
+}
+
+// ---------------------------------------------------------------------
+
+inline bool operator==( const BidiIterator &it1, const BidiIterator &it2 )
+{
+ if(it1.pos != it2.pos) return false;
+ if(it1.obj != it2.obj) return false;
+ return true;
+}
+
+inline bool operator!=( const BidiIterator &it1, const BidiIterator &it2 )
+{
+ if(it1.pos != it2.pos) return true;
+ if(it1.obj != it2.obj) return true;
+ return false;
+}
+
+static inline RenderObject *Bidinext(RenderObject *par, RenderObject *current, BidiState &bidi,
+ bool skipInlines = true)
+{
+ RenderObject *next = 0;
+
+ while(current != 0)
+ {
+ //kdDebug( 6040 ) << "current = " << current << endl;
+ if (!current->isFloating() && !current->isReplaced() && !current->isPositioned()) {
+ next = current->firstChild();
+ if ( next && adjustEmbedding ) {
+ EUnicodeBidi ub = next->style()->unicodeBidi();
+ if ( ub != UBNormal && !emptyRun ) {
+ EDirection dir = next->style()->direction();
+ QChar::Direction d = ( ub == Embed ? ( dir == RTL ? QChar::DirRLE : QChar::DirLRE )
+ : ( dir == RTL ? QChar::DirRLO : QChar::DirLRO ) );
+ embed( d, bidi );
+ }
+ }
+ }
+ if (!next) {
+ while (current && current != par) {
+ next = current->nextSibling();
+ if (next) break;
+ if ( adjustEmbedding && current->style()->unicodeBidi() != UBNormal && !emptyRun ) {
+ embed( QChar::DirPDF, bidi );
+ }
+ current = current->parent();
+ }
+ }
+
+ if (!next) break;
+
+ if (next->isText() || next->isBR() || next->isFloating() || next->isReplaced() || next->isPositioned() || next->isGlyph()
+ || ((!skipInlines || !next->firstChild()) // Always return EMPTY inlines.
+ && next->isInlineFlow()))
+ break;
+ current = next;
+ }
+ return next;
+}
+
+static RenderObject *first( RenderObject *par, BidiState &bidi, bool skipInlines = true )
+{
+ if(!par->firstChild()) return 0;
+ RenderObject *o = par->firstChild();
+
+ if (o->isInlineFlow()) {
+ if (skipInlines && o->firstChild())
+ o = Bidinext( par, o, bidi, skipInlines );
+ else
+ return o; // Never skip empty inlines.
+ }
+
+ if (o && !o->isText() && !o->isBR() && !o->isReplaced() && !o->isFloating() && !o->isPositioned() && !o->isGlyph())
+ o = Bidinext( par, o, bidi, skipInlines );
+ return o;
+}
+
+inline void BidiIterator::increment (BidiState &bidi)
+{
+ if(!obj) return;
+ if(obj->isText()) {
+ pos++;
+ if(pos >= static_cast<RenderText *>(obj)->stringLength()) {
+ obj = Bidinext( par, obj, bidi );
+ pos = 0;
+ }
+ } else {
+ obj = Bidinext( par, obj, bidi );
+ pos = 0;
+ }
+}
+
+inline bool BidiIterator::atEnd() const
+{
+ if(!obj) return true;
+ return false;
+}
+
+const QChar &BidiIterator::current() const
+{
+ static QChar nonBreakingSpace(0xA0);
+
+ if (!obj || !obj->isText())
+ return nonBreakingSpace;
+
+ RenderText* text = static_cast<RenderText*>(obj);
+ if (!text->text())
+ return nonBreakingSpace;
+
+ return text->text()[pos];
+}
+
+inline QChar::Direction BidiIterator::direction() const
+{
+ if(!obj || !obj->isText() ) return QChar::DirON;
+
+ RenderText *renderTxt = static_cast<RenderText *>( obj );
+ if ( pos >= renderTxt->stringLength() )
+ return QChar::DirON;
+ return renderTxt->text()[pos].direction();
+}
+
+// -------------------------------------------------------------------------------------------------
+
+static void addRun(BidiRun* bidiRun)
+{
+ if (!sFirstBidiRun)
+ sFirstBidiRun = sLastBidiRun = bidiRun;
+ else {
+ sLastBidiRun->nextRun = bidiRun;
+ sLastBidiRun = bidiRun;
+ }
+ sBidiRunCount++;
+ bidiRun->compact = sBuildingCompactRuns;
+
+ // Compute the number of spaces in this run,
+ if (bidiRun->obj && bidiRun->obj->isText()) {
+ RenderText* text = static_cast<RenderText*>(bidiRun->obj);
+ if (text->text()) {
+ for (int i = bidiRun->start; i < bidiRun->stop; i++) {
+ const QChar c = text->text()[i];
+ if (c.category() == QChar::Separator_Space || c == '\n')
+ numSpaces++;
+ }
+ }
+ }
+}
+
+static void reverseRuns(int start, int end)
+{
+ if (start >= end)
+ return;
+
+ assert(start >= 0 && end < sBidiRunCount);
+
+ // Get the item before the start of the runs to reverse and put it in
+ // |beforeStart|. |curr| should point to the first run to reverse.
+ BidiRun* curr = sFirstBidiRun;
+ BidiRun* beforeStart = 0;
+ int i = 0;
+ while (i < start) {
+ i++;
+ beforeStart = curr;
+ curr = curr->nextRun;
+ }
+
+ BidiRun* startRun = curr;
+ while (i < end) {
+ i++;
+ curr = curr->nextRun;
+ }
+ BidiRun* endRun = curr;
+ BidiRun* afterEnd = curr->nextRun;
+
+ i = start;
+ curr = startRun;
+ BidiRun* newNext = afterEnd;
+ while (i <= end) {
+ // Do the reversal.
+ BidiRun* next = curr->nextRun;
+ curr->nextRun = newNext;
+ newNext = curr;
+ curr = next;
+ i++;
+ }
+
+ // Now hook up beforeStart and afterEnd to the newStart and newEnd.
+ if (beforeStart)
+ beforeStart->nextRun = endRun;
+ else
+ sFirstBidiRun = endRun;
+
+ startRun->nextRun = afterEnd;
+ if (!afterEnd)
+ sLastBidiRun = startRun;
+}
+
+static void chopMidpointsAt(RenderObject* obj, uint pos)
+{
+ if (!sNumMidpoints) return;
+ BidiIterator* midpoints = smidpoints->data();
+ for (uint i = 0; i < sNumMidpoints; i++) {
+ const BidiIterator& point = midpoints[i];
+ if (point.obj == obj && point.pos == pos) {
+ sNumMidpoints = i;
+ break;
+ }
+ }
+}
+
+static void checkMidpoints(BidiIterator& lBreak, BidiState &bidi)
+{
+ // Check to see if our last midpoint is a start point beyond the line break. If so,
+ // shave it off the list, and shave off a trailing space if the previous end point isn't
+ // white-space: pre.
+ if (lBreak.obj && sNumMidpoints && sNumMidpoints%2 == 0) {
+ BidiIterator* midpoints = smidpoints->data();
+ BidiIterator& endpoint = midpoints[sNumMidpoints-2];
+ const BidiIterator& startpoint = midpoints[sNumMidpoints-1];
+ BidiIterator currpoint = endpoint;
+ while (!currpoint.atEnd() && currpoint != startpoint && currpoint != lBreak)
+ currpoint.increment( bidi );
+ if (currpoint == lBreak) {
+ // We hit the line break before the start point. Shave off the start point.
+ sNumMidpoints--;
+ if (!endpoint.obj->style()->preserveWS()) {
+ if (endpoint.obj->isText()) {
+ // Don't shave a character off the endpoint if it was from a soft hyphen.
+ RenderText* textObj = static_cast<RenderText*>(endpoint.obj);
+ if (endpoint.pos+1 < textObj->length() &&
+ textObj->text()[endpoint.pos+1].unicode() == SOFT_HYPHEN)
+ return;
+ }
+ endpoint.pos--;
+ }
+ }
+ }
+}
+
+static void addMidpoint(const BidiIterator& midpoint)
+{
+ if (!smidpoints)
+ return;
+
+ if (smidpoints->size() <= sNumMidpoints)
+ smidpoints->resize(sNumMidpoints+10);
+
+ BidiIterator* midpoints = smidpoints->data();
+ midpoints[sNumMidpoints++] = midpoint;
+}
+
+static void appendRunsForObject(int start, int end, RenderObject* obj, BidiState &bidi)
+{
+ if (start > end || obj->isFloating() ||
+ (obj->isPositioned() && !obj->hasStaticX() && !obj->hasStaticY()))
+ return;
+
+ bool haveNextMidpoint = (smidpoints && sCurrMidpoint < sNumMidpoints);
+ BidiIterator nextMidpoint;
+ if (haveNextMidpoint)
+ nextMidpoint = smidpoints->at(sCurrMidpoint);
+ if (betweenMidpoints) {
+ if (!(haveNextMidpoint && nextMidpoint.obj == obj))
+ return;
+ // This is a new start point. Stop ignoring objects and
+ // adjust our start.
+ betweenMidpoints = false;
+ start = nextMidpoint.pos;
+ sCurrMidpoint++;
+ if (start < end)
+ return appendRunsForObject(start, end, obj, bidi);
+ }
+ else {
+ if (!smidpoints || !haveNextMidpoint || (obj != nextMidpoint.obj)) {
+ addRun(new (obj->renderArena()) BidiRun(start, end, obj, bidi.context, dir));
+ return;
+ }
+
+ // An end midpoint has been encountered within our object. We
+ // need to go ahead and append a run with our endpoint.
+ if (int(nextMidpoint.pos+1) <= end) {
+ betweenMidpoints = true;
+ sCurrMidpoint++;
+ if (nextMidpoint.pos != UINT_MAX) { // UINT_MAX means stop at the object and don't include any of it.
+ addRun(new (obj->renderArena())
+ BidiRun(start, nextMidpoint.pos+1, obj, bidi.context, dir));
+ return appendRunsForObject(nextMidpoint.pos+1, end, obj, bidi);
+ }
+ }
+ else
+ addRun(new (obj->renderArena()) BidiRun(start, end, obj, bidi.context, dir));
+ }
+}
+
+static void appendRun( BidiState &bidi )
+{
+ if ( emptyRun ) return;
+#if BIDI_DEBUG > 1
+ kdDebug(6041) << "appendRun: dir="<<(int)dir<<endl;
+#endif
+
+ bool b = adjustEmbedding;
+ adjustEmbedding = false;
+
+ int start = bidi.sor.pos;
+ RenderObject *obj = bidi.sor.obj;
+ while( obj && obj != bidi.eor.obj ) {
+ appendRunsForObject(start, obj->length(), obj, bidi);
+ start = 0;
+ obj = Bidinext( bidi.sor.par, obj, bidi );
+ }
+ if (obj)
+ appendRunsForObject(start, bidi.eor.pos+1, obj, bidi);
+
+ bidi.eor.increment( bidi );
+ bidi.sor = bidi.eor;
+ dir = QChar::DirON;
+ bidi.status.eor = QChar::DirON;
+ adjustEmbedding = b;
+}
+
+static void embed( QChar::Direction d, BidiState &bidi )
+{
+#if BIDI_DEBUG > 1
+ qDebug("*** embed dir=%d emptyrun=%d", d, emptyRun );
+#endif
+ bool b = adjustEmbedding ;
+ adjustEmbedding = false;
+ if ( d == QChar::DirPDF ) {
+ BidiContext *c = bidi.context->parent;
+ if (c) {
+ if ( bidi.eor != bidi.last ) {
+ appendRun( bidi );
+ bidi.eor = bidi.last;
+ }
+ appendRun( bidi );
+ emptyRun = true;
+ bidi.status.last = bidi.context->dir;
+ bidi.context->deref();
+ bidi.context = c;
+ if(bidi.context->override)
+ dir = bidi.context->dir;
+ else
+ dir = QChar::DirON;
+ bidi.status.lastStrong = bidi.context->dir;
+ }
+ } else {
+ QChar::Direction runDir;
+ if( d == QChar::DirRLE || d == QChar::DirRLO )
+ runDir = QChar::DirR;
+ else
+ runDir = QChar::DirL;
+ bool override;
+ if( d == QChar::DirLRO || d == QChar::DirRLO )
+ override = true;
+ else
+ override = false;
+
+ unsigned char level = bidi.context->level;
+ if ( runDir == QChar::DirR ) {
+ if(level%2) // we have an odd level
+ level += 2;
+ else
+ level++;
+ } else {
+ if(level%2) // we have an odd level
+ level++;
+ else
+ level += 2;
+ }
+
+ if(level < 61) {
+ if ( bidi.eor != bidi.last ) {
+ appendRun( bidi );
+ bidi.eor = bidi.last;
+ }
+ appendRun( bidi );
+ emptyRun = true;
+
+ bidi.context = new BidiContext(level, runDir, bidi.context, override);
+ bidi.context->ref();
+ dir = runDir;
+ bidi.status.last = runDir;
+ bidi.status.lastStrong = runDir;
+ bidi.status.eor = runDir;
+ }
+ }
+ adjustEmbedding = b;
+}
+
+InlineFlowBox* RenderBlock::createLineBoxes(RenderObject* obj)
+{
+ // See if we have an unconstructed line box for this object that is also
+ // the last item on the line.
+ KHTMLAssert(obj->isInlineFlow() || obj == this);
+ RenderFlow* flow = static_cast<RenderFlow*>(obj);
+
+ // Get the last box we made for this render object.
+ InlineFlowBox* box = flow->lastLineBox();
+
+ // If this box is constructed then it is from a previous line, and we need
+ // to make a new box for our line. If this box is unconstructed but it has
+ // something following it on the line, then we know we have to make a new box
+ // as well. In this situation our inline has actually been split in two on
+ // the same line (this can happen with very fancy language mixtures).
+ if (!box || box->isConstructed() || box->nextOnLine()) {
+ // We need to make a new box for this render object. Once
+ // made, we need to place it at the end of the current line.
+ InlineBox* newBox = obj->createInlineBox(false, obj == this);
+ KHTMLAssert(newBox->isInlineFlowBox());
+ box = static_cast<InlineFlowBox*>(newBox);
+ box->setFirstLineStyleBit(m_firstLine);
+
+ // We have a new box. Append it to the inline box we get by constructing our
+ // parent. If we have hit the block itself, then |box| represents the root
+ // inline box for the line, and it doesn't have to be appended to any parent
+ // inline.
+ if (obj != this) {
+ InlineFlowBox* parentBox = createLineBoxes(obj->parent());
+ parentBox->addToLine(box);
+ }
+ }
+
+ return box;
+}
+
+InlineFlowBox* RenderBlock::constructLine(const BidiIterator &/*start*/, const BidiIterator &end)
+{
+ if (!sFirstBidiRun)
+ return 0; // We had no runs. Don't make a root inline box at all. The line is empty.
+
+ InlineFlowBox* parentBox = 0;
+ for (BidiRun* r = sFirstBidiRun; r; r = r->nextRun) {
+ // Create a box for our object.
+ r->box = r->obj->createInlineBox(r->obj->isPositioned(), false);
+
+ // If we have no parent box yet, or if the run is not simply a sibling,
+ // then we need to construct inline boxes as necessary to properly enclose the
+ // run's inline box.
+ if (!parentBox || (parentBox->object() != r->obj->parent()))
+ // Create new inline boxes all the way back to the appropriate insertion point.
+ parentBox = createLineBoxes(r->obj->parent());
+
+ // Append the inline box to this line.
+ parentBox->addToLine(r->box);
+ }
+
+ // We should have a root inline box. It should be unconstructed and
+ // be the last continuation of our line list.
+ KHTMLAssert(lastLineBox() && !lastLineBox()->isConstructed());
+
+ // Set bits on our inline flow boxes that indicate which sides should
+ // paint borders/margins/padding. This knowledge will ultimately be used when
+ // we determine the horizontal positions and widths of all the inline boxes on
+ // the line.
+ RenderObject* endObject = 0;
+ bool lastLine = !end.obj;
+ if (end.obj && end.pos == 0)
+ endObject = end.obj;
+ lastLineBox()->determineSpacingForFlowBoxes(lastLine, endObject);
+
+ // Now mark the line boxes as being constructed.
+ lastLineBox()->setConstructed();
+
+ // Return the last line.
+ return lastLineBox();
+}
+
+void RenderBlock::computeHorizontalPositionsForLine(InlineFlowBox* lineBox, BidiState &bidi)
+{
+ // First determine our total width.
+ int totWidth = lineBox->getFlowSpacingWidth();
+ BidiRun* r = 0;
+ for (r = sFirstBidiRun; r; r = r->nextRun) {
+ if (r->obj->isPositioned())
+ continue; // Positioned objects are only participating to figure out their
+ // correct static x position. They have no effect on the width.
+ if (r->obj->isText())
+ r->box->setWidth(static_cast<RenderText *>(r->obj)->width(r->start, r->stop-r->start, m_firstLine));
+ else if (!r->obj->isInlineFlow()) {
+ r->obj->calcWidth();
+ r->box->setWidth(r->obj->width());
+ totWidth += r->obj->marginLeft() + r->obj->marginRight();
+ }
+ totWidth += r->box->width();
+ }
+
+ // Armed with the total width of the line (without justification),
+ // we now examine our text-align property in order to determine where to position the
+ // objects horizontally. The total width of the line can be increased if we end up
+ // justifying text.
+ int x = leftOffset(m_height);
+ int availableWidth = lineWidth(m_height);
+ switch(style()->textAlign()) {
+ case LEFT:
+ case KHTML_LEFT:
+ numSpaces = 0;
+ break;
+ case JUSTIFY:
+ if (numSpaces != 0 && !bidi.current.atEnd() && !bidi.current.obj->isBR() )
+ break;
+ // fall through
+ case TAAUTO:
+ numSpaces = 0;
+ // for right to left fall through to right aligned
+ if (bidi.context->basicDir == QChar::DirL)
+ break;
+ case RIGHT:
+ case KHTML_RIGHT:
+ x += availableWidth - totWidth;
+ numSpaces = 0;
+ break;
+ case CENTER:
+ case KHTML_CENTER:
+ int xd = (availableWidth - totWidth)/2;
+ x += xd >0 ? xd : 0;
+ numSpaces = 0;
+ break;
+ }
+
+ if (numSpaces > 0) {
+ for (r = sFirstBidiRun; r; r = r->nextRun) {
+ int spaceAdd = 0;
+ if (numSpaces > 0 && r->obj->isText()) {
+ // get the number of spaces in the run
+ int spaces = 0;
+ for ( int i = r->start; i < r->stop; i++ ) {
+ const QChar c = static_cast<RenderText *>(r->obj)->text()[i];
+ if (c.category() == QChar::Separator_Space || c == '\n')
+ spaces++;
+ }
+
+ KHTMLAssert(spaces <= numSpaces);
+
+ // Only justify text with white-space: normal.
+ if (r->obj->style()->whiteSpace() == NORMAL) {
+ spaceAdd = (availableWidth - totWidth)*spaces/numSpaces;
+ static_cast<InlineTextBox*>(r->box)->setSpaceAdd(spaceAdd);
+ totWidth += spaceAdd;
+ }
+ numSpaces -= spaces;
+ }
+ }
+ }
+
+ // The widths of all runs are now known. We can now place every inline box (and
+ // compute accurate widths for the inline flow boxes).
+ int rightPos = lineBox->placeBoxesHorizontally(x);
+ if (rightPos > m_overflowWidth)
+ m_overflowWidth = rightPos; // FIXME: Work for rtl overflow also.
+ if (x < 0)
+ m_overflowLeft = kMin(m_overflowLeft, x);
+}
+
+void RenderBlock::computeVerticalPositionsForLine(InlineFlowBox* lineBox)
+{
+ lineBox->verticallyAlignBoxes(m_height);
+// lineBox->setBlockHeight(m_height);
+
+ // Check for page-breaks
+ if (canvas()->pagedMode() && !lineBox->afterPageBreak())
+ // If we get a page-break we might need to redo the line-break
+ if (clearLineOfPageBreaks(lineBox) && hasFloats()) return;
+
+ // See if the line spilled out. If so set overflow height accordingly.
+ int bottomOfLine = lineBox->bottomOverflow();
+ if (bottomOfLine > m_height && bottomOfLine > m_overflowHeight)
+ m_overflowHeight = bottomOfLine;
+
+ bool beforeContent = true;
+
+ // Now make sure we place replaced render objects correctly.
+ for (BidiRun* r = sFirstBidiRun; r; r = r->nextRun) {
+
+ // For positioned placeholders, cache the static Y position an object with non-inline display would have.
+ // Either it is unchanged if it comes before any real linebox, or it must clear the current line (already accounted in m_height).
+ // This value will be picked up by position() if relevant.
+ if (r->obj->isPositioned())
+ r->box->setYPos( beforeContent && r->obj->isBox() ? static_cast<RenderBox*>(r->obj)->staticY() : m_height );
+ else if (beforeContent)
+ beforeContent = false;
+
+ // Position is used to properly position both replaced elements and
+ // to update the static normal flow x/y of positioned elements.
+ r->obj->position(r->box, r->start, r->stop - r->start, r->level%2);
+ }
+}
+
+bool RenderBlock::clearLineOfPageBreaks(InlineFlowBox* lineBox)
+{
+ bool doPageBreak = false;
+ // Check for physical page-breaks
+ int xpage = crossesPageBreak(lineBox->topOverflow(), lineBox->bottomOverflow());
+ if (xpage) {
+#ifdef PAGE_DEBUG
+ kdDebug(6040) << renderName() << " Line crosses to page " << xpage << endl;
+ kdDebug(6040) << renderName() << " at pos " << lineBox->yPos() << " height " << lineBox->height() << endl;
+#endif
+
+ doPageBreak = true;
+ // check page-break-inside
+ if (!style()->pageBreakInside()) {
+ if (parent()->canClear(this, PageBreakNormal)) {
+ setNeedsPageClear(true);
+ doPageBreak = false;
+ }
+#ifdef PAGE_DEBUG
+ else
+ kdDebug(6040) << "Ignoring page-break-inside: avoid" << endl;
+#endif
+ }
+ // check orphans
+ int orphans = 0;
+ InlineRunBox* box = lineBox->prevLineBox();
+ while (box && orphans < style()->orphans()) {
+ orphans++;
+ box = box->prevLineBox();
+ }
+
+ if (orphans == 0) {
+ setNeedsPageClear(true);
+ doPageBreak = false;
+ } else
+ if (orphans < style()->orphans() ) {
+#ifdef PAGE_DEBUG
+ kdDebug(6040) << "Orphans: " << orphans << endl;
+#endif
+ // Orphans is a level 2 page-break rule and can be broken only
+ // if the break is physically required.
+ if (parent()->canClear(this, PageBreakHarder)) {
+ // move block instead
+ setNeedsPageClear(true);
+ doPageBreak = false;
+ }
+#ifdef PAGE_DEBUG
+ else
+ kdDebug(6040) << "Ignoring violated orphans" << endl;
+#endif
+ }
+ if (doPageBreak) {
+ int pTop = pageTopAfter(lineBox->yPos());
+
+ m_height = pTop;
+ lineBox->setAfterPageBreak(true);
+ lineBox->verticallyAlignBoxes(m_height);
+ if (lineBox->yPos() < pTop) {
+ // ### serious crap. render_line is sometimes placing lines too high
+ kdDebug(6040) << "page top overflow by repositioned line" << endl;
+ int heightIncrease = pTop - lineBox->yPos();
+ m_height = pTop + heightIncrease;
+ lineBox->verticallyAlignBoxes(m_height);
+ }
+#ifdef PAGE_DEBUG
+ kdDebug(6040) << "Cleared line " << lineBox->yPos() - oldYPos << "px" << endl;
+#endif
+ setContainsPageBreak(true);
+ }
+ }
+ return doPageBreak;
+}
+
+// collects one line of the paragraph and transforms it to visual order
+void RenderBlock::bidiReorderLine(const BidiIterator &start, const BidiIterator &end, BidiState &bidi)
+{
+ if ( start == end ) {
+ if ( start.current() == '\n' ) {
+ m_height += lineHeight( m_firstLine );
+ }
+ return;
+ }
+
+#if BIDI_DEBUG > 1
+ kdDebug(6041) << "reordering Line from " << start.obj << "/" << start.pos << " to " << end.obj << "/" << end.pos << endl;
+#endif
+
+ sFirstBidiRun = 0;
+ sLastBidiRun = 0;
+ sBidiRunCount = 0;
+
+ // context->ref();
+
+ dir = QChar::DirON;
+ emptyRun = true;
+
+ numSpaces = 0;
+
+ bidi.current = start;
+ bidi.last = bidi.current;
+ bool atEnd = false;
+ while( 1 ) {
+
+ QChar::Direction dirCurrent;
+ if (atEnd) {
+ //kdDebug(6041) << "atEnd" << endl;
+ BidiContext *c = bidi.context;
+ if ( bidi.current.atEnd())
+ while ( c->parent )
+ c = c->parent;
+ dirCurrent = c->dir;
+ } else {
+ dirCurrent = bidi.current.direction();
+ }
+
+#ifndef QT_NO_UNICODETABLES
+
+#if BIDI_DEBUG > 1
+ kdDebug(6041) << "directions: dir=" << (int)dir << " current=" << (int)dirCurrent << " last=" << status.last << " eor=" << status.eor << " lastStrong=" << status.lastStrong << " embedding=" << (int)context->dir << " level =" << (int)context->level << endl;
+#endif
+
+ switch(dirCurrent) {
+
+ // embedding and overrides (X1-X9 in the Bidi specs)
+ case QChar::DirRLE:
+ case QChar::DirLRE:
+ case QChar::DirRLO:
+ case QChar::DirLRO:
+ case QChar::DirPDF:
+ embed( dirCurrent, bidi );
+ break;
+
+ // strong types
+ case QChar::DirL:
+ if(dir == QChar::DirON)
+ dir = QChar::DirL;
+ switch(bidi.status.last)
+ {
+ case QChar::DirL:
+ bidi.eor = bidi.current; bidi.status.eor = QChar::DirL; break;
+ case QChar::DirR:
+ case QChar::DirAL:
+ case QChar::DirEN:
+ case QChar::DirAN:
+ appendRun( bidi );
+ break;
+ case QChar::DirES:
+ case QChar::DirET:
+ case QChar::DirCS:
+ case QChar::DirBN:
+ case QChar::DirB:
+ case QChar::DirS:
+ case QChar::DirWS:
+ case QChar::DirON:
+ if( bidi.status.eor != QChar::DirL ) {
+ //last stuff takes embedding dir
+ if(bidi.context->dir == QChar::DirL || bidi.status.lastStrong == QChar::DirL) {
+ if ( bidi.status.eor != QChar::DirEN && bidi.status.eor != QChar::DirAN && bidi.status.eor != QChar::DirON )
+ appendRun( bidi );
+ dir = QChar::DirL;
+ bidi.eor = bidi.current;
+ bidi.status.eor = QChar::DirL;
+ } else {
+ if ( bidi.status.eor == QChar::DirEN || bidi.status.eor == QChar::DirAN )
+ {
+ dir = bidi.status.eor;
+ appendRun( bidi );
+ }
+ dir = QChar::DirR;
+ bidi.eor = bidi.last;
+ appendRun( bidi );
+ dir = QChar::DirL;
+ bidi.status.eor = QChar::DirL;
+ }
+ } else {
+ bidi.eor = bidi.current; bidi.status.eor = QChar::DirL;
+ }
+ default:
+ break;
+ }
+ bidi.status.lastStrong = QChar::DirL;
+ break;
+ case QChar::DirAL:
+ case QChar::DirR:
+ if(dir == QChar::DirON) dir = QChar::DirR;
+ switch(bidi.status.last)
+ {
+ case QChar::DirR:
+ case QChar::DirAL:
+ bidi.eor = bidi.current; bidi.status.eor = QChar::DirR; break;
+ case QChar::DirL:
+ case QChar::DirEN:
+ case QChar::DirAN:
+ appendRun( bidi );
+ dir = QChar::DirR;
+ bidi.eor = bidi.current;
+ bidi.status.eor = QChar::DirR;
+ break;
+ case QChar::DirES:
+ case QChar::DirET:
+ case QChar::DirCS:
+ case QChar::DirBN:
+ case QChar::DirB:
+ case QChar::DirS:
+ case QChar::DirWS:
+ case QChar::DirON:
+ if( !(bidi.status.eor == QChar::DirR) && !(bidi.status.eor == QChar::DirAL) ) {
+ //last stuff takes embedding dir
+ if(bidi.context->dir == QChar::DirR || bidi.status.lastStrong == QChar::DirR
+ || bidi.status.lastStrong == QChar::DirAL) {
+ appendRun( bidi );
+ dir = QChar::DirR;
+ bidi.eor = bidi.current;
+ bidi.status.eor = QChar::DirR;
+ } else {
+ dir = QChar::DirL;
+ bidi.eor = bidi.last;
+ appendRun( bidi );
+ dir = QChar::DirR;
+ bidi.status.eor = QChar::DirR;
+ }
+ } else {
+ bidi.eor = bidi.current; bidi.status.eor = QChar::DirR;
+ }
+ default:
+ break;
+ }
+ bidi.status.lastStrong = dirCurrent;
+ break;
+
+ // weak types:
+
+ case QChar::DirNSM:
+ // ### if @sor, set dir to dirSor
+ break;
+ case QChar::DirEN:
+ if(!(bidi.status.lastStrong == QChar::DirAL)) {
+ // if last strong was AL change EN to AN
+ if(dir == QChar::DirON) {
+ dir = QChar::DirL;
+ }
+ switch(bidi.status.last)
+ {
+ case QChar::DirET:
+ if ( bidi.status.lastStrong == QChar::DirR || bidi.status.lastStrong == QChar::DirAL ) {
+ appendRun( bidi );
+ dir = QChar::DirEN;
+ bidi.status.eor = QChar::DirEN;
+ }
+ // fall through
+ case QChar::DirEN:
+ case QChar::DirL:
+ bidi.eor = bidi.current;
+ bidi.status.eor = dirCurrent;
+ break;
+ case QChar::DirR:
+ case QChar::DirAL:
+ case QChar::DirAN:
+ appendRun( bidi );
+ bidi.status.eor = QChar::DirEN;
+ dir = QChar::DirEN; break;
+ case QChar::DirES:
+ case QChar::DirCS:
+ if(bidi.status.eor == QChar::DirEN) {
+ bidi.eor = bidi.current; break;
+ }
+ case QChar::DirBN:
+ case QChar::DirB:
+ case QChar::DirS:
+ case QChar::DirWS:
+ case QChar::DirON:
+ if(bidi.status.eor == QChar::DirR) {
+ // neutrals go to R
+ bidi.eor = bidi.last;
+ appendRun( bidi );
+ dir = QChar::DirEN;
+ bidi.status.eor = QChar::DirEN;
+ }
+ else if( bidi.status.eor == QChar::DirL ||
+ (bidi.status.eor == QChar::DirEN && bidi.status.lastStrong == QChar::DirL)) {
+ bidi.eor = bidi.current; bidi.status.eor = dirCurrent;
+ } else {
+ // numbers on both sides, neutrals get right to left direction
+ if(dir != QChar::DirL) {
+ appendRun( bidi );
+ bidi.eor = bidi.last;
+ dir = QChar::DirR;
+ appendRun( bidi );
+ dir = QChar::DirEN;
+ bidi.status.eor = QChar::DirEN;
+ } else {
+ bidi.eor = bidi.current; bidi.status.eor = dirCurrent;
+ }
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ case QChar::DirAN:
+ dirCurrent = QChar::DirAN;
+ if(dir == QChar::DirON) dir = QChar::DirAN;
+ switch(bidi.status.last)
+ {
+ case QChar::DirL:
+ case QChar::DirAN:
+ bidi.eor = bidi.current; bidi.status.eor = QChar::DirAN; break;
+ case QChar::DirR:
+ case QChar::DirAL:
+ case QChar::DirEN:
+ appendRun( bidi );
+ dir = QChar::DirAN; bidi.status.eor = QChar::DirAN;
+ break;
+ case QChar::DirCS:
+ if(bidi.status.eor == QChar::DirAN) {
+ bidi.eor = bidi.current; break;
+ }
+ case QChar::DirES:
+ case QChar::DirET:
+ case QChar::DirBN:
+ case QChar::DirB:
+ case QChar::DirS:
+ case QChar::DirWS:
+ case QChar::DirON:
+ if(bidi.status.eor == QChar::DirR) {
+ // neutrals go to R
+ bidi.eor = bidi.last;
+ appendRun( bidi );
+ dir = QChar::DirAN;
+ bidi.status.eor = QChar::DirAN;
+ } else if( bidi.status.eor == QChar::DirL ||
+ (bidi.status.eor == QChar::DirEN && bidi.status.lastStrong == QChar::DirL)) {
+ bidi.eor = bidi.current; bidi.status.eor = dirCurrent;
+ } else {
+ // numbers on both sides, neutrals get right to left direction
+ if(dir != QChar::DirL) {
+ appendRun( bidi );
+ bidi.eor = bidi.last;
+ dir = QChar::DirR;
+ appendRun( bidi );
+ dir = QChar::DirAN;
+ bidi.status.eor = QChar::DirAN;
+ } else {
+ bidi.eor = bidi.current; bidi.status.eor = dirCurrent;
+ }
+ }
+ default:
+ break;
+ }
+ break;
+ case QChar::DirES:
+ case QChar::DirCS:
+ break;
+ case QChar::DirET:
+ if(bidi.status.last == QChar::DirEN) {
+ dirCurrent = QChar::DirEN;
+ bidi.eor = bidi.current; bidi.status.eor = dirCurrent;
+ break;
+ }
+ break;
+
+ // boundary neutrals should be ignored
+ case QChar::DirBN:
+ break;
+ // neutrals
+ case QChar::DirB:
+ // ### what do we do with newline and paragraph seperators that come to here?
+ break;
+ case QChar::DirS:
+ // ### implement rule L1
+ break;
+ case QChar::DirWS:
+ break;
+ case QChar::DirON:
+ break;
+ default:
+ break;
+ }
+
+ //cout << " after: dir=" << // dir << " current=" << dirCurrent << " last=" << status.last << " eor=" << status.eor << " lastStrong=" << status.lastStrong << " embedding=" << context->dir << endl;
+
+ if(bidi.current.atEnd()) break;
+
+ // set status.last as needed.
+ switch(dirCurrent)
+ {
+ case QChar::DirET:
+ case QChar::DirES:
+ case QChar::DirCS:
+ case QChar::DirS:
+ case QChar::DirWS:
+ case QChar::DirON:
+ switch(bidi.status.last)
+ {
+ case QChar::DirL:
+ case QChar::DirR:
+ case QChar::DirAL:
+ case QChar::DirEN:
+ case QChar::DirAN:
+ bidi.status.last = dirCurrent;
+ break;
+ default:
+ bidi.status.last = QChar::DirON;
+ }
+ break;
+ case QChar::DirNSM:
+ case QChar::DirBN:
+ // ignore these
+ break;
+ case QChar::DirEN:
+ if ( bidi.status.last == QChar::DirL ) {
+ break;
+ }
+ // fall through
+ default:
+ bidi.status.last = dirCurrent;
+ }
+#endif
+
+ if ( atEnd ) break;
+ bidi.last = bidi.current;
+
+ if ( emptyRun ) {
+ bidi.sor = bidi.current;
+ bidi.eor = bidi.current;
+ emptyRun = false;
+ }
+
+ // this causes the operator ++ to open and close embedding levels as needed
+ // for the CSS unicode-bidi property
+ adjustEmbedding = true;
+ bidi.current.increment( bidi );
+ adjustEmbedding = false;
+
+ if ( bidi.current == end ) {
+ if ( emptyRun )
+ break;
+ atEnd = true;
+ }
+ }
+
+#if BIDI_DEBUG > 0
+ kdDebug(6041) << "reached end of line current=" << current.obj << "/" << current.pos
+ << ", eor=" << eor.obj << "/" << eor.pos << endl;
+#endif
+ if ( !emptyRun && bidi.sor != bidi.current ) {
+ bidi.eor = bidi.last;
+ appendRun( bidi );
+ }
+
+ // reorder line according to run structure...
+
+ // first find highest and lowest levels
+ uchar levelLow = 128;
+ uchar levelHigh = 0;
+ BidiRun *r = sFirstBidiRun;
+ while ( r ) {
+ if ( r->level > levelHigh )
+ levelHigh = r->level;
+ if ( r->level < levelLow )
+ levelLow = r->level;
+ r = r->nextRun;
+ }
+
+ // implements reordering of the line (L2 according to Bidi spec):
+ // L2. From the highest level found in the text to the lowest odd level on each line,
+ // reverse any contiguous sequence of characters that are at that level or higher.
+
+ // reversing is only done up to the lowest odd level
+ if( !(levelLow%2) ) levelLow++;
+
+#if BIDI_DEBUG > 0
+ kdDebug(6041) << "lineLow = " << (uint)levelLow << ", lineHigh = " << (uint)levelHigh << endl;
+ kdDebug(6041) << "logical order is:" << endl;
+ QPtrListIterator<BidiRun> it2(runs);
+ BidiRun *r2;
+ for ( ; (r2 = it2.current()); ++it2 )
+ kdDebug(6041) << " " << r2 << " start=" << r2->start << " stop=" << r2->stop << " level=" << (uint)r2->level << endl;
+#endif
+
+ int count = sBidiRunCount - 1;
+
+ // do not reverse for visually ordered web sites
+ if(!style()->visuallyOrdered()) {
+ while(levelHigh >= levelLow) {
+ int i = 0;
+ BidiRun* currRun = sFirstBidiRun;
+ while ( i < count ) {
+ while(i < count && currRun && currRun->level < levelHigh) {
+ i++;
+ currRun = currRun->nextRun;
+ }
+ int start = i;
+ while(i <= count && currRun && currRun->level >= levelHigh) {
+ i++;
+ currRun = currRun->nextRun;
+ }
+ int end = i-1;
+ reverseRuns(start, end);
+ }
+ levelHigh--;
+ }
+ }
+
+#if BIDI_DEBUG > 0
+ kdDebug(6041) << "visual order is:" << endl;
+ for (BidiRun* curr = sFirstRun; curr; curr = curr->nextRun)
+ kdDebug(6041) << " " << curr << endl;
+#endif
+}
+
+#ifdef APPLE_CHANGES // KDE handles compact blocks differently
+static void buildCompactRuns(RenderObject* compactObj, BidiState &bidi)
+{
+ sBuildingCompactRuns = true;
+ if (!compactObj->isRenderBlock()) {
+ // Just append a run for our object.
+ isLineEmpty = false;
+ addRun(new (compactObj->renderArena()) BidiRun(0, compactObj->length(), compactObj, bidi.context, dir));
+ }
+ else {
+ // Format the compact like it is its own single line. We build up all the runs for
+ // the little compact and then reorder them for bidi.
+ RenderBlock* compactBlock = static_cast<RenderBlock*>(compactObj);
+ adjustEmbedding = true;
+ BidiIterator start(compactBlock, first(compactBlock, bidi), 0);
+ adjustEmbedding = false;
+ BidiIterator end = start;
+
+ betweenMidpoints = false;
+ isLineEmpty = true;
+ previousLineBrokeAtBR = true;
+
+ end = compactBlock->findNextLineBreak(start, bidi);
+ if (!isLineEmpty)
+ compactBlock->bidiReorderLine(start, end, bidi);
+ }
+
+
+ sCompactFirstBidiRun = sFirstBidiRun;
+ sCompactLastBidiRun = sLastBidiRun;
+ sCompactBidiRunCount = sBidiRunCount;
+
+ sNumMidpoints = 0;
+ sCurrMidpoint = 0;
+ betweenMidpoints = false;
+ sBuildingCompactRuns = false;
+}
+#endif
+
+void RenderBlock::layoutInlineChildren(bool relayoutChildren, int breakBeforeLine)
+{
+ BidiState bidi;
+
+ m_overflowHeight = 0;
+
+ invalidateVerticalPositions();
+#ifdef DEBUG_LAYOUT
+ QTime qt;
+ qt.start();
+ kdDebug( 6040 ) << renderName() << " layoutInlineChildren( " << this <<" )" << endl;
+#endif
+#if BIDI_DEBUG > 1 || defined( DEBUG_LINEBREAKS )
+ kdDebug(6041) << " ------- bidi start " << this << " -------" << endl;
+#endif
+
+ m_height = borderTop() + paddingTop();
+ int toAdd = borderBottom() + paddingBottom();
+ if (m_layer && scrollsOverflowX() && style()->height().isVariable())
+ toAdd += m_layer->horizontalScrollbarHeight();
+
+ // Clear out our line boxes.
+ deleteInlineBoxes();
+
+ // Text truncation only kicks in if your overflow isn't visible and your
+ // text-overflow-mode isn't clip.
+ bool hasTextOverflow = style()->textOverflow() && hasOverflowClip();
+
+ // Walk all the lines and delete our ellipsis line boxes if they exist.
+ if (hasTextOverflow)
+ deleteEllipsisLineBoxes();
+
+ if (firstChild()) {
+ // layout replaced elements
+ RenderObject *o = first( this, bidi, false );
+ while ( o ) {
+ if (o->markedForRepaint()) {
+ o->repaintDuringLayout();
+ o->setMarkedForRepaint(false);
+ }
+ if (o->isReplaced() || o->isFloating() || o->isPositioned()) {
+ // clear the placeHolderBox
+ if (o->isBox())
+ static_cast<RenderBox*>(o)->RenderBox::deleteInlineBoxes();
+
+ //kdDebug(6041) << "layouting replaced or floating child" << endl;
+ if (relayoutChildren || o->style()->width().isPercent() || o->style()->height().isPercent())
+ o->setChildNeedsLayout(true, false);
+ if (o->isPositioned())
+ o->containingBlock()->insertPositionedObject(o);
+ else
+ o->layoutIfNeeded();
+ }
+ else {
+ o->deleteInlineBoxes();
+ o->setNeedsLayout(false);
+ }
+ o = Bidinext( this, o, bidi, false );
+ }
+
+ BidiContext *startEmbed;
+ if( style()->direction() == LTR ) {
+ startEmbed = new BidiContext( 0, QChar::DirL );
+ bidi.status.eor = QChar::DirL;
+ } else {
+ startEmbed = new BidiContext( 1, QChar::DirR );
+ bidi.status.eor = QChar::DirR;
+ }
+ startEmbed->ref();
+
+ bidi.status.lastStrong = QChar::DirON;
+ bidi.status.last = QChar::DirON;
+
+ bidi.context = startEmbed;
+ adjustEmbedding = true;
+ BidiIterator start(this, first(this, bidi), 0);
+ adjustEmbedding = false;
+ BidiIterator end = start;
+
+ m_firstLine = true;
+
+ if (!smidpoints)
+ smidpoints = new QMemArray<BidiIterator>;
+
+ sNumMidpoints = 0;
+ sCurrMidpoint = 0;
+ sCompactFirstBidiRun = sCompactLastBidiRun = 0;
+ sCompactBidiRunCount = 0;
+
+ previousLineBrokeAtBR = true;
+
+ int lineCount = 0;
+ bool pagebreakHint = false;
+ int oldPos = 0;
+ BidiIterator oldStart;
+ BidiState oldBidi;
+ const bool pagedMode = canvas()->pagedMode();
+//
+ while( !end.atEnd() ) {
+ start = end;
+ lineCount++;
+ betweenMidpoints = false;
+ isLineEmpty = true;
+ pagebreakHint = false;
+ if (pagedMode) {
+ oldPos = m_height;
+ oldStart = start;
+ oldBidi = bidi;
+ }
+#ifdef APPLE_CHANGES // KDE handles compact blocks differently
+ if (m_firstLine && firstChild() && firstChild()->isCompact()) {
+ buildCompactRuns(firstChild(), bidi);
+ start.obj = firstChild()->nextSibling();
+ end = start;
+ }
+#endif
+ if (lineCount == breakBeforeLine) {
+ m_height = pageTopAfter(oldPos);
+ pagebreakHint = true;
+ }
+redo_linebreak:
+ end = findNextLineBreak(start, bidi);
+ if( start.atEnd() ) break;
+ if (!isLineEmpty) {
+ bidiReorderLine(start, end, bidi);
+
+ // Now that the runs have been ordered, we create the line boxes.
+ // At the same time we figure out where border/padding/margin should be applied for
+ // inline flow boxes.
+
+#ifdef APPLE_CHANGES // KDE handles compact blocks differently
+ if (sCompactFirstBidiRun) {
+ // We have a compact line sharing this line. Link the compact runs
+ // to our runs to create a single line of runs.
+ sCompactLastBidiRun->nextRun = sFirstBidiRun;
+ sFirstBidiRun = sCompactFirstBidiRun;
+ sBidiRunCount += sCompactBidiRunCount;
+ }
+#endif
+ if (sBidiRunCount) {
+ InlineFlowBox* lineBox = constructLine(start, end);
+ if (lineBox) {
+ if (pagebreakHint) lineBox->setAfterPageBreak(true);
+
+ // Now we position all of our text runs horizontally.
+ computeHorizontalPositionsForLine(lineBox, bidi);
+
+ // Now position our text runs vertically.
+ computeVerticalPositionsForLine(lineBox);
+
+ deleteBidiRuns(renderArena());
+
+ if (lineBox->afterPageBreak() && hasFloats() && !pagebreakHint) {
+ start = end = oldStart;
+ bidi = oldBidi;
+ m_height = pageTopAfter(oldPos);
+ deleteLastLineBox(renderArena());
+ pagebreakHint = true;
+ goto redo_linebreak;
+ }
+ }
+ }
+
+ if( end == start || (end.obj && end.obj->isBR() && !start.obj->isBR() ) ) {
+ adjustEmbedding = true;
+ end.increment(bidi);
+ adjustEmbedding = false;
+ } else if (end.obj && end.obj->style()->preserveLF() && end.current() == QChar('\n')) {
+ adjustEmbedding = true;
+ end.increment(bidi);
+ adjustEmbedding = false;
+ }
+
+ m_firstLine = false;
+ newLine();
+ }
+
+ sNumMidpoints = 0;
+ sCurrMidpoint = 0;
+ sCompactFirstBidiRun = sCompactLastBidiRun = 0;
+ sCompactBidiRunCount = 0;
+ }
+ startEmbed->deref();
+ //embed->deref();
+ }
+
+ sNumMidpoints = 0;
+ sCurrMidpoint = 0;
+
+ // If we violate widows page-breaking rules, we set a hint and relayout.
+ // Note that the widows rule might still be violated afterwards if the lines have become wider
+ if (canvas()->pagedMode() && containsPageBreak() && breakBeforeLine == 0)
+ {
+ int orphans = 0;
+ int widows = 0;
+ // find breaking line
+ InlineRunBox* lineBox = firstLineBox();
+ while (lineBox) {
+ if (lineBox->isInlineFlowBox()) {
+ InlineFlowBox* flowBox = static_cast<InlineFlowBox*>(lineBox);
+ if (flowBox->afterPageBreak()) break;
+ }
+ orphans++;
+ lineBox = lineBox->nextLineBox();
+ }
+ InlineFlowBox* pageBreaker = static_cast<InlineFlowBox*>(lineBox);
+ if (!pageBreaker) goto no_break;
+ // count widows
+ while (lineBox && widows < style()->widows()) {
+ if (lineBox->hasTextChildren())
+ widows++;
+ lineBox = lineBox->nextLineBox();
+ }
+ // Widows rule broken and more orphans left to use
+ if (widows < style()->widows() && orphans > 0) {
+ kdDebug( 6040 ) << "Widows: " << widows << endl;
+ // Check if we have enough orphans after respecting widows count
+ int newOrphans = orphans - (style()->widows() - widows);
+ if (newOrphans < style()->orphans()) {
+ if (parent()->canClear(this,PageBreakHarder)) {
+ // Relayout to remove incorrect page-break
+ setNeedsPageClear(true);
+ setContainsPageBreak(false);
+ layoutInlineChildren(relayoutChildren, -1);
+ return;
+ }
+ } else {
+ // Set hint and try again
+ layoutInlineChildren(relayoutChildren, newOrphans+1);
+ return;
+ }
+ }
+ }
+ no_break:
+
+ // in case we have a float on the last line, it might not be positioned up to now.
+ // This has to be done before adding in the bottom border/padding, or the float will
+ // include the padding incorrectly. -dwh
+ positionNewFloats();
+
+ // Now add in the bottom border/padding.
+ m_height += toAdd;
+
+ // Always make sure this is at least our height.
+ m_overflowHeight = kMax(m_height, m_overflowHeight);
+
+ // See if any lines spill out of the block. If so, we need to update our overflow width.
+ checkLinesForOverflow();
+
+ // See if we have any lines that spill out of our block. If we do, then we will
+ // possibly need to truncate text.
+ if (hasTextOverflow)
+ checkLinesForTextOverflow();
+
+#if BIDI_DEBUG > 1
+ kdDebug(6041) << " ------- bidi end " << this << " -------" << endl;
+#endif
+ //kdDebug() << "RenderBlock::layoutInlineChildren time used " << qt.elapsed() << endl;
+ //kdDebug(6040) << "height = " << m_height <<endl;
+}
+
+static void setStaticPosition( RenderBlock* p, RenderObject *o, bool *needToSetStaticX = 0, bool *needToSetStaticY = 0 )
+{
+ // If our original display wasn't an inline type, then we can
+ // determine our static x position now.
+ bool nssx, nssy;
+ bool isInlineType = o->style()->isOriginalDisplayInlineType();
+ nssx = o->hasStaticX();
+ if (nssx && !isInlineType && o->isBox()) {
+ static_cast<RenderBox*>(o)->setStaticX(o->parent()->style()->direction() == LTR ?
+ p->borderLeft()+p->paddingLeft() :
+ p->borderRight()+p->paddingRight());
+ nssx = false;
+ }
+
+ // If our original display was an INLINE type, then we can
+ // determine our static y position now.
+ nssy = o->hasStaticY();
+ if (nssy && o->isBox()) {
+ static_cast<RenderBox*>(o)->setStaticY(p->height());
+ nssy = !isInlineType;
+ }
+ if (needToSetStaticX) *needToSetStaticX = nssx;
+ if (needToSetStaticY) *needToSetStaticY = nssy;
+}
+
+BidiIterator RenderBlock::findNextLineBreak(BidiIterator &start, BidiState &bidi)
+{
+ int width = lineWidth(m_height);
+ int w = 0;
+ int tmpW = 0;
+#ifdef DEBUG_LINEBREAKS
+ kdDebug(6041) << "findNextLineBreak: line at " << m_height << " line width " << width << endl;
+ kdDebug(6041) << "sol: " << start.obj << " " << start.pos << endl;
+#endif
+
+ BidiIterator posStart = start;
+ bool hadPosStart = false;
+
+ // eliminate spaces at beginning of line
+ // remove leading spaces. Any inline flows we encounter will be empty and should also
+ // be skipped.
+ while (!start.atEnd() && (start.obj->isInlineFlow() || (!start.obj->style()->preserveWS() && !start.obj->isBR() &&
+#ifndef QT_NO_UNICODETABLES
+ ( (start.current().unicode() == (ushort)0x0020) || // ASCII space
+ (start.current().unicode() == (ushort)0x0009) || // ASCII tab
+ (start.current().unicode() == (ushort)0x000A) || // ASCII line feed
+ (start.current().unicode() == (ushort)0x000C) || // ASCII form feed
+ (start.current().unicode() == (ushort)0x200B) || // Zero-width space
+ start.obj->isFloatingOrPositioned() )
+#else
+ ( start.current() == ' ' || start.current() == '\n' || start.obj->isFloatingOrPositioned() )
+#endif
+ ))) {
+ if( start.obj->isFloatingOrPositioned() ) {
+ RenderObject *o = start.obj;
+ // add to special objects...
+ if (o->isFloating()) {
+ insertFloatingObject(o);
+ positionNewFloats();
+ width = lineWidth(m_height);
+ }
+ else if (o->isBox() && o->isPositioned()) {
+ if (!hadPosStart) {
+ hadPosStart = true;
+ posStart = start;
+ // end
+ addMidpoint(BidiIterator(0, o, 0));
+ } else {
+ // start/end
+ addMidpoint(BidiIterator(0, o, 0));
+ addMidpoint(BidiIterator(0, o, 0));
+ }
+ setStaticPosition(this, o);
+ }
+ }
+ adjustEmbedding = true;
+ start.increment(bidi);
+ adjustEmbedding = false;
+ }
+
+ if (hadPosStart && !start.atEnd())
+ addMidpoint(start);
+
+ if ( start.atEnd() ){
+ if (hadPosStart) {
+ start = posStart;
+ posStart.increment(bidi);
+ return posStart;
+ }
+ return start;
+ }
+
+ // This variable is used only if whitespace isn't set to PRE, and it tells us whether
+ // or not we are currently ignoring whitespace.
+ bool ignoringSpaces = false;
+ BidiIterator ignoreStart;
+
+ // This variable tracks whether the very last character we saw was a space. We use
+ // this to detect when we encounter a second space so we know we have to terminate
+ // a run.
+ bool currentCharacterIsSpace = false;
+ RenderObject* trailingSpaceObject = 0;
+
+ BidiIterator lBreak = start;
+
+ RenderObject *o = start.obj;
+ RenderObject *last = o;
+ int pos = start.pos;
+
+ bool prevLineBrokeCleanly = previousLineBrokeAtBR;
+ previousLineBrokeAtBR = false;
+
+ while( o ) {
+#ifdef DEBUG_LINEBREAKS
+ kdDebug(6041) << "new object "<< o <<" width = " << w <<" tmpw = " << tmpW << endl;
+#endif
+ if(o->isBR()) {
+ if( w + tmpW <= width ) {
+ lBreak.obj = o;
+ lBreak.pos = 0;
+
+ // A <br> always breaks a line, so don't let the line be collapsed
+ // away. Also, the space at the end of a line with a <br> does not
+ // get collapsed away. It only does this if the previous line broke
+ // cleanly. Otherwise the <br> has no effect on whether the line is
+ // empty or not.
+ if (prevLineBrokeCleanly)
+ isLineEmpty = false;
+ trailingSpaceObject = 0;
+ previousLineBrokeAtBR = true;
+
+ if (!isLineEmpty) {
+ // only check the clear status for non-empty lines.
+ EClear clear = o->style()->clear();
+ if(clear != CNONE)
+ m_clearStatus = (EClear) (m_clearStatus | clear);
+ }
+ }
+ goto end;
+ }
+ if( o->isFloatingOrPositioned() ) {
+ // add to special objects...
+ if(o->isFloating()) {
+ insertFloatingObject(o);
+ // check if it fits in the current line.
+ // If it does, position it now, otherwise, position
+ // it after moving to next line (in newLine() func)
+ if (o->width()+o->marginLeft()+o->marginRight()+w+tmpW <= width) {
+ positionNewFloats();
+ width = lineWidth(m_height);
+ }
+ }
+ else if (o->isPositioned()) {
+ bool needToSetStaticX;
+ bool needToSetStaticY;
+ setStaticPosition(this, o, &needToSetStaticX, &needToSetStaticY);
+
+ // If we're ignoring spaces, we have to stop and include this object and
+ // then start ignoring spaces again.
+ if (needToSetStaticX || needToSetStaticY) {
+ trailingSpaceObject = 0;
+ ignoreStart.obj = o;
+ ignoreStart.pos = 0;
+ if (ignoringSpaces) {
+ addMidpoint(ignoreStart); // Stop ignoring spaces.
+ addMidpoint(ignoreStart); // Start ignoring again.
+ }
+ }
+ }
+ } else if (o->isInlineFlow()) {
+ // Only empty inlines matter. We treat those similarly to replaced elements.
+ KHTMLAssert(!o->firstChild());
+ tmpW += o->marginLeft()+o->borderLeft()+o->paddingLeft()+
+ o->marginRight()+o->borderRight()+o->paddingRight();
+ } else if ( o->isReplaced() || o->isGlyph() ) {
+ EWhiteSpace currWS = o->style()->whiteSpace();
+ EWhiteSpace lastWS = last->style()->whiteSpace();
+
+ // WinIE marquees have different whitespace characteristics by default when viewed from
+ // the outside vs. the inside. Text inside is NOWRAP, and so we altered the marquee's
+ // style to reflect this, but we now have to get back to the original whitespace value
+ // for the marquee when checking for line breaking.
+ if (o->isHTMLMarquee() && o->layer() && o->layer()->marquee())
+ currWS = o->layer()->marquee()->whiteSpace();
+ if (last->isHTMLMarquee() && last->layer() && last->layer()->marquee())
+ lastWS = last->layer()->marquee()->whiteSpace();
+
+ // Break on replaced elements if either has normal white-space.
+ if (currWS == NORMAL || lastWS == NORMAL) {
+ w += tmpW;
+ tmpW = 0;
+ lBreak.obj = o;
+ lBreak.pos = 0;
+ }
+
+ tmpW += o->width()+o->marginLeft()+o->marginRight()+inlineWidth(o);
+ if (ignoringSpaces) {
+ BidiIterator startMid( 0, o, 0 );
+ addMidpoint(startMid);
+ }
+ isLineEmpty = false;
+ ignoringSpaces = false;
+ currentCharacterIsSpace = false;
+ trailingSpaceObject = 0;
+
+ if (o->isListMarker() && o->style()->listStylePosition() == OUTSIDE) {
+ // The marker must not have an effect on whitespace at the start
+ // of the line. We start ignoring spaces to make sure that any additional
+ // spaces we see will be discarded.
+ //
+ // Optimize for a common case. If we can't find whitespace after the list
+ // item, then this is all moot. -dwh
+ RenderObject* next = Bidinext( start.par, o, bidi );
+ if (!style()->preserveWS() && next && next->isText() && static_cast<RenderText*>(next)->stringLength() > 0 &&
+ (static_cast<RenderText*>(next)->text()[0].category() == QChar::Separator_Space ||
+ static_cast<RenderText*>(next)->text()[0] == '\n')) {
+ currentCharacterIsSpace = true;
+ ignoringSpaces = true;
+ BidiIterator endMid( 0, o, 0 );
+ addMidpoint(endMid);
+ }
+ }
+ } else if ( o->isText() ) {
+ RenderText *t = static_cast<RenderText *>(o);
+ int strlen = t->stringLength();
+ int len = strlen - pos;
+ QChar *str = t->text();
+
+ const Font *f = t->htmlFont( m_firstLine );
+ // proportional font, needs a bit more work.
+ int lastSpace = pos;
+ bool autoWrap = o->style()->autoWrap();
+ bool preserveWS = o->style()->preserveWS();
+ bool preserveLF = o->style()->preserveLF();
+#ifdef APPLE_CHANGES
+ int wordSpacing = o->style()->wordSpacing();
+#endif
+ bool appliedStartWidth = pos > 0; // If the span originated on a previous line,
+ // then assume the start width has been applied.
+ bool appliedEndWidth = false;
+ bool nextIsSoftBreakable = false;
+
+ while(len) {
+ bool previousCharacterIsSpace = currentCharacterIsSpace;
+ bool isSoftBreakable = nextIsSoftBreakable;
+ nextIsSoftBreakable = false;
+ const QChar c = str[pos];
+ currentCharacterIsSpace = c == ' ';
+
+ if (preserveWS || !currentCharacterIsSpace)
+ isLineEmpty = false;
+
+ // Check for soft hyphens. Go ahead and ignore them.
+ if (c.unicode() == SOFT_HYPHEN && pos > 0) {
+ nextIsSoftBreakable = true;
+ if (!ignoringSpaces) {
+ // Ignore soft hyphens
+ BidiIterator endMid(0, o, pos-1);
+ addMidpoint(endMid);
+
+ // Add the width up to but not including the hyphen.
+ tmpW += t->width(lastSpace, pos - lastSpace, f);
+
+ // For wrapping text only, include the hyphen. We need to ensure it will fit
+ // on the line if it shows when we break.
+ if (o->style()->autoWrap())
+ tmpW += t->width(pos, 1, f);
+
+ BidiIterator startMid(0, o, pos+1);
+ addMidpoint(startMid);
+ }
+
+ pos++;
+ len--;
+ lastSpace = pos; // Cheesy hack to prevent adding in widths of the run twice.
+ continue;
+ }
+#ifdef APPLE_CHANGES // KDE applies wordspacing differently
+ bool applyWordSpacing = false;
+#endif
+ if (ignoringSpaces) {
+ // We need to stop ignoring spaces, if we encounter a non-space or
+ // a run that doesn't collapse spaces.
+ if (!currentCharacterIsSpace || preserveWS) {
+ // Stop ignoring spaces and begin at this
+ // new point.
+ ignoringSpaces = false;
+ lastSpace = pos; // e.g., "Foo goo", don't add in any of the ignored spaces.
+ BidiIterator startMid ( 0, o, pos );
+ addMidpoint(startMid);
+ }
+ else {
+ // Just keep ignoring these spaces.
+ pos++;
+ len--;
+ continue;
+ }
+ }
+
+ if ( (preserveLF && c == '\n') || (autoWrap && (isBreakable( str, pos, strlen ) || isSoftBreakable)) ) {
+
+ tmpW += t->width(lastSpace, pos - lastSpace, f);
+ if (!appliedStartWidth) {
+ tmpW += inlineWidth(o, true, false);
+ appliedStartWidth = true;
+ }
+#ifdef APPLE_CHANGES
+ applyWordSpacing = (wordSpacing && currentCharacterIsSpace && !previousCharacterIsSpace &&
+ !t->containsOnlyWhitespace(pos+1, strlen-(pos+1)));
+#endif
+#ifdef DEBUG_LINEBREAKS
+ kdDebug(6041) << "found space at " << pos << " in string '" << QString( str, strlen ).latin1() << "' adding " << tmpW << " new width = " << w << endl;
+#endif
+ if ( autoWrap && w + tmpW > width && w == 0 ) {
+ int fb = nearestFloatBottom(m_height);
+ int newLineWidth = lineWidth(fb);
+ // See if |tmpW| will fit on the new line. As long as it does not,
+ // keep adjusting our float bottom until we find some room.
+ int lastFloatBottom = m_height;
+ while (lastFloatBottom < fb && tmpW > newLineWidth) {
+ lastFloatBottom = fb;
+ fb = nearestFloatBottom(fb);
+ newLineWidth = lineWidth(fb);
+ }
+
+ if(!w && m_height < fb && width < newLineWidth) {
+ m_height = fb;
+ width = newLineWidth;
+#ifdef DEBUG_LINEBREAKS
+ kdDebug() << "RenderBlock::findNextLineBreak new position at " << m_height << " newWidth " << width << endl;
+#endif
+ }
+ }
+
+ if (autoWrap) {
+ if (w+tmpW > width)
+ goto end;
+ else if ( (pos > 1 && str[pos-1].unicode() == SOFT_HYPHEN) )
+ // Subtract the width of the soft hyphen out since we fit on a line.
+ tmpW -= t->width(pos-1, 1, f);
+ }
+
+ if( preserveLF && *(str+pos) == '\n' ) {
+ lBreak.obj = o;
+ lBreak.pos = pos;
+
+#ifdef DEBUG_LINEBREAKS
+ kdDebug(6041) << "forced break sol: " << start.obj << " " << start.pos << " end: " << lBreak.obj << " " << lBreak.pos << " width=" << w << endl;
+#endif
+ return lBreak;
+ }
+
+ if ( autoWrap ) {
+ w += tmpW;
+ tmpW = 0;
+ lBreak.obj = o;
+ lBreak.pos = pos;
+ }
+
+ lastSpace = pos;
+#ifdef APPLE_CHANGES
+ if (applyWordSpacing)
+ w += wordSpacing;
+#endif
+ }
+
+ if (!ignoringSpaces && !preserveWS) {
+ // If we encounter a second space, we need to go ahead and break up this run
+ // and enter a mode where we start collapsing spaces.
+ if (currentCharacterIsSpace && previousCharacterIsSpace) {
+ ignoringSpaces = true;
+
+ // We just entered a mode where we are ignoring
+ // spaces. Create a midpoint to terminate the run
+ // before the second space.
+ addMidpoint(ignoreStart);
+ lastSpace = pos;
+ }
+ }
+
+ if (currentCharacterIsSpace && !previousCharacterIsSpace) {
+ ignoreStart.obj = o;
+ ignoreStart.pos = pos;
+ }
+
+ if (!preserveWS && currentCharacterIsSpace && !ignoringSpaces)
+ trailingSpaceObject = o;
+ else if (preserveWS || !currentCharacterIsSpace)
+ trailingSpaceObject = 0;
+
+ pos++;
+ len--;
+ }
+
+ // IMPORTANT: pos is > length here!
+ if (!ignoringSpaces)
+ tmpW += t->width(lastSpace, pos - lastSpace, f);
+ if (!appliedStartWidth)
+ tmpW += inlineWidth(o, true, false);
+ if (!appliedEndWidth)
+ tmpW += inlineWidth(o, false, true);
+ } else
+ KHTMLAssert( false );
+
+ RenderObject* next = Bidinext(start.par, o, bidi);
+ bool autoWrap = o->style()->autoWrap();
+ bool checkForBreak = autoWrap;
+ if (w && w + tmpW > width && lBreak.obj && !o->style()->preserveLF() && !autoWrap)
+ checkForBreak = true;
+ else if (next && o->isText() && next->isText() && !next->isBR()) {
+ if (autoWrap || next->style()->autoWrap()) {
+ if (currentCharacterIsSpace)
+ checkForBreak = true;
+ else {
+ checkForBreak = false;
+ RenderText* nextText = static_cast<RenderText*>(next);
+ if (nextText->stringLength() != 0) {
+ QChar c = nextText->text()[0];
+ if (c == ' ' || c == '\t' || (c == '\n' && !next->style()->preserveLF())) {
+ // If the next item on the line is text, and if we did not end with
+ // a space, then the next text run continues our word (and so it needs to
+ // keep adding to |tmpW|. Just update and continue.
+ checkForBreak = true;
+ }
+ }
+
+ bool canPlaceOnLine = (w + tmpW <= width) || !autoWrap;
+ if (canPlaceOnLine && checkForBreak) {
+ w += tmpW;
+ tmpW = 0;
+ lBreak.obj = next;
+ lBreak.pos = 0;
+ }
+ }
+ }
+ }
+
+ if (checkForBreak && (w + tmpW > width)) {
+ //kdDebug() << " too wide w=" << w << " tmpW = " << tmpW << " width = " << width << endl;
+ //kdDebug() << "start=" << start.obj << " current=" << o << endl;
+ // if we have floats, try to get below them.
+ if (currentCharacterIsSpace && !ignoringSpaces && !o->style()->preserveWS())
+ trailingSpaceObject = 0;
+
+ int fb = nearestFloatBottom(m_height);
+ int newLineWidth = lineWidth(fb);
+ // See if |tmpW| will fit on the new line. As long as it does not,
+ // keep adjusting our float bottom until we find some room.
+ int lastFloatBottom = m_height;
+ while (lastFloatBottom < fb && tmpW > newLineWidth) {
+ lastFloatBottom = fb;
+ fb = nearestFloatBottom(fb);
+ newLineWidth = lineWidth(fb);
+ }
+ if( !w && m_height < fb && width < newLineWidth ) {
+ m_height = fb;
+ width = newLineWidth;
+#ifdef DEBUG_LINEBREAKS
+ kdDebug() << "RenderBlock::findNextLineBreak new position at " << m_height << " newWidth " << width << endl;
+#endif
+ }
+
+ // |width| may have been adjusted because we got shoved down past a float (thus
+ // giving us more room), so we need to retest, and only jump to
+ // the end label if we still don't fit on the line. -dwh
+ if (w + tmpW > width)
+ goto end;
+ }
+
+ last = o;
+ o = next;
+
+ if (!last->isFloatingOrPositioned() && last->isReplaced() && last->style()->autoWrap()) {
+ // Go ahead and add in tmpW.
+ w += tmpW;
+ tmpW = 0;
+ lBreak.obj = o;
+ lBreak.pos = 0;
+ }
+
+ // Clear out our character space bool, since inline <pre>s don't collapse whitespace
+ // with adjacent inline normal/nowrap spans.
+ if (last->style()->preserveWS())
+ currentCharacterIsSpace = false;
+
+ pos = 0;
+ }
+
+#ifdef DEBUG_LINEBREAKS
+ kdDebug( 6041 ) << "end of par, width = " << width << " linewidth = " << w + tmpW << endl;
+#endif
+ if( w + tmpW <= width || (last && !last->style()->autoWrap())) {
+ lBreak.obj = 0;
+ lBreak.pos = 0;
+ }
+
+ end:
+
+ if( lBreak == start && !lBreak.obj->isBR() ) {
+ // we just add as much as possible
+ if ( style()->whiteSpace() == PRE ) {
+ // FIXME: Don't really understand this case.
+ if(pos != 0) {
+ lBreak.obj = o;
+ lBreak.pos = pos - 1;
+ } else {
+ lBreak.obj = last;
+ lBreak.pos = last->isText() ? last->length() : 0;
+ }
+ } else if( lBreak.obj ) {
+ if( last != o ) {
+ // better to break between object boundaries than in the middle of a word
+ lBreak.obj = o;
+ lBreak.pos = 0;
+ } else {
+ // Don't ever break in the middle of a word if we can help it.
+ // There's no room at all. We just have to be on this line,
+ // even though we'll spill out.
+ lBreak.obj = o;
+ lBreak.pos = pos;
+ }
+ }
+ }
+
+ if (hadPosStart)
+ start = posStart;
+
+ // make sure we consume at least one char/object.
+ if( lBreak == start )
+ lBreak.increment(bidi);
+
+#ifdef DEBUG_LINEBREAKS
+ kdDebug(6041) << "regular break sol: " << start.obj << " " << start.pos << " end: " << lBreak.obj << " " << lBreak.pos << " width=" << w << endl;
+#endif
+
+ // Sanity check our midpoints.
+ checkMidpoints(lBreak, bidi);
+
+ if (trailingSpaceObject) {
+ // This object is either going to be part of the last midpoint, or it is going
+ // to be the actual endpoint. In both cases we just decrease our pos by 1 level to
+ // exclude the space, allowing it to - in effect - collapse into the newline.
+ if (sNumMidpoints%2==1) {
+ BidiIterator* midpoints = smidpoints->data();
+ midpoints[sNumMidpoints-1].pos--;
+ }
+ //else if (lBreak.pos > 0)
+ // lBreak.pos--;
+ else if (lBreak.obj == 0 && trailingSpaceObject->isText()) {
+ // Add a new end midpoint that stops right at the very end.
+ RenderText* text = static_cast<RenderText *>(trailingSpaceObject);
+ unsigned pos = text->length() >=2 ? text->length() - 2 : UINT_MAX;
+ BidiIterator endMid ( 0, trailingSpaceObject, pos );
+ addMidpoint(endMid);
+ }
+ }
+
+ // We might have made lBreak an iterator that points past the end
+ // of the object. Do this adjustment to make it point to the start
+ // of the next object instead to avoid confusing the rest of the
+ // code.
+ if (lBreak.pos > 0) {
+ lBreak.pos--;
+ lBreak.increment(bidi);
+ }
+
+ if (lBreak.obj && lBreak.pos >= 2 && lBreak.obj->isText()) {
+ // For soft hyphens on line breaks, we have to chop out the midpoints that made us
+ // ignore the hyphen so that it will render at the end of the line.
+ QChar c = static_cast<RenderText*>(lBreak.obj)->text()[lBreak.pos-1];
+ if (c.unicode() == SOFT_HYPHEN)
+ chopMidpointsAt(lBreak.obj, lBreak.pos-2);
+ }
+
+ return lBreak;
+}
+
+void RenderBlock::checkLinesForOverflow()
+{
+ for (RootInlineBox* curr = static_cast<khtml::RootInlineBox*>(firstLineBox()); curr; curr = static_cast<khtml::RootInlineBox*>(curr->nextLineBox())) {
+// m_overflowLeft = min(curr->leftOverflow(), m_overflowLeft);
+ m_overflowTop = kMin(curr->topOverflow(), m_overflowTop);
+// m_overflowWidth = max(curr->rightOverflow(), m_overflowWidth);
+ m_overflowHeight = kMax(curr->bottomOverflow(), m_overflowHeight);
+ }
+}
+
+void RenderBlock::deleteEllipsisLineBoxes()
+{
+ for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox())
+ curr->clearTruncation();
+}
+
+void RenderBlock::checkLinesForTextOverflow()
+{
+ // Determine the width of the ellipsis using the current font.
+ QChar ellipsis = 0x2026; // FIXME: CSS3 says this is configurable, also need to use 0x002E (FULL STOP) if 0x2026 not renderable
+ static QString ellipsisStr(ellipsis);
+ const Font& firstLineFont = style(true)->htmlFont();
+ const Font& font = style()->htmlFont();
+ int firstLineEllipsisWidth = firstLineFont.width(&ellipsis, 1, 0);
+ int ellipsisWidth = (font == firstLineFont) ? firstLineEllipsisWidth : font.width(&ellipsis, 1, 0);
+
+ // For LTR text truncation, we want to get the right edge of our padding box, and then we want to see
+ // if the right edge of a line box exceeds that. For RTL, we use the left edge of the padding box and
+ // check the left edge of the line box to see if it is less
+ // Include the scrollbar for overflow blocks, which means we want to use "contentWidth()"
+ bool ltr = style()->direction() == LTR;
+ for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
+ int blockEdge = ltr ? rightOffset(curr->yPos()) : leftOffset(curr->yPos());
+ int lineBoxEdge = ltr ? curr->xPos() + curr->width() : curr->xPos();
+ if ((ltr && lineBoxEdge > blockEdge) || (!ltr && lineBoxEdge < blockEdge)) {
+ // This line spills out of our box in the appropriate direction. Now we need to see if the line
+ // can be truncated. In order for truncation to be possible, the line must have sufficient space to
+ // accommodate our truncation string, and no replaced elements (images, tables) can overlap the ellipsis
+ // space.
+ int width = curr == firstRootBox() ? firstLineEllipsisWidth : ellipsisWidth;
+ if (curr->canAccommodateEllipsis(ltr, blockEdge, lineBoxEdge, width))
+ curr->placeEllipsis(ellipsisStr, ltr, blockEdge, width);
+ }
+ }
+}
+
+// For --enable-final
+#undef BIDI_DEBUG
+#undef DEBUG_LINEBREAKS
+#undef DEBUG_LAYOUT
+
+}
diff --git a/khtml/rendering/bidi.h b/khtml/rendering/bidi.h
new file mode 100644
index 000000000..4d6b6aaf1
--- /dev/null
+++ b/khtml/rendering/bidi.h
@@ -0,0 +1,106 @@
+/*
+ * This file is part of the html renderer for KDE.
+ *
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * Copyright (C) 2003 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.
+ *
+ */
+#ifndef BIDI_H
+#define BIDI_H
+
+#include <qstring.h>
+
+namespace khtml {
+ class RenderArena;
+ class RenderBlock;
+ class RenderObject;
+ class InlineBox;
+
+ class BidiContext {
+ public:
+ BidiContext(unsigned char level, QChar::Direction embedding, BidiContext *parent = 0, bool override = false);
+ ~BidiContext();
+
+ void ref() const;
+ void deref() const;
+
+ unsigned char level;
+ bool override : 1;
+ QChar::Direction dir : 5;
+ QChar::Direction basicDir : 5;
+
+ BidiContext *parent;
+
+
+ // refcounting....
+ mutable int count;
+ };
+
+ struct BidiRun {
+ BidiRun(int _start, int _stop, RenderObject *_obj, BidiContext *context, QChar::Direction dir)
+ : start( _start ), stop( _stop ), obj( _obj ), box(0), nextRun(0)
+ {
+ if(dir == QChar::DirON) dir = context->dir;
+
+ level = context->level;
+
+ // add level of run (cases I1 & I2)
+ if( level % 2 ) {
+ if(dir == QChar::DirL || dir == QChar::DirAN || dir == QChar::DirEN)
+ level++;
+ } else {
+ if( dir == QChar::DirR )
+ level++;
+ else if( dir == QChar::DirAN || dir == QChar::DirEN)
+ level += 2;
+ }
+ }
+
+ void detach(RenderArena* renderArena);
+
+ // Overloaded new operator.
+ void* operator new(size_t sz, RenderArena* renderArena) throw();
+
+ // Overridden to prevent the normal delete from being called.
+ void operator delete(void* ptr, size_t sz);
+
+private:
+ // The normal operator new is disallowed.
+ void* operator new(size_t sz) throw();
+
+public:
+ int start;
+ int stop;
+
+ RenderObject *obj;
+ InlineBox* box;
+
+ // explicit + implicit levels here
+ uchar level;
+
+ bool compact : 1;
+
+ BidiRun* nextRun;
+ };
+
+ struct BidiIterator;
+ struct BidiState;
+
+}
+
+#endif
diff --git a/khtml/rendering/break_lines.cpp b/khtml/rendering/break_lines.cpp
new file mode 100644
index 000000000..483455127
--- /dev/null
+++ b/khtml/rendering/break_lines.cpp
@@ -0,0 +1,126 @@
+#include <break_lines.h>
+#include <klibloader.h>
+#include "qcstring.h"
+#include <qtextcodec.h>
+#include <qcleanuphandler.h>
+#include <config.h>
+
+
+/* If HAVE_LIBTHAI is defined, libkhtml will link against
+ * libthai since compile time. Otherwise it will try to
+ * dlopen at run-time
+ *
+ * Ott Pattara Nov 14, 2004
+ */
+
+#ifndef HAVE_LIBTHAI
+typedef int (*th_brk_def)(const unsigned char*, int[], int);
+static th_brk_def th_brk;
+#else
+#include <thai/thailib.h>
+#include <thai/thbrk.h>
+#endif
+
+namespace khtml {
+ struct ThaiCache
+ {
+ ThaiCache() {
+ string = 0;
+ allocated = 0x400;
+ wbrpos = (int *) malloc(allocated*sizeof(int));
+ numwbrpos = 0;
+ numisbreakable = 0x400;
+ isbreakable = (int *) malloc(numisbreakable*sizeof(int));
+ library = 0;
+ }
+ ~ThaiCache() {
+ free(wbrpos);
+ free(isbreakable);
+ if (library) library->unload();
+ }
+ const QChar *string;
+ int *wbrpos;
+ int *isbreakable;
+ int allocated;
+ int numwbrpos,numisbreakable;
+ KLibrary *library;
+ };
+ static ThaiCache *cache = 0;
+
+ void cleanup_thaibreaks()
+ {
+ delete cache;
+ cache = 0;
+#ifndef HAVE_LIBTHAI
+ th_brk = 0;
+#endif
+ }
+
+ bool isBreakableThai( const QChar *string, const int pos, const int len)
+ {
+ static QTextCodec *thaiCodec = QTextCodec::codecForMib(2259);
+ //printf("Entering isBreakableThai with pos = %d\n", pos);
+
+#ifndef HAVE_LIBTHAI
+
+ KLibrary *lib = 0;
+
+ /* load libthai dynamically */
+ if (( !th_brk ) && thaiCodec ) {
+ printf("Try to load libthai dynamically...\n");
+ KLibLoader *loader = KLibLoader::self();
+ lib = loader->library("libthai");
+ if (lib && lib->hasSymbol("th_brk")) {
+ th_brk = (th_brk_def) lib->symbol("th_brk");
+ } else {
+ // indication that loading failed and we shouldn't try to load again
+ printf("Error, can't load libthai...\n");
+ thaiCodec = 0;
+ if (lib)
+ lib->unload();
+ }
+ }
+
+ if (!th_brk ) {
+ return true;
+ }
+#endif
+
+ if (!cache ) {
+ cache = new ThaiCache;
+#ifndef HAVE_LIBTHAI
+ cache->library = lib;
+#endif
+ }
+
+ // build up string of thai chars
+ if ( string != cache->string ) {
+ //fprintf(stderr,"new string found (not in cache), calling libthai\n");
+ QCString cstr = thaiCodec->fromUnicode( QConstString(string,len).string());
+ //printf("About to call libthai::th_brk with str: %s",cstr.data());
+
+ cache->numwbrpos = th_brk((const unsigned char*) cstr.data(), cache->wbrpos, cache->allocated);
+ //fprintf(stderr,"libthai returns with value %d\n",cache->numwbrpos);
+ if (cache->numwbrpos > cache->allocated) {
+ cache->allocated = cache->numwbrpos;
+ cache->wbrpos = (int *)realloc(cache->wbrpos, cache->allocated*sizeof(int));
+ cache->numwbrpos = th_brk((const unsigned char*) cstr.data(), cache->wbrpos, cache->allocated);
+ }
+ if ( len > cache->numisbreakable ) {
+ cache->numisbreakable=len;
+ cache->isbreakable = (int *)realloc(cache->isbreakable, cache->numisbreakable*sizeof(int));
+ }
+ for (int i = 0 ; i < len ; ++i) {
+ cache->isbreakable[i] = 0;
+ }
+ if ( cache->numwbrpos > 0 ) {
+ for (int i = cache->numwbrpos-1; i >= 0; --i) {
+ cache->isbreakable[cache->wbrpos[i]] = 1;
+ }
+ }
+ cache->string = string;
+ }
+ //printf("Returning %d\n", cache->isbreakable[pos]);
+ return cache->isbreakable[pos];
+ }
+}
diff --git a/khtml/rendering/break_lines.h b/khtml/rendering/break_lines.h
new file mode 100644
index 000000000..e33fce247
--- /dev/null
+++ b/khtml/rendering/break_lines.h
@@ -0,0 +1,163 @@
+#ifndef BREAK_LINES_H
+#define BREAK_LINES_H
+
+#include <qstring.h>
+
+namespace khtml {
+
+ /*
+ array of unicode codes where breaking shouldn't occur.
+ (in sorted order because of using with binary search)
+ these are currently for Japanese, though simply adding
+ Korean, Chinese ones should work as well
+ */
+ /*
+ dontbreakbefore[] contains characters not covered by QChar::Punctuation_Close that shouldn't be broken before.
+ chars included in QChar::Punctuation_Close are listed below.(look at UAX #14)
+ - 3001 ideographic comma
+ - 3002 ideographic full stop
+ - FE50 small comma
+ - FF52 small full stop
+ - FF0C fullwidth comma
+ - FF0E fullwidth full stop
+ - FF61 halfwidth ideographic full stop
+ - FF64 halfwidth ideographic comma
+ these character is commented out.
+ */
+ const ushort dontbreakbefore[] = {
+ //0x3001, //ideographic comma
+ //0x3002, //ideographic full stop
+ 0x3005, //ideographic iteration mark
+ 0x3009, //right angle bracket
+ 0x300b, //right double angle bracket
+ 0x300d, //right corner bracket
+ 0x300f, //right white corner bracket
+ 0x3011, //right black lenticular bracket
+ 0x3015, //right tortoise shell bracket
+ 0x3041, //small a hiragana
+ 0x3043, //small i hiragana
+ 0x3045, //small u hiragana
+ 0x3047, //small e hiragana
+ 0x3049, //small o hiragana
+ 0x3063, //small tsu hiragana
+ 0x3083, //small ya hiragana
+ 0x3085, //small yu hiragana
+ 0x3087, //small yo hiragana
+ 0x308E, //small wa hiragana
+ 0x309B, //jap voiced sound mark
+ 0x309C, //jap semi-voiced sound mark
+ 0x309D, //jap iteration mark hiragana
+ 0x309E, //jap voiced iteration mark hiragana
+ 0x30A1, //small a katakana
+ 0x30A3, //small i katakana
+ 0x30A5, //small u katakana
+ 0x30A7, //small e katakana
+ 0x30A9, //small o katakana
+ 0x30C3, //small tsu katakana
+ 0x30E3, //small ya katakana
+ 0x30E5, //small yu katakana
+ 0x30E7, //small yo katakana
+ 0x30EE, //small wa katakana
+ 0x30F5, //small ka katakana
+ 0x30F6, //small ke katakana
+ 0x30FC, //jap prolonged sound mark
+ 0x30FD, //jap iteration mark katakana
+ 0x30FE, //jap voiced iteration mark katakana
+ //0xFE50, //small comma
+ //0xFF52, //small full stop
+ 0xFF01, //fullwidth exclamation mark
+ 0xFF09, //fullwidth right parenthesis
+ //0xFF0C, //fullwidth comma
+ 0xFF0D, //fullwidth hypen-minus
+ //0xFF0E, //fullwidth full stop
+ 0xFF1F, //fullwidth question mark
+ 0xFF3D, //fullwidth right square bracket
+ 0xFF5D, //fullwidth right curly bracket
+ //0xFF61, //halfwidth ideographic full stop
+ 0xFF63, //halfwidth right corner bracket
+ //0xFF64, //halfwidth ideographic comma
+ 0xFF67, //halfwidth katakana letter small a
+ 0xFF68, //halfwidth katakana letter small i
+ 0xFF69, //halfwidth katakana letter small u
+ 0xFF6a, //halfwidth katakana letter small e
+ 0xFF6b, //halfwidth katakana letter small o
+ 0xFF6c, //halfwidth katakana letter small ya
+ 0xFF6d, //halfwidth katakana letter small yu
+ 0xFF6e, //halfwidth katakana letter small yo
+ 0xFF6f, //halfwidth katakana letter small tu
+ 0xFF70 //halfwidth katakana-hiragana prolonged sound mark
+ };
+
+ // characters that aren't covered by QChar::Punctuation_Open
+ const ushort dontbreakafter[] = {
+ 0x3012, //postal mark
+ 0xFF03, //full width pound mark
+ 0xFF04, //full width dollar sign
+ 0xFF20, //full width @
+ 0xFFE1, //full width british pound sign
+ 0xFFE5 //full width yen sign
+ };
+
+ inline bool break_bsearch( const ushort* arr, const ushort val ) {
+ int left = 0;
+ int right = (sizeof(arr) / sizeof(ushort)) - 1;
+
+ while (1) {
+ if (left == right)
+ return val != arr[left];
+
+ int i = (left + right) >> 1;
+ if ( val == arr[i] )
+ return false;
+ if ( val < arr[i] )
+ right = i;
+ else
+ left = i + 1;
+ }
+ }
+
+ bool isBreakableThai( const QChar *string, const int pos, const int len);
+ void cleanup_thaibreaks();
+
+ inline bool isBreakable( const QChar *str, const int pos, int len )
+ {
+ const QChar *c = str+pos;
+ unsigned short ch = c->unicode();
+ if ( ch > 0xff ) {
+ // not latin1, need to do more sophisticated checks for asian fonts
+ unsigned char row = c->row();
+ if ( row == 0x0e ) {
+ // 0e00 - 0e7f == Thai
+ if ( c->cell() < 0x80 ) {
+ // consult libthai
+ return isBreakableThai(str, pos, len);
+ } else
+ return false;
+ }
+ if ( row > 0x2d && row < 0xfb || row == 0x11 ) {
+ /* asian line breaking. */
+ if ( pos == 0 )
+ return false; // never break before first character
+
+ // check for simple punctuation cases
+ QChar::Category cat = c->category();
+ if ( cat == QChar::Punctuation_Close ||
+ cat == QChar::Punctuation_Other ||
+ (str+(pos-1))->category() == QChar::Punctuation_Open )
+ return false;
+
+ // do binary search in dontbreak[]
+ return break_bsearch(dontbreakbefore, c->unicode()) &&
+ break_bsearch(dontbreakafter, (str+(pos-1))->unicode());
+ } else // no asian font
+ return c->isSpace();
+ } else {
+ if ( ch == ' ' || ch == '\n' )
+ return true;
+ }
+ return false;
+ }
+
+}
+
+#endif
diff --git a/khtml/rendering/counter_tree.cpp b/khtml/rendering/counter_tree.cpp
new file mode 100644
index 000000000..5b178d96e
--- /dev/null
+++ b/khtml/rendering/counter_tree.cpp
@@ -0,0 +1,222 @@
+/*
+ * This file is part of the HTML rendering engine for KDE.
+ *
+ * Copyright (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com)
+ *
+ * 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 "rendering/counter_tree.h"
+#include "rendering/render_object.h"
+
+namespace khtml {
+
+CounterNode::CounterNode(RenderObject *o)
+ : m_hasCounters(false), m_isVisual(false),
+ m_value(0), m_count(0), m_parent(0), m_previous(0), m_next(0),
+ m_renderer(o) {}
+
+CounterNode::~CounterNode()
+{
+ if (m_parent) m_parent->removeChild(this);
+}
+
+void CounterNode::insertAfter ( CounterNode *, CounterNode *)
+{
+ Q_ASSERT( false);
+}
+
+void CounterNode::removeChild ( CounterNode *)
+{
+ Q_ASSERT( false);
+}
+
+void CounterNode::remove ()
+{
+ if (m_parent) m_parent->removeChild(this);
+ else {
+ Q_ASSERT(isReset());
+ Q_ASSERT(!firstChild());
+ Q_ASSERT(!lastChild());
+ }
+}
+
+void CounterNode::setHasCounters ()
+{
+ m_hasCounters = true;
+ if (parent())
+ parent()->setHasCounters();
+}
+
+void CounterNode::recount (bool first)
+{
+ int old_count = m_count;
+ if (m_previous)
+ m_count = m_previous->count() + m_value;
+ else {
+ assert(m_parent->firstChild() == this);
+ m_count = m_parent->value() + m_value;
+ }
+ if (old_count != m_count && !first)
+ setSelfDirty();
+ if (old_count != m_count || first) {
+ if (m_parent) m_parent->updateTotal(m_count);
+ if (m_next) m_next->recount();
+ }
+}
+
+void CounterNode::setSelfDirty ()
+{
+ if (m_renderer && m_isVisual)
+ m_renderer->setNeedsLayoutAndMinMaxRecalc();
+}
+
+void CounterNode::setParentDirty ()
+{
+ if (m_renderer && m_isVisual && m_hasCounters)
+ m_renderer->setNeedsLayoutAndMinMaxRecalc();
+}
+
+CounterReset::CounterReset(RenderObject *o) : CounterNode(o), m_total(0), m_first(0), m_last(0) {}
+CounterReset::~CounterReset() {}
+
+void CounterReset::insertAfter ( CounterNode *newChild, CounterNode *refChild )
+{
+ Q_ASSERT( newChild );
+ Q_ASSERT( !refChild || refChild->parent() == this );
+
+ newChild->m_parent = this;
+ newChild->m_previous = refChild;
+
+ if (refChild) {
+ newChild->m_next = refChild->m_next;
+ refChild->m_next = newChild;
+ } else {
+ newChild->m_next = m_first;
+ m_first = newChild;
+ }
+
+ if (newChild->m_next) {
+ assert(newChild->m_next->m_previous == refChild);
+ newChild->m_next->m_previous = newChild;
+ }
+ else {
+ assert (m_last == refChild);
+ m_last = newChild;
+ }
+
+ newChild->recount(true);
+}
+
+void CounterReset::removeChild ( CounterNode *oldChild )
+{
+ Q_ASSERT( oldChild );
+
+ CounterNode* next = oldChild->m_next;
+ CounterNode* prev = oldChild->m_previous;
+
+ if (oldChild->firstChild()) {
+ CounterNode* first = oldChild->firstChild();
+ CounterNode* last = oldChild->lastChild();
+ if (prev) {
+ prev->m_next = first;
+ first->m_previous = prev;
+ }
+ else {
+ assert ( m_first == oldChild );
+ m_first = first;
+ }
+
+ if (next) {
+ next->m_previous = last;
+ last->m_next = next;
+ }
+ else {
+ assert ( m_last == oldChild );
+ m_last = last;
+ }
+
+ next = first;
+ while (next) {
+ next->m_parent = this;
+ if (next == last) break;
+ next = next->m_next;
+ }
+
+ first->recount(true);
+ }
+ else {
+ if (prev) prev->m_next = next;
+ else {
+ assert ( m_first == oldChild );
+ m_first = next;
+ }
+ if (next) next->m_previous = prev;
+ else {
+ assert ( m_last == oldChild );
+ m_last = prev;
+ }
+ if (next)
+ next->recount();
+ }
+
+
+ oldChild->m_next = 0;
+ oldChild->m_previous = 0;
+ oldChild->m_parent = 0;
+}
+
+void CounterReset::recount (bool first)
+{
+ int old_count = m_count;
+ if (m_previous)
+ m_count = m_previous->count();
+ else if (m_parent)
+ m_count = m_parent->value();
+ else
+ m_count = 0;
+
+ updateTotal(m_value);
+ if (!first) setSelfDirty();
+ if (first || m_count != old_count) {
+ if (m_next) m_next->recount();
+ }
+}
+
+void CounterReset::setSelfDirty ()
+{
+ setParentDirty();
+}
+
+void CounterReset::setParentDirty ()
+{
+ if (hasCounters()) {
+ if (m_renderer && m_isVisual) m_renderer->setNeedsLayoutAndMinMaxRecalc();
+ CounterNode* n = firstChild();
+ for(; n; n = n->nextSibling())
+ {
+ n->setParentDirty();
+ }
+ }
+}
+
+void CounterReset::updateTotal (int value)
+{
+ if (value > m_total) m_total = value;
+}
+
+} // namespace
diff --git a/khtml/rendering/counter_tree.h b/khtml/rendering/counter_tree.h
new file mode 100644
index 000000000..55b924b80
--- /dev/null
+++ b/khtml/rendering/counter_tree.h
@@ -0,0 +1,114 @@
+/*
+ * This file is part of the HTML rendering engine for KDE.
+ *
+ * Copyright (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef _Counter_Tree_h_
+#define _Counter_Tree_h_
+
+#include "misc/shared.h"
+#include "rendering/render_object.h"
+
+namespace khtml {
+
+class CounterReset;
+
+// This file implements a counter-tree that is used for finding all parents in counters() lookup,
+// and for propagating count-changes when nodes are added or removed.
+// Please note that only counter-reset and root can be parents here, and that render-tree parents
+// are just counter-tree siblings
+
+// Implementation of counter-increment and counter-content
+class CounterNode
+{
+public:
+ CounterNode(RenderObject *o);
+ virtual ~CounterNode();
+
+ CounterReset* parent() const { return m_parent; }
+ CounterNode* previousSibling() const { return m_previous; }
+ CounterNode* nextSibling() const { return m_next; }
+ virtual CounterNode* firstChild() const { return 0; } ;
+ virtual CounterNode* lastChild() const { return 0; };
+ virtual void insertAfter ( CounterNode *newChild, CounterNode *refChild );
+ virtual void removeChild ( CounterNode *oldChild );
+ // Convenient self-refering version of the above
+ void remove();
+
+ int value() const { return m_value; };
+ void setValue(short v) { m_value = v; };
+ int count() const { return m_count; };
+
+ virtual bool isReset() { return false; };
+ virtual void recount( bool first = false );
+ virtual void setSelfDirty();
+ virtual void setParentDirty();
+
+ bool hasCounters() const { return m_hasCounters; };
+ bool isVisual() const { return m_isVisual; };
+ void setHasCounters();
+ void setIsVisual() { m_isVisual = true; };
+ bool isRoot() { return m_renderer && m_renderer->isRoot(); };
+
+ void setRenderer(RenderObject *o) { m_renderer = o; };
+ RenderObject* renderer() const { return m_renderer; };
+
+ friend class CounterReset;
+protected:
+ bool m_hasCounters : 1;
+ bool m_isVisual : 1;
+ short m_value;
+ short m_count;
+ CounterReset *m_parent;
+ CounterNode *m_previous;
+ CounterNode *m_next;
+ RenderObject *m_renderer;
+};
+
+// Implementation of counter-reset and root
+class CounterReset : public CounterNode
+{
+public:
+ CounterReset(RenderObject *o);
+ virtual ~CounterReset();
+
+ virtual CounterNode *firstChild() const { return m_first; };
+ virtual CounterNode *lastChild() const { return m_last; };
+ virtual void insertAfter ( CounterNode *newChild, CounterNode *refChild );
+ virtual void removeChild ( CounterNode *oldChild );
+
+ virtual bool isReset() { return true; };
+ virtual void recount( bool first = false );
+ virtual void setSelfDirty();
+ virtual void setParentDirty();
+
+ void updateTotal(int value);
+ // The highest value among children
+ int total() const { return m_total; };
+
+protected:
+ int m_total;
+ CounterNode *m_first;
+ CounterNode *m_last;
+};
+
+} // namespace
+
+#endif
+
diff --git a/khtml/rendering/enumerate.cpp b/khtml/rendering/enumerate.cpp
new file mode 100644
index 000000000..75af71465
--- /dev/null
+++ b/khtml/rendering/enumerate.cpp
@@ -0,0 +1,411 @@
+/**
+ * This file is part of the HTML rendering engine for KDE.
+ *
+ * Copyright (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com)
+ *
+ * (C) Hebrew algorithm by herouth@netvision.net.il
+ * and schlpbch@iam.unibe.ch
+ *
+ * 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 "rendering/enumerate.h"
+
+#include <qstring.h>
+#include <qvaluelist.h>
+
+namespace khtml {
+
+namespace Enumerate {
+
+QString toRoman( int number, bool upper )
+{
+ if (number < 1 || number > 3999) return QString::number(number);
+ QString roman;
+ static const QChar ldigits[] = { 'i', 'v', 'x', 'l', 'c', 'd', 'm' };
+ static const QChar udigits[] = { 'I', 'V', 'X', 'L', 'C', 'D', 'M' };
+ const QChar *digits = upper ? udigits : ldigits;
+ int i, d = 0;
+
+ do
+ {
+ int num = number % 10;
+
+ if ( num % 5 < 4 )
+ for ( i = num % 5; i > 0; i-- )
+ roman.prepend( digits[ d ] );
+
+ if ( num >= 4 && num <= 8)
+ roman.prepend( digits[ d+1 ] );
+
+ if ( num == 9 )
+ roman.prepend( digits[ d+2 ] );
+
+ if ( num % 5 == 4 )
+ roman.prepend( digits[ d ] );
+
+ number /= 10;
+ d += 2;
+ }
+ while ( number );
+
+ return roman;
+}
+
+QString toGeorgian( int number )
+{
+ QString georgian;
+ const QChar tenthousand = 0x10ef;
+ static const QChar thousands[9] = {0x10e8, 0x10e9, 0x10ea, 0x10eb, 0x10ec,
+ 0x10ed, 0x10ee, 0x10f4, 0x10f5 };
+ static const QChar hundreds[9] = {0x10e0, 0x10e1, 0x10e2, 0x10e3, 0x10f3,
+ 0x10e4, 0x10e5, 0x10e6, 0x10e7 };
+ static const QChar tens[9] = {0x10d8, 0x10d9, 0x10da, 0x10db, 0x10dc,
+ 0x10f2, 0x10dd, 0x10de, 0x10df };
+ static const QChar units[9] = {0x10d0, 0x10d1, 0x10d2, 0x10d3, 0x10d4,
+ 0x10d5, 0x10d6, 0x10f1, 0x10d7 };
+
+ if (number < 1 || number > 19999) return QString::number(number);
+ if (number >= 10000) {
+ georgian.append(tenthousand);
+ number = number - 10000;
+ }
+ if (number >= 1000) {
+ georgian.append(thousands[number/1000-1]);
+ number = number % 1000;
+ }
+ if (number >= 100) {
+ georgian.append(hundreds[number/100-1]);
+ number = number % 100;
+ }
+ if (number >= 10) {
+ georgian.append(tens[number/10-1]);
+ number = number % 10;
+ }
+ if (number >= 1) {
+ georgian.append(units[number-1]);
+ }
+
+ return georgian;
+}
+
+QString toArmenian( int number )
+{
+ QString armenian;
+ int onethousand = 0x57c;
+ int hundreds = 0x572;
+ int tens = 0x569;
+ int units = 0x560;
+
+ // The standard defines values over 1999, but 7000 is very hard to render
+ if (number < 1 || number > 1999) return QString::number(number);
+ if (number >= 1000) {
+ armenian.append(QChar(onethousand));
+ number = number - 1000;
+ }
+ if (number >= 100) {
+ armenian.append(QChar(hundreds+number/100));
+ number = number % 100;
+ }
+ if (number >= 10) {
+ armenian.append(QChar(tens+number/10));
+ number = number % 10;
+ }
+ if (number >= 1) {
+ armenian.append(QChar(units+number));
+ }
+
+ return armenian;
+}
+
+QString toHebrew( int number ) {
+ static const QChar tenDigit[] = {1497, 1499, 1500, 1502, 1504, 1505, 1506, 1508, 1510};
+
+ QString letter;
+ if (number < 1) return QString::number(number);
+ if (number>999) {
+ letter = toHebrew(number/1000) + QString::fromLatin1("'");
+ number = number%1000;
+ }
+
+ int hunderts = (number/400);
+ if (hunderts > 0) {
+ for(int i=0; i<hunderts; i++) {
+ letter += QChar(1511 + 3);
+ }
+ }
+ number = number % 400;
+ if ((number / 100) != 0) {
+ letter += QChar (1511 + (number / 100) -1);
+ }
+ number = number % 100;
+ int tens = number/10;
+ if (tens > 0 && !(number == 15 || number == 16)) {
+ letter += tenDigit[tens-1];
+ }
+ if (number == 15 || number == 16) { // special because of religious
+ letter += QChar(1487 + 9); // reasons
+ letter += QChar(1487 + number - 9);
+ } else {
+ number = number % 10;
+ if (number != 0) {
+ letter += QChar (1487 + number);
+ }
+ }
+ return letter;
+}
+
+static inline QString toLatin( int number, int base ) {
+ if (number < 1) return QString::number(number);
+ QValueList<QChar> letters;
+ while(number > 0) {
+ number--; // number 0 is letter a
+ QChar letter = (QChar) (base + (number % 26));
+ letters.prepend(letter);
+ number /= 26;
+ }
+ QString str;
+ str.setLength(letters.size());
+ int i=0;
+ while(!letters.isEmpty()) {
+ str[i++] = letters.front();
+ letters.pop_front();
+ }
+ return str;
+}
+
+QString toLowerLatin( int number ) {
+ return toLatin( number, 'a' );
+}
+
+QString toUpperLatin( int number ) {
+ return toLatin( number, 'A' );
+}
+
+static inline QString toAlphabetic( int number, int base, const QChar alphabet[] ) {
+ if (number < 1) return QString::number(number);
+ QValueList<QChar> letters;
+ while(number > 0) {
+ number--; // number 0 is letter 1
+ QChar letter = alphabet[number % base];
+ letters.prepend(letter);
+ number /= base;
+ }
+ QString str;
+ str.setLength(letters.size());
+ int i=0;
+ while(!letters.isEmpty()) {
+ str[i++] = letters.front();
+ letters.pop_front();
+ }
+ return str;
+}
+
+QString toHiragana( int number ) {
+ static const QChar hiragana[48] = {0x3042, 0x3044, 0x3046, 0x3048, 0x304A, 0x304B, 0x304D,
+ 0x304F, 0x3051, 0x3053, 0x3055, 0x3057, 0x3059, 0x305B, 0x305D,
+ 0x305F, 0x3061, 0x3064, 0x3066, 0x3068, 0x306A, 0x306B,
+ 0x306C, 0x306D, 0x306E, 0x306F, 0x3072, 0x3075, 0x3078,
+ 0x307B, 0x307E, 0x307F, 0x3080, 0x3081, 0x3082, 0x3084, 0x3086,
+ 0x3088, 0x3089, 0x308A, 0x308B, 0x308C, 0x308D, 0x308F,
+ 0x3090, 0x3091, 0x9092, 0x3093};
+ return toAlphabetic( number, 48, hiragana );
+}
+
+QString toHiraganaIroha( int number ) {
+ static const QChar hiragana[47] = {0x3044, 0x308D, 0x306F, 0x306B, 0x307B, 0x3078, 0x3068,
+ 0x3061, 0x308A, 0x306C, 0x308B, 0x3092, 0x308F, 0x304B,
+ 0x3088, 0x305F, 0x308C, 0x305D, 0x3064, 0x306D, 0x306A,
+ 0x3089, 0x3080, 0x3046, 0x3090, 0x306E, 0x304A, 0x304F, 0x3084,
+ 0x307E, 0x3051, 0x3075, 0x3053, 0x3048, 0x3066, 0x3042, 0x3055,
+ 0x304D, 0x3086, 0x3081, 0x307F, 0x3057, 0x3091, 0x3072, 0x3082,
+ 0x305B, 0x3059 };
+ return toAlphabetic( number, 47, hiragana );
+}
+
+QString toKatakana( int number ) {
+ static const QChar katakana[48] = {0x30A2, 0x30A4, 0x30A6, 0x30A8, 0x30AA, 0x30AB, 0x30AD,
+ 0x30AF, 0x30B1, 0x30B3, 0x30B5, 0x30B7, 0x30B9, 0x30BB,
+ 0x30BD, 0x30BF, 0x30C1, 0x30C4, 0x30C6, 0x30C8, 0x30CA,
+ 0x30CB, 0x30CC, 0x30CD, 0x30CE, 0x30CF, 0x30D2, 0x30D5,
+ 0x30D8, 0x30DB, 0x30DE, 0x30DF, 0x30E0, 0x30E1, 0x30E2,
+ 0x30E4, 0x30E6, 0x30E8, 0x30E9, 0x30EA, 0x30EB, 0x30EC,
+ 0x30ED, 0x30EF, 0x30F0, 0x30F1, 0x90F2, 0x30F3};
+ return toAlphabetic( number, 48, katakana );
+}
+
+QString toKatakanaIroha( int number ) {
+ static const QChar katakana[47] = {0x30A4, 0x30ED, 0x30CF, 0x30CB, 0x30DB, 0x30D8, 0x30C8,
+ 0x30C1, 0x30EA, 0x30CC, 0x30EB, 0x30F2, 0x30EF, 0x30AB,
+ 0x30E8, 0x30BF, 0x30EC, 0x30ED, 0x30C4, 0x30CD, 0x30CA,
+ 0x30E9, 0x30E0, 0x30A6, 0x30F0, 0x30CE, 0x30AA, 0x30AF,
+ 0x30E4, 0x30DE, 0x30B1, 0x30D5, 0x30B3, 0x30A8, 0x30C6,
+ 0x30A2, 0x30B5, 0x30AD, 0x30E6, 0x30E1, 0x30DF, 0x30B7,
+ 0x30F1, 0x30D2, 0x30E2, 0x30BB, 0x90B9};
+ return toAlphabetic( number, 47, katakana );
+}
+
+QString toLowerGreek( int number ) {
+ static const QChar greek[24] = { 0x3B1, 0x3B2, 0x3B3, 0x3B4, 0x3B5, 0x3B6, 0x3B7,
+ 0x3B8, 0x3B9, 0x3BA, 0x3BB, 0x3BC, 0x3BD, 0x3BE,
+ 0x3BF, 0x3C0, 0x3C1, 0x3C3, 0x3C4, 0x3C5, 0x3C6,
+ 0x3C7, 0x3C8, 0x3C0};
+
+ return toAlphabetic( number, 24, greek );
+}
+
+QString toUpperGreek( int number ) {
+ // The standard claims to be base 24, but only lists 19 letters.
+ static const QChar greek[19] = { 0x391, 0x392, 0x393, 0x394, 0x395, 0x396, 0x397, 0x398,
+ 0x399, 0x39A, 0x39B, 0x39C, 0x39D, 0x39E, 0x39F,
+ 0x3A0, 0x3A1, 0x3A3, 0x3A9};
+
+ return toAlphabetic( number, 19, greek );
+}
+
+static inline QString toNumeric( int number, int base ) {
+ QString letter = QString::number(number);
+ for(unsigned int i = 0; i < letter.length(); i++) {
+ if (letter[i].isDigit())
+ letter[i] = QChar(letter[i].digitValue()+base);
+ }
+ return letter;
+}
+
+QString toArabicIndic( int number ) {
+ return toNumeric(number, 0x660);
+}
+
+QString toPersianUrdu( int number ) {
+ return toNumeric(number, 0x6F0);
+}
+
+QString toLao( int number ) {
+ return toNumeric(number, 0xED0);
+}
+
+QString toThai( int number ) {
+ return toNumeric(number, 0xE50);
+}
+
+QString toTibetan( int number ) {
+ return toNumeric(number, 0xF20);
+}
+
+static inline QString toIdeographic(int number, const QChar digits[], const QChar digitmarkers[]) {
+ if (number < 0 || number > 9999) return QString::number(number);
+
+ QString grp = QString::number(number);
+
+ // ### Append group markers to handle numbers > 9999
+
+ QString str;
+
+ // special case
+ if (number < 20 && number >= 10) {
+ str.append(digitmarkers[0]);
+ str.append(digits[grp[1].digitValue()]);
+ return str;
+ }
+
+ int len = grp.length();
+ bool collapseZero = false;
+ for (int i = 0; i < len ; i++) {
+ int digit = grp[i].digitValue();
+ // Add digit markers to digits > 0
+ if ((len-i-1) > 0 && digit > 0)
+ str.append(digitmarkers[(len-i-2)]);
+ // Add digit, but collapse consecutive zeros
+ if (!collapseZero || digit > 0) {
+ str.append(digits[digit]);
+
+ if (digit == 0)
+ collapseZero = true;
+ else
+ collapseZero = false;
+ }
+ }
+ return str;
+}
+
+QString toTradChineseFormal( int number ) {
+// static const QChar groupMarkers[3] = {0x4e07, 0x4ebf, 0x5146};
+ static const QChar digitMarkers[3] = {0x4e07, 0x4ebf, 0x5146};
+ static const QChar digits[10] = {0x96f6, 0x4e00,
+ 0x4ebc, 0x4e09,
+ 0x56db, 0x4e94,
+ 0x516d, 0x4e03,
+ 0x516b, 0x4e5d};
+ return toIdeographic(number, digits, digitMarkers);
+}
+
+QString toTradChineseInformal( int number ) {
+// static const QChar groupMarkers[3] = {0x842c, 0x5104, 0x5146};
+ static const QChar digitMarkers[3] = {0x842c, 0x5104, 0x5146};
+ static const QChar digits[10] = {0x96f6, 0x4e00,
+ 0x4ebc, 0x4e09,
+ 0x56db, 0x4e94,
+ 0x516d, 0x4e03,
+ 0x516b, 0x4e5d};
+ return toIdeographic(number, digits, digitMarkers);
+}
+
+QString toSimpChineseFormal( int number ) {
+// static const QChar groupMarkers[3] = {0x4e07, 0x5104, 0x5146};
+ static const QChar digitMarkers[3] = {0x4e07, 0x4ebf, 0x5146};
+ static const QChar digits[10] = {0x96f6, 0x58f9,
+ 0x8cb3, 0x53c3,
+ 0x8086, 0x4f0d,
+ 0x9678, 0x67d2,
+ 0x634c, 0x7396};
+ return toIdeographic(number, digits, digitMarkers);
+}
+
+QString toSimpChineseInformal( int number ) {
+// static const QChar groupMarkers[3] = {0x842c, 0x5104, 0x5146};
+ static const QChar digitMarkers[3] = {0x842c, 0x5104, 0x5146};
+ static const QChar digits[10] = {0x96f6, 0x58f9,
+ 0x8cb3, 0x53c3,
+ 0x8086, 0x4f0d,
+ 0x9678, 0x67d2,
+ 0x634c, 0x7396};
+ return toIdeographic(number, digits, digitMarkers);
+}
+
+QString toJapaneseFormal( int number ) {
+// static const QChar groupMarkers[3] = {0x4e07, 0x5104, 0x5146};
+ static const QChar digitMarkers[3] = {0x62fe, 0x4f70, 0x4edf};
+ static const QChar digits[10] = {0x96f6, 0x58f9,
+ 0x8cb3, 0x53c3,
+ 0x8086, 0x4f0d,
+ 0x9678, 0x67d2,
+ 0x634c, 0x7396};
+ return toIdeographic(number, digits, digitMarkers);
+}
+
+QString toJapaneseInformal( int number ) {
+// static const QChar groupMarkers[3] = {0x842c, 0x5104, 0x5146};
+ static const QChar digitMarkers[3] = {0x842c, 0x5104, 0x5146};
+ static const QChar digits[10] = {0x96f6, 0x58f9,
+ 0x8d30, 0x53c1,
+ 0x8086, 0x4f0d,
+ 0x9646, 0x67d2,
+ 0x634c, 0x7396};
+ return toIdeographic(number, digits, digitMarkers);
+}
+
+}} // namespace
diff --git a/khtml/rendering/enumerate.h b/khtml/rendering/enumerate.h
new file mode 100644
index 000000000..518a8e9d6
--- /dev/null
+++ b/khtml/rendering/enumerate.h
@@ -0,0 +1,66 @@
+/**
+ * This file is part of the HTML rendering engine for KDE.
+ *
+ * Copyright (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ENUMERATE_H
+#define ENUMERATE_H
+
+class QChar;
+class QString;
+
+namespace khtml {
+
+namespace Enumerate {
+
+// Numeric
+ QString toArabicIndic( int number );
+ QString toLao( int number );
+ QString toPersianUrdu( int number );
+ QString toThai( int number );
+ QString toTibetan( int number );
+
+// Alphabetic
+ QString toLowerLatin( int number );
+ QString toUpperLatin( int number );
+ QString toLowerGreek( int number );
+ QString toUpperGreek( int number );
+ QString toHiragana( int number );
+ QString toHiraganaIroha( int number );
+ QString toKatakana( int number );
+ QString toKatakanaIroha( int number );
+
+// Algorithmic
+ QString toRoman( int number, bool upper );
+ QString toHebrew( int number );
+ QString toGeorgian( int number );
+ QString toArmenian( int number );
+
+// Ideographic
+ QString toJapaneseFormal ( int number );
+ QString toJapaneseInformal ( int number );
+ QString toSimpChineseFormal ( int number );
+ QString toSimpChineseInformal ( int number );
+ QString toTradChineseFormal ( int number );
+ QString toTradChineseInformal ( int number );
+
+}} // namespaces
+
+#endif
diff --git a/khtml/rendering/font.cpp b/khtml/rendering/font.cpp
new file mode 100644
index 000000000..cdd580596
--- /dev/null
+++ b/khtml/rendering/font.cpp
@@ -0,0 +1,502 @@
+/**
+ * This file is part of the html renderer for KDE.
+ *
+ * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#endif
+
+#include "font.h"
+#include "khtml_factory.h"
+#include "khtml_settings.h"
+
+#include <kdebug.h>
+#include <kglobal.h>
+
+#include <qpainter.h>
+#include <qfontdatabase.h>
+#include <qpaintdevicemetrics.h>
+
+using namespace khtml;
+
+/** closes the current word and returns its width in pixels
+ * @param fm metrics of font to be used
+ * @param str string
+ * @param pos zero-indexed position within @p str upon which all other
+ * indices are based
+ * @param wordStart relative index pointing to the position where the word started
+ * @param wordEnd relative index pointing one position after the word ended
+ * @return the width in pixels. May be 0 if @p wordStart and @p wordEnd were
+ * equal.
+ */
+inline int closeWordAndGetWidth(const QFontMetrics &fm, const QChar *str, int pos,
+ int wordStart, int wordEnd)
+{
+ if (wordEnd <= wordStart) return 0;
+
+ QConstString s(str + pos + wordStart, wordEnd - wordStart);
+ return fm.width(s.string());
+}
+
+/** closes the current word and draws it
+ * @param p painter
+ * @param d text direction
+ * @param x current x position, will be inc-/decremented correctly according
+ * to text direction
+ * @param y baseline of text
+ * @param widths list of widths; width of word is expected at position
+ * wordStart
+ * @param str string
+ * @param pos zero-indexed position within @p str upon which all other
+ * indices are based
+ * @param wordStart relative index pointing to the position where the word started,
+ * will be set to wordEnd after function
+ * @param wordEnd relative index pointing one position after the word ended
+ */
+inline void closeAndDrawWord(QPainter *p, QPainter::TextDirection d,
+ int &x, int y, const short widths[], const QChar *str, int pos,
+ int &wordStart, int wordEnd)
+{
+ if (wordEnd <= wordStart) return;
+
+ int width = widths[wordStart];
+ if (d == QPainter::RTL)
+ x -= width;
+
+ QConstString s(str + pos + wordStart, wordEnd - wordStart);
+ p->drawText(x, y, s.string(), -1, d);
+
+ if (d != QPainter::RTL)
+ x += width;
+
+ wordStart = wordEnd;
+}
+
+void Font::drawText( QPainter *p, int x, int y, QChar *str, int slen, int pos, int len,
+ int toAdd, QPainter::TextDirection d, int from, int to, QColor bg, int uy, int h, int deco ) const
+{
+ if (!str) return;
+ QConstString cstr = QConstString(str, slen);
+ QString qstr = cstr.string();
+
+ // ### fixme for RTL
+ if ( !scFont && !letterSpacing && !wordSpacing && !toAdd && from==-1 ) {
+ // simply draw it
+ // Due to some unfounded cause QPainter::drawText traverses the
+ // *whole* string when painting, not only the specified
+ // [pos, pos + len) segment. This makes painting *extremely* slow for
+ // long render texts (in the order of several megabytes).
+ // Hence, only hand over the piece of text of the actual inline text box
+ QConstString cstr = QConstString(str + pos, len);
+ p->drawText( x, y, cstr.string(), 0, len, d );
+ } else {
+ if (from < 0) from = 0;
+ if (to < 0) to = len;
+
+ int numSpaces = 0;
+ if ( toAdd ) {
+ for( int i = 0; i < len; ++i )
+ if ( str[i+pos].category() == QChar::Separator_Space )
+ ++numSpaces;
+ }
+
+ const int totWidth = width( str, slen, pos, len );
+ if ( d == QPainter::RTL ) {
+ x += totWidth + toAdd;
+ }
+ QString upper = qstr;
+ QFontMetrics sc_fm = fm;
+ if ( scFont ) {
+ // draw in small caps
+ upper = qstr.upper();
+ sc_fm = QFontMetrics( *scFont );
+ }
+
+ // ### sc could be optimized by only painting uppercase letters extra,
+ // and treat the rest WordWise, but I think it's not worth it.
+ // Somebody else may volunteer, and implement this ;-) (LS)
+
+ // The mode determines whether the text is displayed character by
+ // character, word by word, or as a whole
+ enum { CharacterWise, WordWise, Whole }
+ mode = Whole;
+ if (!letterSpacing && !scFont && (wordSpacing || toAdd > 0))
+ mode = WordWise;
+ else if (letterSpacing || scFont)
+ mode = CharacterWise;
+
+ if (mode == Whole) { // most likely variant is treated extra
+
+ if (to < 0) to = len;
+ const QConstString cstr(str + pos, len);
+ const QConstString segStr(str + pos + from, to - from);
+ const int preSegmentWidth = fm.width(cstr.string(), from);
+ const int segmentWidth = fm.width(segStr.string());
+ const int eff_x = d == QPainter::RTL ? x - preSegmentWidth - segmentWidth
+ : x + preSegmentWidth;
+
+ // draw whole string segment, with optional background
+ if ( bg.isValid() )
+ p->fillRect( eff_x, uy, segmentWidth, h, bg );
+ p->drawText(eff_x, y, segStr.string(), -1, d);
+ if (deco)
+ drawDecoration(p, eff_x, uy, y - uy, segmentWidth - 1, h, deco);
+ return;
+ }
+
+ // We are using two passes. In the first pass, the widths are collected,
+ // and stored. In the second, the actual characters are drawn.
+
+ // For each letter in the text box, save the width of the character.
+ // When word-wise, only the first letter contains the width, but of the
+ // whole word.
+ short* const widthList = (short *)alloca(to*sizeof(short));
+
+ // First pass: gather widths
+ int preSegmentWidth = 0;
+ int segmentWidth = 0;
+ int lastWordBegin = 0;
+ bool onSegment = from == 0;
+ for( int i = 0; i < to; ++i ) {
+ if (i == from) {
+ // Also close words on visibility boundary
+ if (mode == WordWise) {
+ const int width = closeWordAndGetWidth(fm, str, pos, lastWordBegin, i);
+
+ if (lastWordBegin < i) {
+ widthList[lastWordBegin] = (short)width;
+ lastWordBegin = i;
+ preSegmentWidth += width;
+ }
+ }
+ onSegment = true;
+ }
+
+ const QChar ch = str[pos+i];
+ bool lowercase = (ch.category() == QChar::Letter_Lowercase);
+ bool is_space = (ch.category() == QChar::Separator_Space);
+ int chw = 0;
+ if ( letterSpacing )
+ chw += letterSpacing;
+ if ( (wordSpacing || toAdd) && is_space ) {
+ if (mode == WordWise) {
+ const int width = closeWordAndGetWidth(fm, str, pos, lastWordBegin, i);
+ if (lastWordBegin < i) {
+ widthList[lastWordBegin] = (short)width;
+ lastWordBegin = i;
+ (onSegment ? segmentWidth : preSegmentWidth) += width;
+ }
+ ++lastWordBegin; // ignore this space
+ }
+ chw += wordSpacing;
+ if ( numSpaces ) {
+ const int a = toAdd/numSpaces;
+ chw += a;
+ toAdd -= a;
+ --numSpaces;
+ }
+ }
+ if (is_space || mode == CharacterWise) {
+ chw += lowercase ? sc_fm.charWidth( upper, pos+i ) : fm.charWidth( qstr, pos+i );
+ widthList[i] = (short)chw;
+
+ (onSegment ? segmentWidth : preSegmentWidth) += chw;
+ }
+
+ }
+
+ // close last word
+ Q_ASSERT(onSegment);
+ if (mode == WordWise) {
+ const int width = closeWordAndGetWidth(fm, str, pos, lastWordBegin, to);
+ segmentWidth += width;
+ widthList[lastWordBegin] = (short)width;
+ }
+
+ if (d == QPainter::RTL) x -= preSegmentWidth;
+ else x += preSegmentWidth;
+
+ const int startx = d == QPainter::RTL ? x-segmentWidth : x;
+
+ // optionally draw background
+ if ( bg.isValid() )
+ p->fillRect( startx, uy, segmentWidth, h, bg );
+
+ // second pass: do the actual drawing
+ lastWordBegin = from;
+ for( int i = from; i < to; ++i ) {
+ const QChar ch = str[pos+i];
+ bool lowercase = (ch.category() == QChar::Letter_Lowercase);
+ bool is_space = ch.category() == QChar::Separator_Space;
+ if ( is_space ) {
+ if (mode == WordWise) {
+ closeAndDrawWord(p, d, x, y, widthList, str, pos, lastWordBegin, i);
+ ++lastWordBegin; // jump over space
+ }
+ }
+ if (is_space || mode == CharacterWise) {
+ const int chw = widthList[i];
+ if (d == QPainter::RTL)
+ x -= chw;
+
+ if ( scFont )
+ p->setFont( lowercase ? *scFont : f );
+ p->drawText( x, y, (lowercase ? upper : qstr), pos+i, 1, d );
+
+ if (d != QPainter::RTL)
+ x += chw;
+ }
+
+ }
+
+ // don't forget to draw last word
+ if (mode == WordWise) {
+ closeAndDrawWord(p, d, x, y, widthList, str, pos, lastWordBegin, to);
+ }
+
+ if (deco)
+ drawDecoration(p, startx, uy, y - uy, segmentWidth - 1, h, deco);
+
+ if ( scFont )
+ p->setFont( f );
+ }
+}
+
+
+int Font::width( QChar *chs, int, int pos, int len, int start, int end, int toAdd ) const
+{
+ const QConstString cstr(chs+pos, len);
+ int w = 0;
+
+ const QString qstr = cstr.string();
+ if ( scFont ) {
+ const QString upper = qstr.upper();
+ const QChar *uc = qstr.unicode();
+ const QFontMetrics sc_fm( *scFont );
+ for ( int i = 0; i < len; ++i ) {
+ if ( (uc+i)->category() == QChar::Letter_Lowercase )
+ w += sc_fm.charWidth( upper, i );
+ else
+ w += fm.charWidth( qstr, i );
+ }
+ } else {
+ // ### might be a little inaccurate
+ w = fm.width( qstr );
+ }
+
+ if ( letterSpacing )
+ w += len*letterSpacing;
+
+ if ( wordSpacing )
+ // add amount
+ for( int i = 0; i < len; ++i ) {
+ if( chs[i+pos].category() == QChar::Separator_Space )
+ w += wordSpacing;
+ }
+
+ if ( toAdd ) {
+ // first gather count of spaces
+ int numSpaces = 0;
+ for( int i = start; i != end; ++i )
+ if ( chs[i].category() == QChar::Separator_Space )
+ ++numSpaces;
+ // distribute pixels evenly among spaces, but count only those within
+ // [pos, pos+len)
+ for ( int i = start; numSpaces && i != pos + len; i++ )
+ if ( chs[i].category() == QChar::Separator_Space ) {
+ const int a = toAdd/numSpaces;
+ if ( i >= pos ) w += a;
+ toAdd -= a;
+ --numSpaces;
+ }
+ }
+
+ return w;
+}
+
+int Font::width( QChar *chs, int slen, int pos ) const
+{
+ int w;
+ if ( scFont && chs[pos].category() == QChar::Letter_Lowercase ) {
+ QString str( chs, slen );
+ str[pos] = chs[pos].upper();
+ w = QFontMetrics( *scFont ).charWidth( str, pos );
+ } else {
+ const QConstString cstr( chs, slen );
+ w = fm.charWidth( cstr.string(), pos );
+ }
+ if ( letterSpacing )
+ w += letterSpacing;
+
+ if ( wordSpacing && (chs+pos)->category() == QChar::Separator_Space )
+ w += wordSpacing;
+ return w;
+}
+
+/** Querying QFontDB whether something is scalable is expensive, so we cache. */
+struct Font::ScalKey
+{
+ QString family;
+ int weight;
+ int italic;
+
+ ScalKey(const QFont& font) :
+ family(font.family()), weight(font.weight()), italic(font.italic())
+ {}
+
+ ScalKey() {}
+
+ bool operator<(const ScalKey& other) const {
+ if (weight < other.weight)
+ return true;
+ if (weight > other.weight)
+ return false;
+
+ if (italic < other.italic)
+ return true;
+ if (italic > other.italic)
+ return false;
+
+ return family < other.family;
+ }
+
+ bool operator==(const ScalKey& other) const {
+ return (weight == other.weight &&
+ italic == other.italic &&
+ family == other.family);
+ }
+};
+
+QMap<Font::ScalKey, Font::ScalInfo>* Font::scalCache;
+QMap<Font::ScalKey, QValueList<int> >* Font::scalSizesCache;
+
+bool Font::isFontScalable(QFontDatabase& db, const QFont& font)
+{
+ if (!scalCache)
+ scalCache = new QMap<ScalKey, ScalInfo>;
+
+ ScalKey key(font);
+
+ ScalInfo &s = (*scalCache)[key];
+ if (s == Unknown) {
+ s = db.isSmoothlyScalable(font.family(), db.styleString(font)) ? Yes : No;
+
+ if (s == No) {
+ /* Cache size info */
+ if (!scalSizesCache)
+ scalSizesCache = new QMap<ScalKey, QValueList<int> >;
+ (*scalSizesCache)[key] = db.smoothSizes(font.family(), db.styleString(font));
+ }
+ }
+
+ return (s == Yes);
+}
+
+
+void Font::update( QPaintDeviceMetrics* devMetrics ) const
+{
+ f.setFamily( fontDef.family.isEmpty() ? KHTMLFactory::defaultHTMLSettings()->stdFontName() : fontDef.family );
+ f.setItalic( fontDef.italic );
+ f.setWeight( fontDef.weight );
+
+ QFontDatabase db;
+
+ int size = fontDef.size;
+ const int lDpiY = kMax(devMetrics->logicalDpiY(), 96);
+
+ // ok, now some magic to get a nice unscaled font
+ // all other font properties should be set before this one!!!!
+ if( !isFontScalable(db, f) )
+ {
+ const QValueList<int>& pointSizes = (*scalSizesCache)[ScalKey(f)];
+ // lets see if we find a nice looking font, which is not too far away
+ // from the requested one.
+ // kdDebug(6080) << "khtml::setFontSize family = " << f.family() << " size requested=" << size << endl;
+
+
+ float diff = 1; // ### 100% deviation
+ float bestSize = 0;
+
+ QValueList<int>::ConstIterator it = pointSizes.begin();
+ const QValueList<int>::ConstIterator itEnd = pointSizes.end();
+
+ for( ; it != itEnd; ++it )
+ {
+ float newDiff = ((*it)*(lDpiY/72.) - float(size))/float(size);
+ //kdDebug( 6080 ) << "smooth font size: " << *it << " diff=" << newDiff << endl;
+ if(newDiff < 0) newDiff = -newDiff;
+ if(newDiff < diff)
+ {
+ diff = newDiff;
+ bestSize = *it;
+ }
+ }
+ //kdDebug( 6080 ) << "best smooth font size: " << bestSize << " diff=" << diff << endl;
+ if ( bestSize != 0 && diff < 0.2 ) // 20% deviation, otherwise we use a scaled font...
+ size = (int)((bestSize*lDpiY) / 72);
+ }
+
+ // make sure we don't bust up X11
+ // Also, Qt does not support sizing a QFont to zero.
+ size = kMax(1, kMin(255, size));
+
+// qDebug("setting font to %s, italic=%d, weight=%d, size=%d", fontDef.family.latin1(), fontDef.italic,
+// fontDef.weight, size );
+
+
+ f.setPixelSize( size );
+
+ fm = QFontMetrics( f );
+
+ // small caps
+ delete scFont;
+ scFont = 0;
+
+ if ( fontDef.smallCaps ) {
+ scFont = new QFont( f );
+ scFont->setPixelSize( kMax(1, f.pixelSize()*7/10) );
+ }
+}
+
+void Font::drawDecoration(QPainter *pt, int _tx, int _ty, int baseline, int width, int height, int deco) const
+{
+ Q_UNUSED(height);
+ // thick lines on small fonts look ugly
+ const int thickness = fm.height() > 20 ? fm.lineWidth() : 1;
+ const QBrush brush = pt->pen().color();
+ if (deco & UNDERLINE) {
+ int underlineOffset = ( fm.height() + baseline ) / 2;
+ if (underlineOffset <= baseline) underlineOffset = baseline+1;
+
+ pt->fillRect(_tx, _ty + underlineOffset, width + 1, thickness, brush );
+ }
+ if (deco & OVERLINE) {
+ pt->fillRect(_tx, _ty, width + 1, thickness, brush );
+ }
+ if (deco & LINE_THROUGH) {
+ pt->fillRect(_tx, _ty + 2*baseline/3, width + 1, thickness, brush );
+ }
+}
+
diff --git a/khtml/rendering/font.h b/khtml/rendering/font.h
new file mode 100644
index 000000000..c35ab53aa
--- /dev/null
+++ b/khtml/rendering/font.h
@@ -0,0 +1,188 @@
+/*
+ * This file is part of the html renderer for KDE.
+ *
+ * Copyright (C) 2000-2003 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef KHTMLFONT_H
+#define KHTMLFONT_H
+
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qmap.h>
+#include <qpainter.h>
+
+class QFontDatabase;
+class QPaintDeviceMetrics;
+
+namespace khtml
+{
+class RenderStyle;
+class CSSStyleSelector;
+
+class FontDef
+{
+public:
+ FontDef()
+ : size( 0 ), italic( false ), smallCaps( false ), weight( 50 ) {}
+ bool operator == ( const FontDef &other ) const {
+ return ( family == other.family &&
+ size == other.size &&
+ italic == other.italic &&
+ smallCaps == other.smallCaps &&
+ weight == other.weight );
+ }
+
+ QString family;
+ short int size;
+ bool italic : 1;
+ bool smallCaps : 1;
+ unsigned int weight : 8;
+};
+
+
+class Font
+{
+ friend class RenderStyle;
+ friend class CSSStyleSelector;
+
+public:
+ Font() : fontDef(), f(), fm( f ), scFont( 0 ), letterSpacing( 0 ), wordSpacing( 0 ) {}
+ Font( const FontDef &fd )
+ : fontDef( fd ), f(), fm( f ), scFont( 0 ), letterSpacing( 0 ), wordSpacing( 0 )
+ {}
+ Font(const Font& o)
+ : fontDef(o.fontDef), f(o.f), fm(o.fm), scFont(o.scFont), letterSpacing(o.letterSpacing), wordSpacing(o.wordSpacing) { if (o.scFont) scFont = new QFont(*o.scFont); }
+ ~Font() { delete scFont; }
+
+ bool operator == ( const Font &other ) const {
+ return (fontDef == other.fontDef &&
+ letterSpacing == other.letterSpacing &&
+ wordSpacing == other.wordSpacing );
+ }
+
+ const FontDef& getFontDef() const { return fontDef; }
+
+ void update( QPaintDeviceMetrics *devMetrics ) const;
+
+ /**
+ * Draws a piece from the given piece of text.
+ * @param p painter
+ * @param x x-coordinate to begin drawing, always denotes leftmost position
+ * @param y y-coordinate of baseline of text
+ * @param str string to draw a piece from
+ * @param slen total length of string
+ * @param pos zero-based offset of beginning of piece
+ * @param len length of piece
+ * @param width additional pixels to be distributed equally among all
+ * spaces
+ * @param d text direction
+ * @param from begin with this position relative to @p pos, -1 to start
+ * at @p pos
+ * @param to stop before this position relative to @p pos, -1 to use full
+ * length of piece
+ * @param bg if valid, fill the background of the drawn piece with this
+ * color
+ * @param uy y-coordinate of top position, used for background and text
+ * decoration painting
+ * @param h total height of line, only used for background and text
+ * decoration painting
+ * @param deco combined text decoration (see Decoration)
+ */
+ void drawText( QPainter *p, int x, int y, QChar *str, int slen, int pos, int len, int width,
+ QPainter::TextDirection d, int from=-1, int to=-1, QColor bg=QColor(),
+ int uy=-1, int h=-1, int deco=0 ) const;
+
+ /** returns the width of the given string chunk in pixels.
+ *
+ * The method also considers various styles like text-align and font-variant
+ * @param str pointer to string
+ * @param slen total length of string
+ * @param pos zero-based position in string where to start measuring
+ * @param len count of characters up to which the width should be determined
+ * @param start starting position of inline text box within str, only
+ * used when toAdd is specified.
+ * @param end ending position of inline text box within str, only
+ * used when toAdd is specified.
+ * @param toAdd amount of pixels to distribute evenly among all spaces of
+ * str. Note that toAdd applies to all spaces within str, but only those
+ * within [pos, pos+len) are counted towards the width.
+ */
+ int width( QChar *str, int slen, int pos, int len, int start = 0, int end = 0, int toAdd = 0 ) const;
+ /** return the width of the given char in pixels.
+ *
+ * The method also considers various styles like text-align and font-variant
+ * @param str pointer to string
+ * @param slen total length of string
+ * @param pos zero-based position of char in string
+ */
+ int width( QChar *str, int slen, int pos) const;
+
+ /** Text decoration constants.
+ *
+ * The enumeration constant values match those of ETextDecoration, but only
+ * a subset is supported.
+ */
+ enum Decoration { UNDERLINE = 0x1, OVERLINE = 0x2, LINE_THROUGH= 0x4 };
+ // Keep in sync with ETextDecoration
+
+ /** draws text decoration
+ * @param p painter
+ * @param x x-coordinate
+ * @param y top y-coordinate of line box
+ * @param baseline baseline
+ * @param width length of decoration in pixels
+ * @param height height of line box
+ * @param deco decoration to be drawn (see Decoration). The enumeration
+ * constants may be combined.
+ */
+ void drawDecoration(QPainter *p, int x, int y, int baseline, int width, int height, int deco) const;
+
+ /** returns letter spacing
+ */
+ int getLetterSpacing() const { return letterSpacing; }
+ /** returns word spacing
+ */
+ int getWordSpacing() const { return wordSpacing; }
+
+private:
+ mutable FontDef fontDef;
+ mutable QFont f;
+ mutable QFontMetrics fm;
+ mutable QFont *scFont;
+ short letterSpacing;
+ short wordSpacing;
+
+ struct ScalKey;
+ enum ScalInfo {
+ Unknown,
+ No,
+ Yes
+ };
+
+ static QMap<ScalKey, ScalInfo>* scalCache;
+ static QMap<ScalKey, QValueList<int> >* scalSizesCache;
+ static bool isFontScalable(QFontDatabase& db, const QFont& font);
+};
+
+} // namespace
+
+#endif
diff --git a/khtml/rendering/img-loading.png b/khtml/rendering/img-loading.png
new file mode 100644
index 000000000..ae5a9732f
--- /dev/null
+++ b/khtml/rendering/img-loading.png
Binary files differ
diff --git a/khtml/rendering/loading_icon.cpp b/khtml/rendering/loading_icon.cpp
new file mode 100644
index 000000000..218befcf8
--- /dev/null
+++ b/khtml/rendering/loading_icon.cpp
@@ -0,0 +1,25 @@
+static const unsigned char loading_icon_data[] = {
+0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x00,0x00,0x00,0x0d,0x49,0x48,0x44,
+0x52,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x10,0x08,0x04,0x00,0x00,0x00,0x8c,
+0x9d,0x86,0xb1,0x00,0x00,0x00,0x04,0x67,0x41,0x4d,0x41,0x00,0x00,0xb1,0x8f,
+0x0b,0xfc,0x61,0x05,0x00,0x00,0x00,0x02,0x62,0x4b,0x47,0x44,0x00,0xff,0x87,
+0x8f,0xcc,0xbf,0x00,0x00,0x00,0x09,0x70,0x48,0x59,0x73,0x00,0x00,0x0b,0x12,
+0x00,0x00,0x0b,0x12,0x01,0xd2,0xdd,0x7e,0xfc,0x00,0x00,0x00,0x07,0x74,0x49,
+0x4d,0x45,0x07,0xd4,0x0c,0x17,0x10,0x00,0x23,0xf3,0x04,0xa4,0xbc,0x00,0x00,
+0x00,0xbf,0x49,0x44,0x41,0x54,0x78,0x9c,0x75,0xd1,0x31,0x4e,0x04,0x31,0x0c,
+0x85,0xe1,0x2f,0xc9,0x1c,0x63,0xee,0x02,0x82,0x9a,0x23,0x70,0x01,0xba,0xad,
+0xe8,0x98,0x0d,0x35,0xab,0xe9,0x10,0xf7,0x80,0x76,0xe0,0x32,0x7b,0x01,0x44,
+0x9d,0x84,0x22,0xc3,0x32,0xd2,0x82,0x5d,0xd9,0xbf,0xf5,0xe4,0x67,0x07,0xd9,
+0x59,0x4c,0xf2,0x04,0x91,0xf6,0x47,0x3e,0x64,0x18,0xe0,0x1d,0x51,0x15,0x05,
+0x05,0xc1,0x5e,0xc8,0x79,0x1a,0xba,0x50,0x54,0x3d,0x49,0x76,0xa2,0xaa,0x61,
+0x4f,0x8e,0x1d,0x05,0xb3,0x1b,0xc5,0xb5,0x2a,0x79,0x14,0x04,0xb9,0xcb,0x06,
+0x45,0xf1,0x2a,0x21,0x2a,0x16,0x5c,0x09,0x1d,0x16,0xd1,0xbd,0x4b,0x2c,0xaa,
+0xa4,0x48,0x4e,0x0b,0xf5,0x58,0x24,0xcd,0x2d,0x38,0x6a,0xdd,0x0a,0xd1,0x41,
+0x41,0x01,0xcf,0xa7,0xf1,0x08,0xd5,0x68,0x16,0x11,0x70,0xe7,0x73,0x0b,0x13,
+0x46,0x07,0x34,0x47,0x47,0x5f,0x2b,0x1c,0xa0,0x81,0xd1,0x6c,0x67,0xd9,0x9c,
+0x71,0xe8,0xb2,0x2f,0x6b,0xf9,0xe6,0x43,0xd0,0xd4,0x5f,0x98,0x2c,0x48,0x9a,
+0xba,0xb6,0x37,0x56,0x2e,0xce,0x1f,0xf3,0x03,0xc3,0x3f,0x88,0x6f,0xc1,0xe0,
+0x3e,0x9e,0x60,0xe9,0x10,0xaa,0x00,0x00,0x00,0x00,0x49,0x45,0x4e,0x44,0xae,
+0x42,0x60,0x82
+};
+static const unsigned int loading_icon_len = 318;
diff --git a/khtml/rendering/render_applet.cpp b/khtml/rendering/render_applet.cpp
new file mode 100644
index 000000000..f6ce1ebab
--- /dev/null
+++ b/khtml/rendering/render_applet.cpp
@@ -0,0 +1,145 @@
+/**
+ * This file is part of the HTML widget for KDE.
+ *
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+#include <config.h>
+#include <klocale.h>
+
+#include <kdebug.h>
+
+#include "rendering/render_applet.h"
+#include "rendering/render_canvas.h"
+#include "xml/dom_docimpl.h"
+#include "khtmlview.h"
+#include "khtml_part.h"
+
+#include <qlabel.h>
+
+#ifndef Q_WS_QWS // We don't have Java in Qt Embedded
+
+#include "java/kjavaappletwidget.h"
+#include "misc/htmltags.h"
+#include "html/html_objectimpl.h"
+
+using namespace khtml;
+using namespace DOM;
+
+RenderApplet::RenderApplet(HTMLElementImpl *applet, const QMap<QString, QString> &args )
+ : RenderWidget(applet)
+{
+ // init RenderObject attributes
+ setInline(true);
+
+ KJavaAppletContext *context = 0;
+ KHTMLView *_view = applet->getDocument()->view();
+ if ( _view ) {
+ KHTMLPart *part = _view->part();
+ context = part->createJavaContext();
+ }
+
+ if ( context ) {
+ //kdDebug(6100) << "RenderApplet::RenderApplet, setting QWidget" << endl;
+ setQWidget( new KJavaAppletWidget(context, _view->viewport()) );
+ processArguments(args);
+ }
+}
+
+RenderApplet::~RenderApplet()
+{
+}
+
+short RenderApplet::intrinsicWidth() const
+{
+ int rval = 300;
+
+ if( m_widget )
+ rval = ((KJavaAppletWidget*)(m_widget))->sizeHint().width();
+
+ return rval > 10 ? rval : 50;
+}
+
+int RenderApplet::intrinsicHeight() const
+{
+ int rval = 150;
+
+ if( m_widget )
+ rval = m_widget->sizeHint().height();
+
+ return rval > 10 ? rval : 50;
+}
+
+void RenderApplet::layout()
+{
+ //kdDebug(6100) << "RenderApplet::layout" << endl;
+
+ KHTMLAssert( needsLayout() );
+ KHTMLAssert( minMaxKnown() );
+
+ calcWidth();
+ calcHeight();
+
+ KJavaAppletWidget *tmp = static_cast<KJavaAppletWidget*>(m_widget);
+ if ( tmp ) {
+ NodeImpl *child = element()->firstChild();
+
+ while(child) {
+
+ if(child->id() == ID_PARAM) {
+ HTMLParamElementImpl *p = static_cast<HTMLParamElementImpl *>(child);
+ if(tmp->applet())
+ tmp->applet()->setParameter( p->name(), p->value());
+ }
+ child = child->nextSibling();
+ }
+ //kdDebug(6100) << "setting applet widget to size: " << m_width << ", " << m_height << endl;
+ m_widget->resize(m_width-borderLeft()-borderRight()-paddingLeft()-paddingRight(),
+ m_height-borderTop()-borderBottom()-paddingTop()-paddingBottom());
+ tmp->showApplet();
+ }
+
+ setNeedsLayout(false);
+}
+
+void RenderApplet::processArguments(const QMap<QString, QString> &args)
+{
+ KJavaAppletWidget *w = static_cast<KJavaAppletWidget*>(m_widget);
+ KJavaApplet* applet = w ? w->applet() : 0;
+
+ if ( applet ) {
+ applet->setBaseURL( args[QString::fromLatin1("baseURL") ] );
+ applet->setAppletClass( args[QString::fromLatin1("code") ] );
+
+ QString str = args[QString::fromLatin1("codeBase") ];
+ if( !str.isEmpty() )
+ applet->setCodeBase( str );
+
+ str = args[QString::fromLatin1("name") ];
+ if( !str.isNull() )
+ applet->setAppletName( str );
+ else
+ applet->setAppletName( args[QString::fromLatin1("code") ] );
+
+ str = args[QString::fromLatin1("archive") ];
+ if( !str.isEmpty() )
+ applet->setArchives( args[QString::fromLatin1("archive") ] );
+ }
+}
+
+#endif
diff --git a/khtml/rendering/render_applet.h b/khtml/rendering/render_applet.h
new file mode 100644
index 000000000..54af6f280
--- /dev/null
+++ b/khtml/rendering/render_applet.h
@@ -0,0 +1,60 @@
+/*
+ * This file is part of the HTML widget for KDE.
+ *
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef render_applet_h
+#define render_applet_h
+
+#include "rendering/render_replaced.h"
+#include "html/html_objectimpl.h"
+
+#include <qwidget.h>
+#include <qmap.h>
+
+class KHTMLView;
+
+namespace DOM {
+ class HTMLElementImpl;
+}
+
+namespace khtml {
+
+class RenderApplet : public RenderWidget
+{
+public:
+ RenderApplet(DOM::HTMLElementImpl* node, const QMap<QString, QString> &args);
+ virtual ~RenderApplet();
+
+ virtual const char *renderName() const { return "RenderApplet"; }
+
+ virtual void layout();
+ virtual short intrinsicWidth() const;
+ virtual int intrinsicHeight() const;
+ virtual bool isApplet() const { return true; }
+
+ DOM::HTMLElementImpl *element() const
+ { return static_cast<DOM::HTMLElementImpl*>(RenderObject::element()); }
+
+private:
+ void processArguments( const QMap<QString, QString> &args );
+};
+
+}
+#endif
diff --git a/khtml/rendering/render_arena.cpp b/khtml/rendering/render_arena.cpp
new file mode 100644
index 000000000..99fbf4f4d
--- /dev/null
+++ b/khtml/rendering/render_arena.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2002 Apple Computer, Inc.
+ * Copyright (C) 2003 Dirk Mueller (mueller@kde.org)
+ *
+ * Portions are Copyright (C) 1998 Netscape Communications Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Alternatively, the contents of this file may be used under the terms
+ * of either the Mozilla Public License Version 1.1, found at
+ * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public
+ * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html
+ * (the "GPL"), in which case the provisions of the MPL or the GPL are
+ * applicable instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of one of those two
+ * licenses (the MPL or the GPL) and not to allow others to use your
+ * version of this file under the LGPL, indicate your decision by
+ * deletingthe provisions above and replace them with the notice and
+ * other provisions required by the MPL or the GPL, as the case may be.
+ * If you do not delete the provisions above, a recipient may use your
+ * version of this file under any of the LGPL, the MPL or the GPL.
+ */
+
+#include "render_arena.h"
+
+#include <string.h>
+#include <assert.h>
+
+using namespace khtml;
+
+namespace khtml {
+
+//#ifdef NDEBUG
+#define KHTML_USE_ARENA_ALLOCATOR
+//#endif
+
+typedef struct {
+ RenderArena *arena;
+ size_t size;
+} RenderArenaDebugHeader;
+
+#ifdef VALGRIND_SUPPORT
+Arena* findContainingArena(ArenaPool* pool, void* ptr) {
+ uword ptrBits = reinterpret_cast<uword>(ptr);
+ for (Arena* a = &pool->first; a; a = a->next)
+ if (ptrBits >= a->base && ptrBits < a->limit)
+ return a;
+ return 0; //Should not happen
+}
+#endif
+
+RenderArena::RenderArena(unsigned int arenaSize)
+{
+ // Initialize the arena pool
+ INIT_ARENA_POOL(&m_pool, "RenderArena", arenaSize);
+
+ // Zero out the recyclers array
+ memset(m_recyclers, 0, sizeof(m_recyclers));
+}
+
+RenderArena::~RenderArena()
+{
+ // Free the arena in the pool and finish using it
+ FreeArenaPool(&m_pool);
+}
+
+void* RenderArena::allocate(size_t size)
+{
+#ifndef KHTML_USE_ARENA_ALLOCATOR
+ // Use standard malloc so that memory debugging tools work.
+ void *block = ::malloc(sizeof(RenderArenaDebugHeader) + size);
+ RenderArenaDebugHeader *header = (RenderArenaDebugHeader *)block;
+ header->arena = this;
+ header->size = size;
+ return header + 1;
+#else
+ void* result = 0;
+
+ // Ensure we have correct alignment for pointers. Important for Tru64
+ size = KHTML_ROUNDUP(size, sizeof(void*));
+
+ // Check recyclers first
+ if (size < KHTML_MAX_RECYCLED_SIZE) {
+ const int index = size >> 2;
+
+ result = m_recyclers[index];
+ if (result) {
+#ifdef VALGRIND_SUPPORT
+ VALGRIND_MEMPOOL_ALLOC(findContainingArena(&m_pool, result)->base, result, size);
+#endif
+ // Need to move to the next object
+ void* next = *((void**)result);
+ m_recyclers[index] = next;
+ }
+ }
+
+ if (!result) {
+ // Allocate a new chunk from the arena
+ ARENA_ALLOCATE(result, &m_pool, size);
+ }
+
+ return result;
+#endif
+}
+
+void RenderArena::free(size_t size, void* ptr)
+{
+#ifndef KHTML_USE_ARENA_ALLOCATOR
+ // Use standard free so that memory debugging tools work.
+ assert(this);
+ RenderArenaDebugHeader *header = (RenderArenaDebugHeader *)ptr - 1;
+ assert(header->size == size);
+ assert(header->arena == this);
+ ::free(header);
+#else
+
+#ifdef VALGRIND_SUPPORT
+ VALGRIND_MEMPOOL_FREE(findContainingArena(&m_pool, ptr)->base, ptr);
+#endif
+
+ // Ensure we have correct alignment for pointers. Important for Tru64
+ size = KHTML_ROUNDUP(size, sizeof(void*));
+
+ // See if it's a size that we recycle
+ if (size < KHTML_MAX_RECYCLED_SIZE) {
+ const int index = size >> 2;
+ void* currentTop = m_recyclers[index];
+ m_recyclers[index] = ptr;
+ *((void**)ptr) = currentTop;
+ }
+#endif
+}
+
+}
diff --git a/khtml/rendering/render_arena.h b/khtml/rendering/render_arena.h
new file mode 100644
index 000000000..786cd208a
--- /dev/null
+++ b/khtml/rendering/render_arena.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2002 Apple Computer, Inc.
+ * Copyright (C) 2003 Dirk Mueller (mueller@kde.org)
+ *
+ * Portions are Copyright (C) 1998 Netscape Communications Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Alternatively, the contents of this file may be used under the terms
+ * of either the Mozilla Public License Version 1.1, found at
+ * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public
+ * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html
+ * (the "GPL"), in which case the provisions of the MPL or the GPL are
+ * applicable instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of one of those two
+ * licenses (the MPL or the GPL) and not to allow others to use your
+ * version of this file under the LGPL, indicate your decision by
+ * deletingthe provisions above and replace them with the notice and
+ * other provisions required by the MPL or the GPL, as the case may be.
+ * If you do not delete the provisions above, a recipient may use your
+ * version of this file under any of the LGPL, the MPL or the GPL.
+ */
+
+#ifndef RENDERARENA_H
+#define RENDERARENA_H
+
+#include "misc/arena.h"
+#include "misc/shared.h"
+
+#include <stdlib.h>
+
+namespace khtml {
+
+#define KHTML_MAX_RECYCLED_SIZE 400
+#define KHTML_ROUNDUP(x,y) ((((x)+((y)-1))/(y))*(y))
+
+class RenderArena: public Shared<RenderArena> {
+public:
+ RenderArena(unsigned int arenaSize = 4096);
+ ~RenderArena();
+
+ // Memory management functions
+ void* allocate(size_t size);
+ void free(size_t size, void* ptr);
+
+private:
+ // Underlying arena pool
+ ArenaPool m_pool;
+
+ // The recycler array is sparse with the indices being multiples of 4,
+ // i.e., 0, 4, 8, 12, 16, 20, ...
+ void* m_recyclers[KHTML_MAX_RECYCLED_SIZE >> 2];
+};
+
+
+} // namespace
+
+
+#endif
+
diff --git a/khtml/rendering/render_block.cpp b/khtml/rendering/render_block.cpp
new file mode 100644
index 000000000..ba9645a62
--- /dev/null
+++ b/khtml/rendering/render_block.cpp
@@ -0,0 +1,3223 @@
+/*
+ * This file is part of the render object implementation for KHTML.
+ *
+ * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
+ * (C) 1999-2003 Antti Koivisto (koivisto@kde.org)
+ * (C) 2002-2003 Dirk Mueller (mueller@kde.org)
+ * (C) 2003,2005 Apple Computer, Inc.
+ * (C) 2004 Germain Garand (germain@ebooksfrance.org)
+ * (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com)
+ * (C) 2006 Charles Samuels (charles@kde.org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+//#define DEBUG
+//#define DEBUG_LAYOUT
+//#define BOX_DEBUG
+//#define FLOAT_DEBUG
+//#define PAGE_DEBUG
+
+#include <kdebug.h>
+#include "rendering/render_text.h"
+#include "rendering/render_table.h"
+#include "rendering/render_canvas.h"
+#include "rendering/render_layer.h"
+#include "rendering/render_block.h"
+
+#include "xml/dom_nodeimpl.h"
+#include "xml/dom_docimpl.h"
+#include "html/html_formimpl.h"
+#include "misc/htmltags.h"
+
+#include "khtmlview.h"
+
+using namespace DOM;
+
+namespace khtml {
+
+// -------------------------------------------------------------------------------------------------------
+
+// Our MarginInfo state used when laying out block children.
+RenderBlock::MarginInfo::MarginInfo(RenderBlock* block, int top, int bottom)
+{
+ // Whether or not we can collapse our own margins with our children. We don't do this
+ // if we had any border/padding (obviously), if we're the root or HTML elements, or if
+ // we're positioned, floating, a table cell.
+ m_canCollapseWithChildren = !block->isCanvas() && !block->isRoot() && !block->isPositioned() &&
+ !block->isFloating() && !block->isTableCell() && !block->hasOverflowClip() && !block->isInlineBlockOrInlineTable();
+
+ m_canCollapseTopWithChildren = m_canCollapseWithChildren && (top == 0) /*&& block->style()->marginTopCollapse() != MSEPARATE */;
+
+ // If any height other than auto is specified in CSS, then we don't collapse our bottom
+ // margins with our children's margins. To do otherwise would be to risk odd visual
+ // effects when the children overflow out of the parent block and yet still collapse
+ // with it. We also don't collapse if we have any bottom border/padding.
+ m_canCollapseBottomWithChildren = m_canCollapseWithChildren && (bottom == 0) &&
+ (block->style()->height().isVariable() && block->style()->height().value() == 0) /*&& block->style()->marginBottomCollapse() != MSEPARATE*/;
+
+ m_quirkContainer = block->isTableCell() || block->isBody() /*|| block->style()->marginTopCollapse() == MDISCARD ||
+ block->style()->marginBottomCollapse() == MDISCARD*/;
+
+ m_atTopOfBlock = true;
+ m_atBottomOfBlock = false;
+
+ m_posMargin = m_canCollapseTopWithChildren ? block->maxTopMargin(true) : 0;
+ m_negMargin = m_canCollapseTopWithChildren ? block->maxTopMargin(false) : 0;
+
+ m_selfCollapsingBlockClearedFloat = false;
+
+ m_topQuirk = m_bottomQuirk = m_determinedTopQuirk = false;
+}
+
+// -------------------------------------------------------------------------------------------------------
+
+RenderBlock::RenderBlock(DOM::NodeImpl* node)
+ : RenderFlow(node)
+{
+ m_childrenInline = true;
+ m_floatingObjects = 0;
+ m_positionedObjects = 0;
+ m_firstLine = false;
+ m_avoidPageBreak = false;
+ m_clearStatus = CNONE;
+ m_maxTopPosMargin = m_maxTopNegMargin = m_maxBottomPosMargin = m_maxBottomNegMargin = 0;
+ m_topMarginQuirk = m_bottomMarginQuirk = false;
+ m_overflowHeight = m_overflowWidth = 0;
+ m_overflowLeft = m_overflowTop = 0;
+}
+
+RenderBlock::~RenderBlock()
+{
+ delete m_floatingObjects;
+ delete m_positionedObjects;
+}
+
+void RenderBlock::setStyle(RenderStyle* _style)
+{
+ setReplaced(_style->isDisplayReplacedType());
+
+ RenderFlow::setStyle(_style);
+
+ // ### we could save this call when the change only affected
+ // non inherited properties
+ RenderObject *child = firstChild();
+ while (child != 0)
+ {
+ if (child->isAnonymousBlock())
+ {
+ RenderStyle* newStyle = new RenderStyle();
+ newStyle->inheritFrom(style());
+ newStyle->setDisplay(BLOCK);
+ child->setStyle(newStyle);
+ }
+ child = child->nextSibling();
+ }
+
+ if (attached()) {
+ // Update generated content and ::inside
+ updateReplacedContent();
+ // Update pseudos for :before and :after
+ updatePseudoChildren();
+ }
+
+ // handled by close() during parsing
+ // ### remove close move upto updatePseudo
+ if (!document()->parsing()) {
+ updateFirstLetter();
+ }
+}
+
+// Attach handles initial setStyle that requires parent nodes
+void RenderBlock::attach()
+{
+ RenderFlow::attach();
+
+ updateReplacedContent();
+ updatePseudoChildren();
+}
+
+void RenderBlock::updateFirstLetter()
+{
+ // Only blocks with inline-children can generate a first-letter
+ if (!childrenInline() || !firstChild()) return;
+
+ // Don't recurse
+ if (style()->styleType() == RenderStyle::FIRST_LETTER) return;
+
+ // The first-letter style is inheritable.
+ RenderStyle *pseudoStyle = style()->getPseudoStyle(RenderStyle::FIRST_LETTER);
+ RenderObject *o = this;
+ while (o && !pseudoStyle) {
+ // ### We should ignore empty preceding siblings
+ if (o->parent() && o->parent()->firstChild() == this)
+ o = o->parent();
+ else
+ break;
+ pseudoStyle = o->style()->getPseudoStyle(RenderStyle::FIRST_LETTER);
+ };
+
+ // FIXME: Currently we don't delete first-letters, this is
+ // handled instead in NodeImpl::diff by issuing Detach on first-letter changes.
+ if (!pseudoStyle) {
+ return;
+ }
+
+ // Drill into inlines looking for our first text child.
+ RenderObject* firstText = firstChild();
+ while (firstText && firstText->needsLayout() && !firstText->isFloating() && !firstText->isRenderBlock() && !firstText->isReplaced() && !firstText->isText())
+ // ### We should skip first children with only white-space and punctuation
+ firstText = firstText->firstChild();
+
+ if (firstText && firstText->isText() && !firstText->isBR()) {
+ RenderObject* firstLetterObject = 0;
+ // Find the old first-letter
+ if (firstText->parent()->style()->styleType() == RenderStyle::FIRST_LETTER)
+ firstLetterObject = firstText->parent();
+
+ // Force inline display (except for floating first-letters)
+ pseudoStyle->setDisplay( pseudoStyle->isFloating() ? BLOCK : INLINE);
+ pseudoStyle->setPosition( STATIC ); // CSS2 says first-letter can't be positioned.
+
+ if (firstLetterObject != 0) {
+ firstLetterObject->setStyle( pseudoStyle );
+ RenderStyle* newStyle = new RenderStyle();
+ newStyle->inheritFrom( pseudoStyle );
+ firstText->setStyle( newStyle );
+ return;
+ }
+
+ RenderText* textObj = static_cast<RenderText*>(firstText);
+ RenderObject* firstLetterContainer = firstText->parent();
+
+ firstLetterObject = RenderFlow::createFlow(node(), pseudoStyle, renderArena() );
+ firstLetterObject->setIsAnonymous( true );
+ firstLetterContainer->addChild(firstLetterObject, firstLetterContainer->firstChild());
+
+ // if this object is the result of a :begin, then the text may have not been
+ // generated yet if it is a counter
+ if (textObj->recalcMinMax())
+ textObj->recalcMinMaxWidths();
+
+ // The original string is going to be either a generated content string or a DOM node's
+ // string. We want the original string before it got transformed in case first-letter has
+ // no text-transform or a different text-transform applied to it.
+ DOMStringImpl* oldText = textObj->originalString();
+ if (!oldText)
+ oldText = textObj->string();
+ // ### In theory a first-letter can stretch across multiple text objects, if they only contain
+ // punctuation and white-space
+ if(oldText->l >= 1) {
+ oldText->ref();
+ // begin: we need skip leading whitespace so that RenderBlock::findNextLineBreak
+ // won't think we're continuing from a previous run
+ unsigned int begin = 0; // the position that first-letter begins
+ unsigned int length = 0; // the position that "the rest" begins
+ while ( length < oldText->l && (oldText->s+length)->isSpace() )
+ length++;
+ begin = length;
+ while ( length < oldText->l &&
+ ( (oldText->s+length)->isPunct()) || (oldText->s+length)->isSpace() )
+ length++;
+ if ( length < oldText->l &&
+ !( (oldText->s+length)->isSpace() || (oldText->s+length)->isPunct() ))
+ length++;
+ while ( length < oldText->l && (oldText->s+length)->isMark() )
+ length++;
+
+ // we need to generated a remainingText object even if no text is left
+ // because it holds the place and style for the old textObj
+ RenderTextFragment* remainingText =
+ new (renderArena()) RenderTextFragment(textObj->node(), oldText, length, oldText->l-length);
+ remainingText->setIsAnonymous( textObj->isAnonymous() );
+ remainingText->setStyle(textObj->style());
+ if (remainingText->element())
+ remainingText->element()->setRenderer(remainingText);
+
+ RenderObject* nextObj = textObj->nextSibling();
+ textObj->detach();
+ firstLetterContainer->addChild(remainingText, nextObj);
+
+ RenderTextFragment* letter =
+ new (renderArena()) RenderTextFragment(remainingText->node(), oldText, begin, length-begin);
+ letter->setIsAnonymous( remainingText->isAnonymous() );
+ RenderStyle* newStyle = new RenderStyle();
+ newStyle->inheritFrom(pseudoStyle);
+ letter->setStyle(newStyle);
+ firstLetterObject->addChild(letter);
+ oldText->deref();
+ }
+ firstLetterObject->close();
+ }
+}
+
+void RenderBlock::addChildToFlow(RenderObject* newChild, RenderObject* beforeChild)
+{
+ // Make sure we don't append things after :after-generated content if we have it.
+ if ( !beforeChild && lastChild() && lastChild()->style()->styleType() == RenderStyle::AFTER )
+ beforeChild = lastChild();
+
+ bool madeBoxesNonInline = false;
+
+ // If the requested beforeChild is not one of our children, then this is most likely because
+ // there is an anonymous block box within this object that contains the beforeChild. So
+ // just insert the child into the anonymous block box instead of here. This may also be
+ // needed in cases of things like anonymous tables.
+ if (beforeChild && beforeChild->parent() != this) {
+
+ KHTMLAssert(beforeChild->parent());
+
+ // In the special case where we are prepending a block-level element before
+ // something contained inside an anonymous block, we can just prepend it before
+ // the anonymous block.
+ if (!newChild->isInline() && beforeChild->parent()->isAnonymousBlock() &&
+ beforeChild->parent()->parent() == this &&
+ beforeChild->parent()->firstChild() == beforeChild)
+ return addChildToFlow(newChild, beforeChild->parent());
+
+ // Otherwise find our kid inside which the beforeChild is, and delegate to it.
+ // This may be many levels deep due to anonymous tables, table sections, etc.
+ RenderObject* responsible = beforeChild->parent();
+ while (responsible->parent() != this)
+ responsible = responsible->parent();
+
+ return responsible->addChild(newChild,beforeChild);
+ }
+
+ // prevent elements that haven't received a layout yet from getting painted by pushing
+ // them far above the top of the page
+ if (!newChild->isInline())
+ newChild->setPos(newChild->xPos(), -500000);
+
+ if (!newChild->isText() && newChild->style()->position() != STATIC)
+ setOverhangingContents();
+
+ // A block has to either have all of its children inline, or all of its children as blocks.
+ // So, if our children are currently inline and a block child has to be inserted, we move all our
+ // inline children into anonymous block boxes
+ if ( m_childrenInline && !newChild->isInline() && !newChild->isFloatingOrPositioned() )
+ {
+ // This is a block with inline content. Wrap the inline content in anonymous blocks.
+ makeChildrenNonInline(beforeChild);
+ madeBoxesNonInline = true;
+
+ if (beforeChild && beforeChild->parent() != this) {
+ beforeChild = beforeChild->parent();
+ KHTMLAssert(beforeChild->isAnonymousBlock());
+ KHTMLAssert(beforeChild->parent() == this);
+ }
+ }
+ else if (!m_childrenInline && !newChild->isFloatingOrPositioned())
+ {
+ // If we're inserting an inline child but all of our children are blocks, then we have to make sure
+ // it is put into an anomyous block box. We try to use an existing anonymous box if possible, otherwise
+ // a new one is created and inserted into our list of children in the appropriate position.
+ if (newChild->isInline()) {
+ if (beforeChild) {
+ if ( beforeChild->previousSibling() && beforeChild->previousSibling()->isAnonymousBlock() ) {
+ beforeChild->previousSibling()->addChild(newChild);
+ return;
+ }
+ }
+ else {
+ if ( m_last && m_last->isAnonymousBlock() ) {
+ m_last->addChild(newChild);
+ return;
+ }
+ }
+
+ // no suitable existing anonymous box - create a new one
+ RenderBlock* newBox = createAnonymousBlock();
+ RenderBox::addChild(newBox,beforeChild);
+ newBox->addChild(newChild);
+
+ //the above may actually destroy newBox in case an anonymous
+ //table got created, and made the anonymous block redundant.
+ //so look up what to hide indirectly.
+ RenderObject* toHide = newChild;
+ while (toHide->parent() != this)
+ toHide = toHide->parent();
+
+ toHide->setPos(toHide->xPos(), -500000);
+ return;
+ }
+ else {
+ // We are adding another block child... if the current last child is an anonymous box
+ // then it needs to be closed.
+ // ### get rid of the closing thing altogether this will only work during initial parsing
+ if (lastChild() && lastChild()->isAnonymous()) {
+ lastChild()->close();
+ }
+ }
+ }
+
+ RenderBox::addChild(newChild,beforeChild);
+ // ### care about aligned stuff
+
+ if ( madeBoxesNonInline )
+ removeLeftoverAnonymousBoxes();
+}
+
+static void getInlineRun(RenderObject* start, RenderObject* stop,
+ RenderObject*& inlineRunStart,
+ RenderObject*& inlineRunEnd)
+{
+ // Beginning at |start| we find the largest contiguous run of inlines that
+ // we can. We denote the run with start and end points, |inlineRunStart|
+ // and |inlineRunEnd|. Note that these two values may be the same if
+ // we encounter only one inline.
+ //
+ // We skip any non-inlines we encounter as long as we haven't found any
+ // inlines yet.
+ //
+ // |stop| indicates a non-inclusive stop point. Regardless of whether |stop|
+ // is inline or not, we will not include it in a run with inlines before it. It's as though we encountered
+ // a non-inline.
+
+ RenderObject * curr = start;
+ bool sawInline;
+ do {
+ while (curr && !(curr->isInline() || curr->isFloatingOrPositioned()))
+ curr = curr->nextSibling();
+
+ inlineRunStart = inlineRunEnd = curr;
+
+ if (!curr)
+ return; // No more inline children to be found.
+
+ sawInline = curr->isInline();
+
+ curr = curr->nextSibling();
+ while (curr && (curr->isInline() || curr->isFloatingOrPositioned()) && (curr != stop)) {
+ inlineRunEnd = curr;
+ if (curr->isInline())
+ sawInline = true;
+ curr = curr->nextSibling();
+ }
+ } while (!sawInline);
+
+}
+
+void RenderBlock::makeChildrenNonInline(RenderObject *insertionPoint)
+{
+ // makeChildrenNonInline takes a block whose children are *all* inline and it
+ // makes sure that inline children are coalesced under anonymous
+ // blocks. If |insertionPoint| is defined, then it represents the insertion point for
+ // the new block child that is causing us to have to wrap all the inlines. This
+ // means that we cannot coalesce inlines before |insertionPoint| with inlines following
+ // |insertionPoint|, because the new child is going to be inserted in between the inlines,
+ // splitting them.
+ KHTMLAssert(isReplacedBlock() || !isInline());
+ KHTMLAssert(!insertionPoint || insertionPoint->parent() == this);
+
+ m_childrenInline = false;
+
+ RenderObject *child = firstChild();
+
+ while (child) {
+ RenderObject *inlineRunStart, *inlineRunEnd;
+ getInlineRun(child, insertionPoint, inlineRunStart, inlineRunEnd);
+
+ if (!inlineRunStart)
+ break;
+
+ child = inlineRunEnd->nextSibling();
+
+ RenderBlock* box = createAnonymousBlock();
+ insertChildNode(box, inlineRunStart);
+ RenderObject* o = inlineRunStart;
+ while(o != inlineRunEnd)
+ {
+ RenderObject* no = o;
+ o = no->nextSibling();
+ box->appendChildNode(removeChildNode(no));
+ }
+ box->appendChildNode(removeChildNode(inlineRunEnd));
+ box->close();
+ box->setPos(box->xPos(), -500000);
+ }
+}
+
+void RenderBlock::makePageBreakAvoidBlocks()
+{
+ KHTMLAssert(!childrenInline());
+ KHTMLAssert(canvas()->pagedMode());
+
+ RenderObject *breakAfter = firstChild();
+ RenderObject *breakBefore = breakAfter ? breakAfter->nextSibling() : 0;
+
+ RenderBlock* pageRun = 0;
+
+ // ### Should follow margin-collapsing rules, skipping self-collapsing blocks
+ // and exporting page-breaks from first/last child when collapsing with parent margin.
+ while (breakAfter) {
+ if (breakAfter->isRenderBlock() && !breakAfter->childrenInline())
+ static_cast<RenderBlock*>(breakAfter)->makePageBreakAvoidBlocks();
+ EPageBreak pbafter = breakAfter->style()->pageBreakAfter();
+ EPageBreak pbbefore = breakBefore ? breakBefore->style()->pageBreakBefore() : PBALWAYS;
+ if ((pbafter == PBAVOID && pbbefore == PBAVOID) ||
+ (pbafter == PBAVOID && pbbefore == PBAUTO) ||
+ (pbafter == PBAUTO && pbbefore == PBAVOID))
+ {
+ if (!pageRun) {
+ pageRun = createAnonymousBlock();
+ pageRun->m_avoidPageBreak = true;
+ pageRun->setChildrenInline(false);
+ }
+ pageRun->appendChildNode(removeChildNode(breakAfter));
+ } else
+ {
+ if (pageRun) {
+ pageRun->appendChildNode(removeChildNode(breakAfter));
+ pageRun->close();
+ insertChildNode(pageRun, breakBefore);
+ pageRun = 0;
+ }
+ }
+ breakAfter = breakBefore;
+ breakBefore = breakBefore ? breakBefore->nextSibling() : 0;
+ }
+
+ // recurse into positioned block children as well.
+ if (m_positionedObjects) {
+ QPtrListIterator<RenderObject> it(*m_positionedObjects);
+ for ( ; it.current(); ++it ) {
+ if (it.current()->isRenderBlock() && !it.current()->childrenInline()) {
+ static_cast<RenderBlock*>(it.current())->makePageBreakAvoidBlocks();
+ }
+ }
+ }
+
+ // recurse into floating block children.
+ if (m_floatingObjects) {
+ QPtrListIterator<FloatingObject> it(*m_floatingObjects);
+ for ( ; it.current(); ++it ) {
+ if (it.current()->node->isRenderBlock() && !it.current()->node->childrenInline()) {
+ static_cast<RenderBlock*>(it.current()->node)->makePageBreakAvoidBlocks();
+ }
+ }
+ }
+}
+
+void RenderBlock::removeChild(RenderObject *oldChild)
+{
+ // If this child is a block, and if our previous and next siblings are
+ // both anonymous blocks with inline content, then we can go ahead and
+ // fold the inline content back together.
+ RenderObject* prev = oldChild->previousSibling();
+ RenderObject* next = oldChild->nextSibling();
+ bool mergedBlocks = false;
+ if (document()->renderer() && !isInline() && !oldChild->isInline() && !oldChild->continuation() &&
+ prev && prev->isAnonymousBlock() && prev->childrenInline() &&
+ next && next->isAnonymousBlock() && next->childrenInline()) {
+ // Take all the children out of the |next| block and put them in
+ // the |prev| block.
+ RenderObject* o = next->firstChild();
+ while (o) {
+ RenderObject* no = o;
+ o = no->nextSibling();
+ prev->appendChildNode(next->removeChildNode(no));
+ no->setNeedsLayoutAndMinMaxRecalc();
+ }
+ prev->setNeedsLayoutAndMinMaxRecalc();
+
+ // Nuke the now-empty block.
+ next->detach();
+
+ mergedBlocks = true;
+ }
+
+ RenderFlow::removeChild(oldChild);
+
+ if (mergedBlocks && prev && !prev->previousSibling() && !prev->nextSibling()) {
+ // The remerge has knocked us down to containing only a single anonymous
+ // box. We can go ahead and pull the content right back up into our
+ // box.
+ RenderObject* anonBlock = removeChildNode(prev);
+ m_childrenInline = true;
+ RenderObject* o = anonBlock->firstChild();
+ while (o) {
+ RenderObject* no = o;
+ o = no->nextSibling();
+ appendChildNode(anonBlock->removeChildNode(no));
+ no->setNeedsLayoutAndMinMaxRecalc();
+ }
+
+ // Nuke the now-empty block.
+ anonBlock->detach();
+ }
+}
+
+bool RenderBlock::isSelfCollapsingBlock() const
+{
+ // We are not self-collapsing if we
+ // (a) have a non-zero height according to layout (an optimization to avoid wasting time)
+ // (b) are a table,
+ // (c) have border/padding,
+ // (d) have a min-height
+ if (m_height > 0 ||
+ isTable() || (borderBottom() + paddingBottom() + borderTop() + paddingTop()) != 0 ||
+ style()->minHeight().value() > 0)
+ return false;
+
+ bool hasAutoHeight = style()->height().isVariable();
+ if (style()->height().isPercent() && !style()->htmlHacks()) {
+ hasAutoHeight = true;
+ for (RenderBlock* cb = containingBlock(); !cb->isCanvas(); cb = cb->containingBlock()) {
+ if (cb->style()->height().isFixed() || cb->isTableCell())
+ hasAutoHeight = false;
+ }
+ }
+
+ // If the height is 0 or auto, then whether or not we are a self-collapsing block depends
+ // on whether we have content that is all self-collapsing or not.
+ if (hasAutoHeight || ((style()->height().isFixed() || style()->height().isPercent()) && style()->height().value() == 0)) {
+ // If the block has inline children, see if we generated any line boxes. If we have any
+ // line boxes, then we can't be self-collapsing, since we have content.
+ if (childrenInline())
+ return !firstLineBox();
+
+ // Whether or not we collapse is dependent on whether all our normal flow children
+ // are also self-collapsing.
+ for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
+ if (child->isFloatingOrPositioned())
+ continue;
+ if (!child->isSelfCollapsingBlock())
+ return false;
+ }
+ return true;
+ }
+ return false;
+}
+
+void RenderBlock::layout()
+{
+ // Table cells call layoutBlock directly, so don't add any logic here. Put code into
+ // layoutBlock().
+ layoutBlock(false);
+}
+
+void RenderBlock::layoutBlock(bool relayoutChildren)
+{
+ if (isInline() && !isReplacedBlock()) {
+ setNeedsLayout(false);
+ return;
+ }
+ // kdDebug( 6040 ) << renderName() << " " << this << "::layoutBlock() start" << endl;
+ // QTime t;
+ // t.start();
+ KHTMLAssert( needsLayout() );
+ KHTMLAssert( minMaxKnown() );
+
+ if (canvas()->pagedMode()) relayoutChildren = true;
+
+ if (!relayoutChildren && posChildNeedsLayout() && !normalChildNeedsLayout() && !selfNeedsLayout()) {
+ // All we have to is lay out our positioned objects.
+ layoutPositionedObjects(relayoutChildren);
+ if (hasOverflowClip())
+ m_layer->checkScrollbarsAfterLayout();
+ setNeedsLayout(false);
+ return;
+ }
+
+ if (markedForRepaint()) {
+ repaintDuringLayout();
+ setMarkedForRepaint(false);
+ }
+
+ int oldWidth = m_width;
+
+ calcWidth();
+ m_overflowWidth = m_width;
+ m_overflowLeft = 0;
+ if (style()->direction() == LTR )
+ {
+ int cw=0;
+ if (style()->textIndent().isPercent())
+ cw = containingBlock()->contentWidth();
+ m_overflowLeft = kMin(0, style()->textIndent().minWidth(cw));
+ }
+
+ if ( oldWidth != m_width )
+ relayoutChildren = true;
+
+ // kdDebug( 6040 ) << floatingObjects << "," << oldWidth << ","
+ // << m_width << ","<< needsLayout() << "," << isAnonymousBox() << ","
+ // << overhangingContents() << "," << isPositioned() << endl;
+
+#ifdef DEBUG_LAYOUT
+ kdDebug( 6040 ) << renderName() << "(RenderBlock) " << this << " ::layout() width=" << m_width << ", needsLayout=" << needsLayout() << endl;
+ if(containingBlock() == static_cast<RenderObject *>(this))
+ kdDebug( 6040 ) << renderName() << ": containingBlock == this" << endl;
+#endif
+
+ clearFloats();
+
+ int previousHeight = m_height;
+ m_height = 0;
+ m_overflowHeight = 0;
+ m_clearStatus = CNONE;
+
+ // We use four values, maxTopPos, maxPosNeg, maxBottomPos, and maxBottomNeg, to track
+ // our current maximal positive and negative margins. These values are used when we
+ // are collapsed with adjacent blocks, so for example, if you have block A and B
+ // collapsing together, then you'd take the maximal positive margin from both A and B
+ // and subtract it from the maximal negative margin from both A and B to get the
+ // true collapsed margin. This algorithm is recursive, so when we finish layout()
+ // our block knows its current maximal positive/negative values.
+ //
+ // Start out by setting our margin values to our current margins. Table cells have
+ // no margins, so we don't fill in the values for table cells.
+ if (!isTableCell()) {
+ initMaxMarginValues();
+
+ m_topMarginQuirk = style()->marginTop().isQuirk();
+ m_bottomMarginQuirk = style()->marginBottom().isQuirk();
+
+ if (element() && element()->id() == ID_FORM && static_cast<HTMLFormElementImpl*>(element())->isMalformed())
+ // See if this form is malformed (i.e., unclosed). If so, don't give the form
+ // a bottom margin.
+ m_maxBottomPosMargin = m_maxBottomNegMargin = 0;
+ }
+
+ if (scrollsOverflow() && m_layer) {
+ // For overflow:scroll blocks, ensure we have both scrollbars in place always.
+ if (style()->overflowX() == OSCROLL)
+ m_layer->showScrollbar( Qt::Horizontal, true );
+ if (style()->overflowY() == OSCROLL)
+ m_layer->showScrollbar( Qt::Vertical, true );
+ }
+
+ setContainsPageBreak(false);
+
+ if (childrenInline())
+ layoutInlineChildren( relayoutChildren );
+ else
+ layoutBlockChildren( relayoutChildren );
+
+ // Expand our intrinsic height to encompass floats.
+ int toAdd = borderBottom() + paddingBottom();
+ if (m_layer && scrollsOverflowX() && style()->height().isVariable())
+ toAdd += m_layer->horizontalScrollbarHeight();
+ if ( hasOverhangingFloats() && (isFloatingOrPositioned() || flowAroundFloats()) )
+ m_overflowHeight = m_height = floatBottom() + toAdd;
+
+ int oldHeight = m_height;
+ calcHeight();
+ if (oldHeight != m_height) {
+ m_overflowHeight -= toAdd;
+ if (m_layer && scrollsOverflowY()) {
+ // overflow-height only includes padding-bottom when it scrolls
+ m_overflowHeight += paddingBottom();
+ }
+ // If the block got expanded in size, then increase our overflowheight to match.
+ if (m_overflowHeight < m_height)
+ m_overflowHeight = m_height;
+ }
+ if (previousHeight != m_height)
+ relayoutChildren = true;
+
+ if (isTableCell()) {
+ // Table cells need to grow to accommodate both overhanging floats and
+ // blocks that have overflowed content.
+ // Check for an overhanging float first.
+ // FIXME: This needs to look at the last flow, not the last child.
+ if (lastChild() && lastChild()->hasOverhangingFloats() && !lastChild()->hasOverflowClip()) {
+ KHTMLAssert(lastChild()->isRenderBlock());
+ m_height = lastChild()->yPos() + static_cast<RenderBlock*>(lastChild())->floatBottom();
+ m_height += borderBottom() + paddingBottom();
+ }
+
+ if (m_overflowHeight > m_height && !hasOverflowClip())
+ m_height = m_overflowHeight + borderBottom() + paddingBottom();
+ }
+
+ if( hasOverhangingFloats() && ((isFloating() && style()->height().isVariable()) || isTableCell())) {
+ m_height = floatBottom();
+ m_height += borderBottom() + paddingBottom();
+ }
+
+ if (canvas()->pagedMode()) {
+#ifdef PAGE_DEBUG
+ kdDebug(6040) << renderName() << " Page Bottom: " << pageTopAfter(0) << endl;
+ kdDebug(6040) << renderName() << " Bottom: " << m_height << endl;
+#endif
+ bool needsPageBreak = false;
+ int xpage = crossesPageBreak(0, m_height);
+ if (xpage) {
+ needsPageBreak = true;
+#ifdef PAGE_DEBUG
+ kdDebug( 6040 ) << renderName() << " crosses to page " << xpage << endl;
+#endif
+ }
+ if (needsPageBreak && !containsPageBreak()) {
+ setNeedsPageClear(true);
+#ifdef PAGE_DEBUG
+ kdDebug( 6040 ) << renderName() << " marked for page-clear" << endl;
+#endif
+ }
+ }
+
+ layoutPositionedObjects( relayoutChildren );
+
+ // Always ensure our overflow width/height are at least as large as our width/height.
+ m_overflowWidth = kMax(m_overflowWidth, (int)m_width);
+ m_overflowHeight = kMax(m_overflowHeight, m_height);
+
+ // Update our scrollbars if we're overflow:auto/scroll now that we know if
+ // we overflow or not.
+ if (hasOverflowClip() && m_layer)
+ m_layer->checkScrollbarsAfterLayout();
+
+ setNeedsLayout(false);
+}
+
+void RenderBlock::adjustPositionedBlock(RenderObject* child, const MarginInfo& marginInfo)
+{
+ if (child->isBox() && child->hasStaticX()) {
+ if (style()->direction() == LTR)
+ static_cast<RenderBox*>(child)->setStaticX(borderLeft() + paddingLeft());
+ else
+ static_cast<RenderBox*>(child)->setStaticX(borderRight() + paddingRight());
+ }
+
+ if (child->isBox() && child->hasStaticY()) {
+ int y = m_height;
+ if (!marginInfo.canCollapseWithTop()) {
+ child->calcVerticalMargins();
+ int marginTop = child->marginTop();
+ int collapsedTopPos = marginInfo.posMargin();
+ int collapsedTopNeg = marginInfo.negMargin();
+ if (marginTop > 0) {
+ if (marginTop > collapsedTopPos)
+ collapsedTopPos = marginTop;
+ } else {
+ if (-marginTop > collapsedTopNeg)
+ collapsedTopNeg = -marginTop;
+ }
+ y += (collapsedTopPos - collapsedTopNeg) - marginTop;
+ }
+ static_cast<RenderBox*>(child)->setStaticY(y);
+ }
+}
+
+void RenderBlock::adjustFloatingBlock(const MarginInfo& marginInfo)
+{
+ // The float should be positioned taking into account the bottom margin
+ // of the previous flow. We add that margin into the height, get the
+ // float positioned properly, and then subtract the margin out of the
+ // height again. In the case of self-collapsing blocks, we always just
+ // use the top margins, since the self-collapsing block collapsed its
+ // own bottom margin into its top margin.
+ //
+ // Note also that the previous flow may collapse its margin into the top of
+ // our block. If this is the case, then we do not add the margin in to our
+ // height when computing the position of the float. This condition can be tested
+ // for by simply calling canCollapseWithTop. See
+ // http://www.hixie.ch/tests/adhoc/css/box/block/margin-collapse/046.html for
+ // an example of this scenario.
+ int marginOffset = marginInfo.canCollapseWithTop() ? 0 : marginInfo.margin();
+ m_height += marginOffset;
+ positionNewFloats();
+ m_height -= marginOffset;
+}
+
+RenderObject* RenderBlock::handleSpecialChild(RenderObject* child, const MarginInfo& marginInfo, CompactInfo& compactInfo, bool& handled)
+{
+ // Handle positioned children first.
+ RenderObject* next = handlePositionedChild(child, marginInfo, handled);
+ if (handled) return next;
+
+ // Handle floating children next.
+ next = handleFloatingChild(child, marginInfo, handled);
+ if (handled) return next;
+
+ // See if we have a compact element. If we do, then try to tuck the compact element into the margin space of the next block.
+ next = handleCompactChild(child, compactInfo, marginInfo, handled);
+ if (handled) return next;
+
+ // Finally, see if we have a run-in element.
+ return handleRunInChild(child, handled);
+}
+
+RenderObject* RenderBlock::handlePositionedChild(RenderObject* child, const MarginInfo& marginInfo, bool& handled)
+{
+ if (child->isPositioned()) {
+ handled = true;
+ child->containingBlock()->insertPositionedObject(child);
+ adjustPositionedBlock(child, marginInfo);
+ return child->nextSibling();
+ }
+ return 0;
+}
+
+RenderObject* RenderBlock::handleFloatingChild(RenderObject* child, const MarginInfo& marginInfo, bool& handled)
+{
+ if (child->isFloating()) {
+ handled = true;
+ insertFloatingObject(child);
+ adjustFloatingBlock(marginInfo);
+ return child->nextSibling();
+ }
+ return 0;
+}
+
+static inline bool isAnonymousWhitespace( RenderObject* o ) {
+ if (!o->isAnonymous())
+ return false;
+ RenderObject *fc = o->firstChild();
+ return fc && fc == o->lastChild() && fc->isText() && static_cast<RenderText *>(fc)->stringLength() == 1 &&
+ static_cast<RenderText *>(fc)->text()[0].unicode() == ' ';
+}
+
+RenderObject* RenderBlock::handleCompactChild(RenderObject* child, CompactInfo& compactInfo, const MarginInfo& marginInfo, bool& handled)
+{
+ // FIXME: We only deal with one compact at a time. It is unclear what should be
+ // done if multiple contiguous compacts are encountered. For now we assume that
+ // compact A followed by another compact B should simply be treated as block A.
+ if (child->isCompact() && !compactInfo.compact() && (child->childrenInline() || child->isReplaced())) {
+ // Get the next non-positioned/non-floating RenderBlock.
+ RenderObject* next = child->nextSibling();
+ RenderObject* curr = next;
+ while (curr && (curr->isFloatingOrPositioned() || isAnonymousWhitespace(curr)) )
+ curr = curr->nextSibling();
+ if (curr && curr->isRenderBlock() && !curr->isCompact() && !curr->isRunIn()) {
+ curr->calcWidth(); // So that horizontal margins are correct.
+ // Need to compute margins for the child as though it is a block.
+ child->style()->setDisplay(BLOCK);
+ child->calcWidth();
+ child->style()->setDisplay(COMPACT);
+
+ int childMargins = child->marginLeft() + child->marginRight();
+ int margin = style()->direction() == LTR ? curr->marginLeft() : curr->marginRight();
+ if (margin >= (childMargins + child->maxWidth())) {
+ // The compact will fit in the margin.
+ handled = true;
+ compactInfo.set(child, curr);
+ child->layoutIfNeeded();
+ int off = marginInfo.margin();
+ m_height += off + curr->marginTop() < child->marginTop() ?
+ child->marginTop() - curr->marginTop() -off: 0;
+
+ child->setPos(0,0); // This position will be updated to reflect the compact's
+ // desired position and the line box for the compact will
+ // pick that position up.
+ return next;
+ }
+ }
+ }
+ return 0;
+}
+
+void RenderBlock::adjustSizeForCompactIfNeeded(RenderObject* child, CompactInfo& compactInfo)
+{
+ // if the compact is bigger than the block it was run into
+ // then "this" block should take the height of the compact
+ if (compactInfo.matches(child)) {
+ // We have a compact child to squeeze in.
+ RenderObject* compactChild = compactInfo.compact();
+ if (compactChild->height() > child->height())
+ m_height += compactChild->height() - child->height();
+ }
+}
+
+void RenderBlock::insertCompactIfNeeded(RenderObject* child, CompactInfo& compactInfo)
+{
+ if (compactInfo.matches(child)) {
+ // We have a compact child to squeeze in.
+ RenderObject* compactChild = compactInfo.compact();
+ int compactXPos = borderLeft() + paddingLeft() + compactChild->marginLeft();
+ if (style()->direction() == RTL) {
+ compactChild->calcWidth(); // have to do this because of the capped maxwidth
+ compactXPos = width() - borderRight() - paddingRight() -
+ compactChild->width() - compactChild->marginRight();
+ }
+
+ int compactYPos = child->yPos() + child->borderTop() + child->paddingTop()
+ - compactChild->paddingTop() - compactChild->borderTop();
+ int adj = 0;
+ KHTMLAssert(child->isRenderBlock());
+ InlineRunBox *b = static_cast<RenderBlock*>(child)->firstLineBox();
+ InlineRunBox *c = static_cast<RenderBlock*>(compactChild)->firstLineBox();
+ if (b && c) {
+ // adjust our vertical position
+ int vpos = compactChild->getVerticalPosition( true, child );
+ if (vpos == PositionBottom)
+ adj = b->height() > c->height() ? (b->height() + b->yPos() - c->height() - c->yPos()) : 0;
+ else if (vpos == PositionTop)
+ adj = b->yPos() - c->yPos();
+ else
+ adj = vpos;
+ compactYPos += adj;
+ }
+ Length newLineHeight( kMax(compactChild->lineHeight(true)+adj, (int)child->lineHeight(true)), khtml::Fixed);
+ child->style()->setLineHeight( newLineHeight );
+ child->setNeedsLayout( true, false );
+ child->layout();
+
+ compactChild->setPos(compactXPos, compactYPos); // Set the x position.
+ compactInfo.clear();
+ }
+}
+
+RenderObject* RenderBlock::handleRunInChild(RenderObject* child, bool& handled)
+{
+ // See if we have a run-in element with inline children. If the
+ // children aren't inline, then just treat the run-in as a normal
+ // block.
+ if (child->isRunIn() && (child->childrenInline() || child->isReplaced())) {
+ // Get the next non-positioned/non-floating RenderBlock.
+ RenderObject* curr = child->nextSibling();
+ while (curr && (curr->isFloatingOrPositioned() || isAnonymousWhitespace(curr)) )
+ curr = curr->nextSibling();
+ if (curr && (curr->isRenderBlock() && curr->childrenInline() && !curr->isCompact() && !curr->isRunIn())) {
+ // The block acts like an inline, so just null out its
+ // position.
+ handled = true;
+ child->setInline(true);
+ child->setPos(0,0);
+
+ // Remove the child.
+ RenderObject* next = child->nextSibling();
+ removeChildNode(child);
+
+ // Now insert the child under |curr|.
+ curr->insertChildNode(child, curr->firstChild());
+ return next;
+ }
+ }
+ return 0;
+}
+
+void RenderBlock::collapseMargins(RenderObject* child, MarginInfo& marginInfo, int yPosEstimate)
+{
+ // Get our max pos and neg top margins.
+ int posTop = child->maxTopMargin(true);
+ int negTop = child->maxTopMargin(false);
+
+ // For self-collapsing blocks, collapse our bottom margins into our
+ // top to get new posTop and negTop values.
+ if (child->isSelfCollapsingBlock()) {
+ posTop = kMax(posTop, (int)child->maxBottomMargin(true));
+ negTop = kMax(negTop, (int)child->maxBottomMargin(false));
+ }
+
+ // See if the top margin is quirky. We only care if this child has
+ // margins that will collapse with us.
+ bool topQuirk = child->isTopMarginQuirk() /*|| style()->marginTopCollapse() == MDISCARD*/;
+
+ if (marginInfo.canCollapseWithTop()) {
+ // This child is collapsing with the top of the
+ // block. If it has larger margin values, then we need to update
+ // our own maximal values.
+ if (!style()->htmlHacks() || !marginInfo.quirkContainer() || !topQuirk) {
+ m_maxTopPosMargin = kMax(posTop, (int)m_maxTopPosMargin);
+ m_maxTopNegMargin = kMax(negTop, (int)m_maxTopNegMargin);
+ }
+
+ // The minute any of the margins involved isn't a quirk, don't
+ // collapse it away, even if the margin is smaller (www.webreference.com
+ // has an example of this, a <dt> with 0.8em author-specified inside
+ // a <dl> inside a <td>.
+ if (!marginInfo.determinedTopQuirk() && !topQuirk && (posTop-negTop)) {
+ m_topMarginQuirk = false;
+ marginInfo.setDeterminedTopQuirk(true);
+ }
+
+ if (!marginInfo.determinedTopQuirk() && topQuirk && marginTop() == 0)
+ // We have no top margin and our top child has a quirky margin.
+ // We will pick up this quirky margin and pass it through.
+ // This deals with the <td><div><p> case.
+ // Don't do this for a block that split two inlines though. You do
+ // still apply margins in this case.
+ m_topMarginQuirk = true;
+ }
+
+ if (marginInfo.quirkContainer() && marginInfo.atTopOfBlock() && (posTop - negTop))
+ marginInfo.setTopQuirk(topQuirk);
+
+ int ypos = m_height;
+ if (child->isSelfCollapsingBlock()) {
+ // This child has no height. We need to compute our
+ // position before we collapse the child's margins together,
+ // so that we can get an accurate position for the zero-height block.
+ int collapsedTopPos = kMax(marginInfo.posMargin(), (int)child->maxTopMargin(true));
+ int collapsedTopNeg = kMax(marginInfo.negMargin(), (int)child->maxTopMargin(false));
+ marginInfo.setMargin(collapsedTopPos, collapsedTopNeg);
+
+ // Now collapse the child's margins together, which means examining our
+ // bottom margin values as well.
+ marginInfo.setPosMarginIfLarger(child->maxBottomMargin(true));
+ marginInfo.setNegMarginIfLarger(child->maxBottomMargin(false));
+
+ if (!marginInfo.canCollapseWithTop())
+ // We need to make sure that the position of the self-collapsing block
+ // is correct, since it could have overflowing content
+ // that needs to be positioned correctly (e.g., a block that
+ // had a specified height of 0 but that actually had subcontent).
+ ypos = m_height + collapsedTopPos - collapsedTopNeg;
+ }
+ else {
+#ifdef APPLE_CHANGES
+ if (child->style()->marginTopCollapse() == MSEPARATE) {
+ m_height += marginInfo.margin() + child->marginTop();
+ ypos = m_height;
+ }
+ else
+#endif
+ if (!marginInfo.atTopOfBlock() ||
+ (!marginInfo.canCollapseTopWithChildren()
+ && (!style()->htmlHacks() || !marginInfo.quirkContainer() || !marginInfo.topQuirk()))) {
+ // We're collapsing with a previous sibling's margins and not
+ // with the top of the block.
+ m_height += kMax(marginInfo.posMargin(), posTop) - kMax(marginInfo.negMargin(), negTop);
+ ypos = m_height;
+ }
+
+ marginInfo.setPosMargin(child->maxBottomMargin(true));
+ marginInfo.setNegMargin(child->maxBottomMargin(false));
+
+ if (marginInfo.margin())
+ marginInfo.setBottomQuirk(child->isBottomMarginQuirk() /*|| style()->marginBottomCollapse() == MDISCARD*/);
+
+ marginInfo.setSelfCollapsingBlockClearedFloat(false);
+ }
+
+ child->setPos(child->xPos(), ypos);
+ if (ypos != yPosEstimate) {
+ if (child->style()->width().isPercent() && child->usesLineWidth())
+ // The child's width is a percentage of the line width.
+ // When the child shifts to clear an item, its width can
+ // change (because it has more available line width).
+ // So go ahead and mark the item as dirty.
+ child->setChildNeedsLayout(true);
+
+ if (!child->flowAroundFloats() && child->hasFloats())
+ child->markAllDescendantsWithFloatsForLayout();
+
+ // Our guess was wrong. Make the child lay itself out again.
+ child->layoutIfNeeded();
+ }
+}
+
+void RenderBlock::clearFloatsIfNeeded(RenderObject* child, MarginInfo& marginInfo, int oldTopPosMargin, int oldTopNegMargin)
+{
+ int heightIncrease = getClearDelta(child);
+ if (heightIncrease) {
+ // The child needs to be lowered. Move the child so that it just clears the float.
+ child->setPos(child->xPos(), child->yPos() + heightIncrease);
+
+ // Increase our height by the amount we had to clear.
+ bool selfCollapsing = child->isSelfCollapsingBlock();
+ if (!selfCollapsing)
+ m_height += heightIncrease;
+ else {
+ // For self-collapsing blocks that clear, they may end up collapsing
+ // into the bottom of the parent block. We simulate this behavior by
+ // setting our positive margin value to compensate for the clear.
+ marginInfo.setPosMargin(kMax(0, child->yPos() - m_height));
+ marginInfo.setNegMargin(0);
+ marginInfo.setSelfCollapsingBlockClearedFloat(true);
+ }
+
+ if (marginInfo.canCollapseWithTop()) {
+ // We can no longer collapse with the top of the block since a clear
+ // occurred. The empty blocks collapse into the cleared block.
+ // FIXME: This isn't quite correct. Need clarification for what to do
+ // if the height the cleared block is offset by is smaller than the
+ // margins involved.
+ m_maxTopPosMargin = oldTopPosMargin;
+ m_maxTopNegMargin = oldTopNegMargin;
+ marginInfo.setAtTopOfBlock(false);
+ }
+
+ // If our value of clear caused us to be repositioned vertically to be
+ // underneath a float, we might have to do another layout to take into account
+ // the extra space we now have available.
+ if (!selfCollapsing && !child->style()->width().isFixed() && child->usesLineWidth())
+ // The child's width is a percentage of the line width.
+ // When the child shifts to clear an item, its width can
+ // change (because it has more available line width).
+ // So go ahead and mark the item as dirty.
+ child->setChildNeedsLayout(true);
+ if (!child->flowAroundFloats() && child->hasFloats())
+ child->markAllDescendantsWithFloatsForLayout();
+ child->layoutIfNeeded();
+ }
+}
+
+bool RenderBlock::canClear(RenderObject *child, PageBreakLevel level)
+{
+ KHTMLAssert(child->parent() && child->parent() == this);
+ KHTMLAssert(canvas()->pagedMode());
+
+ // Positioned elements cannot be moved. Only normal flow and floating.
+ if (child->isPositioned() || child->isRelPositioned()) return false;
+
+ switch(level) {
+ case PageBreakNormal:
+ // check page-break-inside: avoid
+ if (!style()->pageBreakInside())
+ // we cannot, but can our parent?
+ if(!parent()->canClear(this, level)) return false;
+ case PageBreakHarder:
+ // check page-break-after/before: avoid
+ if (m_avoidPageBreak)
+ // we cannot, but can our parent?
+ if(!parent()->canClear(this, level)) return false;
+ case PageBreakForced:
+ // child is larger than page-height and is forced to break
+ if(child->height() > canvas()->pageHeight()) return false;
+ return true;
+ }
+ assert(false);
+ return false;
+}
+
+void RenderBlock::clearPageBreak(RenderObject* child, int pageBottom)
+{
+ KHTMLAssert(child->parent() && child->parent() == this);
+ KHTMLAssert(canvas()->pagedMode());
+
+ if (child->yPos() >= pageBottom) return;
+
+ int heightIncrease = 0;
+
+ heightIncrease = pageBottom - child->yPos();
+
+ // ### should never happen, canClear should have been called to detect it.
+ if (child->height() > canvas()->pageHeight()) {
+ kdDebug(6040) << "### child is too large to clear: " << child->height() << " > " << canvas()->pageHeight() << endl;
+ return;
+ }
+
+ // The child needs to be lowered. Move the child so that it just clears the break.
+ child->setPos(child->xPos(), pageBottom);
+
+#ifdef PAGE_DEBUG
+ kdDebug(6040) << "Cleared block " << heightIncrease << "px" << endl;
+#endif
+
+ // Increase our height by the amount we had to clear.
+ m_height += heightIncrease;
+
+ // We might have to do another layout to take into account
+ // the extra space we now have available.
+ if (!child->style()->width().isFixed() && child->usesLineWidth())
+ // The child's width is a percentage of the line width.
+ // When the child shifts to clear a page-break, its width can
+ // change (because it has more available line width).
+ // So go ahead and mark the item as dirty.
+ child->setChildNeedsLayout(true);
+ if (!child->flowAroundFloats() && child->hasFloats())
+ child->markAllDescendantsWithFloatsForLayout();
+ if (child->containsPageBreak())
+ child->setNeedsLayout(true);
+ child->layoutIfNeeded();
+
+ child->setAfterPageBreak(true);
+}
+
+int RenderBlock::estimateVerticalPosition(RenderObject* child, const MarginInfo& marginInfo)
+{
+ // FIXME: We need to eliminate the estimation of vertical position, because
+ // when it's wrong we sometimes trigger a pathological relayout if there are
+ // intruding floats.
+ int yPosEstimate = m_height;
+ if (!marginInfo.canCollapseWithTop()) {
+ int childMarginTop = child->selfNeedsLayout() ? child->marginTop() : child->collapsedMarginTop();
+ yPosEstimate += kMax(marginInfo.margin(), childMarginTop);
+ }
+ return yPosEstimate;
+}
+
+void RenderBlock::determineHorizontalPosition(RenderObject* child)
+{
+ if (style()->direction() == LTR) {
+ int xPos = borderLeft() + paddingLeft();
+
+ // Add in our left margin.
+ int chPos = xPos + child->marginLeft();
+
+ // Some objects (e.g., tables, horizontal rules, overflow:auto blocks) avoid floats. They need
+ // to shift over as necessary to dodge any floats that might get in the way.
+ if (child->flowAroundFloats()) {
+ int leftOff = leftOffset(m_height);
+ if (style()->textAlign() != KHTML_CENTER && !child->style()->marginLeft().isVariable()) {
+ if (child->marginLeft() < 0)
+ leftOff += child->marginLeft();
+ chPos = kMax(chPos, leftOff); // Let the float sit in the child's margin if it can fit.
+ }
+ else if (leftOff != xPos) {
+ // The object is shifting right. The object might be centered, so we need to
+ // recalculate our horizontal margins. Note that the containing block content
+ // width computation will take into account the delta between |leftOff| and |xPos|
+ // so that we can just pass the content width in directly to the |calcHorizontalMargins|
+ // function.
+ static_cast<RenderBox*>(child)->calcHorizontalMargins(child->style()->marginLeft(), child->style()->marginRight(), lineWidth(child->yPos()));
+ chPos = leftOff + child->marginLeft();
+ }
+ }
+
+ child->setPos(chPos, child->yPos());
+ } else {
+ int xPos = m_width - borderRight() - paddingRight();
+ if (m_layer && scrollsOverflowY())
+ xPos -= m_layer->verticalScrollbarWidth();
+ int chPos = xPos - (child->width() + child->marginRight());
+ if (child->flowAroundFloats()) {
+ int rightOff = rightOffset(m_height);
+ if (style()->textAlign() != KHTML_CENTER && !child->style()->marginRight().isVariable()) {
+ if (child->marginRight() < 0)
+ rightOff -= child->marginRight();
+ chPos = kMin(chPos, rightOff - child->width()); // Let the float sit in the child's margin if it can fit.
+ } else if (rightOff != xPos) {
+ // The object is shifting left. The object might be centered, so we need to
+ // recalculate our horizontal margins. Note that the containing block content
+ // width computation will take into account the delta between |rightOff| and |xPos|
+ // so that we can just pass the content width in directly to the |calcHorizontalMargins|
+ // function.
+ static_cast<RenderBox*>(child)->calcHorizontalMargins(child->style()->marginLeft(), child->style()->marginRight(), lineWidth(child->yPos()));
+ chPos = rightOff - child->marginRight() - child->width();
+ }
+ }
+ child->setPos(chPos, child->yPos());
+ }
+}
+
+void RenderBlock::setCollapsedBottomMargin(const MarginInfo& marginInfo)
+{
+ if (marginInfo.canCollapseWithBottom() && !marginInfo.canCollapseWithTop()) {
+ // Update our max pos/neg bottom margins, since we collapsed our bottom margins
+ // with our children.
+ m_maxBottomPosMargin = kMax((int)m_maxBottomPosMargin, marginInfo.posMargin());
+ m_maxBottomNegMargin = kMax((int)m_maxBottomNegMargin, marginInfo.negMargin());
+
+ if (!marginInfo.bottomQuirk())
+ m_bottomMarginQuirk = false;
+
+ if (marginInfo.bottomQuirk() && marginBottom() == 0)
+ // We have no bottom margin and our last child has a quirky margin.
+ // We will pick up this quirky margin and pass it through.
+ // This deals with the <td><div><p> case.
+ m_bottomMarginQuirk = true;
+ }
+}
+
+void RenderBlock::handleBottomOfBlock(int top, int bottom, MarginInfo& marginInfo)
+{
+ // If our last flow was a self-collapsing block that cleared a float, then we don't
+ // collapse it with the bottom of the block.
+ if (!marginInfo.selfCollapsingBlockClearedFloat())
+ marginInfo.setAtBottomOfBlock(true);
+
+ // If we can't collapse with children then go ahead and add in the bottom margin.
+ if (!marginInfo.canCollapseWithBottom() && !marginInfo.canCollapseWithTop()
+ && (!style()->htmlHacks() || !marginInfo.quirkContainer() || !marginInfo.bottomQuirk()))
+ m_height += marginInfo.margin();
+
+ // Now add in our bottom border/padding.
+ m_height += bottom;
+
+ // Negative margins can cause our height to shrink below our minimal height (border/padding).
+ // If this happens, ensure that the computed height is increased to the minimal height.
+ m_height = kMax(m_height, top + bottom);
+
+ // Always make sure our overflow height is at least our height.
+ m_overflowHeight = kMax(m_height, m_overflowHeight);
+
+ // Update our bottom collapsed margin info.
+ setCollapsedBottomMargin(marginInfo);
+}
+
+void RenderBlock::layoutBlockChildren( bool relayoutChildren )
+{
+#ifdef DEBUG_LAYOUT
+ kdDebug( 6040 ) << renderName() << " layoutBlockChildren( " << this <<" ), relayoutChildren="<< relayoutChildren << endl;
+#endif
+
+ int top = borderTop() + paddingTop();
+ int bottom = borderBottom() + paddingBottom();
+ if (m_layer && scrollsOverflowX() && style()->height().isVariable())
+ bottom += m_layer->horizontalScrollbarHeight();
+
+ m_height = m_overflowHeight = top;
+
+ // The margin struct caches all our current margin collapsing state.
+ // The compact struct caches state when we encounter compacts.
+ MarginInfo marginInfo(this, top, bottom);
+ CompactInfo compactInfo;
+
+ // Fieldsets need to find their legend and position it inside the border of the object.
+ // The legend then gets skipped during normal layout.
+ RenderObject* legend = layoutLegend(relayoutChildren);
+
+ PageBreakInfo pageBreakInfo(pageTopAfter(0));
+
+ RenderObject* child = firstChild();
+ while( child != 0 )
+ {
+ if (legend == child) {
+ child = child->nextSibling();
+ continue; // Skip the legend, since it has already been positioned up in the fieldset's border.
+ }
+
+ int oldTopPosMargin = m_maxTopPosMargin;
+ int oldTopNegMargin = m_maxTopNegMargin;
+
+ // make sure we relayout children if we need it.
+ if (!child->isPositioned() && (relayoutChildren ||
+ (child->isReplaced() && (child->style()->width().isPercent() || child->style()->height().isPercent())) ||
+ (child->isRenderBlock() && child->style()->height().isPercent()) ||
+ (child->isBody() && child->style()->htmlHacks())))
+ {
+ child->setChildNeedsLayout(true);
+ }
+
+ // Handle the four types of special elements first. These include positioned content, floating content, compacts and
+ // run-ins. When we encounter these four types of objects, we don't actually lay them out as normal flow blocks.
+ bool handled = false;
+ RenderObject* next = handleSpecialChild(child, marginInfo, compactInfo, handled);
+ if (handled) { child = next; continue; }
+
+ // The child is a normal flow object. Compute its vertical margins now.
+ child->calcVerticalMargins();
+
+#ifdef APPLE_CHANGES /* margin-*-collapse not merged yet */
+ // Do not allow a collapse if the margin top collapse style is set to SEPARATE.
+ if (child->style()->marginTopCollapse() == MSEPARATE) {
+ marginInfo.setAtTopOfBlock(false);
+ marginInfo.clearMargin();
+ }
+#endif
+
+ // Try to guess our correct y position. In most cases this guess will
+ // be correct. Only if we're wrong (when we compute the real y position)
+ // will we have to potentially relayout.
+ int yPosEstimate = estimateVerticalPosition(child, marginInfo);
+
+ // If an element might be affected by the presence of floats, then always mark it for
+ // layout.
+ if ( !child->flowAroundFloats() || child->usesLineWidth() ) {
+ int fb = floatBottom();
+ if (fb > m_height || fb > yPosEstimate)
+ child->setChildNeedsLayout(true);
+ }
+
+ // Go ahead and position the child as though it didn't collapse with the top.
+ child->setPos(child->xPos(), yPosEstimate);
+ child->layoutIfNeeded();
+
+ // Now determine the correct ypos based on examination of collapsing margin
+ // values.
+ collapseMargins(child, marginInfo, yPosEstimate);
+
+ // Now check for clear.
+ clearFloatsIfNeeded(child, marginInfo, oldTopPosMargin, oldTopNegMargin);
+
+ // We are no longer at the top of the block if we encounter a non-empty child.
+ // This has to be done after checking for clear, so that margins can be reset if a clear occurred.
+ if (marginInfo.atTopOfBlock() && !child->isSelfCollapsingBlock())
+ marginInfo.setAtTopOfBlock(false);
+
+ // Now place the child in the correct horizontal position
+ determineHorizontalPosition(child);
+
+ adjustSizeForCompactIfNeeded(child, compactInfo);
+ // Update our height now that the child has been placed in the correct position.
+ m_height += child->height();
+
+#ifdef APPLE_CHANGES
+ if (child->style()->marginBottomCollapse() == MSEPARATE) {
+ m_height += child->marginBottom();
+ marginInfo.clearMargin();
+ }
+#endif
+
+ // Check for page-breaks
+ if (canvas()->pagedMode())
+ clearChildOfPageBreaks(child, pageBreakInfo, marginInfo);
+
+ if (child->hasOverhangingFloats() && !child->flowAroundFloats()) {
+ // need to add the child's floats to our floating objects list, but not in the case where
+ // overflow is auto/scroll
+ addOverHangingFloats( static_cast<RenderBlock *>(child), -child->xPos(), -child->yPos(), true );
+ }
+
+ // See if this child has made our overflow need to grow.
+ int effX = child->effectiveXPos();
+ int effY = child->effectiveYPos();
+ m_overflowWidth = kMax(effX + child->effectiveWidth(), m_overflowWidth);
+ m_overflowLeft = kMin(effX, m_overflowLeft);
+ m_overflowHeight = kMax(effY + child->effectiveHeight(), m_overflowHeight);
+ m_overflowTop = kMin(effY, m_overflowTop);
+
+ // Insert our compact into the block margin if we have one.
+ insertCompactIfNeeded(child, compactInfo);
+
+ child = child->nextSibling();
+ }
+
+ // The last child had forced page-break-after
+ if (pageBreakInfo.forcePageBreak())
+ m_height = pageBreakInfo.pageBottom();
+
+ // Now do the handling of the bottom of the block, adding in our bottom border/padding and
+ // determining the correct collapsed bottom margin information.
+ handleBottomOfBlock(top, bottom, marginInfo);
+
+ setNeedsLayout(false);
+}
+
+void RenderBlock::clearChildOfPageBreaks(RenderObject *child, PageBreakInfo &pageBreakInfo, MarginInfo &marginInfo)
+{
+ (void)marginInfo;
+ int childTop = child->yPos();
+ int childBottom = child->yPos()+child->height();
+#ifdef PAGE_DEBUG
+ kdDebug(6040) << renderName() << " ChildTop: " << childTop << " ChildBottom: " << childBottom << endl;
+#endif
+
+ bool forcePageBreak = pageBreakInfo.forcePageBreak() || child->style()->pageBreakBefore() == PBALWAYS;
+#ifdef PAGE_DEBUG
+ if (forcePageBreak)
+ kdDebug(6040) << renderName() << "Forced break required" << endl;
+#endif
+
+ int xpage = crossesPageBreak(childTop, childBottom);
+ if (xpage || forcePageBreak)
+ {
+ if (!forcePageBreak && child->containsPageBreak() && !child->needsPageClear()) {
+#ifdef PAGE_DEBUG
+ kdDebug(6040) << renderName() << " Child contains page-break to page " << xpage << endl;
+#endif
+ // ### Actually this assumes floating children are breaking/clearing
+ // nicely as well.
+ setContainsPageBreak(true);
+ }
+ else {
+ bool doBreak = true;
+ // don't break before the first child or when page-break-inside is avoid
+ if (!forcePageBreak && (!style()->pageBreakInside() || m_avoidPageBreak || child == firstChild())) {
+ if (parent()->canClear(this, (m_avoidPageBreak) ? PageBreakHarder : PageBreakNormal )) {
+#ifdef PAGE_DEBUG
+ kdDebug(6040) << renderName() << "Avoid page-break inside" << endl;
+#endif
+ child->setNeedsPageClear(false);
+ setNeedsPageClear(true);
+ doBreak = false;
+ }
+#ifdef PAGE_DEBUG
+ else
+ kdDebug(6040) << renderName() << "Ignoring page-break avoid" << endl;
+#endif
+ }
+ if (doBreak) {
+#ifdef PAGE_DEBUG
+ kdDebug(6040) << renderName() << " Clearing child of page-break" << endl;
+ kdDebug(6040) << renderName() << " child top of page " << xpage << endl;
+#endif
+ clearPageBreak(child, pageBreakInfo.pageBottom());
+ child->setNeedsPageClear(false);
+ setContainsPageBreak(true);
+ }
+ }
+ pageBreakInfo.setPageBottom(pageBreakInfo.pageBottom() + canvas()->pageHeight());
+ }
+ else
+ if (child->yPos() >= pageBreakInfo.pageBottom()) {
+ bool doBreak = true;
+#ifdef PAGE_DEBUG
+ kdDebug(6040) << "Page-break between children" << endl;
+#endif
+ if (!style()->pageBreakInside() || m_avoidPageBreak) {
+ if(parent()->canClear(this, (m_avoidPageBreak) ? PageBreakHarder : PageBreakNormal )) {
+#ifdef PAGE_DEBUG
+ kdDebug(6040) << "Avoid page-break inside" << endl;
+#endif
+ child->setNeedsPageClear(false);
+ setNeedsPageClear(true);
+ doBreak = false;
+ }
+#ifdef PAGE_DEBUG
+ else
+ kdDebug(6040) << "Ignoring page-break avoid" << endl;
+#endif
+ }
+ if (doBreak) {
+ // Break between children
+ setContainsPageBreak(true);
+ // ### Should collapse top-margin with page-margin
+ }
+ pageBreakInfo.setPageBottom(pageBreakInfo.pageBottom() + canvas()->pageHeight());
+ }
+
+ // Do we need a forced page-break before next child?
+ pageBreakInfo.setForcePageBreak(false);
+ if (child->style()->pageBreakAfter() == PBALWAYS)
+ pageBreakInfo.setForcePageBreak(true);
+}
+
+void RenderBlock::layoutPositionedObjects(bool relayoutChildren)
+{
+ if (m_positionedObjects) {
+ //kdDebug( 6040 ) << renderName() << " " << this << "::layoutPositionedObjects() start" << endl;
+ RenderObject* r;
+ QPtrListIterator<RenderObject> it(*m_positionedObjects);
+ for ( ; (r = it.current()); ++it ) {
+ //kdDebug(6040) << " have a positioned object" << endl;
+ if (r->markedForRepaint()) {
+ r->repaintDuringLayout();
+ r->setMarkedForRepaint(false);
+ }
+ if ( relayoutChildren || r->style()->position() == FIXED ||
+ ((r->hasStaticY()||r->hasStaticX()) && r->parent() != this && r->parent()->isBlockFlow()) ) {
+ r->setChildNeedsLayout(true);
+ r->dirtyFormattingContext(false);
+ }
+ r->layoutIfNeeded();
+ }
+ }
+}
+
+void RenderBlock::paint(PaintInfo& pI, int _tx, int _ty)
+{
+ _tx += m_x;
+ _ty += m_y;
+
+ // check if we need to do anything at all...
+ if (!isRoot() && !isInlineFlow() && !overhangingContents() && !isRelPositioned() && !isPositioned() )
+ {
+ int h = m_overflowHeight;
+ int yPos = _ty;
+ if (m_floatingObjects && floatBottom() > h)
+ h = floatBottom();
+
+ yPos += overflowTop();
+
+ int os = maximalOutlineSize(pI.phase);
+ if( (yPos > pI.r.bottom() + os) || (_ty + h <= pI.r.y() - os))
+ return;
+ }
+
+ paintObject(pI, _tx, _ty);
+}
+
+void RenderBlock::paintObject(PaintInfo& pI, int _tx, int _ty, bool shouldPaintOutline)
+{
+#ifdef DEBUG_LAYOUT
+ //kdDebug( 6040 ) << renderName() << "(RenderBlock) " << this << " ::paintObject() w/h = (" << width() << "/" << height() << ")" << endl;
+#endif
+
+ // If we're a repositioned run-in, don't paint background/borders.
+ bool inlineFlow = isInlineFlow();
+
+ // 1. paint background, borders etc
+ if (!inlineFlow &&
+ (pI.phase == PaintActionElementBackground || pI.phase == PaintActionChildBackground ) &&
+ shouldPaintBackgroundOrBorder() && style()->visibility() == VISIBLE)
+ paintBoxDecorations(pI, _tx, _ty);
+
+ if ( pI.phase == PaintActionElementBackground )
+ return;
+ if ( pI.phase == PaintActionChildBackgrounds )
+ pI.phase = PaintActionChildBackground;
+
+ // 2. paint contents
+ int scrolledX = _tx;
+ int scrolledY = _ty;
+ if (hasOverflowClip() && m_layer)
+ m_layer->subtractScrollOffset(scrolledX, scrolledY);
+
+ if (childrenInline())
+ paintLines(pI, scrolledX, scrolledY);
+ else {
+ for(RenderObject *child = firstChild(); child; child = child->nextSibling())
+ if(!child->layer() && !child->isFloating())
+ child->paint(pI, scrolledX, scrolledY);
+ }
+
+ // 3. paint floats.
+ if (!inlineFlow && (pI.phase == PaintActionFloat || pI.phase == PaintActionSelection))
+ paintFloats(pI, scrolledX, scrolledY, pI.phase == PaintActionSelection);
+
+ // 4. paint outline.
+ if (shouldPaintOutline && !inlineFlow && pI.phase == PaintActionOutline &&
+ style()->outlineWidth() && style()->visibility() == VISIBLE)
+ paintOutline(pI.p, _tx, _ty, width(), height(), style());
+
+#ifdef BOX_DEBUG
+ if ( style() && style()->visibility() == VISIBLE ) {
+ if(isAnonymous())
+ outlineBox(pI.p, _tx, _ty, "green");
+ if(isFloating())
+ outlineBox(pI.p, _tx, _ty, "yellow");
+ else
+ outlineBox(pI.p, _tx, _ty);
+ }
+#endif
+}
+
+void RenderBlock::paintFloats(PaintInfo& pI, int _tx, int _ty, bool paintSelection)
+{
+ if (!m_floatingObjects)
+ return;
+
+ FloatingObject* r;
+ QPtrListIterator<FloatingObject> it(*m_floatingObjects);
+ for ( ; (r = it.current()); ++it) {
+ // Only paint the object if our noPaint flag isn't set.
+ if (r->node->isFloating() && !r->noPaint && !r->node->layer()) {
+ PaintAction oldphase = pI.phase;
+ if (paintSelection) {
+ pI.phase = PaintActionSelection;
+ r->node->paint(pI, _tx + r->left - r->node->xPos() + r->node->marginLeft(),
+ _ty + r->startY - r->node->yPos() + r->node->marginTop());
+ }
+ else {
+ pI.phase = PaintActionElementBackground;
+ r->node->paint(pI,
+ _tx + r->left - r->node->xPos() + r->node->marginLeft(),
+ _ty + r->startY - r->node->yPos() + r->node->marginTop());
+ pI.phase = PaintActionChildBackgrounds;
+ r->node->paint(pI,
+ _tx + r->left - r->node->xPos() + r->node->marginLeft(),
+ _ty + r->startY - r->node->yPos() + r->node->marginTop());
+ pI.phase = PaintActionFloat;
+ r->node->paint(pI,
+ _tx + r->left - r->node->xPos() + r->node->marginLeft(),
+ _ty + r->startY - r->node->yPos() + r->node->marginTop());
+ pI.phase = PaintActionForeground;
+ r->node->paint(pI,
+ _tx + r->left - r->node->xPos() + r->node->marginLeft(),
+ _ty + r->startY - r->node->yPos() + r->node->marginTop());
+ pI.phase = PaintActionOutline;
+ r->node->paint(pI,
+ _tx + r->left - r->node->xPos() + r->node->marginLeft(),
+ _ty + r->startY - r->node->yPos() + r->node->marginTop());
+ }
+ pI.phase = oldphase;
+ }
+ }
+}
+
+void RenderBlock::insertPositionedObject(RenderObject *o)
+{
+ // Create the list of special objects if we don't aleady have one
+ if (!m_positionedObjects) {
+ m_positionedObjects = new QPtrList<RenderObject>;
+ m_positionedObjects->setAutoDelete(false);
+ }
+ else {
+ // Don't insert the object again if it's already in the list
+ QPtrListIterator<RenderObject> it(*m_positionedObjects);
+ RenderObject* f;
+ while ( (f = it.current()) ) {
+ if (f == o) return;
+ ++it;
+ }
+ }
+
+ // Create the special object entry & append it to the list
+ setOverhangingContents();
+ m_positionedObjects->append(o);
+}
+
+void RenderBlock::removePositionedObject(RenderObject *o)
+{
+ if (m_positionedObjects) {
+ QPtrListIterator<RenderObject> it(*m_positionedObjects);
+ while (it.current()) {
+ if (it.current() == o)
+ m_positionedObjects->removeRef(it.current());
+ ++it;
+ }
+ if (m_positionedObjects->isEmpty()) {
+ delete m_positionedObjects;
+ m_positionedObjects = 0;
+ }
+ }
+}
+
+void RenderBlock::insertFloatingObject(RenderObject *o)
+{
+ // Create the list of special objects if we don't aleady have one
+ if (!m_floatingObjects) {
+ m_floatingObjects = new QPtrList<FloatingObject>;
+ m_floatingObjects->setAutoDelete(true);
+ }
+ else {
+ // Don't insert the object again if it's already in the list
+ QPtrListIterator<FloatingObject> it(*m_floatingObjects);
+ FloatingObject* f;
+ while ( (f = it.current()) ) {
+ if (f->node == o) return;
+ ++it;
+ }
+ }
+
+ // Create the special object entry & append it to the list
+
+ FloatingObject *newObj;
+ if (o->isFloating()) {
+ // floating object
+ o->layoutIfNeeded();
+
+ if(o->style()->floating() & FLEFT)
+ newObj = new FloatingObject(FloatingObject::FloatLeft);
+ else
+ newObj = new FloatingObject(FloatingObject::FloatRight);
+
+ newObj->startY = -500000;
+ newObj->endY = -500000;
+ newObj->width = o->width() + o->marginLeft() + o->marginRight();
+ }
+ else {
+ // We should never get here, as insertFloatingObject() should only ever be called with floating
+ // objects.
+ KHTMLAssert(false);
+ newObj = 0; // keep gcc's uninitialized variable warnings happy
+ }
+
+ newObj->node = o;
+
+ m_floatingObjects->append(newObj);
+}
+
+void RenderBlock::removeFloatingObject(RenderObject *o)
+{
+ if (m_floatingObjects) {
+ QPtrListIterator<FloatingObject> it(*m_floatingObjects);
+ while (it.current()) {
+ if (it.current()->node == o)
+ m_floatingObjects->removeRef(it.current());
+ ++it;
+ }
+ }
+}
+
+void RenderBlock::positionNewFloats()
+{
+ if(!m_floatingObjects) return;
+ FloatingObject *f = m_floatingObjects->getLast();
+ if(!f || f->startY != -500000) return;
+ FloatingObject *lastFloat;
+ while(1)
+ {
+ lastFloat = m_floatingObjects->prev();
+ if (!lastFloat || lastFloat->startY != -500000) {
+ m_floatingObjects->next();
+ break;
+ }
+ f = lastFloat;
+ }
+
+ int y = m_height;
+
+
+ // the float can not start above the y position of the last positioned float.
+ if(lastFloat && lastFloat->startY > y)
+ y = lastFloat->startY;
+
+ while(f)
+ {
+ //skip elements copied from elsewhere and positioned elements
+ if (f->node->containingBlock()!=this)
+ {
+ f = m_floatingObjects->next();
+ continue;
+ }
+
+ RenderObject *o = f->node;
+ int _height = o->height() + o->marginTop() + o->marginBottom();
+
+ // floats avoid page-breaks
+ if(canvas()->pagedMode())
+ {
+ int top = y;
+ int bottom = y + o->height();
+ if (crossesPageBreak(top, bottom) && o->height() < canvas()->pageHeight() ) {
+ int newY = pageTopAfter(top);
+#ifdef PAGE_DEBUG
+ kdDebug(6040) << renderName() << " clearing float " << newY - y << "px" << endl;
+#endif
+ y = newY;
+ }
+ }
+
+ int ro = rightOffset(); // Constant part of right offset.
+ int lo = leftOffset(); // Constant part of left offset.
+ int fwidth = f->width; // The width we look for.
+ //kdDebug( 6040 ) << " Object width: " << fwidth << " available width: " << ro - lo << endl;
+
+ // in quirk mode, floated auto-width tables try to fit within remaining linewidth
+ bool ftQuirk = o->isTable() && style()->htmlHacks() && o->style()->width().isVariable();
+ if (ftQuirk)
+ fwidth = kMin( o->minWidth()+o->marginLeft()+o->marginRight(), fwidth );
+
+ if (ro - lo < fwidth)
+ fwidth = ro - lo; // Never look for more than what will be available.
+
+ if ( o->style()->clear() & CLEFT )
+ y = kMax( leftBottom(), y );
+ if ( o->style()->clear() & CRIGHT )
+ y = kMax( rightBottom(), y );
+
+ bool canClearLine;
+ if (o->style()->floating() & FLEFT)
+ {
+ int heightRemainingLeft = 1;
+ int heightRemainingRight = 1;
+ int fx = leftRelOffset(y,lo, false, &heightRemainingLeft, &canClearLine);
+ if (canClearLine)
+ {
+ while (rightRelOffset(y,ro, false, &heightRemainingRight)-fx < fwidth)
+ {
+ y += kMin( heightRemainingLeft, heightRemainingRight );
+ fx = leftRelOffset(y,lo, false, &heightRemainingLeft);
+ }
+ }
+ if (ftQuirk && (rightRelOffset(y,ro, false)-fx < f->width)) {
+ o->setPos( o->xPos(), y + o->marginTop() );
+ o->setChildNeedsLayout(true, false);
+ o->layoutIfNeeded();
+ _height = o->height() + o->marginTop() + o->marginBottom();
+ f->width = o->width() + o->marginLeft() + o->marginRight();
+ }
+ f->left = fx;
+ //kdDebug( 6040 ) << "positioning left aligned float at (" << fx + o->marginLeft() << "/" << y + o->marginTop() << ") fx=" << fx << endl;
+ o->setPos(fx + o->marginLeft(), y + o->marginTop());
+ }
+ else
+ {
+ int heightRemainingLeft = 1;
+ int heightRemainingRight = 1;
+ int fx = rightRelOffset(y,ro, false, &heightRemainingRight, &canClearLine);
+ if (canClearLine)
+ {
+ while (fx - leftRelOffset(y,lo, false, &heightRemainingLeft) < fwidth)
+ {
+ y += kMin(heightRemainingLeft, heightRemainingRight);
+ fx = rightRelOffset(y,ro, false, &heightRemainingRight);
+ }
+ }
+ if (ftQuirk && (fx - leftRelOffset(y,lo, false) < f->width)) {
+ o->setPos( o->xPos(), y + o->marginTop() );
+ o->setChildNeedsLayout(true, false);
+ o->layoutIfNeeded();
+ _height = o->height() + o->marginTop() + o->marginBottom();
+ f->width = o->width() + o->marginLeft() + o->marginRight();
+ }
+ f->left = fx - f->width;
+ //kdDebug( 6040 ) << "positioning right aligned float at (" << fx - o->marginRight() - o->width() << "/" << y + o->marginTop() << ")" << endl;
+ o->setPos(fx - o->marginRight() - o->width(), y + o->marginTop());
+ }
+
+ if ( m_layer && hasOverflowClip()) {
+ if (o->xPos()+o->width() > m_overflowWidth)
+ m_overflowWidth = o->xPos()+o->width();
+ else
+ if (o->xPos() < m_overflowLeft)
+ m_overflowLeft = o->xPos();
+ }
+
+ f->startY = y;
+ f->endY = f->startY + _height;
+
+
+ //kdDebug( 6040 ) << "floatingObject x/y= (" << f->left << "/" << f->startY << "-" << f->width << "/" << f->endY - f->startY << ")" << endl;
+
+ f = m_floatingObjects->next();
+ }
+}
+
+void RenderBlock::newLine()
+{
+ positionNewFloats();
+ // set y position
+ int newY = 0;
+ switch(m_clearStatus)
+ {
+ case CLEFT:
+ newY = leftBottom();
+ break;
+ case CRIGHT:
+ newY = rightBottom();
+ break;
+ case CBOTH:
+ newY = floatBottom();
+ default:
+ break;
+ }
+ if(m_height < newY)
+ {
+ // kdDebug( 6040 ) << "adjusting y position" << endl;
+ m_height = newY;
+ }
+ m_clearStatus = CNONE;
+}
+
+int
+RenderBlock::leftOffset() const
+{
+ return borderLeft()+paddingLeft();
+}
+
+int
+RenderBlock::leftRelOffset(int y, int fixedOffset, bool applyTextIndent, int *heightRemaining, bool *canClearLine ) const
+{
+ int left = fixedOffset;
+ if (canClearLine) *canClearLine = true;
+
+ if (m_floatingObjects) {
+ if ( heightRemaining ) *heightRemaining = 1;
+ FloatingObject* r;
+ QPtrListIterator<FloatingObject> it(*m_floatingObjects);
+ for ( ; (r = it.current()); ++it )
+ {
+ //kdDebug( 6040 ) <<(void *)this << " left: sy, ey, x, w " << r->startY << "," << r->endY << "," << r->left << "," << r->width << " " << endl;
+ if (r->startY <= y && r->endY > y &&
+ r->type == FloatingObject::FloatLeft &&
+ r->left + r->width > left) {
+ left = r->left + r->width;
+ if ( heightRemaining ) *heightRemaining = r->endY - y;
+ if ( canClearLine ) *canClearLine = (r->node->style()->floating() != FLEFT_ALIGN);
+ }
+ }
+ }
+
+ if (applyTextIndent && m_firstLine && style()->direction() == LTR ) {
+ int cw=0;
+ if (style()->textIndent().isPercent())
+ cw = containingBlock()->contentWidth();
+ left += style()->textIndent().minWidth(cw);
+ }
+
+ //kdDebug( 6040 ) << "leftOffset(" << y << ") = " << left << endl;
+ return left;
+}
+
+int
+RenderBlock::rightOffset() const
+{
+ int right = m_width - borderRight() - paddingRight();
+ if (m_layer && scrollsOverflowY())
+ right -= m_layer->verticalScrollbarWidth();
+ return right;
+}
+
+int
+RenderBlock::rightRelOffset(int y, int fixedOffset, bool applyTextIndent, int *heightRemaining, bool *canClearLine ) const
+{
+ int right = fixedOffset;
+ if (canClearLine) *canClearLine = true;
+
+ if (m_floatingObjects) {
+ if (heightRemaining) *heightRemaining = 1;
+ FloatingObject* r;
+ QPtrListIterator<FloatingObject> it(*m_floatingObjects);
+ for ( ; (r = it.current()); ++it )
+ {
+ //kdDebug( 6040 ) << "right: sy, ey, x, w " << r->startY << "," << r->endY << "," << r->left << "," << r->width << " " << endl;
+ if (r->startY <= y && r->endY > y &&
+ r->type == FloatingObject::FloatRight &&
+ r->left < right) {
+ right = r->left;
+ if ( heightRemaining ) *heightRemaining = r->endY - y;
+ if ( canClearLine ) *canClearLine = (r->node->style()->floating() != FRIGHT_ALIGN);
+ }
+ }
+ }
+
+ if (applyTextIndent && m_firstLine && style()->direction() == RTL ) {
+ int cw=0;
+ if (style()->textIndent().isPercent())
+ cw = containingBlock()->contentWidth();
+ right -= style()->textIndent().minWidth(cw);
+ }
+
+ //kdDebug( 6040 ) << "rightOffset(" << y << ") = " << right << endl;
+ return right;
+}
+
+unsigned short
+RenderBlock::lineWidth(int y, bool *canClearLine) const
+{
+ //kdDebug( 6040 ) << "lineWidth(" << y << ")=" << rightOffset(y) - leftOffset(y) << endl;
+ int result;
+ if (canClearLine) {
+ bool rightCanClearLine;
+ bool leftCanClearLine;
+ result = rightOffset(y, &rightCanClearLine) - leftOffset(y, &leftCanClearLine);
+ *canClearLine = rightCanClearLine && leftCanClearLine;
+ } else
+ result = rightOffset(y) - leftOffset(y);
+ return (result < 0) ? 0 : result;
+}
+
+int
+RenderBlock::nearestFloatBottom(int height) const
+{
+ if (!m_floatingObjects) return 0;
+ int bottom = 0;
+ FloatingObject* r;
+ QPtrListIterator<FloatingObject> it(*m_floatingObjects);
+ for ( ; (r = it.current()); ++it )
+ if (r->endY>height && (r->endY<bottom || bottom==0))
+ bottom=r->endY;
+ return kMax(bottom, height);
+}
+
+int RenderBlock::floatBottom() const
+{
+ if (!m_floatingObjects) return 0;
+ int bottom=0;
+ FloatingObject* r;
+ QPtrListIterator<FloatingObject> it(*m_floatingObjects);
+ for ( ; (r = it.current()); ++it )
+ if (r->endY>bottom)
+ bottom=r->endY;
+ return bottom;
+}
+
+int RenderBlock::lowestPosition(bool includeOverflowInterior, bool includeSelf) const
+{
+ int bottom = RenderFlow::lowestPosition(includeOverflowInterior, includeSelf);
+ if (!includeOverflowInterior && hasOverflowClip())
+ return bottom;
+ if (includeSelf && m_overflowHeight > bottom)
+ bottom = m_overflowHeight;
+
+ if (m_floatingObjects) {
+ FloatingObject* r;
+ QPtrListIterator<FloatingObject> it(*m_floatingObjects);
+ for ( ; (r = it.current()); ++it ) {
+ if (!r->noPaint) {
+ int lp = r->startY + r->node->marginTop() + r->node->lowestPosition(false);
+ bottom = kMax(bottom, lp);
+ }
+ }
+ }
+ bottom = kMax(bottom, lowestAbsolutePosition());
+
+ if (!includeSelf && lastLineBox()) {
+ int lp = lastLineBox()->yPos() + lastLineBox()->height();
+ bottom = kMax(bottom, lp);
+ }
+
+ return bottom;
+}
+
+int RenderBlock::lowestAbsolutePosition() const
+{
+ if (!m_positionedObjects)
+ return 0;
+
+ // Fixed positioned objects do not scroll and thus should not constitute
+ // part of the lowest position.
+ int bottom = 0;
+ RenderObject* r;
+ QPtrListIterator<RenderObject> it(*m_positionedObjects);
+ for ( ; (r = it.current()); ++it ) {
+ if (r->style()->position() == FIXED)
+ continue;
+ int lp = r->yPos() + r->lowestPosition(false);
+ bottom = kMax(bottom, lp);
+ }
+ return bottom;
+}
+
+int RenderBlock::rightmostPosition(bool includeOverflowInterior, bool includeSelf) const
+{
+ int right = RenderFlow::rightmostPosition(includeOverflowInterior, includeSelf);
+ if (!includeOverflowInterior && hasOverflowClip())
+ return right;
+ if (includeSelf && m_overflowWidth > right)
+ right = m_overflowWidth;
+
+ if (m_floatingObjects) {
+ FloatingObject* r;
+ QPtrListIterator<FloatingObject> it(*m_floatingObjects);
+ for ( ; (r = it.current()); ++it ) {
+ if (!r->noPaint) {
+ int rp = r->left + r->node->marginLeft() + r->node->rightmostPosition(false);
+ right = kMax(right, rp);
+ }
+ }
+ }
+ right = kMax(right, rightmostAbsolutePosition());
+
+ if (!includeSelf && firstLineBox()) {
+ for (InlineRunBox* currBox = firstLineBox(); currBox; currBox = currBox->nextLineBox()) {
+ int rp = currBox->xPos() + currBox->width();
+ right = kMax(right, rp);
+ }
+ }
+
+ return right;
+}
+
+int RenderBlock::rightmostAbsolutePosition() const
+{
+ if (!m_positionedObjects)
+ return 0;
+ int right = 0;
+ RenderObject* r;
+ QPtrListIterator<RenderObject> it(*m_positionedObjects);
+ for ( ; (r = it.current()); ++it ) {
+ if (r->style()->position() == FIXED)
+ continue;
+ int rp = r->xPos() + r->rightmostPosition(false);
+ right = kMax(right, rp);
+ }
+ return right;
+}
+
+int RenderBlock::leftmostPosition(bool includeOverflowInterior, bool includeSelf) const
+{
+ int left = RenderFlow::leftmostPosition(includeOverflowInterior, includeSelf);
+ if (!includeOverflowInterior && hasOverflowClip())
+ return left;
+
+ if (includeSelf && m_overflowLeft < left)
+ left = m_overflowLeft;
+
+ if (m_floatingObjects) {
+ FloatingObject* r;
+ QPtrListIterator<FloatingObject> it(*m_floatingObjects);
+ for ( ; (r = it.current()); ++it ) {
+ if (!r->noPaint) {
+ int lp = r->left + r->node->marginLeft() + r->node->leftmostPosition(false);
+ left = kMin(left, lp);
+ }
+ }
+ }
+ left = kMin(left, leftmostAbsolutePosition());
+
+ if (!includeSelf && firstLineBox()) {
+ for (InlineRunBox* currBox = firstLineBox(); currBox; currBox = currBox->nextLineBox())
+ left = kMin(left, (int)currBox->xPos());
+ }
+
+ return left;
+}
+
+int RenderBlock::leftmostAbsolutePosition() const
+{
+ if (!m_positionedObjects)
+ return 0;
+ int left = 0;
+ RenderObject* r;
+ QPtrListIterator<RenderObject> it(*m_positionedObjects);
+ for ( ; (r = it.current()); ++it ) {
+ if (r->style()->position() == FIXED)
+ continue;
+ int lp = r->xPos() + r->leftmostPosition(false);
+ left = kMin(left, lp);
+ }
+ return left;
+}
+
+int RenderBlock::highestPosition(bool includeOverflowInterior, bool includeSelf) const
+{
+ int top = RenderFlow::highestPosition(includeOverflowInterior, includeSelf);
+ if (!includeOverflowInterior && hasOverflowClip())
+ return top;
+
+ if (includeSelf && m_overflowTop < top)
+ top = m_overflowTop;
+
+ if (m_floatingObjects) {
+ FloatingObject* r;
+ QPtrListIterator<FloatingObject> it(*m_floatingObjects);
+ for ( ; (r = it.current()); ++it ) {
+ if (!r->noPaint) {
+ int hp = r->startY + r->node->marginTop() + r->node->highestPosition(false);
+ top = kMin(top, hp);
+ }
+ }
+ }
+ top = kMin(top, highestAbsolutePosition());
+
+ if (!includeSelf && firstLineBox()) {
+ top = kMin(top, (int)firstLineBox()->yPos());
+ }
+
+ return top;
+}
+
+int RenderBlock::highestAbsolutePosition() const
+{
+ if (!m_positionedObjects)
+ return 0;
+ int top = 0;
+ RenderObject* r;
+ QPtrListIterator<RenderObject> it(*m_positionedObjects);
+ for ( ; (r = it.current()); ++it ) {
+ if (r->style()->position() == FIXED)
+ continue;
+ int hp = r->yPos() + r->highestPosition(false);
+ hp = kMin(top, hp);
+ }
+ return top;
+}
+
+int
+RenderBlock::leftBottom()
+{
+ if (!m_floatingObjects) return 0;
+ int bottom=0;
+ FloatingObject* r;
+ QPtrListIterator<FloatingObject> it(*m_floatingObjects);
+ for ( ; (r = it.current()); ++it )
+ if (r->endY>bottom && r->type == FloatingObject::FloatLeft)
+ bottom=r->endY;
+
+ return bottom;
+}
+
+int
+RenderBlock::rightBottom()
+{
+ if (!m_floatingObjects) return 0;
+ int bottom=0;
+ FloatingObject* r;
+ QPtrListIterator<FloatingObject> it(*m_floatingObjects);
+ for ( ; (r = it.current()); ++it )
+ if (r->endY>bottom && r->type == FloatingObject::FloatRight)
+ bottom=r->endY;
+
+ return bottom;
+}
+
+void
+RenderBlock::clearFloats()
+{
+ if (m_floatingObjects)
+ m_floatingObjects->clear();
+
+ // we are done if the element defines a new block formatting context
+ if (flowAroundFloats() || isRoot() || isCanvas() || isFloatingOrPositioned() || isTableCell()) return;
+
+ RenderObject *prev = previousSibling();
+
+ // find the element to copy the floats from
+ // pass non-flows
+ // pass fAF's
+ bool parentHasFloats = false;
+ while (prev) {
+ if (!prev->isRenderBlock() || prev->isFloatingOrPositioned() || prev->flowAroundFloats()) {
+ if ( prev->isFloating() && parent()->isRenderBlock() ) {
+ parentHasFloats = true;
+ }
+ prev = prev->previousSibling();
+ } else
+ break;
+ }
+
+ int offset = m_y;
+ if (parentHasFloats)
+ {
+ addOverHangingFloats( static_cast<RenderBlock *>( parent() ),
+ parent()->borderLeft() + parent()->paddingLeft(), offset, false );
+ }
+
+ int xoffset = 0;
+ if (prev) {
+ if(prev->isTableCell()) return;
+ offset -= prev->yPos();
+ } else {
+ prev = parent();
+ if(!prev) return;
+ xoffset += prev->borderLeft() + prev->paddingLeft();
+ }
+ //kdDebug() << "RenderBlock::clearFloats found previous "<< (void *)this << " prev=" << (void *)prev<< endl;
+
+ // add overhanging special objects from the previous RenderBlock
+ if(!prev->isRenderBlock()) return;
+ RenderBlock * flow = static_cast<RenderBlock *>(prev);
+ if(!flow->m_floatingObjects) return;
+ if(flow->floatBottom() > offset)
+ addOverHangingFloats( flow, xoffset, offset, false );
+}
+
+void RenderBlock::addOverHangingFloats( RenderBlock *flow, int xoff, int offset, bool child )
+{
+#ifdef DEBUG_LAYOUT
+ kdDebug( 6040 ) << (void *)this << ": adding overhanging floats xoff=" << xoff << " offset=" << offset << " child=" << child << endl;
+#endif
+
+ // Prevent floats from being added to the canvas by the root element, e.g., <html>.
+ if ( !flow->m_floatingObjects || (child && flow->isRoot()) )
+ return;
+
+ // if I am clear of my floats, don't add them
+ // the CSS spec also mentions that child floats
+ // are not cleared.
+ if (!child && style()->clear() == CBOTH)
+ {
+ return;
+ }
+
+ QPtrListIterator<FloatingObject> it(*flow->m_floatingObjects);
+ FloatingObject *r;
+ for ( ; (r = it.current()); ++it ) {
+
+ if (!child && r->type == FloatingObject::FloatLeft && style()->clear() == CLEFT )
+ continue;
+ if (!child && r->type == FloatingObject::FloatRight && style()->clear() == CRIGHT )
+ continue;
+
+ if ( ( !child && r->endY > offset ) ||
+ ( child && flow->yPos() + r->endY > height() ) ) {
+ if (child && !r->crossedLayer) {
+ if (flow->enclosingLayer() == enclosingLayer()) {
+ // Set noPaint to true only if we didn't cross layers.
+ r->noPaint = true;
+ } else {
+ r->crossedLayer = true;
+ }
+ }
+
+ FloatingObject* f = 0;
+ // don't insert it twice!
+ if (m_floatingObjects) {
+ QPtrListIterator<FloatingObject> it(*m_floatingObjects);
+ while ( (f = it.current()) ) {
+ if (f->node == r->node) break;
+ ++it;
+ }
+ }
+ if ( !f ) {
+ FloatingObject *floatingObj = new FloatingObject(r->type);
+ floatingObj->startY = r->startY - offset;
+ floatingObj->endY = r->endY - offset;
+ floatingObj->left = r->left - xoff;
+ // Applying the child's margin makes no sense in the case where the child was passed in.
+ // since his own margin was added already through the subtraction of the |xoff| variable
+ // above. |xoff| will equal -flow->marginLeft() in this case, so it's already been taken
+ // into account. Only apply this code if |child| is false, since otherwise the left margin
+ // will get applied twice. -dwh
+ if (!child && flow != parent())
+ floatingObj->left += flow->marginLeft();
+ if ( !child ) {
+ floatingObj->left -= marginLeft();
+ floatingObj->noPaint = true;
+ }
+ else {
+ floatingObj->noPaint = (r->crossedLayer || !r->noPaint);
+ floatingObj->crossedLayer = r->crossedLayer;
+ }
+
+ floatingObj->width = r->width;
+ floatingObj->node = r->node;
+ if (!m_floatingObjects) {
+ m_floatingObjects = new QPtrList<FloatingObject>;
+ m_floatingObjects->setAutoDelete(true);
+ }
+ m_floatingObjects->append(floatingObj);
+#ifdef DEBUG_LAYOUT
+ kdDebug( 6040 ) << "addOverHangingFloats x/y= (" << floatingObj->left << "/" << floatingObj->startY << "-" << floatingObj->width << "/" << floatingObj->endY - floatingObj->startY << ")" << endl;
+#endif
+ }
+ }
+ }
+}
+
+bool RenderBlock::containsFloat(RenderObject* o) const
+{
+ if (m_floatingObjects) {
+ QPtrListIterator<FloatingObject> it(*m_floatingObjects);
+ while (it.current()) {
+ if (it.current()->node == o)
+ return true;
+ ++it;
+ }
+ }
+ return false;
+}
+
+void RenderBlock::markAllDescendantsWithFloatsForLayout(RenderObject* floatToRemove)
+{
+ dirtyFormattingContext(false);
+ setChildNeedsLayout(true);
+
+ if (floatToRemove)
+ removeFloatingObject(floatToRemove);
+
+ // Iterate over our children and mark them as needed.
+ if (!childrenInline()) {
+ for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
+ if (isBlockFlow() && !child->isFloatingOrPositioned() &&
+ ((floatToRemove ? child->containsFloat(floatToRemove) : child->hasFloats()) || child->usesLineWidth()))
+ child->markAllDescendantsWithFloatsForLayout(floatToRemove);
+ }
+ }
+}
+
+int RenderBlock::getClearDelta(RenderObject *child)
+{
+ if (!hasFloats())
+ return 0;
+
+ //kdDebug( 6040 ) << "getClearDelta on child " << child << " oldheight=" << m_height << endl;
+ bool clearSet = child->style()->clear() != CNONE;
+ int bottom = 0;
+ switch(child->style()->clear())
+ {
+ case CNONE:
+ break;
+ case CLEFT:
+ bottom = leftBottom();
+ break;
+ case CRIGHT:
+ bottom = rightBottom();
+ break;
+ case CBOTH:
+ bottom = floatBottom();
+ break;
+ }
+
+ // We also clear floats if we are too big to sit on the same line as a float, and happen to flow around floats.
+ // FIXME: Note that the remaining space checks aren't quite accurate, since you should be able to clear only some floats (the minimum # needed
+ // to fit) and not all (we should be using nearestFloatBottom and looping).
+
+ int result = clearSet ? kMax(0, bottom - child->yPos()) : 0;
+ if (!result && child->flowAroundFloats() && !style()->width().isVariable()) {
+ bool canClearLine;
+ int lw = lineWidth(child->yPos(), &canClearLine);
+ if (((child->style()->width().isPercent() && child->width() > lw) ||
+ (child->style()->width().isFixed() && child->minWidth() > lw)) &&
+ child->minWidth() <= contentWidth() && canClearLine)
+ result = kMax(0, floatBottom() - child->yPos());
+ }
+ return result;
+}
+
+bool RenderBlock::isPointInScrollbar(int _x, int _y, int _tx, int _ty)
+{
+ if (!scrollsOverflow() || !m_layer)
+ return false;
+
+ if (m_layer->verticalScrollbarWidth()) {
+ QRect vertRect(_tx + width() - borderRight() - m_layer->verticalScrollbarWidth(),
+ _ty + borderTop() - borderTopExtra(),
+ m_layer->verticalScrollbarWidth(),
+ height() + borderTopExtra() + borderBottomExtra()-borderTop()-borderBottom());
+ if (vertRect.contains(_x, _y)) {
+#ifdef APPLE_CHANGES
+ RenderLayer::gScrollBar = m_layer->verticalScrollbar();
+#endif
+ return true;
+ }
+ }
+
+ if (m_layer->horizontalScrollbarHeight()) {
+ QRect horizRect(_tx + borderLeft(),
+ _ty + height() + borderTop() + borderBottomExtra() - borderBottom() - m_layer->horizontalScrollbarHeight(),
+ width()-borderLeft()-borderRight(),
+ m_layer->horizontalScrollbarHeight());
+ if (horizRect.contains(_x, _y)) {
+#ifdef APPLE_CHANGES
+ RenderLayer::gScrollBar = m_layer->horizontalScrollbar();
+#endif
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool RenderBlock::nodeAtPoint(NodeInfo& info, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction, bool inBox)
+{
+ bool inScrollbar = isPointInScrollbar(_x, _y, _tx+xPos(), _ty+yPos());
+ if (inScrollbar && hitTestAction != HitTestChildrenOnly)
+ inBox = true;
+
+ if (hitTestAction != HitTestSelfOnly && m_floatingObjects && !inScrollbar) {
+ int stx = _tx + xPos();
+ int sty = _ty + yPos();
+ if (hasOverflowClip() && m_layer)
+ m_layer->subtractScrollOffset(stx, sty);
+ FloatingObject* o;
+ QPtrListIterator<FloatingObject> it(*m_floatingObjects);
+ for (it.toLast(); (o = it.current()); --it)
+ if (!o->noPaint && !o->node->layer())
+ inBox |= o->node->nodeAtPoint(info, _x, _y,
+ stx+o->left + o->node->marginLeft() - o->node->xPos(),
+ sty+o->startY + o->node->marginTop() - o->node->yPos(), HitTestAll ) ;
+ }
+
+ inBox |= RenderFlow::nodeAtPoint(info, _x, _y, _tx, _ty, hitTestAction, inBox);
+ return inBox;
+}
+
+void RenderBlock::calcMinMaxWidth()
+{
+ KHTMLAssert( !minMaxKnown() );
+
+#ifdef DEBUG_LAYOUT
+ kdDebug( 6040 ) << renderName() << "(RenderBlock)::calcMinMaxWidth() this=" << this << endl;
+#endif
+
+ m_minWidth = 0;
+ m_maxWidth = 0;
+
+ bool noWrap = !style()->autoWrap();
+ if (childrenInline())
+ calcInlineMinMaxWidth();
+ else
+ calcBlockMinMaxWidth();
+
+ if(m_maxWidth < m_minWidth) m_maxWidth = m_minWidth;
+
+ if (noWrap && childrenInline()) {
+ m_minWidth = m_maxWidth;
+
+ // A horizontal marquee with inline children has no minimum width.
+ if (style()->overflowX() == OMARQUEE && m_layer && m_layer->marquee() &&
+ m_layer->marquee()->isHorizontal() && !m_layer->marquee()->isUnfurlMarquee())
+ m_minWidth = 0;
+ }
+
+ if (isTableCell()) {
+ Length w = static_cast<RenderTableCell*>(this)->styleOrColWidth();
+ if (w.isFixed() && w.value() > 0)
+ m_maxWidth = kMax((int)m_minWidth, calcContentWidth(w.value()));
+ } else if (style()->width().isFixed() && style()->width().value() > 0)
+ m_minWidth = m_maxWidth = calcContentWidth(style()->width().value());
+
+ if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) {
+ m_maxWidth = kMax(m_maxWidth, (int)calcContentWidth(style()->minWidth().value()));
+ m_minWidth = kMax(m_minWidth, (short)calcContentWidth(style()->minWidth().value()));
+ }
+
+ if (style()->maxWidth().isFixed() && style()->maxWidth().value() != UNDEFINED) {
+ m_maxWidth = kMin(m_maxWidth, (int)calcContentWidth(style()->maxWidth().value()));
+ m_minWidth = kMin(m_minWidth, (short)calcContentWidth(style()->maxWidth().value()));
+ }
+
+ int toAdd = 0;
+ toAdd = borderLeft() + borderRight() + paddingLeft() + paddingRight();
+
+ m_minWidth += toAdd;
+ m_maxWidth += toAdd;
+
+ setMinMaxKnown();
+
+ //kdDebug( 6040 ) << "Text::calcMinMaxWidth(" << this << "): min = " << m_minWidth << " max = " << m_maxWidth << endl;
+ // ### compare with min/max width set in style sheet...
+}
+
+struct InlineMinMaxIterator
+{
+/* InlineMinMaxIterator is a class that will iterate over all render objects that contribute to
+ inline min/max width calculations. Note the following about the way it walks:
+ (1) Positioned content is skipped (since it does not contribute to min/max width of a block)
+ (2) We do not drill into the children of floats or replaced elements, since you can't break
+ in the middle of such an element.
+ (3) Inline flows (e.g., <a>, <span>, <i>) are walked twice, since each side can have
+ distinct borders/margin/padding that contribute to the min/max width.
+*/
+ RenderObject* parent;
+ RenderObject* current;
+ bool endOfInline;
+
+ InlineMinMaxIterator(RenderObject* p, RenderObject* o, bool end = false)
+ :parent(p), current(o), endOfInline(end) {}
+
+ RenderObject* next();
+};
+
+RenderObject* InlineMinMaxIterator::next()
+{
+ RenderObject* result = 0;
+ bool oldEndOfInline = endOfInline;
+ endOfInline = false;
+ while (current != 0 || (current == parent))
+ {
+ //kdDebug( 6040 ) << "current = " << current << endl;
+ if (!oldEndOfInline &&
+ (current == parent ||
+ (!current->isFloating() && !current->isReplaced() && !current->isPositioned())))
+ result = current->firstChild();
+ if (!result) {
+ // We hit the end of our inline. (It was empty, e.g., <span></span>.)
+ if (!oldEndOfInline && current->isInlineFlow()) {
+ result = current;
+ endOfInline = true;
+ break;
+ }
+
+ while (current && current != parent) {
+ result = current->nextSibling();
+ if (result) break;
+ current = current->parent();
+ if (current && current != parent && current->isInlineFlow()) {
+ result = current;
+ endOfInline = true;
+ break;
+ }
+ }
+ }
+
+ if (!result) break;
+
+ if (!result->isPositioned() && (result->isText() || result->isBR() ||
+ result->isFloating() || result->isReplaced() ||
+ result->isInlineFlow()))
+ break;
+
+ current = result;
+ result = 0;
+ }
+
+ // Update our position.
+ current = result;
+ return current;
+}
+
+// bidi.cpp defines the following functions too.
+// Maybe these should not be static, after all...
+
+#ifndef KDE_USE_FINAL
+
+static int getBPMWidth(int childValue, Length cssUnit)
+{
+ if (!cssUnit.isVariable())
+ return (cssUnit.isFixed() ? cssUnit.value() : childValue);
+ return 0;
+}
+
+static int getBorderPaddingMargin(RenderObject* child, bool endOfInline)
+{
+ RenderStyle* cstyle = child->style();
+ int result = 0;
+ bool leftSide = (cstyle->direction() == LTR) ? !endOfInline : endOfInline;
+ result += getBPMWidth((leftSide ? child->marginLeft() : child->marginRight()),
+ (leftSide ? cstyle->marginLeft() :
+ cstyle->marginRight()));
+ result += getBPMWidth((leftSide ? child->paddingLeft() : child->paddingRight()),
+ (leftSide ? cstyle->paddingLeft() :
+ cstyle->paddingRight()));
+ result += leftSide ? child->borderLeft() : child->borderRight();
+ return result;
+}
+#endif
+
+static void stripTrailingSpace(bool preserveWS,
+ int& inlineMax, int& inlineMin,
+ RenderObject* trailingSpaceChild)
+{
+ if (!preserveWS && trailingSpaceChild && trailingSpaceChild->isText()) {
+ // Collapse away the trailing space at the end of a block.
+ RenderText* t = static_cast<RenderText *>(trailingSpaceChild);
+ const Font *f = t->htmlFont( false );
+ QChar space[1]; space[0] = ' ';
+ int spaceWidth = f->width(space, 1, 0);
+ inlineMax -= spaceWidth;
+ if (inlineMin > inlineMax)
+ inlineMin = inlineMax;
+ }
+}
+
+void RenderBlock::calcInlineMinMaxWidth()
+{
+ int inlineMax=0;
+ int inlineMin=0;
+
+ int cw = containingBlock()->contentWidth();
+ int floatMaxWidth = 0;
+
+ // If we are at the start of a line, we want to ignore all white-space.
+ // Also strip spaces if we previously had text that ended in a trailing space.
+ bool stripFrontSpaces = true;
+
+ bool isTcQuirk = isTableCell() && style()->htmlHacks() && style()->width().isVariable();
+
+ RenderObject* trailingSpaceChild = 0;
+
+ bool autoWrap, oldAutoWrap;
+ autoWrap = oldAutoWrap = style()->autoWrap();
+
+ InlineMinMaxIterator childIterator(this, this);
+ bool addedTextIndent = false; // Only gets added in once.
+ RenderObject* prevFloat = 0;
+ while (RenderObject* child = childIterator.next())
+ {
+ autoWrap = child->style()->autoWrap();
+
+ if( !child->isBR() )
+ {
+ // Step One: determine whether or not we need to go ahead and
+ // terminate our current line. Each discrete chunk can become
+ // the new min-width, if it is the widest chunk seen so far, and
+ // it can also become the max-width.
+
+ // Children fall into three categories:
+ // (1) An inline flow object. These objects always have a min/max of 0,
+ // and are included in the iteration solely so that their margins can
+ // be added in.
+ //
+ // (2) An inline non-text non-flow object, e.g., an inline replaced element.
+ // These objects can always be on a line by themselves, so in this situation
+ // we need to go ahead and break the current line, and then add in our own
+ // margins and min/max width on its own line, and then terminate the line.
+ //
+ // (3) A text object. Text runs can have breakable characters at the start,
+ // the middle or the end. They may also lose whitespace off the front if
+ // we're already ignoring whitespace. In order to compute accurate min-width
+ // information, we need three pieces of information.
+ // (a) the min-width of the first non-breakable run. Should be 0 if the text string
+ // starts with whitespace.
+ // (b) the min-width of the last non-breakable run. Should be 0 if the text string
+ // ends with whitespace.
+ // (c) the min/max width of the string (trimmed for whitespace).
+ //
+ // If the text string starts with whitespace, then we need to go ahead and
+ // terminate our current line (unless we're already in a whitespace stripping
+ // mode.
+ //
+ // If the text string has a breakable character in the middle, but didn't start
+ // with whitespace, then we add the width of the first non-breakable run and
+ // then end the current line. We then need to use the intermediate min/max width
+ // values (if any of them are larger than our current min/max). We then look at
+ // the width of the last non-breakable run and use that to start a new line
+ // (unless we end in whitespace).
+ RenderStyle* cstyle = child->style();
+ short childMin = 0;
+ short childMax = 0;
+
+ if (!child->isText()) {
+ // Case (1) and (2). Inline replaced and inline flow elements.
+ if (child->isInlineFlow()) {
+ // Add in padding/border/margin from the appropriate side of
+ // the element.
+ int bpm = getBorderPaddingMargin(child, childIterator.endOfInline);
+ childMin += bpm;
+ childMax += bpm;
+
+ inlineMin += childMin;
+ inlineMax += childMax;
+ }
+ else {
+ // Inline replaced elements add in their margins to their min/max values.
+ int margins = 0;
+ LengthType type = cstyle->marginLeft().type();
+ if ( type != Variable )
+ margins += (type == Fixed ? cstyle->marginLeft().value() : child->marginLeft());
+ type = cstyle->marginRight().type();
+ if ( type != Variable )
+ margins += (type == Fixed ? cstyle->marginRight().value() : child->marginRight());
+ childMin += margins;
+ childMax += margins;
+ }
+ }
+
+ if (!child->isRenderInline() && !child->isText()) {
+
+ bool qBreak = isTcQuirk && !child->isFloatingOrPositioned();
+ // Case (2). Inline replaced elements and floats.
+ // Go ahead and terminate the current line as far as
+ // minwidth is concerned.
+ childMin += child->minWidth();
+ childMax += child->maxWidth();
+
+ if (!qBreak && (autoWrap || oldAutoWrap)) {
+ if(m_minWidth < inlineMin) m_minWidth = inlineMin;
+ inlineMin = 0;
+ }
+
+ // Check our "clear" setting. If we're supposed to clear the previous float, then
+ // go ahead and terminate maxwidth as well.
+ if (child->isFloating()) {
+ if (prevFloat &&
+ ((inlineMax + childMax > floatMaxWidth) ||
+ ((prevFloat->style()->floating() & FLEFT) && (child->style()->clear() & CLEFT)) ||
+ ((prevFloat->style()->floating() & FRIGHT) && (child->style()->clear() & CRIGHT)))) {
+ m_maxWidth = kMax(inlineMax, (int)m_maxWidth);
+ inlineMax = 0;
+ }
+ prevFloat = child;
+ if (!floatMaxWidth)
+ floatMaxWidth = availableWidth();
+ }
+
+ // Add in text-indent. This is added in only once.
+ int ti = 0;
+ if ( !addedTextIndent ) {
+ addedTextIndent = true;
+ ti = style()->textIndent().minWidth( cw );
+ childMin+=ti;
+ childMax+=ti;
+ }
+
+ // Add our width to the max.
+ inlineMax += childMax;
+
+ if (!autoWrap||qBreak)
+ inlineMin += childMin;
+ else {
+ // Now check our line.
+ inlineMin = childMin;
+ if(m_minWidth < inlineMin) m_minWidth = inlineMin;
+
+ // Now start a new line.
+ inlineMin = 0;
+ }
+
+ // We are no longer stripping whitespace at the start of
+ // a line.
+ if (!child->isFloating()) {
+ stripFrontSpaces = false;
+ trailingSpaceChild = 0;
+ }
+ }
+ else if (child->isText())
+ {
+ // Case (3). Text.
+ RenderText* t = static_cast<RenderText *>(child);
+
+ // Determine if we have a breakable character. Pass in
+ // whether or not we should ignore any spaces at the front
+ // of the string. If those are going to be stripped out,
+ // then they shouldn't be considered in the breakable char
+ // check.
+ bool hasBreakableChar, hasBreak;
+ short beginMin, endMin;
+ bool beginWS, endWS;
+ short beginMax, endMax;
+ t->trimmedMinMaxWidth(beginMin, beginWS, endMin, endWS, hasBreakableChar,
+ hasBreak, beginMax, endMax,
+ childMin, childMax, stripFrontSpaces);
+
+ // This text object is insignificant and will not be rendered. Just
+ // continue.
+ if (!hasBreak && childMax == 0) continue;
+
+ if (stripFrontSpaces)
+ trailingSpaceChild = child;
+ else
+ trailingSpaceChild = 0;
+
+ // Add in text-indent. This is added in only once.
+ int ti = 0;
+ if (!addedTextIndent) {
+ addedTextIndent = true;
+ ti = style()->textIndent().minWidth(cw);
+ childMin+=ti; beginMin += ti;
+ childMax+=ti; beginMax += ti;
+ }
+
+ // If we have no breakable characters at all,
+ // then this is the easy case. We add ourselves to the current
+ // min and max and continue.
+ if (!hasBreakableChar) {
+ inlineMin += childMin;
+ }
+ else {
+ // We have a breakable character. Now we need to know if
+ // we start and end with whitespace.
+ if (beginWS) {
+ // Go ahead and end the current line.
+ if(m_minWidth < inlineMin) m_minWidth = inlineMin;
+ }
+ else {
+ inlineMin += beginMin;
+ if(m_minWidth < inlineMin) m_minWidth = inlineMin;
+ childMin -= ti;
+ }
+
+ inlineMin = childMin;
+
+ if (endWS) {
+ // We end in whitespace, which means we can go ahead
+ // and end our current line.
+ if(m_minWidth < inlineMin) m_minWidth = inlineMin;
+ inlineMin = 0;
+ }
+ else {
+ if(m_minWidth < inlineMin) m_minWidth = inlineMin;
+ inlineMin = endMin;
+ }
+ }
+
+ if (hasBreak) {
+ inlineMax += beginMax;
+ if (m_maxWidth < inlineMax) m_maxWidth = inlineMax;
+ if (m_maxWidth < childMax) m_maxWidth = childMax;
+ inlineMax = endMax;
+ }
+ else
+ inlineMax += childMax;
+ }
+ }
+ else
+ {
+ if(m_minWidth < inlineMin) m_minWidth = inlineMin;
+ if(m_maxWidth < inlineMax) m_maxWidth = inlineMax;
+ inlineMin = inlineMax = 0;
+ stripFrontSpaces = true;
+ trailingSpaceChild = 0;
+ }
+
+ oldAutoWrap = autoWrap;
+ }
+
+ stripTrailingSpace(style()->preserveWS(), inlineMax, inlineMin, trailingSpaceChild);
+
+ if(m_minWidth < inlineMin) m_minWidth = inlineMin;
+ if(m_maxWidth < inlineMax) m_maxWidth = inlineMax;
+ // kdDebug( 6040 ) << "m_minWidth=" << m_minWidth
+ // << " m_maxWidth=" << m_maxWidth << endl;
+}
+
+// Use a very large value (in effect infinite).
+#define BLOCK_MAX_WIDTH 15000
+
+void RenderBlock::calcBlockMinMaxWidth()
+{
+ bool nowrap = !style()->autoWrap();
+
+ RenderObject *child = firstChild();
+ RenderObject* prevFloat = 0;
+ int floatWidths = 0;
+ int floatMaxWidth = 0;
+
+ while(child != 0)
+ {
+ // positioned children don't affect the minmaxwidth
+ if (child->isPositioned()) {
+ child = child->nextSibling();
+ continue;
+ }
+
+ if (prevFloat && (!child->isFloating() ||
+ ((prevFloat->style()->floating() & FLEFT) && (child->style()->clear() & CLEFT)) ||
+ ((prevFloat->style()->floating() & FRIGHT) && (child->style()->clear() & CRIGHT)))) {
+ m_maxWidth = kMax(floatWidths, m_maxWidth);
+ floatWidths = 0;
+ }
+
+ Length ml = child->style()->marginLeft();
+ Length mr = child->style()->marginRight();
+
+ // Call calcWidth on the child to ensure that our margins are
+ // up to date. This method can be called before the child has actually
+ // calculated its margins (which are computed inside calcWidth).
+ if (ml.isPercent() || mr.isPercent())
+ calcWidth();
+
+ // A margin basically has three types: fixed, percentage, and auto (variable).
+ // Auto margins simply become 0 when computing min/max width.
+ // Fixed margins can be added in as is.
+ // Percentage margins are computed as a percentage of the width we calculated in
+ // the calcWidth call above. In this case we use the actual cached margin values on
+ // the RenderObject itself.
+ int margin = 0;
+ if (ml.isFixed())
+ margin += ml.value();
+ else if (ml.isPercent())
+ margin += child->marginLeft();
+
+ if (mr.isFixed())
+ margin += mr.value();
+ else if (mr.isPercent())
+ margin += child->marginRight();
+
+ if (margin < 0) margin = 0;
+
+ int w = child->minWidth() + margin;
+ if(m_minWidth < w) m_minWidth = w;
+ // IE ignores tables for calculation of nowrap. Makes some sense.
+ if ( nowrap && !child->isTable() && m_maxWidth < w )
+ m_maxWidth = w;
+
+ w = child->maxWidth() + margin;
+
+ if(m_maxWidth < w) m_maxWidth = w;
+
+ if (child->isFloating()) {
+ if (prevFloat && (floatWidths + w > floatMaxWidth)) {
+ m_maxWidth = kMax(floatWidths, m_maxWidth);
+ floatWidths = w;
+ } else
+ floatWidths += w;
+ } else if (m_maxWidth < w)
+ m_maxWidth = w;
+
+ // A very specific WinIE quirk.
+ // Example:
+ /*
+ <div style="position:absolute; width:100px; top:50px;">
+ <div style="position:absolute;left:0px;top:50px;height:50px;background-color:green">
+ <table style="width:100%"><tr><td></table>
+ </div>
+ </div>
+ */
+ // In the above example, the inner absolute positioned block should have a computed width
+ // of 100px because of the table.
+ // We can achieve this effect by making the maxwidth of blocks that contain tables
+ // with percentage widths be infinite (as long as they are not inside a table cell).
+ if (style()->htmlHacks() && child->style()->width().isPercent() &&
+ !isTableCell() && child->isTable() && m_maxWidth < BLOCK_MAX_WIDTH) {
+ RenderBlock* cb = containingBlock();
+ while (!cb->isCanvas() && !cb->isTableCell())
+ cb = cb->containingBlock();
+ if (!cb->isTableCell())
+ m_maxWidth = BLOCK_MAX_WIDTH;
+ }
+ if (child->isFloating()) {
+ prevFloat = child;
+ if (!floatMaxWidth)
+ floatMaxWidth = availableWidth();
+ }
+ child = child->nextSibling();
+ }
+ m_maxWidth = kMax(floatWidths, m_maxWidth);
+}
+
+void RenderBlock::close()
+{
+ if (lastChild() && lastChild()->isAnonymousBlock())
+ lastChild()->close();
+ updateFirstLetter();
+ RenderFlow::close();
+}
+
+int RenderBlock::getBaselineOfFirstLineBox()
+{
+ if (m_firstLineBox)
+ return m_firstLineBox->yPos() + m_firstLineBox->baseline();
+
+ if (isInline())
+ return -1; // We're inline and had no line box, so we have no baseline we can return.
+
+ for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
+ int result = curr->getBaselineOfFirstLineBox();
+ if (result != -1)
+ return curr->yPos() + result; // Translate to our coordinate space.
+ }
+
+ return -1;
+}
+
+InlineFlowBox* RenderBlock::getFirstLineBox()
+{
+ if (m_firstLineBox)
+ return m_firstLineBox;
+
+ if (isInline())
+ return 0; // We're inline and had no line box, so we have no baseline we can return.
+
+ for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
+ InlineFlowBox* result = curr->getFirstLineBox();
+ if (result)
+ return result;
+ }
+
+ return 0;
+}
+
+bool RenderBlock::inRootBlockContext() const
+{
+ if (isTableCell() || isFloatingOrPositioned() || hasOverflowClip())
+ return false;
+
+ if (isRoot() || isCanvas())
+ return true;
+
+ return containingBlock()->inRootBlockContext();
+}
+
+const char *RenderBlock::renderName() const
+{
+ if (isFloating())
+ return "RenderBlock (floating)";
+ if (isPositioned())
+ return "RenderBlock (positioned)";
+ if (isAnonymousBlock() && m_avoidPageBreak)
+ return "RenderBlock (avoidPageBreak)";
+ if (isAnonymousBlock())
+ return "RenderBlock (anonymous)";
+ else if (isAnonymous())
+ return "RenderBlock (generated)";
+ if (isRelPositioned())
+ return "RenderBlock (relative positioned)";
+ if (style() && style()->display() == COMPACT)
+ return "RenderBlock (compact)";
+ if (style() && style()->display() == RUN_IN)
+ return "RenderBlock (run-in)";
+ return "RenderBlock";
+}
+
+#ifdef ENABLE_DUMP
+void RenderBlock::printTree(int indent) const
+{
+ RenderFlow::printTree(indent);
+
+ if (m_floatingObjects)
+ {
+ QPtrListIterator<FloatingObject> it(*m_floatingObjects);
+ FloatingObject *r;
+ for ( ; (r = it.current()); ++it )
+ {
+ QString s;
+ s.fill(' ', indent);
+ kdDebug() << s << renderName() << ": " <<
+ (r->type == FloatingObject::FloatLeft ? "FloatLeft" : "FloatRight" ) <<
+ "[" << r->node->renderName() << ": " << (void*)r->node << "] (" << r->startY << " - " << r->endY << ")" << "width: " << r->width <<
+ endl;
+ }
+ }
+}
+
+void RenderBlock::dump(QTextStream &stream, const QString &ind) const
+{
+ RenderFlow::dump(stream,ind);
+
+ if (m_childrenInline) { stream << " childrenInline"; }
+ // FIXME: currently only print pre to not mess up regression
+ if (style()->preserveWS()) { stream << " pre"; }
+ if (m_firstLine) { stream << " firstLine"; }
+
+ if (m_floatingObjects && !m_floatingObjects->isEmpty())
+ {
+ stream << " special(";
+ QPtrListIterator<FloatingObject> it(*m_floatingObjects);
+ FloatingObject *r;
+ bool first = true;
+ for ( ; (r = it.current()); ++it )
+ {
+ if (!first)
+ stream << ",";
+ stream << r->node->renderName();
+ first = false;
+ }
+ stream << ")";
+ }
+
+ // ### EClear m_clearStatus
+}
+#endif
+
+#undef DEBUG
+#undef DEBUG_LAYOUT
+#undef BOX_DEBUG
+
+} // namespace khtml
+
diff --git a/khtml/rendering/render_block.h b/khtml/rendering/render_block.h
new file mode 100644
index 000000000..112d331ca
--- /dev/null
+++ b/khtml/rendering/render_block.h
@@ -0,0 +1,378 @@
+/*
+ * This file is part of the render object implementation for KHTML.
+ *
+ * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
+ * (C) 1999-2003 Antti Koivisto (koivisto@kde.org)
+ * (C) 2002-2003 Dirk Mueller (mueller@kde.org)
+ * (C) 2003 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.
+ *
+ */
+
+#ifndef RENDER_BLOCK_H
+#define RENDER_BLOCK_H
+
+#include <qptrlist.h>
+
+#include "render_flow.h"
+
+namespace khtml {
+
+class RenderBlock : public RenderFlow
+{
+public:
+ RenderBlock(DOM::NodeImpl* node);
+ virtual ~RenderBlock();
+
+ virtual const char *renderName() const;
+
+ virtual bool isRenderBlock() const { return true; }
+ virtual bool isBlockFlow() const { return !isInline() && !isTable(); }
+ virtual bool isInlineFlow() const { return isInline() && !isReplaced(); }
+ virtual bool isInlineBlockOrInlineTable() const { return isInline() && isReplaced(); }
+
+ virtual bool childrenInline() const { return m_childrenInline; }
+ virtual void setChildrenInline(bool b) { m_childrenInline = b; }
+ void makeChildrenNonInline(RenderObject* insertionPoint = 0);
+
+ void makePageBreakAvoidBlocks();
+
+ // The height (and width) of a block when you include overflow spillage out of the bottom
+ // of the block (e.g., a <div style="height:25px"> that has a 100px tall image inside
+ // it would have an overflow height of borderTop() + paddingTop() + 100px.
+ virtual int overflowHeight() const { return m_overflowHeight; }
+ virtual int overflowWidth() const { return m_overflowWidth; }
+ virtual int overflowLeft() const { return m_overflowLeft; }
+ virtual int overflowTop() const { return m_overflowTop; }
+ virtual void setOverflowHeight(int h) { m_overflowHeight = h; }
+ virtual void setOverflowWidth(int w) { m_overflowWidth = w; }
+ virtual void setOverflowLeft(int l) { m_overflowLeft = l; }
+ virtual void setOverflowTop(int t) { m_overflowTop = t; }
+
+ virtual bool isSelfCollapsingBlock() const;
+ virtual bool isTopMarginQuirk() const { return m_topMarginQuirk; }
+ virtual bool isBottomMarginQuirk() const { return m_bottomMarginQuirk; }
+
+ virtual short maxTopMargin(bool positive) const {
+ if (positive)
+ return m_maxTopPosMargin;
+ else
+ return m_maxTopNegMargin;
+ }
+ virtual short maxBottomMargin(bool positive) const {
+ if (positive)
+ return m_maxBottomPosMargin;
+ else
+ return m_maxBottomNegMargin;
+ }
+
+ void initMaxMarginValues() {
+ if (m_marginTop >= 0)
+ m_maxTopPosMargin = m_marginTop;
+ else
+ m_maxTopNegMargin = -m_marginTop;
+ if (m_marginBottom >= 0)
+ m_maxBottomPosMargin = m_marginBottom;
+ else
+ m_maxBottomNegMargin = -m_marginBottom;
+ }
+
+ virtual void addChildToFlow(RenderObject* newChild, RenderObject* beforeChild);
+ virtual void removeChild(RenderObject *oldChild);
+
+ virtual void setStyle(RenderStyle* _style);
+ virtual void attach();
+ void updateFirstLetter();
+
+ virtual void layout();
+ void layoutBlock( bool relayoutChildren );
+ void layoutBlockChildren( bool relayoutChildren );
+ void layoutInlineChildren( bool relayoutChildren, int breakBeforeLine = 0);
+
+ void layoutPositionedObjects( bool relayoutChildren );
+ void insertPositionedObject(RenderObject *o);
+ void removePositionedObject(RenderObject *o);
+
+ // Called to lay out the legend for a fieldset.
+ virtual RenderObject* layoutLegend(bool /*relayoutChildren*/) { return 0; };
+
+ // the implementation of the following functions is in bidi.cpp
+ void bidiReorderLine(const BidiIterator &start, const BidiIterator &end, BidiState &bidi );
+ BidiIterator findNextLineBreak(BidiIterator &start, BidiState &info );
+ InlineFlowBox* constructLine(const BidiIterator& start, const BidiIterator& end);
+ InlineFlowBox* createLineBoxes(RenderObject* obj);
+ void computeHorizontalPositionsForLine(InlineFlowBox* lineBox, BidiState &bidi);
+ void computeVerticalPositionsForLine(InlineFlowBox* lineBox);
+ bool clearLineOfPageBreaks(InlineFlowBox* lineBox);
+ void checkLinesForOverflow();
+ void deleteEllipsisLineBoxes();
+ void checkLinesForTextOverflow();
+ // end bidi.cpp functions
+
+ virtual void paint(PaintInfo& i, int tx, int ty);
+ void paintObject(PaintInfo& i, int tx, int ty, bool paintOutline = true);
+ void paintFloats(PaintInfo& i, int _tx, int _ty, bool paintSelection = false);
+
+ void insertFloatingObject(RenderObject *o);
+ void removeFloatingObject(RenderObject *o);
+
+ // called from lineWidth, to position the floats added in the last line.
+ void positionNewFloats();
+ void clearFloats();
+ int getClearDelta(RenderObject *child);
+ virtual void markAllDescendantsWithFloatsForLayout(RenderObject* floatToRemove = 0);
+
+ // FIXME: containsFloats() should not return true if the floating objects list
+ // is empty. However, layoutInlineChildren() relies on the current behavior.
+ // http://bugzilla.opendarwin.org/show_bug.cgi?id=7395#c3
+ virtual bool hasFloats() const { return m_floatingObjects!=0; }
+ virtual bool containsFloat(RenderObject* o) const;
+
+ virtual bool hasOverhangingFloats() const { return floatBottom() > m_height; }
+ void addOverHangingFloats( RenderBlock *block, int xoffset, int yoffset, bool child );
+
+ int nearestFloatBottom(int height) const;
+ int floatBottom() const;
+ inline int leftBottom();
+ inline int rightBottom();
+
+ virtual unsigned short lineWidth(int y, bool *canClearLine = 0) const;
+ virtual int lowestPosition(bool includeOverflowInterior=true, bool includeSelf=true) const;
+ virtual int rightmostPosition(bool includeOverflowInterior=true, bool includeSelf=true) const;
+ virtual int leftmostPosition(bool includeOverflowInterior=true, bool includeSelf=true) const;
+ virtual int highestPosition(bool includeOverflowInterior, bool includeSelf) const;
+ int lowestAbsolutePosition() const;
+ int leftmostAbsolutePosition() const;
+ int rightmostAbsolutePosition() const;
+ int highestAbsolutePosition() const;
+
+ int rightOffset() const;
+ int rightRelOffset(int y, int fixedOffset, bool applyTextIndent=true, int *heightRemaining = 0, bool *canClearLine = 0) const;
+ int rightOffset(int y, bool *canClearLine = 0) const { return rightRelOffset(y, rightOffset(), true, 0, canClearLine); }
+
+ int leftOffset() const;
+ int leftRelOffset(int y, int fixedOffset, bool applyTextIndent=true, int *heightRemaining = 0, bool *canClearLine = 0) const;
+ int leftOffset(int y, bool *canClearLine = 0) const { return leftRelOffset(y, leftOffset(), true, 0, canClearLine); }
+
+ virtual bool nodeAtPoint(NodeInfo& info, int x, int y, int _tx, int _ty, HitTestAction hitTestAction = HitTestAll, bool inside=false);
+
+ bool isPointInScrollbar(int x, int y, int tx, int ty);
+
+ virtual void calcMinMaxWidth();
+ void calcInlineMinMaxWidth();
+ void calcBlockMinMaxWidth();
+
+ virtual void close();
+
+ virtual int getBaselineOfFirstLineBox();
+ virtual InlineFlowBox* getFirstLineBox();
+
+ RootInlineBox* firstRootBox() { return static_cast<RootInlineBox*>(m_firstLineBox); }
+ RootInlineBox* lastRootBox() { return static_cast<RootInlineBox*>(m_lastLineBox); }
+
+ bool inRootBlockContext() const;
+
+#ifdef ENABLE_DUMP
+ virtual void printTree(int indent=0) const;
+ virtual void dump(QTextStream &stream, const QString &ind) const;
+#endif
+
+protected:
+ void newLine();
+
+protected:
+ struct FloatingObject {
+ enum Type {
+ FloatLeft,
+ FloatRight
+ };
+
+ FloatingObject(Type _type) {
+ node = 0;
+ startY = 0;
+ endY = 0;
+ type = _type;
+ left = 0;
+ width = 0;
+ noPaint = false;
+ crossedLayer = false;
+
+ }
+ RenderObject* node;
+ int startY;
+ int endY;
+ short left;
+ short width;
+ Type type : 1; // left or right aligned
+ bool noPaint : 1;
+ bool crossedLayer : 1; // lock noPaint flag
+ };
+
+ // The following helper functions and structs are used by layoutBlockChildren.
+ class CompactInfo {
+ // A compact child that needs to be collapsed into the margin of the following block.
+ RenderObject* m_compact;
+
+ // The block with the open margin that the compact child is going to place itself within.
+ RenderObject* m_block;
+ bool m_treatAsBlock : 1;
+
+ public:
+ RenderObject* compact() const { return m_compact; }
+ RenderObject* block() const { return m_block; }
+ void setTreatAsBlock(bool b) { m_treatAsBlock = b; }
+ bool treatAsBlock() const { return m_treatAsBlock; }
+ bool matches(RenderObject* child) const { return m_compact && m_block == child; }
+
+ void clear() { set(0, 0); }
+ void set(RenderObject* c, RenderObject* b) { m_compact = c; m_block = b; }
+
+ CompactInfo() { clear(); }
+ };
+
+ class MarginInfo {
+ // Collapsing flags for whether we can collapse our margins with our children's margins.
+ bool m_canCollapseWithChildren : 1;
+ bool m_canCollapseTopWithChildren : 1;
+ bool m_canCollapseBottomWithChildren : 1;
+
+ // Whether or not we are a quirky container, i.e., do we collapse away top and bottom
+ // margins in our container. Table cells and the body are the common examples. We
+ // also have a custom style property for Safari RSS to deal with TypePad blog articles.
+ bool m_quirkContainer : 1;
+
+ // This flag tracks whether we are still looking at child margins that can all collapse together at the beginning of a block.
+ // They may or may not collapse with the top margin of the block (|m_canCollapseTopWithChildren| tells us that), but they will
+ // always be collapsing with one another. This variable can remain set to true through multiple iterations
+ // as long as we keep encountering self-collapsing blocks.
+ bool m_atTopOfBlock : 1;
+
+ // This flag is set when we know we're examining bottom margins and we know we're at the bottom of the block.
+ bool m_atBottomOfBlock : 1;
+
+ // If our last normal flow child was a self-collapsing block that cleared a float,
+ // we track it in this variable.
+ bool m_selfCollapsingBlockClearedFloat : 1;
+
+ // These variables are used to detect quirky margins that we need to collapse away (in table cells
+ // and in the body element).
+ bool m_topQuirk : 1;
+ bool m_bottomQuirk : 1;
+ bool m_determinedTopQuirk : 1;
+
+ // These flags track the previous maximal positive and negative margins.
+ int m_posMargin;
+ int m_negMargin;
+
+ public:
+ MarginInfo(RenderBlock* b, int top, int bottom);
+
+ void setAtTopOfBlock(bool b) { m_atTopOfBlock = b; }
+ void setAtBottomOfBlock(bool b) { m_atBottomOfBlock = b; }
+ void clearMargin() { m_posMargin = m_negMargin = 0; }
+ void setSelfCollapsingBlockClearedFloat(bool b) { m_selfCollapsingBlockClearedFloat = b; }
+ void setTopQuirk(bool b) { m_topQuirk = b; }
+ void setBottomQuirk(bool b) { m_bottomQuirk = b; }
+ void setDeterminedTopQuirk(bool b) { m_determinedTopQuirk = b; }
+ void setPosMargin(int p) { m_posMargin = p; }
+ void setNegMargin(int n) { m_negMargin = n; }
+ void setPosMarginIfLarger(int p) { if (p > m_posMargin) m_posMargin = p; }
+ void setNegMarginIfLarger(int n) { if (n > m_negMargin) m_negMargin = n; }
+
+ void setMargin(int p, int n) { m_posMargin = p; m_negMargin = n; }
+
+ bool atTopOfBlock() const { return m_atTopOfBlock; }
+ bool canCollapseWithTop() const { return m_atTopOfBlock && m_canCollapseTopWithChildren; }
+ bool canCollapseWithBottom() const { return m_atBottomOfBlock && m_canCollapseBottomWithChildren; }
+ bool canCollapseTopWithChildren() const { return m_canCollapseTopWithChildren; }
+ bool canCollapseBottomWithChildren() const { return m_canCollapseBottomWithChildren; }
+ bool selfCollapsingBlockClearedFloat() const { return m_selfCollapsingBlockClearedFloat; }
+ bool quirkContainer() const { return m_quirkContainer; }
+ bool determinedTopQuirk() const { return m_determinedTopQuirk; }
+ bool topQuirk() const { return m_topQuirk; }
+ bool bottomQuirk() const { return m_bottomQuirk; }
+ int posMargin() const { return m_posMargin; }
+ int negMargin() const { return m_negMargin; }
+ int margin() const { return m_posMargin - m_negMargin; }
+ };
+
+ class PageBreakInfo {
+ int m_pageBottom; // Next calculated page-break
+ bool m_forcePageBreak : 1; // Must break before next block
+ // ### to do better "page-break-after/before: avoid" this struct
+ // should keep a pagebreakAvoid block and gather children in it
+ public:
+ PageBreakInfo(int pageBottom) : m_pageBottom(pageBottom), m_forcePageBreak(false) {};
+ bool forcePageBreak() { return m_forcePageBreak; }
+ void setForcePageBreak(bool b) { m_forcePageBreak = b; }
+ int pageBottom() { return m_pageBottom; };
+ void setPageBottom(int bottom) { m_pageBottom = bottom; }
+ };
+
+ virtual bool canClear(RenderObject *child, PageBreakLevel level);
+ void clearPageBreak(RenderObject* child, int pageBottom);
+
+ void adjustPositionedBlock(RenderObject* child, const MarginInfo& marginInfo);
+ void adjustFloatingBlock(const MarginInfo& marginInfo);
+ RenderObject* handleSpecialChild(RenderObject* child, const MarginInfo& marginInfo, CompactInfo& compactInfo, bool& handled);
+ RenderObject* handleFloatingChild(RenderObject* child, const MarginInfo& marginInfo, bool& handled);
+ RenderObject* handlePositionedChild(RenderObject* child, const MarginInfo& marginInfo, bool& handled);
+ RenderObject* handleCompactChild(RenderObject* child, CompactInfo& compactInfo, const MarginInfo& marginInfo, bool& handled);
+ RenderObject* handleRunInChild(RenderObject* child, bool& handled);
+ void collapseMargins(RenderObject* child, MarginInfo& marginInfo, int yPosEstimate);
+ void clearFloatsIfNeeded(RenderObject* child, MarginInfo& marginInfo, int oldTopPosMargin, int oldTopNegMargin);
+ void adjustSizeForCompactIfNeeded(RenderObject* child, CompactInfo& compactInfo);
+ void insertCompactIfNeeded(RenderObject* child, CompactInfo& compactInfo);
+ int estimateVerticalPosition(RenderObject* child, const MarginInfo& info);
+ void determineHorizontalPosition(RenderObject* child);
+ void handleBottomOfBlock(int top, int bottom, MarginInfo& marginInfo);
+ void setCollapsedBottomMargin(const MarginInfo& marginInfo);
+ void clearChildOfPageBreaks(RenderObject* child, PageBreakInfo &pageBreakInfo, MarginInfo &marginInfo);
+ // End helper functions and structs used by layoutBlockChildren.
+
+protected:
+ // How much content overflows out of our block vertically or horizontally (all we support
+ // for now is spillage out of the bottom and the right, which are the common cases).
+ int m_overflowHeight;
+ int m_overflowWidth;
+
+ // Left and top overflow.
+ int m_overflowTop;
+ int m_overflowLeft;
+
+private:
+ QPtrList<FloatingObject>* m_floatingObjects;
+ QPtrList<RenderObject>* m_positionedObjects;
+
+ bool m_childrenInline : 1;
+ bool m_firstLine : 1; // used in inline layouting
+ EClear m_clearStatus : 2; // used during layuting of paragraphs
+ bool m_avoidPageBreak : 1; // anonymous avoid page-break block
+ bool m_topMarginQuirk : 1;
+ bool m_bottomMarginQuirk : 1;
+
+ short m_maxTopPosMargin;
+ short m_maxTopNegMargin;
+ short m_maxBottomPosMargin;
+ short m_maxBottomNegMargin;
+
+};
+
+} // namespace
+
+#endif // RENDER_BLOCK_H
+
diff --git a/khtml/rendering/render_body.cpp b/khtml/rendering/render_body.cpp
new file mode 100644
index 000000000..e9da3c71c
--- /dev/null
+++ b/khtml/rendering/render_body.cpp
@@ -0,0 +1,121 @@
+/**
+ * This file is part of the html renderer for KDE.
+ *
+ * Copyright (C) 2000-2003 Lars Knoll (knoll@kde.org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+#include "rendering/render_body.h"
+#include "rendering/render_canvas.h"
+#include "html/html_baseimpl.h"
+#include "xml/dom_docimpl.h"
+#include "khtmlview.h"
+
+#include <kglobal.h>
+#include <kdebug.h>
+
+using namespace khtml;
+using namespace DOM;
+
+RenderBody::RenderBody(HTMLBodyElementImpl* element)
+ : RenderBlock(element)
+{
+ scrollbarsStyled = false;
+}
+
+RenderBody::~RenderBody()
+{
+}
+
+void RenderBody::setStyle(RenderStyle* style)
+{
+// qDebug("RenderBody::setStyle()");
+ // ignore position: fixed on body
+ if (style->htmlHacks() && style->position() == FIXED)
+ style->setPosition(STATIC);
+
+ RenderBlock::setStyle(style);
+ document()->setTextColor( style->color() );
+ scrollbarsStyled = false;
+}
+
+void RenderBody::paintBoxDecorations(PaintInfo& paintInfo, int _tx, int _ty)
+{
+ //kdDebug( 6040 ) << renderName() << "::paintDecorations()" << endl;
+ QColor bgColor;
+ const BackgroundLayer *bgLayer = 0;
+
+ if( parent()->style()->hasBackground() ) {
+ // the root element already has a non-transparent background of its own
+ // so we must fork our own. (CSS2.1 - 14.2 §4)
+ bgColor = style()->backgroundColor();
+ bgLayer = style()->backgroundLayers();
+ }
+
+ int w = width();
+ int h = height() + borderTopExtra() + borderBottomExtra();
+ _ty -= borderTopExtra();
+
+ int my = kMax(_ty, paintInfo.r.y());
+ int end = kMin( paintInfo.r.y()+paintInfo.r.height(), _ty + h );
+ int mh = end - my;
+
+ paintBackgrounds(paintInfo.p, bgColor, bgLayer, my, mh, _tx, _ty, w, h);
+
+ if(style()->hasBorder())
+ paintBorder( paintInfo.p, _tx, _ty, w, h, style() );
+
+}
+
+void RenderBody::repaint(Priority p)
+{
+ RenderObject *cb = containingBlock();
+ if(cb)
+ cb->repaint(p);
+}
+
+void RenderBody::layout()
+{
+ // in quirk mode, we'll need to have our margins determined
+ // for percentage height calculations
+ if (style()->htmlHacks())
+ calcHeight();
+ RenderBlock::layout();
+
+ if (!scrollbarsStyled)
+ {
+ RenderCanvas* canvas = this->canvas();
+ if (canvas->view())
+ {
+ canvas->view()->horizontalScrollBar()->setPalette(style()->palette());
+ canvas->view()->verticalScrollBar()->setPalette(style()->palette());
+ }
+ scrollbarsStyled=true;
+ }
+}
+
+int RenderBody::availableHeight() const
+{
+ int h = RenderBlock::availableHeight();
+
+ if( style()->marginTop().isFixed() )
+ h -= style()->marginTop().value();
+ if( style()->marginBottom().isFixed() )
+ h -= style()->marginBottom().value();
+
+ return kMax(0, h);
+}
diff --git a/khtml/rendering/render_body.h b/khtml/rendering/render_body.h
new file mode 100644
index 000000000..7951f73e1
--- /dev/null
+++ b/khtml/rendering/render_body.h
@@ -0,0 +1,56 @@
+/*
+ * This file is part of the html renderer for KDE.
+ *
+ * Copyright (C) 2000-2003 Lars Knoll (knoll@kde.org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef RENDER_BODY
+#define RENDER_BODY
+
+#include "rendering/render_block.h"
+
+namespace DOM
+{
+ class HTMLBodyElementImpl;
+}
+
+namespace khtml {
+
+class RenderBody : public RenderBlock
+{
+public:
+ RenderBody(DOM::HTMLBodyElementImpl* node);
+ virtual ~RenderBody();
+
+ virtual bool isBody() const { return true; }
+
+ virtual const char *renderName() const { return "RenderBody"; }
+ virtual void repaint(Priority p=NormalPriority);
+
+ virtual void layout();
+ virtual void setStyle(RenderStyle* style);
+
+ virtual int availableHeight() const;
+
+protected:
+ virtual void paintBoxDecorations(PaintInfo&, int _tx, int _ty);
+ bool scrollbarsStyled;
+};
+
+} // end namespace
+#endif
diff --git a/khtml/rendering/render_box.cpp b/khtml/rendering/render_box.cpp
new file mode 100644
index 000000000..56a3109d5
--- /dev/null
+++ b/khtml/rendering/render_box.cpp
@@ -0,0 +1,2325 @@
+/**
+ * This file is part of the DOM implementation for KDE.
+ *
+ * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * (C) 2002-2003 Apple Computer, Inc.
+ * (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com)
+ * (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
+ *
+ * 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.
+ *
+ */
+// -------------------------------------------------------------------------
+//#define DEBUG_LAYOUT
+//#define CLIP_DEBUG
+
+
+#include <qpainter.h>
+
+#include "misc/loader.h"
+#include "rendering/render_replaced.h"
+#include "rendering/render_canvas.h"
+#include "rendering/render_table.h"
+#include "rendering/render_inline.h"
+#include "rendering/render_block.h"
+#include "rendering/render_line.h"
+#include "rendering/render_layer.h"
+#include "misc/htmlhashes.h"
+#include "xml/dom_nodeimpl.h"
+#include "xml/dom_docimpl.h"
+#include "html/html_elementimpl.h"
+
+#include <khtmlview.h>
+#include <kdebug.h>
+#include <kglobal.h>
+#include <assert.h>
+
+
+using namespace DOM;
+using namespace khtml;
+
+#define TABLECELLMARGIN -0x4000
+
+RenderBox::RenderBox(DOM::NodeImpl* node)
+ : RenderContainer(node)
+{
+ m_minWidth = -1;
+ m_maxWidth = -1;
+ m_width = m_height = 0;
+ m_x = 0;
+ m_y = 0;
+ m_marginTop = 0;
+ m_marginBottom = 0;
+ m_marginLeft = 0;
+ m_marginRight = 0;
+ m_staticX = 0;
+ m_staticY = 0;
+
+ m_placeHolderBox = 0;
+ m_layer = 0;
+}
+
+RenderBlock* RenderBox::createAnonymousBlock()
+{
+ RenderStyle *newStyle = new RenderStyle();
+ newStyle->inheritFrom(style());
+ newStyle->setDisplay(BLOCK);
+
+ RenderBlock *newBox = new (renderArena()) RenderBlock(document() /* anonymous*/);
+ newBox->setStyle(newStyle);
+ return newBox;
+}
+
+void RenderBox::restructureParentFlow() {
+ if (!parent() || parent()->childrenInline() == isInline())
+ return;
+ // We have gone from not affecting the inline status of the parent flow to suddenly
+ // having an impact. See if there is a mismatch between the parent flow's
+ // childrenInline() state and our state.
+ if (!isInline()) {
+ if (parent()->isRenderInline()) {
+ // We have to split the parent flow.
+ RenderInline* parentInline = static_cast<RenderInline*>(parent());
+ RenderBlock* newBox = parentInline->createAnonymousBlock();
+
+ RenderFlow* oldContinuation = parent()->continuation();
+ parentInline->setContinuation(newBox);
+
+ RenderObject* beforeChild = nextSibling();
+ parent()->removeChildNode(this);
+ parentInline->splitFlow(beforeChild, newBox, this, oldContinuation);
+ }
+ else if (parent()->isRenderBlock())
+ static_cast<RenderBlock*>(parent())->makeChildrenNonInline();
+ }
+ else {
+ // An anonymous block must be made to wrap this inline.
+ RenderBlock* box = createAnonymousBlock();
+ parent()->insertChildNode(box, this);
+ box->appendChildNode(parent()->removeChildNode(this));
+ }
+}
+
+static inline bool overflowAppliesTo(RenderObject* o)
+{
+ // css 2.1-11.1.1
+ // 1) overflow only applies to non-replaced block-level elements, table cells, and inline-block elements
+ if (o->isRenderBlock() || o->isTableRow() || o->isTableSection())
+ // 2) overflow on root applies to the viewport (cf. KHTMLView::layout)
+ if (!o->isRoot())
+ // 3) overflow on body may apply to the viewport...
+ if (!o->isBody()
+ // ...but only for HTML documents...
+ || !o->document()->isHTMLDocument()
+ // ...and only when the root has a visible overflow
+ || !o->document()->documentElement()->renderer()
+ || !o->document()->documentElement()->renderer()->style()
+ || o->document()->documentElement()->renderer()->style()->hidesOverflow())
+ return true;
+
+ return false;
+}
+
+void RenderBox::setStyle(RenderStyle *_style)
+{
+ bool affectsParent = style() && isFloatingOrPositioned() &&
+ (!_style->isFloating() && _style->position() != ABSOLUTE && _style->position() != FIXED) &&
+ parent() && (parent()->isBlockFlow() || parent()->isInlineFlow());
+
+ RenderContainer::setStyle(_style);
+
+ // The root always paints its background/border.
+ if (isRoot())
+ setShouldPaintBackgroundOrBorder(true);
+
+ switch(_style->display())
+ {
+ case INLINE:
+ case INLINE_BLOCK:
+ case INLINE_TABLE:
+ setInline(true);
+ break;
+ case RUN_IN:
+ if (isInline() && parent() && parent()->childrenInline())
+ break;
+ default:
+ setInline(false);
+ }
+
+ switch(_style->position())
+ {
+ case ABSOLUTE:
+ case FIXED:
+ setPositioned(true);
+ break;
+ default:
+ setPositioned(false);
+ if( !isTableCell() && _style->isFloating() )
+ setFloating(true);
+
+ if( _style->position() == RELATIVE )
+ setRelPositioned(true);
+ }
+
+ if (overflowAppliesTo(this) && _style->hidesOverflow())
+ setHasOverflowClip();
+
+ if (requiresLayer()) {
+ if (!m_layer) {
+ m_layer = new (renderArena()) RenderLayer(this);
+ m_layer->insertOnlyThisLayer();
+ if (parent() && containingBlock())
+ m_layer->updateLayerPosition();
+ }
+ }
+ else if (m_layer && !isCanvas()) {
+ m_layer->removeOnlyThisLayer();
+ m_layer = 0;
+ }
+
+ if (m_layer)
+ m_layer->styleChanged();
+
+ if (style()->outlineWidth() > 0 && style()->outlineSize() > maximalOutlineSize(PaintActionOutline))
+ static_cast<RenderCanvas*>(document()->renderer())->setMaximalOutlineSize(style()->outlineSize());
+ if (affectsParent)
+ restructureParentFlow();
+}
+
+RenderBox::~RenderBox()
+{
+ //kdDebug( 6040 ) << "Element destructor: this=" << nodeName().string() << endl;
+}
+
+void RenderBox::detach()
+{
+ RenderLayer* layer = m_layer;
+ RenderArena* arena = renderArena();
+
+ RenderContainer::detach();
+
+ if (layer)
+ layer->detach(arena);
+}
+
+InlineBox* RenderBox::createInlineBox(bool /*makePlaceHolderBox*/, bool /*isRootLineBox*/)
+{
+ if (m_placeHolderBox)
+ m_placeHolderBox->detach(renderArena());
+ return (m_placeHolderBox = new (renderArena()) InlineBox(this));
+}
+
+void RenderBox::deleteInlineBoxes(RenderArena* arena)
+{
+ if (m_placeHolderBox) {
+ m_placeHolderBox->detach( arena ? arena : renderArena() );
+ m_placeHolderBox = 0;
+ }
+}
+
+short RenderBox::contentWidth() const
+{
+ short w = m_width - style()->borderLeftWidth() - style()->borderRightWidth();
+ w -= paddingLeft() + paddingRight();
+
+ if (m_layer && scrollsOverflowY())
+ w -= m_layer->verticalScrollbarWidth();
+
+ //kdDebug( 6040 ) << "RenderBox::contentWidth(2) = " << w << endl;
+ return w;
+}
+
+int RenderBox::contentHeight() const
+{
+ int h = m_height - style()->borderTopWidth() - style()->borderBottomWidth();
+ h -= paddingTop() + paddingBottom();
+
+ if (m_layer && scrollsOverflowX())
+ h -= m_layer->horizontalScrollbarHeight();
+
+ return h;
+}
+
+void RenderBox::setPos( int xPos, int yPos )
+{
+ m_x = xPos; m_y = yPos;
+}
+
+short RenderBox::width() const
+{
+ return m_width;
+}
+
+int RenderBox::height() const
+{
+ return m_height;
+}
+
+void RenderBox::setWidth( int width )
+{
+ m_width = width;
+}
+
+void RenderBox::setHeight( int height )
+{
+ m_height = height;
+}
+
+int RenderBox::calcBoxHeight(int h) const
+{
+ if (style()->boxSizing() == CONTENT_BOX)
+ h += borderTop() + borderBottom() + paddingTop() + paddingBottom();
+
+ return h;
+}
+
+int RenderBox::calcBoxWidth(int w) const
+{
+ if (style()->boxSizing() == CONTENT_BOX)
+ w += borderLeft() + borderRight() + paddingLeft() + paddingRight();
+
+ return w;
+}
+
+int RenderBox::calcContentHeight(int h) const
+{
+ if (style()->boxSizing() == BORDER_BOX)
+ h -= borderTop() + borderBottom() + paddingTop() + paddingBottom();
+
+ return kMax(0, h);
+}
+
+int RenderBox::calcContentWidth(int w) const
+{
+ if (style()->boxSizing() == BORDER_BOX)
+ w -= borderLeft() + borderRight() + paddingLeft() + paddingRight();
+
+ return kMax(0, w);
+}
+
+// --------------------- painting stuff -------------------------------
+
+void RenderBox::paint(PaintInfo& i, int _tx, int _ty)
+{
+ _tx += m_x;
+ _ty += m_y;
+
+ if (hasOverflowClip() && m_layer)
+ m_layer->subtractScrollOffset(_tx, _ty);
+
+ // default implementation. Just pass things through to the children
+ for(RenderObject* child = firstChild(); child; child = child->nextSibling())
+ child->paint(i, _tx, _ty);
+}
+
+void RenderBox::paintRootBoxDecorations(PaintInfo& paintInfo, int _tx, int _ty)
+{
+ //kdDebug( 6040 ) << renderName() << "::paintRootBoxDecorations()" << _tx << "/" << _ty << endl;
+ const BackgroundLayer* bgLayer = style()->backgroundLayers();
+ QColor bgColor = style()->backgroundColor();
+ if (document()->isHTMLDocument() && !style()->hasBackground()) {
+ // Locate the <body> element using the DOM. This is easier than trying
+ // to crawl around a render tree with potential :before/:after content and
+ // anonymous blocks created by inline <body> tags etc. We can locate the <body>
+ // render object very easily via the DOM.
+ HTMLElementImpl* body = document()->body();
+ RenderObject* bodyObject = (body && body->id() == ID_BODY) ? body->renderer() : 0;
+
+ if (bodyObject) {
+ bgLayer = bodyObject->style()->backgroundLayers();
+ bgColor = bodyObject->style()->backgroundColor();
+ }
+ }
+
+ if( !bgColor.isValid() && canvas()->view())
+ bgColor = canvas()->view()->palette().active().color(QColorGroup::Base);
+
+ int w = width();
+ int h = height();
+
+ // kdDebug(0) << "width = " << w <<endl;
+
+ int rw, rh;
+ if (canvas()->view()) {
+ rw = canvas()->view()->contentsWidth();
+ rh = canvas()->view()->contentsHeight();
+ } else {
+ rw = canvas()->docWidth();
+ rh = canvas()->docHeight();
+ }
+
+ // kdDebug(0) << "rw = " << rw <<endl;
+
+ int bx = _tx - marginLeft();
+ int by = _ty - marginTop();
+ int bw = QMAX(w + marginLeft() + marginRight() + borderLeft() + borderRight(), rw);
+ int bh = QMAX(h + marginTop() + marginBottom() + borderTop() + borderBottom(), rh);
+
+ // CSS2 14.2:
+ // " The background of the box generated by the root element covers the entire canvas."
+ // hence, paint the background even in the margin areas (unlike for every other element!)
+ // I just love these little inconsistencies .. :-( (Dirk)
+ int my = kMax(by, paintInfo.r.y());
+
+ paintBackgrounds(paintInfo.p, bgColor, bgLayer, my, paintInfo.r.height(), bx, by, bw, bh);
+
+ if(style()->hasBorder())
+ paintBorder( paintInfo.p, _tx, _ty, w, h, style() );
+}
+
+void RenderBox::paintBoxDecorations(PaintInfo& paintInfo, int _tx, int _ty)
+{
+ //kdDebug( 6040 ) << renderName() << "::paintDecorations()" << endl;
+
+ if(isRoot())
+ return paintRootBoxDecorations(paintInfo, _tx, _ty);
+
+ int w = width();
+ int h = height() + borderTopExtra() + borderBottomExtra();
+ _ty -= borderTopExtra();
+
+ int my = kMax(_ty,paintInfo.r.y());
+ int end = kMin( paintInfo.r.y() + paintInfo.r.height(), _ty + h );
+ int mh = end - my;
+
+ // The <body> only paints its background if the root element has defined a background
+ // independent of the body. Go through the DOM to get to the root element's render object,
+ // since the root could be inline and wrapped in an anonymous block.
+
+ if (!isBody() || !document()->isHTMLDocument() || document()->documentElement()->renderer()->style()->hasBackground())
+ paintBackgrounds(paintInfo.p, style()->backgroundColor(), style()->backgroundLayers(), my, mh, _tx, _ty, w, h);
+
+ if(style()->hasBorder()) {
+ paintBorder(paintInfo.p, _tx, _ty, w, h, style());
+ }
+}
+
+void RenderBox::paintBackgrounds(QPainter *p, const QColor& c, const BackgroundLayer* bgLayer, int clipy, int cliph, int _tx, int _ty, int w, int height)
+ {
+ if (!bgLayer) return;
+ paintBackgrounds(p, c, bgLayer->next(), clipy, cliph, _tx, _ty, w, height);
+ paintBackground(p, c, bgLayer, clipy, cliph, _tx, _ty, w, height);
+}
+
+void RenderBox::paintBackground(QPainter *p, const QColor& c, const BackgroundLayer* bgLayer, int clipy, int cliph, int _tx, int _ty, int w, int height)
+{
+ paintBackgroundExtended(p, c, bgLayer, clipy, cliph, _tx, _ty, w, height,
+ borderLeft(), borderRight(), paddingLeft(), paddingRight());
+}
+
+static void calculateBackgroundSize(const BackgroundLayer* bgLayer, int& scaledWidth, int& scaledHeight)
+{
+ CachedImage* bg = bgLayer->backgroundImage();
+
+ if (bgLayer->isBackgroundSizeSet()) {
+ Length bgWidth = bgLayer->backgroundSize().width;
+ Length bgHeight = bgLayer->backgroundSize().height;
+
+ if (bgWidth.isPercent())
+ scaledWidth = scaledWidth * bgWidth.value() / 100;
+ else if (bgWidth.isFixed())
+ scaledWidth = bgWidth.value();
+ else if (bgWidth.isVariable()) {
+ // If the width is auto and the height is not, we have to use the appropriate
+ // scale to maintain our aspect ratio.
+ if (bgHeight.isPercent()) {
+ int scaledH = scaledHeight * bgHeight.value() / 100;
+ scaledWidth = bg->pixmap_size().width() * scaledH / bg->pixmap_size().height();
+ } else if (bgHeight.isFixed())
+ scaledWidth = bg->pixmap_size().width() * bgHeight.value() / bg->pixmap_size().height();
+ }
+
+ if (bgHeight.isPercent())
+ scaledHeight = scaledHeight * bgHeight.value() / 100;
+ else if (bgHeight.isFixed())
+ scaledHeight = bgHeight.value();
+ else if (bgHeight.isVariable()) {
+ // If the height is auto and the width is not, we have to use the appropriate
+ // scale to maintain our aspect ratio.
+ if (bgWidth.isPercent())
+ scaledHeight = bg->pixmap_size().height() * scaledWidth / bg->pixmap_size().width();
+ else if (bgWidth.isFixed())
+ scaledHeight = bg->pixmap_size().height() * bgWidth.value() / bg->pixmap_size().width();
+ else if (bgWidth.isVariable()) {
+ // If both width and height are auto, we just want to use the image's
+ // intrinsic size.
+ scaledWidth = bg->pixmap_size().width();
+ scaledHeight = bg->pixmap_size().height();
+ }
+ }
+ } else {
+ scaledWidth = bg->pixmap_size().width();
+ scaledHeight = bg->pixmap_size().height();
+ }
+}
+
+void RenderBox::paintBackgroundExtended(QPainter *p, const QColor &c, const BackgroundLayer* bgLayer, int clipy, int cliph,
+ int _tx, int _ty, int w, int h,
+ int bleft, int bright, int pleft, int pright)
+{
+ if ( cliph < 0 )
+ return;
+
+ if (bgLayer->backgroundClip() != BGBORDER) {
+ // Clip to the padding or content boxes as necessary.
+ bool includePadding = bgLayer->backgroundClip() == BGCONTENT;
+ int x = _tx + bleft + (includePadding ? pleft : 0);
+ int y = _ty + borderTop() + (includePadding ? paddingTop() : 0);
+ int width = w - bleft - bright - (includePadding ? pleft + pright : 0);
+ int height = h - borderTop() - borderBottom() - (includePadding ? paddingTop() + paddingBottom() : 0);
+ p->save();
+ p->setClipRect(QRect(x, y, width, height), QPainter::CoordPainter);
+ }
+
+ CachedImage* bg = bgLayer->backgroundImage();
+ bool shouldPaintBackgroundImage = bg && bg->pixmap_size() == bg->valid_rect().size() && !bg->isTransparent() && !bg->isErrorImage();
+ QColor bgColor = c;
+
+ // Paint the color first underneath all images.
+ if (!bgLayer->next() && bgColor.isValid() && qAlpha(bgColor.rgb()) > 0)
+ p->fillRect(_tx, clipy, w, cliph, bgColor);
+
+ // no progressive loading of the background image
+ if (shouldPaintBackgroundImage) {
+ int sx = 0;
+ int sy = 0;
+ int cw,ch;
+ int cx,cy;
+ int scaledImageWidth, scaledImageHeight;
+
+ // CSS2 chapter 14.2.1
+
+ if (bgLayer->backgroundAttachment()) {
+ //scroll
+ int hpab = 0, vpab = 0, left = 0, top = 0; // Init to 0 for background-origin of 'border'
+ if (bgLayer->backgroundOrigin() != BGBORDER) {
+ hpab += bleft + bright;
+ vpab += borderTop() + borderBottom();
+ left += bleft;
+ top += borderTop();
+ if (bgLayer->backgroundOrigin() == BGCONTENT) {
+ hpab += pleft + pright;
+ vpab += paddingTop() + paddingBottom();
+ left += pleft;
+ top += paddingTop();
+ }
+ }
+
+ int pw = w - hpab;
+ int ph = h - vpab;
+ scaledImageWidth = pw;
+ scaledImageHeight = ph;
+ calculateBackgroundSize(bgLayer, scaledImageWidth, scaledImageHeight);
+
+ EBackgroundRepeat bgr = bgLayer->backgroundRepeat();
+ if (bgr == NO_REPEAT || bgr == REPEAT_Y) {
+ cw = scaledImageWidth;
+ int xPosition = bgLayer->backgroundXPosition().minWidth(pw-scaledImageWidth);
+ if ( xPosition >= 0 ) {
+ cx = _tx + xPosition;
+ cw = kMin(scaledImageWidth, pw - xPosition);
+ }
+ else {
+ cx = _tx;
+ if (scaledImageWidth > 0) {
+ sx = -xPosition;
+ cw = kMin(scaledImageWidth+xPosition, pw);
+ }
+ }
+ cx += left;
+ } else {
+ // repeat over x
+ cw = w;
+ cx = _tx;
+ if (scaledImageWidth > 0) {
+ int xPosition = bgLayer->backgroundXPosition().minWidth(pw-scaledImageWidth);
+ sx = scaledImageWidth - (xPosition % scaledImageWidth);
+ sx -= left % scaledImageWidth;
+ }
+ }
+ if (bgr == NO_REPEAT || bgr == REPEAT_X) {
+ ch = scaledImageHeight;
+ int yPosition = bgLayer->backgroundYPosition().minWidth(ph - scaledImageHeight);
+ if ( yPosition >= 0 ) {
+ cy = _ty + yPosition;
+ ch = kMin(ch, ph - yPosition);
+ }
+ else {
+ cy = _ty;
+ if (scaledImageHeight > 0) {
+ sy = -yPosition;
+ ch = kMin(scaledImageHeight+yPosition, ph);
+ }
+ }
+
+ cy += top;
+ } else {
+ // repeat over y
+ ch = h;
+ cy = _ty;
+ if (scaledImageHeight > 0) {
+ int yPosition = bgLayer->backgroundYPosition().minWidth(ph - scaledImageHeight);
+ sy = scaledImageHeight - (yPosition % scaledImageHeight);
+ sy -= top % scaledImageHeight;
+ }
+ }
+ if (layer())
+ layer()->scrollOffset(sx, sy);
+ }
+ else
+ {
+ //fixed
+ QRect vr = viewRect();
+ int pw = vr.width();
+ int ph = vr.height();
+ scaledImageWidth = pw;
+ scaledImageHeight = ph;
+ calculateBackgroundSize(bgLayer, scaledImageWidth, scaledImageHeight);
+ EBackgroundRepeat bgr = bgLayer->backgroundRepeat();
+
+ int xPosition = bgLayer->backgroundXPosition().minWidth(pw-scaledImageWidth);
+ if (bgr == NO_REPEAT || bgr == REPEAT_Y) {
+ cw = kMin(scaledImageWidth, pw - xPosition);
+ cx = vr.x() + xPosition;
+ } else {
+ cw = pw;
+ cx = vr.x();
+ if (scaledImageWidth > 0)
+ sx = scaledImageWidth - xPosition % scaledImageWidth;
+ }
+
+ int yPosition = bgLayer->backgroundYPosition().minWidth(ph-scaledImageHeight);
+ if (bgr == NO_REPEAT || bgr == REPEAT_X) {
+ ch = kMin(scaledImageHeight, ph - yPosition);
+ cy = vr.y() + yPosition;
+ } else {
+ ch = ph;
+ cy = vr.y();
+ if (scaledImageHeight > 0)
+ sy = scaledImageHeight - yPosition % scaledImageHeight;
+ }
+
+ QRect fix(cx, cy, cw, ch);
+ QRect ele(_tx, _ty, w, h);
+ QRect b = fix.intersect(ele);
+
+ //kdDebug() <<" ele is " << ele << " b is " << b << " fix is " << fix << endl;
+ sx+=b.x()-cx;
+ sy+=b.y()-cy;
+ cx=b.x();cy=b.y();cw=b.width();ch=b.height();
+ }
+ // restrict painting to repaint-clip
+ if (cy < clipy) {
+ ch -= (clipy - cy);
+ sy += (clipy - cy);
+ cy = clipy;
+ }
+ ch = kMin(ch, cliph);
+
+// kdDebug() << " clipy, cliph: " << clipy << ", " << cliph << endl;
+// kdDebug() << " drawTiledPixmap(" << cx << ", " << cy << ", " << cw << ", " << ch << ", " << sx << ", " << sy << ")" << endl;
+ if (cw>0 && ch>0)
+ p->drawTiledPixmap(cx, cy, cw, ch, bg->tiled_pixmap(c, scaledImageWidth, scaledImageHeight), sx, sy);
+
+ }
+
+ if (bgLayer->backgroundClip() != BGBORDER)
+ p->restore(); // Undo the background clip
+
+}
+
+void RenderBox::outlineBox(QPainter *p, int _tx, int _ty, const char *color)
+{
+ p->setPen(QPen(QColor(color), 1, Qt::DotLine));
+ p->setBrush( Qt::NoBrush );
+ p->drawRect(_tx, _ty, m_width, m_height);
+}
+
+QRect RenderBox::getOverflowClipRect(int tx, int ty)
+{
+ // XXX When overflow-clip (CSS3) is implemented, we'll obtain the property
+ // here.
+ int bl=borderLeft(),bt=borderTop(),bb=borderBottom(),br=borderRight();
+ int clipx = tx+bl;
+ int clipy = ty+bt;
+ int clipw = m_width-bl-br;
+ int cliph = m_height-bt-bb+borderTopExtra()+borderBottomExtra();
+
+ // Substract out scrollbars if we have them.
+ if (m_layer) {
+ clipw -= m_layer->verticalScrollbarWidth();
+ cliph -= m_layer->horizontalScrollbarHeight();
+ }
+
+ return QRect(clipx,clipy,clipw,cliph);
+}
+
+QRect RenderBox::getClipRect(int tx, int ty)
+{
+ int bl=borderLeft(),bt=borderTop(),bb=borderBottom(),br=borderRight();
+ // ### what about paddings?
+ int clipw = m_width-bl-br;
+ int cliph = m_height-bt-bb;
+
+ bool rtl = (style()->direction() == RTL);
+
+ int clipleft = 0;
+ int clipright = clipw;
+ int cliptop = 0;
+ int clipbottom = cliph;
+
+ if ( style()->hasClip() && style()->position() == ABSOLUTE ) {
+ // the only case we use the clip property according to CSS 2.1
+ if (!style()->clipLeft().isVariable()) {
+ int c = style()->clipLeft().width(clipw);
+ if ( rtl )
+ clipleft = clipw - c;
+ else
+ clipleft = c;
+ }
+ if (!style()->clipRight().isVariable()) {
+ int w = style()->clipRight().width(clipw);
+ if ( rtl ) {
+ clipright = clipw - w;
+ } else {
+ clipright = w;
+ }
+ }
+ if (!style()->clipTop().isVariable())
+ cliptop = style()->clipTop().width(cliph);
+ if (!style()->clipBottom().isVariable())
+ clipbottom = style()->clipBottom().width(cliph);
+ }
+ int clipx = tx + clipleft;
+ int clipy = ty + cliptop;
+ clipw = clipright-clipleft;
+ cliph = clipbottom-cliptop;
+
+ //kdDebug( 6040 ) << "setting clip("<<clipx<<","<<clipy<<","<<clipw<<","<<cliph<<")"<<endl;
+
+ return QRect(clipx,clipy,clipw,cliph);
+}
+
+void RenderBox::close()
+{
+ setNeedsLayoutAndMinMaxRecalc();
+}
+
+short RenderBox::containingBlockWidth() const
+{
+ if (isCanvas() && canvas()->view())
+ {
+ if (canvas()->pagedMode())
+ return canvas()->width();
+ else
+ return canvas()->view()->visibleWidth();
+ }
+
+ RenderBlock* cb = containingBlock();
+ if (isRenderBlock() && cb->isTable() && static_cast<RenderTable*>(cb)->caption() == this) {
+ //captions are not affected by table border or padding
+ return cb->width();
+ }
+ if (usesLineWidth())
+ return cb->lineWidth(m_y);
+ else
+ return cb->contentWidth();
+}
+
+bool RenderBox::absolutePosition(int &_xPos, int &_yPos, bool f) const
+{
+ if ( style()->position() == FIXED )
+ f = true;
+ RenderObject *o = container();
+ if( o && o->absolutePosition(_xPos, _yPos, f))
+ {
+ if ( o->layer() ) {
+ if (o->hasOverflowClip())
+ o->layer()->subtractScrollOffset( _xPos, _yPos );
+ if (isPositioned())
+ o->layer()->checkInlineRelOffset(this, _xPos, _yPos);
+ }
+
+ if(!isInline() || isReplaced()) {
+ _xPos += xPos(),
+ _yPos += yPos();
+ }
+
+ if(isRelPositioned())
+ relativePositionOffset(_xPos, _yPos);
+ return true;
+ }
+ else
+ {
+ _xPos = 0;
+ _yPos = 0;
+ return false;
+ }
+}
+
+void RenderBox::position(InlineBox* box, int /*from*/, int /*len*/, bool /*reverse*/)
+{
+ if (isPositioned()) {
+ // Cache the x position only if we were an INLINE type originally.
+ bool wasInline = style()->isOriginalDisplayInlineType();
+
+ if (wasInline && hasStaticX()) {
+ // The value is cached in the xPos of the box. We only need this value if
+ // our object was inline originally, since otherwise it would have ended up underneath
+ // the inlines.
+ m_staticX = box->xPos();
+ }
+ else if (!wasInline && hasStaticY()) {
+ // Our object was a block originally, so we make our normal flow position be
+ // just below the line box (as though all the inlines that came before us got
+ // wrapped in an anonymous block, which is what would have happened had we been
+ // in flow). This value was cached in the yPos() of the box.
+ m_staticY = box->yPos();
+ }
+ }
+ else if (isReplaced())
+ setPos( box->xPos(), box->yPos() );
+}
+
+void RenderBox::repaint(Priority prior)
+{
+ int ow = style() ? style()->outlineSize() : 0;
+ if( isInline() && !isReplaced() )
+ {
+ RenderObject* p = parent();
+ Q_ASSERT(p);
+ while( p->isInline() && !p->isReplaced() )
+ p = p->parent();
+ int xoff = p->hasOverflowClip() ? 0 : p->overflowLeft();
+ int yoff = p->hasOverflowClip() ? 0 : p->overflowTop();
+ p->repaintRectangle( -ow + xoff, -ow + yoff, p->effectiveWidth()+ow*2, p->effectiveHeight()+ow*2, prior);
+ }
+ else
+ {
+ int xoff = hasOverflowClip() ? 0 : overflowLeft();
+ int yoff = hasOverflowClip() ? 0 : overflowTop();
+ repaintRectangle( -ow + xoff, -ow + yoff, effectiveWidth()+ow*2, effectiveHeight()+ow*2, prior);
+ }
+}
+
+void RenderBox::repaintRectangle(int x, int y, int w, int h, Priority p, bool f)
+{
+ x += m_x;
+ y += m_y;
+
+ // Apply the relative position offset when invalidating a rectangle. The layer
+ // is translated, but the render box isn't, so we need to do this to get the
+ // right dirty rect. Since this is called from RenderObject::setStyle, the relative position
+ // flag on the RenderObject has been cleared, so use the one on the style().
+ if (style()->position() == RELATIVE && m_layer)
+ relativePositionOffset(x,y);
+
+ if (style()->position() == FIXED) f=true;
+
+ // kdDebug( 6040 ) << "RenderBox(" <<this << ", " << renderName() << ")::repaintRectangle (" << x << "/" << y << ") (" << w << "/" << h << ")" << endl;
+ RenderObject *o = container();
+ if( o ) {
+ if (o->layer()) {
+ if (o->style()->hidesOverflow() && o->layer() && !o->isInlineFlow())
+ o->layer()->subtractScrollOffset(x,y); // For overflow:auto/scroll/hidden.
+ if (style()->position() == ABSOLUTE)
+ o->layer()->checkInlineRelOffset(this,x,y);
+ }
+ o->repaintRectangle(x, y, w, h, p, f);
+ }
+}
+
+void RenderBox::relativePositionOffset(int &tx, int &ty) const
+{
+ if(!style()->left().isVariable())
+ tx += style()->left().width(containingBlockWidth());
+ else if(!style()->right().isVariable())
+ tx -= style()->right().width(containingBlockWidth());
+ if(!style()->top().isVariable())
+ {
+ if (!style()->top().isPercent()
+ || containingBlock()->style()->height().isFixed())
+ ty += style()->top().width(containingBlockHeight());
+ }
+ else if(!style()->bottom().isVariable())
+ {
+ if (!style()->bottom().isPercent()
+ || containingBlock()->style()->height().isFixed())
+ ty -= style()->bottom().width(containingBlockHeight());
+ }
+}
+
+void RenderBox::calcWidth()
+{
+#ifdef DEBUG_LAYOUT
+ kdDebug( 6040 ) << "RenderBox("<<renderName()<<")::calcWidth()" << endl;
+#endif
+ if (isPositioned())
+ {
+ calcAbsoluteHorizontal();
+ }
+ else
+ {
+ bool treatAsReplaced = isReplaced() && !isInlineBlockOrInlineTable();
+ Length w;
+ if (treatAsReplaced)
+ w = Length( calcReplacedWidth(), Fixed );
+ else
+ w = style()->width();
+
+ Length ml = style()->marginLeft();
+ Length mr = style()->marginRight();
+
+ int cw = containingBlockWidth();
+ if (cw<0) cw = 0;
+
+ m_marginLeft = 0;
+ m_marginRight = 0;
+
+ if (isInline() && !isInlineBlockOrInlineTable())
+ {
+ // just calculate margins
+ m_marginLeft = ml.minWidth(cw);
+ m_marginRight = mr.minWidth(cw);
+ if (treatAsReplaced)
+ {
+ m_width = calcBoxWidth(w.width(cw));
+ m_width = KMAX(m_width, m_minWidth);
+ }
+
+ return;
+ }
+ else
+ {
+ LengthType widthType, minWidthType, maxWidthType;
+ if (treatAsReplaced) {
+ m_width = calcBoxWidth(w.width(cw));
+ widthType = w.type();
+ } else {
+ m_width = calcWidthUsing(Width, cw, widthType);
+ int minW = calcWidthUsing(MinWidth, cw, minWidthType);
+ int maxW = style()->maxWidth().value() == UNDEFINED ?
+ m_width : calcWidthUsing(MaxWidth, cw, maxWidthType);
+
+ if (m_width > maxW) {
+ m_width = maxW;
+ widthType = maxWidthType;
+ }
+ if (m_width < minW) {
+ m_width = minW;
+ widthType = minWidthType;
+ }
+ }
+
+ if (widthType == Variable) {
+ // kdDebug( 6040 ) << "variable" << endl;
+ m_marginLeft = ml.minWidth(cw);
+ m_marginRight = mr.minWidth(cw);
+ }
+ else
+ {
+// kdDebug( 6040 ) << "non-variable " << w.type << ","<< w.value << endl;
+ calcHorizontalMargins(ml,mr,cw);
+ }
+ }
+
+ if (cw && cw != m_width + m_marginLeft + m_marginRight && !isFloating() && !isInline())
+ {
+ if (containingBlock()->style()->direction()==LTR)
+ m_marginRight = cw - m_width - m_marginLeft;
+ else
+ m_marginLeft = cw - m_width - m_marginRight;
+ }
+ }
+
+#ifdef DEBUG_LAYOUT
+ kdDebug( 6040 ) << "RenderBox::calcWidth(): m_width=" << m_width << " containingBlockWidth()=" << containingBlockWidth() << endl;
+ kdDebug( 6040 ) << "m_marginLeft=" << m_marginLeft << " m_marginRight=" << m_marginRight << endl;
+#endif
+}
+
+int RenderBox::calcWidthUsing(WidthType widthType, int cw, LengthType& lengthType)
+{
+ int width = m_width;
+ Length w;
+ if (widthType == Width)
+ w = style()->width();
+ else if (widthType == MinWidth)
+ w = style()->minWidth();
+ else
+ w = style()->maxWidth();
+
+ lengthType = w.type();
+
+ if (lengthType == Variable) {
+ int marginLeft = style()->marginLeft().minWidth(cw);
+ int marginRight = style()->marginRight().minWidth(cw);
+ if (cw) width = cw - marginLeft - marginRight;
+
+ // size to max width?
+ if (sizesToMaxWidth()) {
+ width = KMAX(width, (int)m_minWidth);
+ width = KMIN(width, (int)m_maxWidth);
+ }
+ }
+ else
+ {
+ width = calcBoxWidth(w.width(cw));
+ }
+
+ return width;
+}
+
+void RenderBox::calcHorizontalMargins(const Length& ml, const Length& mr, int cw)
+{
+ if (isFloating() || isInline()) // Inline blocks/tables and floats don't have their margins increased.
+ {
+ m_marginLeft = ml.minWidth(cw);
+ m_marginRight = mr.minWidth(cw);
+ }
+ else
+ {
+ if ( (ml.isVariable() && mr.isVariable() && m_width<cw) ||
+ (!ml.isVariable() && !mr.isVariable() &&
+ containingBlock()->style()->textAlign() == KHTML_CENTER) )
+ {
+ m_marginLeft = (cw - m_width)/2;
+ if (m_marginLeft<0) m_marginLeft=0;
+ m_marginRight = cw - m_width - m_marginLeft;
+ }
+ else if ( (mr.isVariable() && m_width<cw) ||
+ (!ml.isVariable() && containingBlock()->style()->direction() == RTL &&
+ containingBlock()->style()->textAlign() == KHTML_LEFT))
+ {
+ m_marginLeft = ml.width(cw);
+ m_marginRight = cw - m_width - m_marginLeft;
+ }
+ else if ( (ml.isVariable() && m_width<cw) ||
+ (!mr.isVariable() && containingBlock()->style()->direction() == LTR &&
+ containingBlock()->style()->textAlign() == KHTML_RIGHT))
+ {
+ m_marginRight = mr.width(cw);
+ m_marginLeft = cw - m_width - m_marginRight;
+ }
+ else
+ {
+ // this makes auto margins 0 if we failed a m_width<cw test above (css2.1, 10.3.3)
+ m_marginLeft = ml.minWidth(cw);
+ m_marginRight = mr.minWidth(cw);
+ }
+ }
+}
+
+void RenderBox::calcHeight()
+{
+
+#ifdef DEBUG_LAYOUT
+ kdDebug( 6040 ) << "RenderBox::calcHeight()" << endl;
+#endif
+
+ //cell height is managed by table, inline elements do not have a height property.
+ if ( isTableCell() || (isInline() && !isReplaced()) )
+ return;
+
+ if (isPositioned())
+ calcAbsoluteVertical();
+ else
+ {
+ calcVerticalMargins();
+
+ // For tables, calculate margins only
+ if (isTable())
+ return;
+
+ Length h;
+ bool treatAsReplaced = isReplaced() && !isInlineBlockOrInlineTable();
+ bool checkMinMaxHeight = false;
+
+ if ( treatAsReplaced )
+ h = Length( calcReplacedHeight(), Fixed );
+ else {
+ h = style()->height();
+ checkMinMaxHeight = true;
+ }
+
+ int height;
+ if (checkMinMaxHeight) {
+ height = calcHeightUsing(style()->height());
+ if (height == -1)
+ height = m_height;
+ int minH = calcHeightUsing(style()->minHeight()); // Leave as -1 if unset.
+ int maxH = style()->maxHeight().value() == UNDEFINED ? height : calcHeightUsing(style()->maxHeight());
+ if (maxH == -1)
+ maxH = height;
+ height = kMin(maxH, height);
+ height = kMax(minH, height);
+ }
+ else {
+ // The only times we don't check min/max height are when a fixed length has
+ // been given as an override. Just use that.
+ height = calcBoxHeight(h.value());
+ }
+
+ if (height<m_height && !overhangingContents() && !hasOverflowClip())
+ setOverhangingContents();
+
+ m_height = height;
+ }
+
+ // Unfurling marquees override with the furled height.
+ if (style()->overflowX() == OMARQUEE && m_layer && m_layer->marquee() &&
+ m_layer->marquee()->isUnfurlMarquee() && !m_layer->marquee()->isHorizontal()) {
+ m_layer->marquee()->setEnd(m_height);
+ m_height = kMin(m_height, m_layer->marquee()->unfurlPos());
+ }
+
+}
+
+int RenderBox::calcHeightUsing(const Length& h)
+{
+ int height = -1;
+ if (!h.isVariable()) {
+ if (h.isFixed())
+ height = h.value();
+ else if (h.isPercent())
+ height = calcPercentageHeight(h);
+ if (height != -1) {
+ height = calcBoxHeight(height);
+ return height;
+ }
+ }
+ return height;
+}
+
+int RenderBox::calcImplicitHeight() const {
+ assert(hasImplicitHeight());
+
+ RenderBlock* cb = containingBlock();
+ // padding-box height
+ int ch = cb->height() - cb->borderTop() + cb->borderBottom();
+ int top = style()->top().width(ch);
+ int bottom = style()->bottom().width(ch);
+
+ return ch - top - bottom;
+}
+
+int RenderBox::calcPercentageHeight(const Length& height, bool treatAsReplaced) const
+{
+ int result = -1;
+ RenderBlock* cb = containingBlock();
+ // In quirk mode, table cells violate what the CSS spec says to do with heights.
+ if (cb->isTableCell() && style()->htmlHacks()) {
+ result = static_cast<RenderTableCell*>(cb)->cellPercentageHeight();
+ }
+
+ // Otherwise we only use our percentage height if our containing block had a specified
+ // height.
+ else if (cb->style()->height().isFixed())
+ result = cb->calcContentHeight(cb->style()->height().value());
+ else if (cb->style()->height().isPercent()) {
+ // We need to recur and compute the percentage height for our containing block.
+ result = cb->calcPercentageHeight(cb->style()->height(), treatAsReplaced);
+ if (result != -1)
+ result = cb->calcContentHeight(result);
+ }
+ else if (cb->isCanvas()) {
+ if (!canvas()->pagedMode())
+ result = static_cast<RenderCanvas*>(cb)->viewportHeight();
+ else
+ result = static_cast<RenderCanvas*>(cb)->height();
+ result -= cb->style()->borderTopWidth() - cb->style()->borderBottomWidth();
+ result -= cb->paddingTop() + cb->paddingBottom();
+ }
+ else if (cb->isBody() && style()->htmlHacks() &&
+ cb->style()->height().isVariable() && !cb->isFloatingOrPositioned()) {
+ int margins = cb->collapsedMarginTop() + cb->collapsedMarginBottom();
+ int visHeight = canvas()->viewportHeight();
+ RenderObject* p = cb->parent();
+ result = visHeight - (margins + p->marginTop() + p->marginBottom() +
+ p->borderTop() + p->borderBottom() +
+ p->paddingTop() + p->paddingBottom());
+ }
+ else if (cb->isRoot() && style()->htmlHacks() && cb->style()->height().isVariable()) {
+ int visHeight = canvas()->viewportHeight();
+ result = visHeight - (marginTop() + marginBottom() +
+ borderTop() + borderBottom() +
+ paddingTop() + paddingBottom());
+ }
+ else if (cb->isAnonymousBlock() || treatAsReplaced && style()->htmlHacks()) {
+ // IE quirk.
+ result = cb->calcPercentageHeight(cb->style()->height(), treatAsReplaced);
+ }
+ else if (cb->hasImplicitHeight()) {
+ result = cb->calcImplicitHeight();
+ }
+
+ if (result != -1) {
+ result = height.width(result);
+ if (cb->isTableCell() && style()->boxSizing() != BORDER_BOX) {
+ result -= (borderTop() + paddingTop() + borderBottom() + paddingBottom());
+ result = kMax(0, result);
+ }
+ }
+ return result;
+}
+
+short RenderBox::calcReplacedWidth() const
+{
+ int width = calcReplacedWidthUsing(Width);
+ int minW = calcReplacedWidthUsing(MinWidth);
+ int maxW = style()->maxWidth().value() == UNDEFINED ? width : calcReplacedWidthUsing(MaxWidth);
+
+ if (width > maxW)
+ width = maxW;
+
+ if (width < minW)
+ width = minW;
+
+ return width;
+}
+
+int RenderBox::calcReplacedWidthUsing(WidthType widthType) const
+{
+ Length w;
+ if (widthType == Width)
+ w = style()->width();
+ else if (widthType == MinWidth)
+ w = style()->minWidth();
+ else
+ w = style()->maxWidth();
+
+ switch (w.type()) {
+ case Fixed:
+ return w.value();
+ case Percent:
+ {
+ const int cw = containingBlockWidth();
+ if (cw > 0) {
+ int result = w.minWidth(cw);
+ return result;
+ }
+ }
+ // fall through
+ default:
+ return intrinsicWidth();
+ }
+}
+
+int RenderBox::calcReplacedHeight() const
+{
+ int height = calcReplacedHeightUsing(Height);
+ int minH = calcReplacedHeightUsing(MinHeight);
+ int maxH = style()->maxHeight().value() == UNDEFINED ? height : calcReplacedHeightUsing(MaxHeight);
+
+ if (height > maxH)
+ height = maxH;
+
+ if (height < minH)
+ height = minH;
+
+ return height;
+}
+
+int RenderBox::calcReplacedHeightUsing(HeightType heightType) const
+{
+ Length h;
+ if (heightType == Height)
+ h = style()->height();
+ else if (heightType == MinHeight)
+ h = style()->minHeight();
+ else
+ h = style()->maxHeight();
+ switch( h.type() ) {
+ case Fixed:
+ return h.value();
+ case Percent:
+ {
+ int th = calcPercentageHeight(h, true);
+ if (th != -1)
+ return th;
+ // fall through
+ }
+ default:
+ return intrinsicHeight();
+ };
+}
+
+int RenderBox::availableHeight() const
+{
+ return availableHeightUsing(style()->height());
+}
+
+int RenderBox::availableHeightUsing(const Length& h) const
+{
+ if (h.isFixed())
+ return calcContentHeight(h.value());
+
+ if (isCanvas())
+ if (static_cast<const RenderCanvas*>(this)->pagedMode())
+ return static_cast<const RenderCanvas*>(this)->pageHeight();
+ else
+ return static_cast<const RenderCanvas*>(this)->viewportHeight();
+
+ // We need to stop here, since we don't want to increase the height of the table
+ // artificially. We're going to rely on this cell getting expanded to some new
+ // height, and then when we lay out again we'll use the calculation below.
+ if (isTableCell() && (h.isVariable() || h.isPercent())) {
+ const RenderTableCell* tableCell = static_cast<const RenderTableCell*>(this);
+ return tableCell->cellPercentageHeight() -
+ (borderTop()+borderBottom()+paddingTop()+paddingBottom());
+ }
+
+ if (h.isPercent())
+ return calcContentHeight(h.width(containingBlock()->availableHeight()));
+
+ // Check for implicit height
+ if (hasImplicitHeight())
+ return calcImplicitHeight();
+
+ return containingBlock()->availableHeight();
+}
+
+int RenderBox::availableWidth() const
+{
+ return availableWidthUsing(style()->width());
+}
+
+int RenderBox::availableWidthUsing(const Length& w) const
+{
+ if (w.isFixed())
+ return calcContentWidth(w.value());
+
+ if (isCanvas())
+ return static_cast<const RenderCanvas*>(this)->viewportWidth();
+
+ if (w.isPercent())
+ return calcContentWidth(w.width(containingBlock()->availableWidth()));
+
+ return containingBlock()->availableWidth();
+}
+
+void RenderBox::calcVerticalMargins()
+{
+ if( isTableCell() ) {
+ // table margins are basically infinite
+ m_marginTop = TABLECELLMARGIN;
+ m_marginBottom = TABLECELLMARGIN;
+ return;
+ }
+
+ Length tm = style()->marginTop();
+ Length bm = style()->marginBottom();
+
+ // margins are calculated with respect to the _width_ of
+ // the containing block (8.3)
+ int cw = containingBlock()->contentWidth();
+
+ m_marginTop = tm.minWidth(cw);
+ m_marginBottom = bm.minWidth(cw);
+}
+
+void RenderBox::setStaticX(short staticX)
+{
+ m_staticX = staticX;
+}
+
+void RenderBox::setStaticY(int staticY)
+{
+ m_staticY = staticY;
+}
+
+void RenderBox::calcAbsoluteHorizontal()
+{
+ if (isReplaced()) {
+ calcAbsoluteHorizontalReplaced();
+ return;
+ }
+
+ // QUESTIONS
+ // FIXME 1: Which RenderObject's 'direction' property should used: the
+ // containing block (cb) as the spec seems to imply, the parent (parent()) as
+ // was previously done in calculating the static distances, or ourself, which
+ // was also previously done for deciding what to override when you had
+ // over-constrained margins? Also note that the container block is used
+ // in similar situations in other parts of the RenderBox class (see calcWidth()
+ // and calcHorizontalMargins()). For now we are using the parent for quirks
+ // mode and the containing block for strict mode.
+
+ // FIXME 2: Can perhaps optimize out cases when max-width/min-width are greater
+ // than or less than the computed m_width. Be careful of box-sizing and
+ // percentage issues.
+
+ // The following is based off of the W3C Working Draft from April 11, 2006 of
+ // CSS 2.1: Section 10.3.7 "Absolutely positioned, non-replaced elements"
+ // <http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width>
+ // (block-style-comments in this function and in calcAbsoluteHorizontalValues()
+ // correspond to text from the spec)
+
+
+ // We don't use containingBlock(), since we may be positioned by an enclosing
+ // relative positioned inline.
+ const RenderObject* containerBlock = container();
+
+ // FIXME: This is incorrect for cases where the container block is a relatively
+ // positioned inline.
+ const int containerWidth = containingBlockWidth() + containerBlock->paddingLeft() + containerBlock->paddingRight();
+
+ // To match WinIE, in quirks mode use the parent's 'direction' property
+ // instead of the the container block's.
+ EDirection containerDirection = (style()->htmlHacks()) ? parent()->style()->direction() : containerBlock->style()->direction();
+
+ const int bordersPlusPadding = borderLeft() + borderRight() + paddingLeft() + paddingRight();
+ const Length marginLeft = style()->marginLeft();
+ const Length marginRight = style()->marginRight();
+ Length left = style()->left();
+ Length right = style()->right();
+
+ /*---------------------------------------------------------------------------*\
+ * For the purposes of this section and the next, the term "static position"
+ * (of an element) refers, roughly, to the position an element would have had
+ * in the normal flow. More precisely:
+ *
+ * * The static position for 'left' is the distance from the left edge of the
+ * containing block to the left margin edge of a hypothetical box that would
+ * have been the first box of the element if its 'position' property had
+ * been 'static' and 'float' had been 'none'. The value is negative if the
+ * hypothetical box is to the left of the containing block.
+ * * The static position for 'right' is the distance from the right edge of the
+ * containing block to the right margin edge of the same hypothetical box as
+ * above. The value is positive if the hypothetical box is to the left of the
+ * containing block's edge.
+ *
+ * But rather than actually calculating the dimensions of that hypothetical box,
+ * user agents are free to make a guess at its probable position.
+ *
+ * For the purposes of calculating the static position, the containing block of
+ * fixed positioned elements is the initial containing block instead of the
+ * viewport, and all scrollable boxes should be assumed to be scrolled to their
+ * origin.
+ \*---------------------------------------------------------------------------*/
+
+ // Calculate the static distance if needed.
+ if (left.isVariable() && right.isVariable()) {
+ if (containerDirection == LTR) {
+ // 'm_staticX' should already have been set through layout of the parent.
+ int staticPosition = m_staticX - containerBlock->borderLeft();
+ for (RenderObject* po = parent(); po && po != containerBlock; po = po->parent())
+ staticPosition += po->xPos();
+ left = Length(staticPosition, Fixed);
+ } else {
+ RenderObject* po = parent();
+ // 'm_staticX' should already have been set through layout of the parent.
+ int staticPosition = m_staticX + containerWidth + containerBlock->borderRight() - po->width();
+ for (; po && po != containerBlock; po = po->parent())
+ staticPosition -= po->xPos();
+ right = Length(staticPosition, Fixed);
+ }
+ }
+
+ // Calculate constraint equation values for 'width' case.
+ calcAbsoluteHorizontalValues(style()->width(), containerBlock, containerDirection,
+ containerWidth, bordersPlusPadding,
+ left, right, marginLeft, marginRight,
+ m_width, m_marginLeft, m_marginRight, m_x);
+ // Calculate constraint equation values for 'max-width' case.calcContentWidth(width.width(containerWidth));
+ if (style()->maxWidth().value() != UNDEFINED) {
+ short maxWidth;
+ short maxMarginLeft;
+ short maxMarginRight;
+ short maxXPos;
+
+ calcAbsoluteHorizontalValues(style()->maxWidth(), containerBlock, containerDirection,
+ containerWidth, bordersPlusPadding,
+ left, right, marginLeft, marginRight,
+ maxWidth, maxMarginLeft, maxMarginRight, maxXPos);
+
+ if (m_width > maxWidth) {
+ m_width = maxWidth;
+ m_marginLeft = maxMarginLeft;
+ m_marginRight = maxMarginRight;
+ m_x = maxXPos;
+ }
+ }
+
+ // Calculate constraint equation values for 'min-width' case.
+ if (style()->minWidth().value()) {
+ short minWidth;
+ short minMarginLeft;
+ short minMarginRight;
+ short minXPos;
+
+ calcAbsoluteHorizontalValues(style()->minWidth(), containerBlock, containerDirection,
+ containerWidth, bordersPlusPadding,
+ left, right, marginLeft, marginRight,
+ minWidth, minMarginLeft, minMarginRight, minXPos);
+
+ if (m_width < minWidth) {
+ m_width = minWidth;
+ m_marginLeft = minMarginLeft;
+ m_marginRight = minMarginRight;
+ m_x = minXPos;
+ }
+ }
+
+ // Put m_width into correct form.
+ m_width += bordersPlusPadding;
+}
+
+void RenderBox::calcAbsoluteHorizontalValues(Length width, const RenderObject* containerBlock, EDirection containerDirection,
+ const int containerWidth, const int bordersPlusPadding,
+ const Length left, const Length right, const Length marginLeft, const Length marginRight,
+ short& widthValue, short& marginLeftValue, short& marginRightValue, short& xPos)
+{
+ // 'left' and 'right' cannot both be 'auto' because one would of been
+ // converted to the static postion already
+ assert(!(left.isVariable() && right.isVariable()));
+
+ int leftValue = 0;
+
+ bool widthIsAuto = width.isVariable();
+ bool leftIsAuto = left.isVariable();
+ bool rightIsAuto = right.isVariable();
+
+ if (!leftIsAuto && !widthIsAuto && !rightIsAuto) {
+ /*-----------------------------------------------------------------------*\
+ * If none of the three is 'auto': If both 'margin-left' and 'margin-
+ * right' are 'auto', solve the equation under the extra constraint that
+ * the two margins get equal values, unless this would make them negative,
+ * in which case when direction of the containing block is 'ltr' ('rtl'),
+ * set 'margin-left' ('margin-right') to zero and solve for 'margin-right'
+ * ('margin-left'). If one of 'margin-left' or 'margin-right' is 'auto',
+ * solve the equation for that value. If the values are over-constrained,
+ * ignore the value for 'left' (in case the 'direction' property of the
+ * containing block is 'rtl') or 'right' (in case 'direction' is 'ltr')
+ * and solve for that value.
+ \*-----------------------------------------------------------------------*/
+ // NOTE: It is not necessary to solve for 'right' in the over constrained
+ // case because the value is not used for any further calculations.
+
+ leftValue = left.width(containerWidth);
+ widthValue = calcContentWidth(width.width(containerWidth));
+
+ const int availableSpace = containerWidth - (leftValue + widthValue + right.width(containerWidth) + bordersPlusPadding);
+
+ // Margins are now the only unknown
+ if (marginLeft.isVariable() && marginRight.isVariable()) {
+ // Both margins auto, solve for equality
+ if (availableSpace >= 0) {
+ marginLeftValue = availableSpace / 2; // split the diference
+ marginRightValue = availableSpace - marginLeftValue; // account for odd valued differences
+ } else {
+ // see FIXME 1
+ if (containerDirection == LTR) {
+ marginLeftValue = 0;
+ marginRightValue = availableSpace; // will be negative
+ } else {
+ marginLeftValue = availableSpace; // will be negative
+ marginRightValue = 0;
+ }
+ }
+ } else if (marginLeft.isVariable()) {
+ // Solve for left margin
+ marginRightValue = marginRight.width(containerWidth);
+ marginLeftValue = availableSpace - marginRightValue;
+ } else if (marginRight.isVariable()) {
+ // Solve for right margin
+ marginLeftValue = marginLeft.width(containerWidth);
+ marginRightValue = availableSpace - marginLeftValue;
+ } else {
+ // Over-constrained, solve for left if direction is RTL
+ marginLeftValue = marginLeft.width(containerWidth);
+ marginRightValue = marginRight.width(containerWidth);
+
+ // see FIXME 1 -- used to be "this->style()->direction()"
+ if (containerDirection == RTL)
+ leftValue = (availableSpace + leftValue) - marginLeftValue - marginRightValue;
+ }
+ } else {
+ /*--------------------------------------------------------------------*\
+ * Otherwise, set 'auto' values for 'margin-left' and 'margin-right'
+ * to 0, and pick the one of the following six rules that applies.
+ *
+ * 1. 'left' and 'width' are 'auto' and 'right' is not 'auto', then the
+ * width is shrink-to-fit. Then solve for 'left'
+ *
+ * OMIT RULE 2 AS IT SHOULD NEVER BE HIT
+ * ------------------------------------------------------------------
+ * 2. 'left' and 'right' are 'auto' and 'width' is not 'auto', then if
+ * the 'direction' property of the containing block is 'ltr' set
+ * 'left' to the static position, otherwise set 'right' to the
+ * static position. Then solve for 'left' (if 'direction is 'rtl')
+ * or 'right' (if 'direction' is 'ltr').
+ * ------------------------------------------------------------------
+ *
+ * 3. 'width' and 'right' are 'auto' and 'left' is not 'auto', then the
+ * width is shrink-to-fit . Then solve for 'right'
+ * 4. 'left' is 'auto', 'width' and 'right' are not 'auto', then solve
+ * for 'left'
+ * 5. 'width' is 'auto', 'left' and 'right' are not 'auto', then solve
+ * for 'width'
+ * 6. 'right' is 'auto', 'left' and 'width' are not 'auto', then solve
+ * for 'right'
+ *
+ * Calculation of the shrink-to-fit width is similar to calculating the
+ * width of a table cell using the automatic table layout algorithm.
+ * Roughly: calculate the preferred width by formatting the content
+ * without breaking lines other than where explicit line breaks occur,
+ * and also calculate the preferred minimum width, e.g., by trying all
+ * possible line breaks. CSS 2.1 does not define the exact algorithm.
+ * Thirdly, calculate the available width: this is found by solving
+ * for 'width' after setting 'left' (in case 1) or 'right' (in case 3)
+ * to 0.
+ *
+ * Then the shrink-to-fit width is:
+ * kMin(kMax(preferred minimum width, available width), preferred width).
+ \*--------------------------------------------------------------------*/
+ // NOTE: For rules 3 and 6 it is not necessary to solve for 'right'
+ // because the value is not used for any further calculations.
+
+ // Calculate margins, 'auto' margins are ignored.
+ marginLeftValue = marginLeft.minWidth(containerWidth);
+ marginRightValue = marginRight.minWidth(containerWidth);
+
+ const int availableSpace = containerWidth - (marginLeftValue + marginRightValue + bordersPlusPadding);
+
+ // FIXME: Is there a faster way to find the correct case?
+ // Use rule/case that applies.
+ if (leftIsAuto && widthIsAuto && !rightIsAuto) {
+ // RULE 1: (use shrink-to-fit for width, and solve of left)
+ int rightValue = right.width(containerWidth);
+
+ // FIXME: would it be better to have shrink-to-fit in one step?
+ int preferredWidth = m_maxWidth - bordersPlusPadding;
+ int preferredMinWidth = m_minWidth - bordersPlusPadding;
+ int availableWidth = availableSpace - rightValue;
+ widthValue = kMin(kMax(preferredMinWidth, availableWidth), preferredWidth);
+ leftValue = availableSpace - (widthValue + rightValue);
+ } else if (!leftIsAuto && widthIsAuto && rightIsAuto) {
+ // RULE 3: (use shrink-to-fit for width, and no need solve of right)
+ leftValue = left.width(containerWidth);
+
+ // FIXME: would it be better to have shrink-to-fit in one step?
+ int preferredWidth = m_maxWidth - bordersPlusPadding;
+ int preferredMinWidth = m_minWidth - bordersPlusPadding;
+ int availableWidth = availableSpace - leftValue;
+ widthValue = kMin(kMax(preferredMinWidth, availableWidth), preferredWidth);
+ } else if (leftIsAuto && !width.isVariable() && !rightIsAuto) {
+ // RULE 4: (solve for left)
+ widthValue = calcContentWidth(width.width(containerWidth));
+ leftValue = availableSpace - (widthValue + right.width(containerWidth));
+ } else if (!leftIsAuto && widthIsAuto && !rightIsAuto) {
+ // RULE 5: (solve for width)
+ leftValue = left.width(containerWidth);
+ widthValue = availableSpace - (leftValue + right.width(containerWidth));
+ } else if (!leftIsAuto&& !widthIsAuto && rightIsAuto) {
+ // RULE 6: (no need solve for right)
+ leftValue = left.width(containerWidth);
+ widthValue = calcContentWidth(width.width(containerWidth));
+ }
+ }
+
+ // Use computed values to calculate the horizontal position.
+ xPos = leftValue + marginLeftValue + containerBlock->borderLeft();
+}
+
+
+void RenderBox::calcAbsoluteVertical()
+{
+ if (isReplaced()) {
+ calcAbsoluteVerticalReplaced();
+ return;
+ }
+
+ // The following is based off of the W3C Working Draft from April 11, 2006 of
+ // CSS 2.1: Section 10.6.4 "Absolutely positioned, non-replaced elements"
+ // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-non-replaced-height>
+ // (block-style-comments in this function and in calcAbsoluteVerticalValues()
+ // correspond to text from the spec)
+
+
+ // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline.
+ const RenderObject* containerBlock = container();
+ const int containerHeight = containerBlock->height() - containerBlock->borderTop() - containerBlock->borderBottom();
+
+ const int bordersPlusPadding = borderTop() + borderBottom() + paddingTop() + paddingBottom();
+ const Length marginTop = style()->marginTop();
+ const Length marginBottom = style()->marginBottom();
+ Length top = style()->top();
+ Length bottom = style()->bottom();
+
+ /*---------------------------------------------------------------------------*\
+ * For the purposes of this section and the next, the term "static position"
+ * (of an element) refers, roughly, to the position an element would have had
+ * in the normal flow. More precisely, the static position for 'top' is the
+ * distance from the top edge of the containing block to the top margin edge
+ * of a hypothetical box that would have been the first box of the element if
+ * its 'position' property had been 'static' and 'float' had been 'none'. The
+ * value is negative if the hypothetical box is above the containing block.
+ *
+ * But rather than actually calculating the dimensions of that hypothetical
+ * box, user agents are free to make a guess at its probable position.
+ *
+ * For the purposes of calculating the static position, the containing block
+ * of fixed positioned elements is the initial containing block instead of
+ * the viewport.
+ \*---------------------------------------------------------------------------*/
+
+ // Calculate the static distance if needed.
+ if (top.isVariable() && bottom.isVariable()) {
+ // m_staticY should already have been set through layout of the parent()
+ int staticTop = m_staticY - containerBlock->borderTop();
+ for (RenderObject* po = parent(); po && po != containerBlock; po = po->parent()) {
+ staticTop += po->yPos();
+ }
+ top.setValue(Fixed, staticTop);
+ }
+
+
+ int height; // Needed to compute overflow.
+
+ // Calculate constraint equation values for 'height' case.
+ calcAbsoluteVerticalValues(style()->height(), containerBlock, containerHeight, bordersPlusPadding,
+ top, bottom, marginTop, marginBottom,
+ height, m_marginTop, m_marginBottom, m_y);
+
+ // Avoid doing any work in the common case (where the values of min-height and max-height are their defaults).
+ // see FIXME 2
+
+ // Calculate constraint equation values for 'max-height' case.
+ if (style()->maxHeight().value() != UNDEFINED) {
+ int maxHeight;
+ short maxMarginTop;
+ short maxMarginBottom;
+ int maxYPos;
+
+ calcAbsoluteVerticalValues(style()->maxHeight(), containerBlock, containerHeight, bordersPlusPadding,
+ top, bottom, marginTop, marginBottom,
+ maxHeight, maxMarginTop, maxMarginBottom, maxYPos);
+
+ if (height > maxHeight) {
+ height = maxHeight;
+ m_marginTop = maxMarginTop;
+ m_marginBottom = maxMarginBottom;
+ m_y = maxYPos;
+ }
+ }
+
+ // Calculate constraint equation values for 'min-height' case.
+ if (style()->minHeight().value()) {
+ int minHeight;
+ short minMarginTop;
+ short minMarginBottom;
+ int minYPos;
+
+ calcAbsoluteVerticalValues(style()->minHeight(), containerBlock, containerHeight, bordersPlusPadding,
+ top, bottom, marginTop, marginBottom,
+ minHeight, minMarginTop, minMarginBottom, minYPos);
+
+ if (height < minHeight) {
+ height = minHeight;
+ m_marginTop = minMarginTop;
+ m_marginBottom = minMarginBottom;
+ m_y = minYPos;
+ }
+ }
+
+ height += bordersPlusPadding;
+
+ // Set final height value.
+ m_height = height;
+}
+
+void RenderBox::calcAbsoluteVerticalValues(Length height, const RenderObject* containerBlock,
+ const int containerHeight, const int bordersPlusPadding,
+ const Length top, const Length bottom, const Length marginTop, const Length marginBottom,
+ int& heightValue, short& marginTopValue, short& marginBottomValue, int& yPos)
+{
+ // 'top' and 'bottom' cannot both be 'auto' because 'top would of been
+ // converted to the static position in calcAbsoluteVertical()
+ assert(!(top.isVariable() && bottom.isVariable()));
+
+ int contentHeight = m_height - bordersPlusPadding;
+
+ int topValue = 0;
+
+ bool heightIsAuto = height.isVariable();
+ bool topIsAuto = top.isVariable();
+ bool bottomIsAuto = bottom.isVariable();
+
+ if (isTable() && heightIsAuto) {
+ // Height is never unsolved for tables. "auto" means shrink to fit.
+ // Use our height instead.
+ heightValue = contentHeight;
+ heightIsAuto = false;
+ } else if (!heightIsAuto) {
+ heightValue = calcContentHeight(height.width(containerHeight));
+ if (contentHeight > heightValue) {
+ if (!isTable())
+ contentHeight = heightValue;
+ else
+ heightValue = contentHeight;
+ }
+ }
+
+
+ if (!topIsAuto && !heightIsAuto && !bottomIsAuto) {
+ /*-----------------------------------------------------------------------*\
+ * If none of the three are 'auto': If both 'margin-top' and 'margin-
+ * bottom' are 'auto', solve the equation under the extra constraint that
+ * the two margins get equal values. If one of 'margin-top' or 'margin-
+ * bottom' is 'auto', solve the equation for that value. If the values
+ * are over-constrained, ignore the value for 'bottom' and solve for that
+ * value.
+ \*-----------------------------------------------------------------------*/
+ // NOTE: It is not necessary to solve for 'bottom' in the over constrained
+ // case because the value is not used for any further calculations.
+
+ topValue = top.width(containerHeight);
+
+ const int availableSpace = containerHeight - (topValue + heightValue + bottom.width(containerHeight) + bordersPlusPadding);
+
+ // Margins are now the only unknown
+ if (marginTop.isVariable() && marginBottom.isVariable()) {
+ // Both margins auto, solve for equality
+ // NOTE: This may result in negative values.
+ marginTopValue = availableSpace / 2; // split the diference
+ marginBottomValue = availableSpace - marginTopValue; // account for odd valued differences
+ } else if (marginTop.isVariable()) {
+ // Solve for top margin
+ marginBottomValue = marginBottom.width(containerHeight);
+ marginTopValue = availableSpace - marginBottomValue;
+ } else if (marginBottom.isVariable()) {
+ // Solve for bottom margin
+ marginTopValue = marginTop.width(containerHeight);
+ marginBottomValue = availableSpace - marginTopValue;
+ } else {
+ // Over-constrained, (no need solve for bottom)
+ marginTopValue = marginTop.width(containerHeight);
+ marginBottomValue = marginBottom.width(containerHeight);
+ }
+ } else {
+ /*--------------------------------------------------------------------*\
+ * Otherwise, set 'auto' values for 'margin-top' and 'margin-bottom'
+ * to 0, and pick the one of the following six rules that applies.
+ *
+ * 1. 'top' and 'height' are 'auto' and 'bottom' is not 'auto', then
+ * the height is based on the content, and solve for 'top'.
+ *
+ * OMIT RULE 2 AS IT SHOULD NEVER BE HIT
+ * ------------------------------------------------------------------
+ * 2. 'top' and 'bottom' are 'auto' and 'height' is not 'auto', then
+ * set 'top' to the static position, and solve for 'bottom'.
+ * ------------------------------------------------------------------
+ *
+ * 3. 'height' and 'bottom' are 'auto' and 'top' is not 'auto', then
+ * the height is based on the content, and solve for 'bottom'.
+ * 4. 'top' is 'auto', 'height' and 'bottom' are not 'auto', and
+ * solve for 'top'.
+ * 5. 'height' is 'auto', 'top' and 'bottom' are not 'auto', and
+ * solve for 'height'.
+ * 6. 'bottom' is 'auto', 'top' and 'height' are not 'auto', and
+ * solve for 'bottom'.
+ \*--------------------------------------------------------------------*/
+ // NOTE: For rules 3 and 6 it is not necessary to solve for 'bottom'
+ // because the value is not used for any further calculations.
+
+ // Calculate margins, 'auto' margins are ignored.
+ marginTopValue = marginTop.minWidth(containerHeight);
+ marginBottomValue = marginBottom.minWidth(containerHeight);
+
+ const int availableSpace = containerHeight - (marginTopValue + marginBottomValue + bordersPlusPadding);
+
+ // Use rule/case that applies.
+ if (topIsAuto && heightIsAuto && !bottomIsAuto) {
+ // RULE 1: (height is content based, solve of top)
+ heightValue = contentHeight;
+ topValue = availableSpace - (heightValue + bottom.width(containerHeight));
+ }
+ else if (topIsAuto && !heightIsAuto && bottomIsAuto) {
+ // RULE 2: (shouldn't happen)
+ }
+ else if (!topIsAuto && heightIsAuto && bottomIsAuto) {
+ // RULE 3: (height is content based, no need solve of bottom)
+ heightValue = contentHeight;
+ topValue = top.width(containerHeight);
+ } else if (topIsAuto && !heightIsAuto && !bottomIsAuto) {
+ // RULE 4: (solve of top)
+ topValue = availableSpace - (heightValue + bottom.width(containerHeight));
+ } else if (!topIsAuto && heightIsAuto && !bottomIsAuto) {
+ // RULE 5: (solve of height)
+ topValue = top.width(containerHeight);
+ heightValue = kMax(0, availableSpace - (topValue + bottom.width(containerHeight)));
+ } else if (!topIsAuto && !heightIsAuto && bottomIsAuto) {
+ // RULE 6: (no need solve of bottom)
+ topValue = top.width(containerHeight);
+ }
+ }
+
+ // Use computed values to calculate the vertical position.
+ yPos = topValue + marginTopValue + containerBlock->borderTop();
+}
+
+void RenderBox::calcAbsoluteHorizontalReplaced()
+{
+ // The following is based off of the W3C Working Draft from April 11, 2006 of
+ // CSS 2.1: Section 10.3.8 "Absolutly positioned, replaced elements"
+ // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-width>
+ // (block-style-comments in this function correspond to text from the spec and
+ // the numbers correspond to numbers in spec)
+
+ // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline.
+ const RenderObject* containerBlock = container();
+
+ // FIXME: This is incorrect for cases where the container block is a relatively
+ // positioned inline.
+ const int containerWidth = containingBlockWidth() + containerBlock->paddingLeft() + containerBlock->paddingRight();
+
+ // To match WinIE, in quirks mode use the parent's 'direction' property
+ // instead of the the container block's.
+ EDirection containerDirection = (style()->htmlHacks()) ? parent()->style()->direction() : containerBlock->style()->direction();
+
+ // Variables to solve.
+ Length left = style()->left();
+ Length right = style()->right();
+ Length marginLeft = style()->marginLeft();
+ Length marginRight = style()->marginRight();
+
+
+ /*-----------------------------------------------------------------------*\
+ * 1. The used value of 'width' is determined as for inline replaced
+ * elements.
+ \*-----------------------------------------------------------------------*/
+ // NOTE: This value of width is FINAL in that the min/max width calculations
+ // are dealt with in calcReplacedWidth(). This means that the steps to produce
+ // correct max/min in the non-replaced version, are not necessary.
+ m_width = calcReplacedWidth() + borderLeft() + borderRight() + paddingLeft() + paddingRight();
+ const int availableSpace = containerWidth - m_width;
+
+ /*-----------------------------------------------------------------------*\
+ * 2. If both 'left' and 'right' have the value 'auto', then if 'direction'
+ * of the containing block is 'ltr', set 'left' to the static position;
+ * else if 'direction' is 'rtl', set 'right' to the static position.
+ \*-----------------------------------------------------------------------*/
+ if (left.isVariable() && right.isVariable()) {
+ // see FIXME 1
+ if (containerDirection == LTR) {
+ // 'm_staticX' should already have been set through layout of the parent.
+ int staticPosition = m_staticX - containerBlock->borderLeft();
+ for (RenderObject* po = parent(); po && po != containerBlock; po = po->parent())
+ staticPosition += po->xPos();
+ left.setValue(Fixed, staticPosition);
+ } else {
+ RenderObject* po = parent();
+ // 'm_staticX' should already have been set through layout of the parent.
+ int staticPosition = m_staticX + containerWidth + containerBlock->borderRight() - po->width();
+ for (; po && po != containerBlock; po = po->parent())
+ staticPosition -= po->xPos();
+ right.setValue(Fixed, staticPosition);
+ }
+ }
+
+ /*-----------------------------------------------------------------------*\
+ * 3. If 'left' or 'right' are 'auto', replace any 'auto' on 'margin-left'
+ * or 'margin-right' with '0'.
+ \*-----------------------------------------------------------------------*/
+ if (left.isVariable() || right.isVariable()) {
+ if (marginLeft.isVariable())
+ marginLeft.setValue(Fixed, 0);
+ if (marginRight.isVariable())
+ marginRight.setValue(Fixed, 0);
+ }
+
+ /*-----------------------------------------------------------------------*\
+ * 4. If at this point both 'margin-left' and 'margin-right' are still
+ * 'auto', solve the equation under the extra constraint that the two
+ * margins must get equal values, unless this would make them negative,
+ * in which case when the direction of the containing block is 'ltr'
+ * ('rtl'), set 'margin-left' ('margin-right') to zero and solve for
+ * 'margin-right' ('margin-left').
+ \*-----------------------------------------------------------------------*/
+ int leftValue = 0;
+ int rightValue = 0;
+
+ if (marginLeft.isVariable() && marginRight.isVariable()) {
+ // 'left' and 'right' cannot be 'auto' due to step 3
+ assert(!(left.isVariable() && right.isVariable()));
+
+ leftValue = left.width(containerWidth);
+ rightValue = right.width(containerWidth);
+
+ int difference = availableSpace - (leftValue + rightValue);
+ if (difference > 0) {
+ m_marginLeft = difference / 2; // split the diference
+ m_marginRight = difference - m_marginLeft; // account for odd valued differences
+ } else {
+ // see FIXME 1
+ if (containerDirection == LTR) {
+ m_marginLeft = 0;
+ m_marginRight = difference; // will be negative
+ } else {
+ m_marginLeft = difference; // will be negative
+ m_marginRight = 0;
+ }
+ }
+
+ /*-----------------------------------------------------------------------*\
+ * 5. If at this point there is an 'auto' left, solve the equation for
+ * that value.
+ \*-----------------------------------------------------------------------*/
+ } else if (left.isVariable()) {
+ m_marginLeft = marginLeft.width(containerWidth);
+ m_marginRight = marginRight.width(containerWidth);
+ rightValue = right.width(containerWidth);
+
+ // Solve for 'left'
+ leftValue = availableSpace - (rightValue + m_marginLeft + m_marginRight);
+ } else if (right.isVariable()) {
+ m_marginLeft = marginLeft.width(containerWidth);
+ m_marginRight = marginRight.width(containerWidth);
+ leftValue = left.width(containerWidth);
+
+ // Solve for 'right'
+ rightValue = availableSpace - (leftValue + m_marginLeft + m_marginRight);
+ } else if (marginLeft.isVariable()) {
+ m_marginRight = marginRight.width(containerWidth);
+ leftValue = left.width(containerWidth);
+ rightValue = right.width(containerWidth);
+
+ // Solve for 'margin-left'
+ m_marginLeft = availableSpace - (leftValue + rightValue + m_marginRight);
+ } else if (marginRight.isVariable()) {
+ m_marginLeft = marginLeft.width(containerWidth);
+ leftValue = left.width(containerWidth);
+ rightValue = right.width(containerWidth);
+
+ // Solve for 'margin-right'
+ m_marginRight = availableSpace - (leftValue + rightValue + m_marginLeft);
+ }
+
+ /*-----------------------------------------------------------------------*\
+ * 6. If at this point the values are over-constrained, ignore the value
+ * for either 'left' (in case the 'direction' property of the
+ * containing block is 'rtl') or 'right' (in case 'direction' is
+ * 'ltr') and solve for that value.
+ \*-----------------------------------------------------------------------*/
+ else {
+ m_marginLeft = marginLeft.width(containerWidth);
+ m_marginRight = marginRight.width(containerWidth);
+ if (containerDirection == LTR) {
+ leftValue = left.width(containerWidth);
+ rightValue = availableSpace - (leftValue + m_marginLeft + m_marginRight);
+ }
+ else {
+ rightValue = right.width(containerWidth);
+ leftValue = availableSpace - (rightValue + m_marginLeft + m_marginRight);
+ }
+ }
+
+ int totalWidth = m_width + leftValue + rightValue + m_marginLeft + m_marginRight;
+ if (totalWidth > containerWidth && (containerDirection == RTL))
+ leftValue = containerWidth - (totalWidth - leftValue);
+
+ // Use computed values to calculate the horizontal position.
+ m_x = leftValue + m_marginLeft + containerBlock->borderLeft();
+}
+
+void RenderBox::calcAbsoluteVerticalReplaced()
+{
+ // The following is based off of the W3C Working Draft from April 11, 2006 of
+ // CSS 2.1: Section 10.6.5 "Absolutly positioned, replaced elements"
+ // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-height>
+ // (block-style-comments in this function correspond to text from the spec and
+ // the numbers correspond to numbers in spec)
+
+ // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline.
+ const RenderObject* containerBlock = container();
+ const int containerHeight = containerBlock->height() - containerBlock->borderTop() - containerBlock->borderBottom();
+
+ // Variables to solve.
+ Length top = style()->top();
+ Length bottom = style()->bottom();
+ Length marginTop = style()->marginTop();
+ Length marginBottom = style()->marginBottom();
+
+
+ /*-----------------------------------------------------------------------*\
+ * 1. The used value of 'height' is determined as for inline replaced
+ * elements.
+ \*-----------------------------------------------------------------------*/
+ // NOTE: This value of height is FINAL in that the min/max height calculations
+ // are dealt with in calcReplacedHeight(). This means that the steps to produce
+ // correct max/min in the non-replaced version, are not necessary.
+ m_height = calcReplacedHeight() + borderTop() + borderBottom() + paddingTop() + paddingBottom();
+ const int availableSpace = containerHeight - m_height;
+
+ /*-----------------------------------------------------------------------*\
+ * 2. If both 'top' and 'bottom' have the value 'auto', replace 'top'
+ * with the element's static position.
+ \*-----------------------------------------------------------------------*/
+ if (top.isVariable() && bottom.isVariable()) {
+ // m_staticY should already have been set through layout of the parent().
+ int staticTop = m_staticY - containerBlock->borderTop();
+ for (RenderObject* po = parent(); po && po != containerBlock; po = po->parent()) {
+ staticTop += po->yPos();
+ }
+ top.setValue(Fixed, staticTop);
+ }
+
+ /*-----------------------------------------------------------------------*\
+ * 3. If 'bottom' is 'auto', replace any 'auto' on 'margin-top' or
+ * 'margin-bottom' with '0'.
+ \*-----------------------------------------------------------------------*/
+ // FIXME: The spec. says that this step should only be taken when bottom is
+ // auto, but if only top is auto, this makes step 4 impossible.
+ if (top.isVariable() || bottom.isVariable()) {
+ if (marginTop.isVariable())
+ marginTop.setValue(Fixed, 0);
+ if (marginBottom.isVariable())
+ marginBottom.setValue(Fixed, 0);
+ }
+
+ /*-----------------------------------------------------------------------*\
+ * 4. If at this point both 'margin-top' and 'margin-bottom' are still
+ * 'auto', solve the equation under the extra constraint that the two
+ * margins must get equal values.
+ \*-----------------------------------------------------------------------*/
+ int topValue = 0;
+ int bottomValue = 0;
+
+ if (marginTop.isVariable() && marginBottom.isVariable()) {
+ // 'top' and 'bottom' cannot be 'auto' due to step 2 and 3 combinded.
+ assert(!(top.isVariable() || bottom.isVariable()));
+
+ topValue = top.width(containerHeight);
+ bottomValue = bottom.width(containerHeight);
+
+ int difference = availableSpace - (topValue + bottomValue);
+ // NOTE: This may result in negative values.
+ m_marginTop = difference / 2; // split the difference
+ m_marginBottom = difference - m_marginTop; // account for odd valued differences
+
+ /*-----------------------------------------------------------------------*\
+ * 5. If at this point there is only one 'auto' left, solve the equation
+ * for that value.
+ \*-----------------------------------------------------------------------*/
+ } else if (top.isVariable()) {
+ m_marginTop = marginTop.width(containerHeight);
+ m_marginBottom = marginBottom.width(containerHeight);
+ bottomValue = bottom.width(containerHeight);
+
+ // Solve for 'top'
+ topValue = availableSpace - (bottomValue + m_marginTop + m_marginBottom);
+ } else if (bottom.isVariable()) {
+ m_marginTop = marginTop.width(containerHeight);
+ m_marginBottom = marginBottom.width(containerHeight);
+ topValue = top.width(containerHeight);
+
+ // Solve for 'bottom'
+ // NOTE: It is not necessary to solve for 'bottom' because we don't ever
+ // use the value.
+ } else if (marginTop.isVariable()) {
+ m_marginBottom = marginBottom.width(containerHeight);
+ topValue = top.width(containerHeight);
+ bottomValue = bottom.width(containerHeight);
+
+ // Solve for 'margin-top'
+ m_marginTop = availableSpace - (topValue + bottomValue + m_marginBottom);
+ } else if (marginBottom.isVariable()) {
+ m_marginTop = marginTop.width(containerHeight);
+ topValue = top.width(containerHeight);
+ bottomValue = bottom.width(containerHeight);
+
+ // Solve for 'margin-bottom'
+ m_marginBottom = availableSpace - (topValue + bottomValue + m_marginTop);
+ }
+
+ /*-----------------------------------------------------------------------*\
+ * 6. If at this point the values are over-constrained, ignore the value
+ * for 'bottom' and solve for that value.
+ \*-----------------------------------------------------------------------*/
+ else {
+ m_marginTop = marginTop.width(containerHeight);
+ m_marginBottom = marginBottom.width(containerHeight);
+ topValue = top.width(containerHeight);
+
+ // Solve for 'bottom'
+ // NOTE: It is not necessary to solve for 'bottom' because we don't ever
+ // use the value.
+ }
+
+ // Use computed values to calculate the vertical position.
+ m_y = topValue + m_marginTop + containerBlock->borderTop();
+}
+
+int RenderBox::highestPosition(bool /*includeOverflowInterior*/, bool includeSelf) const
+{
+ return includeSelf ? 0 : m_height;
+}
+
+int RenderBox::lowestPosition(bool /*includeOverflowInterior*/, bool includeSelf) const
+{
+ return includeSelf ? m_height : 0;
+}
+
+int RenderBox::rightmostPosition(bool /*includeOverflowInterior*/, bool includeSelf) const
+{
+ return includeSelf ? m_width : 0;
+}
+
+int RenderBox::leftmostPosition(bool /*includeOverflowInterior*/, bool includeSelf) const
+{
+ return includeSelf ? 0 : m_width;
+}
+
+int RenderBox::pageTopAfter(int y) const
+{
+ RenderObject* cb = container();
+ if (cb)
+ return cb->pageTopAfter(y+yPos()) - yPos();
+ else
+ return 0;
+}
+
+int RenderBox::crossesPageBreak(int t, int b) const
+{
+ RenderObject* cb = container();
+ if (cb)
+ return cb->crossesPageBreak(yPos()+t, yPos()+b);
+ else
+ return false;
+}
+
+void RenderBox::caretPos(int /*offset*/, int flags, int &_x, int &_y, int &width, int &height)
+{
+#if 0
+ _x = -1;
+
+ // propagate it downwards to its children, someone will feel responsible
+ RenderObject *child = firstChild();
+// if (child) kdDebug(6040) << "delegating caretPos to " << child->renderName() << endl;
+ if (child) child->caretPos(offset, override, _x, _y, width, height);
+
+ // if not, use the extents of this box. offset 0 means left, offset 1 means
+ // right
+ if (_x == -1) {
+ //kdDebug(6040) << "no delegation" << endl;
+ _x = xPos() + (offset == 0 ? 0 : m_width);
+ _y = yPos();
+ height = m_height;
+ width = override && offset == 0 ? m_width : 1;
+
+ // If height of box is smaller than font height, use the latter one,
+ // otherwise the caret might become invisible.
+ // FIXME: ignoring :first-line, missing good reason to take care of
+ int fontHeight = style()->fontMetrics().height();
+ if (fontHeight > height)
+ height = fontHeight;
+
+ int absx, absy;
+
+ RenderObject *cb = containingBlock();
+
+ if (cb && cb != this && cb->absolutePosition(absx,absy)) {
+ //kdDebug(6040) << "absx=" << absx << " absy=" << absy << endl;
+ _x += absx;
+ _y += absy;
+ } else {
+ // we don't know our absolute position, and there is no point returning
+ // just a relative one
+ _x = _y = -1;
+ }
+ }
+#endif
+
+ _x = xPos();
+ _y = yPos();
+// kdDebug(6040) << "_x " << _x << " _y " << _y << endl;
+ width = 1; // no override is indicated in boxes
+
+ RenderBlock *cb = containingBlock();
+
+ // Place caret outside the border
+ if (flags & CFOutside) {
+
+ RenderStyle *s = element() && element()->parent()
+ && element()->parent()->renderer()
+ ? element()->parent()->renderer()->style()
+ : cb->style();
+
+ const QFontMetrics &fm = s->fontMetrics();
+ height = fm.height();
+
+ bool rtl = s->direction() == RTL;
+ bool outsideEnd = flags & CFOutsideEnd;
+
+ if (outsideEnd) {
+ _x += this->width();
+ } else {
+ _x--;
+ }
+
+ int hl = fm.leading() / 2;
+ if (!isReplaced() || style()->display() == BLOCK) {
+ if (!outsideEnd ^ rtl)
+ _y -= hl;
+ else
+ _y += kMax(this->height() - fm.ascent() - hl, 0);
+ } else {
+ _y += baselinePosition(false) - fm.ascent() - hl;
+ }
+
+ // Place caret inside the element
+ } else {
+ const QFontMetrics &fm = style()->fontMetrics();
+ height = fm.height();
+
+ RenderStyle *s = style();
+
+ _x += borderLeft() + paddingLeft();
+ _y += borderTop() + paddingTop();
+
+ // ### regard direction
+ switch (s->textAlign()) {
+ case LEFT:
+ case KHTML_LEFT:
+ case TAAUTO: // ### find out what this does
+ case JUSTIFY:
+ break;
+ case CENTER:
+ case KHTML_CENTER:
+ _x += contentWidth() / 2;
+ break;
+ case KHTML_RIGHT:
+ case RIGHT:
+ _x += contentWidth();
+ break;
+ }
+ }
+
+ int absx, absy;
+ if (cb && cb != this && cb->absolutePosition(absx,absy)) {
+// kdDebug(6040) << "absx=" << absx << " absy=" << absy << endl;
+ _x += absx;
+ _y += absy;
+ } else {
+ // we don't know our absolute position, and there is no point returning
+ // just a relative one
+ _x = _y = -1;
+ }
+}
+
+#undef DEBUG_LAYOUT
diff --git a/khtml/rendering/render_box.h b/khtml/rendering/render_box.h
new file mode 100644
index 000000000..ad0a5b9fb
--- /dev/null
+++ b/khtml/rendering/render_box.h
@@ -0,0 +1,213 @@
+/*
+ * This file is part of the DOM implementation for KDE.
+ *
+ * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * (C) 2002-2003 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.
+ *
+ */
+#ifndef RENDER_BOX_H
+#define RENDER_BOX_H
+
+#include "render_container.h"
+
+namespace khtml {
+
+enum WidthType { Width, MinWidth, MaxWidth };
+enum HeightType { Height, MinHeight, MaxHeight };
+
+class RenderBlock;
+
+class RenderBox : public RenderContainer
+{
+
+
+// combines ElemImpl & PosElImpl (all rendering objects are positioned)
+// should contain all border and padding handling
+
+public:
+ RenderBox(DOM::NodeImpl* node);
+ virtual ~RenderBox();
+
+ virtual const char *renderName() const { return "RenderBox"; }
+ virtual bool isBox() const { return true; }
+
+ virtual void setStyle(RenderStyle *style);
+ virtual void paint(PaintInfo& i, int _tx, int _ty);
+
+ virtual void close();
+
+ virtual InlineBox* createInlineBox(bool makePlaceHolderBox, bool isRootLineBox);
+ virtual void deleteInlineBoxes(RenderArena* arena=0);
+
+ virtual void detach();
+
+ virtual short minWidth() const { return m_minWidth; }
+ virtual int maxWidth() const { return m_maxWidth; }
+
+ virtual short contentWidth() const;
+ virtual int contentHeight() const;
+
+ virtual bool absolutePosition(int &xPos, int &yPos, bool f = false) const;
+
+ virtual void setPos( int xPos, int yPos );
+
+ virtual int xPos() const { return m_x; }
+ virtual int yPos() const { return m_y; }
+ virtual short width() const;
+ virtual int height() const;
+
+ virtual short marginTop() const { return m_marginTop; }
+ virtual short marginBottom() const { return m_marginBottom; }
+ virtual short marginLeft() const { return m_marginLeft; }
+ virtual short marginRight() const { return m_marginRight; }
+
+ virtual void setWidth( int width );
+ virtual void setHeight( int height );
+
+ virtual void position(InlineBox* box, int from, int len, bool reverse);
+
+ virtual int highestPosition(bool includeOverflowInterior=true, bool includeSelf=true) const;
+ virtual int lowestPosition(bool includeOverflowInterior=true, bool includeSelf=true) const;
+ virtual int rightmostPosition(bool includeOverflowInterior=true, bool includeSelf=true) const;
+ virtual int leftmostPosition(bool includeOverflowInterior=true, bool includeSelf=true) const;
+
+ virtual void repaint(Priority p=NormalPriority);
+
+ virtual void repaintRectangle(int x, int y, int w, int h, Priority p=NormalPriority, bool f=false);
+
+ virtual short containingBlockWidth() const;
+ void relativePositionOffset(int &tx, int &ty) const;
+
+ virtual void calcWidth();
+ virtual void calcHeight();
+
+ virtual short calcReplacedWidth() const;
+ virtual int calcReplacedHeight() const;
+
+ virtual int availableHeight() const;
+ virtual int availableWidth() const;
+
+ void calcVerticalMargins();
+
+ virtual RenderLayer* layer() const { return m_layer; }
+
+ void setStaticX(short staticX);
+ void setStaticY(int staticY);
+ int staticX() const { return m_staticX; }
+ int staticY() const { return m_staticY; }
+
+ virtual void caretPos(int offset, int flags, int &_x, int &_y, int &width, int &height);
+
+ void calcHorizontalMargins(const Length& ml, const Length& mr, int cw);
+ RenderBlock* createAnonymousBlock();
+
+ virtual int pageTopAfter(int y) const;
+ virtual int crossesPageBreak(int t, int b) const;
+
+ int calcBoxWidth(int w) const;
+ int calcBoxHeight(int h) const;
+ int calcContentWidth(int w) const;
+ int calcContentHeight(int h) const;
+
+protected:
+ int calcWidthUsing(WidthType widthType, int cw, LengthType& lengthType);
+ int calcHeightUsing(const Length& height);
+ int calcReplacedWidthUsing(WidthType widthType) const;
+ int calcReplacedHeightUsing(HeightType heightType) const;
+ int calcPercentageHeight(const Length& height, bool treatAsReplaced = false) const;
+ int availableHeightUsing(const Length& h) const;
+ int availableWidthUsing(const Length& w) const;
+ int calcImplicitHeight() const;
+ bool hasImplicitHeight() const {
+ return isPositioned() && !style()->top().isVariable() && !style()->bottom().isVariable();
+ }
+
+protected:
+ virtual void paintBoxDecorations(PaintInfo& paintInfo, int _tx, int _ty);
+ void paintRootBoxDecorations( PaintInfo& paintInfo, int _tx, int _ty);
+
+ void paintBackgrounds(QPainter *p, const QColor& c, const BackgroundLayer* bgLayer, int clipy, int cliph, int _tx, int _ty, int w, int h);
+ void paintBackground(QPainter *p, const QColor& c, const BackgroundLayer* bgLayer, int clipy, int cliph, int _tx, int _ty, int w, int h);
+
+ virtual void paintBackgroundExtended(QPainter* /*p*/, const QColor& /*c*/, const BackgroundLayer* /*bgLayer*/,
+ int /*clipy*/, int /*cliph*/, int /*_tx*/, int /*_ty*/,
+ int /*w*/, int /*height*/, int /*bleft*/, int /*bright*/, int /*pleft*/, int /*pright*/ );
+
+ void outlineBox(QPainter *p, int _tx, int _ty, const char *color = "red");
+
+ void calcAbsoluteHorizontal();
+ void calcAbsoluteVertical();
+ void calcAbsoluteHorizontalValues(Length width, const RenderObject* cb, EDirection containerDirection,
+ const int containerWidth, const int bordersPlusPadding,
+ const Length left, const Length right, const Length marginLeft, const Length marginRight,
+ short& widthValue, short& marginLeftValue, short& marginRightValue, short& xPos);
+ void calcAbsoluteVerticalValues(Length height, const RenderObject* cb,
+ const int containerHeight, const int bordersPlusPadding,
+ const Length top, const Length bottom, const Length marginTop, const Length marginBottom,
+ int& heightValue, short& marginTopValue, short& marginBottomValue, int& yPos);
+
+ void calcAbsoluteVerticalReplaced();
+ void calcAbsoluteHorizontalReplaced();
+
+ QRect getOverflowClipRect(int tx, int ty);
+ QRect getClipRect(int tx, int ty);
+
+ void restructureParentFlow();
+
+
+ // the actual height of the contents + borders + padding (border-box)
+ int m_height;
+ int m_y;
+
+ short m_width;
+ short m_x;
+
+ short m_marginTop;
+ short m_marginBottom;
+
+ short m_marginLeft;
+ short m_marginRight;
+
+ /*
+ * the minimum width the element needs, to be able to render
+ * its content without clipping
+ */
+ short m_minWidth;
+ /* The maximum width the element can fill horizontally
+ * ( = the width of the element with line breaking disabled)
+ */
+ int m_maxWidth;
+
+ // Cached normal flow values for absolute positioned elements with static left/top values.
+ short m_staticX;
+ int m_staticY;
+
+ RenderLayer *m_layer;
+
+ /* A box used to represent this object on a line
+ * when its inner content isn't contextually relevant
+ * (e.g replaced or positioned elements)
+ */
+ InlineBox *m_placeHolderBox;
+};
+
+
+} //namespace
+
+#endif
diff --git a/khtml/rendering/render_br.cpp b/khtml/rendering/render_br.cpp
new file mode 100644
index 000000000..42709839a
--- /dev/null
+++ b/khtml/rendering/render_br.cpp
@@ -0,0 +1,79 @@
+/**
+ * This file is part of the DOM implementation for KDE.
+ *
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+#include "render_br.h"
+
+using namespace khtml;
+
+
+RenderBR::RenderBR(DOM::NodeImpl* node)
+ : RenderText(node, new DOM::DOMStringImpl(QChar('\n')))
+{
+ m_hasReturn = true;
+}
+
+RenderBR::~RenderBR()
+{
+}
+
+#if 0
+void RenderBR::caretPos(int offset, int flags, int &_x, int &_y, int &width, int &height)
+{
+ RenderText::caretPos(offset,flags,_x,_y,width,height);
+ return;
+#if 0
+ if (previousSibling() && !previousSibling()->isBR() && !previousSibling()->isFloating()) {
+ int offset = 0;
+ if (previousSibling()->isText())
+ offset = static_cast<RenderText*>(previousSibling())->maxOffset();
+
+ // FIXME: this won't return a big width in override mode (LS)
+ previousSibling()->caretPos(offset,override,_x,_y,width,height);
+ return;
+ }
+
+ int absx, absy;
+ absolutePosition(absx,absy);
+ if (absx == -1) {
+ // we don't know out absolute position, and there is no point returning
+ // just a relative one
+ _x = _y = -1;
+ }
+ else {
+ _x += absx;
+ _y += absy;
+ }
+ height = RenderText::verticalPositionHint( false );
+ width = override ? height / 2 : 1;
+#endif
+}
+#endif
+
+FindSelectionResult RenderBR::checkSelectionPoint(int _x, int _y, int _tx, int _ty, DOM::NodeImpl*& node, int &offset, SelPointState &state)
+{
+ // Simply take result of previous one
+ RenderText *prev = static_cast<RenderText *>(previousSibling());
+ if (!prev || !prev->isText() || !prev->inlineTextBoxCount() || prev->isBR())
+ prev = this;
+
+ //kdDebug(6040) << "delegated to " << prev->renderName() << "@" << prev << endl;
+ return prev->RenderText::checkSelectionPoint(_x, _y, _tx, _ty, node, offset, state);
+}
diff --git a/khtml/rendering/render_br.h b/khtml/rendering/render_br.h
new file mode 100644
index 000000000..f4175015f
--- /dev/null
+++ b/khtml/rendering/render_br.h
@@ -0,0 +1,77 @@
+/*
+ * This file is part of the DOM implementation for KDE.
+ *
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef RENDER_BR_H
+#define RENDER_BR_H
+
+#include "render_text.h"
+
+/*
+ * The whole class here is a hack to get <br> working, as long as we don't have support for
+ * CSS2 :before and :after pseudo elements
+ */
+namespace khtml {
+
+class RenderBR : public RenderText
+{
+public:
+ RenderBR(DOM::NodeImpl* node);
+ virtual ~RenderBR();
+
+ virtual const char *renderName() const { return "RenderBR"; }
+
+ virtual void paint( PaintInfo&, int, int) {}
+
+ virtual unsigned int width(unsigned int, unsigned int, const Font *) const { return 0; }
+ virtual unsigned int width( unsigned int, unsigned int, bool) const { return 0; }
+ virtual short width() const { return RenderText::width(); }
+
+ virtual int height() const { return 0; }
+
+ // overrides
+ virtual void calcMinMaxWidth() {}
+ virtual short minWidth() const { return 0; }
+ virtual int maxWidth() const { return 0; }
+
+ virtual FindSelectionResult checkSelectionPoint( int _x, int _y, int _tx, int _ty,
+ DOM::NodeImpl*& node, int & offset,
+ SelPointState & );
+
+ virtual bool isBR() const { return true; }
+#if 0
+ virtual void caretPos(int offset, int flags, int &_x, int &_y, int &width, int &height);
+#endif
+ /** returns the lowest possible value the caret offset may have to
+ * still point to a valid position.
+ *
+ * Returns 0.
+ */
+ virtual long minOffset() const { return 0; }
+ /** returns the highest possible value the caret offset may have to
+ * still point to a valid position.
+ *
+ * Returns also 0, as BRs have no width.
+ */
+ virtual long maxOffset() const { return 0; }
+};
+
+}
+#endif
diff --git a/khtml/rendering/render_canvas.cpp b/khtml/rendering/render_canvas.cpp
new file mode 100644
index 000000000..50cad914a
--- /dev/null
+++ b/khtml/rendering/render_canvas.cpp
@@ -0,0 +1,780 @@
+/**
+ * This file is part of the HTML widget for KDE.
+ *
+ * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
+ * (C) 2003 Apple Computer, Inc.
+ * (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com)
+ *
+ * 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 "rendering/render_canvas.h"
+#include "rendering/render_layer.h"
+#include "xml/dom_docimpl.h"
+
+#include "khtmlview.h"
+#include "khtml_part.h"
+#include <kdebug.h>
+#include <kglobal.h>
+
+using namespace khtml;
+
+//#define BOX_DEBUG
+//#define SPEED_DEBUG
+
+RenderCanvas::RenderCanvas(DOM::NodeImpl* node, KHTMLView *view)
+ : RenderBlock(node)
+{
+ // init RenderObject attributes
+ setInline(false);
+ setIsAnonymous(false);
+
+ m_view = view;
+ // try to contrain the width to the views width
+
+ m_minWidth = 0;
+ m_height = 0;
+
+ m_width = m_minWidth;
+ m_maxWidth = m_minWidth;
+
+ m_rootWidth = m_rootHeight = 0;
+ m_viewportWidth = m_viewportHeight = 0;
+ m_cachedDocWidth = m_cachedDocHeight = -1;
+
+ setPositioned(true); // to 0,0 :)
+
+ m_staticMode = false;
+ m_pagedMode = false;
+ m_printImages = true;
+
+ m_pageTop = 0;
+ m_pageBottom = 0;
+
+ m_page = 0;
+
+ m_maximalOutlineSize = 0;
+
+ m_selectionStart = 0;
+ m_selectionEnd = 0;
+ m_selectionStartPos = -1;
+ m_selectionEndPos = -1;
+
+ m_needsWidgetMasks = false;
+
+ // Create a new root layer for our layer hierarchy.
+ m_layer = new (node->getDocument()->renderArena()) RenderLayer(this);
+}
+
+RenderCanvas::~RenderCanvas()
+{
+ delete m_page;
+}
+
+void RenderCanvas::setStyle(RenderStyle* style)
+{
+ /*
+ if (m_pagedMode)
+ style->setOverflow(OHIDDEN); */
+ RenderBlock::setStyle(style);
+}
+
+void RenderCanvas::calcHeight()
+{
+ if (m_pagedMode || !m_view)
+ m_height = m_rootHeight;
+ else
+ m_height = m_view->visibleHeight();
+}
+
+void RenderCanvas::calcWidth()
+{
+ // the width gets set by KHTMLView::print when printing to a printer.
+ if(m_pagedMode || !m_view)
+ {
+ m_width = m_rootWidth;
+ return;
+ }
+
+ m_width = m_view ? m_view->frameWidth() : m_minWidth;
+
+ if (style()->marginLeft().isFixed())
+ m_marginLeft = style()->marginLeft().value();
+ else
+ m_marginLeft = 0;
+
+ if (style()->marginRight().isFixed())
+ m_marginRight = style()->marginRight().value();
+ else
+ m_marginRight = 0;
+}
+
+void RenderCanvas::calcMinMaxWidth()
+{
+ KHTMLAssert( !minMaxKnown() );
+
+ RenderBlock::calcMinMaxWidth();
+
+ m_maxWidth = m_minWidth;
+
+ setMinMaxKnown();
+}
+
+//#define SPEED_DEBUG
+
+void RenderCanvas::layout()
+{
+ if (m_pagedMode) {
+ m_minWidth = m_width;
+// m_maxWidth = m_width;
+ }
+
+ m_needsFullRepaint = markedForRepaint() || !view() || view()->needsFullRepaint() || m_pagedMode;
+
+ setChildNeedsLayout(true);
+ setMinMaxKnown(false);
+ for(RenderObject* c = firstChild(); c; c = c->nextSibling())
+ c->setChildNeedsLayout(true);
+
+ int oldWidth = m_width;
+ int oldHeight = m_height;
+
+ m_cachedDocWidth = m_cachedDocHeight = -1;
+
+ if (m_pagedMode || !m_view) {
+ m_width = m_rootWidth;
+ m_height = m_rootHeight;
+ }
+ else
+ {
+ m_viewportWidth = m_width = m_view->visibleWidth();
+ m_viewportHeight = m_height = m_view->visibleHeight();
+ }
+
+#ifdef SPEED_DEBUG
+ QTime qt;
+ qt.start();
+#endif
+
+ if ( recalcMinMax() )
+ recalcMinMaxWidths();
+
+#ifdef SPEED_DEBUG
+ kdDebug() << "RenderCanvas::calcMinMax time used=" << qt.elapsed() << endl;
+ qt.start();
+#endif
+
+ bool relayoutChildren = (oldWidth != m_width) || (oldHeight != m_height);
+
+ RenderBlock::layoutBlock( relayoutChildren );
+
+#ifdef SPEED_DEBUG
+ kdDebug() << "RenderCanvas::layout time used=" << qt.elapsed() << endl;
+ qt.start();
+#endif
+
+ updateDocumentSize();
+
+ layer()->updateLayerPositions( layer(), needsFullRepaint(), true );
+
+ if (!m_pagedMode && m_needsWidgetMasks)
+ layer()->updateWidgetMasks(layer());
+
+ scheduleDeferredRepaints();
+ setNeedsLayout(false);
+
+#ifdef SPEED_DEBUG
+ kdDebug() << "RenderCanvas::end time used=" << qt.elapsed() << endl;
+#endif
+}
+
+void RenderCanvas::updateDocumentSize()
+{
+ // update our cached document size
+ int hDocH = m_cachedDocHeight = docHeight();
+ int hDocW = m_cachedDocWidth = docWidth();
+
+ if (!m_pagedMode && m_view) {
+
+ bool vss = m_view->verticalScrollBar()->isShown();
+ bool hss = m_view->horizontalScrollBar()->isShown();
+ QSize s = m_view->viewportSize(m_cachedDocWidth, m_cachedDocHeight);
+
+ // if we are about to show a scrollbar, and the document is sized to the viewport w or h,
+ // then reserve the scrollbar space so that it doesn't trigger the _other_ scrollbar
+
+ if (!vss && m_width - m_view->verticalScrollBar()->sizeHint().width() == s.width() &&
+ m_cachedDocWidth <= m_width)
+ hDocW = kMin( m_cachedDocWidth, s.width() );
+
+ if (!hss && m_height - m_view->horizontalScrollBar()->sizeHint().height() == s.height() &&
+ m_cachedDocHeight <= m_height)
+ hDocH = kMin( m_cachedDocHeight, s.height() );
+
+ // likewise, if a scrollbar is shown, and we have a cunning plan to turn it off,
+ // think again if we are falling downright in the hysteresis zone
+
+ if (vss && s.width() > m_cachedDocWidth && m_cachedDocWidth > m_view->visibleWidth())
+ hDocW = s.width()+1;
+
+ if (hss && s.height() > m_cachedDocHeight && m_cachedDocHeight > m_view->visibleHeight())
+ hDocH = s.height()+1;
+
+ m_view->resizeContents(hDocW, hDocH);
+
+ setWidth( m_viewportWidth = s.width() );
+ setHeight( m_viewportHeight = s.height() );
+ }
+ layer()->resize( kMax( m_cachedDocWidth,int( m_width ) ), kMax( m_cachedDocHeight,m_height ) );
+}
+
+void RenderCanvas::updateDocSizeAfterLayerTranslation( RenderObject* o, bool posXOffset, bool posYOffset )
+{
+ if (needsLayout())
+ return;
+ int rightmost, lowest;
+ o->absolutePosition( rightmost, lowest );
+ if (posXOffset) {
+ rightmost += o->rightmostPosition(false, true);
+ setCachedDocWidth( kMax(docWidth(), rightmost) );
+ } else {
+ setCachedDocWidth( -1 );
+ }
+ if (posYOffset) {
+ lowest += o->lowestPosition(false, true);
+ setCachedDocHeight( kMax(docHeight(), lowest) );
+ } else {
+ setCachedDocHeight( -1 );
+ }
+// kdDebug() << " posXOffset: " << posXOffset << " posYOffset " << posYOffset << " m_cachedDocWidth " << m_cachedDocWidth << " m_cachedDocHeight " << m_cachedDocHeight << endl;
+ updateDocumentSize();
+}
+
+bool RenderCanvas::needsFullRepaint() const
+{
+ return m_needsFullRepaint || m_pagedMode;
+}
+
+void RenderCanvas::repaintViewRectangle(int x, int y, int w, int h, bool asap)
+{
+ KHTMLAssert( view() );
+ view()->scheduleRepaint( x, y, w, h, asap );
+}
+
+bool RenderCanvas::absolutePosition(int &xPos, int &yPos, bool f) const
+{
+ if ( f && m_pagedMode) {
+ xPos = 0;
+ yPos = m_pageTop;
+ }
+ else if ( f && m_view) {
+ xPos = m_view->contentsX();
+ yPos = m_view->contentsY();
+ }
+ else {
+ xPos = yPos = 0;
+ }
+ return true;
+}
+
+void RenderCanvas::paint(PaintInfo& paintInfo, int _tx, int _ty)
+{
+#ifdef DEBUG_LAYOUT
+ kdDebug( 6040 ) << renderName() << this << " ::paintObject() w/h = (" << width() << "/" << height() << ")" << endl;
+#endif
+
+ // 1. paint background, borders etc
+ if(paintInfo.phase == PaintActionElementBackground) {
+ paintBoxDecorations(paintInfo, _tx, _ty);
+ return;
+ }
+
+ // 2. paint contents
+ for( RenderObject *child = firstChild(); child; child=child->nextSibling())
+ if(!child->layer() && !child->isFloating())
+ child->paint(paintInfo, _tx, _ty);
+
+ // 3. paint floats.
+ if (paintInfo.phase == PaintActionFloat)
+ paintFloats(paintInfo, _tx, _ty);
+
+#ifdef BOX_DEBUG
+ if (m_view)
+ {
+ _tx += m_view->contentsX();
+ _ty += m_view->contentsY();
+ }
+
+ outlineBox(p, _tx, _ty);
+#endif
+
+}
+
+void RenderCanvas::paintBoxDecorations(PaintInfo& paintInfo, int /*_tx*/, int /*_ty*/)
+{
+ if ((firstChild() && firstChild()->style()->visibility() == VISIBLE) || !view())
+ return;
+
+ paintInfo.p->fillRect(paintInfo.r, view()->palette().active().color(QColorGroup::Base));
+}
+
+void RenderCanvas::repaintRectangle(int x, int y, int w, int h, Priority p, bool f)
+{
+ if (m_staticMode) return;
+// kdDebug( 6040 ) << "updating views contents (" << x << "/" << y << ") (" << w << "/" << h << ")" << endl;
+
+ if (f && m_pagedMode) {
+ y += m_pageTop;
+ } else
+ if ( f && m_view ) {
+ x += m_view->contentsX();
+ y += m_view->contentsY();
+ }
+
+ QRect vr = viewRect();
+ QRect ur(x, y, w, h);
+
+ if (m_view && ur.intersects(vr)) {
+
+ if (p == RealtimePriority)
+ // ### KWQ's updateContents has an additional parameter "now".
+ // It's not clear what the difference between updateContents(...,true)
+ // and repaintContents(...) is. As Qt doesn't have this, I'm leaving it out. (LS)
+ m_view->updateContents(ur/*, true*/);
+ else if (p == HighPriority)
+ m_view->scheduleRepaint(x, y, w, h, true /*asap*/);
+ else
+ m_view->scheduleRepaint(x, y, w, h);
+ }
+}
+
+void RenderCanvas::deferredRepaint( RenderObject* o )
+{
+ m_dirtyChildren.append( o );
+}
+
+void RenderCanvas::scheduleDeferredRepaints()
+{
+ if (!needsFullRepaint()) {
+ QValueList<RenderObject*>::const_iterator it;
+ for ( it = m_dirtyChildren.begin(); it != m_dirtyChildren.end(); ++it )
+ (*it)->repaint();
+ }
+ //kdDebug(6040) << "scheduled deferred repaints: " << m_dirtyChildren.count() << " needed full repaint: " << needsFullRepaint() << endl;
+ m_dirtyChildren.clear();
+}
+
+void RenderCanvas::repaint(Priority p)
+{
+ if (m_view && !m_staticMode) {
+ if (p == RealtimePriority) {
+ //m_view->resizeContents(docWidth(), docHeight());
+ m_view->unscheduleRepaint();
+ if (needsLayout()) {
+ m_view->scheduleRelayout();
+ return;
+ }
+ // ### same as in repaintRectangle
+ m_view->updateContents(m_view->contentsX(), m_view->contentsY(),
+ m_view->visibleWidth(), m_view->visibleHeight()/*, true*/);
+ }
+ else if (p == HighPriority)
+ m_view->scheduleRepaint(m_view->contentsX(), m_view->contentsY(),
+ m_view->visibleWidth(), m_view->visibleHeight(), true /*asap*/);
+ else
+ m_view->scheduleRepaint(m_view->contentsX(), m_view->contentsY(),
+ m_view->visibleWidth(), m_view->visibleHeight());
+ }
+}
+
+static QRect enclosingPositionedRect (RenderObject *n)
+{
+ RenderObject *enclosingParent = n->containingBlock();
+ QRect rect(0,0,0,0);
+ if (enclosingParent) {
+ int ox, oy;
+ enclosingParent->absolutePosition(ox, oy);
+ int off = 0;
+ if (!enclosingParent->hasOverflowClip()) {
+ ox += enclosingParent->overflowLeft();
+ oy += enclosingParent->overflowTop();
+ }
+ rect.setX(ox);
+ rect.setY(oy);
+ rect.setWidth(enclosingParent->effectiveWidth());
+ rect.setHeight(enclosingParent->effectiveHeight());
+ }
+ return rect;
+}
+
+QRect RenderCanvas::selectionRect() const
+{
+ RenderObject *r = m_selectionStart;
+ if (!r)
+ return QRect();
+
+ QRect selectionRect = enclosingPositionedRect(r);
+
+ while (r && r != m_selectionEnd)
+ {
+ RenderObject* n;
+ if ( !(n = r->firstChild()) ){
+ if ( !(n = r->nextSibling()) )
+ {
+ n = r->parent();
+ while (n && !n->nextSibling())
+ n = n->parent();
+ if (n)
+ n = n->nextSibling();
+ }
+ }
+ r = n;
+ if (r) {
+ selectionRect = selectionRect.unite(enclosingPositionedRect(r));
+ }
+ }
+
+ return selectionRect;
+}
+
+void RenderCanvas::setSelection(RenderObject *s, int sp, RenderObject *e, int ep)
+{
+ // Check we got valid renderobjects. www.msnbc.com and clicking
+ // around, to find the case where this happened.
+ if ( !s || !e )
+ {
+ kdWarning(6040) << "RenderCanvas::setSelection() called with start=" << s << " end=" << e << endl;
+ return;
+ }
+// kdDebug( 6040 ) << "RenderCanvas::setSelection(" << s << "," << sp << "," << e << "," << ep << ")" << endl;
+
+ bool changedSelectionBorder = ( s != m_selectionStart || e != m_selectionEnd );
+
+ // Cut out early if the selection hasn't changed.
+ if ( !changedSelectionBorder && m_selectionStartPos == sp && m_selectionEndPos == ep )
+ return;
+
+ // Record the old selected objects. Will be used later
+ // to delta against the selected objects.
+
+ RenderObject *oldStart = m_selectionStart;
+ int oldStartPos = m_selectionStartPos;
+ RenderObject *oldEnd = m_selectionEnd;
+ int oldEndPos = m_selectionEndPos;
+ QPtrList<RenderObject> oldSelectedInside;
+ QPtrList<RenderObject> newSelectedInside;
+ RenderObject *os = oldStart;
+
+ while (os && os != oldEnd)
+ {
+ RenderObject* no;
+ if ( !(no = os->firstChild()) ){
+ if ( !(no = os->nextSibling()) )
+ {
+ no = os->parent();
+ while (no && !no->nextSibling())
+ no = no->parent();
+ if (no)
+ no = no->nextSibling();
+ }
+ }
+ if (os->selectionState() == SelectionInside && !oldSelectedInside.containsRef(os))
+ oldSelectedInside.append(os);
+
+ os = no;
+ }
+ if (changedSelectionBorder)
+ clearSelection(false);
+
+ while (s->firstChild())
+ s = s->firstChild();
+ while (e->lastChild())
+ e = e->lastChild();
+
+#if 0
+ bool changedSelectionBorder = ( s != m_selectionStart || e != m_selectionEnd );
+
+ if ( !changedSelectionBorder && m_selectionStartPos == sp && m_selectionEndPos = ep )
+ return;
+#endif
+
+ // set selection start
+ if (m_selectionStart)
+ m_selectionStart->setIsSelectionBorder(false);
+ m_selectionStart = s;
+ if (m_selectionStart)
+ m_selectionStart->setIsSelectionBorder(true);
+ m_selectionStartPos = sp;
+
+ // set selection end
+ if (m_selectionEnd)
+ m_selectionEnd->setIsSelectionBorder(false);
+ m_selectionEnd = e;
+ if (m_selectionEnd)
+ m_selectionEnd->setIsSelectionBorder(true);
+ m_selectionEndPos = ep;
+
+#if 0
+ kdDebug( 6040 ) << "old selection (" << oldStart << "," << oldStartPos << "," << oldEnd << "," << oldEndPos << ")" << endl;
+ kdDebug( 6040 ) << "new selection (" << s << "," << sp << "," << e << "," << ep << ")" << endl;
+#endif
+
+ // update selection status of all objects between m_selectionStart and m_selectionEnd
+ RenderObject* o = s;
+
+ while (o && o!=e)
+ {
+ o->setSelectionState(SelectionInside);
+// kdDebug( 6040 ) << "setting selected " << o << ", " << o->isText() << endl;
+ RenderObject* no;
+ if ( !(no = o->firstChild()) )
+ if ( !(no = o->nextSibling()) )
+ {
+ no = o->parent();
+ while (no && !no->nextSibling())
+ no = no->parent();
+ if (no)
+ no = no->nextSibling();
+ }
+ if (o->selectionState() == SelectionInside && !newSelectedInside.containsRef(o))
+ newSelectedInside.append(o);
+
+ o=no;
+ }
+ s->setSelectionState(SelectionStart);
+ e->setSelectionState(SelectionEnd);
+ if(s == e) s->setSelectionState(SelectionBoth);
+
+ if (!m_view)
+ return;
+
+ newSelectedInside.removeRef(s);
+ newSelectedInside.removeRef(e);
+
+ QRect updateRect;
+
+ // Don't use repaint() because it will cause all rects to
+ // be united (see khtmlview::scheduleRepaint()). Instead
+ // just draw damage rects for objects that have a change
+ // in selection state.
+ // ### for Qt, updateContents will unite them, too. This has to be
+ // circumvented somehow (LS)
+
+ // Are any of the old fully selected objects not in the new selection?
+ // If so we have to draw them.
+ // Could be faster by building list of non-intersecting rectangles rather
+ // than unioning rectangles.
+ QPtrListIterator<RenderObject> oldIterator(oldSelectedInside);
+ bool firstRect = true;
+ for (; oldIterator.current(); ++oldIterator){
+ if (!newSelectedInside.containsRef(oldIterator.current())){
+ if (firstRect){
+ updateRect = enclosingPositionedRect(oldIterator.current());
+ firstRect = false;
+ }
+ else
+ updateRect = updateRect.unite(enclosingPositionedRect(oldIterator.current()));
+ }
+ }
+ if (!firstRect){
+ m_view->updateContents( updateRect );
+ }
+
+ // Are any of the new fully selected objects not in the previous selection?
+ // If so we have to draw them.
+ // Could be faster by building list of non-intersecting rectangles rather
+ // than unioning rectangles.
+ QPtrListIterator<RenderObject> newIterator(newSelectedInside);
+ firstRect = true;
+ for (; newIterator.current(); ++newIterator){
+ if (!oldSelectedInside.containsRef(newIterator.current())){
+ if (firstRect){
+ updateRect = enclosingPositionedRect(newIterator.current());
+ firstRect = false;
+ }
+ else
+ updateRect = updateRect.unite(enclosingPositionedRect(newIterator.current()));
+ }
+ }
+ if (!firstRect) {
+ m_view->updateContents( updateRect );
+ }
+
+ // Is the new starting object different, or did the position in the starting
+ // element change? If so we have to draw it.
+ if (oldStart != m_selectionStart ||
+ (oldStart == oldEnd && (oldStartPos != m_selectionStartPos || oldEndPos != m_selectionEndPos)) ||
+ (oldStart == m_selectionStart && oldStartPos != m_selectionStartPos)){
+ m_view->updateContents( enclosingPositionedRect(m_selectionStart) );
+ }
+
+ // Draw the old selection start object if it's different than the new selection
+ // start object.
+ if (oldStart && oldStart != m_selectionStart){
+ m_view->updateContents( enclosingPositionedRect(oldStart) );
+ }
+
+ // Does the selection span objects and is the new end object different, or did the position
+ // in the end element change? If so we have to draw it.
+ if (/*(oldStart != oldEnd || !oldEnd) &&*/
+ (oldEnd != m_selectionEnd ||
+ (oldEnd == m_selectionEnd && oldEndPos != m_selectionEndPos))){
+ m_view->updateContents( enclosingPositionedRect(m_selectionEnd) );
+ }
+
+ // Draw the old selection end object if it's different than the new selection
+ // end object.
+ if (oldEnd && oldEnd != m_selectionEnd){
+ m_view->updateContents( enclosingPositionedRect(oldEnd) );
+ }
+}
+
+void RenderCanvas::clearSelection(bool doRepaint)
+{
+ // update selection status of all objects between m_selectionStart and m_selectionEnd
+ RenderObject* o = m_selectionStart;
+ while (o && o!=m_selectionEnd)
+ {
+ if (o->selectionState()!=SelectionNone)
+ if (doRepaint)
+ o->repaint();
+ o->setSelectionState(SelectionNone);
+ o->repaint();
+ RenderObject* no;
+ if ( !(no = o->firstChild()) )
+ if ( !(no = o->nextSibling()) )
+ {
+ no = o->parent();
+ while (no && !no->nextSibling())
+ no = no->parent();
+ if (no)
+ no = no->nextSibling();
+ }
+ o=no;
+ }
+ if (m_selectionEnd) {
+ m_selectionEnd->setSelectionState(SelectionNone);
+ if (doRepaint)
+ m_selectionEnd->repaint();
+ }
+
+ // set selection start & end to 0
+ if (m_selectionStart)
+ m_selectionStart->setIsSelectionBorder(false);
+ m_selectionStart = 0;
+ m_selectionStartPos = -1;
+
+ if (m_selectionEnd)
+ m_selectionEnd->setIsSelectionBorder(false);
+ m_selectionEnd = 0;
+ m_selectionEndPos = -1;
+}
+
+void RenderCanvas::selectionStartEnd(int& spos, int& epos)
+{
+ spos = m_selectionStartPos;
+ epos = m_selectionEndPos;
+}
+
+QRect RenderCanvas::viewRect() const
+{
+ if (m_pagedMode)
+ if (m_pageTop == m_pageBottom) {
+ kdDebug(6040) << "viewRect: " << QRect(0, m_pageTop, m_width, m_height) << endl;
+ return QRect(0, m_pageTop, m_width, m_height);
+ }
+ else {
+ kdDebug(6040) << "viewRect: " << QRect(0, m_pageTop, m_width, m_pageBottom - m_pageTop) << endl;
+ return QRect(0, m_pageTop, m_width, m_pageBottom - m_pageTop);
+ }
+ else if (m_view)
+ return QRect(m_view->contentsX(),
+ m_view->contentsY(),
+ m_view->visibleWidth(),
+ m_view->visibleHeight());
+ else
+ return QRect(0,0,m_rootWidth,m_rootHeight);
+}
+
+int RenderCanvas::docHeight() const
+{
+ if (m_cachedDocHeight != -1)
+ return m_cachedDocHeight;
+
+ int h;
+ if (m_pagedMode || !m_view)
+ h = m_height;
+ else
+ h = 0;
+
+ RenderObject *fc = firstChild();
+ if(fc) {
+ int dh = fc->overflowHeight() + fc->marginTop() + fc->marginBottom();
+ int lowestPos = fc->lowestPosition(false);
+// kdDebug(6040) << "h " << h << " lowestPos " << lowestPos << " dh " << dh << " fc->rh " << fc->effectiveHeight() << " fc->height() " << fc->height() << endl;
+ if( lowestPos > dh )
+ dh = lowestPos;
+ lowestPos = lowestAbsolutePosition();
+ if( lowestPos > dh )
+ dh = lowestPos;
+ if( dh > h )
+ h = dh;
+ }
+
+ RenderLayer *layer = m_layer;
+ h = kMax( h, layer->yPos() + layer->height() );
+// kdDebug(6040) << "h " << h << " layer(" << layer->renderer()->renderName() << "@" << layer->renderer() << ")->height " << layer->height() << " lp " << (layer->yPos() + layer->height()) << " height() " << layer->renderer()->height() << " rh " << layer->renderer()->effectiveHeight() << endl;
+ return h;
+}
+
+int RenderCanvas::docWidth() const
+{
+ if (m_cachedDocWidth != -1)
+ return m_cachedDocWidth;
+
+ int w;
+ if (m_pagedMode || !m_view)
+ w = m_width;
+ else
+ w = 0;
+
+ RenderObject *fc = firstChild();
+ if(fc) {
+ // ow: like effectiveWidth() but without the negative
+ const int ow = fc->hasOverflowClip() ? fc->width() : fc->overflowWidth();
+ int dw = ow + fc->marginLeft() + fc->marginRight();
+ int rightmostPos = fc->rightmostPosition(false);
+// kdDebug(6040) << "w " << w << " rightmostPos " << rightmostPos << " dw " << dw << " fc->rw " << fc->effectiveWidth() << " fc->width() " << fc->width() << endl;
+ if( rightmostPos > dw )
+ dw = rightmostPos;
+ rightmostPos = rightmostAbsolutePosition();
+ if ( rightmostPos > dw )
+ dw = rightmostPos;
+ if( dw > w )
+ w = dw;
+ }
+
+ RenderLayer *layer = m_layer;
+ w = kMax( w, layer->xPos() + layer->width() );
+// kdDebug(6040) << "w " << w << " layer(" << layer->renderer()->renderName() << ")->width " << layer->width() << " rm " << (layer->xPos() + layer->width()) << " width() " << layer->renderer()->width() << " rw " << layer->renderer()->effectiveWidth() << endl;
+ return w;
+}
+
+RenderPage* RenderCanvas::page() {
+ if (!m_page) m_page = new RenderPage(this);
+ return m_page;
+}
diff --git a/khtml/rendering/render_canvas.h b/khtml/rendering/render_canvas.h
new file mode 100644
index 000000000..017bd02b2
--- /dev/null
+++ b/khtml/rendering/render_canvas.h
@@ -0,0 +1,250 @@
+/*
+ * This file is part of the HTML widget for KDE.
+ *
+ * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
+ * (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef render_canvas_h
+#define render_canvas_h
+
+#include "render_block.h"
+
+class KHTMLView;
+class QScrollView;
+
+namespace khtml {
+
+class RenderPage;
+class RenderStyle;
+
+enum CanvasMode {
+ CanvasViewPort, // Paints inside a viewport
+ CanvasPage, // Paints one page
+ CanvasDocument // Paints the whole document
+};
+
+class RenderCanvas : public RenderBlock
+{
+public:
+ RenderCanvas(DOM::NodeImpl* node, KHTMLView *view);
+ ~RenderCanvas();
+
+ virtual const char *renderName() const { return "RenderCanvas"; }
+
+ virtual bool isCanvas() const { return true; }
+
+ virtual void setStyle(RenderStyle *style);
+ virtual void layout();
+ virtual void calcWidth();
+ virtual void calcHeight();
+ virtual void calcMinMaxWidth();
+ virtual bool absolutePosition(int &xPos, int&yPos, bool f = false) const;
+
+ int docHeight() const;
+ int docWidth() const;
+
+ KHTMLView *view() const { return m_view; }
+
+ virtual void repaint(Priority p=NormalPriority);
+ virtual void repaintRectangle(int x, int y, int w, int h, Priority p=NormalPriority, bool f=false);
+ void repaintViewRectangle(int x, int y, int w, int h, bool asap=false);
+ bool needsFullRepaint() const;
+ void deferredRepaint( RenderObject* o );
+ void scheduleDeferredRepaints();
+
+ virtual void paint(PaintInfo&, int tx, int ty);
+ virtual void paintBoxDecorations(PaintInfo& paintInfo, int _tx, int _ty);
+ virtual void setSelection(RenderObject *s, int sp, RenderObject *e, int ep);
+ virtual void clearSelection(bool doRepaint=true);
+ virtual RenderObject *selectionStart() const { return m_selectionStart; }
+ virtual RenderObject *selectionEnd() const { return m_selectionEnd; }
+
+ void setPrintImages(bool enable) { m_printImages = enable; }
+ bool printImages() const { return m_printImages; }
+
+ void setCanvasMode(CanvasMode mode) { m_canvasMode = mode; }
+ CanvasMode canvasMode() const { return m_canvasMode; }
+
+ void setPagedMode(bool b) { m_pagedMode = b; }
+ void setStaticMode(bool b) { m_staticMode = b; }
+
+ bool pagedMode() const { return m_pagedMode; }
+ bool staticMode() const { return m_staticMode; }
+
+ void setPageTop(int top) {
+ m_pageTop = top;
+// m_y = top;
+ }
+ void setPageBottom(int bottom) { m_pageBottom = bottom; }
+ int pageTop() const { return m_pageTop; }
+ int pageBottom() const { return m_pageBottom; }
+
+ int pageTopAfter(int y) const {
+ if (pageHeight() == 0) return 0;
+ return (y / pageHeight() + 1) * pageHeight() ;
+ }
+
+ int crossesPageBreak(int top, int bottom) const {
+ if (pageHeight() == 0) return false;
+ int pT = top / pageHeight();
+ // bottom is actually the first line not in the box
+ int pB = (bottom-1) / pageHeight();
+ return (pT == pB) ? 0 : (pB + 1);
+ }
+
+ void setPageNumber(int number) { m_pageNr = number; }
+ int pageNumber() const { return m_pageNr; }
+
+public:
+ virtual void setWidth( int width ) { m_rootWidth = m_width = width; }
+ virtual void setHeight( int height ) { m_rootHeight = m_height = height; }
+
+// void setPageHeight( int height ) { m_viewportHeight = m_pageHeight = height; }
+ int pageHeight() const { return m_pageBottom - m_pageTop; }
+
+ int viewportWidth() const { return m_viewportWidth; }
+ int viewportHeight() const { return m_viewportHeight; }
+
+ RenderPage* page();
+
+ QRect selectionRect() const;
+
+ void setMaximalOutlineSize(int o) { m_maximalOutlineSize = o; }
+ int maximalOutlineSize() const { return m_maximalOutlineSize; }
+
+ void setNeedsWidgetMasks( bool b=true) { m_needsWidgetMasks = b; }
+ bool needsWidgetMasks() const { return m_needsWidgetMasks; }
+
+ void updateDocSizeAfterLayerTranslation( RenderObject* o, bool posXOffset, bool posYOffset );
+protected:
+ // makes sure document, scrollbars and viewport size are accurate
+ void updateDocumentSize();
+
+ // internal setters for cached values of document width/height
+ // Setting to -1/-1 invalidates the cache.
+ void setCachedDocWidth(int w ) { m_cachedDocWidth = w; }
+ void setCachedDocHeight(int h) { m_cachedDocHeight = h; }
+
+ virtual void selectionStartEnd(int& spos, int& epos);
+
+ virtual QRect viewRect() const;
+
+ KHTMLView *m_view;
+
+ RenderObject* m_selectionStart;
+ RenderObject* m_selectionEnd;
+ int m_selectionStartPos;
+ int m_selectionEndPos;
+
+ CanvasMode m_canvasMode;
+
+ int m_rootWidth;
+ int m_rootHeight;
+
+ int m_viewportWidth;
+ int m_viewportHeight;
+
+ int m_cachedDocWidth;
+ int m_cachedDocHeight;
+
+ bool m_printImages;
+ bool m_needsFullRepaint;
+
+ // Canvas is not interactive
+ bool m_staticMode;
+ // Canvas is paged
+ bool m_pagedMode;
+ // Canvas contains overlaid widgets
+ bool m_needsWidgetMasks;
+
+ short m_pageNr;
+
+ int m_pageTop;
+ int m_pageBottom;
+
+ RenderPage* m_page;
+
+ int m_maximalOutlineSize; // Used to apply a fudge factor to dirty-rect checks on blocks/tables.
+ QValueList<RenderObject*> m_dirtyChildren;
+};
+
+inline RenderCanvas* RenderObject::canvas() const
+{
+ return static_cast<RenderCanvas*>(document()->renderer());
+}
+
+// Represents the page-context of CSS
+class RenderPage
+{
+public:
+ RenderPage(RenderCanvas* canvas) : m_canvas(canvas),
+ m_marginTop(0), m_marginBottom(0),
+ m_marginLeft(0), m_marginRight(0),
+ m_pageWidth(0), m_pageHeight(0),
+ m_fixedSize(false)
+ {
+ m_style = new RenderPageStyle();
+ }
+ virtual ~RenderPage()
+ {
+ delete m_style;
+ }
+
+ int marginTop() const { return m_marginTop; }
+ int marginBottom() const { return m_marginBottom; }
+ int marginLeft() const { return m_marginLeft; }
+ int marginRight() const { return m_marginRight; }
+
+ void setMarginTop(int margin) { m_marginTop = margin; }
+ void setMarginBottom(int margin) { m_marginBottom = margin; }
+ void setMarginLeft(int margin) { m_marginLeft = margin; }
+ void setMarginRight(int margin) { m_marginRight = margin; }
+
+ int pageWidth() const { return m_pageWidth; }
+ int pageHeight() const { return m_pageHeight; }
+
+ void setPageSize(int width, int height) {
+ m_pageWidth = width;
+ m_pageHeight = height;
+ }
+
+ // Returns true if size was set by document, false if set by user-agent
+ bool fixedSize() const { return m_fixedSize; }
+ void setFixedSize(bool b) { m_fixedSize = b; }
+
+ RenderPageStyle* style() { return m_style; }
+ const RenderPageStyle* style() const { return m_style; }
+
+protected:
+ RenderCanvas* m_canvas;
+ RenderPageStyle* m_style;
+
+ int m_marginTop;
+ int m_marginBottom;
+ int m_marginLeft;
+ int m_marginRight;
+
+ int m_pageWidth;
+ int m_pageHeight;
+
+ bool m_fixedSize;
+};
+
+}
+#endif
diff --git a/khtml/rendering/render_container.cpp b/khtml/rendering/render_container.cpp
new file mode 100644
index 000000000..69f987477
--- /dev/null
+++ b/khtml/rendering/render_container.cpp
@@ -0,0 +1,597 @@
+/**
+ * This file is part of the html renderer for KDE.
+ *
+ * Copyright (C) 2001-2003 Lars Knoll (knoll@kde.org)
+ * (C) 2001 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000-2003 Dirk Mueller (mueller@kde.org)
+ * (C) 2002-2003 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.
+ *
+ */
+
+//#define DEBUG_LAYOUT
+
+#include "rendering/render_container.h"
+#include "rendering/render_table.h"
+#include "rendering/render_text.h"
+#include "rendering/render_image.h"
+#include "rendering/render_canvas.h"
+#include "rendering/render_generated.h"
+#include "rendering/render_inline.h"
+#include "xml/dom_docimpl.h"
+#include "css/css_valueimpl.h"
+
+#include <kdebug.h>
+#include <assert.h>
+
+using namespace khtml;
+
+RenderContainer::RenderContainer(DOM::NodeImpl* node)
+ : RenderObject(node)
+{
+ m_first = 0;
+ m_last = 0;
+}
+
+void RenderContainer::detach()
+{
+ if (continuation())
+ continuation()->detach();
+
+ // We simulate removeNode calls for all our children
+ // and set parent to 0 to avoid removeNode from being called.
+ // First call removeLayers and removeFromObjectLists since they assume
+ // a valid render-tree
+ for(RenderObject* n = m_first; n; n = n->nextSibling() ) {
+ n->removeLayers(enclosingLayer());
+ n->removeFromObjectLists();
+ }
+
+ RenderObject* next;
+ for(RenderObject* n = m_first; n; n = next ) {
+ n->setParent(0);
+ next = n->nextSibling();
+ n->detach();
+ }
+ m_first = 0;
+ m_last = 0;
+
+ RenderObject::detach();
+}
+
+void RenderContainer::addChild(RenderObject *newChild, RenderObject *beforeChild)
+{
+#ifdef DEBUG_LAYOUT
+ kdDebug( 6040 ) << this << ": " << renderName() << "(RenderObject)::addChild( " << newChild << ": " <<
+ newChild->renderName() << ", " << (beforeChild ? beforeChild->renderName() : "0") << " )" << endl;
+#endif
+ // protect ourselves from deletion
+ setDoNotDelete(true);
+
+ bool needsTable = false;
+
+ if(!newChild->isText() && !newChild->isReplaced()) {
+ switch(newChild->style()->display()) {
+ case INLINE:
+ case BLOCK:
+ case LIST_ITEM:
+ case RUN_IN:
+ case COMPACT:
+ case INLINE_BLOCK:
+ case TABLE:
+ case INLINE_TABLE:
+ break;
+ case TABLE_COLUMN:
+ if ( isTableCol() )
+ break;
+ // nobreak
+ case TABLE_COLUMN_GROUP:
+ case TABLE_CAPTION:
+ case TABLE_ROW_GROUP:
+ case TABLE_HEADER_GROUP:
+ case TABLE_FOOTER_GROUP:
+
+ //kdDebug( 6040 ) << "adding section" << endl;
+ if ( !isTable() )
+ needsTable = true;
+ break;
+ case TABLE_ROW:
+ //kdDebug( 6040 ) << "adding row" << endl;
+ if ( !isTableSection() )
+ needsTable = true;
+ break;
+ case TABLE_CELL:
+ //kdDebug( 6040 ) << "adding cell" << endl;
+ if ( !isTableRow() )
+ needsTable = true;
+ // I'm not 100% sure this is the best way to fix this, but without this
+ // change we recurse infinitely when trying to render the CSS2 test page:
+ // http://www.bath.ac.uk/%7Epy8ieh/internet/eviltests/htmlbodyheadrendering2.html.
+ if ( isTableCell() && !firstChild() && !newChild->isTableCell() )
+ needsTable = false;
+
+ break;
+ case NONE:
+ // RenderHtml and some others can have display:none
+ // KHTMLAssert(false);
+ break;
+ }
+ }
+
+ if ( needsTable ) {
+ RenderTable *table;
+ RenderObject *last = beforeChild ? beforeChild->previousSibling() : lastChild();
+ if ( last && last->isTable() && last->isAnonymous() ) {
+ table = static_cast<RenderTable *>(last);
+ } else {
+ //kdDebug( 6040 ) << "creating anonymous table, before=" << beforeChild << endl;
+ table = new (renderArena()) RenderTable(document() /* is anonymous */);
+ RenderStyle *newStyle = new RenderStyle();
+ newStyle->inheritFrom(style());
+ newStyle->setDisplay( TABLE );
+ newStyle->setFlowAroundFloats( true );
+ table->setParent( this ); // so it finds the arena
+ table->setStyle(newStyle);
+ table->setParent( 0 );
+ addChild(table, beforeChild);
+ }
+ table->addChild(newChild);
+ } else {
+ // just add it...
+ insertChildNode(newChild, beforeChild);
+ }
+
+ if (newChild->isText() && newChild->style()->textTransform() == CAPITALIZE) {
+ DOM::DOMStringImpl* textToTransform = static_cast<RenderText*>(newChild)->originalString();
+ if (textToTransform)
+ static_cast<RenderText*>(newChild)->setText(textToTransform, true);
+ }
+ newChild->attach();
+
+ setDoNotDelete(false);
+}
+
+RenderObject* RenderContainer::removeChildNode(RenderObject* oldChild)
+{
+ KHTMLAssert(oldChild->parent() == this);
+
+ // So that we'll get the appropriate dirty bit set (either that a normal flow child got yanked or
+ // that a positioned child got yanked). We also repaint, so that the area exposed when the child
+ // disappears gets repainted properly.
+ if ( document()->renderer() ) {
+ oldChild->setNeedsLayoutAndMinMaxRecalc();
+ oldChild->repaint();
+
+ // Keep our layer hierarchy updated.
+ oldChild->removeLayers(enclosingLayer());
+ // remove the child from any special layout lists
+ oldChild->removeFromObjectLists();
+
+ // if oldChild is the start or end of the selection, then clear
+ // the selection to avoid problems of invalid pointers
+
+ // ### This is not the "proper" solution... ideally the selection
+ // ### should be maintained based on DOM Nodes and a Range, which
+ // ### gets adjusted appropriately when nodes are deleted/inserted
+ // ### near etc. But this at least prevents crashes caused when
+ // ### the start or end of the selection is deleted and then
+ // ### accessed when the user next selects something.
+
+ if (oldChild->isSelectionBorder()) {
+ RenderObject *root = oldChild;
+ while (root->parent())
+ root = root->parent();
+ if (root->isCanvas()) {
+ static_cast<RenderCanvas*>(root)->clearSelection();
+ }
+ }
+ }
+
+ // remove the child from the render-tree
+ if (oldChild->previousSibling())
+ oldChild->previousSibling()->setNextSibling(oldChild->nextSibling());
+ if (oldChild->nextSibling())
+ oldChild->nextSibling()->setPreviousSibling(oldChild->previousSibling());
+
+ if (m_first == oldChild)
+ m_first = oldChild->nextSibling();
+ if (m_last == oldChild)
+ m_last = oldChild->previousSibling();
+
+ oldChild->setPreviousSibling(0);
+ oldChild->setNextSibling(0);
+ oldChild->setParent(0);
+
+ return oldChild;
+}
+
+void RenderContainer::setStyle(RenderStyle* _style)
+{
+ RenderObject::setStyle(_style);
+
+ // If we are a pseudo-container we need to restyle the children
+ if (style()->isGenerated())
+ {
+ // ### we could save this call when the change only affected
+ // non inherited properties
+ RenderStyle *pseudoStyle = new RenderStyle();
+ pseudoStyle->inheritFrom(style());
+ pseudoStyle->ref();
+ RenderObject *child = firstChild();
+ while (child != 0)
+ {
+ child->setStyle(pseudoStyle);
+ child = child->nextSibling();
+ }
+ pseudoStyle->deref();
+ }
+}
+
+void RenderContainer::updatePseudoChildren()
+{
+ // In CSS2, before/after pseudo-content cannot nest. Check this first.
+ // Remove when CSS 3 Generated Content becomes Candidate Recommendation
+ if (style()->styleType() == RenderStyle::BEFORE
+ || style()->styleType() == RenderStyle::AFTER)
+ return;
+
+ updatePseudoChild(RenderStyle::BEFORE);
+ updatePseudoChild(RenderStyle::AFTER);
+ // updatePseudoChild(RenderStyle::MARKER, marker());
+}
+
+void RenderContainer::updatePseudoChild(RenderStyle::PseudoId type)
+{
+ // The head manages generated content for its continuations
+ if (isInlineContinuation()) return;
+
+ RenderStyle* pseudo = style()->getPseudoStyle(type);
+
+ RenderObject* child = pseudoContainer(type);
+
+ // Whether or not we currently have generated content attached.
+ bool oldContentPresent = child && (child->style()->styleType() == type);
+
+ // Whether or not we now want generated content.
+ bool newContentWanted = pseudo && pseudo->display() != NONE;
+
+ // No generated content
+ if (!oldContentPresent && !newContentWanted)
+ return;
+
+ bool movedContent = (type == RenderStyle::AFTER && isRenderInline() && continuation());
+
+ // Whether or not we want the same old content.
+ bool sameOldContent = oldContentPresent && newContentWanted && !movedContent
+ && (child->style()->contentDataEquivalent(pseudo));
+
+ // No change in content, update style
+ if( sameOldContent ) {
+ child->setStyle(pseudo);
+ return;
+ }
+
+ // If we don't want generated content any longer, or if we have generated content,
+ // but it's no longer identical to the new content data we want to build
+ // render objects for, then we nuke all of the old generated content.
+ if (oldContentPresent && (!newContentWanted || !sameOldContent))
+ {
+ // The child needs to be removed.
+ oldContentPresent = false;
+ child->detach();
+ child = 0;
+ }
+
+ // If we have no pseudo-style or if the pseudo's display type is NONE, then we
+ // have no generated content and can now return.
+ if (!newContentWanted)
+ return;
+
+ // Generated content consists of a single container that houses multiple children (specified
+ // by the content property). This pseudo container gets the pseudo style set on it.
+ RenderContainer* pseudoContainer = 0;
+ pseudoContainer = RenderFlow::createFlow(element(), pseudo, renderArena());
+ pseudoContainer->setIsAnonymous( true );
+ pseudoContainer->createGeneratedContent();
+
+ // Only add the container if it had content
+ if (pseudoContainer->firstChild()) {
+ addPseudoContainer(pseudoContainer);
+ pseudoContainer->close();
+ }
+}
+
+void RenderContainer::createGeneratedContent()
+{
+ RenderStyle* pseudo = style();
+ RenderStyle* style = new RenderStyle();
+ style->ref();
+ style->inheritFrom(pseudo);
+
+ // Now walk our list of generated content and create render objects for every type
+ // we encounter.
+ for (ContentData* contentData = pseudo->contentData();
+ contentData; contentData = contentData->_nextContent)
+ {
+ if (contentData->_contentType == CONTENT_TEXT)
+ {
+ RenderText* t = new (renderArena()) RenderText( node(), 0);
+ t->setIsAnonymous( true );
+ t->setStyle(style);
+ t->setText(contentData->contentText());
+ addChild(t);
+ }
+ else if (contentData->_contentType == CONTENT_OBJECT)
+ {
+ RenderImage* img = new (renderArena()) RenderImage(node());
+ img->setIsAnonymous( true );
+ img->setStyle(style);
+ img->setContentObject(contentData->contentObject());
+ addChild(img);
+ }
+ else if (contentData->_contentType == CONTENT_COUNTER)
+ {
+ // really a counter or just a glyph?
+ EListStyleType type = (EListStyleType)contentData->contentCounter()->listStyle();
+ RenderObject *t = 0;
+ if (isListStyleCounted(type)) {
+ t = new (renderArena()) RenderCounter( node(), contentData->contentCounter() );
+ }
+ else {
+ t = new (renderArena()) RenderGlyph( node(), type );
+ }
+ t->setIsAnonymous( true );
+ t->setStyle(style);
+ addChild(t);
+ }
+ else if (contentData->_contentType == CONTENT_QUOTE)
+ {
+ RenderQuote* t = new (renderArena()) RenderQuote( node(), contentData->contentQuote() );
+ t->setIsAnonymous( true );
+ t->setStyle(style);
+ addChild(t);
+ }
+ }
+ style->deref();
+}
+
+RenderContainer* RenderContainer::pseudoContainer(RenderStyle::PseudoId type) const
+{
+ RenderObject *child = 0;
+ switch (type) {
+ case RenderStyle::AFTER:
+ child = lastChild();
+ break;
+ case RenderStyle::BEFORE:
+ child = firstChild();
+ break;
+ case RenderStyle::REPLACED:
+ child = lastChild();
+ if (child && child->style()->styleType() == RenderStyle::AFTER)
+ child = child->previousSibling();
+ break;
+ default:
+ child = 0;
+ }
+
+ if (child && child->style()->styleType() == type) {
+ assert(child->isRenderBlock() || child->isRenderInline());
+ return static_cast<RenderContainer*>(child);
+ }
+ if (type == RenderStyle::AFTER) {
+ // check continuations
+ if (continuation())
+ return continuation()->pseudoContainer(type);
+ }
+ if (child && child->isAnonymousBlock())
+ return static_cast<RenderBlock*>(child)->pseudoContainer(type);
+ return 0;
+}
+
+void RenderContainer::addPseudoContainer(RenderObject* child)
+{
+ RenderStyle::PseudoId type = child->style()->styleType();
+ switch (type) {
+ case RenderStyle::AFTER: {
+ RenderObject *o = this;
+ while (o->continuation()) o = o->continuation();
+
+ // Coalesce inlines
+ if (child->style()->display() == INLINE && o->lastChild() && o->lastChild()->isAnonymousBlock()) {
+ o->lastChild()->addChild(child, 0);
+ } else
+ o->addChild(child, 0);
+ break;
+ }
+ case RenderStyle::BEFORE:
+ // Coalesce inlines
+ if (child->style()->display() == INLINE && firstChild() && firstChild()->isAnonymousBlock()) {
+ firstChild()->addChild(child, firstChild()->firstChild());
+ } else
+ addChild(child, firstChild());
+ break;
+ case RenderStyle::REPLACED:
+ addChild(child, pseudoContainer(RenderStyle::AFTER));
+ break;
+ default:
+ break;
+ }
+}
+
+void RenderContainer::updateReplacedContent()
+{
+ // Only for normal elements
+ if (!style() || style()->styleType() != RenderStyle::NOPSEUDO)
+ return;
+
+ // delete old generated content
+ RenderContainer *container = pseudoContainer(RenderStyle::REPLACED);
+ if (container) {
+ container->detach();
+ }
+
+ if (style()->useNormalContent()) return;
+
+ // create generated content
+ RenderStyle* pseudo = style()->getPseudoStyle(RenderStyle::REPLACED);
+ if (!pseudo) {
+ pseudo = new RenderStyle();
+ pseudo->inheritFrom(style());
+ pseudo->setStyleType(RenderStyle::REPLACED);
+ }
+ if (pseudo->useNormalContent())
+ pseudo->setContentData(style()->contentData());
+
+ container = RenderFlow::createFlow(node(), pseudo, renderArena());
+ container->setIsAnonymous( true );
+ container->createGeneratedContent();
+
+ addChild(container, pseudoContainer(RenderStyle::AFTER));
+}
+
+void RenderContainer::appendChildNode(RenderObject* newChild)
+{
+ KHTMLAssert(newChild->parent() == 0);
+
+ newChild->setParent(this);
+ RenderObject* lChild = lastChild();
+
+ if(lChild)
+ {
+ newChild->setPreviousSibling(lChild);
+ lChild->setNextSibling(newChild);
+ }
+ else
+ setFirstChild(newChild);
+
+ setLastChild(newChild);
+
+ // Keep our layer hierarchy updated. Optimize for the common case where we don't have any children
+ // and don't have a layer attached to ourselves.
+ if (newChild->firstChild() || newChild->layer()) {
+ RenderLayer* layer = enclosingLayer();
+ newChild->addLayers(layer, newChild);
+ }
+
+ newChild->setNeedsLayoutAndMinMaxRecalc(); // Goes up the containing block hierarchy.
+ if (!normalChildNeedsLayout())
+ setChildNeedsLayout(true); // We may supply the static position for an absolute positioned child.
+}
+
+void RenderContainer::insertChildNode(RenderObject* child, RenderObject* beforeChild)
+{
+ if(!beforeChild) {
+ appendChildNode(child);
+ return;
+ }
+
+ KHTMLAssert(!child->parent());
+ while ( beforeChild->parent() != this && beforeChild->parent()->isAnonymousBlock() )
+ beforeChild = beforeChild->parent();
+ KHTMLAssert(beforeChild->parent() == this);
+
+ if(beforeChild == firstChild())
+ setFirstChild(child);
+
+ RenderObject* prev = beforeChild->previousSibling();
+ child->setNextSibling(beforeChild);
+ beforeChild->setPreviousSibling(child);
+ if(prev) prev->setNextSibling(child);
+ child->setPreviousSibling(prev);
+ child->setParent(this);
+
+ // Keep our layer hierarchy updated.
+ RenderLayer* layer = enclosingLayer();
+ child->addLayers(layer, child);
+
+ child->setNeedsLayoutAndMinMaxRecalc();
+ if (!normalChildNeedsLayout())
+ setChildNeedsLayout(true); // We may supply the static position for an absolute positioned child.
+}
+
+
+void RenderContainer::layout()
+{
+ KHTMLAssert( needsLayout() );
+ KHTMLAssert( minMaxKnown() );
+ const bool pagedMode = canvas()->pagedMode();
+ RenderObject *child = firstChild();
+ while( child ) {
+ if (pagedMode) child->setNeedsLayout(true);
+ child->layoutIfNeeded();
+ if (child->containsPageBreak()) setContainsPageBreak(true);
+ if (child->needsPageClear()) setNeedsPageClear(true);
+ child = child->nextSibling();
+ }
+ setNeedsLayout(false);
+}
+
+void RenderContainer::removeLeftoverAnonymousBoxes()
+{
+ // we have to go over all child nodes and remove anonymous boxes, that do _not_
+ // have inline children to keep the tree flat
+ RenderObject *child = firstChild();
+ while( child ) {
+ RenderObject *next = child->nextSibling();
+
+ if ( child->isRenderBlock() && child->isAnonymousBlock() && !child->continuation() &&
+ !child->childrenInline() && !child->isTableCell() && !child->doNotDelete()) {
+ RenderObject *firstAnChild = child->firstChild();
+ RenderObject *lastAnChild = child->lastChild();
+ if ( firstAnChild ) {
+ RenderObject *o = firstAnChild;
+ while( o ) {
+ o->setParent( this );
+ o = o->nextSibling();
+ }
+ firstAnChild->setPreviousSibling( child->previousSibling() );
+ lastAnChild->setNextSibling( child->nextSibling() );
+ if ( child->previousSibling() )
+ child->previousSibling()->setNextSibling( firstAnChild );
+ if ( child->nextSibling() )
+ child->nextSibling()->setPreviousSibling( lastAnChild );
+ if ( child == firstChild() )
+ m_first = firstAnChild;
+ if ( child == lastChild() )
+ m_last = lastAnChild;
+ } else {
+ if ( child->previousSibling() )
+ child->previousSibling()->setNextSibling( child->nextSibling() );
+ if ( child->nextSibling() )
+ child->nextSibling()->setPreviousSibling( child->previousSibling() );
+ if ( child == firstChild() )
+ m_first = child->nextSibling();
+ if ( child == lastChild() )
+ m_last = child->previousSibling();
+ }
+ child->setParent( 0 );
+ child->setPreviousSibling( 0 );
+ child->setNextSibling( 0 );
+ if ( !child->isText() ) {
+ RenderContainer *c = static_cast<RenderContainer *>(child);
+ c->m_first = 0;
+ c->m_next = 0;
+ }
+ child->detach();
+ }
+ child = next;
+ }
+ if ( parent() )
+ parent()->removeLeftoverAnonymousBoxes();
+}
+
+#undef DEBUG_LAYOUT
diff --git a/khtml/rendering/render_container.h b/khtml/rendering/render_container.h
new file mode 100644
index 000000000..4cf386140
--- /dev/null
+++ b/khtml/rendering/render_container.h
@@ -0,0 +1,85 @@
+/*
+ * This file is part of the html renderer for KDE.
+ *
+ * Copyright (C) 2001 Antti Koivisto (koivisto@kde.org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef render_container_h
+#define render_container_h
+
+#include "render_object.h"
+
+namespace khtml
+{
+
+
+/**
+ * Base class for rendering objects that can have children
+ */
+class RenderContainer : public RenderObject
+{
+public:
+ RenderContainer(DOM::NodeImpl* node);
+
+ void detach();
+
+ RenderObject *firstChild() const { return m_first; }
+ RenderObject *lastChild() const { return m_last; }
+
+ virtual bool childAllowed() const {
+ // Prevent normal children when we are replaced by generated content
+ if (style()) return style()->useNormalContent();
+ return true;
+ }
+
+ virtual void addChild(RenderObject *newChild, RenderObject *beforeChild = 0);
+
+ virtual RenderObject* removeChildNode(RenderObject* child);
+ virtual void appendChildNode(RenderObject* child);
+ virtual void insertChildNode(RenderObject* child, RenderObject* before);
+
+ virtual void layout();
+ virtual void calcMinMaxWidth() { setMinMaxKnown( true ); }
+
+ virtual void removeLeftoverAnonymousBoxes();
+
+ virtual void setStyle(RenderStyle* _style);
+
+protected:
+ // Generate CSS content
+ void createGeneratedContent();
+ void updateReplacedContent();
+
+ void updatePseudoChildren();
+ void updatePseudoChild(RenderStyle::PseudoId type);
+
+ RenderContainer* pseudoContainer( RenderStyle::PseudoId type ) const;
+ void addPseudoContainer(RenderObject* child);
+private:
+
+ void setFirstChild(RenderObject *first) { m_first = first; }
+ void setLastChild(RenderObject *last) { m_last = last; }
+
+protected:
+
+ RenderObject *m_first;
+ RenderObject *m_last;
+};
+
+}
+#endif
diff --git a/khtml/rendering/render_flow.cpp b/khtml/rendering/render_flow.cpp
new file mode 100644
index 000000000..88e06aab9
--- /dev/null
+++ b/khtml/rendering/render_flow.cpp
@@ -0,0 +1,412 @@
+/**
+ * This file is part of the html renderer for KDE.
+ *
+ * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
+ * (C) 1999-2003 Antti Koivisto (koivisto@kde.org)
+ * (C) 2002-2003 Dirk Mueller (mueller@kde.org)
+ * (C) 2003-2006 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 <kdebug.h>
+#include <assert.h>
+#include <qpainter.h>
+#include <kglobal.h>
+
+#include "rendering/render_flow.h"
+#include "rendering/render_text.h"
+#include "rendering/render_table.h"
+#include "rendering/render_canvas.h"
+#include "rendering/render_inline.h"
+#include "rendering/render_block.h"
+#include "rendering/render_arena.h"
+#include "rendering/render_line.h"
+#include "xml/dom_nodeimpl.h"
+#include "xml/dom_docimpl.h"
+#include "misc/htmltags.h"
+#include "html/html_formimpl.h"
+
+#include "khtmlview.h"
+
+using namespace DOM;
+using namespace khtml;
+
+RenderFlow* RenderFlow::createFlow(DOM::NodeImpl* node, RenderStyle* style, RenderArena* arena)
+{
+ RenderFlow* result;
+ if (style->display() == INLINE)
+ result = new (arena) RenderInline(node);
+ else
+ result = new (arena) RenderBlock(node);
+ result->setStyle(style);
+ return result;
+}
+
+RenderFlow* RenderFlow::continuationBefore(RenderObject* beforeChild)
+{
+ if (beforeChild && beforeChild->parent() == this)
+ return this;
+
+ RenderFlow* curr = continuation();
+ RenderFlow* nextToLast = this;
+ RenderFlow* last = this;
+ while (curr) {
+ if (beforeChild && beforeChild->parent() == curr) {
+ if (curr->firstChild() == beforeChild)
+ return last;
+ return curr;
+ }
+
+ nextToLast = last;
+ last = curr;
+ curr = curr->continuation();
+ }
+
+ if (!beforeChild && !last->firstChild())
+ return nextToLast;
+ return last;
+}
+
+void RenderFlow::addChildWithContinuation(RenderObject* newChild, RenderObject* beforeChild)
+{
+ RenderFlow* flow = continuationBefore(beforeChild);
+ while(beforeChild && beforeChild->parent() != flow && !beforeChild->parent()->isAnonymousBlock()) {
+ // skip implicit containers around beforeChild
+ beforeChild = beforeChild->parent();
+ }
+ RenderFlow* beforeChildParent = beforeChild ? static_cast<RenderFlow*>(beforeChild->parent()) :
+ (flow->continuation() ? flow->continuation() : flow);
+
+ if (newChild->isFloatingOrPositioned())
+ return beforeChildParent->addChildToFlow(newChild, beforeChild);
+
+ // A continuation always consists of two potential candidates: an inline or an anonymous
+ // block box holding block children.
+ bool childInline = newChild->isInline();
+ bool bcpInline = beforeChildParent->isInline();
+ bool flowInline = flow->isInline();
+
+ if (flow == beforeChildParent)
+ return flow->addChildToFlow(newChild, beforeChild);
+ else {
+ // The goal here is to match up if we can, so that we can coalesce and create the
+ // minimal # of continuations needed for the inline.
+ if (childInline == bcpInline)
+ return beforeChildParent->addChildToFlow(newChild, beforeChild);
+ else if (flowInline == childInline)
+ return flow->addChildToFlow(newChild, 0); // Just treat like an append.
+ else
+ return beforeChildParent->addChildToFlow(newChild, beforeChild);
+ }
+}
+
+void RenderFlow::addChild(RenderObject *newChild, RenderObject *beforeChild)
+{
+#ifdef DEBUG_LAYOUT
+ kdDebug( 6040 ) << renderName() << "(RenderFlow)::addChild( " << newChild->renderName() <<
+ ", " << (beforeChild ? beforeChild->renderName() : "0") << " )" << endl;
+ kdDebug( 6040 ) << "current height = " << m_height << endl;
+#endif
+
+ if (continuation())
+ return addChildWithContinuation(newChild, beforeChild);
+ return addChildToFlow(newChild, beforeChild);
+}
+
+void RenderFlow::deleteInlineBoxes(RenderArena* arena)
+{
+ RenderBox::deleteInlineBoxes(arena); //In case we upcalled
+ //during construction
+ if (m_firstLineBox) {
+ if (!arena)
+ arena = renderArena();
+ InlineRunBox *curr=m_firstLineBox, *next=0;
+ while (curr) {
+ next = curr->nextLineBox();
+ curr->detach(arena);
+ curr = next;
+ }
+ m_firstLineBox = 0;
+ m_lastLineBox = 0;
+ }
+}
+
+void RenderFlow::deleteLastLineBox(RenderArena* arena)
+{
+ if (m_lastLineBox) {
+ if (!arena)
+ arena = renderArena();
+ InlineRunBox *curr=m_lastLineBox, *prev = m_lastLineBox;
+ if (m_firstLineBox == m_lastLineBox)
+ m_firstLineBox = m_lastLineBox = 0;
+ else {
+ prev = curr->prevLineBox();
+ while (!prev->isInlineFlowBox()) {
+ prev = prev->prevLineBox();
+ prev->detach(arena);
+ }
+ m_lastLineBox = static_cast<InlineFlowBox*>(prev);
+ prev->setNextLineBox(0);
+ }
+ if (curr->parent()) {
+ curr->parent()->removeFromLine(curr);
+ }
+ curr->detach(arena);
+ }
+}
+
+InlineBox* RenderFlow::createInlineBox(bool makePlaceHolderBox, bool isRootLineBox)
+{
+ if ( !isRootLineBox &&
+ (isReplaced() || makePlaceHolderBox) ) // Inline tables and inline blocks
+ return RenderBox::createInlineBox(false, false); // (or positioned element placeholders).
+
+ InlineFlowBox* flowBox = 0;
+ if (isInlineFlow())
+ flowBox = new (renderArena()) InlineFlowBox(this);
+ else
+ flowBox = new (renderArena()) RootInlineBox(this);
+
+ if (!m_firstLineBox) {
+ m_firstLineBox = m_lastLineBox = flowBox;
+ } else {
+ m_lastLineBox->setNextLineBox(flowBox);
+ flowBox->setPreviousLineBox(m_lastLineBox);
+ m_lastLineBox = flowBox;
+ }
+
+ return flowBox;
+}
+
+void RenderFlow::paintLines(PaintInfo& i, int _tx, int _ty)
+{
+ // Only paint during the foreground/selection phases.
+ if (i.phase != PaintActionForeground && i.phase != PaintActionSelection && i.phase != PaintActionOutline)
+ return;
+
+ if (!firstLineBox())
+ return;
+
+ // We can check the first box and last box and avoid painting if we don't
+ // intersect. This is a quick short-circuit that we can take to avoid walking any lines.
+ // FIXME: This check is flawed in two extremely obscure ways.
+ // (1) If some line in the middle has a huge overflow, it might actually extend below the last line.
+ // (2) The overflow from an inline block on a line is not reported to the line.
+ int maxOutlineSize = maximalOutlineSize(i.phase);
+ int yPos = firstLineBox()->root()->topOverflow() - maxOutlineSize;
+ int h = maxOutlineSize + lastLineBox()->root()->bottomOverflow() - yPos;
+ yPos += _ty;
+ if ((yPos >= i.r.y() + i.r.height()) || (yPos + h <= i.r.y()))
+ return;
+ for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextFlowBox()) {
+ yPos = curr->root()->topOverflow() - maxOutlineSize;
+ h = curr->root()->bottomOverflow() + maxOutlineSize - yPos;
+ yPos += _ty;
+ if ((yPos < i.r.y() + i.r.height()) && (yPos + h > i.r.y()))
+ curr->paint(i, _tx, _ty);
+ }
+
+ if (i.phase == PaintActionOutline && i.outlineObjects) {
+ QValueList<RenderFlow *>::iterator it;;
+ for( it = (*i.outlineObjects).begin(); it != (*i.outlineObjects).end(); ++it )
+ if ((*it)->isRenderInline())
+ static_cast<RenderInline*>(*it)->paintOutlines(i.p, _tx, _ty);
+ i.outlineObjects->clear();
+ }
+}
+
+
+bool RenderFlow::hitTestLines(NodeInfo& i, int x, int y, int tx, int ty, HitTestAction hitTestAction)
+{
+ (void) hitTestAction;
+ /*
+ if (hitTestAction != HitTestForeground) // ### port hitTest
+ return false;
+ */
+
+ if (!firstLineBox())
+ return false;
+
+ // We can check the first box and last box and avoid hit testing if we don't
+ // contain the point. This is a quick short-circuit that we can take to avoid walking any lines.
+ // FIXME: This check is flawed in two extremely obscure ways.
+ // (1) If some line in the middle has a huge overflow, it might actually extend below the last line.
+ // (2) The overflow from an inline block on a line is not reported to the line.
+ if ((y >= ty + lastLineBox()->root()->bottomOverflow()) || (y < ty + firstLineBox()->root()->topOverflow()))
+ return false;
+
+ // See if our root lines contain the point. If so, then we hit test
+ // them further. Note that boxes can easily overlap, so we can't make any assumptions
+ // based off positions of our first line box or our last line box.
+ for (InlineFlowBox* curr = lastLineBox(); curr; curr = curr->prevFlowBox()) {
+ if (y >= ty + curr->root()->topOverflow() && y < ty + curr->root()->bottomOverflow()) {
+ bool inside = curr->nodeAtPoint(i, x, y, tx, ty);
+ if (inside) {
+ setInnerNode(i);
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+
+void RenderFlow::repaint(Priority prior)
+{
+ if (isInlineFlow()) {
+ // Find our leftmost position.
+ int left = 0;
+ // root inline box not reliably availabe during relayout
+ int top = firstLineBox() ? (
+ needsLayout() ? firstLineBox()->xPos() : firstLineBox()->root()->topOverflow()
+ ) : 0;
+ for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox())
+ if (curr == firstLineBox() || curr->xPos() < left)
+ left = curr->xPos();
+
+ // Now invalidate a rectangle.
+ int ow = style() ? style()->outlineSize() : 0;
+
+ // We need to add in the relative position offsets of any inlines (including us) up to our
+ // containing block.
+ RenderBlock* cb = containingBlock();
+ for (RenderObject* inlineFlow = this; inlineFlow && inlineFlow->isInlineFlow() && inlineFlow != cb;
+ inlineFlow = inlineFlow->parent()) {
+ if (inlineFlow->style() && inlineFlow->style()->position() == RELATIVE && inlineFlow->layer()) {
+ KHTMLAssert(inlineFlow->isBox());
+ static_cast<RenderBox*>(inlineFlow)->relativePositionOffset(left, top);
+ }
+ }
+
+ RootInlineBox *lastRoot = lastLineBox() && !needsLayout() ? lastLineBox()->root() : 0;
+ containingBlock()->repaintRectangle(-ow+left, -ow+top,
+ width()+ow*2,
+ (lastRoot ? lastRoot->bottomOverflow() - top : height())+ow*2, prior);
+ }
+ else {
+ if (firstLineBox() && firstLineBox()->topOverflow() < 0) {
+ int ow = style() ? style()->outlineSize() : 0;
+ repaintRectangle(-ow, -ow+firstLineBox()->topOverflow(),
+ effectiveWidth()+ow*2, effectiveHeight()+ow*2, prior);
+ }
+ else
+ return RenderBox::repaint(prior);
+ }
+}
+
+int
+RenderFlow::lowestPosition(bool includeOverflowInterior, bool includeSelf) const
+{
+ int bottom = RenderBox::lowestPosition(includeOverflowInterior, includeSelf);
+ if (!includeOverflowInterior && hasOverflowClip())
+ return bottom;
+
+ // FIXME: Come up with a way to use the layer tree to avoid visiting all the kids.
+ // For now, we have to descend into all the children, since we may have a huge abs div inside
+ // a tiny rel div buried somewhere deep in our child tree. In this case we have to get to
+ // the abs div.
+ for (RenderObject *c = firstChild(); c; c = c->nextSibling()) {
+ if (!c->isFloatingOrPositioned() && !c->isText() && !c->isInlineFlow()) {
+ int lp = c->yPos() + c->lowestPosition(false);
+ bottom = kMax(bottom, lp);
+ }
+ }
+
+ if (isRelPositioned()) {
+ int x;
+ relativePositionOffset(x, bottom);
+ }
+
+ return bottom;
+}
+
+int RenderFlow::rightmostPosition(bool includeOverflowInterior, bool includeSelf) const
+{
+ int right = RenderBox::rightmostPosition(includeOverflowInterior, includeSelf);
+ if (!includeOverflowInterior && hasOverflowClip())
+ return right;
+
+ // FIXME: Come up with a way to use the layer tree to avoid visiting all the kids.
+ // For now, we have to descend into all the children, since we may have a huge abs div inside
+ // a tiny rel div buried somewhere deep in our child tree. In this case we have to get to
+ // the abs div.
+ for (RenderObject *c = firstChild(); c; c = c->nextSibling()) {
+ if (!c->isFloatingOrPositioned() && !c->isText() && !c->isInlineFlow()) {
+ int rp = c->xPos() + c->rightmostPosition(false);
+ right = kMax(right, rp);
+ }
+ }
+
+ if (isRelPositioned()) {
+ int y;
+ relativePositionOffset(right, y);
+ }
+
+ return right;
+}
+
+int RenderFlow::leftmostPosition(bool includeOverflowInterior, bool includeSelf) const
+{
+ int left = RenderBox::leftmostPosition(includeOverflowInterior, includeSelf);
+ if (!includeOverflowInterior && hasOverflowClip())
+ return left;
+
+ // FIXME: Come up with a way to use the layer tree to avoid visiting all the kids.
+ // For now, we have to descend into all the children, since we may have a huge abs div inside
+ // a tiny rel div buried somewhere deep in our child tree. In this case we have to get to
+ // the abs div.
+ for (RenderObject *c = firstChild(); c; c = c->nextSibling()) {
+ if (!c->isFloatingOrPositioned() && !c->isText() && !c->isInlineFlow()) {
+ int lp = c->xPos() + c->leftmostPosition(false);
+ left = kMin(left, lp);
+ }
+ }
+
+ if (isRelPositioned()) {
+ int y;
+ relativePositionOffset(left, y);
+ }
+
+ return left;
+}
+
+int RenderFlow::highestPosition(bool includeOverflowInterior, bool includeSelf) const
+{
+ int top = RenderBox::highestPosition(includeOverflowInterior, includeSelf);
+ if (!includeOverflowInterior && hasOverflowClip())
+ return top;
+
+ // FIXME: Come up with a way to use the layer tree to avoid visiting all the kids.
+ // For now, we have to descend into all the children, since we may have a huge abs div inside
+ // a tiny rel div buried somewhere deep in our child tree. In this case we have to get to
+ // the abs div.
+ for (RenderObject *c = firstChild(); c; c = c->nextSibling()) {
+ if (!c->isFloatingOrPositioned() && !c->isText() && !c->isInlineFlow()) {
+ int hp = c->yPos() + c->highestPosition(false);
+ top = kMin(top, hp);
+ }
+ }
+
+ if (isRelPositioned()) {
+ int x;
+ relativePositionOffset(x, top);
+ }
+
+ return top;
+}
diff --git a/khtml/rendering/render_flow.h b/khtml/rendering/render_flow.h
new file mode 100644
index 000000000..1e23822bb
--- /dev/null
+++ b/khtml/rendering/render_flow.h
@@ -0,0 +1,96 @@
+/*
+ * This file is part of the DOM implementation for KDE.
+ *
+ * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
+ * (C) 1999-2003 Antti Koivisto (koivisto@kde.org)
+ * (C) 2002-2003 Dirk Mueller (mueller@kde.org)
+ * (C) 2003 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.
+ *
+ */
+#ifndef RENDER_FLOW_H
+#define RENDER_FLOW_H
+
+#include "render_box.h"
+#include "bidi.h"
+#include "render_line.h"
+
+namespace khtml {
+
+/**
+ * all geometry managing stuff is only in the block elements.
+ *
+ * Inline elements don't layout themselves, but the whole paragraph
+ * gets flowed by the surrounding block element. This is, because
+ * one needs to know the whole paragraph to calculate bidirectional
+ * behaviour of text, so putting the layouting routines in the inline
+ * elements is impossible.
+ */
+class RenderFlow : public RenderBox
+{
+public:
+ RenderFlow(DOM::NodeImpl* node)
+ : RenderBox(node)
+ { m_continuation = 0; m_firstLineBox = 0; m_lastLineBox = 0; }
+
+ virtual RenderFlow* continuation() const { return m_continuation; }
+ void setContinuation(RenderFlow* c) { m_continuation = c; }
+ RenderFlow* continuationBefore(RenderObject* beforeChild);
+
+ void addChildWithContinuation(RenderObject* newChild, RenderObject* beforeChild);
+ virtual void addChildToFlow(RenderObject* newChild, RenderObject* beforeChild) = 0;
+ virtual void addChild(RenderObject *newChild, RenderObject *beforeChild = 0);
+
+ static RenderFlow* createFlow(DOM::NodeImpl* node, RenderStyle* style, RenderArena* arena);
+
+ virtual void deleteLastLineBox(RenderArena* arena=0);
+ virtual void deleteInlineBoxes(RenderArena* arena=0);
+
+
+ InlineFlowBox* firstLineBox() const { return m_firstLineBox; }
+ InlineFlowBox* lastLineBox() const { return m_lastLineBox; }
+
+ virtual InlineBox* createInlineBox(bool makePlaceHolderBox, bool isRootLineBox);
+
+ void paintLines(PaintInfo& i, int _tx, int _ty);
+ bool hitTestLines(NodeInfo& i, int x, int y, int tx, int ty, HitTestAction hitTestAction);
+
+ virtual void repaint(Priority p=NormalPriority);
+
+ virtual int highestPosition(bool includeOverflowInterior=true, bool includeSelf=true) const;
+ virtual int lowestPosition(bool includeOverflowInterior=true, bool includeSelf=true) const;
+ virtual int rightmostPosition(bool includeOverflowInterior=true, bool includeSelf=true) const;
+ virtual int leftmostPosition(bool includeOverflowInterior=true, bool includeSelf=true) const;
+
+protected:
+ // An inline can be split with blocks occurring in between the inline content.
+ // When this occurs we need a pointer to our next object. We can basically be
+ // split into a sequence of inlines and blocks. The continuation will either be
+ // an anonymous block (that houses other blocks) or it will be an inline flow.
+ RenderFlow* m_continuation;
+
+ // For block flows, each box represents the root inline box for a line in the
+ // paragraph.
+ // For inline flows, each box represents a portion of that inline.
+ InlineFlowBox* m_firstLineBox;
+ InlineFlowBox* m_lastLineBox;
+};
+
+
+} //namespace
+
+#endif
diff --git a/khtml/rendering/render_form.cpp b/khtml/rendering/render_form.cpp
new file mode 100644
index 000000000..f8daba1e3
--- /dev/null
+++ b/khtml/rendering/render_form.cpp
@@ -0,0 +1,1898 @@
+/*
+ * This file is part of the DOM implementation for KDE.
+ *
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * (C) 2006 Maksim Orlovich (maksim@kde.org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include <kcompletionbox.h>
+#include <kcursor.h>
+#include <kdebug.h>
+#include <kfiledialog.h>
+#include <kfind.h>
+#include <kfinddialog.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kreplace.h>
+#include <kreplacedialog.h>
+#include <kspell.h>
+#include <kurlcompletion.h>
+#include <kwin.h>
+
+#include <qstyle.h>
+
+#include "misc/helper.h"
+#include "xml/dom2_eventsimpl.h"
+#include "html/html_formimpl.h"
+#include "misc/htmlhashes.h"
+
+#include "rendering/render_form.h"
+#include <assert.h>
+
+#include "khtmlview.h"
+#include "khtml_ext.h"
+#include "xml/dom_docimpl.h"
+
+#include <qpopupmenu.h>
+#include <qbitmap.h>
+
+using namespace khtml;
+
+RenderFormElement::RenderFormElement(HTMLGenericFormElementImpl *element)
+ : RenderWidget(element)
+{
+ // init RenderObject attributes
+ setInline(true); // our object is Inline
+
+ m_state = 0;
+}
+
+RenderFormElement::~RenderFormElement()
+{
+}
+
+short RenderFormElement::baselinePosition( bool f ) const
+{
+ return RenderWidget::baselinePosition( f ) - 2 - style()->fontMetrics().descent();
+}
+
+void RenderFormElement::updateFromElement()
+{
+ m_widget->setEnabled(!element()->disabled());
+ RenderWidget::updateFromElement();
+}
+
+void RenderFormElement::layout()
+{
+ KHTMLAssert( needsLayout() );
+ KHTMLAssert( minMaxKnown() );
+
+ // minimum height
+ m_height = 0;
+
+ calcWidth();
+ calcHeight();
+
+ if ( m_widget )
+ resizeWidget(m_width-borderLeft()-borderRight()-paddingLeft()-paddingRight(),
+ m_height-borderTop()-borderBottom()-paddingTop()-paddingBottom());
+
+ setNeedsLayout(false);
+}
+
+Qt::AlignmentFlags RenderFormElement::textAlignment() const
+{
+ switch (style()->textAlign()) {
+ case LEFT:
+ case KHTML_LEFT:
+ return AlignLeft;
+ case RIGHT:
+ case KHTML_RIGHT:
+ return AlignRight;
+ case CENTER:
+ case KHTML_CENTER:
+ return AlignHCenter;
+ case JUSTIFY:
+ // Just fall into the auto code for justify.
+ case TAAUTO:
+ return style()->direction() == RTL ? AlignRight : AlignLeft;
+ }
+ assert(false); // Should never be reached.
+ return AlignLeft;
+}
+
+// -------------------------------------------------------------------------
+
+RenderButton::RenderButton(HTMLGenericFormElementImpl *element)
+ : RenderFormElement(element)
+{
+}
+
+short RenderButton::baselinePosition( bool f ) const
+{
+ return RenderWidget::baselinePosition( f ) - 2;
+}
+
+// -------------------------------------------------------------------------------
+
+
+RenderCheckBox::RenderCheckBox(HTMLInputElementImpl *element)
+ : RenderButton(element)
+{
+ QCheckBox* b = new QCheckBox(view()->viewport(), "__khtml");
+ b->setAutoMask(true);
+ b->setMouseTracking(true);
+ setQWidget(b);
+
+ // prevent firing toggled() signals on initialization
+ b->setChecked(element->checked());
+
+ connect(b,SIGNAL(stateChanged(int)),this,SLOT(slotStateChanged(int)));
+}
+
+
+void RenderCheckBox::calcMinMaxWidth()
+{
+ KHTMLAssert( !minMaxKnown() );
+
+ QCheckBox *cb = static_cast<QCheckBox *>( m_widget );
+ QSize s( cb->style().pixelMetric( QStyle::PM_IndicatorWidth ),
+ cb->style().pixelMetric( QStyle::PM_IndicatorHeight ) );
+ setIntrinsicWidth( s.width() );
+ setIntrinsicHeight( s.height() );
+
+ RenderButton::calcMinMaxWidth();
+}
+
+void RenderCheckBox::updateFromElement()
+{
+ widget()->setChecked(element()->checked());
+
+ RenderButton::updateFromElement();
+}
+
+void RenderCheckBox::slotStateChanged(int state)
+{
+ element()->setChecked(state == QButton::On);
+ element()->setIndeterminate(state == QButton::NoChange);
+
+ ref();
+ element()->onChange();
+ deref();
+}
+
+// -------------------------------------------------------------------------------
+
+RenderRadioButton::RenderRadioButton(HTMLInputElementImpl *element)
+ : RenderButton(element)
+{
+ QRadioButton* b = new QRadioButton(view()->viewport(), "__khtml");
+ b->setMouseTracking(true);
+ setQWidget(b);
+
+ // prevent firing toggled() signals on initialization
+ b->setChecked(element->checked());
+
+ connect(b,SIGNAL(toggled(bool)),this,SLOT(slotToggled(bool)));
+}
+
+void RenderRadioButton::updateFromElement()
+{
+ widget()->setChecked(element()->checked());
+
+ RenderButton::updateFromElement();
+}
+
+void RenderRadioButton::calcMinMaxWidth()
+{
+ KHTMLAssert( !minMaxKnown() );
+
+ QRadioButton *rb = static_cast<QRadioButton *>( m_widget );
+ QSize s( rb->style().pixelMetric( QStyle::PM_ExclusiveIndicatorWidth ),
+ rb->style().pixelMetric( QStyle::PM_ExclusiveIndicatorHeight ) );
+ setIntrinsicWidth( s.width() );
+ setIntrinsicHeight( s.height() );
+
+ RenderButton::calcMinMaxWidth();
+}
+
+void RenderRadioButton::slotToggled(bool activated)
+{
+ if(activated) {
+ ref();
+ element()->onChange();
+ deref();
+ }
+}
+
+// -------------------------------------------------------------------------------
+
+
+RenderSubmitButton::RenderSubmitButton(HTMLInputElementImpl *element)
+ : RenderButton(element)
+{
+ QPushButton* p = new QPushButton(view()->viewport(), "__khtml");
+ setQWidget(p);
+ p->setAutoMask(true);
+ p->setMouseTracking(true);
+}
+
+QString RenderSubmitButton::rawText()
+{
+ QString value = element()->valueWithDefault().string();
+ value = value.stripWhiteSpace();
+ QString raw;
+ for(unsigned int i = 0; i < value.length(); i++) {
+ raw += value[i];
+ if(value[i] == '&')
+ raw += '&';
+ }
+ return raw;
+}
+
+void RenderSubmitButton::calcMinMaxWidth()
+{
+ KHTMLAssert( !minMaxKnown() );
+
+ QString raw = rawText();
+ QPushButton* pb = static_cast<QPushButton*>(m_widget);
+ pb->setText(raw);
+ pb->setFont(style()->font());
+
+ bool empty = raw.isEmpty();
+ if ( empty )
+ raw = QString::fromLatin1("X");
+ QFontMetrics fm = pb->fontMetrics();
+ QSize ts = fm.size( ShowPrefix, raw);
+ QSize s(pb->style().sizeFromContents( QStyle::CT_PushButton, pb, ts )
+ .expandedTo(QApplication::globalStrut()));
+ int margin = pb->style().pixelMetric( QStyle::PM_ButtonMargin, pb) +
+ pb->style().pixelMetric( QStyle::PM_DefaultFrameWidth, pb ) * 2;
+ int w = ts.width() + margin;
+ int h = s.height();
+ if (pb->isDefault() || pb->autoDefault()) {
+ int dbw = pb->style().pixelMetric( QStyle::PM_ButtonDefaultIndicator, pb ) * 2;
+ w += dbw;
+ }
+
+ // add 30% margins to the width (heuristics to make it look similar to IE)
+ s = QSize( w*13/10, h ).expandedTo(QApplication::globalStrut());
+
+ setIntrinsicWidth( s.width() );
+ setIntrinsicHeight( s.height() );
+
+ RenderButton::calcMinMaxWidth();
+}
+
+void RenderSubmitButton::updateFromElement()
+{
+ QString oldText = static_cast<QPushButton*>(m_widget)->text();
+ QString newText = rawText();
+ static_cast<QPushButton*>(m_widget)->setText(newText);
+ if ( oldText != newText )
+ setNeedsLayoutAndMinMaxRecalc();
+ RenderFormElement::updateFromElement();
+}
+
+short RenderSubmitButton::baselinePosition( bool f ) const
+{
+ return RenderFormElement::baselinePosition( f );
+}
+
+// -------------------------------------------------------------------------------
+
+RenderResetButton::RenderResetButton(HTMLInputElementImpl *element)
+ : RenderSubmitButton(element)
+{
+}
+
+// -------------------------------------------------------------------------------
+
+LineEditWidget::LineEditWidget(DOM::HTMLInputElementImpl* input, KHTMLView* view, QWidget* parent)
+ : KLineEdit(parent, "__khtml"), m_input(input), m_view(view), m_spell(0)
+{
+ setMouseTracking(true);
+ KActionCollection *ac = new KActionCollection(this);
+ m_spellAction = KStdAction::spelling( this, SLOT( slotCheckSpelling() ), ac );
+}
+
+LineEditWidget::~LineEditWidget()
+{
+ delete m_spell;
+ m_spell = 0L;
+}
+
+void LineEditWidget::slotCheckSpelling()
+{
+ if ( text().isEmpty() ) {
+ return;
+ }
+
+ delete m_spell;
+ m_spell = new KSpell( this, i18n( "Spell Checking" ), this, SLOT( slotSpellCheckReady( KSpell *) ), 0, true, true);
+
+ connect( m_spell, SIGNAL( death() ),this, SLOT( spellCheckerFinished() ) );
+ connect( m_spell, SIGNAL( misspelling( const QString &, const QStringList &, unsigned int ) ),this, SLOT( spellCheckerMisspelling( const QString &, const QStringList &, unsigned int ) ) );
+ connect( m_spell, SIGNAL( corrected( const QString &, const QString &, unsigned int ) ),this, SLOT( spellCheckerCorrected( const QString &, const QString &, unsigned int ) ) );
+}
+
+void LineEditWidget::spellCheckerMisspelling( const QString &_text, const QStringList &, unsigned int pos)
+{
+ highLightWord( _text.length(),pos );
+}
+
+void LineEditWidget::highLightWord( unsigned int length, unsigned int pos )
+{
+ setSelection ( pos, length );
+}
+
+void LineEditWidget::spellCheckerCorrected( const QString &old, const QString &corr, unsigned int pos )
+{
+ if( old!= corr )
+ {
+ setSelection ( pos, old.length() );
+ insert( corr );
+ setSelection ( pos, corr.length() );
+ }
+}
+
+void LineEditWidget::spellCheckerFinished()
+{
+}
+
+void LineEditWidget::slotSpellCheckReady( KSpell *s )
+{
+ s->check( text() );
+ connect( s, SIGNAL( done( const QString & ) ), this, SLOT( slotSpellCheckDone( const QString & ) ) );
+}
+
+void LineEditWidget::slotSpellCheckDone( const QString &s )
+{
+ if( s != text() )
+ setText( s );
+}
+
+
+QPopupMenu *LineEditWidget::createPopupMenu()
+{
+ QPopupMenu *popup = KLineEdit::createPopupMenu();
+
+ if ( !popup ) {
+ return 0L;
+ }
+
+ connect( popup, SIGNAL( activated( int ) ),
+ this, SLOT( extendedMenuActivated( int ) ) );
+
+ if (m_input->autoComplete()) {
+ popup->insertSeparator();
+ int id = popup->insertItem( SmallIconSet("history_clear"), i18n("Clear &History"), ClearHistory );
+ popup->setItemEnabled( id, (compObj() && !compObj()->isEmpty()) );
+ }
+
+ if (echoMode() == QLineEdit::Normal &&
+ !isReadOnly()) {
+ popup->insertSeparator();
+
+ m_spellAction->plug(popup);
+ m_spellAction->setEnabled( !text().isEmpty() );
+ }
+
+ return popup;
+}
+
+
+void LineEditWidget::extendedMenuActivated( int id)
+{
+ switch ( id )
+ {
+ case ClearHistory:
+ m_view->clearCompletionHistory(m_input->name().string());
+ if (compObj())
+ compObj()->clear();
+ default:
+ break;
+ }
+}
+
+bool LineEditWidget::event( QEvent *e )
+{
+ if (KLineEdit::event(e))
+ return true;
+
+ if ( e->type() == QEvent::AccelAvailable && isReadOnly() ) {
+ QKeyEvent* ke = (QKeyEvent*) e;
+ if ( ke->state() & ControlButton ) {
+ switch ( ke->key() ) {
+ case Key_Left:
+ case Key_Right:
+ case Key_Up:
+ case Key_Down:
+ case Key_Home:
+ case Key_End:
+ ke->accept();
+ default:
+ break;
+ }
+ }
+ }
+ return false;
+}
+
+void LineEditWidget::mouseMoveEvent(QMouseEvent *e)
+{
+ // hack to prevent Qt from calling setCursor on the widget
+ setDragEnabled(false);
+ KLineEdit::mouseMoveEvent(e);
+ setDragEnabled(true);
+}
+
+
+// -----------------------------------------------------------------------------
+
+RenderLineEdit::RenderLineEdit(HTMLInputElementImpl *element)
+ : RenderFormElement(element)
+{
+ LineEditWidget *edit = new LineEditWidget(element, view(), view()->viewport());
+ connect(edit,SIGNAL(returnPressed()), this, SLOT(slotReturnPressed()));
+ connect(edit,SIGNAL(textChanged(const QString &)),this,SLOT(slotTextChanged(const QString &)));
+
+ if(element->inputType() == HTMLInputElementImpl::PASSWORD)
+ edit->setEchoMode( QLineEdit::Password );
+
+ if ( element->autoComplete() ) {
+ QStringList completions = view()->formCompletionItems(element->name().string());
+ if (completions.count()) {
+ edit->completionObject()->setItems(completions);
+ edit->setContextMenuEnabled(true);
+ edit->completionBox()->setTabHandling( false );
+ }
+ }
+
+ setQWidget(edit);
+}
+
+void RenderLineEdit::setStyle(RenderStyle* _style)
+{
+ RenderFormElement::setStyle( _style );
+
+ widget()->setAlignment(textAlignment());
+}
+
+void RenderLineEdit::highLightWord( unsigned int length, unsigned int pos )
+{
+ LineEditWidget* w = static_cast<LineEditWidget*>(m_widget);
+ if ( w )
+ w->highLightWord( length, pos );
+}
+
+
+void RenderLineEdit::slotReturnPressed()
+{
+ // don't submit the form when return was pressed in a completion-popup
+ KCompletionBox *box = widget()->completionBox(false);
+
+ if ( box && box->isVisible() && box->currentItem() != -1 ) {
+ box->hide();
+ return;
+ }
+
+ // Emit onChange if necessary
+ // Works but might not be enough, dirk said he had another solution at
+ // hand (can't remember which) - David
+ handleFocusOut();
+
+ HTMLFormElementImpl* fe = element()->form();
+ if ( fe )
+ fe->submitFromKeyboard();
+}
+
+void RenderLineEdit::handleFocusOut()
+{
+ if ( widget() && widget()->edited() ) {
+ element()->onChange();
+ widget()->setEdited( false );
+ }
+}
+
+void RenderLineEdit::calcMinMaxWidth()
+{
+ KHTMLAssert( !minMaxKnown() );
+
+ const QFontMetrics &fm = style()->fontMetrics();
+ QSize s;
+
+ int size = element()->size();
+
+ int h = fm.lineSpacing();
+ int w = fm.width( 'x' ) * (size > 0 ? size+1 : 17); // "some"
+ s = QSize(w + 2 + 2*widget()->frameWidth(),
+ kMax(h, 14) + 2 + 2*widget()->frameWidth())
+ .expandedTo(QApplication::globalStrut());
+
+ setIntrinsicWidth( s.width() );
+ setIntrinsicHeight( s.height() );
+
+ RenderFormElement::calcMinMaxWidth();
+}
+
+void RenderLineEdit::updateFromElement()
+{
+ int ml = element()->maxLength();
+ if ( ml < 0 )
+ ml = 32767;
+
+ if ( widget()->maxLength() != ml ) {
+ widget()->setMaxLength( ml );
+ }
+
+ if (element()->value().string() != widget()->text()) {
+ widget()->blockSignals(true);
+ int pos = widget()->cursorPosition();
+ widget()->setText(element()->value().string());
+
+ widget()->setEdited( false );
+
+ widget()->setCursorPosition(pos);
+ widget()->blockSignals(false);
+ }
+ widget()->setReadOnly(element()->readOnly());
+
+ RenderFormElement::updateFromElement();
+}
+
+void RenderLineEdit::slotTextChanged(const QString &string)
+{
+ // don't use setValue here!
+ element()->m_value = string;
+ element()->m_unsubmittedFormChange = true;
+}
+
+void RenderLineEdit::select()
+{
+ static_cast<LineEditWidget*>(m_widget)->selectAll();
+}
+
+long RenderLineEdit::selectionStart()
+{
+ LineEditWidget* w = static_cast<LineEditWidget*>(m_widget);
+ if (w->hasSelectedText())
+ return w->selectionStart();
+ else
+ return w->cursorPosition();
+}
+
+
+long RenderLineEdit::selectionEnd()
+{
+ LineEditWidget* w = static_cast<LineEditWidget*>(m_widget);
+ if (w->hasSelectedText())
+ return w->selectionStart() + w->selectedText().length();
+ else
+ return w->cursorPosition();
+}
+
+void RenderLineEdit::setSelectionStart(long pos)
+{
+ LineEditWidget* w = static_cast<LineEditWidget*>(m_widget);
+ //See whether we have a non-empty selection now.
+ long end = selectionEnd();
+ if (end > pos)
+ w->setSelection(pos, end - pos);
+ w->setCursorPosition(pos);
+}
+
+void RenderLineEdit::setSelectionEnd(long pos)
+{
+ LineEditWidget* w = static_cast<LineEditWidget*>(m_widget);
+ //See whether we have a non-empty selection now.
+ long start = selectionStart();
+ if (start < pos)
+ w->setSelection(start, pos - start);
+
+ w->setCursorPosition(pos);
+}
+
+void RenderLineEdit::setSelectionRange(long start, long end)
+{
+ LineEditWidget* w = static_cast<LineEditWidget*>(m_widget);
+ w->setCursorPosition(end);
+ w->setSelection(start, end - start);
+}
+
+// ---------------------------------------------------------------------------
+
+RenderFieldset::RenderFieldset(HTMLGenericFormElementImpl *element)
+ : RenderBlock(element)
+{
+}
+
+RenderObject* RenderFieldset::layoutLegend(bool relayoutChildren)
+{
+ RenderObject* legend = findLegend();
+ if (legend) {
+ if (relayoutChildren)
+ legend->setNeedsLayout(true);
+ legend->layoutIfNeeded();
+
+ int xPos = borderLeft() + paddingLeft() + legend->marginLeft();
+ if (style()->direction() == RTL)
+ xPos = m_width - paddingRight() - borderRight() - legend->width() - legend->marginRight();
+ int b = borderTop();
+ int h = legend->height();
+ legend->setPos(xPos, kMax((b-h)/2, 0));
+ m_height = kMax(b,h) + paddingTop();
+ }
+ return legend;
+}
+
+RenderObject* RenderFieldset::findLegend()
+{
+ for (RenderObject* legend = firstChild(); legend; legend = legend->nextSibling()) {
+ if (!legend->isFloatingOrPositioned() && legend->element() &&
+ legend->element()->id() == ID_LEGEND)
+ return legend;
+ }
+ return 0;
+}
+
+void RenderFieldset::paintBoxDecorations(PaintInfo& pI, int _tx, int _ty)
+{
+ //kdDebug( 6040 ) << renderName() << "::paintDecorations()" << endl;
+
+ RenderObject* legend = findLegend();
+ if (!legend)
+ return RenderBlock::paintBoxDecorations(pI, _tx, _ty);
+
+ int w = width();
+ int h = height() + borderTopExtra() + borderBottomExtra();
+ int yOff = (legend->yPos() > 0) ? 0 : (legend->height()-borderTop())/2;
+ h -= yOff;
+ _ty += yOff - borderTopExtra();
+
+ int my = kMax(_ty,pI.r.y());
+ int end = kMin( pI.r.y() + pI.r.height(), _ty + h );
+ int mh = end - my;
+
+ paintBackground(pI.p, style()->backgroundColor(), style()->backgroundLayers(), my, mh, _tx, _ty, w, h);
+
+ if ( style()->hasBorder() )
+ paintBorderMinusLegend(pI.p, _tx, _ty, w, h, style(), legend->xPos(), legend->width());
+}
+
+void RenderFieldset::paintBorderMinusLegend(QPainter *p, int _tx, int _ty, int w, int h,
+ const RenderStyle* style, int lx, int lw)
+{
+
+ const QColor& tc = style->borderTopColor();
+ const QColor& bc = style->borderBottomColor();
+
+ EBorderStyle ts = style->borderTopStyle();
+ EBorderStyle bs = style->borderBottomStyle();
+ EBorderStyle ls = style->borderLeftStyle();
+ EBorderStyle rs = style->borderRightStyle();
+
+ bool render_t = ts > BHIDDEN;
+ bool render_l = ls > BHIDDEN;
+ bool render_r = rs > BHIDDEN;
+ bool render_b = bs > BHIDDEN;
+
+ if(render_t) {
+ drawBorder(p, _tx, _ty, _tx + lx, _ty + style->borderTopWidth(), BSTop, tc, style->color(), ts,
+ (render_l && (ls == DOTTED || ls == DASHED || ls == DOUBLE)?style->borderLeftWidth():0), 0);
+ drawBorder(p, _tx+lx+lw, _ty, _tx + w, _ty + style->borderTopWidth(), BSTop, tc, style->color(), ts,
+ 0, (render_r && (rs == DOTTED || rs == DASHED || rs == DOUBLE)?style->borderRightWidth():0));
+ }
+
+ if(render_b)
+ drawBorder(p, _tx, _ty + h - style->borderBottomWidth(), _tx + w, _ty + h, BSBottom, bc, style->color(), bs,
+ (render_l && (ls == DOTTED || ls == DASHED || ls == DOUBLE)?style->borderLeftWidth():0),
+ (render_r && (rs == DOTTED || rs == DASHED || rs == DOUBLE)?style->borderRightWidth():0));
+
+ if(render_l)
+ {
+ const QColor& lc = style->borderLeftColor();
+
+ bool ignore_top =
+ (tc == lc) &&
+ (ls >= OUTSET) &&
+ (ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET);
+
+ bool ignore_bottom =
+ (bc == lc) &&
+ (ls >= OUTSET) &&
+ (bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET);
+
+ drawBorder(p, _tx, _ty, _tx + style->borderLeftWidth(), _ty + h, BSLeft, lc, style->color(), ls,
+ ignore_top?0:style->borderTopWidth(),
+ ignore_bottom?0:style->borderBottomWidth());
+ }
+
+ if(render_r)
+ {
+ const QColor& rc = style->borderRightColor();
+
+ bool ignore_top =
+ (tc == rc) &&
+ (rs >= DOTTED || rs == INSET) &&
+ (ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET);
+
+ bool ignore_bottom =
+ (bc == rc) &&
+ (rs >= DOTTED || rs == INSET) &&
+ (bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET);
+
+ drawBorder(p, _tx + w - style->borderRightWidth(), _ty, _tx + w, _ty + h, BSRight, rc, style->color(), rs,
+ ignore_top?0:style->borderTopWidth(),
+ ignore_bottom?0:style->borderBottomWidth());
+ }
+}
+
+void RenderFieldset::setStyle(RenderStyle* _style)
+{
+ RenderBlock::setStyle(_style);
+
+ // WinIE renders fieldsets with display:inline like they're inline-blocks. For us,
+ // an inline-block is just a block element with replaced set to true and inline set
+ // to true. Ensure that if we ended up being inline that we set our replaced flag
+ // so that we're treated like an inline-block.
+ if (isInline())
+ setReplaced(true);
+}
+
+// -------------------------------------------------------------------------
+
+RenderFileButton::RenderFileButton(HTMLInputElementImpl *element)
+ : RenderFormElement(element)
+{
+ KURLRequester* w = new KURLRequester( view()->viewport(), "__khtml" );
+
+ w->setMode(KFile::File | KFile::ExistingOnly);
+ w->completionObject()->setDir(KGlobalSettings::documentPath());
+
+ connect(w->lineEdit(), SIGNAL(returnPressed()), this, SLOT(slotReturnPressed()));
+ connect(w->lineEdit(), SIGNAL(textChanged(const QString &)),this,SLOT(slotTextChanged(const QString &)));
+ connect(w, SIGNAL(urlSelected(const QString &)),this,SLOT(slotUrlSelected(const QString &)));
+
+ setQWidget(w);
+ m_haveFocus = false;
+}
+
+
+
+void RenderFileButton::calcMinMaxWidth()
+{
+ KHTMLAssert( !minMaxKnown() );
+
+ const QFontMetrics &fm = style()->fontMetrics();
+ int size = element()->size();
+
+ int h = fm.lineSpacing();
+ int w = fm.width( 'x' ) * (size > 0 ? size+1 : 17); // "some"
+ KLineEdit* edit = static_cast<KURLRequester*>( m_widget )->lineEdit();
+ QSize s = edit->style().sizeFromContents(QStyle::CT_LineEdit,
+ edit,
+ QSize(w + 2 + 2*edit->frameWidth(), kMax(h, 14) + 2 + 2*edit->frameWidth()))
+ .expandedTo(QApplication::globalStrut());
+ QSize bs = static_cast<KURLRequester*>( m_widget )->minimumSizeHint() - edit->minimumSizeHint();
+
+ setIntrinsicWidth( s.width() + bs.width() );
+ setIntrinsicHeight( kMax(s.height(), bs.height()) );
+
+ RenderFormElement::calcMinMaxWidth();
+}
+
+void RenderFileButton::handleFocusOut()
+{
+ if ( widget()->lineEdit() && widget()->lineEdit()->edited() ) {
+ element()->onChange();
+ widget()->lineEdit()->setEdited( false );
+ }
+}
+
+void RenderFileButton::updateFromElement()
+{
+ KLineEdit* edit = widget()->lineEdit();
+ edit->blockSignals(true);
+ edit->setText(element()->value().string());
+ edit->blockSignals(false);
+ edit->setEdited( false );
+
+ RenderFormElement::updateFromElement();
+}
+
+void RenderFileButton::slotReturnPressed()
+{
+ handleFocusOut();
+
+ if (element()->form())
+ element()->form()->submitFromKeyboard();
+}
+
+void RenderFileButton::slotTextChanged(const QString &/*string*/)
+{
+ element()->m_value = KURL( widget()->url() ).prettyURL( 0, KURL::StripFileProtocol );
+}
+
+void RenderFileButton::slotUrlSelected(const QString &)
+{
+ element()->onChange();
+}
+
+void RenderFileButton::select()
+{
+ widget()->lineEdit()->selectAll();
+}
+
+// -------------------------------------------------------------------------
+
+RenderLabel::RenderLabel(HTMLGenericFormElementImpl *element)
+ : RenderFormElement(element)
+{
+
+}
+
+// -------------------------------------------------------------------------
+
+RenderLegend::RenderLegend(HTMLGenericFormElementImpl *element)
+ : RenderBlock(element)
+{
+}
+
+// -------------------------------------------------------------------------------
+
+ComboBoxWidget::ComboBoxWidget(QWidget *parent)
+ : KComboBox(false, parent, "__khtml")
+{
+ setAutoMask(true);
+ if (listBox()) listBox()->installEventFilter(this);
+ setMouseTracking(true);
+}
+
+bool ComboBoxWidget::event(QEvent *e)
+{
+ if (KComboBox::event(e))
+ return true;
+ if (e->type()==QEvent::KeyPress)
+ {
+ QKeyEvent *ke = static_cast<QKeyEvent *>(e);
+ switch(ke->key())
+ {
+ case Key_Return:
+ case Key_Enter:
+ popup();
+ ke->accept();
+ return true;
+ default:
+ return false;
+ }
+ }
+ return false;
+}
+
+bool ComboBoxWidget::eventFilter(QObject *dest, QEvent *e)
+{
+ if (dest==listBox() && e->type()==QEvent::KeyPress)
+ {
+ QKeyEvent *ke = static_cast<QKeyEvent *>(e);
+ bool forward = false;
+ switch(ke->key())
+ {
+ case Key_Tab:
+ forward=true;
+ case Key_BackTab:
+ // ugly hack. emulate popdownlistbox() (private in QComboBox)
+ // we re-use ke here to store the reference to the generated event.
+ ke = new QKeyEvent(QEvent::KeyPress, Key_Escape, 0, 0);
+ QApplication::sendEvent(dest,ke);
+ focusNextPrevChild(forward);
+ delete ke;
+ return true;
+ default:
+ return KComboBox::eventFilter(dest, e);
+ }
+ }
+ return KComboBox::eventFilter(dest, e);
+}
+
+// -------------------------------------------------------------------------
+
+RenderSelect::RenderSelect(HTMLSelectElementImpl *element)
+ : RenderFormElement(element)
+{
+ m_ignoreSelectEvents = false;
+ m_multiple = element->multiple();
+ m_size = element->size();
+ m_useListBox = (m_multiple || m_size > 1);
+ m_selectionChanged = true;
+ m_optionsChanged = true;
+
+ if(m_useListBox)
+ setQWidget(createListBox());
+ else
+ setQWidget(createComboBox());
+}
+
+void RenderSelect::updateFromElement()
+{
+ m_ignoreSelectEvents = true;
+
+ // change widget type
+ bool oldMultiple = m_multiple;
+ unsigned oldSize = m_size;
+ bool oldListbox = m_useListBox;
+
+ m_multiple = element()->multiple();
+ m_size = element()->size();
+ m_useListBox = (m_multiple || m_size > 1);
+
+ if (oldMultiple != m_multiple || oldSize != m_size) {
+ if (m_useListBox != oldListbox) {
+ // type of select has changed
+ if(m_useListBox)
+ setQWidget(createListBox());
+ else
+ setQWidget(createComboBox());
+ }
+
+ if (m_useListBox && oldMultiple != m_multiple) {
+ static_cast<KListBox*>(m_widget)->setSelectionMode(m_multiple ? QListBox::Extended : QListBox::Single);
+ }
+ m_selectionChanged = true;
+ m_optionsChanged = true;
+ }
+
+ // update contents listbox/combobox based on options in m_element
+ if ( m_optionsChanged ) {
+ if (element()->m_recalcListItems)
+ element()->recalcListItems();
+ QMemArray<HTMLGenericFormElementImpl*> listItems = element()->listItems();
+ int listIndex;
+
+ if(m_useListBox) {
+ static_cast<KListBox*>(m_widget)->clear();
+ }
+
+ else
+ static_cast<KComboBox*>(m_widget)->clear();
+
+ for (listIndex = 0; listIndex < int(listItems.size()); listIndex++) {
+ if (listItems[listIndex]->id() == ID_OPTGROUP) {
+ DOMString text = listItems[listIndex]->getAttribute(ATTR_LABEL);
+ if (text.isNull())
+ text = "";
+
+ if(m_useListBox) {
+ QListBoxText *item = new QListBoxText(QString(text.implementation()->s, text.implementation()->l));
+ static_cast<KListBox*>(m_widget)
+ ->insertItem(item, listIndex);
+ item->setSelectable(false);
+ }
+ else {
+ static_cast<KComboBox*>(m_widget)
+ ->insertItem(QString(text.implementation()->s, text.implementation()->l), listIndex);
+ static_cast<KComboBox*>(m_widget)->listBox()->item(listIndex)->setSelectable(false);
+ }
+ }
+ else if (listItems[listIndex]->id() == ID_OPTION) {
+ HTMLOptionElementImpl* optElem = static_cast<HTMLOptionElementImpl*>(listItems[listIndex]);
+ QString text = optElem->text().string();
+ if (optElem->parentNode()->id() == ID_OPTGROUP)
+ {
+ // Prefer label if set
+ DOMString label = optElem->getAttribute(ATTR_LABEL);
+ if (!label.isEmpty())
+ text = label.string();
+ text = QString::fromLatin1(" ")+text;
+ }
+
+ if(m_useListBox) {
+ KListBox *l = static_cast<KListBox*>(m_widget);
+ l->insertItem(text, listIndex);
+ DOMString disabled = optElem->getAttribute(ATTR_DISABLED);
+ if (!disabled.isNull() && l->item( listIndex )) {
+ l->item( listIndex )->setSelectable( false );
+ }
+ } else
+ static_cast<KComboBox*>(m_widget)->insertItem(text, listIndex);
+ }
+ else
+ KHTMLAssert(false);
+ m_selectionChanged = true;
+ }
+
+ // QComboBox caches the size hint unless you call setFont (ref: TT docu)
+ if(!m_useListBox) {
+ KComboBox *that = static_cast<KComboBox*>(m_widget);
+ that->setFont( that->font() );
+ }
+ setNeedsLayoutAndMinMaxRecalc();
+ m_optionsChanged = false;
+ }
+
+ // update selection
+ if (m_selectionChanged) {
+ updateSelection();
+ }
+
+
+ m_ignoreSelectEvents = false;
+
+ RenderFormElement::updateFromElement();
+}
+
+void RenderSelect::calcMinMaxWidth()
+{
+ KHTMLAssert( !minMaxKnown() );
+
+ if (m_optionsChanged)
+ updateFromElement();
+
+ // ### ugly HACK FIXME!!!
+ setMinMaxKnown();
+ layoutIfNeeded();
+ setNeedsLayoutAndMinMaxRecalc();
+ // ### end FIXME
+
+ RenderFormElement::calcMinMaxWidth();
+}
+
+void RenderSelect::layout( )
+{
+ KHTMLAssert(needsLayout());
+ KHTMLAssert(minMaxKnown());
+
+ // ### maintain selection properly between type/size changes, and work
+ // out how to handle multiselect->singleselect (probably just select
+ // first selected one)
+
+ // calculate size
+ if(m_useListBox) {
+ KListBox* w = static_cast<KListBox*>(m_widget);
+
+ QListBoxItem* p = w->firstItem();
+ int width = 0;
+ int height = 0;
+ while(p) {
+ width = kMax(width, p->width(p->listBox()));
+ height = kMax(height, p->height(p->listBox()));
+ p = p->next();
+ }
+ if ( !height )
+ height = w->fontMetrics().height();
+ if ( !width )
+ width = w->fontMetrics().width( 'x' );
+
+ int size = m_size;
+ // check if multiple and size was not given or invalid
+ // Internet Exploder sets size to kMin(number of elements, 4)
+ // Netscape seems to simply set it to "number of elements"
+ // the average of that is IMHO kMin(number of elements, 10)
+ // so I did that ;-)
+ if(size < 1)
+ size = kMin(static_cast<KListBox*>(m_widget)->count(), 10u);
+
+ width += 2*w->frameWidth() + w->verticalScrollBar()->sizeHint().width();
+ height = size*height + 2*w->frameWidth();
+
+ setIntrinsicWidth( width );
+ setIntrinsicHeight( height );
+ }
+ else {
+ QSize s(m_widget->sizeHint());
+ setIntrinsicWidth( s.width() );
+ setIntrinsicHeight( s.height() );
+ }
+
+ /// uuh, ignore the following line..
+ setNeedsLayout(true);
+ RenderFormElement::layout();
+
+ // and now disable the widget in case there is no <option> given
+ QMemArray<HTMLGenericFormElementImpl*> listItems = element()->listItems();
+
+ bool foundOption = false;
+ for (uint i = 0; i < listItems.size() && !foundOption; i++)
+ foundOption = (listItems[i]->id() == ID_OPTION);
+
+ m_widget->setEnabled(foundOption && ! element()->disabled());
+}
+
+void RenderSelect::slotSelected(int index) // emitted by the combobox only
+{
+ if ( m_ignoreSelectEvents ) return;
+
+ KHTMLAssert( !m_useListBox );
+
+ QMemArray<HTMLGenericFormElementImpl*> listItems = element()->listItems();
+ if(index >= 0 && index < int(listItems.size()))
+ {
+ bool found = ( listItems[index]->id() == ID_OPTION );
+
+ if ( !found ) {
+ // this one is not selectable, we need to find an option element
+ while ( ( unsigned ) index < listItems.size() ) {
+ if ( listItems[index]->id() == ID_OPTION ) {
+ found = true;
+ break;
+ }
+ ++index;
+ }
+
+ if ( !found ) {
+ while ( index >= 0 ) {
+ if ( listItems[index]->id() == ID_OPTION ) {
+ found = true;
+ break;
+ }
+ --index;
+ }
+ }
+ }
+
+ if ( found ) {
+ bool changed = false;
+
+ for ( unsigned int i = 0; i < listItems.size(); ++i )
+ if ( listItems[i]->id() == ID_OPTION && i != (unsigned int) index )
+ {
+ HTMLOptionElementImpl* opt = static_cast<HTMLOptionElementImpl*>( listItems[i] );
+ changed |= (opt->m_selected == true);
+ opt->m_selected = false;
+ }
+
+ HTMLOptionElementImpl* opt = static_cast<HTMLOptionElementImpl*>(listItems[index]);
+ changed |= (opt->m_selected == false);
+ opt->m_selected = true;
+
+ if ( index != static_cast<ComboBoxWidget*>( m_widget )->currentItem() )
+ static_cast<ComboBoxWidget*>( m_widget )->setCurrentItem( index );
+
+ // When selecting an optgroup item, and we move forward to we
+ // shouldn't emit onChange. Hence this bool, the if above doesn't do it.
+ if ( changed )
+ {
+ ref();
+ element()->onChange();
+ deref();
+ }
+ }
+ }
+}
+
+
+void RenderSelect::slotSelectionChanged() // emitted by the listbox only
+{
+ if ( m_ignoreSelectEvents ) return;
+
+ // don't use listItems() here as we have to avoid recalculations - changing the
+ // option list will make use update options not in the way the user expects them
+ QMemArray<HTMLGenericFormElementImpl*> listItems = element()->m_listItems;
+ for ( unsigned i = 0; i < listItems.count(); i++ )
+ // don't use setSelected() here because it will cause us to be called
+ // again with updateSelection.
+ if ( listItems[i]->id() == ID_OPTION )
+ static_cast<HTMLOptionElementImpl*>( listItems[i] )
+ ->m_selected = static_cast<KListBox*>( m_widget )->isSelected( i );
+
+ ref();
+ element()->onChange();
+ deref();
+}
+
+void RenderSelect::setOptionsChanged(bool _optionsChanged)
+{
+ m_optionsChanged = _optionsChanged;
+}
+
+KListBox* RenderSelect::createListBox()
+{
+ KListBox *lb = new KListBox(view()->viewport(), "__khtml");
+ lb->setSelectionMode(m_multiple ? QListBox::Extended : QListBox::Single);
+ // ### looks broken
+ //lb->setAutoMask(true);
+ connect( lb, SIGNAL( selectionChanged() ), this, SLOT( slotSelectionChanged() ) );
+// connect( lb, SIGNAL( clicked( QListBoxItem * ) ), this, SLOT( slotClicked() ) );
+ m_ignoreSelectEvents = false;
+ lb->setMouseTracking(true);
+
+ return lb;
+}
+
+ComboBoxWidget *RenderSelect::createComboBox()
+{
+ ComboBoxWidget *cb = new ComboBoxWidget(view()->viewport());
+ connect(cb, SIGNAL(activated(int)), this, SLOT(slotSelected(int)));
+ return cb;
+}
+
+void RenderSelect::updateSelection()
+{
+ QMemArray<HTMLGenericFormElementImpl*> listItems = element()->listItems();
+ int i;
+ if (m_useListBox) {
+ // if multi-select, we select only the new selected index
+ KListBox *listBox = static_cast<KListBox*>(m_widget);
+ for (i = 0; i < int(listItems.size()); i++)
+ listBox->setSelected(i,listItems[i]->id() == ID_OPTION &&
+ static_cast<HTMLOptionElementImpl*>(listItems[i])->selected());
+ }
+ else {
+ bool found = false;
+ unsigned firstOption = listItems.size();
+ i = listItems.size();
+ while (i--)
+ if (listItems[i]->id() == ID_OPTION) {
+ if (found)
+ static_cast<HTMLOptionElementImpl*>(listItems[i])->m_selected = false;
+ else if (static_cast<HTMLOptionElementImpl*>(listItems[i])->selected()) {
+ static_cast<KComboBox*>( m_widget )->setCurrentItem(i);
+ found = true;
+ }
+ firstOption = i;
+ }
+
+ Q_ASSERT(firstOption == listItems.size() || found);
+ }
+
+ m_selectionChanged = false;
+}
+
+
+// -------------------------------------------------------------------------
+
+TextAreaWidget::TextAreaWidget(int wrap, QWidget* parent)
+ : KTextEdit(parent, "__khtml"), m_findDlg(0), m_find(0), m_repDlg(0), m_replace(0)
+{
+ if(wrap != DOM::HTMLTextAreaElementImpl::ta_NoWrap) {
+ setWordWrap(QTextEdit::WidgetWidth);
+ setHScrollBarMode( AlwaysOff );
+ setVScrollBarMode( AlwaysOn );
+ }
+ else {
+ setWordWrap(QTextEdit::NoWrap);
+ setHScrollBarMode( Auto );
+ setVScrollBarMode( Auto );
+ }
+ KCursor::setAutoHideCursor(viewport(), true);
+ setTextFormat(QTextEdit::PlainText);
+ setAutoMask(true);
+ setMouseTracking(true);
+
+ KActionCollection *ac = new KActionCollection(this);
+ m_findAction = KStdAction::find( this, SLOT( slotFind() ), ac );
+ m_findNextAction = KStdAction::findNext( this, SLOT( slotFindNext() ), ac );
+ m_replaceAction = KStdAction::replace( this, SLOT( slotReplace() ), ac );
+}
+
+
+TextAreaWidget::~TextAreaWidget()
+{
+ delete m_replace;
+ m_replace = 0L;
+ delete m_find;
+ m_find = 0L;
+ delete m_repDlg;
+ m_repDlg = 0L;
+ delete m_findDlg;
+ m_findDlg = 0L;
+}
+
+
+QPopupMenu *TextAreaWidget::createPopupMenu(const QPoint& pos)
+{
+ QPopupMenu *popup = KTextEdit::createPopupMenu(pos);
+
+ if ( !popup ) {
+ return 0L;
+ }
+
+ if (!isReadOnly()) {
+ popup->insertSeparator();
+
+ m_findAction->plug(popup);
+ m_findAction->setEnabled( !text().isEmpty() );
+
+ m_findNextAction->plug(popup);
+ m_findNextAction->setEnabled( m_find != 0 );
+
+ m_replaceAction->plug(popup);
+ m_replaceAction->setEnabled( !text().isEmpty() );
+ }
+
+ return popup;
+}
+
+
+void TextAreaWidget::slotFindHighlight(const QString& text, int matchingIndex, int matchingLength)
+{
+ Q_UNUSED(text)
+ //kdDebug() << "Highlight: [" << text << "] mi:" << matchingIndex << " ml:" << matchingLength << endl;
+ if (sender() == m_replace) {
+ setSelection(m_repPara, matchingIndex, m_repPara, matchingIndex + matchingLength);
+ setCursorPosition(m_repPara, matchingIndex);
+ } else {
+ setSelection(m_findPara, matchingIndex, m_findPara, matchingIndex + matchingLength);
+ setCursorPosition(m_findPara, matchingIndex);
+ }
+ ensureCursorVisible();
+}
+
+
+void TextAreaWidget::slotReplaceText(const QString &text, int replacementIndex, int /*replacedLength*/, int matchedLength) {
+ Q_UNUSED(text)
+ //kdDebug() << "Replace: [" << text << "] ri:" << replacementIndex << " rl:" << replacedLength << " ml:" << matchedLength << endl;
+ setSelection(m_repPara, replacementIndex, m_repPara, replacementIndex + matchedLength);
+ removeSelectedText();
+ insertAt(m_repDlg->replacement(), m_repPara, replacementIndex);
+ if (m_replace->options() & KReplaceDialog::PromptOnReplace) {
+ ensureCursorVisible();
+ }
+}
+
+
+void TextAreaWidget::slotDoReplace()
+{
+ if (!m_repDlg) {
+ // Should really assert()
+ return;
+ }
+
+ delete m_replace;
+ m_replace = new KReplace(m_repDlg->pattern(), m_repDlg->replacement(), m_repDlg->options(), this);
+ if (m_replace->options() & KFindDialog::FromCursor) {
+ getCursorPosition(&m_repPara, &m_repIndex);
+ } else if (m_replace->options() & KFindDialog::FindBackwards) {
+ m_repPara = paragraphs() - 1;
+ m_repIndex = paragraphLength(m_repPara) - 1;
+ } else {
+ m_repPara = 0;
+ m_repIndex = 0;
+ }
+
+ // Connect highlight signal to code which handles highlighting
+ // of found text.
+ connect(m_replace, SIGNAL(highlight(const QString &, int, int)),
+ this, SLOT(slotFindHighlight(const QString &, int, int)));
+ connect(m_replace, SIGNAL(findNext()), this, SLOT(slotReplaceNext()));
+ connect(m_replace, SIGNAL(replace(const QString &, int, int, int)),
+ this, SLOT(slotReplaceText(const QString &, int, int, int)));
+
+ m_repDlg->close();
+ slotReplaceNext();
+}
+
+
+void TextAreaWidget::slotReplaceNext()
+{
+ if (!m_replace) {
+ // assert?
+ return;
+ }
+
+ if (!(m_replace->options() & KReplaceDialog::PromptOnReplace)) {
+ viewport()->setUpdatesEnabled(false);
+ }
+
+ KFind::Result res = KFind::NoMatch;
+ while (res == KFind::NoMatch) {
+ // If we're done.....
+ if (m_replace->options() & KFindDialog::FindBackwards) {
+ if (m_repIndex == 0 && m_repPara == 0) {
+ break;
+ }
+ } else {
+ if (m_repPara == paragraphs() - 1 &&
+ m_repIndex == paragraphLength(m_repPara) - 1) {
+ break;
+ }
+ }
+
+ if (m_replace->needData()) {
+ m_replace->setData(text(m_repPara), m_repIndex);
+ }
+
+ res = m_replace->replace();
+
+ if (res == KFind::NoMatch) {
+ if (m_replace->options() & KFindDialog::FindBackwards) {
+ if (m_repPara == 0) {
+ m_repIndex = 0;
+ } else {
+ m_repPara--;
+ m_repIndex = paragraphLength(m_repPara) - 1;
+ }
+ } else {
+ if (m_repPara == paragraphs() - 1) {
+ m_repIndex = paragraphLength(m_repPara) - 1;
+ } else {
+ m_repPara++;
+ m_repIndex = 0;
+ }
+ }
+ }
+ }
+
+ if (!(m_replace->options() & KReplaceDialog::PromptOnReplace)) {
+ viewport()->setUpdatesEnabled(true);
+ repaintChanged();
+ }
+
+ if (res == KFind::NoMatch) { // at end
+ m_replace->displayFinalDialog();
+ delete m_replace;
+ m_replace = 0;
+ ensureCursorVisible();
+ //or if ( m_replace->shouldRestart() ) { reinit (w/o FromCursor) and call slotReplaceNext(); }
+ } else {
+ //m_replace->closeReplaceNextDialog();
+ }
+}
+
+
+void TextAreaWidget::slotDoFind()
+{
+ if (!m_findDlg) {
+ // Should really assert()
+ return;
+ }
+
+ delete m_find;
+ m_find = new KFind(m_findDlg->pattern(), m_findDlg->options(), this);
+ if (m_find->options() & KFindDialog::FromCursor) {
+ getCursorPosition(&m_findPara, &m_findIndex);
+ } else if (m_find->options() & KFindDialog::FindBackwards) {
+ m_findPara = paragraphs() - 1;
+ m_findIndex = paragraphLength(m_findPara) - 1;
+ } else {
+ m_findPara = 0;
+ m_findIndex = 0;
+ }
+
+ // Connect highlight signal to code which handles highlighting
+ // of found text.
+ connect(m_find, SIGNAL(highlight(const QString &, int, int)),
+ this, SLOT(slotFindHighlight(const QString &, int, int)));
+ connect(m_find, SIGNAL(findNext()), this, SLOT(slotFindNext()));
+
+ m_findDlg->close();
+ m_find->closeFindNextDialog();
+ slotFindNext();
+}
+
+
+void TextAreaWidget::slotFindNext()
+{
+ if (!m_find) {
+ // assert?
+ return;
+ }
+
+ KFind::Result res = KFind::NoMatch;
+ while (res == KFind::NoMatch) {
+ // If we're done.....
+ if (m_find->options() & KFindDialog::FindBackwards) {
+ if (m_findIndex == 0 && m_findPara == 0) {
+ break;
+ }
+ } else {
+ if (m_findPara == paragraphs() - 1 &&
+ m_findIndex == paragraphLength(m_findPara) - 1) {
+ break;
+ }
+ }
+
+ if (m_find->needData()) {
+ m_find->setData(text(m_findPara), m_findIndex);
+ }
+
+ res = m_find->find();
+
+ if (res == KFind::NoMatch) {
+ if (m_find->options() & KFindDialog::FindBackwards) {
+ if (m_findPara == 0) {
+ m_findIndex = 0;
+ } else {
+ m_findPara--;
+ m_findIndex = paragraphLength(m_findPara) - 1;
+ }
+ } else {
+ if (m_findPara == paragraphs() - 1) {
+ m_findIndex = paragraphLength(m_findPara) - 1;
+ } else {
+ m_findPara++;
+ m_findIndex = 0;
+ }
+ }
+ }
+ }
+
+ if (res == KFind::NoMatch) { // at end
+ m_find->displayFinalDialog();
+ delete m_find;
+ m_find = 0;
+ //or if ( m_find->shouldRestart() ) { reinit (w/o FromCursor) and call slotFindNext(); }
+ } else {
+ //m_find->closeFindNextDialog();
+ }
+}
+
+
+void TextAreaWidget::slotFind()
+{
+ if( text().isEmpty() ) // saves having to track the text changes
+ return;
+
+ if ( m_findDlg ) {
+ KWin::activateWindow( m_findDlg->winId() );
+ } else {
+ m_findDlg = new KFindDialog(false, this, "KHTML Text Area Find Dialog");
+ connect( m_findDlg, SIGNAL(okClicked()), this, SLOT(slotDoFind()) );
+ }
+ m_findDlg->show();
+}
+
+
+void TextAreaWidget::slotReplace()
+{
+ if( text().isEmpty() ) // saves having to track the text changes
+ return;
+
+ if ( m_repDlg ) {
+ KWin::activateWindow( m_repDlg->winId() );
+ } else {
+ m_repDlg = new KReplaceDialog(this, "KHTMLText Area Replace Dialog", 0,
+ QStringList(), QStringList(), false);
+ connect( m_repDlg, SIGNAL(okClicked()), this, SLOT(slotDoReplace()) );
+ }
+ m_repDlg->show();
+}
+
+
+bool TextAreaWidget::event( QEvent *e )
+{
+ if ( e->type() == QEvent::AccelAvailable && isReadOnly() ) {
+ QKeyEvent* ke = (QKeyEvent*) e;
+ if ( ke->state() & ControlButton ) {
+ switch ( ke->key() ) {
+ case Key_Left:
+ case Key_Right:
+ case Key_Up:
+ case Key_Down:
+ case Key_Home:
+ case Key_End:
+ ke->accept();
+ default:
+ break;
+ }
+ }
+ }
+ return KTextEdit::event( e );
+}
+
+// -------------------------------------------------------------------------
+
+RenderTextArea::RenderTextArea(HTMLTextAreaElementImpl *element)
+ : RenderFormElement(element)
+{
+ scrollbarsStyled = false;
+
+ TextAreaWidget *edit = new TextAreaWidget(element->wrap(), view());
+ setQWidget(edit);
+ const KHTMLSettings *settings = view()->part()->settings();
+ edit->setCheckSpellingEnabled( settings->autoSpellCheck() );
+ edit->setTabChangesFocus( ! settings->allowTabulation() );
+
+ connect(edit,SIGNAL(textChanged()),this,SLOT(slotTextChanged()));
+}
+
+RenderTextArea::~RenderTextArea()
+{
+ if ( element()->m_dirtyvalue ) {
+ element()->m_value = text();
+ element()->m_dirtyvalue = false;
+ }
+}
+
+void RenderTextArea::handleFocusOut()
+{
+ TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
+ if ( w && element()->m_dirtyvalue ) {
+ element()->m_value = text();
+ element()->m_dirtyvalue = false;
+ }
+
+ if ( w && element()->m_changed ) {
+ element()->m_changed = false;
+ element()->onChange();
+ }
+}
+
+void RenderTextArea::calcMinMaxWidth()
+{
+ KHTMLAssert( !minMaxKnown() );
+
+ TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
+ const QFontMetrics &m = style()->fontMetrics();
+ w->setTabStopWidth(8 * m.width(" "));
+ QSize size( kMax(element()->cols(), 1L)*m.width('x') + w->frameWidth() +
+ w->verticalScrollBar()->sizeHint().width(),
+ kMax(element()->rows(), 1L)*m.lineSpacing() + w->frameWidth()*4 +
+ (w->wordWrap() == QTextEdit::NoWrap ?
+ w->horizontalScrollBar()->sizeHint().height() : 0)
+ );
+
+ setIntrinsicWidth( size.width() );
+ setIntrinsicHeight( size.height() );
+
+ RenderFormElement::calcMinMaxWidth();
+}
+
+void RenderTextArea::setStyle(RenderStyle* _style)
+{
+ bool unsubmittedFormChange = element()->m_unsubmittedFormChange;
+
+ RenderFormElement::setStyle(_style);
+
+ widget()->blockSignals(true);
+ widget()->setAlignment(textAlignment());
+ widget()->blockSignals(false);
+
+ scrollbarsStyled = false;
+
+ element()->m_unsubmittedFormChange = unsubmittedFormChange;
+}
+
+void RenderTextArea::layout()
+{
+ KHTMLAssert( needsLayout() );
+
+ RenderFormElement::layout();
+
+ TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
+
+ if (!scrollbarsStyled) {
+ w->horizontalScrollBar()->setPalette(style()->palette());
+ w->verticalScrollBar()->setPalette(style()->palette());
+ scrollbarsStyled=true;
+ }
+}
+
+void RenderTextArea::updateFromElement()
+{
+ TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
+ w->setReadOnly(element()->readOnly());
+ QString elementText = element()->value().string();
+ if ( elementText != text() )
+ {
+ w->blockSignals(true);
+ int line, col;
+ w->getCursorPosition( &line, &col );
+ int cx = w->contentsX();
+ int cy = w->contentsY();
+ w->setText( elementText );
+ w->setCursorPosition( line, col );
+ w->scrollBy( cx, cy );
+ w->blockSignals(false);
+ }
+ element()->m_dirtyvalue = false;
+
+ RenderFormElement::updateFromElement();
+}
+
+void RenderTextArea::close( )
+{
+ element()->setValue( element()->defaultValue() );
+
+ RenderFormElement::close();
+}
+
+
+QString RenderTextArea::text()
+{
+ QString txt;
+ TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
+
+ if(element()->wrap() == DOM::HTMLTextAreaElementImpl::ta_Physical) {
+ // yeah, QTextEdit has no accessor for getting the visually wrapped text
+ for (int p=0; p < w->paragraphs(); ++p) {
+ int ll = 0;
+ int lindex = w->lineOfChar(p, 0);
+ QString paragraphText = w->text(p);
+ int pl = w->paragraphLength(p);
+ paragraphText = paragraphText.left(pl); //Snip invented space.
+ for (int l = 0; l < pl; ++l) {
+ if (lindex != w->lineOfChar(p, l)) {
+ paragraphText.insert(l+ll++, QString::fromLatin1("\n"));
+ lindex = w->lineOfChar(p, l);
+ }
+ }
+ txt += paragraphText;
+ if (p < w->paragraphs() - 1)
+ txt += QString::fromLatin1("\n");
+ }
+ }
+ else
+ txt = w->text();
+
+ return txt;
+}
+
+int RenderTextArea::queryParagraphInfo(int para, Mode m, int param) {
+ /* We have to be a bit careful here, as we need to match up the positions
+ to what our value returns here*/
+ TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
+ int length = 0;
+
+ bool physWrap = element()->wrap() == DOM::HTMLTextAreaElementImpl::ta_Physical;
+
+ QString paragraphText = w->text(para);
+ int pl = w->paragraphLength(para);
+ int physicalPL = pl;
+ if (m == ParaPortionLength)
+ pl = param;
+
+ if (physWrap) {
+ //Go through all the chars of paragraph, and count line changes, chars, etc.
+ int lindex = w->lineOfChar(para, 0);
+ for (int c = 0; c < pl; ++c) {
+ ++length;
+ // Is there a change after this char?
+ if (c+1 < physicalPL && lindex != w->lineOfChar(para, c+1)) {
+ lindex = w->lineOfChar(para, c+1);
+ ++length;
+ }
+ if (m == ParaPortionOffset && length > param)
+ return c;
+ }
+ } else {
+ //Make sure to count the LF, CR as appropriate. ### this is stupid now, simplify
+ for (int c = 0; c < pl; ++c) {
+ ++length;
+ if (m == ParaPortionOffset && length > param)
+ return c;
+ }
+ }
+ if (m == ParaPortionOffset)
+ return pl;
+ if (m == ParaPortionLength)
+ return length;
+ return length + 1;
+}
+
+long RenderTextArea::computeCharOffset(int para, int index) {
+ if (para < 0)
+ return 0;
+
+ long pos = 0;
+ for (int cp = 0; cp < para; ++cp)
+ pos += queryParagraphInfo(cp, ParaLength);
+
+ if (index >= 0)
+ pos += queryParagraphInfo(para, ParaPortionLength, index);
+ return pos;
+}
+
+void RenderTextArea::computeParagraphAndIndex(long offset, int* para, int* index) {
+ TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
+
+ if (!w->paragraphs()) {
+ *para = -1;
+ *index = -1;
+ return;
+ }
+
+ //Find the paragraph that contains us..
+ int containingPar = 0;
+ long endPos = 0;
+ long startPos = 0;
+ for (int p = 0; p < w->paragraphs(); ++p) {
+ int len = queryParagraphInfo(p, ParaLength);
+ endPos += len;
+ if (endPos > offset) {
+ containingPar = p;
+ break;
+ }
+ startPos += len;
+ }
+
+ *para = containingPar;
+
+ //Now, scan within the paragraph to find the position..
+ long localOffset = offset - startPos;
+
+ *index = queryParagraphInfo(containingPar, ParaPortionOffset, localOffset);
+}
+
+void RenderTextArea::highLightWord( unsigned int length, unsigned int pos )
+{
+ TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
+ if ( w )
+ w->highLightWord( length, pos );
+}
+
+
+void RenderTextArea::slotTextChanged()
+{
+ element()->m_dirtyvalue = true;
+ element()->m_changed = true;
+ if (element()->m_value != text())
+ element()->m_unsubmittedFormChange = true;
+}
+
+void RenderTextArea::select()
+{
+ static_cast<TextAreaWidget *>(m_widget)->selectAll();
+}
+
+long RenderTextArea::selectionStart()
+{
+ TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
+ int para, index, dummy1, dummy2;
+ w->getSelection(&para, &index, &dummy1, &dummy2);
+ if (para == -1 || index == -1)
+ w->getCursorPosition(&para, &index);
+
+ return computeCharOffset(para, index);
+}
+
+long RenderTextArea::selectionEnd()
+{
+ TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
+ int para, index, dummy1, dummy2;
+ w->getSelection(&dummy1, &dummy2, &para, &index);
+ if (para == -1 || index == -1)
+ w->getCursorPosition(&para, &index);
+
+ return computeCharOffset(para, index);
+}
+
+void RenderTextArea::setSelectionStart(long offset) {
+ TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
+ int fromPara, fromIndex, toPara, toIndex;
+ w->getSelection(&fromPara, &fromIndex, &toPara, &toIndex);
+ computeParagraphAndIndex(offset, &fromPara, &fromIndex);
+ if (toPara == -1 || toIndex == -1) {
+ toPara = fromPara;
+ toIndex = fromIndex;
+ }
+ w->setSelection(fromPara, fromIndex, toPara, toIndex);
+}
+
+void RenderTextArea::setSelectionEnd(long offset) {
+ TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
+ int fromPara, fromIndex, toPara, toIndex;
+ w->getSelection(&fromPara, &fromIndex, &toPara, &toIndex);
+ computeParagraphAndIndex(offset, &toPara, &toIndex);
+ w->setSelection(fromPara, fromIndex, toPara, toIndex);
+}
+
+void RenderTextArea::setSelectionRange(long start, long end) {
+ TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
+ int fromPara, fromIndex, toPara, toIndex;
+ computeParagraphAndIndex(start, &fromPara, &fromIndex);
+ computeParagraphAndIndex(end, &toPara, &toIndex);
+ w->setSelection(fromPara, fromIndex, toPara, toIndex);
+}
+// ---------------------------------------------------------------------------
+
+#include "render_form.moc"
diff --git a/khtml/rendering/render_form.h b/khtml/rendering/render_form.h
new file mode 100644
index 000000000..f4d35b927
--- /dev/null
+++ b/khtml/rendering/render_form.h
@@ -0,0 +1,509 @@
+/*
+ * This file is part of the DOM implementation for KDE.
+ *
+ * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000-2003 Dirk Mueller (mueller@kde.org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef RENDER_FORM_H
+#define RENDER_FORM_H
+
+#include "rendering/render_replaced.h"
+#include "rendering/render_image.h"
+#include "rendering/render_flow.h"
+#include "rendering/render_style.h"
+#include "html/html_formimpl.h"
+
+class QWidget;
+class QLineEdit;
+class QListboxItem;
+
+#include <ktextedit.h>
+#include <kurlrequester.h>
+#include <klineedit.h>
+#include <qcheckbox.h>
+#include <qradiobutton.h>
+#include <qpushbutton.h>
+#include <qhbox.h>
+#include <klistbox.h>
+#include <kcombobox.h>
+#include "dom/dom_misc.h"
+
+class KHTMLPartBrowserExtension;
+class KSpell;
+class KFindDialog;
+class KReplaceDialog;
+class KFind;
+class KReplace;
+class KAction;
+class KURLRequester;
+
+namespace DOM {
+ class HTMLFormElementImpl;
+ class HTMLInputElementImpl;
+ class HTMLSelectElementImpl;
+ class HTMLGenericFormElementImpl;
+ class HTMLTextAreaElementImpl;
+}
+
+namespace khtml {
+
+class DocLoader;
+
+// -------------------------------------------------------------------------
+
+class RenderFormElement : public khtml::RenderWidget
+{
+public:
+ RenderFormElement(DOM::HTMLGenericFormElementImpl* node);
+ virtual ~RenderFormElement();
+
+ virtual const char *renderName() const { return "RenderForm"; }
+
+ virtual bool isFormElement() const { return true; }
+
+ // form elements never have padding
+ virtual int paddingTop() const { return 0; }
+ virtual int paddingBottom() const { return 0; }
+ virtual int paddingLeft() const { return 0; }
+ virtual int paddingRight() const { return 0; }
+
+ virtual void updateFromElement();
+
+ virtual void layout();
+ virtual short baselinePosition( bool ) const;
+
+ DOM::HTMLGenericFormElementImpl *element() const
+ { return static_cast<DOM::HTMLGenericFormElementImpl*>(RenderObject::element()); }
+
+protected:
+ virtual bool isRenderButton() const { return false; }
+ virtual bool isEditable() const { return false; }
+ AlignmentFlags textAlignment() const;
+
+ QPoint m_mousePos;
+ int m_state;
+};
+
+// -------------------------------------------------------------------------
+
+// generic class for all buttons
+class RenderButton : public RenderFormElement
+{
+ Q_OBJECT
+public:
+ RenderButton(DOM::HTMLGenericFormElementImpl* node);
+
+ virtual const char *renderName() const { return "RenderButton"; }
+
+ virtual short baselinePosition( bool ) const;
+
+ // don't even think about making this method virtual!
+ DOM::HTMLInputElementImpl* element() const
+ { return static_cast<DOM::HTMLInputElementImpl*>(RenderObject::element()); }
+
+protected:
+ virtual bool isRenderButton() const { return true; }
+};
+
+// -------------------------------------------------------------------------
+
+class RenderCheckBox : public RenderButton
+{
+ Q_OBJECT
+public:
+ RenderCheckBox(DOM::HTMLInputElementImpl* node);
+
+ virtual const char *renderName() const { return "RenderCheckBox"; }
+ virtual void updateFromElement();
+ virtual void calcMinMaxWidth();
+
+ virtual bool handleEvent(const DOM::EventImpl&) { return false; }
+
+ QCheckBox *widget() const { return static_cast<QCheckBox*>(m_widget); }
+
+public slots:
+ virtual void slotStateChanged(int state);
+};
+
+// -------------------------------------------------------------------------
+
+class RenderRadioButton : public RenderButton
+{
+ Q_OBJECT
+public:
+ RenderRadioButton(DOM::HTMLInputElementImpl* node);
+
+ virtual const char *renderName() const { return "RenderRadioButton"; }
+
+ virtual void calcMinMaxWidth();
+ virtual void updateFromElement();
+
+ virtual bool handleEvent(const DOM::EventImpl&) { return false; }
+
+ QRadioButton *widget() const { return static_cast<QRadioButton*>(m_widget); }
+
+public slots:
+ virtual void slotToggled(bool);
+};
+
+// -------------------------------------------------------------------------
+
+class RenderSubmitButton : public RenderButton
+{
+public:
+ RenderSubmitButton(DOM::HTMLInputElementImpl *element);
+
+ virtual const char *renderName() const { return "RenderSubmitButton"; }
+
+ virtual void calcMinMaxWidth();
+ virtual void updateFromElement();
+ virtual short baselinePosition( bool ) const;
+private:
+ QString rawText();
+};
+
+// -------------------------------------------------------------------------
+
+class RenderImageButton : public RenderImage
+{
+public:
+ RenderImageButton(DOM::HTMLInputElementImpl *element)
+ : RenderImage(element) {}
+
+ virtual const char *renderName() const { return "RenderImageButton"; }
+};
+
+
+// -------------------------------------------------------------------------
+
+class RenderResetButton : public RenderSubmitButton
+{
+public:
+ RenderResetButton(DOM::HTMLInputElementImpl *element);
+
+ virtual const char *renderName() const { return "RenderResetButton"; }
+
+};
+
+// -------------------------------------------------------------------------
+
+class RenderPushButton : public RenderSubmitButton
+{
+public:
+ RenderPushButton(DOM::HTMLInputElementImpl *element)
+ : RenderSubmitButton(element) {}
+
+};
+
+// -------------------------------------------------------------------------
+
+class RenderLineEdit : public RenderFormElement
+{
+ Q_OBJECT
+public:
+ RenderLineEdit(DOM::HTMLInputElementImpl *element);
+
+ virtual void calcMinMaxWidth();
+
+ virtual const char *renderName() const { return "RenderLineEdit"; }
+ virtual void updateFromElement();
+ virtual void setStyle(RenderStyle *style);
+
+ void select();
+
+ KLineEdit *widget() const { return static_cast<KLineEdit*>(m_widget); }
+ DOM::HTMLInputElementImpl* element() const
+ { return static_cast<DOM::HTMLInputElementImpl*>(RenderObject::element()); }
+ void highLightWord( unsigned int length, unsigned int pos );
+
+ long selectionStart();
+ long selectionEnd();
+ void setSelectionStart(long pos);
+ void setSelectionEnd(long pos);
+ void setSelectionRange(long start, long end);
+public slots:
+ void slotReturnPressed();
+ void slotTextChanged(const QString &string);
+protected:
+ virtual void handleFocusOut();
+
+private:
+ virtual bool isEditable() const { return true; }
+ virtual bool canHaveBorder() const { return true; }
+};
+
+// -------------------------------------------------------------------------
+
+class LineEditWidget : public KLineEdit
+{
+ Q_OBJECT
+public:
+ LineEditWidget(DOM::HTMLInputElementImpl* input,
+ KHTMLView* view, QWidget* parent);
+ ~LineEditWidget();
+ void highLightWord( unsigned int length, unsigned int pos );
+
+protected:
+ virtual bool event( QEvent *e );
+ virtual void mouseMoveEvent(QMouseEvent *e);
+ virtual QPopupMenu *createPopupMenu();
+private slots:
+ void extendedMenuActivated( int id);
+ void slotCheckSpelling();
+ void slotSpellCheckReady( KSpell *s );
+ void slotSpellCheckDone( const QString &s );
+ void spellCheckerMisspelling( const QString &text, const QStringList &, unsigned int pos);
+ void spellCheckerCorrected( const QString &, const QString &, unsigned int );
+ void spellCheckerFinished();
+
+private:
+ enum LineEditMenuID {
+ ClearHistory
+ };
+ DOM::HTMLInputElementImpl* m_input;
+ KHTMLView* m_view;
+ KSpell *m_spell;
+ KAction *m_spellAction;
+};
+
+// -------------------------------------------------------------------------
+
+class RenderFieldset : public RenderBlock
+{
+public:
+ RenderFieldset(DOM::HTMLGenericFormElementImpl *element);
+
+ virtual const char *renderName() const { return "RenderFieldSet"; }
+ virtual RenderObject* layoutLegend(bool relayoutChildren);
+ virtual void setStyle(RenderStyle* _style);
+
+protected:
+ virtual void paintBoxDecorations(PaintInfo& pI, int _tx, int _ty);
+ void paintBorderMinusLegend(QPainter *p, int _tx, int _ty, int w,
+ int h, const RenderStyle *style, int lx, int lw);
+ RenderObject* findLegend();
+};
+
+// -------------------------------------------------------------------------
+
+class RenderFileButton : public RenderFormElement
+{
+ Q_OBJECT
+public:
+ RenderFileButton(DOM::HTMLInputElementImpl *element);
+
+ virtual const char *renderName() const { return "RenderFileButton"; }
+ virtual void calcMinMaxWidth();
+ virtual void updateFromElement();
+ void select();
+
+ KURLRequester *widget() const { return static_cast<KURLRequester*>(m_widget); }
+
+ DOM::HTMLInputElementImpl *element() const
+ { return static_cast<DOM::HTMLInputElementImpl*>(RenderObject::element()); }
+
+public slots:
+ void slotReturnPressed();
+ void slotTextChanged(const QString &string);
+ void slotUrlSelected(const QString &string);
+
+protected:
+ virtual void handleFocusOut();
+
+ virtual bool isEditable() const { return true; }
+ virtual bool canHaveBorder() const { return true; }
+ virtual bool acceptsSyntheticEvents() const { return false; }
+
+ bool m_clicked;
+ bool m_haveFocus;
+};
+
+
+// -------------------------------------------------------------------------
+
+class RenderLabel : public RenderFormElement
+{
+public:
+ RenderLabel(DOM::HTMLGenericFormElementImpl *element);
+
+ virtual const char *renderName() const { return "RenderLabel"; }
+
+protected:
+ virtual bool canHaveBorder() const { return true; }
+};
+
+
+// -------------------------------------------------------------------------
+
+class RenderLegend : public RenderBlock
+{
+public:
+ RenderLegend(DOM::HTMLGenericFormElementImpl *element);
+
+ virtual const char *renderName() const { return "RenderLegend"; }
+};
+
+// -------------------------------------------------------------------------
+
+class ComboBoxWidget : public KComboBox
+{
+public:
+ ComboBoxWidget(QWidget *parent);
+
+protected:
+ virtual bool event(QEvent *);
+ virtual bool eventFilter(QObject *dest, QEvent *e);
+};
+
+// -------------------------------------------------------------------------
+
+class RenderSelect : public RenderFormElement
+{
+ Q_OBJECT
+public:
+ RenderSelect(DOM::HTMLSelectElementImpl *element);
+
+ virtual const char *renderName() const { return "RenderSelect"; }
+
+ virtual void calcMinMaxWidth();
+ virtual void layout();
+
+ void setOptionsChanged(bool _optionsChanged);
+
+ bool selectionChanged() { return m_selectionChanged; }
+ void setSelectionChanged(bool _selectionChanged) { m_selectionChanged = _selectionChanged; }
+ virtual void updateFromElement();
+
+ void updateSelection();
+
+ DOM::HTMLSelectElementImpl *element() const
+ { return static_cast<DOM::HTMLSelectElementImpl*>(RenderObject::element()); }
+
+protected:
+ KListBox *createListBox();
+ ComboBoxWidget *createComboBox();
+
+ unsigned m_size;
+ bool m_multiple;
+ bool m_useListBox;
+ bool m_selectionChanged;
+ bool m_ignoreSelectEvents;
+ bool m_optionsChanged;
+
+protected slots:
+ void slotSelected(int index);
+ void slotSelectionChanged();
+};
+
+// -------------------------------------------------------------------------
+class TextAreaWidget : public KTextEdit
+{
+ Q_OBJECT
+public:
+ TextAreaWidget(int wrap, QWidget* parent);
+ virtual ~TextAreaWidget();
+
+protected:
+ virtual bool event (QEvent *e );
+ virtual QPopupMenu *createPopupMenu(const QPoint& pos);
+ virtual QPopupMenu* createPopupMenu() { return KTextEdit::createPopupMenu(); }
+private slots:
+ void slotFind();
+ void slotDoFind();
+ void slotFindNext();
+ void slotReplace();
+ void slotDoReplace();
+ void slotReplaceNext();
+ void slotReplaceText(const QString&, int, int, int);
+ void slotFindHighlight(const QString&, int, int);
+private:
+ KFindDialog *m_findDlg;
+ KFind *m_find;
+ KReplaceDialog *m_repDlg;
+ KReplace *m_replace;
+ KAction *m_findAction;
+ KAction *m_findNextAction;
+ KAction *m_replaceAction;
+ int m_findIndex, m_findPara;
+ int m_repIndex, m_repPara;
+};
+
+
+// -------------------------------------------------------------------------
+
+class RenderTextArea : public RenderFormElement
+{
+ Q_OBJECT
+public:
+ RenderTextArea(DOM::HTMLTextAreaElementImpl *element);
+ ~RenderTextArea();
+
+ virtual const char *renderName() const { return "RenderTextArea"; }
+ virtual void calcMinMaxWidth();
+ virtual void layout();
+ virtual void setStyle(RenderStyle *style);
+
+ virtual void close ( );
+ virtual void updateFromElement();
+
+ // don't even think about making this method virtual!
+ TextAreaWidget *widget() const { return static_cast<TextAreaWidget*>(m_widget); }
+ DOM::HTMLTextAreaElementImpl* element() const
+ { return static_cast<DOM::HTMLTextAreaElementImpl*>(RenderObject::element()); }
+
+ QString text();
+ void highLightWord( unsigned int length, unsigned int pos );
+
+ void select();
+
+ long selectionStart();
+ long selectionEnd();
+ void setSelectionStart(long pos);
+ void setSelectionEnd(long pos);
+ void setSelectionRange(long start, long end);
+protected slots:
+ void slotTextChanged();
+
+protected:
+ virtual void handleFocusOut();
+
+ virtual bool isEditable() const { return true; }
+ virtual bool canHaveBorder() const { return true; }
+
+ bool scrollbarsStyled;
+private:
+ //Convert para, index -> offset
+ long computeCharOffset(int para, int index);
+
+ //Convert offset -> para, index
+ void computeParagraphAndIndex(long offset, int* para, int* index);
+
+ //Helper for doing the conversion..
+ enum Mode { ParaLength, //Returns the length of the entire paragraph
+ ParaPortionLength, //Return length of paragraph portion set by threshold
+ ParaPortionOffset }; //Return offset that matches the length threshold.
+ int queryParagraphInfo(int para, Mode m, int param = -1);
+};
+
+// -------------------------------------------------------------------------
+
+} //namespace
+
+#endif
diff --git a/khtml/rendering/render_frames.cpp b/khtml/rendering/render_frames.cpp
new file mode 100644
index 000000000..7ee69b239
--- /dev/null
+++ b/khtml/rendering/render_frames.cpp
@@ -0,0 +1,1010 @@
+/**
+ * This file is part of the KDE project.
+ *
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Simon Hausmann <hausmann@kde.org>
+ * (C) 2000 Stefan Schimanski (1Stein@gmx.de)
+ * (C) 2003 Apple Computer, Inc.
+ * (C) 2005 Niels Leenheer <niels.leenheer@gmail.com>
+ *
+ * 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.
+ *
+ */
+//#define DEBUG_LAYOUT
+
+#include "rendering/render_frames.h"
+#include "rendering/render_canvas.h"
+#include "html/html_baseimpl.h"
+#include "html/html_objectimpl.h"
+#include "html/htmltokenizer.h"
+#include "misc/htmlattrs.h"
+#include "xml/dom2_eventsimpl.h"
+#include "xml/dom_docimpl.h"
+#include "misc/htmltags.h"
+#include "khtmlview.h"
+#include "khtml_part.h"
+
+#include <kapplication.h>
+#include <kmessagebox.h>
+#include <kmimetype.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <qtimer.h>
+#include <qpainter.h>
+#include <qcursor.h>
+
+#include <assert.h>
+
+using namespace khtml;
+using namespace DOM;
+
+RenderFrameSet::RenderFrameSet( HTMLFrameSetElementImpl *frameSet)
+ : RenderBox(frameSet)
+{
+ // init RenderObject attributes
+ setInline(false);
+
+ for (int k = 0; k < 2; ++k) {
+ m_gridLen[k] = -1;
+ m_gridDelta[k] = 0;
+ m_gridLayout[k] = 0;
+ }
+
+ m_resizing = m_clientresizing= false;
+
+ m_cursor = Qt::ArrowCursor;
+
+ m_hSplit = -1;
+ m_vSplit = -1;
+
+ m_hSplitVar = 0;
+ m_vSplitVar = 0;
+}
+
+RenderFrameSet::~RenderFrameSet()
+{
+ for (int k = 0; k < 2; ++k) {
+ delete [] m_gridLayout[k];
+ delete [] m_gridDelta[k];
+ }
+ delete [] m_hSplitVar;
+ delete [] m_vSplitVar;
+}
+
+bool RenderFrameSet::nodeAtPoint(NodeInfo& info, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction, bool inBox)
+{
+ RenderBox::nodeAtPoint(info, _x, _y, _tx, _ty, hitTestAction, inBox);
+
+ bool inside = m_resizing || canResize(_x, _y);
+
+ if ( inside && element() && !element()->noResize() && !info.readonly()) {
+ info.setInnerNode(element());
+ info.setInnerNonSharedNode(element());
+ }
+
+ return inside || m_clientresizing;
+}
+
+void RenderFrameSet::layout( )
+{
+ KHTMLAssert( needsLayout() );
+ KHTMLAssert( minMaxKnown() );
+
+ if ( !parent()->isFrameSet() ) {
+ KHTMLView* view = canvas()->view();
+ m_width = view ? view->visibleWidth() : 0;
+ m_height = view ? view->visibleHeight() : 0;
+ }
+
+#ifdef DEBUG_LAYOUT
+ kdDebug( 6040 ) << renderName() << "(FrameSet)::layout( ) width=" << width() << ", height=" << height() << endl;
+#endif
+
+ int remainingLen[2];
+ remainingLen[1] = m_width - (element()->totalCols()-1)*element()->border();
+ if(remainingLen[1]<0) remainingLen[1]=0;
+ remainingLen[0] = m_height - (element()->totalRows()-1)*element()->border();
+ if(remainingLen[0]<0) remainingLen[0]=0;
+
+ int availableLen[2];
+ availableLen[0] = remainingLen[0];
+ availableLen[1] = remainingLen[1];
+
+ if (m_gridLen[0] != element()->totalRows() || m_gridLen[1] != element()->totalCols()) {
+ // number of rows or cols changed
+ // need to zero out the deltas
+ m_gridLen[0] = element()->totalRows();
+ m_gridLen[1] = element()->totalCols();
+ for (int k = 0; k < 2; ++k) {
+ delete [] m_gridDelta[k];
+ m_gridDelta[k] = new int[m_gridLen[k]];
+ delete [] m_gridLayout[k];
+ m_gridLayout[k] = new int[m_gridLen[k]];
+ for (int i = 0; i < m_gridLen[k]; ++i)
+ m_gridDelta[k][i] = 0;
+ }
+ }
+
+ for (int k = 0; k < 2; ++k) {
+ int totalRelative = 0;
+ int totalFixed = 0;
+ int totalPercent = 0;
+ int countRelative = 0;
+ int countFixed = 0;
+ int countPercent = 0;
+ int gridLen = m_gridLen[k];
+ int* gridDelta = m_gridDelta[k];
+ khtml::Length* grid = k ? element()->m_cols : element()->m_rows;
+ int* gridLayout = m_gridLayout[k];
+
+ if (grid) {
+ // First we need to investigate how many columns of each type we have and
+ // how much space these columns are going to require.
+ for (int i = 0; i < gridLen; ++i) {
+ // Count the total length of all of the fixed columns/rows -> totalFixed
+ // Count the number of columns/rows which are fixed -> countFixed
+ if (grid[i].isFixed()) {
+ gridLayout[i] = kMax(grid[i].value(), 0);
+ totalFixed += gridLayout[i];
+ countFixed++;
+ }
+
+ // Count the total percentage of all of the percentage columns/rows -> totalPercent
+ // Count the number of columns/rows which are percentages -> countPercent
+ if (grid[i].isPercent()) {
+ gridLayout[i] = kMax(grid[i].width(availableLen[k]), 0);
+ totalPercent += gridLayout[i];
+ countPercent++;
+ }
+
+ // Count the total relative of all the relative columns/rows -> totalRelative
+ // Count the number of columns/rows which are relative -> countRelative
+ if (grid[i].isRelative()) {
+ totalRelative += kMax(grid[i].value(), 1);
+ countRelative++;
+ }
+ }
+
+ // Fixed columns/rows are our first priority. If there is not enough space to fit all fixed
+ // columns/rows we need to proportionally adjust their size.
+ if (totalFixed > remainingLen[k]) {
+ int remainingFixed = remainingLen[k];
+
+ for (int i = 0; i < gridLen; ++i) {
+ if (grid[i].isFixed()) {
+ gridLayout[i] = (gridLayout[i] * remainingFixed) / totalFixed;
+ remainingLen[k] -= gridLayout[i];
+ }
+ }
+ } else {
+ remainingLen[k] -= totalFixed;
+ }
+
+ // Percentage columns/rows are our second priority. Divide the remaining space proportionally
+ // over all percentage columns/rows. IMPORTANT: the size of each column/row is not relative
+ // to 100%, but to the total percentage. For example, if there are three columns, each of 75%,
+ // and the available space is 300px, each column will become 100px in width.
+ if (totalPercent > remainingLen[k]) {
+ int remainingPercent = remainingLen[k];
+
+ for (int i = 0; i < gridLen; ++i) {
+ if (grid[i].isPercent()) {
+ gridLayout[i] = (gridLayout[i] * remainingPercent) / totalPercent;
+ remainingLen[k] -= gridLayout[i];
+ }
+ }
+ } else {
+ remainingLen[k] -= totalPercent;
+ }
+
+ // Relative columns/rows are our last priority. Divide the remaining space proportionally
+ // over all relative columns/rows. IMPORTANT: the relative value of 0* is treated as 1*.
+ if (countRelative) {
+ int lastRelative = 0;
+ int remainingRelative = remainingLen[k];
+
+ for (int i = 0; i < gridLen; ++i) {
+ if (grid[i].isRelative()) {
+ gridLayout[i] = (kMax(grid[i].value(), 1) * remainingRelative) / totalRelative;
+ remainingLen[k] -= gridLayout[i];
+ lastRelative = i;
+ }
+ }
+
+ // If we could not evently distribute the available space of all of the relative
+ // columns/rows, the remainder will be added to the last column/row.
+ // For example: if we have a space of 100px and three columns (*,*,*), the remainder will
+ // be 1px and will be added to the last column: 33px, 33px, 34px.
+ if (remainingLen[k]) {
+ gridLayout[lastRelative] += remainingLen[k];
+ remainingLen[k] = 0;
+ }
+ }
+
+ // If we still have some left over space we need to divide it over the already existing
+ // columns/rows
+ if (remainingLen[k]) {
+ // Our first priority is to spread if over the percentage columns. The remaining
+ // space is spread evenly, for example: if we have a space of 100px, the columns
+ // definition of 25%,25% used to result in two columns of 25px. After this the
+ // columns will each be 50px in width.
+ if (countPercent && totalPercent) {
+ int remainingPercent = remainingLen[k];
+ int changePercent = 0;
+
+ for (int i = 0; i < gridLen; ++i) {
+ if (grid[i].isPercent()) {
+ changePercent = (remainingPercent * gridLayout[i]) / totalPercent;
+ gridLayout[i] += changePercent;
+ remainingLen[k] -= changePercent;
+ }
+ }
+ } else if (totalFixed) {
+ // Our last priority is to spread the remaining space over the fixed columns.
+ // For example if we have 100px of space and two column of each 40px, both
+ // columns will become exactly 50px.
+ int remainingFixed = remainingLen[k];
+ int changeFixed = 0;
+
+ for (int i = 0; i < gridLen; ++i) {
+ if (grid[i].isFixed()) {
+ changeFixed = (remainingFixed * gridLayout[i]) / totalFixed;
+ gridLayout[i] += changeFixed;
+ remainingLen[k] -= changeFixed;
+ }
+ }
+ }
+ }
+
+ // If we still have some left over space we probably ended up with a remainder of
+ // a division. We can not spread it evenly anymore. If we have any percentage
+ // columns/rows simply spread the remainder equally over all available percentage columns,
+ // regardless of their size.
+ if (remainingLen[k] && countPercent) {
+ int remainingPercent = remainingLen[k];
+ int changePercent = 0;
+
+ for (int i = 0; i < gridLen; ++i) {
+ if (grid[i].isPercent()) {
+ changePercent = remainingPercent / countPercent;
+ gridLayout[i] += changePercent;
+ remainingLen[k] -= changePercent;
+ }
+ }
+ }
+
+ // If we don't have any percentage columns/rows we only have fixed columns. Spread
+ // the remainder equally over all fixed columns/rows.
+ else if (remainingLen[k] && countFixed) {
+ int remainingFixed = remainingLen[k];
+ int changeFixed = 0;
+
+ for (int i = 0; i < gridLen; ++i) {
+ if (grid[i].isFixed()) {
+ changeFixed = remainingFixed / countFixed;
+ gridLayout[i] += changeFixed;
+ remainingLen[k] -= changeFixed;
+ }
+ }
+ }
+
+ // Still some left over... simply add it to the last column, because it is impossible
+ // spread it evenly or equally.
+ if (remainingLen[k]) {
+ gridLayout[gridLen - 1] += remainingLen[k];
+ }
+
+ // now we have the final layout, distribute the delta over it
+ bool worked = true;
+ for (int i = 0; i < gridLen; ++i) {
+ if (gridLayout[i] && gridLayout[i] + gridDelta[i] <= 0)
+ worked = false;
+ gridLayout[i] += gridDelta[i];
+ }
+ // now the delta's broke something, undo it and reset deltas
+ if (!worked)
+ for (int i = 0; i < gridLen; ++i) {
+ gridLayout[i] -= gridDelta[i];
+ gridDelta[i] = 0;
+ }
+ }
+ else
+ gridLayout[0] = remainingLen[k];
+ }
+
+ positionFrames();
+
+ RenderObject *child = firstChild();
+ if ( !child )
+ goto end2;
+
+ if(!m_hSplitVar && !m_vSplitVar)
+ {
+#ifdef DEBUG_LAYOUT
+ kdDebug( 6031 ) << "calculationg fixed Splitters" << endl;
+#endif
+ if(!m_vSplitVar && element()->totalCols() > 1)
+ {
+ m_vSplitVar = new bool[element()->totalCols()];
+ for(int i = 0; i < element()->totalCols(); i++) m_vSplitVar[i] = true;
+ }
+ if(!m_hSplitVar && element()->totalRows() > 1)
+ {
+ m_hSplitVar = new bool[element()->totalRows()];
+ for(int i = 0; i < element()->totalRows(); i++) m_hSplitVar[i] = true;
+ }
+
+ for(int r = 0; r < element()->totalRows(); r++)
+ {
+ for(int c = 0; c < element()->totalCols(); c++)
+ {
+ bool fixed = false;
+
+ if ( child->isFrameSet() )
+ fixed = static_cast<RenderFrameSet *>(child)->element()->noResize();
+ else
+ fixed = static_cast<RenderFrame *>(child)->element()->noResize();
+
+ if(fixed)
+ {
+#ifdef DEBUG_LAYOUT
+ kdDebug( 6031 ) << "found fixed cell " << r << "/" << c << "!" << endl;
+#endif
+ if( element()->totalCols() > 1)
+ {
+ if(c>0) m_vSplitVar[c-1] = false;
+ m_vSplitVar[c] = false;
+ }
+ if( element()->totalRows() > 1)
+ {
+ if(r>0) m_hSplitVar[r-1] = false;
+ m_hSplitVar[r] = false;
+ }
+ child = child->nextSibling();
+ if(!child) goto end2;
+ }
+#ifdef DEBUG_LAYOUT
+ else
+ kdDebug( 6031 ) << "not fixed: " << r << "/" << c << "!" << endl;
+#endif
+ }
+ }
+
+ }
+ RenderContainer::layout();
+ end2:
+ setNeedsLayout(false);
+}
+
+void RenderFrameSet::positionFrames()
+{
+ int r;
+ int c;
+
+ RenderObject *child = firstChild();
+ if ( !child )
+ return;
+
+ // NodeImpl *child = _first;
+ // if(!child) return;
+
+ int yPos = 0;
+
+ for(r = 0; r < element()->totalRows(); r++)
+ {
+ int xPos = 0;
+ for(c = 0; c < element()->totalCols(); c++)
+ {
+ child->setPos( xPos, yPos );
+#ifdef DEBUG_LAYOUT
+ kdDebug(6040) << "child frame at (" << xPos << "/" << yPos << ") size (" << m_gridLayout[1][c] << "/" << m_gridLayout[0][r] << ")" << endl;
+#endif
+ // has to be resized and itself resize its contents
+ if ((m_gridLayout[1][c] != child->width()) || (m_gridLayout[0][r] != child->height())) {
+ child->setWidth( m_gridLayout[1][c] );
+ child->setHeight( m_gridLayout[0][r] );
+ child->setNeedsLayout(true);
+ child->layout();
+ }
+
+ xPos += m_gridLayout[1][c] + element()->border();
+ child = child->nextSibling();
+
+ if ( !child )
+ return;
+
+ }
+
+ yPos += m_gridLayout[0][r] + element()->border();
+ }
+
+ // all the remaining frames are hidden to avoid ugly
+ // spurious unflowed frames
+ while ( child ) {
+ child->setWidth( 0 );
+ child->setHeight( 0 );
+ child->setNeedsLayout(false);
+
+ child = child->nextSibling();
+ }
+}
+
+bool RenderFrameSet::userResize( MouseEventImpl *evt )
+{
+ if (needsLayout()) return false;
+
+ bool res = false;
+ int _x = evt->clientX();
+ int _y = evt->clientY();
+
+ if ( !m_resizing && evt->id() == EventImpl::MOUSEMOVE_EVENT || evt->id() == EventImpl::MOUSEDOWN_EVENT )
+ {
+#ifdef DEBUG_LAYOUT
+ kdDebug( 6031 ) << "mouseEvent:check" << endl;
+#endif
+
+ m_hSplit = -1;
+ m_vSplit = -1;
+ //bool resizePossible = true;
+
+ // check if we're over a horizontal or vertical boundary
+ int pos = m_gridLayout[1][0] + xPos();
+ for(int c = 1; c < element()->totalCols(); c++)
+ {
+ if(_x >= pos && _x <= pos+element()->border())
+ {
+ if(m_vSplitVar && m_vSplitVar[c-1] == true) m_vSplit = c-1;
+#ifdef DEBUG_LAYOUT
+ kdDebug( 6031 ) << "vsplit!" << endl;
+#endif
+ res = true;
+ break;
+ }
+ pos += m_gridLayout[1][c] + element()->border();
+ }
+
+ pos = m_gridLayout[0][0] + yPos();
+ for(int r = 1; r < element()->totalRows(); r++)
+ {
+ if( _y >= pos && _y <= pos+element()->border())
+ {
+ if(m_hSplitVar && m_hSplitVar[r-1] == true) m_hSplit = r-1;
+#ifdef DEBUG_LAYOUT
+ kdDebug( 6031 ) << "hsplitvar = " << m_hSplitVar << endl;
+ kdDebug( 6031 ) << "hsplit!" << endl;
+#endif
+ res = true;
+ break;
+ }
+ pos += m_gridLayout[0][r] + element()->border();
+ }
+#ifdef DEBUG_LAYOUT
+ kdDebug( 6031 ) << m_hSplit << "/" << m_vSplit << endl;
+#endif
+ }
+
+
+ m_cursor = Qt::ArrowCursor;
+ if(m_hSplit != -1 && m_vSplit != -1)
+ m_cursor = Qt::SizeAllCursor;
+ else if( m_vSplit != -1 )
+ m_cursor = Qt::SizeHorCursor;
+ else if( m_hSplit != -1 )
+ m_cursor = Qt::SizeVerCursor;
+
+ if(!m_resizing && evt->id() == EventImpl::MOUSEDOWN_EVENT)
+ {
+ setResizing(true);
+ KApplication::setOverrideCursor(QCursor(m_cursor));
+ m_vSplitPos = _x;
+ m_hSplitPos = _y;
+ m_oldpos = -1;
+ }
+
+ // ### check the resize is not going out of bounds.
+ if(m_resizing && evt->id() == EventImpl::MOUSEUP_EVENT)
+ {
+ setResizing(false);
+ KApplication::restoreOverrideCursor();
+
+ if(m_vSplit != -1 )
+ {
+#ifdef DEBUG_LAYOUT
+ kdDebug( 6031 ) << "split xpos=" << _x << endl;
+#endif
+ int delta = m_vSplitPos - _x;
+ m_gridDelta[1][m_vSplit] -= delta;
+ m_gridDelta[1][m_vSplit+1] += delta;
+ }
+ if(m_hSplit != -1 )
+ {
+#ifdef DEBUG_LAYOUT
+ kdDebug( 6031 ) << "split ypos=" << _y << endl;
+#endif
+ int delta = m_hSplitPos - _y;
+ m_gridDelta[0][m_hSplit] -= delta;
+ m_gridDelta[0][m_hSplit+1] += delta;
+ }
+
+ // this just schedules the relayout
+ // important, otherwise the moving indicator is not correctly erased
+ setNeedsLayout(true);
+ }
+
+ KHTMLView *view = canvas()->view();
+ if ((m_resizing || evt->id() == EventImpl::MOUSEUP_EVENT) && view) {
+ QPainter paint( view );
+ paint.setPen( Qt::gray );
+ paint.setBrush( Qt::gray );
+ paint.setRasterOp( Qt::XorROP );
+ QRect r(xPos(), yPos(), width(), height());
+ const int rBord = 3;
+ int sw = element()->border();
+ int p = m_resizing ? (m_vSplit > -1 ? _x : _y) : -1;
+ if (m_vSplit > -1) {
+ if ( m_oldpos >= 0 )
+ paint.drawRect( m_oldpos + sw/2 - rBord , r.y(),
+ 2*rBord, r.height() );
+ if ( p >= 0 )
+ paint.drawRect( p + sw/2 - rBord, r.y(), 2*rBord, r.height() );
+ } else {
+ if ( m_oldpos >= 0 )
+ paint.drawRect( r.x(), m_oldpos + sw/2 - rBord,
+ r.width(), 2*rBord );
+ if ( p >= 0 )
+ paint.drawRect( r.x(), p + sw/2 - rBord, r.width(), 2*rBord );
+ }
+ m_oldpos = p;
+ }
+
+ return res;
+}
+
+void RenderFrameSet::setResizing(bool e)
+{
+ m_resizing = e;
+ for (RenderObject* p = parent(); p; p = p->parent())
+ if (p->isFrameSet()) static_cast<RenderFrameSet*>(p)->m_clientresizing = m_resizing;
+}
+
+bool RenderFrameSet::canResize( int _x, int _y )
+{
+ // if we haven't received a layout, then the gridLayout doesn't contain useful data yet
+ if (needsLayout() || !m_gridLayout[0] || !m_gridLayout[1] ) return false;
+
+ // check if we're over a horizontal or vertical boundary
+ int pos = m_gridLayout[1][0];
+ for(int c = 1; c < element()->totalCols(); c++)
+ if(_x >= pos && _x <= pos+element()->border())
+ return true;
+
+ pos = m_gridLayout[0][0];
+ for(int r = 1; r < element()->totalRows(); r++)
+ if( _y >= pos && _y <= pos+element()->border())
+ return true;
+
+ return false;
+}
+
+#ifdef ENABLE_DUMP
+void RenderFrameSet::dump(QTextStream &stream, const QString &ind) const
+{
+ RenderBox::dump(stream,ind);
+ stream << " totalrows=" << element()->totalRows();
+ stream << " totalcols=" << element()->totalCols();
+
+ if ( m_hSplitVar )
+ for (uint i = 0; i < (uint)element()->totalRows(); i++) {
+ stream << " hSplitvar(" << i << ")=" << m_hSplitVar[i];
+ }
+
+ if ( m_vSplitVar )
+ for (uint i = 0; i < (uint)element()->totalCols(); i++)
+ stream << " vSplitvar(" << i << ")=" << m_vSplitVar[i];
+}
+#endif
+
+/**************************************************************************************/
+
+RenderPart::RenderPart(DOM::HTMLElementImpl* node)
+ : RenderWidget(node)
+{
+ // init RenderObject attributes
+ setInline(false);
+}
+
+void RenderPart::setWidget( QWidget *widget )
+{
+#ifdef DEBUG_LAYOUT
+ kdDebug(6031) << "RenderPart::setWidget()" << endl;
+#endif
+
+ setQWidget( widget );
+ widget->setFocusPolicy(QWidget::WheelFocus);
+ if(widget->inherits("KHTMLView"))
+ connect( widget, SIGNAL( cleared() ), this, SLOT( slotViewCleared() ) );
+
+ setNeedsLayoutAndMinMaxRecalc();
+
+ // make sure the scrollbars are set correctly for restore
+ // ### find better fix
+ slotViewCleared();
+}
+
+bool RenderPart::partLoadingErrorNotify(khtml::ChildFrame *, const KURL& , const QString& )
+{
+ return false;
+}
+
+short RenderPart::intrinsicWidth() const
+{
+ return 300;
+}
+
+int RenderPart::intrinsicHeight() const
+{
+ return 150;
+}
+
+void RenderPart::slotViewCleared()
+{
+}
+
+/***************************************************************************************/
+
+RenderFrame::RenderFrame( DOM::HTMLFrameElementImpl *frame )
+ : RenderPart(frame)
+{
+ setInline( false );
+}
+
+void RenderFrame::slotViewCleared()
+{
+ if(m_widget->inherits("QScrollView")) {
+#ifdef DEBUG_LAYOUT
+ kdDebug(6031) << "frame is a scrollview!" << endl;
+#endif
+ QScrollView *view = static_cast<QScrollView *>(m_widget);
+ if(!element()->frameBorder || !((static_cast<HTMLFrameSetElementImpl *>(element()->parentNode()))->frameBorder()))
+ view->setFrameStyle(QFrame::NoFrame);
+ view->setVScrollBarMode(element()->scrolling );
+ view->setHScrollBarMode(element()->scrolling );
+ if(view->inherits("KHTMLView")) {
+#ifdef DEBUG_LAYOUT
+ kdDebug(6031) << "frame is a KHTMLview!" << endl;
+#endif
+ KHTMLView *htmlView = static_cast<KHTMLView *>(view);
+ if(element()->marginWidth != -1) htmlView->setMarginWidth(element()->marginWidth);
+ if(element()->marginHeight != -1) htmlView->setMarginHeight(element()->marginHeight);
+ }
+ }
+}
+
+/****************************************************************************************/
+
+RenderPartObject::RenderPartObject( DOM::HTMLElementImpl* element )
+ : RenderPart( element )
+{
+ // init RenderObject attributes
+ setInline(true);
+}
+
+void RenderPartObject::updateWidget()
+{
+ QString url;
+ KHTMLPart *part = m_view->part();
+
+ setNeedsLayoutAndMinMaxRecalc();
+
+ if (element()->id() == ID_IFRAME) {
+
+ HTMLIFrameElementImpl *o = static_cast<HTMLIFrameElementImpl *>(element());
+ url = o->url.string();
+ if (!o->getDocument()->isURLAllowed(url)) return;
+ part->requestFrame( this, url, o->name.string(), QStringList(), true );
+ // ### this should be constant true - move iframe to somewhere else
+ } else {
+
+ QStringList params;
+ HTMLObjectBaseElementImpl * objbase = static_cast<HTMLObjectBaseElementImpl *>(element());
+ url = objbase->url;
+
+ for (NodeImpl* child = element()->firstChild(); child; child=child->nextSibling()) {
+ if ( child->id() == ID_PARAM ) {
+ HTMLParamElementImpl *p = static_cast<HTMLParamElementImpl *>( child );
+
+ QString aStr = p->name();
+ aStr += QString::fromLatin1("=\"");
+ aStr += p->value();
+ aStr += QString::fromLatin1("\"");
+ QString name_lower = p->name().lower();
+ if (name_lower == QString::fromLatin1("type") && objbase->id() != ID_APPLET) {
+ objbase->setServiceType(p->value());
+ } else if (url.isEmpty() &&
+ (name_lower == QString::fromLatin1("src") ||
+ name_lower == QString::fromLatin1("movie") ||
+ name_lower == QString::fromLatin1("code"))) {
+ url = p->value();
+ }
+ params.append(aStr);
+ }
+ }
+ if (element()->id() != ID_OBJECT) {
+ // add all attributes set on the embed object
+ NamedAttrMapImpl* a = objbase->attributes();
+ if (a) {
+ for (unsigned long i = 0; i < a->length(); ++i) {
+ NodeImpl::Id id = a->idAt(i);
+ DOMString value = a->valueAt(i);
+ params.append(objbase->getDocument()->getName(NodeImpl::AttributeId, id).string() + "=\"" + value.string() + "\"");
+ }
+ }
+ }
+ params.append( QString::fromLatin1("__KHTML__PLUGINEMBED=\"YES\"") );
+ params.append( QString::fromLatin1("__KHTML__PLUGINBASEURL=\"%1\"").arg(element()->getDocument()->baseURL().url()));
+
+ HTMLEmbedElementImpl *embed = 0;
+ QString classId;
+ QString serviceType = objbase->serviceType;
+ if ( element()->id() == ID_EMBED ) {
+
+ embed = static_cast<HTMLEmbedElementImpl *>( objbase );
+
+ }
+ else { // if(element()->id() == ID_OBJECT || element()->id() == ID_APPLET)
+
+ // check for embed child object
+ for (NodeImpl *child = objbase->firstChild(); child; child = child->nextSibling())
+ if ( child->id() == ID_EMBED ) {
+ embed = static_cast<HTMLEmbedElementImpl *>( child );
+ break;
+ }
+ classId = objbase->classId;
+
+ params.append( QString::fromLatin1("__KHTML__CLASSID=\"%1\"").arg( classId ) );
+ params.append( QString::fromLatin1("__KHTML__CODEBASE=\"%1\"").arg( objbase->getAttribute(ATTR_CODEBASE).string() ) );
+ if (!objbase->getAttribute(ATTR_WIDTH).isEmpty())
+ params.append( QString::fromLatin1("WIDTH=\"%1\"").arg( objbase->getAttribute(ATTR_WIDTH).string() ) );
+ else if (embed && !embed->getAttribute(ATTR_WIDTH).isEmpty()) {
+ params.append( QString::fromLatin1("WIDTH=\"%1\"").arg( embed->getAttribute(ATTR_WIDTH).string() ) );
+ objbase->setAttribute(ATTR_WIDTH, embed->getAttribute(ATTR_WIDTH));
+ }
+ if (!objbase->getAttribute(ATTR_HEIGHT).isEmpty())
+ params.append( QString::fromLatin1("HEIGHT=\"%1\"").arg( objbase->getAttribute(ATTR_HEIGHT).string() ) );
+ else if (embed && !embed->getAttribute(ATTR_HEIGHT).isEmpty()) {
+ params.append( QString::fromLatin1("HEIGHT=\"%1\"").arg( embed->getAttribute(ATTR_HEIGHT).string() ) );
+ objbase->setAttribute(ATTR_HEIGHT, embed->getAttribute(ATTR_HEIGHT));
+ }
+
+ if ( embed ) {
+ // render embed object
+ url = embed->url;
+ if (!embed->serviceType.isEmpty())
+ serviceType = embed->serviceType;
+ } else if (url.isEmpty() && objbase->classId.startsWith("java:")) {
+ serviceType = "application/x-java-applet";
+ url = objbase->classId.mid(5);
+ }
+ if ( (serviceType.isEmpty() ||
+ serviceType == "application/x-oleobject") &&
+ !objbase->classId.isEmpty())
+ {
+#if 0
+ // We have a clsid, means this is activex (Niko)
+ serviceType = "application/x-activex-handler";
+#endif
+
+ if(classId.find(QString::fromLatin1("D27CDB6E-AE6D-11cf-96B8-444553540000")) >= 0) {
+ // It is ActiveX, but the nsplugin system handling
+ // should also work, that's why we don't override the
+ // serviceType with application/x-activex-handler
+ // but let the KTrader in khtmlpart::createPart() detect
+ // the user's preference: launch with activex viewer or
+ // with nspluginviewer (Niko)
+ serviceType = "application/x-shockwave-flash";
+ }
+ else if(classId.find(QString::fromLatin1("CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA")) >= 0)
+ serviceType = "audio/x-pn-realaudio-plugin";
+ else if(classId.find(QString::fromLatin1("8AD9C840-044E-11D1-B3E9-00805F499D93")) >= 0 ||
+ objbase->classId.find(QString::fromLatin1("CAFEEFAC-0014-0000-0000-ABCDEFFEDCBA")) >= 0)
+ serviceType = "application/x-java-applet";
+ // http://www.apple.com/quicktime/tools_tips/tutorials/activex.html
+ else if(classId.find(QString::fromLatin1("02BF25D5-8C17-4B23-BC80-D3488ABDDC6B")) >= 0)
+ serviceType = "video/quicktime";
+ // http://msdn.microsoft.com/library/en-us/dnwmt/html/adding_windows_media_to_web_pages__etse.asp?frame=true
+ else if(objbase->classId.find(QString::fromLatin1("6BF52A52-394A-11d3-B153-00C04F79FAA6")) >= 0 ||
+ classId.find(QString::fromLatin1("22D6f312-B0F6-11D0-94AB-0080C74C7E95")) >= 0)
+ serviceType = "video/x-msvideo";
+
+ else
+ kdDebug(6031) << "ActiveX classId " << objbase->classId << endl;
+
+ // TODO: add more plugins here
+ }
+ }
+ if ((url.isEmpty() && !embed &&
+ (serviceType.isEmpty() || classId.isEmpty())) ||
+ !document()->isURLAllowed(url) ||
+ !part->requestObject( this, url, serviceType, params ))
+ objbase->renderAlternative();
+ }
+}
+
+// ugly..
+void RenderPartObject::close()
+{
+ RenderPart::close();
+
+ if ( element()->id() != ID_IFRAME )
+ updateWidget();
+ // deleted here
+}
+
+
+bool RenderPartObject::partLoadingErrorNotify( khtml::ChildFrame *childFrame, const KURL& url, const QString& serviceType )
+{
+ KHTMLPart *part = static_cast<KHTMLView *>(m_view)->part();
+ kdDebug(6031) << "RenderPartObject::partLoadingErrorNotify serviceType=" << serviceType << endl;
+ // Check if we just tried with e.g. nsplugin
+ // and fallback to the activexhandler if there is a classid
+ // and a codebase, where we may download the ocx if it's missing
+ if( serviceType != "application/x-activex-handler" && element()->id()==ID_OBJECT ) {
+
+ // check for embed child object
+ HTMLObjectElementImpl *o = static_cast<HTMLObjectElementImpl *>(element());
+ HTMLEmbedElementImpl *embed = 0;
+ NodeImpl *child = o->firstChild();
+ while ( child ) {
+ if ( child->id() == ID_EMBED )
+ embed = static_cast<HTMLEmbedElementImpl *>( child );
+
+ child = child->nextSibling();
+ }
+ if( embed && !o->classId.isEmpty() &&
+ !( static_cast<ElementImpl *>(o)->getAttribute(ATTR_CODEBASE).string() ).isEmpty() )
+ {
+ KParts::URLArgs args;
+ args.serviceType = "application/x-activex-handler";
+ kdDebug(6031) << "set to activex" << endl;
+ if (part->requestObject( childFrame, url, args ))
+ return true; // success
+
+ return false;
+ }
+ }
+ // Dissociate ourselves from the current event loop (to prevent crashes
+ // due to the message box staying up)
+ QTimer::singleShot( 0, this, SLOT( slotPartLoadingErrorNotify() ) );
+#if 0
+ Tokenizer *tokenizer = static_cast<DOM::DocumentImpl *>(part->document().handle())->tokenizer();
+ if (tokenizer) tokenizer->setOnHold( true );
+ slotPartLoadingErrorNotify();
+ if (tokenizer) tokenizer->setOnHold( false );
+#endif
+ return false;
+}
+
+void RenderPartObject::slotPartLoadingErrorNotify()
+{
+ // First we need to find out the servicetype - again - this code is too duplicated !
+ HTMLEmbedElementImpl *embed = 0;
+ QString serviceType;
+ if( element()->id()==ID_OBJECT ) {
+
+ // check for embed child object
+ HTMLObjectElementImpl *o = static_cast<HTMLObjectElementImpl *>(element());
+ serviceType = o->serviceType;
+ NodeImpl *child = o->firstChild();
+ while ( child ) {
+ if ( child->id() == ID_EMBED )
+ embed = static_cast<HTMLEmbedElementImpl *>( child );
+
+ child = child->nextSibling();
+ }
+
+ } else if( element()->id()==ID_EMBED ) {
+ embed = static_cast<HTMLEmbedElementImpl *>(element());
+ }
+ if ( embed )
+ serviceType = embed->serviceType;
+
+ // prepare for the local eventloop in KMessageBox
+ ref();
+
+ KHTMLPart *part = static_cast<KHTMLView *>(m_view)->part();
+ KParts::BrowserExtension *ext = part->browserExtension();
+ if( embed && !embed->pluginPage.isEmpty() && ext ) {
+ // Prepare the mimetype to show in the question (comment if available, name as fallback)
+ QString mimeName = serviceType;
+ KMimeType::Ptr mime = KMimeType::mimeType(serviceType);
+ if ( mime->name() != KMimeType::defaultMimeType() )
+ mimeName = mime->comment();
+
+ // Check if we already asked the user, for this page
+ if (!mimeName.isEmpty() && part->docImpl() && !part->pluginPageQuestionAsked( serviceType ) )
+ {
+ part->setPluginPageQuestionAsked( serviceType );
+ // Prepare the URL to show in the question (host only if http, to make it short)
+ KURL pluginPageURL( embed->pluginPage );
+ QString shortURL = pluginPageURL.protocol() == "http" ? pluginPageURL.host() : pluginPageURL.prettyURL();
+ int res = KMessageBox::questionYesNo( m_view,
+ i18n("No plugin found for '%1'.\nDo you want to download one from %2?").arg(mimeName).arg(shortURL),
+ i18n("Missing Plugin"), i18n("Download"), i18n("Do Not Download"), QString("plugin-")+serviceType);
+ if ( res == KMessageBox::Yes )
+ {
+ // Display vendor download page
+ ext->createNewWindow( pluginPageURL );
+ return;
+ }
+ }
+ }
+
+ // didn't work, render alternative content.
+ if ( element() && (
+ element()->id() == ID_OBJECT || element()->id() == ID_EMBED || element()->id() == ID_APPLET))
+ static_cast<HTMLObjectBaseElementImpl*>( element() )->renderAlternative();
+
+ deref();
+}
+
+void RenderPartObject::layout( )
+{
+ KHTMLAssert( needsLayout() );
+ KHTMLAssert( minMaxKnown() );
+
+ calcWidth();
+ calcHeight();
+
+ RenderPart::layout();
+
+ setNeedsLayout(false);
+}
+
+void RenderPartObject::slotViewCleared()
+{
+ if(m_widget->inherits("QScrollView") ) {
+#ifdef DEBUG_LAYOUT
+ kdDebug(6031) << "iframe is a scrollview!" << endl;
+#endif
+ QScrollView *view = static_cast<QScrollView *>(m_widget);
+ int frameStyle = QFrame::NoFrame;
+ QScrollView::ScrollBarMode scroll = QScrollView::Auto;
+ int marginw = -1;
+ int marginh = -1;
+ if ( element()->id() == ID_IFRAME) {
+ HTMLIFrameElementImpl *frame = static_cast<HTMLIFrameElementImpl *>(element());
+ if(frame->frameBorder)
+ frameStyle = QFrame::Box;
+ scroll = frame->scrolling;
+ marginw = frame->marginWidth;
+ marginh = frame->marginHeight;
+ }
+ view->setFrameStyle(frameStyle);
+ view->setVScrollBarMode(scroll );
+ view->setHScrollBarMode(scroll );
+ if(view->inherits("KHTMLView")) {
+#ifdef DEBUG_LAYOUT
+ kdDebug(6031) << "frame is a KHTMLview!" << endl;
+#endif
+ KHTMLView *htmlView = static_cast<KHTMLView *>(view);
+ htmlView->setIgnoreWheelEvents( element()->id() == ID_IFRAME );
+ if(marginw != -1) htmlView->setMarginWidth(marginw);
+ if(marginh != -1) htmlView->setMarginHeight(marginh);
+ }
+ }
+}
+
+#include "render_frames.moc"
diff --git a/khtml/rendering/render_frames.h b/khtml/rendering/render_frames.h
new file mode 100644
index 000000000..c6e050525
--- /dev/null
+++ b/khtml/rendering/render_frames.h
@@ -0,0 +1,172 @@
+/*
+ * This file is part of the KDE project.
+ *
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Simon Hausmann <hausmann@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef __render_frames_h__
+#define __render_frames_h__
+
+#include "rendering/render_replaced.h"
+#include "xml/dom_nodeimpl.h"
+#include "html/html_baseimpl.h"
+class KHTMLView;
+
+namespace DOM
+{
+ class HTMLFrameElementImpl;
+ class HTMLElementImpl;
+ class MouseEventImpl;
+}
+
+namespace khtml
+{
+ class ChildFrame;
+
+class RenderFrameSet : public RenderBox
+{
+ friend class DOM::HTMLFrameSetElementImpl;
+public:
+ RenderFrameSet( DOM::HTMLFrameSetElementImpl *frameSet );
+
+ virtual ~RenderFrameSet();
+
+ virtual const char *renderName() const { return "RenderFrameSet"; }
+ virtual bool isFrameSet() const { return true; }
+
+ virtual void layout();
+
+ void positionFrames( );
+
+ bool resizing() const { return m_resizing; }
+ bool noResize() const { return element()->noResize(); }
+
+ bool userResize( DOM::MouseEventImpl *evt );
+ bool canResize( int _x, int _y);
+ void setResizing(bool e);
+
+ Qt::CursorShape cursorShape() const { return m_cursor; }
+
+ bool nodeAtPoint(NodeInfo& info, int x, int y, int tx, int ty, HitTestAction hitTestAction, bool inside);
+
+ DOM::HTMLFrameSetElementImpl *element() const
+ { return static_cast<DOM::HTMLFrameSetElementImpl*>(RenderObject::element()); }
+
+#ifdef ENABLE_DUMP
+ virtual void dump(QTextStream &stream, const QString &ind) const;
+#endif
+
+private:
+ Qt::CursorShape m_cursor;
+ int m_oldpos;
+ int m_gridLen[2];
+ int* m_gridDelta[2];
+ int* m_gridLayout[2];
+
+ bool *m_hSplitVar; // is this split variable?
+ bool *m_vSplitVar;
+
+ int m_hSplit; // the split currently resized
+ int m_vSplit;
+ int m_hSplitPos;
+ int m_vSplitPos;
+
+ bool m_resizing;
+ bool m_clientresizing;
+};
+
+class RenderPart : public khtml::RenderWidget
+{
+ Q_OBJECT
+public:
+ RenderPart(DOM::HTMLElementImpl* node);
+
+ virtual const char *renderName() const { return "RenderPart"; }
+
+ virtual void setWidget( QWidget *widget );
+
+ /**
+ * Called by KHTMLPart to notify the frame object that loading the
+ * part was not successfuly. (called either asyncroniously after a
+ * after the servicetype of the given url (the one passed with requestObject)
+ * has been determined or syncroniously from within requestObject)
+ *
+ * The default implementation does nothing.
+ *
+ * Return false in the normal case, return true if a fallback was found
+ * and the url was successfully opened.
+ */
+ virtual bool partLoadingErrorNotify( khtml::ChildFrame *childFrame, const KURL& url, const QString& serviceType );
+
+ virtual short intrinsicWidth() const;
+ virtual int intrinsicHeight() const;
+
+public slots:
+ virtual void slotViewCleared();
+};
+
+class RenderFrame : public khtml::RenderPart
+{
+ Q_OBJECT
+public:
+ RenderFrame( DOM::HTMLFrameElementImpl *frame );
+
+ virtual const char *renderName() const { return "RenderFrame"; }
+ virtual bool isFrame() const { return true; }
+
+ // frames never have padding
+ virtual int paddingTop() const { return 0; }
+ virtual int paddingBottom() const { return 0; }
+ virtual int paddingLeft() const { return 0; }
+ virtual int paddingRight() const { return 0; }
+
+ DOM::HTMLFrameElementImpl *element() const
+ { return static_cast<DOM::HTMLFrameElementImpl*>(RenderObject::element()); }
+
+public slots:
+ void slotViewCleared();
+};
+
+// I can hardly call the class RenderObject ;-)
+class RenderPartObject : public khtml::RenderPart
+{
+ Q_OBJECT
+public:
+ RenderPartObject( DOM::HTMLElementImpl * );
+
+ virtual const char *renderName() const { return "RenderPartObject"; }
+
+ virtual void close();
+
+ virtual void layout( );
+ virtual void updateWidget();
+
+ virtual bool canHaveBorder() const { return true; }
+
+ virtual bool partLoadingErrorNotify( khtml::ChildFrame *childFrame, const KURL& url, const QString& serviceType );
+
+public slots:
+ void slotViewCleared();
+private slots:
+ void slotPartLoadingErrorNotify();
+};
+
+}
+
+#endif
diff --git a/khtml/rendering/render_generated.cpp b/khtml/rendering/render_generated.cpp
new file mode 100644
index 000000000..aeab48630
--- /dev/null
+++ b/khtml/rendering/render_generated.cpp
@@ -0,0 +1,392 @@
+/**
+ * This file is part of the HTML rendering engine for KDE.
+ *
+ * Copyright (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com)
+ *
+ * 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 "rendering/render_generated.h"
+#include "rendering/render_style.h"
+#include "rendering/enumerate.h"
+#include "rendering/counter_tree.h"
+#include "css/css_valueimpl.h"
+
+using namespace khtml;
+using namespace Enumerate;
+
+// -------------------------------------------------------------------------
+
+RenderCounterBase::RenderCounterBase(DOM::NodeImpl* node)
+ : RenderText(node,0), m_counterNode(0)
+{
+}
+
+void RenderCounterBase::layout()
+{
+ KHTMLAssert( needsLayout() );
+
+ if ( !minMaxKnown() )
+ calcMinMaxWidth();
+
+ RenderText::layout();
+}
+
+void RenderCounterBase::calcMinMaxWidth()
+{
+ KHTMLAssert( !minMaxKnown() );
+
+ generateContent();
+
+ if (str) str->deref();
+ str = new DOM::DOMStringImpl(m_item.unicode(), m_item.length());
+ str->ref();
+
+ RenderText::calcMinMaxWidth();
+}
+
+
+void RenderCounterBase::updateContent()
+{
+ setMinMaxKnown(false);
+}
+
+// -------------------------------------------------------------------------
+
+RenderCounter::RenderCounter(DOM::NodeImpl* node, const DOM::CounterImpl* counter)
+ : RenderCounterBase(node), m_counter(counter)
+{
+}
+
+QString RenderCounter::toListStyleType(int value, int total, EListStyleType type)
+{
+ QString item;
+ switch(type)
+ {
+ case LNONE:
+ break;
+// Glyphs: (these values are not really used and instead handled by RenderGlyph)
+ case LDISC:
+ item = QChar(0x2022);
+ break;
+ case LCIRCLE:
+ item = QChar(0x25e6);
+ break;
+ case LSQUARE:
+ item = QChar(0x25a0);
+ break;
+ case LBOX:
+ item = QChar(0x25a1);
+ break;
+ case LDIAMOND:
+ item = QChar(0x25c6);
+ break;
+// Numeric:
+ case LDECIMAL:
+ item.setNum ( value );
+ break;
+ case DECIMAL_LEADING_ZERO: {
+ int decimals = 2;
+ int t = total/100;
+ while (t>0) {
+ t = t/10;
+ decimals++;
+ }
+ decimals = kMax(decimals, 2);
+ QString num = QString::number(value);
+ item.fill('0',decimals-num.length());
+ item.append(num);
+ break;
+ }
+ case ARABIC_INDIC:
+ item = toArabicIndic( value );
+ break;
+ case LAO:
+ item = toLao( value );
+ break;
+ case PERSIAN:
+ case URDU:
+ item = toPersianUrdu( value );
+ break;
+ case THAI:
+ item = toThai( value );
+ break;
+ case TIBETAN:
+ item = toTibetan( value );
+ break;
+// Algoritmic:
+ case LOWER_ROMAN:
+ item = toRoman( value, false );
+ break;
+ case UPPER_ROMAN:
+ item = toRoman( value, true );
+ break;
+ case HEBREW:
+ item = toHebrew( value );
+ break;
+ case ARMENIAN:
+ item = toArmenian( value );
+ break;
+ case GEORGIAN:
+ item = toGeorgian( value );
+ break;
+// Alphabetic:
+ case LOWER_ALPHA:
+ case LOWER_LATIN:
+ item = toLowerLatin( value );
+ break;
+ case UPPER_ALPHA:
+ case UPPER_LATIN:
+ item = toUpperLatin( value );
+ break;
+ case LOWER_GREEK:
+ item = toLowerGreek( value );
+ break;
+ case UPPER_GREEK:
+ item = toUpperGreek( value );
+ break;
+ case HIRAGANA:
+ item = toHiragana( value );
+ break;
+ case HIRAGANA_IROHA:
+ item = toHiraganaIroha( value );
+ break;
+ case KATAKANA:
+ item = toKatakana( value );
+ break;
+ case KATAKANA_IROHA:
+ item = toKatakanaIroha( value );
+ break;
+// Ideographic:
+ case JAPANESE_FORMAL:
+ item = toJapaneseFormal( value );
+ break;
+ case JAPANESE_INFORMAL:
+ item = toJapaneseInformal( value );
+ break;
+ case SIMP_CHINESE_FORMAL:
+ item = toSimpChineseFormal( value );
+ break;
+ case SIMP_CHINESE_INFORMAL:
+ item = toSimpChineseInformal( value );
+ break;
+ case TRAD_CHINESE_FORMAL:
+ item = toTradChineseFormal( value );
+ break;
+ case CJK_IDEOGRAPHIC:
+ // CSS 3 List says treat as trad-chinese-informal
+ case TRAD_CHINESE_INFORMAL:
+ item = toTradChineseInformal( value );
+ break;
+ default:
+ item.setNum ( value );
+ break;
+ }
+ return item;
+}
+
+void RenderCounter::generateContent()
+{
+ bool counters;
+ counters = !m_counter->separator().isNull();
+
+ if (!m_counterNode)
+ m_counterNode = getCounter(m_counter->identifier().string(), true, counters);
+
+ int value = m_counterNode->count();
+ if (m_counterNode->isReset()) value = m_counterNode->value();
+ int total = value;
+ if (m_counterNode->parent()) total = m_counterNode->parent()->total();
+ m_item = toListStyleType(value, total, (EListStyleType)m_counter->listStyle());
+
+ if (counters) {
+ CounterNode *counter = m_counterNode->parent();
+ // we deliberately do not render the root counter-node
+ while(counter->parent() && !(counter->isReset() && counter->parent()->isRoot())) {
+ value = counter->count();
+ total = counter->parent()->total();
+ m_item = toListStyleType(value, total, (EListStyleType)m_counter->listStyle()) + m_counter->separator().string() + m_item;
+ counter = counter->parent();
+ };
+ }
+
+}
+
+// -------------------------------------------------------------------------
+
+RenderQuote::RenderQuote(DOM::NodeImpl* node, EQuoteContent type)
+ : RenderCounterBase(node), m_quoteType(type)
+{
+}
+
+
+int RenderQuote::quoteCount() const
+{
+ switch(m_quoteType) {
+ case OPEN_QUOTE:
+ case NO_OPEN_QUOTE:
+ return 1;
+ case CLOSE_QUOTE:
+ case NO_CLOSE_QUOTE:
+ return -1;
+ case NO_QUOTE:
+ return 0;
+ }
+ assert(false);
+ return 0;
+}
+
+void RenderQuote::generateContent()
+{
+ bool visual;
+ if (m_quoteType == NO_CLOSE_QUOTE || m_quoteType == NO_OPEN_QUOTE)
+ visual = false;
+ else
+ visual = true;
+
+ if (!m_counterNode)
+ m_counterNode = getCounter("-khtml-quotes", visual, false);
+
+ int value = m_counterNode->count();
+ if (m_counterNode->isReset()) value = m_counterNode->value();
+ switch (m_quoteType) {
+ case OPEN_QUOTE:
+ m_item = style()->openQuote( value );
+ break;
+ case CLOSE_QUOTE:
+ m_item = style()->closeQuote( value );
+ break;
+ case NO_OPEN_QUOTE:
+ case NO_CLOSE_QUOTE:
+ case NO_QUOTE:
+ m_item = QString();
+ }
+}
+
+// -------------------------------------------------------------------------
+
+RenderGlyph::RenderGlyph(DOM::NodeImpl* node, EListStyleType type)
+ : RenderBox(node), m_type(type)
+{
+ setInline(true);
+// setReplaced(true);
+}
+
+void RenderGlyph::setStyle(RenderStyle *_style)
+{
+ RenderBox::setStyle(_style);
+
+ const QFontMetrics &fm = style()->fontMetrics();
+ QRect xSize= fm.boundingRect('x');
+ m_height = xSize.height();
+ m_width = xSize.width();;
+
+ switch(m_type) {
+ // Glyphs:
+ case LDISC:
+ case LCIRCLE:
+ case LSQUARE:
+ case LBOX:
+ case LDIAMOND:
+ case LNONE:
+ break;
+ default:
+ // not a glyph !
+ assert(false);
+ break;
+ }
+}
+
+void RenderGlyph::calcMinMaxWidth()
+{
+ m_minWidth = m_width;
+ m_maxWidth = m_width;
+
+ setMinMaxKnown();
+}
+
+short RenderGlyph::lineHeight(bool /*b*/) const
+{
+ return height();
+}
+
+short RenderGlyph::baselinePosition(bool /*b*/) const
+{
+ return height();
+}
+
+void RenderGlyph::paint(PaintInfo& paintInfo, int _tx, int _ty)
+{
+ if (paintInfo.phase != PaintActionForeground)
+ return;
+
+ if (style()->visibility() != VISIBLE) return;
+
+ _tx += m_x;
+ _ty += m_y;
+
+ if((_ty > paintInfo.r.bottom()) || (_ty + m_height <= paintInfo.r.top()))
+ return;
+
+ QPainter* p = paintInfo.p;
+
+ const QColor color( style()->color() );
+ p->setPen( color );
+
+ int xHeight = m_height;
+ int bulletWidth = (xHeight+1)/2;
+ int yoff = (xHeight - 1)/4;
+ QRect marker(_tx, _ty + yoff, bulletWidth, bulletWidth);
+
+ switch(m_type) {
+ case LDISC:
+ p->setBrush( color );
+ p->drawEllipse( marker );
+ return;
+ case LCIRCLE:
+ p->setBrush( Qt::NoBrush );
+ p->drawEllipse( marker );
+ return;
+ case LSQUARE:
+ p->setBrush( color );
+ p->drawRect( marker );
+ return;
+ case LBOX:
+ p->setBrush( Qt::NoBrush );
+ p->drawRect( marker );
+ return;
+ case LDIAMOND: {
+ static QPointArray diamond(4);
+ int x = marker.x();
+ int y = marker.y();
+ int s = bulletWidth/2;
+ diamond[0] = QPoint(x+s, y);
+ diamond[1] = QPoint(x+2*s, y+s);
+ diamond[2] = QPoint(x+s, y+2*s);
+ diamond[3] = QPoint(x, y+s);
+ p->setBrush( color );
+ p->drawConvexPolygon( diamond, 0, 4 );
+ return;
+ }
+ case LNONE:
+ return;
+ default:
+ // not a glyph
+ assert(false);
+ }
+}
+
diff --git a/khtml/rendering/render_generated.h b/khtml/rendering/render_generated.h
new file mode 100644
index 000000000..15b30b8c7
--- /dev/null
+++ b/khtml/rendering/render_generated.h
@@ -0,0 +1,125 @@
+/*
+ * This file is part of the HTML rendering engine for KDE.
+ *
+ * Copyright (C) 2004,2005 Allan Sandfeld Jensen (kde@carewolf.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef RENDER_GENERATED_H
+#define RENDER_GENERATED_H
+
+#include "rendering/render_text.h"
+#include "rendering/render_box.h"
+
+namespace DOM {
+ class CounterImpl;
+}
+
+namespace khtml {
+ class CounterNode;
+
+// -----------------------------------------------------------------------------
+
+class RenderCounterBase : public RenderText
+{
+public:
+ RenderCounterBase(DOM::NodeImpl* node);
+
+ virtual const char *renderName() const { return "RenderCounterBase"; }
+
+ virtual void layout( );
+ virtual void calcMinMaxWidth();
+ virtual bool isCounter() const { return true; }
+
+ virtual void generateContent() = 0;
+ void updateContent();
+
+protected:
+ QString m_item;
+ CounterNode *m_counterNode; // Cache of the counternode
+};
+
+// -----------------------------------------------------------------------------
+
+class RenderCounter : public RenderCounterBase
+{
+public:
+ RenderCounter(DOM::NodeImpl* node, const DOM::CounterImpl* counter);
+ virtual ~RenderCounter() {};
+
+ virtual const char *renderName() const { return "RenderCounter"; }
+
+ virtual void generateContent();
+
+protected:
+ QString toListStyleType(int value, int total, EListStyleType type);
+
+ const DOM::CounterImpl* m_counter;
+};
+
+// -----------------------------------------------------------------------------
+
+class RenderQuote : public RenderCounterBase
+{
+public:
+ RenderQuote(DOM::NodeImpl* node, EQuoteContent type);
+ virtual ~RenderQuote() {};
+
+ virtual const char *renderName() const { return "RenderQuote"; }
+
+ virtual bool isQuote() const { return true; }
+ virtual int quoteCount() const;
+
+ virtual void generateContent();
+
+protected:
+ EQuoteContent m_quoteType;
+};
+
+// -----------------------------------------------------------------------------
+
+// Is actually a special case of renderCounter for non-counted list-styles
+// These have traditionally been drawn rather than use Unicode characters
+class RenderGlyph : public RenderBox
+{
+public:
+ RenderGlyph(DOM::NodeImpl* node, EListStyleType type);
+ virtual ~RenderGlyph() {};
+
+ virtual const char *renderName() const { return "RenderGlyph"; }
+
+ virtual void paint(PaintInfo& paintInfo, int _tx, int _ty);
+ virtual void calcMinMaxWidth();
+
+ virtual void setStyle(RenderStyle *_style);
+
+ virtual short lineHeight( bool firstLine ) const;
+ virtual short baselinePosition( bool firstLine ) const;
+
+ virtual bool isGlyph() const { return true; }
+
+ virtual void position(InlineBox* box, int /*from*/, int /*len*/, bool /*reverse*/) {
+ setPos( box->xPos(), box->yPos() );
+ }
+
+protected:
+ EListStyleType m_type;
+};
+
+} //namespace
+
+#endif
diff --git a/khtml/rendering/render_image.cpp b/khtml/rendering/render_image.cpp
new file mode 100644
index 000000000..5d33da2ab
--- /dev/null
+++ b/khtml/rendering/render_image.cpp
@@ -0,0 +1,604 @@
+/**
+ * This file is part of the DOM implementation for KDE.
+ *
+ * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000-2003 Dirk Mueller (mueller@kde.org)
+ * (C) 2003 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.
+ *
+ */
+//#define DEBUG_LAYOUT
+
+#include "render_image.h"
+#include "render_canvas.h"
+
+#include <qdrawutil.h>
+#include <qpainter.h>
+
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kglobalsettings.h>
+
+#include "css/csshelper.h"
+#include "misc/helper.h"
+#include "misc/htmlattrs.h"
+#include "misc/loader.h"
+#include "misc/htmltags.h"
+#include "html/html_formimpl.h"
+#include "html/html_imageimpl.h"
+#include "html/dtd.h"
+#include "xml/dom2_eventsimpl.h"
+#include "html/html_documentimpl.h"
+#include "html/html_objectimpl.h"
+#include "khtmlview.h"
+#include "khtml_part.h"
+#include <math.h>
+
+#include "loading_icon.cpp"
+
+using namespace DOM;
+using namespace khtml;
+
+// -------------------------------------------------------------------------
+
+RenderImage::RenderImage(NodeImpl *_element)
+ : RenderReplaced(_element)
+{
+ m_oldImage = m_cachedImage = 0;
+
+ m_selectionState = SelectionNone;
+ berrorPic = false;
+
+ const KHTMLSettings *settings = _element->getDocument()->view()->part()->settings();
+ bUnfinishedImageFrame = settings->unfinishedImageFrame();
+
+ setIntrinsicWidth( 0 );
+ setIntrinsicHeight( 0 );
+}
+
+RenderImage::~RenderImage()
+{
+ if(m_cachedImage) m_cachedImage->deref( this );
+ if(m_oldImage) m_oldImage->deref( this );
+}
+
+QPixmap RenderImage::pixmap() const
+{
+ return m_cachedImage ? m_cachedImage->pixmap() : QPixmap();
+}
+
+void RenderImage::setStyle(RenderStyle* _style)
+{
+ RenderReplaced::setStyle(_style);
+ // init RenderObject attributes
+ //setOverhangingContents(style()->height().isPercent());
+ setShouldPaintBackgroundOrBorder(true);
+}
+
+void RenderImage::setContentObject(CachedObject* co )
+{
+ if (co && m_cachedImage != co)
+ updateImage( static_cast<CachedImage*>( co ) );
+}
+
+void RenderImage::setPixmap( const QPixmap &p, const QRect& r, CachedImage *o)
+{
+ if ( o == m_oldImage )
+ return;
+
+ if(o != m_cachedImage) {
+ RenderReplaced::setPixmap(p, r, o);
+ return;
+ }
+
+ bool iwchanged = false;
+
+ if(o->isErrorImage()) {
+ int iw = p.width() + 8;
+ int ih = p.height() + 8;
+
+ // we have an alt and the user meant it (its not a text we invented)
+ if ( element() && !alt.isEmpty() && !element()->getAttribute( ATTR_ALT ).isNull()) {
+ const QFontMetrics &fm = style()->fontMetrics();
+ QRect br = fm.boundingRect ( 0, 0, 1024, 256, Qt::AlignAuto|Qt::WordBreak, alt.string() );
+ if ( br.width() > iw )
+ iw = br.width();
+ if ( br.height() > ih )
+ ih = br.height();
+ }
+
+ if ( iw != intrinsicWidth() ) {
+ setIntrinsicWidth( iw );
+ iwchanged = true;
+ }
+ if ( ih != intrinsicHeight() ) {
+ setIntrinsicHeight( ih );
+ iwchanged = true;
+ }
+ if ( element() && element()->id() == ID_OBJECT ) {
+ static_cast<HTMLObjectElementImpl*>( element() )->renderAlternative();
+ return;
+ }
+ }
+ berrorPic = o->isErrorImage();
+
+ bool needlayout = false;
+
+ // Image dimensions have been changed, see what needs to be done
+ if( o->pixmap_size().width() != intrinsicWidth() ||
+ o->pixmap_size().height() != intrinsicHeight() || iwchanged )
+ {
+// qDebug("image dimensions have been changed, old: %d/%d new: %d/%d",
+// intrinsicWidth(), intrinsicHeight(),
+// o->pixmap_size().width(), o->pixmap_size().height());
+
+ if(!o->isErrorImage()) {
+ setIntrinsicWidth( o->pixmap_size().width() );
+ setIntrinsicHeight( o->pixmap_size().height() );
+ }
+
+ // lets see if we need to relayout at all..
+ int oldwidth = m_width;
+ int oldheight = m_height;
+ int oldminwidth = m_minWidth;
+ m_minWidth = 0;
+
+ if ( parent() ) {
+ calcWidth();
+ calcHeight();
+ }
+
+ if(iwchanged || m_width != oldwidth || m_height != oldheight)
+ needlayout = true;
+
+ m_minWidth = oldminwidth;
+ m_width = oldwidth;
+ m_height = oldheight;
+ }
+
+ // we're not fully integrated in the tree yet.. we'll come back.
+ if ( !parent() )
+ return;
+
+ if(needlayout)
+ {
+ if (!selfNeedsLayout())
+ setNeedsLayout(true);
+ if (minMaxKnown())
+ setMinMaxKnown(false);
+ }
+ else
+ {
+ bool completeRepaint = !resizeCache.isNull();
+ int cHeight = contentHeight();
+ int scaledHeight = intrinsicHeight() ? ((o->valid_rect().height()*cHeight)/intrinsicHeight()) : 0;
+
+ // don't bog down X server doing xforms
+ if(completeRepaint && cHeight >= 5 && o->valid_rect().height() < intrinsicHeight() &&
+ (scaledHeight / (cHeight/5) == resizeCache.height() / (cHeight/5)))
+ return;
+
+ resizeCache = QPixmap(); // for resized animations
+
+ if(completeRepaint)
+ repaintRectangle(borderLeft()+paddingLeft(), borderTop()+paddingTop(), contentWidth(), contentHeight());
+ else
+ {
+ repaintRectangle(r.x() + borderLeft() + paddingLeft(), r.y() + borderTop() + paddingTop(),
+ r.width(), r.height());
+ }
+ }
+}
+
+void RenderImage::paint(PaintInfo& paintInfo, int _tx, int _ty)
+{
+ if (paintInfo.phase == PaintActionOutline && style()->outlineWidth() && style()->visibility() == VISIBLE)
+ paintOutline(paintInfo.p, _tx + m_x, _ty + m_y, width(), height(), style());
+
+ if (paintInfo.phase != PaintActionForeground && paintInfo.phase != PaintActionSelection)
+ return;
+
+ // not visible or not even once layouted?
+ if (style()->visibility() != VISIBLE || m_y <= -500000) return;
+
+ _tx += m_x;
+ _ty += m_y;
+
+ if((_ty > paintInfo.r.bottom()) || (_ty + m_height <= paintInfo.r.top())) return;
+
+ if(shouldPaintBackgroundOrBorder())
+ paintBoxDecorations(paintInfo, _tx, _ty);
+
+ int cWidth = contentWidth();
+ int cHeight = contentHeight();
+ int leftBorder = borderLeft();
+ int topBorder = borderTop();
+ int leftPad = paddingLeft();
+ int topPad = paddingTop();
+
+ if (!canvas()->printImages())
+ return;
+
+ CachedImage* i = m_oldImage && m_oldImage->valid_rect().size() == m_oldImage->pixmap_size() &&
+ m_oldImage->pixmap_size().width() == intrinsicWidth() &&
+ m_oldImage->pixmap_size().height() == intrinsicHeight()
+ ? m_oldImage : m_cachedImage;
+
+ // paint frame around image as long as it is not completely loaded from web.
+ if (bUnfinishedImageFrame && paintInfo.phase == PaintActionForeground && cWidth > 2 && cHeight > 2 && !complete()) {
+ static QPixmap *loadingIcon;
+ QColor bg = khtml::retrieveBackgroundColor(this);
+ QColor fg = khtml::hasSufficientContrast(Qt::gray, bg) ? Qt::gray :
+ (hasSufficientContrast(Qt::white, bg) ? Qt::white : Qt::black);
+ paintInfo.p->setPen(QPen(fg, 1));
+ paintInfo.p->setBrush( Qt::NoBrush );
+ paintInfo.p->drawRect(_tx, _ty, m_width, m_height);
+ if (!(m_width <= 5 || m_height <= 5)) {
+ if (!loadingIcon) {
+ loadingIcon = new QPixmap();
+ loadingIcon->loadFromData(loading_icon_data, loading_icon_len);
+ }
+ paintInfo.p->drawPixmap(_tx + 4, _ty + 4, *loadingIcon, 0, 0, m_width - 5, m_height - 5);
+ }
+
+ }
+
+ //kdDebug( 6040 ) << " contents (" << contentWidth << "/" << contentHeight << ") border=" << borderLeft() << " padding=" << paddingLeft() << endl;
+ if ( !i || berrorPic)
+ {
+ if(cWidth > 2 && cHeight > 2)
+ {
+ if ( !berrorPic ) {
+ //qDebug("qDrawShadePanel %d/%d/%d/%d", _tx + leftBorder, _ty + topBorder, cWidth, cHeight);
+ qDrawShadePanel( paintInfo.p, _tx + leftBorder + leftPad, _ty + topBorder + topPad, cWidth, cHeight,
+ KApplication::palette().inactive(), true, 1 );
+ }
+ QPixmap const* pix = i ? &i->pixmap() : 0;
+ if(berrorPic && pix && (cWidth >= pix->width()+4) && (cHeight >= pix->height()+4) )
+ {
+ QRect r(pix->rect());
+ r = r.intersect(QRect(0, 0, cWidth-4, cHeight-4));
+ paintInfo.p->drawPixmap( QPoint( _tx + leftBorder + leftPad+2, _ty + topBorder + topPad+2), *pix, r );
+ }
+ if(!alt.isEmpty()) {
+ QString text = alt.string();
+ paintInfo.p->setFont(style()->font());
+ paintInfo.p->setPen( style()->color() );
+ int ax = _tx + leftBorder + leftPad + 2;
+ int ay = _ty + topBorder + topPad + 2;
+ const QFontMetrics &fm = style()->fontMetrics();
+ if (cWidth>5 && cHeight>=fm.height())
+ paintInfo.p->drawText(ax, ay+1, cWidth - 4, cHeight - 4, Qt::WordBreak, text );
+ }
+ }
+ }
+ else if (i && !i->isTransparent())
+ {
+ paintInfo.p->setPen( Qt::black ); // used for bitmaps
+ const QPixmap& pix = i->pixmap();
+ if ( (cWidth != intrinsicWidth() || cHeight != intrinsicHeight()) &&
+ pix.width() > 0 && pix.height() > 0 && i->valid_rect().isValid())
+ {
+ if (resizeCache.isNull() && cWidth && cHeight && intrinsicWidth() && intrinsicHeight())
+ {
+ QRect scaledrect(i->valid_rect());
+// kdDebug(6040) << "time elapsed: " << dt->elapsed() << endl;
+// kdDebug( 6040 ) << "have to scale: " << endl;
+// qDebug("cw=%d ch=%d pw=%d ph=%d rcw=%d, rch=%d",
+// cWidth, cHeight, intrinsicWidth(), intrinsicHeight(), resizeCache.width(), resizeCache.height());
+ QWMatrix matrix;
+ matrix.scale( (float)(cWidth)/intrinsicWidth(),
+ (float)(cHeight)/intrinsicHeight() );
+ resizeCache = pix.xForm( matrix );
+ scaledrect.setWidth( ( cWidth*scaledrect.width() ) / intrinsicWidth() );
+ scaledrect.setHeight( ( cHeight*scaledrect.height() ) / intrinsicHeight() );
+// qDebug("resizeCache size: %d/%d", resizeCache.width(), resizeCache.height());
+// qDebug("valid: %d/%d, scaled: %d/%d",
+// i->valid_rect().width(), i->valid_rect().height(),
+// scaledrect.width(), scaledrect.height());
+
+ // sometimes scaledrect.width/height are off by one because
+ // of rounding errors. if the i is fully loaded, we
+ // make sure that we don't do unnecessary resizes during painting
+ QSize s(scaledrect.size());
+ if(i->valid_rect().size() == QSize( intrinsicWidth(), intrinsicHeight() )) // fully loaded
+ s = QSize(cWidth, cHeight);
+ if(kAbs(s.width() - cWidth) < 2) // rounding errors
+ s.setWidth(cWidth);
+ if(resizeCache.size() != s)
+ resizeCache.resize(s);
+
+ paintInfo.p->drawPixmap( QPoint( _tx + leftBorder + leftPad, _ty + topBorder + topPad),
+ resizeCache, scaledrect );
+ }
+ else
+ paintInfo.p->drawPixmap( QPoint( _tx + leftBorder + leftPad, _ty + topBorder + topPad), resizeCache );
+ }
+ else
+ {
+ // we might be just about switching images
+ QRect rect(i->valid_rect().isValid() ? i->valid_rect()
+ : QRect(0, 0, intrinsicWidth(), intrinsicHeight()));
+
+ QPoint offs( _tx + leftBorder + leftPad, _ty + topBorder + topPad);
+// qDebug("normal paint rect %d/%d/%d/%d", rect.x(), rect.y(), rect.width(), rect.height());
+// rect = rect & QRect( 0 , y - offs.y() - 10, w, 10 + y + h - offs.y());
+
+// qDebug("normal paint rect after %d/%d/%d/%d", rect.x(), rect.y(), rect.width(), rect.height());
+// qDebug("normal paint: offs.y(): %d, y: %d, diff: %d", offs.y(), y, y - offs.y());
+// qDebug("");
+
+// p->setClipRect(QRect(x,y,w,h));
+
+
+// p->drawPixmap( offs.x(), y, pix, rect.x(), rect.y(), rect.width(), rect.height() );
+ paintInfo.p->drawPixmap(offs, pix, rect);
+
+ }
+ }
+ if (m_selectionState != SelectionNone) {
+// kdDebug(6040) << "_tx " << _tx << " _ty " << _ty << " _x " << _x << " _y " << _y << endl;
+ // Draw in any case if inside selection. For selection borders, the
+ // offset will decide whether to draw selection or not
+ bool draw = true;
+ if (m_selectionState != SelectionInside) {
+ int startPos, endPos;
+ selectionStartEnd(startPos, endPos);
+ if(selectionState() == SelectionStart)
+ endPos = 1;
+ else if(selectionState() == SelectionEnd)
+ startPos = 0;
+ draw = endPos - startPos > 0;
+ }
+ if (draw) {
+ // setting the brush origin is important for compatibility,
+ // don't touch it unless you know what you're doing
+ paintInfo.p->setBrushOrigin(_tx, _ty - paintInfo.r.y());
+ paintInfo.p->fillRect(_tx, _ty, width(), height(),
+ QBrush(style()->palette().active().highlight(),
+ Qt::Dense4Pattern));
+ }
+ }
+}
+
+void RenderImage::layout()
+{
+ KHTMLAssert( needsLayout());
+ KHTMLAssert( minMaxKnown() );
+
+ short oldwidth = m_width;
+ int oldheight = m_height;
+
+ // minimum height
+ m_height = m_cachedImage && m_cachedImage->isErrorImage() ? intrinsicHeight() : 0;
+
+ calcWidth();
+ calcHeight();
+
+ // if they are variable width and we calculate a huge height or width, we assume they
+ // actually wanted the intrinsic width.
+ if ( m_width > 4096 && !style()->width().isFixed() )
+ m_width = intrinsicWidth() + paddingLeft() + paddingRight() + borderLeft() + borderRight();
+ if ( m_height > 2048 && !style()->height().isFixed() )
+ m_height = intrinsicHeight() + paddingTop() + paddingBottom() + borderTop() + borderBottom();
+
+ // limit total size to not run out of memory when doing the xform call.
+ if ( ( m_width * m_height > 4096*2048 ) &&
+ ( contentWidth() > intrinsicWidth() || contentHeight() > intrinsicHeight() ) ) {
+ float scale = sqrt( m_width*m_height / ( 4096.*2048. ) );
+ m_width = (int) (m_width/scale);
+ m_height = (int) (m_height/scale);
+ }
+
+ if ( m_width != oldwidth || m_height != oldheight )
+ resizeCache = QPixmap();
+
+ setNeedsLayout(false);
+}
+
+void RenderImage::notifyFinished(CachedObject *finishedObj)
+{
+ if ( ( m_cachedImage == finishedObj || m_oldImage == finishedObj ) && m_oldImage ) {
+ m_oldImage->deref( this );
+ m_oldImage = 0;
+ repaint();
+ }
+
+ RenderReplaced::notifyFinished(finishedObj);
+}
+
+bool RenderImage::nodeAtPoint(NodeInfo& info, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction, bool inside)
+{
+ inside |= RenderReplaced::nodeAtPoint(info, _x, _y, _tx, _ty, hitTestAction, inside);
+
+ if (inside && element()) {
+ int tx = _tx + m_x;
+ int ty = _ty + m_y;
+ if (isRelPositioned())
+ relativePositionOffset(tx, ty);
+
+ HTMLImageElementImpl* i = element()->id() == ID_IMG ? static_cast<HTMLImageElementImpl*>(element()) : 0;
+ HTMLMapElementImpl* map;
+ if (i && i->getDocument()->isHTMLDocument() &&
+ (map = static_cast<HTMLDocumentImpl*>(i->getDocument())->getMap(i->imageMap()))) {
+ // we're a client side image map
+ inside = map->mapMouseEvent(_x - tx, _y - ty, contentWidth(), contentHeight(), info);
+ info.setInnerNonSharedNode(element());
+ }
+ }
+
+ return inside;
+}
+
+void RenderImage::updateImage(CachedImage* new_image)
+{
+ CachedImage* tempimage = m_oldImage;
+ m_oldImage = m_cachedImage;
+ m_cachedImage = new_image;
+ assert( m_cachedImage != m_oldImage );
+
+ if ( m_cachedImage )
+ m_cachedImage->ref(this);
+
+ if ( tempimage )
+ tempimage->deref(this);
+
+ // if the loading finishes we might get an error and then the image is deleted
+ if ( m_cachedImage )
+ berrorPic = m_cachedImage->isErrorImage();
+ else
+ berrorPic = true;
+}
+
+void RenderImage::updateFromElement()
+{
+ if (element()->id() == ID_INPUT)
+ alt = static_cast<HTMLInputElementImpl*>(element())->altText();
+ else if (element()->id() == ID_IMG)
+ alt = static_cast<HTMLImageElementImpl*>(element())->altText();
+
+ DOMString u = element()->id() == ID_OBJECT ?
+ element()->getAttribute(ATTR_DATA) : element()->getAttribute(ATTR_SRC);
+
+ if (!u.isEmpty() &&
+ ( !m_cachedImage || m_cachedImage->url() != u ) ) {
+ CachedImage *new_image = element()->getDocument()->docLoader()->
+ requestImage(khtml::parseURL(u));
+
+ if(new_image && new_image != m_cachedImage)
+ updateImage( new_image );
+ }
+}
+
+bool RenderImage::complete() const
+{
+ // "complete" means that the image has been loaded
+ // but also that its width/height (contentWidth(),contentHeight()) have been calculated.
+ return m_cachedImage && m_cachedImage->valid_rect().size() == m_cachedImage->pixmap_size() && !needsLayout();
+}
+
+bool RenderImage::isWidthSpecified() const
+{
+ switch (style()->width().type()) {
+ case Fixed:
+ case Percent:
+ return true;
+ default:
+ return false;
+ }
+ assert(false);
+ return false;
+}
+
+bool RenderImage::isHeightSpecified() const
+{
+ switch (style()->height().type()) {
+ case Fixed:
+ case Percent:
+ return true;
+ default:
+ return false;
+ }
+ assert(false);
+ return false;
+}
+
+short RenderImage::calcAspectRatioWidth() const
+{
+ if (intrinsicHeight() == 0)
+ return 0;
+ if (!m_cachedImage || m_cachedImage->isErrorImage())
+ return intrinsicWidth(); // Don't bother scaling.
+ return RenderReplaced::calcReplacedHeight() * intrinsicWidth() / intrinsicHeight();
+}
+
+int RenderImage::calcAspectRatioHeight() const
+{
+ if (intrinsicWidth() == 0)
+ return 0;
+ if (!m_cachedImage || m_cachedImage->isErrorImage())
+ return intrinsicHeight(); // Don't bother scaling.
+ return RenderReplaced::calcReplacedWidth() * intrinsicHeight() / intrinsicWidth();
+}
+
+short RenderImage::calcReplacedWidth() const
+{
+ int width;
+ if (isWidthSpecified())
+ width = calcReplacedWidthUsing(Width);
+ else
+ width = calcAspectRatioWidth();
+ int minW = calcReplacedWidthUsing(MinWidth);
+ int maxW = style()->maxWidth().value() == UNDEFINED ? width : calcReplacedWidthUsing(MaxWidth);
+
+ if (width > maxW)
+ width = maxW;
+
+ if (width < minW)
+ width = minW;
+
+ return width;
+}
+
+int RenderImage::calcReplacedHeight() const
+{
+ int height;
+ if (isHeightSpecified())
+ height = calcReplacedHeightUsing(Height);
+ else
+ height = calcAspectRatioHeight();
+
+ int minH = calcReplacedHeightUsing(MinHeight);
+ int maxH = style()->maxHeight().value() == UNDEFINED ? height : calcReplacedHeightUsing(MaxHeight);
+
+ if (height > maxH)
+ height = maxH;
+
+ if (height < minH)
+ height = minH;
+
+ return height;
+}
+
+#if 0
+void RenderImage::caretPos(int offset, int flags, int &_x, int &_y, int &width, int &height)
+{
+ RenderReplaced::caretPos(offset, flags, _x, _y, width, height);
+
+#if 0 // doesn't work reliably
+ height = intrinsicHeight();
+ width = override && offset == 0 ? intrinsicWidth() : 0;
+ _x = xPos();
+ _y = yPos();
+ if (offset > 0) _x += intrinsicWidth();
+
+ RenderObject *cb = containingBlock();
+
+ int absx, absy;
+ if (cb && cb != this && cb->absolutePosition(absx,absy))
+ {
+ _x += absx;
+ _y += absy;
+ } else {
+ // we don't know our absolute position, and there is no point returning
+ // just a relative one
+ _x = _y = -1;
+ }
+#endif
+}
+#endif
diff --git a/khtml/rendering/render_image.h b/khtml/rendering/render_image.h
new file mode 100644
index 000000000..bd887ddf5
--- /dev/null
+++ b/khtml/rendering/render_image.h
@@ -0,0 +1,105 @@
+/*
+ * This file is part of the DOM implementation for KDE.
+ *
+ * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef RENDER_IMAGE_H
+#define RENDER_IMAGE_H
+
+#include "html/dtd.h"
+#include "html/html_elementimpl.h"
+#include "rendering/render_replaced.h"
+#include "dom/dom_string.h"
+
+#include <qmap.h>
+#include <qpixmap.h>
+
+namespace khtml {
+
+class DocLoader;
+class CachedObject;
+
+class RenderImage : public RenderReplaced
+{
+public:
+ RenderImage(DOM::NodeImpl* _element);
+ virtual ~RenderImage();
+
+ virtual const char *renderName() const { return "RenderImage"; }
+ virtual void paint( PaintInfo& i, int tx, int ty );
+
+ virtual void layout();
+
+ virtual void setPixmap( const QPixmap &, const QRect&, CachedImage *);
+
+ // don't even think about making these methods virtual!
+ QPixmap pixmap() const;
+ DOM::HTMLElementImpl* element() const
+ { return static_cast<DOM::HTMLElementImpl*>(RenderObject::element()); }
+
+ bool complete() const;
+
+ CachedObject *contentObject() { return m_cachedImage; }
+ void setContentObject( CachedObject* );
+
+ // hook to keep RendeObject::m_inline() up to date
+ virtual void setStyle(RenderStyle *style);
+ virtual void updateFromElement();
+
+ virtual void notifyFinished(CachedObject *finishedObj);
+ virtual bool nodeAtPoint(NodeInfo& info, int x, int y, int tx, int ty, HitTestAction hitTestAction, bool inside);
+
+ bool isWidthSpecified() const;
+ bool isHeightSpecified() const;
+
+ short calcAspectRatioWidth() const;
+ int calcAspectRatioHeight() const;
+
+ virtual short calcReplacedWidth() const;
+ virtual int calcReplacedHeight() const;
+
+ virtual SelectionState selectionState() const {return m_selectionState;}
+ virtual void setSelectionState(SelectionState s) {m_selectionState = s; }
+#if 0
+ virtual void caretPos(int offset, int flags, int &_x, int &_y, int &width, int &height);
+#endif
+
+private:
+ void updateImage(CachedImage* new_image);
+ /*
+ * Cache for images that need resizing
+ */
+ QPixmap resizeCache;
+
+ // text to display as long as the image isn't available
+ DOM::DOMString alt;
+
+ CachedImage *m_cachedImage;
+ CachedImage *m_oldImage;
+
+ bool berrorPic : 1;
+ bool bUnfinishedImageFrame :1;
+ SelectionState m_selectionState : 3; // FIXME: don't forget to enlarge this as the enum grows
+};
+
+
+} //namespace
+
+#endif
diff --git a/khtml/rendering/render_inline.cpp b/khtml/rendering/render_inline.cpp
new file mode 100644
index 000000000..5848db47a
--- /dev/null
+++ b/khtml/rendering/render_inline.cpp
@@ -0,0 +1,935 @@
+/*
+ * This file is part of the render object implementation for KHTML.
+ *
+ * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
+ * (C) 1999-2003 Antti Koivisto (koivisto@kde.org)
+ * (C) 2002-2003 Dirk Mueller (mueller@kde.org)
+ * (C) 2003 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 <kglobal.h>
+
+#include "rendering/render_arena.h"
+#include "rendering/render_inline.h"
+#include "rendering/render_block.h"
+#include "xml/dom_docimpl.h"
+
+#include <qvaluevector.h>
+
+using namespace khtml;
+
+void RenderInline::setStyle(RenderStyle* _style)
+{
+ RenderFlow::setStyle(_style);
+ setInline(true);
+
+ // Ensure that all of the split inlines pick up the new style. We
+ // only do this if we're an inline, since we don't want to propagate
+ // a block's style to the other inlines.
+ // e.g., <font>foo <h4>goo</h4> moo</font>. The <font> inlines before
+ // and after the block share the same style, but the block doesn't
+ // need to pass its style on to anyone else.
+ RenderFlow* currCont = continuation();
+ while (currCont) {
+ if (currCont->isInline()) {
+ RenderFlow* nextCont = currCont->continuation();
+ currCont->setContinuation(0);
+ currCont->setStyle(style());
+ currCont->setContinuation(nextCont);
+ }
+ currCont = currCont->continuation();
+ }
+
+ if (attached()) {
+ // Update replaced content
+ updateReplacedContent();
+ // Update pseudos for ::before and ::after
+ updatePseudoChildren();
+ }
+}
+
+// Attach handles initial setStyle that requires parent nodes
+void RenderInline::attach()
+{
+ RenderFlow::attach();
+
+ updateReplacedContent();
+ updatePseudoChildren();
+}
+
+bool RenderInline::isInlineContinuation() const
+{
+ return m_isContinuation;
+}
+
+void RenderInline::addChildToFlow(RenderObject* newChild, RenderObject* beforeChild)
+{
+ // Make sure we don't append things after :after-generated content if we have it.
+ if (!beforeChild && lastChild() && lastChild()->style()->styleType() == RenderStyle::AFTER)
+ beforeChild = lastChild();
+
+ if (!newChild->isText() && newChild->style()->position() != STATIC)
+ setOverhangingContents();
+
+ if (!newChild->isInline() && !newChild->isFloatingOrPositioned() )
+ {
+ // We are placing a block inside an inline. We have to perform a split of this
+ // inline into continuations. This involves creating an anonymous block box to hold
+ // |newChild|. We then make that block box a continuation of this inline. We take all of
+ // the children after |beforeChild| and put them in a clone of this object.
+
+ RenderBlock *newBox = createAnonymousBlock();
+ RenderFlow* oldContinuation = continuation();
+ setContinuation(newBox);
+
+ splitFlow(beforeChild, newBox, newChild, oldContinuation);
+ return;
+ }
+
+ RenderBox::addChild(newChild,beforeChild);
+
+ newChild->setNeedsLayoutAndMinMaxRecalc();
+}
+
+RenderInline* RenderInline::cloneInline(RenderFlow* src)
+{
+ RenderInline *o = new (src->renderArena()) RenderInline(src->element());
+ o->m_isContinuation = true;
+ o->setStyle(src->style());
+ return o;
+}
+
+void RenderInline::splitInlines(RenderBlock* fromBlock, RenderBlock* toBlock,
+ RenderBlock* middleBlock,
+ RenderObject* beforeChild, RenderFlow* oldCont)
+{
+ // Create a clone of this inline.
+ RenderInline* clone = cloneInline(this);
+ clone->setContinuation(oldCont);
+
+ // Now take all of the children from beforeChild to the end and remove
+ // then from |this| and place them in the clone.
+ RenderObject* o = beforeChild;
+ while (o) {
+ RenderObject* tmp = o;
+ o = tmp->nextSibling();
+ clone->addChildToFlow(removeChildNode(tmp), 0);
+ tmp->setNeedsLayoutAndMinMaxRecalc();
+ }
+
+ // Hook |clone| up as the continuation of the middle block.
+ middleBlock->setContinuation(clone);
+
+ // We have been reparented and are now under the fromBlock. We need
+ // to walk up our inline parent chain until we hit the containing block.
+ // Once we hit the containing block we're done.
+ RenderFlow* curr = static_cast<RenderFlow*>(parent());
+ RenderFlow* currChild = this;
+ while (curr && curr != fromBlock) {
+ // Create a new clone.
+ RenderInline* cloneChild = clone;
+ clone = cloneInline(curr);
+
+ // Insert our child clone as the first child.
+ clone->addChildToFlow(cloneChild, 0);
+
+ // Hook the clone up as a continuation of |curr|.
+ RenderFlow* oldCont = curr->continuation();
+ curr->setContinuation(clone);
+ clone->setContinuation(oldCont);
+
+ // Now we need to take all of the children starting from the first child
+ // *after* currChild and append them all to the clone.
+ o = currChild->nextSibling();
+ while (o) {
+ RenderObject* tmp = o;
+ o = tmp->nextSibling();
+ clone->appendChildNode(curr->removeChildNode(tmp));
+ tmp->setNeedsLayoutAndMinMaxRecalc();
+ }
+
+ // Keep walking up the chain.
+ currChild = curr;
+ curr = static_cast<RenderFlow*>(curr->parent());
+ }
+
+ // Now we are at the block level. We need to put the clone into the toBlock.
+ toBlock->appendChildNode(clone);
+
+ // Now take all the children after currChild and remove them from the fromBlock
+ // and put them in the toBlock.
+ o = currChild->nextSibling();
+ while (o) {
+ RenderObject* tmp = o;
+ o = tmp->nextSibling();
+ toBlock->appendChildNode(fromBlock->removeChildNode(tmp));
+ }
+}
+
+void RenderInline::splitFlow(RenderObject* beforeChild, RenderBlock* newBlockBox,
+ RenderObject* newChild, RenderFlow* oldCont)
+{
+ RenderBlock* pre = 0;
+ RenderBlock* block = containingBlock();
+ bool madeNewBeforeBlock = false;
+ if (block->isAnonymousBlock()) {
+ // We can reuse this block and make it the preBlock of the next continuation.
+ pre = block;
+ block = block->containingBlock();
+ }
+ else {
+ // No anonymous block available for use. Make one.
+ pre = block->createAnonymousBlock();
+ madeNewBeforeBlock = true;
+ }
+
+ RenderBlock* post = block->createAnonymousBlock();
+
+ RenderObject* boxFirst = madeNewBeforeBlock ? block->firstChild() : pre->nextSibling();
+ if (madeNewBeforeBlock)
+ block->insertChildNode(pre, boxFirst);
+ block->insertChildNode(newBlockBox, boxFirst);
+ block->insertChildNode(post, boxFirst);
+ block->setChildrenInline(false);
+
+ if (madeNewBeforeBlock) {
+ RenderObject* o = boxFirst;
+ while (o)
+ {
+ RenderObject* no = o;
+ o = no->nextSibling();
+ pre->appendChildNode(block->removeChildNode(no));
+ no->setNeedsLayoutAndMinMaxRecalc();
+ }
+ }
+
+ splitInlines(pre, post, newBlockBox, beforeChild, oldCont);
+
+ // We already know the newBlockBox isn't going to contain inline kids, so avoid wasting
+ // time in makeChildrenNonInline by just setting this explicitly up front.
+ newBlockBox->setChildrenInline(false);
+
+ // We don't just call addChild, since it would pass things off to the
+ // continuation, so we call addChildToFlow explicitly instead. We delayed
+ // adding the newChild until now so that the |newBlockBox| would be fully
+ // connected, thus allowing newChild access to a renderArena should it need
+ // to wrap itself in additional boxes (e.g., table construction).
+ newBlockBox->addChildToFlow(newChild, 0);
+
+ // XXXdwh is any of this even necessary? I don't think it is.
+ pre->close();
+ pre->setPos(0, -500000);
+ pre->setNeedsLayout(true);
+ newBlockBox->close();
+ newBlockBox->setPos(0, -500000);
+ newBlockBox->setNeedsLayout(true);
+ post->close();
+ post->setPos(0, -500000);
+ post->setNeedsLayout(true);
+
+ updatePseudoChildren();
+
+ block->setNeedsLayoutAndMinMaxRecalc();
+}
+
+void RenderInline::paint(PaintInfo& i, int _tx, int _ty)
+{
+ paintLines(i, _tx, _ty);
+}
+
+/**
+ * Appends the given coordinate-pair to the point-array if it is not
+ * equal to the last element.
+ * @param pointArray point-array
+ * @param pnt point to append
+ * @return \c true if \c pnt has actually been appended
+ */
+inline static bool appendIfNew(QValueVector<QPoint> &pointArray, const QPoint &pnt)
+{
+// if (!pointArray.isEmpty()) kdDebug(6040) << "appifnew: " << pointArray.back() << " == " << pnt << ": " << (pointArray.back() == pnt) << endl;
+// else kdDebug(6040) << "appifnew: " << pnt << " (unconditional)" << endl;
+ if (!pointArray.isEmpty() && pointArray.back() == pnt) return false;
+ pointArray.append(pnt);
+ return true;
+}
+
+/**
+ * Does spike-reduction on the given point-array's stack-top.
+ *
+ * Spikes are path segments of which one goes forward, and the sucessor
+ * goes backward on the predecessor's segment:
+ *
+ * 2 0 1
+ * x------x<-----x
+ * (0 is stack-top in point-array)
+ *
+ * This will be reduced to
+ * 1 0
+ * x------x
+ *
+ * Preconditions:
+ * - No other spikes exist in the whole point-array except at most
+ * one at the end
+ * - No two succeeding points are ever equal
+ * - For each two succeeding points either p1.x == p2.x or p1.y == p2.y holds
+ * true
+ * - No such spike exists where 2 is situated between 0 and 1.
+ *
+ * Postcondition:
+ * - No spikes exist in the whole point-array
+ *
+ * If no spike is found, the point-array is left unchanged.
+ * @return \c true if an actual reduction was done
+ */
+inline static bool reduceSpike(QValueVector<QPoint> &pointArray)
+{
+ if (pointArray.size() < 3) return false;
+ QValueVector<QPoint>::Iterator it = pointArray.end();
+ QPoint p0 = *--it;
+ QPoint p1 = *--it;
+ QPoint p2 = *--it;
+
+ bool elide = false;
+
+ if (p0.x() == p1.x() && p1.x() == p2.x()
+ && (p1.y() < p0.y() && p0.y() < p2.y()
+ || p2.y() < p0.y() && p0.y() < p1.y()
+ || p1.y() < p2.y() && p2.y() < p0.y()
+ || p0.y() < p2.y() && p2.y() < p1.y()
+ || (elide = p2.y() == p0.y() && p0.y() < p1.y())
+ || (elide = p1.y() < p0.y() && p0.y() == p2.y()))
+ || p0.y() == p1.y() && p1.y() == p2.y()
+ && (p1.x() < p0.x() && p0.x() < p2.x()
+ || p2.x() < p0.x() && p0.x() < p1.x()
+ || p1.x() < p2.x() && p2.x() < p0.x()
+ || p0.x() < p2.x() && p2.x() < p1.x()
+ || (elide = p2.x() == p0.x() && p0.x() < p1.x())
+ || (elide = p1.x() < p0.x() && p0.x() == p2.x())))
+ {
+// kdDebug(6040) << "spikered p2" << (elide ? " (elide)" : "") << ": " << p2 << " p1: " << p1 << " p0: " << p0 << endl;
+ pointArray.pop_back(); pointArray.pop_back();
+ if (!elide)
+ pointArray.push_back(p0);
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Reduces segment separators.
+ *
+ * A segment separator separates a segment into two segments, thus causing
+ * two adjacent segment with the same orientation.
+ *
+ * 2 1 0
+ * x-------x---->x
+ * (0 means stack-top)
+ *
+ * Here, 1 is a segment separator. As segment separators not only make
+ * the line drawing algorithm inefficient, but also make the spike-reduction
+ * fail, they must be eliminated:
+ *
+ * 1 0
+ * x------------>x
+ *
+ * Preconditions:
+ * - No other segment separators exist in the whole point-array except
+ * at most one at the end
+ * - No two succeeding points are ever equal
+ * - For each two succeeding points either p1.x == p2.x or p1.y == p2.y holds
+ * true
+ * - No such spike exists where 2 is situated between 0 and 1.
+ *
+ * Postcondition:
+ * - No segment separators exist in the whole point-array
+ *
+ * If no segment separator is found at the end of the point-array, it is
+ * left unchanged.
+ * @return \c true if a segment separator was actually reduced.
+ */
+inline static bool reduceSegmentSeparator(QValueVector<QPoint> &pointArray)
+{
+ if (pointArray.size() < 3) return false;
+ QValueVector<QPoint>::Iterator it = pointArray.end();
+ QPoint p0 = *--it;
+ QPoint p1 = *--it;
+ QPoint p2 = *--it;
+// kdDebug(6040) << "checking p2: " << p2 << " p1: " << p1 << " p0: " << p0 << endl;
+
+ if (p0.x() == p1.x() && p1.x() == p2.x()
+ && (p2.y() < p1.y() && p1.y() < p0.y()
+ || p0.y() < p1.y() && p1.y() < p2.y())
+ || p0.y() == p1.y() && p1.y() == p2.y()
+ && (p2.x() < p1.x() && p1.x() < p0.x()
+ || p0.x() < p1.x() && p1.x() < p2.x()))
+ {
+// kdDebug(6040) << "segred p2: " << p2 << " p1: " << p1 << " p0: " << p0 << endl;
+ pointArray.pop_back(); pointArray.pop_back();
+ pointArray.push_back(p0);
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Appends the given point to the point-array, doing necessary reductions to
+ * produce a path without spikes and segment separators.
+ */
+static void appendPoint(QValueVector<QPoint> &pointArray, QPoint &pnt)
+{
+ if (!appendIfNew(pointArray, pnt)) return;
+// kdDebug(6040) << "appendPoint: appended " << pnt << endl;
+ reduceSegmentSeparator(pointArray)
+ || reduceSpike(pointArray);
+}
+
+/**
+ * Traverses the horizontal inline boxes and appends the point coordinates to
+ * the given array.
+ * @param box inline box
+ * @param pointArray array collecting coordinates
+ * @param bottom \c true, collect bottom coordinates, \c false, collect top
+ * coordinates.
+ * @param limit lower limit that an y-coordinate must at least reach. Note
+ * that limit designates the highest y-coordinate for \c bottom, and
+ * the lowest for !\c bottom.
+ */
+static void collectHorizontalBoxCoordinates(InlineBox *box,
+ QValueVector<QPoint> &pointArray,
+ bool bottom, int offset, int limit = -500000)
+{
+// kdDebug(6000) << "collectHorizontalBoxCoordinates: " << endl;
+ offset = bottom ? offset:-offset;
+ int y = box->yPos() + bottom*box->height() + offset;
+ if (limit != -500000 && (bottom ? y < limit : y > limit))
+ y = limit;
+ int x = box->xPos() + bottom*box->width() + offset;
+ QPoint newPnt(x, y);
+ // Add intersection point if point-array not empty.
+ if (!pointArray.isEmpty()) {
+ QPoint lastPnt = pointArray.back();
+ QPoint insPnt(newPnt.x(), lastPnt.y());
+ if (offset && ((bottom && lastPnt.y() > y) || (!bottom && lastPnt.y() < y))) {
+ insPnt.rx() = lastPnt.x();
+ insPnt.ry() = y;
+ }
+// kdDebug(6040) << "left: " << lastPnt << " == " << insPnt << ": " << (insPnt == lastPnt) << endl;
+ appendPoint(pointArray, insPnt);
+ }
+ // Insert starting point of box
+ appendPoint(pointArray, newPnt);
+
+ newPnt.rx() += (bottom ? -box->width() : box->width()) - 2*offset;
+
+ if (box->isInlineFlowBox()) {
+ InlineFlowBox *flowBox = static_cast<InlineFlowBox *>(box);
+ for (InlineBox *b = bottom ? flowBox->lastChild() : flowBox->firstChild(); b; b = bottom ? b->prevOnLine() : b->nextOnLine()) {
+ // Don't let boxes smaller than this flow box' height influence
+ // the vertical position of the outline if they have a different
+ // x-coordinate
+ int l2;
+ if (b->xPos() != box->xPos() && b->xPos() + b->width() != box->xPos() + box->width())
+ l2 = y;
+ else
+ l2 = limit;
+ collectHorizontalBoxCoordinates(b, pointArray, bottom, kAbs(offset), l2);
+ }
+
+ // Add intersection point if flow box contained any children
+ if (flowBox->firstChild()) {
+ QPoint lastPnt = pointArray.back();
+ QPoint insPnt(lastPnt.x(), newPnt.y());
+// kdDebug(6040) << "right: " << lastPnt << " == " << insPnt << ": " << (insPnt == lastPnt) << endl;
+ appendPoint(pointArray, insPnt);
+ }
+ }
+
+ // Insert ending point of box
+ appendPoint(pointArray, newPnt);
+
+// kdDebug(6000) << "collectHorizontalBoxCoordinates: " << "ende" << endl;
+}
+
+/**
+ * Checks whether the given line box' extents and the following line box'
+ * extents are disjount (i. e. do not share the same x-coordinate range).
+ * @param line line box
+ * @param toBegin \c true, compare with preceding line box, \c false, with
+ * succeeding
+ * @return \c true if this and the next box are disjoint
+ */
+inline static bool lineBoxesDisjoint(InlineRunBox *line, int offset, bool toBegin)
+{
+ InlineRunBox *next = toBegin ? line->prevLineBox() : line->nextLineBox();
+ return !next || next->xPos() + next->width() + 2*offset < line->xPos()
+ || next->xPos() > line->xPos() + line->width() + 2*offset;
+}
+
+/**
+ * Traverses the vertical outer borders of the given render flow's line
+ * boxes and appends the point coordinates to the given point array.
+ * @param line line box to begin traversal
+ * @param pointArray point array
+ * @param left \c true, traverse the left vertical coordinates,
+ * \c false, traverse the right vertical coordinates.
+ * @param lastline if not 0, returns the pointer to the last line box traversed
+ */
+static void collectVerticalBoxCoordinates(InlineRunBox *line,
+ QValueVector<QPoint> &pointArray,
+ bool left, int offset, InlineRunBox **lastline = 0)
+{
+ InlineRunBox *last = 0;
+ offset = left ? -offset:offset;
+ for (InlineRunBox* curr = line; curr && !last; curr = left ? curr->prevLineBox() : curr->nextLineBox()) {
+ InlineBox *root = curr;
+
+ bool isLast = lineBoxesDisjoint(curr, kAbs(offset), left);
+ if (isLast) last = curr;
+
+ if (root != line && !isLast)
+ while (root->parent()) root = root->parent();
+ QPoint newPnt(curr->xPos() + !left*curr->width() + offset,
+ (left ? root->topOverflow() : root->bottomOverflow()) + offset);
+ if (!pointArray.isEmpty()) {
+ QPoint lastPnt = pointArray.back();
+ if (newPnt.x()>lastPnt.x() && !left)
+ pointArray.back().setY( kMin(lastPnt.y(), root->topOverflow()-offset) );
+ else if (newPnt.x()<lastPnt.x() && left)
+ pointArray.back().setY( kMax(lastPnt.y(), root->bottomOverflow()+offset) );
+ QPoint insPnt(newPnt.x(), pointArray.back().y());
+// kdDebug(6040) << "left: " << lastPnt << " == " << insPnt << ": " << (insPnt == lastPnt) << endl;
+ appendPoint(pointArray, insPnt);
+ }
+ appendPoint(pointArray, newPnt);
+ }
+ if (lastline) *lastline = last;
+}
+
+/**
+ * Links up the end of the given point-array such that the starting point
+ * is not a segment separator.
+ *
+ * To achieve this, improper points are removed from the beginning of
+ * the point-array (by changing the array's starting iterator), and
+ * proper ones appended to the point-array's back.
+ *
+ * @param pointArray point-array
+ * @return actual begin of point array
+ */
+static QPoint *linkEndToBegin(QValueVector<QPoint> &pointArray)
+{
+ uint index = 0;
+ assert(pointArray.size() >= 3);
+
+ // if first and last points match, ignore the last one.
+ bool linkup = false; QPoint linkupPnt;
+ if (pointArray.front() == pointArray.back()) {
+ linkupPnt = pointArray.back();
+ pointArray.pop_back();
+ linkup = true;
+ }
+
+ const QPoint *it = pointArray.begin() + index;
+ QPoint pfirst = *it;
+ QPoint pnext = *++it;
+ QPoint plast = pointArray.back();
+// kdDebug(6040) << "linkcheck plast: " << plast << " pfirst: " << pfirst << " pnext: " << pnext << endl;
+
+ if (plast.x() == pfirst.x() && pfirst.x() == pnext.x()
+ || plast.y() == pfirst.y() && pfirst.y() == pnext.y()) {
+
+ ++index;
+ appendPoint(pointArray, pfirst);
+ appendPoint(pointArray, pnext);
+ } else if (linkup)
+ pointArray.push_back(linkupPnt);
+ return pointArray.begin() + index;
+}
+
+void RenderInline::paintOutlines(QPainter *p, int _tx, int _ty)
+{
+ if (style()->outlineWidth() == 0 || style()->outlineStyle() <= BHIDDEN)
+ return;
+ int offset = style()->outlineOffset();
+
+ // We may have to draw more than one outline path as they may be
+ // disjoint.
+ for (InlineRunBox *curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
+ QValueVector<QPoint> path;
+
+ // collect topmost outline
+ collectHorizontalBoxCoordinates(curr, path, false, offset);
+ // collect right outline
+ collectVerticalBoxCoordinates(curr, path, false, offset, &curr);
+ // collect bottommost outline
+ collectHorizontalBoxCoordinates(curr, path, true, offset);
+ // collect left outline
+ collectVerticalBoxCoordinates(curr, path, true, offset);
+
+ if (path.size() < 3) continue;
+
+ const QPoint *begin = linkEndToBegin(path);
+
+ // paint the outline
+ paintOutlinePath(p, _tx, _ty, begin, path.end(), BSLeft, -1, BSTop);
+ }
+}
+
+template<class T> inline void kSwap(T &a1, T &a2)
+{
+ T tmp = a2;
+ a2 = a1;
+ a1 = tmp;
+}
+
+enum BSOrientation { BSHorizontal, BSVertical };
+
+/**
+ * Returns the orientation of the given border side.
+ */
+inline BSOrientation bsOrientation(RenderObject::BorderSide bs)
+{
+ switch (bs) {
+ case RenderObject::BSTop:
+ case RenderObject::BSBottom:
+ return BSHorizontal;
+ case RenderObject::BSLeft:
+ case RenderObject::BSRight:
+ return BSVertical;
+ }
+ return BSHorizontal; // make gcc happy (sigh)
+}
+
+/**
+ * Determines the new border side by evaluating the new direction as determined
+ * by the given coordinates, the old border side, and the relative direction.
+ *
+ * The relative direction specifies whether the old border side meets with the
+ * straight given by the coordinates from below (negative), or above (positive).
+ */
+inline RenderObject::BorderSide newBorderSide(RenderObject::BorderSide oldBS, int direction, const QPoint &last, const QPoint &cur)
+{
+ bool below = direction < 0;
+ if (last.x() == cur.x()) { // new segment is vertical
+ bool t = oldBS == RenderObject::BSTop;
+ bool b = oldBS == RenderObject::BSBottom;
+ if ((t || b) && last.y() != cur.y())
+ return (cur.y() < last.y()) ^ (t && below || b && !below)
+ ? RenderObject::BSLeft : RenderObject::BSRight;
+ } else /*if (last.y() == cur.y())*/ { // new segment is horizontal
+ bool l = oldBS == RenderObject::BSLeft;
+ bool r = oldBS == RenderObject::BSRight;
+ if ((l || r) && last.x() != cur.x())
+ return (cur.x() < last.x()) ^ (l && below || r && !below)
+ ? RenderObject::BSTop : RenderObject::BSBottom;
+ }
+ return oldBS; // same direction
+}
+
+/**
+ * Draws an outline segment between the given two points.
+ * @param o render object
+ * @param p painter
+ * @param tx absolute x-coordinate of containing block
+ * @param ty absolute y-coordinate of containing block
+ * @param p1 starting point
+ * @param p2 end point
+ * @param prevBS border side of previous segment
+ * @param curBS border side of this segment
+ * @param nextBS border side of next segment
+ */
+static void paintOutlineSegment(RenderObject *o, QPainter *p, int tx, int ty,
+ const QPoint &p1, const QPoint &p2,
+ RenderObject::BorderSide prevBS,
+ RenderObject::BorderSide curBS,
+ RenderObject::BorderSide nextBS)
+{
+ int ow = o->style()->outlineWidth();
+ EBorderStyle os = o->style()->outlineStyle();
+ QColor oc = o->style()->outlineColor();
+
+ int x1 = tx + p1.x();
+ int y1 = ty + p1.y();
+ int x2 = tx + p2.x();
+ int y2 = ty + p2.y();
+ if (x1 > x2) {
+ kSwap(x1, x2);
+ if (bsOrientation(curBS) == BSHorizontal) kSwap(prevBS, nextBS);
+ }
+ if (y1 > y2) {
+ kSwap(y1, y2);
+ if (bsOrientation(curBS) == BSVertical) kSwap(prevBS, nextBS);
+ }
+
+// kdDebug(6040) << "segment(" << x1 << "," << y1 << ") - (" << x2 << "," << y2 << ")" << endl;
+/* p->setPen(Qt::gray);
+ p->drawLine(x1,y1,x2,y2);*/
+ switch (curBS) {
+ case RenderObject::BSLeft:
+ case RenderObject::BSRight:
+/* p->setPen(QColor("#ffe4dd"));
+ p->drawLine(
+ x1 - (curBS == RenderObject::BSLeft ? ow : 0),
+ y1 - (prevBS == RenderObject::BSTop ? ow : 0),
+ x2 + (curBS == RenderObject::BSRight ? ow : 0),
+ y2 + (nextBS == RenderObject::BSBottom ? ow : 0)
+ );*/
+ o->drawBorder(p,
+ x1 - (curBS == RenderObject::BSLeft ? ow : 0),
+ y1 - (prevBS == RenderObject::BSTop ? ow : 0),
+ x2 + (curBS == RenderObject::BSRight ? ow : 0),
+ y2 + (nextBS == RenderObject::BSBottom ? ow : 0),
+ curBS, oc, o->style()->color(), os,
+ prevBS == RenderObject::BSTop ? ow
+ : prevBS == RenderObject::BSBottom ? -ow : 0,
+ nextBS == RenderObject::BSTop ? -ow
+ : nextBS == RenderObject::BSBottom ? ow : 0,
+ true);
+ break;
+ case RenderObject::BSBottom:
+ case RenderObject::BSTop:
+// kdDebug(6040) << "BSTop/BSBottom: prevBS " << prevBS << " curBS " << curBS << " nextBS " << nextBS << endl;
+ o->drawBorder(p,
+ x1 - (prevBS == RenderObject::BSLeft ? ow : 0),
+ y1 - (curBS == RenderObject::BSTop ? ow : 0),
+ x2 + (nextBS == RenderObject::BSRight ? ow : 0),
+ y2 + (curBS == RenderObject::BSBottom ? ow : 0),
+ curBS, oc, o->style()->color(), os,
+ prevBS == RenderObject::BSLeft ? ow
+ : prevBS == RenderObject::BSRight ? -ow : 0,
+ nextBS == RenderObject::BSLeft ? -ow
+ : nextBS == RenderObject::BSRight ? ow : 0,
+ true);
+ break;
+ }
+}
+
+void RenderInline::paintOutlinePath(QPainter *p, int tx, int ty, const QPoint *begin, const QPoint *end, BorderSide bs, int direction, BorderSide endingBS)
+{
+ int ow = style()->outlineWidth();
+ if (ow == 0 || m_isContinuation) // Continuations get painted by the original inline.
+ return;
+
+ QPoint last = *begin;
+ BorderSide lastBS = bs;
+ Q_ASSERT(begin != end);
+ ++begin;
+
+// kdDebug(6040) << "last: " << last << endl;
+
+ bs = newBorderSide(bs, direction, last, *begin);
+// kdDebug(6040) << "newBorderSide: " << lastBS << " " << direction << "d " << last << " - " << *begin << " => " << bs << endl;
+
+ for (const QPoint *it = begin; it != end; ++it) {
+ QPoint cur = *it;
+// kdDebug(6040) << "cur: " << cur << endl;
+ BorderSide nextBS;
+ if (it + 1 != end) {
+ QPoint diff = cur - last;
+ direction = diff.x() + diff.y();
+ nextBS = newBorderSide(bs, direction, cur, *(it + 1));
+// kdDebug(6040) << "newBorderSide*: " << bs << " " << direction << "d " << cur << " - " << *(it + 1) << " => " << nextBS << endl;
+ } else
+ nextBS = endingBS;
+
+ Q_ASSERT(bsOrientation(bs) != bsOrientation(nextBS));
+ paintOutlineSegment(this, p, tx, ty, last, cur,
+ lastBS, bs, nextBS);
+ lastBS = bs;
+ last = cur;
+ bs = nextBS;
+ }
+
+}
+
+void RenderInline::calcMinMaxWidth()
+{
+ KHTMLAssert( !minMaxKnown() );
+
+#ifdef DEBUG_LAYOUT
+ kdDebug( 6040 ) << renderName() << "(RenderInline)::calcMinMaxWidth() this=" << this << endl;
+#endif
+
+ // Irrelevant, since some enclosing block will actually measure us and our children.
+ m_minWidth = 0;
+ m_maxWidth = 0;
+
+ setMinMaxKnown();
+}
+
+short RenderInline::width() const
+{
+ // Return the width of the minimal left side and the maximal right side.
+ short leftSide = 0;
+ short rightSide = 0;
+ for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
+ if (curr == firstLineBox() || curr->xPos() < leftSide)
+ leftSide = curr->xPos();
+ if (curr == firstLineBox() || curr->xPos() + curr->width() > rightSide)
+ rightSide = curr->xPos() + curr->width();
+ }
+
+ return rightSide - leftSide;
+}
+
+int RenderInline::height() const
+{
+ int h = 0;
+ if (firstLineBox())
+ h = lastLineBox()->yPos() + lastLineBox()->height() - firstLineBox()->yPos();
+ return h;
+}
+
+int RenderInline::offsetLeft() const
+{
+ int x = RenderFlow::offsetLeft();
+ if (firstLineBox())
+ x += firstLineBox()->xPos();
+ return x;
+}
+
+int RenderInline::offsetTop() const
+{
+ int y = RenderFlow::offsetTop();
+ if (firstLineBox())
+ y += firstLineBox()->yPos();
+ return y;
+}
+
+const char *RenderInline::renderName() const
+{
+ if (isRelPositioned())
+ return "RenderInline (relative positioned)";
+ if (isAnonymous())
+ return "RenderInline (anonymous)";
+ return "RenderInline";
+}
+
+bool RenderInline::nodeAtPoint(NodeInfo& info, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction, bool inside)
+{
+/*
+ if ( hitTestAction != HitTestSelfOnly ) {
+ for (RenderObject* child = lastChild(); child; child = child->previousSibling())
+ if (!child->layer() && !child->isFloating() && child->nodeAtPoint(info, _x, _y, _tx, _ty, HitTestAll))
+ inside = true;
+ }
+*/
+ // Check our line boxes if we're still not inside.
+ if (/*hitTestAction != HitTestChildrenOnly &&*/ !inside && style()->visibility() != HIDDEN) {
+ // See if we're inside one of our line boxes.
+ inside = hitTestLines(info, _x, _y, _tx, _ty, hitTestAction);
+ }
+
+ if (inside && element()) {
+ if (info.innerNode() && info.innerNode()->renderer() &&
+ !info.innerNode()->renderer()->isInline()) {
+ // Within the same layer, inlines are ALWAYS fully above blocks. Change inner node.
+ info.setInnerNode(element());
+
+ // Clear everything else.
+ info.setInnerNonSharedNode(0);
+ info.setURLElement(0);
+ }
+
+ if (!info.innerNode())
+ info.setInnerNode(element());
+
+ if(!info.innerNonSharedNode())
+ info.setInnerNonSharedNode(element());
+ }
+
+ return inside;
+}
+
+void RenderInline::caretPos(int offset, int flags, int &_x, int &_y, int &width, int &height)
+{
+ _x = -1;
+
+ RenderBlock *cb = containingBlock();
+ bool rtl = cb->style()->direction() == RTL;
+ bool outsideEnd = flags & CFOutsideEnd;
+ // I need to explain that: outsideEnd contains a meaningful value if
+ // and only if flags & CFOutside is set. If it is not, then randomly
+ // either the first or the last line box is returned.
+ // This doesn't matter because the only case this can happen is on an
+ // empty inline element, whose first and last line boxes are actually
+ // the same.
+ InlineFlowBox *line = !outsideEnd ^ rtl ? firstLineBox() : lastLineBox();
+
+ if (!line) { // umpf, handle "gracefully"
+ RenderFlow::caretPos(offset, flags, _x, _y, width, height);
+ return;
+ }
+
+ _x = line->xPos();
+ width = 1; // ### regard CFOverride
+
+ // Place caret outside the border
+ if (flags & CFOutside) {
+ RenderStyle *s = element() && element()->parent()
+ && element()->parent()->renderer()
+ ? element()->parent()->renderer()->style()
+ : style();
+ const QFontMetrics &fm = s->fontMetrics();
+ _y = line->yPos() + line->baseline() - fm.ascent();
+ height = fm.height();
+
+ if (!outsideEnd ^ rtl) {
+ _x -= line->marginBorderPaddingLeft();
+ } else {
+ _x += line->width() + line->marginBorderPaddingRight();
+ }
+
+ } else {
+ const QFontMetrics &fm = style()->fontMetrics();
+ _y = line->yPos() + line->baseline() - fm.ascent();
+ height = fm.height();
+ }
+
+ int absx, absy;
+ if (cb && cb->absolutePosition(absx,absy)) {
+ //kdDebug(6040) << "absx=" << absx << " absy=" << absy << endl;
+ _x += absx;
+ _y += absy;
+ } else {
+ // we don't know our absolute position, and there is no point returning
+ // just a relative one
+ _x = _y = -1;
+ }
+}
+
+inline int minXPos(const RenderInline *o)
+{
+ int retval=6666666;
+ if (!o->firstLineBox()) return 0;
+ for (InlineRunBox* curr = o->firstLineBox(); curr; curr = curr->nextLineBox())
+ retval = kMin( retval, int( curr->m_x ));
+ return retval;
+}
+
+int RenderInline::inlineXPos() const
+{
+ return minXPos(this);
+}
+
+int RenderInline::inlineYPos() const
+{
+ return firstLineBox() ? firstLineBox()->yPos() : 0;
+}
+
diff --git a/khtml/rendering/render_inline.h b/khtml/rendering/render_inline.h
new file mode 100644
index 000000000..b87e53037
--- /dev/null
+++ b/khtml/rendering/render_inline.h
@@ -0,0 +1,94 @@
+/*
+ * This file is part of the render object implementation for KHTML.
+ *
+ * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
+ * (C) 1999-2003 Antti Koivisto (koivisto@kde.org)
+ * (C) 2002-2003 Dirk Mueller (mueller@kde.org)
+ * (C) 2003 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.
+ *
+ */
+
+#ifndef RENDER_INLINE_H
+#define RENDER_INLINE_H
+
+#include "render_flow.h"
+
+namespace khtml {
+
+class RenderInline : public RenderFlow
+{
+public:
+ RenderInline(DOM::NodeImpl* node) : RenderFlow( node ), m_isContinuation( false ) {}
+
+ virtual const char *renderName() const;
+
+ virtual bool isRenderInline() const { return true; }
+ virtual bool isInlineFlow() const { return true; }
+ virtual bool childrenInline() const { return true; }
+
+ virtual bool isInlineContinuation() const;
+
+ virtual void addChildToFlow(RenderObject* newChild, RenderObject* beforeChild);
+
+ void splitInlines(RenderBlock* fromBlock, RenderBlock* toBlock, RenderBlock* middleBlock,
+ RenderObject* beforeChild, RenderFlow* oldCont);
+
+ void splitFlow(RenderObject* beforeChild, RenderBlock* newBlockBox,
+ RenderObject* newChild, RenderFlow* oldCont);
+
+ virtual void setStyle(RenderStyle* _style);
+ virtual void attach();
+
+ virtual void layout() {} // Do nothing for layout()
+
+ virtual void paint(PaintInfo&, int tx, int ty);
+
+ virtual bool nodeAtPoint(NodeInfo& info, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction, bool inside);
+
+ virtual void calcMinMaxWidth();
+
+ // overrides RenderObject
+ virtual bool requiresLayer() const { return isRelPositioned(); }
+
+ virtual short width() const;
+ virtual int height() const;
+
+ virtual int inlineXPos() const;
+ virtual int inlineYPos() const;
+
+ // used to calculate offsetWidth/Height. Overridden by inlines (render_flow) to return
+ // the remaining width on a given line (and the height of a single line).
+ virtual int offsetLeft() const;
+ virtual int offsetTop() const;
+
+ virtual void caretPos(int offset, int flags, int &_x, int &_y, int &width, int &height);
+ void paintOutlines(QPainter *p, int tx, int ty);
+
+protected:
+ static RenderInline* cloneInline(RenderFlow* src);
+ void paintOutlinePath(QPainter *p, int tx, int ty, const QPoint *begin, const QPoint *end, BorderSide startingBS, int initialDirection, BorderSide endingBS);
+
+private:
+ bool m_isContinuation : 1; // Whether or not we're a continuation of an inline.
+
+};
+
+} // namespace
+
+#endif // RENDER_BLOCK_H
+
diff --git a/khtml/rendering/render_layer.cpp b/khtml/rendering/render_layer.cpp
new file mode 100644
index 000000000..b4af3536c
--- /dev/null
+++ b/khtml/rendering/render_layer.cpp
@@ -0,0 +1,1830 @@
+/*
+ * Copyright (C) 2003 Apple Computer, Inc.
+ * (C) 2006 Germain Garand <germain@ebooksfrance.org>
+ * (C) 2006 Allan Sandfeld Jense <kde@carewolf.com>
+ *
+ * Portions are Copyright (C) 1998 Netscape Communications Corporation.
+ *
+ * Other contributors:
+ * Robert O'Callahan <roc+@cs.cmu.edu>
+ * David Baron <dbaron@fas.harvard.edu>
+ * Christian Biesinger <cbiesinger@web.de>
+ * Randall Jesup <rjesup@wgate.com>
+ * Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
+ * Josh Soref <timeless@mac.com>
+ * Boris Zbarsky <bzbarsky@mit.edu>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Alternatively, the contents of this file may be used under the terms
+ * of either the Mozilla Public License Version 1.1, found at
+ * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public
+ * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html
+ * (the "GPL"), in which case the provisions of the MPL or the GPL are
+ * applicable instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of one of those two
+ * licenses (the MPL or the GPL) and not to allow others to use your
+ * version of this file under the LGPL, indicate your decision by
+ * deletingthe provisions above and replace them with the notice and
+ * other provisions required by the MPL or the GPL, as the case may be.
+ * If you do not delete the provisions above, a recipient may use your
+ * version of this file under any of the LGPL, the MPL or the GPL.
+ */
+
+//#define BOX_DEBUG
+
+#include "render_layer.h"
+#include <kdebug.h>
+#include <assert.h>
+#include "khtmlview.h"
+#include "render_canvas.h"
+#include "render_arena.h"
+#include "render_replaced.h"
+#include "xml/dom_docimpl.h"
+#include "xml/dom2_eventsimpl.h"
+#include "misc/htmltags.h"
+#include "html/html_blockimpl.h"
+#include "xml/dom_restyler.h"
+
+#include <qscrollbar.h>
+#include <qptrvector.h>
+#include <qstyle.h>
+
+using namespace DOM;
+using namespace khtml;
+
+#ifdef APPLE_CHANGES
+QScrollBar* RenderLayer::gScrollBar = 0;
+#endif
+
+#ifndef NDEBUG
+static bool inRenderLayerDetach;
+#endif
+
+void
+RenderScrollMediator::slotValueChanged()
+{
+ m_layer->updateScrollPositionFromScrollbars();
+}
+
+RenderLayer::RenderLayer(RenderObject* object)
+: m_object( object ),
+m_parent( 0 ),
+m_previous( 0 ),
+m_next( 0 ),
+m_first( 0 ),
+m_last( 0 ),
+m_x( 0 ),
+m_y( 0 ),
+m_scrollX( 0 ),
+m_scrollY( 0 ),
+m_scrollWidth( 0 ),
+m_scrollHeight( 0 ),
+m_hBar( 0 ),
+m_vBar( 0 ),
+m_scrollMediator( 0 ),
+m_posZOrderList( 0 ),
+m_negZOrderList( 0 ),
+m_overflowList(0),
+m_zOrderListsDirty( true ),
+m_overflowListDirty(true),
+m_isOverflowOnly( shouldBeOverflowOnly() ),
+m_markedForRepaint( false ),
+m_hasOverlaidWidgets( false ),
+m_marquee( 0 )
+{
+}
+
+RenderLayer::~RenderLayer()
+{
+ // Child layers will be deleted by their corresponding render objects, so
+ // our destructor doesn't have to do anything.
+ delete m_hBar;
+ delete m_vBar;
+ delete m_scrollMediator;
+ delete m_posZOrderList;
+ delete m_negZOrderList;
+ delete m_overflowList;
+ delete m_marquee;
+}
+
+void RenderLayer::updateLayerPosition()
+{
+
+ // The canvas is sized to the docWidth/Height over in RenderCanvas::layout, so we
+ // don't need to ever update our layer position here.
+ if (renderer()->isCanvas())
+ return;
+
+ int x = m_object->xPos();
+ int y = m_object->yPos() - m_object->borderTopExtra();
+
+ if (!m_object->isPositioned()) {
+ // We must adjust our position by walking up the render tree looking for the
+ // nearest enclosing object with a layer.
+ RenderObject* curr = m_object->parent();
+ while (curr && !curr->layer()) {
+ x += curr->xPos();
+ y += curr->yPos();
+ curr = curr->parent();
+ }
+ if (curr)
+ y += curr->borderTopExtra();
+ }
+
+ if (m_object->isRelPositioned())
+ static_cast<RenderBox*>(m_object)->relativePositionOffset(x, y);
+
+ // Subtract our parent's scroll offset.
+ if (m_object->isPositioned() && enclosingPositionedAncestor()) {
+ RenderLayer* positionedParent = enclosingPositionedAncestor();
+
+ // For positioned layers, we subtract out the enclosing positioned layer's scroll offset.
+ positionedParent->subtractScrollOffset(x, y);
+ positionedParent->checkInlineRelOffset(m_object, x, y);
+ }
+ else if (parent())
+ parent()->subtractScrollOffset(x, y);
+
+ setPos(x,y);
+}
+
+QRegion RenderLayer::paintedRegion(RenderLayer* rootLayer)
+{
+ updateZOrderLists();
+ QRegion r;
+ if (m_negZOrderList) {
+ uint count = m_negZOrderList->count();
+ for (uint i = 0; i < count; i++) {
+ RenderLayer* child = m_negZOrderList->at(i);
+ r += child->paintedRegion(rootLayer);
+ }
+ }
+ const RenderStyle *s= renderer()->style();
+ if (s->visibility() == VISIBLE) {
+ int x = 0; int y = 0;
+ convertToLayerCoords(rootLayer,x,y);
+ QRect cr(x,y,width(),height());
+ if ( s->backgroundImage() || s->backgroundColor().isValid() || s->hasBorder() ||
+ renderer()->scrollsOverflow() || renderer()->isReplaced() ) {
+ r += cr;
+ } else {
+ r += renderer()->visibleFlowRegion(x, y);
+ }
+ }
+
+ if (m_posZOrderList) {
+ uint count = m_posZOrderList->count();
+ for (uint i = 0; i < count; i++) {
+ RenderLayer* child = m_posZOrderList->at(i);
+ r += child->paintedRegion(rootLayer);
+ }
+ }
+ return r;
+}
+
+void RenderLayer::repaint( Priority p, bool markForRepaint )
+{
+ if (markForRepaint && m_markedForRepaint)
+ return;
+ for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
+ child->repaint( p, markForRepaint );
+ QRect layerBounds, damageRect, fgrect;
+ calculateRects(renderer()->canvas()->layer(), renderer()->viewRect(), layerBounds, damageRect, fgrect);
+ m_visibleRect = damageRect.intersect( layerBounds );
+ if (m_visibleRect.isValid())
+ renderer()->canvas()->repaintViewRectangle( m_visibleRect.x(), m_visibleRect.y(), m_visibleRect.width(), m_visibleRect.height(), (p > NormalPriority) );
+ if (markForRepaint)
+ m_markedForRepaint = true;
+}
+
+void RenderLayer::updateLayerPositions(RenderLayer* rootLayer, bool doFullRepaint, bool checkForRepaint)
+{
+ if (doFullRepaint) {
+ m_object->repaint();
+ checkForRepaint = doFullRepaint = false;
+ }
+
+ updateLayerPosition(); // For relpositioned layers or non-positioned layers,
+ // we need to keep in sync, since we may have shifted relative
+ // to our parent layer.
+
+ if (m_hBar || m_vBar) {
+ // Need to position the scrollbars.
+ int x = 0;
+ int y = 0;
+ convertToLayerCoords(rootLayer, x, y);
+ QRect layerBounds = QRect(x,y,width(),height());
+ positionScrollbars(layerBounds);
+ }
+
+#ifdef APPLE_CHANGES
+ // FIXME: Child object could override visibility.
+ if (checkForRepaint && (m_object->style()->visibility() == VISIBLE))
+ m_object->repaintAfterLayoutIfNeeded(m_repaintRect, m_fullRepaintRect);
+#else
+ if (checkForRepaint && m_markedForRepaint) {
+ QRect layerBounds, damageRect, fgrect;
+ calculateRects(rootLayer, renderer()->viewRect(), layerBounds, damageRect, fgrect);
+ QRect vr = damageRect.intersect( layerBounds );
+ if (vr != m_visibleRect && vr.isValid()) {
+ renderer()->canvas()->repaintViewRectangle( vr.x(), vr.y(), vr.width(), vr.height() );
+ m_visibleRect = vr;
+ }
+ }
+ m_markedForRepaint = false;
+#endif
+
+ for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
+ child->updateLayerPositions(rootLayer, doFullRepaint, checkForRepaint);
+
+ // With all our children positioned, now update our marquee if we need to.
+ if (m_marquee)
+ m_marquee->updateMarqueePosition();
+}
+
+void RenderLayer::updateWidgetMasks(RenderLayer* rootLayer)
+{
+ if (hasOverlaidWidgets() && !renderer()->canvas()->pagedMode()) {
+ updateZOrderLists();
+ uint count = m_posZOrderList ? m_posZOrderList->count() : 0;
+ bool needUpdate = (count || !m_region.isNull());
+ if (count) {
+ QScrollView* sv = m_object->document()->view();
+ m_region = QRect(0,0,sv->contentsWidth(),sv->contentsHeight());
+
+ for (uint i = 0; i < count; i++) {
+ RenderLayer* child = m_posZOrderList->at(i);
+ if (child->zIndex() == 0 && child->renderer()->style()->position() == STATIC)
+ continue; // we don't know the widget's exact stacking position within flow
+ m_region -= child->paintedRegion(rootLayer);
+ }
+ } else {
+ m_region = QRegion();
+ }
+ if (needUpdate)
+ renderer()->updateWidgetMasks();
+ }
+ for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
+ child->updateWidgetMasks(rootLayer);
+}
+
+short RenderLayer::width() const
+{
+ int w = m_object->width();
+ if (!m_object->hasOverflowClip())
+ w = kMax(m_object->overflowWidth(), w);
+ return w;
+}
+
+int RenderLayer::height() const
+{
+ int h = m_object->height() + m_object->borderTopExtra() + m_object->borderBottomExtra();
+ if (!m_object->hasOverflowClip())
+ h = kMax(m_object->overflowHeight(), h);
+ return h;
+}
+
+
+RenderLayer *RenderLayer::stackingContext() const
+{
+ RenderLayer* curr = parent();
+ for ( ; curr && !curr->m_object->isCanvas() &&
+ curr->m_object->style()->hasAutoZIndex();
+ curr = curr->parent());
+ return curr;
+}
+
+RenderLayer* RenderLayer::enclosingPositionedAncestor() const
+{
+ RenderLayer* curr = parent();
+ for ( ; curr && !curr->m_object->isCanvas() &&
+ !curr->m_object->isPositioned() && !curr->m_object->isRelPositioned();
+ curr = curr->parent());
+
+ return curr;
+}
+
+#ifdef APPLE_CHANGES
+bool RenderLayer::isTransparent()
+{
+ return m_object->style()->opacity() < 1.0f;
+}
+
+RenderLayer* RenderLayer::transparentAncestor()
+{
+ RenderLayer* curr = parent();
+ for ( ; curr && curr->m_object->style()->opacity() == 1.0f; curr = curr->parent());
+ return curr;
+}
+#endif
+
+void* RenderLayer::operator new(size_t sz, RenderArena* renderArena) throw()
+{
+ return renderArena->allocate(sz);
+}
+
+void RenderLayer::operator delete(void* ptr, size_t sz)
+{
+ assert(inRenderLayerDetach);
+
+ // Stash size where detach can find it.
+ *(size_t *)ptr = sz;
+}
+
+void RenderLayer::detach(RenderArena* renderArena)
+{
+#ifndef NDEBUG
+ inRenderLayerDetach = true;
+#endif
+ delete this;
+#ifndef NDEBUG
+ inRenderLayerDetach = false;
+#endif
+
+ // Recover the size left there for us by operator delete and free the memory.
+ renderArena->free(*(size_t *)this, this);
+}
+
+void RenderLayer::addChild(RenderLayer *child, RenderLayer* beforeChild)
+{
+ RenderLayer* prevSibling = beforeChild ? beforeChild->previousSibling() : lastChild();
+ if (prevSibling) {
+ child->setPreviousSibling(prevSibling);
+ prevSibling->setNextSibling(child);
+ }
+ else
+ setFirstChild(child);
+
+ if (beforeChild) {
+ beforeChild->setPreviousSibling(child);
+ child->setNextSibling(beforeChild);
+ }
+ else
+ setLastChild(child);
+
+ child->setParent(this);
+
+ if (child->isOverflowOnly())
+ dirtyOverflowList();
+ else {
+ // Dirty the z-order list in which we are contained. The stackingContext() can be null in the
+ // case where we're building up generated content layers. This is ok, since the lists will start
+ // off dirty in that case anyway.
+ RenderLayer* stackingContext = child->stackingContext();
+ if (stackingContext)
+ stackingContext->dirtyZOrderLists();
+ }
+}
+
+RenderLayer* RenderLayer::removeChild(RenderLayer* oldChild)
+{
+ // remove the child
+ if (oldChild->previousSibling())
+ oldChild->previousSibling()->setNextSibling(oldChild->nextSibling());
+ if (oldChild->nextSibling())
+ oldChild->nextSibling()->setPreviousSibling(oldChild->previousSibling());
+
+ if (m_first == oldChild)
+ m_first = oldChild->nextSibling();
+ if (m_last == oldChild)
+ m_last = oldChild->previousSibling();
+
+ if (oldChild->isOverflowOnly())
+ dirtyOverflowList();
+ else {
+ // Dirty the z-order list in which we are contained. When called via the
+ // reattachment process in removeOnlyThisLayer, the layer may already be disconnected
+ // from the main layer tree, so we need to null-check the |stackingContext| value.
+ RenderLayer* stackingContext = oldChild->stackingContext();
+ if (stackingContext)
+ stackingContext->dirtyZOrderLists();
+ }
+
+ oldChild->setPreviousSibling(0);
+ oldChild->setNextSibling(0);
+ oldChild->setParent(0);
+
+ return oldChild;
+}
+
+void RenderLayer::removeOnlyThisLayer()
+{
+ if (!m_parent)
+ return;
+
+ // Remove us from the parent.
+ RenderLayer* parent = m_parent;
+ RenderLayer* nextSib = nextSibling();
+ parent->removeChild(this);
+
+ // Now walk our kids and reattach them to our parent.
+ RenderLayer* current = m_first;
+ while (current) {
+ RenderLayer* next = current->nextSibling();
+ removeChild(current);
+ parent->addChild(current, nextSib);
+ current = next;
+ }
+
+ detach(renderer()->renderArena());
+}
+
+void RenderLayer::insertOnlyThisLayer()
+{
+ if (!m_parent && renderer()->parent()) {
+ // We need to connect ourselves when our renderer() has a parent.
+ // Find our enclosingLayer and add ourselves.
+ RenderLayer* parentLayer = renderer()->parent()->enclosingLayer();
+ if (parentLayer)
+ parentLayer->addChild(this,
+ renderer()->parent()->findNextLayer(parentLayer, renderer()));
+ }
+
+ // Remove all descendant layers from the hierarchy and add them to the new position.
+ for (RenderObject* curr = renderer()->firstChild(); curr; curr = curr->nextSibling())
+ curr->moveLayers(m_parent, this);
+}
+
+void RenderLayer::convertToLayerCoords(const RenderLayer* ancestorLayer, int& x, int& y) const
+{
+ if (ancestorLayer == this)
+ return;
+
+ if (m_object->style()->position() == FIXED) {
+ // Add in the offset of the view. We can obtain this by calling
+ // absolutePosition() on the RenderCanvas.
+ int xOff, yOff;
+ m_object->absolutePosition(xOff, yOff, true);
+ x += xOff;
+ y += yOff;
+ return;
+ }
+
+ RenderLayer* parentLayer;
+ if (m_object->style()->position() == ABSOLUTE)
+ parentLayer = enclosingPositionedAncestor();
+ else
+ parentLayer = parent();
+
+ if (!parentLayer) return;
+
+ parentLayer->convertToLayerCoords(ancestorLayer, x, y);
+
+ x += xPos();
+ y += yPos();
+}
+
+void RenderLayer::scrollOffset(int& x, int& y)
+{
+ x += scrollXOffset();
+ y += scrollYOffset();
+}
+
+void RenderLayer::subtractScrollOffset(int& x, int& y)
+{
+ x -= scrollXOffset();
+ y -= scrollYOffset();
+}
+
+void RenderLayer::checkInlineRelOffset(const RenderObject* o, int& x, int& y)
+{
+ if(o->style()->position() != ABSOLUTE || !renderer()->isRelPositioned() || !renderer()->isInlineFlow())
+ return;
+
+ // Our renderer is an enclosing relpositioned inline, we need to add in the offset of the first line
+ // box from the rest of the content, but only in the cases where we know our descendant is positioned
+ // relative to the inline itself.
+ assert( o->container() == m_object );
+
+ RenderFlow* flow = static_cast<RenderFlow*>(m_object);
+ int sx = 0, sy = 0;
+ if (flow->firstLineBox()) {
+ if (flow->style()->direction() == LTR)
+ sx = flow->firstLineBox()->xPos();
+ else
+ sx = flow->lastLineBox()->xPos();
+ sy = flow->firstLineBox()->yPos();
+ } else {
+ sx = flow->staticX(); // ###
+ sy = flow->staticY();
+ }
+ bool isInlineType = o->style()->isOriginalDisplayInlineType();
+
+ if (!o->hasStaticX())
+ x += sx;
+
+ // Despite the positioned child being a block display type inside an inline, we still keep
+ // its x locked to our left. Arguably the correct behavior would be to go flush left to
+ // the block that contains us, but that isn't what other browsers do.
+ if (o->hasStaticX() && !isInlineType)
+ // Avoid adding in the left border/padding of the containing block twice. Subtract it out.
+ x += sx - (o->containingBlock()->borderLeft() + o->containingBlock()->paddingLeft());
+
+ if (!o->hasStaticY())
+ y += sy;
+}
+
+void RenderLayer::scrollToOffset(int x, int y, bool updateScrollbars, bool repaint)
+{
+ if (renderer()->style()->overflowX() != OMARQUEE || !renderer()->hasOverflowClip()) {
+ if (x < 0) x = 0;
+ if (y < 0) y = 0;
+
+ // Call the scrollWidth/Height functions so that the dimensions will be computed if they need
+ // to be (for overflow:hidden blocks).
+ // ### merge the scrollWidth()/scrollHeight() methods
+ int maxX = m_scrollWidth - m_object->clientWidth();
+ int maxY = m_scrollHeight - m_object->clientHeight();
+
+ if (x > maxX) x = maxX;
+ if (y > maxY) y = maxY;
+ }
+
+ // FIXME: Eventually, we will want to perform a blit. For now never
+ // blit, since the check for blitting is going to be very
+ // complicated (since it will involve testing whether our layer
+ // is either occluded by another layer or clipped by an enclosing
+ // layer or contains fixed backgrounds, etc.).
+ m_scrollX = x;
+ m_scrollY = y;
+
+ // Update the positions of our child layers.
+ RenderLayer* rootLayer = root();
+ for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
+ child->updateLayerPositions(rootLayer);
+
+ // Fire the scroll DOM event.
+ m_object->element()->dispatchHTMLEvent(EventImpl::SCROLL_EVENT, true, false);
+
+ // Just schedule a full repaint of our object.
+ if (repaint)
+ m_object->repaint(RealtimePriority);
+
+ if (updateScrollbars) {
+ if (m_hBar)
+ m_hBar->setValue(m_scrollX);
+ if (m_vBar)
+ m_vBar->setValue(m_scrollY);
+ }
+}
+
+void RenderLayer::updateScrollPositionFromScrollbars()
+{
+ bool needUpdate = false;
+ int newX = m_scrollX;
+ int newY = m_scrollY;
+
+ if (m_hBar) {
+ newX = m_hBar->value();
+ if (newX != m_scrollX)
+ needUpdate = true;
+ }
+
+ if (m_vBar) {
+ newY = m_vBar->value();
+ if (newY != m_scrollY)
+ needUpdate = true;
+ }
+
+ if (needUpdate)
+ scrollToOffset(newX, newY, false);
+}
+
+void
+RenderLayer::showScrollbar(Qt::Orientation o, bool show)
+{
+ QScrollBar *sb = (o == Qt::Horizontal) ? m_hBar : m_vBar;
+
+ if (show && !sb) {
+ QScrollView* scrollView = m_object->document()->view();
+ sb = new QScrollBar(o, scrollView, "__khtml");
+ scrollView->addChild(sb, 0, -50000);
+ sb->setBackgroundMode(QWidget::NoBackground);
+ sb->show();
+ if (!m_scrollMediator)
+ m_scrollMediator = new RenderScrollMediator(this);
+ m_scrollMediator->connect(sb, SIGNAL(valueChanged(int)), SLOT(slotValueChanged()));
+ }
+ else if (!show && sb) {
+ delete sb;
+ sb = 0;
+ }
+
+ if (o == Qt::Horizontal)
+ m_hBar = sb;
+ else
+ m_vBar = sb;
+}
+
+int RenderLayer::verticalScrollbarWidth()
+{
+ if (!m_vBar)
+ return 0;
+
+#ifdef APPLE_CHANGES
+ return m_vBar->width();
+#else
+ return m_vBar->style().pixelMetric(QStyle::PM_ScrollBarExtent);
+#endif
+
+}
+
+int RenderLayer::horizontalScrollbarHeight()
+{
+ if (!m_hBar)
+ return 0;
+
+#ifdef APPLE_CHANGES
+ return m_hBar->height();
+#else
+ return m_hBar->style().pixelMetric(QStyle::PM_ScrollBarExtent);
+#endif
+
+}
+
+void RenderLayer::positionScrollbars(const QRect& absBounds)
+{
+#ifdef APPLE_CHANGES
+ if (m_vBar) {
+ scrollView->addChild(m_vBar, absBounds.x()+absBounds.width()-m_object->borderRight()-m_vBar->width(),
+ absBounds.y()+m_object->borderTop());
+ m_vBar->resize(m_vBar->width(), absBounds.height() -
+ (m_object->borderTop()+m_object->borderBottom()) -
+ (m_hBar ? m_hBar->height()-1 : 0));
+ }
+
+ if (m_hBar) {
+ scrollView->addChild(m_hBar, absBounds.x()+m_object->borderLeft(),
+ absBounds.y()+absBounds.height()-m_object->borderBottom()-m_hBar->height());
+ m_hBar->resize(absBounds.width() - (m_object->borderLeft()+m_object->borderRight()) -
+ (m_vBar ? m_vBar->width()-1 : 0), m_hBar->height());
+ }
+#else
+ int tx = absBounds.x();
+ int ty = absBounds.y();
+ int bl = m_object->borderLeft();
+ int bt = m_object->borderTop();
+ int w = width() - bl - m_object->borderRight();
+ int h = height() - bt - m_object->borderBottom();
+
+ if (w <= 0 || h <= 0 || (!m_vBar && !m_hBar))
+ return;
+
+ QScrollView* scrollView = m_object->document()->view();
+
+ tx += bl;
+ ty += bt;
+
+ QScrollBar *b = m_hBar;
+ if (!m_hBar)
+ b = m_vBar;
+ int sw = b->style().pixelMetric(QStyle::PM_ScrollBarExtent);
+
+ if (m_vBar) {
+ QRect vBarRect = QRect(tx + w - sw + 1, ty, sw, h - (m_hBar ? sw : 0) + 1);
+ m_vBar->resize(vBarRect.width(), vBarRect.height());
+ scrollView->addChild(m_vBar, vBarRect.x(), vBarRect.y());
+ }
+
+ if (m_hBar) {
+ QRect hBarRect = QRect(tx, ty + h - sw + 1, w - (m_vBar ? sw : 0) + 1, sw);
+ m_hBar->resize(hBarRect.width(), hBarRect.height());
+ scrollView->addChild(m_hBar, hBarRect.x(), hBarRect.y());
+ }
+#endif
+}
+
+#define LINE_STEP 10
+#define PAGE_KEEP 40
+
+void RenderLayer::checkScrollbarsAfterLayout()
+{
+ int rightPos = m_object->rightmostPosition(true);
+ int bottomPos = m_object->lowestPosition(true);
+
+/* TODO
+ m_scrollLeft = m_object->leftmostPosition(true);
+ m_scrollTop = m_object->highestPosition(true);
+*/
+
+ int clientWidth = m_object->clientWidth();
+ int clientHeight = m_object->clientHeight();
+ m_scrollWidth = clientWidth;
+ m_scrollHeight = clientHeight;
+
+ if (rightPos - m_object->borderLeft() > m_scrollWidth)
+ m_scrollWidth = rightPos - m_object->borderLeft();
+ if (bottomPos - m_object->borderTop() > m_scrollHeight)
+ m_scrollHeight = bottomPos - m_object->borderTop();
+
+ bool needHorizontalBar = rightPos > width();
+ bool needVerticalBar = bottomPos > height();
+
+ bool haveHorizontalBar = m_hBar && m_hBar->isEnabled();
+ bool haveVerticalBar = m_vBar && m_vBar->isEnabled();
+
+ bool hasOvf = m_object->hasOverflowClip();
+
+ // overflow:scroll should just enable/disable.
+ if (hasOvf && m_object->style()->overflowX() == OSCROLL)
+ m_hBar->setEnabled(needHorizontalBar);
+ if (hasOvf && m_object->style()->overflowY() == OSCROLL)
+ m_vBar->setEnabled(needVerticalBar);
+
+ // overflow:auto may need to lay out again if scrollbars got added/removed.
+ bool scrollbarsChanged = (hasOvf && m_object->style()->overflowX() == OAUTO && haveHorizontalBar != needHorizontalBar)
+ || (hasOvf && m_object->style()->overflowY() == OAUTO && haveVerticalBar != needVerticalBar);
+ if (scrollbarsChanged) {
+ if (m_object->style()->overflowX() == OAUTO) {
+ showScrollbar(Qt::Horizontal, needHorizontalBar);
+ if (m_hBar)
+ m_hBar->setEnabled(true);
+ }
+ if (m_object->style()->overflowY() == OAUTO) {
+ showScrollbar(Qt::Vertical, needVerticalBar);
+ if (m_vBar)
+ m_vBar->setEnabled(true);
+ }
+
+ m_object->setNeedsLayout(true);
+ if (m_object->isRenderBlock())
+ static_cast<RenderBlock*>(m_object)->layoutBlock(true);
+ else
+ m_object->layout();
+ return;
+ }
+
+ // Set up the range (and page step/line step).
+ if (m_hBar) {
+ int pageStep = (clientWidth-PAGE_KEEP);
+ if (pageStep < 0) pageStep = clientWidth;
+ m_hBar->setSteps(LINE_STEP, pageStep);
+#ifdef APPLE_CHANGES
+ m_hBar->setKnobProportion(clientWidth, m_scrollWidth);
+#else
+ m_hBar->setRange(0, needHorizontalBar ? m_scrollWidth-clientWidth : 0);
+#endif
+ }
+ if (m_vBar) {
+ int pageStep = (clientHeight-PAGE_KEEP);
+ if (pageStep < 0) pageStep = clientHeight;
+ m_vBar->setSteps(LINE_STEP, pageStep);
+#ifdef APPLE_CHANGES
+ m_vBar->setKnobProportion(clientHeight, m_scrollHeight);
+#else
+ m_vBar->setRange(0, needVerticalBar ? m_scrollHeight-clientHeight : 0);
+#endif
+ }
+}
+
+void RenderLayer::paintScrollbars(RenderObject::PaintInfo& pI)
+{
+#ifdef APPLE_CHANGES
+ if (m_hBar)
+ m_hBar->paint(p, damageRect);
+ if (m_vBar)
+ m_vBar->paint(p, damageRect);
+#else
+ if (!m_object->element())
+ return;
+
+ QScrollView* scrollView = m_object->document()->view();
+ if (m_hBar) {
+ int x = m_hBar->x();
+ int y = m_hBar->y();
+ scrollView->viewportToContents(x, y, x, y);
+ RenderWidget::paintWidget(pI, m_hBar, x, y);
+ }
+ if (m_vBar) {
+ int x = m_vBar->x();
+ int y = m_vBar->y();
+ scrollView->viewportToContents(x, y, x, y);
+ RenderWidget::paintWidget(pI, m_vBar, x, y);
+ }
+#endif
+}
+
+void RenderLayer::paint(QPainter *p, const QRect& damageRect, bool selectionOnly)
+{
+ paintLayer(this, p, damageRect, selectionOnly);
+}
+
+static void setClip(QPainter* p, const QRect& paintDirtyRect, const QRect& clipRect)
+{
+ if (paintDirtyRect == clipRect)
+ return;
+ p->save();
+
+#ifdef APPLE_CHANGES
+ p->addClip(clipRect);
+#else
+
+ QRect clippedRect = p->xForm(clipRect);
+ QRegion creg(clippedRect);
+ QRegion old = p->clipRegion();
+ if (!old.isNull())
+ creg = old.intersect(creg);
+ p->setClipRegion(creg);
+#endif
+
+}
+
+static void restoreClip(QPainter* p, const QRect& paintDirtyRect, const QRect& clipRect)
+{
+ if (paintDirtyRect == clipRect)
+ return;
+ p->restore();
+}
+
+void RenderLayer::paintLayer(RenderLayer* rootLayer, QPainter *p,
+ const QRect& paintDirtyRect, bool selectionOnly)
+{
+ // Calculate the clip rects we should use.
+ QRect layerBounds, damageRect, clipRectToApply;
+ calculateRects(rootLayer, paintDirtyRect, layerBounds, damageRect, clipRectToApply);
+ int x = layerBounds.x();
+ int y = layerBounds.y();
+
+ // Ensure our lists are up-to-date.
+ updateZOrderLists();
+ updateOverflowList();
+
+#ifdef APPLE_CHANGES
+ // Set our transparency if we need to.
+ if (isTransparent())
+ p->beginTransparencyLayer(renderer()->style()->opacity());
+#endif
+
+ // We want to paint our layer, but only if we intersect the damage rect.
+ bool shouldPaint = intersectsDamageRect(layerBounds, damageRect);
+ if (shouldPaint && !selectionOnly) {
+ // Paint our background first, before painting any child layers.
+ if (!damageRect.isEmpty()) {
+ // Establish the clip used to paint our background.
+ setClip(p, paintDirtyRect, damageRect);
+
+ // Paint the background.
+ RenderObject::PaintInfo paintInfo(p, damageRect, PaintActionElementBackground);
+ renderer()->paint(paintInfo,
+ x - renderer()->xPos(), y - renderer()->yPos() + renderer()->borderTopExtra());
+
+ // Position our scrollbars.
+ positionScrollbars(layerBounds);
+
+ // Our scrollbar widgets paint exactly when we tell them to, so that they work properly with
+ // z-index. We paint after we painted the background/border, so that the scrollbars will
+ // sit above the background/border.
+ paintScrollbars(paintInfo);
+
+ // Restore the clip.
+ restoreClip(p, paintDirtyRect, damageRect);
+ }
+ }
+
+ // Now walk the sorted list of children with negative z-indices.
+ if (m_negZOrderList) {
+ uint count = m_negZOrderList->count();
+ for (uint i = 0; i < count; i++) {
+ RenderLayer* child = m_negZOrderList->at(i);
+ child->paintLayer(rootLayer, p, paintDirtyRect, selectionOnly);
+ }
+ }
+
+ // Now establish the appropriate clip and paint our child RenderObjects.
+ if (shouldPaint && !clipRectToApply.isEmpty()) {
+ // Set up the clip used when painting our children.
+ setClip(p, paintDirtyRect, clipRectToApply);
+
+ RenderObject::PaintInfo paintInfo(p, clipRectToApply, PaintActionSelection);
+
+ int tx = x - renderer()->xPos();
+ int ty = y - renderer()->yPos() + renderer()->borderTopExtra();
+
+ if (selectionOnly)
+ renderer()->paint(paintInfo, tx, ty);
+ else {
+ paintInfo.phase = PaintActionChildBackgrounds;
+ renderer()->paint(paintInfo, tx, ty);
+ paintInfo.phase = PaintActionFloat;
+ renderer()->paint(paintInfo, tx, ty);
+ paintInfo.phase = PaintActionForeground;
+ renderer()->paint(paintInfo, tx, ty);
+ RenderCanvas *rc = static_cast<RenderCanvas*>(renderer()->document()->renderer());
+ if (rc->maximalOutlineSize()) {
+ paintInfo.phase = PaintActionOutline;
+ renderer()->paint(paintInfo, tx, ty);
+ }
+ if (rc->selectionStart() && rc->selectionEnd()) {
+ paintInfo.phase = PaintActionSelection;
+ renderer()->paint(paintInfo, tx, ty);
+ }
+ }
+
+ // Now restore our clip.
+ restoreClip(p, paintDirtyRect, clipRectToApply);
+ }
+
+ // Paint any child layers that have overflow.
+ if (m_overflowList)
+ for (QValueList<RenderLayer*>::iterator it = m_overflowList->begin(); it != m_overflowList->end(); ++it)
+ (*it)->paintLayer(rootLayer, p, paintDirtyRect, selectionOnly);
+
+ // Now walk the sorted list of children with positive z-indices.
+ if (m_posZOrderList) {
+ uint count = m_posZOrderList->count();
+ for (uint i = 0; i < count; i++) {
+ RenderLayer* child = m_posZOrderList->at(i);
+ child->paintLayer(rootLayer, p, paintDirtyRect, selectionOnly);
+ }
+ }
+
+#ifdef BOX_DEBUG
+ {
+ int ax=0;
+ int ay=0;
+ renderer()->absolutePosition( ax, ay );
+ p->setPen(QPen(QColor("yellow"), 1, Qt::DotLine));
+ p->setBrush( Qt::NoBrush );
+ p->drawRect(ax, ay, width(), height());
+ }
+#endif
+
+#ifdef APPLE_CHANGES
+ // End our transparency layer
+ if (isTransparent())
+ p->endTransparencyLayer();
+#endif
+}
+
+bool RenderLayer::nodeAtPoint(RenderObject::NodeInfo& info, int x, int y)
+{
+#ifdef APPLE_CHANGES
+ // Clear our our scrollbar variable
+ RenderLayer::gScrollBar = 0;
+#endif
+
+ int stx = m_x;
+ int sty = m_y;
+
+#ifdef __GNUC__
+#warning HACK
+#endif
+ if (renderer()->isCanvas()) {
+ stx += static_cast<RenderCanvas*>(renderer())->view()->contentsX();
+ sty += static_cast<RenderCanvas*>(renderer())->view()->contentsY();
+ }
+
+ QRect damageRect(stx,sty, width(), height());
+ RenderLayer* insideLayer = nodeAtPointForLayer(this, info, x, y, damageRect);
+
+ // Now determine if the result is inside an anchor.
+ DOM::NodeImpl* node = info.innerNode();
+ while (node) {
+ if (node->hasAnchor() && !info.URLElement())
+ info.setURLElement(node);
+ node = node->parentNode();
+ }
+
+ // Next set up the correct :hover/:active state along the new chain.
+ updateHoverActiveState(info);
+
+ // Now return whether we were inside this layer (this will always be true for the root
+ // layer).
+ return insideLayer;
+}
+
+RenderLayer* RenderLayer::nodeAtPointForLayer(RenderLayer* rootLayer, RenderObject::NodeInfo& info,
+ int xMousePos, int yMousePos, const QRect& hitTestRect)
+{
+ // Calculate the clip rects we should use.
+ QRect layerBounds, bgRect, fgRect;
+ calculateRects(rootLayer, hitTestRect, layerBounds, bgRect, fgRect);
+
+ // Ensure our lists are up-to-date.
+ updateZOrderLists();
+ updateOverflowList();
+
+ // This variable tracks which layer the mouse ends up being inside. The minute we find an insideLayer,
+ // we are done and can return it.
+ RenderLayer* insideLayer = 0;
+
+ // Begin by walking our list of positive layers from highest z-index down to the lowest
+ // z-index.
+ if (m_posZOrderList) {
+ uint count = m_posZOrderList->count();
+ for (int i = count-1; i >= 0; i--) {
+ RenderLayer* child = m_posZOrderList->at(i);
+ insideLayer = child->nodeAtPointForLayer(rootLayer, info, xMousePos, yMousePos, hitTestRect);
+ if (insideLayer)
+ return insideLayer;
+ }
+ }
+
+ // Now check our overflow objects.
+ if (m_overflowList) {
+ QValueList<RenderLayer*>::iterator it = m_overflowList->end();
+ for (--it; it != m_overflowList->end(); --it) {
+ insideLayer = (*it)->nodeAtPointForLayer(rootLayer, info, xMousePos, yMousePos, hitTestRect);
+ if (insideLayer)
+ return insideLayer;
+ }
+ }
+
+ // Next we want to see if the mouse pos is inside the child RenderObjects of the layer.
+ if (containsPoint(xMousePos, yMousePos, fgRect) &&
+ renderer()->nodeAtPoint(info, xMousePos, yMousePos,
+ layerBounds.x() - renderer()->xPos(),
+ layerBounds.y() - renderer()->yPos() + m_object->borderTopExtra(),
+ HitTestChildrenOnly)) {
+ if (info.innerNode() != m_object->element())
+ return this;
+ }
+
+ // Now check our negative z-index children.
+ if (m_negZOrderList) {
+ uint count = m_negZOrderList->count();
+ for (int i = count-1; i >= 0; i--) {
+ RenderLayer* child = m_negZOrderList->at(i);
+ insideLayer = child->nodeAtPointForLayer(rootLayer, info, xMousePos, yMousePos, hitTestRect);
+ if (insideLayer)
+ return insideLayer;
+ }
+ }
+
+ // Next we want to see if the mouse pos is inside this layer but not any of its children.
+ if (containsPoint(xMousePos, yMousePos, bgRect) &&
+ renderer()->nodeAtPoint(info, xMousePos, yMousePos,
+ layerBounds.x() - renderer()->xPos(),
+ layerBounds.y() - renderer()->yPos() + m_object->borderTopExtra(),
+ HitTestSelfOnly))
+ return this;
+
+ // No luck.
+ return 0;
+}
+
+void RenderLayer::calculateClipRects(const RenderLayer* rootLayer, QRect& overflowClipRect,
+ QRect& posClipRect, QRect& fixedClipRect)
+{
+ if (parent())
+ parent()->calculateClipRects(rootLayer, overflowClipRect, posClipRect, fixedClipRect);
+
+ switch (m_object->style()->position()) {
+ // A fixed object is essentially the root of its containing block hierarchy, so when
+ // we encounter such an object, we reset our clip rects to the fixedClipRect.
+ case FIXED:
+ posClipRect = fixedClipRect;
+ overflowClipRect = fixedClipRect;
+ break;
+ case ABSOLUTE:
+ overflowClipRect = posClipRect;
+ break;
+ case RELATIVE:
+ posClipRect = overflowClipRect;
+ break;
+ default:
+ break;
+ }
+
+ // Update the clip rects that will be passed to child layers.
+ if (m_object->hasOverflowClip() || m_object->hasClip()) {
+ // This layer establishes a clip of some kind.
+ int x = 0;
+ int y = 0;
+ convertToLayerCoords(rootLayer, x, y);
+
+ if (m_object->hasOverflowClip()) {
+ QRect newOverflowClip = m_object->getOverflowClipRect(x,y);
+ overflowClipRect = newOverflowClip.intersect(overflowClipRect);
+ if (m_object->isPositioned() || m_object->isRelPositioned())
+ posClipRect = newOverflowClip.intersect(posClipRect);
+ }
+ if (m_object->hasClip()) {
+ QRect newPosClip = m_object->getClipRect(x,y);
+ posClipRect = posClipRect.intersect(newPosClip);
+ overflowClipRect = overflowClipRect.intersect(newPosClip);
+ fixedClipRect = fixedClipRect.intersect(newPosClip);
+ }
+ }
+}
+
+void RenderLayer::calculateRects(const RenderLayer* rootLayer, const QRect& paintDirtyRect, QRect& layerBounds,
+ QRect& backgroundRect, QRect& foregroundRect)
+{
+ QRect overflowClipRect = paintDirtyRect;
+ QRect posClipRect = paintDirtyRect;
+ QRect fixedClipRect = paintDirtyRect;
+ if (parent())
+ parent()->calculateClipRects(rootLayer, overflowClipRect, posClipRect, fixedClipRect);
+
+ int x = 0;
+ int y = 0;
+ convertToLayerCoords(rootLayer, x, y);
+ layerBounds = QRect(x,y,width(),height());
+
+ backgroundRect = m_object->style()->position() == FIXED ? fixedClipRect :
+ (m_object->isPositioned() ? posClipRect : overflowClipRect);
+ foregroundRect = backgroundRect;
+
+ // Update the clip rects that will be passed to child layers.
+ if (m_object->hasOverflowClip() || m_object->hasClip()) {
+ // This layer establishes a clip of some kind.
+ if (m_object->hasOverflowClip())
+ foregroundRect = foregroundRect.intersect(m_object->getOverflowClipRect(x,y));
+
+ if (m_object->hasClip()) {
+ // Clip applies to *us* as well, so go ahead and update the damageRect.
+ QRect newPosClip = m_object->getClipRect(x,y);
+ backgroundRect = backgroundRect.intersect(newPosClip);
+ foregroundRect = foregroundRect.intersect(newPosClip);
+ }
+
+ // If we establish a clip at all, then go ahead and make sure our background
+ // rect is intersected with our layer's bounds.
+ backgroundRect = backgroundRect.intersect(layerBounds);
+ }
+}
+
+bool RenderLayer::intersectsDamageRect(const QRect& layerBounds, const QRect& damageRect) const
+{
+ return (renderer()->isCanvas() || renderer()->isRoot() || renderer()->isBody() ||
+ (renderer()->hasOverhangingFloats() && !renderer()->hasOverflowClip()) ||
+ (renderer()->isInline() && !renderer()->isReplaced()) ||
+ layerBounds.intersects(damageRect));
+}
+
+bool RenderLayer::containsPoint(int x, int y, const QRect& damageRect) const
+{
+ return (renderer()->isCanvas() || renderer()->isRoot() || renderer()->isBody() ||
+ renderer()->hasOverhangingFloats() ||
+ (renderer()->isInline() && !renderer()->isReplaced()) ||
+ damageRect.contains(x, y));
+}
+
+// This code has been written to anticipate the addition of CSS3-::outside and ::inside generated
+// content (and perhaps XBL). That's why it uses the render tree and not the DOM tree.
+static RenderObject* hoverAncestor(RenderObject* obj)
+{
+ return (!obj->isInline() && obj->continuation()) ? obj->continuation() : obj->parent();
+}
+
+static RenderObject* commonAncestor(RenderObject* obj1, RenderObject* obj2)
+{
+ if (!obj1 || !obj2)
+ return 0;
+
+ for (RenderObject* currObj1 = obj1; currObj1; currObj1 = hoverAncestor(currObj1))
+ for (RenderObject* currObj2 = obj2; currObj2; currObj2 = hoverAncestor(currObj2))
+ if (currObj1 == currObj2)
+ return currObj1;
+
+ return 0;
+}
+
+
+void RenderLayer::updateHoverActiveState(RenderObject::NodeInfo& info)
+{
+ // We don't update :hover/:active state when the info is marked as readonly.
+ if (info.readonly())
+ return;
+
+ DOM::NodeImpl *e = m_object->element();
+ DOM::DocumentImpl *doc = e ? e->getDocument() : 0;
+ if (!doc) return;
+
+ // Check to see if the hovered node has changed. If not, then we don't need to
+ // do anything.
+ DOM::NodeImpl* oldHoverNode = doc->hoverNode();
+ DOM::NodeImpl* newHoverNode = info.innerNode();
+
+ if (oldHoverNode == newHoverNode && (!oldHoverNode || oldHoverNode->active() == info.active()))
+ return;
+
+ // Update our current hover node.
+ doc->setHoverNode(newHoverNode);
+ if (info.active())
+ doc->setActiveNode(newHoverNode);
+ else
+ doc->setActiveNode(0);
+
+ // We have two different objects. Fetch their renderers.
+ RenderObject* oldHoverObj = oldHoverNode ? oldHoverNode->renderer() : 0;
+ RenderObject* newHoverObj = newHoverNode ? newHoverNode->renderer() : 0;
+
+ // Locate the common ancestor render object for the two renderers.
+ RenderObject* ancestor = commonAncestor(oldHoverObj, newHoverObj);
+
+ // The old hover path only needs to be cleared up to (and not including) the common ancestor;
+ for (RenderObject* curr = oldHoverObj; curr && curr != ancestor; curr = hoverAncestor(curr)) {
+ curr->setMouseInside(false);
+ if (curr->element()) {
+ curr->element()->setActive(false);
+ curr->element()->setHovered(false);
+ }
+ }
+
+ // Now set the hover state for our new object up to the root.
+ for (RenderObject* curr = newHoverObj; curr; curr = hoverAncestor(curr)) {
+ curr->setMouseInside(true);
+ if (curr->element()) {
+ curr->element()->setActive(info.active());
+ curr->element()->setHovered(true);
+ }
+ }
+}
+
+// Sort the buffer from lowest z-index to highest. The common scenario will have
+// most z-indices equal, so we optimize for that case (i.e., the list will be mostly
+// sorted already).
+static void sortByZOrder(QPtrVector<RenderLayer>* buffer,
+ QPtrVector<RenderLayer>* mergeBuffer,
+ uint start, uint end)
+{
+ if (start >= end)
+ return; // Sanity check.
+
+ if (end - start <= 6) {
+ // Apply a bubble sort for smaller lists.
+ for (uint i = end-1; i > start; i--) {
+ bool sorted = true;
+ for (uint j = start; j < i; j++) {
+ RenderLayer* elt = buffer->at(j);
+ RenderLayer* elt2 = buffer->at(j+1);
+ if (elt->zIndex() > elt2->zIndex()) {
+ sorted = false;
+ buffer->insert(j, elt2);
+ buffer->insert(j+1, elt);
+ }
+ }
+ if (sorted)
+ return;
+ }
+ }
+ else {
+ // Peform a merge sort for larger lists.
+ uint mid = (start+end)/2;
+ sortByZOrder(buffer, mergeBuffer, start, mid);
+ sortByZOrder(buffer, mergeBuffer, mid, end);
+
+ RenderLayer* elt = buffer->at(mid-1);
+ RenderLayer* elt2 = buffer->at(mid);
+
+ // Handle the fast common case (of equal z-indices). The list may already
+ // be completely sorted.
+ if (elt->zIndex() <= elt2->zIndex())
+ return;
+
+ // We have to merge sort. Ensure our merge buffer is big enough to hold
+ // all the items.
+ mergeBuffer->resize(end - start);
+ uint i1 = start;
+ uint i2 = mid;
+
+ elt = buffer->at(i1);
+ elt2 = buffer->at(i2);
+
+ while (i1 < mid || i2 < end) {
+ if (i1 < mid && (i2 == end || elt->zIndex() <= elt2->zIndex())) {
+ mergeBuffer->insert(mergeBuffer->count(), elt);
+ i1++;
+ if (i1 < mid)
+ elt = buffer->at(i1);
+ }
+ else {
+ mergeBuffer->insert(mergeBuffer->count(), elt2);
+ i2++;
+ if (i2 < end)
+ elt2 = buffer->at(i2);
+ }
+ }
+
+ for (uint i = start; i < end; i++)
+ buffer->insert(i, mergeBuffer->at(i-start));
+
+ mergeBuffer->clear();
+ }
+}
+
+void RenderLayer::dirtyZOrderLists()
+{
+ if (m_posZOrderList)
+ m_posZOrderList->clear();
+ if (m_negZOrderList)
+ m_negZOrderList->clear();
+ m_zOrderListsDirty = true;
+}
+
+void RenderLayer::dirtyOverflowList()
+{
+ if (m_overflowList)
+ m_overflowList->clear();
+ m_overflowListDirty = true;
+}
+
+void RenderLayer::updateZOrderLists()
+{
+ if (!isStackingContext() || !m_zOrderListsDirty)
+ return;
+
+ for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
+ child->collectLayers(m_posZOrderList, m_negZOrderList);
+
+ // Sort the two lists.
+ if (m_posZOrderList) {
+ QPtrVector<RenderLayer> mergeBuffer;
+ sortByZOrder(m_posZOrderList, &mergeBuffer, 0, m_posZOrderList->count());
+ }
+ if (m_negZOrderList) {
+ QPtrVector<RenderLayer> mergeBuffer;
+ sortByZOrder(m_negZOrderList, &mergeBuffer, 0, m_negZOrderList->count());
+ }
+
+ m_zOrderListsDirty = false;
+}
+
+void RenderLayer::updateOverflowList()
+{
+ if (!m_overflowListDirty)
+ return;
+
+ for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) {
+ if (child->isOverflowOnly()) {
+ if (!m_overflowList)
+ m_overflowList = new QValueList<RenderLayer*>;
+ m_overflowList->append(child);
+ }
+ }
+
+ m_overflowListDirty = false;
+}
+
+void RenderLayer::collectLayers(QPtrVector<RenderLayer>*& posBuffer, QPtrVector<RenderLayer>*& negBuffer)
+{
+ // FIXME: A child render object or layer could override visibility. Don't remove this
+ // optimization though until RenderObject's nodeAtPoint is patched to understand what to do
+ // when visibility is overridden by a child.
+ if (renderer()->style()->visibility() != VISIBLE)
+ return;
+
+ // Overflow layers are just painted by their enclosing layers, so they don't get put in zorder lists.
+ if (!isOverflowOnly()) {
+
+ // Determine which buffer the child should be in.
+ QPtrVector<RenderLayer>*& buffer = (zIndex() >= 0) ? posBuffer : negBuffer;
+
+ // Create the buffer if it doesn't exist yet.
+ if (!buffer)
+ buffer = new QPtrVector<RenderLayer>();
+
+ // Resize by a power of 2 when our buffer fills up.
+ if (buffer->count() == buffer->size())
+ buffer->resize(2*(buffer->size()+1));
+
+ // Append ourselves at the end of the appropriate buffer.
+ buffer->insert(buffer->count(), this);
+ }
+
+ // Recur into our children to collect more layers, but only if we don't establish
+ // a stacking context.
+ if (!isStackingContext()) {
+ for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
+ child->collectLayers(posBuffer, negBuffer);
+ }
+}
+
+#ifdef ENABLE_DUMP
+#ifndef KDE_USE_FINAL
+static QTextStream &operator<<(QTextStream &ts, const QRect &r)
+{
+ return ts << "at (" << r.x() << "," << r.y() << ") size " << r.width() << "x" << r.height();
+}
+#endif
+
+static void write(QTextStream &ts, RenderObject& o, const QString& indent )
+{
+ o.dump(ts, indent);
+
+ for (RenderObject *child = o.firstChild(); child; child = child->nextSibling()) {
+ if (child->layer()) continue;
+ write( ts, *child, indent + " " );
+ }
+}
+
+static void write(QTextStream &ts, const RenderLayer &l,
+ const QRect& layerBounds, const QRect& backgroundClipRect, const QRect& clipRect,
+ int layerType = 0, const QString& indent = QString::null)
+
+{
+ ts << indent << "layer";
+
+ ts << " at (" << l.xPos() << "," << l.yPos() << ") size " << l.width() << "x" << l.height();
+
+ if (layerBounds != layerBounds.intersect(backgroundClipRect)) {
+ ts << " backgroundClip " << backgroundClipRect;
+ }
+ if (layerBounds != layerBounds.intersect(clipRect)) {
+ ts << " clip " << clipRect;
+ }
+
+ if (layerType == -1)
+ ts << " layerType: background only";
+ else if (layerType == 1)
+ ts << " layerType: foreground only";
+
+ ts << "\n";
+
+ if (layerType != -1)
+ write( ts, *l.renderer(), indent + " " );
+
+ ts << "\n";
+}
+
+static void writeLayers(QTextStream &ts, const RenderLayer* rootLayer, RenderLayer* l,
+ const QRect& paintDirtyRect, const QString& indent)
+{
+ // Calculate the clip rects we should use.
+ QRect layerBounds, damageRect, clipRectToApply;
+ l->calculateRects(rootLayer, paintDirtyRect, layerBounds, damageRect, clipRectToApply);
+
+ // Ensure our lists are up-to-date.
+ l->updateZOrderLists();
+ l->updateOverflowList();
+
+ bool shouldPaint = l->intersectsDamageRect(layerBounds, damageRect);
+ QPtrVector<RenderLayer>* negList = l->negZOrderList();
+ QValueList<RenderLayer*>* ovfList = l->overflowList();
+ if (shouldPaint && negList && negList->count() > 0)
+ write(ts, *l, layerBounds, damageRect, clipRectToApply, -1, indent);
+
+ if (negList) {
+ for (unsigned i = 0; i != negList->count(); ++i)
+ writeLayers(ts, rootLayer, negList->at(i), paintDirtyRect, indent );
+ }
+
+ if (shouldPaint)
+ write(ts, *l, layerBounds, damageRect, clipRectToApply, negList && negList->count() > 0, indent);
+
+ if (ovfList) {
+ for (QValueList<RenderLayer*>::iterator it = ovfList->begin(); it != ovfList->end(); ++it)
+ writeLayers(ts, rootLayer, *it, paintDirtyRect, indent);
+ }
+
+ QPtrVector<RenderLayer>* posList = l->posZOrderList();
+ if (posList) {
+ for (unsigned i = 0; i != posList->count(); ++i)
+ writeLayers(ts, rootLayer, posList->at(i), paintDirtyRect, indent);
+ }
+}
+
+
+void RenderLayer::dump(QTextStream &ts, const QString &ind)
+{
+ assert( renderer()->isCanvas() );
+
+ writeLayers(ts, this, this, QRect(xPos(), yPos(), width(), height()), ind);
+}
+
+
+#endif
+
+bool RenderLayer::shouldBeOverflowOnly() const
+{
+ return renderer()->style() && renderer()->hasOverflowClip() &&
+ !renderer()->isPositioned() && !renderer()->isRelPositioned();
+ /* && !isTransparent(); */
+}
+
+void RenderLayer::styleChanged()
+{
+ bool isOverflowOnly = shouldBeOverflowOnly();
+ if (isOverflowOnly != m_isOverflowOnly) {
+ m_isOverflowOnly = isOverflowOnly;
+ RenderLayer* p = parent();
+ RenderLayer* sc = stackingContext();
+ if (p)
+ p->dirtyOverflowList();
+ if (sc)
+ sc->dirtyZOrderLists();
+ }
+
+ if (m_object->hasOverflowClip() &&
+ m_object->style()->overflowX() == OMARQUEE && m_object->style()->marqueeBehavior() != MNONE) {
+ if (!m_marquee)
+ m_marquee = new Marquee(this);
+ m_marquee->updateMarqueeStyle();
+ }
+ else if (m_marquee) {
+ delete m_marquee;
+ m_marquee = 0;
+ }
+}
+
+void RenderLayer::suspendMarquees()
+{
+ if (m_marquee)
+ m_marquee->suspend();
+
+ for (RenderLayer* curr = firstChild(); curr; curr = curr->nextSibling())
+ curr->suspendMarquees();
+}
+
+// --------------------------------------------------------------------------
+// Marquee implementation
+
+Marquee::Marquee(RenderLayer* l)
+:m_layer(l), m_currentLoop(0), m_totalLoops(0), m_timerId(0), m_start(0), m_end(0), m_speed(0), m_unfurlPos(0), m_reset(false),
+ m_suspended(false), m_stopped(false), m_whiteSpace(NORMAL), m_direction(MAUTO)
+{
+}
+
+int Marquee::marqueeSpeed() const
+{
+ int result = m_layer->renderer()->style()->marqueeSpeed();
+ DOM::NodeImpl* elt = m_layer->renderer()->element();
+ if (elt && elt->id() == ID_MARQUEE) {
+ HTMLMarqueeElementImpl* marqueeElt = static_cast<HTMLMarqueeElementImpl*>(elt);
+ result = kMax(result, marqueeElt->minimumDelay());
+ }
+ return result;
+}
+
+EMarqueeDirection Marquee::direction() const
+{
+ // FIXME: Support the CSS3 "auto" value for determining the direction of the marquee.
+ // For now just map MAUTO to MBACKWARD
+ EMarqueeDirection result = m_layer->renderer()->style()->marqueeDirection();
+ EDirection dir = m_layer->renderer()->style()->direction();
+ if (result == MAUTO)
+ result = MBACKWARD;
+ if (result == MFORWARD)
+ result = (dir == LTR) ? MRIGHT : MLEFT;
+ if (result == MBACKWARD)
+ result = (dir == LTR) ? MLEFT : MRIGHT;
+
+ // Now we have the real direction. Next we check to see if the increment is negative.
+ // If so, then we reverse the direction.
+ Length increment = m_layer->renderer()->style()->marqueeIncrement();
+ if (increment.value() < 0)
+ result = static_cast<EMarqueeDirection>(-result);
+
+ return result;
+}
+
+bool Marquee::isHorizontal() const
+{
+ return direction() == MLEFT || direction() == MRIGHT;
+}
+
+bool Marquee::isUnfurlMarquee() const
+{
+ EMarqueeBehavior behavior = m_layer->renderer()->style()->marqueeBehavior();
+ return (behavior == MUNFURL);
+}
+
+int Marquee::computePosition(EMarqueeDirection dir, bool stopAtContentEdge)
+{
+ RenderObject* o = m_layer->renderer();
+ RenderStyle* s = o->style();
+ if (isHorizontal()) {
+ bool ltr = s->direction() == LTR;
+ int clientWidth = o->clientWidth();
+ int contentWidth = ltr ? o->rightmostPosition(true, false) : o->leftmostPosition(true, false);
+ if (ltr)
+ contentWidth += (o->paddingRight() - o->borderLeft());
+ else {
+ contentWidth = o->width() - contentWidth;
+ contentWidth += (o->paddingLeft() - o->borderRight());
+ }
+ if (dir == MRIGHT) {
+ if (stopAtContentEdge)
+ return kMax(0, ltr ? (contentWidth - clientWidth) : (clientWidth - contentWidth));
+ else
+ return ltr ? contentWidth : clientWidth;
+ }
+ else {
+ if (stopAtContentEdge)
+ return kMin(0, ltr ? (contentWidth - clientWidth) : (clientWidth - contentWidth));
+ else
+ return ltr ? -clientWidth : -contentWidth;
+ }
+ }
+ else {
+ int contentHeight = m_layer->renderer()->lowestPosition(true, false) -
+ m_layer->renderer()->borderTop() + m_layer->renderer()->paddingBottom();
+ int clientHeight = m_layer->renderer()->clientHeight();
+ if (dir == MUP) {
+ if (stopAtContentEdge)
+ return kMin(contentHeight - clientHeight, 0);
+ else
+ return -clientHeight;
+ }
+ else {
+ if (stopAtContentEdge)
+ return kMax(contentHeight - clientHeight, 0);
+ else
+ return contentHeight;
+ }
+ }
+}
+
+void Marquee::start()
+{
+ if (m_timerId || m_layer->renderer()->style()->marqueeIncrement().value() == 0)
+ return;
+
+ if (!m_suspended && !m_stopped) {
+ if (isUnfurlMarquee()) {
+ bool forward = direction() == MDOWN || direction() == MRIGHT;
+ bool isReversed = (forward && m_currentLoop % 2) || (!forward && !(m_currentLoop % 2));
+ m_unfurlPos = isReversed ? m_end : m_start;
+ m_layer->renderer()->setChildNeedsLayout(true);
+ }
+ else {
+ if (isHorizontal())
+ m_layer->scrollToOffset(m_start, 0, false, false);
+ else
+ m_layer->scrollToOffset(0, m_start, false, false);
+ }
+ }
+ else
+ m_suspended = false;
+
+ m_stopped = false;
+ m_timerId = startTimer(speed());
+}
+
+void Marquee::suspend()
+{
+ if (m_timerId) {
+ killTimer(m_timerId);
+ m_timerId = 0;
+ }
+
+ m_suspended = true;
+}
+
+void Marquee::stop()
+{
+ if (m_timerId) {
+ killTimer(m_timerId);
+ m_timerId = 0;
+ }
+
+ m_stopped = true;
+}
+
+void Marquee::updateMarqueePosition()
+{
+ bool activate = (m_totalLoops <= 0 || m_currentLoop < m_totalLoops);
+ if (activate) {
+ if (isUnfurlMarquee()) {
+ if (m_unfurlPos < m_start) {
+ m_unfurlPos = m_start;
+ m_layer->renderer()->setChildNeedsLayout(true);
+ }
+ else if (m_unfurlPos > m_end) {
+ m_unfurlPos = m_end;
+ m_layer->renderer()->setChildNeedsLayout(true);
+ }
+ }
+ else {
+ EMarqueeBehavior behavior = m_layer->renderer()->style()->marqueeBehavior();
+ m_start = computePosition(direction(), behavior == MALTERNATE);
+ m_end = computePosition(reverseDirection(), behavior == MALTERNATE || behavior == MSLIDE);
+ }
+ if (!m_stopped) start();
+ }
+}
+
+void Marquee::updateMarqueeStyle()
+{
+ RenderStyle* s = m_layer->renderer()->style();
+
+ if (m_direction != s->marqueeDirection() || (m_totalLoops != s->marqueeLoopCount() && m_currentLoop >= m_totalLoops))
+ m_currentLoop = 0; // When direction changes or our loopCount is a smaller number than our current loop, reset our loop.
+
+ m_totalLoops = s->marqueeLoopCount();
+ m_direction = s->marqueeDirection();
+ m_whiteSpace = s->whiteSpace();
+
+ if (m_layer->renderer()->isHTMLMarquee()) {
+ // Hack for WinIE. In WinIE, a value of 0 or lower for the loop count for SLIDE means to only do
+ // one loop.
+ if (m_totalLoops <= 0 && (s->marqueeBehavior() == MSLIDE || s->marqueeBehavior() == MUNFURL))
+ m_totalLoops = 1;
+
+ // Hack alert: Set the white-space value to nowrap for horizontal marquees with inline children, thus ensuring
+ // all the text ends up on one line by default. Limit this hack to the <marquee> element to emulate
+ // WinIE's behavior. Someone using CSS3 can use white-space: nowrap on their own to get this effect.
+ // Second hack alert: Set the text-align back to auto. WinIE completely ignores text-align on the
+ // marquee element.
+ // FIXME: Bring these up with the CSS WG.
+ if (isHorizontal() && m_layer->renderer()->childrenInline()) {
+ s->setWhiteSpace(NOWRAP);
+ s->setTextAlign(TAAUTO);
+ }
+ }
+
+ if (speed() != marqueeSpeed()) {
+ m_speed = marqueeSpeed();
+ if (m_timerId) {
+ killTimer(m_timerId);
+ m_timerId = startTimer(speed());
+ }
+ }
+
+ // Check the loop count to see if we should now stop.
+ bool activate = (m_totalLoops <= 0 || m_currentLoop < m_totalLoops);
+ if (activate && !m_timerId)
+ m_layer->renderer()->setNeedsLayout(true);
+ else if (!activate && m_timerId) {
+ // Destroy the timer.
+ killTimer(m_timerId);
+ m_timerId = 0;
+ }
+}
+
+void Marquee::timerEvent(QTimerEvent* /*evt*/)
+{
+ if (m_layer->renderer()->needsLayout())
+ return;
+
+ if (m_reset) {
+ m_reset = false;
+ if (isHorizontal())
+ m_layer->scrollToXOffset(m_start);
+ else
+ m_layer->scrollToYOffset(m_start);
+ return;
+ }
+
+ RenderStyle* s = m_layer->renderer()->style();
+
+ int endPoint = m_end;
+ int range = m_end - m_start;
+ int newPos;
+ if (range == 0)
+ newPos = m_end;
+ else {
+ bool addIncrement = direction() == MUP || direction() == MLEFT;
+ bool isReversed = s->marqueeBehavior() == MALTERNATE && m_currentLoop % 2;
+ if (isUnfurlMarquee()) {
+ isReversed = (!addIncrement && m_currentLoop % 2) || (addIncrement && !(m_currentLoop % 2));
+ addIncrement = !isReversed;
+ }
+ if (isReversed) {
+ // We're going in the reverse direction.
+ endPoint = m_start;
+ range = -range;
+ if (!isUnfurlMarquee())
+ addIncrement = !addIncrement;
+ }
+ bool positive = range > 0;
+ int clientSize = isUnfurlMarquee() ? abs(range) :
+ (isHorizontal() ? m_layer->renderer()->clientWidth() : m_layer->renderer()->clientHeight());
+ int increment = kMax(1, abs(m_layer->renderer()->style()->marqueeIncrement().width(clientSize)));
+ int currentPos = isUnfurlMarquee() ? m_unfurlPos :
+ (isHorizontal() ? m_layer->scrollXOffset() : m_layer->scrollYOffset());
+ newPos = currentPos + (addIncrement ? increment : -increment);
+ if (positive)
+ newPos = kMin(newPos, endPoint);
+ else
+ newPos = kMax(newPos, endPoint);
+ }
+
+ if (newPos == endPoint) {
+ m_currentLoop++;
+ if (m_totalLoops > 0 && m_currentLoop >= m_totalLoops) {
+ killTimer(m_timerId);
+ m_timerId = 0;
+ }
+ else if (s->marqueeBehavior() != MALTERNATE && s->marqueeBehavior() != MUNFURL)
+ m_reset = true;
+ }
+
+ if (isUnfurlMarquee()) {
+ m_unfurlPos = newPos;
+ m_layer->renderer()->setChildNeedsLayout(true);
+ }
+ else {
+ if (isHorizontal())
+ m_layer->scrollToXOffset(newPos);
+ else
+ m_layer->scrollToYOffset(newPos);
+ }
+}
+
+#include "render_layer.moc"
diff --git a/khtml/rendering/render_layer.h b/khtml/rendering/render_layer.h
new file mode 100644
index 000000000..c7587ae39
--- /dev/null
+++ b/khtml/rendering/render_layer.h
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2003 Apple Computer, Inc.
+ *
+ * Portions are Copyright (C) 1998 Netscape Communications Corporation.
+ *
+ * Other contributors:
+ * Robert O'Callahan <roc+@cs.cmu.edu>
+ * David Baron <dbaron@fas.harvard.edu>
+ * Christian Biesinger <cbiesinger@web.de>
+ * Randall Jesup <rjesup@wgate.com>
+ * Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
+ * Josh Soref <timeless@mac.com>
+ * Boris Zbarsky <bzbarsky@mit.edu>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Alternatively, the contents of this file may be used under the terms
+ * of either the Mozilla Public License Version 1.1, found at
+ * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public
+ * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html
+ * (the "GPL"), in which case the provisions of the MPL or the GPL are
+ * applicable instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of one of those two
+ * licenses (the MPL or the GPL) and not to allow others to use your
+ * version of this file under the LGPL, indicate your decision by
+ * deletingthe provisions above and replace them with the notice and
+ * other provisions required by the MPL or the GPL, as the case may be.
+ * If you do not delete the provisions above, a recipient may use your
+ * version of this file under any of the LGPL, the MPL or the GPL.
+ */
+
+#ifndef render_layer_h
+#define render_layer_h
+
+#include <qcolor.h>
+#include <qrect.h>
+#include <assert.h>
+
+#include "render_object.h"
+
+class QScrollBar;
+template <class T> class QPtrVector;
+
+namespace khtml {
+ class RenderStyle;
+ class RenderTable;
+ class CachedObject;
+ class RenderCanvas;
+ class RenderText;
+ class RenderFrameSet;
+ class RenderObject;
+ class RenderScrollMediator;
+
+class RenderScrollMediator: public QObject
+{
+ Q_OBJECT
+public:
+ RenderScrollMediator(RenderLayer* layer)
+ :m_layer(layer) {}
+
+public slots:
+ void slotValueChanged();
+
+private:
+ RenderLayer* m_layer;
+};
+
+// This class handles the auto-scrolling of layers with overflow: marquee.
+class Marquee: public QObject
+{
+ Q_OBJECT
+
+public:
+ Marquee(RenderLayer* l);
+
+ void timerEvent(QTimerEvent*);
+
+ int speed() const { return m_speed; }
+ int marqueeSpeed() const;
+ EMarqueeDirection direction() const;
+ EMarqueeDirection reverseDirection() const { return static_cast<EMarqueeDirection>(-direction()); }
+ bool isHorizontal() const;
+ bool isUnfurlMarquee() const;
+ int unfurlPos() const { return m_unfurlPos; }
+
+ EWhiteSpace whiteSpace() { return m_whiteSpace; }
+
+ int computePosition(EMarqueeDirection dir, bool stopAtClientEdge);
+
+ void setEnd(int end) { m_end = end; }
+
+ void start();
+ void suspend();
+ void stop();
+
+ void updateMarqueeStyle();
+ void updateMarqueePosition();
+
+private:
+ RenderLayer* m_layer;
+ int m_currentLoop;
+ int m_totalLoops;
+ int m_timerId;
+ int m_start;
+ int m_end;
+ int m_speed;
+ int m_unfurlPos;
+ bool m_reset:1;
+ bool m_suspended:1;
+ bool m_stopped:1;
+ EWhiteSpace m_whiteSpace : 3;
+ EMarqueeDirection m_direction : 4;
+};
+
+class RenderLayer
+{
+public:
+#ifdef APPLE_CHANGES
+ static QScrollBar* gScrollBar;
+#endif
+
+ RenderLayer(RenderObject* object);
+ ~RenderLayer();
+
+ RenderObject* renderer() const { return m_object; }
+ RenderLayer *parent() const { return m_parent; }
+ RenderLayer *previousSibling() const { return m_previous; }
+ RenderLayer *nextSibling() const { return m_next; }
+
+ RenderLayer *firstChild() const { return m_first; }
+ RenderLayer *lastChild() const { return m_last; }
+
+ void addChild(RenderLayer *newChild, RenderLayer* beforeChild = 0);
+ RenderLayer* removeChild(RenderLayer *oldChild);
+
+ void removeOnlyThisLayer();
+ void insertOnlyThisLayer();
+
+ void styleChanged();
+
+ Marquee* marquee() const { return m_marquee; }
+ void suspendMarquees();
+
+ bool isOverflowOnly() const { return m_isOverflowOnly; }
+
+#ifdef APPLE_CHANGES
+ bool isTransparent();
+ RenderLayer* transparentAncestor();
+#endif
+
+ RenderLayer* root() {
+ RenderLayer* curr = this;
+ while (curr->parent()) curr = curr->parent();
+ return curr;
+ }
+
+ int xPos() const { return m_x; }
+ int yPos() const { return m_y; }
+
+ short width() const;
+ int height() const;
+
+ short scrollWidth() const { return m_scrollWidth; }
+ int scrollHeight() const { return m_scrollHeight; }
+
+ void resize( int w, int h ) {
+ m_scrollWidth = w; m_scrollHeight = h;
+ }
+
+ void setPos( int xPos, int yPos ) {
+ m_x = xPos;
+ m_y = yPos;
+ }
+
+ // Scrolling methods for layers that can scroll their overflow.
+ void scrollOffset(int& x, int& y);
+ void subtractScrollOffset(int& x, int& y);
+ void checkInlineRelOffset(const RenderObject* o, int& x, int& y);
+ short scrollXOffset() { return m_scrollX; }
+ int scrollYOffset() { return m_scrollY; }
+ void scrollToOffset(int x, int y, bool updateScrollbars = true, bool repaint = true);
+ void scrollToXOffset(int x) { scrollToOffset(x, m_scrollY); }
+ void scrollToYOffset(int y) { scrollToOffset(m_scrollX, y); }
+ void showScrollbar(Qt::Orientation, bool);
+ QScrollBar* horizontalScrollbar() { return m_hBar; }
+ QScrollBar* verticalScrollbar() { return m_vBar; }
+ int verticalScrollbarWidth();
+ int horizontalScrollbarHeight();
+ void positionScrollbars(const QRect &damageRect);
+ void paintScrollbars(RenderObject::PaintInfo& pI);
+ void checkScrollbarsAfterLayout();
+ void slotValueChanged(int);
+ void repaint(Priority p=NormalPriority, bool markForRepaint = false);
+ void updateScrollPositionFromScrollbars();
+
+ void updateLayerPosition();
+ void updateLayerPositions( RenderLayer* rootLayer, bool doFullRepaint = false, bool checkForRepaint = false);
+
+ // Get the enclosing stacking context for this layer. A stacking context is a layer
+ // that has a non-auto z-index.
+ RenderLayer* stackingContext() const;
+ bool isStackingContext() const { return !hasAutoZIndex() || renderer()->isCanvas(); }
+
+ void dirtyZOrderLists();
+ void updateZOrderLists();
+ QPtrVector<RenderLayer>* posZOrderList() const { return m_posZOrderList; }
+ QPtrVector<RenderLayer>* negZOrderList() const { return m_negZOrderList; }
+
+ void dirtyOverflowList();
+ void updateOverflowList();
+ QValueList<RenderLayer*>* overflowList() const { return m_overflowList; }
+
+ void setHasOverlaidWidgets(bool b=true) { m_hasOverlaidWidgets = b; }
+ bool hasOverlaidWidgets() const { return m_hasOverlaidWidgets; }
+ QRegion getMask() const { return m_region; }
+ QRegion paintedRegion(RenderLayer* rootLayer);
+ void updateWidgetMasks(RenderLayer* rootLayer);
+
+ // Gets the nearest enclosing positioned ancestor layer (also includes
+ // the <html> layer and the root layer).
+ RenderLayer* enclosingPositionedAncestor() const;
+
+ void convertToLayerCoords(const RenderLayer* ancestorLayer, int& x, int& y) const;
+
+ bool hasAutoZIndex() const { return renderer()->style()->hasAutoZIndex(); }
+ int zIndex() const { return renderer()->style()->zIndex(); }
+
+ // The two main functions that use the layer system. The paint method
+ // paints the layers that intersect the damage rect from back to
+ // front. The nodeAtPoint method looks for mouse events by walking
+ // layers that intersect the point from front to back.
+ KDE_EXPORT void paint(QPainter *p, const QRect& damageRect, bool selectionOnly=false);
+ bool nodeAtPoint(RenderObject::NodeInfo& info, int x, int y);
+
+ // This method figures out our layerBounds in coordinates relative to
+ // |rootLayer}. It also computes our background and foreground clip rects
+ // for painting/event handling.
+ void calculateRects(const RenderLayer* rootLayer, const QRect& paintDirtyRect, QRect& layerBounds,
+ QRect& backgroundRect, QRect& foregroundRect);
+ void calculateClipRects(const RenderLayer* rootLayer, QRect& overflowClipRect,
+ QRect& posClipRect, QRect& fixedClipRect);
+
+ bool intersectsDamageRect(const QRect& layerBounds, const QRect& damageRect) const;
+ bool containsPoint(int x, int y, const QRect& damageRect) const;
+
+ void updateHoverActiveState(RenderObject::NodeInfo& info);
+
+ void detach(RenderArena* renderArena);
+
+#ifdef ENABLE_DUMP
+ KDE_EXPORT void dump(QTextStream &stream, const QString &ind = QString::null);
+#endif
+
+ // Overloaded new operator. Derived classes must override operator new
+ // in order to allocate out of the RenderArena.
+ void* operator new(size_t sz, RenderArena* renderArena) throw();
+
+ // Overridden to prevent the normal delete from being called.
+ void operator delete(void* ptr, size_t sz);
+
+private:
+ // The normal operator new is disallowed on all render objects.
+ void* operator new(size_t sz) throw();
+
+private:
+ void setNextSibling(RenderLayer* next) { m_next = next; }
+ void setPreviousSibling(RenderLayer* prev) { m_previous = prev; }
+ void setParent(RenderLayer* parent) { m_parent = parent; }
+ void setFirstChild(RenderLayer* first) { m_first = first; }
+ void setLastChild(RenderLayer* last) { m_last = last; }
+
+ void collectLayers(QPtrVector<RenderLayer>*&, QPtrVector<RenderLayer>*&);
+
+ KDE_EXPORT void paintLayer(RenderLayer* rootLayer, QPainter *p, const QRect& paintDirtyRect, bool selectionOnly=false);
+ RenderLayer* nodeAtPointForLayer(RenderLayer* rootLayer, RenderObject::NodeInfo& info,
+ int x, int y, const QRect& hitTestRect);
+ bool shouldBeOverflowOnly() const;
+
+protected:
+ RenderObject* m_object;
+
+ RenderLayer* m_parent;
+ RenderLayer* m_previous;
+ RenderLayer* m_next;
+
+ RenderLayer* m_first;
+ RenderLayer* m_last;
+
+ // Our (x,y) coordinates are in our parent layer's coordinate space.
+ short m_x;
+ int m_y;
+
+ // Our scroll offsets if the view is scrolled.
+ short m_scrollX;
+ int m_scrollY;
+
+ // The width/height of our scrolled area.
+ short m_scrollWidth;
+ int m_scrollHeight;
+
+ // For layers with overflow, we have a pair of scrollbars.
+ QScrollBar* m_hBar;
+ QScrollBar* m_vBar;
+ RenderScrollMediator* m_scrollMediator;
+
+ // For layers that establish stacking contexts, m_posZOrderList holds a sorted list of all the
+ // descendant layers within the stacking context that have z-indices of 0 or greater
+ // (auto will count as 0). m_negZOrderList holds descendants within our stacking context with negative
+ // z-indices.
+ QPtrVector<RenderLayer>* m_posZOrderList;
+ QPtrVector<RenderLayer>* m_negZOrderList;
+
+ // This list contains our overflow child layers.
+ QValueList<RenderLayer*>* m_overflowList;
+
+ bool m_zOrderListsDirty: 1;
+ bool m_overflowListDirty: 1;
+ bool m_isOverflowOnly: 1;
+ bool m_markedForRepaint: 1;
+ bool m_hasOverlaidWidgets: 1;
+
+ QRect m_visibleRect;
+
+ QRegion m_region; // used by overlaid (non z-order aware) widgets
+
+ Marquee* m_marquee; // Used by layers with overflow:marquee
+};
+
+} // namespace
+#endif
diff --git a/khtml/rendering/render_line.cpp b/khtml/rendering/render_line.cpp
new file mode 100644
index 000000000..18a5f5101
--- /dev/null
+++ b/khtml/rendering/render_line.cpp
@@ -0,0 +1,996 @@
+/**
+* This file is part of the html renderer for KDE.
+ *
+ * Copyright (C) 2003-2006 Apple Computer, Inc.
+ * (C) 2006 Germain Garand (germain@ebooksfrance.org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+// -------------------------------------------------------------------------
+
+#include <kdebug.h>
+#include <assert.h>
+#include <qpainter.h>
+#include <kglobal.h>
+
+#include "rendering/render_flow.h"
+#include "rendering/render_text.h"
+#include "rendering/render_table.h"
+#include "rendering/render_inline.h"
+#include "rendering/render_block.h"
+#include "rendering/render_arena.h"
+#include "rendering/render_line.h"
+#include "xml/dom_nodeimpl.h"
+#include "xml/dom_docimpl.h"
+#include "html/html_formimpl.h"
+#include "misc/htmltags.h"
+#include "khtmlview.h"
+
+using namespace DOM;
+using namespace khtml;
+
+#ifndef NDEBUG
+static bool inInlineBoxDetach;
+#endif
+
+class khtml::EllipsisBox : public InlineBox
+{
+public:
+ EllipsisBox(RenderObject* obj, const DOM::DOMString& ellipsisStr, InlineFlowBox* p,
+ int w, int y, int h, int b, bool firstLine, InlineBox* markupBox)
+ :InlineBox(obj), m_str(ellipsisStr) {
+ m_parent = p;
+ m_width = w;
+ m_y = y;
+ m_height = h;
+ m_baseline = b;
+ m_firstLine = firstLine;
+ m_constructed = true;
+ m_markupBox = markupBox;
+ }
+
+ void paint(RenderObject::PaintInfo& i, int _tx, int _ty);
+ bool nodeAtPoint(RenderObject::NodeInfo& info, int _x, int _y, int _tx, int _ty);
+
+private:
+ DOM::DOMString m_str;
+ InlineBox* m_markupBox;
+};
+
+void InlineBox::detach(RenderArena* renderArena)
+{
+ if (m_parent)
+ m_parent->removeFromLine(this);
+#ifndef NDEBUG
+ inInlineBoxDetach = true;
+#endif
+ delete this;
+#ifndef NDEBUG
+ inInlineBoxDetach = false;
+#endif
+
+ // Recover the size left there for us by operator delete and free the memory.
+ renderArena->free(*(size_t *)this, this);
+}
+
+void* InlineBox::operator new(size_t sz, RenderArena* renderArena) throw()
+{
+ return renderArena->allocate(sz);
+}
+
+void InlineBox::operator delete(void* ptr, size_t sz)
+{
+ assert(inInlineBoxDetach);
+
+ // Stash size where detach can find it.
+ *(size_t *)ptr = sz;
+}
+
+static bool needsOutlinePhaseRepaint(RenderObject* o, RenderObject::PaintInfo& i, int tx, int ty) {
+ if (o->style()->outlineWidth() <= 0)
+ return false;
+ QRect r(tx+o->xPos(),ty+o->yPos(),o->width(),o->height());
+ if (r.intersects(i.r))
+ return false;
+ r.addCoords(-o->style()->outlineSize(),
+ -o->style()->outlineSize(),
+ o->style()->outlineSize(),
+ o->style()->outlineSize());
+ if (!r.intersects(i.r))
+ return false;
+ return true;
+}
+
+void InlineBox::paint(RenderObject::PaintInfo& i, int tx, int ty)
+{
+ if ( i.phase == PaintActionOutline && !needsOutlinePhaseRepaint(object(), i, tx, ty) )
+ return;
+
+ // Paint all phases of replaced elements atomically, as though the replaced element established its
+ // own stacking context. (See Appendix E.2, section 6.4 on inline block/table elements in the CSS2.1
+ // specification.)
+ bool paintSelectionOnly = i.phase == PaintActionSelection;
+ RenderObject::PaintInfo info(i.p, i.r, paintSelectionOnly ? i.phase : PaintActionElementBackground);
+ object()->paint(info, tx, ty);
+ if (!paintSelectionOnly) {
+ info.phase = PaintActionChildBackgrounds;
+ object()->paint(info, tx, ty);
+ info.phase = PaintActionFloat;
+ object()->paint(info, tx, ty);
+ info.phase = PaintActionForeground;
+ object()->paint(info, tx, ty);
+ info.phase = PaintActionOutline;
+ object()->paint(info, tx, ty);
+ }
+}
+
+bool InlineBox::nodeAtPoint(RenderObject::NodeInfo& i, int x, int y, int tx, int ty)
+{
+ // Hit test all phases of replaced elements atomically, as though the replaced element established its
+ // own stacking context. (See Appendix E.2, section 6.4 on inline block/table elements in the CSS2.1
+ // specification.)
+ bool inside = false;
+ return object()->nodeAtPoint(i, x, y, tx, ty, HitTestAll, inside); // ### port hitTest
+}
+
+RootInlineBox* InlineBox::root()
+{
+ if (m_parent)
+ return m_parent->root();
+ return static_cast<RootInlineBox*>(this);
+}
+
+InlineFlowBox::~InlineFlowBox()
+{
+ /* If we're destroyed, set the children free, and break their links */
+ while (m_firstChild)
+ removeFromLine(m_firstChild);
+}
+
+void InlineFlowBox::removeFromLine(InlineBox *child)
+{
+ if (child == m_firstChild) {
+ m_firstChild = child->nextOnLine();
+ }
+ if (child == m_lastChild) {
+ m_lastChild = child->prevOnLine();
+ }
+ if (child->nextOnLine()) {
+ child->nextOnLine()->m_prev = child->prevOnLine();
+ }
+ if (child->prevOnLine()) {
+ child->prevOnLine()->m_next = child->nextOnLine();
+ }
+
+ child->setParent(0);
+}
+
+bool InlineBox::canAccommodateEllipsisBox(bool ltr, int blockEdge, int ellipsisWidth)
+{
+ // Non-replaced elements can always accommodate an ellipsis.
+ if (!m_object || !m_object->isReplaced())
+ return true;
+
+ QRect boxRect(m_x, 0, m_width, 10);
+ QRect ellipsisRect(ltr ? blockEdge - ellipsisWidth : blockEdge, 0, ellipsisWidth, 10);
+ return !(boxRect.intersects(ellipsisRect));
+}
+
+int InlineBox::placeEllipsisBox(bool /*ltr*/, int /*blockEdge*/, int /*ellipsisWidth*/, bool&)
+{
+ // Use -1 to mean "we didn't set the position."
+ return -1;
+}
+
+int InlineFlowBox::marginLeft() const
+{
+ if (!includeLeftEdge())
+ return 0;
+
+ RenderStyle* cstyle = object()->style();
+ Length margin = cstyle->marginLeft();
+ if (!margin.isVariable())
+ return (margin.isFixed() ? margin.value() : object()->marginLeft());
+ return 0;
+}
+
+int InlineFlowBox::marginRight() const
+{
+ if (!includeRightEdge())
+ return 0;
+
+ RenderStyle* cstyle = object()->style();
+ Length margin = cstyle->marginRight();
+ if (!margin.isVariable())
+ return (margin.isFixed() ? margin.value() : object()->marginRight());
+ return 0;
+}
+
+int InlineFlowBox::marginBorderPaddingLeft() const
+{
+ return marginLeft() + borderLeft() + paddingLeft();
+}
+
+int InlineFlowBox::marginBorderPaddingRight() const
+{
+ return marginRight() + borderRight() + paddingRight();
+}
+
+int InlineFlowBox::getFlowSpacingWidth() const
+{
+ int totWidth = marginBorderPaddingLeft() + marginBorderPaddingRight();
+ for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) {
+ if (curr->isInlineFlowBox())
+ totWidth += static_cast<InlineFlowBox*>(curr)->getFlowSpacingWidth();
+ }
+ return totWidth;
+}
+
+bool InlineFlowBox::nextOnLineExists()
+{
+ if (!parent())
+ return false;
+
+ if (nextOnLine())
+ return true;
+
+ return parent()->nextOnLineExists();
+}
+
+bool InlineFlowBox::prevOnLineExists()
+{
+ if (!parent())
+ return false;
+
+ if (prevOnLine())
+ return true;
+
+ return parent()->prevOnLineExists();
+}
+
+bool InlineFlowBox::onEndChain(RenderObject* endObject)
+{
+ if (!endObject)
+ return false;
+
+ if (endObject == object())
+ return true;
+
+ RenderObject* curr = endObject;
+ RenderObject* parent = curr->parent();
+ while (parent && !parent->isRenderBlock() || parent == object()) {
+ if (parent->lastChild() != curr)
+ return false;
+
+ curr = parent;
+ parent = curr->parent();
+ }
+
+ return true;
+}
+
+void InlineFlowBox::determineSpacingForFlowBoxes(bool lastLine, RenderObject* endObject)
+{
+ // All boxes start off open. They will not apply any margins/border/padding on
+ // any side.
+ bool includeLeftEdge = false;
+ bool includeRightEdge = false;
+
+ RenderFlow* flow = static_cast<RenderFlow*>(object());
+
+ if (!flow->firstChild())
+ includeLeftEdge = includeRightEdge = true; // Empty inlines never split across lines.
+ else if (parent()) { // The root inline box never has borders/margins/padding.
+ bool ltr = flow->style()->direction() == LTR;
+
+ // Check to see if all initial lines are unconstructed. If so, then
+ // we know the inline began on this line.
+ if (!flow->firstLineBox()->isConstructed()) {
+ if (ltr && flow->firstLineBox() == this)
+ includeLeftEdge = true;
+ else if (!ltr && flow->lastLineBox() == this)
+ includeRightEdge = true;
+ }
+
+ // In order to determine if the inline ends on this line, we check three things:
+ // (1) If we are the last line and we don't have a continuation(), then we can
+ // close up.
+ // (2) If the last line box for the flow has an object following it on the line (ltr,
+ // reverse for rtl), then the inline has closed.
+ // (3) The line may end on the inline. If we are the last child (climbing up
+ // the end object's chain), then we just closed as well.
+ if (!flow->lastLineBox()->isConstructed()) {
+ if (ltr) {
+ if (!nextLineBox() &&
+ ((lastLine && !object()->continuation()) || nextOnLineExists()
+ || onEndChain(endObject)))
+ includeRightEdge = true;
+ }
+ else {
+ if ((!prevLineBox() || !prevLineBox()->isConstructed()) &&
+ ((lastLine && !object()->continuation()) ||
+ prevOnLineExists() || onEndChain(endObject)))
+ includeLeftEdge = true;
+ }
+
+ }
+ }
+
+ setEdges(includeLeftEdge, includeRightEdge);
+
+ // Recur into our children.
+ for (InlineBox* currChild = firstChild(); currChild; currChild = currChild->nextOnLine()) {
+ if (currChild->isInlineFlowBox()) {
+ InlineFlowBox* currFlow = static_cast<InlineFlowBox*>(currChild);
+ currFlow->determineSpacingForFlowBoxes(lastLine, endObject);
+ }
+ }
+}
+
+int InlineFlowBox::placeBoxesHorizontally(int x)
+{
+ // Set our x position.
+ setXPos(x);
+
+ int startX = x;
+ x += borderLeft() + paddingLeft();
+
+ for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) {
+ if (curr->object()->isText()) {
+ InlineTextBox* text = static_cast<InlineTextBox*>(curr);
+ text->setXPos(x);
+ x += curr->width();
+ }
+ else {
+ if (curr->object()->isPositioned()) {
+ if (curr->object()->parent()->style()->direction() == LTR)
+ curr->setXPos(x);
+ else {
+ // Our offset that we cache needs to be from the edge of the right border box and
+ // not the left border box. We have to subtract |x| from the width of the block
+ // (which can be obtained by walking up to the root line box).
+ InlineBox* root = this;
+ while (!root->isRootInlineBox())
+ root = root->parent();
+ curr->setXPos(root->object()->width()-x);
+ }
+ continue; // The positioned object has no effect on the width.
+ }
+ if (curr->object()->isInlineFlow()) {
+ InlineFlowBox* flow = static_cast<InlineFlowBox*>(curr);
+ x += flow->marginLeft();
+ x = flow->placeBoxesHorizontally(x);
+ x += flow->marginRight();
+ }
+ else {
+ x += curr->object()->marginLeft();
+ curr->setXPos(x);
+ x += curr->width() + curr->object()->marginRight();
+ }
+ }
+ }
+
+ x += borderRight() + paddingRight();
+ setWidth(x-startX);
+ return x;
+}
+
+void InlineFlowBox::verticallyAlignBoxes(int& heightOfBlock)
+{
+ int maxPositionTop = 0;
+ int maxPositionBottom = 0;
+ int maxAscent = 0;
+ int maxDescent = 0;
+
+ // Figure out if we're in strict mode.
+ RenderObject* curr = object();
+ while (curr && !curr->element())
+ curr = curr->container();
+ bool strictMode = (curr && curr->element()->getDocument()->inStrictMode());
+
+ computeLogicalBoxHeights(maxPositionTop, maxPositionBottom, maxAscent, maxDescent, strictMode);
+
+ if (maxAscent + maxDescent < kMax(maxPositionTop, maxPositionBottom))
+ adjustMaxAscentAndDescent(maxAscent, maxDescent, maxPositionTop, maxPositionBottom);
+
+ int maxHeight = maxAscent + maxDescent;
+ int topPosition = heightOfBlock;
+ int bottomPosition = heightOfBlock;
+ placeBoxesVertically(heightOfBlock, maxHeight, maxAscent, strictMode, topPosition, bottomPosition);
+
+ setOverflowPositions(topPosition, bottomPosition);
+
+ // Shrink boxes with no text children in quirks and almost strict mode.
+ if (!strictMode)
+ shrinkBoxesWithNoTextChildren(topPosition, bottomPosition);
+
+ heightOfBlock += maxHeight;
+}
+
+void InlineFlowBox::adjustMaxAscentAndDescent(int& maxAscent, int& maxDescent,
+ int maxPositionTop, int maxPositionBottom)
+{
+ for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) {
+ // The computed lineheight needs to be extended for the
+ // positioned elements
+ // see khtmltests/rendering/html_align.html
+
+ if (curr->object()->isPositioned())
+ continue; // Positioned placeholders don't affect calculations.
+ if (curr->yPos() == PositionTop || curr->yPos() == PositionBottom) {
+ if (curr->yPos() == PositionTop) {
+ if (maxAscent + maxDescent < curr->height())
+ maxDescent = curr->height() - maxAscent;
+ }
+ else {
+ if (maxAscent + maxDescent < curr->height())
+ maxAscent = curr->height() - maxDescent;
+ }
+
+ if ( maxAscent + maxDescent >= kMax( maxPositionTop, maxPositionBottom ) )
+ break;
+ }
+
+ if (curr->isInlineFlowBox())
+ static_cast<InlineFlowBox*>(curr)->adjustMaxAscentAndDescent(maxAscent, maxDescent, maxPositionTop, maxPositionBottom);
+ }
+}
+
+void InlineFlowBox::computeLogicalBoxHeights(int& maxPositionTop, int& maxPositionBottom,
+ int& maxAscent, int& maxDescent, bool strictMode)
+{
+ if (isRootInlineBox()) {
+ // Examine our root box.
+ setHeight(object()->lineHeight(m_firstLine));
+ bool isTableCell = object()->isTableCell();
+ if (isTableCell) {
+ RenderTableCell* tableCell = static_cast<RenderTableCell*>(object());
+ setBaseline(tableCell->RenderBlock::baselinePosition(m_firstLine));
+ }
+ else
+ setBaseline(object()->baselinePosition(m_firstLine));
+ if (hasTextChildren() || strictMode) {
+ int ascent = baseline();
+ int descent = height() - ascent;
+ if (maxAscent < ascent)
+ maxAscent = ascent;
+ if (maxDescent < descent)
+ maxDescent = descent;
+ }
+ }
+
+ for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) {
+ if (curr->object()->isPositioned())
+ continue; // Positioned placeholders don't affect calculations.
+
+ curr->setHeight(curr->object()->lineHeight(m_firstLine));
+ curr->setBaseline(curr->object()->baselinePosition(m_firstLine));
+ curr->setYPos(curr->object()->verticalPositionHint(m_firstLine));
+ if (curr->yPos() == PositionTop) {
+ if (maxPositionTop < curr->height())
+ maxPositionTop = curr->height();
+ }
+ else if (curr->yPos() == PositionBottom) {
+ if (maxPositionBottom < curr->height())
+ maxPositionBottom = curr->height();
+ }
+ else if (curr->hasTextChildren() || strictMode) {
+ int ascent = curr->baseline() - curr->yPos();
+ int descent = curr->height() - ascent;
+ if (maxAscent < ascent)
+ maxAscent = ascent;
+ if (maxDescent < descent)
+ maxDescent = descent;
+ }
+
+ if (curr->isInlineFlowBox())
+ static_cast<InlineFlowBox*>(curr)->computeLogicalBoxHeights(maxPositionTop, maxPositionBottom, maxAscent, maxDescent, strictMode);
+ }
+}
+
+void InlineFlowBox::placeBoxesVertically(int y, int maxHeight, int maxAscent, bool strictMode,
+ int& topPosition, int& bottomPosition)
+{
+ if (isRootInlineBox()) {
+ setYPos(y + maxAscent - baseline());// Place our root box.
+ // CSS2: 10.8.1 - line-height on the block level element specifies the *minimum*
+ // height of the generated line box
+ if (hasTextChildren() && maxHeight < object()->lineHeight(m_firstLine))
+ maxHeight = object()->lineHeight(m_firstLine);
+ }
+
+ for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) {
+ if (curr->object()->isPositioned())
+ continue; // Positioned placeholders don't affect calculations.
+
+ // Adjust boxes to use their real box y/height and not the logical height (as dictated by
+ // line-height).
+ if (curr->isInlineFlowBox())
+ static_cast<InlineFlowBox*>(curr)->placeBoxesVertically(y, maxHeight, maxAscent, strictMode,
+ topPosition, bottomPosition);
+
+ bool childAffectsTopBottomPos = true;
+
+ if (curr->yPos() == PositionTop)
+ curr->setYPos(y);
+ else if (curr->yPos() == PositionBottom)
+ curr->setYPos(y + maxHeight - curr->height());
+ else {
+ if (!strictMode && !curr->hasTextDescendant())
+ childAffectsTopBottomPos = false;
+ curr->setYPos(curr->yPos() + y + maxAscent - curr->baseline());
+ }
+ int newY = curr->yPos();
+ int newHeight = curr->height();
+ int newBaseline = curr->baseline();
+ int overflowTop = 0;
+ int overflowBottom = 0;
+ if (curr->isInlineTextBox() || curr->isInlineFlowBox()) {
+ const QFontMetrics &fm = curr->object()->fontMetrics( m_firstLine );
+#ifdef APPLE_CHANGES
+ newBaseline = fm.ascent();
+ newY += curr->baseline() - newBaseline;
+ newHeight = newBaseline+fm.descent();
+#else
+ // only adjust if the leading delta is superior to the font's natural leading
+ if ( kAbs(fm.ascent() - curr->baseline()) > fm.leading()/2 ) {
+ int ascent = fm.ascent()+fm.leading()/2;
+ newBaseline = ascent;
+ newY += curr->baseline() - newBaseline;
+ newHeight = fm.lineSpacing();
+ }
+#endif
+ for (ShadowData* shadow = curr->object()->style()->textShadow(); shadow; shadow = shadow->next) {
+ overflowTop = kMin(overflowTop, shadow->y - shadow->blur);
+ overflowBottom = kMax(overflowBottom, shadow->y + shadow->blur);
+ }
+ if (curr->isInlineFlowBox()) {
+ newHeight += curr->object()->borderTop() + curr->object()->paddingTop() +
+ curr->object()->borderBottom() + curr->object()->paddingBottom();
+ newY -= curr->object()->borderTop() + curr->object()->paddingTop();
+ newBaseline += curr->object()->borderTop() + curr->object()->paddingTop();
+ }
+ } else {
+ newY += curr->object()->marginTop();
+ newHeight = curr->height() - (curr->object()->marginTop() + curr->object()->marginBottom());
+ overflowTop = curr->object()->overflowTop();
+ overflowBottom = curr->object()->overflowHeight() - newHeight;
+ }
+ curr->setYPos(newY);
+ curr->setHeight(newHeight);
+ curr->setBaseline(newBaseline);
+
+ if (childAffectsTopBottomPos) {
+ topPosition = kMin(topPosition, newY + overflowTop);
+ bottomPosition = kMax(bottomPosition, newY + newHeight + overflowBottom);
+ }
+ }
+
+ if (isRootInlineBox()) {
+ const QFontMetrics &fm = object()->fontMetrics( m_firstLine );
+#ifdef APPLE_CHANGES
+ setHeight(fm.ascent()+fm.descent());
+ setYPos(yPos() + baseline() - fm.ascent());
+ setBaseline(fm.ascent());
+#else
+ if ( kAbs(fm.ascent() - baseline()) > fm.leading()/2 ) {
+ int ascent = fm.ascent()+fm.leading()/2;
+ setHeight(fm.lineSpacing());
+ setYPos(yPos() + baseline() - ascent);
+ setBaseline(ascent);
+ }
+#endif
+ if (hasTextDescendant() || strictMode) {
+ if (yPos() < topPosition)
+ topPosition = yPos();
+ if (yPos() + height() > bottomPosition)
+ bottomPosition = yPos() + height();
+ }
+ }
+}
+
+void InlineFlowBox::shrinkBoxesWithNoTextChildren(int topPos, int bottomPos)
+{
+ // First shrink our kids.
+ for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) {
+ if (curr->object()->isPositioned())
+ continue; // Positioned placeholders don't affect calculations.
+
+ if (curr->isInlineFlowBox())
+ static_cast<InlineFlowBox*>(curr)->shrinkBoxesWithNoTextChildren(topPos, bottomPos);
+ }
+
+ // See if we have text children. If not, then we need to shrink ourselves to fit on the line.
+ if (!hasTextDescendant()) {
+ if (yPos() < topPos)
+ setYPos(topPos);
+ if (yPos() + height() > bottomPos)
+ setHeight(bottomPos - yPos());
+ if (baseline() > height())
+ setBaseline(height());
+ }
+}
+
+bool InlineFlowBox::nodeAtPoint(RenderObject::NodeInfo& i, int x, int y, int tx, int ty)
+{
+ // Check children first.
+ for (InlineBox* curr = lastChild(); curr; curr = curr->prevOnLine()) {
+ if (!curr->object()->layer() && curr->nodeAtPoint(i, x, y, tx, ty)) {
+ object()->setInnerNode(i);
+ return true;
+ }
+ }
+
+ // Now check ourselves.
+ QRect rect(tx + m_x, ty + m_y, m_width, m_height);
+ if (object()->style()->visibility() == VISIBLE && rect.contains(x, y)) {
+ object()->setInnerNode(i);
+ return true;
+ }
+
+ return false;
+}
+
+
+void InlineFlowBox::paint(RenderObject::PaintInfo& i, int tx, int ty)
+{
+ bool intersectsDamageRect = true;
+ int xPos = tx + m_x - object()->maximalOutlineSize(i.phase);
+ int w = width() + 2 * object()->maximalOutlineSize(i.phase);
+ if ((xPos >= i.r.x() + i.r.width()) || (xPos + w <= i.r.x()))
+ intersectsDamageRect = false;
+
+ if (intersectsDamageRect) {
+ if (i.phase == PaintActionOutline) {
+ // Add ourselves to the paint info struct's list of inlines that need to paint their
+ // outlines.
+ if (object()->style()->visibility() == VISIBLE && object()->style()->outlineWidth() > 0 &&
+ !object()->isInlineContinuation() && !isRootInlineBox()) {
+ if (!i.outlineObjects)
+ i.outlineObjects = new QValueList<RenderFlow*>;
+ i.outlineObjects->append(static_cast<RenderFlow*>(object()));
+ }
+ }
+ else {
+ // 1. Paint our background and border.
+ paintBackgroundAndBorder(i, tx, ty);
+
+ // 2. Paint our underline and overline.
+ paintDecorations(i, tx, ty, false);
+ }
+ }
+
+ // 3. Paint our children.
+ for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) {
+ if (!curr->object()->layer())
+ curr->paint(i, tx, ty);
+ }
+
+ // 4. Paint our strike-through
+ if (intersectsDamageRect && i.phase != PaintActionOutline)
+ paintDecorations(i, tx, ty, true);
+}
+
+
+void InlineFlowBox::paintBackgrounds(QPainter* p, const QColor& c, const BackgroundLayer* bgLayer,
+ int my, int mh, int _tx, int _ty, int w, int h)
+{
+ if (!bgLayer)
+ return;
+ paintBackgrounds(p, c, bgLayer->next(), my, mh, _tx, _ty, w, h);
+ paintBackground(p, c, bgLayer, my, mh, _tx, _ty, w, h);
+}
+
+void InlineFlowBox::paintBackground(QPainter* p, const QColor& c, const BackgroundLayer* bgLayer,
+ int my, int mh, int _tx, int _ty, int w, int h)
+{
+ CachedImage* bg = bgLayer->backgroundImage();
+ bool hasBackgroundImage = bg && (bg->pixmap_size() == bg->valid_rect().size()) &&
+ !bg->isTransparent() && !bg->isErrorImage();
+ if (!hasBackgroundImage || (!prevLineBox() && !nextLineBox()) || !parent())
+ object()->paintBackgroundExtended(p, c, bgLayer, my, mh, _tx, _ty, w, h, borderLeft(), borderRight(), paddingLeft(), paddingRight());
+ else {
+ // We have a background image that spans multiple lines.
+ // We need to adjust _tx and _ty by the width of all previous lines.
+ // Think of background painting on inlines as though you had one long line, a single continuous
+ // strip. Even though that strip has been broken up across multiple lines, you still paint it
+ // as though you had one single line. This means each line has to pick up the background where
+ // the previous line left off.
+ // FIXME: What the heck do we do with RTL here? The math we're using is obviously not right,
+ // but it isn't even clear how this should work at all.
+ int xOffsetOnLine = 0;
+ for (InlineRunBox* curr = prevLineBox(); curr; curr = curr->prevLineBox())
+ xOffsetOnLine += curr->width();
+ int startX = _tx - xOffsetOnLine;
+ int totalWidth = xOffsetOnLine;
+ for (InlineRunBox* curr = this; curr; curr = curr->nextLineBox())
+ totalWidth += curr->width();
+ p->save();
+ p->setClipRect(QRect(_tx, _ty, width(), height()), QPainter::CoordPainter);
+ object()->paintBackgroundExtended(p, c, bgLayer, my, mh, startX, _ty,
+ totalWidth, h, borderLeft(), borderRight(), paddingLeft(), paddingRight());
+ p->restore();
+ }
+}
+
+void InlineFlowBox::paintBackgroundAndBorder(RenderObject::PaintInfo& pI, int _tx, int _ty)
+{
+ if (object()->style()->visibility() != VISIBLE || pI.phase != PaintActionForeground)
+ return;
+
+ // Move x/y to our coordinates.
+ _tx += m_x;
+ _ty += m_y;
+
+ int w = width();
+ int h = height();
+
+ int my = kMax(_ty, pI.r.y());
+ int mh;
+ if (_ty<pI.r.y())
+ mh= kMax(0,h-(pI.r.y()-_ty));
+ else
+ mh = kMin(pI.r.height(),h);
+
+ // You can use p::first-line to specify a background. If so, the root line boxes for
+ // a line may actually have to paint a background.
+ RenderStyle* styleToUse = object()->style(m_firstLine);
+ if ((!parent() && m_firstLine && styleToUse != object()->style()) ||
+ (parent() && object()->shouldPaintBackgroundOrBorder())) {
+ QColor c = styleToUse->backgroundColor();
+ paintBackgrounds(pI.p, c, styleToUse->backgroundLayers(), my, mh, _tx, _ty, w, h);
+
+ // :first-line cannot be used to put borders on a line. Always paint borders with our
+ // non-first-line style.
+ if (parent() && object()->style()->hasBorder())
+ object()->paintBorder(pI.p, _tx, _ty, w, h, object()->style(), includeLeftEdge(), includeRightEdge());
+ }
+}
+
+static bool shouldDrawDecoration(RenderObject* obj)
+{
+ bool shouldDraw = false;
+ for (RenderObject* curr = obj->firstChild();
+ curr; curr = curr->nextSibling()) {
+ if (curr->isInlineFlow()) {
+ shouldDraw = true;
+ break;
+ }
+ else if (curr->isText() && !curr->isBR() && (curr->style()->preserveWS() ||
+ !curr->element() || !curr->element()->containsOnlyWhitespace())) {
+ shouldDraw = true;
+ break;
+ }
+ }
+ return shouldDraw;
+}
+
+void InlineFlowBox::paintDecorations(RenderObject::PaintInfo& pI, int _tx, int _ty, bool paintedChildren)
+{
+ // Now paint our text decorations. We only do this if we aren't in quirks mode (i.e., in
+ // almost-strict mode or strict mode).
+ if (object()->style()->htmlHacks() || object()->style()->visibility() != VISIBLE)
+ return;
+
+ _tx += m_x;
+ _ty += m_y;
+ RenderStyle* styleToUse = object()->style(m_firstLine);
+ int deco = parent() ? styleToUse->textDecoration() : styleToUse->textDecorationsInEffect();
+ if (deco != TDNONE &&
+ ((!paintedChildren && ((deco & UNDERLINE) || (deco & OVERLINE))) || (paintedChildren && (deco & LINE_THROUGH))) &&
+ shouldDrawDecoration(object())) {
+ // We must have child boxes and have decorations defined.
+ _tx += borderLeft() + paddingLeft();
+ int w = m_width - (borderLeft() + paddingLeft() + borderRight() + paddingRight());
+ if ( !w )
+ return;
+ const QFontMetrics &fm = object()->fontMetrics( m_firstLine );
+ // thick lines on small fonts look ugly
+ int thickness = fm.height() > 20 ? fm.lineWidth() : 1;
+ QColor underline, overline, linethrough;
+ underline = overline = linethrough = styleToUse->color();
+ if (!parent())
+ object()->getTextDecorationColors(deco, underline, overline, linethrough);
+
+ if (styleToUse->font() != pI.p->font())
+ pI.p->setFont(styleToUse->font());
+
+ if (deco & UNDERLINE && !paintedChildren) {
+ int underlineOffset = ( fm.height() + m_baseline ) / 2;
+ if (underlineOffset <= m_baseline) underlineOffset = m_baseline+1;
+
+ pI.p->fillRect(_tx, _ty + underlineOffset, w, thickness, underline );
+ }
+ if (deco & OVERLINE && !paintedChildren) {
+ pI.p->fillRect(_tx, _ty, w, thickness, overline );
+ }
+ if (deco & LINE_THROUGH && paintedChildren) {
+ pI.p->fillRect(_tx, _ty + 2*m_baseline/3, w, thickness, linethrough );
+ }
+ }
+}
+
+bool InlineFlowBox::canAccommodateEllipsisBox(bool ltr, int blockEdge, int ellipsisWidth)
+{
+ for (InlineBox *box = firstChild(); box; box = box->nextOnLine()) {
+ if (!box->canAccommodateEllipsisBox(ltr, blockEdge, ellipsisWidth))
+ return false;
+ }
+ return true;
+}
+
+int InlineFlowBox::placeEllipsisBox(bool ltr, int blockEdge, int ellipsisWidth, bool& foundBox)
+{
+ int result = -1;
+ for (InlineBox *box = firstChild(); box; box = box->nextOnLine()) {
+ int currResult = box->placeEllipsisBox(ltr, blockEdge, ellipsisWidth, foundBox);
+ if (currResult != -1 && result == -1)
+ result = currResult;
+ }
+ return result;
+}
+
+void InlineFlowBox::clearTruncation()
+{
+ for (InlineBox *box = firstChild(); box; box = box->nextOnLine())
+ box->clearTruncation();
+}
+
+void EllipsisBox::paint(RenderObject::PaintInfo& i, int _tx, int _ty)
+{
+ QPainter* p = i.p;
+ RenderStyle* _style = m_firstLine ? m_object->style(true) : m_object->style();
+ if (_style->font() != p->font())
+ p->setFont(_style->font());
+
+ const Font* font = &_style->htmlFont();
+ QColor textColor = _style->color();
+ if (textColor != p->pen().color())
+ p->setPen(textColor);
+ /*
+ bool setShadow = false;
+ if (_style->textShadow()) {
+ p->setShadow(_style->textShadow()->x, _style->textShadow()->y,
+ _style->textShadow()->blur, _style->textShadow()->color);
+ setShadow = true;
+ }*/
+
+ const DOMString& str = m_str.string();
+ font->drawText(p, m_x + _tx,
+ m_y + _ty + m_baseline,
+ (str.implementation())->s,
+ str.length(), 0, str.length(),
+ 0,
+ QPainter::LTR, _style->visuallyOrdered());
+
+ /*
+ if (setShadow)
+ p->clearShadow();
+ */
+
+ if (m_markupBox) {
+ // Paint the markup box
+ _tx += m_x + m_width - m_markupBox->xPos();
+ _ty += m_y + m_baseline - (m_markupBox->yPos() + m_markupBox->baseline());
+ m_markupBox->object()->paint(i, _tx, _ty);
+ }
+}
+
+bool EllipsisBox::nodeAtPoint(RenderObject::NodeInfo& info, int _x, int _y, int _tx, int _ty)
+{
+ // Hit test the markup box.
+ if (m_markupBox) {
+ _tx += m_x + m_width - m_markupBox->xPos();
+ _ty += m_y + m_baseline - (m_markupBox->yPos() + m_markupBox->baseline());
+ if (m_markupBox->nodeAtPoint(info, _x, _y, _tx, _ty)) {
+ object()->setInnerNode(info);
+ return true;
+ }
+ }
+
+ QRect rect(_tx + m_x, _ty + m_y, m_width, m_height);
+ if (object()->style()->visibility() == VISIBLE && rect.contains(_x, _y)) {
+ object()->setInnerNode(info);
+ return true;
+ }
+ return false;
+}
+
+void RootInlineBox::detach(RenderArena* arena)
+{
+ detachEllipsisBox(arena);
+ InlineFlowBox::detach(arena);
+}
+
+void RootInlineBox::detachEllipsisBox(RenderArena* arena)
+{
+ if (m_ellipsisBox) {
+ m_ellipsisBox->detach(arena);
+ m_ellipsisBox = 0;
+ }
+}
+
+void RootInlineBox::clearTruncation()
+{
+ if (m_ellipsisBox) {
+ detachEllipsisBox(m_object->renderArena());
+ InlineFlowBox::clearTruncation();
+ }
+}
+
+bool RootInlineBox::canAccommodateEllipsis(bool ltr, int blockEdge, int lineBoxEdge, int ellipsisWidth)
+{
+ // First sanity-check the unoverflowed width of the whole line to see if there is sufficient room.
+ int delta = ltr ? lineBoxEdge - blockEdge : blockEdge - lineBoxEdge;
+ if (width() - delta < ellipsisWidth)
+ return false;
+
+ // Next iterate over all the line boxes on the line. If we find a replaced element that intersects
+ // then we refuse to accommodate the ellipsis. Otherwise we're ok.
+ return InlineFlowBox::canAccommodateEllipsisBox(ltr, blockEdge, ellipsisWidth);
+}
+
+void RootInlineBox::placeEllipsis(const DOMString& ellipsisStr, bool ltr, int blockEdge, int ellipsisWidth, InlineBox* markupBox)
+{
+ // Create an ellipsis box.
+ m_ellipsisBox = new (m_object->renderArena()) EllipsisBox(m_object, ellipsisStr, this,
+ ellipsisWidth - (markupBox ? markupBox->width() : 0),
+ yPos(), height(), baseline(), !prevRootBox(),
+ markupBox);
+
+ if (ltr && (xPos() + width() + ellipsisWidth) <= blockEdge) {
+ m_ellipsisBox->m_x = xPos() + width();
+ return;
+ }
+
+ // Now attempt to find the nearest glyph horizontally and place just to the right (or left in RTL)
+ // of that glyph. Mark all of the objects that intersect the ellipsis box as not painting (as being
+ // truncated).
+ bool foundBox = false;
+ m_ellipsisBox->m_x = placeEllipsisBox(ltr, blockEdge, ellipsisWidth, foundBox);
+}
+
+int RootInlineBox::placeEllipsisBox(bool ltr, int blockEdge, int ellipsisWidth, bool& foundBox)
+{
+ int result = InlineFlowBox::placeEllipsisBox(ltr, blockEdge, ellipsisWidth, foundBox);
+ if (result == -1)
+ result = ltr ? blockEdge - ellipsisWidth : blockEdge;
+ return result;
+}
+
+void RootInlineBox::paintEllipsisBox(RenderObject::PaintInfo& i, int _tx, int _ty) const
+{
+ if (m_ellipsisBox)
+ m_ellipsisBox->paint(i, _tx, _ty);
+}
+
+void RootInlineBox::paint(RenderObject::PaintInfo& i, int tx, int ty)
+{
+ InlineFlowBox::paint(i, tx, ty);
+ paintEllipsisBox(i, tx, ty);
+}
+
+bool RootInlineBox::nodeAtPoint(RenderObject::NodeInfo& i, int x, int y, int tx, int ty)
+{
+ if (m_ellipsisBox && object()->style()->visibility() == VISIBLE) {
+ if (m_ellipsisBox->nodeAtPoint(i, x, y, tx, ty)) {
+ object()->setInnerNode(i);
+ return true;
+ }
+ }
+ return InlineFlowBox::nodeAtPoint(i, x, y, tx, ty);
+}
+
diff --git a/khtml/rendering/render_line.h b/khtml/rendering/render_line.h
new file mode 100644
index 000000000..43603d362
--- /dev/null
+++ b/khtml/rendering/render_line.h
@@ -0,0 +1,310 @@
+/*
+ * This file is part of the line box implementation for KDE.
+ *
+ * Copyright (C) 2003 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.
+ *
+ */
+#ifndef RENDER_LINE_H
+#define RENDER_LINE_H
+
+namespace khtml {
+
+class EllipsisBox;
+class InlineFlowBox;
+class RootInlineBox;
+
+// InlineBox represents a rectangle that occurs on a line. It corresponds to
+// some RenderObject (i.e., it represents a portion of that RenderObject).
+class InlineBox
+{
+public:
+ InlineBox(RenderObject* obj)
+ :m_object(obj), m_x(0), m_width(0), m_y(0), m_height(0), m_baseline(0),
+ m_firstLine(false), m_constructed(false)
+ {
+ m_next = 0;
+ m_prev = 0;
+ m_parent = 0;
+ }
+
+ virtual ~InlineBox() {}
+
+ void detach(RenderArena* renderArena);
+
+ virtual void paint(RenderObject::PaintInfo& i, int _tx, int _ty);
+ virtual bool nodeAtPoint(RenderObject::NodeInfo& i, int x, int y, int tx, int ty);
+
+ // Overloaded new operator.
+ void* operator new(size_t sz, RenderArena* renderArena) throw();
+
+ // Overridden to prevent the normal delete from being called.
+ void operator delete(void* ptr, size_t sz);
+
+private:
+ // The normal operator new is disallowed.
+ void* operator new(size_t sz) throw();
+
+public:
+ virtual bool isInlineBox() const { return false; }
+ virtual bool isInlineFlowBox() const { return false; }
+ virtual bool isContainer() const { return false; }
+ virtual bool isInlineTextBox() const { return false; }
+ virtual bool isRootInlineBox() const { return false; }
+
+ bool isConstructed() const { return m_constructed; }
+ virtual void setConstructed() {
+ m_constructed = true;
+ if (m_next)
+ m_next->setConstructed();
+ }
+
+ void setFirstLineStyleBit(bool f) { m_firstLine = f; }
+
+ InlineBox* nextOnLine() { return m_next; }
+ InlineBox* prevOnLine() { return m_prev; }
+ RenderObject* object() const { return m_object; }
+
+ InlineFlowBox* parent() { return m_parent; }
+ void setParent(InlineFlowBox* par) { m_parent = par; }
+
+ RootInlineBox* root();
+
+ void setWidth(short w) { m_width = w; }
+ short width() const { return m_width; }
+
+ void setXPos(short x) { m_x = x; }
+ short xPos() const { return m_x; }
+
+ void setYPos(int y) { m_y = y; }
+ int yPos() const { return m_y; }
+
+ void setHeight(int h) { m_height = h; }
+ int height() const { return m_height; }
+
+ void setBaseline(int b) { m_baseline = b; }
+ int baseline() const { return m_baseline; }
+
+ virtual bool hasTextChildren() const { return true; }
+ virtual bool hasTextDescendant() const { return true; }
+
+ virtual int topOverflow() const { return yPos(); }
+ virtual int bottomOverflow() const { return yPos()+height(); }
+
+ virtual long minOffset() const { return 0; }
+ virtual long maxOffset() const { return 0; }
+
+ virtual void clearTruncation() {};
+
+ virtual bool canAccommodateEllipsisBox(bool ltr, int blockEdge, int ellipsisWidth);
+ virtual int placeEllipsisBox(bool ltr, int blockEdge, int ellipsisWidth, bool&);
+
+public: // FIXME: Would like to make this protected, but methods are accessing these
+ // members over in the part.
+ RenderObject* m_object;
+
+ short m_x;
+ short m_width;
+ int m_y;
+ int m_height;
+ int m_baseline;
+
+ bool m_firstLine : 1;
+ bool m_constructed : 1;
+
+ InlineBox* m_next; // The next element on the same line as us.
+ InlineBox* m_prev; // The previous element on the same line as us.
+
+ InlineFlowBox* m_parent; // The box that contains us.
+};
+
+class InlineRunBox : public InlineBox
+{
+public:
+ InlineRunBox(RenderObject* obj)
+ :InlineBox(obj)
+ {
+ m_prevLine = 0;
+ m_nextLine = 0;
+ }
+
+ InlineRunBox* prevLineBox() { return m_prevLine; }
+ InlineRunBox* nextLineBox() { return m_nextLine; }
+ void setNextLineBox(InlineRunBox* n) { m_nextLine = n; }
+ void setPreviousLineBox(InlineRunBox* p) { m_prevLine = p; }
+
+ virtual void paintBackgroundAndBorder(RenderObject::PaintInfo&, int /*_tx*/, int /*_ty*/) {}
+ virtual void paintDecorations(RenderObject::PaintInfo&, int /*_tx*/, int /*_ty*/, bool /*paintedChildren*/ = false) {}
+
+protected:
+ InlineRunBox* m_prevLine; // The previous box that also uses our RenderObject
+ InlineRunBox* m_nextLine; // The next box that also uses our RenderObject
+};
+
+class InlineFlowBox : public InlineRunBox
+{
+public:
+ InlineFlowBox(RenderObject* obj)
+ :InlineRunBox(obj)
+ {
+ m_firstChild = 0;
+ m_lastChild = 0;
+ m_includeLeftEdge = m_includeRightEdge = false;
+ m_hasTextChildren = false;
+ m_hasTextDescendant = false;
+ m_afterPageBreak = false;
+ }
+
+ ~InlineFlowBox();
+
+ virtual bool isInlineFlowBox() const { return true; }
+
+ InlineFlowBox* prevFlowBox() const { return static_cast<InlineFlowBox*>(m_prevLine); }
+ InlineFlowBox* nextFlowBox() const { return static_cast<InlineFlowBox*>(m_nextLine); }
+
+ InlineBox* firstChild() const { return m_firstChild; }
+ InlineBox* lastChild() const { return m_lastChild; }
+
+ virtual void setConstructed() {
+ InlineBox::setConstructed();
+ if (m_firstChild)
+ m_firstChild->setConstructed();
+ }
+ void addToLine(InlineBox* child) {
+ if (!m_firstChild)
+ m_firstChild = m_lastChild = child;
+ else {
+ m_lastChild->m_next = child;
+ child->m_prev = m_lastChild;
+ m_lastChild = child;
+ }
+ child->setFirstLineStyleBit(m_firstLine);
+ child->setParent(this);
+ if (!m_hasTextChildren && child->isInlineTextBox()) {
+ m_hasTextDescendant = m_hasTextChildren = true;
+ for (InlineFlowBox* p = m_parent; p && !p->hasTextDescendant(); p = p->parent())
+ p->m_hasTextDescendant = true;
+ }
+ }
+
+ virtual void clearTruncation();
+
+ void removeFromLine(InlineBox* child);
+ virtual void paintBackgroundAndBorder(RenderObject::PaintInfo&, int _tx, int _ty);
+ void paintBackgrounds(QPainter* p, const QColor& c, const BackgroundLayer* bgLayer,
+ int my, int mh, int _tx, int _ty, int w, int h);
+ void paintBackground(QPainter* p, const QColor& c, const BackgroundLayer* bgLayer,
+ int my, int mh, int _tx, int _ty, int w, int h);
+ virtual void paint(RenderObject::PaintInfo& i, int _tx, int _ty);
+ virtual void paintDecorations(RenderObject::PaintInfo&, int _tx, int _ty, bool paintedChildren = false);
+ virtual bool nodeAtPoint(RenderObject::NodeInfo& i, int x, int y, int tx, int ty);
+
+ int marginBorderPaddingLeft() const;
+ int marginBorderPaddingRight() const;
+ int marginLeft() const;
+ int marginRight( )const;
+ int borderLeft() const { if (includeLeftEdge()) return object()->borderLeft(); return 0; }
+ int borderRight() const { if (includeRightEdge()) return object()->borderRight(); return 0; }
+ int paddingLeft() const { if (includeLeftEdge()) return object()->paddingLeft(); return 0; }
+ int paddingRight() const { if (includeRightEdge()) return object()->paddingRight(); return 0; }
+
+ bool includeLeftEdge() const { return m_includeLeftEdge; }
+ bool includeRightEdge() const { return m_includeRightEdge; }
+ void setEdges(bool includeLeft, bool includeRight) {
+ m_includeLeftEdge = includeLeft;
+ m_includeRightEdge = includeRight;
+ }
+ virtual bool hasTextChildren() const { return m_hasTextChildren; }
+ bool hasTextDescendant() const { return m_hasTextDescendant; }
+
+ // Helper functions used during line construction and placement.
+ void determineSpacingForFlowBoxes(bool lastLine, RenderObject* endObject);
+ int getFlowSpacingWidth() const;
+ bool nextOnLineExists();
+ bool prevOnLineExists();
+ bool onEndChain(RenderObject* endObject);
+ int placeBoxesHorizontally(int x);
+ void verticallyAlignBoxes(int& heightOfBlock);
+ void computeLogicalBoxHeights(int& maxPositionTop, int& maxPositionBottom,
+ int& maxAscent, int& maxDescent, bool strictMode);
+ void adjustMaxAscentAndDescent(int& maxAscent, int& maxDescent,
+ int maxPositionTop, int maxPositionBottom);
+ void placeBoxesVertically(int y, int maxHeight, int maxAscent, bool strictMode,
+ int& topPosition, int& bottomPosition);
+ void shrinkBoxesWithNoTextChildren(int topPosition, int bottomPosition);
+
+ virtual void setOverflowPositions(int /*top*/, int /*bottom*/) {}
+
+ void setAfterPageBreak(bool b = true) { m_afterPageBreak = b; }
+ bool afterPageBreak() const { return m_afterPageBreak; }
+
+ virtual bool canAccommodateEllipsisBox(bool ltr, int blockEdge, int ellipsisWidth);
+ virtual int placeEllipsisBox(bool ltr, int blockEdge, int ellipsisWidth, bool&);
+
+protected:
+ InlineBox* m_firstChild;
+ InlineBox* m_lastChild;
+ bool m_includeLeftEdge : 1;
+ bool m_includeRightEdge : 1;
+ bool m_hasTextChildren : 1;
+ bool m_hasTextDescendant : 1;
+ bool m_afterPageBreak : 1;
+};
+
+class RootInlineBox : public InlineFlowBox
+{
+public:
+ RootInlineBox(RenderObject* obj) : InlineFlowBox(obj), m_ellipsisBox(0)
+ {
+ m_topOverflow = m_bottomOverflow = 0;
+ }
+
+ virtual void detach(RenderArena* renderArena);
+ void detachEllipsisBox(RenderArena* renderArena);
+
+ RootInlineBox* nextRootBox() { return static_cast<RootInlineBox*>(m_nextLine); }
+ RootInlineBox* prevRootBox() { return static_cast<RootInlineBox*>(m_prevLine); }
+
+ virtual bool isRootInlineBox() const { return true; }
+ virtual int topOverflow() const { return m_topOverflow; }
+ virtual int bottomOverflow() const { return m_bottomOverflow; }
+ virtual void setOverflowPositions(int top, int bottom) { m_topOverflow = top; m_bottomOverflow = bottom; }
+
+ bool canAccommodateEllipsis(bool ltr, int blockEdge, int lineBoxEdge, int ellipsisWidth);
+ void placeEllipsis(const DOM::DOMString& ellipsisStr, bool ltr, int blockEdge, int ellipsisWidth, InlineBox* markupBox = 0);
+ virtual int placeEllipsisBox(bool ltr, int blockEdge, int ellipsisWidth, bool&);
+
+ EllipsisBox* ellipsisBox() const { return m_ellipsisBox; }
+ void paintEllipsisBox(RenderObject::PaintInfo& i, int _tx, int _ty) const;
+ bool hitTestEllipsisBox(RenderObject::NodeInfo& info, int _x, int _y, int _tx, int _ty);
+
+ virtual void clearTruncation();
+
+ virtual void paint(RenderObject::PaintInfo& i, int _tx, int _ty);
+ virtual bool nodeAtPoint(RenderObject::NodeInfo& i, int x, int y, int tx, int ty);
+
+protected:
+ int m_topOverflow;
+ int m_bottomOverflow;
+
+ // An inline text box that represents our text truncation string.
+ EllipsisBox* m_ellipsisBox;
+};
+
+} //namespace
+
+#endif
diff --git a/khtml/rendering/render_list.cpp b/khtml/rendering/render_list.cpp
new file mode 100644
index 000000000..139201e03
--- /dev/null
+++ b/khtml/rendering/render_list.cpp
@@ -0,0 +1,586 @@
+/**
+ * This file is part of the HTML rendering engine for KDE.
+ *
+ * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000-2002 Dirk Mueller (mueller@kde.org)
+ * (C) 2003 Apple Computer, Inc.
+ * (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com)
+ *
+ * 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 "rendering/render_list.h"
+#include "rendering/render_canvas.h"
+#include "rendering/enumerate.h"
+#include "rendering/counter_tree.h"
+#include "html/html_listimpl.h"
+#include "misc/helper.h"
+#include "misc/htmltags.h"
+#include "misc/loader.h"
+#include "xml/dom_docimpl.h"
+
+#include <kdebug.h>
+#include <kglobal.h>
+#include <qvaluelist.h>
+
+//#define BOX_DEBUG
+
+using namespace khtml;
+using namespace Enumerate;
+
+const int cMarkerPadding = 7;
+
+// -------------------------------------------------------------------------
+
+RenderListItem::RenderListItem(DOM::NodeImpl* node)
+ : RenderBlock(node)
+{
+ // init RenderObject attributes
+ setInline(false); // our object is not Inline
+
+ predefVal = -1;
+ m_marker = 0;
+ m_counter = 0;
+ m_insideList = false;
+ m_deleteMarker = false;
+}
+
+void RenderListItem::setStyle(RenderStyle *_style)
+{
+ RenderBlock::setStyle(_style);
+
+ RenderStyle *newStyle = new RenderStyle();
+ newStyle->ref();
+
+ newStyle->inheritFrom(style());
+
+ if(!m_marker && style()->listStyleType() != LNONE) {
+ m_marker = new (renderArena()) RenderListMarker(element());
+ m_marker->setIsAnonymous( true );
+ m_marker->setStyle(newStyle);
+ m_marker->setListItem( this );
+ m_deleteMarker = true;
+ } else if ( m_marker && style()->listStyleType() == LNONE) {
+ m_marker->detach();
+ m_marker = 0;
+ }
+ else if ( m_marker ) {
+ m_marker->setStyle(newStyle);
+ }
+
+ newStyle->deref();
+}
+
+void RenderListItem::detach()
+{
+ if ( m_marker && m_deleteMarker )
+ m_marker->detach();
+ RenderBlock::detach();
+}
+
+static RenderObject* getParentOfFirstLineBox(RenderObject* curr, RenderObject* marker)
+{
+ RenderObject* firstChild = curr->firstChild();
+ if (!firstChild)
+ return 0;
+
+ for (RenderObject* currChild = firstChild;
+ currChild; currChild = currChild->nextSibling()) {
+ if (currChild == marker)
+ continue;
+
+ if (currChild->isInline())
+ return curr;
+
+ if (currChild->isFloating() || currChild->isPositioned())
+ continue;
+
+ if (currChild->isTable() || !currChild->isRenderBlock())
+ break;
+
+ if (currChild->style()->htmlHacks() && currChild->element() &&
+ (currChild->element()->id() == ID_UL || currChild->element()->id() == ID_OL))
+ break;
+
+ RenderObject* lineBox = getParentOfFirstLineBox(currChild, marker);
+ if (lineBox)
+ return lineBox;
+ }
+
+ return 0;
+}
+
+
+void RenderListItem::updateMarkerLocation()
+{
+ // Sanity check the location of our marker.
+ if (m_marker) {
+ RenderObject* markerPar = m_marker->parent();
+ RenderObject* lineBoxParent = getParentOfFirstLineBox(this, m_marker);
+ if (!lineBoxParent) {
+ // If the marker is currently contained inside an anonymous box,
+ // then we are the only item in that anonymous box (since no line box
+ // parent was found). It's ok to just leave the marker where it is
+ // in this case.
+ if (markerPar && markerPar->isAnonymousBlock())
+ lineBoxParent = markerPar;
+ else
+ lineBoxParent = this;
+ }
+ if (markerPar != lineBoxParent)
+ {
+ if (markerPar)
+ markerPar->removeChild(m_marker);
+ if (!lineBoxParent)
+ lineBoxParent = this;
+ lineBoxParent->addChild(m_marker, lineBoxParent->firstChild());
+ m_deleteMarker = false;
+ if (!m_marker->minMaxKnown())
+ m_marker->calcMinMaxWidth();
+ recalcMinMaxWidths();
+ }
+ }
+}
+
+void RenderListItem::calcMinMaxWidth()
+{
+ // Make sure our marker is in the correct location.
+ updateMarkerLocation();
+ if (!minMaxKnown())
+ RenderBlock::calcMinMaxWidth();
+}
+/*
+short RenderListItem::marginLeft() const
+{
+ if (m_insideList)
+ return RenderBlock::marginLeft();
+ else
+ return kMax(m_marker->markerWidth(), RenderBlock::marginLeft());
+}
+
+short RenderListItem::marginRight() const
+{
+ return RenderBlock::marginRight();
+}*/
+
+void RenderListItem::layout( )
+{
+ KHTMLAssert( needsLayout() );
+ KHTMLAssert( minMaxKnown() );
+
+ updateMarkerLocation();
+ RenderBlock::layout();
+}
+
+// -----------------------------------------------------------
+
+RenderListMarker::RenderListMarker(DOM::NodeImpl* node)
+ : RenderBox(node), m_listImage(0), m_markerWidth(0)
+{
+ // init RenderObject attributes
+ setInline(true); // our object is Inline
+ setReplaced(true); // pretend to be replaced
+ // val = -1;
+ // m_listImage = 0;
+}
+
+RenderListMarker::~RenderListMarker()
+{
+ if(m_listImage)
+ m_listImage->deref(this);
+ if (m_listItem)
+ m_listItem->resetListMarker();
+}
+
+void RenderListMarker::setStyle(RenderStyle *s)
+{
+ if ( s && style() && s->listStylePosition() != style()->listStylePosition() )
+ setNeedsLayoutAndMinMaxRecalc();
+
+ RenderBox::setStyle(s);
+
+ if ( m_listImage != style()->listStyleImage() ) {
+ if(m_listImage) m_listImage->deref(this);
+ m_listImage = style()->listStyleImage();
+ if(m_listImage) m_listImage->ref(this);
+ }
+}
+
+
+void RenderListMarker::paint(PaintInfo& paintInfo, int _tx, int _ty)
+{
+ if (paintInfo.phase != PaintActionForeground)
+ return;
+
+ if (style()->visibility() != VISIBLE) return;
+
+ _tx += m_x;
+ _ty += m_y;
+
+ if((_ty > paintInfo.r.bottom()) || (_ty + m_height <= paintInfo.r.top()))
+ return;
+
+ if(shouldPaintBackgroundOrBorder())
+ paintBoxDecorations(paintInfo, _tx, _ty);
+
+ QPainter* p = paintInfo.p;
+#ifdef DEBUG_LAYOUT
+ kdDebug( 6040 ) << nodeName().string() << "(ListMarker)::paintObject(" << _tx << ", " << _ty << ")" << endl;
+#endif
+ p->setFont(style()->font());
+ const QFontMetrics fm = p->fontMetrics();
+
+
+ // The marker needs to adjust its tx, for the case where it's an outside marker.
+ RenderObject* listItem = 0;
+ int leftLineOffset = 0;
+ int rightLineOffset = 0;
+ if (!listPositionInside()) {
+ listItem = this;
+ int yOffset = 0;
+ int xOffset = 0;
+ while (listItem && listItem != m_listItem) {
+ yOffset += listItem->yPos();
+ xOffset += listItem->xPos();
+ listItem = listItem->parent();
+ }
+
+ // Now that we have our xoffset within the listbox, we need to adjust ourselves by the delta
+ // between our current xoffset and our desired position (which is just outside the border box
+ // of the list item).
+ if (style()->direction() == LTR) {
+ leftLineOffset = m_listItem->leftRelOffset(yOffset, m_listItem->leftOffset(yOffset));
+ _tx -= (xOffset - leftLineOffset) + m_listItem->paddingLeft() + m_listItem->borderLeft();
+ }
+ else {
+ rightLineOffset = m_listItem->rightRelOffset(yOffset, m_listItem->rightOffset(yOffset));
+ _tx += (rightLineOffset-xOffset) + m_listItem->paddingRight() + m_listItem->borderRight();
+ }
+ }
+
+ int offset = fm.ascent()*2/3;
+ bool haveImage = m_listImage && !m_listImage->isErrorImage();
+ if (haveImage)
+ offset = m_listImage->pixmap().width();
+
+ int xoff = 0;
+ int yoff = fm.ascent() - offset;
+
+ int bulletWidth = offset/2;
+ if (offset%2)
+ bulletWidth++;
+ if (!listPositionInside()) {
+ if (listItem && listItem->style()->direction() == LTR)
+ xoff = -cMarkerPadding - offset;
+ else
+ xoff = cMarkerPadding + (haveImage ? 0 : (offset - bulletWidth));
+ }
+ else if (style()->direction() == RTL)
+ xoff += haveImage ? cMarkerPadding : (m_width - bulletWidth);
+
+ if ( m_listImage && !m_listImage->isErrorImage()) {
+ p->drawPixmap( QPoint( _tx + xoff, _ty ), m_listImage->pixmap());
+ return;
+ }
+
+#ifdef BOX_DEBUG
+ p->setPen( Qt::red );
+ p->drawRect( _tx + xoff, _ty + yoff, offset, offset );
+#endif
+
+ const QColor color( style()->color() );
+ p->setPen( color );
+
+ switch(style()->listStyleType()) {
+ case LDISC:
+ p->setBrush( color );
+ p->drawEllipse( _tx + xoff, _ty + (3 * yoff)/2, (offset>>1)+1, (offset>>1)+1 );
+ return;
+ case LCIRCLE:
+ p->setBrush( Qt::NoBrush );
+ p->drawEllipse( _tx + xoff, _ty + (3 * yoff)/2, (offset>>1)+1, (offset>>1)+1 );
+ return;
+ case LSQUARE:
+ p->setBrush( color );
+ p->drawRect( _tx + xoff, _ty + (3 * yoff)/2, (offset>>1)+1, (offset>>1)+1 );
+ return;
+ case LBOX:
+ p->setBrush( Qt::NoBrush );
+ p->drawRect( _tx + xoff, _ty + (3 * yoff)/2, (offset>>1)+1, (offset>>1)+1 );
+ return;
+ case LDIAMOND: {
+ static QPointArray diamond(4);
+ int x = _tx + xoff;
+ int y = _ty + (3 * yoff)/2 - 1;
+ int s = (offset>>2)+1;
+ diamond[0] = QPoint(x+s, y);
+ diamond[1] = QPoint(x+2*s, y+s);
+ diamond[2] = QPoint(x+s, y+2*s);
+ diamond[3] = QPoint(x, y+s);
+ p->setBrush( color );
+ p->drawConvexPolygon( diamond, 0, 4 );
+ return;
+ }
+ case LNONE:
+ return;
+ default:
+ if (!m_item.isEmpty()) {
+ if(listPositionInside()) {
+ if( style()->direction() == LTR) {
+ p->drawText(_tx, _ty, 0, 0, Qt::AlignLeft|Qt::DontClip, m_item);
+ p->drawText(_tx + fm.width(m_item), _ty, 0, 0, Qt::AlignLeft|Qt::DontClip,
+ QString::fromLatin1(". "));
+ }
+ else {
+ const QString& punct(QString::fromLatin1(" ."));
+ p->drawText(_tx, _ty, 0, 0, Qt::AlignLeft|Qt::DontClip, punct);
+ p->drawText(_tx + fm.width(punct), _ty, 0, 0, Qt::AlignLeft|Qt::DontClip, m_item);
+ }
+ } else {
+ if (style()->direction() == LTR) {
+ const QString& punct(QString::fromLatin1(". "));
+ p->drawText(_tx-offset/2, _ty, 0, 0, Qt::AlignRight|Qt::DontClip, punct);
+ p->drawText(_tx-offset/2-fm.width(punct), _ty, 0, 0, Qt::AlignRight|Qt::DontClip, m_item);
+ }
+ else {
+ const QString& punct(QString::fromLatin1(" ."));
+ p->drawText(_tx+offset/2, _ty, 0, 0, Qt::AlignLeft|Qt::DontClip, punct);
+ p->drawText(_tx+offset/2+fm.width(punct), _ty, 0, 0, Qt::AlignLeft|Qt::DontClip, m_item);
+ }
+ }
+ }
+ }
+}
+
+void RenderListMarker::layout()
+{
+ KHTMLAssert( needsLayout() );
+
+ if ( !minMaxKnown() )
+ calcMinMaxWidth();
+
+ setNeedsLayout(false);
+}
+
+void RenderListMarker::setPixmap( const QPixmap &p, const QRect& r, CachedImage *o)
+{
+ if(o != m_listImage) {
+ RenderBox::setPixmap(p, r, o);
+ return;
+ }
+
+ if(m_width != m_listImage->pixmap_size().width() || m_height != m_listImage->pixmap_size().height())
+ setNeedsLayoutAndMinMaxRecalc();
+ else
+ repaintRectangle(0, 0, m_width, m_height);
+}
+
+void RenderListMarker::calcMinMaxWidth()
+{
+ KHTMLAssert( !minMaxKnown() );
+
+ m_markerWidth = m_width = 0;
+
+ if(m_listImage && !m_listImage->isErrorImage()) {
+ m_markerWidth = m_listImage->pixmap().width() + cMarkerPadding;
+ if (listPositionInside())
+ m_width = m_markerWidth;
+ m_height = m_listImage->pixmap().height();
+ m_minWidth = m_maxWidth = m_width;
+ setMinMaxKnown();
+ return;
+ }
+
+ const QFontMetrics &fm = style()->fontMetrics();
+ m_height = fm.ascent();
+
+ // Skip uncounted elements
+ switch(style()->listStyleType()) {
+ // Glyphs:
+ case LDISC:
+ case LCIRCLE:
+ case LSQUARE:
+ case LBOX:
+ case LDIAMOND:
+ m_markerWidth = fm.ascent();
+ goto end;
+ default:
+ break;
+ }
+
+ { // variable scope
+ CounterNode *counter = m_listItem->m_counter;
+ if (!counter) {
+ counter = m_listItem->getCounter("list-item", true);
+ counter->setRenderer(this);
+ m_listItem->m_counter = counter;
+ }
+
+
+ assert(counter);
+ int value = counter->count();
+ if (counter->isReset()) value = counter->value();
+ int total = value;
+ if (counter->parent()) total = counter->parent()->total();
+
+ switch(style()->listStyleType())
+ {
+// Numeric:
+ case LDECIMAL:
+ m_item.setNum ( value );
+ break;
+ case DECIMAL_LEADING_ZERO: {
+ int decimals = 2;
+ int t = total/100;
+ while (t>0) {
+ t = t/10;
+ decimals++;
+ }
+ decimals = kMax(decimals, 2);
+ QString num = QString::number(value);
+ m_item.fill('0',decimals-num.length());
+ m_item.append(num);
+ break;
+ }
+ case ARABIC_INDIC:
+ m_item = toArabicIndic( value );
+ break;
+ case LAO:
+ m_item = toLao( value );
+ break;
+ case PERSIAN:
+ case URDU:
+ m_item = toPersianUrdu( value );
+ break;
+ case THAI:
+ m_item = toThai( value );
+ break;
+ case TIBETAN:
+ m_item = toTibetan( value );
+ break;
+// Algoritmic:
+ case LOWER_ROMAN:
+ m_item = toRoman( value, false );
+ break;
+ case UPPER_ROMAN:
+ m_item = toRoman( value, true );
+ break;
+ case HEBREW:
+ m_item = toHebrew( value );
+ break;
+ case ARMENIAN:
+ m_item = toArmenian( value );
+ break;
+ case GEORGIAN:
+ m_item = toGeorgian( value );
+ break;
+// Alphabetic:
+ case LOWER_ALPHA:
+ case LOWER_LATIN:
+ m_item = toLowerLatin( value );
+ break;
+ case UPPER_ALPHA:
+ case UPPER_LATIN:
+ m_item = toUpperLatin( value );
+ break;
+ case LOWER_GREEK:
+ m_item = toLowerGreek( value );
+ break;
+ case UPPER_GREEK:
+ m_item = toUpperGreek( value );
+ break;
+ case HIRAGANA:
+ m_item = toHiragana( value );
+ break;
+ case HIRAGANA_IROHA:
+ m_item = toHiraganaIroha( value );
+ break;
+ case KATAKANA:
+ m_item = toKatakana( value );
+ break;
+ case KATAKANA_IROHA:
+ m_item = toKatakanaIroha( value );
+ break;
+// Ideographic:
+ case JAPANESE_FORMAL:
+ m_item = toJapaneseFormal( value );
+ break;
+ case JAPANESE_INFORMAL:
+ m_item = toJapaneseInformal( value );
+ break;
+ case SIMP_CHINESE_FORMAL:
+ m_item = toSimpChineseFormal( value );
+ break;
+ case SIMP_CHINESE_INFORMAL:
+ m_item = toSimpChineseInformal( value );
+ break;
+ case TRAD_CHINESE_FORMAL:
+ m_item = toTradChineseFormal( value );
+ break;
+ case CJK_IDEOGRAPHIC:
+ // CSS 3 List says treat as trad-chinese-informal
+ case TRAD_CHINESE_INFORMAL:
+ m_item = toTradChineseInformal( value );
+ break;
+// special:
+ case LNONE:
+ break;
+ default:
+ KHTMLAssert(false);
+ }
+ m_markerWidth = fm.width(m_item) + fm.width(QString::fromLatin1(". "));
+ }
+
+end:
+ if(listPositionInside())
+ m_width = m_markerWidth;
+
+ m_minWidth = m_width;
+ m_maxWidth = m_width;
+
+ setMinMaxKnown();
+}
+
+short RenderListMarker::lineHeight(bool /*b*/) const
+{
+ return height();
+}
+
+short RenderListMarker::baselinePosition(bool /*b*/) const
+{
+ return height();
+}
+
+void RenderListMarker::calcWidth()
+{
+ RenderBox::calcWidth();
+}
+
+/*
+int CounterListItem::recount() const
+{
+ static_cast<RenderListItem*>(m_renderer)->m_marker->setNeedsLayoutAndMinMaxRecalc();
+}
+
+void CounterListItem::setSelfDirty()
+{
+
+}*/
+
+
+#undef BOX_DEBUG
diff --git a/khtml/rendering/render_list.h b/khtml/rendering/render_list.h
new file mode 100644
index 000000000..ff4e24842
--- /dev/null
+++ b/khtml/rendering/render_list.h
@@ -0,0 +1,140 @@
+/*
+ * This file is part of the HTML rendering engine for KDE.
+ *
+ * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * (C) 2004 Allan Sandfeld Jensen (kde@carewolf.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef RENDER_LIST_H
+#define RENDER_LIST_H
+
+#include "rendering/render_block.h"
+
+// ### list-style-position, list-style-image is still missing
+
+namespace DOM
+{
+ class DocumentImpl;
+}
+
+namespace khtml
+{
+
+class RenderListItem;
+class RenderListMarker;
+class CounterNode;
+
+// -----------------------------------------------------------------------------
+
+class RenderListItem : public RenderBlock
+{
+ friend class RenderListMarker;
+// friend class CounterListItem;
+
+public:
+ RenderListItem(DOM::NodeImpl*);
+
+ virtual const char *renderName() const { return "RenderListItem"; }
+
+ virtual void setStyle(RenderStyle *style);
+
+ virtual bool isListItem() const { return true; }
+
+ void setValue( long v ) { predefVal = v; }
+
+ virtual void layout( );
+ virtual void detach( );
+ virtual void calcMinMaxWidth();
+ //virtual short marginLeft() const;
+ //virtual short marginRight() const;
+
+ void setInsideList(bool b ) { m_insideList = b; }
+
+protected:
+
+ void updateMarkerLocation();
+ void resetListMarker() { m_marker = 0; }
+
+ RenderListMarker *m_marker;
+ CounterNode *m_counter;
+ signed long predefVal : 30;
+ bool m_insideList : 1;
+ bool m_deleteMarker: 1;
+};
+
+// -----------------------------------------------------------------------------
+
+class RenderListMarker : public RenderBox
+{
+public:
+ RenderListMarker(DOM::NodeImpl* node);
+ ~RenderListMarker();
+
+ virtual void setStyle(RenderStyle *style);
+
+ virtual const char *renderName() const { return "RenderListMarker"; }
+ // so the marker gets to layout itself. Only needed for
+ // list-style-position: inside
+
+ virtual void paint(PaintInfo& i, int xoff, int yoff);
+ virtual void layout( );
+ virtual void calcMinMaxWidth();
+
+ virtual short lineHeight( bool firstLine ) const;
+ virtual short baselinePosition( bool firstLine ) const;
+
+ virtual void setPixmap( const QPixmap &, const QRect&, CachedImage *);
+
+ virtual void calcWidth();
+
+ virtual bool isListMarker() const { return true; }
+
+ virtual short markerWidth() const { return m_markerWidth; }
+
+ RenderListItem* listItem() const { return m_listItem; }
+ void setListItem(RenderListItem* listItem) { m_listItem = listItem; }
+
+ bool listPositionInside() const
+ { return !m_listItem->m_insideList || style()->listStylePosition() == INSIDE; }
+
+protected:
+ friend class RenderListItem;
+
+ QString m_item;
+ CachedImage *m_listImage;
+ short m_markerWidth;
+ RenderListItem* m_listItem;
+};
+
+// Implementation of list-item counter
+// ### should replace most list-item specific code in renderObject::getCounter
+/*
+class CounterListItem : public CounterNode
+{
+public:
+ int count() const;
+
+ virtual void recount( bool first = false );
+ virtual void setSelfDirty();
+
+}; */
+
+} //namespace
+
+#endif
diff --git a/khtml/rendering/render_object.cpp b/khtml/rendering/render_object.cpp
new file mode 100644
index 000000000..dfb2e06df
--- /dev/null
+++ b/khtml/rendering/render_object.cpp
@@ -0,0 +1,2325 @@
+/**
+ * This file is part of the html renderer for KDE.
+ *
+ * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000-2003 Dirk Mueller (mueller@kde.org)
+ * (C) 2002-2006 Apple Computer, Inc.
+ * (C) 2006 Germain Garand <germain@ebooksfrance.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "rendering/render_object.h"
+#include "rendering/render_table.h"
+#include "rendering/render_list.h"
+#include "rendering/render_canvas.h"
+#include "rendering/render_block.h"
+#include "rendering/render_arena.h"
+#include "rendering/render_layer.h"
+#include "rendering/render_line.h"
+#include "rendering/render_inline.h"
+#include "rendering/render_text.h"
+#include "rendering/render_replaced.h"
+#include "rendering/render_generated.h"
+#include "rendering/counter_tree.h"
+
+#include "xml/dom_elementimpl.h"
+#include "xml/dom_docimpl.h"
+#include "dom/dom_doc.h"
+#include "misc/htmlhashes.h"
+#include "misc/loader.h"
+
+#include <kdebug.h>
+#include <kglobal.h>
+#include <qpainter.h>
+#include "khtmlview.h"
+#include <khtml_part.h>
+
+#include <assert.h>
+using namespace DOM;
+using namespace khtml;
+
+#define RED_LUMINOSITY 30
+#define GREEN_LUMINOSITY 59
+#define BLUE_LUMINOSITY 11
+#define INTENSITY_FACTOR 25
+#define LIGHT_FACTOR 0
+#define LUMINOSITY_FACTOR 75
+
+#define MAX_COLOR 255
+#define COLOR_DARK_THRESHOLD 51
+#define COLOR_LIGHT_THRESHOLD 204
+
+#define COLOR_LITE_BS_FACTOR 45
+#define COLOR_LITE_TS_FACTOR 70
+
+#define COLOR_DARK_BS_FACTOR 30
+#define COLOR_DARK_TS_FACTOR 50
+
+#define LIGHT_GRAY qRgb(192, 192, 192)
+#define DARK_GRAY qRgb(96, 96, 96)
+
+#ifndef NDEBUG
+static void *baseOfRenderObjectBeingDeleted;
+#endif
+
+//#define MASK_DEBUG
+
+void* RenderObject::operator new(size_t sz, RenderArena* renderArena) throw()
+{
+ return renderArena->allocate(sz);
+}
+
+void RenderObject::operator delete(void* ptr, size_t sz)
+{
+ assert(baseOfRenderObjectBeingDeleted == ptr);
+
+ // Stash size where detach can find it.
+ *(size_t *)ptr = sz;
+}
+
+RenderObject *RenderObject::createObject(DOM::NodeImpl* node, RenderStyle* style)
+{
+ RenderObject *o = 0;
+ khtml::RenderArena* arena = node->getDocument()->renderArena();
+ switch(style->display())
+ {
+ case NONE:
+ break;
+ case INLINE:
+ o = new (arena) RenderInline(node);
+ break;
+ case BLOCK:
+ o = new (arena) RenderBlock(node);
+ break;
+ case INLINE_BLOCK:
+ o = new (arena) RenderBlock(node);
+ break;
+ case LIST_ITEM:
+ o = new (arena) RenderListItem(node);
+ break;
+ case RUN_IN:
+ case COMPACT:
+ o = new (arena) RenderBlock(node);
+ break;
+ case TABLE:
+ case INLINE_TABLE:
+ style->setFlowAroundFloats(true);
+ o = new (arena) RenderTable(node);
+ break;
+ case TABLE_ROW_GROUP:
+ case TABLE_HEADER_GROUP:
+ case TABLE_FOOTER_GROUP:
+ o = new (arena) RenderTableSection(node);
+ break;
+ case TABLE_ROW:
+ o = new (arena) RenderTableRow(node);
+ break;
+ case TABLE_COLUMN_GROUP:
+ case TABLE_COLUMN:
+ o = new (arena) RenderTableCol(node);
+ break;
+ case TABLE_CELL:
+ o = new (arena) RenderTableCell(node);
+ break;
+ case TABLE_CAPTION:
+ o = new (arena) RenderBlock(node);
+ break;
+ }
+ return o;
+}
+
+
+RenderObject::RenderObject(DOM::NodeImpl* node)
+ : CachedObjectClient(),
+ m_style( 0 ),
+ m_node( node ),
+ m_parent( 0 ),
+ m_previous( 0 ),
+ m_next( 0 ),
+ m_verticalPosition( PositionUndefined ),
+ m_needsLayout( false ),
+ m_normalChildNeedsLayout( false ),
+ m_markedForRepaint( false ),
+ m_posChildNeedsLayout( false ),
+ m_minMaxKnown( false ),
+ m_floating( false ),
+
+ m_positioned( false ),
+ m_overhangingContents( false ),
+ m_relPositioned( false ),
+ m_paintBackground( false ),
+
+ m_isAnonymous( node->isDocumentNode() ),
+ m_recalcMinMax( false ),
+ m_isText( false ),
+ m_inline( true ),
+ m_attached( false ),
+
+ m_replaced( false ),
+ m_mouseInside( false ),
+ m_hasFirstLine( false ),
+ m_isSelectionBorder( false ),
+ m_isRoot( false ),
+ m_afterPageBreak( false ),
+ m_needsPageClear( false ),
+ m_containsPageBreak( false ),
+ m_hasOverflowClip( false ),
+ m_doNotDelete( false )
+{
+ assert( node );
+ if (node->getDocument()->documentElement() == node) setIsRoot(true);
+}
+
+RenderObject::~RenderObject()
+{
+ const BackgroundLayer* bgLayer = m_style->backgroundLayers();
+ while (bgLayer) {
+ if(bgLayer->backgroundImage())
+ bgLayer->backgroundImage()->deref(this);
+ bgLayer = bgLayer->next();
+ }
+
+ if (m_style)
+ m_style->deref();
+}
+
+
+
+RenderObject* RenderObject::objectBelow() const
+{
+ RenderObject* obj = firstChild();
+ if ( !obj ) {
+ obj = nextSibling();
+ if ( !obj )
+ {
+ obj = parent();
+ while (obj && !obj->nextSibling())
+ obj = obj->parent();
+ if (obj)
+ obj = obj->nextSibling();
+ }
+ }
+ return obj;
+}
+
+RenderObject* RenderObject::objectAbove() const
+{
+ RenderObject* obj = previousSibling();
+ if ( !obj )
+ return parent();
+
+ RenderObject* last = obj->lastChild();
+ while ( last )
+ {
+ obj = last;
+ last = last->lastChild();
+ }
+ return obj;
+}
+/*
+bool RenderObject::isRoot() const
+{
+ return !isAnonymous() &&
+ element()->getDocument()->documentElement() == element();
+}*/
+
+bool RenderObject::isHR() const
+{
+ return element() && element()->id() == ID_HR;
+}
+
+bool RenderObject::isHTMLMarquee() const
+{
+ return element() && element()->renderer() == this && element()->id() == ID_MARQUEE;
+}
+
+void RenderObject::addChild(RenderObject* , RenderObject *)
+{
+ KHTMLAssert(0);
+}
+
+RenderObject* RenderObject::removeChildNode(RenderObject* )
+{
+ KHTMLAssert(0);
+ return 0;
+}
+
+void RenderObject::removeChild(RenderObject *o )
+{
+ setNeedsLayout(true);
+ removeChildNode( o );
+}
+
+void RenderObject::appendChildNode(RenderObject*)
+{
+ KHTMLAssert(0);
+}
+
+void RenderObject::insertChildNode(RenderObject*, RenderObject*)
+{
+ KHTMLAssert(0);
+}
+
+RenderObject *RenderObject::nextRenderer() const
+{
+ if (firstChild())
+ return firstChild();
+ else if (nextSibling())
+ return nextSibling();
+ else {
+ const RenderObject *r = this;
+ while (r && !r->nextSibling())
+ r = r->parent();
+ if (r)
+ return r->nextSibling();
+ }
+ return 0;
+}
+
+RenderObject *RenderObject::previousRenderer() const
+{
+ if (previousSibling()) {
+ RenderObject *r = previousSibling();
+ while (r->lastChild())
+ r = r->lastChild();
+ return r;
+ }
+ else if (parent()) {
+ return parent();
+ }
+ else {
+ return 0;
+ }
+}
+
+static void addLayers(RenderObject* obj, RenderLayer* parentLayer, RenderObject*& newObject,
+ RenderLayer*& beforeChild)
+{
+ if (obj->layer()) {
+ if (!beforeChild && newObject) {
+ // We need to figure out the layer that follows newObject. We only do
+ // this the first time we find a child layer, and then we update the
+ // pointer values for newObject and beforeChild used by everyone else.
+ beforeChild = newObject->parent()->findNextLayer(parentLayer, newObject);
+ newObject = 0;
+ }
+ parentLayer->addChild(obj->layer(), beforeChild);
+ return;
+ }
+
+ for (RenderObject* curr = obj->firstChild(); curr; curr = curr->nextSibling())
+ addLayers(curr, parentLayer, newObject, beforeChild);
+}
+
+void RenderObject::addLayers(RenderLayer* parentLayer, RenderObject* newObject)
+{
+ if (!parentLayer)
+ return;
+
+ RenderObject* object = newObject;
+ RenderLayer* beforeChild = 0;
+ ::addLayers(this, parentLayer, object, beforeChild);
+}
+
+void RenderObject::removeLayers(RenderLayer* parentLayer)
+{
+ if (!parentLayer)
+ return;
+
+ if (layer()) {
+ parentLayer->removeChild(layer());
+ return;
+ }
+
+ for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling())
+ curr->removeLayers(parentLayer);
+}
+
+void RenderObject::moveLayers(RenderLayer* oldParent, RenderLayer* newParent)
+{
+ if (!newParent)
+ return;
+
+ if (layer()) {
+ if (oldParent)
+ oldParent->removeChild(layer());
+ newParent->addChild(layer());
+ return;
+ }
+
+ for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling())
+ curr->moveLayers(oldParent, newParent);
+}
+
+RenderLayer* RenderObject::findNextLayer(RenderLayer* parentLayer, RenderObject* startPoint,
+ bool checkParent)
+{
+ // Error check the parent layer passed in. If it's null, we can't find anything.
+ if (!parentLayer)
+ return 0;
+
+ // Step 1: If our layer is a child of the desired parent, then return our layer.
+ RenderLayer* ourLayer = layer();
+ if (ourLayer && ourLayer->parent() == parentLayer)
+ return ourLayer;
+
+ // Step 2: If we don't have a layer, or our layer is the desired parent, then descend
+ // into our siblings trying to find the next layer whose parent is the desired parent.
+ if (!ourLayer || ourLayer == parentLayer) {
+ for (RenderObject* curr = startPoint ? startPoint->nextSibling() : firstChild();
+ curr; curr = curr->nextSibling()) {
+ RenderLayer* nextLayer = curr->findNextLayer(parentLayer, 0, false);
+ if (nextLayer)
+ return nextLayer;
+ }
+ }
+
+ // Step 3: If our layer is the desired parent layer, then we're finished. We didn't
+ // find anything.
+ if (parentLayer == ourLayer)
+ return 0;
+
+ // Step 4: If |checkParent| is set, climb up to our parent and check its siblings that
+ // follow us to see if we can locate a layer.
+ if (checkParent && parent())
+ return parent()->findNextLayer(parentLayer, this, true);
+
+ return 0;
+}
+
+RenderLayer* RenderObject::enclosingLayer() const
+{
+ const RenderObject* curr = this;
+ while (curr) {
+ RenderLayer *layer = curr->layer();
+ if (layer)
+ return layer;
+ curr = curr->parent();
+ }
+ return 0;
+}
+
+RenderLayer* RenderObject::enclosingStackingContext() const
+{
+ RenderLayer* l = enclosingLayer();
+ while (l && !l->isStackingContext())
+ l = l->parent();
+ return l;
+}
+
+int RenderObject::offsetLeft() const
+{
+ if ( isPositioned() )
+ return xPos();
+
+ if ( isBody() && style()->htmlHacks() )
+ return 0;
+
+ int x = xPos();
+ if (isRelPositioned()) {
+ int y = 0;
+ static_cast<const RenderBox*>(this)->relativePositionOffset(x, y);
+ }
+
+ RenderObject* offsetPar = offsetParent();
+ for( RenderObject* curr = parent();
+ curr && curr != offsetPar;
+ curr = curr->parent() )
+ x += curr->xPos();
+
+ if ( offsetPar && offsetPar->isBody() && style()->htmlHacks() )
+ x += offsetPar->xPos();
+
+ return x;
+}
+
+int RenderObject::offsetTop() const
+{
+ if ( isPositioned() )
+ return yPos();
+
+ if ( isBody() && style()->htmlHacks() )
+ return 0;
+
+ int y = yPos();
+ if (isRelPositioned()) {
+ int x = 0;
+ static_cast<const RenderBox*>(this)->relativePositionOffset(x, y);
+ }
+ RenderObject* offsetPar = offsetParent();
+ for( RenderObject* curr = parent();
+ curr && curr != offsetPar;
+ curr = curr->parent() )
+ y += curr->yPos();
+
+ if ( offsetPar && offsetPar->isBody() && style()->htmlHacks() )
+ y += offsetPar->yPos();
+
+ return y;
+}
+
+RenderObject* RenderObject::offsetParent() const
+{
+ if (isBody())
+ return 0;
+
+ // can't really use containing blocks here (#113280)
+ bool skipTables = isPositioned() || isRelPositioned();
+ bool strict = !style()->htmlHacks();
+ RenderObject* curr = parent();
+ while (curr && (!curr->element() ||
+ (!curr->isPositioned() && !curr->isRelPositioned() &&
+ !(strict && skipTables ? curr->isRoot() : curr->isBody())))) {
+ if (!skipTables && curr->element() && (curr->isTableCell() || curr->isTable()))
+ break;
+ curr = curr->parent();
+ }
+ return curr;
+}
+
+// IE extensions.
+// clientWidth and clientHeight represent the interior of an object
+short RenderObject::clientWidth() const
+{
+ return width() - borderLeft() - borderRight() -
+ (layer() ? layer()->verticalScrollbarWidth() : 0);
+}
+
+int RenderObject::clientHeight() const
+{
+ return height() - borderTop() - borderBottom() -
+ (layer() ? layer()->horizontalScrollbarHeight() : 0);
+}
+
+// scrollWidth/scrollHeight is the size including the overflow area
+short RenderObject::scrollWidth() const
+{
+ return (hasOverflowClip() && layer()) ? layer()->scrollWidth() : overflowWidth() - overflowLeft();
+}
+
+int RenderObject::scrollHeight() const
+{
+ return (hasOverflowClip() && layer()) ? layer()->scrollHeight() : overflowHeight() - overflowTop();
+}
+
+bool RenderObject::hasStaticX() const
+{
+ return (style()->left().isVariable() && style()->right().isVariable());
+}
+
+bool RenderObject::hasStaticY() const
+{
+ return (style()->top().isVariable() && style()->bottom().isVariable());
+}
+
+void RenderObject::setPixmap(const QPixmap&, const QRect& /*r*/, CachedImage* image)
+{
+ //repaint bg when it finished loading
+ if(image && parent() && style() && style()->backgroundLayers()->containsImage(image)) {
+ isBody() ? canvas()->repaint() : repaint();
+ }
+}
+
+void RenderObject::setNeedsLayout(bool b, bool markParents)
+{
+ bool alreadyNeededLayout = m_needsLayout;
+ m_needsLayout = b;
+ if (b) {
+ if (!alreadyNeededLayout && markParents && m_parent) {
+ dirtyFormattingContext( false );
+ markContainingBlocksForLayout();
+ }
+ }
+ else {
+ m_posChildNeedsLayout = false;
+ m_normalChildNeedsLayout = false;
+ }
+}
+
+void RenderObject::setChildNeedsLayout(bool b, bool markParents)
+{
+ bool alreadyNeededLayout = m_normalChildNeedsLayout;
+ m_normalChildNeedsLayout = b;
+ if (b) {
+ if (!alreadyNeededLayout && markParents)
+ markContainingBlocksForLayout();
+ }
+ else {
+ m_posChildNeedsLayout = false;
+ m_normalChildNeedsLayout = false;
+ }
+}
+
+void RenderObject::markContainingBlocksForLayout()
+{
+ RenderObject *o = container();
+ RenderObject *last = this;
+
+ while (o) {
+ if (!last->isText() && (last->style()->position() == FIXED || last->style()->position() == ABSOLUTE)) {
+ if (o->m_posChildNeedsLayout)
+ return;
+ o->m_posChildNeedsLayout = true;
+ }
+ else {
+ if (o->m_normalChildNeedsLayout)
+ return;
+ o->m_normalChildNeedsLayout = true;
+ }
+
+ last = o;
+ o = o->container();
+ }
+
+ last->scheduleRelayout();
+}
+
+RenderBlock *RenderObject::containingBlock() const
+{
+ if(isTableCell())
+ return static_cast<RenderBlock*>( parent()->parent()->parent() );
+ if (isCanvas())
+ return const_cast<RenderBlock*>( static_cast<const RenderBlock*>(this) );
+
+ RenderObject *o = parent();
+ if(m_style->position() == FIXED) {
+ while ( o && !o->isCanvas() )
+ o = o->parent();
+ }
+ else if(m_style->position() == ABSOLUTE) {
+ while (o &&
+ ( o->style()->position() == STATIC || ( o->isInline() && !o->isReplaced() ) ) && !o->isCanvas()) {
+ // for relpos inlines, return the nearest block - it will host the positioned objects list
+ if (o->isInline() && !o->isReplaced() && o->style()->position() == RELATIVE)
+ return o->containingBlock();
+ o = o->parent();
+ }
+ } else {
+ while(o && ( ( o->isInline() && !o->isReplaced() ) || o->isTableRow() || o->isTableSection() ||
+ o->isTableCol() || o->isFrameSet() ) )
+ o = o->parent();
+ }
+ // this is just to make sure we return a valid element.
+ // the case below should never happen...
+ if(!o || !o->isRenderBlock()) {
+ if(!isCanvas()) {
+#ifndef NDEBUG
+ kdDebug( 6040 ) << this << ": " << renderName() << "(RenderObject): No containingBlock!" << endl;
+ kdDebug( 6040 ) << kdBacktrace() << endl;
+ const RenderObject* p = this;
+ while (p->parent()) p = p->parent();
+ p->printTree();
+#endif
+ }
+ return 0L;
+ }
+
+ return static_cast<RenderBlock*>( o );
+}
+
+short RenderObject::containingBlockWidth() const
+{
+ // ###
+ return containingBlock()->contentWidth();
+}
+
+int RenderObject::containingBlockHeight() const
+{
+ // ###
+ return containingBlock()->contentHeight();
+}
+
+bool RenderObject::sizesToMaxWidth() const
+{
+ // Marquees in WinIE are like a mixture of blocks and inline-blocks. They size as though they're blocks,
+ // but they allow text to sit on the same line as the marquee.
+ if (isFloating() || isCompact() ||
+ (isInlineBlockOrInlineTable() && !isHTMLMarquee()) ||
+ (element() && (element()->id() == ID_BUTTON || element()->id() == ID_LEGEND)))
+ return true;
+
+ // Children of a horizontal marquee do not fill the container by default.
+ // FIXME: Need to deal with MAUTO value properly. It could be vertical.
+ if (parent()->style()->overflowX() == OMARQUEE) {
+ EMarqueeDirection dir = parent()->style()->marqueeDirection();
+ if (dir == MAUTO || dir == MFORWARD || dir == MBACKWARD || dir == MLEFT || dir == MRIGHT)
+ return true;
+ }
+
+#ifdef APPLE_CHANGES // ### what the heck is a flexbox?
+ // Flexible horizontal boxes lay out children at their maxwidths. Also vertical boxes
+ // that don't stretch their kids lay out their children at their maxwidths.
+ if (parent()->isFlexibleBox() &&
+ (parent()->style()->boxOrient() == HORIZONTAL || parent()->style()->boxAlign() != BSTRETCH))
+ return true;
+#endif
+
+ return false;
+}
+
+// from Mozilla's nsCSSColorUtils.cpp
+static int brightness(int red, int green, int blue)
+{
+
+ int intensity = (red + green + blue) / 3;
+
+ int luminosity =
+ ((RED_LUMINOSITY * red) / 100) +
+ ((GREEN_LUMINOSITY * green) / 100) +
+ ((BLUE_LUMINOSITY * blue) / 100);
+
+ return ((intensity * INTENSITY_FACTOR) +
+ (luminosity * LUMINOSITY_FACTOR)) / 100;
+}
+
+static void calc3DColor(QColor &color, bool darken)
+{
+ int rb = color.red();
+ int gb = color.green();
+ int bb = color.blue();
+
+ int brightness_ = brightness(rb,gb,bb);
+
+ int f0, f1;
+ if (brightness_ < COLOR_DARK_THRESHOLD) {
+ f0 = COLOR_DARK_BS_FACTOR;
+ f1 = COLOR_DARK_TS_FACTOR;
+ } else if (brightness_ > COLOR_LIGHT_THRESHOLD) {
+ f0 = COLOR_LITE_BS_FACTOR;
+ f1 = COLOR_LITE_TS_FACTOR;
+ } else {
+ f0 = COLOR_DARK_BS_FACTOR +
+ (brightness_ *
+ (COLOR_LITE_BS_FACTOR - COLOR_DARK_BS_FACTOR) / MAX_COLOR);
+ f1 = COLOR_DARK_TS_FACTOR +
+ (brightness_ *
+ (COLOR_LITE_TS_FACTOR - COLOR_DARK_TS_FACTOR) / MAX_COLOR);
+ }
+
+ if (darken) {
+ int r = rb - (f0 * rb / 100);
+ int g = gb - (f0 * gb / 100);
+ int b = bb - (f0 * bb / 100);
+ if ((r == rb) && (g == gb) && (b == bb))
+ color = (color == Qt::black) ? DARK_GRAY : Qt::black;
+ else
+ color.setRgb(r, g, b);
+ } else {
+ int r = kMin(rb + (f1 * (MAX_COLOR - rb) / 100), 255);
+ int g = kMin(gb + (f1 * (MAX_COLOR - gb) / 100), 255);
+ int b = kMin(bb + (f1 * (MAX_COLOR - bb) / 100), 255);
+ if ((r == rb) && (g == gb) && (b == bb))
+ color = (color == Qt::white) ? LIGHT_GRAY : Qt::white;
+ else
+ color.setRgb(r, g, b);
+ }
+}
+
+void RenderObject::drawBorder(QPainter *p, int x1, int y1, int x2, int y2,
+ BorderSide s, QColor c, const QColor& textcolor, EBorderStyle style,
+ int adjbw1, int adjbw2, bool invalidisInvert)
+{
+ int width = (s==BSTop||s==BSBottom?y2-y1:x2-x1);
+
+ if(style == DOUBLE && width < 3)
+ style = SOLID;
+
+ if(!c.isValid()) {
+ if(invalidisInvert)
+ {
+ p->setRasterOp(Qt::XorROP);
+ c = Qt::white;
+ }
+ else {
+ if(style == INSET || style == OUTSET || style == RIDGE || style ==
+ GROOVE)
+ c = Qt::white;
+ else
+ c = textcolor;
+ }
+ }
+
+ switch(style)
+ {
+ case BNATIVE:
+ case BNONE:
+ case BHIDDEN:
+ // should not happen
+ if(invalidisInvert && p->rasterOp() == Qt::XorROP)
+ p->setRasterOp(Qt::CopyROP);
+
+ return;
+ case DOTTED:
+ if ( width == 1 ) {
+ // workaround Qt brokenness
+ p->setPen(QPen(c, width, Qt::SolidLine));
+ switch(s) {
+ case BSBottom:
+ case BSTop:
+ for ( ; x1 < x2; x1 += 2 )
+ p->drawPoint( x1, y1 );
+ break;
+ case BSRight:
+ case BSLeft:
+ for ( ; y1 < y2; y1 += 2 )
+ p->drawPoint( x1, y1 );
+ }
+ break;
+ }
+
+ p->setPen(QPen(c, width, Qt::DotLine));
+ /* nobreak; */
+ case DASHED:
+ if(style == DASHED)
+ p->setPen(QPen(c, width == 1 ? 0 : width, width == 1 ? Qt::DotLine : Qt::DashLine));
+
+ if (width > 0)
+ switch(s) {
+ case BSBottom:
+ case BSTop:
+ p->drawLine(x1, (y1+y2)/2, x2, (y1+y2)/2);
+ break;
+ case BSRight:
+ case BSLeft:
+ p->drawLine((x1+x2)/2, y1, (x1+x2)/2, y2);
+ break;
+ }
+
+ break;
+ case DOUBLE:
+ {
+ int third = (width+1)/3;
+
+ if (adjbw1 == 0 && adjbw2 == 0)
+ {
+ p->setPen(Qt::NoPen);
+ p->setBrush(c);
+ switch(s)
+ {
+ case BSTop:
+ case BSBottom:
+ p->drawRect(x1, y1 , x2-x1, third);
+ p->drawRect(x1, y2-third, x2-x1, third);
+ break;
+ case BSLeft:
+ p->drawRect(x1 , y1+1, third, y2-y1-1);
+ p->drawRect(x2-third, y1+1, third, y2-y1-1);
+ break;
+ case BSRight:
+ p->drawRect(x1 , y1+1, third, y2-y1-1);
+ p->drawRect(x2-third, y1+1, third, y2-y1-1);
+ break;
+ }
+ }
+ else
+ {
+ int adjbw1bigthird;
+ if (adjbw1>0) adjbw1bigthird = adjbw1+1;
+ else adjbw1bigthird = adjbw1 - 1;
+ adjbw1bigthird /= 3;
+
+ int adjbw2bigthird;
+ if (adjbw2>0) adjbw2bigthird = adjbw2 + 1;
+ else adjbw2bigthird = adjbw2 - 1;
+ adjbw2bigthird /= 3;
+
+ switch(s)
+ {
+ case BSTop:
+ drawBorder(p, x1+kMax((-adjbw1*2+1)/3,0), y1 , x2-kMax((-adjbw2*2+1)/3,0), y1 + third, s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird);
+ drawBorder(p, x1+kMax(( adjbw1*2+1)/3,0), y2 - third, x2-kMax(( adjbw2*2+1)/3,0), y2 , s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird);
+ break;
+ case BSLeft:
+ drawBorder(p, x1 , y1+kMax((-adjbw1*2+1)/3,0), x1+third, y2-kMax((-adjbw2*2+1)/3,0), s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird);
+ drawBorder(p, x2 - third, y1+kMax(( adjbw1*2+1)/3,0), x2 , y2-kMax(( adjbw2*2+1)/3,0), s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird);
+ break;
+ case BSBottom:
+ drawBorder(p, x1+kMax(( adjbw1*2+1)/3,0), y1 , x2-kMax(( adjbw2*2+1)/3,0), y1+third, s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird);
+ drawBorder(p, x1+kMax((-adjbw1*2+1)/3,0), y2-third, x2-kMax((-adjbw2*2+1)/3,0), y2 , s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird);
+ break;
+ case BSRight:
+ drawBorder(p, x1 , y1+kMax(( adjbw1*2+1)/3,0), x1+third, y2-kMax(( adjbw2*2+1)/3,0), s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird);
+ drawBorder(p, x2-third, y1+kMax((-adjbw1*2+1)/3,0), x2 , y2-kMax((-adjbw2*2+1)/3,0), s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird);
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ }
+ case RIDGE:
+ case GROOVE:
+ {
+ EBorderStyle s1;
+ EBorderStyle s2;
+ if (style==GROOVE)
+ {
+ s1 = INSET;
+ s2 = OUTSET;
+ }
+ else
+ {
+ s1 = OUTSET;
+ s2 = INSET;
+ }
+
+ int adjbw1bighalf;
+ int adjbw2bighalf;
+ if (adjbw1>0) adjbw1bighalf=adjbw1+1;
+ else adjbw1bighalf=adjbw1-1;
+ adjbw1bighalf/=2;
+
+ if (adjbw2>0) adjbw2bighalf=adjbw2+1;
+ else adjbw2bighalf=adjbw2-1;
+ adjbw2bighalf/=2;
+
+ switch (s)
+ {
+ case BSTop:
+ drawBorder(p, x1+kMax(-adjbw1 ,0)/2, y1 , x2-kMax(-adjbw2,0)/2, (y1+y2+1)/2, s, c, textcolor, s1, adjbw1bighalf, adjbw2bighalf);
+ drawBorder(p, x1+kMax( adjbw1+1,0)/2, (y1+y2+1)/2, x2-kMax( adjbw2+1,0)/2, y2 , s, c, textcolor, s2, adjbw1/2, adjbw2/2);
+ break;
+ case BSLeft:
+ drawBorder(p, x1 , y1+kMax(-adjbw1 ,0)/2, (x1+x2+1)/2, y2-kMax(-adjbw2,0)/2, s, c, textcolor, s1, adjbw1bighalf, adjbw2bighalf);
+ drawBorder(p, (x1+x2+1)/2, y1+kMax( adjbw1+1,0)/2, x2 , y2-kMax( adjbw2+1,0)/2, s, c, textcolor, s2, adjbw1/2, adjbw2/2);
+ break;
+ case BSBottom:
+ drawBorder(p, x1+kMax( adjbw1 ,0)/2, y1 , x2-kMax( adjbw2,0)/2, (y1+y2+1)/2, s, c, textcolor, s2, adjbw1bighalf, adjbw2bighalf);
+ drawBorder(p, x1+kMax(-adjbw1+1,0)/2, (y1+y2+1)/2, x2-kMax(-adjbw2+1,0)/2, y2 , s, c, textcolor, s1, adjbw1/2, adjbw2/2);
+ break;
+ case BSRight:
+ drawBorder(p, x1 , y1+kMax( adjbw1 ,0)/2, (x1+x2+1)/2, y2-kMax( adjbw2,0)/2, s, c, textcolor, s2, adjbw1bighalf, adjbw2bighalf);
+ drawBorder(p, (x1+x2+1)/2, y1+kMax(-adjbw1+1,0)/2, x2 , y2-kMax(-adjbw2+1,0)/2, s, c, textcolor, s1, adjbw1/2, adjbw2/2);
+ break;
+ }
+ break;
+ }
+ case INSET:
+ case OUTSET:
+ calc3DColor(c, (style == OUTSET && (s == BSBottom || s == BSRight)) ||
+ (style == INSET && ( s == BSTop || s == BSLeft ) ) );
+ /* nobreak; */
+ case SOLID:
+ p->setPen(Qt::NoPen);
+ p->setBrush(c);
+ Q_ASSERT(x2>=x1);
+ Q_ASSERT(y2>=y1);
+ if (adjbw1==0 && adjbw2 == 0) {
+ p->drawRect(x1,y1,x2-x1,y2-y1);
+ return;
+ }
+ QPointArray quad(4);
+ switch(s) {
+ case BSTop:
+ quad.setPoints(4,
+ x1+kMax(-adjbw1,0), y1,
+ x1+kMax( adjbw1,0), y2,
+ x2-kMax( adjbw2,0), y2,
+ x2-kMax(-adjbw2,0), y1);
+ break;
+ case BSBottom:
+ quad.setPoints(4,
+ x1+kMax( adjbw1,0), y1,
+ x1+kMax(-adjbw1,0), y2,
+ x2-kMax(-adjbw2,0), y2,
+ x2-kMax( adjbw2,0), y1);
+ break;
+ case BSLeft:
+ quad.setPoints(4,
+ x1, y1+kMax(-adjbw1,0),
+ x1, y2-kMax(-adjbw2,0),
+ x2, y2-kMax( adjbw2,0),
+ x2, y1+kMax( adjbw1,0));
+ break;
+ case BSRight:
+ quad.setPoints(4,
+ x1, y1+kMax( adjbw1,0),
+ x1, y2-kMax( adjbw2,0),
+ x2, y2-kMax(-adjbw2,0),
+ x2, y1+kMax(-adjbw1,0));
+ break;
+ }
+ p->drawConvexPolygon(quad);
+ break;
+ }
+
+ if(invalidisInvert && p->rasterOp() == Qt::XorROP)
+ p->setRasterOp(Qt::CopyROP);
+}
+
+void RenderObject::paintBorder(QPainter *p, int _tx, int _ty, int w, int h, const RenderStyle* style, bool begin, bool end)
+{
+ const QColor& tc = style->borderTopColor();
+ const QColor& bc = style->borderBottomColor();
+ const QColor& lc = style->borderLeftColor();
+ const QColor& rc = style->borderRightColor();
+
+ bool tt = style->borderTopIsTransparent();
+ bool bt = style->borderBottomIsTransparent();
+ bool rt = style->borderRightIsTransparent();
+ bool lt = style->borderLeftIsTransparent();
+
+ EBorderStyle ts = style->borderTopStyle();
+ EBorderStyle bs = style->borderBottomStyle();
+ EBorderStyle ls = style->borderLeftStyle();
+ EBorderStyle rs = style->borderRightStyle();
+
+ bool render_t = ts > BHIDDEN && !tt;
+ bool render_l = ls > BHIDDEN && begin && !lt;
+ bool render_r = rs > BHIDDEN && end && !rt;
+ bool render_b = bs > BHIDDEN && !bt;
+
+ if(render_t) {
+ bool ignore_left =
+ (tc == lc) && (tt == lt) &&
+ (ts >= OUTSET) &&
+ (ls == DOTTED || ls == DASHED || ls == SOLID || ls == OUTSET);
+
+ bool ignore_right =
+ (tc == rc) && (tt == rt) &&
+ (ts >= OUTSET) &&
+ (rs == DOTTED || rs == DASHED || rs == SOLID || rs == INSET);
+
+ drawBorder(p, _tx, _ty, _tx + w, _ty + style->borderTopWidth(), BSTop, tc, style->color(), ts,
+ ignore_left?0:style->borderLeftWidth(),
+ ignore_right?0:style->borderRightWidth());
+ }
+
+ if(render_b) {
+ bool ignore_left =
+ (bc == lc) && (bt == lt) &&
+ (bs >= OUTSET) &&
+ (ls == DOTTED || ls == DASHED || ls == SOLID || ls == OUTSET);
+
+ bool ignore_right =
+ (bc == rc) && (bt == rt) &&
+ (bs >= OUTSET) &&
+ (rs == DOTTED || rs == DASHED || rs == SOLID || rs == INSET);
+
+ drawBorder(p, _tx, _ty + h - style->borderBottomWidth(), _tx + w, _ty + h, BSBottom, bc, style->color(), bs,
+ ignore_left?0:style->borderLeftWidth(),
+ ignore_right?0:style->borderRightWidth());
+ }
+
+ if(render_l)
+ {
+ bool ignore_top =
+ (tc == lc) && (tt == lt) &&
+ (ls >= OUTSET) &&
+ (ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET);
+
+ bool ignore_bottom =
+ (bc == lc) && (bt == lt) &&
+ (ls >= OUTSET) &&
+ (bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET);
+
+ drawBorder(p, _tx, _ty, _tx + style->borderLeftWidth(), _ty + h, BSLeft, lc, style->color(), ls,
+ ignore_top?0:style->borderTopWidth(),
+ ignore_bottom?0:style->borderBottomWidth());
+ }
+
+ if(render_r)
+ {
+ bool ignore_top =
+ (tc == rc) && (tt == rt) &&
+ (rs >= DOTTED || rs == INSET) &&
+ (ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET);
+
+ bool ignore_bottom =
+ (bc == rc) && (bt == rt) &&
+ (rs >= DOTTED || rs == INSET) &&
+ (bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET);
+
+ drawBorder(p, _tx + w - style->borderRightWidth(), _ty, _tx + w, _ty + h, BSRight, rc, style->color(), rs,
+ ignore_top?0:style->borderTopWidth(),
+ ignore_bottom?0:style->borderBottomWidth());
+ }
+}
+
+void RenderObject::paintOutline(QPainter *p, int _tx, int _ty, int w, int h, const RenderStyle* style)
+{
+ int ow = style->outlineWidth();
+ if(!ow) return;
+
+ const QColor& oc = style->outlineColor();
+ EBorderStyle os = style->outlineStyle();
+ int offset = style->outlineOffset();
+
+#ifdef APPLE_CHANGES
+ if (style->outlineStyleIsAuto()) {
+ p->initFocusRing(ow, offset, oc);
+ addFocusRingRects(p, _tx, _ty);
+ p->drawFocusRing();
+ p->clearFocusRing();
+ return;
+ }
+#endif
+
+ _tx -= offset;
+ _ty -= offset;
+ w += 2*offset;
+ h += 2*offset;
+
+ drawBorder(p, _tx-ow, _ty-ow, _tx, _ty+h+ow, BSLeft,
+ QColor(oc), style->color(),
+ os, ow, ow, true);
+
+ drawBorder(p, _tx-ow, _ty-ow, _tx+w+ow, _ty, BSTop,
+ QColor(oc), style->color(),
+ os, ow, ow, true);
+
+ drawBorder(p, _tx+w, _ty-ow, _tx+w+ow, _ty+h+ow, BSRight,
+ QColor(oc), style->color(),
+ os, ow, ow, true);
+
+ drawBorder(p, _tx-ow, _ty+h, _tx+w+ow, _ty+h+ow, BSBottom,
+ QColor(oc), style->color(),
+ os, ow, ow, true);
+
+}
+
+void RenderObject::paint( PaintInfo&, int /*tx*/, int /*ty*/)
+{
+}
+
+void RenderObject::repaintRectangle(int x, int y, int w, int h, Priority p, bool f)
+{
+ if(parent()) parent()->repaintRectangle(x, y, w, h, p, f);
+}
+
+#ifdef ENABLE_DUMP
+
+QString RenderObject::information() const
+{
+ QString str;
+ int x; int y;
+ absolutePosition(x,y);
+ x += inlineXPos();
+ y += inlineYPos();
+ QTextStream ts( &str, IO_WriteOnly );
+ ts << renderName()
+ << "(" << (style() ? style()->refCount() : 0) << ")"
+ << ": " << (void*)this << " ";
+ ts << "{" << x << " " << y << "} ";
+ if (isInline()) ts << "il ";
+ if (childrenInline()) ts << "ci ";
+ if (isFloating()) ts << "fl ";
+ if (isAnonymous()) ts << "an ";
+ if (isRelPositioned()) ts << "rp ";
+ if (isPositioned()) ts << "ps ";
+ if (isReplaced()) ts << "rp ";
+ if (overhangingContents()) ts << "oc ";
+ if (needsLayout()) ts << "nl ";
+ if (minMaxKnown()) ts << "mmk ";
+ if (m_recalcMinMax) ts << "rmm ";
+ if (mouseInside()) ts << "mi ";
+ if (style() && style()->zIndex()) ts << "zI: " << style()->zIndex();
+ if (style() && style()->hasAutoZIndex()) ts << "zI: auto ";
+ if (element()) {
+ if (element()->active()) ts << "act ";
+ if (element()->hasAnchor()) ts << "anchor ";
+ if (element()->focused()) ts << "focus ";
+ ts << " <" << getTagName(element()->id()) << ">";
+
+ } else if (isPseudoAnonymous() && style() && style()->styleType() != RenderStyle::NOPSEUDO) {
+ ts << " <" << getTagName(node()->id());
+ QString pseudo;
+ switch (style()->styleType()) {
+ case RenderStyle::FIRST_LETTER:
+ pseudo = ":first-letter"; break;
+ case RenderStyle::BEFORE:
+ pseudo = ":before"; break;
+ case RenderStyle::AFTER:
+ pseudo = ":after"; break;
+ default:
+ pseudo = ":pseudo-element";
+ }
+ ts << pseudo;
+ ts << ">";
+ }
+ ts << " (" << xPos() << "," << yPos() << "," << width() << "," << height() << ")"
+ << " [" << minWidth() << "-" << maxWidth() << "]"
+ << " { mT: " << marginTop() << " qT: " << isTopMarginQuirk()
+ << " mB: " << marginBottom() << " qB: " << isBottomMarginQuirk()
+ << "}"
+ << (isTableCell() ?
+ ( QString::fromLatin1(" [r=") +
+ QString::number( static_cast<const RenderTableCell *>(this)->row() ) +
+ QString::fromLatin1(" c=") +
+ QString::number( static_cast<const RenderTableCell *>(this)->col() ) +
+ QString::fromLatin1(" rs=") +
+ QString::number( static_cast<const RenderTableCell *>(this)->rowSpan() ) +
+ QString::fromLatin1(" cs=") +
+ QString::number( static_cast<const RenderTableCell *>(this)->colSpan() ) +
+ QString::fromLatin1("]") ) : QString::null );
+ if ( layer() )
+ ts << " layer=" << layer();
+ if ( continuation() )
+ ts << " continuation=" << continuation();
+ if (isText())
+ ts << " \"" << QConstString(static_cast<const RenderText *>(this)->text(), kMin(static_cast<const RenderText *>(this)->length(), 10u)).string() << "\"";
+ return str;
+}
+
+void RenderObject::printTree(int indent) const
+{
+ QString ind;
+ ind.fill(' ', indent);
+
+ kdDebug() << ind << information() << endl;
+
+ RenderObject *child = firstChild();
+ while( child != 0 )
+ {
+ child->printTree(indent+2);
+ child = child->nextSibling();
+ }
+}
+
+static QTextStream &operator<<(QTextStream &ts, const QRect &r)
+{
+ return ts << "at (" << r.x() << "," << r.y() << ") size " << r.width() << "x" << r.height();
+}
+
+//A bit like getTagName, but handles XML, too.
+static QString lookupTagName(NodeImpl* node) {
+ return node->getDocument()->getName(NodeImpl::ElementId, node->id()).string();
+}
+
+void RenderObject::dump(QTextStream &ts, const QString &ind) const
+{
+ if ( !layer() )
+ ts << endl;
+
+ ts << ind << renderName();
+
+ if (style() && style()->zIndex()) {
+ ts << " zI: " << style()->zIndex();
+ }
+
+ if (element()) {
+ QString tagName(lookupTagName(element()));
+ if (!tagName.isEmpty()) {
+ ts << " {" << tagName << "}";
+ }
+ } else if (isPseudoAnonymous() && style() && style()->styleType() != RenderStyle::NOPSEUDO) {
+ QString pseudo;
+ QString tagName(lookupTagName(node()));
+ switch (style()->styleType()) {
+ case RenderStyle::FIRST_LETTER:
+ pseudo = ":first-letter"; break;
+ case RenderStyle::BEFORE:
+ pseudo = ":before"; break;
+ case RenderStyle::AFTER:
+ pseudo = ":after"; break;
+ default:
+ pseudo = ":pseudo-element";
+ }
+ ts << " {" << tagName << pseudo << "}";
+ }
+
+ QRect r(xPos(), yPos(), width(), height());
+ ts << " " << r;
+
+ if ( parent() )
+ ts << style()->createDiff( *parent()->style() );
+
+ if (isAnonymous()) { ts << " anonymousBox"; }
+ if (isFloating()) { ts << " floating"; }
+ if (isPositioned()) { ts << " positioned"; }
+ if (isRelPositioned()) { ts << " relPositioned"; }
+ if (isText()) { ts << " text"; }
+ if (isInline()) { ts << " inline"; }
+ if (isReplaced()) { ts << " replaced"; }
+ if (shouldPaintBackgroundOrBorder()) { ts << " paintBackground"; }
+ if (needsLayout()) { ts << " needsLayout"; }
+ if (minMaxKnown()) { ts << " minMaxKnown"; }
+ if (overhangingContents()) { ts << " overhangingContents"; }
+ if (hasFirstLine()) { ts << " hasFirstLine"; }
+ if (afterPageBreak()) { ts << " afterPageBreak"; }
+}
+#endif
+
+void RenderObject::selectionStartEnd(int& spos, int& epos)
+{
+ if (parent())
+ parent()->selectionStartEnd(spos, epos);
+}
+
+void RenderObject::setStyle(RenderStyle *style)
+{
+ if (m_style == style)
+ return;
+
+ RenderStyle::Diff d = m_style ? m_style->diff( style ) : RenderStyle::Layout;
+ //qDebug("m_style: %p new style, diff=%d", m_style, d);
+
+ Priority pri = NormalPriority;
+ if (m_style) {
+ pri = HighPriority;
+ if ( d >= RenderStyle::Visible && !isText() && m_parent &&
+ ( d == RenderStyle::Position ||
+ m_style->outlineWidth() > style->outlineWidth() ||
+ (!m_style->hidesOverflow() && style->hidesOverflow()) ||
+ ( m_style->hasClip() && !(m_style->clip() == style->clip()) ) ) ) {
+ // schedule a repaint with the old style
+ if (layer() && !isInlineFlow())
+ layer()->repaint(pri);
+ else
+ repaint(pri);
+ }
+
+ if ( ( isFloating() && m_style->floating() != style->floating() ) ||
+ ( isPositioned() && m_style->position() != style->position() &&
+ style->position() != ABSOLUTE && style->position() != FIXED ) )
+ removeFromObjectLists();
+
+ if ( layer() ) {
+ if ( ( m_style->hasAutoZIndex() != style->hasAutoZIndex() ||
+ m_style->zIndex() != style->zIndex() ||
+ m_style->visibility() != style->visibility() ) ) {
+ layer()->stackingContext()->dirtyZOrderLists();
+ layer()->dirtyZOrderLists();
+ }
+ }
+
+ // reset style flags
+ m_floating = false;
+ m_positioned = false;
+ m_relPositioned = false;
+ m_paintBackground = false;
+ m_hasOverflowClip = false;
+ }
+
+ // only honour z-index for non-static objects
+ // ### and objects with opacity
+ if ( style->position() == STATIC ) {
+ if ( isRoot() )
+ style->setZIndex( 0 );
+ else
+ style->setHasAutoZIndex();
+ }
+
+ RenderStyle *oldStyle = m_style;
+ m_style = style;
+
+ updateBackgroundImages(oldStyle);
+
+ m_style->ref();
+
+ if (oldStyle)
+ oldStyle->deref();
+
+ setShouldPaintBackgroundOrBorder(m_style->hasBorder() || m_style->hasBackground());
+
+ m_hasFirstLine = (style->getPseudoStyle(RenderStyle::FIRST_LINE) != 0);
+ if (m_parent) {
+ if (d == RenderStyle::Position && !attemptDirectLayerTranslation())
+ d = RenderStyle::Layout;
+
+ if ( d > RenderStyle::Position) {
+ // we must perform a full layout
+ if (!isText() && d == RenderStyle::CbLayout) {
+ dirtyFormattingContext( true );
+ }
+ setNeedsLayoutAndMinMaxRecalc();
+ } else if (!isText() && d >= RenderStyle::Visible) {
+ // a repaint is enough
+ if (layer()) {
+ if (canvas() && canvas()->needsWidgetMasks()) {
+ // update our widget masks
+ RenderLayer *p, *d = 0;
+ for (p=layer()->parent();p;p=p->parent())
+ if (p->hasOverlaidWidgets()) d=p;
+ if (d) // deepest
+ d->updateWidgetMasks( canvas()->layer() );
+ }
+ }
+ if (layer() && !isInlineFlow())
+ layer()->repaint(pri);
+ else
+ repaint(pri);
+ }
+ }
+}
+
+bool RenderObject::attemptDirectLayerTranslation()
+{
+ // When the difference between two successive styles is only 'Position'
+ // we may attempt to save a layout by directly updating the object position.
+
+ KHTMLAssert( m_style->position() != STATIC );
+ if (!layer())
+ return false;
+ setInline(m_style->isDisplayInlineType());
+ setPositioned(m_style->position() != RELATIVE);
+ setRelPositioned(m_style->position() == RELATIVE);
+ int oldXPos = xPos();
+ int oldYPos = yPos();
+ int oldWidth = width();
+ int oldHeight = height();
+ calcWidth();
+ calcHeight();
+ if (oldWidth != width() || oldHeight != height()) {
+ // implicit size change or overconstrained dimensions:
+ // we'll need a layout.
+ setWidth(oldWidth);
+ setHeight(oldHeight);
+ // kdDebug() << "Layer translation failed for " << information() << endl;
+ return false;
+ }
+ layer()->updateLayerPosition();
+ if (m_style->position() != FIXED) {
+ bool needsDocSizeUpdate = true;
+ RenderObject *cb = container();
+ while (cb) {
+ if (cb->hasOverflowClip() && cb->layer()) {
+ cb->layer()->checkScrollbarsAfterLayout();
+ needsDocSizeUpdate = false;
+ break;
+ }
+ cb = cb->container();
+ }
+ if (needsDocSizeUpdate && canvas()) {
+ bool posXOffset = (xPos()-oldXPos >= 0);
+ bool posYOffset = (yPos()-oldYPos >= 0);
+ canvas()->updateDocSizeAfterLayerTranslation(this, posXOffset, posYOffset);
+ }
+ }
+ // success
+ return true;
+}
+
+void RenderObject::dirtyFormattingContext( bool checkContainer )
+{
+ if (m_markedForRepaint && !checkContainer)
+ return;
+ m_markedForRepaint = true;
+ if (layer() && (style()->position() == FIXED || style()->position() == ABSOLUTE))
+ return;
+ if (m_parent && (checkContainer || style()->width().isVariable() || style()->height().isVariable() ||
+ !(isFloating() || flowAroundFloats() || isTableCell())))
+ m_parent->dirtyFormattingContext(false);
+}
+
+void RenderObject::repaintDuringLayout()
+{
+ if (canvas()->needsFullRepaint() || isText())
+ return;
+ if (layer() && !isInlineFlow()) {
+ layer()->repaint( NormalPriority, true );
+ } else {
+ repaint();
+ canvas()->deferredRepaint( this );
+ }
+}
+
+void RenderObject::setOverhangingContents(bool p)
+{
+ if (m_overhangingContents == p)
+ return;
+
+ RenderBlock *cb = containingBlock();
+ if (p)
+ {
+ m_overhangingContents = true;
+ KHTMLAssert( cb != this || isCanvas());
+ if (cb && cb != this)
+ cb->setOverhangingContents();
+ }
+ else
+ {
+ RenderObject *n;
+ bool c=false;
+
+ for( n = firstChild(); n != 0; n = n->nextSibling() )
+ {
+ if (n->isPositioned() || n->overhangingContents())
+ c=true;
+ }
+
+ if (c)
+ return;
+ else
+ {
+ m_overhangingContents = false;
+ KHTMLAssert( cb != this );
+ if (cb && cb != this)
+ cb->setOverhangingContents(false);
+ }
+ }
+}
+
+void RenderObject::updateBackgroundImages(RenderStyle* oldStyle)
+{
+ // FIXME: This will be slow when a large number of images is used. Fix by using a dict.
+ const BackgroundLayer* oldLayers = oldStyle ? oldStyle->backgroundLayers() : 0;
+ const BackgroundLayer* newLayers = m_style ? m_style->backgroundLayers() : 0;
+ for (const BackgroundLayer* currOld = oldLayers; currOld; currOld = currOld->next()) {
+ if (currOld->backgroundImage() && (!newLayers || !newLayers->containsImage(currOld->backgroundImage())))
+ currOld->backgroundImage()->deref(this);
+ }
+ for (const BackgroundLayer* currNew = newLayers; currNew; currNew = currNew->next()) {
+ if (currNew->backgroundImage() && (!oldLayers || !oldLayers->containsImage(currNew->backgroundImage())))
+ currNew->backgroundImage()->ref(this);
+ }
+}
+
+QRect RenderObject::viewRect() const
+{
+ return containingBlock()->viewRect();
+}
+
+bool RenderObject::absolutePosition(int &xPos, int &yPos, bool f) const
+{
+ RenderObject* p = parent();
+ if (p) {
+ p->absolutePosition(xPos, yPos, f);
+ if ( p->hasOverflowClip() )
+ p->layer()->subtractScrollOffset( xPos, yPos );
+ return true;
+ }
+ else
+ {
+ xPos = yPos = 0;
+ return false;
+ }
+}
+
+void RenderObject::caretPos(int /*offset*/, int /*flags*/, int &_x, int &_y, int &width, int &height)
+{
+ _x = _y = height = -1;
+ width = 1; // the caret has a default width of one pixel. If you want
+ // to check for validity, only test the x-coordinate for >= 0.
+}
+
+int RenderObject::paddingTop() const
+{
+ int w = 0;
+ Length padding = m_style->paddingTop();
+ if (padding.isPercent())
+ w = containingBlock()->contentWidth();
+ w = padding.minWidth(w);
+ if ( isTableCell() && padding.isVariable() )
+ w = static_cast<const RenderTableCell *>(this)->table()->cellPadding();
+ return w;
+}
+
+int RenderObject::paddingBottom() const
+{
+ int w = 0;
+ Length padding = style()->paddingBottom();
+ if (padding.isPercent())
+ w = containingBlock()->contentWidth();
+ w = padding.minWidth(w);
+ if ( isTableCell() && padding.isVariable() )
+ w = static_cast<const RenderTableCell *>(this)->table()->cellPadding();
+ return w;
+}
+
+int RenderObject::paddingLeft() const
+{
+ int w = 0;
+ Length padding = style()->paddingLeft();
+ if (padding.isPercent())
+ w = containingBlock()->contentWidth();
+ w = padding.minWidth(w);
+ if ( isTableCell() && padding.isVariable() )
+ w = static_cast<const RenderTableCell *>(this)->table()->cellPadding();
+ return w;
+}
+
+int RenderObject::paddingRight() const
+{
+ int w = 0;
+ Length padding = style()->paddingRight();
+ if (padding.isPercent())
+ w = containingBlock()->contentWidth();
+ w = padding.minWidth(w);
+ if ( isTableCell() && padding.isVariable() )
+ w = static_cast<const RenderTableCell *>(this)->table()->cellPadding();
+ return w;
+}
+
+RenderObject *RenderObject::container() const
+{
+ // This method is extremely similar to containingBlock(), but with a few notable
+ // exceptions.
+ // (1) It can be used on orphaned subtrees, i.e., it can be called safely even when
+ // the object is not part of the primary document subtree yet.
+ // (2) For normal flow elements, it just returns the parent.
+ // (3) For absolute positioned elements, it will return a relative positioned inline.
+ // containingBlock() simply skips relpositioned inlines and lets an enclosing block handle
+ // the layout of the positioned object. This does mean that calcAbsoluteHorizontal and
+ // calcAbsoluteVertical have to use container().
+ EPosition pos = m_style->position();
+ RenderObject *o = 0;
+ if( pos == FIXED ) {
+ // container() can be called on an object that is not in the
+ // tree yet. We don't call canvas() since it will assert if it
+ // can't get back to the canvas. Instead we just walk as high up
+ // as we can. If we're in the tree, we'll get the root. If we
+ // aren't we'll get the root of our little subtree (most likely
+ // we'll just return 0).
+ o = parent();
+ while ( o && o->parent() ) o = o->parent();
+ }
+ else if ( pos == ABSOLUTE ) {
+ // Same goes here. We technically just want our containing block, but
+ // we may not have one if we're part of an uninstalled subtree. We'll
+ // climb as high as we can though.
+ o = parent();
+ while (o && o->style()->position() == STATIC && !o->isCanvas())
+ o = o->parent();
+ }
+ else
+ o = parent();
+ return o;
+}
+
+DOM::DocumentImpl* RenderObject::document() const
+{
+ return m_node->getDocument();
+}
+
+void RenderObject::remove()
+{
+ if ( parent() )
+ //have parent, take care of the tree integrity
+ parent()->removeChild(this);
+}
+
+void RenderObject::removeFromObjectLists()
+{
+ // in destruction mode, don't care.
+ if ( !document()->renderer() ) return;
+
+ if (isFloating()) {
+ RenderBlock* outermostBlock = containingBlock();
+ for (RenderBlock* p = outermostBlock; p && !p->isCanvas() && p->containsFloat(this);) {
+ outermostBlock = p;
+ if (p->isFloatingOrPositioned())
+ break;
+ p = p->containingBlock();
+ }
+
+ if (outermostBlock)
+ outermostBlock->markAllDescendantsWithFloatsForLayout(this);
+ }
+
+ if (isPositioned()) {
+ RenderObject *p;
+ for (p = parent(); p; p = p->parent()) {
+ if (p->isRenderBlock())
+ static_cast<RenderBlock*>(p)->removePositionedObject(this);
+ }
+ }
+}
+
+RenderArena* RenderObject::renderArena() const
+{
+ return m_node->getDocument()->renderArena();
+}
+
+void RenderObject::detach()
+{
+ detachCounters();
+
+ deleteInlineBoxes();
+ remove();
+
+ // make sure our DOM-node don't think we exist
+ if ( node() && node()->renderer() == this)
+ node()->setRenderer(0);
+
+ // by default no refcounting
+ arenaDelete(renderArena(), this);
+}
+
+void RenderObject::arenaDelete(RenderArena *arena, void *base)
+{
+#ifndef NDEBUG
+ void *savedBase = baseOfRenderObjectBeingDeleted;
+ baseOfRenderObjectBeingDeleted = base;
+#endif
+ delete this;
+#ifndef NDEBUG
+ baseOfRenderObjectBeingDeleted = savedBase;
+#endif
+
+ // Recover the size left there for us by operator delete and free the memory.
+ arena->free(*(size_t *)base, base);
+}
+
+void RenderObject::arenaDelete(RenderArena *arena)
+{
+ // static_cast unfortunately doesn't work, since we multiple inherit
+ // in eg. RenderWidget.
+ arenaDelete(arena, dynamic_cast<void *>(this));
+}
+
+FindSelectionResult RenderObject::checkSelectionPoint( int _x, int _y, int _tx, int _ty, DOM::NodeImpl*& node, int & offset, SelPointState &state )
+{
+#if 0
+ NodeInfo info(true, false);
+ if ( nodeAtPoint( info, _x, _y, _tx, _ty ) && info.innerNode() )
+ {
+ RenderObject* r = info.innerNode()->renderer();
+ if ( r ) {
+ if ( r == this ) {
+ node = info.innerNode();
+ offset = 0; // we have no text...
+ return SelectionPointInside;
+ }
+ else
+ return r->checkSelectionPoint( _x, _y, _tx, _ty, node, offset, state );
+ }
+ }
+ //kdDebug(6030) << "nodeAtPoint Failed. Fallback - hmm, SelectionPointAfter" << endl;
+ node = 0;
+ offset = 0;
+ return SelectionPointAfter;
+#endif
+ int off = offset;
+ DOM::NodeImpl* nod = node;
+
+ for (RenderObject *child = firstChild(); child; child=child->nextSibling()) {
+ // ignore empty text boxes, they produce totally bogus information
+ // for caret navigation (LS)
+ if (child->isText() && !static_cast<RenderText *>(child)->inlineTextBoxCount())
+ continue;
+
+// kdDebug(6040) << "iterating " << (child ? child->renderName() : "") << "@" << child << (child->isText() ? " contains: \"" + QConstString(static_cast<RenderText *>(child)->text(), kMin(static_cast<RenderText *>(child)->length(), 10u)).string() + "\"" : QString::null) << endl;
+// kdDebug(6040) << "---------- checkSelectionPoint recursive -----------" << endl;
+ khtml::FindSelectionResult pos = child->checkSelectionPoint(_x, _y, _tx+xPos(), _ty+yPos(), nod, off, state);
+// kdDebug(6040) << "-------- end checkSelectionPoint recursive ---------" << endl;
+// kdDebug(6030) << this << " child->findSelectionNode returned result=" << pos << " nod=" << nod << " off=" << off << endl;
+ switch(pos) {
+ case SelectionPointBeforeInLine:
+ case SelectionPointInside:
+ //kdDebug(6030) << "RenderObject::checkSelectionPoint " << this << " returning SelectionPointInside offset=" << offset << endl;
+ node = nod;
+ offset = off;
+ return SelectionPointInside;
+ case SelectionPointBefore:
+ //x,y is before this element -> stop here
+ if ( state.m_lastNode ) {
+ node = state.m_lastNode;
+ offset = state.m_lastOffset;
+ //kdDebug(6030) << "RenderObject::checkSelectionPoint " << this << " before this child "
+ // << node << "-> returning SelectionPointInside, offset=" << offset << endl;
+ return SelectionPointInside;
+ } else {
+ node = nod;
+ offset = off;
+ //kdDebug(6030) << "RenderObject::checkSelectionPoint " << this << " before us -> returning SelectionPointBefore " << node << "/" << offset << endl;
+ return SelectionPointBefore;
+ }
+ break;
+ case SelectionPointAfter:
+ if (state.m_afterInLine) break;
+ // fall through
+ case SelectionPointAfterInLine:
+ if (pos == SelectionPointAfterInLine) state.m_afterInLine = true;
+ //kdDebug(6030) << "RenderObject::checkSelectionPoint: selection after: " << nod << " offset: " << off << " afterInLine: " << state.m_afterInLine << endl;
+ state.m_lastNode = nod;
+ state.m_lastOffset = off;
+ // No "return" here, obviously. We must keep looking into the children.
+ break;
+ }
+ }
+ // If we are after the last child, return lastNode/lastOffset
+ // But lastNode can be 0L if there is no child, for instance.
+ if ( state.m_lastNode )
+ {
+ node = state.m_lastNode;
+ offset = state.m_lastOffset;
+ }
+ //kdDebug(6030) << "fallback - SelectionPointAfter node=" << node << " offset=" << offset << endl;
+ return SelectionPointAfter;
+}
+
+bool RenderObject::mouseInside() const
+{
+ if (!m_mouseInside && continuation())
+ return continuation()->mouseInside();
+ return m_mouseInside;
+}
+
+bool RenderObject::nodeAtPoint(NodeInfo& info, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction, bool inside)
+{
+ int tx = _tx + xPos();
+ int ty = _ty + yPos();
+
+ inside |= ( style()->visibility() != HIDDEN &&
+ (_y >= ty) && (_y < ty + height()) && (_x >= tx) && (_x < tx + width())) || isRoot() || isBody();
+ bool inOverflowRect = inside;
+ if ( !inOverflowRect ) {
+ int ol = overflowLeft();
+ int ot = overflowTop();
+ QRect overflowRect( tx+ol, ty+ot, overflowWidth()-ol, overflowHeight()-ot );
+ inOverflowRect = overflowRect.contains( _x, _y );
+ }
+
+ // ### table should have its own, more performant method
+ if (hitTestAction != HitTestSelfOnly &&
+ (( !isRenderBlock() ||
+ !static_cast<RenderBlock*>( this )->isPointInScrollbar( _x, _y, _tx, _ty )) &&
+ (overhangingContents() || inOverflowRect || isInline() || isRoot() || isCanvas() ||
+ isTableRow() || isTableSection() || inside || mouseInside() ))) {
+ if ( hitTestAction == HitTestChildrenOnly )
+ inside = false;
+ if ( hasOverflowClip() && layer() )
+ layer()->subtractScrollOffset(tx, ty);
+ for (RenderObject* child = lastChild(); child; child = child->previousSibling())
+ if (!child->layer() && child->nodeAtPoint(info, _x, _y, tx, ty, HitTestAll))
+ inside = true;
+ }
+
+ if (inside)
+ setInnerNode(info);
+
+ return inside;
+}
+
+
+void RenderObject::setInnerNode(NodeInfo& info)
+{
+ if (!info.innerNode() && !isInline() && continuation()) {
+ // We are in the margins of block elements that are part of a continuation. In
+ // this case we're actually still inside the enclosing inline element that was
+ // split. Go ahead and set our inner node accordingly.
+ info.setInnerNode(continuation()->element());
+ if (!info.innerNonSharedNode())
+ info.setInnerNonSharedNode(continuation()->element());
+ }
+
+ if (!info.innerNode() && element())
+ info.setInnerNode(element());
+
+ if(!info.innerNonSharedNode() && element())
+ info.setInnerNonSharedNode(element());
+}
+
+
+short RenderObject::verticalPositionHint( bool firstLine ) const
+{
+ short vpos = m_verticalPosition;
+ if ( m_verticalPosition == PositionUndefined || firstLine ) {
+ vpos = getVerticalPosition( firstLine );
+ if ( !firstLine )
+ const_cast<RenderObject *>(this)->m_verticalPosition = vpos;
+ }
+ return vpos;
+
+}
+
+short RenderObject::getVerticalPosition( bool firstLine, RenderObject* ref ) const
+{
+ // vertical align for table cells has a different meaning
+ int vpos = 0;
+ if ( !isTableCell() && isInline() ) {
+ EVerticalAlign va = style()->verticalAlign();
+ if ( va == TOP ) {
+ vpos = PositionTop;
+ } else if ( va == BOTTOM ) {
+ vpos = PositionBottom;
+ } else {
+ if (!ref) ref = parent();
+ bool checkParent = ref->isInline() && !ref->isReplacedBlock() &&
+ !( ref->style()->verticalAlign() == TOP || ref->style()->verticalAlign() == BOTTOM );
+ vpos = checkParent ? ref->verticalPositionHint( firstLine ) : 0;
+ // don't allow elements nested inside text-top to have a different valignment.
+ if ( va == BASELINE )
+ return vpos;
+ else if ( va == LENGTH )
+ return vpos - style()->verticalAlignLength().width( lineHeight( firstLine ) );
+
+ const QFont &f = ref->font( firstLine );
+ int fontsize = f.pixelSize();
+
+ if ( va == SUB )
+ vpos += fontsize/5 + 1;
+ else if ( va == SUPER )
+ vpos -= fontsize/3 + 1;
+ else if ( va == TEXT_TOP ) {
+ vpos += baselinePosition( firstLine ) - (QFontMetrics(f).ascent() + QFontMetrics(f).leading()/2);
+ } else if ( va == MIDDLE ) {
+ QRect b = QFontMetrics(f).boundingRect('x');
+ vpos += -b.height()/2 - lineHeight( firstLine )/2 + baselinePosition( firstLine );
+ } else if ( va == TEXT_BOTTOM ) {
+ vpos += QFontMetrics(f).descent() + QFontMetrics(f).leading()/2;
+ if ( !isReplaced() )
+ vpos -= fontMetrics(firstLine).descent();
+ } else if ( va == BASELINE_MIDDLE )
+ vpos += - lineHeight( firstLine )/2 + baselinePosition( firstLine );
+ }
+ }
+ return vpos;
+}
+
+short RenderObject::lineHeight( bool firstLine ) const
+{
+ // Inline blocks are replaced elements. Otherwise, just pass off to
+ // the base class. If we're being queried as though we're the root line
+ // box, then the fact that we're an inline-block is irrelevant, and we behave
+ // just like a block.
+
+ if (isReplaced() && (!isInlineBlockOrInlineTable() || !needsLayout()))
+ return height()+marginTop()+marginBottom();
+
+ Length lh;
+ if( firstLine && hasFirstLine() ) {
+ RenderStyle *pseudoStyle = style()->getPseudoStyle(RenderStyle::FIRST_LINE);
+ if ( pseudoStyle )
+ lh = pseudoStyle->lineHeight();
+ }
+ else
+ lh = style()->lineHeight();
+
+ // its "unset", choose nice default
+ if ( lh.value() < 0 )
+ return style()->fontMetrics().lineSpacing();
+
+ if ( lh.isPercent() )
+ return lh.minWidth( style()->font().pixelSize() );
+
+ // its fixed
+ return lh.value();
+}
+
+short RenderObject::baselinePosition( bool firstLine ) const
+{
+ // Inline blocks are replaced elements. Otherwise, just pass off to
+ // the base class. If we're being queried as though we're the root line
+ // box, then the fact that we're an inline-block is irrelevant, and we behave
+ // just like a block.
+
+ if (isReplaced() && (!isInlineBlockOrInlineTable() || !needsLayout()))
+ return height()+marginTop()+marginBottom();
+
+ const QFontMetrics &fm = fontMetrics( firstLine );
+ return fm.ascent() + ( lineHeight( firstLine) - fm.height() ) / 2;
+}
+
+void RenderObject::invalidateVerticalPositions()
+{
+ m_verticalPosition = PositionUndefined;
+ RenderObject *child = firstChild();
+ while( child ) {
+ child->invalidateVerticalPositions();
+ child = child->nextSibling();
+ }
+}
+
+void RenderObject::recalcMinMaxWidths()
+{
+ KHTMLAssert( m_recalcMinMax );
+
+#ifdef DEBUG_LAYOUT
+ kdDebug( 6040 ) << renderName() << " recalcMinMaxWidths() this=" << this <<endl;
+#endif
+
+ RenderObject *child = firstChild();
+ int cmin=0;
+ int cmax=0;
+
+ while( child ) {
+ bool test = false;
+ if ( ( m_minMaxKnown && child->m_recalcMinMax ) || !child->m_minMaxKnown ) {
+ cmin = child->minWidth();
+ cmax = child->maxWidth();
+ test = true;
+ }
+ if ( child->m_recalcMinMax )
+ child->recalcMinMaxWidths();
+ if ( !child->m_minMaxKnown )
+ child->calcMinMaxWidth();
+ if ( m_minMaxKnown && test && (cmin != child->minWidth() || cmax != child->maxWidth()) )
+ m_minMaxKnown = false;
+ child = child->nextSibling();
+ }
+
+ // we need to recalculate, if the contains inline children, as the change could have
+ // happened somewhere deep inside the child tree
+ if ( ( !isInline() || isReplacedBlock() ) && childrenInline() )
+ m_minMaxKnown = false;
+
+ if ( !m_minMaxKnown )
+ calcMinMaxWidth();
+ m_recalcMinMax = false;
+}
+
+void RenderObject::scheduleRelayout(RenderObject *clippedObj)
+{
+ if (!isCanvas()) return;
+ KHTMLView *view = static_cast<RenderCanvas *>(this)->view();
+ if ( view )
+ view->scheduleRelayout(clippedObj);
+}
+
+
+void RenderObject::removeLeftoverAnonymousBoxes()
+{
+}
+
+InlineBox* RenderObject::createInlineBox(bool /*makePlaceHolderBox*/, bool /*isRootLineBox*/)
+{
+ KHTMLAssert(false);
+ return 0;
+}
+
+void RenderObject::getTextDecorationColors(int decorations, QColor& underline, QColor& overline,
+ QColor& linethrough, bool quirksMode)
+{
+ RenderObject* curr = this;
+ do {
+ RenderStyle *st = curr->style();
+ int currDecs = st->textDecoration();
+ if (currDecs) {
+ if (currDecs & UNDERLINE) {
+ decorations &= ~UNDERLINE;
+ underline = st->color();
+ }
+ if (currDecs & OVERLINE) {
+ decorations &= ~OVERLINE;
+ overline = st->color();
+ }
+ if (currDecs & LINE_THROUGH) {
+ decorations &= ~LINE_THROUGH;
+ linethrough = st->color();
+ }
+ }
+ curr = curr->parent();
+ if (curr && curr->isRenderBlock() && curr->continuation())
+ curr = curr->continuation();
+ } while (curr && decorations && (!quirksMode || !curr->element() ||
+ (curr->element()->id() != ID_A && curr->element()->id() != ID_FONT)));
+
+ // If we bailed out, use the element we bailed out at (typically a <font> or <a> element).
+ if (decorations && curr) {
+ RenderStyle *st = curr->style();
+ if (decorations & UNDERLINE)
+ underline = st->color();
+ if (decorations & OVERLINE)
+ overline = st->color();
+ if (decorations & LINE_THROUGH)
+ linethrough = st->color();
+ }
+}
+
+int RenderObject::maximalOutlineSize(PaintAction p) const
+{
+ if (p != PaintActionOutline)
+ return 0;
+ return static_cast<RenderCanvas*>(document()->renderer())->maximalOutlineSize();
+}
+
+void RenderObject::collectBorders(QValueList<CollapsedBorderValue>& borderStyles)
+{
+ for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling())
+ curr->collectBorders(borderStyles);
+}
+
+bool RenderObject::flowAroundFloats() const
+{
+ return isReplaced() || hasOverflowClip() || style()->flowAroundFloats();
+}
+
+bool RenderObject::usesLineWidth() const
+{
+ // 1. All auto-width objects that avoid floats should always use lineWidth
+ // 2. For objects with a specified width, we match WinIE's behavior:
+ // (a) tables use contentWidth
+ // (b) <hr>s use lineWidth
+ // (c) all other objects use lineWidth in quirks mode and contentWidth in strict mode.
+ return (flowAroundFloats() && (style()->width().isVariable() || isHR() || (style()->htmlHacks() && !isTable())));
+}
+
+bool RenderObject::hasCounter(const QString& counter) const
+{
+ if (style() && (!isText() || isCounter())) {
+ if (lookupCounter(counter)) return true;
+ if (style()->hasCounterReset(counter)) {
+ return true;
+ }
+ else if (style()->hasCounterIncrement(counter)) {
+ return true;
+ }
+ }
+ if (counter == "list-item") {
+ if (isListItem()) return true;
+ if (element() && (
+ element()->id() == ID_OL ||
+ element()->id() == ID_UL ||
+ element()->id() == ID_MENU ||
+ element()->id() == ID_DIR))
+ return true;
+ } else
+ if (counter == "-khtml-quotes" && isQuote()) {
+ return (static_cast<const RenderQuote*>(this)->quoteCount() != 0);
+ }
+ return false;
+}
+
+CounterNode* RenderObject::getCounter(const QString& counter, bool view, bool counters)
+{
+// kdDebug( 6040 ) << renderName() << " getCounter(" << counter << ")" << endl;
+
+ if (!style()) return 0;
+
+ if (isText() && !isCounter()) return 0;
+
+ CounterNode *i = lookupCounter(counter);
+ if (i) return i;
+ int val = 0;
+
+ if (style()->hasCounterReset(counter) || isRoot()) {
+ i = new CounterReset(this);
+ val = style()->counterReset(counter);
+ if (style()->hasCounterIncrement(counter)) {
+ val += style()->counterIncrement(counter);
+ }
+// kdDebug( 6040 ) << renderName() << " counter-reset: " << counter << " " << val << endl;
+ }
+ else
+ if (style()->hasCounterIncrement(counter)) {
+ i = new CounterNode(this);
+ val = style()->counterIncrement(counter);
+// kdDebug( 6040 ) << renderName() << " counter-increment: " << counter << " " << val << endl;
+ }
+ else if (counter == "list-item") {
+ if (isListItem()) {
+ if (element() && element()->id() == ID_LI) {
+ DOMString v = static_cast<ElementImpl*>(element())->getAttribute(ATTR_VALUE);
+ if ( !v.isEmpty() ) {
+ i = new CounterReset(this);
+ val = v.toInt();
+// kdDebug( 6040 ) << renderName() << " counter-reset: " << counter << " " << val << endl;
+ }
+ }
+ if (!i) {
+ i = new CounterNode(this);
+ val = 1;
+// kdDebug( 6040 ) << renderName() << " counter-increment: " << counter << " " << val << endl;
+ }
+ }
+ else
+ if (element() && element()->id() == ID_OL) {
+ i = new CounterReset(this);
+ DOMString v = static_cast<ElementImpl*>(element())->getAttribute(ATTR_START);
+ if ( !v.isEmpty() )
+ val = v.toInt()-1;
+ else
+ val = 0;
+// kdDebug( 6040 ) << renderName() << " counter-reset: " << counter << " " << val << endl;
+ }
+ else
+ if (element() &&
+ (element()->id() == ID_UL ||
+ element()->id() == ID_MENU||
+ element()->id() == ID_DIR))
+ {
+ i = new CounterReset(this);
+ val = 0;
+// kdDebug( 6040 ) << renderName() << " counter-reset: " << counter << " " << val << endl;
+ }
+ }
+ else if (counter == "-khtml-quotes" && isQuote()) {
+ i = new CounterNode(this);
+ val = static_cast<RenderQuote*>(this)->quoteCount();
+ }
+
+ if (!i) {
+ i = new CounterNode(this);
+ val = 0;
+// kdDebug( 6040 ) << renderName() << " counter-increment: " << counter << " " << val << endl;
+ }
+ i->setValue(val);
+ if (view) i->setIsVisual();
+ if (counters) i->setHasCounters();
+
+ insertCounter(counter, i);
+
+ if (!isRoot()) {
+ CounterNode *last=0, *current=0;
+ RenderObject *n = previousSibling();
+ while(n) {
+ if (n->hasCounter(counter)) {
+ current = n->getCounter(counter);
+ break;
+ }
+ else
+ n = n->previousSibling();
+ }
+ last = current;
+
+ CounterNode *sibling = current;
+ // counter-reset on same render-level is our counter-parent
+ if (last) {
+ // Found render-sibling, now search for later counter-siblings among its render-children
+ n = n->lastChild();
+ while (n) {
+ if (n->hasCounter(counter)) {
+ current = n->getCounter(counter);
+ if (last->parent() == current->parent() || sibling == current->parent()) {
+ last = current;
+ // If the current counter is not the last, search deeper
+ if (current->nextSibling()) {
+ n = n->lastChild();
+ continue;
+ }
+ else
+ break;
+ }
+ }
+ n = n->previousSibling();
+ }
+ if (sibling->isReset())
+ {
+ if (last != sibling)
+ sibling->insertAfter(i, last);
+ else
+ sibling->insertAfter(i, 0);
+ }
+ else if (last->parent())
+ last->parent()->insertAfter(i, last);
+ }
+ else if (parent()) {
+ // Nothing found among siblings, let our parent search
+ last = parent()->getCounter(counter, false);
+ if (last->isReset())
+ last->insertAfter(i, 0);
+ else if (last->parent())
+ last->parent()->insertAfter(i, last);
+ }
+ }
+
+ return i;
+}
+
+CounterNode* RenderObject::lookupCounter(const QString& counter) const
+{
+ QDict<khtml::CounterNode>* counters = document()->counters(this);
+ if (counters)
+ return counters->find(counter);
+ else
+ return 0;
+}
+
+void RenderObject::detachCounters()
+{
+ QDict<khtml::CounterNode>* counters = document()->counters(this);
+ if (!counters) return;
+
+ QDictIterator<khtml::CounterNode> i(*counters);
+
+ while (i.current()) {
+ (*i)->remove();
+ delete (*i);
+ ++i;
+ }
+ document()->removeCounters(this);
+}
+
+void RenderObject::insertCounter(const QString& counter, CounterNode* val)
+{
+ QDict<khtml::CounterNode>* counters = document()->counters(this);
+
+ if (!counters) {
+ counters = new QDict<khtml::CounterNode>(11);
+ document()->setCounters(this, counters);
+ }
+
+ counters->insert(counter, val);
+}
+
+void RenderObject::updateWidgetMasks() {
+ for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
+ if ( curr->isWidget() && static_cast<RenderWidget*>(curr)->needsMask() ) {
+ QWidget* w = static_cast<RenderWidget*>(curr)->widget();
+ if (!w)
+ return;
+ RenderLayer* l = curr->enclosingStackingContext();
+ QRegion r = l ? l->getMask() : QRegion();
+ int x,y;
+ if (!r.isNull() && curr->absolutePosition(x,y)) {
+ int pbx = curr->borderLeft()+curr->paddingLeft();
+ int pby = curr->borderTop()+curr->paddingTop();
+ x+= pbx;
+ y+= pby;
+ r = r.intersect(QRect(x,y,
+ curr->width()-pbx-curr->borderRight()-curr->paddingRight(),
+ curr->height()-pby-curr->borderBottom()-curr->paddingBottom()));
+#ifdef MASK_DEBUG
+ QMemArray<QRect> ar = r.rects();
+ kdDebug(6040) << "|| Setting widget mask for " << curr->information() << endl;
+ for (int i = 0; i < ar.size() ; ++i) {
+ kdDebug(6040) << " " << ar[i] << endl;
+ }
+#endif
+ r.translate(-x,-y);
+ w->setMask(r);
+ } else {
+ w->clearMask();
+ }
+ }
+ else if (!curr->layer() || !curr->layer()->isStackingContext())
+ curr->updateWidgetMasks();
+
+ }
+}
+
+QRegion RenderObject::visibleFlowRegion(int x, int y) const
+{
+ QRegion r;
+ for (RenderObject* ro=firstChild();ro;ro=ro->nextSibling()) {
+ if( !ro->layer() && !ro->isInlineFlow() && ro->style()->visibility() == VISIBLE) {
+ const RenderStyle *s = ro->style();
+ if (ro->isRelPositioned())
+ static_cast<const RenderBox*>(ro)->relativePositionOffset(x,y);
+ if ( s->backgroundImage() || s->backgroundColor().isValid() || s->hasBorder() )
+ r += QRect(x + ro->effectiveXPos(),y + ro->effectiveYPos(), ro->effectiveWidth(), ro->effectiveHeight());
+ else
+ r += ro->visibleFlowRegion(x+ro->xPos(), y+ro->yPos());
+ }
+ }
+ return r;
+}
+
+#undef RED_LUMINOSITY
+#undef GREEN_LUMINOSITY
+#undef BLUE_LUMINOSITY
+#undef INTENSITY_FACTOR
+#undef LIGHT_FACTOR
+#undef LUMINOSITY_FACTOR
+
+#undef MAX_COLOR
+#undef COLOR_DARK_THRESHOLD
+#undef COLOR_LIGHT_THRESHOLD
+
+#undef COLOR_LITE_BS_FACTOR
+#undef COLOR_LITE_TS_FACTOR
+
+#undef COLOR_DARK_BS_FACTOR
+#undef COLOR_DARK_TS_FACTOR
+
+#undef LIGHT_GRAY
+#undef DARK_GRAY
+
diff --git a/khtml/rendering/render_object.h b/khtml/rendering/render_object.h
new file mode 100644
index 000000000..6b98a5fa4
--- /dev/null
+++ b/khtml/rendering/render_object.h
@@ -0,0 +1,865 @@
+/*
+ * This file is part of the html renderer for KDE.
+ *
+ * Copyright (C) 2000-2003 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000-2003 Dirk Mueller (mueller@kde.org)
+ * (C) 2002-2003 Apple Computer, Inc.
+ * (C) 2004 Allan Sandfeld Jensen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef render_object_h
+#define render_object_h
+
+#include <qcolor.h>
+#include <qrect.h>
+#include <assert.h>
+#include <qvaluelist.h>
+
+#include <kdebug.h>
+#include <kglobal.h>
+
+#include "xml/dom_docimpl.h"
+#include "misc/khtmllayout.h"
+#include "misc/loader_client.h"
+#include "misc/helper.h"
+#include "rendering/render_style.h"
+
+class QPainter;
+class QTextStream;
+class CSSStyle;
+class KHTMLView;
+
+#ifndef NDEBUG
+#define KHTMLAssert( x ) if( !(x) ) { \
+ const RenderObject *o = this; while( o->parent() ) o = o->parent(); \
+ o->printTree(); \
+ qDebug(" this object = %p, %s", (void*) this, kdBacktrace().latin1() ); \
+ assert( x ); \
+}
+#else
+#define KHTMLAssert( x )
+#endif
+
+/*
+ * The painting of a layer occurs in three distinct phases. Each phase involves
+ * a recursive descent into the layer's render objects. The first phase is the background phase.
+ * The backgrounds and borders of all blocks are painted. Inlines are not painted at all.
+ * Floats must paint above block backgrounds but entirely below inline content that can overlap them.
+ * In the foreground phase, all inlines are fully painted. Inline replaced elements will get all
+ * three phases invoked on them during this phase.
+ */
+
+typedef enum {
+ PaintActionElementBackground = 0,
+ PaintActionChildBackground,
+ PaintActionChildBackgrounds,
+ PaintActionFloat,
+ PaintActionForeground,
+ PaintActionOutline,
+ PaintActionSelection,
+ PaintActionCollapsedTableBorders
+} PaintAction;
+
+typedef enum {
+ HitTestAll = 0,
+ HitTestSelfOnly = 1,
+ HitTestChildrenOnly = 2
+} HitTestAction;
+
+typedef enum {
+ PageBreakNormal = 0, // all rules apply
+ PageBreakHarder = 1, // page-break-inside: avoid is ignored
+ PageBreakForced = 2 // page-break-after/before: avoid, orphans and widows ignored
+} PageBreakLevel;
+
+typedef enum {
+ LowPriority = 0,
+ NormalPriority = 1,
+ HighPriority = 2,
+ RealtimePriority = 3
+} Priority;
+
+inline PageBreakLevel operator| (PageBreakLevel a, PageBreakLevel b) {
+ if (a == PageBreakForced || b == PageBreakForced)
+ return PageBreakForced;
+ if (a == PageBreakHarder || b == PageBreakHarder)
+ return PageBreakHarder;
+ return PageBreakNormal;
+}
+
+namespace DOM {
+ class HTMLAreaElementImpl;
+ class DOMString;
+ class NodeImpl;
+ class DocumentImpl;
+ class ElementImpl;
+ class EventImpl;
+}
+
+namespace khtml {
+ class RenderFlow;
+ class RenderStyle;
+ class RenderTable;
+ class CachedObject;
+ class RenderObject;
+ class RenderCanvas;
+ class RenderText;
+ class RenderFrameSet;
+ class RenderArena;
+ class RenderLayer;
+ class RenderBlock;
+ class InlineBox;
+ class InlineFlowBox;
+ class CounterNode;
+
+/**
+ * Base Class for all rendering tree objects.
+ */
+class RenderObject : public CachedObjectClient
+{
+ RenderObject(const RenderObject&);
+ RenderObject& operator=(const RenderObject&);
+public:
+
+ RenderObject(DOM::NodeImpl* node);
+ virtual ~RenderObject();
+
+ RenderObject *parent() const { return m_parent; }
+
+ RenderObject *previousSibling() const { return m_previous; }
+ RenderObject *nextSibling() const { return m_next; }
+
+ virtual RenderObject *firstChild() const { return 0; }
+ virtual RenderObject *lastChild() const { return 0; }
+
+ RenderObject *nextRenderer() const;
+ RenderObject *previousRenderer() const;
+
+ virtual bool childAllowed() const { return false; }
+ virtual int borderTopExtra() const { return 0; }
+ virtual int borderBottomExtra() const { return 0; }
+
+ virtual RenderLayer* layer() const { return 0; }
+ RenderLayer* enclosingLayer() const;
+ RenderLayer* enclosingStackingContext() const;
+ void addLayers(RenderLayer* parentLayer, RenderObject* newObject);
+ void removeLayers(RenderLayer* parentLayer);
+ void moveLayers(RenderLayer* oldParent, RenderLayer* newParent);
+ RenderLayer* findNextLayer(RenderLayer* parentLayer, RenderObject* startPoint,
+ bool checkParent=true);
+ virtual void positionChildLayers() { }
+ virtual bool requiresLayer() const {
+ return isRoot()/* ### */ || isPositioned() || isRelPositioned() || hasOverflowClip();
+ }
+
+ // ### rename to overflowClipRect and clipRect
+ virtual QRect getOverflowClipRect(int /*tx*/, int /*ty*/)
+ { return QRect(0,0,0,0); }
+ virtual QRect getClipRect(int /*tx*/, int /*ty*/) { return QRect(0,0,0,0); }
+ bool hasClip() const { return isPositioned() && style()->hasClip(); }
+ bool hasOverflowClip() const { return m_hasOverflowClip; }
+
+ bool scrollsOverflow() const { return scrollsOverflowX() || scrollsOverflowY(); }
+ bool scrollsOverflowX() const { return hasOverflowClip() && (style()->overflowX() == OSCROLL || style()->overflowX() == OAUTO); }
+ bool scrollsOverflowY() const { return hasOverflowClip() && (style()->overflowY() == OSCROLL || style()->overflowY() == OAUTO); }
+
+ virtual int getBaselineOfFirstLineBox() { return -1; } // Tables and blocks implement this.
+ virtual InlineFlowBox* getFirstLineBox() { return 0; } // Tables and blocks implement this.
+
+ // Whether or not a positioned element requires normal flow x/y to be computed
+ // to determine its position.
+ bool hasStaticX() const;
+ bool hasStaticY() const;
+
+ // Linear tree traversal
+ RenderObject *objectBelow() const;
+ RenderObject *objectAbove() const;
+
+ // Returns if an object has counter-increment or counter-reset
+ bool hasCounter(const QString& counter) const;
+ // Calculates the value of the counter
+ CounterNode* getCounter(const QString& counter, bool view = false, bool counters = false);
+ // Detaches all counterNodes
+ void detachCounters();
+
+
+protected:
+ // Helper functions for counter-cache
+ void insertCounter(const QString& counter, CounterNode* value);
+ CounterNode* lookupCounter(const QString& counter) const;
+
+public:
+ //////////////////////////////////////////
+ // RenderObject tree manipulation
+ virtual void addChild(RenderObject *newChild, RenderObject *beforeChild = 0);
+ void removeChild(RenderObject *oldChild);
+
+ // raw tree manipulation
+ virtual RenderObject* removeChildNode(RenderObject* child);
+ virtual void appendChildNode(RenderObject* child);
+ virtual void insertChildNode(RenderObject* child, RenderObject* before);
+ //////////////////////////////////////////
+
+ //////////////////////////////////////////
+ // Helper functions. Dangerous to use!
+ void setPreviousSibling(RenderObject *previous) { m_previous = previous; }
+ void setNextSibling(RenderObject *next) { m_next = next; }
+ void setParent(RenderObject *parent) { m_parent = parent; }
+ //////////////////////////////////////////
+
+public:
+ virtual const char *renderName() const { return "RenderObject"; }
+#ifdef ENABLE_DUMP
+ QString information() const;
+ virtual void printTree(int indent=0) const;
+ virtual void dump(QTextStream &stream, const QString &ind = QString::null) const;
+#endif
+
+ static RenderObject *createObject(DOM::NodeImpl* node, RenderStyle* style);
+
+ // Overloaded new operator. Derived classes must override operator new
+ // in order to allocate out of the RenderArena.
+ void* operator new(size_t sz, RenderArena* renderArena) throw();
+
+ // Overridden to prevent the normal delete from being called.
+ void operator delete(void* ptr, size_t sz);
+
+private:
+ // The normal operator new is disallowed on all render objects.
+ void* operator new(size_t sz);
+
+public:
+ RenderArena* renderArena() const;
+ virtual RenderFlow* continuation() const { return 0; }
+ virtual bool isInlineContinuation() const { return false; }
+
+
+ bool isRoot() const { return m_isRoot && !m_isAnonymous; }
+ void setIsRoot(bool b) { m_isRoot = b; }
+ bool isHR() const;
+ // some helper functions...
+ virtual bool isRenderBlock() const { return false; }
+ virtual bool isRenderInline() const { return false; }
+ virtual bool isInlineFlow() const { return false; }
+ virtual bool isBlockFlow() const { return false; }
+ virtual bool isInlineBlockOrInlineTable() const { return false; }
+ virtual bool childrenInline() const { return false; }
+ virtual bool isBox() const { return false; }
+ virtual bool isRenderReplaced() const { return false; }
+
+ virtual bool isGlyph() const { return false; }
+ virtual bool isCounter() const { return false; }
+ virtual bool isQuote() const { return false; }
+ virtual bool isListItem() const { return false; }
+ virtual bool isListMarker() const { return false; }
+ virtual bool isCanvas() const { return false; }
+ virtual bool isBR() const { return false; }
+ virtual bool isTableCell() const { return false; }
+ virtual bool isTableRow() const { return false; }
+ virtual bool isTableSection() const { return false; }
+ virtual bool isTableCol() const { return false; }
+ virtual bool isTable() const { return false; }
+ virtual bool isWidget() const { return false; }
+ virtual bool isBody() const { return false; }
+ virtual bool isFormElement() const { return false; }
+ virtual bool isFrameSet() const { return false; }
+ virtual bool isApplet() const { return false; }
+
+ bool isHTMLMarquee() const;
+
+ bool isAnonymous() const { return m_isAnonymous; }
+ void setIsAnonymous(bool b) { m_isAnonymous = b; }
+ bool isAnonymousBlock() const { return isAnonymous() && style()->display() == BLOCK && node()->isDocumentNode(); }
+ bool isPseudoAnonymous() const { return isAnonymous() && !node()->isDocumentNode(); }
+
+ bool isFloating() const { return m_floating; }
+ bool isPositioned() const { return m_positioned; }
+ bool isRelPositioned() const { return m_relPositioned; }
+ bool isText() const { return m_isText; }
+ bool isInline() const { return m_inline; }
+ bool isCompact() const { return style()->display() == COMPACT; } // compact
+ bool isRunIn() const { return style()->display() == RUN_IN; } // run-in object
+ bool mouseInside() const;
+ bool isReplaced() const { return m_replaced; }
+ bool isReplacedBlock() const { return isInline() && isReplaced() && isRenderBlock(); }
+ bool shouldPaintBackgroundOrBorder() const { return m_paintBackground; }
+ bool needsLayout() const { return m_needsLayout || m_normalChildNeedsLayout || m_posChildNeedsLayout; }
+ bool markedForRepaint() const { return m_markedForRepaint; }
+ void setMarkedForRepaint(bool m) { m_markedForRepaint = m; }
+ bool selfNeedsLayout() const { return m_needsLayout; }
+ bool posChildNeedsLayout() const { return m_posChildNeedsLayout; }
+ bool normalChildNeedsLayout() const { return m_normalChildNeedsLayout; }
+ bool minMaxKnown() const{ return m_minMaxKnown; }
+ bool overhangingContents() const { return m_overhangingContents; }
+ bool hasFirstLine() const { return m_hasFirstLine; }
+ bool isSelectionBorder() const { return m_isSelectionBorder; }
+ bool recalcMinMax() const { return m_recalcMinMax; }
+
+ RenderCanvas* canvas() const;
+ // don't even think about making this method virtual!
+ DOM::DocumentImpl* document() const;
+ DOM::NodeImpl* element() const { return isAnonymous() ? 0L : m_node; }
+ DOM::NodeImpl* node() const { return m_node; }
+
+ /**
+ * returns the object containing this one. can be different from parent for
+ * positioned elements
+ */
+ RenderObject *container() const;
+
+ void setOverhangingContents(bool p=true);
+ void markContainingBlocksForLayout();
+ void dirtyFormattingContext( bool checkContainer );
+ void repaintDuringLayout();
+ void setNeedsLayout(bool b, bool markParents = true);
+ void setChildNeedsLayout(bool b, bool markParents = true);
+ void setMinMaxKnown(bool b=true) {
+ m_minMaxKnown = b;
+ if ( !b ) {
+ RenderObject *o = this;
+ RenderObject *root = this;
+ while( o ) { // ### && !o->m_recalcMinMax ) {
+ o->m_recalcMinMax = true;
+ root = o;
+ o = o->m_parent;
+ }
+ }
+ }
+ void setNeedsLayoutAndMinMaxRecalc() {
+ setMinMaxKnown(false);
+ setNeedsLayout(true);
+ }
+ void setPositioned(bool b=true) { m_positioned = b; }
+ void setRelPositioned(bool b=true) { m_relPositioned = b; }
+ void setFloating(bool b=true) { m_floating = b; }
+ void setInline(bool b=true) { m_inline = b; }
+ void setMouseInside(bool b=true) { m_mouseInside = b; }
+ void setShouldPaintBackgroundOrBorder(bool b=true) { m_paintBackground = b; }
+ void setRenderText() { m_isText = true; }
+ void setReplaced(bool b=true) { m_replaced = b; }
+ void setHasOverflowClip(bool b = true) { m_hasOverflowClip = b; }
+ void setIsSelectionBorder(bool b=true) { m_isSelectionBorder = b; }
+
+ void scheduleRelayout(RenderObject *clippedObj = 0);
+
+ void updateBackgroundImages(RenderStyle* oldStyle);
+
+ virtual InlineBox* createInlineBox(bool makePlaceHolderBox, bool isRootLineBox);
+
+ virtual short lineHeight( bool firstLine ) const;
+ virtual short verticalPositionHint( bool firstLine ) const;
+ virtual short baselinePosition( bool firstLine ) const;
+ short getVerticalPosition( bool firstLine, RenderObject* ref=0 ) const;
+
+ /*
+ * Print the object and its children, clipped by (x|y|w|h).
+ * (tx|ty) is the calculated position of the parent
+ */
+ struct PaintInfo {
+ PaintInfo(QPainter* _p, const QRect& _r, PaintAction _phase)
+ : p(_p), r(_r), phase(_phase), outlineObjects(0) {}
+ ~PaintInfo() { delete outlineObjects; }
+ QPainter* p;
+ QRect r;
+ PaintAction phase;
+ QValueList<RenderFlow *>* outlineObjects; // used to list which outlines should be painted by a block with inline children
+ };
+ virtual void paint( PaintInfo& i, int tx, int ty);
+
+ void paintBorder(QPainter *p, int _tx, int _ty, int w, int h, const RenderStyle* style, bool begin=true, bool end=true);
+ void paintOutline(QPainter *p, int _tx, int _ty, int w, int h, const RenderStyle* style);
+
+ virtual void paintBoxDecorations(PaintInfo&, int /*_tx*/, int /*_ty*/) {}
+
+ virtual void paintBackgroundExtended(QPainter* /*p*/, const QColor& /*c*/, const BackgroundLayer */*bgLayer*/,
+ int /*clipy*/, int /*cliph*/, int /*_tx*/, int /*_ty*/,
+ int /*w*/, int /*height*/, int /*bleft*/, int /*bright*/, int /*pleft*/, int /*pright*/ ) {}
+
+
+ /*
+ * This function calculates the minimum & maximum width that the object
+ * can be set to.
+ *
+ * when the Element calls setMinMaxKnown(true), calcMinMaxWidth() will
+ * be no longer called.
+ *
+ * when a element has a fixed size, m_minWidth and m_maxWidth should be
+ * set to the same value. This has the special meaning that m_width,
+ * contains the actual value.
+ *
+ * assumes calcMinMaxWidth has already been called for all children.
+ */
+ virtual void calcMinMaxWidth() { }
+
+ /*
+ * Does the min max width recalculations after changes.
+ */
+ void recalcMinMaxWidths();
+
+ /*
+ * Calculates the actual width of the object (only for non inline
+ * objects)
+ */
+ virtual void calcWidth() {}
+
+ /*
+ * Calculates the actual width of the object (only for non inline
+ * objects)
+ */
+ virtual void calcHeight() {}
+
+ /*
+ * This function should cause the Element to calculate its
+ * width and height and the layout of its content
+ *
+ * when the Element calls setNeedsLayout(false), layout() is no
+ * longer called during relayouts, as long as there is no
+ * style sheet change. When that occurs, m_needsLayout will be
+ * set to true and the Element receives layout() calls
+ * again.
+ */
+ virtual void layout() = 0;
+
+ /* This function performs a layout only if one is needed. */
+ void layoutIfNeeded() { if (needsLayout()) layout(); }
+
+ // used for element state updates that can not be fixed with a
+ // repaint and do not need a relayout
+ virtual void updateFromElement() {}
+
+ // Called immediately after render-object is inserted
+ virtual void attach() { m_attached = true; }
+ bool attached() { return m_attached; }
+ // The corresponding closing element has been parsed. ### remove me
+ virtual void close() { }
+
+ virtual int availableHeight() const { return 0; }
+
+ // Whether or not the element shrinks to its max width (rather than filling the width
+ // of a containing block). HTML4 buttons, legends, and floating/compact elements do this.
+ bool sizesToMaxWidth() const;
+
+ /*
+ * NeesPageClear indicates the object crossed a page-break but could not break itself and now
+ * needs to be moved clear by its parent.
+ */
+ void setNeedsPageClear(bool b = true) { m_needsPageClear = b; }
+ virtual bool needsPageClear() const { return m_needsPageClear; }
+
+ /*
+ * ContainsPageBreak indicates the object contains a clean page-break.
+ * ### should be removed and replaced with (crossesPageBreak && !needsPageClear)
+ */
+ void setContainsPageBreak(bool b = true) { m_containsPageBreak = b; }
+ virtual bool containsPageBreak() const { return m_containsPageBreak; }
+
+ virtual int pageTopAfter(int y) const { if (parent()) return parent()->pageTopAfter(y); else return 0; }
+
+ virtual int crossesPageBreak(int top, int bottom) const
+ { if (parent()) return parent()->crossesPageBreak(top, bottom); else return 0; }
+
+ // Checks if a page-break before child is possible at the given page-break level
+ // false means the child should attempt the break self.
+ virtual bool canClear(RenderObject */*child*/, PageBreakLevel level)
+ { if (parent()) return parent()->canClear(this, level); else return false; }
+
+ void setAfterPageBreak(bool b = true) { m_afterPageBreak = b; };
+ void setBeforePageBreak(bool b = true) { m_beforePageBreak = b; };
+ virtual bool afterPageBreak() const { return m_afterPageBreak; }
+ virtual bool beforePageBreak() const { return m_beforePageBreak; }
+
+ // does a query on the rendertree and finds the innernode
+ // and overURL for the given position
+ // if readonly == false, it will recalc hover styles accordingly
+ class NodeInfo
+ {
+ friend class RenderImage;
+ friend class RenderFlow;
+ friend class RenderInline;
+ friend class RenderText;
+ friend class RenderWidget;
+ friend class RenderObject;
+ friend class RenderFrameSet;
+ friend class RenderLayer;
+ friend class DOM::HTMLAreaElementImpl;
+ public:
+ NodeInfo(bool readonly, bool active)
+ : m_innerNode(0), m_innerNonSharedNode(0), m_innerURLElement(0), m_readonly(readonly), m_active(active)
+ { }
+
+ DOM::NodeImpl* innerNode() const { return m_innerNode; }
+ DOM::NodeImpl* innerNonSharedNode() const { return m_innerNonSharedNode; }
+ DOM::NodeImpl* URLElement() const { return m_innerURLElement; }
+ bool readonly() const { return m_readonly; }
+ bool active() const { return m_active; }
+
+ private:
+ void setInnerNode(DOM::NodeImpl* n) { m_innerNode = n; }
+ void setInnerNonSharedNode(DOM::NodeImpl* n) { m_innerNonSharedNode = n; }
+ void setURLElement(DOM::NodeImpl* n) { m_innerURLElement = n; }
+
+ DOM::NodeImpl* m_innerNode;
+ DOM::NodeImpl* m_innerNonSharedNode;
+ DOM::NodeImpl* m_innerURLElement;
+ bool m_readonly;
+ bool m_active;
+ };
+
+ /** contains stateful information for a checkSelectionPoint call
+ */
+ struct SelPointState {
+ /** last node that was before the current position */
+ DOM::NodeImpl *m_lastNode;
+ /** offset of last node */
+ long m_lastOffset;
+ /** true when the last node had the result SelectionAfterInLine */
+ bool m_afterInLine;
+
+ SelPointState() : m_lastNode(0), m_lastOffset(0), m_afterInLine(false)
+ {}
+ };
+
+ virtual FindSelectionResult checkSelectionPoint( int _x, int _y, int _tx, int _ty,
+ DOM::NodeImpl*&, int & offset,
+ SelPointState & );
+ virtual bool nodeAtPoint(NodeInfo& info, int x, int y, int tx, int ty, HitTestAction, bool inside = false);
+ void setInnerNode(NodeInfo& info);
+
+ // set the style of the object.
+ virtual void setStyle(RenderStyle *style);
+
+ // returns the containing block level element for this element.
+ RenderBlock *containingBlock() const;
+
+ // return just the width of the containing block
+ virtual short containingBlockWidth() const;
+ // return just the height of the containing block
+ virtual int containingBlockHeight() const;
+
+ // size of the content area (box size minus padding/border)
+ virtual short contentWidth() const { return 0; }
+ virtual int contentHeight() const { return 0; }
+
+ // intrinsic extend of replaced elements. undefined otherwise
+ virtual short intrinsicWidth() const { return 0; }
+ virtual int intrinsicHeight() const { return 0; }
+
+ // relative to parent node
+ virtual void setPos( int /*xPos*/, int /*yPos*/ ) { }
+ virtual void setWidth( int /*width*/ ) { }
+ virtual void setHeight( int /*height*/ ) { }
+
+ virtual int xPos() const { return 0; }
+ virtual int yPos() const { return 0; }
+
+ /** the position of the object from where it begins drawing, including
+ * its negative overflow
+ */
+ int effectiveXPos() const { return xPos() + (hasOverflowClip() ? 0 : overflowLeft()); }
+
+ /** the position of the object from where it begins drawing, including
+ * its negative overflow
+ */
+ int effectiveYPos() const { return yPos() + (hasOverflowClip() ? -borderTopExtra() : kMin(overflowTop(), -borderTopExtra())); }
+
+ /** Leftmost coordinate of this inline element relative to containing
+ * block. Always zero for non-inline elements.
+ */
+ virtual int inlineXPos() const { return 0; }
+ /** Topmost coordinate of this inline element relative to containing
+ * block. Always zero for non-inline elements.
+ */
+ virtual int inlineYPos() const { return 0; }
+
+ // calculate client position of box
+ virtual bool absolutePosition(int &/*xPos*/, int &/*yPos*/, bool fixed = false) const;
+
+ // width and height are without margins but include paddings and borders
+ virtual short width() const { return 0; }
+ virtual int height() const { return 0; }
+
+ // The height of a block when you include overflow spillage out of
+ // the bottom of the block (e.g., a <div style="height:25px"> that
+ // has a 100px tall image inside it would have an overflow height
+ // of borderTop() + paddingTop() + 100px.
+ virtual int overflowHeight() const { return height(); }
+ virtual int overflowWidth() const { return width(); }
+ // how much goes over the left hand side (0 or a negative number)
+ virtual int overflowTop() const { return 0; }
+ virtual int overflowLeft() const { return 0; }
+
+ /**
+ * Returns the height that is effectively considered when contemplating the
+ * object as a whole -- usually the overflow height, or the height if clipped.
+ */
+ int effectiveHeight() const { return hasOverflowClip() ? height() + borderTopExtra() + borderBottomExtra() :
+ kMax(overflowHeight() - overflowTop(), height() + borderTopExtra() + borderBottomExtra()); }
+ /**
+ * Returns the width that is effectively considered when contemplating the
+ * object as a whole -- usually the overflow width, or the width if clipped.
+ */
+ int effectiveWidth() const { return hasOverflowClip() ? width() : overflowWidth() - overflowLeft(); }
+
+ // IE extensions, heavily used in ECMA
+ virtual short offsetWidth() const { return width(); }
+ virtual int offsetHeight() const { return height() + borderTopExtra() + borderBottomExtra(); }
+ virtual int offsetLeft() const;
+ virtual int offsetTop() const;
+ virtual RenderObject* offsetParent() const;
+ short clientWidth() const;
+ int clientHeight() const;
+ short scrollWidth() const;
+ int scrollHeight() const;
+
+ virtual bool isSelfCollapsingBlock() const { return false; }
+ short collapsedMarginTop() const { return maxTopMargin(true)-maxTopMargin(false); }
+ short collapsedMarginBottom() const { return maxBottomMargin(true)-maxBottomMargin(false); }
+
+ virtual bool isTopMarginQuirk() const { return false; }
+ virtual bool isBottomMarginQuirk() const { return false; }
+ virtual short maxTopMargin(bool positive) const
+ { return positive ? kMax( int( marginTop() ), 0 ) : - kMin( int( marginTop() ), 0 ); }
+ virtual short maxBottomMargin(bool positive) const
+ { return positive ? kMax( int( marginBottom() ), 0 ) : - kMin( int( marginBottom() ), 0 ); }
+
+ virtual short marginTop() const { return 0; }
+ virtual short marginBottom() const { return 0; }
+ virtual short marginLeft() const { return 0; }
+ virtual short marginRight() const { return 0; }
+
+ virtual int paddingTop() const;
+ virtual int paddingBottom() const;
+ virtual int paddingLeft() const;
+ virtual int paddingRight() const;
+
+ virtual int borderTop() const { return style()->borderTopWidth(); }
+ virtual int borderBottom() const { return style()->borderBottomWidth(); }
+ virtual int borderLeft() const { return style()->borderLeftWidth(); }
+ virtual int borderRight() const { return style()->borderRightWidth(); }
+
+ virtual short minWidth() const { return 0; }
+ virtual int maxWidth() const { return 0; }
+
+ RenderStyle* style() const { return m_style; }
+ RenderStyle* style( bool firstLine ) const {
+ RenderStyle *s = m_style;
+ if( firstLine && hasFirstLine() ) {
+ RenderStyle *pseudoStyle = style()->getPseudoStyle(RenderStyle::FIRST_LINE);
+ if ( pseudoStyle )
+ s = pseudoStyle;
+ }
+ return s;
+ }
+
+ void getTextDecorationColors(int decorations, QColor& underline, QColor& overline,
+ QColor& linethrough, bool quirksMode=false);
+
+ enum BorderSide {
+ BSTop, BSBottom, BSLeft, BSRight
+ };
+ void drawBorder(QPainter *p, int x1, int y1, int x2, int y2, BorderSide s,
+ QColor c, const QColor& textcolor, EBorderStyle style,
+ int adjbw1, int adjbw2, bool invalidisInvert = false);
+
+ // Used by collapsed border tables.
+ virtual void collectBorders(QValueList<CollapsedBorderValue>& borderStyles);
+
+ // force a complete repaint
+ virtual void repaint(Priority p = NormalPriority) { if(m_parent) m_parent->repaint(p); }
+ virtual void repaintRectangle(int x, int y, int w, int h, Priority p=NormalPriority, bool f=false);
+
+ virtual unsigned int length() const { return 1; }
+
+ virtual bool isHidden() const { return isFloating() || isPositioned(); }
+
+ // Special objects are objects that are neither really inline nor blocklevel
+ bool isFloatingOrPositioned() const { return (isFloating() || isPositioned()); };
+ virtual bool hasOverhangingFloats() const { return false; }
+ virtual bool hasFloats() const { return false; }
+ virtual bool containsFloat(RenderObject* /*o*/) const { return false; }
+ virtual void markAllDescendantsWithFloatsForLayout(RenderObject* /*floatToRemove*/ = 0) {}
+
+ bool flowAroundFloats() const;
+ bool usesLineWidth() const;
+
+ // positioning of inline children (bidi)
+ virtual void position(InlineBox*, int, int, bool) {}
+// virtual void position(int, int, int, int, int, bool, bool, int) {}
+
+ // Applied as a "slop" to dirty rect checks during the outline painting phase's dirty-rect checks.
+ int maximalOutlineSize(PaintAction p) const;
+
+ enum SelectionState {
+ SelectionNone,
+ SelectionStart,
+ SelectionInside,
+ SelectionEnd,
+ SelectionBoth
+ };
+
+ virtual SelectionState selectionState() const { return SelectionNone;}
+ virtual void setSelectionState(SelectionState) {}
+
+ /**
+ * Flags which influence the appearence and position
+ * @param CFOverride input overrides existing character, caret should be
+ * cover the whole character
+ * @param CFOutside coordinates are to be interpreted outside of the
+ * render object
+ * @param CFOutsideEnd coordinates are to be interpreted at the outside
+ * end of the render object (only valid if CFOutside is also set)
+ */
+ enum CaretFlags { CFOverride = 0x01, CFOutside = 0x02, CFOutsideEnd = 0x04 };
+
+ /**
+ * Returns the content coordinates of the caret within this render object.
+ * @param offset zero-based offset determining position within the render object.
+ * @param flags combination of enum CaretFlags
+ * @param _x returns the left coordinate
+ * @param _y returns the top coordinate
+ * @param width returns the caret's width
+ * @param height returns the caret's height
+ */
+ virtual void caretPos(int offset, int flags, int &_x, int &_y, int &width, int &height);
+
+ // returns the lowest position of the lowest object in that particular object.
+ // This 'height' is relative to the topleft of the margin box of the object.
+ // Implemented in RenderFlow.
+ virtual int lowestPosition(bool /*includeOverflowInterior*/=true, bool /*includeSelf*/=true) const { return 0; }
+ virtual int rightmostPosition(bool /*includeOverflowInterior*/=true, bool /*includeSelf*/=true) const { return 0; }
+ virtual int leftmostPosition(bool /*includeOverflowInterior*/=true, bool /*includeSelf*/=true) const { return 0; }
+ virtual int highestPosition(bool /*includeOverflowInterior*/=true, bool /*includeSelf*/=true) const { return 0; }
+
+ // recursively invalidate current layout
+ // unused: void invalidateLayout();
+
+ virtual void calcVerticalMargins() {}
+ void removeFromObjectLists();
+
+ virtual void deleteInlineBoxes(RenderArena* arena=0) {(void)arena;}
+ virtual void detach( );
+
+ void setDoNotDelete(bool b) { m_doNotDelete = b; }
+ bool doNotDelete() const { return m_doNotDelete; }
+
+ const QFont &font(bool firstLine) const {
+ return style( firstLine )->font();
+ }
+
+ const QFontMetrics &fontMetrics(bool firstLine) const {
+ return style( firstLine )->fontMetrics();
+ }
+
+ /** returns the lowest possible value the caret offset may have to
+ * still point to a valid position.
+ *
+ * Returns 0 by default.
+ */
+ virtual long minOffset() const { return 0; }
+ /** returns the highest possible value the caret offset may have to
+ * still point to a valid position.
+ *
+ * Returns 0 by default, as generic elements are considered to have no
+ * width.
+ */
+ virtual long maxOffset() const { return 0; }
+
+ virtual void setPixmap(const QPixmap &, const QRect&, CachedImage *);
+
+ QRegion visibleFlowRegion(int x, int y) const;
+
+protected:
+ virtual void selectionStartEnd(int& spos, int& epos);
+
+ virtual QRect viewRect() const;
+ void remove();
+ void invalidateVerticalPositions();
+ bool attemptDirectLayerTranslation();
+ void updateWidgetMasks();
+
+ virtual void removeLeftoverAnonymousBoxes();
+
+ void arenaDelete(RenderArena *arena);
+
+private:
+ RenderStyle* m_style;
+ DOM::NodeImpl* m_node;
+ RenderObject *m_parent;
+ RenderObject *m_previous;
+ RenderObject *m_next;
+
+ short m_verticalPosition;
+
+ bool m_needsLayout : 1;
+ bool m_normalChildNeedsLayout : 1;
+ bool m_markedForRepaint : 1;
+ bool m_posChildNeedsLayout : 1;
+
+ bool m_minMaxKnown : 1;
+ bool m_floating : 1;
+
+ bool m_positioned : 1;
+ bool m_overhangingContents : 1;
+ bool m_relPositioned : 1;
+ bool m_paintBackground : 1; // if the box has something to paint in the
+ // background painting phase (background, border, etc)
+
+ bool m_isAnonymous : 1;
+ bool m_recalcMinMax : 1;
+ bool m_isText : 1;
+ bool m_inline : 1;
+ bool m_attached : 1;
+
+ bool m_replaced : 1;
+ bool m_mouseInside : 1;
+ bool m_hasFirstLine : 1;
+ bool m_isSelectionBorder : 1;
+
+ bool m_isRoot : 1;
+
+ bool m_beforePageBreak : 1;
+ bool m_afterPageBreak : 1;
+
+ bool m_needsPageClear : 1;
+ bool m_containsPageBreak : 1;
+
+ bool m_hasOverflowClip : 1;
+
+ bool m_doNotDelete : 1; // This object should not be auto-deleted
+
+ // ### we have 16 + 26 bits.
+
+
+ void arenaDelete(RenderArena *arena, void *objectBase);
+
+ friend class RenderLayer;
+ friend class RenderListItem;
+ friend class RenderContainer;
+ friend class RenderCanvas;
+};
+
+
+enum VerticalPositionHint {
+ PositionTop = -0x4000,
+ PositionBottom = 0x4000,
+ PositionUndefined = 0x3fff
+};
+
+} //namespace
+#endif
diff --git a/khtml/rendering/render_replaced.cpp b/khtml/rendering/render_replaced.cpp
new file mode 100644
index 000000000..a74338e29
--- /dev/null
+++ b/khtml/rendering/render_replaced.cpp
@@ -0,0 +1,939 @@
+/**
+ * This file is part of the HTML widget for KDE.
+ *
+ * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
+ * Copyright (C) 2000-2003 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003 Apple Computer, Inc.
+ * Copyright (C) 2004 Germain Garand (germain@ebooksfrance.org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+#include "render_replaced.h"
+#include "render_layer.h"
+#include "render_canvas.h"
+#include "render_line.h"
+
+#include "render_arena.h"
+
+#include <assert.h>
+#include <qwidget.h>
+#include <qpainter.h>
+#include <qevent.h>
+#include <qapplication.h>
+#include <qlineedit.h>
+#include <kglobalsettings.h>
+#include <qobjectlist.h>
+#include <qvaluevector.h>
+
+#include "khtml_ext.h"
+#include "khtmlview.h"
+#include "xml/dom2_eventsimpl.h"
+#include "khtml_part.h"
+#include "xml/dom_docimpl.h"
+#include <kdebug.h>
+
+bool khtml::allowWidgetPaintEvents = false;
+
+using namespace khtml;
+using namespace DOM;
+
+
+RenderReplaced::RenderReplaced(DOM::NodeImpl* node)
+ : RenderBox(node)
+{
+ // init RenderObject attributes
+ setReplaced(true);
+
+ m_intrinsicWidth = 300;
+ m_intrinsicHeight = 150;
+}
+
+void RenderReplaced::calcMinMaxWidth()
+{
+ KHTMLAssert( !minMaxKnown());
+
+#ifdef DEBUG_LAYOUT
+ kdDebug( 6040 ) << "RenderReplaced::calcMinMaxWidth() known=" << minMaxKnown() << endl;
+#endif
+
+ m_width = calcReplacedWidth();
+ m_width = calcBoxWidth( m_width );
+
+ if ( style()->width().isPercent() || style()->height().isPercent() ||
+ style()->maxWidth().isPercent() || style()->maxHeight().isPercent() ||
+ style()->minWidth().isPercent() || style()->minHeight().isPercent() ) {
+ m_minWidth = 0;
+ m_maxWidth = m_width;
+ }
+ else
+ m_minWidth = m_maxWidth = m_width;
+
+ setMinMaxKnown();
+}
+
+void RenderReplaced::position(InlineBox* box, int /*from*/, int /*len*/, bool /*reverse*/)
+{
+ setPos( box->xPos(), box->yPos() );
+}
+
+// -----------------------------------------------------------------------------
+
+RenderWidget::RenderWidget(DOM::NodeImpl* node)
+ : RenderReplaced(node)
+{
+ m_widget = 0;
+ // a widget doesn't support being anonymous
+ assert(!isAnonymous());
+ m_view = node->getDocument()->view();
+ m_arena.reset(renderArena());
+ m_resizePending = false;
+ m_discardResizes = false;
+ m_isKHTMLWidget = false;
+ m_needsMask = false;
+
+ // this is no real reference counting, its just there
+ // to make sure that we're not deleted while we're recursed
+ // in an eventFilter of the widget
+ ref();
+}
+
+void RenderWidget::detach()
+{
+ remove();
+ deleteInlineBoxes();
+
+ if ( m_widget ) {
+ if ( m_view ) {
+ m_view->setWidgetVisible(this, false);
+ m_view->removeChild( m_widget );
+ }
+
+ m_widget->removeEventFilter( this );
+ m_widget->setMouseTracking( false );
+ }
+
+ deref();
+}
+
+RenderWidget::~RenderWidget()
+{
+ KHTMLAssert( refCount() <= 0 );
+
+ if(m_widget) {
+ m_widget->hide();
+ m_widget->deleteLater();
+ }
+}
+
+class QWidgetResizeEvent : public QEvent
+{
+public:
+ enum { Type = QEvent::User + 0xbee };
+ QWidgetResizeEvent( int _w, int _h ) :
+ QEvent( ( QEvent::Type ) Type ), w( _w ), h( _h ) {}
+ int w;
+ int h;
+};
+
+void RenderWidget::resizeWidget( int w, int h )
+{
+ // ugly hack to limit the maximum size of the widget ( as X11 has problems if
+ // its bigger )
+ h = kMin( h, 3072 );
+ w = kMin( w, 2000 );
+
+ if (m_widget->width() != w || m_widget->height() != h) {
+ m_resizePending = isKHTMLWidget();
+ ref();
+ element()->ref();
+ QApplication::postEvent( this, new QWidgetResizeEvent( w, h ) );
+ element()->deref();
+ deref();
+ }
+}
+
+void RenderWidget::cancelPendingResize()
+{
+ if (!m_widget)
+ return;
+ m_discardResizes = true;
+ QApplication::sendPostedEvents(this, QWidgetResizeEvent::Type);
+ m_discardResizes = false;
+}
+
+bool RenderWidget::event( QEvent *e )
+{
+ if ( m_widget && (e->type() == (QEvent::Type)QWidgetResizeEvent::Type) ) {
+ m_resizePending = false;
+ if (m_discardResizes)
+ return true;
+ QWidgetResizeEvent *re = static_cast<QWidgetResizeEvent *>(e);
+ m_widget->resize( re->w, re->h );
+ repaint();
+ }
+ // eat all events - except if this is a frame (in which case KHTMLView handles it all)
+ if ( ::qt_cast<KHTMLView *>( m_widget ) )
+ return QObject::event( e );
+ return true;
+}
+
+void RenderWidget::flushWidgetResizes() //static
+{
+ QApplication::sendPostedEvents( 0, QWidgetResizeEvent::Type );
+}
+
+void RenderWidget::setQWidget(QWidget *widget)
+{
+ if (widget != m_widget)
+ {
+ if (m_widget) {
+ m_widget->removeEventFilter(this);
+ disconnect( m_widget, SIGNAL( destroyed()), this, SLOT( slotWidgetDestructed()));
+ m_widget->hide();
+ m_widget->deleteLater(); //Might happen due to event on the widget, so be careful
+ m_widget = 0;
+ }
+ m_widget = widget;
+ if (m_widget) {
+ connect( m_widget, SIGNAL( destroyed()), this, SLOT( slotWidgetDestructed()));
+ m_widget->installEventFilter(this);
+
+ if ( (m_isKHTMLWidget = !strcmp(m_widget->name(), "__khtml")) && !::qt_cast<QFrame*>(m_widget))
+ m_widget->setBackgroundMode( QWidget::NoBackground );
+
+ if (m_widget->focusPolicy() > QWidget::StrongFocus)
+ m_widget->setFocusPolicy(QWidget::StrongFocus);
+ // if we've already received a layout, apply the calculated space to the
+ // widget immediately, but we have to have really been full constructed (with a non-null
+ // style pointer).
+ if (!needsLayout() && style()) {
+ resizeWidget( m_width-borderLeft()-borderRight()-paddingLeft()-paddingRight(),
+ m_height-borderTop()-borderBottom()-paddingTop()-paddingBottom() );
+ }
+ else
+ setPos(xPos(), -500000);
+ }
+ m_view->setWidgetVisible(this, false);
+ m_view->addChild( m_widget, 0, -500000);
+ if ( m_widget ) m_widget->hide();
+ m_resizePending = false;
+ }
+}
+
+void RenderWidget::layout( )
+{
+ KHTMLAssert( needsLayout() );
+ KHTMLAssert( minMaxKnown() );
+ if ( m_widget ) {
+ resizeWidget( m_width-borderLeft()-borderRight()-paddingLeft()-paddingRight(),
+ m_height-borderTop()-borderBottom()-paddingTop()-paddingBottom() );
+ if (!isKHTMLWidget() && !isFrame() && !m_needsMask) {
+ m_needsMask = true;
+ RenderLayer* rl = enclosingStackingContext();
+ RenderLayer* el = enclosingLayer();
+ while (rl && el && el != rl) {
+ if (el->renderer()->style()->position() != STATIC) {
+ m_needsMask = false;
+ break;
+ }
+ el = el->parent();
+ }
+ if (m_needsMask) {
+ if (rl) rl->setHasOverlaidWidgets();
+ canvas()->setNeedsWidgetMasks();
+ }
+ }
+ }
+
+ setNeedsLayout(false);
+}
+
+void RenderWidget::updateFromElement()
+{
+ if (m_widget) {
+ // Color:
+ QColor color = style()->color();
+ QColor backgroundColor = style()->backgroundColor();
+
+ if ( color.isValid() || backgroundColor.isValid() ) {
+ QPalette pal(QApplication::palette(m_widget));
+
+ int contrast_ = KGlobalSettings::contrast();
+ int highlightVal = 100 + (2*contrast_+4)*16/10;
+ int lowlightVal = 100 + (2*contrast_+4)*10;
+
+ if (backgroundColor.isValid()) {
+ if (!isKHTMLWidget())
+ widget()->setEraseColor(backgroundColor );
+ for ( int i = 0; i < QPalette::NColorGroups; ++i ) {
+ pal.setColor( (QPalette::ColorGroup)i, QColorGroup::Background, backgroundColor );
+ pal.setColor( (QPalette::ColorGroup)i, QColorGroup::Light, backgroundColor.light(highlightVal) );
+ pal.setColor( (QPalette::ColorGroup)i, QColorGroup::Dark, backgroundColor.dark(lowlightVal) );
+ pal.setColor( (QPalette::ColorGroup)i, QColorGroup::Mid, backgroundColor.dark(120) );
+ pal.setColor( (QPalette::ColorGroup)i, QColorGroup::Midlight, backgroundColor.light(110) );
+ pal.setColor( (QPalette::ColorGroup)i, QColorGroup::Button, backgroundColor );
+ pal.setColor( (QPalette::ColorGroup)i, QColorGroup::Base, backgroundColor );
+ }
+ }
+ if ( color.isValid() ) {
+ struct ColorSet {
+ QPalette::ColorGroup cg;
+ QColorGroup::ColorRole cr;
+ };
+ const struct ColorSet toSet [] = {
+ { QPalette::Active, QColorGroup::Foreground },
+ { QPalette::Active, QColorGroup::ButtonText },
+ { QPalette::Active, QColorGroup::Text },
+ { QPalette::Inactive, QColorGroup::Foreground },
+ { QPalette::Inactive, QColorGroup::ButtonText },
+ { QPalette::Inactive, QColorGroup::Text },
+ { QPalette::Disabled,QColorGroup::ButtonText },
+ { QPalette::NColorGroups, QColorGroup::NColorRoles },
+ };
+ const ColorSet *set = toSet;
+ while( set->cg != QPalette::NColorGroups ) {
+ pal.setColor( set->cg, set->cr, color );
+ ++set;
+ }
+
+ QColor disfg = color;
+ int h, s, v;
+ disfg.hsv( &h, &s, &v );
+ if (v > 128)
+ // dark bg, light fg - need a darker disabled fg
+ disfg = disfg.dark(lowlightVal);
+ else if (disfg != Qt::black)
+ // light bg, dark fg - need a lighter disabled fg - but only if !black
+ disfg = disfg.light(highlightVal);
+ else
+ // black fg - use darkgray disabled fg
+ disfg = Qt::darkGray;
+ pal.setColor(QPalette::Disabled,QColorGroup::Foreground,disfg);
+ }
+
+ m_widget->setPalette(pal);
+ }
+ else
+ m_widget->unsetPalette();
+ // Border:
+ QFrame* frame = ::qt_cast<QFrame*>(m_widget);
+ if (frame) {
+ if (shouldPaintBackgroundOrBorder())
+ {
+ frame->setFrameShape(QFrame::NoFrame);
+ }
+ }
+
+ }
+
+ RenderReplaced::updateFromElement();
+}
+
+void RenderWidget::slotWidgetDestructed()
+{
+ if (m_view)
+ m_view->setWidgetVisible(this, false);
+ m_widget = 0;
+}
+
+void RenderWidget::setStyle(RenderStyle *_style)
+{
+ RenderReplaced::setStyle(_style);
+ if(m_widget)
+ {
+ m_widget->setFont(style()->font());
+ if (style()->visibility() != VISIBLE) {
+ if (m_view)
+ m_view->setWidgetVisible(this, false);
+ m_widget->hide();
+ }
+ }
+
+ // Don't paint borders if the border-style is native
+ // or borders are not supported on this widget
+ if (!canHaveBorder() ||
+ (style()->borderLeftStyle() == BNATIVE &&
+ style()->borderRightStyle() == BNATIVE &&
+ style()->borderTopStyle() == BNATIVE &&
+ style()->borderBottomStyle() == BNATIVE))
+ {
+ setShouldPaintBackgroundOrBorder(false);
+ }
+}
+
+void RenderWidget::paint(PaintInfo& paintInfo, int _tx, int _ty)
+{
+ _tx += m_x;
+ _ty += m_y;
+
+ if (shouldPaintBackgroundOrBorder() &&
+ (paintInfo.phase == PaintActionChildBackground || paintInfo.phase == PaintActionChildBackgrounds))
+ paintBoxDecorations(paintInfo, _tx, _ty);
+
+ if (!m_widget || !m_view || paintInfo.phase != PaintActionForeground)
+ return;
+
+ // not visible or not even once layouted
+ if (style()->visibility() != VISIBLE || m_y <= -500000 || m_resizePending )
+ return;
+
+ if ( (_ty > paintInfo.r.bottom()) || (_ty + m_height <= paintInfo.r.top()) ||
+ (_tx + m_width <= paintInfo.r.left()) || (_tx > paintInfo.r.right()) )
+ return;
+
+ int xPos = _tx+borderLeft()+paddingLeft();
+ int yPos = _ty+borderTop()+paddingTop();
+
+ bool khtmlw = isKHTMLWidget();
+ int childw = m_widget->width();
+ int childh = m_widget->height();
+ if ( (childw == 2000 || childh == 3072) && m_widget->inherits( "KHTMLView" ) ) {
+ KHTMLView *vw = static_cast<KHTMLView *>(m_widget);
+ int cy = m_view->contentsY();
+ int ch = m_view->visibleHeight();
+
+
+ int childx = m_view->childX( m_widget );
+ int childy = m_view->childY( m_widget );
+
+ int xNew = xPos;
+ int yNew = childy;
+
+ // qDebug("cy=%d, ch=%d, childy=%d, childh=%d", cy, ch, childy, childh );
+ if ( childh == 3072 ) {
+ if ( cy + ch > childy + childh ) {
+ yNew = cy + ( ch - childh )/2;
+ } else if ( cy < childy ) {
+ yNew = cy + ( ch - childh )/2;
+ }
+// qDebug("calculated yNew=%d", yNew);
+ }
+ yNew = kMin( yNew, yPos + m_height - childh );
+ yNew = kMax( yNew, yPos );
+ if ( yNew != childy || xNew != childx ) {
+ if ( vw->contentsHeight() < yNew - yPos + childh )
+ vw->resizeContents( vw->contentsWidth(), yNew - yPos + childh );
+ vw->setContentsPos( xNew - xPos, yNew - yPos );
+ }
+ xPos = xNew;
+ yPos = yNew;
+ }
+ m_view->setWidgetVisible(this, true);
+ m_view->addChild(m_widget, xPos, yPos );
+ m_widget->show();
+ if (khtmlw)
+ paintWidget(paintInfo, m_widget, xPos, yPos);
+}
+
+#include <private/qinternal_p.h>
+
+// The PaintBuffer class provides a shared buffer for widget painting.
+//
+// It will grow to encompass the biggest widget encountered, in order to avoid
+// constantly resizing.
+// When it grows over maxPixelBuffering, it periodically checks if such a size
+// is still needed. If not, it shrinks down to the biggest size < maxPixelBuffering
+// that was requested during the overflow lapse.
+
+class PaintBuffer: public QObject
+{
+public:
+ static const int maxPixelBuffering = 320*200;
+ static const int leaseTime = 20*1000;
+
+ static QPixmap *grab( QSize s = QSize() ) {
+ if (!m_inst)
+ m_inst = new PaintBuffer;
+ return m_inst->getBuf( s );
+ }
+ static void release() { m_inst->m_grabbed = false; }
+protected:
+ PaintBuffer(): m_overflow(false), m_grabbed(false),
+ m_timer(0), m_resetWidth(0), m_resetHeight(0) {};
+ void timerEvent(QTimerEvent* e) {
+ assert( m_timer == e->timerId() );
+ if (m_grabbed)
+ return;
+ m_buf.resize(m_resetWidth, m_resetHeight);
+ m_resetWidth = m_resetHeight = 0;
+ killTimer( m_timer );
+ m_timer = 0;
+ }
+
+ QPixmap *getBuf( QSize s ) {
+ assert( !m_grabbed );
+ if (s.isEmpty())
+ return 0;
+
+ m_grabbed = true;
+ bool cur_overflow = false;
+
+ int nw = kMax(m_buf.width(), s.width());
+ int nh = kMax(m_buf.height(), s.height());
+
+ if (!m_overflow && (nw*nh > maxPixelBuffering))
+ cur_overflow = true;
+
+ if (nw != m_buf.width() || nh != m_buf.height())
+ m_buf.resize(nw, nh);
+
+ if (cur_overflow) {
+ m_overflow = true;
+ m_timer = startTimer( leaseTime );
+ } else if (m_overflow) {
+ if( s.width()*s.height() > maxPixelBuffering ) {
+ killTimer( m_timer );
+ m_timer = startTimer( leaseTime );
+ } else {
+ if (s.width() > m_resetWidth)
+ m_resetWidth = s.width();
+ if (s.height() > m_resetHeight)
+ m_resetHeight = s.height();
+ }
+ }
+ return &m_buf;
+ }
+private:
+ static PaintBuffer* m_inst;
+ QPixmap m_buf;
+ bool m_overflow;
+ bool m_grabbed;
+ int m_timer;
+ int m_resetWidth;
+ int m_resetHeight;
+};
+
+PaintBuffer *PaintBuffer::m_inst = 0;
+
+static void copyWidget(const QRect& r, QPainter *p, QWidget *widget, int tx, int ty)
+{
+ if (r.isNull() || r.isEmpty() )
+ return;
+ QRegion blit(r);
+ QValueVector<QWidget*> cw;
+ QValueVector<QRect> cr;
+
+ if (widget->children()) {
+ // build region
+ QObjectListIterator it = *widget->children();
+ for (; it.current(); ++it) {
+ QWidget* const w = ::qt_cast<QWidget *>(it.current());
+ if ( w && !w->isTopLevel() && !w->isHidden()) {
+ QRect r2 = w->geometry();
+ blit -= r2;
+ r2 = r2.intersect( r );
+ r2.moveBy(-w->x(), -w->y());
+ cr.append(r2);
+ cw.append(w);
+ }
+ }
+ }
+ QMemArray<QRect> br = blit.rects();
+
+ const int cnt = br.size();
+ const bool external = p->device()->isExtDev();
+ QPixmap* const pm = PaintBuffer::grab( widget->size() );
+ if (!pm)
+ {
+ kdWarning(6040) << "Rendering widget [ " << widget->className() << " ] failed due to invalid size." << endl;
+ return;
+ }
+
+ // fill background
+ if ( external ) {
+ // even hackier!
+ QPainter pt( pm );
+ const QColor c = widget->colorGroup().base();
+ for (int i = 0; i < cnt; ++i)
+ pt.fillRect( br[i], c );
+ } else {
+ QRect dr;
+ for (int i = 0; i < cnt; ++i ) {
+ dr = br[i];
+ dr.moveBy( tx, ty );
+ dr = p->xForm( dr );
+ bitBlt(pm, br[i].topLeft(), p->device(), dr);
+ }
+ }
+
+ // send paint event
+ QPainter::redirect(widget, pm);
+ QPaintEvent e( r, false );
+ QApplication::sendEvent( widget, &e );
+ QPainter::redirect(widget, 0);
+
+ // transfer result
+ if ( external )
+ for ( int i = 0; i < cnt; ++i )
+ p->drawPixmap(QPoint(tx+br[i].x(), ty+br[i].y()), *pm, br[i]);
+ else
+ for ( int i = 0; i < cnt; ++i )
+ bitBlt(p->device(), p->xForm( QPoint(tx, ty) + br[i].topLeft() ), pm, br[i]);
+
+ // cleanup and recurse
+ PaintBuffer::release();
+ QValueVector<QWidget*>::iterator cwit = cw.begin();
+ QValueVector<QWidget*>::iterator cwitEnd = cw.end();
+ QValueVector<QRect>::const_iterator crit = cr.begin();
+ for (; cwit != cwitEnd; ++cwit, ++crit)
+ copyWidget(*crit, p, *cwit, tx+(*cwit)->x(), ty+(*cwit)->y());
+}
+
+void RenderWidget::paintWidget(PaintInfo& pI, QWidget *widget, int tx, int ty)
+{
+ QPainter* const p = pI.p;
+ allowWidgetPaintEvents = true;
+
+ const bool dsbld = QSharedDoubleBuffer::isDisabled();
+ QSharedDoubleBuffer::setDisabled(true);
+ QRect rr = pI.r;
+ rr.moveBy(-tx, -ty);
+ const QRect r = widget->rect().intersect( rr );
+ copyWidget(r, p, widget, tx, ty);
+ QSharedDoubleBuffer::setDisabled(dsbld);
+
+ allowWidgetPaintEvents = false;
+}
+
+bool RenderWidget::eventFilter(QObject* /*o*/, QEvent* e)
+{
+ // no special event processing if this is a frame (in which case KHTMLView handles it all)
+ if ( ::qt_cast<KHTMLView *>( m_widget ) )
+ return false;
+ if ( !element() ) return true;
+
+
+ static bool directToWidget = false;
+ if (directToWidget) {
+ //We're trying to get the event to the widget
+ //promptly. So get out of here..
+ return false;
+ }
+
+ ref();
+ element()->ref();
+
+ bool filtered = false;
+
+ //kdDebug() << "RenderWidget::eventFilter type=" << e->type() << endl;
+ switch(e->type()) {
+ case QEvent::FocusOut:
+ // First, forward it to the widget, so that Qt gets a precise
+ // state of the focus before pesky JS can try changing it..
+ directToWidget = true;
+ QApplication::sendEvent(m_widget, e);
+ directToWidget = false;
+ filtered = true; //We already delivered it!
+
+ // Don't count popup as a valid reason for losing the focus
+ // (example: opening the options of a select combobox shouldn't emit onblur)
+ if ( QFocusEvent::reason() != QFocusEvent::Popup )
+ handleFocusOut();
+ break;
+ case QEvent::FocusIn:
+ //As above, forward to the widget first...
+ directToWidget = true;
+ QApplication::sendEvent(m_widget, e);
+ directToWidget = false;
+ filtered = true; //We already delivered it!
+
+ //kdDebug(6000) << "RenderWidget::eventFilter captures FocusIn" << endl;
+ element()->getDocument()->setFocusNode(element());
+// if ( isEditable() ) {
+// KHTMLPartBrowserExtension *ext = static_cast<KHTMLPartBrowserExtension *>( element()->view->part()->browserExtension() );
+// if ( ext ) ext->editableWidgetFocused( m_widget );
+// }
+ break;
+ case QEvent::KeyPress:
+ case QEvent::KeyRelease:
+ // TODO this seems wrong - Qt events are not correctly translated to DOM ones,
+ // like in KHTMLView::dispatchKeyEvent()
+ if (element()->dispatchKeyEvent(static_cast<QKeyEvent*>(e),false))
+ filtered = true;
+ break;
+
+ case QEvent::Wheel:
+ if (widget()->parentWidget() == view()->viewport()) {
+ // don't allow the widget to react to wheel event unless its
+ // currently focused. this avoids accidentally changing a select box
+ // or something while wheeling a webpage.
+ if (qApp->focusWidget() != widget() &&
+ widget()->focusPolicy() <= QWidget::StrongFocus) {
+ static_cast<QWheelEvent*>(e)->ignore();
+ QApplication::sendEvent(view(), e);
+ filtered = true;
+ }
+ }
+ break;
+ default:
+ break;
+ };
+
+ element()->deref();
+
+ // stop processing if the widget gets deleted, but continue in all other cases
+ if (hasOneRef())
+ filtered = true;
+ deref();
+
+ return filtered;
+}
+
+void RenderWidget::EventPropagator::sendEvent(QEvent *e) {
+ switch(e->type()) {
+ case QEvent::MouseButtonPress:
+ mousePressEvent(static_cast<QMouseEvent *>(e));
+ break;
+ case QEvent::MouseButtonRelease:
+ mouseReleaseEvent(static_cast<QMouseEvent *>(e));
+ break;
+ case QEvent::MouseButtonDblClick:
+ mouseDoubleClickEvent(static_cast<QMouseEvent *>(e));
+ break;
+ case QEvent::MouseMove:
+ mouseMoveEvent(static_cast<QMouseEvent *>(e));
+ break;
+ case QEvent::KeyPress:
+ keyPressEvent(static_cast<QKeyEvent *>(e));
+ break;
+ case QEvent::KeyRelease:
+ keyReleaseEvent(static_cast<QKeyEvent *>(e));
+ break;
+ default:
+ break;
+ }
+}
+
+void RenderWidget::ScrollViewEventPropagator::sendEvent(QEvent *e) {
+ switch(e->type()) {
+ case QEvent::MouseButtonPress:
+ viewportMousePressEvent(static_cast<QMouseEvent *>(e));
+ break;
+ case QEvent::MouseButtonRelease:
+ viewportMouseReleaseEvent(static_cast<QMouseEvent *>(e));
+ break;
+ case QEvent::MouseButtonDblClick:
+ viewportMouseDoubleClickEvent(static_cast<QMouseEvent *>(e));
+ break;
+ case QEvent::MouseMove:
+ viewportMouseMoveEvent(static_cast<QMouseEvent *>(e));
+ break;
+ case QEvent::KeyPress:
+ keyPressEvent(static_cast<QKeyEvent *>(e));
+ break;
+ case QEvent::KeyRelease:
+ keyReleaseEvent(static_cast<QKeyEvent *>(e));
+ break;
+ default:
+ break;
+ }
+}
+
+bool RenderWidget::handleEvent(const DOM::EventImpl& ev)
+{
+ bool ret = false;
+ switch(ev.id()) {
+ case EventImpl::MOUSEDOWN_EVENT:
+ case EventImpl::MOUSEUP_EVENT:
+ case EventImpl::MOUSEMOVE_EVENT: {
+ if (!ev.isMouseEvent()) break;
+ const MouseEventImpl &me = static_cast<const MouseEventImpl &>(ev);
+ QMouseEvent* const qme = me.qEvent();
+
+ int absx = 0;
+ int absy = 0;
+
+ absolutePosition(absx, absy);
+ QPoint p(me.clientX() - absx + m_view->contentsX(),
+ me.clientY() - absy + m_view->contentsY());
+ QMouseEvent::Type type;
+ int button = 0;
+ int state = 0;
+
+ if (qme) {
+ button = qme->button();
+ state = qme->state();
+ type = qme->type();
+ } else {
+ switch(me.id()) {
+ case EventImpl::MOUSEDOWN_EVENT:
+ type = QMouseEvent::MouseButtonPress;
+ break;
+ case EventImpl::MOUSEUP_EVENT:
+ type = QMouseEvent::MouseButtonRelease;
+ break;
+ case EventImpl::MOUSEMOVE_EVENT:
+ default:
+ type = QMouseEvent::MouseMove;
+ break;
+ }
+ switch (me.button()) {
+ case 0:
+ button = LeftButton;
+ break;
+ case 1:
+ button = MidButton;
+ break;
+ case 2:
+ button = RightButton;
+ break;
+ default:
+ break;
+ }
+ if (me.ctrlKey())
+ state |= ControlButton;
+ if (me.altKey())
+ state |= AltButton;
+ if (me.shiftKey())
+ state |= ShiftButton;
+ if (me.metaKey())
+ state |= MetaButton;
+ }
+
+// kdDebug(6000) << "sending event to widget "
+// << " pos=" << p << " type=" << type
+// << " button=" << button << " state=" << state << endl;
+ QMouseEvent e(type, p, button, state);
+ QScrollView * sc = ::qt_cast<QScrollView*>(m_widget);
+ if (sc && !::qt_cast<QListBox*>(m_widget))
+ static_cast<ScrollViewEventPropagator *>(sc)->sendEvent(&e);
+ else
+ static_cast<EventPropagator *>(m_widget)->sendEvent(&e);
+ ret = e.isAccepted();
+ break;
+ }
+ case EventImpl::KEYDOWN_EVENT:
+ // do nothing; see the mapping table below
+ break;
+ case EventImpl::KEYUP_EVENT: {
+ if (!ev.isKeyRelatedEvent()) break;
+
+ const KeyEventBaseImpl& domKeyEv = static_cast<const KeyEventBaseImpl &>(ev);
+ if (domKeyEv.isSynthetic() && !acceptsSyntheticEvents()) break;
+
+ QKeyEvent* const ke = domKeyEv.qKeyEvent();
+ static_cast<EventPropagator *>(m_widget)->sendEvent(ke);
+ ret = ke->isAccepted();
+ break;
+ }
+ case EventImpl::KEYPRESS_EVENT: {
+ if (!ev.isKeyRelatedEvent()) break;
+
+ const KeyEventBaseImpl& domKeyEv = static_cast<const KeyEventBaseImpl &>(ev);
+ if (domKeyEv.isSynthetic() && !acceptsSyntheticEvents()) break;
+
+ // See KHTMLView::dispatchKeyEvent: autorepeat is just keypress in the DOM
+ // but it's keyrelease+keypress in Qt. So here we do the inverse mapping as
+ // the one done in KHTMLView: generate two events for one DOM auto-repeat keypress.
+ // Similarly, DOM keypress events with non-autorepeat Qt event do nothing here,
+ // because the matching Qt keypress event was already sent from DOM keydown event.
+
+ // Reverse drawing as the one in KHTMLView:
+ // DOM: Down Press | Press | Up
+ // Qt: (nothing) Press | Release(autorepeat) + Press(autorepeat) | Release
+ //
+ // Qt::KeyPress is sent for DOM keypress and not DOM keydown to allow
+ // sites to block a key with onkeypress, #99749
+
+ QKeyEvent* const ke = domKeyEv.qKeyEvent();
+ if (ke->isAutoRepeat()) {
+ QKeyEvent releaseEv( QEvent::KeyRelease, ke->key(), ke->ascii(), ke->state(),
+ ke->text(), ke->isAutoRepeat(), ke->count() );
+ static_cast<EventPropagator *>(m_widget)->sendEvent(&releaseEv);
+ }
+ static_cast<EventPropagator *>(m_widget)->sendEvent(ke);
+ ret = ke->isAccepted();
+ break;
+ }
+ case EventImpl::MOUSEOUT_EVENT: {
+ QEvent moe( QEvent::Leave );
+ QApplication::sendEvent(m_widget, &moe);
+ break;
+ }
+ case EventImpl::MOUSEOVER_EVENT: {
+ QEvent moe( QEvent::Enter );
+ QApplication::sendEvent(m_widget, &moe);
+ view()->part()->resetHoverText();
+ break;
+ }
+ default:
+ break;
+ }
+ return ret;
+}
+
+void RenderWidget::deref()
+{
+ if (_ref) _ref--;
+// qDebug( "deref(%p): width get count is %d", this, _ref);
+ if (!_ref) {
+ khtml::SharedPtr<RenderArena> guard(m_arena); //Since delete on us gets called -first-,
+ //before the arena free
+ arenaDelete(m_arena.get());
+ }
+}
+
+FindSelectionResult RenderReplaced::checkSelectionPoint(int _x, int _y, int _tx, int _ty, DOM::NodeImpl*& node, int &offset, SelPointState &)
+{
+#if 0
+ kdDebug(6040) << "RenderReplaced::checkSelectionPoint(_x="<<_x<<",_y="<<_y<<",_tx="<<_tx<<",_ty="<<_ty<<")" << endl
+ << "xPos: " << xPos() << " yPos: " << yPos() << " width: " << width() << " height: " << height() << endl
+ << "_ty + yPos: " << (_ty + yPos()) << " + height: " << (_ty + yPos() + height()) << "; _tx + xPos: " << (_tx + xPos()) << " + width: " << (_tx + xPos() + width()) << endl;
+#endif
+ node = element();
+ offset = 0;
+
+ if ( _y < _ty + yPos() )
+ return SelectionPointBefore; // above -> before
+
+ if ( _y > _ty + yPos() + height() ) {
+ // below -> after
+ // Set the offset to the max
+ offset = 1;
+ return SelectionPointAfter;
+ }
+ if ( _x > _tx + xPos() + width() ) {
+ // to the right
+ // ### how to regard bidi in replaced elements? (LS)
+ offset = 1;
+ return SelectionPointAfterInLine;
+ }
+
+ // The Y matches, check if we're on the left
+ if ( _x < _tx + xPos() ) {
+ // ### how to regard bidi in replaced elements? (LS)
+ return SelectionPointBeforeInLine;
+ }
+
+ offset = _x > _tx + xPos() + width()/2;
+ return SelectionPointInside;
+}
+
+#ifdef ENABLE_DUMP
+void RenderWidget::dump(QTextStream &stream, const QString &ind) const
+{
+ RenderReplaced::dump(stream,ind);
+ if ( widget() )
+ stream << " color=" << widget()->foregroundColor().name()
+ << " bg=" << widget()->backgroundColor().name();
+ else
+ stream << " null widget";
+}
+#endif
+
+#include "render_replaced.moc"
+
diff --git a/khtml/rendering/render_replaced.h b/khtml/rendering/render_replaced.h
new file mode 100644
index 000000000..97c14a0a8
--- /dev/null
+++ b/khtml/rendering/render_replaced.h
@@ -0,0 +1,169 @@
+/*
+ * This file is part of the HTML widget for KDE.
+ *
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef render_replaced_h
+#define render_replaced_h
+
+#include "rendering/render_block.h"
+#include <qobject.h>
+#include <qscrollview.h>
+
+class KHTMLView;
+class QWidget;
+
+namespace DOM
+{
+ class MouseEventImpl;
+}
+
+namespace khtml {
+
+class RenderReplaced : public RenderBox
+{
+public:
+ RenderReplaced(DOM::NodeImpl* node);
+
+ virtual const char *renderName() const { return "RenderReplaced"; }
+ virtual bool isRenderReplaced() const { return true; }
+
+ virtual bool childAllowed() const { return false; }
+
+ virtual void calcMinMaxWidth();
+
+ virtual short intrinsicWidth() const { return m_intrinsicWidth; }
+ virtual int intrinsicHeight() const { return m_intrinsicHeight; }
+
+ void setIntrinsicWidth(int w) { m_intrinsicWidth = w; }
+ void setIntrinsicHeight(int h) { m_intrinsicHeight = h; }
+
+ virtual void position(InlineBox*, int, int, bool);
+
+ // Return before, after (offset set to max), or inside the replaced element,
+ // at @p offset
+ virtual FindSelectionResult checkSelectionPoint( int _x, int _y, int _tx, int _ty,
+ DOM::NodeImpl*& node, int & offset,
+ SelPointState & );
+
+ /** returns the lowest possible value the caret offset may have to
+ * still point to a valid position.
+ *
+ * Returns 0.
+ */
+ virtual long minOffset() const { return 0; }
+ /** returns the highest possible value the caret offset may have to
+ * still point to a valid position.
+ *
+ * Returns 0.
+ */
+ virtual long maxOffset() const { return 0; }
+
+protected:
+ short m_intrinsicWidth;
+ short m_intrinsicHeight;
+};
+
+
+class RenderWidget : public QObject, public RenderReplaced, public khtml::Shared<RenderWidget>
+{
+ Q_OBJECT
+public:
+ RenderWidget(DOM::NodeImpl* node);
+ virtual ~RenderWidget();
+
+ virtual void setStyle(RenderStyle *style);
+ virtual void paint( PaintInfo& i, int tx, int ty );
+ virtual bool isWidget() const { return true; };
+
+ virtual bool isFrame() const { return false; }
+
+ virtual void detach( );
+ virtual void layout( );
+
+ virtual void updateFromElement();
+
+ QWidget *widget() const { return m_widget; }
+ KHTMLView* view() const { return m_view; }
+
+ void deref();
+
+ void cancelPendingResize();
+ bool needsMask() const { return m_needsMask; }
+
+ static void paintWidget(PaintInfo& pI, QWidget *widget, int tx, int ty);
+ virtual bool handleEvent(const DOM::EventImpl& ev);
+
+#ifdef ENABLE_DUMP
+ virtual void dump(QTextStream &stream, const QString &ind) const;
+#endif
+
+ // for ECMA to flush all pending resizes
+ KDE_EXPORT static void flushWidgetResizes();
+
+public slots:
+ void slotWidgetDestructed();
+ bool isKHTMLWidget() const { return m_isKHTMLWidget; }
+
+protected:
+ virtual bool canHaveBorder() const { return false; }
+
+ virtual bool acceptsSyntheticEvents() const { return true; }
+
+ virtual void handleFocusOut() {}
+ bool event( QEvent *e );
+
+ bool eventFilter(QObject* /*o*/, QEvent* e);
+ void setQWidget(QWidget *widget);
+ void resizeWidget( int w, int h );
+
+ QWidget *m_widget;
+ KHTMLView* m_view;
+
+ //Because we mess with normal detach due to ref/deref,
+ //we need to keep track of the arena ourselves
+ //so it doesn't get yanked from us, etc.
+ SharedPtr<RenderArena> m_arena;
+
+ bool m_resizePending;
+ bool m_discardResizes;
+ bool m_isKHTMLWidget;
+ bool m_needsMask;
+
+public:
+ virtual int borderTop() const { return canHaveBorder() ? RenderReplaced::borderTop() : 0; }
+ virtual int borderBottom() const { return canHaveBorder() ? RenderReplaced::borderBottom() : 0; }
+ virtual int borderLeft() const { return canHaveBorder() ? RenderReplaced::borderLeft() : 0; }
+ virtual int borderRight() const { return canHaveBorder() ? RenderReplaced::borderRight() : 0; }
+
+ class EventPropagator : public QWidget {
+ public:
+ void sendEvent(QEvent *e);
+ };
+ class ScrollViewEventPropagator : public QScrollView {
+ public:
+ void sendEvent(QEvent *e);
+ };
+};
+
+extern bool allowWidgetPaintEvents;
+
+}
+
+#endif
diff --git a/khtml/rendering/render_style.cpp b/khtml/rendering/render_style.cpp
new file mode 100644
index 000000000..936964b98
--- /dev/null
+++ b/khtml/rendering/render_style.cpp
@@ -0,0 +1,1300 @@
+/*
+ * This file is part of the DOM implementation for KDE.
+ *
+ * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
+ * Copyright (C) 2002-2003 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2002-2005 Apple Computer, Inc.
+ * Copyright (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com)
+ *
+ * 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 "xml/dom_stringimpl.h"
+#include "css/cssstyleselector.h"
+#include "css/css_valueimpl.h"
+#include "render_style.h"
+
+#include "kdebug.h"
+
+using namespace khtml;
+using namespace DOM;
+
+/* CSS says Fixed for the default padding value, but we treat variable as 0 padding anyways, and like
+ * this is works fine for table paddings aswell
+ */
+StyleSurroundData::StyleSurroundData()
+ : margin( Fixed ), padding( Variable )
+{
+}
+
+StyleSurroundData::StyleSurroundData(const StyleSurroundData& o )
+ : Shared<StyleSurroundData>(),
+ offset( o.offset ), margin( o.margin ), padding( o.padding ),
+ border( o.border )
+{
+}
+
+bool StyleSurroundData::operator==(const StyleSurroundData& o) const
+{
+ return offset==o.offset && margin==o.margin &&
+ padding==o.padding && border==o.border;
+}
+
+StyleBoxData::StyleBoxData()
+ : z_index( 0 ), z_auto( true )
+{
+ min_width = min_height = RenderStyle::initialMinSize();
+ max_width = max_height = RenderStyle::initialMaxSize();
+ box_sizing = RenderStyle::initialBoxSizing();
+}
+
+StyleBoxData::StyleBoxData(const StyleBoxData& o )
+ : Shared<StyleBoxData>(),
+ width( o.width ), height( o.height ),
+ min_width( o.min_width ), max_width( o.max_width ),
+ min_height ( o.min_height ), max_height( o.max_height ),
+ box_sizing( o.box_sizing),
+ z_index( o.z_index ), z_auto( o.z_auto )
+{
+}
+
+bool StyleBoxData::operator==(const StyleBoxData& o) const
+{
+ return
+ width == o.width &&
+ height == o.height &&
+ min_width == o.min_width &&
+ max_width == o.max_width &&
+ min_height == o.min_height &&
+ max_height == o.max_height &&
+ box_sizing == o.box_sizing &&
+ z_index == o.z_index &&
+ z_auto == o.z_auto;
+}
+
+StyleVisualData::StyleVisualData()
+ : textDecoration(RenderStyle::initialTextDecoration()),
+ palette( QApplication::palette() )
+{
+}
+
+StyleVisualData::~StyleVisualData() {
+}
+
+StyleVisualData::StyleVisualData(const StyleVisualData& o )
+ : Shared<StyleVisualData>(),
+ clip( o.clip ), textDecoration(o.textDecoration),
+ palette( o.palette )
+{
+}
+
+BackgroundLayer::BackgroundLayer()
+:m_image(RenderStyle::initialBackgroundImage()),
+ m_bgAttachment(RenderStyle::initialBackgroundAttachment()),
+ m_bgClip(RenderStyle::initialBackgroundClip()),
+ m_bgOrigin(RenderStyle::initialBackgroundOrigin()),
+ m_bgRepeat(RenderStyle::initialBackgroundRepeat()),
+ m_backgroundSize(RenderStyle::initialBackgroundSize()),
+ m_next(0)
+{
+ m_imageSet = m_attachmentSet = m_clipSet = m_originSet =
+ m_repeatSet = m_xPosSet = m_yPosSet = m_backgroundSizeSet = false;
+}
+
+BackgroundLayer::BackgroundLayer(const BackgroundLayer& o)
+{
+ m_next = o.m_next ? new BackgroundLayer(*o.m_next) : 0;
+ m_image = o.m_image;
+ m_xPosition = o.m_xPosition;
+ m_yPosition = o.m_yPosition;
+ m_bgAttachment = o.m_bgAttachment;
+ m_bgClip = o.m_bgClip;
+ m_bgOrigin = o.m_bgOrigin;
+ m_bgRepeat = o.m_bgRepeat;
+ m_backgroundSize = o.m_backgroundSize;
+ m_imageSet = o.m_imageSet;
+ m_attachmentSet = o.m_attachmentSet;
+ m_clipSet = o.m_clipSet;
+ m_originSet = o.m_originSet;
+ m_repeatSet = o.m_repeatSet;
+ m_xPosSet = o.m_xPosSet;
+ m_yPosSet = o.m_yPosSet;
+ m_backgroundSizeSet = o.m_backgroundSizeSet;
+}
+
+BackgroundLayer::~BackgroundLayer()
+{
+ delete m_next;
+}
+
+BackgroundLayer& BackgroundLayer::operator=(const BackgroundLayer& o) {
+ if (m_next != o.m_next) {
+ delete m_next;
+ m_next = o.m_next ? new BackgroundLayer(*o.m_next) : 0;
+ }
+
+ m_image = o.m_image;
+ m_xPosition = o.m_xPosition;
+ m_yPosition = o.m_yPosition;
+ m_bgAttachment = o.m_bgAttachment;
+ m_bgClip = o.m_bgClip;
+ m_bgOrigin = o.m_bgOrigin;
+ m_bgRepeat = o.m_bgRepeat;
+ m_backgroundSize = o.m_backgroundSize;
+
+ m_imageSet = o.m_imageSet;
+ m_attachmentSet = o.m_attachmentSet;
+ m_originSet = o.m_originSet;
+ m_repeatSet = o.m_repeatSet;
+ m_xPosSet = o.m_xPosSet;
+ m_yPosSet = o.m_yPosSet;
+ m_backgroundSizeSet = o.m_backgroundSizeSet;
+
+ return *this;
+}
+
+bool BackgroundLayer::operator==(const BackgroundLayer& o) const {
+ return m_image == o.m_image && m_xPosition == o.m_xPosition && m_yPosition == o.m_yPosition &&
+ m_bgAttachment == o.m_bgAttachment && m_bgClip == o.m_bgClip && m_bgOrigin == o.m_bgOrigin && m_bgRepeat == o.m_bgRepeat &&
+ m_backgroundSize.width == o.m_backgroundSize.width && m_backgroundSize.height == o.m_backgroundSize.height &&
+ m_imageSet == o.m_imageSet && m_attachmentSet == o.m_attachmentSet && m_repeatSet == o.m_repeatSet &&
+ m_xPosSet == o.m_xPosSet && m_yPosSet == o.m_yPosSet && m_backgroundSizeSet == o.m_backgroundSizeSet &&
+ ((m_next && o.m_next) ? *m_next == *o.m_next : m_next == o.m_next);
+}
+
+void BackgroundLayer::fillUnsetProperties()
+{
+ BackgroundLayer* curr;
+ for (curr = this; curr && curr->isBackgroundImageSet(); curr = curr->next());
+ if (curr && curr != this) {
+ // We need to fill in the remaining values with the pattern specified.
+ for (BackgroundLayer* pattern = this; curr; curr = curr->next()) {
+ curr->m_image = pattern->m_image;
+ pattern = pattern->next();
+ if (pattern == curr || !pattern)
+ pattern = this;
+ }
+ }
+
+ for (curr = this; curr && curr->isBackgroundXPositionSet(); curr = curr->next());
+ if (curr && curr != this) {
+ // We need to fill in the remaining values with the pattern specified.
+ for (BackgroundLayer* pattern = this; curr; curr = curr->next()) {
+ curr->m_xPosition = pattern->m_xPosition;
+ pattern = pattern->next();
+ if (pattern == curr || !pattern)
+ pattern = this;
+ }
+ }
+
+ for (curr = this; curr && curr->isBackgroundYPositionSet(); curr = curr->next());
+ if (curr && curr != this) {
+ // We need to fill in the remaining values with the pattern specified.
+ for (BackgroundLayer* pattern = this; curr; curr = curr->next()) {
+ curr->m_yPosition = pattern->m_yPosition;
+ pattern = pattern->next();
+ if (pattern == curr || !pattern)
+ pattern = this;
+ }
+ }
+
+ for (curr = this; curr && curr->isBackgroundAttachmentSet(); curr = curr->next());
+ if (curr && curr != this) {
+ // We need to fill in the remaining values with the pattern specified.
+ for (BackgroundLayer* pattern = this; curr; curr = curr->next()) {
+ curr->m_bgAttachment = pattern->m_bgAttachment;
+ pattern = pattern->next();
+ if (pattern == curr || !pattern)
+ pattern = this;
+ }
+ }
+
+ for (curr = this; curr && curr->isBackgroundClipSet(); curr = curr->next());
+ if (curr && curr != this) {
+ // We need to fill in the remaining values with the pattern specified.
+ for (BackgroundLayer* pattern = this; curr; curr = curr->next()) {
+ curr->m_bgClip = pattern->m_bgClip;
+ pattern = pattern->next();
+ if (pattern == curr || !pattern)
+ pattern = this;
+ }
+ }
+
+ for (curr = this; curr && curr->isBackgroundOriginSet(); curr = curr->next());
+ if (curr && curr != this) {
+ // We need to fill in the remaining values with the pattern specified.
+ for (BackgroundLayer* pattern = this; curr; curr = curr->next()) {
+ curr->m_bgOrigin = pattern->m_bgOrigin;
+ pattern = pattern->next();
+ if (pattern == curr || !pattern)
+ pattern = this;
+ }
+ }
+
+ for (curr = this; curr && curr->isBackgroundRepeatSet(); curr = curr->next());
+ if (curr && curr != this) {
+ // We need to fill in the remaining values with the pattern specified.
+ for (BackgroundLayer* pattern = this; curr; curr = curr->next()) {
+ curr->m_bgRepeat = pattern->m_bgRepeat;
+ pattern = pattern->next();
+ if (pattern == curr || !pattern)
+ pattern = this;
+ }
+ }
+
+ for (curr = this; curr && curr->isBackgroundSizeSet(); curr = curr->next());
+ if (curr && curr != this) {
+ // We need to fill in the remaining values with the pattern specified.
+ for (BackgroundLayer* pattern = this; curr; curr = curr->next()) {
+ curr->m_backgroundSize = pattern->m_backgroundSize;
+ pattern = pattern->next();
+ if (pattern == curr || !pattern)
+ pattern = this;
+ }
+ }
+}
+
+void BackgroundLayer::cullEmptyLayers()
+{
+ BackgroundLayer *next;
+ for (BackgroundLayer *p = this; p; p = next) {
+ next = p->m_next;
+ if (next && !next->isBackgroundImageSet() &&
+ !next->isBackgroundXPositionSet() && !next->isBackgroundYPositionSet() &&
+ !next->isBackgroundAttachmentSet() && !next->isBackgroundClipSet() &&
+ !next->isBackgroundOriginSet() && !next->isBackgroundRepeatSet() &&
+ !next->isBackgroundSizeSet()) {
+ delete next;
+ p->m_next = 0;
+ break;
+ }
+ }
+}
+
+StyleBackgroundData::StyleBackgroundData()
+{}
+
+StyleBackgroundData::StyleBackgroundData(const StyleBackgroundData& o)
+ : Shared<StyleBackgroundData>(), m_background(o.m_background), m_outline(o.m_outline)
+{}
+
+bool StyleBackgroundData::operator==(const StyleBackgroundData& o) const
+{
+ return m_background == o.m_background && m_color == o.m_color && m_outline == o.m_outline;
+}
+
+StyleGeneratedData::StyleGeneratedData() : Shared<StyleGeneratedData>(), content(0), counter_reset(0), counter_increment(0) {}
+
+StyleGeneratedData::~StyleGeneratedData()
+{
+ if (counter_reset) counter_reset->deref();
+ if (counter_increment) counter_increment->deref();
+ delete content;
+}
+
+StyleGeneratedData::StyleGeneratedData(const StyleGeneratedData& o)
+ : Shared<StyleGeneratedData>(), content(0),
+ counter_reset(o.counter_reset), counter_increment(o.counter_increment)
+{
+ if (o.content) content = new ContentData(*o.content);
+ if (counter_reset) counter_reset->ref();
+ if (counter_increment) counter_increment->ref();
+}
+
+bool StyleGeneratedData::contentDataEquivalent(const StyleGeneratedData* otherStyle) const
+{
+ ContentData* c1 = content;
+ ContentData* c2 = otherStyle->content;
+
+ while (c1 && c2) {
+ if (c1->_contentType != c2->_contentType)
+ return false;
+ if (c1->_contentType == CONTENT_TEXT) {
+ DOM::DOMString c1Str(c1->_content.text);
+ DOM::DOMString c2Str(c2->_content.text);
+ if (c1Str != c2Str)
+ return false;
+ }
+ else if (c1->_contentType == CONTENT_OBJECT) {
+ if (c1->_content.object != c2->_content.object)
+ return false;
+ }
+ else if (c1->_contentType == CONTENT_COUNTER) {
+ if (c1->_content.counter != c2->_content.counter)
+ return false;
+ }
+ else if (c1->_contentType == CONTENT_QUOTE) {
+ if (c1->_content.quote != c2->_content.quote)
+ return false;
+ }
+
+ c1 = c1->_nextContent;
+ c2 = c2->_nextContent;
+ }
+
+ return !c1 && !c2;
+}
+
+static bool compareCounterActList(const CSSValueListImpl* ca, const CSSValueListImpl* cb) {
+ // weeee....
+ CSSValueListImpl* a = const_cast<CSSValueListImpl*>(ca);
+ CSSValueListImpl* b = const_cast<CSSValueListImpl*>(cb);
+
+ if (!a && !b) return true;
+ if (!a || !b) return false;
+ if (a->length() != b->length()) return false;
+ for(uint i=0; i< a->length(); i++) {
+ CSSValueImpl *ai = a->item(i);
+ CSSValueImpl *bi = b->item(i);
+ assert(ai && ai->cssValueType() == CSSValue::CSS_CUSTOM);
+ assert(bi && bi->cssValueType() == CSSValue::CSS_CUSTOM);
+ CounterActImpl* caa = static_cast<CounterActImpl*>(ai);
+ CounterActImpl* cab = static_cast<CounterActImpl*>(bi);
+ if (caa->value() != cab->value()) return false;
+ if (caa->counter() != cab->counter()) return false;
+ }
+ return true;
+}
+
+bool StyleGeneratedData::counterDataEquivalent(const StyleGeneratedData* otherStyle) const
+{
+ return compareCounterActList(counter_reset, otherStyle->counter_reset) &&
+ compareCounterActList(counter_increment, otherStyle->counter_increment);
+}
+
+bool StyleGeneratedData::operator==(const StyleGeneratedData& o) const
+{
+ return contentDataEquivalent(&o) && counterDataEquivalent(&o);
+}
+
+StyleMarqueeData::StyleMarqueeData()
+{
+ increment = RenderStyle::initialMarqueeIncrement();
+ speed = RenderStyle::initialMarqueeSpeed();
+ direction = RenderStyle::initialMarqueeDirection();
+ behavior = RenderStyle::initialMarqueeBehavior();
+ loops = RenderStyle::initialMarqueeLoopCount();
+}
+
+StyleMarqueeData::StyleMarqueeData(const StyleMarqueeData& o)
+:Shared<StyleMarqueeData>(), increment(o.increment), speed(o.speed), loops(o.loops),
+ behavior(o.behavior), direction(o.direction)
+{}
+
+bool StyleMarqueeData::operator==(const StyleMarqueeData& o) const
+{
+ return (increment == o.increment && speed == o.speed && direction == o.direction &&
+ behavior == o.behavior && loops == o.loops);
+}
+
+StyleCSS3NonInheritedData::StyleCSS3NonInheritedData()
+:Shared<StyleCSS3NonInheritedData>()
+, opacity(RenderStyle::initialOpacity())
+{
+}
+
+StyleCSS3NonInheritedData::StyleCSS3NonInheritedData(const StyleCSS3NonInheritedData& o)
+:Shared<StyleCSS3NonInheritedData>(),
+ opacity(o.opacity),
+#ifdef APPLE_CHANGES
+ flexibleBox(o.flexibleBox),
+#endif
+ marquee(o.marquee)
+{
+}
+
+bool StyleCSS3NonInheritedData::operator==(const StyleCSS3NonInheritedData& o) const
+{
+ return
+ opacity == o.opacity &&
+#ifdef APPLE_CHANGES
+ flexibleBox == o.flexibleBox &&
+#endif
+ marquee == o.marquee;
+}
+
+StyleCSS3InheritedData::StyleCSS3InheritedData()
+:Shared<StyleCSS3InheritedData>(), textShadow(0)
+#ifdef APPLE_CHANGES
+, userModify(READ_ONLY), textSizeAdjust(RenderStyle::initialTextSizeAdjust())
+#endif
+{
+
+}
+
+StyleCSS3InheritedData::StyleCSS3InheritedData(const StyleCSS3InheritedData& o)
+:Shared<StyleCSS3InheritedData>()
+{
+ textShadow = o.textShadow ? new ShadowData(*o.textShadow) : 0;
+#ifdef APPLE_CHANGES
+ userModify = o.userModify;
+ textSizeAdjust = o.textSizeAdjust;
+#endif
+}
+
+StyleCSS3InheritedData::~StyleCSS3InheritedData()
+{
+ delete textShadow;
+}
+
+bool StyleCSS3InheritedData::operator==(const StyleCSS3InheritedData& o) const
+{
+ return shadowDataEquivalent(o)
+#ifdef APPLE_CHANGES
+ && (userModify == o.userModify) && (textSizeAdjust == o.textSizeAdjust)
+#endif
+ ;
+}
+
+bool StyleCSS3InheritedData::shadowDataEquivalent(const StyleCSS3InheritedData& o) const
+{
+ if (!textShadow && o.textShadow || textShadow && !o.textShadow)
+ return false;
+ if (textShadow && o.textShadow && (*textShadow != *o.textShadow))
+ return false;
+ return true;
+}
+
+StyleInheritedData::StyleInheritedData()
+ : indent( RenderStyle::initialTextIndent() ), line_height( RenderStyle::initialLineHeight() ),
+ style_image( RenderStyle::initialListStyleImage() ),
+ font(), color( RenderStyle::initialColor() ),
+ border_hspacing( RenderStyle::initialBorderHorizontalSpacing() ),
+ border_vspacing( RenderStyle::initialBorderVerticalSpacing() ),
+ widows( RenderStyle::initialWidows() ), orphans( RenderStyle::initialOrphans() ),
+ quotes(0)
+{
+}
+
+StyleInheritedData::~StyleInheritedData()
+{
+ if (quotes) quotes->deref();
+}
+
+StyleInheritedData::StyleInheritedData(const StyleInheritedData& o )
+ : Shared<StyleInheritedData>(),
+ indent( o.indent ), line_height( o.line_height ), style_image( o.style_image ),
+ font( o.font ), color( o.color ),
+ border_hspacing( o.border_hspacing ),
+ border_vspacing( o.border_vspacing ),
+ widows(o.widows), orphans(o.orphans)
+{
+ quotes = o.quotes;
+ if (quotes) quotes->ref();
+}
+
+bool StyleInheritedData::operator==(const StyleInheritedData& o) const
+{
+ return
+ indent == o.indent &&
+ line_height == o.line_height &&
+ border_hspacing == o.border_hspacing &&
+ border_vspacing == o.border_vspacing &&
+ style_image == o.style_image &&
+ font == o.font &&
+ color == o.color &&
+ border_hspacing == o.border_hspacing &&
+ border_vspacing == o.border_vspacing &&
+ quotes == o.quotes &&
+ widows == o.widows &&
+ orphans == o.orphans ;
+
+ // doesn't work because structs are not packed
+ //return memcmp(this, &o, sizeof(*this))==0;
+}
+
+RenderStyle::RenderStyle()
+{
+// counter++;
+ if (!_default)
+ _default = new RenderStyle(true);
+
+ box = _default->box;
+ visual = _default->visual;
+ background = _default->background;
+ surround = _default->surround;
+ generated = _default->generated;
+ css3NonInheritedData = _default->css3NonInheritedData;
+ css3InheritedData = _default->css3InheritedData;
+
+ inherited = _default->inherited;
+
+ setBitDefaults();
+
+ pseudoStyle = 0;
+}
+
+RenderStyle::RenderStyle(bool)
+{
+ setBitDefaults();
+
+ box.init();
+ visual.init();
+ background.init();
+ surround.init();
+ generated.init();
+ css3NonInheritedData.init();
+#ifdef APPLE_CHANGES // ### yet to be merged
+ css3NonInheritedData.access()->flexibleBox.init();
+#endif
+ css3NonInheritedData.access()->marquee.init();
+ css3InheritedData.init();
+ inherited.init();
+
+ pseudoStyle = 0;
+}
+
+RenderStyle::RenderStyle(const RenderStyle& o)
+ : Shared<RenderStyle>(),
+ inherited_flags( o.inherited_flags ), noninherited_flags( o.noninherited_flags ),
+ box( o.box ), visual( o.visual ), background( o.background ), surround( o.surround ), generated(o.generated),
+ css3NonInheritedData( o.css3NonInheritedData ), css3InheritedData( o.css3InheritedData ),
+ inherited( o.inherited ), pseudoStyle( 0 )
+{}
+
+void RenderStyle::inheritFrom(const RenderStyle* inheritParent)
+{
+ css3InheritedData = inheritParent->css3InheritedData;
+ inherited = inheritParent->inherited;
+ inherited_flags = inheritParent->inherited_flags;
+
+ // Simulate ":after,:before { white-space: pre-line }"
+ if (styleType() == AFTER || styleType() == BEFORE)
+ setWhiteSpace(PRE_LINE);
+}
+
+RenderStyle::~RenderStyle()
+{
+ RenderStyle *ps = pseudoStyle;
+ RenderStyle *prev = 0;
+
+ while (ps) {
+ prev = ps;
+ ps = ps->pseudoStyle;
+ // to prevent a double deletion.
+ // this works only because the styles below aren't really shared
+ // Dirk said we need another construct as soon as these are shared
+ prev->pseudoStyle = 0;
+ prev->deref();
+ }
+}
+
+bool RenderStyle::operator==(const RenderStyle& o) const
+{
+// compare everything except the pseudoStyle pointer
+ return (inherited_flags == o.inherited_flags &&
+ noninherited_flags == o.noninherited_flags &&
+ box == o.box &&
+ visual == o.visual &&
+ background == o.background &&
+ surround == o.surround &&
+ generated == o.generated &&
+ css3NonInheritedData == o.css3NonInheritedData &&
+ css3InheritedData == o.css3InheritedData &&
+ inherited == o.inherited);
+}
+
+enum EPseudoBit { NO_BIT = 0x0,
+ FIRST_LINE_BIT = 0x1, FIRST_LETTER_BIT = 0x2, SELECTION_BIT = 0x4,
+ BEFORE_BIT = 0x8, AFTER_BIT = 0x10, MARKER_BIT = 0x20,
+ REPLACED_BIT = 0x40
+ };
+
+static int pseudoBit(RenderStyle::PseudoId pseudo)
+{
+ switch (pseudo) {
+ case RenderStyle::BEFORE:
+ return BEFORE_BIT;
+ case RenderStyle::AFTER:
+ return AFTER_BIT;
+ case RenderStyle::MARKER:
+ return MARKER_BIT;
+ case RenderStyle::REPLACED:
+ return REPLACED_BIT;
+ case RenderStyle::FIRST_LINE:
+ return FIRST_LINE_BIT;
+ case RenderStyle::FIRST_LETTER:
+ return FIRST_LETTER_BIT;
+ case RenderStyle::SELECTION:
+ return SELECTION_BIT;
+ default:
+ return NO_BIT;
+ }
+}
+
+bool RenderStyle::hasPseudoStyle(PseudoId pseudo) const
+{
+ return (pseudoBit(pseudo) & noninherited_flags.f._pseudoBits) != 0;
+}
+
+void RenderStyle::setHasPseudoStyle(PseudoId pseudo, bool b)
+{
+ if (b)
+ noninherited_flags.f._pseudoBits |= pseudoBit(pseudo);
+ else
+ noninherited_flags.f._pseudoBits &= ~(pseudoBit(pseudo));
+}
+
+RenderStyle* RenderStyle::getPseudoStyle(PseudoId pid) const
+{
+ if (!hasPseudoStyle(pid)) return 0;
+
+ RenderStyle *ps = 0;
+ if (noninherited_flags.f._styleType==NOPSEUDO)
+ for (ps = pseudoStyle; ps; ps = ps->pseudoStyle)
+ if (ps->noninherited_flags.f._styleType==pid)
+ break;
+ return ps;
+}
+
+RenderStyle* RenderStyle::addPseudoStyle(PseudoId pid)
+{
+ if (hasPseudoStyle(pid)) return getPseudoStyle(pid);
+
+ RenderStyle *ps = 0;
+
+ switch (pid) {
+ case FIRST_LETTER: // pseudo-elements (FIRST_LINE has a special handling)
+ case SELECTION:
+ case BEFORE:
+ case AFTER:
+ ps = new RenderStyle();
+ break;
+ default:
+ ps = new RenderStyle(*this); // use the real copy constructor to get an identical copy
+ }
+ ps->ref();
+ ps->noninherited_flags.f._styleType = pid;
+ ps->pseudoStyle = pseudoStyle;
+
+ pseudoStyle = ps;
+
+ setHasPseudoStyle(pid, true);
+
+ return ps;
+}
+
+void RenderStyle::removePseudoStyle(PseudoId pid)
+{
+ RenderStyle *ps = pseudoStyle;
+ RenderStyle *prev = this;
+
+ while (ps) {
+ if (ps->noninherited_flags.f._styleType==pid) {
+ prev->pseudoStyle = ps->pseudoStyle;
+ ps->deref();
+ return;
+ }
+ prev = ps;
+ ps = ps->pseudoStyle;
+ }
+
+ setHasPseudoStyle(pid, false);
+}
+
+
+bool RenderStyle::inheritedNotEqual( RenderStyle *other ) const
+{
+ return
+ (
+ inherited_flags != other->inherited_flags ||
+ inherited != other->inherited ||
+ css3InheritedData != other->css3InheritedData
+ );
+}
+
+/*
+ compares two styles. The result gives an idea of the action that
+ needs to be taken when replacing the old style with a new one.
+
+ CbLayout: The containing block of the object needs a relayout.
+ Layout: the RenderObject needs a relayout after the style change
+ Visible: The change is visible, but no relayout is needed
+ NonVisible: The object does need neither repaint nor relayout after
+ the change.
+
+ ### TODO:
+ A lot can be optimised here based on the display type, lots of
+ optimisations are unimplemented, and currently result in the
+ worst case result causing a relayout of the containing block.
+*/
+RenderStyle::Diff RenderStyle::diff( const RenderStyle *other ) const
+{
+ // we anyway assume they are the same
+// EDisplay _display : 5;
+
+ // NonVisible:
+// ECursor _cursor_style : 4;
+// EUserInput _user_input : 2; as long as :enabled is not impl'd
+
+// ### this needs work to know more exactly if we need a relayout
+// or just a repaint
+
+// non-inherited attributes
+// DataRef<StyleBoxData> box;
+// DataRef<StyleVisualData> visual;
+// DataRef<StyleSurroundData> surround;
+
+// inherited attributes
+// DataRef<StyleInheritedData> inherited;
+
+ if ( *box.get() != *other->box.get() ||
+ *visual.get() != *other->visual.get() ||
+ (*surround.get() != *other->surround.get()
+ && (other->position() == STATIC || other->position() != position())) ||
+ !(inherited->indent == other->inherited->indent) ||
+ !(inherited->line_height == other->inherited->line_height) ||
+ !(inherited->style_image == other->inherited->style_image) ||
+ !(inherited->font == other->inherited->font) ||
+ !(inherited->border_hspacing == other->inherited->border_hspacing) ||
+ !(inherited->border_vspacing == other->inherited->border_vspacing) ||
+ !(inherited_flags.f._visuallyOrdered == other->inherited_flags.f._visuallyOrdered) ||
+ !(inherited_flags.f._htmlHacks == other->inherited_flags.f._htmlHacks) ||
+ !(noninherited_flags.f._textOverflow == other->noninherited_flags.f._textOverflow) )
+ return CbLayout;
+
+ // changes causing Layout changes:
+
+// only for tables:
+// _border_collapse
+// EEmptyCell _empty_cells : 2 ;
+// ECaptionSide _caption_side : 2;
+// ETableLayout _table_layout : 1;
+// EPosition _position : 2;
+// EFloat _floating : 2;
+ if ( ((int)noninherited_flags.f._display) >= TABLE ) {
+ if ( !(inherited_flags.f._empty_cells == other->inherited_flags.f._empty_cells) ||
+ !(inherited_flags.f._caption_side == other->inherited_flags.f._caption_side) ||
+ !(inherited_flags.f._border_collapse == other->inherited_flags.f._border_collapse) ||
+ !(noninherited_flags.f._table_layout == other->noninherited_flags.f._table_layout) ||
+ !(noninherited_flags.f._position == other->noninherited_flags.f._position) ||
+ !(noninherited_flags.f._floating == other->noninherited_flags.f._floating) ||
+ !(noninherited_flags.f._flowAroundFloats == other->noninherited_flags.f._flowAroundFloats) ||
+ !(noninherited_flags.f._unicodeBidi == other->noninherited_flags.f._unicodeBidi) )
+ return CbLayout;
+ }
+
+// only for lists:
+// EListStyleType _list_style_type : 5 ;
+// EListStylePosition _list_style_position :1;
+ if (noninherited_flags.f._display == LIST_ITEM ) {
+ if ( !(inherited_flags.f._list_style_type == other->inherited_flags.f._list_style_type) ||
+ !(inherited_flags.f._list_style_position == other->inherited_flags.f._list_style_position) )
+ return Layout;
+ }
+
+// ### These could be better optimised
+// ETextAlign _text_align : 3;
+// ETextTransform _text_transform : 4;
+// EDirection _direction : 1;
+// EWhiteSpace _white_space : 2;
+// EClear _clear : 2;
+ if ( !(inherited_flags.f._text_align == other->inherited_flags.f._text_align) ||
+ !(inherited_flags.f._text_transform == other->inherited_flags.f._text_transform) ||
+ !(inherited_flags.f._direction == other->inherited_flags.f._direction) ||
+ !(inherited_flags.f._white_space == other->inherited_flags.f._white_space) ||
+ !(noninherited_flags.f._clear == other->noninherited_flags.f._clear)
+ )
+ return Layout;
+
+ // Overflow returns a layout hint.
+ if (noninherited_flags.f._overflowX != other->noninherited_flags.f._overflowX ||
+ noninherited_flags.f._overflowY != other->noninherited_flags.f._overflowY)
+ return Layout;
+
+// only for inline:
+// EVerticalAlign _vertical_align : 4;
+
+ if ( !(noninherited_flags.f._display == INLINE) &&
+ !(noninherited_flags.f._vertical_align == other->noninherited_flags.f._vertical_align) )
+ return Layout;
+
+ if (*surround.get() != *other->surround.get()) {
+ assert( other->position() != STATIC ); // this style is positioned or relatively positioned
+ if ( surround->hasSamePBMData(*other->surround.get()) && // padding/border/margin are identical
+ (other->position() == RELATIVE ||
+ !(other->left().isVariable() && other->right().isVariable()) && // X isn't static
+ !(other->top().isVariable() && other->bottom().isVariable()) )) // neither is Y
+ // therefore only the offset is different
+ return Position;
+ return Layout;
+ }
+
+ // Visible:
+// EVisibility _visibility : 2;
+// int _text_decorations : 4;
+// DataRef<StyleBackgroundData> background;
+ if (inherited->color != other->inherited->color ||
+ !(inherited_flags.f._visibility == other->inherited_flags.f._visibility) ||
+ !(inherited_flags.f._text_decorations == other->inherited_flags.f._text_decorations) ||
+ !(noninherited_flags.f._hasClip == other->noninherited_flags.f._hasClip) ||
+ visual->textDecoration != other->visual->textDecoration ||
+ *background.get() != *other->background.get() ||
+ css3NonInheritedData->opacity != other->css3NonInheritedData->opacity ||
+ !css3InheritedData->shadowDataEquivalent(*other->css3InheritedData.get())
+ )
+ return Visible;
+
+ RenderStyle::Diff ch = Equal;
+ // Check for visible pseudo-changes:
+ if (hasPseudoStyle(FIRST_LINE) != other->hasPseudoStyle(FIRST_LINE))
+ ch = Visible;
+ else
+ if (hasPseudoStyle(FIRST_LINE) && other->hasPseudoStyle(FIRST_LINE))
+ ch = getPseudoStyle(FIRST_LINE)->diff(other->getPseudoStyle(FIRST_LINE));
+
+ if (ch != Equal) return ch;
+
+ // Check for visible pseudo-changes:
+ if (hasPseudoStyle(SELECTION) != other->hasPseudoStyle(SELECTION))
+ ch = Visible;
+ else
+ if (hasPseudoStyle(SELECTION) && other->hasPseudoStyle(SELECTION))
+ ch = getPseudoStyle(SELECTION)->diff(other->getPseudoStyle(SELECTION));
+
+ return ch;
+}
+
+
+RenderStyle* RenderStyle::_default = 0;
+
+void RenderStyle::cleanup()
+{
+ delete _default;
+ _default = 0;
+}
+
+void RenderStyle::setPaletteColor(QPalette::ColorGroup g, QColorGroup::ColorRole r, const QColor& c)
+{
+ visual.access()->palette.setColor(g,r,c);
+}
+
+void RenderStyle::adjustBackgroundLayers()
+{
+ if (backgroundLayers()->next()) {
+ // First we cull out layers that have no properties set.
+ accessBackgroundLayers()->cullEmptyLayers();
+
+ // Next we repeat patterns into layers that don't have some properties set.
+ accessBackgroundLayers()->fillUnsetProperties();
+ }
+}
+
+void RenderStyle::setClip( Length top, Length right, Length bottom, Length left )
+{
+ StyleVisualData *data = visual.access();
+ data->clip.top = top;
+ data->clip.right = right;
+ data->clip.bottom = bottom;
+ data->clip.left = left;
+}
+
+void RenderStyle::setQuotes(DOM::QuotesValueImpl* q)
+{
+ DOM::QuotesValueImpl *t = inherited->quotes;
+ inherited.access()->quotes = q;
+ if (q) q->ref();
+ if (t) t->deref();
+}
+
+QString RenderStyle::openQuote(int level) const
+{
+ if (inherited->quotes)
+ return inherited->quotes->openQuote(level);
+ else
+ return "\""; // 0 is default quotes
+}
+
+QString RenderStyle::closeQuote(int level) const
+{
+ if (inherited->quotes)
+ return inherited->quotes->closeQuote(level);
+ else
+ return "\""; // 0 is default quotes
+}
+
+void RenderStyle::addContent(CachedObject* o)
+{
+ if (!o)
+ return; // The object is null. Nothing to do. Just bail.
+
+ StyleGeneratedData *t_generated = generated.access();
+
+ ContentData* lastContent = t_generated->content;
+ while (lastContent && lastContent->_nextContent)
+ lastContent = lastContent->_nextContent;
+
+ ContentData* newContentData = new ContentData;
+
+ if (lastContent)
+ lastContent->_nextContent = newContentData;
+ else
+ t_generated->content = newContentData;
+
+ // o->ref();
+ newContentData->_content.object = o;
+ newContentData->_contentType = CONTENT_OBJECT;
+}
+
+void RenderStyle::addContent(DOM::DOMStringImpl* s)
+{
+ if (!s)
+ return; // The string is null. Nothing to do. Just bail.
+
+ StyleGeneratedData *t_generated = generated.access();
+
+ ContentData* lastContent = t_generated->content;
+ while (lastContent && lastContent->_nextContent)
+ lastContent = lastContent->_nextContent;
+
+ if (lastContent) {
+ if (lastContent->_contentType == CONTENT_TEXT) {
+ // We can augment the existing string and share this ContentData node.
+ DOMStringImpl* oldStr = lastContent->_content.text;
+ DOMStringImpl* newStr = oldStr->copy();
+ newStr->ref();
+ oldStr->deref();
+ newStr->append(s);
+ lastContent->_content.text = newStr;
+ return;
+ }
+ }
+
+ ContentData* newContentData = new ContentData;
+
+ if (lastContent)
+ lastContent->_nextContent = newContentData;
+ else
+ t_generated->content = newContentData;
+
+ newContentData->_content.text = s;
+ newContentData->_content.text->ref();
+ newContentData->_contentType = CONTENT_TEXT;
+
+}
+
+void RenderStyle::addContent(DOM::CounterImpl* c)
+{
+ if (!c)
+ return;
+
+ StyleGeneratedData *t_generated = generated.access();
+
+ ContentData* lastContent = t_generated->content;
+ while (lastContent && lastContent->_nextContent)
+ lastContent = lastContent->_nextContent;
+
+ ContentData* newContentData = new ContentData;
+
+ if (lastContent)
+ lastContent->_nextContent = newContentData;
+ else
+ t_generated->content = newContentData;
+
+ c->ref();
+ newContentData->_content.counter = c;
+ newContentData->_contentType = CONTENT_COUNTER;
+}
+
+void RenderStyle::addContent(EQuoteContent q)
+{
+ if (q == NO_QUOTE)
+ return;
+
+ StyleGeneratedData *t_generated = generated.access();
+
+ ContentData* lastContent = t_generated->content;
+ while (lastContent && lastContent->_nextContent)
+ lastContent = lastContent->_nextContent;
+
+ ContentData* newContentData = new ContentData;
+
+ if (lastContent)
+ lastContent->_nextContent = newContentData;
+ else
+ t_generated->content = newContentData;
+
+ newContentData->_content.quote = q;
+ newContentData->_contentType = CONTENT_QUOTE;
+}
+
+// content: normal is the same as having no content at all
+void RenderStyle::setContentNormal() {
+ if (generated->content != 0) {
+ delete generated->content;
+ generated.access()->content = 0;
+ }
+}
+
+// content: none, add an empty content node
+void RenderStyle::setContentNone() {
+ setContentNormal();
+ generated.access()->content = new ContentData;
+}
+
+void RenderStyle::setContentData(ContentData *data) {
+ if (data != generated->content) {
+ if (data)
+ generated.access()->content = new ContentData(*data);
+ else
+ generated.access()->content = 0;
+ }
+}
+
+ContentData::ContentData(const ContentData& o) : _contentType(o._contentType)
+{
+ switch (_contentType) {
+ case CONTENT_OBJECT:
+ _content.object = o._content.object;
+ break;
+ case CONTENT_TEXT:
+ _content.text = o._content.text;
+ _content.text->ref();
+ break;
+ case CONTENT_COUNTER:
+ _content.counter = o._content.counter;
+ _content.counter->ref();
+ break;
+ case CONTENT_QUOTE:
+ _content.quote = o._content.quote;
+ break;
+ case CONTENT_NONE:
+ default:
+ break;
+ }
+
+ _nextContent = o._nextContent ? new ContentData(*o._nextContent) : 0;
+}
+
+ContentData::~ContentData()
+{
+ clearContent();
+}
+
+void ContentData::clearContent()
+{
+ delete _nextContent;
+ _nextContent = 0;
+
+ switch (_contentType)
+ {
+ case CONTENT_OBJECT:
+ _content.object = 0;
+ break;
+ case CONTENT_TEXT:
+ _content.text->deref();
+ _content.text = 0;
+ break;
+ case CONTENT_COUNTER:
+ _content.counter->deref();
+ _content.counter = 0;
+ break;
+ case CONTENT_QUOTE:
+ _content.quote = NO_QUOTE;
+ break;
+ default:
+ ;
+ }
+}
+
+void RenderStyle::setTextShadow(ShadowData* val, bool add)
+{
+ StyleCSS3InheritedData* css3Data = css3InheritedData.access();
+ if (!add) {
+ delete css3Data->textShadow;
+ css3Data->textShadow = val;
+ return;
+ }
+
+ ShadowData* last = css3Data->textShadow;
+ while (last->next) last = last->next;
+ last->next = val;
+}
+
+ShadowData::ShadowData(const ShadowData& o)
+:x(o.x), y(o.y), blur(o.blur), color(o.color)
+{
+ next = o.next ? new ShadowData(*o.next) : 0;
+}
+
+bool ShadowData::operator==(const ShadowData& o) const
+{
+ if ((next && !o.next) || (!next && o.next) ||
+ (next && o.next && *next != *o.next))
+ return false;
+
+ return x == o.x && y == o.y && blur == o.blur && color == o.color;
+}
+
+static bool hasCounter(const DOM::DOMString& c, CSSValueListImpl *l)
+{
+ int len = l->length();
+ for(int i=0; i<len; i++) {
+ CounterActImpl* ca = static_cast<CounterActImpl*>(l->item(i));
+ Q_ASSERT(ca != 0);
+ if (ca->m_counter == c) return true;
+ }
+ return false;
+}
+
+bool RenderStyle::hasCounterReset(const DOM::DOMString& c) const
+{
+ if (generated->counter_reset)
+ return hasCounter(c, generated->counter_reset);
+ else
+ return false;
+}
+
+bool RenderStyle::hasCounterIncrement(const DOM::DOMString& c) const
+{
+ if (generated->counter_increment)
+ return hasCounter(c, generated->counter_increment);
+ else
+ return false;
+}
+
+static short readCounter(const DOM::DOMString& c, CSSValueListImpl *l)
+{
+ int len = l->length();
+ for(int i=0; i<len; i++) {
+ CounterActImpl* ca = static_cast<CounterActImpl*>(l->item(i));
+ Q_ASSERT(ca != 0);
+ if (ca->m_counter == c) return ca->m_value;
+ }
+ return 0;
+}
+
+short RenderStyle::counterReset(const DOM::DOMString& c) const
+{
+ if (generated->counter_reset)
+ return readCounter(c, generated->counter_reset);
+ else
+ return 0;
+}
+
+short RenderStyle::counterIncrement(const DOM::DOMString& c) const
+{
+ if (generated->counter_increment)
+ return readCounter(c, generated->counter_increment);
+ else
+ return 0;
+}
+
+void RenderStyle::setCounterReset(CSSValueListImpl *l)
+{
+ CSSValueListImpl *t = generated->counter_reset;
+ generated.access()->counter_reset = l;
+ if (l) l->ref();
+ if (t) t->deref();
+}
+
+void RenderStyle::setCounterIncrement(CSSValueListImpl *l)
+{
+ CSSValueListImpl *t = generated->counter_increment;
+ generated.access()->counter_increment = l;
+ if (l) l->ref();
+ if (t) t->deref();
+}
+
+#ifdef ENABLE_DUMP
+
+static QString describeFont( const QFont &f)
+{
+ QString res = "'" + f.family() + "' ";
+
+ if ( f.pointSize() > 0)
+ res += QString::number( f.pointSize() ) + "pt";
+ else
+ res += QString::number( f.pixelSize() ) + "px";
+
+ if ( f.bold() )
+ res += " bold";
+ if ( f.italic() )
+ res += " italic";
+ if ( f.underline() )
+ res += " underline";
+ if ( f.overline() )
+ res += " overline";
+ if ( f.strikeOut() )
+ res += " strikeout";
+ return res;
+}
+
+QString RenderStyle::createDiff( const RenderStyle &parent ) const
+{
+ QString res;
+ if ( color().isValid() && parent.color() != color() )
+ res += " [color=" + color().name() + "]";
+ if ( backgroundColor().isValid() && parent.backgroundColor() != backgroundColor() )
+ res += " [bgcolor=" + backgroundColor().name() + "]";
+ if ( parent.font() != font() )
+ res += " [font=" + describeFont( font() ) + "]";
+
+ return res;
+}
+#endif
+
+RenderPageStyle::RenderPageStyle() : next(0), m_pageType(ANY_PAGE)
+{
+}
+
+RenderPageStyle::~RenderPageStyle()
+{
+ delete next;
+}
+
+RenderPageStyle* RenderPageStyle::getPageStyle(PageType type)
+{
+ RenderPageStyle *ps = 0;
+ for (ps = this; ps; ps = ps->next)
+ if (ps->m_pageType==type)
+ break;
+ return ps;
+}
+
+RenderPageStyle* RenderPageStyle::addPageStyle(PageType type)
+{
+ RenderPageStyle *ps = getPageStyle(type);
+
+ if (!ps)
+ {
+ ps = new RenderPageStyle(*this); // use the real copy constructor to get an identical copy
+ ps->m_pageType = type;
+
+ ps->next = next;
+ next = ps;
+ }
+
+ return ps;
+}
+
+void RenderPageStyle::removePageStyle(PageType type)
+{
+ RenderPageStyle *ps = next;
+ RenderPageStyle *prev = this;
+
+ while (ps) {
+ if (ps->m_pageType==type) {
+ prev->next = ps->next;
+ delete ps;
+ return;
+ }
+ prev = ps;
+ ps = ps->next;
+ }
+}
diff --git a/khtml/rendering/render_style.h b/khtml/rendering/render_style.h
new file mode 100644
index 000000000..f8c2affd7
--- /dev/null
+++ b/khtml/rendering/render_style.h
@@ -0,0 +1,1517 @@
+/*
+ * This file is part of the DOM implementation for KDE.
+ *
+ * Copyright (C) 2000-2003 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000-2003 Dirk Mueller (mueller@kde.org)
+ * (C) 2003-2005 Apple Computer, Inc.
+ * (C) 2004-2006 Allan Sandfeld Jensen (kde@carewolf.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef RENDERSTYLE_H
+#define RENDERSTYLE_H
+
+/*
+ * WARNING:
+ * --------
+ *
+ * The order of the values in the enums have to agree with the order specified
+ * in cssvalues.in, otherwise some optimizations in the parser will fail,
+ * and produce invaliud results.
+ */
+
+#include <qcolor.h>
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qptrlist.h>
+#include <qpalette.h>
+#include <qapplication.h>
+
+#include "dom/dom_misc.h"
+#include "dom/dom_string.h"
+#include "misc/khtmllayout.h"
+#include "misc/shared.h"
+#include "rendering/font.h"
+
+#include <assert.h>
+
+#define SET_VAR(group,variable,value) \
+ if (!(group->variable == value)) \
+ group.access()->variable = value;
+
+#ifndef ENABLE_DUMP
+#ifndef NDEBUG
+#define ENABLE_DUMP 1
+#endif
+#endif
+
+namespace DOM {
+ class DOMStringImpl;
+ class ShadowValueImpl;
+ class QuotesValueImpl;
+ class CounterImpl;
+ class CSSValueListImpl;
+ class CounterActImpl;
+}
+
+namespace khtml {
+
+ class CachedImage;
+ class CachedObject;
+
+template <class DATA>
+class DataRef
+{
+public:
+
+ DataRef()
+ {
+ data=0;
+ }
+ DataRef( const DataRef<DATA> &d )
+ {
+ data = d.data;
+ data->ref();
+ }
+
+ ~DataRef()
+ {
+ if(data) data->deref();
+ }
+
+ const DATA* operator->() const
+ {
+ return data;
+ }
+
+ const DATA* get() const
+ {
+ return data;
+ }
+
+
+ DATA* access()
+ {
+ if (!data->hasOneRef())
+ {
+ data->deref();
+ data = new DATA(*data);
+ data->ref();
+ }
+ return data;
+ }
+
+ void init()
+ {
+ data = new DATA;
+ data->ref();
+ }
+
+ DataRef<DATA>& operator=(const DataRef<DATA>& d)
+ {
+ if (data==d.data)
+ return *this;
+ if (data)
+ data->deref();
+ data = d.data;
+
+ data->ref();
+
+ return *this;
+ }
+
+ bool operator == ( const DataRef<DATA> &o ) const {
+ return (*data == *(o.data) );
+ }
+ bool operator != ( const DataRef<DATA> &o ) const {
+ return (*data != *(o.data) );
+ }
+
+private:
+ DATA* data;
+};
+
+
+//------------------------------------------------
+
+//------------------------------------------------
+// Box model attributes. Not inherited.
+
+struct LengthBox
+{
+ LengthBox()
+ {
+ }
+ LengthBox( LengthType t )
+ : left( t ), right ( t ), top( t ), bottom( t ) {}
+
+ Length left;
+ Length right;
+ Length top;
+ Length bottom;
+ Length& operator=(Length& len)
+ {
+ left=len;
+ right=len;
+ top=len;
+ bottom=len;
+ return len;
+ }
+
+ bool operator==(const LengthBox& o) const
+ {
+ return left==o.left && right==o.right && top==o.top && bottom==o.bottom;
+ }
+
+
+ bool nonZero() const { return left.value() || right.value() || top.value() || bottom.value(); }
+};
+
+
+
+enum EPosition {
+ STATIC, RELATIVE, ABSOLUTE, FIXED
+};
+
+enum EFloat {
+ FNONE = 0, FLEFT = 0x01, FRIGHT = 0x02, FLEFT_ALIGN = 0x05, FRIGHT_ALIGN = 0x06
+};
+
+
+//------------------------------------------------
+// Border attributes. Not inherited.
+
+
+// These have been defined in the order of their precedence for border-collapsing. Do
+// not change this order!
+enum EBorderStyle {
+ BNATIVE, BNONE, BHIDDEN, INSET, GROOVE, RIDGE, OUTSET, DOTTED, DASHED, SOLID, DOUBLE
+};
+
+class BorderValue
+{
+public:
+ BorderValue() : width( 3 ), style( BNONE ) {}
+
+ QColor color;
+ unsigned short width : 12;
+ EBorderStyle style : 6;
+
+ bool nonZero(bool checkStyle = true) const {
+ return width != 0 && !(checkStyle && style == BNONE);
+ }
+
+ bool isTransparent() const {
+ return color.isValid() && qAlpha(color.rgb()) == 0;
+ }
+
+ bool operator==(const BorderValue& o) const
+ {
+ return width==o.width && style==o.style && color==o.color;
+ }
+
+ bool operator!=(const BorderValue& o) const
+ {
+ return !(*this == o);
+ }
+};
+
+class OutlineValue : public BorderValue
+{
+ public:
+ OutlineValue()
+ {
+ _offset = 0;
+ _auto = false;
+ }
+
+ bool operator==(const OutlineValue& o) const
+ {
+ return width==o.width && style==o.style && color==o.color && _offset == o._offset && _auto == o._auto;
+ }
+
+ bool operator!=(const OutlineValue& o) const
+ {
+ return !(*this == o);
+ }
+
+ int _offset;
+ bool _auto;
+};
+
+enum EBorderPrecedence { BOFF, BTABLE, BCOLGROUP, BCOL, BROWGROUP, BROW, BCELL };
+
+struct CollapsedBorderValue
+{
+ CollapsedBorderValue() :border(0), precedence(BOFF) {}
+ CollapsedBorderValue(const BorderValue* b, EBorderPrecedence p) :border(b), precedence(p) {}
+
+ int width() const { return border && border->nonZero() ? border->width : 0; }
+ EBorderStyle style() const { return border ? border->style : BHIDDEN; }
+ bool exists() const { return border; }
+ QColor color() const { return border ? border->color : QColor(); }
+ bool isTransparent() const { return border ? border->isTransparent() : true; }
+
+ bool operator==(const CollapsedBorderValue& o) const
+ {
+ if (!border) return !o.border;
+ if (!o.border) return false;
+ return *border == *o.border && precedence == o.precedence;
+ }
+
+ const BorderValue* border;
+ EBorderPrecedence precedence;
+};
+
+class BorderData : public Shared<BorderData>
+{
+public:
+ BorderValue left;
+ BorderValue right;
+ BorderValue top;
+ BorderValue bottom;
+
+ bool hasBorder() const
+ {
+ return left.nonZero() || right.nonZero() || top.nonZero() || bottom.nonZero();
+ }
+
+ unsigned short borderLeftWidth() const {
+ if (left.style == BNONE || left.style == BHIDDEN || left.style == BNATIVE)
+ return 0;
+ return left.width;
+ }
+
+ unsigned short borderRightWidth() const {
+ if (right.style == BNONE || right.style == BHIDDEN || right.style == BNATIVE)
+ return 0;
+ return right.width;
+ }
+
+ unsigned short borderTopWidth() const {
+ if (top.style == BNONE || top.style == BHIDDEN || top.style == BNATIVE)
+ return 0;
+ return top.width;
+ }
+
+ unsigned short borderBottomWidth() const {
+ if (bottom.style == BNONE || bottom.style == BHIDDEN || bottom.style == BNATIVE)
+ return 0;
+ return bottom.width;
+ }
+
+ bool operator==(const BorderData& o) const
+ {
+ return left==o.left && right==o.right && top==o.top && bottom==o.bottom;
+ }
+
+};
+
+class StyleSurroundData : public Shared<StyleSurroundData>
+{
+public:
+ StyleSurroundData();
+
+ StyleSurroundData(const StyleSurroundData& o );
+ bool operator==(const StyleSurroundData& o) const;
+ bool operator!=(const StyleSurroundData& o) const {
+ return !(*this == o);
+ }
+ bool hasSamePBMData(const StyleSurroundData& o) const {
+ return (margin == o.margin) && (padding == o.padding) && (border == o.border);
+ }
+
+ LengthBox offset;
+ LengthBox margin;
+ LengthBox padding;
+ BorderData border;
+};
+
+
+//------------------------------------------------
+// Box attributes. Not inherited.
+
+enum EBoxSizing {
+ BORDER_BOX, CONTENT_BOX
+};
+
+class StyleBoxData : public Shared<StyleBoxData>
+{
+public:
+ StyleBoxData();
+
+ StyleBoxData(const StyleBoxData& o );
+
+
+ // copy and assignment
+// StyleBoxData(const StyleBoxData &other);
+// const StyleBoxData &operator = (const StyleBoxData &other);
+
+ bool operator==(const StyleBoxData& o) const;
+ bool operator!=(const StyleBoxData& o) const {
+ return !(*this == o);
+ }
+
+ Length width;
+ Length height;
+
+ Length min_width;
+ Length max_width;
+
+ Length min_height;
+ Length max_height;
+
+ Length vertical_align;
+
+ EBoxSizing box_sizing;
+
+ signed int z_index :31;
+ bool z_auto : 1;
+};
+
+//------------------------------------------------
+// Random visual rendering model attributes. Not inherited.
+
+enum EOverflow {
+ OVISIBLE, OHIDDEN, OSCROLL, OAUTO, OMARQUEE
+};
+
+enum EVerticalAlign {
+ BASELINE, MIDDLE, SUB, SUPER, TEXT_TOP,
+ TEXT_BOTTOM, TOP, BOTTOM, BASELINE_MIDDLE, LENGTH
+};
+
+enum EClear{
+ CNONE = 0, CLEFT = 1, CRIGHT = 2, CBOTH = 3
+};
+
+enum ETableLayout {
+ TAUTO, TFIXED
+};
+
+enum EUnicodeBidi {
+ UBNormal, Embed, Override
+};
+
+class StyleVisualData : public Shared<StyleVisualData>
+{
+public:
+ StyleVisualData();
+
+ ~StyleVisualData();
+
+ StyleVisualData(const StyleVisualData& o );
+
+ bool operator==( const StyleVisualData &o ) const {
+ return ( clip == o.clip &&
+ palette == o.palette );
+ }
+ bool operator!=( const StyleVisualData &o ) const {
+ return !(*this == o);
+ }
+
+ LengthBox clip;
+ unsigned textDecoration : 4; // Text decorations defined *only* by this element.
+
+ QPalette palette; //widget styling with IE attributes
+
+};
+
+//------------------------------------------------
+enum EBackgroundBox {
+ BGBORDER, BGPADDING, BGCONTENT
+};
+
+enum EBackgroundRepeat {
+ REPEAT, REPEAT_X, REPEAT_Y, NO_REPEAT
+};
+
+struct LengthSize {
+ Length width;
+ Length height;
+};
+
+struct BackgroundLayer {
+public:
+ BackgroundLayer();
+ ~BackgroundLayer();
+
+ CachedImage* backgroundImage() const { return m_image; }
+ Length backgroundXPosition() const { return m_xPosition; }
+ Length backgroundYPosition() const { return m_yPosition; }
+ bool backgroundAttachment() const { return m_bgAttachment; }
+ EBackgroundBox backgroundClip() const { return m_bgClip; }
+ EBackgroundBox backgroundOrigin() const { return m_bgOrigin; }
+ EBackgroundRepeat backgroundRepeat() const { return m_bgRepeat; }
+ LengthSize backgroundSize() const { return m_backgroundSize; }
+
+ BackgroundLayer* next() const { return m_next; }
+ BackgroundLayer* next() { return m_next; }
+
+ bool isBackgroundImageSet() const { return m_imageSet; }
+ bool isBackgroundXPositionSet() const { return m_xPosSet; }
+ bool isBackgroundYPositionSet() const { return m_yPosSet; }
+ bool isBackgroundAttachmentSet() const { return m_attachmentSet; }
+ bool isBackgroundClipSet() const { return m_clipSet; }
+ bool isBackgroundOriginSet() const { return m_originSet; }
+ bool isBackgroundRepeatSet() const { return m_repeatSet; }
+ bool isBackgroundSizeSet() const { return m_backgroundSizeSet; }
+
+ void setBackgroundImage(CachedImage* i) { m_image = i; m_imageSet = true; }
+ void setBackgroundXPosition(const Length& l) { m_xPosition = l; m_xPosSet = true; }
+ void setBackgroundYPosition(const Length& l) { m_yPosition = l; m_yPosSet = true; }
+ void setBackgroundAttachment(bool b) { m_bgAttachment = b; m_attachmentSet = true; }
+ void setBackgroundClip(EBackgroundBox b) { m_bgClip = b; m_clipSet = true; }
+ void setBackgroundOrigin(EBackgroundBox b) { m_bgOrigin = b; m_originSet = true; }
+ void setBackgroundRepeat(EBackgroundRepeat r) { m_bgRepeat = r; m_repeatSet = true; }
+ void setBackgroundSize(const LengthSize& b) { m_backgroundSize = b; m_backgroundSizeSet = true; }
+
+ void clearBackgroundImage() { m_imageSet = false; }
+ void clearBackgroundXPosition() { m_xPosSet = false; }
+ void clearBackgroundYPosition() { m_yPosSet = false; }
+ void clearBackgroundAttachment() { m_attachmentSet = false; }
+ void clearBackgroundClip() { m_clipSet = false; }
+ void clearBackgroundOrigin() { m_originSet = false; }
+ void clearBackgroundRepeat() { m_repeatSet = false; }
+ void clearBackgroundSize() { m_backgroundSizeSet = false; }
+
+ void setNext(BackgroundLayer* n) { if (m_next != n) { delete m_next; m_next = n; } }
+
+ BackgroundLayer& operator=(const BackgroundLayer& o);
+ BackgroundLayer(const BackgroundLayer& o);
+
+ bool operator==(const BackgroundLayer& o) const;
+ bool operator!=(const BackgroundLayer& o) const {
+ return !(*this == o);
+ }
+
+ bool containsImage(CachedImage* c) const { if (c == m_image) return true; if (m_next) return m_next->containsImage(c); return false; }
+
+ bool hasImage() const {
+ if (m_image)
+ return true;
+ return m_next ? m_next->hasImage() : false;
+ }
+ bool hasFixedImage() const {
+ if (m_image && !m_bgAttachment)
+ return true;
+ return m_next ? m_next->hasFixedImage() : false;
+ }
+
+ void fillUnsetProperties();
+ void cullEmptyLayers();
+
+ CachedImage* m_image;
+
+ Length m_xPosition;
+ Length m_yPosition;
+
+ bool m_bgAttachment : 1;
+ EBackgroundBox m_bgClip : 2;
+ EBackgroundBox m_bgOrigin : 2;
+ EBackgroundRepeat m_bgRepeat : 2;
+
+ LengthSize m_backgroundSize;
+
+ bool m_imageSet : 1;
+ bool m_attachmentSet : 1;
+ bool m_clipSet : 1;
+ bool m_originSet : 1;
+ bool m_repeatSet : 1;
+ bool m_xPosSet : 1;
+ bool m_yPosSet : 1;
+ bool m_backgroundSizeSet : 1;
+
+ BackgroundLayer* m_next;
+};
+
+class StyleBackgroundData : public Shared<StyleBackgroundData>
+{
+public:
+ StyleBackgroundData();
+ ~StyleBackgroundData() {}
+ StyleBackgroundData(const StyleBackgroundData& o );
+
+ bool operator==(const StyleBackgroundData& o) const;
+ bool operator!=(const StyleBackgroundData &o) const {
+ return !(*this == o);
+ }
+
+ BackgroundLayer m_background;
+ QColor m_color;
+ OutlineValue m_outline;
+};
+
+enum EQuoteContent {
+ NO_QUOTE = 0, OPEN_QUOTE, CLOSE_QUOTE, NO_OPEN_QUOTE, NO_CLOSE_QUOTE
+};
+
+enum ContentType {
+ CONTENT_NONE = 0, CONTENT_NORMAL, CONTENT_OBJECT,
+ CONTENT_TEXT, CONTENT_COUNTER, CONTENT_QUOTE
+};
+
+struct ContentData {
+ ContentData() : _contentType( CONTENT_NONE ), _nextContent(0) {}
+ ContentData(const ContentData& o);
+ ~ContentData();
+ void clearContent();
+
+ DOM::DOMStringImpl* contentText()
+ { if (_contentType == CONTENT_TEXT) return _content.text; return 0; }
+ CachedObject* contentObject()
+ { if (_contentType == CONTENT_OBJECT) return _content.object; return 0; }
+ DOM::CounterImpl* contentCounter()
+ { if (_contentType == CONTENT_COUNTER) return _content.counter; return 0; }
+ EQuoteContent contentQuote()
+ { if (_contentType == CONTENT_QUOTE) return _content.quote; return NO_QUOTE; }
+
+ ContentType _contentType;
+
+ union {
+ CachedObject* object;
+ DOM::DOMStringImpl* text;
+ DOM::CounterImpl* counter;
+ EQuoteContent quote;
+ } _content ;
+
+ ContentData* _nextContent;
+};
+
+class StyleGeneratedData : public Shared<StyleGeneratedData>
+{
+public:
+ StyleGeneratedData();
+ ~StyleGeneratedData();
+ StyleGeneratedData(const StyleGeneratedData& o );
+
+ bool operator==(const StyleGeneratedData& o) const;
+ bool operator!=(const StyleGeneratedData &o) const {
+ return !(*this == o);
+ }
+
+ bool contentDataEquivalent(const StyleGeneratedData* otherStyle) const;
+ bool counterDataEquivalent(const StyleGeneratedData* otherStyle) const;
+
+ ContentData *content;
+ DOM::CSSValueListImpl *counter_reset;
+ DOM::CSSValueListImpl *counter_increment;
+};
+
+//------------------------------------------------
+// CSS3 Marquee Properties
+
+enum EMarqueeBehavior { MNONE, MSCROLL, MSLIDE, MALTERNATE, MUNFURL };
+enum EMarqueeDirection { MAUTO = 0, MLEFT = 1, MRIGHT = -1, MUP = 2, MDOWN = -2, MFORWARD = 3, MBACKWARD = -3 };
+
+class StyleMarqueeData : public Shared<StyleMarqueeData>
+{
+public:
+ StyleMarqueeData();
+ StyleMarqueeData(const StyleMarqueeData& o);
+
+ bool operator==(const StyleMarqueeData& o) const;
+ bool operator!=(const StyleMarqueeData& o) const {
+ return !(*this == o);
+ }
+
+ Length increment;
+ int speed;
+
+ int loops; // -1 means infinite.
+
+ EMarqueeBehavior behavior : 3;
+ EMarqueeDirection direction : 3;
+};
+
+// This struct holds information about shadows for the text-shadow and box-shadow properties.
+struct ShadowData {
+ ShadowData(int _x, int _y, int _blur, const QColor& _color)
+ :x(_x), y(_y), blur(_blur), color(_color), next(0) {}
+ ShadowData(const ShadowData& o);
+
+ ~ShadowData() { delete next; }
+
+ bool operator==(const ShadowData& o) const;
+ bool operator!=(const ShadowData &o) const {
+ return !(*this == o);
+ }
+
+ int x;
+ int y;
+ int blur;
+ QColor color;
+ ShadowData* next;
+};
+
+// This struct is for rarely used non-inherited CSS3 properties. By grouping them together,
+// we save space, and only allocate this object when someone actually uses
+// a non-inherited CSS3 property.
+class StyleCSS3NonInheritedData : public Shared<StyleCSS3NonInheritedData>
+{
+public:
+ StyleCSS3NonInheritedData();
+ ~StyleCSS3NonInheritedData() {}
+ StyleCSS3NonInheritedData(const StyleCSS3NonInheritedData& o);
+
+ bool operator==(const StyleCSS3NonInheritedData& o) const;
+ bool operator!=(const StyleCSS3NonInheritedData &o) const {
+ return !(*this == o);
+ }
+
+ float opacity; // Whether or not we're transparent.
+#ifdef APPLE_CHANGES // ### we don't have those (yet)
+ DataRef<StyleFlexibleBoxData> flexibleBox; // Flexible box properties
+#endif
+ DataRef<StyleMarqueeData> marquee; // Marquee properties
+};
+
+// This struct is for rarely used inherited CSS3 properties. By grouping them together,
+// we save space, and only allocate this object when someone actually uses
+// an inherited CSS3 property.
+class StyleCSS3InheritedData : public Shared<StyleCSS3InheritedData>
+{
+ public:
+ StyleCSS3InheritedData();
+ ~StyleCSS3InheritedData();
+ StyleCSS3InheritedData(const StyleCSS3InheritedData& o);
+
+ bool operator==(const StyleCSS3InheritedData& o) const;
+ bool operator!=(const StyleCSS3InheritedData &o) const {
+ return !(*this == o);
+ }
+ bool shadowDataEquivalent(const StyleCSS3InheritedData& o) const;
+
+ ShadowData* textShadow; // Our text shadow information for shadowed text drawing.
+#ifdef APPLE_CHANGES
+ EUserModify userModify : 2; // Flag used for editing state
+ bool textSizeAdjust : 1; // An Apple extension. Not really CSS3 but not worth making a new struct over.
+#endif
+ private:
+ StyleCSS3InheritedData &operator=(const StyleCSS3InheritedData &);
+};
+
+//------------------------------------------------
+// Inherited attributes.
+//
+// the inherited-decoration and inherited-shadow attributes
+// are inherited from the
+// first parent which is block level
+//
+
+enum EWhiteSpace {
+ NORMAL, PRE, NOWRAP, PRE_WRAP, PRE_LINE, KHTML_NOWRAP
+};
+
+enum ETextAlign {
+ TAAUTO, LEFT, RIGHT, CENTER, JUSTIFY, KHTML_LEFT, KHTML_RIGHT, KHTML_CENTER
+};
+
+enum ETextTransform {
+ CAPITALIZE, UPPERCASE, LOWERCASE, TTNONE
+};
+
+enum EDirection {
+ LTR, RTL
+};
+
+enum ETextDecoration {
+ TDNONE = 0x0 , UNDERLINE = 0x1, OVERLINE = 0x2, LINE_THROUGH= 0x4, BLINK = 0x8
+};
+
+enum EPageBreak {
+ PBAUTO, PBALWAYS, PBAVOID,
+ /* reserved for later use: */
+ PBLEFT, PBRIGHT
+};
+
+class StyleInheritedData : public Shared<StyleInheritedData>
+{
+ StyleInheritedData& operator=(const StyleInheritedData&);
+public:
+ StyleInheritedData();
+ ~StyleInheritedData();
+ StyleInheritedData(const StyleInheritedData& o );
+
+ bool operator==(const StyleInheritedData& o) const;
+ bool operator != ( const StyleInheritedData &o ) const {
+ return !(*this == o);
+ }
+
+ Length indent;
+ // could be packed in a short but doesn't
+ // make a difference currently because of padding
+ Length line_height;
+
+ CachedImage *style_image;
+
+ khtml::Font font;
+ QColor color;
+
+ short border_hspacing;
+ short border_vspacing;
+
+ // Paged media properties.
+ short widows;
+ short orphans;
+
+ DOM::QuotesValueImpl* quotes;
+};
+
+
+enum EEmptyCell {
+ SHOW, HIDE
+};
+
+enum ECaptionSide {
+ CAPTOP, CAPBOTTOM, CAPLEFT, CAPRIGHT
+};
+
+
+enum EListStyleType {
+ // Symbols:
+ LDISC, LCIRCLE, LSQUARE, LBOX, LDIAMOND,
+ // Numeric:
+ LDECIMAL, DECIMAL_LEADING_ZERO, ARABIC_INDIC, LAO, PERSIAN, URDU, THAI, TIBETAN,
+ // Algorithmic:
+ LOWER_ROMAN, UPPER_ROMAN, HEBREW, ARMENIAN, GEORGIAN,
+ // Ideographic:
+ CJK_IDEOGRAPHIC, JAPANESE_FORMAL, JAPANESE_INFORMAL,
+ SIMP_CHINESE_FORMAL, SIMP_CHINESE_INFORMAL, TRAD_CHINESE_FORMAL, TRAD_CHINESE_INFORMAL,
+ // Alphabetic:
+ LOWER_GREEK, UPPER_GREEK, LOWER_ALPHA, LOWER_LATIN, UPPER_ALPHA, UPPER_LATIN,
+ HIRAGANA, KATAKANA, HIRAGANA_IROHA, KATAKANA_IROHA,
+ // Special:
+ LNONE
+};
+
+inline bool isListStyleCounted(EListStyleType type)
+{
+ switch(type) {
+ case LDISC: case LCIRCLE: case LSQUARE: case LBOX: case LDIAMOND:
+ case LNONE:
+ return false;
+ default:
+ return true;
+ }
+}
+
+enum EListStylePosition { OUTSIDE, INSIDE };
+
+enum EVisibility { VISIBLE, HIDDEN, COLLAPSE };
+
+enum ECursor {
+ CURSOR_AUTO, CURSOR_CROSS, CURSOR_DEFAULT, CURSOR_POINTER, CURSOR_PROGRESS, CURSOR_MOVE,
+ CURSOR_E_RESIZE, CURSOR_NE_RESIZE, CURSOR_NW_RESIZE, CURSOR_N_RESIZE, CURSOR_SE_RESIZE, CURSOR_SW_RESIZE,
+ CURSOR_S_RESIZE, CURSOR_W_RESIZE, CURSOR_TEXT, CURSOR_WAIT, CURSOR_HELP
+};
+
+enum EUserInput {
+ UI_ENABLED, UI_DISABLED, UI_NONE
+};
+
+//------------------------------------------------
+
+enum EDisplay {
+ INLINE, BLOCK, LIST_ITEM, RUN_IN,
+ COMPACT, INLINE_BLOCK, TABLE, INLINE_TABLE,
+ TABLE_ROW_GROUP, TABLE_HEADER_GROUP, TABLE_FOOTER_GROUP, TABLE_ROW,
+ TABLE_COLUMN_GROUP, TABLE_COLUMN, TABLE_CELL,
+ TABLE_CAPTION, NONE
+};
+
+class RenderStyle : public Shared<RenderStyle>
+{
+ friend class CSSStyleSelector;
+public:
+ KDE_EXPORT static void cleanup();
+
+ // pseudo elements
+ enum PseudoId {
+ NOPSEUDO, FIRST_LINE, FIRST_LETTER, SELECTION,
+ BEFORE, AFTER, REPLACED, MARKER
+ };
+
+protected:
+
+// !START SYNC!: Keep this in sync with the copy constructor in render_style.cpp
+
+ // inherit
+ struct InheritedFlags {
+ // 64 bit inherited, update unused when adding to the struct, or the operator will break.
+ bool operator==( const InheritedFlags &other ) const
+ { return _iflags ==other._iflags; }
+ bool operator!=( const InheritedFlags &other ) const
+ { return _iflags != other._iflags; }
+
+ union {
+ struct {
+ EEmptyCell _empty_cells : 1 ;
+ ECaptionSide _caption_side : 2;
+ EListStyleType _list_style_type : 6;
+ EListStylePosition _list_style_position :1;
+
+ EVisibility _visibility : 2;
+ ETextAlign _text_align : 4;
+ ETextTransform _text_transform : 2;
+ unsigned _text_decorations : 4;
+ ECursor _cursor_style : 5;
+
+ EDirection _direction : 1;
+ bool _border_collapse : 1 ;
+ EWhiteSpace _white_space : 3;
+ // non CSS2 inherited
+ bool _visuallyOrdered : 1;
+ bool _htmlHacks :1;
+ EUserInput _user_input : 2;
+
+ bool _page_break_inside : 1; // AUTO/AVOID
+
+ unsigned int unused : 27;
+ } f;
+ Q_UINT64 _iflags;
+ };
+ } inherited_flags;
+
+// don't inherit
+ struct NonInheritedFlags {
+ // 64 bit non-inherited, update unused when adding to the struct, or the operator will break.
+ bool operator==( const NonInheritedFlags &other ) const
+ { return _niflags == other._niflags; }
+ bool operator!=( const NonInheritedFlags &other ) const
+ { return _niflags != other._niflags; }
+
+ union {
+ struct {
+ EDisplay _display : 5;
+ EDisplay _originalDisplay: 5;
+ EOverflow _overflowX : 4 ;
+ EOverflow _overflowY : 4 ;
+ EVerticalAlign _vertical_align : 4;
+ EClear _clear : 2;
+ EPosition _position : 2;
+ EFloat _floating : 3;
+ ETableLayout _table_layout : 1;
+ bool _flowAroundFloats :1;
+
+ EPageBreak _page_break_before : 3;
+ EPageBreak _page_break_after : 3;
+
+ PseudoId _styleType : 4;
+ bool _hasClip : 1;
+ unsigned _pseudoBits : 8;
+ EUnicodeBidi _unicodeBidi : 2;
+
+ // non CSS2 non-inherited
+ bool _textOverflow : 1; // Whether or not lines that spill out should be truncated with "..."
+
+ unsigned int unused : 11;
+ } f;
+ Q_UINT64 _niflags;
+ };
+ } noninherited_flags;
+
+// non-inherited attributes
+ DataRef<StyleBoxData> box;
+ DataRef<StyleVisualData> visual;
+ DataRef<StyleBackgroundData> background;
+ DataRef<StyleSurroundData> surround;
+ DataRef<StyleGeneratedData> generated;
+ DataRef<StyleCSS3NonInheritedData> css3NonInheritedData;
+
+// inherited attributes
+ DataRef<StyleCSS3InheritedData> css3InheritedData;
+ DataRef<StyleInheritedData> inherited;
+
+// list of associated pseudo styles
+ RenderStyle* pseudoStyle;
+
+// !END SYNC!
+
+// static default style
+ static RenderStyle* _default;
+
+private:
+ RenderStyle(const RenderStyle*) {}
+
+protected:
+ void setBitDefaults()
+ {
+ inherited_flags.f._empty_cells = initialEmptyCells();
+ inherited_flags.f._caption_side = initialCaptionSide();
+ inherited_flags.f._list_style_type = initialListStyleType();
+ inherited_flags.f._list_style_position = initialListStylePosition();
+ inherited_flags.f._visibility = initialVisibility();
+ inherited_flags.f._text_align = initialTextAlign();
+ inherited_flags.f._text_transform = initialTextTransform();
+ inherited_flags.f._text_decorations = initialTextDecoration();
+ inherited_flags.f._cursor_style = initialCursor();
+ inherited_flags.f._direction = initialDirection();
+ inherited_flags.f._border_collapse = initialBorderCollapse();
+ inherited_flags.f._white_space = initialWhiteSpace();
+ inherited_flags.f._visuallyOrdered = false;
+ inherited_flags.f._htmlHacks=false;
+ inherited_flags.f._user_input = UI_NONE;
+ inherited_flags.f._page_break_inside = true;
+ inherited_flags.f.unused = 0;
+
+ noninherited_flags._niflags = 0L; // for safety: without this, the equality method sometimes
+ // makes use of uninitialised bits according to valgrind
+
+ noninherited_flags.f._display = noninherited_flags.f._originalDisplay = initialDisplay();
+ noninherited_flags.f._overflowX = initialOverflowX();
+ noninherited_flags.f._overflowY = initialOverflowY();
+ noninherited_flags.f._vertical_align = initialVerticalAlign();
+ noninherited_flags.f._clear = initialClear();
+ noninherited_flags.f._position = initialPosition();
+ noninherited_flags.f._floating = initialFloating();
+ noninherited_flags.f._table_layout = initialTableLayout();
+ noninherited_flags.f._flowAroundFloats= initialFlowAroundFloats();
+ noninherited_flags.f._page_break_before = initialPageBreak();
+ noninherited_flags.f._page_break_after = initialPageBreak();
+ noninherited_flags.f._styleType = NOPSEUDO;
+ noninherited_flags.f._hasClip = false;
+ noninherited_flags.f._pseudoBits = 0;
+ noninherited_flags.f._unicodeBidi = initialUnicodeBidi();
+ noninherited_flags.f._textOverflow = initialTextOverflow();
+ noninherited_flags.f.unused = 0;
+ }
+
+public:
+
+ RenderStyle();
+ // used to create the default style.
+ RenderStyle(bool);
+ RenderStyle(const RenderStyle&);
+
+ ~RenderStyle();
+
+ void inheritFrom(const RenderStyle* inheritParent);
+
+ PseudoId styleType() const { return noninherited_flags.f._styleType; }
+ void setStyleType(PseudoId pi) { noninherited_flags.f._styleType = pi; }
+ bool isGenerated() const {
+ if (styleType() == AFTER || styleType() == BEFORE || styleType() == MARKER || styleType() == REPLACED)
+ return true;
+ else
+ return false;
+ }
+
+ bool hasPseudoStyle(PseudoId pi) const;
+ void setHasPseudoStyle(PseudoId pi, bool b=true);
+ RenderStyle* getPseudoStyle(PseudoId pi) const;
+ RenderStyle* addPseudoStyle(PseudoId pi);
+ void removePseudoStyle(PseudoId pi);
+
+ bool operator==(const RenderStyle& other) const;
+ bool isFloating() const { return !(noninherited_flags.f._floating == FNONE); }
+ bool hasMargin() const { return surround->margin.nonZero(); }
+ bool hasBorder() const { return surround->border.hasBorder(); }
+ bool hasOffset() const { return surround->offset.nonZero(); }
+
+ bool hasBackground() const {
+ if (backgroundColor().isValid() && qAlpha(backgroundColor().rgb()) > 0)
+ return true;
+ else
+ return background->m_background.hasImage();
+ }
+ bool hasFixedBackgroundImage() const { return background->m_background.hasFixedImage(); }
+
+ bool visuallyOrdered() const { return inherited_flags.f._visuallyOrdered; }
+ void setVisuallyOrdered(bool b) { inherited_flags.f._visuallyOrdered = b; }
+
+// attribute getter methods
+
+ EDisplay display() const { return noninherited_flags.f._display; }
+ EDisplay originalDisplay() const { return noninherited_flags.f._originalDisplay; }
+
+ Length left() const { return surround->offset.left; }
+ Length right() const { return surround->offset.right; }
+ Length top() const { return surround->offset.top; }
+ Length bottom() const { return surround->offset.bottom; }
+
+ EPosition position() const { return noninherited_flags.f._position; }
+ EFloat floating() const { return noninherited_flags.f._floating; }
+
+ Length width() const { return box->width; }
+ Length height() const { return box->height; }
+ Length minWidth() const { return box->min_width; }
+ Length maxWidth() const { return box->max_width; }
+ Length minHeight() const { return box->min_height; }
+ Length maxHeight() const { return box->max_height; }
+
+ const BorderData& border() const { return surround->border; }
+ const BorderValue& borderLeft() const { return surround->border.left; }
+ const BorderValue& borderRight() const { return surround->border.right; }
+ const BorderValue& borderTop() const { return surround->border.top; }
+ const BorderValue& borderBottom() const { return surround->border.bottom; }
+
+ unsigned short borderLeftWidth() const { return surround->border.borderLeftWidth(); }
+ EBorderStyle borderLeftStyle() const { return surround->border.left.style; }
+ const QColor& borderLeftColor() const { return surround->border.left.color; }
+ bool borderLeftIsTransparent() const { return surround->border.left.isTransparent(); }
+ unsigned short borderRightWidth() const { return surround->border.borderRightWidth(); }
+ EBorderStyle borderRightStyle() const { return surround->border.right.style; }
+ const QColor& borderRightColor() const { return surround->border.right.color; }
+ bool borderRightIsTransparent() const { return surround->border.right.isTransparent(); }
+ unsigned short borderTopWidth() const { return surround->border.borderTopWidth(); }
+ EBorderStyle borderTopStyle() const { return surround->border.top.style; }
+ const QColor& borderTopColor() const { return surround->border.top.color; }
+ bool borderTopIsTransparent() const { return surround->border.top.isTransparent(); }
+ unsigned short borderBottomWidth() const { return surround->border.borderBottomWidth(); }
+ EBorderStyle borderBottomStyle() const { return surround->border.bottom.style; }
+ const QColor& borderBottomColor() const { return surround->border.bottom.color; }
+ bool borderBottomIsTransparent() const { return surround->border.bottom.isTransparent(); }
+
+ unsigned short outlineSize() const { return outlineWidth() + outlineOffset(); }
+ unsigned short outlineWidth() const
+ { if(background->m_outline.style == BNONE || background->m_outline.style == BHIDDEN) return 0;
+ else return background->m_outline.width; }
+ EBorderStyle outlineStyle() const { return background->m_outline.style; }
+ bool outlineStyleIsAuto() const { return background->m_outline._auto; }
+ const QColor & outlineColor() const { return background->m_outline.color; }
+
+ EOverflow overflowX() const { return noninherited_flags.f._overflowX; }
+ EOverflow overflowY() const { return noninherited_flags.f._overflowY; }
+ bool hidesOverflow() const {
+ // either both overflow are visible or none are
+ return overflowX() != OVISIBLE;
+ }
+
+ EVisibility visibility() const { return inherited_flags.f._visibility; }
+ EVerticalAlign verticalAlign() const { return noninherited_flags.f._vertical_align; }
+ Length verticalAlignLength() const { return box->vertical_align; }
+
+ Length clipLeft() const { return visual->clip.left; }
+ Length clipRight() const { return visual->clip.right; }
+ Length clipTop() const { return visual->clip.top; }
+ Length clipBottom() const { return visual->clip.bottom; }
+ LengthBox clip() const { return visual->clip; }
+ bool hasClip() const { return noninherited_flags.f._hasClip; }
+
+ EUnicodeBidi unicodeBidi() const { return noninherited_flags.f._unicodeBidi; }
+
+ EClear clear() const { return noninherited_flags.f._clear; }
+ ETableLayout tableLayout() const { return noninherited_flags.f._table_layout; }
+
+ const QFont & font() const { return inherited->font.f; }
+ // use with care. call font->update() after modifications
+ const Font &htmlFont() { return inherited->font; }
+ const QFontMetrics & fontMetrics() const { return inherited->font.fm; }
+
+ const QColor & color() const { return inherited->color; }
+ Length textIndent() const { return inherited->indent; }
+ ETextAlign textAlign() const { return inherited_flags.f._text_align; }
+ ETextTransform textTransform() const { return inherited_flags.f._text_transform; }
+ int textDecorationsInEffect() const { return inherited_flags.f._text_decorations; }
+ int textDecoration() const { return visual->textDecoration; }
+ int wordSpacing() const { return inherited->font.wordSpacing; }
+ int letterSpacing() const { return inherited->font.letterSpacing; }
+
+ EDirection direction() const { return inherited_flags.f._direction; }
+ Length lineHeight() const { return inherited->line_height; }
+
+ EWhiteSpace whiteSpace() const { return inherited_flags.f._white_space; }
+ bool autoWrap() const {
+ if (whiteSpace() == NORMAL || whiteSpace() == PRE_WRAP || whiteSpace() == PRE_LINE)
+ return true;
+ // nowrap | pre
+ return false;
+ }
+ bool preserveLF() const {
+ if (whiteSpace() == PRE || whiteSpace() == PRE_WRAP || whiteSpace() == PRE_LINE)
+ return true;
+ // normal | nowrap
+ return false;
+ }
+ bool preserveWS() const {
+ if (whiteSpace() == PRE || whiteSpace() == PRE_WRAP)
+ return true;
+ // normal | nowrap | pre-line
+ return false;
+ }
+
+ const QColor & backgroundColor() const { return background->m_color; }
+ CachedImage *backgroundImage() const { return background->m_background.m_image; }
+ EBackgroundRepeat backgroundRepeat() const { return background->m_background.m_bgRepeat; }
+ bool backgroundAttachment() const { return background->m_background.m_bgAttachment; }
+ Length backgroundXPosition() const { return background->m_background.m_xPosition; }
+ Length backgroundYPosition() const { return background->m_background.m_yPosition; }
+ BackgroundLayer* accessBackgroundLayers() { return &(background.access()->m_background); }
+ const BackgroundLayer* backgroundLayers() const { return &(background->m_background); }
+
+ // returns true for collapsing borders, false for separate borders
+ bool borderCollapse() const { return inherited_flags.f._border_collapse; }
+ short borderHorizontalSpacing() const { return inherited->border_hspacing; }
+ short borderVerticalSpacing() const { return inherited->border_vspacing; }
+ EEmptyCell emptyCells() const { return inherited_flags.f._empty_cells; }
+ ECaptionSide captionSide() const { return inherited_flags.f._caption_side; }
+
+ EListStyleType listStyleType() const { return inherited_flags.f._list_style_type; }
+ CachedImage *listStyleImage() const { return inherited->style_image; }
+ EListStylePosition listStylePosition() const { return inherited_flags.f._list_style_position; }
+
+ Length marginTop() const { return surround->margin.top; }
+ Length marginBottom() const { return surround->margin.bottom; }
+ Length marginLeft() const { return surround->margin.left; }
+ Length marginRight() const { return surround->margin.right; }
+
+ Length paddingTop() const { return surround->padding.top; }
+ Length paddingBottom() const { return surround->padding.bottom; }
+ Length paddingLeft() const { return surround->padding.left; }
+ Length paddingRight() const { return surround->padding.right; }
+
+ ECursor cursor() const { return inherited_flags.f._cursor_style; }
+
+ short widows() const { return inherited->widows; }
+ short orphans() const { return inherited->orphans; }
+ bool pageBreakInside() const { return inherited_flags.f._page_break_inside; }
+ EPageBreak pageBreakBefore() const { return noninherited_flags.f._page_break_before; }
+ EPageBreak pageBreakAfter() const { return noninherited_flags.f._page_break_after; }
+
+ DOM::QuotesValueImpl* quotes() const { return inherited->quotes; }
+ QString openQuote(int level) const;
+ QString closeQuote(int level) const;
+
+ // CSS3 Getter Methods
+ EBoxSizing boxSizing() const { return box->box_sizing; }
+ int outlineOffset() const {
+ if (background->m_outline.style == BNONE || background->m_outline.style == BHIDDEN) return 0;
+ return background->m_outline._offset;
+ }
+ ShadowData* textShadow() const { return css3InheritedData->textShadow; }
+ float opacity() { return css3NonInheritedData->opacity; }
+ EUserInput userInput() const { return inherited_flags.f._user_input; }
+
+ Length marqueeIncrement() { return css3NonInheritedData->marquee->increment; }
+ int marqueeSpeed() { return css3NonInheritedData->marquee->speed; }
+ int marqueeLoopCount() { return css3NonInheritedData->marquee->loops; }
+ EMarqueeBehavior marqueeBehavior() { return css3NonInheritedData->marquee->behavior; }
+ EMarqueeDirection marqueeDirection() { return css3NonInheritedData->marquee->direction; }
+ bool textOverflow() const { return noninherited_flags.f._textOverflow; }
+ // End CSS3 Getters
+
+// attribute setter methods
+
+ void setDisplay(EDisplay v) { noninherited_flags.f._display = v; }
+ void setOriginalDisplay(EDisplay v) { noninherited_flags.f._originalDisplay = v; }
+ void setPosition(EPosition v) { noninherited_flags.f._position = v; }
+ void setFloating(EFloat v) { noninherited_flags.f._floating = v; }
+
+ void setLeft(Length v) { SET_VAR(surround,offset.left,v) }
+ void setRight(Length v) { SET_VAR(surround,offset.right,v) }
+ void setTop(Length v) { SET_VAR(surround,offset.top,v) }
+ void setBottom(Length v){ SET_VAR(surround,offset.bottom,v) }
+
+ void setWidth(Length v) { SET_VAR(box,width,v) }
+ void setHeight(Length v) { SET_VAR(box,height,v) }
+
+ void setMinWidth(Length v) { SET_VAR(box,min_width,v) }
+ void setMaxWidth(Length v) { SET_VAR(box,max_width,v) }
+ void setMinHeight(Length v) { SET_VAR(box,min_height,v) }
+ void setMaxHeight(Length v) { SET_VAR(box,max_height,v) }
+
+ void resetBorderTop() { SET_VAR(surround, border.top, BorderValue()) }
+ void resetBorderRight() { SET_VAR(surround, border.right, BorderValue()) }
+ void resetBorderBottom() { SET_VAR(surround, border.bottom, BorderValue()) }
+ void resetBorderLeft() { SET_VAR(surround, border.left, BorderValue()) }
+ void resetOutline() { SET_VAR(background, m_outline, OutlineValue()) }
+
+ void setBackgroundColor(const QColor& v) { SET_VAR(background, m_color, v) }
+
+ void setBorderLeftWidth(unsigned short v) { SET_VAR(surround,border.left.width,v) }
+ void setBorderLeftStyle(EBorderStyle v) { SET_VAR(surround,border.left.style,v) }
+ void setBorderLeftColor(const QColor & v) { SET_VAR(surround,border.left.color,v) }
+ void setBorderRightWidth(unsigned short v) { SET_VAR(surround,border.right.width,v) }
+ void setBorderRightStyle(EBorderStyle v) { SET_VAR(surround,border.right.style,v) }
+ void setBorderRightColor(const QColor & v) { SET_VAR(surround,border.right.color,v) }
+ void setBorderTopWidth(unsigned short v) { SET_VAR(surround,border.top.width,v) }
+ void setBorderTopStyle(EBorderStyle v) { SET_VAR(surround,border.top.style,v) }
+ void setBorderTopColor(const QColor & v) { SET_VAR(surround,border.top.color,v) }
+ void setBorderBottomWidth(unsigned short v) { SET_VAR(surround,border.bottom.width,v) }
+ void setBorderBottomStyle(EBorderStyle v) { SET_VAR(surround,border.bottom.style,v) }
+ void setBorderBottomColor(const QColor & v) { SET_VAR(surround,border.bottom.color,v) }
+ void setOutlineWidth(unsigned short v) { SET_VAR(background,m_outline.width,v) }
+ void setOutlineStyle(EBorderStyle v, bool isAuto = false)
+ {
+ SET_VAR(background,m_outline.style,v)
+ SET_VAR(background,m_outline._auto, isAuto)
+ }
+ void setOutlineColor(const QColor & v) { SET_VAR(background,m_outline.color,v) }
+
+ void setOverflowX(EOverflow v) { noninherited_flags.f._overflowX = v; }
+ void setOverflowY(EOverflow v) { noninherited_flags.f._overflowY = v; }
+ void setVisibility(EVisibility v) { inherited_flags.f._visibility = v; }
+ void setVerticalAlign(EVerticalAlign v) { noninherited_flags.f._vertical_align = v; }
+ void setVerticalAlignLength(Length l) { SET_VAR(box, vertical_align, l ) }
+
+ void setClipLeft(Length v) { SET_VAR(visual,clip.left,v) }
+ void setClipRight(Length v) { SET_VAR(visual,clip.right,v) }
+ void setClipTop(Length v) { SET_VAR(visual,clip.top,v) }
+ void setClipBottom(Length v) { SET_VAR(visual,clip.bottom,v) }
+ void setClip( Length top, Length right, Length bottom, Length left );
+ void setHasClip( bool b ) { noninherited_flags.f._hasClip = b; }
+
+ void setUnicodeBidi( EUnicodeBidi b ) { noninherited_flags.f._unicodeBidi = b; }
+
+ void setClear(EClear v) { noninherited_flags.f._clear = v; }
+ void setTableLayout(ETableLayout v) { noninherited_flags.f._table_layout = v; }
+ bool setFontDef(const khtml::FontDef & v) {
+ // bah, this doesn't compare pointers. broken! (Dirk)
+ if (!(inherited->font.fontDef == v)) {
+ inherited.access()->font = Font( v );
+ return true;
+ }
+ return false;
+ }
+
+ void setColor(const QColor & v) { SET_VAR(inherited,color,v) }
+ void setTextIndent(Length v) { SET_VAR(inherited,indent,v) }
+ void setTextAlign(ETextAlign v) { inherited_flags.f._text_align = v; }
+ void setTextTransform(ETextTransform v) { inherited_flags.f._text_transform = v; }
+ void addToTextDecorationsInEffect(int v) { inherited_flags.f._text_decorations |= v; }
+ void setTextDecorationsInEffect(int v) { inherited_flags.f._text_decorations = v; }
+ void setTextDecoration(unsigned v) { SET_VAR(visual, textDecoration, v); }
+ void setDirection(EDirection v) { inherited_flags.f._direction = v; }
+ void setLineHeight(Length v) { SET_VAR(inherited,line_height,v) }
+
+ void setWhiteSpace(EWhiteSpace v) { inherited_flags.f._white_space = v; }
+
+ void setWordSpacing(int v) { SET_VAR(inherited,font.wordSpacing,v) }
+ void setLetterSpacing(int v) { SET_VAR(inherited,font.letterSpacing,v) }
+
+ void clearBackgroundLayers() { background.access()->m_background = BackgroundLayer(); }
+ void inheritBackgroundLayers(const BackgroundLayer& parent) { background.access()->m_background = parent; }
+ void adjustBackgroundLayers();
+
+ void setBorderCollapse(bool collapse) { inherited_flags.f._border_collapse = collapse; }
+ void setBorderHorizontalSpacing(short v) { SET_VAR(inherited,border_hspacing,v) }
+ void setBorderVerticalSpacing(short v) { SET_VAR(inherited,border_vspacing,v) }
+
+ void setEmptyCells(EEmptyCell v) { inherited_flags.f._empty_cells = v; }
+ void setCaptionSide(ECaptionSide v) { inherited_flags.f._caption_side = v; }
+
+ void setListStyleType(EListStyleType v) { inherited_flags.f._list_style_type = v; }
+ void setListStyleImage(CachedImage *v) { SET_VAR(inherited,style_image,v)}
+ void setListStylePosition(EListStylePosition v) { inherited_flags.f._list_style_position = v; }
+
+ void resetMargin() { SET_VAR(surround, margin, LengthBox(Fixed)) }
+ void setMarginTop(Length v) { SET_VAR(surround,margin.top,v) }
+ void setMarginBottom(Length v) { SET_VAR(surround,margin.bottom,v) }
+ void setMarginLeft(Length v) { SET_VAR(surround,margin.left,v) }
+ void setMarginRight(Length v) { SET_VAR(surround,margin.right,v) }
+
+ void resetPadding() { SET_VAR(surround, padding, LengthBox(Variable)) }
+ void setPaddingTop(Length v) { SET_VAR(surround,padding.top,v) }
+ void setPaddingBottom(Length v) { SET_VAR(surround,padding.bottom,v) }
+ void setPaddingLeft(Length v) { SET_VAR(surround,padding.left,v) }
+ void setPaddingRight(Length v) { SET_VAR(surround,padding.right,v) }
+
+ void setCursor( ECursor c ) { inherited_flags.f._cursor_style = c; }
+
+ bool htmlHacks() const { return inherited_flags.f._htmlHacks; }
+ void setHtmlHacks(bool b=true) { inherited_flags.f._htmlHacks = b; }
+
+ bool flowAroundFloats() const { return noninherited_flags.f._flowAroundFloats; }
+ void setFlowAroundFloats(bool b=true) { noninherited_flags.f._flowAroundFloats = b; }
+
+ int zIndex() const { return box->z_auto? 0 : box->z_index; }
+ void setZIndex(int v) { SET_VAR(box,z_auto,false ); SET_VAR(box, z_index, v); }
+ bool hasAutoZIndex() const { return box->z_auto; }
+ void setHasAutoZIndex() { SET_VAR(box, z_auto, true ); }
+
+ void setWidows(short w) { SET_VAR(inherited, widows, w); }
+ void setOrphans(short o) { SET_VAR(inherited, orphans, o); }
+ void setPageBreakInside(bool b) { inherited_flags.f._page_break_inside = b; }
+ void setPageBreakBefore(EPageBreak b) { noninherited_flags.f._page_break_before = b; }
+ void setPageBreakAfter(EPageBreak b) { noninherited_flags.f._page_break_after = b; }
+
+ void setQuotes(DOM::QuotesValueImpl* q);
+
+ // CSS3 Setters
+ void setBoxSizing( EBoxSizing b ) { SET_VAR(box,box_sizing,b); }
+ void setOutlineOffset(unsigned short v) { SET_VAR(background,m_outline._offset,v) }
+ void setTextShadow(ShadowData* val, bool add=false);
+ void setOpacity(float f) { SET_VAR(css3NonInheritedData, opacity, f); }
+ void setUserInput(EUserInput ui) { inherited_flags.f._user_input = ui; }
+
+ void setMarqueeIncrement(const Length& f) { SET_VAR(css3NonInheritedData.access()->marquee, increment, f); }
+ void setMarqueeSpeed(int f) { SET_VAR(css3NonInheritedData.access()->marquee, speed, f); }
+ void setMarqueeDirection(EMarqueeDirection d) { SET_VAR(css3NonInheritedData.access()->marquee, direction, d); }
+ void setMarqueeBehavior(EMarqueeBehavior b) { SET_VAR(css3NonInheritedData.access()->marquee, behavior, b); }
+ void setMarqueeLoopCount(int i) { SET_VAR(css3NonInheritedData.access()->marquee, loops, i); }
+ void setTextOverflow(bool b) { noninherited_flags.f._textOverflow = b; }
+ // End CSS3 Setters
+
+ QPalette palette() const { return visual->palette; }
+ void setPaletteColor(QPalette::ColorGroup g, QColorGroup::ColorRole r, const QColor& c);
+ void resetPalette() // Called when the desktop color scheme changes.
+ {
+ const_cast<StyleVisualData *>(visual.get())->palette = QApplication::palette();
+ }
+
+ bool useNormalContent() const { return generated->content == 0; }
+ ContentData* contentData() const { return generated->content; }
+ bool contentDataEquivalent(const RenderStyle* otherStyle) const
+ {
+ return generated->contentDataEquivalent(otherStyle->generated.get());
+ }
+ void addContent(DOM::DOMStringImpl* s);
+ void addContent(CachedObject* o);
+ void addContent(DOM::CounterImpl* c);
+ void addContent(EQuoteContent q);
+ void setContentNone();
+ void setContentNormal();
+ void setContentData(ContentData* content);
+
+ DOM::CSSValueListImpl* counterReset() const { return generated->counter_reset; }
+ DOM::CSSValueListImpl* counterIncrement() const { return generated->counter_increment; }
+ void setCounterReset(DOM::CSSValueListImpl* v);
+ void setCounterIncrement(DOM::CSSValueListImpl* v);
+ bool hasCounterReset(const DOM::DOMString& c) const;
+ bool hasCounterIncrement(const DOM::DOMString& c) const;
+ short counterReset(const DOM::DOMString& c) const;
+ short counterIncrement(const DOM::DOMString& c) const;
+
+
+ bool inheritedNotEqual( RenderStyle *other ) const;
+
+ enum Diff { Equal, NonVisible = Equal, Visible, Position, Layout, CbLayout };
+ Diff diff( const RenderStyle *other ) const;
+
+ bool isDisplayReplacedType() {
+ return display() == INLINE_BLOCK ||/* display() == INLINE_BOX ||*/ display() == INLINE_TABLE;
+ }
+ bool isDisplayInlineType() {
+ return display() == INLINE || isDisplayReplacedType();
+ }
+ bool isOriginalDisplayInlineType() {
+ return originalDisplay() == INLINE || originalDisplay() == INLINE_BLOCK ||
+ /*originalDisplay() == INLINE_BOX ||*/ originalDisplay() == INLINE_TABLE;
+ }
+
+
+#ifdef ENABLE_DUMP
+ QString createDiff( const RenderStyle &parent ) const;
+#endif
+
+ // Initial values for all the properties
+ static bool initialBackgroundAttachment() { return true; }
+ static EBackgroundBox initialBackgroundClip() { return BGBORDER; }
+ static EBackgroundBox initialBackgroundOrigin() { return BGPADDING; }
+ static EBackgroundRepeat initialBackgroundRepeat() { return REPEAT; }
+ static LengthSize initialBackgroundSize() { return LengthSize(); }
+ static bool initialBorderCollapse() { return false; }
+ static EBorderStyle initialBorderStyle() { return BNONE; }
+ static ECaptionSide initialCaptionSide() { return CAPTOP; }
+ static EClear initialClear() { return CNONE; }
+ static EDirection initialDirection() { return LTR; }
+ static EDisplay initialDisplay() { return INLINE; }
+ static EEmptyCell initialEmptyCells() { return SHOW; }
+ static EFloat initialFloating() { return FNONE; }
+ static EListStylePosition initialListStylePosition() { return OUTSIDE; }
+ static EListStyleType initialListStyleType() { return LDISC; }
+ static EOverflow initialOverflowX() { return OVISIBLE; }
+ static EOverflow initialOverflowY() { return OVISIBLE; }
+ static EPageBreak initialPageBreak() { return PBAUTO; }
+ static bool initialPageBreakInside() { return true; }
+ static EPosition initialPosition() { return STATIC; }
+ static ETableLayout initialTableLayout() { return TAUTO; }
+ static EUnicodeBidi initialUnicodeBidi() { return UBNormal; }
+ static DOM::QuotesValueImpl* initialQuotes() { return 0; }
+ static EBoxSizing initialBoxSizing() { return CONTENT_BOX; }
+ static ETextTransform initialTextTransform() { return TTNONE; }
+ static EVisibility initialVisibility() { return VISIBLE; }
+ static EWhiteSpace initialWhiteSpace() { return NORMAL; }
+ static Length initialBackgroundXPosition() { return Length(); }
+ static Length initialBackgroundYPosition() { return Length(); }
+ static short initialBorderHorizontalSpacing() { return 0; }
+ static short initialBorderVerticalSpacing() { return 0; }
+ static ECursor initialCursor() { return CURSOR_AUTO; }
+ static QColor initialColor() { return Qt::black; }
+ static CachedImage* initialBackgroundImage() { return 0; }
+ static CachedImage* initialListStyleImage() { return 0; }
+ static unsigned short initialBorderWidth() { return 3; }
+ static int initialLetterWordSpacing() { return 0; }
+ static Length initialSize() { return Length(); }
+ static Length initialMinSize() { return Length(0, Fixed); }
+ static Length initialMaxSize() { return Length(UNDEFINED, Fixed); }
+ static Length initialOffset() { return Length(); }
+ static Length initialMargin() { return Length(Fixed); }
+ static Length initialPadding() { return Length(Variable); }
+ static Length initialTextIndent() { return Length(Fixed); }
+ static EVerticalAlign initialVerticalAlign() { return BASELINE; }
+ static int initialWidows() { return 2; }
+ static int initialOrphans() { return 2; }
+ static Length initialLineHeight() { return Length(-100, Percent); }
+ static ETextAlign initialTextAlign() { return TAAUTO; }
+ static ETextDecoration initialTextDecoration() { return TDNONE; }
+ static bool initialFlowAroundFloats() { return false; }
+ static int initialOutlineOffset() { return 0; }
+ static float initialOpacity() { return 1.0f; }
+ static int initialMarqueeLoopCount() { return -1; }
+ static int initialMarqueeSpeed() { return 85; }
+ static Length initialMarqueeIncrement() { return Length(6, Fixed); }
+ static EMarqueeBehavior initialMarqueeBehavior() { return MSCROLL; }
+ static EMarqueeDirection initialMarqueeDirection() { return MAUTO; }
+ static bool initialTextOverflow() { return false; }
+};
+
+class RenderPageStyle {
+ friend class CSSStyleSelector;
+public:
+ enum PageType { NO_PAGE = 0, ANY_PAGE, FIRST_PAGE, LEFT_PAGES, RIGHT_PAGES };
+
+ RenderPageStyle();
+ ~RenderPageStyle();
+
+ PageType pageType() { return m_pageType; }
+
+ RenderPageStyle* getPageStyle(PageType type);
+ RenderPageStyle* addPageStyle(PageType type);
+ void removePageStyle(PageType type);
+
+ Length marginTop() const { return margin.top; }
+ Length marginBottom() const { return margin.bottom; }
+ Length marginLeft() const { return margin.left; }
+ Length marginRight() const { return margin.right; }
+
+ Length pageWidth() const { return m_pageWidth; }
+ Length pageHeight() const { return m_pageHeight; }
+
+ void setMarginTop(Length v) { margin.top = v; }
+ void setMarginBottom(Length v) { margin.bottom = v; }
+ void setMarginLeft(Length v) { margin.left = v; }
+ void setMarginRight(Length v) { margin.right = v; }
+
+ void setPageWidth(Length v) { m_pageWidth = v; }
+ void setPageHeight(Length v) { m_pageHeight = v; }
+
+protected:
+ RenderPageStyle *next;
+ PageType m_pageType;
+
+ LengthBox margin;
+ Length m_pageWidth;
+ Length m_pageHeight;
+};
+
+} // namespace
+
+#endif
+
diff --git a/khtml/rendering/render_table.cpp b/khtml/rendering/render_table.cpp
new file mode 100644
index 000000000..592db9dbd
--- /dev/null
+++ b/khtml/rendering/render_table.cpp
@@ -0,0 +1,3070 @@
+/**
+ * This file is part of the DOM implementation for KDE.
+ *
+ * Copyright (C) 1997 Martin Jones (mjones@kde.org)
+ * (C) 1997 Torben Weis (weis@kde.org)
+ * (C) 1998 Waldo Bastian (bastian@kde.org)
+ * (C) 1999-2003 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * (C) 2003 Apple Computer, Inc.
+ * (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com)
+ *
+ * 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.
+ */
+
+//#define TABLE_DEBUG
+//#define TABLE_PRINT
+//#define DEBUG_LAYOUT
+//#define BOX_DEBUG
+#include "rendering/render_table.h"
+#include "rendering/render_replaced.h"
+#include "rendering/render_canvas.h"
+#include "rendering/table_layout.h"
+#include "html/html_tableimpl.h"
+#include "html/html_formimpl.h"
+#include "misc/htmltags.h"
+#include "misc/htmlattrs.h"
+#include "rendering/render_line.h"
+#include "xml/dom_docimpl.h"
+
+#include <kglobal.h>
+
+#include <qapplication.h>
+#include <qstyle.h>
+
+#include <kdebug.h>
+#include <assert.h>
+
+using namespace khtml;
+using namespace DOM;
+
+RenderTable::RenderTable(DOM::NodeImpl* node)
+ : RenderBlock(node)
+{
+
+ tCaption = 0;
+ head = foot = firstBody = 0;
+ tableLayout = 0;
+ m_currentBorder = 0;
+
+ has_col_elems = false;
+ hspacing = vspacing = 0;
+ padding = 0;
+ needSectionRecalc = false;
+ padding = 0;
+
+ columnPos.resize( 2 );
+ columnPos.fill( 0 );
+ columns.resize( 1 );
+ columns.fill( ColumnStruct() );
+
+ columnPos[0] = 0;
+}
+
+RenderTable::~RenderTable()
+{
+ delete tableLayout;
+}
+
+void RenderTable::setStyle(RenderStyle *_style)
+{
+ ETableLayout oldTableLayout = style() ? style()->tableLayout() : TAUTO;
+ if ( _style->display() == INLINE ) _style->setDisplay( INLINE_TABLE );
+ if ( _style->display() != INLINE_TABLE ) _style->setDisplay(TABLE);
+ if ( !_style->flowAroundFloats() ) _style->setFlowAroundFloats(true);
+ RenderBlock::setStyle(_style);
+
+ // init RenderObject attributes
+ setInline(style()->display()==INLINE_TABLE && !isPositioned());
+ setReplaced(style()->display()==INLINE_TABLE);
+
+ // In the collapsed border model, there is no cell spacing.
+ hspacing = collapseBorders() ? 0 : style()->borderHorizontalSpacing();
+ vspacing = collapseBorders() ? 0 : style()->borderVerticalSpacing();
+ columnPos[0] = hspacing;
+
+ if ( !tableLayout || style()->tableLayout() != oldTableLayout ) {
+ delete tableLayout;
+
+ // According to the CSS2 spec, you only use fixed table layout if an
+ // explicit width is specified on the table. Auto width implies auto table layout.
+ if (style()->tableLayout() == TFIXED && !style()->width().isVariable()) {
+ tableLayout = new FixedTableLayout(this);
+#ifdef DEBUG_LAYOUT
+ kdDebug( 6040 ) << "using fixed table layout" << endl;
+#endif
+ } else
+ tableLayout = new AutoTableLayout(this);
+ }
+}
+
+short RenderTable::lineHeight(bool b) const
+{
+ // Inline tables are replaced elements. Otherwise, just pass off to
+ // the base class.
+ if (isReplaced())
+ return height()+marginTop()+marginBottom();
+ return RenderBlock::lineHeight(b);
+}
+
+short RenderTable::baselinePosition(bool b) const
+{
+ // Inline tables are replaced elements. Otherwise, just pass off to
+ // the base class.
+ if (isReplaced())
+ return height()+marginTop()+marginBottom();
+ return RenderBlock::baselinePosition(b);
+}
+
+
+void RenderTable::addChild(RenderObject *child, RenderObject *beforeChild)
+{
+#ifdef DEBUG_LAYOUT
+ kdDebug( 6040 ) << renderName() << "(Table)::addChild( " << child->renderName() << ", " <<
+ (beforeChild ? beforeChild->renderName() : "0") << " )" << endl;
+#endif
+ bool wrapInAnonymousSection = false;
+
+ switch(child->style()->display())
+ {
+ case TABLE_CAPTION:
+ if (child->isRenderBlock())
+ tCaption = static_cast<RenderBlock *>(child);
+ break;
+ case TABLE_COLUMN:
+ case TABLE_COLUMN_GROUP:
+ has_col_elems = true;
+ break;
+ case TABLE_HEADER_GROUP:
+ if ( !head ) {
+ if (child->isTableSection())
+ head = static_cast<RenderTableSection *>(child);
+ }
+ else if ( !firstBody )
+ if (child->isTableSection())
+ firstBody = static_cast<RenderTableSection *>(child);
+ break;
+ case TABLE_FOOTER_GROUP:
+ if ( !foot ) {
+ if (child->isTableSection())
+ foot = static_cast<RenderTableSection *>(child);
+ break;
+ }
+ // fall through
+ case TABLE_ROW_GROUP:
+ if(!firstBody)
+ if (child->isTableSection())
+ firstBody = static_cast<RenderTableSection *>(child);
+ break;
+ case TABLE_CELL:
+ case TABLE_ROW:
+ wrapInAnonymousSection = true;
+ break;
+ case BLOCK:
+// case BOX:
+ case COMPACT:
+ case INLINE:
+ case INLINE_BLOCK:
+// case INLINE_BOX:
+ case INLINE_TABLE:
+ case LIST_ITEM:
+ case NONE:
+ case RUN_IN:
+ case TABLE:
+ // The special TABLE > FORM quirk allows the form to sit directly under the table
+ if (child->element() && child->element()->isHTMLElement() && child->element()->id() == ID_FORM)
+ wrapInAnonymousSection = !static_cast<HTMLFormElementImpl*>(child->element())->isMalformed();
+ else
+ wrapInAnonymousSection = true;
+ break;
+ }
+
+ if (!wrapInAnonymousSection) {
+ RenderContainer::addChild(child, beforeChild);
+ return;
+ }
+
+ if (!beforeChild && lastChild() && lastChild()->isTableSection() && lastChild()->isAnonymous()) {
+ lastChild()->addChild(child);
+ return;
+ }
+
+ RenderObject *lastBox = beforeChild;
+ RenderObject *nextToLastBox = beforeChild;
+ while (lastBox && lastBox->parent()->isAnonymous() &&
+ !lastBox->isTableSection() && lastBox->style()->display() != TABLE_CAPTION) {
+ nextToLastBox = lastBox;
+ lastBox = lastBox->parent();
+ }
+ if (lastBox && lastBox->isAnonymous()) {
+ lastBox->addChild(child, nextToLastBox);
+ return;
+ }
+
+ if (beforeChild && !beforeChild->isTableSection())
+ beforeChild = 0;
+ RenderTableSection* section = new (renderArena()) RenderTableSection(document() /* anonymous */);
+ RenderStyle* newStyle = new RenderStyle();
+ newStyle->inheritFrom(style());
+ newStyle->setDisplay(TABLE_ROW_GROUP);
+ section->setStyle(newStyle);
+ addChild(section, beforeChild);
+ section->addChild(child);
+}
+
+void RenderTable::calcWidth()
+{
+ if ( isPositioned() ) {
+ calcAbsoluteHorizontal();
+ }
+
+ RenderBlock *cb = containingBlock();
+ int availableWidth = cb->lineWidth( m_y );
+
+ LengthType widthType = style()->width().type();
+ if(widthType > Relative && style()->width().value() > 0) {
+ // Percent or fixed table
+ // Percent is calculated from contentWidth, not available width
+ m_width = calcBoxWidth(style()->width().minWidth( cb->contentWidth() ));
+ } else {
+ // Subtract out any fixed margins from our available width for auto width tables.
+ int marginTotal = 0;
+ if (!style()->marginLeft().isVariable())
+ marginTotal += style()->marginLeft().width(availableWidth);
+ if (!style()->marginRight().isVariable())
+ marginTotal += style()->marginRight().width(availableWidth);
+
+ // Subtract out our margins to get the available content width.
+ int availContentWidth = kMax(0, availableWidth - marginTotal);
+
+ // Ensure we aren't bigger than our max width or smaller than our min width.
+ m_width = kMin(availContentWidth, m_maxWidth);
+ }
+
+ m_width = kMax (m_width, m_minWidth);
+
+ // Finally, with our true width determined, compute our margins for real.
+ m_marginRight=0;
+ m_marginLeft=0;
+
+ calcHorizontalMargins(style()->marginLeft(),style()->marginRight(),availableWidth);
+}
+
+void RenderTable::layout()
+{
+ KHTMLAssert( needsLayout() );
+ KHTMLAssert( minMaxKnown() );
+ KHTMLAssert( !needSectionRecalc );
+
+ if (posChildNeedsLayout() && !normalChildNeedsLayout() && !selfNeedsLayout()) {
+ // All we have to is lay out our positioned objects.
+ layoutPositionedObjects(true);
+ setNeedsLayout(false);
+ return;
+ }
+
+ if (markedForRepaint()) {
+ repaintDuringLayout();
+ setMarkedForRepaint(false);
+ }
+
+ m_height = 0;
+ initMaxMarginValues();
+
+ int oldWidth = m_width;
+ calcWidth();
+ m_overflowWidth = m_width;
+
+ if (tCaption && (oldWidth != m_width || tCaption->style()->height().isPercent()))
+ tCaption->setChildNeedsLayout(true);
+
+ // the optimization below doesn't work since the internal table
+ // layout could have changed. we need to add a flag to the table
+ // layout that tells us if something has changed in the min max
+ // calculations to do it correctly.
+// if ( oldWidth != m_width || columns.size() + 1 != columnPos.size() )
+ tableLayout->layout();
+
+#ifdef DEBUG_LAYOUT
+ kdDebug( 6040 ) << renderName() << "(Table)::layout1() width=" << width() << ", marginLeft=" << marginLeft() << " marginRight=" << marginRight() << endl;
+#endif
+
+ setCellWidths();
+
+ // layout child objects
+ int calculatedHeight = 0;
+
+ RenderObject *child = firstChild();
+ while( child ) {
+ // FIXME: What about a form that has a display value that makes it a table section?
+ if ( child->needsLayout() && !(child->element() && child->element()->id() == ID_FORM) )
+ child->layout();
+ if ( child->isTableSection() ) {
+ static_cast<RenderTableSection *>(child)->calcRowHeight();
+ calculatedHeight += static_cast<RenderTableSection *>(child)->layoutRows( 0 );
+ }
+ child = child->nextSibling();
+ }
+
+ // ### collapse caption margin
+ if(tCaption && tCaption->style()->captionSide() != CAPBOTTOM) {
+ tCaption->setPos(tCaption->marginLeft(), tCaption->marginTop()+m_height);
+ m_height += tCaption->height() + tCaption->marginTop() + tCaption->marginBottom();
+ }
+
+ int bpTop = borderTop() + (collapseBorders() ? 0 : paddingTop());
+ int bpBottom = borderBottom() + (collapseBorders() ? 0 : paddingBottom());
+
+ m_height += bpTop;
+
+ int oldHeight = m_height;
+ if (isPositioned())
+ m_height += calculatedHeight + bpBottom;
+ calcHeight();
+ int newHeight = m_height;
+ m_height = oldHeight;
+
+ Length h = style()->height();
+ int th = -(bpTop + bpBottom); // Tables size as though CSS height includes border/padding.
+ if (isPositioned())
+ th += newHeight;
+ else if (h.isFixed())
+ th += h.value();
+ else if (h.isPercent())
+ th += calcPercentageHeight(h);
+
+ // layout rows
+ if ( th > calculatedHeight ) {
+ // we have to redistribute that height to get the constraint correctly
+ // just force the first body to the height needed
+ // ### FIXME This should take height constraints on all table sections into account and distribute
+ // accordingly. For now this should be good enough
+ if (firstBody) {
+ firstBody->calcRowHeight();
+ firstBody->layoutRows( th - calculatedHeight );
+ }
+ else if (!style()->htmlHacks()) {
+ // Completely empty tables (with no sections or anything) should at least honor specified height
+ // in strict mode.
+ m_height += th;
+ }
+ }
+
+ int bl = borderLeft();
+ if (!collapseBorders())
+ bl += paddingLeft();
+
+ // position the table sections
+ if ( head ) {
+ head->setPos(bl, m_height);
+ m_height += head->height();
+ }
+ RenderObject *body = firstBody;
+ while ( body ) {
+ if ( body != head && body != foot && body->isTableSection() ) {
+ body->setPos(bl, m_height);
+ m_height += body->height();
+ }
+ body = body->nextSibling();
+ }
+ if ( foot ) {
+ foot->setPos(bl, m_height);
+ m_height += foot->height();
+ }
+
+ m_height += bpBottom;
+
+ if(tCaption && tCaption->style()->captionSide()==CAPBOTTOM) {
+ tCaption->setPos(tCaption->marginLeft(), tCaption->marginTop()+m_height);
+ m_height += tCaption->height() + tCaption->marginTop() + tCaption->marginBottom();
+ }
+
+ if (canvas()->pagedMode()) {
+ RenderObject *child = firstChild();
+ // relayout taking real position into account
+ while( child ) {
+ if ( !(child->element() && child->element()->id() == ID_FORM) ) {
+ child->setNeedsLayout(true);
+ child->layout();
+ if (child->containsPageBreak()) setContainsPageBreak(true);
+ if (child->needsPageClear()) setNeedsPageClear(true);
+ }
+ child = child->nextSibling();
+ }
+ }
+
+ //kdDebug(0) << "table height: " << m_height << endl;
+
+ // table can be containing block of positioned elements.
+ // ### only pass true if width or height changed.
+ layoutPositionedObjects( true );
+
+ m_overflowHeight = m_height;
+
+ setNeedsLayout(false);
+}
+
+void RenderTable::setCellWidths()
+{
+#ifdef DEBUG_LAYOUT
+ kdDebug( 6040 ) << renderName() << "(Table, this=0x" << this << ")::setCellWidths()" << endl;
+#endif
+
+ RenderObject *child = firstChild();
+ while( child ) {
+ if ( child->isTableSection() )
+ static_cast<RenderTableSection *>(child)->setCellWidths();
+ child = child->nextSibling();
+ }
+}
+
+void RenderTable::paint( PaintInfo& pI, int _tx, int _ty)
+{
+ if(needsLayout()) return;
+
+ _tx += xPos();
+ _ty += yPos();
+
+#ifdef TABLE_PRINT
+ kdDebug( 6040 ) << "RenderTable::paint() w/h = (" << width() << "/" << height() << ")" << endl;
+#endif
+ if (!overhangingContents() && !isRelPositioned() && !isPositioned())
+ {
+ int os = 2*maximalOutlineSize(pI.phase);
+ if((_ty > pI.r.y() + pI.r.height() + os) || (_ty + height() < pI.r.y() - os)) return;
+ if((_tx > pI.r.x() + pI.r.width() + os) || (_tx + width() < pI.r.x() - os)) return;
+ }
+
+#ifdef TABLE_PRINT
+ kdDebug( 6040 ) << "RenderTable::paint(2) " << _tx << "/" << _ty << " (" << _y << "/" << _h << ")" << endl;
+#endif
+
+ if (pI.phase == PaintActionOutline)
+ paintOutline(pI.p, _tx, _ty, width(), height(), style());
+
+ if(( pI.phase == PaintActionElementBackground || pI.phase == PaintActionChildBackground )
+ && shouldPaintBackgroundOrBorder() && style()->visibility() == VISIBLE)
+ paintBoxDecorations(pI, _tx, _ty);
+
+ if ( pI.phase == PaintActionElementBackground )
+ return;
+
+ PaintAction oldphase = pI.phase;
+ if ( pI.phase == PaintActionChildBackgrounds )
+ pI.phase = PaintActionChildBackground;
+
+ for( RenderObject *child = firstChild(); child; child = child->nextSibling())
+ if ( child->isTableSection() || child == tCaption )
+ child->paint( pI, _tx, _ty );
+
+ if (collapseBorders() &&
+ (pI.phase == PaintActionElementBackground || pI.phase == PaintActionChildBackground)
+ && style()->visibility() == VISIBLE) {
+ // Collect all the unique border styles that we want to paint in a sorted list. Once we
+ // have all the styles sorted, we then do individual passes, painting each style of border
+ // from lowest precedence to highest precedence.
+ pI.phase = PaintActionCollapsedTableBorders;
+ QValueList<CollapsedBorderValue> borderStyles;
+ collectBorders(borderStyles);
+#if 0
+ QString m;
+ for (uint i = 0; i < borderStyles.count(); i++)
+ m += QString("%1 ").arg((*borderStyles.at(i)).width());
+ kdDebug(6040) << m << endl;
+#endif
+ QValueListIterator<CollapsedBorderValue> it = borderStyles.begin();
+ QValueListIterator<CollapsedBorderValue> end = borderStyles.end();
+ for (; it != end; ++it) {
+ m_currentBorder = &*it;
+ for (RenderObject *child = firstChild(); child; child = child->nextSibling()) {
+ if (child->isTableSection())
+ child->paint(pI, _tx, _ty);
+ }
+ }
+ m_currentBorder = 0;
+ }
+
+ pI.phase = oldphase;
+#ifdef BOX_DEBUG
+ outlineBox(p, _tx, _ty, "blue");
+#endif
+}
+
+void RenderTable::paintBoxDecorations(PaintInfo &pI, int _tx, int _ty)
+{
+ int w = width();
+ int h = height();
+
+ // Account for the caption.
+ if (tCaption) {
+ int captionHeight = (tCaption->height() + tCaption->marginBottom() + tCaption->marginTop());
+ h -= captionHeight;
+ if (tCaption->style()->captionSide() != CAPBOTTOM)
+ _ty += captionHeight;
+ }
+
+ int my = kMax(_ty,pI.r.y());
+ int mh;
+ if (_ty<pI.r.y())
+ mh= kMax(0,h-(pI.r.y()-_ty));
+ else
+ mh = kMin(pI.r.height(),h);
+
+ paintBackground(pI.p, style()->backgroundColor(), style()->backgroundLayers(), my, mh, _tx, _ty, w, h);
+
+ if (style()->hasBorder() && !collapseBorders())
+ paintBorder(pI.p, _tx, _ty, w, h, style());
+}
+
+void RenderTable::calcMinMaxWidth()
+{
+ KHTMLAssert( !minMaxKnown() );
+
+ if ( needSectionRecalc )
+ recalcSections();
+
+#ifdef DEBUG_LAYOUT
+ kdDebug( 6040 ) << renderName() << "(Table " << this << ")::calcMinMaxWidth()" << endl;
+#endif
+
+ tableLayout->calcMinMaxWidth();
+
+ if (tCaption) {
+ tCaption->calcWidth();
+ if (tCaption->marginLeft()+tCaption->marginRight()+tCaption->minWidth() > m_minWidth)
+ m_minWidth = tCaption->marginLeft()+tCaption->marginRight()+tCaption->minWidth();
+ }
+
+ setMinMaxKnown();
+#ifdef DEBUG_LAYOUT
+ kdDebug( 6040 ) << renderName() << " END: (Table " << this << ")::calcMinMaxWidth() min = " << m_minWidth << " max = " << m_maxWidth << endl;
+#endif
+}
+
+void RenderTable::close()
+{
+// kdDebug( 6040 ) << "RenderTable::close()" << endl;
+ setNeedsLayoutAndMinMaxRecalc();
+}
+
+void RenderTable::splitColumn( int pos, int firstSpan )
+{
+ // we need to add a new columnStruct
+ int oldSize = columns.size();
+ columns.resize( oldSize + 1 );
+ int oldSpan = columns[pos].span;
+// qDebug("splitColumn( %d,%d ), oldSize=%d, oldSpan=%d", pos, firstSpan, oldSize, oldSpan );
+ KHTMLAssert( oldSpan > firstSpan );
+ columns[pos].span = firstSpan;
+ memmove( columns.data()+pos+1, columns.data()+pos, (oldSize-pos)*sizeof(ColumnStruct) );
+ columns[pos+1].span = oldSpan - firstSpan;
+
+ // change width of all rows.
+ RenderObject *child = firstChild();
+ while ( child ) {
+ if ( child->isTableSection() ) {
+ RenderTableSection *section = static_cast<RenderTableSection *>(child);
+ int size = section->grid.size();
+ int row = 0;
+ if ( section->cCol > pos )
+ section->cCol++;
+ while ( row < size ) {
+ section->grid[row].row->resize( oldSize+1 );
+ RenderTableSection::Row &r = *section->grid[row].row;
+ memmove( r.data()+pos+1, r.data()+pos, (oldSize-pos)*sizeof( RenderTableCell * ) );
+// qDebug("moving from %d to %d, num=%d", pos, pos+1, (oldSize-pos-1) );
+ r[pos+1] = r[pos] ? (RenderTableCell *)-1 : 0;
+ row++;
+ }
+ }
+ child = child->nextSibling();
+ }
+ columnPos.resize( numEffCols()+1 );
+ setNeedsLayoutAndMinMaxRecalc();
+}
+
+void RenderTable::appendColumn( int span )
+{
+ // easy case.
+ int pos = columns.size();
+// qDebug("appendColumn( %d ), size=%d", span, pos );
+ int newSize = pos + 1;
+ columns.resize( newSize );
+ columns[pos].span = span;
+ //qDebug("appending column at %d, span %d", pos, span );
+
+ // change width of all rows.
+ RenderObject *child = firstChild();
+ while ( child ) {
+ if ( child->isTableSection() ) {
+ RenderTableSection *section = static_cast<RenderTableSection *>(child);
+ int size = section->grid.size();
+ int row = 0;
+ while ( row < size ) {
+ section->grid[row].row->resize( newSize );
+ section->cellAt( row, pos ) = 0;
+ row++;
+ }
+
+ }
+ child = child->nextSibling();
+ }
+ columnPos.resize( numEffCols()+1 );
+ setNeedsLayoutAndMinMaxRecalc();
+}
+
+RenderTableCol *RenderTable::colElement( int col ) {
+ if ( !has_col_elems )
+ return 0;
+ RenderObject *child = firstChild();
+ int cCol = 0;
+ while ( child ) {
+ if ( child->isTableCol() ) {
+ RenderTableCol *colElem = static_cast<RenderTableCol *>(child);
+ int span = colElem->span();
+ if ( !colElem->firstChild() ) {
+ cCol += span;
+ if ( cCol > col )
+ return colElem;
+ }
+
+ RenderObject *next = child->firstChild();
+ if ( !next )
+ next = child->nextSibling();
+ if ( !next && child->parent()->isTableCol() )
+ next = child->parent()->nextSibling();
+ child = next;
+ } else if (child == tCaption) {
+ child = child->nextSibling();
+ } else
+ break;
+ }
+ return 0;
+}
+
+void RenderTable::recalcSections()
+{
+ tCaption = 0;
+ head = foot = firstBody = 0;
+ has_col_elems = false;
+
+ RenderObject *child = firstChild();
+ // We need to get valid pointers to caption, head, foot and firstbody again
+ while ( child ) {
+ switch(child->style()->display()) {
+ case TABLE_CAPTION:
+ if ( !tCaption && child->isRenderBlock() ) {
+ tCaption = static_cast<RenderBlock*>(child);
+ tCaption->setNeedsLayout(true);
+ }
+ break;
+ case TABLE_COLUMN:
+ case TABLE_COLUMN_GROUP:
+ has_col_elems = true;
+ break;
+ case TABLE_HEADER_GROUP:
+ if (child->isTableSection()) {
+ RenderTableSection *section = static_cast<RenderTableSection *>(child);
+ if (!head)
+ head = section;
+ else if (!firstBody)
+ firstBody = section;
+ if (section->needCellRecalc)
+ section->recalcCells();
+ }
+ break;
+ case TABLE_FOOTER_GROUP:
+ if (child->isTableSection()) {
+ RenderTableSection *section = static_cast<RenderTableSection *>(child);
+ if (!foot)
+ foot = section;
+ else if (!firstBody)
+ firstBody = section;
+ if (section->needCellRecalc)
+ section->recalcCells();
+ }
+ break;
+ case TABLE_ROW_GROUP:
+ if (child->isTableSection()) {
+ RenderTableSection *section = static_cast<RenderTableSection *>(child);
+ if (!firstBody)
+ firstBody = section;
+ if (section->needCellRecalc)
+ section->recalcCells();
+ }
+ break;
+ default:
+ break;
+ }
+ child = child->nextSibling();
+ }
+ needSectionRecalc = false;
+ setNeedsLayout(true);
+}
+
+RenderObject* RenderTable::removeChildNode(RenderObject* child)
+{
+ setNeedSectionRecalc();
+ return RenderContainer::removeChildNode( child );
+}
+
+int RenderTable::borderLeft() const
+{
+ if (collapseBorders()) {
+ // FIXME: For strict mode, returning 0 is correct, since the table border half spills into the margin,
+ // but I'm working to get this changed. For now, follow the spec.
+ return 0;
+ }
+ return RenderBlock::borderLeft();
+}
+
+int RenderTable::borderRight() const
+{
+ if (collapseBorders()) {
+ // FIXME: For strict mode, returning 0 is correct, since the table border half spills into the margin,
+ // but I'm working to get this changed. For now, follow the spec.
+ return 0;
+ }
+ return RenderBlock::borderRight();
+}
+
+int RenderTable::borderTop() const
+{
+ if (collapseBorders()) {
+ // FIXME: For strict mode, returning 0 is correct, since the table border half spills into the margin,
+ // but I'm working to get this changed. For now, follow the spec.
+ return 0;
+ }
+ return RenderBlock::borderTop();
+}
+
+int RenderTable::borderBottom() const
+{
+ if (collapseBorders()) {
+ // FIXME: For strict mode, returning 0 is correct, since the table border half spills into the margin,
+ // but I'm working to get this changed. For now, follow the spec.
+ return 0;
+ }
+ return RenderBlock::borderBottom();
+}
+
+RenderTableSection* RenderTable::sectionAbove(const RenderTableSection* section, bool skipEmptySections) const
+{
+ if (section == head)
+ return 0;
+ RenderObject *prevSection = (section == foot ? lastChild() : const_cast<RenderTableSection *>(section))->previousSibling();
+ while (prevSection) {
+ if (prevSection->isTableSection() && prevSection != head && prevSection != foot && (!skipEmptySections || static_cast<RenderTableSection*>(prevSection)->numRows()))
+ break;
+ prevSection = prevSection->previousSibling();
+ }
+ if (!prevSection && head && (!skipEmptySections || head->numRows()))
+ prevSection = head;
+ return static_cast<RenderTableSection*>(prevSection);
+}
+
+RenderTableSection* RenderTable::sectionBelow(const RenderTableSection* section, bool skipEmptySections) const
+{
+ if (section == foot)
+ return 0;
+ RenderObject *nextSection = (section == head ? firstChild() : const_cast<RenderTableSection *>(section))->nextSibling();
+ while (nextSection) {
+ if (nextSection->isTableSection() && nextSection != head && nextSection != foot && (!skipEmptySections || static_cast<RenderTableSection*>(nextSection)->numRows()))
+ break;
+ nextSection = nextSection->nextSibling();
+ }
+ if (!nextSection && foot && (!skipEmptySections || foot->numRows()))
+ nextSection = foot;
+ return static_cast<RenderTableSection*>(nextSection);
+}
+
+RenderTableCell* RenderTable::cellAbove(const RenderTableCell* cell) const
+{
+ // Find the section and row to look in
+ int r = cell->row();
+ RenderTableSection* section = 0;
+ int rAbove = 0;
+ if (r > 0) {
+ // cell is not in the first row, so use the above row in its own section
+ section = cell->section();
+ rAbove = r-1;
+ } else {
+ section = sectionAbove(cell->section(), true);
+ if (section)
+ rAbove = section->numRows() - 1;
+ }
+
+ // Look up the cell in the section's grid, which requires effective col index
+ if (section) {
+ int effCol = colToEffCol(cell->col());
+ RenderTableCell* aboveCell;
+ // If we hit a span back up to a real cell.
+ do {
+ aboveCell = section->cellAt(rAbove, effCol);
+ effCol--;
+ } while (aboveCell == (RenderTableCell *)-1 && effCol >=0);
+ return (aboveCell == (RenderTableCell *)-1) ? 0 : aboveCell;
+ } else {
+ return 0;
+ }
+}
+
+RenderTableCell* RenderTable::cellBelow(const RenderTableCell* cell) const
+{
+ // Find the section and row to look in
+ int r = cell->row() + cell->rowSpan() - 1;
+ RenderTableSection* section = 0;
+ int rBelow = 0;
+ if (r < cell->section()->numRows()-1) {
+ // The cell is not in the last row, so use the next row in the section.
+ section = cell->section();
+ rBelow= r+1;
+ } else {
+ section = sectionBelow(cell->section(), true);
+ if (section)
+ rBelow = 0;
+ }
+
+ // Look up the cell in the section's grid, which requires effective col index
+ if (section) {
+ int effCol = colToEffCol(cell->col());
+ RenderTableCell* belowCell;
+ // If we hit a colspan back up to a real cell.
+ do {
+ belowCell = section->cellAt(rBelow, effCol);
+ effCol--;
+ } while (belowCell == (RenderTableCell *)-1 && effCol >=0);
+ return (belowCell == (RenderTableCell *)-1) ? 0 : belowCell;
+ } else {
+ return 0;
+ }
+}
+
+RenderTableCell* RenderTable::cellLeft(const RenderTableCell* cell) const
+{
+ RenderTableSection* section = cell->section();
+ int effCol = colToEffCol(cell->col());
+ if (effCol == 0)
+ return 0;
+
+ // If we hit a colspan back up to a real cell.
+ RenderTableCell* prevCell;
+ do {
+ prevCell = section->cellAt(cell->row(), effCol-1);
+ effCol--;
+ } while (prevCell == (RenderTableCell *)-1 && effCol >=0);
+ return (prevCell == (RenderTableCell *)-1) ? 0 : prevCell;
+}
+
+RenderTableCell* RenderTable::cellRight(const RenderTableCell* cell) const
+{
+ int effCol = colToEffCol(cell->col()+cell->colSpan());
+ if (effCol >= numEffCols())
+ return 0;
+ RenderTableCell* result = cell->section()->cellAt(cell->row(), effCol);
+ return (result == (RenderTableCell*)-1) ? 0 : result;
+}
+
+#ifdef ENABLE_DUMP
+void RenderTable::dump(QTextStream &stream, const QString &ind) const
+{
+ RenderBlock::dump(stream, ind);
+
+ if (tCaption)
+ stream << " tCaption";
+ if (head)
+ stream << " head";
+ if (foot)
+ stream << " foot";
+
+ stream << " [cspans:";
+ for ( unsigned int i = 0; i < columns.size(); i++ )
+ stream << " " << columns[i].span;
+ stream << "]";
+}
+
+#endif
+
+FindSelectionResult RenderTable::checkSelectionPoint( int _x, int _y, int _tx, int _ty, DOM::NodeImpl*& node, int & offset, SelPointState &state )
+{
+ int off = offset;
+ DOM::NodeImpl* nod = node;
+
+ FindSelectionResult pos;
+ TableSectionIterator it(this);
+ for (; *it; ++it) {
+ pos = (*it)->checkSelectionPoint(_x, _y, _tx + m_x, _ty + m_y, nod, off, state);
+ switch(pos) {
+ case SelectionPointBeforeInLine:
+ case SelectionPointInside:
+ //kdDebug(6030) << "RenderTable::checkSelectionPoint " << this << " returning SelectionPointInside offset=" << offset << endl;
+ node = nod;
+ offset = off;
+ return SelectionPointInside;
+ case SelectionPointBefore:
+ //x,y is before this element -> stop here
+ if ( state.m_lastNode ) {
+ node = state.m_lastNode;
+ offset = state.m_lastOffset;
+ //kdDebug(6030) << "RenderTable::checkSelectionPoint " << this << " before this child "
+ // << node << "-> returning SelectionPointInside, offset=" << offset << endl;
+ return SelectionPointInside;
+ } else {
+ node = nod;
+ offset = off;
+ //kdDebug(6030) << "RenderTable::checkSelectionPoint " << this << " before us -> returning SelectionPointBefore " << node << "/" << offset << endl;
+ return SelectionPointBefore;
+ }
+ break;
+ case SelectionPointAfter:
+ if (state.m_afterInLine) break;
+ // fall through
+ case SelectionPointAfterInLine:
+ if (pos == SelectionPointAfterInLine) state.m_afterInLine = true;
+ //kdDebug(6030) << "RenderTable::checkSelectionPoint: selection after: " << nod << " offset: " << off << " afterInLine: " << state.m_afterInLine << endl;
+ state.m_lastNode = nod;
+ state.m_lastOffset = off;
+ // No "return" here, obviously. We must keep looking into the children.
+ break;
+ }
+ }
+ // If we are after the last child, return lastNode/lastOffset
+ // But lastNode can be 0L if there is no child, for instance.
+ if ( state.m_lastNode )
+ {
+ node = state.m_lastNode;
+ offset = state.m_lastOffset;
+ }
+ // Fallback
+ return SelectionPointAfter;
+}
+
+// --------------------------------------------------------------------------
+
+RenderTableSection::RenderTableSection(DOM::NodeImpl* node)
+ : RenderBox(node)
+{
+ // init RenderObject attributes
+ setInline(false); // our object is not Inline
+ cCol = 0;
+ cRow = -1;
+ needCellRecalc = false;
+}
+
+RenderTableSection::~RenderTableSection()
+{
+ clearGrid();
+}
+
+void RenderTableSection::detach()
+{
+ // recalc cell info because RenderTable has unguarded pointers
+ // stored that point to this RenderTableSection.
+ if (table())
+ table()->setNeedSectionRecalc();
+
+ RenderBox::detach();
+}
+
+void RenderTableSection::setStyle(RenderStyle* _style)
+{
+ // we don't allow changing this one
+ if (style())
+ _style->setDisplay(style()->display());
+ else if (_style->display() != TABLE_FOOTER_GROUP && _style->display() != TABLE_HEADER_GROUP)
+ _style->setDisplay(TABLE_ROW_GROUP);
+
+ RenderBox::setStyle(_style);
+}
+
+void RenderTableSection::addChild(RenderObject *child, RenderObject *beforeChild)
+{
+#ifdef DEBUG_LAYOUT
+ kdDebug( 6040 ) << renderName() << "(TableSection)::addChild( " << child->renderName() << ", beforeChild=" <<
+ (beforeChild ? beforeChild->renderName() : "0") << " )" << endl;
+#endif
+ if ( !child->isTableRow() ) {
+ // TBODY > FORM quirk (???)
+ if (child->element() && child->element()->isHTMLElement() && child->element()->id() == ID_FORM &&
+ static_cast<HTMLFormElementImpl*>(child->element())->isMalformed())
+ {
+ RenderContainer::addChild(child, beforeChild);
+ return;
+ }
+
+ RenderObject* last = beforeChild;
+ if (!last)
+ last = lastChild();
+ if (last && last->isAnonymous()) {
+ last->addChild(child);
+ return;
+ }
+
+ // If beforeChild is inside an anonymous cell/row, insert into the cell or into
+ // the anonymous row containing it, if there is one.
+ RenderObject* lastBox = last;
+ while (lastBox && lastBox->parent()->isAnonymous() && !lastBox->isTableRow())
+ lastBox = lastBox->parent();
+ if (lastBox && lastBox->isAnonymous()) {
+ lastBox->addChild(child, beforeChild);
+ return;
+ }
+
+ RenderObject* row = new (renderArena()) RenderTableRow(document() /* anonymous table */);
+ RenderStyle* newStyle = new RenderStyle();
+ newStyle->inheritFrom(style());
+ newStyle->setDisplay(TABLE_ROW);
+ row->setStyle(newStyle);
+ addChild(row, beforeChild);
+ row->addChild(child);
+ return;
+ }
+
+ if (beforeChild)
+ setNeedCellRecalc();
+
+ cRow++;
+ cCol = 0;
+
+ ensureRows( cRow+1 );
+ KHTMLAssert( child->isTableRow() );
+ grid[cRow].rowRenderer = static_cast<RenderTableRow*>(child);
+
+ if (!beforeChild) {
+ grid[cRow].height = child->style()->height();
+ if ( grid[cRow].height.isRelative() )
+ grid[cRow].height = Length();
+ }
+
+
+ RenderContainer::addChild(child,beforeChild);
+}
+
+void RenderTableSection::ensureRows( int numRows )
+{
+ int nRows = grid.size();
+ int nCols = table()->numEffCols();
+ if ( numRows > nRows ) {
+ grid.resize( numRows );
+ for ( int r = nRows; r < numRows; r++ ) {
+ grid[r].row = new Row( nCols );
+ grid[r].row->fill( 0 );
+ grid[r].rowRenderer = 0;
+ grid[r].baseLine = 0;
+ grid[r].height = Length();
+ }
+ }
+
+}
+
+void RenderTableSection::addCell( RenderTableCell *cell, RenderTableRow *row )
+{
+ int rSpan = cell->rowSpan();
+ int cSpan = cell->colSpan();
+ QMemArray<RenderTable::ColumnStruct> &columns = table()->columns;
+ int nCols = columns.size();
+
+ // ### mozilla still seems to do the old HTML way, even for strict DTD
+ // (see the annotation on table cell layouting in the CSS specs and the testcase below:
+ // <TABLE border>
+ // <TR><TD>1 <TD rowspan="2">2 <TD>3 <TD>4
+ // <TR><TD colspan="2">5
+ // </TABLE>
+ while ( cCol < nCols && cellAt( cRow, cCol ) )
+ cCol++;
+
+// qDebug("adding cell at %d/%d span=(%d/%d)", cRow, cCol, rSpan, cSpan );
+
+ if ( rSpan == 1 ) {
+ // we ignore height settings on rowspan cells
+ Length height = cell->style()->height();
+ if ( height.value() > 0 || (height.isRelative() && height.value() >= 0) ) {
+ Length cRowHeight = grid[cRow].height;
+ switch( height.type() ) {
+ case Percent:
+ if ( !cRowHeight.isPercent() ||
+ (cRowHeight.isPercent() && cRowHeight.value() < height.value() ) )
+ grid[cRow].height = height;
+ break;
+ case Fixed:
+ if ( cRowHeight.type() < Percent ||
+ ( cRowHeight.isFixed() && cRowHeight.value() < height.value() ) )
+ grid[cRow].height = height;
+ break;
+ case Relative:
+#if 0
+ // we treat this as variable. This is correct according to HTML4, as it only specifies length for the height.
+ if ( cRowHeight.type == Variable ||
+ ( cRowHeight.type == Relative && cRowHeight.value < height.value ) )
+ grid[cRow].height = height;
+ break;
+#endif
+ default:
+ break;
+ }
+ }
+ }
+
+ // make sure we have enough rows
+ ensureRows( cRow + rSpan );
+
+ grid[cRow].rowRenderer = row;
+
+ int col = cCol;
+ // tell the cell where it is
+ RenderTableCell *set = cell;
+ while ( cSpan ) {
+ int currentSpan;
+ if ( cCol >= nCols ) {
+ table()->appendColumn( cSpan );
+ currentSpan = cSpan;
+ } else {
+ if ( cSpan < columns[cCol].span )
+ table()->splitColumn( cCol, cSpan );
+ currentSpan = columns[cCol].span;
+ }
+ int r = 0;
+ while ( r < rSpan ) {
+ if ( !cellAt( cRow + r, cCol ) ) {
+// qDebug(" adding cell at %d, %d", cRow + r, cCol );
+ cellAt( cRow + r, cCol ) = set;
+ }
+ r++;
+ }
+ cCol++;
+ cSpan -= currentSpan;
+ set = (RenderTableCell *)-1;
+ }
+ if ( cell ) {
+ cell->setRow( cRow );
+ cell->setCol( table()->effColToCol( col ) );
+ }
+}
+
+
+
+void RenderTableSection::setCellWidths()
+{
+#ifdef DEBUG_LAYOUT
+ kdDebug( 6040 ) << renderName() << "(Table, this=0x" << this << ")::setCellWidths()" << endl;
+#endif
+ QMemArray<int> &columnPos = table()->columnPos;
+
+ int rows = grid.size();
+ for ( int i = 0; i < rows; i++ ) {
+ Row &row = *grid[i].row;
+ int cols = row.size();
+ for ( int j = 0; j < cols; j++ ) {
+ RenderTableCell *cell = row[j];
+// qDebug("cell[%d,%d] = %p", i, j, cell );
+ if ( !cell || cell == (RenderTableCell *)-1 )
+ continue;
+ int endCol = j;
+ int cspan = cell->colSpan();
+ while ( cspan && endCol < cols ) {
+ cspan -= table()->columns[endCol].span;
+ endCol++;
+ }
+ int w = columnPos[endCol] - columnPos[j] - table()->borderHSpacing();
+#ifdef DEBUG_LAYOUT
+ kdDebug( 6040 ) << "setting width of cell " << cell << " " << cell->row() << "/" << cell->col() << " to " << w << " colspan=" << cell->colSpan() << " start=" << j << " end=" << endCol << endl;
+#endif
+ int oldWidth = cell->width();
+ if ( w != oldWidth ) {
+ cell->setNeedsLayout(true);
+ cell->setWidth( w );
+ }
+ }
+ }
+}
+
+short RenderTableSection::width() const
+{
+ return table()->width();
+}
+
+
+void RenderTableSection::calcRowHeight()
+{
+ int indx;
+ RenderTableCell *cell;
+
+ int totalRows = grid.size();
+ int vspacing = table()->borderVSpacing();
+
+ rowPos.resize( totalRows + 1 );
+ rowPos[0] = vspacing + borderTop();
+
+ for ( int r = 0; r < totalRows; r++ ) {
+ rowPos[r+1] = 0;
+
+ int baseline=0;
+ int bdesc = 0;
+// qDebug("height of row %d is %d/%d", r, grid[r].height.value, grid[r].height.type );
+ int ch = grid[r].height.minWidth( 0 );
+ int pos = rowPos[r] + ch + (grid[r].rowRenderer ? vspacing : 0);
+
+ if ( pos > rowPos[r+1] )
+ rowPos[r+1] = pos;
+
+ Row *row = grid[r].row;
+ int totalCols = row->size();
+ int totalRows = grid.size();
+ bool pagedMode = canvas()->pagedMode();
+
+ grid[r].needFlex = false;
+
+ for ( int c = 0; c < totalCols; c++ ) {
+ cell = cellAt(r, c);
+ if ( !cell || cell == (RenderTableCell *)-1 )
+ continue;
+ if ( r < totalRows - 1 && cellAt(r+1, c) == cell )
+ continue;
+
+ if ( ( indx = r - cell->rowSpan() + 1 ) < 0 )
+ indx = 0;
+
+ if (cell->cellPercentageHeight() != -1) {
+ cell->setCellPercentageHeight(-1);
+ cell->setChildNeedsLayout(true, false);
+ if (cell->hasFlexedAnonymous()) {
+ for (RenderObject* o = cell->firstChild(); o ; o = o->nextSibling())
+ if (o->isAnonymousBlock())
+ o->setChildNeedsLayout(true, false);
+ }
+ if (pagedMode) cell->setNeedsLayout(true);
+ cell->layoutIfNeeded();
+ }
+
+ ch = cell->style()->height().width(0);
+ if ( cell->height() > ch)
+ ch = cell->height();
+
+ if (!cell->style()->height().isVariable())
+ grid[r].needFlex = true;
+
+ pos = rowPos[indx] + ch + (grid[r].rowRenderer ? vspacing : 0);
+
+ if ( pos > rowPos[r+1] )
+ rowPos[r+1] = pos;
+
+ // find out the baseline
+ EVerticalAlign va = cell->style()->verticalAlign();
+ if (va == BASELINE || va == TEXT_BOTTOM || va == TEXT_TOP
+ || va == SUPER || va == SUB)
+ {
+ int b=cell->baselinePosition();
+ if (b > cell->borderTop() + cell->paddingTop()) {
+ if (b>baseline)
+ baseline=b;
+
+ int td = rowPos[ indx ] + ch - b;
+ if (td>bdesc)
+ bdesc = td;
+ }
+ }
+ }
+
+ //do we have baseline aligned elements?
+ if (baseline) {
+ // increase rowheight if baseline requires
+ int bRowPos = baseline + bdesc + (grid[r].rowRenderer ? vspacing : 0);
+ if (rowPos[r+1]<bRowPos)
+ rowPos[r+1]=bRowPos;
+
+ grid[r].baseLine = baseline;
+ }
+
+ if ( rowPos[r+1] < rowPos[r] )
+ rowPos[r+1] = rowPos[r];
+// qDebug("rowpos(%d)=%d", r, rowPos[r] );
+ }
+}
+
+int RenderTableSection::layoutRows( int toAdd )
+{
+ int rHeight;
+ int rindx;
+ int totalRows = grid.size();
+ int hspacing = table()->borderHSpacing();
+ int vspacing = table()->borderVSpacing();
+
+ // Set the width of our section now. The rows will also be this width.
+ m_width = table()->contentWidth();
+
+ if (markedForRepaint()) {
+ repaintDuringLayout();
+ setMarkedForRepaint(false);
+ }
+
+ if (toAdd && totalRows && (rowPos[totalRows] || !nextSibling())) {
+
+ int totalHeight = rowPos[totalRows] + toAdd;
+// qDebug("layoutRows: totalHeight = %d", totalHeight );
+
+ int dh = toAdd;
+ int totalPercent = 0;
+ int numVariable = 0;
+ for ( int r = 0; r < totalRows; r++ ) {
+ if ( grid[r].height.isVariable() && !emptyRow(r))
+ numVariable++;
+ else if ( grid[r].height.isPercent() )
+ totalPercent += grid[r].height.value();
+ }
+ if ( totalPercent ) {
+// qDebug("distributing %d over percent rows totalPercent=%d", dh, totalPercent );
+ // try to satisfy percent
+ int add = 0;
+ if ( totalPercent > 100 )
+ totalPercent = 100;
+ int rh = rowPos[1]-rowPos[0];
+ for ( int r = 0; r < totalRows; r++ ) {
+ if ( totalPercent > 0 && grid[r].height.isPercent() ) {
+ int toAdd = kMin( dh, (totalHeight * grid[r].height.value() / 100)-rh );
+ // If toAdd is negative, then we don't want to shrink the row (this bug
+ // affected Outlook Web Access).
+ toAdd = kMax(0, toAdd);
+ add += toAdd;
+ dh -= toAdd;
+ totalPercent -= grid[r].height.value();
+// qDebug( "adding %d to row %d", toAdd, r );
+ }
+ if ( r < totalRows-1 )
+ rh = rowPos[r+2] - rowPos[r+1];
+ rowPos[r+1] += add;
+ }
+ }
+ if ( numVariable ) {
+ // distribute over non-empty variable rows
+// qDebug("distributing %d over variable rows numVariable=%d", dh, numVariable );
+ int add = 0;
+ int toAdd = dh/numVariable;
+ for ( int r = 0; r < totalRows; r++ ) {
+ if ( grid[r].height.isVariable() && !emptyRow(r)) {
+ add += toAdd;
+ }
+ rowPos[r+1] += add;
+ }
+ dh -= add;
+ }
+ if (dh>0 && rowPos[totalRows]) {
+ // if some left overs, distribute weighted.
+ int tot=rowPos[totalRows];
+ int add=0;
+ int prev=rowPos[0];
+ for ( int r = 0; r < totalRows; r++ ) {
+ //weight with the original height
+ add+=dh*(rowPos[r+1]-prev)/tot;
+ prev=rowPos[r+1];
+ rowPos[r+1]+=add;
+ }
+ dh -= add;
+ }
+ if (dh > totalRows) {
+ // distribute to tables with all empty rows
+ int add=0;
+ int toAdd = dh/totalRows;
+ for ( int r = 0; r < totalRows; r++ ) {
+ add += toAdd;
+ rowPos[r+1] += add;
+ }
+ dh -= add;
+ }
+ // Finally distribute round-off values
+ if (dh > 0) {
+ // There is not enough for every row
+ int add=0;
+ for ( int r = 0; r < totalRows; r++ ) {
+ if (add < dh) add++;
+ rowPos[r+1] += add;
+ }
+ dh -= add;
+ }
+ assert (dh == 0);
+ }
+
+ int leftOffset = borderLeft() + hspacing;
+
+ int nEffCols = table()->numEffCols();
+ for ( int r = 0; r < totalRows; r++ )
+ {
+ Row *row = grid[r].row;
+ int totalCols = row->size();
+
+#ifdef APPLE_CHANGES
+ // in WC, rows and cells share the same coordinate space, so that rows can have
+ // dimensions in the layer system. This is of dubious value, and a heavy maintenance burden
+ // (RenderObject's coordinates can't be used deterministically anymore) so we'll consider other options.
+
+ // Set the row's x/y position and width/height.
+ if (grid[r].rowRenderer) {
+ grid[r].rowRenderer->setPos(0, rowPos[r]);
+ grid[r].rowRenderer->setWidth(m_width);
+ grid[r].rowRenderer->setHeight(rowPos[r+1] - rowPos[r] - vspacing);
+ }
+#endif
+
+ for ( int c = 0; c < nEffCols; c++ )
+ {
+ RenderTableCell *cell = cellAt(r, c);
+ if (!cell || cell == (RenderTableCell *)-1 )
+ continue;
+ if ( r < totalRows - 1 && cell == cellAt(r+1, c) )
+ continue;
+
+ if ( ( rindx = r-cell->rowSpan()+1 ) < 0 )
+ rindx = 0;
+
+ rHeight = rowPos[r+1] - rowPos[rindx] - vspacing;
+
+ // Force percent height children to lay themselves out again.
+ // This will cause, e.g., textareas to grow to
+ // fill the area. FIXME: <div>s and blocks still don't
+ // work right. We'll need to have an efficient way of
+ // invalidating all percent height objects in a render subtree.
+ // For now, we just handle immediate children. -dwh
+
+ bool flexAllChildren = grid[r].needFlex || (!table()->style()->height().isVariable() && rHeight != cell->height());
+ cell->setHasFlexedAnonymous(false);
+ if ( flexAllChildren && flexCellChildren(cell) ) {
+ cell->setCellPercentageHeight(kMax(0,
+ rHeight - cell->borderTop() - cell->paddingTop() -
+ cell->borderBottom() - cell->paddingBottom()));
+ cell->layoutIfNeeded();
+
+ }
+ {
+#ifdef DEBUG_LAYOUT
+ kdDebug( 6040 ) << "setting position " << r << "/" << c << ": "
+ << table()->columnPos[c] /*+ padding */ << "/" << rowPos[rindx] << " height=" << rHeight<< endl;
+#endif
+
+ EVerticalAlign va = cell->style()->verticalAlign();
+ int te=0;
+ switch (va)
+ {
+ case SUB:
+ case SUPER:
+ case TEXT_TOP:
+ case TEXT_BOTTOM:
+ case BASELINE:
+ te = getBaseline(r) - cell->baselinePosition() ;
+ break;
+ case TOP:
+ te = 0;
+ break;
+ case MIDDLE:
+ te = (rHeight - cell->height())/2;
+ break;
+ case BOTTOM:
+ te = rHeight - cell->height();
+ break;
+ default:
+ break;
+ }
+ te = kMax( 0, te );
+#ifdef DEBUG_LAYOUT
+ // kdDebug( 6040 ) << "CELL " << cell << " te=" << te << ", be=" << rHeight - cell->height() - te << ", rHeight=" << rHeight << ", valign=" << va << endl;
+#endif
+ cell->setCellTopExtra( te );
+ cell->setCellBottomExtra( rHeight - cell->height() - te);
+ }
+ if (style()->direction()==RTL) {
+ cell->setPos(
+ table()->columnPos[(int)totalCols] -
+ table()->columnPos[table()->colToEffCol(cell->col()+cell->colSpan())] +
+ leftOffset,
+ rowPos[rindx] );
+ } else {
+ cell->setPos( table()->columnPos[c] + leftOffset, rowPos[rindx] );
+ }
+ }
+ }
+
+ m_height = rowPos[totalRows];
+ return m_height;
+}
+
+bool RenderTableSection::flexCellChildren(RenderObject* p) const
+{
+ if (!p)
+ return false;
+ RenderObject* o = p->firstChild();
+ bool didFlex = false;
+ while (o) {
+ if (!o->isText() && o->style()->height().isPercent()) {
+ if (o->isWidget()) {
+ // cancel resizes from transitory relayouts
+ static_cast<RenderWidget *>(o)->cancelPendingResize();
+ }
+ o->setNeedsLayout(true, false);
+ p->setChildNeedsLayout(true, false);
+ didFlex = true;
+ } else if (o->isAnonymousBlock() && flexCellChildren( o )) {
+ p->setChildNeedsLayout(true, false);
+ if (p->isTableCell())
+ static_cast<RenderTableCell*>(p)->setHasFlexedAnonymous();
+ didFlex = true;
+ }
+ o = o->nextSibling();
+ }
+ return didFlex;
+}
+
+inline static RenderTableRow *firstTableRow(RenderObject *row)
+{
+ while (row && !row->isTableRow())
+ row = row->nextSibling();
+ return static_cast<RenderTableRow *>(row);
+}
+
+inline static RenderTableRow *nextTableRow(RenderObject *row)
+{
+ row = row ? row->nextSibling() : row;
+ while (row && !row->isTableRow())
+ row = row->nextSibling();
+ return static_cast<RenderTableRow *>(row);
+}
+
+int RenderTableSection::lowestPosition(bool includeOverflowInterior, bool includeSelf) const
+{
+ int bottom = RenderBox::lowestPosition(includeOverflowInterior, includeSelf);
+ if (!includeOverflowInterior && hasOverflowClip())
+ return bottom;
+
+ for (RenderObject *row = firstChild(); row; row = row->nextSibling()) {
+ for (RenderObject *cell = row->firstChild(); cell; cell = cell->nextSibling())
+ if (cell->isTableCell()) {
+ int bp = cell->yPos() + cell->lowestPosition(false);
+ bottom = kMax(bottom, bp);
+ }
+ }
+
+ return bottom;
+}
+
+int RenderTableSection::rightmostPosition(bool includeOverflowInterior, bool includeSelf) const
+{
+ int right = RenderBox::rightmostPosition(includeOverflowInterior, includeSelf);
+ if (!includeOverflowInterior && hasOverflowClip())
+ return right;
+
+ for (RenderObject *row = firstChild(); row; row = row->nextSibling()) {
+ for (RenderObject *cell = row->firstChild(); cell; cell = cell->nextSibling())
+ if (cell->isTableCell()) {
+ int rp = cell->xPos() + cell->rightmostPosition(false);
+ right = kMax(right, rp);
+ }
+ }
+
+ return right;
+}
+
+int RenderTableSection::leftmostPosition(bool includeOverflowInterior, bool includeSelf) const
+{
+ int left = RenderBox::leftmostPosition(includeOverflowInterior, includeSelf);
+ if (!includeOverflowInterior && hasOverflowClip())
+ return left;
+
+ for (RenderObject *row = firstChild(); row; row = row->nextSibling()) {
+ for (RenderObject *cell = row->firstChild(); cell; cell = cell->nextSibling())
+ if (cell->isTableCell()) {
+ int lp = cell->xPos() + cell->leftmostPosition(false);
+ left = kMin(left, lp);
+ }
+ }
+
+ return left;
+}
+
+int RenderTableSection::highestPosition(bool includeOverflowInterior, bool includeSelf) const
+{
+ int top = RenderBox::highestPosition(includeOverflowInterior, includeSelf);
+ if (!includeOverflowInterior && hasOverflowClip())
+ return top;
+
+ for (RenderObject *row = firstChild(); row; row = row->nextSibling()) {
+ for (RenderObject *cell = row->firstChild(); cell; cell = cell->nextSibling())
+ if (cell->isTableCell()) {
+ int hp = cell->yPos() + cell->highestPosition(false);
+ top = kMin(top, hp);
+ }
+ }
+
+ return top;
+}
+
+// Search from first_row to last_row for the row containing y
+static unsigned int findRow(unsigned int first_row, unsigned int last_row,
+ const QMemArray<int> &rowPos, int y)
+{
+ unsigned int under = first_row;
+ unsigned int over = last_row;
+ int offset = (over - under)/2;
+ while (over-under > 1) {
+ if (rowPos[under+offset] <= y)
+ under = under+offset;
+ else
+ over = under+offset;
+ offset = (over - under)/2;
+ }
+
+ assert(under == first_row || rowPos[under] <= y);
+ assert(over == last_row || rowPos[over] > y);
+
+ return under;
+}
+
+static void findRowCover(unsigned int &startrow, unsigned int &endrow,
+ const QMemArray<int> &rowPos,
+ int min_y, int max_y)
+{
+ assert(max_y >= min_y);
+ unsigned int totalRows = endrow;
+
+ unsigned int index = 0;
+ // Initial binary search boost:
+ if (totalRows >= 8) {
+ int offset = (endrow - startrow)/2;
+ while (endrow - startrow > 1) {
+ index = startrow+offset;
+ if (rowPos[index] <= min_y )
+ // index is below both min_y and max_y
+ startrow = index;
+ else
+ if (rowPos[index] > max_y)
+ // index is above both min_y and max_y
+ endrow = index;
+ else
+ // index is within the selection
+ break;
+ offset = (endrow - startrow)/2;
+ }
+ }
+
+ // Binary search for startrow
+ startrow = findRow(startrow, endrow, rowPos, min_y);
+ // Binary search for endrow
+ endrow = findRow(startrow, endrow, rowPos, max_y) + 1;
+ if (endrow > totalRows) endrow = totalRows;
+}
+
+void RenderTableSection::paint( PaintInfo& pI, int tx, int ty )
+{
+ unsigned int totalRows = grid.size();
+ unsigned int totalCols = table()->columns.size();
+
+ tx += m_x;
+ ty += m_y;
+
+ if (pI.phase == PaintActionOutline)
+ paintOutline(pI.p, tx, ty, width(), height(), style());
+
+ CollapsedBorderValue *cbs = table()->currentBorderStyle();
+ int cbsw2 = cbs ? cbs->width()/2 : 0;
+ int cbsw21 = cbs ? (cbs->width()+1)/2 : 0;
+
+ int x = pI.r.x(), y = pI.r.y(), w = pI.r.width(), h = pI.r.height();
+ // check which rows and cols are visible and only paint these
+ int os = 2*maximalOutlineSize(pI.phase);
+ unsigned int startrow = 0;
+ unsigned int endrow = totalRows;
+ findRowCover(startrow, endrow, rowPos, y - os - ty - kMax(cbsw21, os), y + h + os - ty + kMax(cbsw2, os));
+
+ // A binary search is probably not worthwhile for coloumns
+ unsigned int startcol = 0;
+ unsigned int endcol = totalCols;
+ if ( style()->direction() == LTR ) {
+ for ( ; startcol < totalCols; startcol++ ) {
+ if ( tx + table()->columnPos[startcol+1] + kMax(cbsw21, os) > x - os )
+ break;
+ }
+ for ( ; endcol > 0; endcol-- ) {
+ if ( tx + table()->columnPos[endcol-1] - kMax(cbsw2, os) < x + w + os )
+ break;
+ }
+ }
+ if ( startcol < endcol ) {
+ // draw the cells
+ for ( unsigned int r = startrow; r < endrow; r++ ) {
+ // paint the row
+ if (grid[r].rowRenderer) {
+ int height = rowPos[r+1] - rowPos[r] - table()->borderVSpacing();
+ grid[r].rowRenderer->paintRow(pI, tx, ty + rowPos[r], width(), height);
+ }
+
+ unsigned int c = startcol;
+ Row *row = grid[r].row;
+ Row *nextrow = (r < endrow - 1) ? grid[r+1].row : 0;
+ // since a cell can be -1 (indicating a colspan) we might have to search backwards to include it
+ while ( c && (*row)[c] == (RenderTableCell *)-1 )
+ c--;
+ for ( ; c < endcol; c++ ) {
+ RenderTableCell *cell = (*row)[c];
+ if ( !cell || cell == (RenderTableCell *)-1 || nextrow && (*nextrow)[c] == cell )
+ continue;
+#ifdef TABLE_PRINT
+ kdDebug( 6040 ) << "painting cell " << r << "/" << c << endl;
+#endif
+ if (pI.phase == PaintActionElementBackground || pI.phase == PaintActionChildBackground) {
+ // We need to handle painting a stack of backgrounds. This stack (from bottom to top) consists of
+ // the column group, column, row group, row, and then the cell.
+ RenderObject* col = table()->colElement(c);
+ RenderObject* colGroup = 0;
+ if (col) {
+ RenderStyle *style = col->parent()->style();
+ if (style->display() == TABLE_COLUMN_GROUP)
+ colGroup = col->parent();
+ }
+ RenderObject* row = cell->parent();
+
+ // ###
+ // Column groups and columns first.
+ // FIXME: Columns and column groups do not currently support opacity, and they are being painted "too late" in
+ // the stack, since we have already opened a transparency layer (potentially) for the table row group.
+ // Note that we deliberately ignore whether or not the cell has a layer, since these backgrounds paint "behind" the
+ // cell.
+ cell->paintBackgroundsBehindCell(pI, tx, ty, colGroup);
+ cell->paintBackgroundsBehindCell(pI, tx, ty, col);
+
+ // Paint the row group next.
+ cell->paintBackgroundsBehindCell(pI, tx, ty, this);
+
+ // Paint the row next, but only if it doesn't have a layer. If a row has a layer, it will be responsible for
+ // painting the row background for the cell.
+ if (!row->layer())
+ cell->paintBackgroundsBehindCell(pI, tx, ty, row);
+ }
+
+ if ((!cell->layer() && !cell->parent()->layer()) || pI.phase == PaintActionCollapsedTableBorders)
+ cell->paint(pI, tx, ty);
+ }
+ }
+ }
+}
+
+void RenderTableSection::recalcCells()
+{
+ cCol = 0;
+ cRow = -1;
+ clearGrid();
+ grid.resize( 0 );
+
+ for (RenderObject *row = firstChild(); row; row = row->nextSibling()) {
+ if (row->isTableRow()) {
+ cRow++;
+ cCol = 0;
+ ensureRows( cRow+1 );
+ grid[cRow].rowRenderer = static_cast<RenderTableRow*>(row);
+
+ for (RenderObject *cell = row->firstChild(); cell; cell = cell->nextSibling())
+ if (cell->isTableCell())
+ addCell( static_cast<RenderTableCell *>(cell), static_cast<RenderTableRow *>(row) );
+ }
+ }
+ needCellRecalc = false;
+ setNeedsLayout(true);
+}
+
+void RenderTableSection::clearGrid()
+{
+ int rows = grid.size();
+ while ( rows-- ) {
+ delete grid[rows].row;
+ }
+}
+
+bool RenderTableSection::emptyRow(int rowNum) {
+ Row &r = *grid[rowNum].row;
+ const int s = r.size();
+ RenderTableCell *cell;
+ for(int i=0; i<s; i++) {
+ cell = r[i];
+ if (!cell || cell==(RenderTableCell*)-1)
+ continue;
+ return false;
+ }
+ return true;
+}
+
+RenderObject* RenderTableSection::removeChildNode(RenderObject* child)
+{
+ setNeedCellRecalc();
+ return RenderContainer::removeChildNode( child );
+}
+
+bool RenderTableSection::canClear(RenderObject */*child*/, PageBreakLevel level)
+{
+ // We cannot clear rows yet.
+ return parent()->canClear(this, level);
+}
+
+void RenderTableSection::addSpaceAt(int pos, int dy)
+{
+ const int nEffCols = table()->numEffCols();
+ const int totalRows = numRows();
+ for ( int r = 0; r < totalRows; r++ ) {
+ if (rowPos[r] >= pos) {
+ rowPos[r] += dy;
+ int rindx;
+ for ( int c = 0; c < nEffCols; c++ )
+ {
+ RenderTableCell *cell = cellAt(r, c);
+ if (!cell || cell == (RenderTableCell *)-1 )
+ continue;
+ if ( r > 0 && cell == cellAt(r-1, c) )
+ continue;
+
+ if ( ( rindx = r-cell->rowSpan()+1 ) < 0 )
+ rindx = 0;
+
+ cell->setPos(cell->xPos(), rowPos[r]);
+ }
+ }
+ }
+ if (rowPos[totalRows] >= pos)
+ rowPos[totalRows] += dy;
+ m_height = rowPos[totalRows];
+
+ setContainsPageBreak(true);
+}
+
+
+#ifdef ENABLE_DUMP
+void RenderTableSection::dump(QTextStream &stream, const QString &ind) const
+{
+ RenderContainer::dump(stream,ind);
+
+ stream << " grid=(" << grid.size() << "," << table()->numEffCols() << ")";
+ for ( unsigned int r = 0; r < grid.size(); r++ ) {
+ for ( int c = 0; c < table()->numEffCols(); c++ ) {
+ if ( cellAt( r, c ) && cellAt( r, c ) != (RenderTableCell *)-1 )
+ stream << " (" << cellAt( r, c )->row() << "," << cellAt( r, c )->col() << ","
+ << cellAt(r, c)->rowSpan() << "," << cellAt(r, c)->colSpan() << ") ";
+ else
+ stream << " null cell";
+ }
+ }
+}
+#endif
+
+static RenderTableCell *seekCell(RenderTableSection *section, int row, int col)
+{
+ if (row < 0 || col < 0 || row >= section->numRows()) return 0;
+ // since a cell can be -1 (indicating a colspan) we might have to search backwards to include it
+ while ( col && section->cellAt( row, col ) == (RenderTableCell *)-1 )
+ col--;
+
+ return section->cellAt(row, col);
+}
+
+/** Looks for the first element suitable for text selection, beginning from
+ * the last.
+ * @param base search is restricted within this node. This node must have
+ * a renderer.
+ * @return the element or @p base if no suitable element found.
+ */
+static NodeImpl *findLastSelectableNode(NodeImpl *base)
+{
+ NodeImpl *last = base;
+ // Look for last text/cdata node that has a renderer,
+ // or last childless replaced element
+ while ( last && !(last->renderer()
+ && ((last->nodeType() == Node::TEXT_NODE || last->nodeType() == Node::CDATA_SECTION_NODE)
+ || (last->renderer()->isReplaced() && !last->renderer()->lastChild()))))
+ {
+ NodeImpl *next = last->lastChild();
+ if ( !next ) next = last->previousSibling();
+ while ( last && last != base && !next )
+ {
+ last = last->parentNode();
+ if ( last && last != base )
+ next = last->previousSibling();
+ }
+ last = next;
+ }
+
+ return last ? last : base;
+}
+
+FindSelectionResult RenderTableSection::checkSelectionPoint( int _x, int _y, int _tx, int _ty, DOM::NodeImpl*& node, int & offset, SelPointState &state )
+{
+ // Table sections need extra treatment for selections. The rows are scanned
+ // from top to bottom, and within each row, only the cell that matches
+ // the given position best is descended into.
+
+ unsigned int totalRows = grid.size();
+ unsigned int totalCols = table()->columns.size();
+
+// absolutePosition(_tx, _ty, false);
+
+ _tx += m_x;
+ _ty += m_y;
+
+// bool save_last = false; // true to save last investigated cell
+
+ if (needsLayout() || _y < _ty) return SelectionPointBefore;
+// else if (_y >= _ty + height()) save_last = true;
+
+ // Find the row containing the pointer
+ int row_idx = findRow(0, totalRows, rowPos, _y - _ty);
+
+ int col_idx;
+ if ( style()->direction() == LTR ) {
+ for ( col_idx = (int)totalCols - 1; col_idx >= 0; col_idx-- ) {
+ if ( _tx + table()->columnPos[col_idx] < _x )
+ break;
+ }
+ if (col_idx < 0) col_idx = 0;
+ } else {
+ for ( col_idx = 0; col_idx < (int)totalCols; col_idx++ ) {
+ if ( _tx + table()->columnPos[col_idx] > _x )
+ break;
+ }
+ if (col_idx >= (int)totalCols) col_idx = (int)totalCols + 1;
+ }
+
+ FindSelectionResult pos = SelectionPointBefore;
+
+ RenderTableCell *cell = seekCell(this, row_idx, col_idx);
+ // ### dunno why cell can be 0, maybe due to weird spans? (LS)
+ if (cell) {
+ SelPointState localState;
+ pos = cell->checkSelectionPoint(_x, _y, _tx, _ty, node, offset, localState);
+ }
+
+ if (pos != SelectionPointBefore) return pos;
+
+ // store last column of last line
+ row_idx--;
+ col_idx = totalCols - 1;
+ cell = seekCell(this, row_idx, col_idx);
+
+ // end of section? take previous section
+ RenderTableSection *sec = this;
+ if (!cell) {
+ sec = *--TableSectionIterator(sec);
+ if (!sec) return pos;
+
+ cell = seekCell(sec, sec->grid.size() - 1, col_idx);
+ if (!cell) return pos;
+ }
+
+ // take last child of previous cell, and store this one as last node
+ NodeImpl *element = cell->element();
+ if (!element) return SelectionPointBefore;
+
+ element = findLastSelectableNode(element);
+
+ state.m_lastNode = element;
+ state.m_lastOffset = element->maxOffset();
+ return SelectionPointBefore;
+}
+
+// Hit Testing
+bool RenderTableSection::nodeAtPoint(NodeInfo& info, int x, int y, int tx, int ty, HitTestAction action, bool inside)
+{
+ // Table sections cannot ever be hit tested. Effectively they do not exist.
+ // Just forward to our children always.
+ tx += m_x;
+ ty += m_y;
+
+ for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
+ // FIXME: We have to skip over inline flows, since they can show up inside table rows
+ // at the moment (a demoted inline <form> for example). If we ever implement a
+ // table-specific hit-test method (which we should do for performance reasons anyway),
+ // then we can remove this check.
+ if (!child->layer() && !child->isInlineFlow() && child->nodeAtPoint(info, x, y, tx, ty, action, inside)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+// -------------------------------------------------------------------------
+
+RenderTableRow::RenderTableRow(DOM::NodeImpl* node)
+ : RenderContainer(node)
+{
+ // init RenderObject attributes
+ setInline(false); // our object is not Inline
+}
+
+RenderObject* RenderTableRow::removeChildNode(RenderObject* child)
+{
+ RenderTableSection *s = section();
+ if (s)
+ s->setNeedCellRecalc();
+
+ return RenderContainer::removeChildNode( child );
+}
+
+void RenderTableRow::detach()
+{
+ RenderTableSection *s = section();
+ if (s)
+ s->setNeedCellRecalc();
+
+ RenderContainer::detach();
+}
+
+void RenderTableRow::setStyle(RenderStyle* newStyle)
+{
+ if (section() && style() && style()->height() != newStyle->height())
+ section()->setNeedCellRecalc();
+
+ newStyle->setDisplay(TABLE_ROW);
+ RenderContainer::setStyle(newStyle);
+}
+
+void RenderTableRow::addChild(RenderObject *child, RenderObject *beforeChild)
+{
+#ifdef DEBUG_LAYOUT
+ kdDebug( 6040 ) << renderName() << "(TableRow)::addChild( " << child->renderName() << " )" << ", " <<
+ (beforeChild ? beforeChild->renderName() : "0") << " )" << endl;
+#endif
+
+ if ( !child->isTableCell() ) {
+ // TR > FORM quirk (???)
+ if (child->element() && child->element()->isHTMLElement() && child->element()->id() == ID_FORM &&
+ static_cast<HTMLFormElementImpl*>(child->element())->isMalformed())
+ {
+ RenderContainer::addChild(child, beforeChild);
+ return;
+ }
+
+ RenderObject* last = beforeChild;
+ if (!last)
+ last = lastChild();
+ if (last && last->isAnonymous() && last->isTableCell()) {
+ last->addChild(child);
+ return;
+ }
+
+ // If beforeChild is inside an anonymous cell, insert into the cell.
+ if (last && !last->isTableCell() && last->parent() && last->parent()->isAnonymous()) {
+ last->parent()->addChild(child, beforeChild);
+ return;
+ }
+
+ RenderTableCell* cell = new (renderArena()) RenderTableCell(document() /* anonymous object */);
+ RenderStyle* newStyle = new RenderStyle();
+ newStyle->inheritFrom(style());
+ newStyle->setDisplay(TABLE_CELL);
+ cell->setStyle(newStyle);
+ addChild(cell, beforeChild);
+ cell->addChild(child);
+ return;
+ }
+
+ RenderTableCell* cell = static_cast<RenderTableCell*>(child);
+
+ section()->addCell( cell, this );
+
+ RenderContainer::addChild(cell,beforeChild);
+
+ if ( beforeChild || nextSibling() )
+ section()->setNeedCellRecalc();
+}
+
+void RenderTableRow::layout()
+{
+ KHTMLAssert( needsLayout() );
+ KHTMLAssert( minMaxKnown() );
+
+ RenderObject *child = firstChild();
+ const bool pagedMode = canvas()->pagedMode();
+ while( child ) {
+ if ( child->isTableCell() ) {
+ RenderTableCell *cell = static_cast<RenderTableCell *>(child);
+ if (pagedMode) {
+ cell->setNeedsLayout(true);
+ int oldHeight = child->height();
+ cell->layout();
+ if (oldHeight > 0 && child->containsPageBreak() && child->height() != oldHeight)
+ section()->addSpaceAt(child->yPos()+1, child->height() - oldHeight);
+ } else
+ if ( child->needsLayout() ) {
+ if (markedForRepaint())
+ cell->setMarkedForRepaint( true );
+ cell->calcVerticalMargins();
+ cell->layout();
+ cell->setCellTopExtra(0);
+ cell->setCellBottomExtra(0);
+ if (child->containsPageBreak()) setContainsPageBreak(true);
+ }
+ }
+ child = child->nextSibling();
+ }
+ setMarkedForRepaint(false);
+ setNeedsLayout(false);
+}
+
+int RenderTableRow::offsetLeft() const
+{
+ RenderObject *child = firstChild();
+ while (child && !child->isTableCell())
+ child = child->nextSibling();
+
+ if (!child)
+ return 0;
+
+ return child->offsetLeft();
+}
+
+int RenderTableRow::offsetTop() const
+{
+ RenderObject *child = firstChild();
+ while (child && !child->isTableCell())
+ child = child->nextSibling();
+
+ if (!child)
+ return 0;
+
+ return child->offsetTop() -
+ static_cast<RenderTableCell*>(child)->cellTopExtra();
+}
+
+int RenderTableRow::offsetHeight() const
+{
+ RenderObject *child = firstChild();
+ while (child && !child->isTableCell())
+ child = child->nextSibling();
+
+ if (!child)
+ return 0;
+
+ return child->offsetHeight() +
+ static_cast<RenderTableCell*>(child)->cellTopExtra() +
+ static_cast<RenderTableCell*>(child)->cellBottomExtra();
+}
+
+short RenderTableRow::offsetWidth() const
+{
+ RenderObject *fc = firstChild();
+ RenderObject *lc = lastChild();
+ while (fc && !fc->isTableCell())
+ fc = fc->nextSibling();
+ while (lc && !lc->isTableCell())
+ lc = lc->previousSibling();
+ if (!lc || !fc)
+ return 0;
+
+ return lc->xPos()+lc->width()-fc->xPos();
+}
+
+void RenderTableRow::paintRow( PaintInfo& pI, int tx, int ty, int w, int h )
+{
+ if (pI.phase == PaintActionOutline)
+ paintOutline(pI.p, tx, ty, w, h, style());
+}
+
+void RenderTableRow::paint(PaintInfo& i, int tx, int ty)
+{
+ KHTMLAssert(layer());
+ if (!layer())
+ return;
+
+ for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
+ if (child->isTableCell()) {
+ // Paint the row background behind the cell.
+ if (i.phase == PaintActionElementBackground || i.phase == PaintActionChildBackground) {
+ RenderTableCell* cell = static_cast<RenderTableCell*>(child);
+ cell->paintBackgroundsBehindCell(i, tx, ty, this);
+ }
+ if (!child->layer())
+ child->paint(i, tx, ty);
+ }
+ }
+}
+
+// Hit Testing
+bool RenderTableRow::nodeAtPoint(NodeInfo& info, int x, int y, int tx, int ty, HitTestAction action, bool inside)
+{
+ // Table rows cannot ever be hit tested. Effectively they do not exist.
+ // Just forward to our children always.
+ for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
+ // FIXME: We have to skip over inline flows, since they can show up inside table rows
+ // at the moment (a demoted inline <form> for example). If we ever implement a
+ // table-specific hit-test method (which we should do for performance reasons anyway),
+ // then we can remove this check.
+ if (!child->layer() && !child->isInlineFlow() && child->nodeAtPoint(info, x, y, tx, ty, action, inside)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// -------------------------------------------------------------------------
+
+RenderTableCell::RenderTableCell(DOM::NodeImpl* _node)
+ : RenderBlock(_node)
+{
+ _col = -1;
+ _row = -1;
+ updateFromElement();
+ setShouldPaintBackgroundOrBorder(true);
+ _topExtra = 0;
+ _bottomExtra = 0;
+ m_percentageHeight = -1;
+ m_hasFlexedAnonymous = false;
+ m_widthChanged = false;
+}
+
+void RenderTableCell::detach()
+{
+ if (parent() && section())
+ section()->setNeedCellRecalc();
+
+ RenderBlock::detach();
+}
+
+void RenderTableCell::updateFromElement()
+{
+ DOM::NodeImpl *node = element();
+ if ( node && (node->id() == ID_TD || node->id() == ID_TH) ) {
+ DOM::HTMLTableCellElementImpl *tc = static_cast<DOM::HTMLTableCellElementImpl *>(node);
+ cSpan = tc->colSpan();
+ rSpan = tc->rowSpan();
+ } else {
+ cSpan = rSpan = 1;
+ }
+}
+
+Length RenderTableCell::styleOrColWidth()
+{
+ Length w = style()->width();
+ if (colSpan() > 1 || !w.isVariable())
+ return w;
+ RenderTableCol* col = table()->colElement(_col);
+ if (col)
+ w = col->style()->width();
+ return w;
+}
+
+void RenderTableCell::calcMinMaxWidth()
+{
+ KHTMLAssert( !minMaxKnown() );
+#ifdef DEBUG_LAYOUT
+ kdDebug( 6040 ) << renderName() << "(TableCell)::calcMinMaxWidth() known=" << minMaxKnown() << endl;
+#endif
+
+ if (section()->needCellRecalc)
+ section()->recalcCells();
+
+ RenderBlock::calcMinMaxWidth();
+ if (element() && style()->whiteSpace() == NORMAL) {
+ // See if nowrap was set.
+ Length w = styleOrColWidth();
+ DOMString nowrap = static_cast<ElementImpl*>(element())->getAttribute(ATTR_NOWRAP);
+ if (!nowrap.isNull() && w.isFixed() &&
+ m_minWidth < w.value() )
+ // Nowrap is set, but we didn't actually use it because of the
+ // fixed width set on the cell. Even so, it is a WinIE/Moz trait
+ // to make the minwidth of the cell into the fixed width. They do this
+ // even in strict mode, so do not make this a quirk. Affected the top
+ // of hiptop.com.
+ m_minWidth = w.value();
+ }
+
+ setMinMaxKnown();
+}
+
+void RenderTableCell::calcWidth()
+{
+}
+
+void RenderTableCell::setWidth( int width )
+{
+ if ( width != m_width ) {
+ m_width = width;
+ m_widthChanged = true;
+ }
+}
+
+void RenderTableCell::layout()
+{
+ layoutBlock( m_widthChanged );
+ m_widthChanged = false;
+}
+
+void RenderTableCell::close()
+{
+ RenderBlock::close();
+
+#ifdef DEBUG_LAYOUT
+ kdDebug( 6040 ) << renderName() << "(RenderTableCell)::close() total height =" << m_height << endl;
+#endif
+}
+
+bool RenderTableCell::requiresLayer() const {
+ // table-cell display is never positioned (css 2.1-9.7), so the only time a layer is needed
+ // is when overflow != visible (or when there is opacity when we support it)
+ return /* style()->opacity() < 1.0f || */ hasOverflowClip() || isRelPositioned();
+}
+
+void RenderTableCell::repaintRectangle(int x, int y, int w, int h, Priority p, bool f)
+{
+ RenderBlock::repaintRectangle(x, y, w, h + _topExtra + _bottomExtra, p, f);
+}
+
+bool RenderTableCell::absolutePosition(int &xPos, int &yPos, bool f) const
+{
+ bool result = RenderBlock::absolutePosition(xPos, yPos, f);
+ xPos -= parent()->xPos(); // Rows are in the same coordinate space, so don't add their offset in.
+ yPos -= parent()->yPos();
+ return result;
+}
+
+int RenderTableCell::pageTopAfter(int y) const
+{
+ return section()->pageTopAfter(y+m_y + _topExtra) - (m_y + _topExtra);
+}
+
+short RenderTableCell::baselinePosition( bool ) const
+{
+ RenderObject* o = firstChild();
+ int offset = paddingTop() + borderTop();
+ if (!o) return offset + contentHeight();
+ while (o->firstChild()) {
+ if (!o->isInline())
+ offset += o->paddingTop() + o->borderTop();
+ o = o->firstChild();
+ }
+
+ if (!o->isInline())
+ return paddingTop() + borderTop() + contentHeight();
+
+ offset += o->baselinePosition( true );
+ return offset;
+}
+
+
+void RenderTableCell::setStyle( RenderStyle *newStyle )
+{
+ if (parent() && section() && style() && style()->height() != newStyle->height())
+ section()->setNeedCellRecalc();
+
+ newStyle->setDisplay(TABLE_CELL);
+ RenderBlock::setStyle( newStyle );
+ setShouldPaintBackgroundOrBorder(true);
+
+ if (newStyle->whiteSpace() == KHTML_NOWRAP) {
+ // Figure out if we are really nowrapping or if we should just
+ // use normal instead. If the width of the cell is fixed, then
+ // we don't actually use NOWRAP.
+ if (newStyle->width().isFixed())
+ newStyle->setWhiteSpace(NORMAL);
+ else
+ newStyle->setWhiteSpace(NOWRAP);
+ }
+}
+
+bool RenderTableCell::nodeAtPoint(NodeInfo& info, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction, bool inside)
+{
+ int tx = _tx + m_x;
+ int ty = _ty + m_y;
+
+ // also include the top and bottom extra space
+ inside |= style()->visibility() != HIDDEN
+ && (_y >= ty) && (_y < ty + height() + _topExtra + _bottomExtra)
+ && (_x >= tx) && (_x < tx + width());
+
+ return RenderBlock::nodeAtPoint(info, _x, _y, _tx, _ty, hitTestAction, inside);
+}
+
+// The following rules apply for resolving conflicts and figuring out which border
+// to use.
+// (1) Borders with the 'border-style' of 'hidden' take precedence over all other conflicting
+// borders. Any border with this value suppresses all borders at this location.
+// (2) Borders with a style of 'none' have the lowest priority. Only if the border properties of all
+// the elements meeting at this edge are 'none' will the border be omitted (but note that 'none' is
+// the default value for the border style.)
+// (3) If none of the styles are 'hidden' and at least one of them is not 'none', then narrow borders
+// are discarded in favor of wider ones. If several have the same 'border-width' then styles are preferred
+// in this order: 'double', 'solid', 'dashed', 'dotted', 'ridge', 'outset', 'groove', and the lowest: 'inset'.
+// (4) If border styles differ only in color, then a style set on a cell wins over one on a row,
+// which wins over a row group, column, column group and, lastly, table. It is undefined which color
+// is used when two elements of the same type disagree.
+static CollapsedBorderValue compareBorders(const CollapsedBorderValue& border1,
+ const CollapsedBorderValue& border2)
+{
+ // Sanity check the values passed in. If either is null, return the other.
+ if (!border2.exists()) return border1;
+ if (!border1.exists()) return border2;
+
+ // Rule #1 above.
+ if (border1.style() == BHIDDEN || border2.style() == BHIDDEN)
+ return CollapsedBorderValue(); // No border should exist at this location.
+
+ // Rule #2 above. A style of 'none' has lowest priority and always loses to any other border.
+ if (border2.style() == BNONE) return border1;
+ if (border1.style() == BNONE) return border2;
+
+ // The first part of rule #3 above. Wider borders win.
+ if (border1.width() != border2.width())
+ return border1.width() > border2.width() ? border1 : border2;
+
+ // The borders have equal width. Sort by border style.
+ if (border1.style() != border2.style())
+ return border1.style() > border2.style() ? border1 : border2;
+
+ // The border have the same width and style. Rely on precedence (cell over row over row group, etc.)
+ return border1.precedence >= border2.precedence ? border1 : border2;
+}
+
+CollapsedBorderValue RenderTableCell::collapsedLeftBorder() const
+{
+ // For border left, we need to check, in order of precedence:
+ // (1) Our left border.
+ CollapsedBorderValue result(&style()->borderLeft(), BCELL);
+
+ // (2) The previous cell's right border.
+ RenderTableCell* prevCell = table()->cellLeft(this);
+ if (prevCell) {
+ result = compareBorders(result, CollapsedBorderValue(&prevCell->style()->borderRight(), BCELL));
+ if (!result.exists()) return result;
+ }
+ else if (col() == 0) {
+ // (3) Our row's left border.
+ result = compareBorders(result, CollapsedBorderValue(&parent()->style()->borderLeft(), BROW));
+ if (!result.exists()) return result;
+
+ // (4) Our row group's left border.
+ result = compareBorders(result, CollapsedBorderValue(&section()->style()->borderLeft(), BROWGROUP));
+ if (!result.exists()) return result;
+ }
+
+ // (5) Our column's left border.
+ RenderTableCol* colElt = table()->colElement(col());
+ if (colElt) {
+ result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderLeft(), BCOL));
+ if (!result.exists()) return result;
+ }
+
+ // (6) The previous column's right border.
+ if (col() > 0) {
+ colElt = table()->colElement(col()-1);
+ if (colElt) {
+ result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderRight(), BCOL));
+ if (!result.exists()) return result;
+ }
+ }
+
+ if (col() == 0) {
+ // (7) The table's left border.
+ result = compareBorders(result, CollapsedBorderValue(&table()->style()->borderLeft(), BTABLE));
+ if (!result.exists()) return result;
+ }
+
+ return result;
+}
+
+CollapsedBorderValue RenderTableCell::collapsedRightBorder() const
+{
+ RenderTable* tableElt = table();
+ bool inLastColumn = false;
+ int effCol = tableElt->colToEffCol(col()+colSpan()-1);
+ if (effCol == tableElt->numEffCols()-1)
+ inLastColumn = true;
+
+ // For border right, we need to check, in order of precedence:
+ // (1) Our right border.
+ CollapsedBorderValue result = CollapsedBorderValue(&style()->borderRight(), BCELL);
+
+ // (2) The next cell's left border.
+ if (!inLastColumn) {
+ RenderTableCell* nextCell = tableElt->cellRight(this);
+ if (nextCell) {
+ result = compareBorders(result, CollapsedBorderValue(&nextCell->style()->borderLeft(), BCELL));
+ if (!result.exists()) return result;
+ }
+ }
+ else {
+ // (3) Our row's right border.
+ result = compareBorders(result, CollapsedBorderValue(&parent()->style()->borderRight(), BROW));
+ if (!result.exists()) return result;
+
+ // (4) Our row group's right border.
+ result = compareBorders(result, CollapsedBorderValue(&section()->style()->borderRight(), BROWGROUP));
+ if (!result.exists()) return result;
+ }
+
+ // (5) Our column's right border.
+ RenderTableCol* colElt = table()->colElement(col()+colSpan()-1);
+ if (colElt) {
+ result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderRight(), BCOL));
+ if (!result.exists()) return result;
+ }
+
+ // (6) The next column's left border.
+ if (!inLastColumn) {
+ colElt = tableElt->colElement(col()+colSpan());
+ if (colElt) {
+ result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderLeft(), BCOL));
+ if (!result.exists()) return result;
+ }
+ }
+ else {
+ // (7) The table's right border.
+ result = compareBorders(result, CollapsedBorderValue(&tableElt->style()->borderRight(), BTABLE));
+ if (!result.exists()) return result;
+ }
+
+ return result;
+}
+
+CollapsedBorderValue RenderTableCell::collapsedTopBorder() const
+{
+ // For border top, we need to check, in order of precedence:
+ // (1) Our top border.
+ CollapsedBorderValue result = CollapsedBorderValue(&style()->borderTop(), BCELL);
+
+ RenderTableCell* prevCell = table()->cellAbove(this);
+ if (prevCell) {
+ // (2) A previous cell's bottom border.
+ result = compareBorders(result, CollapsedBorderValue(&prevCell->style()->borderBottom(), BCELL));
+ if (!result.exists()) return result;
+ }
+
+ // (3) Our row's top border.
+ result = compareBorders(result, CollapsedBorderValue(&parent()->style()->borderTop(), BROW));
+ if (!result.exists()) return result;
+
+ // (4) The previous row's bottom border.
+ if (prevCell) {
+ RenderObject* prevRow = 0;
+ if (prevCell->section() == section())
+ prevRow = parent()->previousSibling();
+ else
+ prevRow = prevCell->section()->lastChild();
+
+ if (prevRow) {
+ result = compareBorders(result, CollapsedBorderValue(&prevRow->style()->borderBottom(), BROW));
+ if (!result.exists()) return result;
+ }
+ }
+
+ // Now check row groups.
+ RenderTableSection* currSection = section();
+ if (row() == 0) {
+ // (5) Our row group's top border.
+ result = compareBorders(result, CollapsedBorderValue(&currSection->style()->borderTop(), BROWGROUP));
+ if (!result.exists()) return result;
+
+ // (6) Previous row group's bottom border.
+ currSection = table()->sectionAbove(currSection);
+ if (currSection) {
+ result = compareBorders(result, CollapsedBorderValue(&currSection->style()->borderBottom(), BROWGROUP));
+ if (!result.exists())
+ return result;
+ }
+ }
+
+ if (!currSection) {
+ // (8) Our column's top border.
+ RenderTableCol* colElt = table()->colElement(col());
+ if (colElt) {
+ result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderTop(), BCOL));
+ if (!result.exists()) return result;
+ }
+
+ // (9) The table's top border.
+ result = compareBorders(result, CollapsedBorderValue(&table()->style()->borderTop(), BTABLE));
+ if (!result.exists()) return result;
+ }
+
+ return result;
+}
+
+CollapsedBorderValue RenderTableCell::collapsedBottomBorder() const
+{
+ // For border top, we need to check, in order of precedence:
+ // (1) Our bottom border.
+ CollapsedBorderValue result = CollapsedBorderValue(&style()->borderBottom(), BCELL);
+
+ RenderTableCell* nextCell = table()->cellBelow(this);
+ if (nextCell) {
+ // (2) A following cell's top border.
+ result = compareBorders(result, CollapsedBorderValue(&nextCell->style()->borderTop(), BCELL));
+ if (!result.exists()) return result;
+ }
+
+ // (3) Our row's bottom border. (FIXME: Deal with rowspan!)
+ result = compareBorders(result, CollapsedBorderValue(&parent()->style()->borderBottom(), BROW));
+ if (!result.exists()) return result;
+
+ // (4) The next row's top border.
+ if (nextCell) {
+ result = compareBorders(result, CollapsedBorderValue(&nextCell->parent()->style()->borderTop(), BROW));
+ if (!result.exists()) return result;
+ }
+
+ // Now check row groups.
+ RenderTableSection* currSection = section();
+ if (row()+rowSpan() >= static_cast<RenderTableSection*>(currSection)->numRows()) {
+ // (5) Our row group's bottom border.
+ result = compareBorders(result, CollapsedBorderValue(&currSection->style()->borderBottom(), BROWGROUP));
+ if (!result.exists()) return result;
+
+ // (6) Following row group's top border.
+ currSection = table()->sectionBelow(currSection);
+ if (currSection) {
+ result = compareBorders(result, CollapsedBorderValue(&currSection->style()->borderTop(), BROWGROUP));
+ if (!result.exists())
+ return result;
+ }
+ }
+
+ if (!currSection) {
+ // (8) Our column's bottom border.
+ RenderTableCol* colElt = table()->colElement(col());
+ if (colElt) {
+ result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderBottom(), BCOL));
+ if (!result.exists()) return result;
+ }
+
+ // (9) The table's bottom border.
+ result = compareBorders(result, CollapsedBorderValue(&table()->style()->borderBottom(), BTABLE));
+ if (!result.exists()) return result;
+ }
+
+ return result;
+}
+
+int RenderTableCell::borderLeft() const
+{
+ if (table()->collapseBorders()) {
+ CollapsedBorderValue border = collapsedLeftBorder();
+ if (border.exists())
+ return (border.width()+1)/2; // Give the extra pixel to top and left.
+ return 0;
+ }
+ return RenderBlock::borderLeft();
+}
+
+int RenderTableCell::borderRight() const
+{
+ if (table()->collapseBorders()) {
+ CollapsedBorderValue border = collapsedRightBorder();
+ if (border.exists())
+ return border.width()/2;
+ return 0;
+ }
+ return RenderBlock::borderRight();
+}
+
+int RenderTableCell::borderTop() const
+{
+ if (table()->collapseBorders()) {
+ CollapsedBorderValue border = collapsedTopBorder();
+ if (border.exists())
+ return (border.width()+1)/2; // Give the extra pixel to top and left.
+ return 0;
+ }
+ return RenderBlock::borderTop();
+}
+
+int RenderTableCell::borderBottom() const
+{
+ if (table()->collapseBorders()) {
+ CollapsedBorderValue border = collapsedBottomBorder();
+ if (border.exists())
+ return border.width()/2;
+ return 0;
+ }
+ return RenderBlock::borderBottom();
+}
+
+#ifdef BOX_DEBUG
+#include <qpainter.h>
+
+static void outlineBox(QPainter *p, int _tx, int _ty, int w, int h)
+{
+ p->setPen(QPen(QColor("yellow"), 3, Qt::DotLine));
+ p->setBrush( Qt::NoBrush );
+ p->drawRect(_tx, _ty, w, h );
+}
+#endif
+
+void RenderTableCell::paint(PaintInfo& pI, int _tx, int _ty)
+{
+
+#ifdef TABLE_PRINT
+ kdDebug( 6040 ) << renderName() << "(RenderTableCell)::paint() w/h = (" << width() << "/" << height() << ")" << " _y/_h=" << pI.r.y() << "/" << pI.r.height() << endl;
+#endif
+
+ if (needsLayout()) return;
+
+ _tx += m_x;
+ _ty += m_y/* + _topExtra*/;
+
+ RenderTable *tbl = table();
+
+ // check if we need to do anything at all...
+ int os = kMax(tbl->currentBorderStyle() ? (tbl->currentBorderStyle()->border->width+1)/2 : 0, 2*maximalOutlineSize(pI.phase));
+ if (!overhangingContents() && ((_ty >= pI.r.y() + pI.r.height() + os)
+ || (_ty + _topExtra + m_height + _bottomExtra <= pI.r.y() - os))) return;
+
+ if (pI.phase == PaintActionOutline) {
+ paintOutline( pI.p, _tx, _ty, width(), height() + borderTopExtra() + borderBottomExtra(), style());
+ }
+
+ if (pI.phase == PaintActionCollapsedTableBorders && style()->visibility() == VISIBLE) {
+ int w = width();
+ int h = height() + borderTopExtra() + borderBottomExtra();
+ paintCollapsedBorder(pI.p, _tx, _ty, w, h);
+ }
+ else
+ RenderBlock::paintObject(pI, _tx, _ty + _topExtra, false);
+
+#ifdef BOX_DEBUG
+ ::outlineBox( p, _tx, _ty - _topExtra, width(), height() + borderTopExtra() + borderBottomExtra());
+#endif
+}
+
+static EBorderStyle collapsedBorderStyle(EBorderStyle style)
+{
+ if (style == OUTSET)
+ style = GROOVE;
+ else if (style == INSET)
+ style = RIDGE;
+ return style;
+}
+
+struct CollapsedBorder {
+ CollapsedBorder(){}
+
+ CollapsedBorderValue border;
+ RenderObject::BorderSide side;
+ bool shouldPaint;
+ int x1;
+ int y1;
+ int x2;
+ int y2;
+ EBorderStyle style;
+};
+
+class CollapsedBorders
+{
+public:
+ CollapsedBorders() :count(0) {}
+
+ void addBorder(const CollapsedBorderValue& b, RenderObject::BorderSide s, bool paint,
+ int _x1, int _y1, int _x2, int _y2,
+ EBorderStyle _style)
+ {
+ if (b.exists() && paint) {
+ borders[count].border = b;
+ borders[count].side = s;
+ borders[count].shouldPaint = paint;
+ borders[count].x1 = _x1;
+ borders[count].x2 = _x2;
+ borders[count].y1 = _y1;
+ borders[count].y2 = _y2;
+ borders[count].style = _style;
+ count++;
+ }
+ }
+
+ CollapsedBorder* nextBorder() {
+ for (int i = 0; i < count; i++) {
+ if (borders[i].border.exists() && borders[i].shouldPaint) {
+ borders[i].shouldPaint = false;
+ return &borders[i];
+ }
+ }
+
+ return 0;
+ }
+
+ CollapsedBorder borders[4];
+ int count;
+};
+
+static void addBorderStyle(QValueList<CollapsedBorderValue>& borderStyles, CollapsedBorderValue borderValue)
+{
+ if (!borderValue.exists() || borderStyles.contains(borderValue))
+ return;
+
+ QValueListIterator<CollapsedBorderValue> it = borderStyles.begin();
+ QValueListIterator<CollapsedBorderValue> end = borderStyles.end();
+ for (; it != end; ++it) {
+ CollapsedBorderValue result = compareBorders(*it, borderValue);
+ if (result == *it) {
+ borderStyles.insert(it, borderValue);
+ return;
+ }
+ }
+
+ borderStyles.append(borderValue);
+}
+
+void RenderTableCell::collectBorders(QValueList<CollapsedBorderValue>& borderStyles)
+{
+ addBorderStyle(borderStyles, collapsedLeftBorder());
+ addBorderStyle(borderStyles, collapsedRightBorder());
+ addBorderStyle(borderStyles, collapsedTopBorder());
+ addBorderStyle(borderStyles, collapsedBottomBorder());
+}
+
+void RenderTableCell::paintCollapsedBorder(QPainter* p, int _tx, int _ty, int w, int h)
+{
+ if (!table()->currentBorderStyle())
+ return;
+
+ CollapsedBorderValue leftVal = collapsedLeftBorder();
+ CollapsedBorderValue rightVal = collapsedRightBorder();
+ CollapsedBorderValue topVal = collapsedTopBorder();
+ CollapsedBorderValue bottomVal = collapsedBottomBorder();
+
+ // Adjust our x/y/width/height so that we paint the collapsed borders at the correct location.
+ int topWidth = topVal.width();
+ int bottomWidth = bottomVal.width();
+ int leftWidth = leftVal.width();
+ int rightWidth = rightVal.width();
+
+ _tx -= leftWidth/2;
+ _ty -= topWidth/2;
+ w += leftWidth/2 + (rightWidth+1)/2;
+ h += topWidth/2 + (bottomWidth+1)/2;
+
+ bool tt = topVal.isTransparent();
+ bool bt = bottomVal.isTransparent();
+ bool rt = rightVal.isTransparent();
+ bool lt = leftVal.isTransparent();
+
+ EBorderStyle ts = collapsedBorderStyle(topVal.style());
+ EBorderStyle bs = collapsedBorderStyle(bottomVal.style());
+ EBorderStyle ls = collapsedBorderStyle(leftVal.style());
+ EBorderStyle rs = collapsedBorderStyle(rightVal.style());
+
+ bool render_t = ts > BHIDDEN && !tt && (topVal.precedence != BCELL || *topVal.border == style()->borderTop());
+ bool render_l = ls > BHIDDEN && !lt && (leftVal.precedence != BCELL || *leftVal.border == style()->borderLeft());
+ bool render_r = rs > BHIDDEN && !rt && (rightVal.precedence != BCELL || *rightVal.border == style()->borderRight());
+ bool render_b = bs > BHIDDEN && !bt && (bottomVal.precedence != BCELL || *bottomVal.border == style()->borderBottom());
+
+ // We never paint diagonals at the joins. We simply let the border with the highest
+ // precedence paint on top of borders with lower precedence.
+ CollapsedBorders borders;
+ borders.addBorder(topVal, BSTop, render_t, _tx, _ty, _tx + w, _ty + topWidth, ts);
+ borders.addBorder(bottomVal, BSBottom, render_b, _tx, _ty + h - bottomWidth, _tx + w, _ty + h, bs);
+ borders.addBorder(leftVal, BSLeft, render_l, _tx, _ty, _tx + leftWidth, _ty + h, ls);
+ borders.addBorder(rightVal, BSRight, render_r, _tx + w - rightWidth, _ty, _tx + w, _ty + h, rs);
+
+ for (CollapsedBorder* border = borders.nextBorder(); border; border = borders.nextBorder()) {
+ if (border->border == *table()->currentBorderStyle())
+ drawBorder(p, border->x1, border->y1, border->x2, border->y2, border->side,
+ border->border.color(), style()->color(), border->style, 0, 0);
+ }
+}
+
+void RenderTableCell::paintBackgroundsBehindCell(PaintInfo& pI, int _tx, int _ty, RenderObject* backgroundObject)
+{
+ if (!backgroundObject)
+ return;
+
+ RenderTable* tableElt = table();
+ if (backgroundObject != this) {
+ _tx += m_x;
+ _ty += m_y + _topExtra;
+ }
+
+ int w = width();
+ int h = height() + borderTopExtra() + borderBottomExtra();
+ _ty -= borderTopExtra();
+
+ int my = kMax(_ty,pI.r.y());
+ int end = kMin( pI.r.y() + pI.r.height(), _ty + h );
+ int mh = end - my;
+
+ QColor c = backgroundObject->style()->backgroundColor();
+ const BackgroundLayer* bgLayer = backgroundObject->style()->backgroundLayers();
+
+ if (bgLayer->hasImage() || c.isValid()) {
+ // We have to clip here because the background would paint
+ // on top of the borders otherwise. This only matters for cells and rows.
+ bool hasLayer = backgroundObject->layer() && (backgroundObject == this || backgroundObject == parent());
+ if (hasLayer && tableElt->collapseBorders()) {
+ pI.p->save();
+ QRect clipRect(_tx + borderLeft(), _ty + borderTop(), w - borderLeft() - borderRight(), h - borderTop() - borderBottom());
+ clipRect = pI.p->xForm(clipRect);
+ QRegion creg(clipRect);
+ QRegion old = pI.p->clipRegion();
+ if (!old.isNull())
+ creg = old.intersect(creg);
+ pI.p->setClipRegion(creg);
+ }
+ paintBackground(pI.p, c, bgLayer, my, mh, _tx, _ty, w, h);
+ if (hasLayer && tableElt->collapseBorders())
+ pI.p->restore();
+ }
+}
+
+void RenderTableCell::paintBoxDecorations(PaintInfo& pI, int _tx, int _ty)
+{
+ RenderTable* tableElt = table();
+ bool drawBorders = true;
+ // Moz paints bgcolor/bgimage on <td>s in quirks mode even if
+ // empty-cells are on. Fixes regression on #43426, attachment #354
+ if (!tableElt->collapseBorders() && style()->emptyCells() == HIDE && !firstChild())
+ drawBorders = false;
+ if (!style()->htmlHacks() && !drawBorders) return;
+
+ // Paint our cell background.
+ paintBackgroundsBehindCell(pI, _tx, _ty, this);
+
+ int w = width();
+ int h = height() + borderTopExtra() + borderBottomExtra();
+ _ty -= borderTopExtra();
+
+ if (drawBorders && style()->hasBorder() && !tableElt->collapseBorders())
+ paintBorder(pI.p, _tx, _ty, w, h, style());
+}
+
+
+#ifdef ENABLE_DUMP
+void RenderTableCell::dump(QTextStream &stream, const QString &ind) const
+{
+ RenderFlow::dump(stream,ind);
+ stream << " row=" << _row;
+ stream << " col=" << _col;
+ stream << " rSpan=" << rSpan;
+ stream << " cSpan=" << cSpan;
+// *stream << " nWrap=" << nWrap;
+}
+#endif
+
+// -------------------------------------------------------------------------
+
+RenderTableCol::RenderTableCol(DOM::NodeImpl* node)
+ : RenderContainer(node), m_span(1)
+{
+ // init RenderObject attributes
+ setInline(true); // our object is not Inline
+ updateFromElement();
+}
+
+void RenderTableCol::updateFromElement()
+{
+ DOM::NodeImpl *node = element();
+ if ( node && (node->id() == ID_COL || node->id() == ID_COLGROUP) ) {
+ DOM::HTMLTableColElementImpl *tc = static_cast<DOM::HTMLTableColElementImpl *>(node);
+ m_span = tc->span();
+ } else
+ m_span = ! ( style() && style()->display() == TABLE_COLUMN_GROUP );
+}
+
+#ifdef ENABLE_DUMP
+void RenderTableCol::dump(QTextStream &stream, const QString &ind) const
+{
+ RenderContainer::dump(stream,ind);
+ stream << " _span=" << m_span;
+}
+#endif
+
+// -------------------------------------------------------------------------
+
+TableSectionIterator::TableSectionIterator(RenderTable *table, bool fromEnd)
+{
+ if (fromEnd) {
+ sec = table->foot;
+ if (sec) return;
+
+ sec = static_cast<RenderTableSection *>(table->lastChild());
+ while (sec && (!sec->isTableSection()
+ || sec == table->head || sec == table->foot))
+ sec = static_cast<RenderTableSection *>(sec->previousSibling());
+ if (sec) return;
+
+ sec = table->head;
+ } else {
+ sec = table->head;
+ if (sec) return;
+
+ sec = static_cast<RenderTableSection *>(table->firstChild());
+ while (sec && (!sec->isTableSection()
+ || sec == table->head || sec == table->foot))
+ sec = static_cast<RenderTableSection *>(sec->nextSibling());
+ if (sec) return;
+
+ sec = table->foot;
+ }/*end if*/
+
+}
+
+TableSectionIterator &TableSectionIterator::operator ++()
+{
+ RenderTable *table = sec->table();
+ if (sec == table->head) {
+
+ sec = static_cast<RenderTableSection *>(table->firstChild());
+ while (sec && (!sec->isTableSection()
+ || sec == table->head || sec == table->foot))
+ sec = static_cast<RenderTableSection *>(sec->nextSibling());
+ if (sec) return *this;
+
+ } else if (sec == table->foot) {
+ sec = 0;
+ return *this;
+
+ } else {
+
+ do {
+ sec = static_cast<RenderTableSection *>(sec->nextSibling());
+ } while (sec && (!sec->isTableSection() || sec == table->head || sec == table->foot));
+ if (sec) return *this;
+
+ }/*end if*/
+
+ sec = table->foot;
+ return *this;
+}
+
+TableSectionIterator &TableSectionIterator::operator --()
+{
+ RenderTable *table = sec->table();
+ if (sec == table->foot) {
+
+ sec = static_cast<RenderTableSection *>(table->lastChild());
+ while (sec && (!sec->isTableSection()
+ || sec == table->head || sec == table->foot))
+ sec = static_cast<RenderTableSection *>(sec->previousSibling());
+ if (sec) return *this;
+
+ } else if (sec == table->head) {
+ sec = 0;
+ return *this;
+
+ } else {
+
+ do {
+ sec = static_cast<RenderTableSection *>(sec->previousSibling());
+ } while (sec && (!sec->isTableSection() || sec == table->head || sec == table->foot));
+ if (sec) return *this;
+
+ }/*end if*/
+
+ sec = table->foot;
+ return *this;
+}
+
+#undef TABLE_DEBUG
+#undef DEBUG_LAYOUT
+#undef BOX_DEBUG
diff --git a/khtml/rendering/render_table.h b/khtml/rendering/render_table.h
new file mode 100644
index 000000000..979b92187
--- /dev/null
+++ b/khtml/rendering/render_table.h
@@ -0,0 +1,524 @@
+/*
+ * This file is part of the DOM implementation for KDE.
+ *
+ * Copyright (C) 1997 Martin Jones (mjones@kde.org)
+ * (C) 1997 Torben Weis (weis@kde.org)
+ * (C) 1998 Waldo Bastian (bastian@kde.org)
+ * (C) 1999-2003 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * (C) 2003 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.
+ *
+ */
+#ifndef RENDER_TABLE_H
+#define RENDER_TABLE_H
+
+#include <qcolor.h>
+#include <qptrvector.h>
+
+#include "rendering/render_box.h"
+#include "rendering/render_block.h"
+#include "rendering/render_style.h"
+
+#include "misc/khtmllayout.h"
+
+namespace DOM {
+ class DOMString;
+}
+
+namespace khtml {
+
+class RenderTable;
+class RenderTableSection;
+class RenderTableRow;
+class RenderTableCell;
+class RenderTableCol;
+class TableLayout;
+
+class RenderTable : public RenderBlock
+{
+public:
+
+ RenderTable(DOM::NodeImpl* node);
+ ~RenderTable();
+
+ virtual const char *renderName() const { return "RenderTable"; }
+
+ virtual void setStyle(RenderStyle *style);
+
+ virtual bool isTable() const { return true; }
+
+ int getColumnPos(int col) const
+ { return columnPos[col]; }
+
+ int borderHSpacing() const { return hspacing; }
+ int borderVSpacing() const { return vspacing; }
+
+ bool collapseBorders() const { return style()->borderCollapse(); }
+ int borderLeft() const;
+ int borderRight() const;
+ int borderTop() const;
+ int borderBottom() const;
+ int paddingLeft() const { return collapseBorders() ? 0 : RenderBlock::paddingLeft(); }
+ int paddingRight() const { return collapseBorders() ? 0 : RenderBlock::paddingRight(); }
+ int paddingTop() const { return collapseBorders() ? 0 : RenderBlock::paddingTop(); }
+ int paddingBottom() const { return collapseBorders() ? 0 : RenderBlock::paddingBottom(); }
+
+ const QColor &bgColor() const { return style()->backgroundColor(); }
+
+ uint cellPadding() const { return padding; }
+ void setCellPadding( uint p ) { padding = p; }
+
+ // overrides
+ virtual void addChild(RenderObject *child, RenderObject *beforeChild = 0);
+ virtual void paint( PaintInfo&, int tx, int ty);
+ virtual void paintBoxDecorations(PaintInfo&, int _tx, int _ty);
+ virtual void layout();
+ virtual void calcMinMaxWidth();
+ virtual void close();
+
+ virtual short lineHeight(bool b) const;
+ virtual short baselinePosition(bool b) const;
+
+ virtual void setCellWidths( );
+
+ virtual void calcWidth();
+
+ virtual FindSelectionResult checkSelectionPoint( int _x, int _y, int _tx, int _ty,
+ DOM::NodeImpl*& node, int & offset,
+ SelPointState & );
+
+#ifdef ENABLE_DUMP
+ virtual void dump(QTextStream &stream, const QString &ind) const;
+#endif
+ struct ColumnStruct {
+ enum {
+ WidthUndefined = 0xffff
+ };
+ ColumnStruct() {
+ span = 1;
+ width = WidthUndefined;
+ }
+ ushort span;
+ ushort width; // the calculated position of the column
+ };
+
+ QMemArray<int> columnPos;
+ QMemArray<ColumnStruct> columns;
+
+ void splitColumn( int pos, int firstSpan );
+ void appendColumn( int span );
+ int numEffCols() const { return columns.size(); }
+ int spanOfEffCol( int effCol ) const { return columns[effCol].span; }
+ int colToEffCol( int col ) const {
+ int c = 0;
+ int i = 0;
+ while ( c < col && i < (int)columns.size() ) {
+ c += columns[i].span;
+ i++;
+ }
+ return i;
+ }
+ int effColToCol( int effCol ) const {
+ int c = 0;
+ for ( int i = 0; i < effCol; i++ )
+ c += columns[i].span;
+ return c;
+ }
+
+ int bordersPaddingAndSpacing() const {
+ return borderLeft() + borderRight() +
+ (collapseBorders() ? 0 : (paddingLeft() + paddingRight() + (numEffCols()+1) * borderHSpacing()));
+ }
+
+ RenderTableCol *colElement( int col );
+
+ void setNeedSectionRecalc() { needSectionRecalc = true; }
+
+ virtual RenderObject* removeChildNode(RenderObject* child);
+
+ RenderTableSection* sectionAbove(const RenderTableSection*, bool skipEmptySections = false) const;
+ RenderTableSection* sectionBelow(const RenderTableSection*, bool skipEmptySections = false) const;
+
+ RenderTableCell* cellAbove(const RenderTableCell* cell) const;
+ RenderTableCell* cellBelow(const RenderTableCell* cell) const;
+ RenderTableCell* cellLeft(const RenderTableCell* cell) const;
+ RenderTableCell* cellRight(const RenderTableCell* cell) const;
+
+ CollapsedBorderValue* currentBorderStyle() { return m_currentBorder; }
+
+ RenderTableSection *firstBodySection() const { return firstBody; }
+ RenderFlow* caption() const { return tCaption; }
+
+protected:
+
+ void recalcSections();
+
+ friend class AutoTableLayout;
+ friend class FixedTableLayout;
+
+ RenderFlow *tCaption;
+ RenderTableSection *head;
+ RenderTableSection *foot;
+ RenderTableSection *firstBody;
+
+ TableLayout *tableLayout;
+
+ CollapsedBorderValue* m_currentBorder;
+
+ bool has_col_elems : 1;
+ uint needSectionRecalc : 1;
+ uint padding : 22;
+
+ ushort hspacing;
+ ushort vspacing;
+
+ friend class TableSectionIterator;
+};
+
+// -------------------------------------------------------------------------
+
+class RenderTableSection : public RenderBox
+{
+public:
+ RenderTableSection(DOM::NodeImpl* node);
+ ~RenderTableSection();
+ virtual void detach();
+
+ virtual void setStyle(RenderStyle *style);
+
+ virtual const char *renderName() const { return "RenderTableSection"; }
+
+ // overrides
+ virtual void addChild(RenderObject *child, RenderObject *beforeChild = 0);
+ virtual bool isTableSection() const { return true; }
+
+ virtual short lineHeight(bool) const { return 0; }
+ virtual void position(InlineBox*, int, int, bool) {}
+
+ virtual short width() const;
+
+ virtual FindSelectionResult checkSelectionPoint( int _x, int _y, int _tx, int _ty,
+ DOM::NodeImpl*& node, int & offset,
+ SelPointState & );
+
+#ifdef ENABLE_DUMP
+ virtual void dump(QTextStream &stream, const QString &ind) const;
+#endif
+
+ void addCell( RenderTableCell *cell, RenderTableRow *row );
+
+ void setCellWidths();
+ void calcRowHeight();
+ int layoutRows( int height );
+
+ RenderTable *table() const { return static_cast<RenderTable *>(parent()); }
+
+ typedef QMemArray<RenderTableCell *> Row;
+ struct RowStruct {
+ Row *row;
+ RenderTableRow* rowRenderer;
+ int baseLine;
+ Length height;
+ bool needFlex;
+ };
+
+ RenderTableCell *&cellAt( int row, int col ) {
+ return (*(grid[row].row))[col];
+ }
+ RenderTableCell *cellAt( int row, int col ) const {
+ return (*(grid[row].row))[col];
+ }
+
+ virtual int lowestPosition(bool includeOverflowInterior, bool includeSelf) const;
+ virtual int rightmostPosition(bool includeOverflowInterior, bool includeSelf) const;
+ virtual int leftmostPosition(bool includeOverflowInterior, bool includeSelf) const;
+ virtual int highestPosition(bool includeOverflowInterior, bool includeSelf) const;
+
+ int borderLeft() const { return table()->collapseBorders() ? 0 : RenderBox::borderLeft(); }
+ int borderRight() const { return table()->collapseBorders() ? 0 : RenderBox::borderRight(); }
+ int borderTop() const { return table()->collapseBorders() ? 0 : RenderBox::borderTop(); }
+ int borderBottom() const { return table()->collapseBorders() ? 0 : RenderBox::borderBottom(); }
+
+ virtual void paint( PaintInfo& i, int tx, int ty);
+
+ int numRows() const { return grid.size(); }
+ int getBaseline(int row) {return grid[row].baseLine;}
+
+ void setNeedCellRecalc() {
+ needCellRecalc = true;
+ table()->setNeedSectionRecalc();
+ }
+
+ virtual RenderObject* removeChildNode(RenderObject* child);
+
+ virtual bool canClear(RenderObject *child, PageBreakLevel level);
+ void addSpaceAt(int pos, int dy);
+
+ virtual bool nodeAtPoint(NodeInfo& info, int x, int y, int tx, int ty, HitTestAction action, bool inside);
+
+ // this gets a cell grid data structure. changing the number of
+ // columns is done by the table
+ QMemArray<RowStruct> grid;
+ QMemArray<int> rowPos;
+
+ signed short cRow;
+ ushort cCol;
+ bool needCellRecalc;
+
+ void recalcCells();
+protected:
+ void ensureRows( int numRows );
+ void clearGrid();
+ bool emptyRow(int rowNum);
+ bool flexCellChildren(RenderObject* p) const;
+
+
+ friend class TableSectionIterator;
+};
+
+// -------------------------------------------------------------------------
+
+class RenderTableRow : public RenderContainer
+{
+public:
+ RenderTableRow(DOM::NodeImpl* node);
+
+ virtual void detach();
+
+ virtual void setStyle( RenderStyle* );
+ virtual const char *renderName() const { return "RenderTableRow"; }
+ virtual bool isTableRow() const { return true; }
+ virtual void addChild(RenderObject *child, RenderObject *beforeChild = 0);
+
+ virtual short offsetWidth() const;
+ virtual int offsetHeight() const;
+ virtual int offsetLeft() const;
+ virtual int offsetTop() const;
+
+ virtual short lineHeight( bool ) const { return 0; }
+ virtual void position(InlineBox*, int, int, bool) {}
+
+ virtual bool nodeAtPoint(NodeInfo& info, int x, int y, int tx, int ty, HitTestAction action, bool inside);
+
+ virtual void layout();
+
+ virtual RenderObject* removeChildNode(RenderObject* child);
+
+ // The only time rows get a layer is when they have transparency.
+ virtual bool requiresLayer() const { return /* style()->opacity() < 1.0f; */ false ; }
+ virtual void paint(PaintInfo& i, int tx, int ty);
+
+ void paintRow( PaintInfo& i, int tx, int ty, int w, int h);
+
+ RenderTable *table() const { return static_cast<RenderTable *>(parent()->parent()); }
+ RenderTableSection *section() const { return static_cast<RenderTableSection *>(parent()); }
+};
+
+// -------------------------------------------------------------------------
+
+class RenderTableCell : public RenderBlock
+{
+public:
+ RenderTableCell(DOM::NodeImpl* node);
+
+ virtual void layout();
+ virtual void detach();
+
+ virtual const char *renderName() const { return "RenderTableCell"; }
+ virtual bool isTableCell() const { return true; }
+
+ // ### FIX these two...
+ long cellIndex() const { return 0; }
+ void setCellIndex( long ) { }
+
+ unsigned short colSpan() const { return cSpan; }
+ void setColSpan( unsigned short c ) { cSpan = c; }
+
+ unsigned short rowSpan() const { return rSpan; }
+ void setRowSpan( unsigned short r ) { rSpan = r; }
+
+ int col() const { return _col; }
+ void setCol(int col) { _col = col; }
+ int row() const { return _row; }
+ void setRow(int r) { _row = r; }
+
+ Length styleOrColWidth();
+
+ // overrides
+ virtual void calcMinMaxWidth();
+ virtual void calcWidth();
+ virtual void setWidth( int width );
+ virtual void setStyle( RenderStyle *style );
+ virtual bool requiresLayer() const;
+
+ int borderLeft() const;
+ int borderRight() const;
+ int borderTop() const;
+ int borderBottom() const;
+
+ CollapsedBorderValue collapsedLeftBorder() const;
+ CollapsedBorderValue collapsedRightBorder() const;
+ CollapsedBorderValue collapsedTopBorder() const;
+ CollapsedBorderValue collapsedBottomBorder() const;
+ virtual void collectBorders(QValueList<CollapsedBorderValue>& borderStyles);
+
+ virtual void updateFromElement();
+
+ void setCellTopExtra(int p) { _topExtra = p; }
+ void setCellBottomExtra(int p) { _bottomExtra = p; }
+ int cellTopExtra() const { return _topExtra; }
+ int cellBottomExtra() const { return _bottomExtra; }
+
+ int pageTopAfter(int x) const;
+
+ virtual void paint( PaintInfo& i, int tx, int ty);
+
+ void paintCollapsedBorder(QPainter* p, int x, int y, int w, int h);
+ void paintBackgroundsBehindCell(PaintInfo& i, int _tx, int _ty, RenderObject* backgroundObject);
+
+ virtual void close();
+
+ // lie position to outside observers
+ virtual int yPos() const { return m_y + _topExtra; }
+
+ virtual void repaintRectangle(int x, int y, int w, int h, Priority p=NormalPriority, bool f=false);
+ virtual bool absolutePosition(int &xPos, int &yPos, bool f = false) const;
+
+ virtual short baselinePosition( bool = false ) const;
+
+ virtual bool nodeAtPoint(NodeInfo& info, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction, bool inside);
+
+ RenderTable *table() const { return static_cast<RenderTable *>(parent()->parent()->parent()); }
+ RenderTableSection *section() const { return static_cast<RenderTableSection *>(parent()->parent()); }
+
+#ifdef ENABLE_DUMP
+ virtual void dump(QTextStream &stream, const QString &ind) const;
+#endif
+
+ bool widthChanged() {
+ bool retval = m_widthChanged;
+ m_widthChanged = false;
+ return retval;
+ }
+
+ int cellPercentageHeight() const
+ { return m_percentageHeight; }
+ void setCellPercentageHeight(int h)
+ { m_percentageHeight = h; }
+ bool hasFlexedAnonymous() const
+ { return m_hasFlexedAnonymous; }
+ void setHasFlexedAnonymous(bool b=true)
+ { m_hasFlexedAnonymous = b; }
+
+protected:
+ virtual void paintBoxDecorations(PaintInfo& p, int _tx, int _ty);
+ virtual int borderTopExtra() const { return _topExtra; }
+ virtual int borderBottomExtra() const { return _bottomExtra; }
+
+ short _row;
+ short _col;
+ ushort rSpan;
+ ushort cSpan;
+ int _topExtra;
+ signed int _bottomExtra : 30;
+ bool m_widthChanged : 1;
+ bool m_hasFlexedAnonymous : 1;
+ int m_percentageHeight;
+};
+
+
+// -------------------------------------------------------------------------
+
+class RenderTableCol : public RenderContainer
+{
+public:
+ RenderTableCol(DOM::NodeImpl* node);
+
+ virtual const char *renderName() const { return "RenderTableCol"; }
+
+ virtual bool isTableCol() const { return true; }
+
+ virtual short lineHeight( bool ) const { return 0; }
+ virtual void position(InlineBox*, int, int, bool) {}
+ virtual void layout() {}
+ virtual bool requiresLayer() const { return false; }
+
+ virtual void updateFromElement();
+
+#ifdef ENABLE_DUMP
+ virtual void dump(QTextStream &stream, const QString& ind) const;
+#endif
+
+ int span() const { return m_span; }
+ void setSpan( int s ) { m_span = s; }
+
+private:
+ int m_span;
+};
+
+// -------------------------------------------------------------------------
+
+/** This class provides an iterator to iterate through the sections of a
+ * render table in their visual order.
+ *
+ * In HTML, sections are specified in the order of THEAD, TFOOT, and TBODY.
+ * Visually, TBODY sections appear between THEAD and TFOOT, which this iterator
+ * takes into regard.
+ * @author Leo Savernik
+ * @internal
+ * @since 3.2
+ */
+class TableSectionIterator {
+public:
+
+ /**
+ * Initializes a new iterator
+ * @param table table whose sections to iterate
+ * @param fromEnd @p true, begin with last section, @p false, begin with
+ * first section.
+ */
+ TableSectionIterator(RenderTable *table, bool fromEnd = false);
+
+ /**
+ * Initializes a new iterator
+ * @param start table section to start with.
+ */
+ TableSectionIterator(RenderTableSection *start) : sec(start) {}
+
+ /**
+ * Uninitialized iterator.
+ */
+ TableSectionIterator() {}
+
+ /** Returns the current section, or @p 0 if the end has been reached.
+ */
+ RenderTableSection *operator *() const { return sec; }
+
+ /** Moves to the next section in visual order. */
+ TableSectionIterator &operator ++();
+
+ /** Moves to the previous section in visual order. */
+ TableSectionIterator &operator --();
+
+private:
+ RenderTableSection *sec;
+};
+
+}
+#endif
+
diff --git a/khtml/rendering/render_text.cpp b/khtml/rendering/render_text.cpp
new file mode 100644
index 000000000..a862c3ad5
--- /dev/null
+++ b/khtml/rendering/render_text.cpp
@@ -0,0 +1,1546 @@
+/**
+ * This file is part of the DOM implementation for KDE.
+ *
+ * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
+ * (C) 2000-2003 Dirk Mueller (mueller@kde.org)
+ * (C) 2003 Apple Computer, Inc.
+ * (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com)
+ *
+ * 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.
+ *
+ */
+
+//#define DEBUG_LAYOUT
+//#define BIDI_DEBUG
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "rendering/render_text.h"
+#include "rendering/render_canvas.h"
+#include "rendering/break_lines.h"
+#include "rendering/render_arena.h"
+#include "xml/dom_nodeimpl.h"
+
+#include "misc/loader.h"
+#include "misc/helper.h"
+
+#include <qbitmap.h>
+#include <qimage.h>
+#include <qpainter.h>
+#include <kdebug.h>
+#include <kglobal.h>
+#include <assert.h>
+#include <limits.h>
+#include <math.h>
+
+#ifdef HAVE_ALLOCA_H
+// explicitly included for systems that don't provide it in stdlib.h
+#include <alloca.h>
+#else
+#include <stdlib.h>
+#endif
+
+using namespace khtml;
+using namespace DOM;
+
+#ifndef NDEBUG
+static bool inInlineTextBoxDetach;
+#endif
+
+void InlineTextBox::detach(RenderArena* renderArena)
+{
+ if (m_parent)
+ m_parent->removeFromLine(this);
+
+#ifndef NDEBUG
+ inInlineTextBoxDetach = true;
+#endif
+ delete this;
+#ifndef NDEBUG
+ inInlineTextBoxDetach = false;
+#endif
+
+ // Recover the size left there for us by operator delete and free the memory.
+ renderArena->free(*(size_t *)this, this);
+}
+
+void* InlineTextBox::operator new(size_t sz, RenderArena* renderArena) throw()
+{
+ return renderArena->allocate(sz);
+}
+
+void InlineTextBox::operator delete(void* ptr, size_t sz)
+{
+ assert(inInlineTextBoxDetach);
+
+ // Stash size where detach can find it.
+ *(size_t *)ptr = sz;
+}
+
+void InlineTextBox::selectionStartEnd(int& sPos, int& ePos)
+{
+ int startPos, endPos;
+ if (object()->selectionState() == RenderObject::SelectionInside) {
+ startPos = 0;
+ endPos = renderText()->string()->l;
+ } else {
+ renderText()->selectionStartEnd(startPos, endPos);
+ if (object()->selectionState() == RenderObject::SelectionStart)
+ endPos = renderText()->string()->l;
+ else if (object()->selectionState() == RenderObject::SelectionEnd)
+ startPos = 0;
+ }
+
+ sPos = kMax(startPos - m_start, 0);
+ ePos = kMin(endPos - m_start, (int)m_len);
+}
+
+RenderObject::SelectionState InlineTextBox::selectionState()
+{
+ RenderObject::SelectionState state = object()->selectionState();
+ if (state == RenderObject::SelectionStart || state == RenderObject::SelectionEnd ||
+ state == RenderObject::SelectionBoth) {
+ int startPos, endPos;
+ renderText()->selectionStartEnd(startPos, endPos);
+
+ bool start = (state != RenderObject::SelectionEnd && startPos >= m_start && startPos < m_start + m_len);
+ bool end = (state != RenderObject::SelectionStart && endPos > m_start && endPos <= m_start + m_len);
+ if (start && end)
+ state = RenderObject::SelectionBoth;
+ else if (start)
+ state = RenderObject::SelectionStart;
+ else if (end)
+ state = RenderObject::SelectionEnd;
+ else if ((state == RenderObject::SelectionEnd || startPos < m_start) &&
+ (state == RenderObject::SelectionStart || endPos > m_start + m_len))
+ state = RenderObject::SelectionInside;
+ }
+ return state;
+}
+
+void InlineTextBox::paint(RenderObject::PaintInfo& i, int tx, int ty)
+{
+ if (object()->isBR() || object()->style()->visibility() != VISIBLE ||
+ m_truncation == cFullTruncation || i.phase == PaintActionOutline)
+ return;
+
+ if (i.phase == PaintActionSelection && object()->selectionState() == RenderObject::SelectionNone)
+ // When only painting the selection, don't bother to paint if there is none.
+ return;
+
+ int xPos = tx + m_x;
+ int w = width();
+ if ((xPos >= i.r.x() + i.r.width()) || (xPos + w <= i.r.x()))
+ return;
+
+ // Set our font.
+ RenderStyle* styleToUse = object()->style(m_firstLine);
+ int d = styleToUse->textDecorationsInEffect();
+ if (styleToUse->font() != i.p->font())
+ i.p->setFont(styleToUse->font());
+ const Font *font = &styleToUse->htmlFont();
+ bool haveSelection = selectionState() != RenderObject::SelectionNone;
+
+ // Now calculate startPos and endPos, for painting selection.
+ // We paint selection while endPos > 0
+ int ePos = 0, sPos = 0;
+ if (haveSelection && !object()->canvas()->staticMode()) {
+ selectionStartEnd(sPos, ePos);
+ }
+ if (styleToUse->color() != i.p->pen().color())
+ i.p->setPen(styleToUse->color());
+
+ if (m_len > 0 && i.phase != PaintActionSelection) {
+ int endPoint = m_len;
+ if (m_truncation != cNoTruncation)
+ endPoint = m_truncation - m_start;
+ if (styleToUse->textShadow())
+ paintShadow(i.p, font, tx, ty, styleToUse->textShadow());
+ if (!haveSelection || sPos != 0 || ePos != m_len) {
+ font->drawText(i.p, m_x + tx, m_y + ty + m_baseline, renderText()->string()->s, renderText()->string()->l, m_start, endPoint,
+ m_toAdd, m_reversed ? QPainter::RTL : QPainter::LTR);
+ }
+ }
+
+ if (d != TDNONE && i.phase != PaintActionSelection && styleToUse->htmlHacks()) {
+ i.p->setPen(styleToUse->color());
+ paintDecoration(i.p, font, tx, ty, d);
+ }
+
+ if (haveSelection && i.phase == PaintActionSelection) {
+ //kdDebug(6040) << this << " paintSelection with startPos=" << sPos << " endPos=" << ePos << endl;
+ if ( sPos < ePos )
+ paintSelection(font, renderText(), i.p, styleToUse, tx, ty, sPos, ePos, d);
+ }
+}
+
+/** returns the proper ::selection pseudo style for the given element
+ * @return the style or 0 if no ::selection pseudo applies.
+ */
+inline const RenderStyle *retrieveSelectionPseudoStyle(const RenderObject *obj)
+{
+ // http://www.w3.org/Style/CSS/Test/CSS3/Selectors/20021129/html/tests/css3-modsel-162.html
+ // is of the opinion that ::selection of parent elements is also to be applied
+ // to children, so let's do it.
+ while (obj) {
+ const RenderStyle *style = obj->style()->getPseudoStyle(RenderStyle::SELECTION);
+ if (style) return style;
+
+ obj = obj->parent();
+ }/*wend*/
+ return 0;
+}
+
+void InlineTextBox::paintSelection(const Font *f, RenderText *text, QPainter *p, RenderStyle* style, int tx, int ty, int startPos, int endPos, int deco)
+{
+ if(startPos > m_len) return;
+ if(startPos < 0) startPos = 0;
+
+ QColor hc;
+ QColor hbg;
+ const RenderStyle* pseudoStyle = retrieveSelectionPseudoStyle(text);
+ if (pseudoStyle) {
+ // ### support outline (mandated by CSS3)
+ // ### support background-image? (optional by CSS3)
+ if (pseudoStyle->backgroundColor().isValid())
+ hbg = pseudoStyle->backgroundColor();
+ hc = pseudoStyle->color();
+ } else {
+ const QColorGroup &grp = style->palette().active();
+ hc = grp.highlightedText();
+ hbg = grp.highlight();
+ // ### should be at most retrieved once per render text
+ QColor bg = khtml::retrieveBackgroundColor(text);
+ // It may happen that the contrast is -- well -- virtually non existent.
+ // In this case, simply swap the colors, thus in compliance with
+ // NN4 (win32 only), IE, and Mozilla.
+ if (!khtml::hasSufficientContrast(hbg, bg))
+ qSwap(hc, hbg);
+ }
+
+ p->setPen(hc);
+
+ //kdDebug( 6040 ) << "textRun::painting(" << QConstString(text->str->s + m_start, m_len).string().left(30) << ") at(" << m_x+tx << "/" << m_y+ty << ")" << endl;
+
+ const bool needClipping = startPos != 0 || endPos != m_len;
+
+ if (needClipping) {
+ p->save();
+
+ int visualSelectionStart = f->width(text->str->s, text->str->l, m_start, startPos, m_start, m_start + m_len, m_toAdd);
+ int visualSelectionEnd = f->width(text->str->s, text->str->l, m_start, endPos, m_start, m_start + m_len, m_toAdd);
+ int visualSelectionWidth = visualSelectionEnd - visualSelectionStart;
+ if (m_reversed) {
+ visualSelectionStart = f->width(text->str->s, text->str->l, m_start, m_len) - visualSelectionEnd;
+ }
+
+ QRect selectionRect(m_x + tx + visualSelectionStart, m_y + ty, visualSelectionWidth, height());
+ QRegion r(selectionRect);
+ if (p->hasClipping())
+ r &= p->clipRegion(QPainter::CoordPainter);
+ p->setClipRegion(r, QPainter::CoordPainter);
+ }
+
+ f->drawText(p, m_x + tx, m_y + ty + m_baseline, text->str->s, text->str->l,
+ m_start, m_len, m_toAdd,
+ m_reversed ? QPainter::RTL : QPainter::LTR,
+ needClipping ? 0 : startPos, needClipping ? m_len : endPos,
+ hbg, m_y + ty, height(), deco);
+
+ if (needClipping) p->restore();
+}
+
+void InlineTextBox::paintDecoration( QPainter *pt, const Font *f, int _tx, int _ty, int deco)
+{
+ _tx += m_x;
+ _ty += m_y;
+
+ if (m_truncation == cFullTruncation)
+ return;
+
+ int width = m_width - 1;
+ if (m_truncation != cNoTruncation) {
+ width = static_cast<RenderText*>(m_object)->width(m_start, m_truncation - m_start, m_firstLine);
+ }
+
+ RenderObject *p = object();
+
+ QColor underline, overline, linethrough;
+ p->getTextDecorationColors(deco, underline, overline, linethrough, p->style()->htmlHacks());
+
+ if(deco & UNDERLINE){
+ pt->setPen(underline);
+ f->drawDecoration(pt, _tx, _ty, baseline(), width, height(), Font::UNDERLINE);
+ }
+ if (deco & OVERLINE) {
+ pt->setPen(overline);
+ f->drawDecoration(pt, _tx, _ty, baseline(), width, height(), Font::OVERLINE);
+ }
+ if(deco & LINE_THROUGH) {
+ pt->setPen(linethrough);
+ f->drawDecoration(pt, _tx, _ty, baseline(), width, height(), Font::LINE_THROUGH);
+ }
+ // NO! Do NOT add BLINK! It is the most annouing feature of Netscape, and IE has a reason not to
+ // support it. Lars
+}
+
+void InlineTextBox::paintShadow(QPainter *pt, const Font *f, int _tx, int _ty, const ShadowData *shadow )
+{
+ int x = m_x + _tx + shadow->x;
+ int y = m_y + _ty + shadow->y;
+ const RenderText* text = renderText();
+
+ if (shadow->blur <= 0) {
+ QColor c = pt->pen().color();
+ pt->setPen(shadow->color);
+ f->drawText(pt, x, y+m_baseline, text->str->s, text->str->l,
+ m_start, m_len, m_toAdd,
+ m_reversed ? QPainter::RTL : QPainter::LTR);
+ pt->setPen(c);
+
+ }
+ else {
+ const int thickness = shadow->blur;
+ const int w = m_width+2*thickness;
+ const int h = m_height+2*thickness;
+ const QRgb color = shadow->color.rgb();
+ const int gray = qGray(color);
+ const bool inverse = (gray < 100);
+ const QRgb bgColor = (inverse) ? qRgb(255,255,255) : qRgb(0,0,0);
+ QPixmap pixmap(w, h);
+ pixmap.fill(bgColor);
+ QPainter p;
+
+ p.begin(&pixmap);
+ p.setPen(shadow->color);
+ p.setFont(pt->font());
+ f->drawText(&p, thickness, thickness+m_baseline, text->str->s, text->str->l,
+ m_start, m_len, m_toAdd,
+ m_reversed ? QPainter::RTL : QPainter::LTR);
+
+ p.end();
+ QImage img = pixmap.convertToImage().convertDepth(32);
+
+ int md = thickness*thickness; // max-dist^2
+
+ // blur map (division cache)
+ float *bmap = (float*)alloca(sizeof(float)*(md+1));
+ for(int n=0; n<=md; n++) {
+ float f;
+ f = n/(float)(md+1);
+ f = 1.0 - f*f;
+ bmap[n] = f;
+ }
+
+ float factor = 0.0; // maximal potential opacity-sum
+ for(int n=-thickness; n<=thickness; n++)
+ for(int m=-thickness; m<=thickness; m++) {
+ int d = n*n+m*m;
+ if (d<=md)
+ factor += bmap[d];
+ }
+
+ // arbitratry factor adjustment to make shadows solid.
+ factor = factor/1.333;
+
+ // alpha map
+ float* amap = (float*)alloca(sizeof(float)*(h*w));
+ memset(amap, 0, h*w*(sizeof(float)));
+ for(int j=thickness; j<h-thickness; j++) {
+ for(int i=thickness; i<w-thickness; i++) {
+ QRgb col= img.pixel(i,j);
+ if (col == bgColor) continue;
+ float g = qGray(col);
+ if (inverse)
+ g = (255-g)/(255-gray);
+ else
+ g = g/gray;
+ for(int n=-thickness; n<=thickness; n++) {
+ for(int m=-thickness; m<=thickness; m++) {
+ int d = n*n+m*m;
+ if (d>md) continue;
+ float f = bmap[d];
+ amap[(i+m)+(j+n)*w] += (g*f);
+ }
+ }
+ }
+ }
+
+ QImage res(w,h,32);
+ res.setAlphaBuffer(true);
+ int r = qRed(color);
+ int g = qGreen(color);
+ int b = qBlue(color);
+
+ // divide by factor
+ factor = 1.0/factor;
+
+ for(int j=0; j<h; j++) {
+ for(int i=0; i<w; i++) {
+ int a = (int)(amap[i+j*w] * factor * 255.0);
+ if (a > 255) a = 255;
+ res.setPixel(i,j, qRgba(r,g,b,a));
+ }
+ }
+
+ pt->drawImage(x-thickness, y-thickness, res, 0, 0, -1, -1, Qt::DiffuseAlphaDither | Qt::ColorOnly | Qt::PreferDither);
+ }
+ // Paint next shadow effect
+ if (shadow->next) paintShadow(pt, f, _tx, _ty, shadow->next);
+}
+
+/**
+ * Distributes pixels to justify text.
+ * @param numSpaces spaces left, will be decremented by one
+ * @param toAdd number of pixels left to be distributed, will have the
+ * amount of pixels distributed during this call subtracted.
+ * @return number of pixels to distribute
+ */
+static inline int justifyWidth(int &numSpaces, int &toAdd) {
+ int a = 0;
+ if ( numSpaces ) {
+ a = toAdd/numSpaces;
+ toAdd -= a;
+ numSpaces--;
+ }/*end if*/
+ return a;
+}
+
+FindSelectionResult InlineTextBox::checkSelectionPoint(int _x, int _y, int _tx, int _ty, const Font *f, RenderText *text, int & offset, short lineHeight)
+{
+// kdDebug(6040) << "InlineTextBox::checkSelectionPoint " << this << " _x=" << _x << " _y=" << _y
+// << " _tx+m_x=" << _tx+m_x << " _ty+m_y=" << _ty+m_y << endl;
+ offset = 0;
+
+ if ( _y < _ty + m_y )
+ return SelectionPointBefore; // above -> before
+
+ if ( _y > _ty + m_y + lineHeight ) {
+ // below -> after
+ // Set the offset to the max
+ offset = m_len;
+ return SelectionPointAfter;
+ }
+ if ( _x > _tx + m_x + m_width ) {
+ // to the right
+ return SelectionPointAfterInLine;
+ }
+
+ // The Y matches, check if we're on the left
+ if ( _x < _tx + m_x ) {
+ return SelectionPointBeforeInLine;
+ }
+
+ // consider spacing for justified text
+ int toAdd = m_toAdd;
+ bool justified = text->style()->textAlign() == JUSTIFY && toAdd > 0;
+ int numSpaces = 0;
+ if (justified) {
+
+ for( int i = 0; i < m_len; i++ )
+ if ( text->str->s[m_start+i].category() == QChar::Separator_Space )
+ numSpaces++;
+
+ }/*end if*/
+
+ int delta = _x - (_tx + m_x);
+ //kdDebug(6040) << "InlineTextBox::checkSelectionPoint delta=" << delta << endl;
+ int pos = 0;
+ if ( m_reversed ) {
+ delta -= m_width;
+ while(pos < m_len) {
+ int w = f->width( text->str->s, text->str->l, m_start + pos);
+ if (justified && text->str->s[m_start + pos].category() == QChar::Separator_Space)
+ w += justifyWidth(numSpaces, toAdd);
+ int w2 = w/2;
+ w -= w2;
+ delta += w2;
+ if(delta >= 0) break;
+ pos++;
+ delta += w;
+ }
+ } else {
+ while(pos < m_len) {
+ int w = f->width( text->str->s, text->str->l, m_start + pos);
+ if (justified && text->str->s[m_start + pos].category() == QChar::Separator_Space)
+ w += justifyWidth(numSpaces, toAdd);
+ int w2 = w/2;
+ w -= w2;
+ delta -= w2;
+ if(delta <= 0) break;
+ pos++;
+ delta -= w;
+ }
+ }
+// kdDebug( 6040 ) << " Text --> inside at position " << pos << endl;
+ offset = pos;
+ return SelectionPointInside;
+}
+
+int InlineTextBox::offsetForPoint(int _x, int &ax) const
+{
+ // Do binary search for finding out offset, saves some time for long
+ // runs.
+ int start = 0;
+ int end = m_len;
+ ax = m_x;
+ int offset = (start + end) / 2;
+ while (end - start > 0) {
+ // always snap to the right column. This makes up for "jumpy" vertical
+ // navigation.
+ if (end - start == 1) start = end;
+
+ offset = (start + end) / 2;
+ ax = m_x + widthFromStart(offset);
+ if (ax > _x) end = offset;
+ else if (ax < _x) start = offset;
+ else break;
+ }
+ return m_start + offset;
+}
+
+int InlineTextBox::widthFromStart(int pos) const
+{
+ // gasp! sometimes pos is i < 0 which crashes Font::width
+ pos = kMax(pos, 0);
+
+ const RenderText *t = renderText();
+ Q_ASSERT(t->isText());
+ const Font *f = t->htmlFont(m_firstLine);
+ const QFontMetrics &fm = t->fontMetrics(m_firstLine);
+
+ int numSpaces = 0;
+ // consider spacing for justified text
+ bool justified = t->style()->textAlign() == JUSTIFY;
+ //kdDebug(6000) << "InlineTextBox::width(int)" << endl;
+ if (justified && m_toAdd > 0) do {
+ //kdDebug(6000) << "justify" << endl;
+
+// QConstString cstr = QConstString(t->str->s + m_start, m_len);
+ for( int i = 0; i < m_len; i++ )
+ if ( t->str->s[m_start+i].category() == QChar::Separator_Space )
+ numSpaces++;
+ if (numSpaces == 0) break;
+
+ int toAdd = m_toAdd;
+ int w = 0; // accumulated width
+ int start = 0; // start of non-space sequence
+ int current = 0; // current position
+ while (current < pos) {
+ // add spacing
+ while (current < pos && t->str->s[m_start + current].category() == QChar::Separator_Space) {
+ w += f->getWordSpacing();
+ w += f->getLetterSpacing();
+ w += justifyWidth(numSpaces, toAdd);
+ w += fm.width(' '); // ### valid assumption? (LS)
+ current++; start++;
+ }/*wend*/
+ if (current >= pos) break;
+
+ // seek next space
+ while (current < pos && t->str->s[m_start + current].category() != QChar::Separator_Space)
+ current++;
+
+ // check run without spaces
+ if ( current > start ) {
+ w += f->width(t->str->s + m_start, m_len, start, current - start);
+ start = current;
+ }
+ }
+
+ return w;
+
+ } while(false);/*end if*/
+
+ //kdDebug(6000) << "default" << endl;
+ // else use existing width function
+ return f->width(t->str->s + m_start, m_len, 0, pos);
+
+}
+
+long InlineTextBox::minOffset() const
+{
+ return m_start;
+}
+
+long InlineTextBox::maxOffset() const
+{
+ return m_start + m_len;
+}
+
+int InlineTextBox::placeEllipsisBox(bool ltr, int blockEdge, int ellipsisWidth, bool& foundBox)
+{
+ if (foundBox) {
+ m_truncation = cFullTruncation;
+ return -1;
+ }
+
+ int ellipsisX = ltr ? blockEdge - ellipsisWidth : blockEdge + ellipsisWidth;
+
+ // For LTR, if the left edge of the ellipsis is to the left of our text run, then we are the run that will get truncated.
+ if (ltr) {
+ if (ellipsisX <= m_x) {
+ // Too far. Just set full truncation, but return -1 and let the ellipsis just be placed at the edge of the box.
+ m_truncation = cFullTruncation;
+ foundBox = true;
+ return -1;
+ }
+
+ if (ellipsisX < m_x + m_width) {
+ if (m_reversed)
+ return -1; // FIXME: Support LTR truncation when the last run is RTL someday.
+
+ foundBox = true;
+
+ int ax;
+ int offset = offsetForPoint(ellipsisX, ax) - 1;
+ if (offset <= m_start) {
+ // No characters should be rendered. Set ourselves to full truncation and place the ellipsis at the min of our start
+ // and the ellipsis edge.
+ m_truncation = cFullTruncation;
+ return kMin(ellipsisX, (int)m_x);
+ }
+
+ // Set the truncation index on the text run. The ellipsis needs to be placed just after the last visible character.
+ m_truncation = offset;
+ return widthFromStart(offset - m_start);
+ }
+ }
+ else {
+ // FIXME: Support RTL truncation someday, including both modes (when the leftmost run on the line is either RTL or LTR)
+ }
+ return -1;
+}
+
+// -----------------------------------------------------------------------------
+
+InlineTextBoxArray::InlineTextBoxArray()
+{
+ setAutoDelete(false);
+}
+
+int InlineTextBoxArray::compareItems( Item d1, Item d2 )
+{
+ assert(d1);
+ assert(d2);
+
+ return static_cast<InlineTextBox*>(d1)->m_y - static_cast<InlineTextBox*>(d2)->m_y;
+}
+
+// remove this once QVector::bsearch is fixed
+int InlineTextBoxArray::findFirstMatching(Item d) const
+{
+ int len = count();
+
+ if ( !len )
+ return -1;
+ if ( !d )
+ return -1;
+ int n1 = 0;
+ int n2 = len - 1;
+ int mid = 0;
+ bool found = false;
+ while ( n1 <= n2 ) {
+ int res;
+ mid = (n1 + n2)/2;
+ if ( (*this)[mid] == 0 ) // null item greater
+ res = -1;
+ else
+ res = ((QGVector*)this)->compareItems( d, (*this)[mid] );
+ if ( res < 0 )
+ n2 = mid - 1;
+ else if ( res > 0 )
+ n1 = mid + 1;
+ else { // found it
+ found = true;
+ break;
+ }
+ }
+ /* if ( !found )
+ return -1; */
+ // search to first one equal or bigger
+ while ( found && (mid > 0) && !((QGVector*)this)->compareItems(d, (*this)[mid-1]) )
+ mid--;
+ return mid;
+}
+
+// -------------------------------------------------------------------------------------
+
+RenderText::RenderText(DOM::NodeImpl* node, DOMStringImpl *_str)
+ : RenderObject(node)
+{
+ // init RenderObject attributes
+ setRenderText(); // our object inherits from RenderText
+
+ m_minWidth = -1;
+ m_maxWidth = -1;
+ str = _str;
+ if(str) str->ref();
+ KHTMLAssert(!str || !str->l || str->s);
+
+ m_selectionState = SelectionNone;
+ m_hasReturn = true;
+
+#ifdef DEBUG_LAYOUT
+ QConstString cstr(str->s, str->l);
+ kdDebug( 6040 ) << "RenderText ctr( "<< cstr.string().length() << " ) '" << cstr.string() << "'" << endl;
+#endif
+}
+
+void RenderText::setStyle(RenderStyle *_style)
+{
+ if ( style() != _style ) {
+ bool changedText = ((!style() && ( _style->textTransform() != TTNONE ||
+ !_style->preserveLF() || !_style->preserveWS() )) ||
+ (style() && (style()->textTransform() != _style->textTransform() ||
+ style()->whiteSpace() != _style->whiteSpace())));
+
+ RenderObject::setStyle( _style );
+ m_lineHeight = RenderObject::lineHeight(false);
+
+ if (!isBR() && changedText) {
+ DOM::DOMStringImpl* textToTransform = originalString();
+ if (textToTransform)
+ setText(textToTransform, true);
+ }
+ }
+}
+
+RenderText::~RenderText()
+{
+ KHTMLAssert(m_lines.count() == 0);
+ if(str) str->deref();
+}
+
+void RenderText::deleteInlineBoxes(RenderArena* arena)
+{
+ // this is a slight variant of QArray::clear().
+ // We don't delete the array itself here because its
+ // likely to be used in the same size later again, saves
+ // us resize() calls
+ unsigned int len = m_lines.size();
+ if (len) {
+ if (!arena)
+ arena = renderArena();
+ for(unsigned int i=0; i < len; i++) {
+ InlineTextBox* s = m_lines.at(i);
+ if (s)
+ s->detach(arena);
+ m_lines.remove(i);
+ }
+ }
+
+ KHTMLAssert(m_lines.count() == 0);
+}
+
+bool RenderText::isTextFragment() const
+{
+ return false;
+}
+
+DOM::DOMStringImpl* RenderText::originalString() const
+{
+ return element() ? element()->string() : 0;
+}
+
+InlineTextBox * RenderText::findInlineTextBox( int offset, int &pos, bool checkFirstLetter )
+{
+ // The text boxes point to parts of the rendertext's str string
+ // (they don't include '\n')
+ // Find the text box that includes the character at @p offset
+ // and return pos, which is the position of the char in the run.
+
+ // FIXME: make this use binary search? Dirk says it won't work :-( (LS)
+ (void)checkFirstLetter;
+#if 0
+ if (checkFirstLetter && forcedMinOffset()) {
+// kdDebug(6040) << "checkFirstLetter: forcedMinOffset: " << forcedMinOffset() << endl;
+ RenderFlow *firstLetter = static_cast<RenderFlow *>(previousSibling());
+ if (firstLetter && firstLetter->isFlow() && firstLetter->isFirstLetter()) {
+ RenderText *letterText = static_cast<RenderText *>(firstLetter->firstChild());
+ //kdDebug(6040) << "lettertext: " << letterText << " minOfs: " << letterText->minOffset() << " maxOfs: " << letterText->maxOffset() << endl;
+ if (offset >= letterText->minOffset() && offset <= letterText->maxOffset()) {
+ InlineTextBox *result = letterText->findInlineTextBox(offset, pos, false);
+ //kdDebug(6040) << "result: " << result << endl;
+ if (result) return result;
+ }
+ }
+ }
+#endif
+
+ if ( m_lines.isEmpty() )
+ return 0L;
+
+ // The text boxes don't resemble a contiguous coverage of the text, there
+ // may be holes. Therefore, we snap to the nearest previous text box if
+ // the given offset happens to point to such a hole.
+
+ InlineTextBox* s = m_lines[0];
+ uint count = m_lines.count();
+ uint si = 0;
+ uint nearest_idx = 0; // index of nearest text box
+ int nearest = INT_MAX; // nearest distance
+//kdDebug(6040) << "s[" << si << "] m_start " << s->m_start << " m_end " << (s->m_start + s->m_len) << endl;
+ while(!(offset >= s->m_start && offset <= s->m_start + s->m_len)
+ && ++si < count)
+ {
+ int dist = offset - (s->m_start + s->m_len);
+//kdDebug(6040) << "dist " << dist << " nearest " << nearest << endl;
+ if (dist >= 0 && dist <= nearest) {
+ nearest = dist;
+ nearest_idx = si - 1;
+ }/*end if*/
+ s = m_lines[si];
+//kdDebug(6040) << "s[" << si << "] m_start " << s->m_start << " m_end " << (s->m_start + s->m_len) << endl;
+ }
+//kdDebug(6040) << "nearest_idx " << nearest_idx << " count " << count << endl;
+ if (si >= count) s = m_lines[nearest_idx];
+ // we are now in the correct text box
+ pos = kMin(offset - s->m_start, int( s->m_len ));
+ //kdDebug(6040) << "offset=" << offset << " s->m_start=" << s->m_start << endl;
+ return s;
+}
+
+bool RenderText::nodeAtPoint(NodeInfo& info, int _x, int _y, int _tx, int _ty, HitTestAction /*hitTestAction*/, bool /*inBox*/)
+{
+ assert(parent());
+
+ bool inside = false;
+ if (style()->visibility() != HIDDEN) {
+ InlineTextBox *s = m_lines.count() ? m_lines[0] : 0;
+ int si = 0;
+ while(s) {
+ if((_y >=_ty + s->m_y) && (_y < _ty + s->m_y + s->m_height) &&
+ (_x >= _tx + s->m_x) && (_x <_tx + s->m_x + s->m_width) ) {
+ inside = true;
+ break;
+ }
+
+ s = si < (int) m_lines.count()-1 ? m_lines[++si] : 0;
+ }
+ }
+
+ // #### ported over from Safari. Can this happen at all? (lars)
+
+ if (inside && element()) {
+ if (info.innerNode() && info.innerNode()->renderer() &&
+ !info.innerNode()->renderer()->isInline()) {
+ // Within the same layer, inlines are ALWAYS fully above blocks. Change inner node.
+ info.setInnerNode(element());
+
+ // Clear everything else.
+ info.setInnerNonSharedNode(0);
+ info.setURLElement(0);
+ }
+
+ if (!info.innerNode())
+ info.setInnerNode(element());
+
+ if(!info.innerNonSharedNode())
+ info.setInnerNonSharedNode(element());
+ }
+
+ return inside;
+}
+
+FindSelectionResult RenderText::checkSelectionPoint(int _x, int _y, int _tx, int _ty, DOM::NodeImpl*& node, int &offset, SelPointState &)
+{
+// kdDebug(6040) << "RenderText::checkSelectionPoint " << this << " _x=" << _x << " _y=" << _y
+// << " _tx=" << _tx << " _ty=" << _ty << endl;
+//kdDebug(6040) << renderName() << "::checkSelectionPoint x=" << xPos() << " y=" << yPos() << " w=" << width() << " h=" << height() << " m_lines.count=" << m_lines.count() << endl;
+
+ NodeImpl *lastNode = 0;
+ int lastOffset = 0;
+ FindSelectionResult lastResult = SelectionPointAfter;
+
+ for(unsigned int si = 0; si < m_lines.count(); si++)
+ {
+ InlineTextBox* s = m_lines[si];
+ FindSelectionResult result;
+ const Font *f = htmlFont( si==0 );
+ result = s->checkSelectionPoint(_x, _y, _tx, _ty, f, this, offset, m_lineHeight);
+
+// kdDebug(6040) << "RenderText::checkSelectionPoint " << this << " line " << si << " result=" << result << " offset=" << offset << endl;
+ if ( result == SelectionPointInside ) // x,y is inside the textrun
+ {
+ offset += s->m_start; // add the offset from the previous lines
+// kdDebug(6040) << "RenderText::checkSelectionPoint inside -> " << offset << endl;
+ node = element();
+ return SelectionPointInside;
+ } else if ( result == SelectionPointBefore ) {
+ if (!lastNode) {
+ // x,y is before the textrun -> stop here
+ offset = 0;
+// kdDebug(6040) << "RenderText::checkSelectionPoint " << this << "before us -> returning Before" << endl;
+ node = element();
+ return SelectionPointBefore;
+ }
+ } else if ( result == SelectionPointBeforeInLine ) {
+ offset = s->m_start;
+ node = element();
+ return SelectionPointInside;
+ } else if ( result == SelectionPointAfterInLine ) {
+ lastOffset = s->m_start + s->m_len;
+ lastNode = element();
+ lastResult = result;
+ // no return here
+ }
+
+ }
+
+ if (lastNode) {
+ offset = lastOffset;
+ node = lastNode;
+// kdDebug(6040) << "RenderText::checkSelectionPoint: lastNode " << lastNode << " lastOffset " << lastOffset << endl;
+ return lastResult;
+ }
+
+ // set offset to max
+ offset = str->l;
+ //qDebug("setting node to %p", element());
+ node = element();
+// kdDebug(6040) << "RenderText::checkSelectionPoint: node " << node << " offset " << offset << endl;
+ return SelectionPointAfter;
+}
+
+void RenderText::caretPos(int offset, int flags, int &_x, int &_y, int &width, int &height)
+{
+ if (!m_lines.count()) {
+ _x = _y = height = -1;
+ width = 1;
+ return;
+ }
+
+ int pos;
+ InlineTextBox * s = findInlineTextBox( offset, pos, true );
+ RenderText *t = s->renderText();
+// kdDebug(6040) << "offset="<<offset << " pos="<<pos << endl;
+
+ const QFontMetrics &fm = t->metrics( s->m_firstLine );
+ height = fm.height(); // s->m_height;
+
+ _x = s->m_x + s->widthFromStart(pos);
+ _y = s->m_y + s->baseline() - fm.ascent();
+ width = 1;
+ if (flags & CFOverride) {
+ width = offset < maxOffset() ? fm.width(str->s[offset]) : 1;
+ }/*end if*/
+#if 0
+ kdDebug(6040) << "_x="<<_x << " s->m_x="<<s->m_x
+ << " s->m_start"<<s->m_start
+ << " s->m_len" << s->m_len << " _y=" << _y << endl;
+#endif
+
+ int absx, absy;
+
+ if (absolutePosition(absx,absy))
+ {
+ //kdDebug(6040) << "absx=" << absx << " absy=" << absy << endl;
+ _x += absx;
+ _y += absy;
+ } else {
+ // we don't know our absolute position, and there is no point returning
+ // just a relative one
+ _x = _y = -1;
+ }
+}
+
+long RenderText::minOffset() const
+{
+ if (!m_lines.count()) return 0;
+ // FIXME: it is *not* guaranteed that the first run contains the lowest offset
+ // Either make this a linear search (slow),
+ // or maintain an index (needs much mem),
+ // or calculate and store it in bidi.cpp (needs calculation even if not needed)
+ // (LS)
+ return m_lines[0]->m_start;
+}
+
+long RenderText::maxOffset() const
+{
+ int count = m_lines.count();
+ if (!count) return str->l;
+ // FIXME: it is *not* guaranteed that the last run contains the highest offset
+ // Either make this a linear search (slow),
+ // or maintain an index (needs much mem),
+ // or calculate and store it in bidi.cpp (needs calculation even if not needed)
+ // (LS)
+ return m_lines[count - 1]->m_start + m_lines[count - 1]->m_len;
+}
+
+bool RenderText::absolutePosition(int &xPos, int &yPos, bool) const
+{
+ return RenderObject::absolutePosition(xPos, yPos, false);
+}
+
+bool RenderText::posOfChar(int chr, int &x, int &y)
+{
+ if (!parent())
+ return false;
+ parent()->absolutePosition( x, y, false );
+
+ int pos;
+ InlineTextBox * s = findInlineTextBox( chr, pos );
+
+ if ( s ) {
+ // s is the line containing the character
+ x += s->m_x; // this is the x of the beginning of the line, but it's good enough for now
+ y += s->m_y;
+ return true;
+ }
+
+ return false;
+}
+
+void RenderText::paint( PaintInfo& /*pI*/, int /*tx*/, int /*ty*/)
+{
+ KHTMLAssert( false );
+}
+
+void RenderText::calcMinMaxWidth()
+{
+ KHTMLAssert( !minMaxKnown() );
+
+ // ### calc Min and Max width...
+ m_minWidth = m_beginMinWidth = m_endMinWidth = 0;
+ m_maxWidth = 0;
+
+ if (isBR())
+ return;
+
+ int currMinWidth = 0;
+ int currMaxWidth = 0;
+ m_hasBreakableChar = m_hasBreak = m_hasBeginWS = m_hasEndWS = false;
+
+ // ### not 100% correct for first-line
+ const Font *f = htmlFont( false );
+ int wordSpacing = style()->wordSpacing();
+ int len = str->l;
+ bool isSpace = false;
+ bool firstWord = true;
+ bool firstLine = true;
+ for(int i = 0; i < len; i++)
+ {
+ unsigned short c = str->s[i].unicode();
+ bool isNewline = false;
+
+ // If line-breaks survive to here they are preserved
+ if ( c == '\n' ) {
+ m_hasBreak = true;
+ isNewline = true;
+ isSpace = false;
+ } else
+ isSpace = c == ' ';
+
+ if ((isSpace || isNewline) && i == 0)
+ m_hasBeginWS = true;
+ if ((isSpace || isNewline) && i == len-1)
+ m_hasEndWS = true;
+
+ if (i && c == SOFT_HYPHEN)
+ continue;
+
+ int wordlen = 0;
+ while( i+wordlen < len && (i+wordlen == 0 || str->s[i+wordlen].unicode() != SOFT_HYPHEN) &&
+ !(isBreakable( str->s, i+wordlen, str->l )) )
+ wordlen++;
+
+ if (wordlen)
+ {
+#ifndef APPLE_CHANGES
+ int w = f->width(str->s, str->l, i, wordlen);
+#else
+ int w = widthFromCache(f, i, wordlen);
+#endif
+ currMinWidth += w;
+ currMaxWidth += w;
+
+ // Add in wordspacing to our maxwidth, but not if this is the last word.
+ if (wordSpacing && !containsOnlyWhitespace(i+wordlen, len-(i+wordlen)))
+ currMaxWidth += wordSpacing;
+
+ if (firstWord) {
+ firstWord = false;
+ m_beginMinWidth = w;
+ }
+ m_endMinWidth = w;
+
+ if(currMinWidth > m_minWidth) m_minWidth = currMinWidth;
+ currMinWidth = 0;
+
+ i += wordlen-1;
+ }
+ else {
+ // Nowrap can never be broken, so don't bother setting the
+ // breakable character boolean. Pre can only be broken if we encounter a newline.
+ if (style()->autoWrap() || isNewline)
+ m_hasBreakableChar = true;
+
+ if(currMinWidth > m_minWidth) m_minWidth = currMinWidth;
+ currMinWidth = 0;
+
+ if (isNewline) // Only set if isPre was true and we saw a newline.
+ {
+ if ( firstLine ) {
+ firstLine = false;
+ m_beginMinWidth = currMaxWidth;
+ }
+
+ if(currMaxWidth > m_maxWidth) m_maxWidth = currMaxWidth;
+ currMaxWidth = 0;
+ }
+ else
+ {
+ currMaxWidth += f->width( str->s, str->l, i + wordlen );
+ }
+ }
+ }
+
+ if(currMinWidth > m_minWidth) m_minWidth = currMinWidth;
+ if(currMaxWidth > m_maxWidth) m_maxWidth = currMaxWidth;
+
+ if (!style()->autoWrap()) {
+ m_minWidth = m_maxWidth;
+ if (style()->preserveLF()) {
+ if (firstLine)
+ m_beginMinWidth = m_maxWidth;
+ m_endMinWidth = currMaxWidth;
+ }
+ }
+
+ setMinMaxKnown();
+ //kdDebug( 6040 ) << "Text::calcMinMaxWidth(): min = " << m_minWidth << " max = " << m_maxWidth << endl;
+
+}
+
+int RenderText::minXPos() const
+{
+ if (!m_lines.count())
+ return 0;
+ int retval=6666666;
+ for (unsigned i=0;i < m_lines.count(); i++)
+ {
+ retval = kMin ( retval, int( m_lines[i]->m_x ));
+ }
+ return retval;
+}
+
+int RenderText::inlineXPos() const
+{
+ return minXPos();
+}
+
+int RenderText::inlineYPos() const
+{
+ return m_lines.isEmpty() ? 0 : m_lines[0]->yPos();
+}
+
+const QFont &RenderText::font()
+{
+ return style()->font();
+}
+
+void RenderText::setText(DOMStringImpl *text, bool force)
+{
+ if( !force && str == text ) return;
+
+ DOMStringImpl *oldstr = str;
+ if(text && style())
+ str = text->collapseWhiteSpace(style()->preserveLF(), style()->preserveWS());
+ else
+ str = text;
+ if(str) str->ref();
+ if(oldstr) oldstr->deref();
+
+ if ( str && style() ) {
+ oldstr = str;
+ switch(style()->textTransform()) {
+ case CAPITALIZE:
+ {
+ RenderObject *o;
+ bool runOnString = false;
+
+ // find previous non-empty text renderer if one exists
+ for (o = previousRenderer(); o; o = o->previousRenderer()) {
+ if (!o->isInlineFlow()) {
+ if (!o->isText())
+ break;
+
+ DOMStringImpl *prevStr = static_cast<RenderText*>(o)->string();
+ // !prevStr can happen with css like "content:open-quote;"
+ if (!prevStr)
+ break;
+
+ if (prevStr->length() == 0)
+ continue;
+ QChar c = (*prevStr)[prevStr->length() - 1];
+ if (!c.isSpace())
+ runOnString = true;
+
+ break;
+ }
+ }
+
+ str = str->capitalize(runOnString);
+ }
+ break;
+
+
+
+ case UPPERCASE: str = str->upper(); break;
+ case LOWERCASE: str = str->lower(); break;
+ case NONE:
+ default:;
+ }
+ str->ref();
+ oldstr->deref();
+ }
+
+ // ### what should happen if we change the text of a
+ // RenderBR object ?
+ KHTMLAssert(!isBR() || (str->l == 1 && (*str->s) == '\n'));
+ KHTMLAssert(!str->l || str->s);
+
+ setNeedsLayoutAndMinMaxRecalc();
+#ifdef BIDI_DEBUG
+ QConstString cstr(str->s, str->l);
+ kdDebug( 6040 ) << "RenderText::setText( " << cstr.string().length() << " ) '" << cstr.string() << "'" << endl;
+#endif
+}
+
+int RenderText::height() const
+{
+ int retval;
+ if ( m_lines.count() )
+ retval = m_lines[m_lines.count()-1]->m_y + m_lineHeight - m_lines[0]->m_y;
+ else
+ retval = metrics( false ).height();
+
+ return retval;
+}
+
+short RenderText::lineHeight( bool firstLine ) const
+{
+ if ( firstLine )
+ return RenderObject::lineHeight( firstLine );
+
+ return m_lineHeight;
+}
+
+short RenderText::baselinePosition( bool firstLine ) const
+{
+ const QFontMetrics &fm = metrics( firstLine );
+ return fm.ascent() +
+ ( lineHeight( firstLine ) - fm.height() ) / 2;
+}
+
+InlineBox* RenderText::createInlineBox(bool, bool isRootLineBox)
+{
+ KHTMLAssert( !isRootLineBox );
+ return new (renderArena()) InlineTextBox(this);
+}
+
+void RenderText::position(InlineBox* box, int from, int len, bool reverse)
+{
+//kdDebug(6040) << "position: from="<<from<<" len="<<len<<endl;
+ // ### should not be needed!!!
+ // asserts sometimes with pre (that unibw-hamburg testcase). ### find out why
+ //KHTMLAssert(!(len == 0 || (str->l && len == 1 && *(str->s+from) == '\n') ));
+ // It is now needed. BRs need text boxes too otherwise caret navigation
+ // gets stuck (LS)
+ // if (len == 0 || (str->l && len == 1 && *(str->s+from) == '\n') ) return;
+
+ reverse = reverse && !style()->visuallyOrdered();
+
+#ifdef DEBUG_LAYOUT
+ QChar *ch = str->s+from;
+ QConstString cstr(ch, len);
+#endif
+
+ KHTMLAssert(box->isInlineTextBox());
+ InlineTextBox *s = static_cast<InlineTextBox *>(box);
+ s->m_start = from;
+ s->m_len = len;
+ s->m_reversed = reverse;
+ //kdDebug(6040) << "m_start: " << s->m_start << " m_len: " << s->m_len << endl;
+
+ if(m_lines.count() == m_lines.size())
+ m_lines.resize(m_lines.size()*2+1);
+
+ m_lines.insert(m_lines.count(), s);
+ //kdDebug(6040) << this << " " << renderName() << "::position inserted" << endl;
+}
+
+unsigned int RenderText::width(unsigned int from, unsigned int len, bool firstLine) const
+{
+ if(!str->s || from > str->l ) return 0;
+ if ( from + len > str->l ) len = str->l - from;
+
+ const Font *f = htmlFont( firstLine );
+ return width( from, len, f );
+}
+
+unsigned int RenderText::width(unsigned int from, unsigned int len, const Font *f) const
+{
+ if(!str->s || from > str->l ) return 0;
+ if ( from + len > str->l ) len = str->l - from;
+
+ if ( f == &style()->htmlFont() && from == 0 && len == str->l )
+ return m_maxWidth;
+
+ int w = f->width(str->s, str->l, from, len );
+
+ //kdDebug( 6040 ) << "RenderText::width(" << from << ", " << len << ") = " << w << endl;
+ return w;
+}
+
+short RenderText::width() const
+{
+ int w;
+ int minx = 100000000;
+ int maxx = 0;
+ // slooow
+ for(unsigned int si = 0; si < m_lines.count(); si++) {
+ InlineTextBox* s = m_lines[si];
+ if(s->m_x < minx)
+ minx = s->m_x;
+ if(s->m_x + s->m_width > maxx)
+ maxx = s->m_x + s->m_width;
+ }
+
+ w = kMax(0, maxx-minx);
+
+ return w;
+}
+
+void RenderText::repaint(Priority p)
+{
+ RenderObject *cb = containingBlock();
+ if(cb)
+ cb->repaint(p);
+}
+
+bool RenderText::isFixedWidthFont() const
+{
+ return QFontInfo(style()->font()).fixedPitch();
+}
+
+short RenderText::verticalPositionHint( bool firstLine ) const
+{
+ return parent()->verticalPositionHint( firstLine );
+}
+
+const QFontMetrics &RenderText::metrics(bool firstLine) const
+{
+ if( firstLine && hasFirstLine() ) {
+ RenderStyle *pseudoStyle = style()->getPseudoStyle(RenderStyle::FIRST_LINE);
+ if ( pseudoStyle )
+ return pseudoStyle->fontMetrics();
+ }
+ return style()->fontMetrics();
+}
+
+const Font *RenderText::htmlFont(bool firstLine) const
+{
+ const Font *f = 0;
+ if( firstLine && hasFirstLine() ) {
+ RenderStyle *pseudoStyle = style()->getPseudoStyle(RenderStyle::FIRST_LINE);
+ if ( pseudoStyle )
+ f = &pseudoStyle->htmlFont();
+ } else {
+ f = &style()->htmlFont();
+ }
+ return f;
+}
+
+bool RenderText::containsOnlyWhitespace(unsigned int from, unsigned int len) const
+{
+ unsigned int currPos;
+ for (currPos = from;
+ currPos < from+len && (str->s[currPos] == '\n' || str->s[currPos].direction() == QChar::DirWS);
+ currPos++);
+ return currPos >= (from+len);
+}
+
+void RenderText::trimmedMinMaxWidth(short& beginMinW, bool& beginWS,
+ short& endMinW, bool& endWS,
+ bool& hasBreakableChar, bool& hasBreak,
+ short& beginMaxW, short& endMaxW,
+ short& minW, short& maxW, bool& stripFrontSpaces)
+{
+ bool preserveWS = style()->preserveWS();
+ bool preserveLF = style()->preserveLF();
+ bool autoWrap = style()->autoWrap();
+ if (preserveWS)
+ stripFrontSpaces = false;
+
+ int len = str->l;
+ if (len == 0 || (stripFrontSpaces && str->containsOnlyWhitespace())) {
+ maxW = 0;
+ hasBreak = false;
+ return;
+ }
+
+ minW = m_minWidth;
+ maxW = m_maxWidth;
+ beginWS = stripFrontSpaces ? false : m_hasBeginWS;
+ endWS = m_hasEndWS;
+
+ beginMinW = m_beginMinWidth;
+ endMinW = m_endMinWidth;
+
+ hasBreakableChar = m_hasBreakableChar;
+ hasBreak = m_hasBreak;
+
+ if (stripFrontSpaces && (str->s[0].direction() == QChar::DirWS || (!preserveLF && str->s[0] == '\n'))) {
+ const Font *f = htmlFont( false );
+ QChar space[1]; space[0] = ' ';
+ int spaceWidth = f->width(space, 1, 0);
+ maxW -= spaceWidth;
+ }
+
+ stripFrontSpaces = !preserveWS && m_hasEndWS;
+
+ if (!autoWrap)
+ minW = maxW;
+ else if (minW > maxW)
+ minW = maxW;
+
+ // Compute our max widths by scanning the string for newlines.
+ if (hasBreak) {
+ const Font *f = htmlFont( false );
+ bool firstLine = true;
+ beginMaxW = endMaxW = maxW;
+ for(int i = 0; i < len; i++)
+ {
+ int linelen = 0;
+ while( i+linelen < len && str->s[i+linelen] != '\n')
+ linelen++;
+
+ if (linelen)
+ {
+#ifndef APPLE_CHANGES
+ endMaxW = f->width(str->s, str->l, i, linelen);
+#else
+ endMaxW = widthFromCache(f, i, linelen);
+#endif
+ if (firstLine) {
+ firstLine = false;
+ beginMaxW = endMaxW;
+ }
+ i += linelen;
+ }
+ else if (firstLine) {
+ beginMaxW = 0;
+ firstLine = false;
+ }
+ if (i == len-1)
+ // A <pre> run that ends with a newline, as in, e.g.,
+ // <pre>Some text\n\n<span>More text</pre>
+ endMaxW = 0;
+ }
+ }
+}
+
+#ifdef ENABLE_DUMP
+
+static QString quoteAndEscapeNonPrintables(const QString &s)
+{
+ QString result;
+ result += '"';
+ for (uint i = 0; i != s.length(); ++i) {
+ QChar c = s.at(i);
+ if (c == '\\') {
+ result += "\\\\";
+ } else if (c == '"') {
+ result += "\\\"";
+ } else {
+ ushort u = c.unicode();
+ if (u >= 0x20 && u < 0x7F) {
+ result += c;
+ } else {
+ QString hex;
+ hex.sprintf("\\x{%X}", u);
+ result += hex;
+ }
+ }
+ }
+ result += '"';
+ return result;
+}
+
+static void writeTextRun(QTextStream &ts, const RenderText &o, const InlineTextBox &run)
+{
+ ts << "text run at (" << run.m_x << "," << run.m_y << ") width " << run.m_width << ": "
+ << quoteAndEscapeNonPrintables(o.data().string().mid(run.m_start, run.m_len));
+}
+
+void RenderText::dump(QTextStream &stream, const QString &ind) const
+{
+ RenderObject::dump( stream, ind );
+
+ for (unsigned int i = 0; i < m_lines.count(); i++) {
+ stream << endl << ind << " ";
+ writeTextRun(stream, *this, *m_lines[i]);
+ }
+}
+#endif
+
+RenderTextFragment::RenderTextFragment(DOM::NodeImpl* _node, DOM::DOMStringImpl* _str,
+ int startOffset, int endOffset)
+:RenderText(_node, _str->substring(startOffset, endOffset)),
+m_start(startOffset), m_end(endOffset), m_generatedContentStr(0)
+{}
+
+RenderTextFragment::RenderTextFragment(DOM::NodeImpl* _node, DOM::DOMStringImpl* _str)
+:RenderText(_node, _str), m_start(0)
+{
+ m_generatedContentStr = _str;
+ if (_str) {
+ _str->ref();
+ m_end = _str->l;
+ }
+ else
+ m_end = 0;
+}
+
+RenderTextFragment::~RenderTextFragment()
+{
+ if (m_generatedContentStr)
+ m_generatedContentStr->deref();
+}
+
+bool RenderTextFragment::isTextFragment() const
+{
+ return true;
+}
+
+DOM::DOMStringImpl* RenderTextFragment::originalString() const
+{
+ DOM::DOMStringImpl* result = 0;
+ if (element())
+ result = element()->string();
+ else
+ result = contentString();
+ if (result && (start() > 0 || start() < result->l))
+ result = result->substring(start(), end());
+ return result;
+}
+
+#undef BIDI_DEBUG
+#undef DEBUG_LAYOUT
diff --git a/khtml/rendering/render_text.h b/khtml/rendering/render_text.h
new file mode 100644
index 000000000..c65973c99
--- /dev/null
+++ b/khtml/rendering/render_text.h
@@ -0,0 +1,345 @@
+/*
+ * This file is part of the DOM implementation for KDE.
+ *
+ * (C) 1999-2003 Lars Knoll (knoll@kde.org)
+ * (C) 2000-2003 Dirk Mueller (mueller@kde.org)
+ * (C) 2003 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.
+ *
+ */
+#ifndef RENDERTEXT_H
+#define RENDERTEXT_H
+
+#include "dom/dom_string.h"
+#include "xml/dom_stringimpl.h"
+#include "xml/dom_textimpl.h"
+#include "rendering/render_object.h"
+#include "rendering/render_line.h"
+
+#include <qptrvector.h>
+#include <assert.h>
+
+class QPainter;
+class QFontMetrics;
+
+// Define a constant for soft hyphen's unicode value.
+#define SOFT_HYPHEN 173
+
+const int cNoTruncation = -1;
+const int cFullTruncation = -2;
+
+namespace khtml
+{
+ class RenderText;
+ class RenderStyle;
+
+class InlineTextBox : public InlineBox
+{
+public:
+ InlineTextBox(RenderObject *obj)
+ :InlineBox(obj),
+ // ### necessary as some codepaths (<br>) do *not* initialize these (LS)
+ m_start(0), m_len(0), m_truncation(cNoTruncation), m_reversed(false), m_toAdd(0)
+ {
+ }
+
+ void detach(RenderArena* renderArena);
+
+ virtual void clearTruncation() { m_truncation = cNoTruncation; }
+ virtual int placeEllipsisBox(bool ltr, int blockEdge, int ellipsisWidth, bool& foundBox);
+
+ // Overloaded new operator. Derived classes must override operator new
+ // in order to allocate out of the RenderArena.
+ void* operator new(size_t sz, RenderArena* renderArena) throw();
+
+ // Overridden to prevent the normal delete from being called.
+ void operator delete(void* ptr, size_t sz);
+
+private:
+ // The normal operator new is disallowed.
+ void* operator new(size_t sz) throw();
+
+public:
+ void setSpaceAdd(int add) { m_width -= m_toAdd; m_toAdd = add; m_width += m_toAdd; }
+ int spaceAdd() { return m_toAdd; }
+
+ virtual bool isInlineTextBox() const { return true; }
+
+ void paint(RenderObject::PaintInfo& i, int tx, int ty);
+ void paintDecoration(QPainter *pt, const Font *f, int _tx, int _ty, int decoration);
+ void paintShadow(QPainter *pt, const Font* f, int _tx, int _ty, const ShadowData *shadow );
+ void paintSelection(const Font *f, RenderText *text, QPainter *p, RenderStyle* style, int tx, int ty, int startPos, int endPos, int deco);
+
+ void selectionStartEnd(int& sPos, int& ePos);
+ RenderObject::SelectionState selectionState();
+
+ // Return before, after (offset set to max), or inside the text, at @p offset
+ FindSelectionResult checkSelectionPoint(int _x, int _y, int _tx, int _ty, const Font *f, RenderText *text, int & offset, short lineheight);
+
+ bool checkVerticalPoint(int _y, int _ty, int _h, int height)
+ { if((_ty + m_y > _y + _h) || (_ty + m_y + m_baseline + height < _y)) return false; return true; }
+
+ /**
+ * determines the offset into the DOMString of the character the given
+ * coordinate points to.
+ * The returned offset is never out of range.
+ * @param _x given coordinate (relative to containing block)
+ * @param ax returns exact coordinate the offset corresponds to
+ * (relative to containing block)
+ * @return the offset (relative to the RenderText object, not to this run)
+ */
+ int offsetForPoint(int _x, int &ax) const;
+
+ /**
+ * calculates the with of the specified chunk in this text box.
+ * @param pos zero-based position within the text box up to which
+ * the width is to be determined
+ * @return the width in pixels
+ */
+ int widthFromStart(int pos) const;
+
+ /** returns the lowest possible value the caret offset may have to
+ * still point to a valid position.
+ */
+ virtual long minOffset() const;
+ /** returns the highest possible value the caret offset may have to
+ * still point to a valid position.
+ */
+ virtual long maxOffset() const;
+
+ /** returns the associated render text
+ */
+ const RenderText *renderText() const;
+ RenderText *renderText();
+
+ int m_start;
+ unsigned short m_len;
+
+ int m_truncation; // Where to truncate when text overflow is applied.
+ // We use special constants to denote no truncation (the whole run paints)
+ // and full truncation (nothing paints at all).
+
+ bool m_reversed : 1;
+ unsigned m_toAdd : 14; // for justified text
+private:
+ // this is just for QVector::bsearch. Don't use it otherwise
+ InlineTextBox(int _x, int _y)
+ :InlineBox(0)
+ {
+ m_x = _x;
+ m_y = _y;
+ m_reversed = false;
+ };
+ friend class RenderText;
+};
+
+class InlineTextBoxArray : public QPtrVector<InlineTextBox>
+{
+public:
+ InlineTextBoxArray();
+
+ InlineTextBox* first();
+
+ int findFirstMatching( Item ) const;
+ virtual int compareItems( Item, Item );
+};
+
+class RenderText : public RenderObject
+{
+ friend class InlineTextBox;
+
+public:
+ RenderText(DOM::NodeImpl* node, DOM::DOMStringImpl *_str);
+ virtual ~RenderText();
+
+ virtual bool isTextFragment() const;
+ virtual DOM::DOMStringImpl* originalString() const;
+
+ virtual const char *renderName() const { return "RenderText"; }
+
+ virtual void setStyle(RenderStyle *style);
+
+
+ virtual void paint( PaintInfo& i, int tx, int ty );
+
+ virtual void deleteInlineBoxes(RenderArena* arena=0);
+
+ DOM::DOMString data() const { return str; }
+ DOM::DOMStringImpl *string() const { return str; }
+
+ virtual InlineBox* createInlineBox(bool, bool);
+
+ virtual void layout() {assert(false);}
+
+ virtual bool nodeAtPoint(NodeInfo& info, int x, int y, int tx, int ty, HitTestAction hitTestAction, bool inBox);
+
+ // Return before, after (offset set to max), or inside the text, at @p offset
+ virtual FindSelectionResult checkSelectionPoint( int _x, int _y, int _tx, int _ty,
+ DOM::NodeImpl*& node, int & offset,
+ SelPointState & );
+
+ unsigned int length() const { if (str) return str->l; else return 0; }
+ QChar *text() const { if (str) return str->s; else return 0; }
+ unsigned int stringLength() const { return str->l; } // non virtual implementation of length()
+ virtual void position(InlineBox* box, int from, int len, bool reverse);
+
+ virtual unsigned int width(unsigned int from, unsigned int len, const Font *f) const;
+ virtual unsigned int width(unsigned int from, unsigned int len, bool firstLine = false) const;
+ virtual short width() const;
+ virtual int height() const;
+
+ // height of the contents (without paddings, margins and borders)
+ virtual short lineHeight( bool firstLine ) const;
+ virtual short baselinePosition( bool firstLine ) const;
+
+ // overrides
+ virtual void calcMinMaxWidth();
+ virtual short minWidth() const { return m_minWidth; }
+ virtual int maxWidth() const { return m_maxWidth; }
+
+ void trimmedMinMaxWidth(short& beginMinW, bool& beginWS,
+ short& endMinW, bool& endWS,
+ bool& hasBreakableChar, bool& hasBreak,
+ short& beginMaxW, short& endMaxW,
+ short& minW, short& maxW, bool& stripFrontSpaces);
+
+ bool containsOnlyWhitespace(unsigned int from, unsigned int len) const;
+
+ ushort startMin() const { return m_startMin; }
+ ushort endMin() const { return m_endMin; }
+
+ // returns the minimum x position of all runs relative to the parent.
+ // defaults to 0.
+ int minXPos() const;
+
+ virtual int inlineXPos() const;
+ virtual int inlineYPos() const;
+
+ bool hasReturn() const { return m_hasReturn; }
+
+ virtual const QFont &font();
+ virtual short verticalPositionHint( bool firstLine ) const;
+
+ bool isFixedWidthFont() const;
+
+ void setText(DOM::DOMStringImpl *text, bool force=false);
+
+ virtual SelectionState selectionState() const {return m_selectionState;}
+ virtual void setSelectionState(SelectionState s) {m_selectionState = s; }
+ virtual void caretPos(int offset, int flags, int &_x, int &_y, int &width, int &height);
+ virtual bool absolutePosition(int &/*xPos*/, int &/*yPos*/, bool f = false) const;
+ bool posOfChar(int ch, int &x, int &y);
+
+ virtual short marginLeft() const { return style()->marginLeft().minWidth(0); }
+ virtual short marginRight() const { return style()->marginRight().minWidth(0); }
+
+ virtual void repaint(Priority p=NormalPriority);
+
+ bool hasBreakableChar() const { return m_hasBreakableChar; }
+ const QFontMetrics &metrics(bool firstLine) const;
+ const Font *htmlFont(bool firstLine) const;
+
+ DOM::TextImpl *element() const
+ { return static_cast<DOM::TextImpl*>(RenderObject::element()); }
+
+ /** returns the lowest possible value the caret offset may have to
+ * still point to a valid position.
+ */
+ virtual long minOffset() const;
+
+ /** returns the highest possible value the caret offset may have to
+ * still point to a valid position.
+ */
+ virtual long maxOffset() const;
+
+ /** returns the number of inline text boxes
+ */
+ unsigned inlineTextBoxCount() const { return m_lines.count(); }
+ /** returns the array of inline text boxes for this render text.
+ */
+ const InlineTextBoxArray &inlineTextBoxes() const { return m_lines; }
+
+#ifdef ENABLE_DUMP
+ virtual void dump(QTextStream &stream, const QString &ind) const;
+#endif
+
+ /** Find the text box that includes the character at @p offset
+ * and return pos, which is the position of the char in the run.
+ * @param offset zero-based offset into DOM string
+ * @param pos returns relative position within text box
+ * @param checkFirstLetter passing @p true will also regard :first-letter
+ * boxes, if available.
+ * @return the text box, or 0 if no match has been found
+ */
+ InlineTextBox * findInlineTextBox( int offset, int &pos,
+ bool checkFirstLetter = false );
+
+protected: // members
+ InlineTextBoxArray m_lines;
+ DOM::DOMStringImpl *str; //
+
+ short m_lineHeight;
+ short m_minWidth;
+ int m_maxWidth;
+ short m_beginMinWidth;
+ short m_endMinWidth;
+
+ SelectionState m_selectionState : 3 ;
+ bool m_hasReturn : 1;
+ bool m_hasBreakableChar : 1;
+ bool m_hasBreak : 1;
+ bool m_hasBeginWS : 1;
+ bool m_hasEndWS : 1;
+
+ ushort m_startMin : 8;
+ ushort m_endMin : 8;
+};
+
+inline const RenderText* InlineTextBox::renderText() const
+{ return static_cast<RenderText*>( object() ); }
+
+inline RenderText* InlineTextBox::renderText()
+{ return static_cast<RenderText*>( object() ); }
+
+// Used to represent a text substring of an element, e.g., for text runs that are split because of
+// first letter and that must therefore have different styles (and positions in the render tree).
+// We cache offsets so that text transformations can be applied in such a way that we can recover
+// the original unaltered string from our corresponding DOM node.
+class RenderTextFragment : public RenderText
+{
+public:
+ RenderTextFragment(DOM::NodeImpl* _node, DOM::DOMStringImpl* _str,
+ int startOffset, int endOffset);
+ RenderTextFragment(DOM::NodeImpl* _node, DOM::DOMStringImpl* _str);
+ ~RenderTextFragment();
+
+ virtual bool isTextFragment() const;
+ virtual const char *renderName() const { return "RenderTextFragment"; }
+
+ uint start() const { return m_start; }
+ uint end() const { return m_end; }
+
+ DOM::DOMStringImpl* contentString() const { return m_generatedContentStr; }
+ virtual DOM::DOMStringImpl* originalString() const;
+
+private:
+ uint m_start;
+ uint m_end;
+ DOM::DOMStringImpl* m_generatedContentStr;
+};
+} // end namespace
+#endif
diff --git a/khtml/rendering/table_layout.cpp b/khtml/rendering/table_layout.cpp
new file mode 100644
index 000000000..bf3daad3a
--- /dev/null
+++ b/khtml/rendering/table_layout.cpp
@@ -0,0 +1,1193 @@
+/*
+ * This file is part of the HTML rendering engine for KDE.
+ *
+ * Copyright (C) 2002 Lars Knoll (knoll@kde.org)
+ * (C) 2002 Dirk Mueller (mueller@kde.org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License.
+ *
+ * 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 "table_layout.h"
+#include "render_table.h"
+
+#include <kglobal.h>
+
+using namespace khtml;
+
+// #define DEBUG_LAYOUT
+
+/*
+ The text below is from the CSS 2.1 specs.
+
+ Fixed table layout
+ ------------------
+
+ With this (fast) algorithm, the horizontal layout of the table does
+ not depend on the contents of the cells; it only depends on the
+ table's width, the width of the columns, and borders or cell
+ spacing.
+
+ The table's width may be specified explicitly with the 'width'
+ property. A value of 'auto' (for both 'display: table' and 'display:
+ inline-table') means use the automatic table layout algorithm.
+
+ In the fixed table layout algorithm, the width of each column is
+ determined as follows:
+
+ 1. A column element with a value other than 'auto' for the 'width'
+ property sets the width for that column.
+
+ 2. Otherwise, a cell in the first row with a value other than
+ 'auto' for the 'width' property sets the width for that column. If
+ the cell spans more than one column, the width is divided over the
+ columns.
+
+ 3. Any remaining columns equally divide the remaining horizontal
+ table space (minus borders or cell spacing).
+
+ The width of the table is then the greater of the value of the
+ 'width' property for the table element and the sum of the column
+ widths (plus cell spacing or borders). If the table is wider than
+ the columns, the extra space should be distributed over the columns.
+
+
+ In this manner, the user agent can begin to lay out the table once
+ the entire first row has been received. Cells in subsequent rows do
+ not affect column widths. Any cell that has content that overflows
+ uses the 'overflow' property to determine whether to clip the
+ overflow content.
+
+_____________________________________________________
+
+ This is not quite true when comparing to IE. IE always honors
+ table-layout:fixed and treats a variable table width as 100%. Makes
+ a lot of sense, and is implemented here the same way.
+
+*/
+
+FixedTableLayout::FixedTableLayout( RenderTable *table )
+ : TableLayout ( table )
+{
+}
+
+FixedTableLayout::~FixedTableLayout()
+{
+}
+
+int FixedTableLayout::calcWidthArray()
+{
+ int usedWidth = 0;
+
+ // iterate over all <col> elements
+ RenderObject *child = table->firstChild();
+ int cCol = 0;
+ int nEffCols = table->numEffCols();
+ width.resize( nEffCols );
+ width.fill( Length( Variable ) );
+
+#ifdef DEBUG_LAYOUT
+ qDebug("FixedTableLayout::calcWidthArray()" );
+ qDebug(" col elements:");
+#endif
+
+ Length grpWidth;
+ while ( child ) {
+ if ( child->isTableCol() ) {
+ RenderTableCol *col = static_cast<RenderTableCol *>(child);
+ int span = col->span();
+ if ( col->firstChild() ) {
+ grpWidth = col->style()->width();
+ } else {
+ Length w = col->style()->width();
+ if ( w.isVariable() )
+ w = grpWidth;
+ int effWidth = 0;
+ if ( w.isFixed() && w.value() > 0 ) {
+ effWidth = w.value();
+ effWidth = KMIN( effWidth, 32760 );
+ }
+#ifdef DEBUG_LAYOUT
+ qDebug(" col element: effCol=%d, span=%d: %d w=%d type=%d",
+ cCol, span, effWidth, w.value(), w.type());
+#endif
+ int usedSpan = 0;
+ int i = 0;
+ while ( usedSpan < span ) {
+ if( cCol + i >= nEffCols ) {
+ table->appendColumn( span - usedSpan );
+ nEffCols++;
+ width.resize( nEffCols );
+ width[nEffCols-1] = Length();
+ }
+ int eSpan = table->spanOfEffCol( cCol+i );
+ if ( (w.isFixed() || w.isPercent()) && w.value() > 0 ) {
+ width[cCol+i] = Length( w.value() * eSpan, w.type() );
+ usedWidth += effWidth * eSpan;
+#ifdef DEBUG_LAYOUT
+ qDebug(" setting effCol %d (span=%d) to width %d(type=%d)",
+ cCol+i, eSpan, width[cCol+i].value(), width[cCol+i].type() );
+#endif
+ }
+ usedSpan += eSpan;
+ i++;
+ }
+ cCol += i;
+ }
+ } else {
+ break;
+ }
+
+ RenderObject *next = child->firstChild();
+ if ( !next )
+ next = child->nextSibling();
+ if ( !next && child->parent()->isTableCol() ) {
+ next = child->parent()->nextSibling();
+ grpWidth = Length();
+ }
+ child = next;
+ }
+
+#ifdef DEBUG_LAYOUT
+ qDebug(" first row:");
+#endif
+ // iterate over the first row in case some are unspecified.
+ RenderTableSection *section = table->head;
+ if ( !section )
+ section = table->firstBody;
+ if ( !section )
+ section = table->foot;
+ if ( section && section->firstChild() ) {
+ cCol = 0;
+ // get the first cell in the first row
+ child = section->firstChild()->firstChild();
+ while ( child ) {
+ if ( child->isTableCell() ) {
+ RenderTableCell *cell = static_cast<RenderTableCell *>(child);
+ Length w = cell->styleOrColWidth();
+ int span = cell->colSpan();
+ int effWidth = 0;
+ if ( (w.isFixed() || w.isPercent()) && w.value() > 0 ) {
+ effWidth = w.value();
+ effWidth = kMin( effWidth, 32760 );
+ }
+#ifdef DEBUG_LAYOUT
+ qDebug(" table cell: effCol=%d, span=%d: %d", cCol, span, effWidth);
+#endif
+ int usedSpan = 0;
+ int i = 0;
+ while ( usedSpan < span ) {
+ Q_ASSERT( cCol + i < nEffCols );
+ int eSpan = table->spanOfEffCol( cCol+i );
+ // only set if no col element has already set it.
+ if ( width[cCol+i].isVariable() && !w.isVariable() ) {
+ width[cCol+i] = Length( w.value()*eSpan, w.type() );
+ usedWidth += effWidth*eSpan;
+#ifdef DEBUG_LAYOUT
+ qDebug(" setting effCol %d (span=%d) to width %d(type=%d)",
+ cCol+i, eSpan, width[cCol+i].value(), width[cCol+i].type() );
+#endif
+ }
+#ifdef DEBUG_LAYOUT
+ else {
+ qDebug(" width of col %d already defined (span=%d)", cCol, table->spanOfEffCol( cCol ) );
+ }
+#endif
+ usedSpan += eSpan;
+ i++;
+ }
+ cCol += i;
+ } else {
+ Q_ASSERT( false );
+ }
+ child = child->nextSibling();
+ }
+ }
+
+ return usedWidth;
+
+}
+
+void FixedTableLayout::calcMinMaxWidth()
+{
+ // we might want to wait until we have all of the first row before
+ // layouting for the first time.
+
+ // only need to calculate the minimum width as the sum of the
+ // cols/cells with a fixed width.
+ //
+ // The maximum width is kMax( minWidth, tableWidth ) if table
+ // width is fixed. If table width is percent, we set maxWidth to
+ // unlimited.
+
+ int bs = table->bordersPaddingAndSpacing();
+ int tableWidth = 0;
+ if (table->style()->width().isFixed()) {
+ tableWidth = table->calcBoxWidth(table->style()->width().value());
+ }
+
+ int mw = calcWidthArray() + bs;
+ table->m_minWidth = kMin( kMax( mw, tableWidth ), 0x7fff );
+ table->m_maxWidth = table->m_minWidth;
+
+ if ( !tableWidth ) {
+ bool haveNonFixed = false;
+ for ( unsigned int i = 0; i < width.size(); i++ ) {
+ if ( !width[i].isFixed() ) {
+ haveNonFixed = true;
+ break;
+ }
+ }
+ if ( haveNonFixed )
+ table->m_maxWidth = 0x7fff;
+ }
+#ifdef DEBUG_LAYOUT
+ qDebug("FixedTableLayout::calcMinMaxWidth: minWidth=%d, maxWidth=%d", table->m_minWidth, table->m_maxWidth );
+#endif
+}
+
+void FixedTableLayout::layout()
+{
+ int tableWidth = table->width() - table->bordersPaddingAndSpacing();
+ int available = tableWidth;
+ int nEffCols = table->numEffCols();
+#ifdef DEBUG_LAYOUT
+ qDebug("FixedTableLayout::layout: tableWidth=%d, numEffCols=%d", tableWidth, nEffCols);
+#endif
+
+
+ QMemArray<int> calcWidth;
+ calcWidth.resize( nEffCols );
+ calcWidth.fill( -1 );
+
+ // first assign fixed width
+ for ( int i = 0; i < nEffCols; i++ ) {
+ if ( width[i].isFixed() ) {
+ calcWidth[i] = width[i].value();
+ available -= width[i].value();
+ }
+ }
+
+ // assign percent width
+ if ( available > 0 ) {
+ int totalPercent = 0;
+ for ( int i = 0; i < nEffCols; i++ )
+ if ( width[i].isPercent() )
+ totalPercent += width[i].value();
+
+ // calculate how much to distribute to percent cells.
+ int base = tableWidth * totalPercent / 100;
+ if ( base > available )
+ base = available;
+
+#ifdef DEBUG_LAYOUT
+ qDebug("FixedTableLayout::layout: assigning percent width, base=%d, totalPercent=%d", base, totalPercent);
+#endif
+ for ( int i = 0; available > 0 && i < nEffCols; i++ ) {
+ if ( width[i].isPercent() ) {
+ // totalPercent may be 0 below if all %-width specifed are 0%. (#172557)
+ int w = totalPercent ? base * width[i].value() / totalPercent : 0;
+ available -= w;
+ calcWidth[i] = w;
+ }
+ }
+ }
+
+ // assign variable width
+ if ( available > 0 ) {
+ int totalVariable = 0;
+ for ( int i = 0; i < nEffCols; i++ )
+ if ( width[i].isVariable() )
+ totalVariable++;
+
+ for ( int i = 0; available > 0 && i < nEffCols; i++ ) {
+ if ( width[i].isVariable() ) {
+ // totalVariable may be 0 below if all the variable widths specified are 0.
+ int w = totalVariable ? available / totalVariable : 0;
+ available -= w;
+ calcWidth[i] = w;
+ totalVariable--;
+ }
+ }
+ }
+
+ for ( int i = 0; i < nEffCols; i++ )
+ if ( calcWidth[i] < 0 )
+ calcWidth[i] = 0; // IE gives min 1 px...
+
+ // spread extra space over columns
+ if ( available > 0 ) {
+ int total = nEffCols;
+ // still have some width to spread
+ int i = nEffCols;
+ while ( i-- ) {
+ int w = available / total;
+ available -= w;
+ total--;
+ calcWidth[i] += w;
+ }
+ }
+
+ int pos = 0;
+ int hspacing = table->borderHSpacing();
+ for ( int i = 0; i < nEffCols; i++ ) {
+#ifdef DEBUG_LAYOUT
+ qDebug("col %d: %d (width %d)", i, pos, calcWidth[i] );
+#endif
+ table->columnPos[i] = pos;
+ pos += calcWidth[i] + hspacing;
+ }
+ table->columnPos[table->columnPos.size()-1] = pos;
+}
+
+// -------------------------------------------------------------------------
+// -------------------------------------------------------------------------
+
+
+AutoTableLayout::AutoTableLayout( RenderTable* table )
+ : TableLayout( table )
+{
+ percentagesDirty = true;
+ effWidthDirty = true;
+ total_percent = 0;
+ hasPercent = false;
+}
+
+AutoTableLayout::~AutoTableLayout()
+{
+}
+
+/* recalculates the full structure needed to do layouting and minmax calculations.
+ This is usually calculated on the fly, but needs to be done fully when table cells change
+ dynamically
+*/
+void AutoTableLayout::recalcColumn( int effCol )
+{
+ Layout &l = layoutStruct[effCol];
+
+ RenderObject *child = table->firstChild();
+ // first we iterate over all rows.
+
+ RenderTableCell *fixedContributor = 0;
+ RenderTableCell *maxContributor = 0;
+
+ while ( child ) {
+ if ( child->isTableSection() ) {
+ RenderTableSection *section = static_cast<RenderTableSection *>(child);
+ int numRows = section->numRows();
+ RenderTableCell *last = 0;
+ for ( int i = 0; i < numRows; i++ ) {
+ RenderTableCell *cell = section->cellAt( i, effCol );
+ if ( cell == (RenderTableCell *)-1 )
+ continue;
+ if ( cell && cell->colSpan() == 1 ) {
+ // A cell originates in this column. Ensure we have
+ // a min/max width of at least 1px for this column now.
+ l.minWidth = kMax(int( l.minWidth ), 1);
+ l.maxWidth = kMax(int( l.maxWidth ), 1);
+
+ if ( !cell->minMaxKnown() )
+ cell->calcMinMaxWidth();
+ if ( cell->minWidth() > l.minWidth )
+ l.minWidth = cell->minWidth();
+ if ( cell->maxWidth() > l.maxWidth ) {
+ l.maxWidth = cell->maxWidth();
+ maxContributor = cell;
+ }
+
+ Length w = cell->styleOrColWidth();
+ w.l.value = kMin( 32767, kMax( 0, w.value() ) );
+ switch( w.type() ) {
+ case Fixed:
+ // ignore width=0
+ if ( w.value() > 0 && !l.width.isPercent() ) {
+ int wval = cell->calcBoxWidth(w.value());
+ if ( l.width.isFixed() ) {
+ // Nav/IE weirdness
+ if ((wval > l.width.value()) ||
+ ((l.width.value() == wval) && (maxContributor == cell))) {
+ l.width.l.value = wval;
+ fixedContributor = cell;
+ }
+ } else {
+ l.width = Length( wval, Fixed );
+ fixedContributor = cell;
+ }
+ }
+ break;
+ case Percent:
+ hasPercent = true;
+ if ( w.value() > 0 && (!l.width.isPercent() || w.value() > l.width.value() ) )
+ l.width = w;
+ break;
+ case Relative:
+ if ( w.isVariable() || (w.isRelative() && w.value() > l.width.value() ) )
+ l.width = w;
+ default:
+ break;
+ }
+ } else {
+ if ( cell && (!effCol || section->cellAt( i, effCol-1 ) != cell) ) {
+ // This spanning cell originates in this column. Ensure we have
+ // a min/max width of at least 1px for this column now.
+ l.minWidth = kMax(int( l.minWidth ), 1);
+ l.maxWidth = kMax(int( l.maxWidth ), 1);
+ insertSpanCell( cell );
+ }
+ last = cell;
+ }
+ }
+ }
+ child = child->nextSibling();
+ }
+
+ // Nav/IE weirdness
+ if ( l.width.isFixed() ) {
+ if ( table->style()->htmlHacks()
+ && (l.maxWidth > l.width.value()) && (fixedContributor != maxContributor)) {
+ l.width = Length();
+ fixedContributor = 0;
+ }
+ }
+
+ l.maxWidth = kMax(l.maxWidth, int(l.minWidth));
+#ifdef DEBUG_LAYOUT
+ qDebug("col %d, final min=%d, max=%d, width=%d(%d)", effCol, l.minWidth, l.maxWidth, l.width.value(), l.width.type() );
+#endif
+
+ // ### we need to add col elements aswell
+}
+
+
+void AutoTableLayout::fullRecalc()
+{
+ percentagesDirty = true;
+ hasPercent = false;
+ effWidthDirty = true;
+
+ int nEffCols = table->numEffCols();
+ layoutStruct.resize( nEffCols );
+ layoutStruct.fill( Layout() );
+ spanCells.fill( 0 );
+
+ RenderObject *child = table->firstChild();
+ Length grpWidth;
+ int cCol = 0;
+ while ( child ) {
+ if ( child->isTableCol() ) {
+ RenderTableCol *col = static_cast<RenderTableCol *>(child);
+ int span = col->span();
+ if ( col->firstChild() ) {
+ grpWidth = col->style()->width();
+ } else {
+ Length w = col->style()->width();
+ if ( w.isVariable() )
+ w = grpWidth;
+ if ( (w.isFixed() && w.value() == 0) ||
+ (w.isPercent() && w.value() == 0) )
+ w = Length();
+ int cEffCol = table->colToEffCol( cCol );
+#ifdef DEBUG_LAYOUT
+ qDebug(" col element %d (eff=%d): Length=%d(%d), span=%d, effColSpan=%d", cCol, cEffCol, w.value(), w.type(), span, table->spanOfEffCol(cEffCol ) );
+#endif
+ if ( !w.isVariable() && span == 1 && cEffCol < nEffCols ) {
+ if ( table->spanOfEffCol( cEffCol ) == 1 ) {
+ layoutStruct[cEffCol].width = w;
+ if (w.isFixed() && layoutStruct[cEffCol].maxWidth < w.value())
+ layoutStruct[cEffCol].maxWidth = w.value();
+ }
+ }
+ cCol += span;
+ }
+ } else {
+ break;
+ }
+
+ RenderObject *next = child->firstChild();
+ if ( !next )
+ next = child->nextSibling();
+ if ( !next && child->parent()->isTableCol() ) {
+ next = child->parent()->nextSibling();
+ grpWidth = Length();
+ }
+ child = next;
+ }
+
+
+ for ( int i = 0; i < nEffCols; i++ )
+ recalcColumn( i );
+}
+
+static bool shouldScaleColumns(RenderTable* table)
+{
+ // A special case. If this table is not fixed width and contained inside
+ // a cell, then don't bloat the maxwidth by examining percentage growth.
+ bool scale = true;
+ while (table) {
+ Length tw = table->style()->width();
+ if ((tw.isVariable() || tw.isPercent()) && !table->isPositioned()) {
+ RenderBlock* cb = table->containingBlock();
+ while (cb && !cb->isCanvas() && !cb->isTableCell() &&
+ cb->style()->width().isVariable() && !cb->isPositioned())
+ cb = cb->containingBlock();
+
+ table = 0;
+ if (cb && cb->isTableCell() &&
+ (cb->style()->width().isVariable() || cb->style()->width().isPercent())) {
+ if (tw.isPercent())
+ scale = false;
+ else {
+ RenderTableCell* cell = static_cast<RenderTableCell*>(cb);
+ if (cell->colSpan() > 1 || cell->table()->style()->width().isVariable())
+ scale = false;
+ else
+ table = cell->table();
+ }
+ }
+ }
+ else
+ table = 0;
+ }
+ return scale;
+}
+
+void AutoTableLayout::calcMinMaxWidth()
+{
+#ifdef DEBUG_LAYOUT
+ qDebug("AutoTableLayout::calcMinMaxWidth");
+#endif
+ fullRecalc();
+
+ int spanMaxWidth = calcEffectiveWidth();
+ int minWidth = 0;
+ int maxWidth = 0;
+ int maxPercent = 0;
+ int maxNonPercent = 0;
+
+ int remainingPercent = 100;
+ for ( unsigned int i = 0; i < layoutStruct.size(); i++ ) {
+ minWidth += layoutStruct[i].effMinWidth;
+ maxWidth += layoutStruct[i].effMaxWidth;
+ if ( layoutStruct[i].effWidth.isPercent() ) {
+ int percent = kMin(layoutStruct[i].effWidth.value(), remainingPercent);
+ int pw = ( layoutStruct[i].effMaxWidth * 100) / kMax(percent, 1);
+ remainingPercent -= percent;
+ maxPercent = kMax( pw, maxPercent );
+ } else {
+ maxNonPercent += layoutStruct[i].effMaxWidth;
+ }
+ }
+
+ if (shouldScaleColumns(table)) {
+ maxNonPercent = (maxNonPercent * 100 + 50) / kMax(remainingPercent, 1);
+ maxWidth = kMax( maxNonPercent, maxWidth );
+ maxWidth = kMax( maxWidth, maxPercent );
+ }
+
+ maxWidth = kMax( maxWidth, spanMaxWidth );
+
+ int bs = table->bordersPaddingAndSpacing();
+ minWidth += bs;
+ maxWidth += bs;
+
+ Length tw = table->style()->width();
+ if ( tw.isFixed() && tw.value() > 0 ) {
+ int width = table->calcBoxWidth(tw.value());
+ minWidth = kMax( minWidth, width );
+ maxWidth = minWidth;
+ }
+
+ table->m_maxWidth = kMin(maxWidth, 0x7fff);
+ table->m_minWidth = kMin(minWidth, 0x7fff);
+#ifdef DEBUG_LAYOUT
+ qDebug(" minWidth=%d, maxWidth=%d", table->m_minWidth, table->m_maxWidth );
+#endif
+}
+
+/*
+ This method takes care of colspans.
+ effWidth is the same as width for cells without colspans. If we have colspans, they get modified.
+ */
+int AutoTableLayout::calcEffectiveWidth()
+{
+ int tMaxWidth = 0;
+
+ unsigned int nEffCols = layoutStruct.size();
+ int hspacing = table->borderHSpacing();
+#ifdef DEBUG_LAYOUT
+ qDebug("AutoTableLayout::calcEffectiveWidth for %d cols", nEffCols );
+#endif
+ for ( unsigned int i = 0; i < nEffCols; i++ ) {
+ layoutStruct[i].effWidth = layoutStruct[i].width;
+ layoutStruct[i].effMinWidth = layoutStruct[i].minWidth;
+ layoutStruct[i].effMaxWidth = layoutStruct[i].maxWidth;
+ }
+
+ for ( unsigned int i = 0; i < spanCells.size(); i++ ) {
+ RenderTableCell *cell = spanCells[i];
+ if ( !cell || cell == (RenderTableCell *)-1 )
+ break;
+ int span = cell->colSpan();
+
+ Length w = cell->styleOrColWidth();
+ if ( !w.isRelative() && w.value() == 0 )
+ w = Length(); // make it Variable
+
+ int col = table->colToEffCol( cell->col() );
+ unsigned int lastCol = col;
+ int cMinWidth = cell->minWidth() + hspacing;
+ int cMaxWidth = cell->maxWidth() + hspacing;
+ int totalPercent = 0;
+ int minWidth = 0;
+ int maxWidth = 0;
+ bool allColsArePercent = true;
+ bool allColsAreFixed = true;
+ bool haveVariable = false;
+ int fixedWidth = 0;
+#ifdef DEBUG_LAYOUT
+ int cSpan = span;
+#endif
+ while ( lastCol < nEffCols && span > 0 ) {
+ switch( layoutStruct[lastCol].width.type() ) {
+ case Percent:
+ totalPercent += layoutStruct[lastCol].width.value();
+ allColsAreFixed = false;
+ break;
+ case Fixed:
+ if (layoutStruct[lastCol].width.value() > 0) {
+ fixedWidth += layoutStruct[lastCol].width.value();
+ allColsArePercent = false;
+ // IE resets effWidth to Variable here, but this breaks the konqueror about page and seems to be some bad
+ // legacy behavior anyway. mozilla doesn't do this so I decided we don't either.
+ break;
+ }
+ // fall through
+ case Variable:
+ haveVariable = true;
+ // fall through
+ default:
+ // If the column is a percentage width, do not let the spanning cell overwrite the
+ // width value. This caused a mis-rendering on amazon.com.
+ // Sample snippet:
+ // <table border=2 width=100%><
+ // <tr><td>1</td><td colspan=2>2-3</tr>
+ // <tr><td>1</td><td colspan=2 width=100%>2-3</td></tr>
+ // </table>
+ if (!layoutStruct[lastCol].effWidth.isPercent()) {
+ layoutStruct[lastCol].effWidth = Length();
+ allColsArePercent = false;
+ }
+ else
+ totalPercent += layoutStruct[lastCol].effWidth.value();
+ allColsAreFixed = false;
+ }
+ span -= table->spanOfEffCol( lastCol );
+ minWidth += layoutStruct[lastCol].effMinWidth;
+ maxWidth += layoutStruct[lastCol].effMaxWidth;
+ lastCol++;
+ cMinWidth -= hspacing;
+ cMaxWidth -= hspacing;
+ }
+#ifdef DEBUG_LAYOUT
+ qDebug(" colspan cell %p at effCol %d, span %d, type %d, value %d cmin=%d min=%d fixedwidth=%d", cell, col, cSpan, w.type(), w.value(), cMinWidth, minWidth, fixedWidth );
+#endif
+
+ // adjust table max width if needed
+ if ( w.isPercent() ) {
+ if ( totalPercent > w.value() || allColsArePercent ) {
+ // can't satify this condition, treat as variable
+ w = Length();
+ } else {
+ int spanMax = kMax( maxWidth, cMaxWidth );
+#ifdef DEBUG_LAYOUT
+ qDebug(" adjusting tMaxWidth (%d): spanMax=%d, value=%d, totalPercent=%d", tMaxWidth, spanMax, w.value(), totalPercent );
+#endif
+ tMaxWidth = kMax( tMaxWidth, spanMax * 100 / w.value() );
+
+ // all non percent columns in the span get percent values to sum up correctly.
+ int percentMissing = w.value() - totalPercent;
+ int totalWidth = 0;
+ for ( unsigned int pos = col; pos < lastCol; pos++ ) {
+ if ( !(layoutStruct[pos].width.isPercent() ) )
+ totalWidth += layoutStruct[pos].effMaxWidth;
+ }
+
+ for ( unsigned int pos = col; pos < lastCol && totalWidth > 0; pos++ ) {
+ if ( !(layoutStruct[pos].width.isPercent() ) ) {
+ int percent = percentMissing * layoutStruct[pos].effMaxWidth / totalWidth;
+#ifdef DEBUG_LAYOUT
+ qDebug(" col %d: setting percent value %d effMaxWidth=%d totalWidth=%d", pos, percent, layoutStruct[pos].effMaxWidth, totalWidth );
+#endif
+ totalWidth -= layoutStruct[pos].effMaxWidth;
+ percentMissing -= percent;
+ if ( percent > 0 )
+ layoutStruct[pos].effWidth = Length( percent, Percent );
+ else
+ layoutStruct[pos].effWidth = Length();
+ }
+ }
+
+ }
+ }
+
+ // make sure minWidth and maxWidth of the spanning cell are honoured
+ if ( cMinWidth > minWidth ) {
+ if ( allColsAreFixed ) {
+#ifdef DEBUG_LAYOUT
+ qDebug("extending minWidth of cols %d-%d to %dpx currentMin=%d accroding to fixed sum %d", col, lastCol-1, cMinWidth, minWidth, fixedWidth );
+#endif
+ for ( unsigned int pos = col; fixedWidth > 0 && pos < lastCol; pos++ ) {
+ int w = kMax( int( layoutStruct[pos].effMinWidth ), cMinWidth * layoutStruct[pos].width.value() / fixedWidth );
+#ifdef DEBUG_LAYOUT
+ qDebug(" col %d: min=%d, effMin=%d, new=%d", pos, layoutStruct[pos].effMinWidth, layoutStruct[pos].effMinWidth, w );
+#endif
+ fixedWidth -= layoutStruct[pos].width.value();
+ cMinWidth -= w;
+ layoutStruct[pos].effMinWidth = w;
+ }
+
+ } else if ( allColsArePercent ) {
+ int maxw = maxWidth;
+ int minw = minWidth;
+ int cminw = cMinWidth;
+
+ for ( unsigned int pos = col; maxw > 0 && pos < lastCol; pos++ ) {
+ if ( layoutStruct[pos].effWidth.isPercent() && layoutStruct[pos].effWidth.value()>0 && fixedWidth <= cMinWidth) {
+ int w = layoutStruct[pos].effMinWidth;
+ w = kMax( w, cminw*layoutStruct[pos].effWidth.value()/totalPercent );
+ w = kMin(layoutStruct[pos].effMinWidth+(cMinWidth-minw), w);
+#ifdef DEBUG_LAYOUT
+ qDebug(" col %d: min=%d, effMin=%d, new=%d", pos, layoutStruct[pos].effMinWidth, layoutStruct[pos].effMinWidth, w );
+#endif
+ maxw -= layoutStruct[pos].effMaxWidth;
+ minw -= layoutStruct[pos].effMinWidth;
+ cMinWidth -= w;
+ layoutStruct[pos].effMinWidth = w;
+ }
+ }
+ } else {
+#ifdef DEBUG_LAYOUT
+ qDebug("extending minWidth of cols %d-%d to %dpx currentMin=%d", col, lastCol-1, cMinWidth, minWidth );
+#endif
+ int maxw = maxWidth;
+ int minw = minWidth;
+
+ // Give min to variable first, to fixed second, and to others third.
+ for ( unsigned int pos = col; maxw > 0 && pos < lastCol; pos++ ) {
+ if ( layoutStruct[pos].width.isFixed() && haveVariable && fixedWidth <= cMinWidth ) {
+ int w = kMax( int( layoutStruct[pos].effMinWidth ), layoutStruct[pos].width.value() );
+ fixedWidth -= layoutStruct[pos].width.value();
+ minw -= layoutStruct[pos].effMinWidth;
+#ifdef DEBUG_LAYOUT
+ qDebug(" col %d: min=%d, effMin=%d, new=%d", pos, layoutStruct[pos].effMinWidth, layoutStruct[pos].effMinWidth, w );
+#endif
+ maxw -= layoutStruct[pos].effMaxWidth;
+ cMinWidth -= w;
+ layoutStruct[pos].effMinWidth = w;
+ }
+ }
+
+ for ( unsigned int pos = col; maxw > 0 && pos < lastCol && minw < cMinWidth; pos++ ) {
+ if ( !(layoutStruct[pos].width.isFixed() && haveVariable && fixedWidth <= cMinWidth) ) {
+ int w = kMax( int( layoutStruct[pos].effMinWidth ), cMinWidth * layoutStruct[pos].effMaxWidth / maxw );
+ w = kMin(layoutStruct[pos].effMinWidth+(cMinWidth-minw), w);
+
+#ifdef DEBUG_LAYOUT
+ qDebug(" col %d: min=%d, effMin=%d, new=%d", pos, layoutStruct[pos].effMinWidth, layoutStruct[pos].effMinWidth, w );
+#endif
+ maxw -= layoutStruct[pos].effMaxWidth;
+ minw -= layoutStruct[pos].effMinWidth;
+ cMinWidth -= w;
+ layoutStruct[pos].effMinWidth = w;
+ }
+ }
+ }
+ }
+ if ( !w.isPercent() ) {
+ if ( cMaxWidth > maxWidth ) {
+#ifdef DEBUG_LAYOUT
+ qDebug("extending maxWidth of cols %d-%d to %dpx", col, lastCol-1, cMaxWidth );
+#endif
+ for ( unsigned int pos = col; maxWidth > 0 && pos < lastCol; pos++ ) {
+ int w = kMax( int( layoutStruct[pos].effMaxWidth ), cMaxWidth * layoutStruct[pos].effMaxWidth / maxWidth );
+#ifdef DEBUG_LAYOUT
+ qDebug(" col %d: max=%d, effMax=%d, new=%d", pos, layoutStruct[pos].effMaxWidth, layoutStruct[pos].effMaxWidth, w );
+#endif
+ maxWidth -= layoutStruct[pos].effMaxWidth;
+ cMaxWidth -= w;
+ layoutStruct[pos].effMaxWidth = w;
+ }
+ }
+ } else {
+ for ( unsigned int pos = col; pos < lastCol; pos++ )
+ layoutStruct[pos].maxWidth = kMax(layoutStruct[pos].maxWidth, int(layoutStruct[pos].minWidth) );
+ }
+ }
+ effWidthDirty = false;
+
+// qDebug("calcEffectiveWidth: tMaxWidth=%d", tMaxWidth );
+ return tMaxWidth;
+}
+
+/* gets all cells that originate in a column and have a cellspan > 1
+ Sorts them by increasing cellspan
+*/
+void AutoTableLayout::insertSpanCell( RenderTableCell *cell )
+{
+ if ( !cell || cell == (RenderTableCell *)-1 || cell->colSpan() == 1 )
+ return;
+
+// qDebug("inserting span cell %p with span %d", cell, cell->colSpan() );
+ int size = spanCells.size();
+ if ( !size || spanCells[size-1] != 0 ) {
+ spanCells.resize( size + 10 );
+ for ( int i = 0; i < 10; i++ )
+ spanCells[size+i] = 0;
+ size += 10;
+ }
+
+ // add them in sort. This is a slow algorithm, and a binary search or a fast sorting after collection would be better
+ unsigned int pos = 0;
+ int span = cell->colSpan();
+ while ( pos < spanCells.size() && spanCells[pos] && span > spanCells[pos]->colSpan() )
+ pos++;
+ memmove( spanCells.data()+pos+1, spanCells.data()+pos, (size-pos-1)*sizeof( RenderTableCell * ) );
+ spanCells[pos] = cell;
+}
+
+
+void AutoTableLayout::layout()
+{
+ // table layout based on the values collected in the layout structure.
+ int tableWidth = table->width() - table->bordersPaddingAndSpacing();
+ int available = tableWidth;
+ int nEffCols = table->numEffCols();
+
+ if ( nEffCols != (int)layoutStruct.size() ) {
+ qWarning("WARNING: nEffCols is not equal to layoutstruct!" );
+ fullRecalc();
+ nEffCols = table->numEffCols();
+ }
+#ifdef DEBUG_LAYOUT
+ qDebug("AutoTableLayout::layout()");
+#endif
+
+ if ( effWidthDirty )
+ calcEffectiveWidth();
+
+#ifdef DEBUG_LAYOUT
+ qDebug(" tableWidth=%d, nEffCols=%d", tableWidth, nEffCols );
+ for ( int i = 0; i < nEffCols; i++ ) {
+ qDebug(" effcol %d is of type %d value %d, minWidth=%d, maxWidth=%d",
+ i, layoutStruct[i].width.type(), layoutStruct[i].width.value(),
+ layoutStruct[i].minWidth, layoutStruct[i].maxWidth );
+ qDebug(" effective: type %d value %d, minWidth=%d, maxWidth=%d",
+ layoutStruct[i].effWidth.type(), layoutStruct[i].effWidth.value(),
+ layoutStruct[i].effMinWidth, layoutStruct[i].effMaxWidth );
+ }
+#endif
+
+ bool havePercent = false;
+ bool haveRelative = false;
+ int totalRelative = 0;
+ int numVariable = 0;
+ int numFixed = 0;
+ int totalVariable = 0;
+ int totalFixed = 0;
+ int totalPercent = 0;
+ int allocVariable = 0;
+
+ // fill up every cell with it's minWidth
+ for ( int i = 0; i < nEffCols; i++ ) {
+ int w = layoutStruct[i].effMinWidth;
+ layoutStruct[i].calcWidth = w;
+ available -= w;
+ Length& width = layoutStruct[i].effWidth;
+ switch( width.type()) {
+ case Percent:
+ havePercent = true;
+ totalPercent += width.value();
+ break;
+ case Relative:
+ haveRelative = true;
+ totalRelative += width.value();
+ break;
+ case Fixed:
+ numFixed++;
+ totalFixed += layoutStruct[i].effMaxWidth;
+ // fall through
+ break;
+ case Variable:
+ case Static:
+ numVariable++;
+ totalVariable += layoutStruct[i].effMaxWidth;
+ allocVariable += w;
+ }
+ }
+
+ // allocate width to percent cols
+ if ( available > 0 && havePercent ) {
+ for ( int i = 0; i < nEffCols; i++ ) {
+ const Length &width = layoutStruct[i].effWidth;
+ if ( width.isPercent() ) {
+ int w = kMax ( int( layoutStruct[i].effMinWidth ), width.minWidth( tableWidth ) );
+ available += layoutStruct[i].calcWidth - w;
+ layoutStruct[i].calcWidth = w;
+ }
+ }
+ if ( totalPercent > 100 ) {
+ // remove overallocated space from the last columns
+ int excess = tableWidth*(totalPercent-100)/100;
+ for ( int i = nEffCols-1; i >= 0; i-- ) {
+ if ( layoutStruct[i].effWidth.isPercent() ) {
+ int w = layoutStruct[i].calcWidth;
+ int reduction = kMin( w, excess );
+ // the lines below might look inconsistent, but that's the way it's handled in mozilla
+ excess -= reduction;
+ int newWidth = kMax( int (layoutStruct[i].effMinWidth), w - reduction );
+ available += w - newWidth;
+ layoutStruct[i].calcWidth = newWidth;
+ //qDebug("col %d: reducing to %d px (reduction=%d)", i, newWidth, reduction );
+ }
+ }
+ }
+ }
+#ifdef DEBUG_LAYOUT
+ qDebug("percent satisfied: available is %d", available);
+#endif
+
+ // then allocate width to fixed cols
+ if ( available > 0 ) {
+ for ( int i = 0; i < nEffCols; ++i ) {
+ const Length &width = layoutStruct[i].effWidth;
+ if ( width.isFixed() && width.value() > layoutStruct[i].calcWidth ) {
+ available += layoutStruct[i].calcWidth - width.value();
+ layoutStruct[i].calcWidth = width.value();
+ }
+ }
+ }
+#ifdef DEBUG_LAYOUT
+ qDebug("fixed satisfied: available is %d", available);
+#endif
+
+ // now satisfy relative
+ if ( available > 0 ) {
+ for ( int i = 0; i < nEffCols; i++ ) {
+ const Length &width = layoutStruct[i].effWidth;
+ if ( width.isRelative() && width.value() ) {
+ // width=0* gets effMinWidth.
+ int w = width.value()*tableWidth/totalRelative;
+ available += layoutStruct[i].calcWidth - w;
+ layoutStruct[i].calcWidth = w;
+ }
+ }
+ }
+
+ // now satisfy variable
+ if ( available > 0 && numVariable ) {
+ available += allocVariable; // this gets redistributed
+ //qDebug("redistributing %dpx to %d variable columns. totalVariable=%d", available, numVariable, totalVariable );
+ for ( int i = 0; i < nEffCols; i++ ) {
+ const Length &width = layoutStruct[i].effWidth;
+ if ( width.isVariable() && totalVariable != 0 ) {
+ int w = kMax( int ( layoutStruct[i].calcWidth ),
+ available * layoutStruct[i].effMaxWidth / totalVariable );
+ available -= w;
+ totalVariable -= layoutStruct[i].effMaxWidth;
+ layoutStruct[i].calcWidth = w;
+ }
+ }
+ }
+#ifdef DEBUG_LAYOUT
+ qDebug("variable satisfied: available is %d", available );
+#endif
+
+ // spread over fixed colums
+ if ( available > 0 && numFixed) {
+ // still have some width to spread, distribute to fixed columns
+ for ( int i = 0; i < nEffCols; i++ ) {
+ const Length &width = layoutStruct[i].effWidth;
+ if ( width.isFixed() ) {
+ int w = available * layoutStruct[i].effMaxWidth / totalFixed;
+ available -= w;
+ totalFixed -= layoutStruct[i].effMaxWidth;
+ layoutStruct[i].calcWidth += w;
+ }
+ }
+ }
+
+#ifdef DEBUG_LAYOUT
+ qDebug("after fixed distribution: available=%d", available );
+#endif
+
+ // spread over percent colums
+ if ( available > 0 && hasPercent && totalPercent < 100) {
+ // still have some width to spread, distribute weighted to percent columns
+ for ( int i = 0; i < nEffCols; i++ ) {
+ const Length &width = layoutStruct[i].effWidth;
+ if ( width.isPercent() ) {
+ int w = available * width.value() / totalPercent;
+ available -= w;
+ totalPercent -= width.value();
+ layoutStruct[i].calcWidth += w;
+ if (!available || !totalPercent) break;
+ }
+ }
+ }
+
+#ifdef DEBUG_LAYOUT
+ qDebug("after percent distribution: available=%d", available );
+#endif
+
+ // spread over the rest
+ if ( available > 0 ) {
+ int total = nEffCols;
+ // still have some width to spread
+ int i = nEffCols;
+ while ( i-- ) {
+ int w = available / total;
+ available -= w;
+ total--;
+ layoutStruct[i].calcWidth += w;
+ }
+ }
+
+#ifdef DEBUG_LAYOUT
+ qDebug("after equal distribution: available=%d", available );
+#endif
+ // if we have overallocated, reduce every cell according to the difference between desired width and minwidth
+ // this seems to produce to the pixel exaxt results with IE. Wonder is some of this also holds for width distributing.
+ if ( available < 0 ) {
+ // Need to reduce cells with the following prioritization:
+ // (1) Variable
+ // (2) Relative
+ // (3) Fixed
+ // (4) Percent
+ // This is basically the reverse of how we grew the cells.
+ if (available < 0) {
+ int mw = 0;
+ for ( int i = nEffCols-1; i >= 0; i-- ) {
+ Length &width = layoutStruct[i].effWidth;
+ if (width.isVariable())
+ mw += layoutStruct[i].calcWidth - layoutStruct[i].effMinWidth;
+ }
+
+ for ( int i = nEffCols-1; i >= 0 && mw > 0; i-- ) {
+ Length &width = layoutStruct[i].effWidth;
+ if (width.isVariable()) {
+ int minMaxDiff = layoutStruct[i].calcWidth-layoutStruct[i].effMinWidth;
+ int reduce = available * minMaxDiff / mw;
+ layoutStruct[i].calcWidth += reduce;
+ available -= reduce;
+ mw -= minMaxDiff;
+ if ( available >= 0 )
+ break;
+ }
+ }
+ }
+
+ if (available < 0) {
+ int mw = 0;
+ for ( int i = nEffCols-1; i >= 0; i-- ) {
+ Length &width = layoutStruct[i].effWidth;
+ if (width.isRelative())
+ mw += layoutStruct[i].calcWidth - layoutStruct[i].effMinWidth;
+ }
+
+ for ( int i = nEffCols-1; i >= 0 && mw > 0; i-- ) {
+ Length &width = layoutStruct[i].effWidth;
+ if (width.isRelative()) {
+ int minMaxDiff = layoutStruct[i].calcWidth-layoutStruct[i].effMinWidth;
+ int reduce = available * minMaxDiff / mw;
+ layoutStruct[i].calcWidth += reduce;
+ available -= reduce;
+ mw -= minMaxDiff;
+ if ( available >= 0 )
+ break;
+ }
+ }
+ }
+
+ if (available < 0) {
+ int mw = 0;
+ for ( int i = nEffCols-1; i >= 0; i-- ) {
+ Length &width = layoutStruct[i].effWidth;
+ if (width.isFixed())
+ mw += layoutStruct[i].calcWidth - layoutStruct[i].effMinWidth;
+ }
+
+ for ( int i = nEffCols-1; i >= 0 && mw > 0; i-- ) {
+ Length &width = layoutStruct[i].effWidth;
+ if (width.isFixed()) {
+ int minMaxDiff = layoutStruct[i].calcWidth-layoutStruct[i].effMinWidth;
+ int reduce = available * minMaxDiff / mw;
+ layoutStruct[i].calcWidth += reduce;
+ available -= reduce;
+ mw -= minMaxDiff;
+ if ( available >= 0 )
+ break;
+ }
+ }
+ }
+
+ if (available < 0) {
+ int mw = 0;
+ for ( int i = nEffCols-1; i >= 0; i-- ) {
+ Length &width = layoutStruct[i].effWidth;
+ if (width.isPercent())
+ mw += layoutStruct[i].calcWidth - layoutStruct[i].effMinWidth;
+ }
+
+ for ( int i = nEffCols-1; i >= 0 && mw > 0; i-- ) {
+ Length &width = layoutStruct[i].effWidth;
+ if (width.isPercent()) {
+ int minMaxDiff = layoutStruct[i].calcWidth-layoutStruct[i].effMinWidth;
+ int reduce = available * minMaxDiff / mw;
+ layoutStruct[i].calcWidth += reduce;
+ available -= reduce;
+ mw -= minMaxDiff;
+ if ( available >= 0 )
+ break;
+ }
+ }
+ }
+ }
+
+ //qDebug( " final available=%d", available );
+
+ int pos = 0;
+ for ( int i = 0; i < nEffCols; i++ ) {
+#ifdef DEBUG_LAYOUT
+ qDebug("col %d: %d (width %d)", i, pos, layoutStruct[i].calcWidth );
+#endif
+ table->columnPos[i] = pos;
+ pos += layoutStruct[i].calcWidth + table->borderHSpacing();
+ }
+ table->columnPos[table->columnPos.size()-1] = pos;
+
+}
+
+
+void AutoTableLayout::calcPercentages() const
+{
+ total_percent = 0;
+ for ( unsigned int i = 0; i < layoutStruct.size(); i++ ) {
+ if ( layoutStruct[i].width.isPercent() )
+ total_percent += layoutStruct[i].width.value();
+ }
+ percentagesDirty = false;
+}
+
+#undef DEBUG_LAYOUT
diff --git a/khtml/rendering/table_layout.h b/khtml/rendering/table_layout.h
new file mode 100644
index 000000000..bc28a91a5
--- /dev/null
+++ b/khtml/rendering/table_layout.h
@@ -0,0 +1,112 @@
+/*
+ * This file is part of the HTML rendering engine for KDE.
+ *
+ * Copyright (C) 2002 Lars Knoll (knoll@kde.org)
+ * (C) 2002 Dirk Mueller (mueller@kde.org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef TABLE_LAYOUT_H
+#define TABLE_LAYOUT_H
+
+#include <qmemarray.h>
+#include <misc/khtmllayout.h>
+
+namespace khtml {
+
+class RenderTable;
+class RenderTableCell;
+
+// -------------------------------------------------------------------------
+
+class TableLayout
+{
+public:
+ TableLayout( RenderTable *t ) : table( t ) {}
+ virtual ~TableLayout() {}
+
+ virtual void calcMinMaxWidth() = 0;
+ virtual void layout() = 0;
+
+protected:
+ RenderTable *table;
+};
+
+// -------------------------------------------------------------------------
+
+class FixedTableLayout : public TableLayout
+{
+public:
+ FixedTableLayout( RenderTable *table );
+ ~FixedTableLayout();
+
+ void calcMinMaxWidth();
+ void layout();
+
+protected:
+ int calcWidthArray();
+
+ QMemArray<Length> width;
+};
+
+// -------------------------------------------------------------------------
+
+class AutoTableLayout : public TableLayout
+{
+public:
+ AutoTableLayout( RenderTable *table );
+ ~AutoTableLayout();
+
+ void calcMinMaxWidth();
+ void layout();
+
+
+protected:
+ void fullRecalc();
+ void recalcColumn( int effCol );
+ int totalPercent() const {
+ if ( percentagesDirty )
+ calcPercentages();
+ return total_percent;
+ }
+ void calcPercentages() const;
+ int calcEffectiveWidth();
+ void insertSpanCell( RenderTableCell *cell );
+
+ struct Layout {
+ Layout() : minWidth( 1 ), maxWidth( 1 ),
+ effMinWidth( 0 ), effMaxWidth( 0 ),
+ calcWidth( 0 ) {}
+ Length width;
+ Length effWidth;
+ short minWidth;
+ int maxWidth;
+ short effMinWidth;
+ int effMaxWidth;
+ short calcWidth;
+ };
+
+ QMemArray<Layout> layoutStruct;
+ QMemArray<RenderTableCell *>spanCells;
+ bool hasPercent : 1;
+ mutable bool percentagesDirty : 1;
+ mutable bool effWidthDirty : 1;
+ mutable unsigned short total_percent;
+};
+
+}
+#endif
diff --git a/khtml/rendering/table_layout.txt b/khtml/rendering/table_layout.txt
new file mode 100644
index 000000000..14a74bd1e
--- /dev/null
+++ b/khtml/rendering/table_layout.txt
@@ -0,0 +1,74 @@
+CSS describes two ways of layouting tables. Auto layout (the NS4
+compliant HTML table layout) and fixed layout. The fixed layout
+strategy is described in detail in the CSS specs and will not be
+repeated here.
+
+Due to the fact that two layout strategies exist, it is rather natural
+to encapsulate the layouting process in a TableLayout class. Two types
+(FixedTableLayout and AutoTableLayout) exist. AutoTableLayout is the
+default and also need a quirks flags for NS compatibility
+mode. FixedTableLayout will be used if a style rule sets the
+table-layout property to fixed.
+
+The grid of table cells is stored in each table section, as spans
+can't pass section borders. Changing the number of cols in the grid
+has to be done by the table to keep all grids (for all sections) in
+sync. The grid only stores effective columns. The table below would
+only result in one column being allocated in the grid:
+
+<table><tr><td colspan=1000>foo</td></tr></table>
+
+Once a colspan get's used, the column is split into its subparts. To
+do this, the table has to store the colspans of effective columns in a
+structure.
+
+
+
+
+NS & Mozilla compliant table layouting algorithm (AutoTableLayout)
+------------------------------------------------------------------
+
+The table layout algorithm computes a set of layout hints from the
+informations in the table cells, <col> elements and style
+sheets. Hints from different sources are treated a bit differently in
+the collection stage.
+
+In addition certain operations are only done in quirks (NS4 compat)
+mode.
+
+Resizing the table doesn't require you to build up this information
+again. All that is needed is a list of layout hints for each column.
+
+The algorithm below describes to the best of our knowledge the way
+table alyouting is handled in a NS compatible way.
+
+There are two stages, the collection stage (assocaited with
+calcMinMaxWidth() in khtml) and the stage assigning width to cells
+(associated with layout()).
+
+A set of hinted widths are used to determine the final table layout in
+the layouting stage.
+
+enum hintType {
+ MinWidth,
+ DesiredWidth,
+ FixedWidth,
+ MinWidthAdjusted,
+ DesiredWidthAdjusted,
+ FixedWidthAdjusted,
+ PercentWidth,
+ PercentWidthAdjusted,
+ ProportionalWidth
+};
+
+One width (in pixels) for each hintType describes how the column
+wishes to be layouted. The layouting stage operates only on an array
+of hints for each column.
+
+An additional totalCellSpacing variable is used to know about the
+remaining width to distribute.
+
+Collection stage:
+-----------------
+
+