/* ** 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: W3DGameWindow.cpp //////////////////////////////////////////////////// //----------------------------------------------------------------------------- // // Westwood Studios Pacific. // // Confidential Information // Copyright (C) 2001 - All Rights Reserved // //----------------------------------------------------------------------------- // // Project: RTS3 // // File name: W3DGameWindow.cpp // // Created: Colin Day, June 2001 // // Desc: W3D implementation of a game window // //----------------------------------------------------------------------------- /////////////////////////////////////////////////////////////////////////////// // SYSTEM INCLUDES //////////////////////////////////////////////////////////// #include // USER INCLUDES ////////////////////////////////////////////////////////////// #include "GameClient/Gadget.h" #include "GameClient/GameWindowGlobal.h" #include "W3DDevice/GameClient/W3DGameWindow.h" #include "W3DDevice/GameClient/W3DGameWindowManager.h" #include "W3DDevice/GameClient/W3DDisplay.h" // DEFINES //////////////////////////////////////////////////////////////////// enum { BORDER_CORNER_SIZE = 15, BORDER_LINE_SIZE = 20, }; // PRIVATE TYPES ////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // PRIVATE DATA /////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// static Bool bordersInit = FALSE; static const Image *borderPieces[NUM_BORDER_PIECES] = { 0 }; // PUBLIC DATA //////////////////////////////////////////////////////////////// // PRIVATE PROTOTYPES ///////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // PRIVATE FUNCTIONS ////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // initBorders ================================================================ //============================================================================= static void initBorders( void ) { borderPieces[ BORDER_CORNER_UL ] = TheMappedImageCollection->findImageByName( AsciiString( "BorderCornerUL" ) ); borderPieces[ BORDER_CORNER_UR ] = TheMappedImageCollection->findImageByName( AsciiString( "BorderCornerUR" ) ); borderPieces[ BORDER_CORNER_LL ] = TheMappedImageCollection->findImageByName( AsciiString( "BorderCornerLL" ) ); borderPieces[ BORDER_CORNER_LR ] = TheMappedImageCollection->findImageByName( AsciiString( "BorderCornerLR" ) ); borderPieces[ BORDER_VERTICAL_LEFT ] = TheMappedImageCollection->findImageByName( AsciiString( "BorderLeft" ) ); borderPieces[ BORDER_VERTICAL_LEFT_SHORT ] = TheMappedImageCollection->findImageByName( AsciiString( "BorderLeftShort" ) ); borderPieces[ BORDER_HORIZONTAL_TOP ] = TheMappedImageCollection->findImageByName( AsciiString( "BorderTop" ) ); borderPieces[ BORDER_HORIZONTAL_TOP_SHORT ] = TheMappedImageCollection->findImageByName( AsciiString( "BorderTopShort" ) ); borderPieces[ BORDER_VERTICAL_RIGHT ] = TheMappedImageCollection->findImageByName( AsciiString( "BorderRight" ) ); borderPieces[ BORDER_VERTICAL_RIGHT_SHORT ] = TheMappedImageCollection->findImageByName( AsciiString( "BorderRightShort" ) ); borderPieces[ BORDER_HORIZONTAL_BOTTOM ] = TheMappedImageCollection->findImageByName( AsciiString( "BorderBottom" ) ); borderPieces[ BORDER_HORIZONTAL_BOTTOM_SHORT ] = TheMappedImageCollection->findImageByName( AsciiString( "BorderBottomShort" ) ); bordersInit = TRUE; } // W3DGameWindow::blitBorderRect ============================================== //============================================================================= void W3DGameWindow::blitBorderRect( Int x, Int y, Int width, Int height ) { Int Offset = 15; Int OffsetLower = 5; // init image loc if needed if( bordersInit == FALSE ) initBorders(); // save original x, y Int originalX = x; Int originalY = y; Int maxX = x + width; Int maxY = y + height; Int x2, y2; // used for simultaneous drawing of line pairs Int size = 20; Int halfSize = size / 2; // Draw Horizontal Lines // All border pieces are based on a 10 pixel offset from the centerline y = originalY - Offset; y2 = maxY - OffsetLower; x2 = maxX - (OffsetLower + BORDER_LINE_SIZE); for( x=(originalX + OffsetLower); x <= x2; x += BORDER_LINE_SIZE ) { TheDisplay->drawImage( borderPieces[ BORDER_HORIZONTAL_TOP ], x, y, x + size, y + size ); TheDisplay->drawImage( borderPieces[ BORDER_HORIZONTAL_BOTTOM ], x, y2, x + size, y2 + size ); } x2 = maxX - 5;//BORDER_CORNER_SIZE; // x == place to draw remainder if any if( (x2 - x) >= (BORDER_LINE_SIZE / 2) ) { //Blit Half piece TheDisplay->drawImage( borderPieces[ BORDER_HORIZONTAL_TOP_SHORT ], x, y, x + halfSize, y + size ); TheDisplay->drawImage( borderPieces[ BORDER_HORIZONTAL_BOTTOM_SHORT ], x, y2, x + halfSize, y2 + size ); x += (BORDER_LINE_SIZE / 2); } // x2 - x ... must now be less than a half piece // check for equals and if not blit an adjusted half piece border pieces have // a two pixel repeat so we will blit one pixel over if necessary to line up // the art, but we'll cover-up the overlap with the corners if( x < x2 ) { x -= ((BORDER_LINE_SIZE / 2) - (((x2 - x) + 1) & ~1)); //Blit Half piece TheDisplay->drawImage( borderPieces[ BORDER_HORIZONTAL_TOP_SHORT ], x, y, x + halfSize, y + size ); TheDisplay->drawImage( borderPieces[ BORDER_HORIZONTAL_BOTTOM_SHORT ], x, y2, x + halfSize, y2 + size ); } // Draw Vertical Lines // All border pieces are based on a 10 pixel offset from the centerline x = originalX - Offset; x2 = maxX - OffsetLower; y2 = maxY - (OffsetLower + BORDER_LINE_SIZE); for( y=(originalY + OffsetLower); y <= y2; y += BORDER_LINE_SIZE ) { TheDisplay->drawImage( borderPieces[ BORDER_VERTICAL_LEFT ], x, y, x + size, y + size ); TheDisplay->drawImage( borderPieces[ BORDER_VERTICAL_RIGHT ], x2, y, x2 + size, y + size ); } y2 = maxY - OffsetLower;//BORDER_CORNER_SIZE; // y == place to draw remainder if any if( (y2 - y) >= (BORDER_LINE_SIZE / 2) ) { //Blit Half piece TheDisplay->drawImage( borderPieces[ BORDER_VERTICAL_LEFT_SHORT ], x, y, x + size, y + halfSize ); TheDisplay->drawImage( borderPieces[ BORDER_VERTICAL_RIGHT_SHORT ], x2, y, x2 + size, y + halfSize ); y += (BORDER_LINE_SIZE / 2); } // y2 - y ... must now be less than a half piece // check for equals and if not blit an adjusted half piece border pieces have // a two pixel repeat so we will blit one pixel over if necessary to line up // the art, but we'll cover-up the overlap with the corners if( y < y2 ) { y -= ((BORDER_LINE_SIZE / 2) - (((y2 - y) + 1) & ~1)); //Blit Half piece TheDisplay->drawImage( borderPieces[ BORDER_VERTICAL_LEFT_SHORT ], x, y, x + size, y + halfSize ); TheDisplay->drawImage( borderPieces[ BORDER_VERTICAL_RIGHT_SHORT ], x2, y, x2 + size, y + halfSize ); } // Draw Corners x = originalX - BORDER_CORNER_SIZE ; y = originalY - BORDER_CORNER_SIZE; TheDisplay->drawImage( borderPieces[ BORDER_CORNER_UL ], x, y, x + size, y + size ); x = maxX - 5;//BORDER_CORNER_SIZE; y = originalY - BORDER_CORNER_SIZE; TheDisplay->drawImage( borderPieces[ BORDER_CORNER_UR ], x, y, x + size, y + size ); x = originalX - BORDER_CORNER_SIZE; y = maxY - 5;//BORDER_CORNER_SIZE; TheDisplay->drawImage( borderPieces[ BORDER_CORNER_LL ], x, y, x + size, y + size ); x = maxX - 5;//BORDER_CORNER_SIZE; y = maxY - 5;//BORDER_CORNER_SIZE; TheDisplay->drawImage( borderPieces[ BORDER_CORNER_LR ], x, y, x + size, y + size ); } // end blitBorderRect /////////////////////////////////////////////////////////////////////////////// // PUBLIC FUNCTIONS /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // W3DGameWindow::W3DGameWindow =============================================== //============================================================================= W3DGameWindow::W3DGameWindow( void ) { // override the default draw with our own default draw function for W3D winSetDrawFunc( TheWindowManager->getDefaultDraw() ); m_textPos.x = m_textPos.y = 0; m_currTextColor = WIN_COLOR_UNDEFINED; m_needPolyDraw = FALSE; m_newTextPos = FALSE; } // end W3DGameWindow // W3DGameWindow::~W3DGameWindow ============================================== //============================================================================= W3DGameWindow::~W3DGameWindow( void ) { } // end ~W3DGameWindow // W3DGameWinDefaultDraw ====================================================== /** The default redraw callback. Draws the background using either * the drawData or the background color unless the background color * is set to -1 indicating that default drawing is turned off. */ //============================================================================= void W3DGameWinDefaultDraw( GameWindow *window, WinInstanceData *instData ) { Real borderWidth = 1.0f; ICoord2D origin; ICoord2D size; W3DGameWindow *w3dWindow = (W3DGameWindow *)window; /** @todo NOTE that we're making a W3DGameWindow cast here, it seems logical because we are in a W3D draw function so it's reasonable to assume that we have a W3DGameWindow. However, we may want to revisit this type of casting in the future, we have the same problems with the ObjectModules where we cast object modules for their individual methods. Also note that the other W3D implementations of GUI controls are making a similar cast for their device implementation functions */ // get the window position in the screen coordinates w3dWindow->winGetScreenPosition( &origin.x, &origin.y ); // get size of window w3dWindow->winGetSize( &size.x, &size.y ); // image drawing vs color drawing if( BitTest( window->winGetStatus(), WIN_STATUS_IMAGE ) ) { const Image *image; // get image if( BitTest( window->winGetStatus(), WIN_STATUS_ENABLED ) == FALSE ) image = window->winGetDisabledImage( 0 ); else if( BitTest( instData->getState(), WIN_STATE_HILITED ) ) image = window->winGetHiliteImage( 0 ); else image = window->winGetEnabledImage( 0 ); if( image ) { ICoord2D start, end; start.x = origin.x + instData->m_imageOffset.x; start.y = origin.y + instData->m_imageOffset.y; end.x = start.x + size.x; end.y = start.y + size.y; TheWindowManager->winDrawImage( image, start.x, start.y, end.x, end.y ); } // end if } // end if else { Color color, borderColor; // get colors if( BitTest( window->winGetStatus(), WIN_STATUS_ENABLED ) == FALSE ) { color = window->winGetDisabledColor( 0 ); borderColor = window->winGetDisabledBorderColor( 0 ); } // end if else if( BitTest( instData->getState(), WIN_STATE_HILITED ) ) { color = window->winGetHiliteColor( 0 ); borderColor = window->winGetHiliteBorderColor( 0 ); } // end else if else { color = window->winGetEnabledColor( 0 ); borderColor = window->winGetEnabledBorderColor( 0 ); } // end else // // draw the border at the edges // if( borderColor != WIN_COLOR_UNDEFINED ) TheWindowManager->winOpenRect( borderColor, borderWidth, origin.x, origin.y, origin.x + size.x, origin.y + size.y ); // draw filled background if( color != WIN_COLOR_UNDEFINED ) TheWindowManager->winFillRect( color, borderWidth, origin.x + borderWidth, origin.y + borderWidth, origin.x + size.x - borderWidth, origin.y + size.y - borderWidth ); } // end else // if we have a video buffer, draw the video buffer if ( instData->m_videoBuffer ) { ICoord2D pos, size; window->winGetScreenPosition( &pos.x, &pos.y ); window->winGetSize( &size.x, &size.y ); TheDisplay->drawVideoBuffer( instData->m_videoBuffer, pos.x, pos.y, pos.x + size.x, pos.y + size.y ); } } // end W3DGameWinDefaultDraw // W3DGameWindow::winDrawBorder =============================================== //============================================================================= void W3DGameWindow::winDrawBorder( void ) { Bool found = FALSE; Int originalX, originalY; Int x, y; Int width; Int i, bits; /** @todo this WinDrawBorder is the old Nox function for drawing the borders * on various windows and controls. We should derive classes of game * windows for the different GUI controls and move the specific pieces of * code that apply for those gadgets to those classes */ // based on window class pass different regions to real draw winGetScreenPosition( &originalX, &originalY ); for( i = 0; (i < (sizeof(UnsignedInt) * 8)) && (found == FALSE); i++ ) { bits = (1 << i); if( m_instData.getStyle() & bits ) { switch( m_instData.getStyle() & bits ) { case GWS_CHECK_BOX: found = TRUE; break; case GWS_ENTRY_FIELD: { // EntryData *e = (EntryData *)m_userData; width = m_size.x; x = originalX; y = originalY; // Calculate space for Label if( m_instData.getTextLength() ) { Int textWidth = 0; TheWindowManager->winGetTextSize( m_instData.getFont(), m_instData.getText(), &textWidth, NULL, 0 ); width -= textWidth + 6; x += textWidth + 6; } /* // Colin: The very notion of entry width makes no sense to me since // we already have a gadget width, and the max characters for // an entry box so I am removing this. // Adjust entry box if an entryWidth is provided if( (e->entryWidth > 0) && (width > e->entryWidth) ) { width = e->entryWidth; x = originalX + (m_size.x - e->entryWidth); } */ blitBorderRect( x, y, width, m_size.y ); found = TRUE; break; } // Sliders may need a bar for the button // it is assumed that the smaller size component (width, height) // is the axis of the slider case GWS_VERT_SLIDER: case GWS_HORZ_SLIDER: // blitBorderLine( originalX, originalY, window->size.x, window->size.y ); found = TRUE; break; case GWS_SCROLL_LISTBOX: { ListboxData *list = (ListboxData *)m_userData; Int sliderAdjustment = 0; Int labelAdjustment = 0; if( list->scrollBar ) { GameWindow *child = list->slider->winGetChild(); ICoord2D size; child->winGetSize( &size.x, &size.y ); sliderAdjustment = size.y; } // end if if( m_instData.getTextLength() ) labelAdjustment = 4; blitBorderRect( (originalX - 3), (originalY - (3 + labelAdjustment)), (m_size.x + 3 - sliderAdjustment), (m_size.y + 6) ); found = TRUE; break; } case GWS_RADIO_BUTTON: case GWS_STATIC_TEXT: case GWS_PROGRESS_BAR: case GWS_PUSH_BUTTON: case GWS_USER_WINDOW: case GWS_TAB_CONTROL: blitBorderRect( originalX, originalY, m_size.x, m_size.y ); found = TRUE; break; } // end switch } // end if } // end for i } // end WinDrawBorder // W3DGameWindow::winSetFont ================================================== /** Set the font for a widow */ //============================================================================= void W3DGameWindow::winSetFont( GameFont *font ) { // extending functionality GameWindow::winSetFont( font ); // assign font to text renderer m_textRenderer.Set_Font( static_cast(font->fontData) ); // this is a visual change m_needPolyDraw = TRUE; } // end WinSetFont // W3DGameWindow::winSetText ================================================== /** Set the text for window */ //============================================================================= Int W3DGameWindow::winSetText( UnicodeString newText ) { // extending functionality GameWindow::winSetText( newText ); // rebuild the sentence in our text renderer m_textRenderer.Build_Sentence( m_instData.getText().str(),NULL, NULL ); // this is a visual change m_needPolyDraw = TRUE; return WIN_ERR_OK; } // end WinSetText // W3DGameWindow::winSetPosition ============================================== /** Set window position */ //============================================================================= Int W3DGameWindow::winSetPosition( Int x, Int y ) { ICoord2D prevPos; // get previous position prevPos.x = m_region.lo.x; prevPos.y = m_region.lo.y; // extending functionality GameWindow::winSetPosition( x, y ); // update any text position change m_textPos.x += m_region.lo.x - prevPos.x; m_textPos.y += m_region.lo.y - prevPos.y; m_newTextPos = TRUE; return WIN_ERR_OK; } // end WinSetPosition // W3DGameWindow::getTextSize ================================================= /** Get the size of the text in our inst data */ //============================================================================= void W3DGameWindow::getTextSize( Int *width, Int *height ) { Vector2 extents = m_textRenderer.Get_Text_Extents( m_instData.getText().str() ); if( width ) *width = extents.X; if( height ) *height = extents.Y; } // end getTextSize // W3DGameWindow::getTextLoc ================================================== // Set our text rendering location */ //============================================================================= void W3DGameWindow::setTextLoc( Int x, Int y ) { if( m_textPos.x != x ) { m_textPos.x = x; m_newTextPos = TRUE; } if( m_textPos.y != y ) { m_textPos.y = y; m_newTextPos = TRUE; } // end if } // end setTextLoc // W3DGameWindow::drawText ==================================================== /** Draw the text in our 2d sentence renderer */ //============================================================================= void W3DGameWindow::drawText( Color color ) { Bool needDraw = FALSE; // if new text pos we need to redraw if( m_newTextPos ) { m_newTextPos = FALSE; needDraw = TRUE; } // end if // if color switch, set new color if( m_currTextColor != color ) { m_currTextColor = color; needDraw = TRUE; } // end if // draw the quads if needed if( needDraw || m_needPolyDraw ) { UnsignedInt outline = TheWindowManager->winMakeColor( 0, 0, 0, 255 ); m_textRenderer.Reset_Polys(); m_textRenderer.Set_Location( Vector2( m_textPos.x + 1, m_textPos.y + 1 ) ); m_textRenderer.Draw_Sentence( outline ); m_textRenderer.Set_Location( Vector2( m_textPos.x, m_textPos.y ) ); m_textRenderer.Draw_Sentence( m_currTextColor ); m_needPolyDraw = FALSE; } // end if // do the render m_textRenderer.Render(); } // end drawText