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 : std::as_const(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  range.format.setFontStyleHint(QFont::SansSerif);
510  range.format.setForeground(fnt->GetBrush());
511  }
512  else
513  {
514  LOG(VB_GUI, LOG_ERR,
515  QString("'%1' Unknown Font '%2' specified in template.")
516  .arg(objectName(), fontname));
517  }
518 
519  LOG(VB_GUI, LOG_DEBUG, QString("Removing %1 through %2 '%3'")
520  .arg(pos).arg(end + 7 - pos).arg(paragraph.mid(pos,
521  end + 7 - pos)));
522  paragraph.remove(pos, end + 7 - pos);
523  res = true;
524  }
525  else
526  {
527  LOG(VB_GUI, LOG_ERR,
528  QString("'%1' Non-terminated [font] found in template")
529  .arg(objectName()));
530  break;
531  }
532  }
533 
534  if (range.length == -1) // To the end
535  {
536  range.length = paragraph.length() - range.start;
537  formats.push_back(range);
538  LOG(VB_GUI, LOG_DEBUG,
539  QString("'%1' Setting \"%2\" with FONT %3")
540  .arg(objectName(),
541  paragraph.mid(range.start, range.length),
542  fontname));
543  }
544 
545  if (!formats.empty())
546  layout->setFormats(formats);
547 
548  return res;
549 }
550 
551 bool MythUIText::Layout(QString & paragraph, QTextLayout *layout, bool final,
552  bool & overflow, qreal width, qreal & height,
553  bool force, qreal & last_line_width,
554  QRectF & min_rect, int & num_lines)
555 {
556  int last_line = 0;
557 
558  FormatTemplate(paragraph, layout);
559  layout->setText(paragraph);
560  layout->beginLayout();
561  num_lines = 0;
562  for (;;)
563  {
564  QTextLine line = layout->createLine();
565  if (!line.isValid())
566  break;
567 
568  // Try "visible" width first, so alignment works
569  line.setLineWidth(width);
570 
571  if (!m_multiLine && line.textLength() < paragraph.size())
572  {
573  if (!force && m_cutdown != Qt::ElideNone)
574  {
575  QFontMetrics fm(GetFontProperties()->face());
576  paragraph = fm.elidedText(paragraph, m_cutdown,
577  width - fm.averageCharWidth());
578  return false;
579  }
580  // If text does not fit, then expand so canvas size is correct
581  line.setLineWidth(INT_MAX);
582  }
583 
584  height += m_leading;
585  line.setPosition(QPointF(0, height));
586  height += m_lineHeight;
587  if (!overflow)
588  {
589  if (height > m_area.height())
590  {
591  LOG(VB_GUI, num_lines ? LOG_DEBUG : LOG_NOTICE,
592  QString("'%1' (%2): height overflow. line height %3 "
593  "paragraph height %4, area height %5")
594  .arg(objectName(),
595  GetXMLLocation())
596  .arg(line.height())
597  .arg(height)
598  .arg(m_area.height()));
599 
600  if (!m_multiLine)
601  m_drawRect.setHeight(height);
602  if (m_cutdown != Qt::ElideNone)
603  {
604  QFontMetrics fm(GetFontProperties()->face());
605  QString cut_line = fm.elidedText
606  (paragraph.mid(last_line),
607  Qt::ElideRight,
608  width - fm.averageCharWidth());
609  paragraph = paragraph.left(last_line) + cut_line;
610  if (last_line == 0)
611  min_rect |= line.naturalTextRect();
612  return false;
613  }
614  overflow = true;
615  }
616  else
617  m_drawRect.setHeight(height);
618  if (!m_multiLine)
619  overflow = true;
620  }
621 
622  last_line = line.textStart();
623  last_line_width = line.naturalTextWidth();
624  min_rect |= line.naturalTextRect();
625  ++num_lines;
626 
627  if (final && line.textLength())
628  {
637  QFontMetrics fm(GetFontProperties()->face());
638 
639  int bearing = fm.leftBearing(m_cutMessage[last_line]);
640  if (m_leftBearing > bearing)
641  m_leftBearing = bearing;
642  bearing = fm.rightBearing
643  (m_cutMessage[last_line + line.textLength() - 1]);
644  if (m_rightBearing > bearing)
645  m_rightBearing = bearing;
646  }
647  }
648 
649  layout->endLayout();
650  return true;
651 }
652 
653 bool MythUIText::LayoutParagraphs(const QStringList & paragraphs,
654  const QTextOption & textoption,
655  qreal width, qreal & height,
656  QRectF & min_rect, qreal & last_line_width,
657  int & num_lines, bool final)
658 {
659  QStringList::const_iterator Ipara;
660  bool overflow = false;
661  int idx = 0;
662 
663  for (auto *layout : std::as_const(m_layouts))
664  layout->clearLayout();
665 
666  for (Ipara = paragraphs.begin(), idx = 0;
667  Ipara != paragraphs.end(); ++Ipara, ++idx)
668  {
669  QTextLayout *layout = m_layouts[idx];
670  layout->setTextOption(textoption);
671  layout->setFont(m_font->face());
672 
673  QString para = *Ipara;
674  qreal saved_height = height;
675  QRectF saved_rect = min_rect;
676  if (!Layout(para, layout, final, overflow, width, height, false,
677  last_line_width, min_rect, num_lines))
678  {
679  // Again, with cut down
680  min_rect = saved_rect;
681  height = saved_height;
682  Layout(para, layout, final, overflow, width, height, true,
683  last_line_width, min_rect, num_lines);
684  break;
685  }
686  }
687  m_drawRect.setWidth(width);
688 
689  return (!overflow);
690 }
691 
692 bool MythUIText::GetNarrowWidth(const QStringList & paragraphs,
693  const QTextOption & textoption, qreal & width)
694 {
695  qreal last_line_width = NAN;
696  int last_width = -1;
697  int num_lines = 0;
698  Qt::TextElideMode cutdown = m_cutdown;
699  m_cutdown = Qt::ElideNone;
700 
701  int line_height = m_leading + m_lineHeight;
702  width = m_area.width() / 2.0;
703  int best_width = m_area.width();
704  int too_narrow = 0;
705 
706  for (int attempt = 0; attempt < 10; ++attempt)
707  {
708  QRectF min_rect;
709 
710  m_drawRect.setWidth(0);
711  qreal height = 0;
712 
713  LayoutParagraphs(paragraphs, textoption, width, height,
714  min_rect, last_line_width, num_lines, false);
715 
716  if (num_lines <= 0)
717  return false;
718 
719  if (height > m_drawRect.height())
720  {
721  if (too_narrow < width)
722  too_narrow = width;
723 
724  // Too narrow? How many lines didn't fit?
725  qreal lines = roundf((height - m_drawRect.height()) / line_height);
726  lines -= (1.0 - last_line_width / width);
727  width += (lines * width) /
728  ((double)m_drawRect.height() / line_height);
729 
730  if (width > best_width || static_cast<int>(width) == last_width)
731  {
732  width = best_width;
733  m_cutdown = cutdown;
734  return true;
735  }
736  }
737  else
738  {
739  if (best_width > width)
740  best_width = width;
741 
742  qreal lines = floor((m_area.height() - height) / line_height);
743  if (lines >= 1)
744  {
745  // Too wide?
746  width -= width * (lines / num_lines - 1 + lines);
747  if (static_cast<int>(width) == last_width)
748  {
749  m_cutdown = cutdown;
750  return true;
751  }
752  }
753  else if (last_line_width < m_area.width())
754  {
755  // Is the last line fully used?
756  width -= (1.0 - last_line_width / width) / num_lines;
757  if (width > last_line_width)
758  width = last_line_width;
759  if (static_cast<int>(width) == last_width)
760  {
761  m_cutdown = cutdown;
762  return true;
763  }
764  }
765  if (width < too_narrow)
766  width = too_narrow;
767  }
768  last_width = width;
769  }
770 
771  LOG(VB_GENERAL, LOG_ERR, QString("'%1' (%2) GetNarrowWidth: Gave up "
772  "while trying to find optimal width "
773  "for '%3'.")
774  .arg(objectName(), GetXMLLocation(), m_cutMessage));
775 
776  width = best_width;
777  m_cutdown = cutdown;
778  return false;
779 }
780 
782 {
783  if (m_area.isNull())
784  return;
785 
786  QRectF min_rect;
787  QFontMetrics fm(GetFontProperties()->face());
788 
789  m_lineHeight = fm.height();
790  m_leading = m_multiLine ? fm.leading() + m_extraLeading : m_extraLeading;
791  m_cutMessage.clear();
792  m_textCursor = -1;
793 
795  {
796  bool isNumber = false;
797  int value = m_message.toInt(&isNumber);
798 
799  if (isNumber && m_templateText.contains("%n"))
800  {
801  m_cutMessage = QCoreApplication::translate("ThemeUI",
802  m_templateText.toUtf8(), nullptr,
803  qAbs(value));
804  }
805  else if (m_templateText.contains("%1"))
806  {
807  QString tmp = QCoreApplication::translate("ThemeUI", m_templateText.toUtf8());
808  m_cutMessage = tmp.arg(m_message);
809  }
810  }
811 
812  if (m_cutMessage.isEmpty())
814 
815  if (m_cutMessage.isEmpty())
816  {
817  if (m_layouts.empty())
818  m_layouts.push_back(new QTextLayout);
819 
820  QTextLine line;
821  QTextOption textoption(static_cast<Qt::Alignment>(m_justification));
822  QVector<QTextLayout *>::iterator Ilayout = m_layouts.begin();
823 
824  (*Ilayout)->setTextOption(textoption);
825  (*Ilayout)->setText("");
826  (*Ilayout)->beginLayout();
827  line = (*Ilayout)->createLine();
828  line.setLineWidth(m_area.width());
829  line.setPosition(QPointF(0, 0));
830  (*Ilayout)->endLayout();
831  m_drawRect.setWidth(m_area.width());
833 
834  for (++Ilayout ; Ilayout != m_layouts.end(); ++Ilayout)
835  (*Ilayout)->clearLayout();
836 
838  }
839  else
840  {
841  QStringList templist;
842  QStringList::iterator it;
843 
844  switch (m_textCase)
845  {
846  case CaseUpper :
847  m_cutMessage = m_cutMessage.toUpper();
848  break;
849  case CaseLower :
850  m_cutMessage = m_cutMessage.toLower();
851  break;
852  case CaseCapitaliseFirst :
853  //m_cutMessage = m_cutMessage.toLower();
854  templist = m_cutMessage.split(". ");
855 
856  for (it = templist.begin(); it != templist.end(); ++it)
857  if (!(*it).isEmpty())
858  (*it).replace(0, 1, (*it).at(0).toUpper());
859 
860  m_cutMessage = templist.join(". ");
861  break;
862  case CaseCapitaliseAll :
863  //m_cutMessage = m_cutMessage.toLower();
864  templist = m_cutMessage.split(" ");
865 
866  for (it = templist.begin(); it != templist.end(); ++it)
867  if (!(*it).isEmpty())
868  (*it).replace(0, 1, (*it).at(0).toUpper());
869 
870  m_cutMessage = templist.join(" ");
871  break;
872  case CaseNormal:
873  break;
874  }
875 
876  QTextOption textoption(static_cast<Qt::Alignment>(m_justification));
877  textoption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
878 
879  QStringList paragraphs = m_cutMessage.split('\n', Qt::KeepEmptyParts);
880  for (int idx = m_layouts.size(); idx < paragraphs.size(); ++idx)
881  m_layouts.push_back(new QTextLayout);
882 
883  qreal width = NAN;
884  if (m_multiLine && m_shrinkNarrow &&
885  m_minSize.isValid() && !m_cutMessage.isEmpty())
886  GetNarrowWidth(paragraphs, textoption, width);
887  else
888  width = m_area.width();
889 
890  qreal height = 0;
892  int num_lines = 0;
893  qreal last_line_width = NAN;
894  LayoutParagraphs(paragraphs, textoption, width, height,
895  min_rect, last_line_width, num_lines, true);
896 
897  m_canvas.setRect(0, 0, min_rect.x() + min_rect.width(), height);
899  m_scrollBounce = false;
900 
907  QRect actual = fm.boundingRect(m_cutMessage);
908  m_ascent = -(actual.y() + fm.ascent());
909  m_descent = actual.height() - fm.height();
910  }
911 
912  if (m_scrolling)
913  {
914  if (m_scrollDirection == ScrollLeft ||
917  {
918  if (m_canvas.width() > m_drawRect.width())
919  {
920  m_drawRect.setX(m_area.x());
921  m_drawRect.setWidth(m_area.width());
922  m_scrollOffset = m_drawRect.x() - m_canvas.x();
923  }
924  }
925  else
926  {
927  if (m_canvas.height() > m_drawRect.height())
928  {
929  m_drawRect.setY(m_area.y());
930  m_drawRect.setHeight(m_area.height());
931  m_scrollOffset = m_drawRect.y() - m_canvas.y();
932  }
933  }
934  }
935 
936  // If any of hcenter|vcenter|Justify, center it all, then adjust
937  if ((m_justification & (Qt::AlignCenter|Qt::AlignJustify)) != 0U)
938  {
939  m_drawRect.moveCenter(m_area.center());
940  min_rect.moveCenter(m_area.center());
941  }
942 
943  // Adjust horizontal
944  if (m_justification & Qt::AlignLeft)
945  {
946  // If text size is less than allowed min size, center it
947  if (m_shrinkNarrow && m_minSize.isValid() && min_rect.isValid() &&
948  min_rect.width() < m_minSize.x())
949  {
951  (((m_minSize.x() - min_rect.width() +
952  fm.averageCharWidth()) / 2)));
953  min_rect.setWidth(m_minSize.x());
954  }
955  else
957 
958  min_rect.moveLeft(m_area.x());
959  }
960  else if (m_justification & Qt::AlignRight)
961  {
962  // If text size is less than allowed min size, center it
963  if (m_shrinkNarrow && m_minSize.isValid() && min_rect.isValid() &&
964  min_rect.width() < m_minSize.x())
965  {
966  m_drawRect.moveRight(m_area.x() + m_area.width() -
967  (((m_minSize.x() - min_rect.width() +
968  fm.averageCharWidth()) / 2)));
969  min_rect.setWidth(m_minSize.x());
970  }
971  else
972  m_drawRect.moveRight(m_area.x() + m_area.width());
973 
974  min_rect.moveRight(m_area.x() + m_area.width());
975  }
976 
977  // Adjust vertical
978  if (m_justification & Qt::AlignTop)
979  {
980  // If text size is less than allowed min size, center it
981  if (!m_shrinkNarrow && m_minSize.isValid() && min_rect.isValid() &&
982  min_rect.height() < m_minSize.y())
983  {
985  ((m_minSize.y() - min_rect.height()) / 2));
986  min_rect.setHeight(m_minSize.y());
987  }
988  else
990 
991  min_rect.moveTop(m_area.y());
992  }
993  else if (m_justification & Qt::AlignBottom)
994  {
995  // If text size is less than allowed min size, center it
996  if (!m_shrinkNarrow && m_minSize.isValid() && min_rect.isValid() &&
997  min_rect.height() < m_minSize.y())
998  {
999  m_drawRect.moveBottom(m_area.y() + m_area.height() -
1000  ((m_minSize.y() - min_rect.height()) / 2));
1001  min_rect.setHeight(m_minSize.y());
1002  }
1003  else
1004  m_drawRect.moveBottom(m_area.y() + m_area.height());
1005 
1006  min_rect.moveBottom(m_area.y() + m_area.height());
1007  }
1008 
1010  if (m_minSize.isValid())
1011  {
1012  // Record the minimal area needed for the message.
1013  SetMinArea(MythRect(min_rect.toRect()));
1014  }
1015 }
1016 
1018 {
1019  int lineNo = -1;
1020  int lineCount = 0;
1021  int currPos = 0;
1022  int layoutStartPos = 0;
1023  int xPos = 0;
1024 
1025  for (int x = 0; x < m_layouts.count(); x++)
1026  {
1027  QTextLayout *layout = m_layouts.at(x);
1028 
1029  for (int y = 0; y < layout->lineCount(); y++)
1030  {
1031  lineCount++;
1032  if (lineNo != -1)
1033  continue;
1034 
1035  QTextLine line = layout->lineAt(y);
1036 
1037  if (m_textCursor >= currPos && m_textCursor < currPos + line.textLength()
1038  + (line.lineNumber() == layout->lineCount() - 1 ? 1 : 0))
1039  {
1040  lineNo = lineCount - 1;
1041  xPos = line.cursorToX(m_textCursor - layoutStartPos);
1042  continue;
1043  }
1044 
1045  currPos += line.textLength();
1046  }
1047 
1048  currPos += 1; // skip the newline
1049  layoutStartPos = currPos;
1050  }
1051 
1052  // are we are at the top and need to scroll up
1053  if (lineNo == 0 && lines < 0)
1054  return -1;
1055 
1056  // are we at the bottom and need to scroll down
1057  if (lineNo == lineCount - 1 && lines > 0)
1058  return -1;
1059 
1060  if (lineNo == -1)
1061  {
1062  LOG(VB_GENERAL, LOG_ERR,
1063  QString("'%1' (%2) MoveCursor offset %3 not found in ANY paragraph!")
1064  .arg(objectName(), GetXMLLocation(), QString::number(m_textCursor)));
1065  return m_textCursor;
1066  }
1067 
1068  int newLine = lineNo + lines;
1069 
1070  if (newLine < 0)
1071  newLine = 0;
1072 
1073  if (newLine >= lineCount)
1074  newLine = lineCount - 1;
1075 
1076  lineNo = -1;
1077  layoutStartPos = 0;
1078 
1079  for (int x = 0; x < m_layouts.count(); x++)
1080  {
1081  QTextLayout *layout = m_layouts.at(x);
1082 
1083  for (int y = 0; y < layout->lineCount(); y++)
1084  {
1085  lineNo++;
1086  QTextLine line = layout->lineAt(y);
1087 
1088  if (lineNo == newLine)
1089  return layoutStartPos + line.xToCursor(xPos);
1090  }
1091 
1092  layoutStartPos += layout->text().length() + 1;
1093  }
1094 
1095  // should never reach here
1096  return m_textCursor;
1097 }
1098 
1099 QPoint MythUIText::CursorPosition(int text_offset)
1100 {
1101  if (m_layouts.empty())
1102  return m_area.topLeft().toQPoint();
1103 
1104  if (text_offset == m_textCursor)
1105  return m_cursorPos;
1106  m_textCursor = text_offset;
1107 
1108  QVector<QTextLayout *>::const_iterator Ipara {};
1109  QPoint pos;
1110  int x = 0;
1111  int y = 0;
1112  int offset = text_offset;
1113 
1114  for (Ipara = m_layouts.constBegin(); Ipara != m_layouts.constEnd(); ++Ipara)
1115  {
1116  QTextLine line = (*Ipara)->lineForTextPosition(offset);
1117 
1118  if (line.isValid())
1119  {
1120  pos.setX(line.cursorToX(&offset));
1121  pos.setY(line.y());
1122  break;
1123  }
1124  offset -= ((*Ipara)->text().size() + 1); // Account for \n
1125  }
1126  if (Ipara == m_layouts.constEnd())
1127  {
1128  LOG(VB_GENERAL, LOG_ERR,
1129  QString("'%1' (%2) CursorPosition offset %3 not found in "
1130  "ANY paragraph!")
1131  .arg(objectName(), GetXMLLocation(), QString::number(text_offset)));
1132  return m_area.topLeft().toQPoint();
1133  }
1134 
1135  int mid = m_drawRect.width() / 2;
1136  if (m_canvas.width() <= m_drawRect.width() || pos.x() <= mid)
1137  x = 0; // start
1138  else if (pos.x() >= m_canvas.width() - mid) // end
1139  {
1140  x = m_canvas.width() - m_drawRect.width();
1141  pos.setX(pos.x() - x);
1142  }
1143  else // middle
1144  {
1145  x = pos.x() - mid;
1146  pos.setX(pos.x() - x);
1147  }
1148 
1149  int line_height = m_lineHeight + m_leading;
1150  mid = m_area.height() / 2;
1151  mid -= (mid % line_height);
1152  y = pos.y() - mid;
1153 
1154  if (y <= 0 || m_canvas.height() <= m_area.height()) // Top of buffer
1155  y = 0;
1156  else if (y + m_area.height() > m_canvas.height()) // Bottom of buffer
1157  {
1158  int visible_lines = ((m_area.height() / line_height) * line_height);
1159  y = m_canvas.height() - visible_lines;
1160  pos.setY(visible_lines - (m_canvas.height() - pos.y()));
1161  }
1162  else // somewhere in the middle
1163  {
1164  y -= m_leading;
1165  pos.setY(mid + m_leading);
1166  }
1167 
1168  m_canvas.moveTopLeft(QPoint(-x, -y));
1169 
1170  // Compensate for vertical alignment
1171  pos.setY(pos.y() + m_drawRect.y() - m_area.y());
1172 
1173  pos += m_area.topLeft().toQPoint();
1174  m_cursorPos = pos;
1175 
1176  return pos;
1177 }
1178 
1180 {
1181  //
1182  // Call out base class pulse which will handle any alpha cycling and
1183  // movement
1184  //
1185 
1187 
1188  // Calculate interval since last update in msecs - clamped to 50msecs (20Hz)
1189  // in case of UI blocking/pauses
1190  int64_t currentmsecs = QDateTime::currentMSecsSinceEpoch();
1191  int64_t interval = std::min(currentmsecs - m_lastUpdate, static_cast<int64_t>(50));
1192  m_lastUpdate = currentmsecs;
1193 
1194  // Rates are pixels per second (float) normalised to 70Hz for historical reasons.
1195  // Convert interval accordingly.
1196  float rate = (interval / 1000.0F) * DEFAULT_REFRESH_RATE;
1197 
1198  if (m_colorCycling)
1199  {
1200  m_curR += m_incR;
1201  m_curG += m_incG;
1202  m_curB += m_incB;
1203 
1204  m_curStep++;
1205 
1206  if (m_curStep >= m_numSteps)
1207  {
1208  m_curStep = 0;
1209  m_incR *= -1;
1210  m_incG *= -1;
1211  m_incB *= -1;
1212  }
1213 
1214  QColor newColor = QColor(static_cast<int>(m_curR),
1215  static_cast<int>(m_curG),
1216  static_cast<int>(m_curB));
1217  if (newColor != m_font->color())
1218  {
1219  m_font->SetColor(newColor);
1220  SetRedraw();
1221  }
1222  }
1223 
1224  if (m_scrolling)
1225  {
1226  if (m_scrollPause > 0.0F)
1227  m_scrollPause -= rate;
1228  else
1229  {
1230  if (m_scrollBounce)
1231  m_scrollPos += m_scrollReturnRate * rate;
1232  else
1233  m_scrollPos += m_scrollForwardRate * rate;
1234  }
1235 
1236  int whole = static_cast<int>(m_scrollPos);
1237  if (m_scrollPosWhole != whole)
1238  {
1239  int shift = whole - m_scrollPosWhole;
1240  m_scrollPosWhole = whole;
1241 
1242  switch (m_scrollDirection)
1243  {
1244  case ScrollLeft :
1245  if (m_canvas.width() > m_drawRect.width())
1246  {
1247  ShiftCanvas(-shift, 0);
1248  if (m_canvas.x() + m_canvas.width() < 0)
1249  {
1250  SetCanvasPosition(m_drawRect.width(), 0);
1252  }
1253  }
1254  break;
1255  case ScrollRight :
1256  if (m_canvas.width() > m_drawRect.width())
1257  {
1258  ShiftCanvas(shift, 0);
1259  if (m_canvas.x() > m_drawRect.width())
1260  {
1261  SetCanvasPosition(-m_canvas.width(), 0);
1263  }
1264  }
1265  break;
1266  case ScrollHorizontal:
1267  if (m_canvas.width() <= m_drawRect.width())
1268  break;
1269  if (m_scrollBounce) // scroll right
1270  {
1271  if (m_canvas.x() + m_scrollOffset > m_drawRect.x())
1272  {
1273  m_scrollBounce = false;
1276  }
1277  else
1278  ShiftCanvas(shift, 0);
1279  }
1280  else // scroll left
1281  {
1282  if (m_canvas.x() + m_canvas.width() + m_scrollOffset <
1283  m_drawRect.x() + m_drawRect.width())
1284  {
1285  m_scrollBounce = true;
1288  }
1289  else
1290  ShiftCanvas(-shift, 0);
1291  }
1292  break;
1293  case ScrollUp :
1294  if (m_canvas.height() > m_drawRect.height())
1295  {
1296  ShiftCanvas(0, -shift);
1297  if (m_canvas.y() + m_canvas.height() < 0)
1298  {
1299  SetCanvasPosition(0, m_drawRect.height());
1301  }
1302  }
1303  break;
1304  case ScrollDown :
1305  if (m_canvas.height() > m_drawRect.height())
1306  {
1307  ShiftCanvas(0, shift);
1308  if (m_canvas.y() > m_drawRect.height())
1309  {
1310  SetCanvasPosition(0, -m_canvas.height());
1312  }
1313  }
1314  break;
1315  case ScrollVertical:
1316  if (m_canvas.height() <= m_drawRect.height())
1317  break;
1318  if (m_scrollBounce) // scroll down
1319  {
1320  if (m_canvas.y() + m_scrollOffset > m_drawRect.y())
1321  {
1322  m_scrollBounce = false;
1325  }
1326  else
1327  ShiftCanvas(0, shift);
1328  }
1329  else // scroll up
1330  {
1331  if (m_canvas.y() + m_canvas.height() + m_scrollOffset <
1332  m_drawRect.y() + m_drawRect.height())
1333  {
1334  m_scrollBounce = true;
1337  }
1338  else
1339  ShiftCanvas(0, -shift);
1340  }
1341  break;
1342  case ScrollNone:
1343  break;
1344  }
1345  }
1346  }
1347 }
1348 
1349 void MythUIText::CycleColor(const QColor& startColor, const QColor& endColor, int numSteps)
1350 {
1351  if (!GetPainter()->SupportsAnimation())
1352  return;
1353 
1354  m_startColor = startColor;
1355  m_endColor = endColor;
1356  m_numSteps = numSteps;
1357  m_curStep = 0;
1358 
1359  m_curR = startColor.red();
1360  m_curG = startColor.green();
1361  m_curB = startColor.blue();
1362 
1363  m_incR = (endColor.red() * 1.0F - m_curR) / m_numSteps;
1364  m_incG = (endColor.green() * 1.0F - m_curG) / m_numSteps;
1365  m_incB = (endColor.blue() * 1.0F - m_curB) / m_numSteps;
1366 
1367  m_colorCycling = true;
1368 }
1369 
1371 {
1372  if (!m_colorCycling)
1373  return;
1374 
1376  m_colorCycling = false;
1377  SetRedraw();
1378 }
1379 
1381  const QString &filename, QDomElement &element, bool showWarnings)
1382 {
1383  if (element.tagName() == "area")
1384  {
1385  SetArea(parseRect(element));
1387  }
1388  // else if (element.tagName() == "altarea") // Unused, but maybe in future?
1389  // m_altDisplayRect = parseRect(element);
1390  else if (element.tagName() == "font")
1391  {
1392  QString fontname = getFirstText(element);
1393  MythFontProperties *fp = GetFont(fontname);
1394 
1395  if (!fp)
1396  fp = GetGlobalFontMap()->GetFont(fontname);
1397 
1398  if (fp)
1399  {
1400  MythFontProperties font = *fp;
1401  MythMainWindow* window = GetMythMainWindow();
1402  font.Rescale(window->GetUIScreenRect().height());
1403  font.AdjustStretch(window->GetFontStretch());
1404  QString state = element.attribute("state", "");
1405 
1406  if (!state.isEmpty())
1407  {
1408  m_fontStates.insert(state, font);
1409  }
1410  else
1411  {
1412  m_fontStates.insert("default", font);
1413  *m_font = m_fontStates["default"];
1414  }
1415  }
1416  }
1417  else if (element.tagName() == "extraleading")
1418  {
1419  m_extraLeading = getFirstText(element).toInt();
1420  }
1421  else if (element.tagName() == "value")
1422  {
1423  if (element.attribute("lang", "").isEmpty())
1424  {
1425  m_message = QCoreApplication::translate("ThemeUI",
1426  parseText(element).toUtf8());
1427  }
1428  else if ((element.attribute("lang", "").toLower() ==
1430  (element.attribute("lang", "").toLower() ==
1432  {
1433  m_message = parseText(element);
1434  }
1435 
1437  SetText(m_message);
1438  }
1439  else if (element.tagName() == "template")
1440  {
1441  m_templateText = parseText(element);
1442  }
1443  else if (element.tagName() == "cutdown")
1444  {
1445  QString mode = getFirstText(element).toLower();
1446 
1447  if (mode == "left")
1448  SetCutDown(Qt::ElideLeft);
1449  else if (mode == "middle")
1450  SetCutDown(Qt::ElideMiddle);
1451  else if (mode == "right" || parseBool(element))
1452  SetCutDown(Qt::ElideRight);
1453  else
1454  SetCutDown(Qt::ElideNone);
1455  }
1456  else if (element.tagName() == "multiline")
1457  {
1458  SetMultiLine(parseBool(element));
1459  }
1460  else if (element.tagName() == "align")
1461  {
1462  QString align = getFirstText(element).toLower();
1464  }
1465  else if (element.tagName() == "colorcycle")
1466  {
1467  if (GetPainter()->SupportsAnimation())
1468  {
1469  QString tmp = element.attribute("start");
1470 
1471  if (!tmp.isEmpty())
1472  m_startColor = QColor(tmp);
1473 
1474  tmp = element.attribute("end");
1475 
1476  if (!tmp.isEmpty())
1477  m_endColor = QColor(tmp);
1478 
1479  tmp = element.attribute("steps");
1480 
1481  if (!tmp.isEmpty())
1482  m_numSteps = tmp.toInt();
1483 
1484  // initialize the rest of the stuff
1486  }
1487  else
1488  m_colorCycling = false;
1489 
1490  m_colorCycling = parseBool(element.attribute("disable"));
1491  }
1492  else if (element.tagName() == "scroll")
1493  {
1494  if (GetPainter()->SupportsAnimation())
1495  {
1496  QString tmp = element.attribute("direction");
1497 
1498  if (!tmp.isEmpty())
1499  {
1500  tmp = tmp.toLower();
1501 
1502  if (tmp == "left")
1504  else if (tmp == "right")
1506  else if (tmp == "up")
1508  else if (tmp == "down")
1510  else if (tmp == "horizontal")
1512  else if (tmp == "vertical")
1514  else
1515  {
1517  LOG(VB_GENERAL, LOG_ERR,
1518  QString("'%1' (%2) Invalid scroll attribute")
1519  .arg(objectName(), GetXMLLocation()));
1520  }
1521  }
1522 
1523  tmp = element.attribute("startdelay");
1524  if (!tmp.isEmpty())
1525  {
1526  float seconds = tmp.toFloat();
1527  m_scrollStartDelay = static_cast<int>(lroundf(seconds * DEFAULT_REFRESH_RATE));
1528  }
1529  tmp = element.attribute("returndelay");
1530  if (!tmp.isEmpty())
1531  {
1532  float seconds = tmp.toFloat();
1533  m_scrollReturnDelay = static_cast<int>(lroundf(seconds * DEFAULT_REFRESH_RATE));
1534  }
1535  tmp = element.attribute("rate");
1536  if (!tmp.isEmpty())
1537  {
1538 #if 0 // scroll rate as a percentage of 70Hz
1539  float percent = tmp.toFloat() / 100.0;
1540  m_scrollForwardRate = percent;
1541 #else // scroll rate as pixels per second
1542  int pixels = tmp.toInt();
1543  m_scrollForwardRate = pixels / static_cast<float>(DEFAULT_REFRESH_RATE);
1544 #endif
1545  }
1546  tmp = element.attribute("returnrate");
1547  if (!tmp.isEmpty())
1548  {
1549 #if 0 // scroll rate as a percentage of 70Hz
1550  float percent = tmp.toFloat() / 100.0;
1551  m_scrollReturnRate = percent;
1552 #else // scroll rate as pixels per second
1553  int pixels = tmp.toInt();
1554  m_scrollReturnRate = pixels / static_cast<float>(DEFAULT_REFRESH_RATE);
1555 #endif
1556  }
1557 
1558  m_scrolling = true;
1559  }
1560  else
1561  m_scrolling = false;
1562  }
1563  else if (element.tagName() == "case")
1564  {
1565  QString stringCase = getFirstText(element).toLower();
1566 
1567  if (stringCase == "lower")
1569  else if (stringCase == "upper")
1571  else if (stringCase == "capitalisefirst")
1573  else if (stringCase == "capitaliseall")
1575  else
1577  }
1578  else
1579  {
1580  if (element.tagName() == "minsize" && element.hasAttribute("shrink"))
1581  {
1582  m_shrinkNarrow = (element.attribute("shrink")
1583  .toLower() != "short");
1584  }
1585 
1586  return MythUIType::ParseElement(filename, element, showWarnings);
1587  }
1588 
1589  return true;
1590 }
1591 
1593 {
1594  auto *text = dynamic_cast<MythUIText *>(base);
1595 
1596  if (!text)
1597  {
1598  LOG(VB_GENERAL, LOG_ERR,
1599  QString("'%1' (%2) ERROR, bad parsing '%3' (%4)")
1600  .arg(objectName(), GetXMLLocation(),
1601  base->objectName(), base->GetXMLLocation()));
1602  return;
1603  }
1604 
1605  m_justification = text->m_justification;
1606  m_origDisplayRect = text->m_origDisplayRect;
1607  m_altDisplayRect = text->m_altDisplayRect;
1608  m_canvas = text->m_canvas;
1609  m_drawRect = text->m_drawRect;
1610 
1611  m_defaultMessage = text->m_defaultMessage;
1612  SetText(text->m_message);
1613  m_cutMessage = text->m_cutMessage;
1614  m_templateText = text->m_templateText;
1615 
1616  m_shrinkNarrow = text->m_shrinkNarrow;
1617  m_cutdown = text->m_cutdown;
1618  m_multiLine = text->m_multiLine;
1619  m_leading = text->m_leading;
1620  m_extraLeading = text->m_extraLeading;
1621  m_lineHeight = text->m_lineHeight;
1622  m_textCursor = text->m_textCursor;
1623 
1624  QMutableMapIterator<QString, MythFontProperties> it(text->m_fontStates);
1625 
1626  while (it.hasNext())
1627  {
1628  it.next();
1629  m_fontStates.insert(it.key(), it.value());
1630  }
1631 
1632  *m_font = m_fontStates["default"];
1633 
1634  m_colorCycling = text->m_colorCycling;
1635  m_startColor = text->m_startColor;
1636  m_endColor = text->m_endColor;
1637  m_numSteps = text->m_numSteps;
1638  m_curStep = text->m_curStep;
1639  m_curR = text->m_curR;
1640  m_curG = text->m_curG;
1641  m_curB = text->m_curB;
1642  m_incR = text->m_incR;
1643  m_incG = text->m_incG;
1644  m_incB = text->m_incB;
1645 
1646  m_scrollStartDelay = text->m_scrollStartDelay;
1647  m_scrollReturnDelay = text->m_scrollReturnDelay;
1648  m_scrollForwardRate = text->m_scrollForwardRate;
1649  m_scrollReturnRate = text->m_scrollReturnRate;
1650  m_scrollDirection = text->m_scrollDirection;
1651  m_scrolling = text->m_scrolling;
1652 
1653  m_textCase = text->m_textCase;
1654 
1655  MythUIType::CopyFrom(base);
1656  FillCutMessage();
1657 }
1658 
1660 {
1661  auto *text = new MythUIText(parent, objectName());
1662  text->CopyFrom(this);
1663 }
1664 
1665 
1667 {
1668  if (m_scrolling && m_cutdown != Qt::ElideNone)
1669  {
1670  LOG(VB_GENERAL, LOG_ERR,
1671  QString("'%1' (%2): <scroll> and <cutdown> are not combinable.")
1672  .arg(objectName(), GetXMLLocation()));
1673  m_cutdown = Qt::ElideNone;
1674  }
1675  FillCutMessage();
1676 }
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:551
mythuitext.h
XMLParseBase::parseAlignment
static int parseAlignment(const QString &text)
Definition: xmlparsebase.cpp:153
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:781
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:1592
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:653
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:692
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:1017
XMLParseBase::parseText
static QString parseText(QDomElement &element)
Definition: xmlparsebase.cpp:315
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:1666
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:1099
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:1349
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:1380
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:1370
MythPoint::NormPoint
void NormPoint(void)
Definition: mythrect.cpp:471
XMLParseBase::parseRect
static MythRect parseRect(const QString &text, bool normalize=true)
Definition: xmlparsebase.cpp:132
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:1767
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:1659
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:1779
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:1179
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