/*
** 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