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