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  * aint32 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  * Based on the original sources
23  *   Faery Tale II -- The Halls of the Dead
24  *   (c) 1993-1996 The Wyrmkeep Entertainment Co.
25  */
26 
27 #include "common/events.h"
28 
29 #include "saga2/saga2.h"
30 #include "saga2/panel.h"
31 #include "saga2/fontlib.h"
32 #include "saga2/floating.h"
33 #include "saga2/display.h"
34 #include "saga2/gbevel.h"
35 
36 namespace Saga2 {
37 
38 //extern vDisplayPage   *drawPage;
39 extern char iniFile[];
40 
41 //  Function to enable/disable user interface keys
42 extern bool enableUIKeys(bool enabled);
43 
44 /* ======================================================================= *
45    global dispatcher base
46  * ======================================================================= */
47 
48 gDisplayPort        *globalPort;
49 gFont               *mainFont;
50 
51 //  UI locking feature, currently kludged it could get better later.
52 int         lockUINest = 0;
53 
54 /* ======================================================================= *
55    gPanel member functions
56  * ======================================================================= */
57 
gPanel(gWindow & win,const Rect16 & box,AppFunc * cmd)58 gPanel::gPanel(gWindow &win, const Rect16 &box, AppFunc *cmd)
59 	: window(win), _extent(box), command(cmd) {
60 	enabled = 1;
61 	ghosted = 0;
62 	selected = 0;
63 	imageLabel = 0;
64 	title = nullptr;
65 	id = 0;
66 	wantMousePoll = 0;
67 	userData = nullptr;
68 }
69 
gPanel(gPanelList & list,const Rect16 & box,const char * newTitle,uint16 ident,AppFunc * cmd)70 gPanel::gPanel(gPanelList &list, const Rect16 &box,
71                const char *newTitle, uint16 ident, AppFunc *cmd)
72 	: window(list.window) {
73 	title = newTitle;
74 	_extent = box;
75 	enabled = 1;
76 	ghosted = 0;
77 	selected = 0;
78 	imageLabel = 0;
79 	command = cmd;
80 	id = ident;
81 	wantMousePoll = 0;
82 	userData = nullptr;
83 }
84 
gPanel(gPanelList & list,const Rect16 & box,gPixelMap & pic,uint16 ident,AppFunc * cmd)85 gPanel::gPanel(gPanelList &list, const Rect16 &box,
86                gPixelMap &pic, uint16 ident, AppFunc *cmd)
87 	: window(list.window) {
88 	title = (char *)&pic;
89 	_extent = box;
90 	enabled = 1;
91 	ghosted = 0;
92 	selected = 0;
93 	imageLabel = 1;
94 	command = cmd;
95 	id = ident;
96 	wantMousePoll = 0;
97 	userData = nullptr;
98 }
99 
gPanel(gPanelList & list,const StaticRect & box,const char * newTitle,uint16 ident,AppFunc * cmd)100 gPanel::gPanel(gPanelList &list, const StaticRect &box,
101                const char *newTitle, uint16 ident, AppFunc *cmd)
102 	: window(list.window) {
103 	title = newTitle;
104 	_extent = Rect16(box);
105 	enabled = 1;
106 	ghosted = 0;
107 	selected = 0;
108 	imageLabel = 0;
109 	command = cmd;
110 	id = ident;
111 	wantMousePoll = 0;
112 	userData = nullptr;
113 }
114 
115 //  Dummy virtual functions
116 
~gPanel()117 gPanel::~gPanel() {
118 	if (this == g_vm->_toolBase->mousePanel)
119 		g_vm->_toolBase->mousePanel = NULL;
120 	if (this == g_vm->_toolBase->activePanel)
121 		g_vm->_toolBase->activePanel = NULL;
122 }
draw(void)123 void gPanel::draw(void) {}
drawClipped(gPort &,const Point16 &,const Rect16 &)124 void gPanel::drawClipped(gPort &, const Point16 &, const Rect16 &) {}
pointerMove(gPanelMessage &)125 void gPanel::pointerMove(gPanelMessage &) {}
pointerHit(gPanelMessage &)126 bool gPanel::pointerHit(gPanelMessage &) {
127 	return false;
128 }
pointerRHit(gPanelMessage &)129 bool gPanel::pointerRHit(gPanelMessage &) {
130 	return false;
131 }
pointerDrag(gPanelMessage &)132 void gPanel::pointerDrag(gPanelMessage &) {}
pointerRelease(gPanelMessage &)133 void gPanel::pointerRelease(gPanelMessage &) {}
keyStroke(gPanelMessage &)134 bool gPanel::keyStroke(gPanelMessage &) {
135 	return false;
136 }
timerTick(gPanelMessage &)137 void gPanel::timerTick(gPanelMessage &) {}
onMouseHintDelay(void)138 void gPanel::onMouseHintDelay(void) {}
139 
enable(bool abled)140 void gPanel::enable(bool abled) {
141 	enabled = abled ? 1 : 0;
142 }
143 
select(uint16 sel)144 void gPanel::select(uint16 sel) {
145 	selected = sel ? 1 : 0;
146 }
147 
ghost(bool b)148 void gPanel::ghost(bool b) {
149 	ghosted = b ? 1 : 0;
150 }
151 
isActive(void)152 bool gPanel::isActive(void) {
153 	return (this == g_vm->_toolBase->activePanel);
154 }
155 
notify(enum gEventType type,int32 value)156 void gPanel::notify(enum gEventType type, int32 value) {
157 	gEvent          ev;
158 
159 	ev.panel = this;
160 	ev.eventType = type;
161 	ev.value = value;
162 	ev.mouse.x = g_vm->_toolBase->pickPos.x - _extent.x;
163 	ev.mouse.y = g_vm->_toolBase->pickPos.y - _extent.y;
164 	ev.window = &window;
165 
166 	if (command) command(ev);
167 	else if (this != &window) window.notify(ev);
168 }
169 
activate(gEventType)170 bool gPanel::activate(gEventType) {
171 	return false;
172 }
173 
deactivate(void)174 void gPanel::deactivate(void) {
175 	if (isActive()) g_vm->_toolBase->activePanel = NULL;
176 }
177 
makeActive(void)178 void gPanel::makeActive(void) {
179 	g_vm->_toolBase->setActive(this);
180 }
181 
invalidate(Rect16 *)182 void gPanel::invalidate(Rect16 *) {
183 	assert(displayEnabled());
184 	window.update(_extent);
185 }
186 
187 
drawTitle(enum text_positions placement)188 void gPanel::drawTitle(enum text_positions placement) {
189 	gPort           &port = window.windowPort;
190 	Rect16          r = _extent;
191 	const gPixelMap *img = nullptr;
192 
193 	if (title == NULL)
194 		return;
195 
196 	if (imageLabel) {
197 		img = (const gPixelMap *)title;
198 		r.width = img->size.x;
199 		r.height = img->size.y;
200 	} else {
201 		r.width = TextWidth(mainFont, title, -1, textStyleUnderBar);
202 		r.height = mainFont->height;
203 	}
204 
205 	switch (placement) {
206 	case textPosLeft:
207 		r.x -= r.width + 2;
208 		r.y += (_extent.height - r.height) / 2 + 1;
209 		break;
210 
211 	case textPosRight:
212 		r.x += _extent.width + 3;
213 		r.y += (_extent.height - r.height) / 2 + 1;
214 		break;
215 
216 	case textPosHigh:
217 		r.x += (_extent.width - r.width) / 2;
218 		r.y -= r.height + 1;
219 		break;
220 
221 	case textPosLow:
222 		r.x += (_extent.width - r.width) / 2;
223 		r.y += _extent.height + 2;
224 		break;
225 
226 	default:
227 		r.x += (_extent.width - r.width) / 2;
228 		r.y += (_extent.height - r.height) / 2;
229 		break;
230 	}
231 
232 	SAVE_GPORT_STATE(port);                  // save pen color, etc.
233 
234 	if (imageLabel) {
235 		port.setIndirectColor(blackPen);     // pen color black
236 		port.setMode(drawModeColor);         // draw as glyph
237 		port.bltPixels(*img, 0, 0, r.x, r.y, r.width, r.height);
238 	} else {
239 		port.setMode(drawModeMatte);         // draw as glyph
240 		port.setIndirectColor(blackPen);     // pen color black
241 		port.setStyle(textStyleUnderBar);    // set style to do underbars
242 		port.moveTo(r.x, r.y);           // move to new text pos
243 
244 		g_vm->_pointer->hide(*globalPort, r);        // hide the pointer
245 		port.drawText(title, -1);            // draw the text
246 		g_vm->_pointer->show(*globalPort, r);        // hide the pointer
247 	}
248 }
249 
hitTest(const Point16 & p)250 gPanel *gPanel::hitTest(const Point16 &p) {
251 	return enabled && !ghosted && _extent.ptInside(p) ? this : NULL;
252 }
253 
keyTest(int16)254 gPanel *gPanel::keyTest(int16) {
255 	return NULL;
256 }
257 
258 /* ===================================================================== *
259    gPanelList class: A context for holding panels.
260  * ===================================================================== */
261 
262 //  Constructor which is called from window subclass
263 
gPanelList(gWindow & win,const Rect16 & box,char * newTitle,uint16 ident,AppFunc * cmd)264 gPanelList::gPanelList(gWindow &win, const Rect16 &box, char *newTitle,
265                        uint16 ident, AppFunc *cmd)
266 	: gPanel(win, box, cmd) {
267 	title = newTitle;
268 	id = ident;
269 }
270 
271 //  Constructor for standalone panels..
272 
gPanelList(gPanelList & list)273 gPanelList::gPanelList(gPanelList &list)
274 	: gPanel(list, list.window.getExtent(), NULL, 0, NULL) {
275 	window.contents.push_back(this);
276 }
277 
~gPanelList()278 gPanelList::~gPanelList() {
279 	removeControls();
280 	window.contents.remove(this);
281 }
282 
removeControls(void)283 void gPanelList::removeControls(void) {
284 	gPanel *ctl;
285 
286 	//  Delete all sub-panels.
287 	while (contents.size()) {
288 		ctl = contents.front();
289 		contents.remove(ctl);
290 		delete ctl;
291 	}
292 }
293 
294 //  enable/disable gPanelList and all it's children
enable(bool abled)295 void gPanelList::enable(bool abled) {
296 	gPanel::enable(abled);
297 }
298 
invalidate(Rect16 *)299 void gPanelList::invalidate(Rect16 *) {
300 	gPanel *ctl;
301 	Rect16 invArea;
302 
303 	assert(displayEnabled());
304 
305 	if (displayEnabled())
306 		if (contents.size()) {
307 			ctl = contents.back();
308 			invArea = ctl->getExtent();
309 
310 			for (Common::List<gPanel *>::iterator it = contents.reverse_begin(); it != contents.end(); --it) {
311 				ctl = *it;
312 				invArea = bound(invArea, ctl->getExtent());
313 			}
314 			window.update(invArea);
315 		}
316 }
317 
draw(void)318 void gPanelList::draw(void) {
319 	gPanel *ctl;
320 
321 	if (displayEnabled())
322 		if (enabled) {
323 			for (Common::List<gPanel *>::iterator it = contents.reverse_begin(); it != contents.end(); --it) {
324 				ctl = *it;
325 				if (ctl->getEnabled())
326 					ctl->draw();
327 			}
328 		}
329 }
330 
drawClipped(gPort & port,const Point16 & offset,const Rect16 & r)331 void gPanelList::drawClipped(
332     gPort           &port,
333     const Point16   &offset,
334     const Rect16    &r) {
335 	gPanel          *ctl;
336 	Point16         tmpOffset = offset - Point16(_extent.x, _extent.y);
337 	Rect16          tmpR = r - Point16(_extent.x, _extent.y);
338 
339 	if (displayEnabled())
340 		if (enabled) {
341 			for (Common::List<gPanel *>::iterator it = contents.reverse_begin(); it != contents.end(); --it) {
342 				ctl = *it;
343 				if (ctl->getEnabled())
344 					ctl->drawClipped(port, tmpOffset, tmpR);
345 			}
346 		}
347 }
348 
hitTest(const Point16 & p)349 gPanel *gPanelList::hitTest(const Point16 &p) {
350 	gPanel        *ctl;
351 	gPanel        *result;
352 
353 	if (enabled && !ghosted) {
354 		for (Common::List<gPanel *>::iterator it = contents.begin(); it != contents.end(); ++it) {
355 			ctl = *it;
356 			if ((result = ctl->hitTest(p)) != NULL)
357 				return result;
358 		}
359 	}
360 	return NULL;
361 }
362 
keyTest(int16 key)363 gPanel *gPanelList::keyTest(int16 key) {
364 	gPanel          *ctl;
365 	gPanel          *result;
366 
367 	if (enabled && !ghosted) {
368 		for (Common::List<gPanel *>::iterator it = contents.reverse_begin(); it != contents.end(); --it) {
369 			ctl = *it;
370 			if ((result = ctl->keyTest(key)) != NULL)
371 				return result;
372 		}
373 	}
374 
375 	return NULL;
376 }
377 
378 /* ===================================================================== *
379    gWindow class: A panel list plus a drawing port.
380  * ===================================================================== */
381 
382 //  gWindow static variables
383 
384 int           gWindow::dragMode = 0;              // current dragging mode
385 StaticRect    gWindow::dragExtent = {0, 0, 0, 0};            // dragging extent
386 StaticPoint16 gWindow::dragOffset = {0, 0};            // offset to window origin
387 
gWindow(const Rect16 & box,uint16 ident,const char saveName[],AppFunc * cmd)388 gWindow::gWindow(const Rect16 &box, uint16 ident, const char saveName[], AppFunc *cmd)
389 	: gPanelList(*this, box, NULL, ident, cmd)
390 	  //, saver(WIIFF_POS|WIIFS_NORMAL|WIIFE_ONEXIT,iniFile,saveName,box,this)
391 {
392 	openFlag = false;
393 //	pointerImage = &arrowPtr;
394 //	pointerOffset = Point16( 0, 0 );
395 
396 	//  Set up the window feature bits
397 
398 //	windowFeatures = features;
399 
400 	//  Set up the window's gPort
401 
402 	windowPort.setFont(mainFont);
403 	windowPort.setPenMap(globalPort->penMap);
404 
405 	/*  if (windowFeatures & windowBackSaved)   // backsave data under window
406 	    {
407 	        backSave = NEW_UI gBackSave( box );
408 	        // rem: if backsave fails, then what?
409 	    }
410 	    else backSave = NULL;
411 	*/
412 	//  Set the window position.
413 
414 	setPos(Point16(box.x, box.y));
415 }
416 
~gWindow()417 gWindow::~gWindow() {
418 //	gControl     *ctl;
419 
420 	if (isOpen()) close();
421 
422 	//  Delete all sub-panels.
423 
424 //	while ( (ctl = (gControl *)contents.remHead()) != NULL )
425 //		delete ctl;
426 
427 //	delete backSave;
428 }
429 
open(void)430 bool gWindow::open(void) {
431 	if (isOpen()) return true;
432 
433 	//  Send a "pointer-leave" message to mouse panel.
434 
435 	g_vm->_toolBase->leavePanel();
436 	g_vm->_toolBase->windowList.push_front(this);
437 	g_vm->_toolBase->activeWindow = this;
438 	g_vm->_toolBase->setActive(NULL);
439 
440 //	g_vm->_pointer->hide();
441 //	if (backSave) backSave->save( *globalPort );
442 //	g_vm->_pointer->setImage( *pointerImage, pointerOffset.x, pointerOffset.y );
443 //	g_vm->_pointer->show();
444 
445 	openFlag = true;
446 
447 	draw();
448 	return true;
449 }
450 
close(void)451 void gWindow::close(void) {
452 	//saver.onExit(this);
453 	if (!isOpen()) return;
454 
455 	//  If any panels on this window are active, then deactivate them.
456 	if (g_vm->_toolBase->activePanel && g_vm->_toolBase->activePanel->getWindow() == this)
457 		g_vm->_toolBase->activePanel->deactivate();
458 
459 	//  Don't close a window that is being dragged (should never happen,
460 	//  but just in case).
461 	if (DragBar::dragWindow == (FloatingWindow *)this)
462 		return;
463 
464 	openFlag = false;
465 
466 	//  remove this window from the window list.
467 
468 	g_vm->_toolBase->windowList.remove(this);
469 
470 	g_vm->_toolBase->mouseWindow = g_vm->_toolBase->activeWindow = g_vm->_toolBase->windowList.front();
471 	g_vm->_toolBase->mousePanel = g_vm->_toolBase->activePanel = NULL;
472 }
473 
474 //  Move the window to the front...
475 
toFront(void)476 void gWindow::toFront(void) {            // re-order the windows
477 	if (!isOpen()) return;
478 
479 	g_vm->_toolBase->windowList.remove(this);
480 	g_vm->_toolBase->windowList.push_front(this);
481 
482 	g_vm->_toolBase->activePanel = NULL;
483 	g_vm->_toolBase->activeWindow = this;
484 
485 	//  redraw the window
486 	update(_extent);
487 }
488 
isModal(void)489 bool gWindow::isModal(void) {
490 	return false;
491 }
492 
setPos(Point16 pos)493 void gWindow::setPos(Point16 pos) {
494 	Rect16  newClip;
495 
496 	_extent.x = pos.x;
497 	_extent.y = pos.y;
498 
499 //	int16            titleHeight = mainFont->height + 5;
500 
501 	//  We also need to set up the window's port in a similar fashion.
502 
503 	windowPort.origin.x = _extent.x;
504 	windowPort.origin.y = _extent.y;
505 
506 	//  set port's clip
507 	newClip = intersect(_extent, g_vm->_mainPort.clip);
508 	newClip.x -= _extent.x;
509 	newClip.y -= _extent.y;
510 	windowPort.setClip(newClip);
511 	//saver.onMove(this);
512 
513 //	if (backSave) backSave->setPos( pos );
514 }
515 
setExtent(const Rect16 & r)516 void gWindow::setExtent(const Rect16 &r) {
517 	_extent.width = r.width;
518 	_extent.height = r.height;
519 
520 	//saver.onSize(this);
521 	setPos(Point16(r.x, r.y));
522 }
523 
524 //  insert window into window list
insert(void)525 void gWindow::insert(void) {
526 	g_vm->_toolBase->windowList.push_front(this);
527 }
528 
529 
530 //  REM: Need to either adjuct coords when we draw OR
531 //  redefine the address of the pixel map.
532 
deactivate(void)533 void gWindow::deactivate(void) {
534 	selected = 0;
535 	gPanel::deactivate();
536 }
537 
activate(gEventType why)538 bool gWindow::activate(gEventType why) {
539 	if (why == gEventMouseDown) {           // momentarily depress
540 		selected = 1;
541 		notify(why, 0);                      // notify App of successful hit
542 		return true;
543 	}
544 	return false;
545 }
546 
pointerMove(gPanelMessage &)547 void gWindow::pointerMove(gPanelMessage &) {
548 	notify(gEventMouseMove, 0);
549 }
550 
pointerHit(gPanelMessage &)551 bool gWindow::pointerHit(gPanelMessage &) {
552 	activate(gEventMouseDown);
553 	return true;
554 }
555 
pointerDrag(gPanelMessage &)556 void gWindow::pointerDrag(gPanelMessage &) {
557 	if (selected) {
558 		notify(gEventMouseDrag, 0);
559 	}
560 }
561 
pointerRelease(gPanelMessage &)562 void gWindow::pointerRelease(gPanelMessage &) {
563 	if (selected) notify(gEventMouseUp, 0);   // notify App of successful hit
564 	deactivate();
565 }
566 
draw(void)567 void gWindow::draw(void) {
568 	if (displayEnabled())
569 		gPanelList::draw();
570 }
571 
drawClipped(gPort & port,const Point16 & offset,const Rect16 & r)572 void gWindow::drawClipped(
573     gPort           &port,
574     const Point16   &offset,
575     const Rect16    &r) {
576 	if (displayEnabled())
577 		gPanelList::drawClipped(port, offset, r);
578 }
579 
enable(bool abled)580 void gWindow::enable(bool abled) {
581 	gPanel::enable(abled);
582 	draw();
583 }
584 
select(uint16 sel)585 void gWindow::select(uint16 sel) {
586 	gPanel::select(sel);
587 	draw();
588 }
589 
590 /*
591 void gWindow::setPointer( gPixelMap &map, int x, int y )
592 {
593     pointerImage = &map;
594     pointerOffset.x = x;
595     pointerOffset.y = y;
596 
597     if (this == g_vm->_toolBase->activeWindow)
598     {
599         g_vm->_pointer->hide();
600         g_vm->_pointer->setImage( *pointerImage, pointerOffset.x, pointerOffset.y );
601         g_vm->_pointer->show();
602     }
603 }
604 */
605 
606 /* ===================================================================== *
607    gControl class: The basis for buttons and other controls.
608  * ===================================================================== */
609 
gControl(gPanelList & list,const Rect16 & box,const char * title_,uint16 ident,AppFunc * cmd)610 gControl::gControl(gPanelList &list, const Rect16 &box, const char *title_, uint16 ident,
611                    AppFunc *cmd) : gPanel(list, box, title_, ident, cmd) {
612 	accelKey = 0;
613 
614 	//  Add control to the window's control list.
615 
616 	_list = &list;
617 	list.contents.push_back(this);
618 }
619 
gControl(gPanelList & list,const Rect16 & box,gPixelMap & img,uint16 ident,AppFunc * cmd)620 gControl::gControl(gPanelList &list, const Rect16 &box, gPixelMap &img, uint16 ident,
621                    AppFunc *cmd) : gPanel(list, box, img, ident, cmd) {
622 	accelKey = 0;
623 
624 	//  Add control to the window's control list.
625 
626 	_list = &list;
627 	list.contents.push_back(this);
628 }
629 
~gControl()630 gControl::~gControl() {
631 	_list->contents.remove(this);
632 }
633 
gControl(gPanelList & list,const StaticRect & box,const char * title_,uint16 ident,AppFunc * cmd)634 gControl::gControl(gPanelList &list, const StaticRect &box, const char *title_, uint16 ident,
635                    AppFunc *cmd) : gPanel(list, box, title_, ident, cmd) {
636 	accelKey = 0;
637 
638 	//  Add control to the window's control list.
639 
640 	_list = &list;
641 	list.contents.push_back(this);
642 }
643 
enable(bool abled)644 void gControl::enable(bool abled) {
645 	if (!abled != !getEnabled()) {  //  Use '!' to insure boolean-ness
646 		gPanel::enable(abled);
647 		invalidate();
648 	}
649 }
650 
select(uint16 sel)651 void gControl::select(uint16 sel) {
652 	if (!sel != !isSelected()) {    //  Use '!' to insure boolean-ness
653 		gPanel::select(sel);
654 		invalidate();
655 	}
656 }
657 
ghost(bool sel)658 void gControl::ghost(bool sel) {
659 	if (!sel != !isGhosted()) {     //  Use '!' to insure boolean-ness
660 		gPanel::ghost(sel);
661 		invalidate();
662 	}
663 }
664 
keyTest(int16 key)665 gPanel *gControl::keyTest(int16 key) {
666 	return accelKey == key ? this : NULL;
667 }
668 
669 //  For many controls, the only drawing routine we need is the
670 //  "clipped" one, and the normal draw routine just calls
671 //  drawClipped with the main port.
672 
draw(void)673 void gControl::draw(void) {
674 	g_vm->_pointer->hide(window.windowPort, _extent);
675 	if (displayEnabled())
676 		drawClipped(*globalPort,
677 		            Point16(-window._extent.x, -window._extent.y),
678 		            window._extent);
679 	g_vm->_pointer->show(window.windowPort, _extent);
680 }
681 
682 /* ===================================================================== *
683    gGenericControl class: A generic button that notifies everything
684  * ===================================================================== */
685 
gGenericControl(gPanelList & list,const Rect16 & box,uint16 ident,AppFunc * cmd)686 gGenericControl::gGenericControl(gPanelList &list, const Rect16 &box,
687                                  uint16 ident, AppFunc *cmd)
688 	: gControl(list, box, NULL, ident, cmd) {
689 	dblClickFlag = false;
690 }
691 
activate(gEventType)692 bool gGenericControl::activate(gEventType) {
693 	selected = 1;
694 	return true;
695 }
696 
deactivate(void)697 void gGenericControl::deactivate(void) {
698 	selected = 0;
699 	gPanel::deactivate();
700 }
701 
pointerMove(gPanelMessage & msg)702 void gGenericControl::pointerMove(gPanelMessage &msg) {
703 	notify(gEventMouseMove, (msg.pointerEnter ? enter : 0) | (msg.pointerLeave ? leave : 0));
704 }
705 
pointerHit(gPanelMessage & msg)706 bool gGenericControl::pointerHit(gPanelMessage &msg) {
707 	if (msg.rightButton)
708 		notify(gEventRMouseDown, 0);
709 	else if (msg.doubleClick && !dblClickFlag) {
710 		dblClickFlag = true;
711 		notify(gEventDoubleClick, 0);
712 	} else {
713 		dblClickFlag = false;
714 		notify(gEventMouseDown, 0);
715 	}
716 
717 	return true;
718 }
719 
pointerDrag(gPanelMessage &)720 void gGenericControl::pointerDrag(gPanelMessage &) {
721 	notify(gEventMouseDrag, 0);
722 }
723 
pointerRelease(gPanelMessage &)724 void gGenericControl::pointerRelease(gPanelMessage &) {
725 	notify(gEventMouseUp, 0);
726 	deactivate();
727 }
728 
729 //  Generic control has no rendering code.
draw(void)730 void gGenericControl::draw(void) {
731 }
732 
733 /* ===================================================================== *
734    class gToolBase: Global dispatcher for events
735  * ===================================================================== */
736 
setActive(gPanel * ctl)737 void gToolBase::setActive(gPanel *ctl) {
738 	if (activePanel && activePanel == ctl)  return;
739 	if (activePanel) activePanel->deactivate();
740 	if (ctl == NULL || ctl->activate(gEventNone)) activePanel = ctl;
741 }
742 
handleMouse(Common::Event & event,uint32 time)743 void gToolBase::handleMouse(Common::Event &event, uint32 time) {
744 	gWindow         *w = activeWindow;
745 	gPanel          *ctl,
746 	                *pickPanel = NULL;
747 	static gMouseState prevState;
748 	static int32    lastClickTime = 0x8000;
749 	static Point16  lastClickPos;
750 
751 
752 	// Emulate mouse state for now
753 	switch (event.type) {
754 	case Common::EVENT_LBUTTONDOWN:
755 		_curMouseState.left = true;
756 		break;
757 	case Common::EVENT_RBUTTONDOWN:
758 		_curMouseState.right = true;
759 		break;
760 	case Common::EVENT_LBUTTONUP:
761 		_curMouseState.left = false;
762 		break;
763 	case Common::EVENT_RBUTTONUP:
764 		_curMouseState.right = false;
765 		break;
766 	case Common::EVENT_MOUSEMOVE:
767 		_curMouseState.pos.x = event.mouse.x;
768 		_curMouseState.pos.y = event.mouse.y;
769 		g_vm->_pointer->move(Point16(_curMouseState.pos.x, _curMouseState.pos.y));
770 		break;
771 	default:
772 		break;
773 	}
774 
775 	//  Do nothing if UI locked.
776 	if (lockUINest > 0)
777 		return;
778 
779 #if CURSOR_CYCLING
780 	if (_curMouseState.right) {
781 		cycleCursor();
782 		return;
783 	}
784 #endif
785 
786 	//  Code for "Tool tip delay"
787 	if (prevState.pos != _curMouseState.pos
788 	        &&  prevState.left != _curMouseState.left
789 	        &&  prevState.right != _curMouseState.right) {
790 		lastMouseMoveTime = msg.timeStamp;
791 		if (mouseHintSet)
792 			setMouseTextF(NULL);
793 	}
794 
795 	//  If there is no active window, then do nothing.
796 
797 	if (w == NULL) {
798 		prevState = _curMouseState;
799 		return;
800 	}
801 
802 	//  Set up the pick position relative to the window
803 
804 	if (activePanel) {
805 		pickPos.x = _curMouseState.pos.x - activePanel->window._extent.x;
806 		pickPos.y = _curMouseState.pos.y - activePanel->window._extent.y;
807 	} else {
808 		pickPos.x = _curMouseState.pos.x - w->_extent.x;
809 		pickPos.y = _curMouseState.pos.y - w->_extent.y;
810 	}
811 
812 	//  Fill in the message to be sent to the various panels
813 
814 	msg.pickAbsPos  = pickPos;
815 	msg.leftButton  = _curMouseState.left ? 1 : 0;
816 	msg.rightButton = _curMouseState.right ? 1 : 0;
817 	msg.pointerEnter = 0;
818 	msg.pointerLeave = 0;
819 	msg.doubleClick = 0;
820 	msg.timeStamp = time;
821 
822 	if (((_curMouseState.left  && !leftDrag)            // if left button hit
823 	        || (_curMouseState.right && !rightDrag))      // or right button hit
824 	        && activePanel != NULL) {           // and a panel is active
825 		//  Then we have a button hit event. If the button hit
826 		//  is occuring outside the panel, then it should be
827 		//  deselected.
828 
829 		if (activePanel->_extent.ptInside(pickPos) == false)
830 			activePanel->deactivate();
831 	}
832 
833 	if (prevState.pos == _curMouseState.pos) ;          // don't do anything if same pos
834 	else if (activePanel) {                 // if control active
835 		mousePanel = activePanel;           // assume mouse over active panel
836 
837 		if (leftDrag || rightDrag) {
838 			setMsg(msg, activePanel);        // set up gPanelMessage
839 			activePanel->pointerDrag(msg);  // send panel a mouse movement
840 		} else {
841 			setMsg(msg, activePanel);        // set up gPanelMessage
842 			activePanel->pointerMove(msg);  // send panel a mouse movement
843 		}
844 	}
845 
846 	if (!activePanel /* && !ms.right */) {
847 		//  If the point is within the window
848 		Common::List<gWindow *>::iterator it;
849 		for (it = windowList.begin(); it != windowList.end(); ++it) {
850 			w = *it;
851 			if (w->_extent.ptInside(_curMouseState.pos) || w->isModal()) {
852 				//  Set up the pick position relative to the window
853 
854 				pickPos.x = _curMouseState.pos.x - w->_extent.x;
855 				pickPos.y = _curMouseState.pos.y - w->_extent.y;
856 
857 				if ((ctl = w->hitTest(pickPos)) != NULL)
858 					pickPanel = ctl;
859 				else
860 					pickPanel = w;
861 
862 				break;
863 			}
864 		}
865 
866 		if (it == windowList.end()) {
867 			prevState = _curMouseState;
868 			return;
869 		}
870 
871 		mouseWindow = w;
872 
873 		//  If the mouse is not over the control any more, tell it so.
874 
875 		if (mousePanel && mousePanel != pickPanel) {
876 			if (&mousePanel->window != w) {
877 				//  Temporarily adjust pickPos to be relative to the old panel's window
878 				//  instead of the new panel's window.
879 				pickPos.x = _curMouseState.pos.x - mousePanel->window._extent.x;
880 				pickPos.y = _curMouseState.pos.y - mousePanel->window._extent.y;
881 
882 				setMsgQ(msg, mousePanel);        // set up gPanelMessage
883 
884 				pickPos.x = _curMouseState.pos.x - w->_extent.x;
885 				pickPos.y = _curMouseState.pos.y - w->_extent.y;
886 			} else {
887 				setMsgQ(msg, mousePanel);        // set up gPanelMessage
888 			}
889 //			msg.pickPos.x  = pickPos.x - mousePanel->extent.x;
890 //			msg.pickPos.y  = pickPos.y - mousePanel->extent.y;
891 			msg.inPanel     = 0;
892 			msg.pointerEnter = 0;
893 			msg.pointerLeave = 1;
894 
895 			mousePanel->pointerMove(msg);
896 
897 		}
898 
899 		//  If the mouse is over a new control, make that the current
900 		//  mouse control.
901 
902 		if (pickPanel) {
903 			setMsg(msg, pickPanel);          // set up gPanelMessage
904 //			msg.pickPos.x  = pickPos.x - pickPanel->extent.x;
905 //			msg.pickPos.y  = pickPos.y - pickPanel->extent.y;
906 			msg.leftButton  = _curMouseState.left ? 1 : 0;
907 //			msg.inPanel        = pickPanel->extent.ptInside(pickPos);
908 			msg.pointerEnter = (mousePanel == pickPanel) ? 0 : 1;
909 			msg.pointerLeave = 0;
910 
911 			mousePanel = pickPanel;
912 			mousePanel->pointerMove(msg);
913 		} else
914 			mousePanel = NULL;
915 	}
916 
917 	//  Fix up flags because earlier code may have changed them
918 
919 	msg.pointerEnter = 0;
920 	msg.pointerLeave = 0;
921 
922 	//  Send appropriate button-press messages to the panels
923 
924 	if (prevState.left  != _curMouseState.left         // if buttons changed state
925 	        || prevState.right != _curMouseState.right) {
926 
927 		//  If both buttons were previously up, then a mouse
928 		//  hit must have occured.
929 
930 		if (prevState.left == 0 && prevState.right == 0) {
931 
932 			//  Check for mouse double-click. Check to see that
933 			//  the elapsed time from the last click is less than
934 			//  1/3 of a second, and that the mouse ptr hasn't moved
935 			//  very much.
936 
937 			if (((uint32)(msg.timeStamp - lastClickTime) < 333)
938 			        ||  _curMouseState.left > 1
939 			        ||  _curMouseState.right > 1) {
940 				Point16 diff = lastClickPos - _curMouseState.pos;
941 
942 				if (ABS(diff.x) + ABS(diff.y) < 6)
943 					msg.doubleClick = 1;
944 			}
945 
946 			//  Record the last mouse time and position for
947 			//  future double-click checks.
948 
949 			lastClickTime = msg.timeStamp;
950 			lastClickPos = _curMouseState.pos;
951 
952 			if (mousePanel) {               // if over a control
953 				setMsgQ(msg, mousePanel);        // set up gPanelMessage
954 //				msg.pickPos.x = pickPos.x - mousePanel->extent.x;
955 //				msg.pickPos.y = pickPos.y - mousePanel->extent.y;
956 				msg.inPanel     = 1;
957 
958 				if (activeWindow && activeWindow->isModal()) {
959 					mouseWindow = activeWindow;
960 				} else if (mouseWindow == NULL) {
961 					mouseWindow = &mousePanel->window;
962 				}
963 
964 				if (mouseWindow && mouseWindow != activeWindow) {
965 					msg.pickAbsPos = pickPos;
966 					//  Re-order the windows.
967 					mouseWindow->toFront();
968 				}
969 				// send it a hit message
970 				if (mousePanel->pointerHit(msg)) {
971 					activePanel = mousePanel;
972 					if (_curMouseState.left)
973 						leftDrag = true;
974 					else
975 						rightDrag = true;
976 				}
977 			}
978 		} else if ((leftDrag && _curMouseState.left == false)  // check for release
979 		           || (rightDrag && _curMouseState.right == false)) {
980 			if (activePanel && mousePanel) {            // if a control is active
981 				setMsg(msg, mousePanel);    // send it a release message
982 				mousePanel->pointerRelease(msg);
983 			}
984 			leftDrag = rightDrag = false;
985 		}
986 	}
987 
988 	prevState = _curMouseState;
989 }
990 
leavePanel(void)991 void gToolBase::leavePanel(void) {
992 	msg.timeStamp = g_system->getMillis();
993 
994 	if (mousePanel) {
995 		msg.inPanel     = 0;
996 		msg.pointerEnter = 0;
997 		msg.pointerLeave = 1;
998 
999 		mousePanel->pointerMove(msg);
1000 		mousePanel = NULL;
1001 	}
1002 
1003 	if (activePanel) activePanel->deactivate();
1004 }
1005 
handleKeyStroke(Common::Event & event)1006 void gToolBase::handleKeyStroke(Common::Event &event) {
1007 	gWindow *w = activeWindow;
1008 	gPanel  *ctl;
1009 
1010 	uint16 key = event.kbd.ascii; // FIXME
1011 	uint16 qualifier = 0;
1012 
1013 	if (event.kbd.flags & Common::KBD_SHIFT)
1014 		qualifier |= qualifierShift;
1015 
1016 	if (event.kbd.flags & Common::KBD_CTRL)
1017 		qualifier |= qualifierControl;
1018 
1019 	if (event.kbd.flags & Common::KBD_ALT)
1020 		qualifier |= qualifierAlt;
1021 
1022 	msg.pickAbsPos  = pickPos;
1023 	msg.pointerEnter = 0;
1024 	msg.pointerLeave = 0;
1025 	msg.key = key;
1026 	msg.qualifier = qualifier;
1027 	msg.timeStamp = g_system->getMillis();
1028 
1029 	if (activePanel) {                      // send keystroke to active panel
1030 		setMsg(msg, activePanel);            // set up gPanelMessage
1031 		if (activePanel->keyStroke(msg))
1032 			return;
1033 	}
1034 
1035 	//  Now, search the contents of the window for a control with
1036 	//  the correct accelerator key
1037 
1038 	if (w) {
1039 		uint16 k = key;
1040 		//uint8 k = key & 0xff;
1041 
1042 		if (k != 0) {
1043 			k = toupper(k);
1044 
1045 			if ((ctl = w->keyTest(k)) != NULL) {
1046 				if (activePanel == ctl) return;
1047 				if (activePanel) activePanel->deactivate();
1048 				if (ctl->activate(gEventKeyDown)) {
1049 					activePanel = ctl;
1050 					return;
1051 				}
1052 			}
1053 		}
1054 
1055 		//  Try sending the message to the window
1056 
1057 		if (w->keyStroke(msg))
1058 			return;
1059 
1060 		// else send the message to the app.
1061 
1062 		w->notify(gEventKeyDown, (qualifier << 16) | key);
1063 	}
1064 }
1065 
handleTimerTick(int32 tick)1066 void gToolBase::handleTimerTick(int32 tick) {
1067 	msg.pickAbsPos  = pickPos;
1068 	msg.pointerEnter = 0;
1069 	msg.pointerLeave = 0;
1070 	msg.timeStamp = tick;
1071 
1072 	if (activePanel) {                      // send keystroke to active panel
1073 		setMsg(msg, activePanel);            // set up gPanelMessage
1074 		activePanel->timerTick(msg);
1075 	} else if (mousePanel) {
1076 		if (mousePanel->wantMousePoll) {
1077 			setMsg(msg, mousePanel);         // set up gPanelMessage
1078 			mousePanel->pointerMove(msg);
1079 		} else if (!mouseHintSet
1080 		           && ((uint32)(tick - lastMouseMoveTime) > 500)) {
1081 			mousePanel->onMouseHintDelay();
1082 		}
1083 	}
1084 }
1085 
HandleTimerTick(long tick)1086 void HandleTimerTick(long tick) {
1087 	static int32        lastTick;
1088 
1089 	if (tick - lastTick > 1) {
1090 		lastTick = tick;
1091 		g_vm->_toolBase->handleTimerTick(tick);
1092 	}
1093 }
1094 
1095 /* ===================================================================== *
1096    Code to initialize the panel system
1097  * ===================================================================== */
1098 
initPanels(gDisplayPort & port)1099 void initPanels(gDisplayPort &port) {
1100 	globalPort = &port;
1101 	mainFont = &Helv11Font;
1102 }
1103 
cleanupPanels(void)1104 void cleanupPanels(void) {
1105 }
1106 
leftButtonState(void)1107 int16 leftButtonState(void) {
1108 	return g_vm->_toolBase->msg.leftButton;
1109 }
1110 
rightButtonState(void)1111 int16 rightButtonState(void) {
1112 	return g_vm->_toolBase->msg.rightButton;
1113 }
1114 
LockUI(bool state)1115 void LockUI(bool state) {
1116 	if (state == true) {
1117 		if (lockUINest <= 0) {
1118 			g_vm->_pointer->hide();
1119 			enableUIKeys(false);
1120 			g_vm->_toolBase->setActive(NULL);
1121 		}
1122 		lockUINest++;
1123 	} else {
1124 		lockUINest--;
1125 		assert(lockUINest >= 0);
1126 		if (lockUINest <= 0) {
1127 			enableUIKeys(true);
1128 			g_vm->_pointer->show();
1129 		}
1130 	}
1131 }
1132 
dumpGBASE(char * msg)1133 void dumpGBASE(char *msg) {
1134 }
1135 
1136 } // end of namespace Saga2
1137