75 mRtf->setDefaultFont(q->font()); |
74 mRtf->setDefaultFont(q->font()); |
76 } |
75 } |
77 |
76 |
78 void HbRichTextItemPrivate::clear() |
77 void HbRichTextItemPrivate::clear() |
79 { |
78 { |
80 delete mRtf; |
79 } |
81 } |
80 |
82 |
81 void HbRichTextItemPrivate::setDocumentWidth(qreal newWidth) |
83 int HbRichTextItemPrivate::textFlagsFromTextOption() const |
82 { |
84 { |
83 if (!qFuzzyCompare(mRtf->textWidth(), newWidth)) { |
85 int flags = (int)mAlignment; |
84 mRtf->setTextWidth(newWidth); |
86 |
|
87 switch(mTextOption.wrapMode()) { |
|
88 case QTextOption::NoWrap: |
|
89 flags |= Qt::TextSingleLine; |
|
90 break; |
|
91 case QTextOption::WordWrap: |
|
92 flags |=Qt::TextWordWrap; |
|
93 break; |
|
94 case QTextOption::ManualWrap: |
|
95 break; |
|
96 case QTextOption::WrapAnywhere: |
|
97 flags |=Qt::TextWrapAnywhere; |
|
98 break; |
|
99 case QTextOption::WrapAtWordBoundaryOrAnywhere: |
|
100 flags |=Qt::TextWordWrap | Qt::TextWrapAnywhere; |
|
101 break; |
|
102 } |
|
103 |
|
104 if(mDontClip) flags |= Qt::TextDontClip; |
|
105 if(mDontPrint) flags |= Qt::TextDontPrint; |
|
106 |
|
107 return flags; |
|
108 } |
|
109 |
|
110 bool HbRichTextItemPrivate::setLayoutDirection(Qt::LayoutDirection newDirection) |
|
111 { |
|
112 Qt::Alignment oldAlign = mTextOption.alignment(); |
|
113 Qt::Alignment alignment = QStyle::visualAlignment(newDirection, mAlignment); |
|
114 mTextOption.setAlignment(alignment); |
|
115 mTextOption.setTextDirection(newDirection); |
|
116 mRtf->setDefaultTextOption(mTextOption); |
|
117 |
|
118 return alignment!=oldAlign; |
|
119 } |
|
120 |
|
121 void HbRichTextItemPrivate::setSize(const QSizeF &newSize) |
|
122 { |
|
123 if(mRtf->size()!=newSize) { |
|
124 Q_Q(HbRichTextItem); |
|
125 mRtf->setTextWidth(newSize.width()); |
|
126 calculateOffset(); |
85 calculateOffset(); |
127 q->update(); |
|
128 } |
86 } |
129 } |
87 } |
130 |
88 |
131 // #define HB_RICH_TEXT_ITEM_ALWAS_SHOW_FIRST_LINE |
89 // #define HB_RICH_TEXT_ITEM_ALWAS_SHOW_FIRST_LINE |
132 void HbRichTextItemPrivate::calculateOffset() |
90 void HbRichTextItemPrivate::calculateOffset() |
133 { |
91 { |
134 Q_Q(HbRichTextItem); |
92 Q_Q(HbRichTextItem); |
135 |
93 |
136 qreal diff; |
94 qreal diff; |
137 if(mAlignment.testFlag(Qt::AlignTop)) { |
95 Qt::Alignment align = mTextOption.alignment(); |
|
96 if (align.testFlag(Qt::AlignTop)) { |
138 diff = 0.0; |
97 diff = 0.0; |
139 } else { |
98 } else { |
140 diff = q->geometry().height() - mRtf->size().height(); |
99 diff = q->geometry().height() - mRtf->size().height(); |
141 if(!mAlignment.testFlag(Qt::AlignBottom)) { |
100 if (!align.testFlag(Qt::AlignBottom)) { |
142 // default align Qt::AlignVCenter if no flags are set |
101 // default align Qt::AlignVCenter if no flags are set |
143 diff*=0.5; |
102 diff*=0.5; |
144 } |
103 } |
145 } |
104 } |
146 #ifdef HB_RICH_TEXT_ITEM_ALWAS_SHOW_FIRST_LINE |
105 #ifdef HB_RICH_TEXT_ITEM_ALWAS_SHOW_FIRST_LINE |
147 diff = qMax(diff, (qreal)0.0); |
106 diff = qMax(diff, (qreal)0.0); |
148 #endif |
107 #endif |
149 |
108 |
150 if(diff!=mOffset.y()) { |
109 if (diff!=mOffset.y()) { |
151 mOffset.setY(diff); |
110 mOffset.setY(diff); |
152 q->prepareGeometryChange(); |
111 q->prepareGeometryChange(); |
153 } |
112 } |
|
113 } |
|
114 |
|
115 QSizeF HbRichTextItemPrivate::minimumSizeHint(const QSizeF &/*constraint*/) const |
|
116 { |
|
117 QSizeF result(KMinimumRichTextWidth, 0); |
|
118 |
|
119 QTextBlock textBlock = mRtf->begin(); |
|
120 if (textBlock.isValid() && textBlock.layout()->lineCount() > 0) { |
|
121 QTextLine line = textBlock.layout()->lineAt(0); |
|
122 result.setHeight(line.height()); |
|
123 } else { |
|
124 QFontMetricsF metrics(mRtf->defaultFont()); |
|
125 result.setHeight(metrics.height()); |
|
126 } |
|
127 |
|
128 qreal doubleDocMargin = mRtf->documentMargin() * (qreal)2.0; |
|
129 result.rheight() += doubleDocMargin; |
|
130 result.rwidth() += doubleDocMargin; |
|
131 |
|
132 return result; |
|
133 } |
|
134 |
|
135 void HbRichTextItemPrivate::clearPrefSizeCache() |
|
136 { |
|
137 mPrefSize.setWidth(-1); |
|
138 mMinWidthForAdjust = QWIDGETSIZE_MAX; |
|
139 mMaxWidthForAdjust = -1; |
|
140 } |
|
141 |
|
142 QSizeF HbRichTextItemPrivate::preferredSizeHint(const QSizeF &constraint) const |
|
143 { |
|
144 if (mPrefSizeConstraint==constraint && mPrefSize.isValid()) { |
|
145 return mPrefSize; |
|
146 } |
|
147 mPrefSizeConstraint=constraint; |
|
148 |
|
149 QSizeF result; |
|
150 |
|
151 if (constraint.width()<=0) { |
|
152 mRtf->setTextWidth(QWIDGETSIZE_MAX); |
|
153 } else { |
|
154 QTextOption::WrapMode wrapMode = mTextOption.wrapMode(); |
|
155 // optimization when there is no automatic wrap there is no reason |
|
156 // to setTextWidth with width constraint (width measure is not needed) |
|
157 if (wrapMode!=QTextOption::NoWrap |
|
158 && wrapMode!=QTextOption::ManualWrap) { |
|
159 mRtf->setTextWidth(constraint.width()); |
|
160 } |
|
161 } |
|
162 result = mRtf->size(); |
|
163 mMaxWidthForAdjust = result.width(); |
|
164 result.setWidth(mRtf->idealWidth()); |
|
165 mMinWidthForAdjust = result.width(); |
|
166 mDefaultPrefHeight = result.height(); |
|
167 mPrefSize = result; |
|
168 |
|
169 return result; |
|
170 } |
|
171 |
|
172 bool HbRichTextItemPrivate::isAdjustHeightNeeded(qreal newWidth, |
|
173 qreal prefHeight, |
|
174 qreal minHeight, |
|
175 qreal maxHeight) |
|
176 { |
|
177 // first check if wrapping of text is not active |
|
178 QTextOption::WrapMode wrapMode = mTextOption.wrapMode(); |
|
179 if (wrapMode==QTextOption::NoWrap |
|
180 || wrapMode==QTextOption::ManualWrap) { |
|
181 return false; |
|
182 } |
|
183 |
|
184 // preferred height was set from outside of this class so there is mo reason to adjust it |
|
185 if (mPrefSizeConstraint.height()>0) { |
|
186 return false; |
|
187 } |
|
188 |
|
189 // check if adjusted size has been already calculated |
|
190 // new width is bigger than last estimated range of same height |
|
191 if ((mMaxWidthForAdjust>=newWidth |
|
192 // new width is smaller than last estimated range of same height |
|
193 && newWidth>=mMinWidthForAdjust)) { |
|
194 return false; |
|
195 } |
|
196 |
|
197 if (!mPrefSize.isValid()) { |
|
198 // this means that preferred size is set outside of class by setPreferredSize |
|
199 // so sizeHint(Qt::Preferredsize) was not called and size adjustment is useless |
|
200 return false; |
|
201 } |
|
202 |
|
203 // new text width was set in setGeometry here it is not needed |
|
204 Q_ASSERT_X(qFuzzyCompare(mRtf->textWidth(), newWidth), |
|
205 "HbRichTextItemPrivate::isAdjustHeightNeeded", |
|
206 QString("mRtf->textWidth()=%1, newWidth=%2") |
|
207 .arg(mRtf->textWidth()) |
|
208 .arg(newWidth).toAscii().data()); |
|
209 |
|
210 // if preconditions are met test if current height is enough |
|
211 QSizeF newAdjust = mRtf->size(); |
|
212 |
|
213 if (qFuzzyCompare(newAdjust.height(), mPrefSize.height())) { |
|
214 // height is same as last time update range of same height |
|
215 mMaxWidthForAdjust = qMax(mMaxWidthForAdjust, newWidth); |
|
216 mMinWidthForAdjust = qMin(mMinWidthForAdjust, newWidth); |
|
217 return false; |
|
218 } |
|
219 |
|
220 // new height was calculated create new range for which |
|
221 // current mPrefSize.height is valid |
|
222 mMaxWidthForAdjust = newWidth; |
|
223 mMinWidthForAdjust = mRtf->idealWidth(); |
|
224 |
|
225 // store new height, don't change width |
|
226 mPrefSize.setHeight(newAdjust.height()); |
|
227 |
|
228 Q_ASSERT_X(mPrefSizeConstraint.width()>0 || mPrefSize.width()>=mMinWidthForAdjust, |
|
229 "HbRichTextItemPrivate::isAdjustHeightNeeded", |
|
230 QString("Fail for (%1<%2) string: \"%3\"") |
|
231 .arg(mPrefSize.width()) |
|
232 .arg(mMinWidthForAdjust) |
|
233 .arg(mText).toAscii().data()); |
|
234 |
|
235 if (qFuzzyCompare(qBound(minHeight, mPrefSize.height(), maxHeight), |
|
236 prefHeight)) { |
|
237 // updateGeometry has no effect |
|
238 return false; |
|
239 } |
|
240 |
|
241 // all conditions for calling updateGeometry are meet |
|
242 return true; |
|
243 } |
|
244 |
|
245 /* |
|
246 returns true if updateGeometry is needed |
|
247 */ |
|
248 bool HbRichTextItemPrivate::restoreDefaultHeightHint() |
|
249 { |
|
250 if (mPrefSizeConstraint.height()>0) { |
|
251 return false; |
|
252 } |
|
253 |
|
254 if (mPrefSizeConstraint.width()>0) { |
|
255 clearPrefSizeCache(); |
|
256 return true; |
|
257 } |
|
258 |
|
259 if (mPrefSize.height()==mDefaultPrefHeight) { |
|
260 return true; |
|
261 } |
|
262 |
|
263 mPrefSize.setHeight(mDefaultPrefHeight); |
|
264 mMinWidthForAdjust = mPrefSize.width(); |
|
265 mMaxWidthForAdjust = QWIDGETSIZE_MAX; |
|
266 return true; |
154 } |
267 } |
155 |
268 |
156 /*! |
269 /*! |
157 @proto |
270 @proto |
158 @hbcore |
271 @hbcore |
227 \sa HbRichTextItem::setText() |
342 \sa HbRichTextItem::setText() |
228 */ |
343 */ |
229 |
344 |
230 QString HbRichTextItem::text() const |
345 QString HbRichTextItem::text() const |
231 { |
346 { |
232 Q_D( const HbRichTextItem ); |
347 Q_D(const HbRichTextItem); |
233 return d->mText; |
348 return d->mText; |
234 } |
349 } |
235 |
350 |
236 /*! |
351 /*! |
237 Sets \a alignment for the text from Qt::Alignment enumeration. |
352 Sets \a alignment for the text from Qt::Alignment enumeration. |
238 |
353 |
239 \sa HbRichTextItem::alignment() |
354 \sa HbRichTextItem::alignment() |
240 */ |
355 */ |
241 void HbRichTextItem::setAlignment(Qt::Alignment alignment) |
356 void HbRichTextItem::setAlignment(Qt::Alignment alignment) |
242 { |
357 { |
243 Q_D( HbRichTextItem ); |
358 Q_D(HbRichTextItem); |
244 d->setApiProtectionFlag(HbWidgetBasePrivate::AC_TextAlign, true); |
359 d->setApiProtectionFlag(HbWidgetBasePrivate::AC_TextAlign, true); |
245 alignment &= Qt::AlignVertical_Mask | Qt::AlignHorizontal_Mask; |
360 alignment = d->combineAlignment(alignment, d->mTextOption.alignment()); |
246 if( d->mAlignment!=alignment ) { |
361 if (d->mTextOption.alignment()!=alignment) { |
247 prepareGeometryChange(); |
362 prepareGeometryChange(); |
248 d->mAlignment = alignment; |
363 d->mTextOption.setAlignment(alignment); |
|
364 d->mRtf->setDefaultTextOption(d->mTextOption); |
249 d->calculateOffset(); |
365 d->calculateOffset(); |
250 if(d->setLayoutDirection(layoutDirection())) { |
366 update(); |
251 update(); |
|
252 } |
|
253 } |
367 } |
254 } |
368 } |
255 |
369 |
256 /*! |
370 /*! |
257 Returns alignment for the text from Qt::Alignment enumeration. |
371 Returns alignment for the text from Qt::Alignment enumeration. |
258 |
372 |
259 \sa HbRichTextItem::setAlignment() |
373 \sa HbRichTextItem::setAlignment() |
260 */ |
374 */ |
261 Qt::Alignment HbRichTextItem::alignment() const |
375 Qt::Alignment HbRichTextItem::alignment() const |
262 { |
376 { |
263 Q_D( const HbRichTextItem ); |
377 Q_D(const HbRichTextItem); |
264 return d->mAlignment; |
378 return d->mTextOption.alignment(); |
265 } |
379 } |
266 |
380 |
267 /*! |
381 /*! |
268 \reimp |
382 \reimp |
269 */ |
383 */ |
270 void HbRichTextItem::paint(QPainter *painter, |
384 void HbRichTextItem::paint(QPainter *painter, |
271 const QStyleOptionGraphicsItem *option, |
385 const QStyleOptionGraphicsItem *option, |
272 QWidget *widget) |
386 QWidget *widget) |
273 { |
387 { |
274 Q_UNUSED(option); |
|
275 Q_UNUSED(widget); |
388 Q_UNUSED(widget); |
276 |
389 |
277 Q_D(HbRichTextItem); |
390 Q_D(HbRichTextItem); |
278 |
391 |
279 // Save painter's state |
392 if (option->exposedRect.isEmpty()) { |
280 QRegion oldClipRegion = painter->clipRegion(); |
393 // nothing to paint |
281 QTransform oldTransform = painter->transform(); |
394 return; |
282 |
395 } |
283 if(!d->mDontPrint) { |
396 |
284 if(!d->mDontClip) { |
397 painter->translate(d->mOffset); |
285 painter->setClipRect(contentsRect(), Qt::IntersectClip); |
398 |
286 } |
399 QAbstractTextDocumentLayout::PaintContext context; |
287 painter->translate(d->mOffset); |
400 context.clip = option->exposedRect; |
288 QAbstractTextDocumentLayout::PaintContext context; |
401 // painter was translated so it should be compensated |
289 context.palette.setColor(QPalette::Text, textDefaultColor()); |
402 context.clip.translate(-d->mOffset); |
290 d->mRtf->documentLayout()->draw(painter, context); |
403 |
291 } |
404 context.palette.setColor(QPalette::Text, textDefaultColor()); |
292 |
405 |
293 // Restore painter's state |
406 d->mRtf->documentLayout()->draw(painter, context); |
294 painter->setClipRegion(oldClipRegion); |
407 |
295 painter->setTransform(oldTransform); |
408 // restore painter |
|
409 painter->translate(-d->mOffset); |
296 } |
410 } |
297 |
411 |
298 /*! |
412 /*! |
299 \reimp |
413 \reimp |
300 |
414 |
304 { |
418 { |
305 Q_D(HbRichTextItem); |
419 Q_D(HbRichTextItem); |
306 |
420 |
307 HbWidgetBase::setGeometry(rect); |
421 HbWidgetBase::setGeometry(rect); |
308 |
422 |
309 if(rect.isValid()) { |
423 // this call is needed since there is possible scenario |
310 d->setSize(rect.size()); |
424 // when size was not changed after updateGeometry and sizeHint calls |
|
425 d->setDocumentWidth(size().width()); |
|
426 |
|
427 if (parentLayoutItem() && parentLayoutItem()->isLayout()) { |
|
428 // rect.size can't be used here since size can be limited inside of |
|
429 // called method HbWidgetBase::setGeometry(rect) so size is used which |
|
430 // holds current size |
|
431 if (d->isAdjustHeightNeeded(size().width(), |
|
432 preferredHeight(), |
|
433 minimumHeight(), |
|
434 maximumHeight())) { |
|
435 updateGeometry(); |
|
436 return; |
|
437 } |
311 } |
438 } |
312 } |
439 } |
313 |
440 |
314 /*! |
441 /*! |
315 \reimp |
442 \reimp |
316 */ |
443 */ |
317 QRectF HbRichTextItem::boundingRect () const |
444 QRectF HbRichTextItem::boundingRect () const |
318 { |
445 { |
319 Q_D(const HbRichTextItem); |
446 Q_D(const HbRichTextItem); |
320 |
447 |
321 QRectF result(d->mOffset, d->mRtf->size()); |
448 QRectF result(d->mRtf->documentLayout()->frameBoundingRect(d->mRtf->rootFrame())); |
322 |
449 result.translate(d->mOffset); |
323 if(!d->mDontClip) { |
450 |
|
451 if (flags().testFlag(QGraphicsItem::ItemClipsToShape)) { |
324 // clip |
452 // clip |
325 result = result.intersect(QRectF(QPointF(), |
453 result = result.intersect(contentsRect()); |
326 size())); |
|
327 } |
454 } |
328 return result; |
455 return result; |
329 } |
456 } |
330 |
457 |
331 /*! |
458 /*! |
332 \reimp |
459 \reimp |
333 Relayouts text according to new size. |
460 Relayouts text according to new size. |
334 */ |
461 */ |
335 void HbRichTextItem::resizeEvent(QGraphicsSceneResizeEvent *event) |
462 void HbRichTextItem::resizeEvent(QGraphicsSceneResizeEvent * /*event*/) |
336 { |
463 { |
337 Q_D(HbRichTextItem); |
464 Q_D(HbRichTextItem); |
338 |
465 |
339 d->setSize(event->newSize()); |
466 d->setDocumentWidth(size().width()); |
|
467 d->calculateOffset(); |
340 } |
468 } |
341 |
469 |
342 /*! |
470 /*! |
343 \reimp |
471 \reimp |
344 This impelementation detects layout direction changes, font changes and theme changes. |
472 This implementation detects layout direction changes, font changes and theme changes. |
345 */ |
473 */ |
346 void HbRichTextItem::changeEvent(QEvent *event) |
474 void HbRichTextItem::changeEvent(QEvent *event) |
347 { |
475 { |
348 Q_D(HbRichTextItem); |
476 Q_D(HbRichTextItem); |
349 |
477 |
350 switch(event->type()) { |
478 switch(event->type()) { |
351 case QEvent::LayoutDirectionChange: { |
479 case QEvent::LayoutDirectionChange: { |
352 prepareGeometryChange(); |
480 prepareGeometryChange(); |
353 d->setLayoutDirection(layoutDirection()); |
481 d->mTextOption.setTextDirection(layoutDirection()); |
|
482 d->mRtf->setDefaultTextOption(d->mTextOption); |
354 update(); |
483 update(); |
355 } |
484 } |
356 break; |
485 break; |
357 |
486 |
358 case QEvent::FontChange: { |
487 case QEvent::FontChange: { |
359 d->mRtf->setDefaultFont(font()); |
488 d->mRtf->setDefaultFont(font()); |
|
489 d->clearPrefSizeCache(); |
360 updateGeometry(); |
490 updateGeometry(); |
361 } |
491 } |
362 break; |
492 break; |
363 |
493 |
364 default: |
494 default: |
365 // Listens theme changed event so that item size hint is |
495 // Listens theme changed event so that item size hint is |
366 // update when physical font is changed. |
496 // update when physical font is changed. |
367 if (event->type() == HbEvent::ThemeChanged) { |
497 if (event->type() == HbEvent::ThemeChanged) { |
368 Q_D(HbRichTextItem); |
498 Q_D(HbRichTextItem); |
369 d->mDefaultColor = QColor(); |
499 d->mDefaultColor = QColor(); |
370 if(!d->mColor.isValid()) { |
500 if (!d->mColor.isValid()) { |
371 update(); |
501 update(); |
372 } |
502 } |
373 } |
503 } |
374 } |
504 } |
375 HbWidgetBase::changeEvent(event); |
505 HbWidgetBase::changeEvent(event); |
511 /*! |
627 /*! |
512 * Shows (default) or hides text. Size hint remains unchanged (same as when text is visible). |
628 * Shows (default) or hides text. Size hint remains unchanged (same as when text is visible). |
513 */ |
629 */ |
514 void HbRichTextItem::setTextVisible(bool isVisible) |
630 void HbRichTextItem::setTextVisible(bool isVisible) |
515 { |
631 { |
516 Q_D(HbRichTextItem); |
632 setVisible(isVisible); |
517 if( d->mDontPrint == isVisible ) { |
|
518 d->mDontPrint = !isVisible; |
|
519 update(); |
|
520 } |
|
521 } |
633 } |
522 |
634 |
523 /*! |
635 /*! |
524 * Returns if text is visible. |
636 * Returns if text is visible. |
525 */ |
637 */ |
526 bool HbRichTextItem::isTextVisible() const |
638 bool HbRichTextItem::isTextVisible() const |
527 { |
639 { |
528 Q_D(const HbRichTextItem); |
640 return isVisible(); |
529 return !d->mDontPrint; |
|
530 } |
641 } |
531 |
642 |
532 /*! |
643 /*! |
533 * Enables (default) or disables text clipping when item geometry is too small. |
644 * Enables (default) or disables text clipping when item geometry is too small. |
534 */ |
645 */ |
535 void HbRichTextItem::setTextClip(bool cliping) |
646 void HbRichTextItem::setTextClip(bool clipping) |
536 { |
647 { |
537 Q_D(HbRichTextItem); |
648 setFlag(QGraphicsItem::ItemClipsToShape, clipping); |
538 if( d->mDontClip == cliping ) { |
|
539 prepareGeometryChange(); |
|
540 d->mDontClip = !cliping; |
|
541 setFlag(QGraphicsItem::ItemClipsToShape, cliping); |
|
542 update(); |
|
543 } |
|
544 } |
649 } |
545 |
650 |
546 /*! |
651 /*! |
547 * Returns true if text is clipped when item geometry is too small. |
652 * Returns true if text is clipped when item geometry is too small. |
548 */ |
653 */ |
549 bool HbRichTextItem::isTextClip() const |
654 bool HbRichTextItem::isTextClip() const |
550 { |
655 { |
551 Q_D(const HbRichTextItem); |
656 return flags().testFlag(ItemClipsToShape); |
552 return !d->mDontClip; |
|
553 } |
657 } |
554 |
658 |
555 // end of file |
659 // end of file |