/* 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 2 * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "common/timer.h" #include "common/system.h" #include "graphics/macgui/macwindowmanager.h" #include "graphics/macgui/macfontmanager.h" #include "graphics/macgui/mactextwindow.h" #include "graphics/macgui/macmenu.h" namespace Graphics { enum { kConWOverlap = 20, kConHOverlap = 20, kConWPadding = 3, kConHPadding = 4, kConOverscan = 3, kConScrollStep = 12, kCursorHeight = 12 }; static void cursorTimerHandler(void *refCon); MacTextWindow::MacTextWindow(MacWindowManager *wm, const MacFont *font, int fgcolor, int bgcolor, int maxWidth, TextAlign textAlignment, MacMenu *menu) : MacWindow(wm->getLastId(), true, true, true, wm) { _font = font; _menu = menu; _mactext = new MacText("", _wm, font, fgcolor, bgcolor, maxWidth, textAlignment); _fontRef = wm->_fontMan->getFont(*font); _inputTextHeight = 0; _maxWidth = maxWidth; _inputIsDirty = true; _inTextSelection = false; _scrollPos = 0; _cursorX = 0; _cursorY = 0; _cursorState = false; _cursorOff = false; _cursorDirty = true; _cursorRect = new Common::Rect(0, 0, 1, kCursorHeight); _cursorSurface = new ManagedSurface(1, kCursorHeight); _cursorSurface->fillRect(*_cursorRect, kColorBlack); g_system->getTimerManager()->installTimerProc(&cursorTimerHandler, 200000, this, "textWindowCursor"); } void MacTextWindow::resize(int w, int h) { if (_surface.w == w && _surface.h == h) return; undrawInput(); MacWindow::resize(w, h); _maxWidth = getInnerDimensions().width(); _mactext->setMaxWidth(_maxWidth); } void MacTextWindow::appendText(Common::String str, const MacFont *macFont, bool skipAdd) { _mactext->appendText(str, macFont->getId(), macFont->getSize(), macFont->getSlant(), skipAdd); _contentIsDirty = true; _scrollPos = MAX(0, _mactext->getTextHeight() - getInnerDimensions().height()); updateCursorPos(); } void MacTextWindow::clearText() { _mactext->clearText(); _contentIsDirty = true; _borderIsDirty = true; updateCursorPos(); } MacTextWindow::~MacTextWindow() { delete _cursorSurface; g_system->getTimerManager()->removeTimerProc(&cursorTimerHandler); } void MacTextWindow::setTextWindowFont(const MacFont *font) { _font = font; _fontRef = _wm->_fontMan->getFont(*font); _mactext->setDefaultFormatting(font->getId(), font->getSlant(), font->getSize(), 0, 0, 0); } const MacFont *MacTextWindow::getTextWindowFont() { return _font; } bool MacTextWindow::draw(ManagedSurface *g, bool forceRedraw) { if (!_borderIsDirty && !_contentIsDirty && !_cursorDirty && !_inputIsDirty && !forceRedraw) return false; if (_borderIsDirty || forceRedraw) { drawBorder(); _composeSurface.clear(kColorWhite); } if (_inputIsDirty || forceRedraw) { drawInput(); _inputIsDirty = false; } _contentIsDirty = false; // Compose _mactext->draw(&_composeSurface, 0, _scrollPos, _surface.w - 2, _scrollPos + _surface.h - 2, kConWOverlap - 2, kConWOverlap - 2); if (_cursorState) _composeSurface.blitFrom(*_cursorSurface, *_cursorRect, Common::Point(_cursorX + kConWOverlap - 2, _cursorY + kConHOverlap - 2)); if (_selectedText.endY != -1) drawSelection(); _composeSurface.transBlitFrom(_borderSurface, kColorGreen); g->transBlitFrom(_composeSurface, _composeSurface.getBounds(), Common::Point(_dims.left - 2, _dims.top - 2), kColorGreen2); return true; } void MacTextWindow::drawSelection() { if (_selectedText.endY == -1) return; SelectedText s = _selectedText; if (s.startY > s.endY || (s.startY == s.endY && s.startX > s.endX)) { SWAP(s.startX, s.endX); SWAP(s.startY, s.endY); SWAP(s.startRow, s.endRow); SWAP(s.startCol, s.endCol); } int lastLineStart = s.endY; s.endY += _mactext->getLineHeight(s.endRow); int start = s.startY - _scrollPos; start = MAX(0, start); if (start > getInnerDimensions().height()) return; int end = s.endY - _scrollPos; if (end < 0) return; end = MIN((int)getInnerDimensions().height(), end); int numLines = 0; int x1, x2; for (int y = start; y < end; y++) { if (!numLines) { x1 = 0; x2 = getInnerDimensions().width() - 1; if (y + _scrollPos == s.startY && s.startX > 0) { numLines = _mactext->getLineHeight(s.startRow); x1 = s.startX; } if (y + _scrollPos >= lastLineStart) { numLines = _mactext->getLineHeight(s.endRow); x2 = s.endX; } } else { numLines--; } byte *ptr = (byte *)_composeSurface.getBasePtr(x1 + kConWOverlap - 2, y + kConWOverlap - 2); for (int x = x1; x < x2; x++, ptr++) if (*ptr == kColorBlack) *ptr = kColorWhite; else *ptr = kColorBlack; } } Common::String MacTextWindow::getSelection(bool formatted, bool newlines) { if (_selectedText.endY == -1) return Common::String(""); SelectedText s = _selectedText; if (s.startY > s.endY || (s.startY == s.endY && s.startX > s.endX)) { SWAP(s.startRow, s.endRow); SWAP(s.startCol, s.endCol); } return _mactext->getTextChunk(s.startRow, s.startCol, s.endRow, s.endCol, formatted, newlines); } void MacTextWindow::clearSelection() { _selectedText.endY = _selectedText.startY = -1; } bool MacTextWindow::processEvent(Common::Event &event) { WindowClick click = isInBorder(event.mouse.x, event.mouse.y); if (event.type == Common::EVENT_KEYDOWN) { _wm->setActive(getId()); switch (event.kbd.keycode) { case Common::KEYCODE_BACKSPACE: if (!_inputText.empty()) { _inputText.deleteLastChar(); _inputIsDirty = true; } return true; case Common::KEYCODE_RETURN: undrawInput(); return false; // Pass it to the higher level for processing default: if (event.kbd.ascii == '~') return false; if (event.kbd.ascii >= 0x20 && event.kbd.ascii <= 0x7f) { _inputText += (char)event.kbd.ascii; _inputIsDirty = true; return true; } break; } } if (click == kBorderScrollUp || click == kBorderScrollDown) { if (event.type == Common::EVENT_LBUTTONDOWN) { int consoleHeight = getInnerDimensions().height(); int textFullSize = _mactext->getTextHeight(); float scrollPos = (float)_scrollPos / textFullSize; float scrollSize = (float)consoleHeight / textFullSize; setScroll(scrollPos, scrollSize); return true; } else if (event.type == Common::EVENT_LBUTTONUP) { int oldScrollPos = _scrollPos; switch (click) { case kBorderScrollUp: _scrollPos = MAX(0, _scrollPos - kConScrollStep); undrawCursor(); _cursorY -= (_scrollPos - oldScrollPos); _contentIsDirty = true; _borderIsDirty = true; break; case kBorderScrollDown: _scrollPos = MIN(_mactext->getTextHeight() - kConScrollStep, _scrollPos + kConScrollStep); undrawCursor(); _cursorY -= (_scrollPos - oldScrollPos); _contentIsDirty = true; _borderIsDirty = true; break; default: return false; } return true; } return false; } if (click == kBorderInner) { if (event.type == Common::EVENT_LBUTTONDOWN) { startMarking(event.mouse.x, event.mouse.y); return true; } else if (event.type == Common::EVENT_LBUTTONUP) { if (_inTextSelection) { _inTextSelection = false; if (_selectedText.endY == -1 || (_selectedText.endX == _selectedText.startX && _selectedText.endY == _selectedText.startY)) { _selectedText.startY = _selectedText.endY = -1; _contentIsDirty = true; _menu->enableCommand("Edit", "Copy", false); } else { _menu->enableCommand("Edit", "Copy", true); bool cutAllowed = false; if (_selectedText.startRow >= _mactext->getLineCount() - _inputTextHeight && _selectedText.endRow >= _mactext->getLineCount() - _inputTextHeight) cutAllowed = true; _menu->enableCommand("Edit", "Cut", cutAllowed); _menu->enableCommand("Edit", "Clear", cutAllowed); } } return true; } else if (event.type == Common::EVENT_MOUSEMOVE) { if (_inTextSelection) { updateTextSelection(event.mouse.x, event.mouse.y); return true; } } return false; } return MacWindow::processEvent(event); } void MacTextWindow::startMarking(int x, int y) { x -= getInnerDimensions().left - 2; y -= getInnerDimensions().top; y += _scrollPos; _mactext->getRowCol(x, y, &_selectedText.startX, &_selectedText.startY, &_selectedText.startRow, &_selectedText.startCol); _selectedText.endY = -1; _inTextSelection = true; } void MacTextWindow::updateTextSelection(int x, int y) { x -= getInnerDimensions().left - 2; y -= getInnerDimensions().top; y += _scrollPos; _mactext->getRowCol(x, y, &_selectedText.endX, &_selectedText.endY, &_selectedText.endRow, &_selectedText.endCol); debug(3, "s: %d,%d (%d, %d) e: %d,%d (%d, %d)", _selectedText.startX, _selectedText.startY, _selectedText.startRow, _selectedText.startCol, _selectedText.endX, _selectedText.endY, _selectedText.endRow, _selectedText.endCol); _contentIsDirty = true; } void MacTextWindow::undrawInput() { for (uint i = 0; i < _inputTextHeight; i++) _mactext->removeLastLine(); if (_inputTextHeight) appendText("\n", _font, true); _inputTextHeight = 0; } void MacTextWindow::drawInput() { undrawInput(); Common::Array text; // Now recalc new text height _fontRef->wordWrapText(_inputText, _maxWidth, text); _inputTextHeight = MAX(1u, text.size()); // We always have line to clean // And add new input line to the text appendText(_inputText, _font, true); _cursorX = _inputText.empty() ? 0 : _fontRef->getStringWidth(text[_inputTextHeight - 1]); updateCursorPos(); _contentIsDirty = true; } void MacTextWindow::clearInput() { undrawCursor(); _cursorX = 0; _inputText.clear(); } void MacTextWindow::appendInput(Common::String str) { _inputText += str; drawInput(); } ////////////////// // Cursor stuff static void cursorTimerHandler(void *refCon) { MacTextWindow *w = (MacTextWindow *)refCon; if (!w->_cursorOff) w->_cursorState = !w->_cursorState; w->_cursorDirty = true; } void MacTextWindow::updateCursorPos() { _cursorY = _mactext->getTextHeight() - _scrollPos - kCursorHeight; _cursorDirty = true; } void MacTextWindow::undrawCursor() { _cursorState = false; _cursorDirty = true; } } // End of namespace Graphics