src/hbcore/primitives/hbtextitem.cpp
changeset 34 ed14f46c0e55
parent 7 923ff622b8b9
--- a/src/hbcore/primitives/hbtextitem.cpp	Mon Oct 04 17:49:30 2010 +0300
+++ b/src/hbcore/primitives/hbtextitem.cpp	Mon Oct 18 18:23:13 2010 +0300
@@ -37,17 +37,30 @@
 #include <QApplication>
 
 #ifdef HB_TEXT_MEASUREMENT_UTILITY
-#include "hbtextmeasurementutility_p.h"
-#include "hbfeaturemanager_r.h"
+#include "hbtextmeasurementutility_r.h"
+#include "hbtextmeasurementutility_r_p.h"
 #endif
 
-// #define HB_TEXT_ITEM_LOGS
 #define EPSILON 0.01
 
-#ifdef HB_TEXT_ITEM_LOGS
-#   include <QDebug>
-const int KMaxLogedTextLength = 30;
-#endif // HB_TEXT_ITEM_LOGS
+// #define HB_DEBUG_TEXT_ITEM_LOGS
+#ifdef HB_DEBUG_TEXT_ITEM_LOGS
+    #include <QDebug>
+
+    #define HB_TEXT_ITEM_LOG(args) qDebug() << "HbTextItem::" << __FUNCTION__ \
+            << "(" << __LINE__ << ")"\
+            << ", objectName:" << this->objectName() \
+            << ", text: " << this->text().left(30) \
+            << " " << args;
+    #define HB_TEXT_ITEM_PRIV_LOG(args) qDebug() << "HbTextItemPrivate::" << __FUNCTION__ \
+            << "(" << __LINE__ << ")"\
+            << ", objectName:" << q_ptr->objectName() \
+            << ", text: " << this->mText.left(30) \
+            << " " << args;
+#else
+    #define HB_TEXT_ITEM_LOG(args)
+    #define HB_TEXT_ITEM_PRIV_LOG(args)
+#endif // HB_DEBUG_TEXT_ITEM_LOGS
 
 bool HbTextItemPrivate::outlinesEnabled = false;
 
@@ -69,15 +82,17 @@
     mMinWidthForAdjust(-1),
     mMaxWidthForAdjust(-1),
     mDefaultHeight(-1),
+    mHasMultiTrans(false),
     mUpdateColor(true),
-    mEventPosted(false)
+    mEventPosted(false),
+    inConstructor(1)
 {
 }
 
 void HbTextItemPrivate::init(QGraphicsItem *)
 {
     Q_Q(HbTextItem);
-
+    inConstructor = 1;
     q->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
     q->setFlag(QGraphicsItem::ItemClipsToShape, false);
     q->setFlag(QGraphicsItem::ItemIsSelectable, false);
@@ -88,6 +103,7 @@
     mTextLayout.setTextOption(textOption);
     mTextLayout.setCacheEnabled(true);
     mTextLayout.setFont(q->font());
+    inConstructor = 0;
 }
 
 void HbTextItemPrivate::clear()
@@ -136,7 +152,7 @@
         tempText.replace('\n', QChar::LineSeparator);
     }
 
-	// Need to call elidedText explicitly to enable multiple length translations.
+    // Need to call elidedText explicitly to enable multiple length translations.
     tempText = fontMetrics.elidedText(tempText, Qt::ElideNone, lineWidth);
     bool textTruncated = doLayout(tempText, lineWidth, fontMetrics.lineSpacing());
 
@@ -152,6 +168,7 @@
     }
 
     calculateVerticalOffset();
+    mBoundingRect = layoutBoundingRect();
     calculateFadeRects();
 
     // build of text layout is completed
@@ -217,8 +234,8 @@
         elidedText.append(QChar::LineSeparator);
     }
 
