~ubuntu-branches/ubuntu/jaunty/kde4libs/jaunty

« back to all changes in this revision

Viewing changes to khtml/rendering/bidi.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Andreas Wenning, Jonathan Thomas, Andreas Wenning
  • Date: 2009-04-01 05:55:52 UTC
  • mfrom: (1.1.29 upstream)
  • Revision ID: james.westby@ubuntu.com-20090401055552-uel5di5f3xiftax3
Tags: 4:4.2.2-0ubuntu1
[ Jonathan Thomas ]
* New upstream release (LP: #344709, #348823):
  - Bump upstreamversion and runtimedeps in debian/rules
  - Remove kubuntu_65_kcmdlineargs_decoding_svn934640.diff, applied upstream

[ Andreas Wenning ]
* Remove patch kubuntu_69_do_not_show_plasma_popups_over_screensaver.diff,
  applied upstream

Show diffs side-by-side

added added

removed removed

Lines of Context:
4
4
 * Copyright (C) 2000-2003 Lars Knoll (knoll@kde.org)
5
5
 *           (C) 2003-2007 Apple Computer, Inc.
6
6
 *           (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com)
7
 
 *           (C) 2007 Germain Garand (germain@ebooksfrance.org)
 
7
 *           (C) 2007-2009 Germain Garand (germain@ebooksfrance.org)
8
8
 *
9
9
 * This library is free software; you can redistribute it and/or
10
10
 * modify it under the terms of the GNU Library General Public
50
50
// an iterator which goes through a BidiParagraph
51
51
struct BidiIterator
52
52
{
53
 
    BidiIterator() : par(0), obj(0), pos(0) {}
54
 
    BidiIterator(RenderBlock *_par, RenderObject *_obj, unsigned int _pos) : par(_par), obj(_obj), pos(_pos) {}
 
53
    BidiIterator() : par(0), obj(0), pos(0), endOfInline(false) {}
 
54
    BidiIterator(RenderBlock *_par, RenderObject *_obj, unsigned int _pos, bool eoi=false) : par(_par), obj(_obj), pos(_pos), endOfInline(eoi) {}
55
55
 
56
 
    void increment( BidiState &bidi );
 
56
    void increment( BidiState *bidi=0, bool skipInlines=true );
57
57
 
58
58
    bool atEnd() const;
59
59
 
63
63
    RenderBlock *par;
64
64
    RenderObject *obj;
65
65
    unsigned int pos;
 
66
    bool endOfInline;
66
67
};
67
68
 
68
69
struct BidiState {
96
97
static bool isLineEmpty = true;
97
98
static bool previousLineBrokeAtBR = false;
98
99
static QChar::Direction dir = QChar::DirON;
99
 
static bool adjustEmbedding;
100
100
static bool emptyRun = true;
101
101
static int numSpaces;
102
102
 
125
125
    return result;
126
126
}
127
127
 
128
 
static int inlineWidth(RenderObject* child, bool start = true, bool end = true)
129
 
{
130
 
    int extraWidth = 0;
131
 
    RenderObject* parent = child->parent();
132
 
    while (parent->isInline() && !parent->isInlineBlockOrInlineTable()) {
133
 
        if (start && parent->firstChild() == child)
134
 
            extraWidth += getBorderPaddingMargin(parent, false);
135
 
        if (end && parent->lastChild() == child)
136
 
            extraWidth += getBorderPaddingMargin(parent, true);
137
 
        child = parent;
138
 
        parent = child->parent();
139
 
    }
140
 
    return extraWidth;
141
 
}
142
 
 
143
128
#ifndef NDEBUG
144
129
static bool inBidiRunDetach;
145
130
#endif
259
244
{
260
245
    return !(status1 == status2);
261
246
}
262
 
        
263
 
static inline RenderObject *Bidinext(RenderObject *par, RenderObject *current, BidiState &bidi,
264
 
                                     bool skipInlines = true)
 
247
 
 
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)
265
251
{
266
252
    RenderObject *next = 0;
267
 
 
 
253
    bool oldEndOfInline = endOfInline ? *endOfInline : false;
 
254
    if (oldEndOfInline)
 
255
        *endOfInline = false;
268
256
    while(current != 0)
269
257
    {
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 ) {
 
261
            if ( next && bidi) {
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 ) );
279
 
                    embed( d, bidi );
 
267
                    embed( d, *bidi );
280
268
                }
