src/gui/text/qtextlayout.cpp
changeset 37 758a864f9613
parent 33 3e2da88830cd
--- a/src/gui/text/qtextlayout.cpp	Fri Sep 17 08:34:18 2010 +0300
+++ b/src/gui/text/qtextlayout.cpp	Mon Oct 04 01:19:32 2010 +0300
@@ -74,6 +74,8 @@
 
     int pos = line.length;
     const HB_CharAttributes *attributes = eng->attributes();
+    if (!attributes)
+        return QFixed();
     while (pos > 0 && attributes[line.from + pos - 1].whiteSpace)
         --pos;
     return eng->width(line.from + pos, line.length - pos);
@@ -601,7 +603,7 @@
 void QTextLayout::beginLayout()
 {
 #ifndef QT_NO_DEBUG
-    if (d->layoutData && d->layoutData->inLayout) {
+    if (d->layoutData && d->layoutData->layoutState == QTextEngine::InLayout) {
         qWarning("QTextLayout::beginLayout: Called while already doing layout");
         return;
     }
@@ -609,7 +611,7 @@
     d->invalidate();
     d->clearLineData();
     d->itemize();
-    d->layoutData->inLayout = true;
+    d->layoutData->layoutState = QTextEngine::InLayout;
 }
 
 /*!
@@ -618,7 +620,7 @@
 void QTextLayout::endLayout()
 {
 #ifndef QT_NO_DEBUG
-    if (!d->layoutData || !d->layoutData->inLayout) {
+    if (!d->layoutData || d->layoutData->layoutState == QTextEngine::LayoutEmpty) {
         qWarning("QTextLayout::endLayout: Called without beginLayout()");
         return;
     }
@@ -627,7 +629,7 @@
     if (l && d->lines.at(l-1).length < 0) {
         QTextLine(l-1, d).setNumColumns(INT_MAX);
     }
-    d->layoutData->inLayout = false;
+    d->layoutData->layoutState = QTextEngine::LayoutEmpty;
     if (!d->cacheGlyphs)
         d->freeMemory();
 }
@@ -757,11 +759,14 @@
 QTextLine QTextLayout::createLine()
 {
 #ifndef QT_NO_DEBUG
-    if (!d->layoutData || !d->layoutData->inLayout) {
+    if (!d->layoutData || d->layoutData->layoutState == QTextEngine::LayoutEmpty) {
         qWarning("QTextLayout::createLine: Called without layouting");
         return QTextLine();
     }
 #endif
+    if (d->layoutData->layoutState == QTextEngine::LayoutFailed)
+        return QTextLine();
+
     int l = d->lines.size();
     if (l && d->lines.at(l-1).length < 0) {
         QTextLine(l-1, d).setNumColumns(INT_MAX);
@@ -1212,7 +1217,7 @@
 
         bool hasText = (selection.format.foreground().style() != Qt::NoBrush);
         bool hasBackground= (selection.format.background().style() != Qt::NoBrush);
-        
+
         if (hasBackground) {
             selection.format.setProperty(ObjectSelectionBrush, selection.format.property(QTextFormat::BackgroundBrush));
             // don't just clear the property, set an empty brush that overrides a potential
@@ -1704,14 +1709,18 @@
             return glyphs.glyphs[logClusters[currentPosition - 1]];
         }
 
+        inline void adjustRightBearing(glyph_t glyph)
+        {
+            qreal rb;
+            fontEngine->getGlyphBearings(glyph, 0, &rb);
+            rightBearing = qMin(QFixed(), QFixed::fromReal(rb));
+        }
+
         inline void adjustRightBearing()
         {
             if (currentPosition <= 0)
                 return;
-
-            qreal rb;
-            fontEngine->getGlyphBearings(currentGlyph(), 0, &rb);
-            rightBearing = qMin(QFixed(), QFixed::fromReal(rb));
+            adjustRightBearing(currentGlyph());
         }
 
         inline void resetRightBearing()
@@ -1722,7 +1731,7 @@
     };
 
 inline bool LineBreakHelper::checkFullOtherwiseExtend(QScriptLine &line)
-{        
+{
     LB_DEBUG("possible break width %f, spacew=%f", tmpData.textWidth.toReal(), spaceData.textWidth.toReal());
 
     QFixed newWidth = calculateNewWidth(line);
@@ -1788,19 +1797,31 @@
     bool breakany = (wrapMode == QTextOption::WrapAnywhere);
     lbh.manualWrap = (wrapMode == QTextOption::ManualWrap || wrapMode == QTextOption::NoWrap);
 
-    // #### binary search!
     int item = -1;
-    int newItem;
-    for (newItem = eng->layoutData->items.size()-1; newItem > 0; --newItem) {
-        if (eng->layoutData->items[newItem].position <= line.from)
+    int newItem = -1;
+    int left = 0;
+    int right = eng->layoutData->items.size()-1;
+    while(left <= right) {
+        int middle = ((right-left)/2)+left;
+        if (line.from > eng->layoutData->items[middle].position)
+            left = middle+1;
+        else if(line.from < eng->layoutData->items[middle].position)
+            right = middle-1;
+        else {
+            newItem = middle;
             break;
+        }
     }
+    if (newItem == -1)
+        newItem = right;
 
     LB_DEBUG("from: %d: item=%d, total %d, width available %f", line.from, newItem, eng->layoutData->items.size(), line.width.toReal());
 
     Qt::Alignment alignment = eng->option.alignment();
 
     const HB_CharAttributes *attributes = eng->attributes();
+    if (!attributes)
+        return;
     lbh.currentPosition = line.from;
     int end = 0;
     lbh.logClusters = eng->layoutData->logClustersPtr;
@@ -1814,6 +1835,8 @@
             if (!current.num_glyphs) {
                 eng->shape(item);
                 attributes = eng->attributes();
+                if (!attributes)
+                    return;
                 lbh.logClusters = eng->layoutData->logClustersPtr;
             }
             lbh.currentPosition = qMax(line.from, current.position);
@@ -1892,6 +1915,9 @@
         } else {
             lbh.whiteSpaceOrObject = false;
             bool sb_or_ws = false;
+            glyph_t previousGlyph = 0;
+            if (lbh.currentPosition > 0 && lbh.logClusters[lbh.currentPosition - 1] <lbh.glyphs.numGlyphs)
+                previousGlyph = lbh.currentGlyph(); // needed to calculate right bearing later
             do {
                 addNextCluster(lbh.currentPosition, end, lbh.tmpData, lbh.glyphCount,
                                current, lbh.logClusters, lbh.glyphs);
@@ -1935,9 +1961,17 @@
             // We ignore the right bearing if the minimum negative bearing is too little to
             // expand the text beyond the edge.
             if (sb_or_ws|breakany) {
+                QFixed rightBearing = lbh.rightBearing; // store previous right bearing
+#if !defined(Q_WS_MAC)
                 if (lbh.calculateNewWidth(line) - lbh.minimumRightBearing > line.width)
+#endif
                     lbh.adjustRightBearing();
                 if (lbh.checkFullOtherwiseExtend(line)) {
+                    // we are too wide, fix right bearing
+                    if (rightBearing <= 0)
+                        lbh.rightBearing = rightBearing; // take from cache
+                    else if (previousGlyph > 0)
+                        lbh.adjustRightBearing(previousGlyph);
                     if (!breakany) {
                         line.textWidth += lbh.softHyphenWidth;
                     }
@@ -1951,7 +1985,7 @@
     }
     LB_DEBUG("reached end of line");
     lbh.checkFullOtherwiseExtend(line);
-found:       
+found:
     if (lbh.rightBearing > 0 && !lbh.whiteSpaceOrObject) // If right bearing has not yet been adjusted
         lbh.adjustRightBearing();
     line.textAdvance = line.textWidth;