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