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