00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "Text.h"
00022 #include "Visible.h"
00023 #include "Presentable.h"
00024 #include "Ingredients.h"
00025 #include "Root.h"
00026 #include "BaseClasses.h"
00027 #include "ParseNode.h"
00028 #include "ASN1Codes.h"
00029 #include "Engine.h"
00030 #include "Logging.h"
00031 #include "freemheg.h"
00032
00033 MHText::MHText()
00034 {
00035 m_nCharSet = -1;
00036 m_HorizJ = m_VertJ = Start;
00037 m_LineOrientation = Horizontal;
00038 m_StartCorner = UpperLeft;
00039 m_fTextWrap = false;
00040 m_pDisplay = NULL;
00041 }
00042
00043 MHText::MHText(const MHText &ref): MHVisible(ref)
00044 {
00045 m_OrigFont.Copy(ref.m_OrigFont);
00046 m_OriginalFontAttrs.Copy(ref.m_OriginalFontAttrs);
00047 m_OriginalTextColour.Copy(ref.m_OriginalTextColour);
00048 m_OriginalBgColour.Copy(ref.m_OriginalBgColour);
00049 m_nCharSet = ref.m_nCharSet;
00050 m_HorizJ = ref.m_HorizJ;
00051 m_VertJ = ref.m_VertJ;
00052 m_LineOrientation = ref.m_LineOrientation;
00053 m_StartCorner = ref.m_StartCorner;
00054 m_fTextWrap = ref.m_fTextWrap;
00055 m_pDisplay = NULL;
00056 }
00057
00058 MHText::~MHText()
00059 {
00060 delete(m_pDisplay);
00061 }
00062
00063
00064 void MHText::Initialise(MHParseNode *p, MHEngine *engine)
00065 {
00066 MHVisible::Initialise(p, engine);
00067
00068 MHParseNode *pFontBody = p->GetNamedArg(C_ORIGINAL_FONT);
00069 if (pFontBody) m_OrigFont.Initialise(pFontBody->GetArgN(0), engine);
00070 MHParseNode *pFontAttrs = p->GetNamedArg(C_FONT_ATTRIBUTES);
00071 if (pFontAttrs) pFontAttrs->GetArgN(0)->GetStringValue(m_OriginalFontAttrs);
00072
00073 MHParseNode *pTextColour = p->GetNamedArg(C_TEXT_COLOUR);
00074 if (pTextColour) m_OriginalTextColour.Initialise(pTextColour->GetArgN(0), engine);
00075 MHParseNode *pBGColour = p->GetNamedArg(C_BACKGROUND_COLOUR);
00076 if (pBGColour) m_OriginalBgColour.Initialise(pBGColour->GetArgN(0), engine);
00077
00078 MHParseNode *pChset = p->GetNamedArg(C_CHARACTER_SET);
00079 if (pChset) m_nCharSet = pChset->GetArgN(0)->GetIntValue();
00080
00081 MHParseNode *pHJust = p->GetNamedArg(C_HORIZONTAL_JUSTIFICATION);
00082 if (pHJust) m_HorizJ = (enum Justification)pHJust->GetArgN(0)->GetEnumValue();
00083 MHParseNode *pVJust = p->GetNamedArg(C_VERTICAL_JUSTIFICATION);
00084 if (pVJust) m_VertJ = (enum Justification)pVJust->GetArgN(0)->GetEnumValue();
00085
00086 MHParseNode *pLineO = p->GetNamedArg(C_LINE_ORIENTATION);
00087 if (pLineO) m_LineOrientation = (enum LineOrientation)pLineO->GetArgN(0)->GetEnumValue();
00088
00089 MHParseNode *pStartC = p->GetNamedArg(C_START_CORNER);
00090 if (pStartC) m_StartCorner = (enum StartCorner)pStartC->GetArgN(0)->GetEnumValue();
00091
00092 MHParseNode *pTextWrap = p->GetNamedArg(C_TEXT_WRAPPING);
00093 if (pTextWrap) m_fTextWrap = pTextWrap->GetArgN(0)->GetBoolValue();
00094
00095 m_pDisplay = engine->GetContext()->CreateText();
00096 m_NeedsRedraw = true;
00097 }
00098
00099 static const char *rchJustification[] =
00100 {
00101 "start",
00102 "end",
00103 "centre",
00104 "justified"
00105 };
00106
00107
00108 int MHText::GetJustification(const char *str)
00109 {
00110 for (int i = 0; i < (int)(sizeof(rchJustification)/sizeof(rchJustification[0])); i++) {
00111 if (strcasecmp(str, rchJustification[i]) == 0) return (i+1);
00112 }
00113 return 0;
00114 }
00115
00116 static const char *rchlineOrientation[] =
00117 {
00118 "vertical",
00119 "horizontal"
00120 };
00121
00122 int MHText::GetLineOrientation(const char *str)
00123 {
00124 for (int i = 0; i < (int)(sizeof(rchlineOrientation)/sizeof(rchlineOrientation[0])); i++) {
00125 if (strcasecmp(str, rchlineOrientation[i]) == 0) return (i+1);
00126 }
00127 return 0;
00128 }
00129
00130 static const char *rchStartCorner[] =
00131 {
00132 "upper-left",
00133 "upper-right",
00134 "lower-left",
00135 "lower-right"
00136 };
00137
00138 int MHText::GetStartCorner(const char *str)
00139 {
00140 for (int i = 0; i < (int)(sizeof(rchStartCorner)/sizeof(rchStartCorner[0])); i++) {
00141 if (strcasecmp(str, rchStartCorner[i]) == 0) return (i+1);
00142 }
00143 return 0;
00144 }
00145
00146 void MHText::PrintMe(FILE *fd, int nTabs) const
00147 {
00148 PrintTabs(fd, nTabs); fprintf(fd, "{:Text ");
00149 MHVisible::PrintMe(fd, nTabs+1);
00150 if (m_OrigFont.IsSet()) { PrintTabs(fd, nTabs+1); fprintf(fd, ":OrigFont "); m_OrigFont.PrintMe(fd, nTabs+1); fprintf(fd, "\n"); }
00151 if (m_OriginalFontAttrs.Size() > 0) { PrintTabs(fd, nTabs+1); fprintf(fd, ":FontAttributes "); m_OriginalFontAttrs.PrintMe(fd, nTabs+1); fprintf(fd, "\n"); }
00152 if (m_OriginalTextColour.IsSet()) { PrintTabs(fd, nTabs+1); fprintf(fd, ":TextColour "); m_OriginalTextColour.PrintMe(fd, nTabs+1); fprintf(fd, "\n"); }
00153 if (m_OriginalBgColour.IsSet()) { PrintTabs(fd, nTabs+1); fprintf(fd, ":BackgroundColour "); m_OriginalBgColour.PrintMe(fd, nTabs+1); fprintf(fd, "\n"); }
00154 if (m_nCharSet >= 0) { PrintTabs(fd, nTabs+1); fprintf(fd, ":CharacterSet %d\n", m_nCharSet); }
00155 if (m_HorizJ != Start) { PrintTabs(fd, nTabs+1); fprintf(fd, ":HJustification %s\n", rchJustification[m_HorizJ-1]); }
00156 if (m_VertJ != Start) { PrintTabs(fd, nTabs+1); fprintf(fd, ":VJustification %s\n", rchJustification[m_VertJ-1]); }
00157 if (m_LineOrientation != Horizontal) { PrintTabs(fd, nTabs+1); fprintf(fd, ":LineOrientation %s\n", rchlineOrientation[m_LineOrientation-1]); }
00158 if (m_StartCorner != UpperLeft) { PrintTabs(fd, nTabs+1); fprintf(fd, ":StartCorner %s\n", rchStartCorner[m_StartCorner-1]); }
00159 if (m_fTextWrap) { PrintTabs(fd, nTabs+1); fprintf(fd, ":TextWrapping true\n"); }
00160 PrintTabs(fd, nTabs);fprintf(fd, "}\n");
00161 }
00162
00163 void MHText::Preparation(MHEngine *engine)
00164 {
00165 if (m_fAvailable) return;
00166
00167
00168
00169 if (m_OriginalTextColour.IsSet()) m_textColour.Copy(m_OriginalTextColour);
00170 else engine->GetDefaultTextColour(m_textColour);
00171 if (m_OriginalBgColour.IsSet()) m_bgColour.Copy(m_OriginalBgColour);
00172 else engine->GetDefaultBGColour(m_bgColour);
00173 if (m_OriginalFontAttrs.Size() > 0) m_fontAttrs.Copy(m_OriginalFontAttrs);
00174 else engine->GetDefaultFontAttrs(m_fontAttrs);
00175 MHVisible::Preparation(engine);
00176
00177 m_pDisplay->SetSize(m_nBoxWidth, m_nBoxHeight);
00178 m_NeedsRedraw = true;
00179 }
00180
00181
00182 void MHText::ContentPreparation(MHEngine *engine)
00183 {
00184 MHVisible::ContentPreparation(engine);
00185 if (m_ContentType == IN_NoContent)
00186 MHERROR("Text object must have content");
00187 if (m_ContentType == IN_IncludedContent) CreateContent(m_IncludedContent.Bytes(), m_IncludedContent.Size(), engine);
00188 }
00189
00190
00191 void MHText::ContentArrived(const unsigned char *data, int length, MHEngine *engine)
00192 {
00193 CreateContent(data, length, engine);
00194
00195 engine->EventTriggered(this, EventContentAvailable);
00196 m_NeedsRedraw = true;
00197 }
00198
00199
00200 void MHText::CreateContent(const unsigned char *p, int s, MHEngine *engine)
00201 {
00202 m_Content.Copy(MHOctetString((const char *)p, s));
00203 engine->Redraw(GetVisibleArea());
00204 m_NeedsRedraw = true;
00205
00206 }
00207
00208 void MHText::SetTextColour(const MHColour &colour, MHEngine *engine)
00209 {
00210 m_textColour.Copy(colour);
00211 m_NeedsRedraw = true;
00212 engine->Redraw(GetVisibleArea());
00213 }
00214
00215 void MHText::SetBackgroundColour(const MHColour &colour, MHEngine *engine)
00216 {
00217 m_bgColour.Copy(colour);
00218
00219
00220 engine->Redraw(GetVisibleArea());
00221 }
00222
00223 void MHText::SetFontAttributes(const MHOctetString &fontAttrs, MHEngine *engine)
00224 {
00225 m_fontAttrs.Copy(fontAttrs);
00226 m_NeedsRedraw = true;
00227 engine->Redraw(GetVisibleArea());
00228 }
00229
00230
00231 static void InterpretAttributes(const MHOctetString &attrs, int &style, int &size, int &lineSpace, int &letterSpace)
00232 {
00233
00234 style = 0; size = 0x18; lineSpace = 0x18; letterSpace = 0;
00235 if (attrs.Size() == 5) {
00236 style = attrs.GetAt(0);
00237 size = attrs.GetAt(1);
00238 lineSpace = attrs.GetAt(2);
00239
00240 letterSpace = attrs.GetAt(3) * 256 + attrs.GetAt(4);
00241 if (letterSpace > 32767) letterSpace -= 65536;
00242 }
00243 else {
00244 const unsigned char *str = attrs.Bytes();
00245 char *p = (char *)str;
00246 char *q = strchr(p, '.');
00247 if (q != NULL) {
00248 if (q-p == 6 && strncmp(p, "italic", 6) == 0) style = 1;
00249 else if (q-p == 4 && strncmp(p, "bold", 4) == 0) style = 2;
00250 else if (q-p == 11 && strncmp(p, "bold-italic", 11) == 0) style = 3;
00251
00252 p = q+1;
00253 q = strchr(p, '.');
00254 }
00255 if (q != NULL) {
00256 size = atoi(p);
00257 if (size == 0) size = 0x18;
00258 p = q+1;
00259 q = strchr(p, '.');
00260 }
00261 if (q != NULL) {
00262 lineSpace = atoi(p);
00263 if (lineSpace == 0) size = 0x18;
00264 p = q+1;
00265 q = strchr(p, '.');
00266 }
00267 if (q != NULL) {
00268 letterSpace = atoi(p);
00269 }
00270 }
00271 }
00272
00273
00274 class MHTextItem {
00275 public:
00276 MHTextItem();
00277 MHOctetString m_Text;
00278 QString m_Unicode;
00279 int m_nUnicode;
00280 int m_Width;
00281 MHRgba m_Colour;
00282 int m_nTabCount;
00283
00284
00285 MHTextItem *NewItem();
00286 };
00287
00288 MHTextItem::MHTextItem()
00289 {
00290 m_nUnicode = 0;
00291 m_Width = 0;
00292 m_Colour = MHRgba(0, 0, 0, 255);
00293 m_nTabCount = 0;
00294 }
00295
00296 MHTextItem *MHTextItem::NewItem()
00297 {
00298 MHTextItem *pItem = new MHTextItem;
00299 pItem->m_Colour = m_Colour;
00300 return pItem;
00301 }
00302
00303
00304 class MHTextLine {
00305 public:
00306 MHTextLine(): m_nLineWidth(0), m_nLineHeight(0), m_nDescent(0) {}
00307 ~MHTextLine();
00308 MHSequence <MHTextItem *> m_Items;
00309 int m_nLineWidth;
00310 int m_nLineHeight;
00311 int m_nDescent;
00312 };
00313
00314 MHTextLine::~MHTextLine()
00315 {
00316 for (int i = 0; i < m_Items.Size(); i++) delete(m_Items.GetAt(i));
00317 }
00318
00319
00320 #define TABSTOP 45
00321
00322
00323
00324
00325
00326
00327 void MHText::Redraw()
00328 {
00329 if (! m_fRunning || !m_pDisplay) return;
00330 if (m_nBoxWidth == 0 || m_nBoxHeight == 0) return;
00331
00332 m_pDisplay->SetSize(m_nBoxWidth, m_nBoxHeight);
00333 m_pDisplay->Clear();
00334
00335 MHRgba textColour = GetColour(m_textColour);
00336
00337 MHSequence <MHTextLine*> theText;
00338
00339 MHTextItem *pCurrItem = new MHTextItem;
00340 MHTextLine *pCurrLine = new MHTextLine;
00341 pCurrLine->m_Items.Append(pCurrItem);
00342 theText.Append(pCurrLine);
00343 MHStack <MHRgba> m_ColourStack;
00344 m_ColourStack.Push(textColour);
00345 pCurrItem->m_Colour = textColour;
00346
00347 int i = 0;
00348 while (i < m_Content.Size()) {
00349 unsigned char ch = m_Content.GetAt(i++);
00350
00351 if (ch == '\t') {
00352 if (pCurrItem->m_Text.Size() != 0) { pCurrItem = pCurrItem->NewItem(); pCurrLine->m_Items.Append(pCurrItem); }
00353 pCurrItem->m_nTabCount++;
00354 }
00355
00356 else if (ch == '\r') {
00357
00358
00359 pCurrLine = new MHTextLine;
00360 theText.Append(pCurrLine);
00361 pCurrItem = pCurrItem->NewItem();
00362 pCurrLine->m_Items.Append(pCurrItem);
00363 }
00364
00365 else if (ch == 0x1b) {
00366 if (i == m_Content.Size()) break;
00367 unsigned char code = m_Content.GetAt(i);
00368
00369
00370
00371 if (code >= 0x40 && code <= 0x5e) {
00372
00373 if (++i == m_Content.Size()) break;
00374 unsigned char paramCount = m_Content.GetAt(i);
00375 i++;
00376 if (code == 0x43 && paramCount == 4 && i+paramCount <= m_Content.Size()) {
00377
00378 if (pCurrItem->m_Text.Size() != 0) {
00379 pCurrItem = pCurrItem->NewItem(); pCurrLine->m_Items.Append(pCurrItem);
00380 }
00381 pCurrItem->m_Colour = MHRgba(m_Content.GetAt(i), m_Content.GetAt(i+1),
00382 m_Content.GetAt(i+2), 255-m_Content.GetAt(i+3));
00383
00384 m_ColourStack.Push(pCurrItem->m_Colour);
00385 }
00386 else MHLOG(MHLogWarning, QString("Unknown text escape code 0x%1").arg(code,2, 16));
00387 i += paramCount;
00388 }
00389 else if (code >= 0x60 && code <= 0x7e) {
00390 i++;
00391 if (code == 0x63) {
00392 if (m_ColourStack.Size() > 1) {
00393 m_ColourStack.Pop();
00394
00395 if (pCurrItem->m_Text.Size() != 0) {
00396 pCurrItem = pCurrItem->NewItem();
00397 pCurrLine->m_Items.Append(pCurrItem);
00398 }
00399
00400 pCurrItem->m_Colour = m_ColourStack.Top();
00401 }
00402 }
00403 }
00404 }
00405
00406 else if (ch <= 0x1f) {
00407
00408
00409 }
00410
00411 else {
00412 int nStart = i-1;
00413 while (i < m_Content.Size() && m_Content.GetAt(i) >= 0x20) i++;
00414 pCurrItem->m_Text.Append(MHOctetString(m_Content, nStart, i-nStart));
00415 }
00416 }
00417
00418
00419 int style, size, lineSpace, letterSpace;
00420 InterpretAttributes(m_fontAttrs, style, size, lineSpace, letterSpace);
00421
00422 m_pDisplay->SetFont(size, (style & 2) != 0, (style & 1) != 0);
00423
00424
00425 for (i = 0; i < theText.Size(); i++) {
00426 MHTextLine *pLine = theText.GetAt(i);
00427 pLine->m_nLineWidth = 0;
00428 for (int j = 0; j < pLine->m_Items.Size(); j++) {
00429 MHTextItem *pItem = pLine->m_Items.GetAt(j);
00430
00431 for (int k = 0; k < pItem->m_nTabCount; k++) pLine->m_nLineWidth += TABSTOP - pLine->m_nLineWidth % TABSTOP;
00432
00433 if (pItem->m_Unicode.isEmpty()) {
00434 int s = pItem->m_Text.Size();
00435 pItem->m_Unicode = QString::fromUtf8((const char*)pItem->m_Text.Bytes(), s);
00436 pItem->m_nUnicode = pItem->m_Unicode.length();
00437 }
00438
00439 int nFullText = pItem->m_nUnicode;
00440
00441 QRect rect = m_pDisplay->GetBounds(pItem->m_Unicode, pItem->m_nUnicode, m_nBoxWidth - pLine->m_nLineWidth);
00442
00443 if (nFullText != pItem->m_nUnicode && m_fTextWrap) {
00444 int nTruncated = pItem->m_nUnicode;
00445
00446 while (pItem->m_nUnicode > 0 && pItem->m_Unicode[pItem->m_nUnicode] != ' ') pItem->m_nUnicode--;
00447
00448 if (pItem->m_nUnicode == 0) pItem->m_nUnicode = nTruncated;
00449
00450 if (pItem->m_nUnicode == 0) pItem->m_nUnicode = 1;
00451
00452
00453 int nNewWidth = nFullText - pItem->m_nUnicode;
00454 int nNewStart = pItem->m_nUnicode;
00455
00456 while (nNewWidth != 0 && pItem->m_Unicode[nNewStart] == ' ') { nNewStart++; nNewWidth--; }
00457 if (nNewWidth != 0) {
00458
00459 MHTextLine *pNewLine = new MHTextLine;
00460 theText.InsertAt(pNewLine, i+1);
00461
00462 MHTextItem *pNewItem = pItem->NewItem();
00463 pNewLine->m_Items.Append(pNewItem);
00464 pNewItem->m_Unicode = pItem->m_Unicode.mid(nNewStart, nNewWidth);
00465 pNewItem->m_nUnicode = nNewWidth;
00466
00467 while (pLine->m_Items.Size() > j+1) {
00468 pNewLine->m_Items.Append(pLine->m_Items.GetAt(j+1));
00469 pLine->m_Items.RemoveAt(j+1);
00470 }
00471 }
00472
00473
00474 while (pItem->m_nUnicode > 1 && pItem->m_Unicode[pItem->m_nUnicode-1] == ' ') pItem->m_nUnicode--;
00475 rect = m_pDisplay->GetBounds(pItem->m_Unicode, pItem->m_nUnicode);
00476 }
00477
00478 pItem->m_Width = rect.width();
00479 pLine->m_nLineWidth += rect.width();
00480 if (rect.height() > pLine->m_nLineHeight) pLine->m_nLineHeight = rect.height();
00481 if (rect.bottom() > pLine->m_nDescent) pLine->m_nDescent = rect.bottom();
00482 }
00483 }
00484
00485
00486 int yOffset = 0;
00487
00488 int nNumLines = theText.Size();
00489 do {
00490 if (m_VertJ == End) yOffset = m_nBoxHeight - nNumLines * lineSpace;
00491 else if (m_VertJ == Centre) yOffset = (m_nBoxHeight - nNumLines * lineSpace)/2;
00492 if (yOffset < 0) nNumLines--;
00493 } while (yOffset < 0);
00494
00495 for (i = 0; i < nNumLines; i++) {
00496 MHTextLine *pLine = theText.GetAt(i);
00497 int xOffset = 0;
00498 if (m_HorizJ == End) xOffset = m_nBoxWidth - pLine->m_nLineWidth;
00499 else if (m_HorizJ == Centre) xOffset = (m_nBoxWidth - pLine->m_nLineWidth)/2;
00500
00501 for (int j = 0; j < pLine->m_Items.Size(); j++) {
00502 MHTextItem *pItem = pLine->m_Items.GetAt(j);
00503
00504 for (int k = 0; k < pItem->m_nTabCount; k++) xOffset += TABSTOP - xOffset % TABSTOP;
00505
00506 if (! pItem->m_Unicode.isEmpty()) {
00507 m_pDisplay->AddText(xOffset, yOffset + (pLine->m_nLineHeight + lineSpace)/2 - pLine->m_nDescent,
00508 pItem->m_Unicode.left(pItem->m_nUnicode), pItem->m_Colour);
00509 }
00510 xOffset += pItem->m_Width;
00511 }
00512 yOffset += lineSpace;
00513 if (yOffset + lineSpace > m_nBoxHeight) break;
00514 }
00515
00516
00517 for (int k = 0; k < theText.Size(); k++) delete(theText.GetAt(k));
00518 }
00519
00520 void MHText::Display(MHEngine *engine)
00521 {
00522 if (! m_fRunning || ! m_pDisplay || m_nBoxWidth == 0 || m_nBoxHeight == 0) return;
00523
00524 if ( m_NeedsRedraw)
00525 {
00526 Redraw();
00527 m_NeedsRedraw = false;
00528 }
00529
00530 engine->GetContext()->DrawRect(m_nPosX, m_nPosY, m_nBoxWidth, m_nBoxHeight, GetColour(m_bgColour));
00531 m_pDisplay->Draw(m_nPosX, m_nPosY);
00532 }
00533
00534
00535 QRegion MHText::GetOpaqueArea()
00536 {
00537 if (! m_fRunning || (GetColour(m_bgColour)).alpha() != 255) return QRegion();
00538 else return QRegion(QRect(m_nPosX, m_nPosY, m_nBoxWidth, m_nBoxHeight));
00539 }
00540
00541
00542 MHHyperText::MHHyperText(): MHInteractible(this)
00543 {
00544
00545 }
00546
00547 MHHyperText::~MHHyperText()
00548 {
00549
00550 }
00551
00552 void MHHyperText::Initialise(MHParseNode *p, MHEngine *engine)
00553 {
00554 MHText::Initialise(p, engine);
00555 MHInteractible::Initialise(p, engine);
00556
00557 }
00558
00559 void MHHyperText::PrintMe(FILE *fd, int nTabs) const
00560 {
00561 PrintTabs(fd, nTabs); fprintf(fd, "{:HyperText ");
00562 MHText::PrintMe(fd, nTabs+1);
00563 MHInteractible::PrintMe(fd, nTabs+1);
00564 fprintf(fd, "****TODO\n");
00565 PrintTabs(fd, nTabs); fprintf(fd, "}\n");
00566 }
00567
00568
00569 void MHSetFontAttributes::Initialise(MHParseNode *p, MHEngine *engine)
00570 {
00571 MHElemAction::Initialise(p, engine);
00572 m_FontAttrs.Initialise(p->GetArgN(1), engine);
00573 }
00574
00575 void MHSetFontAttributes::Perform(MHEngine *engine)
00576 {
00577
00578 MHOctetString newAttrs;
00579 m_FontAttrs.GetValue(newAttrs, engine);
00580 Target(engine)->SetFontAttributes(newAttrs, engine);
00581 }