|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (developer.feedback@nokia.com) |
|
6 ** |
|
7 ** This file is part of the HbCore module of the UI Extensions for Mobile. |
|
8 ** |
|
9 ** GNU Lesser General Public License Usage |
|
10 ** This file may be used under the terms of the GNU Lesser General Public |
|
11 ** License version 2.1 as published by the Free Software Foundation and |
|
12 ** appearing in the file LICENSE.LGPL included in the packaging of this file. |
|
13 ** Please review the following information to ensure the GNU Lesser General |
|
14 ** Public License version 2.1 requirements will be met: |
|
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
16 ** |
|
17 ** In addition, as a special exception, Nokia gives you certain additional |
|
18 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
20 ** |
|
21 ** If you have questions regarding the use of this file, please contact |
|
22 ** Nokia at developer.feedback@nokia.com. |
|
23 ** |
|
24 ****************************************************************************/ |
|
25 |
|
26 #include "hbrichtextitem.h" |
|
27 #include "hbrichtextitem_p.h" |
|
28 #include "hbtextutils_p.h" |
|
29 #include "hbcolorscheme.h" |
|
30 #include "hbevent.h" |
|
31 |
|
32 #include <QTextDocument> |
|
33 #include <QStyle> |
|
34 #include <QGraphicsSceneResizeEvent> |
|
35 #include <QTextBlock> |
|
36 #include <QTextLayout> |
|
37 #include <QPainter> |
|
38 #include <QAbstractTextDocumentLayout> |
|
39 |
|
40 const int KMinimumLetersToShow = 4; |
|
41 |
|
42 static const QString KDefaultColorThemeName = "qtc_view_normal"; |
|
43 |
|
44 HbRichTextItemPrivate::HbRichTextItemPrivate() : |
|
45 mAlignment(Qt::AlignLeft|Qt::AlignVCenter), |
|
46 mTextOption(mAlignment), |
|
47 mDontPrint(false), |
|
48 mDontClip(false), |
|
49 mRtf(0) |
|
50 { |
|
51 } |
|
52 |
|
53 HbRichTextItemPrivate::~HbRichTextItemPrivate() |
|
54 { |
|
55 } |
|
56 |
|
57 /* |
|
58 * private constructor |
|
59 */ |
|
60 void HbRichTextItemPrivate::init() |
|
61 { |
|
62 Q_Q(HbRichTextItem); |
|
63 |
|
64 q->setFlag(QGraphicsItem::ItemClipsToShape, !mDontClip); |
|
65 q->setFlag(QGraphicsItem::ItemIsSelectable, false); |
|
66 q->setFlag(QGraphicsItem::ItemIsFocusable, false); |
|
67 |
|
68 mRtf = new QTextDocument(q); |
|
69 mRtf->setDocumentMargin(0.0); // no margins needed |
|
70 |
|
71 mTextOption.setWrapMode(QTextOption::NoWrap); |
|
72 mRtf->setDefaultTextOption(mTextOption); |
|
73 |
|
74 mRtf->setDefaultFont(q->font()); |
|
75 } |
|
76 |
|
77 void HbRichTextItemPrivate::clear() |
|
78 { |
|
79 delete mRtf; |
|
80 } |
|
81 |
|
82 int HbRichTextItemPrivate::textFlagsFromTextOption() const |
|
83 { |
|
84 int flags = (int)mAlignment; |
|
85 |
|
86 switch(mTextOption.wrapMode()) { |
|
87 case QTextOption::NoWrap: |
|
88 flags |= Qt::TextSingleLine; |
|
89 break; |
|
90 case QTextOption::WordWrap: |
|
91 flags |=Qt::TextWordWrap; |
|
92 break; |
|
93 case QTextOption::ManualWrap: |
|
94 break; |
|
95 case QTextOption::WrapAnywhere: |
|
96 flags |=Qt::TextWrapAnywhere; |
|
97 break; |
|
98 case QTextOption::WrapAtWordBoundaryOrAnywhere: |
|
99 flags |=Qt::TextWordWrap | Qt::TextWrapAnywhere; |
|
100 break; |
|
101 } |
|
102 |
|
103 if(mDontClip) flags |= Qt::TextDontClip; |
|
104 if(mDontPrint) flags |= Qt::TextDontPrint; |
|
105 |
|
106 return flags; |
|
107 } |
|
108 |
|
109 bool HbRichTextItemPrivate::setLayoutDirection(Qt::LayoutDirection newDirection) |
|
110 { |
|
111 Qt::Alignment oldAlign = mTextOption.alignment(); |
|
112 Qt::Alignment alignment = QStyle::visualAlignment(newDirection, mAlignment); |
|
113 if(alignment!=oldAlign) { |
|
114 mTextOption.setAlignment(alignment); |
|
115 mRtf->setDefaultTextOption(mTextOption); |
|
116 return true; |
|
117 } |
|
118 return false; |
|
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(); |
|
127 q->update(); |
|
128 } |
|
129 } |
|
130 |
|
131 // #define HB_RICH_TEXT_ITEM_ALWAS_SHOW_FIRST_LINE |
|
132 void HbRichTextItemPrivate::calculateOffset() |
|
133 { |
|
134 Q_Q(HbRichTextItem); |
|
135 |
|
136 qreal diff; |
|
137 if(mAlignment.testFlag(Qt::AlignTop)) { |
|
138 diff = 0.0; |
|
139 } else { |
|
140 diff = q->geometry().height() - mRtf->size().height(); |
|
141 if(!mAlignment.testFlag(Qt::AlignBottom)) { |
|
142 // default align Qt::AlignVCenter if no flags are set |
|
143 diff*=0.5; |
|
144 } |
|
145 } |
|
146 #ifdef HB_RICH_TEXT_ITEM_ALWAS_SHOW_FIRST_LINE |
|
147 diff = qMax(diff, (qreal)0.0); |
|
148 #endif |
|
149 |
|
150 if(diff!=mOffset.y()) { |
|
151 mOffset.setY(diff); |
|
152 q->prepareGeometryChange(); |
|
153 } |
|
154 } |
|
155 |
|
156 /*! |
|
157 @proto |
|
158 @hbcore |
|
159 \class HbRichTextItem |
|
160 \brief HbRichTextItem is a item for showing formatted text. |
|
161 |
|
162 |
|
163 This is mainly used as a primitive in widgets. |
|
164 It derives from HbWidgetBase so it can be layouted. |
|
165 |
|
166 */ |
|
167 |
|
168 /*! |
|
169 Constructor for the class. |
|
170 */ |
|
171 |
|
172 HbRichTextItem::HbRichTextItem(QGraphicsItem *parent) : |
|
173 HbWidgetBase(*new HbRichTextItemPrivate, parent) |
|
174 { |
|
175 Q_D(HbRichTextItem); |
|
176 d->init(); |
|
177 } |
|
178 |
|
179 /*! |
|
180 Constructor which set content using \a html format. |
|
181 */ |
|
182 HbRichTextItem::HbRichTextItem(const QString &html, QGraphicsItem *parent) : |
|
183 HbWidgetBase(*new HbRichTextItemPrivate, parent) |
|
184 { |
|
185 Q_D(HbRichTextItem); |
|
186 d->init(); |
|
187 setText(html); |
|
188 } |
|
189 |
|
190 /* |
|
191 Constructor for internal use only |
|
192 */ |
|
193 HbRichTextItem::HbRichTextItem(HbRichTextItemPrivate &dd, QGraphicsItem *parent) : |
|
194 HbWidgetBase(dd, parent) |
|
195 { |
|
196 Q_D(HbRichTextItem); |
|
197 d->init(); |
|
198 } |
|
199 |
|
200 /*! |
|
201 Destructor for the class. |
|
202 */ |
|
203 HbRichTextItem::~HbRichTextItem() |
|
204 { |
|
205 Q_D(HbRichTextItem); |
|
206 d->clear(); |
|
207 } |
|
208 |
|
209 /*! |
|
210 Sets the \a text in html format. |
|
211 |
|
212 \sa HbRichTextItem::text() |
|
213 */ |
|
214 void HbRichTextItem::setText(const QString &text) |
|
215 { |
|
216 Q_D(HbRichTextItem); |
|
217 if (d->mText != text) { |
|
218 d->mText = text; |
|
219 d->mRtf->setHtml(text); |
|
220 updateGeometry(); |
|
221 } |
|
222 } |
|
223 |
|
224 /*! |
|
225 Returns the text in html format. |
|
226 |
|
227 \sa HbRichTextItem::setText() |
|
228 */ |
|
229 |
|
230 QString HbRichTextItem::text() const |
|
231 { |
|
232 Q_D( const HbRichTextItem ); |
|
233 return d->mText; |
|
234 } |
|
235 |
|
236 /*! |
|
237 Sets \a alignment for the text from Qt::Alignment enumeration. |
|
238 |
|
239 \sa HbRichTextItem::alignment() |
|
240 */ |
|
241 void HbRichTextItem::setAlignment(Qt::Alignment alignment) |
|
242 { |
|
243 Q_D( HbRichTextItem ); |
|
244 d->setApiProtectionFlag(HbWidgetBasePrivate::AC_TextAlign, true); |
|
245 alignment &= Qt::AlignVertical_Mask | Qt::AlignHorizontal_Mask; |
|
246 if( d->mAlignment!=alignment ) { |
|
247 prepareGeometryChange(); |
|
248 d->mAlignment = alignment; |
|
249 d->calculateOffset(); |
|
250 if(d->setLayoutDirection(layoutDirection())) { |
|
251 update(); |
|
252 } |
|
253 } |
|
254 } |
|
255 |
|
256 /*! |
|
257 Returns alignment for the text from Qt::Alignment enumeration. |
|
258 |
|
259 \sa HbRichTextItem::setAlignment() |
|
260 */ |
|
261 Qt::Alignment HbRichTextItem::alignment() const |
|
262 { |
|
263 Q_D( const HbRichTextItem ); |
|
264 return d->mAlignment; |
|
265 } |
|
266 |
|
267 /*! |
|
268 \reimp |
|
269 */ |
|
270 void HbRichTextItem::paint(QPainter *painter, |
|
271 const QStyleOptionGraphicsItem *option, |
|
272 QWidget *widget) |
|
273 { |
|
274 Q_UNUSED(option); |
|
275 Q_UNUSED(widget); |
|
276 |
|
277 Q_D(HbRichTextItem); |
|
278 |
|
279 if(!d->mDontPrint) { |
|
280 if(!d->mDontClip) { |
|
281 painter->setClipRect(contentsRect(), Qt::IntersectClip); |
|
282 } |
|
283 painter->translate(d->mOffset); |
|
284 QAbstractTextDocumentLayout::PaintContext context; |
|
285 context.palette.setColor(QPalette::Text, textDefaultColor()); |
|
286 d->mRtf->documentLayout()->draw(painter, context); |
|
287 } |
|
288 } |
|
289 |
|
290 /*! |
|
291 \reimp |
|
292 |
|
293 Sets new position and relayouts text according to new size. |
|
294 */ |
|
295 void HbRichTextItem::setGeometry(const QRectF & rect) |
|
296 { |
|
297 Q_D(HbRichTextItem); |
|
298 |
|
299 HbWidgetBase::setGeometry(rect); |
|
300 |
|
301 if(rect.isValid()) { |
|
302 d->setSize(rect.size()); |
|
303 } |
|
304 } |
|
305 |
|
306 /*! |
|
307 \reimp |
|
308 */ |
|
309 QRectF HbRichTextItem::boundingRect () const |
|
310 { |
|
311 Q_D(const HbRichTextItem); |
|
312 |
|
313 QRectF result(d->mOffset, d->mRtf->size()); |
|
314 |
|
315 if(!d->mDontClip) { |
|
316 // clip |
|
317 result = result.intersect(QRectF(QPointF(), |
|
318 size())); |
|
319 } |
|
320 return result; |
|
321 } |
|
322 |
|
323 /*! |
|
324 \reimp |
|
325 Relayouts text according to new size. |
|
326 */ |
|
327 void HbRichTextItem::resizeEvent(QGraphicsSceneResizeEvent *event) |
|
328 { |
|
329 Q_D(HbRichTextItem); |
|
330 |
|
331 d->setSize(event->newSize()); |
|
332 } |
|
333 |
|
334 /*! |
|
335 \reimp |
|
336 This impelementation detects layout direction changes, font changes and theme changes. |
|
337 */ |
|
338 void HbRichTextItem::changeEvent(QEvent *event) |
|
339 { |
|
340 Q_D(HbRichTextItem); |
|
341 |
|
342 switch(event->type()) { |
|
343 case QEvent::LayoutDirectionChange: { |
|
344 prepareGeometryChange(); |
|
345 if(d->setLayoutDirection(layoutDirection())) { |
|
346 update(); |
|
347 } |
|
348 } |
|
349 break; |
|
350 |
|
351 case QEvent::FontChange: { |
|
352 d->mRtf->setDefaultFont(font()); |
|
353 updateGeometry(); |
|
354 } |
|
355 break; |
|
356 |
|
357 default: |
|
358 // Listens theme changed event so that item size hint is |
|
359 // update when physical font is changed. |
|
360 if (event->type() == HbEvent::ThemeChanged) { |
|
361 Q_D(HbRichTextItem); |
|
362 d->mDefaultColor = QColor(); |
|
363 if(!d->mColor.isValid()) { |
|
364 update(); |
|
365 } |
|
366 } |
|
367 } |
|
368 HbWidgetBase::changeEvent(event); |
|
369 } |
|
370 |
|
371 /*! |
|
372 \reimp |
|
373 For which = PreferredSize returns reasonable size (QTextDocument::adjustSize()). |
|
374 For which = MinimumSize returns size of 4 first letters |
|
375 \a constraint width is taken into account. |
|
376 */ |
|
377 QSizeF HbRichTextItem::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const |
|
378 { |
|
379 Q_D(const HbRichTextItem); |
|
380 QSizeF result; |
|
381 switch(which) { |
|
382 case Qt::MinimumSize: { |
|
383 QTextBlock textBlock = d->mRtf->begin(); |
|
384 if(textBlock.isValid() && textBlock.layout()->lineCount() > 0) { |
|
385 QTextLine line = textBlock.layout()->lineAt(0); |
|
386 result.setHeight(line.height()); |
|
387 int cursorPos(KMinimumLetersToShow); |
|
388 result.setWidth( line.cursorToX(&cursorPos) ); |
|
389 |
|
390 qreal doubleDocMargin = d->mRtf->documentMargin() * (qreal)2.0; |
|
391 result.rheight() += doubleDocMargin; |
|
392 result.rwidth() += doubleDocMargin; |
|
393 } else { |
|
394 result = HbWidgetBase::sizeHint(which, constraint); |
|
395 } |
|
396 } |
|
397 break; |
|
398 |
|
399 case Qt::PreferredSize: { |
|
400 if(constraint.width()<=0) { |
|
401 d->mRtf->adjustSize(); |
|
402 } else { |
|
403 d->mRtf->setTextWidth(constraint.width()); |
|
404 } |
|
405 result = d->mRtf->size(); |
|
406 } |
|
407 break; |
|
408 |
|
409 default: |
|
410 result = HbWidgetBase::sizeHint(which, constraint); |
|
411 } |
|
412 return result; |
|
413 } |
|
414 |
|
415 /*! |
|
416 * @proto |
|
417 * Sets the text wrapping mode. |
|
418 * |
|
419 * \sa HbRichTextItem::textWrapping |
|
420 * \sa QTextOption::setWrapMode |
|
421 */ |
|
422 void HbRichTextItem::setTextWrapping(Hb::TextWrapping mode) |
|
423 { |
|
424 Q_D(HbRichTextItem); |
|
425 d->setApiProtectionFlag(HbWidgetBasePrivate::AC_TextWrapMode, true); |
|
426 QTextOption::WrapMode textWrapMode = static_cast<QTextOption::WrapMode>(mode); |
|
427 |
|
428 if(d->mTextOption.wrapMode()!=textWrapMode) { |
|
429 prepareGeometryChange(); |
|
430 d->mTextOption.setWrapMode(textWrapMode); |
|
431 d->mRtf->setDefaultTextOption(d->mTextOption); |
|
432 updateGeometry(); |
|
433 } |
|
434 } |
|
435 |
|
436 /*! |
|
437 * @proto |
|
438 * Returns style of text wrapping. |
|
439 * |
|
440 * \sa HbRichTextItem::setTextWrapping |
|
441 * \sa QTextOption::wrapMode |
|
442 */ |
|
443 Hb::TextWrapping HbRichTextItem::textWrapping() const |
|
444 { |
|
445 Q_D(const HbRichTextItem); |
|
446 |
|
447 return static_cast<Hb::TextWrapping>(d->mTextOption.wrapMode()); |
|
448 } |
|
449 |
|
450 /*! |
|
451 * Returns color used as a default text color. |
|
452 * If invalid color was set color for text is fetch from parent widget. |
|
453 * If invalid color was set and no parent widget was set this will return |
|
454 * default foreground color. |
|
455 * |
|
456 * \sa setTextDefaultColor() |
|
457 */ |
|
458 QColor HbRichTextItem::textDefaultColor() const |
|
459 { |
|
460 Q_D( const HbRichTextItem ); |
|
461 |
|
462 if (d->mColor.isValid()) { // Means user has set text color |
|
463 return d->mColor; |
|
464 } |
|
465 if (!d->mDefaultColor.isValid()) { |
|
466 d->mDefaultColor = HbColorScheme::color(KDefaultColorThemeName); |
|
467 } |
|
468 |
|
469 return d->mDefaultColor; |
|
470 } |
|
471 |
|
472 /*! |
|
473 * Sets color of text. |
|
474 * If invalid color was set color for text is fetch from parent widget. |
|
475 * If invalid color was set and no parent widget was set default foreground color |
|
476 * will be used |
|
477 * |
|
478 * \sa textDefaultColor() |
|
479 */ |
|
480 void HbRichTextItem::setTextDefaultColor(const QColor &color) |
|
481 { |
|
482 Q_D(HbRichTextItem); |
|
483 if (d->mColor != color) { |
|
484 d->mColor = color; |
|
485 update(); |
|
486 } |
|
487 } |
|
488 |
|
489 /*! |
|
490 * Shows (default) or hides text. Size hint remains unchanged (same as when text is visible). |
|
491 */ |
|
492 void HbRichTextItem::setTextVisible(bool isVisible) |
|
493 { |
|
494 Q_D(HbRichTextItem); |
|
495 if( d->mDontPrint == isVisible ) { |
|
496 d->mDontPrint = !isVisible; |
|
497 update(); |
|
498 } |
|
499 } |
|
500 |
|
501 /*! |
|
502 * Returns if text is visible. |
|
503 */ |
|
504 bool HbRichTextItem::isTextVisible() const |
|
505 { |
|
506 Q_D(const HbRichTextItem); |
|
507 return !d->mDontPrint; |
|
508 } |
|
509 |
|
510 /*! |
|
511 * Enables (default) or disables text clipping when item geometry is too small. |
|
512 */ |
|
513 void HbRichTextItem::setTextClip(bool cliping) |
|
514 { |
|
515 Q_D(HbRichTextItem); |
|
516 if( d->mDontClip == cliping ) { |
|
517 prepareGeometryChange(); |
|
518 d->mDontClip = !cliping; |
|
519 setFlag(QGraphicsItem::ItemClipsToShape, cliping); |
|
520 update(); |
|
521 } |
|
522 } |
|
523 |
|
524 /*! |
|
525 * Returns true if text is clipped when item geometry is too small. |
|
526 */ |
|
527 bool HbRichTextItem::isTextClip() const |
|
528 { |
|
529 Q_D(const HbRichTextItem); |
|
530 return !d->mDontClip; |
|
531 } |
|
532 |
|
533 // end of file |