|
|
//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
//===========================================================================//
#include "stdafx.h"
#include "Gizmo.h"
#include "GlobalFunctions.h" // FIXME: For NotifyDuplicates
#include "History.h"
#include "MainFrm.h"
#include "MapDoc.h"
#include "MapDefs.h"
#include "MapEntity.h"
#include "MapPointHandle.h"
#include "MapSolid.h"
#include "MapView2D.h"
#include "MapViewLogical.h"
#include "MapView3D.h"
#include "ObjectProperties.h"
#include "Options.h"
#include "Render2D.h"
#include "ToolSelection.h"
#include "StatusBarIDs.h"
#include "ToolManager.h"
#include "hammer.h"
#include "vgui/Cursor.h"
#include "mapdecal.h"
#include "RenderUtils.h"
#include "tier0/icommandline.h"
#include "Manifest.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
#pragma warning(disable:4244)
// For debugging mouse messages
//static int _nMouseMove = 0;
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
Selection3D::Selection3D(void) { // The block tool uses our bounds as the default size when starting a new
// box. Set to reasonable defaults to begin with.
m_bIsLogicalTranslating = false; m_bInLogicalBoxSelection = false; m_bBoxSelection = false; m_bEyedropper = false; m_b3DEditMode = false; m_bSelected = false; m_bLButtonDown = false; m_bLeftDragged = false; m_bDrawAsSolidBox = false;
SetDrawFlags(Box3D::expandbox | Box3D::boundstext); SetDrawColors(Options.colors.clrToolHandle, Options.colors.clrToolSelection); m_clrLogicalBox = Options.colors.clrToolSelection; m_vLDownLogicalClient.Init();
m_pSelection = NULL; }
void Selection3D::Init( CMapDoc *pDocument ) { Box3D::Init( pDocument ); m_pSelection = pDocument->GetSelection(); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
Selection3D::~Selection3D(void) { }
//-----------------------------------------------------------------------------
// Purpose: Called when the tool is activated.
// Input : eOldTool - The ID of the previously active tool.
//-----------------------------------------------------------------------------
void Selection3D::OnActivate() { EnableHandles(true); }
//-----------------------------------------------------------------------------
// Purpose: Called when the tool is deactivated.
// Input : eNewTool - The ID of the tool that is being activated.
//-----------------------------------------------------------------------------
void Selection3D::OnDeactivate() { EnableHandles(false); }
//-----------------------------------------------------------------------------
// Purpose: Enables or disables the selection handles based on the current
// state of the tool.
//-----------------------------------------------------------------------------
void Selection3D::UpdateHandleState(void) { if ( !IsActiveTool() || m_pSelection->IsEditable() == false ) { EnableHandles(false); } else { EnableHandles(true); } }
//-----------------------------------------------------------------------------
// Purpose:
// Input : pView - The view that invoked the eyedropper.
// VarList -
// Output :
//-----------------------------------------------------------------------------
GDinputvariable *Selection3D::ChooseEyedropperVar(CMapView *pView, CUtlVector<GDinputvariable *> &VarList) { //
// Build a popup menu containing all the variable names.
//
CMenu menu; menu.CreatePopupMenu(); int nVarCount = VarList.Count(); for (int nVar = 0; nVar < nVarCount; nVar++) { GDinputvariable *pVar = VarList.Element(nVar); menu.AppendMenu(MF_STRING, nVar + 1, pVar->GetLongName()); }
//
// Invoke the popup menu.
//
CPoint point; GetCursorPos(&point); int nID = menu.TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_NONOTIFY | TPM_RETURNCMD, point.x, point.y, NULL, NULL); if (nID == 0) { return NULL; }
return VarList.Element(nID - 1); }
//-----------------------------------------------------------------------------
// Purpose:
// Input : pt -
// bValidOnly -
// Output : Returns the handle under the given point, -1 if there is none.
//-----------------------------------------------------------------------------
int Selection3D::HitTest(CMapView *pView, const Vector2D &ptClient, bool bTestHandles) { if (!IsEmpty()) { return Box3D::HitTest(pView, ptClient, bTestHandles); }
return FALSE; }
bool Selection3D::HitTestLogical( CMapView *pView, const Vector2D &ptClient ) { Vector2D vecLogicalMins, vecLogicalMaxs; if ( !m_pSelection->GetLogicalBounds(vecLogicalMins, vecLogicalMaxs) ) return false;
// Build a rect from our bounds to hit test against.
Vector2D vecMinClient, vecMaxClient; Vector vecMins( vecLogicalMins.x, vecLogicalMins.y, 0.0f ); Vector vecMaxs( vecLogicalMaxs.x, vecLogicalMaxs.y, 0.0f );
pView->WorldToClient( vecMinClient, vecMins ); pView->WorldToClient( vecMaxClient, vecMaxs );
CRect rect(vecMinClient.x, vecMinClient.y, vecMaxClient.x, vecMaxClient.y); rect.NormalizeRect();
// See if the point lies within the main rect.
return rect.PtInRect( CPoint( ptClient.x, ptClient.y ) ) ? true : false; }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void Selection3D::SetEmpty(void) { m_vTranslation.Init(); m_bIsTranslating = false; m_pSelection->SelectObject(NULL,scClear); UpdateSelectionBounds(); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool Selection3D::IsEmpty(void) { return (m_bBoxSelection || m_pSelection->GetCount()) ? false : true; }
//-----------------------------------------------------------------------------
// Purpose:
// Input :
//-----------------------------------------------------------------------------
void Selection3D::UpdateSelectionBounds( void ) { if ( !m_pSelection->GetBounds( bmins, bmaxs ) ) { ResetBounds(); } }
//-----------------------------------------------------------------------------
// Purpose:
// Input : pt3 -
// Output : Returns TRUE on success, FALSE on failure.
//-----------------------------------------------------------------------------
bool Selection3D::StartBoxSelection( CMapView *pView, const Vector2D &vPoint, const Vector &vStart) { m_bBoxSelection = true; Box3D::StartNew( pView, vPoint, vStart, Vector(0,0,0) ); return true; }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void Selection3D::EndBoxSelection() { m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_TOOL | MAPVIEW_UPDATE_SELECTION ); m_bBoxSelection = false; }
//-----------------------------------------------------------------------------
// Start, end logical selection
//-----------------------------------------------------------------------------
void Selection3D::StartLogicalBoxSelection( CMapViewLogical *pView, const Vector &vStart ) { m_bInLogicalBoxSelection = true; m_clrLogicalBox = RGB( 50, 255, 255 ); m_vecLogicalSelBoxMins = m_vecLogicalSelBoxMaxs = vStart.AsVector2D(); }
void Selection3D::EndLogicalBoxSelection( ) { m_clrLogicalBox = Options.colors.clrToolSelection; m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_TOOL | MAPVIEW_UPDATE_SELECTION ); m_bInLogicalBoxSelection = false; }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void Selection3D::TransformSelection(void) { // Transform the selected objects.
const CMapObjectList *pSelList = m_pSelection->GetList(); for (int i = 0; i < pSelList->Count(); i++) { CUtlReference< CMapClass > ref = pSelList->Element(i); CMapClass *pobj = ref; pobj->Transform( GetTransformMatrix() ); }
m_pDocument->SetModifiedFlag(); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void Selection3D::TransformLogicalSelection( const Vector2D &vecTranslation ) { // Transform the selected objects.
const CMapObjectList *pSelList = m_pSelection->GetList(); for (int i = 0; i < pSelList->Count(); i++) { CUtlReference< CMapClass > ref = pSelList->Element(i); CMapClass *pObj = ref; Vector2D vecNewPosition; Vector2DAdd( pObj->GetLogicalPosition(), vecTranslation, vecNewPosition ); pObj->SetLogicalPosition( vecNewPosition ); }
// The transformation may have changed some entity properties (such as the "angles" key),
// so we must refresh the Object Properties dialog.
GetMainWnd()->pObjectProperties->MarkDataDirty(); }
//-----------------------------------------------------------------------------
// Purpose: Draws objects when they are selected. Odd, how this code is stuck
// in this obscure place, away from all the other 2D rendering code.
// Input : pobj - Object to draw.
// pSel -
// Output : Returns TRUE to keep enumerating.
//-----------------------------------------------------------------------------
static BOOL DrawObject(CMapClass *pobj, CRender *pRender) { if ( !pobj->IsVisible() ) return true;
// switch selection mode so transformed object is drawn normal
pobj->SetSelectionState( SELECT_NONE );
CRender2D *pRender2D = dynamic_cast<CRender2D*>(pRender);
if ( pRender2D ) pobj->Render2D(pRender2D);
CRender3D *pRender3D = dynamic_cast<CRender3D*>(pRender);
if ( pRender3D ) pobj->Render3D(pRender3D);
pobj->SetSelectionState( SELECT_MODIFY );
return TRUE; }
static BOOL DrawObjectLogical( CMapClass *pObj, CRender2D *pRender2D ) { if ( !pObj->IsVisibleLogical() ) return true;
// switch selection mode so transformed object is drawn normal
pObj->SetSelectionState( SELECT_NONE );
if ( pRender2D ) { pObj->RenderLogical( pRender2D ); }
pObj->SetSelectionState( SELECT_MODIFY );
return TRUE; }
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pRender -
//-----------------------------------------------------------------------------
void Selection3D::RenderTool2D(CRender2D *pRender) { if ( !m_pSelection->IsEmpty() && IsTranslating() && !IsBoxSelecting() ) { //
// Even if this is not the active tool, selected objects should be rendered
// with the selection color.
//
COLORREF clr = Options.colors.clrSelection;
pRender->SetDrawColor( GetRValue(clr), GetGValue(clr), GetBValue(clr) );
VMatrix matrix = GetTransformMatrix();
pRender->BeginLocalTransfrom( matrix );
const CMapObjectList *pSelList = m_pSelection->GetList(); for (int i = 0; i < pSelList->Count(); i++) { CMapClass *pobj = (CUtlReference< CMapClass >)pSelList->Element(i); DrawObject(pobj, pRender); pobj->EnumChildren((ENUMMAPCHILDRENPROC)DrawObject, (DWORD)pRender); }
pRender->EndLocalTransfrom(); } else if ( !IsBoxSelecting() ) { UpdateSelectionBounds(); }
Box3D::RenderTool2D(pRender); }
//-----------------------------------------------------------------------------
// Render tool in visio view
//-----------------------------------------------------------------------------
void Selection3D::RenderToolLogical( CRender2D *pRender ) { if ( !m_pSelection->IsEmpty() && m_bIsLogicalTranslating && !IsLogicalBoxSelecting() ) { // Even if this is not the active tool, selected objects should be rendered
// with the selection color.
COLORREF clr = Options.colors.clrSelection;
pRender->SetDrawColor( GetRValue(clr), GetGValue(clr), GetBValue(clr) );
VMatrix matrix = GetTransformMatrix(); MatrixBuildTranslation( matrix, m_vLogicalTranslation.x, m_vLogicalTranslation.y, 0.0f ); pRender->BeginLocalTransfrom( matrix );
const CMapObjectList *pSelList = m_pSelection->GetList(); for (int i = 0; i < pSelList->Count(); i++) { CMapClass *pobj = (CUtlReference< CMapClass>)pSelList->Element(i); DrawObjectLogical(pobj, pRender); pobj->EnumChildren((ENUMMAPCHILDRENPROC)DrawObjectLogical, (DWORD)pRender); }
pRender->EndLocalTransfrom(); } Vector2D vecLogicalMins, vecLogicalMaxs; if ( IsLogicalBoxSelecting() ) { vecLogicalMins = m_vecLogicalSelBoxMins; vecLogicalMaxs = m_vecLogicalSelBoxMaxs; } else if ( !m_pSelection->GetLogicalBounds( vecLogicalMins, vecLogicalMaxs ) ) return;
Vector mins( vecLogicalMins.x, vecLogicalMins.y, 0.0f ); Vector maxs( vecLogicalMaxs.x, vecLogicalMaxs.y, 0.0f );
Assert( pRender ); pRender->PushRenderMode( RENDER_MODE_DOTTED ); pRender->SetDrawColor( GetRValue(Options.colors.clrToolDrag), GetGValue(Options.colors.clrToolDrag), GetBValue(Options.colors.clrToolDrag) ); pRender->DrawRectangle( mins, maxs, false, 2 ); pRender->PopRenderMode(); }
//-----------------------------------------------------------------------------
// Purpose: Renders a selection gizmo at our bounds center.
// Input : pRender -
//-----------------------------------------------------------------------------
void Selection3D::RenderTool3D(CRender3D *pRender) { const CMapObjectList *pSelList = m_pSelection->GetList();
if ( m_bDrawAsSolidBox ) { // while picking draw Selection tool as solid box
// so we cant pick stuff behind it
if ( pSelList->Count() ) { pRender->PushRenderMode( RENDER_MODE_FLAT ); pRender->BeginRenderHitTarget( (CUtlReference< CMapClass>)pSelList->Element(0) ); pRender->RenderBox( bmins, bmaxs, 255,255,255, SELECT_NONE ); pRender->EndRenderHitTarget(); pRender->PopRenderMode(); } return; }
else if ( !m_pSelection->IsEmpty() && IsTranslating() && !IsBoxSelecting() ) { //
// Even if this is not the active tool, selected objects should be rendered
// with the selection color.
//
COLORREF clr = Options.colors.clrSelection;
pRender->SetDrawColor( GetRValue(clr), GetGValue(clr), GetBValue(clr) );
VMatrix matrix = GetTransformMatrix(); pRender->BeginLocalTransfrom( matrix );
for (int i = 0; i < pSelList->Count(); i++) { CMapClass *pobj = (CUtlReference< CMapClass>)pSelList->Element(i);
DrawObject(pobj, pRender); pobj->EnumChildren((ENUMMAPCHILDRENPROC)DrawObject, (DWORD)pRender); }
pRender->EndLocalTransfrom();
if ( m_pDocument->m_bShowGrid && m_b3DEditMode ) RenderTranslationPlane( pRender ); } else if ( !IsBoxSelecting() ) { UpdateSelectionBounds(); }
if ( m_b3DEditMode ) { Box3D::RenderTool3D(pRender); } }
CBaseTool *Selection3D::GetToolObject( CMapView2D *pView, const Vector2D &vPoint, bool bAttach ) {
const CMapObjectList *pSelList = m_pSelection->GetList(); for (int i = 0; i < pSelList->Count(); i++) { CMapClass *pObject = (CUtlReference< CMapClass>)pSelList->Element(i);
//
// Hit test against the object. nHitData will return with object-specific
// information about what was clicked on.
//
HitInfo_t HitData; if ( pObject->HitTest2D(pView, vPoint, HitData) ) { //
// They clicked on some part of the object. See if there is a
// tool associated with what we clicked on.
//
CBaseTool *pToolHit = HitData.pObject->GetToolObject(HitData.uData, bAttach ); if ( pToolHit != NULL ) { return pToolHit; } } }
return NULL; }
CBaseTool *Selection3D::GetToolObjectLogical( CMapViewLogical *pView, const Vector2D &vPoint, bool bAttach ) { const CMapObjectList *pSelList = m_pSelection->GetList(); for (int i = 0; i < pSelList->Count(); i++) { CMapClass *pObject = (CUtlReference< CMapClass>)pSelList->Element(i);
//
// Hit test against the object. nHitData will return with object-specific
// information about what was clicked on.
//
HitInfo_t HitData; if ( pObject->HitTestLogical(pView, vPoint, HitData) ) { //
// They clicked on some part of the object. See if there is a
// tool associated with what we clicked on.
//
CBaseTool *pToolHit = HitData.pObject->GetToolObject(HitData.uData, bAttach ); if ( pToolHit != NULL ) { return pToolHit; } } }
return NULL; }
//-----------------------------------------------------------------------------
// Purpose:
// Input : pView -
// point -
//-----------------------------------------------------------------------------
bool Selection3D::OnContextMenu2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint) { // First give any selected tool helpers a chance to handle the message.
// Don't hit test against tool helpers when shift is held down
// (beginning a Clone operation).
CBaseTool *pToolHit = GetToolObject( pView, vPoint, true );
if ( pToolHit ) { return pToolHit->OnContextMenu2D(pView, nFlags, vPoint); }
static CMenu menu, menuSelection; static bool bInit = false;
if (!bInit) { bInit = true; menu.LoadMenu(IDR_POPUPS); menuSelection.Attach(::GetSubMenu(menu.m_hMenu, 0)); }
if ( !pView->PointInClientRect( vPoint ) ) return false;
if (!IsEmpty() && !IsBoxSelecting()) { if ( HitTest(pView, vPoint, false) ) { CPoint ptScreen( vPoint.x,vPoint.y); pView->ClientToScreen(&ptScreen); menuSelection.TrackPopupMenu(TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_LEFTALIGN, ptScreen.x, ptScreen.y, pView); return true; } }
return false; }
//-----------------------------------------------------------------------------
// Purpose:
// Input : pView -
// point -
//-----------------------------------------------------------------------------
bool Selection3D::OnContextMenuLogical(CMapViewLogical *pView, UINT nFlags, const Vector2D &vPoint) { // First give any selected tool helpers a chance to handle the message.
// Don't hit test against tool helpers when shift is held down
// (beginning a Clone operation).
CBaseTool *pToolHit = GetToolObjectLogical( pView, vPoint, true ); if ( pToolHit ) return pToolHit->OnContextMenuLogical(pView, nFlags, vPoint);
static CMenu menu, menuSelection; static bool bInit = false;
if (!bInit) { bInit = true; menu.LoadMenu(IDR_POPUPS); menuSelection.Attach(::GetSubMenu(menu.m_hMenu, 8)); }
if ( !pView->PointInClientRect( vPoint ) ) return false;
if (!IsEmpty() && !IsLogicalBoxSelecting()) { if ( HitTestLogical( pView, vPoint ) ) { CPoint ptScreen( vPoint.x, vPoint.y ); pView->ClientToScreen(&ptScreen); menuSelection.TrackPopupMenu(TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_LEFTALIGN, ptScreen.x, ptScreen.y, pView); return true; } }
return false; }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void Selection3D::SelectInBox(CMapDoc *pDoc, bool bInsideOnly) { BoundBox box(*this); EndBoxSelection();
//
// Make selection box "infinite" in 0-depth axes, of which there
// should not be more than 1.
//
int countzero = 0; for(int i = 0; i < 3; i++) { if (box.bmaxs[i] == box.bmins[i]) { box.bmins[i] = -COORD_NOTINIT; box.bmaxs[i] = COORD_NOTINIT; ++countzero; } }
if (countzero <= 1) { pDoc->SelectRegion(&box, bInsideOnly); }
UpdateSelectionBounds(); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void Selection3D::SelectInLogicalBox(CMapDoc *pDoc, bool bInsideOnly) { Vector2D mins = m_vecLogicalSelBoxMins; Vector2D maxs = m_vecLogicalSelBoxMaxs;
// Make selection box "infinite" in 0-depth axes, of which there
// should not be more than 1.
int countzero = 0; for (int i = 0; i < 2; i++) { if (maxs[i] == mins[i]) { mins[i] = -COORD_NOTINIT; maxs[i] = COORD_NOTINIT; ++countzero; } }
if (countzero <= 1) { pDoc->SelectLogicalRegion( mins, maxs, bInsideOnly ); }
UpdateSelectionBounds(); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void Selection3D::NudgeObjects(CMapView *pView, int nChar, bool bSnap, bool bClone) { Vector vecDelta, vVert, vHorz, vThrd;
pView->GetBestTransformPlane( vHorz, vVert, vThrd );
m_pDocument->GetNudgeVector( vHorz, vVert, nChar, bSnap, vecDelta); m_pDocument->NudgeObjects(vecDelta, bClone);
CMapView2DBase *pView2D = dynamic_cast<CMapView2DBase*>(pView);
if ( !pView2D ) return; // Try to keep the selection fully in the view if it started that way.
bool bFullyVisible = pView2D->IsBoxFullyVisible(bmins, bmaxs);
// Make sure it can still fit entirely in the view after nudging and don't scroll the
// view if it can't. This second check is probably unnecessary, but it can't hurt,
// and there might be cases where the selection changes size after a nudge operation.
if (bFullyVisible && pView2D->CanBoxFitInView(bmins, bmaxs)) { pView2D->LockWindowUpdate(); pView2D->EnsureVisible(bmins, 25); pView2D->EnsureVisible(bmaxs, 25); pView2D->UnlockWindowUpdate(); } }
//-----------------------------------------------------------------------------
// Purpose: Handles key down events in the 2D view.
// Input : Per CWnd::OnKeyDown.
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool Selection3D::OnKeyDown2D(CMapView2D *pView, UINT nChar, UINT nRepCnt, UINT nFlags) { bool bShift = ((GetKeyState(VK_SHIFT) & 0x8000) != 0); bool bCtrl = ((GetKeyState(VK_CONTROL) & 0x8000) != 0);
if (Options.view2d.bNudge && (nChar == VK_UP || nChar == VK_DOWN || nChar == VK_LEFT || nChar == VK_RIGHT)) { if (!IsEmpty()) { bool bSnap = m_pDocument->IsSnapEnabled() && !bCtrl; NudgeObjects(pView, nChar, bSnap, bShift); return true; } }
switch (nChar) { // TODO: do we want this here or in the view?
case VK_DELETE: { m_pDocument->OnCmdMsg(ID_EDIT_DELETE, CN_COMMAND, NULL, NULL); break; }
case VK_NEXT: { m_pDocument->OnCmdMsg(ID_EDIT_SELNEXT, CN_COMMAND, NULL, NULL); break; }
case VK_PRIOR: { m_pDocument->OnCmdMsg(ID_EDIT_SELPREV, CN_COMMAND, NULL, NULL); break; }
case VK_ESCAPE: { OnEscape(m_pDocument); break; }
case VK_RETURN: { if (IsBoxSelecting()) { SelectInBox(m_pDocument, bShift); UpdateHandleState(); } break; }
default: { return false; } } return true; }
//-----------------------------------------------------------------------------
// Purpose: Handles key down events in the logical view.
// Input : Per CWnd::OnKeyDown.
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool Selection3D::OnKeyDownLogical(CMapViewLogical *pView, UINT nChar, UINT nRepCnt, UINT nFlags) { bool bShift = ((GetKeyState(VK_SHIFT) & 0x8000) != 0); bool bAlt = GetKeyState(VK_MENU) < 0;
/* FIXME
if ( Options.view2d.bNudge && ( nChar == VK_UP || nChar == VK_DOWN || nChar == VK_LEFT || nChar == VK_RIGHT ) ) { if (!IsEmpty()) { NudgeObjects2D(pView, nChar, !bCtrl, bShift); return true; } } */ switch (nChar) { // TODO: do we want this here or in the view?
case VK_DELETE: m_pDocument->OnCmdMsg(ID_EDIT_DELETE, CN_COMMAND, NULL, NULL); break;
case VK_NEXT: m_pDocument->OnCmdMsg( bAlt ? ID_EDIT_SELNEXT_CASCADING : ID_EDIT_SELNEXT, CN_COMMAND, NULL, NULL); break;
case VK_PRIOR: m_pDocument->OnCmdMsg( bAlt ? ID_EDIT_SELPREV_CASCADING : ID_EDIT_SELPREV, CN_COMMAND, NULL, NULL); break;
case VK_ESCAPE: OnEscape( m_pDocument ); break;
case VK_RETURN: if ( m_bInLogicalBoxSelection ) { EndLogicalBoxSelection( ); SelectInLogicalBox( m_pDocument, bShift ); } break;
default: return false; } return true; }
//-----------------------------------------------------------------------------
// Purpose: Handles left button down events in the 2D view.
// Input : Per CWnd::OnLButtonDown.
// Output : Returns true if the message was handled, false if not.
//-----------------------------------------------------------------------------
bool Selection3D::OnLMouseDown2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint) { // First give any selected tool helpers a chance to handle the message.
// Don't hit test against tool helpers when shift is held down
// (beginning a Clone operation).
if (!(nFlags & MK_SHIFT)) { CBaseTool *pToolHit = GetToolObject( pView, vPoint, true );
if (pToolHit) { // There is a tool. Attach the object to the tool and forward
// the message to the tool.
return pToolHit->OnLMouseDown2D(pView, nFlags, vPoint); } }
Tool3D::OnLMouseDown2D(pView, nFlags, vPoint);
m_bSelected = false;
if ( IsBoxSelecting() ) { // if we click outside of the current selection box, remove old box
if ( !HitTest(pView, vPoint, true) ) { EndBoxSelection(); } }
if (nFlags & MK_CONTROL) { // add object under cursor to selection
m_bSelected = pView->SelectAt(vPoint, false, false); UpdateHandleState(); } else if ( IsEmpty() || !HitTest(pView,vPoint, true) ) { // start new selection
m_TranslateMode = modeScale; m_bSelected = pView->SelectAt(vPoint, true, false); UpdateHandleState(); } return true; }
//-----------------------------------------------------------------------------
// Purpose: Returns the constraints flags for the translation.
// Input : bDisableSnap -
// nKeyFlags -
//-----------------------------------------------------------------------------
unsigned int Selection3D::GetConstraints(unsigned int nKeyFlags) { unsigned int uConstraints = Tool3D::GetConstraints( nKeyFlags );
if ( m_TranslateMode==modeRotate ) { // backwards capability, SHIFT turns snapping off during rotation
if ( (nKeyFlags & MK_SHIFT) || !Options.view2d.bRotateConstrain ) { uConstraints = 0; } }
if ( uConstraints & constrainSnap ) { if ( m_pSelection->GetCount() == 1) { CMapClass *pObject = (CUtlReference< CMapClass>)m_pSelection->GetList()->Element(0);
if (pObject->ShouldSnapToHalfGrid()) { uConstraints |= constrainHalfSnap; } } }
return uConstraints; }
//-----------------------------------------------------------------------------
// 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 Selection3D::OnMouseMove2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint) { Tool3D::OnMouseMove2D(pView, nFlags, vPoint);
bool IsEditable = m_pSelection->IsEditable();
vgui::HCursor hCursor = vgui::dc_arrow;
bool bCtrl = (GetAsyncKeyState(VK_CONTROL) & 0x8000) ? true : false; unsigned int uConstraints = GetConstraints( nFlags); // Convert to world coords.
Vector vecWorld; pView->ClientToWorld(vecWorld, vPoint);
//
// Update status bar position display.
//
char szBuf[128]; sprintf(szBuf, " @%.0f, %.0f ", vecWorld[pView->axHorz], vecWorld[pView->axVert]); SetStatusText(SBI_COORDS, szBuf);
//
// If we are currently dragging the selection (moving, scaling, rotating, or shearing)
// update that operation based on the current cursor position and keyboard state.
//
if ( IsTranslating() ) { Tool3D::UpdateTranslation( pView, vPoint, uConstraints ); hCursor = vgui::dc_none; } //
// Else if we have just started dragging the selection, begin a new translation
//
else if ( m_bMouseDragged[MOUSE_LEFT] ) { pView->SetCapture();
if ( IsEditable && !bCtrl && HitTest( pView, m_vMouseStart[MOUSE_LEFT], true) ) { // we selected a handle - start translation the selection
StartTranslation( pView, m_vMouseStart[MOUSE_LEFT], m_LastHitTestHandle );
hCursor = UpdateCursor( pView, m_LastHitTestHandle, m_TranslateMode ); } else if ( !m_bSelected ) { // start new box selection if we didnt select an addition object
Vector ptOrg; pView->ClientToWorld(ptOrg, m_vMouseStart[MOUSE_LEFT] );
// set best third axis value
ptOrg[pView->axThird] = COORD_NOTINIT; m_pDocument->GetBestVisiblePoint(ptOrg);
if ( uConstraints & constrainSnap ) m_pDocument->Snap(ptOrg,uConstraints);
StartBoxSelection( pView, m_vMouseStart[MOUSE_LEFT], ptOrg ); EnableHandles(true); } } else if (!IsEmpty()) { //DBG("(%d) OnMouseMove2D: Selection NOT empty, update cursor\n", _nMouseMove);
//
// Just in case the selection set is not empty and "selection" hasn't received a left mouse click.
// (NOTE: this is gross, but unfortunately necessary (cab))
//
UpdateHandleState();
//
// If the cursor is on a handle, the cursor will be set by the HitTest code.
//
bool bFoundTool = false;
if ( GetToolObject( pView, vPoint, false ) ) { // If they moused over an interactive handle, it should have set the cursor.
hCursor = vgui::dc_crosshair; bFoundTool = true; }
// If we haven't moused over any interactive handles contained in the object, see if the
// mouse is over one of the selection handles.
if ( IsEditable && !bFoundTool && HitTest(pView, vPoint, true) ) { hCursor = UpdateCursor( pView, m_LastHitTestHandle, m_TranslateMode ); } }
if ( hCursor != vgui::dc_none ) { pView->SetCursor( hCursor ); }
return true; }
void Selection3D::FinishTranslation(bool bSave, bool bClone ) { const CMapObjectList *pSelList = m_pSelection->GetList();
// keep copy of current objects?
if ( bClone && (GetTranslateMode() == modeMove)) { GetHistory()->MarkUndoPosition(pSelList, "Clone Objects"); m_pDocument->CloneObjects(*pSelList); GetHistory()->KeepNew(pSelList); } else { GetHistory()->MarkUndoPosition(pSelList, "Translation"); GetHistory()->Keep(pSelList); }
if ( bSave ) { // transform selected objects
TransformSelection(); }
// finish the tool translation
Box3D::FinishTranslation( bSave );
if ( bSave ) { // update selection bounds
UpdateSelectionBounds(); NotifyDuplicates(pSelList); }
m_pSelection->SetSelectionState( SELECT_NORMAL ); }
void Selection3D::StartTranslation(CMapView *pView, const Vector2D &vPoint, const Vector &vHandleOrigin ) { Vector refPoint; Vector *pRefPoint = NULL;
// use single object origin as translation origin
if (m_pSelection->GetCount() == 1) { if ( vHandleOrigin.IsZero() || m_TranslateMode == modeRotate ) { CMapClass *pMapClassObj = (CUtlReference< CMapClass>)m_pSelection->GetList()->Element(0); CMapEntity *pObject = (CMapEntity *)pMapClassObj;
if ( pObject->IsMapClass(MAPCLASS_TYPE(CMapEntity)) && pObject->IsPlaceholder() ) { // set entity origin as translation center
pObject->GetOrigin( refPoint ); pRefPoint = &refPoint; } } }
// we selected a handle - start translation the selection
// If translating, redo our bounds temporarily to use the entity origins rather than their bounds
// so things will stay on the grid correctly.
Vector vCustomHandleBox[2]; Vector *pCustomHandleBox = NULL; if ( vHandleOrigin.IsZero() ) { pCustomHandleBox = vCustomHandleBox; m_pSelection->GetBoundsForTranslation( vCustomHandleBox[0], vCustomHandleBox[1] ); } Box3D::StartTranslation( pView, vPoint, vHandleOrigin, pRefPoint, pCustomHandleBox ); if ( !m_pSelection->IsEmpty() ) UpdateSelectionBounds();
m_pSelection->SetSelectionState( SELECT_MODIFY ); }
//-----------------------------------------------------------------------------
// Purpose: Handles left button up events in the 2D view.
// Input : Per CWnd::OnLButtonUp.
// Output : Returns true if the message was handled, false if not.
//-----------------------------------------------------------------------------
bool Selection3D::OnLMouseUp2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint) { bool bShift = ( nFlags & MK_SHIFT ) ? true : false;
Tool3D::OnLMouseUp2D(pView, nFlags, vPoint);
bool IsEditable = m_pSelection->IsEditable();
if ( IsTranslating() ) { // selecting stuff in box
if ( IsBoxSelecting() ) { Box3D::FinishTranslation(true);
if (Options.view2d.bAutoSelect) { SelectInBox(m_pDocument, bShift); UpdateHandleState(); } } else { FinishTranslation( true, bShift ); } } else if ( !m_bSelected && !m_pSelection->IsEmpty() ) { if ( IsEditable && HitTest(pView, vPoint, false) ) { ToggleTranslateMode();
UpdateCursor( pView, m_LastHitTestHandle, m_TranslateMode );
m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_TOOL ); } }
// we might have removed some stuff that was relevant:
m_pDocument->UpdateStatusbar(); return true; }
//-----------------------------------------------------------------------------
// Purpose: Handles left button down events in the 2D view.
// Input : Per CWnd::OnLButtonDown.
// Output : Returns true if the message was handled, false if not.
//-----------------------------------------------------------------------------
bool Selection3D::OnLMouseDownLogical(CMapViewLogical *pView, UINT nFlags, const Vector2D &vPoint) { // First give any selected tool helpers a chance to handle the message.
// Don't hit test against tool helpers when shift is held down
// (beginning a Clone operation).
if (!(nFlags & MK_SHIFT)) { CBaseTool *pToolHit = GetToolObjectLogical( pView, vPoint, true );
if (pToolHit) { // There is a tool. Attach the object to the tool and forward
// the message to the tool.
return pToolHit->OnLMouseDownLogical(pView, nFlags, vPoint); } }
m_bLButtonDown = true; m_vLDownLogicalClient = vPoint;
pView->SetCapture();
m_bLeftDragged = false; m_bSelected = false;
if ( m_bInLogicalBoxSelection ) { EndLogicalBoxSelection( ); }
// If they weren't alt- or ctrl-clicking and we have a selection, if they clicked
// in the selection rectangle, maintain what we got.
bool bCtrlClick = (nFlags & MK_CONTROL) != 0; bool bAltClick = GetKeyState(VK_MENU) < 0; if ( !bAltClick && !bCtrlClick && !IsEmpty() ) { if ( HitTestLogical( pView, vPoint ) ) return true; } if ( bAltClick ) { m_bSelected = ( pView->SelectAtCascading( vPoint, !bCtrlClick ) == true ); return true; }
m_bSelected = ( pView->SelectAt( vPoint, !bCtrlClick, false ) == true ); return true; }
//-----------------------------------------------------------------------------
// Purpose: Handles mouse move events in the 2D visio view.
// Input : Per CWnd::OnMouseMove.
// Output : Returns true if the message was handled, false if not.
//-----------------------------------------------------------------------------
bool Selection3D::OnMouseMoveLogical(CMapViewLogical *pView, UINT nFlags, const Vector2D &vPoint) { if ( m_bLButtonDown ) { if ( !m_bLeftDragged ) { // check if mouse was dragged if button is pressed down
Vector2D sizeDragged = vPoint - m_vLDownLogicalClient; if ((abs(sizeDragged.x) > DRAG_THRESHHOLD) || (abs(sizeDragged.y) > DRAG_THRESHHOLD)) { // If here, means we've dragged the mouse
m_bLeftDragged = true; } }
// Make sure the point is visible.
pView->ToolScrollToPoint( vPoint ); }
// Convert to world coords.
Vector2D vecWorld; pView->ClientToWorld( vecWorld, vPoint );
// Update status bar position display.
char szBuf[128]; sprintf(szBuf, " @%.0f, %.0f ", vecWorld.x, vecWorld.y); SetStatusText( SBI_COORDS, szBuf ); // If we are currently dragging the selection (moving)
// update that operation based on the current cursor position and keyboard state.
if ( m_bIsLogicalTranslating ) { Vector2D vecTranslation; Vector2DSubtract( vecWorld, m_vLastLogicalDragPoint, vecTranslation ); m_vLastLogicalDragPoint = vecWorld; m_vLogicalTranslation += vecTranslation; pView->UpdateView( MAPVIEW_UPDATE_TOOL ); return true; }
if ( m_bInLogicalBoxSelection && (nFlags & MK_LBUTTON) ) { Vector vecStartWorld; pView->ClientToWorld( vecStartWorld, m_vLDownLogicalClient ); if ( vecWorld.x < vecStartWorld.x ) { m_vecLogicalSelBoxMins.x = vecWorld.x; m_vecLogicalSelBoxMaxs.x = vecStartWorld.x; } else { m_vecLogicalSelBoxMins.x = vecStartWorld.x; m_vecLogicalSelBoxMaxs.x = vecWorld.x; } if ( vecWorld.y < vecStartWorld.y ) { m_vecLogicalSelBoxMins.y = vecWorld.y; m_vecLogicalSelBoxMaxs.y = vecStartWorld.y; } else { m_vecLogicalSelBoxMins.y = vecStartWorld.y; m_vecLogicalSelBoxMaxs.y = vecWorld.y; } pView->UpdateView( MAPVIEW_UPDATE_TOOL ); return true; }
// If we have just started dragging the selection, begin a new translation
if ( m_bLButtonDown && (nFlags & MK_LBUTTON) && m_bLeftDragged ) { pView->SetCapture();
// Check to see if the point at which we started clicking lies within the selection region
if ( HitTestLogical( pView, m_vLDownLogicalClient ) ) { pView->ClientToWorld( m_vLastLogicalDragPoint, m_vLDownLogicalClient ); m_vLogicalTranslation.Init(); m_bIsLogicalTranslating = true; pView->SetCursor( vgui::dc_sizeall ); m_pSelection->SetSelectionState( SELECT_MODIFY ); } else if ( !m_bSelected ) { // We're doing a drag with the mouse down, and nothing is selected.
// Start a logical box selection
Vector ptOrg; pView->ClientToWorld( ptOrg, m_vLDownLogicalClient ); StartLogicalBoxSelection( pView, ptOrg ); }
return true; } // If we are simply hovering over an object but the mouse isn't down, update the cursor.
vgui::HCursor hCursor = vgui::dc_arrow; if ( !IsEmpty() ) { // If the cursor is on a handle, the cursor will be set by the HitTest code.
bool bFoundTool = false; if ( GetToolObjectLogical( pView, vPoint, false ) ) { // If they moused over an interactive handle, it should have set the cursor.
hCursor = vgui::dc_crosshair; bFoundTool = true; } // If we haven't moused over any interactive handles contained in the object, see if the
// mouse is over one of the selection handles.
if ( !bFoundTool && HitTestLogical(pView, vPoint) ) { hCursor = vgui::dc_sizeall; } }
if ( hCursor != vgui::dc_none ) { pView->SetCursor( hCursor ); } return true; }
//-----------------------------------------------------------------------------
// Purpose: Handles left button up events in the 2D view.
// Input : Per CWnd::OnLButtonUp.
// Output : Returns true if the message was handled, false if not.
//-----------------------------------------------------------------------------
bool Selection3D::OnLMouseUpLogical(CMapViewLogical *pView, UINT nFlags, const Vector2D &vPoint) { bool bShift = ((GetKeyState(VK_SHIFT) & 0x8000) != 0);
ReleaseCapture(); m_bLButtonDown = false;
const CMapObjectList *pSelList = m_pSelection->GetList();
// selecting stuff in box
if ( m_bInLogicalBoxSelection ) { if ( Options.view2d.bAutoSelect ) { EndLogicalBoxSelection( ); SelectInLogicalBox( m_pDocument, bShift ); }
m_pSelection->SetSelectionState( SELECT_NORMAL ); goto updateStatusBar; } if ( m_bIsLogicalTranslating ) { // keep copy of current objects?
if ( nFlags & MK_SHIFT ) { GetHistory()->MarkUndoPosition(pSelList, "Clone Objects"); m_pDocument->CloneObjects(*pSelList); GetHistory()->KeepNew(pSelList); } else { GetHistory()->MarkUndoPosition(pSelList, "Logical Translation"); GetHistory()->Keep( pSelList ); }
TransformLogicalSelection( m_vLogicalTranslation );
// finish the tool translation
m_bIsLogicalTranslating = false;
// update selection bounds
UpdateSelectionBounds();
m_pDocument->SetModifiedFlag();
NotifyDuplicates( pSelList );
m_pSelection->SetSelectionState( SELECT_NORMAL ); goto updateStatusBar; } updateStatusBar: // we might have removed some stuff that was relevant:
m_pDocument->UpdateStatusbar(); 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 Selection3D::OnKeyDown3D(CMapView3D *pView, UINT nChar, UINT nRepCnt, UINT nFlags) { bool bShift = ((GetKeyState(VK_SHIFT) & 0x8000) != 0); bool bCtrl = ((GetKeyState(VK_CONTROL) & 0x8000) != 0); switch (nChar) { /*
dvs: The eyedropper is a somewhat failed experiment, an attempt to create a way to quickly hook entities together. I think a dedicated connection tool with a more rubber-band style UI might be more successful. Either that or relegate that work to a Logical-style view. case 'e': case 'E': { m_bEyedropper = !m_bEyedropper; if (m_bEyedropper) { SetEyedropperCursor(); } else { SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW)); } return true; } */ #ifndef SDK_BUILD
case 'x': case 'X': { m_b3DEditMode = !m_b3DEditMode; pView->UpdateView( MAPVIEW_UPDATE_TOOL ); return true; } #endif
case VK_DELETE: { m_pDocument->OnCmdMsg(ID_EDIT_DELETE, CN_COMMAND, NULL, NULL); return true; }
case VK_ESCAPE: { OnEscape(m_pDocument); return true; } }
if (Options.view2d.bNudge && (nChar == VK_UP || nChar == VK_DOWN || nChar == VK_LEFT || nChar == VK_RIGHT)) { if (!IsEmpty()) { bool bSnap = m_pDocument->IsSnapEnabled() && !bCtrl; NudgeObjects(pView, nChar, bSnap, bShift); return true; } }
return false; }
//-----------------------------------------------------------------------------
// Purpose: Handles double click events in the 3D view.
// Input : Per CWnd::OnLButtonDblClk.
// Output : Returns true if the message was handled, false if not.
//-----------------------------------------------------------------------------
bool Selection3D::OnLMouseDblClk3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint) { if ( !m_pSelection->IsEmpty() ) { if ( m_pSelection->GetCount() == 1 ) { CMapClass *pObject = (CUtlReference< CMapClass>)m_pSelection->GetList()->Element( 0 ); CManifestInstance *pManifestInstance = dynamic_cast< CManifestInstance * >( pObject ); if ( pManifestInstance ) { CManifest *pManifest = CMapDoc::GetManifest();
if ( pManifest ) { pManifest->SetPrimaryMap( pManifestInstance->GetManifestMap() ); return true; } } }
GetMainWnd()->pObjectProperties->ShowWindow(SW_SHOW); }
return true; }
bool Selection3D::OnLMouseDblClkLogical(CMapViewLogical *pView, UINT nFlags, const Vector2D &vPoint) { if ( !m_pSelection->IsEmpty() ) { GetMainWnd()->pObjectProperties->ShowWindow(SW_SHOW); } return true; }
//-----------------------------------------------------------------------------
// Purpose:
// Input : pView -
// point -
//-----------------------------------------------------------------------------
void Selection3D::EyedropperPick2D(CMapView2D *pView, const Vector2D &vPoint) { }
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pView -
// point -
//-----------------------------------------------------------------------------
void Selection3D::EyedropperPick3D(CMapView3D *pView, const Vector2D &vPoint) { //
// We only want to do this if we have at least one entity selected.
//
if ( !m_pSelection->IsAnEntitySelected() ) { MessageBox( NULL, "No entities are selected, so the eyedropper has nothing to assign to.", "No selected entities", MB_OK); return; }
//
// If they clicked on an entity, get the name of the entity they clicked on.
//
ULONG ulFace; CMapClass *pClickObject = pView->NearestObjectAt( vPoint, ulFace); if (pClickObject != NULL) { EyedropperPick(pView, pClickObject); } }
//-----------------------------------------------------------------------------
// Purpose:
// Input : pObject -
//-----------------------------------------------------------------------------
void Selection3D::EyedropperPick(CMapView *pView, CMapClass *pObject) { //
// The eyedropper currently only supports clicking on entities.
// TODO: consider using this to fill out face lists if they click on a solid
//
CMapEntity *pEntity = FindEntityInTree(pObject); if (pEntity == NULL) { // They clicked on something that is not an entity.
return; }
//
// Get the name of the clicked on entity.
//
const char *pszClickName = NULL; pszClickName = pEntity->GetKeyValue("targetname"); if (pszClickName == NULL) { //
// They clicked on an entity with no name.
//
MessageBox( NULL, "The chosen entity has no name.", "No name to pick", MB_OK ); return; }
//
// Build a list of all the keyvalues in the selected entities that support the eyedropper.
//
CUtlVector<GDinputvariable *> VarList;
int nEntityCount = 0;
const CMapObjectList *pSelList = m_pSelection->GetList();
for (int i = 0; i < pSelList->Count(); i++) { CMapClass *pObject = (CUtlReference< CMapClass>)pSelList->Element(i); CMapEntity *pEntity = dynamic_cast <CMapEntity *> (pObject); if (pEntity != NULL) { nEntityCount++; GDclass *pClass = pEntity->GetClass();
int nVarCount = pClass->GetVariableCount(); for (int nVar = 0; nVar < nVarCount; nVar++) { GDinputvariable *pVar = pClass->GetVariableAt(nVar); if (pVar && ((pVar->GetType() == ivTargetDest) || (pVar->GetType() == ivTargetNameOrClass))) { VarList.AddToTail(pVar); } } } }
//
// Prompt for what keyvalue in the selected entities we are filling out.
//
int nCount = VarList.Count(); if (nCount <= 0) { //
// No selected entities have keys of the appropriate type, so there's nothing we can do.
//
MessageBox( NULL, "No selected entities have keyvalues that accept an entity name, so the eyedropper has nothing to assign to.", "No eligible keyvalues", MB_OK ); return; }
//
// Determine the name of the key that we are filling out.
//
GDinputvariable *pVar = ChooseEyedropperVar(pView, VarList); if (!pVar) { return; } const char *pszVarName = pVar->GetName(); if (!pszVarName) { return; }
GetHistory()->MarkUndoPosition( pSelList, "Set Keyvalue");
//
// Apply the key to all selected entities with the chosen keyvalue.
//
for (int i = 0; i < pSelList->Count(); i++) { CMapClass *pObject = (CUtlReference< CMapClass>)pSelList->Element(i); CMapEntity *pEntity = dynamic_cast <CMapEntity *> (pObject); if (pEntity != NULL) { GDclass *pClass = pEntity->GetClass(); GDinputvariable *pVar = pClass->VarForName(pszVarName); if (pVar && ((pVar->GetType() == ivTargetDest) || (pVar->GetType() == ivTargetNameOrClass))) { GetHistory()->Keep(pEntity); pEntity->SetKeyValue(pszVarName, pszClickName); } } }
CMapDoc *pDoc = pView->GetMapDoc(); if (pDoc != NULL) { pDoc->SetModifiedFlag(); } }
//-----------------------------------------------------------------------------
// Purpose: Returns the nearest CMapEntity object up the hierarchy from the
// given object.
// Input : pObject - Object to start from.
//-----------------------------------------------------------------------------
CMapEntity *Selection3D::FindEntityInTree(CMapClass *pObject) { do { CMapEntity *pEntity = dynamic_cast <CMapEntity *> (pObject); if (pEntity != NULL) { return pEntity; }
pObject = pObject->GetParent();
} while (pObject != NULL);
// No entity in this branch of the object tree.
return NULL; }
//-----------------------------------------------------------------------------
// Purpose: Handles left button down events in the 3D view.
// Input : Per CWnd::OnLButtonDown.
// Output : Returns true if the message was handled, false if not.
//-----------------------------------------------------------------------------
bool Selection3D::OnLMouseDown3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint) { Tool3D::OnLMouseDown3D(pView, nFlags, vPoint);
m_bSelected = false;
//
// If they are holding down the eyedropper hotkey, do an eyedropper pick. The eyedropper fills out
// keyvalues in selected entities based on the object they clicked on.
//
if (m_bEyedropper) { EyedropperPick3D(pView, vPoint); m_bEyedropper = false; SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW)); return true; }
if (nFlags & MK_CONTROL) { m_bSelected = pView->SelectAt(vPoint, false, false);; UpdateHandleState(); } else if ( m_b3DEditMode && HitTest(pView,vPoint, true) ) { // if clicked on handles, never change selection
if ( !IsBoxSelecting() && m_LastHitTestHandle == vec3_origin ) { // clicked somewhere on our selection tool but maybe something else is inbetween
HitInfo_t HitData;
m_bDrawAsSolidBox = true;
pView->ObjectsAt( vPoint, &HitData, 1 );
if ( HitData.pObject && !HitData.pObject->IsSelected() ) { m_bSelected = pView->SelectAt(vPoint, true, false); UpdateHandleState(); }
m_bDrawAsSolidBox = false;
pView->SetCursor( UpdateCursor( pView, m_LastHitTestHandle, m_TranslateMode ) ); } } else { m_TranslateMode = modeScale; m_bSelected = pView->SelectAt(vPoint, true, false); UpdateHandleState(); }
if ( m_bSelected && !m_b3DEditMode ) { pView->BeginPick(); }
return true; }
//-----------------------------------------------------------------------------
// Purpose: Handles left button up events in the 3D view.
// Input : Per CWnd::OnLButtonUp.
// Output : Returns true if the message was handled, false if not.
//-----------------------------------------------------------------------------
bool Selection3D::OnLMouseUp3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint) { bool bShift = ( nFlags & MK_SHIFT ) ? true : false;
Tool3D::OnLMouseUp3D(pView, nFlags, vPoint) ;
bool IsEditable = m_pSelection->IsEditable();
if ( IsTranslating() ) { // selecting stuff in box
if ( IsBoxSelecting() ) { Box3D::FinishTranslation(true);
if (Options.view2d.bAutoSelect) { SelectInBox(m_pDocument, bShift); UpdateHandleState(); } } else { FinishTranslation( true, bShift ); } } else if ( m_b3DEditMode && !m_bSelected && !m_pSelection->IsEmpty() ) { if ( IsEditable && HitTest(pView, vPoint, false) ) { ToggleTranslateMode();
UpdateCursor( pView, m_LastHitTestHandle, m_TranslateMode );
m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_TOOL ); } }
pView->EndPick();
// we might have removed some stuff that was relevant:
m_pDocument->UpdateStatusbar();
return true; }
//-----------------------------------------------------------------------------
// Purpose: Handles mouse move events in the 3D view.
// Input : Per CWnd::OnMouseMove.
// Output : Returns true if the message was handled, false if not.
//-----------------------------------------------------------------------------
bool Selection3D::OnMouseMove3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint) { Tool3D::OnMouseMove3D(pView, nFlags, vPoint);
bool IsEditable = m_pSelection->IsEditable();
vgui::HCursor hCursor = vgui::dc_arrow;
if ( m_bEyedropper ) { SetEyedropperCursor(); } //
// If we are currently dragging the selection (moving, scaling, rotating, or shearing)
// update that operation based on the current cursor position and keyboard state.
//
else if ( IsTranslating() ) { unsigned int uConstraints = GetConstraints(nFlags);
//
// If they are dragging with a valid handle, update the views.
//
Tool3D::UpdateTranslation( pView, vPoint, uConstraints );
hCursor = vgui::dc_none; } //
// Else if we have just started dragging the selection, begin a new translation
//
else if ( m_b3DEditMode && m_bMouseDragged[MOUSE_LEFT] ) { if ( IsEditable && HitTest( pView, m_vMouseStart[MOUSE_LEFT], true) ) { // we selected a handle - start translation the selection
StartTranslation( pView, vPoint, m_LastHitTestHandle ); hCursor = UpdateCursor( pView, m_LastHitTestHandle, m_TranslateMode ); } } else if ( IsEditable && m_b3DEditMode && !IsEmpty() ) { UpdateHandleState();
if ( HitTest(pView, vPoint, true) ) { hCursor = UpdateCursor( pView, m_LastHitTestHandle, m_TranslateMode ); } }
if ( hCursor != vgui::dc_none ) { pView->SetCursor( hCursor ); } return true; }
//-----------------------------------------------------------------------------
// Purpose: Sets the cursor to the eyedropper cursor.
//-----------------------------------------------------------------------------
void Selection3D::SetEyedropperCursor(void) { static HCURSOR hcurEyedropper = NULL; if (!hcurEyedropper) { hcurEyedropper = LoadCursor(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDC_EYEDROPPER)); } SetCursor(hcurEyedropper); }
//-----------------------------------------------------------------------------
// Purpose: Handles the escape key in the 2D or 3D views.
//-----------------------------------------------------------------------------
void Selection3D::OnEscape(CMapDoc *pDoc) { //
// If we're in eyedropper mode, go back to selection mode.
//
if (m_bEyedropper) { m_bEyedropper = false; SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW)); } //
// If we're box selecting, clear the box.
//
else if (IsBoxSelecting()) { EndBoxSelection(); UpdateSelectionBounds(); } //
// If we're logical box selecting, clear the box.
//
else if ( m_bInLogicalBoxSelection ) { EndLogicalBoxSelection(); } //
// If we're moving a brush, put it back.
//
else if (IsTranslating()) { FinishTranslation(false,false); } //
// If we have a selection, deselect it.
//
else if (!IsEmpty()) { SetEmpty(); } }
|