mirror of
https://github.com/scummvm/scummvm.git
synced 2025-04-02 10:52:32 -04:00
1035 lines
31 KiB
C++
1035 lines
31 KiB
C++
/* 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/events.h"
|
|
|
|
#include "graphics/macgui/macbutton.h"
|
|
#include "graphics/macgui/mactextwindow.h"
|
|
|
|
#include "director/director.h"
|
|
#include "director/cast.h"
|
|
#include "director/channel.h"
|
|
#include "director/movie.h"
|
|
#include "director/score.h"
|
|
#include "director/sprite.h"
|
|
#include "director/window.h"
|
|
#include "director/castmember/text.h"
|
|
#include "director/lingo/lingo-the.h"
|
|
|
|
namespace Director {
|
|
|
|
TextCastMember::TextCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version, uint8 flags1, bool asButton)
|
|
: CastMember(cast, castId, stream) {
|
|
_type = kCastText;
|
|
|
|
_borderSize = 0;
|
|
_gutterSize = 0;
|
|
_boxShadow = 0;
|
|
_buttonType = kTypeButton;
|
|
_editable = false;
|
|
_maxHeight = _textHeight = 0;
|
|
|
|
_bgcolor = 0;
|
|
_fgcolor = 0xff;
|
|
|
|
_textFlags = 0;
|
|
_scroll = 0;
|
|
_fontId = 1;
|
|
_fontSize = 12;
|
|
_textType = kTextTypeFixed;
|
|
_textAlign = kTextAlignLeft;
|
|
_textShadow = 0;
|
|
_textSlant = 0;
|
|
_bgpalinfo1 = _bgpalinfo2 = _bgpalinfo3 = 0;
|
|
_fgpalinfo1 = _fgpalinfo2 = _fgpalinfo3 = 0xff;
|
|
|
|
// seems like the line spacing is default to 1 in D4
|
|
_lineSpacing = g_director->getVersion() >= 400 ? 1 : 0;
|
|
|
|
if (debugChannelSet(4, kDebugLoading)) {
|
|
stream.hexdump(stream.size());
|
|
}
|
|
|
|
if (version < kFileVer400) {
|
|
_flags1 = flags1; // region: 0 - auto, 1 - matte, 2 - disabled
|
|
_borderSize = stream.readByte();
|
|
_gutterSize = stream.readByte();
|
|
_boxShadow = stream.readByte();
|
|
_textType = static_cast<TextType>(stream.readByte());
|
|
_textAlign = static_cast<TextAlignType>(stream.readUint16());
|
|
_bgpalinfo1 = stream.readUint16();
|
|
_bgpalinfo2 = stream.readUint16();
|
|
_bgpalinfo3 = stream.readUint16();
|
|
|
|
uint32 pad2;
|
|
uint16 pad3;
|
|
uint16 pad4 = 0;
|
|
uint16 totalTextHeight;
|
|
|
|
if (version < kFileVer300) {
|
|
pad2 = stream.readUint16();
|
|
if (pad2 != 0) { // In D2 there are values
|
|
warning("TextCastMember: pad2: %x", pad2);
|
|
}
|
|
|
|
_initialRect = Movie::readRect(stream);
|
|
pad3 = stream.readUint16();
|
|
|
|
_textShadow = stream.readByte();
|
|
_textFlags = stream.readByte();
|
|
if (_textFlags & 0xf8)
|
|
warning("Unprocessed text cast flags: %x", _textFlags & 0xf8);
|
|
|
|
totalTextHeight = stream.readUint16();
|
|
} else {
|
|
pad2 = stream.readUint16();
|
|
_initialRect = Movie::readRect(stream);
|
|
pad3 = stream.readUint16();
|
|
_textFlags = stream.readUint16(); // 1: editable, 2: auto tab, 4: don't wrap
|
|
_editable = _textFlags & 0x1;
|
|
totalTextHeight = stream.readUint16();
|
|
}
|
|
|
|
debugC(2, kDebugLoading, "TextCastMember(): flags1: %d, border: %d gutter: %d shadow: %d textType: %d align: %04x",
|
|
_flags1, _borderSize, _gutterSize, _boxShadow, _textType, _textAlign);
|
|
debugC(2, kDebugLoading, "TextCastMember(): background rgb: 0x%04x 0x%04x 0x%04x, pad2: %x pad3: %d pad4: %d shadow: %d flags: %d totHeight: %d",
|
|
_bgpalinfo1, _bgpalinfo2, _bgpalinfo3, pad2, pad3, pad4, _textShadow, _textFlags, totalTextHeight);
|
|
if (debugChannelSet(2, kDebugLoading)) {
|
|
_initialRect.debugPrint(2, "TextCastMember(): rect:");
|
|
}
|
|
} else if (version >= kFileVer400 && version < kFileVer600) {
|
|
_flags1 = flags1;
|
|
_borderSize = stream.readByte();
|
|
_gutterSize = stream.readByte();
|
|
_boxShadow = stream.readByte();
|
|
_textType = static_cast<TextType>(stream.readByte());
|
|
_textAlign = static_cast<TextAlignType>(stream.readSint16()); // this is because 'right' is -1? or should that be 255?
|
|
_bgpalinfo1 = stream.readUint16();
|
|
_bgpalinfo2 = stream.readUint16();
|
|
_bgpalinfo3 = stream.readUint16();
|
|
_scroll = stream.readUint16();
|
|
|
|
_fontId = 1; // this is in STXT
|
|
|
|
_initialRect = Movie::readRect(stream);
|
|
_maxHeight = stream.readUint16();
|
|
_textShadow = stream.readByte();
|
|
_textFlags = stream.readByte(); // 1: editable, 2: auto tab 4: don't wrap
|
|
_editable = _textFlags & 0x1;
|
|
|
|
_textHeight = stream.readUint16();
|
|
_textSlant = 0;
|
|
debugC(2, kDebugLoading, "TextCastMember(): flags1: %d, border: %d gutter: %d shadow: %d textType: %d align: %04x",
|
|
_flags1, _borderSize, _gutterSize, _boxShadow, _textType, _textAlign);
|
|
debugC(2, kDebugLoading, "TextCastMember(): background rgb: 0x%04x 0x%04x 0x%04x, shadow: %d flags: %d textHeight: %d",
|
|
_bgpalinfo1, _bgpalinfo2, _bgpalinfo3, _textShadow, _textFlags, _textHeight);
|
|
if (debugChannelSet(2, kDebugLoading)) {
|
|
_initialRect.debugPrint(2, "TextCastMember(): rect:");
|
|
}
|
|
} else {
|
|
warning("Text/ButtonCastMember(): >D5 isn't handled");
|
|
}
|
|
|
|
if (asButton) {
|
|
_type = kCastButton;
|
|
_buttonType = static_cast<ButtonType>(stream.readUint16BE() - 1);
|
|
}
|
|
|
|
_bgcolor = g_director->_wm->findBestColor(_bgpalinfo1 & 0xff, _bgpalinfo2 & 0xff, _bgpalinfo3 & 0xff);
|
|
|
|
_modified = true;
|
|
}
|
|
|
|
TextCastMember::TextCastMember(Cast *cast, uint16 castId, TextCastMember &source)
|
|
: CastMember(cast, castId) {
|
|
_type = kCastText;
|
|
// force a load so we can copy the cast resource information
|
|
source.load();
|
|
_loaded = true;
|
|
|
|
_initialRect = source._initialRect;
|
|
_boundingRect = source._boundingRect;
|
|
_children = source._children;
|
|
|
|
_borderSize = source._borderSize;
|
|
_gutterSize = source._gutterSize;
|
|
_boxShadow = source._boxShadow;
|
|
_maxHeight = source._maxHeight;
|
|
_textHeight = source._textHeight;
|
|
|
|
_fontId = source._fontId;
|
|
_fontSize = source._fontSize;
|
|
_textType = source._textType;
|
|
_textAlign = source._textAlign;
|
|
_textShadow = source._textShadow;
|
|
_scroll = source._scroll;
|
|
_textSlant = source._textSlant;
|
|
_textFlags = source._textFlags;
|
|
_bgpalinfo1 = source._bgpalinfo1;
|
|
_bgpalinfo2 = source._bgpalinfo2;
|
|
_bgpalinfo3 = source._bgpalinfo3;
|
|
_fgpalinfo1 = source._fgpalinfo1;
|
|
_fgpalinfo2 = source._fgpalinfo2;
|
|
_fgpalinfo3 = source._fgpalinfo3;
|
|
_buttonType = source._buttonType;
|
|
_editable = source._editable;
|
|
_lineSpacing = source._lineSpacing;
|
|
|
|
_ftext = source._ftext;
|
|
_ptext = source._ptext;
|
|
_rtext = source._rtext;
|
|
|
|
_bgcolor = source._bgcolor;
|
|
_fgcolor = source._fgcolor;
|
|
}
|
|
|
|
void TextCastMember::setColors(uint32 *fgcolor, uint32 *bgcolor) {
|
|
if (fgcolor)
|
|
_fgcolor = *fgcolor;
|
|
|
|
if (bgcolor)
|
|
_bgcolor = *bgcolor;
|
|
|
|
// if we want to keep the format unchanged, then we need to modify _ftext as well
|
|
Graphics::MacWidget *target = getWidget();
|
|
if (target) {
|
|
if (_textType == kTextTypeScrolling) {
|
|
((Graphics::MacTextWindow *)target)->setColors(_fgcolor, _bgcolor);
|
|
} else {
|
|
((Graphics::MacText *)target)->setColors(_fgcolor, _bgcolor);
|
|
}
|
|
} else {
|
|
_modified = true;
|
|
}
|
|
}
|
|
|
|
Graphics::TextAlign TextCastMember::getAlignment() {
|
|
switch (_textAlign) {
|
|
case kTextAlignRight:
|
|
return Graphics::kTextAlignRight;
|
|
case kTextAlignCenter:
|
|
return Graphics::kTextAlignCenter;
|
|
case kTextAlignLeft:
|
|
default:
|
|
return Graphics::kTextAlignLeft;
|
|
}
|
|
}
|
|
|
|
void TextCastMember::setBackColor(uint32 bgCol) {
|
|
_bgcolor = bgCol;
|
|
_modified = true;
|
|
}
|
|
|
|
uint32 TextCastMember::getForeColor(int start, int end) {
|
|
Graphics::MacWidget *target = getWidget();
|
|
if (target) {
|
|
if (_textType == kTextTypeScrolling) {
|
|
return ((Graphics::MacTextWindow *)target)->getTextColor(start, end);
|
|
} else {
|
|
return ((Graphics::MacText *)target)->getTextColor(start, end);
|
|
}
|
|
}
|
|
return _fgcolor;
|
|
}
|
|
|
|
void TextCastMember::setForeColor(uint32 fgCol) {
|
|
_fgcolor = fgCol;
|
|
_modified = true;
|
|
}
|
|
|
|
void TextCastMember::setForeColor(uint32 fgCol, int start, int end) {
|
|
Graphics::MacWidget *target = getWidget();
|
|
if (target) {
|
|
if (_textType == kTextTypeScrolling) {
|
|
return ((Graphics::MacTextWindow *)target)->setTextColor(fgCol, start, end);
|
|
} else {
|
|
return ((Graphics::MacText *)target)->setTextColor(fgCol, start, end);
|
|
}
|
|
}
|
|
_modified = true;
|
|
}
|
|
|
|
|
|
void TextCastMember::importStxt(const Stxt *stxt) {
|
|
_fontId = stxt->_style.fontId;
|
|
_textSlant = stxt->_style.textSlant;
|
|
_fontSize = stxt->_style.fontSize;
|
|
_fgpalinfo1 = stxt->_style.r;
|
|
_fgpalinfo2 = stxt->_style.g;
|
|
_fgpalinfo3 = stxt->_style.b;
|
|
// The default color in the Stxt will override the fgcolor,
|
|
// e.g. empty editable text boxes will use the Stxt color
|
|
_fgcolor = g_director->_wm->findBestColor(_fgpalinfo1 >> 8, _fgpalinfo2 >> 8, _fgpalinfo3 >> 8);
|
|
_ftext = stxt->_ftext;
|
|
_ptext = stxt->_ptext;
|
|
_rtext = stxt->_rtext;
|
|
|
|
// Rectifying _fontId in case of a fallback font
|
|
Graphics::MacFont macFont(_fontId, _fontSize, _textSlant);
|
|
g_director->_wm->_fontMan->getFont(&macFont);
|
|
_fontId = macFont.getId();
|
|
|
|
// If the text is empty, that means we ignored the font and now
|
|
// set the text height to a minimal one.
|
|
//
|
|
// This fixes `number of chars` in Lingo Workshop
|
|
if (_textType == kTextTypeAdjustToFit && _ftext.empty())
|
|
_initialRect.setHeight(macFont.getSize() + (2 * _borderSize) + _gutterSize + _boxShadow);
|
|
}
|
|
|
|
bool textWindowCallback(Graphics::WindowClick click, Common::Event &event, void *ptr) {
|
|
return g_director->getCurrentMovie()->processEvent(event);
|
|
}
|
|
|
|
Graphics::MacWidget *TextCastMember::createWindowOrWidget(Common::Rect &bbox, Common::Rect dims, Graphics::MacFont *macFont) {
|
|
Graphics::MacWidget *widget = nullptr;
|
|
|
|
if (_textType == kTextTypeScrolling) {
|
|
Graphics::MacTextWindow *window = (Graphics::MacTextWindow *)g_director->_wm->addTextWindow(macFont, getForeColor(), getBackColor(), _initialRect.width(),
|
|
getAlignment(), nullptr, false);
|
|
// Set callback so that we can process events like mouse clicks
|
|
window->setCallback(textWindowCallback, window);
|
|
// Set widget to this window!
|
|
widget = window;
|
|
|
|
// Set configuration
|
|
window->setBorderType(Graphics::kWindowBorderMacOSNoBorderScrollbar);
|
|
window->enableScrollbar(true);
|
|
// window->setMode(Graphics::kWindowModeDynamicScrollbar);
|
|
window->move(bbox.left, bbox.top);
|
|
window->resize(dims.width(), dims.height());
|
|
window->setEditable(false);
|
|
window->setSelectable(false);
|
|
window->appendText(_ftext);
|
|
window->draw(true);
|
|
} else {
|
|
widget = new Graphics::MacText(g_director->getCurrentWindow(), bbox.left, bbox.top, dims.width(), dims.height(), g_director->_wm, _ftext, macFont, getForeColor(), getBackColor(), _initialRect.width(), getAlignment(), _lineSpacing, _borderSize, _gutterSize, _boxShadow, _textShadow, _textType == kTextTypeFixed);
|
|
((Graphics::MacText *)widget)->setSelRange(g_director->getCurrentMovie()->_selStart, g_director->getCurrentMovie()->_selEnd);
|
|
((Graphics::MacText *)widget)->draw();
|
|
}
|
|
|
|
return widget;
|
|
}
|
|
|
|
Graphics::MacWidget *TextCastMember::createWidget(Common::Rect &bbox, Channel *channel, SpriteType spriteType) {
|
|
Graphics::MacFont *macFont = new Graphics::MacFont(_fontId, _fontSize, _textSlant);
|
|
Graphics::MacWidget *widget = nullptr;
|
|
Common::Rect dims(bbox);
|
|
|
|
CastType type = _type;
|
|
ButtonType buttonType = _buttonType;
|
|
|
|
// WORKAROUND: In D2/D3 there can be text casts that have button
|
|
// information set in the sprite.
|
|
if (type == kCastText && isButtonSprite(spriteType)) {
|
|
type = kCastButton;
|
|
buttonType = ButtonType(spriteType - 8);
|
|
}
|
|
|
|
switch (type) {
|
|
case kCastText:
|
|
// for mactext, we can expand now, but we can't shrink. so we may pass the small size when we have adjustToFit text style
|
|
if (_textType == kTextTypeAdjustToFit) {
|
|
dims.right = MIN<int>(dims.right, dims.left + _initialRect.width());
|
|
dims.bottom = MIN<int>(dims.bottom, dims.top + _initialRect.height());
|
|
} else if (_textType == kTextTypeFixed || _textType == kTextTypeScrolling) {
|
|
// use initialRect to create widget for fixed style text, this maybe related to version.
|
|
dims.right = MAX<int>(dims.right, dims.left + _initialRect.width());
|
|
dims.bottom = MAX<int>(dims.bottom, dims.top + MAX<int>(_initialRect.height(), _maxHeight));
|
|
}
|
|
widget = createWindowOrWidget(bbox, dims, macFont);
|
|
if (_textType != kTextTypeScrolling) {
|
|
((Graphics::MacText *)widget)->setEditable(channel->_sprite->_editable);
|
|
}
|
|
|
|
// since we disable the ability of setActive in setEdtiable, then we need to set active widget manually
|
|
if (channel->_sprite->_editable) {
|
|
Graphics::MacWidget *activeWidget = g_director->_wm->getActiveWidget();
|
|
if (activeWidget == nullptr || !activeWidget->isEditable())
|
|
g_director->_wm->setActiveWidget(widget);
|
|
}
|
|
break;
|
|
|
|
case kCastButton:
|
|
// note that we use _initialRect for the dimensions of the button;
|
|
// the values provided in the sprite bounding box are ignored
|
|
widget = new Graphics::MacButton(Graphics::MacButtonType(buttonType), getAlignment(), g_director->getCurrentWindow(), bbox.left, bbox.top, _initialRect.width(), _initialRect.height(), g_director->_wm, _ftext, macFont, getForeColor(), g_director->_wm->_colorWhite);
|
|
widget->_focusable = true;
|
|
|
|
((Graphics::MacButton *)widget)->setHilite(_hilite);
|
|
((Graphics::MacButton *)widget)->setCheckBoxType(g_director->getCurrentMovie()->_checkBoxType);
|
|
((Graphics::MacButton *)widget)->draw();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
delete macFont;
|
|
return widget;
|
|
}
|
|
|
|
Graphics::MacWidget *TextCastMember::getWidget() {
|
|
// FIXME: The cast member should be the source of truth for the widget.
|
|
// You don't have the issue you have with e.g. bitmaps where the channel
|
|
// can stretch: all sprites of the cast member have the same dimensions.
|
|
// There is technically a small window between typing something in and hitting
|
|
// enter/defocusing where other copies of the widget are out of sync,
|
|
// but they will resync pretty quickly.
|
|
Channel *toEdit = nullptr;
|
|
Common::Array<Channel *> channels = g_director->getCurrentMovie()->getScore()->_channels;
|
|
for (uint i = 0; i < channels.size(); i++) {
|
|
if (channels[i]->_sprite->_cast == this) {
|
|
toEdit = channels[i];
|
|
break;
|
|
}
|
|
}
|
|
if (toEdit) {
|
|
Common::Rect bbox = toEdit->getBbox();
|
|
if (!toEdit->_widget)
|
|
toEdit->_widget = createWidget(bbox, toEdit, toEdit->_sprite->_spriteType);
|
|
return toEdit->_widget;
|
|
}
|
|
return _widget;
|
|
}
|
|
|
|
void TextCastMember::importRTE(byte *text) {
|
|
//assert(rteList.size() == 3);
|
|
//child0 is probably font data.
|
|
//child1 is the raw text.
|
|
_rtext = _ptext = _ftext = Common::String((char*)text);
|
|
//child2 is positional?
|
|
}
|
|
|
|
void TextCastMember::setRawText(const Common::String &text) {
|
|
// Do nothing if text did not change
|
|
if (_ptext.equals(Common::U32String(text)))
|
|
return;
|
|
|
|
_rtext = text;
|
|
_ptext = Common::U32String(text);
|
|
|
|
// If text has changed, use the cached formatting from first STXT in this castmember.
|
|
Common::U32String formatting = Common::String::format("\001\016%04x%02x%04x%04x%04x%04x", _fontId, _textSlant, _fontSize, _fgpalinfo1, _fgpalinfo2, _fgpalinfo3);
|
|
_ftext = formatting + _ptext;
|
|
_modified = true;
|
|
}
|
|
|
|
int TextCastMember::getLineCount() {
|
|
Graphics::MacWidget *target = getWidget();
|
|
if (target) {
|
|
if (_textType == kTextTypeScrolling) {
|
|
return ((Graphics::MacTextWindow *)target)->getRowCount();
|
|
} else {
|
|
return ((Graphics::MacText *)target)->getRowCount();
|
|
}
|
|
}
|
|
warning("TextCastMember::getLineCount(): no widget available, returning 0");
|
|
return 0;
|
|
}
|
|
|
|
int TextCastMember::getLineHeight(int line) {
|
|
Graphics::MacWidget *target = getWidget();
|
|
if (target) {
|
|
if (_textType == kTextTypeScrolling) {
|
|
return ((Graphics::MacTextWindow *)target)->getLineHeight(line);
|
|
} else {
|
|
return ((Graphics::MacText *)target)->getLineHeight(line);
|
|
}
|
|
}
|
|
warning("TextCastMember::getLineHeight(): no widget available, returning 0");
|
|
return 0;
|
|
}
|
|
|
|
// D4 dictionary book said this is line spacing
|
|
int TextCastMember::getTextHeight() {
|
|
Graphics::MacWidget *target = getWidget();
|
|
if (target) {
|
|
if (_textType == kTextTypeScrolling) {
|
|
return ((Graphics::MacTextWindow *)target)->getLineSpacing();
|
|
} else {
|
|
return ((Graphics::MacText *)target)->getLineSpacing();
|
|
}
|
|
}
|
|
return _lineSpacing;
|
|
}
|
|
|
|
Common::String TextCastMember::getTextFont() {
|
|
Graphics::MacWidget *target = getWidget();
|
|
if (target) {
|
|
if (_textType == kTextTypeScrolling) {
|
|
int fontId = ((Graphics::MacTextWindow *)target)->getTextFont();
|
|
return g_director->_wm->_fontMan->getFontName(fontId);
|
|
} else {
|
|
int fontId = ((Graphics::MacText *)target)->getTextFont();
|
|
return g_director->_wm->_fontMan->getFontName(fontId);
|
|
}
|
|
}
|
|
return g_director->_wm->_fontMan->getFontName(_fontId);
|
|
}
|
|
|
|
Common::String TextCastMember::getTextFont(int start, int end) {
|
|
Graphics::MacWidget *target = getWidget();
|
|
if (target) {
|
|
if (_textType == kTextTypeScrolling) {
|
|
int fontId = ((Graphics::MacTextWindow *)target)->getTextFont(start, end);
|
|
return g_director->_wm->_fontMan->getFontName(fontId);
|
|
} else {
|
|
int fontId = ((Graphics::MacText *)target)->getTextFont(start, end);
|
|
return g_director->_wm->_fontMan->getFontName(fontId);
|
|
}
|
|
}
|
|
return g_director->_wm->_fontMan->getFontName(_fontId);
|
|
}
|
|
|
|
void TextCastMember::setTextFont(const Common::String &fontName) {
|
|
Graphics::MacWidget *target = getWidget();
|
|
if (!target)
|
|
return;
|
|
if (_textType == kTextTypeScrolling) {
|
|
((Graphics::MacTextWindow *)target)->enforceTextFont((uint16) g_director->_wm->_fontMan->getFontIdByName(fontName));
|
|
_ptext = ((Graphics::MacTextWindow *)target)->getPlainText();
|
|
_ftext = ((Graphics::MacTextWindow *)target)->getTextChunk(0, 0, -1, -1, true);
|
|
} else {
|
|
((Graphics::MacText *)target)->enforceTextFont((uint16) g_director->_wm->_fontMan->getFontIdByName(fontName));
|
|
_ptext = ((Graphics::MacText *)target)->getPlainText();
|
|
_ftext = ((Graphics::MacText *)target)->getTextChunk(0, 0, -1, -1, true);
|
|
}
|
|
}
|
|
|
|
void TextCastMember::setTextFont(const Common::String &fontName, int start, int end) {
|
|
Graphics::MacWidget *target = getWidget();
|
|
if (!target)
|
|
return;
|
|
if (_textType == kTextTypeScrolling) {
|
|
((Graphics::MacTextWindow *)target)->setTextFont((uint16) g_director->_wm->_fontMan->getFontIdByName(fontName), start, end);
|
|
_ptext = ((Graphics::MacTextWindow *)target)->getPlainText();
|
|
_ftext = ((Graphics::MacTextWindow *)target)->getTextChunk(0, 0, -1, -1, true);
|
|
} else {
|
|
((Graphics::MacText *)target)->setTextFont((uint16) g_director->_wm->_fontMan->getFontIdByName(fontName), start, end);
|
|
_ptext = ((Graphics::MacText *)target)->getPlainText();
|
|
_ftext = ((Graphics::MacText *)target)->getTextChunk(0, 0, -1, -1, true);
|
|
}
|
|
}
|
|
|
|
Common::U32String TextCastMember::getText() {
|
|
return _ptext;
|
|
}
|
|
|
|
Common::String TextCastMember::getRawText() {
|
|
return _rtext;
|
|
}
|
|
|
|
int TextCastMember::getTextSize() {
|
|
Graphics::MacWidget *target = getWidget();
|
|
if (target) {
|
|
if (_textType == kTextTypeScrolling) {
|
|
return ((Graphics::MacTextWindow *)target)->getTextSize();
|
|
} else {
|
|
return ((Graphics::MacText *)target)->getTextSize();
|
|
}
|
|
}
|
|
|
|
return _fontSize;
|
|
}
|
|
|
|
int TextCastMember::getTextSize(int start, int end) {
|
|
Graphics::MacWidget *target = getWidget();
|
|
if (target) {
|
|
if (_textType == kTextTypeScrolling) {
|
|
return ((Graphics::MacTextWindow *)target)->getTextSize(start, end);
|
|
} else {
|
|
return ((Graphics::MacText *)target)->getTextSize(start, end);
|
|
}
|
|
}
|
|
|
|
return _fontSize;
|
|
}
|
|
|
|
void TextCastMember::setTextSize(int textSize) {
|
|
Graphics::MacWidget *target = getWidget();
|
|
if (target) {
|
|
if (_textType == kTextTypeScrolling) {
|
|
((Graphics::MacTextWindow *)target)->setTextSize(textSize);
|
|
_ptext = ((Graphics::MacTextWindow *)target)->getPlainText();
|
|
_ftext = ((Graphics::MacTextWindow *)target)->getTextChunk(0, 0, -1, -1, true);
|
|
} else {
|
|
((Graphics::MacText *)target)->setTextSize(textSize);
|
|
_ptext = ((Graphics::MacText *)target)->getPlainText();
|
|
_ftext = ((Graphics::MacText *)target)->getTextChunk(0, 0, -1, -1, true);
|
|
((Graphics::MacText *)target)->draw();
|
|
}
|
|
}
|
|
_fontSize = textSize;
|
|
_modified = true;
|
|
}
|
|
|
|
void TextCastMember::setTextSize(int textSize, int start, int end) {
|
|
Graphics::MacWidget *target = getWidget();
|
|
if (target) {
|
|
if (_textType == kTextTypeScrolling) {
|
|
((Graphics::MacTextWindow *)target)->setTextSize(textSize, start, end);
|
|
_ptext = ((Graphics::MacTextWindow *)target)->getPlainText();
|
|
_ftext = ((Graphics::MacTextWindow *)target)->getTextChunk(0, 0, -1, -1, true);
|
|
} else {
|
|
((Graphics::MacText *)target)->setTextSize(textSize, start, end);
|
|
_ptext = ((Graphics::MacText *)target)->getPlainText();
|
|
_ftext = ((Graphics::MacText *)target)->getTextChunk(0, 0, -1, -1, true);
|
|
((Graphics::MacText *)target)->draw();
|
|
}
|
|
}
|
|
_modified = true;
|
|
}
|
|
|
|
Common::String TextCastMember::getTextStyle() {
|
|
int slantVal = _textSlant;
|
|
Graphics::MacWidget *target = getWidget();
|
|
if (target) {
|
|
if (_textType == kTextTypeScrolling) {
|
|
slantVal = ((Graphics::MacTextWindow *)target)->getTextSlant();
|
|
} else {
|
|
slantVal = ((Graphics::MacText *)target)->getTextSlant();
|
|
}
|
|
}
|
|
return g_director->_wm->_fontMan->getNameFromSlant(slantVal);
|
|
}
|
|
|
|
Common::String TextCastMember::getTextStyle(int start, int end) {
|
|
int slantVal = _textSlant;
|
|
Graphics::MacWidget *target = getWidget();
|
|
if (target) {
|
|
if (_textType == kTextTypeScrolling) {
|
|
slantVal = ((Graphics::MacTextWindow *)target)->getTextSlant(start, end);
|
|
} else {
|
|
slantVal = ((Graphics::MacText *)target)->getTextSlant(start, end);
|
|
}
|
|
}
|
|
return g_director->_wm->_fontMan->getNameFromSlant(slantVal);
|
|
}
|
|
|
|
void TextCastMember::setTextStyle(const Common::String &textStyle) {
|
|
Graphics::MacWidget *target = getWidget();
|
|
int slant = g_director->_wm->_fontMan->parseSlantFromName(textStyle);
|
|
if (target) {
|
|
if (_textType == kTextTypeScrolling) {
|
|
((Graphics::MacTextWindow *)target)->enforceTextSlant(slant);
|
|
_ptext = ((Graphics::MacTextWindow *)target)->getPlainText();
|
|
_ftext = ((Graphics::MacTextWindow *)target)->getTextChunk(0, 0, -1, -1, true);
|
|
} else {
|
|
((Graphics::MacText *)target)->enforceTextSlant(slant);
|
|
_ptext = ((Graphics::MacText *)target)->getPlainText();
|
|
_ftext = ((Graphics::MacText *)target)->getTextChunk(0, 0, -1, -1, true);
|
|
((Graphics::MacText *)target)->draw();
|
|
}
|
|
}
|
|
_modified = true;
|
|
}
|
|
|
|
void TextCastMember::setTextStyle(const Common::String &textStyle, int start, int end) {
|
|
Graphics::MacWidget *target = getWidget();
|
|
int slant = g_director->_wm->_fontMan->parseSlantFromName(textStyle);
|
|
if (target) {
|
|
if (_textType == kTextTypeScrolling) {
|
|
((Graphics::MacTextWindow *)target)->setTextSlant(slant, start, end);
|
|
_ptext = ((Graphics::MacTextWindow *)target)->getPlainText();
|
|
_ftext = ((Graphics::MacTextWindow *)target)->getTextChunk(0, 0, -1, -1, true);
|
|
} else {
|
|
((Graphics::MacText *)target)->setTextSlant(slant, start, end);
|
|
_ptext = ((Graphics::MacText *)target)->getPlainText();
|
|
_ftext = ((Graphics::MacText *)target)->getTextChunk(0, 0, -1, -1, true);
|
|
((Graphics::MacText *)target)->draw();
|
|
}
|
|
}
|
|
_modified = true;
|
|
}
|
|
|
|
void TextCastMember::updateFromWidget(Graphics::MacWidget *widget) {
|
|
if (widget && _type == kCastText) {
|
|
if (_textType == kTextTypeScrolling) {
|
|
_ptext = ((Graphics::MacTextWindow *)widget)->getEditedString();
|
|
} else {
|
|
_ptext = ((Graphics::MacText *)widget)->getEditedString();
|
|
}
|
|
}
|
|
}
|
|
|
|
Common::String TextCastMember::formatInfo() {
|
|
// need to pull the data from the STXT resource before the
|
|
// debug output will be visible
|
|
load();
|
|
Common::String format = formatStringForDump(_ptext.encode());
|
|
|
|
return Common::String::format(
|
|
"initialRect: %dx%d@%d,%d, boundingRect: %dx%d@%d,%d, foreColor: %d, backColor: %d, editable: %d, text: \"%s\"",
|
|
_initialRect.width(), _initialRect.height(),
|
|
_initialRect.left, _initialRect.top,
|
|
_boundingRect.width(), _boundingRect.height(),
|
|
_boundingRect.left, _boundingRect.top,
|
|
getForeColor(), getBackColor(),
|
|
_editable, formatStringForDump(format).c_str()
|
|
);
|
|
}
|
|
|
|
void TextCastMember::load() {
|
|
if (_loaded)
|
|
return;
|
|
|
|
uint stxtid = 0;
|
|
if (_cast->_version >= kFileVer400) {
|
|
for (auto &it : _children) {
|
|
if (it.tag == MKTAG('S', 'T', 'X', 'T')) {
|
|
stxtid = it.index;
|
|
break;
|
|
}
|
|
}
|
|
if (!stxtid) {
|
|
warning("TextCastMember::load(): No STXT resource found in %d children", _children.size());
|
|
}
|
|
} else {
|
|
stxtid = _castId;
|
|
}
|
|
|
|
if (_cast->_loadedStxts.contains(stxtid)) {
|
|
const Stxt *stxt = _cast->_loadedStxts.getVal(stxtid);
|
|
importStxt(stxt);
|
|
_size = stxt->_size;
|
|
} else {
|
|
warning("TextCastMember::load(): stxtid %i isn't loaded", stxtid);
|
|
}
|
|
|
|
_loaded = true;
|
|
}
|
|
|
|
void TextCastMember::unload() {
|
|
// No unload necessary.
|
|
}
|
|
|
|
bool TextCastMember::hasField(int field) {
|
|
switch (field) {
|
|
case kTheHilite:
|
|
case kTheText:
|
|
case kTheTextAlign:
|
|
case kTheTextFont:
|
|
case kTheTextHeight:
|
|
case kTheTextSize:
|
|
case kTheTextStyle:
|
|
return true;
|
|
case kTheAutoTab:
|
|
case kTheBorder:
|
|
case kTheBoxDropShadow:
|
|
case kTheBoxType:
|
|
case kTheEditable:
|
|
case kTheLineCount:
|
|
case kTheMargin:
|
|
case kThePageHeight:
|
|
case kTheScrollTop:
|
|
case kTheWordWrap:
|
|
return _type == kCastText;
|
|
case kTheButtonType:
|
|
return _type == kCastButton;
|
|
default:
|
|
break;
|
|
}
|
|
return CastMember::hasField(field);
|
|
}
|
|
|
|
Datum TextCastMember::getField(int field) {
|
|
Datum d;
|
|
|
|
switch (field) {
|
|
case kTheHilite:
|
|
d = _hilite;
|
|
break;
|
|
case kTheText:
|
|
d = getText().encode(Common::kUtf8);
|
|
break;
|
|
case kTheTextAlign:
|
|
d.type = STRING;
|
|
switch (_textAlign) {
|
|
case kTextAlignLeft:
|
|
d.u.s = new Common::String("left");
|
|
break;
|
|
case kTextAlignCenter:
|
|
d.u.s = new Common::String("center");
|
|
break;
|
|
case kTextAlignRight:
|
|
d.u.s = new Common::String("right");
|
|
break;
|
|
default:
|
|
warning("TextCastMember::getField(): Invalid text align spec");
|
|
break;
|
|
}
|
|
break;
|
|
case kTheTextFont:
|
|
d = getTextFont();
|
|
break;
|
|
case kTheTextHeight:
|
|
d = getTextHeight();
|
|
break;
|
|
case kTheTextSize:
|
|
d = getTextSize();
|
|
break;
|
|
case kTheTextStyle:
|
|
d = getTextStyle();
|
|
break;
|
|
case kTheAutoTab:
|
|
warning("STUB: TextCastMember::getField(): autoTab not implemented");
|
|
d = 1;
|
|
break;
|
|
case kTheBorder:
|
|
d = _borderSize;
|
|
break;
|
|
case kTheBoxDropShadow:
|
|
warning("STUB: TextCastMember::getField(): boxDropShadow not implemented");
|
|
d = 1;
|
|
break;
|
|
case kTheEditable:
|
|
d = (int)_editable;
|
|
break;
|
|
case kTheLineCount:
|
|
d = getLineCount();
|
|
break;
|
|
case kTheMargin:
|
|
warning("STUB: TextCastMember::getField(): margin not implemented");
|
|
d = 0;
|
|
break;
|
|
case kThePageHeight:
|
|
warning("STUB: TextCastMember::getField(): pageHeight not implemented");
|
|
d = 0;
|
|
break;
|
|
case kTheScrollTop:
|
|
d = _scroll;
|
|
break;
|
|
case kTheWordWrap:
|
|
warning("STUB: TextCastMember::getField(): wordWrap not implemented");
|
|
d = 1;
|
|
break;
|
|
case kTheButtonType:
|
|
switch (_buttonType) {
|
|
case kTypeCheckBox:
|
|
d = Datum("checkBox");
|
|
d.type = SYMBOL;
|
|
break;
|
|
case kTypeRadio:
|
|
d = Datum("radioButton");
|
|
d.type = SYMBOL;
|
|
break;
|
|
case kTypeButton:
|
|
default:
|
|
d = Datum("pushButton");
|
|
d.type = SYMBOL;
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
d = CastMember::getField(field);
|
|
}
|
|
|
|
return d;
|
|
}
|
|
|
|
bool TextCastMember::setField(int field, const Datum &d) {
|
|
switch (field) {
|
|
case kTheBackColor:
|
|
{
|
|
uint32 color = g_director->transformColor(d.asInt());
|
|
setColors(nullptr, &color);
|
|
}
|
|
return true;
|
|
case kTheForeColor:
|
|
{
|
|
uint32 color = g_director->transformColor(d.asInt());
|
|
setColors(&color, nullptr);
|
|
}
|
|
return true;
|
|
case kTheHilite:
|
|
// TODO: Understand how texts can be selected programmatically as well.
|
|
// since hilite won't affect text castmember, and we may have button info in text cast in D2/3. so don't check type here
|
|
_hilite = (bool)d.asInt();
|
|
_modified = true;
|
|
return true;
|
|
case kTheText:
|
|
setRawText(d.asString());
|
|
return true;
|
|
case kTheTextAlign:
|
|
{
|
|
Common::String select = d.asString();
|
|
TextAlignType align;
|
|
if (select.equalsIgnoreCase("left")) {
|
|
align = kTextAlignLeft;
|
|
} else if (select.equalsIgnoreCase("center")) {
|
|
align = kTextAlignCenter;
|
|
} else if (select.equalsIgnoreCase("right")) {
|
|
align = kTextAlignRight;
|
|
} else {
|
|
warning("TextCastMember::setField(): Unknown text align spec: %s", d.asString(true).c_str());
|
|
break;
|
|
}
|
|
|
|
_textAlign = align;
|
|
_modified = true;
|
|
}
|
|
return true;
|
|
case kTheTextFont:
|
|
setTextFont(d.asString());
|
|
return true;
|
|
case kTheTextHeight:
|
|
_lineSpacing = d.asInt();
|
|
_modified = true;
|
|
return false;
|
|
case kTheTextSize:
|
|
setTextSize(d.asInt());
|
|
return true;
|
|
case kTheTextStyle:
|
|
setTextStyle(d.asString());
|
|
return true;
|
|
case kTheAutoTab:
|
|
warning("STUB: TextCastMember::setField(): autoTab not implemented");
|
|
return false;
|
|
case kTheBorder:
|
|
_borderSize = d.asInt();
|
|
setModified(true);
|
|
return true;
|
|
case kTheBoxDropShadow:
|
|
warning("STUB: TextCastMember::setField(): boxDropShadow not implemented");
|
|
return false;
|
|
case kTheBoxType:
|
|
warning("STUB: TextCastMember::setField(): boxType not implemented");
|
|
return false;
|
|
case kTheEditable:
|
|
_editable = d.asInt();
|
|
setModified(true);
|
|
return true;
|
|
case kTheLineCount:
|
|
warning("BUILDBOT: TextCastMember::setField(): Attempt to set read-only field %s of cast %d", g_lingo->entity2str(field), _castId);
|
|
return false;
|
|
case kTheMargin:
|
|
warning("STUB: TextCastMember::setField(): margin not implemented");
|
|
return false;
|
|
case kThePageHeight:
|
|
warning("BUILDBOT: TextCastMember::setField(): Attempt to set read-only field %s of cast %d", g_lingo->entity2str(field), _castId);
|
|
return false;
|
|
case kTheScrollTop:
|
|
_scroll = d.asInt();
|
|
setModified(true);
|
|
return true;
|
|
case kTheWordWrap:
|
|
warning("STUB: TextCastMember::setField(): wordWrap not implemented");
|
|
return false;
|
|
case kTheButtonType:
|
|
if (d.type == SYMBOL) {
|
|
if (d.u.s->equalsIgnoreCase("pushButton")) {
|
|
_buttonType = kTypeButton;
|
|
setModified(true);
|
|
return true;
|
|
} else if (d.u.s->equalsIgnoreCase("radioButton")) {
|
|
_buttonType = kTypeRadio;
|
|
setModified(true);
|
|
return true;
|
|
} else if (d.u.s->equalsIgnoreCase("checkBox")) {
|
|
_buttonType = kTypeCheckBox;
|
|
setModified(true);
|
|
return true;
|
|
}
|
|
}
|
|
warning("TextCastMember: invalid button type %s", d.asString(true).c_str());
|
|
return false;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return CastMember::setField(field, d);
|
|
}
|
|
|
|
// This isn't documented particularly well by the Lingo Dictionary;
|
|
// as well as letting you read/write properties on the cast member,
|
|
// Director allows you to read/write some properties to a subset of the text
|
|
// within the cast member defined by a chunk expression, e.g.:
|
|
//
|
|
// set the textStyle of char 2 to 4 of field "Pudge" to "bold"
|
|
|
|
bool TextCastMember::hasChunkField(int field) {
|
|
switch (field) {
|
|
case kTheForeColor:
|
|
case kTheTextFont:
|
|
case kTheTextHeight:
|
|
case kTheTextSize:
|
|
case kTheTextStyle:
|
|
return true;
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Datum TextCastMember::getChunkField(int field, int start, int end) {
|
|
Datum d;
|
|
|
|
switch (field) {
|
|
case kTheForeColor:
|
|
d = (int)getForeColor(start, end);
|
|
break;
|
|
case kTheTextFont:
|
|
d = getTextFont(start, end);
|
|
break;
|
|
case kTheTextHeight:
|
|
warning("TextCastMember::getChunkField(): getting text height(line spacing) is not implemented yet, returning the default one");
|
|
d = (int)_lineSpacing;
|
|
break;
|
|
case kTheTextSize:
|
|
d = getTextSize(start, end);
|
|
break;
|
|
case kTheTextStyle:
|
|
d = getTextStyle(start, end);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return d;
|
|
}
|
|
|
|
bool TextCastMember::setChunkField(int field, int start, int end, const Datum &d) {
|
|
|
|
switch (field) {
|
|
case kTheForeColor:
|
|
setForeColor(d.asInt(), start, end);
|
|
return true;
|
|
case kTheTextFont:
|
|
setTextFont(d.asString(), start, end);
|
|
return true;
|
|
case kTheTextHeight:
|
|
warning("TextCastMember::setChunkField(): setting text height(line spacing) is not implemented yet");
|
|
return false;
|
|
case kTheTextSize:
|
|
setTextSize(d.asInt(), start, end);
|
|
return true;
|
|
case kTheTextStyle:
|
|
setTextStyle(d.asString(), start, end);
|
|
return true;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
}
|