MythTV  master
mythuitext.cpp
Go to the documentation of this file.
1 
2 #include "mythuitext.h"
3 
4 #include <cmath>
5 
6 #include <QCoreApplication>
7 #include <QtGlobal>
8 #include <QDomDocument>
9 #include <QFontMetrics>
10 #include <QString>
11 #include <QHash>
12 
13 #include "mythlogging.h"
14 
15 #include "mythuihelper.h"
16 #include "mythpainter.h"
17 #include "mythmainwindow.h"
18 #include "mythfontproperties.h"
19 #include "mythcorecontext.h"
20 
21 #include "compat.h"
22 
23 MythUIText::MythUIText(MythUIType *parent, const QString &name)
24  : MythUIType(parent, name),
25  m_Font(new MythFontProperties())
26 {
27  m_EnableInitiator = true;
28 
29  m_FontStates.insert("default", MythFontProperties());
30  *m_Font = m_FontStates["default"];
31 }
32 
33 MythUIText::MythUIText(const QString &text, const MythFontProperties &font,
34  QRect displayRect, QRect altDisplayRect,
35  MythUIType *parent, const QString &name)
36  : MythUIType(parent, name),
37  m_OrigDisplayRect(displayRect), m_AltDisplayRect(altDisplayRect),
38  m_Canvas(0, 0, displayRect.width(), displayRect.height()),
39  m_drawRect(displayRect),
40  m_Message(text.trimmed()),
41  m_DefaultMessage(text),
42  m_Font(new MythFontProperties())
43 {
44 #if 0 // Not currently used
45  m_usingAltArea = false;
46 #endif
47  m_ShrinkNarrow = true;
48  m_MultiLine = false;
49 
51  m_scrollPause = 0;
54  m_scrollBounce = false;
55  m_scrollOffset = 0;
56  m_scrollPos = 0;
57  m_scrollPosWhole = 0;
59  m_scrolling = false;
60 
63  m_Leading = 1;
64  m_extraLeading = 0;
65  m_lineHeight = 0;
66  m_textCursor = -1;
67  m_EnableInitiator = true;
68 
69  MythUIText::SetArea(displayRect);
70  m_FontStates.insert("default", font);
71  *m_Font = m_FontStates["default"];
72 }
73 
75 {
76  delete m_Font;
77  m_Font = nullptr;
78 
79  QVector<QTextLayout *>::iterator Ilayout;
80  for (Ilayout = m_Layouts.begin(); Ilayout != m_Layouts.end(); ++Ilayout)
81  delete *Ilayout;
82 }
83 
85 {
87  {
89  SetRedraw();
90  emit DependChanged(true);
91  }
92 
93  SetFontState("default");
94 
96 }
97 
98 void MythUIText::ResetMap(const InfoMap &map)
99 {
100  QString newText = GetTemplateText();
101 
102  if (newText.isEmpty())
103  newText = GetDefaultText();
104 
105  QRegExp regexp("%(([^\\|%]+)?\\||\\|(.))?([\\w#]+)(\\|(.+))?%");
106  regexp.setMinimal(true);
107 
108  bool replaced = map.contains(objectName());
109 
110  if (!replaced && !newText.isEmpty() && newText.contains(regexp))
111  {
112  int pos = 0;
113 
114  QString translatedTemplate = qApp->translate("ThemeUI",
115  newText.toUtf8());
116 
117  while ((pos = regexp.indexIn(translatedTemplate, pos)) != -1)
118  {
119  QString key = regexp.cap(4).toLower().trimmed();
120 
121  if (map.contains(key))
122  {
123  replaced = true;
124  break;
125  }
126  pos += regexp.matchedLength();
127  }
128  }
129 
130  if (replaced)
131  {
132  Reset();
133  }
134 }
135 
136 void MythUIText::SetText(const QString &text)
137 {
138  const QString& newtext = text;
139 
140  if (!m_Layouts.isEmpty() && newtext == m_Message)
141  return;
142 
143  if (newtext.isEmpty())
144  {
146  emit DependChanged(true);
147  }
148  else
149  {
150  m_Message = newtext;
151  emit DependChanged(false);
152  }
153  m_CutMessage.clear();
154  FillCutMessage();
155 
156  SetRedraw();
157 }
158 
160 {
161  QString newText = GetTemplateText();
162 
163  if (newText.isEmpty())
164  newText = GetDefaultText();
165 
166  QRegExp regexp("%(([^\\|%]+)?\\||\\|(.))?([\\w#]+)(\\|(.+))?%");
167  regexp.setMinimal(true);
168 
169  if (!newText.isEmpty() && newText.contains(regexp))
170  {
171  int pos = 0;
172 
173  QString translatedTemplate = qApp->translate("ThemeUI",
174  newText.toUtf8());
175 
176  QString tempString = translatedTemplate;
177  bool replaced = map.contains(objectName());
178 
179  while ((pos = regexp.indexIn(translatedTemplate, pos)) != -1)
180  {
181  QString key = regexp.cap(4).toLower().trimmed();
182  QString replacement;
183 
184  if (map.contains(key))
185  {
186  replaced = true;
187  }
188  if (!map.value(key).isEmpty())
189  {
190  replacement = QString("%1%2%3%4")
191  .arg(regexp.cap(2))
192  .arg(regexp.cap(3))
193  .arg(map.value(key))
194  .arg(regexp.cap(6));
195  }
196 
197  tempString.replace(regexp.cap(0), replacement);
198  pos += regexp.matchedLength();
199  }
200  if (replaced)
201  {
202  SetText(tempString);
203  }
204  }
205  else if (map.contains(objectName()))
206  {
207  SetText(map.value(objectName()));
208  }
209 }
210 
212 {
213  m_FontStates.insert("default", fontProps);
214  if (m_Font->GetHash() != m_FontStates["default"].GetHash())
215  {
216  *m_Font = m_FontStates["default"];
217  if (!m_Message.isEmpty())
218  {
219  FillCutMessage();
220  SetRedraw();
221  }
222  }
223 }
224 
225 void MythUIText::SetFontState(const QString &state)
226 {
227  if (m_FontStates.contains(state))
228  {
229  if (m_Font->GetHash() == m_FontStates[state].GetHash())
230  return;
231  *m_Font = m_FontStates[state];
232  }
233  else
234  {
235  if (m_Font->GetHash() == m_FontStates["default"].GetHash())
236  return;
237  *m_Font = m_FontStates["default"];
238  }
239  if (!m_Message.isEmpty())
240  {
241  FillCutMessage();
242  SetRedraw();
243  }
244 }
245 
246 #if 0 // Not currently used
247 void MythUIText::UseAlternateArea(bool useAlt)
248 {
249  if (useAlt && m_AltDisplayRect.width() > 1)
250  {
252  m_usingAltArea = true;
253  }
254  else
255  {
257  m_usingAltArea = false;
258  }
259 
260  FillCutMessage();
261 }
262 #endif
263 
265 {
266  int h = just & Qt::AlignHorizontal_Mask;
267  int v = just & Qt::AlignVertical_Mask;
268 
269  if ((h && (m_Justification & Qt::AlignHorizontal_Mask) ^ h) ||
270  (v && (m_Justification & Qt::AlignVertical_Mask) ^ v))
271  {
272  // preserve the wordbreak attribute, drop everything else
273  m_Justification = m_Justification & Qt::TextWordWrap;
274  m_Justification |= just;
275  if (!m_Message.isEmpty())
276  {
277  FillCutMessage();
278  SetRedraw();
279  }
280  }
281 }
282 
284 {
285  return m_Justification;
286 }
287 
288 void MythUIText::SetCutDown(Qt::TextElideMode mode)
289 {
290  if (mode != m_Cutdown)
291  {
292  m_Cutdown = mode;
293  if (m_scrolling && m_Cutdown != Qt::ElideNone)
294  {
295  LOG(VB_GENERAL, LOG_ERR, QString("'%1' (%2): <scroll> and "
296  "<cutdown> are not combinable.")
297  .arg(objectName()).arg(GetXMLLocation()));
298  m_Cutdown = Qt::ElideNone;
299  }
300  if (!m_Message.isEmpty())
301  {
302  FillCutMessage();
303  SetRedraw();
304  }
305  }
306 }
307 
308 void MythUIText::SetMultiLine(bool multiline)
309 {
310  if (multiline != m_MultiLine)
311  {
312  m_MultiLine = multiline;
313 
314  if (m_MultiLine)
315  m_Justification |= Qt::TextWordWrap;
316  else
317  m_Justification &= ~Qt::TextWordWrap;
318 
319  if (!m_Message.isEmpty())
320  {
321  FillCutMessage();
322  SetRedraw();
323  }
324  }
325 }
326 
327 void MythUIText::SetArea(const MythRect &rect)
328 {
329  MythUIType::SetArea(rect);
330  m_CutMessage.clear();
331 
332  m_drawRect = m_Area;
333  FillCutMessage();
334 }
335 
337 {
340 }
341 
343 {
344  QPoint newpoint(x, y);
345 
346  if (newpoint == m_Canvas.topLeft())
347  return;
348 
349  m_Canvas.moveTopLeft(newpoint);
350  SetRedraw();
351 }
352 
353 void MythUIText::ShiftCanvas(int x, int y)
354 {
355  if (x == 0 && y == 0)
356  return;
357 
358  m_Canvas.moveTop(m_Canvas.y() + y);
359  m_Canvas.moveLeft(m_Canvas.x() + x);
360  SetRedraw();
361 }
362 
363 void MythUIText::DrawSelf(MythPainter *p, int xoffset, int yoffset,
364  int alphaMod, QRect clipRect)
365 {
366  if (m_Canvas.isEmpty())
367  return;
368 
370  QRect drawrect = m_drawRect.toQRect();
371  drawrect.translate(xoffset, yoffset);
372  QRect canvas = m_Canvas.toQRect();
373 
374  int alpha = CalcAlpha(alphaMod);
375 
376  if (m_Ascent)
377  {
378  drawrect.setY(drawrect.y() - m_Ascent);
379  canvas.moveTop(canvas.y() + m_Ascent);
380  canvas.setHeight(canvas.height() + m_Ascent);
381  }
382  if (m_Descent)
383  {
384  drawrect.setHeight(drawrect.height() + m_Descent);
385  canvas.setHeight(canvas.height() + m_Descent);
386  }
387 
388  if (m_leftBearing)
389  {
390  drawrect.setX(drawrect.x() + m_leftBearing);
391  canvas.moveLeft(canvas.x() - m_leftBearing);
392  canvas.setWidth(canvas.width() - m_leftBearing);
393  }
394  if (m_rightBearing)
395  {
396  drawrect.setWidth(drawrect.width() - m_rightBearing);
397  canvas.setWidth(canvas.width() - m_rightBearing);
398  }
399 
400  if (GetFontProperties()->hasOutline())
401  {
402  QColor outlineColor;
403  int outlineSize, outlineAlpha;
404 
405  GetFontProperties()->GetOutline(outlineColor, outlineSize,
406  outlineAlpha);
407 
408  MythPoint outline(outlineSize, outlineSize);
409 
410 #if QT_VERSION < QT_VERSION_CHECK(5,6,0) // else done in MythUIText::FormatTemplate
411  QTextLayout::FormatRange range;
412 
413  outlineColor.setAlpha(outlineAlpha);
414 
415  outline.NormPoint(); // scale it to screen resolution
416 
417  QPen pen;
418  pen.setBrush(outlineColor);
419  pen.setWidth(outline.x());
420 
421  range.start = 0;
422  range.length = m_CutMessage.size();
423  range.format.setTextOutline(pen);
424  formats.push_back(range);
425 #endif
426 
427  drawrect.setX(drawrect.x() - outline.x());
428  drawrect.setWidth(drawrect.width() + outline.x());
429  drawrect.setY(drawrect.y() - outline.y());
430  drawrect.setHeight(drawrect.height() + outline.y());
431 
432  /* Canvas pos is where the view port (drawrect) pulls from, so
433  * it needs moved to the right for the left edge to be picked up*/
434  canvas.moveLeft(canvas.x() + outline.x());
435  canvas.setWidth(canvas.width() + outline.x());
436  canvas.moveTop(canvas.y() + outline.y());
437  canvas.setHeight(canvas.height() + outline.y());
438  }
439 
440  if (GetFontProperties()->hasShadow())
441  {
442  QPoint shadowOffset;
443  QColor shadowColor;
444  int shadowAlpha;
445 
446  GetFontProperties()->GetShadow(shadowOffset, shadowColor, shadowAlpha);
447 
448  MythPoint shadow(shadowOffset);
449  shadow.NormPoint(); // scale it to screen resolution
450 
451  drawrect.setWidth(drawrect.width() + shadow.x());
452  drawrect.setHeight(drawrect.height() + shadow.y());
453 
454  canvas.setWidth(canvas.width() + shadow.x());
455  canvas.setHeight(canvas.height() + shadow.y());
456  }
457 
458  p->SetClipRect(clipRect);
459  p->DrawTextLayout(canvas, m_Layouts, formats,
460  *GetFontProperties(), alpha, drawrect);
461 }
462 
463 bool MythUIText::FormatTemplate(QString & paragraph, QTextLayout *layout)
464 {
465 #if QT_VERSION >= QT_VERSION_CHECK(5,6,0)
466  layout->clearFormats();
467 #endif
468 
470  QTextLayout::FormatRange range;
471  QString fontname;
472  bool res = false; // Return true if paragraph changed.
473 
474 #if QT_VERSION >= QT_VERSION_CHECK(5,6,0) // else done in DrawSelf
475  if (GetFontProperties()->hasOutline())
476  {
477  int outlineSize, outlineAlpha;
478  QColor outlineColor;
479 
480  GetFontProperties()->GetOutline(outlineColor, outlineSize,
481  outlineAlpha);
482 
483  outlineColor.setAlpha(outlineAlpha);
484 
485  MythPoint outline(outlineSize, outlineSize);
486  outline.NormPoint(); // scale it to screen resolution
487 
488  QPen pen;
489  pen.setBrush(outlineColor);
490  pen.setWidth(outline.x());
491 
492  range.start = 0;
493  range.length = paragraph.size();
494  range.format.setTextOutline(pen);
495  formats.push_back(range);
496  }
497 #endif
498 
499  range.start = 0;
500  range.length = 0;
501 
502  int pos = 0, end = 0;
503  while ((pos = paragraph.indexOf("[font]", pos, Qt::CaseInsensitive)) != -1)
504  {
505  if ((end = paragraph.indexOf("[/font]", pos + 1, Qt::CaseInsensitive))
506  != -1)
507  {
508  if (range.length == -1)
509  {
510  // End of the affected text
511  range.length = pos - range.start;
512  if (range.length > 0)
513  {
514  formats.push_back(range);
515  LOG(VB_GUI, LOG_DEBUG,
516  QString("'%1' Setting \"%2\" with FONT %3")
517  .arg(objectName())
518  .arg(paragraph.mid(range.start, range.length))
519  .arg(fontname));
520  }
521  range.length = 0;
522  }
523 
524  int len = end - pos - 6;
525  fontname = paragraph.mid(pos + 6, len);
526 
527  if (GetGlobalFontMap()->Contains(fontname))
528  {
529  MythFontProperties *fnt = GetGlobalFontMap()->GetFont(fontname);
530  range.start = pos;
531  range.length = -1; // Need to find the end of the effect
532  range.format.setFont(fnt->face());
533  range.format.setFontStyleHint(QFont::SansSerif,
534  QFont::OpenGLCompatible);
535  range.format.setForeground(fnt->GetBrush());
536  }
537  else
538  {
539  LOG(VB_GUI, LOG_ERR,
540  QString("'%1' Unknown Font '%2' specified in template.")
541  .arg(objectName())
542  .arg(fontname));
543  }
544 
545  LOG(VB_GUI, LOG_DEBUG, QString("Removing %1 through %2 '%3'")
546  .arg(pos).arg(end + 7 - pos).arg(paragraph.mid(pos,
547  end + 7 - pos)));
548  paragraph.remove(pos, end + 7 - pos);
549  res = true;
550  }
551  else
552  {
553  LOG(VB_GUI, LOG_ERR,
554  QString("'%1' Non-terminated [font] found in template")
555  .arg(objectName()));
556  break;
557  }
558  }
559 
560  if (range.length == -1) // To the end
561  {
562  range.length = paragraph.length() - range.start;
563  formats.push_back(range);
564  LOG(VB_GUI, LOG_DEBUG,
565  QString("'%1' Setting \"%2\" with FONT %3")
566  .arg(objectName())
567  .arg(paragraph.mid(range.start, range.length))
568  .arg(fontname));
569  }
570 
571 #if QT_VERSION >= QT_VERSION_CHECK(5,6,0)
572  if (!formats.empty())
573  layout->setFormats(formats);
574 #endif
575 
576  return res;
577 }
578 
579 bool MythUIText::Layout(QString & paragraph, QTextLayout *layout, bool final,
580  bool & overflow, qreal width, qreal & height,
581  bool force, qreal & last_line_width,
582  QRectF & min_rect, int & num_lines)
583 {
584  int last_line = 0;
585 
586  FormatTemplate(paragraph, layout);
587  layout->setText(paragraph);
588  layout->beginLayout();
589  num_lines = 0;
590  for (;;)
591  {
592  QTextLine line = layout->createLine();
593  if (!line.isValid())
594  break;
595 
596  // Try "visible" width first, so alignment works
597  line.setLineWidth(width);
598 
599  if (!m_MultiLine && line.textLength() < paragraph.size())
600  {
601  if (!force && m_Cutdown != Qt::ElideNone)
602  {
603  QFontMetrics fm(GetFontProperties()->face());
604  paragraph = fm.elidedText(paragraph, m_Cutdown,
605  width - fm.averageCharWidth());
606  return false;
607  }
608  // If text does not fit, then expand so canvas size is correct
609  line.setLineWidth(INT_MAX);
610  }
611 
612  height += m_Leading;
613  line.setPosition(QPointF(0, height));
614  height += m_lineHeight;
615  if (!overflow)
616  {
617  if (height > m_Area.height())
618  {
619  LOG(VB_GUI, num_lines ? LOG_DEBUG : LOG_NOTICE,
620  QString("'%1' (%2): height overflow. line height %3 "
621  "paragraph height %4, area height %5")
622  .arg(objectName())
623  .arg(GetXMLLocation())
624  .arg(line.height())
625  .arg(height)
626  .arg(m_Area.height()));
627 
628  if (!m_MultiLine)
629  m_drawRect.setHeight(height);
630  if (m_Cutdown != Qt::ElideNone)
631  {
632  QFontMetrics fm(GetFontProperties()->face());
633  QString cut_line = fm.elidedText
634  (paragraph.mid(last_line),
635  Qt::ElideRight,
636  width - fm.averageCharWidth());
637  paragraph = paragraph.left(last_line) + cut_line;
638  if (last_line == 0)
639  min_rect |= line.naturalTextRect();
640  return false;
641  }
642  overflow = true;
643  }
644  else
645  m_drawRect.setHeight(height);
646  if (!m_MultiLine)
647  overflow = true;
648  }
649 
650  last_line = line.textStart();
651  last_line_width = line.naturalTextWidth();
652  min_rect |= line.naturalTextRect();
653  ++num_lines;
654 
655  if (final && line.textLength())
656  {
665  QFontMetrics fm(GetFontProperties()->face());
666  int bearing;
667 
668  bearing = fm.leftBearing(m_CutMessage[last_line]);
669  if (m_leftBearing > bearing)
670  m_leftBearing = bearing;
671  bearing = fm.rightBearing
672  (m_CutMessage[last_line + line.textLength() - 1]);
673  if (m_rightBearing > bearing)
674  m_rightBearing = bearing;
675  }
676  }
677 
678  layout->endLayout();
679  return true;
680 }
681 
682 bool MythUIText::LayoutParagraphs(const QStringList & paragraphs,
683  const QTextOption & textoption,
684  qreal width, qreal & height,
685  QRectF & min_rect, qreal & last_line_width,
686  int & num_lines, bool final)
687 {
688  QStringList::const_iterator Ipara;
689  QVector<QTextLayout *>::iterator Ilayout;
690  QTextLayout *layout;
691  QString para;
692  bool overflow = false;
693  qreal saved_height;
694  QRectF saved_rect;
695  int idx;
696 
697  for (Ilayout = m_Layouts.begin(); Ilayout != m_Layouts.end(); ++Ilayout)
698  (*Ilayout)->clearLayout();
699 
700  for (Ipara = paragraphs.begin(), idx = 0;
701  Ipara != paragraphs.end(); ++Ipara, ++idx)
702  {
703  layout = m_Layouts[idx];
704  layout->setTextOption(textoption);
705  layout->setFont(m_Font->face());
706 
707  para = *Ipara;
708  saved_height = height;
709  saved_rect = min_rect;
710  if (!Layout(para, layout, final, overflow, width, height, false,
711  last_line_width, min_rect, num_lines))
712  {
713  // Again, with cut down
714  min_rect = saved_rect;
715  height = saved_height;
716  Layout(para, layout, final, overflow, width, height, true,
717  last_line_width, min_rect, num_lines);
718  break;
719  }
720  }
721  m_drawRect.setWidth(width);
722 
723  return (!overflow);
724 }
725 
726 bool MythUIText::GetNarrowWidth(const QStringList & paragraphs,
727  const QTextOption & textoption, qreal & width)
728 {
729  qreal height, last_line_width, lines;
730  int best_width, too_narrow, last_width = -1;
731  int num_lines = 0;
732  int line_height = 0;
733  int attempt = 0;
734  Qt::TextElideMode cutdown = m_Cutdown;
735  m_Cutdown = Qt::ElideNone;
736 
737  line_height = m_Leading + m_lineHeight;
738  width = m_Area.width() / 2.0;
739  best_width = m_Area.width();
740  too_narrow = 0;
741 
742  for (attempt = 0; attempt < 10; ++attempt)
743  {
744  QRectF min_rect;
745 
746  m_drawRect.setWidth(0);
747  height = 0;
748 
749  LayoutParagraphs(paragraphs, textoption, width, height,
750  min_rect, last_line_width, num_lines, false);
751 
752  if (num_lines <= 0)
753  return false;
754 
755  if (height > m_drawRect.height())
756  {
757  if (too_narrow < width)
758  too_narrow = width;
759 
760  // Too narrow? How many lines didn't fit?
761  lines = static_cast<int>
762  ((height - m_drawRect.height()) / line_height);
763  lines -= (1.0 - last_line_width / width);
764  width += (lines * width) /
765  (m_drawRect.height() / line_height);
766 
767  if (width > best_width || static_cast<int>(width) == last_width)
768  {
769  width = best_width;
770  m_Cutdown = cutdown;
771  return true;
772  }
773  }
774  else
775  {
776  if (best_width > width)
777  best_width = width;
778 
779  lines = static_cast<int>
780  (m_Area.height() - height) / line_height;
781  if (lines >= 1)
782  {
783  // Too wide?
784  width -= width * (lines / num_lines - 1 + lines);
785  if (static_cast<int>(width) == last_width)
786  {
787  m_Cutdown = cutdown;
788  return true;
789  }
790  }
791  else if (last_line_width < m_Area.width())
792  {
793  // Is the last line fully used?
794  width -= (1.0 - last_line_width / width) / num_lines;
795  if (width > last_line_width)
796  width = last_line_width;
797  if (static_cast<int>(width) == last_width)
798  {
799  m_Cutdown = cutdown;
800  return true;
801  }
802  }
803  if (width < too_narrow)
804  width = too_narrow;
805  }
806  last_width = width;
807  }
808 
809  LOG(VB_GENERAL, LOG_ERR, QString("'%1' (%2) GetNarrowWidth: Gave up "
810  "while trying to find optimal width "
811  "for '%3'.")
812  .arg(objectName()).arg(GetXMLLocation()).arg(m_CutMessage));
813 
814  width = best_width;
815  m_Cutdown = cutdown;
816  return false;
817 }
818 
820 {
821  if (m_Area.isNull())
822  return;
823 
824  qreal width, height;
825  QRectF min_rect;
826  QFontMetrics fm(GetFontProperties()->face());
827 
828  m_lineHeight = fm.height();
829  m_Leading = m_MultiLine ? fm.leading() + m_extraLeading : m_extraLeading;
830  m_CutMessage.clear();
831  m_textCursor = -1;
832 
834  {
835  bool isNumber;
836  int value = m_Message.toInt(&isNumber);
837 
838  if (isNumber && m_TemplateText.contains("%n"))
839  {
840  m_CutMessage = qApp->translate("ThemeUI",
841  m_TemplateText.toUtf8(), nullptr,
842  qAbs(value));
843  }
844  else if (m_TemplateText.contains("%1"))
845  {
846  QString tmp = qApp->translate("ThemeUI", m_TemplateText.toUtf8());
847  m_CutMessage = tmp.arg(m_Message);
848  }
849  }
850 
851  if (m_CutMessage.isEmpty())
853 
854  if (m_CutMessage.isEmpty())
855  {
856  if (m_Layouts.empty())
857  m_Layouts.push_back(new QTextLayout);
858 
859  QTextLine line;
860  QTextOption textoption(static_cast<Qt::Alignment>(m_Justification));
861  QVector<QTextLayout *>::iterator Ilayout = m_Layouts.begin();
862 
863  (*Ilayout)->setTextOption(textoption);
864  (*Ilayout)->setText("");
865  (*Ilayout)->beginLayout();
866  line = (*Ilayout)->createLine();
867  line.setLineWidth(m_Area.width());
868  line.setPosition(QPointF(0, 0));
869  (*Ilayout)->endLayout();
870  m_drawRect.setWidth(m_Area.width());
872 
873  for (++Ilayout ; Ilayout != m_Layouts.end(); ++Ilayout)
874  (*Ilayout)->clearLayout();
875 
877  }
878  else
879  {
880  QStringList templist;
881  QStringList::iterator it;
882 
883  switch (m_textCase)
884  {
885  case CaseUpper :
886  m_CutMessage = m_CutMessage.toUpper();
887  break;
888  case CaseLower :
889  m_CutMessage = m_CutMessage.toLower();
890  break;
891  case CaseCapitaliseFirst :
892  //m_CutMessage = m_CutMessage.toLower();
893  templist = m_CutMessage.split(". ");
894 
895  for (it = templist.begin(); it != templist.end(); ++it)
896  (*it).replace(0, 1, (*it).left(1).toUpper());
897 
898  m_CutMessage = templist.join(". ");
899  break;
900  case CaseCapitaliseAll :
901  //m_CutMessage = m_CutMessage.toLower();
902  templist = m_CutMessage.split(" ");
903 
904  for (it = templist.begin(); it != templist.end(); ++it)
905  (*it).replace(0, 1, (*it).left(1).toUpper());
906 
907  m_CutMessage = templist.join(" ");
908  break;
909  case CaseNormal:
910  break;
911  }
912 
913  QTextOption textoption(static_cast<Qt::Alignment>(m_Justification));
914  textoption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
915 
916  int idx, num_lines;
917  qreal last_line_width;
918  QStringList paragraphs = m_CutMessage.split('\n',
919  QString::KeepEmptyParts);
920 
921  for (idx = m_Layouts.size(); idx < paragraphs.size(); ++idx)
922  m_Layouts.push_back(new QTextLayout);
923 
924  if (m_MultiLine && m_ShrinkNarrow &&
925  m_MinSize.isValid() && !m_CutMessage.isEmpty())
926  GetNarrowWidth(paragraphs, textoption, width);
927  else
928  width = m_Area.width();
929 
930  height = 0;
932  LayoutParagraphs(paragraphs, textoption, width, height,
933  min_rect, last_line_width, num_lines, true);
934 
935  m_Canvas.setRect(0, 0, min_rect.x() + min_rect.width(), height);
937  m_scrollBounce = false;
938 
945  QRect actual = fm.boundingRect(m_CutMessage);
946  m_Ascent = -(actual.y() + fm.ascent());
947  m_Descent = actual.height() - fm.height();
948  }
949 
950  if (m_scrolling)
951  {
952  if (m_scrollDirection == ScrollLeft ||
955  {
956  if (m_Canvas.width() > m_drawRect.width())
957  {
958  m_drawRect.setX(m_Area.x());
959  m_drawRect.setWidth(m_Area.width());
960  m_scrollOffset = m_drawRect.x() - m_Canvas.x();
961  }
962  }
963  else
964  {
965  if (m_Canvas.height() > m_drawRect.height())
966  {
967  m_drawRect.setY(m_Area.y());
968  m_drawRect.setHeight(m_Area.height());
969  m_scrollOffset = m_drawRect.y() - m_Canvas.y();
970  }
971  }
972  }
973 
974  // If any of hcenter|vcenter|Justify, center it all, then adjust
975  if ((m_Justification & (Qt::AlignCenter|Qt::AlignJustify)) != 0U)
976  {
977  m_drawRect.moveCenter(m_Area.center());
978  min_rect.moveCenter(m_Area.center());
979  }
980 
981  // Adjust horizontal
982  if (m_Justification & Qt::AlignLeft)
983  {
984  // If text size is less than allowed min size, center it
985  if (m_ShrinkNarrow && m_MinSize.isValid() && min_rect.isValid() &&
986  min_rect.width() < m_MinSize.x())
987  {
989  (((m_MinSize.x() - min_rect.width() +
990  fm.averageCharWidth()) / 2)));
991  min_rect.setWidth(m_MinSize.x());
992  }
993  else
995 
996  min_rect.moveLeft(m_Area.x());
997  }
998  else if (m_Justification & Qt::AlignRight)
999  {
1000  // If text size is less than allowed min size, center it
1001  if (m_ShrinkNarrow && m_MinSize.isValid() && min_rect.isValid() &&
1002  min_rect.width() < m_MinSize.x())
1003  {
1004  m_drawRect.moveRight(m_Area.x() + m_Area.width() -
1005  (((m_MinSize.x() - min_rect.width() +
1006  fm.averageCharWidth()) / 2)));
1007  min_rect.setWidth(m_MinSize.x());
1008  }
1009  else
1010  m_drawRect.moveRight(m_Area.x() + m_Area.width());
1011 
1012  min_rect.moveRight(m_Area.x() + m_Area.width());
1013  }
1014 
1015  // Adjust vertical
1016  if (m_Justification & Qt::AlignTop)
1017  {
1018  // If text size is less than allowed min size, center it
1019  if (!m_ShrinkNarrow && m_MinSize.isValid() && min_rect.isValid() &&
1020  min_rect.height() < m_MinSize.y())
1021  {
1022  m_drawRect.moveTop(m_Area.y() +
1023  ((m_MinSize.y() - min_rect.height()) / 2));
1024  min_rect.setHeight(m_MinSize.y());
1025  }
1026  else
1027  m_drawRect.moveTop(m_Area.y());
1028 
1029  min_rect.moveTop(m_Area.y());
1030  }
1031  else if (m_Justification & Qt::AlignBottom)
1032  {
1033  // If text size is less than allowed min size, center it
1034  if (!m_ShrinkNarrow && m_MinSize.isValid() && min_rect.isValid() &&
1035  min_rect.height() < m_MinSize.y())
1036  {
1037  m_drawRect.moveBottom(m_Area.y() + m_Area.height() -
1038  ((m_MinSize.y() - min_rect.height()) / 2));
1039  min_rect.setHeight(m_MinSize.y());
1040  }
1041  else
1042  m_drawRect.moveBottom(m_Area.y() + m_Area.height());
1043 
1044  min_rect.moveBottom(m_Area.y() + m_Area.height());
1045  }
1046 
1048  if (m_MinSize.isValid())
1049  {
1050  // Record the minimal area needed for the message.
1051  SetMinArea(min_rect.toRect());
1052  }
1053 }
1054 
1056 {
1057  int lineNo = -1;
1058  int lineCount = 0;
1059  int currPos = 0;
1060  int layoutStartPos = 0;
1061  int xPos;
1062 
1063  for (int x = 0; x < m_Layouts.count(); x++)
1064  {
1065  QTextLayout *layout = m_Layouts.at(x);
1066 
1067  for (int y = 0; y < layout->lineCount(); y++)
1068  {
1069  lineCount++;
1070  if (lineNo != -1)
1071  continue;
1072 
1073  QTextLine line = layout->lineAt(y);
1074 
1075  if (m_textCursor >= currPos && m_textCursor < currPos + line.textLength()
1076  + (line.lineNumber() == layout->lineCount() - 1 ? 1 : 0))
1077  {
1078  lineNo = lineCount - 1;
1079  xPos = line.cursorToX(m_textCursor - layoutStartPos);
1080  continue;
1081  }
1082 
1083  currPos += line.textLength();
1084  }
1085 
1086  currPos += 1; // skip the newline
1087  layoutStartPos = currPos;
1088  }
1089 
1090  // are we are at the top and need to scroll up
1091  if (lineNo == 0 && lines < 0)
1092  return -1;
1093 
1094  // are we at the bottom and need to scroll down
1095  if (lineNo == lineCount - 1 && lines > 0)
1096  return -1;
1097 
1098  if (lineNo == -1)
1099  {
1100  LOG(VB_GENERAL, LOG_ERR,
1101  QString("'%1' (%2) MoveCursor offset %3 not found in ANY paragraph!")
1102  .arg(objectName()).arg(GetXMLLocation()).arg(m_textCursor));
1103  return m_textCursor;
1104  }
1105 
1106  int newLine = lineNo + lines;
1107 
1108  if (newLine < 0)
1109  newLine = 0;
1110 
1111  if (newLine >= lineCount)
1112  newLine = lineCount - 1;
1113 
1114  lineNo = -1;
1115  layoutStartPos = 0;
1116 
1117  for (int x = 0; x < m_Layouts.count(); x++)
1118  {
1119  QTextLayout *layout = m_Layouts.at(x);
1120 
1121  for (int y = 0; y < layout->lineCount(); y++)
1122  {
1123  lineNo++;
1124  QTextLine line = layout->lineAt(y);
1125 
1126  if (lineNo == newLine)
1127  return layoutStartPos + line.xToCursor(xPos);
1128  }
1129 
1130  layoutStartPos += layout->text().length() + 1;
1131  }
1132 
1133  // should never reach here
1134  return m_textCursor;
1135 }
1136 
1137 QPoint MythUIText::CursorPosition(int text_offset)
1138 {
1139  if (m_Layouts.empty())
1140  return m_Area.topLeft().toQPoint();
1141 
1142  if (text_offset == m_textCursor)
1143  return m_cursorPos;
1144  m_textCursor = text_offset;
1145 
1146  QVector<QTextLayout *>::const_iterator Ipara;
1147  QPoint pos;
1148  int x, y, mid, line_height;
1149  int offset = text_offset;
1150 
1151  for (Ipara = m_Layouts.constBegin(); Ipara != m_Layouts.constEnd(); ++Ipara)
1152  {
1153  QTextLine line = (*Ipara)->lineForTextPosition(offset);
1154 
1155  if (line.isValid())
1156  {
1157  pos.setX(line.cursorToX(&offset));
1158  pos.setY(line.y());
1159  break;
1160  }
1161  offset -= ((*Ipara)->text().size() + 1); // Account for \n
1162  }
1163  if (Ipara == m_Layouts.constEnd())
1164  {
1165  LOG(VB_GENERAL, LOG_ERR,
1166  QString("'%1' (%2) CursorPosition offset %3 not found in "
1167  "ANY paragraph!")
1168  .arg(objectName()).arg(GetXMLLocation()).arg(text_offset));
1169  return m_Area.topLeft().toQPoint();
1170  }
1171 
1172  mid = m_drawRect.width() / 2;
1173  if (m_Canvas.width() <= m_drawRect.width() || pos.x() <= mid)
1174  x = 0; // start
1175  else if (pos.x() >= m_Canvas.width() - mid) // end
1176  {
1177  x = m_Canvas.width() - m_drawRect.width();
1178  pos.setX(pos.x() - x);
1179  }
1180  else // middle
1181  {
1182  x = pos.x() - mid;
1183  pos.setX(pos.x() - x);
1184  }
1185 
1186  line_height = m_lineHeight + m_Leading;
1187  mid = m_Area.height() / 2;
1188  mid -= (mid % line_height);
1189  y = pos.y() - mid;
1190 
1191  if (y <= 0 || m_Canvas.height() <= m_Area.height()) // Top of buffer
1192  y = 0;
1193  else if (y + m_Area.height() > m_Canvas.height()) // Bottom of buffer
1194  {
1195  int visible_lines = ((m_Area.height() / line_height) * line_height);
1196  y = m_Canvas.height() - visible_lines;
1197  pos.setY(visible_lines - (m_Canvas.height() - pos.y()));
1198  }
1199  else // somewhere in the middle
1200  {
1201  y -= m_Leading;
1202  pos.setY(mid + m_Leading);
1203  }
1204 
1205  m_Canvas.moveTopLeft(QPoint(-x, -y));
1206 
1207  // Compensate for vertical alignment
1208  pos.setY(pos.y() + m_drawRect.y() - m_Area.y());
1209 
1210  pos += m_Area.topLeft().toQPoint();
1211  m_cursorPos = pos;
1212 
1213  return pos;
1214 }
1215 
1217 {
1218  //
1219  // Call out base class pulse which will handle any alpha cycling and
1220  // movement
1221  //
1222 
1224 
1225  if (m_colorCycling)
1226  {
1227  m_curR += m_incR;
1228  m_curG += m_incG;
1229  m_curB += m_incB;
1230 
1231  m_curStep++;
1232 
1233  if (m_curStep >= m_numSteps)
1234  {
1235  m_curStep = 0;
1236  m_incR *= -1;
1237  m_incG *= -1;
1238  m_incB *= -1;
1239  }
1240 
1241  QColor newColor = QColor((int)m_curR, (int)m_curG, (int)m_curB);
1242 
1243  if (newColor != m_Font->color())
1244  {
1245  m_Font->SetColor(newColor);
1246  SetRedraw();
1247  }
1248  }
1249 
1250  if (m_scrolling)
1251  {
1252  int whole;
1253 
1254  if (m_scrollPause > 0)
1255  --m_scrollPause;
1256  else
1257  {
1258  if (m_scrollBounce)
1260  else
1262  }
1263 
1264  whole = static_cast<int>(m_scrollPos);
1265  if (m_scrollPosWhole != whole)
1266  {
1267  int shift = whole - m_scrollPosWhole;
1268  m_scrollPosWhole = whole;
1269 
1270  switch (m_scrollDirection)
1271  {
1272  case ScrollLeft :
1273  if (m_Canvas.width() > m_drawRect.width())
1274  {
1275  ShiftCanvas(-shift, 0);
1276  if (m_Canvas.x() + m_Canvas.width() < 0)
1277  {
1278  SetCanvasPosition(m_drawRect.width(), 0);
1280  }
1281  }
1282  break;
1283  case ScrollRight :
1284  if (m_Canvas.width() > m_drawRect.width())
1285  {
1286  ShiftCanvas(shift, 0);
1287  if (m_Canvas.x() > m_drawRect.width())
1288  {
1289  SetCanvasPosition(-m_Canvas.width(), 0);
1291  }
1292  }
1293  break;
1294  case ScrollHorizontal:
1295  if (m_Canvas.width() <= m_drawRect.width())
1296  break;
1297  if (m_scrollBounce) // scroll right
1298  {
1299  if (m_Canvas.x() + m_scrollOffset > m_drawRect.x())
1300  {
1301  m_scrollBounce = false;
1304  }
1305  else
1306  ShiftCanvas(shift, 0);
1307  }
1308  else // scroll left
1309  {
1310  if (m_Canvas.x() + m_Canvas.width() + m_scrollOffset <
1311  m_drawRect.x() + m_drawRect.width())
1312  {
1313  m_scrollBounce = true;
1316  }
1317  else
1318  ShiftCanvas(-shift, 0);
1319  }
1320  break;
1321  case ScrollUp :
1322  if (m_Canvas.height() > m_drawRect.height())
1323  {
1324  ShiftCanvas(0, -shift);
1325  if (m_Canvas.y() + m_Canvas.height() < 0)
1326  {
1327  SetCanvasPosition(0, m_drawRect.height());
1329  }
1330  }
1331  break;
1332  case ScrollDown :
1333  if (m_Canvas.height() > m_drawRect.height())
1334  {
1335  ShiftCanvas(0, shift);
1336  if (m_Canvas.y() > m_drawRect.height())
1337  {
1338  SetCanvasPosition(0, -m_Canvas.height());
1340  }
1341  }
1342  break;
1343  case ScrollVertical:
1344  if (m_Canvas.height() <= m_drawRect.height())
1345  break;
1346  if (m_scrollBounce) // scroll down
1347  {
1348  if (m_Canvas.y() + m_scrollOffset > m_drawRect.y())
1349  {
1350  m_scrollBounce = false;
1353  }
1354  else
1355  ShiftCanvas(0, shift);
1356  }
1357  else // scroll up
1358  {
1359  if (m_Canvas.y() + m_Canvas.height() + m_scrollOffset <
1360  m_drawRect.y() + m_drawRect.height())
1361  {
1362  m_scrollBounce = true;
1365  }
1366  else
1367  ShiftCanvas(0, -shift);
1368  }
1369  break;
1370  case ScrollNone:
1371  break;
1372  }
1373  }
1374  }
1375 }
1376 
1377 void MythUIText::CycleColor(const QColor& startColor, const QColor& endColor, int numSteps)
1378 {
1379  if (!GetPainter()->SupportsAnimation())
1380  return;
1381 
1382  m_startColor = startColor;
1383  m_endColor = endColor;
1384  m_numSteps = numSteps;
1385  m_curStep = 0;
1386 
1387  m_curR = startColor.red();
1388  m_curG = startColor.green();
1389  m_curB = startColor.blue();
1390 
1391  m_incR = (endColor.red() * 1.0F - m_curR) / m_numSteps;
1392  m_incG = (endColor.green() * 1.0F - m_curG) / m_numSteps;
1393  m_incB = (endColor.blue() * 1.0F - m_curB) / m_numSteps;
1394 
1395  m_colorCycling = true;
1396 }
1397 
1399 {
1400  if (!m_colorCycling)
1401  return;
1402 
1404  m_colorCycling = false;
1405  SetRedraw();
1406 }
1407 
1409  const QString &filename, QDomElement &element, bool showWarnings)
1410 {
1411  if (element.tagName() == "area")
1412  {
1413  SetArea(parseRect(element));
1415  }
1416  // else if (element.tagName() == "altarea") // Unused, but maybe in future?
1417  // m_AltDisplayRect = parseRect(element);
1418  else if (element.tagName() == "font")
1419  {
1420  QString fontname = getFirstText(element);
1421  MythFontProperties *fp = GetFont(fontname);
1422 
1423  if (!fp)
1424  fp = GetGlobalFontMap()->GetFont(fontname);
1425 
1426  if (fp)
1427  {
1428  MythFontProperties font = *fp;
1429  int screenHeight = GetMythMainWindow()->GetUIScreenRect().height();
1430  font.Rescale(screenHeight);
1431  int fontStretch = GetMythUI()->GetFontStretch();
1432  font.AdjustStretch(fontStretch);
1433  QString state = element.attribute("state", "");
1434 
1435  if (!state.isEmpty())
1436  {
1437  m_FontStates.insert(state, font);
1438  }
1439  else
1440  {
1441  m_FontStates.insert("default", font);
1442  *m_Font = m_FontStates["default"];
1443  }
1444  }
1445  }
1446  else if (element.tagName() == "extraleading")
1447  {
1448  m_extraLeading = getFirstText(element).toInt();
1449  }
1450  else if (element.tagName() == "value")
1451  {
1452  if (element.attribute("lang", "").isEmpty())
1453  {
1454  m_Message = qApp->translate("ThemeUI",
1455  parseText(element).toUtf8());
1456  }
1457  else if (element.attribute("lang", "").toLower() ==
1459  {
1460  m_Message = parseText(element);
1461  }
1462  else if (element.attribute("lang", "").toLower() ==
1464  {
1465  m_Message = parseText(element);
1466  }
1467 
1469  SetText(m_Message);
1470  }
1471  else if (element.tagName() == "template")
1472  {
1473  m_TemplateText = parseText(element);
1474  }
1475  else if (element.tagName() == "cutdown")
1476  {
1477  QString mode = getFirstText(element).toLower();
1478 
1479  if (mode == "left")
1480  SetCutDown(Qt::ElideLeft);
1481  else if (mode == "middle")
1482  SetCutDown(Qt::ElideMiddle);
1483  else if (mode == "right" || parseBool(element))
1484  SetCutDown(Qt::ElideRight);
1485  else
1486  SetCutDown(Qt::ElideNone);
1487  }
1488  else if (element.tagName() == "multiline")
1489  {
1490  SetMultiLine(parseBool(element));
1491  }
1492  else if (element.tagName() == "align")
1493  {
1494  QString align = getFirstText(element).toLower();
1496  }
1497  else if (element.tagName() == "colorcycle")
1498  {
1499  if (GetPainter()->SupportsAnimation())
1500  {
1501  QString tmp = element.attribute("start");
1502 
1503  if (!tmp.isEmpty())
1504  m_startColor = QColor(tmp);
1505 
1506  tmp = element.attribute("end");
1507 
1508  if (!tmp.isEmpty())
1509  m_endColor = QColor(tmp);
1510 
1511  tmp = element.attribute("steps");
1512 
1513  if (!tmp.isEmpty())
1514  m_numSteps = tmp.toInt();
1515 
1516  // initialize the rest of the stuff
1518  }
1519  else
1520  m_colorCycling = false;
1521 
1522  m_colorCycling = parseBool(element.attribute("disable"));
1523  }
1524  else if (element.tagName() == "scroll")
1525  {
1526  if (GetPainter()->SupportsAnimation())
1527  {
1528  QString tmp = element.attribute("direction");
1529 
1530  if (!tmp.isEmpty())
1531  {
1532  tmp = tmp.toLower();
1533 
1534  if (tmp == "left")
1536  else if (tmp == "right")
1538  else if (tmp == "up")
1540  else if (tmp == "down")
1542  else if (tmp == "horizontal")
1544  else if (tmp == "vertical")
1546  else
1547  {
1549  LOG(VB_GENERAL, LOG_ERR,
1550  QString("'%1' (%2) Invalid scroll attribute")
1551  .arg(objectName()).arg(GetXMLLocation()));
1552  }
1553  }
1554 
1555  tmp = element.attribute("startdelay");
1556  if (!tmp.isEmpty())
1557  {
1558  float seconds = tmp.toFloat();
1559  m_scrollStartDelay = lroundf(seconds *
1560  static_cast<float>(MythMainWindow::drawRefresh));
1561  }
1562  tmp = element.attribute("returndelay");
1563  if (!tmp.isEmpty())
1564  {
1565  float seconds = tmp.toFloat();
1566  m_scrollReturnDelay = lroundf(seconds *
1567  static_cast<float>(MythMainWindow::drawRefresh));
1568  }
1569  tmp = element.attribute("rate");
1570  if (!tmp.isEmpty())
1571  {
1572 #if 0 // scroll rate as a percentage of 70Hz
1573  float percent = tmp.toFloat() / 100.0;
1574  m_scrollForwardRate = percent *
1575  static_cast<float>(MythMainWindow::drawRefresh) / 70.0;
1576 #else // scroll rate as pixels per second
1577  int pixels = tmp.toInt();
1579  pixels / static_cast<float>(MythMainWindow::drawRefresh);
1580 #endif
1581  }
1582  tmp = element.attribute("returnrate");
1583  if (!tmp.isEmpty())
1584  {
1585 #if 0 // scroll rate as a percentage of 70Hz
1586  float percent = tmp.toFloat() / 100.0;
1587  m_scrollReturnRate = percent *
1588  static_cast<float>(MythMainWindow::drawRefresh) / 70.0;
1589 #else // scroll rate as pixels per second
1590  int pixels = tmp.toInt();
1592  pixels / static_cast<float>(MythMainWindow::drawRefresh);
1593 #endif
1594  }
1595 
1596  m_scrolling = true;
1597  }
1598  else
1599  m_scrolling = false;
1600  }
1601  else if (element.tagName() == "case")
1602  {
1603  QString stringCase = getFirstText(element).toLower();
1604 
1605  if (stringCase == "lower")
1607  else if (stringCase == "upper")
1609  else if (stringCase == "capitalisefirst")
1611  else if (stringCase == "capitaliseall")
1613  else
1615  }
1616  else
1617  {
1618  if (element.tagName() == "minsize" && element.hasAttribute("shrink"))
1619  {
1620  m_ShrinkNarrow = (element.attribute("shrink")
1621  .toLower() != "short");
1622  }
1623 
1624  return MythUIType::ParseElement(filename, element, showWarnings);
1625  }
1626 
1627  return true;
1628 }
1629 
1631 {
1632  MythUIText *text = dynamic_cast<MythUIText *>(base);
1633 
1634  if (!text)
1635  {
1636  LOG(VB_GENERAL, LOG_ERR,
1637  QString("'%1' (%2) ERROR, bad parsing '%3' (%4)")
1638  .arg(objectName()).arg(GetXMLLocation())
1639  .arg(base->objectName()).arg(base->GetXMLLocation()));
1640  return;
1641  }
1642 
1646  m_Canvas = text->m_Canvas;
1647  m_drawRect = text->m_drawRect;
1648 
1650  SetText(text->m_Message);
1651  m_CutMessage = text->m_CutMessage;
1653 
1655  m_Cutdown = text->m_Cutdown;
1656  m_MultiLine = text->m_MultiLine;
1657  m_Leading = text->m_Leading;
1659  m_lineHeight = text->m_lineHeight;
1660  m_textCursor = text->m_textCursor;
1661 
1662  QMutableMapIterator<QString, MythFontProperties> it(text->m_FontStates);
1663 
1664  while (it.hasNext())
1665  {
1666  it.next();
1667  m_FontStates.insert(it.key(), it.value());
1668  }
1669 
1670  *m_Font = m_FontStates["default"];
1671 
1673  m_startColor = text->m_startColor;
1674  m_endColor = text->m_endColor;
1675  m_numSteps = text->m_numSteps;
1676  m_curStep = text->m_curStep;
1677  m_curR = text->m_curR;
1678  m_curG = text->m_curG;
1679  m_curB = text->m_curB;
1680  m_incR = text->m_incR;
1681  m_incG = text->m_incG;
1682  m_incB = text->m_incB;
1683 
1689  m_scrolling = text->m_scrolling;
1690 
1691  m_textCase = text->m_textCase;
1692 
1693  MythUIType::CopyFrom(base);
1694  FillCutMessage();
1695 }
1696 
1698 {
1699  MythUIText *text = new MythUIText(parent, objectName());
1700  text->CopyFrom(this);
1701 }
1702 
1703 
1705 {
1706  if (m_scrolling && m_Cutdown != Qt::ElideNone)
1707  {
1708  LOG(VB_GENERAL, LOG_ERR,
1709  QString("'%1' (%2): <scroll> and <cutdown> are not combinable.")
1710  .arg(objectName()).arg(GetXMLLocation()));
1711  m_Cutdown = Qt::ElideNone;
1712  }
1713  FillCutMessage();
1714 }
QString GetLanguageAndVariant(void)
Returns the user-set language and variant.
QPoint toQPoint(void) const
Definition: mythrect.cpp:514
void SetCanvasPosition(int x, int y)
Definition: mythuitext.cpp:342
QString GetDefaultText(void) const
Definition: mythuitext.h:44
QColor color(void) const
void setHeight(const QString &sHeight)
Definition: mythrect.cpp:233
float m_scrollPos
Definition: mythuitext.h:165
QColor m_endColor
Definition: mythuitext.h:143
void SetFontProperties(const MythFontProperties &fontProps)
Definition: mythuitext.cpp:211
float m_incB
Definition: mythuitext.h:151
bool ParseElement(const QString &filename, QDomElement &element, bool showWarnings) override
Parse the xml definition of this widget setting the state of the object accordingly.
virtual MythPainter * GetPainter(void)
void StopCycling()
static int parseAlignment(const QString &text)
int m_scrollReturnDelay
Definition: mythuitext.h:159
All purpose text widget, displays a text string.
Definition: mythuitext.h:28
MythRect m_drawRect
Definition: mythuitext.h:113
float m_curG
Definition: mythuitext.h:147
void SetRedraw(void)
Definition: mythuitype.cpp:295
bool m_colorCycling
Definition: mythuitext.h:141
QString m_TemplateText
Definition: mythuitext.h:119
int m_scrollOffset
Definition: mythuitext.h:164
float m_scrollReturnRate
Definition: mythuitext.h:162
void CreateCopy(MythUIType *parent) override
Copy the state of this widget to the one given, it must be of the same type.
void SetPosition(const MythPoint &pos) override
Definition: mythuitext.cpp:336
void SetTextFromMap(const InfoMap &map)
Definition: mythuitext.cpp:159
virtual void SetText(const QString &text)
Definition: mythuitext.cpp:136
void GetShadow(QPoint &offset, QColor &color, int &alpha) const
MythRect m_Canvas
Definition: mythuitext.h:112
int CalcAlpha(int alphamod)
Definition: mythuitype.cpp:460
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
void SetColor(const QColor &color)
QString GetHash(void) const
Qt::TextElideMode m_Cutdown
Definition: mythuitext.h:125
void SetArea(const MythRect &rect) override
Definition: mythuitext.cpp:327
QString m_Message
Definition: mythuitext.h:116
QColor m_startColor
Definition: mythuitext.h:142
int m_rightBearing
Definition: mythuitext.h:130
ScrollDir m_scrollDirection
Definition: mythuitext.h:167
int m_extraLeading
Definition: mythuitext.h:132
const char * formats[8]
Definition: vbilut.cpp:190
static guint32 * tmp
Definition: goom_core.c:35
int m_Leading
Definition: mythuitext.h:131
The base class on which all widgets and screens are based.
Definition: mythuitype.h:63
float m_incR
Definition: mythuitext.h:149
QString GetXMLLocation(void) const
Definition: mythuitype.h:155
void SetPosition(int x, int y)
Convenience method, calls SetPosition(const MythPoint&) Override that instead to change functionality...
Definition: mythuitype.cpp:519
float m_curB
Definition: mythuitext.h:148
QVector< QTextLayout * > m_Layouts
Definition: mythuitext.h:136
virtual bool SupportsAnimation(void)=0
void Pulse(void) override
Pulse is called 70 times a second to trigger a single frame of an animation.
void SetJustification(int just)
Definition: mythuitext.cpp:264
FontMap * GetGlobalFontMap(void)
void SetMultiLine(bool multiline)
Definition: mythuitext.cpp:308
int m_numSteps
Definition: mythuitext.h:144
QFont face(void) const
bool FormatTemplate(QString &paragraph, QTextLayout *layout)
Definition: mythuitext.cpp:463
QRect toQRect(void) const
Definition: mythrect.cpp:354
QString m_DefaultMessage
Definition: mythuitext.h:118
void setRect(const QString &sX, const QString &sY, const QString &sWidth, const QString &sHeight, const QString &baseRes=QString())
Definition: mythrect.cpp:95
void setX(const QString &sX)
Definition: mythrect.cpp:202
QHash< QString, QString > InfoMap
Definition: mythtypes.h:15
virtual void SetMinArea(const MythRect &rect)
Set the minimum area based on the given size.
Definition: mythuitype.cpp:798
void DependChanged(bool isDefault)
static QString getFirstText(QDomElement &element)
void Reset(void) override
Reset the widget to it's original state, should not reset changes made by the theme.
Definition: mythuitext.cpp:84
virtual void Pulse(void)
Pulse is called 70 times a second to trigger a single frame of an animation.
Definition: mythuitype.cpp:442
bool m_Initiator
Definition: mythuitype.h:241
void setY(const QString &sY)
Definition: mythrect.cpp:212
Wrapper around QRect allowing us to handle percentage and other relative values for areas in mythui.
Definition: mythrect.h:17
int m_Descent
Definition: mythuitext.h:128
void moveLeft(const QString &sX)
Definition: mythrect.cpp:258
MythFontProperties * m_Font
Definition: mythuitext.h:138
void ResetMap(const InfoMap &map)
Definition: mythuitext.cpp:98
void FillCutMessage(void)
Definition: mythuitext.cpp:819
void AdjustStretch(int stretch)
MythRect m_Area
Definition: mythuitype.h:249
int GetFontStretch(void) const
void setWidth(const QString &sWidth)
Definition: mythrect.cpp:222
const char * name
Definition: ParseText.cpp:328
bool Layout(QString &paragraph, QTextLayout *layout, bool final, bool &overflow, qreal width, qreal &height, bool force, qreal &last_line_width, QRectF &min_rect, int &num_lines)
Definition: mythuitext.cpp:579
float m_curR
Definition: mythuitext.h:146
MythUIHelper * GetMythUI()
void SetCutDown(Qt::TextElideMode mode)
Definition: mythuitext.cpp:288
void GetOutline(QColor &color, int &size, int &alpha) const
static QString parseText(QDomElement &element)
int m_curStep
Definition: mythuitext.h:145
MythMainWindow * GetMythMainWindow(void)
QString m_CutMessage
Definition: mythuitext.h:117
int m_leftBearing
Definition: mythuitext.h:129
MythFontProperties * GetFont(const QString &text) const
virtual void SetArea(const MythRect &rect)
Definition: mythuitype.cpp:591
bool m_ShrinkNarrow
Definition: mythuitext.h:124
float m_scrollForwardRate
Definition: mythuitext.h:161
int m_Justification
Definition: mythuitext.h:109
int m_textCursor
Definition: mythuitext.h:134
void CopyFrom(MythUIType *base) override
Copy this widgets state from another.
bool hasOutline(void) const
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
virtual void Reset(void)
Reset the widget to it's original state, should not reset changes made by the theme.
Definition: mythuitype.cpp:69
MythUIText(MythUIType *parent, const QString &name)
Definition: mythuitext.cpp:23
bool isValid(void) const
Definition: mythrect.h:98
QVector< QTextLayout::FormatRange > FormatVector
Definition: mythpainter.h:30
bool LayoutParagraphs(const QStringList &paragraphs, const QTextOption &textoption, qreal width, qreal &height, QRectF &min_rect, qreal &last_line_width, int &num_lines, bool final)
Definition: mythuitext.cpp:682
MythFontProperties * GetFont(const QString &text)
MythPoint m_MinSize
Definition: mythuitype.h:251
void DrawSelf(MythPainter *p, int xoffset, int yoffset, int alphaMod, QRect clipRect) override
Definition: mythuitext.cpp:363
virtual void DrawTextLayout(const QRect &canvasRect, const LayoutVector &layouts, const FormatVector &formats, const MythFontProperties &font, int alpha, const QRect &destRect)
bool GetNarrowWidth(const QStringList &paragraphs, const QTextOption &textoption, qreal &width)
Definition: mythuitext.cpp:726
MythPoint topLeft(void) const
Definition: mythrect.cpp:244
bool m_scrollBounce
Definition: mythuitext.h:163
bool m_scrolling
Definition: mythuitext.h:168
void moveTopLeft(const MythPoint &point)
Definition: mythrect.cpp:252
virtual void CopyFrom(MythUIType *base)
Copy this widgets state from another.
QString GetTemplateText(void) const
Definition: mythuitext.h:49
TextCase m_textCase
Definition: mythuitext.h:173
int m_scrollStartDelay
Definition: mythuitext.h:158
int m_scrollPause
Definition: mythuitext.h:160
int m_lineHeight
Definition: mythuitext.h:133
QBrush GetBrush(void) const
static MythRect parseRect(const QString &text, bool normalize=true)
FontStates m_FontStates
Definition: mythuitext.h:139
Wrapper around QPoint allowing us to handle percentage and other relative values for positioning in m...
Definition: mythrect.h:86
bool m_MultiLine
Definition: mythuitext.h:126
MythRect m_OrigDisplayRect
Definition: mythuitext.h:110
int MoveCursor(int lines)
bool m_EnableInitiator
Definition: mythuitype.h:240
const MythFontProperties * GetFontProperties()
Definition: mythuitext.h:79
QPoint m_cursorPos
Definition: mythuitext.h:114
bool hasShadow(void) const
int m_scrollPosWhole
Definition: mythuitext.h:166
void SetFontState(const QString &)
Definition: mythuitext.cpp:225
MythRect m_AltDisplayRect
Definition: mythuitext.h:111
QString GetLanguage(void)
Returns two character ISO-639 language descriptor for UI language.
void moveTop(const QString &sY)
Definition: mythrect.cpp:268
void NormPoint(void)
Definition: mythrect.cpp:391
static bool parseBool(const QString &text)
QPoint CursorPosition(int text_offset)
int GetJustification(void)
Definition: mythuitext.cpp:283
virtual void SetClipRect(const QRect &clipRect)
Definition: mythpainter.cpp:48
void ShiftCanvas(int x, int y)
Definition: mythuitext.cpp:353
void CycleColor(const QColor &startColor, const QColor &endColor, int numSteps)
void Finalize(void) override
Perform any post-xml parsing initialisation tasks.
virtual bool ParseElement(const QString &filename, QDomElement &element, bool showWarnings)
Parse the xml definition of this widget setting the state of the object accordingly.
int m_Ascent
Definition: mythuitext.h:127
float m_incG
Definition: mythuitext.h:150