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/algorithm.h"
24 #include "common/textconsole.h"
25 
26 #include "buried/buried.h"
27 #include "buried/graphics.h"
28 #include "buried/message.h"
29 #include "buried/window.h"
30 
31 namespace Buried {
32 
33 const Window *kWindowPosTop = (const Window *)nullptr;
34 const Window *kWindowPosTopMost = (const Window *)-1;
35 
Window(BuriedEngine * vm,Window * parent,bool visible)36 Window::Window(BuriedEngine *vm, Window *parent, bool visible) : _vm(vm), _parent(parent), _visible(visible) {
37 	_enabled = true;
38 
39 	// Add us to the bottom of the parent's window list
40 	if (_parent)
41 		_parent->_children.push_front(this);
42 }
43 
~Window()44 Window::~Window() {
45 	// Remove us from any of the parent's window lists
46 	if (_parent) {
47 		_parent->_children.remove(this);
48 		_parent->_topMostChildren.remove(this);
49 	}
50 
51 	// Remove any of our messages from the queue
52 	_vm->removeAllMessages(this);
53 
54 	// ...and any timers
55 	_vm->removeAllTimers(this);
56 
57 	// Make sure we're not the focused window
58 	if (_vm->_focusedWindow == this)
59 		_vm->_focusedWindow = nullptr;
60 
61 	// And also not captured
62 	if (_vm->_captureWindow == this)
63 		_vm->_captureWindow = nullptr;
64 
65 	// Invalidate this window's rect as well
66 	_vm->_gfx->invalidateRect(getAbsoluteRect());
67 }
68 
invalidateRect(const Common::Rect & rect,bool erase)69 void Window::invalidateRect(const Common::Rect &rect, bool erase) {
70 	_vm->_gfx->invalidateRect(makeAbsoluteRect(rect), erase);
71 }
72 
getClientRect() const73 Common::Rect Window::getClientRect() const {
74 	return Common::Rect(_rect.width(), _rect.height());
75 }
76 
getAbsoluteRect() const77 Common::Rect Window::getAbsoluteRect() const {
78 	return makeAbsoluteRect(_rect);
79 }
80 
sendMessage(Message * message)81 void Window::sendMessage(Message *message) {
82 	switch (message->getMessageType()) {
83 	case kMessageTypeKeyUp:
84 		onKeyUp(((KeyUpMessage *)message)->getKeyState(), ((KeyUpMessage *)message)->getFlags());
85 		break;
86 	case kMessageTypeKeyDown:
87 		onKeyDown(((KeyDownMessage *)message)->getKeyState(), ((KeyDownMessage *)message)->getFlags());
88 		break;
89 	case kMessageTypeTimer:
90 		onTimer(((TimerMessage *)message)->getTimer());
91 		break;
92 	case kMessageTypeMouseMove:
93 		onMouseMove(((MouseMoveMessage *)message)->getPoint(), ((MouseMoveMessage *)message)->getFlags());
94 		break;
95 	case kMessageTypeLButtonUp:
96 		onLButtonUp(((LButtonUpMessage *)message)->getPoint(), ((LButtonUpMessage *)message)->getFlags());
97 		break;
98 	case kMessageTypeLButtonDown:
99 		onLButtonDown(((LButtonDownMessage *)message)->getPoint(), ((LButtonDownMessage *)message)->getFlags());
100 		break;
101 	case kMessageTypeMButtonUp:
102 		onMButtonUp(((MButtonUpMessage *)message)->getPoint(), ((MButtonUpMessage *)message)->getFlags());
103 		break;
104 	case kMessageTypeRButtonUp:
105 		onRButtonUp(((RButtonUpMessage *)message)->getPoint(), ((RButtonUpMessage *)message)->getFlags());
106 		break;
107 	case kMessageTypeRButtonDown:
108 		onRButtonDown(((RButtonDownMessage *)message)->getPoint(), ((RButtonDownMessage *)message)->getFlags());
109 		break;
110 	case kMessageTypeSetCursor:
111 		onSetCursor(((SetCursorMessage *)message)->getMessage());
112 		break;
113 	case kMessageTypeEnable:
114 		onEnable(((EnableMessage *)message)->getEnable());
115 		break;
116 	default:
117 		error("Unknown message type %d", message->getMessageType());
118 	}
119 
120 	delete message;
121 }
122 
postMessage(Message * message)123 void Window::postMessage(Message *message) {
124 	// Simple wrapper
125 	_vm->postMessageToWindow(this, message);
126 }
127 
updateWindow()128 void Window::updateWindow() {
129 	// If we're not visible, ignore
130 	if (!isWindowVisible())
131 		return;
132 
133 	// If we're not in the dirty rect, ignore
134 	if (!_vm->_gfx->getDirtyRect().intersects(getAbsoluteRect()))
135 		return;
136 
137 	// If we need to erase, erase first
138 	if (_vm->_gfx->needsErase())
139 		onEraseBackground();
140 
141 	// Always draw this window first
142 	onPaint();
143 
144 	// Draw children
145 	for (WindowList::iterator it = _children.begin(); it != _children.end(); ++it)
146 		(*it)->updateWindow();
147 
148 	// Draw top-most children
149 	for (WindowList::iterator it = _topMostChildren.begin(); it != _topMostChildren.end(); ++it)
150 		(*it)->updateWindow();
151 }
152 
setWindowPos(const Window * insertAfter,int x,int y,int width,int height,uint flags)153 void Window::setWindowPos(const Window *insertAfter, int x, int y, int width, int height, uint flags) {
154 	if (!(flags & kWindowPosNoZOrder)) {
155 		assert(insertAfter != this); // I don't even want to think about this case
156 
157 		_parent->_children.remove(this);
158 		_parent->_topMostChildren.remove(this);
159 
160 		if (insertAfter == kWindowPosTop) {
161 			// Reposition the window to the top
162 			_parent->_children.push_back(this);
163 		} else if (insertAfter == kWindowPosTopMost) {
164 			// Reposition the window to the top of the top-most
165 			_parent->_topMostChildren.push_back(this);
166 		} else {
167 			// Reposition the window to after insertAfter
168 			WindowList::iterator it = Common::find(_parent->_children.begin(), _parent->_children.end(), insertAfter);
169 
170 			if (it == _parent->_children.end()) {
171 				it = Common::find(_parent->_topMostChildren.begin(), _parent->_topMostChildren.end(), insertAfter);
172 
173 				// It has to be in one of the lists
174 				assert(it != _parent->_topMostChildren.end());
175 
176 				_parent->_topMostChildren.insert(it, this);
177 			} else {
178 				_parent->_children.insert(it, this);
179 			}
180 		}
181 	}
182 
183 	if (flags & kWindowPosShowWindow) {
184 		assert(!(flags & kWindowPosHideWindow));
185 		showWindow(kWindowShow);
186 	} else if (flags & kWindowPosHideWindow) {
187 		assert(!(flags & kWindowPosShowWindow));
188 		showWindow(kWindowHide);
189 	}
190 
191 	if (!(flags & kWindowPosNoActivate)) {
192 		// TODO: Activate the window
193 	}
194 
195 	if (!(flags & kWindowPosNoMove))
196 		_rect.moveTo(x, y);
197 
198 	if (!(flags & kWindowPosNoSize)) {
199 		_rect.right = _rect.left + width;
200 		_rect.bottom = _rect.top + height;
201 	}
202 }
203 
showWindow(WindowShowMode showMode)204 void Window::showWindow(WindowShowMode showMode) {
205 	bool newVisibility = (showMode != kWindowHide);
206 
207 	if (_visible != newVisibility) {
208 		invalidateWindow();
209 		_visible = newVisibility;
210 	}
211 
212 	if (showMode == kWindowShowNormal) {
213 		// TODO: Activate
214 	}
215 }
216 
enableWindow(bool enable)217 void Window::enableWindow(bool enable) {
218 	if (_enabled != enable) {
219 		_enabled = enable;
220 		postMessage(new EnableMessage(enable));
221 	}
222 }
223 
isWindowEnabled() const224 bool Window::isWindowEnabled() const {
225 	if (_parent && !_parent->isWindowEnabled())
226 		return false;
227 
228 	return _enabled && _visible;
229 }
230 
setTimer(uint elapse)231 uint Window::setTimer(uint elapse) {
232 	return _vm->createTimer(this, elapse);
233 }
234 
killTimer(uint timer)235 bool Window::killTimer(uint timer) {
236 	return _vm->killTimer(timer);
237 }
238 
makeAbsoluteRect(const Common::Rect & rect) const239 Common::Rect Window::makeAbsoluteRect(const Common::Rect &rect) const {
240 	// No parent; it's already absolute
241 	if (!_parent)
242 		return rect;
243 
244 	Common::Rect parentRect = _parent->getAbsoluteRect();
245 	Common::Rect absoluteRect = rect;
246 	absoluteRect.translate(parentRect.left, parentRect.top);
247 	absoluteRect.right = MIN(parentRect.right, absoluteRect.right);
248 	absoluteRect.bottom = MIN(parentRect.bottom, absoluteRect.bottom);
249 	return absoluteRect;
250 }
251 
setFocus()252 Window *Window::setFocus() {
253 	// Don't allow focus to be acquired if the window is disabled
254 	if (!isWindowEnabled())
255 		return nullptr;
256 
257 	Window *oldWindow = nullptr;
258 
259 	// Notify the old window we just took its focus
260 	if (_vm->_focusedWindow) {
261 		_vm->_focusedWindow->onKillFocus(this);
262 		oldWindow = _vm->_focusedWindow;
263 	}
264 
265 	_vm->_focusedWindow = this;
266 	onSetFocus(oldWindow);
267 	return oldWindow;
268 }
269 
childWindowAtPoint(const Common::Point & point)270 Window *Window::childWindowAtPoint(const Common::Point &point) {
271 	for (WindowList::iterator it = _topMostChildren.reverse_begin(); it != _topMostChildren.end(); --it)
272 		if ((*it)->getAbsoluteRect().contains(point) && (*it)->isWindowEnabled())
273 			return (*it)->childWindowAtPoint(point);
274 
275 	for (WindowList::iterator it = _children.reverse_begin(); it != _children.end(); --it)
276 		if ((*it)->getAbsoluteRect().contains(point) && (*it)->isWindowEnabled())
277 			return (*it)->childWindowAtPoint(point);
278 
279 	return this;
280 }
281 
setCapture()282 Window *Window::setCapture() {
283 	Window *oldCapturedWindow = _vm->_captureWindow;
284 	_vm->_captureWindow = this;
285 	return oldCapturedWindow;
286 }
287 
convertPointToGlobal(const Common::Point & point)288 Common::Point Window::convertPointToGlobal(const Common::Point &point) {
289 	Common::Rect absoluteRect = getAbsoluteRect();
290 	return Common::Point(point.x + absoluteRect.left, point.y + absoluteRect.top);
291 }
292 
convertPointToLocal(const Common::Point & point)293 Common::Point Window::convertPointToLocal(const Common::Point &point) {
294 	Common::Rect absoluteRect = getAbsoluteRect();
295 	return Common::Point(point.x - absoluteRect.left, point.y - absoluteRect.top);
296 }
297 
convertPointToWindow(const Common::Point & point,Window * dest)298 Common::Point Window::convertPointToWindow(const Common::Point &point, Window *dest) {
299 	return dest->convertPointToLocal(convertPointToGlobal(point));
300 }
301 
onSetCursor(uint message)302 bool Window::onSetCursor(uint message) {
303 	// The default implementation is to try the parent first
304 	if (_parent && _parent->onSetCursor(message))
305 		return true;
306 
307 	// Then otherwise default to the arrow
308 	_vm->_gfx->setCursor(kCursorArrow);
309 	return false;
310 }
311 
312 } // End of namespace Buried
313