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