//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//=============================================================================//

#include "stdafx.h"
#include "GlobalFunctions.h"
#include "History.h"
#include "MapDefs.h"
#include "MapDoc.h"
#include "MapFace.h"
#include "MapSolid.h"
#include "MapView2D.h"
#include "MapWorld.h"
#include "Options.h"
#include "Render2D.h"
#include "Render3D.h"
#include "RenderUtils.h"
#include "StatusBarIDs.h"		// dvs: remove
#include "ToolClipper.h"
#include "ToolManager.h"
#include "vgui/Cursor.h"
#include "Selection.h"

// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>


#pragma warning( disable:4244 )


//=============================================================================
//
// Friend Function (for MapClass->EnumChildren Callback)
//

//-----------------------------------------------------------------------------
// Purpose: This function creates a new clip group with the given solid as 
//          the original solid.
//   Input: pSolid - the original solid to put in the clip list
//          pClipper - the clipper tool
//  Output: successful?? (true/false)
//-----------------------------------------------------------------------------
BOOL AddToClipList( CMapSolid *pSolid, Clipper3D *pClipper )
{
    CClipGroup *pClipGroup = new CClipGroup;
    if( !pClipGroup )
        return false;

    pClipGroup->SetOrigSolid( pSolid );
    pClipper->m_ClipResults.AddToTail( pClipGroup );

    return true;
}


//=============================================================================
//
// CClipGroup
//

//-----------------------------------------------------------------------------
// Purpose: Destructor. Gets rid of the unnecessary clip solids.
//-----------------------------------------------------------------------------
CClipGroup::~CClipGroup()
{
	delete m_pClipSolids[0];
	delete m_pClipSolids[1];
}


//-----------------------------------------------------------------------------
// Purpose: constructor - initialize the clipper variables
//-----------------------------------------------------------------------------
Clipper3D::Clipper3D(void)
{
	m_Mode = FRONT;

    m_ClipPlane.normal.Init();
    m_ClipPlane.dist = 0.0f;
    m_ClipPoints[0].Init();
    m_ClipPoints[1].Init();
    m_ClipPointHit = -1;

    m_pOrigObjects = NULL;

	m_bDrawMeasurements = false;
	SetEmpty();
}


//-----------------------------------------------------------------------------
// Purpose: deconstructor
//-----------------------------------------------------------------------------
Clipper3D::~Clipper3D(void)
{
}


//-----------------------------------------------------------------------------
// Purpose: Called when the tool is activated.
// Input  : eOldTool - The ID of the previously active tool.
//-----------------------------------------------------------------------------
void Clipper3D::OnActivate()
{
	if (IsActiveTool())
	{
		//
		// Already the active tool - toggle the mode.
		//
        IterateClipMode();
	}
}


//-----------------------------------------------------------------------------
// Purpose: Called when the tool is deactivated.
// Input  : eNewTool - The ID of the tool that is being activated.
//-----------------------------------------------------------------------------
void Clipper3D::OnDeactivate()
{
	SetEmpty();
}


//-----------------------------------------------------------------------------
// Purpose: (virtual imp) This function handles the "dragging" of the mouse
//          while the left mouse button is depressed.  It updates the position
//          of the clippoing plane point selected in the StartTranslation
//          function.  This function rebuilds the clipping plane and updates
//          the clipping solids when necessary.
//   Input: pt - current location of the mouse in the 2DView
//          uFlags - constrained clipping plane point movement
//          *dragSize - not used in the virtual implementation
//  Output: success of translation (TRUE/FALSE)
//-----------------------------------------------------------------------------
bool Clipper3D::UpdateTranslation( const Vector &vUpdate, UINT uFlags )
{
    // sanity check
    if( IsEmpty() )
        return false;

	Vector vNewPos = m_vOrgPos + vUpdate;

    // snap point if need be
	if ( uFlags & constrainSnap )
		m_pDocument->Snap( vNewPos, uFlags );

    //
    // update clipping point positions
    //
	if ( m_ClipPoints[m_ClipPointHit] == vNewPos )
		return false;

    
    if( uFlags & constrainMoveAll )
    {
		//
		// calculate the point and delta - to move both clip points simultaneously
		//
		
		Vector delta = vNewPos - m_ClipPoints[m_ClipPointHit];
		m_ClipPoints[(m_ClipPointHit+1)%2] += delta;
    }

	m_ClipPoints[m_ClipPointHit] = vNewPos;

    // build the new clip plane and update clip results
    BuildClipPlane();

    GetClipResults();

	m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_TOOL );

    return true;
}


