1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "common/scummsys.h"
24
25 #include "zvision/scripting/controls/input_control.h"
26 #include "zvision/graphics/cursors/cursor_manager.h"
27
28 #include "zvision/zvision.h"
29 #include "zvision/scripting/script_manager.h"
30 #include "zvision/text/string_manager.h"
31 #include "zvision/graphics/render_manager.h"
32
33 #include "common/str.h"
34 #include "common/stream.h"
35 #include "common/rect.h"
36 #include "video/video_decoder.h"
37
38 namespace ZVision {
39
InputControl(ZVision * engine,uint32 key,Common::SeekableReadStream & stream)40 InputControl::InputControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream)
41 : Control(engine, key, CONTROL_INPUT),
42 _background(0),
43 _nextTabstop(0),
44 _focused(false),
45 _textChanged(false),
46 _enterPressed(false),
47 _readOnly(false),
48 _txtWidth(0),
49 _animation(NULL) {
50 // Loop until we find the closing brace
51 Common::String line = stream.readLine();
52 _engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
53 Common::String param;
54 Common::String values;
55 getParams(line, param, values);
56
57 while (!stream.eos() && !line.contains('}')) {
58 if (param.matchString("rectangle", true)) {
59 int x1;
60 int y1;
61 int x2;
62 int y2;
63
64 sscanf(values.c_str(), "%d %d %d %d", &x1, &y1, &x2, &y2);
65
66 _textRectangle = Common::Rect(x1, y1, x2, y2);
67 } else if (param.matchString("aux_hotspot", true)) {
68 int x1;
69 int y1;
70 int x2;
71 int y2;
72
73 sscanf(values.c_str(), "%d %d %d %d", &x1, &y1, &x2, &y2);
74
75 _headerRectangle = Common::Rect(x1, y1, x2, y2);
76 } else if (param.matchString("string_init", true)) {
77 uint fontFormatNumber;
78
79 sscanf(values.c_str(), "%u", &fontFormatNumber);
80
81 _stringInit.readAllStyles(_engine->getStringManager()->getTextLine(fontFormatNumber));
82 } else if (param.matchString("chooser_init_string", true)) {
83 uint fontFormatNumber;
84
85 sscanf(values.c_str(), "%u", &fontFormatNumber);
86
87 _stringChooserInit.readAllStyles(_engine->getStringManager()->getTextLine(fontFormatNumber));
88 } else if (param.matchString("next_tabstop", true)) {
89 sscanf(values.c_str(), "%u", &_nextTabstop);
90 } else if (param.matchString("cursor_dimensions", true)) {
91 // Ignore, use the dimensions in the animation file
92 } else if (param.matchString("cursor_animation_frames", true)) {
93 // Ignore, use the frame count in the animation file
94 } else if (param.matchString("cursor_animation", true)) {
95 char fileName[25];
96
97 sscanf(values.c_str(), "%24s %*u", fileName);
98
99 _animation = _engine->loadAnimation(fileName);
100 _animation->start();
101 } else if (param.matchString("focus", true)) {
102 _focused = true;
103 _engine->getScriptManager()->setFocusControlKey(_key);
104 } else if (param.matchString("venus_id", true)) {
105 _venusId = atoi(values.c_str());
106 }
107
108 line = stream.readLine();
109 _engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
110 getParams(line, param, values);
111 }
112
113 _maxTxtWidth = _textRectangle.width();
114 if (_animation)
115 _maxTxtWidth -= _animation->getWidth();
116 }
117
~InputControl()118 InputControl::~InputControl() {
119 _background->free();
120 delete _background;
121 }
122
onMouseUp(const Common::Point & screenSpacePos,const Common::Point & backgroundImageSpacePos)123 bool InputControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
124 if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
125 return false;
126
127 if (_textRectangle.contains(backgroundImageSpacePos)) {
128 if (!_readOnly) {
129 // Save
130 _engine->getScriptManager()->focusControl(_key);
131 setVenus();
132 } else {
133 // Restore
134 if (_currentInputText.size()) {
135 setVenus();
136 _enterPressed = true;
137 }
138 }
139 }
140 return false;
141 }
142
onMouseMove(const Common::Point & screenSpacePos,const Common::Point & backgroundImageSpacePos)143 bool InputControl::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
144 if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
145 return false;
146
147 if (_textRectangle.contains(backgroundImageSpacePos)) {
148 if (!_readOnly) {
149 // Save
150 _engine->getCursorManager()->changeCursor(CursorIndex_Active);
151 return true;
152 } else {
153 // Restore
154 if (_currentInputText.size()) {
155 _engine->getCursorManager()->changeCursor(CursorIndex_Active);
156 _engine->getScriptManager()->focusControl(_key);
157 return true;
158 }
159 }
160 }
161 return false;
162 }
163
onKeyDown(Common::KeyState keyState)164 bool InputControl::onKeyDown(Common::KeyState keyState) {
165 if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
166 return false;
167
168 if (!_focused) {
169 return false;
170 }
171
172 if (keyState.keycode == Common::KEYCODE_BACKSPACE) {
173 if (!_readOnly) {
174 _currentInputText.deleteLastChar();
175 _textChanged = true;
176 }
177 } else if (keyState.keycode == Common::KEYCODE_RETURN) {
178 _enterPressed = true;
179 } else if (keyState.keycode == Common::KEYCODE_TAB) {
180 unfocus();
181 // Focus the next input control
182 _engine->getScriptManager()->focusControl(_nextTabstop);
183 // Don't process this event for other controls
184 return true;
185 } else {
186 if (!_readOnly) {
187 // Otherwise, append the new character to the end of the current text
188 uint16 asciiValue = keyState.ascii;
189 // We only care about text values
190 if (asciiValue >= 32 && asciiValue <= 126) {
191 _currentInputText += (char)asciiValue;
192 _textChanged = true;
193 }
194 }
195 }
196 return false;
197 }
198
process(uint32 deltaTimeInMillis)199 bool InputControl::process(uint32 deltaTimeInMillis) {
200 if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
201 return false;
202
203 if (!_background) {
204 _background = _engine->getRenderManager()->getBkgRect(_textRectangle);
205 }
206
207 // First see if we need to render the text
208 if (_textChanged) {
209 // Blit the text using the RenderManager
210
211 Graphics::Surface txt;
212 txt.copyFrom(*_background);
213
214 int32 oldTxtWidth = _txtWidth;
215
216 if (!_readOnly || !_focused)
217 _txtWidth = _engine->getTextRenderer()->drawText(_currentInputText, _stringInit, txt);
218 else
219 _txtWidth = _engine->getTextRenderer()->drawText(_currentInputText, _stringChooserInit, txt);
220
221 if (_readOnly || _txtWidth <= _maxTxtWidth)
222 _engine->getRenderManager()->blitSurfaceToBkg(txt, _textRectangle.left, _textRectangle.top);
223 else {
224 // Assume the last character caused the overflow.
225 _currentInputText.deleteLastChar();
226 _txtWidth = oldTxtWidth;
227 }
228
229 txt.free();
230 }
231
232 if (_animation && !_readOnly && _focused) {
233 if (_animation->endOfVideo())
234 _animation->rewind();
235
236 if (_animation->needsUpdate()) {
237 const Graphics::Surface *srf = _animation->decodeNextFrame();
238 int16 xx = _textRectangle.left + _txtWidth;
239 if (xx >= _textRectangle.left + (_textRectangle.width() - (int16)_animation->getWidth()))
240 xx = _textRectangle.left + _textRectangle.width() - (int16)_animation->getWidth();
241 _engine->getRenderManager()->blitSurfaceToBkg(*srf, xx, _textRectangle.top);
242 }
243 }
244
245 _textChanged = false;
246 return false;
247 }
248
enterPress()249 bool InputControl::enterPress() {
250 if (_enterPressed) {
251 _enterPressed = false;
252 return true;
253 }
254 return false;
255 }
256
setText(const Common::String & _str)257 void InputControl::setText(const Common::String &_str) {
258 _currentInputText = _str;
259 _textChanged = true;
260 }
261
getText()262 const Common::String InputControl::getText() {
263 return _currentInputText;
264 }
265
setReadOnly(bool readonly)266 void InputControl::setReadOnly(bool readonly) {
267 _readOnly = readonly;
268 }
269
270 } // End of namespace ZVision
271