// $Id: opdragbx.cpp 1282 2006-06-09 09:46:49Z alex $ /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE ================================XARAHEADERSTART=========================== Xara LX, a vector drawing and manipulation program. Copyright (C) 1993-2006 Xara Group Ltd. Copyright on certain contributions may be held in joint with their respective authors. See AUTHORS file for details. LICENSE TO USE AND MODIFY SOFTWARE ---------------------------------- This file is part of Xara LX. Xara LX is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. Xara LX and its component source files are distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Xara LX (see the file GPL in the root directory of the distribution); if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ADDITIONAL RIGHTS ----------------- Conditional upon your continuing compliance with the GNU General Public License described above, Xara Group Ltd grants to you certain additional rights. The additional rights are to use, modify, and distribute the software together with the wxWidgets library, the wxXtra library, and the "CDraw" library and any other such library that any version of Xara LX relased by Xara Group Ltd requires in order to compile and execute, including the static linking of that library to XaraLX. In the case of the "CDraw" library, you may satisfy obligation under the GNU General Public License to provide source code by providing a binary copy of the library concerned and a copy of the license accompanying it. Nothing in this section restricts any of the rights you have under the GNU General Public License. SCOPE OF LICENSE ---------------- This license applies to this program (XaraLX) and its constituent source files only, and does not necessarily apply to other Xara products which may in part share the same code base, and are subject to their own licensing terms. This license does not apply to files in the wxXtra directory, which are built into a separate library, and are subject to the wxWindows license contained within that directory in the file "WXXTRA-LICENSE". This license does not apply to the binary libraries (if any) within the "libs" directory, which are subject to a separate license contained within that directory in the file "LIBS-LICENSE". ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS ---------------------------------------------- Subject to the terms of the GNU Public License (see above), you are free to do whatever you like with your modifications. However, you may (at your option) wish contribute them to Xara's source tree. You can find details of how to do this at: http://www.xaraxtreme.org/developers/ Prior to contributing your modifications, you will need to complete our contributor agreement. This can be found at: http://www.xaraxtreme.org/developers/contribute/ Please note that Xara will not accept modifications which modify any of the text between the start and end of this header (marked XARAHEADERSTART and XARAHEADEREND). MARKS ----- Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara designs are registered or unregistered trademarks, design-marks, and/or service marks of Xara Group Ltd. All rights in these marks are reserved. Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK. http://www.xara.com/ =================================XARAHEADEREND============================ */ /* opdragbx.cpp Base-class for easy drag box operations. */ #include "camtypes.h" #include "opdragbx.h" //#include "app.h" //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "rndrgn.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "stockcol.h" // Revision information. DECLARE_SOURCE("$Revision: 1282 $"); // Standard preliminaries. We don't use new anywhere but if we ever do this will help us // use it properly (hee hee). CC_IMPLEMENT_DYNCREATE(OpDragBox, Operation) CC_IMPLEMENT_DYNCREATE(OpCentredDragBox, OpDragBox) #define new CAM_DEBUG_NEW /************************************************************************************** Preference: DoSolidDragBoxes Section: Dragging Author: Justin_Flude (Xara Group Ltd) Range: FALSE or TRUE Purpose: Determines how the drag operation base-class draws a drag box. If FALSE then the "tranditional" unfilled xor rectangle with a certain line colour is drawn. If FALSE a filled ("solid") xor rectangle of a certain line and fill colour will be drawn. The default is FALSE. ***************************************************************************************/ BOOL OpDragBox::m_fDoSolidDragBoxes = FALSE; /******************************************************************************************** > static BOOL OpDragBox::Init() Author: Justin_Flude (Xara Group Ltd) Created: 24/4/95 Returns: TRUE if successful, FALSE if otherwise. Purpose: Initialises the OpDragBox base-class drag operation. Loads preferences. ********************************************************************************************/ BOOL OpDragBox::Init() { return GetApplication()->DeclareSection(TEXT("Dragging"), 5) && GetApplication()->DeclarePref(TEXT("Dragging"), TEXT("DoSolidDragBoxes"), &m_fDoSolidDragBoxes, FALSE, TRUE); } /******************************************************************************************** > OpDragBox::OpDragBox() Author: Justin_Flude (Xara Group Ltd) Created: 20/7/93 Purpose: Default constructor. Only accessible to derived classes. SeeAlso: class OpCentredDragBox ********************************************************************************************/ OpDragBox::OpDragBox() : m_pStartSpread(NULL) // no drag has been started yet { // Empty. } /******************************************************************************************** > BOOL OpDragBox::DoDrag(DragType dt, Spread* pStartSpread, const DocCoord& dcStartPos, ClickModifiers cmods) Author: Justin_Flude (Xara Group Ltd) Created: 22/4/95 Inputs: dt what kind of drag to start, eg. DRAGTYPE_AUTOSCROLL pSpread the spread containing the anchor (start) point of the drag dcStartPos the anchor (start) point of the drag within the spread cmods the mouse click modifier state Returns: TRUE if a drag is successfully started Purpose: This is called to start a drag box operation. SeeAlso: OpDragBox::OnDragStarted ********************************************************************************************/ BOOL OpDragBox::DoDrag(DragType dt, Spread* pStartSpread, const DocCoord& dcStartPos, ClickModifiers cmods) { // We use a null spread to indicate that a drag hasn't started yet, so check for this // when we reckon we should have a valid spread. ERROR3IF(pStartSpread == NULL, "Null StartSpread* passed to OpDragBox::DoDrag"); // Initialise the drag box record to an empty rectangle. m_drLastDragBox.lo = m_drLastDragBox.hi = dcStartPos; // Try to start the drag. Note that we don't touch m_pStartSpread if start drag fails, // so GetDragStartPos will correctly return "no drag started" if the StartDrag call // below fails. return StartDrag(dt) && OnDragStarted(m_pStartSpread = pStartSpread, m_dcStartPos = dcStartPos, cmods); } /******************************************************************************************** > BOOL OpDragBox::GetDragStartPos(Spread** ppStartSpread, DocCoord* pdcStartPos) const Author: Justin_Flude (Xara Group Ltd) Created: 23/4/95 Outputs: ppStartSpread where to store the spread that the drag started on pdcStartPos where to store the position that the drag started Returns: TRUE if a drag has been started, FALSE if one hasn't been started yet Purpose: Gets the the start position of the drag operation, if one has been started. Note that either of the output paramters may be NULL, which means that the caller doesn't require that particular output. ********************************************************************************************/ BOOL OpDragBox::GetDragStartPos(Spread** ppStartSpread, DocCoord* pdcStartPos) const { // If a drag hasn't started yet then return fail code. if (m_pStartSpread == NULL) return FALSE; // Copy the required information to the output parameters and return that a drga has // been started. if (ppStartSpread != NULL) *ppStartSpread = m_pStartSpread; if (pdcStartPos != NULL) *pdcStartPos = m_dcStartPos; return TRUE; } /******************************************************************************************** > static inline MILLIPOINT OpDragBox::Min(MILLIPOINT a, MILLIPOINT b) Author: Justin_Flude (Xara Group Ltd) Created: 22/4/95 Purpose: Helper function to return the smallest of two ordinates. ********************************************************************************************/ inline MILLIPOINT OpDragBox::Min(MILLIPOINT a, MILLIPOINT b) { return (a < b) ? a : b; } /******************************************************************************************** > static inline MILLIPOINT OpDragBox::Max(MILLIPOINT a, MILLIPOINT b) Author: Justin_Flude (Xara Group Ltd) Created: 22/4/95 Purpose: Helper function to return the largest of two ordinates. ********************************************************************************************/ inline MILLIPOINT OpDragBox::Max(MILLIPOINT a, MILLIPOINT b) { return (a > b) ? a : b; } /******************************************************************************************** > void OpDragBox::DragPointerMove(DocCoord dcMousePos, ClickModifiers cmods, Spread* pSpread, BOOL bSolidDrag) Author: Justin_Flude (Xara Group Ltd) Created: 5/7/93 Inputs: dcMousePos the current position of the mouse cmods the state of the mouse modifiers pSpread the spread containing the mouse Purpose: This is called every time the mouse moves, during a drag. It updates the xored drag box and calls the OnPointerMoved function so a derived class can do something useful. SeeAlso: OpDragBox::OnPointerMoved; OpDragBox::UpdateSolidDragBox; OpDragBox::UpdateUnfilledDragBox ********************************************************************************************/ void OpDragBox::DragPointerMove(DocCoord dcMousePos, ClickModifiers cmods, Spread* pSpread, BOOL bSolidDrag) { // If the mouse has moved to another spread than translate its position into the // new spread's coordinate system. if (pSpread != m_pStartSpread && pSpread != NULL) { dcMousePos = MakeRelativeToSpread(m_pStartSpread, pSpread, dcMousePos); } // Calculate the new drag rectangle. DocRect drNewDragBox = CalcDragBox(m_dcStartPos, dcMousePos); ERROR3IF(!drNewDragBox.IsValid(), "Invalid drag box in OpDragBox::DragPointerMove"); // Call the appropriate update function, according to whether we are drawing solid // drag boxes or unfilled drag boxes. if (m_fDoSolidDragBoxes) { UpdateSolidDragBox(drNewDragBox); } else { UpdateUnfilledDragBox(drNewDragBox); } // Update our record of the last drag box and call the derived class. if (!OnPointerMoved(m_pStartSpread, m_drLastDragBox = drNewDragBox, cmods)) { // Cancel the drag and operation. EndDrag(); End(); } } /******************************************************************************************** > void OpDragBox::UpdateSolidDragBox(const DocRect& drNewDragBox) Author: Justin_Flude (Xara Group Ltd) Created: 24/4/95 Inputs: drNewDragBox the new extent of the drag box, in the spread coordinates of the spread where the drag started Purpose: Calculates which rectangles to xor to change the last drawn solid drag box into the new solid drag box. SeeAlso: OpDragBox::DragPointerMove; OpDragBox::UpdateUnfilledDragBox ********************************************************************************************/ void OpDragBox::UpdateSolidDragBox(const DocRect& drNewDragBox) { // Set up four update rectangles. We will xor none, some, or all of these rectangles to // produce the new drag box. DocRect drUpdate[4]; INT32 nHowMany; // Find the intersection of the last and new rectangles. We will exclude this from // any xoring as it is already xored. DocRect drCommonDragBox = drNewDragBox.Intersection(m_drLastDragBox); // Calculate the rectangles that need to be xored to change the last into the new // drag box. This depends on how they overlap. if (drCommonDragBox.IsEmpty()) { // There's no intersection between the last drag box and the new drag box, even // though they share a common corner. So xor the full extent of both drag rects. drUpdate[0] = m_drLastDragBox; drUpdate[1] = drNewDragBox; nHowMany = 2; } else if (drNewDragBox.ContainsRect(m_drLastDragBox)) { // The new drag rect completely contains the last one, so clip out the last. nHowMany = ((DocRect&) drNewDragBox).SplitRect(m_drLastDragBox, drUpdate); } else if (m_drLastDragBox.ContainsRect(drNewDragBox)) { // The last drag rect completely contains the new one, so clip out the new. nHowMany = m_drLastDragBox.SplitRect(drNewDragBox, drUpdate); } else { // The drag rectangles overlap but neither completely contains the other, so set // the xor rectangles to be each drag rectangle less the common drag rectangle. nHowMany = m_drLastDragBox.SplitRect(drCommonDragBox, &drUpdate[0]); nHowMany += ((DocRect&) drNewDragBox).SplitRect(drCommonDragBox, &drUpdate[nHowMany]); } // Sanity check. ERROR3IF(nHowMany < 0 || nHowMany > 4, "Wrong number of split rects in OpDragBox::UpdateSolidDragBox\n"); // Draw the xor rects. for (INT32 i = 0; i < nHowMany; i++) { DrawXorRect(drUpdate[i], m_pStartSpread, drUpdate[i]); } } /******************************************************************************************** > void OpDragBox::UpdateUnfilledDragBox(const DocRect& drNewDragBox) Author: Justin_Flude (Xara Group Ltd) Created: 24/4/95 Inputs: drNewDragBox the new extent of the drag box, in the spread coordinates of the spread where the drag started Purpose: Calculates which rectangles to xor to change the last drawn solid drag box into the new solid drag box. SeeAlso: OpDragBox::DragPointerMove; OpDragBox::UpdateSolidDragBox ********************************************************************************************/ void OpDragBox::UpdateUnfilledDragBox(const DocRect& drNewDragBox) { // Remove the old drag box. DrawXorRect(m_drLastDragBox, m_pStartSpread, m_drLastDragBox); // Draw the new drag box. DrawXorRect(drNewDragBox, m_pStartSpread, drNewDragBox); } /******************************************************************************************** > void OpDragBox::DragPointerIdle(DocCoord dcMousePos, ClickModifiers cmods, Spread* pSpread, BOOL bSolidDrag) Author: Justin_Flude (Xara Group Ltd) Created: 5/7/93 Inputs: dcMousePos the current position of the mouse cmods the state of the mouse modifiers pSpread the spread containing the mouse Purpose: This is called every time the mouse hasn't moved for a while during a drag. It calls the OnPointerIdle function so a derived class can do something useful with this time, such as hit-testing etc. SeeAlso: OpDragBox::OnPointerIdle ********************************************************************************************/ void OpDragBox::DragPointerIdle(DocCoord dcMousePos, ClickModifiers cmods, Spread* pSpread, BOOL bSolidDrag) { // If the mouse has moved to another spread than translate its position into the // new spread's coordinate system. if (pSpread != m_pStartSpread && pSpread != NULL) { dcMousePos = MakeRelativeToSpread(m_pStartSpread, pSpread, dcMousePos); } // Calculate the new drag rectangle. Note that as the mouse is supposed to be // stationary we do NOT redraw the drag box or update our records of it. DocRect drNewDragBox = CalcDragBox(m_dcStartPos, dcMousePos); // Call the derived class, optionally cancelling the drag. if (!OnPointerIdle(m_pStartSpread, drNewDragBox, cmods)) { // Cancel the drag and operation. EndDrag(); End(); } } /******************************************************************************************** > void OpDragBox::DragFinished(DocCoord dcMousePos, ClickModifiers cmods, Spread* pSpread, BOOL fOK, BOOL bSolidDrag) Author: Justin_Flude (Xara Group Ltd) Created: 22/4/95 Inputs: dcMousePos the last position of the mouse cmods the mouse click modifier state at the end pSpread the spread containing the mouse fOK whether the drag was cancelled or not was ended with the escape key being pressed Purpose: This is called when a drag operation finishes. It removes the drag box and calls the OnEndDrag function in a derived class. SeeAlso: OpDragBox::OnDragEnded ********************************************************************************************/ void OpDragBox::DragFinished(DocCoord dcMousePos, ClickModifiers cmods, Spread* pSpread, BOOL fOK, BOOL bSolidDrag) { // Remove the last drag box. RenderDragBlobs(m_drLastDragBox, m_pStartSpread, bSolidDrag); // It's possible that the mouse is in a different position to where it was on the last // call to DragPointerMove, so recalculate the drag box (but don't draw it, of course). // Here, check for a change in the spread containing the mouse. if (pSpread != m_pStartSpread && pSpread != NULL) { dcMousePos = MakeRelativeToSpread(m_pStartSpread, pSpread, dcMousePos); } // Recalculate the drag box. m_drLastDragBox = CalcDragBox(m_dcStartPos, dcMousePos); // Call the derived class handler, passing the drag box and the success code. if (!OnDragEnded(m_pStartSpread, m_drLastDragBox, cmods, EndDrag() && fOK)) { FailAndExecute(); } // Finally, end the operation. End(); } /******************************************************************************************** > void OpDragBox::RenderDragBlobs(DocRect drClip, Spread* pSpread, BOOL bSolidDrag) Author: Justin_Flude (Xara Group Ltd) Created: 22/4/05 Inputs: drClip the clip rectangle to draw against pSpread the spread containing the clip rectangle Purpose: Xors a rectangle onto the screen to mark out the size of the current drag box. SeeAlso: OpDragBox::DrawXorRect ********************************************************************************************/ void OpDragBox::RenderDragBlobs(DocRect drClip, Spread* pSpread, BOOL bSolidDrag) { DrawXorRect(drClip, pSpread, m_drLastDragBox); } /******************************************************************************************** > void OpDragBox::DrawXorRect(const DocRect& drClip, Spread* pspdXor, const DocRect& drXor) const Author: Justin_Flude (Xara Group Ltd) Created: 22/4/95 Inputs: drClip the clipping rectangle to draw against pspdXor pointer to the spread to draw the xor rect onto drXor the position of the xor rect within the spread Purpose: Xors the given rectangle, clipping against the clip rectangle. Works out how to fill the rectangle according to the DoSolidDragBoxes preference. SeeAlso: OpDragBox::DragPointerMove; OpDragBox::RenderDragBlobs ********************************************************************************************/ void OpDragBox::DrawXorRect(const DocRect& drClip, Spread* pspdXor, const DocRect& drXor) const { // Check if we have nothing to do. if (drXor.IsEmpty()) return; // Ask the derived class what colour its drag box is. If we are doing solid drags we // want to use the same colour to fill the box as well, otherwise we fill with no colour. StockColour scLineColour = GetBoxColour(); StockColour scFillColour = (m_fDoSolidDragBoxes) ? scLineColour : COLOUR_NONE; // Xor the rect on all supplied render-regions. RenderRegion* pRegion = DocView::RenderOnTop(&((DocRect&) drClip), pspdXor, ClippedEOR); while (pRegion != NULL) { // Set the line and fill colours. pRegion->SetLineColour(scLineColour); pRegion->SetFillColour(scFillColour); // Draw the xored drag box and go on to the next render-region. pRegion->DrawDragRect(&((DocRect&) drXor)); pRegion = DocView::GetNextOnTop(&((DocRect&) drClip)); } } /******************************************************************************************** > virtual DocRect OpDragBox::CalcDragBox(const DocCoord& dcStartPos, const DocCoord& dcMousePos) const Author: Justin_Flude (Xara Group Ltd) Created: 23/4/95 Inputs: dcStartPos the start position of the drag dcMousePos the current position of the mouse Outputs: - Returns: The extent of the drag box, as a millipoint DocRect. Purpose: Calculates the current extent of the drag box. The default implementation takes the start point as one corner and the mouse point as another corner, but you could, for example, override this to take the start point as the centre of the drag box (useful for the zoom tool). Errors: - SeeAlso: OpDragBox::DragPointerMove; OpDragBox::DragFinished ********************************************************************************************/ DocRect OpDragBox::CalcDragBox(const DocCoord& dcStartPos, const DocCoord& dcMousePos) const { return DocRect(Min(dcStartPos.x, dcMousePos.x), Min(dcStartPos.y, dcMousePos.y), Max(dcStartPos.x, dcMousePos.x), Max(dcStartPos.y, dcMousePos.y)); } /******************************************************************************************** > virtual StockColour OpDragBox::GetBoxColour() const Author: Justin_Flude (Xara Group Ltd) Created: 22/4/95 Returns: The stock colour of the drag box's line. By default this is COLOUR_XORSELECT. Purpose: Allows customisation of the colour of the drag box. SeeAlso: OpDragBox::DrawXorRect ********************************************************************************************/ StockColour OpDragBox::GetBoxColour() const { return COLOUR_XORSELECT; } /******************************************************************************************** > virtual BOOL OpDragBox::OnDragStarted(Spread* pStartSpread, const DocCoord& dcStartPos, ClickModifiers cmods) Author: Justin_Flude (Xara Group Ltd) Created: 22/4/95 Inputs: pStartSpread the spread where the drag was started dcStartPos the point within the spread where the drag was started cmods the state of the mouse click modifiers Returns: FALSE to cancel the drag, TRUE to continue. Purpose: By default does nothing. This function can be overriden to so something useful (eg. set the status bar text). Note that this function will NOT be called if Operation::StartDrag fails. If you return FALSE then the DoDrag() call will also return FALSE (generally notifying the op runner that the drag failed). SeeAlso: OpDragBox::DoDrag ********************************************************************************************/ BOOL OpDragBox::OnDragStarted(Spread*, const DocCoord&, ClickModifiers) { return TRUE; } /******************************************************************************************** > virtual BOOL OpDragBox::OnPointerMoved(Spread* pBoxSpread, const DocRect& drDragBox, ClickModifiers cmods) Author: Justin_Flude (Xara Group Ltd) Created: 22/4/95 Inputs: pBoxSpread the spread where the drag was started drDragBox the rectangle within the spread defining the drag box cmods the state of the mouse click modifiers Returns: FALSE to cancel the drag, TRUE to continue. Purpose: By default does nothing. This function can be overriden to so something useful (eg. set the status bar text). Note that if you return FALSE from this function EndDrag() and End() will be called. SeeAlso: OpDragBox::DragPointerMove ********************************************************************************************/ BOOL OpDragBox::OnPointerMoved(Spread*, const DocRect&, ClickModifiers) { return TRUE; } /******************************************************************************************** > virtual BOOL OpDragBox::OnPointerIdle(Spread* pBoxSpread, const DocRect& drDragBox, ClickModifiers cmods) Author: Justin_Flude (Xara Group Ltd) Created: 22/4/95 Inputs: pBoxSpread the spread where the drag was started drDragBox the rectangle within the spread defining the drag box cmods the state of the mouse click modifiers Returns: FALSE to cancel the drag, TRUE to continue. Purpose: By default does nothing. This function can be overriden to so something useful (eg. set the status bar text). Note that if you return FALSE from this function EndDrag() and End() will be called. SeeAlso: OpDragBox::DragPointerIdle ********************************************************************************************/ BOOL OpDragBox::OnPointerIdle(Spread*, const DocRect&, ClickModifiers) { return TRUE; } /******************************************************************************************** > virtual BOOL OpDragBox::OnDragEnded(Spread* pBoxSpread, const DocRect& drDragBox, ClickModifiers cmods, BOOL fDragOK); Author: Justin_Flude (Xara Group Ltd) Created: 22/4/95 Inputs: pBoxSpread the spread where the drag was started drDragBox the rectangle within the spread defining the drag box cmods the state of the mouse click modifiers fDragOK the drag has not failed or been cancelled, its a good 'un Returns: FALSE to cancel the op, TRUE for success. Note that FailAndExecute() will be called for this operation if you return FALSE from this function. If your operation requires different failure handling (or no failure handling at all) then you should perform the handling yourself and return TRUE. Purpose: You should override this function to do something useful (eg. set the status bar text, perform a selection operation). It would be nice to make this a pure virtual function, as it doesn't make any sense not to override this function - a drag op should do something as a result of a mouse drag, but unfortunately the MFC/CC run-time dynamic creation won't allow it. SeeAlso: OpDragBox::DragFinished ********************************************************************************************/ BOOL OpDragBox::OnDragEnded(Spread*, const DocRect&, ClickModifiers, BOOL) { return TRUE; } /******************************************************************************************** > OpCentredDragBox::OpCentredDragBox() Author: Justin_Flude (Xara Group Ltd) Created: 23/4/95 Purpose: Default constructor for a centred drag-box operation. SeeAlso: class OpDragBox ********************************************************************************************/ OpCentredDragBox::OpCentredDragBox() { // Empty. } /******************************************************************************************** > virtual DocRect OpCentredDragBox::CalcDragBox(const DocCoord& dcStartPos, const DocCoord& dcMousePos) const Author: Justin_Flude (Xara Group Ltd) Created: 23/4/95 Inputs: dcStartPos the position where the drag was started dcMousePos the current position of the mouse Returns: A document (spread) rectangle the is the current bounds of the drag box. Purpose: Overrides the default OpDragBox method to allow the drag box to be drawn from the centre (start of drag) to the corner (current mouse position). SeeAlso: OpDragBox::CalcDragBox ********************************************************************************************/ DocRect OpCentredDragBox::CalcDragBox(const DocCoord& dcStartPos, const DocCoord& dcMousePos) const { // Find the half of the absolute width and height of the drag box. MILLIPOINT nWidth2 = dcStartPos.x - dcMousePos.x; if (nWidth2 < 0) nWidth2 = -nWidth2; MILLIPOINT nHeight2 = dcStartPos.y - dcMousePos.y; if (nHeight2 < 0) nHeight2 = -nHeight2; // Return a rectangle of this width and height centre on the click point. return DocRect(dcStartPos.x - nWidth2, dcStartPos.y - nHeight2, dcStartPos.x + nWidth2, dcStartPos.y + nHeight2); }