/*********************************************************************************************** Button System.c Rewritten mostly by Kris Morness ***********************************************************************************************/ #ifdef JA2_PRECOMPILED_HEADERS #include "JA2 SGP ALL.H" #elif defined( WIZ8_PRECOMPILED_HEADERS ) #include "WIZ8 SGP ALL.H" #else #include "types.h" #include #include #include #include "debug.h" #include "input.h" #include "memman.h" #include "english.h" #include "vobject.h" #include "vobject_blitters.h" #include "soundman.h" #include "Button System.h" #include "line.h" #include #if defined( JA2 ) || defined( UTIL ) #include "WordWrap.h" #include "video.h" #include "Button Sound Control.h" #ifdef _JA2_RENDER_DIRTY #include "\JA2\Build\utils\Font Control.h" #include "Render Dirty.h" #include "utilities.h" #endif #else #include "video2.h" #endif #endif //ATE: Added to let Wiz default creating mouse regions with no cursor, JA2 default to a cursor ( first one ) #ifdef JA2 #define MSYS_STARTING_CURSORVAL 0 #else #define MSYS_STARTING_CURSORVAL MSYS_NO_CURSOR // The following should be moved from here #define GETPIXELDEPTH( ) ( gbPixelDepth ) // From "utilities.h" in JA2 #define COLOR_RED 162 // From "lighting.h" in JA2 #define COLOR_BLUE 203 #define COLOR_YELLOW 144 #define COLOR_GREEN 184 #define COLOR_LTGREY 134 #define COLOR_BROWN 80 #define COLOR_PURPLE 160 #define COLOR_ORANGE 76 #define COLOR_WHITE 208 #define COLOR_BLACK 72 // this doesn't exactly belong here either... (From "Font Control.h" in JA2) #define FONT_MCOLOR_BLACK 0 #endif #define COLOR_DKGREY 136 #define MAX_GENERIC_PICS 40 #define MAX_BUTTON_ICONS 40 #define GUI_BTN_NONE 0 #define GUI_BTN_DUPLICATE_VOBJ 1 #define GUI_BTN_EXTERNAL_VOBJ 2 UINT8 str[128]; //Kris: December 2, 1997 //Special internal debugging utilities that will ensure that you don't attempt to delete //an already deleted button, or it's images, etc. It will also ensure that you don't create //the same button that already exists. //TO REMOVE ALL DEBUG FUNCTIONALITY: simply comment out BUTTONSYSTEM_DEBUGGING definition #ifdef JA2 #ifdef _DEBUG #define BUTTONSYSTEM_DEBUGGING #endif #endif #ifdef BUTTONSYSTEM_DEBUGGING BOOLEAN gfIgnoreShutdownAssertions; //Called immediately before assigning the button to the button list. void AssertFailIfIdenticalButtonAttributesFound( GUI_BUTTON *b ) { INT32 x; GUI_BUTTON *c; for( x = 0; x < MAX_BUTTONS; x++ ) { c = ButtonList[ x ]; if( !c ) continue; if( c->uiFlags & BUTTON_DELETION_PENDING ) continue; if( c->UserData[3] == 0xffffffff ) continue; if( b->Area.PriorityLevel != c->Area.PriorityLevel ) continue; if( b->Area.RegionTopLeftX != c->Area.RegionTopLeftX ) continue; if( b->Area.RegionTopLeftY != c->Area.RegionTopLeftY ) continue; if( b->Area.RegionBottomRightX != c->Area.RegionBottomRightX ) continue; if( b->Area.RegionBottomRightY != c->Area.RegionBottomRightY ) continue; if( b->ClickCallback != c->ClickCallback ) continue; if( b->MoveCallback != c->MoveCallback ) continue; if( b->XLoc != c->XLoc ) continue; if( b->YLoc != c->YLoc ) continue; //if we get this far, it is reasonably safe to assume that the newly created //button already exists. Placing a break point on the following assert will //allow the coder to easily isolate the case! sprintf( str, "Attempting to create a button that has already been created (existing buttonID %d).", c->IDNum ); AssertMsg( 0, str ); } } #endif //Kris: //These are the variables used for the anchoring of a particular button. //When you click on a button, it get's anchored, until you release the mouse button. //When you move around, you don't want to select other buttons, even when you release //it. This follows the Windows 95 convention. GUI_BUTTON *gpAnchoredButton; GUI_BUTTON *gpPrevAnchoredButton; BOOLEAN gfAnchoredState; void ReleaseAnchorMode(); INT8 gbDisabledButtonStyle; void DrawHatchOnButton( GUI_BUTTON *b ); void DrawShadeOnButton( GUI_BUTTON *b ); void DrawDefaultOnButton( GUI_BUTTON *b ); GUI_BUTTON *gpCurrentFastHelpButton; BOOLEAN gfRenderHilights = TRUE; BUTTON_PICS ButtonPictures[MAX_BUTTON_PICS]; INT32 ButtonPicsLoaded; UINT32 ButtonDestBuffer = BACKBUFFER; UINT32 ButtonDestPitch = 640*2; UINT32 ButtonDestBPP = 16; GUI_BUTTON *ButtonList[MAX_BUTTONS]; INT32 ButtonsInList=0; UINT16 GetWidthOfButtonPic( UINT16 usButtonPicID, INT32 iSlot ) { return ButtonPictures[ usButtonPicID ].vobj->pETRLEObject[ iSlot ].usWidth; } HVOBJECT GenericButtonGrayed[MAX_GENERIC_PICS]; HVOBJECT GenericButtonOffNormal[MAX_GENERIC_PICS]; HVOBJECT GenericButtonOffHilite[MAX_GENERIC_PICS]; HVOBJECT GenericButtonOnNormal[MAX_GENERIC_PICS]; HVOBJECT GenericButtonOnHilite[MAX_GENERIC_PICS]; HVOBJECT GenericButtonBackground[MAX_GENERIC_PICS]; UINT16 GenericButtonFillColors[MAX_GENERIC_PICS]; UINT16 GenericButtonBackgroundIndex[MAX_GENERIC_PICS]; INT16 GenericButtonOffsetX[MAX_GENERIC_PICS]; INT16 GenericButtonOffsetY[MAX_GENERIC_PICS]; HVOBJECT GenericButtonIcons[MAX_BUTTON_ICONS]; // flag to state we wish to render buttons on the one after the next pass through render buttons BOOLEAN fPausedMarkButtonsDirtyFlag = FALSE; BOOLEAN fDisableHelpTextRestoreFlag = FALSE; BOOLEAN gfDelayButtonDeletion = FALSE; BOOLEAN gfPendingButtonDeletion = FALSE; void RemoveButtonsMarkedForDeletion(); extern MOUSE_REGION *MSYS_PrevRegion; extern MOUSE_REGION *MSYS_CurrRegion; //============================================================================= // FindFreeButtonSlot // // Finds an available slot for loading button pictures // INT32 FindFreeButtonSlot(void) { int slot; // Are there any slots available? if(ButtonPicsLoaded >= MAX_BUTTON_PICS) return(BUTTON_NO_SLOT); // Search for a slot for(slot=0;slotpETRLEObject[Grayed]); ThisHeight = (UINT32)(pTrav->usHeight+pTrav->sOffsetY); ThisWidth = (UINT32)(pTrav->usWidth+pTrav->sOffsetX); if(MaxWidthpETRLEObject[OffNormal]); ThisHeight = (UINT32)(pTrav->usHeight+pTrav->sOffsetY); ThisWidth = (UINT32)(pTrav->usWidth+pTrav->sOffsetX); if(MaxWidthpETRLEObject[OffHilite]); ThisHeight = (UINT32)(pTrav->usHeight+pTrav->sOffsetY); ThisWidth = (UINT32)(pTrav->usWidth+pTrav->sOffsetX); if(MaxWidthpETRLEObject[OnNormal]); ThisHeight = (UINT32)(pTrav->usHeight+pTrav->sOffsetY); ThisWidth = (UINT32)(pTrav->usWidth+pTrav->sOffsetX); if(MaxWidthpETRLEObject[OnHilite]); ThisHeight = (UINT32)(pTrav->usHeight+pTrav->sOffsetY); ThisWidth = (UINT32)(pTrav->usWidth+pTrav->sOffsetX); if(MaxWidthpETRLEObject[Grayed]); ThisHeight = (UINT32)(pTrav->usHeight+pTrav->sOffsetY); ThisWidth = (UINT32)(pTrav->usWidth+pTrav->sOffsetX); if(MaxWidthpETRLEObject[OffNormal]); ThisHeight = (UINT32)(pTrav->usHeight+pTrav->sOffsetY); ThisWidth = (UINT32)(pTrav->usWidth+pTrav->sOffsetX); if(MaxWidthpETRLEObject[OffHilite]); ThisHeight = (UINT32)(pTrav->usHeight+pTrav->sOffsetY); ThisWidth = (UINT32)(pTrav->usWidth+pTrav->sOffsetX); if(MaxWidthpETRLEObject[OnNormal]); ThisHeight = (UINT32)(pTrav->usHeight+pTrav->sOffsetY); ThisWidth = (UINT32)(pTrav->usWidth+pTrav->sOffsetX); if(MaxWidthpETRLEObject[OnHilite]); ThisHeight = (UINT32)(pTrav->usHeight+pTrav->sOffsetY); ThisWidth = (UINT32)(pTrav->usWidth+pTrav->sOffsetX); if(MaxWidthpETRLEObject[Grayed]); ThisHeight = (UINT32)(pTrav->usHeight+pTrav->sOffsetY); ThisWidth = (UINT32)(pTrav->usWidth+pTrav->sOffsetX); if(MaxWidthpETRLEObject[OffNormal]); ThisHeight = (UINT32)(pTrav->usHeight+pTrav->sOffsetY); ThisWidth = (UINT32)(pTrav->usWidth+pTrav->sOffsetX); if(MaxWidthpETRLEObject[OffHilite]); ThisHeight = (UINT32)(pTrav->usHeight+pTrav->sOffsetY); ThisWidth = (UINT32)(pTrav->usWidth+pTrav->sOffsetX); if(MaxWidthpETRLEObject[OnNormal]); ThisHeight = (UINT32)(pTrav->usHeight+pTrav->sOffsetY); ThisWidth = (UINT32)(pTrav->usWidth+pTrav->sOffsetX); if(MaxWidthpETRLEObject[OnHilite]); ThisHeight = (UINT32)(pTrav->usHeight+pTrav->sOffsetY); ThisWidth = (UINT32)(pTrav->usWidth+pTrav->sOffsetX); if(MaxWidth= MAX_BUTTON_PICS ) { sprintf( str, "Attempting to UnloadButtonImage with out of range index %d.", Index ); AssertMsg( 0, str ); } if( !ButtonPictures[ Index ].vobj ) { #ifdef BUTTONSYSTEM_DEBUGGING if( gfIgnoreShutdownAssertions ) #endif return; AssertMsg( 0, "Attempting to UnloadButtonImage that has a null vobj (already deleted)."); } // If this is a duplicated button image, then don't trash the vobject if(ButtonPictures[Index].fFlags & GUI_BTN_DUPLICATE_VOBJ || ButtonPictures[Index].fFlags & GUI_BTN_EXTERNAL_VOBJ) { ButtonPictures[Index].vobj = NULL; ButtonPicsLoaded--; } else { // Deleting a non-duplicate, so see if any dups present. if so, then // convert one of them to an original! fDone = FALSE; for( x = 0; x < MAX_BUTTON_PICS && !fDone; x++ ) { if( (x != Index) && (ButtonPictures[x].vobj == ButtonPictures[Index].vobj) ) { if ( ButtonPictures[x].fFlags & GUI_BTN_DUPLICATE_VOBJ ) { // If we got here, then we got a duplicate object of the one we // want to delete, so convert it to an original! ButtonPictures[x].fFlags &= (~GUI_BTN_DUPLICATE_VOBJ); // Now remove this button, but not it's vobject ButtonPictures[Index].vobj = NULL; fDone = TRUE; ButtonPicsLoaded--; } } } } // If image slot isn't empty, delete the image if(ButtonPictures[Index].vobj != NULL) { DeleteVideoObject(ButtonPictures[Index].vobj); ButtonPictures[Index].vobj = NULL; ButtonPicsLoaded--; } } //============================================================================= // EnableButton // // Enables an already created button. // BOOLEAN EnableButton( INT32 iButtonID ) { GUI_BUTTON *b; UINT32 OldState; if( iButtonID < 0 || iButtonID >= MAX_BUTTONS ) { sprintf( str, "Attempting to EnableButton with out of range buttonID %d.", iButtonID ); AssertMsg( 0, str ); } b = ButtonList[ iButtonID ]; // If button exists, set the ENABLED flag if( b ) { OldState = b->uiFlags & BUTTON_ENABLED; b->uiFlags |= ( BUTTON_ENABLED | BUTTON_DIRTY ); } else OldState = 0; // Return previous ENABLED state of this button return((OldState==BUTTON_ENABLED)?TRUE:FALSE); } //============================================================================= // DisableButton // // Disables a button. The button remains in the system list, and can be // reactivated by calling EnableButton. // // Diabled buttons will appear "grayed out" on the screen (unless the // graphics for such are not available). // BOOLEAN DisableButton(INT32 iButtonID ) { GUI_BUTTON *b; UINT32 OldState; if( iButtonID < 0 || iButtonID >= MAX_BUTTONS ) { sprintf( str, "Attempting to DisableButton with out of range buttonID %d.", iButtonID ); AssertMsg( 0, str ); } b = ButtonList[ iButtonID ]; // If button exists, reset the ENABLED flag if( b ) { OldState = b->uiFlags & BUTTON_ENABLED; b->uiFlags &= (~BUTTON_ENABLED); b->uiFlags |= BUTTON_DIRTY; } else OldState = 0; // Return previous ENABLED state of button return((OldState==BUTTON_ENABLED)?TRUE:FALSE); } //============================================================================= // InitializeButtonImageManager // // Initializes the button image sub-system. This function is called by // InitButtonSystem. // BOOLEAN InitializeButtonImageManager(INT32 DefaultBuffer, INT32 DefaultPitch, INT32 DefaultBPP) { VOBJECT_DESC vo_desc; UINT8 Pix; int x; // Set up the default settings if(DefaultBuffer != BUTTON_USE_DEFAULT) ButtonDestBuffer = (UINT32)DefaultBuffer; else ButtonDestBuffer = FRAME_BUFFER; if(DefaultPitch != BUTTON_USE_DEFAULT) ButtonDestPitch = (UINT32)DefaultPitch; else ButtonDestPitch = 640*2; if(DefaultBPP != BUTTON_USE_DEFAULT) ButtonDestBPP = (UINT32)DefaultBPP; else ButtonDestBPP = 16; // Blank out all QuickButton images for(x=0;xp16BPPPalette[Pix]; else if(GETPIXELDEPTH()==8) GenericButtonFillColors[0]=COLOR_DKGREY; return(TRUE); } //============================================================================= // FindFreeGenericSlot // // Finds the next available slot for generic (TEXT and/or ICONIC) buttons. // INT16 FindFreeGenericSlot(void) { INT16 slot,x; slot=BUTTON_NO_SLOT; for(x=0;x= MAX_BUTTON_ICONS ) { sprintf( str, "Attempting to UnloadGenericButtonIcon with out of range index %d.", GenImg ); AssertMsg( 0, str ); } if( !GenericButtonIcons[ GenImg ] ) { #ifdef BUTTONSYSTEM_DEBUGGING if( gfIgnoreShutdownAssertions ) #endif return FALSE; AssertMsg( 0, "Attempting to UnloadGenericButtonIcon that has no icon (already deleted)." ); } // If an icon is present in the slot, remove it. DeleteVideoObject(GenericButtonIcons[GenImg]); GenericButtonIcons[GenImg]=NULL; return TRUE; } //============================================================================= // UnloadGenericButtonImage // // Removes the images associated with a generic button. Except the icon // image of iconic buttons. See above. // BOOLEAN UnloadGenericButtonImage(INT16 GenImg) { BOOLEAN fDeletedSomething = FALSE; if( GenImg < 0 || GenImg >= MAX_GENERIC_PICS ) { sprintf( str, "Attempting to UnloadGenericButtonImage with out of range index %d.", GenImg ); AssertMsg( 0, str ); } // For each possible image type in a generic button, check if it's // present, and if so, remove it. if(GenericButtonGrayed[GenImg]!=NULL) { DeleteVideoObject(GenericButtonGrayed[GenImg]); GenericButtonGrayed[GenImg]=NULL; fDeletedSomething = TRUE; } if(GenericButtonOffNormal[GenImg]!=NULL) { DeleteVideoObject(GenericButtonOffNormal[GenImg]); GenericButtonOffNormal[GenImg]=NULL; fDeletedSomething = TRUE; } if(GenericButtonOffHilite[GenImg]!=NULL) { DeleteVideoObject(GenericButtonOffHilite[GenImg]); GenericButtonOffHilite[GenImg]=NULL; fDeletedSomething = TRUE; } if(GenericButtonOnNormal[GenImg]!=NULL) { DeleteVideoObject(GenericButtonOnNormal[GenImg]); GenericButtonOnNormal[GenImg]=NULL; fDeletedSomething = TRUE; } if(GenericButtonOnHilite[GenImg]!=NULL) { DeleteVideoObject(GenericButtonOnHilite[GenImg]); GenericButtonOnHilite[GenImg]=NULL; fDeletedSomething = TRUE; } if(GenericButtonBackground[GenImg]!=NULL) { DeleteVideoObject(GenericButtonBackground[GenImg]); GenericButtonBackground[GenImg]=NULL; fDeletedSomething = TRUE; } #ifdef BUTTONSYSTEM_DEBUGGING if( !gfIgnoreShutdownAssertions && !fDeletedSomething ) AssertMsg( 0, "Attempting to UnloadGenericButtonImage that has no images (already deleted)." ); #endif // Reset the remaining variables GenericButtonFillColors[GenImg]=0; GenericButtonBackgroundIndex[GenImg]=0; GenericButtonOffsetX[GenImg]=0; GenericButtonOffsetY[GenImg]=0; return(TRUE); } //============================================================================= // LoadGenericButtonImages // // Loads the image files required for displaying a generic button. // INT16 LoadGenericButtonImages(UINT8 *GrayName,UINT8 *OffNormName,UINT8 *OffHiliteName,UINT8 *OnNormName,UINT8 *OnHiliteName,UINT8 *BkGrndName,INT16 Index,INT16 OffsetX, INT16 OffsetY) { INT16 ImgSlot; VOBJECT_DESC vo_desc; UINT8 Pix; // if the images for Off-Normal and On-Normal don't exist, abort call if((OffNormName == BUTTON_NO_FILENAME) || (OnNormName == BUTTON_NO_FILENAME)) { DbgMessage(TOPIC_BUTTON_HANDLER, DBG_LEVEL_0, "LoadGenericButtonImages: No filenames for OFFNORMAL and/or ONNORMAL images"); return(-1); } // Get a slot number for these images if((ImgSlot=FindFreeGenericSlot()) == -1) { DbgMessage(TOPIC_BUTTON_HANDLER, DBG_LEVEL_0, "LoadGenericButtonImages: Out of generic button slots"); return(-1); } // Load the image for the Off-Normal button state (required) vo_desc.fCreateFlags = VOBJECT_CREATE_FROMFILE; strcpy(vo_desc.ImageFile, OffNormName); if((GenericButtonOffNormal[ImgSlot] = CreateVideoObject(&vo_desc)) == NULL) { DbgMessage(TOPIC_BUTTON_HANDLER, DBG_LEVEL_0, String("LoadGenericButtonImages: Couldn't create VOBJECT for %s",OffNormName)); return(-1); } // Load the image for the On-Normal button state (required) vo_desc.fCreateFlags = VOBJECT_CREATE_FROMFILE; strcpy(vo_desc.ImageFile, OnNormName); if((GenericButtonOnNormal[ImgSlot] = CreateVideoObject(&vo_desc)) == NULL) { DbgMessage(TOPIC_BUTTON_HANDLER, DBG_LEVEL_0, String("LoadGenericButtonImages: Couldn't create VOBJECT for %s",OnNormName)); return(-1); } // For the optional button state images, see if a filename was given, and // if so, load it. if(GrayName != BUTTON_NO_FILENAME) { vo_desc.fCreateFlags = VOBJECT_CREATE_FROMFILE; strcpy(vo_desc.ImageFile, GrayName); if((GenericButtonGrayed[ImgSlot] = CreateVideoObject(&vo_desc)) == NULL) { DbgMessage(TOPIC_BUTTON_HANDLER, DBG_LEVEL_0, String("LoadGenericButtonImages: Couldn't create VOBJECT for %s",GrayName)); return(-1); } } else GenericButtonGrayed[ImgSlot] = NULL; if(OffHiliteName != BUTTON_NO_FILENAME) { vo_desc.fCreateFlags = VOBJECT_CREATE_FROMFILE; strcpy(vo_desc.ImageFile, OffHiliteName); if((GenericButtonOffHilite[ImgSlot] = CreateVideoObject(&vo_desc)) == NULL) { DbgMessage(TOPIC_BUTTON_HANDLER, DBG_LEVEL_0, String("LoadGenericButtonImages: Couldn't create VOBJECT for %s",OffHiliteName)); return(-1); } } else GenericButtonOffHilite[ImgSlot] = NULL; if(OnHiliteName != BUTTON_NO_FILENAME) { vo_desc.fCreateFlags = VOBJECT_CREATE_FROMFILE; strcpy(vo_desc.ImageFile, OnHiliteName); if((GenericButtonOnHilite[ImgSlot] = CreateVideoObject(&vo_desc)) == NULL) { DbgMessage(TOPIC_BUTTON_HANDLER, DBG_LEVEL_0, String("LoadGenericButtonImages: Couldn't create VOBJECT for %s",OnHiliteName)); return(-1); } } else GenericButtonOnHilite[ImgSlot] = NULL; if(BkGrndName != BUTTON_NO_FILENAME) { vo_desc.fCreateFlags = VOBJECT_CREATE_FROMFILE; strcpy(vo_desc.ImageFile, BkGrndName); if((GenericButtonBackground[ImgSlot] = CreateVideoObject(&vo_desc)) == NULL) { DbgMessage(TOPIC_BUTTON_HANDLER, DBG_LEVEL_0, String("LoadGenericButtonImages: Couldn't create VOBJECT for %s",BkGrndName)); return(-1); } } else GenericButtonBackground[ImgSlot] = NULL; GenericButtonBackgroundIndex[ImgSlot]=Index; // Get the background fill color from the last (9th) sub-image in the // Off-Normal image. Pix=0; if(!GetETRLEPixelValue(&Pix,GenericButtonOffNormal[ImgSlot],8,0,0)) { DbgMessage(TOPIC_BUTTON_HANDLER, DBG_LEVEL_0, "LoadGenericButtonImages: Couldn't get generic button's background pixel value"); return(-1); } GenericButtonFillColors[ImgSlot]=GenericButtonOffNormal[ImgSlot]->p16BPPPalette[Pix]; // Set the button's background image adjustement offsets GenericButtonOffsetX[ImgSlot]=OffsetX; GenericButtonOffsetY[ImgSlot]=OffsetY; // Return the slot number used. return(ImgSlot); } //============================================================================= // ShutdownButtonImageManager // // Cleans up, and shuts down the button image manager sub-system. // // This function is called by ShutdownButtonSystem. // void ShutdownButtonImageManager(void) { int x; #ifdef BUTTONSYSTEM_DEBUGGING gfIgnoreShutdownAssertions = TRUE; #endif // Remove all QuickButton images for(x=0;xuiFlags & BUTTON_DELETION_PENDING ) { RemoveButton( i ); } } } //============================================================================= // RemoveButton // // Removes a button from the system's list. All memory associated with the // button is released. // void RemoveButton(INT32 iButtonID) { GUI_BUTTON *b; if( iButtonID < 0 || iButtonID >= MAX_BUTTONS ) { sprintf( str, "Attempting to RemoveButton with out of range buttonID %d.", iButtonID ); AssertMsg( 0, str ); } b = ButtonList[ iButtonID ]; // If button exists... if( !b ) { #ifdef BUTTONSYSTEM_DEBUGGING if( gfIgnoreShutdownAssertions ) #endif return; AssertMsg( 0, "Attempting to remove a button that has already been deleted." ); } //If we happen to be in the middle of a callback, and attempt to delete a button, //like deleting a node during list processing, then we delay it till after the callback //is completed. if( gfDelayButtonDeletion ) { b->uiFlags |= BUTTON_DELETION_PENDING; gfPendingButtonDeletion = TRUE; return; } //Kris: if( b->uiFlags & BUTTON_SELFDELETE_IMAGE ) { //checkboxes and simple create buttons have their own graphics associated with them, //and it is handled internally. We delete it here. This provides the advantage of less //micromanagement, but with the disadvantage of wasting more memory if you have lots of //buttons using the same graphics. UnloadButtonImage( b->ImageNum ); } // ...kill it!!! MSYS_RemoveRegion(&b->Area); #ifdef _JA2_RENDER_DIRTY if ( b->uiFlags & BUTTON_SAVEBACKGROUND ) { FreeBackgroundRectPending(b->BackRect); } #endif // Get rid of the text string if (b->string != NULL) MemFree( b->string ); if( b == gpAnchoredButton ) gpAnchoredButton = NULL; if( b == gpPrevAnchoredButton ) gpPrevAnchoredButton = NULL; MemFree(b); b=NULL; ButtonList[ iButtonID ] = NULL; } //============================================================================= // GetNextButtonNumber // // Finds the next available button slot. // INT32 GetNextButtonNumber(void) { INT32 x; for(x=0;x= MAX_BUTTONS ) { sprintf( str, "Attempting to resize button with out of range buttonID %d.", iButtonID ); AssertMsg( 0, str ); } // if button size is too small, adjust it. if(w<4) w=4; if(h<3) h=3; b = ButtonList[ iButtonID ]; if( !b ) { sprintf( str, "Attempting to resize deleted button with buttonID %d", iButtonID ); AssertMsg( 0, str ); } // If this is a QuickButton, ignore this call if((b->uiFlags & BUTTON_TYPES) == BUTTON_QUICK) return; // Get current button screen location xloc=b->XLoc; yloc=b->YLoc; // Set the new MOUSE_REGION area values to reflect change in size. b->Area.RegionTopLeftX=(UINT16)xloc; b->Area.RegionTopLeftY=(UINT16)yloc; b->Area.RegionBottomRightX=(UINT16)(xloc+w); b->Area.RegionBottomRightY=(UINT16)(yloc+h); b->uiFlags |= BUTTON_DIRTY; #ifdef _JA2_RENDER_DIRTY if ( b->uiFlags & BUTTON_SAVEBACKGROUND ) { FreeBackgroundRectPending(b->BackRect); b->BackRect = RegisterBackgroundRect(BGND_FLAG_PERMANENT | BGND_FLAG_SAVERECT, NULL, (INT16)xloc, (INT16)yloc, (INT16)(xloc+w), (INT16)(yloc+h) ); } #endif } //============================================================================= // SetButtonPosition // // Sets the position of a button on the screen. The position is relative // to the top left corner of the button. // void SetButtonPosition( INT32 iButtonID ,INT16 x, INT16 y) { GUI_BUTTON *b; INT32 xloc,yloc,w,h; if( iButtonID < 0 || iButtonID >= MAX_BUTTONS ) { sprintf( str, "Attempting to set button position with out of range buttonID %d.", iButtonID ); AssertMsg( 0, str ); } b=ButtonList[ iButtonID ]; if( !b ) { sprintf( str, "Attempting to set button position with buttonID %d", iButtonID ); AssertMsg( 0, str ); } // Get new screen position xloc=(INT16)x; yloc=(INT16)y; // Compute current width and height of this button w = b->Area.RegionBottomRightX - b->Area.RegionTopLeftX; h = b->Area.RegionBottomRightY - b->Area.RegionTopLeftY; // Set button to new location b->XLoc=x; b->YLoc=y; // Set the buttons MOUSE_REGION to appropriate area b->Area.RegionTopLeftX=(UINT16)xloc; b->Area.RegionTopLeftY=(UINT16)yloc; b->Area.RegionBottomRightX=(UINT16)(xloc+w); b->Area.RegionBottomRightY=(UINT16)(yloc+h); b->uiFlags |= BUTTON_DIRTY; #ifdef _JA2_RENDER_DIRTY if ( b->uiFlags & BUTTON_SAVEBACKGROUND ) { FreeBackgroundRectPending(b->BackRect); b->BackRect = RegisterBackgroundRect(BGND_FLAG_PERMANENT | BGND_FLAG_SAVERECT, NULL, (INT16)xloc, (INT16)yloc, (INT16)(xloc+w), (INT16)(yloc+h) ); } #endif } //============================================================================= // SetButtonIcon // // Sets the icon to be displayed on a IconicButton. // // Calling this function with a button type other than Iconic has no effect. // INT32 SetButtonIcon( INT32 iButtonID, INT16 Icon, INT16 IconIndex ) { GUI_BUTTON *b; if( iButtonID < 0 || iButtonID >= MAX_BUTTONS ) { sprintf( str, "Attempting to set button icon with out of range buttonID %d.", iButtonID ); AssertMsg( 0, str ); return -1; } if( Icon < 0 || Icon >= MAX_BUTTON_ICONS ) { sprintf( str, "Attempting to set button[%d] icon with out of range icon index %d.", iButtonID, Icon ); AssertMsg( 0, str ); return -1; } b = ButtonList[ iButtonID ]; if( !b ) { sprintf( str, "Attempting to set deleted button icon with buttonID %d", iButtonID ); AssertMsg( 0, str ); return -1; } // If button isn't an icon button, ignore this call if(((b->uiFlags & BUTTON_TYPES) == BUTTON_QUICK) || ((b->uiFlags & BUTTON_TYPES) == BUTTON_HOT_SPOT) || ((b->uiFlags & BUTTON_TYPES) == BUTTON_GENERIC)) { return -1; } // Set the icon number and index to use for this button b->iIconID = Icon; b->usIconIndex = IconIndex; return Icon; } //============================================================================= // CreateIconButton // // Creates an Iconic type button. // INT32 CreateIconButton(INT16 Icon,INT16 IconIndex,INT16 GenImg,INT16 xloc,INT16 yloc,INT16 w,INT16 h,INT32 Type,INT16 Priority,GUI_CALLBACK MoveCallback,GUI_CALLBACK ClickCallback) { GUI_BUTTON *b; INT32 ButtonNum; INT32 BType,x; if( xloc < 0 || yloc < 0 ) { sprintf( str, "Attempting to CreateIconButton with invalid position of %d,%d", xloc, yloc ); AssertMsg( 0, str ); } if( GenImg < -1 || GenImg >= MAX_GENERIC_PICS ) { sprintf( str, "Attempting to CreateIconButton with out of range iconID %d.", GenImg ); AssertMsg( 0, str ); } // if button size is too small, adjust it. if(w<4) w=4; if(h<3) h=3; // Strip off any extraneous bits from button type BType = Type & ( BUTTON_TYPE_MASK | BUTTON_NEWTOGGLE ); // Get a button number (slot) for this new button if((ButtonNum = GetNextButtonNumber()) == BUTTON_NO_SLOT) { DbgMessage(TOPIC_BUTTON_HANDLER,DBG_LEVEL_0,"CreateIconButton: No more button slots"); return(-1); } // Allocate memory for the GUI_BUTTON structure if((b=(GUI_BUTTON *)MemAlloc(sizeof(GUI_BUTTON))) == NULL) { DbgMessage(TOPIC_BUTTON_HANDLER,DBG_LEVEL_0,"CreateIconButton: Can't alloc mem for button struct"); return(-1); } // Init the values in the struct b->uiFlags = BUTTON_DIRTY; b->uiOldFlags = 0; b->IDNum = ButtonNum; b->XLoc = xloc; b->YLoc = yloc; if(GenImg<0) b->ImageNum = 0; else b->ImageNum = GenImg; for(x=0;x<4;x++) b->UserData[x] = 0; b->Group = -1; b->bDefaultStatus = DEFAULT_STATUS_NONE; b->bDisabledStyle = DISABLED_STYLE_DEFAULT; //Init text b->string = NULL; b->usFont = 0; b->fMultiColor=FALSE; b->sForeColor = 0; b->sWrappedWidth = -1; b->sShadowColor = -1; b->sForeColorDown = -1; b->sShadowColorDown = -1; b->sForeColorHilited = -1; b->sShadowColorHilited = -1; b->bJustification = BUTTON_TEXT_CENTER; b->bTextXOffset = -1; b->bTextYOffset = -1; b->bTextXSubOffSet = 0; b->bTextYSubOffSet = 0; b->fShiftText = TRUE; //Init icon b->iIconID = Icon; b->usIconIndex = IconIndex; b->bIconXOffset = -1; b->bIconYOffset = -1; b->fShiftImage = TRUE; // Set the click callback function (if any) if(ClickCallback != BUTTON_NO_CALLBACK) { b->ClickCallback = ClickCallback; BType |= BUTTON_CLICK_CALLBACK; } else b->ClickCallback = BUTTON_NO_CALLBACK; // Set the move callback function (if any) if(MoveCallback != BUTTON_NO_CALLBACK) { b->MoveCallback = MoveCallback; BType |= BUTTON_MOVE_CALLBACK; } else b->MoveCallback = BUTTON_NO_CALLBACK; // Define a mouse region for this button MSYS_DefineRegion(&b->Area, (UINT16)xloc, (UINT16)yloc, (UINT16)(xloc+w), (UINT16)(yloc+h), (INT8)Priority, MSYS_STARTING_CURSORVAL, (MOUSE_CALLBACK)QuickButtonCallbackMMove, (MOUSE_CALLBACK)QuickButtonCallbackMButn); // Link the mouse region to this button (for callback purposes) MSYS_SetRegionUserData(&b->Area,0,ButtonNum); // Set this button's flags b->uiFlags |= ( BUTTON_ENABLED | BType | BUTTON_GENERIC ); #ifdef _JA2_RENDER_DIRTY b->BackRect = -1; #endif // Add button to the button list #ifdef BUTTONSYSTEM_DEBUGGING AssertFailIfIdenticalButtonAttributesFound( b ); #endif ButtonList[ButtonNum]=b; SpecifyButtonSoundScheme( b->IDNum, BUTTON_SOUND_SCHEME_GENERIC ); // return this button's slot number return(ButtonNum); } //Creates a generic button with text on it. INT32 CreateTextButton(UINT16 *string, UINT32 uiFont, INT16 sForeColor, INT16 sShadowColor, INT16 GenImg, INT16 xloc, INT16 yloc, INT16 w, INT16 h, INT32 Type, INT16 Priority,GUI_CALLBACK MoveCallback, GUI_CALLBACK ClickCallback) { GUI_BUTTON *b; INT32 ButtonNum; INT32 BType,x; if( xloc < 0 || yloc < 0 ) { sprintf( str, "Attempting to CreateTextButton with invalid position of %d,%d", xloc, yloc ); AssertMsg( 0, str ); } if( GenImg < -1 || GenImg >= MAX_GENERIC_PICS ) { sprintf( str, "Attempting to CreateTextButton with out of range iconID %d.", GenImg ); AssertMsg( 0, str ); } // if button size is too small, adjust it. if(w<4) w=4; if(h<3) h=3; // Strip off any extraneous bits from button type BType = Type & ( BUTTON_TYPE_MASK | BUTTON_NEWTOGGLE ); // Get a button number for this new button if((ButtonNum = GetNextButtonNumber()) == BUTTON_NO_SLOT) { DbgMessage(TOPIC_BUTTON_HANDLER,DBG_LEVEL_0,"CreateTextButton: No more button slots"); return(-1); } // Allocate memory for a GUI_BUTTON structure if((b=(GUI_BUTTON *)MemAlloc(sizeof(GUI_BUTTON))) == NULL) { DbgMessage(TOPIC_BUTTON_HANDLER,DBG_LEVEL_0,"CreateTextButton: Can't alloc mem for button struct"); return(-1); } // Allocate memory for the button's text string... b->string = NULL; if ( string && wcslen( string ) ) { b->string = (UINT16*)MemAlloc( (wcslen(string)+1)*sizeof(UINT16) ); AssertMsg( b->string, "Out of memory error: Couldn't allocate string in CreateTextButton." ); wcscpy( b->string, string ); } // Init the button structure variables b->uiFlags = BUTTON_DIRTY; b->uiOldFlags = 0; b->IDNum = ButtonNum; b->XLoc = xloc; b->YLoc = yloc; if(GenImg<0) b->ImageNum = 0; else b->ImageNum = GenImg; for(x=0;x<4;x++) b->UserData[x] = 0; b->Group = -1; b->bDefaultStatus = DEFAULT_STATUS_NONE; b->bDisabledStyle = DISABLED_STYLE_DEFAULT; //Init string b->usFont = (UINT16)uiFont; b->fMultiColor=FALSE; b->sForeColor = sForeColor; b->sWrappedWidth = -1; b->sShadowColor = sShadowColor; b->sForeColorDown = -1; b->sShadowColorDown = -1; b->sForeColorHilited = -1; b->sShadowColorHilited = -1; b->bJustification = BUTTON_TEXT_CENTER; b->bTextXOffset = -1; b->bTextYOffset = -1; b->bTextXSubOffSet = 0; b->bTextYSubOffSet = 0; b->fShiftText = TRUE; //Init icon b->iIconID = -1; b->usIconIndex = -1; b->bIconXOffset = -1; b->bIconYOffset = -1; b->fShiftImage = TRUE; // Set the button click callback function (if any) if(ClickCallback != BUTTON_NO_CALLBACK) { b->ClickCallback = ClickCallback; BType |= BUTTON_CLICK_CALLBACK; } else b->ClickCallback = BUTTON_NO_CALLBACK; // Set the button's mouse movement callback function (if any) if(MoveCallback != BUTTON_NO_CALLBACK) { b->MoveCallback = MoveCallback; BType |= BUTTON_MOVE_CALLBACK; } else b->MoveCallback = BUTTON_NO_CALLBACK; // Define a MOUSE_REGION for this button MSYS_DefineRegion(&b->Area, (UINT16)xloc, (UINT16)yloc, (UINT16)(xloc+w), (UINT16)(yloc+h), (INT8)Priority, MSYS_STARTING_CURSORVAL, (MOUSE_CALLBACK)QuickButtonCallbackMMove, (MOUSE_CALLBACK)QuickButtonCallbackMButn); // Link the MOUSE_REGION to this button MSYS_SetRegionUserData(&b->Area,0,ButtonNum); // Set the flags for this button b->uiFlags |= ( BUTTON_ENABLED | BType | BUTTON_GENERIC); #ifdef _JA2_RENDER_DIRTY b->BackRect = -1; #endif // Add this button to the button list #ifdef BUTTONSYSTEM_DEBUGGING AssertFailIfIdenticalButtonAttributesFound( b ); #endif ButtonList[ButtonNum]=b; SpecifyButtonSoundScheme( b->IDNum, BUTTON_SOUND_SCHEME_GENERIC ); // return the slot number return(ButtonNum); } //============================================================================= // CreateHotSpot // // Creates a button like HotSpot. HotSpots have no graphics associated with // them. // INT32 CreateHotSpot(INT16 xloc, INT16 yloc, INT16 Width, INT16 Height,INT16 Priority,GUI_CALLBACK MoveCallback,GUI_CALLBACK ClickCallback) { GUI_BUTTON *b; INT32 ButtonNum; INT16 BType,x; if( xloc < 0 || yloc < 0 || Width < 0 || Height < 0 ) { sprintf( str, "Attempting to CreateHotSpot with invalid coordinates: %d,%d, width: %d, and height: %d.", xloc, yloc, Width, Height ); AssertMsg( 0, str ); } BType=0; // Get a button number for this hotspot if((ButtonNum = GetNextButtonNumber()) == BUTTON_NO_SLOT) { DbgMessage(TOPIC_BUTTON_HANDLER,DBG_LEVEL_0,"CreateHotSpot: No more button slots"); return(-1); } // Allocate memory for the GUI_BUTTON structure if((b=(GUI_BUTTON *)MemAlloc(sizeof(GUI_BUTTON))) == NULL) { DbgMessage(TOPIC_BUTTON_HANDLER,DBG_LEVEL_0,"CreateHotSpot: Can't alloc mem for button struct"); return(-1); } // Init the structure values b->uiFlags = 0; b->uiOldFlags = 0; b->IDNum = ButtonNum; b->XLoc = xloc; b->YLoc = yloc; b->ImageNum = 0xffffffff; for(x=0;x<4;x++) b->UserData[x] = 0; b->Group = -1; b->string = NULL; // Set the hotspot click callback function (if any) if(ClickCallback != BUTTON_NO_CALLBACK) { b->ClickCallback = ClickCallback; BType |= BUTTON_CLICK_CALLBACK; } else b->ClickCallback = BUTTON_NO_CALLBACK; // Set the hotspot's mouse movement callback function (if any) if(MoveCallback != BUTTON_NO_CALLBACK) { b->MoveCallback = MoveCallback; BType |= BUTTON_MOVE_CALLBACK; } else b->MoveCallback = BUTTON_NO_CALLBACK; // define a MOUSE_REGION for this hotspot MSYS_DefineRegion(&b->Area,(UINT16)xloc,(UINT16)yloc,(UINT16)(xloc+Width),(UINT16)(yloc+Height), (INT8)Priority, MSYS_STARTING_CURSORVAL, (MOUSE_CALLBACK)QuickButtonCallbackMMove, (MOUSE_CALLBACK)QuickButtonCallbackMButn); // Link the MOUSE_REGION to this hotspot MSYS_SetRegionUserData(&b->Area,0,ButtonNum); // Set the flags entry for this hotspot b->uiFlags |= (BUTTON_ENABLED|BType|BUTTON_HOT_SPOT); #ifdef _JA2_RENDER_DIRTY b->BackRect = -1; #endif // Add this button (hotspot) to the button list #ifdef BUTTONSYSTEM_DEBUGGING AssertFailIfIdenticalButtonAttributesFound( b ); #endif ButtonList[ButtonNum]=b; SpecifyButtonSoundScheme( b->IDNum, BUTTON_SOUND_SCHEME_GENERIC ); // return the button slot number return(ButtonNum); } // ============================================================================ // Addition Oct15/97, Carter // SetButtonCursor // will simply set the cursor for the mouse region the button occupies BOOLEAN SetButtonCursor(INT32 iBtnId, UINT16 crsr) { GUI_BUTTON *b; b = ButtonList[iBtnId]; if (!b) return FALSE; b->Area.Cursor = crsr; return TRUE; } //============================================================================= // QuickCreateButton // // Creates a QuickButton. QuickButtons only have graphics associated with // them. They cannot be re-sized, nor can the graphic be changed. // INT32 QuickCreateButton(UINT32 Image,INT16 xloc,INT16 yloc,INT32 Type,INT16 Priority,GUI_CALLBACK MoveCallback,GUI_CALLBACK ClickCallback) { GUI_BUTTON *b; INT32 ButtonNum; INT32 BType,x; if( xloc < 0 || yloc < 0 ) { sprintf( str, "Attempting to QuickCreateButton with invalid position of %d,%d", xloc, yloc ); AssertMsg( 0, str ); } if( Image < 0 || Image >= MAX_BUTTON_PICS ) { sprintf( str, "Attempting to QuickCreateButton with out of range ImageID %d.", Image ); AssertMsg( 0, str ); } // Strip off any extraneous bits from button type BType = Type & ( BUTTON_TYPE_MASK | BUTTON_NEWTOGGLE ); // Is there a QuickButton image in the given image slot? if(ButtonPictures[Image].vobj == NULL) { DbgMessage(TOPIC_BUTTON_HANDLER,DBG_LEVEL_0,"QuickCreateButton: Invalid button image number"); return(-1); } // Get a new button number if((ButtonNum = GetNextButtonNumber()) == BUTTON_NO_SLOT) { DbgMessage(TOPIC_BUTTON_HANDLER,DBG_LEVEL_0,"QuickCreateButton: No more button slots"); return(-1); } // Allocate memory for a GUI_BUTTON structure if((b=(GUI_BUTTON *)MemAlloc(sizeof(GUI_BUTTON))) == NULL) { DbgMessage(TOPIC_BUTTON_HANDLER,DBG_LEVEL_0,"QuickCreateButton: Can't alloc mem for button struct"); return(-1); } // Set the values for this buttn b->uiFlags = BUTTON_DIRTY; b->uiOldFlags = 0; // Set someflags if of s certain type.... if ( Type & BUTTON_NEWTOGGLE ) { b->uiFlags |= BUTTON_NEWTOGGLE; } // shadow style b->bDefaultStatus = DEFAULT_STATUS_NONE; b->bDisabledStyle = DISABLED_STYLE_DEFAULT; b->Group = -1; //Init string b->string = NULL; b->usFont = 0; b->fMultiColor=FALSE; b->sForeColor = 0; b->sWrappedWidth = -1; b->sShadowColor = -1; b->sForeColorDown = -1; b->sShadowColorDown = -1; b->sForeColorHilited = -1; b->sShadowColorHilited = -1; b->bJustification = BUTTON_TEXT_CENTER; b->bTextXOffset = -1; b->bTextYOffset = -1; b->bTextXSubOffSet = 0; b->bTextYSubOffSet = 0; b->fShiftText = TRUE; //Init icon b->iIconID = -1; b->usIconIndex = -1; b->bIconXOffset = -1; b->bIconYOffset = -1; b->fShiftImage = TRUE; //Init quickbutton b->IDNum = ButtonNum; b->ImageNum = Image; for(x=0;x<4;x++) b->UserData[x] = 0; b->XLoc = xloc; b->YLoc = yloc; b->ubToggleButtonOldState = 0; b->ubToggleButtonActivated = FALSE; // Set the button click callback function (if any) if(ClickCallback != BUTTON_NO_CALLBACK) { b->ClickCallback = ClickCallback; BType |= BUTTON_CLICK_CALLBACK; } else b->ClickCallback = BUTTON_NO_CALLBACK; // Set the button's mouse movement callback function (if any) if(MoveCallback != BUTTON_NO_CALLBACK) { b->MoveCallback = MoveCallback; BType |= BUTTON_MOVE_CALLBACK; } else b->MoveCallback = BUTTON_NO_CALLBACK; memset( &b->Area, 0, sizeof( MOUSE_REGION ) ); // Define a MOUSE_REGION for this QuickButton MSYS_DefineRegion(&b->Area,(UINT16)xloc,(UINT16)yloc, (UINT16)(xloc+(INT16)ButtonPictures[Image].MaxWidth), (UINT16)(yloc+(INT16)ButtonPictures[Image].MaxHeight), (INT8)Priority, MSYS_STARTING_CURSORVAL, (MOUSE_CALLBACK)QuickButtonCallbackMMove, (MOUSE_CALLBACK)QuickButtonCallbackMButn); // Link the MOUSE_REGION with this QuickButton MSYS_SetRegionUserData(&b->Area,0,ButtonNum); // Set the flags for this button b->uiFlags |= BUTTON_ENABLED | BType | BUTTON_QUICK; #ifdef _JA2_RENDER_DIRTY b->BackRect = -1; #endif // Add this QuickButton to the button list #ifdef BUTTONSYSTEM_DEBUGGING AssertFailIfIdenticalButtonAttributesFound( b ); #endif ButtonList[ButtonNum]=b; SpecifyButtonSoundScheme( b->IDNum, BUTTON_SOUND_SCHEME_GENERIC ); // return the button number (slot) return(ButtonNum); } //A hybrid of QuickCreateButton. Takes a lot less parameters, but makes more assumptions. It self manages the //loading, and deleting of the image. The size of the image determines the size of the button. It also uses //the default move callback which emulates Win95. Finally, it sets the priority to normal. The function you //choose also determines the type of button (toggle, notoggle, or newtoggle) INT32 CreateEasyNoToggleButton ( INT32 x, INT32 y, UINT8 *filename, GUI_CALLBACK ClickCallback ) { return CreateSimpleButton( x, y, filename, BUTTON_NO_TOGGLE, MSYS_PRIORITY_NORMAL, ClickCallback ); } INT32 CreateEasyToggleButton ( INT32 x, INT32 y, UINT8 *filename, GUI_CALLBACK ClickCallback ) { return CreateSimpleButton( x, y, filename, BUTTON_TOGGLE, MSYS_PRIORITY_NORMAL, ClickCallback ); } INT32 CreateEasyNewToggleButton( INT32 x, INT32 y, UINT8 *filename, GUI_CALLBACK ClickCallback ) { return CreateSimpleButton( x, y, filename, BUTTON_NEWTOGGLE, MSYS_PRIORITY_NORMAL, ClickCallback ); } //Same as above, but accepts specify toggle type INT32 CreateEasyButton( INT32 x, INT32 y, UINT8 *filename, INT32 Type, GUI_CALLBACK ClickCallback) { return CreateSimpleButton( x, y, filename, Type, MSYS_PRIORITY_NORMAL, ClickCallback ); } //Same as above, but accepts priority specification. INT32 CreateSimpleButton( INT32 x, INT32 y, UINT8 *filename, INT32 Type, INT16 Priority, GUI_CALLBACK ClickCallback ) { INT32 ButPic,ButNum; if( !filename || !strlen(filename) ) AssertMsg( 0, "Attempting to CreateSimpleButton with null filename." ); if( ( ButPic = LoadButtonImage( filename, -1, 1, 2, 3, 4 ) ) == -1) { DbgMessage(TOPIC_BUTTON_HANDLER,DBG_LEVEL_0,"Can't load button image"); return( -1 ); } ButNum = (INT16)QuickCreateButton( ButPic, (INT16)x,(INT16)y, Type, Priority, DEFAULT_MOVE_CALLBACK, ClickCallback); AssertMsg( ButNum != -1, "Failed to CreateSimpleButton." ); ButtonList[ ButNum ]->uiFlags |= BUTTON_SELFDELETE_IMAGE; SpecifyDisabledButtonStyle( ButNum, DISABLED_STYLE_SHADED ); return( ButNum ); } INT32 CreateIconAndTextButton( INT32 Image, UINT16 *string, UINT32 uiFont, INT16 sForeColor, INT16 sShadowColor, INT16 sForeColorDown, INT16 sShadowColorDown, INT8 bJustification, INT16 xloc, INT16 yloc, INT32 Type, INT16 Priority, GUI_CALLBACK MoveCallback,GUI_CALLBACK ClickCallback) { GUI_BUTTON *b; INT32 iButtonID; INT32 BType,x; if( xloc < 0 || yloc < 0 ) { sprintf( str, "Attempting to CreateIconAndTextButton with invalid position of %d,%d", xloc, yloc ); AssertMsg( 0, str ); } if( Image < 0 || Image >= MAX_BUTTON_PICS ) { sprintf( str, "Attemting to CreateIconAndTextButton with out of range ImageID %d.", Image ); AssertMsg( 0, str ); } // Strip off any extraneous bits from button type BType = Type & ( BUTTON_TYPE_MASK | BUTTON_NEWTOGGLE ); // Is there a QuickButton image in the given image slot? if(ButtonPictures[Image].vobj == NULL) { DbgMessage(TOPIC_BUTTON_HANDLER,DBG_LEVEL_0,"QuickCreateButton: Invalid button image number"); return(-1); } // Get a new button number if((iButtonID = GetNextButtonNumber()) == BUTTON_NO_SLOT) { DbgMessage(TOPIC_BUTTON_HANDLER,DBG_LEVEL_0,"QuickCreateButton: No more button slots"); return(-1); } // Allocate memory for a GUI_BUTTON structure if((b=(GUI_BUTTON *)MemAlloc(sizeof(GUI_BUTTON))) == NULL) { DbgMessage(TOPIC_BUTTON_HANDLER,DBG_LEVEL_0,"QuickCreateButton: Can't alloc mem for button struct"); return(-1); } // Set the values for this button b->uiFlags = BUTTON_DIRTY; b->uiOldFlags = 0; b->IDNum = iButtonID; b->XLoc = xloc; b->YLoc = yloc; b->ImageNum = Image; for(x=0;x<4;x++) b->UserData[x] = 0; b->Group = -1; b->bDefaultStatus = DEFAULT_STATUS_NONE; b->bDisabledStyle = DISABLED_STYLE_DEFAULT; // Allocate memory for the button's text string... b->string = NULL; if ( string ) { b->string = (UINT16*)MemAlloc( (wcslen(string)+1)*sizeof(UINT16) ); AssertMsg( b->string, "Out of memory error: Couldn't allocate string in CreateIconAndTextButton." ); wcscpy( b->string, string ); } b->bJustification = bJustification; b->usFont = (UINT16)uiFont; b->fMultiColor=FALSE; b->sForeColor = sForeColor; b->sWrappedWidth = -1; b->sShadowColor = sShadowColor; b->sForeColorDown = sForeColorDown; b->sShadowColorDown = sShadowColorDown; b->sForeColorHilited = -1; b->sShadowColorHilited = -1; b->bTextXOffset = -1; b->bTextYOffset = -1; b->bTextXSubOffSet = 0; b->bTextYSubOffSet = 0; b->fShiftText = TRUE; b->iIconID = -1; b->usIconIndex = 0; // Set the button click callback function (if any) if(ClickCallback != BUTTON_NO_CALLBACK) { b->ClickCallback = ClickCallback; BType |= BUTTON_CLICK_CALLBACK; } else b->ClickCallback = BUTTON_NO_CALLBACK; // Set the button's mouse movement callback function (if any) if(MoveCallback != BUTTON_NO_CALLBACK) { b->MoveCallback = MoveCallback; BType |= BUTTON_MOVE_CALLBACK; } else b->MoveCallback = BUTTON_NO_CALLBACK; // Define a MOUSE_REGION for this QuickButton MSYS_DefineRegion(&b->Area,(UINT16)xloc,(UINT16)yloc, (UINT16)(xloc+(INT16)ButtonPictures[Image].MaxWidth), (UINT16)(yloc+(INT16)ButtonPictures[Image].MaxHeight), (INT8)Priority, MSYS_STARTING_CURSORVAL, (MOUSE_CALLBACK)QuickButtonCallbackMMove, (MOUSE_CALLBACK)QuickButtonCallbackMButn); // Link the MOUSE_REGION with this QuickButton MSYS_SetRegionUserData(&b->Area,0,iButtonID); // Set the flags for this button b->uiFlags |= ( BUTTON_ENABLED | BType | BUTTON_QUICK ); #ifdef _JA2_RENDER_DIRTY b->BackRect = -1; #endif // Add this QuickButton to the button list #ifdef BUTTONSYSTEM_DEBUGGING AssertFailIfIdenticalButtonAttributesFound( b ); #endif ButtonList[iButtonID]=b; SpecifyButtonSoundScheme( b->IDNum, BUTTON_SOUND_SCHEME_GENERIC ); // return the button number (slot) return(iButtonID); } //New functions void SpecifyButtonText( INT32 iButtonID, UINT16 *string ) { GUI_BUTTON *b; Assert( iButtonID >= 0 ); Assert( iButtonID < MAX_BUTTONS ); b = ButtonList[ iButtonID ]; //free the previous strings memory if applicable if( b->string ) MemFree( b->string ); b->string = NULL; if( string && wcslen( string ) ) { //allocate memory for the new string b->string = (UINT16*)MemAlloc( (wcslen(string)+1)*sizeof(UINT16) ); Assert( b->string ); //copy the string to the button wcscpy( b->string, string ); b->uiFlags |= BUTTON_DIRTY; } } void SpecifyButtonMultiColorFont(INT32 iButtonID, BOOLEAN fMultiColor) { GUI_BUTTON *b; Assert( iButtonID >= 0 ); Assert( iButtonID < MAX_BUTTONS ); b = ButtonList[ iButtonID ]; Assert( b ); b->fMultiColor = fMultiColor; b->uiFlags |= BUTTON_DIRTY ; } void SpecifyButtonFont( INT32 iButtonID, UINT32 uiFont ) { GUI_BUTTON *b; Assert( iButtonID >= 0 ); Assert( iButtonID < MAX_BUTTONS ); b = ButtonList[ iButtonID ]; Assert( b ); b->usFont = (UINT16)uiFont; b->uiFlags |= BUTTON_DIRTY ; } void SpecifyButtonUpTextColors( INT32 iButtonID, INT16 sForeColor, INT16 sShadowColor ) { GUI_BUTTON *b; Assert( iButtonID >= 0 ); Assert( iButtonID < MAX_BUTTONS ); b = ButtonList[ iButtonID ]; Assert( b ); b->sForeColor = sForeColor; b->sShadowColor = sShadowColor; b->uiFlags |= BUTTON_DIRTY ; } void SpecifyButtonDownTextColors( INT32 iButtonID, INT16 sForeColorDown, INT16 sShadowColorDown ) { GUI_BUTTON *b; Assert( iButtonID >= 0 ); Assert( iButtonID < MAX_BUTTONS ); b = ButtonList[ iButtonID ]; Assert( b ); b->sForeColorDown = sForeColorDown; b->sShadowColorDown = sShadowColorDown; b->uiFlags |= BUTTON_DIRTY ; } void SpecifyButtonHilitedTextColors( INT32 iButtonID, INT16 sForeColorHilited, INT16 sShadowColorHilited ) { GUI_BUTTON *b; Assert( iButtonID >= 0 ); Assert( iButtonID < MAX_BUTTONS ); b = ButtonList[ iButtonID ]; Assert( b ); b->sForeColorHilited = sForeColorHilited; b->sShadowColorHilited = sShadowColorHilited; b->uiFlags |= BUTTON_DIRTY ; } void SpecifyButtonTextJustification( INT32 iButtonID, INT8 bJustification ) { GUI_BUTTON *b; Assert( iButtonID >= 0 ); Assert( iButtonID < MAX_BUTTONS ); b = ButtonList[ iButtonID ]; Assert( b ); //Range check: if invalid, then set it to center justified. if( bJustification < BUTTON_TEXT_LEFT || bJustification > BUTTON_TEXT_RIGHT ) bJustification = BUTTON_TEXT_CENTER; b->bJustification = bJustification; b->uiFlags |= BUTTON_DIRTY ; } void SpecifyFullButtonTextAttributes( INT32 iButtonID, UINT16 *string, INT32 uiFont, INT16 sForeColor, INT16 sShadowColor, INT16 sForeColorDown, INT16 sShadowColorDown, INT8 bJustification ) { GUI_BUTTON *b; Assert( iButtonID >= 0 ); Assert( iButtonID < MAX_BUTTONS ); b = ButtonList[ iButtonID ]; Assert( b ); //Copy over information SpecifyButtonText( iButtonID, string ); b->usFont = (UINT16)uiFont; b->sForeColor = sForeColor; b->sShadowColor = sShadowColor; b->sForeColorDown = sForeColorDown; b->sShadowColorDown = sShadowColorDown; //Range check: if invalid, then set it to center justified. if( bJustification < BUTTON_TEXT_LEFT || bJustification > BUTTON_TEXT_RIGHT ) bJustification = BUTTON_TEXT_CENTER; b->bJustification = bJustification; b->uiFlags |= BUTTON_DIRTY ; } void SpecifyGeneralButtonTextAttributes( INT32 iButtonID, UINT16 *string, INT32 uiFont, INT16 sForeColor, INT16 sShadowColor ) { GUI_BUTTON *b; Assert( iButtonID >= 0 ); Assert( iButtonID < MAX_BUTTONS ); b = ButtonList[ iButtonID ]; Assert( b ); //Copy over information SpecifyButtonText( iButtonID, string ); b->usFont = (UINT16)uiFont; b->sForeColor = sForeColor; b->sShadowColor = sShadowColor; b->uiFlags |= BUTTON_DIRTY ; } void SpecifyButtonTextOffsets( INT32 iButtonID, INT8 bTextXOffset, INT8 bTextYOffset, BOOLEAN fShiftText ) { GUI_BUTTON *b; Assert( iButtonID >= 0 ); Assert( iButtonID < MAX_BUTTONS ); b = ButtonList[ iButtonID ]; Assert( b ); //Copy over information b->bTextXOffset = bTextXOffset; b->bTextYOffset = bTextYOffset; b->fShiftText = fShiftText; } void SpecifyButtonTextSubOffsets( INT32 iButtonID, INT8 bTextXOffset, INT8 bTextYOffset, BOOLEAN fShiftText ) { GUI_BUTTON *b; Assert( iButtonID >= 0 ); Assert( iButtonID < MAX_BUTTONS ); b = ButtonList[ iButtonID ]; Assert( b ); //Copy over information b->bTextXSubOffSet = bTextXOffset; b->bTextYSubOffSet = bTextYOffset; b->fShiftText = fShiftText; } void SpecifyButtonTextWrappedWidth(INT32 iButtonID, INT16 sWrappedWidth) { GUI_BUTTON *b; Assert( iButtonID >= 0 ); Assert( iButtonID < MAX_BUTTONS ); b = ButtonList[ iButtonID ]; Assert( b ); b->sWrappedWidth = sWrappedWidth; } void SpecifyDisabledButtonStyle( INT32 iButtonID, INT8 bStyle ) { GUI_BUTTON *b; Assert( iButtonID >= 0 ); Assert( iButtonID < MAX_BUTTONS ); b = ButtonList[ iButtonID ]; Assert( b ); Assert( bStyle >= DISABLED_STYLE_NONE && bStyle <= DISABLED_STYLE_SHADED ); b->bDisabledStyle = bStyle; } //Note: Text is always on top //If fShiftImage is true, then the image will shift down one pixel and right one pixel //just like the text does. BOOLEAN SpecifyButtonIcon( INT32 iButtonID, INT32 iVideoObjectID, UINT16 usVideoObjectIndex, INT8 bXOffset, INT8 bYOffset, BOOLEAN fShiftImage ) { GUI_BUTTON *b; Assert( iButtonID >= 0 ); Assert( iButtonID < MAX_BUTTONS ); b = ButtonList[ iButtonID ]; Assert( b ); b->iIconID = iVideoObjectID; b->usIconIndex = usVideoObjectIndex; if( b->iIconID == -1 ) return FALSE; b->bIconXOffset = bXOffset; b->bIconYOffset = bYOffset; b->fShiftImage = TRUE; b->uiFlags |= BUTTON_DIRTY; return TRUE; } void RemoveTextFromButton( INT32 iButtonID ) { GUI_BUTTON *b; Assert( iButtonID >= 0 ); Assert( iButtonID < MAX_BUTTONS ); b = ButtonList[ iButtonID ]; Assert( b ); //Init string if( b->string ) MemFree( b->string ); b->string = NULL; b->usFont = 0; b->sForeColor = 0; b->sWrappedWidth = -1; b->sShadowColor = -1; b->sForeColorDown = -1; b->sShadowColorDown = -1; b->sForeColorHilited = -1; b->sShadowColorHilited = -1; b->bJustification = BUTTON_TEXT_CENTER; b->bTextXOffset = -1; b->bTextYOffset = -1; b->bTextXSubOffSet = 0; b->bTextYSubOffSet = 0; b->fShiftText = TRUE; } void RemoveIconFromButton( INT32 iButtonID ) { GUI_BUTTON *b; Assert( iButtonID >= 0 ); Assert( iButtonID < MAX_BUTTONS ); b = ButtonList[ iButtonID ]; Assert( b ); //Clear icon b->iIconID = -1; b->usIconIndex = -1; b->bIconXOffset = -1; b->bIconYOffset = -1; b->fShiftImage = TRUE; } void AllowDisabledButtonFastHelp( INT32 iButtonID, BOOLEAN fAllow ) { GUI_BUTTON *b; Assert( iButtonID >= 0 ); Assert( iButtonID < MAX_BUTTONS ); b = ButtonList[ iButtonID ]; Assert( b ); b->Area.uiFlags |= MSYS_ALLOW_DISABLED_FASTHELP; } //============================================================================= // SetButtonFastHelpText // // Set the text that will be displayed as the FastHelp // void SetButtonFastHelpText(INT32 iButton, UINT16 *Text) { GUI_BUTTON *b; if(iButton<0 || iButton>MAX_BUTTONS) return; b = ButtonList[iButton]; AssertMsg( b, "Called SetButtonFastHelpText() with a non-existant button." ); SetRegionFastHelpText( &b->Area, Text ); } void SetBtnHelpEndCallback( INT32 iButton, MOUSE_HELPTEXT_DONE_CALLBACK CallbackFxn ) { GUI_BUTTON *b; if(iButton<0 || iButton>MAX_BUTTONS) return; b = ButtonList[iButton]; AssertMsg( b, "Called SetBtnHelpEndCallback() with a non-existant button." ); SetRegionHelpEndCallback( &b->Area, CallbackFxn ); } //============================================================================= // QuickButtonCallbackMMove // // Dispatches all button callbacks for mouse movement. This function gets // called by the Mouse System. *DO NOT CALL DIRECTLY* // void QuickButtonCallbackMMove(MOUSE_REGION *reg,INT32 reason) { GUI_BUTTON *b; INT32 iButtonID; Assert(reg != NULL); iButtonID = MSYS_GetRegionUserData(reg,0); sprintf( str, "QuickButtonCallbackMMove: Mouse Region #%d (%d,%d to %d,%d) has invalid buttonID %d", reg->IDNumber, reg->RegionTopLeftX, reg->RegionTopLeftY, reg->RegionBottomRightX, reg->RegionBottomRightY, iButtonID ); AssertMsg( iButtonID >= 0, str ); AssertMsg( iButtonID < MAX_BUTTONS, str ); b = ButtonList[ iButtonID ]; AssertMsg( b != NULL, str ); if( !b ) return; //This is getting called when Adding new regions... if( b->uiFlags & BUTTON_ENABLED && reason & (MSYS_CALLBACK_REASON_LOST_MOUSE | MSYS_CALLBACK_REASON_GAIN_MOUSE) ) { b->uiFlags |= BUTTON_DIRTY; } // Mouse moved on the button, so reset it's timer to maximum. if( b->Area.uiFlags & MSYS_CALLBACK_REASON_GAIN_MOUSE ) { //check for sound playing stuff if( b->ubSoundSchemeID ) { if( &b->Area == MSYS_PrevRegion && !gpAnchoredButton ) { if( b->uiFlags & BUTTON_ENABLED ) { PlayButtonSound( iButtonID, BUTTON_SOUND_MOVED_ONTO ); } else { PlayButtonSound( iButtonID, BUTTON_SOUND_DISABLED_MOVED_ONTO ); } } } } else { //Check if we should play a sound if( b->ubSoundSchemeID ) { if( b->uiFlags & BUTTON_ENABLED ) { if( &b->Area == MSYS_PrevRegion && !gpAnchoredButton ) { PlayButtonSound( iButtonID, BUTTON_SOUND_MOVED_OFF_OF ); } } else { PlayButtonSound( iButtonID, BUTTON_SOUND_DISABLED_MOVED_OFF_OF ); } } } // ATE: New stuff for toggle buttons that work with new Win95 paridigm if ( ( b->uiFlags & BUTTON_NEWTOGGLE ) ) { if(reason & MSYS_CALLBACK_REASON_LOST_MOUSE ) { if ( b->ubToggleButtonActivated ) { b->ubToggleButtonActivated = FALSE; if ( !b->ubToggleButtonOldState ) { b->uiFlags &= (~BUTTON_CLICKED_ON ); } else { b->uiFlags |= BUTTON_CLICKED_ON; } } } } // If this button is enabled and there is a callback function associated with it, // call the callback function. if((b->uiFlags & BUTTON_ENABLED) && (b->uiFlags & BUTTON_MOVE_CALLBACK)) (b->MoveCallback)(b,reason); } //============================================================================= // QuickButtonCallbackMButn // // Dispatches all button callbacks for button presses. This function is // called by the Mouse System. *DO NOT CALL DIRECTLY* // void QuickButtonCallbackMButn( MOUSE_REGION *reg, INT32 reason ) { GUI_BUTTON *b; INT32 iButtonID; BOOLEAN MouseBtnDown; BOOLEAN StateBefore,StateAfter; Assert(reg != NULL); iButtonID = MSYS_GetRegionUserData(reg,0); sprintf( str, "QuickButtonCallbackMButn: Mouse Region #%d (%d,%d to %d,%d) has invalid buttonID %d", reg->IDNumber, reg->RegionTopLeftX, reg->RegionTopLeftY, reg->RegionBottomRightX, reg->RegionBottomRightY, iButtonID ); AssertMsg( iButtonID >= 0, str ); AssertMsg( iButtonID < MAX_BUTTONS, str ); b = ButtonList[ iButtonID ]; AssertMsg( b != NULL, str ); if( !b ) return; if( reason & (MSYS_CALLBACK_REASON_LBUTTON_DWN | MSYS_CALLBACK_REASON_RBUTTON_DWN) ) MouseBtnDown = TRUE; else MouseBtnDown = FALSE; StateBefore = (b->uiFlags & BUTTON_CLICKED_ON) ? (TRUE) : (FALSE); // ATE: New stuff for toggle buttons that work with new Win95 paridigm if( b->uiFlags & BUTTON_NEWTOGGLE && b->uiFlags & BUTTON_ENABLED ) { if(reason & MSYS_CALLBACK_REASON_LBUTTON_DWN ) { if ( !b->ubToggleButtonActivated ) { if ( !(b->uiFlags & BUTTON_CLICKED_ON ) ) { b->ubToggleButtonOldState = FALSE; b->uiFlags |= BUTTON_CLICKED_ON; } else { b->ubToggleButtonOldState = TRUE; b->uiFlags &= (~BUTTON_CLICKED_ON); } b->ubToggleButtonActivated = TRUE; } } else if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) { b->ubToggleButtonActivated = FALSE; } } //Kris: //Set the anchored button incase the user moves mouse off region while still holding //down the button, but only if the button is up. In Win95, buttons that are already //down, and anchored never change state, unless you release the mouse in the button area. if( b->MoveCallback == DEFAULT_MOVE_CALLBACK && b->uiFlags & BUTTON_ENABLED ) { if( reason & MSYS_CALLBACK_REASON_LBUTTON_DWN ) { gpAnchoredButton = b; gfAnchoredState = StateBefore; b->uiFlags |= BUTTON_CLICKED_ON; } else if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP && b->uiFlags & BUTTON_NO_TOGGLE ) { b->uiFlags &= (~BUTTON_CLICKED_ON); } } else if( b->uiFlags & BUTTON_CHECKBOX ) { if( reason & MSYS_CALLBACK_REASON_LBUTTON_DWN ) { //the check box button gets anchored, though it doesn't actually use the anchoring move callback. //The effect is different, we don't want to toggle the button state, but we do want to anchor this //button so that we don't effect any other buttons while we move the mouse around in anchor mode. gpAnchoredButton = b; gfAnchoredState = StateBefore; //Trick the before state of the button to be different so the sound will play properly as checkbox buttons //are processed differently. StateBefore = (b->uiFlags & BUTTON_CLICKED_ON) ? FALSE : TRUE; StateAfter = !StateBefore; } else if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) { b->uiFlags ^= BUTTON_CLICKED_ON; //toggle the checkbox state upon release inside button area. //Trick the before state of the button to be different so the sound will play properly as checkbox buttons //are processed differently. StateBefore = (b->uiFlags & BUTTON_CLICKED_ON) ? FALSE : TRUE; StateAfter = !StateBefore; } } // Should we play a sound if clicked on while disabled? if( b->ubSoundSchemeID && !(b->uiFlags & BUTTON_ENABLED) && MouseBtnDown ) { PlayButtonSound( iButtonID, BUTTON_SOUND_DISABLED_CLICK ); } // If this button is disabled, and no callbacks allowed when disabled // callback if(!(b->uiFlags & BUTTON_ENABLED) && !(b->uiFlags & BUTTON_ALLOW_DISABLED_CALLBACK)) return; // Button not enabled but allowed to use callback, then do that! if(!(b->uiFlags & BUTTON_ENABLED) && (b->uiFlags & BUTTON_ALLOW_DISABLED_CALLBACK)) { if( b->uiFlags & BUTTON_CLICK_CALLBACK ) { (b->ClickCallback)(b, reason | BUTTON_DISABLED_CALLBACK ); } return; } // If there is a callback function with this button, call it if(b->uiFlags & BUTTON_CLICK_CALLBACK) { //Kris: January 6, 1998 //Added these checks to avoid a case where it was possible to process a leftbuttonup message when //the button wasn't anchored, and should have been. gfDelayButtonDeletion = TRUE; if( !(reason & MSYS_CALLBACK_REASON_LBUTTON_UP) || b->MoveCallback != DEFAULT_MOVE_CALLBACK || b->MoveCallback == DEFAULT_MOVE_CALLBACK && gpPrevAnchoredButton == b ) (b->ClickCallback)(b,reason); gfDelayButtonDeletion = FALSE; } else if((reason & MSYS_CALLBACK_REASON_LBUTTON_DWN) && !(b->uiFlags & BUTTON_IGNORE_CLICKS)) { // Otherwise, do default action with this button. b->uiFlags^=BUTTON_CLICKED_ON; } if( b->uiFlags & BUTTON_CHECKBOX ) { StateAfter = (b->uiFlags & BUTTON_CLICKED_ON) ? (TRUE) : (FALSE); } // Play sounds for this enabled button (disabled sounds have already been done) if( b->ubSoundSchemeID && b->uiFlags & BUTTON_ENABLED ) { if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) { if( b->ubSoundSchemeID && StateBefore && !StateAfter ) { PlayButtonSound( iButtonID, BUTTON_SOUND_CLICKED_OFF ); } } else if( reason & MSYS_CALLBACK_REASON_LBUTTON_DWN ) { if( b->ubSoundSchemeID && !StateBefore && StateAfter) { PlayButtonSound( iButtonID, BUTTON_SOUND_CLICKED_ON ); } } } if( StateBefore != StateAfter ) { #ifdef JA2 InvalidateRegion(b->Area.RegionTopLeftX, b->Area.RegionTopLeftY, b->Area.RegionBottomRightX, b->Area.RegionBottomRightY); #endif } if( gfPendingButtonDeletion ) { RemoveButtonsMarkedForDeletion(); } } void RenderButtons(void) { INT32 iButtonID; BOOLEAN fOldButtonDown, fOldEnabled; GUI_BUTTON *b; SaveFontSettings(); for(iButtonID=0;iButtonIDArea.uiFlags & MSYS_REGION_ENABLED ) { // Check for buttonchanged status fOldButtonDown = (BOOLEAN)(b->uiFlags & BUTTON_CLICKED_ON); if ( fOldButtonDown != ( b->uiOldFlags & BUTTON_CLICKED_ON ) ) { //Something is different, set dirty! b->uiFlags |= BUTTON_DIRTY; } // Check for button dirty flags fOldEnabled = (BOOLEAN)(b->uiFlags & BUTTON_ENABLED); if ( fOldEnabled != ( b->uiOldFlags & BUTTON_ENABLED ) ) { //Something is different, set dirty! b->uiFlags |= BUTTON_DIRTY; } // If we ABSOLUTELY want to render every frame.... if ( b->uiFlags & BUTTON_SAVEBACKGROUND ) { b->uiFlags |= BUTTON_DIRTY; } // Set old flags b->uiOldFlags = b->uiFlags; if ( b->uiFlags & BUTTON_FORCE_UNDIRTY ) { b->uiFlags &= ~( BUTTON_DIRTY ); b->uiFlags &= ~( BUTTON_FORCE_UNDIRTY ); } // Check if we need to update! if ( b->uiFlags & BUTTON_DIRTY ) { // Turn off dirty flag b->uiFlags &= (~BUTTON_DIRTY); DrawButtonFromPtr(b); #ifdef JA2 InvalidateRegion(b->Area.RegionTopLeftX, b->Area.RegionTopLeftY, b->Area.RegionBottomRightX, b->Area.RegionBottomRightY); //#else // InvalidateRegion(b->Area.RegionTopLeftX, b->Area.RegionTopLeftY, b->Area.RegionBottomRightX, b->Area.RegionBottomRightY, FALSE); #endif } } } // check if we want to render 1 frame later? if( ( fPausedMarkButtonsDirtyFlag == TRUE ) && ( fDisableHelpTextRestoreFlag == FALSE ) ) { fPausedMarkButtonsDirtyFlag = FALSE; MarkButtonsDirty( ); } RestoreFontSettings(); } //***************************************************************************** // MarkAButtonDirty // void MarkAButtonDirty( INT32 iButtonNum ) { // surgical dirtying -> marks a user specified button dirty, without dirty the whole lot of them // If the button exists, and it's not owned by another object, draw it if( ButtonList[ iButtonNum ] ) { // Turn on dirty flag ButtonList[ iButtonNum ]->uiFlags |= BUTTON_DIRTY; } } //============================================================================= // MarkButtonsDirty // void MarkButtonsDirty( void ) { INT32 x; for(x=0;xuiFlags |= BUTTON_DIRTY; } } } void UnMarkButtonDirty( INT32 iButtonIndex ) { if ( ButtonList[ iButtonIndex ] ) { ButtonList[ iButtonIndex ]->uiFlags &= ~( BUTTON_DIRTY ); } } void UnmarkButtonsDirty( void ) { INT32 x; for(x=0;xuiFlags &= ~( BUTTON_DIRTY ); ButtonList[ iButtonIndex ]->uiFlags |= BUTTON_FORCE_UNDIRTY; } //============================================================================= // PauseMarkButtonsDirty // void PausedMarkButtonsDirty( void ) { // set flag for frame after the next rendering of buttons fPausedMarkButtonsDirtyFlag = TRUE; return; } //============================================================================= // DrawButton // // Draws a single button on the screen. // BOOLEAN DrawButton(INT32 iButtonID ) { // Fail if button handle out of range if( iButtonID < 0 || iButtonID > MAX_BUTTONS ) return FALSE; // Fail if button handle is invalid if( !ButtonList[ iButtonID ] ) return FALSE; if( ButtonList[ iButtonID ]->string ) SaveFontSettings(); // Draw this button if( ButtonList[ iButtonID ]->Area.uiFlags & MSYS_REGION_ENABLED ) { DrawButtonFromPtr( ButtonList[ iButtonID ] ); } if( ButtonList[ iButtonID ]->string ) RestoreFontSettings(); return TRUE; } //============================================================================= // DrawButtonFromPtr // // Given a pointer to a GUI_BUTTON structure, draws the button on the // screen. // void DrawButtonFromPtr(GUI_BUTTON *b) { Assert( b ); // Draw the appropriate button according to button type gbDisabledButtonStyle = DISABLED_STYLE_NONE; switch(b->uiFlags & BUTTON_TYPES) { case BUTTON_QUICK: DrawQuickButton(b); break; case BUTTON_GENERIC: DrawGenericButton(b); break; case BUTTON_HOT_SPOT: if(b->uiFlags & BUTTON_NO_TOGGLE) b->uiFlags &= (~BUTTON_CLICKED_ON); return; //hotspots don't have text, but if you want to, change this to a break! case BUTTON_CHECKBOX: DrawCheckBoxButton(b); break; } //If button has an icon, overlay it on current button. if( b->iIconID != -1 ) DrawIconOnButton( b ); //If button has text, draw it now if( b->string ) DrawTextOnButton( b ); //If the button is disabled, and a style has been calculated, then //draw the style last. switch( gbDisabledButtonStyle ) { case DISABLED_STYLE_HATCHED: DrawHatchOnButton( b ); break; case DISABLED_STYLE_SHADED: DrawShadeOnButton( b ); break; } if( b->bDefaultStatus ) { DrawDefaultOnButton( b ); } } //============================================================================= // DrawQuickButton // // Draws a QuickButton type button on the screen. // void DrawQuickButton(GUI_BUTTON *b) { INT32 UseImage; UseImage=0; // Is button Enabled, or diabled but no "Grayed" image associated with this QuickButton? if(b->uiFlags & BUTTON_ENABLED ) { // Is the button's state ON? if(b->uiFlags & BUTTON_CLICKED_ON) { // Is the mouse over this area, and we have a hilite image? if((b->Area.uiFlags & MSYS_MOUSE_IN_AREA) && gfRenderHilights && (ButtonPictures[b->ImageNum].OnHilite != -1)) UseImage = ButtonPictures[b->ImageNum].OnHilite; // Use On-Hilite image else if(ButtonPictures[b->ImageNum].OnNormal != -1) UseImage = ButtonPictures[b->ImageNum].OnNormal; // Use On-Normal image } else { // Is the mouse over the button, and do we have hilite image? if((b->Area.uiFlags & MSYS_MOUSE_IN_AREA) && gfRenderHilights && (ButtonPictures[b->ImageNum].OffHilite != -1)) UseImage = ButtonPictures[b->ImageNum].OffHilite; // Use Off-Hilite image else if(ButtonPictures[b->ImageNum].OffNormal != -1) UseImage = ButtonPictures[b->ImageNum].OffNormal; // Use Off-Normal image } } else if( ButtonPictures[b->ImageNum].Grayed != -1) { // Button is diabled so use the "Grayed-out" image UseImage = ButtonPictures[b->ImageNum].Grayed; } else { UseImage = ButtonPictures[b->ImageNum].OffNormal; switch( b->bDisabledStyle ) { case DISABLED_STYLE_DEFAULT: gbDisabledButtonStyle = b->string ? DISABLED_STYLE_SHADED : DISABLED_STYLE_HATCHED; break; case DISABLED_STYLE_HATCHED: case DISABLED_STYLE_SHADED: gbDisabledButtonStyle = b->bDisabledStyle; break; } } // Display the button image BltVideoObject(ButtonDestBuffer, ButtonPictures[b->ImageNum].vobj, (UINT16)UseImage, b->XLoc, b->YLoc, VO_BLT_SRCTRANSPARENCY, NULL); } void DrawHatchOnButton( GUI_BUTTON *b ) { UINT8 *pDestBuf; UINT32 uiDestPitchBYTES; SGPRect ClipRect; ClipRect.iLeft = b->Area.RegionTopLeftX; ClipRect.iRight = b->Area.RegionBottomRightX - 1; ClipRect.iTop = b->Area.RegionTopLeftY; ClipRect.iBottom = b->Area.RegionBottomRightY - 1; pDestBuf = LockVideoSurface( ButtonDestBuffer, &uiDestPitchBYTES ); Blt16BPPBufferHatchRect( (UINT16*)pDestBuf, uiDestPitchBYTES, &ClipRect ); UnLockVideoSurface( ButtonDestBuffer ); } void DrawShadeOnButton( GUI_BUTTON *b ) { UINT8 *pDestBuf; UINT32 uiDestPitchBYTES; SGPRect ClipRect; ClipRect.iLeft = b->Area.RegionTopLeftX; ClipRect.iRight = b->Area.RegionBottomRightX-1; ClipRect.iTop = b->Area.RegionTopLeftY; ClipRect.iBottom = b->Area.RegionBottomRightY-1; pDestBuf = LockVideoSurface( ButtonDestBuffer, &uiDestPitchBYTES ); Blt16BPPBufferShadowRect( (UINT16*)pDestBuf, uiDestPitchBYTES, &ClipRect ); UnLockVideoSurface( ButtonDestBuffer ); } void DrawDefaultOnButton( GUI_BUTTON *b ) { UINT8 *pDestBuf; UINT32 uiDestPitchBYTES; pDestBuf = LockVideoSurface( ButtonDestBuffer, &uiDestPitchBYTES ); SetClippingRegionAndImageWidth( uiDestPitchBYTES, 0, 0, 640, 480 ); if( b->bDefaultStatus == DEFAULT_STATUS_DARKBORDER || b->bDefaultStatus == DEFAULT_STATUS_WINDOWS95 ) { //left (one thick) LineDraw( TRUE, b->Area.RegionTopLeftX-1, b->Area.RegionTopLeftY-1, b->Area.RegionTopLeftX-1, b->Area.RegionBottomRightY+1, 0, pDestBuf ); //top (one thick) LineDraw( TRUE, b->Area.RegionTopLeftX-1, b->Area.RegionTopLeftY-1, b->Area.RegionBottomRightX+1, b->Area.RegionTopLeftY-1, 0, pDestBuf ); //right (two thick) LineDraw( TRUE, b->Area.RegionBottomRightX, b->Area.RegionTopLeftY-1, b->Area.RegionBottomRightX, b->Area.RegionBottomRightY+1, 0, pDestBuf ); LineDraw( TRUE, b->Area.RegionBottomRightX+1, b->Area.RegionTopLeftY-1, b->Area.RegionBottomRightX+1, b->Area.RegionBottomRightY+1, 0, pDestBuf ); //bottom (two thick) LineDraw( TRUE, b->Area.RegionTopLeftX-1, b->Area.RegionBottomRightY, b->Area.RegionBottomRightX+1, b->Area.RegionBottomRightY, 0, pDestBuf ); LineDraw( TRUE, b->Area.RegionTopLeftX-1, b->Area.RegionBottomRightY+1, b->Area.RegionBottomRightX+1, b->Area.RegionBottomRightY+1, 0, pDestBuf ); #ifdef JA2 InvalidateRegion( b->Area.RegionTopLeftX-1, b->Area.RegionTopLeftY-1, b->Area.RegionBottomRightX+1, b->Area.RegionBottomRightY+1 ); #endif } if( b->bDefaultStatus == DEFAULT_STATUS_DOTTEDINTERIOR || b->bDefaultStatus == DEFAULT_STATUS_WINDOWS95 ) { //Draw an internal dotted rectangle. } UnLockVideoSurface( ButtonDestBuffer ); } void DrawCheckBoxButtonOn( INT32 iButtonID ) { GUI_BUTTON *b; BOOLEAN fLeftButtonState = gfLeftButtonState; Assert( iButtonID >= 0 ); Assert( iButtonID < MAX_BUTTONS); b = ButtonList[ iButtonID ]; Assert( b ); gfLeftButtonState = TRUE; b->Area.uiFlags |= MSYS_MOUSE_IN_AREA; DrawButton( iButtonID ); gfLeftButtonState = fLeftButtonState; } void DrawCheckBoxButtonOff( INT32 iButtonID ) { GUI_BUTTON *b; BOOLEAN fLeftButtonState = gfLeftButtonState; Assert( iButtonID >= 0 ); Assert( iButtonID < MAX_BUTTONS); b = ButtonList[ iButtonID ]; Assert( b ); gfLeftButtonState = FALSE; b->Area.uiFlags |= MSYS_MOUSE_IN_AREA; DrawButton( iButtonID ); gfLeftButtonState = fLeftButtonState; } void DrawCheckBoxButton( GUI_BUTTON *b ) { INT32 UseImage; UseImage=0; // Is button Enabled, or diabled but no "Grayed" image associated with this QuickButton? if( b->uiFlags & BUTTON_ENABLED ) { // Is the button's state ON? if(b->uiFlags & BUTTON_CLICKED_ON) { // Is the mouse over this area, and we have a hilite image? if( b->Area.uiFlags & MSYS_MOUSE_IN_AREA && gfRenderHilights && gfLeftButtonState && ButtonPictures[b->ImageNum].OnHilite != -1 ) UseImage = ButtonPictures[b->ImageNum].OnHilite; // Use On-Hilite image else if(ButtonPictures[b->ImageNum].OnNormal != -1) UseImage = ButtonPictures[b->ImageNum].OnNormal; // Use On-Normal image } else { // Is the mouse over the button, and do we have hilite image? if( b->Area.uiFlags & MSYS_MOUSE_IN_AREA && gfRenderHilights && gfLeftButtonState && ButtonPictures[b->ImageNum].OffHilite != -1 ) UseImage = ButtonPictures[b->ImageNum].OffHilite; // Use Off-Hilite image else if(ButtonPictures[b->ImageNum].OffNormal != -1) UseImage = ButtonPictures[b->ImageNum].OffNormal; // Use Off-Normal image } } else if( ButtonPictures[b->ImageNum].Grayed != -1 ) { // Button is disabled so use the "Grayed-out" image UseImage = ButtonPictures[b->ImageNum].Grayed; } else //use the disabled style { if( b->uiFlags & BUTTON_CLICKED_ON ) UseImage = ButtonPictures[b->ImageNum].OnHilite; else UseImage = ButtonPictures[b->ImageNum].OffHilite; switch( b->bDisabledStyle ) { case DISABLED_STYLE_DEFAULT: gbDisabledButtonStyle = DISABLED_STYLE_HATCHED; break; case DISABLED_STYLE_HATCHED: case DISABLED_STYLE_SHADED: gbDisabledButtonStyle = b->bDisabledStyle; break; } } // Display the button image BltVideoObject(ButtonDestBuffer, ButtonPictures[b->ImageNum].vobj, (UINT16)UseImage, b->XLoc, b->YLoc, VO_BLT_SRCTRANSPARENCY, NULL); } void DrawIconOnButton(GUI_BUTTON *b) { INT32 xp,yp,width,height,IconX,IconY; INT32 IconW,IconH; SGPRect NewClip,OldClip; ETRLEObject *pTrav; HVOBJECT hvObject; // If there's an actual icon on this button, try to show it. if(b->iIconID >= 0) { // Get width and height of button area width = b->Area.RegionBottomRightX - b->Area.RegionTopLeftX; height = b->Area.RegionBottomRightY - b->Area.RegionTopLeftY; // Compute viewable area (inside borders) NewClip.iLeft = b->XLoc + 3; NewClip.iRight = b->XLoc + width - 3; NewClip.iTop = b->YLoc + 2; NewClip.iBottom = b->YLoc + height - 2; // Get Icon's blit start coordinates IconX = NewClip.iLeft; IconY = NewClip.iTop; // Get current clip area GetClippingRect(&OldClip); // Clip button's viewable area coords to screen if(NewClip.iLeft < OldClip.iLeft) NewClip.iLeft = OldClip.iLeft; // Is button right off the right side of the screen? if(NewClip.iLeft > OldClip.iRight) return; if(NewClip.iRight > OldClip.iRight) NewClip.iRight = OldClip.iRight; // Is button completely off the left side of the screen? if(NewClip.iRight < OldClip.iLeft) return; if(NewClip.iTop < OldClip.iTop) NewClip.iTop = OldClip.iTop; // Are we right off the bottom of the screen? if(NewClip.iTop > OldClip.iBottom) return; if(NewClip.iBottom > OldClip.iBottom) NewClip.iBottom = OldClip.iBottom; // Are we off the top? if(NewClip.iBottom < OldClip.iTop) return; // Did we clip the viewable area out of existance? if((NewClip.iRight <= NewClip.iLeft) || (NewClip.iBottom <= NewClip.iTop)) return; // Get the width and height of the icon itself if( b->uiFlags & BUTTON_GENERIC ) pTrav = &(GenericButtonIcons[b->iIconID]->pETRLEObject[b->usIconIndex]); else { GetVideoObject( &hvObject, b->iIconID ); pTrav = &(hvObject->pETRLEObject[b->usIconIndex] ); } IconH = (UINT32)(pTrav->usHeight+pTrav->sOffsetY); IconW = (UINT32)(pTrav->usWidth+pTrav->sOffsetX); // Compute coordinates for centering the icon on the button or // use the offset system. if( b->bIconXOffset == -1 ) xp = (((width-6)-IconW) / 2) + IconX; else xp = b->Area.RegionTopLeftX + b->bIconXOffset; if( b->bIconYOffset == -1 ) yp = (((height-4)-IconH) / 2) + IconY; else yp = b->Area.RegionTopLeftY + b->bIconYOffset; // Was the button clicked on? if so, move the image slightly for the illusion // that the image moved into the screen. if( b->uiFlags & BUTTON_CLICKED_ON && b->fShiftImage ) { xp++; yp++; } // Set the clipping rectangle to the viewable area of the button SetClippingRect(&NewClip); // Blit the icon if( b->uiFlags & BUTTON_GENERIC ) BltVideoObject( ButtonDestBuffer,GenericButtonIcons[b->iIconID], b->usIconIndex, (INT16)xp, (INT16)yp, VO_BLT_SRCTRANSPARENCY, NULL); else BltVideoObject( ButtonDestBuffer, hvObject, b->usIconIndex, (INT16)xp, (INT16)yp, VO_BLT_SRCTRANSPARENCY, NULL ); // Restore previous clip region SetClippingRect(&OldClip); } } //If a button has text attached to it, then it'll draw it last. void DrawTextOnButton(GUI_BUTTON *b) { INT32 xp,yp,width,height,TextX,TextY; SGPRect NewClip,OldClip; INT16 sForeColor; // If this button actually has a string to print if( b->string ) { // Get the width and height of this button width = b->Area.RegionBottomRightX - b->Area.RegionTopLeftX; height = b->Area.RegionBottomRightY - b->Area.RegionTopLeftY; // Compute the viewable area on this button NewClip.iLeft = b->XLoc + 3; NewClip.iRight = b->XLoc + width - 3; NewClip.iTop = b->YLoc + 2; NewClip.iBottom = b->YLoc + height - 2; // Get the starting coordinates to print TextX = NewClip.iLeft; TextY = NewClip.iTop; // Get the current clipping area GetClippingRect(&OldClip); // Clip the button's viewable area to the screen if(NewClip.iLeft < OldClip.iLeft) NewClip.iLeft = OldClip.iLeft; // Are we off hte right side? if(NewClip.iLeft > OldClip.iRight) return; if(NewClip.iRight > OldClip.iRight) NewClip.iRight = OldClip.iRight; // Are we off the left side? if(NewClip.iRight < OldClip.iLeft) return; if(NewClip.iTop < OldClip.iTop) NewClip.iTop = OldClip.iTop; // Are we off the bottom of the screen? if(NewClip.iTop > OldClip.iBottom) return; if(NewClip.iBottom > OldClip.iBottom) NewClip.iBottom = OldClip.iBottom; // Are we off the top? if(NewClip.iBottom < OldClip.iTop) return; // Did we clip the viewable area out of existance? if((NewClip.iRight <= NewClip.iLeft) || (NewClip.iBottom <= NewClip.iTop)) return; // Set the font printing settings to the buttons viewable area SetFontDestBuffer(ButtonDestBuffer, NewClip.iLeft, NewClip.iTop, NewClip.iRight, NewClip.iBottom, FALSE); // Compute the coordinates to center the text if( b->bTextYOffset == -1 ) yp = (((height) - GetFontHeight(b->usFont)) / 2) + TextY - 1; else yp = b->Area.RegionTopLeftY + b->bTextYOffset; if( b->bTextXOffset == -1 ) { switch( b->bJustification ) { case BUTTON_TEXT_LEFT: xp = TextX + 3; break; case BUTTON_TEXT_RIGHT: xp = NewClip.iRight - StringPixLength(b->string, b->usFont) - 3; break; case BUTTON_TEXT_CENTER: default: xp = (((width-6)-StringPixLength(b->string,b->usFont)) / 2) + TextX; break; } } else xp = b->Area.RegionTopLeftX + b->bTextXOffset; // Set the printing font to the button text font SetFont(b->usFont); // print the text SetFontBackground( FONT_MCOLOR_BLACK ); SetFontForeground( (UINT8)b->sForeColor ); sForeColor = b->sForeColor; if( b->sShadowColor != -1 ) SetFontShadow( (UINT8)b->sShadowColor ); //Override the colors if necessary. if( b->uiFlags & BUTTON_ENABLED && b->Area.uiFlags & MSYS_MOUSE_IN_AREA && b->sForeColorHilited != -1 ) { SetFontForeground( (UINT8)b->sForeColorHilited ); sForeColor = b->sForeColorHilited; } else if( b->uiFlags & BUTTON_CLICKED_ON && b->sForeColorDown != -1 ) { SetFontForeground( (UINT8)b->sForeColorDown ); sForeColor = b->sForeColorDown; } if( b->uiFlags & BUTTON_ENABLED && b->Area.uiFlags & MSYS_MOUSE_IN_AREA && b->sShadowColorHilited != -1 ) { SetFontShadow( (UINT8)b->sShadowColorHilited ); } else if( b->uiFlags & BUTTON_CLICKED_ON && b->sShadowColorDown != -1 ) { SetFontShadow( (UINT8)b->sShadowColorDown ); } if( b->uiFlags & BUTTON_CLICKED_ON && b->fShiftText ) { // Was the button clicked on? if so, move the text slightly for the illusion // that the text moved into the screen. xp++; yp++; } #ifdef JA2 if( b->sWrappedWidth != -1 ) { UINT8 bJustified=0; switch( b->bJustification ) { case BUTTON_TEXT_LEFT: bJustified = LEFT_JUSTIFIED; break; case BUTTON_TEXT_RIGHT: bJustified = RIGHT_JUSTIFIED; break; case BUTTON_TEXT_CENTER: bJustified = CENTER_JUSTIFIED; break; default: Assert( 0 ); break; } if( b->bTextXOffset == -1 ) { //Kris: //There needs to be recalculation of the start positions based on the //justification and the width specified wrapped width. I was drawing a //double lined word on the right side of the button to find it drawing way //over to the left. I've added the necessary code for the right and center //justification. yp = b->Area.RegionTopLeftY + 2; switch( b->bJustification ) { case BUTTON_TEXT_RIGHT: xp = b->Area.RegionBottomRightX - 3 - b->sWrappedWidth; if( b->fShiftText && b->uiFlags & BUTTON_CLICKED_ON ) xp++, yp++; break; case BUTTON_TEXT_CENTER: xp = b->Area.RegionTopLeftX + 3 + b->sWrappedWidth/2; if( b->fShiftText && b->uiFlags & BUTTON_CLICKED_ON ) xp++, yp++; break; } } yp+= b->bTextYSubOffSet; xp+= b->bTextXSubOffSet; DisplayWrappedString((UINT16)xp, (UINT16)yp, b->sWrappedWidth, 1, b->usFont, (UINT8)sForeColor, b->string, FONT_MCOLOR_BLACK, FALSE, bJustified); } else { yp+= b->bTextYSubOffSet; xp+= b->bTextXSubOffSet; mprintf(xp, yp, b->string); } #else if(b->fMultiColor) gprintf(xp, yp, b->string); else mprintf(xp, yp, b->string); #endif // Restore the old text printing settings } } //============================================================================= // DrawGenericButton // // This function is called by the DrawIconicButton and DrawTextButton // routines to draw the borders and background of the buttons. // void DrawGenericButton(GUI_BUTTON *b) { INT32 NumChunksWide,NumChunksHigh,cx,cy,width,height,hremain,wremain; INT32 q,ImgNum,ox,oy; INT32 iBorderHeight, iBorderWidth; HVOBJECT BPic; UINT32 uiDestPitchBYTES; UINT8 *pDestBuf; SGPRect ClipRect; ETRLEObject *pTrav; // Select the graphics to use depending on the current state of the button if( b->uiFlags & BUTTON_ENABLED ) { if ( !(b->uiFlags & BUTTON_ENABLED) && (GenericButtonGrayed[b->ImageNum]==NULL) ) BPic = GenericButtonOffNormal[b->ImageNum]; else if(b->uiFlags & BUTTON_CLICKED_ON) { if((b->Area.uiFlags & MSYS_MOUSE_IN_AREA) && (GenericButtonOnHilite[b->ImageNum]!=NULL) && gfRenderHilights ) BPic = GenericButtonOnHilite[b->ImageNum]; else BPic = GenericButtonOnNormal[b->ImageNum]; } else { if((b->Area.uiFlags & MSYS_MOUSE_IN_AREA) && (GenericButtonOffHilite[b->ImageNum]!=NULL) && gfRenderHilights ) BPic = GenericButtonOffHilite[b->ImageNum]; else BPic = GenericButtonOffNormal[b->ImageNum]; } } else if( GenericButtonGrayed[ b->ImageNum ] ) BPic = GenericButtonGrayed[b->ImageNum]; else { BPic = GenericButtonOffNormal[ b->ImageNum ]; switch( b->bDisabledStyle ) { case DISABLED_STYLE_DEFAULT: gbDisabledButtonStyle = b->string ? DISABLED_STYLE_SHADED : DISABLED_STYLE_HATCHED; break; case DISABLED_STYLE_HATCHED: case DISABLED_STYLE_SHADED: gbDisabledButtonStyle = b->bDisabledStyle; break; } } iBorderWidth=3; iBorderHeight=2; pTrav=NULL; // DB - Added this to support more flexible sizing of border images // The 3x2 size was a bit limiting. JA2 should default to the original // size, unchanged #ifndef JA2 pTrav = &(BPic->pETRLEObject[0] ); iBorderHeight = (INT32)pTrav->usHeight; iBorderWidth = (INT32)pTrav->usWidth; #endif // Compute the number of button "chunks" needed to be blitted width = b->Area.RegionBottomRightX - b->Area.RegionTopLeftX; height = b->Area.RegionBottomRightY - b->Area.RegionTopLeftY; NumChunksWide = width / iBorderWidth; NumChunksHigh = height / iBorderHeight; hremain = height % iBorderHeight; wremain = width % iBorderWidth; cx = (b->XLoc + ((NumChunksWide-1)*iBorderWidth) + wremain); cy = (b->YLoc + ((NumChunksHigh-1)*iBorderHeight) + hremain); // Fill the button's area with the button's background color ColorFillVideoSurfaceArea(ButtonDestBuffer,b->Area.RegionTopLeftX, b->Area.RegionTopLeftY, b->Area.RegionBottomRightX, b->Area.RegionBottomRightY, GenericButtonFillColors[b->ImageNum]); // If there is a background image, fill the button's area with it if(GenericButtonBackground[b->ImageNum]!=NULL) { ox=oy=0; // if the button was clicked on, adjust the background image so that we get // the illusion that it is sunk into the screen. if(b->uiFlags & BUTTON_CLICKED_ON) ox=oy=1; // Fill the area with the image, tilling it if need be. ImageFillVideoSurfaceArea(ButtonDestBuffer,b->Area.RegionTopLeftX+ox, b->Area.RegionTopLeftY+oy, b->Area.RegionBottomRightX, b->Area.RegionBottomRightY, GenericButtonBackground[b->ImageNum], GenericButtonBackgroundIndex[b->ImageNum], GenericButtonOffsetX[b->ImageNum], GenericButtonOffsetY[b->ImageNum]); } // Lock the dest buffer pDestBuf = LockVideoSurface( ButtonDestBuffer, &uiDestPitchBYTES ); GetClippingRect(&ClipRect); // Draw the button's borders and corners (horizontally) for(q=0;qXLoc + (q*iBorderWidth)), (INT32)b->YLoc, (UINT16)ImgNum, &ClipRect ); } else if(gbPixelDepth==8) { Blt8BPPDataTo8BPPBufferTransparentClip( (UINT16*)pDestBuf, uiDestPitchBYTES, BPic, (INT32)(b->XLoc + (q*iBorderWidth)), (INT32)b->YLoc, (UINT16)ImgNum, &ClipRect ); } if(q==0) ImgNum=5; else ImgNum=6; if(gbPixelDepth==16) { Blt8BPPDataTo16BPPBufferTransparentClip( (UINT16*)pDestBuf, uiDestPitchBYTES, BPic, (INT32)(b->XLoc + (q*iBorderWidth)), cy, (UINT16)ImgNum, &ClipRect ); } else if(gbPixelDepth==8) { Blt8BPPDataTo8BPPBufferTransparentClip((UINT16*)pDestBuf, uiDestPitchBYTES, BPic, (INT32)(b->XLoc + (q*iBorderWidth)), cy, (UINT16)ImgNum, &ClipRect ); } } // Blit the right side corners if(gbPixelDepth==16) { Blt8BPPDataTo16BPPBufferTransparentClip( (UINT16*)pDestBuf, uiDestPitchBYTES, BPic, cx, (INT32)b->YLoc, 2, &ClipRect ); } else if(gbPixelDepth==8) { Blt8BPPDataTo8BPPBufferTransparentClip( (UINT16*)pDestBuf, uiDestPitchBYTES, BPic, cx, (INT32)b->YLoc, 2, &ClipRect ); } if(gbPixelDepth==16) { Blt8BPPDataTo16BPPBufferTransparentClip( (UINT16*)pDestBuf, uiDestPitchBYTES, BPic, cx, cy, 7, &ClipRect ); } else if(gbPixelDepth==8) { Blt8BPPDataTo8BPPBufferTransparentClip( (UINT16*)pDestBuf, uiDestPitchBYTES, BPic, cx, cy, 7, &ClipRect ); } // Draw the vertical members of the button's borders NumChunksHigh--; if(hremain!=0) { q=NumChunksHigh; if(gbPixelDepth==16) { Blt8BPPDataTo16BPPBufferTransparentClip( (UINT16*)pDestBuf, uiDestPitchBYTES, BPic, (INT32)b->XLoc, (INT32)(b->YLoc + (q*iBorderHeight) - (iBorderHeight-hremain)), 3, &ClipRect ); } else if(gbPixelDepth==8) { Blt8BPPDataTo8BPPBufferTransparentClip( (UINT16*)pDestBuf, uiDestPitchBYTES, BPic, (INT32)b->XLoc, (INT32)(b->YLoc + (q*iBorderHeight) - (iBorderHeight-hremain)), 3, &ClipRect ); } if(gbPixelDepth==16) { Blt8BPPDataTo16BPPBufferTransparentClip( (UINT16*)pDestBuf, uiDestPitchBYTES, BPic, cx, (INT32)(b->YLoc + (q*iBorderHeight) - (iBorderHeight-hremain)), 4, &ClipRect ); } else if(gbPixelDepth==8) { Blt8BPPDataTo8BPPBufferTransparentClip((UINT16*)pDestBuf, uiDestPitchBYTES, BPic, cx, (INT32)(b->YLoc + (q*iBorderHeight) - (iBorderHeight-hremain)), 4, &ClipRect ); } } for(q=1;qXLoc, (INT32)(b->YLoc + (q*iBorderHeight)), 3, &ClipRect ); } else if(gbPixelDepth==8) { Blt8BPPDataTo8BPPBufferTransparentClip((UINT16*)pDestBuf, uiDestPitchBYTES, BPic, (INT32)b->XLoc, (INT32)(b->YLoc + (q*iBorderHeight)), 3, &ClipRect ); } if(gbPixelDepth==16) { Blt8BPPDataTo16BPPBufferTransparentClip( (UINT16*)pDestBuf, uiDestPitchBYTES, BPic, cx, (INT32)(b->YLoc + (q*iBorderHeight)), 4, &ClipRect ); } else if(gbPixelDepth==8) { Blt8BPPDataTo8BPPBufferTransparentClip((UINT16*)pDestBuf, uiDestPitchBYTES, BPic, cx, (INT32)(b->YLoc + (q*iBorderHeight)), 4, &ClipRect ); } } // Unlock buffer UnLockVideoSurface( ButtonDestBuffer ); } //======================================================================================================= // Dialog box code // //======================================================================================================= //======================================================================================================= // // Very preliminary stuff follows // //======================================================================================================= //======================================================================================================= typedef struct _CreateDlgInfo { INT32 iFlags; // Holds the creation flags INT32 iPosX; // Screen position of dialog box INT32 iPosY; INT32 iWidth; // Dimensions of dialog box (if needed) INT32 iHeight; INT32 iAreaWidth; // Dimensions of area the dialog box will be INT32 iAreaHeight; // placed (for auto-sizing and auto-placement INT32 iAreaOffsetX; // only) INT32 iAreaOffsetY; UINT16 *zDlgText; // Text to be displayed (if any) INT32 iTextFont; // Font to be used for text (if any) UINT16 usTextCols; // Font colors (for mono fonts only) INT32 iTextAreaX; // Area in dialog box where text is to be INT32 iTextAreaY; // put (for non-auto placed text) INT32 iTextAreaWidth; INT32 iTextAreaHeight; HVOBJECT hBackImg; // Background pic for dialog box (if any) INT32 iBackImgIndex; // Sub-image index to use for image INT32 iBackOffsetX; // Offset on dialog box where to put image INT32 iBackOffsetY; HVOBJECT hIconImg; // Icon image pic and index. INT32 iIconImgIndex; INT32 iIconPosX; INT32 iIconPosY; INT32 iBtnTypes; INT32 iOkPosX; // Ok button info INT32 iOkPosY; INT32 iOkWidth; INT32 iOkHeight; INT32 iOkImg; INT32 iCnclPosX; // Cancel button info INT32 iCnclPosY; INT32 iCnclWidth; INT32 iCnclHeight; INT32 iCnclImg; } CreateDlgInfo; #define DLG_RESTRICT_MOUSE 1 #define DLG_OK_BUTTON 2 #define DLG_CANCEL_BUTTON 4 #define DLG_AUTOSIZE 8 #define DLG_RECREATE 16 #define DLG_AUTOPOSITION 32 #define DLG_TEXT_IN_AREA 64 #define DLG_USE_BKGRND_IMAGE 128 #define DLG_USE_BORDERS 256 #define DLG_USE_BTN_HOTSPOTS 512 #define DLG_USE_MONO_FONTS 1024 #define DLG_IS_ACTIVE 2048 #define DLG_MANUAL_RENDER 0 #define DLG_START_RENDER 1 #define DLG_STOP_RENDER 2 #define DLG_AUTO_RENDER 3 #define DLG_GET_STATUS 0 #define DLG_WAIT_FOR_RESPONSE 1 #define DLG_STATUS_NONE 0 #define DLG_STATUS_OK 1 #define DLG_STATUS_CANCEL 2 #define DLG_STATUS_PENDING 3 #define DLG_CLEARALL 0 #define DLG_POSITION 1 #define DLG_AREA 2 #define DLG_TEXT 3 #define DLG_TEXTAREA 4 #define DLG_BACKPIC 5 #define DLG_ICON 6 #define DLG_OKBUTTON 7 #define DLG_CANCELBUTTON 8 #define DLG_OPTIONS 9 #define DLG_SIZE 10 BOOLEAN SetDialogAttributes( CreateDlgInfo *pDlgInfo, INT32 iAttrib, ... ) { va_list arg; INT32 iFont,iFontOptions; UINT16 *zString; INT32 iX,iY,iW,iH; INT32 iIndex; HVOBJECT hVObj; INT32 iButnImg; INT32 iFlags; UINT8 ubFGrnd,ubBGrnd; // Set up for var args va_start( arg, iAttrib ); // Init variable argument list switch (iAttrib) { case DLG_CLEARALL: // Check to make sure it's not enabled, if so either abort or trash it! memset( pDlgInfo, 0, sizeof(CreateDlgInfo) ); break; case DLG_POSITION: // Screen X/Y position of dialog box iFlags = va_arg( arg, INT32 ); iX = va_arg( arg, INT32 ); iY = va_arg( arg, INT32 ); iW = va_arg( arg, INT32 ); iH = va_arg( arg, INT32 ); break; case DLG_SIZE: // Width and height of doalog box iFlags = va_arg( arg, INT32 ); iW = va_arg( arg, INT32 ); iH = va_arg( arg, INT32 ); break; case DLG_AREA: // Area where dialog box should be placed in (if not screen) iFlags = va_arg( arg, INT32 ); iX = va_arg( arg, INT32 ); iY = va_arg( arg, INT32 ); iW = va_arg( arg, INT32 ); iH = va_arg( arg, INT32 ); break; case DLG_TEXT: // Set text and font for this dialog box iFontOptions = va_arg( arg, INT32 ); iFont = va_arg( arg, INT32 ); pDlgInfo->iTextFont = iFont; zString = (UINT16 *)va_arg( arg, UINT32 ); if ( pDlgInfo->zDlgText != NULL ) MemFree( pDlgInfo->zDlgText ); if ( zString == NULL ) pDlgInfo->zDlgText = NULL; else { pDlgInfo->zDlgText = NULL; // Temp } if ( iFontOptions & DLG_USE_MONO_FONTS ) { ubFGrnd = va_arg( arg, UINT8 ); ubBGrnd = va_arg( arg, UINT8 ); pDlgInfo->usTextCols = ((((UINT16)ubBGrnd)<<8) | (UINT16)ubFGrnd); } break; case DLG_TEXTAREA: // Area on dialog box where the text should go! iFlags = va_arg( arg, INT32 ); iX = va_arg( arg, INT32 ); iY = va_arg( arg, INT32 ); iW = va_arg( arg, INT32 ); iH = va_arg( arg, INT32 ); break; case DLG_BACKPIC: iFlags = va_arg( arg, INT32 ); hVObj = (HVOBJECT)va_arg( arg, UINT32 ); iIndex = va_arg( arg, INT32 ); iX = va_arg( arg, INT32 ); iY = va_arg( arg, INT32 ); break; case DLG_ICON: // Icon iFlags = va_arg( arg, INT32 ); hVObj = (HVOBJECT)va_arg( arg, UINT32 ); iIndex = va_arg( arg, INT32 ); iX = va_arg( arg, INT32 ); iY = va_arg( arg, INT32 ); break; case DLG_OKBUTTON: // Ok button options iFlags = va_arg( arg, INT32 ); iX = va_arg( arg, INT32 ); iY = va_arg( arg, INT32 ); iW = va_arg( arg, INT32 ); iH = va_arg( arg, INT32 ); iButnImg = va_arg( arg, INT32 ); break; case DLG_CANCELBUTTON: // Cancel button options iFlags = va_arg( arg, INT32 ); iX = va_arg( arg, INT32 ); iY = va_arg( arg, INT32 ); iW = va_arg( arg, INT32 ); iH = va_arg( arg, INT32 ); iButnImg = va_arg( arg, INT32 ); break; case DLG_OPTIONS: iFlags = va_arg( arg, INT32 ); break; } va_end( arg ); // Must have this for proper exit return(TRUE); } INT32 CreateDialogBox( CreateDlgInfo *pDlgInfo ) { return (-1); } void RemoveDialogBox( void ) { } void DrawDialogBox( INT32 iDlgBox ) { } //------------------------------------------------------------------------------------------------------ INT32 CreateCheckBoxButton( INT16 x, INT16 y, UINT8 *filename, INT16 Priority, GUI_CALLBACK ClickCallback ) { GUI_BUTTON *b; INT32 ButPic, iButtonID; Assert( filename != NULL ); Assert( strlen(filename) ); if( ( ButPic = LoadButtonImage(filename,-1,0,1,2,3) ) == -1 ) { DbgMessage(TOPIC_BUTTON_HANDLER,DBG_LEVEL_0,"CreateCheckBoxButton: Can't load button image"); return( -1 ); } iButtonID = (INT16)QuickCreateButton( (UINT32)ButPic, x, y, BUTTON_CHECKBOX, Priority, MSYS_NO_CALLBACK, ClickCallback ); if( iButtonID == - 1 ) { DbgMessage(TOPIC_BUTTON_HANDLER,DBG_LEVEL_0,"CreateCheckBoxButton: Can't create button"); return( -1 ); } //change the flags so that it isn't a quick button anymore b = ButtonList[ iButtonID ]; b->uiFlags &= ( ~BUTTON_QUICK ); b->uiFlags |= ( BUTTON_CHECKBOX | BUTTON_SELFDELETE_IMAGE); return( iButtonID ); } // Added Oct17, 97 Carter - kind of mindless, but might as well have it void MSYS_SetBtnUserData(INT32 iButtonNum,INT32 index,INT32 userdata) { GUI_BUTTON *b; b=ButtonList[iButtonNum]; if(index < 0 || index > 3) return; b->UserData[index]=userdata; } INT32 MSYS_GetBtnUserData(GUI_BUTTON *b,INT32 index) { if(index < 0 || index > 3) return(0); return(b->UserData[index]); } //Generic Button Movement Callback to reset the mouse button if the mouse is no longer //in the button region. void BtnGenericMouseMoveButtonCallback(GUI_BUTTON *btn,INT32 reason) { //If the button isn't the anchored button, then we don't want to modify the button state. if( btn != gpAnchoredButton ) return; if( reason & MSYS_CALLBACK_REASON_LOST_MOUSE ) { if( !gfAnchoredState ) { btn->uiFlags &= (~BUTTON_CLICKED_ON ); if( btn->ubSoundSchemeID ) { PlayButtonSound( btn->IDNum, BUTTON_SOUND_CLICKED_OFF ); } } #ifdef JA2 InvalidateRegion(btn->Area.RegionTopLeftX, btn->Area.RegionTopLeftY, btn->Area.RegionBottomRightX, btn->Area.RegionBottomRightY); #endif } else if( reason & MSYS_CALLBACK_REASON_GAIN_MOUSE ) { btn->uiFlags |= BUTTON_CLICKED_ON ; if( btn->ubSoundSchemeID ) { PlayButtonSound( btn->IDNum, BUTTON_SOUND_CLICKED_ON ); } #ifdef JA2 InvalidateRegion(btn->Area.RegionTopLeftX, btn->Area.RegionTopLeftY, btn->Area.RegionBottomRightX, btn->Area.RegionBottomRightY); #endif } } void ReleaseAnchorMode() { if( !gpAnchoredButton ) return; if( gusMouseXPos < gpAnchoredButton->Area.RegionTopLeftX || gusMouseXPos > gpAnchoredButton->Area.RegionBottomRightX || gusMouseYPos < gpAnchoredButton->Area.RegionTopLeftY || gusMouseYPos > gpAnchoredButton->Area.RegionBottomRightY ) { //released outside button area, so restore previous button state. if( gfAnchoredState ) gpAnchoredButton->uiFlags |= BUTTON_CLICKED_ON; else gpAnchoredButton->uiFlags &= ( ~BUTTON_CLICKED_ON ); #ifdef JA2 InvalidateRegion(gpAnchoredButton->Area.RegionTopLeftX, gpAnchoredButton->Area.RegionTopLeftY, gpAnchoredButton->Area.RegionBottomRightX, gpAnchoredButton->Area.RegionBottomRightY); #endif } gpPrevAnchoredButton = gpAnchoredButton; gpAnchoredButton = NULL; } #ifdef _JA2_RENDER_DIRTY // Used to setup a dirtysaved region for buttons BOOLEAN SetButtonSavedRect( INT32 iButton ) { GUI_BUTTON *b; INT32 xloc,yloc,w,h; Assert( iButton >=0 ); Assert( iButton XLoc; yloc=b->YLoc; w = ( b->Area.RegionBottomRightX - b->Area.RegionTopLeftX ); h = ( b->Area.RegionBottomRightY - b->Area.RegionTopLeftY ); if ( !(b->uiFlags & BUTTON_SAVEBACKGROUND ) ) { b->uiFlags |= BUTTON_SAVEBACKGROUND; b->BackRect = RegisterBackgroundRect(BGND_FLAG_PERMANENT | BGND_FLAG_SAVERECT, NULL, (INT16)xloc, (INT16)yloc, (INT16)(xloc+w), (INT16)(yloc+h) ); } return( TRUE ); } void FreeButtonSavedRect( INT32 iButton ) { GUI_BUTTON *b; Assert( iButton >=0 ); Assert( iButton uiFlags & BUTTON_SAVEBACKGROUND ) ) { b->uiFlags &= (~BUTTON_SAVEBACKGROUND); FreeBackgroundRectPending(b->BackRect); } } #endif //Kris: //Yet new logical additions to the winbart library. void HideButton( INT32 iButtonNum ) { GUI_BUTTON *b; Assert( iButtonNum >= 0 ); Assert( iButtonNum < MAX_BUTTONS); b = ButtonList[ iButtonNum ]; Assert( b ); b->Area.uiFlags &= (~MSYS_REGION_ENABLED); b->uiFlags |= BUTTON_DIRTY; #ifdef JA2 InvalidateRegion(b->Area.RegionTopLeftX, b->Area.RegionTopLeftY, b->Area.RegionBottomRightX, b->Area.RegionBottomRightY); #endif } void ShowButton( INT32 iButtonNum ) { GUI_BUTTON *b; Assert( iButtonNum >= 0 ); Assert( iButtonNum < MAX_BUTTONS); b = ButtonList[ iButtonNum ]; Assert( b ); b->Area.uiFlags |= MSYS_REGION_ENABLED; b->uiFlags |= BUTTON_DIRTY; #ifdef JA2 InvalidateRegion(b->Area.RegionTopLeftX, b->Area.RegionTopLeftY, b->Area.RegionBottomRightX, b->Area.RegionBottomRightY); #endif } void DisableButtonHelpTextRestore( void ) { fDisableHelpTextRestoreFlag = TRUE; } void EnableButtonHelpTextRestore( void ) { fDisableHelpTextRestoreFlag = TRUE; } void GiveButtonDefaultStatus( INT32 iButtonID, INT32 iDefaultStatus ) { GUI_BUTTON *b; Assert( iButtonID >= 0 ); Assert( iButtonID < MAX_BUTTONS); b = ButtonList[ iButtonID ]; //If new default status added, then this assert may need to be adjusted. AssertMsg( iDefaultStatus >= DEFAULT_STATUS_NONE && iDefaultStatus <= DEFAULT_STATUS_WINDOWS95, String( "Illegal button default status of %d", iDefaultStatus ) ); Assert( b ); if( b->bDefaultStatus != (INT8)iDefaultStatus ) { b->bDefaultStatus = (INT8)iDefaultStatus; b->uiFlags |= BUTTON_DIRTY; } } void RemoveButtonDefaultStatus( INT32 iButtonID ) { GUI_BUTTON *b; Assert( iButtonID >= 0 ); Assert( iButtonID < MAX_BUTTONS); b = ButtonList[ iButtonID ]; Assert( b ); if( b->bDefaultStatus ) { b->bDefaultStatus = DEFAULT_STATUS_NONE; b->uiFlags |= BUTTON_DIRTY; } } BOOLEAN GetButtonArea(INT32 iButtonID, SGPRect *pRect) { GUI_BUTTON *b; Assert( iButtonID >= 0 ); Assert( iButtonID < MAX_BUTTONS); Assert(pRect); b = ButtonList[ iButtonID ]; Assert( b ); if((pRect==NULL) || (b==NULL)) return(FALSE); pRect->iLeft=b->Area.RegionTopLeftX; pRect->iTop=b->Area.RegionTopLeftY; pRect->iRight=b->Area.RegionBottomRightX; pRect->iBottom=b->Area.RegionBottomRightY; return(TRUE); } INT32 GetButtonWidth(INT32 iButtonID) { GUI_BUTTON *b; Assert( iButtonID >= 0 ); Assert( iButtonID < MAX_BUTTONS); b = ButtonList[ iButtonID ]; Assert( b ); if(b==NULL) return(-1); return(b->Area.RegionBottomRightX - b->Area.RegionTopLeftX); } INT32 GetButtonHeight(INT32 iButtonID) { GUI_BUTTON *b; Assert( iButtonID >= 0 ); Assert( iButtonID < MAX_BUTTONS); b = ButtonList[ iButtonID ]; Assert( b ); if(b==NULL) return(-1); return(b->Area.RegionBottomRightY - b->Area.RegionTopLeftY); } INT32 GetButtonX(INT32 iButtonID) { GUI_BUTTON *b; Assert( iButtonID >= 0 ); Assert( iButtonID < MAX_BUTTONS); b = ButtonList[ iButtonID ]; Assert( b ); if(b==NULL) return(0); return(b->Area.RegionTopLeftX); } INT32 GetButtonY(INT32 iButtonID) { GUI_BUTTON *b; Assert( iButtonID >= 0 ); Assert( iButtonID < MAX_BUTTONS); b = ButtonList[ iButtonID ]; Assert( b ); if(b==NULL) return(0); return(b->Area.RegionTopLeftY); }