blob: 978a6359e36104282f254ae4c471704f152caf07 [file] [log] [blame]
kociendabb0c24b2001-08-24 14:24:40 +00001/**
2 * This file is part of the html renderer for KDE.
3 *
4 * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
hyatt98b16282004-03-31 18:43:12 +00005 * Copyright (C) 2004 Apple Computer, Inc.
kociendabb0c24b2001-08-24 14:24:40 +00006 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB. If not, write to
19 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
21 *
kociendabb0c24b2001-08-24 14:24:40 +000022 */
darinbe4c67d2005-12-19 19:53:12 +000023
mjsb64c50a2005-10-03 21:13:12 +000024#include "config.h"
kociendabb0c24b2001-08-24 14:24:40 +000025#include "bidi.h"
darin36d11362006-04-11 16:30:21 +000026
darinb9481ed2006-03-20 02:57:59 +000027#include "Document.h"
eseidel40eb1b92006-03-25 22:20:36 +000028#include "Element.h"
mjsd4145d12006-01-11 09:36:47 +000029#include "FrameView.h"
eseidel3a6d1322006-01-09 03:14:50 +000030#include "InlineTextBox.h"
darin36d11362006-04-11 16:30:21 +000031#include "RenderArena.h"
32#include "RenderCanvas.h"
33#include "break_lines.h"
34#include <kxmlcore/AlwaysInline.h>
hyatt8c371e52004-06-16 01:19:26 +000035
darin7bd70952006-04-13 07:07:34 +000036using namespace std;
37
darinb9481ed2006-03-20 02:57:59 +000038namespace WebCore {
mjsfe301d72003-10-02 18:46:18 +000039
hyatt275d0702005-11-03 23:53:57 +000040// an iterator which traverses all the objects within a block
mjsfe301d72003-10-02 18:46:18 +000041struct BidiIterator
42{
hyatt275d0702005-11-03 23:53:57 +000043 BidiIterator() : block(0), obj(0), pos(0) {}
44 BidiIterator(RenderBlock* b, RenderObject* o, unsigned int p)
45 :block(b), obj(o), pos(p) {}
hyattffe78712003-02-11 01:59:29 +000046
hyatt275d0702005-11-03 23:53:57 +000047 void increment(BidiState& state);
mjsfe301d72003-10-02 18:46:18 +000048 bool atEnd() const;
49
hyatt275d0702005-11-03 23:53:57 +000050 const QChar& current() const;
mjsfe301d72003-10-02 18:46:18 +000051
hyatt275d0702005-11-03 23:53:57 +000052 QChar::Direction direction() const;
53
54 RenderBlock* block;
55 RenderObject* obj;
mjsfe301d72003-10-02 18:46:18 +000056 unsigned int pos;
57};
58
mjsfe301d72003-10-02 18:46:18 +000059struct BidiState {
eseidel789896f2005-11-27 22:52:09 +000060 BidiState() : context(0), dir(QChar::DirON), adjustEmbedding(false), reachedEndOfLine(false) {}
mjsfe301d72003-10-02 18:46:18 +000061
62 BidiIterator sor;
63 BidiIterator eor;
64 BidiIterator last;
65 BidiIterator current;
mjsbb3d15c2005-12-01 10:32:32 +000066 RefPtr<BidiContext> context;
mjsfe301d72003-10-02 18:46:18 +000067 BidiStatus status;
hyatt275d0702005-11-03 23:53:57 +000068 QChar::Direction dir;
69 bool adjustEmbedding;
eseidel789896f2005-11-27 22:52:09 +000070 BidiIterator endOfLine;
71 bool reachedEndOfLine;
darine4fa9e22005-12-16 18:18:50 +000072 BidiIterator lastBeforeET;
mjsfe301d72003-10-02 18:46:18 +000073};
hyatt2f1e7102003-02-20 01:27:03 +000074
eseidel789896f2005-11-27 22:52:09 +000075inline bool operator==(const BidiStatus& status1, const BidiStatus& status2)
76{
77 return status1.eor == status2.eor && status1.last == status2.last && status1.lastStrong == status2.lastStrong;
78}
79
80inline bool operator!=(const BidiStatus& status1, const BidiStatus& status2)
81{
82 return !(status1 == status2);
83}
84
hyatt2f1e7102003-02-20 01:27:03 +000085// Used to track a list of chained bidi runs.
mjsfe301d72003-10-02 18:46:18 +000086static BidiRun* sFirstBidiRun;
87static BidiRun* sLastBidiRun;
88static int sBidiRunCount;
89static BidiRun* sCompactFirstBidiRun;
90static BidiRun* sCompactLastBidiRun;
91static int sCompactBidiRunCount;
92static bool sBuildingCompactRuns;
hyatt85586af2003-02-19 23:22:42 +000093
94// Midpoint globals. The goal is not to do any allocation when dealing with
95// these midpoints, so we just keep an array around and never clear it. We track
96// the number of items and position using the two other variables.
darinb9481ed2006-03-20 02:57:59 +000097static DeprecatedArray<BidiIterator> *smidpoints;
98static unsigned sNumMidpoints;
99static unsigned sCurrMidpoint;
mjsfe301d72003-10-02 18:46:18 +0000100static bool betweenMidpoints;
hyatt85586af2003-02-19 23:22:42 +0000101
hyatt33f8d492002-11-12 21:44:52 +0000102static bool isLineEmpty = true;
hyatt0c3a9862004-02-23 21:26:26 +0000103static bool previousLineBrokeCleanly = true;
mjs6f821c82002-03-22 00:31:57 +0000104static bool emptyRun = true;
darinb70665a2002-04-15 23:43:21 +0000105static int numSpaces;
mjs6f821c82002-03-22 00:31:57 +0000106
hyatt275d0702005-11-03 23:53:57 +0000107static void embed(QChar::Direction d, BidiState &bidi);
108static void appendRun(BidiState &bidi);
mjs6f821c82002-03-22 00:31:57 +0000109
hyattffe78712003-02-11 01:59:29 +0000110static int getBPMWidth(int childValue, Length cssUnit)
111{
hyatt275d0702005-11-03 23:53:57 +0000112 if (!cssUnit.isIntrinsicOrAuto())
darin947a31b2006-02-24 03:08:41 +0000113 return (cssUnit.isFixed() ? cssUnit.value() : childValue);
hyattffe78712003-02-11 01:59:29 +0000114 return 0;
115}
116
117static int getBorderPaddingMargin(RenderObject* child, bool endOfInline)
118{
119 RenderStyle* cstyle = child->style();
120 int result = 0;
121 bool leftSide = (cstyle->direction() == LTR) ? !endOfInline : endOfInline;
122 result += getBPMWidth((leftSide ? child->marginLeft() : child->marginRight()),
123 (leftSide ? cstyle->marginLeft() :
124 cstyle->marginRight()));
125 result += getBPMWidth((leftSide ? child->paddingLeft() : child->paddingRight()),
126 (leftSide ? cstyle->paddingLeft() :
127 cstyle->paddingRight()));
128 result += leftSide ? child->borderLeft() : child->borderRight();
129 return result;
130}
131
132static int inlineWidth(RenderObject* child, bool start = true, bool end = true)
133{
134 int extraWidth = 0;
135 RenderObject* parent = child->parent();
hyatt450813d2003-07-25 20:22:34 +0000136 while (parent->isInline() && !parent->isInlineBlockOrInlineTable()) {
hyattffe78712003-02-11 01:59:29 +0000137 if (start && parent->firstChild() == child)
138 extraWidth += getBorderPaddingMargin(parent, false);
139 if (end && parent->lastChild() == child)
140 extraWidth += getBorderPaddingMargin(parent, true);
141 child = parent;
142 parent = child->parent();
143 }
144 return extraWidth;
145}
146
darin35355e52002-12-20 09:19:00 +0000147#ifndef NDEBUG
harrison0012ced2005-10-06 18:37:42 +0000148static bool inBidiRunDestroy;
hyattffe78712003-02-11 01:59:29 +0000149#endif
150
harrisone8363b42005-10-06 00:54:06 +0000151void BidiRun::destroy(RenderArena* renderArena)
hyattffe78712003-02-11 01:59:29 +0000152{
153#ifndef NDEBUG
harrison0012ced2005-10-06 18:37:42 +0000154 inBidiRunDestroy = true;
hyattffe78712003-02-11 01:59:29 +0000155#endif
156 delete this;
157#ifndef NDEBUG
harrison0012ced2005-10-06 18:37:42 +0000158 inBidiRunDestroy = false;
hyattffe78712003-02-11 01:59:29 +0000159#endif
160
161 // Recover the size left there for us by operator delete and free the memory.
162 renderArena->free(*(size_t *)this, this);
163}
164
165void* BidiRun::operator new(size_t sz, RenderArena* renderArena) throw()
166{
167 return renderArena->allocate(sz);
168}
169
170void BidiRun::operator delete(void* ptr, size_t sz)
171{
harrison0012ced2005-10-06 18:37:42 +0000172 assert(inBidiRunDestroy);
hyattffe78712003-02-11 01:59:29 +0000173
harrisone8363b42005-10-06 00:54:06 +0000174 // Stash size where destroy() can find it.
hyattffe78712003-02-11 01:59:29 +0000175 *(size_t*)ptr = sz;
176}
177
178static void deleteBidiRuns(RenderArena* arena)
179{
darinef0c09f2005-10-09 01:46:17 +0000180 emptyRun = true;
hyatt2f1e7102003-02-20 01:27:03 +0000181 if (!sFirstBidiRun)
hyattffe78712003-02-11 01:59:29 +0000182 return;
183
hyatt2f1e7102003-02-20 01:27:03 +0000184 BidiRun* curr = sFirstBidiRun;
185 while (curr) {
186 BidiRun* s = curr->nextRun;
harrisone8363b42005-10-06 00:54:06 +0000187 curr->destroy(arena);
hyatt2f1e7102003-02-20 01:27:03 +0000188 curr = s;
hyattffe78712003-02-11 01:59:29 +0000189 }
hyatt2f1e7102003-02-20 01:27:03 +0000190
191 sFirstBidiRun = 0;
192 sLastBidiRun = 0;
193 sBidiRunCount = 0;
hyattffe78712003-02-11 01:59:29 +0000194}
195
kociendabb0c24b2001-08-24 14:24:40 +0000196// ---------------------------------------------------------------------
197
198/* a small helper class used internally to resolve Bidi embedding levels.
199 Each line of text caches the embedding level at the start of the line for faster
200 relayouting
201*/
202BidiContext::BidiContext(unsigned char l, QChar::Direction e, BidiContext *p, bool o)
mjs08191d52006-03-06 02:53:41 +0000203 : level(l), override(o), m_dir(e)
kociendabb0c24b2001-08-24 14:24:40 +0000204{
205 parent = p;
hyatt275d0702005-11-03 23:53:57 +0000206 if (p) {
kociendabb0c24b2001-08-24 14:24:40 +0000207 p->ref();
mjs08191d52006-03-06 02:53:41 +0000208 m_basicDir = p->basicDir();
kociendabb0c24b2001-08-24 14:24:40 +0000209 } else
mjs08191d52006-03-06 02:53:41 +0000210 m_basicDir = e;
kociendabb0c24b2001-08-24 14:24:40 +0000211 count = 0;
212}
213
214BidiContext::~BidiContext()
215{
hyatt275d0702005-11-03 23:53:57 +0000216 if (parent)
217 parent->deref();
kociendabb0c24b2001-08-24 14:24:40 +0000218}
219
220void BidiContext::ref() const
221{
222 count++;
223}
224
225void BidiContext::deref() const
226{
227 count--;
hyatt275d0702005-11-03 23:53:57 +0000228 if (count <= 0)
229 delete this;
kociendabb0c24b2001-08-24 14:24:40 +0000230}
231
eseidel789896f2005-11-27 22:52:09 +0000232bool operator==(const BidiContext& c1, const BidiContext& c2)
233{
234 if (&c1 == &c2)
235 return true;
mjs08191d52006-03-06 02:53:41 +0000236 if (c1.level != c2.level || c1.override != c2.override || c1.dir() != c2.dir() || c1.basicDir() != c2.basicDir())
eseidel789896f2005-11-27 22:52:09 +0000237 return false;
eseidel6465a0f2006-01-10 13:35:45 +0000238 if (!c1.parent)
239 return !c2.parent;
240 return c2.parent && *c1.parent == *c2.parent;
eseidel789896f2005-11-27 22:52:09 +0000241}
242
243inline bool operator!=(const BidiContext& c1, const BidiContext& c2)
244{
245 return !(c1 == c2);
246}
247
kociendabb0c24b2001-08-24 14:24:40 +0000248// ---------------------------------------------------------------------
249
hyatt275d0702005-11-03 23:53:57 +0000250inline bool operator==(const BidiIterator& it1, const BidiIterator& it2)
darinb70665a2002-04-15 23:43:21 +0000251{
hyatt275d0702005-11-03 23:53:57 +0000252 if (it1.pos != it2.pos)
253 return false;
254 if (it1.obj != it2.obj)
255 return false;
darinb70665a2002-04-15 23:43:21 +0000256 return true;
257}
258
hyatt275d0702005-11-03 23:53:57 +0000259inline bool operator!=(const BidiIterator& it1, const BidiIterator& it2)
darinb70665a2002-04-15 23:43:21 +0000260{
hyatt275d0702005-11-03 23:53:57 +0000261 if (it1.pos != it2.pos)
262 return true;
263 if (it1.obj != it2.obj)
264 return true;
darinb70665a2002-04-15 23:43:21 +0000265 return false;
266}
267
hyatt275d0702005-11-03 23:53:57 +0000268static inline RenderObject* bidiNext(RenderBlock* block, RenderObject* current, BidiState& bidi,
hyattffe78712003-02-11 01:59:29 +0000269 bool skipInlines = true, bool* endOfInline = 0)
mjs6f821c82002-03-22 00:31:57 +0000270{
hyatt275d0702005-11-03 23:53:57 +0000271 RenderObject* next = 0;
hyattffe78712003-02-11 01:59:29 +0000272 bool oldEndOfInline = endOfInline ? *endOfInline : false;
273 if (endOfInline)
274 *endOfInline = false;
275
hyatt275d0702005-11-03 23:53:57 +0000276 while (current) {
hyattffe78712003-02-11 01:59:29 +0000277 if (!oldEndOfInline && !current->isFloating() && !current->isReplaced() && !current->isPositioned()) {
278 next = current->firstChild();
darindde01502005-12-18 22:55:35 +0000279 if (next && bidi.adjustEmbedding && next->isInlineFlow()) {
hyattffe78712003-02-11 01:59:29 +0000280 EUnicodeBidi ub = next->style()->unicodeBidi();
hyatt275d0702005-11-03 23:53:57 +0000281 if (ub != UBNormal) {
darin9d0a6282006-03-01 07:49:33 +0000282 TextDirection dir = next->style()->direction();
283 QChar::Direction d = (ub == Embed
284 ? (dir == RTL ? QChar::DirRLE : QChar::DirLRE)
285 : (dir == RTL ? QChar::DirRLO : QChar::DirLRO));
hyatt275d0702005-11-03 23:53:57 +0000286 embed(d, bidi);
hyattffe78712003-02-11 01:59:29 +0000287 }
288 }
289 }
hyatt275d0702005-11-03 23:53:57 +0000290
hyattffe78712003-02-11 01:59:29 +0000291 if (!next) {
hyatt275d0702005-11-03 23:53:57 +0000292 if (!skipInlines && !oldEndOfInline && current->isInlineFlow()) {
hyattffe78712003-02-11 01:59:29 +0000293 next = current;
294 if (endOfInline)
295 *endOfInline = true;
296 break;
297 }
mjs6f821c82002-03-22 00:31:57 +0000298
hyatt275d0702005-11-03 23:53:57 +0000299 while (current && current != block) {
darindde01502005-12-18 22:55:35 +0000300 if (bidi.adjustEmbedding && current->isInlineFlow() && current->style()->unicodeBidi() != UBNormal)
hyatt275d0702005-11-03 23:53:57 +0000301 embed(QChar::DirPDF, bidi);
302
darindde01502005-12-18 22:55:35 +0000303 next = current->nextSibling();
304 if (next) {
305 if (bidi.adjustEmbedding && next->isInlineFlow()) {
306 EUnicodeBidi ub = next->style()->unicodeBidi();
307 if (ub != UBNormal) {
darin9d0a6282006-03-01 07:49:33 +0000308 TextDirection dir = next->style()->direction();
309 QChar::Direction d = (ub == Embed
310 ? (dir == RTL ? QChar::DirRLE : QChar::DirLRE)
311 : (dir == RTL ? QChar::DirRLO : QChar::DirLRO));
darindde01502005-12-18 22:55:35 +0000312 embed(d, bidi);
313 }
314 }
315 break;
316 }
317
hyattffe78712003-02-11 01:59:29 +0000318 current = current->parent();
hyatt275d0702005-11-03 23:53:57 +0000319 if (!skipInlines && current && current != block && current->isInlineFlow()) {
hyattffe78712003-02-11 01:59:29 +0000320 next = current;
321 if (endOfInline)
322 *endOfInline = true;
323 break;
324 }
325 }
326 }
mjs6f821c82002-03-22 00:31:57 +0000327
hyatt275d0702005-11-03 23:53:57 +0000328 if (!next)
329 break;
hyattffe78712003-02-11 01:59:29 +0000330
331 if (next->isText() || next->isBR() || next->isFloating() || next->isReplaced() || next->isPositioned()
332 || ((!skipInlines || !next->firstChild()) // Always return EMPTY inlines.
333 && next->isInlineFlow()))
mjs6f821c82002-03-22 00:31:57 +0000334 break;
335 current = next;
336 }
337 return next;
338}
339
hyatt275d0702005-11-03 23:53:57 +0000340static RenderObject* bidiFirst(RenderBlock* block, BidiState& bidi, bool skipInlines = true )
mjs6f821c82002-03-22 00:31:57 +0000341{
hyatt275d0702005-11-03 23:53:57 +0000342 if (!block->firstChild())
343 return 0;
344
345 RenderObject* o = block->firstChild();
hyattffe78712003-02-11 01:59:29 +0000346 if (o->isInlineFlow()) {
darindde01502005-12-18 22:55:35 +0000347 if (bidi.adjustEmbedding) {
348 EUnicodeBidi ub = o->style()->unicodeBidi();
349 if (ub != UBNormal) {
darin9d0a6282006-03-01 07:49:33 +0000350 TextDirection dir = o->style()->direction();
351 QChar::Direction d = (ub == Embed
352 ? (dir == RTL ? QChar::DirRLE : QChar::DirLRE)
353 : (dir == RTL ? QChar::DirRLO : QChar::DirLRO));
darindde01502005-12-18 22:55:35 +0000354 embed(d, bidi);
355 }
356 }
hyattffe78712003-02-11 01:59:29 +0000357 if (skipInlines && o->firstChild())
hyatt275d0702005-11-03 23:53:57 +0000358 o = bidiNext(block, o, bidi, skipInlines);
hyattffe78712003-02-11 01:59:29 +0000359 else
360 return o; // Never skip empty inlines.
361 }
hyattc5334f12003-08-08 22:26:08 +0000362
363 if (o && !o->isText() && !o->isBR() && !o->isReplaced() && !o->isFloating() && !o->isPositioned())
hyatt275d0702005-11-03 23:53:57 +0000364 o = bidiNext(block, o, bidi, skipInlines);
mjs6f821c82002-03-22 00:31:57 +0000365 return o;
366}
367
hyatt275d0702005-11-03 23:53:57 +0000368inline void BidiIterator::increment(BidiState& bidi)
kociendabb0c24b2001-08-24 14:24:40 +0000369{
hyatt275d0702005-11-03 23:53:57 +0000370 if (!obj)
371 return;
372 if (obj->isText()) {
kociendabb0c24b2001-08-24 14:24:40 +0000373 pos++;
eseidel789896f2005-11-27 22:52:09 +0000374 if (pos >= static_cast<RenderText *>(obj)->stringLength()) {
hyatt275d0702005-11-03 23:53:57 +0000375 obj = bidiNext(block, obj, bidi);
kociendabb0c24b2001-08-24 14:24:40 +0000376 pos = 0;
377 }
378 } else {
hyatt275d0702005-11-03 23:53:57 +0000379 obj = bidiNext(block, obj, bidi);
kociendabb0c24b2001-08-24 14:24:40 +0000380 pos = 0;
381 }
382}
383
mjs6f821c82002-03-22 00:31:57 +0000384inline bool BidiIterator::atEnd() const
kociendabb0c24b2001-08-24 14:24:40 +0000385{
hyatt275d0702005-11-03 23:53:57 +0000386 return !obj;
kociendabb0c24b2001-08-24 14:24:40 +0000387}
388
hyatt275d0702005-11-03 23:53:57 +0000389const QChar& BidiIterator::current() const
kociendabb0c24b2001-08-24 14:24:40 +0000390{
kociendae40cb942004-10-05 20:05:38 +0000391 static QChar nullCharacter;
hyatt30586b42003-12-02 23:19:11 +0000392
393 if (!obj || !obj->isText())
hyatt275d0702005-11-03 23:53:57 +0000394 return nullCharacter;
hyatt30586b42003-12-02 23:19:11 +0000395
396 RenderText* text = static_cast<RenderText*>(obj);
397 if (!text->text())
kociendae40cb942004-10-05 20:05:38 +0000398 return nullCharacter;
hyatt30586b42003-12-02 23:19:11 +0000399
400 return text->text()[pos];
kociendabb0c24b2001-08-24 14:24:40 +0000401}
402
mjscf6bf062005-10-19 00:03:47 +0000403ALWAYS_INLINE QChar::Direction BidiIterator::direction() const
kociendabb0c24b2001-08-24 14:24:40 +0000404{
hyatt5c18e1a2004-09-28 18:32:47 +0000405 if (!obj)
406 return QChar::DirON;
407 if (obj->isListMarker())
408 return obj->style()->direction() == LTR ? QChar::DirL : QChar::DirR;
409 if (!obj->isText())
410 return QChar::DirON;
411
hyatt59136b72005-07-09 20:19:28 +0000412 RenderText *renderTxt = static_cast<RenderText *>(obj);
413 if (pos >= renderTxt->stringLength())
kociendabb0c24b2001-08-24 14:24:40 +0000414 return QChar::DirON;
415 return renderTxt->text()[pos].direction();
416}
417
kociendabb0c24b2001-08-24 14:24:40 +0000418// -------------------------------------------------------------------------------------------------
419
hyatt2f1e7102003-02-20 01:27:03 +0000420static void addRun(BidiRun* bidiRun)
421{
422 if (!sFirstBidiRun)
423 sFirstBidiRun = sLastBidiRun = bidiRun;
424 else {
425 sLastBidiRun->nextRun = bidiRun;
426 sLastBidiRun = bidiRun;
427 }
428 sBidiRunCount++;
hyatt4b381692003-03-10 21:11:59 +0000429 bidiRun->compact = sBuildingCompactRuns;
hyattfe99c872003-07-31 22:25:29 +0000430
431 // Compute the number of spaces in this run,
432 if (bidiRun->obj && bidiRun->obj->isText()) {
433 RenderText* text = static_cast<RenderText*>(bidiRun->obj);
hyattefebbdd2003-12-02 22:25:31 +0000434 if (text->text()) {
435 for (int i = bidiRun->start; i < bidiRun->stop; i++) {
436 const QChar c = text->text()[i];
harrison208ea792005-07-29 23:42:59 +0000437 if (c == ' ' || c == '\n' || c == '\t')
hyattefebbdd2003-12-02 22:25:31 +0000438 numSpaces++;
439 }
darina3c48282003-10-07 15:49:30 +0000440 }
hyattfe99c872003-07-31 22:25:29 +0000441 }
hyatt2f1e7102003-02-20 01:27:03 +0000442}
443
444static void reverseRuns(int start, int end)
445{
446 if (start >= end)
447 return;
448
449 assert(start >= 0 && end < sBidiRunCount);
450
451 // Get the item before the start of the runs to reverse and put it in
452 // |beforeStart|. |curr| should point to the first run to reverse.
453 BidiRun* curr = sFirstBidiRun;
454 BidiRun* beforeStart = 0;
455 int i = 0;
456 while (i < start) {
457 i++;
458 beforeStart = curr;
459 curr = curr->nextRun;
460 }
461
hyatt793fbbc2003-02-26 02:38:01 +0000462 BidiRun* startRun = curr;
hyatt2f1e7102003-02-20 01:27:03 +0000463 while (i < end) {
hyatt793fbbc2003-02-26 02:38:01 +0000464 i++;
465 curr = curr->nextRun;
466 }
467 BidiRun* endRun = curr;
468 BidiRun* afterEnd = curr->nextRun;
469
470 i = start;
471 curr = startRun;
472 BidiRun* newNext = afterEnd;
473 while (i <= end) {
hyatt2f1e7102003-02-20 01:27:03 +0000474 // Do the reversal.
475 BidiRun* next = curr->nextRun;
hyatt793fbbc2003-02-26 02:38:01 +0000476 curr->nextRun = newNext;
477 newNext = curr;
hyatt2f1e7102003-02-20 01:27:03 +0000478 curr = next;
479 i++;
480 }
481
hyatt2f1e7102003-02-20 01:27:03 +0000482 // Now hook up beforeStart and afterEnd to the newStart and newEnd.
483 if (beforeStart)
hyatt793fbbc2003-02-26 02:38:01 +0000484 beforeStart->nextRun = endRun;
hyatt2f1e7102003-02-20 01:27:03 +0000485 else
hyatt793fbbc2003-02-26 02:38:01 +0000486 sFirstBidiRun = endRun;
hyatt2f1e7102003-02-20 01:27:03 +0000487
hyatt793fbbc2003-02-26 02:38:01 +0000488 startRun->nextRun = afterEnd;
hyatt2f1e7102003-02-20 01:27:03 +0000489 if (!afterEnd)
hyatt793fbbc2003-02-26 02:38:01 +0000490 sLastBidiRun = startRun;
hyatt2f1e7102003-02-20 01:27:03 +0000491}
492
darinb9481ed2006-03-20 02:57:59 +0000493static void chopMidpointsAt(RenderObject* obj, unsigned pos)
hyatt78b85132004-03-29 20:07:45 +0000494{
hyatt275d0702005-11-03 23:53:57 +0000495 if (!sNumMidpoints)
496 return;
hyatt78b85132004-03-29 20:07:45 +0000497 BidiIterator* midpoints = smidpoints->data();
darinb9481ed2006-03-20 02:57:59 +0000498 for (unsigned i = 0; i < sNumMidpoints; i++) {
hyatt78b85132004-03-29 20:07:45 +0000499 const BidiIterator& point = midpoints[i];
500 if (point.obj == obj && point.pos == pos) {
501 sNumMidpoints = i;
502 break;
503 }
504 }
505}
506
hyatt275d0702005-11-03 23:53:57 +0000507static void checkMidpoints(BidiIterator& lBreak, BidiState& bidi)
hyattfe99c872003-07-31 22:25:29 +0000508{
509 // Check to see if our last midpoint is a start point beyond the line break. If so,
hyattdca76e92005-11-02 08:52:50 +0000510 // shave it off the list, and shave off a trailing space if the previous end point doesn't
511 // preserve whitespace.
hyattfe99c872003-07-31 22:25:29 +0000512 if (lBreak.obj && sNumMidpoints && sNumMidpoints%2 == 0) {
513 BidiIterator* midpoints = smidpoints->data();
514 BidiIterator& endpoint = midpoints[sNumMidpoints-2];
515 const BidiIterator& startpoint = midpoints[sNumMidpoints-1];
516 BidiIterator currpoint = endpoint;
517 while (!currpoint.atEnd() && currpoint != startpoint && currpoint != lBreak)
hyatt275d0702005-11-03 23:53:57 +0000518 currpoint.increment(bidi);
hyattfe99c872003-07-31 22:25:29 +0000519 if (currpoint == lBreak) {
520 // We hit the line break before the start point. Shave off the start point.
521 sNumMidpoints--;
hyattdca76e92005-11-02 08:52:50 +0000522 if (endpoint.obj->style()->collapseWhiteSpace()) {
hyattee1bcae2004-03-29 21:36:04 +0000523 if (endpoint.obj->isText()) {
524 // Don't shave a character off the endpoint if it was from a soft hyphen.
525 RenderText* textObj = static_cast<RenderText*>(endpoint.obj);
bdakin9151ba52005-10-24 22:51:06 +0000526 if (endpoint.pos+1 < textObj->length()) {
527 if (textObj->text()[endpoint.pos+1].unicode() == SOFT_HYPHEN)
528 return;
529 } else if (startpoint.obj->isText()) {
530 RenderText *startText = static_cast<RenderText*>(startpoint.obj);
531 if (startText->length() > 0 && startText->text()[0].unicode() == SOFT_HYPHEN)
532 return;
533 }
hyattee1bcae2004-03-29 21:36:04 +0000534 }
hyattfe99c872003-07-31 22:25:29 +0000535 endpoint.pos--;
hyattee1bcae2004-03-29 21:36:04 +0000536 }
hyattfe99c872003-07-31 22:25:29 +0000537 }
538 }
539}
540
hyatt85586af2003-02-19 23:22:42 +0000541static void addMidpoint(const BidiIterator& midpoint)
542{
543 if (!smidpoints)
544 return;
545
546 if (smidpoints->size() <= sNumMidpoints)
547 smidpoints->resize(sNumMidpoints+10);
548
549 BidiIterator* midpoints = smidpoints->data();
550 midpoints[sNumMidpoints++] = midpoint;
551}
552
mjsfe301d72003-10-02 18:46:18 +0000553static void appendRunsForObject(int start, int end, RenderObject* obj, BidiState &bidi)
hyatt33f8d492002-11-12 21:44:52 +0000554{
hyatt98ee7e42003-05-14 01:39:15 +0000555 if (start > end || obj->isFloating() ||
hyatt853cd7d2004-11-05 02:59:48 +0000556 (obj->isPositioned() && !obj->hasStaticX() && !obj->hasStaticY() && !obj->container()->isInlineFlow()))
hyatteb003b82002-11-15 22:35:10 +0000557 return;
hyatt85586af2003-02-19 23:22:42 +0000558
559 bool haveNextMidpoint = (smidpoints && sCurrMidpoint < sNumMidpoints);
mjsfe301d72003-10-02 18:46:18 +0000560 BidiIterator nextMidpoint;
hyatt85586af2003-02-19 23:22:42 +0000561 if (haveNextMidpoint)
562 nextMidpoint = smidpoints->at(sCurrMidpoint);
hyatt33f8d492002-11-12 21:44:52 +0000563 if (betweenMidpoints) {
hyatt85586af2003-02-19 23:22:42 +0000564 if (!(haveNextMidpoint && nextMidpoint.obj == obj))
hyatt33f8d492002-11-12 21:44:52 +0000565 return;
566 // This is a new start point. Stop ignoring objects and
567 // adjust our start.
568 betweenMidpoints = false;
hyatt85586af2003-02-19 23:22:42 +0000569 start = nextMidpoint.pos;
570 sCurrMidpoint++;
hyatt33f8d492002-11-12 21:44:52 +0000571 if (start < end)
mjsfe301d72003-10-02 18:46:18 +0000572 return appendRunsForObject(start, end, obj, bidi);
hyatt33f8d492002-11-12 21:44:52 +0000573 }
574 else {
hyatt85586af2003-02-19 23:22:42 +0000575 if (!smidpoints || !haveNextMidpoint || (obj != nextMidpoint.obj)) {
eseidel789896f2005-11-27 22:52:09 +0000576 addRun(new (obj->renderArena()) BidiRun(start, end, obj, bidi.context.get(), bidi.dir));
hyatt33f8d492002-11-12 21:44:52 +0000577 return;
578 }
579
hyatt78b85132004-03-29 20:07:45 +0000580 // An end midpoint has been encountered within our object. We
hyatt33f8d492002-11-12 21:44:52 +0000581 // need to go ahead and append a run with our endpoint.
hyatt85586af2003-02-19 23:22:42 +0000582 if (int(nextMidpoint.pos+1) <= end) {
hyatt26179432002-11-17 01:57:27 +0000583 betweenMidpoints = true;
hyatt85586af2003-02-19 23:22:42 +0000584 sCurrMidpoint++;
hyattc64f9fc2003-03-14 01:25:59 +0000585 if (nextMidpoint.pos != UINT_MAX) { // UINT_MAX means stop at the object and don't include any of it.
darinef0c09f2005-10-09 01:46:17 +0000586 if (int(nextMidpoint.pos+1) > start)
587 addRun(new (obj->renderArena())
eseidel789896f2005-11-27 22:52:09 +0000588 BidiRun(start, nextMidpoint.pos+1, obj, bidi.context.get(), bidi.dir));
mjsfe301d72003-10-02 18:46:18 +0000589 return appendRunsForObject(nextMidpoint.pos+1, end, obj, bidi);
hyattc64f9fc2003-03-14 01:25:59 +0000590 }
hyatt26179432002-11-17 01:57:27 +0000591 }
592 else
eseidel789896f2005-11-27 22:52:09 +0000593 addRun(new (obj->renderArena()) BidiRun(start, end, obj, bidi.context.get(), bidi.dir));
hyatt33f8d492002-11-12 21:44:52 +0000594 }
595}
596
hyatt275d0702005-11-03 23:53:57 +0000597static void appendRun(BidiState &bidi)
kociendabb0c24b2001-08-24 14:24:40 +0000598{
ggarenef026b22005-07-06 00:21:53 +0000599 if (emptyRun || !bidi.eor.obj)
600 return;
kociendabb0c24b2001-08-24 14:24:40 +0000601#if BIDI_DEBUG > 1
602 kdDebug(6041) << "appendRun: dir="<<(int)dir<<endl;
603#endif
darinf028f812002-06-10 20:08:04 +0000604
hyatt275d0702005-11-03 23:53:57 +0000605 bool b = bidi.adjustEmbedding;
606 bidi.adjustEmbedding = false;
kociendabb0c24b2001-08-24 14:24:40 +0000607
mjsfe301d72003-10-02 18:46:18 +0000608 int start = bidi.sor.pos;
609 RenderObject *obj = bidi.sor.obj;
eseidel789896f2005-11-27 22:52:09 +0000610 while (obj && obj != bidi.eor.obj && obj != bidi.endOfLine.obj) {
mjsfe301d72003-10-02 18:46:18 +0000611 appendRunsForObject(start, obj->length(), obj, bidi);
kociendabb0c24b2001-08-24 14:24:40 +0000612 start = 0;
hyatt275d0702005-11-03 23:53:57 +0000613 obj = bidiNext(bidi.sor.block, obj, bidi);
kociendabb0c24b2001-08-24 14:24:40 +0000614 }
justing5b0e0422005-08-01 03:20:49 +0000615 if (obj) {
eseidel789896f2005-11-27 22:52:09 +0000616 unsigned pos = obj == bidi.eor.obj ? bidi.eor.pos : UINT_MAX;
617 if (obj == bidi.endOfLine.obj && bidi.endOfLine.pos <= pos) {
618 bidi.reachedEndOfLine = true;
619 pos = bidi.endOfLine.pos;
620 }
justing5b0e0422005-08-01 03:20:49 +0000621 // It's OK to add runs for zero-length RenderObjects, just don't make the run larger than it should be
eseidel789896f2005-11-27 22:52:09 +0000622 int end = obj->length() ? pos+1 : 0;
justing5b0e0422005-08-01 03:20:49 +0000623 appendRunsForObject(start, end, obj, bidi);
624 }
hyatt33f8d492002-11-12 21:44:52 +0000625
hyatt275d0702005-11-03 23:53:57 +0000626 bidi.eor.increment(bidi);
mjsfe301d72003-10-02 18:46:18 +0000627 bidi.sor = bidi.eor;
hyatt275d0702005-11-03 23:53:57 +0000628 bidi.dir = QChar::DirON;
mjsfe301d72003-10-02 18:46:18 +0000629 bidi.status.eor = QChar::DirON;
hyatt275d0702005-11-03 23:53:57 +0000630 bidi.adjustEmbedding = b;
mjs6f821c82002-03-22 00:31:57 +0000631}
632
hyatt275d0702005-11-03 23:53:57 +0000633static void embed(QChar::Direction d, BidiState& bidi)
mjs6f821c82002-03-22 00:31:57 +0000634{
hyatt275d0702005-11-03 23:53:57 +0000635 bool b = bidi.adjustEmbedding;
636 bidi.adjustEmbedding = false;
darinef0c09f2005-10-09 01:46:17 +0000637 if (d == QChar::DirPDF) {
darin54008922006-01-13 16:39:05 +0000638 BidiContext *c = bidi.context->parent;
639 if (c) {
640 if (!emptyRun && bidi.eor != bidi.last) {
hyatt275d0702005-11-03 23:53:57 +0000641 assert(bidi.status.eor != QChar::DirON);
642 // bidi.sor ... bidi.eor ... bidi.last eor; need to append the bidi.sor-bidi.eor run or extend it through bidi.last
643 assert(bidi.status.last == QChar::DirES || bidi.status.last == QChar::DirET || bidi.status.last == QChar::DirCS || bidi.status.last == QChar::DirBN || bidi.status.last == QChar::DirB || bidi.status.last == QChar::DirS || bidi.status.last == QChar::DirWS || bidi.status.last == QChar::DirON);
644 if (bidi.dir == QChar::DirON)
mjs08191d52006-03-06 02:53:41 +0000645 bidi.dir = bidi.context->dir();
646 if (bidi.context->dir() == QChar::DirL) {
hyatt275d0702005-11-03 23:53:57 +0000647 // bidi.sor ... bidi.eor ... bidi.last L
648 if (bidi.status.eor == QChar::DirEN) {
649 if (bidi.status.lastStrong != QChar::DirL) {
650 bidi.dir = QChar::DirEN;
651 appendRun(bidi);
652 }
653 } else if (bidi.status.eor == QChar::DirAN) {
654 bidi.dir = QChar::DirAN;
darinef0c09f2005-10-09 01:46:17 +0000655 appendRun(bidi);
hyatt275d0702005-11-03 23:53:57 +0000656 } else if (bidi.status.eor != QChar::DirL)
657 appendRun(bidi);
658 } else if (bidi.status.eor != QChar::DirR && bidi.status.eor != QChar::DirAL)
darinef0c09f2005-10-09 01:46:17 +0000659 appendRun(bidi);
hyatt275d0702005-11-03 23:53:57 +0000660 bidi.eor = bidi.last;
661 }
darin54008922006-01-13 16:39:05 +0000662 appendRun(bidi);
663 emptyRun = true;
hyatt275d0702005-11-03 23:53:57 +0000664 // sor for the new run is determined by the higher level (rule X10)
mjs08191d52006-03-06 02:53:41 +0000665 bidi.status.last = bidi.context->dir();
666 bidi.status.lastStrong = bidi.context->dir();
darin54008922006-01-13 16:39:05 +0000667 bidi.context = c;
mjs08191d52006-03-06 02:53:41 +0000668 bidi.status.eor = bidi.context->dir();
hyatt275d0702005-11-03 23:53:57 +0000669 bidi.eor.obj = 0;
670 }
mjs6f821c82002-03-22 00:31:57 +0000671 } else {
darin54008922006-01-13 16:39:05 +0000672 QChar::Direction runDir;
673 if (d == QChar::DirRLE || d == QChar::DirRLO)
674 runDir = QChar::DirR;
675 else
676 runDir = QChar::DirL;
677 bool override = d == QChar::DirLRO || d == QChar::DirRLO;
mjs6f821c82002-03-22 00:31:57 +0000678
darin54008922006-01-13 16:39:05 +0000679 unsigned char level = bidi.context->level;
680 if (runDir == QChar::DirR) {
681 if (level%2) // we have an odd level
682 level += 2;
683 else
684 level++;
685 } else {
686 if (level%2) // we have an odd level
687 level++;
688 else
689 level += 2;
690 }
mjs6f821c82002-03-22 00:31:57 +0000691
darin54008922006-01-13 16:39:05 +0000692 if (level < 61) {
693 if (!emptyRun && bidi.eor != bidi.last) {
hyatt275d0702005-11-03 23:53:57 +0000694 assert(bidi.status.eor != QChar::DirON);
695 // bidi.sor ... bidi.eor ... bidi.last eor; need to append the bidi.sor-bidi.eor run or extend it through bidi.last
696 assert(bidi.status.last == QChar::DirES || bidi.status.last == QChar::DirET || bidi.status.last == QChar::DirCS || bidi.status.last == QChar::DirBN || bidi.status.last == QChar::DirB || bidi.status.last == QChar::DirS || bidi.status.last == QChar::DirWS || bidi.status.last == QChar::DirON);
697 if (bidi.dir == QChar::DirON)
698 bidi.dir = runDir;
699 if (runDir == QChar::DirL) {
700 // bidi.sor ... bidi.eor ... bidi.last L
701 if (bidi.status.eor == QChar::DirEN) {
702 if (bidi.status.lastStrong != QChar::DirL) {
703 bidi.dir = QChar::DirEN;
704 appendRun(bidi);
mjs08191d52006-03-06 02:53:41 +0000705 if (bidi.context->dir() != QChar::DirL)
hyatt275d0702005-11-03 23:53:57 +0000706 bidi.dir = QChar::DirR;
707 }
708 } else if (bidi.status.eor == QChar::DirAN) {
709 bidi.dir = QChar::DirAN;
darinef0c09f2005-10-09 01:46:17 +0000710 appendRun(bidi);
mjs08191d52006-03-06 02:53:41 +0000711 if (bidi.context->dir() != QChar::DirL) {
hyatt275d0702005-11-03 23:53:57 +0000712 bidi.eor = bidi.last;
713 bidi.dir = QChar::DirR;
714 appendRun(bidi);
715 }
716 } else if (bidi.status.eor != QChar::DirL) {
mjs08191d52006-03-06 02:53:41 +0000717 if (bidi.context->dir() == QChar::DirL || bidi.status.lastStrong == QChar::DirL)
hyatt275d0702005-11-03 23:53:57 +0000718 appendRun(bidi);
719 else
720 bidi.dir = QChar::DirR;
darinef0c09f2005-10-09 01:46:17 +0000721 }
hyatt275d0702005-11-03 23:53:57 +0000722 } else if (bidi.status.eor != QChar::DirR && bidi.status.eor != QChar::DirAL) {
723 // bidi.sor ... bidi.eor ... bidi.last R; bidi.eor=L/EN/AN; EN,AN behave like R (rule N1)
mjs08191d52006-03-06 02:53:41 +0000724 if (bidi.context->dir() == QChar::DirR || bidi.status.lastStrong == QChar::DirR || bidi.status.lastStrong == QChar::DirAL)
darinef0c09f2005-10-09 01:46:17 +0000725 appendRun(bidi);
726 else
hyatt275d0702005-11-03 23:53:57 +0000727 bidi.dir = QChar::DirL;
darinef0c09f2005-10-09 01:46:17 +0000728 }
hyatt275d0702005-11-03 23:53:57 +0000729 bidi.eor = bidi.last;
hyatt2f1e7102003-02-20 01:27:03 +0000730 }
hyatt275d0702005-11-03 23:53:57 +0000731 appendRun(bidi);
732 emptyRun = true;
eseidel789896f2005-11-27 22:52:09 +0000733 bidi.context = new BidiContext(level, runDir, bidi.context.get(), override);
hyatt275d0702005-11-03 23:53:57 +0000734 bidi.status.last = runDir;
735 bidi.status.lastStrong = runDir;
736 bidi.status.eor = runDir;
737 bidi.eor.obj = 0;
darinef0c09f2005-10-09 01:46:17 +0000738 }
mjs6f821c82002-03-22 00:31:57 +0000739 }
hyatt275d0702005-11-03 23:53:57 +0000740 bidi.adjustEmbedding = b;
kociendabb0c24b2001-08-24 14:24:40 +0000741}
742
hyattffe78712003-02-11 01:59:29 +0000743InlineFlowBox* RenderBlock::createLineBoxes(RenderObject* obj)
744{
745 // See if we have an unconstructed line box for this object that is also
746 // the last item on the line.
747 KHTMLAssert(obj->isInlineFlow() || obj == this);
748 RenderFlow* flow = static_cast<RenderFlow*>(obj);
749
750 // Get the last box we made for this render object.
751 InlineFlowBox* box = flow->lastLineBox();
752
753 // If this box is constructed then it is from a previous line, and we need
754 // to make a new box for our line. If this box is unconstructed but it has
755 // something following it on the line, then we know we have to make a new box
756 // as well. In this situation our inline has actually been split in two on
757 // the same line (this can happen with very fancy language mixtures).
758 if (!box || box->isConstructed() || box->nextOnLine()) {
759 // We need to make a new box for this render object. Once
760 // made, we need to place it at the end of the current line.
hyatt450813d2003-07-25 20:22:34 +0000761 InlineBox* newBox = obj->createInlineBox(false, obj == this);
hyattffe78712003-02-11 01:59:29 +0000762 KHTMLAssert(newBox->isInlineFlowBox());
763 box = static_cast<InlineFlowBox*>(newBox);
764 box->setFirstLineStyleBit(m_firstLine);
765
766 // We have a new box. Append it to the inline box we get by constructing our
767 // parent. If we have hit the block itself, then |box| represents the root
768 // inline box for the line, and it doesn't have to be appended to any parent
769 // inline.
770 if (obj != this) {
771 InlineFlowBox* parentBox = createLineBoxes(obj->parent());
772 parentBox->addToLine(box);
773 }
774 }
775
776 return box;
777}
778
hyatt275d0702005-11-03 23:53:57 +0000779RootInlineBox* RenderBlock::constructLine(const BidiIterator& start, const BidiIterator& end)
hyattffe78712003-02-11 01:59:29 +0000780{
hyatt2f1e7102003-02-20 01:27:03 +0000781 if (!sFirstBidiRun)
hyattffe78712003-02-11 01:59:29 +0000782 return 0; // We had no runs. Don't make a root inline box at all. The line is empty.
783
784 InlineFlowBox* parentBox = 0;
hyatt2f1e7102003-02-20 01:27:03 +0000785 for (BidiRun* r = sFirstBidiRun; r; r = r->nextRun) {
hyattffe78712003-02-11 01:59:29 +0000786 // Create a box for our object.
harrison8d773e72006-01-29 15:06:17 +0000787 bool isOnlyRun = (sBidiRunCount == 1);
788 if (sBidiRunCount == 2 && !r->obj->isListMarker())
789 isOnlyRun = ((style()->direction() == RTL) ? sLastBidiRun : sFirstBidiRun)->obj->isListMarker();
790 r->box = r->obj->createInlineBox(r->obj->isPositioned(), false, isOnlyRun);
hyatt0c3a9862004-02-23 21:26:26 +0000791 if (r->box) {
792 // If we have no parent box yet, or if the run is not simply a sibling,
793 // then we need to construct inline boxes as necessary to properly enclose the
794 // run's inline box.
hyatt275d0702005-11-03 23:53:57 +0000795 if (!parentBox || parentBox->object() != r->obj->parent())
hyatt0c3a9862004-02-23 21:26:26 +0000796 // Create new inline boxes all the way back to the appropriate insertion point.
797 parentBox = createLineBoxes(r->obj->parent());
hyattffe78712003-02-11 01:59:29 +0000798
hyatt0c3a9862004-02-23 21:26:26 +0000799 // Append the inline box to this line.
800 parentBox->addToLine(r->box);
darin06dcb9c2005-08-15 04:31:09 +0000801
802 if (r->box->isInlineTextBox()) {
803 InlineTextBox *text = static_cast<InlineTextBox*>(r->box);
804 text->setStart(r->start);
805 text->setLen(r->stop-r->start);
806 }
hyatt0c3a9862004-02-23 21:26:26 +0000807 }
hyattffe78712003-02-11 01:59:29 +0000808 }
809
810 // We should have a root inline box. It should be unconstructed and
811 // be the last continuation of our line list.
hyatt275d0702005-11-03 23:53:57 +0000812 assert(lastLineBox() && !lastLineBox()->isConstructed());
hyattffe78712003-02-11 01:59:29 +0000813
814 // Set bits on our inline flow boxes that indicate which sides should
815 // paint borders/margins/padding. This knowledge will ultimately be used when
816 // we determine the horizontal positions and widths of all the inline boxes on
817 // the line.
818 RenderObject* endObject = 0;
819 bool lastLine = !end.obj;
820 if (end.obj && end.pos == 0)
821 endObject = end.obj;
822 lastLineBox()->determineSpacingForFlowBoxes(lastLine, endObject);
823
824 // Now mark the line boxes as being constructed.
825 lastLineBox()->setConstructed();
826
827 // Return the last line.
hyatt0c3a9862004-02-23 21:26:26 +0000828 return lastRootBox();
hyattffe78712003-02-11 01:59:29 +0000829}
830
harrison208ea792005-07-29 23:42:59 +0000831// usage: tw - (xpos % tw);
832int RenderBlock::tabWidth(bool isWhitespacePre)
833{
834 if (!isWhitespacePre)
835 return 0;
836
837 if (!m_tabWidth) {
hyatt275d0702005-11-03 23:53:57 +0000838 QChar spaceChar(' ');
hyatt3e99d1c2006-02-24 21:13:08 +0000839 const Font& font = style()->font();
hyattef0a9392006-03-05 05:51:37 +0000840 int spaceWidth = font.width(&spaceChar, 1);
harrison208ea792005-07-29 23:42:59 +0000841 m_tabWidth = spaceWidth * 8;
842 assert(m_tabWidth != 0);
843 }
844
845 return m_tabWidth;
846}
847
hyatt275d0702005-11-03 23:53:57 +0000848void RenderBlock::computeHorizontalPositionsForLine(RootInlineBox* lineBox, BidiState& bidi)
hyattffe78712003-02-11 01:59:29 +0000849{
850 // First determine our total width.
kociendae40cb942004-10-05 20:05:38 +0000851 int availableWidth = lineWidth(m_height);
hyattffe78712003-02-11 01:59:29 +0000852 int totWidth = lineBox->getFlowSpacingWidth();
hyatt2f1e7102003-02-20 01:27:03 +0000853 BidiRun* r = 0;
darin06dcb9c2005-08-15 04:31:09 +0000854 bool needsWordSpacing = false;
hyatt2f1e7102003-02-20 01:27:03 +0000855 for (r = sFirstBidiRun; r; r = r->nextRun) {
hyatt0c05e102006-04-14 08:15:00 +0000856 if (!r->box || r->obj->isPositioned() || r->box->isLineBreak())
hyatt98ee7e42003-05-14 01:39:15 +0000857 continue; // Positioned objects are only participating to figure out their
858 // correct static x position. They have no effect on the width.
hyatt0c05e102006-04-14 08:15:00 +0000859 // Similarly, line break boxes have no effect on the width.
kociendae40cb942004-10-05 20:05:38 +0000860 if (r->obj->isText()) {
darin06dcb9c2005-08-15 04:31:09 +0000861 RenderText *rt = static_cast<RenderText *>(r->obj);
862 int textWidth = rt->width(r->start, r->stop-r->start, totWidth, m_firstLine);
863 int effectiveWidth = textWidth;
864 int rtLength = rt->length();
865 if (rtLength != 0) {
866 if (r->start == 0 && needsWordSpacing && rt->text()[r->start].isSpace())
hyattecad67c2006-03-01 06:16:38 +0000867 effectiveWidth += rt->font(m_firstLine)->wordSpacing();
darin06dcb9c2005-08-15 04:31:09 +0000868 needsWordSpacing = !rt->text()[r->stop-1].isSpace() && r->stop == rtLength;
869 }
kociendae40cb942004-10-05 20:05:38 +0000870 if (!r->compact) {
871 RenderStyle *style = r->obj->style();
hyattdca76e92005-11-02 08:52:50 +0000872 if (style->autoWrap() && style->breakOnlyAfterWhiteSpace()) {
kociendae40cb942004-10-05 20:05:38 +0000873 // shrink the box as needed to keep the line from overflowing the available width
darin7bd70952006-04-13 07:07:34 +0000874 textWidth = min(effectiveWidth, availableWidth - totWidth);
kociendae40cb942004-10-05 20:05:38 +0000875 }
kociendae40cb942004-10-05 20:05:38 +0000876 }
kociendaee701982004-10-05 23:06:58 +0000877 r->box->setWidth(textWidth);
darin06dcb9c2005-08-15 04:31:09 +0000878 } else if (!r->obj->isInlineFlow()) {
hyattffe78712003-02-11 01:59:29 +0000879 r->obj->calcWidth();
880 r->box->setWidth(r->obj->width());
hyatt4b381692003-03-10 21:11:59 +0000881 if (!r->compact)
darin06dcb9c2005-08-15 04:31:09 +0000882 totWidth += r->obj->marginLeft() + r->obj->marginRight();
hyattffe78712003-02-11 01:59:29 +0000883 }
hyatt4b381692003-03-10 21:11:59 +0000884
885 // Compacts don't contribute to the width of the line, since they are placed in the margin.
886 if (!r->compact)
887 totWidth += r->box->width();
hyattffe78712003-02-11 01:59:29 +0000888 }
889
890 // Armed with the total width of the line (without justification),
891 // we now examine our text-align property in order to determine where to position the
892 // objects horizontally. The total width of the line can be increased if we end up
893 // justifying text.
894 int x = leftOffset(m_height);
hyattffe78712003-02-11 01:59:29 +0000895 switch(style()->textAlign()) {
896 case LEFT:
hyatt47c1d4d2003-07-24 22:07:45 +0000897 case KHTML_LEFT:
hyatt959a54e2004-09-27 17:52:18 +0000898 // The direction of the block should determine what happens with wide lines. In
899 // particular with RTL blocks, wide lines should still spill out to the left.
900 if (style()->direction() == RTL && totWidth > availableWidth)
901 x -= (totWidth - availableWidth);
hyattffe78712003-02-11 01:59:29 +0000902 numSpaces = 0;
903 break;
904 case JUSTIFY:
hyatt0c3a9862004-02-23 21:26:26 +0000905 if (numSpaces != 0 && !bidi.current.atEnd() && !lineBox->endsWithBreak())
hyattffe78712003-02-11 01:59:29 +0000906 break;
907 // fall through
908 case TAAUTO:
909 numSpaces = 0;
910 // for right to left fall through to right aligned
mjs08191d52006-03-06 02:53:41 +0000911 if (bidi.context->basicDir() == QChar::DirL)
hyattffe78712003-02-11 01:59:29 +0000912 break;
913 case RIGHT:
hyatt47c1d4d2003-07-24 22:07:45 +0000914 case KHTML_RIGHT:
hyatt959a54e2004-09-27 17:52:18 +0000915 // Wide lines spill out of the block based off direction.
916 // So even if text-align is right, if direction is LTR, wide lines should overflow out of the right
917 // side of the block.
918 if (style()->direction() == RTL || totWidth < availableWidth)
919 x += availableWidth - totWidth;
hyattffe78712003-02-11 01:59:29 +0000920 numSpaces = 0;
921 break;
922 case CENTER:
hyatt47c1d4d2003-07-24 22:07:45 +0000923 case KHTML_CENTER:
hyattffe78712003-02-11 01:59:29 +0000924 int xd = (availableWidth - totWidth)/2;
eseidel789896f2005-11-27 22:52:09 +0000925 x += xd > 0 ? xd : 0;
hyattffe78712003-02-11 01:59:29 +0000926 numSpaces = 0;
927 break;
928 }
929
hyattacbb0d42003-04-25 01:32:49 +0000930 if (numSpaces > 0) {
931 for (r = sFirstBidiRun; r; r = r->nextRun) {
hyatt0c3a9862004-02-23 21:26:26 +0000932 if (!r->box) continue;
933
hyattacbb0d42003-04-25 01:32:49 +0000934 int spaceAdd = 0;
935 if (numSpaces > 0 && r->obj->isText() && !r->compact) {
936 // get the number of spaces in the run
937 int spaces = 0;
darina3c48282003-10-07 15:49:30 +0000938 for ( int i = r->start; i < r->stop; i++ ) {
939 const QChar c = static_cast<RenderText *>(r->obj)->text()[i];
harrison208ea792005-07-29 23:42:59 +0000940 if (c == ' ' || c == '\n' || c == '\t')
hyattacbb0d42003-04-25 01:32:49 +0000941 spaces++;
darina3c48282003-10-07 15:49:30 +0000942 }
hyatt870bdda2003-05-21 23:37:33 +0000943
hyattacbb0d42003-04-25 01:32:49 +0000944 KHTMLAssert(spaces <= numSpaces);
hyatt870bdda2003-05-21 23:37:33 +0000945
hyattdca76e92005-11-02 08:52:50 +0000946 // Only justify text if whitespace is collapsed.
947 if (r->obj->style()->collapseWhiteSpace()) {
hyatt870bdda2003-05-21 23:37:33 +0000948 spaceAdd = (availableWidth - totWidth)*spaces/numSpaces;
kocienda01bcc352003-09-25 16:51:11 +0000949 static_cast<InlineTextBox*>(r->box)->setSpaceAdd(spaceAdd);
hyatt870bdda2003-05-21 23:37:33 +0000950 totWidth += spaceAdd;
951 }
hyattacbb0d42003-04-25 01:32:49 +0000952 numSpaces -= spaces;
hyattacbb0d42003-04-25 01:32:49 +0000953 }
hyattffe78712003-02-11 01:59:29 +0000954 }
hyattffe78712003-02-11 01:59:29 +0000955 }
hyattacbb0d42003-04-25 01:32:49 +0000956
hyattffe78712003-02-11 01:59:29 +0000957 // The widths of all runs are now known. We can now place every inline box (and
958 // compute accurate widths for the inline flow boxes).
hyatt1f14a612004-12-07 00:34:02 +0000959 int leftPosition = x;
960 int rightPosition = x;
darin06dcb9c2005-08-15 04:31:09 +0000961 needsWordSpacing = false;
962 lineBox->placeBoxesHorizontally(x, leftPosition, rightPosition, needsWordSpacing);
hyatt1f14a612004-12-07 00:34:02 +0000963 lineBox->setHorizontalOverflowPositions(leftPosition, rightPosition);
hyattffe78712003-02-11 01:59:29 +0000964}
965
hyatt0c3a9862004-02-23 21:26:26 +0000966void RenderBlock::computeVerticalPositionsForLine(RootInlineBox* lineBox)
hyattffe78712003-02-11 01:59:29 +0000967{
968 lineBox->verticallyAlignBoxes(m_height);
hyatt0c3a9862004-02-23 21:26:26 +0000969 lineBox->setBlockHeight(m_height);
hyattffe78712003-02-11 01:59:29 +0000970
hyatt35a85e52003-02-14 19:43:07 +0000971 // See if the line spilled out. If so set overflow height accordingly.
972 int bottomOfLine = lineBox->bottomOverflow();
973 if (bottomOfLine > m_height && bottomOfLine > m_overflowHeight)
974 m_overflowHeight = bottomOfLine;
975
hyattffe78712003-02-11 01:59:29 +0000976 // Now make sure we place replaced render objects correctly.
hyatt98ee7e42003-05-14 01:39:15 +0000977 for (BidiRun* r = sFirstBidiRun; r; r = r->nextRun) {
eseidel789896f2005-11-27 22:52:09 +0000978 if (!r->box)
979 continue; // Skip runs with no line boxes.
hyatt0c3a9862004-02-23 21:26:26 +0000980
hyatt98ee7e42003-05-14 01:39:15 +0000981 // Align positioned boxes with the top of the line box. This is
982 // a reasonable approximation of an appropriate y position.
983 if (r->obj->isPositioned())
984 r->box->setYPos(m_height);
985
986 // Position is used to properly position both replaced elements and
987 // to update the static normal flow x/y of positioned elements.
darin0ce8a6b2005-09-24 11:45:21 +0000988 r->obj->position(r->box, r->start, r->stop - r->start, r->level%2, r->override);
hyatt98ee7e42003-05-14 01:39:15 +0000989 }
hyattffe78712003-02-11 01:59:29 +0000990}
kociendabb0c24b2001-08-24 14:24:40 +0000991
992// collects one line of the paragraph and transforms it to visual order
hyatt275d0702005-11-03 23:53:57 +0000993void RenderBlock::bidiReorderLine(const BidiIterator& start, const BidiIterator& end, BidiState& bidi)
kociendabb0c24b2001-08-24 14:24:40 +0000994{
hyatt275d0702005-11-03 23:53:57 +0000995 if (start == end) {
996 if (start.current() == '\n')
997 m_height += lineHeight(m_firstLine, true);
hyatt33f8d492002-11-12 21:44:52 +0000998 return;
darinb70665a2002-04-15 23:43:21 +0000999 }
hyattffe78712003-02-11 01:59:29 +00001000
hyatt2f1e7102003-02-20 01:27:03 +00001001 sFirstBidiRun = 0;
1002 sLastBidiRun = 0;
1003 sBidiRunCount = 0;
rjw834c8062005-02-25 23:41:39 +00001004
eseidel789896f2005-11-27 22:52:09 +00001005 assert(bidi.dir == QChar::DirON);
1006
mjs6f821c82002-03-22 00:31:57 +00001007 emptyRun = true;
eseidel789896f2005-11-27 22:52:09 +00001008
ggarenef026b22005-07-06 00:21:53 +00001009 bidi.eor.obj = 0;
darinf028f812002-06-10 20:08:04 +00001010
darinb70665a2002-04-15 23:43:21 +00001011 numSpaces = 0;
kociendabb0c24b2001-08-24 14:24:40 +00001012
mjsfe301d72003-10-02 18:46:18 +00001013 bidi.current = start;
1014 bidi.last = bidi.current;
eseidel789896f2005-11-27 22:52:09 +00001015 bool pastEnd = false;
eseidel789896f2005-11-27 22:52:09 +00001016 BidiState stateAtEnd;
rjw834c8062005-02-25 23:41:39 +00001017
hyatt275d0702005-11-03 23:53:57 +00001018 while (true) {
kociendabb0c24b2001-08-24 14:24:40 +00001019 QChar::Direction dirCurrent;
adele7fc3e832006-02-17 09:31:35 +00001020 if (pastEnd && (previousLineBrokeCleanly || bidi.current.atEnd())) {
eseidel789896f2005-11-27 22:52:09 +00001021 BidiContext *c = bidi.context.get();
1022 while (c->parent)
1023 c = c->parent;
mjs08191d52006-03-06 02:53:41 +00001024 dirCurrent = c->dir();
adele7fc3e832006-02-17 09:31:35 +00001025 if (previousLineBrokeCleanly) {
eseidel789896f2005-11-27 22:52:09 +00001026 // A deviation from the Unicode Bidi Algorithm in order to match
1027 // Mac OS X text and WinIE: a hard line break resets bidi state.
1028 stateAtEnd.context = c;
1029 stateAtEnd.status.eor = dirCurrent;
1030 stateAtEnd.status.last = dirCurrent;
1031 stateAtEnd.status.lastStrong = dirCurrent;
1032 }
darinb70665a2002-04-15 23:43:21 +00001033 } else {
mjsfe301d72003-10-02 18:46:18 +00001034 dirCurrent = bidi.current.direction();
darin0ce8a6b2005-09-24 11:45:21 +00001035 if (bidi.context->override && dirCurrent != QChar::DirRLE && dirCurrent != QChar::DirLRE && dirCurrent != QChar::DirRLO && dirCurrent != QChar::DirLRO && dirCurrent != QChar::DirPDF)
mjs08191d52006-03-06 02:53:41 +00001036 dirCurrent = bidi.context->dir();
darinef0c09f2005-10-09 01:46:17 +00001037 else if (dirCurrent == QChar::DirNSM)
1038 dirCurrent = bidi.status.last;
hyatt33f8d492002-11-12 21:44:52 +00001039 }
darinf028f812002-06-10 20:08:04 +00001040
darinef0c09f2005-10-09 01:46:17 +00001041 assert(bidi.status.eor != QChar::DirON);
hyatt275d0702005-11-03 23:53:57 +00001042 switch (dirCurrent) {
kociendabb0c24b2001-08-24 14:24:40 +00001043
hyatt275d0702005-11-03 23:53:57 +00001044 // embedding and overrides (X1-X9 in the Bidi specs)
kociendabb0c24b2001-08-24 14:24:40 +00001045 case QChar::DirRLE:
kociendabb0c24b2001-08-24 14:24:40 +00001046 case QChar::DirLRE:
kociendabb0c24b2001-08-24 14:24:40 +00001047 case QChar::DirRLO:
kociendabb0c24b2001-08-24 14:24:40 +00001048 case QChar::DirLRO:
kociendabb0c24b2001-08-24 14:24:40 +00001049 case QChar::DirPDF:
hyatt275d0702005-11-03 23:53:57 +00001050 embed(dirCurrent, bidi);
hyatt33f8d492002-11-12 21:44:52 +00001051 break;
kociendabb0c24b2001-08-24 14:24:40 +00001052
1053 // strong types
1054 case QChar::DirL:
hyatt275d0702005-11-03 23:53:57 +00001055 switch(bidi.status.last) {
kociendabb0c24b2001-08-24 14:24:40 +00001056 case QChar::DirR:
1057 case QChar::DirAL:
1058 case QChar::DirEN:
1059 case QChar::DirAN:
ggarena61e04c2005-07-19 21:18:25 +00001060 if (bidi.status.last != QChar::DirEN || bidi.status.lastStrong != QChar::DirL)
hyatt275d0702005-11-03 23:53:57 +00001061 appendRun(bidi);
eseidel789896f2005-11-27 22:52:09 +00001062 break;
ggarenef026b22005-07-06 00:21:53 +00001063 case QChar::DirL:
kociendabb0c24b2001-08-24 14:24:40 +00001064 break;
1065 case QChar::DirES:
1066 case QChar::DirET:
1067 case QChar::DirCS:
1068 case QChar::DirBN:
1069 case QChar::DirB:
1070 case QChar::DirS:
1071 case QChar::DirWS:
1072 case QChar::DirON:
ggarenef026b22005-07-06 00:21:53 +00001073 if (bidi.status.eor == QChar::DirEN) {
1074 if (bidi.status.lastStrong != QChar::DirL) {
1075 // the numbers need to be on a higher embedding level, so let's close that run
hyatt275d0702005-11-03 23:53:57 +00001076 bidi.dir = QChar::DirEN;
ggarenef026b22005-07-06 00:21:53 +00001077 appendRun(bidi);
mjs08191d52006-03-06 02:53:41 +00001078 if (bidi.context->dir() != QChar::DirL) {
ggarenef026b22005-07-06 00:21:53 +00001079 // the neutrals take the embedding direction, which is R
1080 bidi.eor = bidi.last;
hyatt275d0702005-11-03 23:53:57 +00001081 bidi.dir = QChar::DirR;
ggarenef026b22005-07-06 00:21:53 +00001082 appendRun(bidi);
1083 }
1084 }
1085 } else if (bidi.status.eor == QChar::DirAN) {
1086 // Arabic numbers are always on a higher embedding level, so let's close that run
hyatt275d0702005-11-03 23:53:57 +00001087 bidi.dir = QChar::DirAN;
ggarenef026b22005-07-06 00:21:53 +00001088 appendRun(bidi);
mjs08191d52006-03-06 02:53:41 +00001089 if (bidi.context->dir() != QChar::DirL) {
ggarenef026b22005-07-06 00:21:53 +00001090 // the neutrals take the embedding direction, which is R
1091 bidi.eor = bidi.last;
hyatt275d0702005-11-03 23:53:57 +00001092 bidi.dir = QChar::DirR;
ggarenef026b22005-07-06 00:21:53 +00001093 appendRun(bidi);
1094 }
eseidel789896f2005-11-27 22:52:09 +00001095 } else if(bidi.status.eor != QChar::DirL) {
1096 //last stuff takes embedding dir
mjs08191d52006-03-06 02:53:41 +00001097 if (bidi.context->dir() != QChar::DirL && bidi.status.lastStrong != QChar::DirL) {
darin6f05b4c2005-05-27 01:17:32 +00001098 bidi.eor = bidi.last;
eseidel789896f2005-11-27 22:52:09 +00001099 bidi.dir = QChar::DirR;
kociendabb0c24b2001-08-24 14:24:40 +00001100 }
eseidel789896f2005-11-27 22:52:09 +00001101 appendRun(bidi);
kociendabb0c24b2001-08-24 14:24:40 +00001102 }
1103 default:
1104 break;
eseidel789896f2005-11-27 22:52:09 +00001105 }
1106 bidi.eor = bidi.current;
1107 bidi.status.eor = QChar::DirL;
mjsfe301d72003-10-02 18:46:18 +00001108 bidi.status.lastStrong = QChar::DirL;
hyatt275d0702005-11-03 23:53:57 +00001109 bidi.dir = QChar::DirL;
kociendabb0c24b2001-08-24 14:24:40 +00001110 break;
1111 case QChar::DirAL:
1112 case QChar::DirR:
eseidel789896f2005-11-27 22:52:09 +00001113 switch (bidi.status.last) {
kociendabb0c24b2001-08-24 14:24:40 +00001114 case QChar::DirL:
1115 case QChar::DirEN:
1116 case QChar::DirAN:
darinef0c09f2005-10-09 01:46:17 +00001117 appendRun(bidi);
1118 case QChar::DirR:
1119 case QChar::DirAL:
kociendabb0c24b2001-08-24 14:24:40 +00001120 break;
1121 case QChar::DirES:
1122 case QChar::DirET:
1123 case QChar::DirCS:
1124 case QChar::DirBN:
1125 case QChar::DirB:
1126 case QChar::DirS:
1127 case QChar::DirWS:
1128 case QChar::DirON:
darinef0c09f2005-10-09 01:46:17 +00001129 if (bidi.status.eor != QChar::DirR && bidi.status.eor != QChar::DirAL) {
kociendabb0c24b2001-08-24 14:24:40 +00001130 //last stuff takes embedding dir
mjs08191d52006-03-06 02:53:41 +00001131 if (bidi.context->dir() != QChar::DirR && bidi.status.lastStrong != QChar::DirR
eseidel789896f2005-11-27 22:52:09 +00001132 && bidi.status.lastStrong != QChar::DirAL) {
mjsfe301d72003-10-02 18:46:18 +00001133 bidi.eor = bidi.last;
eseidel789896f2005-11-27 22:52:09 +00001134 bidi.dir = QChar::DirL;
kociendabb0c24b2001-08-24 14:24:40 +00001135 }
eseidel789896f2005-11-27 22:52:09 +00001136 appendRun(bidi);
1137 }
kociendabb0c24b2001-08-24 14:24:40 +00001138 default:
1139 break;
eseidel789896f2005-11-27 22:52:09 +00001140 }
1141 bidi.eor = bidi.current;
darinef0c09f2005-10-09 01:46:17 +00001142 bidi.status.eor = QChar::DirR;
mjsfe301d72003-10-02 18:46:18 +00001143 bidi.status.lastStrong = dirCurrent;
hyatt275d0702005-11-03 23:53:57 +00001144 bidi.dir = QChar::DirR;
kociendabb0c24b2001-08-24 14:24:40 +00001145 break;
1146
1147 // weak types:
1148
kociendabb0c24b2001-08-24 14:24:40 +00001149 case QChar::DirEN:
eseidel789896f2005-11-27 22:52:09 +00001150 if (bidi.status.lastStrong != QChar::DirAL) {
kociendabb0c24b2001-08-24 14:24:40 +00001151 // if last strong was AL change EN to AN
hyatt275d0702005-11-03 23:53:57 +00001152 switch (bidi.status.last) {
kociendabb0c24b2001-08-24 14:24:40 +00001153 case QChar::DirEN:
1154 case QChar::DirL:
kociendabb0c24b2001-08-24 14:24:40 +00001155 break;
1156 case QChar::DirR:
1157 case QChar::DirAL:
1158 case QChar::DirAN:
darinef0c09f2005-10-09 01:46:17 +00001159 bidi.eor = bidi.last;
hyatt275d0702005-11-03 23:53:57 +00001160 appendRun(bidi);
hyatt275d0702005-11-03 23:53:57 +00001161 bidi.dir = QChar::DirEN;
darinef0c09f2005-10-09 01:46:17 +00001162 break;
kociendabb0c24b2001-08-24 14:24:40 +00001163 case QChar::DirES:
1164 case QChar::DirCS:
hyatt275d0702005-11-03 23:53:57 +00001165 if (bidi.status.eor == QChar::DirEN)
eseidel789896f2005-11-27 22:52:09 +00001166 break;
darine4fa9e22005-12-16 18:18:50 +00001167 case QChar::DirET:
kociendabb0c24b2001-08-24 14:24:40 +00001168 case QChar::DirBN:
1169 case QChar::DirB:
1170 case QChar::DirS:
1171 case QChar::DirWS:
1172 case QChar::DirON:
eseidel789896f2005-11-27 22:52:09 +00001173 if (bidi.status.eor == QChar::DirR) {
kociendabb0c24b2001-08-24 14:24:40 +00001174 // neutrals go to R
darine4fa9e22005-12-16 18:18:50 +00001175 bidi.eor = bidi.status.last == QChar::DirET ? bidi.lastBeforeET : bidi.last;
hyatt275d0702005-11-03 23:53:57 +00001176 appendRun(bidi);
1177 bidi.dir = QChar::DirEN;
eseidel789896f2005-11-27 22:52:09 +00001178 } else if (bidi.status.eor != QChar::DirL &&
1179 (bidi.status.eor != QChar::DirEN || bidi.status.lastStrong != QChar::DirL) &&
1180 bidi.dir != QChar::DirL) {
kociendabb0c24b2001-08-24 14:24:40 +00001181 // numbers on both sides, neutrals get right to left direction
eseidel789896f2005-11-27 22:52:09 +00001182 appendRun(bidi);
darine4fa9e22005-12-16 18:18:50 +00001183 bidi.eor = bidi.status.last == QChar::DirET ? bidi.lastBeforeET : bidi.last;
eseidel789896f2005-11-27 22:52:09 +00001184 bidi.dir = QChar::DirR;
1185 appendRun(bidi);
1186 bidi.dir = QChar::DirEN;
kociendabb0c24b2001-08-24 14:24:40 +00001187 }
1188 default:
1189 break;
eseidel789896f2005-11-27 22:52:09 +00001190 }
1191 bidi.eor = bidi.current;
1192 bidi.status.eor = QChar::DirEN;
hyatt275d0702005-11-03 23:53:57 +00001193 if (bidi.dir == QChar::DirON)
1194 bidi.dir = QChar::DirL;
kociendabb0c24b2001-08-24 14:24:40 +00001195 break;
1196 }
1197 case QChar::DirAN:
1198 dirCurrent = QChar::DirAN;
eseidel789896f2005-11-27 22:52:09 +00001199 switch (bidi.status.last) {
kociendabb0c24b2001-08-24 14:24:40 +00001200 case QChar::DirL:
mjs08191d52006-03-06 02:53:41 +00001201 if (bidi.context->dir() == QChar::DirL)
mjs89ab8912005-11-28 04:37:33 +00001202 appendRun(bidi);
1203 break;
kociendabb0c24b2001-08-24 14:24:40 +00001204 case QChar::DirAN:
eseidel789896f2005-11-27 22:52:09 +00001205 break;
kociendabb0c24b2001-08-24 14:24:40 +00001206 case QChar::DirR:
1207 case QChar::DirAL:
1208 case QChar::DirEN:
darinef0c09f2005-10-09 01:46:17 +00001209 bidi.eor = bidi.last;
hyatt275d0702005-11-03 23:53:57 +00001210 appendRun(bidi);
kociendabb0c24b2001-08-24 14:24:40 +00001211 break;
1212 case QChar::DirCS:
hyatt275d0702005-11-03 23:53:57 +00001213 if (bidi.status.eor == QChar::DirAN)
eseidel789896f2005-11-27 22:52:09 +00001214 break;
kociendabb0c24b2001-08-24 14:24:40 +00001215 case QChar::DirES:
1216 case QChar::DirET:
1217 case QChar::DirBN:
1218 case QChar::DirB:
1219 case QChar::DirS:
1220 case QChar::DirWS:
1221 case QChar::DirON:
hyatt275d0702005-11-03 23:53:57 +00001222 if (bidi.status.eor != QChar::DirR && bidi.status.eor != QChar::DirAL) {
ggarenef026b22005-07-06 00:21:53 +00001223 // run of L before neutrals, neutrals take embedding dir (N2)
mjs08191d52006-03-06 02:53:41 +00001224 if (bidi.context->dir() == QChar::DirR || bidi.status.lastStrong == QChar::DirR
ggarenef026b22005-07-06 00:21:53 +00001225 || bidi.status.lastStrong == QChar::DirAL) {
1226 // the embedding direction is R
1227 // close the L run
hyatt275d0702005-11-03 23:53:57 +00001228 appendRun(bidi);
ggarenef026b22005-07-06 00:21:53 +00001229 // neutrals become an R run
hyatt275d0702005-11-03 23:53:57 +00001230 bidi.dir = QChar::DirR;
kociendabb0c24b2001-08-24 14:24:40 +00001231 } else {
ggarenef026b22005-07-06 00:21:53 +00001232 // the embedding direction is L
1233 // append neutrals to the L run and close it
hyatt275d0702005-11-03 23:53:57 +00001234 bidi.dir = QChar::DirL;
kociendabb0c24b2001-08-24 14:24:40 +00001235 }
1236 }
eseidel789896f2005-11-27 22:52:09 +00001237 bidi.eor = bidi.last;
1238 appendRun(bidi);
kociendabb0c24b2001-08-24 14:24:40 +00001239 default:
1240 break;
hyatt275d0702005-11-03 23:53:57 +00001241 }
eseidel789896f2005-11-27 22:52:09 +00001242 bidi.eor = bidi.current;
1243 bidi.status.eor = QChar::DirAN;
hyatt275d0702005-11-03 23:53:57 +00001244 if (bidi.dir == QChar::DirON)
1245 bidi.dir = QChar::DirAN;
kociendabb0c24b2001-08-24 14:24:40 +00001246 break;
1247 case QChar::DirES:
1248 case QChar::DirCS:
1249 break;
1250 case QChar::DirET:
hyatt275d0702005-11-03 23:53:57 +00001251 if (bidi.status.last == QChar::DirEN) {
kociendabb0c24b2001-08-24 14:24:40 +00001252 dirCurrent = QChar::DirEN;
hyatt275d0702005-11-03 23:53:57 +00001253 bidi.eor = bidi.current;
1254 bidi.status.eor = dirCurrent;
darine4fa9e22005-12-16 18:18:50 +00001255 } else if (bidi.status.last != QChar::DirET)
1256 bidi.lastBeforeET = emptyRun ? bidi.eor : bidi.last;
kociendabb0c24b2001-08-24 14:24:40 +00001257 break;
1258
1259 // boundary neutrals should be ignored
1260 case QChar::DirBN:
darinef0c09f2005-10-09 01:46:17 +00001261 if (bidi.eor == bidi.last)
1262 bidi.eor = bidi.current;
kociendabb0c24b2001-08-24 14:24:40 +00001263 break;
1264 // neutrals
1265 case QChar::DirB:
1266 // ### what do we do with newline and paragraph seperators that come to here?
1267 break;
1268 case QChar::DirS:
1269 // ### implement rule L1
1270 break;
1271 case QChar::DirWS:
hyattfe99c872003-07-31 22:25:29 +00001272 break;
kociendabb0c24b2001-08-24 14:24:40 +00001273 case QChar::DirON:
1274 break;
1275 default:
1276 break;
1277 }
1278
eseidel789896f2005-11-27 22:52:09 +00001279 if (pastEnd) {
1280 if (bidi.eor == bidi.current) {
1281 if (!bidi.reachedEndOfLine) {
1282 bidi.eor = bidi.endOfLine;
1283 switch (bidi.status.eor) {
1284 case QChar::DirL:
1285 case QChar::DirR:
1286 case QChar::DirAN:
1287 bidi.dir = bidi.status.eor;
1288 break;
1289 case QChar::DirEN:
1290 bidi.dir = bidi.status.lastStrong == QChar::DirL ? QChar::DirL : QChar::DirEN;
1291 break;
1292 default:
1293 assert(false);
1294 }
1295 appendRun(bidi);
1296 }
1297 bidi = stateAtEnd;
1298 bidi.dir = QChar::DirON;
1299 break;
1300 }
1301 }
kociendabb0c24b2001-08-24 14:24:40 +00001302
1303 // set status.last as needed.
hyatt275d0702005-11-03 23:53:57 +00001304 switch (dirCurrent) {
kociendabb0c24b2001-08-24 14:24:40 +00001305 case QChar::DirET:
ggarenef026b22005-07-06 00:21:53 +00001306 if (bidi.status.last != QChar::DirEN)
1307 bidi.status.last = QChar::DirET;
1308 break;
kociendabb0c24b2001-08-24 14:24:40 +00001309 case QChar::DirES:
1310 case QChar::DirCS:
1311 case QChar::DirS:
1312 case QChar::DirWS:
1313 case QChar::DirON:
hyatt275d0702005-11-03 23:53:57 +00001314 switch(bidi.status.last) {
kociendabb0c24b2001-08-24 14:24:40 +00001315 case QChar::DirL:
1316 case QChar::DirR:
1317 case QChar::DirAL:
1318 case QChar::DirEN:
1319 case QChar::DirAN:
mjsfe301d72003-10-02 18:46:18 +00001320 bidi.status.last = dirCurrent;
kociendabb0c24b2001-08-24 14:24:40 +00001321 break;
1322 default:
mjsfe301d72003-10-02 18:46:18 +00001323 bidi.status.last = QChar::DirON;
kociendabb0c24b2001-08-24 14:24:40 +00001324 }
1325 break;
1326 case QChar::DirNSM:
1327 case QChar::DirBN:
darinef0c09f2005-10-09 01:46:17 +00001328 case QChar::DirRLE:
1329 case QChar::DirLRE:
1330 case QChar::DirRLO:
1331 case QChar::DirLRO:
1332 case QChar::DirPDF:
kociendabb0c24b2001-08-24 14:24:40 +00001333 // ignore these
1334 break;
rjw2028aa42003-01-14 21:14:16 +00001335 case QChar::DirEN:
rjw2028aa42003-01-14 21:14:16 +00001336 // fall through
kociendabb0c24b2001-08-24 14:24:40 +00001337 default:
mjsfe301d72003-10-02 18:46:18 +00001338 bidi.status.last = dirCurrent;
hyatt275d0702005-11-03 23:53:57 +00001339 }
kociendabb0c24b2001-08-24 14:24:40 +00001340
mjsfe301d72003-10-02 18:46:18 +00001341 bidi.last = bidi.current;
mjs6f821c82002-03-22 00:31:57 +00001342
darin54008922006-01-13 16:39:05 +00001343 if (emptyRun && !(dirCurrent == QChar::DirRLE || dirCurrent == QChar::DirLRE || dirCurrent == QChar::DirRLO || dirCurrent == QChar::DirLRO || dirCurrent == QChar::DirPDF)) {
1344 bidi.sor = bidi.current;
1345 emptyRun = false;
1346 }
darinb70665a2002-04-15 23:43:21 +00001347
darin54008922006-01-13 16:39:05 +00001348 // this causes the operator ++ to open and close embedding levels as needed
1349 // for the CSS unicode-bidi property
1350 bidi.adjustEmbedding = true;
hyatt275d0702005-11-03 23:53:57 +00001351 bidi.current.increment(bidi);
darin54008922006-01-13 16:39:05 +00001352 bidi.adjustEmbedding = false;
darinef0c09f2005-10-09 01:46:17 +00001353 if (emptyRun && (dirCurrent == QChar::DirRLE || dirCurrent == QChar::DirLRE || dirCurrent == QChar::DirRLO || dirCurrent == QChar::DirLRO || dirCurrent == QChar::DirPDF)) {
1354 // exclude the embedding char itself from the new run so that ATSUI will never see it
1355 bidi.eor.obj = 0;
1356 bidi.last = bidi.current;
1357 bidi.sor = bidi.current;
1358 }
darinb70665a2002-04-15 23:43:21 +00001359
eseidel789896f2005-11-27 22:52:09 +00001360 if (!pastEnd && (bidi.current == end || bidi.current.atEnd())) {
1361 if (emptyRun)
1362 break;
1363 stateAtEnd = bidi;
1364 bidi.endOfLine = bidi.last;
1365 pastEnd = true;
eseidel789896f2005-11-27 22:52:09 +00001366 }
mjs6f821c82002-03-22 00:31:57 +00001367 }
kociendabb0c24b2001-08-24 14:24:40 +00001368
kociendabb0c24b2001-08-24 14:24:40 +00001369 // reorder line according to run structure...
kociendabb0c24b2001-08-24 14:24:40 +00001370 // do not reverse for visually ordered web sites
hyatt275d0702005-11-03 23:53:57 +00001371 if (!style()->visuallyOrdered()) {
eseidel789896f2005-11-27 22:52:09 +00001372
1373 // first find highest and lowest levels
darin479ef852006-01-30 06:06:32 +00001374 unsigned char levelLow = 128;
1375 unsigned char levelHigh = 0;
eseidel789896f2005-11-27 22:52:09 +00001376 BidiRun* r = sFirstBidiRun;
1377 while (r) {
1378 if (r->level > levelHigh)
1379 levelHigh = r->level;
1380 if (r->level < levelLow)
1381 levelLow = r->level;
1382 r = r->nextRun;
1383 }
1384
1385 // implements reordering of the line (L2 according to Bidi spec):
1386 // L2. From the highest level found in the text to the lowest odd level on each line,
1387 // reverse any contiguous sequence of characters that are at that level or higher.
1388
1389 // reversing is only done up to the lowest odd level
1390 if (!(levelLow%2))
1391 levelLow++;
1392
1393 int count = sBidiRunCount - 1;
1394
hyatt275d0702005-11-03 23:53:57 +00001395 while (levelHigh >= levelLow) {
kociendabb0c24b2001-08-24 14:24:40 +00001396 int i = 0;
hyatt2f1e7102003-02-20 01:27:03 +00001397 BidiRun* currRun = sFirstBidiRun;
hyatt275d0702005-11-03 23:53:57 +00001398 while (i < count) {
1399 while (i < count && currRun && currRun->level < levelHigh) {
kociendabb0c24b2001-08-24 14:24:40 +00001400 i++;
hyatt2f1e7102003-02-20 01:27:03 +00001401 currRun = currRun->nextRun;
kociendabb0c24b2001-08-24 14:24:40 +00001402 }
hyatt2f1e7102003-02-20 01:27:03 +00001403 int start = i;
hyatt275d0702005-11-03 23:53:57 +00001404 while (i <= count && currRun && currRun->level >= levelHigh) {
hyatt2f1e7102003-02-20 01:27:03 +00001405 i++;
1406 currRun = currRun->nextRun;
1407 }
1408 int end = i-1;
1409 reverseRuns(start, end);
kociendabb0c24b2001-08-24 14:24:40 +00001410 }
1411 levelHigh--;
1412 }
1413 }
eseidel789896f2005-11-27 22:52:09 +00001414 bidi.endOfLine.obj = 0;
hyatt4b381692003-03-10 21:11:59 +00001415}
kociendabb0c24b2001-08-24 14:24:40 +00001416
hyatt275d0702005-11-03 23:53:57 +00001417static void buildCompactRuns(RenderObject* compactObj, BidiState& bidi)
hyatt4b381692003-03-10 21:11:59 +00001418{
1419 sBuildingCompactRuns = true;
1420 if (!compactObj->isRenderBlock()) {
1421 // Just append a run for our object.
1422 isLineEmpty = false;
eseidel789896f2005-11-27 22:52:09 +00001423 addRun(new (compactObj->renderArena()) BidiRun(0, compactObj->length(), compactObj, bidi.context.get(), bidi.dir));
hyatt4b381692003-03-10 21:11:59 +00001424 }
1425 else {
1426 // Format the compact like it is its own single line. We build up all the runs for
1427 // the little compact and then reorder them for bidi.
1428 RenderBlock* compactBlock = static_cast<RenderBlock*>(compactObj);
hyatt275d0702005-11-03 23:53:57 +00001429 bidi.adjustEmbedding = true;
1430 BidiIterator start(compactBlock, bidiFirst(compactBlock, bidi), 0);
1431 bidi.adjustEmbedding = false;
darin8341f372003-04-29 18:30:31 +00001432 BidiIterator end = start;
hyattffe78712003-02-11 01:59:29 +00001433
hyatt4b381692003-03-10 21:11:59 +00001434 betweenMidpoints = false;
1435 isLineEmpty = true;
hyatt0c3a9862004-02-23 21:26:26 +00001436 previousLineBrokeCleanly = true;
hyatt01eff982003-03-14 20:13:23 +00001437
mjsfe301d72003-10-02 18:46:18 +00001438 end = compactBlock->findNextLineBreak(start, bidi);
hyatt4b381692003-03-10 21:11:59 +00001439 if (!isLineEmpty)
mjsfe301d72003-10-02 18:46:18 +00001440 compactBlock->bidiReorderLine(start, end, bidi);
hyatt4b381692003-03-10 21:11:59 +00001441 }
hyatt275d0702005-11-03 23:53:57 +00001442
hyatt4b381692003-03-10 21:11:59 +00001443 sCompactFirstBidiRun = sFirstBidiRun;
1444 sCompactLastBidiRun = sLastBidiRun;
1445 sCompactBidiRunCount = sBidiRunCount;
1446
1447 sNumMidpoints = 0;
1448 sCurrMidpoint = 0;
1449 betweenMidpoints = false;
1450 sBuildingCompactRuns = false;
kociendabb0c24b2001-08-24 14:24:40 +00001451}
1452
hyatt673184e2006-01-14 22:07:08 +00001453IntRect RenderBlock::layoutInlineChildren(bool relayoutChildren)
kociendabb0c24b2001-08-24 14:24:40 +00001454{
mjsfe301d72003-10-02 18:46:18 +00001455 BidiState bidi;
1456
hyatt0c3a9862004-02-23 21:26:26 +00001457 bool useRepaintRect = false;
hyatt673184e2006-01-14 22:07:08 +00001458 IntRect repaintRect(0,0,0,0);
hyatt0c3a9862004-02-23 21:26:26 +00001459
hyatta70560a2002-11-20 01:53:20 +00001460 m_overflowHeight = 0;
1461
kociendabb0c24b2001-08-24 14:24:40 +00001462 invalidateVerticalPositions();
hyattdd7dfe92003-11-16 20:48:12 +00001463
1464 m_height = borderTop() + paddingTop();
1465 int toAdd = borderBottom() + paddingBottom();
hyatt0bab2a62004-04-13 22:57:12 +00001466 if (includeScrollbarSize())
hyattdd7dfe92003-11-16 20:48:12 +00001467 toAdd += m_layer->horizontalScrollbarHeight();
1468
hyatt0c3a9862004-02-23 21:26:26 +00001469 // Figure out if we should clear out our line boxes.
1470 // FIXME: Handle resize eventually!
1471 // FIXME: Do something better when floats are present.
1472 bool fullLayout = !firstLineBox() || !firstChild() || selfNeedsLayout() || relayoutChildren || containsFloats();
1473 if (fullLayout)
1474 deleteLineBoxes();
hyatted77ad82004-06-15 07:21:23 +00001475
1476 // Text truncation only kicks in if your overflow isn't visible and your text-overflow-mode isn't
1477 // clip.
1478 // FIXME: CSS3 says that descendants that are clipped must also know how to truncate. This is insanely
1479 // difficult to figure out (especially in the middle of doing layout), and is really an esoteric pile of nonsense
1480 // anyway, so we won't worry about following the draft here.
1481 bool hasTextOverflow = style()->textOverflow() && hasOverflowClip();
hyatt33f8d492002-11-12 21:44:52 +00001482
hyatted77ad82004-06-15 07:21:23 +00001483 // Walk all the lines and delete our ellipsis line boxes if they exist.
1484 if (hasTextOverflow)
1485 deleteEllipsisLineBoxes();
1486
hyatt0dd79772004-10-26 21:08:41 +00001487 int oldLineBottom = lastRootBox() ? lastRootBox()->bottomOverflow() : m_height;
hyatt2161d952005-01-12 23:33:21 +00001488 int startLineBottom = 0;
1489
hyattffe78712003-02-11 01:59:29 +00001490 if (firstChild()) {
kociendabb0c24b2001-08-24 14:24:40 +00001491 // layout replaced elements
hyattffe78712003-02-11 01:59:29 +00001492 bool endOfInline = false;
hyatt275d0702005-11-03 23:53:57 +00001493 RenderObject *o = bidiFirst(this, bidi, false);
hyatt0c3a9862004-02-23 21:26:26 +00001494 bool hasFloat = false;
1495 while (o) {
1496 if (o->isReplaced() || o->isFloating() || o->isPositioned()) {
hyatt89377f12002-12-13 21:24:40 +00001497 if (relayoutChildren || o->style()->width().isPercent() || o->style()->height().isPercent())
hyattadbb2ef2003-11-18 09:21:44 +00001498 o->setChildNeedsLayout(true, false);
hyatt3ac01352003-03-22 01:37:33 +00001499 if (o->isPositioned())
1500 o->containingBlock()->insertPositionedObject(o);
hyatt0c3a9862004-02-23 21:26:26 +00001501 else {
1502 if (o->isFloating())
1503 hasFloat = true;
1504 else if (fullLayout || o->needsLayout()) // Replaced elements
1505 o->dirtyLineBoxes(fullLayout);
hyatt390427a2003-05-03 18:33:42 +00001506 o->layoutIfNeeded();
hyatt0c3a9862004-02-23 21:26:26 +00001507 }
kociendabb0c24b2001-08-24 14:24:40 +00001508 }
hyatt0c3a9862004-02-23 21:26:26 +00001509 else if (o->isText() || (o->isInlineFlow() && !endOfInline)) {
1510 if (fullLayout || o->selfNeedsLayout())
1511 o->dirtyLineBoxes(fullLayout);
hyattf4fe67f2003-10-07 04:43:23 +00001512 o->setNeedsLayout(false);
hyattf4fe67f2003-10-07 04:43:23 +00001513 }
hyatt275d0702005-11-03 23:53:57 +00001514 o = bidiNext(this, o, bidi, false, &endOfInline);
kociendabb0c24b2001-08-24 14:24:40 +00001515 }
1516
hyatt0c3a9862004-02-23 21:26:26 +00001517 if (hasFloat)
1518 fullLayout = true; // FIXME: Will need to find a way to optimize floats some day.
1519
hyatt837eb362004-05-21 22:17:10 +00001520 if (fullLayout && !selfNeedsLayout()) {
hyatt0c3a9862004-02-23 21:26:26 +00001521 setNeedsLayout(true, false); // Mark ourselves as needing a full layout. This way we'll repaint like
1522 // we're supposed to.
hyatt837eb362004-05-21 22:17:10 +00001523 if (!document()->view()->needsFullRepaint() && m_layer) {
1524 // Because we waited until we were already inside layout to discover
1525 // that the block really needed a full layout, we missed our chance to repaint the layer
1526 // before layout started. Luckily the layer has cached the repaint rect for its original
1527 // position and size, and so we can use that to make a repaint happen now.
1528 RenderCanvas* c = canvas();
1529 if (c && !c->printingMode())
1530 c->repaintViewRectangle(m_layer->repaintRect());
1531 }
1532 }
hyatt0c3a9862004-02-23 21:26:26 +00001533
kociendabb0c24b2001-08-24 14:24:40 +00001534 BidiContext *startEmbed;
hyatt275d0702005-11-03 23:53:57 +00001535 if (style()->direction() == LTR) {
darin0ce8a6b2005-09-24 11:45:21 +00001536 startEmbed = new BidiContext( 0, QChar::DirL, NULL, style()->unicodeBidi() == Override );
mjsfe301d72003-10-02 18:46:18 +00001537 bidi.status.eor = QChar::DirL;
kociendabb0c24b2001-08-24 14:24:40 +00001538 } else {
darin0ce8a6b2005-09-24 11:45:21 +00001539 startEmbed = new BidiContext( 1, QChar::DirR, NULL, style()->unicodeBidi() == Override );
mjsfe301d72003-10-02 18:46:18 +00001540 bidi.status.eor = QChar::DirR;
kociendabb0c24b2001-08-24 14:24:40 +00001541 }
kociendabb0c24b2001-08-24 14:24:40 +00001542
mjs08191d52006-03-06 02:53:41 +00001543 bidi.status.lastStrong = startEmbed->dir();
1544 bidi.status.last = startEmbed->dir();
1545 bidi.status.eor = startEmbed->dir();
mjsfe301d72003-10-02 18:46:18 +00001546 bidi.context = startEmbed;
eseidel789896f2005-11-27 22:52:09 +00001547 bidi.dir = QChar::DirON;
hyatt33f8d492002-11-12 21:44:52 +00001548
hyatt85586af2003-02-19 23:22:42 +00001549 if (!smidpoints)
darinb9481ed2006-03-20 02:57:59 +00001550 smidpoints = new DeprecatedArray<BidiIterator>;
hyatt0c3a9862004-02-23 21:26:26 +00001551
hyatt85586af2003-02-19 23:22:42 +00001552 sNumMidpoints = 0;
1553 sCurrMidpoint = 0;
hyatt4b381692003-03-10 21:11:59 +00001554 sCompactFirstBidiRun = sCompactLastBidiRun = 0;
1555 sCompactBidiRunCount = 0;
hyatt01eff982003-03-14 20:13:23 +00001556
hyatt0c3a9862004-02-23 21:26:26 +00001557 // We want to skip ahead to the first dirty line
1558 BidiIterator start;
1559 RootInlineBox* startLine = determineStartPosition(fullLayout, start, bidi);
1560
1561 // We also find the first clean line and extract these lines. We will add them back
1562 // if we determine that we're able to synchronize after handling all our dirty lines.
1563 BidiIterator cleanLineStart;
eseidel789896f2005-11-27 22:52:09 +00001564 BidiStatus cleanLineBidiStatus;
1565 BidiContext* cleanLineBidiContext;
hyatt0c3a9862004-02-23 21:26:26 +00001566 int endLineYPos;
1567 RootInlineBox* endLine = (fullLayout || !startLine) ?
eseidel789896f2005-11-27 22:52:09 +00001568 0 : determineEndPosition(startLine, cleanLineStart, cleanLineBidiStatus, cleanLineBidiContext, endLineYPos);
1569 if (endLine && cleanLineBidiContext)
1570 cleanLineBidiContext->ref();
hyatt0c3a9862004-02-23 21:26:26 +00001571 if (startLine) {
1572 useRepaintRect = true;
hyatt2161d952005-01-12 23:33:21 +00001573 startLineBottom = startLine->bottomOverflow();
darin7bd70952006-04-13 07:07:34 +00001574 repaintRect.setY(min(m_height, startLine->topOverflow()));
hyatt0c3a9862004-02-23 21:26:26 +00001575 RenderArena* arena = renderArena();
1576 RootInlineBox* box = startLine;
1577 while (box) {
1578 RootInlineBox* next = box->nextRootBox();
1579 box->deleteLine(arena);
1580 box = next;
1581 }
1582 startLine = 0;
1583 }
1584
1585 BidiIterator end = start;
1586
1587 bool endLineMatched = false;
1588 while (!end.atEnd()) {
kociendabb0c24b2001-08-24 14:24:40 +00001589 start = end;
eseidel789896f2005-11-27 22:52:09 +00001590 if (endLine && (endLineMatched = matchedEndLine(start, bidi.status, bidi.context.get(), cleanLineStart, cleanLineBidiStatus, cleanLineBidiContext, endLine, endLineYPos)))
hyatt0c3a9862004-02-23 21:26:26 +00001591 break;
1592
hyatt33f8d492002-11-12 21:44:52 +00001593 betweenMidpoints = false;
1594 isLineEmpty = true;
hyatt75c30b62004-07-16 20:15:34 +00001595 if (m_firstLine && firstChild() && firstChild()->isCompact() && firstChild()->isRenderBlock()) {
mjsfe301d72003-10-02 18:46:18 +00001596 buildCompactRuns(firstChild(), bidi);
hyatt4b381692003-03-10 21:11:59 +00001597 start.obj = firstChild()->nextSibling();
1598 end = start;
1599 }
mjsfe301d72003-10-02 18:46:18 +00001600 end = findNextLineBreak(start, bidi);
eseidel789896f2005-11-27 22:52:09 +00001601 if (start.atEnd())
1602 break;
hyatt33f8d492002-11-12 21:44:52 +00001603 if (!isLineEmpty) {
mjsfe301d72003-10-02 18:46:18 +00001604 bidiReorderLine(start, end, bidi);
hyatt4b381692003-03-10 21:11:59 +00001605
1606 // Now that the runs have been ordered, we create the line boxes.
1607 // At the same time we figure out where border/padding/margin should be applied for
1608 // inline flow boxes.
1609 if (sCompactFirstBidiRun) {
1610 // We have a compact line sharing this line. Link the compact runs
1611 // to our runs to create a single line of runs.
1612 sCompactLastBidiRun->nextRun = sFirstBidiRun;
1613 sFirstBidiRun = sCompactFirstBidiRun;
1614 sBidiRunCount += sCompactBidiRunCount;
1615 }
hyatt4b381692003-03-10 21:11:59 +00001616
hyatt0c3a9862004-02-23 21:26:26 +00001617 RootInlineBox* lineBox = 0;
hyattdfb09052003-03-11 02:58:59 +00001618 if (sBidiRunCount) {
hyatt0c3a9862004-02-23 21:26:26 +00001619 lineBox = constructLine(start, end);
hyattdfb09052003-03-11 02:58:59 +00001620 if (lineBox) {
hyatt0c3a9862004-02-23 21:26:26 +00001621 lineBox->setEndsWithBreak(previousLineBrokeCleanly);
1622
hyattdfb09052003-03-11 02:58:59 +00001623 // Now we position all of our text runs horizontally.
mjsfe301d72003-10-02 18:46:18 +00001624 computeHorizontalPositionsForLine(lineBox, bidi);
hyattdfb09052003-03-11 02:58:59 +00001625
1626 // Now position our text runs vertically.
1627 computeVerticalPositionsForLine(lineBox);
1628
1629 deleteBidiRuns(renderArena());
1630 }
1631 }
hyatt4b381692003-03-10 21:11:59 +00001632
hyatt0c05e102006-04-14 08:15:00 +00001633 if (end == start) {
hyatt275d0702005-11-03 23:53:57 +00001634 bidi.adjustEmbedding = true;
mjsfe301d72003-10-02 18:46:18 +00001635 end.increment(bidi);
hyatt275d0702005-11-03 23:53:57 +00001636 bidi.adjustEmbedding = false;
hyatt33f8d492002-11-12 21:44:52 +00001637 }
hyatt35a85e52003-02-14 19:43:07 +00001638
hyatt0c3a9862004-02-23 21:26:26 +00001639 if (lineBox)
eseidel789896f2005-11-27 22:52:09 +00001640 lineBox->setLineBreakInfo(end.obj, end.pos, &bidi.status, bidi.context.get());
hyatt0c3a9862004-02-23 21:26:26 +00001641
hyatt35a85e52003-02-14 19:43:07 +00001642 m_firstLine = false;
hyatt33f8d492002-11-12 21:44:52 +00001643 newLine();
darinb70665a2002-04-15 23:43:21 +00001644 }
hyatt74eec4d2003-03-23 08:02:47 +00001645
hyatt85586af2003-02-19 23:22:42 +00001646 sNumMidpoints = 0;
1647 sCurrMidpoint = 0;
hyatt4b381692003-03-10 21:11:59 +00001648 sCompactFirstBidiRun = sCompactLastBidiRun = 0;
1649 sCompactBidiRunCount = 0;
kociendabb0c24b2001-08-24 14:24:40 +00001650 }
hyatt0c3a9862004-02-23 21:26:26 +00001651
1652 if (endLine) {
1653 if (endLineMatched) {
hyatt2161d952005-01-12 23:33:21 +00001654 // Note our current y-position for correct repainting when no lines move. If no lines move, we still have to
1655 // repaint up to the maximum of the bottom overflow of the old start line or the bottom overflow of the new last line.
darin7bd70952006-04-13 07:07:34 +00001656 int currYPos = max(startLineBottom, m_height);
hyatt2161d952005-01-12 23:33:21 +00001657 if (lastRootBox())
darin7bd70952006-04-13 07:07:34 +00001658 currYPos = max(currYPos, lastRootBox()->bottomOverflow());
hyatt2161d952005-01-12 23:33:21 +00001659
hyatt0c3a9862004-02-23 21:26:26 +00001660 // Attach all the remaining lines, and then adjust their y-positions as needed.
1661 for (RootInlineBox* line = endLine; line; line = line->nextRootBox())
1662 line->attachLine();
1663
1664 // Now apply the offset to each line if needed.
1665 int delta = m_height - endLineYPos;
harrison208ea792005-07-29 23:42:59 +00001666 if (delta) {
hyatt0c3a9862004-02-23 21:26:26 +00001667 for (RootInlineBox* line = endLine; line; line = line->nextRootBox())
hyattda77c4b2004-06-17 18:09:49 +00001668 line->adjustPosition(0, delta);
harrison208ea792005-07-29 23:42:59 +00001669 }
hyatt0c3a9862004-02-23 21:26:26 +00001670 m_height = lastRootBox()->blockHeight();
darin7bd70952006-04-13 07:07:34 +00001671 m_overflowHeight = max(m_height, m_overflowHeight);
hyatt0c3a9862004-02-23 21:26:26 +00001672 int bottomOfLine = lastRootBox()->bottomOverflow();
1673 if (bottomOfLine > m_height && bottomOfLine > m_overflowHeight)
1674 m_overflowHeight = bottomOfLine;
1675 if (delta)
darin7bd70952006-04-13 07:07:34 +00001676 repaintRect.setHeight(max(m_overflowHeight-delta, m_overflowHeight) - repaintRect.y());
hyatt0c3a9862004-02-23 21:26:26 +00001677 else
1678 repaintRect.setHeight(currYPos - repaintRect.y());
1679 }
1680 else {
1681 // Delete all the remaining lines.
darin7bd70952006-04-13 07:07:34 +00001682 m_overflowHeight = max(m_height, m_overflowHeight);
hyatt0c3a9862004-02-23 21:26:26 +00001683 InlineRunBox* line = endLine;
1684 RenderArena* arena = renderArena();
1685 while (line) {
1686 InlineRunBox* next = line->nextLineBox();
1687 if (!next)
darin7bd70952006-04-13 07:07:34 +00001688 repaintRect.setHeight(max(m_overflowHeight, line->bottomOverflow()) - repaintRect.y());
hyatt0c3a9862004-02-23 21:26:26 +00001689 line->deleteLine(arena);
1690 line = next;
1691 }
1692 }
eseidel789896f2005-11-27 22:52:09 +00001693 if (cleanLineBidiContext)
1694 cleanLineBidiContext->deref();
hyatt0c3a9862004-02-23 21:26:26 +00001695 }
kociendabb0c24b2001-08-24 14:24:40 +00001696 }
hyatt85586af2003-02-19 23:22:42 +00001697
1698 sNumMidpoints = 0;
1699 sCurrMidpoint = 0;
hyatta70560a2002-11-20 01:53:20 +00001700
hyattefa00882003-03-22 23:27:03 +00001701 // in case we have a float on the last line, it might not be positioned up to now.
1702 // This has to be done before adding in the bottom border/padding, or the float will
1703 // include the padding incorrectly. -dwh
1704 positionNewFloats();
1705
hyatta70560a2002-11-20 01:53:20 +00001706 // Now add in the bottom border/padding.
kociendabb0c24b2001-08-24 14:24:40 +00001707 m_height += toAdd;
1708
hyatta70560a2002-11-20 01:53:20 +00001709 // Always make sure this is at least our height.
darin7bd70952006-04-13 07:07:34 +00001710 m_overflowHeight = max(m_height, m_overflowHeight);
hyatta70560a2002-11-20 01:53:20 +00001711
hyattb4b20872004-10-20 21:34:01 +00001712 // See if any lines spill out of the block. If so, we need to update our overflow width.
1713 checkLinesForOverflow();
1714
hyatt0c3a9862004-02-23 21:26:26 +00001715 if (useRepaintRect) {
eseidelcf075d02006-03-26 22:58:26 +00001716 repaintRect.setX(m_overflowLeft);
darin7bd70952006-04-13 07:07:34 +00001717 repaintRect.setWidth(max((int)m_width, m_overflowWidth) - m_overflowLeft);
hyatt0c3a9862004-02-23 21:26:26 +00001718 if (repaintRect.height() == 0)
darin7bd70952006-04-13 07:07:34 +00001719 repaintRect.setHeight(max(oldLineBottom, m_overflowHeight) - repaintRect.y());
hyattea43b002006-04-19 18:47:03 +00001720 if (hasOverflowClip())
1721 // Don't allow this rect to spill out of our overflow box.
1722 repaintRect.intersect(IntRect(0, 0, m_width, m_height));
hyatt0c3a9862004-02-23 21:26:26 +00001723 }
hyatta6960b12004-12-07 02:09:10 +00001724
hyattb4b20872004-10-20 21:34:01 +00001725 if (!firstLineBox() && element() && element()->isContentEditable() && element()->rootEditableElement() == element())
kocienda616320e2004-04-01 00:34:07 +00001726 m_height += lineHeight(true);
hyatted77ad82004-06-15 07:21:23 +00001727
1728 // See if we have any lines that spill out of our block. If we do, then we will possibly need to
1729 // truncate text.
1730 if (hasTextOverflow)
1731 checkLinesForTextOverflow();
1732
hyatt0c3a9862004-02-23 21:26:26 +00001733 return repaintRect;
1734
darinb70665a2002-04-15 23:43:21 +00001735#if BIDI_DEBUG > 1
1736 kdDebug(6041) << " ------- bidi end " << this << " -------" << endl;
darinf028f812002-06-10 20:08:04 +00001737#endif
kociendabb0c24b2001-08-24 14:24:40 +00001738}
1739
hyatt0c3a9862004-02-23 21:26:26 +00001740RootInlineBox* RenderBlock::determineStartPosition(bool fullLayout, BidiIterator& start, BidiState& bidi)
1741{
1742 RootInlineBox* curr = 0;
1743 RootInlineBox* last = 0;
1744 RenderObject* startObj = 0;
1745 int pos = 0;
1746
1747 if (fullLayout) {
1748 // Nuke all our lines.
1749 if (firstRootBox()) {
1750 RenderArena* arena = renderArena();
1751 curr = firstRootBox();
1752 while (curr) {
1753 RootInlineBox* next = curr->nextRootBox();
1754 curr->deleteLine(arena);
1755 curr = next;
1756 }
1757 KHTMLAssert(!m_firstLineBox && !m_lastLineBox);
1758 }
eseidel789896f2005-11-27 22:52:09 +00001759 } else {
hyatt0c3a9862004-02-23 21:26:26 +00001760 for (curr = firstRootBox(); curr && !curr->isDirty(); curr = curr->nextRootBox());
1761 if (curr) {
1762 // We have a dirty line.
1763 if (curr->prevRootBox()) {
1764 // We have a previous line.
1765 if (!curr->prevRootBox()->endsWithBreak())
1766 curr = curr->prevRootBox(); // The previous line didn't break cleanly, so treat it as dirty also.
1767 }
eseidel789896f2005-11-27 22:52:09 +00001768 } else {
hyatt0c3a9862004-02-23 21:26:26 +00001769 // No dirty lines were found.
1770 // If the last line didn't break cleanly, treat it as dirty.
1771 if (lastRootBox() && !lastRootBox()->endsWithBreak())
1772 curr = lastRootBox();
1773 }
1774
1775 // If we have no dirty lines, then last is just the last root box.
1776 last = curr ? curr->prevRootBox() : lastRootBox();
1777 }
1778
1779 m_firstLine = !last;
1780 previousLineBrokeCleanly = !last || last->endsWithBreak();
1781 if (last) {
1782 m_height = last->blockHeight();
1783 int bottomOfLine = last->bottomOverflow();
1784 if (bottomOfLine > m_height && bottomOfLine > m_overflowHeight)
1785 m_overflowHeight = bottomOfLine;
1786 startObj = last->lineBreakObj();
1787 pos = last->lineBreakPos();
eseidel789896f2005-11-27 22:52:09 +00001788 bidi.status = last->lineBreakBidiStatus();
1789 bidi.context = last->lineBreakBidiContext();
darindde01502005-12-18 22:55:35 +00001790 } else {
1791 bidi.adjustEmbedding = true;
hyatt275d0702005-11-03 23:53:57 +00001792 startObj = bidiFirst(this, bidi, 0);
darindde01502005-12-18 22:55:35 +00001793 bidi.adjustEmbedding = false;
1794 }
hyatt0c3a9862004-02-23 21:26:26 +00001795
hyatt0c3a9862004-02-23 21:26:26 +00001796 start = BidiIterator(this, startObj, pos);
hyatt0c3a9862004-02-23 21:26:26 +00001797
1798 return curr;
1799}
1800
1801RootInlineBox* RenderBlock::determineEndPosition(RootInlineBox* startLine, BidiIterator& cleanLineStart,
eseidel789896f2005-11-27 22:52:09 +00001802 BidiStatus& cleanLineBidiStatus, BidiContext*& cleanLineBidiContext,
hyatt0c3a9862004-02-23 21:26:26 +00001803 int& yPos)
1804{
1805 RootInlineBox* last = 0;
hyatta6960b12004-12-07 02:09:10 +00001806 if (!startLine)
hyatt0c3a9862004-02-23 21:26:26 +00001807 last = 0;
1808 else {
hyatt04420ca2004-07-16 00:05:42 +00001809 for (RootInlineBox* curr = startLine->nextRootBox(); curr; curr = curr->nextRootBox()) {
1810 if (curr->isDirty())
1811 last = 0;
1812 else if (!last)
1813 last = curr;
1814 }
hyatt0c3a9862004-02-23 21:26:26 +00001815 }
1816
1817 if (!last)
1818 return 0;
1819
eseidel789896f2005-11-27 22:52:09 +00001820 RootInlineBox* prev = last->prevRootBox();
1821 cleanLineStart = BidiIterator(this, prev->lineBreakObj(), prev->lineBreakPos());
1822 cleanLineBidiStatus = prev->lineBreakBidiStatus();
1823 cleanLineBidiContext = prev->lineBreakBidiContext();
hyatt0c3a9862004-02-23 21:26:26 +00001824 yPos = last->prevRootBox()->blockHeight();
1825
1826 for (RootInlineBox* line = last; line; line = line->nextRootBox())
1827 line->extractLine(); // Disconnect all line boxes from their render objects while preserving
1828 // their connections to one another.
1829
1830 return last;
1831}
1832
eseidel789896f2005-11-27 22:52:09 +00001833bool RenderBlock::matchedEndLine(const BidiIterator& start, const BidiStatus& status, BidiContext* context,
1834 const BidiIterator& endLineStart, const BidiStatus& endLineStatus,
1835 BidiContext* endLineContext, RootInlineBox*& endLine, int& endYPos)
hyatt0c3a9862004-02-23 21:26:26 +00001836{
hyatt40731602005-04-18 18:12:42 +00001837 if (start == endLineStart)
eseidel789896f2005-11-27 22:52:09 +00001838 return status == endLineStatus && *context == *endLineContext;
hyatt40731602005-04-18 18:12:42 +00001839 else {
hyatt0c3a9862004-02-23 21:26:26 +00001840 // The first clean line doesn't match, but we can check a handful of following lines to try
1841 // to match back up.
1842 static int numLines = 8; // The # of lines we're willing to match against.
1843 RootInlineBox* line = endLine;
1844 for (int i = 0; i < numLines && line; i++, line = line->nextRootBox()) {
1845 if (line->lineBreakObj() == start.obj && line->lineBreakPos() == start.pos) {
1846 // We have a match.
eseidel789896f2005-11-27 22:52:09 +00001847 if (line->lineBreakBidiStatus() != status || *line->lineBreakBidiContext() != *context)
1848 return false; // ...but the bidi state doesn't match.
hyatt0c3a9862004-02-23 21:26:26 +00001849 RootInlineBox* result = line->nextRootBox();
1850
1851 // Set our yPos to be the block height of endLine.
1852 if (result)
1853 endYPos = line->blockHeight();
1854
1855 // Now delete the lines that we failed to sync.
1856 RootInlineBox* boxToDelete = endLine;
1857 RenderArena* arena = renderArena();
1858 while (boxToDelete && boxToDelete != result) {
1859 RootInlineBox* next = boxToDelete->nextRootBox();
1860 boxToDelete->deleteLine(arena);
1861 boxToDelete = next;
1862 }
1863
1864 endLine = result;
1865 return result;
1866 }
1867 }
1868 }
1869 return false;
1870}
1871
darin479ef852006-01-30 06:06:32 +00001872static const unsigned short nonBreakingSpace = 0xa0;
kociendae40cb942004-10-05 20:05:38 +00001873
hyattd9953212005-11-03 21:05:59 +00001874static inline bool skipNonBreakingSpace(BidiIterator &it)
kocienda98440082004-10-14 23:51:47 +00001875{
1876 if (it.obj->style()->nbspMode() != SPACE || it.current().unicode() != nonBreakingSpace)
1877 return false;
1878
hyattdca76e92005-11-02 08:52:50 +00001879 // FIXME: This is bad. It makes nbsp inconsistent with space and won't work correctly
1880 // with m_minWidth/m_maxWidth.
kocienda498d1982004-10-15 21:07:24 +00001881 // Do not skip a non-breaking space if it is the first character
hyattdca76e92005-11-02 08:52:50 +00001882 // on a line after a clean line break (or on the first line, since previousLineBrokeCleanly starts off
1883 // |true|).
1884 if (isLineEmpty && previousLineBrokeCleanly)
kocienda498d1982004-10-15 21:07:24 +00001885 return false;
1886
1887 return true;
kocienda98440082004-10-14 23:51:47 +00001888}
1889
hyattd9953212005-11-03 21:05:59 +00001890static inline bool shouldCollapseWhiteSpace(const RenderStyle* style)
1891{
1892 return style->collapseWhiteSpace() || (style->whiteSpace() == PRE_WRAP && (!isLineEmpty || !previousLineBrokeCleanly));
1893}
1894
kociendae40cb942004-10-05 20:05:38 +00001895int RenderBlock::skipWhitespace(BidiIterator &it, BidiState &bidi)
kociendabb0c24b2001-08-24 14:24:40 +00001896{
hyatt853cd7d2004-11-05 02:59:48 +00001897 // FIXME: The entire concept of the skipWhitespace function is flawed, since we really need to be building
1898 // line boxes even for containers that may ultimately collapse away. Otherwise we'll never get positioned
1899 // elements quite right. In other words, we need to build this function's work into the normal line
1900 // object iteration process.
1901 int w = lineWidth(m_height);
hyattd9953212005-11-03 21:05:59 +00001902 while (!it.atEnd() && (it.obj->isInlineFlow() || (shouldCollapseWhiteSpace(it.obj->style()) && !it.obj->isBR() &&
hyattdca76e92005-11-02 08:52:50 +00001903 (it.current() == ' ' || it.current() == '\t' || (!it.obj->style()->preserveNewline() && it.current() == '\n') ||
1904 it.current().unicode() == SOFT_HYPHEN || skipNonBreakingSpace(it) || it.obj->isFloatingOrPositioned())))) {
kociendae40cb942004-10-05 20:05:38 +00001905 if (it.obj->isFloatingOrPositioned()) {
1906 RenderObject *o = it.obj;
hyatt3b5905b2003-02-01 01:13:30 +00001907 // add to special objects...
hyatt98ee7e42003-05-14 01:39:15 +00001908 if (o->isFloating()) {
hyatt3ac01352003-03-22 01:37:33 +00001909 insertFloatingObject(o);
hyatt38e32632003-07-31 00:46:03 +00001910 positionNewFloats();
hyatt853cd7d2004-11-05 02:59:48 +00001911 w = lineWidth(m_height);
hyatt33f8d492002-11-12 21:44:52 +00001912 }
hyatt98ee7e42003-05-14 01:39:15 +00001913 else if (o->isPositioned()) {
hyatt853cd7d2004-11-05 02:59:48 +00001914 // FIXME: The math here is actually not really right. It's a best-guess approximation that
1915 // will work for the common cases
1916 RenderObject* c = o->container();
1917 if (c->isInlineFlow()) {
1918 // A relative positioned inline encloses us. In this case, we also have to determine our
1919 // position as though we were an inline. Set |staticX| and |staticY| on the relative positioned
1920 // inline so that we can obtain the value later.
1921 c->setStaticX(style()->direction() == LTR ?
1922 leftOffset(m_height) : rightOffset(m_height));
1923 c->setStaticY(m_height);
1924 }
1925
1926 if (o->hasStaticX()) {
1927 bool wasInline = o->style()->isOriginalDisplayInlineType();
1928 if (wasInline)
1929 o->setStaticX(style()->direction() == LTR ?
1930 leftOffset(m_height) :
1931 width() - rightOffset(m_height));
1932 else
1933 o->setStaticX(style()->direction() == LTR ?
1934 borderLeft() + paddingLeft() :
1935 borderRight() + paddingRight());
1936 }
hyatt98ee7e42003-05-14 01:39:15 +00001937 if (o->hasStaticY())
1938 o->setStaticY(m_height);
1939 }
hyatt33f8d492002-11-12 21:44:52 +00001940 }
hyatt4b381692003-03-10 21:11:59 +00001941
hyatt275d0702005-11-03 23:53:57 +00001942 bidi.adjustEmbedding = true;
kociendae40cb942004-10-05 20:05:38 +00001943 it.increment(bidi);
hyatt275d0702005-11-03 23:53:57 +00001944 bidi.adjustEmbedding = false;
mjs6f821c82002-03-22 00:31:57 +00001945 }
hyatt853cd7d2004-11-05 02:59:48 +00001946 return w;
kociendae40cb942004-10-05 20:05:38 +00001947}
1948
1949BidiIterator RenderBlock::findNextLineBreak(BidiIterator &start, BidiState &bidi)
1950{
thatcher459b94a2005-10-13 22:07:51 +00001951 // eliminate spaces at beginning of line
1952 int width = skipWhitespace(start, bidi);
kociendae40cb942004-10-05 20:05:38 +00001953 int w = 0;
1954 int tmpW = 0;
kociendae40cb942004-10-05 20:05:38 +00001955
hyatt853cd7d2004-11-05 02:59:48 +00001956 if (start.atEnd())
darinb70665a2002-04-15 23:43:21 +00001957 return start;
mjs6f821c82002-03-22 00:31:57 +00001958
hyatt33f8d492002-11-12 21:44:52 +00001959 // This variable is used only if whitespace isn't set to PRE, and it tells us whether
1960 // or not we are currently ignoring whitespace.
1961 bool ignoringSpaces = false;
hyatt98b16282004-03-31 18:43:12 +00001962 BidiIterator ignoreStart;
hyatt33f8d492002-11-12 21:44:52 +00001963
1964 // This variable tracks whether the very last character we saw was a space. We use
1965 // this to detect when we encounter a second space so we know we have to terminate
1966 // a run.
rjwc9c257d2003-01-24 03:46:17 +00001967 bool currentCharacterIsSpace = false;
harrisone343c412005-01-18 01:07:26 +00001968 bool currentCharacterIsWS = false;
hyatt33f8d492002-11-12 21:44:52 +00001969 RenderObject* trailingSpaceObject = 0;
hyatt98b16282004-03-31 18:43:12 +00001970
mjs6f821c82002-03-22 00:31:57 +00001971 BidiIterator lBreak = start;
1972
kociendabb0c24b2001-08-24 14:24:40 +00001973 RenderObject *o = start.obj;
1974 RenderObject *last = o;
bdakin9151ba52005-10-24 22:51:06 +00001975 RenderObject *previous = o;
kociendabb0c24b2001-08-24 14:24:40 +00001976 int pos = start.pos;
1977
hyatt0c3a9862004-02-23 21:26:26 +00001978 bool prevLineBrokeCleanly = previousLineBrokeCleanly;
1979 previousLineBrokeCleanly = false;
hyatt01eff982003-03-14 20:13:23 +00001980
hyatt275d0702005-11-03 23:53:57 +00001981 while (o) {
1982 if (o->isBR()) {
hyatt0c3a9862004-02-23 21:26:26 +00001983 if (w + tmpW <= width) {
kociendabb0c24b2001-08-24 14:24:40 +00001984 lBreak.obj = o;
1985 lBreak.pos = 0;
hyatt0c3a9862004-02-23 21:26:26 +00001986 lBreak.increment(bidi);
1987
hyatt33f8d492002-11-12 21:44:52 +00001988 // A <br> always breaks a line, so don't let the line be collapsed
1989 // away. Also, the space at the end of a line with a <br> does not
hyatt01eff982003-03-14 20:13:23 +00001990 // get collapsed away. It only does this if the previous line broke
1991 // cleanly. Otherwise the <br> has no effect on whether the line is
1992 // empty or not.
1993 if (prevLineBrokeCleanly)
1994 isLineEmpty = false;
hyatt33f8d492002-11-12 21:44:52 +00001995 trailingSpaceObject = 0;
hyatt0c3a9862004-02-23 21:26:26 +00001996 previousLineBrokeCleanly = true;
hyatt74eec4d2003-03-23 08:02:47 +00001997
1998 if (!isLineEmpty) {
1999 // only check the clear status for non-empty lines.
2000 EClear clear = o->style()->clear();
eseidel789896f2005-11-27 22:52:09 +00002001 if (clear != CNONE)
hyatt74eec4d2003-03-23 08:02:47 +00002002 m_clearStatus = (EClear) (m_clearStatus | clear);
kociendabb0c24b2001-08-24 14:24:40 +00002003 }
2004 }
2005 goto end;
2006 }
hyatt275d0702005-11-03 23:53:57 +00002007 if (o->isFloatingOrPositioned()) {
kociendabb0c24b2001-08-24 14:24:40 +00002008 // add to special objects...
hyatt275d0702005-11-03 23:53:57 +00002009 if (o->isFloating()) {
hyatt3ac01352003-03-22 01:37:33 +00002010 insertFloatingObject(o);
hyatt33f8d492002-11-12 21:44:52 +00002011 // check if it fits in the current line.
2012 // If it does, position it now, otherwise, position
2013 // it after moving to next line (in newLine() func)
2014 if (o->width()+o->marginLeft()+o->marginRight()+w+tmpW <= width) {
2015 positionNewFloats();
2016 width = lineWidth(m_height);
2017 }
hyatt275d0702005-11-03 23:53:57 +00002018 } else if (o->isPositioned()) {
hyatt851816b2003-07-08 07:54:17 +00002019 // If our original display wasn't an inline type, then we can
hyatt98ee7e42003-05-14 01:39:15 +00002020 // go ahead and determine our static x position now.
hyatt851816b2003-07-08 07:54:17 +00002021 bool isInlineType = o->style()->isOriginalDisplayInlineType();
hyatt98ee7e42003-05-14 01:39:15 +00002022 bool needToSetStaticX = o->hasStaticX();
2023 if (o->hasStaticX() && !isInlineType) {
2024 o->setStaticX(o->parent()->style()->direction() == LTR ?
2025 borderLeft()+paddingLeft() :
2026 borderRight()+paddingRight());
2027 needToSetStaticX = false;
2028 }
2029
2030 // If our original display was an INLINE type, then we can go ahead
2031 // and determine our static y position now.
2032 bool needToSetStaticY = o->hasStaticY();
2033 if (o->hasStaticY() && isInlineType) {
2034 o->setStaticY(m_height);
2035 needToSetStaticY = false;
2036 }
2037
hyatt853cd7d2004-11-05 02:59:48 +00002038 bool needToCreateLineBox = needToSetStaticX || needToSetStaticY;
2039 RenderObject* c = o->container();
2040 if (c->isInlineFlow() && (!needToSetStaticX || !needToSetStaticY))
2041 needToCreateLineBox = true;
2042
hyatt98ee7e42003-05-14 01:39:15 +00002043 // If we're ignoring spaces, we have to stop and include this object and
2044 // then start ignoring spaces again.
hyatt853cd7d2004-11-05 02:59:48 +00002045 if (needToCreateLineBox) {
hyattbc7f07f2003-05-27 20:04:26 +00002046 trailingSpaceObject = 0;
hyatt98b16282004-03-31 18:43:12 +00002047 ignoreStart.obj = o;
2048 ignoreStart.pos = 0;
hyattbc7f07f2003-05-27 20:04:26 +00002049 if (ignoringSpaces) {
hyatt98b16282004-03-31 18:43:12 +00002050 addMidpoint(ignoreStart); // Stop ignoring spaces.
2051 addMidpoint(ignoreStart); // Start ignoring again.
hyattbc7f07f2003-05-27 20:04:26 +00002052 }
hyatt98b16282004-03-31 18:43:12 +00002053
hyatt851816b2003-07-08 07:54:17 +00002054 }
hyatt98ee7e42003-05-14 01:39:15 +00002055 }
hyattffe78712003-02-11 01:59:29 +00002056 } else if (o->isInlineFlow()) {
2057 // Only empty inlines matter. We treat those similarly to replaced elements.
hyatt275d0702005-11-03 23:53:57 +00002058 assert(!o->firstChild());
hyattffe78712003-02-11 01:59:29 +00002059 tmpW += o->marginLeft()+o->borderLeft()+o->paddingLeft()+
2060 o->marginRight()+o->borderRight()+o->paddingRight();
hyatt275d0702005-11-03 23:53:57 +00002061 } else if (o->isReplaced()) {
hyattde396342003-10-29 08:57:20 +00002062 EWhiteSpace currWS = o->style()->whiteSpace();
2063 EWhiteSpace lastWS = last->style()->whiteSpace();
2064
2065 // WinIE marquees have different whitespace characteristics by default when viewed from
2066 // the outside vs. the inside. Text inside is NOWRAP, and so we altered the marquee's
2067 // style to reflect this, but we now have to get back to the original whitespace value
2068 // for the marquee when checking for line breaking.
2069 if (o->isHTMLMarquee() && o->layer() && o->layer()->marquee())
2070 currWS = o->layer()->marquee()->whiteSpace();
2071 if (last->isHTMLMarquee() && last->layer() && last->layer()->marquee())
2072 lastWS = last->layer()->marquee()->whiteSpace();
2073
2074 // Break on replaced elements if either has normal white-space.
2075 // FIXME: This does not match WinIE, Opera, and Mozilla. They treat replaced elements
2076 // like characters in a word, and require spaces between the replaced elements in order
2077 // to break.
hyattdca76e92005-11-02 08:52:50 +00002078 if (RenderStyle::autoWrap(currWS) || RenderStyle::autoWrap(lastWS)) {
hyatt711fe232002-11-20 21:25:14 +00002079 w += tmpW;
2080 tmpW = 0;
hyattf14a4a32002-11-21 22:06:32 +00002081 lBreak.obj = o;
2082 lBreak.pos = 0;
hyatt711fe232002-11-20 21:25:14 +00002083 }
2084
hyatt33f8d492002-11-12 21:44:52 +00002085 if (ignoringSpaces) {
mjsfe301d72003-10-02 18:46:18 +00002086 BidiIterator startMid( 0, o, 0 );
hyatt85586af2003-02-19 23:22:42 +00002087 addMidpoint(startMid);
hyatt33f8d492002-11-12 21:44:52 +00002088 }
2089 isLineEmpty = false;
2090 ignoringSpaces = false;
rjwc9c257d2003-01-24 03:46:17 +00002091 currentCharacterIsSpace = false;
harrisone343c412005-01-18 01:07:26 +00002092 currentCharacterIsWS = false;
hyatt33f8d492002-11-12 21:44:52 +00002093 trailingSpaceObject = 0;
hyatte85e4a72002-12-08 02:06:16 +00002094
2095 if (o->isListMarker() && o->style()->listStylePosition() == OUTSIDE) {
2096 // The marker must not have an effect on whitespace at the start
2097 // of the line. We start ignoring spaces to make sure that any additional
2098 // spaces we see will be discarded.
2099 //
2100 // Optimize for a common case. If we can't find whitespace after the list
2101 // item, then this is all moot. -dwh
hyatt275d0702005-11-03 23:53:57 +00002102 RenderObject* next = bidiNext(start.block, o, bidi);
harrison8d773e72006-01-29 15:06:17 +00002103 if (style()->collapseWhiteSpace() && next && !next->isBR() && next->isText() && static_cast<RenderText*>(next)->stringLength() > 0) {
harrison208ea792005-07-29 23:42:59 +00002104 RenderText *nextText = static_cast<RenderText*>(next);
2105 QChar nextChar = nextText->text()[0];
2106
hyattdca76e92005-11-02 08:52:50 +00002107 if (nextText->style()->isCollapsibleWhiteSpace(nextChar)) {
harrisone343c412005-01-18 01:07:26 +00002108 currentCharacterIsSpace = true;
2109 currentCharacterIsWS = true;
2110 ignoringSpaces = true;
2111 BidiIterator endMid( 0, o, 0 );
2112 addMidpoint(endMid);
2113 }
hyatte85e4a72002-12-08 02:06:16 +00002114 }
justing244d3a32006-04-13 01:31:24 +00002115 } else
2116 tmpW += o->width()+o->marginLeft()+o->marginRight()+inlineWidth(o);
eseidel789896f2005-11-27 22:52:09 +00002117 } else if (o->isText()) {
hyatt33f8d492002-11-12 21:44:52 +00002118 RenderText *t = static_cast<RenderText *>(o);
2119 int strlen = t->stringLength();
2120 int len = strlen - pos;
darin9d0a6282006-03-01 07:49:33 +00002121 const QChar* str = t->text();
kociendabb0c24b2001-08-24 14:24:40 +00002122
hyattecad67c2006-03-01 06:16:38 +00002123 const Font *f = t->font(m_firstLine);
hyatt33f8d492002-11-12 21:44:52 +00002124 // proportional font, needs a bit more work.
2125 int lastSpace = pos;
hyatt3aff2332003-01-23 01:15:28 +00002126 int wordSpacing = o->style()->wordSpacing();
eseideld13fe532005-11-30 02:40:29 +00002127 int lastSpaceWordSpacing = 0;
hyattffe78712003-02-11 01:59:29 +00002128
2129 bool appliedStartWidth = pos > 0; // If the span originated on a previous line,
2130 // then assume the start width has been applied.
darin54008922006-01-13 16:39:05 +00002131 int wrapW = tmpW + inlineWidth(o, !appliedStartWidth, true);
darin47ece0d2005-09-04 07:42:31 +00002132 int nextBreakable = -1;
kociendae4134242004-10-25 18:48:44 +00002133
hyatt275d0702005-11-03 23:53:57 +00002134 while (len) {
rjwc9c257d2003-01-24 03:46:17 +00002135 bool previousCharacterIsSpace = currentCharacterIsSpace;
harrisone343c412005-01-18 01:07:26 +00002136 bool previousCharacterIsWS = currentCharacterIsWS;
darina3c48282003-10-07 15:49:30 +00002137 const QChar c = str[pos];
hyattdca76e92005-11-02 08:52:50 +00002138 currentCharacterIsSpace = c == ' ' || c == '\t' || (!o->style()->preserveNewline() && (c == '\n'));
darin47ece0d2005-09-04 07:42:31 +00002139
hyatt7555cbd2005-12-07 22:48:56 +00002140 if (!o->style()->collapseWhiteSpace() || !currentCharacterIsSpace)
hyatt33f8d492002-11-12 21:44:52 +00002141 isLineEmpty = false;
hyatt3aff2332003-01-23 01:15:28 +00002142
hyatt78b85132004-03-29 20:07:45 +00002143 // Check for soft hyphens. Go ahead and ignore them.
bdakin9151ba52005-10-24 22:51:06 +00002144 if (c.unicode() == SOFT_HYPHEN) {
hyatt78b85132004-03-29 20:07:45 +00002145 if (!ignoringSpaces) {
2146 // Ignore soft hyphens
bdakin9151ba52005-10-24 22:51:06 +00002147 BidiIterator endMid;
2148 if (pos > 0)
2149 endMid = BidiIterator(0, o, pos-1);
2150 else
2151 endMid = BidiIterator(0, previous, previous->isText() ? static_cast<RenderText *>(previous)->stringLength() - 1 : 0);
2152 // Two consecutive soft hyphens. Avoid overlapping midpoints.
2153 if (sNumMidpoints && smidpoints->at(sNumMidpoints - 1).obj == endMid.obj && smidpoints->at(sNumMidpoints - 1).pos > endMid.pos)
2154 sNumMidpoints--;
2155 else
2156 addMidpoint(endMid);
hyatt78b85132004-03-29 20:07:45 +00002157
2158 // Add the width up to but not including the hyphen.
eseideld13fe532005-11-30 02:40:29 +00002159 tmpW += t->width(lastSpace, pos - lastSpace, f, w+tmpW) + lastSpaceWordSpacing;
hyatt78b85132004-03-29 20:07:45 +00002160
hyattdca76e92005-11-02 08:52:50 +00002161 // For wrapping text only, include the hyphen. We need to ensure it will fit
hyatt78b85132004-03-29 20:07:45 +00002162 // on the line if it shows when we break.
hyattdca76e92005-11-02 08:52:50 +00002163 if (o->style()->autoWrap())
harrison208ea792005-07-29 23:42:59 +00002164 tmpW += t->width(pos, 1, f, w+tmpW);
hyatt78b85132004-03-29 20:07:45 +00002165
2166 BidiIterator startMid(0, o, pos+1);
2167 addMidpoint(startMid);
2168 }
2169
2170 pos++;
2171 len--;
eseideld13fe532005-11-30 02:40:29 +00002172 lastSpaceWordSpacing = 0;
hyatt78b85132004-03-29 20:07:45 +00002173 lastSpace = pos; // Cheesy hack to prevent adding in widths of the run twice.
2174 continue;
2175 }
2176
hyatt3aff2332003-01-23 01:15:28 +00002177 bool applyWordSpacing = false;
hyattdca76e92005-11-02 08:52:50 +00002178 bool allowBreak = o->style()->autoWrap();
2179 bool breakNBSP = allowBreak && o->style()->nbspMode() == SPACE;
2180
2181 // FIXME: This check looks suspicious. Why does w have to be 0?
2182 bool breakWords = o->style()->wordWrap() == BREAK_WORD && ((allowBreak && w == 0) || o->style()->whiteSpace() == PRE);
harrisone343c412005-01-18 01:07:26 +00002183
2184 currentCharacterIsWS = currentCharacterIsSpace || (breakNBSP && c.unicode() == nonBreakingSpace);
2185
kociendae4ebdd92004-09-29 21:55:48 +00002186 if (breakWords)
harrison208ea792005-07-29 23:42:59 +00002187 wrapW += t->width(pos, 1, f, w+wrapW);
darin54008922006-01-13 16:39:05 +00002188 bool midWordBreak = breakWords && (w + wrapW > width);
darin47ece0d2005-09-04 07:42:31 +00002189
darin54008922006-01-13 16:39:05 +00002190 if (c == '\n' || (o->style()->whiteSpace() != PRE && isBreakable(str, pos, strlen, nextBreakable, breakNBSP)) || midWordBreak) {
hyatt0c05e102006-04-14 08:15:00 +00002191 bool stoppedIgnoringSpaces = false;
hyatt33f8d492002-11-12 21:44:52 +00002192 if (ignoringSpaces) {
rjwc9c257d2003-01-24 03:46:17 +00002193 if (!currentCharacterIsSpace) {
hyatt33f8d492002-11-12 21:44:52 +00002194 // Stop ignoring spaces and begin at this
2195 // new point.
hyatt48710d62003-08-21 09:17:13 +00002196 ignoringSpaces = false;
eseideld13fe532005-11-30 02:40:29 +00002197 lastSpaceWordSpacing = 0;
hyatt48710d62003-08-21 09:17:13 +00002198 lastSpace = pos; // e.g., "Foo goo", don't add in any of the ignored spaces.
mjsfe301d72003-10-02 18:46:18 +00002199 BidiIterator startMid ( 0, o, pos );
hyatt85586af2003-02-19 23:22:42 +00002200 addMidpoint(startMid);
hyatt0c05e102006-04-14 08:15:00 +00002201 stoppedIgnoringSpaces = true;
harrisone343c412005-01-18 01:07:26 +00002202 } else {
hyatt33f8d492002-11-12 21:44:52 +00002203 // Just keep ignoring these spaces.
2204 pos++;
2205 len--;
2206 continue;
2207 }
2208 }
rjwc9c257d2003-01-24 03:46:17 +00002209
darin54008922006-01-13 16:39:05 +00002210 int additionalTmpW = t->width(lastSpace, pos - lastSpace, f, w+tmpW) + lastSpaceWordSpacing;
2211 tmpW += additionalTmpW;
hyattffe78712003-02-11 01:59:29 +00002212 if (!appliedStartWidth) {
2213 tmpW += inlineWidth(o, true, false);
2214 appliedStartWidth = true;
2215 }
2216
eseideld13fe532005-11-30 02:40:29 +00002217 applyWordSpacing = wordSpacing && currentCharacterIsSpace && !previousCharacterIsSpace;
hyatt3aff2332003-01-23 01:15:28 +00002218
hyattdca76e92005-11-02 08:52:50 +00002219 if (o->style()->autoWrap() && w + tmpW > width && w == 0) {
hyatte2b14092003-01-23 23:33:39 +00002220 int fb = nearestFloatBottom(m_height);
hyatt33f8d492002-11-12 21:44:52 +00002221 int newLineWidth = lineWidth(fb);
hyatt2df52192003-03-28 22:05:02 +00002222 // See if |tmpW| will fit on the new line. As long as it does not,
2223 // keep adjusting our float bottom until we find some room.
2224 int lastFloatBottom = m_height;
2225 while (lastFloatBottom < fb && tmpW > newLineWidth) {
2226 lastFloatBottom = fb;
2227 fb = nearestFloatBottom(fb);
2228 newLineWidth = lineWidth(fb);
2229 }
2230
hyatt275d0702005-11-03 23:53:57 +00002231 if (!w && m_height < fb && width < newLineWidth) {
hyatt33f8d492002-11-12 21:44:52 +00002232 m_height = fb;
2233 width = newLineWidth;
hyatt33f8d492002-11-12 21:44:52 +00002234 }
2235 }
2236
hyattdca76e92005-11-02 08:52:50 +00002237 if (o->style()->autoWrap() || breakWords) {
2238 // If we break only after white-space, consider the current character
kociendae4134242004-10-25 18:48:44 +00002239 // as candidate width for this line.
eseideld13fe532005-11-30 02:40:29 +00002240 int charWidth = o->style()->breakOnlyAfterWhiteSpace() ?
2241 t->width(pos, 1, f, w + tmpW) + (applyWordSpacing ? wordSpacing : 0) : 0;
kocienda9dbe9b12004-10-22 20:07:05 +00002242 if (w + tmpW + charWidth > width) {
hyattdca76e92005-11-02 08:52:50 +00002243 if (o->style()->breakOnlyAfterWhiteSpace()) {
kociendae4134242004-10-25 18:48:44 +00002244 // Check if line is too big even without the extra space
2245 // at the end of the line. If it is not, do nothing.
2246 // If the line needs the extra whitespace to be too long,
2247 // then move the line break to the space and skip all
2248 // additional whitespace.
harrison07b5e582005-08-15 23:31:16 +00002249 if (w + tmpW <= width) {
kociendae4134242004-10-25 18:48:44 +00002250 lBreak.obj = o;
2251 lBreak.pos = pos;
2252 skipWhitespace(lBreak, bidi);
2253 }
kocienda9dbe9b12004-10-22 20:07:05 +00002254 }
adele7fc3e832006-02-17 09:31:35 +00002255 if (lBreak.obj && lBreak.obj->style()->preserveNewline() && lBreak.obj->isText() && static_cast<RenderText*>(lBreak.obj)->text()[lBreak.pos] == '\n') {
hyatt0c05e102006-04-14 08:15:00 +00002256 if (!stoppedIgnoringSpaces && pos > 0) {
2257 // We need to stop right before the newline and then start up again.
2258 BidiIterator midpoint(0, o, pos);
2259 addMidpoint(BidiIterator(0, o, pos-1)); // Stop
2260 addMidpoint(BidiIterator(0, o, pos)); // Start
2261 }
2262 lBreak.increment(bidi);
adele7fc3e832006-02-17 09:31:35 +00002263 previousLineBrokeCleanly = true;
adele7fc3e832006-02-17 09:31:35 +00002264 }
hyatt78b85132004-03-29 20:07:45 +00002265 goto end; // Didn't fit. Jump to the end.
darin54008922006-01-13 16:39:05 +00002266 } else {
2267 if (midWordBreak)
2268 tmpW -= additionalTmpW;
2269 if (pos > 0 && str[pos-1].unicode() == SOFT_HYPHEN)
2270 // Subtract the width of the soft hyphen out since we fit on a line.
2271 tmpW -= t->width(pos-1, 1, f, w+tmpW);
2272 }
rjwc9c257d2003-01-24 03:46:17 +00002273 }
hyatt33f8d492002-11-12 21:44:52 +00002274
adele7fc3e832006-02-17 09:31:35 +00002275 if (c == '\n' && o->style()->preserveNewline()) {
hyatt0c05e102006-04-14 08:15:00 +00002276 if (!stoppedIgnoringSpaces && pos > 0) {
2277 // We need to stop right before the newline and then start up again.
2278 BidiIterator midpoint(0, o, pos);
2279 addMidpoint(BidiIterator(0, o, pos-1)); // Stop
2280 addMidpoint(BidiIterator(0, o, pos)); // Start
2281 }
hyatta9f48e32003-02-03 22:48:01 +00002282 lBreak.obj = o;
2283 lBreak.pos = pos;
hyatt0c05e102006-04-14 08:15:00 +00002284 lBreak.increment(bidi);
adele7fc3e832006-02-17 09:31:35 +00002285 previousLineBrokeCleanly = true;
hyatt33f8d492002-11-12 21:44:52 +00002286 return lBreak;
2287 }
hyatta9f48e32003-02-03 22:48:01 +00002288
hyattdca76e92005-11-02 08:52:50 +00002289 if (o->style()->autoWrap()) {
hyatta9f48e32003-02-03 22:48:01 +00002290 w += tmpW;
2291 tmpW = 0;
2292 lBreak.obj = o;
2293 lBreak.pos = pos;
2294 }
hyatt33f8d492002-11-12 21:44:52 +00002295
darin54008922006-01-13 16:39:05 +00002296 if (midWordBreak) {
2297 // Remember this as a breakable position in case
2298 // adding the end width forces a break.
2299 lBreak.obj = o;
2300 lBreak.pos = pos;
2301 } else {
2302 lastSpaceWordSpacing = applyWordSpacing ? wordSpacing : 0;
2303 lastSpace = pos;
2304 }
hyatt33f8d492002-11-12 21:44:52 +00002305
hyattdca76e92005-11-02 08:52:50 +00002306 if (!ignoringSpaces && o->style()->collapseWhiteSpace()) {
hyatt33f8d492002-11-12 21:44:52 +00002307 // If we encounter a newline, or if we encounter a
2308 // second space, we need to go ahead and break up this
2309 // run and enter a mode where we start collapsing spaces.
hyatt98b16282004-03-31 18:43:12 +00002310 if (currentCharacterIsSpace && previousCharacterIsSpace) {
hyatt33f8d492002-11-12 21:44:52 +00002311 ignoringSpaces = true;
hyatt98b16282004-03-31 18:43:12 +00002312
hyatt33f8d492002-11-12 21:44:52 +00002313 // We just entered a mode where we are ignoring
2314 // spaces. Create a midpoint to terminate the run
2315 // before the second space.
hyatt98b16282004-03-31 18:43:12 +00002316 addMidpoint(ignoreStart);
hyatt33f8d492002-11-12 21:44:52 +00002317 }
2318 }
eseidel789896f2005-11-27 22:52:09 +00002319 } else if (ignoringSpaces) {
hyatt33f8d492002-11-12 21:44:52 +00002320 // Stop ignoring spaces and begin at this
2321 // new point.
2322 ignoringSpaces = false;
eseideld13fe532005-11-30 02:40:29 +00002323 lastSpaceWordSpacing = applyWordSpacing ? wordSpacing : 0;
hyatt33f8d492002-11-12 21:44:52 +00002324 lastSpace = pos; // e.g., "Foo goo", don't add in any of the ignored spaces.
eseidel789896f2005-11-27 22:52:09 +00002325 BidiIterator startMid(0, o, pos);
hyatt85586af2003-02-19 23:22:42 +00002326 addMidpoint(startMid);
hyatt33f8d492002-11-12 21:44:52 +00002327 }
hyatt98b16282004-03-31 18:43:12 +00002328
2329 if (currentCharacterIsSpace && !previousCharacterIsSpace) {
2330 ignoreStart.obj = o;
2331 ignoreStart.pos = pos;
2332 }
harrisone343c412005-01-18 01:07:26 +00002333
2334 if (!currentCharacterIsWS && previousCharacterIsWS) {
hyattdca76e92005-11-02 08:52:50 +00002335 if (o->style()->autoWrap() && o->style()->breakOnlyAfterWhiteSpace()) {
harrisone343c412005-01-18 01:07:26 +00002336 lBreak.obj = o;
2337 lBreak.pos = pos;
2338 }
2339 }
hyatt33f8d492002-11-12 21:44:52 +00002340
hyattdca76e92005-11-02 08:52:50 +00002341 if (o->style()->collapseWhiteSpace() && currentCharacterIsSpace && !ignoringSpaces)
hyatt33f8d492002-11-12 21:44:52 +00002342 trailingSpaceObject = o;
hyattdca76e92005-11-02 08:52:50 +00002343 else if (!o->style()->collapseWhiteSpace() || !currentCharacterIsSpace)
hyatt33f8d492002-11-12 21:44:52 +00002344 trailingSpaceObject = 0;
2345
2346 pos++;
2347 len--;
2348 }
2349
kociendabb0c24b2001-08-24 14:24:40 +00002350 // IMPORTANT: pos is > length here!
hyatt33f8d492002-11-12 21:44:52 +00002351 if (!ignoringSpaces)
eseideld13fe532005-11-30 02:40:29 +00002352 tmpW += t->width(lastSpace, pos - lastSpace, f, w+tmpW) + lastSpaceWordSpacing;
hyattffe78712003-02-11 01:59:29 +00002353 if (!appliedStartWidth)
2354 tmpW += inlineWidth(o, true, false);
darin54008922006-01-13 16:39:05 +00002355 tmpW += inlineWidth(o, false, true);
kociendabb0c24b2001-08-24 14:24:40 +00002356 } else
mjs6f821c82002-03-22 00:31:57 +00002357 KHTMLAssert( false );
kociendabb0c24b2001-08-24 14:24:40 +00002358
hyatt275d0702005-11-03 23:53:57 +00002359 RenderObject* next = bidiNext(start.block, o, bidi);
hyattdca76e92005-11-02 08:52:50 +00002360 bool autoWrap = o->style()->autoWrap();
2361 bool checkForBreak = autoWrap;
darin54008922006-01-13 16:39:05 +00002362 if (w && w + tmpW > width && lBreak.obj && o->style()->whiteSpace() == NOWRAP)
hyatt74eec4d2003-03-23 08:02:47 +00002363 checkForBreak = true;
2364 else if (next && o->isText() && next->isText() && !next->isBR()) {
hyattdca76e92005-11-02 08:52:50 +00002365 if (autoWrap || (next->style()->autoWrap())) {
hyatta9f48e32003-02-03 22:48:01 +00002366 if (currentCharacterIsSpace)
2367 checkForBreak = true;
2368 else {
harrison07b5e582005-08-15 23:31:16 +00002369 checkForBreak = false;
hyatta9f48e32003-02-03 22:48:01 +00002370 RenderText* nextText = static_cast<RenderText*>(next);
harrison07b5e582005-08-15 23:31:16 +00002371 if (nextText->stringLength() != 0) {
eseideld13fe532005-11-30 02:40:29 +00002372 QChar c = nextText->text()[0];
hyattdca76e92005-11-02 08:52:50 +00002373 if (c == ' ' || c == '\t' || (c == '\n' && !next->style()->preserveNewline())) {
eseideld13fe532005-11-30 02:40:29 +00002374 // If the next item on the line is text, and if we did not end with
2375 // a space, then the next text run continues our word (and so it needs to
2376 // keep adding to |tmpW|. Just update and continue.
2377 checkForBreak = true;
harrison07b5e582005-08-15 23:31:16 +00002378 }
2379 }
darin54008922006-01-13 16:39:05 +00002380 bool canPlaceOnLine = (w + tmpW <= width) || !autoWrap;
hyatta9f48e32003-02-03 22:48:01 +00002381 if (canPlaceOnLine && checkForBreak) {
2382 w += tmpW;
2383 tmpW = 0;
2384 lBreak.obj = next;
2385 lBreak.pos = 0;
2386 }
2387 }
2388 }
2389 }
2390
darin54008922006-01-13 16:39:05 +00002391 if (checkForBreak && (w + tmpW > width)) {
kociendabb0c24b2001-08-24 14:24:40 +00002392 // if we have floats, try to get below them.
hyattdca76e92005-11-02 08:52:50 +00002393 if (currentCharacterIsSpace && !ignoringSpaces && o->style()->collapseWhiteSpace())
hyatt33f8d492002-11-12 21:44:52 +00002394 trailingSpaceObject = 0;
hyatta14d1742003-01-02 20:25:46 +00002395
hyatte2b14092003-01-23 23:33:39 +00002396 int fb = nearestFloatBottom(m_height);
hyatt33f8d492002-11-12 21:44:52 +00002397 int newLineWidth = lineWidth(fb);
hyatt2df52192003-03-28 22:05:02 +00002398 // See if |tmpW| will fit on the new line. As long as it does not,
2399 // keep adjusting our float bottom until we find some room.
2400 int lastFloatBottom = m_height;
2401 while (lastFloatBottom < fb && tmpW > newLineWidth) {
2402 lastFloatBottom = fb;
2403 fb = nearestFloatBottom(fb);
2404 newLineWidth = lineWidth(fb);
2405 }
hyatt275d0702005-11-03 23:53:57 +00002406 if (!w && m_height < fb && width < newLineWidth) {
kociendabb0c24b2001-08-24 14:24:40 +00002407 m_height = fb;
darinb70665a2002-04-15 23:43:21 +00002408 width = newLineWidth;
kociendabb0c24b2001-08-24 14:24:40 +00002409 }
hyattf14a4a32002-11-21 22:06:32 +00002410
hyatta14d1742003-01-02 20:25:46 +00002411 // |width| may have been adjusted because we got shoved down past a float (thus
2412 // giving us more room), so we need to retest, and only jump to
2413 // the end label if we still don't fit on the line. -dwh
darin54008922006-01-13 16:39:05 +00002414 if (w + tmpW > width)
hyatta14d1742003-01-02 20:25:46 +00002415 goto end;
kociendabb0c24b2001-08-24 14:24:40 +00002416 }
hyatt1d9e29b2003-04-10 01:48:53 +00002417
hyattf14a4a32002-11-21 22:06:32 +00002418 last = o;
bdakin9151ba52005-10-24 22:51:06 +00002419 if (!o->isFloating() && (!o->isPositioned() || o->hasStaticX() || o->hasStaticY() || !o->container()->isInlineFlow()))
2420 previous = o;
hyatta9f48e32003-02-03 22:48:01 +00002421 o = next;
hyattf14a4a32002-11-21 22:06:32 +00002422
hyattdca76e92005-11-02 08:52:50 +00002423 if (!last->isFloatingOrPositioned() && last->isReplaced() && last->style()->autoWrap() &&
bdakina964eb32005-10-24 17:47:26 +00002424 (!last->isListMarker() || last->style()->listStylePosition()==INSIDE)) {
hyatt711fe232002-11-20 21:25:14 +00002425 // Go ahead and add in tmpW.
2426 w += tmpW;
2427 tmpW = 0;
2428 lBreak.obj = o;
2429 lBreak.pos = 0;
2430 }
2431
hyatta9f48e32003-02-03 22:48:01 +00002432 // Clear out our character space bool, since inline <pre>s don't collapse whitespace
2433 // with adjacent inline normal/nowrap spans.
hyattdca76e92005-11-02 08:52:50 +00002434 if (!last->style()->collapseWhiteSpace())
hyatta9f48e32003-02-03 22:48:01 +00002435 currentCharacterIsSpace = false;
2436
kociendabb0c24b2001-08-24 14:24:40 +00002437 pos = 0;
2438 }
2439
hyatt275d0702005-11-03 23:53:57 +00002440 if (w + tmpW <= width || (last && last->style()->whiteSpace() == NOWRAP)) {
kociendabb0c24b2001-08-24 14:24:40 +00002441 lBreak.obj = 0;
2442 lBreak.pos = 0;
2443 }
2444
2445 end:
rjwc9c257d2003-01-24 03:46:17 +00002446
hyatt275d0702005-11-03 23:53:57 +00002447 if (lBreak == start && !lBreak.obj->isBR()) {
kociendabb0c24b2001-08-24 14:24:40 +00002448 // we just add as much as possible
hyattdca76e92005-11-02 08:52:50 +00002449 if (style()->whiteSpace() == PRE) {
2450 // FIXME: Don't really understand this case.
hyatt275d0702005-11-03 23:53:57 +00002451 if (pos != 0) {
kociendabb0c24b2001-08-24 14:24:40 +00002452 lBreak.obj = o;
2453 lBreak.pos = pos - 1;
2454 } else {
2455 lBreak.obj = last;
hyattc3731d42002-12-12 06:20:22 +00002456 lBreak.pos = last->isText() ? last->length() : 0;
kociendabb0c24b2001-08-24 14:24:40 +00002457 }
hyatt275d0702005-11-03 23:53:57 +00002458 } else if (lBreak.obj) {
2459 if (last != o && !last->isListMarker()) {
bdakina964eb32005-10-24 17:47:26 +00002460 // better to break between object boundaries than in the middle of a word (except for list markers)
hyatt33f8d492002-11-12 21:44:52 +00002461 lBreak.obj = o;
2462 lBreak.pos = 0;
2463 } else {
hyattdda1d1b2002-12-13 09:44:17 +00002464 // Don't ever break in the middle of a word if we can help it.
2465 // There's no room at all. We just have to be on this line,
2466 // even though we'll spill out.
2467 lBreak.obj = o;
2468 lBreak.pos = pos;
hyatt33f8d492002-11-12 21:44:52 +00002469 }
kociendabb0c24b2001-08-24 14:24:40 +00002470 }
2471 }
2472
2473 // make sure we consume at least one char/object.
hyatt275d0702005-11-03 23:53:57 +00002474 if (lBreak == start)
mjsfe301d72003-10-02 18:46:18 +00002475 lBreak.increment(bidi);
hyatt33f8d492002-11-12 21:44:52 +00002476
hyattfe99c872003-07-31 22:25:29 +00002477 // Sanity check our midpoints.
mjsfe301d72003-10-02 18:46:18 +00002478 checkMidpoints(lBreak, bidi);
hyattfe99c872003-07-31 22:25:29 +00002479
hyatt33f8d492002-11-12 21:44:52 +00002480 if (trailingSpaceObject) {
2481 // This object is either going to be part of the last midpoint, or it is going
2482 // to be the actual endpoint. In both cases we just decrease our pos by 1 level to
2483 // exclude the space, allowing it to - in effect - collapse into the newline.
hyatt85586af2003-02-19 23:22:42 +00002484 if (sNumMidpoints%2==1) {
2485 BidiIterator* midpoints = smidpoints->data();
2486 midpoints[sNumMidpoints-1].pos--;
hyatt33f8d492002-11-12 21:44:52 +00002487 }
2488 //else if (lBreak.pos > 0)
2489 // lBreak.pos--;
2490 else if (lBreak.obj == 0 && trailingSpaceObject->isText()) {
hyattd20075d2002-11-16 02:23:32 +00002491 // Add a new end midpoint that stops right at the very end.
hyattd20075d2002-11-16 02:23:32 +00002492 RenderText* text = static_cast<RenderText *>(trailingSpaceObject);
darin8341f372003-04-29 18:30:31 +00002493 unsigned pos = text->length() >=2 ? text->length() - 2 : UINT_MAX;
eseidel789896f2005-11-27 22:52:09 +00002494 BidiIterator endMid(0, trailingSpaceObject, pos);
hyatt85586af2003-02-19 23:22:42 +00002495 addMidpoint(endMid);
hyatt33f8d492002-11-12 21:44:52 +00002496 }
2497 }
rjwc9c257d2003-01-24 03:46:17 +00002498
mjs54b64002003-04-02 02:59:02 +00002499 // We might have made lBreak an iterator that points past the end
2500 // of the object. Do this adjustment to make it point to the start
2501 // of the next object instead to avoid confusing the rest of the
2502 // code.
2503 if (lBreak.pos > 0) {
darin54008922006-01-13 16:39:05 +00002504 lBreak.pos--;
2505 lBreak.increment(bidi);
mjs54b64002003-04-02 02:59:02 +00002506 }
2507
hyatt78b85132004-03-29 20:07:45 +00002508 if (lBreak.obj && lBreak.pos >= 2 && lBreak.obj->isText()) {
2509 // For soft hyphens on line breaks, we have to chop out the midpoints that made us
2510 // ignore the hyphen so that it will render at the end of the line.
2511 QChar c = static_cast<RenderText*>(lBreak.obj)->text()[lBreak.pos-1];
2512 if (c.unicode() == SOFT_HYPHEN)
2513 chopMidpointsAt(lBreak.obj, lBreak.pos-2);
2514 }
2515
kociendabb0c24b2001-08-24 14:24:40 +00002516 return lBreak;
2517}
2518
hyattb4b20872004-10-20 21:34:01 +00002519void RenderBlock::checkLinesForOverflow()
2520{
hyattb4b20872004-10-20 21:34:01 +00002521 // FIXME: Inline blocks can have overflow. Need to understand when those objects are present on a line
2522 // and factor that in somehow.
2523 m_overflowWidth = m_width;
2524 for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
darin7bd70952006-04-13 07:07:34 +00002525 m_overflowLeft = min(curr->leftOverflow(), m_overflowLeft);
2526 m_overflowTop = min(curr->topOverflow(), m_overflowTop);
2527 m_overflowWidth = max(curr->rightOverflow(), m_overflowWidth);
2528 m_overflowHeight = max(curr->bottomOverflow(), m_overflowHeight);
hyattb4b20872004-10-20 21:34:01 +00002529 }
2530}
2531
hyatted77ad82004-06-15 07:21:23 +00002532void RenderBlock::deleteEllipsisLineBoxes()
2533{
hyatted77ad82004-06-15 07:21:23 +00002534 for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox())
hyattda77c4b2004-06-17 18:09:49 +00002535 curr->clearTruncation();
hyatted77ad82004-06-15 07:21:23 +00002536}
2537
2538void RenderBlock::checkLinesForTextOverflow()
2539{
2540 // Determine the width of the ellipsis using the current font.
2541 QChar ellipsis = 0x2026; // FIXME: CSS3 says this is configurable, also need to use 0x002E (FULL STOP) if 0x2026 not renderable
hyattda77c4b2004-06-17 18:09:49 +00002542 static AtomicString ellipsisStr(ellipsis);
hyatt3e99d1c2006-02-24 21:13:08 +00002543 const Font& firstLineFont = firstLineStyle()->font();
2544 const Font& font = style()->font();
hyattef0a9392006-03-05 05:51:37 +00002545 int firstLineEllipsisWidth = firstLineFont.width(&ellipsis, 1);
2546 int ellipsisWidth = (font == firstLineFont) ? firstLineEllipsisWidth : font.width(&ellipsis, 1);
hyatted77ad82004-06-15 07:21:23 +00002547
2548 // For LTR text truncation, we want to get the right edge of our padding box, and then we want to see
2549 // if the right edge of a line box exceeds that. For RTL, we use the left edge of the padding box and
2550 // check the left edge of the line box to see if it is less
2551 // Include the scrollbar for overflow blocks, which means we want to use "contentWidth()"
2552 bool ltr = style()->direction() == LTR;
hyatted77ad82004-06-15 07:21:23 +00002553 for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
hyattda77c4b2004-06-17 18:09:49 +00002554 int blockEdge = ltr ? rightOffset(curr->yPos()) : leftOffset(curr->yPos());
hyatt1a8d2512004-06-17 01:38:30 +00002555 int lineBoxEdge = ltr ? curr->xPos() + curr->width() : curr->xPos();
hyatted77ad82004-06-15 07:21:23 +00002556 if ((ltr && lineBoxEdge > blockEdge) || (!ltr && lineBoxEdge < blockEdge)) {
hyattf918d2d2004-06-15 07:24:11 +00002557 // This line spills out of our box in the appropriate direction. Now we need to see if the line
hyatted77ad82004-06-15 07:21:23 +00002558 // can be truncated. In order for truncation to be possible, the line must have sufficient space to
2559 // accommodate our truncation string, and no replaced elements (images, tables) can overlap the ellipsis
2560 // space.
2561 int width = curr == firstRootBox() ? firstLineEllipsisWidth : ellipsisWidth;
hyatt1a8d2512004-06-17 01:38:30 +00002562 if (curr->canAccommodateEllipsis(ltr, blockEdge, lineBoxEdge, width))
2563 curr->placeEllipsis(ellipsisStr, ltr, blockEdge, width);
hyatted77ad82004-06-15 07:21:23 +00002564 }
2565 }
2566}
2567
hyattffe78712003-02-11 01:59:29 +00002568}