/* ** 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: 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 #include #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