//-----------------------------------------------------------------------------
// Purpose: (virtual imp) This function defines all finishing functionality 
//          necessary at the end of a clipping action.  Nothing really!!!
// Input  : bSave - passed along the the Tool finish translation call
//-----------------------------------------------------------------------------
void Clipper3D::FinishTranslation( bool bSave )
{
    // get the clip results -- in case the update is a click and not a drag
    GetClipResults();

    Tool3D::FinishTranslation( bSave );
}


//-----------------------------------------------------------------------------
// Purpose: iterate through the types of clipping modes, update after an
//          iteration takes place to visualize the new clip results
//-----------------------------------------------------------------------------
void Clipper3D::IterateClipMode( void )
{
    //
    // increment the clipping mode (wrap when necessary)
    //
    m_Mode++;

    if( m_Mode > BOTH )
    {
        m_Mode = FRONT;
    }

    // update the clipped objects based on the mode
    GetClipResults();
}


//-----------------------------------------------------------------------------
// Purpose: This resets the solids to clip (the original list) and calls the
//          CalcClipResults function to generate new "clip" solids
//-----------------------------------------------------------------------------
void Clipper3D::GetClipResults( void )
{
    // reset the clip list to the original solid lsit
    SetClipObjects( m_pOrigObjects );

    // calculate the clipped objects based on the current "clip plane"
    CalcClipResults();
}


//-----------------------------------------------------------------------------
// Purpose: This function allows one to specifically set the clipping plane
//          information, as opposed to building a clip plane during "translation"
//   Input: pPlane - the plane information used to create the clip plane
//-----------------------------------------------------------------------------
void Clipper3D::SetClipPlane( PLANE *pPlane )
{
    //
    // copy the clipping plane info
    //
    m_ClipPlane.normal = pPlane->normal;
    m_ClipPlane.dist = pPlane->dist;
}


//-----------------------------------------------------------------------------
// Purpose: This function builds a clipping plane based on the clip point
//          locations manipulated in the "translation" functions and the 2DView
//-----------------------------------------------------------------------------
void Clipper3D::BuildClipPlane( void )
{
	// calculate the up vector
    Vector upVect = m_vPlaneNormal;
    
	// calculate the right vector
    Vector rightVect;
    VectorSubtract( m_ClipPoints[1], m_ClipPoints[0], rightVect );
    
    // calculate the forward (normal) vector
    Vector forwardVect;
    CrossProduct( upVect, rightVect, forwardVect );
    VectorNormalize( forwardVect );
    
    //
    // save the clip plane info
    //
    m_ClipPlane.normal = forwardVect;
    m_ClipPlane.dist = DotProduct( m_ClipPoints[0], forwardVect );
}


//-----------------------------------------------------------------------------
// Purpose: This functions sets up the list of objects to be clipped.  
//          Initially the list is passed in (typically a Selection set).  On 
//          subsequent "translation" updates the list is refreshed from the 
//          m_pOrigObjects list.
//   Input: pList - the list of objects (solids) to be clipped
//-----------------------------------------------------------------------------
void Clipper3D::SetClipObjects( const CMapObjectList *pList )
{
    // check for an empty list
    if( !pList )
        return;

    // save the original list
    m_pOrigObjects = pList;

    // clear the clip results list
    ResetClipResults();

    //
    // copy solids into the clip list
    //
    FOR_EACH_OBJ( *m_pOrigObjects, pos )
    {
        CMapClass *pObject = m_pOrigObjects->Element( pos );
        if( !pObject )
            continue;

        if( pObject->IsMapClass( MAPCLASS_TYPE( CMapSolid ) ) )
        {
            AddToClipList( ( CMapSolid* )pObject, this );
        }

        pObject->EnumChildren( ENUMMAPCHILDRENPROC( AddToClipList ), DWORD( this ), MAPCLASS_TYPE( CMapSolid ) );
    }

    // the clipping list is not empty anymore
    m_bEmpty = false;
}


