1 // Rewritten mostly by Kris Morness
2 #include "Button_Sound_Control.h"
3 #include "Button_System.h"
4 #include "Debug.h"
5 #include "Font.h"
6 #include "HImage.h"
7 #include "Input.h"
8 #include "MemMan.h"
9 #include "VObject.h"
10 #include "VObject_Blitters.h"
11 #include "VSurface.h"
12 #include "Video.h"
13 #include "WCheck.h"
14 #include "WordWrap.h"
15 
16 #ifdef _JA2_RENDER_DIRTY
17 #	include "Font_Control.h"
18 #endif
19 
20 #include <string_theory/string>
21 
22 #include <stdexcept>
23 
24 
25 // Names of the default generic button image files.
26 #define DEFAULT_GENERIC_BUTTON_OFF    "genbutn.sti"
27 #define DEFAULT_GENERIC_BUTTON_ON     "genbutn2.sti"
28 #define DEFAULT_GENERIC_BUTTON_OFF_HI "genbutn3.sti"
29 #define DEFAULT_GENERIC_BUTTON_ON_HI  "genbutn4.sti"
30 
31 
32 #define MSYS_STARTING_CURSORVAL 0
33 
34 
35 #define MAX_BUTTON_ICONS 40
36 
37 
38 #define GUI_BTN_NONE           0
39 #define GUI_BTN_DUPLICATE_VOBJ 1
40 
41 
42 /* Kris:  December 2, 1997
43  * Special internal debugging utilities that will ensure that you don't attempt
44  * to delete an already deleted button, or it's images, etc.  It will also
45  * ensure that you don't create the same button that already exists.
46  * TO REMOVE ALL DEBUG FUNCTIONALITY: simply comment out BUTTONSYSTEM_DEBUGGING
47  * definition
48  */
49 #if defined _DEBUG
50 #	define BUTTONSYSTEM_DEBUGGING
51 #endif
52 
53 
54 #define FOR_EACH_BUTTON(iter) \
55 	FOR_EACH(GUI_BUTTON*, iter, ButtonList) \
56 		if (!*iter) continue; else
57 
58 
59 #ifdef BUTTONSYSTEM_DEBUGGING
60 
61 // Called immediately before assigning the button to the button list.
AssertFailIfIdenticalButtonAttributesFound(const GUI_BUTTON * b)62 static void AssertFailIfIdenticalButtonAttributesFound(const GUI_BUTTON* b)
63 {
64 	FOR_EACH_BUTTON(i)
65 	{
66 		GUI_BUTTON const* const c = *i;
67 		if (c->uiFlags            &   BUTTON_DELETION_PENDING) continue;
68 		if (c->uiFlags            &   BUTTON_NO_DUPLICATE)     continue;
69 		if (b->Area.PriorityLevel != c->Area.PriorityLevel)    continue;
70 		if (b->X()                != c->X())                   continue;
71 		if (b->Y()                != c->Y())                   continue;
72 		if (b->BottomRightX()     != c->BottomRightX())        continue;
73 		if (b->BottomRightY()     != c->BottomRightY())        continue;
74 		if (b->ClickCallback      != c->ClickCallback)         continue;
75 		if (b->MoveCallback       != c->MoveCallback)          continue;
76 		/* if we get this far, it is reasonably safe to assume that the newly
77 		 * created button already exists.  Placing a break point on the following
78 		 * assert will allow the coder to easily isolate the case!
79 		 */
80 		SLOGA("Attempting to create a button that has already been created (existing buttonID %d).", c->IDNum);
81 	}
82 }
83 
84 #endif
85 
86 
87 /* Kris:
88  * These are the variables used for the anchoring of a particular button.  When
89  * you click on a button, it get's anchored, until you release the mouse button.
90  * When you move around, you don't want to select other buttons, even when you
91  * release it.  This follows the Windows 95 convention.
92  */
93 static GUI_BUTTON* gpAnchoredButton;
94 static GUI_BUTTON* gpPrevAnchoredButton;
95 static BOOLEAN gfAnchoredState;
96 
97 static INT8 gbDisabledButtonStyle;
98 
99 BOOLEAN gfRenderHilights = TRUE;
100 
101 // Struct definition for the QuickButton pictures.
102 struct BUTTON_PICS
103 {
104 	HVOBJECT vobj;      // The Image itself
105 	INT32    Grayed;    // Index to use for a "Grayed-out" button
106 	INT32    OffNormal; // Index to use when button is OFF
107 	INT32    OffHilite; // Index to use when button is OFF w/ hilite on it
108 	INT32    OnNormal;  // Index to use when button is ON
109 	INT32    OnHilite;  // Index to use when button is ON w/ hilite on it
110 	ButtonDimensions max; // width/height of largest image in use
111 	UINT32   fFlags;    // Special image flags
112 };
113 
114 static BUTTON_PICS ButtonPictures[MAX_BUTTON_PICS];
115 
116 SGPVSurface* ButtonDestBuffer;
117 
118 GUI_BUTTON* ButtonList[MAX_BUTTONS];
119 
120 
GetDimensionsOfButtonPic(const BUTTON_PICS * const pics)121 const ButtonDimensions* GetDimensionsOfButtonPic(const BUTTON_PICS* const pics)
122 {
123 	return &pics->max;
124 }
125 
126 
127 static HVOBJECT GenericButtonOffNormal;
128 static HVOBJECT GenericButtonOffHilite;
129 static HVOBJECT GenericButtonOnNormal;
130 static HVOBJECT GenericButtonOnHilite;
131 static UINT16   GenericButtonFillColors;
132 
133 static HVOBJECT GenericButtonIcons[MAX_BUTTON_ICONS];
134 
135 static BOOLEAN gfDelayButtonDeletion   = FALSE;
136 static BOOLEAN gfPendingButtonDeletion = FALSE;
137 
138 extern MOUSE_REGION* MSYS_PrevRegion;
139 
140 
141 // Finds an available slot for loading button pictures
FindFreeButtonSlot(void)142 static BUTTON_PICS* FindFreeButtonSlot(void)
143 {
144 	// Search for a slot
145 	FOR_EACH(BUTTON_PICS, i, ButtonPictures)
146 	{
147 		if (i->vobj == NULL) return i;
148 	}
149 	throw std::runtime_error("Out of button image slots");
150 }
151 
152 
SetMaxSize(BUTTON_PICS * const pics,const INT32 img_idx)153 static void SetMaxSize(BUTTON_PICS* const pics, const INT32 img_idx)
154 {
155 	if (img_idx == BUTTON_NO_IMAGE) return;
156 	ETRLEObject const& e = pics->vobj->SubregionProperties(img_idx);
157 	UINT32      const  w = e.sOffsetX + e.usWidth;
158 	UINT32      const  h = e.sOffsetY + e.usHeight;
159 	if (pics->max.w < w) pics->max.w = w;
160 	if (pics->max.h < h) pics->max.h = h;
161 }
162 
163 
InitButtonImage(BUTTON_PICS * const pics,const HVOBJECT VObj,const UINT32 Flags,const INT32 Grayed,const INT32 OffNormal,const INT32 OffHilite,const INT32 OnNormal,const INT32 OnHilite)164 static void InitButtonImage(BUTTON_PICS* const pics, const HVOBJECT VObj, const UINT32 Flags, const INT32 Grayed, const INT32 OffNormal, const INT32 OffHilite, const INT32 OnNormal, const INT32 OnHilite)
165 {
166 	pics->vobj = VObj;
167 
168 	// Init the QuickButton image structure with indexes to use
169 	pics->Grayed    = Grayed;
170 	pics->OffNormal = OffNormal;
171 	pics->OffHilite = OffHilite;
172 	pics->OnNormal  = OnNormal;
173 	pics->OnHilite  = OnHilite;
174 	pics->fFlags    = Flags;
175 
176 	// Fit the button size to the largest image in the set
177 	pics->max.w = 0;
178 	pics->max.h = 0;
179 	SetMaxSize(pics, Grayed);
180 	SetMaxSize(pics, OffNormal);
181 	SetMaxSize(pics, OffHilite);
182 	SetMaxSize(pics, OnNormal);
183 	SetMaxSize(pics, OnHilite);
184 }
185 
186 
LoadButtonImage(const char * filename,INT32 Grayed,INT32 OffNormal,INT32 OffHilite,INT32 OnNormal,INT32 OnHilite)187 BUTTON_PICS* LoadButtonImage(const char* filename, INT32 Grayed, INT32 OffNormal, INT32 OffHilite, INT32 OnNormal, INT32 OnHilite)
188 {
189 	AssertMsg(filename != NULL, "Attempting to LoadButtonImage() with null filename.");
190 
191 	if (Grayed    == BUTTON_NO_IMAGE &&
192 			OffNormal == BUTTON_NO_IMAGE &&
193 			OffHilite == BUTTON_NO_IMAGE &&
194 			OnNormal  == BUTTON_NO_IMAGE &&
195 			OnHilite  == BUTTON_NO_IMAGE)
196 	{
197 		throw std::logic_error("No button pictures selected");
198 	}
199 
200 	BUTTON_PICS* const UseSlot = FindFreeButtonSlot();
201 	SGPVObject*  const VObj    = AddVideoObjectFromFile(filename);
202 	InitButtonImage(UseSlot, VObj, GUI_BTN_NONE, Grayed, OffNormal, OffHilite, OnNormal, OnHilite);
203 	return UseSlot;
204 }
205 
206 
LoadButtonImage(char const * const filename,INT32 const off_normal,INT32 const on_normal)207 BUTTON_PICS* LoadButtonImage(char const* const filename, INT32 const off_normal, INT32 const on_normal)
208 {
209 	return LoadButtonImage(filename, -1, off_normal, -1, on_normal, -1);
210 }
211 
212 
UseLoadedButtonImage(BUTTON_PICS * const LoadedImg,const INT32 Grayed,const INT32 OffNormal,const INT32 OffHilite,const INT32 OnNormal,const INT32 OnHilite)213 BUTTON_PICS* UseLoadedButtonImage(BUTTON_PICS* const LoadedImg, const INT32 Grayed, const INT32 OffNormal, const INT32 OffHilite, const INT32 OnNormal, const INT32 OnHilite)
214 {
215 	if (Grayed    == BUTTON_NO_IMAGE &&
216 			OffNormal == BUTTON_NO_IMAGE &&
217 			OffHilite == BUTTON_NO_IMAGE &&
218 			OnNormal  == BUTTON_NO_IMAGE &&
219 			OnHilite  == BUTTON_NO_IMAGE)
220 	{
221 		throw std::logic_error("No button pictures selected for pre-loaded button image");
222 	}
223 
224 	// Is button image index given valid?
225 	const HVOBJECT vobj = LoadedImg->vobj;
226 	if (!vobj)
227 	{
228 		throw std::logic_error("Invalid button picture handle given for pre-loaded button image");
229 	}
230 
231 	BUTTON_PICS* const UseSlot = FindFreeButtonSlot();
232 	InitButtonImage(UseSlot, vobj, GUI_BTN_DUPLICATE_VOBJ, Grayed, OffNormal, OffHilite, OnNormal, OnHilite);
233 	return UseSlot;
234 }
235 
236 
UseLoadedButtonImage(BUTTON_PICS * const img,INT32 const off_normal,INT32 const on_normal)237 BUTTON_PICS* UseLoadedButtonImage(BUTTON_PICS* const img, INT32 const off_normal, INT32 const on_normal)
238 {
239 	return UseLoadedButtonImage(img, -1, off_normal, -1, on_normal, -1);
240 }
241 
242 
UnloadButtonImage(BUTTON_PICS * const pics)243 void UnloadButtonImage(BUTTON_PICS* const pics)
244 {
245 #if defined BUTTONSYSTEM_DEBUGGING
246 	AssertMsg(pics->vobj != NULL, "Attempting to UnloadButtonImage that has a null vobj (already deleted).");
247 #endif
248 	if (pics->vobj == NULL) return;
249 
250 	// If this is a duplicated button image, then don't trash the vobject
251 	if (!(pics->fFlags & GUI_BTN_DUPLICATE_VOBJ))
252 	{
253 		/* Deleting a non-duplicate, so see if any dups present. if so, then convert
254 		 * one of them to an original!
255 		 */
256 		FOR_EACH(BUTTON_PICS, other, ButtonPictures)
257 		{
258 			if (other == pics) continue;
259 			if (other->vobj != pics->vobj) continue;
260 			if (!(other->fFlags & GUI_BTN_DUPLICATE_VOBJ)) continue;
261 
262 			/* If we got here, then we got a duplicate object of the one we want to
263 			 * delete, so convert it to an original!
264 			 */
265 			other->fFlags &= ~GUI_BTN_DUPLICATE_VOBJ;
266 
267 			// Now remove this button, but not its vobject
268 			goto remove_pic;
269 		}
270 
271 		DeleteVideoObject(pics->vobj);
272 	}
273 
274 remove_pic:
275 	pics->vobj = NULL;
276 }
277 
278 
EnableButton(GUIButtonRef const b)279 void EnableButton(GUIButtonRef const b)
280 {
281 	CHECKV(b != NULL); // XXX HACK000C
282 	b->uiFlags |= BUTTON_ENABLED | BUTTON_DIRTY;
283 }
284 
285 
DisableButton(GUIButtonRef const b)286 void DisableButton(GUIButtonRef const b)
287 {
288 	CHECKV(b != NULL); // XXX HACK000C
289 	b->uiFlags &= ~BUTTON_ENABLED;
290 	b->uiFlags |= BUTTON_DIRTY;
291 }
292 
293 
EnableButton(GUIButtonRef const b,bool const enable)294 void EnableButton(GUIButtonRef const b, bool const enable)
295 {
296 	enable ? EnableButton(b) : DisableButton(b);
297 }
298 
299 
300 /* Initializes the button image sub-system. This function is called by
301  * InitButtonSystem.
302  */
InitializeButtonImageManager(void)303 static void InitializeButtonImageManager(void)
304 {
305 	// Blank out all QuickButton images
306 	for (int x = 0; x < MAX_BUTTON_PICS; ++x)
307 	{
308 		BUTTON_PICS* const pics = &ButtonPictures[x];
309 		pics->vobj      = NULL;
310 		pics->Grayed    = -1;
311 		pics->OffNormal = -1;
312 		pics->OffHilite = -1;
313 		pics->OnNormal  = -1;
314 		pics->OnHilite  = -1;
315 	}
316 
317 	// Blank out all Generic button data
318 	GenericButtonOffNormal  = NULL;
319 	GenericButtonOffHilite  = NULL;
320 	GenericButtonOnNormal   = NULL;
321 	GenericButtonOnHilite   = NULL;
322 	GenericButtonFillColors = 0;
323 
324 	// Blank out all icon images
325 	for (int x = 0; x < MAX_BUTTON_ICONS; ++x)
326 		GenericButtonIcons[x] = NULL;
327 
328 	// Load the default generic button images
329 	GenericButtonOffNormal = AddVideoObjectFromFile(DEFAULT_GENERIC_BUTTON_OFF);
330 	GenericButtonOnNormal  = AddVideoObjectFromFile(DEFAULT_GENERIC_BUTTON_ON);
331 
332 	/* Load up the off hilite and on hilite images. We won't check for errors
333 	 * because if the file doesn't exists, the system simply ignores that file.
334 	 * These are only here as extra images, they aren't required for operation
335 	 * (only OFF Normal and ON Normal are required).
336 	 */
337 	try
338 	{
339 		GenericButtonOffHilite = AddVideoObjectFromFile(DEFAULT_GENERIC_BUTTON_OFF_HI);
340 	}
341 	catch (...) { /* see comment above */ }
342 	try
343 	{
344 		GenericButtonOnHilite  = AddVideoObjectFromFile(DEFAULT_GENERIC_BUTTON_ON_HI);
345 	}
346 	catch (...) { /* see comment above */ }
347 
348 	UINT8 const Pix = GenericButtonOffNormal->GetETRLEPixelValue(8, 0, 0);
349 	GenericButtonFillColors = GenericButtonOffNormal->Palette16()[Pix];
350 }
351 
352 
353 // Finds the next available slot for button icon images.
FindFreeIconSlot(void)354 static INT16 FindFreeIconSlot(void)
355 {
356 	for (INT16 x = 0; x < MAX_BUTTON_ICONS; ++x)
357 	{
358 		if (GenericButtonIcons[x] == NULL) return x;
359 	}
360 	throw std::runtime_error("Out of generic button icon slots");
361 }
362 
363 
LoadGenericButtonIcon(const char * filename)364 INT16 LoadGenericButtonIcon(const char* filename)
365 {
366 	AssertMsg(filename != NULL, "Attempting to LoadGenericButtonIcon() with null filename.");
367 
368 	// Get slot for icon image
369 	INT16 const ImgSlot = FindFreeIconSlot();
370 
371 	// Load the icon
372 	GenericButtonIcons[ImgSlot] = AddVideoObjectFromFile(filename);
373 
374 	// Return the slot number
375 	return ImgSlot;
376 }
377 
378 
UnloadGenericButtonIcon(INT16 GenImg)379 void UnloadGenericButtonIcon(INT16 GenImg)
380 {
381 	AssertMsg(0 <= GenImg && GenImg < MAX_BUTTON_ICONS, String("Attempting to UnloadGenericButtonIcon with out of range index %d.", GenImg));
382 
383 #if defined BUTTONSYSTEM_DEBUGGING
384 	AssertMsg(GenericButtonIcons[GenImg], "Attempting to UnloadGenericButtonIcon that has no icon (already deleted).");
385 #endif
386 	if (!GenericButtonIcons[GenImg]) return;
387 	// If an icon is present in the slot, remove it.
388 	DeleteVideoObject(GenericButtonIcons[GenImg]);
389 	GenericButtonIcons[GenImg] = NULL;
390 }
391 
392 
393 // Cleans up, and shuts down the button image manager sub-system.
ShutdownButtonImageManager(void)394 static void ShutdownButtonImageManager(void)
395 {
396 	// Remove all QuickButton images
397 	FOR_EACH(BUTTON_PICS, i, ButtonPictures)
398 	{
399 		if (i->vobj != NULL) UnloadButtonImage(i);
400 	}
401 
402 	// Remove all GenericButton images
403 	if (GenericButtonOffNormal != NULL)
404 	{
405 		DeleteVideoObject(GenericButtonOffNormal);
406 		GenericButtonOffNormal = NULL;
407 	}
408 
409 	if (GenericButtonOffHilite != NULL)
410 	{
411 		DeleteVideoObject(GenericButtonOffHilite);
412 		GenericButtonOffHilite = NULL;
413 	}
414 
415 	if (GenericButtonOnNormal != NULL)
416 	{
417 		DeleteVideoObject(GenericButtonOnNormal);
418 		GenericButtonOnNormal = NULL;
419 	}
420 
421 	if (GenericButtonOnHilite != NULL)
422 	{
423 		DeleteVideoObject(GenericButtonOnHilite);
424 		GenericButtonOnHilite = NULL;
425 	}
426 
427 	GenericButtonFillColors = 0;
428 
429 	// Remove all button icons
430 	for (int x = 0; x < MAX_BUTTON_ICONS; ++x)
431 	{
432 		if (GenericButtonIcons[x] != NULL) UnloadGenericButtonIcon(x);
433 	}
434 }
435 
436 
InitButtonSystem(void)437 void InitButtonSystem(void)
438 {
439 	ButtonDestBuffer = FRAME_BUFFER;
440 
441 	// Initialize the button image manager sub-system
442 	InitializeButtonImageManager();
443 }
444 
445 
ShutdownButtonSystem(void)446 void ShutdownButtonSystem(void)
447 {
448 	// Kill off all buttons in the system
449 	FOR_EACH_BUTTON(i)
450 	{
451 		delete *i;
452 	}
453 	ShutdownButtonImageManager();
454 }
455 
456 
RemoveButtonsMarkedForDeletion(void)457 static void RemoveButtonsMarkedForDeletion(void)
458 {
459 	FOR_EACH_BUTTON(i)
460 	{
461 		if ((*i)->uiFlags & BUTTON_DELETION_PENDING) delete *i;
462 	}
463 }
464 
465 
RemoveButton(GUIButtonRef & btn)466 void RemoveButton(GUIButtonRef& btn)
467 {
468 	INT32 const btn_id = btn.ID();
469 	btn.Reset();
470 
471 	CHECKV(0 < btn_id && btn_id < MAX_BUTTONS); // XXX HACK000C
472 	AssertMsg(0 < btn_id && btn_id < MAX_BUTTONS, String("ButtonID %d is out of range.", btn_id));
473 	GUI_BUTTON* const b = ButtonList[btn_id];
474 	CHECKV(b); // XXX HACK000C
475 	AssertMsg(b, String("Accessing non-existent button %d.", btn_id));
476 
477 	/* If we happen to be in the middle of a callback, and attempt to delete a
478 	 * button, like deleting a node during list processing, then we delay it till
479 	 * after the callback is completed.
480 	 */
481 	if (gfDelayButtonDeletion)
482 	{
483 		b->uiFlags |= BUTTON_DELETION_PENDING;
484 		gfPendingButtonDeletion = TRUE;
485 		return;
486 	}
487 
488 	delete b;
489 }
490 
491 
492 // Finds the next available button slot.
GetNextButtonNumber(void)493 static INT32 GetNextButtonNumber(void)
494 {
495 	/* Never hand out ID 0.  Slot 0 is always a null pointer */
496 	for (INT32 x = 1; x < MAX_BUTTONS; x++)
497 	{
498 		if (ButtonList[x] == NULL) return x;
499 	}
500 	throw std::runtime_error("No more button slots");
501 }
502 
503 
504 static void QuickButtonCallbackMButn(MOUSE_REGION* reg, INT32 reason);
505 static void QuickButtonCallbackMMove(MOUSE_REGION* reg, INT32 reason);
506 
507 
GUI_BUTTON(UINT32 const flags,INT16 const left,INT16 const top,INT16 const width,INT16 const height,INT8 const priority,GUI_CALLBACK const click,GUI_CALLBACK const move)508 GUI_BUTTON::GUI_BUTTON(UINT32 const flags, INT16 const left, INT16 const top, INT16 const width, INT16 const height, INT8 const priority, GUI_CALLBACK const click, GUI_CALLBACK const move) :
509 	IDNum(GetNextButtonNumber()),
510 	image(0),
511 	Area(left, top, width, height, priority, MSYS_STARTING_CURSORVAL, QuickButtonCallbackMMove, QuickButtonCallbackMButn),
512 	ClickCallback(click),
513 	MoveCallback(move),
514 	uiFlags(BUTTON_DIRTY | BUTTON_ENABLED | flags),
515 	uiOldFlags(0),
516 	bDisabledStyle(GUI_BUTTON::DISABLED_STYLE_DEFAULT),
517 	codepoints(),
518 	usFont(0),
519 	sForeColor(0),
520 	sShadowColor(-1),
521 	sForeColorDown(-1),
522 	sShadowColorDown(-1),
523 	sForeColorHilited(-1),
524 	sShadowColorHilited(-1),
525 	bJustification(GUI_BUTTON::TEXT_CENTER),
526 	bTextXOffset(-1),
527 	bTextYOffset(-1),
528 	bTextXSubOffSet(-1),
529 	bTextYSubOffSet(-1),
530 	fShiftText(TRUE),
531 	sWrappedWidth(-1),
532 	icon(0),
533 	usIconIndex(-1),
534 	bIconXOffset(-1),
535 	bIconYOffset(-1),
536 	fShiftImage(TRUE),
537 	ubToggleButtonActivated(FALSE),
538 	ubSoundSchemeID(BUTTON_SOUND_SCHEME_NONE)
539 {
540 	AssertMsg(left >= 0 && top >= 0 && width >= 0 && height >= 0, String("Attempting to create button with invalid coordinates %dx%d+%dx%d", left, top, width, height));
541 
542 	Area.SetUserPtr(this);
543 
544 #ifdef BUTTONSYSTEM_DEBUGGING
545 	AssertFailIfIdenticalButtonAttributesFound(this);
546 #endif
547 
548 	ButtonList[IDNum] = this;
549 
550 	SpecifyButtonSoundScheme(this, BUTTON_SOUND_SCHEME_GENERIC);
551 }
552 
553 
~GUI_BUTTON()554 GUI_BUTTON::~GUI_BUTTON()
555 {
556 	if (this == gpAnchoredButton)     gpAnchoredButton     = 0;
557 	if (this == gpPrevAnchoredButton) gpPrevAnchoredButton = 0;
558 
559 	ButtonList[IDNum] = 0;
560 
561 	if (uiFlags & BUTTON_SELFDELETE_IMAGE)
562 	{
563 		/* Checkboxes and simple create buttons have their own graphics associated
564 		 * with them, and it is handled internally.  We delete it here.  This
565 		 * provides the advantage of less micromanagement, but with the
566 		 * disadvantage of wasting more memory if you have lots of buttons using the
567 		 * same graphics.
568 		 */
569 		UnloadButtonImage(image);
570 	}
571 }
572 
573 
574 static void DefaultMoveCallback(GUI_BUTTON* btn, INT32 reason);
575 
576 
CreateIconButton(INT16 Icon,INT16 IconIndex,INT16 xloc,INT16 yloc,INT16 w,INT16 h,INT16 Priority,GUI_CALLBACK ClickCallback)577 GUIButtonRef CreateIconButton(INT16 Icon, INT16 IconIndex, INT16 xloc, INT16 yloc, INT16 w, INT16 h, INT16 Priority, GUI_CALLBACK ClickCallback)
578 {
579 	// if button size is too small, adjust it.
580 	if (w < 4) w = 4;
581 	if (h < 3) h = 3;
582 
583 	GUI_BUTTON* const b = new GUI_BUTTON(BUTTON_GENERIC, xloc, yloc, w, h, Priority, ClickCallback, DefaultMoveCallback);
584 	b->icon        = GenericButtonIcons[Icon];
585 	b->usIconIndex = IconIndex;
586 	return b;
587 }
588 
589 
CreateTextButton(const ST::string & str,SGPFont font,INT16 sForeColor,INT16 sShadowColor,INT16 xloc,INT16 yloc,INT16 w,INT16 h,INT16 Priority,GUI_CALLBACK ClickCallback)590 GUIButtonRef CreateTextButton(const ST::string& str, SGPFont font, INT16 sForeColor, INT16 sShadowColor, INT16 xloc, INT16 yloc, INT16 w, INT16 h, INT16 Priority, GUI_CALLBACK ClickCallback)
591 {
592 	// if button size is too small, adjust it.
593 	if (w < 4) w = 4;
594 	if (h < 3) h = 3;
595 
596 	GUI_BUTTON* const b = new GUI_BUTTON(BUTTON_GENERIC, xloc, yloc, w, h, Priority, ClickCallback, DefaultMoveCallback);
597 	b->codepoints   = str.to_utf32();
598 	b->usFont       = font;
599 	b->sForeColor   = sForeColor;
600 	b->sShadowColor = sShadowColor;
601 	return b;
602 }
603 
604 
CreateHotSpot(INT16 xloc,INT16 yloc,INT16 Width,INT16 Height,INT16 Priority,GUI_CALLBACK ClickCallback)605 GUIButtonRef CreateHotSpot(INT16 xloc, INT16 yloc, INT16 Width, INT16 Height, INT16 Priority, GUI_CALLBACK ClickCallback)
606 {
607 	return new GUI_BUTTON(BUTTON_HOT_SPOT, xloc, yloc, Width, Height, Priority, ClickCallback, DefaultMoveCallback);
608 }
609 
610 
QuickCreateButtonInternal(BUTTON_PICS * const pics,const INT16 xloc,const INT16 yloc,const INT32 Type,const INT16 Priority,const GUI_CALLBACK MoveCallback,const GUI_CALLBACK ClickCallback)611 static GUIButtonRef QuickCreateButtonInternal(BUTTON_PICS* const pics, const INT16 xloc, const INT16 yloc, const INT32 Type, const INT16 Priority, const GUI_CALLBACK MoveCallback, const GUI_CALLBACK ClickCallback)
612 {
613 	// Is there a QuickButton image in the given image slot?
614 	if (!pics->vobj)
615 	{
616 		throw std::runtime_error("QuickCreateButton: Invalid button image");
617 	}
618 
619 	GUI_BUTTON* const b = new GUI_BUTTON((Type & (BUTTON_CHECKBOX | BUTTON_NEWTOGGLE)) | BUTTON_QUICK, xloc, yloc, pics->max.w, pics->max.h, Priority, ClickCallback, MoveCallback);
620 	b->image = pics;
621 	return b;
622 }
623 
624 
QuickCreateButton(BUTTON_PICS * const image,const INT16 x,const INT16 y,const INT16 priority,const GUI_CALLBACK click)625 GUIButtonRef QuickCreateButton(BUTTON_PICS* const image, const INT16 x, const INT16 y, const INT16 priority, const GUI_CALLBACK click)
626 {
627 	return QuickCreateButtonInternal(image, x, y, BUTTON_TOGGLE, priority, DefaultMoveCallback, click);
628 }
629 
630 
QuickCreateButtonNoMove(BUTTON_PICS * const image,const INT16 x,const INT16 y,const INT16 priority,const GUI_CALLBACK click)631 GUIButtonRef QuickCreateButtonNoMove(BUTTON_PICS* const image, const INT16 x, const INT16 y, const INT16 priority, const GUI_CALLBACK click)
632 {
633 	return QuickCreateButtonInternal(image, x, y, BUTTON_TOGGLE, priority, MSYS_NO_CALLBACK, click);
634 }
635 
636 
QuickCreateButtonToggle(BUTTON_PICS * const image,const INT16 x,const INT16 y,const INT16 priority,const GUI_CALLBACK click)637 GUIButtonRef QuickCreateButtonToggle(BUTTON_PICS* const image, const INT16 x, const INT16 y, const INT16 priority, const GUI_CALLBACK click)
638 {
639 	return QuickCreateButtonInternal(image, x, y, BUTTON_NEWTOGGLE, priority, MSYS_NO_CALLBACK, click);
640 }
641 
642 
QuickCreateButtonImg(const char * gfx,INT32 grayed,INT32 off_normal,INT32 off_hilite,INT32 on_normal,INT32 on_hilite,INT16 x,INT16 y,INT16 priority,GUI_CALLBACK click)643 GUIButtonRef QuickCreateButtonImg(const char* gfx, INT32 grayed, INT32 off_normal, INT32 off_hilite, INT32 on_normal, INT32 on_hilite, INT16 x, INT16 y, INT16 priority, GUI_CALLBACK click)
644 {
645 	BUTTON_PICS* const img = LoadButtonImage(gfx, grayed, off_normal, off_hilite, on_normal, on_hilite);
646 	GUIButtonRef const btn = QuickCreateButton(img, x, y, priority, click);
647 	btn->uiFlags |= BUTTON_SELFDELETE_IMAGE;
648 	return btn;
649 }
650 
651 
QuickCreateButtonImg(char const * const gfx,INT32 const off_normal,INT32 const on_normal,INT16 const x,INT16 const y,INT16 const priority,GUI_CALLBACK const click)652 GUIButtonRef QuickCreateButtonImg(char const* const gfx, INT32 const off_normal, INT32 const on_normal, INT16 const x, INT16 const y, INT16 const priority, GUI_CALLBACK const click)
653 {
654 	return QuickCreateButtonImg(gfx, -1, off_normal, -1, on_normal, -1, x, y, priority, click);
655 }
656 
657 
CreateIconAndTextButton(BUTTON_PICS * Image,const ST::string & str,SGPFont font,INT16 sForeColor,INT16 sShadowColor,INT16 sForeColorDown,INT16 sShadowColorDown,INT16 xloc,INT16 yloc,INT16 Priority,GUI_CALLBACK ClickCallback)658 GUIButtonRef CreateIconAndTextButton(BUTTON_PICS* Image, const ST::string& str, SGPFont font, INT16 sForeColor, INT16 sShadowColor, INT16 sForeColorDown, INT16 sShadowColorDown, INT16 xloc, INT16 yloc, INT16 Priority, GUI_CALLBACK ClickCallback)
659 {
660 	GUIButtonRef const b = QuickCreateButton(Image, xloc, yloc, Priority, ClickCallback);
661 	b->codepoints       = str.to_utf32();
662 	b->usFont           = font;
663 	b->sForeColor       = sForeColor;
664 	b->sShadowColor     = sShadowColor;
665 	b->sForeColorDown   = sForeColorDown;
666 	b->sShadowColorDown = sShadowColorDown;
667 	return b;
668 }
669 
670 
CreateLabel(const ST::string & str,SGPFont font,INT16 forecolor,INT16 shadowcolor,INT16 x,INT16 y,INT16 w,INT16 h,INT16 priority)671 GUIButtonRef CreateLabel(const ST::string& str, SGPFont font, INT16 forecolor, INT16 shadowcolor, INT16 x, INT16 y, INT16 w, INT16 h, INT16 priority)
672 {
673 	GUIButtonRef const btn = CreateTextButton(str, font, forecolor, shadowcolor, x, y, w, h, priority, NULL);
674 	btn->SpecifyDisabledStyle(GUI_BUTTON::DISABLED_STYLE_NONE);
675 	DisableButton(btn);
676 	return btn;
677 }
678 
679 
SpecifyText(const ST::string & str)680 void GUI_BUTTON::SpecifyText(const ST::string& str)
681 {
682 	this->codepoints = str.to_utf32();
683 	uiFlags |= BUTTON_DIRTY;
684 }
685 
686 
SpecifyDownTextColors(INT16 const fore_colour_down,INT16 const shadow_colour_down)687 void GUI_BUTTON::SpecifyDownTextColors(INT16 const fore_colour_down, INT16 const shadow_colour_down)
688 {
689 	sForeColorDown    = fore_colour_down;
690 	sShadowColorDown  = shadow_colour_down;
691 	uiFlags          |= BUTTON_DIRTY;
692 }
693 
694 
SpecifyHilitedTextColors(INT16 const fore_colour_highlighted,INT16 const shadow_colour_highlighted)695 void GUI_BUTTON::SpecifyHilitedTextColors(INT16 const fore_colour_highlighted, INT16 const shadow_colour_highlighted)
696 {
697 	sForeColorHilited    = fore_colour_highlighted;
698 	sShadowColorHilited  = shadow_colour_highlighted;
699 	uiFlags             |= BUTTON_DIRTY;
700 }
701 
702 
SpecifyTextJustification(Justification const j)703 void GUI_BUTTON::SpecifyTextJustification(Justification const j)
704 {
705 	bJustification  = j;
706 	uiFlags        |= BUTTON_DIRTY;
707 }
708 
709 
SpecifyGeneralTextAttributes(const ST::string & str,SGPFont font,INT16 fore_colour,INT16 shadow_colour)710 void GUI_BUTTON::SpecifyGeneralTextAttributes(const ST::string& str, SGPFont font, INT16 fore_colour, INT16 shadow_colour)
711 {
712 	SpecifyText(str);
713 	usFont        = font;
714 	sForeColor    = fore_colour;
715 	sShadowColor  = shadow_colour;
716 	uiFlags      |= BUTTON_DIRTY;
717 }
718 
719 
SpecifyTextOffsets(INT8 const text_x_offset,INT8 const text_y_offset,BOOLEAN const shift_text)720 void GUI_BUTTON::SpecifyTextOffsets(INT8 const text_x_offset, INT8 const text_y_offset, BOOLEAN const shift_text)
721 {
722 	bTextXOffset = text_x_offset;
723 	bTextYOffset = text_y_offset;
724 	fShiftText   = shift_text;
725 }
726 
727 
SpecifyTextSubOffsets(INT8 const text_x_offset,INT8 const text_y_offset,BOOLEAN const shift_text)728 void GUI_BUTTON::SpecifyTextSubOffsets(INT8 const text_x_offset, INT8 const text_y_offset, BOOLEAN const shift_text)
729 {
730 	bTextXSubOffSet = text_x_offset;
731 	bTextYSubOffSet = text_y_offset;
732 	fShiftText      = shift_text;
733 }
734 
735 
SpecifyTextWrappedWidth(INT16 const wrapped_width)736 void GUI_BUTTON::SpecifyTextWrappedWidth(INT16 const wrapped_width)
737 {
738 	sWrappedWidth = wrapped_width;
739 }
740 
741 
SpecifyDisabledStyle(DisabledStyle const style)742 void GUI_BUTTON::SpecifyDisabledStyle(DisabledStyle const style)
743 {
744 	bDisabledStyle = style;
745 }
746 
747 
SpecifyIcon(SGPVObject const * const icon_,UINT16 const usVideoObjectIndex,INT8 const bXOffset,INT8 const bYOffset,BOOLEAN const)748 void GUI_BUTTON::SpecifyIcon(SGPVObject const* const icon_, UINT16 const usVideoObjectIndex, INT8 const bXOffset, INT8 const bYOffset, BOOLEAN const)
749 {
750 	icon        = icon_;
751 	usIconIndex = usVideoObjectIndex;
752 
753 	if (!icon_) return;
754 
755 	bIconXOffset = bXOffset;
756 	bIconYOffset = bYOffset;
757 	fShiftImage  = TRUE;
758 
759 	uiFlags |= BUTTON_DIRTY;
760 }
761 
762 
AllowDisabledFastHelp()763 void GUI_BUTTON::AllowDisabledFastHelp()
764 {
765 	Area.uiFlags |= MSYS_ALLOW_DISABLED_FASTHELP;
766 }
767 
768 
SetFastHelpText(const ST::string & str)769 void GUI_BUTTON::SetFastHelpText(const ST::string& str)
770 {
771 	Area.SetFastHelpText(str);
772 }
773 
774 
775 /* Dispatches all button callbacks for mouse movement. This function gets
776  * called by the Mouse System. *DO NOT CALL DIRECTLY*
777  */
QuickButtonCallbackMMove(MOUSE_REGION * reg,INT32 reason)778 static void QuickButtonCallbackMMove(MOUSE_REGION* reg, INT32 reason)
779 {
780 	Assert(reg != NULL);
781 	GUI_BUTTON* const b = reg->GetUserPtr<GUI_BUTTON>();
782 
783 	// ATE: New stuff for toggle buttons that work with new Win95 paradigm
784 	if (b->uiFlags & BUTTON_NEWTOGGLE &&
785 			reason & MSYS_CALLBACK_REASON_LOST_MOUSE &&
786 			b->ubToggleButtonActivated)
787 	{
788 		b->uiFlags ^= BUTTON_CLICKED_ON;
789 		b->ubToggleButtonActivated = FALSE;
790 	}
791 
792 	if (!b->Enabled()) return;
793 
794 	if (reason & (MSYS_CALLBACK_REASON_LOST_MOUSE | MSYS_CALLBACK_REASON_GAIN_MOUSE))
795 	{
796 		b->uiFlags |= BUTTON_DIRTY;
797 	}
798 
799 	if (b->MoveCallback) b->MoveCallback(b, reason);
800 }
801 
802 
803 /* Dispatches all button callbacks for button presses. This function is called
804  * by the Mouse System. *DO NOT CALL DIRECTLY*
805  */
QuickButtonCallbackMButn(MOUSE_REGION * reg,INT32 reason)806 static void QuickButtonCallbackMButn(MOUSE_REGION* reg, INT32 reason)
807 {
808 	Assert(reg != NULL);
809 	GUI_BUTTON* const b = reg->GetUserPtr<GUI_BUTTON>();
810 
811 	// ATE: New stuff for toggle buttons that work with new Win95 paradigm
812 	if (!b->Enabled())
813 	{
814 		// Should we play a sound if clicked on while disabled?
815 		if (b->ubSoundSchemeID &&
816 				reason & (MSYS_CALLBACK_REASON_LBUTTON_DWN | MSYS_CALLBACK_REASON_RBUTTON_DWN))
817 		{
818 			PlayButtonSound(b, BUTTON_SOUND_DISABLED_CLICK);
819 		}
820 		return;
821 	}
822 
823 	bool    StateBefore = b->Clicked();
824 	bool    StateAfter  = true; // XXX HACK000E
825 
826 	if (b->uiFlags & BUTTON_NEWTOGGLE)
827 	{
828 		if (reason & MSYS_CALLBACK_REASON_LBUTTON_DWN)
829 		{
830 			if (!b->ubToggleButtonActivated)
831 			{
832 				b->uiFlags ^= BUTTON_CLICKED_ON;
833 				b->ubToggleButtonActivated = TRUE;
834 			}
835 		}
836 		else if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
837 		{
838 			b->ubToggleButtonActivated = FALSE;
839 		}
840 	}
841 
842 	/* Kris:
843 	 * Set the anchored button incase the user moves mouse off region while still
844 	 * holding down the button, but only if the button is up.  In Win95, buttons
845 	 * that are already down, and anchored never change state, unless you release
846 	 * the mouse in the button area.
847 	 */
848 	if (b->MoveCallback == DefaultMoveCallback)
849 	{
850 		if (reason & MSYS_CALLBACK_REASON_LBUTTON_DWN)
851 		{
852 			gpAnchoredButton = b;
853 			gfAnchoredState = StateBefore;
854 			b->uiFlags |= BUTTON_CLICKED_ON;
855 		}
856 		else if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
857 		{
858 			b->uiFlags &= ~BUTTON_CLICKED_ON;
859 		}
860 	}
861 	else if (b->uiFlags & BUTTON_CHECKBOX)
862 	{
863 		if (reason & MSYS_CALLBACK_REASON_LBUTTON_DWN)
864 		{
865 			/* The check box button gets anchored, though it doesn't actually use the
866 			 * anchoring move callback.  The effect is different, we don't want to
867 			 * toggle the button state, but we do want to anchor this button so that
868 			 * we don't effect any other buttons while we move the mouse around in
869 			 * anchor mode.
870 			 */
871 			gpAnchoredButton = b;
872 			gfAnchoredState = StateBefore;
873 
874 			/* Trick the before state of the button to be different so the sound will
875 			 * play properly as checkbox buttons are processed differently.
876 			 */
877 			StateBefore = !b->Clicked();
878 			StateAfter  = !StateBefore;
879 		}
880 		else if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
881 		{
882 			b->uiFlags ^= BUTTON_CLICKED_ON; //toggle the checkbox state upon release inside button area.
883 			/* Trick the before state of the button to be different so the sound will
884 			 * play properly as checkbox buttons are processed differently.
885 			 */
886 			StateBefore = !b->Clicked();
887 			StateAfter  = !StateBefore;
888 		}
889 	}
890 
891 	// If there is a callback function with this button, call it
892 	if (b->ClickCallback != NULL)
893 	{
894 		/* Kris:  January 6, 1998
895 		 * Added these checks to avoid a case where it was possible to process a
896 		 * leftbuttonup message when the button wasn't anchored, and should have
897 		 * been.
898 		 */
899 		gfDelayButtonDeletion = TRUE;
900 		if (!(reason & MSYS_CALLBACK_REASON_LBUTTON_UP) ||
901 				b->MoveCallback != DefaultMoveCallback ||
902 				gpPrevAnchoredButton == b)
903 		{
904 			b->ClickCallback(b, reason);
905 		}
906 		gfDelayButtonDeletion = FALSE;
907 	}
908 	else if (reason & MSYS_CALLBACK_REASON_LBUTTON_DWN)
909 	{
910 		// Otherwise, do default action with this button.
911 		b->uiFlags ^= BUTTON_CLICKED_ON;
912 	}
913 
914 	if (b->uiFlags & BUTTON_CHECKBOX)
915 	{
916 		StateAfter = b->Clicked();
917 	}
918 
919 	// Play sounds for this enabled button (disabled sounds have already been done)
920 	if (b->ubSoundSchemeID && b->Enabled())
921 	{
922 		if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
923 		{
924 			if (StateBefore && !StateAfter)
925 			{
926 				PlayButtonSound(b, BUTTON_SOUND_CLICKED_OFF);
927 			}
928 		}
929 		else if (reason & MSYS_CALLBACK_REASON_LBUTTON_DWN)
930 		{
931 			if (!StateBefore && StateAfter)
932 			{
933 				PlayButtonSound(b, BUTTON_SOUND_CLICKED_ON);
934 			}
935 		}
936 	}
937 
938 	if (StateBefore != StateAfter)
939 	{
940 		InvalidateRegion(b->X(), b->Y(), b->BottomRightX(), b->BottomRightY());
941 	}
942 
943 	if (gfPendingButtonDeletion) RemoveButtonsMarkedForDeletion();
944 }
945 
946 
947 static void DrawButtonFromPtr(GUI_BUTTON* b);
948 
949 
RenderButtons(void)950 void RenderButtons(void)
951 {
952 	SaveFontSettings();
953 	FOR_EACH_BUTTON(i)
954 	{
955 		// If the button exists, and it's not owned by another object, draw it
956 		// Kris:  and make sure that the button isn't hidden.
957 		GUI_BUTTON* const b = *i;
958 		if (!(b->Area.uiFlags & MSYS_REGION_ENABLED)) continue;
959 
960 		if ((b->uiFlags ^ b->uiOldFlags) & (BUTTON_CLICKED_ON | BUTTON_ENABLED))
961 		{
962 			// Something is different, set dirty!
963 			b->uiFlags |= BUTTON_DIRTY;
964 		}
965 
966 		// Set old flags
967 		b->uiOldFlags = b->uiFlags;
968 
969 		if (b->uiFlags & BUTTON_FORCE_UNDIRTY)
970 		{
971 			b->uiFlags &= ~BUTTON_DIRTY;
972 			b->uiFlags &= ~BUTTON_FORCE_UNDIRTY;
973 		}
974 
975 		// Check if we need to update!
976 		if (b->uiFlags & BUTTON_DIRTY)
977 		{
978 			// Turn off dirty flag
979 			b->uiFlags &= ~BUTTON_DIRTY;
980 			DrawButtonFromPtr(b);
981 
982 			InvalidateRegion(b->X(), b->Y(), b->BottomRightX(), b->BottomRightY());
983 		}
984 	}
985 
986 	RestoreFontSettings();
987 }
988 
989 
MarkAButtonDirty(GUIButtonRef const b)990 void MarkAButtonDirty(GUIButtonRef const b)
991 {
992 	// surgical dirtying -> marks a user specified button dirty, without dirty the whole lot of them
993 	CHECKV(b != NULL); // XXX HACK000C
994 	b->uiFlags |= BUTTON_DIRTY;
995 }
996 
997 
MarkButtonsDirty(void)998 void MarkButtonsDirty(void)
999 {
1000 	FOR_EACH_BUTTON(i)
1001 	{
1002 		(*i)->uiFlags |= BUTTON_DIRTY;
1003 	}
1004 }
1005 
1006 
UnMarkButtonDirty(GUIButtonRef const b)1007 void UnMarkButtonDirty(GUIButtonRef const b)
1008 {
1009 	CHECKV(b != NULL); // XXX HACK000C
1010 	b->uiFlags &= ~BUTTON_DIRTY;
1011 }
1012 
1013 
UnmarkButtonsDirty(void)1014 void UnmarkButtonsDirty(void)
1015 {
1016 	FOR_EACH_BUTTON(i)
1017 	{
1018 		UnMarkButtonDirty(*i);
1019 	}
1020 }
1021 
1022 
ForceButtonUnDirty(GUIButtonRef const b)1023 void ForceButtonUnDirty(GUIButtonRef const b)
1024 {
1025 	CHECKV(b != NULL); // XXX HACK000C
1026 	b->uiFlags &= ~BUTTON_DIRTY;
1027 	b->uiFlags |= BUTTON_FORCE_UNDIRTY;
1028 }
1029 
1030 
Draw()1031 void GUI_BUTTON::Draw()
1032 {
1033 	if (!codepoints.empty()) SaveFontSettings();
1034 	if (Area.uiFlags & MSYS_REGION_ENABLED) DrawButtonFromPtr(this);
1035 	if (!codepoints.empty()) RestoreFontSettings();
1036 }
1037 
1038 
1039 static void DrawCheckBoxButton(const GUI_BUTTON* b);
1040 static void DrawGenericButton( const GUI_BUTTON* b);
1041 static void DrawHatchOnButton( const GUI_BUTTON* b);
1042 static void DrawIconOnButton(  const GUI_BUTTON* b);
1043 static void DrawQuickButton(   const GUI_BUTTON* b);
1044 static void DrawShadeOnButton( const GUI_BUTTON* b);
1045 static void DrawTextOnButton(  const GUI_BUTTON* b);
1046 
1047 
1048 // Given a pointer to a GUI_BUTTON structure, draws the button on the screen.
DrawButtonFromPtr(GUI_BUTTON * b)1049 static void DrawButtonFromPtr(GUI_BUTTON* b)
1050 {
1051 	// Draw the appropriate button according to button type
1052 	gbDisabledButtonStyle = GUI_BUTTON::DISABLED_STYLE_NONE;
1053 	switch (b->uiFlags & BUTTON_TYPES)
1054 	{
1055 		case BUTTON_QUICK:    DrawQuickButton(b);    break;
1056 		case BUTTON_GENERIC:  DrawGenericButton(b);  break;
1057 		case BUTTON_CHECKBOX: DrawCheckBoxButton(b); break;
1058 
1059 		case BUTTON_HOT_SPOT:
1060 			return; // hotspots don't have text, but if you want to, change this to a break!
1061 	}
1062 	if (b->icon)   DrawIconOnButton(b);
1063 	if (!b->codepoints.empty()) DrawTextOnButton(b);
1064 	/* If the button is disabled, and a style has been calculated, then draw the
1065 	 * style last.
1066 	 */
1067 	switch (gbDisabledButtonStyle)
1068 	{
1069 		case GUI_BUTTON::DISABLED_STYLE_HATCHED: DrawHatchOnButton(b); break;
1070 		case GUI_BUTTON::DISABLED_STYLE_SHADED:  DrawShadeOnButton(b); break;
1071 	}
1072 }
1073 
1074 
1075 // Draws a QuickButton type button on the screen.
DrawQuickButton(const GUI_BUTTON * b)1076 static void DrawQuickButton(const GUI_BUTTON* b)
1077 {
1078 	const BUTTON_PICS* const pics = b->image;
1079 
1080 	INT32 UseImage = 0;
1081 	if (b->Enabled())
1082 	{
1083 		if (b->Clicked())
1084 		{
1085 			// Is the mouse over this area, and we have a hilite image?
1086 			if (b->Area.uiFlags & MSYS_MOUSE_IN_AREA &&
1087 					gfRenderHilights &&
1088 					pics->OnHilite != -1)
1089 			{
1090 				UseImage = pics->OnHilite;
1091 			}
1092 			else if (pics->OnNormal != -1)
1093 			{
1094 				UseImage = pics->OnNormal;
1095 			}
1096 		}
1097 		else
1098 		{
1099 			// Is the mouse over the button, and do we have hilite image?
1100 			if (b->Area.uiFlags & MSYS_MOUSE_IN_AREA &&
1101 					gfRenderHilights &&
1102 					pics->OffHilite != -1)
1103 			{
1104 				UseImage = pics->OffHilite;
1105 			}
1106 			else if (pics->OffNormal != -1)
1107 			{
1108 				UseImage = pics->OffNormal;
1109 			}
1110 		}
1111 	}
1112 	else if (pics->Grayed != -1)
1113 	{
1114 		// Button is diabled so use the "Grayed-out" image
1115 		UseImage = pics->Grayed;
1116 	}
1117 	else
1118 	{
1119 		UseImage = pics->OffNormal;
1120 		switch (b->bDisabledStyle)
1121 		{
1122 			case GUI_BUTTON::DISABLED_STYLE_DEFAULT:
1123 				gbDisabledButtonStyle = !b->codepoints.empty() ?
1124 					GUI_BUTTON::DISABLED_STYLE_SHADED :
1125 					GUI_BUTTON::DISABLED_STYLE_HATCHED;
1126 				break;
1127 
1128 			case GUI_BUTTON::DISABLED_STYLE_HATCHED:
1129 			case GUI_BUTTON::DISABLED_STYLE_SHADED:
1130 				gbDisabledButtonStyle = b->bDisabledStyle;
1131 				break;
1132 		}
1133 	}
1134 
1135 	BltVideoObject(ButtonDestBuffer, pics->vobj, UseImage, b->X(), b->Y());
1136 }
1137 
1138 
DrawHatchOnButton(const GUI_BUTTON * b)1139 static void DrawHatchOnButton(const GUI_BUTTON* b)
1140 {
1141 	SGPRect ClipRect;
1142 	ClipRect.iLeft   = b->X();
1143 	ClipRect.iRight  = b->BottomRightX() - 1;
1144 	ClipRect.iTop    = b->Y();
1145 	ClipRect.iBottom = b->BottomRightY() - 1;
1146 	SGPVSurface::Lock l(ButtonDestBuffer);
1147 	Blt16BPPBufferHatchRect(l.Buffer<UINT16>(), l.Pitch(), &ClipRect);
1148 }
1149 
1150 
DrawShadeOnButton(const GUI_BUTTON * b)1151 static void DrawShadeOnButton(const GUI_BUTTON* b)
1152 {
1153 	ButtonDestBuffer->ShadowRect(b->X(), b->Y(), b->BottomRightX(), b->BottomRightY());
1154 }
1155 
1156 
DrawCheckBoxOnOff(BOOLEAN const on)1157 void GUI_BUTTON::DrawCheckBoxOnOff(BOOLEAN const on)
1158 {
1159 	BOOLEAN const fLeftButtonState = gfLeftButtonState;
1160 
1161 	gfLeftButtonState = on;
1162 	Area.uiFlags |= MSYS_MOUSE_IN_AREA;
1163 	Draw();
1164 
1165 	gfLeftButtonState = fLeftButtonState;
1166 }
1167 
1168 
DrawCheckBoxButton(const GUI_BUTTON * b)1169 static void DrawCheckBoxButton(const GUI_BUTTON *b)
1170 {
1171 	const BUTTON_PICS* const pics = b->image;
1172 
1173 	INT32 UseImage = 0;
1174 	if (b->Enabled())
1175 	{
1176 		if (b->Clicked())
1177 		{
1178 			// Is the mouse over this area, and we have a hilite image?
1179 			if (b->Area.uiFlags & MSYS_MOUSE_IN_AREA &&
1180 					gfRenderHilights &&
1181 					gfLeftButtonState &&
1182 					pics->OnHilite != -1)
1183 			{
1184 				UseImage = pics->OnHilite;
1185 			}
1186 			else if (pics->OnNormal != -1)
1187 			{
1188 				UseImage = pics->OnNormal;
1189 			}
1190 		}
1191 		else
1192 		{
1193 			// Is the mouse over the button, and do we have hilite image?
1194 			if (b->Area.uiFlags & MSYS_MOUSE_IN_AREA &&
1195 					gfRenderHilights &&
1196 					gfLeftButtonState &&
1197 					pics->OffHilite != -1)
1198 			{
1199 				UseImage = pics->OffHilite;
1200 			}
1201 			else if (pics->OffNormal != -1)
1202 			{
1203 				UseImage = pics->OffNormal;
1204 			}
1205 		}
1206 	}
1207 	else if (pics->Grayed != -1)
1208 	{
1209 		// Button is disabled so use the "Grayed-out" image
1210 		UseImage = pics->Grayed;
1211 	}
1212 	else //use the disabled style
1213 	{
1214 		if (b->Clicked())
1215 		{
1216 			UseImage = pics->OnHilite;
1217 		}
1218 		else
1219 		{
1220 			UseImage = pics->OffHilite;
1221 		}
1222 		switch (b->bDisabledStyle)
1223 		{
1224 			case GUI_BUTTON::DISABLED_STYLE_DEFAULT:
1225 				gbDisabledButtonStyle = GUI_BUTTON::DISABLED_STYLE_HATCHED;
1226 				break;
1227 
1228 			case GUI_BUTTON::DISABLED_STYLE_HATCHED:
1229 			case GUI_BUTTON::DISABLED_STYLE_SHADED:
1230 				gbDisabledButtonStyle = b->bDisabledStyle;
1231 				break;
1232 		}
1233 	}
1234 
1235 	BltVideoObject(ButtonDestBuffer, pics->vobj, UseImage, b->X(), b->Y());
1236 }
1237 
1238 
DrawIconOnButton(const GUI_BUTTON * b)1239 static void DrawIconOnButton(const GUI_BUTTON* b)
1240 {
1241 	if (!b->icon) return;
1242 
1243 	// Get width and height of button area
1244 	INT32 const width  = b->W();
1245 	INT32 const height = b->H();
1246 
1247 	// Compute viewable area (inside borders)
1248 	SGPRect NewClip;
1249 	NewClip.iLeft   = b->X() + 3;
1250 	NewClip.iRight  = b->X() + width - 3;
1251 	NewClip.iTop    = b->Y() + 2;
1252 	NewClip.iBottom = b->Y() + height - 2;
1253 
1254 	// Get Icon's blit start coordinates
1255 	INT32 IconX = NewClip.iLeft;
1256 	INT32 IconY = NewClip.iTop;
1257 
1258 	// Get current clip area
1259 	SGPRect OldClip;
1260 	GetClippingRect(&OldClip);
1261 
1262 	// Clip button's viewable area coords to screen
1263 	if (NewClip.iLeft < OldClip.iLeft) NewClip.iLeft = OldClip.iLeft;
1264 
1265 	// Is button right off the right side of the screen?
1266 	if (NewClip.iLeft > OldClip.iRight) return;
1267 
1268 	if (NewClip.iRight > OldClip.iRight) NewClip.iRight = OldClip.iRight;
1269 
1270 	// Is button completely off the left side of the screen?
1271 	if (NewClip.iRight < OldClip.iLeft) return;
1272 
1273 	if (NewClip.iTop < OldClip.iTop) NewClip.iTop = OldClip.iTop;
1274 
1275 	// Are we right off the bottom of the screen?
1276 	if (NewClip.iTop > OldClip.iBottom) return;
1277 
1278 	if (NewClip.iBottom > OldClip.iBottom) NewClip.iBottom = OldClip.iBottom;
1279 
1280 	// Are we off the top?
1281 	if (NewClip.iBottom < OldClip.iTop) return;
1282 
1283 	// Did we clip the viewable area out of existance?
1284 	if (NewClip.iRight <= NewClip.iLeft || NewClip.iBottom <= NewClip.iTop) return;
1285 
1286 	// Get the width and height of the icon itself
1287 	SGPVObject  const* const hvObject = b->icon;
1288 	ETRLEObject const&       pTrav    = hvObject->SubregionProperties(b->usIconIndex);
1289 
1290 	/* Compute coordinates for centering the icon on the button or use the offset
1291 	 * system.
1292 	 */
1293 	INT32 xp;
1294 	if (b->bIconXOffset == -1)
1295 	{
1296 		const INT32 IconW = pTrav.usWidth  + pTrav.sOffsetX;
1297 		xp = IconX + (width - 6 - IconW) / 2;
1298 	}
1299 	else
1300 	{
1301 		xp = b->X() + b->bIconXOffset;
1302 	}
1303 
1304 	INT32 yp;
1305 	if (b->bIconYOffset == -1)
1306 	{
1307 		const INT32 IconH = pTrav.usHeight + pTrav.sOffsetY;
1308 		yp = IconY + (height - 4 - IconH) / 2;
1309 	}
1310 	else
1311 	{
1312 		yp = b->Y() + b->bIconYOffset;
1313 	}
1314 
1315 	/* Was the button clicked on? if so, move the image slightly for the illusion
1316 	 * that the image moved into the screen.
1317 	 */
1318 	if (b->Clicked() && b->fShiftImage)
1319 	{
1320 		xp++;
1321 		yp++;
1322 	}
1323 
1324 	// Set the clipping rectangle to the viewable area of the button
1325 	SetClippingRect(&NewClip);
1326 
1327 	BltVideoObject(ButtonDestBuffer, hvObject, b->usIconIndex, xp, yp);
1328 
1329 	// Restore previous clip region
1330 	SetClippingRect(&OldClip);
1331 }
1332 
1333 
1334 // If a button has text attached to it, then it'll draw it last.
DrawTextOnButton(const GUI_BUTTON * b)1335 static void DrawTextOnButton(const GUI_BUTTON* b)
1336 {
1337 	// If this button actually has a string to print
1338 	if (b->codepoints.empty()) return;
1339 
1340 	// Get the width and height of this button
1341 	INT32 const width  = b->W();
1342 	INT32 const height = b->H();
1343 
1344 	// Compute the viewable area on this button
1345 	SGPRect NewClip;
1346 	NewClip.iLeft   = b->X() + 3;
1347 	NewClip.iRight  = b->X() + width - 3;
1348 	NewClip.iTop    = b->Y() + 2;
1349 	NewClip.iBottom = b->Y() + height - 2;
1350 
1351 	// Get the starting coordinates to print
1352 	const INT32 TextX = NewClip.iLeft;
1353 	const INT32 TextY = NewClip.iTop;
1354 
1355 	// Get the current clipping area
1356 	SGPRect OldClip;
1357 	GetClippingRect(&OldClip);
1358 
1359 	// Clip the button's viewable area to the screen
1360 	if (NewClip.iLeft < OldClip.iLeft) NewClip.iLeft = OldClip.iLeft;
1361 
1362 	// Are we off hte right side?
1363 	if (NewClip.iLeft > OldClip.iRight) return;
1364 
1365 	if (NewClip.iRight > OldClip.iRight) NewClip.iRight = OldClip.iRight;
1366 
1367 	// Are we off the left side?
1368 	if (NewClip.iRight < OldClip.iLeft) return;
1369 
1370 	if (NewClip.iTop < OldClip.iTop) NewClip.iTop = OldClip.iTop;
1371 
1372 	// Are we off the bottom of the screen?
1373 	if (NewClip.iTop > OldClip.iBottom) return;
1374 
1375 	if (NewClip.iBottom > OldClip.iBottom) NewClip.iBottom = OldClip.iBottom;
1376 
1377 	// Are we off the top?
1378 	if (NewClip.iBottom < OldClip.iTop) return;
1379 
1380 	// Did we clip the viewable area out of existance?
1381 	if (NewClip.iRight <= NewClip.iLeft || NewClip.iBottom <= NewClip.iTop) return;
1382 
1383 	// Set the font printing settings to the buttons viewable area
1384 	SetFontDestBuffer(ButtonDestBuffer, NewClip.iLeft, NewClip.iTop, NewClip.iRight, NewClip.iBottom);
1385 
1386 	// Compute the coordinates to center the text
1387 	INT32 yp;
1388 	if (b->bTextYOffset == -1)
1389 	{
1390 		yp = TextY + (height - GetFontHeight(b->usFont)) / 2 - 1;
1391 	}
1392 	else
1393 	{
1394 		yp = b->Y() + b->bTextYOffset;
1395 	}
1396 
1397 	INT32 xp;
1398 	if (b->bTextXOffset == -1)
1399 	{
1400 		switch (b->bJustification)
1401 		{
1402 			case GUI_BUTTON::TEXT_LEFT:   xp = TextX + 3; break;
1403 			case GUI_BUTTON::TEXT_RIGHT:  xp = NewClip.iRight - StringPixLength(b->codepoints, b->usFont) - 3; break;
1404 			default:
1405 			case GUI_BUTTON::TEXT_CENTER: xp = TextX + (width - 6 - StringPixLength(b->codepoints, b->usFont)) / 2; break;
1406 		}
1407 	}
1408 	else
1409 	{
1410 		xp = b->X() + b->bTextXOffset;
1411 	}
1412 
1413 	// print the text
1414 
1415 	//Override the colors if necessary.
1416 	INT16 sForeColor;
1417 	if (b->Enabled() && b->Area.uiFlags & MSYS_MOUSE_IN_AREA && b->sForeColorHilited != -1)
1418 	{
1419 		sForeColor = b->sForeColorHilited;
1420 	}
1421 	else if (b->Clicked() && b->sForeColorDown != -1)
1422 	{
1423 		sForeColor = b->sForeColorDown;
1424 	}
1425 	else
1426 	{
1427 		sForeColor = b->sForeColor;
1428 	}
1429 
1430 	UINT8 shadow;
1431 	if (b->Enabled() && b->Area.uiFlags & MSYS_MOUSE_IN_AREA && b->sShadowColorHilited != -1)
1432 	{
1433 		shadow = b->sShadowColorHilited;
1434 	}
1435 	else if (b->Clicked() && b->sShadowColorDown != -1)
1436 	{
1437 		shadow = b->sShadowColorDown;
1438 	}
1439 	else if (b->sShadowColor != -1)
1440 	{
1441 		shadow = b->sShadowColor;
1442 	}
1443 	else
1444 	{
1445 		shadow = DEFAULT_SHADOW;
1446 	}
1447 
1448 	SetFontAttributes(b->usFont, sForeColor, shadow);
1449 
1450 	if (b->Clicked() && b->fShiftText)
1451 	{
1452 		/* Was the button clicked on? if so, move the text slightly for the illusion
1453 		 * that the text moved into the screen. */
1454 		xp++;
1455 		yp++;
1456 	}
1457 
1458 	if (b->sWrappedWidth != -1)
1459 	{
1460 		UINT8 bJustified = 0;
1461 		switch (b->bJustification)
1462 		{
1463 			case GUI_BUTTON::TEXT_LEFT:    bJustified = LEFT_JUSTIFIED;    break;
1464 			case GUI_BUTTON::TEXT_RIGHT:   bJustified = RIGHT_JUSTIFIED;   break;
1465 			case GUI_BUTTON::TEXT_CENTER:  bJustified = CENTER_JUSTIFIED;  break;
1466 			default:                       SLOGA("DrawTextOnButton: invalid text alignment"); break;
1467 		}
1468 		if (b->bTextXOffset == -1)
1469 		{
1470 			/* Kris:
1471 			 * There needs to be recalculation of the start positions based on the
1472 			 * justification and the width specified wrapped width.  I was drawing a
1473 			 * double lined word on the right side of the button to find it drawing
1474 			 * way over to the left.  I've added the necessary code for the right and
1475 			 * center justification.
1476 			 */
1477 			yp = b->Y() + 2;
1478 
1479 			switch (b->bJustification)
1480 			{
1481 				case GUI_BUTTON::TEXT_RIGHT:
1482 					xp = b->BottomRightX() - 3 - b->sWrappedWidth;
1483 					if (b->fShiftText && b->Clicked())
1484 					{
1485 						xp++;
1486 						yp++;
1487 					}
1488 					break;
1489 
1490 				case GUI_BUTTON::TEXT_CENTER:
1491 					xp = b->X() + 3 + b->sWrappedWidth / 2;
1492 					if (b->fShiftText && b->Clicked())
1493 					{
1494 						xp++;
1495 						yp++;
1496 					}
1497 					break;
1498 			}
1499 		}
1500 		yp += b->bTextYSubOffSet;
1501 		xp += b->bTextXSubOffSet;
1502 		DisplayWrappedString(xp, yp, b->sWrappedWidth, 1, b->usFont, sForeColor, b->codepoints, FONT_MCOLOR_BLACK, bJustified);
1503 	}
1504 	else
1505 	{
1506 		yp += b->bTextYSubOffSet;
1507 		xp += b->bTextXSubOffSet;
1508 		MPrint(xp, yp, b->codepoints);
1509 	}
1510 	// Restore the old text printing settings
1511 }
1512 
1513 
1514 /* This function is called by the DrawIconicButton and DrawTextButton routines
1515  * to draw the borders and background of the buttons.
1516  */
DrawGenericButton(const GUI_BUTTON * b)1517 static void DrawGenericButton(const GUI_BUTTON* b)
1518 {
1519 	// Select the graphics to use depending on the current state of the button
1520 	HVOBJECT BPic;
1521 	if (!b->Enabled())
1522 	{
1523 		BPic = GenericButtonOffNormal;
1524 		switch (b->bDisabledStyle)
1525 		{
1526 			case GUI_BUTTON::DISABLED_STYLE_DEFAULT:
1527 				gbDisabledButtonStyle = !b->codepoints.empty() ?
1528 					GUI_BUTTON::DISABLED_STYLE_SHADED :
1529 					GUI_BUTTON::DISABLED_STYLE_HATCHED;
1530 				break;
1531 
1532 			case GUI_BUTTON::DISABLED_STYLE_HATCHED:
1533 			case GUI_BUTTON::DISABLED_STYLE_SHADED:
1534 				gbDisabledButtonStyle = b->bDisabledStyle;
1535 				break;
1536 		}
1537 	}
1538 	else if (b->Clicked())
1539 	{
1540 		if  (b->Area.uiFlags & MSYS_MOUSE_IN_AREA && GenericButtonOnHilite != NULL && gfRenderHilights)
1541 		{
1542 			BPic = GenericButtonOnHilite;
1543 		}
1544 		else
1545 		{
1546 			BPic = GenericButtonOnNormal;
1547 		}
1548 	}
1549 	else
1550 	{
1551 		if (b->Area.uiFlags & MSYS_MOUSE_IN_AREA && GenericButtonOffHilite != NULL && gfRenderHilights)
1552 		{
1553 			BPic = GenericButtonOffHilite;
1554 		}
1555 		else
1556 		{
1557 			BPic = GenericButtonOffNormal;
1558 		}
1559 	}
1560 
1561 	const INT32 iBorderWidth  = 3;
1562 	const INT32 iBorderHeight = 2;
1563 
1564 	// Compute the number of button "chunks" needed to be blitted
1565 	INT32 const width         = b->W();
1566 	INT32 const height        = b->H();
1567 	const INT32 NumChunksWide = width  / iBorderWidth;
1568 	INT32       NumChunksHigh = height / iBorderHeight;
1569 	const INT32 hremain       = height % iBorderHeight;
1570 	const INT32 wremain       = width  % iBorderWidth;
1571 
1572 	INT32 const bx = b->X();
1573 	INT32 const by = b->Y();
1574 	INT32 const cx = bx + (NumChunksWide - 1) * iBorderWidth  + wremain;
1575 	INT32 const cy = by + (NumChunksHigh - 1) * iBorderHeight + hremain;
1576 
1577 	// Fill the button's area with the button's background color
1578 	ColorFillVideoSurfaceArea(ButtonDestBuffer, b->X(), b->Y(), b->BottomRightX(), b->BottomRightY(), GenericButtonFillColors);
1579 
1580 	SGPVSurface::Lock l(ButtonDestBuffer);
1581 	UINT16* const pDestBuf         = l.Buffer<UINT16>();
1582 	UINT32  const uiDestPitchBYTES = l.Pitch();
1583 
1584 	SGPRect ClipRect;
1585 	GetClippingRect(&ClipRect);
1586 
1587 	// Draw the button's borders and corners (horizontally)
1588 	for (INT32 q = 0; q < NumChunksWide; q++)
1589 	{
1590 		INT32 const ImgNum = (q == 0 ? 0 : 1);
1591 		INT32 const x = bx + q * iBorderWidth;
1592 		Blt8BPPDataTo16BPPBufferTransparentClip(pDestBuf, uiDestPitchBYTES, BPic, x,  by, ImgNum,     &ClipRect);
1593 		Blt8BPPDataTo16BPPBufferTransparentClip(pDestBuf, uiDestPitchBYTES, BPic, x,  cy, ImgNum + 5, &ClipRect);
1594 	}
1595 	// Blit the right side corners
1596 	Blt8BPPDataTo16BPPBufferTransparentClip(pDestBuf, uiDestPitchBYTES, BPic, cx, by, 2, &ClipRect);
1597 	Blt8BPPDataTo16BPPBufferTransparentClip(pDestBuf, uiDestPitchBYTES, BPic, cx, cy, 7, &ClipRect);
1598 	// Draw the vertical members of the button's borders
1599 	NumChunksHigh--;
1600 
1601 	if (hremain != 0)
1602 	{
1603 		INT32 const y = by + NumChunksHigh * iBorderHeight - iBorderHeight + hremain;
1604 		Blt8BPPDataTo16BPPBufferTransparentClip(pDestBuf, uiDestPitchBYTES, BPic, bx, y, 3, &ClipRect);
1605 		Blt8BPPDataTo16BPPBufferTransparentClip(pDestBuf, uiDestPitchBYTES, BPic, cx, y, 4, &ClipRect);
1606 	}
1607 
1608 	for (INT32 q = 1; q < NumChunksHigh; q++)
1609 	{
1610 		INT32 const y = by + q * iBorderHeight;
1611 		Blt8BPPDataTo16BPPBufferTransparentClip(pDestBuf, uiDestPitchBYTES, BPic, bx, y, 3, &ClipRect);
1612 		Blt8BPPDataTo16BPPBufferTransparentClip(pDestBuf, uiDestPitchBYTES, BPic, cx, y, 4, &ClipRect);
1613 	}
1614 }
1615 
1616 
CreateCheckBoxButton(INT16 x,INT16 y,const char * filename,INT16 Priority,GUI_CALLBACK ClickCallback)1617 GUIButtonRef CreateCheckBoxButton(INT16 x, INT16 y, const char* filename, INT16 Priority, GUI_CALLBACK ClickCallback)
1618 {
1619 	Assert(filename != NULL);
1620 	BUTTON_PICS* const ButPic = LoadButtonImage(filename, -1, 0, 1, 2, 3);
1621 	GUIButtonRef const b      = QuickCreateButtonInternal(ButPic, x, y, BUTTON_CHECKBOX, Priority, MSYS_NO_CALLBACK, ClickCallback);
1622 
1623 	//change the flags so that it isn't a quick button anymore
1624 	b->uiFlags &= ~BUTTON_QUICK;
1625 	b->uiFlags |= BUTTON_CHECKBOX | BUTTON_SELFDELETE_IMAGE;
1626 
1627 	return b;
1628 }
1629 
1630 
1631 /* Generic Button Movement Callback to reset the mouse button if the mouse is no
1632  * longer in the button region.
1633  */
DefaultMoveCallback(GUI_BUTTON * btn,INT32 reason)1634 static void DefaultMoveCallback(GUI_BUTTON* btn, INT32 reason)
1635 {
1636 	// If the button isn't the anchored button, then we don't want to modify the button state.
1637 	if (btn != gpAnchoredButton) return;
1638 
1639 	if (reason & MSYS_CALLBACK_REASON_LOST_MOUSE)
1640 	{
1641 		if (!gfAnchoredState)
1642 		{
1643 			btn->uiFlags &= ~BUTTON_CLICKED_ON;
1644 			if (btn->ubSoundSchemeID)
1645 			{
1646 				PlayButtonSound(btn, BUTTON_SOUND_CLICKED_OFF);
1647 			}
1648 		}
1649 		InvalidateRegion(btn->X(), btn->Y(), btn->BottomRightX(), btn->BottomRightY());
1650 	}
1651 	else if (reason & MSYS_CALLBACK_REASON_GAIN_MOUSE)
1652 	{
1653 		btn->uiFlags |= BUTTON_CLICKED_ON;
1654 		if (btn->ubSoundSchemeID)
1655 		{
1656 			PlayButtonSound(btn, BUTTON_SOUND_CLICKED_ON);
1657 		}
1658 		InvalidateRegion(btn->X(), btn->Y(), btn->BottomRightX(), btn->BottomRightY());
1659 	}
1660 }
1661 
1662 
ReleaseAnchorMode(void)1663 void ReleaseAnchorMode(void)
1664 {
1665 	GUI_BUTTON* const b = gpAnchoredButton;
1666 	if (!b) return;
1667 
1668 	if (gusMouseXPos < b->X() || b->BottomRightX() < gusMouseXPos ||
1669 			gusMouseYPos < b->Y() || b->BottomRightY() < gusMouseYPos)
1670 	{
1671 		//released outside button area, so restore previous button state.
1672 		if (gfAnchoredState)
1673 		{
1674 			b->uiFlags |= BUTTON_CLICKED_ON;
1675 		}
1676 		else
1677 		{
1678 			b->uiFlags &= ~BUTTON_CLICKED_ON;
1679 		}
1680 		InvalidateRegion(b->X(), b->Y(), b->BottomRightX(), b->BottomRightY());
1681 	}
1682 	gpPrevAnchoredButton = b;
1683 	gpAnchoredButton     = 0;
1684 }
1685 
1686 
Hide()1687 void GUI_BUTTON::Hide()
1688 {
1689 	Area.Disable();
1690 	uiFlags |= BUTTON_DIRTY;
1691 	InvalidateRegion(X(), Y(), BottomRightX(), BottomRightY());
1692 }
1693 
1694 
HideButton(GUIButtonRef const b)1695 void HideButton(GUIButtonRef const b)
1696 {
1697 	CHECKV(b != NULL); // XXX HACK000C
1698 	b->Hide();
1699 }
1700 
1701 
Show()1702 void GUI_BUTTON::Show()
1703 {
1704 	Area.Enable();
1705 	uiFlags |= BUTTON_DIRTY;
1706 	InvalidateRegion(X(), Y(), BottomRightX(), BottomRightY());
1707 }
1708 
1709 
ShowButton(GUIButtonRef const b)1710 void ShowButton(GUIButtonRef const b)
1711 {
1712 	CHECKV(b != NULL); // XXX HACK000C
1713 	b->Show();
1714 }
1715 
1716 
GetGenericButtonFillColor(void)1717 UINT16 GetGenericButtonFillColor(void)
1718 {
1719 	return GenericButtonFillColors;
1720 }
1721