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 "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 
84 
85  if (pBGColour)
86  {
87  m_OriginalBgColour.Initialise(pBGColour->GetArgN(0), engine);
88  }
89 
90  // Character set
92 
93  if (pChset)
94  {
95  m_nCharSet = pChset->GetArgN(0)->GetIntValue();
96  }
97 
98  // Justification
100 
101  if (pHJust)
102  {
103  m_HorizJ = (enum Justification)pHJust->GetArgN(0)->GetEnumValue();
104  }
105 
107 
108  if (pVJust)
109  {
110  m_VertJ = (enum Justification)pVJust->GetArgN(0)->GetEnumValue();
111  }
112 
113  // 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 char * const 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 char *str)
151 {
152  for (int i = 0; i < (int)(sizeof(rchJustification) / sizeof(rchJustification[0])); i++)
153  {
154  if (strcasecmp(str, rchJustification[i]) == 0)
155  {
156  return (i + 1); // Numbered from 1
157  }
158  }
159 
160  return 0;
161 }
162 
163 static const char * const rchlineOrientation[] =
164 {
165  "vertical", // 1
166  "horizontal"
167 };
168 
169 int MHText::GetLineOrientation(const char *str)
170 {
171  for (int i = 0; i < (int)(sizeof(rchlineOrientation) / sizeof(rchlineOrientation[0])); i++)
172  {
173  if (strcasecmp(str, rchlineOrientation[i]) == 0)
174  {
175  return (i + 1);
176  }
177  }
178 
179  return 0;
180 }
181 
182 static const char * const rchStartCorner[] =
183 {
184  "upper-left", // 1
185  "upper-right",
186  "lower-left",
187  "lower-right" // 4
188 };
189 
190 int MHText::GetStartCorner(const char *str)
191 {
192  for (int i = 0; i < (int)(sizeof(rchStartCorner) / sizeof(rchStartCorner[0])); i++)
193  {
194  if (strcasecmp(str, rchStartCorner[i]) == 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", rchJustification[m_HorizJ-1]);
251  }
252 
253  if (m_VertJ != Start)
254  {
255  PrintTabs(fd, nTabs + 1);
256  fprintf(fd, ":VJustification %s\n", rchJustification[m_VertJ-1]);
257  }
258 
260  {
261  PrintTabs(fd, nTabs + 1);
262  fprintf(fd, ":LineOrientation %s\n", rchlineOrientation[m_LineOrientation-1]);
263  }
264 
265  if (m_StartCorner != UpperLeft)
266  {
267  PrintTabs(fd, nTabs + 1);
268  fprintf(fd, ":StartCorner %s\n", 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();
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  MHTextItem *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 #define 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  MHTextItem *pCurrItem = new MHTextItem;
559  MHTextLine *pCurrLine = new MHTextLine;
560  pCurrLine->m_Items.Append(pCurrItem);
561  theText.Append(pCurrLine);
562  MHStack <MHRgba> m_ColourStack; // Stack to handle nested colour codes.
563  m_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  m_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 (m_ColourStack.Size() > 1)
644  {
645  m_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 = m_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, size = 0, lineSpace = 0, letterSpace = 0;
684  InterpretAttributes(m_fontAttrs, style, size, lineSpace, letterSpace);
685  // Create a font with this information.
686  m_pDisplay->SetFont(size, (style & 2) != 0, (style & 1) != 0);
687 
688  // Calculate the layout of each section.
689  for (i = 0; i < theText.Size(); i++)
690  {
691  MHTextLine *pLine = theText.GetAt(i);
692  pLine->m_nLineWidth = 0;
693 
694  for (int j = 0; j < pLine->m_Items.Size(); j++)
695  {
696  MHTextItem *pItem = pLine->m_Items.GetAt(j);
697 
698  // Set any tabs.
699  pLine->m_nLineWidth = Tabs(pLine->m_nLineWidth, pItem->m_nTabCount);
700 
701  if (pItem->m_Unicode.isEmpty()) // Convert UTF-8 to Unicode.
702  {
703  int s = pItem->m_Text.Size();
704  pItem->m_Unicode = QString::fromUtf8((const char *)pItem->m_Text.Bytes(), s);
705  pItem->m_nUnicode = pItem->m_Unicode.length();
706  }
707 
708  // Fit the text onto the line.
709  int nFullText = pItem->m_nUnicode;
710  // Get the box size and update pItem->m_nUnicode to the number that will fit.
711  QRect rect = m_pDisplay->GetBounds(pItem->m_Unicode, pItem->m_nUnicode, m_nBoxWidth - pLine->m_nLineWidth);
712 
713  if (nFullText != pItem->m_nUnicode && m_fTextWrap) // Doesn't fit, we have to word-wrap.
714  {
715  int nTruncated = pItem->m_nUnicode; // Just in case.
716  // Now remove characters until we find a word-break character.
717  while (pItem->m_nUnicode > 0 && pItem->m_Unicode[pItem->m_nUnicode] != ' ')
718  {
719  pItem->m_nUnicode--;
720  }
721 
722  // If there are now word-break characters we truncate the text.
723  if (pItem->m_nUnicode == 0)
724  {
725  pItem->m_nUnicode = nTruncated;
726  }
727 
728  // Special case to avoid infinite loop if the box is very narrow.
729  if (pItem->m_nUnicode == 0)
730  {
731  pItem->m_nUnicode = 1;
732  }
733 
734  // We need to move the text we've cut off this line into a new line.
735  int nNewWidth = nFullText - pItem->m_nUnicode;
736  int nNewStart = pItem->m_nUnicode;
737 
738  // Remove any spaces at the start of the new section.
739  while (nNewWidth != 0 && pItem->m_Unicode[nNewStart] == ' ')
740  {
741  nNewStart++;
742  nNewWidth--;
743  }
744 
745  if (nNewWidth != 0)
746  {
747  // Create a new line from the extra text.
748  MHTextLine *pNewLine = new MHTextLine;
749  theText.InsertAt(pNewLine, i + 1);
750  // The first item on the new line is the rest of the text.
751  MHTextItem *pNewItem = pItem->NewItem();
752  pNewLine->m_Items.Append(pNewItem);
753  pNewItem->m_Unicode = pItem->m_Unicode.mid(nNewStart, nNewWidth);
754  pNewItem->m_nUnicode = nNewWidth;
755 
756  // Move any remaining items, e.g. in a different colour, from this line onto the new line.
757  while (pLine->m_Items.Size() > j + 1)
758  {
759  pNewLine->m_Items.Append(pLine->m_Items.GetAt(j + 1));
760  pLine->m_Items.RemoveAt(j + 1);
761  }
762  }
763 
764  // Remove any spaces at the end of the old section. If we don't do that and
765  // we are centering or right aligning the text we'll get it wrong.
766  while (pItem->m_nUnicode > 1 && pItem->m_Unicode[pItem->m_nUnicode-1] == ' ')
767  {
768  pItem->m_nUnicode--;
769  }
770 
771  rect = m_pDisplay->GetBounds(pItem->m_Unicode, pItem->m_nUnicode);
772  }
773 
774  pItem->m_Width = rect.width();
775  pLine->m_nLineWidth += rect.width();
776 
777  if (rect.height() > pLine->m_nLineHeight)
778  {
779  pLine->m_nLineHeight = rect.height();
780  }
781 
782  if (rect.bottom() > pLine->m_nDescent)
783  {
784  pLine->m_nDescent = rect.bottom();
785  }
786  }
787  }
788 
789  // Now output the text.
790  int yOffset = 0;
791  // If there isn't space for all the lines we should drop extra lines.
792  int nNumLines = theText.Size();
793 
794  do
795  {
796  if (m_VertJ == End)
797  {
798  yOffset = m_nBoxHeight - nNumLines * lineSpace;
799  }
800  else if (m_VertJ == Centre)
801  {
802  yOffset = (m_nBoxHeight - nNumLines * lineSpace) / 2;
803  }
804 
805  if (yOffset < 0)
806  {
807  nNumLines--;
808  }
809  }
810  while (yOffset < 0);
811 
812  for (i = 0; i < nNumLines; i++)
813  {
814  MHTextLine *pLine = theText.GetAt(i);
815  int xOffset = 0;
816 
817  if (m_HorizJ == End)
818  {
819  xOffset = m_nBoxWidth - pLine->m_nLineWidth;
820  }
821  else if (m_HorizJ == Centre)
822  {
823  xOffset = (m_nBoxWidth - pLine->m_nLineWidth) / 2;
824  }
825 
826  for (int j = 0; j < pLine->m_Items.Size(); j++)
827  {
828  MHTextItem *pItem = pLine->m_Items.GetAt(j);
829 
830  // Tab across if necessary.
831  xOffset = Tabs(xOffset, pItem->m_nTabCount);
832 
833  if (! pItem->m_Unicode.isEmpty()) // We may have blank lines.
834  {
835  m_pDisplay->AddText(xOffset, yOffset + (pLine->m_nLineHeight + lineSpace) / 2 - pLine->m_nDescent,
836  pItem->m_Unicode.left(pItem->m_nUnicode), pItem->m_Colour);
837  }
838 
839  xOffset += pItem->m_Width;
840  }
841 
842  yOffset += lineSpace;
843 
844  if (yOffset + lineSpace > m_nBoxHeight)
845  {
846  break;
847  }
848  }
849 
850  // Clean up.
851  for (int k = 0; k < theText.Size(); k++)
852  {
853  delete(theText.GetAt(k));
854  }
855 }
856 
858 {
859  if (! m_fRunning || ! m_pDisplay || m_nBoxWidth == 0 || m_nBoxHeight == 0)
860  {
861  return; // Can't draw zero sized boxes.
862  }
863 
864  // We only need to recreate the display if something has changed.
865  if (m_fNeedsRedraw)
866  {
867  Redraw();
868  m_fNeedsRedraw = false;
869  }
870 
871  // Draw the background first, then the text.
874 }
875 
876 // Return the area actually obscured. This is empty unless the background is opaque.
878 {
879  if (! m_fRunning || (GetColour(m_bgColour)).alpha() != 255)
880  {
881  return QRegion();
882  }
883  return QRegion(QRect(m_nPosX, m_nPosY, m_nBoxWidth, m_nBoxHeight));
884 }
885 
886 
888 {
889  MHText::Initialise(p, engine);
890  MHInteractible::Initialise(p, engine);
891  //
892 }
893 
894 void MHHyperText::PrintMe(FILE *fd, int nTabs) const
895 {
896  PrintTabs(fd, nTabs);
897  fprintf(fd, "{:HyperText ");
898  MHText::PrintMe(fd, nTabs + 1);
899  MHInteractible::PrintMe(fd, nTabs + 1);
900  fprintf(fd, "****TODO\n");
901  PrintTabs(fd, nTabs);
902  fprintf(fd, "}\n");
903 }
904 
905 
907 {
908  MHElemAction::Initialise(p, engine); // Target
909  m_FontAttrs.Initialise(p->GetArgN(1), engine); // New font attrs
910 }
911 
913 {
914  // Get the new font attributes.
915  MHOctetString newAttrs;
916  m_FontAttrs.GetValue(newAttrs, engine);
917  Target(engine)->SetFontAttributes(newAttrs, engine);
918 }
void PrintMe(FILE *, int nTabs) const override
Definition: Text.cpp:203
void GetDefaultTextColour(MHColour &colour)
Definition: Engine.cpp:1369
MHText()=default
virtual void SetFontAttributes(const MHOctetString &, MHEngine *)
Definition: Root.h:107
void Append(const MHOctetString &str)
QString m_Unicode
Definition: Text.cpp:474
static int Tabs(int nXpos, int nTabCount)
Definition: Text.cpp:523
MHTextItem()
Definition: Text.cpp:484
virtual void AddText(int x, int y, const QString &, MHRgba colour)=0
#define MHERROR(__text)
Definition: Logging.h:42
virtual MHTextDisplay * CreateText(void)=0
void Initialise(MHParseNode *p, MHEngine *engine) override
Definition: Text.cpp:906
virtual void SetSize(int width, int height)=0
void Initialise(MHParseNode *p, MHEngine *engine) override
Definition: Text.cpp:57
void Initialise(MHParseNode *p, MHEngine *engine)
Definition: Visible.cpp:496
void Display(MHEngine *engine) override
Definition: Text.cpp:857
MHOctetString m_Text
Definition: Text.cpp:473
Justification m_HorizJ
Definition: Text.h:82
#define C_HORIZONTAL_JUSTIFICATION
Definition: ASN1Codes.h:120
void CreateContent(const unsigned char *p, int s, MHEngine *engine)
Definition: Text.cpp:357
int m_nCharSet
Definition: Text.h:77
MHRoot * Target(MHEngine *engine)
Definition: BaseActions.cpp:46
BASE GetAt(int i) const
Definition: BaseClasses.h:48
~MHText()
Definition: Text.cpp:51
void PrintMe(FILE *fd, int nTabs) const override
Definition: Text.cpp:894
bool IsSet() const
Definition: BaseClasses.h:141
BASE Pop()
Definition: BaseClasses.h:87
void PrintMe(FILE *fd, int nTabs) const override
Definition: Visible.cpp:84
#define C_START_CORNER
Definition: ASN1Codes.h:123
int m_nBoxWidth
Definition: Visible.h:79
MHColour m_textColour
Definition: Text.h:90
virtual QRegion GetVisibleArea()
Definition: Visible.cpp:194
bool m_fAvailable
Definition: Root.h:207
void GetDefaultBGColour(MHColour &colour)
Definition: Engine.cpp:1355
virtual void Draw(int x, int y)=0
MHColour m_OriginalBgColour
Definition: Text.h:76
LineOrientation
Definition: Text.h:80
int m_nPosY
Definition: Visible.h:82
QString Printable() const
void SetBackgroundColour(const MHColour &colour, MHEngine *engine) override
Definition: Text.cpp:372
static MHRgba GetColour(const MHColour &colour)
Definition: Visible.cpp:156
MHOctetString m_fontAttrs
Definition: Text.h:91
MHFontBody m_OrigFont
Definition: Text.h:74
enum MHIngredient::@10 IN_NoContent
virtual void Clear(void)=0
void InsertAt(BASE b, int n)
Definition: BaseClasses.h:52
void GetValue(MHOctetString &str, MHEngine *engine) const
const unsigned char * Bytes() const
Definition: BaseClasses.h:122
unsigned char GetAt(int i) const
Definition: BaseClasses.h:121
QRegion GetOpaqueArea() override
Definition: Text.cpp:877
MHColour m_OriginalTextColour
Definition: Text.h:76
void PrintMe(FILE *fd, int nTabs) const
void Copy(const MHColour &col)
MHSequence< MHTextItem * > m_Items
Definition: Text.cpp:505
int m_nLineWidth
Definition: Text.cpp:506
MHContext * GetContext()
Definition: Engine.h:153
MHOctetString m_OriginalFontAttrs
Definition: Text.h:75
int Size() const
Definition: BaseClasses.h:98
static int GetLineOrientation(const char *str)
Definition: Text.cpp:169
virtual void DrawRect(int xPos, int yPos, int width, int height, MHRgba colour)=0
void Initialise(MHParseNode *p, MHEngine *engine)
void Redraw(const QRegion &region)
Definition: Engine.cpp:899
int m_nUnicode
Definition: Text.cpp:475
void ContentPreparation(MHEngine *engine) override
void PrintTabs(FILE *fd, int n)
Definition: ParseNode.cpp:34
void Copy(const MHFontBody &fb)
void Copy(const MHOctetString &str)
void SetTextColour(const MHColour &colour, MHEngine *engine) override
Definition: Text.cpp:365
virtual void SetFont(int size, bool isBold, bool isItalic)=0
int Size() const
Definition: BaseClasses.h:117
int m_nPosX
Definition: Visible.h:81
void RemoveAt(int i)
Definition: BaseClasses.h:64
MHTextItem * NewItem()
Definition: Text.cpp:492
MHTextDisplay * m_pDisplay
Definition: Text.h:94
void ContentArrived(const unsigned char *data, int length, MHEngine *engine) override
Definition: Text.cpp:346
int GetIntValue()
Definition: ParseNode.cpp:170
int m_nTabCount
Definition: Text.cpp:478
QString Printable() const
Definition: BaseClasses.h:125
void EventTriggered(MHRoot *pSource, enum EventType ev)
Definition: Engine.h:93
static int GetStartCorner(const char *str)
Definition: Text.cpp:190
void Preparation(MHEngine *engine) override
Definition: Visible.cpp:105
void GetStringValue(MHOctetString &str)
Definition: ParseNode.cpp:203
MHParseNode * GetNamedArg(int nTag)
Definition: ParseNode.cpp:110
void Initialise(MHParseNode *p, MHEngine *engine) override
Definition: Visible.cpp:50
void PrintMe(FILE *fd, int nTabs) const
~MHTextLine()
Definition: Text.cpp:511
#define C_TEXT_COLOUR
Definition: ASN1Codes.h:74
void Initialise(MHParseNode *p, MHEngine *engine)
#define C_VERTICAL_JUSTIFICATION
Definition: ASN1Codes.h:121
void PrintMe(FILE *fd, int nTabs) const
bool m_fRunning
Definition: Root.h:208
MHObjectRef m_ObjectReference
Definition: Root.h:203
#define C_BACKGROUND_COLOUR
Definition: ASN1Codes.h:72
int FILE
Definition: mythburn.py:110
int m_nDescent
Definition: Text.cpp:508
static const char *const rchStartCorner[]
Definition: Text.cpp:182
static int GetJustification(const char *str)
Definition: Text.cpp:150
void Redraw()
Definition: Text.cpp:539
bool m_fTextWrap
Definition: Text.h:86
MHOctetString m_IncludedContent
Definition: Ingredients.h:77
bool IsSet() const
Definition: BaseClasses.h:305
MHOctetString m_Content
Definition: Text.h:92
BASE Top()
Definition: BaseClasses.h:94
void GetDefaultFontAttrs(MHOctetString &str)
Definition: Engine.cpp:1458
void ContentPreparation(MHEngine *engine) override
Definition: Text.cpp:330
void SetFontAttributes(const MHOctetString &fontAttrs, MHEngine *engine) override
Definition: Text.cpp:380
static const char *const rchJustification[]
Definition: Text.cpp:141
StartCorner m_StartCorner
Definition: Text.h:85
MHGenericOctetString m_FontAttrs
Definition: Text.h:159
void Initialise(MHParseNode *p, MHEngine *engine)
void Initialise(MHParseNode *p, MHEngine *engine) override
Definition: Text.cpp:887
int GetEnumValue()
Definition: ParseNode.cpp:181
#define MHLOG(__level, __text)
Definition: Logging.h:36
virtual void Initialise(MHParseNode *p, MHEngine *engine)
Definition: BaseActions.cpp:31
Definition: Text.h:37
void Preparation(MHEngine *engine) override
Definition: Text.cpp:281
bool GetBoolValue()
Definition: ParseNode.cpp:192
#define C_LINE_ORIENTATION
Definition: ASN1Codes.h:122
#define C_FONT_ATTRIBUTES
Definition: ASN1Codes.h:76
static void InterpretAttributes(const MHOctetString &attrs, int &style, int &size, int &lineSpace, int &letterSpace)
Definition: Text.cpp:388
bool m_fNeedsRedraw
Definition: Text.h:95
int m_nLineHeight
Definition: Text.cpp:507
MHParseNode * GetArgN(int n)
Definition: ParseNode.cpp:78
#define C_TEXT_WRAPPING
Definition: ASN1Codes.h:124
StartCorner
Definition: Text.h:81
void Push(BASE b)
Definition: BaseClasses.h:92
void Append(BASE b)
Definition: BaseClasses.h:62
void Perform(MHEngine *engine) override
Definition: Text.cpp:912
MHTextLine()=default
Justification
Definition: Text.h:79
MHColour m_bgColour
Definition: Text.h:90
int m_nBoxHeight
Definition: Visible.h:80
int Size() const
Definition: BaseClasses.h:46
static const char *const rchlineOrientation[]
Definition: Text.cpp:163
Justification m_VertJ
Definition: Text.h:83
LineOrientation m_LineOrientation
Definition: Text.h:84
#define C_CHARACTER_SET
Definition: ASN1Codes.h:71
int m_Width
Definition: Text.cpp:476
#define C_ORIGINAL_FONT
Definition: ASN1Codes.h:119
MHRgba m_Colour
Definition: Text.cpp:477
void PrintMe(FILE *fd, int nTabs) const
Definition: Visible.cpp:522
#define TABSTOP
Definition: Text.cpp:522
virtual QRect GetBounds(const QString &str, int &strLen, int maxSize=-1)=0