// Return the colour, looking up in the palette if necessary. Used by the sub-classes MHRgba MHVisible::GetColour(const MHColour &colour) { int red = 0, green = 0, blue = 0, alpha = 0; int cSize = colour.m_ColStr.Size(); if (cSize != 4) { MHLOG(MHLogWarning, QString("Colour string has length %1 not 4.").arg(cSize)); } // Just in case the length is short we handle those properly. if (cSize > 0) { red = colour.m_ColStr.GetAt(0); } if (cSize > 1) { green = colour.m_ColStr.GetAt(1); } if (cSize > 2) { blue = colour.m_ColStr.GetAt(2); } if (cSize > 3) { alpha = 255 - colour.m_ColStr.GetAt(3); // Convert transparency to alpha } return MHRgba(red, green, blue, alpha); }
MHTextItem::MHTextItem() { m_nUnicode = 0; m_Width = 0; // Size of this block m_Colour = MHRgba(0, 0, 0, 255); m_nTabCount = 0; }
// Fill in the background. This is only called if there is some area of // the screen that is not covered with other visibles. void MHIContext::DrawBackground(const QRegion ®) { if (reg.isEmpty()) return; QRect bounds = reg.boundingRect(); DrawRect(bounds.x(), bounds.y(), bounds.width(), bounds.height(), MHRgba(0, 0, 0, 255)/* black. */); }
// Reset the drawing. void MHIDLA::Clear() { if (m_width == 0 || m_height == 0) { m_image = QImage(); return; } m_image = QImage(m_width, m_height, QImage::Format_ARGB32); // Fill the image with "transparent colour". DrawRect(0, 0, m_width, m_height, MHRgba(0, 0, 0, 0)); }
// Recreate the image. void MHText::Redraw() { if (! m_fRunning || !m_pDisplay) { return; } if (m_nBoxWidth == 0 || m_nBoxHeight == 0) { return; // Can't draw zero sized boxes. } m_pDisplay->SetSize(m_nBoxWidth, m_nBoxHeight); m_pDisplay->Clear(); MHRgba textColour = GetColour(m_textColour); // Process any escapes in the text and construct the text arrays. MHSequence <MHTextLine *> theText; // Set up the first item on the first line. MHTextItem *pCurrItem = new MHTextItem; MHTextLine *pCurrLine = new MHTextLine; pCurrLine->m_Items.Append(pCurrItem); theText.Append(pCurrLine); MHStack <MHRgba> m_ColourStack; // Stack to handle nested colour codes. m_ColourStack.Push(textColour); pCurrItem->m_Colour = textColour; // FILE *fd=stdout; fprintf(fd, "Redraw Text "); m_Content.PrintMe(fd, 0); fprintf(fd, "\n"); int i = 0; while (i < m_Content.Size()) { unsigned char ch = m_Content.GetAt(i++); if (ch == 0x09) // Tab - start a new item if we have any text in the existing one. { if (pCurrItem->m_Text.Size() != 0) { pCurrItem = pCurrItem->NewItem(); pCurrLine->m_Items.Append(pCurrItem); } if (m_HorizJ == Start) pCurrItem->m_nTabCount++; } else if (ch == 0x0d) // CR - line break. { // TODO: Two CRs next to one another are treated as </P> rather than <BR><BR> // This should also include the sequence CRLFCRLF. pCurrLine = new MHTextLine; theText.Append(pCurrLine); pCurrItem = pCurrItem->NewItem(); pCurrLine->m_Items.Append(pCurrItem); } else if (ch == 0x1b) // Escape - special codes. { if (i == m_Content.Size()) { break; } unsigned char code = m_Content.GetAt(i); // The only codes we are interested in are the start and end of colour. // TODO: We may also need "bold" and some hypertext colours. if (code >= 0x40 && code <= 0x5e) // Start code { // Start codes are followed by a parameter count and a number of parameter bytes. if (++i == m_Content.Size()) { break; } unsigned char paramCount = m_Content.GetAt(i); i++; if (code == 0x43 && paramCount == 4 && i + paramCount <= m_Content.Size()) { // Start of colour. if (pCurrItem->m_Text.Size() != 0) { pCurrItem = pCurrItem->NewItem(); pCurrLine->m_Items.Append(pCurrItem); } pCurrItem->m_Colour = MHRgba(m_Content.GetAt(i), m_Content.GetAt(i + 1), m_Content.GetAt(i + 2), 255 - m_Content.GetAt(i + 3)); // Push this colour onto the colour stack. m_ColourStack.Push(pCurrItem->m_Colour); } else { MHLOG(MHLogWarning, QString("Unknown text escape code 0x%1").arg(code, 2, 16)); } i += paramCount; // Skip the parameters } else if (code >= 0x60 && code <= 0x7e) // End code. { i++; if (code == 0x63) { if (m_ColourStack.Size() > 1) { m_ColourStack.Pop(); // Start a new item since we're using a new colour. if (pCurrItem->m_Text.Size() != 0) { pCurrItem = pCurrItem->NewItem(); pCurrLine->m_Items.Append(pCurrItem); } // Set the subsequent text in the colour we're using now. pCurrItem->m_Colour = m_ColourStack.Top(); } } else MHLOG(MHLogWarning, QString("Unknown text escape code 0x%1").arg(code,2,16)); } else MHLOG(MHLogWarning, QString("Unknown text escape code 0x%1").arg(code,2,16)); } else if (ch <= 0x1f) { // Certain characters including LF and the marker codes between 0x1c and 0x1f are // explicitly intended to be ignored. Include all the other codes. } else // Add to the current text. { int nStart = i - 1; while (i < m_Content.Size() && m_Content.GetAt(i) >= 0x20) { i++; } pCurrItem->m_Text.Append(MHOctetString(m_Content, nStart, i - nStart)); } } // Set up the initial attributes. int style, size, lineSpace, letterSpace; InterpretAttributes(m_fontAttrs, style, size, lineSpace, letterSpace); // Create a font with this information. m_pDisplay->SetFont(size, (style & 2) != 0, (style & 1) != 0); // Calculate the layout of each section. for (i = 0; i < theText.Size(); i++) { MHTextLine *pLine = theText.GetAt(i); pLine->m_nLineWidth = 0; for (int j = 0; j < pLine->m_Items.Size(); j++) { MHTextItem *pItem = pLine->m_Items.GetAt(j); // Set any tabs. pLine->m_nLineWidth = Tabs(pLine->m_nLineWidth, pItem->m_nTabCount); if (pItem->m_Unicode.isEmpty()) // Convert UTF-8 to Unicode. { int s = pItem->m_Text.Size(); pItem->m_Unicode = QString::fromUtf8((const char *)pItem->m_Text.Bytes(), s); pItem->m_nUnicode = pItem->m_Unicode.length(); } // Fit the text onto the line. int nFullText = pItem->m_nUnicode; // Get the box size and update pItem->m_nUnicode to the number that will fit. QRect rect = m_pDisplay->GetBounds(pItem->m_Unicode, pItem->m_nUnicode, m_nBoxWidth - pLine->m_nLineWidth); if (nFullText != pItem->m_nUnicode && m_fTextWrap) // Doesn't fit, we have to word-wrap. { int nTruncated = pItem->m_nUnicode; // Just in case. // Now remove characters until we find a word-break character. while (pItem->m_nUnicode > 0 && pItem->m_Unicode[pItem->m_nUnicode] != ' ') { pItem->m_nUnicode--; } // If there are now word-break characters we truncate the text. if (pItem->m_nUnicode == 0) { pItem->m_nUnicode = nTruncated; } // Special case to avoid infinite loop if the box is very narrow. if (pItem->m_nUnicode == 0) { pItem->m_nUnicode = 1; } // We need to move the text we've cut off this line into a new line. int nNewWidth = nFullText - pItem->m_nUnicode; int nNewStart = pItem->m_nUnicode; // Remove any spaces at the start of the new section. while (nNewWidth != 0 && pItem->m_Unicode[nNewStart] == ' ') { nNewStart++; nNewWidth--; } if (nNewWidth != 0) { // Create a new line from the extra text. MHTextLine *pNewLine = new MHTextLine; theText.InsertAt(pNewLine, i + 1); // The first item on the new line is the rest of the text. MHTextItem *pNewItem = pItem->NewItem(); pNewLine->m_Items.Append(pNewItem); pNewItem->m_Unicode = pItem->m_Unicode.mid(nNewStart, nNewWidth); pNewItem->m_nUnicode = nNewWidth; // Move any remaining items, e.g. in a different colour, from this line onto the new line. while (pLine->m_Items.Size() > j + 1) { pNewLine->m_Items.Append(pLine->m_Items.GetAt(j + 1)); pLine->m_Items.RemoveAt(j + 1); } } // Remove any spaces at the end of the old section. If we don't do that and // we are centering or right aligning the text we'll get it wrong. while (pItem->m_nUnicode > 1 && pItem->m_Unicode[pItem->m_nUnicode-1] == ' ') { pItem->m_nUnicode--; } rect = m_pDisplay->GetBounds(pItem->m_Unicode, pItem->m_nUnicode); } pItem->m_Width = rect.width(); pLine->m_nLineWidth += rect.width(); if (rect.height() > pLine->m_nLineHeight) { pLine->m_nLineHeight = rect.height(); } if (rect.bottom() > pLine->m_nDescent) { pLine->m_nDescent = rect.bottom(); } } } // Now output the text. int yOffset = 0; // If there isn't space for all the lines we should drop extra lines. int nNumLines = theText.Size(); do { if (m_VertJ == End) { yOffset = m_nBoxHeight - nNumLines * lineSpace; } else if (m_VertJ == Centre) { yOffset = (m_nBoxHeight - nNumLines * lineSpace) / 2; } if (yOffset < 0) { nNumLines--; } } while (yOffset < 0); for (i = 0; i < nNumLines; i++) { MHTextLine *pLine = theText.GetAt(i); int xOffset = 0; if (m_HorizJ == End) { xOffset = m_nBoxWidth - pLine->m_nLineWidth; } else if (m_HorizJ == Centre) { xOffset = (m_nBoxWidth - pLine->m_nLineWidth) / 2; } for (int j = 0; j < pLine->m_Items.Size(); j++) { MHTextItem *pItem = pLine->m_Items.GetAt(j); // Tab across if necessary. xOffset = Tabs(xOffset, pItem->m_nTabCount); if (! pItem->m_Unicode.isEmpty()) // We may have blank lines. { m_pDisplay->AddText(xOffset, yOffset + (pLine->m_nLineHeight + lineSpace) / 2 - pLine->m_nDescent, pItem->m_Unicode.left(pItem->m_nUnicode), pItem->m_Colour); } xOffset += pItem->m_Width; } yOffset += lineSpace; if (yOffset + lineSpace > m_nBoxHeight) { break; } } // Clean up. for (int k = 0; k < theText.Size(); k++) { delete(theText.GetAt(k)); } }