/* =========================================================================== 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 "tools/edit_gui_common.h" #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 SetWindowLongPtr( mWnd, GWLP_USERDATA, ( LONG_PTR ) this ); UpdateTitle ( ); return true; } /* ================ rvGEWorkspace::Detach Detaches the workspace from the window it is currently attached to ================ */ void rvGEWorkspace::Detach ( void ) { assert ( mWnd ); SetWindowLongPtr ( mWnd, GWLP_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 qglMatrixMode(GL_PROJECTION); qglLoadIdentity(); qglOrtho(0,mWindowWidth, mWindowHeight, 0, -1, 1); qglMatrixMode(GL_MODELVIEW); qglLoadIdentity(); 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 ); qglMatrixMode(GL_PROJECTION); qglLoadIdentity(); qglOrtho(0,mWindowWidth, mWindowHeight, 0, -1, 1); qglMatrixMode(GL_MODELVIEW); qglLoadIdentity(); 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 ( ); } }