/* ** Command & Conquer Generals Zero Hour(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ //////////////////////////////////////////////////////////////////////////////// // // // (c) 2001-2003 Electronic Arts Inc. // // // //////////////////////////////////////////////////////////////////////////////// // FILE: GadgetPushButton.cpp ///////////////////////////////////////////////// //----------------------------------------------------------------------------- // // Westwood Studios Pacific. // // Confidential Information // Copyright (C) 2001 - All Rights Reserved // //----------------------------------------------------------------------------- // // Project: RTS3 // // File name: PushButton.cpp // // Created: Colin Day, June 2001 // // Desc: Pushbutton GUI gadget control callbacks // //----------------------------------------------------------------------------- /////////////////////////////////////////////////////////////////////////////// // SYSTEM INCLUDES //////////////////////////////////////////////////////////// #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine // USER INCLUDES ////////////////////////////////////////////////////////////// #include "Common/AudioEventRTS.h" #include "Common/Language.h" #include "Common/GameAudio.h" #include "GameClient/Gadget.h" #include "GameClient/GameWindowManager.h" #include "GameClient/InGameUI.h" #ifdef _INTERNAL // for occasional debugging... //#pragma optimize("", off) //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes") #endif // DEFINES //////////////////////////////////////////////////////////////////// // PRIVATE TYPES ////////////////////////////////////////////////////////////// // PRIVATE DATA /////////////////////////////////////////////////////////////// // PUBLIC DATA //////////////////////////////////////////////////////////////// // PRIVATE PROTOTYPES ///////////////////////////////////////////////////////// // PRIVATE FUNCTIONS ////////////////////////////////////////////////////////// static Bool buttonTriggersOnMouseDown(GameWindow *window) { // Buttons with the on down status set trigger on mouse down. jba. [8/6/2003] Bool onDown = BitTest( window->winGetStatus(), WIN_STATUS_ON_MOUSE_DOWN); // Checkboxes always trigger on mouse down. jba [8/6/2003] if (BitTest( window->winGetStatus(), WIN_STATUS_CHECK_LIKE )) { onDown = true; } return onDown; } // GadgetPushButtonInput ====================================================== /** Handle input for push button */ //============================================================================= WindowMsgHandledType GadgetPushButtonInput( GameWindow *window, UnsignedInt msg, WindowMsgData mData1, WindowMsgData mData2 ) { WinInstanceData *instData = window->winGetInstanceData(); switch( msg ) { // ------------------------------------------------------------------------ case GWM_MOUSE_ENTERING: { if( BitTest( instData->getStyle(), GWS_MOUSE_TRACK ) ) { BitSet( instData->m_state, WIN_STATE_HILITED ); TheWindowManager->winSendSystemMsg( instData->getOwner(), GBM_MOUSE_ENTERING, (WindowMsgData)window, mData1 ); //TheWindowManager->winSetFocus( window ); } if(window->winGetParent() && BitTest(window->winGetParent()->winGetStyle(),GWS_HORZ_SLIDER) ) { WinInstanceData *instDataParent = window->winGetParent()->winGetInstanceData(); BitSet(instDataParent->m_state, WIN_STATE_HILITED); } break; } // end mouse entering // ------------------------------------------------------------------------ case GWM_MOUSE_LEAVING: { if(BitTest( instData->getStyle(), GWS_MOUSE_TRACK ) ) { BitClear( instData->m_state, WIN_STATE_HILITED ); TheWindowManager->winSendSystemMsg( instData->getOwner(), GBM_MOUSE_LEAVING, (WindowMsgData)window, mData1 ); } // // if this is not a check-like button, clear any selected state when the // move leaves the window area // if( BitTest( window->winGetStatus(), WIN_STATUS_CHECK_LIKE ) == FALSE ) if( BitTest( instData->getState(), WIN_STATE_SELECTED ) ) BitClear( instData->m_state, WIN_STATE_SELECTED ); //TheWindowManager->winSetFocus( NULL ); if(window->winGetParent() && BitTest(window->winGetParent()->winGetStyle(),GWS_HORZ_SLIDER) ) { WinInstanceData *instDataParent = window->winGetParent()->winGetInstanceData(); BitClear(instDataParent->m_state, WIN_STATE_HILITED); } break; } // end mouse leaving // ------------------------------------------------------------------------ case GWM_LEFT_DRAG: { TheWindowManager->winSendSystemMsg( instData->getOwner(), GGM_LEFT_DRAG, (WindowMsgData)window, mData1 ); break; } // end left drag // ------------------------------------------------------------------------ case GWM_LEFT_DOWN: { PushButtonData *pData = (PushButtonData *)window->winGetUserData(); AudioEventRTS buttonClick; if(pData && pData->altSound.isNotEmpty()) buttonClick.setEventName(pData->altSound); else buttonClick.setEventName("GUIClick"); if( TheAudio ) { TheAudio->addAudioEvent( &buttonClick ); } // end if // // for 'check-like' buttons we have "dual state", we flip the selected status // in that case instead of just turning it on like normal ... also note // that selected messages are sent immediately // if( BitTest( window->winGetStatus(), WIN_STATUS_CHECK_LIKE ) ) { if( BitTest( instData->m_state, WIN_STATE_SELECTED ) ) BitClear( instData->m_state, WIN_STATE_SELECTED ); else BitSet( instData->m_state, WIN_STATE_SELECTED ); } // end if else { // just select as normal BitSet( instData->m_state, WIN_STATE_SELECTED ); } // end else if (buttonTriggersOnMouseDown(window)) { TheWindowManager->winSendSystemMsg( instData->getOwner(), GBM_SELECTED, (WindowMsgData)window, mData1 ); } break; } // end left down //------------------------------------------------------------------------- case GWM_LEFT_UP: { // // note check like selected messages aren't sent here ... they are sent // on the down press // if( BitTest( instData->getState(), WIN_STATE_SELECTED ) && BitTest( window->winGetStatus(), WIN_STATUS_CHECK_LIKE ) == FALSE ) { if (!buttonTriggersOnMouseDown(window)) { // If it didn't trigger on mouse down, trigger on the mouse up. jba [8/6/2003] TheWindowManager->winSendSystemMsg( instData->getOwner(), GBM_SELECTED, (WindowMsgData)window, mData1 ); } BitClear( instData->m_state, WIN_STATE_SELECTED ); } else { // this up click was not meant for this button return MSG_IGNORED; } break; } // end left up or left click // ------------------------------------------------------------------------ case GWM_RIGHT_DOWN: { PushButtonData *pData = (PushButtonData *)window->winGetUserData(); AudioEventRTS buttonClick; if(pData && pData->altSound.isNotEmpty()) buttonClick.setEventName(pData->altSound); else buttonClick.setEventName("GUIClick"); if( BitTest( instData->getStatus(), WIN_STATUS_RIGHT_CLICK ) ) { // Need to be specially marked to care about right mouse events if( TheAudio ) { TheAudio->addAudioEvent( &buttonClick ); } // end if // // for 'check-like' buttons we have "dual state", we flip the selected status // in that case instead of just turning it on like normal ... also note // that selected messages are sent immediately // if( BitTest( window->winGetStatus(), WIN_STATUS_CHECK_LIKE ) ) { if( BitTest( instData->m_state, WIN_STATE_SELECTED ) ) BitClear( instData->m_state, WIN_STATE_SELECTED ); else BitSet( instData->m_state, WIN_STATE_SELECTED ); TheWindowManager->winSendSystemMsg( instData->getOwner(), GBM_SELECTED_RIGHT, (WindowMsgData)window, mData1 ); } // end if else { // just select as normal BitSet( instData->m_state, WIN_STATE_SELECTED ); } // end else } else { // Else I don't care about right events return MSG_IGNORED; } break; } // end right down //------------------------------------------------------------------------- case GWM_RIGHT_UP: { if( BitTest( instData->getStatus(), WIN_STATUS_RIGHT_CLICK ) ) { // // note check like selected messages aren't sent here ... they are sent // on the down press // if( BitTest( instData->getState(), WIN_STATE_SELECTED ) && BitTest( window->winGetStatus(), WIN_STATUS_CHECK_LIKE ) == FALSE ) { TheWindowManager->winSendSystemMsg( instData->getOwner(), GBM_SELECTED_RIGHT, (WindowMsgData)window, mData1 ); BitClear( instData->m_state, WIN_STATE_SELECTED ); } else { // this up click was not meant for this button return MSG_IGNORED; } } else { // Else I don't care about right events return MSG_IGNORED; } break; } // end right up or right click // ------------------------------------------------------------------------ case GWM_CHAR: { switch( mData1 ) { // -------------------------------------------------------------------- case KEY_ENTER: case KEY_SPACE: { if( BitTest( mData2, KEY_STATE_UP ) ) { // // note check like selected messages aren't sent here ... they are sent // on the down press // if( BitTest( instData->getState(), WIN_STATE_SELECTED ) && BitTest( window->winGetStatus(), WIN_STATUS_CHECK_LIKE ) == FALSE ) { TheWindowManager->winSendSystemMsg( instData->getOwner(), GBM_SELECTED, (WindowMsgData)window, 0 ); BitClear( instData->m_state, WIN_STATE_SELECTED ); } } else { // // for 'check-like' buttons we have "dual state", we flip the selected status // in that case instead of just turning it on like normal ... also note // that selected messages are sent immediately // if( BitTest( window->winGetStatus(), WIN_STATUS_CHECK_LIKE ) ) { if( BitTest( instData->m_state, WIN_STATE_SELECTED ) ) BitClear( instData->m_state, WIN_STATE_SELECTED ); else BitSet( instData->m_state, WIN_STATE_SELECTED ); TheWindowManager->winSendSystemMsg( instData->getOwner(), GBM_SELECTED, (WindowMsgData)window, mData1 ); } // end if else { // just select as normal BitSet( instData->m_state, WIN_STATE_SELECTED ); } // end else } // end else break; } // end handle enter and space button // -------------------------------------------------------------------- case KEY_DOWN: case KEY_RIGHT: case KEY_TAB: { if( BitTest( mData2, KEY_STATE_DOWN ) ) TheWindowManager->winNextTab(window); break; } // end key down, right or tab // -------------------------------------------------------------------- case KEY_UP: case KEY_LEFT: { if( BitTest( mData2, KEY_STATE_DOWN ) ) TheWindowManager->winPrevTab(window); break; } // end key up or left // -------------------------------------------------------------------- default: return MSG_IGNORED; } // end switch on char break; } // end character message // ------------------------------------------------------------------------ default: return MSG_IGNORED; } // end switch( msg ) return MSG_HANDLED; } // end GadgetPushButtonInput // GadgetPushButtonSystem ===================================================== /** Handle system messages for push button */ //============================================================================= WindowMsgHandledType GadgetPushButtonSystem( GameWindow *window, UnsignedInt msg, WindowMsgData mData1, WindowMsgData mData2 ) { WinInstanceData *instData = window->winGetInstanceData(); switch( msg ) { // ------------------------------------------------------------------------ case GGM_SET_LABEL: { // set text into the win instance text data field window->winSetText( *(UnicodeString*)mData1 ); break; } // ------------------------------------------------------------------------ case GWM_CREATE: break; // ------------------------------------------------------------------------ case GWM_DESTROY: { PushButtonData *pData = (PushButtonData *)window->winGetUserData(); if(pData) delete pData; window->winSetUserData(NULL); } break; // ------------------------------------------------------------------------ case GWM_INPUT_FOCUS: if( mData1 == FALSE ) BitClear( instData->m_state, WIN_STATE_HILITED ); else BitSet( instData->m_state, WIN_STATE_HILITED ); TheWindowManager->winSendSystemMsg( instData->getOwner(), GGM_FOCUS_CHANGE, (WindowMsgData)mData1, window->winGetWindowId() ); if( mData1 == FALSE ) *(Bool*)mData2 = FALSE; else *(Bool*)mData2 = TRUE; break; default: return MSG_IGNORED; } // end switch( msg ) return MSG_HANDLED; } // end GadgetPushButtonSystem // ------------------------------------------------------------------------------------------------ /** Set the visual status of a button to make it looked checked/unchecked ... DO NOT send * any actual button selected messages, this is ONLY VISUAL */ // ------------------------------------------------------------------------------------------------ void GadgetCheckLikeButtonSetVisualCheck( GameWindow *g, Bool checked ) { // sanity if( g == NULL ) return; // get instance data WinInstanceData *instData = g->winGetInstanceData(); if( instData == NULL ) return; // sanity, must be a check like button if( BitTest( g->winGetStatus(), WIN_STATUS_CHECK_LIKE ) == FALSE ) { DEBUG_CRASH(( "GadgetCheckLikeButtonSetVisualCheck: Window is not 'CHECK-LIKE'\n" )); return; } // end if // set or clear the 'pushed' state if( instData ) { if( checked == TRUE ) BitSet( instData->m_state, WIN_STATE_SELECTED ); else BitClear( instData->m_state, WIN_STATE_SELECTED ); } // end if } // end GadgetCheckLikeButtonSetVisualCheck // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ Bool GadgetCheckLikeButtonIsChecked( GameWindow *g ) { // sanity if( g == NULL ) return FALSE; // get instance data WinInstanceData *instData = g->winGetInstanceData(); if( instData == NULL ) return FALSE; // we just hold this "check like dual state thingie" using the selected state return BitTest( instData->m_state, WIN_STATE_SELECTED ); } // end GadgetCheckLikeButtonIsChecked // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ void GadgetButtonEnableCheckLike( GameWindow *g, Bool makeCheckLike, Bool initiallyChecked ) { // sanity if( g == NULL ) return; // get inst data WinInstanceData *instData = g->winGetInstanceData(); if( instData == NULL ) return; // make it check like if( makeCheckLike ) g->winSetStatus( WIN_STATUS_CHECK_LIKE ); else g->winClearStatus( WIN_STATUS_CHECK_LIKE ); // set the initially checked "state" if( initiallyChecked ) BitSet( instData->m_state, WIN_STATE_SELECTED ); else BitClear( instData->m_state, WIN_STATE_SELECTED ); } // end GadgetButtonEnableCheckLike // GadgetButtonSetText ======================================================== /** Set the text for a push button */ //============================================================================= void GadgetButtonSetText( GameWindow *g, UnicodeString text ) { // sanity if( g == NULL ) return; TheWindowManager->winSendSystemMsg( g, GGM_SET_LABEL, (WindowMsgData)&text, 0 ); } // end GadgetButtonSetText PushButtonData * getNewPushButtonData( void ) { PushButtonData *p = NEW PushButtonData; if(!p) return NULL; p->userData = NULL; p->drawBorder = FALSE; p->drawClock = NO_CLOCK; p->overlayImage = NULL; return p; } // GadgetButtonSetBorder ====================================================== /** Set to draw the special borders in the game */ //============================================================================= void GadgetButtonSetBorder( GameWindow *g, Color color, Bool drawBorder = TRUE ) { if( g == NULL ) return; PushButtonData *pData = (PushButtonData *)g->winGetUserData(); if(!pData) { pData = getNewPushButtonData(); } pData->drawBorder = drawBorder; pData->colorBorder = color; g->winSetUserData(pData); } // GadgetButtonDrawClock ====================================================== /** Set to draw a rectClock on the button */ //============================================================================= void GadgetButtonDrawClock( GameWindow *g, Int percent, Color color ) { if( g == NULL ) return; PushButtonData *pData = (PushButtonData *)g->winGetUserData(); if(!pData) { pData = getNewPushButtonData(); } pData->drawClock = NORMAL_CLOCK; pData->percentClock = percent; pData->colorClock = color; g->winSetUserData(pData); } // GadgetButtonDrawInverseClock ====================================================== /** Set to draw an inversed rectClock on the button */ //============================================================================= void GadgetButtonDrawInverseClock( GameWindow *g, Int percent, Color color ) { if( g == NULL ) return; PushButtonData *pData = (PushButtonData *)g->winGetUserData(); if(!pData) { pData = getNewPushButtonData(); } pData->drawClock = INVERSE_CLOCK; pData->percentClock = percent; pData->colorClock = color; g->winSetUserData(pData); } void GadgetButtonDrawOverlayImage( GameWindow *g, const Image *image ) { if( g == NULL ) return; PushButtonData *pData = (PushButtonData *)g->winGetUserData(); if(!pData) { pData = getNewPushButtonData(); } pData->overlayImage = image; g->winSetUserData(pData); } // GadgetButtonSetData ====================================================== /** Sets random data that the user can contain on the button */ //============================================================================= void GadgetButtonSetData(GameWindow *g, void *data) { if( g == NULL ) return; PushButtonData *pData = (PushButtonData *)g->winGetUserData(); if(!pData) { pData = getNewPushButtonData(); } pData->userData = data; g->winSetUserData(pData); } // GadgetButtonGetData ====================================================== /** Gets the random data the user had already set on the button */ //============================================================================= void *GadgetButtonGetData(GameWindow *g) { if( g == NULL ) return NULL; PushButtonData *pData = (PushButtonData *)g->winGetUserData(); if(!pData) { return NULL; } return pData->userData; } void GadgetButtonSetAltSound(GameWindow *g, AsciiString altSound ) { if(!g) return; PushButtonData *pData = (PushButtonData *)g->winGetUserData(); if(!pData) { return; } pData->altSound = altSound; }