281
269
            }
282
270
        }
283
271
        if (!next) {
 
272
            if (!skipInlines && !oldEndOfInline && current->isInlineFlow() && endOfInline) {
 
273
                next = current;
 
274
                *endOfInline = true;
 
275
                break;
 
276
            }
 
277
 
284
278
            while (current && current != par) {
285
279
                next = current->nextSibling();
286
280
                if (next) break;
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 );
289
283
                }
290
284
                current = current->parent();
 
285
                if (!skipInlines && current && current != par && current->isInlineFlow() && endOfInline) {
 
286
                    next = current;
 
287
                    *endOfInline = true;
 
288
                    break;
 
289
                }
291
290
            }
292
291
        }
293
292
 
302
301
    return next;
303
302
}
304
303
 
305
 
static RenderObject *first( RenderObject *par, BidiState &bidi, bool skipInlines = true )
 
304
static RenderObject *first( RenderObject *par, BidiState *bidi, bool skipInlines = true )
306
305
{
307
306
    if(!par->firstChild()) return 0;
308
307
    RenderObject *o = par->firstChild();
319
318
    return o;
320
319
}
321
320
 
322
 
inline void BidiIterator::increment (BidiState &bidi)
 
321
inline void BidiIterator::increment(BidiState *bidi, bool skipInlines)
323
322
{
324
323
    if(!obj) return;
325
324
    if(obj->isText()) {
326
325
        pos++;
327
326
        if(pos >= static_cast<RenderText *>(obj)->stringLength()) {
328
 
            obj = Bidinext( par, obj, bidi );
 
327
            obj = Bidinext( par, obj, bidi, skipInlines );
329
328
            pos = 0;
330
329
        }
331
330
    } else {
332
 
        obj = Bidinext( par, obj, bidi );
 
331
        obj = Bidinext( par, obj, bidi, skipInlines, &endOfInline );
333
332
        pos = 0;
334
333
    }
335
334
}
384
383
        if (text->text()) {
385
384
            for (int i = bidiRun->start; i < bidiRun->stop; i++) {
386
385
                const QChar c = text->text()[i];
387
 
                if (c.category() == QChar::Separator_Space || c == '\n')
 
386
                if (c.unicode() == '\n' || c.category() == QChar::Separator_Space)
388
387
                    numSpaces++;
389
388
            }
390
389
        }
453
452
    }
454
453
}
455
454
 
456
 
static void checkMidpoints(BidiIterator& lBreak, BidiState &bidi)
 
455
static void checkMidpoints(BidiIterator& lBreak)
457
456
{
458
457
    // Check to see if our last midpoint is a start point beyond the line break.  If so,
459
458
    // shave it off the list, and shave off a trailing space if the previous end point isn't
464
463
        const BidiIterator& startpoint = midpoints[sNumMidpoints-1];
465
464
        BidiIterator currpoint = endpoint;
466
465
        while (!currpoint.atEnd() && currpoint != startpoint && currpoint != lBreak)
467
 
            currpoint.increment( bidi );
 
466
            currpoint.increment();
468
467
        if (currpoint == lBreak) {
469
468
            // We hit the line break before the start point.  Shave off the start point.
470
469
            sNumMidpoints--;
491
490
        smidpoints->resize(sNumMidpoints+10);
492
491
 
493
492
    BidiIterator* midpoints = smidpoints->data();
494
 
    midpoints[sNumMidpoints++] = midpoint;
 
493
 
 
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;
 
500
        n.increment();
 
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;
 
505
        if (!n.atEnd())
 
506
            midpoints[sNumMidpoints++] = n;
 
507
    } else {
 
508
        assert(!midpoint.endOfInline);
 
509
        midpoints[sNumMidpoints++] = midpoint;
 
510
    }
495
511
}
496
512
 