//-----------------------------------------------------------------------------
// Purpose: This function calculates based on the defined or given clipping
//          plane and clipping mode the new clip solids.
//-----------------------------------------------------------------------------
void Clipper3D::CalcClipResults( void )
{
    // sanity check
    if( IsEmpty() )
        return;

    //
    // iterate through and clip all of the solids in the clip list
    //
    FOR_EACH_OBJ( m_ClipResults, pos )
    {
        CClipGroup *pClipGroup = m_ClipResults.Element( pos );
        CMapSolid *pOrigSolid = pClipGroup->GetOrigSolid();
        if( !pOrigSolid )
            continue;

        //
        // check the modes for which solids to generate
        //
        CMapSolid *pFront = NULL;
        CMapSolid *pBack = NULL;
        if( m_Mode == FRONT )
        {
            pOrigSolid->Split( &m_ClipPlane, &pFront, NULL );
        }
        else if( m_Mode == BACK )
        {
            pOrigSolid->Split( &m_ClipPlane, NULL, &pBack );
        }
        else if( m_Mode == BOTH )
        {
            pOrigSolid->Split( &m_ClipPlane, &pFront, &pBack );
        }

        if( pFront )
        {
			pFront->SetTemporary(true);
            pClipGroup->SetClipSolid( pFront, FRONT );
        }

        if( pBack )
        {
			pBack->SetTemporary(true);
            pClipGroup->SetClipSolid( pBack, BACK );
        }
    }
}


//-----------------------------------------------------------------------------
// Purpose: This function handles the removal of the "original" solid when it
//          has been clipped into new solid(s) or removed from the world (group
//          or entity) entirely.  It handles this in an undo safe fashion.
//   Input: pOrigSolid - the solid to remove
//-----------------------------------------------------------------------------
void Clipper3D::RemoveOrigSolid( CMapSolid *pOrigSolid )
{
	m_pDocument->DeleteObject(pOrigSolid);

    //
    // remove the solid from the selection set if in the seleciton set and
    // its parent is the world, or set the selection state to none parent is group
    // or entity in the selection set
    //    

	CSelection *pSelection = m_pDocument->GetSelection();

    if ( pSelection->IsSelected( pOrigSolid ) )
    {
        pSelection->SelectObject( pOrigSolid, scUnselect );
    }
    else
    {
        pOrigSolid->SetSelectionState( SELECT_NONE );
    }
}


//-----------------------------------------------------------------------------
// Purpose: This function handles the saving of newly clipped solids (derived
//          from an "original" solid).  It handles them in an undo safe fashion.
//   Input: pSolid - the newly clipped solid
//          pOrigSolid - the "original" solid or solid the clipped solid was
//                       derived from
//-----------------------------------------------------------------------------
void Clipper3D::SaveClipSolid( CMapSolid *pSolid, CMapSolid *pOrigSolid )
{
    //
    // no longer a temporary solid
    //
    pSolid->SetTemporary( FALSE );

    //
    // Add the new solid to the original solid's parent (group, entity, world, etc.).
    //
	m_pDocument->AddObjectToWorld(pSolid, pOrigSolid->GetParent());
    
    //
    // handle linking solid into selection -- via selection set when parent is the world
    // and selected, or set the selection state if parent is group or entity in selection set
    //
    if( m_pDocument->GetSelection()->IsSelected( pOrigSolid ) )
    {
        m_pDocument->SelectObject( pSolid, scSelect );
    }
    else
    {
        pSolid->SetSelectionState( SELECT_NORMAL );
    }

    GetHistory()->KeepNew( pSolid );
}


