Windows2000/private/windows/shell/accesory/mspaint/t_poly.cpp
2020-09-30 17:12:32 +02:00

600 lines
18 KiB
C++

/* T_POLY.CPP: IMPLEMENTATION OF THE CPolygonTool CLASS */
/* Methods in this file */
/* Polygon Tool Class Object */
/* CPolygonTool::CPolygonTool */
/* CPolygonTool::~CPolygonTool */
/* CPolygonTool::DeleteArrayContents */
/* CPolygonTool::AdjustBoundingRect */
/* CPolygonTool::CopyPointsToMemArray */
/* CPolygonTool::AddPoint */
/* CPolygonTool::SetCurrentPoint */
/* CPolygonTool::RenderInProgress */
/* CPolygonTool::RenderFinal */
/* CPolygonTool::SetupPenBrush */
/* CPolygonTool::AdjustPointsForConstraint */
/* CPolygonTool::PreProcessPoints */
/* CPolygonTool::Render */
/* CPolygonTool::OnStartDrag */
/* CPolygonTool::OnEndDrag */
/* CPolygonTool::OnDrag */
/* CPolygonTool::OnCancel */
/* CPolygonTool::CanEndMultiptOperation */
/* CPolygonTool::EndMultiptOperation */
/* Briefly, This object stores the points of the polygon in a CObArray of */
/* CPoint Objects. For the in progress drawing, it calls PolyLine. When the */
/* polygon is closed or completed (by the user doubleclicking => asking us to */
/* close it), Polygon is called on the same points. */
/* The last point in the array of points is always the point the current line */
/* is being drawn to. The first time 2 points are added (the Anchor/first */
/* point, and the point the line is being drawn to) It does happen that this */
/* first time, they are the same point. It is necessary that the first time */
/* 2 points are added, since subsequent times, new points are not added, but */
/* the last point is reset. */
#include "stdafx.h"
#include "global.h"
#include "pbrush.h"
#include "pbrusdoc.h"
#include "pbrusfrm.h"
#include "bmobject.h"
#include "imgsuprt.h"
#include "imgwnd.h"
#include "imgwell.h"
#include "t_poly.h"
#ifdef _DEBUG
#undef THIS_FILE
static CHAR BASED_CODE THIS_FILE[] = __FILE__;
#endif
IMPLEMENT_DYNAMIC(CPolygonTool, CClosedFormTool)
#include "memtrace.h"
extern CLineTool NEAR g_lineTool;
CPolygonTool NEAR g_polygonTool;
extern MTI NEAR mti;
CPolygonTool::CPolygonTool()
{
m_nCmdID = IDMB_POLYGONTOOL;
m_cRectBounding.SetRectEmpty();
m_bMultPtOpInProgress = FALSE;
m_nStrokeWidth = 1;
}
CPolygonTool::~CPolygonTool()
{
m_cRectBounding.SetRectEmpty();
DeleteArrayContents();
}
/* delete all cpoint objects allocated and stored in the array */
/* also free any memory associated with the array */
void CPolygonTool::DeleteArrayContents(void)
{
int iSize = (int)m_cObArrayPoints.GetSize();
CPoint *pcPoint;
for (int i = 0; i < iSize; i++)
{
pcPoint= (CPoint *)m_cObArrayPoints.GetAt( i );
delete pcPoint;
}
m_cObArrayPoints.RemoveAll();
}
/* recalculate the bounding rectangle for the polyline/polygon */
void CPolygonTool::AdjustBoundingRect(void)
{
int iSize = (int)m_cObArrayPoints.GetSize();
CPoint *pcPoint;
int iStrokeWidth = GetStrokeWidth();
int i;
if (iSize >= 1)
{
pcPoint= (CPoint*)m_cObArrayPoints.GetAt( 0 );
//set the rect to equal the 1st value
m_cRectBounding.SetRect(pcPoint->x, pcPoint->y, pcPoint->x, pcPoint->y);
}
for (i = 1; i < iSize; i++)
{
pcPoint = (CPoint *)m_cObArrayPoints.GetAt( i );
m_cRectBounding.SetRect( min( pcPoint->x, m_cRectBounding.left ),
min( pcPoint->y, m_cRectBounding.top ),
max( pcPoint->x, m_cRectBounding.right ),
max( pcPoint->y, m_cRectBounding.bottom ) );
}
// Adjust for width of current drawing line/border
m_cRectBounding.OffsetRect ( -(iStrokeWidth / 2), -(iStrokeWidth / 2) );
m_cRectBounding.InflateRect( iStrokeWidth , iStrokeWidth);
}
/* This method will copy the CObArray structure of CPoints to a contiguous */
/* memory block of CPoint Structures */
BOOL CPolygonTool::CopyPointsToMemArray(CPoint **pcPoint, int *piNumElements)
{
BOOL bRC = TRUE;
int i;
int iSize = (int)m_cObArrayPoints.GetSize();
if (! iSize)
{
*piNumElements = 0;
*pcPoint = NULL;
return TRUE;
}
TRY
{
*pcPoint = new CPoint[iSize];
if (*pcPoint == NULL)
{
AfxThrowMemoryException();
}
for (i=0; i < iSize; i++)
{
(*pcPoint)[i] = *((CPoint*) (m_cObArrayPoints[i]));
}
*piNumElements = iSize;
}
CATCH(CMemoryException,e)
{
*piNumElements = 0;
bRC = FALSE;
}
END_CATCH
return bRC;
}
/* This routine can Throw a CMemoryException!! */
/* It adds a new point to the end of the array, possibly increasing the size */
void CPolygonTool::AddPoint(POINT ptNewPoint)
{
CPoint *pcPoint;
pcPoint = new CPoint(ptNewPoint);
if (pcPoint == NULL)
{
AfxThrowMemoryException();
}
m_cObArrayPoints.Add((CObject *)pcPoint);
AdjustBoundingRect();
}
/* This method changes the value of the last point in the array. It does not */
/* remove the point and add a new one. It just modifies it in place */
void CPolygonTool::SetCurrentPoint(POINT ptNewPoint)
{
int iLast = (int)m_cObArrayPoints.GetUpperBound();
if (iLast >= 0)
{
CPoint *pcPoint = (CPoint *) m_cObArrayPoints[iLast];
pcPoint->x = ptNewPoint.x;
pcPoint->y = ptNewPoint.y;
AdjustBoundingRect();
}
}
/* Render In Progress is called for all drawing during the multi-pt operation */
/* The only difference between this method and RenderFinal is that it calls */
/* polyline and RenderFinal calls polygon. */
void CPolygonTool::RenderInProgress(CDC* pDC)
{
CPoint *pcPointArray;
int iNumElements;
if (CopyPointsToMemArray( &pcPointArray, &iNumElements ))
{
pDC->Polyline(pcPointArray, iNumElements);
delete [] pcPointArray;
}
}
/* Render Final is called at the end of the multi-pt drawing mode. The only */
/* difference between this method and RenderInProgress is that it calls */
/* polygon and RenderInProgress calls polyline. */
void CPolygonTool::RenderFinal(CDC* pDC)
{
CPoint *pcPointArray;
int iNumElements;
if (CopyPointsToMemArray(&pcPointArray, &iNumElements))
{
// Remove RIP with only 2 points
if (iNumElements > 2)
pDC->Polygon(pcPointArray, iNumElements);
delete [] pcPointArray;
}
}
/* This routine is called before rendering onto the DC. It basically, calls */
/* the default setup to setup the pen and brush, and then overrides the Pen if*/
/* drawing in progress and drawing without any border. This case is necessary*/
/* since if you do not have a border, you need to see something during the in */
/* progress drawing mode. It uses the inverse (not) of the screen color as */
/* the border in this mode. */
BOOL CPolygonTool::SetupPenBrush(HDC hDC, BOOL bLeftButton, BOOL bSetup, BOOL bCtrlDown)
{
static int iOldROP2Code;
static BOOL bCurrentlySetup = FALSE;
BOOL bRC = CClosedFormTool::SetupPenBrush(hDC, bLeftButton, bSetup, bCtrlDown);
// for multipt operations in progress (e.g. drawing outline, not fill yet
// if there is no border, use the not of the screen color for the border.
// When bMultiptopinprogress == FALSE, final drawing, we will use a null
// pen and thus have no border.
if (m_bMultPtOpInProgress)
{
if (bSetup)
{
if (! bCurrentlySetup)
{
bCurrentlySetup = TRUE;
// if no border, draw inprogress border as inverse of screen color
if (! m_bBorder)
iOldROP2Code = SetROP2(hDC, R2_NOT);
}
else
// Error: Will lose allocated Brush/Pen
bRC = FALSE;
}
else
{
if (bCurrentlySetup)
{
bCurrentlySetup = FALSE;
// if no border, restore drawing mode
if (! m_bBorder)
SetROP2(hDC, iOldROP2Code);
}
else
// Error: Cannot Free/cleanup Brush/Pen -- Never allocated.
bRC = FALSE;
}
}
return bRC;
}
/* Call the line's adjustpointsforconstraint member function */
void CPolygonTool::AdjustPointsForConstraint(MTI *pmti)
{
g_lineTool.AdjustPointsForConstraint(pmti);
}
// ptDown must be anchor point for our line, not where we did mouse button down
void CPolygonTool::PreProcessPoints(MTI *pmti)
{
int iLast = (int)m_cObArrayPoints.GetUpperBound();
if (iLast > 0)
iLast--;
CPoint* pcPoint;
if (iLast >= 0)
{
pcPoint = (CPoint *)m_cObArrayPoints[iLast];
pmti->ptDown = *pcPoint;
}
CClosedFormTool::PreProcessPoints(pmti);
}
/* Render sets up the pen and brush, and then calls either RenderInProgress */
/* or RenderFinal. RenderInProgress is called if in the middle of a multipt */
/* operation, and RenderFinal is called when a multipt operation is complete */
/* The pen and brush is set up exactly the same as the parent routine in */
/* CRubberTool */
void CPolygonTool::Render(CDC* pDC, CRect& rect, BOOL bDraw, BOOL bCommit, BOOL bCtrlDown)
{
// Setup Pen/Brush
SetupPenBrush(pDC->m_hDC, bDraw, TRUE, bCtrlDown);
if (m_bMultPtOpInProgress)
{
RenderInProgress(pDC);
}
else
{
RenderFinal(pDC);
}
// Cleanup Pen/Brush
SetupPenBrush(pDC->m_hDC, bDraw, FALSE, bCtrlDown);
// Need to return the bounding rect
rect = m_cRectBounding;
}
void CPolygonTool::OnActivate( BOOL bActivate )
{
if (! bActivate && m_bMultPtOpInProgress)
{
if (m_pImgWnd != NULL)
if (m_cObArrayPoints.GetSize() > 1)
{
OnStartDrag( m_pImgWnd, &m_MTI );
OnEndDrag ( m_pImgWnd, &m_MTI );
m_MTI.ptPrev = m_MTI.pt;
EndMultiptOperation(); // end the multipt operation
OnEndDrag( m_pImgWnd, &m_MTI );
mti.fLeft = FALSE;
mti.fRight = FALSE;
}
else
OnCancel( m_pImgWnd );
else
EndMultiptOperation( TRUE );
}
m_pImgWnd = NULL;
if (bActivate)
{
m_nStrokeWidth = g_nStrokeWidth;
}
else
{
g_nStrokeWidth = m_nStrokeWidth;
}
CImgTool::OnActivate( bActivate );
}
void CPolygonTool::OnEnter( CImgWnd* pImgWnd, MTI* pmti )
{
m_pImgWnd = NULL;
}
void CPolygonTool::OnLeave( CImgWnd* pImgWnd, MTI* pmti )
{
m_pImgWnd = pImgWnd;
}
/* On Start Drag is called on mouse button down. We basically call on Start */
/* Drag of the parent (default) class after adding in our point(s) into the */
/* array of points. If this is the first point (i.e. bMultiptOpInProgress == */
/* False, then we need 2 points in our array, and we can call the default */
/* OnStartDrag. If it is not the first point, then we just add the new point */
/* and call our OnDrag. In either case, OnDrag is called which eventually */
/* calls render to do our drawing on the mouse down */
void CPolygonTool::OnStartDrag( CImgWnd* pImgWnd, MTI* pmti )
{
TRY {
if (m_bMultPtOpInProgress)
{
CRect rect;
CPoint pt = pmti->pt;
pImgWnd->ImageToClient( pt );
pImgWnd->GetClientRect( &rect );
if (rect.PtInRect( pt ))
{
AddPoint( pmti->pt );
OnDrag( pImgWnd, pmti );
}
}
else
{
DeleteArrayContents();
m_cRectBounding.SetRectEmpty();
AddPoint( pmti->pt );
// must set m_bmultptopinprogress prior to calling onstartdrag
// since that calls render,and render will call renderinprogress
// or renderfinal depending on the sate of this variable.
m_bMultPtOpInProgress = TRUE;
// No Mult Pt In Progress => 1st Click
// add a 2nd point, last point is what we are draing to
// 1st point is anchor. 1st time, need 2 points to draw a line
// subsequent times, just re-use last point as anchor and only one
// more point is added (above outside test for m_bmultptopinprogress)
AddPoint( pmti->pt );
CClosedFormTool::OnStartDrag( pImgWnd, pmti );
}
}
CATCH(CMemoryException,e)
{
}
END_CATCH
}
/* On End Drag is sent on a mouse button up. This basically is a clone of the*/
/* CRubberTool::OnEndDrag method, except that we use our bounding rect for all*/
/* the image invalidation, and commit, and undo function calls. */
/* if we are in the middle of a multipoint operation, we do not want to call */
/* all the routines to fix the drawing (e.g. invalImgRect, CommitImgRect, */
/* FinishUndo). We just want to save the current point, render, and return */
void CPolygonTool::OnEndDrag( CImgWnd* pImgWnd, MTI* pmti )
{
PreProcessPoints(pmti);
SetCurrentPoint(pmti->pt);
if (m_bMultPtOpInProgress)
{
m_MTI = *pmti;
// can't call OnDrag for this object/class, since it calls preprocesspt
// again, and then onDrag. If you call preproces again, you will lose
// bounding rectange box prev, and not be able to invalidate / repaint
// Still have to invalidate bounding rect, since if rect is larger than
// current rect, must invalidate to paint. E.g. If let off shift, then
// let off button, end point would be adjusted and bouning rect would
// also be correct, but rect calculated in CClosedFormTool::OnDrag is
// incorrect.
InvalImgRect(pImgWnd->m_pImg, &m_cRectBounding);
CClosedFormTool::OnDrag(pImgWnd, pmti);
return;
}
if (! m_cObArrayPoints.GetSize())
return;
OnDrag(pImgWnd, pmti); // one last time to refresh display in prep for final render
Render(CDC::FromHandle(pImgWnd->m_pImg->hDC), m_cRectBounding, pmti->fLeft, TRUE, pmti->fCtrlDown);
InvalImgRect(pImgWnd->m_pImg, &m_cRectBounding);
CommitImgRect(pImgWnd->m_pImg, &m_cRectBounding);
pImgWnd->FinishUndo(m_cRectBounding);
ClearStatusBarSize();
CImgTool::OnEndDrag(pImgWnd, pmti);
}
/* On Drag is sent when the mouse is moved with the button down. We basically*/
/* save the current point, and call the base class processing. Since the base*/
/* class processing invalidates the rect on the screen and cleans it up so we */
/* can paint a new line, we have to adjust the previous rectangle to be the */
/* bounding rectangle of our polyline. If we did not do this, our previous */
/* drawing would not get erased, and we would be drawing our new line over */
/* part of the previous line. The default processing finally calls Render */
/* which since our render is virtual, will call our render method above. */
void CPolygonTool::OnDrag( CImgWnd* pImgWnd, MTI* pmti )
{
PreProcessPoints ( pmti );
SetCurrentPoint ( pmti->pt );
SetStatusBarPosition( pmti->pt );
SetStatusBarSize ( m_cRectBounding.Size() );
CClosedFormTool::OnDrag(pImgWnd, pmti);
}
/* On Cancel is sent when the user aborts an operation while in progress */
/* EndMultiptOperation with TRUE will do all our cleanup */
void CPolygonTool::OnCancel(CImgWnd* pImgWnd)
{
InvalImgRect( pImgWnd->m_pImg, &m_cRectBounding );
EndMultiptOperation(TRUE);
CClosedFormTool::OnCancel(pImgWnd);
}
/* If point is on 1st point (i.e. closes the polygon) then can end is true */
// Use the stroke width to determine the width of the line and whether the */
/* end point touches the beginning point because of the line thickness */
BOOL CPolygonTool::CanEndMultiptOperation(MTI* pmti )
{
CPoint *pcPoint = (CPoint *) m_cObArrayPoints[0];
CSize cSizeDiff = (*pcPoint) - pmti->pt;
int iStrokeWidth = GetStrokeWidth() * 2;
m_bMultPtOpInProgress = ! ((abs( cSizeDiff.cx ) <= iStrokeWidth)
&& (abs( cSizeDiff.cy ) <= iStrokeWidth));
return ( TRUE );
}
/* If bAbort is true, this means an error occurred, or the user cancelled the */
/* multipoint operation in the middle of it. We need to clean up the */
/* allocated memory in our array of points. */
void CPolygonTool::EndMultiptOperation( BOOL bAbort )
{
if (bAbort)
{
DeleteArrayContents();
}
CClosedFormTool::EndMultiptOperation();
}
void CPolygonTool::OnUpdateColors( CImgWnd* pImgWnd )
{
if (m_cObArrayPoints.GetSize() && m_bMultPtOpInProgress)
{
OnStartDrag( pImgWnd, &m_MTI );
OnEndDrag ( pImgWnd, &m_MTI );
}
}