-    int n = lastLine.textLength();
-    if(textToElide.at(lastLine.textStart()+n-1)!=QChar::LineSeparator) {
+    int n = lastLine.textLength()-1;
+    if(textToElide.at(lastLine.textStart()+n)!=QChar::LineSeparator) {
         n = -1;
     }
     elidedText.append(metrics.elidedText(textToElide.mid(lastLine.textStart(), n),
@@ -303,16 +320,6 @@
                                             qreal minHeight,
                                             qreal maxHeight)
 {
-#ifdef HB_TEXT_ITEM_LOGS
-    qDebug() << "isAdjustHightNeeded for: " << q_ptr->objectName()
-            << " text=" << mText.left(KMaxLogedTextLength)
-            << " adjusted=" << mAdjustedSize
-            << " prefHeight=" << prefHeight
-            << " minHeight=" << minHeight
-            << " lastConstraint=" << mLastConstraint
-            << " " << mTextLayout.font();
-#endif // HB_TEXT_ITEM_LOGS
-
     // first check if wrapping of text is not active
     QTextOption::WrapMode wrapMode = mTextLayout.textOption().wrapMode();
     if (wrapMode==QTextOption::NoWrap
@@ -330,6 +337,12 @@
         return false;
     }
 
+    HB_TEXT_ITEM_PRIV_LOG("adjusted=" << mAdjustedSize
+            << " prefHeight=" << prefHeight
+            << " minHeight=" << minHeight
+            << " lastConstraint=" << mLastConstraint
+            << " " << mTextLayout.font())
+
     // check if adjusted size has been already calculated
     // new width is bigger then last estimated range of same height
     if ((mMaxWidthForAdjust>=newWidth
@@ -347,10 +360,16 @@
     // if preconditions are met test if current hight is enough
     const QFontMetricsF metrics(mTextLayout.font());
 
+    QString txt(mText);
+    if (mHasMultiTrans) {
+        // it has to be like that since same aproach is used  in rebuildTextLayout
+        // so to be consistent is sizeHInt and what is shown this has to be done
+        txt = metrics.elidedText(mText, Qt::ElideNone, newWidth);
+    }
     // heavy calculation: check if text fits in current size and cache result
     QSizeF newAdjust = metrics.boundingRect(QRectF(0, 0, newWidth, QWIDGETSIZE_MAX),
                                             textFlagsFromTextOption(),
-                                            mText).size();
+                                            txt).size();
 
     if (qFuzzyCompare(newAdjust.height(), mAdjustedSize.height())) {
         // height is same as last time update range of same height
@@ -385,6 +404,67 @@
     return true;
 }
 
+bool HbTextItemPrivate::isAdjustWidthNeeded(qreal newWidth)
+{
+    if (!mHasMultiTrans) {
+        return false;
+    }
+
+    if (mLastConstraint.width()>0) {
+        return false;
+    }    // check if adjusted size has been already calculated
+    // new width is bigger then last estimated range of same height
+
+    if (!mAdjustedSize.isValid()) {
+        // this means that preferred size is set outside of class by setPreferredSize
+        // so sizeHint(Qt::Preferredsize) was not called and size adjustment is useless
+        return false;
+    }
+
+    HB_TEXT_ITEM_PRIV_LOG("oldadjusted=" << mAdjustedSize
+            << " newWidth=" << newWidth
+            << " lastConstraint=" << mLastConstraint)
+
+    if ((mMaxWidthForAdjust>=newWidth
+         // new width is smaller then last estimated range of same height
+         && newWidth>=mMinWidthForAdjust)) {
+        return false;
+    }
+
+    QFontMetricsF metrics(q_ptr->font());
+
+    // heavy calculation: check if text fits in current size and cache result
+    // note that no wrapping is used here since full width of text is needed to return
+    // proper preferred sizeHint
+    QSizeF newAdjust = metrics.boundingRect(QRectF(0, 0, newWidth, QWIDGETSIZE_MAX),
+                                            0,
+                                            mText).size();
+
+    if (qFuzzyCompare(newAdjust.width(), mAdjustedSize.width())) {
+        return false;
+    }
+
+    if (mAdjustedSize.width()<newAdjust.width()
+        && newAdjust.width()>newWidth) {
+        qWarning("Provided multiple translation: \"%s\", "
+                 "has wrong order of possible translations. "
+                 "This could lead to infinitive loop of LayoutRequest events.",
+                 mText.toLatin1().data());
+
+        return false;
+    }
+    mAdjustedSize = newAdjust;
+
+    mMaxWidthForAdjust = mMinWidthForAdjust = newAdjust.width();
+    // it must be like this since it is unknown if there will be next resize or not
+    // size hint width is changed so change of width can occur and wrapping can be active
+
+    HB_TEXT_ITEM_PRIV_LOG("newAdjusted=" << mAdjustedSize
+            << " newWidth=" << newWidth)
+
+    return true;
+}
+
 void HbTextItemPrivate::clearAdjustedSizeCache()
 {
     // clear cache of size
@@ -402,11 +482,9 @@
         //      - HbTextItemPrivate::isAdjustHightNeeded
         //      - HbTextItem::resizeEvent
 
-#ifdef HB_TEXT_ITEM_LOGS
-        qDebug() << "HbTextItemPrivate::calculatePrefferedSize: returning cached value: " << mAdjustedSize
-                << " font:" << mTextLayout.font()
-                << " text:" << mText.left(KMaxLogedTextLength);
-#endif
+        HB_TEXT_ITEM_PRIV_LOG("mAdjustedSize: " << mAdjustedSize
+                << " font:" << mTextLayout.font())
+
         return mAdjustedSize;
     }
     mLastConstraint = constraint;
@@ -419,33 +497,37 @@
         maxSize.setHeight(constraint.height());
     }
 
+    QString txt(mText);
+    if (constraint.width()>0 && mHasMultiTrans) {
+        // it has to be like that since same aproach is used  in rebuildTextLayout
+        // so to be consistent is sizeHInt and what is shown this has to be done
+        txt = metrics.elidedText(mText, Qt::ElideNone, constraint.width());
+    }
     QSizeF size = metrics.boundingRect(QRectF(QPointF(),maxSize),
                                        textFlagsFromTextOption(),
-                                       mText).size();
+                                       txt).size();
 
     mAdjustedSize = size;
     mDefaultHeight = size.height();
     mMinWidthForAdjust = size.width();
     mMaxWidthForAdjust = maxSize.width();
 
-#ifdef HB_TEXT_ITEM_LOGS
-        qDebug() << "HbTextItemPrivate::calculatePrefferedSize:"
-                << " text: " << mText.left(KMaxLogedTextLength)
-                << " returnSize: " << mAdjustedSize
+    HB_TEXT_ITEM_PRIV_LOG("returnSize: " << mAdjustedSize
                 << " constraint: "  << constraint
-                << " font: " << mTextLayout.font();
-#endif
+                << " font: " << mTextLayout.font());
+
     return size;
 }
 
 bool HbTextItemPrivate::fadeNeeded(const QRectF& contentRect) const
 {
+    QRectF textRect = mBoundingRect.translated(mOffsetPos);
+    textRect.adjust(KFadeTolerance,
+                    KFadeTolerance,
+                    -KFadeTolerance,
+                    -KFadeTolerance);
     return (mFadeLengthX!=0 || mFadeLengthY!=0)
-            && !contentRect.contains(
-                    layoutBoundingRect().adjusted(KFadeTolerance,
-                                                  KFadeTolerance,
-                                                  -KFadeTolerance,
-                                                  -KFadeTolerance));
+            && !contentRect.contains(textRect);
 }
 
 void HbTextItemPrivate::setupGradient(QLinearGradient *gradient, QColor color)
@@ -513,8 +595,8 @@
     On platforms: Linux, Windows and S60 emulator there is no such problem.
     Below flag detects platform which have this problem to activate work-around.
  */
-#if defined(Q_WS_S60) && defined(Q_BIG_ENDIAN)
-#   warning Work-around is active in fade effect of HbTextItem (see comment)
+#if defined(Q_OS_SYMBIAN) && defined(Q_BIG_ENDIAN)
+//#   warning Work-around is active in fade effect of HbTextItem (see comment)
 #   define HB_FADE_EFFECT_WORKAROUND_ON_PHONE
 #endif
 inline void HbTextItemPrivate::setPainterPen(QPainter *painter,
@@ -834,20 +916,19 @@
 
 QRectF HbTextItemPrivate::layoutBoundingRect () const
 {
-    QRectF result;
+    QRectF result; // (mTextLayout.boundingRect());
     for (int i=0, n=mTextLayout.lineCount(); i<n; ++i) {
         result = result.unite(
                 mTextLayout.lineAt(i).naturalTextRect());
     }
 
-    result.translate(mOffsetPos);
-
     return result;
 }
 
 QRectF HbTextItemPrivate::boundingRect (const QRectF& contentsRect) const
 {
-    QRectF result(layoutBoundingRect());
+    QRectF result(mBoundingRect);
+    result.translate(mOffsetPos);
 
     if (mPaintFaded) {
         result = result.intersected(mFadeToRect);
@@ -899,7 +980,9 @@
 {
     Q_D(HbTextItem);
     d->init(parent);
+    d->inConstructor = 1;
     setText(text);
+    d->inConstructor = 0;
 }
 
 /*
@@ -984,7 +1067,7 @@
 
 #ifdef HB_TEXT_MEASUREMENT_UTILITY
 
-    if ( HbFeatureManager::instance()->featureStatus( HbFeatureManager::TextMeasurement ) ) {
+    if (HbTextMeasurementUtility::instance()->locTestMode()) {
         if (text.endsWith(QChar(LOC_TEST_END))) {
             int index = text.indexOf(QChar(LOC_TEST_START));
             setProperty( HbTextMeasurementUtilityNameSpace::textIdPropertyName,  text.mid(index + 1, text.indexOf(QChar(LOC_TEST_END)) - index - 1) );
@@ -1001,6 +1084,7 @@
         prepareGeometryChange();
         d->mText = txt;
         d->mTextLayout.setCacheEnabled(KLayoutCacheLimit >= d->mText.length());
+        d->mHasMultiTrans = (txt.indexOf('\x9c')>=0);
         d->clearAdjustedSizeCache();
         update();
 
@@ -1016,10 +1100,9 @@
             if (sizePolicy().horizontalPolicy()&QSizePolicy::IgnoreFlag
                 // or was preferred width set from outside of this class?
                 || d->mLastConstraint.width()>0) {
-#ifdef HB_TEXT_ITEM_LOGS
-                qDebug() << "HbTextItem::setText: skiping updateGeometry for: "
-                        << objectName() << " text:" << d->mText.left(KMaxLogedTextLength);
-#endif
+
+                HB_TEXT_ITEM_LOG("skiping updateGeometry!")
+
                 // in those cases skip updateGeometry
                 return;
             }
@@ -1067,13 +1150,14 @@
 void HbTextItem::setAlignment (Qt::Alignment alignment)
 {
     Q_D(HbTextItem);
-	d->setApiProtectionFlag(HbWidgetBasePrivate::AC_TextAlign, true);
-    alignment &= Qt::AlignVertical_Mask | Qt::AlignHorizontal_Mask;
+    d->setApiProtectionFlag(HbWidgetBasePrivate::AC_TextAlign, true);
+    alignment = d->combineAlignment(alignment, d->mAlignment);
     if (d->mAlignment != alignment) {
         prepareGeometryChange();
         d->mAlignment = alignment;
         d->updateTextOption();
         d->calculateVerticalOffset();
+        d->mBoundingRect = d->layoutBoundingRect();
 
         update();
     }
@@ -1089,6 +1173,7 @@
 void HbTextItem::setElideMode (Qt::TextElideMode elideMode)
 {
     Q_D(HbTextItem);
+    d->setApiProtectionFlag(HbWidgetBasePrivate::AC_TextElideMode, true);
     if (elideMode != d->mElideMode) {
         d->mElideMode = elideMode;
         d->scheduleTextBuild();
@@ -1163,14 +1248,19 @@
         // called method HbWidgetBase::setGeometry(rect) so size is used which
         // holds current size
         Q_D(HbTextItem);
+        if (d->isAdjustWidthNeeded(size().width())) {
+            updateGeometry();
+            HB_TEXT_ITEM_LOG("isAdjustWidthNeeded - updateGeometry was called");
+            return;
+        }
+
         if (d->isAdjustHightNeeded(size().width(),
                                    preferredHeight(),
                                    minimumHeight(),
                                    maximumHeight())) {
-            updateGeometry();            
-#ifdef HB_TEXT_ITEM_LOGS
-            qDebug() << "isAdjustHightNeeded returned true - updateGeometry was called";
-#endif // HB_TEXT_ITEM_LOGS
+            updateGeometry();
+            HB_TEXT_ITEM_LOG("isAdjustHightNeeded - updateGeometry was called");
+            return;
         }
     }
 }
@@ -1202,13 +1292,9 @@
     // TODO: Temporary work-around - font change event are not always received
     // so updating font here (this is needed because of sizeHint adjustments).
     if (d->mTextLayout.font()!=font()) {
-#ifdef HB_TEXT_ITEM_LOGS
-        qWarning() << "Font change was not recieved on time: work-around is active"
-                << objectName()
-                << " test: " << d->mText.left(KMaxLogedTextLength)
+        HB_TEXT_ITEM_LOG("Font change was not recieved on time: work-around is active"
                 << " oldFont:" << d->mTextLayout.font()
-                << " newFont:" << font();
-#endif // HB_TEXT_ITEM_LOGS
+                << " newFont:" << font())
 
         const_cast<HbTextItemPrivate *>(d)->mTextLayout.setFont(font());
         const_cast<HbTextItemPrivate *>(d)->clearAdjustedSizeCache();
@@ -1266,12 +1352,11 @@
  */
 void HbTextItem::changeEvent(QEvent *event)
 {
-    // Listens theme changed event so that item size hint is
-
     switch(event->type()) {
     case QEvent::LayoutDirectionChange: {
             Q_D(HbTextItem);
             d->scheduleTextBuild();
+            prepareGeometryChange();
         }
         break;
 
@@ -1279,12 +1364,7 @@
             Q_D(HbTextItem);
 
             if (!d->mTextLayout.font().isCopyOf(font())) {
-
-#ifdef HB_TEXT_ITEM_LOGS
-                qDebug() << "fontChangeEvent: " << objectName()
-                        << " text: " << text().left(KMaxLogedTextLength)
-                        << " font: " << font();
-#endif // HB_TEXT_ITEM_LOGS
+                HB_TEXT_ITEM_LOG(" font: " << font());
 
                 d->mTextLayout.setFont(font());
                 d->clearAdjustedSizeCache();
@@ -1335,7 +1415,7 @@
 void HbTextItem::setTextWrapping(Hb::TextWrapping mode)
 {
     Q_D(HbTextItem);
-	d->setApiProtectionFlag(HbWidgetBasePrivate::AC_TextWrapMode, true);
+    d->setApiProtectionFlag(HbWidgetBasePrivate::AC_TextWrapMode, true);
     QTextOption::WrapMode textWrapMode = static_cast<QTextOption::WrapMode>(mode);
 
     QTextOption textOption = d->mTextLayout.textOption();
@@ -1442,7 +1522,7 @@
     Q_D( HbTextItem );
     minLines = qMax(minLines, 1); // zero or nagative values are meanless and are restoring 1
 
-	d->setApiProtectionFlag(HbWidgetBasePrivate::AC_TextLinesMin, true);
+    d->setApiProtectionFlag(HbWidgetBasePrivate::AC_TextLinesMin, true);
 
     if( minLines != d->mMinLines ) {
         if( ( d->mMaxLines > 0 ) && ( minLines > d->mMaxLines ) ) {
@@ -1471,7 +1551,7 @@
 void HbTextItem::setMaximumLines( int maxLines )
 {
     Q_D( HbTextItem );
-	d->setApiProtectionFlag(HbWidgetBasePrivate::AC_TextLinesMax, true);
+    d->setApiProtectionFlag(HbWidgetBasePrivate::AC_TextLinesMax, true);
 
     maxLines = qMax(maxLines, 0);
 
@@ -1487,7 +1567,7 @@
 
         updateGeometry();
 #ifdef HB_TEXT_MEASUREMENT_UTILITY
-        if ( HbFeatureManager::instance()->featureStatus( HbFeatureManager::TextMeasurement ) ) {
+        if (HbTextMeasurementUtility::instance()->locTestMode()) {
             setProperty( HbTextMeasurementUtilityNameSpace::textMaxLines, d->mMaxLines );
         }
 #endif
@@ -1584,4 +1664,16 @@
     d->setFadeLengths(lengths.x(), lengths.y());
 }
 
+/*!
+    \reimp
+ */
+void HbTextItem::updateGeometry()
+{
+    Q_D( HbTextItem );
+    if (d->inConstructor) {
+        return;
+    }
+    HbWidgetBase::updateGeometry();
+}
+
 // end of file