497
513
static void appendRunsForObject(int start, int end, RenderObject* obj, BidiState &bidi)
527
543
            betweenMidpoints = true;
528
544
            sCurrMidpoint++;
529
545
            if (nextMidpoint.pos != UINT_MAX) { // UINT_MAX means stop at the object and don't include any of it.
530
 
                addRun(new (obj->renderArena())
531
 
                    BidiRun(start, nextMidpoint.pos+1, obj, bidi.context, dir));
 
546
               if (!nextMidpoint.endOfInline) // In this context, this flag means the stop point is exclusive, not inclusive (see addMidpoint).
 
547
                   addRun(new (obj->renderArena())
 
548
                       BidiRun(start, nextMidpoint.pos+1, obj, bidi.context, dir));
532
549
                return appendRunsForObject(nextMidpoint.pos+1, end, obj, bidi);
533
550
            }
534
551
        }
544
561
    kDebug(6041) << "appendRun: dir="<<(int)dir;
545
562
#endif
546
563
 
547
 
    bool b = adjustEmbedding;
548
 
    adjustEmbedding = false;
549
 
 
550
564
    int start = bidi.sor.pos;
551
565
    RenderObject *obj = bidi.sor.obj;
552
566
    while( obj && obj != bidi.eor.obj ) {
553
567
        appendRunsForObject(start, obj->length(), obj, bidi);
554
568
        start = 0;
555
 
        obj = Bidinext( bidi.sor.par, obj, bidi );
 
569
        obj = Bidinext( bidi.sor.par, obj);
556
570
    }
557
571
    if (obj)
558
572
        appendRunsForObject(start, bidi.eor.pos+1, obj, bidi);
559
573
 
560
 
    bidi.eor.increment( bidi );
 
574
    bidi.eor.increment();
561
575
    bidi.sor = bidi.eor;
562
576
    dir = QChar::DirON;
563
577
    bidi.status.eor = QChar::DirON;
564
 
    adjustEmbedding = b;
565
578
}
566
579
 
567
580
static void embed( QChar::Direction d, BidiState &bidi )
569
582
#if BIDI_DEBUG > 1
570
583
    qDebug("*** embed dir=%d emptyrun=%d", d, emptyRun );
571
584
#endif
572
 
    bool b = adjustEmbedding ;
573
 
    adjustEmbedding = false;
574
585
    if ( d == QChar::DirPDF ) {
575
586
        BidiContext *c = bidi.context->parent;
576
587
        if (c) {
630
641
            bidi.status.eor = runDir;
631
642
        }
632
643
    }
633
 
    adjustEmbedding = b;
634
644
}
635
645
 
636
646
InlineFlowBox* RenderBlock::createLineBoxes(RenderObject* obj)
1268
1278
 
1269
1279
        // this causes the operator ++ to open and close embedding levels as needed
1270
1280
        // for the CSS unicode-bidi property
1271
 
        adjustEmbedding = true;
1272
 
        bidi.current.increment( bidi );
1273
 
        adjustEmbedding = false;
 
1281
        bidi.current.increment( &bidi );
1274
1282
 
1275
1283
        if ( bidi.current == end ) {
1276
1284
            if ( emptyRun )
1340
1348
#endif
1341
1349
}
1342
1350
 
1343
 
#ifdef APPLE_CHANGES    // KDE handles compact blocks differently
1344
 
static void buildCompactRuns(RenderObject* compactObj, BidiState &bidi)
1345
 
{
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));
1351
 
    }
1352
 
    else {
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;
1360
 
 
1361
 
        betweenMidpoints = false;
1362
 
        isLineEmpty = true;
1363
 
        previousLineBrokeAtBR = true;
1364
 
 
1365
 
        end = compactBlock->findNextLineBreak(start, bidi);
1366
 
        if (!isLineEmpty)
1367
 
            compactBlock->bidiReorderLine(start, end, bidi);
1368
 
    }
1369
 
 
1370
 
 
1371
 
    sCompactFirstBidiRun = sFirstBidiRun;
