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