//-----------------------------------------------------------------------------
// Purpose: This function saves all the clipped solid information.  If new solids
//          were generated from the original, they are saved and the original is
//          set for desctruciton.  Otherwise, the original solid is kept.
//-----------------------------------------------------------------------------
void Clipper3D::SaveClipResults( void )
{
    // sanity check!
    if( IsEmpty() )
        return;

	// mark this place in the history
    GetHistory()->MarkUndoPosition( NULL, "Clip Objects" );

    //
    // save all new objects into the selection list
    //
    FOR_EACH_OBJ( m_ClipResults, pos )
    {
        CClipGroup *pClipGroup = m_ClipResults.Element( pos );
        if( !pClipGroup )
            continue;

        CMapSolid *pOrigSolid = pClipGroup->GetOrigSolid();
        CMapSolid *pBackSolid = pClipGroup->GetClipSolid( CClipGroup::BACK );
        CMapSolid *pFrontSolid = pClipGroup->GetClipSolid( CClipGroup::FRONT );

        //
        // save the front clip solid and clear the clip results list of itself
        //
        if( pFrontSolid )
        {
            SaveClipSolid( pFrontSolid, pOrigSolid );
            pClipGroup->SetClipSolid( NULL, CClipGroup::FRONT );
        }

        //
        // save the front clip solid and clear the clip results list of itself
        //
        if( pBackSolid )
        {
            SaveClipSolid( pBackSolid, pOrigSolid );
            pClipGroup->SetClipSolid( NULL, CClipGroup::BACK );
        }
     
		// Send the notification that this solid as been clipped.
		pOrigSolid->PostUpdate( Notify_Clipped );

        // remove the original solid
        RemoveOrigSolid( pOrigSolid );
    }

    // set the the clipping results list as empty
    ResetClipResults();

	// update world and views
	
    m_pDocument->SetModifiedFlag();
}


