[Scummvm-git-logs] scummvm master -> 67ab2657d8a20b0defb52be24280e87fc58b68c2
sev-
noreply at scummvm.org
Tue Oct 24 00:38:29 UTC 2023
This automated email contains information about 3 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
4e614216b2 GRAPHICS: MACGUI: Add equality test for MacFontRun
37deafe413 GRAPHICS: MACGUI: Moved more methods to MacTextCanvas
67ab2657d8 GRAPHICS: MACGUI: Split out MacTextCanvas into a separate file
Commit: 4e614216b26dffd551e56092fbcd489e73bfa366
https://github.com/scummvm/scummvm/commit/4e614216b26dffd551e56092fbcd489e73bfa366
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2023-10-24T02:38:14+02:00
Commit Message:
GRAPHICS: MACGUI: Add equality test for MacFontRun
Changed paths:
graphics/macgui/mactext.h
diff --git a/graphics/macgui/mactext.h b/graphics/macgui/mactext.h
index 96d647a0cdc..ae158e36132 100644
--- a/graphics/macgui/mactext.h
+++ b/graphics/macgui/mactext.h
@@ -115,6 +115,17 @@ struct MacFontRun {
Common::CodePage getEncoding();
bool plainByteMode();
Common::String getEncodedText();
+
+ bool equals(const MacFontRun *x, const MacFontRun *y) {
+ return (x->fontId == y->fontId &&
+ x->textSlant == y->textSlant &&
+ x->fontSize == y->fontSize &&
+ x->palinfo1 == y->palinfo1 &&
+ x->palinfo2 == y->palinfo2 &&
+ x->palinfo3 == y->palinfo3 &&
+ x->fgcolor == y->fgcolor);
+ }
+
};
struct MacTextLine;
Commit: 37deafe413e7e7a984c76f9a67f20c9d3844e040
https://github.com/scummvm/scummvm/commit/37deafe413e7e7a984c76f9a67f20c9d3844e040
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2023-10-24T02:38:17+02:00
Commit Message:
GRAPHICS: MACGUI: Moved more methods to MacTextCanvas
Changed paths:
graphics/macgui/mactext.cpp
graphics/macgui/mactext.h
diff --git a/graphics/macgui/mactext.cpp b/graphics/macgui/mactext.cpp
index 57adb0a5bc9..616db8bc6d3 100644
--- a/graphics/macgui/mactext.cpp
+++ b/graphics/macgui/mactext.cpp
@@ -338,7 +338,7 @@ void MacText::setMaxWidth(int maxWidth) {
return;
}
- Common::U32String str = getTextChunk(0, 0, -1, -1, true, true);
+ Common::U32String str = _canvas.getTextChunk(0, 0, -1, -1, true, true);
// keep the cursor pos
int ppos = 0;
@@ -1807,7 +1807,7 @@ Common::U32String MacText::getSelection(bool formatted, bool newlines) {
SWAP(s.startCol, s.endCol);
}
- return getTextChunk(s.startRow, s.startCol, s.endRow, s.endCol, formatted, newlines);
+ return _canvas.getTextChunk(s.startRow, s.startCol, s.endRow, s.endCol, formatted, newlines);
}
void MacText::clearSelection() {
@@ -1909,7 +1909,7 @@ bool MacText::isCutAllowed() {
}
Common::U32String MacText::getEditedString() {
- return getTextChunk(_editableRow, 0, -1, -1);
+ return _canvas.getTextChunk(_editableRow, 0, -1, -1);
}
Common::U32String MacText::getPlainText() {
@@ -1934,7 +1934,7 @@ Common::U32String MacText::cutSelection() {
SWAP(s.startCol, s.endCol);
}
- Common::U32String selection = MacText::getTextChunk(s.startRow, s.startCol, s.endRow, s.endCol, true, true);
+ Common::U32String selection = _canvas.getTextChunk(s.startRow, s.startCol, s.endRow, s.endCol, true, true);
deleteSelection();
clearSelection();
@@ -2452,39 +2452,43 @@ void MacText::getRowCol(int x, int y, int *sx, int *sy, int *row, int *col, int
*chunk_ = (int)chunk;
}
+Common::U32String MacText::getTextChunk(int startRow, int startCol, int endRow, int endCol, bool formatted, bool newlines) {
+ return _canvas.getTextChunk(startRow, startCol, endRow, endCol, formatted, newlines);
+}
+
// If adjacent chunks have same format, then skip the format definition
// This happens when a long paragraph is split into several lines
#define ADDFORMATTING() \
if (formatted) { \
- formatting = Common::U32String(_canvas._text[i].chunks[chunk].toString()); \
+ formatting = Common::U32String(_text[i].chunks[chunk].toString()); \
if (formatting != prevformatting) { \
res += formatting; \
prevformatting = formatting; \
} \
}
-Common::U32String MacText::getTextChunk(int startRow, int startCol, int endRow, int endCol, bool formatted, bool newlines) {
+Common::U32String MacTextCanvas::getTextChunk(int startRow, int startCol, int endRow, int endCol, bool formatted, bool newlines) {
Common::U32String res("");
if (endRow == -1)
- endRow = _canvas._text.size() - 1;
+ endRow = _text.size() - 1;
if (endCol == -1)
- endCol = _canvas.getLineCharWidth(endRow);
- if (_canvas._text.empty()) {
+ endCol = getLineCharWidth(endRow);
+ if (_text.empty()) {
return res;
}
- startRow = CLIP(startRow, 0, (int)_canvas._text.size() - 1);
- endRow = CLIP(endRow, 0, (int)_canvas._text.size() - 1);
+ startRow = CLIP(startRow, 0, (int)_text.size() - 1);
+ endRow = CLIP(endRow, 0, (int)_text.size() - 1);
Common::U32String formatting(""), prevformatting("");
for (int i = startRow; i <= endRow; i++) {
// We requested only part of one line
if (i == startRow && i == endRow) {
- for (uint chunk = 0; chunk < _canvas._text[i].chunks.size(); chunk++) {
- if (_canvas._text[i].chunks[chunk].text.empty()) {
+ for (uint chunk = 0; chunk < _text[i].chunks.size(); chunk++) {
+ if (_text[i].chunks[chunk].text.empty()) {
// skip empty chunks, but keep them formatted,
// a text input box needs to keep the formatting even when all text is removed.
ADDFORMATTING();
@@ -2494,68 +2498,68 @@ Common::U32String MacText::getTextChunk(int startRow, int startCol, int endRow,
if (startCol <= 0) {
ADDFORMATTING();
- if (endCol >= (int)_canvas._text[i].chunks[chunk].text.size())
- res += _canvas._text[i].chunks[chunk].text;
+ if (endCol >= (int)_text[i].chunks[chunk].text.size())
+ res += _text[i].chunks[chunk].text;
else
- res += _canvas._text[i].chunks[chunk].text.substr(0, endCol);
- } else if ((int)_canvas._text[i].chunks[chunk].text.size() > startCol) {
+ res += _text[i].chunks[chunk].text.substr(0, endCol);
+ } else if ((int)_text[i].chunks[chunk].text.size() > startCol) {
ADDFORMATTING();
- res += _canvas._text[i].chunks[chunk].text.substr(startCol, endCol - startCol);
+ res += _text[i].chunks[chunk].text.substr(startCol, endCol - startCol);
}
- startCol -= _canvas._text[i].chunks[chunk].text.size();
- endCol -= _canvas._text[i].chunks[chunk].text.size();
+ startCol -= _text[i].chunks[chunk].text.size();
+ endCol -= _text[i].chunks[chunk].text.size();
if (endCol <= 0)
break;
}
// We are at the top line and it is not completely requested
} else if (i == startRow && startCol != 0) {
- for (uint chunk = 0; chunk < _canvas._text[i].chunks.size(); chunk++) {
- if (_canvas._text[i].chunks[chunk].text.empty()) // skip empty chunks
+ for (uint chunk = 0; chunk < _text[i].chunks.size(); chunk++) {
+ if (_text[i].chunks[chunk].text.empty()) // skip empty chunks
continue;
if (startCol <= 0) {
ADDFORMATTING();
- res += _canvas._text[i].chunks[chunk].text;
- } else if ((int)_canvas._text[i].chunks[chunk].text.size() > startCol) {
+ res += _text[i].chunks[chunk].text;
+ } else if ((int)_text[i].chunks[chunk].text.size() > startCol) {
ADDFORMATTING();
- res += _canvas._text[i].chunks[chunk].text.substr(startCol);
+ res += _text[i].chunks[chunk].text.substr(startCol);
}
- startCol -= _canvas._text[i].chunks[chunk].text.size();
+ startCol -= _text[i].chunks[chunk].text.size();
}
- if (newlines && _canvas._text[i].paragraphEnd)
+ if (newlines && _text[i].paragraphEnd)
res += '\n';
// We are at the end row, and it could be not completely requested
} else if (i == endRow) {
- for (uint chunk = 0; chunk < _canvas._text[i].chunks.size(); chunk++) {
- if (_canvas._text[i].chunks[chunk].text.empty()) // skip empty chunks
+ for (uint chunk = 0; chunk < _text[i].chunks.size(); chunk++) {
+ if (_text[i].chunks[chunk].text.empty()) // skip empty chunks
continue;
ADDFORMATTING();
- if (endCol >= (int)_canvas._text[i].chunks[chunk].text.size())
- res += _canvas._text[i].chunks[chunk].text;
+ if (endCol >= (int)_text[i].chunks[chunk].text.size())
+ res += _text[i].chunks[chunk].text;
else
- res += _canvas._text[i].chunks[chunk].text.substr(0, endCol);
+ res += _text[i].chunks[chunk].text.substr(0, endCol);
- endCol -= _canvas._text[i].chunks[chunk].text.size();
+ endCol -= _text[i].chunks[chunk].text.size();
if (endCol <= 0)
break;
}
// We are in the middle of requested range, pass whole line
} else {
- for (uint chunk = 0; chunk < _canvas._text[i].chunks.size(); chunk++) {
- if (_canvas._text[i].chunks[chunk].text.empty()) // skip empty chunks
+ for (uint chunk = 0; chunk < _text[i].chunks.size(); chunk++) {
+ if (_text[i].chunks[chunk].text.empty()) // skip empty chunks
continue;
ADDFORMATTING();
- res += _canvas._text[i].chunks[chunk].text;
+ res += _text[i].chunks[chunk].text;
}
- if (newlines && _canvas._text[i].paragraphEnd)
+ if (newlines && _text[i].paragraphEnd)
res += '\n';
}
}
@@ -2583,8 +2587,8 @@ void MacText::insertTextFromClipboard() {
ppos += _canvas.getLineCharWidth(i);
ppos += _cursorCol;
- Common::U32String pre_str = getTextChunk(start, 0, _cursorRow, _cursorCol, true, true);
- Common::U32String sub_str = getTextChunk(_cursorRow, _cursorCol, end, _canvas.getLineCharWidth(end, true), true, true);
+ Common::U32String pre_str = _canvas.getTextChunk(start, 0, _cursorRow, _cursorCol, true, true);
+ Common::U32String sub_str = _canvas.getTextChunk(_cursorRow, _cursorCol, end, _canvas.getLineCharWidth(end, true), true, true);
// Remove it from the text
for (int i = start; i <= end; i++) {
@@ -2655,7 +2659,7 @@ void MacText::insertChar(byte c, int *row, int *col) {
(*col)++;
if (_canvas.getLineWidth(*row) - oldw + chunkw > _canvas._maxWidth) { // Needs reshuffle
- reshuffleParagraph(row, col);
+ _canvas.reshuffleParagraph(row, col, _defaultFormatting);
_fullRefresh = true;
recalcDims();
render();
@@ -2695,7 +2699,7 @@ void MacText::deleteSelection() {
deletePreviousCharInternal(&row, &col);
}
- reshuffleParagraph(&row, &col);
+ _canvas.reshuffleParagraph(&row, &col, _defaultFormatting);
_fullRefresh = true;
recalcDims();
@@ -2755,7 +2759,7 @@ void MacText::deletePreviousChar(int *row, int *col) {
}
D(9, "**deleteChar cursor row %d col %d", _cursorRow, _cursorCol);
- reshuffleParagraph(row, col);
+ _canvas.reshuffleParagraph(row, col, _defaultFormatting);
_fullRefresh = true;
recalcDims();
@@ -2799,7 +2803,7 @@ void MacText::addNewLine(int *row, int *col) {
(*row)++;
*col = 0;
- reshuffleParagraph(row, col);
+ _canvas.reshuffleParagraph(row, col, _defaultFormatting);
for (int i = 0; i < (int)_canvas._text.size(); i++) {
D(9, "** addNewLine line %d", i);
@@ -2814,42 +2818,42 @@ void MacText::addNewLine(int *row, int *col) {
render();
}
-void MacText::reshuffleParagraph(int *row, int *col) {
+void MacTextCanvas::reshuffleParagraph(int *row, int *col, MacFontRun &defaultFormatting) {
// First, we looking for the paragraph start and end
int start = *row, end = *row;
- while (start && !_canvas._text[start - 1].paragraphEnd)
+ while (start && !_text[start - 1].paragraphEnd)
start--;
- while (end < (int)_canvas._text.size() - 1 && !_canvas._text[end].paragraphEnd) // stop at last line
+ while (end < (int)_text.size() - 1 && !_text[end].paragraphEnd) // stop at last line
end++;
// Get character pos within paragraph
int ppos = 0;
for (int i = start; i < *row; i++)
- ppos += _canvas.getLineCharWidth(i);
+ ppos += getLineCharWidth(i);
ppos += *col;
// Get whole paragraph
- Common::U32String paragraph = getTextChunk(start, 0, end, _canvas.getLineCharWidth(end, true), true, true);
+ Common::U32String paragraph = getTextChunk(start, 0, end, getLineCharWidth(end, true), true, true);
// Remove it from the text
for (int i = start; i <= end; i++) {
- _canvas._text.remove_at(start);
+ _text.remove_at(start);
}
// And now read it
D(9, "start %d end %d", start, end);
- _canvas.splitString(paragraph, start, _defaultFormatting);
+ splitString(paragraph, start, defaultFormatting);
// Find new pos within paragraph after reshuffling
*row = start;
warning("FIXME, bad design");
- while (ppos > _canvas.getLineCharWidth(*row, true)) {
- ppos -= _canvas.getLineCharWidth(*row, true);
+ while (ppos > getLineCharWidth(*row, true)) {
+ ppos -= getLineCharWidth(*row, true);
(*row)++;
}
*col = ppos;
diff --git a/graphics/macgui/mactext.h b/graphics/macgui/mactext.h
index ae158e36132..69f1d940568 100644
--- a/graphics/macgui/mactext.h
+++ b/graphics/macgui/mactext.h
@@ -176,6 +176,14 @@ public:
const Common::U32String::value_type *splitString(const Common::U32String::value_type *s, int curLine, MacFontRun &defaultFormatting);
void chopChunk(const Common::U32String &str, int *curLinePtr, int indent, int maxWidth);
+ Common::U32String getTextChunk(int startRow, int startCol, int endRow, int endCol, bool formatted = false, bool newlines = true);
+
+ /**
+ * Rewraps paragraph containing given text row.
+ * When text is modified, we redo whole thing again without touching
+ * other paragraphs. Also, cursor position is returned in the arguments
+ */
+ void reshuffleParagraph(int *row, int *col, MacFontRun &defaultFormatting);
void processTable(int line, int maxWidth);
};
@@ -381,13 +389,6 @@ private:
void init(uint32 fgcolor, uint32 bgcolor, int maxWidth, TextAlign textAlignment, int interlinear, uint16 textShadow, bool macFontMode);
bool isCutAllowed();
- /**
- * Rewraps paragraph containing given text row.
- * When text is modified, we redo whole thing again without touching
- * other paragraphs. Also, cursor position is returned in the arguments
- */
- void reshuffleParagraph(int *row, int *col);
-
void recalcDims();
void drawSelection(int xoff, int yoff);
Commit: 67ab2657d8a20b0defb52be24280e87fc58b68c2
https://github.com/scummvm/scummvm/commit/67ab2657d8a20b0defb52be24280e87fc58b68c2
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2023-10-24T02:38:17+02:00
Commit Message:
GRAPHICS: MACGUI: Split out MacTextCanvas into a separate file
Changed paths:
A graphics/macgui/mactext-canvas.cpp
A graphics/macgui/mactext-canvas.h
graphics/macgui/mactext.cpp
graphics/macgui/mactext.h
graphics/module.mk
diff --git a/graphics/macgui/mactext-canvas.cpp b/graphics/macgui/mactext-canvas.cpp
new file mode 100644
index 00000000000..77e94891ea2
--- /dev/null
+++ b/graphics/macgui/mactext-canvas.cpp
@@ -0,0 +1,1101 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "common/tokenizer.h"
+#include "common/unicode-bidi.h"
+
+#include "graphics/macgui/mactext.h"
+
+namespace Graphics {
+
+#define DEBUG 0
+
+#if DEBUG
+#define D(...) debug(__VA_ARGS__)
+#else
+#define D(...) ;
+#endif
+
+// Adds the given string to the end of the last line/chunk
+// while observing the _canvas._maxWidth and keeping this chunk's
+// formatting
+void MacTextCanvas::chopChunk(const Common::U32String &str, int *curLinePtr, int indent, int maxWidth) {
+ int curLine = *curLinePtr;
+ int curChunk;
+ MacFontRun *chunk;
+
+ curChunk = _text[curLine].chunks.size() - 1;
+ chunk = &_text[curLine].chunks[curChunk];
+
+ // Check if there is nothing to add, then remove the last chunk
+ // This happens when the previous run is finished only with
+ // empty formatting, or when we were adding text for the first time
+ if (chunk->text.empty() && str.empty()) {
+ D(9, "** chopChunk, replaced formatting, line %d", curLine);
+
+ _text[curLine].chunks.pop_back();
+
+ return;
+ }
+
+ if (maxWidth == -1) {
+ chunk->text += str;
+
+ return;
+ }
+
+ Common::Array<Common::U32String> text;
+
+ int w = getLineWidth(curLine, true);
+ D(9, "** chopChunk before wrap \"%s\"", Common::toPrintable(str.encode()).c_str());
+
+ chunk->getFont()->wordWrapText(str, maxWidth, text, w);
+
+ if (text.size() == 0) {
+ warning("chopChunk: too narrow width, >%d", maxWidth);
+ chunk->text += str;
+ getLineCharWidth(curLine, true);
+
+ return;
+ }
+
+ for (int i = 0; i < (int)text.size(); i++) {
+ D(9, "** chopChunk result %d \"%s\"", i, toPrintable(text[i].encode()).c_str());
+ }
+ chunk->text += text[0];
+
+ // Recalc dims
+ getLineWidth(curLine, true);
+
+ D(9, "** chopChunk, subchunk: \"%s\" (%d lines, maxW: %d)", toPrintable(text[0].encode()).c_str(), text.size(), maxWidth);
+
+ // We do not overlap, so we're done
+ if (text.size() == 1)
+ return;
+
+ // Now add rest of the chunks
+ MacFontRun newchunk = _text[curLine].chunks[curChunk];
+
+ for (uint i = 1; i < text.size(); i++) {
+ newchunk.text = text[i];
+
+ curLine++;
+ _text.insert_at(curLine, MacTextLine());
+ _text[curLine].chunks.push_back(newchunk);
+ _text[curLine].indent = indent;
+ _text[curLine].firstLineIndent = 0;
+
+ D(9, "** chopChunk, added line (firstIndent: %d): \"%s\"", _text[curLine].firstLineIndent, toPrintable(text[i].encode()).c_str());
+ }
+
+ *curLinePtr = curLine;
+}
+
+void MacTextCanvas::splitString(const Common::U32String &str, int curLine, MacFontRun &defaultFormatting) {
+ D(9, "** splitString(\"%s\", %d)", toPrintable(str.encode()).c_str(), curLine);
+
+ if (str.empty()) {
+ D(9, "** splitString, empty line");
+ return;
+ }
+
+ (void)splitString(str.c_str(), curLine, defaultFormatting);
+}
+
+const Common::U32String::value_type *MacTextCanvas::splitString(const Common::U32String::value_type *s, int curLine, MacFontRun &defaultFormatting) {
+ if (_text.empty()) {
+ _text.resize(1);
+ _text[0].chunks.push_back(defaultFormatting);
+ D(9, "** splitString, added default formatting");
+ } else {
+ D(9, "** splitString, continuing, %d lines", _text.size());
+ }
+
+ Common::U32String tmp;
+
+ if (curLine == -1 || curLine >= (int)_text.size())
+ curLine = _text.size() - 1;
+
+ int curChunk = _text[curLine].chunks.size() - 1;
+ MacFontRun chunk = _text[curLine].chunks[curChunk];
+ int indentSize = 0;
+ int firstLineIndent = 0;
+ bool inTable = false;
+
+ while (*s) {
+ firstLineIndent = 0;
+
+ tmp.clear();
+
+ MacTextLine *curTextLine = &_text[curLine];
+
+ while (*s) {
+ bool endOfLine = false;
+
+ // Scan till next font change or end of line
+ while (*s && *s != '\001') {
+ if (*s == '\r') {
+ s++;
+ if (*s == '\n') // Skip whole '\r\n'
+ s++;
+
+ endOfLine = true;
+
+ break;
+ }
+
+ // deal with single \n
+ if (*s == '\n') {
+ s++;
+
+ endOfLine = true;
+ break;
+ }
+
+ tmp += *s;
+
+ s++;
+ }
+
+ if (*s == '\001') // If it was \001, skip it
+ s++;
+
+ if (*s == '\001') { // \001\001 -> \001
+ tmp += *s++;
+
+ if (*s) // Check we reached end of line
+ continue;
+ }
+
+ D(9, "** splitString, chunk: \"%s\"", Common::toPrintable(tmp.encode()).c_str());
+
+ // Okay, now we are either at the end of the line, or in the next
+ // chunk definition. That means, that we have to store the previous chunk
+ chopChunk(tmp, &curLine, indentSize, _maxWidth > 0 ? _maxWidth - indentSize : _maxWidth);
+
+ curTextLine = &_text[curLine];
+
+ firstLineIndent = curTextLine->firstLineIndent;
+
+ tmp.clear();
+
+ // If it is end of the line, we're done
+ if (!*s) {
+ D(9, "** splitString, end of line");
+
+ break;
+ }
+
+ // get format (sync with stripFormat() )
+ if (*s == '\016') { // human-readable format
+ s++;
+
+ // First two digits is slant, third digit is Header number
+ switch (*s) {
+ case '+': { // \016+XXYZ -- opening textSlant, H<Y>, indent<+Z>
+ uint16 textSlant, headSize, indent;
+ s++;
+
+ s = readHex(&textSlant, s, 2);
+
+ chunk.textSlant |= textSlant; // Setting the specified bit
+
+ s = readHex(&headSize, s, 1);
+ if (headSize >= 1 && headSize <= 6) { // set
+ const float sizes[] = { 1, 2.0f, 1.41f, 1.155f, 1.0f, .894f, .816f };
+ chunk.fontSize = defaultFormatting.fontSize * sizes[headSize];
+ }
+
+ s = readHex(&indent, s, 1);
+
+ if (s)
+ indentSize += indent * chunk.fontSize * 2;
+
+ D(9, "** splitString+: fontId: %d, textSlant: %d, fontSize: %d, indent: %d",
+ chunk.fontId, chunk.textSlant, chunk.fontSize,
+ indent);
+
+ break;
+ }
+ case '-': { // \016-XXYZ -- closing textSlant, H<Y>, indent<+Z>
+ uint16 textSlant, headSize, indent;
+ s++;
+
+ s = readHex(&textSlant, s, 2);
+
+ chunk.textSlant &= ~textSlant; // Clearing the specified bit
+
+ s = readHex(&headSize, s, 1);
+ if (headSize == 0xf) // reset
+ chunk.fontSize = defaultFormatting.fontSize;
+
+ s = readHex(&indent, s, 1);
+
+ if (s)
+ indentSize -= indent * chunk.fontSize * 2;
+
+ D(9, "** splitString-: fontId: %d, textSlant: %d, fontSize: %d, indent: %d",
+ chunk.fontId, chunk.textSlant, chunk.fontSize,
+ indent);
+ break;
+ }
+
+ case '[': { // \016[RRGGBB -- setting color
+ uint16 palinfo1, palinfo2, palinfo3;
+ s++;
+
+ s = readHex(&palinfo1, s, 4);
+ s = readHex(&palinfo2, s, 4);
+ s = readHex(&palinfo3, s, 4);
+
+ chunk.palinfo1 = palinfo1;
+ chunk.palinfo2 = palinfo2;
+ chunk.palinfo3 = palinfo3;
+ chunk.fgcolor = _wm->findBestColor(palinfo1 & 0xff, palinfo2 & 0xff, palinfo3 & 0xff);
+
+ D(9, "** splitString[: %08x", chunk.fgcolor);
+ break;
+ }
+
+ case ']': { // \016] -- setting default color
+ s++;
+
+ chunk.palinfo1 = defaultFormatting.palinfo1;
+ chunk.palinfo2 = defaultFormatting.palinfo2;
+ chunk.palinfo3 = defaultFormatting.palinfo3;
+ chunk.fgcolor = defaultFormatting.fgcolor;
+
+ D(9, "** splitString]: %08x", chunk.fgcolor);
+ break;
+ }
+
+ case '*': { // \016*XXsssssss -- negative indent, XX size, sssss is the string
+ s++;
+
+ uint16 len;
+
+ s = readHex(&len, s, 2);
+
+ Common::U32String bullet = Common::U32String(s, len);
+
+ s += len;
+
+ firstLineIndent = -chunk.getFont()->getStringWidth(bullet);
+
+ D(9, "** splitString*: %02x '%s' (%d)", len, bullet.encode().c_str(), firstLineIndent);
+ break;
+ }
+
+ case 'i': { // \016iXXNNnnnnAAaaaaTTttt -- image, XX% width,
+ // NN, nnnn -- filename len and text
+ // AA, aaaa -- alt len and text
+ // TT, tttt -- text (tooltip) len and text
+ s++;
+
+ uint16 len;
+
+ s = readHex(&_text[curLine].picpercent, s, 2);
+ s = readHex(&len, s, 2);
+ _text[curLine].picfname = Common::U32String(s, len).encode();
+ s += len;
+
+ s = readHex(&len, s, 2);
+ _text[curLine].picalt = Common::U32String(s, len);
+ s += len;
+
+ s = readHex(&len, s, 2);
+ _text[curLine].pictitle = Common::U32String(s, len);
+ s += len;
+
+ D(9, "** splitString[i]: %d%% fname: '%s' alt: '%s' title: '%s'",
+ _text[curLine].picpercent,
+ _text[curLine].picfname.c_str(), _text[curLine].picalt.encode().c_str(),
+ _text[curLine].pictitle.encode().c_str());
+ break;
+ }
+
+ case 't': { // \016tXXXX -- switch to the requested font id
+ s++;
+
+ uint16 fontId;
+
+ s = readHex(&fontId, s, 4);
+
+ chunk.fontId = fontId == 0xffff ? defaultFormatting.fontId : fontId;
+
+ D(9, "** splitString[t]: fontId: %d", fontId);
+ break;
+ }
+
+ case 'l': { // \016lLLllll -- link len and text
+ s++;
+
+ uint16 len;
+
+ s = readHex(&len, s, 2);
+ chunk.link = Common::U32String(s, len);
+ s += len;
+
+ D(9, "** splitString[l]: link: %s", chunk.link.c_str());
+ break;
+ }
+
+ case 'T': { // \016T -- table
+ s++;
+
+ char cmd = *s++;
+
+ if (cmd == 'h') { // Header, beginning of the table
+ curTextLine->table = new Common::Array<MacTextTableRow>();
+ inTable = true;
+
+ D(9, "** splitString[table header]");
+ } else if (cmd == 'b') { // Body start
+ D(9, "** splitString[body start]");
+ } else if (cmd == 'B') { // Body end
+ inTable = false;
+
+ D(9, "** splitString[body end]");
+ processTable(curLine, _maxWidth);
+
+ continue;
+ } else if (cmd == 'r') { // Row
+ curTextLine->table->push_back(MacTextTableRow());
+ D(9, "** splitString[row]");
+ } else if (cmd == 'c') { // Cell start
+ uint16 align;
+ s = readHex(&align, s, 2);
+
+ curTextLine->table->back().cells.push_back(MacTextCanvas());
+
+ MacTextCanvas *cellCanvas = &curTextLine->table->back().cells.back();
+ cellCanvas->_textAlignment = (TextAlign)align;
+ cellCanvas->_wm = _wm;
+ cellCanvas->_macText = _macText;
+ cellCanvas->_maxWidth = -1;
+ cellCanvas->_macFontMode = _macFontMode;
+ cellCanvas->_tfgcolor = _tfgcolor;
+ cellCanvas->_tbgcolor = _tbgcolor;
+
+ D(9, "** splitString[cell start]: align: %d", align);
+
+ D(9, "** splitString[RECURSION start]");
+
+ s = cellCanvas->splitString(s, curLine, defaultFormatting);
+
+ D(9, "** splitString[RECURSION end]");
+ } else if (cmd == 'C') { // Cell end
+ D(9, "** splitString[cell end]");
+
+ return s;
+ } else {
+ error("MacText: Unknown table subcommand (%c)", cmd);
+ }
+ break;
+ }
+
+ default: {
+ uint16 fontId, textSlant, fontSize, palinfo1, palinfo2, palinfo3;
+
+ s = readHex(&fontId, s, 4);
+ s = readHex(&textSlant, s, 2);
+ s = readHex(&fontSize, s, 4);
+ s = readHex(&palinfo1, s, 4);
+ s = readHex(&palinfo2, s, 4);
+ s = readHex(&palinfo3, s, 4);
+
+ chunk.setValues(_wm, fontId, textSlant, fontSize, palinfo1, palinfo2, palinfo3);
+
+ D(9, "** splitString: fontId: %d, textSlant: %d, fontSize: %d, fg: %04x",
+ fontId, textSlant, fontSize, chunk.fgcolor);
+
+ // So far, we enforce single font here, though in the future, font size could be altered
+ if (!_macFontMode)
+ chunk.font = defaultFormatting.font;
+ }
+ }
+ }
+
+
+ curTextLine->indent = indentSize;
+ curTextLine->firstLineIndent = firstLineIndent;
+
+ // Push new formatting
+ curTextLine->chunks.push_back(chunk);
+
+ // If we reached end of paragraph, go to outer loop
+ if (endOfLine)
+ break;
+ }
+
+ // We avoid adding new lines while in table. Recursive cell rendering
+ // has this flag as false (obviously)
+ if (inTable)
+ continue;
+
+ // Add new line
+ D(9, "** splitString: new line");
+
+ curTextLine->paragraphEnd = true;
+ // if the chunks is empty, which means the line will not be rendered properly
+ // so we add a empty string here
+ if (curTextLine->chunks.empty()) {
+ curTextLine->chunks.push_back(defaultFormatting);
+ }
+
+ curLine++;
+ _text.insert_at(curLine, MacTextLine());
+ _text[curLine].chunks.push_back(chunk);
+
+ curTextLine = &_text[curLine];
+ }
+
+#if DEBUG
+ for (uint i = 0; i < _text.size(); i++) {
+ debugN(9, "** splitString: %2d ", i);
+
+ for (uint j = 0; j < _text[i].chunks.size(); j++)
+ debugN(9, "[%d] \"%s\"", _text[i].chunks[j].text.size(), Common::toPrintable(_text[i].chunks[j].text.encode()).c_str());
+
+ debugN(9, "\n");
+ }
+ debug(9, "** splitString: done");
+#endif
+
+ return s;
+}
+
+
+void MacTextCanvas::reallocSurface() {
+ // round to closest 10
+ //TODO: work out why this rounding doesn't correctly fill the entire width
+ //int requiredH = (_text.size() + (_text.size() * 10 + 9) / 10) * lineH
+
+ if (!_surface) {
+ _surface = new ManagedSurface(_maxWidth, _textMaxHeight, _wm->_pixelformat);
+
+ if (_textShadow)
+ _shadowSurface = new ManagedSurface(_maxWidth, _textMaxHeight, _wm->_pixelformat);
+
+ return;
+ }
+
+ if (_surface->w < _maxWidth || _surface->h < _textMaxHeight) {
+ // realloc surface and copy old content
+ ManagedSurface *n = new ManagedSurface(_maxWidth, _textMaxHeight, _wm->_pixelformat);
+ n->clear(_tbgcolor);
+ n->blitFrom(*_surface, Common::Point(0, 0));
+
+ delete _surface;
+ _surface = n;
+
+ // same as shadow surface
+ if (_textShadow) {
+ ManagedSurface *newShadowSurface = new ManagedSurface(_maxWidth, _textMaxHeight, _wm->_pixelformat);
+ newShadowSurface->clear(_tbgcolor);
+ newShadowSurface->blitFrom(*_shadowSurface, Common::Point(0, 0));
+
+ delete _shadowSurface;
+ _shadowSurface = newShadowSurface;
+ }
+ }
+}
+
+void MacTextCanvas::render(int from, int to, int shadow) {
+ int w = MIN(_maxWidth, _textMaxWidth);
+ ManagedSurface *surface = shadow ? _shadowSurface : _surface;
+
+ int myFrom = from, myTo = to + 1, delta = 1;
+
+ if (_wm->_language == Common::HE_ISR) {
+ myFrom = to;
+ myTo = from - 1;
+ delta = -1;
+ }
+
+ for (int i = myFrom; i != myTo; i += delta) {
+ if (!_text[i].picfname.empty()) {
+ const Surface *image = _macText->getImageSurface(_text[i].picfname);
+
+ int xOffset = (_text[i].width - _text[i].charwidth) / 2;
+ Common::Rect bbox(xOffset, _text[i].y, xOffset + _text[i].charwidth, _text[i].y + _text[i].height);
+
+ if (image) {
+ surface->blitFrom(image, Common::Rect(0, 0, image->w, image->h), bbox);
+
+ D(9, "MacTextCanvas::render: Image %d x %d bbox: %d, %d, %d, %d", image->w, image->h, bbox.left, bbox.top,
+ bbox.right, bbox.bottom);
+ }
+
+ continue;
+ }
+
+ if (_text[i].tableSurface) {
+ surface->blitFrom(*_text[i].tableSurface, Common::Point(0, _text[i].y));
+
+ D(9, "MacTextCanvas::render: Table %d x %d at: %d, %d", _text[i].tableSurface->w, _text[i].tableSurface->h, 0, _text[i].y);
+
+ continue;
+ }
+
+ int xOffset = getAlignOffset(i) + _text[i].indent + _text[i].firstLineIndent;
+ xOffset++;
+
+ int start = 0, end = _text[i].chunks.size();
+ if (_wm->_language == Common::HE_ISR) {
+ start = _text[i].chunks.size() - 1;
+ end = -1;
+ }
+
+ int maxAscentForRow = 0;
+ for (int j = start; j != end; j += delta) {
+ if (_text[i].chunks[j].font->getFontAscent() > maxAscentForRow)
+ maxAscentForRow = _text[i].chunks[j].font->getFontAscent();
+ }
+
+ // TODO: _canvas._textMaxWidth, when -1, was not rendering ANY text.
+ for (int j = start; j != end; j += delta) {
+ D(9, "MacTextCanvas::render: line %d[%d] h:%d at %d,%d (%s) fontid: %d fontsize: %d on %dx%d, fgcolor: %08x bgcolor: %08x",
+ i, j, _text[i].height, xOffset, _text[i].y, _text[i].chunks[j].text.encode().c_str(),
+ _text[i].chunks[j].fontId, _text[i].chunks[j].fontSize, _surface->w, _surface->h, _text[i].chunks[j].fgcolor, _tbgcolor);
+
+ if (_text[i].chunks[j].text.empty())
+ continue;
+
+ int yOffset = 0;
+ if (_text[i].chunks[j].font->getFontAscent() < maxAscentForRow) {
+ yOffset = maxAscentForRow - _text[i].chunks[j].font->getFontAscent();
+ }
+
+ if (_text[i].chunks[j].plainByteMode()) {
+ Common::String str = _text[i].chunks[j].getEncodedText();
+ _text[i].chunks[j].getFont()->drawString(surface, str, xOffset, _text[i].y + yOffset, w, shadow ? _wm->_colorBlack : _text[i].chunks[j].fgcolor, kTextAlignLeft, 0, true);
+ xOffset += _text[i].chunks[j].getFont()->getStringWidth(str);
+ } else {
+ if (_wm->_language == Common::HE_ISR)
+ _text[i].chunks[j].getFont()->drawString(surface, convertBiDiU32String(_text[i].chunks[j].text, Common::BIDI_PAR_RTL), xOffset, _text[i].y + yOffset, w, shadow ? _wm->_colorBlack : _text[i].chunks[j].fgcolor, kTextAlignLeft, 0, true);
+ else
+ _text[i].chunks[j].getFont()->drawString(surface, convertBiDiU32String(_text[i].chunks[j].text), xOffset, _text[i].y + yOffset, w, shadow ? _wm->_colorBlack : _text[i].chunks[j].fgcolor, kTextAlignLeft, 0, true);
+ xOffset += _text[i].chunks[j].getFont()->getStringWidth(_text[i].chunks[j].text);
+ }
+ }
+ }
+}
+
+void MacTextCanvas::render(int from, int to) {
+ if (_text.empty())
+ return;
+
+ reallocSurface();
+
+ from = MAX<int>(0, from);
+ to = MIN<int>(to, _text.size() - 1);
+
+ // Clear the screen
+ _surface->fillRect(Common::Rect(0, _text[from].y, _surface->w, _text[to].y + getLineHeight(to)), _tbgcolor);
+
+ // render the shadow surface;
+ if (_textShadow)
+ render(from, to, _textShadow);
+
+ render(from, to, 0);
+
+ for (uint i = 0; i < _text.size(); i++) {
+ debugN(9, "MacTextCanvas::render: %2d (firstInd: %d indent: %d) ", i, _text[i].firstLineIndent, _text[i].indent);
+
+ for (uint j = 0; j < _text[i].chunks.size(); j++)
+ debugN(9, "[%d (%d)] \"%s\" ", _text[i].chunks[j].fontId, _text[i].chunks[j].textSlant, _text[i].chunks[j].text.encode().c_str());
+
+ debug(9, "%s", "");
+ }
+}
+
+int getStringMaxWordWidth(MacFontRun &format, const Common::U32String &str) {
+ if (format.plainByteMode()) {
+ Common::StringTokenizer tok(Common::convertFromU32String(str, format.getEncoding()));
+ int maxW = 0;
+
+ while (!tok.empty()) {
+ int w = format.getFont()->getStringWidth(tok.nextToken());
+
+ maxW = MAX(maxW, w);
+ }
+
+ return maxW;
+ } else {
+ Common::U32StringTokenizer tok(str);
+ int maxW = 0;
+
+ while (!tok.empty()) {
+ int w = format.getFont()->getStringWidth(tok.nextToken());
+
+ maxW = MAX(maxW, w);
+ }
+
+ return maxW;
+ }
+}
+
+int MacTextCanvas::getLineWidth(int lineNum, bool enforce, int col) {
+ if ((uint)lineNum >= _text.size())
+ return 0;
+
+ MacTextLine *line = &_text[lineNum];
+
+ if (line->width != -1 && !enforce && col == -1)
+ return line->width;
+
+ if (!line->picfname.empty()) {
+ const Surface *image = _macText->getImageSurface(line->picfname);
+
+ if (image) {
+ float ratio = _maxWidth * line->picpercent / 100.0 / (float)image->w;
+ line->width = _maxWidth;
+ line->height = image->h * ratio;
+ line->charwidth = image->w * ratio;
+ } else {
+ line->width = _maxWidth;
+ line->height = 1;
+ line->charwidth = 1;
+ }
+
+ return line->width;
+ }
+
+ if (line->table) {
+ line->width = _maxWidth;
+ line->height = line->tableSurface->h;
+ line->charwidth = _maxWidth;
+
+ return line->width;
+ }
+
+ int width = line->indent + line->firstLineIndent;
+ int height = 0;
+ int charwidth = 0;
+ int minWidth = 0;
+ bool firstWord = true;
+
+ for (uint i = 0; i < line->chunks.size(); i++) {
+ if (enforce && _macFontMode)
+ line->chunks[i].font = nullptr;
+
+ if (col >= 0) {
+ if (col >= (int)line->chunks[i].text.size()) {
+ col -= line->chunks[i].text.size();
+ } else {
+ Common::U32String tmp = line->chunks[i].text.substr(0, col);
+
+ width += getStringWidth(line->chunks[i], tmp);
+
+ return width;
+ }
+ }
+
+ if (!line->chunks[i].text.empty()) {
+ int w = getStringWidth(line->chunks[i], line->chunks[i].text);
+ int mW = getStringMaxWordWidth(line->chunks[i], line->chunks[i].text);
+
+ if (firstWord) {
+ minWidth = mW + width; // Take indent into account
+ firstWord = false;
+ } else {
+ minWidth = MAX(minWidth, mW);
+ }
+ width += w;
+ charwidth += line->chunks[i].text.size();
+ }
+
+ height = MAX(height, line->chunks[i].getFont()->getFontHeight());
+ }
+
+
+ line->width = width;
+ line->minWidth = minWidth;
+ line->height = height;
+ line->charwidth = charwidth;
+
+ return width;
+}
+
+int MacTextCanvas::getLineCharWidth(int line, bool enforce) {
+ if ((uint)line >= _text.size())
+ return 0;
+
+ if (_text[line].charwidth != -1 && !enforce)
+ return _text[line].charwidth;
+
+ int width = 0;
+
+ for (uint i = 0; i < _text[line].chunks.size(); i++) {
+ if (!_text[line].chunks[i].text.empty())
+ width += _text[line].chunks[i].text.size();
+ }
+
+ _text[line].charwidth = width;
+
+ return width;
+}
+
+int MacTextCanvas::getLineHeight(int line) {
+ if ((uint)line >= _text.size())
+ return 0;
+
+ (void)getLineWidth(line); // This calculates height also
+
+ return _text[line].height;
+}
+
+void MacTextCanvas::recalcDims() {
+ if (_text.empty())
+ return;
+
+ int y = 0;
+ _textMaxWidth = 0;
+
+ for (uint i = 0; i < _text.size(); i++) {
+ _text[i].y = y;
+
+ // We must calculate width first, because it enforces
+ // the computation. Calling Height() will return cached value!
+ _textMaxWidth = MAX(_textMaxWidth, getLineWidth(i, true));
+ y += MAX(getLineHeight(i), _interLinear);
+ }
+
+ _textMaxHeight = y;
+}
+
+int MacTextCanvas::getAlignOffset(int row) {
+ int alignOffset = 0;
+ if (_textAlignment == kTextAlignRight)
+ alignOffset = MAX<int>(0, _maxWidth - getLineWidth(row) - 1);
+ else if (_textAlignment == kTextAlignCenter)
+ alignOffset = (_maxWidth / 2) - (getLineWidth(row) / 2);
+ return alignOffset;
+}
+
+// If adjacent chunks have same format, then skip the format definition
+// This happens when a long paragraph is split into several lines
+#define ADDFORMATTING() \
+ if (formatted) { \
+ formatting = Common::U32String(_text[i].chunks[chunk].toString()); \
+ if (formatting != prevformatting) { \
+ res += formatting; \
+ prevformatting = formatting; \
+ } \
+ }
+
+Common::U32String MacTextCanvas::getTextChunk(int startRow, int startCol, int endRow, int endCol, bool formatted, bool newlines) {
+ Common::U32String res("");
+
+ if (endRow == -1)
+ endRow = _text.size() - 1;
+
+ if (endCol == -1)
+ endCol = getLineCharWidth(endRow);
+ if (_text.empty()) {
+ return res;
+ }
+
+ startRow = CLIP(startRow, 0, (int)_text.size() - 1);
+ endRow = CLIP(endRow, 0, (int)_text.size() - 1);
+
+ Common::U32String formatting(""), prevformatting("");
+
+ for (int i = startRow; i <= endRow; i++) {
+ // We requested only part of one line
+ if (i == startRow && i == endRow) {
+ for (uint chunk = 0; chunk < _text[i].chunks.size(); chunk++) {
+ if (_text[i].chunks[chunk].text.empty()) {
+ // skip empty chunks, but keep them formatted,
+ // a text input box needs to keep the formatting even when all text is removed.
+ ADDFORMATTING();
+ continue;
+ }
+
+ if (startCol <= 0) {
+ ADDFORMATTING();
+
+ if (endCol >= (int)_text[i].chunks[chunk].text.size())
+ res += _text[i].chunks[chunk].text;
+ else
+ res += _text[i].chunks[chunk].text.substr(0, endCol);
+ } else if ((int)_text[i].chunks[chunk].text.size() > startCol) {
+ ADDFORMATTING();
+ res += _text[i].chunks[chunk].text.substr(startCol, endCol - startCol);
+ }
+
+ startCol -= _text[i].chunks[chunk].text.size();
+ endCol -= _text[i].chunks[chunk].text.size();
+
+ if (endCol <= 0)
+ break;
+ }
+ // We are at the top line and it is not completely requested
+ } else if (i == startRow && startCol != 0) {
+ for (uint chunk = 0; chunk < _text[i].chunks.size(); chunk++) {
+ if (_text[i].chunks[chunk].text.empty()) // skip empty chunks
+ continue;
+
+ if (startCol <= 0) {
+ ADDFORMATTING();
+ res += _text[i].chunks[chunk].text;
+ } else if ((int)_text[i].chunks[chunk].text.size() > startCol) {
+ ADDFORMATTING();
+ res += _text[i].chunks[chunk].text.substr(startCol);
+ }
+
+ startCol -= _text[i].chunks[chunk].text.size();
+ }
+ if (newlines && _text[i].paragraphEnd)
+ res += '\n';
+ // We are at the end row, and it could be not completely requested
+ } else if (i == endRow) {
+ for (uint chunk = 0; chunk < _text[i].chunks.size(); chunk++) {
+ if (_text[i].chunks[chunk].text.empty()) // skip empty chunks
+ continue;
+
+ ADDFORMATTING();
+
+ if (endCol >= (int)_text[i].chunks[chunk].text.size())
+ res += _text[i].chunks[chunk].text;
+ else
+ res += _text[i].chunks[chunk].text.substr(0, endCol);
+
+ endCol -= _text[i].chunks[chunk].text.size();
+
+ if (endCol <= 0)
+ break;
+ }
+ // We are in the middle of requested range, pass whole line
+ } else {
+ for (uint chunk = 0; chunk < _text[i].chunks.size(); chunk++) {
+ if (_text[i].chunks[chunk].text.empty()) // skip empty chunks
+ continue;
+
+ ADDFORMATTING();
+ res += _text[i].chunks[chunk].text;
+ }
+
+ if (newlines && _text[i].paragraphEnd)
+ res += '\n';
+ }
+ }
+
+ return res;
+}
+
+void MacTextCanvas::reshuffleParagraph(int *row, int *col, MacFontRun &defaultFormatting) {
+ // First, we looking for the paragraph start and end
+ int start = *row, end = *row;
+
+ while (start && !_text[start - 1].paragraphEnd)
+ start--;
+
+ while (end < (int)_text.size() - 1 && !_text[end].paragraphEnd) // stop at last line
+ end++;
+
+ // Get character pos within paragraph
+ int ppos = 0;
+
+ for (int i = start; i < *row; i++)
+ ppos += getLineCharWidth(i);
+
+ ppos += *col;
+
+ // Get whole paragraph
+ Common::U32String paragraph = getTextChunk(start, 0, end, getLineCharWidth(end, true), true, true);
+
+ // Remove it from the text
+ for (int i = start; i <= end; i++) {
+ _text.remove_at(start);
+ }
+
+ // And now read it
+ D(9, "start %d end %d", start, end);
+ splitString(paragraph, start, defaultFormatting);
+
+ // Find new pos within paragraph after reshuffling
+ *row = start;
+
+ warning("FIXME, bad design");
+ while (ppos > getLineCharWidth(*row, true)) {
+ ppos -= getLineCharWidth(*row, true);
+ (*row)++;
+ }
+ *col = ppos;
+}
+
+void MacTextCanvas::processTable(int line, int maxWidth) {
+ Common::Array<MacTextTableRow> *table = _text[line].table;
+ uint numCols = table->front().cells.size();
+ uint numRows = table->size();
+ Common::Array<int> maxW(numCols), maxL(numCols), colW(numCols), rowH(numRows);
+ Common::Array<bool> flex(numCols), wrap(numCols);
+
+ int width = maxWidth * 0.9;
+ int gutter = 10;
+
+ // Compute column widths, both minimal and maximal
+ for (auto &row : *table) {
+ int i = 0;
+ for (auto &cell : row.cells) {
+ int cW = 0, cL = 0;
+ for (uint l = 0; l < cell._text.size(); l++) {
+ (void)cell.getLineWidth(l); // calculate it
+
+ cW = MAX(cW, cell._text[l].width);
+ cL = MAX(cL, cell._text[l].minWidth);
+ }
+
+ maxW[i] = MAX(maxW[i], cW);
+ maxL[i] = MAX(maxL[i], cL);
+
+ i++;
+ }
+ }
+
+ for (uint i = 0; i < numCols; i++) {
+ warning("%d: %d - %d", i, maxL[i], maxW[i]);
+
+ wrap[i] = (maxW[i] != maxL[i]);
+ }
+
+ int left = width - (numCols - 1) * gutter;
+ int avg = left / numCols;
+ int nflex = 0;
+
+ // determine whether columns should be flexible and assign
+ // width of non-flexible cells
+ for (uint i = 0; i < numCols; i++) {
+ flex[i] = (maxW[i] > 2 * avg);
+ if (flex[i]) {
+ nflex++;
+ } else {
+ colW[i] = maxW[i];
+ left -= colW[i];
+ }
+ }
+
+ // if there is not enough space, make columns that could
+ // be word-wrapped flexible, too
+ if (left < nflex * avg) {
+ for (uint i = 0; i < numCols; i++) {
+ if (!flex[i] && wrap[i]) {
+ left += colW[i];
+ colW[i] = 0;
+ flex[i] = true;
+ nflex += 1;
+ }
+ }
+ }
+
+ // Calculate weights for flexible columns. The max width
+ // is capped at the page width to treat columns that have to
+ // be wrapped more or less equal
+ int tot = 0;
+ for (uint i = 0; i < numCols; i++) {
+ if (flex[i]) {
+ maxW[i] = MIN(maxW[i], width);
+ tot += maxW[i];
+ }
+ }
+
+ // Now assign the actual width for flexible columns. Make
+ // sure that it is at least as long as the longest word length
+ for (uint i = 0; i < numCols; i++) {
+ if (flex[i]) {
+ colW[i] = left * maxW[i] / tot;
+ colW[i] = MAX(colW[i], maxL[i]);
+ left -= colW[i];
+ }
+ }
+
+ for (uint i = 0; i < numCols; i++) {
+ warning("%d: %d", i, colW[i]);
+ }
+
+ int r = 0;
+ for (auto &row : *table) {
+ int c = 0;
+ rowH[r] = 0;
+ for (auto &cell : row.cells) {
+ cell._maxWidth = colW[c];
+
+ cell.recalcDims();
+ cell.reallocSurface();
+ cell._surface->clear(_tbgcolor);
+ cell.render(0, cell._text.size());
+
+ rowH[r] = MAX(rowH[r], cell._textMaxHeight);
+
+ c++;
+ }
+
+ r++;
+ }
+
+ int tW = 1, tH = 1;
+ for (uint i = 0; i < table->size(); i++)
+ tH += rowH[i] + gutter * 2 + 1;
+
+ for (uint i = 0; i < table->front().cells.size(); i++)
+ tW += colW[i] + gutter * 2 + 1;
+
+ ManagedSurface *surf = new ManagedSurface(tW, tH, _wm->_pixelformat);
+ _text[line].tableSurface = surf;
+ _text[line].height = tH;
+ _text[line].width = tW;
+ surf->clear(_tbgcolor);
+
+ surf->hLine(0, 0, tW, _tfgcolor);
+ surf->vLine(0, 0, tH, _tfgcolor);
+
+ int y = 1;
+ for (uint i = 0; i < table->size(); i++) {
+ y += gutter * 2 + rowH[i];
+ surf->hLine(0, y, tW, _tfgcolor);
+ y++;
+ }
+
+ int x = 1;
+ for (uint i = 0; i < table->front().cells.size(); i++) {
+ x += gutter * 2 + colW[i];
+ surf->vLine(x, 0, tH, _tfgcolor);
+ x++;
+ }
+
+ r = 0;
+ y = 1 + gutter;
+ for (auto &row : *table) {
+ int c = 0;
+ x = 1 + gutter;
+ for (auto &cell : row.cells) {
+ surf->blitFrom(*cell._surface, Common::Point(x, y));
+ x += gutter * 2 + 1 + colW[c];
+ c++;
+ }
+ y += gutter * 2 + 1 + rowH[r];
+ r++;
+ }
+}
+
+} // End of namespace Graphics
diff --git a/graphics/macgui/mactext-canvas.h b/graphics/macgui/mactext-canvas.h
new file mode 100644
index 00000000000..89dfdfd727e
--- /dev/null
+++ b/graphics/macgui/mactext-canvas.h
@@ -0,0 +1,215 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef GRAPHICS_MACGUI_MACTEXTCANVAS_H
+#define GRAPHICS_MACGUI_MACTEXTCANVAS_H
+
+#include "graphics/macgui/macwindowmanager.h"
+
+namespace Graphics {
+
+class MacText;
+
+struct MacFontRun {
+ Common::U32String text;
+
+ uint16 fontId;
+ byte textSlant;
+ uint16 fontSize;
+ uint16 palinfo1;
+ uint16 palinfo2;
+ uint16 palinfo3;
+ uint32 fgcolor;
+ // to determine whether the next word is part of this one
+ bool wordContinuation;
+ const Font *font;
+ MacWindowManager *wm;
+ Common::String link; // Substitute to return when hover or click
+
+ MacFontRun() {
+ wm = nullptr;
+ fontId = textSlant = fontSize = 0;
+ palinfo1 = palinfo2 = palinfo3 = 0;
+ fgcolor = 0;
+ font = nullptr;
+ wordContinuation = false;
+ }
+
+ MacFontRun(MacWindowManager *wm_) {
+ wm = wm_;
+ fontId = textSlant = fontSize = 0;
+ palinfo1 = palinfo2 = palinfo3 = 0;
+ fgcolor = 0;
+ font = nullptr;
+ wordContinuation = false;
+ }
+
+ MacFontRun(MacWindowManager *wm_, uint16 fontId_, byte textSlant_, uint16 fontSize_,
+ uint16 palinfo1_, uint16 palinfo2_, uint16 palinfo3_) {
+ setValues(wm_, fontId_, textSlant_, fontSize_, palinfo1_, palinfo2_, palinfo3_);
+ wordContinuation = false;
+ }
+
+ MacFontRun(MacWindowManager *wm_, const Font *font_, byte textSlant_, uint16 fontSize_,
+ uint16 palinfo1_, uint16 palinfo2_, uint16 palinfo3_) {
+ setValues(wm_, 0, textSlant_, fontSize_, palinfo1_, palinfo2_, palinfo3_);
+ font = font_;
+ wordContinuation = false;
+ }
+
+ void setValues(MacWindowManager *wm_, uint16 fontId_, byte textSlant_, uint16 fontSize_,
+ uint16 palinfo1_, uint16 palinfo2_, uint16 palinfo3_) {
+ wm = wm_;
+ fontId = fontId_;
+ textSlant = textSlant_;
+ fontSize = fontSize_;
+ palinfo1 = palinfo1_;
+ palinfo2 = palinfo2_;
+ palinfo3 = palinfo3_;
+ fgcolor = wm_->findBestColor(palinfo1_ & 0xff, palinfo2_ & 0xff, palinfo3_ & 0xff);
+ font = nullptr;
+ }
+
+ const Font *getFont();
+
+ const Common::String toString();
+ bool equals(MacFontRun &to);
+
+ Common::CodePage getEncoding();
+ bool plainByteMode();
+ Common::String getEncodedText();
+
+ bool equals(const MacFontRun *x, const MacFontRun *y) {
+ return (x->fontId == y->fontId &&
+ x->textSlant == y->textSlant &&
+ x->fontSize == y->fontSize &&
+ x->palinfo1 == y->palinfo1 &&
+ x->palinfo2 == y->palinfo2 &&
+ x->palinfo3 == y->palinfo3 &&
+ x->fgcolor == y->fgcolor);
+ }
+
+};
+
+struct MacTextLine;
+
+class MacTextCanvas {
+public:
+ Common::Array<MacTextLine> _text;
+ ManagedSurface *_surface = nullptr, *_shadowSurface = nullptr;
+ int _maxWidth = 0;
+ int _textMaxWidth = 0;
+ int _textMaxHeight = 0;
+ TextAlign _textAlignment = kTextAlignLeft;
+ int _interLinear = 0;
+ int _textShadow = 0;
+ MacWindowManager *_wm = nullptr;
+ uint32 _tfgcolor = 0;
+ uint32 _tbgcolor = 0;
+ bool _macFontMode = true;
+ MacText *_macText;
+
+public:
+ ~MacTextCanvas() {
+ delete _surface;
+ delete _shadowSurface;
+ }
+
+ void recalcDims();
+ void reallocSurface();
+ void render(int from, int to);
+ void render(int from, int to, int shadow);
+ int getAlignOffset(int row);
+
+ /**
+ * Returns line width in pixels. This takes into account chunks.
+ * The result is cached for faster subsequent calls.
+ *
+ * @param line Line number
+ * @param enforce Flag for indicating skipping the cache and computing the width,
+ * must be called when text gets changed
+ * @param col Compute line width up to specified column, including this column
+ * @return line width in pixels, or 0 for non-existent lines
+ */
+ int getLineWidth(int line, bool enforce = false, int col = -1);
+ int getLineHeight(int line);
+ int getLineCharWidth(int line, bool enforce = false);
+
+ void splitString(const Common::U32String &str, int curLine, MacFontRun &defaultFormatting);
+ const Common::U32String::value_type *splitString(const Common::U32String::value_type *s, int curLine, MacFontRun &defaultFormatting);
+
+ void chopChunk(const Common::U32String &str, int *curLinePtr, int indent, int maxWidth);
+ Common::U32String getTextChunk(int startRow, int startCol, int endRow, int endCol, bool formatted = false, bool newlines = true);
+
+ /**
+ * Rewraps paragraph containing given text row.
+ * When text is modified, we redo whole thing again without touching
+ * other paragraphs. Also, cursor position is returned in the arguments
+ */
+ void reshuffleParagraph(int *row, int *col, MacFontRun &defaultFormatting);
+
+ void processTable(int line, int maxWidth);
+};
+
+struct MacTextTableRow {
+ Common::Array<MacTextCanvas> cells;
+ int heght = -1;
+};
+
+struct MacTextLine {
+ int width = -1;
+ int height = -1;
+ int minWidth = -1;
+ int y = 0;
+ int charwidth = -1;
+ bool paragraphEnd = false;
+ int indent = 0; // in units
+ int firstLineIndent = 0; // in pixels
+ Common::String picfname;
+ Common::U32String picalt, pictitle;
+ uint16 picpercent = 50;
+ Common::Array<MacTextTableRow> *table = nullptr;
+ ManagedSurface *tableSurface = nullptr;
+
+ Common::Array<MacFontRun> chunks;
+
+ MacFontRun &firstChunk() { return chunks[0]; }
+ MacFontRun &lastChunk() { return chunks[chunks.size() - 1]; }
+
+ /**
+ * Search for a chunk at given char column.
+ *
+ * @param col Requested column, gets modified with in-chunk column
+ * @returns Chunk number
+ *
+ * @note If requested column is too big, returns last character in the line
+ */
+ uint getChunkNum(int *col);
+
+ ~MacTextLine() {
+ delete table;
+ delete tableSurface;
+ }
+};
+
+} // End of namespace Graphics
+
+#endif
diff --git a/graphics/macgui/mactext.cpp b/graphics/macgui/mactext.cpp
index 616db8bc6d3..b04a1ab15db 100644
--- a/graphics/macgui/mactext.cpp
+++ b/graphics/macgui/mactext.cpp
@@ -21,17 +21,9 @@
#include "common/file.h"
#include "common/timer.h"
-#include "common/tokenizer.h"
-#include "common/unicode-bidi.h"
#include "common/compression/unzip.h"
-#include "graphics/font.h"
#include "graphics/macgui/mactext.h"
-#include "graphics/macgui/macwindowmanager.h"
-#include "graphics/macgui/macfontmanager.h"
-#include "graphics/macgui/macmenu.h"
-#include "graphics/macgui/macwidget.h"
-#include "graphics/macgui/macwindow.h"
#ifdef USE_PNG
#include "image/png.h"
@@ -302,33 +294,6 @@ int getStringWidth(MacFontRun &format, const Common::U32String &str) {
return format.getFont()->getStringWidth(str);
}
-int getStringMaxWordWidth(MacFontRun &format, const Common::U32String &str) {
- if (format.plainByteMode()) {
- Common::StringTokenizer tok(Common::convertFromU32String(str, format.getEncoding()));
- int maxW = 0;
-
- while (!tok.empty()) {
- int w = format.getFont()->getStringWidth(tok.nextToken());
-
- maxW = MAX(maxW, w);
- }
-
- return maxW;
- } else {
- Common::U32StringTokenizer tok(str);
- int maxW = 0;
-
- while (!tok.empty()) {
- int w = format.getFont()->getStringWidth(tok.nextToken());
-
- maxW = MAX(maxW, w);
- }
-
- return maxW;
- }
-}
-
-
void MacText::setMaxWidth(int maxWidth) {
if (maxWidth == _canvas._maxWidth)
return;
@@ -587,491 +552,6 @@ void MacText::setDefaultFormatting(uint16 fontId, byte textSlant, uint16 fontSiz
_defaultFormatting.font = _wm->_fontMan->getFont(macFont);
}
-// Adds the given string to the end of the last line/chunk
-// while observing the _canvas._maxWidth and keeping this chunk's
-// formatting
-void MacTextCanvas::chopChunk(const Common::U32String &str, int *curLinePtr, int indent, int maxWidth) {
- int curLine = *curLinePtr;
- int curChunk;
- MacFontRun *chunk;
-
- curChunk = _text[curLine].chunks.size() - 1;
- chunk = &_text[curLine].chunks[curChunk];
-
- // Check if there is nothing to add, then remove the last chunk
- // This happens when the previous run is finished only with
- // empty formatting, or when we were adding text for the first time
- if (chunk->text.empty() && str.empty()) {
- D(9, "** chopChunk, replaced formatting, line %d", curLine);
-
- _text[curLine].chunks.pop_back();
-
- return;
- }
-
- if (maxWidth == -1) {
- chunk->text += str;
-
- return;
- }
-
- Common::Array<Common::U32String> text;
-
- int w = getLineWidth(curLine, true);
- D(9, "** chopChunk before wrap \"%s\"", Common::toPrintable(str.encode()).c_str());
-
- chunk->getFont()->wordWrapText(str, maxWidth, text, w);
-
- if (text.size() == 0) {
- warning("chopChunk: too narrow width, >%d", maxWidth);
- chunk->text += str;
- getLineCharWidth(curLine, true);
-
- return;
- }
-
- for (int i = 0; i < (int)text.size(); i++) {
- D(9, "** chopChunk result %d \"%s\"", i, toPrintable(text[i].encode()).c_str());
- }
- chunk->text += text[0];
-
- // Recalc dims
- getLineWidth(curLine, true);
-
- D(9, "** chopChunk, subchunk: \"%s\" (%d lines, maxW: %d)", toPrintable(text[0].encode()).c_str(), text.size(), maxWidth);
-
- // We do not overlap, so we're done
- if (text.size() == 1)
- return;
-
- // Now add rest of the chunks
- MacFontRun newchunk = _text[curLine].chunks[curChunk];
-
- for (uint i = 1; i < text.size(); i++) {
- newchunk.text = text[i];
-
- curLine++;
- _text.insert_at(curLine, MacTextLine());
- _text[curLine].chunks.push_back(newchunk);
- _text[curLine].indent = indent;
- _text[curLine].firstLineIndent = 0;
-
- D(9, "** chopChunk, added line (firstIndent: %d): \"%s\"", _text[curLine].firstLineIndent, toPrintable(text[i].encode()).c_str());
- }
-
- *curLinePtr = curLine;
-}
-
-void MacTextCanvas::splitString(const Common::U32String &str, int curLine, MacFontRun &defaultFormatting) {
- D(9, "** splitString(\"%s\", %d)", toPrintable(str.encode()).c_str(), curLine);
-
- if (str.empty()) {
- D(9, "** splitString, empty line");
- return;
- }
-
- (void)splitString(str.c_str(), curLine, defaultFormatting);
-}
-
-const Common::U32String::value_type *MacTextCanvas::splitString(const Common::U32String::value_type *s, int curLine, MacFontRun &defaultFormatting) {
- if (_text.empty()) {
- _text.resize(1);
- _text[0].chunks.push_back(defaultFormatting);
- D(9, "** splitString, added default formatting");
- } else {
- D(9, "** splitString, continuing, %d lines", _text.size());
- }
-
- Common::U32String tmp;
-
- if (curLine == -1 || curLine >= (int)_text.size())
- curLine = _text.size() - 1;
-
- int curChunk = _text[curLine].chunks.size() - 1;
- MacFontRun chunk = _text[curLine].chunks[curChunk];
- int indentSize = 0;
- int firstLineIndent = 0;
- bool inTable = false;
-
- while (*s) {
- firstLineIndent = 0;
-
- tmp.clear();
-
- MacTextLine *curTextLine = &_text[curLine];
-
- while (*s) {
- bool endOfLine = false;
-
- // Scan till next font change or end of line
- while (*s && *s != '\001') {
- if (*s == '\r') {
- s++;
- if (*s == '\n') // Skip whole '\r\n'
- s++;
-
- endOfLine = true;
-
- break;
- }
-
- // deal with single \n
- if (*s == '\n') {
- s++;
-
- endOfLine = true;
- break;
- }
-
- tmp += *s;
-
- s++;
- }
-
- if (*s == '\001') // If it was \001, skip it
- s++;
-
- if (*s == '\001') { // \001\001 -> \001
- tmp += *s++;
-
- if (*s) // Check we reached end of line
- continue;
- }
-
- D(9, "** splitString, chunk: \"%s\"", Common::toPrintable(tmp.encode()).c_str());
-
- // Okay, now we are either at the end of the line, or in the next
- // chunk definition. That means, that we have to store the previous chunk
- chopChunk(tmp, &curLine, indentSize, _maxWidth > 0 ? _maxWidth - indentSize : _maxWidth);
-
- curTextLine = &_text[curLine];
-
- firstLineIndent = curTextLine->firstLineIndent;
-
- tmp.clear();
-
- // If it is end of the line, we're done
- if (!*s) {
- D(9, "** splitString, end of line");
-
- break;
- }
-
- // get format (sync with stripFormat() )
- if (*s == '\016') { // human-readable format
- s++;
-
- // First two digits is slant, third digit is Header number
- switch (*s) {
- case '+': { // \016+XXYZ -- opening textSlant, H<Y>, indent<+Z>
- uint16 textSlant, headSize, indent;
- s++;
-
- s = readHex(&textSlant, s, 2);
-
- chunk.textSlant |= textSlant; // Setting the specified bit
-
- s = readHex(&headSize, s, 1);
- if (headSize >= 1 && headSize <= 6) { // set
- const float sizes[] = { 1, 2.0f, 1.41f, 1.155f, 1.0f, .894f, .816f };
- chunk.fontSize = defaultFormatting.fontSize * sizes[headSize];
- }
-
- s = readHex(&indent, s, 1);
-
- if (s)
- indentSize += indent * chunk.fontSize * 2;
-
- D(9, "** splitString+: fontId: %d, textSlant: %d, fontSize: %d, indent: %d",
- chunk.fontId, chunk.textSlant, chunk.fontSize,
- indent);
-
- break;
- }
- case '-': { // \016-XXYZ -- closing textSlant, H<Y>, indent<+Z>
- uint16 textSlant, headSize, indent;
- s++;
-
- s = readHex(&textSlant, s, 2);
-
- chunk.textSlant &= ~textSlant; // Clearing the specified bit
-
- s = readHex(&headSize, s, 1);
- if (headSize == 0xf) // reset
- chunk.fontSize = defaultFormatting.fontSize;
-
- s = readHex(&indent, s, 1);
-
- if (s)
- indentSize -= indent * chunk.fontSize * 2;
-
- D(9, "** splitString-: fontId: %d, textSlant: %d, fontSize: %d, indent: %d",
- chunk.fontId, chunk.textSlant, chunk.fontSize,
- indent);
- break;
- }
-
- case '[': { // \016[RRGGBB -- setting color
- uint16 palinfo1, palinfo2, palinfo3;
- s++;
-
- s = readHex(&palinfo1, s, 4);
- s = readHex(&palinfo2, s, 4);
- s = readHex(&palinfo3, s, 4);
-
- chunk.palinfo1 = palinfo1;
- chunk.palinfo2 = palinfo2;
- chunk.palinfo3 = palinfo3;
- chunk.fgcolor = _wm->findBestColor(palinfo1 & 0xff, palinfo2 & 0xff, palinfo3 & 0xff);
-
- D(9, "** splitString[: %08x", chunk.fgcolor);
- break;
- }
-
- case ']': { // \016] -- setting default color
- s++;
-
- chunk.palinfo1 = defaultFormatting.palinfo1;
- chunk.palinfo2 = defaultFormatting.palinfo2;
- chunk.palinfo3 = defaultFormatting.palinfo3;
- chunk.fgcolor = defaultFormatting.fgcolor;
-
- D(9, "** splitString]: %08x", chunk.fgcolor);
- break;
- }
-
- case '*': { // \016*XXsssssss -- negative indent, XX size, sssss is the string
- s++;
-
- uint16 len;
-
- s = readHex(&len, s, 2);
-
- Common::U32String bullet = Common::U32String(s, len);
-
- s += len;
-
- firstLineIndent = -chunk.getFont()->getStringWidth(bullet);
-
- D(9, "** splitString*: %02x '%s' (%d)", len, bullet.encode().c_str(), firstLineIndent);
- break;
- }
-
- case 'i': { // \016iXXNNnnnnAAaaaaTTttt -- image, XX% width,
- // NN, nnnn -- filename len and text
- // AA, aaaa -- alt len and text
- // TT, tttt -- text (tooltip) len and text
- s++;
-
- uint16 len;
-
- s = readHex(&_text[curLine].picpercent, s, 2);
- s = readHex(&len, s, 2);
- _text[curLine].picfname = Common::U32String(s, len).encode();
- s += len;
-
- s = readHex(&len, s, 2);
- _text[curLine].picalt = Common::U32String(s, len);
- s += len;
-
- s = readHex(&len, s, 2);
- _text[curLine].pictitle = Common::U32String(s, len);
- s += len;
-
- D(9, "** splitString[i]: %d%% fname: '%s' alt: '%s' title: '%s'",
- _text[curLine].picpercent,
- _text[curLine].picfname.c_str(), _text[curLine].picalt.encode().c_str(),
- _text[curLine].pictitle.encode().c_str());
- break;
- }
-
- case 't': { // \016tXXXX -- switch to the requested font id
- s++;
-
- uint16 fontId;
-
- s = readHex(&fontId, s, 4);
-
- chunk.fontId = fontId == 0xffff ? defaultFormatting.fontId : fontId;
-
- D(9, "** splitString[t]: fontId: %d", fontId);
- break;
- }
-
- case 'l': { // \016lLLllll -- link len and text
- s++;
-
- uint16 len;
-
- s = readHex(&len, s, 2);
- chunk.link = Common::U32String(s, len);
- s += len;
-
- D(9, "** splitString[l]: link: %s", chunk.link.c_str());
- break;
- }
-
- case 'T': { // \016T -- table
- s++;
-
- char cmd = *s++;
-
- if (cmd == 'h') { // Header, beginning of the table
- curTextLine->table = new Common::Array<MacTextTableRow>();
- inTable = true;
-
- D(9, "** splitString[table header]");
- } else if (cmd == 'b') { // Body start
- D(9, "** splitString[body start]");
- } else if (cmd == 'B') { // Body end
- inTable = false;
-
- D(9, "** splitString[body end]");
- processTable(curLine, _maxWidth);
-
- continue;
- } else if (cmd == 'r') { // Row
- curTextLine->table->push_back(MacTextTableRow());
- D(9, "** splitString[row]");
- } else if (cmd == 'c') { // Cell start
- uint16 align;
- s = readHex(&align, s, 2);
-
- curTextLine->table->back().cells.push_back(MacTextCanvas());
-
- MacTextCanvas *cellCanvas = &curTextLine->table->back().cells.back();
- cellCanvas->_textAlignment = (TextAlign)align;
- cellCanvas->_wm = _wm;
- cellCanvas->_macText = _macText;
- cellCanvas->_maxWidth = -1;
- cellCanvas->_macFontMode = _macFontMode;
- cellCanvas->_tfgcolor = _tfgcolor;
- cellCanvas->_tbgcolor = _tbgcolor;
-
- D(9, "** splitString[cell start]: align: %d", align);
-
- D(9, "** splitString[RECURSION start]");
-
- s = cellCanvas->splitString(s, curLine, defaultFormatting);
-
- D(9, "** splitString[RECURSION end]");
- } else if (cmd == 'C') { // Cell end
- D(9, "** splitString[cell end]");
-
- return s;
- } else {
- error("MacText: Unknown table subcommand (%c)", cmd);
- }
- break;
- }
-
- default: {
- uint16 fontId, textSlant, fontSize, palinfo1, palinfo2, palinfo3;
-
- s = readHex(&fontId, s, 4);
- s = readHex(&textSlant, s, 2);
- s = readHex(&fontSize, s, 4);
- s = readHex(&palinfo1, s, 4);
- s = readHex(&palinfo2, s, 4);
- s = readHex(&palinfo3, s, 4);
-
- chunk.setValues(_wm, fontId, textSlant, fontSize, palinfo1, palinfo2, palinfo3);
-
- D(9, "** splitString: fontId: %d, textSlant: %d, fontSize: %d, fg: %04x",
- fontId, textSlant, fontSize, chunk.fgcolor);
-
- // So far, we enforce single font here, though in the future, font size could be altered
- if (!_macFontMode)
- chunk.font = defaultFormatting.font;
- }
- }
- }
-
-
- curTextLine->indent = indentSize;
- curTextLine->firstLineIndent = firstLineIndent;
-
- // Push new formatting
- curTextLine->chunks.push_back(chunk);
-
- // If we reached end of paragraph, go to outer loop
- if (endOfLine)
- break;
- }
-
- // We avoid adding new lines while in table. Recursive cell rendering
- // has this flag as false (obviously)
- if (inTable)
- continue;
-
- // Add new line
- D(9, "** splitString: new line");
-
- curTextLine->paragraphEnd = true;
- // if the chunks is empty, which means the line will not be rendered properly
- // so we add a empty string here
- if (curTextLine->chunks.empty()) {
- curTextLine->chunks.push_back(defaultFormatting);
- }
-
- curLine++;
- _text.insert_at(curLine, MacTextLine());
- _text[curLine].chunks.push_back(chunk);
-
- curTextLine = &_text[curLine];
- }
-
-#if DEBUG
- for (uint i = 0; i < _text.size(); i++) {
- debugN(9, "** splitString: %2d ", i);
-
- for (uint j = 0; j < _text[i].chunks.size(); j++)
- debugN(9, "[%d] \"%s\"", _text[i].chunks[j].text.size(), Common::toPrintable(_text[i].chunks[j].text.encode()).c_str());
-
- debugN(9, "\n");
- }
- debug(9, "** splitString: done");
-#endif
-
- return s;
-}
-
-
-void MacTextCanvas::reallocSurface() {
- // round to closest 10
- //TODO: work out why this rounding doesn't correctly fill the entire width
- //int requiredH = (_text.size() + (_text.size() * 10 + 9) / 10) * lineH
-
- if (!_surface) {
- _surface = new ManagedSurface(_maxWidth, _textMaxHeight, _wm->_pixelformat);
-
- if (_textShadow)
- _shadowSurface = new ManagedSurface(_maxWidth, _textMaxHeight, _wm->_pixelformat);
-
- return;
- }
-
- if (_surface->w < _maxWidth || _surface->h < _textMaxHeight) {
- // realloc surface and copy old content
- ManagedSurface *n = new ManagedSurface(_maxWidth, _textMaxHeight, _wm->_pixelformat);
- n->clear(_tbgcolor);
- n->blitFrom(*_surface, Common::Point(0, 0));
-
- delete _surface;
- _surface = n;
-
- // same as shadow surface
- if (_textShadow) {
- ManagedSurface *newShadowSurface = new ManagedSurface(_maxWidth, _textMaxHeight, _wm->_pixelformat);
- newShadowSurface->clear(_tbgcolor);
- newShadowSurface->blitFrom(*_shadowSurface, Common::Point(0, 0));
-
- delete _shadowSurface;
- _shadowSurface = newShadowSurface;
- }
- }
-}
-
void MacText::render() {
if (_fullRefresh) {
_canvas._surface->clear(_canvas._tbgcolor);
@@ -1093,216 +573,6 @@ void MacText::render() {
}
}
-void MacTextCanvas::render(int from, int to, int shadow) {
- int w = MIN(_maxWidth, _textMaxWidth);
- ManagedSurface *surface = shadow ? _shadowSurface : _surface;
-
- int myFrom = from, myTo = to + 1, delta = 1;
-
- if (_wm->_language == Common::HE_ISR) {
- myFrom = to;
- myTo = from - 1;
- delta = -1;
- }
-
- for (int i = myFrom; i != myTo; i += delta) {
- if (!_text[i].picfname.empty()) {
- const Surface *image = _macText->getImageSurface(_text[i].picfname);
-
- int xOffset = (_text[i].width - _text[i].charwidth) / 2;
- Common::Rect bbox(xOffset, _text[i].y, xOffset + _text[i].charwidth, _text[i].y + _text[i].height);
-
- if (image) {
- surface->blitFrom(image, Common::Rect(0, 0, image->w, image->h), bbox);
-
- D(9, "MacTextCanvas::render: Image %d x %d bbox: %d, %d, %d, %d", image->w, image->h, bbox.left, bbox.top,
- bbox.right, bbox.bottom);
- }
-
- continue;
- }
-
- if (_text[i].tableSurface) {
- surface->blitFrom(*_text[i].tableSurface, Common::Point(0, _text[i].y));
-
- D(9, "MacTextCanvas::render: Table %d x %d at: %d, %d", _text[i].tableSurface->w, _text[i].tableSurface->h, 0, _text[i].y);
-
- continue;
- }
-
- int xOffset = getAlignOffset(i) + _text[i].indent + _text[i].firstLineIndent;
- xOffset++;
-
- int start = 0, end = _text[i].chunks.size();
- if (_wm->_language == Common::HE_ISR) {
- start = _text[i].chunks.size() - 1;
- end = -1;
- }
-
- int maxAscentForRow = 0;
- for (int j = start; j != end; j += delta) {
- if (_text[i].chunks[j].font->getFontAscent() > maxAscentForRow)
- maxAscentForRow = _text[i].chunks[j].font->getFontAscent();
- }
-
- // TODO: _canvas._textMaxWidth, when -1, was not rendering ANY text.
- for (int j = start; j != end; j += delta) {
- D(9, "MacTextCanvas::render: line %d[%d] h:%d at %d,%d (%s) fontid: %d fontsize: %d on %dx%d, fgcolor: %08x bgcolor: %08x",
- i, j, _text[i].height, xOffset, _text[i].y, _text[i].chunks[j].text.encode().c_str(),
- _text[i].chunks[j].fontId, _text[i].chunks[j].fontSize, _surface->w, _surface->h, _text[i].chunks[j].fgcolor, _tbgcolor);
-
- if (_text[i].chunks[j].text.empty())
- continue;
-
- int yOffset = 0;
- if (_text[i].chunks[j].font->getFontAscent() < maxAscentForRow) {
- yOffset = maxAscentForRow - _text[i].chunks[j].font->getFontAscent();
- }
-
- if (_text[i].chunks[j].plainByteMode()) {
- Common::String str = _text[i].chunks[j].getEncodedText();
- _text[i].chunks[j].getFont()->drawString(surface, str, xOffset, _text[i].y + yOffset, w, shadow ? _wm->_colorBlack : _text[i].chunks[j].fgcolor, kTextAlignLeft, 0, true);
- xOffset += _text[i].chunks[j].getFont()->getStringWidth(str);
- } else {
- if (_wm->_language == Common::HE_ISR)
- _text[i].chunks[j].getFont()->drawString(surface, convertBiDiU32String(_text[i].chunks[j].text, Common::BIDI_PAR_RTL), xOffset, _text[i].y + yOffset, w, shadow ? _wm->_colorBlack : _text[i].chunks[j].fgcolor, kTextAlignLeft, 0, true);
- else
- _text[i].chunks[j].getFont()->drawString(surface, convertBiDiU32String(_text[i].chunks[j].text), xOffset, _text[i].y + yOffset, w, shadow ? _wm->_colorBlack : _text[i].chunks[j].fgcolor, kTextAlignLeft, 0, true);
- xOffset += _text[i].chunks[j].getFont()->getStringWidth(_text[i].chunks[j].text);
- }
- }
- }
-}
-
-void MacTextCanvas::render(int from, int to) {
- if (_text.empty())
- return;
-
- reallocSurface();
-
- from = MAX<int>(0, from);
- to = MIN<int>(to, _text.size() - 1);
-
- // Clear the screen
- _surface->fillRect(Common::Rect(0, _text[from].y, _surface->w, _text[to].y + getLineHeight(to)), _tbgcolor);
-
- // render the shadow surface;
- if (_textShadow)
- render(from, to, _textShadow);
-
- render(from, to, 0);
-
- for (uint i = 0; i < _text.size(); i++) {
- debugN(9, "MacTextCanvas::render: %2d (firstInd: %d indent: %d) ", i, _text[i].firstLineIndent, _text[i].indent);
-
- for (uint j = 0; j < _text[i].chunks.size(); j++)
- debugN(9, "[%d (%d)] \"%s\" ", _text[i].chunks[j].fontId, _text[i].chunks[j].textSlant, _text[i].chunks[j].text.encode().c_str());
-
- debug(9, "%s", "");
- }
-}
-
-int MacTextCanvas::getLineWidth(int lineNum, bool enforce, int col) {
- if ((uint)lineNum >= _text.size())
- return 0;
-
- MacTextLine *line = &_text[lineNum];
-
- if (line->width != -1 && !enforce && col == -1)
- return line->width;
-
- if (!line->picfname.empty()) {
- const Surface *image = _macText->getImageSurface(line->picfname);
-
- if (image) {
- float ratio = _maxWidth * line->picpercent / 100.0 / (float)image->w;
- line->width = _maxWidth;
- line->height = image->h * ratio;
- line->charwidth = image->w * ratio;
- } else {
- line->width = _maxWidth;
- line->height = 1;
- line->charwidth = 1;
- }
-
- return line->width;
- }
-
- if (line->table) {
- line->width = _maxWidth;
- line->height = line->tableSurface->h;
- line->charwidth = _maxWidth;
-
- return line->width;
- }
-
- int width = line->indent + line->firstLineIndent;
- int height = 0;
- int charwidth = 0;
- int minWidth = 0;
- bool firstWord = true;
-
- for (uint i = 0; i < line->chunks.size(); i++) {
- if (enforce && _macFontMode)
- line->chunks[i].font = nullptr;
-
- if (col >= 0) {
- if (col >= (int)line->chunks[i].text.size()) {
- col -= line->chunks[i].text.size();
- } else {
- Common::U32String tmp = line->chunks[i].text.substr(0, col);
-
- width += getStringWidth(line->chunks[i], tmp);
-
- return width;
- }
- }
-
- if (!line->chunks[i].text.empty()) {
- int w = getStringWidth(line->chunks[i], line->chunks[i].text);
- int mW = getStringMaxWordWidth(line->chunks[i], line->chunks[i].text);
-
- if (firstWord) {
- minWidth = mW + width; // Take indent into account
- firstWord = false;
- } else {
- minWidth = MAX(minWidth, mW);
- }
- width += w;
- charwidth += line->chunks[i].text.size();
- }
-
- height = MAX(height, line->chunks[i].getFont()->getFontHeight());
- }
-
-
- line->width = width;
- line->minWidth = minWidth;
- line->height = height;
- line->charwidth = charwidth;
-
- return width;
-}
-
-int MacTextCanvas::getLineCharWidth(int line, bool enforce) {
- if ((uint)line >= _text.size())
- return 0;
-
- if (_text[line].charwidth != -1 && !enforce)
- return _text[line].charwidth;
-
- int width = 0;
-
- for (uint i = 0; i < _text[line].chunks.size(); i++) {
- if (!_text[line].chunks[i].text.empty())
- width += _text[line].chunks[i].text.size();
- }
-
- _text[line].charwidth = width;
-
- return width;
-}
-
int MacText::getLastLineWidth() {
if (_canvas._text.size() == 0)
return 0;
@@ -1314,15 +584,6 @@ int MacText::getLineHeight(int line) {
return _canvas.getLineHeight(line);
}
-int MacTextCanvas::getLineHeight(int line) {
- if ((uint)line >= _text.size())
- return 0;
-
- (void)getLineWidth(line); // This calculates height also
-
- return _text[line].height;
-}
-
void MacText::setInterLinear(int interLinear) {
_canvas._interLinear = interLinear;
@@ -1352,25 +613,6 @@ void MacText::recalcDims() {
}
}
-void MacTextCanvas::recalcDims() {
- if (_text.empty())
- return;
-
- int y = 0;
- _textMaxWidth = 0;
-
- for (uint i = 0; i < _text.size(); i++) {
- _text[i].y = y;
-
- // We must calculate width first, because it enforces
- // the computation. Calling Height() will return cached value!
- _textMaxWidth = MAX(_textMaxWidth, getLineWidth(i, true));
- y += MAX(getLineHeight(i), _interLinear);
- }
-
- _textMaxHeight = y;
-}
-
void MacText::setAlignOffset(TextAlign align) {
if (_canvas._textAlignment == align)
return;
@@ -2365,15 +1607,6 @@ Common::U32String MacText::getMouseLink(int x, int y) {
return Common::U32String();
}
-int MacTextCanvas::getAlignOffset(int row) {
- int alignOffset = 0;
- if (_textAlignment == kTextAlignRight)
- alignOffset = MAX<int>(0, _maxWidth - getLineWidth(row) - 1);
- else if (_textAlignment == kTextAlignCenter)
- alignOffset = (_maxWidth / 2) - (getLineWidth(row) / 2);
- return alignOffset;
-}
-
void MacText::getRowCol(int x, int y, int *sx, int *sy, int *row, int *col, int *chunk_) {
int nsx = 0, nsy = 0, nrow = 0, ncol = 0;
@@ -2456,117 +1689,6 @@ Common::U32String MacText::getTextChunk(int startRow, int startCol, int endRow,
return _canvas.getTextChunk(startRow, startCol, endRow, endCol, formatted, newlines);
}
-// If adjacent chunks have same format, then skip the format definition
-// This happens when a long paragraph is split into several lines
-#define ADDFORMATTING() \
- if (formatted) { \
- formatting = Common::U32String(_text[i].chunks[chunk].toString()); \
- if (formatting != prevformatting) { \
- res += formatting; \
- prevformatting = formatting; \
- } \
- }
-
-Common::U32String MacTextCanvas::getTextChunk(int startRow, int startCol, int endRow, int endCol, bool formatted, bool newlines) {
- Common::U32String res("");
-
- if (endRow == -1)
- endRow = _text.size() - 1;
-
- if (endCol == -1)
- endCol = getLineCharWidth(endRow);
- if (_text.empty()) {
- return res;
- }
-
- startRow = CLIP(startRow, 0, (int)_text.size() - 1);
- endRow = CLIP(endRow, 0, (int)_text.size() - 1);
-
- Common::U32String formatting(""), prevformatting("");
-
- for (int i = startRow; i <= endRow; i++) {
- // We requested only part of one line
- if (i == startRow && i == endRow) {
- for (uint chunk = 0; chunk < _text[i].chunks.size(); chunk++) {
- if (_text[i].chunks[chunk].text.empty()) {
- // skip empty chunks, but keep them formatted,
- // a text input box needs to keep the formatting even when all text is removed.
- ADDFORMATTING();
- continue;
- }
-
- if (startCol <= 0) {
- ADDFORMATTING();
-
- if (endCol >= (int)_text[i].chunks[chunk].text.size())
- res += _text[i].chunks[chunk].text;
- else
- res += _text[i].chunks[chunk].text.substr(0, endCol);
- } else if ((int)_text[i].chunks[chunk].text.size() > startCol) {
- ADDFORMATTING();
- res += _text[i].chunks[chunk].text.substr(startCol, endCol - startCol);
- }
-
- startCol -= _text[i].chunks[chunk].text.size();
- endCol -= _text[i].chunks[chunk].text.size();
-
- if (endCol <= 0)
- break;
- }
- // We are at the top line and it is not completely requested
- } else if (i == startRow && startCol != 0) {
- for (uint chunk = 0; chunk < _text[i].chunks.size(); chunk++) {
- if (_text[i].chunks[chunk].text.empty()) // skip empty chunks
- continue;
-
- if (startCol <= 0) {
- ADDFORMATTING();
- res += _text[i].chunks[chunk].text;
- } else if ((int)_text[i].chunks[chunk].text.size() > startCol) {
- ADDFORMATTING();
- res += _text[i].chunks[chunk].text.substr(startCol);
- }
-
- startCol -= _text[i].chunks[chunk].text.size();
- }
- if (newlines && _text[i].paragraphEnd)
- res += '\n';
- // We are at the end row, and it could be not completely requested
- } else if (i == endRow) {
- for (uint chunk = 0; chunk < _text[i].chunks.size(); chunk++) {
- if (_text[i].chunks[chunk].text.empty()) // skip empty chunks
- continue;
-
- ADDFORMATTING();
-
- if (endCol >= (int)_text[i].chunks[chunk].text.size())
- res += _text[i].chunks[chunk].text;
- else
- res += _text[i].chunks[chunk].text.substr(0, endCol);
-
- endCol -= _text[i].chunks[chunk].text.size();
-
- if (endCol <= 0)
- break;
- }
- // We are in the middle of requested range, pass whole line
- } else {
- for (uint chunk = 0; chunk < _text[i].chunks.size(); chunk++) {
- if (_text[i].chunks[chunk].text.empty()) // skip empty chunks
- continue;
-
- ADDFORMATTING();
- res += _text[i].chunks[chunk].text;
- }
-
- if (newlines && _text[i].paragraphEnd)
- res += '\n';
- }
- }
-
- return res;
-}
-
// mostly, we refering reshuffleParagraph to implement this function
void MacText::insertTextFromClipboard() {
int ppos = 0;
@@ -2818,47 +1940,6 @@ void MacText::addNewLine(int *row, int *col) {
render();
}
-void MacTextCanvas::reshuffleParagraph(int *row, int *col, MacFontRun &defaultFormatting) {
- // First, we looking for the paragraph start and end
- int start = *row, end = *row;
-
- while (start && !_text[start - 1].paragraphEnd)
- start--;
-
- while (end < (int)_text.size() - 1 && !_text[end].paragraphEnd) // stop at last line
- end++;
-
- // Get character pos within paragraph
- int ppos = 0;
-
- for (int i = start; i < *row; i++)
- ppos += getLineCharWidth(i);
-
- ppos += *col;
-
- // Get whole paragraph
- Common::U32String paragraph = getTextChunk(start, 0, end, getLineCharWidth(end, true), true, true);
-
- // Remove it from the text
- for (int i = start; i <= end; i++) {
- _text.remove_at(start);
- }
-
- // And now read it
- D(9, "start %d end %d", start, end);
- splitString(paragraph, start, defaultFormatting);
-
- // Find new pos within paragraph after reshuffling
- *row = start;
-
- warning("FIXME, bad design");
- while (ppos > getLineCharWidth(*row, true)) {
- ppos -= getLineCharWidth(*row, true);
- (*row)++;
- }
- *col = ppos;
-}
-
//////////////////
// Cursor stuff
static void cursorTimerHandler(void *refCon) {
@@ -2946,158 +2027,4 @@ const Surface *MacText::getImageSurface(Common::String &fname) {
#endif // USE_PNG
}
-void MacTextCanvas::processTable(int line, int maxWidth) {
- Common::Array<MacTextTableRow> *table = _text[line].table;
- uint numCols = table->front().cells.size();
- uint numRows = table->size();
- Common::Array<int> maxW(numCols), maxL(numCols), colW(numCols), rowH(numRows);
- Common::Array<bool> flex(numCols), wrap(numCols);
-
- int width = maxWidth * 0.9;
- int gutter = 10;
-
- // Compute column widths, both minimal and maximal
- for (auto &row : *table) {
- int i = 0;
- for (auto &cell : row.cells) {
- int cW = 0, cL = 0;
- for (uint l = 0; l < cell._text.size(); l++) {
- (void)cell.getLineWidth(l); // calculate it
-
- cW = MAX(cW, cell._text[l].width);
- cL = MAX(cL, cell._text[l].minWidth);
- }
-
- maxW[i] = MAX(maxW[i], cW);
- maxL[i] = MAX(maxL[i], cL);
-
- i++;
- }
- }
-
- for (uint i = 0; i < numCols; i++) {
- warning("%d: %d - %d", i, maxL[i], maxW[i]);
-
- wrap[i] = (maxW[i] != maxL[i]);
- }
-
- int left = width - (numCols - 1) * gutter;
- int avg = left / numCols;
- int nflex = 0;
-
- // determine whether columns should be flexible and assign
- // width of non-flexible cells
- for (uint i = 0; i < numCols; i++) {
- flex[i] = (maxW[i] > 2 * avg);
- if (flex[i]) {
- nflex++;
- } else {
- colW[i] = maxW[i];
- left -= colW[i];
- }
- }
-
- // if there is not enough space, make columns that could
- // be word-wrapped flexible, too
- if (left < nflex * avg) {
- for (uint i = 0; i < numCols; i++) {
- if (!flex[i] && wrap[i]) {
- left += colW[i];
- colW[i] = 0;
- flex[i] = true;
- nflex += 1;
- }
- }
- }
-
- // Calculate weights for flexible columns. The max width
- // is capped at the page width to treat columns that have to
- // be wrapped more or less equal
- int tot = 0;
- for (uint i = 0; i < numCols; i++) {
- if (flex[i]) {
- maxW[i] = MIN(maxW[i], width);
- tot += maxW[i];
- }
- }
-
- // Now assign the actual width for flexible columns. Make
- // sure that it is at least as long as the longest word length
- for (uint i = 0; i < numCols; i++) {
- if (flex[i]) {
- colW[i] = left * maxW[i] / tot;
- colW[i] = MAX(colW[i], maxL[i]);
- left -= colW[i];
- }
- }
-
- for (uint i = 0; i < numCols; i++) {
- warning("%d: %d", i, colW[i]);
- }
-
- int r = 0;
- for (auto &row : *table) {
- int c = 0;
- rowH[r] = 0;
- for (auto &cell : row.cells) {
- cell._maxWidth = colW[c];
-
- cell.recalcDims();
- cell.reallocSurface();
- cell._surface->clear(_tbgcolor);
- cell.render(0, cell._text.size());
-
- rowH[r] = MAX(rowH[r], cell._textMaxHeight);
-
- c++;
- }
-
- r++;
- }
-
- int tW = 1, tH = 1;
- for (uint i = 0; i < table->size(); i++)
- tH += rowH[i] + gutter * 2 + 1;
-
- for (uint i = 0; i < table->front().cells.size(); i++)
- tW += colW[i] + gutter * 2 + 1;
-
- ManagedSurface *surf = new ManagedSurface(tW, tH, _wm->_pixelformat);
- _text[line].tableSurface = surf;
- _text[line].height = tH;
- _text[line].width = tW;
- surf->clear(_tbgcolor);
-
- surf->hLine(0, 0, tW, _tfgcolor);
- surf->vLine(0, 0, tH, _tfgcolor);
-
- int y = 1;
- for (uint i = 0; i < table->size(); i++) {
- y += gutter * 2 + rowH[i];
- surf->hLine(0, y, tW, _tfgcolor);
- y++;
- }
-
- int x = 1;
- for (uint i = 0; i < table->front().cells.size(); i++) {
- x += gutter * 2 + colW[i];
- surf->vLine(x, 0, tH, _tfgcolor);
- x++;
- }
-
- r = 0;
- y = 1 + gutter;
- for (auto &row : *table) {
- int c = 0;
- x = 1 + gutter;
- for (auto &cell : row.cells) {
- surf->blitFrom(*cell._surface, Common::Point(x, y));
- x += gutter * 2 + 1 + colW[c];
- c++;
- }
- y += gutter * 2 + 1 + rowH[r];
- r++;
- }
-}
-
} // End of namespace Graphics
diff --git a/graphics/macgui/mactext.h b/graphics/macgui/mactext.h
index 69f1d940568..8fcd3539cf5 100644
--- a/graphics/macgui/mactext.h
+++ b/graphics/macgui/mactext.h
@@ -22,18 +22,7 @@
#ifndef GRAPHICS_MACGUI_MACTEXT_H
#define GRAPHICS_MACGUI_MACTEXT_H
-#include "common/timer.h"
-#include "common/system.h"
-
-#include "graphics/macgui/macwindowmanager.h"
-#include "graphics/macgui/macfontmanager.h"
-#include "graphics/macgui/macmenu.h"
-#include "graphics/macgui/macwidget.h"
-#include "graphics/macgui/macwindow.h"
-
-namespace Common {
-class Archive;
-}
+#include "graphics/macgui/mactext-canvas.h"
namespace Image {
class PNGDecoder;
@@ -41,194 +30,6 @@ class PNGDecoder;
namespace Graphics {
-class MacMenu;
-class MacText;
-class MacWidget;
-class MacWindow;
-class MacWindowManager;
-
-struct MacFontRun {
- Common::U32String text;
-
- uint16 fontId;
- byte textSlant;
- uint16 fontSize;
- uint16 palinfo1;
- uint16 palinfo2;
- uint16 palinfo3;
- uint32 fgcolor;
- // to determine whether the next word is part of this one
- bool wordContinuation;
- const Font *font;
- MacWindowManager *wm;
- Common::String link; // Substitute to return when hover or click
-
- MacFontRun() {
- wm = nullptr;
- fontId = textSlant = fontSize = 0;
- palinfo1 = palinfo2 = palinfo3 = 0;
- fgcolor = 0;
- font = nullptr;
- wordContinuation = false;
- }
-
- MacFontRun(MacWindowManager *wm_) {
- wm = wm_;
- fontId = textSlant = fontSize = 0;
- palinfo1 = palinfo2 = palinfo3 = 0;
- fgcolor = 0;
- font = nullptr;
- wordContinuation = false;
- }
-
- MacFontRun(MacWindowManager *wm_, uint16 fontId_, byte textSlant_, uint16 fontSize_,
- uint16 palinfo1_, uint16 palinfo2_, uint16 palinfo3_) {
- setValues(wm_, fontId_, textSlant_, fontSize_, palinfo1_, palinfo2_, palinfo3_);
- wordContinuation = false;
- }
-
- MacFontRun(MacWindowManager *wm_, const Font *font_, byte textSlant_, uint16 fontSize_,
- uint16 palinfo1_, uint16 palinfo2_, uint16 palinfo3_) {
- setValues(wm_, 0, textSlant_, fontSize_, palinfo1_, palinfo2_, palinfo3_);
- font = font_;
- wordContinuation = false;
- }
-
- void setValues(MacWindowManager *wm_, uint16 fontId_, byte textSlant_, uint16 fontSize_,
- uint16 palinfo1_, uint16 palinfo2_, uint16 palinfo3_) {
- wm = wm_;
- fontId = fontId_;
- textSlant = textSlant_;
- fontSize = fontSize_;
- palinfo1 = palinfo1_;
- palinfo2 = palinfo2_;
- palinfo3 = palinfo3_;
- fgcolor = wm_->findBestColor(palinfo1_ & 0xff, palinfo2_ & 0xff, palinfo3_ & 0xff);
- font = nullptr;
- }
-
- const Font *getFont();
-
- const Common::String toString();
- bool equals(MacFontRun &to);
-
- Common::CodePage getEncoding();
- bool plainByteMode();
- Common::String getEncodedText();
-
- bool equals(const MacFontRun *x, const MacFontRun *y) {
- return (x->fontId == y->fontId &&
- x->textSlant == y->textSlant &&
- x->fontSize == y->fontSize &&
- x->palinfo1 == y->palinfo1 &&
- x->palinfo2 == y->palinfo2 &&
- x->palinfo3 == y->palinfo3 &&
- x->fgcolor == y->fgcolor);
- }
-
-};
-
-struct MacTextLine;
-
-class MacTextCanvas {
-public:
- Common::Array<MacTextLine> _text;
- ManagedSurface *_surface = nullptr, *_shadowSurface = nullptr;
- int _maxWidth = 0;
- int _textMaxWidth = 0;
- int _textMaxHeight = 0;
- TextAlign _textAlignment = kTextAlignLeft;
- int _interLinear = 0;
- int _textShadow = 0;
- MacWindowManager *_wm = nullptr;
- uint32 _tfgcolor = 0;
- uint32 _tbgcolor = 0;
- bool _macFontMode = true;
- MacText *_macText;
-
-public:
- ~MacTextCanvas() {
- delete _surface;
- delete _shadowSurface;
- }
-
- void recalcDims();
- void reallocSurface();
- void render(int from, int to);
- void render(int from, int to, int shadow);
- int getAlignOffset(int row);
-
- /**
- * Returns line width in pixels. This takes into account chunks.
- * The result is cached for faster subsequent calls.
- *
- * @param line Line number
- * @param enforce Flag for indicating skipping the cache and computing the width,
- * must be called when text gets changed
- * @param col Compute line width up to specified column, including this column
- * @return line width in pixels, or 0 for non-existent lines
- */
- int getLineWidth(int line, bool enforce = false, int col = -1);
- int getLineHeight(int line);
- int getLineCharWidth(int line, bool enforce = false);
-
- void splitString(const Common::U32String &str, int curLine, MacFontRun &defaultFormatting);
- const Common::U32String::value_type *splitString(const Common::U32String::value_type *s, int curLine, MacFontRun &defaultFormatting);
-
- void chopChunk(const Common::U32String &str, int *curLinePtr, int indent, int maxWidth);
- Common::U32String getTextChunk(int startRow, int startCol, int endRow, int endCol, bool formatted = false, bool newlines = true);
-
- /**
- * Rewraps paragraph containing given text row.
- * When text is modified, we redo whole thing again without touching
- * other paragraphs. Also, cursor position is returned in the arguments
- */
- void reshuffleParagraph(int *row, int *col, MacFontRun &defaultFormatting);
-
- void processTable(int line, int maxWidth);
-};
-
-struct MacTextTableRow {
- Common::Array<MacTextCanvas> cells;
- int heght = -1;
-};
-
-struct MacTextLine {
- int width = -1;
- int height = -1;
- int minWidth = -1;
- int y = 0;
- int charwidth = -1;
- bool paragraphEnd = false;
- int indent = 0; // in units
- int firstLineIndent = 0; // in pixels
- Common::String picfname;
- Common::U32String picalt, pictitle;
- uint16 picpercent = 50;
- Common::Array<MacTextTableRow> *table = nullptr;
- ManagedSurface *tableSurface = nullptr;
-
- Common::Array<MacFontRun> chunks;
-
- MacFontRun &firstChunk() { return chunks[0]; }
- MacFontRun &lastChunk() { return chunks[chunks.size() - 1]; }
-
- /**
- * Search for a chunk at given char column.
- *
- * @param col Requested column, gets modified with in-chunk column
- * @returns Chunk number
- *
- * @note If requested column is too big, returns last character in the line
- */
- uint getChunkNum(int *col);
-
- ~MacTextLine() {
- delete table;
- delete tableSurface;
- }
-};
-
struct SelectedText {
int startX, startY;
int endX, endY;
@@ -442,6 +243,8 @@ private:
Common::Archive *_imageArchive = nullptr;
};
+int getStringWidth(MacFontRun &format, const Common::U32String &str);
+
} // End of namespace Graphics
#endif
diff --git a/graphics/module.mk b/graphics/module.mk
index c2a2113896c..1cedbcd6e01 100644
--- a/graphics/module.mk
+++ b/graphics/module.mk
@@ -29,6 +29,7 @@ MODULE_OBJS := \
macgui/macmenu.o \
macgui/macpopupmenu.o \
macgui/mactext.o \
+ macgui/mactext-canvas.o \
macgui/mactext-md.o \
macgui/mactextwindow.o \
macgui/macwidget.o \
More information about the Scummvm-git-logs
mailing list