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 Ilayout = m_Layouts.begin(); Ilayout != m_Layouts.end(); ++Ilayout)
80  delete *Ilayout;
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("%(([^\\|%]+)?\\||\\|(.))?([\\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 = qApp->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("%(([^\\|%]+)?\\||\\|(.))?([\\w#]+)(\\|(.+))?%");
166  regexp.setMinimal(true);
167 
168  if (!newText.isEmpty() && newText.contains(regexp))
169  {
170  int pos = 0;
171 
172  QString translatedTemplate = qApp->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 #if QT_VERSION < QT_VERSION_CHECK(5,6,0) // else done in MythUIText::FormatTemplate
411  QTextLayout::FormatRange range;
412 
413  outlineColor.setAlpha(outlineAlpha);
414 
415  outline.NormPoint(); // scale it to screen resolution
416 
417  QPen pen;
418  pen.setBrush(outlineColor);
419  pen.setWidth(outline.x());
420 
421  range.start = 0;
422  range.length = m_CutMessage.size();
423  range.format.setTextOutline(pen);
424  formats.push_back(range);
425 #endif
426 
427  drawrect.setX(drawrect.x() - outline.x());
428  drawrect.setWidth(drawrect.width() + outline.x());
429  drawrect.setY(drawrect.y() - outline.y());
430  drawrect.setHeight(drawrect.height() + outline.y());
431 
432  /* Canvas pos is where the view port (drawrect) pulls from, so
433  * it needs moved to the right for the left edge to be picked up*/
434  canvas.moveLeft(canvas.x() + outline.x());
435  canvas.setWidth(canvas.width() + outline.x());
436  canvas.moveTop(canvas.y() + outline.y());
437  canvas.setHeight(canvas.height() + outline.y());
438  }
439 
440  if (GetFontProperties()->hasShadow())
441  {
442  QPoint shadowOffset;
443  QColor shadowColor;
444  int shadowAlpha = 255;
445 
446  GetFontProperties()->GetShadow(shadowOffset, shadowColor, shadowAlpha);
447 
448  MythPoint shadow(shadowOffset);
449  shadow.NormPoint(); // scale it to screen resolution
450 
451  drawrect.setWidth(drawrect.width() + shadow.x());
452  drawrect.setHeight(drawrect.height() + shadow.y());
453 
454  canvas.setWidth(canvas.width() + shadow.x());
455  canvas.setHeight(canvas.height() + shadow.y());
456  }
457 
458  p->SetClipRect(clipRect);
459  p->DrawTextLayout(canvas, m_Layouts, formats,
460  *GetFontProperties(), alpha, drawrect);
461 }
462 
463 bool MythUIText::FormatTemplate(QString & paragraph, QTextLayout *layout)
464 {
465 #if QT_VERSION >= QT_VERSION_CHECK(5,6,0)
466  layout->clearFormats();
467 #endif
468 
470  QTextLayout::FormatRange range;
471  QString fontname;
472  bool res = false; // Return true if paragraph changed.
473 
474 #if QT_VERSION >= QT_VERSION_CHECK(5,6,0) // else done in DrawSelf
475  if (GetFontProperties()->hasOutline())
476  {
477  int outlineSize = 0;
478  int outlineAlpha = 255;
479  QColor outlineColor;
480 
481  GetFontProperties()->GetOutline(outlineColor, outlineSize,
482  outlineAlpha);
483 
484  outlineColor.setAlpha(outlineAlpha);
485 
486  MythPoint outline(outlineSize, outlineSize);
487  outline.NormPoint(); // scale it to screen resolution
488 
489  QPen pen;
490  pen.setBrush(outlineColor);
491  pen.setWidth(outline.x());
492 
493  range.start = 0;
494  range.length = paragraph.size();
495  range.format.setTextOutline(pen);
496  formats.push_back(range);
497  }
498 #endif
499 
500  range.start = 0;
501  range.length = 0;
502 
503  int pos = 0;
504  int end = 0;
505  while ((pos = paragraph.indexOf("[font]", pos, Qt::CaseInsensitive)) != -1)
506  {
507  if ((end = paragraph.indexOf("[/font]", pos + 1, Qt::CaseInsensitive))
508  != -1)
509  {
510  if (range.length == -1)
511  {
512  // End of the affected text
513  range.length = pos - range.start;
514  if (range.length > 0)
515  {
516  formats.push_back(range);
517  LOG(VB_GUI, LOG_DEBUG,
518  QString("'%1' Setting \"%2\" with FONT %3")
519  .arg(objectName())
520  .arg(paragraph.mid(range.start, range.length))
521  .arg(fontname));
522  }
523  range.length = 0;
524  }
525 
526  int len = end - pos - 6;
527  fontname = paragraph.mid(pos + 6, len);
528 
529  if (GetGlobalFontMap()->Contains(fontname))
530  {
531  MythFontProperties *fnt = GetGlobalFontMap()->GetFont(fontname);
532  range.start = pos;
533  range.length = -1; // Need to find the end of the effect
534  range.format.setFont(fnt->face());
535  range.format.setFontStyleHint(QFont::SansSerif,
536  QFont::OpenGLCompatible);
537  range.format.setForeground(fnt->GetBrush());
538  }
539  else
540  {
541  LOG(VB_GUI, LOG_ERR,
542  QString("'%1' Unknown Font '%2' specified in template.")
543  .arg(objectName())
544  .arg(fontname));
545  }
546 
547  LOG(VB_GUI, LOG_DEBUG, QString("Removing %1 through %2 '%3'")
548  .arg(pos).arg(end + 7 - pos).arg(paragraph.mid(pos,
549  end + 7 - pos)));
550  paragraph.remove(pos, end + 7 - pos);
551  res = true;
552  }
553  else
554  {
555  LOG(VB_GUI, LOG_ERR,
556  QString("'%1' Non-terminated [font] found in template")
557  .arg(objectName()));
558  break;
559  }
560  }
561 
562  if (range.length == -1) // To the end
563  {
564  range.length = paragraph.length() - range.start;
565  formats.push_back(range);
566  LOG(VB_GUI, LOG_DEBUG,
567  QString("'%1' Setting \"%2\" with FONT %3")
568  .arg(objectName())
569  .arg(paragraph.mid(range.start, range.length))
570  .arg(fontname));
571  }
572 
573 #if QT_VERSION >= QT_VERSION_CHECK(5,6,0)
574  if (!formats.empty())
575  layout->setFormats(formats);
576 #endif
577 
578  return res;
579 }
580 
581 bool MythUIText::Layout(QString & paragraph, QTextLayout *layout, bool final,
582  bool & overflow, qreal width, qreal & height,
583  bool force, qreal & last_line_width,
584  QRectF & min_rect, int & num_lines)
585 {
586  int last_line = 0;
587 
588  FormatTemplate(paragraph, layout);
589  layout->setText(paragraph);
590  layout->beginLayout();
591  num_lines = 0;
592  for (;;)
593  {
594  QTextLine line = layout->createLine();
595  if (!line.isValid())
596  break;
597 
598  // Try "visible" width first, so alignment works
599  line.setLineWidth(width);
600 
601  if (!m_MultiLine && line.textLength() < paragraph.size())
602  {
603  if (!force && m_Cutdown != Qt::ElideNone)
604  {
605  QFontMetrics fm(GetFontProperties()->face());
606  paragraph = fm.elidedText(paragraph, m_Cutdown,
607  width - fm.averageCharWidth());
608  return false;
609  }
610  // If text does not fit, then expand so canvas size is correct
611  line.setLineWidth(INT_MAX);
612  }
613 
614  height += m_Leading;
615  line.setPosition(QPointF(0, height));
616  height += m_lineHeight;
617  if (!overflow)
618  {
619  if (height > m_Area.height())
620  {
621  LOG(VB_GUI, num_lines ? LOG_DEBUG : LOG_NOTICE,
622  QString("'%1' (%2): height overflow. line height %3 "
623  "paragraph height %4, area height %5")
624  .arg(objectName())
625  .arg(GetXMLLocation())
626  .arg(line.height())
627  .arg(height)
628  .arg(m_Area.height()));
629 
630  if (!m_MultiLine)
631  m_drawRect.setHeight(height);
632  if (m_Cutdown != Qt::ElideNone)
633  {
634  QFontMetrics fm(GetFontProperties()->face());
635  QString cut_line = fm.elidedText
636  (paragraph.mid(last_line),
637  Qt::ElideRight,
638  width - fm.averageCharWidth());
639  paragraph = paragraph.left(last_line) + cut_line;
640  if (last_line == 0)
641  min_rect |= line.naturalTextRect();
642  return false;
643  }
644  overflow = true;
645  }
646  else
647  m_drawRect.setHeight(height);
648  if (!m_MultiLine)
649  overflow = true;
650  }
651 
652  last_line = line.textStart();
653  last_line_width = line.naturalTextWidth();
654  min_rect |= line.naturalTextRect();
655  ++num_lines;
656 
657  if (final && line.textLength())
658  {
667  QFontMetrics fm(GetFontProperties()->face());
668 
669  int bearing = fm.leftBearing(m_CutMessage[last_line]);
670  if (m_leftBearing > bearing)
671  m_leftBearing = bearing;
672  bearing = fm.rightBearing
673  (m_CutMessage[last_line + line.textLength() - 1]);
674  if (m_rightBearing > bearing)
675  m_rightBearing = bearing;
676  }
677  }
678 
679  layout->endLayout();
680  return true;
681 }
682 
683 bool MythUIText::LayoutParagraphs(const QStringList & paragraphs,
684  const QTextOption & textoption,
685  qreal width, qreal & height,
686  QRectF & min_rect, qreal & last_line_width,
687  int & num_lines, bool final)
688 {
689  QStringList::const_iterator Ipara;
690  bool overflow = false;
691  int idx = 0;
692 
693  for (auto Ilayout = m_Layouts.begin(); Ilayout != m_Layouts.end(); ++Ilayout)
694  (*Ilayout)->clearLayout();
695 
696  for (Ipara = paragraphs.begin(), idx = 0;
697  Ipara != paragraphs.end(); ++Ipara, ++idx)
698  {
699  QTextLayout *layout = m_Layouts[idx];
700  layout->setTextOption(textoption);
701  layout->setFont(m_Font->face());
702 
703  QString para = *Ipara;
704  qreal saved_height = height;
705  QRectF saved_rect = min_rect;
706  if (!Layout(para, layout, final, overflow, width, height, false,
707  last_line_width, min_rect, num_lines))
708  {
709  // Again, with cut down
710  min_rect = saved_rect;
711  height = saved_height;
712  Layout(para, layout, final, overflow, width, height, true,
713  last_line_width, min_rect, num_lines);
714  break;
715  }
716  }
717  m_drawRect.setWidth(width);
718 
719  return (!overflow);
720 }
721 
722 bool MythUIText::GetNarrowWidth(const QStringList & paragraphs,
723  const QTextOption & textoption, qreal & width)
724 {
725  qreal last_line_width = NAN;
726  int last_width = -1;
727  int num_lines = 0;
728  Qt::TextElideMode cutdown = m_Cutdown;
729  m_Cutdown = Qt::ElideNone;
730 
731  int line_height = m_Leading + m_lineHeight;
732  width = m_Area.width() / 2.0;
733  int best_width = m_Area.width();
734  int too_narrow = 0;
735 
736  for (int attempt = 0; attempt < 10; ++attempt)
737  {
738  QRectF min_rect;
739 
740  m_drawRect.setWidth(0);
741  qreal height = 0;
742 
743  LayoutParagraphs(paragraphs, textoption, width, height,
744  min_rect, last_line_width, num_lines, false);
745 
746  if (num_lines <= 0)
747  return false;
748 
749  if (height > m_drawRect.height())
750  {
751  if (too_narrow < width)
752  too_narrow = width;
753 
754  // Too narrow? How many lines didn't fit?
755  qreal lines = roundf((height - m_drawRect.height()) / line_height);
756  lines -= (1.0 - last_line_width / width);
757  width += (lines * width) /
758  ((double)m_drawRect.height() / line_height);
759 
760  if (width > best_width || static_cast<int>(width) == last_width)
761  {
762  width = best_width;
763  m_Cutdown = cutdown;
764  return true;
765  }
766  }
767  else
768  {
769  if (best_width > width)
770  best_width = width;
771 
772  qreal lines = floor((m_Area.height() - height) / line_height);
773  if (lines >= 1)
774  {
775  // Too wide?
776  width -= width * (lines / num_lines - 1 + lines);
777  if (static_cast<int>(width) == last_width)
778  {
779  m_Cutdown = cutdown;
780  return true;
781  }
782  }
783  else if (last_line_width < m_Area.width())
784  {
785  // Is the last line fully used?
786  width -= (1.0 - last_line_width / width) / num_lines;
787  if (width > last_line_width)
788  width = last_line_width;
789  if (static_cast<int>(width) == last_width)
790  {
791  m_Cutdown = cutdown;
792  return true;
793  }
794  }
795  if (width < too_narrow)
796  width = too_narrow;
797  }
798  last_width = width;
799  }
800 
801  LOG(VB_GENERAL, LOG_ERR, QString("'%1' (%2) GetNarrowWidth: Gave up "
802  "while trying to find optimal width "
803  "for '%3'.")
804  .arg(objectName()).arg(GetXMLLocation()).arg(m_CutMessage));
805 
806  width = best_width;
807  m_Cutdown = cutdown;
808  return false;
809 }
810 
812 {
813  if (m_Area.isNull())
814  return;
815 
816  QRectF min_rect;
817  QFontMetrics fm(GetFontProperties()->face());
818 
819  m_lineHeight = fm.height();
820  m_Leading = m_MultiLine ? fm.leading() + m_extraLeading : m_extraLeading;
821  m_CutMessage.clear();
822  m_textCursor = -1;
823 
825  {
826  bool isNumber = false;
827  int value = m_Message.toInt(&isNumber);
828 
829  if (isNumber && m_TemplateText.contains("%n"))
830  {
831  m_CutMessage = qApp->translate("ThemeUI",
832  m_TemplateText.toUtf8(), nullptr,
833  qAbs(value));
834  }
835  else if (m_TemplateText.contains("%1"))
836  {
837  QString tmp = qApp->translate("ThemeUI", m_TemplateText.toUtf8());
838  m_CutMessage = tmp.arg(m_Message);
839  }
840  }
841 
842  if (m_CutMessage.isEmpty())
844 
845  if (m_CutMessage.isEmpty())
846  {
847  if (m_Layouts.empty())
848  m_Layouts.push_back(new QTextLayout);
849 
850  QTextLine line;
851  QTextOption textoption(static_cast<Qt::Alignment>(m_Justification));
852  QVector<QTextLayout *>::iterator Ilayout = m_Layouts.begin();
853 
854  (*Ilayout)->setTextOption(textoption);
855  (*Ilayout)->setText("");
856  (*Ilayout)->beginLayout();
857  line = (*Ilayout)->createLine();
858  line.setLineWidth(m_Area.width());
859  line.setPosition(QPointF(0, 0));
860  (*Ilayout)->endLayout();
861  m_drawRect.setWidth(m_Area.width());
863 
864  for (++Ilayout ; Ilayout != m_Layouts.end(); ++Ilayout)
865  (*Ilayout)->clearLayout();
866 
868  }
869  else
870  {
871  QStringList templist;
872  QStringList::iterator it;
873 
874  switch (m_textCase)
875  {
876  case CaseUpper :
877  m_CutMessage = m_CutMessage.toUpper();
878  break;
879  case CaseLower :
880  m_CutMessage = m_CutMessage.toLower();
881  break;
882  case CaseCapitaliseFirst :
883  //m_CutMessage = m_CutMessage.toLower();
884  templist = m_CutMessage.split(". ");
885 
886  for (it = templist.begin(); it != templist.end(); ++it)
887  (*it).replace(0, 1, (*it).left(1).toUpper());
888 
889  m_CutMessage = templist.join(". ");
890  break;
891  case CaseCapitaliseAll :
892  //m_CutMessage = m_CutMessage.toLower();
893  templist = m_CutMessage.split(" ");
894 
895  for (it = templist.begin(); it != templist.end(); ++it)
896  (*it).replace(0, 1, (*it).left(1).toUpper());
897 
898  m_CutMessage = templist.join(" ");
899  break;
900  case CaseNormal:
901  break;
902  }
903 
904  QTextOption textoption(static_cast<Qt::Alignment>(m_Justification));
905  textoption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
906 
907  QStringList paragraphs = m_CutMessage.split('\n',
908  QString::KeepEmptyParts);
909 
910  for (int idx = m_Layouts.size(); idx < paragraphs.size(); ++idx)
911  m_Layouts.push_back(new QTextLayout);
912 
913  qreal width = NAN;
914  if (m_MultiLine && m_ShrinkNarrow &&
915  m_MinSize.isValid() && !m_CutMessage.isEmpty())
916  GetNarrowWidth(paragraphs, textoption, width);
917  else
918  width = m_Area.width();
919 
920  qreal height = 0;
922  int num_lines = 0;
923  qreal last_line_width = NAN;
924  LayoutParagraphs(paragraphs, textoption, width, height,
925  min_rect, last_line_width, num_lines, true);
926 
927  m_Canvas.setRect(0, 0, min_rect.x() + min_rect.width(), height);
929  m_scrollBounce = false;
930 
937  QRect actual = fm.boundingRect(m_CutMessage);
938  m_Ascent = -(actual.y() + fm.ascent());
939  m_Descent = actual.height() - fm.height();
940  }
941 
942  if (m_scrolling)
943  {
944  if (m_scrollDirection == ScrollLeft ||
947  {
948  if (m_Canvas.width() > m_drawRect.width())
949  {
950  m_drawRect.setX(m_Area.x());
951  m_drawRect.setWidth(m_Area.width());
952  m_scrollOffset = m_drawRect.x() - m_Canvas.x();
953  }
954  }
955  else
956  {
957  if (m_Canvas.height() > m_drawRect.height())
958  {
959  m_drawRect.setY(m_Area.y());
960  m_drawRect.setHeight(m_Area.height());
961  m_scrollOffset = m_drawRect.y() - m_Canvas.y();
962  }
963  }
964  }
965 
966  // If any of hcenter|vcenter|Justify, center it all, then adjust
967  if ((m_Justification & (Qt::AlignCenter|Qt::AlignJustify)) != 0U)
968  {
969  m_drawRect.moveCenter(m_Area.center());
970  min_rect.moveCenter(m_Area.center());
971  }
972 
973  // Adjust horizontal
974  if (m_Justification & Qt::AlignLeft)
975  {
976  // If text size is less than allowed min size, center it
977  if (m_ShrinkNarrow && m_MinSize.isValid() && min_rect.isValid() &&
978  min_rect.width() < m_MinSize.x())
979  {
981  (((m_MinSize.x() - min_rect.width() +
982  fm.averageCharWidth()) / 2)));
983  min_rect.setWidth(m_MinSize.x());
984  }
985  else
987 
988  min_rect.moveLeft(m_Area.x());
989  }
990  else if (m_Justification & Qt::AlignRight)
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.width() < m_MinSize.x())
995  {
996  m_drawRect.moveRight(m_Area.x() + m_Area.width() -
997  (((m_MinSize.x() - min_rect.width() +
998  fm.averageCharWidth()) / 2)));
999  min_rect.setWidth(m_MinSize.x());
1000  }
1001  else
1002  m_drawRect.moveRight(m_Area.x() + m_Area.width());
1003 
1004  min_rect.moveRight(m_Area.x() + m_Area.width());
1005  }
1006 
1007  // Adjust vertical
1008  if (m_Justification & Qt::AlignTop)
1009  {
1010  // If text size is less than allowed min size, center it
1011  if (!m_ShrinkNarrow && m_MinSize.isValid() && min_rect.isValid() &&
1012  min_rect.height() < m_MinSize.y())
1013  {
1014  m_drawRect.moveTop(m_Area.y() +
1015  ((m_MinSize.y() - min_rect.height()) / 2));
1016  min_rect.setHeight(m_MinSize.y());
1017  }
1018  else
1019  m_drawRect.moveTop(m_Area.y());
1020 
1021  min_rect.moveTop(m_Area.y());
1022  }
1023  else if (m_Justification & Qt::AlignBottom)
1024  {
1025  // If text size is less than allowed min size, center it
1026  if (!m_ShrinkNarrow && m_MinSize.isValid() && min_rect.isValid() &&
1027  min_rect.height() < m_MinSize.y())
1028  {
1029  m_drawRect.moveBottom(m_Area.y() + m_Area.height() -
1030  ((m_MinSize.y() - min_rect.height()) / 2));
1031  min_rect.setHeight(m_MinSize.y());
1032  }
1033  else
1034  m_drawRect.moveBottom(m_Area.y() + m_Area.height());
1035 
1036  min_rect.moveBottom(m_Area.y() + m_Area.height());
1037  }
1038 
1040  if (m_MinSize.isValid())
1041  {
1042  // Record the minimal area needed for the message.
1043  SetMinArea(min_rect.toRect());
1044  }
1045 }
1046 
1048 {
1049  int lineNo = -1;
1050  int lineCount = 0;
1051  int currPos = 0;
1052  int layoutStartPos = 0;
1053  int xPos = 0;
1054 
1055  for (int x = 0; x < m_Layouts.count(); x++)
1056  {
1057  QTextLayout *layout = m_Layouts.at(x);
1058 
1059  for (int y = 0; y < layout->lineCount(); y++)
1060  {
1061  lineCount++;
1062  if (lineNo != -1)
1063  continue;
1064 
1065  QTextLine line = layout->lineAt(y);
1066 
1067  if (m_textCursor >= currPos && m_textCursor < currPos + line.textLength()
1068  + (line.lineNumber() == layout->lineCount() - 1 ? 1 : 0))
1069  {
1070  lineNo = lineCount - 1;
1071  xPos = line.cursorToX(m_textCursor - layoutStartPos);
1072  continue;
1073  }
1074 
1075  currPos += line.textLength();
1076  }
1077 
1078  currPos += 1; // skip the newline
1079  layoutStartPos = currPos;
1080  }
1081 
1082  // are we are at the top and need to scroll up
1083  if (lineNo == 0 && lines < 0)
1084  return -1;
1085 
1086  // are we at the bottom and need to scroll down
1087  if (lineNo == lineCount - 1 && lines > 0)
1088  return -1;
1089 
1090  if (lineNo == -1)
1091  {
1092  LOG(VB_GENERAL, LOG_ERR,
1093  QString("'%1' (%2) MoveCursor offset %3 not found in ANY paragraph!")
1094  .arg(objectName()).arg(GetXMLLocation()).arg(m_textCursor));
1095  return m_textCursor;
1096  }
1097 
1098  int newLine = lineNo + lines;
1099 
1100  if (newLine < 0)
1101  newLine = 0;
1102 
1103  if (newLine >= lineCount)
1104  newLine = lineCount - 1;
1105 
1106  lineNo = -1;
1107  layoutStartPos = 0;
1108 
1109  for (int x = 0; x < m_Layouts.count(); x++)
1110  {
1111  QTextLayout *layout = m_Layouts.at(x);
1112 
1113  for (int y = 0; y < layout->lineCount(); y++)
1114  {
1115  lineNo++;
1116  QTextLine line = layout->lineAt(y);
1117 
1118  if (lineNo == newLine)
1119  return layoutStartPos + line.xToCursor(xPos);
1120  }
1121 
1122  layoutStartPos += layout->text().length() + 1;
1123  }
1124 
1125  // should never reach here
1126  return m_textCursor;
1127 }
1128 
1129 QPoint MythUIText::CursorPosition(int text_offset)
1130 {
1131  if (m_Layouts.empty())
1132  return m_Area.topLeft().toQPoint();
1133 
1134  if (text_offset == m_textCursor)
1135  return m_cursorPos;
1136  m_textCursor = text_offset;
1137 
1138  QVector<QTextLayout *>::const_iterator Ipara = nullptr;
1139  QPoint pos;
1140  int x = 0;
1141  int y = 0;
1142  int offset = text_offset;
1143 
1144  for (Ipara = m_Layouts.constBegin(); Ipara != m_Layouts.constEnd(); ++Ipara)
1145  {
1146  QTextLine line = (*Ipara)->lineForTextPosition(offset);
1147 
1148  if (line.isValid())
1149  {
1150  pos.setX(line.cursorToX(&offset));
1151  pos.setY(line.y());
1152  break;
1153  }
1154  offset -= ((*Ipara)->text().size() + 1); // Account for \n
1155  }
1156  if (Ipara == m_Layouts.constEnd())
1157  {
1158  LOG(VB_GENERAL, LOG_ERR,
1159  QString("'%1' (%2) CursorPosition offset %3 not found in "
1160  "ANY paragraph!")
1161  .arg(objectName()).arg(GetXMLLocation()).arg(text_offset));
1162  return m_Area.topLeft().toQPoint();
1163  }
1164 
1165  int mid = m_drawRect.width() / 2;
1166  if (m_Canvas.width() <= m_drawRect.width() || pos.x() <= mid)
1167  x = 0; // start
1168  else if (pos.x() >= m_Canvas.width() - mid) // end
1169  {
1170  x = m_Canvas.width() - m_drawRect.width();
1171  pos.setX(pos.x() - x);
1172  }
1173  else // middle
1174  {
1175  x = pos.x() - mid;
1176  pos.setX(pos.x() - x);
1177  }
1178 
1179  int line_height = m_lineHeight + m_Leading;
1180  mid = m_Area.height() / 2;
1181  mid -= (mid % line_height);
1182  y = pos.y() - mid;
1183 
1184  if (y <= 0 || m_Canvas.height() <= m_Area.height()) // Top of buffer
1185  y = 0;
1186  else if (y + m_Area.height() > m_Canvas.height()) // Bottom of buffer
1187  {
1188  int visible_lines = ((m_Area.height() / line_height) * line_height);
1189  y = m_Canvas.height() - visible_lines;
1190  pos.setY(visible_lines - (m_Canvas.height() - pos.y()));
1191  }
1192  else // somewhere in the middle
1193  {
1194  y -= m_Leading;
1195  pos.setY(mid + m_Leading);
1196  }
1197 
1198  m_Canvas.moveTopLeft(QPoint(-x, -y));
1199 
1200  // Compensate for vertical alignment
1201  pos.setY(pos.y() + m_drawRect.y() - m_Area.y());
1202 
1203  pos += m_Area.topLeft().toQPoint();
1204  m_cursorPos = pos;
1205 
1206  return pos;
1207 }
1208 
1210 {
1211  //
1212  // Call out base class pulse which will handle any alpha cycling and
1213  // movement
1214  //
1215 
1217 
1218  if (m_colorCycling)
1219  {
1220  m_curR += m_incR;
1221  m_curG += m_incG;
1222  m_curB += m_incB;
1223 
1224  m_curStep++;
1225 
1226  if (m_curStep >= m_numSteps)
1227  {
1228  m_curStep = 0;
1229  m_incR *= -1;
1230  m_incG *= -1;
1231  m_incB *= -1;
1232  }
1233 
1234  QColor newColor = QColor((int)m_curR, (int)m_curG, (int)m_curB);
1235 
1236  if (newColor != m_Font->color())
1237  {
1238  m_Font->SetColor(newColor);
1239  SetRedraw();
1240  }
1241  }
1242 
1243  if (m_scrolling)
1244  {
1245  if (m_scrollPause > 0)
1246  --m_scrollPause;
1247  else
1248  {
1249  if (m_scrollBounce)
1251  else
1253  }
1254 
1255  int whole = static_cast<int>(m_scrollPos);
1256  if (m_scrollPosWhole != whole)
1257  {
1258  int shift = whole - m_scrollPosWhole;
1259  m_scrollPosWhole = whole;
1260 
1261  switch (m_scrollDirection)
1262  {
1263  case ScrollLeft :
1264  if (m_Canvas.width() > m_drawRect.width())
1265  {
1266  ShiftCanvas(-shift, 0);
1267  if (m_Canvas.x() + m_Canvas.width() < 0)
1268  {
1269  SetCanvasPosition(m_drawRect.width(), 0);
1271  }
1272  }
1273  break;
1274  case ScrollRight :
1275  if (m_Canvas.width() > m_drawRect.width())
1276  {
1277  ShiftCanvas(shift, 0);
1278  if (m_Canvas.x() > m_drawRect.width())
1279  {
1280  SetCanvasPosition(-m_Canvas.width(), 0);
1282  }
1283  }
1284  break;
1285  case ScrollHorizontal:
1286  if (m_Canvas.width() <= m_drawRect.width())
1287  break;
1288  if (m_scrollBounce) // scroll right
1289  {
1290  if (m_Canvas.x() + m_scrollOffset > m_drawRect.x())
1291  {
1292  m_scrollBounce = false;
1295  }
1296  else
1297  ShiftCanvas(shift, 0);
1298  }
1299  else // scroll left
1300  {
1301  if (m_Canvas.x() + m_Canvas.width() + m_scrollOffset <
1302  m_drawRect.x() + m_drawRect.width())
1303  {
1304  m_scrollBounce = true;
1307  }
1308  else
1309  ShiftCanvas(-shift, 0);
1310  }
1311  break;
1312  case ScrollUp :
1313  if (m_Canvas.height() > m_drawRect.height())
1314  {
1315  ShiftCanvas(0, -shift);
1316  if (m_Canvas.y() + m_Canvas.height() < 0)
1317  {
1318  SetCanvasPosition(0, m_drawRect.height());
1320  }
1321  }
1322  break;
1323  case ScrollDown :
1324  if (m_Canvas.height() > m_drawRect.height())
1325  {
1326  ShiftCanvas(0, shift);
1327  if (m_Canvas.y() > m_drawRect.height())
1328  {
1329  SetCanvasPosition(0, -m_Canvas.height());
1331  }
1332  }
1333  break;
1334  case ScrollVertical:
1335  if (m_Canvas.height() <= m_drawRect.height())
1336  break;
1337  if (m_scrollBounce) // scroll down
1338  {
1339  if (m_Canvas.y() + m_scrollOffset > m_drawRect.y())
1340  {
1341  m_scrollBounce = false;
1344  }
1345  else
1346  ShiftCanvas(0, shift);
1347  }
1348  else // scroll up
1349  {
1350  if (m_Canvas.y() + m_Canvas.height() + m_scrollOffset <
1351  m_drawRect.y() + m_drawRect.height())
1352  {
1353  m_scrollBounce = true;
1356  }
1357  else
1358  ShiftCanvas(0, -shift);
1359  }
1360  break;
1361  case ScrollNone:
1362  break;
1363  }
1364  }
1365  }
1366 }
1367 
1368 void MythUIText::CycleColor(const QColor& startColor, const QColor& endColor, int numSteps)
1369 {
1370  if (!GetPainter()->SupportsAnimation())
1371  return;
1372 
1373  m_startColor = startColor;
1374  m_endColor = endColor;
1375  m_numSteps = numSteps;
1376  m_curStep = 0;
1377 
1378  m_curR = startColor.red();
1379  m_curG = startColor.green();
1380  m_curB = startColor.blue();
1381 
1382  m_incR = (endColor.red() * 1.0F - m_curR) / m_numSteps;
1383  m_incG = (endColor.green() * 1.0F - m_curG) / m_numSteps;
1384  m_incB = (endColor.blue() * 1.0F - m_curB) / m_numSteps;
1385 
1386  m_colorCycling = true;
1387 }
1388 
1390 {
1391  if (!m_colorCycling)
1392  return;
1393 
1395  m_colorCycling = false;
1396  SetRedraw();
1397 }
1398 
1400  const QString &filename, QDomElement &element, bool showWarnings)
1401 {
1402  if (element.tagName() == "area")
1403  {
1404  SetArea(parseRect(element));
1406  }
1407  // else if (element.tagName() == "altarea") // Unused, but maybe in future?
1408  // m_AltDisplayRect = parseRect(element);
1409  else if (element.tagName() == "font")
1410  {
1411  QString fontname = getFirstText(element);
1412  MythFontProperties *fp = GetFont(fontname);
1413 
1414  if (!fp)
1415  fp = GetGlobalFontMap()->GetFont(fontname);
1416 
1417  if (fp)
1418  {
1419  MythFontProperties font = *fp;
1420  int screenHeight = GetMythMainWindow()->GetUIScreenRect().height();
1421  font.Rescale(screenHeight);
1422  int fontStretch = GetMythUI()->GetFontStretch();
1423  font.AdjustStretch(fontStretch);
1424  QString state = element.attribute("state", "");
1425 
1426  if (!state.isEmpty())
1427  {
1428  m_FontStates.insert(state, font);
1429  }
1430  else
1431  {
1432  m_FontStates.insert("default", font);
1433  *m_Font = m_FontStates["default"];
1434  }
1435  }
1436  }
1437  else if (element.tagName() == "extraleading")
1438  {
1439  m_extraLeading = getFirstText(element).toInt();
1440  }
1441  else if (element.tagName() == "value")
1442  {
1443  if (element.attribute("lang", "").isEmpty())
1444  {
1445  m_Message = qApp->translate("ThemeUI",
1446  parseText(element).toUtf8());
1447  }
1448  else if ((element.attribute("lang", "").toLower() ==
1450  (element.attribute("lang", "").toLower() ==
1452  {
1453  m_Message = parseText(element);
1454  }
1455 
1457  SetText(m_Message);
1458  }
1459  else if (element.tagName() == "template")
1460  {
1461  m_TemplateText = parseText(element);
1462  }
1463  else if (element.tagName() == "cutdown")
1464  {
1465  QString mode = getFirstText(element).toLower();
1466 
1467  if (mode == "left")
1468  SetCutDown(Qt::ElideLeft);
1469  else if (mode == "middle")
1470  SetCutDown(Qt::ElideMiddle);
1471  else if (mode == "right" || parseBool(element))
1472  SetCutDown(Qt::ElideRight);
1473  else
1474  SetCutDown(Qt::ElideNone);
1475  }
1476  else if (element.tagName() == "multiline")
1477  {
1478  SetMultiLine(parseBool(element));
1479  }
1480  else if (element.tagName() == "align")
1481  {
1482  QString align = getFirstText(element).toLower();
1484  }
1485  else if (element.tagName() == "colorcycle")
1486  {
1487  if (GetPainter()->SupportsAnimation())
1488  {
1489  QString tmp = element.attribute("start");
1490 
1491  if (!tmp.isEmpty())
1492  m_startColor = QColor(tmp);
1493 
1494  tmp = element.attribute("end");
1495 
1496  if (!tmp.isEmpty())
1497  m_endColor = QColor(tmp);
1498 
1499  tmp = element.attribute("steps");
1500 
1501  if (!tmp.isEmpty())
1502  m_numSteps = tmp.toInt();
1503 
1504  // initialize the rest of the stuff
1506  }
1507  else
1508  m_colorCycling = false;
1509 
1510  m_colorCycling = parseBool(element.attribute("disable"));
1511  }
1512  else if (element.tagName() == "scroll")
1513  {
1514  if (GetPainter()->SupportsAnimation())
1515  {
1516  QString tmp = element.attribute("direction");
1517 
1518  if (!tmp.isEmpty())
1519  {
1520  tmp = tmp.toLower();
1521 
1522  if (tmp == "left")
1524  else if (tmp == "right")
1526  else if (tmp == "up")
1528  else if (tmp == "down")
1530  else if (tmp == "horizontal")
1532  else if (tmp == "vertical")
1534  else
1535  {
1537  LOG(VB_GENERAL, LOG_ERR,
1538  QString("'%1' (%2) Invalid scroll attribute")
1539  .arg(objectName()).arg(GetXMLLocation()));
1540  }
1541  }
1542 
1543  tmp = element.attribute("startdelay");
1544  if (!tmp.isEmpty())
1545  {
1546  float seconds = tmp.toFloat();
1547  m_scrollStartDelay = lroundf(seconds *
1548  static_cast<float>(MythMainWindow::drawRefresh));
1549  }
1550  tmp = element.attribute("returndelay");
1551  if (!tmp.isEmpty())
1552  {
1553  float seconds = tmp.toFloat();
1554  m_scrollReturnDelay = lroundf(seconds *
1555  static_cast<float>(MythMainWindow::drawRefresh));
1556  }
1557  tmp = element.attribute("rate");
1558  if (!tmp.isEmpty())
1559  {
1560 #if 0 // scroll rate as a percentage of 70Hz
1561  float percent = tmp.toFloat() / 100.0;
1562  m_scrollForwardRate = percent *
1563  static_cast<float>(MythMainWindow::drawRefresh) / 70.0;
1564 #else // scroll rate as pixels per second
1565  int pixels = tmp.toInt();
1567  pixels / static_cast<float>(MythMainWindow::drawRefresh);
1568 #endif
1569  }
1570  tmp = element.attribute("returnrate");
1571  if (!tmp.isEmpty())
1572  {
1573 #if 0 // scroll rate as a percentage of 70Hz
1574  float percent = tmp.toFloat() / 100.0;
1575  m_scrollReturnRate = percent *
1576  static_cast<float>(MythMainWindow::drawRefresh) / 70.0;
1577 #else // scroll rate as pixels per second
1578  int pixels = tmp.toInt();
1580  pixels / static_cast<float>(MythMainWindow::drawRefresh);
1581 #endif
1582  }
1583 
1584  m_scrolling = true;
1585  }
1586  else
1587  m_scrolling = false;
1588  }
1589  else if (element.tagName() == "case")
1590  {
1591  QString stringCase = getFirstText(element).toLower();
1592 
1593  if (stringCase == "lower")
1595  else if (stringCase == "upper")
1597  else if (stringCase == "capitalisefirst")
1599  else if (stringCase == "capitaliseall")
1601  else
1603  }
1604  else
1605  {
1606  if (element.tagName() == "minsize" && element.hasAttribute("shrink"))
1607  {
1608  m_ShrinkNarrow = (element.attribute("shrink")
1609  .toLower() != "short");
1610  }
1611 
1612  return MythUIType::ParseElement(filename, element, showWarnings);
1613  }
1614 
1615  return true;
1616 }
1617 
1619 {
1620  auto *text = dynamic_cast<MythUIText *>(base);
1621 
1622  if (!text)
1623  {
1624  LOG(VB_GENERAL, LOG_ERR,
1625  QString("'%1' (%2) ERROR, bad parsing '%3' (%4)")
1626  .arg(objectName()).arg(GetXMLLocation())
1627  .arg(base->objectName()).arg(base->GetXMLLocation()));
1628  return;
1629  }
1630 
1631  m_Justification = text->m_Justification;
1632  m_OrigDisplayRect = text->m_OrigDisplayRect;
1633  m_AltDisplayRect = text->m_AltDisplayRect;
1634  m_Canvas = text->m_Canvas;
1635  m_drawRect = text->m_drawRect;
1636 
1637  m_DefaultMessage = text->m_DefaultMessage;
1638  SetText(text->m_Message);
1639  m_CutMessage = text->m_CutMessage;
1640  m_TemplateText = text->m_TemplateText;
1641 
1642  m_ShrinkNarrow = text->m_ShrinkNarrow;
1643  m_Cutdown = text->m_Cutdown;
1644  m_MultiLine = text->m_MultiLine;
1645  m_Leading = text->m_Leading;
1646  m_extraLeading = text->m_extraLeading;
1647  m_lineHeight = text->m_lineHeight;
1648  m_textCursor = text->m_textCursor;
1649 
1650  QMutableMapIterator<QString, MythFontProperties> it(text->m_FontStates);
1651 
1652  while (it.hasNext())
1653  {
1654  it.next();
1655  m_FontStates.insert(it.key(), it.value());
1656  }
1657 
1658  *m_Font = m_FontStates["default"];
1659 
1660  m_colorCycling = text->m_colorCycling;
1661  m_startColor = text->m_startColor;
1662  m_endColor = text->m_endColor;
1663  m_numSteps = text->m_numSteps;
1664  m_curStep = text->m_curStep;
1665  m_curR = text->m_curR;
1666  m_curG = text->m_curG;
1667  m_curB = text->m_curB;
1668  m_incR = text->m_incR;
1669  m_incG = text->m_incG;
1670  m_incB = text->m_incB;
1671 
1672  m_scrollStartDelay = text->m_scrollStartDelay;
1673  m_scrollReturnDelay = text->m_scrollReturnDelay;
1674  m_scrollForwardRate = text->m_scrollForwardRate;
1675  m_scrollReturnRate = text->m_scrollReturnRate;
1676  m_scrollDirection = text->m_scrollDirection;
1677  m_scrolling = text->m_scrolling;
1678 
1679  m_textCase = text->m_textCase;
1680 
1681  MythUIType::CopyFrom(base);
1682  FillCutMessage();
1683 }
1684 
1686 {
1687  auto *text = new MythUIText(parent, objectName());
1688  text->CopyFrom(this);
1689 }
1690 
1691 
1693 {
1694  if (m_scrolling && m_Cutdown != Qt::ElideNone)
1695  {
1696  LOG(VB_GENERAL, LOG_ERR,
1697  QString("'%1' (%2): <scroll> and <cutdown> are not combinable.")
1698  .arg(objectName()).arg(GetXMLLocation()));
1699  m_Cutdown = Qt::ElideNone;
1700  }
1701  FillCutMessage();
1702 }
QString GetLanguageAndVariant(void)
Returns the user-set language and variant.
QPoint toQPoint(void) const
Definition: mythrect.cpp:514
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:233
float m_scrollPos
Definition: mythuitext.h:165
QColor m_endColor
Definition: mythuitext.h:143
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()
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:295
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
int CalcAlpha(int alphamod)
Definition: mythuitype.cpp:460
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
const char * formats[8]
Definition: vbilut.cpp:190
static guint32 * tmp
Definition: goom_core.c:35
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:519
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:463
QRect toQRect(void) const
Definition: mythrect.cpp:354
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:95
void setX(const QString &sX)
Definition: mythrect.cpp:202
virtual void SetMinArea(const MythRect &rect)
Set the minimum area based on the given size.
Definition: mythuitype.cpp:801
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:442
bool m_Initiator
Definition: mythuitype.h:241
void setY(const QString &sY)
Definition: mythrect.cpp:212
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 moveLeft(const QString &sX)
Definition: mythrect.cpp:258
MythFontProperties * m_Font
Definition: mythuitext.h:138
void ResetMap(const InfoMap &map)
Definition: mythuitext.cpp:97
void FillCutMessage(void)
Definition: mythuitext.cpp:811
void AdjustStretch(int stretch)
MythRect m_Area
Definition: mythuitype.h:249
int GetFontStretch(void) const
void setWidth(const QString &sWidth)
Definition: mythrect.cpp:222
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:581
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:591
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
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
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:98
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:683
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:722
MythPoint topLeft(void) const
Definition: mythrect.cpp:244
bool m_scrollBounce
Definition: mythuitext.h:163
bool m_scrolling
Definition: mythuitext.h:168
void moveTopLeft(const MythPoint &point)
Definition: mythrect.cpp:252
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:86
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
void SetFontState(const QString &)
Definition: mythuitext.cpp:224
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:268
void NormPoint(void)
Definition: mythrect.cpp:391
static bool parseBool(const QString &text)
QPoint CursorPosition(int text_offset)
int GetJustification(void)
Definition: mythuitext.cpp:282
void ShiftCanvas(int x, int y)
Definition: mythuitext.cpp:352
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