/* ** Command & Conquer Generals(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: ExtendedMessageBox.cpp /////////////////////////////////////////////////////////////////// // Author: Matt Campbell, January 2003 // Description: We go quiet in 1 day, gold in 15. Poor time to rewrite message boxes, so // we get this file instead. Phooey. /////////////////////////////////////////////////////////////////////////////////////////////////// // SYSTEM INCLUDES //////////////////////////////////////////////////////////// // USER INCLUDES ////////////////////////////////////////////////////////////// #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine #include "Common/GameEngine.h" #include "Common/NameKeyGenerator.h" #include "GameClient/WindowLayout.h" #include "GameClient/Gadget.h" #include "GameClient/Shell.h" #include "GameClient/KeyDefs.h" #include "GameClient/GadgetStaticText.h" #include "GameClient/GameWindowManager.h" #include "GameClient/ExtendedMessageBox.h" WindowMsgHandledType ExtendedMessageBoxSystem( GameWindow *window, UnsignedInt msg, WindowMsgData mData1, WindowMsgData mData2 ); //------------------------------------------------------------------------------------------------- /** Create an extended Modal Message Box */ //------------------------------------------------------------------------------------------------- static GameWindow *gogoExMessageBox(Int x, Int y, Int width, Int height, UnsignedShort buttonFlags, UnicodeString titleString, UnicodeString bodyString, void *userData, MessageBoxFunc yesCallback, MessageBoxFunc noCallback, MessageBoxFunc okCallback, MessageBoxFunc cancelCallback ) { // first check to make sure we have some buttons to display if(buttonFlags == 0 ) { return NULL; } GameWindow *parent = TheWindowManager->winCreateFromScript( AsciiString("Menus/MessageBox.wnd") ); TheWindowManager->winSetModal( parent ); TheWindowManager->winSetFocus( NULL ); // make sure we lose focus from other windows even if we refuse focus ourselves TheWindowManager->winSetFocus( parent ); // If the user wants the size to be different then the default float ratioX, ratioY = 1; if( width > 0 && height > 0 ) { ICoord2D temp; //First grab the percent increase/decrease compaired to the default size parent->winGetSize( &temp.x, &temp.y); ratioX = (float)width / (float)temp.x; ratioY = (float)height / (float)temp.y; //Set the window's new size parent->winSetSize( width, height); //Resize/reposition all the children windows based off the ratio GameWindow *child; for( child = parent->winGetChild(); child; child = child->winGetNext() ) { child->winGetSize(&temp.x, &temp.y); temp.x =Int(temp.x * ratioX); temp.y =Int(temp.y * ratioY); child->winSetSize(temp.x, temp.y); child->winGetPosition(&temp.x, &temp.y); temp.x =Int(temp.x * ratioX); temp.y =Int(temp.y * ratioY); child->winSetPosition(temp.x, temp.y); } } // If the user wants to position the message box somewhere other then default if( x >= 0 && y >= 0) parent->winSetPosition(x, y); // Reposition the buttons Int buttonX[3], buttonY[3]; //In the layout, buttonOk will be in the first button position NameKeyType buttonOkID = TheNameKeyGenerator->nameToKey( AsciiString( "MessageBox.wnd:ButtonOk" ) ); GameWindow *buttonOk = TheWindowManager->winGetWindowFromId(parent, buttonOkID); buttonOk->winGetPosition(&buttonX[0], &buttonY[0]); NameKeyType buttonYesID = TheNameKeyGenerator->nameToKey( AsciiString( "MessageBox.wnd:ButtonYes" ) ); GameWindow *buttonYes = TheWindowManager->winGetWindowFromId(parent, buttonYesID); //buttonNo in the second position NameKeyType buttonNoID = TheNameKeyGenerator->nameToKey( AsciiString( "MessageBox.wnd:ButtonNo" ) ); GameWindow *buttonNo = TheWindowManager->winGetWindowFromId(parent, buttonNoID); buttonNo->winGetPosition(&buttonX[1], &buttonY[1]); //and buttonCancel in the third NameKeyType buttonCancelID = TheNameKeyGenerator->nameToKey( AsciiString( "MessageBox.wnd:ButtonCancel" ) ); GameWindow *buttonCancel = TheWindowManager->winGetWindowFromId(parent, buttonCancelID); buttonCancel->winGetPosition(&buttonX[2], &buttonY[2]); //we shouldn't have button OK and Yes on the same dialog if((buttonFlags & (MSG_BOX_OK | MSG_BOX_YES)) == (MSG_BOX_OK | MSG_BOX_YES) ) { DEBUG_ASSERTCRASH(false, ("Passed in MSG_BOX_OK and MSG_BOX_YES. Big No No.")); } //Position the OK button if we have one if( (buttonFlags & MSG_BOX_OK) == MSG_BOX_OK) { buttonOk->winSetPosition(buttonX[0], buttonY[0]); buttonOk->winHide(FALSE); } else if( (buttonFlags & MSG_BOX_YES) == MSG_BOX_YES) { //Position the Yes if we have one buttonYes->winSetPosition(buttonX[0], buttonY[0]); buttonYes->winHide(FALSE); } if((buttonFlags & (MSG_BOX_NO | MSG_BOX_CANCEL)) == (MSG_BOX_NO | MSG_BOX_CANCEL) ) { //If we have both the No and Cancel button, then the no should go in the middle position buttonNo->winSetPosition(buttonX[1], buttonY[1]); buttonCancel->winSetPosition(buttonX[2], buttonY[2]); buttonNo->winHide(FALSE); buttonCancel->winHide(FALSE); } else if( (buttonFlags & MSG_BOX_NO) == MSG_BOX_NO) { //if we just have the no button, then position it in the right most spot buttonNo->winSetPosition(buttonX[2], buttonY[2]); buttonNo->winHide(FALSE); } else if( (buttonFlags & MSG_BOX_CANCEL) == MSG_BOX_CANCEL) { //else if we just have the Cancel button, well, it should always go in the right spot buttonCancel->winSetPosition(buttonX[2], buttonY[2]); buttonCancel->winHide(FALSE); } // Fill the text into the text boxes NameKeyType staticTextTitleID = TheNameKeyGenerator->nameToKey( AsciiString( "MessageBox.wnd:StaticTextTitle" ) ); GameWindow *staticTextTitle = TheWindowManager->winGetWindowFromId(parent, staticTextTitleID); GadgetStaticTextSetText(staticTextTitle,titleString); NameKeyType staticTextMessageID = TheNameKeyGenerator->nameToKey( AsciiString( "MessageBox.wnd:StaticTextMessage" ) ); GameWindow *staticTextMessage = TheWindowManager->winGetWindowFromId(parent, staticTextMessageID); GadgetStaticTextSetText(staticTextMessage,bodyString); // create a structure that will pass the functions to WindowExMessageBoxData *MsgBoxCallbacks = NEW WindowExMessageBoxData; MsgBoxCallbacks->cancelCallback = cancelCallback; MsgBoxCallbacks->noCallback = noCallback; MsgBoxCallbacks->okCallback = okCallback; MsgBoxCallbacks->yesCallback = yesCallback; MsgBoxCallbacks->userData = userData; //pass the structure to the dialog parent->winSetUserData( MsgBoxCallbacks ); parent->winSetSystemFunc(ExtendedMessageBoxSystem); //make sure the dialog is showing and bring it to the top parent->winHide(FALSE); parent->winBringToTop(); return parent; }// gogoExMessageBox GameWindow *ExMessageBoxYesNo (UnicodeString titleString,UnicodeString bodyString, void *userData, MessageBoxFunc yesCallback, MessageBoxFunc noCallback) { return gogoExMessageBox(-1,-1,-1,-1,MSG_BOX_NO | MSG_BOX_YES , titleString, bodyString, userData, yesCallback, noCallback, NULL, NULL); } GameWindow *ExMessageBoxYesNoCancel (UnicodeString titleString,UnicodeString bodyString, void *userData, MessageBoxFunc yesCallback, MessageBoxFunc noCallback, MessageBoxFunc cancelCallback) { return gogoExMessageBox(-1,-1,-1,-1,MSG_BOX_NO | MSG_BOX_YES | MSG_BOX_CANCEL , titleString, bodyString, userData, yesCallback, noCallback, NULL, cancelCallback); } GameWindow *ExMessageBoxOkCancel (UnicodeString titleString,UnicodeString bodyString, void *userData, MessageBoxFunc okCallback, MessageBoxFunc cancelCallback) { return gogoExMessageBox(-1,-1,-1,-1,MSG_BOX_OK | MSG_BOX_CANCEL , titleString, bodyString, userData, NULL, NULL, okCallback, cancelCallback); } GameWindow *ExMessageBoxOk (UnicodeString titleString,UnicodeString bodyString, void *userData, MessageBoxFunc okCallback) { return gogoExMessageBox(-1,-1,-1,-1,MSG_BOX_OK, titleString, bodyString, userData, NULL, NULL, okCallback, NULL); } GameWindow *ExMessageBoxCancel (UnicodeString titleString,UnicodeString bodyString, void *userData, MessageBoxFunc cancelCallback) { return gogoExMessageBox(-1,-1,-1,-1, MSG_BOX_CANCEL, titleString, bodyString, userData, NULL, NULL, NULL, cancelCallback); } // PRIVATE DATA /////////////////////////////////////////////////////////////////////////////////// //------------------------------------------------------------------------------------------------- /** Message Box window system callback */ //------------------------------------------------------------------------------------------------- WindowMsgHandledType ExtendedMessageBoxSystem( GameWindow *window, UnsignedInt msg, WindowMsgData mData1, WindowMsgData mData2 ) { switch( msg ) { //--------------------------------------------------------------------------------------------- case GWM_DESTROY: { delete (WindowExMessageBoxData *)window->winGetUserData(); window->winSetUserData( NULL ); break; } // end case // -------------------------------------------------------------------------------------------- case GWM_INPUT_FOCUS: { // if we're givin the opportunity to take the keyboard focus we must say we want it if( mData1 == TRUE ) *(Bool *)mData2 = TRUE; break; } // end input //--------------------------------------------------------------------------------------------- case GBM_SELECTED: { GameWindow *control = (GameWindow *)mData1; Int controlID = control->winGetWindowId(); static NameKeyType buttonOkID = TheNameKeyGenerator->nameToKey( AsciiString( "MessageBox.wnd:ButtonOk" ) ); static NameKeyType buttonYesID = TheNameKeyGenerator->nameToKey( AsciiString( "MessageBox.wnd:ButtonYes" ) ); static NameKeyType buttonNoID = TheNameKeyGenerator->nameToKey( AsciiString( "MessageBox.wnd:ButtonNo" ) ); static NameKeyType buttonCancelID = TheNameKeyGenerator->nameToKey( AsciiString( "MessageBox.wnd:ButtonCancel" ) ); WindowExMessageBoxData *MsgBoxCallbacks = (WindowExMessageBoxData *)window->winGetUserData(); MessageBoxReturnType ret = MB_RETURN_CLOSE; if( controlID == buttonOkID ) { if (MsgBoxCallbacks->okCallback) ret = MsgBoxCallbacks->okCallback(MsgBoxCallbacks->userData); } // end if else if( controlID == buttonYesID ) { if (MsgBoxCallbacks->yesCallback) ret = MsgBoxCallbacks->yesCallback(MsgBoxCallbacks->userData); } // end else if else if( controlID == buttonNoID ) { if (MsgBoxCallbacks->noCallback) ret = MsgBoxCallbacks->noCallback(MsgBoxCallbacks->userData); } // end else if else if( controlID == buttonCancelID ) { if (MsgBoxCallbacks->cancelCallback) ret = MsgBoxCallbacks->cancelCallback(MsgBoxCallbacks->userData); } // end else if if (ret == MB_RETURN_CLOSE) TheWindowManager->winDestroy(window); break; } // end selected //--------------------------------------------------------------------------------------------- default: return MSG_IGNORED; } // end switch return MSG_HANDLED; } // end ExtendedMessageBoxSystem