1372
 
    sCompactLastBidiRun = sLastBidiRun;
1373
 
    sCompactBidiRunCount = sBidiRunCount;
1374
 
 
1375
 
    sNumMidpoints = 0;
1376
 
    sCurrMidpoint = 0;
1377
 
    betweenMidpoints = false;
1378
 
    sBuildingCompactRuns = false;
1379
 
}
1380
 
#endif
1381
 
 
1382
1351
void RenderBlock::layoutInlineChildren(bool relayoutChildren, int breakBeforeLine)
1383
1352
{
1384
1353
    BidiState bidi;
1416
1385
 
1417
1386
    if (firstChild()) {
1418
1387
        // layout replaced elements
1419
 
        RenderObject *o = first( this, bidi, false );
 
1388
        RenderObject *o = first( this, 0, false );
1420
1389
        while ( o ) {
1421
1390
            invalidateVerticalPosition();
1422
1391
            if (o->markedForRepaint()) {
1445
1414
                    o->dirtyInlineBoxes(fullLayout);
1446
1415
                o->setNeedsLayout(false);
1447
1416
            }
1448
 
            o = Bidinext( this, o, bidi, false );
 
1417
            o = Bidinext( this, o, 0, false );
1449
1418
        }
1450
1419
 
1451
1420
        BidiContext *startEmbed;
1462
1431
        bidi.status.last = QChar::DirON;
1463
1432
 
1464
1433
        bidi.context = startEmbed;
1465
 
        adjustEmbedding = true;
1466
1434
 
1467
1435
        // We want to skip ahead to the first dirty line
1468
1436
        BidiIterator start;
1493
1461
            }
1494
1462
            startLine = 0;
1495
1463
        }
1496
 
        
1497
1464
        BidiIterator end = start;
1498
 
 
1499
1465
        bool endLineMatched = false;
1500
 
        
1501
 
        adjustEmbedding = false;
1502
1466
        m_firstLine = true;
1503
1467
 
1504
1468
        if (!smidpoints)
1531
1495
                oldStart = start;
1532
1496
                oldBidi = bidi;
1533
1497
            }
1534
 
#ifdef APPLE_CHANGES    // KDE handles compact blocks differently
1535
 
            if (m_firstLine && firstChild() && firstChild()->isCompact()) {
1536
 
                buildCompactRuns(firstChild(), bidi);
1537
 
                start.obj = firstChild()->nextSibling();
1538
 
                end = start;
1539
 
            }
1540
 
#endif
1541
1498
            if (lineCount == breakBeforeLine) {
1542
1499
                m_height = pageTopAfter(oldPos);
1543
1500
                pagebreakHint = true;
1555
1512
                // At the same time we figure out where border/padding/margin should be applied for
1556
1513
                // inline flow boxes.
1557
1514
 
1558
 
#ifdef APPLE_CHANGES    // KDE handles compact blocks differently
1559
 
                if (sCompactFirstBidiRun) {
1560
 
                    // We have a compact line sharing this line.  Link the compact runs
1561
 
                    // to our runs to create a single line of runs.
1562
 
                    sCompactLastBidiRun->nextRun = sFirstBidiRun;
1563
 
                    sFirstBidiRun = sCompactFirstBidiRun;
1564
 
                    sBidiRunCount += sCompactBidiRunCount;
1565
 
                }
1566
 
#endif
1567
1515
                RootInlineBox* lineBox = 0;
1568
1516
                if (sBidiRunCount) {
1569
1517
                    lineBox = constructLine(start, end);
1598
1546
                }
1599
1547
 
1600
1548
                if( end == start || (end.obj && end.obj->isBR() && !start.obj->isBR() ) ) {
1601
 
                    adjustEmbedding = true;
1602
 
                    end.increment(bidi);
1603
 
                    adjustEmbedding = false;
 
1549
                    end.increment(&bidi);
1604
1550
                } else if (end.obj && end.obj->style()->preserveLF() && end.current() == QChar('\n')) {
1605
 
                    adjustEmbedding = true;
1606
 
                    end.increment(bidi);
1607
 
                    adjustEmbedding = false;
 
1551
                    end.increment(&bidi);
1608
1552
                }
1609
1553
 
1610
1554
                if (lineBox)
1777
1721
        pos = last->lineBreakPos();
1778
1722
        bidi.status = last->lineBreakBidiStatus();
1779
1723
    } else {
1780
 
        adjustEmbedding = true;
1781
 
        startObj = first(this, bidi, 0);
1782
 
        adjustEmbedding = false;
 
1724
        startObj = first(this, &bidi, false);
1783
1725
    }
