260
245
return !(status1 == status2);
263
static inline RenderObject *Bidinext(RenderObject *par, RenderObject *current, BidiState &bidi,
264
bool skipInlines = true)
248
// when modifying this function, make sure you check InlineMinMaxIterator::next() as well.
249
static inline RenderObject *Bidinext(RenderObject *par, RenderObject *current, BidiState *bidi=0,
250
bool skipInlines = true, bool* endOfInline = 0)
266
252
RenderObject *next = 0;
253
bool oldEndOfInline = endOfInline ? *endOfInline : false;
255
*endOfInline = false;
268
256
while(current != 0)
270
258
//kDebug( 6040 ) << "current = " << current;
271
if (!current->isFloating() && !current->isReplaced() && !current->isPositioned()) {
259
if (!oldEndOfInline && !current->isFloating() && !current->isReplaced() && !current->isPositioned()) {
272
260
next = current->firstChild();
273
if ( next && adjustEmbedding ) {
274
262
EUnicodeBidi ub = next->style()->unicodeBidi();
275
263
if ( ub != UBNormal && !emptyRun ) {
276
264
EDirection dir = next->style()->direction();
277
265
QChar::Direction d = ( ub == Embed ? ( dir == RTL ? QChar::DirRLE : QChar::DirLRE )
278
266
: ( dir == RTL ? QChar::DirRLO : QChar::DirLRO ) );
272
if (!skipInlines && !oldEndOfInline && current->isInlineFlow() && endOfInline) {
284
278
while (current && current != par) {
285
279
next = current->nextSibling();
287
if ( adjustEmbedding && current->style()->unicodeBidi() != UBNormal && !emptyRun ) {
288
embed( QChar::DirPDF, bidi );
281
if ( bidi && current->style()->unicodeBidi() != UBNormal && !emptyRun ) {
282
embed( QChar::DirPDF, *bidi );
290
284
current = current->parent();
285
if (!skipInlines && current && current != par && current->isInlineFlow() && endOfInline) {
491
490
smidpoints->resize(sNumMidpoints+10);
493
492
BidiIterator* midpoints = smidpoints->data();
494
midpoints[sNumMidpoints++] = midpoint;
494
// do not place midpoints in inline flows that are going to be skipped by the bidi iteration process.
495
// Place them at the next non-skippable object instead.
496
// #### eventually, we may want to have the same iteration in bidi and in findNextLineBreak,
497
// then this extra complexity can go away.
498
if (midpoint.obj && midpoint.obj->isInlineFlow() && (midpoint.obj->firstChild() || midpoint.endOfInline)) {
499
BidiIterator n = midpoint;
501
assert(!n.endOfInline);
502
// we'll recycle the endOfInline flag to mean : don't include this stop point, stop right before it.
503
// this is necessary because we just advanced our position to skip an inline, so we passed the real stop point
504
n.endOfInline = true;
506
midpoints[sNumMidpoints++] = n;
508
assert(!midpoint.endOfInline);
509
midpoints[sNumMidpoints++] = midpoint;
497
513
static void appendRunsForObject(int start, int end, RenderObject* obj, BidiState &bidi)
1343
#ifdef APPLE_CHANGES // KDE handles compact blocks differently
1344
static void buildCompactRuns(RenderObject* compactObj, BidiState &bidi)
1346
sBuildingCompactRuns = true;
1347
if (!compactObj->isRenderBlock()) {
1348
// Just append a run for our object.
1349
isLineEmpty = false;
1350
addRun(new (compactObj->renderArena()) BidiRun(0, compactObj->length(), compactObj, bidi.context, dir));
1353
// Format the compact like it is its own single line. We build up all the runs for
1354
// the little compact and then reorder them for bidi.
1355
RenderBlock* compactBlock = static_cast<RenderBlock*>(compactObj);
1356
adjustEmbedding = true;
1357
BidiIterator start(compactBlock, first(compactBlock, bidi), 0);
1358
adjustEmbedding = false;
1359
BidiIterator end = start;
1361
betweenMidpoints = false;
1363
previousLineBrokeAtBR = true;
1365
end = compactBlock->findNextLineBreak(start, bidi);
1367
compactBlock->bidiReorderLine(start, end, bidi);
1371
sCompactFirstBidiRun = sFirstBidiRun;
1372
sCompactLastBidiRun = sLastBidiRun;
1373
sCompactBidiRunCount = sBidiRunCount;
1377
betweenMidpoints = false;
1378
sBuildingCompactRuns = false;
1382
1351
void RenderBlock::layoutInlineChildren(bool relayoutChildren, int breakBeforeLine)
1384
1353
BidiState bidi;
2041
1991
} else if (o->isInlineFlow()) {
2042
// Only empty inlines matter. We treat those similarly to replaced elements.
2043
KHTMLAssert(!o->firstChild());
2044
if (o->isWordBreak()) {
1992
tmpW += getBorderPaddingMargin(o, it.endOfInline);
1993
if (isLineEmpty) isLineEmpty = !tmpW;
1994
if (o->isWordBreak()) { // #### shouldn't be an InlineFlow!
2047
1997
lBreak.obj = o;
2048
1998
lBreak.pos = 0;
1999
lBreak.endOfInline = it.endOfInline;
2000
} else if (!it.endOfInline) {
2001
// this is the beginning of the line (other non-initial inline flows are handled directly when
2002
// incrementing the iterator below). We want to skip initial whitespace as much as possible.
2003
checkShouldIgnoreInitialWhitespace = true;
2050
tmpW += o->marginLeft()+o->borderLeft()+o->paddingLeft()+
2051
o->marginRight()+o->borderRight()+o->paddingRight();
2052
2005
} else if ( o->isReplaced() || o->isGlyph() ) {
2053
2006
EWhiteSpace currWS = o->style()->whiteSpace();
2054
EWhiteSpace lastWS = last->style()->whiteSpace();
2007
EWhiteSpace lastWS = lastIt.current->style()->whiteSpace();
2056
2009
// WinIE marquees have different whitespace characteristics by default when viewed from
2057
2010
// the outside vs. the inside. Text inside is NOWRAP, and so we altered the marquee's
2081
2035
trailingSpaceObject = 0;
2083
2037
if (o->isListMarker() && o->style()->listStylePosition() == OUTSIDE) {
2084
// The marker must not have an effect on whitespace at the start
2085
// of the line. We start ignoring spaces to make sure that any additional
2086
// spaces we see will be discarded.
2088
// Optimize for a common case. If we can't find whitespace after the list
2089
// item, then this is all moot. -dwh
2090
RenderObject* next = Bidinext( start.par, o, bidi );
2091
if (!style()->preserveWS() && next && next->isText() && static_cast<RenderText*>(next)->stringLength() > 0 &&
2092
(static_cast<RenderText*>(next)->text()[0].category() == QChar::Separator_Space ||
2093
static_cast<RenderText*>(next)->text()[0] == '\n')) {
2094
currentCharacterIsSpace = true;
2095
ignoringSpaces = true;
2096
BidiIterator endMid( 0, o, 0 );
2097
addMidpoint(endMid);
2038
checkShouldIgnoreInitialWhitespace = true;
2100
2040
} else if ( o->isText() ) {
2101
2041
RenderText *t = static_cast<RenderText *>(o);
2272
2207
// IMPORTANT: pos is > length here!
2273
2208
if (!ignoringSpaces)
2274
2209
tmpW += t->width(lastSpace, pos - lastSpace, f);
2275
if (!appliedStartWidth)
2276
tmpW += inlineWidth(o, true, false);
2277
if (!appliedEndWidth)
2278
tmpW += inlineWidth(o, false, true);
2280
2211
KHTMLAssert( false );
2282
RenderObject* next = Bidinext(start.par, o, bidi);
2283
bool autoWrap = o->style()->autoWrap();
2213
InlineMinMaxIterator savedIt = lastIt;
2217
// advance the iterator to the next non-inline-flow
2218
while (o && o->isInlineFlow() && !o->isWordBreak()) {
2219
tmpW += getBorderPaddingMargin(o, it.endOfInline);
2220
if (isLineEmpty) isLineEmpty = !tmpW;
2224
if (checkShouldIgnoreInitialWhitespace) {
2225
// Check if we should switch to ignoringSpaces state
2226
if (!style()->preserveWS() && it.current && it.current->isText()) {
2227
const RenderText* rt = static_cast<RenderText*>(it.current);
2228
if (rt->stringLength() > 0 && (rt->text()[0].category() == QChar::Separator_Space || rt->text()[0] == '\n')) {
2229
currentCharacterIsSpace = true;
2230
ignoringSpaces = true;
2231
BidiIterator endMid( 0, lastIt.current, 0 );
2232
addMidpoint(endMid);
2235
checkShouldIgnoreInitialWhitespace = false;
2238
bool autoWrap = lastIt.current->style()->autoWrap();
2239
bool canBreak = !lBreak.obj || !lBreak.obj->isInlineFlow() || !lBreak.obj->firstChild();
2284
2241
bool checkForBreak = autoWrap;
2285
if (w && w + tmpW > width && lBreak.obj && !o->style()->preserveLF() && !autoWrap)
2286
checkForBreak = true;
2287
else if (next && o->isText() && next->isText() && !next->isBR()) {
2288
if (autoWrap || next->style()->autoWrap()) {
2289
if (currentCharacterIsSpace)
2290
checkForBreak = true;
2292
checkForBreak = false;
2293
RenderText* nextText = static_cast<RenderText*>(next);
2294
if (nextText->stringLength() != 0) {
2295
QChar c = nextText->text()[0];
2296
if (c == ' ' || c == '\t' || (c == '\n' && !next->style()->preserveLF())) {
2297
// If the next item on the line is text, and if we did not end with
2298
// a space, then the next text run continues our word (and so it needs to
2299
// keep adding to |tmpW|. Just update and continue.
2300
checkForBreak = true;
2304
bool canPlaceOnLine = (w + tmpW <= width+1) || !autoWrap;
2305
if (canPlaceOnLine && checkForBreak) {
2315
if (checkForBreak && (w + tmpW > width)) {
2316
//kDebug() << " too wide w=" << w << " tmpW = " << tmpW << " width = " << width;
2317
//kDebug() << "start=" << start.obj << " current=" << o;
2318
// if we have floats, try to get below them.
2319
if (currentCharacterIsSpace && !ignoringSpaces && !o->style()->preserveWS())
2320
trailingSpaceObject = 0;
2322
int fb = nearestFloatBottom(m_height);
2323
int newLineWidth = lineWidth(fb);
2324
// See if |tmpW| will fit on the new line. As long as it does not,
2325
// keep adjusting our float bottom until we find some room.
2326
int lastFloatBottom = m_height;
2327
while (lastFloatBottom < fb && tmpW > newLineWidth) {
2328
lastFloatBottom = fb;
2329
fb = nearestFloatBottom(fb);
2330
newLineWidth = lineWidth(fb);
2332
if( !w && m_height < fb && width < newLineWidth ) {
2334
width = newLineWidth;
2243
if (w && w + tmpW > width && lBreak.obj && !lastIt.current->style()->preserveLF() && !autoWrap)
2244
checkForBreak = true;
2245
else if (it.current && lastIt.current->isText() && it.current->isText() && !it.current->isBR()) {
2246
if (autoWrap || it.current->style()->autoWrap()) {
2247
if (currentCharacterIsSpace)
2248
checkForBreak = true;
2250
checkForBreak = false;
2251
RenderText* nextText = static_cast<RenderText*>(it.current);
2252
if (nextText->stringLength() != 0) {
2253
QChar c = nextText->text()[0];
2254
if (c == ' ' || c == '\t' || (c == '\n' && !it.current->style()->preserveLF())) {
2255
// If the next item on the line is text, and if we did not end with
2256
// a space, then the next text run continues our word (and so it needs to
2257
// keep adding to |tmpW|. Just update and continue.
2258
checkForBreak = true;
2262
bool canPlaceOnLine = (w + tmpW <= width+1) || !autoWrap;
2263
if (canPlaceOnLine && checkForBreak) {
2266
lBreak.obj = it.current;
2268
lBreak.endOfInline = it.endOfInline;
2274
if (checkForBreak && (w + tmpW > width)) {
2275
// if we have floats, try to get below them.
2276
if (currentCharacterIsSpace && !ignoringSpaces && !lastIt.current->style()->preserveWS())
2277
trailingSpaceObject = 0;
2279
int fb = nearestFloatBottom(m_height);
2280
int newLineWidth = lineWidth(fb);
2281
// See if |tmpW| will fit on the new line. As long as it does not,
2282
// keep adjusting our float bottom until we find some room.
2283
int lastFloatBottom = m_height;
2284
while (lastFloatBottom < fb && tmpW > newLineWidth) {
2285
lastFloatBottom = fb;
2286
fb = nearestFloatBottom(fb);
2287
newLineWidth = lineWidth(fb);
2289
if( !w && m_height < fb && width < newLineWidth ) {
2291
width = newLineWidth;
2335
2292
#ifdef DEBUG_LINEBREAKS
2336
2293
kDebug() << "RenderBlock::findNextLineBreak new position at " << m_height << " newWidth " << width;
2297
// |width| may have been adjusted because we got shoved down past a float (thus
2298
// giving us more room), so we need to retest, and only jump to
2299
// the end label if we still don't fit on the line. -dwh
2300
if (w + tmpW > width) {
2340
// |width| may have been adjusted because we got shoved down past a float (thus
2341
// giving us more room), so we need to retest, and only jump to
2342
// the end label if we still don't fit on the line. -dwh
2343
if (w + tmpW > width)
2350
if (!last->isFloatingOrPositioned() && last->isReplaced() && last->style()->autoWrap()) {
2308
if (!lastIt.current->isFloatingOrPositioned() && lastIt.current->isReplaced() && lastIt.current->style()->autoWrap()) {
2351
2309
// Go ahead and add in tmpW.
2354
2312
lBreak.obj = o;
2355
2313
lBreak.pos = 0;
2314
lBreak.endOfInline = it.endOfInline;
2358
2317
// Clear out our character space bool, since inline <pre>s don't collapse whitespace
2359
2318
// with adjacent inline normal/nowrap spans.
2360
if (last->style()->preserveWS())
2319
if (lastIt.current->style()->preserveWS())
2361
2320
currentCharacterIsSpace = false;
2366
2325
#ifdef DEBUG_LINEBREAKS
2367
2326
kDebug( 6041 ) << "end of par, width = " << width << " linewidth = " << w + tmpW;
2369
if( w + tmpW <= width || (last && !last->style()->autoWrap())) {
2328
if( w + tmpW <= width || (lastIt.current && !lastIt.current->style()->autoWrap())) {
2370
2329
lBreak.obj = 0;
2371
2330
lBreak.pos = 0;
2331
lBreak.endOfInline = false;
2376
if( lBreak == start && !lBreak.obj->isBR() ) {
2377
// we just add as much as possible
2335
if ( lBreak == start && !lBreak.obj->isBR() ) {
2336
// we didn't find any suitable break point so far
2337
// so we'll just add as much as possible
2378
2338
if ( style()->whiteSpace() == PRE ) {
2379
2339
// FIXME: Don't really understand this case.
2381
2341
lBreak.obj = o;
2382
2342
lBreak.pos = pos - 1;
2343
lBreak.endOfInline = it.endOfInline;
2385
lBreak.pos = last->isText() ? last->length() : 0;
2345
lBreak.obj = lastIt.current;
2346
lBreak.pos = lastIt.current->isText() ? lastIt.current->length() : 0;
2347
lBreak.endOfInline = lastIt.endOfInline;
2387
2349
} else if( lBreak.obj ) {
2350
if( lastIt.current != o) {
2389
2351
// better to break between object boundaries than in the middle of a word
2390
2352
lBreak.obj = o;
2391
2353
lBreak.pos = 0;
2354
lBreak.endOfInline = it.endOfInline;
2356
// (it seems this case can only happen for an object at the beginning of the line,
2357
// that triggered a jump to the |end| label without any iteration.)
2393
2359
// Don't ever break in the middle of a word if we can help it.
2394
2360
// There's no room at all. We just have to be on this line,
2395
2361
// even though we'll spill out.
2396
2362
lBreak.obj = o;
2397
2363
lBreak.pos = pos;
2364
lBreak.endOfInline = it.endOfInline;