/* ** 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: EditWindow.cpp /////////////////////////////////////////////////////// //----------------------------------------------------------------------------- // // Westwood Studios Pacific. // // Confidential Information // Copyright (C) 2001 - All Rights Reserved // //----------------------------------------------------------------------------- // // Project: RTS3 // // File name: EditWindow.cpp // // Created: Colin Day, July 2001 // // Desc: Main edit window for the GUI editing tool // //----------------------------------------------------------------------------- /////////////////////////////////////////////////////////////////////////////// // SYSTEM INCLUDES //////////////////////////////////////////////////////////// #include #include // USER INCLUDES ////////////////////////////////////////////////////////////// #include "Common/Debug.h" #include "GameClient/Display.h" #include "GameClient/GameWindowManager.h" #include "W3DDevice/GameClient/W3DFileSystem.h" #include "Resource.h" #include "EditWindow.h" #include "GUIEdit.h" #include "WinMain.h" #include "HierarchyView.h" #include "Properties.h" #include "WW3D2/WW3D.h" #include "WW3D2/Render2D.h" // DEFINES //////////////////////////////////////////////////////////////////// // PRIVATE TYPES ////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // PRIVATE DATA /////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// Bool EditWindow::m_classRegistered = FALSE; ///< class registered flag char *EditWindow::m_className = "EditWindowClass"; ///< edit window class name /////////////////////////////////////////////////////////////////////////////// // PUBLIC DATA //////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// EditWindow *TheEditWindow = NULL; ///< edit window singleton /////////////////////////////////////////////////////////////////////////////// // PRIVATE PROTOTYPES ///////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // PRIVATE FUNCTIONS ////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // editProc =================================================================== /** Window procedure for the edit window */ //============================================================================= LRESULT CALLBACK EditWindow::editProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) { switch( message ) { // ------------------------------------------------------------------------ case WM_TIMER: { Int timerID = wParam; if( TheEditWindow ) { if( timerID == TIMER_EDIT_WINDOW_PULSE ) TheEditWindow->updatePulse(); } // end if return 0; } // end timer //------------------------------------------------------------------------- case WM_MOUSEMOVE: case WM_LBUTTONDOWN: case WM_LBUTTONUP: case WM_MBUTTONDOWN: case WM_MBUTTONUP: case WM_RBUTTONDOWN: case WM_RBUTTONUP: { TheEditWindow->mouseEvent( message, wParam, lParam ); return 0; } // end mouse events // ------------------------------------------------------------------------ case WM_COMMAND: { Int controlID = LOWORD( wParam ); // Int nofifyCode = HIWORD( wParam ); // HWND hWndControl = (HWND)lParam; switch( controlID ) { // -------------------------------------------------------------------- case POPUP_MENU_NEW_WINDOW: case POPUP_MENU_NEW_PUSH_BUTTON: case POPUP_MENU_NEW_RADIO_BUTTON: case POPUP_MENU_NEW_TAB_CONTROL: case POPUP_MENU_NEW_CHECK_BOX: case POPUP_MENU_NEW_LISTBOX: case POPUP_MENU_NEW_COMBO_BOX: case POPUP_MENU_NEW_HORIZONTAL_SLIDER: case POPUP_MENU_NEW_VERTICAL_SLIDER: case POPUP_MENU_NEW_PROGRESS_BAR: case POPUP_MENU_NEW_TEXT_ENTRY: case POPUP_MENU_NEW_STATIC_TEXT: case POPUP_MENU_PROPERTIES: case POPUP_MENU_DELETE: case POPUP_MENU_BRING_TO_TOP: { ICoord2D pos; GameWindow *window; // get position the menu was initiated at TheEditWindow->getPopupMenuClickPos( &pos ); // // if the position the user clicked at was on another window, // that window will become the parent of the new one // window = TheEditor->getWindowAtPos( pos.x, pos.y ); // create new window at that location switch( controlID ) { // ---------------------------------------------------------------- case POPUP_MENU_NEW_WINDOW: TheEditor->newWindow( GWS_USER_WINDOW, window, pos.x, pos.y, 15 * TheEditor->getGridResolution(), 15 * TheEditor->getGridResolution() ); break; // ---------------------------------------------------------------- case POPUP_MENU_NEW_PUSH_BUTTON: TheEditor->newWindow( GWS_PUSH_BUTTON, window, pos.x, pos.y, 15 * TheEditor->getGridResolution(), 3 * TheEditor->getGridResolution() ); break; // ---------------------------------------------------------------- case POPUP_MENU_NEW_CHECK_BOX: TheEditor->newWindow( GWS_CHECK_BOX, window, pos.x, pos.y, 15 * TheEditor->getGridResolution(), 3 * TheEditor->getGridResolution() ); break; // ---------------------------------------------------------------- case POPUP_MENU_NEW_RADIO_BUTTON: TheEditor->newWindow( GWS_RADIO_BUTTON, window, pos.x, pos.y, 15 * TheEditor->getGridResolution(), 3 * TheEditor->getGridResolution() ); break; // ---------------------------------------------------------------- case POPUP_MENU_NEW_TAB_CONTROL: TheEditor->newWindow( GWS_TAB_CONTROL, window, pos.x, pos.y, 45 * TheEditor->getGridResolution(), 30 * TheEditor->getGridResolution() ); break; // ---------------------------------------------------------------- case POPUP_MENU_NEW_LISTBOX: TheEditor->newWindow( GWS_SCROLL_LISTBOX, window, pos.x, pos.y, 20 * TheEditor->getGridResolution(), 20 * TheEditor->getGridResolution() ); break; // ---------------------------------------------------------------- case POPUP_MENU_NEW_COMBO_BOX: TheEditor->newWindow( GWS_COMBO_BOX, window, pos.x, pos.y, 15 * TheEditor->getGridResolution(), 3 * TheEditor->getGridResolution() ); break; // ---------------------------------------------------------------- case POPUP_MENU_NEW_HORIZONTAL_SLIDER: TheEditor->newWindow( GWS_HORZ_SLIDER, window, pos.x, pos.y, 20 * TheEditor->getGridResolution(), GADGET_SIZE ); break; // ---------------------------------------------------------------- case POPUP_MENU_NEW_VERTICAL_SLIDER: TheEditor->newWindow( GWS_VERT_SLIDER, window, pos.x, pos.y, GADGET_SIZE, 20 * TheEditor->getGridResolution() ); break; // ---------------------------------------------------------------- case POPUP_MENU_NEW_PROGRESS_BAR: TheEditor->newWindow( GWS_PROGRESS_BAR, window, pos.x, pos.y, 40 * TheEditor->getGridResolution(), GADGET_SIZE ); break; // ---------------------------------------------------------------- case POPUP_MENU_NEW_TEXT_ENTRY: TheEditor->newWindow( GWS_ENTRY_FIELD, window, pos.x, pos.y, 20 * TheEditor->getGridResolution(), 25 ); break; // ---------------------------------------------------------------- case POPUP_MENU_NEW_STATIC_TEXT: TheEditor->newWindow( GWS_STATIC_TEXT, window, pos.x, pos.y, 15 * TheEditor->getGridResolution(), 15 * TheEditor->getGridResolution() ); break; // ---------------------------------------------------------------- case POPUP_MENU_DELETE: TheEditor->deleteSelected(); break; // ---------------------------------------------------------------- case POPUP_MENU_BRING_TO_TOP: TheEditor->bringSelectedToTop(); break; // ---------------------------------------------------------------- case POPUP_MENU_PROPERTIES: if( window ) InitPropertiesDialog( window, pos.x, pos.y ); break; } // end switch break; } // end new window } // end switch on control id return 0; } // end command // ------------------------------------------------------------------------ default: { break; } // end default } // end switch( message ) return DefWindowProc( hWnd, message, wParam, lParam ); } // end editProc // EditWindow::registerEditWindowClass ======================================== /** Register a class with the windows OS for an edit window */ //============================================================================= void EditWindow::registerEditWindowClass( void ) { WNDCLASSEX wcex; ATOM atom; HINSTANCE hInst = TheEditor->getInstance(); wcex.cbSize = sizeof( WNDCLASSEX ); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = (WNDPROC)editProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInst; wcex.hIcon = LoadIcon( hInst, (LPCTSTR)IDI_GUIEDIT ); wcex.hCursor = NULL; //LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)GetStockObject( BLACK_BRUSH ); wcex.lpszMenuName = NULL; wcex.lpszClassName = m_className; wcex.hIconSm = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL); atom = RegisterClassEx( &wcex ); // if successfully registered we don't ever need to do this again if( atom != 0 ) m_classRegistered = TRUE; } // end registerEditWindowClass /////////////////////////////////////////////////////////////////////////////// // PUBLIC FUNCTIONS /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // EditWindow::EditWindow ===================================================== /** */ //============================================================================= EditWindow::EditWindow( void ) { m_pulse = 0; m_size.x = 0; m_size.y = 0; m_bitDepth = 32; m_editWindowHWnd = NULL; m_assetManager = NULL; m_2DRender = NULL; m_w3dInitialized = FALSE; m_popupMenuClickPos.x = 0; m_popupMenuClickPos.y = 0; m_pickedWindow = NULL; m_dragMoveOrigin.x = 0; m_dragMoveOrigin.y = 0; m_dragMoveDest.x = 0; m_dragMoveDest.y = 0; m_dragSelecting = FALSE; m_selectRegion.lo.x = 0; m_selectRegion.lo.y = 0; m_selectRegion.hi.x = 0; m_selectRegion.hi.y = 0; m_resizingWindow = FALSE; m_windowToResize = NULL; m_resizeOrigin.x = 0; m_resizeOrigin.y = 0; m_resizeDest.x = 0; m_resizeDest.y = 0; m_backgroundColor.red = 0.0f; m_backgroundColor.green = 0.3f; m_backgroundColor.blue = 0.3f; m_backgroundColor.alpha = 1.0f; m_clipRegion.lo.x = 0; m_clipRegion.lo.y = 0; m_clipRegion.hi.x = 0; m_clipRegion.hi.y = 0; m_isClippedEnabled = FALSE; } // end EditWindow // EditWindow::~EditWindow ==================================================== /** */ //============================================================================= EditWindow::~EditWindow( void ) { // call the shutdown shutdown(); } // end ~EditWindow // EditWindow::init =========================================================== /** Initialize the edit window */ //============================================================================= void EditWindow::init( UnsignedInt clientWidth, UnsignedInt clientHeight ) { UnsignedInt windowStyle = WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_BORDER | WS_CAPTION; UnsignedInt extendedWindowStyle = 0; ICoord2D size; // register an edit window class with windows if not already done if( m_classRegistered == FALSE ) registerEditWindowClass(); // create 2D renderer m_2DRender = new Render2DClass; // save width and height size.x = clientWidth; size.y = clientHeight; setSize( &size ); // // we want a client area the specified width and height, figure out how // big our window rectangle needs to be in order to have a client are // of that size // RECT clientRect; clientRect.left = 0; clientRect.top = 0; clientRect.right = m_size.x; clientRect.bottom = m_size.y; AdjustWindowRect( &clientRect, windowStyle, FALSE ); // create the window m_editWindowHWnd = CreateWindowEx( extendedWindowStyle, // extended window style m_className, // class name "Edit Window", // window name windowStyle, // style bits 0, // x location 0, // y location clientRect.right - clientRect.left, // width clientRect.bottom - clientRect.top, // height, TheEditor->getWindowHandle(), // parent NULL, // menu TheEditor->getInstance(), // instance NULL ); // creation parameters // display the window ShowWindow( m_editWindowHWnd, SW_SHOW ); // create the file system for our asset file locations TheW3DFileSystem = new W3DFileSystem; // our own file system for asset locations // initialize W3D WWMath::Init(); WW3D::Init( m_editWindowHWnd ); WW3D::Set_Screen_UV_Bias( TRUE ); ///< this makes text look good :) if( WW3D::Set_Render_Device( 0, m_size.x, m_size.y, m_bitDepth, TRUE ) != WW3D_ERROR_OK ) { assert( 0 ); shutdown(); return; } // end if // create asset manager m_assetManager = new WW3DAssetManager; assert( m_assetManager ); m_assetManager->Set_WW3D_Load_On_Demand( true ); // W3D is now initialized m_w3dInitialized = TRUE; // set a timer for updating visual pulse drawing SetTimer( m_editWindowHWnd, TIMER_EDIT_WINDOW_PULSE, 5, NULL ); } // end init // EditWindow::shutdown ======================================================= /** Shutdown edit window */ //============================================================================= void EditWindow::shutdown( void ) { // delete 2d renderer delete m_2DRender; m_2DRender = NULL; // delete asset manager m_assetManager->Free_Assets(); delete m_assetManager; // shutdown WW3D WW3D::Shutdown(); WWMath::Shutdown(); // delete the w3d file system delete TheW3DFileSystem; TheW3DFileSystem = NULL; // destroy the edit window if( m_editWindowHWnd ) DestroyWindow( m_editWindowHWnd ); m_editWindowHWnd = NULL; // unregister our edit window class UnregisterClass( m_className, TheEditor->getInstance() ); m_classRegistered = FALSE; } // EditWindowShutdown // EditWindow::updatePulse ==================================================== /** Update pulse from timer message */ //============================================================================= void EditWindow::updatePulse( void ) { static Bool dir = 1; static Int stepSize = 4; static Int pulseMax = 175, pulseMin = 75; // this is used for drawing pusling lines for moving stuff if( dir == 1 ) { m_pulse += stepSize; if( m_pulse >= pulseMax ) dir = 0; } // end if else { m_pulse -= stepSize; if( m_pulse <= pulseMin ) dir = 1; } // end else } // end updatePulse // EditWindow::mouseEvent ===================================================== /** A mouse event has occurred from our window procedure */ //============================================================================= void EditWindow::mouseEvent( UnsignedInt windowsMessage, WPARAM wParam, LPARAM lParam ) { Int x = LOWORD( lParam ); Int y = HIWORD( lParam ); Bool controlDown = BitTest( GetKeyState( VK_CONTROL ), 0x1000 ); ICoord2D mouse; // setup mouse in nice struct mouse.x = x; mouse.y = y; // for mouse move messges always update the status bar if( windowsMessage == WM_MOUSEMOVE ) { char buffer[ 64 ]; ICoord2D mousePrint; // only print mouse positions in the edit window mousePrint.x = x; mousePrint.y = y; if( mousePrint.x < 0 ) mousePrint.x = 0; if( mousePrint.x > m_size.x ) mousePrint.x = m_size.x; if( mousePrint.y < 0 ) mousePrint.y = 0; if( mousePrint.y > m_size.y ) mousePrint.y = m_size.y; sprintf( buffer, "Mouse Location (X = %d, Y = %d)", mousePrint.x, mousePrint.y ); TheEditor->statusMessage( STATUS_MOUSE_COORDS, buffer ); // keep focus in our app if( GetFocus() != TheEditor->getWindowHandle() ) SetFocus( TheEditor->getWindowHandle() ); } // end if // // if we're in test mode just pump all input through to the // window system so we can see how everything acts // if( TheEditor->getMode() == MODE_TEST_RUN ) { if( TheWin32Mouse ) TheWin32Mouse->addWin32Event( windowsMessage, wParam, lParam, NO_TIME_FROM_WINDOWS); return; } // end if // // If we're in the keyboard move, ignore the mouse // if (TheEditor->getMode() == MODE_KEYBOARD_MOVE) return; // do logic for each of the mouse messages switch( windowsMessage ) { // ------------------------------------------------------------------------ case WM_MOUSEMOVE: { // // this is really stupid, but the tree controls just don't // give enough hooks into all the events we need ... it's possible // to be in drag and drop mode due to menus opening up and the // user clicking on blank area etc. So if the mouse is in the // the edit window just make sure all our drag and drop stuff is // clear in the hierarchy // TheHierarchyView->setDragWindow( NULL ); TheHierarchyView->setDragTarget( NULL ); TheHierarchyView->setPopupTarget( NULL ); if( TheEditor->getMode() == MODE_DRAG_MOVE ) { // update destination for drag move m_dragMoveDest = mouse; } // end if else if( m_dragSelecting ) { // update drag selection region m_selectRegion.hi.x = x; m_selectRegion.hi.y = y; } // end else if else if( m_resizingWindow ) { // save the position of our mouse for resizing m_resizeDest = mouse; } // end else if else { // // if we have ONE window selected and are close to an anchor corner // to resize it change the cursor to resize cursor // TheEditWindow->handleResizeAvailable( x, y ); } // end else break; } // end mouse move // ------------------------------------------------------------------------ case WM_LBUTTONDOWN: { // // if we're in drag move mode ignore this command ... usually this // doesn't happen cause you usually get into drag move mode by pressing // down, holding down, and dragging ... but it's possible to move // windows via the hierarchy view for those hard to reach windows // if( TheEditor->getMode() == MODE_DRAG_MOVE && TheEditor->selectionCount() == 1 && TheHierarchyView->getPopupTarget() != NULL ) break; // // if we are in one of the resize modes then this click will // resize the window selected // if( TheEditor->getMode() == MODE_RESIZE_TOP_LEFT || TheEditor->getMode() == MODE_RESIZE_BOTTOM_RIGHT || TheEditor->getMode() == MODE_RESIZE_TOP_RIGHT || TheEditor->getMode() == MODE_RESIZE_BOTTOM_LEFT || TheEditor->getMode() == MODE_RESIZE_TOP || TheEditor->getMode() == MODE_RESIZE_BOTTOM || TheEditor->getMode() == MODE_RESIZE_RIGHT || TheEditor->getMode() == MODE_RESIZE_LEFT ) { // mouse movements will now resize the window in the selection list m_windowToResize = TheEditor->getFirstSelected(); if( m_windowToResize ) m_resizingWindow = TRUE; // save our mouse position for the resizing process m_resizeOrigin = mouse; m_resizeDest = mouse; } // end if else { GameWindow *window = TheEditor->getWindowAtPos( x, y ); if( window ) { // // if this window is not selected, then this window will become // the selected window instead of anything else that is selected. // if( TheEditor->isWindowSelected( window ) == FALSE ) { // // if control key is not down we clear selections, otherwise // we will add this one to the select list // if( controlDown == FALSE ) TheEditor->clearSelections(); // select this window TheEditor->selectWindow( window ); } // end if else { // // this window is selected, if control is down we will // unselect this window // if( controlDown == TRUE ) { TheEditor->unSelectWindow( window ); } // only proceed into drag mode if we have something selected else if( TheEditor->isWindowSelected( window ) ) { // set move locations m_dragMoveOrigin = mouse; m_dragMoveDest = mouse; // capture the mouse SetCapture( m_editWindowHWnd ); // change to drag move mode and switch cursor TheEditor->setMode( MODE_DRAG_MOVE ); } // end if } // end else } // end if else { // start a drag selection box m_dragSelecting = TRUE; m_selectRegion.lo.x = x; m_selectRegion.lo.y = y; m_selectRegion.hi.x = x; m_selectRegion.hi.y = y; } // end else } // end if break; } // end left button down // ------------------------------------------------------------------------ case WM_LBUTTONUP: { // exit drag move mode if( TheEditor->getMode() == MODE_DRAG_MOVE ) { // move the windows if ((m_dragMoveOrigin.x != m_dragMoveDest.x) || (m_dragMoveOrigin.y != m_dragMoveDest.y)) { TheEditor->dragMoveSelectedWindows( &m_dragMoveOrigin, &m_dragMoveDest ); } // release capture SetCapture( NULL ); // go back to normal mode TheEditor->setMode( MODE_EDIT ); } // end if else if( m_dragSelecting ) { // select the windows in the region TheEditor->selectWindowsInRegion( &m_selectRegion ); // stop a drag selection if in progress m_dragSelecting = FALSE; } // end else else if( m_resizingWindow ) { GameWindow *window = TheEditor->getFirstSelected(); DEBUG_ASSERTCRASH(window, ("No window selected for resize!")); if (window) { ICoord2D resultLoc, resultSize; ICoord2D dest = m_resizeDest; // adjust resize dest by the grid if it's on if( TheEditor->isGridSnapOn() ) TheEditor->gridSnapLocation( &dest, &dest ); // compute the location to resize it at TheEditor->computeResizeLocation( TheEditor->getMode(), window, &m_resizeOrigin, &dest, &resultLoc, &resultSize ); // move the window TheEditor->moveWindowTo( window, resultLoc.x, resultLoc.y ); // resize the window window->winSetSize( resultSize.x, resultSize.y ); } // go back to normal m_resizingWindow = FALSE; TheEditor->setMode( MODE_EDIT ); } // end resizing window break; } // end left button up // ------------------------------------------------------------------------ case WM_MBUTTONDOWN: { break; } // end middle button down // ------------------------------------------------------------------------ case WM_MBUTTONUP: { break; } // end middle button up // ------------------------------------------------------------------------ case WM_RBUTTONDOWN: { ICoord2D clickPos = mouse; GameWindow *window; // adjust the mouse pos if we're on a grid if( TheEditor->isGridSnapOn() ) TheEditor->gridSnapLocation( &mouse, &clickPos ); // get the window at the click pos window = TheEditor->getWindowAtPos( clickPos.x, clickPos.y ); // // if there is a window here and it is not part of the selection // list, everything else is unselected // if( window ) { if( TheEditor->isWindowSelected( window ) == FALSE ) TheEditor->clearSelections(); // select this window TheEditor->selectWindow( window ); } // end if else { // no window here, clear selections anyway TheEditor->clearSelections(); } // end else // open right click menu TheEditWindow->openPopupMenu( clickPos.x, clickPos.y ); break; } // end right button down // ------------------------------------------------------------------------ case WM_RBUTTONUP: { break; } // end right button up // ------------------------------------------------------------------------ default: { break; } // end default } // end switch on widnows message } // end mouseEvent // EditWindow::inCornerTolerance ============================================== /** If the 'dest' point is within 'tolerance' distance to the 'source' * point this returns TRUE */ //============================================================================= Bool EditWindow::inCornerTolerance( ICoord2D *dest, ICoord2D *source, Int tolerance ) { IRegion2D region; // sanity if( dest == NULL || source == NULL ) return FALSE; /// @todo we should write PointInRegion() stuff again like it was in Nox // build the region around the source point region.lo.x = source->x - tolerance; region.lo.y = source->y - tolerance; region.hi.x = source->x + tolerance; region.hi.y = source->y + tolerance; // check if in region if( dest->x >= region.lo.x && dest->x <= region.hi.x && dest->y >= region.lo.y && dest->y <= region.hi.y ) return TRUE; return FALSE; } // end inCornerTolerance // EditWindow::inLineTolerance ================================================ /** If the 'dest' point is within the region defined around the * line with th specified tolerance return TRUE */ //============================================================================= Bool EditWindow::inLineTolerance( ICoord2D *dest, ICoord2D *lineStart, ICoord2D *lineEnd, Int tolerance ) { IRegion2D region; // sanity if( dest == NULL || lineStart == NULL || lineEnd == NULL ) return FALSE; // setup region region.lo.x = lineStart->x - tolerance; region.lo.y = lineStart->y - tolerance; region.hi.x = lineEnd->x + tolerance; region.hi.y = lineEnd->y + tolerance; // check if in region if( dest->x >= region.lo.x && dest->x <= region.hi.x && dest->y >= region.lo.y && dest->y <= region.hi.y ) return TRUE; return FALSE; } // end inLineTolerance // EditWindow::handleResizeAvailable ========================================== /** Given the mouse position, if it is close enough to a corner of a * SINGLE selected window then we change the icon to the appropriate * resize icon. If the mouse is not moved from this position then * the next left click will allow for a resize of the window */ //============================================================================= void EditWindow::handleResizeAvailable( Int mouseX, Int mouseY ) { GameWindow *window; ICoord2D origin, size, mouse, point; const Int tol = 5; // if there is zero or more than 1 window selected we can't resize if( TheEditor->selectionCount() != 1 ) return; // get mouse data in a nice coord 2d mouse.x = mouseX; mouse.y = mouseY; // get the selected window data window = TheEditor->getFirstSelected(); window->winGetScreenPosition( &origin.x, &origin.y ); window->winGetSize( &size.x, &size.y ); // check for around top left corner point = origin; if( inCornerTolerance( &mouse, &point, tol ) == TRUE ) { TheEditor->setMode( MODE_RESIZE_TOP_LEFT ); return; } // end if // check for around bottom right corner point.x = origin.x + size.x; point.y = origin.y + size.y; if( inCornerTolerance( &mouse, &point, tol ) == TRUE ) { TheEditor->setMode( MODE_RESIZE_BOTTOM_RIGHT ); return; } // end if // check for around top right corner point.x = origin.x + size.x; point.y = origin.y; if( inCornerTolerance( &mouse, &point, tol ) == TRUE ) { TheEditor->setMode( MODE_RESIZE_TOP_RIGHT ); return; } // end if // check for around bottom left corner point.x = origin.x; point.y = origin.y + size.y; if( inCornerTolerance( &mouse, &point, tol ) == TRUE ) { TheEditor->setMode( MODE_RESIZE_BOTTOM_LEFT ); return; } // end if ICoord2D lineStart, lineEnd; // check for along top edge lineStart = origin; lineEnd.x = origin.x + size.x; lineEnd.y = origin.y; if( inLineTolerance( &mouse, &lineStart, &lineEnd, tol ) == TRUE ) { TheEditor->setMode( MODE_RESIZE_TOP ); return; } // end if // check for along bottom edge lineStart.x = origin.x; lineStart.y = origin.y + size.y; lineEnd.x = origin.x + size.x; lineEnd.y = origin.y + size.y; if( inLineTolerance( &mouse, &lineStart, &lineEnd, tol ) == TRUE ) { TheEditor->setMode( MODE_RESIZE_BOTTOM ); return; } // end if // check for along left edge lineStart = origin; lineEnd.x = origin.x; lineEnd.y = origin.y + size.y; if( inLineTolerance( &mouse, &lineStart, &lineEnd, tol ) == TRUE ) { TheEditor->setMode( MODE_RESIZE_LEFT ); return; } // end if // check for along right edge lineStart.x = origin.x + size.x; lineStart.y = origin.y; lineEnd.x = origin.x + size.x; lineEnd.y = origin.y + size.y; if( inLineTolerance( &mouse, &lineStart, &lineEnd, tol ) == TRUE ) { TheEditor->setMode( MODE_RESIZE_RIGHT ); return; } // end if // we are not resizing anything at all, set us to normal mode TheEditor->setMode( MODE_EDIT ); } // end handleResizeAvailable // EditWindow::drawSeeThruOutlines ============================================ /** Draw an outline for a window that is see thru so we can still work * with it in the editor */ //============================================================================= void EditWindow::drawSeeThruOutlines( GameWindow *windowList, Color c ) { // end recursion if( windowList == NULL ) return; // draw outline for this window if( BitTest( windowList->winGetStatus(), WIN_STATUS_SEE_THRU ) ) { ICoord2D pos; ICoord2D size; // get position and size windowList->winGetScreenPosition( &pos.x, &pos.y ); windowList->winGetSize( &size.x, &size.y ); // draw a box on the window drawOpenRect( pos.x, pos.y, size.x, size.y, 1, c ); } // end if // check window children GameWindow *child; for( child = windowList->winGetChild(); child; child = child->winGetNext() ) drawSeeThruOutlines( child, c ); // go to siblings drawSeeThruOutlines( windowList->winGetNext(), c ); } // end drawSeeThruOutlines // EditWindow::drawHiddenOutlines ============================================= /** Draw an outline for a window that is hidden so we can still work * with it in the editor */ //============================================================================= void EditWindow::drawHiddenOutlines( GameWindow *windowList, Color c ) { // end recursion if( windowList == NULL ) return; // // draw outline for this window, we are hidden if we have the hidden // status or any of our parents are hidden // Bool hidden = FALSE; GameWindow *parent= windowList->winGetParent(); while( parent ) { if( BitTest( parent->winGetStatus(), WIN_STATUS_HIDDEN ) ) hidden = TRUE; parent = parent->winGetParent(); } // end while if( BitTest( windowList->winGetStatus(), WIN_STATUS_HIDDEN ) ) hidden = TRUE; if( hidden ) { ICoord2D pos; ICoord2D size; // get position and size windowList->winGetScreenPosition( &pos.x, &pos.y ); windowList->winGetSize( &size.x, &size.y ); // draw a box on the window drawOpenRect( pos.x, pos.y, size.x, size.y, 2, c ); } // end if // check window children GameWindow *child; for( child = windowList->winGetChild(); child; child = child->winGetNext() ) drawHiddenOutlines( child, c ); // go to siblings drawHiddenOutlines( windowList->winGetNext(), c ); } // end drawHiddenOutlines // EditWindow::drawUIback ===================================================== /** Draw any visual feedback to the user about selection boxes or windows * that are selected, draggin windows etc */ //============================================================================= void EditWindow::drawUIFeedback( void ) { WindowSelectionEntry *select; Int color = m_pulse * 2; if( color > 255 ) color = 255; // draw hidden window outlines if requested if( TheEditor->getShowHiddenOutlines() ) drawHiddenOutlines( TheWindowManager->winGetWindowList(), GameMakeColor( color, 64, 64, 255 ) ); // draw see-thru window outlines if requested if( TheEditor->getShowSeeThruOutlines() ) drawSeeThruOutlines( TheWindowManager->winGetWindowList(), GameMakeColor( 64, 64, color, 255 ) ); // if the grid is visible draw it on top of everything if( TheEditor->isGridVisible() == TRUE ) drawGrid(); // draw drag selection box if( m_dragSelecting ) { Int width, height; Real selectBoxWidth = 2.0f; Color selectBoxColor = GameMakeColor( 0, 255, 0, 255 ); width = m_selectRegion.hi.x - m_selectRegion.lo.x; height = m_selectRegion.hi.y - m_selectRegion.lo.y; drawOpenRect( m_selectRegion.lo.x, m_selectRegion.lo.y, width, height, selectBoxWidth, selectBoxColor ); } // end if // draw select lines on selected windows select = TheEditor->getSelectList(); while( select ) { ICoord2D origin, size; GameWindow *window; Real windowSelectWidth = 2.0f; Color windowSelectColor; windowSelectColor = GameMakeColor( 0, color, 0, 255 ); // get window properties window = select->window; window->winGetScreenPosition( &origin.x, &origin.y ); window->winGetSize( &size.x, &size.y ); // draw a box on the window drawOpenRect( origin.x, origin.y, size.x, size.y, windowSelectWidth, windowSelectColor ); // go to next selection select = select->next; } // end while // // if we're drag moving, draw outlines of all the windows in the // select list offset by the start to end of the drag move // if( TheEditor->getMode() == MODE_DRAG_MOVE || TheEditor->getMode() == MODE_KEYBOARD_MOVE ) { ICoord2D origin, size; ICoord2D moveLoc, safeLoc; GameWindow *window, *parent; Real outlineWidth = 1.0f; Color outlineColor; // determine outline using pulse counter outlineColor = GameMakeColor( m_pulse, m_pulse, m_pulse + 25, 255 ); // traverse selection list select = TheEditor->getSelectList(); while( select ) { // get window data window = select->window; window->winGetScreenPosition( &origin.x, &origin.y ); window->winGetSize( &size.x, &size.y ); // figure out the destination of the window from this move ICoord2D change; change.x = (m_dragMoveDest.x - m_dragMoveOrigin.x); change.y = (m_dragMoveDest.y - m_dragMoveOrigin.y); // [SKB: Jun 02 2003 @ 2:7pm] : // Don't move the object unless we have moved the mouse, // this is to avoid the irritating movement of a window when just // clicking on a window. if (change.x || change.y) { moveLoc.x = origin.x + change.x; moveLoc.y = origin.y + change.y; // snap move location to grid if on if( (TheEditor->getMode() == MODE_DRAG_MOVE) && TheEditor->isGridSnapOn() ) TheEditor->gridSnapLocation( &moveLoc, &moveLoc ); // keep location legal TheEditor->computeSafeLocation( window, moveLoc.x, moveLoc.y, &safeLoc.x, &safeLoc.y ); // adjust location by parent location if present parent = window->winGetParent(); if( parent ) { ICoord2D parentOrigin; parent->winGetScreenPosition( &parentOrigin.x, &parentOrigin.y ); safeLoc.x += parentOrigin.x; safeLoc.y += parentOrigin.y; } // end if // draw outline of window at what would be the drag move destination drawOpenRect( safeLoc.x, safeLoc.y, size.x, size.y, outlineWidth, outlineColor ); } // go to next selection select = select->next; } // end while } // end if // if resizing a window draw that resize representation if( m_resizingWindow ) { GameWindow *window = m_windowToResize; ICoord2D loc, size; Color outlineColor; Real outlineWidth = 1.0f; GameWindow *parent; ICoord2D dest = m_resizeDest; // adjust resize dest by the grid if it's on if( TheEditor->isGridSnapOn() ) TheEditor->gridSnapLocation( &dest, &dest ); // // given the location that we started dragging at and the destination // drag location along with the mode of resizing compute what the // new location and the new size of the selected window to resize // TheEditor->computeResizeLocation( TheEditor->getMode(), window, &m_resizeOrigin, &dest, &loc, &size ); // adjust location by parent location if present parent = window->winGetParent(); if( parent ) { ICoord2D parentOrigin; parent->winGetScreenPosition( &parentOrigin.x, &parentOrigin.y ); loc.x += parentOrigin.x; loc.y += parentOrigin.y; } // end if // draw a box for the window at its new location outlineColor = GameMakeColor( m_pulse, m_pulse, m_pulse, 255 ); drawOpenRect( loc.x, loc.y, size.x, size.y, outlineWidth, outlineColor ); } // end if // draw lines around any drag source and drag targets in the hierarchy view GameWindow *dragSource = TheHierarchyView->getDragWindow(); Color dragColor = GameMakeColor( color, 0, color, 255 ); if( dragSource ) { ICoord2D origin, size; // get size and position dragSource->winGetScreenPosition( &origin.x, &origin.y ); dragSource->winGetSize( &size.x, &size.y ); // draw box drawOpenRect( origin.x, origin.y, size.x, size.y, 2, dragColor ); } // end if // drag target GameWindow *dragTarget = TheHierarchyView->getDragTarget(); if( dragTarget ) { ICoord2D origin, size; // get size and position dragTarget->winGetScreenPosition( &origin.x, &origin.y ); dragTarget->winGetSize( &size.x, &size.y ); // draw box drawOpenRect( origin.x, origin.y, size.x, size.y, 2, dragColor ); } // end if } // end drawUIFeedback // EditWindow::drawGrid ======================================================= /** Draw the grid */ //============================================================================= void EditWindow::drawGrid( void ) { // HDC hdc = GetDC( getWindowHandle() ); Int res = TheEditor->getGridResolution(); Int x, y; RGBColorInt *gridColor = TheEditor->getGridColor(); Color color = GameMakeColor( gridColor->red, gridColor->green, gridColor->blue, gridColor->alpha ); // set us to invert where we draw // SetROP2( hdc, R2_NOT ); // SelectObject( hdc, (HPEN)GetStockObject( WHITE_PEN ) ); for( y = 0; y < m_size.y; y += res ) { TheDisplay->drawLine( 0, y, m_size.x, y, 1, color ); // MoveToEx( hdc, 0, y, NULL ); // LineTo( hdc, m_size.x, y ); } for( x = 0; x < m_size.x; x += res ) { TheDisplay->drawLine( x, 0, x, m_size.y, 1, color ); // MoveToEx( hdc, x, 0, NULL ); // LineTo( hdc, x, m_size.y ); } /* for( y = 0; y < m_size.y; y += res ) { for( x = 0; x < m_size.x; x += res ) { MoveToEx( hdc, x, y, NULL ); LineTo( hdc, x + 1, y + 1 ); // TheDisplay->drawLine( x, y, x + 1, y + 1, 1, 0xFFFFFFFF ); } // end for x } // end for y */ // release the dc // ReleaseDC( getWindowHandle(), hdc ); } // end drawGrid // EditWindow::draw =========================================================== /** Draw the edit window */ //============================================================================= void EditWindow::draw( void ) { static UnsignedInt syncTime = 0; // allow W3D to update its internals WW3D::Sync( syncTime ); // for now, use constant time steps to avoid animations running independent of framerate syncTime += 50; // start render block WW3D::Begin_Render( true, true, Vector3( m_backgroundColor.red, m_backgroundColor.green, m_backgroundColor.blue ) ); // draw the windows TheWindowManager->winRepaint(); // draw selected drag box and any window selection boxes if( TheEditor->getMode() != MODE_TEST_RUN ) drawUIFeedback(); // render is all done! WW3D::End_Render(); } // end draw // EditWindow::setSize ======================================================== /** The edit window should now be logically consider this size */ //============================================================================= void EditWindow::setSize( ICoord2D *size ) { // save the size m_size = *size; // the display should reflect the new size as well if( TheDisplay ) { TheDisplay->setWidth( m_size.x ); TheDisplay->setHeight( m_size.y ); } // end if // set the extents for our 2D renderer if( m_2DRender ) m_2DRender->Set_Coordinate_Range( RectClass( 0, 0, m_size.x, m_size.y ) ); } // end setSize // EditWindow::openPopupMenu ================================================== /** Open the new control menu that comes up when the user right clicks * in the workspace to create new controls */ //============================================================================= void EditWindow::openPopupMenu( Int x, Int y ) { HMENU menu, subMenu; POINT screen; Int selectCount = TheEditor->selectionCount(); // get the menu with the new control items menu = LoadMenu( TheEditor->getInstance(), (LPCTSTR)POPUP_MENU ); subMenu = GetSubMenu( menu, 0 ); // enable/disable menu items if( selectCount == 0 ) { EnableMenuItem( subMenu, POPUP_MENU_DELETE, MF_GRAYED ); EnableMenuItem( subMenu, POPUP_MENU_PROPERTIES, MF_GRAYED ); EnableMenuItem( subMenu, POPUP_MENU_BRING_TO_TOP, MF_GRAYED ); } // end if else { EnableMenuItem( subMenu, POPUP_MENU_DELETE, MF_ENABLED ); EnableMenuItem( subMenu, POPUP_MENU_PROPERTIES, MF_ENABLED ); EnableMenuItem( subMenu, POPUP_MENU_BRING_TO_TOP, MF_ENABLED ); } // end else // // open up right mouse track menu, note that we have to translate the // x,y of this mouse click which is local to this window into // screen coordinates // screen.x = x; screen.y = y; ClientToScreen( m_editWindowHWnd, &screen ); TrackPopupMenuEx( subMenu, 0, screen.x, screen.y, m_editWindowHWnd, NULL ); // save the location click for the creation of the popup menu m_popupMenuClickPos.x = x; m_popupMenuClickPos.y = y; } // end openPopupMenu // EditWindow::drawLine ======================================================= /** draw a line on the display in pixel coordinates with the specified color */ //============================================================================= void EditWindow::drawLine( Int startX, Int startY, Int endX, Int endY, Real lineWidth, UnsignedInt lineColor ) { m_2DRender->Reset(); m_2DRender->Enable_Texturing( FALSE ); m_2DRender->Add_Line( Vector2( startX, startY ), Vector2( endX, endY ), lineWidth, lineColor ); m_2DRender->Render(); } // end drawLIne // EditWindow::drawOpenRect =================================================== /** draw a rect border on the display in pixel coordinates with the * specified color */ //============================================================================= void EditWindow::drawOpenRect( Int startX, Int startY, Int width, Int height, Real lineWidth, UnsignedInt lineColor ) { m_2DRender->Reset(); m_2DRender->Enable_Texturing( FALSE ); m_2DRender->Add_Outline( RectClass( startX, startY, startX + width, startY + height ), lineWidth, lineColor ); // render it now! m_2DRender->Render(); } // end drawOpenRect // EditWindow::drawFillRect =================================================== /** draw a filled rect on the display in pixel coords with the * specified color */ //============================================================================= void EditWindow::drawFillRect( Int startX, Int startY, Int width, Int height, UnsignedInt color ) { m_2DRender->Reset(); m_2DRender->Enable_Texturing( FALSE ); m_2DRender->Add_Rect( RectClass( startX, startY, startX + width, startY + height ), 0, 0, color ); // render it now! m_2DRender->Render(); } // end drawFillRect // EditWindow::drawImage ====================================================== /** draw an image fit within the screen coordinates */ //============================================================================= void EditWindow::drawImage( const Image *image, Int startX, Int startY, Int endX, Int endY, Color color ) { // sanity if( image == NULL ) return; const Region2D *uv = image->getUV(); m_2DRender->Reset(); m_2DRender->Enable_Texturing( TRUE ); m_2DRender->Set_Texture( image->getFilename().str() ); RectClass screen_rect(startX,startY,endX,endY); RectClass uv_rect(uv->lo.x,uv->lo.y,uv->hi.x,uv->hi.y); if (m_isClippedEnabled) { //need to clip this quad to clip rectangle // // Check for completely clipped // if ( endX <= m_clipRegion.lo.x || endY <= m_clipRegion.lo.y) { return; //nothing to render } else { // // Clip the polygons to the specified area // RectClass clipped_rect; clipped_rect.Left = __max (screen_rect.Left, m_clipRegion.lo.x); clipped_rect.Right = __min (screen_rect.Right, m_clipRegion.hi.x); clipped_rect.Top = __max (screen_rect.Top, m_clipRegion.lo.y); clipped_rect.Bottom = __min (screen_rect.Bottom, m_clipRegion.hi.y); // // Clip the texture to the specified area // RectClass clipped_uv_rect; float percent = ((clipped_rect.Left - screen_rect.Left) / screen_rect.Width ()); clipped_uv_rect.Left = uv_rect.Left + (uv_rect.Width () * percent); percent = ((clipped_rect.Right - screen_rect.Left) / screen_rect.Width ()); clipped_uv_rect.Right = uv_rect.Left + (uv_rect.Width () * percent); percent = ((clipped_rect.Top - screen_rect.Top) / screen_rect.Height ()); clipped_uv_rect.Top = uv_rect.Top + (uv_rect.Height () * percent); percent = ((clipped_rect.Bottom - screen_rect.Top) / screen_rect.Height ()); clipped_uv_rect.Bottom = uv_rect.Top + (uv_rect.Height () * percent); // // Use the clipped rectangles to render // screen_rect = clipped_rect; uv_rect = clipped_uv_rect; } } // if rotated 90 degrees clockwise we have to adjust the uv coords if( BitTest( image->getStatus(), IMAGE_STATUS_ROTATED_90_CLOCKWISE ) ) { m_2DRender->Add_Tri( Vector2( screen_rect.Left, screen_rect.Top ), Vector2( screen_rect.Left, screen_rect.Bottom ), Vector2( screen_rect.Right, screen_rect.Top ), Vector2( uv_rect.Right, uv_rect.Top), Vector2( uv_rect.Left, uv_rect.Top), Vector2( uv_rect.Right, uv_rect.Bottom ), color ); m_2DRender->Add_Tri( Vector2( screen_rect.Right, screen_rect.Bottom ), Vector2( screen_rect.Right, screen_rect.Top ), Vector2( screen_rect.Left, screen_rect.Bottom ), Vector2( uv_rect.Left, uv_rect.Bottom ), Vector2( uv_rect.Right, uv_rect.Bottom ), Vector2( uv_rect.Left, uv_rect.Top ), color ); } // end if else { // just draw as normal m_2DRender->Add_Quad( screen_rect, uv_rect, color ); } // end else m_2DRender->Render(); } // end drawImage // EditWindow::getBackgroundColor ============================================= /** Get the background color for the edit window */ //============================================================================= RGBColorReal EditWindow::getBackgroundColor( void ) { return m_backgroundColor; } // end getBackgroundColor // EditWindow::setBackgroundColor ============================================= /** Set the background color for the edit window */ //============================================================================= void EditWindow::setBackgroundColor( RGBColorReal color ) { m_backgroundColor = color; } // end setBackgroundColor // EditWindow::notifyWindowDeleted ============================================ /** A window has been deleted from the editor and the editor is now * notifying the edit window about it in case anything must be * cleaned up */ //============================================================================= void EditWindow::notifyWindowDeleted( GameWindow *window ) { // sanity if( window == NULL ) return; // check to see if the resizing window was deleted if( m_windowToResize == window ) { // get back to normal mode and clean up m_windowToResize = NULL; m_resizingWindow = FALSE; TheEditor->setMode( MODE_EDIT ); } // end if // null out picked window if needed if( m_pickedWindow == window ) m_pickedWindow = NULL; // // go back to edit mode, this keeps us from staying in a resize mode // after deleting the window under the cursor // TheEditor->setMode( MODE_EDIT ); } // end notifyWindowDeleted