1784
1726
        
1785
1727
    start = BidiIterator(this, startObj, pos);
1879
1821
 
1880
1822
static inline bool requiresLineBox(BidiIterator& it)
1881
1823
{
1882
 
    if (it.obj->isFloatingOrPositioned() || it.obj->isInlineFlow())
 
1824
    if (it.obj->isFloatingOrPositioned())
1883
1825
        return false;
1884
 
    
 
1826
    if (it.obj->isInlineFlow())
 
1827
        return (getBorderPaddingMargin(it.obj, it.endOfInline) != 0);
1885
1828
    if (it.obj->style()->preserveWS() || it.obj->isBR())
1886
1829
        return true;
1887
1830
 
1901
1844
    assert(inlineObj->parent() == this);
1902
1845
 
1903
1846
    BidiIterator it(this, inlineObj, 0);
1904
 
    BidiState state;
1905
1847
    while (!it.atEnd() && !requiresLineBox(it))
1906
 
        it.increment(state);
 
1848
        it.increment(0, false /*skipInlines*/);
1907
1849
 
1908
1850
    return !it.atEnd();
1909
1851
}
1921
1863
    BidiIterator posStart = start;
1922
1864
    bool hadPosStart = false;
1923
1865
 
 
1866
    // Skip initial whitespace
1924
1867
    while (!start.atEnd() && !requiresLineBox(start)) {
1925
1868
        if( start.obj->isFloating() || start.obj->isPosWithStaticDim()) {
1926
1869
            RenderObject *o = start.obj;
1931
1874
                width = lineWidth(m_height);
1932
1875
            }
1933
1876
            else if (o->isPositioned()) {
 
1877
                // add midpoints to have positioned objects at the correct static location
 
1878
                // while still skipping initial whitespace.
1934
1879
                if (!hadPosStart) {
1935
1880
                    hadPosStart = true;
1936
1881
                    posStart = start;
1937
 
                    // end
 
1882
                    // include this object then stop
1938
1883
                    addMidpoint(BidiIterator(0, o, 0));
1939
1884
                } else {
1940
 
                    // start/end
 
1885
                    // start/stop
1941
1886
                    addMidpoint(BidiIterator(0, o, 0));
1942
1887
                    addMidpoint(BidiIterator(0, o, 0));
1943
1888
                }
1944
1889
                setStaticPosition(this, o);
1945
1890
            }
1946
1891
        }
1947
 
        adjustEmbedding = true;
1948
 
        start.increment(bidi);
1949
 
        adjustEmbedding = false;
 
1892
        start.increment(&bidi, false /*skipInlines*/);
1950
1893
    }
1951
1894
 
1952
1895
    if (hadPosStart && !start.atEnd())
1955
1898
    if ( start.atEnd() ){
1956
1899
        if (hadPosStart) {
1957
1900
            start = posStart;
1958
 
            posStart.increment(bidi);
 
1901
            posStart.increment();
1959
1902
            return posStart;
1960
1903
        }
1961
1904
        return start;
1962
1905
    }
1963
1906
 
 
1907
    // This variable says we have encountered an object after which initial whitespace should be ignored (e.g. InlineFlows at the begining of a line).
 
1908
    // Either we have nothing to do, if there is no whitespace after the object... or we have to enter the ignoringSpaces state.
 
1909
    // This dilemma will be resolved when we have a peek at the next object.
 
1910
    bool checkShouldIgnoreInitialWhitespace = false;
 
1911
 
1964
1912
    // This variable is used only if whitespace isn't set to PRE, and it tells us whether
