1618 lines
50 KiB
C++
Raw Permalink Normal View History

/*
** 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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: GUIEditWindowManager.cpp /////////////////////////////////////////////////////////////////
// Created: Colin Day, July 2001
// Desc: Window manager for the GUI edit tool, we want this up
// fast and to look like what we use in the game so we're going
// to use the WW3D window manager, and just override the
// drawing functions to draw lines and images to the
// display. We will also be adding our own functionality
// here for editing and interacting with the GUI windows.
///////////////////////////////////////////////////////////////////////////////////////////////////
#include <stdlib.h>
#include <stdio.h>
#include "Common/Debug.h"
#include "GameClient/GadgetSlider.h"
#include "GUIEditWindowManager.h"
#include "EditWindow.h"
#include "GUIEdit.h"
#include "HierarchyView.h"
// PUBLIC DATA ////////////////////////////////////////////////////////////////////////////////////
GUIEditWindowManager *TheGUIEditWindowManager = NULL; ///< editor use only
///////////////////////////////////////////////////////////////////////////////////////////////////
// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
/** Is the given widnow in the clipboard at the top level. NOTE that
* children are NOT included in this search */
//-------------------------------------------------------------------------------------------------
Bool GUIEditWindowManager::isWindowInClipboard( GameWindow *window,
GameWindow **list )
{
GameWindow *other;
// sanity
if( list == NULL || window == NULL )
return FALSE;
// just run through the window list in the clipboard to check
for( other = *list; other; other = other->winGetNext() )
{
if( window == other )
return TRUE; // found
} // end for window
return FALSE; // not found
} // end isWindowInClipboard
//-------------------------------------------------------------------------------------------------
/** Add the window to clipboard list */
//-------------------------------------------------------------------------------------------------
void GUIEditWindowManager::linkToClipboard( GameWindow *window,
GameWindow **list )
{
// sanity
if( window == NULL || list == NULL )
return;
// debug sanity checking, can't add if already in it
if( isWindowInClipboard( window, list ) )
return;
window->winSetPrev( NULL );
window->winSetNext( *list );
if( *list )
(*list)->winSetPrev( window );
*list = window;
} // end linkToClipboard
//-------------------------------------------------------------------------------------------------
/** Unlink window from the clipboard list */
//-------------------------------------------------------------------------------------------------
void GUIEditWindowManager::unlinkFromClipboard( GameWindow *window,
GameWindow **list )
{
GameWindow *next, *prev;
// sanity
if( window == NULL || list == NULL )
return;
// debug sanity checking, can't remove if not in
if( isWindowInClipboard( window, list ) == FALSE )
return;
prev = window->winGetPrev();
next = window->winGetNext();
if( next )
next->winSetPrev( prev );
if( prev )
prev->winSetNext( next );
else
*list = next;
} // end unlinkFromClipboard
//-------------------------------------------------------------------------------------------------
/** Remove selection entries for child windows that also have ANY of
* their parents in the selection list. */
//-------------------------------------------------------------------------------------------------
void GUIEditWindowManager::removeSupervisedChildSelections( void )
{
WindowSelectionEntry *select, *next;
GameWindow *window, *parent;
// iterate selections
for( select = TheEditor->getSelectList(); select; select = next )
{
Bool parentSelected;
// get next entry
next = select->next;
// get window data
window = select->window;
parent = window->winGetParent();
// check to see if any parent is selected
parentSelected = FALSE;
while( parent )
{
if( TheEditor->isWindowSelected( parent ) )
parentSelected = TRUE;
parent = parent->winGetParent();
} // end while
//
// if there is a parent selected then we can remove this
// selection
//
if( parentSelected == TRUE )
TheEditor->unSelectWindow( window );
} // end for select
} // end removeSupervisedChildSelections
//-------------------------------------------------------------------------------------------------
/** Traverse the selected window list, any child windows we encounter
* are removed from their parent and placed in the window list at
* the top level. Window position adjustments are made to compensate
* for the transition from "parent space" to "screen space" */
//-------------------------------------------------------------------------------------------------
/*
void GUIEditWindowManager::orphanSelectedChildren( void )
{
WindowSelectionEntry *select;
GameWindow *window, *parent;
for( select = TheEditor->getSelectList(); select; select = select->next )
{
// get window information
window = select->window;
parent = window->winGetParent();
// we have a parent we're a candidate
if( parent )
{
ICoord2D pos;
// get the screen position of the window BEFORE we remove the parent
window->winGetScreenPosition( &pos.x, &pos.y );
// remove the child from the parent and add to top level of window system
window->winSetParent( NULL );
//
// adjust the position, which previously was relative to the parent
// but now is relative to the screen
//
window->winSetPosition( pos.x, pos.y );
} // end if, we had a parent
} // end for select
} // end orphanSelectedChildren
*/
///////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
GUIEditWindowManager::GUIEditWindowManager( void )
{
m_clipboard = NULL;
m_clipboardDup = NULL;
m_copySpacing = 8;
m_numCopiesPasted = 0;
} // end GUIEditWindowManager
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
GUIEditWindowManager::~GUIEditWindowManager( void )
{
// the duplicate list is only used in the actual act of pasting
assert( m_clipboardDup == NULL );
// free all data on the clipboard
resetClipboard();
} // end ~GUIEditWindowManager
//-------------------------------------------------------------------------------------------------
/** initialize the system */
//-------------------------------------------------------------------------------------------------
void GUIEditWindowManager::init( void )
{
// extending functionality
W3DGameWindowManager::init();
} // end init
//-------------------------------------------------------------------------------------------------
/** Destroy a game window */
//-------------------------------------------------------------------------------------------------
Int GUIEditWindowManager::winDestroy( GameWindow *window )
{
//
// delete the edit data from window if present, the editor is the
// ONLY place where this edit data is allocated anyway, it is
// NOT present in the game
//
GameWindowEditData *editData = window->winGetEditData();
if( editData )
{
// delete it
delete editData;
// set the edit data to NULL in the window
window->winSetEditData( NULL );
} // end if
// call our base class functionality
return W3DGameWindowManager::winDestroy( window );
} // end winDestroy
//-------------------------------------------------------------------------------------------------
/** Create a new window by setting up its parameters and callbacks. */
//-------------------------------------------------------------------------------------------------
GameWindow *GUIEditWindowManager::winCreate( GameWindow *parent,
UnsignedInt status,
Int x, Int y,
Int width, Int height,
GameWinSystemFunc system,
WinInstanceData *instData )
{
GameWindow *window;
// call base class create
window = W3DGameWindowManager::winCreate( parent, status, x, y,
width, height, system,
instData );
// allocate edit data for the window
if( window )
{
GameWindowEditData *editData = new GameWindowEditData;
// set edit data to nothing
memset( editData, 0, sizeof( GameWindowEditData ) );
// attach edit data to window
window->winSetEditData( editData );
} // end if
return window;
} // end winCreate
//-------------------------------------------------------------------------------------------------
/** Destroy all windows in the clipboard */
//-------------------------------------------------------------------------------------------------
void GUIEditWindowManager::resetClipboard( void )
{
GameWindow *window, *next;
// iterate through all windows
for( window = m_clipboard; window; window = next )
{
// get next window
next = window->winGetNext();
//
// in order for the destroy to really work correctly, the system
// assumes that the window is actually part of the system, so take it
// away from the clipboard and add it back to the regular system
//
unlinkFromClipboard( window, &m_clipboard );
linkWindow( window );
//
// delete the window, note that these aren't part of the active editor,
// the live only in the clipboard land so we don't need to notify
// the edit window or the editor that this is going away ... they can't
// possible be selected or anything
//
winDestroy( window );
} // end for window
// immediately process the destroyed windows just to be clean here
processDestroyList();
// nothing in the buffer now
m_clipboard = NULL;
m_numCopiesPasted = 0;
} // end resetClipboard
//-------------------------------------------------------------------------------------------------
/** Is the clipboard empty */
//-------------------------------------------------------------------------------------------------
Bool GUIEditWindowManager::isClipboardEmpty( void )
{
if( m_clipboard )
return TRUE;
return FALSE;
} // end isClipboardEmtpy
//-------------------------------------------------------------------------------------------------
/** Remove the selected windows from the current layout and put them into
* the clipboard. If the parent of a window is not being cut with the
* child the child will have no parent and will be adjusted to absolute
* screen coordinates with no parent */
//-------------------------------------------------------------------------------------------------
void GUIEditWindowManager::cutSelectedToClipboard( void )
{
//
// reset the clipboard just to be safe, this is done in the copy but
// it's nice cause we depend on it here for deleting
//
resetClipboard();
// first copy the selected windows to the clipboard
copySelectedToClipboard();
//
// now delete the selected windows from the scene if we actually
// copied them to the clipboard
//
if( m_clipboard )
TheEditor->deleteSelected();
} // end cutSelectedToClipboard
//-------------------------------------------------------------------------------------------------
/** Given the current window in a list pointed to by 'root', if that
* window is selected, duplicate it and place it on the clipboard. If
* it's not selected check it's children to see if any of them are
* selected and should be duplicated. Then just move on down the list
* and check the other sibling windows as well */
//-------------------------------------------------------------------------------------------------
void GUIEditWindowManager::duplicateSelected( GameWindow *root )
{
// end of recursion
if( root == NULL )
return;
// if widow is selected duplicate and continue on
if( TheEditor->isWindowSelected( root ) )
{
GameWindow *duplicate;
// perform the duplication of window and all children
duplicate = duplicateWindow( root, NULL );
if( duplicate )
{
//
// if our source window had a parent, that means that the child
// was selected, but the parent wasn't. Therefore the new
// duplicate has no parent, but needs to have its position
// adjusted to be relative to the screen instead of the now
// missing parent
//
GameWindow *parent = root->winGetParent();
if( parent )
{
ICoord2D parentPos;
ICoord2D pos;
parent->winGetScreenPosition( &parentPos.x, &parentPos.y );
duplicate->winGetScreenPosition( &pos.x, &pos.y );
pos.x += parentPos.x;
pos.y += parentPos.y;
duplicate->winSetPosition( pos.x, pos.y );
} // end if
// add window to the clipboard
linkToClipboard( duplicate, &m_clipboard );
} // end if
} // end if
else
{
//
// the window itself is not selected, but some of it's children
// may be so check them
//
duplicateSelected( root->winGetChild() );
} // end else
// move on to the next window
duplicateSelected( root->winGetNext() );
} // end duplicateSelected
//-------------------------------------------------------------------------------------------------
/** Copy the selected windows to the clipboard */
//-------------------------------------------------------------------------------------------------
void GUIEditWindowManager::copySelectedToClipboard( void )
{
// reset the contents of the clipboard
resetClipboard();
//
// to simplify things, child windows that ALSO have their
// parent window selected will become unselected, anything that
// happens to the parent will automatically include the child
//
removeSupervisedChildSelections();
//
// go through the window list and duplicate anything selected, note
// that the clipboard will contain the list of windows in reverse
// order from the original order in the layout
//
duplicateSelected( m_windowList );
} // end copySelectedToClipboard
//-------------------------------------------------------------------------------------------------
/** Take the string representation of the name of the window passed in,
* and add a number to it at the end. If there is already a number
* on it at the end, that number will be incremented */
//-------------------------------------------------------------------------------------------------
void GUIEditWindowManager::incrementName( GameWindow *window )
{
WinInstanceData *instData = window->winGetInstanceData();
// bleah, this is ugly and hokey, but if I rewrite
// this I will botch it (cuz I'm currently not sure
// how to test it)
char name[MAX_WINDOW_NAME_LEN];
strcpy(name, instData->m_decoratedNameString.str());
Int len = strlen( name );
char numberBuffer[ MAX_WINDOW_NAME_LEN ];
Int i, j;
char c;
Bool hasNumberSuffix = FALSE;
Int numberStartIndex = 0;
// trivial case, just append a number to the end and get outta here
if( len == 0 )
{
strcat( name, "1" );
goto cleanup;
} // end if
//
// start from the end of the string and back up to the start picking
// off each character, checking if it's a number, and adding that number
// to a string buffer. After all numbers are read from the name
// the string is turned real number, incremented, and replaces the
// existing number string
//
for( i = 0; i < MAX_WINDOW_NAME_LEN; i++ )
numberBuffer[ i ] = ' ';
numberBuffer[ MAX_WINDOW_NAME_LEN - 1 ] = '\0';
for( i = len - 1, j = MAX_WINDOW_NAME_LEN - 2; i >= 0; i-- )
{
c = name[ i ];
if( isdigit( c ) )
{
hasNumberSuffix = TRUE;
numberBuffer[ j-- ] = c;
} // end if
else
{
numberStartIndex = i + 1;
break; // exit for i
} // end else
} // end for i
//
// if we have a number suffix change it to a number, increment it, and
// paste it over the old number
//
if( hasNumberSuffix == TRUE )
{
Int number = atoi( numberBuffer );
Int charsAdded;
// increment the number
number++;
// turn number back into string
itoa( number, numberBuffer, 10 );
// put number string OVER the original number string in the name
len = strlen( numberBuffer );
charsAdded = 0;
for( i = 0; i < len; i++ )
{
if( i + numberStartIndex <= MAX_WINDOW_NAME_LEN - 2 )
{
name[ i + numberStartIndex ] = numberBuffer[ i ];
charsAdded++;
} // end if
}
name[ numberStartIndex + charsAdded ] = 0; // terminate at end of new string
} // end if
else
{
// no number at end, easy ... just append a number '1'
strcat( name, "1" );
} // end else
cleanup:
instData->m_decoratedNameString = name;
} // end incrementName
//-------------------------------------------------------------------------------------------------
/** Validate the names for all the windows and child windows in the copy
* buffer that they are unique and don't collide with any windows in
* the real system. This is in preparation for a paste of the copy
* buffer into the real working layout */
//-------------------------------------------------------------------------------------------------
void GUIEditWindowManager::validateClipboardNames( GameWindow *root )
{
Bool nameOK = FALSE;
WinInstanceData *instData;
Int sanityLoop = 0;
// end of recursion
if( root == NULL )
return;
// get our inst data
instData = root->winGetInstanceData();
//
// we will check our name against all names in the active layout AND
// in the clipboard, if it's a dupe we will pick a new name and
// try it over and over until it's OK
//
while( nameOK == FALSE )
{
// if we're not a dupe with anything anywhere we're ok
if( !TheEditor->isNameDuplicate( m_windowList, root, instData->m_decoratedNameString ) &&
!TheEditor->isNameDuplicate( m_clipboardDup, root, instData->m_decoratedNameString ) )
nameOK = TRUE;
else
incrementName( root ); // have to change our name, so sorry :(
//
// and just because I don't want the possibility of a infinite loop
// EVER to occur here this is just a sanity check that will probably
// never get run
//
if( ++sanityLoop > 99999999 )
{
char buffer[ 1024 ];
sprintf( buffer, "Unable to make unique name for '%s', please manually edit the name before saving.",
instData->m_decoratedNameString.str() );
MessageBox( TheEditor->getWindowHandle(), buffer, "Error Mapping Names", MB_OK );
nameOK = TRUE;
} // end if
} // end if
// validate our children names
GameWindow *child;
for( child = root->winGetChild(); child; child = child->winGetNext() )
validateClipboardNames( child );
// validate the next window in the list
validateClipboardNames( root->winGetNext() );
} // end validateClipboardNames
//-------------------------------------------------------------------------------------------------
/** Paste the contents of the clipboard into the window world */
//-------------------------------------------------------------------------------------------------
void GUIEditWindowManager::pasteClipboard( void )
{
GameWindow *window, *next;
GameWindow *firstWindow = NULL;
// check for empty clipboard
if( m_clipboard == NULL )
{
MessageBox( TheEditor->getWindowHandle(),
"Cannot perform paste, the clipboard is empty.",
"Clipboard Empty",
MB_OK );
return;
} // end if
// create a duplicate of everything in the clipboard
assert( m_clipboardDup == NULL );
createClipboardDuplicate();
//
// we are about to paste the contents of the clipboard dupe into the world,
// scan through the clipboard dupe and adjust any names that will cause
// collisions in the current layout
//
validateClipboardNames( m_clipboardDup );
// keep a pointer to the first window we will add
firstWindow = m_clipboardDup;
// move everything from the duplicate list into the real world
for( window = m_clipboardDup; window; window = next )
{
// get next window
next = window->winGetNext();
// remove from clipboard
unlinkFromClipboard( window, &m_clipboardDup );
// place in real layout
linkWindow( window );
//
// since we just pasted in a new window, offset it a little bit
// from the original location based on how many times we're pasted
// this set of copies. This way they won't all stack on top of
// each other
//
ICoord2D pos, safePos;
window->winGetPosition( &pos.x, &pos.y );
pos.x += m_copySpacing * (m_numCopiesPasted + 1);
pos.y += m_copySpacing * (m_numCopiesPasted + 1);
// kee the location legal
TheEditor->computeSafeLocation( window, pos.x, pos.y,
&safePos.x, &safePos.y );
// set the new location
TheEditor->moveWindowTo( window, safePos.x, safePos.y );
// notify the hierarchy of the new window added
TheHierarchyView->addWindow( window, HIERARCHY_ADD_AT_TOP );
} // end for window
// the clipboard duplicate list is only for the act of pasting
assert( m_clipboardDup == NULL );
// we've now completed another successful copy pasted in
m_numCopiesPasted++;
//
// now that all windows were added to the layout, and we have
// the first window that we added ... since we know they were added
// at the head of the window list we can traverse from the first
// window added to the head of the window list to access each new
// window we just pasted. As a convenience we will unselct anything
// selected and select all the windows we added
//
TheEditor->clearSelections();
window = firstWindow;
while( window )
{
TheEditor->selectWindow( window );
window = window->winGetPrev();
// notify the editor that each of the windows was created
TheEditor->notifyNewWindow( window );
} // end while
// if we did in fact paste a window then our file contents have changed
if( firstWindow )
TheEditor->setUnsaved( TRUE );
} // end pasteClipboard
//-------------------------------------------------------------------------------------------------
/** Convinience funtion to copy the Draw state info for a given instance */
//-------------------------------------------------------------------------------------------------
void InstDrawCopy ( WinInstanceData *instData, WinInstanceData *sourceInstData)
{
// do the copy of the colors for each state
memcpy( &instData->m_enabledDrawData,
&sourceInstData->m_enabledDrawData,
sizeof( WinDrawData ) * MAX_DRAW_DATA );
memcpy( &instData->m_disabledDrawData,
&sourceInstData->m_disabledDrawData,
sizeof( WinDrawData ) * MAX_DRAW_DATA );
memcpy( &instData->m_hiliteDrawData,
&sourceInstData->m_hiliteDrawData,
sizeof( WinDrawData ) * MAX_DRAW_DATA );
}
//-------------------------------------------------------------------------------------------------
/** Duplciate a window and all its children */
//-------------------------------------------------------------------------------------------------
GameWindow *GUIEditWindowManager::duplicateWindow( GameWindow *source,
GameWindow *parent )
{
GameWindow *duplicate = NULL;
UnsignedInt style, status;
WinInstanceData *instData;
WinInstanceData instDataCopy;
ICoord2D pos, size;
// sanity
if( source == NULL )
return NULL;
// get the window instance data and make a copy of it for creating new stuff
instData = source->winGetInstanceData();
instDataCopy = *instData;
// memcpy( &instDataCopy, instData, sizeof( instDataCopy ) );
//
// do NOT copy the display string instances, these MUST be allocated when
// needed in real windows
//
instDataCopy.m_text = NULL;
instDataCopy.m_tooltip = NULL;
// get a few properties we're going to need
style = source->winGetStyle();
status = source->winGetStatus();
source->winGetPosition( &pos.x, &pos.y );
source->winGetSize( &size.x, &size.y );
// create a new window or gadget of the appropriate type
if( BitTest( style, GWS_PUSH_BUTTON ) )
{
duplicate =
TheWindowManager->gogoGadgetPushButton( parent,
status,
pos.x,
pos.y,
size.x,
size.y,
&instDataCopy,
source->winGetFont(),
FALSE );
} // end if
else if( BitTest( style, GWS_RADIO_BUTTON ) )
{
RadioButtonData *radioData = (RadioButtonData *)source->winGetUserData();
RadioButtonData radioDataCopy;
radioDataCopy = *radioData;
// memcpy( &radioDataCopy, radioData, sizeof( RadioButtonData ) );
duplicate =
TheWindowManager->gogoGadgetRadioButton( parent,
status,
pos.x,
pos.y,
size.x,
size.y,
&instDataCopy,
&radioDataCopy,
source->winGetFont(),
FALSE );
} // end else if
else if( BitTest( style, GWS_CHECK_BOX ) )
{
duplicate =
TheWindowManager->gogoGadgetCheckbox( parent,
status,
pos.x,
pos.y,
size.x,
size.y,
&instDataCopy,
source->winGetFont(),
FALSE );
} // end else if
else if( BitTest( style, GWS_HORZ_SLIDER | GWS_VERT_SLIDER ) )
{
SliderData *sliderData = (SliderData *)source->winGetUserData();
SliderData sliderDataCopy;
sliderDataCopy = *sliderData;
// memcpy( &sliderDataCopy, sliderData, sizeof( SliderData ) );
duplicate = TheWindowManager->gogoGadgetSlider( parent,
status,
pos.x,
pos.y,
size.x,
size.y,
&instDataCopy,
&sliderDataCopy,
source->winGetFont(),
FALSE );
//
// copy the color and image schemes of the sub controls that make
// up a slider
//
if( duplicate )
{
WinInstanceData *instData;
WinInstanceData *sourceInstData;
// do the slider thumb
GameWindow *thumb = GadgetSliderGetThumb( duplicate );
GameWindow *sourceThumb = GadgetSliderGetThumb( source );
if( thumb && sourceThumb )
{
instData = thumb->winGetInstanceData();
sourceInstData = sourceThumb->winGetInstanceData();
// do the copy of the colors for each state
memcpy( &instData->m_enabledDrawData,
&sourceInstData->m_enabledDrawData,
sizeof( WinDrawData ) * MAX_DRAW_DATA );
memcpy( &instData->m_disabledDrawData,
&sourceInstData->m_disabledDrawData,
sizeof( WinDrawData ) * MAX_DRAW_DATA );
memcpy( &instData->m_hiliteDrawData,
&sourceInstData->m_hiliteDrawData,
sizeof( WinDrawData ) * MAX_DRAW_DATA );
} // end if
} // end if
} // end else if
else if( BitTest( style, GWS_COMBO_BOX ) )
{
ComboBoxData *comboData = (ComboBoxData *)source->winGetUserData();
ComboBoxData comboDataCopy;
memset( &comboDataCopy, 0, sizeof( ComboBoxData ) );
comboDataCopy.entryData = new EntryData;
memset ( comboDataCopy.entryData, 0, sizeof(EntryData));
comboDataCopy.listboxData = new ListboxData;
memset ( comboDataCopy.listboxData, 0, sizeof(ListboxData));
comboDataCopy.entryCount = comboData->entryCount;
comboDataCopy.isEditable = comboData->isEditable;
comboDataCopy.maxChars = comboData->maxChars;
comboDataCopy.maxDisplay = comboData->maxDisplay;
comboDataCopy.lettersAndNumbersOnly = comboData->lettersAndNumbersOnly;
comboDataCopy.asciiOnly = comboData->asciiOnly;
comboDataCopy.listboxData->autoPurge = comboData->listboxData->autoPurge;
comboDataCopy.listboxData->autoScroll = comboData->listboxData->autoScroll;
comboDataCopy.listboxData->scrollIfAtEnd = comboData->listboxData->scrollIfAtEnd;
comboDataCopy.listboxData->forceSelect = comboData->listboxData->forceSelect;
comboDataCopy.listboxData->listLength = comboData->listboxData->listLength;
comboDataCopy.listboxData->multiSelect = comboData->listboxData->multiSelect;
comboDataCopy.listboxData->scrollBar = comboData->listboxData->scrollBar;
comboDataCopy.listboxData->columns = comboData->listboxData->columns;
comboDataCopy.listboxData->columnWidth = comboData->listboxData->columnWidth;
comboDataCopy.listboxData->columnWidthPercentage = comboData->listboxData->columnWidthPercentage;
comboDataCopy.entryData->alphaNumericalOnly = comboData->entryData->alphaNumericalOnly;
comboDataCopy.entryData->aSCIIOnly = comboData->entryData->aSCIIOnly;
comboDataCopy.entryData->maxTextLen = comboData->entryData->maxTextLen;
comboDataCopy.entryData->numericalOnly = comboData->entryData->numericalOnly;
duplicate =
TheWindowManager->gogoGadgetComboBox( parent,
status,
pos.x,
pos.y,
size.x,
size.y,
&instDataCopy,
&comboDataCopy,
source->winGetFont(),
FALSE );
//
// copy the color and image schemes of the sub controls that make
// up a listbox
//
if( duplicate )
{
ComboBoxData *comboData = (ComboBoxData *)duplicate->winGetUserData();
ComboBoxData *sourceComboData = (ComboBoxData *)source->winGetUserData();
WinInstanceData *instData;
WinInstanceData *sourceInstData;
// drop down button
GameWindow *dropDownButton = comboData->dropDownButton;
GameWindow *sourceDropDownButton = sourceComboData->dropDownButton;
if(dropDownButton && sourceDropDownButton)
{
instData = dropDownButton->winGetInstanceData();
sourceInstData = sourceDropDownButton->winGetInstanceData();
InstDrawCopy(instData,sourceInstData);
}
// edit box
GameWindow *editBox = comboData->editBox;
GameWindow *sourceEditBox = sourceComboData->editBox;
if(editBox && sourceEditBox)
{
instData = editBox->winGetInstanceData();
sourceInstData = sourceEditBox->winGetInstanceData();
InstDrawCopy(instData,sourceInstData);
}
// ListBox
GameWindow *listBox = comboData->listBox;
GameWindow *sourceListBox = sourceComboData->listBox;
if(listBox && sourceListBox)
{
instData = listBox->winGetInstanceData();
sourceInstData = sourceListBox->winGetInstanceData();
InstDrawCopy(instData,sourceInstData);
// up button
GameWindow *upButton = comboData->listboxData->upButton;
GameWindow *sourceUpButton = sourceComboData->listboxData->upButton;
if( upButton && sourceUpButton )
{
instData = upButton->winGetInstanceData();
sourceInstData = sourceUpButton->winGetInstanceData();
// do the copy of the colors for each state
memcpy( &instData->m_enabledDrawData,
&sourceInstData->m_enabledDrawData,
sizeof( WinDrawData ) * MAX_DRAW_DATA );
memcpy( &instData->m_disabledDrawData,
&sourceInstData->m_disabledDrawData,
sizeof( WinDrawData ) * MAX_DRAW_DATA );
memcpy( &instData->m_hiliteDrawData,
&sourceInstData->m_hiliteDrawData,
sizeof( WinDrawData ) * MAX_DRAW_DATA );
} // if
// down button
GameWindow *downButton = comboData->listboxData->downButton;
GameWindow *sourceDownButton = sourceComboData->listboxData->downButton;
if( downButton && sourceDownButton )
{
instData = downButton->winGetInstanceData();
sourceInstData = sourceDownButton->winGetInstanceData();
// do the copy of the colors for each state
memcpy( &instData->m_enabledDrawData,
&sourceInstData->m_enabledDrawData,
sizeof( WinDrawData ) * MAX_DRAW_DATA );
memcpy( &instData->m_disabledDrawData,
&sourceInstData->m_disabledDrawData,
sizeof( WinDrawData ) * MAX_DRAW_DATA );
memcpy( &instData->m_hiliteDrawData,
&sourceInstData->m_hiliteDrawData,
sizeof( WinDrawData ) * MAX_DRAW_DATA );
} // if
// slider
GameWindow *slider = comboData->listboxData->slider;
GameWindow *sourceSlider = sourceComboData->listboxData->slider;
if( slider && sourceSlider )
{
instData = slider->winGetInstanceData();
sourceInstData = sourceSlider->winGetInstanceData();
// do the copy of the colors for each state
memcpy( &instData->m_enabledDrawData,
&sourceInstData->m_enabledDrawData,
sizeof( WinDrawData ) * MAX_DRAW_DATA );
memcpy( &instData->m_disabledDrawData,
&sourceInstData->m_disabledDrawData,
sizeof( WinDrawData ) * MAX_DRAW_DATA );
memcpy( &instData->m_hiliteDrawData,
&sourceInstData->m_hiliteDrawData,
sizeof( WinDrawData ) * MAX_DRAW_DATA );
// do the slider thumb
GameWindow *thumb = GadgetSliderGetThumb( slider );
GameWindow *sourceThumb = GadgetSliderGetThumb( sourceSlider );
if( thumb && sourceThumb )
{
instData = thumb->winGetInstanceData();
sourceInstData = sourceThumb->winGetInstanceData();
// do the copy of the colors for each state
memcpy( &instData->m_enabledDrawData,
&sourceInstData->m_enabledDrawData,
sizeof( WinDrawData ) * MAX_DRAW_DATA );
memcpy( &instData->m_disabledDrawData,
&sourceInstData->m_disabledDrawData,
sizeof( WinDrawData ) * MAX_DRAW_DATA );
memcpy( &instData->m_hiliteDrawData,
&sourceInstData->m_hiliteDrawData,
sizeof( WinDrawData ) * MAX_DRAW_DATA );
} // end if
} // if
} // if
} // end if
} // end else if
else if( BitTest( style, GWS_SCROLL_LISTBOX ) )
{
ListboxData *listData = (ListboxData *)source->winGetUserData();
ListboxData listDataCopy;
memset( &listDataCopy, 0, sizeof( listDataCopy ) );
listDataCopy.autoPurge = listData->autoPurge;
listDataCopy.autoScroll = listData->autoScroll;
listDataCopy.scrollIfAtEnd = listData->scrollIfAtEnd;
listDataCopy.forceSelect = listData->forceSelect;
listDataCopy.listLength = listData->listLength;
listDataCopy.multiSelect = listData->multiSelect;
listDataCopy.scrollBar = listData->scrollBar;
listDataCopy.columns = listData->columns;
if(listData->columnWidth)
{
listDataCopy.columnWidth = new Int[listData->columns];
for(Int i = 0; i < listData->columns; i ++)
{
listDataCopy.columnWidth[i] = listData->columnWidth[i];
}
}
else
listDataCopy.columnWidth = NULL;
if(listData->columnWidthPercentage)
{
listDataCopy.columnWidthPercentage = new Int[listData->columns];
for(Int i = 0; i < listData->columns; i ++)
{
listDataCopy.columnWidthPercentage[i] = listData->columnWidthPercentage[i];
}
}
else
listDataCopy.columnWidthPercentage = NULL;
duplicate =
TheWindowManager->gogoGadgetListBox( parent,
status,
pos.x,
pos.y,
size.x,
size.y,
&instDataCopy,
&listDataCopy,
source->winGetFont(),
FALSE );
//
// copy the color and image schemes of the sub controls that make
// up a listbox
//
if( duplicate )
{
ListboxData *listData = (ListboxData *)duplicate->winGetUserData();
ListboxData *sourceListData = (ListboxData *)source->winGetUserData();
WinInstanceData *instData;
WinInstanceData *sourceInstData;
// up button
GameWindow *upButton = listData->upButton;
GameWindow *sourceUpButton = sourceListData->upButton;
if( upButton && sourceUpButton )
{
instData = upButton->winGetInstanceData();
sourceInstData = sourceUpButton->winGetInstanceData();
// do the copy of the colors for each state
memcpy( &instData->m_enabledDrawData,
&sourceInstData->m_enabledDrawData,
sizeof( WinDrawData ) * MAX_DRAW_DATA );
memcpy( &instData->m_disabledDrawData,
&sourceInstData->m_disabledDrawData,
sizeof( WinDrawData ) * MAX_DRAW_DATA );
memcpy( &instData->m_hiliteDrawData,
&sourceInstData->m_hiliteDrawData,
sizeof( WinDrawData ) * MAX_DRAW_DATA );
} // if
// down button
GameWindow *downButton = listData->downButton;
GameWindow *sourceDownButton = sourceListData->downButton;
if( downButton && sourceDownButton )
{
instData = downButton->winGetInstanceData();
sourceInstData = sourceDownButton->winGetInstanceData();
// do the copy of the colors for each state
memcpy( &instData->m_enabledDrawData,
&sourceInstData->m_enabledDrawData,
sizeof( WinDrawData ) * MAX_DRAW_DATA );
memcpy( &instData->m_disabledDrawData,
&sourceInstData->m_disabledDrawData,
sizeof( WinDrawData ) * MAX_DRAW_DATA );
memcpy( &instData->m_hiliteDrawData,
&sourceInstData->m_hiliteDrawData,
sizeof( WinDrawData ) * MAX_DRAW_DATA );
} // if
// slider
GameWindow *slider = listData->slider;
GameWindow *sourceSlider = sourceListData->slider;
if( slider && sourceSlider )
{
instData = slider->winGetInstanceData();
sourceInstData = sourceSlider->winGetInstanceData();
// do the copy of the colors for each state
memcpy( &instData->m_enabledDrawData,
&sourceInstData->m_enabledDrawData,
sizeof( WinDrawData ) * MAX_DRAW_DATA );
memcpy( &instData->m_disabledDrawData,
&sourceInstData->m_disabledDrawData,
sizeof( WinDrawData ) * MAX_DRAW_DATA );
memcpy( &instData->m_hiliteDrawData,
&sourceInstData->m_hiliteDrawData,
sizeof( WinDrawData ) * MAX_DRAW_DATA );
// do the slider thumb
GameWindow *thumb = GadgetSliderGetThumb( slider );
GameWindow *sourceThumb = GadgetSliderGetThumb( sourceSlider );
if( thumb && sourceThumb )
{
instData = thumb->winGetInstanceData();
sourceInstData = sourceThumb->winGetInstanceData();
// do the copy of the colors for each state
memcpy( &instData->m_enabledDrawData,
&sourceInstData->m_enabledDrawData,
sizeof( WinDrawData ) * MAX_DRAW_DATA );
memcpy( &instData->m_disabledDrawData,
&sourceInstData->m_disabledDrawData,
sizeof( WinDrawData ) * MAX_DRAW_DATA );
memcpy( &instData->m_hiliteDrawData,
&sourceInstData->m_hiliteDrawData,
sizeof( WinDrawData ) * MAX_DRAW_DATA );
} // end if
} // if
} // end if
} // end else if
else if( BitTest( style, GWS_ENTRY_FIELD ) )
{
EntryData *entryData = (EntryData *)source->winGetUserData();
EntryData entryDataCopy;
memset( &entryDataCopy, 0, sizeof( entryDataCopy ) );
entryDataCopy.alphaNumericalOnly = entryData->alphaNumericalOnly;
entryDataCopy.aSCIIOnly = entryData->aSCIIOnly;
entryDataCopy.maxTextLen = entryData->maxTextLen;
entryDataCopy.numericalOnly = entryData->numericalOnly;
entryDataCopy.secretText = entryData->secretText;
duplicate =
TheWindowManager->gogoGadgetTextEntry( parent,
status,
pos.x,
pos.y,
size.x,
size.y,
&instDataCopy,
&entryDataCopy,
source->winGetFont(),
FALSE );
} // end else if
else if( BitTest( style, GWS_STATIC_TEXT ) )
{
TextData *textData = (TextData *)source->winGetUserData();
TextData textDataCopy;
textDataCopy.centered = textData->centered;
duplicate =
TheWindowManager->gogoGadgetStaticText( parent,
status,
pos.x,
pos.y,
size.x,
size.y,
&instDataCopy,
&textDataCopy,
source->winGetFont(),
FALSE );
} // end else if
else if( BitTest( style, GWS_PROGRESS_BAR ) )
{
duplicate =
TheWindowManager->gogoGadgetProgressBar( parent,
status,
pos.x,
pos.y,
size.x,
size.y,
&instDataCopy,
source->winGetFont(),
FALSE );
} // end else if
else if( BitTest( style, GWS_USER_WINDOW ) )
{
// create plain ol generic window
duplicate = TheWindowManager->winCreate( parent,
status,
pos.x,
pos.y,
size.x,
size.y,
NULL,
&instDataCopy );
} // end else if
else
{
MessageBox( TheEditor->getWindowHandle(),
"Cannot duplicate window, undefined style.",
"Internal Error", MB_OK );
assert( 0 );
memset( &instDataCopy, 0, sizeof( instDataCopy ) ); // see comment below
return NULL;
} // end else
// sanity
if( duplicate == NULL )
{
MessageBox( TheEditor->getWindowHandle(), "Unable to duplicate window",
"Internal Error", MB_OK );
assert( 0 );
memset( &instDataCopy, 0, sizeof( instDataCopy ) ); // see comment below
return NULL;
} // end if
//
// since we're using the real window system here to create things, we
// want to immediately remove it from the real system list cause we're
// not adding windows to the layout, only duplicating windows in
// isolation. Note that child windows are linked to their parents
// but that is totally isolated in the parent so that's OK and
// necessary.
//
if( parent == NULL )
unlinkWindow( duplicate );
// copy edit data, only for the editor
if( duplicate )
{
GameWindowEditData *editData = duplicate->winGetEditData();
GameWindowEditData *sourceEditData = source->winGetEditData();
// set edit data to zero or copy from source
if( sourceEditData && editData )
*editData = *sourceEditData;
// memcpy( editData, sourceEditData, sizeof( GameWindowEditData ) );
} // end if
//
// duplicate all the children for the window, except if this window
// is a gadget, those windows may have children but all of that
// is already taken care of in the creation above ... a gadget should
// be considered a single atomic unit
//
if( TheEditor->windowIsGadget( duplicate ) == FALSE )
{
GameWindow *child;
for( child = source->winGetChild(); child; child = child->winGetNext() )
duplicateWindow( child, duplicate );
} // end if
//
// the inst data copy was literally a copy, now it's going to go out of
// scope and we DONT want any data to be freed that points back to the
// original window e got it from. So therefore we'll just blow it away
//
memset( &instDataCopy, 0, sizeof( instDataCopy ) );
// return the duped window
return duplicate;
} // end duplicateWindow
//-------------------------------------------------------------------------------------------------
/** Create a duplicate everything on the clipboard list and put it
* on the m_clipboardDup list for pasting */
//-------------------------------------------------------------------------------------------------
void GUIEditWindowManager::createClipboardDuplicate( void )
{
GameWindow *duplicate;
GameWindow *window;
GameWindow *lastWindow;
// find last window in clipboard
lastWindow = m_clipboard;
while( lastWindow && lastWindow->winGetNext() != NULL )
lastWindow = lastWindow->winGetNext();
//
// go through each window in the clipboard, note we're starting from
// the bottom and going to the front to preserve the same order
// as the clipboard
//
for( window = lastWindow; window; window = window->winGetPrev() )
{
// duplicate the window and all its children
duplicate = duplicateWindow( window, NULL );
// add duplicate to list
if( duplicate )
linkToClipboard( duplicate, &m_clipboardDup );
} // end for
} // end createClipboardDuplicate
//-------------------------------------------------------------------------------------------------
/** Make the 'target' a child of the 'parent' */
//-------------------------------------------------------------------------------------------------
void GUIEditWindowManager::makeChildOf( GameWindow *target,
GameWindow *parent )
{
// sanity
if( target == NULL )
return;
// get target parent
GameWindow *prevParent = target->winGetParent();
// check for no parent
if( parent == NULL )
{
// if target already has no parent nothing to do
if( prevParent == NULL )
return;
//
// we are removing a parent from the target and placing target
// at the top of the window list. Be sure to preserve the
// screen position as the new coords so it's in the same place
//
ICoord2D screenPos;
target->winGetScreenPosition( &screenPos.x, &screenPos.y );
unlinkChildWindow( target );
linkWindow( target );
target->winSetPosition( screenPos.x, screenPos.y );
return;
} // end if
// check to see if this is already out parent, nothing to do
if( prevParent == parent )
return;
// you cannot make a window a child of a gadget control
if( TheEditor->windowIsGadget( parent ) )
{
MessageBox( TheEditor->getWindowHandle(),
"You cannot give a gadget control a child.",
"Child Not Allowed",
MB_OK );
return;
} // end if
// get the target screen position before the move to child
ICoord2D screenPosBeforeMove;
target->winGetScreenPosition( &screenPosBeforeMove.x,
&screenPosBeforeMove.y );
// remove the target from the window or any child list
if( prevParent )
unlinkChildWindow( target );
else
unlinkWindow( target );
// add as child of 'parent'
addWindowToParent( target, parent );
// adjust the position so it's relative to the new parent
ICoord2D parentPos;
parent->winGetScreenPosition( &parentPos.x, &parentPos.y );
target->winSetPosition( screenPosBeforeMove.x - parentPos.x,
screenPosBeforeMove.y - parentPos.y );
// notify the hierarchy of the change
TheHierarchyView->moveWindowChildOf( target, parent );
//
// run through the move code to keep the child in a legal position
// inside the parent client area
//
ICoord2D origin, safeLoc;
target->winGetScreenPosition( &origin.x, &origin.y );
// kee the location legal
TheEditor->computeSafeLocation( target, origin.x, origin.y,
&safeLoc.x, &safeLoc.y );
// move the target
TheEditor->moveWindowTo( target, safeLoc.x, safeLoc.y );
} // end makeChildOf
//-------------------------------------------------------------------------------------------------
/** Move the 'windowToMove' to be just in front of the 'aheadOf' window
* in either the master window list or in the child list */
//-------------------------------------------------------------------------------------------------
void GUIEditWindowManager::moveAheadOf( GameWindow *windowToMove,
GameWindow *aheadOf )
{
// sanity
if( windowToMove == NULL || aheadOf == NULL || windowToMove == aheadOf )
return;
//
// get our screen position before the move ... we want to appear in the
// same place after we're moved
//
ICoord2D beforeMoveScreenPos;
windowToMove->winGetScreenPosition( &beforeMoveScreenPos.x,
&beforeMoveScreenPos.y );
// get the parent of that which we are moving
GameWindow *prevParent = windowToMove->winGetParent();
// take the window to move off the chain
if( prevParent )
{
ICoord2D pos;
// get he screen position of the window BEFORE we unlink it
windowToMove->winGetScreenPosition( &pos.x, &pos.y );
// take off the child list, this will remove our parent pointer as well
unlinkChildWindow( windowToMove );
//
// now that we're unlinked, set the position to be accurate with
// the screen position and no parent
//
windowToMove->winSetPosition( pos.x, pos.y );
} // end if
else
{
// just take off the main list
unlinkWindow( windowToMove );
} // end else
// insert the window at the specified location
insertWindowAheadOf( windowToMove, aheadOf );
//
// adjust our position to be in the same place in our new location
// in the chain ... if we gained a parent we have to offset by
// the difference between our parent screen and our screen positions.
// if we lost a parent our position must be set to the absolute
// screen position
//
GameWindow *newParent = windowToMove->winGetParent();
ICoord2D parentPos = { 0 }; // init to top left of screen
if( newParent )
newParent->winGetScreenPosition( &parentPos.x, &parentPos.y );
windowToMove->winSetPosition( beforeMoveScreenPos.x - parentPos.x,
beforeMoveScreenPos.y - parentPos.y );
// move the window hierarchy representation of this window
TheHierarchyView->moveWindowAheadOf( windowToMove, aheadOf );
//
// run through the move code to keep the child in a legal position
// inside the parent client area
//
ICoord2D origin, safeLoc;
windowToMove->winGetScreenPosition( &origin.x, &origin.y );
// kee the location legal
TheEditor->computeSafeLocation( windowToMove, origin.x, origin.y,
&safeLoc.x, &safeLoc.y );
// move the target
TheEditor->moveWindowTo( windowToMove, safeLoc.x, safeLoc.y );
} // end moveAheadOf