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 "ultima/ultima8/gumps/gump.h"
24 #include "ultima/ultima8/graphics/render_surface.h"
25 #include "ultima/ultima8/graphics/shape.h"
26 #include "ultima/ultima8/graphics/shape_frame.h"
27 #include "ultima/ultima8/graphics/shape_archive.h"
28 #include "ultima/ultima8/games/game_data.h"
29 #include "ultima/ultima8/gumps/gump_notify_process.h"
30 #include "ultima/ultima8/kernel/kernel.h"
31 #include "ultima/ultima8/kernel/object_manager.h"
32 #include "ultima/ultima8/ultima8.h"
33 
34 namespace Ultima {
35 namespace Ultima8 {
36 
DEFINE_RUNTIME_CLASSTYPE_CODE(Gump)37 DEFINE_RUNTIME_CLASSTYPE_CODE(Gump)
38 
39 Gump::Gump() : Object(), _parent(nullptr), _owner(0),
40 	_x(0), _y(0), _flags(0), _layer(0), _index(-1),
41 	_shape(nullptr), _frameNum(0), _focusChild(nullptr),
42 	_notifier(0), _processResult(0) {
43 }
44 
Gump(int inX,int inY,int width,int height,uint16 inOwner,uint32 inFlags,int32 inLayer)45 Gump::Gump(int inX, int inY, int width, int height, uint16 inOwner,
46 		   uint32 inFlags, int32 inLayer) :
47 	Object(), _owner(inOwner), _parent(nullptr), _x(inX), _y(inY),
48 	_dims(0, 0, width, height), _flags(inFlags), _layer(inLayer), _index(-1),
49 	_shape(nullptr), _frameNum(0), _children(), _focusChild(nullptr),
50 	_notifier(0), _processResult(0) {
51 	assignObjId(); // gumps always get an objid
52 }
53 
~Gump()54 Gump::~Gump() {
55 	// Get rid of focus
56 	if (_focusChild) _focusChild->OnFocus(false);
57 	_focusChild = nullptr;
58 
59 	// Delete all children
60 	Std::list<Gump *>::iterator it = _children.begin();
61 	Std::list<Gump *>::iterator end = _children.end();
62 
63 	while (it != end) {
64 		Gump *g = *it;
65 		it = _children.erase(it);
66 		delete g;
67 	}
68 }
69 
InitGump(Gump * newparent,bool take_focus)70 void Gump::InitGump(Gump *newparent, bool take_focus) {
71 	if (newparent)
72 		newparent->AddChild(this, take_focus);
73 	else
74 		Ultima8Engine::get_instance()->addGump(this);
75 
76 	if (_owner && !_notifier) CreateNotifier();
77 }
78 
SetShape(FrameID frame,bool adjustsize)79 void Gump::SetShape(FrameID frame, bool adjustsize) {
80 	_shape = GameData::get_instance()->getShape(frame);
81 	_frameNum = frame._frameNum;
82 
83 	if (adjustsize && _shape) {
84 		UpdateDimsFromShape();
85 	}
86 }
87 
UpdateDimsFromShape()88 void Gump::UpdateDimsFromShape() {
89 	const ShapeFrame *sf = _shape->getFrame(_frameNum);
90 	assert(sf);
91 	_dims.left = -sf->_xoff;
92 	_dims.top = -sf->_yoff;
93 	_dims.setWidth(sf->_width);
94 	_dims.setHeight(sf->_height);
95 }
96 
CreateNotifier()97 void Gump::CreateNotifier() {
98 	assert(_notifier == 0);
99 
100 	// Create us a GumpNotifyProcess
101 	GumpNotifyProcess *p = new GumpNotifyProcess(_owner);
102 	p->setGump(this);
103 	_notifier = Kernel::get_instance()->addProcess(p);
104 }
105 
SetNotifyProcess(GumpNotifyProcess * proc)106 void Gump::SetNotifyProcess(GumpNotifyProcess *proc) {
107 	assert(_notifier == 0);
108 	_notifier = proc->getPid();
109 }
110 
GetNotifyProcess()111 GumpNotifyProcess *Gump::GetNotifyProcess() {
112 	return dynamic_cast<GumpNotifyProcess *>(Kernel::get_instance()->
113 	        getProcess(_notifier));
114 }
115 
116 
Close(bool no_del)117 void Gump::Close(bool no_del) {
118 	GumpNotifyProcess *p = GetNotifyProcess();
119 	if (p) {
120 		p->notifyClosing(_processResult);
121 	}
122 	_notifier = 0;
123 
124 	_flags |= FLAG_CLOSING;
125 	if (!_parent) {
126 		if (!no_del)
127 			delete this;
128 	} else {
129 		_parent->ChildNotify(this, Gump::GUMP_CLOSING);
130 		if (!no_del)
131 			_flags |= FLAG_CLOSE_AND_DEL;
132 	}
133 }
134 
RenderSurfaceChanged()135 void Gump::RenderSurfaceChanged() {
136 	// Iterate all children
137 	Std::list<Gump *>::reverse_iterator it = _children.rbegin();
138 	Std::list<Gump *>::reverse_iterator end = _children.rend();
139 
140 	while (it != end) {
141 		(*it)->RenderSurfaceChanged();
142 		++it;
143 	}
144 }
145 
run()146 void Gump::run() {
147 	// Iterate all children
148 	Std::list<Gump *>::iterator it = _children.begin();
149 	Std::list<Gump *>::iterator end = _children.end();
150 
151 	while (it != end) {
152 		Gump *g = *it;
153 
154 		// Run the child if it's not closing
155 		if (!(g->_flags & FLAG_CLOSING))
156 			g->run();
157 
158 		// If closing, we can kill it
159 		if (g->_flags & FLAG_CLOSING) {
160 			it = _children.erase(it);
161 			FindNewFocusChild();
162 			if (g->_flags & FLAG_CLOSE_AND_DEL) delete g;
163 		} else {
164 			++it;
165 		}
166 	}
167 }
168 
CloseItemDependents()169 void Gump::CloseItemDependents() {
170 	// Close it, and return
171 	if (_flags & FLAG_ITEM_DEPENDENT) {
172 		Close();
173 		return;
174 	}
175 
176 	// Pass the MapChanged message to all the children
177 	Std::list<Gump *>::iterator it = _children.begin();
178 	Std::list<Gump *>::iterator end = _children.end();
179 
180 	while (it != end) {
181 		Gump *g = *it;
182 
183 		// Pass to child if it's not closing
184 		if (!(g->_flags & FLAG_CLOSING)) g->CloseItemDependents();
185 
186 		// If closing, we can kill it
187 		if (g->_flags & FLAG_CLOSING) {
188 			it = _children.erase(it);
189 			FindNewFocusChild();
190 			if (g->_flags & FLAG_CLOSE_AND_DEL) delete g;
191 		} else {
192 			++it;
193 		}
194 	}
195 }
196 
GetMouseCursor(int32 mx,int32 my,Shape & shape,int32 & frame)197 bool Gump::GetMouseCursor(int32 mx, int32 my, Shape &shape, int32 &frame) {
198 	ParentToGump(mx, my);
199 
200 	bool ret = false;
201 
202 	// This reverse iterates the children
203 	Std::list<Gump *>::reverse_iterator it;
204 	for (it = _children.rbegin(); it != _children.rend(); ++it)
205 	{
206 		Gump *g = *it;
207 
208 		// Not if closing
209 		if (g->_flags & FLAG_CLOSING) continue;
210 
211 		// It's got the point
212 		if (g->PointOnGump(mx, my))
213 			ret = g->GetMouseCursor(mx, my, shape, frame);
214 
215 		if (ret) break;
216 	}
217 
218 	return ret;
219 }
220 
Paint(RenderSurface * surf,int32 lerp_factor,bool scaled)221 void Gump::Paint(RenderSurface *surf, int32 lerp_factor, bool scaled) {
222 	// Don't paint if hidden
223 	if (IsHidden()) return;
224 
225 	// Get old Origin
226 	int32 ox = 0, oy = 0;
227 	surf->GetOrigin(ox, oy);
228 
229 	// Set the new Origin
230 	int32 nx = 0, ny = 0;
231 	GumpToParent(nx, ny);
232 	surf->SetOrigin(ox + nx, oy + ny);
233 
234 	// Get Old Clipping Rect
235 	Rect old_rect;
236 	surf->GetClippingRect(old_rect);
237 
238 	// Set new clipping rect
239 	Rect new_rect = _dims;
240 	new_rect.clip(old_rect);
241 	surf->SetClippingRect(new_rect);
242 
243 	// Paint This
244 	PaintThis(surf, lerp_factor, scaled);
245 
246 	// Paint children
247 	PaintChildren(surf, lerp_factor, scaled);
248 
249 	// Reset The Clipping Rect
250 	surf->SetClippingRect(old_rect);
251 
252 	// Reset The Origin
253 	surf->SetOrigin(ox, oy);
254 }
255 
PaintThis(RenderSurface * surf,int32,bool)256 void Gump::PaintThis(RenderSurface *surf, int32 /*lerp_factor*/, bool /*scaled*/) {
257 	if (_shape)
258 		surf->Paint(_shape, _frameNum, 0, 0);
259 }
260 
PaintChildren(RenderSurface * surf,int32 lerp_factor,bool scaled)261 void Gump::PaintChildren(RenderSurface *surf, int32 lerp_factor, bool scaled) {
262 	// Iterate all children
263 	Std::list<Gump *>::iterator it = _children.begin();
264 	Std::list<Gump *>::iterator end = _children.end();
265 
266 	while (it != end) {
267 		Gump *g = *it;
268 		// Paint if not closing
269 		if (!(g->_flags & FLAG_CLOSING))
270 			g->Paint(surf, lerp_factor, scaled);
271 
272 		++it;
273 	}
274 }
275 
PaintCompositing(RenderSurface * surf,int32 lerp_factor,int32 sx,int32 sy)276 void Gump::PaintCompositing(RenderSurface *surf, int32 lerp_factor,
277 							int32 sx, int32 sy) {
278 	// Don't paint if hidden
279 	if (IsHidden()) return;
280 
281 	// Get old Origin
282 	int32 ox = 0, oy = 0;
283 	surf->GetOrigin(ox, oy);
284 
285 	// FIXME - Big accuracy problems here with the origin and clipping rect
286 
287 	// Set the new Origin
288 	surf->SetOrigin(0, 0);
289 
290 	// Get Old Clipping Rect
291 	Rect old_rect;
292 	surf->GetClippingRect(old_rect);
293 
294 	// Set new clipping rect
295 	Rect new_rect(_dims);
296 	GumpRectToScreenSpace(new_rect, ROUND_OUTSIDE);
297 	new_rect.clip(old_rect);
298 	surf->SetClippingRect(new_rect);
299 
300 	// Iterate all children
301 	Std::list<Gump *>::reverse_iterator it = _children.rbegin();
302 	Std::list<Gump *>::reverse_iterator end = _children.rend();
303 
304 	while (it != end) {
305 		Gump *g = *it;
306 		// Paint if not closing
307 		if (!g->IsClosing())
308 			g->PaintCompositing(surf, lerp_factor, sx, sy);
309 
310 		++it;
311 	}
312 
313 	// Paint This
314 	PaintComposited(surf, lerp_factor, sx, sy);
315 
316 	// Reset The Clipping Rect
317 	surf->SetClippingRect(old_rect);
318 
319 	// Reset The Origin
320 	surf->SetOrigin(ox, oy);
321 }
322 
PaintComposited(RenderSurface *,int32,int32,int32)323 void Gump::PaintComposited(RenderSurface * /*surf*/, int32 /*lerp_factor*/, int32 /*scalex*/, int32 /*scaley*/) {
324 }
325 
FindGump(int mx,int my)326 Gump *Gump::FindGump(int mx, int my) {
327 	int32 gx = mx, gy = my;
328 	ParentToGump(gx, gy);
329 	Gump *gump = nullptr;
330 
331 	// Iterate all children
332 	Std::list<Gump *>::reverse_iterator it = _children.rbegin();
333 	Std::list<Gump *>::reverse_iterator end = _children.rend();
334 
335 	while (it != end && !gump) {
336 		Gump *g = *it;
337 		gump = g->FindGump(gx, gy);
338 		++it;
339 	}
340 
341 	// it's over a child
342 	if (gump)
343 		return gump;
344 
345 	// it's over this gump
346 	if (PointOnGump(mx, my))
347 		return this;
348 
349 	return nullptr;
350 }
351 
setRelativePosition(Gump::Position pos,int xoffset,int yoffset)352 void Gump::setRelativePosition(Gump::Position pos, int xoffset, int yoffset) {
353 	if (_parent) {
354 		Rect rect;
355 		_parent->GetDims(rect);
356 
357 		switch (pos) {
358 		case CENTER:
359 			Move(rect.width() / 2 - _dims.width() / 2 + xoffset,
360 			     rect.height() / 2 - _dims.height() / 2 + yoffset);
361 			break;
362 		case TOP_LEFT:
363 			Move(xoffset, yoffset);
364 			break;
365 		case TOP_RIGHT:
366 			Move(rect.width() - _dims.width() + xoffset, yoffset);
367 			break;
368 		case BOTTOM_LEFT:
369 			Move(xoffset, rect.height() - _dims.height() + yoffset);
370 			break;
371 		case BOTTOM_RIGHT:
372 			Move(rect.width() - _dims.width() + xoffset, rect.height() - _dims.height() + yoffset);
373 			break;
374 		case TOP_CENTER:
375 			Move(rect.width() / 2 - _dims.width() / 2 + xoffset, yoffset);
376 			break;
377 		case BOTTOM_CENTER:
378 			Move(rect.width() / 2 - _dims.width() / 2 + xoffset, rect.height() - _dims.height() + yoffset);
379 			break;
380 		default:
381 			break;
382 		}
383 	}
384 }
385 
PointOnGump(int mx,int my)386 bool Gump::PointOnGump(int mx, int my) {
387 	int32 gx = mx, gy = my;
388 	ParentToGump(gx, gy);
389 
390 	// First check again rectangle
391 	if (!_dims.contains(gx, gy)) {
392 		return false;
393 	}
394 
395 	if (!_shape) {
396 		// no shape? Then if it's in the rectangle it's on the gump.
397 		return true;
398 	}
399 
400 	const ShapeFrame *sf = _shape->getFrame(_frameNum);
401 	assert(sf);
402 	if (sf->hasPoint(gx, gy)) {
403 		return true;
404 	}
405 
406 	// reverse-iterate children
407 	Std::list<Gump *>::reverse_iterator it;
408 	for (it = _children.rbegin(); it != _children.rend(); ++it) {
409 		Gump *g = *it;
410 
411 		// It's got the point
412 		if (g->PointOnGump(gx, gy)) return true;
413 	}
414 
415 	return false;
416 }
417 
418 // Convert a screen space point to a gump point
ScreenSpaceToGump(int32 & sx,int32 & sy,PointRoundDir r)419 void Gump::ScreenSpaceToGump(int32 &sx, int32 &sy, PointRoundDir r) {
420 	// This is a recursive operation. We get each
421 	// parent to convert the point into their local
422 	// coords.
423 	if (_parent) _parent->ScreenSpaceToGump(sx, sy, r);
424 
425 	ParentToGump(sx, sy, r);
426 }
427 
428 // Convert a gump point to a screen space point
GumpToScreenSpace(int32 & gx,int32 & gy,PointRoundDir r)429 void Gump::GumpToScreenSpace(int32 &gx, int32 &gy, PointRoundDir r) {
430 	// This is a recursive operation. We get each
431 	// gump to convert the point to their parent
432 
433 	GumpToParent(gx, gy, r);
434 
435 	if (_parent) _parent->GumpToScreenSpace(gx, gy, r);
436 }
437 
438 // Convert a parent relative point to a gump point
ParentToGump(int32 & px,int32 & py,PointRoundDir)439 void Gump::ParentToGump(int32 &px, int32 &py, PointRoundDir) {
440 	px -= _x;
441 	px += _dims.left;
442 	py -= _y;
443 	py += _dims.top;
444 }
445 
446 // Convert a gump point to parent relative point
GumpToParent(int32 & gx,int32 & gy,PointRoundDir)447 void Gump::GumpToParent(int32 &gx, int32 &gy, PointRoundDir) {
448 	gx -= _dims.left;
449 	gx += _x;
450 	gy -= _dims.top;
451 	gy += _y;
452 }
453 
454 // Transform a rectangle to screenspace from gumpspace
GumpRectToScreenSpace(Rect & gr,RectRoundDir r)455 void Gump::GumpRectToScreenSpace(Rect &gr, RectRoundDir r) {
456 	PointRoundDir tl = (r == ROUND_INSIDE ? ROUND_BOTTOMRIGHT : ROUND_TOPLEFT);
457 	PointRoundDir br = (r == ROUND_OUTSIDE ? ROUND_BOTTOMRIGHT : ROUND_TOPLEFT);
458 
459 	int32 x1 = gr.left, y1 = gr.top;
460 	int32 x2 = gr.right, y2 = gr.bottom;
461 	GumpToScreenSpace(x1, y1, tl);
462 	GumpToScreenSpace(x2, y2, br);
463 	gr.moveTo(x1, y1);
464 	if (gr.width() != 0)
465 		gr.setWidth(x2 - x1);
466 	if (gr.height() != 0)
467 		gr.setHeight(y2 - y1);
468 }
469 
470 // Transform a rectangle to gumpspace from screenspace
ScreenSpaceToGumpRect(Rect & sr,RectRoundDir r)471 void Gump::ScreenSpaceToGumpRect(Rect &sr, RectRoundDir r) {
472 	PointRoundDir tl = (r == ROUND_INSIDE ? ROUND_BOTTOMRIGHT : ROUND_TOPLEFT);
473 	PointRoundDir br = (r == ROUND_OUTSIDE ? ROUND_BOTTOMRIGHT : ROUND_TOPLEFT);
474 
475 	int32 x1 = sr.left, y1 = sr.top;
476 	int32 x2 = sr.right, y2 = sr.bottom;
477 	ScreenSpaceToGump(x1, y1, tl);
478 	ScreenSpaceToGump(x2, y2, br);
479 	sr.moveTo(x1, y1);
480 	if (sr.width() != 0)
481 		sr.setWidth(x2 - x1);
482 	if (sr.height() != 0)
483 		sr.setHeight(y2 - y1);
484 }
485 
TraceObjId(int32 mx,int32 my)486 uint16 Gump::TraceObjId(int32 mx, int32 my) {
487 	// Convert to local coords
488 	int32 gx = mx, gy = my;
489 	ParentToGump(gx, gy);
490 
491 	uint16 objId_ = 0;
492 
493 	// reverse-iterate children
494 	Std::list<Gump *>::reverse_iterator it;
495 	for (it = _children.rbegin(); it != _children.rend(); ++it) {
496 		Gump *g = *it;
497 
498 		// Not if closing
499 		if (g->_flags & FLAG_CLOSING) continue;
500 
501 		// It's got the point
502 		if (g->PointOnGump(gx, gy)) objId_ = g->TraceObjId(gx, gy);
503 
504 		if (objId_ && objId_ != 65535) break;
505 	}
506 
507 //	if (!objId_ || objId_ == 65535)
508 //		if (PointOnGump(mx,my))
509 //			objId_ = getObjId();
510 
511 	return objId_;
512 }
513 
GetLocationOfItem(uint16 itemid,int32 & gx,int32 & gy,int32 lerp_factor)514 bool Gump::GetLocationOfItem(uint16 itemid, int32 &gx, int32 &gy,
515 							 int32 lerp_factor) {
516 	gx = 0;
517 	gy = 0;
518 	return false;
519 }
520 
521 // Find a child gump that matches the matching function
FindGump(const FindGumpPredicate predicate,bool recursive)522 Gump *Gump::FindGump(const FindGumpPredicate predicate, bool recursive) {
523 	if (predicate(this))
524 		return this;
525 
526 	// Iterate all children
527 	Std::list<Gump *>::iterator  it = _children.begin();
528 	Std::list<Gump *>::iterator  end = _children.end();
529 
530 	for (; it != end; ++it) {
531 		Gump *g = *it;
532 
533 		// Not if closing
534 		if (g->_flags & FLAG_CLOSING)
535 			continue;
536 
537 		if (predicate(g))
538 			return g;
539 	}
540 
541 	if (!recursive)
542 		return nullptr;
543 
544 	// Recursive Iterate all children
545 	it = _children.begin();
546 	end = _children.end();
547 
548 	for (; it != end; ++it) {
549 		Gump *g = (*it);
550 
551 		// Not if closing
552 		if (g->_flags & FLAG_CLOSING)
553 			continue;
554 
555 		g = g->FindGump(predicate, recursive);
556 
557 		if (g)
558 			return g;
559 	}
560 
561 	return nullptr;
562 }
563 
564 // Makes this gump the focus
MakeFocus()565 void Gump::MakeFocus() {
566 	// By default we WONT do anything
567 	if (_parent) {
568 		if (_parent->_focusChild) _parent->_focusChild->OnFocus(false);
569 		_parent->_focusChild = this;
570 	}
571 	OnFocus(true);
572 }
573 
FindNewFocusChild()574 void Gump::FindNewFocusChild() {
575 	if (_focusChild)
576 		_focusChild->OnFocus(false);
577 	_focusChild = nullptr;
578 
579 	// Now add the gump to use as the new focus
580 	Std::list<Gump *>::reverse_iterator	it = _children.rbegin();
581 
582 	if (it != _children.rend()) {
583 		(*it)->MakeFocus();
584 	}
585 }
586 
587 
588 // Adds a child to the list
AddChild(Gump * gump,bool take_focus)589 void Gump::AddChild(Gump *gump, bool take_focus) {
590 	if (!gump) return;
591 
592 	// Remove it if required
593 	Gump *old_parent = gump->GetParent();
594 	if (old_parent) old_parent->RemoveChild(gump);
595 
596 	// Now add the gump in the correct spot
597 	Std::list<Gump *>::iterator  it = _children.begin();
598 	Std::list<Gump *>::iterator  end = _children.end();
599 
600 	for (; it != end; ++it) {
601 		Gump *other = *it;
602 
603 		// Why don't we check for FLAG_CLOSING here?
604 		// Because we want to make sure that the sort order is always valid
605 
606 		// If we are same layer as focus and we wont take it, we will not be
607 		// placed in front of it
608 		if (!take_focus && other == _focusChild && other->_layer == gump->_layer)
609 			break;
610 
611 		// Lower layers get placed before higher layers
612 		if (other->_layer > gump->_layer) break;
613 	}
614 
615 	// Now add it
616 	_children.insert(it, gump);
617 	gump->_parent = this;
618 
619 	// Make the gump the focus if needed
620 	if (take_focus || !_focusChild) {
621 		if (_focusChild) _focusChild->OnFocus(false);
622 		gump->OnFocus(true);
623 		_focusChild = gump;
624 	}
625 }
626 
627 // Remove a gump from the list
RemoveChild(Gump * gump)628 void Gump::RemoveChild(Gump *gump) {
629 	if (!gump) return;
630 
631 	// Remove it
632 	_children.remove(gump);
633 	gump->_parent = nullptr;
634 
635 	// Remove focus, the give upper most gump the focus
636 	if (gump == _focusChild) {
637 		FindNewFocusChild();
638 	}
639 }
640 
MoveChildToFront(Gump * gump)641 void Gump::MoveChildToFront(Gump *gump) {
642 	if (!gump) return;
643 
644 	_children.remove(gump);
645 
646 	Std::list<Gump *>::iterator  it = _children.begin();
647 	Std::list<Gump *>::iterator  end = _children.end();
648 	for (; it != end; ++it) {
649 		Gump *other = *it;
650 
651 		// Lower layers get placed before higher layers
652 		if (other->_layer > gump->_layer) break;
653 	}
654 
655 	_children.insert(it, gump);
656 }
657 
658 
GetRootGump()659 Gump *Gump::GetRootGump() {
660 	if (!_parent) return this;
661 	return _parent->GetRootGump();
662 }
663 
664 
StartDraggingChild(Gump * gump,int32 mx,int32 my)665 bool Gump::StartDraggingChild(Gump *gump, int32 mx, int32 my) {
666 	return false;
667 }
668 
DraggingChild(Gump * gump,int mx,int my)669 void Gump::DraggingChild(Gump *gump, int mx, int my) {
670 	CANT_HAPPEN();
671 }
672 
StopDraggingChild(Gump * gump)673 void Gump::StopDraggingChild(Gump *gump) {
674 	CANT_HAPPEN();
675 }
676 
677 //
678 // Input handling
679 //
680 
onMouseDown(int button,int32 mx,int32 my)681 Gump *Gump::onMouseDown(int button, int32 mx, int32 my) {
682 	// Convert to local coords
683 	ParentToGump(mx, my);
684 
685 	Gump *handled = nullptr;
686 
687 	// Iterate children backwards
688 	Std::list<Gump *>::reverse_iterator it;
689 	for (it = _children.rbegin(); it != _children.rend(); ++it) {
690 		Gump *g = *it;
691 
692 		// Not if closing
693 		if (g->_flags & FLAG_CLOSING || g->IsHidden()) continue;
694 
695 		// It's got the point
696 		if (g->PointOnGump(mx, my)) handled = g->onMouseDown(button, mx, my);
697 
698 		if (handled) break;
699 	}
700 
701 	return handled;
702 }
703 
onMouseMotion(int32 mx,int32 my)704 Gump *Gump::onMouseMotion(int32 mx, int32 my) {
705 	// Convert to local coords
706 	ParentToGump(mx, my);
707 
708 	Gump *handled = nullptr;
709 
710 	// Iterate children backwards
711 	Std::list<Gump *>::reverse_iterator it;
712 	for (it = _children.rbegin(); it != _children.rend(); ++it) {
713 		Gump *g = *it;
714 
715 		// Not if closing
716 		if (g->_flags & FLAG_CLOSING || g->IsHidden()) continue;
717 
718 		// It's got the point
719 		if (g->PointOnGump(mx, my)) handled = g->onMouseMotion(mx, my);
720 
721 		if (handled) break;
722 	}
723 
724 	// All gumps need to handle mouse motion
725 	if (!handled) handled = this;
726 
727 	return handled;
728 }
729 
730 //
731 // KeyInput
732 //
OnKeyDown(int key,int mod)733 bool Gump::OnKeyDown(int key, int mod) {
734 	bool handled = false;
735 	if (_focusChild) handled = _focusChild->OnKeyDown(key, mod);
736 	return handled;
737 }
738 
OnKeyUp(int key)739 bool Gump::OnKeyUp(int key) {
740 	bool handled = false;
741 	if (_focusChild) handled = _focusChild->OnKeyUp(key);
742 	return handled;
743 }
744 
OnTextInput(int unicode)745 bool Gump::OnTextInput(int unicode) {
746 	bool handled = false;
747 	if (_focusChild) handled = _focusChild->OnTextInput(unicode);
748 	return handled;
749 }
750 
mustSave(bool toplevel) const751 bool Gump::mustSave(bool toplevel) const {
752 	// DONT_SAVE flag
753 	if (_flags & FLAG_DONT_SAVE)
754 		return false;
755 
756 	if (toplevel) {
757 		// don't save gumps with parents, unless parent is a core gump
758 		if (_parent && !(_parent->_flags & FLAG_CORE_GUMP))
759 			return false;
760 	}
761 
762 	return true;
763 }
764 
saveData(Common::WriteStream * ws)765 void Gump::saveData(Common::WriteStream *ws) {
766 	Object::saveData(ws);
767 
768 	ws->writeUint16LE(_owner);
769 	ws->writeUint32LE(static_cast<uint32>(_x));
770 	ws->writeUint32LE(static_cast<uint32>(_y));
771 	ws->writeUint32LE(static_cast<uint32>(_dims.left));
772 	ws->writeUint32LE(static_cast<uint32>(_dims.top));
773 	ws->writeUint32LE(static_cast<uint32>(_dims.width()));
774 	ws->writeUint32LE(static_cast<uint32>(_dims.height()));
775 	ws->writeUint32LE(_flags);
776 	ws->writeUint32LE(static_cast<uint32>(_layer));
777 	ws->writeUint32LE(static_cast<uint32>(_index));
778 
779 	uint16 flex = 0;
780 	uint32 shapenum = 0;
781 	if (_shape) {
782 		_shape->getShapeId(flex, shapenum);
783 	}
784 	ws->writeUint16LE(flex);
785 	ws->writeUint32LE(shapenum);
786 
787 	ws->writeUint32LE(_frameNum);
788 	if (_focusChild)
789 		ws->writeUint16LE(_focusChild->getObjId());
790 	else
791 		ws->writeUint16LE(0);
792 	ws->writeUint16LE(_notifier);
793 	ws->writeUint32LE(_processResult);
794 
795 	unsigned int childcount = 0;
796 	Std::list<Gump *>::iterator it;
797 	for (it = _children.begin(); it != _children.end(); ++it) {
798 		if (!(*it)->mustSave(false)) continue;
799 		childcount++;
800 	}
801 
802 	// write children:
803 	ws->writeUint32LE(childcount);
804 	for (it = _children.begin(); it != _children.end(); ++it) {
805 		if (!(*it)->mustSave(false)) continue;
806 
807 		ObjectManager::get_instance()->saveObject(ws, *it);
808 	}
809 }
810 
loadData(Common::ReadStream * rs,uint32 version)811 bool Gump::loadData(Common::ReadStream *rs, uint32 version) {
812 	if (!Object::loadData(rs, version)) return false;
813 
814 	_owner = rs->readUint16LE();
815 	_x = static_cast<int32>(rs->readUint32LE());
816 	_y = static_cast<int32>(rs->readUint32LE());
817 
818 	int dx = static_cast<int32>(rs->readUint32LE());
819 	int dy = static_cast<int32>(rs->readUint32LE());
820 	int dw = static_cast<int32>(rs->readUint32LE());
821 	int dh = static_cast<int32>(rs->readUint32LE());
822 	_dims.moveTo(dx, dy);
823 	_dims.setWidth(dw);
824 	_dims.setHeight(dh);
825 
826 	_flags = rs->readUint32LE();
827 	_layer = static_cast<int32>(rs->readUint32LE());
828 	_index = static_cast<int32>(rs->readUint32LE());
829 
830 	_shape = nullptr;
831 	ShapeArchive *flex = GameData::get_instance()->getShapeFlex(rs->readUint16LE());
832 	uint32 shapenum = rs->readUint32LE();
833 	if (flex) {
834 		_shape = flex->getShape(shapenum);
835 		if (shapenum > 0 && !_shape) {
836 			warning("Gump shape %d is not valid. Corrupt save?", shapenum);
837 			return false;
838 		}
839 	}
840 
841 	_frameNum = rs->readUint32LE();
842 	uint16 focusid = rs->readUint16LE();
843 	_focusChild = nullptr;
844 	_notifier = rs->readUint16LE();
845 	_processResult = rs->readUint32LE();
846 
847 	// read children
848 	uint32 childcount = rs->readUint32LE();
849 
850 	if (childcount > 65535) {
851 		warning("Improbable gump child count %d.  Corrupt save?", childcount);
852 		return false;
853 	}
854 	for (unsigned int i = 0; i < childcount; ++i) {
855 		Object *obj = ObjectManager::get_instance()->loadObject(rs, version);
856 		Gump *child = dynamic_cast<Gump *>(obj);
857 		if (!child) return false;
858 
859 		AddChild(child, false);
860 
861 		if (child->getObjId() == focusid)
862 			_focusChild = child;
863 
864 	}
865 
866 	return true;
867 }
868 
869 } // End of namespace Ultima8
870 } // End of namespace Ultima
871