1965
1913
    // or not we are currently ignoring whitespace.
1966
1914
    bool ignoringSpaces = false;
1970
1918
    // this to detect when we encounter a second space so we know we have to terminate
1971
1919
    // a run.
1972
1920
    bool currentCharacterIsSpace = false;
 
1921
 
1973
1922
    RenderObject* trailingSpaceObject = 0;
1974
1923
 
1975
1924
    BidiIterator lBreak = start;
1976
 
 
1977
 
    RenderObject *o = start.obj;
1978
 
    RenderObject *last = o;
 
1925
    InlineMinMaxIterator it(start.par, start.obj, start.endOfInline, false /*skipPositioned*/);
 
1926
    InlineMinMaxIterator lastIt = it;
1979
1927
    int pos = start.pos;
1980
1928
 
1981
1929
    bool prevLineBrokeCleanly = previousLineBrokeAtBR;
1982
1930
    previousLineBrokeAtBR = false;
1983
1931
 
 
1932
    RenderObject* o = it.current;
1984
1933
    while( o ) {
1985
1934
#ifdef DEBUG_LINEBREAKS
1986
1935
        kDebug(6041) << "new object "<< o <<" width = " << w <<" tmpw = " << tmpW;
1989
1938
            if( w + tmpW <= width ) {
1990
1939
                lBreak.obj = o;
1991
1940
                lBreak.pos = 0;
 
1941
                lBreak.endOfInline = it.endOfInline;
1992
1942
 
1993
1943
                // A <br> always breaks a line, so don't let the line be collapsed
1994
1944
                // away. Also, the space at the end of a line with a <br> does not
2039
1989
                }
2040
1990
            }
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!
2045
1995
                w += tmpW;
2046
1996
                tmpW = 0;
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;
2049
2004
            }
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();
2055
2008
 
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
2059
2012
            // for the marquee when checking for line breaking.
2060
2013
            if (o->isHTMLMarquee() && o->layer() && o->layer()->marquee())
2061
2014
                currWS = o->layer()->marquee()->whiteSpace();
2062
 
            if (last->isHTMLMarquee() && last->layer() && last->layer()->marquee())
2063
 
                lastWS = last->layer()->marquee()->whiteSpace();
 
2015
            if (lastIt.current->isHTMLMarquee() && lastIt.current->layer() && lastIt.current->layer()->marquee())
 
2016
                lastWS = lastIt.current->layer()->marquee()->whiteSpace();
2064
2017
 
2065
2018
            // Break on replaced elements if either has normal white-space.
2066
2019
            if (currWS == NORMAL || lastWS == NORMAL) {
2068
2021
                tmpW = 0;
2069
2022
                lBreak.obj = o;
2070
2023
                lBreak.pos = 0;
 
2024
                lBreak.endOfInline = false;
2071
2025
            }
2072
2026
 
2073
 
            tmpW += o->width()+o->marginLeft()+o->marginRight()+inlineWidth(o);
 
2027
            tmpW += o->width()+o->marginLeft()+o->marginRight();
2074
2028
            if (ignoringSpaces) {
2075
2029
                BidiIterator startMid( 0, o, 0 );
2076
2030
                addMidpoint(startMid);
2081
2035
            trailingSpaceObject = 0;
2082
2036
 
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.
2087
 
                //
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);
2098
 
                }
 
2038
                checkShouldIgnoreInitialWhitespace = true;
2099
2039
            }
2100
2040
        } else if ( o->isText() ) {
2101
2041
            RenderText *t = static_cast<RenderText *>(o);
2112
2052
#ifdef APPLE_CHANGES
2113
2053
            int wordSpacing = o->style()->wordSpacing();
2114
2054
#endif
2115
 
            bool appliedStartWidth = pos > 0; // If the span originated on a previous line,
2116
 
                                              // then assume the start width has been applied.
2117
 
            bool appliedEndWidth = false;
2118
2055
            bool nextIsSoftBreakable = false;
2119
2056
 
2120
2057
            while(len) {
2122
2059
                bool isSoftBreakable = nextIsSoftBreakable;
2123
2060
                nextIsSoftBreakable = false;
2124
2061
                const QChar c = str[pos];
2125
 
                currentCharacterIsSpace = c == ' ';
 
2062
                currentCharacterIsSpace = c.unicode() == ' ';
2126
2063
 
2127
2064
                if (preserveWS || !currentCharacterIsSpace)
2128
2065
                    isLineEmpty = false;
2174
2111
                    }
2175
2112
                }
