MythTV  master
Text.cpp
Go to the documentation of this file.
1 /* Text.cpp
2 
3  Copyright (C) David C. J. Matthews 2004, 2008 dm at prolingua.co.uk
4 
5  This program is free software; you can redistribute it and/or
6  modify it under the terms of the GNU General Public License
7  as published by the Free Software Foundation; either version 2
8  of the License, or (at your option) any later version.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program; if not, write to the Free Software
17  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
18  Or, point your browser to http://www.gnu.org/copyleft/gpl.html
19 
20 */
21 #include "libmythbase/compat.h"
22 
23 #include "Text.h"
24 #include "Visible.h"
25 #include "Presentable.h"
26 #include "Ingredients.h"
27 #include "Root.h"
28 #include "BaseClasses.h"
29 #include "ParseNode.h"
30 #include "ASN1Codes.h"
31 #include "Engine.h"
32 #include "Logging.h"
33 #include "freemheg.h"
34 
35 MHText::MHText(const MHText &ref): MHVisible(ref) // Copy constructor for cloning.
36 {
41  m_nCharSet = ref.m_nCharSet;
42  m_horizJ = ref.m_horizJ;
43  m_vertJ = ref.m_vertJ;
47  m_pDisplay = nullptr;
49 }
50 
52 {
53  delete(m_pDisplay);
54 }
55 
56 
58 {
59  MHVisible::Initialise(p, engine);
60  // Font and attributes.
61  MHParseNode *pFontBody = p->GetNamedArg(C_ORIGINAL_FONT);
62 
63  if (pFontBody)
64  {
65  m_origFont.Initialise(pFontBody->GetArgN(0), engine);
66  }
67 
68  MHParseNode *pFontAttrs = p->GetNamedArg(C_FONT_ATTRIBUTES);
69 
70  if (pFontAttrs)
71  {
73  }
74 
75  // Colours
76  MHParseNode *pTextColour = p->GetNamedArg(C_TEXT_COLOUR);
77 
78  if (pTextColour)
79  {
80  m_originalTextColour.Initialise(pTextColour->GetArgN(0), engine);
81  }
82 
83  MHParseNode *pBGColour = p->GetNamedArg(C_BACKGROUND_COLOUR);
84 
85  if (pBGColour)
86  {
87  m_originalBgColour.Initialise(pBGColour->GetArgN(0), engine);
88  }
89 
90  // Character set
91  MHParseNode *pChset = p->GetNamedArg(C_CHARACTER_SET);
92 
93  if (pChset)
94  {
95  m_nCharSet = pChset->GetArgN(0)->GetIntValue();
96  }
97 
98  // Justification
99  MHParseNode *pHJust = p->GetNamedArg(C_HORIZONTAL_JUSTIFICATION);
100 
101  if (pHJust)
102  {
103  m_horizJ = (enum Justification)pHJust->GetArgN(0)->GetEnumValue();
104  }
105 
106  MHParseNode *pVJust = p->GetNamedArg(C_VERTICAL_JUSTIFICATION);
107 
108  if (pVJust)
109  {
110  m_vertJ = (enum Justification)pVJust->GetArgN(0)->GetEnumValue();
111  }
112 
113  // Line orientation
114  MHParseNode *pLineO = p->GetNamedArg(C_LINE_ORIENTATION);
115 
116  if (pLineO)
117  {
119  }
120 
121  // Start corner
122  MHParseNode *pStartC = p->GetNamedArg(C_START_CORNER);
123 
124  if (pStartC)
125  {
126  m_startCorner = (enum StartCorner)pStartC->GetArgN(0)->GetEnumValue();
127  }
128 
129  // Text wrapping
130  MHParseNode *pTextWrap = p->GetNamedArg(C_TEXT_WRAPPING);
131 
132  if (pTextWrap)
133  {
134  m_fTextWrap = pTextWrap->GetArgN(0)->GetBoolValue();
135  }
136 
137  m_pDisplay = engine->GetContext()->CreateText();
138  m_fNeedsRedraw = true;
139 }
140 
141 static const std::array<const QString,4> rchJustification
142 {
143  "start", // 1
144  "end",
145  "centre",
146  "justified" // 4
147 };
148 
149 // Look up the Justification. Returns zero if it doesn't match. Used in the text parser only.
150 int MHText::GetJustification(const QString& str)
151 {
152  for (size_t i = 0; i < rchJustification.size(); i++)
153  {
154  if (str.compare(rchJustification[i], Qt::CaseInsensitive) == 0)
155  {
156  return (i + 1); // Numbered from 1
157  }
158  }
159 
160  return 0;
161 }
162 
163 static const std::array<const QString,2> rchlineOrientation
164 {
165  "vertical", // 1
166  "horizontal"
167 };
168 
169 int MHText::GetLineOrientation(const QString& str)
170 {
171  for (size_t i = 0; i < rchlineOrientation.size(); i++)
172  {
173  if (str.compare(rchlineOrientation[i], Qt::CaseInsensitive) == 0)
174  {
175  return (i + 1);
176  }
177  }
178 
179  return 0;
180 }
181 
182 static const std::array<const QString,4> rchStartCorner
183 {
184  "upper-left", // 1
185  "upper-right",
186  "lower-left",
187  "lower-right" // 4
188 };
189 
190 int MHText::GetStartCorner(const QString& str)
191 {
192  for (size_t i = 0; i < rchStartCorner.size(); i++)
193  {
194  if (str.compare(rchStartCorner[i], Qt::CaseInsensitive) == 0)
195  {
196  return (i + 1);
197  }
198  }
199 
200  return 0;
201 }
202 
203 void MHText::PrintMe(FILE *fd, int nTabs) const
204 {
205  PrintTabs(fd, nTabs);
206  fprintf(fd, "{:Text ");
207  MHVisible::PrintMe(fd, nTabs + 1);
208 
209  if (m_origFont.IsSet())
210  {
211  PrintTabs(fd, nTabs + 1);
212  fprintf(fd, ":OrigFont ");
213  m_origFont.PrintMe(fd, nTabs + 1);
214  fprintf(fd, "\n");
215  }
216 
217  if (m_originalFontAttrs.Size() > 0)
218  {
219  PrintTabs(fd, nTabs + 1);
220  fprintf(fd, ":FontAttributes ");
221  m_originalFontAttrs.PrintMe(fd, nTabs + 1);
222  fprintf(fd, "\n");
223  }
224 
226  {
227  PrintTabs(fd, nTabs + 1);
228  fprintf(fd, ":TextColour ");
229  m_originalTextColour.PrintMe(fd, nTabs + 1);
230  fprintf(fd, "\n");
231  }
232 
234  {
235  PrintTabs(fd, nTabs + 1);
236  fprintf(fd, ":BackgroundColour ");
237  m_originalBgColour.PrintMe(fd, nTabs + 1);
238  fprintf(fd, "\n");
239  }
240 
241  if (m_nCharSet >= 0)
242  {
243  PrintTabs(fd, nTabs + 1);
244  fprintf(fd, ":CharacterSet %d\n", m_nCharSet);
245  }
246 
247  if (m_horizJ != Start)
248  {
249  PrintTabs(fd, nTabs + 1);
250  fprintf(fd, ":HJustification %s\n", qPrintable(rchJustification[m_horizJ-1]));
251  }
252 
253  if (m_vertJ != Start)
254  {
255  PrintTabs(fd, nTabs + 1);
256  fprintf(fd, ":VJustification %s\n", qPrintable(rchJustification[m_vertJ-1]));
257  }
258 
260  {
261  PrintTabs(fd, nTabs + 1);
262  fprintf(fd, ":LineOrientation %s\n", qPrintable(rchlineOrientation[m_lineOrientation-1]));
263  }
264 
265  if (m_startCorner != UpperLeft)
266  {
267  PrintTabs(fd, nTabs + 1);
268  fprintf(fd, ":StartCorner %s\n", qPrintable(rchStartCorner[m_startCorner-1]));
269  }
270 
271  if (m_fTextWrap)
272  {
273  PrintTabs(fd, nTabs + 1);
274  fprintf(fd, ":TextWrapping true\n");
275  }
276 
277  PrintTabs(fd, nTabs);
278  fprintf(fd, "}\n");
279 }
280 
282 {
283  if (m_fAvailable)
284  {
285  return;
286  }
287 
288  // Set the colours and font up from the originals if specified otherwise use the application defaults.
289  // if (m_origFont.IsSet()) m_font.Copy(m_origFont);
290  // else m_font.Copy(engine->m_DefaultFont);
292  {
294  }
295  else
296  {
298  }
299 
301  {
303  }
304  else
305  {
307  }
308 
309  if (m_originalFontAttrs.Size() > 0)
310  {
312  }
313  else
314  {
316  }
317 
318  MHVisible::Preparation(engine);
319 
320  if (m_pDisplay == nullptr)
321  {
322  m_pDisplay = engine->GetContext()->CreateText();
323  }
324 
326  m_fNeedsRedraw = true;
327 }
328 
329 // Content preparation. If it's included we can set up the content.
331 {
333 
334  if (m_contentType == IN_NoContent)
335  {
336  MHERROR("Text object must have content");
337  }
338 
339  if (m_contentType == IN_IncludedContent)
340  {
342  }
343 }
344 
345 // Called when external content is available.
346 void MHText::ContentArrived(const unsigned char *data, int length, MHEngine *engine)
347 {
348  CreateContent(data, length, engine);
349  MHLOG(MHLogDetail, QString("Content arrived %1 := '%2'")
351  // Now signal that the content is available.
352  engine->EventTriggered(this, EventContentAvailable);
353  m_fNeedsRedraw = true;
354 }
355 
356 //
357 void MHText::CreateContent(const unsigned char *p, int s, MHEngine *engine)
358 {
359  m_content.Copy(MHOctetString((const char *)p, s));
360  engine->Redraw(GetVisibleArea()); // Have to redraw if the content has changed.
361  m_fNeedsRedraw = true;
362  // fprintf(fd, "Text content is now "); m_content.PrintMe(0); fprintf(fd, "\n");
363 }
364 
365 void MHText::SetTextColour(const MHColour &colour, MHEngine *engine)
366 {
367  m_textColour.Copy(colour);
368  m_fNeedsRedraw = true;
369  engine->Redraw(GetVisibleArea());
370 }
371 
372 void MHText::SetBackgroundColour(const MHColour &colour, MHEngine *engine)
373 {
374  m_bgColour.Copy(colour);
375  // Setting the background colour doesn't affect the text image but we have to
376  // redraw it onto the display.
377  engine->Redraw(GetVisibleArea());
378 }
379 
380 void MHText::SetFontAttributes(const MHOctetString &fontAttrs, MHEngine *engine)
381 {
382  m_fontAttrs.Copy(fontAttrs);
383  m_fNeedsRedraw = true;
384  engine->Redraw(GetVisibleArea());
385 }
386 
387 // UK MHEG. Interpret the font attributes.
388 static void InterpretAttributes(const MHOctetString &attrs, int &style, int &size, int &lineSpace, int &letterSpace)
389 {
390  // Set the defaults.
391  style = 0;
392  size = 0x18;
393  lineSpace = 0x18;
394  letterSpace = 0;
395 
396  if (attrs.Size() == 5) // Short form.
397  {
398  style = attrs.GetAt(0); // Only the bottom nibble is significant.
399  size = attrs.GetAt(1);
400  lineSpace = attrs.GetAt(2);
401  // Is this big-endian or little-endian? Assume big.
402  letterSpace = attrs.GetAt(3) * 256 + attrs.GetAt(4);
403 
404  if (letterSpace > 32767)
405  {
406  letterSpace -= 65536; // Signed.
407  }
408  }
409  else // Textual form.
410  {
411  const unsigned char *str = attrs.Bytes();
412  char *p = (char *)str;
413  char *q = strchr(p, '.'); // Find the terminating dot
414 
415  if (q != nullptr) // plain, italic etc.
416  {
417  if (q - p == 6 && strncmp(p, "italic", 6) == 0)
418  {
419  style = 1;
420  }
421  else if (q - p == 4 && strncmp(p, "bold", 4) == 0)
422  {
423  style = 2;
424  }
425  else if (q - p == 11 && strncmp(p, "bold-italic", 11) == 0)
426  {
427  style = 3;
428  }
429 
430  // else it's plain.
431  p = q + 1;
432  q = strchr(p, '.'); // Find the next dot.
433  }
434 
435  if (q != nullptr) // Size
436  {
437  size = atoi(p);
438 
439  if (size == 0)
440  {
441  size = 0x18;
442  }
443 
444  p = q + 1;
445  q = strchr(p, '.'); // Find the next dot.
446  }
447 
448  if (q != nullptr) // lineSpacing
449  {
450  lineSpace = atoi(p);
451 
452  if (lineSpace == 0)
453  {
454  lineSpace = 0x18;
455  }
456 
457  p = q + 1;
458  q = strchr(p, '.'); // Find the next dot.
459  }
460 
461  if (q != nullptr) // letter spacing. May be zero or negative
462  {
463  letterSpace = atoi(p);
464  }
465  }
466 }
467 
468 // We break the text up into pieces of line segment.
470 {
471  public:
472  MHTextItem();
473  MHOctetString m_text; // UTF-8 text
474  QString m_unicode; // Unicode text
475  int m_nUnicode; // Number of characters in it
476  int m_width; // Size of this block
477  MHRgba m_colour; // Colour of the text
478  int m_nTabCount; // Number of tabs immediately before this (usually zero)
479 
480  // Generate new items inheriting properties from the previous
481  MHTextItem *NewItem() const;
482 };
483 
485 {
486  m_nUnicode = 0;
487  m_width = 0; // Size of this block
488  m_colour = MHRgba(0, 0, 0, 255);
489  m_nTabCount = 0;
490 }
491 
493 {
494  auto *pItem = new MHTextItem;
495  pItem->m_colour = m_colour;
496  return pItem;
497 }
498 
499 // A line consists of one or more sequences of text items.
501 {
502  public:
503  MHTextLine() = default;
504  ~MHTextLine();
506  int m_nLineWidth {0};
507  int m_nLineHeight {0};
508  int m_nDescent {0};
509 };
510 
512 {
513  for (int i = 0; i < m_items.Size(); i++)
514  {
515  delete(m_items.GetAt(i));
516  }
517 }
518 
519 // Tabs are set every 56 pixels
520 // = (FONT_WIDTHRES * 56)/ 72 points - see libmythtv/mhi.cpp
521 // = (54 * 56)/72 = 42
522 static constexpr int8_t TABSTOP { 42 }; // pts
523 static inline int Tabs(int nXpos, int nTabCount)
524 {
525  int nNextTab = nXpos;
526  if (nTabCount-- > 0)
527  {
528  nNextTab += TABSTOP - nXpos % TABSTOP;
529  nNextTab += nTabCount * TABSTOP;
530  }
531  return nNextTab;
532 }
533 
534 // I attempted to use QSimpleRichText but that does not give sufficient fine control over
535 // the layout. UK MHEG specifies the use of the Tiresias font and broadcasters appear to
536 // assume that all MHEG applications will lay the text out in the same way.
537 
538 // Recreate the image.
540 {
541  if (! m_fRunning || !m_pDisplay)
542  {
543  return;
544  }
545 
546  if (m_nBoxWidth == 0 || m_nBoxHeight == 0)
547  {
548  return; // Can't draw zero sized boxes.
549  }
550 
552  m_pDisplay->Clear();
553 
554  MHRgba textColour = GetColour(m_textColour);
555  // Process any escapes in the text and construct the text arrays.
557  // Set up the first item on the first line.
558  auto *pCurrItem = new MHTextItem;
559  auto *pCurrLine = new MHTextLine;
560  pCurrLine->m_items.Append(pCurrItem);
561  theText.Append(pCurrLine);
562  MHStack <MHRgba> colourStack; // Stack to handle nested colour codes.
563  colourStack.Push(textColour);
564  pCurrItem->m_colour = textColour;
565 
566 // FILE *fd=stdout; fprintf(fd, "Redraw Text "); m_content.PrintMe(fd, 0); fprintf(fd, "\n");
567  int i = 0;
568 
569  while (i < m_content.Size())
570  {
571  unsigned char ch = m_content.GetAt(i++);
572 
573  if (ch == 0x09) // Tab - start a new item if we have any text in the existing one.
574  {
575  if (pCurrItem->m_text.Size() != 0)
576  {
577  pCurrItem = pCurrItem->NewItem();
578  pCurrLine->m_items.Append(pCurrItem);
579  }
580  if (m_horizJ == Start)
581  pCurrItem->m_nTabCount++;
582  }
583 
584  else if (ch == 0x0d) // CR - line break.
585  {
586  // TODO: Two CRs next to one another are treated as </P> rather than <BR><BR>
587  // This should also include the sequence CRLFCRLF.
588  pCurrLine = new MHTextLine;
589  theText.Append(pCurrLine);
590  pCurrItem = pCurrItem->NewItem();
591  pCurrLine->m_items.Append(pCurrItem);
592  }
593 
594  else if (ch == 0x1b) // Escape - special codes.
595  {
596  if (i == m_content.Size())
597  {
598  break;
599  }
600 
601  unsigned char code = m_content.GetAt(i);
602  // The only codes we are interested in are the start and end of colour.
603  // TODO: We may also need "bold" and some hypertext colours.
604 
605  if (code >= 0x40 && code <= 0x5e) // Start code
606  {
607  // Start codes are followed by a parameter count and a number of parameter bytes.
608  if (++i == m_content.Size())
609  {
610  break;
611  }
612 
613  unsigned char paramCount = m_content.GetAt(i);
614  i++;
615 
616  if (code == 0x43 && paramCount == 4 && i + paramCount <= m_content.Size())
617  {
618  // Start of colour.
619  if (pCurrItem->m_text.Size() != 0)
620  {
621  pCurrItem = pCurrItem->NewItem();
622  pCurrLine->m_items.Append(pCurrItem);
623  }
624 
625  pCurrItem->m_colour = MHRgba(m_content.GetAt(i), m_content.GetAt(i + 1),
626  m_content.GetAt(i + 2), 255 - m_content.GetAt(i + 3));
627  // Push this colour onto the colour stack.
628  colourStack.Push(pCurrItem->m_colour);
629  }
630  else
631  {
632  MHLOG(MHLogWarning, QString("Unknown text escape code 0x%1").arg(code, 2, 16));
633  }
634 
635  i += paramCount; // Skip the parameters
636  }
637  else if (code >= 0x60 && code <= 0x7e) // End code.
638  {
639  i++;
640 
641  if (code == 0x63)
642  {
643  if (colourStack.Size() > 1)
644  {
645  colourStack.Pop();
646 
647  // Start a new item since we're using a new colour.
648  if (pCurrItem->m_text.Size() != 0)
649  {
650  pCurrItem = pCurrItem->NewItem();
651  pCurrLine->m_items.Append(pCurrItem);
652  }
653 
654  // Set the subsequent text in the colour we're using now.
655  pCurrItem->m_colour = colourStack.Top();
656  }
657  }
658  else MHLOG(MHLogWarning, QString("Unknown text escape code 0x%1").arg(code,2,16));
659  }
660  else MHLOG(MHLogWarning, QString("Unknown text escape code 0x%1").arg(code,2,16));
661  }
662 
663  else if (ch <= 0x1f)
664  {
665  // Certain characters including LF and the marker codes between 0x1c and 0x1f are
666  // explicitly intended to be ignored. Include all the other codes.
667  }
668 
669  else // Add to the current text.
670  {
671  int nStart = i - 1;
672 
673  while (i < m_content.Size() && m_content.GetAt(i) >= 0x20)
674  {
675  i++;
676  }
677 
678  pCurrItem->m_text.Append(MHOctetString(m_content, nStart, i - nStart));
679  }
680  }
681 
682  // Set up the initial attributes.
683  int style = 0;
684  int size = 0;
685  int lineSpace = 0;
686  int letterSpace = 0;
687  InterpretAttributes(m_fontAttrs, style, size, lineSpace, letterSpace);
688  // Create a font with this information.
689  m_pDisplay->SetFont(size, (style & 2) != 0, (style & 1) != 0);
690 
691  // Calculate the layout of each section.
692  for (i = 0; i < theText.Size(); i++)
693  {
694  MHTextLine *pLine = theText.GetAt(i);
695  pLine->m_nLineWidth = 0;
696 
697  for (int j = 0; j < pLine->m_items.Size(); j++)
698  {
699  MHTextItem *pItem = pLine->m_items.GetAt(j);
700 
701  // Set any tabs.
702  pLine->m_nLineWidth = Tabs(pLine->m_nLineWidth, pItem->m_nTabCount);
703 
704  if (pItem->m_unicode.isEmpty()) // Convert UTF-8 to Unicode.
705  {
706  int s = pItem->m_text.Size();
707  pItem->m_unicode = QString::fromUtf8((const char *)pItem->m_text.Bytes(), s);
708  pItem->m_nUnicode = pItem->m_unicode.length();
709  }
710 
711  // Fit the text onto the line.
712  int nFullText = pItem->m_nUnicode;
713  // Get the box size and update pItem->m_nUnicode to the number that will fit.
714  QRect rect = m_pDisplay->GetBounds(pItem->m_unicode, pItem->m_nUnicode, m_nBoxWidth - pLine->m_nLineWidth);
715 
716  if (nFullText != pItem->m_nUnicode && m_fTextWrap) // Doesn't fit, we have to word-wrap.
717  {
718  int nTruncated = pItem->m_nUnicode; // Just in case.
719  // Now remove characters until we find a word-break character.
720  while (pItem->m_nUnicode > 0 && pItem->m_unicode[pItem->m_nUnicode] != ' ')
721  {
722  pItem->m_nUnicode--;
723  }
724 
725  // If there are now word-break characters we truncate the text.
726  if (pItem->m_nUnicode == 0)
727  {
728  pItem->m_nUnicode = nTruncated;
729  }
730 
731  // Special case to avoid infinite loop if the box is very narrow.
732  if (pItem->m_nUnicode == 0)
733  {
734  pItem->m_nUnicode = 1;
735  }
736 
737  // We need to move the text we've cut off this line into a new line.
738  int nNewWidth = nFullText - pItem->m_nUnicode;
739  int nNewStart = pItem->m_nUnicode;
740 
741  // Remove any spaces at the start of the new section.
742  while (nNewWidth != 0 && pItem->m_unicode[nNewStart] == ' ')
743  {
744  nNewStart++;
745  nNewWidth--;
746  }
747 
748  if (nNewWidth != 0)
749  {
750  // Create a new line from the extra text.
751  auto *pNewLine = new MHTextLine;
752  theText.InsertAt(pNewLine, i + 1);
753  // The first item on the new line is the rest of the text.
754  MHTextItem *pNewItem = pItem->NewItem();
755  pNewLine->m_items.Append(pNewItem);
756  pNewItem->m_unicode = pItem->m_unicode.mid(nNewStart, nNewWidth);
757  pNewItem->m_nUnicode = nNewWidth;
758 
759  // Move any remaining items, e.g. in a different colour, from this line onto the new line.
760  while (pLine->m_items.Size() > j + 1)
761  {
762  pNewLine->m_items.Append(pLine->m_items.GetAt(j + 1));
763  pLine->m_items.RemoveAt(j + 1);
764  }
765  }
766 
767  // Remove any spaces at the end of the old section. If we don't do that and
768  // we are centering or right aligning the text we'll get it wrong.
769  while (pItem->m_nUnicode > 1 && pItem->m_unicode[pItem->m_nUnicode-1] == ' ')
770  {
771  pItem->m_nUnicode--;
772  }
773 
774  rect = m_pDisplay->GetBounds(pItem->m_unicode, pItem->m_nUnicode);
775  }
776 
777  pItem->m_width = rect.width();
778  pLine->m_nLineWidth += rect.width();
779 
780  if (rect.height() > pLine->m_nLineHeight)
781  {
782  pLine->m_nLineHeight = rect.height();
783  }
784 
785  if (rect.bottom() > pLine->m_nDescent)
786  {
787  pLine->m_nDescent = rect.bottom();
788  }
789  }
790  }
791 
792  // Now output the text.
793  int yOffset = 0;
794  // If there isn't space for all the lines we should drop extra lines.
795  int nNumLines = theText.Size();
796 
797  do
798  {
799  if (m_vertJ == End)
800  {
801  yOffset = m_nBoxHeight - nNumLines * lineSpace;
802  }
803  else if (m_vertJ == Centre)
804  {
805  yOffset = (m_nBoxHeight - nNumLines * lineSpace) / 2;
806  }
807 
808  if (yOffset < 0)
809  {
810  nNumLines--;
811  }
812  }
813  while (yOffset < 0);
814 
815  for (i = 0; i < nNumLines; i++)
816  {
817  MHTextLine *pLine = theText.GetAt(i);
818  int xOffset = 0;
819 
820  if (m_horizJ == End)
821  {
822  xOffset = m_nBoxWidth - pLine->m_nLineWidth;
823  }
824  else if (m_horizJ == Centre)
825  {
826  xOffset = (m_nBoxWidth - pLine->m_nLineWidth) / 2;
827  }
828 
829  for (int j = 0; j < pLine->m_items.Size(); j++)
830  {
831  MHTextItem *pItem = pLine->m_items.GetAt(j);
832 
833  // Tab across if necessary.
834  xOffset = Tabs(xOffset, pItem->m_nTabCount);
835 
836  if (! pItem->m_unicode.isEmpty()) // We may have blank lines.
837  {
838  m_pDisplay->AddText(xOffset, yOffset + (pLine->m_nLineHeight + lineSpace) / 2 - pLine->m_nDescent,
839  pItem->m_unicode.left(pItem->m_nUnicode), pItem->m_colour);
840  }
841 
842  xOffset += pItem->m_width;
843  }
844 
845  yOffset += lineSpace;
846 
847  if (yOffset + lineSpace > m_nBoxHeight)
848  {
849  break;
850  }
851  }
852 
853  // Clean up.
854  for (int k = 0; k < theText.Size(); k++)
855  {
856  delete(theText.GetAt(k));
857  }
858 }
859 
861 {
862  if (! m_fRunning || ! m_pDisplay || m_nBoxWidth == 0 || m_nBoxHeight == 0)
863  {
864  return; // Can't draw zero sized boxes.
865  }
866 
867  // We only need to recreate the display if something has changed.
868  if (m_fNeedsRedraw)
869  {
870  Redraw();
871  m_fNeedsRedraw = false;
872  }
873 
874  // Draw the background first, then the text.
877 }
878 
879 // Return the area actually obscured. This is empty unless the background is opaque.
881 {
882  if (! m_fRunning || (GetColour(m_bgColour)).alpha() != 255)
883  {
884  return {};
885  }
886  return {QRect(m_nPosX, m_nPosY, m_nBoxWidth, m_nBoxHeight)};
887 }
888 
889 
891 {
892  MHText::Initialise(p, engine);
893  MHInteractible::Initialise(p, engine);
894  //
895 }
896 
897 void MHHyperText::PrintMe(FILE *fd, int nTabs) const
898 {
899  PrintTabs(fd, nTabs);
900  fprintf(fd, "{:HyperText ");
901  MHText::PrintMe(fd, nTabs + 1);
902  MHInteractible::PrintMe(fd, nTabs + 1);
903  fprintf(fd, "****TODO\n");
904  PrintTabs(fd, nTabs);
905  fprintf(fd, "}\n");
906 }
907 
908 
910 {
911  MHElemAction::Initialise(p, engine); // Target
912  m_fontAttrs.Initialise(p->GetArgN(1), engine); // New font attrs
913 }
914 
916 {
917  // Get the new font attributes.
918  MHOctetString newAttrs;
919  m_fontAttrs.GetValue(newAttrs, engine);
920  Target(engine)->SetFontAttributes(newAttrs, engine);
921 }
MHTextDisplay::AddText
virtual void AddText(int x, int y, const QString &, MHRgba colour)=0
MHContext::CreateText
virtual MHTextDisplay * CreateText(void)=0
MHTextDisplay::Clear
virtual void Clear(void)=0
MHHyperText::PrintMe
void PrintMe(FILE *fd, int nTabs) const override
Definition: Text.cpp:897
Presentable.h
MHEngine::GetDefaultBGColour
void GetDefaultBGColour(MHColour &colour)
Definition: Engine.cpp:1359
MHStack::Push
void Push(BASE b)
Definition: BaseClasses.h:94
EventContentAvailable
@ EventContentAvailable
Definition: Root.h:33
MHTextItem::m_colour
MHRgba m_colour
Definition: Text.cpp:477
MHTextLine::MHTextLine
MHTextLine()=default
MHRoot::m_fAvailable
bool m_fAvailable
Definition: Root.h:251
MHEngine::GetContext
MHContext * GetContext()
Definition: Engine.h:154
ASN1Codes.h
MHText::End
@ End
Definition: Text.h:79
MHEngine
Definition: Engine.h:72
MHParseNode::GetIntValue
int GetIntValue()
Definition: ParseNode.cpp:170
MHVisible::GetColour
static MHRgba GetColour(const MHColour &colour)
Definition: Visible.cpp:156
MHText
Definition: Text.h:37
MHTextItem::NewItem
MHTextItem * NewItem() const
Definition: Text.cpp:492
MHText::ContentPreparation
void ContentPreparation(MHEngine *engine) override
Definition: Text.cpp:330
MHText::LineOrientation
LineOrientation
Definition: Text.h:80
rchStartCorner
static const std::array< const QString, 4 > rchStartCorner
Definition: Text.cpp:183
MHOctetString::GetAt
unsigned char GetAt(int i) const
Definition: BaseClasses.h:124
MHOctetString::Bytes
const unsigned char * Bytes() const
Definition: BaseClasses.h:125
MHSetFontAttributes::Initialise
void Initialise(MHParseNode *p, MHEngine *engine) override
Definition: Text.cpp:909
MHText::m_content
MHOctetString m_content
Definition: Text.h:92
MHVisible::PrintMe
void PrintMe(FILE *fd, int nTabs) const override
Definition: Visible.cpp:84
MHTextDisplay::SetSize
virtual void SetSize(int width, int height)=0
MHIngredient::m_includedContent
MHOctetString m_includedContent
Definition: Ingredients.h:77
C_TEXT_WRAPPING
@ C_TEXT_WRAPPING
Definition: ASN1Codes.h:127
MHText::UpperLeft
@ UpperLeft
Definition: Text.h:81
MHText::m_pDisplay
MHTextDisplay * m_pDisplay
Definition: Text.h:94
Visible.h
MHStack::Size
int Size() const
Definition: BaseClasses.h:100
MHElemAction::Initialise
virtual void Initialise(MHParseNode *p, MHEngine *engine)
Definition: BaseActions.cpp:31
MHStack::Top
BASE Top()
Definition: BaseClasses.h:96
MHStack
Definition: BaseClasses.h:84
MHIngredient::ContentPreparation
void ContentPreparation(MHEngine *engine) override
Definition: Ingredients.cpp:181
MHText::m_originalBgColour
MHColour m_originalBgColour
Definition: Text.h:76
MHSequence< MHTextItem * >
MHText::StartCorner
StartCorner
Definition: Text.h:81
MHOctetString
Definition: BaseClasses.h:107
MHVisible::Preparation
void Preparation(MHEngine *engine) override
Definition: Visible.cpp:105
MHText::CreateContent
void CreateContent(const unsigned char *p, int s, MHEngine *engine)
Definition: Text.cpp:357
C_LINE_ORIENTATION
@ C_LINE_ORIENTATION
Definition: ASN1Codes.h:125
MHTextLine::m_nLineWidth
int m_nLineWidth
Definition: Text.cpp:506
MHFontBody::IsSet
bool IsSet() const
Definition: BaseClasses.h:315
MHFontBody::PrintMe
void PrintMe(FILE *fd, int nTabs) const
Definition: BaseClasses.cpp:810
TABSTOP
static constexpr int8_t TABSTOP
Definition: Text.cpp:522
mythburn.FILE
int FILE
Definition: mythburn.py:139
MHOctetString::PrintMe
void PrintMe(FILE *fd, int nTabs) const
Definition: BaseClasses.cpp:154
MHVisible::m_nPosY
int m_nPosY
Definition: Visible.h:82
MHText::Start
@ Start
Definition: Text.h:79
MHText::m_textColour
MHColour m_textColour
Definition: Text.h:90
rchlineOrientation
static const std::array< const QString, 2 > rchlineOrientation
Definition: Text.cpp:164
InterpretAttributes
static void InterpretAttributes(const MHOctetString &attrs, int &style, int &size, int &lineSpace, int &letterSpace)
Definition: Text.cpp:388
MHText::GetJustification
static int GetJustification(const QString &str)
Definition: Text.cpp:150
MHRoot::m_fRunning
bool m_fRunning
Definition: Root.h:252
MHRoot::m_ObjectReference
MHObjectRef m_ObjectReference
Definition: Root.h:247
MHEngine::Redraw
void Redraw(const QRegion &region)
Definition: Engine.cpp:898
MHTextLine
Definition: Text.cpp:500
MHEngine::EventTriggered
void EventTriggered(MHRoot *pSource, enum EventType ev)
Definition: Engine.h:94
MHText::~MHText
~MHText() override
Definition: Text.cpp:51
MHTextItem::m_unicode
QString m_unicode
Definition: Text.cpp:474
MHText::m_startCorner
StartCorner m_startCorner
Definition: Text.h:85
MHColour::Initialise
void Initialise(MHParseNode *p, MHEngine *engine)
Definition: BaseClasses.cpp:236
hardwareprofile.config.p
p
Definition: config.py:33
MHText::SetFontAttributes
void SetFontAttributes(const MHOctetString &fontAttrs, MHEngine *engine) override
Definition: Text.cpp:380
MHInteractible::Initialise
void Initialise(MHParseNode *p, MHEngine *engine)
Definition: Visible.cpp:499
C_FONT_ATTRIBUTES
@ C_FONT_ATTRIBUTES
Definition: ASN1Codes.h:79
ParseNode.h
compat.h
MHText::GetOpaqueArea
QRegion GetOpaqueArea() override
Definition: Text.cpp:880
MHText::MHText
MHText()=default
MHFontBody::Initialise
void Initialise(MHParseNode *p, MHEngine *engine)
Definition: BaseClasses.cpp:798
MHText::m_fontAttrs
MHOctetString m_fontAttrs
Definition: Text.h:91
MHLogWarning
@ MHLogWarning
Definition: freemheg.h:72
PrintTabs
void PrintTabs(FILE *fd, int n)
Definition: ParseNode.cpp:34
MHGenericOctetString::GetValue
void GetValue(MHOctetString &str, MHEngine *engine) const
Definition: BaseClasses.cpp:505
MHParseNode::GetArgN
MHParseNode * GetArgN(int n)
Definition: ParseNode.cpp:78
MHText::m_horizJ
Justification m_horizJ
Definition: Text.h:82
MHVisible::m_nBoxHeight
int m_nBoxHeight
Definition: Visible.h:80
MHTextItem::MHTextItem
MHTextItem()
Definition: Text.cpp:484
Tabs
static int Tabs(int nXpos, int nTabCount)
Definition: Text.cpp:523
MHColour
Definition: BaseClasses.h:139
MHTextDisplay::GetBounds
virtual QRect GetBounds(const QString &str, int &strLen, int maxSize=-1)=0
MHTextItem::m_width
int m_width
Definition: Text.cpp:476
MHLOG
#define MHLOG(__level, __text)
Definition: Logging.h:36
MHVisible::GetVisibleArea
virtual QRegion GetVisibleArea()
Definition: Visible.cpp:197
MHVisible::m_nPosX
int m_nPosX
Definition: Visible.h:81
MHHyperText::Initialise
void Initialise(MHParseNode *p, MHEngine *engine) override
Definition: Text.cpp:890
MHSequence::RemoveAt
void RemoveAt(int i)
Definition: BaseClasses.h:66
Engine.h
MHText::m_nCharSet
int m_nCharSet
Definition: Text.h:77
C_HORIZONTAL_JUSTIFICATION
@ C_HORIZONTAL_JUSTIFICATION
Definition: ASN1Codes.h:123
MHText::GetStartCorner
static int GetStartCorner(const QString &str)
Definition: Text.cpp:190
MHSetFontAttributes::m_fontAttrs
MHGenericOctetString m_fontAttrs
Definition: Text.h:159
Ingredients.h
MHText::Justification
Justification
Definition: Text.h:79
C_VERTICAL_JUSTIFICATION
@ C_VERTICAL_JUSTIFICATION
Definition: ASN1Codes.h:124
MHEngine::GetDefaultTextColour
void GetDefaultTextColour(MHColour &colour)
Definition: Engine.cpp:1373
MHText::m_originalFontAttrs
MHOctetString m_originalFontAttrs
Definition: Text.h:75
MHIngredient::IN_IncludedContent
@ IN_IncludedContent
Definition: Ingredients.h:71
Text.h
MHLogDetail
@ MHLogDetail
Definition: freemheg.h:77
MHText::Display
void Display(MHEngine *engine) override
Definition: Text.cpp:860
MHGenericOctetString::Initialise
void Initialise(MHParseNode *p, MHEngine *engine)
Definition: BaseClasses.cpp:476
MHElemAction::Target
MHRoot * Target(MHEngine *engine)
Definition: BaseActions.cpp:46
MHOctetString::Printable
QString Printable() const
Definition: BaseClasses.h:128
MHSequence::InsertAt
void InsertAt(BASE b, int n)
Definition: BaseClasses.h:53
MHEngine::GetDefaultFontAttrs
void GetDefaultFontAttrs(MHOctetString &str)
Definition: Engine.cpp:1462
MHFontBody::Copy
void Copy(const MHFontBody &fb)
Definition: BaseClasses.cpp:822
MHParseNode::GetBoolValue
bool GetBoolValue()
Definition: ParseNode.cpp:192
Root.h
MHTextItem
Definition: Text.cpp:469
MHOctetString::Size
int Size() const
Definition: BaseClasses.h:120
MHText::m_fNeedsRedraw
bool m_fNeedsRedraw
Definition: Text.h:95
C_START_CORNER
@ C_START_CORNER
Definition: ASN1Codes.h:126
MHText::m_fTextWrap
bool m_fTextWrap
Definition: Text.h:86
MHVisible
Definition: Visible.h:35
C_TEXT_COLOUR
@ C_TEXT_COLOUR
Definition: ASN1Codes.h:77
C_CHARACTER_SET
@ C_CHARACTER_SET
Definition: ASN1Codes.h:74
MHColour::IsSet
bool IsSet() const
Definition: BaseClasses.h:144
MHText::Centre
@ Centre
Definition: Text.h:79
rchJustification
static const std::array< const QString, 4 > rchJustification
Definition: Text.cpp:142
MHTextLine::m_items
MHSequence< MHTextItem * > m_items
Definition: Text.cpp:505
MHText::m_vertJ
Justification m_vertJ
Definition: Text.h:83
MHIngredient::IN_NoContent
enum MHIngredient::@11 IN_NoContent
MHColour::Copy
void Copy(const MHColour &col)
Definition: BaseClasses.cpp:266
BaseClasses.h
MHVisible::Initialise
void Initialise(MHParseNode *p, MHEngine *engine) override
Definition: Visible.cpp:50
MHTextDisplay::SetFont
virtual void SetFont(int size, bool isBold, bool isItalic)=0
MHTextItem::m_text
MHOctetString m_text
Definition: Text.cpp:473
MHSequence::Size
int Size() const
Definition: BaseClasses.h:47
MHParseNode
Definition: ParseNode.h:38
MHText::m_origFont
MHFontBody m_origFont
Definition: Text.h:74
MHInteractible::PrintMe
void PrintMe(FILE *fd, int nTabs) const
Definition: Visible.cpp:525
MHStack::Pop
BASE Pop()
Definition: BaseClasses.h:89
MHSequence::GetAt
BASE GetAt(int i) const
Definition: BaseClasses.h:49
MHParseNode::GetEnumValue
int GetEnumValue()
Definition: ParseNode.cpp:181
MHText::PrintMe
void PrintMe(FILE *fd, int nTabs) const override
Definition: Text.cpp:203
MHText::Redraw
void Redraw()
Definition: Text.cpp:539
MHParseNode::GetStringValue
void GetStringValue(MHOctetString &str)
Definition: ParseNode.cpp:203
MHText::m_bgColour
MHColour m_bgColour
Definition: Text.h:90
MHSequence::Append
void Append(BASE b)
Definition: BaseClasses.h:64
MHTextLine::~MHTextLine
~MHTextLine()
Definition: Text.cpp:511
freemheg.h
MHERROR
#define MHERROR(__text)
Definition: Logging.h:42
Logging.h
MHText::SetTextColour
void SetTextColour(const MHColour &colour, MHEngine *engine) override
Definition: Text.cpp:365
MHVisible::m_nBoxWidth
int m_nBoxWidth
Definition: Visible.h:79
MHTextLine::m_nLineHeight
int m_nLineHeight
Definition: Text.cpp:507
MHText::Initialise
void Initialise(MHParseNode *p, MHEngine *engine) override
Definition: Text.cpp:57
MHContext::DrawRect
virtual void DrawRect(int xPos, int yPos, int width, int height, MHRgba colour)=0
C_BACKGROUND_COLOUR
@ C_BACKGROUND_COLOUR
Definition: ASN1Codes.h:75
C_ORIGINAL_FONT
@ C_ORIGINAL_FONT
Definition: ASN1Codes.h:122
MHColour::PrintMe
void PrintMe(FILE *fd, int nTabs) const
Definition: BaseClasses.cpp:248
MHText::GetLineOrientation
static int GetLineOrientation(const QString &str)
Definition: Text.cpp:169
MHRoot::SetFontAttributes
virtual void SetFontAttributes(const MHOctetString &, MHEngine *)
Definition: Root.h:118
MHTextItem::m_nUnicode
int m_nUnicode
Definition: Text.cpp:475
MHText::Horizontal
@ Horizontal
Definition: Text.h:80
MHText::Preparation
void Preparation(MHEngine *engine) override
Definition: Text.cpp:281
MHText::m_lineOrientation
LineOrientation m_lineOrientation
Definition: Text.h:84
MHText::SetBackgroundColour
void SetBackgroundColour(const MHColour &colour, MHEngine *engine) override
Definition: Text.cpp:372
MHTextItem::m_nTabCount
int m_nTabCount
Definition: Text.cpp:478
MHTextDisplay::Draw
virtual void Draw(int x, int y)=0
MHOctetString::Copy
void Copy(const MHOctetString &str)
Definition: BaseClasses.cpp:131
MHTextLine::m_nDescent
int m_nDescent
Definition: Text.cpp:508
MHRgba
Definition: freemheg.h:82
MHText::m_originalTextColour
MHColour m_originalTextColour
Definition: Text.h:76
MHObjectRef::Printable
QString Printable() const
Definition: BaseClasses.cpp:313
MHText::ContentArrived
void ContentArrived(const unsigned char *data, int length, MHEngine *engine) override
Definition: Text.cpp:346
MHSetFontAttributes::Perform
void Perform(MHEngine *engine) override
Definition: Text.cpp:915