//-----------------------------------------------------------------------------
// Purpose: Draws the measurements of a brush in the 2D view.
// Input  : pRender - 
//			pSolid - 
//			nFlags - 
//-----------------------------------------------------------------------------
void Clipper3D::DrawBrushExtents( CRender2D *pRender, CMapSolid *pSolid, int nFlags )
{
    //
    // get the bounds of the solid
    //
	Vector Mins, Maxs;
	pSolid->GetRender2DBox( Mins, Maxs );

	//
	// Determine which side of the clipping plane this solid is on in screen
	// space. This tells us where to draw the extents.
	//
    if( ( m_ClipPlane.normal[0] == 0 ) && ( m_ClipPlane.normal[1] == 0 ) && ( m_ClipPlane.normal[2] == 0 ) )
        return;

    Vector normal = m_ClipPlane.normal;

    if( nFlags & DBT_BACK )
    {
       VectorNegate( normal );
    }

	Vector2D planeNormal;

    pRender->TransformNormal( planeNormal, normal );

	if( planeNormal.x <= 0 )
    {
        nFlags &= ~DBT_RIGHT;
        nFlags |= DBT_LEFT;
    }
    else if( planeNormal.x > 0 )
    {
        nFlags &= ~DBT_LEFT;
        nFlags |= DBT_RIGHT;
    }

    if( planeNormal.y <= 0 )
    {
        nFlags &= ~DBT_BOTTOM;
        nFlags |= DBT_TOP;
    }
    else if( planeNormal.y > 0 )
    {
        nFlags &= ~DBT_TOP;
        nFlags |= DBT_BOTTOM;
    }

	DrawBoundsText(pRender, Mins, Maxs, nFlags);
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pRender - 
//-----------------------------------------------------------------------------
void Clipper3D::RenderTool2D(CRender2D *pRender)
{
	if ( IsEmpty() )
		return;

    // check flag for rendering vertices
    bool bDrawVerts = ( bool )( Options.view2d.bDrawVertices == TRUE );

    // setup the line to use

	pRender->SetDrawColor( 255, 255, 255 );

    //
    // render the clipped solids
    //
    FOR_EACH_OBJ( m_ClipResults, pos )
    {
        CClipGroup *pClipGroup = m_ClipResults.Element( pos );
        CMapSolid *pClipBack = pClipGroup->GetClipSolid( CClipGroup::BACK );
        CMapSolid *pClipFront = pClipGroup->GetClipSolid( CClipGroup::FRONT );
        if( !pClipBack && !pClipFront )
            continue;

        //
        // draw clip solids with the extents
        //
        if( pClipBack )
        {
            int faceCount = pClipBack->GetFaceCount();
            for( int i = 0; i < faceCount; i++ )
            {
                CMapFace *pFace = pClipBack->GetFace( i );

				// size 4
                pRender->DrawPolyLine( pFace->nPoints, pFace->Points );

				if ( bDrawVerts )
				{
					pRender->DrawHandles( pFace->nPoints, pFace->Points );
				}

                if( m_bDrawMeasurements )
                {
                    DrawBrushExtents( pRender, pClipBack, DBT_TOP | DBT_LEFT | DBT_BACK );
                }
            }
        }

        if( pClipFront )
        {
            int faceCount = pClipFront->GetFaceCount();
            for( int i = 0; i < faceCount; i++ )
            {
                CMapFace *pFace = pClipFront->GetFace( i );

                pRender->DrawPolyLine( pFace->nPoints, pFace->Points );

				if ( bDrawVerts )
				{
					pRender->DrawHandles( pFace->nPoints, pFace->Points );
				}

                if( m_bDrawMeasurements )
                {
                    DrawBrushExtents( pRender, pClipFront, DBT_BOTTOM | DBT_RIGHT );
                }
            }
        }
	}

    //
	// draw the clip-plane
	//
	pRender->SetDrawColor( 0, 255, 255 );
	pRender->DrawLine( m_ClipPoints[0], m_ClipPoints[1] );

	//
	// draw the clip-plane endpoints
	//

	pRender->SetHandleStyle( HANDLE_RADIUS, CRender::HANDLE_SQUARE );
	pRender->SetHandleColor( 255, 255, 255 );

    pRender->DrawHandle( m_ClipPoints[0] );
    pRender->DrawHandle( m_ClipPoints[1] );
}


//-----------------------------------------------------------------------------
// Purpose: Renders the brushes that will be left by the clipper in white
//			wireframe.
// Input  : pRender - Rendering interface.
//-----------------------------------------------------------------------------
void Clipper3D::RenderTool3D( CRender3D *pRender )
{
    // is there anything to render?
    if( m_bEmpty )
        return;

    //
    // setup the renderer
    //
    pRender->PushRenderMode( RENDER_MODE_WIREFRAME );
    
    FOR_EACH_OBJ( m_ClipResults, pos )
    {
        CClipGroup *pClipGroup = m_ClipResults.Element( pos );

        CMapSolid *pFrontSolid = pClipGroup->GetClipSolid( CClipGroup::FRONT );
        if( pFrontSolid )
        {
			color32 rgbColor = pFrontSolid->GetRenderColor();
            pFrontSolid->SetRenderColor(255, 255, 255);
            pFrontSolid->Render3D(pRender);
            pFrontSolid->SetRenderColor(rgbColor);
        }

        CMapSolid *pBackSolid = pClipGroup->GetClipSolid( CClipGroup::BACK );
        if( pBackSolid )
        {
			color32 rgbColor = pBackSolid->GetRenderColor();
            pBackSolid->SetRenderColor(255, 255, 255);
            pBackSolid->Render3D(pRender);
            pBackSolid->SetRenderColor(rgbColor);
        }
    }

    pRender->PopRenderMode();
}


//-----------------------------------------------------------------------------
// Purpose: (virtual imp)
// Input  : pt - 
//			BOOL - 
// Output : int
//-----------------------------------------------------------------------------
int Clipper3D::HitTest(CMapView *pView, const Vector2D &ptClient, bool bTestHandles)
{
    // check points
    
	for ( int i=0; i<2;i++ )
	{
		if ( HitRect(pView, ptClient, m_ClipPoints[i], HANDLE_RADIUS) )
		{
			return i+1; // return clip point index + 1
		}
	}
    
    // neither point hit
    return 0;
}

//-----------------------------------------------------------------------------
// Purpose: Reset (clear) the clip results.
//-----------------------------------------------------------------------------
void Clipper3D::ResetClipResults( void )
{
    //
    // delete the clip solids held in the list -- originals are just pointers
    // to pre-existing objects
    //
    FOR_EACH_OBJ( m_ClipResults, pos )
    {
        CClipGroup *pClipGroup = m_ClipResults.Element(pos);

        if( pClipGroup )
        {
            delete pClipGroup;
        }
    }

	m_ClipResults.RemoveAll();

    // the clipping list is empty
    SetEmpty();
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : nChar - 
//			nRepCnt - 
//			nFlags - 
//-----------------------------------------------------------------------------
bool Clipper3D::OnKeyDown2D(CMapView2D *pView, UINT nChar, UINT nRepCnt, UINT nFlags) 
{
	switch (nChar)
	{
		case 'O':
		{
			//
			// Toggle the rendering of measurements.
			//
			ToggleMeasurements();
			return true;
		}

		case VK_RETURN:
		{
			//
			// Do the clip.
			//
			if (!IsEmpty() )
			{
				SaveClipResults();
			}
			return true;
		}

		case VK_ESCAPE:
		{
			OnEscape();
			return true;
		}
	}

	return false;	
}


//-----------------------------------------------------------------------------
// 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 Clipper3D::OnLMouseDown2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint)
{
	Tool3D::OnLMouseDown2D(pView, nFlags, vPoint);

	unsigned int uConstraints = GetConstraints( nFlags );

	//
	// Convert point to world coords.
	//
	Vector vecWorld;
	pView->ClientToWorld(vecWorld, vPoint);
	vecWorld[pView->axThird] = COORD_NOTINIT;

	// getvisiblepoint fills in any coord that's still set to COORD_NOTINIT:
	m_pDocument->GetBestVisiblePoint(vecWorld);

	// snap starting position to grid
	if ( uConstraints & constrainSnap )
		m_pDocument->Snap(vecWorld, uConstraints);
	
	
	bool bStarting = false;

	// if the tool is not empty, and shift is not held down (to
	//  start a new camera), don't do anything.
	if(!IsEmpty())
	{
		// test for clip point hit (result = {0, 1, 2}
		int hitPoint = HitTest( pView, vPoint );

		if ( hitPoint > 0 )
		{
			// test for clip point hit (result = {0, 1, -1})
			m_ClipPointHit = hitPoint-1; // convert back to index
			m_vOrgPos = m_ClipPoints[m_ClipPointHit];
			StartTranslation( pView, vPoint );
		}
		else if ( m_vPlaneNormal != pView->GetViewAxis() )
		{
			SetEmpty();
			bStarting = true;
		}
		else
		{
			if (nFlags & MK_SHIFT)
			{
				SetEmpty();
				bStarting = true;
			}
			else
			{
				return true; // do nothing;
			}
		}
	}
	else
	{
		bStarting = true;
	}

	SetClipObjects(m_pDocument->GetSelection()->GetList());

	if (bStarting)
	{
		// start the tools translation functionality
		StartTranslation( pView, vPoint );

		// set the initial clip points
		m_ClipPointHit = 0;
		m_ClipPoints[0] = vecWorld; 
		m_ClipPoints[1] = vecWorld;
		m_vOrgPos = vecWorld;
	}

	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 Clipper3D::OnLMouseUp2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint)
{
	Tool3D::OnLMouseUp2D(pView, nFlags, vPoint);

	if ( IsTranslating() )
	{
		FinishTranslation(true);
	}

	m_pDocument->UpdateStatusbar();
	
	return true;
}

unsigned int Clipper3D::GetConstraints(unsigned int nKeyFlags)
{
	unsigned int uConstraints = Tool3D::GetConstraints( nKeyFlags );

	if(nKeyFlags & MK_CONTROL)
	{
		uConstraints |= constrainMoveAll;
	}

	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 Clipper3D::OnMouseMove2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint)
{
	vgui::HCursor hCursor = vgui::dc_arrow;
	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())
	{
		// cursor is cross here
		Tool3D::UpdateTranslation( pView, vPoint, uConstraints);

		hCursor = vgui::dc_none;
	}
	else if (!IsEmpty())
	{
		//
		// If the cursor is on a handle, set it to a cross.
		//
		if (HitTest( pView, vPoint, true))
		{
			hCursor = vgui::dc_crosshair;
		}
	}

	if ( hCursor != vgui::dc_none  )
		pView->SetCursor( hCursor );

	return true;
}


//-----------------------------------------------------------------------------
// Purpose: Handles character events.
// Input  : Per CWnd::OnKeyDown.
// Output : Returns true if the message was handled, false if not.
//-----------------------------------------------------------------------------
bool Clipper3D::OnKeyDown3D(CMapView3D *pView, UINT nChar, UINT nRepCnt, UINT nFlags)
{
	switch (nChar)
	{
		case VK_RETURN:
		{
			if (!IsEmpty()) // dvs: what does isempty mean for the clipper?
			{
				SaveClipResults();
			}
			return true;
		}

		case VK_ESCAPE:
		{
			OnEscape();
			return true;
		}
	}

	return false;
}


//-----------------------------------------------------------------------------
// Purpose: Handles the escape key in the 2D or 3D views.
//-----------------------------------------------------------------------------
void Clipper3D::OnEscape(void)
{
	// If we're clipping, clear it
	if (!IsEmpty())
	{
		SetEmpty();
	}
	else
	{
		m_pDocument->GetTools()->SetTool(TOOL_POINTER);
	}
}