/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
Doom 3 Source Code 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.
Doom 3 Source Code 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 Doom 3 Source Code. If not, see .
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "../../idlib/precompiled.h"
#pragma hdrstop
#include "../../sys/win32/rc/guied_resource.h"
#include "../../renderer/tr_local.h"
#include "../../sys/win32/win_local.h"
#include "../../ui/DeviceContext.h"
#include "../../ui/EditWindow.h"
#include "../../ui/ListWindow.h"
#include "../../ui/BindWindow.h"
#include "../../ui/RenderWindow.h"
#include "../../ui/ChoiceWindow.h"
#include "GEApp.h"
#include "GEItemPropsDlg.h"
#include "GEItemScriptsDlg.h"
// Modifiers
#include "GEModifierGroup.h"
#include "GEMoveModifier.h"
#include "GESizeModifier.h"
#include "GEStateModifier.h"
#include "GEZOrderModifier.h"
#include "GEInsertModifier.h"
#include "GEHideModifier.h"
#include "GEDeleteModifier.h"
static float g_ZoomScales[rvGEWorkspace::ZOOM_MAX] = { 0, 0.25f, 0.33f, 0.50f, 0.66f, 1.0f, 1.5f, 2.0f, 3.0f };
static const int ID_GUIED_SELECT_FIRST = 9800;
static const int ID_GUIED_SELECT_LAST = 9900;
idList rvGEWorkspace::mClipboard;
rvGEWorkspace::rvGEWorkspace ( rvGEApp* app ) : mApplication ( app )
{
mWnd = 0;
mInterface = 0;
mZoom = ZOOM_100;
mScrollHorz = false;
mScrollVert = false;
mModified = false;
mNew = false;
mDragScroll = false;
mSourceControlState = SCS_CHECKEDOUT;
mFilename = "guis/Untitled.gui";
mDragType = rvGESelectionMgr::HT_NONE;
mHandCursor = LoadCursor ( app->GetInstance(), MAKEINTRESOURCE(IDC_GUIED_HAND) );
mDontAdd = false;
mSelections.SetWorkspace ( this );
}
rvGEWorkspace::~rvGEWorkspace ( )
{
// Make sure all the wrappers get cleaned up
rvGEWindowWrapper::GetWrapper ( mInterface->GetDesktop ( ) )->EnumChildren ( CleanupEnumProc, NULL );
DestroyCursor ( mHandCursor );
delete mInterface;
}
/*
================
rvGEWorkspace::CleanupEnumProc
Window enumeration procedure that deletes all the wrapper classes
================
*/
bool rvGEWorkspace::CleanupEnumProc ( rvGEWindowWrapper* wrapper, void* data )
{
bool result;
if ( !wrapper )
{
return true;
}
result = wrapper->EnumChildren ( CleanupEnumProc, data );
// Cleanup the window wrapper
delete wrapper;
return result;
}
/*
================
rvGEWorkspace::GetZoomScale
Returns the scale of the current zoom level
================
*/
float rvGEWorkspace::GetZoomScale ( void )
{
return g_ZoomScales [ mZoom ];
}
/*
================
rvGEWorkspace::Attach
Attaches the workspace to the given window. This is usually done after the
window is created and the file has been loaded.
================
*/
bool rvGEWorkspace::Attach ( HWND wnd )
{
assert ( wnd );
mWnd = wnd;
// Initialize the pixel format for this window
SetupPixelFormat ( );
// Jam the workspace pointer into the userdata window long so
// we can retrieve the workspace from the window later
SetWindowLong ( mWnd, GWL_USERDATA, (LONG) this );
UpdateTitle ( );
return true;
}
/*
================
rvGEWorkspace::Detach
Detaches the workspace from the window it is currently attached to
================
*/
void rvGEWorkspace::Detach ( void )
{
assert ( mWnd );
SetWindowLong ( mWnd, GWL_USERDATA, 0 );
mWnd = NULL;
}
/*
================
rvGEWorkspace::SetupPixelFormat
Setup the pixel format for the opengl context
================
*/
bool rvGEWorkspace::SetupPixelFormat ( void )
{
HDC hDC = GetDC ( mWnd );
bool result = true;
int pixelFormat = ChoosePixelFormat(hDC, &win32.pfd);
if (pixelFormat > 0)
{
if (SetPixelFormat(hDC, pixelFormat, &win32.pfd) == NULL)
{
result = false;
}
}
else
{
result = false;
}
ReleaseDC ( mWnd, hDC );
return result;
}
/*
================
rvGEWorkspace::RenderGrid
Renders the grid on top of the user interface
================
*/
void rvGEWorkspace::RenderGrid ( void )
{
float x;
float y;
float step;
idVec4& color = mApplication->GetOptions().GetGridColor ( );
// See if the grid is off before rendering it
if ( !mApplication->GetOptions().GetGridVisible ( ))
{
return;
}
qglEnable(GL_BLEND);
qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
qglColor4f ( color[0], color[1], color[2], 0.5f );
qglBegin ( GL_LINES );
step = mApplication->GetOptions().GetGridWidth ( ) * g_ZoomScales[mZoom];
for ( x = mRect.x + mRect.w; x >= mRect.x ; x -= step )
{
qglVertex2f ( x, mRect.y );
qglVertex2f ( x, mRect.y + mRect.h );
}
step = mApplication->GetOptions().GetGridHeight ( ) * g_ZoomScales[mZoom];
for ( y = mRect.y + mRect.h; y >= mRect.y ; y -= step )
{
qglVertex2f ( mRect.x, y );
qglVertex2f ( mRect.x + mRect.w, y );
}
qglEnd ( );
qglDisable(GL_BLEND);
qglColor3f ( color[0], color[1], color[2] );
qglBegin ( GL_LINES );
step = mApplication->GetOptions().GetGridWidth ( ) * g_ZoomScales[mZoom];
for ( x = mRect.x + mRect.w; x >= mRect.x ; x -= step * 4 )
{
qglVertex2f ( x, mRect.y );
qglVertex2f ( x, mRect.y + mRect.h );
}
step = mApplication->GetOptions().GetGridHeight ( ) * g_ZoomScales[mZoom];
for ( y = mRect.y + mRect.h; y >= mRect.y ; y -= step * 4 )
{
qglVertex2f ( mRect.x, y );
qglVertex2f ( mRect.x + mRect.w, y );
}
qglEnd ( );
}
/*
================
rvGEWorkspace::Render
Renders the workspace to the given DC
================
*/
void rvGEWorkspace::Render ( HDC hdc )
{
int front;
int back;
float scale;
scale = g_ZoomScales[mZoom];
// Switch GL contexts to our dc
if (!qwglMakeCurrent( hdc, win32.hGLRC ))
{
common->Printf("ERROR: wglMakeCurrent failed.. Error:%i\n", qglGetError());
common->Printf("Please restart Q3Radiant if the Map view is not working\n");
return;
}
// Prepare the view and clear it
GL_State( GLS_DEFAULT );
qglViewport(0, 0, mWindowWidth, mWindowHeight );
qglScissor(0, 0, mWindowWidth, mWindowHeight );
qglClearColor ( 0.75f, 0.75f, 0.75f, 0 );
qglDisable(GL_DEPTH_TEST);
qglDisable(GL_CULL_FACE);
qglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Render the workspace below
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
qglOrtho(0,mWindowWidth, mWindowHeight, 0, -1, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
qglColor3f ( mApplication->GetOptions().GetWorkspaceColor()[0], mApplication->GetOptions().GetWorkspaceColor()[1], mApplication->GetOptions().GetWorkspaceColor()[2] );
qglBegin ( GL_QUADS );
qglVertex2f ( mRect.x, mRect.y );
qglVertex2f ( mRect.x + mRect.w, mRect.y );
qglVertex2f ( mRect.x + mRect.w, mRect.y + mRect.h );
qglVertex2f ( mRect.x, mRect.y + mRect.h );
qglEnd ( );
// Prepare the renderSystem view to draw the GUI in
viewDef_t viewDef;
memset ( &viewDef, 0, sizeof(viewDef) );
tr.viewDef = &viewDef;
tr.viewDef->renderView.x = mRect.x;
tr.viewDef->renderView.y = mWindowHeight - mRect.y - mRect.h;
tr.viewDef->renderView.width = mRect.w;
tr.viewDef->renderView.height = mRect.h;
tr.viewDef->scissor.x1 = 0;
tr.viewDef->scissor.y1 = 0;
tr.viewDef->scissor.x2 = mRect.w;
tr.viewDef->scissor.y2 = mRect.h;
tr.viewDef->isEditor = true;
renderSystem->BeginFrame(mWindowWidth, mWindowHeight );
// Draw the gui
mInterface->Redraw ( 0 ); // eventLoop->Milliseconds() );
// We are done using the renderSystem now
renderSystem->EndFrame( &front, &back );
if ( mApplication->GetActiveWorkspace ( ) == this )
{
mApplication->GetStatusBar().SetTriangles ( backEnd.pc.c_drawIndexes/3 );
}
// Prepare the viewport for drawing selections, etc.
GL_State( GLS_DEFAULT );
qglDisable( GL_TEXTURE_CUBE_MAP_EXT );
// qglDisable(GL_BLEND);
qglDisable(GL_CULL_FACE);
qglViewport(0, 0, mWindowWidth, mWindowHeight );
qglScissor(0, 0, mWindowWidth, mWindowHeight );
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
qglOrtho(0,mWindowWidth, mWindowHeight, 0, -1, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
RenderGrid ( );
mSelections.Render ( );
qglFinish ( );
qwglSwapBuffers(hdc);
qglEnable( GL_TEXTURE_CUBE_MAP_EXT );
qglEnable( GL_CULL_FACE);
}
/*
================
rvGEWorkspace::UpdateTitle
Updates the window title with the name of the file and the zoom level and weither its open or not
================
*/
void rvGEWorkspace::UpdateTitle ( void )
{
// Set the window title based on the current filename
SetWindowText ( mWnd, va("%s%s (%d%%)", idStr(mFilename).StripPath ( ).c_str( ), mModified?"*":"", (int) (g_ZoomScales[mZoom] * 100)) );
gApp.GetStatusBar().SetZoom ( (int)(g_ZoomScales[mZoom] * 100.0f) );
}
/*
================
rvGEWorkspace::UpdateRectangle
Updates the rectangle (not counting scrolling)
================
*/
void rvGEWorkspace::UpdateRectangle ( bool useScroll )
{
RECT rcClient;
float x;
float y;
float scale;
scale = g_ZoomScales[mZoom];
// Grab the current client rectangle of the window and cache off the width and height
GetClientRect ( mWnd, &rcClient );
mWindowWidth = rcClient.right - rcClient.left;
mWindowHeight = rcClient.bottom - rcClient.top;
// The workspace is always centered in the window
x = mRect.x = mWindowWidth / 2 - (SCREEN_WIDTH * scale) / 2;
y = mRect.y = mWindowHeight / 2 - (SCREEN_HEIGHT * scale) / 2;
mRect.w = (SCREEN_WIDTH * scale);
mRect.h = (SCREEN_HEIGHT * scale);
// When using the scroll position offset the rectangle based on the scrollbar positions
if ( useScroll )
{
// Adjust the start of the rectangle for the scroll positiond
mRect.y -= (float)GetScrollPos ( mWnd, SB_VERT ) / 1000.0f;
mRect.x -= (float)GetScrollPos ( mWnd, SB_HORZ ) / 1000.0f;
}
}
/*
================
rvGEWorkspace::Scroll
Adjusts the given scrollbar by the given offset
================
*/
void rvGEWorkspace::Scroll ( int scrollbar, int offset )
{
SCROLLINFO si;
if ( scrollbar == SB_HORZ && !mScrollHorz )
{
return;
}
else if ( scrollbar == SB_VERT && !mScrollVert )
{
return;
}
// Get all the vertial scroll bar information
si.cbSize = sizeof (si);
si.fMask = SIF_ALL;
// Save the position for comparison later on
GetScrollInfo ( mWnd, scrollbar, &si);
si.nPos += (1000 * offset);
if ( si.nPos < si.nMin ) si.nPos = si.nMin;
if ( si.nPos > si.nMax ) si.nPos = si.nMax;
si.fMask = SIF_POS;
SetScrollInfo (mWnd, scrollbar, &si, TRUE);
GetScrollInfo (mWnd, scrollbar, &si);
UpdateRectangle ( );
}
int rvGEWorkspace::HandleScroll ( int scrollbar, WPARAM wParam, LPARAM lParam )
{
SCROLLINFO si;
// Get all the vertial scroll bar information
si.cbSize = sizeof (si);
si.fMask = SIF_ALL;
// Save the position for comparison later on
GetScrollInfo ( mWnd, scrollbar, &si);
switch (LOWORD (wParam))
{
// user clicked left or up arrow
case SB_LINELEFT:
si.nPos -= 1000;
break;
// user clicked right or down arrow
case SB_LINERIGHT:
si.nPos += 1000;
break;
// user clicked shaft left of the scroll box
case SB_PAGELEFT:
si.nPos -= si.nPage;
break;
// user clicked shaft right of the scroll box
case SB_PAGERIGHT:
si.nPos += si.nPage;
break;
// user dragged the scroll box
case SB_THUMBTRACK:
si.nPos = si.nTrackPos;
break;
default :
break;
}
// Set the position and then retrieve it. Due to adjustments
// by Windows it may not be the same as the value set.
si.fMask = SIF_POS;
SetScrollInfo (mWnd, scrollbar, &si, TRUE);
GetScrollInfo (mWnd, scrollbar, &si);
UpdateRectangle ( );
return 0;
}
/*
================
rvGEWorkspace::UpdateScrollbars
Updates the states and the ranges of the scrollbars as well as the rectangle
================
*/
void rvGEWorkspace::UpdateScrollbars ( void )
{
SCROLLINFO info;
// First update the rectangle without applying scroll positions so
// we know the real sizes and coordinates
UpdateRectangle ( false );
// Setup the veritcal scrollbar
info.cbSize = sizeof(info);
info.fMask = SIF_RANGE|SIF_PAGE;
info.nMax = (mRect.h - mWindowHeight + 10) * 1000 / 2;
info.nMin = -info.nMax;
info.nPage = (int)((float)info.nMax * (float)((float)mWindowHeight / mRect.h));
info.nMax += info.nPage;
// If there is something to scroll then turn on the vertical scroll bar
// if its not on and update the scroll info.
if ( info.nMax > 0 )
{
if ( !mScrollVert )
{
mScrollVert = true;
ShowScrollBar ( mWnd, SB_VERT, mScrollVert );
}
SetScrollInfo ( mWnd, SB_VERT, &info, TRUE );
}
// Nothing to scroll, turn off the scrollbar if its on.
else if ( mScrollVert )
{
mScrollVert = false;
SetScrollPos ( mWnd, SB_VERT, 0, FALSE );
ShowScrollBar ( mWnd, SB_VERT, mScrollVert );
}
// Setup the horizontal scrollbar
info.nMax = (mRect.w - mWindowWidth + 10) * 1000 / 2;
info.nMin = -info.nMax;
info.nPage = (int)((float)info.nMax * (float)((float)mWindowWidth / mRect.w));
info.nMax += info.nPage;
// If there is something to scroll then turn on the vertical scroll bar
// if its not on and update the scroll info.
if ( info.nMax > 0 )
{
if ( !mScrollHorz )
{
mScrollHorz = true;
ShowScrollBar ( mWnd, SB_HORZ, mScrollHorz );
}
SetScrollInfo ( mWnd, SB_HORZ, &info, TRUE );
}
// Nothing to scroll, turn off the scrollbar if its on.
else if ( mScrollHorz )
{
mScrollHorz = false;
SetScrollPos ( mWnd, SB_HORZ, 0, FALSE );
ShowScrollBar ( mWnd, SB_HORZ, mScrollHorz );
}
// Need to update the rectangle again to take the scrollbar changes into account
UpdateRectangle ( true );
}
/*
================
rvGEWorkspace::UpdateCursor
Called to update the cursor when the mouse is within the workspace window
================
*/
void rvGEWorkspace::UpdateCursor ( rvGESelectionMgr::EHitTest type )
{
switch ( type )
{
case rvGESelectionMgr::HT_SELECT:
SetCursor ( LoadCursor ( NULL, MAKEINTRESOURCE(IDC_ARROW) ) );
break;
case rvGESelectionMgr::HT_MOVE:
SetCursor ( LoadCursor ( NULL, MAKEINTRESOURCE(IDC_SIZEALL) ) );
break;
case rvGESelectionMgr::HT_SIZE_LEFT:
case rvGESelectionMgr::HT_SIZE_RIGHT:
SetCursor ( LoadCursor ( NULL, MAKEINTRESOURCE(IDC_SIZEWE ) ) );
break;
case rvGESelectionMgr::HT_SIZE_TOP:
case rvGESelectionMgr::HT_SIZE_BOTTOM:
SetCursor ( LoadCursor ( NULL, MAKEINTRESOURCE(IDC_SIZENS ) ) );
break;
case rvGESelectionMgr::HT_SIZE_TOPRIGHT:
case rvGESelectionMgr::HT_SIZE_BOTTOMLEFT:
SetCursor ( LoadCursor ( NULL, MAKEINTRESOURCE(IDC_SIZENESW ) ) );
break;
case rvGESelectionMgr::HT_SIZE_BOTTOMRIGHT:
case rvGESelectionMgr::HT_SIZE_TOPLEFT:
SetCursor ( LoadCursor ( NULL, MAKEINTRESOURCE(IDC_SIZENWSE ) ) );
break;
}
}
void rvGEWorkspace::UpdateCursor ( float x, float y )
{
idVec2 point;
rvGESelectionMgr::EHitTest type;
// First convert the worspace coord to a window coord
point = WorkspaceToWindow ( idVec2( x, y ) );
// See if it hits anything
type = mSelections.HitTest ( point.x, point.y );
// If it hits something then use it to update the cursor
if ( rvGESelectionMgr::HT_NONE != type )
{
UpdateCursor ( type );
}
else
{
SetCursor ( LoadCursor ( NULL, MAKEINTRESOURCE(IDC_ARROW ) ) );
}
}
void rvGEWorkspace::UpdateCursor ( void )
{
if ( mDragType == rvGESelectionMgr::HT_NONE )
{
POINT point;
idVec2 cursor;
GetCursorPos ( &point );
cursor.Set ( point.x, point.y );
WindowToWorkspace ( cursor );
UpdateCursor ( cursor.x, cursor.y );
}
else
{
UpdateCursor ( mDragType );
}
}
/*
================
rvGEWorkspace::HandleMessage
Handles window messages to the workspace
================
*/
void rvGEWorkspace::HandleMessage ( UINT msg, WPARAM wParam, LPARAM lParam )
{
switch ( msg )
{
case WM_CLOSE:
{
if ( IsModified ( ) )
{
if ( IDYES == gApp.MessageBox ( va("Save changes to the document \"%s\" before closing?", GetFilename() ), MB_YESNO|MB_ICONQUESTION ) )
{
SendMessage ( mApplication->GetMDIFrame(), WM_COMMAND, MAKELONG(ID_GUIED_FILE_SAVE,0), 0 );
}
}
GetApplication ( )->GetNavigator().SetWorkspace(NULL);
GetApplication ( )->GetTransformer().SetWorkspace(NULL);
GetApplication ( )->GetProperties().SetWorkspace(NULL);
break;
}
case WM_CAPTURECHANGED:
if ( (HWND)lParam != mWnd )
{
mDragScroll = false;
mDragType = rvGESelectionMgr::HT_NONE;
}
break;
case WM_SETCURSOR:
{
POINT point;
idVec2 cursor;
GetCursorPos ( &point );
cursor.Set ( point.x, point.y );
WindowToWorkspace ( cursor );
if ( mDragType == rvGESelectionMgr::HT_NONE )
{
UpdateCursor ( cursor.x, cursor.y );
}
else
{
UpdateCursor ( mDragType );
}
break;
}
case WM_MOUSEWHEEL:
if ( (short)HIWORD(wParam) > 0 )
{
ZoomIn ( );
}
else if ( (short)HIWORD(wParam) < 0 )
{
ZoomOut ( );
}
break;
case WM_MOUSEMOVE:
HandleMouseMove ( wParam, lParam );
break;
case WM_MBUTTONDOWN:
HandleMButtonDown ( wParam, lParam );
break;
case WM_MBUTTONUP:
HandleMButtonUp ( wParam, lParam );
break;
case WM_LBUTTONDOWN:
HandleLButtonDown ( wParam, lParam );
break;
case WM_LBUTTONUP:
HandleLButtonUp ( wParam, lParam );
break;
case WM_LBUTTONDBLCLK:
HandleLButtonDblClk ( wParam, lParam );
break;
case WM_INITMENUPOPUP:
SendMessage ( mApplication->GetMDIFrame(), msg, wParam, lParam );
break;
case WM_COMMAND:
HandleCommand ( wParam, lParam );
break;
case WM_RBUTTONDOWN:
HandleRButtonDown ( wParam, lParam );
break;
case WM_SIZE:
UpdateScrollbars();
break;
case WM_VSCROLL:
HandleScroll ( SB_VERT, wParam, lParam );
break;
case WM_HSCROLL:
HandleScroll ( SB_HORZ, wParam, lParam );
break;
case WM_KEYDOWN:
HandleKeyDown ( wParam, lParam );
break;
}
}
/*
================
rvGEWorkspace::HandleCommand
Handles command messages destined for the workspace window. This is for
special workspace commands, any unhandled commands are forwarded to the main window
================
*/
int rvGEWorkspace::HandleCommand ( WPARAM wParam, LPARAM lParam )
{
// Select command
if ( LOWORD(wParam) >= ID_GUIED_SELECT_FIRST && LOWORD(wParam) <= ID_GUIED_SELECT_LAST )
{
idWindow* window = mSelectMenu[LOWORD(wParam)-ID_GUIED_SELECT_FIRST];
rvGEWindowWrapper* wrapper = rvGEWindowWrapper::GetWrapper ( window );
// Handle multi select as well
if ( GetAsyncKeyState ( VK_SHIFT ) & 0x8000 )
{
if ( wrapper->IsSelected ( ) )
{
mSelections.Remove ( window );
}
else
{
mSelections.Add ( window );
}
}
else
{
mSelections.Set ( window );
}
}
return SendMessage ( mApplication->GetMDIFrame(), WM_COMMAND, wParam, lParam );
}
/*
================
rvGEWorkspace::HandleMButtonDown
Handles the middle mouse down message in the workspace
================
*/
int rvGEWorkspace::HandleMButtonDown ( WPARAM wParam, LPARAM lParam )
{
if ( mDragType != rvGESelectionMgr::HT_NONE )
{
return 0;
}
mDragPoint.Set ( LOWORD(lParam), HIWORD(lParam) );
mDragScroll = true;
SetCursor ( mHandCursor );
SetCapture ( mWnd );
WindowToWorkspace ( mDragPoint );
return 0;
}
/*
================
rvGEWorkspace::HandleMButtonUp
Handles the middle mouse up message in the workspace
================
*/
int rvGEWorkspace::HandleMButtonUp ( WPARAM wParam, LPARAM lParam )
{
if ( mDragScroll )
{
mDragScroll = false;
ReleaseCapture ( );
}
return 0;
}
/*
================
rvGEWorkspace::HandleRButtonDown
Handles the left mouse down message in the workspace
================
*/
int rvGEWorkspace::HandleRButtonDown ( WPARAM wParam, LPARAM lParam )
{
POINT point = { LOWORD(lParam), HIWORD(lParam) };
HMENU menu;
// Add the select menu
mSelectMenu.Clear ( );
// Cache where the menu is being brought up so we can
// figure out which windows are under the point
mSelectMenuPos[0] = point.x;
mSelectMenuPos[1] = point.y;
WindowToWorkspace ( mSelectMenuPos );
// Build a list of all the windows under the menu point
rvGEWindowWrapper::GetWrapper ( mInterface->GetDesktop() )->EnumChildren ( BuildSelectMenuEnumProc, this );
// Add the desktop window always
mSelectMenu.Append ( mInterface->GetDesktop() );
//
menu = GetSubMenu ( LoadMenu ( mApplication->GetInstance(), MAKEINTRESOURCE(IDR_GUIED_ITEM_POPUP) ), 0 );
HMENU popup = CreatePopupMenu ( );
int i;
for ( i = 0; i < mSelectMenu.Num(); i ++ )
{
rvGEWindowWrapper* wrapper = rvGEWindowWrapper::GetWrapper ( mSelectMenu[i] );
AppendMenu ( popup, MF_STRING|MF_ENABLED|(wrapper->IsSelected()?MF_CHECKED:0), ID_GUIED_SELECT_FIRST + i, mSelectMenu[i]->GetName() );
}
InsertMenu ( menu, 1, MF_POPUP|MF_BYPOSITION, (LONG) popup, "Select" );
// Bring up the popup menu
ClientToScreen ( mWnd, &point );
TrackPopupMenu ( menu, TPM_RIGHTBUTTON|TPM_LEFTALIGN, point.x, point.y, 0, mWnd, NULL );
DestroyMenu ( popup );
DestroyMenu ( menu );
return 0;
}
/*
================
rvGEWorkspace::HandleLButtonDown
Handles the left mouse down message in the workspace
================
*/
int rvGEWorkspace::HandleLButtonDown ( WPARAM wParam, LPARAM lParam )
{
if ( mDragScroll )
{
return 0;
}
idVec2 point ( LOWORD(lParam), HIWORD(lParam) );
WindowToWorkspace ( point );
// Make sure whatever modifications get generated cant be merged into whats already there
mModifiers.BlockNextMerge ( );
mDragPoint.Set ( LOWORD(lParam), HIWORD(lParam) );
mDragTime = Sys_Milliseconds ( );
mDragX = true;
mDragY = true;
// If we have selections then start a drag
if ( mSelections.Num ( ) )
{
mDragType = mSelections.HitTest ( mDragPoint.x, mDragPoint.y );
}
rvGEWindowWrapper* wrapper;
wrapper = rvGEWindowWrapper::GetWrapper ( mInterface->GetDesktop ( ) );
idWindow* window = wrapper->WindowFromPoint ( point.x, point.y );
// dissallow selection of the desktop.
if ( gApp.GetOptions().GetIgnoreDesktopSelect() && window == mInterface->GetDesktop ( ) )
{
window = NULL;
}
if ( mDragType == rvGESelectionMgr::HT_MOVE || mDragType == rvGESelectionMgr::HT_NONE )
{
if ( window )
{
bool selected;
selected = mSelections.IsSelected ( window );
if ( GetAsyncKeyState ( VK_SHIFT ) & 0x8000 )
{
if ( !selected )
{
mSelections.Add ( window );
mDragType = rvGESelectionMgr::HT_MOVE;
}
else
{
mSelections.Remove ( window );
}
}
else if ( !selected && mDragType == rvGESelectionMgr::HT_NONE )
{
mSelections.Set ( window );
mDragType = rvGESelectionMgr::HT_MOVE;
}
}
else
{
mSelections.Clear ( );
}
}
if ( mSelections.IsExpression ( ) )
{
mDragType = rvGESelectionMgr::HT_SELECT;
}
// Windows capture
else if ( mDragType != rvGESelectionMgr::HT_NONE )
{
SetCapture ( mWnd );
}
WindowToWorkspace ( mDragPoint );
return 0;
}
/*
================
rvGEWorkspace::HandleLButtonUp
Handles the left mouse up message in the workspace
================
*/
int rvGEWorkspace::HandleLButtonUp ( WPARAM wParam, LPARAM lParam )
{
if ( mDragType != rvGESelectionMgr::HT_NONE )
{
ReleaseCapture ( );
mModifiers.BlockNextMerge ( );
// Update the transformer
mApplication->GetTransformer().Update ( );
}
// No more dragging
mDragType = rvGESelectionMgr::HT_NONE;
return 0;
}
/*
================
rvGEWorkspace::HandleLButtonDblClk
Handle a double click by opening properties
================
*/
int rvGEWorkspace::HandleLButtonDblClk ( WPARAM wParam, LPARAM lParam )
{
EditSelectedProperties ( );
return 0;
}
/*
================
rvGEWorkspace::HandleMouseMove
Handles the moving of the mouse for dragging and cursor updating
================
*/
int rvGEWorkspace::HandleMouseMove ( WPARAM wParam, LPARAM lParam )
{
idVec2 cursor;
cursor.Set ( (short)LOWORD(lParam), (short)HIWORD(lParam) );
// Convert the window point to the workspace before updating the
// cursor with the position
WindowToWorkspace ( cursor );
// Scrolling the window around
if ( mDragScroll )
{
Scroll ( SB_HORZ, mDragPoint.x - cursor.x );
Scroll ( SB_VERT, mDragPoint.y - cursor.y );
SetCursor ( mHandCursor );
mDragPoint = cursor;
return 0;
}
// If not dragging then just update the cursor and return
if ( mDragType == rvGESelectionMgr::HT_NONE )
{
UpdateCursor ( cursor.x, cursor.y );
return 0;
}
// Dont allow a drag move start until the button has been down for 100 ms or so
if ( mDragType == rvGESelectionMgr::HT_MOVE && Sys_Milliseconds() - mDragTime <= 50 )
{
return 0;
}
// Handle grid snapping
if ( gApp.GetOptions().GetGridSnap ( ) )
{
cursor.x = (float)(((int)cursor.x + gApp.GetOptions().GetGridWidth()/2) / gApp.GetOptions().GetGridWidth() * gApp.GetOptions().GetGridWidth());
cursor.y = (float)(((int)cursor.y + gApp.GetOptions().GetGridWidth()/2) / gApp.GetOptions().GetGridWidth() * gApp.GetOptions().GetGridWidth());
}
// If the cursor hasnt moved then there is nothing to update with the drag
if ( (int) cursor.x == (int) mDragPoint.x && (int) cursor.y == (int) mDragPoint.y )
{
return 0;
}
switch ( mDragType )
{
case rvGESelectionMgr::HT_MOVE:
AddModifierMove ( "Move", cursor.x - mDragPoint.x, cursor.y - mDragPoint.y, mApplication->GetOptions().GetGridSnap ( ) );
break;
case rvGESelectionMgr::HT_SIZE_BOTTOM:
AddModifierSize ( "Size", 0, 0, 0, cursor.y - mDragPoint.y, mApplication->GetOptions().GetGridSnap ( ) );
break;
case rvGESelectionMgr::HT_SIZE_TOP:
AddModifierSize ( "Size", 0, cursor.y - mDragPoint.y, 0, 0, mApplication->GetOptions().GetGridSnap ( ) );
break;
case rvGESelectionMgr::HT_SIZE_RIGHT:
AddModifierSize ( "Size", 0, 0, cursor.x - mDragPoint.x, 0, mApplication->GetOptions().GetGridSnap ( ) );
break;
case rvGESelectionMgr::HT_SIZE_LEFT:
AddModifierSize ( "Size", cursor.x - mDragPoint.x, 0, 0, 0, mApplication->GetOptions().GetGridSnap ( ) );
break;
case rvGESelectionMgr::HT_SIZE_TOPLEFT:
AddModifierSize ( "Size", cursor.x - mDragPoint.x, cursor.y - mDragPoint.y, 0, 0, mApplication->GetOptions().GetGridSnap ( ) );
break;
case rvGESelectionMgr::HT_SIZE_TOPRIGHT:
AddModifierSize ( "Size", 0, cursor.y - mDragPoint.y, cursor.x - mDragPoint.x, 0, mApplication->GetOptions().GetGridSnap ( ) );
break;
case rvGESelectionMgr::HT_SIZE_BOTTOMLEFT:
AddModifierSize ( "Size", cursor.x - mDragPoint.x, 0, 0, cursor.y - mDragPoint.y, mApplication->GetOptions().GetGridSnap ( ) );
break;
case rvGESelectionMgr::HT_SIZE_BOTTOMRIGHT:
AddModifierSize ( "Size", 0, 0, cursor.x - mDragPoint.x, cursor.y - mDragPoint.y, mApplication->GetOptions().GetGridSnap ( ) );
break;
}
UpdateCursor ( mDragType );
// If the x coordinate has changed then update it
if ( (int)cursor.x != (int)mDragPoint.x && mDragX )
{
mDragPoint.x = cursor.x;
}
// If the y coordinate has changed then update it
if ( (int)cursor.y != (int)mDragPoint.y && mDragY )
{
mDragPoint.y = cursor.y;
}
return 0;
}
/*
================
rvGEWorkspace::HandleKeyDown
Handles the the pressing of a key
================
*/
int rvGEWorkspace::HandleKeyDown ( WPARAM wParam, LPARAM lParam )
{
bool shift = (GetAsyncKeyState ( VK_SHIFT ) & 0x8000) ? true : false;
switch ( wParam )
{
case VK_LEFT:
if ( shift )
{
AddModifierSizeNudge ( -1, 0, false );
}
else
{
AddModifierMoveNudge ( -1, 0, false );
}
break;
case VK_RIGHT:
if ( shift )
{
AddModifierSizeNudge ( 1, 0, false );
}
else
{
AddModifierMoveNudge ( 1, 0, false );
}
break;
case VK_DOWN:
if ( shift )
{
AddModifierSizeNudge ( 0, 1, false );
}
else
{
AddModifierMoveNudge ( 0, 1, false );
}
break;
case VK_UP:
if ( shift )
{
AddModifierSizeNudge ( 0, -1, false );
}
else
{
AddModifierMoveNudge ( 0, -1, false );
}
break;
case VK_ESCAPE:
mSelections.Clear ( );
mApplication->GetNavigator().Update ( );
break;
}
return 0;
}
/*
================
rvGEWorkspace::WindowToWorkspace
Converts the given coordinates in windows space to the workspace's coordinates.
================
*/
idVec2& rvGEWorkspace::WindowToWorkspace ( idVec2& point )
{
point.x = (point.x - mRect.x) / mRect.w * SCREEN_WIDTH;
point.y = (point.y - mRect.y) / mRect.h * SCREEN_HEIGHT;
return point;
}
idRectangle& rvGEWorkspace::WindowToWorkspace ( idRectangle& rect )
{
rect.x = (rect.x - mRect.x) / mRect.w * SCREEN_WIDTH;
rect.y = (rect.y - mRect.y) / mRect.h * SCREEN_HEIGHT;
rect.w = rect.w / mRect.w * SCREEN_WIDTH;
rect.h = rect.h / mRect.h * SCREEN_HEIGHT;
return rect;
}
/*
================
rvGEWorkspace::WindowToWorkspace
Converts the given workspace coordinates to the windows coordinates.
================
*/
idVec2& rvGEWorkspace::WorkspaceToWindow ( idVec2& point )
{
point.x = mRect.x + (point.x / SCREEN_WIDTH * mRect.w);
point.y = mRect.y + (point.y / SCREEN_HEIGHT * mRect.h);
return point;
}
idRectangle& rvGEWorkspace::WorkspaceToWindow ( idRectangle& rect )
{
rect.x = mRect.x + (rect.x / SCREEN_WIDTH * mRect.w);
rect.y = mRect.y + (rect.y / SCREEN_HEIGHT * mRect.h);
rect.w = rect.w / SCREEN_WIDTH * mRect.w;
rect.h = rect.h / SCREEN_HEIGHT * mRect.h;
return rect;
}
/*
================
rvGEWorkspace::ZoomIn
Zooms the workspace in by one zoom level
================
*/
rvGEWorkspace::EZoomLevel rvGEWorkspace::ZoomIn ( void )
{
mZoom = mZoom + 1;
if ( mZoom >= ZOOM_MAX )
{
mZoom = ZOOM_MAX - 1;
}
UpdateScrollbars ( );
UpdateTitle ( );
InvalidateRect ( mWnd, NULL, FALSE );
return (EZoomLevel)mZoom;
}
/*
================
rvGEWorkspace::ZoomOut
Zooms the workspace out by one level
================
*/
rvGEWorkspace::EZoomLevel rvGEWorkspace::ZoomOut ( void )
{
mZoom--;
if ( mZoom <= ZOOM_MIN )
{
mZoom = ZOOM_MIN + 1;
}
UpdateScrollbars ( );
UpdateTitle ( );
InvalidateRect ( mWnd, NULL, FALSE );
return (EZoomLevel)mZoom;
}
/*
================
rvGEWorkspace::CreateModifier
Creates a new modifier of the given type for the given window. This function is called
specifically from the add modifiers function with the variable args list forwarded.
================
*/
rvGEModifier* rvGEWorkspace::CreateModifier ( EModifierType type, idWindow* window, va_list args )
{
rvGEModifier* mod;
switch ( type )
{
case MOD_DELETE:
mod = new rvGEDeleteModifier ( "Delete", window );
break;
case MOD_HIDE:
mod = new rvGEHideModifier ( "Hide", window, true );
break;
case MOD_UNHIDE:
mod = new rvGEHideModifier ( "Hide", window, false );
break;
case MOD_SEND_BACKWARD:
mod = new rvGEZOrderModifier ( "Send Backward", window, rvGEZOrderModifier::ZO_BACKWARD );
break;
case MOD_SEND_BACK:
mod = new rvGEZOrderModifier ( "Send to Back", window, rvGEZOrderModifier::ZO_BACK );
break;
case MOD_BRING_FORWARD:
mod = new rvGEZOrderModifier ( "Bring Forward", window, rvGEZOrderModifier::ZO_FORWARD );
break;
case MOD_BRING_FRONT:
mod = new rvGEZOrderModifier ( "Bring to Front", window, rvGEZOrderModifier::ZO_FRONT );
break;
default:
mod = NULL;
break;
}
return mod;
}
/*
================
rvGEWorkspace::AddModifiers
Add the specific modifier for the given window
================
*/
void rvGEWorkspace::AddModifiers ( idWindow* window, EModifierType type, ... )
{
va_list args;
va_start(args,type) ;
mModifiers.Append ( CreateModifier ( type, window, args ) );
va_end (args) ;
SetModified ( true );
}
void rvGEWorkspace::AddModifiers ( EModifierType type, ... )
{
va_list args;
// Nothing to move if there is no selection
if ( !mSelections.Num ( ) )
{
return;
}
// More than one selection requires a modifier group
else if ( mSelections.Num ( ) > 1 )
{
rvGEModifierGroup* group = new rvGEModifierGroup;
int i;
for ( i = 0; i < mSelections.Num(); i ++ )
{
va_start(args,type);
group->Append ( CreateModifier ( type, mSelections[i], args ) );
va_end (args);
}
mModifiers.Append ( group );
}
// Single modifier
else
{
va_start(args,type) ;
mModifiers.Append ( CreateModifier ( type, mSelections[0], args ) );
va_end (args) ;
}
SetModified ( true );
}
bool rvGEWorkspace::BuildSelectMenuEnumProc ( rvGEWindowWrapper* wrapper, void* data )
{
rvGEWorkspace* workspace;
workspace = (rvGEWorkspace*) data;
assert ( workspace );
if ( !wrapper )
{
return true;
}
wrapper->EnumChildren ( BuildSelectMenuEnumProc, data );
if ( wrapper->IsDeleted ( ) || wrapper->IsHidden ( ) )
{
return true;
}
if ( wrapper->GetScreenRect ( ).Contains ( workspace->mSelectMenuPos[0], workspace->mSelectMenuPos[1] ) )
{
workspace->mSelectMenu.Append ( wrapper->GetWindow ( ));
}
return true;
}
bool rvGEWorkspace::ShowAllEnumProc ( rvGEWindowWrapper* wrapper, void* data )
{
rvGEModifierGroup* group = (rvGEModifierGroup*) data;
wrapper->EnumChildren ( ShowAllEnumProc, data );
if ( wrapper->IsHidden ( ) )
{
group->Append ( new rvGEHideModifier ( "Show Hidden", wrapper->GetWindow ( ), false ) );
}
return true;
}
void rvGEWorkspace::AddModifierShowAll ( void )
{
rvGEModifierGroup* group = new rvGEModifierGroup;
rvGEWindowWrapper::GetWrapper( mInterface->GetDesktop ( ) )->EnumChildren ( ShowAllEnumProc, group );
if ( !group->GetCount ( ) )
{
delete group;
}
else
{
mModifiers.Append ( group );
}
mApplication->GetNavigator().Refresh ( );
}
void rvGEWorkspace::DeleteSelected ( void )
{
AddModifiers ( MOD_DELETE );
mSelections.Clear ( );
mApplication->GetNavigator().Update ( );
}
/*
================
rvGEWorkspace::NewWindow
Create a new window
================
*/
idWindow* rvGEWorkspace::NewWindow ( idDict* state, rvGEWindowWrapper::EWindowType type )
{
idWindow* window = new idWindow ( mInterface->GetDesktop()->GetDC(), mInterface );
rvGEWindowWrapper* wrapper;
int count;
idStr baseName;
switch ( type )
{
case rvGEWindowWrapper::WT_NORMAL:
window = new idWindow ( mInterface->GetDesktop()->GetDC(), mInterface );
break;
case rvGEWindowWrapper::WT_BIND:
window = new idBindWindow ( mInterface->GetDesktop()->GetDC(), mInterface );
break;
case rvGEWindowWrapper::WT_RENDER:
window = new idRenderWindow ( mInterface->GetDesktop()->GetDC(), mInterface );
break;
case rvGEWindowWrapper::WT_CHOICE:
window = new idChoiceWindow ( mInterface->GetDesktop()->GetDC(), mInterface );
break;
case rvGEWindowWrapper::WT_EDIT:
window = new idEditWindow ( mInterface->GetDesktop()->GetDC(), mInterface );
break;
default:
assert ( false );
return NULL;
}
baseName = state ? state->GetString("name","unnamed") : "unnamed";
baseName.StripQuotes ( );
count = 0;
if ( mInterface->GetDesktop()->FindChildByName ( baseName ) )
{
count = 1;
while ( 1 )
{
drawWin_t* dw = mInterface->GetDesktop()->FindChildByName ( va("%s%d",baseName.c_str(),count) );
if ( !dw )
{
break;
}
assert ( dw->win );
wrapper = rvGEWindowWrapper::GetWrapper ( dw->win );
if ( wrapper && wrapper->IsDeleted ( ) )
{
break;
}
count++;
}
}
idStr winName;
idStr winTemplate;
if ( count )
{
winName = va("%s%d", baseName.c_str(), count );
}
else
{
winName = baseName;
}
winTemplate = winName + " { }";
idParser src( winTemplate, winTemplate.Length(), "", LEXFL_ALLOWMULTICHARLITERALS | LEXFL_NOSTRINGCONCAT | LEXFL_ALLOWBACKSLASHSTRINGCONCAT );
window->Parse ( &src );
wrapper = rvGEWindowWrapper::GetWrapper ( window );
if ( state )
{
wrapper->SetState ( *state );
}
wrapper->SetStateKey ( "name", winName );
wrapper->Finish ( );
SetModified ( true );
return window;
}
idWindow* rvGEWorkspace::AddWindow ( rvGEWindowWrapper::EWindowType type )
{
idWindow* window;
idDict state;
state.Set ( "rect", "0,0,100,100" );
state.Set ( "visible", "1" );
window = NewWindow ( &state, type );
assert ( window );
mModifiers.Append ( new rvGEInsertModifier ( "New", window, mInterface->GetDesktop(), NULL ) );
mSelections.Set ( window );
mApplication->GetNavigator().Update ( );
mApplication->GetTransformer().Update ( );
mApplication->GetProperties().Update ( );
return window;
}
bool rvGEWorkspace::EditSelectedProperties ( void )
{
if ( !mSelections.Num ( ) || mSelections.Num() > 1 )
{
return false;
}
idDict dict;
if ( GEItemPropsDlg_DoModal ( mWnd, mSelections[0], dict ) )
{
mModifiers.Append ( new rvGEStateModifier ( "Item Properties", mSelections[0], dict ) );
SetModified ( true );
}
mApplication->GetNavigator().Update ( );
mApplication->GetTransformer().Update ( );
mApplication->GetProperties().Update ( );
return true;
}
bool rvGEWorkspace::EditSelectedScripts ( void )
{
if ( GEItemScriptsDlg_DoModal ( mWnd, mSelections[0] ) )
{
gApp.GetNavigator().Refresh ( );
SetModified ( true );
}
return true;
}
void rvGEWorkspace::BringSelectedForward ( void )
{
AddModifiers ( MOD_BRING_FORWARD );
mApplication->GetNavigator().Update ( );
}
void rvGEWorkspace::BringSelectedToFront ( void )
{
AddModifiers ( MOD_BRING_FRONT );
mApplication->GetNavigator().Update ( );
}
void rvGEWorkspace::SendSelectedToBack ( void )
{
AddModifiers ( MOD_SEND_BACK );
mApplication->GetNavigator().Update ( );
}
void rvGEWorkspace::SendSelectedBackward ( void )
{
AddModifiers ( MOD_SEND_BACKWARD );
mApplication->GetNavigator().Update ( );
}
/*
================
rvGEWorkspace::MakeSelectedSameSize
Align the selected items to the first one using the given align type
================
*/
void rvGEWorkspace::MakeSelectedSameSize ( bool changeWidth, bool changeHeight )
{
rvGEModifierGroup* group;
idRectangle rectTo;
int i;
group = new rvGEModifierGroup ( );
rectTo = rvGEWindowWrapper::GetWrapper ( mSelections[0] )->GetClientRect ( );
for ( i = 1; i < mSelections.Num(); i ++ )
{
idRectangle rectFrom;
float width = 0;
float height = 0;
rectFrom = rvGEWindowWrapper::GetWrapper(mSelections[i])->GetClientRect ();
if ( changeWidth )
{
width = rectTo.w - rectFrom.w;
}
if ( changeHeight )
{
height = rectTo.h - rectFrom.h;
}
group->Append ( new rvGESizeModifier ( "Make Same Size", mSelections[i], 0, 0, width, height ) );
}
mModifiers.Append ( group );
// Cant merge alignments
mModifiers.BlockNextMerge ( );
SetModified ( true );
}
/*
================
rvGEWorkspace::AlignSelected
Align the selected items to the first one using the given align type
================
*/
void rvGEWorkspace::AlignSelected ( EItemAlign align )
{
static const char* alignNames[]={"Lefts","Centers","Rights","Tops","Middles","Bottoms" };
int i;
idStr modName;
rvGEModifierGroup* group;
assert ( mSelections.Num() > 1 );
modName = "Align " + idStr(alignNames[align]);
group = new rvGEModifierGroup ( );
idRectangle rectTo;
rectTo = rvGEWindowWrapper::GetWrapper ( mSelections[0] )->GetScreenRect ( );
// Everything gets aligned to the first selection so run
// through all other selections and move them.
for ( i = 1; i < mSelections.Num(); i ++ )
{
float x;
float y;
idRectangle rectFrom;
rectFrom = rvGEWindowWrapper::GetWrapper ( mSelections[i] )->GetScreenRect ( );
switch ( align )
{
case ALIGN_LEFTS:
x = rectTo[0] - rectFrom[0];
y = 0;
break;
case ALIGN_RIGHTS:
x = (rectTo[0]+rectTo[2]) - (rectFrom[0]+rectFrom[2]);
y = 0;
break;
case ALIGN_CENTERS:
x = (rectTo[0]+rectTo[2]/2) - (rectFrom[0]+rectFrom[2]/2);
y = 0;
break;
case ALIGN_TOPS:
y = rectTo[1] - rectFrom[1];
x = 0;
break;
case ALIGN_BOTTOMS:
x = 0;
y = (rectTo[1]+rectTo[3]) - (rectFrom[1]+rectFrom[3]);
break;
case ALIGN_MIDDLES:
x = 0;
y = (rectTo[1]+rectTo[3]/2) - (rectFrom[1]+rectFrom[3]/2);
break;
default:
assert ( false );
break;
}
group->Append ( new rvGEMoveModifier ( modName, mSelections[i], x, y ) );
}
mModifiers.Append ( group );
// Cant merge alignments
mModifiers.BlockNextMerge ( );
SetModified ( true );
}
/*
================
rvGEWorkspace::AddModifierMove
Adds a move modifier with the given offsets
================
*/
void rvGEWorkspace::AddModifierMove ( const char* modName, float x, float y, bool snap )
{
idRectangle scaleRect;
idRectangle newRect;
scaleRect = mSelections.GetRect ( );
WindowToWorkspace ( scaleRect );
newRect = scaleRect;
newRect.x += x;
newRect.y += y;
if ( snap )
{
gApp.GetOptions ().SnapRectToGrid ( newRect, true, true, false, false );
}
rvGEModifierGroup* group = new rvGEModifierGroup;
for ( int i = 0; i < mSelections.Num(); i ++ )
{
if ( !mSelections[i]->GetParent ( ) )
{
continue;
}
// IF the parent window is being moved around as well then dont move this one.
if ( rvGEWindowWrapper::GetWrapper ( mSelections[i]->GetParent ( ) )->IsSelected ( ) )
{
// We still need the modifier there so the selection can be restored and
// so the rectangle gets updated
group->Append ( new rvGEMoveModifier ( modName, mSelections[i], 0, 0 ) );
continue;
}
group->Append ( new rvGEMoveModifier ( modName, mSelections[i], newRect.x-scaleRect.x, newRect.y-scaleRect.y ) );
}
mModifiers.Append ( group );
SetModified ( true );
}
/*
================
rvGEWorkspace::AddModifierSize
Adds a size modifier with the given offsets
================
*/
void rvGEWorkspace::AddModifierSize ( const char* modName, float l, float t, float r, float b, bool snap )
{
idRectangle scaleRect;
idRectangle sizeRect;
idRectangle newRect;
scaleRect = mSelections.GetRect ( );
WindowToWorkspace ( scaleRect );
newRect = scaleRect;
newRect.x += l;
newRect.y += t;
newRect.w += (r - l);
newRect.h += (b - t);
// Restrict sizing below 1 width
if ( newRect.w <= 1 )
{
newRect.x = newRect.x - (l ? (1 - newRect.w) : 0);
mDragPoint.x = newRect.x;
newRect.w = 1;
mDragX = false;
}
else
{
mDragX = true;
}
// Restrict sizing below 1 height
if ( newRect.h <= 1 )
{
newRect.y = newRect.y - (t ? (1 - newRect.h) : 0);
mDragPoint.y = newRect.y;
newRect.h = 1;
mDragY = false;
}
else
{
mDragY = true;
}
if ( snap )
{
gApp.GetOptions ().SnapRectToGrid ( newRect, l != 0.0f, t != 0.0f, r != 0.0f, b != 0.0f );
}
rvGEModifierGroup* group = new rvGEModifierGroup;
for ( int i = 0; i < mSelections.Num(); i ++ )
{
sizeRect = rvGEWindowWrapper::GetWrapper ( mSelections[i] )->GetScreenRect ( );
l = (newRect.x + ((sizeRect.x - scaleRect.x) / scaleRect.w) * newRect.w) - sizeRect.x;
t = (newRect.y + ((sizeRect.y - scaleRect.y) / scaleRect.h) * newRect.h) - sizeRect.y;
r = (sizeRect.w / scaleRect.w * newRect.w) - sizeRect.w + l;
b = (sizeRect.h / scaleRect.h * newRect.h) - sizeRect.h + t;
// This is sorta crufty but needs to be done. When a parent is being sized at the same
// time as a child you will get double movement because the child is relative to the parent. Therefore
// we need to subtract out the closest parents sizing.
idWindow* parent = mSelections[i];
while ( NULL != (parent = parent->GetParent ( ) ) )
{
rvGEWindowWrapper* pwrapper = rvGEWindowWrapper::GetWrapper ( parent );
float offset;
if ( !pwrapper->IsSelected ( ) )
{
continue;
}
sizeRect = pwrapper->GetScreenRect ( );
// Subtract out the left and right modifications
offset = ((newRect.x + ((sizeRect.x - scaleRect.x) / scaleRect.w) * newRect.w) - sizeRect.x);
l -= offset;
r -= offset;
// Subtract out the top and bottom modifications
offset = ((newRect.y + ((sizeRect.y - scaleRect.y) / scaleRect.h) * newRect.h) - sizeRect.y);
t -= offset;
b -= offset;
break;
}
group->Append ( new rvGESizeModifier ( modName, mSelections[i], l, t, r, b ) );
}
mModifiers.Append ( group );
SetModified ( true );
}
/*
================
rvGEWorkspace::MakeSelectedAChild
Makes the selected windows a child of the first selected window
================
*/
void rvGEWorkspace::MakeSelectedAChild ( void )
{
rvGEModifierGroup* group;
int i;
if ( !rvGEWindowWrapper::GetWrapper ( mSelections[0] )->CanHaveChildren ( ) )
{
gApp.MessageBox ( "Cannot add children to an htmlDef item", MB_OK|MB_ICONERROR );
return;
}
group = new rvGEModifierGroup;
for ( i = 1; i < mSelections.Num(); i ++ )
{
if ( mSelections[i]->GetParent ( ) == mSelections[0] )
{
continue;
}
if ( !mSelections[i]->GetParent ( ) )
{
continue;
}
group->Append ( new rvGEInsertModifier ( "Make Child", mSelections[i], mSelections[0], NULL ) );
}
mModifiers.Append ( group );
// Navigator needs an update since the ordering has changed
gApp.GetNavigator().Update ( );
SetModified ( true );
}
void rvGEWorkspace::Copy ( void )
{
int i;
// Clear the current clipboard
for ( i = 0; i < mClipboard.Num(); i ++ )
{
delete mClipboard[i];
}
mClipboard.Clear ( );
for ( i = 0; i < mSelections.Num(); i ++ )
{
rvGEWindowWrapper* wrapper = rvGEWindowWrapper::GetWrapper ( mSelections[i] );
assert ( wrapper );
rvGEClipboardItem* item = new rvGEClipboardItem;
item->mStateDict = wrapper->GetStateDict ( );
item->mScriptDict = wrapper->GetScriptDict ( );
item->mVarDict = wrapper->GetVariableDict ( );
item->mStateDict.Set ( "windowType", rvGEWindowWrapper::WindowTypeToString ( wrapper->GetWindowType ( ) ) );
mClipboard.Append ( item );
}
}
void rvGEWorkspace::Paste ( void )
{
int i;
rvGEModifierGroup* group = new rvGEModifierGroup;
mSelections.Clear ( );
for ( i = 0; i < mClipboard.Num(); i ++ )
{
idDict state;
rvGEWindowWrapper::EWindowType type;
state.Copy ( mClipboard[i]->mStateDict );
type = rvGEWindowWrapper::StringToWindowType ( state.GetString ( "windowType", "windowDef" ) );
state.Delete ( "windowType" );
idWindow* window = NewWindow ( &state, type );
group->Append ( new rvGEInsertModifier ( "Paste", window, mInterface->GetDesktop(), NULL ) );
mSelections.Add ( window );
rvGEWindowWrapper::GetWrapper ( window )->GetScriptDict ( ) = mClipboard[i]->mScriptDict;
rvGEWindowWrapper::GetWrapper ( window )->GetVariableDict ( ) = mClipboard[i]->mVarDict;
}
mModifiers.Append ( group );
mApplication->GetNavigator().Update ( );
SetModified ( true );
}
void rvGEWorkspace::HideSelected ( void )
{
AddModifiers ( MOD_HIDE );
mSelections.Clear ( );
mApplication->GetNavigator().Refresh ( );
}
void rvGEWorkspace::UnhideSelected ( void )
{
AddModifiers ( MOD_UNHIDE );
mApplication->GetNavigator().Refresh ( );
}
void rvGEWorkspace::HideWindow ( idWindow* window )
{
AddModifiers ( window, MOD_HIDE );
mApplication->GetNavigator().Refresh ( );
}
void rvGEWorkspace::UnhideWindow ( idWindow* window )
{
AddModifiers ( window, MOD_UNHIDE );
mApplication->GetNavigator().Refresh ( );
}
/*
================
rvGEWorkspace::SetModified
Sets the modified state of the window and if source control is enabled it
will attempt to check out the file
================
*/
void rvGEWorkspace::SetModified ( bool mod )
{
if ( mModified != mod )
{
mModified = mod;
UpdateTitle ( );
}
}