shapeops.cpp

Go to the documentation of this file.
00001 // $Id: shapeops.cpp 1776 2007-06-27 11:16:49Z luke $
00002 /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE
00003 ================================XARAHEADERSTART===========================
00004  
00005                Xara LX, a vector drawing and manipulation program.
00006                     Copyright (C) 1993-2006 Xara Group Ltd.
00007        Copyright on certain contributions may be held in joint with their
00008               respective authors. See AUTHORS file for details.
00009 
00010 LICENSE TO USE AND MODIFY SOFTWARE
00011 ----------------------------------
00012 
00013 This file is part of Xara LX.
00014 
00015 Xara LX is free software; you can redistribute it and/or modify it
00016 under the terms of the GNU General Public License version 2 as published
00017 by the Free Software Foundation.
00018 
00019 Xara LX and its component source files are distributed in the hope
00020 that it will be useful, but WITHOUT ANY WARRANTY; without even the
00021 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00022 See the GNU General Public License for more details.
00023 
00024 You should have received a copy of the GNU General Public License along
00025 with Xara LX (see the file GPL in the root directory of the
00026 distribution); if not, write to the Free Software Foundation, Inc., 51
00027 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
00028 
00029 
00030 ADDITIONAL RIGHTS
00031 -----------------
00032 
00033 Conditional upon your continuing compliance with the GNU General Public
00034 License described above, Xara Group Ltd grants to you certain additional
00035 rights. 
00036 
00037 The additional rights are to use, modify, and distribute the software
00038 together with the wxWidgets library, the wxXtra library, and the "CDraw"
00039 library and any other such library that any version of Xara LX relased
00040 by Xara Group Ltd requires in order to compile and execute, including
00041 the static linking of that library to XaraLX. In the case of the
00042 "CDraw" library, you may satisfy obligation under the GNU General Public
00043 License to provide source code by providing a binary copy of the library
00044 concerned and a copy of the license accompanying it.
00045 
00046 Nothing in this section restricts any of the rights you have under
00047 the GNU General Public License.
00048 
00049 
00050 SCOPE OF LICENSE
00051 ----------------
00052 
00053 This license applies to this program (XaraLX) and its constituent source
00054 files only, and does not necessarily apply to other Xara products which may
00055 in part share the same code base, and are subject to their own licensing
00056 terms.
00057 
00058 This license does not apply to files in the wxXtra directory, which
00059 are built into a separate library, and are subject to the wxWindows
00060 license contained within that directory in the file "WXXTRA-LICENSE".
00061 
00062 This license does not apply to the binary libraries (if any) within
00063 the "libs" directory, which are subject to a separate license contained
00064 within that directory in the file "LIBS-LICENSE".
00065 
00066 
00067 ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS
00068 ----------------------------------------------
00069 
00070 Subject to the terms of the GNU Public License (see above), you are
00071 free to do whatever you like with your modifications. However, you may
00072 (at your option) wish contribute them to Xara's source tree. You can
00073 find details of how to do this at:
00074   http://www.xaraxtreme.org/developers/
00075 
00076 Prior to contributing your modifications, you will need to complete our
00077 contributor agreement. This can be found at:
00078   http://www.xaraxtreme.org/developers/contribute/
00079 
00080 Please note that Xara will not accept modifications which modify any of
00081 the text between the start and end of this header (marked
00082 XARAHEADERSTART and XARAHEADEREND).
00083 
00084 
00085 MARKS
00086 -----
00087 
00088 Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara
00089 designs are registered or unregistered trademarks, design-marks, and/or
00090 service marks of Xara Group Ltd. All rights in these marks are reserved.
00091 
00092 
00093       Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK.
00094                         http://www.xara.com/
00095 
00096 =================================XARAHEADEREND============================
00097  */
00098 
00099 /*
00100 */
00101 
00102 #include "camtypes.h"
00103 #include "shapeops.h"
00104 
00105 //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00106 #include "blobs.h"
00107 #include "csrstack.h"
00108 //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00109 #include "nodepath.h"
00110 #include "nodershp.h"
00111 #include "pathedit.h"
00112 //#include "peter.h"
00113 #include "progress.h"
00114 //#include "resource.h"
00115 //#include "rndrgn.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00116 //#include "tool.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00117 //#include "trans2d.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00118 //#include "viewrc.h"
00119 #include "objchge.h"
00120 //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00121 
00122 #define PATRACE if(IsUserName("Peter")) TRACE
00123 
00124 // Useful macro, given a pointer to a CCObject (or an object derived
00125 // from it) and a string format, it will output the class name of the object. 
00126 // Example usage PACLASSNAME(CurrentNode,"Node class = %s\n");
00127 // N.B. No checking is node or parameters.  Note semi-colon on end.
00128 #define PACLASSNAME(a,b) CCRuntimeClass* Blob; Blob = a->GetRuntimeClass(); PATRACE(b,Blob->m_lpszClassName)
00129 
00130 
00131 DECLARE_SOURCE( "$Revision: 1776 $" );
00132 
00133 
00134 CC_IMPLEMENT_MEMDUMP(EditRegularShapeParam, OpParam)    
00135 CC_IMPLEMENT_DYNCREATE(OpEditRegularShape, SelOperation)
00136 CC_IMPLEMENT_DYNCREATE(OpDragRegularShape, SelOperation)
00137 CC_IMPLEMENT_DYNCREATE(OpReformShapeEdge, SelOperation)
00138 CC_IMPLEMENT_DYNCREATE(ChangeShapeDataAction, Action)
00139 CC_IMPLEMENT_DYNCREATE(ChangeShapePointAction, Action)
00140 CC_IMPLEMENT_DYNCREATE(ChangeShapePathAction, Action)
00141 CC_IMPLEMENT_DYNAMIC(ShapeEditedMsg, Msg)
00142 CC_IMPLEMENT_DYNCREATE(OpShapeToggleBase, SelOperation)
00143 CC_IMPLEMENT_DYNCREATE(OpShapeToggleElipPoly, OpShapeToggleBase)
00144 CC_IMPLEMENT_DYNCREATE(OpShapeToggleStellation, OpShapeToggleBase)
00145 CC_IMPLEMENT_DYNCREATE(OpShapeToggleCurvature, OpShapeToggleBase)
00146 CC_IMPLEMENT_DYNCREATE(OpShapeMakeRegular, SelOperation)
00147 
00148 CC_IMPLEMENT_DYNCREATE( OpShapeToggleSetNumSides, SelOperation )    
00149 CC_IMPLEMENT_DYNCREATE( OpShapeToggleSetNumSides3, OpShapeToggleSetNumSides )    
00150 CC_IMPLEMENT_DYNCREATE( OpShapeToggleSetNumSides4, OpShapeToggleSetNumSides )    
00151 CC_IMPLEMENT_DYNCREATE( OpShapeToggleSetNumSides5, OpShapeToggleSetNumSides )    
00152 CC_IMPLEMENT_DYNCREATE( OpShapeToggleSetNumSides6, OpShapeToggleSetNumSides )    
00153 CC_IMPLEMENT_DYNCREATE( OpShapeToggleSetNumSides7, OpShapeToggleSetNumSides )    
00154 CC_IMPLEMENT_DYNCREATE( OpShapeToggleSetNumSides8, OpShapeToggleSetNumSides )    
00155 CC_IMPLEMENT_DYNCREATE( OpShapeToggleSetNumSides9, OpShapeToggleSetNumSides )    
00156 CC_IMPLEMENT_DYNCREATE( OpShapeToggleSetNumSides10, OpShapeToggleSetNumSides )    
00157 
00158 
00159 
00160 // Declare smart memory handling in Debug builds
00161 #define new CAM_DEBUG_NEW
00162 
00163 const DocCoord DontCare (0,0);
00164 
00165 /********************************************************************************************
00166 
00167 >   ShapeEditedMsg::ShapeEditedMsg(NodeRegularShape* pChangeShape, Spread* pParentSpread) 
00168 
00169     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
00170     Created:    17/1/95
00171     Inputs:     pChangeShape - pointer to the edited shape
00172                 pParentSpread - pointer to the parent spread of the object
00173     Purpose:    initilise a ShapeEditedMsg
00174     SeeAlso:    ShapeEditedMsg
00175 
00176 ********************************************************************************************/
00177 ShapeEditedMsg::ShapeEditedMsg(NodeRegularShape* pChangeShape, Spread* pParentSpread)                           
00178 {                              
00179     pShape = pChangeShape;
00180     pSpread = pParentSpread;
00181 }
00182 
00183 
00185 //                                     EditRegularShapeParam                                //
00187 
00188 
00189 
00190 /********************************************************************************************
00191 
00192 >   EditRegularShapeParam::EditRegularShapeParam(NodeRegularShape* ChangeShapes) 
00193 
00194     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
00195     Created:    08/12/94    
00196     Inputs:     ChangeShapes - pointer to the shape to change OR NULL to change all selected shapes                                   
00197     Purpose:    EditRegularShapeParam constructor - sets all member items to unchange.
00198                 After constructing the object, the caller should set those items that it wants
00199                 OpEditRegularShape to change.
00200 
00201 ********************************************************************************************/
00202 EditRegularShapeParam::EditRegularShapeParam(NodeRegularShape* ChangeShapes)                                
00203 {                              
00204     ShapeToChange = ChangeShapes;
00205     ShapesToAffect = AFFECT_ALL;
00206     
00207     NewEdgePath1 = NULL;    
00208     NewEdgePath2 = NULL;    
00209     NewNumSides = 0;        
00210     NewCircular = CHANGE_DONTCARE;
00211     NewStellated = CHANGE_DONTCARE;
00212     NewPrimaryCurvature = CHANGE_DONTCARE;
00213     NewStellationCurvature = CHANGE_DONTCARE;
00214     NewStellRadiusToPrimary = -1.0; 
00215     NewPrimaryCurveToPrimary = -1.0;    
00216     NewStellCurveToStell = -1.0;        
00217     NewStellOffsetRatio = -1000.0;
00218 //  NewCentrePoint = DontCare;      
00219 //  NewMajorAxes = DontCare;        
00220 //  NewMinorAxes = DontCare;        
00221 
00222     TranslateTo = DontCare;     
00223     TranslateBy = DontCare;     
00224     SetRadiusLength = -1;
00225     AddRadiusLength = -1;
00226     RotateTo = -1000.0;         
00227     RotateBy = -1000.0;         
00228     SetStellationLength = -1;   
00229     AddStellationLength = -1;   
00230     SetStellationOffset = -1000.0;
00231     AddStellationOffset = -1000.0;
00232     AddPrimaryCurvature = 0.0;
00233     AddStellationCurvature = 0.0;
00234     SetMajorAxisLength = 0;
00235     AddMajorAxisLength = 0;
00236     SetMinorAxisLength = 0;
00237     AddMinorAxisLength = 0;
00238 }
00239 
00240 
00241 
00243 //                                     OpEditRegularShape                                   //
00245 
00246 /********************************************************************************************
00247 
00248 >   OpEditRegularShape::OpEditRegularShape() 
00249 
00250     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
00251     Created:    08/12/94                                          
00252     Purpose:    OpEditRegularShape constructor - does nothing itself
00253 
00254 ********************************************************************************************/
00255 OpEditRegularShape::OpEditRegularShape(): SelOperation()                                
00256 {                              
00257 }
00258 
00259 
00260 
00261  /********************************************************************************************
00262 
00263 >   BOOL OpEditRegularShape::Init()
00264 
00265     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
00266     Created:    08/12/94
00267     Returns:    TRUE if the operation could be successfully initialised 
00268                 FALSE if no more memory could be allocated 
00269     Purpose:    OpEditRegularShape initialiser method
00270     Errors:     ERROR will be called if there was insufficient memory to allocate the 
00271                 operation.
00272     SeeAlso:    -
00273 
00274 ********************************************************************************************/
00275 BOOL OpEditRegularShape::Init()
00276 {
00277     BOOL Success = RegisterOpDescriptor(0, 
00278                                         _R(IDS_EDITREGULARSHAPEOP),
00279                                         CC_RUNTIME_CLASS(OpEditRegularShape), 
00280                                         OPTOKEN_EDITREGULARSHAPE,
00281                                         OpEditRegularShape::GetState);
00282 
00283     if (Success)
00284         Success = OpShapeToggleElipPoly::Init();
00285     if (Success)
00286         Success = OpShapeToggleStellation::Init();
00287     if (Success)
00288         Success = OpShapeToggleCurvature::Init();
00289     if (Success)
00290         Success = OpShapeToggleSetNumSides::Init();
00291     if (Success)
00292         Success = OpShapeMakeRegular::Init();
00293 
00294     return Success;
00295 }               
00296     
00297 
00298 
00299 /********************************************************************************************
00300 
00301 >   OpState OpEditRegularShape::GetState(String_256*, OpDescriptor*)
00302 
00303     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
00304     Created:    08/12/94
00305     Returns:    The state of the OpEditRegularShape
00306     Purpose:    For finding the OpEditRegularShape's state. 
00307     SeeAlso:    -
00308 
00309 ********************************************************************************************/
00310 OpState OpEditRegularShape::GetState(String_256* UIDescription, OpDescriptor*)
00311 {
00312     OpState OpSt;
00313 
00314     return OpSt;                                 
00315 }
00316 
00317 
00318 
00319 /********************************************************************************************
00320 
00321 >   void OpEditRegularShape::DoWithParam(OpDescriptor* pOpDesc, OpParam* pEntryOpParam)
00322 
00323     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
00324     Created:    08/12/94
00325     Inputs:     OpDescriptor (unused)
00326                 pOpParam - pointer to the parameter object.
00327     Purpose:    Performs the edit regular shapes operation. 
00328     Errors:     -
00329     SeeAlso:    EditRegularShapeParam, OpEditRegularShape::CarryOut
00330 
00331 ********************************************************************************************/
00332 void OpEditRegularShape::DoWithParam(OpDescriptor* pOpDesc, OpParam* pEntryOpParam)
00333 {   
00334     EditRegularShapeParam* pOpParam = (EditRegularShapeParam*)pEntryOpParam;
00335     
00336     // Tell the operation system its kickoff time.
00337     if (DoStartSelOp(FALSE))
00338     {
00339         BOOL Complete = TRUE;
00340 
00341         // Data used by AllowOp() to ask the node if it's ok to do the op
00342         ObjChangeFlags cFlags;
00343         ObjChangeParam ObjChange(OBJCHANGE_STARTING,cFlags,NULL,this);
00344 
00345         // Either change just one shape or all selected shapes.
00346         if (pOpParam->ShapeToChange != NULL)
00347         {
00348             if (ShouldDoThisShape(pOpParam->ShapeToChange, pOpParam->ShapesToAffect))
00349             {
00350                 if (pOpParam->ShapeToChange->AllowOp(&ObjChange))                   // Will node allow the op?
00351                     Complete = CarryOut(pOpParam->ShapeToChange, pOpParam);
00352             }
00353         }
00354         else
00355         {
00356             // Obtain the current selections and the first node in the selection
00357             SelRange*   Selected = GetApplication()->FindSelection();
00358             Node*       pNode = Selected->FindFirst();
00359 
00360             while ((pNode != NULL) && Complete)
00361             {
00362                 if (IS_A(pNode,NodeRegularShape))
00363                 {
00364                     if (ShouldDoThisShape((NodeRegularShape*)pNode, pOpParam->ShapesToAffect))
00365                     {
00366                         if (pNode->AllowOp(&ObjChange))                             // Will node allow the op?
00367                             Complete = CarryOut((NodeRegularShape*)pNode, pOpParam);
00368                     }
00369                 }
00370                 pNode = Selected->FindNext(pNode);
00371             }
00372         }
00373 
00374         GetApplication()->FindSelection()->Update(FALSE);
00375     
00376         if (Complete)
00377         {
00378             // updated effected parents
00379             ObjChange.Define(OBJCHANGE_FINISHED,cFlags,NULL,this);
00380             Complete = UpdateChangedNodes(&ObjChange);
00381         }
00382 
00383         // See if anything failed
00384         if (!Complete)
00385         {
00386             FailAndExecute();
00387         }
00388     }
00389     else
00390     {
00391         FailAndExecute();
00392     }
00393 
00394     End();
00395 }
00396 
00397 
00398 
00399 /********************************************************************************************
00400 
00401 >   BOOL OpEditRegularShape::CarryOut(NodeRegularShape* ShapeToEdit, EditRegularShapeParam* NewShapeData)
00402 
00403     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
00404     Created:    08/12/94
00405     Inputs:     ShapeToEdit points to the Shape to change.
00406                 NewShapeData points to the parameter object with the changes data.
00407     Purpose:    Performs the edit regular shapes operation on one particular shape
00408     Errors:     ERROR2 if there is no current DocView
00409     SeeAlso:    EditRegularShapeParam, OpEditRegularShape::DoWithParam
00410 
00411 ********************************************************************************************/
00412 
00413 BOOL OpEditRegularShape::CarryOut(NodeRegularShape* ShapeToEdit, EditRegularShapeParam* NewShapeData)
00414 {
00415     // Create an action to will cause the shape to recalc its bounds
00416     if (RecalcBoundsAction::DoRecalc(this, &UndoActions, ShapeToEdit) == AC_FAIL)
00417     {
00418         return FALSE;
00419     }
00420 
00421     // See if we should change the polygon/circular flag
00422     if (NewShapeData->NewCircular != EditRegularShapeParam::CHANGE_DONTCARE)
00423     {
00424         if (ChangeShapeDataAction::DoToggle(this, &UndoActions, ShapeToEdit, ChangeShapeDataAction::CHANGE_CIRCULAR,
00425                                                 ShapeToEdit->IsCircular() ? 1.0 : 0.0) == AC_FAIL)
00426         {
00427             return FALSE;
00428         }
00429         ShapeToEdit->SetCircular(NewShapeData->NewCircular == EditRegularShapeParam::CHANGE_SETTRUE);
00430     }
00431 
00432     // See if we should change the stellated flag
00433     if (NewShapeData->NewStellated != EditRegularShapeParam::CHANGE_DONTCARE)
00434     {
00435 /*      // If were turning off stellation them we may need to fix the primary curvature ratio
00436         if (NewShapeData->NewStellated == EditRegularShapeParam::CHANGE_SETFALSE)
00437         {
00438             if (ShapeToEdit->GetPrimaryCurveToPrimary() > 0.5)
00439                 NewShapeData->NewPrimaryCurveToPrimary = 0.2;
00440         }
00441         else
00442         {
00443             // If we've turned on stellation them we may need to fix both curvature ratios
00444             if (ShapeToEdit->GetPrimaryCurveToPrimary()+ShapeToEdit->GetStellCurveToStell() > 1.0)
00445             {
00446                 NewShapeData->NewPrimaryCurveToPrimary = 0.2;
00447                 NewShapeData->NewStellCurveToStell = 0.2;
00448             }
00449         }*/
00450 
00451         if (ChangeShapeDataAction::DoToggle(this, &UndoActions, ShapeToEdit, ChangeShapeDataAction::CHANGE_STELLATED,
00452                                                  ShapeToEdit->IsStellated() ? 1.0 : 0.0) == AC_FAIL)
00453         {
00454             return FALSE;
00455         }
00456         ShapeToEdit->SetStellated(NewShapeData->NewStellated == EditRegularShapeParam::CHANGE_SETTRUE);
00457     }
00458 
00459     // See if we should change the primary curvature flag
00460     if (NewShapeData->NewPrimaryCurvature != EditRegularShapeParam::CHANGE_DONTCARE)
00461     {
00462         if (ChangeShapeDataAction::DoToggle(this, &UndoActions, ShapeToEdit, ChangeShapeDataAction::CHANGE_PRIMARYCURVATURE,
00463                                                  ShapeToEdit->IsPrimaryCurvature() ? 1.0 : 0.0) == AC_FAIL)
00464         {
00465             return FALSE;
00466         }
00467         ShapeToEdit->SetPrimaryCurvature(NewShapeData->NewPrimaryCurvature == EditRegularShapeParam::CHANGE_SETTRUE);
00468     }
00469 
00470     // See if we should change the stellation curvature flag
00471     if (NewShapeData->NewStellationCurvature != EditRegularShapeParam::CHANGE_DONTCARE)
00472     {
00473         if (ChangeShapeDataAction::DoToggle(this, &UndoActions, ShapeToEdit, ChangeShapeDataAction::CHANGE_STELLATIONCURVATURE,
00474                                                  ShapeToEdit->IsStellationCurvature() ? 1.0 : 0.0) == AC_FAIL)
00475         {
00476             return FALSE;
00477         }
00478         ShapeToEdit->SetStellationCurvature(NewShapeData->NewStellationCurvature == EditRegularShapeParam::CHANGE_SETTRUE);
00479     }
00480 
00481     // See if we should change the stellation offset
00482     if (NewShapeData->NewStellOffsetRatio != -1000.0)
00483     {
00484         if (ChangeShapeDataAction::DoToggle(this, &UndoActions, ShapeToEdit, 
00485                 ChangeShapeDataAction::CHANGE_STELLATIONOFFSET, ShapeToEdit->GetStellationRatio()) == AC_FAIL)
00486         {
00487             return FALSE;
00488         }
00489         ShapeToEdit->SetStellationRatio(NewShapeData->NewStellOffsetRatio);
00490     }
00491 
00492     // See if we should change the number of sides
00493     if (NewShapeData->NewNumSides != 0)
00494     {
00495         if (ChangeShapeDataAction::DoToggle(this, &UndoActions, ShapeToEdit, 
00496                 ChangeShapeDataAction::CHANGE_NUMSIDES, ShapeToEdit->GetNumSides()) == AC_FAIL)
00497         {
00498             return FALSE;
00499         }
00500         ShapeToEdit->SetNumSides(NewShapeData->NewNumSides);
00501     }
00502 
00503     // See if we should change the stellation radius ratio
00504     if (NewShapeData->NewStellRadiusToPrimary != -1.0)
00505     {
00506         if (ChangeShapeDataAction::DoToggle(this, &UndoActions, ShapeToEdit, 
00507                 ChangeShapeDataAction::CHANGE_STELLRADIUSTOPRIMARY, 
00508                                             ShapeToEdit->GetStellRadiusToPrimary()) == AC_FAIL)
00509         {
00510             return FALSE;
00511         }
00512         ShapeToEdit->SetStellRadiusToPrimary(NewShapeData->NewStellRadiusToPrimary);
00513     }
00514 
00515     // See if we should change the primary curvature ratio
00516     if (NewShapeData->NewPrimaryCurveToPrimary != -1.0)
00517     {
00518         double NewRatio = NewShapeData->NewPrimaryCurveToPrimary;
00519 /*      if (ShapeToEdit->IsStellated())
00520         {
00521             double StellRatio = ShapeToEdit->GetStellCurveToStell();
00522             if (NewShapeData->NewStellCurveToStell != -1.0)
00523                 StellRatio = NewShapeData->NewStellCurveToStell;    
00524 
00525             NewRatio = __min(1.0-StellRatio, NewShapeData->NewPrimaryCurveToPrimary); 
00526         }
00527         else
00528         {
00529             NewRatio = __min(0.5, NewShapeData->NewPrimaryCurveToPrimary); 
00530         }*/
00531         
00532         if (ChangeShapeDataAction::DoToggle(this, &UndoActions, ShapeToEdit, 
00533                 ChangeShapeDataAction::CHANGE_PRIMARYCURVETOPRIMARY, 
00534                                             ShapeToEdit->GetPrimaryCurveToPrimary()) == AC_FAIL)
00535         {
00536             return FALSE;
00537         }
00538         ShapeToEdit->SetPrimaryCurveToPrimary(NewRatio);
00539     }
00540 
00541     // See if we should bump the primary curvature ratio
00542     if (NewShapeData->AddPrimaryCurvature != -0.0)
00543     {
00544         double NewRatio = ShapeToEdit->GetPrimaryCurveToPrimary() + NewShapeData->AddPrimaryCurvature;
00545 /*      if (ShapeToEdit->IsStellated())
00546         {
00547             NewRatio = __min(1.0-ShapeToEdit->GetStellCurveToStell(), NewRatio);
00548         }
00549         else
00550         {
00551             NewRatio = __min(0.5, NewRatio); 
00552         }*/
00553         NewRatio = max( 0.0, NewRatio );
00554         
00555         if (ChangeShapeDataAction::DoToggle(this, &UndoActions, ShapeToEdit, 
00556                 ChangeShapeDataAction::CHANGE_PRIMARYCURVETOPRIMARY, 
00557                                             ShapeToEdit->GetPrimaryCurveToPrimary()) == AC_FAIL)
00558         {
00559             return FALSE;
00560         }
00561         ShapeToEdit->SetPrimaryCurveToPrimary(NewRatio);
00562     }
00563 
00564     // See if we should change the stellation curvature ratio
00565     if (NewShapeData->NewStellCurveToStell != -1.0)
00566     {
00567         double NewRatio = NewShapeData->NewStellCurveToStell;
00568         
00569 //      NewRatio = __min (1.0-ShapeToEdit->GetPrimaryCurveToPrimary(), NewShapeData->NewStellCurveToStell); 
00570 
00571         if (ChangeShapeDataAction::DoToggle(this, &UndoActions, ShapeToEdit, 
00572                 ChangeShapeDataAction::CHANGE_STELLCURVETOSTELL, 
00573                                             ShapeToEdit->GetStellCurveToStell()) == AC_FAIL)
00574         {
00575             return FALSE;
00576         }
00577         ShapeToEdit->SetStellCurveToStell(NewRatio);
00578     }
00579 
00580     // See if we should bump the stellation curvature ratio
00581     if (NewShapeData->AddStellationCurvature != 0.0)
00582     {
00583         double NewRatio = ShapeToEdit->GetStellCurveToStell() + NewShapeData->AddStellationCurvature;
00584 //      NewRatio = __min(1.0-ShapeToEdit->GetPrimaryCurveToPrimary(), NewRatio); 
00585         NewRatio = max( 0.0, NewRatio );
00586         
00587         if (ChangeShapeDataAction::DoToggle(this, &UndoActions, ShapeToEdit, 
00588                 ChangeShapeDataAction::CHANGE_PRIMARYCURVETOPRIMARY, 
00589                                             ShapeToEdit->GetStellCurveToStell()) == AC_FAIL)
00590         {
00591             return FALSE;
00592         }
00593         ShapeToEdit->SetStellCurveToStell(NewRatio);
00594     }
00595 
00596     // See if we should change the minor axes
00597 /*  if (NewShapeData->NewMinorAxes != DontCare)
00598     {
00599         if (ChangeShapePointAction::DoToggle(this, &UndoActions, ShapeToEdit, 
00600                         ChangeShapePointAction::CHANGE_MINOR, ShapeToEdit->GetMinorAxes()) == AC_FAIL)
00601         {
00602             return FALSE;
00603         }
00604         ShapeToEdit->SetMinorAxes(NewShapeData->NewMinorAxes);
00605     }
00606 
00607     // See if we should change the major axes
00608     if (NewShapeData->NewMajorAxes != DontCare)
00609     {
00610         if (ChangeShapePointAction::DoToggle(this, &UndoActions, ShapeToEdit, 
00611                         ChangeShapePointAction::CHANGE_MAJOR, ShapeToEdit->GetMajorAxes()) == AC_FAIL)
00612         {
00613             return FALSE;
00614         }
00615         ShapeToEdit->SetMajorAxes(NewShapeData->NewMajorAxes);
00616     }*/
00617 
00618     // See if we should change the size of the shape to a new length
00619     // OR see if we should change the size of the shape by a specified ammount
00620     if ((NewShapeData->SetRadiusLength != -1) || (NewShapeData->AddRadiusLength != -1) )
00621     {
00622         // Get scaling factor
00623         fixed16 ScaleFactor;
00624         const double CurrentLength = ShapeToEdit->GetCentrePoint().Distance(ShapeToEdit->GetMajorAxes()) ;
00625         if (CurrentLength != 0.0)
00626         {
00627             if (NewShapeData->SetRadiusLength != -1)
00628             {
00629                 double RequiredLength = NewShapeData->SetRadiusLength;
00630                 ScaleFactor = RequiredLength / CurrentLength;
00631             }
00632             else
00633             {
00634                 double RequiredAddition = NewShapeData->AddRadiusLength;
00635                 ScaleFactor = (RequiredAddition + CurrentLength) / CurrentLength;
00636             }       
00637         }
00638         else
00639             ScaleFactor = 1.0;
00640         
00641         // Translate to origin, scale and translate back
00642         Trans2DMatrix* TransOriginMat = new Trans2DMatrix(-ShapeToEdit->GetCentrePoint().x, -ShapeToEdit->GetCentrePoint().y);
00643         Trans2DMatrix* ScaleMat = new Trans2DMatrix(Matrix(ScaleFactor,ScaleFactor));
00644         Trans2DMatrix* TransBackMat = new Trans2DMatrix(ShapeToEdit->GetCentrePoint().x, ShapeToEdit->GetCentrePoint().y);
00645         if (!(DoTransformNode(ShapeToEdit, TransOriginMat) && DoTransformNode(ShapeToEdit, ScaleMat)
00646                                                             && DoTransformNode(ShapeToEdit, TransBackMat) ))
00647         {
00648             return FALSE;
00649         }
00650     }
00651 
00652     // See if we should change the MajorAxis of the shape to a new length
00653     // OR see if we should change the MajorAxis of the shape by a specified ammount
00654     if ((NewShapeData->SetMajorAxisLength != 0) || (NewShapeData->AddMajorAxisLength != 0) )
00655     {
00656         // Get scaling factor
00657         fixed16 ScaleFactor;
00658         const double CurrentLength = ShapeToEdit->GetCentrePoint().Distance(ShapeToEdit->GetMajorAxes()) ;
00659         if (CurrentLength != 0.0)
00660         {
00661             if (NewShapeData->SetMajorAxisLength != 0)
00662             {
00663                 double RequiredLength = NewShapeData->SetMajorAxisLength;
00664                 ScaleFactor = RequiredLength / CurrentLength;
00665             }
00666             else
00667             {
00668                 double RequiredAddition = NewShapeData->AddMajorAxisLength;
00669                 ScaleFactor = (RequiredAddition + CurrentLength) / CurrentLength;
00670             }       
00671         }
00672         else
00673             ScaleFactor = 1.0;
00674 
00675         if (ChangeShapePointAction::DoToggle(this, &UndoActions, ShapeToEdit, 
00676                         ChangeShapePointAction::CHANGE_MAJOR, ShapeToEdit->GetUTMajorAxes()) == AC_FAIL)
00677         {
00678             return FALSE;
00679         }
00680 
00681         // Scale the major axes relative to the centre point
00682         Matrix ScaleMatrix(FIXED16(1.0), ScaleFactor);
00683         DocCoord Offset = ShapeToEdit->GetUTMajorAxes() - ShapeToEdit->GetUTCentrePoint();
00684         ScaleMatrix.transform(&Offset);
00685         ShapeToEdit->SetMajorAxes(ShapeToEdit->GetUTCentrePoint() + Offset);
00686     }
00687 
00688     // See if we should change the MinorAxis of the shape to a new length
00689     // OR see if we should change the MinorAxis of the shape by a specified ammount
00690     if ((NewShapeData->SetMinorAxisLength != 0) || (NewShapeData->AddMinorAxisLength != 0) )
00691     {
00692         // Get scaling factor
00693         fixed16 ScaleFactor;
00694         const double CurrentLength = ShapeToEdit->GetCentrePoint().Distance(ShapeToEdit->GetMinorAxes()) ;
00695         if (CurrentLength != 0.0)
00696         {
00697             if (NewShapeData->SetMinorAxisLength != 0)
00698             {
00699                 double RequiredLength = NewShapeData->SetMinorAxisLength;
00700                 ScaleFactor = RequiredLength / CurrentLength;
00701             }
00702             else
00703             {
00704                 double RequiredAddition = NewShapeData->AddMinorAxisLength;
00705                 ScaleFactor = (RequiredAddition + CurrentLength) / CurrentLength;
00706             }       
00707         }
00708         else
00709             ScaleFactor = 1.0;
00710 
00711         if (ChangeShapePointAction::DoToggle(this, &UndoActions, ShapeToEdit, 
00712                         ChangeShapePointAction::CHANGE_MINOR, ShapeToEdit->GetUTMinorAxes()) == AC_FAIL)
00713         {
00714             return FALSE;
00715         }
00716 
00717         // Scale the minor axes relative to the centre point
00718         Matrix ScaleMatrix(ScaleFactor, FIXED16(1.0));
00719         DocCoord Offset = ShapeToEdit->GetUTMinorAxes() - ShapeToEdit->GetUTCentrePoint();
00720         ScaleMatrix.transform(&Offset);
00721         ShapeToEdit->SetMinorAxes(ShapeToEdit->GetUTCentrePoint() + Offset);
00722     }
00723 
00724     // See if we should change the size of the stellation radius to a new length
00725     // OR see if we should change the size of the stellation radius by a specified ammount
00726     if ((NewShapeData->SetStellationLength != -1) || (NewShapeData->AddStellationLength != -1) )
00727     {
00728         const double CurrentLength = ShapeToEdit->GetCentrePoint().Distance(ShapeToEdit->GetMajorAxes()) ;
00729         double NewRatio;
00730         if (NewShapeData->SetStellationLength != -1)
00731         {
00732             // Get the current length of the shape and get the ratio from that
00733             NewRatio = double( min( fabs( NewShapeData->SetStellationLength / CurrentLength ), 1.0 ) );
00734         }
00735         else
00736         {
00737             double NewLength = fabs(CurrentLength - NewShapeData->AddStellationLength);
00738             NewRatio = double( min( fabs( ( NewLength / CurrentLength ) - 1 + ShapeToEdit->GetStellRadiusToPrimary() ), 1.0 ) );
00739         }
00740 
00741         if (ChangeShapeDataAction::DoToggle(this, &UndoActions, ShapeToEdit, 
00742                 ChangeShapeDataAction::CHANGE_STELLRADIUSTOPRIMARY, 
00743                                             ShapeToEdit->GetStellRadiusToPrimary()) == AC_FAIL)
00744         {
00745             return FALSE;
00746         }
00747         ShapeToEdit->SetStellRadiusToPrimary(NewRatio);
00748     }
00749 
00750     // See if we should rotate the shape to a specific angle
00751     // OR see if we should rotate the shape by a specified ammount
00752     if ((NewShapeData->RotateTo != -1000.0) || (NewShapeData->RotateBy != -1000.0) )
00753     {
00754         // Get rotate matrix
00755         ANGLE RotateBy;
00756         const DocCoord MajorOrigin = ShapeToEdit->GetMajorAxes() - ShapeToEdit->GetCentrePoint();
00757         const double CurrentAngle = atan2((double)MajorOrigin.y, (double)MajorOrigin.x) ;
00758         if (NewShapeData->RotateTo != -1000.0)
00759         {
00760             RotateBy = -(CurrentAngle - NewShapeData->RotateTo) * (180/PI); 
00761         }
00762         else
00763         {                                                                  
00764             RotateBy = NewShapeData->RotateBy * (180/PI); 
00765         }       
00766         
00767         Trans2DMatrix* RotateMat = new Trans2DMatrix(ShapeToEdit->GetCentrePoint(), RotateBy);
00768         if (!DoTransformNode(ShapeToEdit, RotateMat))
00769         {
00770             return FALSE;
00771         }
00772     }
00773 
00774     // See if we should change the stellation offset to a new angle
00775     // OR see if we should change the stellation offset by a specified ammount
00776     if ((NewShapeData->SetStellationOffset != -1000.0) || (NewShapeData->AddStellationOffset != -1000.0) )
00777     {
00778         const UINT32 NumSides = ShapeToEdit->GetNumSides();
00779         const double OppositeRatio = 180.0/(360.0/NumSides);
00780         double NewRatio;
00781         if (NewShapeData->SetStellationOffset != -1000.0)
00782         {
00783             NewRatio = OppositeRatio * (NewShapeData->SetStellationOffset / 180.0);
00784         }
00785         else
00786         {
00787             double NewOffset = (ShapeToEdit->GetStellationRatio()/OppositeRatio)*180.0 + NewShapeData->AddStellationOffset;
00788 
00789             if (NewOffset < -180.0)
00790                 NewOffset = 180.0 + (NewOffset + 180.0);
00791             if (NewOffset > 180.0)
00792                 NewOffset = -180.0 + (NewOffset - 180.0);
00793             NewRatio = OppositeRatio * (NewOffset / 180.0);
00794         }
00795 
00796         if (ChangeShapeDataAction::DoToggle(this, &UndoActions, ShapeToEdit, 
00797                 ChangeShapeDataAction::CHANGE_STELLATIONOFFSET, ShapeToEdit->GetStellationRatio()) == AC_FAIL)
00798         {
00799             return FALSE;
00800         }
00801         ShapeToEdit->SetStellationRatio(NewRatio);
00802     }
00803 
00804     // See if we should change the centre point
00805 /*  if (NewShapeData->NewCentrePoint != DontCare)
00806     {
00807         if (ChangeShapePointAction::DoToggle(this, &UndoActions, ShapeToEdit, 
00808                     ChangeShapePointAction::CHANGE_CENTRE, ShapeToEdit->GetCentrePoint()) == AC_FAIL)
00809         {
00810             return FALSE;
00811         }
00812         ShapeToEdit->SetCentrePoint(NewShapeData->NewCentrePoint);
00813     }*/
00814 
00815     // See if we should translate the shape to an exact location
00816     if (NewShapeData->TranslateTo != DontCare)
00817     {
00818         DocCoord Offset = NewShapeData->TranslateTo - ShapeToEdit->GetCentrePoint() ;
00819 
00820         Trans2DMatrix* TransMat = new Trans2DMatrix(Offset.x, Offset.y);
00821         if (!DoTransformNode(ShapeToEdit, TransMat))
00822             return FALSE;
00823     }
00824 
00825     // See if we should translate the shape by an offset
00826     if (NewShapeData->TranslateBy != DontCare)
00827     {
00828         Trans2DMatrix* TransMat = new Trans2DMatrix(NewShapeData->TranslateBy.x, NewShapeData->TranslateBy.y);
00829         if (!DoTransformNode(ShapeToEdit, TransMat))
00830             return FALSE;
00831     }
00832 
00833     // See if we should change edge path one
00834     if (NewShapeData->NewEdgePath1 != NULL)
00835     {
00836         if (ChangeShapePathAction::DoReshape(this, &UndoActions, ShapeToEdit, 
00837                     ChangeShapePathAction::CHANGE_PATH1, &(ShapeToEdit->EdgePath1)) == AC_FAIL)
00838         {
00839             return FALSE;
00840         }
00841         ShapeToEdit->EdgePath1.ClearPath();
00842         ShapeToEdit->EdgePath1.CopyPathDataFrom(NewShapeData->NewEdgePath1);        
00843     }
00844 
00845     // See if we should change edge path two
00846     if (NewShapeData->NewEdgePath2 != NULL)
00847     {
00848         if (ChangeShapePathAction::DoReshape(this, &UndoActions, ShapeToEdit, 
00849                     ChangeShapePathAction::CHANGE_PATH2, &(ShapeToEdit->EdgePath2)) == AC_FAIL)
00850         {
00851             return FALSE;
00852         }
00853         ShapeToEdit->EdgePath2.ClearPath();
00854         ShapeToEdit->EdgePath2.CopyPathDataFrom(NewShapeData->NewEdgePath2);        
00855     }
00856 
00857     // Invalidate the shapes bounding rect as it's changed
00858     ShapeToEdit->InvalidateBoundingRect();
00859     ShapeToEdit->InvalidateCache();
00860 
00861     // Cause a redraw of the shape with its end bounds
00862     if (RecordBoundsAction::DoRecord(this, &UndoActions, ShapeToEdit) == AC_FAIL)
00863     {
00864         return FALSE;
00865     }
00866 
00867     return TRUE;
00868 }
00869 
00870 
00871 
00872 /********************************************************************************************
00873 
00874 >   BOOL OpEditRegularShape::ShouldDoThisShape(NodeRegularShape* pShape, EditRegularShapeParam::AffectShape Allowance)
00875 
00876     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
00877     Created:    26/03/95
00878     Inputs:     pShape - points to a regular shape 
00879                 Allowance - the current shape allowance mode
00880     Outputs:    -
00881     Returns:    TRUE if this shape should be changed in this operation
00882                 FALSE if this shape should not be changed
00883     Purpose:    Do decide wether or not the shape should be edited.  The decision is based upon
00884                 the Allowance mode.
00885     Errors:     -
00886     SeeAlso:    -
00887 
00888 ********************************************************************************************/
00889 BOOL OpEditRegularShape::ShouldDoThisShape(NodeRegularShape* pShape, EditRegularShapeParam::AffectShape Allowance)
00890 {
00891     ERROR2IF(pShape == NULL, FALSE, "Can't edit a NULL shape!");
00892 
00893     switch (Allowance)
00894     {
00895         case EditRegularShapeParam::AFFECT_ALL:
00896             return TRUE;
00897             break;
00898         case EditRegularShapeParam::AFFECT_ELLIPSES:
00899             return pShape->IsCircular();
00900             break;
00901         case EditRegularShapeParam::AFFECT_RECTANGLES:
00902             return (!pShape->IsCircular() && (pShape->GetNumSides() == 4) );
00903             break;
00904         default:
00905             ERROR3("Allowance was not known");
00906             return FALSE;
00907     }
00908 }
00909 
00910 
00911 
00912 
00914 // The ChangeShapeDataAction class                                                           //
00916 
00917 /********************************************************************************************
00918 
00919 >   ChangeShapeDataAction::ChangeShapeDataAction()
00920 
00921     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
00922     Created:    10/12/94
00923     Inputs:     -
00924     Outputs:    -
00925     Returns:    -
00926     Purpose:    Constructor for the action to toggle a data item of a regular state.
00927                 Initialises the data members to sensible defaults.
00928     Errors:     -
00929     SeeAlso:    -
00930 
00931 ********************************************************************************************/
00932 ChangeShapeDataAction::ChangeShapeDataAction()
00933 {
00934     pToggleShape = NULL;
00935     ChangeItemID = CHANGE_NUMSIDES;
00936     NewValue = 0.0;
00937 }
00938 
00939 
00940 
00941 /********************************************************************************************
00942 
00943 >   ActionCode ChangeShapeDataAction::Init( Operation* pOp,
00944                                             ActionList* pActionList,
00945                                             NodeRegularShape* pShape,
00946                                             enum ChangeItem NewItem,
00947                                             double NewData,
00948                                             Action** NewAction)
00949 
00950     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
00951     Created:    10/12/94
00952     Inputs:     pOp is the pointer to the operation to which this action belongs
00953                 pShape is a pointer to the NodeRegularShape to change
00954                 NewItem identifes the data item that shoudl be changed
00955                 NewData is the value this action should set the data item to
00956                 pActionList is the action list to which this action should be added
00957     Outputs:    NewAction is a pointer to a pointer to an action, allowing the function to
00958                 return a pointer to the created action
00959     Returns:    ActionCode, one of AC_OK, AC_NO_RECORD or AC_FAIL
00960     Purpose:    This is the function which creates an instance of this action. If there is no
00961                 room in the undo buffer (which is determined by the base class Init function
00962                 called within) the function will either return AC_NO_RECORD which means the
00963                 operation can continue, but no undo information needs to be stored, or AC_OK
00964                 which means the operation should continue AND record undo information. If
00965                 the function returns AC_FAIL, there was not enough memory to record the undo
00966                 information, and the user has decided not to continue with the operation.
00967     Errors:     -
00968     SeeAlso:    Action::Init()
00969 
00970 ********************************************************************************************/
00971 ActionCode ChangeShapeDataAction::Init( Operation* pOp,
00972                                         ActionList* pActionList,
00973                                         NodeRegularShape* pShape,
00974                                         enum ChangeItem NewItem,
00975                                         double NewData,
00976                                         Action** NewAction)
00977 {
00978     UINT32 ActSize = sizeof(ChangeShapeDataAction);         
00979 
00980     ActionCode Ac = Action::Init( pOp, pActionList, ActSize, CC_RUNTIME_CLASS(ChangeShapeDataAction), NewAction);
00981     if ((Ac == AC_OK) && (*NewAction != NULL))
00982     {
00983         ((ChangeShapeDataAction*)*NewAction)->pToggleShape = pShape;
00984         ((ChangeShapeDataAction*)*NewAction)->ChangeItemID = NewItem;
00985         ((ChangeShapeDataAction*)*NewAction)->NewValue = NewData;
00986     }
00987     return Ac;
00988 }
00989 
00990 
00991 
00992 /********************************************************************************************
00993 
00994 >   ActionCode ChangeShapeDataAction::DoToggle(Operation* pOp,
00995                                             ActionList* pActionList,
00996                                             NodeRegularShape* pShape,
00997                                             enum ChangeItem NewItem,
00998                                             double NewData)
00999 
01000     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01001     Created:    10/12/94
01002     Inputs:     pOp is the currently running operation.
01003                 pActionList is a pointer ot the action list to which the action should be appended.
01004                 pShape is a pointer to the NodeRegularShape to change.
01005                 NewItem identifes the data item that shoudl be changed
01006                 NewData is the value this action should set the data item to
01007     Outputs:    -
01008     Returns:    Action code which indicates success or failure to create the action
01009     Purpose:    This static function makes it a little easier to use this action. It creates an instance
01010                 of this action and appends it to the action list.
01011     Errors:     -
01012     SeeAlso:    -
01013 
01014 ********************************************************************************************/
01015 
01016 ActionCode ChangeShapeDataAction::DoToggle(Operation* pOp,
01017                                         ActionList* pActionList,
01018                                         NodeRegularShape* pShape,
01019                                         enum ChangeItem NewItem,
01020                                         double NewData)
01021 {
01022     // Get the runtime class info on this object and create a pointer to another object of the same type 
01023     ChangeShapeDataAction* NewAction; 
01024 
01025     // Now call the init function to set everything up
01026     ActionCode Act = ChangeShapeDataAction::Init(pOp, pActionList, pShape, NewItem, NewData, (Action**)&NewAction);
01027     return Act;
01028 }
01029 
01030 
01031 
01032 /********************************************************************************************
01033 
01034 >   ActionCode ChangeShapeDataAction::Execute()
01035 
01036     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01037     Created:    09/12/94
01038     Inputs:     -
01039     Outputs:    -
01040     Returns:    ActionCode, either AC_OK, AC_NORECORD or AC_FAIL
01041     Purpose:    Changes a data item in a Regular shape, createing another
01042                 ChangeShapeDataAction to undo the change.
01043     Errors:     ERROR3 if the internal shape pointer was NULL.  ERROR2 checks on individual
01044                 changes.
01045     SeeAlso:    -
01046 
01047 ********************************************************************************************/
01048 ActionCode ChangeShapeDataAction::Execute()
01049 {
01050     ERROR3IF(pToggleShape == NULL, "Pointer to shape was NULL.  Did you call Init/DoToggle OR handle their failure?");
01051 
01052     // Get the runtime class info on this object and create a pointer to another object of the same type 
01053     ChangeShapeDataAction *ReAction; 
01054     ActionCode Act = AC_FAIL;
01055     double ReData = 0.0;
01056 
01057     switch (ChangeItemID)
01058     {
01059         case CHANGE_NUMSIDES:
01060             ReData = pToggleShape->GetNumSides();
01061             break;
01062         case CHANGE_CIRCULAR:
01063             ReData = pToggleShape->IsCircular() ? 1.0 : 0.0;    
01064             break;
01065         case CHANGE_STELLATED:
01066             ReData = pToggleShape->IsStellated() ? 1.0 : 0.0;   
01067             break;
01068         case CHANGE_STELLRADIUSTOPRIMARY:
01069             ReData = pToggleShape->GetStellRadiusToPrimary();   
01070             break;
01071         case CHANGE_PRIMARYCURVATURE:
01072             ReData = pToggleShape->IsPrimaryCurvature() ? 1.0 : 0.0;    
01073             break;
01074         case CHANGE_STELLATIONCURVATURE:
01075             ReData = pToggleShape->IsStellationCurvature() ? 1.0 : 0.0; 
01076             break;
01077         case CHANGE_PRIMARYCURVETOPRIMARY:
01078             ReData = pToggleShape->GetPrimaryCurveToPrimary();  
01079             break;
01080         case CHANGE_STELLCURVETOSTELL:
01081             ReData = pToggleShape->GetStellCurveToStell();  
01082             break;
01083         case CHANGE_STELLATIONOFFSET:
01084             ReData = pToggleShape->GetStellationRatio();    
01085             break;
01086         default:
01087             ERROR2(Act, "What was that Change ID?!");
01088             break;
01089     }
01090     
01091     // Create a redo action for this action
01092     if (pToggleShape != NULL)
01093     {
01094         Act = ChangeShapeDataAction::Init(pOperation, pOppositeActLst, pToggleShape, ChangeItemID, 
01095                                                                             ReData, (Action**)(&ReAction));
01096 
01097         if (Act == AC_FAIL)
01098             return AC_FAIL;
01099 
01100         // Now do the actual action
01101         switch (ChangeItemID)
01102         {
01103             case CHANGE_NUMSIDES:
01104                 pToggleShape->SetNumSides((UINT32)NewValue);
01105                 break;
01106             case CHANGE_CIRCULAR:
01107                 pToggleShape->SetCircular(NewValue == 1.0 ? TRUE : FALSE);
01108                 break;
01109             case CHANGE_STELLATED:
01110                 pToggleShape->SetStellated(NewValue == 1.0 ? TRUE : FALSE);
01111                 break;
01112             case CHANGE_STELLRADIUSTOPRIMARY:
01113                 pToggleShape->SetStellRadiusToPrimary(NewValue);
01114                 break;
01115             case CHANGE_PRIMARYCURVATURE:
01116                 pToggleShape->SetPrimaryCurvature(NewValue == 1.0 ? TRUE : FALSE);
01117                 break;
01118             case CHANGE_STELLATIONCURVATURE:
01119                 pToggleShape->SetStellationCurvature(NewValue == 1.0 ? TRUE : FALSE);
01120                 break;
01121             case CHANGE_PRIMARYCURVETOPRIMARY:
01122                 pToggleShape->SetPrimaryCurveToPrimary(NewValue);
01123                 break;
01124             case CHANGE_STELLCURVETOSTELL:
01125                 pToggleShape->SetStellCurveToStell(NewValue);
01126                 break;
01127             case CHANGE_STELLATIONOFFSET:
01128                 pToggleShape->SetStellationRatio(NewValue);
01129                 break;
01130             default:
01131                 ERROR3("What was that Change ID?!");
01132                 break;
01133         }
01134         pToggleShape->InvalidateBoundingRect();
01135         pToggleShape->InvalidateCache();
01136     }
01137 
01138     return Act;
01139 }
01140 
01141 
01142 
01144 // The ChangeShapePointAction class                                                          //
01146 
01147 /********************************************************************************************
01148 
01149 >   ChangeShapePointAction::ChangeShapePointAction()
01150 
01151     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01152     Created:    13/12/94
01153     Inputs:     -
01154     Outputs:    -
01155     Returns:    -
01156     Purpose:    Constructor for the action to change a point in a regular shape.
01157                 Initialises the data members to sensible defaults.
01158     Errors:     -
01159     SeeAlso:    -
01160 
01161 ********************************************************************************************/
01162 ChangeShapePointAction::ChangeShapePointAction()
01163 {
01164     pToggleShape = NULL;
01165     ChangeItemID = CHANGE_MAJOR;
01166     NewValue = DocCoord(0,0);
01167 }
01168 
01169 
01170 
01171 /********************************************************************************************
01172 
01173 >   ActionCode ChangeShapePointAction::Init(    Operation* pOp,
01174                                             ActionList* pActionList,
01175                                             NodeRegularShape* pShape,
01176                                             enum ChangeItem NewItem,
01177                                             DocCoord NewData,
01178                                             Action** NewAction)
01179 
01180     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01181     Created:    10/12/94
01182     Inputs:     pOp is the pointer to the operation to which this action belongs
01183                 pShape is a pointer to the NodeRegularShape to change
01184                 NewItem identifes the data item that shoudl be changed
01185                 NewData is the value this action should set the data item to
01186                 pActionList is the action list to which this action should be added
01187     Outputs:    NewAction is a pointer to a pointer to an action, allowing the function to
01188                 return a pointer to the created action
01189     Returns:    ActionCode, one of AC_OK, AC_NO_RECORD or AC_FAIL
01190     Purpose:    This is the function which creates an instance of this action. If there is no
01191                 room in the undo buffer (which is determined by the base class Init function
01192                 called within) the function will either return AC_NO_RECORD which means the
01193                 operation can continue, but no undo information needs to be stored, or AC_OK
01194                 which means the operation should continue AND record undo information. If
01195                 the function returns AC_FAIL, there was not enough memory to record the undo
01196                 information, and the user has decided not to continue with the operation.
01197     Errors:     -
01198     SeeAlso:    Action::Init()
01199 
01200 ********************************************************************************************/
01201 ActionCode ChangeShapePointAction::Init(    Operation* pOp,
01202                                         ActionList* pActionList,
01203                                         NodeRegularShape* pShape,
01204                                         enum ChangeItem NewItem,
01205                                         DocCoord NewData,
01206                                         Action** NewAction)
01207 {
01208     UINT32 ActSize = sizeof(ChangeShapePointAction);            
01209 
01210     ActionCode Ac = Action::Init( pOp, pActionList, ActSize, CC_RUNTIME_CLASS(ChangeShapePointAction), NewAction);
01211     if ((Ac == AC_OK) && (*NewAction != NULL))
01212     {
01213         ((ChangeShapePointAction*)*NewAction)->pToggleShape = pShape;
01214         ((ChangeShapePointAction*)*NewAction)->ChangeItemID = NewItem;
01215         ((ChangeShapePointAction*)*NewAction)->NewValue = NewData;
01216     }
01217     return Ac;
01218 }
01219 
01220 
01221 
01222 /********************************************************************************************
01223 
01224 >   ActionCode ChangeShapePointAction::DoToggle(Operation* pOp,
01225                                             ActionList* pActionList,
01226                                             NodeRegularShape* pShape,
01227                                             enum ChangeItem NewItem,
01228                                             DocCoord NewData)
01229 
01230     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01231     Created:    10/12/94
01232     Inputs:     pOp is the currently running operation.
01233                 pActionList is a pointer ot the action list to which the action should be appended.
01234                 pShape is a pointer to the NodeRegularShape to change.
01235                 NewItem identifes the data item that shoudl be changed
01236                 NewData is the value this action should set the data item to
01237     Outputs:    -
01238     Returns:    Action code which indicates success or failure to create the action
01239     Purpose:    This static function makes it a little easier to use this action. It creates an instance
01240                 of this action and appends it to the action list.
01241     Errors:     -
01242     SeeAlso:    -
01243 
01244 ********************************************************************************************/
01245 
01246 ActionCode ChangeShapePointAction::DoToggle(Operation* pOp,
01247                                         ActionList* pActionList,
01248                                         NodeRegularShape* pShape,
01249                                         enum ChangeItem NewItem,
01250                                         DocCoord NewData)
01251 {
01252     // Get the runtime class info on this object and create a pointer to another object of the same type 
01253     ChangeShapePointAction* NewAction; 
01254 
01255     // Now call the init function to set everything up
01256     ActionCode Act = ChangeShapePointAction::Init(pOp, pActionList, pShape, NewItem, NewData, (Action**)&NewAction);
01257     return Act;
01258 }
01259 
01260 
01261 
01262 /********************************************************************************************
01263 
01264 >   ActionCode ChangeShapePointAction::Execute()
01265 
01266     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01267     Created:    09/12/94
01268     Inputs:     -
01269     Outputs:    -
01270     Returns:    ActionCode, either AC_OK, AC_NORECORD or AC_FAIL
01271     Purpose:    Changes a data item in a Regular shape, createing another
01272                 ChangeShapePointAction to undo the change.
01273     Errors:     ERROR3 if the internal shape pointer was NULL.  ERROR2 checks on individual
01274                 changes.
01275     SeeAlso:    -
01276 
01277 ********************************************************************************************/
01278 ActionCode ChangeShapePointAction::Execute()
01279 {
01280     ERROR3IF(pToggleShape == NULL, "Pointer to shape was NULL.  Did you call Init/DoToggle OR handle their failure?");
01281 
01282     // Get the runtime class info on this object and create a pointer to another object of the same type 
01283     ChangeShapePointAction *ReAction; 
01284     ActionCode Act = AC_FAIL;
01285     DocCoord ReData(0,0);
01286 
01287     switch (ChangeItemID)
01288     {
01289         case CHANGE_MINOR:
01290             ReData = pToggleShape->GetUTMinorAxes();
01291             break;
01292         case CHANGE_MAJOR:
01293             ReData = pToggleShape->GetUTMajorAxes();
01294             break;
01295         case CHANGE_CENTRE:
01296             ReData = pToggleShape->GetUTCentrePoint();
01297             break;
01298         default:
01299             ERROR2(Act, "What was that Change ID?!");
01300             break;
01301     }
01302     
01303     // Create a redo action for this action
01304     if (pToggleShape != NULL)
01305     {
01306         Act = ChangeShapePointAction::Init(pOperation, pOppositeActLst, pToggleShape, ChangeItemID, 
01307                                                                             ReData, (Action**)(&ReAction));
01308 
01309         if (Act == AC_FAIL)
01310             return AC_FAIL;
01311 
01312         // Now do the actual action
01313         switch (ChangeItemID)
01314         {
01315             case CHANGE_MINOR:
01316                 pToggleShape->SetMinorAxes(NewValue);
01317                 break;
01318             case CHANGE_MAJOR:
01319                 pToggleShape->SetMajorAxes(NewValue);
01320                 break;
01321             case CHANGE_CENTRE:
01322                 pToggleShape->SetCentrePoint(NewValue);
01323                 break;
01324         }
01325         pToggleShape->InvalidateBoundingRect();
01326         pToggleShape->InvalidateCache();
01327     }
01328 
01329     return Act;
01330 }
01331 
01332 
01333 
01335 // The ChangeShapePathAction class                                                           //
01337 
01338 /********************************************************************************************
01339 
01340 >   ChangeShapePathAction::ChangeShapePathAction()
01341 
01342     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01343     Created:    21/12/94
01344     Inputs:     -
01345     Outputs:    -
01346     Returns:    -
01347     Purpose:    Constructor for the action to change an edge path of a regular shape.
01348                 Initialises the data members to sensible defaults.
01349     Errors:     -
01350     SeeAlso:    -
01351 
01352 ********************************************************************************************/
01353 ChangeShapePathAction::ChangeShapePathAction()
01354 {
01355     pShape = NULL;
01356     ChangeItemID = CHANGE_PATH1;
01357 }
01358 
01359 
01360 
01361 /********************************************************************************************
01362 
01363 >   ActionCode ChangeShapePathAction::Init( Operation* pOp,
01364                                             ActionList* pActionList,
01365                                             NodeRegularShape* pChangeShape,
01366                                             enum ChangeItem NewItem,
01367                                             Path* NewPath,
01368                                             Action** NewAction)
01369 
01370     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01371     Created:    21/12/94
01372     Inputs:     pOp is the pointer to the operation to which this action belongs
01373                 pShape is a pointer to the NodeRegularShape to change
01374                 NewItem identifes the data item that shoudl be changed
01375                 NewPath points to the path to store as undo infomation
01376                 pActionList is the action list to which this action should be added
01377     Outputs:    NewAction is a pointer to a pointer to an action, allowing the function to
01378                 return a pointer to the created action
01379     Returns:    ActionCode, one of AC_OK, AC_NO_RECORD or AC_FAIL
01380     Purpose:    This is the function which creates an instance of this action. If there is no
01381                 room in the undo buffer (which is determined by the base class Init function
01382                 called within) the function will either return AC_NO_RECORD which means the
01383                 operation can continue, but no undo information needs to be stored, or AC_OK
01384                 which means the operation should continue AND record undo information. If
01385                 the function returns AC_FAIL, there was not enough memory to record the undo
01386                 information, and the user has decided not to continue with the operation.
01387     Errors:     -
01388     SeeAlso:    Action::Init()
01389 
01390 ********************************************************************************************/
01391 ActionCode ChangeShapePathAction::Init( Operation* pOp,
01392                                         ActionList* pActionList,
01393                                         NodeRegularShape* pChangeShape,
01394                                         enum ChangeItem NewItem,
01395                                         Path* NewPath,
01396                                         Action** NewAction)
01397 {
01398     UINT32 ActSize = sizeof(ChangeShapePathAction);         
01399 
01400     ActionCode Ac = Action::Init( pOp, pActionList, ActSize, CC_RUNTIME_CLASS(ChangeShapePathAction), NewAction);
01401     if ((Ac == AC_OK) && (*NewAction != NULL))
01402     {
01403         ((ChangeShapePathAction*)*NewAction)->pShape = pChangeShape;
01404         ((ChangeShapePathAction*)*NewAction)->ChangeItemID = NewItem;
01405         if (!((ChangeShapePathAction*)*NewAction)->NewEdge.Initialise(NewPath->GetNumCoords()))
01406             return AC_FAIL;
01407         ((ChangeShapePathAction*)*NewAction)->NewEdge.CopyPathDataFrom(NewPath);
01408     }
01409     return Ac;
01410 }
01411 
01412 
01413 
01414 /********************************************************************************************
01415 
01416 >   ActionCode ChangeShapePathAction::DoReshape(Operation* pOp,
01417                                             ActionList* pActionList,
01418                                             NodeRegularShape* pShape,
01419                                             enum ChangeItem NewItem,
01420                                             Path* NewData)
01421 
01422     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01423     Created:    21/12/94
01424     Inputs:     pOp is the currently running operation.
01425                 pActionList is a pointer ot the action list to which the action should be appended.
01426                 pShape is a pointer to the NodeRegularShape to change.
01427                 NewItem identifes the data item that shoudl be changed
01428                 NewData poits to the path that this action should store
01429     Outputs:    -
01430     Returns:    Action code which indicates success or failure to create the action
01431     Purpose:    This static function makes it a little easier to use this action. It creates an instance
01432                 of this action and appends it to the action list.
01433     Errors:     -
01434     SeeAlso:    -
01435 
01436 ********************************************************************************************/
01437 
01438 ActionCode ChangeShapePathAction::DoReshape(Operation* pOp,
01439                                         ActionList* pActionList,
01440                                         NodeRegularShape* pShape,
01441                                         enum ChangeItem NewItem,
01442                                         Path* NewData)
01443 {
01444     // Get the runtime class info on this object and create a pointer to another object of the same type 
01445     ChangeShapePathAction* NewAction; 
01446 
01447     // Now call the init function to set everything up
01448     ActionCode Act = ChangeShapePathAction::Init(pOp, pActionList, pShape, NewItem, NewData, (Action**)&NewAction);
01449     return Act;
01450 }
01451 
01452 
01453 
01454 /********************************************************************************************
01455 
01456 >   ActionCode ChangeShapePathAction::Execute()
01457 
01458     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01459     Created:    21/12/94
01460     Inputs:     -
01461     Outputs:    -
01462     Returns:    ActionCode, either AC_OK, AC_NORECORD or AC_FAIL
01463     Purpose:    Changes a data item in a Regular shape, createing another
01464                 ChangeShapePathAction to undo the change.
01465     Errors:     ERROR2 if the internal shape pointer was NULL.
01466     SeeAlso:    -
01467 
01468 ********************************************************************************************/
01469 ActionCode ChangeShapePathAction::Execute()
01470 {
01471     ERROR2IF(pShape == NULL, AC_FAIL, "Pointer to shape was NULL.  Did you call Init/DoToggle OR handle their failure?");
01472 
01473     // Get the runtime class info on this object and create a pointer to another object of the same type 
01474     ChangeShapePathAction *ReAction; 
01475     ActionCode Act = AC_FAIL;
01476     Path* ReData;
01477 
01478     switch (ChangeItemID)
01479     {
01480         case CHANGE_PATH1:
01481             ReData = &(pShape->EdgePath1);
01482             break;
01483         case CHANGE_PATH2:
01484             ReData = &(pShape->EdgePath2);
01485             break;
01486         default:
01487             ERROR2(Act, "What was that Change ID?!");
01488             break;
01489     }
01490     
01491     // Create a redo action for this action
01492     Act = ChangeShapePathAction::Init(pOperation, pOppositeActLst, pShape, ChangeItemID, 
01493                                                                         ReData, (Action**)(&ReAction));
01494 
01495     if (Act == AC_FAIL)
01496         return AC_FAIL;
01497 
01498     // Now do the actual action
01499     switch (ChangeItemID)
01500     {
01501         case CHANGE_PATH1:
01502             pShape->EdgePath1.CopyPathDataFrom(&NewEdge);
01503             break;
01504         case CHANGE_PATH2:
01505             pShape->EdgePath2.CopyPathDataFrom(&NewEdge);
01506             break;
01507     }
01508     pShape->InvalidateBoundingRect();
01509     pShape->InvalidateCache();
01510 
01511     return Act;
01512 }
01513 
01514 
01515 
01517 //                                      OpDragRegularShape                                //
01519 
01520 
01521 
01522 /********************************************************************************************
01523 
01524 >   OpDragRegularShape::OpDragRegularShape()
01525 
01526     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01527     Created:    14/12/94
01528     Purpose:    Constructor. This sets a few of the operation variables.
01529 
01530 ********************************************************************************************/
01531 
01532 OpDragRegularShape::OpDragRegularShape()
01533 {
01534     pCursor = NULL;
01535     StartSpread = NULL;
01536     OriginalShape = NULL;
01537     EditShape = NULL;
01538 }
01539 
01540 
01541 
01542 /********************************************************************************************
01543 
01544 >   void OpDragRegularShape::DoStartDragEdit(   NodeRegularShape* pShape,
01545                                                 DocCoord Anchor,
01546                                                 Spread *pSpread,
01547                                                 DragType DragThing,
01548                                                 BOOL QuickShape)
01549     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01550     Created:    14/12/94
01551     Inputs:     pShape - Pointer to the shape we are about to edit
01552                 Anchor - The position of the mouse at the start of the Drag
01553                 pSpread - The spread that the shape is on
01554                 DragThing - the object on the shape that should be dragged
01555                 QuickShape - TRUE to drag about the centres of shapes, FALSE to do special
01556                 shape drags
01557     Purpose:    This is called to start a drag operation on a regular shape
01558     Errors:     If there was not enough memory for the setup then the user is informed and
01559                 the op is aborted
01560 
01561 ********************************************************************************************/
01562 
01563 void OpDragRegularShape::DoStartDragEdit( NodeRegularShape* pShape, DocCoord Anchor, Spread *pSpread,
01564                                                                  DragType DragThing, BOOL QuickShape)
01565 {
01566     // Adjust anchor point so this is the actual point on the shape that the drag starts from
01567     INT32 DragPointNumber = -1;
01568     pShape->DetermineClickEffect(&Anchor, pSpread, &DragPointNumber);
01569 
01570     // Take note of the drag parameters
01571     DragObject = DragThing;
01572     LastMousePos = Anchor;
01573     StartMousePos = Anchor;
01574     StartSpread  = pSpread;
01575     OriginalShape = pShape;
01576     DragAroundCentre = QuickShape;
01577 
01578     // We also need to make a version of the shape that we can change
01579     EditShape = (NodeRegularShape*) OriginalShape->SimpleCopy();
01580     BOOL Success = (EditShape != NULL);
01581 
01582     if (Success)
01583     {
01584         // Create the cursors and display it (if it was created ok)
01585         pCursor = new Cursor(TOOLID_BEZTOOL, _R(IDC_MOVEBEZIERCURSOR));
01586 
01587         if ( pCursor != NULL && pCursor->IsValid())
01588         {   // Select the cursor
01589             CurrentCursorID = CursorStack::GPush(pCursor);
01590         }
01591         else
01592         {   // Set cursor pointer to NULL to indicate its non appearence
01593             if (pCursor != NULL)
01594                 delete pCursor;
01595             pCursor = NULL;
01596         }
01597 
01598         // And tell the Dragging system that we need drags to happen
01599         Success = StartDrag(DRAGTYPE_AUTOSCROLL, NULL, &LastMousePos, FALSE);
01600 
01601         // Render the initial shape
01602         if (Success)
01603             RenderDragBlobs(DocRect(), pSpread, FALSE);
01604     }
01605 
01606     // Do special rectangle setup
01607     if (Success && (DragObject == DRAG_PRIMARY) && OriginalShape->IsARectangle())
01608     {
01609         if (DragPointNumber == -1)
01610         {
01611             ERROR3("Couldn't find the rectangle drag point");
01612             Success = FALSE;
01613         }
01614 
01615         // Get the significant points
01616         DocCoord* PointsArray = NULL;
01617         INT32 NumberPoints = 0;
01618         Success = OriginalShape->BuildPolygonPoints(&PointsArray, &NumberPoints);
01619 
01620         // Transform the significant points to document space
01621         if (Success)
01622         {
01623             // Get the shape matrix and its inverse
01624             Matrix ShapeMat;
01625             OriginalShape->GetTransformMatrix(&ShapeMat);
01626             ShapeMat.transform((Coord*)PointsArray,NumberPoints);
01627         }
01628 
01629         // Get the points opposite to the drag point, the stationary point
01630         INT32 Opposite = 0;
01631         if (Success)
01632             Opposite = (DragPointNumber + ((NumberPoints-2)/2)) % (NumberPoints-2);
01633 
01634         // Copy the points into the array.  ) is the opposite point, 2 is the drag point
01635         if (Success)
01636         {
01637             INT32 Increment = (NumberPoints-2)/4;
01638             INT32 Copy = 0;
01639             for (INT32 i = Opposite; Copy < 4; i+=Increment)
01640                 RectangleArray[Copy++] = PointsArray[i%(NumberPoints-2)];
01641             
01642             // Make the array go clockwise
01643             DocCoord temp = RectangleArray[1];
01644             RectangleArray[1] = RectangleArray[3];
01645             RectangleArray[3] = temp;
01646         }
01647 
01648         delete PointsArray;
01649     }
01650 
01651     if (!Success)
01652     {
01653         InformError();
01654         FailAndExecute();
01655         End();
01656     }
01657 }
01658 
01659 
01660 
01661 /********************************************************************************************
01662 
01663 >   void OpDragRegularShape::DoStartDragEditCurve(NodeRegularShape* pShape, DocCoord Anchor,
01664                             Spread *pSpread, DragType DragThing, DocCoord Start, DocCoord End)
01665 
01666     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01667     Created:    14/12/94
01668     Inputs:     pShape - Pointer to the shape we are about to edit
01669                 Anchor - The position of the mouse at the start of the Drag
01670                 pSpread - The spread that the shape is on
01671                 DragThing - the object on the shape that should be dragged
01672                 Start - the start of the line along which the curvature point is to be dragged
01673                 End - the end of the line along which the curvature point is to be dragged.
01674     Purpose:    This is called to start a drag operation on a regular shape
01675     Errors:     If there was not enought memory for the setup then the user is informed and
01676                 the op is aborted
01677 
01678 ********************************************************************************************/
01679 
01680 void OpDragRegularShape::DoStartDragEditCurve( NodeRegularShape* pShape, DocCoord Anchor,
01681                             Spread *pSpread, DragType DragThing, DocCoord Start, DocCoord End)
01682 {
01683     DoStartDragEdit(pShape, Anchor, pSpread, DragThing, TRUE);
01684 
01685     // Take note of the additional drag parameters
01686     LineStart = Start;
01687     LineEnd = End;
01688 }
01689 
01690 
01691 /********************************************************************************************
01692 >   virtual BOOL OpDragRegularShape::SnappingDrag()
01693 
01694     Author:     Ed_Cornes (Xara Group Ltd) <camelotdev@xara.com>
01695     Created:    26/10/95
01696     Returns:    FALSE if the current drag is not of the snapping flavor
01697 ********************************************************************************************/
01698 
01699 BOOL OpDragRegularShape::SnappingDrag()
01700 {
01701     BOOL NonSnappingDrag = (DragObject==DRAG_PRIMARYCURVE || DragObject==DRAG_STELLATIONCURVE);
01702     return !NonSnappingDrag;
01703 }
01704 
01705 
01706 /********************************************************************************************
01707 
01708 >   void OpDragRegularShape::DragPointerMove( DocCoord PointerPos, ClickModifiers ClickMods, Spread *pSpread, BOOL bSolidDrag)
01709 
01710     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01711     Created:    14/12/94
01712     Inputs:     PointerPos - The current position of the mouse in Doc Coords
01713                 ClickMods - Which key modifiers are being pressed
01714                 pSpread - The spread containing PointerPos
01715     Purpose:    This is called every time the mouse moves, during a drag.  We need to render
01716                 off the shape in its current position, calculate any changes to apply to it
01717                 then render it back on
01718     SeeAlso:    ClickModifiers
01719 
01720 ********************************************************************************************/
01721 
01722 void OpDragRegularShape::DragPointerMove( DocCoord PointerPos, ClickModifiers ClickMods, Spread *pSpread, BOOL bSolidDrag)
01723 {
01724     // If the PointerPois is the same as the last one, then dont bother doing anything
01725     if (PointerPos == LastMousePos)
01726         return;
01727     
01728     // If drag has moved onto a different spread, convert the coord to be relative to the
01729     // original spread.
01730     if (pSpread != StartSpread)
01731         PointerPos = MakeRelativeToSpread(StartSpread, pSpread, PointerPos);
01732 
01733     // Rub out the old EORed version of the path
01734     RenderDragBlobs( DocRect(), StartSpread, bSolidDrag);
01735 
01736     // Now apply the changes to the EditShape, depending on the drag type
01737     BOOL Success = FALSE;
01738     switch (DragObject)
01739     {
01740         case DRAG_CENTRE:
01741         {
01742             Success = ProcessCentre(PointerPos, ClickMods);
01743             break;
01744         }
01745         case DRAG_ELLIPSE:
01746         {
01747             if (DragAroundCentre)
01748                 Success = ProcessPrimary(PointerPos, ClickMods);
01749             else
01750                 Success = ProcessEllipse(PointerPos, ClickMods);
01751             break;
01752         }
01753         case DRAG_PRIMARY:
01754         {
01755             if (EditShape->IsARectangle() && !DragAroundCentre)
01756                 Success = ProcessRectangle(PointerPos, ClickMods);
01757             else
01758                 Success = ProcessPrimary(PointerPos, ClickMods);
01759             break;
01760         }
01761         case DRAG_STELLATION:
01762         {
01763             Success = ProcessStellation(PointerPos, ClickMods);
01764             break;
01765         }
01766         case DRAG_PRIMARYCURVE:
01767         {
01768             Success = ProcessPrimaryCurve(PointerPos);
01769             break;
01770         }
01771         case DRAG_STELLATIONCURVE:
01772         {
01773             Success = ProcessStellationCurve(PointerPos);
01774             break;
01775         }
01776         default:
01777             ERROR3("What was the drag type?");
01778             break;
01779     }
01780 
01781     if (Success)
01782     {
01783         // Draw in the new version of the path
01784         RenderDragBlobs( DocRect(), StartSpread, bSolidDrag);
01785 
01786         // Update the Last Mouse Position
01787         LastMousePos = PointerPos;
01788 
01789         // Tell anyone that's interested
01790         BROADCAST_TO_ALL(ShapeEditedMsg(EditShape, pSpread));
01791     }
01792     else
01793     {
01794         InformError();
01795         EndDrag();
01796         FailAndExecute();
01797         End();
01798     }
01799 }
01800 
01801 
01802 
01803 /********************************************************************************************
01804 
01805 >   void OpDragRegularShape::DragFinished( DocCoord PointerPos, ClickModifiers ClickMods, BOOL Success, BOOL bSolidDrag)
01806 
01807     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01808     Created:    12/14/94
01809     Inputs:     PointerPos - The position of the mouse at the end of the drag
01810                 ClickMods - the key modifiers being pressed
01811                 Success - TRUE if the drag was terminated properly, FALSE if it
01812                 was ended with the escape key being pressed
01813     Purpose:    This is called when a drag operation finishes.  If the drag was sucessful then
01814                 the OriginalShape in the document is updated.
01815     SeeAlso:    ClickModifiers
01816 
01817 ********************************************************************************************/
01818 
01819 void OpDragRegularShape::DragFinished( DocCoord PointerPos, ClickModifiers ClickMods, 
01820                                        Spread* pSpread, BOOL Success, BOOL bSolidDrag)
01821 {
01822     // Do some tidying up
01823     RenderDragBlobs( DocRect(), StartSpread, bSolidDrag );
01824     if (pCursor != NULL)
01825     {
01826         CursorStack::GPop(CurrentCursorID);
01827         delete pCursor;
01828         CurrentCursorID = 0;
01829         pCursor = NULL;
01830     }
01831     EndDrag();
01832     BeginSlowJob();
01833 
01834     if ( Success )
01835     {
01836         BOOL Failed = !DoStartSelOp(FALSE, TRUE, FALSE, TRUE);
01837         
01838         //DocView* pDocView = DocView::GetSelected();
01839         //ERROR3IF( pDocView == NULL, "There was no selected docview when editing a shape" );
01840 
01841         // Will the original shape allow the op to happen?
01842         ObjChangeFlags cFlags;
01843         cFlags.TransformNode = TRUE;
01844         ObjChangeParam ObjChange(OBJCHANGE_STARTING,cFlags,OriginalShape,this);
01845         Failed = !OriginalShape->AllowOp(&ObjChange);
01846 
01847         if (!Failed)
01848             Failed = (RecalcBoundsAction::DoRecalc(this, &UndoActions, OriginalShape) == AC_FAIL);
01849 
01850         // Now update the shape depending on the dragtype
01851         if (!Failed)
01852         {
01853             switch (DragObject)
01854             {
01855                 case DRAG_CENTRE:
01856                 {
01857                     // Construct a transformation matrix to move the shape
01858                     Trans2DMatrix* TransMat = new Trans2DMatrix(EditShape->GetCentrePoint().x - OriginalShape->GetCentrePoint().x,
01859                                             EditShape->GetCentrePoint().y - OriginalShape->GetCentrePoint().y);
01860                     DoTransformNode(OriginalShape, TransMat);
01861                     break;
01862                 }
01863                 case DRAG_ELLIPSE:
01864                 case DRAG_PRIMARY:
01865                 {   // Transform shape using the matrix
01866                     Trans2DMatrix* tm = new Trans2DMatrix(transMat);
01867                     Failed = ! DoTransformNode(OriginalShape, tm) ;
01868                     Failed |= !ChangeShapePointAction::DoToggle(this, &UndoActions, OriginalShape, 
01869                                                 ChangeShapePointAction::CHANGE_CENTRE, 
01870                                                             OriginalShape->GetUTCentrePoint());
01871                     OriginalShape->SetCentrePoint(EditShape->GetUTCentrePoint());
01872                     Failed |= !ChangeShapePointAction::DoToggle(this, &UndoActions, OriginalShape, 
01873                                                 ChangeShapePointAction::CHANGE_MINOR, 
01874                                                             OriginalShape->GetUTMinorAxes());
01875                     OriginalShape->SetMinorAxes(EditShape->GetUTMinorAxes());
01876                     Failed |= !ChangeShapePointAction::DoToggle(this, &UndoActions, OriginalShape, 
01877                                                 ChangeShapePointAction::CHANGE_MAJOR, 
01878                                                             OriginalShape->GetUTMajorAxes());
01879                     OriginalShape->SetMajorAxes(EditShape->GetUTMajorAxes());
01880                     break;
01881                 }
01882                 case DRAG_STELLATION:
01883                     Failed = !ChangeShapeDataAction::DoToggle(this, &UndoActions, OriginalShape, 
01884                                                 ChangeShapeDataAction::CHANGE_STELLRADIUSTOPRIMARY, 
01885                                                             OriginalShape->GetStellRadiusToPrimary() );
01886                     OriginalShape->SetStellRadiusToPrimary(EditShape->GetStellRadiusToPrimary());
01887                     Failed = !ChangeShapeDataAction::DoToggle(this, &UndoActions, OriginalShape, 
01888                                                 ChangeShapeDataAction::CHANGE_STELLATIONOFFSET, 
01889                                                             OriginalShape->GetStellationRatio() );
01890                     OriginalShape->SetStellationRatio(EditShape->GetStellationRatio());
01891                     break;
01892                 case DRAG_PRIMARYCURVE:
01893                     Failed = !ChangeShapeDataAction::DoToggle(this, &UndoActions, OriginalShape, 
01894                                                 ChangeShapeDataAction::CHANGE_PRIMARYCURVETOPRIMARY, 
01895                                                             OriginalShape->GetPrimaryCurveToPrimary() );
01896                     OriginalShape->SetPrimaryCurveToPrimary(EditShape->GetPrimaryCurveToPrimary());
01897                     break;
01898                 case DRAG_STELLATIONCURVE:
01899                     Failed = !ChangeShapeDataAction::DoToggle(this, &UndoActions, OriginalShape, 
01900                                                     ChangeShapeDataAction::CHANGE_STELLCURVETOSTELL, 
01901                                                             OriginalShape->GetStellCurveToStell() );
01902                     OriginalShape->SetStellCurveToStell(EditShape->GetStellCurveToStell());
01903                     break;
01904                 default:
01905                     ERROR3("What was that drag type?");
01906                     break;
01907             }
01908         }
01909 
01910         // Recalculate the path's bounding box
01911         OriginalShape->InvalidateBoundingRect();
01912         OriginalShape->InvalidateCache();
01913         
01914         // tell the world that something in the selection has changed so that selection bounds are updated
01915         SelRange *Sel = GetApplication()->FindSelection();
01916         Sel->Update();
01917 
01918         // Try to expand the pasteboard if necessary to include the new bounds of the object
01919         DocRect NewBounds = Sel->GetBoundingRect();
01920         ERROR3IF(pSpread == NULL, "Unexpectedly NULL Spread pointer");
01921         pSpread->ExpandPasteboardToInclude(NewBounds);
01922 
01923         if (!Failed)
01924         {
01925             // Update effected parents
01926             ObjChange.Define(OBJCHANGE_FINISHED,cFlags,OriginalShape,this);
01927             Failed = !UpdateChangedNodes(&ObjChange);
01928         }
01929 
01930         if (!Failed && OriginalShape->FindParentSpread()!=NULL)
01931             Failed = (RecordBoundsAction::DoRecord(this, &UndoActions, OriginalShape) == AC_FAIL) ;
01932 
01933         // If any of the above failed the kill the op
01934         if (Failed)
01935         {
01936 //          PATRACE( _T("An action failed!\n"));
01937             FailAndExecute();
01938         }
01939     }
01940     else
01941     {
01942         // Set up the flags that say it all went wrong
01943         FailAndExecute();
01944     }
01945 
01946     delete (EditShape);
01947     End();
01948 }
01949 
01950 
01951 
01952 /********************************************************************************************
01953 
01954 >   BOOL OpDragRegularShape::ProcessPrimaryCurve( DocCoord PointerPos )
01955 
01956     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01957     Created:    4/1/94
01958     Inputs:     PointerPos - The current position of the mouse in Doc Coords
01959     Purpose:    This is called to update the EditShape after a mouse movement in a primary
01960                 curvature point drag.
01961     SeeAlso:    OpDragRegularShape::DragPointerMove
01962 
01963 ********************************************************************************************/
01964 
01965 BOOL OpDragRegularShape::ProcessPrimaryCurve( DocCoord PointerPos )
01966 {
01967     // Untransform the various things
01968     Matrix Mat;
01969     EditShape->GetTransformMatrix(&Mat);
01970     Mat = Mat.Inverse();
01971     DocCoord UTLineStart = LineStart;
01972     DocCoord UTLineEnd = LineEnd;
01973     DocCoord UTPointerPos = PointerPos;
01974     Mat.transform(&UTLineStart);
01975     Mat.transform(&UTLineEnd);
01976     Mat.transform(&UTPointerPos);
01977 
01978     // Move the point along the edge line to get new ratio
01979     double c = UTLineStart.Distance(UTPointerPos);
01980     double b = fabs(DocCoord::DistanceFromLine(UTLineStart, UTLineEnd, UTPointerPos));
01981     double a = sqrt(c*c - b*b);
01982     double NewRatio = a/EditShape->GetUTMajorAxes().Distance(EditShape->GetUTCentrePoint());
01983 
01984     // Detect a zero ratio case (otherwise the point moves back along the line)
01985     if (!IsJoinAcute(&UTLineStart, &UTLineEnd, &UTPointerPos))
01986     {
01987         NewRatio = 0.0;
01988     }
01989     else
01990     {
01991         // Restrict to fit with along the edge
01992         if (EditShape->IsStellated())
01993         {
01994             // Get the position of the stellation curvature point and its distance
01995             const double OuterMajorLength = OriginalShape->GetMajorRadiusSize();
01996             const double StellationCurvatureLength = OuterMajorLength * OriginalShape->GetStellCurveToStell();
01997             const double AvailableLength = (UTLineStart.Distance(UTLineEnd)) - StellationCurvatureLength;
01998 
01999             double top = a;
02000             if ( (AvailableLength - a) < 1.0)
02001                 top = AvailableLength;
02002 
02003             NewRatio = top/EditShape->GetUTMajorAxes().Distance(EditShape->GetUTCentrePoint());
02004         }
02005         else
02006         {
02007             // We need to limit the drag to halfway along the drag line
02008             const double MaxLength = UTLineStart.Distance(UTLineEnd)/2.0;
02009             if (a > MaxLength)
02010                 NewRatio = MaxLength/EditShape->GetUTMajorAxes().Distance(EditShape->GetUTCentrePoint());
02011         }
02012     }
02013 
02014     EditShape->SetPrimaryCurveToPrimary(NewRatio);
02015 
02016     return TRUE;
02017 }
02018 
02019 
02020 
02021 /********************************************************************************************
02022 
02023 >   BOOL OpDragRegularShape::ProcessStellationCurve( DocCoord PointerPos )
02024 
02025     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02026     Created:    6/1/94
02027     Inputs:     PointerPos - The current position of the mouse in Doc Coords
02028     Purpose:    This is called to update the EditShape after a mouse movement in a stellation
02029                 curvature point drag.
02030     SeeAlso:    OpDragRegularShape::DragPointerMove
02031 
02032 ********************************************************************************************/
02033 
02034 BOOL OpDragRegularShape::ProcessStellationCurve( DocCoord PointerPos )
02035 {
02036     // Untransform the various things
02037     Matrix Mat;
02038     EditShape->GetTransformMatrix(&Mat);
02039     Mat = Mat.Inverse();
02040     DocCoord UTLineStart = LineStart;
02041     DocCoord UTLineEnd = LineEnd;
02042     DocCoord UTPointerPos = PointerPos;
02043     Mat.transform(&UTLineStart);
02044     Mat.transform(&UTLineEnd);
02045     Mat.transform(&UTPointerPos);
02046 
02047     // Move the point along the edge line to get new ratio
02048     double c = UTLineStart.Distance(UTPointerPos);
02049     double b = fabs(DocCoord::DistanceFromLine(UTLineStart, UTLineEnd, UTPointerPos));
02050     double a = sqrt(c*c - b*b);
02051     double NewRatio = a/EditShape->GetUTMajorAxes().Distance(EditShape->GetUTCentrePoint());
02052 
02053     // Detect a zero ratio case (otherwise the point moves back along the line)
02054     if (!IsJoinAcute(&UTLineStart, &UTLineEnd, &UTPointerPos))
02055     {
02056         NewRatio = 0.0;
02057     }
02058     else
02059     {
02060         // Restrict to fit with along the edge
02061         const double OuterMajorLength = OriginalShape->GetMajorRadiusSize();
02062         const double PrimaryCurvatureLength = OuterMajorLength * OriginalShape->GetPrimaryCurveToPrimary();
02063         const double AvailableLength = (UTLineStart.Distance(UTLineEnd)) - PrimaryCurvatureLength;
02064 
02065         double top = a;
02066         if ( (AvailableLength - a) < 1.0)
02067             top = AvailableLength;
02068 
02069         NewRatio = top/EditShape->GetUTMajorAxes().Distance(EditShape->GetUTCentrePoint());
02070     }
02071 
02072     EditShape->SetStellCurveToStell(NewRatio);
02073 
02074     return TRUE;
02075 }
02076 
02077 
02078 
02079 /********************************************************************************************
02080 
02081 >   BOOL OpDragRegularShape::ProcessStellation( DocCoord PointerPos, ClickModifiers Mods )
02082 
02083     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02084     Created:    9/1/94
02085     Inputs:     PointerPos - The current position of the mouse in Doc Coords
02086     Purpose:    This is called to update the EditShape after a mouse movement in a stellation
02087                 point drag.
02088     SeeAlso:    OpDragRegularShape::DragPointerMove
02089 
02090 ********************************************************************************************/
02091 
02092 BOOL OpDragRegularShape::ProcessStellation( DocCoord PointerPos, ClickModifiers Mods)
02093 {
02094     // Constrain the mouse movement
02095     if (Mods.Constrain)
02096     {
02097         WheelConstrain(&PointerPos, StartMousePos);
02098     }
02099 
02100     // Snap the mouse point to the active grid
02101     DocView::SnapCurrent(StartSpread,&PointerPos);
02102 
02103     // Untransform the current pointer pos
02104     DocCoord UTPointerPos = GetUTPosition(PointerPos);
02105     DocCoord UTStartPos = GetUTPosition(StartMousePos);
02106 
02107     // Set the new stellation radius length
02108     double PrimaryRadiusLength = OriginalShape->GetUTMajorAxes().Distance(OriginalShape->GetUTCentrePoint());
02109     double CurrentRadiusLength = UTPointerPos.Distance(OriginalShape->GetUTCentrePoint());
02110     double NewRadius = min( CurrentRadiusLength / PrimaryRadiusLength, 1.0 );
02111     EditShape->SetStellRadiusToPrimary(NewRadius);
02112 
02113     // Now set the new stellation point offset
02114     // Get angle between the Major axes point and the pointer pos
02115     DocCoord OldPos = UTStartPos - OriginalShape->GetUTCentrePoint();
02116     DocCoord NewPos = UTPointerPos - OriginalShape->GetUTCentrePoint();
02117 
02118     if (NewPos != DocCoord(0,0))
02119     {
02120         double OrigAngle = atan2((double)OldPos.y, (double)OldPos.x);
02121         double NewAngle = atan2((double)NewPos.y, (double)NewPos.x);
02122         double OppositeRatio = 180.0/(360.0/EditShape->GetNumSides()) ;
02123         double SettingRatio = ((NewAngle-OrigAngle) / ((2*PI)/EditShape->GetNumSides()) ) + OriginalShape->GetStellationRatio(); 
02124 
02125         while (SettingRatio > OppositeRatio*2)
02126             SettingRatio -= OppositeRatio*2;
02127         while (SettingRatio < -OppositeRatio*2)
02128             SettingRatio += OppositeRatio*2;
02129 
02130         if (SettingRatio > OppositeRatio)
02131             SettingRatio = -OppositeRatio + (SettingRatio-OppositeRatio);
02132         if (SettingRatio < -OppositeRatio)
02133             SettingRatio = OppositeRatio + (SettingRatio+OppositeRatio);
02134             
02135         EditShape->SetStellationRatio(SettingRatio);
02136     }
02137     else
02138         EditShape->SetStellationRatio(0.0);
02139 
02140     return TRUE;
02141 }
02142 
02143 
02144 
02145 /********************************************************************************************
02146 
02147 >   BOOL OpDragRegularShape::ProcessPrimary( DocCoord PointerPos, ClickModifiers Mods )
02148 
02149     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02150     Created:    9/1/94
02151     Inputs:     PointerPos - The current position of the mouse in Doc Coords
02152     Purpose:    This is called to update the EditShape after a mouse movement in a primary
02153                 point drag.
02154     SeeAlso:    OpDragRegularShape::DragPointerMove, OpDragRegularShape::ProcessRadius
02155 
02156 ********************************************************************************************/
02157 
02158 BOOL OpDragRegularShape::ProcessPrimary( DocCoord PointerPos, ClickModifiers Mods )
02159 {
02160     if (Mods.Constrain)
02161     {
02162         WheelConstrain(&PointerPos, StartMousePos);
02163     }
02164 
02165     return ProcessRadius(PointerPos, Mods);
02166 }
02167 
02168 
02169 
02170 /********************************************************************************************
02171 
02172 >   BOOL OpDragRegularShape::ProcessEllipse( DocCoord PointerPos, ClickModifiers Mods )
02173 
02174     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02175     Created:    08/04/95
02176     Inputs:     PointerPos - The current position of the mouse in Doc Coords
02177                 Mods - the drag modifiers
02178     Purpose:    This is called to update the ellipitical EditShape after a mouse movement during
02179                 a drag.
02180     SeeAlso:    OpDragRegularShape::DragPointerMove, OpDragRegularShape::ProcessRectangle
02181 
02182 ********************************************************************************************/
02183 
02184 BOOL OpDragRegularShape::ProcessEllipse( DocCoord PointerPos, ClickModifiers Mods )
02185 {
02186     // If this is an adjust drag then work around the centre
02187     if (Mods.Adjust)
02188     {
02189         if (Mods.Constrain)
02190             WheelConstrain(&PointerPos, StartMousePos);
02191 
02192         return ProcessRadius(PointerPos, Mods);
02193     }
02194 
02195     // Snap the mouse point to the active grid
02196     DocView::SnapCurrent(StartSpread,&PointerPos);
02197 
02198     // Untransform the current pointer pos
02199     DocCoord UTPointerPos = GetUTPosition(PointerPos);
02200     const DocCoord UTCentre = OriginalShape->GetUTCentrePoint();
02201     const DocCoord Centre = OriginalShape->GetCentrePoint();
02202     Matrix OriginalMat;
02203     OriginalShape->GetTransformMatrix(&OriginalMat);
02204     DocCoord UTStartPos = GetUTPosition(StartMousePos);
02205     DocCoord UTOppositePos = DocCoord::PositionPointFromRatio(UTCentre, UTStartPos, -1.0);
02206     DocCoord OppositePos = UTOppositePos;
02207     OriginalMat.transform(&OppositePos);
02208 
02209     // Get rotation of original and current dragging points
02210     DocCoord StartDragPoint = StartMousePos - OppositePos;
02211     double OrigRotation = atan2((double)StartDragPoint.y, (double)StartDragPoint.x);
02212 
02213     // Get the scaling factor
02214     const double OriginalLength = OppositePos.Distance(StartMousePos);
02215     const double Ax = (StartMousePos.x-OppositePos.x);
02216     const double Ay = (StartMousePos.y-OppositePos.y);
02217     const double Bx = (PointerPos.x-OppositePos.x);
02218     const double By = (PointerPos.y-OppositePos.y);
02219     const double NewLength = ((Ax*Bx) + (Ay*By)) / sqrt((Ax*Ax) + (Ay*Ay));
02220     double ScaleFactor = NewLength/OriginalLength;
02221 
02222     // Transform to origin, rotate so drag point to opposite lies on the origin,
02223     // scale, rotate back to current rotation, then transform back
02224     Matrix Transform(-OppositePos.x, -OppositePos.y);
02225     Transform*= Matrix(ANGLE(-(OrigRotation*(180/PI))));
02226     Transform*= Matrix(fixed16(ScaleFactor),fixed16(1.0));
02227     Transform*= Matrix(ANGLE(OrigRotation*(180/PI)));
02228     Transform*= Matrix(OppositePos.x, OppositePos.y);
02229     Trans2DMatrix   Trans(Transform);
02230 
02231     // Copy the original shape then apply the transform
02232     delete EditShape;
02233     EditShape = (NodeRegularShape*)OriginalShape->SimpleCopy();
02234     if (EditShape != NULL)
02235     {
02236         EditShape->Transform(Trans);
02237         transMat = Transform;
02238         return TRUE;
02239     }
02240     else
02241         return FALSE;
02242 }
02243 
02244 
02245 
02246 /********************************************************************************************
02247 
02248 >   BOOL OpDragRegularShape::ProcessRectangle( DocCoord PointerPos, ClickModifiers Mods )
02249 
02250     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02251     Created:    08/04/95
02252     Inputs:     PointerPos - The current position of the mouse in Doc Coords
02253                 Mods - the drag modifiers
02254     Purpose:    This is called to update a rectangular EditShape after a mouse movement 
02255                 during a drag on one if its corners
02256     SeeAlso:    OpDragRegularShape::DragPointerMove, OpDragRegularShape::ProcessEllipse
02257 
02258 ********************************************************************************************/
02259 
02260 BOOL OpDragRegularShape::ProcessRectangle( DocCoord PointerPos, ClickModifiers Mods )
02261 {
02262     // If this is an adjust drag then work around the centre
02263     if (Mods.Adjust)
02264     {
02265         if (Mods.Constrain)
02266             WheelConstrain(&PointerPos, StartMousePos);
02267 
02268         return ProcessRadius(PointerPos, Mods);
02269     }
02270 
02271     // See which line the mouse is nearest to determine the type of the drag
02272     if (Mods.Constrain)
02273     {
02274         double Diagonal = DocCoord::DistanceFromLine(RectangleArray[2], RectangleArray[0], PointerPos);
02275         double Prev = DocCoord::DistanceFromLine(RectangleArray[2], RectangleArray[1], PointerPos);
02276         double Next = DocCoord::DistanceFromLine(RectangleArray[2], RectangleArray[3], PointerPos);
02277 
02278         if ((Diagonal <= Prev) && (Diagonal <= Next))
02279             return ProcessRectangleDiagonal(PointerPos, Mods);
02280         else
02281         {
02282             if (Prev <= Next)
02283                 return ProcessRectangleEdge(PointerPos, RectangleArray[1]);
02284             else
02285                 return ProcessRectangleEdge(PointerPos, RectangleArray[3]);
02286         }
02287     }
02288     else
02289         return ProcessRectangleDiagonal(PointerPos, Mods);
02290 }
02291 
02292 
02293 
02294 /********************************************************************************************
02295 >   BOOL OpDragRegularShape::ProcessRectangleDiagonal( DocCoord PointerPos, ClickModifiers Mods )
02296 
02297     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02298     Created:    12/12/95
02299     Inputs:     PointerPos - The current position of the mouse in Doc Coords
02300                 Mods - the drag modifiers
02301     Purpose:    This is called to update a rectangular EditShape after a mouse movement 
02302                 during a drag on one if its corners.  The rectangle is resized about the
02303                 opposite corner to the drag corner.
02304     SeeAlso:    OpDragRegularShape::ProcessRectangleEdge
02305 ********************************************************************************************/
02306 BOOL OpDragRegularShape::ProcessRectangleDiagonal( DocCoord PointerPos, ClickModifiers Mods )
02307 {
02308     // Constrain the mouse so the aspect of the shape is maintained
02309     if (Mods.Constrain)
02310     {
02311         // Get the scaling factor
02312         const double RectDiagonal = RectangleArray[0].Distance(RectangleArray[2]);
02313         const double Ax = (RectangleArray[2].x-RectangleArray[0].x);
02314         const double Ay = (RectangleArray[2].y-RectangleArray[0].y);
02315         const double Bx = (PointerPos.x-RectangleArray[0].x);
02316         const double By = (PointerPos.y-RectangleArray[0].y);
02317         const double NewLength = ((Ax*Bx) + (Ay*By)) / sqrt((Ax*Ax) + (Ay*Ay));
02318         double ratio = NewLength/RectDiagonal;
02319 
02320         // reposition the pointer
02321         PointerPos = DocCoord::PositionPointFromRatio(RectangleArray[0], RectangleArray[2], ratio);
02322     }
02323 
02324     // Snap the mouse point to the active grid
02325     DocView::SnapCurrent(StartSpread,&PointerPos);
02326 
02327     // Get the shape matrix and its inverse
02328     Matrix ShapeMat;
02329     OriginalShape->GetTransformMatrix(&ShapeMat);
02330     Matrix InvShapeMat = ShapeMat;
02331     InvShapeMat = InvShapeMat.Inverse();
02332 
02333     // Copy the vertext array for changing
02334     DocCoord VertexArray[4];
02335     for (INT32 loop = 0; loop < 4; loop++)
02336         VertexArray[loop] = RectangleArray[loop];
02337 
02338     // Get the matrix to take the original shape down to the unit square
02339     double OrigA = RectangleArray[3].x - RectangleArray[0].x;
02340     double OrigB = RectangleArray[3].y - RectangleArray[0].y;
02341     double OrigC = RectangleArray[1].x - RectangleArray[0].x;
02342     double OrigD = RectangleArray[1].y - RectangleArray[0].y;
02343     double OrigE = RectangleArray[0].x;
02344     double OrigF = RectangleArray[0].y;
02345 
02346     // Invert this
02347     double Det = OrigA*OrigD - OrigB*OrigC;
02348     ERROR2IF(Det==0.0, FALSE, "Matrix Inversion Failed");
02349     double InvertA = OrigD / Det;
02350     double InvertB = -OrigB / Det;
02351     double InvertC = -OrigC / Det;
02352     double InvertD = OrigA / Det;
02353     double InvertE = ((OrigC*OrigF) - (OrigE*OrigD))/Det;
02354     double InvertF = -(((OrigA*OrigF) - (OrigE*OrigB))/Det);
02355 
02356     // Now transform the vertex points
02357     // Calculate the offsets from the old point and the fixed point
02358     double dx0 = PointerPos.x - RectangleArray[2].x;
02359     double dy0 = PointerPos.y - RectangleArray[2].y;
02360 
02361     double dx3 = RectangleArray[1].x - RectangleArray[0].x;
02362     double dy3 = RectangleArray[1].y - RectangleArray[0].y;
02363 
02364     double dx4 = RectangleArray[3].x - RectangleArray[0].x;
02365     double dy4 = RectangleArray[3].y - RectangleArray[0].y;
02366     
02367     // Calculate the bits that we need to know
02368     double dx1 = dx3 * ((dy0*dx4-dy4*dx0) / (dx4*dy3-dy4*dx3));
02369     double dy1 = dy3 * ((dy0*dx4-dy4*dx0) / (dx4*dy3-dy4*dx3));
02370     double dx2 = dx4 * ((dx3*dy0-dy3*dx0) / (dy4*dx3-dx4*dy3));
02371     double dy2 = dy4 * ((dx3*dy0-dy3*dx0) / (dy4*dx3-dx4*dy3));
02372 
02373     // Change the parallelogram
02374     VertexArray[1].x = RectangleArray[1].x + (INT32)dx1;
02375     VertexArray[1].y = RectangleArray[1].y + (INT32)dy1;
02376     VertexArray[3].x = RectangleArray[3].x + (INT32)dx2;
02377     VertexArray[3].y = RectangleArray[3].y + (INT32)dy2;
02378     VertexArray[2] = PointerPos;
02379 
02380     // Get the array from unit to the new position
02381     double NewA = VertexArray[3].x - VertexArray[0].x;
02382     double NewB = VertexArray[3].y - VertexArray[0].y;
02383     double NewC = VertexArray[1].x - VertexArray[0].x;
02384     double NewD = VertexArray[1].y - VertexArray[0].y;
02385     double NewE = VertexArray[0].x;
02386     double NewF = VertexArray[0].y;
02387 
02388     // multiply the two matrices
02389     double FinalA =  (InvertA * NewA) + (InvertB * NewC);
02390     double FinalB =  (InvertA * NewB) + (InvertB * NewD);
02391     double FinalC =  (InvertC * NewA) + (InvertD * NewC);
02392     double FinalD =  (InvertC * NewB) + (InvertD * NewD);
02393     INT32 FinalE =  (INT32)(NewA*InvertE + NewC*InvertF + NewE);
02394     INT32 FinalF =  (INT32)(NewB*InvertE + NewD*InvertF + NewF);
02395 
02396     // apply to the rectangle
02397     Trans2DMatrix Transform(Matrix(FinalA, FinalB, FinalC, FinalD, FinalE, FinalF));
02398 
02399     delete EditShape;
02400     EditShape = new (NodeRegularShape);
02401     if (NULL == EditShape)
02402         return FALSE;
02403 
02404     OriginalShape->CopyNodeContents(EditShape);
02405     EditShape->TransformCentreAndAxes(Transform);
02406 
02407     return TRUE;
02408 }
02409 
02410 
02411 
02412 /********************************************************************************************
02413 >   BOOL OpDragRegularShape::ProcessRectangleEdge( DocCoord PointerPos, DocCoord OtherEnd )
02414 
02415     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02416     Created:    12/12/95
02417     Inputs:     PointerPos - The current position of the mouse in Doc Coords
02418                 OtherEnd - the other corner of the rectangle from the corner being dragged,
02419                 the drag is constrained along this line
02420     Purpose:    This is called during a rectangle drag to resize the rectangle about either
02421                 edge
02422     SeeAlso:    OpDragRegularShape::ProcessRectangleDiagonally
02423 ********************************************************************************************/
02424 BOOL OpDragRegularShape::ProcessRectangleEdge( DocCoord PointerPos, DocCoord OtherEnd )
02425 {
02426     // Work in untransformed space to remove skew
02427     const DocCoord DraggingEnd = GetUTPosition(RectangleArray[2]); 
02428     OtherEnd = GetUTPosition(OtherEnd);
02429     PointerPos = GetUTPosition(PointerPos);
02430 
02431     Matrix TransMat;
02432     OriginalShape->GetTransformMatrix(&TransMat);
02433     TransMat = TransMat.Inverse();
02434 
02435     // Translate rectangle so other (stationary) corner is on origin
02436     TransMat *= Matrix(-OtherEnd.x, -OtherEnd.y);
02437 
02438     // Rotate rectangle so drag edge lies along x-axis
02439     double RotAngle = atan2((double)DraggingEnd.y-OtherEnd.y, (double)DraggingEnd.x-OtherEnd.x);
02440     TransMat *= Matrix(ANGLE(-RotAngle*(180/PI)));
02441 
02442     // Scale along x-axis
02443     double OrigDragEdgeLength = OtherEnd.Distance(DraggingEnd);
02444     double c = OtherEnd.Distance(PointerPos);
02445     double a = DocCoord::DistanceFromLine(OtherEnd, DraggingEnd, PointerPos);
02446     double NewDragEdgeLength = sqrt((c*c) - (a*a));
02447     double ScaleFactor = (OrigDragEdgeLength == 0.0) ? 1.0 : NewDragEdgeLength/OrigDragEdgeLength;
02448     if (!IsJoinAcute(&OtherEnd, &DraggingEnd, &PointerPos))
02449         ScaleFactor = -ScaleFactor;
02450     TransMat *= Matrix (FIXED16(ScaleFactor), FIXED16(1.0));
02451 
02452     // Rotate back
02453     TransMat *= Matrix(ANGLE(RotAngle*(180/PI)));
02454 
02455     // Translate back
02456     TransMat *= Matrix(OtherEnd.x, OtherEnd.y);
02457 
02458     // And finally apply the original transform back
02459     Matrix OrigMatrix;
02460     OriginalShape->GetTransformMatrix(&OrigMatrix);
02461     TransMat *= OrigMatrix;
02462     
02463     // Apply this to the edit shape
02464     delete EditShape;
02465     EditShape = new (NodeRegularShape);
02466     if (NULL == EditShape)
02467         return FALSE;
02468   
02469     OriginalShape->CopyNodeContents(EditShape);
02470     Trans2DMatrix   Trans2dMatrix(TransMat);
02471     EditShape->TransformCentreAndAxes( Trans2dMatrix );
02472   
02473     return TRUE;
02474 }
02475 
02476 
02477 
02478 /********************************************************************************************
02479 
02480 >   BOOL OpDragRegularShape::ProcessCentre( DocCoord PointerPos, ClickModifiers ClickMods )
02481 
02482     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02483     Created:    15/12/94
02484     Inputs:     PointerPos - The current position of the mouse in Doc Coords
02485                 ClickMods - Which key modifiers are being pressed
02486     Purpose:    This is called to update the EditShape after a mouse movement in a centre point
02487                 drag.  The shape is being translated.  Constrain works from angles from the
02488                 original centre point of the shape.
02489     SeeAlso:    OpDragRegularShape::DragPointerMove
02490 
02491 ********************************************************************************************/
02492 
02493 BOOL OpDragRegularShape::ProcessCentre( DocCoord PointerPos, ClickModifiers ClickMods )
02494 {
02495     // Constrain and snap the mouse point if required
02496     DocCoord OldCentre = OriginalShape->GetCentrePoint();
02497     if (ClickMods.Constrain)
02498         DocView::ConstrainToAngle(OldCentre, &PointerPos);
02499     DocView::SnapCurrent(StartSpread,&PointerPos);
02500 
02501     // Translate shape based on the movement.
02502     DocCoord Offset = PointerPos - EditShape->GetCentrePoint();                                                     
02503     Trans2DMatrix Trans(Offset.x, Offset.y);
02504     EditShape->Transform(Trans);
02505 
02506     return TRUE;
02507 }
02508 
02509 
02510 
02511 /********************************************************************************************
02512 
02513 >   BOOL OpDragRegularShape::ProcessRadius( DocCoord PointerPos, ClickModifiers ClickMods)
02514 
02515     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02516     Created:    23/2/95
02517     Inputs:     PointerPos - The current position of the mouse in Doc Coords
02518                 ClickMods - Which key modifiers are being pressed
02519     Purpose:    This is called to process drags on the primary points and on the major axes.
02520     SeeAlso:    -
02521 
02522 ********************************************************************************************/
02523 
02524 BOOL OpDragRegularShape::ProcessRadius(DocCoord Pos, ClickModifiers Mods)
02525 {
02526     // Snap the mouse point to the active grid
02527     DocView::SnapCurrent(StartSpread,&Pos);
02528 
02529     // Untransform the current pointer pos
02530     DocCoord UTPointerPos = GetUTPosition(Pos);
02531     const DocCoord UTCentre = OriginalShape->GetUTCentrePoint();
02532     const DocCoord Centre = OriginalShape->GetCentrePoint();
02533 
02534     // Get rotation of original and current primary points
02535     DocCoord CurrentPrimary = StartMousePos - Centre;
02536     DocCoord NewPrimary = Pos - Centre;
02537     double OrigPrimaryRotation = atan2((double)CurrentPrimary.y, (double)CurrentPrimary.x);
02538     double NewPrimaryRotation = atan2((double)NewPrimary.y, (double)NewPrimary.x);
02539     ANGLE RotBy(-(OrigPrimaryRotation - NewPrimaryRotation)*(180/PI));
02540 
02541     // Get the scaling factor
02542     double NewLength = Pos.Distance(Centre);
02543     double CurrentLength = StartMousePos.Distance(Centre);
02544     double ScaleFactor = (CurrentLength != 0.0) ? (NewLength/CurrentLength) : 0.001;
02545 
02546     // Transform to origin, scale, rotate then transform back
02547     Matrix Transform(-Centre.x, -Centre.y);
02548     Transform*= Matrix(RotBy);
02549     Transform*= Matrix(fixed16(ScaleFactor),fixed16(ScaleFactor));
02550     Transform*= Matrix(Centre.x, Centre.y);
02551     Trans2DMatrix   Trans(Transform);
02552 
02553     // Copy the original shape then apply the transform
02554     delete EditShape;
02555     EditShape = (NodeRegularShape*)OriginalShape->SimpleCopy();
02556     if (EditShape != NULL)
02557     {
02558         EditShape->Transform(Trans);
02559         transMat = Transform;
02560         return TRUE;
02561     }
02562     else
02563         return FALSE;
02564 }
02565 
02566 
02567 
02568 /********************************************************************************************
02569 
02570 >   void OpDragRegularShape::WheelConstrain(DocCoord* PointerPos, const DocCoord StartPos)
02571 
02572     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02573     Created:    10/1/94
02574     Inputs:     PointerPos - The current position of the mouse in Doc Coords
02575                 StartPos - The outer constrain location.
02576     Outputs:    PointerPos is constrained to the shape described below
02577     Returns:    -
02578     Purpose:    This is called to constrain a point to a steering-wheel shape.  The wheel is
02579                 centred on the centre of OriginalShape.  It has radius of the distance from
02580                 the centre to StartPos.  The other constrain line is the line passing through
02581                 the centre point and StartPos.
02582     SeeAlso:    -
02583 
02584 ********************************************************************************************/
02585 
02586 void OpDragRegularShape::WheelConstrain(DocCoord* PointerPos, const DocCoord StartPos)
02587 {
02588     // Obtain perpendicular distance from StartPos
02589     DocCoord LineConstrainPoint = StartPos;
02590     DocCoord RotStartPos = StartPos;
02591     Matrix Mat(-OriginalShape->GetCentrePoint().x, -OriginalShape->GetCentrePoint().y);
02592     Mat *= Matrix(ANGLE(90));
02593     Mat *= Matrix(OriginalShape->GetCentrePoint().x, OriginalShape->GetCentrePoint().y);
02594     Mat.transform(&RotStartPos);
02595     double LineDistance1 = DocCoord::DistanceFromLine(OriginalShape->GetCentrePoint(), StartPos, *PointerPos);
02596     double LineDistance2 = DocCoord::DistanceFromLine(OriginalShape->GetCentrePoint(), RotStartPos, *PointerPos);
02597     BOOL UseRotated = FALSE;
02598     double LineDistance = LineDistance1;
02599     if (LineDistance2 < LineDistance)
02600     {
02601         UseRotated = TRUE;
02602         LineDistance = LineDistance2;
02603     }
02604 
02605     // Now obtain distance the pointer position is from the circumference of the circle
02606     double Radius = OriginalShape->GetCentrePoint().Distance(StartPos);
02607     const double CurrentRadius = OriginalShape->GetCentrePoint().Distance(*PointerPos);
02608     const double CircleDistance = fabs(Radius - CurrentRadius);
02609 
02610     Radius = OriginalShape->GetCentrePoint().Distance(StartPos);
02611 
02612 /*  // Now if the pointer is past the centre point on the other side of the shape from StartPos
02613     // then we want the distance from the centre point
02614     if (!OriginalShape->IsCircular())
02615     {
02616         DocCoord StartOrig = StartPos - OriginalShape->GetCentrePoint();
02617         DocCoord PointerOrig = *PointerPos - OriginalShape->GetCentrePoint();
02618         double StartAngle = atan2((double)StartOrig.y, (double)StartOrig.x);
02619         double PointerAngle = atan2((double)PointerOrig.y, (double)PointerOrig.x);
02620         if ((PointerAngle < StartAngle-(PI/2)) || (PointerAngle > StartAngle+(PI/2)) )
02621             LineDistance = CircleDistance*2;
02622     }*/
02623 
02624     // Now constrain to either the line or the circumference
02625     if ((CircleDistance < LineDistance) && (CurrentRadius >= 0.0001))
02626     {
02627         // Constrain to the circle circumference
02628         *PointerPos = DocCoord::PositionPointFromRatio(OriginalShape->GetCentrePoint(), *PointerPos, Radius/CurrentRadius);
02629     }
02630     else
02631     {
02632         // Constrain to the line
02633         if (UseRotated)
02634             *PointerPos = DocCoord::PositionPointFromRatio(OriginalShape->GetCentrePoint(), RotStartPos, CurrentRadius/Radius);
02635         else
02636             *PointerPos = DocCoord::PositionPointFromRatio(OriginalShape->GetCentrePoint(), StartPos, CurrentRadius/Radius);
02637     }
02638 }
02639 
02640 
02641 
02642 
02643 
02644 
02645 /********************************************************************************************
02646 
02647 >   void OpDragRegularShape::GetUTPosition(DocCoord TransedPos)
02648 
02649     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02650     Created:    23/2/95
02651     Inputs:     TransedPos - A position in the OriginalShape's translated space
02652     Outputs:    -
02653     Returns:    The position untranslated back to the OriginalShape
02654     Purpose:    This is called to convert a current mouse postion to untransform back to the
02655                 coordinates used by the untranslated OriginalShape.
02656     SeeAlso:    -
02657 
02658 ********************************************************************************************/
02659 
02660 DocCoord OpDragRegularShape::GetUTPosition(DocCoord TransedPos)
02661 {
02662     Matrix TransMat;
02663     OriginalShape->GetTransformMatrix(&TransMat);
02664     TransMat = TransMat.Inverse();
02665     TransMat.transform(&TransedPos);
02666 
02667     return TransedPos;
02668 }
02669 
02670 
02671 
02672 /********************************************************************************************
02673 
02674 >   void OpDragRegularShape::RenderDragBlobs( DocRect Rect, SPread* pSpread, BOOL bSolidDrag )
02675 
02676     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02677     Created:    14/12/94
02678     Purpose:    Renders the current version of the shape to the window.
02679 
02680 ********************************************************************************************/
02681 
02682 void OpDragRegularShape::RenderDragBlobs( DocRect Rect, Spread *pSpread, BOOL bSolidDrag )
02683 {
02684     RenderRegion* pRegion = DocView::RenderOnTop(NULL, pSpread, ClippedEOR );
02685     while ( pRegion )
02686     {
02687         // Set the line colour 
02688         pRegion -> SetFillColour(COLOUR_NONE);
02689         pRegion -> SetLineColour(COLOUR_XOREDIT);
02690 
02691         // And draw an EORed version of how the shape will turn out
02692         EditShape->RenderEorDrag( pRegion );
02693 
02694         // Get the Next render region
02695         pRegion = DocView::GetNextOnTop(NULL);
02696     }
02697 }
02698 
02699 
02700 
02701 /********************************************************************************************
02702 >   BOOL OpDragRegularShape::IsJoinAcute(const DocCoord* pJoin, const DocCoord* pOtherEndOfJoin, const DocCoord* pOtherPoint);
02703 
02704     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02705     Created:    13/12/95
02706     Inputs:     pJoin - the coordinate where two lines join
02707                 pOtherEndOfJoin - another point that with pJoin defines a line
02708                 pOtherPoint - another point that with pJoin defines another line
02709     Outputs:    -
02710     Returns:    TRUE if the angle between the two lines is an acute angle
02711     Purpose:    For seeing the perpendicual distance from pOtherPoint to the line
02712                 pJoin-pOtherEndOfJoin end on the line or before pJoin.  Used when dragging
02713                 along a line to see if the drag point has gon back beyond the start of the
02714                 line.
02715 ********************************************************************************************/
02716 BOOL OpDragRegularShape::IsJoinAcute(const DocCoord* pJoin, const DocCoord* pOtherEndOfJoin, const DocCoord* pOtherPoint)
02717 {
02718     return DocCoord::IsJoinAcute(pJoin, pOtherEndOfJoin, pOtherPoint);
02719 }
02720 
02721 
02722 
02723 /********************************************************************************************
02724 
02725 >   BOOL OpDragRegularShape::Init()
02726 
02727     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02728     Created:    14/12/94
02729     Returns:    TRUE if all went OK, False otherwise
02730     Purpose:    Adds the operation to the list of all known operations
02731 
02732 ********************************************************************************************/
02733 
02734 BOOL OpDragRegularShape::Init()
02735 {
02736     return (RegisterOpDescriptor(   0, 
02737                                     _R(IDS_EDITREGULARSHAPEOP),
02738                                     CC_RUNTIME_CLASS(OpDragRegularShape), 
02739                                     OPTOKEN_DRAGREGULARSHAPE,
02740                                     OpDragRegularShape::GetState));
02741 }
02742 
02743 
02744 /********************************************************************************************
02745 
02746 >   OpState OpDragRegularShape::GetState(String_256* Description, OpDescriptor*)
02747 
02748     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02749     Created:    14/12/94
02750     Outputs:    -
02751     Returns:    The state of the operation, so that menu items (ticks and greying can be
02752                 done properly
02753     Purpose:    Find out the state of the operation at the specific time
02754 
02755 ********************************************************************************************/
02756 
02757 OpState OpDragRegularShape::GetState(String_256* Description, OpDescriptor*)
02758 {
02759     OpState Blobby;
02760     
02761     return Blobby;
02762 }
02763 
02764 
02765 
02766 /********************************************************************************************
02767 
02768 >   OpReformShapeEdge::OpReformShapeEdge()
02769 
02770     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02771     Created:    19/12/94
02772     Inputs:     -
02773     Outputs:    -
02774     Returns:    -
02775     Purpose:    Constructor - set member variables to default values.
02776 
02777 ********************************************************************************************/
02778 
02779 OpReformShapeEdge::OpReformShapeEdge()
02780 {
02781     StartSpread = NULL;
02782     EditShape = NULL;
02783     EditEdge = NULL;
02784 }
02785 
02786 
02787 
02788 /********************************************************************************************
02789 
02790 >   void OpReformShapeEdge::DoStartDragEdit(NodeRegularShape* OrigShape, DocCoord Anchor,
02791                                                 Spread *pSpread, INT32 SegmentIndex, double pdist)
02792 
02793     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com> - from Jim code in OpReshapeOrAddPoint
02794     Created:    19/12/95
02795     Inputs:     OrigShape - The original shape object
02796                 Anchor - The position of the mouse at the start of the Drag
02797                 pSpread - the spread that the drag started on
02798                 SegmentIndex - The Coord number of the control point that we are dragging
02799                 pdist - parametric distance along the curve we're dragging from (0 to 1)
02800     Purpose:    This is called when a Drag operation has been started on the edge of a shape.
02801                 It will try and reshape the edge as it's dragged according to the maths in the 
02802                 function RecalculatePath
02803     SeeAlso:    OpReformShapeEdge::RecalculatePath
02804                 
02805 
02806 ********************************************************************************************/
02807 
02808 void OpReformShapeEdge::DoStartDragEdit(NodeRegularShape* OrigShape, BOOL EdgeOne, DocCoord Anchor,
02809                                                 Spread *pSpread, INT32 SegmentIndex)
02810 {
02811     // We had better take a note of the starting point of the drag
02812     StartMousePos = Anchor;
02813     UTStartMousePos = Anchor;           // We'll untransform this later in BuildEditShape
02814     StartSpread  = pSpread;
02815     OriginalShape = OrigShape;
02816     Index = SegmentIndex;
02817     EdgeDrag = EdgeOne ? DRAG_EDGE1 : DRAG_EDGE2;
02818 
02819     // We need to make a version of the shape that we can change
02820     if (!BuildEditEdge())
02821     {
02822         // Inform the person doing the clicking that life is not looking so good
02823         InformError( _R(IDS_OUT_OF_MEMORY), _R(IDS_OK) );
02824         End();
02825         return;
02826     }
02827 
02828     // Render the bits of the path that are different
02829     DocRect EditPathBBox = EditShape->GetBoundingRect();
02830     RenderDragBlobs(EditPathBBox, pSpread, FALSE);
02831 
02832     // And tell the Dragging system that we need drags to happen
02833     StartDrag(DRAGTYPE_AUTOSCROLL, &EditPathBBox, &StartMousePos);
02834 }
02835 
02836 
02837 
02838 /********************************************************************************************
02839 
02840 >   void OpReformShapeEdge::DragPointerMove( DocCoord PointerPos, 
02841                                                 ClickModifiers ClickMods, Spread *pSpread, BOOL bSolidDrag)
02842 
02843     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02844     Created:    19/12/94
02845     Inputs:     PointerPos - The current position of the mouse in Doc Coords
02846                 ClickMods - Which key modifiers are being pressed
02847                 pSpread - the spread that the cursor is over now
02848     Purpose:    This is called every time the mouse moves, during a drag.
02849     SeeAlso:    ClickModifiers
02850 
02851 ********************************************************************************************/
02852 
02853 void OpReformShapeEdge::DragPointerMove( DocCoord PointerPos, ClickModifiers ClickMods, Spread *pSpread, BOOL bSolidDrag)
02854 {
02855     // If drag has moved onto a different spread, convert the coord to be relative to the
02856     // original spread.
02857     if (pSpread != StartSpread)
02858         PointerPos = MakeRelativeToSpread(StartSpread, pSpread, PointerPos);
02859 
02860     // Now snap it to the current grid
02861     DocView::SnapCurrent(StartSpread,&PointerPos);
02862 
02863     // Rub out the old EORed version of the path
02864     RenderDragBlobs( EditShape->GetBoundingRect(), StartSpread, bSolidDrag );
02865 
02866     // This is the bit where we go off and re-calculate the edge path,
02867     // based on how much the mouse has moved.  We need to work in untransfomed coordinates
02868     DocCoord Offset = PointerPos;
02869     Matrix TransMat;
02870     EditShape->GetTransformMatrix(&TransMat);
02871     TransMat = TransMat.Inverse();
02872     TransMat.transform(&Offset);
02873     Offset = Offset-UTStartMousePos;
02874 
02875     RecalculateEdge( Offset );
02876     if (!BuildEditShape(ClickMods.Constrain || !OriginalShape->IsStellated(), ClickMods.Adjust))
02877     {
02878         InformError();
02879         End();
02880     }
02881 
02882     // Draw in the new version of the path and update the Last Mouse Position
02883     RenderDragBlobs( EditShape->GetBoundingRect(), StartSpread, bSolidDrag );
02884 }
02885 
02886 
02887 /********************************************************************************************
02888 
02889 >   void OpReformShapeEdge::DragFinished( DocCoord PointerPos, ClickModifiers ClickMods, Spread* pSpread, BOOL Success, BOOL bSolidDrag)
02890 
02891     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02892     Created:    21/12/94
02893     Inputs:     PointerPos - The position of the mouse at the end of the drag
02894                 ClickMods - the key modifiers being pressed
02895                 pSpread - pointer to the spared the drag ended on.
02896                 Success - TRUE if the drag was terminated properly, FALSE if it
02897                 was ended with the escape key being pressed
02898     Purpose:    This is called when a drag operation finishes.
02899     SeeAlso:    ClickModifiers
02900 
02901 ********************************************************************************************/
02902 
02903 void OpReformShapeEdge::DragFinished( DocCoord PointerPos, ClickModifiers ClickMods, Spread* pSpread, BOOL Success, BOOL bSolidDrag)
02904 {
02905     RenderDragBlobs( EditEdge->GetBoundingRect(), StartSpread, bSolidDrag );
02906     EndDrag();
02907     BeginSlowJob();
02908 
02909     BOOL Failed = FALSE;
02910 
02911     if ( Success )
02912     {
02913         // Start the undo ball rolling
02914         Failed = !DoStartSelOp(FALSE, TRUE, FALSE, TRUE);
02915 
02916         if (!Failed)
02917         {
02918             Failed = RecalcBoundsAction::DoRecalc(this, &UndoActions, OriginalShape) == AC_FAIL;
02919         }
02920 
02921         // Will the original shape allow the op to happen?
02922         ObjChangeFlags cFlags;
02923         ObjChangeParam ObjChange(OBJCHANGE_STARTING,cFlags,OriginalShape,this);
02924         if (!Failed)
02925             Failed = !OriginalShape->AllowOp(&ObjChange);
02926 
02927         if (!Failed)
02928         {
02929             if ((EdgeDrag == DRAG_EDGE1) || ClickMods.Constrain)
02930             {
02931                 Failed = ChangeShapePathAction::DoReshape(this, &UndoActions, OriginalShape, 
02932                     ChangeShapePathAction::CHANGE_PATH1, &(OriginalShape->EdgePath1)) == AC_FAIL ;
02933             }
02934             if ((EdgeDrag == DRAG_EDGE2) || ClickMods.Constrain || !OriginalShape->IsStellated())
02935             {
02936                 if (!Failed)
02937                 {
02938                     Failed = ChangeShapePathAction::DoReshape(this, &UndoActions, OriginalShape, 
02939                         ChangeShapePathAction::CHANGE_PATH2, &(OriginalShape->EdgePath2)) == AC_FAIL ;
02940                 }
02941             }
02942         }
02943                                         
02944         // Now do the change
02945         if (!Failed)
02946         {
02947             if ((EdgeDrag == DRAG_EDGE1) || ClickMods.Constrain )
02948             {
02949                 OriginalShape->EdgePath1.ClearPath();
02950                 OriginalShape->EdgePath1.CopyPathDataFrom(&(EditShape->EdgePath1));     
02951             }
02952             if ((EdgeDrag == DRAG_EDGE2) || ClickMods.Constrain || !OriginalShape->IsStellated() )
02953             {
02954                 OriginalShape->EdgePath2.ClearPath();
02955                 OriginalShape->EdgePath2.CopyPathDataFrom(&(EditShape->EdgePath2));     
02956             }
02957 
02958             // Recalculate the path's bounding box
02959             OriginalShape->InvalidateBoundingRect();
02960             OriginalShape->InvalidateCache();
02961     
02962             // tell the world that something in the selection has changed 
02963             // so that selection bounds are updated
02964             GetApplication()->FindSelection()->Update(TRUE);
02965 
02966             Failed = RecordBoundsAction::DoRecord(this, &UndoActions, OriginalShape) == AC_FAIL ;
02967         }
02968 
02969         if (!Failed)
02970         {
02971             // Update effected parents
02972             ObjChange.Define(OBJCHANGE_FINISHED,cFlags,OriginalShape,this);
02973             Failed = !UpdateChangedNodes(&ObjChange);
02974         }
02975     }
02976     else
02977     {
02978         // Set up the flags that say it all went wrong
02979         Failed = TRUE;
02980     }
02981 
02982     delete (EditShape);
02983     delete (EditEdge);
02984 
02985     if (Failed)
02986         FailAndExecute();
02987     End();      
02988 }
02989 
02990 
02991 
02992 /********************************************************************************************
02993 
02994 >   BOOL OpReformShapeEdge::BuildEditEdge()
02995 
02996     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02997     Created:    19/12/94
02998     Returns:    TRUE if it managed to build the path, FALSE if it failed
02999     Purpose:    Builds a copy of the shape and the edge path that we can edit, without
03000                 destroying the original data
03001     Errors:     If it runs out of memory then it will return FALSE
03002 
03003 ********************************************************************************************/
03004 
03005 BOOL OpReformShapeEdge::BuildEditEdge()
03006 {
03007     // Copy the shape
03008     if ( (EditShape = new (NodeRegularShape)) == NULL)
03009         return FALSE;
03010     OriginalShape->CopyNodeContents(EditShape);
03011 
03012     // Copy the edge path
03013     if ( (EditEdge = new (Path)) == NULL)
03014         return FALSE;
03015 
03016     // We need to make a copy of the edge path as it appears in the shape.
03017     INT32 Count1 = OriginalShape->EdgePath1.GetNumCoords();
03018     INT32 Count2 = OriginalShape->EdgePath2.GetNumCoords();
03019     if (Count1 == 2)
03020         Count1 = 4;
03021     if (Count2 == 2)
03022         Count2 = 4;
03023     Path* RenderPath;
03024 
03025     if (!OriginalShape->BuildShapePath(&RenderPath))
03026         return FALSE;
03027 
03028     // Run through the path makeing linetos into straight curvetos
03029     BOOL FoundLine = TRUE;
03030     while (FoundLine)
03031     {
03032         INT32 NumCoords = RenderPath->GetNumCoords();
03033         
03034         // find a lineto
03035         INT32 loop = 0;
03036         while ((loop < NumCoords) && ((RenderPath->GetVerbArray()[loop] & ~PT_CLOSEFIGURE) != PT_LINETO))
03037         {
03038             loop ++;
03039         }
03040 
03041         if (loop < NumCoords)
03042         {
03043             DocCoord* EdgeCoords = RenderPath->GetCoordArray();
03044             DocCoord CP1 (EdgeCoords[loop-1].x + (EdgeCoords[loop].x-EdgeCoords[loop-1].x)/3,
03045                                         EdgeCoords[loop-1].y + (EdgeCoords[loop].y-EdgeCoords[loop-1].y)/3);
03046             DocCoord CP2 (EdgeCoords[loop].x + (EdgeCoords[loop-1].x-EdgeCoords[loop].x)/3,
03047                                         EdgeCoords[loop].y + (EdgeCoords[loop-1].y-EdgeCoords[loop].y)/3);
03048             DocCoord End = EdgeCoords[loop];
03049             RenderPath->SetPathPosition(loop);
03050             RenderPath->InsertCurveTo(CP1,CP2,End);
03051             RenderPath->DeleteSection(loop+3,1);
03052         }
03053         else
03054             FoundLine = FALSE;
03055     }
03056 
03057     // Now calculate paramdist
03058     RenderPath->SqrDistanceToPoint(StartMousePos, &Index, &paramdist);
03059 
03060     // Now set Index to point to the first element in the edge
03061     PathFlags*  RenderFlags = RenderPath->GetFlagArray();
03062     while ((!RenderFlags[Index].IsEndPoint) && (Index >= 0) )
03063     {
03064         Index--;
03065     }
03066 
03067     INT32 EndCount = EdgeDrag==DRAG_EDGE1 ? Count1 : Count2;
03068 
03069     // Ask for a path with the required number of free slots in it
03070     if (!EditEdge->Initialise(EndCount, 1))
03071         return FALSE;
03072 
03073     // now copy the data from the appropitae place in RenderPath into EdgeEdge
03074     if (!EditEdge->CopySectionFrom(*RenderPath, Index, EndCount))
03075         return FALSE;
03076 
03077     // Set the first verb to be a MoveTo and clear any closefigure from the last verb
03078     PathVerb*   EdgeVerbs = EditEdge->GetVerbArray();
03079     DocCoord*   EdgeCoords = EditEdge->GetCoordArray();
03080     EdgeVerbs[0] = PT_MOVETO;
03081     EdgeVerbs[EditEdge->GetNumCoords()-1] = EdgeVerbs[EditEdge->GetNumCoords()-1] & ~PT_CLOSEFIGURE;
03082 
03083     // Untransform the path 
03084     Matrix TransMat;
03085     EditShape->GetTransformMatrix(&TransMat);
03086     TransMat = TransMat.Inverse();
03087     TransMat.transform((Coord*)EdgeCoords, EditEdge->GetNumCoords());
03088 
03089     // We also need an untransformed starting point of the drag
03090     TransMat.transform(&UTStartMousePos);
03091 
03092     // Record the original positions of the two control points of the drag segment
03093     // NOTE Assumes that the edit path consists of one segment
03094     if (EdgeVerbs[1] == PT_BEZIERTO)
03095     {
03096         OrigControl1 = EdgeCoords[1];
03097         OrigControl2 = EdgeCoords[2];
03098     }
03099     Index = 1;
03100  
03101     return TRUE;
03102 }
03103 
03104 
03105 
03106 /********************************************************************************************
03107 
03108 >   BOOL OpReformShapeEdge::BuildEditShape(BOOL CopyToBoth, BOOL ShouldMirror)
03109 
03110     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
03111     Created:    20/12/94
03112     Inputs:     CopyToBoth - TRUE if the status of EdgeDrag is to be ignored and the new edge
03113                 should be applied to both edges of the shape.
03114     Outputs:    -
03115     Returns:    TRUE if the build succeded, FALSE if it failed
03116     Purpose:    Builds a copy of the shape into EditShape based upon the current edge path
03117     Errors:     -
03118 
03119 ********************************************************************************************/
03120 
03121 BOOL OpReformShapeEdge::BuildEditShape(BOOL CopyToBoth, BOOL ShouldMirror)
03122 {
03123     ERROR2IF(EditShape == NULL, FALSE, "Can't build into a non-existant shape!");
03124 
03125     BOOL Success = TRUE;
03126     if (CopyToBoth)
03127     {
03128         Path* RevPath = new Path;
03129         Path* Path1;
03130         Path* Path2;
03131 
03132         if ((RevPath == NULL) || !ReversePath(RevPath, ShouldMirror))
03133         {
03134             if (RevPath != NULL)
03135                 delete RevPath;
03136             RevPath = EditEdge;
03137         }
03138 
03139         if (EdgeDrag == DRAG_EDGE1)
03140         {
03141             Path1 = EditEdge;
03142             Path2 = RevPath;
03143         }
03144         else
03145         {
03146             Path1 = RevPath;
03147             Path2 = EditEdge;
03148         }
03149 
03150         Success = EditShape->EdgePath1.CopyPathDataFrom(Path1);
03151         if (Success)
03152             Success = EditShape->EdgePath2.CopyPathDataFrom(Path2); 
03153 
03154         if (RevPath != EditEdge)
03155             delete RevPath;
03156     }
03157     else
03158     {
03159         if (EdgeDrag == DRAG_EDGE1)
03160         {
03161             Success = EditShape->EdgePath1.CopyPathDataFrom(EditEdge);
03162             if (Success)
03163                 Success = EditShape->EdgePath2.CopyPathDataFrom(&(OriginalShape->EdgePath2));       
03164         }
03165         else
03166         {
03167             Success = EditShape->EdgePath2.CopyPathDataFrom(EditEdge);
03168             if (Success)
03169                 Success = EditShape->EdgePath1.CopyPathDataFrom(&(OriginalShape->EdgePath1));       
03170         }
03171     }
03172 
03173     if (!Success)
03174     {
03175         Error::SetError(_R(IDS_OUT_OF_MEMORY));
03176     }
03177 
03178     EditShape->InvalidateCache();
03179 
03180     return Success;
03181 }
03182 
03183 
03184 
03185 /********************************************************************************************
03186 
03187 >   void OpReformShapeEdge::RecalculateEdge( DocCoord Offset )
03188 
03189     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com> (stolen from Jim code)
03190     Created:    20/12/94
03191     Inputs:     Offset - The Amount the pointer has moved from the *original* position (i.e. not
03192                         the last position).
03193     Purpose:    Will reshape the edge being edited using Jims curve distorting method
03194     SeeAlso:    OpReshapeOrAddPoint::RecalculatePath
03195 
03196 ********************************************************************************************/
03197 
03198 void OpReformShapeEdge::RecalculateEdge( DocCoord Offset )
03199 {
03200     const double factor = 0.656875;
03201     PathVerb* Verbs = EditEdge->GetVerbArray();
03202     DocCoord* Coords = EditEdge->GetCoordArray();
03203     if (Verbs[Index] == PT_BEZIERTO)        // first ctrl pt won't have closefigure set
03204     {
03205         DocCoord ControlOffset;
03206         ControlOffset.x = (INT32)(OrigControl1.x + (Offset.x) * factor / paramdist);
03207         ControlOffset.y = (INT32)(OrigControl1.y + (Offset.y) * factor / paramdist);
03208 
03209         ControlOffset.x -= Coords[Index].x;
03210         ControlOffset.y -= Coords[Index].y;
03211 
03212         Coords[Index].x += ControlOffset.x;
03213         Coords[Index].y += ControlOffset.y;
03214         
03215         ControlOffset.x = (INT32)(OrigControl2.x + (Offset.x) * factor / (1.0-paramdist));
03216         ControlOffset.y = (INT32)(OrigControl2.y + (Offset.y) * factor / (1.0-paramdist));
03217 
03218         ControlOffset.x -= Coords[Index+1].x;
03219         ControlOffset.y -= Coords[Index+1].y;
03220 
03221         Coords[Index+1].x += ControlOffset.x;
03222         Coords[Index+1].y += ControlOffset.y;
03223     }
03224 }
03225 
03226 
03227 
03228 /********************************************************************************************
03229 
03230 >   void OpReformShapeEdge::ReversePath(Path* NewPath, BOOL ShouldMirror)
03231 
03232     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
03233     Created:    13/1/95
03234     Inputs:     NewPath - Points to an uninitialsed path.
03235                 ShouldMirror - TRUE if the path should be mirrored
03236     Outputs:    NewPath contains a mirror image of EditEdge
03237     Returns:    TRUE for success, FALSE for failure
03238     Purpose:    Use to reverse an edit path before copying it to the other edge path
03239                 NOTE: This function assumes that the edge path consists of a MoveTo-BezierTo
03240     SeeAlso:    -
03241 
03242 ********************************************************************************************/
03243 
03244 BOOL OpReformShapeEdge::ReversePath(Path* NewPath, BOOL ShouldMirror)
03245 {
03246     const INT32 NumCoords = EditEdge->GetNumCoords();
03247     ERROR3IF(NumCoords != 4, "Edit edge was not a single Bezier section");
03248 
03249     if (!( NewPath->Initialise(NumCoords,4) && NewPath->CopyPathDataFrom(EditEdge) ))
03250         return FALSE;
03251 
03252     if (!ShouldMirror)
03253         NewPath->Reverse();
03254 
03255 /*  if (ShouldMirror)
03256     {
03257         DocCoord* Points = NewPath->GetCoordArray();
03258         // Transform point 1
03259         double angle1 = atan2((double)Points[3].y-Points[0].y, (double)Points[3].x-Points[0].x) * (180/PI);
03260         double angle2 = atan2((double)Points[1].y-Points[0].y, (double)Points[1].x-Points[0].x) * (180/PI);
03261 
03262         Matrix Trans(-Points[0].x, -Points[0].x);
03263         Trans *= Matrix(-ANGLE((angle2-angle1)));
03264         Trans *= Matrix(Points[0].x, Points[0].x);
03265 
03266         Trans.transform(&(Points[1]));
03267 
03268         // Transform point 2
03269         angle1 = atan2((double)Points[0].y-Points[3].y, (double)Points[0].x-Points[3].x) * (180/PI);
03270         angle2 = atan2((double)Points[2].y-Points[3].y, (double)Points[2].x-Points[3].x) * (180/PI);
03271 
03272         Trans = Matrix(-Points[3].x, -Points[3].x);
03273         Trans *= Matrix(-ANGLE((angle2-angle1)));
03274         Trans *= Matrix(Points[3].x, Points[3].x);
03275 
03276         Trans.transform(&(Points[2]));
03277     }*/
03278 
03279     return TRUE;
03280 }
03281 
03282 
03283 
03284 /********************************************************************************************
03285 
03286 >   BOOL OpReformShapeEdge::Init()
03287 
03288     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
03289     Created:    16/12/94
03290     Returns:    TRUE if all went OK, False otherwise
03291     Purpose:    Adds the operation to the list of all known operations
03292 
03293 ********************************************************************************************/
03294 
03295 BOOL OpReformShapeEdge::Init()
03296 {
03297     return (RegisterOpDescriptor(   0, 
03298                                     _R(IDS_EDITREGULARSHAPEOP),
03299                                     CC_RUNTIME_CLASS(OpReformShapeEdge), 
03300                                     OPTOKEN_RESHAPESHAPEEDGE,
03301                                     OpReformShapeEdge::GetState));
03302 }
03303 
03304 
03305 
03306 /********************************************************************************************
03307 
03308 >   OpState OpReformShapeEdge::GetState(String_256* Description, OpDescriptor*)
03309 
03310     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
03311     Created:    16/12/94
03312     Outputs:    Description - GetState fills this string with an approriate description
03313                 of the current state of the selector tool
03314     Returns:    The state of the operation, so that menu items (ticks and greying can be
03315                 done properly
03316     Purpose:    Find out the state of the operation at the specific time
03317 
03318 ********************************************************************************************/
03319 
03320 OpState OpReformShapeEdge::GetState(String_256* Description, OpDescriptor*)
03321 {
03322     OpState Blobby;
03323     
03324     return Blobby;
03325 }
03326 
03327 
03328 
03329 /********************************************************************************************
03330 
03331 >   virtual void OpReformShapeEdge::RenderDragBlobs( DocRect Rect, Spread* pSpread, BOOL bSolidDrag )
03332 
03333     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
03334     Created:    19/12/94
03335     Inputs:     Rect - the clipping rectangle
03336                 pSpread - the spread to draw onto
03337     Outputs:    -
03338     Returns:    -
03339     Purpose:    Renders the drag blobs of this operation.
03340 
03341 ********************************************************************************************/
03342 
03343 void OpReformShapeEdge::RenderDragBlobs( DocRect Rect, Spread* pSpread, BOOL bSolidDrag )
03344 {
03345     // If being called from DocView::RenderView, then the spread could be wrong - so
03346     // convert the rectangle if necessary.
03347     if (pSpread != StartSpread)
03348     {
03349         Rect.lo = MakeRelativeToSpread(StartSpread, pSpread, Rect.lo);
03350         Rect.hi = MakeRelativeToSpread(StartSpread, pSpread, Rect.hi);
03351     }
03352 
03353     ERROR3IF(EditShape == NULL, "Can't render a non-existant shape!");
03354     if (EditShape == NULL)
03355         return;
03356 
03357     // start a rendering loop
03358     RenderRegion* pRegion = DocView::RenderOnTop(NULL, pSpread, ClippedEOR);
03359     while (pRegion)
03360     {
03361         // Set the line colour 
03362         pRegion -> SetFillColour(COLOUR_NONE);
03363         pRegion -> SetLineColour(COLOUR_XOREDIT);
03364 
03365         // Draw the outline
03366         EditShape->RenderEorDrag(pRegion);
03367 
03368         // Get the Next render region
03369         pRegion = DocView::GetNextOnTop(NULL);
03370     }
03371 }
03372 
03373 
03374 
03375 
03377 // OpShapeToggleBase
03378 
03379 
03380 /********************************************************************************************
03381 
03382 >   OpShapeToggleBase::OpShapeToggleBase(): SelOperation()
03383 
03384     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
03385     Created:    17/04/95
03386     Purpose:    Constructs an OpShapeToggleBase object.
03387 
03388 ********************************************************************************************/
03389 
03390 OpShapeToggleBase::OpShapeToggleBase(): SelOperation()
03391 {
03392 }
03393 
03394 
03395 
03396 /********************************************************************************************
03397 
03398 >   void OpShapeToggleBase::Do(OpDescriptor*)
03399 
03400     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
03401     Created:    17/04/95
03402     Inputs:     An Operation Descriptor (unused)
03403     Outputs:    -
03404     Returns:    -
03405     Purpose:    Runs through all selected Quickshapes, toggling their one of their member states
03406     Errors:     -
03407     SeeAlso:    -
03408 
03409 ********************************************************************************************/
03410 
03411 void OpShapeToggleBase::Do(OpDescriptor*)
03412 {
03413     // Start the operation
03414     BOOL Success = DoStartSelOp(FALSE, FALSE, FALSE, FALSE);
03415 
03416     // Check with our parent nodes
03417     ObjChangeParam ObjChange(OBJCHANGE_STARTING,ObjChangeFlags(),NULL,this);
03418 
03419     // Run through the selected nodes
03420     if (Success)
03421     {
03422         SelRange* pSelection = GetApplication()->FindSelection();
03423         Node* pNode = pSelection->FindFirst();
03424 
03425         while ((pNode != NULL) && Success)
03426         {
03427             if (IS_A(pNode, NodeRegularShape))
03428             {
03429                 NodeRegularShape* pShape = (NodeRegularShape*)pNode;
03430             
03431                 // Convert this shape if it's parents allow
03432                 if (pShape->AllowOp(&ObjChange))                
03433                 {
03434                     // Create an action to will cause the shape to recalc its bounds
03435                     if (Success)
03436                         Success = (RecalcBoundsAction::DoRecalc(this, &UndoActions, pShape) != AC_FAIL);
03437                     
03438                     // Toggle its ellipseness
03439                     if (Success)
03440                         Success = DoAction(pShape);
03441 
03442                     // Invalidate the shapes bounding rect as it's changed
03443                     if (Success)
03444                     {
03445                         pShape->InvalidateBoundingRect();
03446                         pShape->InvalidateCache();
03447                     }
03448 
03449                     // And redraw its final state
03450                     if (Success)
03451                         Success = (RecordBoundsAction::DoRecord(this, &UndoActions, pShape) != AC_FAIL);
03452 
03453                     // Tell our parents we have finished
03454                     if (Success)
03455                     {
03456                         ObjChange.Define(OBJCHANGE_FINISHED,ObjChangeFlags(),NULL,this);
03457                         Success = UpdateChangedNodes(&ObjChange);
03458                     }
03459                 }
03460             }
03461 
03462             // Get the next selected thing
03463             pNode = pSelection->FindNext(pNode);
03464         }
03465     }
03466 
03467     // Clean up after any failure
03468     if (!Success)
03469     {
03470         InformError();
03471         FailAndExecute();
03472     }
03473 
03474     End();
03475 }
03476 
03477 
03478 
03479 BOOL OpShapeToggleBase::DoAction(NodeRegularShape* pShape)
03480 {
03481     ERROR3("Should not call OpShapeToggleBase::DoAction"); 
03482     return FALSE;
03483 }
03484 
03485 
03486 
03487 
03489 // OpShapeToggleElipPoly
03490 
03491 
03492 
03493 /********************************************************************************************
03494 
03495 >   OpShapeToggleElipPoly::OpShapeToggleElipPoly(): OpShapeToggleBase()
03496 
03497     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
03498     Created:    16/04/95
03499     Purpose:    Constructs an OpShapeToggleElipPoly object.
03500 
03501 ********************************************************************************************/
03502 
03503 OpShapeToggleElipPoly::OpShapeToggleElipPoly(): OpShapeToggleBase()
03504 {
03505 }
03506 
03507 
03508 
03509 /********************************************************************************************
03510 
03511 >   OpState OpShapeToggleElipPoly::GetState(String_256* UIDescription, OpDescriptor*)
03512 
03513     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
03514     Created:    16/04/95
03515     Inputs:     UIDescription - the name of the opertaion to display
03516                 OpDescriptor - To help update the state
03517     Outputs:    UIDescription - The name of the operation
03518     Returns:    The state of the operation
03519     Purpose:    Find the state of the OpShapeToggleElipPoly operation.
03520     Errors:     -
03521     SeeAlso:    -
03522 
03523 ********************************************************************************************/
03524 
03525 OpState OpShapeToggleElipPoly::GetState(String_256* UIDescription, OpDescriptor*)
03526 {
03527     OpState OpSt;  
03528     OpSt.Greyed = TRUE; 
03529     BOOL FoundEllipse = FALSE;  
03530     BOOL FoundPolygon = FALSE;  
03531     ObjChangeFlags cFlags;
03532     ObjChangeParam ObjChange(OBJCHANGE_STARTING,cFlags,NULL,NULL);
03533 
03534     SelRange* pSelection = GetApplication()->FindSelection();
03535     Node* pNode = pSelection->FindFirst();
03536 
03537     while (pNode != NULL)
03538     {
03539         if (IS_A(pNode, NodeRegularShape))
03540         {
03541             NodeRegularShape* pShape = (NodeRegularShape*)pNode;
03542         
03543             // Convert this shape if it's parents allow
03544             if (pShape->AllowOp(&ObjChange))                
03545             {
03546                 if (pShape->IsCircular())
03547                     FoundEllipse = TRUE;
03548                 else
03549                     FoundPolygon = TRUE;
03550             }
03551         }
03552 
03553         // Get the next selected thing
03554         pNode = pSelection->FindNext(pNode);
03555     }
03556 
03557     if (FoundEllipse || FoundPolygon)
03558         OpSt.Greyed = FALSE;
03559     if (FoundEllipse && !FoundPolygon)
03560         UIDescription->Load(_R(IDS_OPSHAPETOGGLE_ELIP_PO));
03561     if (!FoundEllipse && FoundPolygon)
03562         UIDescription->Load(_R(IDS_OPSHAPETOGGLE_ELIP_EP));
03563 
03564     return(OpSt);   
03565 }                                    
03566 
03567 
03568 
03569 /********************************************************************************************
03570 
03571 >   BOOL OpShapeToggleElipPoly::Init()
03572 
03573     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
03574     Created:    16/4/95
03575     Inputs:     -
03576     Outputs:    -
03577     Returns:    TRUE/FALSE for successful init
03578     Purpose:    Initialise the toggle ellipseness operation by registering it
03579     Errors:     -
03580     SeeAlso:    -
03581 
03582 ********************************************************************************************/
03583 
03584 BOOL OpShapeToggleElipPoly::Init()
03585 {
03586     return(RegisterOpDescriptor(0, 
03587                                 _R(IDS_OPSHAPETOGGLE_ELIP),
03588                                 CC_RUNTIME_CLASS(OpShapeToggleElipPoly), 
03589                                 OPTOKEN_TOGGLEELIPPOLY,
03590                                 OpShapeToggleElipPoly::GetState)); 
03591 }
03592 
03593 
03594 
03595 /********************************************************************************************
03596 
03597 >   BOOL OpShapeToggleElipPoly::DoAction(NodeRegularShape* pShape)
03598 
03599     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
03600     Created:    16/4/95
03601     Inputs:     pShape - points to the shape to toggle
03602     Outputs:    -
03603     Returns:    TRUE/FALSE for successful toggle
03604     Purpose:    Toggles the ellipse/polygon state of a shape
03605     Errors:     -
03606     SeeAlso:    -
03607 
03608 ********************************************************************************************/
03609 BOOL OpShapeToggleElipPoly::DoAction(NodeRegularShape* pShape)
03610 {
03611     BOOL Success = TRUE;
03612 
03613     if (Success)
03614         Success = (ChangeShapeDataAction::DoToggle(this, &UndoActions, pShape,
03615                 ChangeShapeDataAction::CHANGE_CIRCULAR, pShape->IsCircular() ? 1.0 : 0.0) != AC_FAIL);
03616     if (Success)
03617         pShape->SetCircular(!pShape->IsCircular());
03618 
03619     return Success;
03620 }
03621 
03622 
03623 
03624 
03626 // OpShapeToggleCurvature
03627 
03628 
03629 
03630 /********************************************************************************************
03631 
03632 >   OpShapeToggleStellation::OpShapeToggleStellation(): OpShapeToggleBase()
03633 
03634     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
03635     Created:    17/04/95
03636     Purpose:    Constructs an OpShapeToggleStellation object.
03637 
03638 ********************************************************************************************/
03639 
03640 OpShapeToggleStellation::OpShapeToggleStellation(): OpShapeToggleBase()
03641 {
03642 }
03643 
03644 
03645 
03646 /********************************************************************************************
03647 
03648 >   OpState OpShapeToggleStellation::GetState(String_256* UIDescription, OpDescriptor*)
03649 
03650     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
03651     Created:    17/04/95
03652     Inputs:     UIDescription - the name of the opertaion to display
03653                 OpDescriptor - To help update the state
03654     Outputs:    UIDescription - The name of the operation
03655     Returns:    The state of the operation
03656     Purpose:    Find the state of the OpShapeToggleStellation operation.
03657     Errors:     -
03658     SeeAlso:    -
03659 
03660 ********************************************************************************************/
03661 
03662 OpState OpShapeToggleStellation::GetState(String_256* UIDescription, OpDescriptor*)
03663 {
03664     OpState OpSt;  
03665     OpSt.Greyed = TRUE; 
03666     BOOL FoundStellated = FALSE;  
03667     BOOL FoundNotStellated = FALSE;  
03668     BOOL AllEllipses = TRUE;
03669     ObjChangeFlags cFlags;
03670     ObjChangeParam ObjChange(OBJCHANGE_STARTING,cFlags,NULL,NULL);
03671 
03672     SelRange* pSelection = GetApplication()->FindSelection();
03673     Node* pNode = pSelection->FindFirst();
03674 
03675     while (pNode != NULL)
03676     {
03677         if (IS_A(pNode, NodeRegularShape))
03678         {
03679             NodeRegularShape* pShape = (NodeRegularShape*)pNode;
03680         
03681             AllEllipses = AllEllipses && pShape->IsCircular();
03682 
03683             // Convert this shape if it's parents allow
03684             if (pShape->AllowOp(&ObjChange))                
03685             {
03686                 if (pShape->IsStellated())
03687                     FoundStellated = TRUE;
03688                 else
03689                     FoundNotStellated = TRUE;
03690             }
03691         }
03692 
03693         // Get the next selected thing
03694         pNode = pSelection->FindNext(pNode);
03695     }
03696 
03697     if (AllEllipses)
03698         UIDescription->Load(_R(IDS_REGSHAPE_ALLELLIPSES));
03699     else
03700     {
03701         if (FoundStellated || FoundNotStellated)
03702             OpSt.Greyed = FALSE;
03703         if (FoundStellated && !FoundNotStellated)
03704             UIDescription->Load(_R(IDS_OPSHAPETOGGLE_STELL_REM));
03705         if (!FoundStellated && FoundNotStellated)
03706             UIDescription->Load(_R(IDS_OPSHAPETOGGLE_STELL_ADD));
03707     }
03708 
03709     return(OpSt);   
03710 }                                    
03711 
03712 
03713 
03714 /********************************************************************************************
03715 
03716 >   BOOL OpShapeToggleStellation::Init()
03717 
03718     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
03719     Created:    16/4/95
03720     Inputs:     -
03721     Outputs:    -
03722     Returns:    TRUE/FALSE for successful init
03723     Purpose:    Initialise the toggle stellation operation by registering it
03724     Errors:     -
03725     SeeAlso:    -
03726 
03727 ********************************************************************************************/
03728 
03729 BOOL OpShapeToggleStellation::Init()
03730 {
03731     return(RegisterOpDescriptor(0, 
03732                                 _R(IDS_OPSHAPETOGGLE_STELL),
03733                                 CC_RUNTIME_CLASS(OpShapeToggleStellation), 
03734                                 OPTOKEN_TOGGLESTELLATION,
03735                                 OpShapeToggleStellation::GetState)); 
03736 }
03737 
03738 
03739 
03740 /********************************************************************************************
03741 
03742 >   BOOL OpShapeToggleStellation::DoAction(NodeRegularShape* pShape)
03743 
03744     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
03745     Created:    16/4/95
03746     Inputs:     pShape - points to the shape to toggle
03747     Outputs:    -
03748     Returns:    TRUE/FALSE for successful toggle
03749     Purpose:    Toggles the stellation state of a shape
03750     Errors:     -
03751     SeeAlso:    -
03752 
03753 ********************************************************************************************/
03754 BOOL OpShapeToggleStellation::DoAction(NodeRegularShape* pShape)
03755 {
03756     BOOL Success = TRUE;
03757 
03758     if (Success)
03759         Success = (ChangeShapeDataAction::DoToggle(this, &UndoActions, pShape,
03760                 ChangeShapeDataAction::CHANGE_STELLATED, pShape->IsStellated() ? 1.0 : 0.0) != AC_FAIL);
03761     if (Success)
03762         pShape->SetStellated(!pShape->IsStellated());
03763 
03764     return Success;
03765 }
03766 
03767 
03768 
03769 
03771 // OpShapeToggleCurvature
03772 
03773 
03774 
03775 /********************************************************************************************
03776 
03777 >   OpShapeToggleCurvature::OpShapeToggleCurvature(): OpShapeToggleBase()
03778 
03779     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
03780     Created:    17/04/95
03781     Purpose:    Constructs an OpShapeToggleCurvature object.
03782 
03783 ********************************************************************************************/
03784 
03785 OpShapeToggleCurvature::OpShapeToggleCurvature(): OpShapeToggleBase()
03786 {
03787 }
03788 
03789 
03790 
03791 /********************************************************************************************
03792 
03793 >   OpState OpShapeToggleCurvature::GetState(String_256* UIDescription, OpDescriptor*)
03794 
03795     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
03796     Created:    17/04/95
03797     Inputs:     UIDescription - the name of the opertaion to display
03798                 OpDescriptor - To help update the state
03799     Outputs:    UIDescription - The name of the operation
03800     Returns:    The state of the operation
03801     Purpose:    Find the state of the OpShapeToggleCurvature operation.
03802     Errors:     -
03803     SeeAlso:    -
03804 
03805 ********************************************************************************************/
03806 
03807 OpState OpShapeToggleCurvature::GetState(String_256* UIDescription, OpDescriptor*)
03808 {
03809     OpState OpSt;  
03810     OpSt.Greyed = TRUE; 
03811     BOOL FoundCurved = FALSE;  
03812     BOOL FoundNotCurved = FALSE;
03813     BOOL AllEllipses = TRUE;
03814     ObjChangeFlags cFlags;
03815     ObjChangeParam ObjChange(OBJCHANGE_STARTING,cFlags,NULL,NULL);
03816 
03817     SelRange* pSelection = GetApplication()->FindSelection();
03818     Node* pNode = pSelection->FindFirst();
03819 
03820     while (pNode != NULL)
03821     {
03822         if (IS_A(pNode, NodeRegularShape))
03823         {
03824             NodeRegularShape* pShape = (NodeRegularShape*)pNode;
03825 
03826             AllEllipses = AllEllipses && pShape->IsCircular();
03827         
03828             // Convert this shape if it's parents allow
03829             if (pShape->AllowOp(&ObjChange))                
03830             {
03831                 if (pShape->IsPrimaryCurvature() && pShape->IsStellationCurvature())
03832                     FoundCurved = TRUE;
03833                 else
03834                     FoundNotCurved = TRUE;
03835             }
03836         }
03837 
03838         // Get the next selected thing
03839         pNode = pSelection->FindNext(pNode);
03840     }
03841 
03842     if (AllEllipses)
03843         UIDescription->Load(_R(IDS_REGSHAPE_ALLELLIPSES));
03844     else
03845     {
03846         if (FoundCurved || FoundNotCurved)
03847             OpSt.Greyed = FALSE;
03848         if (FoundCurved && !FoundNotCurved)
03849             UIDescription->Load(_R(IDS_OPSHAPETOGGLE_CURVE_REM));
03850         if (!FoundCurved && FoundNotCurved)
03851             UIDescription->Load(_R(IDS_OPSHAPETOGGLE_CURVE_ADD));
03852     }
03853 
03854     return(OpSt);   
03855 }                                    
03856 
03857 
03858 
03859 /********************************************************************************************
03860 
03861 >   BOOL OpShapeToggleCurvature::Init()
03862 
03863     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
03864     Created:    16/4/95
03865     Inputs:     -
03866     Outputs:    -
03867     Returns:    TRUE/FALSE for successful init
03868     Purpose:    Initialise the toggle curvature operation by registering it
03869     Errors:     -
03870     SeeAlso:    -
03871 
03872 ********************************************************************************************/
03873 
03874 BOOL OpShapeToggleCurvature::Init()
03875 {
03876     return(RegisterOpDescriptor(0, 
03877                                 _R(IDS_OPSHAPETOGGLE_CURVE),
03878                                 CC_RUNTIME_CLASS(OpShapeToggleCurvature), 
03879                                 OPTOKEN_TOGGLECURVATURE,
03880                                 OpShapeToggleCurvature::GetState)); 
03881 }
03882 
03883 
03884 
03885 /********************************************************************************************
03886 
03887 >   BOOL OpShapeToggleCurvature::DoAction(NodeRegularShape* pShape)
03888 
03889     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
03890     Created:    16/4/95
03891     Inputs:     pShape - points to the shape to toggle
03892     Outputs:    -
03893     Returns:    TRUE/FALSE for successful toggle
03894     Purpose:    Toggles the curvature state of a shape
03895     Errors:     -
03896     SeeAlso:    -
03897 
03898 ********************************************************************************************/
03899 BOOL OpShapeToggleCurvature::DoAction(NodeRegularShape* pShape)
03900 {
03901     BOOL Success = TRUE;
03902 
03903     // Do the primary curvature
03904     if (Success)
03905         Success = (ChangeShapeDataAction::DoToggle(this, &UndoActions, pShape,
03906                 ChangeShapeDataAction::CHANGE_PRIMARYCURVATURE, pShape->IsPrimaryCurvature() ? 1.0 : 0.0) != AC_FAIL);
03907     if (Success)
03908         pShape->SetPrimaryCurvature(!pShape->IsPrimaryCurvature());
03909 
03910     // Do the stellation curvature
03911     if (Success)
03912         Success = (ChangeShapeDataAction::DoToggle(this, &UndoActions, pShape,
03913                 ChangeShapeDataAction::CHANGE_STELLATIONCURVATURE, pShape->IsStellationCurvature() ? 1.0 : 0.0) != AC_FAIL);
03914     if (Success)
03915         pShape->SetStellationCurvature(!pShape->IsStellationCurvature());
03916 
03917     return Success;
03918 }
03919 
03920 
03921 
03923 // OpShapeToggleSetNumSides
03924 
03925 
03926 
03927 /********************************************************************************************
03928 
03929 >   OpShapeToggleSetNumSides::OpShapeToggleSetNumSides(): SelOperation()
03930 
03931     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
03932     Created:    22/04/95
03933     Purpose:    Constructs an OpShapeToggleSetNumSides object.
03934 
03935 ********************************************************************************************/
03936 
03937 OpShapeToggleSetNumSides::OpShapeToggleSetNumSides(): SelOperation()
03938 {
03939 }
03940 
03941 
03942 
03943 /********************************************************************************************
03944 
03945 >   OpState OpShapeToggleSetNumSides::GetState(String_256* UIDescription, OpDescriptor*)
03946 
03947     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
03948     Created:    17/04/95
03949     Inputs:     UIDescription - the name of the opertaion to display
03950                 OpDescriptor - To help update the state
03951     Outputs:    UIDescription - The name of the operation
03952     Returns:    The state of the operation
03953     Purpose:    Find the state of the OpShapeToggleCurvature operation.
03954     Errors:     -
03955     SeeAlso:    -
03956 
03957 ********************************************************************************************/
03958 
03959 OpState OpShapeToggleSetNumSides::GetState(String_256* UIDescription, OpDescriptor*)
03960 {
03961     OpState OpSt; 
03962 
03963     BOOL FoundEditable = FALSE;
03964     ObjChangeParam ObjChange(OBJCHANGE_STARTING,ObjChangeFlags(),NULL,NULL);
03965 
03966     SelRange* pSelection = GetApplication()->FindSelection();
03967     Node* pNode = pSelection->FindFirst();
03968 
03969     while (pNode != NULL)
03970     {
03971         if (IS_A(pNode, NodeRegularShape))
03972         {
03973             NodeRegularShape* pShape = (NodeRegularShape*)pNode;
03974         
03975             // Convert this shape if it's parents allow
03976             if (pNode->AllowOp(&ObjChange) && !pShape->IsCircular())
03977                 FoundEditable = TRUE;
03978         }
03979 
03980         // Get the next selected thing
03981         pNode = pSelection->FindNext(pNode);
03982     }  
03983 
03984     if (!FoundEditable)
03985         OpSt.Greyed = TRUE;
03986 
03987     return OpSt;    
03988 }                                    
03989 
03990 
03991 
03992 /********************************************************************************************
03993 
03994 >   BOOL OpShapeToggleSetNumSides::Init()
03995 
03996     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
03997     Created:    16/4/95
03998     Inputs:     -
03999     Outputs:    -
04000     Returns:    TRUE/FALSE for successful init
04001     Purpose:    Initialise the set number of sides operation by registering it
04002     Errors:     -
04003     SeeAlso:    -
04004 
04005 ********************************************************************************************/
04006 
04007 BOOL OpShapeToggleSetNumSides::Init()
04008 {
04009     BOOL ok = TRUE;
04010     
04011     ok = ok && RegisterOpDescriptor(0, 
04012                                     _R(IDS_QUICKSHAPES_NUMBERSIDES),
04013                                     CC_RUNTIME_CLASS(OpShapeToggleSetNumSides), 
04014                                     OPTOKEN_QUICKSHAPE_NUMBERSIDES,
04015                                     OpShapeToggleSetNumSides::GetState); 
04016     ok = ok && RegisterOpDescriptor(0, 
04017                                     _R(IDS_QUICKSHAPES_NUMBERSIDES3),
04018                                     CC_RUNTIME_CLASS(OpShapeToggleSetNumSides3), 
04019                                     OPTOKEN_QUICKSHAPE_NUMBERSIDES3,
04020                                     OpShapeToggleSetNumSides::GetState); 
04021     ok = ok && RegisterOpDescriptor(0, 
04022                                     _R(IDS_QUICKSHAPES_NUMBERSIDES4),
04023                                     CC_RUNTIME_CLASS(OpShapeToggleSetNumSides4), 
04024                                     OPTOKEN_QUICKSHAPE_NUMBERSIDES4,
04025                                     OpShapeToggleSetNumSides::GetState); 
04026     ok = ok && RegisterOpDescriptor(0, 
04027                                     _R(IDS_QUICKSHAPES_NUMBERSIDES5),
04028                                     CC_RUNTIME_CLASS(OpShapeToggleSetNumSides5), 
04029                                     OPTOKEN_QUICKSHAPE_NUMBERSIDES5,
04030                                     OpShapeToggleSetNumSides::GetState); 
04031     ok = ok && RegisterOpDescriptor(0, 
04032                                     _R(IDS_QUICKSHAPES_NUMBERSIDES6),
04033                                     CC_RUNTIME_CLASS(OpShapeToggleSetNumSides6), 
04034                                     OPTOKEN_QUICKSHAPE_NUMBERSIDES6,
04035                                     OpShapeToggleSetNumSides::GetState); 
04036     ok = ok && RegisterOpDescriptor(0, 
04037                                     _R(IDS_QUICKSHAPES_NUMBERSIDES7),
04038                                     CC_RUNTIME_CLASS(OpShapeToggleSetNumSides7), 
04039                                     OPTOKEN_QUICKSHAPE_NUMBERSIDES7,
04040                                     OpShapeToggleSetNumSides::GetState); 
04041     ok = ok && RegisterOpDescriptor(0, 
04042                                     _R(IDS_QUICKSHAPES_NUMBERSIDES8),
04043                                     CC_RUNTIME_CLASS(OpShapeToggleSetNumSides8), 
04044                                     OPTOKEN_QUICKSHAPE_NUMBERSIDES8,
04045                                     OpShapeToggleSetNumSides::GetState); 
04046     ok = ok && RegisterOpDescriptor(0, 
04047                                     _R(IDS_QUICKSHAPES_NUMBERSIDES9),
04048                                     CC_RUNTIME_CLASS(OpShapeToggleSetNumSides9), 
04049                                     OPTOKEN_QUICKSHAPE_NUMBERSIDES9,
04050                                     OpShapeToggleSetNumSides::GetState); 
04051     ok = ok && RegisterOpDescriptor(0, 
04052                                     _R(IDS_QUICKSHAPES_NUMBERSIDES10),
04053                                     CC_RUNTIME_CLASS(OpShapeToggleSetNumSides10), 
04054                                     OPTOKEN_QUICKSHAPE_NUMBERSIDES10,
04055                                     OpShapeToggleSetNumSides::GetState); 
04056 
04057     return ok;
04058 }
04059 
04060 
04061 
04062 /********************************************************************************************
04063 
04064 >   BOOL OpShapeToggleSetNumSides::DoAction(INT32 NumSides)
04065 
04066     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
04067     Created:    16/4/95
04068     Inputs:     NumSides - the number of sides to set the shape to
04069     Outputs:    -
04070     Returns:    TRUE/FALSE for successful toggle
04071     Purpose:    Sets the number of sides of a shape
04072     Errors:     -
04073     SeeAlso:    -
04074 
04075 ********************************************************************************************/
04076 BOOL OpShapeToggleSetNumSides::DoAction(INT32 NumSides)
04077 {
04078     BOOL ok = DoStartSelOp(FALSE,FALSE);
04079 
04080     ObjChangeParam ObjChange(OBJCHANGE_STARTING,ObjChangeFlags(), NULL, this);
04081     SelRange* Selection = GetApplication()->FindSelection();
04082     Node* pNode = Selection->FindFirst();
04083 
04084     while (ok && (pNode != NULL)) 
04085     {
04086         if (IS_A(pNode,NodeRegularShape))
04087         {
04088             NodeRegularShape* pShape = (NodeRegularShape*)pNode;
04089         
04090             if (pShape->AllowOp(&ObjChange) && !pShape->IsCircular())
04091             {
04092                 // Record the bounds
04093                 if (ok)
04094                     ok = (RecalcBoundsAction::DoRecalc(this, &UndoActions, pShape) != AC_FAIL);
04095                 
04096                 // Change the shape
04097                 ok = (ChangeShapeDataAction::DoToggle(this, &UndoActions, pShape, 
04098                             ChangeShapeDataAction::CHANGE_NUMSIDES, pShape->GetNumSides()) != AC_FAIL);
04099                 if (ok)
04100                     pShape->SetNumSides(NumSides);
04101 
04102                 // Invalidate the shapes bounding rect as it's changed
04103                 if (ok)
04104                 {
04105                     pShape->InvalidateBoundingRect();
04106                     pShape->InvalidateCache();
04107                 }
04108 
04109                 // And redraw its final state
04110                 if (ok)
04111                     ok = (RecordBoundsAction::DoRecord(this, &UndoActions, pShape) != AC_FAIL);
04112 
04113                 // Say we have finished
04114                 if (ok)
04115                 {
04116                     ObjChange.Define(OBJCHANGE_FINISHED,ObjChangeFlags(),NULL,this);
04117                     ok = UpdateChangedNodes(&ObjChange);
04118                 }
04119             }
04120         }
04121 
04122         pNode = Selection->FindNext(pNode);
04123     }
04124 
04125     if (!ok)
04126     {
04127         InformError();
04128         FailAndExecute();
04129     }
04130 
04131     End();
04132 
04133     return ok;
04134 }
04135 
04136 
04137 
04138 void OpShapeToggleSetNumSides3::Do(OpDescriptor* pOpDesc)
04139 {
04140     if (!DoAction(3))
04141         InformError();
04142 }
04143 void OpShapeToggleSetNumSides4::Do(OpDescriptor* pOpDesc)
04144 {
04145     if (!DoAction(4))
04146         InformError();
04147 }
04148 void OpShapeToggleSetNumSides5::Do(OpDescriptor* pOpDesc)
04149 {
04150     if (!DoAction(5))
04151         InformError();
04152 }
04153 void OpShapeToggleSetNumSides6::Do(OpDescriptor* pOpDesc)
04154 {
04155     if (!DoAction(6))
04156         InformError();
04157 }
04158 void OpShapeToggleSetNumSides7::Do(OpDescriptor* pOpDesc)
04159 {
04160     if (!DoAction(7))
04161         InformError();
04162 }
04163 void OpShapeToggleSetNumSides8::Do(OpDescriptor* pOpDesc)
04164 {
04165     if (!DoAction(8))
04166         InformError();
04167 }
04168 void OpShapeToggleSetNumSides9::Do(OpDescriptor* pOpDesc)
04169 {
04170     if (!DoAction(9))
04171         InformError();
04172 }
04173 void OpShapeToggleSetNumSides10::Do(OpDescriptor* pOpDesc)
04174 {
04175     if (!DoAction(10))
04176         InformError();
04177 }
04178 
04179 
04180 
04182 //  OpShapeMakeRegular 
04183 
04184 
04185 /********************************************************************************************
04186 
04187 >   static OpState OpShapeMakeRegular::GetState(String_256* UIDesc, OpDescriptor* OpDesc)
04188 
04189     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
04190     Created:    23/4/95
04191     Inputs:     UIDesc - 
04192                 OpDesc -
04193     Outputs:    -
04194     Returns:    The tick/grey state of this op
04195     Purpose:    Returns the state of this operation
04196     Errors:     -
04197     SeeAlso:    -
04198 
04199 ********************************************************************************************/
04200 OpState OpShapeMakeRegular::GetState(String_256* UIDesc, OpDescriptor* OpDesc)
04201 {
04202     OpState Blobby;
04203     
04204     return Blobby;
04205 }
04206 
04207 
04208 
04209 /********************************************************************************************
04210 
04211 >   static BOOL OpShapeMakeRegular::Init()
04212 
04213     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
04214     Created:    23/4/95
04215     Inputs:     -
04216     Outputs:    -
04217     Returns:    TRUE/FALSE for successful initialise
04218     Purpose:    Registers this op
04219     Errors:     -
04220     SeeAlso:    -
04221 
04222 ********************************************************************************************/
04223 BOOL OpShapeMakeRegular::Init()
04224 {
04225     return RegisterOpDescriptor(0, 
04226                                 _R(IDS_QUICKSHAPE_MAKEREGULAR),
04227                                 CC_RUNTIME_CLASS(OpShapeMakeRegular), 
04228                                 OPTOKEN_QUICKSHAPE_MAKEREGULAR,
04229                                 OpShapeMakeRegular::GetState); 
04230 }                   
04231 
04232 
04233 
04234 /********************************************************************************************
04235 
04236 >   void OpShapeMakeRegular::DoMakeRegular(NodeRegularShape* pShape, DocCoord RegularPoint)
04237 
04238     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
04239     Created:    23/4/95
04240     Inputs:     pShape - points to the shape to make regular
04241                 RegularPoint - defines the new size of the shape, from the centre to this point
04242     Outputs:    -
04243     Returns:    -
04244     Purpose:    Makes a QuickShape regular, ie 100% aspect ratio, no skew, shear etc...
04245     Errors:     Assorted pointer checks
04246     SeeAlso:    -
04247 
04248 ********************************************************************************************/
04249 void OpShapeMakeRegular::DoMakeRegular(NodeRegularShape* pShape, DocCoord RegularPoint)
04250 {
04251     if (!DoActions(pShape, RegularPoint))
04252     {
04253         InformError();
04254         FailAndExecute();
04255     }
04256 
04257     End();
04258 }
04259 
04260 
04261 
04262 /********************************************************************************************
04263 
04264 >   BOOL OpShapeMakeRegular::DoActions(NodeRegularShape* pShape, DocCoord RegularPoint)
04265 
04266     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
04267     Created:    23/4/95
04268     Inputs:     pShape - points to the shape to make regular
04269                 RegularPoint - defines the new size of the shape, from the centre to this point
04270     Outputs:    -
04271     Returns:    -
04272     Purpose:    Makes a QuickShape regular, ie 100% aspect ratio, no skew, shear etc...
04273     Errors:     Assorted pointer checks
04274     SeeAlso:    -
04275 
04276 ********************************************************************************************/
04277 BOOL OpShapeMakeRegular::DoActions(NodeRegularShape* pShape, DocCoord RegularPoint)
04278 {
04279     ERROR2IF(pShape == NULL, FALSE, "Shape pointer was NULL");
04280     ERROR3IF(!pShape->IsCircular(), "Make regular not implemented for non-circular QuickShapes");
04281 
04282     BOOL ok = DoStartSelOp(FALSE, FALSE);
04283 
04284     // Get required variables
04285     DocCoord ShapeCentre = pShape->GetCentrePoint();
04286     DocCoord UTShapeCentre = pShape->GetUTCentrePoint();
04287     const double RequiredLength = ShapeCentre.Distance(RegularPoint);
04288     const double MajorLength = pShape->GetUTMajorAxes().Distance(UTShapeCentre);
04289     const double MinorLength = pShape->GetUTMinorAxes().Distance(UTShapeCentre);
04290     const double RequiredAngle = atan2((double)(RegularPoint.y-ShapeCentre.y),(double)(RegularPoint.x-ShapeCentre.x));
04291     const double MajorAxisAngle = atan2((double)(pShape->GetUTMajorAxes().y-UTShapeCentre.y),
04292                                                 (double)(pShape->GetUTMajorAxes().x-UTShapeCentre.x));
04293     Matrix ShapeMat;
04294     pShape->GetTransformMatrix(&ShapeMat);
04295     Trans2DMatrix* Translation = new Trans2DMatrix(ShapeMat);
04296     Translation->Invert();
04297     
04298     // Translate shape to origin
04299     if (ok)
04300         *Translation *= Trans2DMatrix(-UTShapeCentre.x, -UTShapeCentre.y);
04301 
04302     // Rotate so major axis points along +ve x-axis (not y!)
04303     if (ok)
04304         *Translation *= Trans2DMatrix(Matrix(-ANGLE(MajorAxisAngle*(180/PI))));
04305 
04306     // Scale it so it is regular
04307     if (ok)
04308         *Translation *= Trans2DMatrix(Matrix(FIXED16(RequiredLength/MajorLength), FIXED16(RequiredLength/MinorLength)));
04309     
04310     // Rotate so major axis is under click point
04311     if (ok)
04312         *Translation *= Trans2DMatrix(Matrix(ANGLE(RequiredAngle*(180/PI))));
04313 
04314     // Translate shape to old centre point
04315     if (ok)
04316         *Translation *= Trans2DMatrix(ShapeCentre.x, ShapeCentre.y);
04317 
04318     ObjChangeParam ObjChange(OBJCHANGE_STARTING,ObjChangeFlags(), NULL, this);
04319     if (pShape->AllowOp(&ObjChange))
04320     {
04321         // Record the bounds
04322         if (ok)
04323             ok = (RecalcBoundsAction::DoRecalc(this, &UndoActions, pShape) != AC_FAIL);
04324 
04325         // Now actually transform the shape in the document
04326         if (ok)
04327             ok = DoTransformNode(pShape, Translation);
04328 
04329         // And redraw its final state
04330         if (ok)
04331             ok = (RecordBoundsAction::DoRecord(this, &UndoActions, pShape) != AC_FAIL);
04332 
04333         GetApplication()->FindSelection()->Update();
04334 
04335         // Say we have finished
04336         if (ok)                                   
04337         {
04338             ObjChange.Define(OBJCHANGE_FINISHED,ObjChangeFlags(),NULL,this);
04339             ok = UpdateChangedNodes(&ObjChange);
04340         }
04341     }
04342 
04343     return ok;
04344 }

Generated on Sat Nov 10 03:47:01 2007 for Camelot by  doxygen 1.4.4