2176
2113
 
2177
 
                if ( (preserveLF && c == '\n') || (autoWrap && (isBreakable( str, pos, strlen ) || isSoftBreakable)) ) {
 
2114
                if ( (preserveLF && c.unicode() == '\n') || (autoWrap && (isBreakable( str, pos, strlen ) || isSoftBreakable)) ) {
2178
2115
 
2179
2116
                    tmpW += t->width(lastSpace, pos - lastSpace, f);
2180
 
                    if (!appliedStartWidth) {
2181
 
                        tmpW += inlineWidth(o, true, false);
2182
 
                        appliedStartWidth = true;
2183
 
                    }
2184
2117
#ifdef APPLE_CHANGES
2185
2118
                    applyWordSpacing = (wordSpacing && currentCharacterIsSpace && !previousCharacterIsSpace &&
2186
2119
                        !t->containsOnlyWhitespace(pos+1, strlen-(pos+1)));
2217
2150
                            tmpW -= t->width(pos-1, 1, f);
2218
2151
                    }
2219
2152
 
2220
 
                    if( preserveLF && *(str+pos) == '\n' ) {
 
2153
                    if( preserveLF && (str+pos)->unicode() == '\n' ) {
2221
2154
                        lBreak.obj = o;
2222
2155
                        lBreak.pos = pos;
 
2156
                        lBreak.endOfInline = false;
2223
2157
 
2224
2158
#ifdef DEBUG_LINEBREAKS
2225
2159
                        kDebug(6041) << "forced break sol: " << start.obj << " " << start.pos << "   end: " << lBreak.obj << " " << lBreak.pos << "   width=" << w;
2232
2166
                        tmpW = 0;
2233
2167
                        lBreak.obj = o;
2234
2168
                        lBreak.pos = pos;
 
2169
                        lBreak.endOfInline = false;
2235
2170
                    }
2236
2171
 
2237
2172
                    lastSpace = pos;
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);
2279
2210
        } else
2280
2211
            KHTMLAssert( false );
2281
2212
 
2282
 
        RenderObject* next = Bidinext(start.par, o, bidi);
2283
 
        bool autoWrap = o->style()->autoWrap();
 
2213
        InlineMinMaxIterator savedIt = lastIt;
 
2214
        lastIt = it;
 
2215
        o = it.next();
 
2216
 
 
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;
 
2221
            o = it.next();
 
2222
        }
 
2223
 
 
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);
 
2233
                }
 
2234
            }
 
2235
            checkShouldIgnoreInitialWhitespace = false;
 
2236
        }
 
2237
 
 
2238
        bool autoWrap = lastIt.current->style()->autoWrap();
 
2239
        bool canBreak = !lBreak.obj || !lBreak.obj->isInlineFlow() || !lBreak.obj->firstChild();
 
2240
 
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;
2291
 
                else {
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;
2301
 
                        }
2302
 
                    }
2303
 
 
2304
 
                    bool canPlaceOnLine = (w + tmpW <= width+1) || !autoWrap;
2305
 
                    if (canPlaceOnLine && checkForBreak) {
2306
 
                        w += tmpW;
2307
 
                        tmpW = 0;
2308
 
                        lBreak.obj = next;
2309
 
                        lBreak.pos = 0;
2310
 
                    }
2311
 
                }
2312
 
            }
2313
 
        }
2314
 
 
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;
2321
 
 
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);
2331
 
            }
