src/hbcore/primitives/hbtextitem.cpp
changeset 6 c3690ec91ef8
parent 3 11d3954df52a
child 7 923ff622b8b9
equal deleted inserted replaced
5:627c4a0fd0e7 6:c3690ec91ef8
    36 #include <QTextOption>
    36 #include <QTextOption>
    37 #include <QApplication>
    37 #include <QApplication>
    38 
    38 
    39 #ifdef HB_TEXT_MEASUREMENT_UTILITY
    39 #ifdef HB_TEXT_MEASUREMENT_UTILITY
    40 #include "hbtextmeasurementutility_p.h"
    40 #include "hbtextmeasurementutility_p.h"
    41 #include "hbfeaturemanager_p.h"
    41 #include "hbfeaturemanager_r.h"
    42 #endif
    42 #endif
    43 
    43 
       
    44 // #define HB_TEXT_ITEM_LOGS
    44 #define EPSILON 0.01
    45 #define EPSILON 0.01
    45 
    46 
       
    47 #ifdef HB_TEXT_ITEM_LOGS
       
    48 #   include <QDebug>
       
    49 #endif // HB_TEXT_ITEM_LOGS
       
    50 
    46 bool HbTextItemPrivate::outlinesEnabled = false;
    51 bool HbTextItemPrivate::outlinesEnabled = false;
    47 
    52 
    48 static const QString KDefaultColorThemeName = "qtc_view_normal";
    53 static const QString KDefaultColorThemeName = "qtc_view_normal";
    49 const int MinimumWidth = 5; // minimum width if there is some text.
    54 const qreal MinimumWidth = 5.0; // minimum width if there is some text.
    50 const int KLayoutCacheLimit = 64;
    55 const int KLayoutCacheLimit = 64;
    51 const qreal KFadeTolerance = 1.0;
    56 const qreal KFadeTolerance = 1.0;
    52 
    57 
    53 HbTextItemPrivate::HbTextItemPrivate () :
    58 HbTextItemPrivate::HbTextItemPrivate () :
    54     mAlignment(Qt::AlignLeft | Qt::AlignVCenter),
    59     mAlignment(Qt::AlignLeft | Qt::AlignVCenter),
    56     mInvalidateShownText(true),
    61     mInvalidateShownText(true),
    57     mOffsetPos(0,0),
    62     mOffsetPos(0,0),
    58     mPaintFaded(false),
    63     mPaintFaded(false),
    59     mFadeLengthX(30),
    64     mFadeLengthX(30),
    60     mFadeLengthY(15),
    65     mFadeLengthY(15),
    61     mPrefHeight(0),
    66     mMinLines(1),
    62     mMinLines(0),
       
    63     mMaxLines(0),
    67     mMaxLines(0),
    64     mNeedToAdjustSizeHint(false),
    68     mMinWidthForAdjust(-1),
    65     mUpdateColor(true)
    69     mMaxWidthForAdjust(-1),
       
    70     mDefaultHeight(-1),
       
    71     mUpdateColor(true),
       
    72     mEventPosted(false)
    66 {
    73 {
    67 }
    74 }
    68 
    75 
    69 void HbTextItemPrivate::init(QGraphicsItem *)
    76 void HbTextItemPrivate::init(QGraphicsItem *)
    70 {
    77 {
    77 
    84 
    78     QTextOption textOption = mTextLayout.textOption();
    85     QTextOption textOption = mTextLayout.textOption();
    79     textOption.setWrapMode(QTextOption::WordWrap);
    86     textOption.setWrapMode(QTextOption::WordWrap);
    80     mTextLayout.setTextOption(textOption);
    87     mTextLayout.setTextOption(textOption);
    81     mTextLayout.setCacheEnabled(true);
    88     mTextLayout.setCacheEnabled(true);
       
    89     mTextLayout.setFont(q->font());
    82 }
    90 }
    83 
    91 
    84 void HbTextItemPrivate::clear()
    92 void HbTextItemPrivate::clear()
    85 {
    93 {
    86     // no implementation needed
    94     // no implementation needed
    87 }
    95 }
    88 
    96 
    89 bool HbTextItemPrivate::doLayout(const QString& text, const qreal lineWidth, qreal leading)
    97 bool HbTextItemPrivate::doLayout(const QString& text, const qreal lineWidth, qreal lineSpacing)
    90 {
    98 {
    91     bool textTruncated = false;
    99     bool textTruncated = false;
    92     mInvalidateShownText = false;
       
    93 
   100 
    94     mTextLayout.setText(text);
   101     mTextLayout.setText(text);
    95     mTextLayout.setFont( q_func()->font() );
   102 
    96 
   103     qreal yLinePos = 0;
    97     qreal height = 0;
       
    98     mTextLayout.beginLayout();
   104     mTextLayout.beginLayout();
    99     while (1) {
   105     while (1) {
   100         QTextLine line = mTextLayout.createLine();
   106         QTextLine line = mTextLayout.createLine();
   101         if (!line.isValid())
   107         if (!line.isValid())
   102             break;
   108             break;
   103         if( ( mMaxLines > 0 ) && ( mTextLayout.lineCount() > mMaxLines ) ) {
   109 
   104             textTruncated = true;
   110         line.setLineWidth(lineWidth);
       
   111         line.setPosition(QPointF(0, yLinePos));
       
   112 
       
   113         if( ( mMaxLines > 0 ) && ( mTextLayout.lineCount() >= mMaxLines ) ) {
       
   114             textTruncated = (line.textStart()+line.textLength() < text.length());
   105             break;
   115             break;
   106         }
   116         }
   107 
   117         yLinePos += lineSpacing;
   108         line.setLineWidth(lineWidth);
       
   109         height += leading;
       
   110         line.setPosition(QPointF(0, height));
       
   111         height += line.height();
       
   112     }
   118     }
   113     mTextLayout.endLayout();
   119     mTextLayout.endLayout();
   114 
   120 
   115     if( textTruncated ) {
       
   116         mTextLayout.setText(text);
       
   117         mTextLayout.setFont( q_func()->font() );
       
   118 
       
   119         qreal height = 0;
       
   120         mTextLayout.beginLayout();
       
   121         while ( mTextLayout.lineCount() < mMaxLines ) {
       
   122             QTextLine line = mTextLayout.createLine();
       
   123             line.setLineWidth(lineWidth);
       
   124             height += leading;
       
   125             line.setPosition(QPointF(0, height));
       
   126             height += line.height();
       
   127         }
       
   128         mTextLayout.endLayout();
       
   129     }
       
   130 
       
   131     return textTruncated;
   121     return textTruncated;
   132 }
   122 }
   133 
   123 
   134 void HbTextItemPrivate::setSize(const QSizeF &newSize)
   124 void HbTextItemPrivate::rebuildTextLayout(const QSizeF &newSize)
   135 {
   125 {
   136     Q_Q(HbTextItem);
   126     QFontMetricsF fontMetrics(mTextLayout.font());
   137 
       
   138     QFont usedFont = q->font();
       
   139     QFontMetricsF fontMetrics(usedFont);
       
   140 
   127 
   141     const qreal lineWidth = qRound( newSize.width() + 0.5 ); // round up to integer
   128     const qreal lineWidth = qRound( newSize.width() + 0.5 ); // round up to integer
   142 
   129 
   143     updateTextOption();
   130     updateTextOption();
   144 
   131 
   146     if(tempText.indexOf('\n')>=0) {
   133     if(tempText.indexOf('\n')>=0) {
   147         // to prevent creation of deep copy if replace has no effect
   134         // to prevent creation of deep copy if replace has no effect
   148         tempText.replace('\n', QChar::LineSeparator);
   135         tempText.replace('\n', QChar::LineSeparator);
   149     }
   136     }
   150 
   137 
   151     // function does the layout only when needed
       
   152     mTextLayout.setFont(usedFont);
       
   153 	// Need to call elidedText explicitly to enable multiple length translations.
   138 	// Need to call elidedText explicitly to enable multiple length translations.
   154     tempText = fontMetrics.elidedText(tempText, Qt::ElideNone, lineWidth);
   139     tempText = fontMetrics.elidedText(tempText, Qt::ElideNone, lineWidth);
   155     bool textTruncated = doLayout(tempText, lineWidth, fontMetrics.leading());
   140     bool textTruncated = doLayout(tempText, lineWidth, fontMetrics.lineSpacing());
       
   141 
   156     if(mElideMode!=Qt::ElideNone && !tempText.isEmpty()) {
   142     if(mElideMode!=Qt::ElideNone && !tempText.isEmpty()) {
   157         if( ( mTextLayout.boundingRect().height() - newSize.height() > EPSILON ) ||
   143         if( ( mTextLayout.boundingRect().height() - newSize.height() > EPSILON ) ||
   158             ( mTextLayout.boundingRect().width() - lineWidth > EPSILON ) ||
   144             ( mTextLayout.boundingRect().width() - lineWidth > EPSILON ) ||
   159               textTruncated) {
   145               textTruncated) {
   160             // TODO: Multiple length translations with multiline text
   146             // TODO: Multiple length translations with multiline text
   161             doLayout(elideLayoutedText(newSize, fontMetrics),
   147             doLayout(elideLayoutedText(newSize, fontMetrics),
   162                      lineWidth,
   148                      lineWidth,
   163                      fontMetrics.leading());
   149                      fontMetrics.lineSpacing());
   164         }
   150         }
   165     }
   151     }
       
   152 
   166     calculateVerticalOffset();
   153     calculateVerticalOffset();
   167     calculateFadeRects();
   154     calculateFadeRects();
   168     q->update();
   155 
       
   156     // build of text layout is completed
       
   157     mInvalidateShownText = false;
   169 }
   158 }
   170 
   159 
   171 /*
   160 /*
   172     finds index of last line before given Y coordinate
   161     finds index of last line before given Y coordinate
   173     It is a binary search.
   162     It is a binary search.
   190             }
   179             }
   191             i=k;
   180             i=k;
   192         }
   181         }
   193     }
   182     }
   194     return i;
   183     return i;
       
   184 }
       
   185 
       
   186 QSizeF HbTextItemPrivate::respectSizeLimits(QSizeF size) const
       
   187 {
       
   188     QFontMetricsF metrics(mTextLayout.font());
       
   189 
       
   190     Q_ASSERT(mMinLines>0);
       
   191     qreal minHeight = metrics.lineSpacing()*mMinLines - metrics.leading();
       
   192     if (size.height()<minHeight) {
       
   193         size.setHeight(minHeight);
       
   194     }
       
   195 
       
   196     if (mMaxLines>0) {
       
   197         qreal maxHeight = metrics.lineSpacing()*mMaxLines - metrics.leading();
       
   198         if (size.height()>maxHeight) {
       
   199             size.setHeight(maxHeight);
       
   200         }
       
   201     }
       
   202     return size;
   195 }
   203 }
   196 
   204 
   197 QString HbTextItemPrivate::elideLayoutedText(const QSizeF& size, const QFontMetricsF& metrics) const
   205 QString HbTextItemPrivate::elideLayoutedText(const QSizeF& size, const QFontMetricsF& metrics) const
   198 {
   206 {
   199     int lastVisibleLine =findIndexOfLastLineBeforeY(size.height());
   207     int lastVisibleLine =findIndexOfLastLineBeforeY(size.height());
   278     }
   286     }
   279 
   287 
   280     return flags;
   288     return flags;
   281 }
   289 }
   282 
   290 
   283 bool HbTextItemPrivate::adjustSizeHint()
   291 /*!
   284 {
   292     This method check spetial case of calculating sizeHint.
   285     Q_Q( HbTextItem );
   293     By default prefferedSize returns size of text which has lots of free space.
   286 
   294     But there can be situaltion that text has less avaible free width ther requred.
   287     mNeedToAdjustSizeHint = false;
   295     In such cases prefered hight of HbTextItem should be recaculated because wrapping
   288 
   296     of the line may requre more space in vertical direction.
   289     if ( !(q->sizePolicy().verticalPolicy()&QSizePolicy::IgnoreFlag) ) {
   297 
   290         // only calculated if the vertical sizeHint is taken into account
   298     In such case bigger hight should be cached for sizeHint calculations.
   291 
   299  */
   292         const QFontMetricsF metrics(q->font());
   300 bool HbTextItemPrivate::isAdjustHightNeeded(const QSizeF& newSize,
   293 
   301                                             const QSizeF& prefSize)
   294         if ( mMinLines > 0 && (mMinLines == mMaxLines) ) {
   302 {
   295             // if the number of lines if fixed: optimize
   303 #ifdef HB_TEXT_ITEM_LOGS
   296             const qreal newPrefHeight = ( metrics.height() + metrics.leading() ) * mMinLines - metrics.leading();
   304     qDebug() << "isAdjustHightNeeded for: " << q_ptr->objectName()
   297             if( qAbs( mPrefHeight - newPrefHeight ) > EPSILON ) {
   305             << " text=" << mText.left(20)
   298                 mPrefHeight = newPrefHeight;
   306             << " adjusted=" << mAdjustedSize
   299                 return true;
   307             << " pref=" << prefSize
   300             }
   308             << mTextLayout.font();
   301             return false;
   309 #endif // HB_TEXT_ITEM_LOGS
   302         }
   310 
   303 
   311     // first check if wrapping of text is not active
   304         QSizeF currSize = q->size();
   312     QTextOption::WrapMode wrapMode = mTextLayout.textOption().wrapMode();
   305         // do the heavy calculation
   313     if (wrapMode==QTextOption::NoWrap
   306         QRectF desiredRect = metrics.boundingRect( QRectF( 0, 0 , currSize.width(), QWIDGETSIZE_MAX ), textFlagsFromTextOption(), mText );
   314         || wrapMode==QTextOption::ManualWrap) {
   307 
   315         return false;
   308         if( qAbs( desiredRect.height() - mPrefHeight ) > EPSILON ) {
   316     }
   309             mPrefHeight = desiredRect.height();
   317 
   310             return true;
   318     // check if line count is fixed
   311         }
   319     if (mMaxLines == mMinLines) {
   312     }
   320         return false;
   313 
   321     }
   314     return false;
   322 
       
   323     // check if preffered height is defined by user
       
   324     //    so check if preff height is same as value returned by sizeHint
       
   325     if (!qFuzzyCompare(prefSize.height(), mAdjustedSize.height())) {
       
   326         return false;
       
   327     }
       
   328 
       
   329     // check if adjusted size has been already calculated
       
   330     // new width is bigger then last estimated range of same height
       
   331     if ((mMaxWidthForAdjust>=newSize.width()
       
   332         // new width is smaller then last estimated range of same height
       
   333         && newSize.width()>=mMinWidthForAdjust)) {
       
   334         return false;
       
   335     }
       
   336 
       
   337     if (!mAdjustedSize.isValid()) {
       
   338         // this means that preferred size is set outside of class by setPreferredSize
       
   339         // so sizeHint(Qt::Preferredsize) was not called and size adjustment is useless
       
   340         return false;
       
   341     }
       
   342 
       
   343     // if preconditions are met test if current hight is enough
       
   344     const QFontMetricsF metrics(mTextLayout.font());
       
   345 
       
   346     // heavy calculation: check if text fits in current size and cache result
       
   347     QSizeF newAdjust = metrics.boundingRect(QRectF(0, 0, newSize.width(), QWIDGETSIZE_MAX),
       
   348                                        textFlagsFromTextOption(),
       
   349                                        mText).size();
       
   350 
       
   351     if (qFuzzyCompare(newAdjust.height(), mAdjustedSize.height())) {
       
   352         // height is same as last time update range of same height
       
   353         mMaxWidthForAdjust = qMax(mMaxWidthForAdjust, newSize.width());
       
   354         mMinWidthForAdjust = qMin(mMinWidthForAdjust, newSize.width());
       
   355 
       
   356         // and don't update geometry
       
   357         return false;
       
   358     }
       
   359 
       
   360     // new height was calculated create new range for which
       
   361     // current mAdjustedSize.height is valid
       
   362     mMaxWidthForAdjust = newSize.width();
       
   363     mMinWidthForAdjust = newAdjust.width();
       
   364 
       
   365     // store new hieght use don't change width
       
   366     mAdjustedSize.setHeight(newAdjust.height());
       
   367     Q_ASSERT_X(mAdjustedSize.width()>=newAdjust.width(),
       
   368                "HbTextItemPrivate::isAdjustHightNeeded",
       
   369                QString("Fail for string: \"%1\"").arg(mText).toAscii().data());
       
   370 
       
   371     if (respectSizeLimits(mAdjustedSize)==prefSize) {
       
   372         // updateGeometry has no effect
       
   373         return false;
       
   374     }
       
   375 
       
   376     // all conditions for calling updateGeometry are meet
       
   377     return true;
       
   378 }
       
   379 
       
   380 void HbTextItemPrivate::clearAdjustedSizeCache()
       
   381 {
       
   382     // clear cache of size
       
   383     mMinWidthForAdjust = -1;
       
   384     mMaxWidthForAdjust = -1;
       
   385     mAdjustedSize.setHeight(-1);
       
   386 }
       
   387 
       
   388 QSizeF HbTextItemPrivate::calculatePrefferedSize(const QSizeF& constraint) const
       
   389 {
       
   390     const QFontMetricsF metrics(mTextLayout.font());
       
   391 
       
   392     if (mAdjustedSize.isValid() &&
       
   393         (constraint.height()<0 || qFuzzyCompare(constraint.height(), mAdjustedSize.height())) &&
       
   394         (constraint.width()<0  || qFuzzyCompare(constraint.width(), mAdjustedSize.width()))) {
       
   395         // return cached value, see more in:
       
   396         //      - HbTextItemPrivate::isAdjustHightNeeded
       
   397         //      - HbTextItem::resizeEvent
       
   398         return mAdjustedSize;
       
   399     }
       
   400 
       
   401     QSizeF maxSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
       
   402     if(constraint.width()>0) {
       
   403         maxSize.setWidth(constraint.width());
       
   404     }
       
   405     if(constraint.height()>0) {
       
   406         maxSize.setHeight(constraint.height());
       
   407     }
       
   408 
       
   409     QSizeF size = metrics.boundingRect(QRectF(QPointF(),maxSize),
       
   410                                        textFlagsFromTextOption(),
       
   411                                        mText).size();
       
   412 
       
   413     mAdjustedSize = size;
       
   414     mDefaultHeight = size.height();
       
   415     mMinWidthForAdjust = size.width();
       
   416     mMaxWidthForAdjust = QWIDGETSIZE_MAX;
       
   417 
       
   418     return size;
   315 }
   419 }
   316 
   420 
   317 bool HbTextItemPrivate::fadeNeeded(const QRectF& contentRect) const
   421 bool HbTextItemPrivate::fadeNeeded(const QRectF& contentRect) const
   318 {
   422 {
   319     return (mFadeLengthX!=0 || mFadeLengthY!=0)
   423     return (mFadeLengthX!=0 || mFadeLengthY!=0)
   530 
   634 
   531     const int n = mTextLayout.lineCount();
   635     const int n = mTextLayout.lineCount();
   532 
   636 
   533 // #define SEE_FADE_RECTANGLES
   637 // #define SEE_FADE_RECTANGLES
   534 #ifdef SEE_FADE_RECTANGLES
   638 #ifdef SEE_FADE_RECTANGLES
   535     painter->setClipRect(mFadeToRect);
       
   536     painter->setBrush(QBrush(QColor(215, 0, 0, 30)));
   639     painter->setBrush(QBrush(QColor(215, 0, 0, 30)));
   537     painter->drawRect(mFadeToRect);
   640     painter->drawRect(mFadeToRect);
   538     painter->setBrush(QBrush(QColor(0, 0, 200, 30)));
   641     painter->setBrush(QBrush(QColor(0, 0, 200, 30)));
   539     painter->drawRect(mFadeFromRect.adjusted(0,0,-1,-1));
   642     painter->drawRect(mFadeFromRect.adjusted(0,0,-1,-1));
   540 #endif // SEE_FADE_RECTANGLES
   643 #endif // SEE_FADE_RECTANGLES
   741     }
   844     }
   742 
   845 
   743     return result;
   846     return result;
   744 }
   847 }
   745 
   848 
       
   849 void HbTextItemPrivate::scheduleTextBuild()
       
   850 {
       
   851     mInvalidateShownText = true;
       
   852 }
       
   853 
   746 /*!
   854 /*!
   747     @alpha
   855     @alpha
   748     @hbcore
   856     @hbcore
   749     \class HbTextItem
   857     \class HbTextItem
   750     \brief HbTextItem is a lightweight item for showing text.
   858     \brief HbTextItem is a lightweight item for showing text.
   755  */
   863  */
   756 
   864 
   757 /*!
   865 /*!
   758     Constructor for the class with no content.
   866     Constructor for the class with no content.
   759  */
   867  */
   760 
       
   761 HbTextItem::HbTextItem (QGraphicsItem *parent) :
   868 HbTextItem::HbTextItem (QGraphicsItem *parent) :
   762     HbWidgetBase(*new HbTextItemPrivate, parent)
   869     HbWidgetBase(*new HbTextItemPrivate, parent)
   763 {
   870 {
   764     Q_D(HbTextItem);
   871     Q_D(HbTextItem);
   765     d->init(parent);
   872     d->init(parent);
   870         }
   977         }
   871     }
   978     }
   872 #endif //HB_TEXT_MEASUREMENT_UTILITY
   979 #endif //HB_TEXT_MEASUREMENT_UTILITY
   873 
   980 
   874     if (d->mText != txt) {
   981     if (d->mText != txt) {
   875         d->mInvalidateShownText = true;
   982         d->scheduleTextBuild();
   876         prepareGeometryChange();
   983         prepareGeometryChange();
   877         d->mText = txt;
   984         d->mText = txt;
   878         d->mTextLayout.setCacheEnabled(KLayoutCacheLimit >= d->mText.length());
   985         d->mTextLayout.setCacheEnabled(KLayoutCacheLimit >= d->mText.length());
   879         bool onlyHorizontalSizeHintChanged = false;
   986         d->clearAdjustedSizeCache();
   880         if ( d->mMinLines > 0 && (d->mMinLines == d->mMaxLines) ) {
       
   881             onlyHorizontalSizeHintChanged = true;
       
   882         }
       
   883         if ( (sizePolicy().horizontalPolicy()&QSizePolicy::IgnoreFlag) && onlyHorizontalSizeHintChanged ) {
       
   884             // suppress updateGeometry() and use the same geometry
       
   885             d->setSize( size() );
       
   886         } else {
       
   887             updateGeometry();
       
   888         }
       
   889         update();
   987         update();
       
   988 
       
   989         // check if call of updateGeometry can be ignored
       
   990         // don't call it when minimum and maximum lines are equal (height is fixed) or ...
       
   991         if ((d->mMinLines == d->mMaxLines)
       
   992             // or when preferred height is ignored
       
   993             || (sizePolicy().verticalPolicy()&QSizePolicy::IgnoreFlag)) {
       
   994 
       
   995             // and when preferred width is ignored or ...
       
   996             if (sizePolicy().horizontalPolicy()&QSizePolicy::IgnoreFlag
       
   997                 // or when preferred width is defined from outside
       
   998                 || !qFuzzyCompare(preferredWidth(), d->mAdjustedSize.width())) {
       
   999                   // TODO: looking for better solution since preferredWidth() can cause call of sizeHint
       
  1000 
       
  1001                 // in those cases skip updateGeometry
       
  1002                 return;
       
  1003             }
       
  1004         }
       
  1005         updateGeometry();
   890     }
  1006     }
   891 }
  1007 }
   892 
  1008 
   893 /*!
  1009 /*!
   894     Sets the text color into \a color.
  1010     Sets the text color into \a color.
   950  */
  1066  */
   951 void HbTextItem::setElideMode (Qt::TextElideMode elideMode)
  1067 void HbTextItem::setElideMode (Qt::TextElideMode elideMode)
   952 {
  1068 {
   953     Q_D(HbTextItem);
  1069     Q_D(HbTextItem);
   954     if (elideMode != d->mElideMode) {
  1070     if (elideMode != d->mElideMode) {
   955         d->mInvalidateShownText = true;
       
   956         d->mElideMode = elideMode;
  1071         d->mElideMode = elideMode;
       
  1072         d->scheduleTextBuild();
       
  1073         prepareGeometryChange();
   957         update();
  1074         update();
   958     }
  1075     }
   959 }
  1076 }
   960 
  1077 
   961 /*!
  1078 /*!
   982         painter->setBrush(QBrush(QColor(255, 0, 0, 50)));
  1099         painter->setBrush(QBrush(QColor(255, 0, 0, 50)));
   983         QRectF rect(contentsRect());
  1100         QRectF rect(contentsRect());
   984         // to see border - bounding rect was clipping bottom and right border
  1101         // to see border - bounding rect was clipping bottom and right border
   985         rect.adjust(0, 0, -1.0, -1.0);
  1102         rect.adjust(0, 0, -1.0, -1.0);
   986         painter->drawRect(rect);
  1103         painter->drawRect(rect);
       
  1104     }
       
  1105 
       
  1106     if (d->mInvalidateShownText) {
       
  1107         d->rebuildTextLayout(size());
   987     }
  1108     }
   988 
  1109 
   989 
  1110 
   990     painter->setPen(textColor());
  1111     painter->setPen(textColor());
   991 
  1112 
  1009 
  1130 
  1010     Sets geometry of text
  1131     Sets geometry of text
  1011  */
  1132  */
  1012 void HbTextItem::setGeometry(const QRectF & rect)
  1133 void HbTextItem::setGeometry(const QRectF & rect)
  1013 {
  1134 {
  1014     Q_D(HbTextItem);
       
  1015 
       
  1016     HbWidgetBase::setGeometry(rect);
  1135     HbWidgetBase::setGeometry(rect);
  1017 
  1136 
  1018     // needed when there was no size change and some things
  1137     if (parentLayoutItem() && parentLayoutItem()->isLayout()) {
  1019     // need to relayout text
  1138         // rect.size can't be used here since size can be limited inside of
  1020     if(d->mInvalidateShownText) {
  1139         // called method HbWidgetBase::setGeometry(rect) so size is used which
  1021         prepareGeometryChange();
  1140         // holds current size
  1022         d->setSize(rect.size());
  1141         Q_D(HbTextItem);
       
  1142         if (d->isAdjustHightNeeded(size(), preferredSize())) {
       
  1143             updateGeometry();
       
  1144         }
  1023     }
  1145     }
  1024 }
  1146 }
  1025 
  1147 
  1026 /*!
  1148 /*!
  1027     \reimp
  1149     \reimp
  1030  */
  1152  */
  1031 QRectF HbTextItem::boundingRect () const
  1153 QRectF HbTextItem::boundingRect () const
  1032 {
  1154 {
  1033     Q_D(const HbTextItem);
  1155     Q_D(const HbTextItem);
  1034 
  1156 
       
  1157     if (d->mInvalidateShownText) {
       
  1158         const_cast<HbTextItemPrivate*>(d)->rebuildTextLayout(size());
       
  1159     }
  1035     return d->boundingRect(contentsRect());
  1160     return d->boundingRect(contentsRect());
  1036 } // boundingRect()
  1161 } // boundingRect()
  1037 
  1162 
  1038 /*!
  1163 /*!
  1039     \reimp
  1164     \reimp
  1041 QSizeF HbTextItem::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
  1166 QSizeF HbTextItem::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
  1042 {
  1167 {
  1043     Q_D(const HbTextItem);
  1168     Q_D(const HbTextItem);
  1044 
  1169 
  1045     QSizeF size(0,0);
  1170     QSizeF size(0,0);
       
  1171 
       
  1172     // TODO: Temporary work-around - font change event are not always received
       
  1173     // so updating font here (this is needed because of sizeHint adjustments).
       
  1174     if (d->mTextLayout.font()!=font()) {
       
  1175 #ifdef HB_TEXT_ITEM_LOGS
       
  1176         qWarning() << "Font change was not recieved on time: work-around is active"
       
  1177                 << objectName()
       
  1178                 << " test: " << d->mText.left(20)
       
  1179                 << " oldFont:" << d->mTextLayout.font()
       
  1180                 << " newFont:" << font();
       
  1181 #endif // HB_TEXT_ITEM_LOGS
       
  1182 
       
  1183         const_cast<HbTextItemPrivate *>(d)->mTextLayout.setFont(font());
       
  1184         const_cast<HbTextItemPrivate *>(d)->clearAdjustedSizeCache();
       
  1185     }
  1046 
  1186 
  1047     Qt::Orientations effectiveOrientations(0);
  1187     Qt::Orientations effectiveOrientations(0);
  1048     if ( !(sizePolicy().horizontalPolicy()&QSizePolicy::IgnoreFlag) ) {
  1188     if ( !(sizePolicy().horizontalPolicy()&QSizePolicy::IgnoreFlag) ) {
  1049         effectiveOrientations |= Qt::Horizontal;
  1189         effectiveOrientations |= Qt::Horizontal;
  1050     }
  1190     }
  1054     }   
  1194     }   
  1055 
  1195 
  1056     if ( !effectiveOrientations ) {
  1196     if ( !effectiveOrientations ) {
  1057         // if the whole sizeHint is ignored, return ASAP with default values (0<50<QMAX)
  1197         // if the whole sizeHint is ignored, return ASAP with default values (0<50<QMAX)
  1058         return HbWidgetBase::sizeHint( which, constraint );
  1198         return HbWidgetBase::sizeHint( which, constraint );
  1059     }
       
  1060 
       
  1061     const QFontMetricsF metrics(font());
       
  1062     QSizeF maxSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
       
  1063 
       
  1064     if(constraint.width()>0) {
       
  1065         maxSize.setWidth(constraint.width());
       
  1066     }
       
  1067     if(constraint.height()>0) {
       
  1068         maxSize.setHeight(constraint.height());
       
  1069     }
  1199     }
  1070 
  1200 
  1071     switch(which) {
  1201     switch(which) {
  1072     case Qt::MinimumSize: 
  1202     case Qt::MinimumSize: 
  1073         {
  1203         {
  1074             if ( !d->mText.isEmpty() ) {
  1204             if ( !d->mText.isEmpty() ) {
  1075                 size.setWidth( MinimumWidth ); // just to show something  -- should not matter in read use-case
  1205                 size.setWidth(MinimumWidth); // just to show something  -- should not matter in read use-case
  1076 
  1206                 size = d->respectSizeLimits(size);
  1077                 if( d->mMinLines > 1 ) {
       
  1078                     size.setHeight( ( metrics.height() + metrics.leading() ) * d->mMinLines - metrics.leading() );
       
  1079                 } else {
       
  1080                     size.setHeight( metrics.height() );
       
  1081                 }
       
  1082             }
  1207             }
  1083 
       
  1084             break;
  1208             break;
  1085         }
  1209         }
  1086 
  1210 
  1087     case Qt::PreferredSize: 
  1211     case Qt::PreferredSize: 
  1088         {
  1212         {
  1089             if ( !(effectiveOrientations&Qt::Horizontal) && d->mMinLines > 0 && (d->mMinLines == d->mMaxLines) ) {
  1213             if ( !effectiveOrientations.testFlag(Qt::Horizontal)
       
  1214                     && (d->mMinLines == d->mMaxLines) ) {
  1090                 //optimize single line if the horizontal sizeHint is ignored
  1215                 //optimize single line if the horizontal sizeHint is ignored
  1091                 size.setHeight( ( metrics.height() + metrics.leading() ) * d->mMinLines - metrics.leading() );
  1216                 size = d->respectSizeLimits(size);
  1092                 break;
  1217                 break;
  1093             }
  1218             }
  1094 
  1219 
  1095             // do the heavy calculation
  1220             size = d->calculatePrefferedSize(constraint);
  1096             size = metrics.boundingRect(QRectF(QPointF(),maxSize),
  1221             size = d->respectSizeLimits(size);
  1097                 d->textFlagsFromTextOption(),
       
  1098                 d->mText).size();
       
  1099 
       
  1100 
       
  1101             if( ( constraint.width() < 0 ) && ( constraint.height() < 0 ) ) {
       
  1102 
       
  1103                 if( ( d->mNeedToAdjustSizeHint ) || ( d->oldSize != size ) ) {
       
  1104                     const_cast<HbTextItemPrivate*>(d)->adjustSizeHint();
       
  1105                 }
       
  1106 
       
  1107                 qreal pref =  d->mPrefHeight;
       
  1108 
       
  1109                 if( d->mMaxLines > 0 ) {
       
  1110                     qreal maxLimit =  ( metrics.height() + metrics.leading() ) * d->mMaxLines - metrics.leading();
       
  1111                     if( maxLimit < pref ) {
       
  1112                         pref = maxLimit;
       
  1113                     }
       
  1114 
       
  1115                 }
       
  1116 
       
  1117                 const_cast<HbTextItemPrivate*>(d)->oldSize = size;
       
  1118                 size.setHeight( pref );
       
  1119             }
       
  1120 
       
  1121             break;
  1222             break;
  1122         }
  1223         }
  1123 
  1224 
  1124     default:
  1225     default:
  1125         size = HbWidgetBase::sizeHint( which, constraint );
  1226         size = HbWidgetBase::sizeHint(which, constraint);
  1126     }
  1227     }
  1127 
  1228 
  1128     return size;
  1229     return size;
  1129 }
  1230 }
  1130 
  1231 
  1138     // Listens theme changed event so that item size hint is
  1239     // Listens theme changed event so that item size hint is
  1139 
  1240 
  1140     switch(event->type()) {
  1241     switch(event->type()) {
  1141     case QEvent::LayoutDirectionChange: {
  1242     case QEvent::LayoutDirectionChange: {
  1142             Q_D(HbTextItem);
  1243             Q_D(HbTextItem);
  1143             d->mInvalidateShownText = true;
  1244             d->scheduleTextBuild();
  1144             updateGeometry();
       
  1145         }
  1245         }
  1146         break;
  1246         break;
  1147 
  1247 
  1148     case QEvent::FontChange: {
  1248     case QEvent::FontChange: {
  1149             Q_D(HbTextItem);
  1249             Q_D(HbTextItem);
  1150             d->mInvalidateShownText = true;
  1250 
  1151             prepareGeometryChange();
  1251             if (!d->mTextLayout.font().isCopyOf(font())) {
  1152             updateGeometry();
  1252 
       
  1253 #ifdef HB_TEXT_ITEM_LOGS
       
  1254                 qDebug() << "fontChangeEvent: " << objectName()
       
  1255                         << " text: " << text().left(20)
       
  1256                         << " font: " << font();
       
  1257 #endif // HB_TEXT_ITEM_LOGS
       
  1258 
       
  1259                 d->mTextLayout.setFont(font());
       
  1260                 d->clearAdjustedSizeCache();
       
  1261                 d->scheduleTextBuild();
       
  1262                 prepareGeometryChange();
       
  1263                 updateGeometry();
       
  1264             } else {
       
  1265                 // ignoring event since it has no effect
       
  1266                 return;
       
  1267             }
  1153         }
  1268         }
  1154         break;
  1269         break;
  1155 
  1270 
  1156     default:
  1271     default:
  1157         // comparing event->type() with dynamic values:
  1272         // comparing event->type() with dynamic values:
  1168 }
  1283 }
  1169 
  1284 
  1170 /*!
  1285 /*!
  1171     \reimp
  1286     \reimp
  1172  */
  1287  */
  1173 void HbTextItem::resizeEvent ( QGraphicsSceneResizeEvent * event )
  1288 void HbTextItem::resizeEvent (QGraphicsSceneResizeEvent *event)
  1174 {
  1289 {
  1175     Q_D(HbTextItem);
  1290     Q_D(HbTextItem);
  1176 
  1291 
  1177     HbWidgetBase::resizeEvent(event);
  1292     HbWidgetBase::resizeEvent(event);
  1178 
  1293 
  1179     d->setSize(event->newSize());
  1294     d->scheduleTextBuild();
  1180 
       
  1181     if( ( qAbs(event->oldSize().width() - event->newSize().width()) > EPSILON ) &&
       
  1182         ( ( event->oldSize().width() < preferredWidth() ) || ( event->newSize().width() < preferredWidth() ) ) ){
       
  1183         if( d->adjustSizeHint() ) {
       
  1184             updateGeometry();
       
  1185         }
       
  1186     }
       
  1187 }
  1295 }
  1188 
  1296 
  1189 /*!
  1297 /*!
  1190     @proto
  1298     @proto
  1191     Sets style of text wrapping. \a mode type will be changed to Hb::TextWrapping
  1299     Sets style of text wrapping. \a mode type will be changed to Hb::TextWrapping
  1203     QTextOption textOption = d->mTextLayout.textOption();
  1311     QTextOption textOption = d->mTextLayout.textOption();
  1204     if(textOption.wrapMode()!=textWrapMode) {
  1312     if(textOption.wrapMode()!=textWrapMode) {
  1205         textOption.setWrapMode(textWrapMode);
  1313         textOption.setWrapMode(textWrapMode);
  1206         d->mTextLayout.setTextOption(textOption);
  1314         d->mTextLayout.setTextOption(textOption);
  1207         if(!d->mText.isEmpty()) {
  1315         if(!d->mText.isEmpty()) {
  1208             d->mInvalidateShownText = true;
  1316             d->scheduleTextBuild();
  1209             d->mNeedToAdjustSizeHint = true;
  1317             prepareGeometryChange();
  1210             updateGeometry();
  1318             update();
       
  1319         }
       
  1320 
       
  1321         // is size hint adjustable?
       
  1322         if (parentLayoutItem() && parentLayoutItem()->isLayout()) {
       
  1323             if (d->mAdjustedSize.isValid() &&
       
  1324                 d->mAdjustedSize.width() > size().width()) {
       
  1325                 // restore default size hint
       
  1326                 d->mAdjustedSize = d->respectSizeLimits(
       
  1327                         QSizeF(d->mAdjustedSize.width(), d->mDefaultHeight));
       
  1328                 d->mMinWidthForAdjust = d->mAdjustedSize.width();
       
  1329                 d->mMaxWidthForAdjust = QWIDGETSIZE_MAX;
       
  1330 
       
  1331                 updateGeometry();
       
  1332             }
  1211         }
  1333         }
  1212     }
  1334     }
  1213 }
  1335 }
  1214 
  1336 
  1215 /*!
  1337 /*!
  1259 {
  1381 {
  1260     setFlag(QGraphicsItem::ItemClipsToShape, clipping);
  1382     setFlag(QGraphicsItem::ItemClipsToShape, clipping);
  1261 }
  1383 }
  1262 
  1384 
  1263 /*!
  1385 /*!
  1264     Returns true if text is cliped when item geometry is to small.
  1386     Returns true if text is clipped when item geometry is too small.
       
  1387 
  1265     \sa HbTextItem::setTextClip(bool)
  1388     \sa HbTextItem::setTextClip(bool)
  1266 
  1389 
  1267     Equvalent of QGraphicsItem::flags().testFlag(QGraphicsItem::ItemClipsToShape)
  1390     Equvalent of QGraphicsItem::flags().testFlag(QGraphicsItem::ItemClipsToShape)
  1268  */
  1391  */
  1269 bool HbTextItem::isTextClip() const
  1392 bool HbTextItem::isTextClip() const
  1285     \sa HbTextItem::maximumLines()
  1408     \sa HbTextItem::maximumLines()
  1286  */
  1409  */
  1287 void HbTextItem::setMinimumLines( int minLines )
  1410 void HbTextItem::setMinimumLines( int minLines )
  1288 {
  1411 {
  1289     Q_D( HbTextItem );
  1412     Q_D( HbTextItem );
       
  1413     minLines = qMax(minLines, 1); // zero or nagative values are meanless and are restoring 1
       
  1414 
  1290 	d->setApiProtectionFlag(HbWidgetBasePrivate::AC_TextLinesMin, true);
  1415 	d->setApiProtectionFlag(HbWidgetBasePrivate::AC_TextLinesMin, true);
  1291 
  1416 
  1292     if( minLines != d->mMinLines ) {
  1417     if( minLines != d->mMinLines ) {
  1293         if( ( d->mMaxLines > 0 ) && ( minLines > d->mMaxLines ) ) {
  1418         if( ( d->mMaxLines > 0 ) && ( minLines > d->mMaxLines ) ) {
  1294             d->mMaxLines = minLines;
  1419             d->mMaxLines = minLines;
  1295         }
  1420         }
  1296 
       
  1297         d->mMinLines = minLines;
  1421         d->mMinLines = minLines;
       
  1422 
       
  1423         // not needed?: d->clearAdjustedSizeCache(); // some condition?
  1298         updateGeometry();
  1424         updateGeometry();
  1299     }
  1425     }
  1300 }
  1426 }
  1301 
  1427 
  1302 /*!
  1428 /*!
  1315 void HbTextItem::setMaximumLines( int maxLines )
  1441 void HbTextItem::setMaximumLines( int maxLines )
  1316 {
  1442 {
  1317     Q_D( HbTextItem );
  1443     Q_D( HbTextItem );
  1318 	d->setApiProtectionFlag(HbWidgetBasePrivate::AC_TextLinesMax, true);
  1444 	d->setApiProtectionFlag(HbWidgetBasePrivate::AC_TextLinesMax, true);
  1319 
  1445 
       
  1446     maxLines = qMax(maxLines, 0);
       
  1447 
  1320     if( maxLines != d->mMaxLines ) {
  1448     if( maxLines != d->mMaxLines ) {
  1321         if( ( d->mMinLines > 0 ) && ( maxLines > 0 ) && ( maxLines < d->mMinLines ) ){
  1449         if ((maxLines > 0) && (maxLines < d->mMinLines)){
  1322             d->mMinLines = maxLines;
  1450             d->mMinLines = maxLines;
  1323         }
  1451         }
  1324 
       
  1325         d->mMaxLines = maxLines;
  1452         d->mMaxLines = maxLines;
       
  1453 
       
  1454         d->scheduleTextBuild();
       
  1455         prepareGeometryChange();
       
  1456         update();
       
  1457 
  1326         updateGeometry();
  1458         updateGeometry();
  1327 #ifdef HB_TEXT_MEASUREMENT_UTILITY
  1459 #ifdef HB_TEXT_MEASUREMENT_UTILITY
  1328         if ( HbFeatureManager::instance()->featureStatus( HbFeatureManager::TextMeasurement ) ) {
  1460         if ( HbFeatureManager::instance()->featureStatus( HbFeatureManager::TextMeasurement ) ) {
  1329             setProperty( HbTextMeasurementUtilityNameSpace::textMaxLines, d->mMaxLines );
  1461             setProperty( HbTextMeasurementUtilityNameSpace::textMaxLines, d->mMaxLines );
  1330         }
  1462         }
  1334 
  1466 
  1335 /*!
  1467 /*!
  1336     \sa HbTextItem::setMinimumLines()
  1468     \sa HbTextItem::setMinimumLines()
  1337     \sa HbTextItem::setMaximumLines()
  1469     \sa HbTextItem::setMaximumLines()
  1338     \sa HbTextItem::maximumLines()
  1470     \sa HbTextItem::maximumLines()
  1339     \return "minimum lines" parameter
  1471     \return "minimum lines" parameter (zero value means unset)
  1340  */
  1472  */
  1341 int HbTextItem::minimumLines() const
  1473 int HbTextItem::minimumLines() const
  1342 {
  1474 {
  1343     Q_D( const HbTextItem );
  1475     Q_D( const HbTextItem );
  1344     return d->mMinLines;
  1476     return d->mMinLines;