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