/*
** 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: WindowLayout.cpp /////////////////////////////////////////////////////////////////////////
// Created: Colin Day, September 2001
// Desc: Layouts for grouping windows together
///////////////////////////////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "GameClient/WindowLayout.h"
#include "GameClient/Shell.h"
#include "GameClient/GameWindowManager.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
WindowLayout::WindowLayout( void )
{
m_filenameString.set("EmptyLayout");
m_windowList = NULL;
m_windowTail = NULL;
m_windowCount = 0;
m_hidden = FALSE;
m_init = NULL;
m_update = NULL;
m_shutdown = NULL;
} // end WindowLayout
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
WindowLayout::~WindowLayout( void )
{
//
// it is the users responsability to remove windows from the layout beforing destroying the
// layout itself. This allows for maximum flexibility of the window layouts and you can
// use them in any you see fit, as long as they are clean when they go away
//
DEBUG_ASSERTCRASH( m_windowList == NULL, ("Window layout being destroyed still has window references") );
DEBUG_ASSERTCRASH( m_windowTail == NULL, ("Window layout being destroyed still has window references") );
} // end ~WindowLayout
//-------------------------------------------------------------------------------------------------
/** Set the hidden/visible status of all the windows in this layout */
//-------------------------------------------------------------------------------------------------
void WindowLayout::hide( Bool hide )
{
GameWindow *window;
// hide or unhide all windows in this layout
for( window = m_windowList; window; window = window->winGetNextInLayout() )
{
window->winHide( hide );
} // end for window
// save the new visible state of the system
m_hidden = hide;
} // end hide
//-------------------------------------------------------------------------------------------------
/** Add window to this layout */
//-------------------------------------------------------------------------------------------------
void WindowLayout::addWindow( GameWindow *window )
{
GameWindow *win = findWindow( window );
// only add window if window is not in this layout already
if( win == NULL )
{
DEBUG_ASSERTCRASH( window->winGetNextInLayout() == NULL,
("NextInLayout should be NULL before adding") );
DEBUG_ASSERTCRASH( window->winGetPrevInLayout() == NULL,
("PrevInLayout should be NULL before adding") );
window->winSetPrevInLayout( NULL );
window->winSetNextInLayout( m_windowList );
if( m_windowList )
m_windowList->winSetPrevInLayout( window );
m_windowList = window;
// set layout into window
window->winSetLayout( this );
// if no tail pointer, this is it
if( m_windowTail == NULL )
m_windowTail = window;
// we gots another window now
m_windowCount++;
} // end if
} // end addWindow
//-------------------------------------------------------------------------------------------------
/** Remove window from this layout */
//-------------------------------------------------------------------------------------------------
void WindowLayout::removeWindow( GameWindow *window )
{
GameWindow *win = findWindow( window );
// can't remove window unless it's really part of this layout
if( win )
{
GameWindow *prev, *next;
prev = win->winGetPrevInLayout();
next = win->winGetNextInLayout();
if( next )
next->winSetPrevInLayout( prev );
if( prev )
prev->winSetNextInLayout( next );
else
m_windowList = next;
// set window as having no layout info
win->winSetLayout( NULL );
win->winSetNextInLayout( NULL );
win->winSetPrevInLayout( NULL );
// if we removed the tail, set the new tail
if( m_windowTail == win )
m_windowTail = prev;
// we lost one sir!
m_windowCount--;
} // end if
} // end removeWindow
//-------------------------------------------------------------------------------------------------
/** Destroy all the windows in a layout */
//-------------------------------------------------------------------------------------------------
void WindowLayout::destroyWindows( void )
{
if (this == NULL)
{
return;
}
GameWindow *window;
while( (window = getFirstWindow()) != 0 )
{
// remove window from this layout
removeWindow( window );
// destroy window in window system
TheWindowManager->winDestroy( window );
} // end while
} // end destroyWindows
//-------------------------------------------------------------------------------------------------
/** Create the windows using the .wnd file script and load all windows into
* this layout */
//-------------------------------------------------------------------------------------------------
Bool WindowLayout::load( AsciiString filename )
{
// sanity
if( filename.isEmpty() )
return FALSE;
//
// when we create new windows they are always placed on the top
// of the window stack. The load layout from script will return the
// FIRST window loaded from the script, if we traverse from the head of
// the window list till we see that window returned from the layout
// loader, that will be all the root windows loaded from the .wnd file
//
GameWindow *target;
WindowLayoutInfo info;
target = TheWindowManager->winCreateFromScript( filename, &info );
if( target == NULL )
{
DEBUG_ASSERTCRASH( target, ("WindowLayout::load - Failed to load layout") );
DEBUG_LOG(( "WindowLayout::load - Unable to load layout file '%s'\n", filename.str() ));
return FALSE;
} // end if
//
// add windows loaded from .wnd file to the layout, via info.windows.
//
std::list::iterator it;
for (it = info.windows.begin(); it != info.windows.end(); ++it)
{
// add window to this layout
addWindow( *it );
}
/* MDC - can't do this, as modal windows will be at the head...
//
// add windows loaded from .wnd file to the layout, note we start at
// target and go backwards as the layout->addWindow() puts at top
// of list and will therefore reverse the order
//
GameWindow *window;
for( window = target; window; window = window->winGetPrev() )
{
// add window to this layout
addWindow( window );
} // end for window
*/
// copy filename
m_filenameString = filename;
// assign script info to the layout
setInit( info.init );
setUpdate( info.update );
setShutdown( info.shutdown );
return TRUE; // success
} // end load
//-------------------------------------------------------------------------------------------------
/** Bring all windows in this layout forward */
//-------------------------------------------------------------------------------------------------
void WindowLayout::bringForward( void )
{
//
// loop through all our windows and bring each of them to the top of
// the window stack, note that we are getting a prev pointer because the
// action of bringing a window to the top of the stack does detach it
// from the window list and the layout window list ... it is then
// re-attached at the top. Also note, to preserve the ordering of
// the windows we pull from the tail and add to the top
//
GameWindow *window, *prev;
Int countLeft = m_windowCount;
for( window = m_windowTail; countLeft; window = prev )
{
DEBUG_ASSERTCRASH( window, ("Must have window: m_windowCount is off") );
prev = window->winGetPrevInLayout();
window->winBringToTop();
countLeft--;
} // end for window
} // end bringForward
// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
/** Find window within this layout */
//-------------------------------------------------------------------------------------------------
GameWindow *WindowLayout::findWindow( GameWindow *window )
{
GameWindow *win;
for( win = m_windowList; win; win = win->winGetNextInLayout() )
if( win == window )
return win;
return NULL; // window not found
} // end findWindow