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