blob: e7ef5bd7e10080bac667a48c68f86cff3b6dce51 [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)
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
20 *
kociendabb0c24b2001-08-24 14:24:40 +000021 */
22#include "bidi.h"
23#include "break_lines.h"
24#include "render_flow.h"
25#include "render_text.h"
hyatt33f8d492002-11-12 21:44:52 +000026#include "render_arena.h"
27#include "xml/dom_docimpl.h"
28
kociendabb0c24b2001-08-24 14:24:40 +000029using namespace khtml;
30
31#include "kdebug.h"
32#include "qdatetime.h"
33#include "qfontmetrics.h"
34
35#define BIDI_DEBUG 0
36//#define DEBUG_LINEBREAKS
37
mjs6f821c82002-03-22 00:31:57 +000038
39static BidiIterator sor;
40static BidiIterator eor;
darinb70665a2002-04-15 23:43:21 +000041static BidiIterator last;
42static BidiIterator current;
mjs6f821c82002-03-22 00:31:57 +000043static BidiContext *context;
44static BidiStatus status;
45static QPtrList<BidiRun> *sruns = 0;
hyatt33f8d492002-11-12 21:44:52 +000046static QPtrList<BidiIterator> *smidpoints = 0;
47static bool betweenMidpoints = false;
48static bool isLineEmpty = true;
mjs6f821c82002-03-22 00:31:57 +000049static QChar::Direction dir;
50static bool adjustEmbeddding = false;
51static bool emptyRun = true;
darinb70665a2002-04-15 23:43:21 +000052static int numSpaces;
mjs6f821c82002-03-22 00:31:57 +000053
54static void embed( QChar::Direction d );
darinb70665a2002-04-15 23:43:21 +000055static void appendRun();
mjs6f821c82002-03-22 00:31:57 +000056
hyatt33f8d492002-11-12 21:44:52 +000057void BidiIterator::detach(RenderArena* renderArena)
58{
59 delete this;
60
61 // Now perform the destroy.
62 size_t* sz = (size_t*)this;
63 renderArena->free(*sz, (void*)this);
64}
65
66void* BidiIterator::operator new(size_t sz, RenderArena* renderArena) throw()
67{
68 return renderArena->allocate(sz);
69}
70
71void BidiIterator::operator delete(void* ptr, size_t sz) {
72 size_t* szPtr = (size_t*)ptr;
73 *szPtr = sz;
74}
75
kociendabb0c24b2001-08-24 14:24:40 +000076// ---------------------------------------------------------------------
77
78/* a small helper class used internally to resolve Bidi embedding levels.
79 Each line of text caches the embedding level at the start of the line for faster
80 relayouting
81*/
82BidiContext::BidiContext(unsigned char l, QChar::Direction e, BidiContext *p, bool o)
83 : level(l) , override(o), dir(e)
84{
85 parent = p;
86 if(p) {
87 p->ref();
88 basicDir = p->basicDir;
89 } else
90 basicDir = e;
91 count = 0;
92}
93
94BidiContext::~BidiContext()
95{
96 if(parent) parent->deref();
97}
98
99void BidiContext::ref() const
100{
101 count++;
102}
103
104void BidiContext::deref() const
105{
106 count--;
107 if(count <= 0) delete this;
108}
109
110// ---------------------------------------------------------------------
111
darinb70665a2002-04-15 23:43:21 +0000112inline bool operator==( const BidiIterator &it1, const BidiIterator &it2 )
113{
114 if(it1.pos != it2.pos) return false;
115 if(it1.obj != it2.obj) return false;
116 return true;
117}
118
darinf028f812002-06-10 20:08:04 +0000119inline bool operator!=( const BidiIterator &it1, const BidiIterator &it2 )
darinb70665a2002-04-15 23:43:21 +0000120{
121 if(it1.pos != it2.pos) return true;
122 if(it1.obj != it2.obj) return true;
123 return false;
124}
125
mjs6f821c82002-03-22 00:31:57 +0000126static inline RenderObject *Bidinext(RenderObject *par, RenderObject *current)
127{
128 RenderObject *next = 0;
129 while(current != 0)
130 {
131 //kdDebug( 6040 ) << "current = " << current << endl;
132 if(!current->isFloating() && !current->isReplaced() && !current->isPositioned()) {
133 next = current->firstChild();
134 if ( next && adjustEmbeddding ) {
135 EUnicodeBidi ub = next->style()->unicodeBidi();
darinb70665a2002-04-15 23:43:21 +0000136 if ( ub != UBNormal && !emptyRun ) {
mjs6f821c82002-03-22 00:31:57 +0000137 EDirection dir = next->style()->direction();
138 QChar::Direction d = ( ub == Embed ? ( dir == RTL ? QChar::DirRLE : QChar::DirLRE )
139 : ( dir == RTL ? QChar::DirRLO : QChar::DirLRO ) );
140 embed( d );
141 }
142 }
143 }
144 if(!next) {
145 while(current && current != par) {
146 next = current->nextSibling();
147 if(next) break;
darinb70665a2002-04-15 23:43:21 +0000148 if ( adjustEmbeddding && current->style()->unicodeBidi() != UBNormal && !emptyRun ) {
mjs6f821c82002-03-22 00:31:57 +0000149 embed( QChar::DirPDF );
darinb70665a2002-04-15 23:43:21 +0000150 }
mjs6f821c82002-03-22 00:31:57 +0000151 current = current->parent();
152 }
153 }
154
155 if(!next) break;
156
157 if(next->isText() || next->isBR() || next->isFloating() || next->isReplaced() || next->isPositioned())
158 break;
159 current = next;
160 }
161 return next;
162}
163
164static RenderObject *first( RenderObject *par )
165{
166 if(!par->firstChild()) return 0;
167 RenderObject *o = par->firstChild();
168
169 if(!o->isText() && !o->isBR() && !o->isReplaced() && !o->isFloating() && !o->isPositioned())
170 o = Bidinext( par, o );
171
172 return o;
173}
174
kociendabb0c24b2001-08-24 14:24:40 +0000175BidiIterator::BidiIterator()
176{
177 par = 0;
178 obj = 0;
179 pos = 0;
180}
181
182BidiIterator::BidiIterator(RenderFlow *_par)
183{
184 par = _par;
mjs6f821c82002-03-22 00:31:57 +0000185 obj = first( par );
kociendabb0c24b2001-08-24 14:24:40 +0000186 pos = 0;
187}
188
189BidiIterator::BidiIterator(const BidiIterator &it)
190{
191 par = it.par;
192 obj = it.obj;
193 pos = it.pos;
194}
195
196BidiIterator::BidiIterator(RenderFlow *_par, RenderObject *_obj, int _pos)
197{
198 par = _par;
199 obj = _obj;
200 pos = _pos;
201}
202
203BidiIterator &BidiIterator::operator = (const BidiIterator &it)
204{
205 obj = it.obj;
206 pos = it.pos;
207 par = it.par;
208 return *this;
209}
210
211inline void BidiIterator::operator ++ ()
212{
213 if(!obj) return;
214 if(obj->isText()) {
215 pos++;
darinf028f812002-06-10 20:08:04 +0000216 if(pos >= static_cast<RenderText *>(obj)->stringLength()) {
mjs6f821c82002-03-22 00:31:57 +0000217 obj = Bidinext( par, obj );
kociendabb0c24b2001-08-24 14:24:40 +0000218 pos = 0;
219 }
220 } else {
mjs6f821c82002-03-22 00:31:57 +0000221 obj = Bidinext( par, obj );
kociendabb0c24b2001-08-24 14:24:40 +0000222 pos = 0;
223 }
224}
225
mjs6f821c82002-03-22 00:31:57 +0000226inline bool BidiIterator::atEnd() const
kociendabb0c24b2001-08-24 14:24:40 +0000227{
228 if(!obj) return true;
229 return false;
230}
231
darinf028f812002-06-10 20:08:04 +0000232static const QChar nbsp = QChar(0xA0);
233
234inline const QChar &BidiIterator::current() const
kociendabb0c24b2001-08-24 14:24:40 +0000235{
kociendabb0c24b2001-08-24 14:24:40 +0000236 if( !obj || !obj->isText()) return nbsp; // non breaking space
237 return static_cast<RenderText *>(obj)->text()[pos];
238}
239
darinf028f812002-06-10 20:08:04 +0000240inline QChar::Direction BidiIterator::direction() const
kociendabb0c24b2001-08-24 14:24:40 +0000241{
darinf028f812002-06-10 20:08:04 +0000242 if(!obj || !obj->isText() ) return QChar::DirON;
243
kociendabb0c24b2001-08-24 14:24:40 +0000244 RenderText *renderTxt = static_cast<RenderText *>( obj );
darinf028f812002-06-10 20:08:04 +0000245 if ( pos >= renderTxt->stringLength() )
kociendabb0c24b2001-08-24 14:24:40 +0000246 return QChar::DirON;
247 return renderTxt->text()[pos].direction();
248}
249
kociendabb0c24b2001-08-24 14:24:40 +0000250// -------------------------------------------------------------------------------------------------
251
hyatt33f8d492002-11-12 21:44:52 +0000252static void appendRunsForObject(int start, int end, RenderObject* obj)
253{
hyatteb003b82002-11-15 22:35:10 +0000254 if (start > end)
255 return;
256
hyatt33f8d492002-11-12 21:44:52 +0000257 BidiIterator* nextMidpoint = (smidpoints && smidpoints->count()) ? smidpoints->at(0) : 0;
258 if (betweenMidpoints) {
259 if (!(nextMidpoint && nextMidpoint->obj == obj))
260 return;
261 // This is a new start point. Stop ignoring objects and
262 // adjust our start.
263 betweenMidpoints = false;
264 start = nextMidpoint->pos;
265 smidpoints->removeFirst(); // Delete the midpoint.
266 if (start < end)
267 return appendRunsForObject(start, end, obj);
268 }
269 else {
270 if (!smidpoints || !nextMidpoint || (obj != nextMidpoint->obj)) {
271 sruns->append( new BidiRun(start, end, obj, context, dir) );
272 return;
273 }
274
275 // An end midpoint has been encounted within our object. We
276 // need to go ahead and append a run with our endpoint.
hyatt26179432002-11-17 01:57:27 +0000277 if (int(nextMidpoint->pos+1) <= end) {
278 sruns->append( new BidiRun(start, nextMidpoint->pos+1, obj, context, dir) );
279 betweenMidpoints = true;
280 int nextPos = nextMidpoint->pos+1;
281 smidpoints->removeFirst();
282 return appendRunsForObject(nextPos, end, obj);
283 }
284 else
285 sruns->append( new BidiRun(start, end, obj, context, dir) );
hyatt33f8d492002-11-12 21:44:52 +0000286 }
287}
288
mjs6f821c82002-03-22 00:31:57 +0000289static void appendRun()
kociendabb0c24b2001-08-24 14:24:40 +0000290{
darinb70665a2002-04-15 23:43:21 +0000291 if ( emptyRun ) return;
kociendabb0c24b2001-08-24 14:24:40 +0000292#if BIDI_DEBUG > 1
293 kdDebug(6041) << "appendRun: dir="<<(int)dir<<endl;
294#endif
darinf028f812002-06-10 20:08:04 +0000295
darinb70665a2002-04-15 23:43:21 +0000296 bool b = adjustEmbeddding;
297 adjustEmbeddding = false;
kociendabb0c24b2001-08-24 14:24:40 +0000298
299 int start = sor.pos;
300 RenderObject *obj = sor.obj;
301 while( obj && obj != eor.obj ) {
hyatt33f8d492002-11-12 21:44:52 +0000302 appendRunsForObject(start, obj->length(), obj);
kociendabb0c24b2001-08-24 14:24:40 +0000303 start = 0;
mjs6f821c82002-03-22 00:31:57 +0000304 obj = Bidinext( sor.par, obj );
kociendabb0c24b2001-08-24 14:24:40 +0000305 }
hyatt33f8d492002-11-12 21:44:52 +0000306 appendRunsForObject(start, eor.pos+1, obj);
307
mjs6f821c82002-03-22 00:31:57 +0000308 ++eor;
309 sor = eor;
310 dir = QChar::DirON;
311 status.eor = QChar::DirON;
darinb70665a2002-04-15 23:43:21 +0000312 adjustEmbeddding = b;
mjs6f821c82002-03-22 00:31:57 +0000313}
314
315static void embed( QChar::Direction d )
316{
darinf028f812002-06-10 20:08:04 +0000317#if BIDI_DEBUG > 1
darinb70665a2002-04-15 23:43:21 +0000318 qDebug("*** embed dir=%d emptyrun=%d", d, emptyRun );
darinf028f812002-06-10 20:08:04 +0000319#endif
mjs6f821c82002-03-22 00:31:57 +0000320 bool b = adjustEmbeddding ;
321 adjustEmbeddding = false;
322 if ( d == QChar::DirPDF ) {
323 BidiContext *c = context->parent;
324 if(c && sruns) {
darinb70665a2002-04-15 23:43:21 +0000325 if ( eor != last ) {
326 appendRun();
327 eor = last;
328 }
mjs6f821c82002-03-22 00:31:57 +0000329 appendRun();
330 emptyRun = true;
331 status.last = context->dir;
332 context->deref();
333 context = c;
334 if(context->override)
335 dir = context->dir;
336 else
337 dir = QChar::DirON;
338 status.lastStrong = context->dir;
339 }
340 } else {
341 QChar::Direction runDir;
342 if( d == QChar::DirRLE || d == QChar::DirRLO )
343 runDir = QChar::DirR;
344 else
345 runDir = QChar::DirL;
346 bool override;
347 if( d == QChar::DirLRO || d == QChar::DirRLO )
348 override = true;
349 else
350 override = false;
351
352 unsigned char level = context->level;
353 if ( runDir == QChar::DirR ) {
354 if(level%2) // we have an odd level
355 level += 2;
356 else
357 level++;
358 } else {
359 if(level%2) // we have an odd level
360 level++;
361 else
362 level += 2;
363 }
364
365 if(level < 61) {
366 if ( sruns ) {
darinb70665a2002-04-15 23:43:21 +0000367 if ( eor != last ) {
368 appendRun();
369 eor = last;
370 }
mjs6f821c82002-03-22 00:31:57 +0000371 appendRun();
372 emptyRun = true;
darinb70665a2002-04-15 23:43:21 +0000373
mjs6f821c82002-03-22 00:31:57 +0000374 }
375 context = new BidiContext(level, runDir, context, override);
376 context->ref();
377 if ( override )
378 dir = runDir;
379 status.last = runDir;
380 status.lastStrong = runDir;
381 }
382 }
383 adjustEmbeddding = b;
kociendabb0c24b2001-08-24 14:24:40 +0000384}
385
386
387// collects one line of the paragraph and transforms it to visual order
mjs6f821c82002-03-22 00:31:57 +0000388void RenderFlow::bidiReorderLine(const BidiIterator &start, const BidiIterator &end)
kociendabb0c24b2001-08-24 14:24:40 +0000389{
darinb70665a2002-04-15 23:43:21 +0000390 if ( start == end ) {
hyatt33f8d492002-11-12 21:44:52 +0000391 if ( start.current() == '\n' ) {
392 m_height += lineHeight( firstLine );
393 }
394 return;
darinb70665a2002-04-15 23:43:21 +0000395 }
darinf028f812002-06-10 20:08:04 +0000396#if BIDI_DEBUG > 1
darinb70665a2002-04-15 23:43:21 +0000397 kdDebug(6041) << "reordering Line from " << start.obj << "/" << start.pos << " to " << end.obj << "/" << end.pos << endl;
398#endif
darinf028f812002-06-10 20:08:04 +0000399
mjs6f821c82002-03-22 00:31:57 +0000400 QPtrList<BidiRun> runs;
kociendabb0c24b2001-08-24 14:24:40 +0000401 runs.setAutoDelete(true);
mjs6f821c82002-03-22 00:31:57 +0000402 sruns = &runs;
kociendabb0c24b2001-08-24 14:24:40 +0000403
kociendabb0c24b2001-08-24 14:24:40 +0000404 // context->ref();
405
mjs6f821c82002-03-22 00:31:57 +0000406 dir = QChar::DirON;
407 emptyRun = true;
darinf028f812002-06-10 20:08:04 +0000408
darinb70665a2002-04-15 23:43:21 +0000409 numSpaces = 0;
kociendabb0c24b2001-08-24 14:24:40 +0000410
darinb70665a2002-04-15 23:43:21 +0000411 current = start;
412 last = current;
413 bool atEnd = false;
414 while( 1 ) {
mjs6f821c82002-03-22 00:31:57 +0000415
kociendabb0c24b2001-08-24 14:24:40 +0000416 QChar::Direction dirCurrent;
hyatt33f8d492002-11-12 21:44:52 +0000417 if (atEnd) {
kociendabb0c24b2001-08-24 14:24:40 +0000418 //kdDebug(6041) << "atEnd" << endl;
419 BidiContext *c = context;
hyatt33f8d492002-11-12 21:44:52 +0000420 if ( current.atEnd())
421 while ( c->parent )
422 c = c->parent;
kociendabb0c24b2001-08-24 14:24:40 +0000423 dirCurrent = c->dir;
darinb70665a2002-04-15 23:43:21 +0000424 } else {
kociendabb0c24b2001-08-24 14:24:40 +0000425 dirCurrent = current.direction();
hyatt33f8d492002-11-12 21:44:52 +0000426 }
darinf028f812002-06-10 20:08:04 +0000427
mjs6f821c82002-03-22 00:31:57 +0000428#ifndef QT_NO_UNICODETABLES
kociendabb0c24b2001-08-24 14:24:40 +0000429
430#if BIDI_DEBUG > 1
431 kdDebug(6041) << "directions: dir=" << (int)dir << " current=" << (int)dirCurrent << " last=" << status.last << " eor=" << status.eor << " lastStrong=" << status.lastStrong << " embedding=" << (int)context->dir << " level =" << (int)context->level << endl;
432#endif
433
434 switch(dirCurrent) {
435
436 // embedding and overrides (X1-X9 in the Bidi specs)
437 case QChar::DirRLE:
kociendabb0c24b2001-08-24 14:24:40 +0000438 case QChar::DirLRE:
kociendabb0c24b2001-08-24 14:24:40 +0000439 case QChar::DirRLO:
kociendabb0c24b2001-08-24 14:24:40 +0000440 case QChar::DirLRO:
kociendabb0c24b2001-08-24 14:24:40 +0000441 case QChar::DirPDF:
hyatt33f8d492002-11-12 21:44:52 +0000442 eor = last;
443 embed( dirCurrent );
444 break;
kociendabb0c24b2001-08-24 14:24:40 +0000445
446 // strong types
447 case QChar::DirL:
448 if(dir == QChar::DirON)
449 dir = QChar::DirL;
450 switch(status.last)
451 {
452 case QChar::DirL:
453 eor = current; status.eor = QChar::DirL; break;
454 case QChar::DirR:
455 case QChar::DirAL:
456 case QChar::DirEN:
457 case QChar::DirAN:
darinf028f812002-06-10 20:08:04 +0000458 appendRun();
kociendabb0c24b2001-08-24 14:24:40 +0000459 break;
460 case QChar::DirES:
461 case QChar::DirET:
462 case QChar::DirCS:
463 case QChar::DirBN:
464 case QChar::DirB:
465 case QChar::DirS:
466 case QChar::DirWS:
467 case QChar::DirON:
468 if(dir != QChar::DirL) {
469 //last stuff takes embedding dir
470 if( context->dir == QChar::DirR ) {
mjs6f821c82002-03-22 00:31:57 +0000471 if(!(status.eor == QChar::DirR)) {
kociendabb0c24b2001-08-24 14:24:40 +0000472 // AN or EN
mjs6f821c82002-03-22 00:31:57 +0000473 appendRun();
kociendabb0c24b2001-08-24 14:24:40 +0000474 dir = QChar::DirR;
475 }
476 else
477 eor = last;
mjs6f821c82002-03-22 00:31:57 +0000478 appendRun();
kociendabb0c24b2001-08-24 14:24:40 +0000479 } else {
480 if(status.eor == QChar::DirR) {
mjs6f821c82002-03-22 00:31:57 +0000481 appendRun();
kociendabb0c24b2001-08-24 14:24:40 +0000482 dir = QChar::DirL;
483 } else {
484 eor = current; status.eor = QChar::DirL; break;
485 }
486 }
487 } else {
488 eor = current; status.eor = QChar::DirL;
489 }
490 default:
491 break;
492 }
493 status.lastStrong = QChar::DirL;
494 break;
495 case QChar::DirAL:
496 case QChar::DirR:
497 if(dir == QChar::DirON) dir = QChar::DirR;
498 switch(status.last)
499 {
500 case QChar::DirR:
501 case QChar::DirAL:
502 eor = current; status.eor = QChar::DirR; break;
503 case QChar::DirL:
504 case QChar::DirEN:
505 case QChar::DirAN:
mjs6f821c82002-03-22 00:31:57 +0000506 appendRun();
darinb70665a2002-04-15 23:43:21 +0000507 dir = QChar::DirR;
508 eor = current;
509 status.eor = QChar::DirR;
kociendabb0c24b2001-08-24 14:24:40 +0000510 break;
511 case QChar::DirES:
512 case QChar::DirET:
513 case QChar::DirCS:
514 case QChar::DirBN:
515 case QChar::DirB:
516 case QChar::DirS:
517 case QChar::DirWS:
518 case QChar::DirON:
mjs6f821c82002-03-22 00:31:57 +0000519 if( !(status.eor == QChar::DirR) && !(status.eor == QChar::DirAL) ) {
kociendabb0c24b2001-08-24 14:24:40 +0000520 //last stuff takes embedding dir
521 if(context->dir == QChar::DirR || status.lastStrong == QChar::DirR) {
mjs6f821c82002-03-22 00:31:57 +0000522 appendRun();
kociendabb0c24b2001-08-24 14:24:40 +0000523 dir = QChar::DirR;
524 eor = current;
darinb70665a2002-04-15 23:43:21 +0000525 status.eor = QChar::DirR;
kociendabb0c24b2001-08-24 14:24:40 +0000526 } else {
527 eor = last;
mjs6f821c82002-03-22 00:31:57 +0000528 appendRun();
kociendabb0c24b2001-08-24 14:24:40 +0000529 dir = QChar::DirR;
darinb70665a2002-04-15 23:43:21 +0000530 status.eor = QChar::DirR;
kociendabb0c24b2001-08-24 14:24:40 +0000531 }
532 } else {
533 eor = current; status.eor = QChar::DirR;
534 }
535 default:
536 break;
537 }
538 status.lastStrong = dirCurrent;
539 break;
540
541 // weak types:
542
543 case QChar::DirNSM:
544 // ### if @sor, set dir to dirSor
545 break;
546 case QChar::DirEN:
mjs6f821c82002-03-22 00:31:57 +0000547 if(!(status.lastStrong == QChar::DirAL)) {
kociendabb0c24b2001-08-24 14:24:40 +0000548 // if last strong was AL change EN to AN
549 if(dir == QChar::DirON) {
550 if(status.lastStrong == QChar::DirAL)
551 dir = QChar::DirAN;
552 else
553 dir = QChar::DirL;
554 }
555 switch(status.last)
556 {
557 case QChar::DirET:
558 if ( status.lastStrong == QChar::DirR || status.lastStrong == QChar::DirAL ) {
mjs6f821c82002-03-22 00:31:57 +0000559 appendRun();
kociendabb0c24b2001-08-24 14:24:40 +0000560 dir = QChar::DirAN;
darinb70665a2002-04-15 23:43:21 +0000561 status.eor = QChar::DirAN;
kociendabb0c24b2001-08-24 14:24:40 +0000562 }
563 // fall through
564 case QChar::DirEN:
565 case QChar::DirL:
566 eor = current;
567 status.eor = dirCurrent;
568 break;
569 case QChar::DirR:
570 case QChar::DirAL:
571 case QChar::DirAN:
mjs6f821c82002-03-22 00:31:57 +0000572 appendRun();
darinb70665a2002-04-15 23:43:21 +0000573 status.eor = QChar::DirEN;
kociendabb0c24b2001-08-24 14:24:40 +0000574 dir = QChar::DirAN; break;
575 case QChar::DirES:
576 case QChar::DirCS:
577 if(status.eor == QChar::DirEN) {
578 eor = current; break;
579 }
580 case QChar::DirBN:
581 case QChar::DirB:
582 case QChar::DirS:
583 case QChar::DirWS:
584 case QChar::DirON:
585 if(status.eor == QChar::DirR) {
586 // neutrals go to R
587 eor = last;
mjs6f821c82002-03-22 00:31:57 +0000588 appendRun();
kociendabb0c24b2001-08-24 14:24:40 +0000589 dir = QChar::DirAN;
590 }
591 else if( status.eor == QChar::DirL ||
592 (status.eor == QChar::DirEN && status.lastStrong == QChar::DirL)) {
593 eor = current; status.eor = dirCurrent;
594 } else {
595 // numbers on both sides, neutrals get right to left direction
596 if(dir != QChar::DirL) {
mjs6f821c82002-03-22 00:31:57 +0000597 appendRun();
kociendabb0c24b2001-08-24 14:24:40 +0000598 eor = last;
599 dir = QChar::DirR;
mjs6f821c82002-03-22 00:31:57 +0000600 appendRun();
kociendabb0c24b2001-08-24 14:24:40 +0000601 dir = QChar::DirAN;
602 } else {
603 eor = current; status.eor = dirCurrent;
604 }
605 }
606 default:
607 break;
608 }
609 break;
610 }
611 case QChar::DirAN:
612 dirCurrent = QChar::DirAN;
613 if(dir == QChar::DirON) dir = QChar::DirAN;
614 switch(status.last)
615 {
616 case QChar::DirL:
617 case QChar::DirAN:
618 eor = current; status.eor = QChar::DirAN; break;
619 case QChar::DirR:
620 case QChar::DirAL:
621 case QChar::DirEN:
mjs6f821c82002-03-22 00:31:57 +0000622 appendRun();
kociendabb0c24b2001-08-24 14:24:40 +0000623 break;
624 case QChar::DirCS:
625 if(status.eor == QChar::DirAN) {
626 eor = current; status.eor = QChar::DirR; break;
627 }
628 case QChar::DirES:
629 case QChar::DirET:
630 case QChar::DirBN:
631 case QChar::DirB:
632 case QChar::DirS:
633 case QChar::DirWS:
634 case QChar::DirON:
635 if(status.eor == QChar::DirR) {
636 // neutrals go to R
637 eor = last;
mjs6f821c82002-03-22 00:31:57 +0000638 appendRun();
kociendabb0c24b2001-08-24 14:24:40 +0000639 dir = QChar::DirAN;
640 } else if( status.eor == QChar::DirL ||
641 (status.eor == QChar::DirEN && status.lastStrong == QChar::DirL)) {
642 eor = current; status.eor = dirCurrent;
643 } else {
644 // numbers on both sides, neutrals get right to left direction
645 if(dir != QChar::DirL) {
mjs6f821c82002-03-22 00:31:57 +0000646 appendRun();
kociendabb0c24b2001-08-24 14:24:40 +0000647 eor = last;
648 dir = QChar::DirR;
mjs6f821c82002-03-22 00:31:57 +0000649 appendRun();
kociendabb0c24b2001-08-24 14:24:40 +0000650 dir = QChar::DirAN;
651 } else {
652 eor = current; status.eor = dirCurrent;
653 }
654 }
655 default:
656 break;
657 }
658 break;
659 case QChar::DirES:
660 case QChar::DirCS:
661 break;
662 case QChar::DirET:
663 if(status.last == QChar::DirEN) {
664 dirCurrent = QChar::DirEN;
665 eor = current; status.eor = dirCurrent;
666 break;
667 }
668 break;
669
670 // boundary neutrals should be ignored
671 case QChar::DirBN:
672 break;
673 // neutrals
674 case QChar::DirB:
675 // ### what do we do with newline and paragraph seperators that come to here?
676 break;
677 case QChar::DirS:
678 // ### implement rule L1
679 break;
680 case QChar::DirWS:
darinb70665a2002-04-15 23:43:21 +0000681 numSpaces++;
kociendabb0c24b2001-08-24 14:24:40 +0000682 case QChar::DirON:
683 break;
684 default:
685 break;
686 }
687
688 //cout << " after: dir=" << // dir << " current=" << dirCurrent << " last=" << status.last << " eor=" << status.eor << " lastStrong=" << status.lastStrong << " embedding=" << context->dir << endl;
689
690 if(current.atEnd()) break;
691
692 // set status.last as needed.
693 switch(dirCurrent)
694 {
695 case QChar::DirET:
696 case QChar::DirES:
697 case QChar::DirCS:
698 case QChar::DirS:
699 case QChar::DirWS:
700 case QChar::DirON:
701 switch(status.last)
702 {
703 case QChar::DirL:
704 case QChar::DirR:
705 case QChar::DirAL:
706 case QChar::DirEN:
707 case QChar::DirAN:
708 status.last = dirCurrent;
709 break;
710 default:
711 status.last = QChar::DirON;
712 }
713 break;
714 case QChar::DirNSM:
715 case QChar::DirBN:
716 // ignore these
717 break;
718 default:
719 status.last = dirCurrent;
720 }
mjs6f821c82002-03-22 00:31:57 +0000721#endif
kociendabb0c24b2001-08-24 14:24:40 +0000722
darinb70665a2002-04-15 23:43:21 +0000723 if ( atEnd ) break;
kociendabb0c24b2001-08-24 14:24:40 +0000724 last = current;
mjs6f821c82002-03-22 00:31:57 +0000725
darinb70665a2002-04-15 23:43:21 +0000726 if ( emptyRun ) {
727 sor = current;
728 eor = current;
729 emptyRun = false;
730 }
731
mjs6f821c82002-03-22 00:31:57 +0000732 // this causes the operator ++ to open and close embedding levels as needed
733 // for the CSS unicode-bidi property
734 adjustEmbeddding = true;
kociendabb0c24b2001-08-24 14:24:40 +0000735 ++current;
mjs6f821c82002-03-22 00:31:57 +0000736 adjustEmbeddding = false;
darinb70665a2002-04-15 23:43:21 +0000737
738 if ( current == end ) {
739 if ( emptyRun )
740 break;
741 atEnd = true;
742 }
kociendabb0c24b2001-08-24 14:24:40 +0000743 }
744
745#if BIDI_DEBUG > 0
darinf028f812002-06-10 20:08:04 +0000746 kdDebug(6041) << "reached end of line current=" << current.obj << "/" << current.pos
darinb70665a2002-04-15 23:43:21 +0000747 << ", eor=" << eor.obj << "/" << eor.pos << endl;
kociendabb0c24b2001-08-24 14:24:40 +0000748#endif
darinb70665a2002-04-15 23:43:21 +0000749 if ( !emptyRun && sor != current ) {
750 eor = last;
751 appendRun();
mjs6f821c82002-03-22 00:31:57 +0000752 }
kociendabb0c24b2001-08-24 14:24:40 +0000753
754 BidiContext *endEmbed = context;
755 // both commands below together give a noop...
756 //endEmbed->ref();
757 //context->deref();
758
759 // reorder line according to run structure...
760
761 // first find highest and lowest levels
762 uchar levelLow = 128;
763 uchar levelHigh = 0;
764 BidiRun *r = runs.first();
765
766 while ( r ) {
767 //printf("level = %d\n", r->level);
768 if ( r->level > levelHigh )
769 levelHigh = r->level;
770 if ( r->level < levelLow )
771 levelLow = r->level;
772 r = runs.next();
773 }
774
775 // implements reordering of the line (L2 according to Bidi spec):
776 // L2. From the highest level found in the text to the lowest odd level on each line,
777 // reverse any contiguous sequence of characters that are at that level or higher.
778
779 // reversing is only done up to the lowest odd level
mjs6f821c82002-03-22 00:31:57 +0000780 if( !(levelLow%2) ) levelLow++;
kociendabb0c24b2001-08-24 14:24:40 +0000781
782#if BIDI_DEBUG > 0
darinb70665a2002-04-15 23:43:21 +0000783 kdDebug(6041) << "lineLow = " << (uint)levelLow << ", lineHigh = " << (uint)levelHigh << endl;
kociendabb0c24b2001-08-24 14:24:40 +0000784 kdDebug(6041) << "logical order is:" << endl;
mjs6f821c82002-03-22 00:31:57 +0000785 QPtrListIterator<BidiRun> it2(runs);
kociendabb0c24b2001-08-24 14:24:40 +0000786 BidiRun *r2;
787 for ( ; (r2 = it2.current()); ++it2 )
788 kdDebug(6041) << " " << r2 << " start=" << r2->start << " stop=" << r2->stop << " level=" << (uint)r2->level << endl;
789#endif
790
791 int count = runs.count() - 1;
792
793 // do not reverse for visually ordered web sites
794 if(!style()->visuallyOrdered()) {
795 while(levelHigh >= levelLow) {
796 int i = 0;
797 while ( i < count ) {
798 while(i < count && runs.at(i)->level < levelHigh)
799 i++;
800 int start = i;
801 while(i <= count && runs.at(i)->level >= levelHigh)
802 i++;
803 int end = i-1;
804
805 if(start != end) {
806 //kdDebug(6041) << "reversing from " << start << " to " << end << endl;
807 for(int j = 0; j < (end-start+1)/2; j++)
808 {
809 BidiRun *first = runs.take(start+j);
810 BidiRun *last = runs.take(end-j-1);
811 runs.insert(start+j, last);
812 runs.insert(end-j, first);
813 }
814 }
815 i++;
816 if(i >= count) break;
817 }
818 levelHigh--;
819 }
820 }
821
822#if BIDI_DEBUG > 0
823 kdDebug(6041) << "visual order is:" << endl;
mjs6f821c82002-03-22 00:31:57 +0000824 QPtrListIterator<BidiRun> it3(runs);
kociendabb0c24b2001-08-24 14:24:40 +0000825 BidiRun *r3;
826 for ( ; (r3 = it3.current()); ++it3 )
827 {
828 kdDebug(6041) << " " << r3 << endl;
829 }
830#endif
831
832 int maxPositionTop = 0;
833 int maxPositionBottom = 0;
834 int maxAscent = 0;
835 int maxDescent = 0;
836 r = runs.first();
837 while ( r ) {
838 r->height = r->obj->lineHeight( firstLine );
839 r->baseline = r->obj->baselinePosition( firstLine );
mjs6f821c82002-03-22 00:31:57 +0000840// if ( r->baseline > r->height )
841// r->baseline = r->height;
kociendabb0c24b2001-08-24 14:24:40 +0000842 r->vertical = r->obj->verticalPositionHint( firstLine );
843 //kdDebug(6041) << "object="<< r->obj << " height="<<r->height<<" baseline="<< r->baseline << " vertical=" << r->vertical <<endl;
844 //int ascent;
845 if ( r->vertical == PositionTop ) {
846 if ( maxPositionTop < r->height ) maxPositionTop = r->height;
847 }
848 else if ( r->vertical == PositionBottom ) {
849 if ( maxPositionBottom < r->height ) maxPositionBottom = r->height;
850 }
851 else {
852 int ascent = r->baseline - r->vertical;
853 int descent = r->height - ascent;
854 if(maxAscent < ascent) maxAscent = ascent;
855 if(maxDescent < descent) maxDescent = descent;
856 }
857 r = runs.next();
858 }
859 if ( maxAscent+maxDescent < QMAX( maxPositionTop, maxPositionBottom ) ) {
860 // now the computed lineheight needs to be extended for the
861 // positioned elements
862 // see khtmltests/rendering/html_align.html
863 // ### only iterate over the positioned ones!
864 for ( r = runs.first(); r; r = runs.next() ) {
865 if ( r->vertical == PositionTop ) {
866 if ( maxAscent + maxDescent < r->height )
867 maxDescent = r->height - maxAscent;
868 }
869 else if ( r->vertical == PositionBottom ) {
870 if ( maxAscent + maxDescent < r->height )
871 maxAscent = r->height - maxDescent;
872 }
873 else
874 continue;
875
876 if ( maxAscent + maxDescent >= QMAX( maxPositionTop, maxPositionBottom ) )
877 break;
878
879 }
880 }
881 int maxHeight = maxAscent + maxDescent;
mjs6f821c82002-03-22 00:31:57 +0000882 // CSS2: 10.8.1: line-height on the block level element specifies the *minimum*
883 // height of the generated line box
kociendabb0c24b2001-08-24 14:24:40 +0000884 r = runs.first();
mjs6f821c82002-03-22 00:31:57 +0000885 // ### we have no reliable way of detecting empty lineboxes - which
886 // are not allowed to have any height. sigh.(Dirk)
887// if ( r ) {
888// int blockHeight = lineHeight( firstLine );
889// if ( blockHeight > maxHeight )
890// maxHeight = blockHeight;
891// }
kociendabb0c24b2001-08-24 14:24:40 +0000892 int totWidth = 0;
mjs6f821c82002-03-22 00:31:57 +0000893#if BIDI_DEBUG > 0
894 kdDebug( 6040 ) << "starting run.." << endl;
895#endif
kociendabb0c24b2001-08-24 14:24:40 +0000896 while ( r ) {
897 if(r->vertical == PositionTop)
898 r->vertical = m_height;
899 else if(r->vertical == PositionBottom)
900 r->vertical = m_height + maxHeight - r->height;
901 else
902 r->vertical += m_height + maxAscent - r->baseline;
903
mjs6f821c82002-03-22 00:31:57 +0000904#if BIDI_DEBUG > 0
darinb70665a2002-04-15 23:43:21 +0000905 kdDebug(6040) << "object="<< r->obj << " placing at vertical=" << r->vertical <<endl;
mjs6f821c82002-03-22 00:31:57 +0000906#endif
kociendabb0c24b2001-08-24 14:24:40 +0000907 if(r->obj->isText())
darinb70665a2002-04-15 23:43:21 +0000908 r->width = static_cast<RenderText *>(r->obj)->width(r->start, r->stop-r->start, firstLine);
kociendabb0c24b2001-08-24 14:24:40 +0000909 else {
910 r->obj->calcWidth();
911 r->width = r->obj->width()+r->obj->marginLeft()+r->obj->marginRight();
912 }
913 totWidth += r->width;
914 r = runs.next();
915 }
mjs6f821c82002-03-22 00:31:57 +0000916 //kdDebug(6040) << "yPos of line=" << m_height << " lineBoxHeight=" << maxHeight << endl;
kociendabb0c24b2001-08-24 14:24:40 +0000917
918 // now construct the reordered string out of the runs...
919
920 r = runs.first();
921 int x = leftOffset(m_height);
922 int availableWidth = lineWidth(m_height);
923 switch(style()->textAlign()) {
924 case LEFT:
darinb70665a2002-04-15 23:43:21 +0000925 numSpaces = 0;
kociendabb0c24b2001-08-24 14:24:40 +0000926 break;
927 case JUSTIFY:
darinb70665a2002-04-15 23:43:21 +0000928 if(numSpaces != 0 && !current.atEnd() && !current.obj->isBR() )
kociendabb0c24b2001-08-24 14:24:40 +0000929 break;
darinb70665a2002-04-15 23:43:21 +0000930 // fall through
931 case TAAUTO:
932 numSpaces = 0;
kociendabb0c24b2001-08-24 14:24:40 +0000933 // for right to left fall through to right aligned
darinb70665a2002-04-15 23:43:21 +0000934 if ( endEmbed->basicDir == QChar::DirL )
935 break;
kociendabb0c24b2001-08-24 14:24:40 +0000936 case RIGHT:
937 x += availableWidth - totWidth;
darinb70665a2002-04-15 23:43:21 +0000938 numSpaces = 0;
kociendabb0c24b2001-08-24 14:24:40 +0000939 break;
940 case CENTER:
941 case KONQ_CENTER:
942 int xd = (availableWidth - totWidth)/2;
943 x += xd>0?xd:0;
darinb70665a2002-04-15 23:43:21 +0000944 numSpaces = 0;
kociendabb0c24b2001-08-24 14:24:40 +0000945 break;
946 }
947 while ( r ) {
mjs6f821c82002-03-22 00:31:57 +0000948#if BIDI_DEBUG > 1
949 kdDebug(6040) << "positioning " << r->obj << " start=" << r->start << " stop=" << r->stop << " yPos=" << r->vertical << endl;
950#endif
darinb70665a2002-04-15 23:43:21 +0000951 int spaceAdd = 0;
952 if ( numSpaces > 0 ) {
953 if ( r->obj->isText() ) {
954 // get number of spaces in run
955 int spaces = 0;
956 for ( int i = r->start; i < r->stop; i++ )
957 if ( static_cast<RenderText *>(r->obj)->text()[i].direction() == QChar::DirWS )
958 spaces++;
959 if ( spaces > numSpaces ) // should never happen...
960 spaces = numSpaces;
961 spaceAdd = (availableWidth - totWidth)*spaces/numSpaces;
962 numSpaces -= spaces;
963 totWidth += spaceAdd;
964 }
965 }
966 r->obj->position(x, r->vertical, r->start, r->stop - r->start, r->width, r->level%2, firstLine, spaceAdd);
967 x += r->width + spaceAdd;
kociendabb0c24b2001-08-24 14:24:40 +0000968 r = runs.next();
969 }
970
971 m_height += maxHeight;
972
mjs6f821c82002-03-22 00:31:57 +0000973 sruns = 0;
kociendabb0c24b2001-08-24 14:24:40 +0000974}
975
976
hyatt33f8d492002-11-12 21:44:52 +0000977static void deleteMidpoints(RenderArena* arena, QPtrList<BidiIterator>* midpoints)
978{
979 if (!midpoints)
980 return;
981
982 unsigned int len = midpoints->count();
983 for(unsigned int i=0; i < len; i++) {
984 BidiIterator* s = midpoints->at(i);
985 if (s)
986 s->detach(arena);
987 midpoints->remove(i);
988 }
989}
990
kociendabb0c24b2001-08-24 14:24:40 +0000991void RenderFlow::layoutInlineChildren()
992{
hyatta70560a2002-11-20 01:53:20 +0000993 m_overflowHeight = 0;
994
kociendabb0c24b2001-08-24 14:24:40 +0000995 invalidateVerticalPositions();
996#ifdef DEBUG_LAYOUT
997 QTime qt;
998 qt.start();
999 kdDebug( 6040 ) << renderName() << " layoutInlineChildren( " << this <<" )" << endl;
1000#endif
darinb70665a2002-04-15 23:43:21 +00001001#if BIDI_DEBUG > 1 || defined( DEBUG_LINEBREAKS )
1002 kdDebug(6041) << " ------- bidi start " << this << " -------" << endl;
darinf028f812002-06-10 20:08:04 +00001003#endif
kociendabb0c24b2001-08-24 14:24:40 +00001004 int toAdd = style()->borderBottomWidth();
1005 m_height = style()->borderTopWidth();
1006
hyatt80844872002-11-13 22:07:12 +00001007 if(hasPadding())
kociendabb0c24b2001-08-24 14:24:40 +00001008 {
1009 m_height += paddingTop();
1010 toAdd += paddingBottom();
1011 }
hyatt33f8d492002-11-12 21:44:52 +00001012
kociendabb0c24b2001-08-24 14:24:40 +00001013 if(firstChild()) {
1014 // layout replaced elements
mjs6f821c82002-03-22 00:31:57 +00001015 RenderObject *o = first( this );
kociendabb0c24b2001-08-24 14:24:40 +00001016 while ( o ) {
1017 if(o->isReplaced() || o->isFloating() || o->isPositioned()) {
1018 //kdDebug(6041) << "layouting replaced or floating child" << endl;
mjs6f821c82002-03-22 00:31:57 +00001019 if (o->isReplaced() && (o->style()->width().isPercent() || o->style()->height().isPercent()))
1020 o->setLayouted(false);
1021 if( !o->layouted() )
1022 o->layout();
kociendabb0c24b2001-08-24 14:24:40 +00001023 if(o->isPositioned())
mjs6f821c82002-03-22 00:31:57 +00001024 static_cast<RenderFlow*>(o->containingBlock())->insertSpecialObject(o);
kociendabb0c24b2001-08-24 14:24:40 +00001025 }
1026 else if(o->isText())
1027 static_cast<RenderText *>(o)->deleteSlaves();
mjs6f821c82002-03-22 00:31:57 +00001028 o = Bidinext( this, o );
kociendabb0c24b2001-08-24 14:24:40 +00001029 }
1030
1031 BidiContext *startEmbed;
mjs6f821c82002-03-22 00:31:57 +00001032 status = BidiStatus();
kociendabb0c24b2001-08-24 14:24:40 +00001033 if( style()->direction() == LTR ) {
1034 startEmbed = new BidiContext( 0, QChar::DirL );
1035 status.eor = QChar::DirL;
1036 } else {
1037 startEmbed = new BidiContext( 1, QChar::DirR );
1038 status.eor = QChar::DirR;
1039 }
1040 startEmbed->ref();
1041
mjs6f821c82002-03-22 00:31:57 +00001042 context = startEmbed;
hyatt33f8d492002-11-12 21:44:52 +00001043 adjustEmbeddding = true;
kociendabb0c24b2001-08-24 14:24:40 +00001044 BidiIterator start(this);
hyatt33f8d492002-11-12 21:44:52 +00001045 adjustEmbeddding = false;
kociendabb0c24b2001-08-24 14:24:40 +00001046 BidiIterator end(this);
kociendabb0c24b2001-08-24 14:24:40 +00001047
1048 firstLine = true;
hyatt33f8d492002-11-12 21:44:52 +00001049
1050 if (!smidpoints) {
1051 smidpoints = new QPtrList<BidiIterator>;
1052 smidpoints->setAutoDelete(false);
1053 }
1054
kociendabb0c24b2001-08-24 14:24:40 +00001055 while( !end.atEnd() ) {
1056 start = end;
hyatt33f8d492002-11-12 21:44:52 +00001057 betweenMidpoints = false;
1058 isLineEmpty = true;
1059 end = findNextLineBreak(start, *smidpoints);
mjs6f821c82002-03-22 00:31:57 +00001060 if( start.atEnd() ) break;
hyatt33f8d492002-11-12 21:44:52 +00001061 if (!isLineEmpty) {
1062 bidiReorderLine(start, end);
1063
1064 if( end == start || (end.obj && end.obj->isBR() && !start.obj->isBR() ) ) {
1065 adjustEmbeddding = true;
1066 ++end;
1067 adjustEmbeddding = false;
1068 } else if(m_pre && end.current() == QChar('\n') ) {
1069 adjustEmbeddding = true;
1070 ++end;
1071 adjustEmbeddding = false;
1072 }
1073
1074 newLine();
darinb70665a2002-04-15 23:43:21 +00001075 }
kociendabb0c24b2001-08-24 14:24:40 +00001076 firstLine = false;
hyatt33f8d492002-11-12 21:44:52 +00001077 deleteMidpoints(renderArena(), smidpoints);
kociendabb0c24b2001-08-24 14:24:40 +00001078 }
1079 startEmbed->deref();
1080 //embed->deref();
1081 }
hyatt33f8d492002-11-12 21:44:52 +00001082
1083 deleteMidpoints(renderArena(), smidpoints);
hyatta70560a2002-11-20 01:53:20 +00001084
1085 // Now add in the bottom border/padding.
kociendabb0c24b2001-08-24 14:24:40 +00001086 m_height += toAdd;
1087
mjs6f821c82002-03-22 00:31:57 +00001088 // in case we have a float on the last line, it might not be positioned up to now.
1089 positionNewFloats();
kociendabb0c24b2001-08-24 14:24:40 +00001090
hyatta70560a2002-11-20 01:53:20 +00001091 // Always make sure this is at least our height.
1092 m_overflowHeight = m_height;
1093
darinb70665a2002-04-15 23:43:21 +00001094#if BIDI_DEBUG > 1
1095 kdDebug(6041) << " ------- bidi end " << this << " -------" << endl;
darinf028f812002-06-10 20:08:04 +00001096#endif
kociendabb0c24b2001-08-24 14:24:40 +00001097 //kdDebug() << "RenderFlow::layoutInlineChildren time used " << qt.elapsed() << endl;
1098 //kdDebug(6040) << "height = " << m_height <<endl;
1099}
1100
hyatt33f8d492002-11-12 21:44:52 +00001101BidiIterator RenderFlow::findNextLineBreak(BidiIterator &start, QPtrList<BidiIterator>& midpoints)
kociendabb0c24b2001-08-24 14:24:40 +00001102{
kociendabb0c24b2001-08-24 14:24:40 +00001103 int width = lineWidth(m_height);
1104 int w = 0;
1105 int tmpW = 0;
1106#ifdef DEBUG_LINEBREAKS
1107 kdDebug(6041) << "findNextLineBreak: line at " << m_height << " line width " << width << endl;
1108 kdDebug(6041) << "sol: " << start.obj << " " << start.pos << endl;
1109#endif
1110
mjs6f821c82002-03-22 00:31:57 +00001111 // eliminate spaces at beginning of line
1112 if(!m_pre) {
hyatt33f8d492002-11-12 21:44:52 +00001113 // remove leading spaces
1114 while(!start.atEnd() &&
mjs6f821c82002-03-22 00:31:57 +00001115#ifndef QT_NO_UNICODETABLES
hyatt33f8d492002-11-12 21:44:52 +00001116 ( start.direction() == QChar::DirWS || start.obj->isSpecial() )
mjs6f821c82002-03-22 00:31:57 +00001117#else
hyatt33f8d492002-11-12 21:44:52 +00001118 ( start.current() == ' ' || start.obj->isSpecial() )
mjs6f821c82002-03-22 00:31:57 +00001119#endif
hyatt33f8d492002-11-12 21:44:52 +00001120 ) {
1121 if( start.obj->isSpecial() ) {
1122 RenderObject *o = start.obj;
1123 // add to special objects...
1124 if(o->isFloating()) {
1125 insertSpecialObject(o);
1126 // check if it fits in the current line.
1127 // If it does, position it now, otherwise, position
1128 // it after moving to next line (in newLine() func)
1129 if (o->width()+o->marginLeft()+o->marginRight()+w+tmpW <= width) {
1130 positionNewFloats();
1131 width = lineWidth(m_height);
1132 }
1133 } else if(o->isPositioned()) {
1134 static_cast<RenderFlow*>(o->containingBlock())->insertSpecialObject(o);
1135 }
1136 }
1137
1138 adjustEmbeddding = true;
1139 ++start;
1140 adjustEmbeddding = false;
1141 }
mjs6f821c82002-03-22 00:31:57 +00001142 }
1143 if ( start.atEnd() )
darinb70665a2002-04-15 23:43:21 +00001144 return start;
mjs6f821c82002-03-22 00:31:57 +00001145
hyatt33f8d492002-11-12 21:44:52 +00001146 // This variable is used only if whitespace isn't set to PRE, and it tells us whether
1147 // or not we are currently ignoring whitespace.
1148 bool ignoringSpaces = false;
1149
1150 // This variable tracks whether the very last character we saw was a space. We use
1151 // this to detect when we encounter a second space so we know we have to terminate
1152 // a run.
1153 bool sawSpace = false;
1154 RenderObject* trailingSpaceObject = 0;
1155
1156 // The pos of the last whitespace char we saw, not to be confused with the lastSpace
1157 // variable below, which is really the last breakable char.
1158 int lastSpacePos = 0;
1159
mjs6f821c82002-03-22 00:31:57 +00001160 BidiIterator lBreak = start;
1161
kociendabb0c24b2001-08-24 14:24:40 +00001162 RenderObject *o = start.obj;
1163 RenderObject *last = o;
1164 int pos = start.pos;
1165
1166 while( o ) {
1167#ifdef DEBUG_LINEBREAKS
1168 kdDebug(6041) << "new object "<< o <<" width = " << w <<" tmpw = " << tmpW << endl;
1169#endif
1170 if(o->isBR()) {
1171 if( w + tmpW <= width ) {
1172 lBreak.obj = o;
1173 lBreak.pos = 0;
hyatt33f8d492002-11-12 21:44:52 +00001174
1175 // A <br> always breaks a line, so don't let the line be collapsed
1176 // away. Also, the space at the end of a line with a <br> does not
1177 // get collapsed away. -dwh
1178 isLineEmpty = false;
1179 trailingSpaceObject = 0;
1180
kociendabb0c24b2001-08-24 14:24:40 +00001181 //check the clear status
1182 EClear clear = o->style()->clear();
1183 if(clear != CNONE) {
1184 m_clearStatus = (EClear) (m_clearStatus | clear);
1185 }
1186 }
1187 goto end;
1188 }
1189 if( o->isSpecial() ) {
1190 // add to special objects...
hyatt33f8d492002-11-12 21:44:52 +00001191 if(o->isFloating()) {
1192 insertSpecialObject(o);
1193 // check if it fits in the current line.
1194 // If it does, position it now, otherwise, position
1195 // it after moving to next line (in newLine() func)
1196 if (o->width()+o->marginLeft()+o->marginRight()+w+tmpW <= width) {
1197 positionNewFloats();
1198 width = lineWidth(m_height);
1199 }
1200 } else if(o->isPositioned()) {
1201 static_cast<RenderFlow*>(o->containingBlock())->insertSpecialObject(o);
1202 }
kociendabb0c24b2001-08-24 14:24:40 +00001203 } else if ( o->isReplaced() ) {
hyattf14a4a32002-11-21 22:06:32 +00001204 if (o->style()->whiteSpace() != NOWRAP || last->style()->whiteSpace() != NOWRAP) {
hyatt711fe232002-11-20 21:25:14 +00001205 w += tmpW;
1206 tmpW = 0;
hyattf14a4a32002-11-21 22:06:32 +00001207 lBreak.obj = o;
1208 lBreak.pos = 0;
hyatt711fe232002-11-20 21:25:14 +00001209 }
1210
kociendabb0c24b2001-08-24 14:24:40 +00001211 tmpW += o->width()+o->marginLeft()+o->marginRight();
hyatt33f8d492002-11-12 21:44:52 +00001212 if (ignoringSpaces) {
1213 BidiIterator* startMid = new (o->renderArena()) BidiIterator();
1214 startMid->obj = o;
1215 startMid->pos = 0;
1216 midpoints.append(startMid);
1217 }
1218 isLineEmpty = false;
1219 ignoringSpaces = false;
1220 sawSpace = false;
1221 lastSpacePos = 0;
1222 trailingSpaceObject = 0;
hyatte85e4a72002-12-08 02:06:16 +00001223
1224 if (o->isListMarker() && o->style()->listStylePosition() == OUTSIDE) {
1225 // The marker must not have an effect on whitespace at the start
1226 // of the line. We start ignoring spaces to make sure that any additional
1227 // spaces we see will be discarded.
1228 //
1229 // Optimize for a common case. If we can't find whitespace after the list
1230 // item, then this is all moot. -dwh
1231 RenderObject* next = Bidinext( start.par, o );
1232 if (!m_pre && next && next->isText() && static_cast<RenderText*>(next)->stringLength() > 0 &&
1233 (static_cast<RenderText*>(next)->text()[0].direction() == QChar::DirWS ||
1234 static_cast<RenderText*>(next)->text()[0] == '\n')) {
1235 sawSpace = true;
1236 ignoringSpaces = true;
1237 BidiIterator* endMid = new (o->renderArena()) BidiIterator();
1238 endMid->obj = o;
1239 endMid->pos = 0;
1240 midpoints.append(endMid);
1241 }
1242 }
kociendabb0c24b2001-08-24 14:24:40 +00001243 } else if ( o->isText() ) {
hyatt33f8d492002-11-12 21:44:52 +00001244 RenderText *t = static_cast<RenderText *>(o);
1245 int strlen = t->stringLength();
1246 int len = strlen - pos;
1247 QChar *str = t->text();
kociendabb0c24b2001-08-24 14:24:40 +00001248
hyatt33f8d492002-11-12 21:44:52 +00001249 const Font *f = t->htmlFont( firstLine );
1250 // proportional font, needs a bit more work.
1251 int lastSpace = pos;
1252 bool isPre = o->style()->whiteSpace() == PRE;
1253 //QChar space[1]; space[0] = ' ';
1254 //int spaceWidth = f->width(space, 1, 0);
1255 while(len) {
1256 //XXXdwh This is wrong. Still mutating the DOM
1257 // string for newlines... will fix in second stage.
1258 if (!isPre && str[pos] == '\n')
1259 str[pos] = ' ';
1260
1261 bool oldSawSpace = sawSpace;
1262 sawSpace = (str[pos].direction() == QChar::DirWS);
1263
1264 if (isPre || !sawSpace)
1265 isLineEmpty = false;
1266
1267 if( (isPre && str[pos] == '\n') ||
1268 (!isPre && isBreakable( str, pos, strlen ) ) ) {
1269
1270 if (ignoringSpaces) {
1271 if (!sawSpace) {
1272 // Stop ignoring spaces and begin at this
1273 // new point.
1274 BidiIterator* startMid = new (o->renderArena()) BidiIterator();
1275 startMid->obj = o;
1276 startMid->pos = pos;
1277 midpoints.append(startMid);
1278 }
1279 else {
1280 // Just keep ignoring these spaces.
1281 pos++;
1282 len--;
1283 continue;
1284 }
1285 }
1286 else {
1287 if (sawSpace && !oldSawSpace)
1288 lastSpacePos = pos;
1289 tmpW += t->width(lastSpace, pos - lastSpace, f);
1290 }
1291
kociendabb0c24b2001-08-24 14:24:40 +00001292#ifdef DEBUG_LINEBREAKS
hyatt33f8d492002-11-12 21:44:52 +00001293 kdDebug(6041) << "found space at " << pos << " in string '" << QString( str, strlen ).latin1() << "' adding " << tmpW << " new width = " << w << endl;
kociendabb0c24b2001-08-24 14:24:40 +00001294#endif
hyatt33f8d492002-11-12 21:44:52 +00001295 if ( !isPre && w + tmpW > width && w == 0 ) {
1296 int fb = floatBottom();
1297 int newLineWidth = lineWidth(fb);
1298 if(!w && m_height < fb && width < newLineWidth) {
1299 m_height = fb;
1300 width = newLineWidth;
1301#ifdef DEBUG_LINEBREAKS
1302 kdDebug() << "RenderFlow::findNextLineBreak new position at " << m_height << " newWidth " << width << endl;
1303#endif
1304 }
1305 }
1306
1307 if ( !isPre && w + tmpW > width )
1308 goto end;
1309
1310 lBreak.obj = o;
1311 lBreak.pos = pos;
hyatt711fe232002-11-20 21:25:14 +00001312
hyatt33f8d492002-11-12 21:44:52 +00001313 if( *(str+pos) == '\n' && isPre) {
1314#ifdef DEBUG_LINEBREAKS
1315 kdDebug(6041) << "forced break sol: " << start.obj << " " << start.pos << " end: " << lBreak.obj << " " << lBreak.pos << " width=" << w << endl;
1316#endif
1317 return lBreak;
1318 }
1319
1320 w += tmpW;
1321 tmpW = 0;
1322 lastSpace = pos;
1323
1324 if (!ignoringSpaces && !isPre) {
1325 // If we encounter a newline, or if we encounter a
1326 // second space, we need to go ahead and break up this
1327 // run and enter a mode where we start collapsing spaces.
1328 if (sawSpace && oldSawSpace)
1329 ignoringSpaces = true;
1330
1331 if (ignoringSpaces) {
1332 // We just entered a mode where we are ignoring
1333 // spaces. Create a midpoint to terminate the run
1334 // before the second space.
1335 BidiIterator* endMid = new (o->renderArena()) BidiIterator();
1336 if (trailingSpaceObject) {
1337 endMid->obj = trailingSpaceObject;
1338 }
1339 else
1340 endMid->obj = o;
1341 endMid->pos = lastSpacePos;
1342 midpoints.append(endMid);
1343 lastSpace = pos;
1344 }
1345 }
1346 }
1347 else if (ignoringSpaces) {
1348 // Stop ignoring spaces and begin at this
1349 // new point.
1350 ignoringSpaces = false;
1351 lastSpacePos = 0;
1352 lastSpace = pos; // e.g., "Foo goo", don't add in any of the ignored spaces.
1353 BidiIterator* startMid = new (o->renderArena()) BidiIterator();
1354 startMid->obj = o;
1355 startMid->pos = pos;
1356 midpoints.append(startMid);
1357 }
1358
1359 if (!isPre && sawSpace && !ignoringSpaces)
1360 trailingSpaceObject = o;
1361 else if (isPre || !sawSpace)
1362 trailingSpaceObject = 0;
1363
1364 pos++;
1365 len--;
1366 }
1367
kociendabb0c24b2001-08-24 14:24:40 +00001368 // IMPORTANT: pos is > length here!
hyatt33f8d492002-11-12 21:44:52 +00001369 if (!ignoringSpaces)
1370 tmpW += t->width(lastSpace, pos - lastSpace, f);
kociendabb0c24b2001-08-24 14:24:40 +00001371 } else
mjs6f821c82002-03-22 00:31:57 +00001372 KHTMLAssert( false );
kociendabb0c24b2001-08-24 14:24:40 +00001373
hyatt711fe232002-11-20 21:25:14 +00001374 if( w + tmpW > width+1 && o->style()->whiteSpace() != NOWRAP ) {
kociendabb0c24b2001-08-24 14:24:40 +00001375 //kdDebug() << " too wide w=" << w << " tmpW = " << tmpW << " width = " << width << endl;
hyatt33f8d492002-11-12 21:44:52 +00001376 //kdDebug() << "start=" << start.obj << " current=" << o << endl;
kociendabb0c24b2001-08-24 14:24:40 +00001377 // if we have floats, try to get below them.
hyatt33f8d492002-11-12 21:44:52 +00001378 if (sawSpace && !ignoringSpaces && o->style()->whiteSpace() != PRE)
1379 trailingSpaceObject = 0;
1380
kociendabb0c24b2001-08-24 14:24:40 +00001381 int fb = floatBottom();
hyatt33f8d492002-11-12 21:44:52 +00001382 int newLineWidth = lineWidth(fb);
darinb70665a2002-04-15 23:43:21 +00001383 if( !w && m_height < fb && width < newLineWidth ) {
kociendabb0c24b2001-08-24 14:24:40 +00001384 m_height = fb;
darinb70665a2002-04-15 23:43:21 +00001385 width = newLineWidth;
1386#ifdef DEBUG_LINEBREAKS
1387 kdDebug() << "RenderFlow::findNextLineBreak new position at " << m_height << " newWidth " << width << endl;
1388#endif
kociendabb0c24b2001-08-24 14:24:40 +00001389 }
hyattf14a4a32002-11-21 22:06:32 +00001390
hyatt33f8d492002-11-12 21:44:52 +00001391 if( !w && w + tmpW > width+1 && (o != start.obj || (unsigned) pos != start.pos) ) {
1392 // getting below floats wasn't enough...
1393 //kdDebug() << "still too wide w=" << w << " tmpW = " << tmpW << " width = " << width << endl;
1394 lBreak.obj = o;
kociendabb0c24b2001-08-24 14:24:40 +00001395 if(last != o) {
1396 //kdDebug() << " using last " << last << endl;
hyatt711fe232002-11-20 21:25:14 +00001397 lBreak.pos = 0;
kociendabb0c24b2001-08-24 14:24:40 +00001398 }
1399 else if ( unsigned ( pos ) >= o->length() ) {
mjs6f821c82002-03-22 00:31:57 +00001400 lBreak.obj = Bidinext( start.par, o );
kociendabb0c24b2001-08-24 14:24:40 +00001401 lBreak.pos = 0;
1402 }
hyatt711fe232002-11-20 21:25:14 +00001403 else {
kociendabb0c24b2001-08-24 14:24:40 +00001404 lBreak.pos = pos;
hyatt711fe232002-11-20 21:25:14 +00001405 }
kociendabb0c24b2001-08-24 14:24:40 +00001406 }
darinb70665a2002-04-15 23:43:21 +00001407 goto end;
kociendabb0c24b2001-08-24 14:24:40 +00001408 }
hyatta867c4c2002-11-27 03:04:33 +00001409
hyattf14a4a32002-11-21 22:06:32 +00001410 last = o;
1411 o = Bidinext( start.par, o );
1412
1413 if (last->isReplaced() && last->style()->whiteSpace() != NOWRAP) {
hyatt711fe232002-11-20 21:25:14 +00001414 // Go ahead and add in tmpW.
1415 w += tmpW;
1416 tmpW = 0;
1417 lBreak.obj = o;
1418 lBreak.pos = 0;
1419 }
1420
kociendabb0c24b2001-08-24 14:24:40 +00001421 pos = 0;
1422 }
1423
darinb70665a2002-04-15 23:43:21 +00001424#ifdef DEBUG_LINEBREAKS
kociendabb0c24b2001-08-24 14:24:40 +00001425 kdDebug( 6041 ) << "end of par, width = " << width << " linewidth = " << w + tmpW << endl;
1426#endif
1427 if( w + tmpW <= width ) {
1428 lBreak.obj = 0;
1429 lBreak.pos = 0;
1430 }
1431
1432 end:
hyattdda1d1b2002-12-13 09:44:17 +00001433 int determinedWidth = w + tmpW;
kociendabb0c24b2001-08-24 14:24:40 +00001434 if( lBreak == start && !lBreak.obj->isBR() ) {
kociendabb0c24b2001-08-24 14:24:40 +00001435 // we just add as much as possible
1436 if ( m_pre ) {
1437 if(pos != 0) {
1438 lBreak.obj = o;
1439 lBreak.pos = pos - 1;
1440 } else {
1441 lBreak.obj = last;
hyattc3731d42002-12-12 06:20:22 +00001442 lBreak.pos = last->isText() ? last->length() : 0;
kociendabb0c24b2001-08-24 14:24:40 +00001443 }
1444 } else if( lBreak.obj ) {
hyatt33f8d492002-11-12 21:44:52 +00001445 if( last != o ) {
hyattdda1d1b2002-12-13 09:44:17 +00001446 // better to break between object boundaries than in the middle of a word
hyatt33f8d492002-11-12 21:44:52 +00001447 lBreak.obj = o;
1448 lBreak.pos = 0;
hyattdda1d1b2002-12-13 09:44:17 +00001449 determinedWidth -= tmpW;
hyatt33f8d492002-11-12 21:44:52 +00001450 } else {
hyattdda1d1b2002-12-13 09:44:17 +00001451 // Don't ever break in the middle of a word if we can help it.
1452 // There's no room at all. We just have to be on this line,
1453 // even though we'll spill out.
1454 lBreak.obj = o;
1455 lBreak.pos = pos;
hyatt33f8d492002-11-12 21:44:52 +00001456 }
kociendabb0c24b2001-08-24 14:24:40 +00001457 }
1458 }
1459
hyattdda1d1b2002-12-13 09:44:17 +00001460 // FIXME: XXXdwh Support rtl.
1461 if (style()->direction() == LTR && m_overflowWidth < borderLeft() + paddingLeft() + determinedWidth)
1462 m_overflowWidth = borderLeft() + paddingLeft() + determinedWidth;
1463
kociendabb0c24b2001-08-24 14:24:40 +00001464 // make sure we consume at least one char/object.
1465 if( lBreak == start )
1466 ++lBreak;
hyatt33f8d492002-11-12 21:44:52 +00001467
kociendabb0c24b2001-08-24 14:24:40 +00001468#ifdef DEBUG_LINEBREAKS
1469 kdDebug(6041) << "regular break sol: " << start.obj << " " << start.pos << " end: " << lBreak.obj << " " << lBreak.pos << " width=" << w << endl;
1470#endif
hyatt33f8d492002-11-12 21:44:52 +00001471
1472 if (trailingSpaceObject) {
1473 // This object is either going to be part of the last midpoint, or it is going
1474 // to be the actual endpoint. In both cases we just decrease our pos by 1 level to
1475 // exclude the space, allowing it to - in effect - collapse into the newline.
1476 int count = midpoints.count();
1477 if (count%2==1) {
1478 BidiIterator* lastEndPoint = midpoints.at(count-1);
1479 lastEndPoint->pos--;
1480 }
1481 //else if (lBreak.pos > 0)
1482 // lBreak.pos--;
1483 else if (lBreak.obj == 0 && trailingSpaceObject->isText()) {
hyattd20075d2002-11-16 02:23:32 +00001484 // Add a new end midpoint that stops right at the very end.
1485 BidiIterator* endMid = new (trailingSpaceObject->renderArena()) BidiIterator();
1486 endMid->obj = trailingSpaceObject;
1487 RenderText* text = static_cast<RenderText *>(trailingSpaceObject);
1488 endMid->pos = text->length() >=2 ? text->length() - 2 : 0;
1489 midpoints.append(endMid);
hyatt33f8d492002-11-12 21:44:52 +00001490 }
1491 }
1492
kociendabb0c24b2001-08-24 14:24:40 +00001493 return lBreak;
1494}
1495
kociendabb0c24b2001-08-24 14:24:40 +00001496// For --enable-final
1497#undef BIDI_DEBUG
1498#undef DEBUG_LINEBREAKS
1499#undef DEBUG_LAYOUT