//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //=============================================================================// #include "stdafx.h" #include "History.h" #include "MainFrm.h" #include "MapDefs.h" #include "MapDoc.h" #include "MapView2D.h" #include "MapView3D.h" #include "Options.h" #include "StatusBarIDs.h" #include "ToolBlock.h" #include "ToolManager.h" #include "vgui/Cursor.h" #include "Selection.h" class CToolBlockMessageWnd : public CWnd { public: bool Create(void); void PreMenu2D(CToolBlock *pTool, CMapView2D *pView); protected: //{{AFX_MSG_MAP(CToolBlockMessageWnd) afx_msg void OnCreateObject(); //}}AFX_MSG DECLARE_MESSAGE_MAP() private: CToolBlock *m_pToolBlock; CMapView2D *m_pView2D; }; static CToolBlockMessageWnd s_wndToolMessage; static const char *g_pszClassName = "ValveEditor_BlockToolWnd"; BEGIN_MESSAGE_MAP(CToolBlockMessageWnd, CWnd) //{{AFX_MSG_MAP(CToolMessageWnd) ON_COMMAND(ID_CREATEOBJECT, OnCreateObject) //}}AFX_MSG_MAP END_MESSAGE_MAP() //----------------------------------------------------------------------------- // Purpose: Creates the hidden window that receives context menu commands for the // block tool. // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool CToolBlockMessageWnd::Create(void) { WNDCLASS wndcls; memset(&wndcls, 0, sizeof(WNDCLASS)); wndcls.lpfnWndProc = AfxWndProc; wndcls.hInstance = AfxGetInstanceHandle(); wndcls.lpszClassName = g_pszClassName; if (!AfxRegisterClass(&wndcls)) { return(false); } return(CWnd::CreateEx(0, g_pszClassName, g_pszClassName, 0, CRect(0, 0, 10, 10), NULL, 0) == TRUE); } //----------------------------------------------------------------------------- // Purpose: Attaches the block tool to this window before activating the context // menu. //----------------------------------------------------------------------------- void CToolBlockMessageWnd::PreMenu2D(CToolBlock *pToolBlock, CMapView2D *pView) { Assert(pToolBlock != NULL); m_pToolBlock = pToolBlock; m_pView2D = pView; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CToolBlockMessageWnd::OnCreateObject() { m_pToolBlock->CreateMapObject(m_pView2D); } //----------------------------------------------------------------------------- // Purpose: Constructor. //----------------------------------------------------------------------------- CToolBlock::CToolBlock(void) { // We always show our dimensions when we render. SetDrawFlags(GetDrawFlags() | Box3D::boundstext); SetDrawColors(Options.colors.clrToolHandle, Options.colors.clrToolBlock); } //----------------------------------------------------------------------------- // Purpose: Destructor. //----------------------------------------------------------------------------- CToolBlock::~CToolBlock(void) { } //----------------------------------------------------------------------------- // Purpose: Handles key down events in the 2D view. // Input : Per CWnd::OnKeyDown. // Output : Returns true if the message was handled, false if not. //----------------------------------------------------------------------------- bool CToolBlock::OnKeyDown2D(CMapView2D *pView, UINT nChar, UINT nRepCnt, UINT nFlags) { switch (nChar) { case VK_RETURN: { if ( !IsEmpty() ) { CreateMapObject(pView); } return true; } case VK_ESCAPE: { OnEscape(); return true; } } return false; } //----------------------------------------------------------------------------- // Purpose: Handles context menu events in the 2D view. // Input : Per CWnd::OnContextMenu. // Output : Returns true if the message was handled, false if not. //----------------------------------------------------------------------------- bool CToolBlock::OnContextMenu2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint) { static CMenu menu, menuCreate; static bool bInit = false; if (!bInit) { bInit = true; // Create the menu. menu.LoadMenu(IDR_POPUPS); menuCreate.Attach(::GetSubMenu(menu.m_hMenu, 1)); // Create the window that handles menu messages. s_wndToolMessage.Create(); } if ( !pView->PointInClientRect(vPoint) ) { return false; } if ( !IsEmpty() ) { if ( HitTest(pView, vPoint, false) ) { CPoint ptScreen( vPoint.x,vPoint.y); pView->ClientToScreen(&ptScreen); s_wndToolMessage.PreMenu2D(this, pView); menuCreate.TrackPopupMenu(TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_LEFTALIGN, ptScreen.x, ptScreen.y, &s_wndToolMessage); } } return true; } //----------------------------------------------------------------------------- // Purpose: Handles left mouse button down events in the 2D view. // Input : Per CWnd::OnLButtonDown. // Output : Returns true if the message was handled, false if not. //----------------------------------------------------------------------------- bool CToolBlock::OnLMouseDown3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint) { Tool3D::OnLMouseDown3D(pView, nFlags, vPoint); // // If we have a box, see if they clicked on any part of it. // if ( !IsEmpty() ) { if ( HitTest( pView, vPoint, true ) ) { // just update current block StartTranslation( pView, vPoint, m_LastHitTestHandle ); return true; } } return true; } //----------------------------------------------------------------------------- // Purpose: Handles left mouse button down events in the 2D view. // Input : Per CWnd::OnLButtonDown. // Output : Returns true if the message was handled, false if not. //----------------------------------------------------------------------------- bool CToolBlock::OnLMouseDown2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint) { Tool3D::OnLMouseDown2D(pView, nFlags, vPoint); // If we have a box, see if they clicked on any part of it. if ( !IsEmpty() ) { if ( HitTest( pView, vPoint, true ) ) { // just update current block StartTranslation( pView, vPoint, m_LastHitTestHandle ); return true; } } return true; } //----------------------------------------------------------------------------- // Purpose: Handles left mouse button up events in the 2D view. // Input : Per CWnd::OnLButtonUp. // Output : Returns true if the message was handled, false if not. //----------------------------------------------------------------------------- bool CToolBlock::OnLMouseUp2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint) { Tool3D::OnLMouseUp2D(pView, nFlags, vPoint); if (IsTranslating()) { FinishTranslation(true); } m_pDocument->UpdateStatusbar(); return true; } //----------------------------------------------------------------------------- // Purpose: Handles left mouse button up events in the 2D view. // Input : Per CWnd::OnLButtonUp. // Output : Returns true if the message was handled, false if not. //----------------------------------------------------------------------------- bool CToolBlock::OnLMouseUp3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint) { Tool3D::OnLMouseUp3D(pView, nFlags, vPoint); if (IsTranslating()) { FinishTranslation(true); } m_pDocument->UpdateStatusbar(); return true; } //----------------------------------------------------------------------------- // Purpose: Handles mouse move events in the 2D view. // Input : Per CWnd::OnMouseMove. // Output : Returns true if the message was handled, false if not. //----------------------------------------------------------------------------- bool CToolBlock::OnMouseMove2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint) { vgui::HCursor hCursor = vgui::dc_arrow; // Snap it to the grid. unsigned int uConstraints = GetConstraints( nFlags ); Tool3D::OnMouseMove2D(pView, nFlags, vPoint); // // // Convert to world coords. // Vector vecWorld; pView->ClientToWorld(vecWorld, vPoint); // // Update status bar position display. // char szBuf[128]; if ( uConstraints & constrainSnap ) m_pDocument->Snap(vecWorld,uConstraints); sprintf(szBuf, " @%.0f, %.0f ", vecWorld[pView->axHorz], vecWorld[pView->axVert]); SetStatusText(SBI_COORDS, szBuf); if ( IsTranslating() ) { Tool3D::UpdateTranslation( pView, vPoint, uConstraints); hCursor = vgui::dc_none; } else if ( m_bMouseDragged[MOUSE_LEFT] ) { // Starting a new box. Build a starting point for the drag. pView->SetCursor( "Resource/block.cur" ); Vector vecStart; pView->ClientToWorld(vecStart, m_vMouseStart[MOUSE_LEFT] ); // Snap it to the grid. if ( uConstraints & constrainSnap ) m_pDocument->Snap( vecStart, uConstraints); // Start the new box with the extents of the last selected thing. Vector bmins,bmaxs; m_pDocument->GetSelection()->GetLastValidBounds(bmins, bmaxs); vecStart[pView->axThird] = bmins[pView->axThird]; StartNew(pView, vPoint, vecStart, pView->GetViewAxis() * (bmaxs-bmins) ); } else if ( !IsEmpty() ) { // If the cursor is on a handle, set it to a cross. if ( HitTest(pView, vPoint, true) ) { hCursor = UpdateCursor( pView, m_LastHitTestHandle, m_TranslateMode ); } } if ( hCursor != vgui::dc_none ) pView->SetCursor( hCursor ); return true; } bool CToolBlock::OnMouseMove3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint) { Tool3D::OnMouseMove3D(pView, nFlags, vPoint); if ( IsTranslating() ) { unsigned int uConstraints = GetConstraints( nFlags ); // If they are dragging with a valid handle, update the views. Tool3D::UpdateTranslation( pView, vPoint, uConstraints ); } return true; } //----------------------------------------------------------------------------- // Purpose: Handles key down events in the 3D view. // Input : Per CWnd::OnKeyDown. // Output : Returns true if the message was handled, false if not. //----------------------------------------------------------------------------- bool CToolBlock::OnKeyDown3D(CMapView3D *pView, UINT nChar, UINT nRepCnt, UINT nFlags) { switch (nChar) { case VK_RETURN: { if ( !IsEmpty() ) { CreateMapObject(pView); } return true; } case VK_ESCAPE: { OnEscape(); return true; } } return false; } //----------------------------------------------------------------------------- // Purpose: Handles the escape key in the 2D or 3D views. //----------------------------------------------------------------------------- void CToolBlock::OnEscape(void) { // // If we have nothing blocked, go back to the pointer tool. // if ( IsEmpty() ) { ToolManager()->SetTool(TOOL_POINTER); } // // We're blocking out a region, so clear it. // else { SetEmpty(); } } //----------------------------------------------------------------------------- // Purpose: Creates a solid in the given view. //----------------------------------------------------------------------------- void CToolBlock::CreateMapObject(CMapView *pView) { CMapWorld *pWorld = m_pDocument->GetMapWorld(); if (!(bmaxs[0] - bmins[0]) || !(bmaxs[1] - bmins[1]) || !(bmaxs[2] - bmins[2])) { AfxMessageBox("The box is empty."); SetEmpty(); return; } BoundBox NewBox = *this; if (Options.view2d.bOrientPrimitives) { switch (pView->GetDrawType()) { case VIEW2D_XY: { break; } case VIEW2D_YZ: { NewBox.Rotate90(AXIS_Y); break; } case VIEW2D_XZ: { NewBox.Rotate90(AXIS_X); break; } } } // Create the object within the given bounding box. CMapClass *pObject = GetMainWnd()->m_ObjectBar.CreateInBox(&NewBox, pView); if (pObject == NULL) { SetEmpty(); return; } m_pDocument->ExpandObjectKeywords(pObject, pWorld); GetHistory()->MarkUndoPosition(m_pDocument->GetSelection()->GetList(), "New Object"); m_pDocument->AddObjectToWorld(pObject); // // Do we need to rotate this object based on the view we created it in? // if (Options.view2d.bOrientPrimitives) { Vector center; pObject->GetBoundsCenter( center ); QAngle angles(0, 0, 0); switch (pView->GetDrawType()) { case VIEW2D_XY: { break; } case VIEW2D_YZ: { angles[1] = 90.f; pObject->TransRotate(center, angles); break; } case VIEW2D_XZ: { angles[0] = 90.f; pObject->TransRotate(center, angles); break; } } } GetHistory()->KeepNew(pObject); // Select the new object. m_pDocument->SelectObject(pObject, scClear|scSelect|scSaveChanges); m_pDocument->SetModifiedFlag(); SetEmpty(); ResetBounds(); }