2332
 
            if( !w && m_height < fb && width < newLineWidth ) {
2333
 
                m_height = fb;
2334
 
                width = newLineWidth;
 
2242
        if (canBreak) {
 
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;
 
2249
                    else {
 
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;
 
2259
                            }
 
2260
                        }
 
2261
 
 
2262
                        bool canPlaceOnLine = (w + tmpW <= width+1) || !autoWrap;
 
2263
                        if (canPlaceOnLine && checkForBreak) {
 
2264
                            w += tmpW;
 
2265
                            tmpW = 0;
 
2266
                            lBreak.obj = it.current;
 
2267
                            lBreak.pos = 0;
 
2268
                            lBreak.endOfInline = it.endOfInline;
 
2269
                        }
 
2270
                    }
 
2271
                }
 
2272
            }
 
2273
 
 
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;
 
2278
 
 
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);
 
2288
                }
 
2289
                if( !w && m_height < fb && width < newLineWidth ) {
 
2290
                    m_height = fb;
 
2291
                    width = newLineWidth;
2335
2292
#ifdef DEBUG_LINEBREAKS
2336
2293
                kDebug() << "RenderBlock::findNextLineBreak new position at " << m_height << " newWidth " << width;
2337
2294
#endif
 
2295
                }
 
2296
 
 
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) {
 
2301
                    it = lastIt;
 
2302
                    lastIt = savedIt;
 
2303
                    o = it.current;
 
2304
                    goto end;
 
2305
                }
2338
2306
            }
2339
 
 
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)
2344
 
                goto end;
2345
2307
        }
2346
 
 
2347
 
        last = o;
2348
 
        o = next;
2349
 
 
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.
2352
2310
            w += tmpW;
2353
2311
            tmpW = 0;
2354
2312
            lBreak.obj = o;
2355
2313
            lBreak.pos = 0;
 
2314
            lBreak.endOfInline = it.endOfInline;
2356
2315
        }
2357
2316
 
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;
2362
2321
 
2363
2322
        pos = 0;
2366
2325
#ifdef DEBUG_LINEBREAKS
2367
2326
    kDebug( 6041 ) << "end of par, width = " << width << " linewidth = " << w + tmpW;
2368
2327
#endif
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;
2372
2332
    }
2373
2333
 
2374
2334
 end:
2375
 
 
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.
2380
2340
            if(pos != 0) {
2381
2341
                lBreak.obj = o;
2382
2342
                lBreak.pos = pos - 1;
 
2343
                lBreak.endOfInline = it.endOfInline;
2383
2344
            } else {
2384
 
                lBreak.obj = last;
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;
2386
2348
            }
2387
2349
        } else if( lBreak.obj ) {
2388
 
            if( last != o ) {
 
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;
2392
2355
            } else {
 
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.)
 
2358
 
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;
2398
2365
            }
2399
2366
        }
2400
2367
    }
2402
2369
    if (hadPosStart)
2403
2370
        start = posStart;
2404
2371
 
2405
 
    // make sure we consume at least one char/object.
2406
 
    if( lBreak == start )
2407
 
        lBreak.increment(bidi);
 
2372
    if( lBreak == start) {
 
2373
        // make sure we consume at least one char/object.
 
2374
        lBreak.increment();
 
2375
    }
2408
2376
 
2409
2377
#ifdef DEBUG_LINEBREAKS
2410
2378
    kDebug(6041) << "regular break sol: " << start.obj << " " << start.pos << "   end: " << lBreak.obj << " " << lBreak.pos << "   width=" << w;
2411
2379
#endif
2412
2380
 
2413
2381
    // Sanity check our midpoints.
2414
 
    checkMidpoints(lBreak, bidi);
 
2382
    checkMidpoints(lBreak);
2415
2383
 
2416
2384
    if (trailingSpaceObject) {
2417
2385
        // This object is either going to be part of the last midpoint, or it is going
2438
2406
    // code.
2439
2407
    if (lBreak.pos > 0) {
2440
2408
        lBreak.pos--;
2441
 
        lBreak.increment(bidi);
 
2409
        lBreak.increment();
2442
2410
    }
2443
2411
 
2444
2412
    if (lBreak.obj && lBreak.pos >= 2 && lBreak.obj->isText()) {