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