beztool.cpp

Go to the documentation of this file.
00001 // $Id: beztool.cpp 1700 2006-08-15 13:32:14Z 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 // The Bezier Edit Tool
00099 // Created by Jim on 31/3/94
00100 // Based on Freehand.cpp by Rik
00101 
00102 /*
00103 */
00104 
00105 #include "camtypes.h"
00106 #include "beztool.h"
00107 
00108 //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00109 //#include "attrmgr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00110 //#include "bezbar.h"
00111 #include "blobs.h"
00112 #include "csrstack.h"
00113 //#include "document.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00114 //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00115 #include "finfodlg.h"
00116 #include "keypress.h"
00117 //#include "jim.h"
00118 #include "lineattr.h"
00119 //#include "mario.h"
00120 //#include "markn.h"
00121 //#include "msg.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00122 #include "ndtxtpth.h"
00123 //#include "node.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00124 #include "nodedoc.h"
00125 #include "nodershp.h"
00126 #include "nodetxts.h"
00127 #include "oilfiles.h"
00128 #include "oilmods.h"
00129 #include "opbreak.h"
00130 #include "opbezier.h"
00131 //#include "opdesc.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00132 #include "opnudge.h"
00133 #include "pathedit.h"
00134 #include "pathndge.h"
00135 #include "pathops.h"
00136 //#include "peter.h"
00137 #include "progress.h"
00138 //#include "resource.h"
00139 //#include "rik.h"
00140 //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00141 //#include "viewrc.h"
00142 #include "vkextra.h"
00143 #include "nodeblnd.h"
00144 #include "ndbldpth.h"
00145 #include "blndtool.h"
00146 //#include "justin2.h"
00147 //#include "will2.h"
00148 
00149 #include "lattrops.h"
00150 #include "nbevcont.h"
00151 #include "nodecntr.h"
00152 #include "nodeshad.h"
00153 #include "slicehelper.h"
00154 #include "opbevel.h"
00155 #include "nodebev.h"
00156 
00157 DECLARE_SOURCE("$Revision: 1700 $");
00158 
00159 CC_IMPLEMENT_MEMDUMP( BezierTool, Tool_v1 )
00160 CC_IMPLEMENT_DYNCREATE( BezToolInfoBarOp, InformationBarOp )
00161 
00162 // Better memory tracking please
00163 #define new CAM_DEBUG_NEW
00164 
00165 // These are still TCHAR* while we wait for resource technology to be developed for modules
00166 TCHAR* BezierTool::FamilyName = _T("Drawing Tools");
00167 TCHAR* BezierTool::ToolName = _T("Bezier Tool");
00168 TCHAR* BezierTool::Purpose = _T("To Draw arbitrary lines");
00169 TCHAR* BezierTool::Author = _T("Jim (latterly Peter)");
00170 
00171 BezToolInfoBarOp*   BezierTool::pBezToolInfoBarOp = NULL;
00172 BOOL BezierTool::CreateCurve = TRUE;
00173 BOOL BezierTool::CreateCusp = FALSE;
00174 
00175 const INT32 NumberIconIDs = 18;
00176 INT32 AllIconIDs[NumberIconIDs] =  
00177         {_R(IDC_PATH_EDIT_ENDPOINTX), _R(IDC_PATH_EDIT_ENDPOINTY), _R(IDC_PATH_BUMP_ENDX_LESS),
00178          _R(IDC_PATH_BUMP_ENDX_MORE), _R(IDC_PATH_BUMP_ENDY_LESS), _R(IDC_PATH_BUMP_ENDY_MORE),
00179          _R(IDC_PATH_EDIT_FIRSTX), _R(IDC_PATH_EDIT_FIRSTY), _R(IDC_PATH_BUMP_FIRSTX_LESS),
00180          _R(IDC_PATH_BUMP_FIRSTX_MORE), _R(IDC_PATH_BUMP_FIRSTY_LESS), _R(IDC_PATH_BUMP_FIRSTY_MORE),
00181          _R(IDC_PATH_EDIT_SECONDX), _R(IDC_PATH_EDIT_SECONDY), _R(IDC_PATH_BUMP_SECONDX_LESS),
00182          _R(IDC_PATH_BUMP_SECONDX_MORE), _R(IDC_PATH_BUMP_SECONDY_LESS), _R(IDC_PATH_BUMP_SECONDY_MORE)};
00183 
00184 
00185 /********************************************************************************************
00186 
00187 >   BezierTool::BezierTool()
00188 
00189     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00190     Created:    21/6/93
00191     Purpose:    Dump Constructor - It does nothing. All the real initialisation is done
00192                 in BezierTool::Init which is called by the Tool Manager
00193     SeeAlso:    BezierTool::Init
00194 
00195 ********************************************************************************************/
00196 
00197 BezierTool::BezierTool()
00198 {
00199     StartSpread = NULL;
00200     FloatingEndpoint = FALSE;
00201     MoveToDoc = NULL;
00202     MoveToSpread = NULL;
00203     pSmooth = NULL;
00204     RetroFlag = FALSE;
00205     DontDrawOnClearMoveTo = FALSE;
00206     CurrentToolMode = New;
00207 }
00208 
00209 
00210 
00211 /********************************************************************************************
00212 
00213 >   BezierTool::~BezierTool()
00214 
00215     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00216     Created:    21/6/93
00217     Purpose:    Destructor (Virtual). Does nothing.
00218 
00219 ********************************************************************************************/
00220 
00221 BezierTool::~BezierTool()
00222 {
00223     // Destroy any resident retro smooth object
00224     if (pSmooth != NULL)
00225     {
00226         delete pSmooth;
00227         pSmooth = NULL;
00228     }
00229 
00230     pBezToolInfoBarOp->pBezTool = NULL;
00231 }
00232 
00233 
00234 
00235 /********************************************************************************************
00236 
00237 >   BOOL BezierTool::Init( INT32 Pass )
00238 
00239     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00240     Created:    21/6/93
00241     Returns:    FALSE if it does not want to be created, TRUE otherwise
00242     Purpose:    Used to check if the Tool was properly constructed
00243     SeeAlso:    BezierTool::BezierTool
00244 
00245 \********************************************************************************************/
00246 
00247 BOOL BezierTool::Init()
00248 {
00249     CCResTextFile       file;               // Resource File
00250     BezToolInfoBarOpCreate BarCreate;       // Object that creates InfoBarOp objects
00251 
00252     #if 0
00253     BOOL                ok;
00254     ok = file.open(_R(IDM_BEZTOOL_BAR), _R(IDT_INFO_BAR_RES));          // Open resource
00255     if (ok) ok = DialogBarOp::ReadBarsFromFile(file,BarCreate); // Read and create info bar
00256     if (ok) file.close();                                       // Close resource
00257 
00258     ERROR2IF(!ok, FALSE, "Unable to load bezbar.ini from resource\n"); 
00259 
00260     // Info bar now exists.  Now get a pointer to it
00261     String_32 str = String_32(_R(IDS_BEZTOOL_INFOBARNAME));
00262     DialogBarOp* pDialogBarOp = DialogBarOp::FindDialogBarOp(str);
00263     
00264     ERROR2IF(pDialogBarOp==NULL, FALSE, "Line tool infobar not found\n");
00265 
00266     ERROR3IF(!(pDialogBarOp->IS_KIND_OF(BezToolInfoBarOp)), "Line tool infobar wrong type");
00267     #endif
00268 
00269     pBezToolInfoBarOp = new BezToolInfoBarOp();
00270     if (!pBezToolInfoBarOp)
00271     {
00272         ERROR3("Could not load bezier tool info bar");
00273         return FALSE;
00274     }   
00275 
00276     FloatingEndpoint = FALSE;
00277     MoveToDoc = NULL;
00278     MoveToSpread = NULL;
00279 
00280     pBezToolInfoBarOp->pBezTool = this;             // Set a pointer from the op to this tool
00281     pBezToolInfoBarOp->CurrentIndex = -1;
00282     pBezToolInfoBarOp->CurrentNodePath = NULL;
00283     pBezToolInfoBarOp->CurrentInkPath = NULL;
00284     pBezToolInfoBarOp->CurrentSpread = NULL;
00285     pBezToolInfoBarOp->IgnoreNextUpdate = FALSE;
00286     pBezToolInfoBarOp->LastShownPrevLength = 0;
00287     pBezToolInfoBarOp->LastShownNextLength = 0;
00288 
00289     // Read in the preference settings from the file
00290     GetApplication()->DeclareSection(_T("ShapeEditorTool"),2);
00291     GetApplication()->DeclarePref(_T("ShapeEditorTool"),_T("CreateCusp"), &CreateCusp, 0, 1);
00292     GetApplication()->DeclarePref(_T("ShapeEditorTool"),_T("CreateCurved"), &CreateCurve, 0, 1);
00293 
00294     // create a retro smooth regions object
00295     if (pSmooth != NULL)
00296     {
00297         ERROR3("Initialisation failure in class BezierTool: smooth ptr not NULL");
00298         return FALSE;
00299     }
00300 
00301     pSmooth = new RetroSmooth;
00302     if (pSmooth == NULL)
00303         return FALSE;
00304 
00305     if (!pSmooth->Initialise())
00306         return FALSE;
00307 
00308     // Initialise operations in opbezier.cpp
00309     if (!( OpSelectPathPoints::Declare() && OpInsertFloater::Declare() && OpRemoveFloater::Declare() ) )
00310         return FALSE;
00311 
00312     return TRUE;
00313 }
00314 
00315 
00316 
00317 
00318 
00319 /********************************************************************************************
00320 
00321 >   void BezierTool::Describe(void *InfoPtr)
00322 
00323     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00324     Created:    21/6/93
00325     Inputs:     InfoPtr - A pointer to a tool info block. It is passed cast to void* as
00326                 the version of the tool is unknown at this point. Later versions of the
00327                 Tool class may have more items in this block, that this tool will not use
00328     Outputs:    InfoPtr - The structure pointed to by InfoPtr will have had all the info
00329                 that this version of the Tool knows about
00330     Purpose:    Allows the tool manager to extract information about the tool
00331 
00332 ********************************************************************************************/
00333 
00334 void BezierTool::Describe(void *InfoPtr)
00335 {
00336     // Cast structure into the latest one we understand.
00337     ToolInfo_v1 *Info = (ToolInfo_v1 *) InfoPtr;
00338 
00339     Info -> InfoVersion = 1;
00340     
00341     Info -> InterfaceVersion = GetToolInterfaceVersion();  // You should always have this line.
00342         
00343     // These are all arbitrary at present.
00344     Info -> Version = 1;
00345     Info -> ID      = GetID();
00346     Info -> TextID  = _R(IDS_BEZIER_TOOL);
00347 
00348     Info -> Family  = FamilyName;
00349     Info -> Name    = ToolName;
00350     Info -> Purpose = Purpose;
00351     Info -> Author  = Author;
00352 
00353     Info -> InfoBarDialog = _R(IDD_BEZTOOLBAR);
00354 
00355     Info -> BubbleID = _R(IDBBL_LINETOOL);
00356     Info -> StatusID = _R(IDS_LINETOOL);
00357 }
00358 
00359 
00360 
00361 /********************************************************************************************
00362 >   virtual void BezierTool::SelectChange(BOOL isSelected)
00363 
00364     Author:     Justin_Flude (Xara Group Ltd) <camelotdev@xara.com>
00365     Created:    24/11/93
00366     Inputs:     -
00367     Outputs:    -
00368     Returns:    -
00369     Purpose:    Called when the tool is selected or deselected.  Creates and pushes the
00370                 tool's cursor; pops and destroys it.
00371     Errors:     If creating any cursor fails, all are deleted and the user is told about
00372                 lack of memory
00373     SeeAlso:    -
00374 ********************************************************************************************/
00375 
00376 void BezierTool::SelectChange(BOOL isSelected)
00377 {
00378     if (isSelected)
00379     {
00380         // This tool has just been selected.  Create an appropriate cursor, and push it
00381         // onto the top of the cursor stack so it'll appear when the pointer moves into
00382         // our window.
00383         
00384         MyCurrentCursor = NULL;     // in case we couldn't create one!
00385         
00386         pcMoveBezCursor = new Cursor(this, _R(IDC_MOVEBEZIERCURSOR));
00387         if (!pcMoveBezCursor || !pcMoveBezCursor->IsValid())
00388         {
00389             InformError( _R(IDS_OUT_OF_MEMORY), _R(IDS_OK) );
00390             return;                                      
00391         }
00392 
00393         pcReshapeLineCursor = new Cursor(this, _R(IDC_RESHAPECURSOR));
00394         if (!pcReshapeLineCursor || !pcReshapeLineCursor->IsValid())
00395         {
00396             delete pcMoveBezCursor;
00397             InformError( _R(IDS_OUT_OF_MEMORY), _R(IDS_OK) );
00398             return;
00399         }
00400 
00401         pcNewPathCursor = new Cursor(this, _R(IDC_NEWPATHCURSOR));
00402         if (!pcNewPathCursor || !pcNewPathCursor->IsValid())
00403         {
00404             delete pcMoveBezCursor;
00405             delete pcReshapeLineCursor;
00406             InformError( _R(IDS_OUT_OF_MEMORY), _R(IDS_OK) );
00407             return;
00408         }
00409 
00410         pcClosePathCursor = new Cursor(this, _R(IDC_CLOSEPATHCURSOR));
00411         if (!pcClosePathCursor || !pcClosePathCursor->IsValid())
00412         {
00413             delete pcMoveBezCursor;
00414             delete pcReshapeLineCursor;
00415             delete pcNewPathCursor;
00416             InformError( _R(IDS_OUT_OF_MEMORY), _R(IDS_OK) );
00417             return;
00418         }
00419 
00420         pcAddPathCursor = new Cursor(this, _R(IDC_ADDPATHCURSOR));
00421         if (!pcAddPathCursor || !pcAddPathCursor->IsValid())
00422         {
00423             delete pcMoveBezCursor;
00424             delete pcReshapeLineCursor;
00425             delete pcNewPathCursor;
00426             delete pcClosePathCursor;
00427             InformError( _R(IDS_OUT_OF_MEMORY), _R(IDS_OK) );
00428             return;
00429         }
00430 
00431         CurrentCursorID = CursorStack::GPush(pcNewPathCursor, FALSE);
00432         MyCurrentCursor = pcNewPathCursor;
00433 
00434         // Which blobs do I want displayed
00435         BlobManager* BlobMgr = GetApplication()->GetBlobManager();
00436         if (BlobMgr != NULL)
00437         {
00438             // Decide which blobs we will display
00439             BlobStyle MyBlobs;
00440             MyBlobs.Object = TRUE;
00441 
00442             // Tell the blob manager
00443             BlobMgr->ToolInterest(MyBlobs);
00444         }
00445         
00446         if (FloatingEndpoint)
00447             BlobMgr->RenderToolBlobsOn(this, MoveToSpread,NULL);
00448 
00449         pBezToolInfoBarOp->Create();
00450         SetModeFlag();
00451         RetroFlag = FALSE;
00452         ResetRetroSlider();
00453 
00454         //Alias all the nudge ops to the path specific ones.
00455         OpDescriptor* pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_NUDGEUP1);  
00456         pOpDesc->AliasOperation(CC_RUNTIME_CLASS(OpPathNudgeUp1),NULL,0);  
00457         pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_NUDGEUP5);            
00458         pOpDesc->AliasOperation(CC_RUNTIME_CLASS(OpPathNudgeUp5),NULL,0);  
00459         pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_NUDGEUP10);  
00460         pOpDesc->AliasOperation(CC_RUNTIME_CLASS(OpPathNudgeUp10),NULL,0);
00461         pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_NUDGEUPFIFTH);  
00462         pOpDesc->AliasOperation(CC_RUNTIME_CLASS(OpPathNudgeUpFifth),NULL,0);
00463         pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_NUDGEUPPIXEL1);
00464         pOpDesc->AliasOperation(CC_RUNTIME_CLASS(OpPathNudgeUpPixel1),NULL,0);
00465         pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_NUDGEUPPIXEL10);
00466         pOpDesc->AliasOperation(CC_RUNTIME_CLASS(OpPathNudgeUpPixel10),NULL,0);
00467 
00468 
00469         pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_NUDGEDOWN1);  
00470         pOpDesc->AliasOperation(CC_RUNTIME_CLASS(OpPathNudgeDown1),NULL,0);  
00471         pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_NUDGEDOWN5);  
00472         pOpDesc->AliasOperation(CC_RUNTIME_CLASS(OpPathNudgeDown5),NULL,0);  
00473         pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_NUDGEDOWN10);  
00474         pOpDesc->AliasOperation(CC_RUNTIME_CLASS(OpPathNudgeDown10),NULL,0);  
00475         pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_NUDGEDOWNFIFTH);
00476         pOpDesc->AliasOperation(CC_RUNTIME_CLASS(OpPathNudgeDownFifth),NULL,0);
00477         pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_NUDGEDOWNPIXEL1);
00478         pOpDesc->AliasOperation(CC_RUNTIME_CLASS(OpPathNudgeDownPixel1),NULL,0);
00479         pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_NUDGEDOWNPIXEL10);
00480         pOpDesc->AliasOperation(CC_RUNTIME_CLASS(OpPathNudgeDownPixel10),NULL,0);
00481 
00482 
00483         pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_NUDGELEFT1);  
00484         pOpDesc->AliasOperation(CC_RUNTIME_CLASS(OpPathNudgeLeft1),NULL,0);  
00485         pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_NUDGELEFT5);  
00486         pOpDesc->AliasOperation(CC_RUNTIME_CLASS(OpPathNudgeLeft5),NULL,0);  
00487         pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_NUDGELEFT10);  
00488         pOpDesc->AliasOperation(CC_RUNTIME_CLASS(OpPathNudgeLeft10),NULL,0);  
00489         pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_NUDGELEFTFIFTH);
00490         pOpDesc->AliasOperation(CC_RUNTIME_CLASS(OpPathNudgeLeftFifth),NULL,0);
00491         pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_NUDGELEFTPIXEL1);
00492         pOpDesc->AliasOperation(CC_RUNTIME_CLASS(OpPathNudgeLeftPixel1),NULL,0);
00493         pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_NUDGELEFTPIXEL10);
00494         pOpDesc->AliasOperation(CC_RUNTIME_CLASS(OpPathNudgeLeftPixel10),NULL,0);
00495 
00496 
00497         pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_NUDGERIGHT1);  
00498         pOpDesc->AliasOperation(CC_RUNTIME_CLASS(OpPathNudgeRight1),NULL,0);  
00499         pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_NUDGERIGHT5);  
00500         pOpDesc->AliasOperation(CC_RUNTIME_CLASS(OpPathNudgeRight5),NULL,0);  
00501         pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_NUDGERIGHT10);  
00502         pOpDesc->AliasOperation(CC_RUNTIME_CLASS(OpPathNudgeRight10),NULL,0);  
00503         pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_NUDGERIGHTFIFTH);
00504         pOpDesc->AliasOperation(CC_RUNTIME_CLASS(OpPathNudgeRightFifth),NULL,0);
00505         pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_NUDGERIGHTPIXEL1);
00506         pOpDesc->AliasOperation(CC_RUNTIME_CLASS(OpPathNudgeRightPixel1),NULL,0);
00507         pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_NUDGERIGHTPIXEL10);
00508         pOpDesc->AliasOperation(CC_RUNTIME_CLASS(OpPathNudgeRightPixel10),NULL,0);
00509 
00510         // If there is are TextStories or Blends with paths selected then select the paths
00511         // (PS. This is a rather disgusting way of doing things, and one you can't blame on me - MarkN 17-5-99)
00512         // Now its made even more disgusting - since we wan't to do this for bevels, contours and shadows as well! (CGS 18/9/2000)
00513         Range SelRng(*(GetApplication()->FindSelection()));
00514         Node* pNode = SelRng.FindFirst();
00515     
00516         while (pNode != NULL)
00517         {
00518             if (IS_A(pNode, TextStory))
00519             {
00520                 TextStory* pStory = (TextStory*)pNode;
00521                 if (pStory->GetTextPath() != NULL)
00522                     pStory->GetTextPath()->Select(TRUE);
00523             }
00524 
00525             else if (IS_A(pNode, NodeBlend))
00526             {
00527                 NodeBlend* pNodeBlend = (NodeBlend*)pNode;
00528                 UINT32 NBPCounter = 0;
00529                 NodeBlendPath* pNodeBlendPath = pNodeBlend->GetNodeBlendPath(NBPCounter++);
00530                 while (pNodeBlendPath != NULL)
00531                 {
00532                     pNodeBlendPath->Select(TRUE);
00533                     pNodeBlendPath = pNodeBlend->GetNodeBlendPath(NBPCounter++);
00534                 }
00535             }
00536 
00537             else if ((IS_A (pNode, NodeContour)) || (IS_A (pNode, NodeBevel)) || (IS_A (pNode, NodeShadow)))
00538             {
00539                 Node* pControl = pNode->FindParent ();
00540 
00541                 Node* pNode = SliceHelper::FindNextOfClass(pControl, pControl, CC_RUNTIME_CLASS (NodePath));
00542 
00543                 while (pNode)
00544                 {
00545                     ((NodeRenderable*) pNode)->Select (TRUE);
00546                     pNode = (AttrFillGeometry*) SliceHelper::FindNextOfClass(pNode, pControl, CC_RUNTIME_CLASS (NodePath));
00547                 }
00548 
00549                 pNode = SliceHelper::FindNextOfClass(pControl, pControl, CC_RUNTIME_CLASS (NodeRegularShape));
00550 
00551                 while (pNode)
00552                 {
00553                     ((NodeRenderable*) pNode)->Select (TRUE);
00554                     pNode = (AttrFillGeometry*) SliceHelper::FindNextOfClass(pNode, pControl, CC_RUNTIME_CLASS (NodeRegularShape));
00555                 }
00556             }
00557             
00558             pNode = SelRng.FindNext(pNode);
00559         }
00560     }
00561     else
00562     {
00563         // Deselection - destroy the tool's cursor, if there is one.
00564         if (MyCurrentCursor)
00565         {
00566             CursorStack::GPop(CurrentCursorID);
00567             MyCurrentCursor = NULL;
00568             CurrentCursorID = 0;
00569             delete pcNewPathCursor;
00570             delete pcMoveBezCursor;
00571             delete pcReshapeLineCursor;
00572             delete pcClosePathCursor;
00573             delete pcAddPathCursor;
00574         }
00575         
00576         // ensure any tool object blobs are removed.
00577         BlobManager* BlobMgr = GetApplication()->GetBlobManager();
00578         if (BlobMgr != NULL)
00579         {
00580             BlobStyle bsRemoves;
00581             bsRemoves.ToolObject = TRUE;
00582             BlobMgr->RemoveInterest(bsRemoves);
00583         }
00584 
00585         // Clear the floating endpoint condition if it exists
00586         // If camelot is shutting down then dont do it as the operation history has already been
00587         // vaped from the document!
00588         if (FloatingEndpoint && !GetApplication()->CamelotIsDying())
00589             RemoveFloater(&MoveToPoint, MoveToSpread, Document::GetSelected());
00590         
00591         pBezToolInfoBarOp->Delete();
00592     }
00593 }
00594 
00595 /********************************************************************************************
00596 
00597 >   MsgResult BezToolInfoBarOp::Message(Msg* Message) 
00598 
00599     Author:     Jim_Lynn (Xara Group Ltd) <camelotdev@xara.com>
00600     Created:    16/8/94
00601     Inputs:     Message: The message to handle
00602     Outputs:    -
00603     Returns:    -
00604     Purpose:    Bezier tool info bar dialog message handler
00605     Errors:     -
00606     SeeAlso:    -
00607 
00608 ********************************************************************************************/
00609 
00610 MsgResult BezToolInfoBarOp::Message(Msg* Message) 
00611 {
00612     if (IS_OUR_DIALOG_MSG(Message))
00613     {
00614         // Message aimed at out infobar
00615         MsgResult TempResult = BezToolInfoBarOp::InfobarMessage(Message);
00616         if (TempResult != OK)
00617             return TempResult;      
00618     }
00619     else if (MESSAGE_IS_A(Message,OpMsg))   // Check for undo/redo
00620     {
00621         if ( (((OpMsg*)Message)->MsgType == OpMsg::AFTER_UNDO) ||
00622                                         (((OpMsg*)Message)->MsgType == OpMsg::AFTER_REDO) )
00623         {
00624             if (pBezTool != NULL)
00625             {
00626                 pBezTool->SetModeFlag();
00627                 pBezTool->ResetRetroSlider();
00628             }
00629         }
00630     }
00631     else if (MESSAGE_IS_A(Message,SelChangingMsg)) 
00632     {
00633         if ( (((SelChangingMsg*)Message)->State == SelChangingMsg::SELECTIONCHANGED) ||
00634                          (((SelChangingMsg*)Message)->State == SelChangingMsg::NODECHANGED) )
00635         {
00636             if (pBezTool != NULL)
00637             {
00638                 pBezTool->SetModeFlag();
00639                 BezToolInfoBarOp::UpdateState();
00640                 pBezTool->ResetRetroSlider();
00641             }
00642         }
00643     }
00644     else if (MESSAGE_IS_A(Message,UnitMsg))     // Update the bar as someone has changd a unit
00645     {
00646         if (pBezTool != NULL)
00647         {
00648             pBezTool->SetModeFlag();
00649             BezToolInfoBarOp::UpdateState();
00650         }
00651     }
00652     else if (MESSAGE_IS_A(Message,DocChangingMsg)) 
00653     {                                                                           
00654         // Remove the floating endpoint from the old document
00655         DocChangingMsg* msg = (DocChangingMsg*)Message;
00656         if ((msg->State == DocChangingMsg::SELCHANGED) && pBezTool!=NULL && pBezTool->FloatingEndpoint)
00657         {
00658             if ((msg->pOldDoc != NULL) && (msg->pNewDoc != msg->pOldDoc) && !GetApplication()->CamelotIsDying())
00659             {
00660                 // Clear the floating endpoint condition
00661                 pBezTool->DontDrawOnClearMoveTo = TRUE;
00662                 pBezTool->RemoveFloater(&(pBezTool->MoveToPoint), pBezTool->MoveToSpread, msg->pOldDoc);
00663                 pBezTool->DontDrawOnClearMoveTo = FALSE;
00664             }
00665         }
00666     }
00667     else if (MESSAGE_IS_A(Message,DocViewMsg)) 
00668     {                                                                           
00669         DocViewMsg* msg = (DocViewMsg*) Message;
00670         BlobManager* pBlobManager = GetApplication()->GetBlobManager();
00671         ENSURE(pBlobManager, "Can't get BlobManager");
00672 
00673         // Render the floating endpoint off the old view just before it changes
00674         if (msg->State == DocViewMsg::SELABOUTTOCHANGE)
00675         {
00676             if (msg->pNewDocView!=NULL && pBezTool!=NULL)
00677                 pBlobManager->RenderToolBlobsOff(pBezTool, pBezTool->MoveToSpread, NULL);
00678         }
00679 
00680         // Render the floating endpoint onto the new view
00681         if (msg->State == DocViewMsg::SELCHANGED)
00682         {
00683             if (msg->pNewDocView!=NULL && pBezTool!=NULL)
00684                 pBlobManager->RenderToolBlobsOn(pBezTool, pBezTool->MoveToSpread, NULL);
00685         }
00686     }
00687     else if (MESSAGE_IS_A(Message,NewPathCreatedMsg)) 
00688     {
00689         // When this message arrives the OpNewPath is still active so we can insert an
00690         // InsertFloaterAction into its undo record.
00691         DocCoord OldPos;
00692         Spread*  OldpSpread;
00693         Document* OldpDoc;
00694         NewPathCreatedMsg* Msg = (NewPathCreatedMsg*)Message;
00695 
00696         if (pBezTool != NULL)
00697         {
00698             if (pBezTool->GetMoveTo(&OldpSpread, &OldPos, &OldpDoc))
00699             {
00700                 ERROR3IF((OldpDoc != Document::GetCurrent()),"Current doc was not doc with the floating endpoint!");
00701                 if (!InsertFloaterAction::DoInsert(Msg->CurrentOp, Msg->UndoActs, pBezTool, &OldPos, OldpSpread))
00702                 {
00703                     FailAndExecute();
00704                 }
00705             }
00706             pBezTool->ClearMoveTo();
00707         }
00708     }
00709     else if (MESSAGE_IS_A(Message,PathEditedMsg)) 
00710     {
00711         // We can use this message to update the edit fields in the infobar
00712         PathEditedMsg* Msg = (PathEditedMsg*)Message;
00713         CurrentSpread = Msg->EditSpread;
00714         CurrentNodePath = NULL;         // We will rely on updating on the SelChange message at the end of the drag
00715         CurrentInkPath = Msg->EditPath;
00716         CurrentIndex = Msg->EndPoint;
00717         UpdatePositionFields(TRUE, FALSE);
00718         
00719         /*
00720         // DY 27/9/99 if we are editing a blend on a path then update the number of blend steps
00721         NodeBlend* pNodeBlend = GetBlendOnCurve();
00722         if (pNodeBlend != NULL)
00723         {
00724             
00725             if (pNodeBlend->GetEditState() == EDIT_DISTANCE)
00726             {
00727                 UINT32 NumSteps = 0;
00728                 double StepDistance = pNodeBlend->GetStepDistance();
00729                 BOOL Valid = pNodeBlend->GetNumStepsFromDistance(StepDistance, &NumSteps);
00730                 if (Valid)
00731                 {
00732                     OpDescriptor* pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_CHANGEBLENDSTEPS);
00733                     if (pOpDesc != NULL)
00734                     {
00735                         OpParam Param((INT32)NumSteps,0);
00736                         pOpDesc->Invoke(&Param);
00737                     }
00738                             
00739                 }
00740             }
00741             else
00742                 pNodeBlend->UpdateStepDistance();
00743                 
00744         } */
00745 //      IgnoreNextUpdate = TRUE;
00746     }
00747 
00748     // Pass the message on
00749     return (DialogBarOp::Message(Message));
00750 }    
00751 
00752 
00753 
00754 /********************************************************************************************
00755 
00756 >   void BezToolInfoBarOp::UpdateState()
00757 
00758     Author:     Jim_Lynn (Xara Group Ltd) <camelotdev@xara.com>
00759     Created:    16/8/94
00760     Purpose:    Overrides the empty UpdateState function provided by InformationBarOp
00761                 making a call to the function in DialogBarOp.
00762 
00763 *********************************************************************************************/
00764 
00765 void BezToolInfoBarOp::UpdateState()
00766 {
00767     DialogBarOp::UpdateState();
00768     UpdateTextIndicator();  
00769     UpdateLineButton();
00770     UpdateCurveButton();
00771     UpdateReversePathsButton ();
00772     UpdateSmoothButton();
00773     UpdateCuspButton();
00774     UpdatePositionFields();
00775     UpdateStartArrowButton();
00776     UpdateEndArrowButton();
00777 
00778 }
00779 
00780 
00781 
00782 /********************************************************************************************
00783 
00784 >   NodeBlend* BezInfoBarOp::GetBlendOnCurve()
00785 
00786     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
00787     Created:    26/9/99
00788     inputs :    -
00789     outputs:    
00790     returns:    pointer to the first nodeblend in the selection if successful, 
00791                 NULL otherwise
00792     Purpose:    To determine if the current selection is a blend on a curve
00793 
00794     NOTE this function exists for the purposes of getting the editing of paths of
00795     blends on a curve to work.  it should probably be located elsewhere but for the
00796     time being...
00797     
00798 
00799 *********************************************************************************************/
00800 
00801 NodeBlend* BezToolInfoBarOp::GetBlendOnCurve()
00802 {
00803     // first get the selection
00804     SelRange Sel(*( GetApplication()->FindSelection()));
00805     NodeBlend* pNodeBlend = NULL;
00806     NodeBlendPath* pNodeBlendPath = NULL;
00807     // get the node blend
00808     if (!Sel.IsEmpty())
00809     {
00810         Node* pNode = Sel.FindFirst();
00811         while (pNode != NULL)
00812         {
00813             if (pNode->IS_KIND_OF(NodeBlendPath))
00814             {
00815                 pNodeBlendPath = (NodeBlendPath*)pNode;
00816                 break;
00817             }
00818             pNode = Sel.FindNext(pNode);
00819         }
00820     }
00821 
00822     if (pNodeBlendPath == NULL)  // if no blend node then there won't be a blender
00823         return NULL;
00824     else
00825         pNodeBlend = (NodeBlend*)pNodeBlendPath->FindParent();
00826     
00827     ERROR2IF(!(pNodeBlend->IS_KIND_OF(NodeBlend)), FALSE, "Node is a not a NodeBlend");
00828     return pNodeBlend;
00829 
00830 }
00831 
00832 
00833 
00834 
00835 
00836 
00837 
00838 
00839 /********************************************************************************************
00840 
00841 >   void BezierTool::OnClick( DocCoord PointerPos, ClickType Click, 
00842                                 ClickModifiers ClickMods, Spread *pSpread )
00843 
00844     Author:     Jim_Lynn (Xara Group Ltd) <camelotdev@xara.com> (via Rik)
00845     Created:    21/4/94
00846     Inputs:     PointerPos - The Coords (in spread coords) of the point where the mouse 
00847                 button was clicked
00848                 Click - Describes the type of click that was detected. 
00849                 ClickMods - Indicates which buttons caused the click and which modifers were
00850                 pressed at the same time
00851                 pSpread - the spread in which the click happened
00852     Returns:    TRUE if it handled the Click, FALSE otherwise
00853     Purpose:    To handle a Mouse Click event for the Bezier Tool. It starts up a Bezier 
00854                 Operation.
00855     SeeAlso:    Tool::MouseClick; ClickType; ClickModifiers
00856 
00857 ********************************************************************************************/
00858 
00859 void BezierTool::OnClick( DocCoord PointerPos, ClickType Click, ClickModifiers ClickMods,
00860                         Spread* pSpread )
00861 {
00862     if (ClickMods.Menu) return;                         // Don't do anything if the user clicked the Menu button
00863 
00864     // Have a flag telling me what we have to do (either click on an existing point, 
00865     // create a new point on a path, create an all new path or reshape an existing line)
00866     // clickeffect is defined in the class header.
00867 
00868     INT32 PathPosition;     // index into the path of interesting elements
00869     NodeRenderableInk* WhichNode = NULL;
00870     INT32   NumPts;
00871     INT32   NumPaths;
00872 
00873     // Set the infobars spread pointer so if any kernel op wants to force edit field values
00874     // there is a spread to get units from.
00875     pBezToolInfoBarOp->CurrentSpread = pSpread;
00876 
00877     clickeffect WhatToDo = DetermineClickEffect(PointerPos, pSpread,&WhichNode, &PathPosition, &NumPaths, &NumPts);
00878 
00879     // So, like, WhatToDo tells me what to do, WhichNode points at the node that's involved
00880     // and PathPosition is the index into WhichNode of the element we are dealing with.
00881     // if WhatToDo = OnPoint we have to pass the click onto the path
00882     // if WhatToDo = AddSegment we have to add a curve or line segment to the given path.
00883     // we should also remember to do things with the selected points.
00884     // if WhatToDo = NewPath there wasn't a suitable selected path, so we have to start a new
00885     // path. A stupid person (i.e. me) might create a new path, but I think this could be done
00886     // better. When they click in this mode, I'll start an operation which drags out a single
00887     // line/curve element, but if they release immediately I won't insert anything - I'll set
00888     // the FloatingEndpoint flag and set the MoveToPoint coords to that point.
00889 
00890     // Take note of the else ifs - this should help to ensure that only one event is ever triggered
00891     // by an incoming click event. Care should be taken in ordering these clauses so that the correct 
00892     // results ensue from each click.
00893 
00894     // Special case: WhatToDo = ClosePath and the Adjust button is used. Instead of closing the path
00895     // we should select the point. Thus, we change it to OnPoint
00896 
00897     if ( WhatToDo == ClosePath && ClickMods.Adjust)
00898         WhatToDo = OnPoint;
00899     
00900     // Ordinary click from OnPoint
00901     if ( Click == CLICKTYPE_SINGLE && WhatToDo == OnPoint )
00902     {
00903         WhichNode->OnClick(PointerPos, Click, ClickMods, pSpread);
00904 
00905         // Need to clear any exiting floating endpoint condition
00906         if (FloatingEndpoint)
00907             RemoveFloater(&MoveToPoint, MoveToSpread, NULL);
00908     }
00909 
00910     // If the click is AddSegment, I should add a segment to the end of the given path (WhichNode,
00911     // returned by DetermineClickEffect(). Unlike the clicking on existing nodes, it is the tool's
00912     // job to initiate this operation
00913 
00914     else if (Click == CLICKTYPE_SINGLE && WhatToDo == AddSegment && !ClickMods.Adjust)
00915     {
00916         OpNodePathAddEndpoint* pOpAddEnd = new OpNodePathAddEndpoint;
00917         if (!pOpAddEnd)
00918             InformError( _R(IDS_OUT_OF_MEMORY), _R(IDS_OK) );
00919         else
00920             pOpAddEnd->DoStartDragEdit((NodePath*) WhichNode, PointerPos, pSpread, ClickMods, PathPosition, CreateCurve, FALSE, !CreateCusp);
00921     }
00922 
00923     else if (Click == CLICKTYPE_SINGLE && (WhatToDo == NewPath || (WhatToDo == AddSegment && ClickMods.Adjust)))
00924     {
00925         if (FloatingEndpoint)
00926         {   
00927             if (ClickMods.Adjust)
00928             {
00929                 // Clear the floating endpoint condition
00930                 RemoveFloater(&MoveToPoint, MoveToSpread, NULL);
00931             }
00932             else
00933             {
00934                 // Handle adding of new path
00935                 OpNewPath* pOpNew = new OpNewPath;
00936                 if (!pOpNew)
00937                     InformError( _R(IDS_OUT_OF_MEMORY), _R(IDS_OK));
00938                 else
00939                 {
00940                     pOpNew->DoStartDragEdit(MoveToPoint, PointerPos, MoveToSpread, ClickMods, CreateCurve, !CreateCusp);
00941 
00942                         
00943                 
00944                 }
00945             }
00946         }
00947         else
00948         {
00949             if (!OpSelectPathPoints::DragInProgress())
00950             {
00951                 // Start a drag operation to marquee select points
00952                 OpSelectPathPoints* pOpSelect = new OpSelectPathPoints;
00953                 if (!pOpSelect)
00954                 {
00955                     InformError( _R(IDS_OUT_OF_MEMORY), _R(IDS_OK) );
00956                 }
00957                 else
00958                 {
00959                     // Start the drag, giving a pointer back to this tool so that if
00960                     // the drag was not really a drag, we can insert a floater.
00961                     pOpSelect->DoDrag( PointerPos, pSpread, ClickMods.Adjust , this, ClickMods);
00962                 }       
00963             } 
00964         }
00965     }
00966 
00967     else if ( Click == CLICKTYPE_DOUBLE && WhatToDo == OnPoint && !ClickMods.Adjust)
00968     {
00969         WhichNode->OnClick(PointerPos, Click, ClickMods, pSpread);
00970 //      DialogBarOp::SetSystemStateChanged();       // Inform the system that greying needs updating
00971     }
00972 
00973     else if ( Click == CLICKTYPE_SINGLE && WhatToDo == ClosePath)
00974     {
00975         // Create the same operation as for adding an endpoint, but call it using
00976         // the flag that tells the operation to close the path.
00977 
00978         OpNodePathAddEndpoint* pOpAddEnd = new OpNodePathAddEndpoint;
00979         if (!pOpAddEnd)
00980         {
00981             InformError( _R(IDS_OUT_OF_MEMORY), _R(IDS_OK) );
00982         }
00983         else
00984         {
00985             // Start dragging the path. The TRUE flag at the end tells the operation 
00986             // to close the path
00987             // I have to flip the PathPosition index from the clicked-on point to the opposite
00988             // point so that the right segment is inserted
00989             ((NodePath*)WhichNode)->InkPath.SetPathPosition(PathPosition);
00990             ((NodePath*)WhichNode)->InkPath.FindStartOfSubPath();
00991             if (PathPosition == ((NodePath*)WhichNode)->InkPath.GetPathPosition())
00992             {
00993                 ((NodePath*)WhichNode)->InkPath.FindEndOfSubPath();
00994                 PathPosition = ((NodePath*)WhichNode)->InkPath.GetPathPosition();
00995             }
00996             else
00997                 PathPosition = ((NodePath*)WhichNode)->InkPath.GetPathPosition();
00998 
00999             pOpAddEnd->DoStartDragEdit((NodePath*) WhichNode, PointerPos, pSpread, ClickMods, PathPosition, CreateCurve, TRUE, !CreateCusp);
01000         }
01001     }
01002     else if (Click == CLICKTYPE_SINGLE && WhatToDo == ReshapeLine)
01003     {
01004         // Create the same operation as for adding an endpoint, but call it using
01005         // the flag that tells the operation to close the path.
01006 
01007         double pdist;
01008         INT32 tempel;
01009         ((NodePath*) WhichNode)->InkPath.SqrDistanceToPoint(PointerPos, &tempel, &pdist);
01010 
01011         OpReshapeOrAddPoint* pOpReshape = new OpReshapeOrAddPoint;
01012         if (!pOpReshape)
01013             InformError( _R(IDS_OUT_OF_MEMORY), _R(IDS_OK) );
01014         else
01015             pOpReshape->DoStartDragEdit((NodePath*) WhichNode, PointerPos, pSpread, PathPosition, pdist);
01016         
01017     }
01018 /*
01019     else if (Click == CLICKTYPE_DRAG && WhatToDo == NewPath && FloatingEndpoint && pSpread == MoveToSpread)
01020     {
01021         OpNodePathStartNew* pOpStartNew = new OpNodePathStartNew;
01022         if (!pOpStartNew)
01023         {
01024             if (IsUserName("Jim"))
01025             {
01026                 TRACE( _T("Unable to create operation to start new path in Beztool\n"));
01027             }
01028             
01029             // Inform the user that we are out of memory
01030             InformError( _R(IDS_OUT_OF_MEMORY), _R(IDS_OK) );
01031         }
01032         else
01033         {
01034             // Start dragging the path
01035             pOpStartNew->DoStartDragEdit( PointerPos, pSpread );
01036         }
01037     }
01038 */
01039 
01040     else if ( Click == CLICKTYPE_DRAG && WhatToDo == OnPoint && (WhichNode != NULL) )
01041     {
01042         WhichNode->OnClick(PointerPos, Click, ClickMods, pSpread);
01043 //      ((NodeRenderableInk*)LastPathClicked)->OnClick(LastPointClicked,Click, ClickMods, LastSpreadClicked);
01044 //      DialogBarOp::SetSystemStateChanged();       // Inform the system that greying needs updating
01045     }
01046 
01047     // Now update the cursor and status bar
01048     OnMouseMove(PointerPos, pSpread, ClickMods);
01049     SetModeFlag();
01050     ResetRetroSlider();
01051 }
01052 
01053 
01054 /********************************************************************************************
01055 
01056 >   BOOL BezierTool::OnKeyPress(KeyPress* pKeyPress)
01057 
01058     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01059     Created:    13/10/94
01060     Inputs:     pKeyPress - pointer to a keypress object
01061     Returns:    TRUE if it handled the keypress, FALSE otherwise
01062     Purpose:    To handle keypress events for the Bezier Tool. If it is a keypress that it
01063                 know about it starts up an appropiate operation.
01064                 THIS IS BADLY WRONG AT THE MOMENT - WHEN THERE IS THE TECHNOLOGY FOR USER
01065                 DEFINABLE KEY-SHORTCUTS THIS FUNCTION WILL NEED FIXING  
01066 
01067 ********************************************************************************************/
01068 
01069 BOOL BezierTool::OnKeyPress(KeyPress* pKeyPress)
01070 {
01071     // We don't want to know about key release or character events
01072     if (!pKeyPress->IsPress())
01073         return FALSE;
01074 
01075     // Comparing keypresses is exceeding greif!
01076 
01077     if (*pKeyPress == KeyPress(CAMKEY(TAB)))
01078     {
01079         CyclePathPoints(TRUE);
01080         goto UsedKey;
01081     } 
01082 
01083     if (*pKeyPress == KeyPress(CAMKEY(TAB), TRUE))
01084     {
01085         CyclePathPoints(FALSE);
01086         goto UsedKey;
01087     } 
01088 
01089     if (*pKeyPress == KeyPress(CAMKEY(RETURN)))
01090     {
01091         if (!AutoClosePaths())
01092             InformError();
01093         goto UsedKey;
01094     } 
01095 
01096     if ((pKeyPress->GetVirtKey() == CAMKEY(HOME)) && !pKeyPress->IsAdjust() &&
01097                                             !pKeyPress->IsConstrain() && !pKeyPress->IsAlternative())
01098     {
01099         HomePathPoints();
01100         goto UsedKey;
01101     } 
01102 
01103     if ((pKeyPress->GetVirtKey() == CAMKEY(END)) && !pKeyPress->IsAdjust() &&
01104                                             !pKeyPress->IsConstrain() && !pKeyPress->IsAlternative())
01105     {
01106         EndPathPoints();
01107         goto UsedKey;
01108     } 
01109 
01110     if (*pKeyPress == KeyPress(CAMKEY(Z)))
01111     {
01112         pBezToolInfoBarOp->HandleClickOnCuspButton(FALSE);
01113         goto UsedKey;
01114     } 
01115 
01116     if (*pKeyPress == KeyPress(CAMKEY(S)))
01117     {
01118         pBezToolInfoBarOp->HandleClickOnSmoothButton();
01119         goto UsedKey;
01120     } 
01121 
01122     if (*pKeyPress == KeyPress(CAMKEY(L)))
01123     {
01124         pBezToolInfoBarOp->HandleClickOnLineButton();
01125         goto UsedKey;
01126     } 
01127 
01128     if (*pKeyPress == KeyPress(CAMKEY(C)))
01129     {
01130         pBezToolInfoBarOp->HandleClickOnCurveButton();
01131         goto UsedKey;
01132     } 
01133 
01134     if (*pKeyPress == KeyPress(CAMKEY(B)))
01135     {
01136         OpDescriptor* pOpDesc = OpDescriptor::FindOpDescriptor(CC_RUNTIME_CLASS(OpBreakAtPoints));
01137         String_256      UIDesc;
01138         if (pOpDesc != NULL)
01139         {
01140             OpState State = OpBreakAtPoints::GetState(&UIDesc, pOpDesc);
01141             if (!State.Greyed)
01142             {
01143                 pOpDesc->Invoke();
01144                 goto UsedKey;
01145             }
01146         }
01147     } 
01148 
01149     if ((pKeyPress->GetVirtKey() == CAMKEY(DELETE)) || (*pKeyPress == KeyPress(WXK_BACK)) ) 
01150     {
01151         // Run a delete selected points operation
01152         String_256      UIDesc;
01153         OpState State = OpDeletePoints::GetState(&UIDesc, NULL);
01154         OpDescriptor* pOpDesc = OpDescriptor::FindOpDescriptor(CC_RUNTIME_CLASS(OpDeletePoints));
01155         if ((!State.Greyed) && (pOpDesc != NULL))
01156         {
01157             pOpDesc->Invoke();
01158             goto UsedKey;
01159         }
01160     } 
01161 
01162     // If ESCAPE is pressed then clear the floating endpoint, but don't claim the keypress
01163     // so the selection is also cleared.
01164     if ((*pKeyPress == KeyPress(CAMKEY(ESCAPE))) && FloatingEndpoint)
01165         RemoveFloater(&MoveToPoint, MoveToSpread, Document::GetSelected());
01166 
01167     // If we get this far then the keypress wasn't handled
01168     return FALSE;
01169 
01170 UsedKey:
01171     pBezToolInfoBarOp->IgnoreNextUpdate = FALSE;
01172     return TRUE;
01173 }
01174 
01175 
01176 
01177 /**************************************************************************************************************
01178 
01179 > clickeffect BezierTool::DetermineClickEffect(DocCoord PointerPos, Spread* pSpread,
01180                                             NodeRenderableInk** ReturnNode, INT32* ReturnPosition,
01181                                             INT32* NumSelectedPaths, INT32* NumSelectedPoints )
01182 
01183     Author:     Jim_Lynn (Xara Group Ltd) <camelotdev@xara.com>
01184     Created:    29/4/94
01185     Inputs:     PointerPos is the mouse position
01186                 pSpread is a pointer to the spread containing the mouse position
01187     Outputs:    ReturnNode returns a pointer to the node the mouse would click on
01188                 ReturnPosition returns the index into the nodepath of the affected point
01189                 NumSelectedPaths is the number of paths in the selection
01190                 NumSelectedPoints is the total number of selected points (on all paths)
01191     Returns:    The effect of clicking - one of NewPath, AddSegment, OnPoint or ReshapeLine
01192     Purpose:    Used when the cursor moves and when single clicking. This routine determines
01193                 what effect a click will have. In this tool, clicking will either select a point
01194                 on an already selected line, reshape a line segment (a la Corel), add a segment to
01195                 the end of a line, or start a new path entirely.
01196 
01197 ***************************************************************************************************************/
01198 
01199 clickeffect BezierTool::DetermineClickEffect(DocCoord PointerPos, Spread* pSpread,
01200                                             NodeRenderableInk** ReturnNode, INT32* ReturnPosition,
01201                                             INT32* NumSelectedPaths, INT32* NumSelectedPoints )
01202 {
01203     clickeffect WhatToDo = NewPath; // tells me what effect the click would have
01204     INT32 PathPosition = 0;         // temp var for return value
01205     INT32 TempIndex = 0;                // Temporary index into a path
01206     INT32 NumSelPaths = 0;          // Number of selected paths
01207     double Distance = 0;            // gets the returned distance from the nearest point
01208     double Nearest = 0;             // distance of current nearest point
01209     NodeRenderableInk* WhichNode = NULL;    // Saves me using a pointer to a pointer
01210 
01211     // Scan through the selected paths, and see if any of them want the click
01212 
01213     DocRect BlobRect;
01214     DocView* pDocView = DocView::GetCurrent();
01215     ERROR2IF( pDocView==NULL, NewPath, "BezierTool::DetermineClickEffect: Can't find current DocView");
01216 
01217     // Find the selected range of objects
01218     SelRange* Selected = GetApplication()->FindSelection();
01219     Node* pNode = Selected->FindFirst();
01220 
01221     INT32 NumSelectedEndpoints = 0;     // Count selected points in paths
01222 
01223     if (pNode != NULL)
01224     {
01225         Spread* NodeSpread = pNode->FindParentSpread();
01226         if (NodeSpread == pSpread)
01227         {
01228             // On the same spread, so see if the pointer is over an endpoint
01229             while ((pNode != NULL) && WhatToDo!=OnPoint)
01230             {
01231                 NodePath* pActNode = FindEditablePath(pNode);
01232                 if (pActNode)
01233                 {
01234 
01235                     // Now we know it's a NodePath, get a pointer to the Path object within it, so
01236                     // we can find any endpoints
01237                 
01238                     Path* ThisPath = &((pActNode)->InkPath);
01239 
01240                     // Increment the number of selected paths
01241                     NumSelPaths++;
01242                     
01243                     // count the number of selected points on the path (excluding control points)
01244                     INT32 NumCoords = ThisPath->GetNumCoords();
01245                     PathFlags* Flags = ThisPath->GetFlagArray();
01246 
01247                     for (INT32 i=0; i<NumCoords; i++)
01248                     {
01249                         if (Flags[i].IsSelected && Flags[i].IsEndPoint)
01250                             NumSelectedEndpoints++;
01251                     }
01252 
01253                     if (ThisPath->FindStartOfPath())
01254                     {
01255                         // First, check to see if this click occurs on a selected point
01256                         INT32 tempPos;
01257                         if (ThisPath->FindNearestPoint(PointerPos,
01258                                                        POINTFLAG_ENDPOINTS |
01259                                                        POINTFLAG_CONTROLPOINTS |
01260                                                        POINTFLAG_ENDSFIRST,
01261                                                        &tempPos)
01262                            )
01263                         {
01264                             // The click occurred on one of the points on the line
01265                             // so remember this path, the position, and the fact that
01266                             // the user clicked on a point
01267 
01268                             WhatToDo = OnPoint;
01269                             WhichNode = (NodeRenderableInk*)pActNode;
01270                             PathPosition = tempPos;
01271                         }
01272                         else if (WhatToDo != OnPoint && ThisPath->PointCloseToLine(PointerPos, &tempPos))
01273                         {
01274                             WhatToDo = ReshapeLine;
01275                             WhichNode = (NodeRenderableInk*)pActNode;
01276                             PathPosition = tempPos;
01277                         }
01278                         else if ((WhatToDo == AddSegment || WhatToDo == NewPath) && ThisPath->ClosestSelectedEndpoint(PointerPos,&TempIndex,&Distance))
01279                         {
01280                             // TempIndex is the index into the path for the closest selected endpoint
01281                             // Distance is the distance
01282                             if (WhatToDo == NewPath)
01283                             {
01284                                 WhatToDo = AddSegment;
01285                                 WhichNode = (NodeRenderableInk*)pActNode;
01286                                 Nearest = Distance;
01287                                 PathPosition = TempIndex;
01288                             }
01289                             else if (Nearest > Distance)
01290                             {
01291                                 Nearest = Distance;
01292                                 WhichNode = (NodeRenderableInk*)pActNode;
01293                                 PathPosition = TempIndex;
01294                             }
01295                         }
01296                     }
01297                 }
01298                 // Now find the next selected node
01299                 pNode = Selected->FindNext(pNode);
01300             }
01301         }
01302     }
01303 
01304     // WhatToDo tells us what the action will be
01305     // WhichNode points to the node we are dealing with
01306     // PathPosition is the index into that path of the element we are using
01307 
01308     // If WhatToDo == OnPoint and it's the end of a subpath, and the opposite
01309     // end is selected, and the path isn't closed we should change WhatToDo 
01310     // to be ClosePath
01311 
01312     if (WhatToDo == OnPoint && NumSelectedEndpoints == 1)
01313     {
01314         Path* ThisPath = &(((NodePath*)WhichNode)->InkPath);
01315         PathFlags* Flags = ThisPath->GetFlagArray();
01316         PathVerb* Verbs = ThisPath->GetVerbArray();
01317 //      DocCoord* Coords = ThisPath->GetCoordArray();
01318         INT32 NumCoords = ThisPath->GetNumCoords();
01319         if (Verbs[PathPosition] == PT_MOVETO)           // Start of subpath
01320         {
01321             INT32 i = PathPosition;
01322             ThisPath->FindEndElOfSubPath(&i);               // i = index to end element
01323             
01324             if ((Flags[i].IsSelected) && !(Verbs[i] & PT_CLOSEFIGURE))
01325                 WhatToDo = ClosePath;
01326         }
01327         else if (PathPosition+1 == NumCoords || Verbs[PathPosition+1] == PT_MOVETO)
01328         {
01329             if (!(Verbs[PathPosition] & PT_CLOSEFIGURE))
01330             {
01331                 INT32 i = PathPosition;
01332                 ThisPath->FindStartOfSubPath(&i);
01333                 if (Flags[i].IsSelected)
01334                     WhatToDo = ClosePath;
01335             }
01336         }
01337         // Now we must detect the case of an attempted closepath on a path consisting
01338         // of a moveto and one segment
01339         if ((WhatToDo == ClosePath) && ( (((NodePath*)WhichNode)->InkPath.GetNumCoords() == 2) ||
01340             ((((NodePath*)WhichNode)->InkPath.GetNumCoords() == 4) && (Verbs[3] == PT_BEZIERTO)) ) )
01341         {
01342             WhatToDo = OnPoint;
01343         }
01344     }
01345 
01346 
01347     if ((WhatToDo == AddSegment || WhatToDo == ClosePath) && NumSelectedEndpoints > 1)
01348         WhatToDo = NewPath;
01349 
01350     if (WhatToDo == AddSegment || WhatToDo == ReshapeLine)
01351     {
01352         // If we're pointing at a curve, make sure we're pointing to the first element
01353         Path* ThisPath = &(((NodePath*)WhichNode)->InkPath);
01354         PathFlags* flags = ThisPath->GetFlagArray();
01355         while(!(flags[PathPosition].IsEndPoint ))
01356             PathPosition++;
01357         ThisPath->SetPathPosition(PathPosition);
01358         if (ThisPath->GetVerb() == PT_BEZIERTO)
01359             PathPosition-=2;
01360     }
01361 
01362     *ReturnPosition = PathPosition;
01363     *ReturnNode = WhichNode;
01364     *NumSelectedPoints = NumSelectedEndpoints;
01365     *NumSelectedPaths = NumSelPaths;
01366     return (WhatToDo);
01367 }
01368 
01369 
01370 
01371 /********************************************************************************************
01372 
01373 >   NodePath* BezierTool::FindEditablePath(Node* pSelected)
01374 
01375     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01376     Created:    19/3/95
01377     Inputs:     pSelected = A pointer to the selected node to check
01378     Outputs:    -
01379     Returns:    A pointer to an editable node path (or derived) object
01380                 NULL if there isn't one.
01381     Purpose:    Finds the node to edit (if it exists). This node could actually be the
01382                 selected node passed as a parameter or one of its children which the
01383                 node wants to be editable.
01384 
01385 ********************************************************************************************/
01386 
01387 NodePath* BezierTool::FindEditablePath(Node* pSelected)
01388 {
01389     if (pSelected->IsNodePath())
01390         return ((NodePath*)pSelected);
01391 
01392     return ((NodePath*)pSelected->HasEditableChild(CC_RUNTIME_CLASS(NodePath), NULL));
01393 }
01394 
01395 
01396 
01397 
01398 /********************************************************************************************
01399 
01400 >   void BezierTool::OnMouseMove(DocCoord coord, Spread* pSpread, ClickModifiers mods)
01401 
01402     Author:     Jim_Lynn (Xara Group Ltd) <camelotdev@xara.com>
01403     Created:    15/3/94
01404     Inputs:     coordinate of mouse move, pointer to spread containing coord, mouse modifiers
01405     Outputs:    -
01406     Returns:    -
01407     Purpose:    This routine is called whenever the mouse moves while we're in the freehand 
01408                 tool. it sees what is under the pointer, and flips the cursor if clicking 
01409                 will have a different effect. The rules are:
01410 
01411                 Over space, no selection        - freehand cursor
01412                 Over selected endpoint          - add freehand to line
01413 
01414     Errors:     -
01415     SeeAlso:    -
01416 
01417 ********************************************************************************************/
01418 
01419 void BezierTool::OnMouseMove(DocCoord coord, Spread* pSpread, ClickModifiers mods)
01420 {
01421     // Call DetermineClickEffect to see what a click will do at this position
01422 
01423     INT32 PathPosition;         // Needed to receive a return, not used otherwise
01424     NodeRenderableInk* node;    // again, only temporary
01425     INT32 NumPts;               // Number of selected points
01426     INT32 NumPaths;             // Number of selected paths
01427     clickeffect WhatToDo = DetermineClickEffect(coord, pSpread, &node, &PathPosition, &NumPaths, &NumPts);
01428     
01429     // Generate the status line help string
01430     String_256 status("");
01431     GenerateStatusLineText(&status, pSpread, coord, mods);
01432     GetApplication()->UpdateStatusBarText(&status);
01433 
01434     // Now change the cursor
01435     switch (WhatToDo)
01436     {
01437         case AddSegment:
01438             ChangeCursor(pcAddPathCursor);
01439             break;
01440         case NewPath:
01441             ChangeCursor(pcNewPathCursor);
01442             break;
01443         case OnPoint:
01444             ChangeCursor(pcMoveBezCursor);
01445             break;
01446         case ReshapeLine:
01447             ChangeCursor(pcReshapeLineCursor);
01448             break;
01449         case ClosePath:
01450             ChangeCursor(pcClosePathCursor);
01451             break;
01452     }
01453 }
01454 
01455 
01456 /********************************************************************************************
01457 
01458 >   void BezierTool::ChangeCursor(Cursor* cursor)
01459 
01460     Author:     Jim_Lynn (Xara Group Ltd) <camelotdev@xara.com>
01461     Created:    16/3/94
01462     Inputs:     ID of the cursor you want to flip to
01463     Outputs:    -
01464     Returns:    -
01465     Purpose:    Changes to the specified cursor. Will only change the cursor if it isn't already
01466                 this cursor, so it doesn't flicker.
01467     Errors:     can fail if the cursor cannot be created - the cursor code will fail.
01468     SeeAlso:    -
01469 
01470 ********************************************************************************************/
01471 
01472 void BezierTool::ChangeCursor(Cursor* cursor)
01473 {
01474     // only change if this cursor is different from the current cursor
01475     if (cursor != MyCurrentCursor)
01476     {
01477         // set this cursor as the current cursor and immediately display it
01478         CursorStack::GSetTop(cursor, CurrentCursorID);
01479         // remember this is our current cursor
01480         MyCurrentCursor = cursor;
01481     }
01482 
01483 }
01484 
01485 /********************************************************************************************
01486 
01487 >   void BezierTool::SetMoveTo(DocCoord MovePos, Spread* pSpread, Document* pDoc)
01488 
01489     Author:     Jim_Lynn (Xara Group Ltd) <camelotdev@xara.com>
01490     Created:    18/7/94
01491     Inputs:     MovePos is the coordinate of the virtual moveto
01492                 pSpread points at the spread containing the coordinate
01493                 pDoc pointer to the document that contains the endpoint
01494     Outputs:    -
01495     Returns:    -
01496     Purpose:    This function is called from an operation which tells the tool that there
01497                 should be a virtual moveTo coordinate at the given point. This virtual coordinate
01498                 looks much like the old single moveto used to in ArtWorks, with the added advantage
01499                 that it doesn't really exist, so it can't cock up the tree like they used
01500                 to do in ArtWorks.
01501     Errors:     -
01502     SeeAlso:    BezierTool::ClearMoveTo
01503 
01504 ********************************************************************************************/
01505 
01506 void BezierTool::SetMoveTo(DocCoord MovePos, Spread* pSpread, Document* pDoc)
01507 {
01508     // Set the member variables of the tool
01509     MoveToDoc = pDoc;
01510     MoveToPoint = MovePos;
01511     MoveToSpread = pSpread;
01512     FloatingEndpoint = TRUE;
01513 
01514     // Now display the blob
01515     if (IsCurrent())
01516     {
01517         BlobManager* BlobMgr = GetApplication()->GetBlobManager();
01518         if (BlobMgr) BlobMgr->RenderToolBlobsOn(this, pSpread, NULL);
01519     }
01520 }
01521 
01522 
01523 
01524 /********************************************************************************************
01525 
01526 >   void BezierTool::ClearMoveTo()
01527 
01528     Author:     Jim_Lynn (Xara Group Ltd) <camelotdev@xara.com>
01529     Created:    19/7/94
01530     Inputs:     -
01531     Outputs:    -
01532     Returns:    -
01533     Purpose:    Clears the floating endpoint condition.
01534     Errors:     -
01535     SeeAlso:    BezierTool::SetMoveTo
01536 
01537 ********************************************************************************************/
01538 
01539 void BezierTool::ClearMoveTo()
01540 {
01541     if (FloatingEndpoint && IsCurrent() && !DontDrawOnClearMoveTo)
01542     {
01543         BlobManager* pBlobManager = GetApplication()->GetBlobManager();
01544         ENSURE(pBlobManager, "Can't get BlobManager");
01545         pBlobManager->RenderToolBlobsOff(this, MoveToSpread,NULL);
01546     }
01547     FloatingEndpoint = FALSE;
01548 }
01549 
01550 
01551 
01552 /********************************************************************************************
01553 
01554 >   BOOL BezierTool::GetMoveTo(Spread** ppSpread, DocCoord* pCoord)
01555 
01556     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01557     Created:    05/12/94
01558     Inputs:     ppSpread - pointer to a pointer to a spread
01559                 pCoord - pointer to a DocCoord
01560     Outputs:    *ppSpread is set to point to the spread the floating endpoint is on
01561                 *pCoord is set to the location of the floating endpoint
01562     Returns:    TRUE if there was a floating endpoint, FALSE if not (in this case there is 
01563                 no outputs)
01564     Purpose:    Read the state of the floating endpoint
01565     Errors:     -
01566     SeeAlso:    BezierTool::SetMoveTo, BezierTool::ClearMoveTo
01567 
01568 ********************************************************************************************/
01569 
01570 BOOL BezierTool::GetMoveTo(Spread** ppSpread, DocCoord* pCoord, Document** ppDoc)
01571 {
01572     if (FloatingEndpoint)
01573     {
01574         *ppSpread = MoveToSpread;
01575         *pCoord = MoveToPoint;
01576         *ppDoc = MoveToDoc;
01577         return TRUE;
01578     }
01579     else
01580         return FALSE;
01581 }
01582 
01583 
01584 
01585 /********************************************************************************************
01586 
01587 >   void BezierTool::RenderToolBlobs(Spread* pSpread, DocRect* pClipRect)
01588 
01589     Author:     Jim_Lynn (Xara Group Ltd) <camelotdev@xara.com>
01590     Created:    18/7/94
01591     Inputs:     pSpread - The spread that the blob is to appear on
01592                 pClipRect - Pointer to the rect that contains the blobs
01593     Purpose:    Renders the Tools Blobs. The only blob this tool renders is the floating endpoint
01594                 which only appears the the flag is set.
01595 
01596 ********************************************************************************************/
01597 
01598 void BezierTool::RenderToolBlobs(Spread* pSpread, DocRect* pClipRect)
01599 {
01600     // Can only draw the path if there is a path to draw
01601     if (FloatingEndpoint)
01602     {
01603         RenderRegion* pRegion = DocView::RenderOnTop(pClipRect, pSpread, ClippedEOR);
01604         while (pRegion)
01605         {
01606             // Draw a Cross Hair
01607             pRegion->SetLineColour(COLOUR_BEZIERBLOB);
01608             pRegion->SetFillColour(COLOUR_TRANS);
01609             pRegion->DrawBlob(MoveToPoint,BT_SELECTED);
01610 
01611             // Get the next region in the list
01612             pRegion = DocView::GetNextOnTop(pClipRect);
01613         }
01614     }   
01615 }
01616 
01617 
01618 
01619 /********************************************************************************************
01620 
01621 >   void BezierTool::SetModeFlag()
01622 
01623     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01624     Created:    26/9/94
01625     Inputs:     -
01626     Outputs:    CurrentToolMode is set the the current tool mode
01627     Purpose:    Sets the current tool mode (new/add/change) and gets the infobar to redraw
01628                 the text item that shows it.
01629                 We are in New mode if there are no selected lines.  We are in Add mode if there
01630                 is just one endpoint on the end od a path selected.  Otherwise we are in Change mode.
01631 
01632 ********************************************************************************************/
01633 
01634 void BezierTool::SetModeFlag()
01635 {
01636     SelRange*   Selected = GetApplication()->FindSelection();
01637     Node*   pNode = Selected->FindFirst();
01638 //  CurrentMode OldMode = CurrentToolMode;
01639     CurrentToolMode = New;
01640 
01641     while ((pNode != NULL) && (CurrentToolMode != Change))
01642     {
01643         NodePath* pSelected = FindEditablePath(pNode);
01644         
01645         if (pSelected)
01646         {
01647             Path* ThisPath = &(pSelected->InkPath);
01648             PathFlags* Flags = ThisPath->GetFlagArray();
01649             INT32 UsedSlots = ThisPath->GetNumCoords();
01650 
01651             // Go through the points in this path summing the selected endpoints
01652             for (INT32 i=0; i<UsedSlots; i++)
01653             {   
01654                 if (Flags[i].IsSelected && Flags[i].IsEndPoint)
01655                 {
01656                     if ( (i!=0) && (i!=(UsedSlots-1)) )
01657                     {
01658                         CurrentToolMode = Change;
01659                         break;
01660                     }
01661                     else
01662                     {
01663                         if (CurrentToolMode == Add)
01664                         {
01665                             CurrentToolMode = Change;
01666                             break;
01667                         }
01668                         else
01669                             CurrentToolMode = Add;
01670                     }
01671                 }
01672             }
01673         }
01674         pNode = Selected->FindNext(pNode);
01675     }
01676     
01677     DialogBarOp::SetSystemStateChanged();
01678 }
01679 
01680 
01681 
01682 
01683 /********************************************************************************************
01684 
01685 >   INT32 BezToolInfoBarOp::GetCurrentIndex()
01686 
01687     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01688     Created:    07/11/94
01689     Inputs:     -
01690     Returns:    The index of the Endpoint currently being displayed in the infobar.
01691     Purpose:    To enable other classes to see which endpoint is currently selected for display.
01692 
01693 ********************************************************************************************/
01694 INT32 BezToolInfoBarOp::GetCurrentIndex()
01695 {
01696     return CurrentIndex;
01697 }
01698 
01699 
01700 
01701 /********************************************************************************************
01702 
01703 >   void BezToolInfoBarOp::UpdateTextIndicator()
01704 
01705     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01706     Created:    05/10/94
01707     Inputs:     -
01708     Purpose:    Updates the text in the "Add/Change/New" field.  If there is a floating
01709                 endpoint then the field says "New", if the first or last point on an open
01710                 path is selected then it says "Add", otherwise it says "Change"
01711 
01712 ********************************************************************************************/
01713 void BezToolInfoBarOp::UpdateTextIndicator()
01714 {
01715     // Exit now if there is no infobar
01716     if (WindowID==NULL || pBezTool==NULL)
01717         return;
01718 
01719     String_16 DisplayWord;
01720 
01721     switch (pBezTool->CurrentToolMode)
01722     {
01723         case BezierTool::New:
01724             DisplayWord.Load(_R(IDS_BEZTOOL_NEW),Tool::GetModuleID(pBezTool->GetID()));
01725             break;
01726         case BezierTool::Add:
01727             DisplayWord.Load(_R(IDS_BEZTOOL_ADD),Tool::GetModuleID(pBezTool->GetID()));
01728             break;
01729         case BezierTool::Change:
01730             DisplayWord.Load(_R(IDS_BEZTOOL_CHANGE),Tool::GetModuleID(pBezTool->GetID()));
01731             break;
01732         default:
01733             ERROR3("Unknown tool mode encountered");
01734     }
01735 
01736     SetStringGadgetValue(_R(IDC_BEZTOOLADDCHANGE), DisplayWord);
01737     PaintGadgetNow(_R(IDC_BEZTOOLADDCHANGE));
01738 }
01739 
01740 
01741 
01742 /********************************************************************************************
01743 
01744 >   void BezToolInfoBarOp::UpdateCurveButton()
01745 
01746     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01747     Created:    27/9/94
01748     Inputs:     -
01749     Purpose:    Sets the grey and select status of the curve button on the infobar.
01750 
01751 ********************************************************************************************/
01752 
01753 void BezToolInfoBarOp::UpdateCurveButton()
01754 {
01755     // Exit now if there is no infobar
01756     if (WindowID==NULL || pBezTool==NULL)
01757         return;
01758 
01759     if (pBezTool->CurrentToolMode != BezierTool::Change)
01760     {
01761         SetLongGadgetValue(_R(IDC_BTN_MAKECURVE), pBezTool->CreateCurve, FALSE);
01762         EnableGadget(_R(IDC_BTN_MAKECURVE), TRUE);
01763     }
01764     else
01765     {
01766         OpState State = OpMakeSegmentsCurves::GetState(NULL, NULL);
01767 
01768         EnableGadget(_R(IDC_BTN_MAKECURVE), !State.Greyed);
01769         SetLongGadgetValue(_R(IDC_BTN_MAKECURVE), (State.Ticked==FALSE ? 0 : 1), FALSE);
01770     }
01771 }
01772 
01773 
01774 
01775 /********************************************************************************************
01776 
01777 >   void BezToolInfoBarOp::UpdateLineButton()
01778 
01779     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01780     Created:    27/9/94
01781     Inputs:     -
01782     Purpose:    Sets the grey and select status of the line button on the infobar.
01783 
01784 ********************************************************************************************/
01785 
01786 void BezToolInfoBarOp::UpdateLineButton()
01787 {
01788     // Exit now if there is no infobar
01789     if (WindowID==NULL || pBezTool==NULL)
01790         return;
01791 
01792     if (pBezTool->CurrentToolMode != BezierTool::Change)
01793     {
01794         SetLongGadgetValue(_R(IDC_BTN_MAKELINE), !pBezTool->CreateCurve, FALSE);
01795         EnableGadget(_R(IDC_BTN_MAKELINE), TRUE);
01796     }
01797     else
01798     {
01799         OpState State = OpMakeSegmentsLines::GetState(NULL, NULL);
01800 
01801         EnableGadget(_R(IDC_BTN_MAKELINE), !State.Greyed);
01802         SetLongGadgetValue(_R(IDC_BTN_MAKELINE), (State.Ticked==FALSE ? 0 : 1) , FALSE);
01803     }
01804 }
01805 
01806 
01807 
01808 /********************************************************************************************
01809 
01810 >   void BezToolInfoBarOp::UpdateReversePathsButton ()
01811 
01812     Author:     Chris_Snook (Xara Group Ltd) <camelotdev@xara.com>
01813     Created:    11/5/2000
01814     Inputs:     -
01815     Purpose:    Sets the grey and select status of the reverse paths button on the infobar.
01816 
01817 ********************************************************************************************/
01818 
01819 void BezToolInfoBarOp::UpdateReversePathsButton ()
01820 {
01821     // Exit now if there is no infobar
01822     if (WindowID==NULL || pBezTool==NULL)
01823         return;
01824 
01825     /*if ((pBezTool->CurrentToolMode != BezierTool::Change) && (pBezTool->CurrentToolMode != BezierTool::Add))
01826     {
01827         SetLongGadgetValue(_R(IDC_BTN_BEZ_REVERSE_PATH), !pBezTool->CreateCurve, FALSE);
01828         EnableGadget(_R(IDC_BTN_BEZ_REVERSE_PATH), FALSE);
01829     }
01830     else
01831     {*/
01832         OpState State = OpReversePath::GetState(NULL, NULL);
01833 
01834         EnableGadget(_R(IDC_BTN_BEZ_REVERSE_PATH), !State.Greyed);
01835     //  SetLongGadgetValue(_R(IDC_BTN_BEZ_REVERSE_PATH), (State.Ticked==FALSE ? 0 : 1) , FALSE);
01836     //}
01837 }
01838 
01839 
01840 
01841 /********************************************************************************************
01842 
01843 >   void BezToolInfoBarOp::UpdateSmoothButton()
01844 
01845     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01846     Created:    27/9/94
01847     Inputs:     -
01848     Purpose:    Sets the grey and select status of the smooth button on the infobar.
01849 
01850 ********************************************************************************************/
01851 
01852 void BezToolInfoBarOp::UpdateSmoothButton()
01853 {
01854     // Exit now if there is no infobar
01855     if (WindowID==NULL || pBezTool==NULL)
01856         return;
01857 
01858     if (pBezTool->CurrentToolMode != BezierTool::Change)
01859     {
01860         SetLongGadgetValue(_R(IDC_BTN_SMOOTHPOINT), !pBezTool->CreateCusp, FALSE);
01861         EnableGadget(_R(IDC_BTN_SMOOTHPOINT), TRUE);
01862     }
01863     else
01864     {
01865         BOOL    Smooth;
01866         BOOL    Cusp;
01867 
01868         if (ScanPointsForJoins(&Smooth, &Cusp))
01869         {
01870             SetLongGadgetValue(_R(IDC_BTN_SMOOTHPOINT), Smooth, FALSE);
01871             EnableGadget(_R(IDC_BTN_SMOOTHPOINT), TRUE);
01872         }
01873         else
01874         {
01875             SetLongGadgetValue(_R(IDC_BTN_SMOOTHPOINT), FALSE, FALSE);
01876             EnableGadget(_R(IDC_BTN_SMOOTHPOINT), FALSE);
01877         }
01878     }
01879 }
01880 
01881 
01882 /********************************************************************************************
01883 
01884 >   void BezToolInfoBarOp::UpdateStartArrowButton()
01885 
01886     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01887     Created:    26/2/99
01888     Inputs:     -
01889     Purpose:    Sets the grey and select status of the arrow extents button on the infobar.
01890 
01891 ********************************************************************************************/
01892 
01893 void BezToolInfoBarOp::UpdateStartArrowButton()
01894 {
01895     // Exit now if there is no infobar
01896     if (WindowID==NULL || pBezTool==NULL)
01897         return;
01898 #ifdef ARROWHEADS
01899 
01900     if (pBezTool->CurrentToolMode != BezierTool::Change)
01901     {
01902         SetLongGadgetValue(_R(IDC_BTN_BEZ_START_ARROW), FALSE, FALSE);
01903         EnableGadget(_R(IDC_BTN_BEZ_START_ARROW), TRUE);
01904     }
01905     else
01906     {
01907         SetLongGadgetValue(_R(IDC_BTN_BEZ_START_ARROW), FALSE, FALSE);
01908         EnableGadget(_R(IDC_BTN_BEZ_START_ARROW), TRUE);    
01909     }
01910 
01911 #endif
01912 }
01913 
01914 /********************************************************************************************
01915 
01916 >   void BezToolInfoBarOp::UpdateStartArrowButton()
01917 
01918     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01919     Created:    26/2/99
01920     Inputs:     -
01921     Purpose:    Sets the grey and select status of the arrow extents button on the infobar.
01922 
01923 ********************************************************************************************/
01924 
01925 void BezToolInfoBarOp::UpdateEndArrowButton()
01926 {
01927     // Exit now if there is no infobar
01928     if (WindowID==NULL || pBezTool==NULL)
01929         return;
01930 #ifdef ARROWHEADS
01931     if (pBezTool->CurrentToolMode != BezierTool::Change)
01932     {
01933         SetLongGadgetValue(_R(IDC_BTN_BEZ_END_ARROW), FALSE, FALSE);
01934         EnableGadget(_R(IDC_BTN_BEZ_END_ARROW), TRUE);
01935     }
01936     else
01937     {
01938         SetLongGadgetValue(_R(IDC_BTN_BEZ_END_ARROW), FALSE, FALSE);
01939         EnableGadget(_R(IDC_BTN_BEZ_END_ARROW), TRUE);  
01940     }
01941 #endif
01942 }
01943 
01944 /********************************************************************************************
01945 
01946 >   void BezToolInfoBarOp::UpdateCuspButton()
01947 
01948     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01949     Created:    27/9/94
01950     Inputs:     -
01951     Purpose:    Sets the grey and select status of the cusp button on the infobar.
01952 
01953 ********************************************************************************************/
01954 
01955 void BezToolInfoBarOp::UpdateCuspButton()
01956 {
01957     // Exit now if there is no infobar
01958     if (WindowID==NULL || pBezTool==NULL)
01959         return;
01960 
01961     if (pBezTool->CurrentToolMode != BezierTool::Change)
01962     {
01963         SetLongGadgetValue(_R(IDC_BTN_CUSPPOINT), pBezTool->CreateCusp, FALSE);
01964         EnableGadget(_R(IDC_BTN_CUSPPOINT), TRUE);
01965     }
01966     else
01967     {
01968         BOOL    Smooth;
01969         BOOL    Cusp;
01970 
01971         if (ScanPointsForJoins(&Smooth, &Cusp))
01972         {
01973             SetLongGadgetValue(_R(IDC_BTN_CUSPPOINT), Cusp, FALSE);
01974             EnableGadget(_R(IDC_BTN_CUSPPOINT), TRUE);
01975         }
01976         else
01977         {
01978             SetLongGadgetValue(_R(IDC_BTN_CUSPPOINT), FALSE, FALSE);
01979             EnableGadget(_R(IDC_BTN_CUSPPOINT), FALSE);
01980         }
01981     }
01982 }
01983 
01984 
01985 
01986 /********************************************************************************************
01987 
01988 >   void BezToolInfoBarOp::UpdatePositionFields(BOOL ForceUpdate = FALSE, BOOL ScanForCurrent = TRUE)
01989 
01990     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01991     Created:    17/10/1994
01992     Inputs:     ScanForCurrent - TRUE to scan the SelRange to update CurrentPath, CurrentIndex, etc..
01993                                  FALSE to use the existing values
01994     Purpose:    Sets the grey and select status of the point position icons on the infobar.
01995                 If there is one selected point on a path then the fields are enabled, in
01996                 all other cases they are greyed.
01997 
01998 ********************************************************************************************/
01999 
02000 void BezToolInfoBarOp::UpdatePositionFields(BOOL ForceUpdate, BOOL ScanForCurrent)
02001 {
02002     // Exit now if there is no infobar
02003     if (WindowID==NULL || pBezTool==NULL)
02004         return;
02005 
02006     INT32   SelectedPoints = 0;
02007     if (ScanForCurrent)
02008     {
02009         SelRange* Selection = GetApplication()->FindSelection();
02010         Node* pNode = Selection->FindFirst();
02011 
02012         while ( (pNode != NULL) && (SelectedPoints<2) )
02013         {
02014             NodePath* pSelected = pBezTool->FindEditablePath(pNode);
02015 
02016             if (pSelected)
02017             {
02018                 Path*   ThisPath = &(pSelected->InkPath);
02019                 INT32   UsedSlots = ThisPath->GetNumCoords();
02020                 PathFlags*  Flags = ThisPath->GetFlagArray();
02021                 PathVerb*   Verbs = ThisPath->GetVerbArray();
02022 
02023                 // Now scan the path and count the number of selected points
02024                 for (INT32 i=0; i<UsedSlots; i++)
02025                 {
02026                     if (Flags[i].IsSelected && Flags[i].IsEndPoint)
02027                     {
02028                         // if it's a closed path the first point is selected
02029                         // as well as the last. We have to account for this.
02030                         if (! ((Verbs[i] == PT_MOVETO) && ThisPath->IsSubPathClosed(i)) )
02031                         {
02032                             SelectedPoints++;
02033                             CurrentSpread = pSelected->FindParentSpread();
02034                             CurrentNodePath = pSelected;
02035                             CurrentInkPath = &(CurrentNodePath->InkPath);
02036                             CurrentIndex = i;
02037                         }
02038                     }
02039                 }
02040             }
02041             pNode = Selection->FindNext(pNode);
02042         }
02043     }
02044 
02045     String_8 NullString = _T("");
02046     BOOL EnableArray[NumberIconIDs];
02047     for (INT32 loop = 0; loop < NumberIconIDs; loop++)
02048         EnableArray[loop] = TRUE;
02049 
02050     if (IgnoreNextUpdate)
02051         IgnoreNextUpdate = FALSE;
02052     else
02053     {
02054         if ((SelectedPoints == 1) || !ScanForCurrent)
02055         {   // Enable and update things
02056             UpdateEditFieldsFromPath(CurrentInkPath, CurrentSpread, CurrentIndex, ForceUpdate, EnableArray);
02057 
02058             for (INT32 i = 0; i < NumberIconIDs; i++)
02059             {
02060                 EnableGadget(AllIconIDs[i], EnableArray[i]);
02061             }
02062         }
02063         else
02064         {   // Disable and clear everything
02065             CurrentNodePath = NULL;
02066             CurrentInkPath = NULL;
02067             for (INT32 loop = 0; loop < NumberIconIDs; loop++)
02068                 EnableArray[loop] = FALSE;
02069             SetStringGadgetValue(_R(IDC_PATH_EDIT_ENDPOINTX), NullString);
02070             SetStringGadgetValue(_R(IDC_PATH_EDIT_ENDPOINTY), NullString);
02071             SetStringGadgetValue(_R(IDC_PATH_EDIT_FIRSTX), NullString);
02072             SetStringGadgetValue(_R(IDC_PATH_EDIT_FIRSTY), NullString);
02073             SetStringGadgetValue(_R(IDC_PATH_EDIT_SECONDX), NullString);
02074             SetStringGadgetValue(_R(IDC_PATH_EDIT_SECONDY), NullString);
02075             SetStringGadgetValue(_R(IDC_PATH_STATIC_FIRSTX), NullString);
02076             SetStringGadgetValue(_R(IDC_PATH_STATIC_FIRSTY), NullString);
02077             SetStringGadgetValue(_R(IDC_PATH_STATIC_SECONDX), NullString);
02078             SetStringGadgetValue(_R(IDC_PATH_STATIC_SECONDY), NullString);
02079 
02080             for (INT32 i = 0; i < NumberIconIDs; i++)
02081             {
02082                 EnableGadget(AllIconIDs[i], FALSE);//EnableArray[i]);
02083             }
02084         }
02085     }
02086 }
02087 
02088 
02089 
02090 /********************************************************************************************
02091 >   BOOL BezToolInfoBarOp::SetEdit(CGadgetID gid, INT32 nValue, Spread* pUnitSpread, BOOL PaintNow = FALSE)
02092 
02093     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com> 
02094     Created:    25/1/95
02095     Inputs:     gid             gadget identifier
02096                 nValue          the value to set in the edit field (in millipoints
02097                 pUnitSpread     the spread containing the distance
02098                 PaintNow - TRUE if the edit filed should be redrawn immediatly.
02099     Outputs:    -
02100     Returns:    TRUE if everything suceeded, FALSE if an error occured
02101     Purpose:    Converts the given value to the appropriate units, as contained in the
02102                 spread, converts it to text and writes in into the edit-field.
02103     Errors:     -
02104     SeeAlso:    BezToolInfoBarOp::SetEditPosition
02105     
02106 ********************************************************************************************/
02107 
02108 BOOL BezToolInfoBarOp::SetEditLength(CGadgetID gid, INT32 nValue, Spread* pSpread, BOOL PaintNow)
02109 {
02110     ERROR2IF(pSpread == NULL, FALSE, "Spread was NULL in SetEditLength");
02111 
02112     // Get the dimension scaling object (units) associated with the given spread
02113     // and convert to its units.
02114     DimScale* pDimScale = DimScale::GetPtrDimScale((Node*) pSpread);
02115     ERROR2IF (pDimScale == NULL, FALSE, "Null DimScale* in SelectorInfoBarOp::SetEdit");
02116 
02117     // Convert to units & text and display.
02118     String_256 str;
02119     pDimScale->ConvertToUnits((INT32) nValue, &str);
02120 
02121     if (!UpdateStringGadgetValue(gid, &str))
02122         PaintNow = FALSE;
02123 
02124     if (PaintNow)
02125         PaintGadgetNow(gid);
02126 
02127     return TRUE;
02128 }
02129 
02130 
02131 
02132 
02133 /********************************************************************************************
02134 >   BOOL BezToolInfoBarOp::SetEditPosition(CGadgetID gid, INT32 nValue, Spread* pUnitSpread, BOOL PaintNow = FALSE)
02135 
02136     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com> (copied from the selector tool by JustinF)
02137     Created:    31/8/94
02138     Inputs:     gid             gadget identifier
02139                 nValue          the value to set in the edit field (in millipoints
02140                 pUnitSpread     the spread containing the object (so its units can be
02141                                 extracted).  If this parameter is NULL then no unit
02142                                 conversion is performed.
02143                 PaintNow - TRUE if the edit filed should be redrawn immediatly.
02144     Outputs:    -
02145     Returns:    TRUE if everything suceeded, FALSE if an error occured
02146     Purpose:    Converts the given value to the appropriate units, as contained in the
02147                 spread, converts it to text and writes in into the edit-field.
02148     Errors:     -
02149     SeeAlso:    SelectorInfoBarOp::SetEdit
02150     
02151 ********************************************************************************************/
02152 
02153 BOOL BezToolInfoBarOp::SetEditPosition(CGadgetID gid, INT32 nValue, Spread* pSpread, BOOL PaintNow)
02154 {
02155     ERROR2IF(pSpread == NULL, FALSE, "Current spread was NULL");
02156 
02157     DocCoord    FakePoint(nValue, 0);
02158     String_256  XText;
02159     String_256  YText;
02160 
02161     pSpread->SpreadCoordToText(&XText, &YText, FakePoint);
02162     if (!UpdateStringGadgetValue(gid, &XText))
02163         PaintNow = FALSE;
02164 
02165     if (PaintNow)
02166         PaintGadgetNow(gid);
02167 
02168     return TRUE;
02169 }
02170 
02171 
02172 
02173 /********************************************************************************************
02174 >   BOOL BezToolInfoBarOp::SetEditPosition(CGadgetID gidX, CGadgetID gidY, DocCoord loc,
02175                                                         Spread* pSpread, BOOL PaintNow = FALSE)
02176 
02177     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com> 
02178     Created:    25/1/95
02179     Inputs:     gidX - X coordinate gadget identifier
02180                 gidY - Y coordinate gadget identifier
02181                 loc - the SpreadCoord to show in the edit fields
02182                 pUnitSpread - the spread containing the object (so its units can be extracted).
02183                 PaintNow - TRUE if the edit field should be redrawn immediatly.
02184     Outputs:    -
02185     Returns:    TRUE if everything suceeded, FALSE if an error occured
02186     Purpose:    Converts the given coordinate to the appropriate units, as contained in the
02187                 spread, converts it to text and writes in into the edit-fields.
02188     Errors:     -
02189     SeeAlso:    -
02190     
02191 ********************************************************************************************/
02192 
02193 BOOL BezToolInfoBarOp::SetEditPosition(CGadgetID gidX, CGadgetID gidY, DocCoord loc, Spread* pSpread, BOOL PaintNow)
02194 {
02195     ERROR2IF(pSpread == NULL, FALSE, "Current spread was NULL");
02196 
02197     String_256  XText;
02198     String_256  YText;
02199 
02200     pSpread->SpreadCoordToText(&XText, &YText, loc);
02201     BOOL UpdatedX = UpdateStringGadgetValue(gidX, &XText) ;
02202     BOOL UpdatedY = UpdateStringGadgetValue(gidY, &YText) ;
02203 
02204     if (PaintNow)
02205     {
02206         if (UpdatedX)
02207             PaintGadgetNow(gidX);
02208         if (UpdatedY)
02209             PaintGadgetNow(gidY);
02210     }
02211 
02212     return TRUE;
02213 }
02214 
02215 
02216 
02217 /********************************************************************************************
02218 
02219 >   void BezToolInfoBarOp::HandleClickOnCurveButton()
02220 
02221     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02222     Created:    28/9/94
02223     Inputs:     -
02224     Returns:    -
02225     Purpose:    Called by the message handler to handle clicks on the curve button on the
02226                 infobar.
02227 
02228 ********************************************************************************************/
02229 
02230 void BezToolInfoBarOp::HandleClickOnCurveButton()
02231 {
02232     if (pBezTool==NULL)
02233         return;
02234     
02235     if (pBezTool->CurrentToolMode != BezierTool::Change)
02236     {
02237         pBezTool->CreateCurve = TRUE;
02238     }
02239     else
02240     {
02241 
02242         SelRange* Selected = GetApplication()->FindSelection();
02243         if (Selected != NULL)
02244         {
02245             OpDescriptor* pOpDesc = OpDescriptor::FindOpDescriptor(CC_RUNTIME_CLASS(OpMakeSegmentsCurves));
02246             if (pOpDesc != NULL)
02247             {
02248                 OpState State = OpMakeSegmentsCurves::GetState(NULL,pOpDesc);
02249                 if (!State.Greyed)
02250                     pOpDesc->Invoke();
02251             }
02252         }
02253 
02254         if (AttributeManager::LastAttrAppliedBecomesCurrent)
02255             pBezTool->CreateCurve = TRUE;
02256     }
02257     UpdateLineButton();
02258     UpdateCurveButton();
02259     UpdateCuspButton();
02260     UpdateSmoothButton();
02261 }
02262 
02263 /********************************************************************************************
02264 
02265 >   void BezToolInfoBarOp::HandleClickOnStartArrowButton()
02266 
02267     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
02268     Created:    26/2/99
02269     Inputs:     -
02270     Returns:    -
02271     Purpose:    Called by the message handler to handle clicks on the arrow extends button
02272                 on the infobar.
02273 
02274 ********************************************************************************************/
02275 void BezToolInfoBarOp::HandleClickOnStartArrowButton()
02276 {
02277     // invoke the op
02278 #ifdef ARROWHEADS
02279     OpDescriptor * pOpDesc = 
02280         OpDescriptor::FindOpDescriptor(CC_RUNTIME_CLASS(OpChangeStartArrowExtendsDesc));
02281         
02282     if (pOpDesc)
02283     {
02284         pOpDesc->Invoke();
02285     }
02286 #endif
02287 }
02288 
02289 /********************************************************************************************
02290 
02291 >   void BezToolInfoBarOp::HandleClickOnEndArrowButton()
02292 
02293     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
02294     Created:    26/2/99
02295     Inputs:     -
02296     Returns:    -
02297     Purpose:    Called by the message handler to handle clicks on the arrow extends button
02298                 on the infobar.
02299 
02300 ********************************************************************************************/
02301 void BezToolInfoBarOp::HandleClickOnEndArrowButton()
02302 {
02303     // invoke the op
02304 #ifdef ARROWHEADS
02305     OpDescriptor * pOpDesc = 
02306         OpDescriptor::FindOpDescriptor(CC_RUNTIME_CLASS(OpChangeEndArrowExtendsDesc));
02307         
02308     if (pOpDesc)
02309     {
02310         pOpDesc->Invoke();
02311     }
02312 #endif
02313 }
02314     
02315 /********************************************************************************************
02316 
02317 >   void BezToolInfoBarOp::HandleClickOnLineButton()
02318 
02319     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02320     Created:    28/9/94
02321     Inputs:     -
02322     Returns:    -
02323     Purpose:    Called by the message handler to handle clicks on the line button on the
02324                 infobar.
02325 
02326 ********************************************************************************************/
02327 void BezToolInfoBarOp::HandleClickOnLineButton()
02328 {
02329     if (pBezTool==NULL)
02330         return;
02331 
02332     if (pBezTool->CurrentToolMode != BezierTool::Change)
02333     {
02334         pBezTool->CreateCurve = FALSE;
02335     }
02336     else
02337     {
02338         SelRange* Selected = GetApplication()->FindSelection();
02339         if (Selected != NULL)
02340         {
02341             OpDescriptor* pOpDesc = OpDescriptor::FindOpDescriptor(CC_RUNTIME_CLASS(OpMakeSegmentsLines));
02342             if (pOpDesc != NULL)
02343             {
02344                 OpState State = OpMakeSegmentsLines::GetState(NULL,pOpDesc);
02345                 if (!State.Greyed)
02346                     pOpDesc->Invoke();
02347             }
02348         }
02349 
02350         if (AttributeManager::LastAttrAppliedBecomesCurrent)
02351             pBezTool->CreateCurve = FALSE;
02352     }
02353 
02354     UpdateLineButton();
02355     UpdateCurveButton();
02356     UpdateCuspButton();
02357     UpdateSmoothButton();
02358 }
02359 
02360 
02361 
02362 /********************************************************************************************
02363 
02364 >   void BezToolInfoBarOp::HandleClickOnReversePathButton ()
02365 
02366     Author:     Chris_Snook (Xara Group Ltd) <camelotdev@xara.com>
02367     Created:    10/5/2000
02368     Inputs:     -
02369     Returns:    -
02370     Purpose:    Called by the message handler to handle clicks on the reverse path button
02371                 on the infobar.
02372 
02373 ********************************************************************************************/
02374 
02375 void BezToolInfoBarOp::HandleClickOnReversePathButton ()
02376 {
02377     if (pBezTool==NULL)
02378     {
02379         return;
02380     }
02381 
02382 /*  if (pBezTool->CurrentToolMode != BezierTool::Change)
02383     {
02384         pBezTool->CreateCurve = FALSE;
02385     }
02386     else
02387     {*/
02388 
02389     // if this function is called - then the button MUST be enabled (thereby proving that we
02390     // have a selection).  BUT lets just make sure of this fact ....
02391 
02392         SelRange* Selected = GetApplication()->FindSelection();
02393         
02394         if (Selected != NULL)
02395         {
02396             OpDescriptor* pOpDesc = OpDescriptor::FindOpDescriptor (CC_RUNTIME_CLASS (OpReversePath));
02397             
02398             if (pOpDesc != NULL)
02399             {
02400                 OpState State = OpReversePath::GetState (NULL, pOpDesc);
02401                 
02402                 if (!State.Greyed)
02403                 {
02404                     pOpDesc->Invoke ();
02405                 }
02406             }
02407         }
02408 
02409     /*  if (AttributeManager::LastAttrAppliedBecomesCurrent)
02410         {
02411             pBezTool->CreateCurve = FALSE;
02412         }*/
02413     //}
02414 }
02415 
02416 
02417 
02418 /********************************************************************************************
02419 
02420 >   void BezToolInfoBarOp::HandleClickOnSmoothButton()
02421 
02422     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02423     Created:    28/9/94
02424     Inputs:     -
02425     Returns:    -
02426     Purpose:    Called by the message handler to handle clicks on the smooth button on the
02427                 infobar.
02428 
02429 ********************************************************************************************/
02430 void BezToolInfoBarOp::HandleClickOnSmoothButton()
02431 {
02432     if (pBezTool==NULL)
02433         return;
02434 
02435     if (pBezTool->CurrentToolMode != BezierTool::Change)
02436     {
02437         pBezTool->CreateCusp = FALSE;
02438     }
02439     else
02440     {
02441         BOOL    Smooth;
02442         BOOL    Cusp;
02443 
02444         if (ScanPointsForJoins(&Smooth, &Cusp))
02445             ScanPointsForToggleSmooth(TRUE, FALSE);
02446 
02447         if (AttributeManager::LastAttrAppliedBecomesCurrent)
02448             pBezTool->CreateCusp = FALSE;
02449     }
02450     UpdateCuspButton();
02451     UpdateSmoothButton();
02452 }
02453 
02454 
02455 
02456 /********************************************************************************************
02457 >   void BezToolInfoBarOp::HandleClickOnCuspButton(BOOL WithConstrain)
02458 
02459     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02460     Created:    28/9/94
02461     Inputs:     WithConstrain - TRUE if the control points should not be moved
02462     Returns:    -
02463     Purpose:    Called by the message handler to handle clicks on the cusp button on the
02464                 infobar.
02465 ********************************************************************************************/
02466 void BezToolInfoBarOp::HandleClickOnCuspButton(BOOL WithConstrain)
02467 {
02468     if (pBezTool==NULL)
02469         return;
02470 
02471     if (pBezTool->CurrentToolMode != BezierTool::Change)
02472     {
02473         pBezTool->CreateCusp = TRUE;
02474     }
02475     else
02476     {
02477         BOOL Smooth = TRUE;
02478         BOOL Cusp = TRUE;
02479 
02480         if (ScanPointsForJoins(&Smooth, &Cusp))
02481             ScanPointsForToggleSmooth(FALSE, !WithConstrain);   // invert WithConstrain
02482 
02483         if (AttributeManager::LastAttrAppliedBecomesCurrent)
02484             pBezTool->CreateCusp = TRUE;
02485     }
02486     UpdateCuspButton();
02487     UpdateSmoothButton();
02488 }
02489 
02490 
02491 
02492 /********************************************************************************************
02493 >   BOOL BezToolInfoBarOp::HandleBumpClick(INT32 GadgetID)
02494 
02495     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02496     Created:    9/8/95
02497     Inputs:     GadgetID - the gadget ID of the clicked bump button
02498     Returns:    TRUE/FALSE for success/failure
02499     Purpose:    Called by the message handler to handle clicks on any of the bump buttons.
02500     Errors:     If an invalid gadget code is detected or if current path is NULL
02501 ********************************************************************************************/
02502 BOOL BezToolInfoBarOp::HandleBumpClick(CGadgetID GadgetID)
02503 {
02504     ERROR2IF(((CurrentNodePath == NULL) || (CurrentInkPath == NULL) || (&(CurrentNodePath->InkPath) != CurrentInkPath)), FALSE, "HandleClickOnBump found CurrentPath(s) was NULL"); 
02505 //  PathVerb*   Verbs = CurrentInkPath->GetVerbArray();
02506 //  DocCoord*   Coords = CurrentInkPath->GetCoordArray();
02507 
02508     UINT32 ChangeField = 0;         // ID of edit field with value
02509     BOOL IsCoordChange = FALSE;     // TRUE if is coord change
02510     BOOL IsLengthChange = FALSE;    // TRUE if is line length change
02511     BOOL IsAngleChange = FALSE;     // TRUE if is line angle change
02512     INT32 ChangeValue = 0;          // Amount of change
02513 
02514     //  switch (GadgetID)
02515     if (
02516         // Bumping the currently selected path endpoint
02517         (GadgetID == _R(IDC_PATH_BUMP_ENDX_LESS)) ||
02518         (GadgetID == _R(IDC_PATH_BUMP_ENDX_MORE)) ||
02519         (GadgetID == _R(IDC_PATH_BUMP_ENDY_LESS)) ||
02520         (GadgetID == _R(IDC_PATH_BUMP_ENDY_MORE))
02521         )
02522     { 
02523         ERROR2IF(CurrentIndex == -1, FALSE, "Attempt to nudge unspecified endpoint");   
02524         IsCoordChange = TRUE;
02525         // Set the coord nudge ammount
02526         if ((GadgetID == _R(IDC_PATH_BUMP_ENDY_LESS)) || (GadgetID == _R(IDC_PATH_BUMP_ENDX_MORE)))
02527             ChangeValue = OpNudge::GetNudgeStep();
02528         else
02529             ChangeValue = -OpNudge::GetNudgeStep();
02530         // Set the edit field
02531         if ((GadgetID == _R(IDC_PATH_BUMP_ENDX_LESS)) || (GadgetID == _R(IDC_PATH_BUMP_ENDX_MORE)))
02532             ChangeField = _R(IDC_PATH_EDIT_ENDPOINTX);
02533         else
02534             ChangeField = _R(IDC_PATH_EDIT_ENDPOINTY);
02535     }
02536     else if (
02537         // Bumping the previous endpoint
02538         (GadgetID == _R(IDC_PATH_BUMP_FIRSTX_LESS)) ||
02539         (GadgetID == _R(IDC_PATH_BUMP_FIRSTX_MORE)) ||
02540         (GadgetID == _R(IDC_PATH_BUMP_FIRSTY_LESS)) ||
02541         (GadgetID == _R(IDC_PATH_BUMP_FIRSTY_MORE))
02542         )
02543     {
02544         if (CurrentInkPath->FindPrevControlPoint(CurrentIndex) != -1)
02545         {
02546             // Bumping control point position
02547             IsCoordChange = TRUE;
02548             // Set the coord nudge ammount
02549             if ((GadgetID == _R(IDC_PATH_BUMP_FIRSTX_MORE)) || (GadgetID == _R(IDC_PATH_BUMP_FIRSTY_LESS)))
02550                 ChangeValue = OpNudge::GetNudgeStep();
02551             else
02552                 ChangeValue = -OpNudge::GetNudgeStep();
02553             // Set the edit field
02554             if ((GadgetID == _R(IDC_PATH_BUMP_FIRSTX_LESS)) || (GadgetID == _R(IDC_PATH_BUMP_FIRSTX_MORE)))
02555                 ChangeField = _R(IDC_PATH_EDIT_FIRSTX);
02556             else
02557                 ChangeField = _R(IDC_PATH_EDIT_FIRSTY);
02558         }
02559         else
02560         {
02561             if ((GadgetID == _R(IDC_PATH_BUMP_FIRSTX_LESS)) || (GadgetID == _R(IDC_PATH_BUMP_FIRSTX_MORE)))
02562             {
02563                 // Change the length of the line by the bump amount     
02564                 IsLengthChange = TRUE;
02565                 // Set the length nudge ammount
02566                 if (GadgetID == _R(IDC_PATH_BUMP_FIRSTX_MORE))
02567                     ChangeValue = OpNudge::GetNudgeStep();
02568                 else
02569                     ChangeValue = -OpNudge::GetNudgeStep();
02570                 ChangeField = _R(IDC_PATH_EDIT_FIRSTX);
02571             }
02572             else
02573             {
02574                 // Change the angle of the line by the bump amount      
02575                 ERROR3IF((GadgetID != _R(IDC_PATH_BUMP_FIRSTY_LESS)) && (GadgetID != _R(IDC_PATH_BUMP_FIRSTY_MORE)),"What's that gadget?");
02576                 IsAngleChange = TRUE;
02577                 // Set the angle nudge ammount
02578                 if (GadgetID == _R(IDC_PATH_BUMP_FIRSTY_MORE))
02579                     ChangeValue = 5;
02580                 else
02581                     ChangeValue = -5;
02582                 ChangeField = _R(IDC_PATH_EDIT_FIRSTY);
02583             }
02584         }
02585     }
02586     else if (
02587     // Bumping the next endpoint
02588         (GadgetID == _R(IDC_PATH_BUMP_SECONDX_LESS)) ||
02589         (GadgetID == _R(IDC_PATH_BUMP_SECONDX_MORE)) ||
02590         (GadgetID == _R(IDC_PATH_BUMP_SECONDY_LESS)) ||
02591         (GadgetID == _R(IDC_PATH_BUMP_SECONDY_MORE))
02592         )
02593     {
02594         if (CurrentInkPath->FindNextControlPoint(CurrentIndex) != -1)
02595         {
02596             // Bumping control point position
02597             IsCoordChange = TRUE;
02598             // Set the coord nudge ammount
02599             if ((GadgetID == _R(IDC_PATH_BUMP_SECONDX_MORE)) || (GadgetID == _R(IDC_PATH_BUMP_SECONDY_LESS)))
02600                 ChangeValue = OpNudge::GetNudgeStep();
02601             else
02602                 ChangeValue = -OpNudge::GetNudgeStep();
02603             // Set the edit field
02604             if ((GadgetID == _R(IDC_PATH_BUMP_SECONDX_LESS)) || (GadgetID == _R(IDC_PATH_BUMP_SECONDX_MORE)))
02605                 ChangeField = _R(IDC_PATH_EDIT_SECONDX);
02606             else
02607                 ChangeField = _R(IDC_PATH_EDIT_SECONDY);
02608         }
02609         else
02610         {
02611             if ((GadgetID == _R(IDC_PATH_BUMP_SECONDX_LESS)) || (GadgetID == _R(IDC_PATH_BUMP_SECONDX_MORE)))
02612             {
02613                 // Change the length of the line by the bump amount     
02614                 IsLengthChange = TRUE;
02615                 // Set the length nudge ammount
02616                 if (GadgetID == _R(IDC_PATH_BUMP_SECONDX_MORE))
02617                     ChangeValue = OpNudge::GetNudgeStep();
02618                 else
02619                     ChangeValue = -OpNudge::GetNudgeStep();
02620                 ChangeField = _R(IDC_PATH_EDIT_SECONDX);
02621             }
02622             else
02623             {
02624                 // Change the angle of the line by the bump amount      
02625                 ERROR3IF((GadgetID != _R(IDC_PATH_BUMP_SECONDY_LESS)) && (GadgetID != _R(IDC_PATH_BUMP_SECONDY_MORE)),"What's that gadget?");
02626                 IsAngleChange = TRUE;
02627                 // Set the angle nudge ammount
02628                 if (GadgetID == _R(IDC_PATH_BUMP_SECONDY_MORE))
02629                     ChangeValue = 5;
02630                 else
02631                     ChangeValue = -5;
02632                 ChangeField = _R(IDC_PATH_EDIT_SECONDY);
02633             }
02634         }
02635     }
02636 
02637     // Now update the edit field
02638     BOOL Valid = TRUE;
02639     if (IsCoordChange || IsLengthChange)
02640     {
02641         // Read the current field value
02642         ERROR2IF(CurrentSpread == NULL, FALSE, "HandleClickOnBump found CurrentSpread was NULL");   
02643         DimScale* pDimScale = DimScale::GetPtrDimScale((Node*) CurrentSpread);
02644         ERROR2IF(pDimScale == NULL, FALSE, "NULL DimScalePtr");
02645         String_32 FieldContents = GetStringGadgetValue(ChangeField, &Valid);
02646         MILLIPOINT CurrentPos = 0;
02647 
02648         if (Valid)
02649             Valid = pDimScale->ConvertToMillipoints(FieldContents, &CurrentPos);
02650         if (Valid)
02651         {
02652             CurrentPos += ChangeValue;
02653             if (IsLengthChange && (CurrentPos<0))
02654                 CurrentPos = 0;
02655 
02656             // Put this new value back into the edit field.
02657             SetEditLength(ChangeField, CurrentPos, CurrentSpread, TRUE);
02658         }
02659     }
02660     else if (IsAngleChange)
02661     {
02662         // Read the current field value
02663         double RequiredAngle = GetDoubleGadgetValue(ChangeField, -360.0, 360.0, _R(IDE_INVALID_ANGLE), &Valid);
02664         if (Valid)
02665         {
02666             RequiredAngle += ChangeValue;
02667 
02668             // Get the angle to lie between -180 and 180
02669             while (RequiredAngle < -180)
02670                 RequiredAngle += 360;
02671             while (RequiredAngle > 180)
02672                 RequiredAngle -= 360;
02673 
02674             SetDoubleGadgetValue(ChangeField, RequiredAngle);
02675         }
02676     }
02677 
02678     return TRUE;
02679 }
02680 
02681 
02682 
02683 /********************************************************************************************
02684 
02685 >   void BezToolInfoBarOp::HandleEditFieldCommit(CGadgetID CommitField)
02686 
02687     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02688     Created:    18/10/1994
02689     Inputs:     CommitField is the gadget code of the field that was committed
02690     Outputs:    -
02691     Returns:    TRUE if the fields were read sucessfully, FALSE if they wern't
02692     Purpose:    Called by the message handler when RETURN is pressed in one of the point
02693                 position edit fields.
02694     Errors:     Checks if CurrentPath and CurrentSpread are not NULL.
02695 
02696 ********************************************************************************************/
02697 BOOL BezToolInfoBarOp::HandleEditFieldCommit(CGadgetID CommitField)
02698 {
02699     ERROR2IF(((CurrentNodePath == NULL) || (CurrentInkPath == NULL)), FALSE, "HandleEditFieldCommit found CurrentPath was NULL");
02700     ERROR2IF(CurrentSpread == NULL, FALSE, "HandleEditFieldCommit found CurrentSpread was NULL");
02701     
02702     // Get path info
02703 //  const INT32 NumCoords = CurrentInkPath->GetNumCoords();
02704     const INT32 FirstIndex = CurrentInkPath->FindPrevControlPoint(CurrentIndex);
02705     const INT32 SecondIndex = CurrentInkPath->FindNextControlPoint(CurrentIndex);
02706     DocCoord*   Coords = CurrentInkPath->GetCoordArray();
02707 
02708     INT32 LineAnglePrevIndex = GetPrevLineIndex(CurrentInkPath, CurrentIndex);
02709     INT32 LineAngleNextIndex = GetNextLineIndex(CurrentInkPath, CurrentIndex);
02710     if (LineAnglePrevIndex==-2 || LineAngleNextIndex==-2)
02711         return FALSE;
02712 
02713     BOOL Valid = TRUE;
02714     INT32   NumChanges = 0;
02715     ElementCoord NewPos[3];
02716 
02717     // Check the centre edit fields which is always the coordinate of the selected endpoint
02718     BOOL EndPointMoved = FALSE;
02719     DocCoord NewEndPoint;
02720     DocCoord OldEndPoint = Coords[CurrentIndex];
02721     if (!HandleCoordFieldCommit(_R(IDC_PATH_EDIT_ENDPOINTX), _R(IDC_PATH_EDIT_ENDPOINTY), CurrentIndex, &NewEndPoint, &Valid))
02722         return FALSE;
02723     if (Valid && (OldEndPoint != NewEndPoint))
02724     {
02725         NewPos[NumChanges].Element = CurrentIndex;
02726         NewPos[NumChanges].Coordinate = NewEndPoint;
02727         NumChanges ++;
02728         EndPointMoved = TRUE;
02729     }
02730 
02731     // Now check the left hand pair, either the previous control point OR length & angle to previous line
02732     if (Valid && 0==NumChanges)
02733     {
02734         if (FirstIndex != -1)
02735         {
02736             DocCoord NewLeftPoint;
02737             DocCoord OldLeftPoint = Coords[FirstIndex];
02738             if (!HandleCoordFieldCommit(_R(IDC_PATH_EDIT_FIRSTX), _R(IDC_PATH_EDIT_FIRSTY), FirstIndex, &NewLeftPoint, &Valid))
02739                 return FALSE;
02740             if (Valid && (OldLeftPoint != NewLeftPoint))
02741             {
02742                 NewPos[NumChanges].Element = FirstIndex;
02743                 NewPos[NumChanges].Coordinate = NewLeftPoint;
02744                 NumChanges ++;
02745             }
02746         }
02747         else
02748         {
02749             if (LineAnglePrevIndex!=-1)
02750             {
02751                 DocCoord OtherEnd = Coords[LineAnglePrevIndex];
02752                 DocCoord NewLoc = OldEndPoint;
02753                 if (!HandleLAFieldCommit(_R(IDC_PATH_EDIT_FIRSTX), _R(IDC_PATH_EDIT_FIRSTY), CommitField, OldEndPoint, OtherEnd, &NewLoc))
02754                     return FALSE;
02755                                                          
02756                 if (NewLoc != Coords[CurrentIndex])
02757                 {
02758                     NewPos[NumChanges].Element = CurrentIndex;
02759                     NewPos[NumChanges].Coordinate = NewLoc;
02760                     NumChanges++;
02761                 }
02762             }
02763         }
02764     }
02765 
02766     // Now check the right hand pair
02767     if (Valid && 0==NumChanges)
02768     {
02769         if (SecondIndex != -1)
02770         {
02771             DocCoord NewRightPoint;
02772             DocCoord OldRightPoint = Coords[SecondIndex];
02773             if (!HandleCoordFieldCommit(_R(IDC_PATH_EDIT_SECONDX), _R(IDC_PATH_EDIT_SECONDY), SecondIndex, &NewRightPoint, &Valid))
02774                 return FALSE;
02775             if (Valid && (OldRightPoint != NewRightPoint))
02776             {
02777                 NewPos[NumChanges].Element = SecondIndex;
02778                 NewPos[NumChanges].Coordinate = NewRightPoint;
02779                 NumChanges ++;
02780             }
02781         }
02782         else
02783         {
02784             if (LineAngleNextIndex!=-1)
02785             {
02786                 DocCoord OtherEnd = Coords[LineAngleNextIndex];
02787                 DocCoord NewLoc = OldEndPoint;
02788                 if (!HandleLAFieldCommit(_R(IDC_PATH_EDIT_SECONDX), _R(IDC_PATH_EDIT_SECONDY), CommitField, OldEndPoint, OtherEnd, &NewLoc))
02789                     return FALSE;
02790 
02791                 if (NewLoc != Coords[CurrentIndex])
02792                 {
02793                     NewPos[NumChanges].Element = CurrentIndex;
02794                     NewPos[NumChanges].Coordinate = NewLoc;
02795                     NumChanges++;
02796                 }
02797             }
02798         }
02799     }
02800 
02801     // Now fire off a MovePoints operation to do the movement with undo.
02802     if (Valid)
02803     {
02804         if (NumChanges > 0)
02805         {
02806             // Accumulate the bounds of the changed point[s]
02807             DocRect NewBounds(NewPos[0].Coordinate.x, NewPos[0].Coordinate.y,
02808                                 NewPos[0].Coordinate.x, NewPos[0].Coordinate.y);
02809 
02810             for (INT32 loop = 1; loop < NumChanges; loop++)
02811                 NewBounds.IncludePoint(NewPos[loop].Coordinate);
02812 
02813             // Expand the pasteboard as necessary to include any moved points
02814             // If this doesn't work, we'll tell the user below that the points still fall off
02815             // the available pasteboard area.
02816             BOOL AllOK = CurrentSpread->ExpandPasteboardToInclude(NewBounds);
02817 
02818             // Check that the new coords are on the spread
02819             DocRect SpreadBounds = CurrentSpread->GetPasteboardRect(FALSE);
02820             SpreadBounds = SpreadBounds.ToSpread(CurrentSpread, DocView::GetSelected());
02821 
02822             if (AllOK)
02823             {
02824                 MovePointsParams MoveParams(CurrentNodePath, NewPos, NumChanges);
02825 
02826                 OpDescriptor* pOpDesc = OpDescriptor::FindOpDescriptor(CC_RUNTIME_CLASS(OpMovePathPoint));
02827                 if (pOpDesc != NULL)
02828                 {
02829                     // Invoke the operation
02830                     pOpDesc->Invoke(&MoveParams);
02831 
02832                     // If the endpoint has been moved then scroll to show it
02833                     if (EndPointMoved == TRUE)
02834                     {
02835                         DocView* pDocView = DocView::GetSelected();
02836                         if (pDocView != NULL)
02837                         {
02838                             pDocView->ScrollToShowWithMargin(&NewEndPoint);
02839                         }
02840                     }
02841                 }
02842             }
02843             else
02844             {
02845                 Error::SetError(_R(IDE_MOVEPOINTSOFFSPREAD));
02846                 InformError();
02847                 UpdatePositionFields();
02848             }
02849         }
02850     }
02851     else
02852     {
02853         // Positions were not correct.  Inform user and reset
02854         Error::SetError(_R(IDE_PATHMOVE_DUFFFIELD));
02855         InformError();
02856         UpdatePositionFields();
02857     }
02858     return TRUE;
02859 }
02860 
02861 
02862 
02863 /********************************************************************************************
02864 >   void BezToolInfoBarOp::ScanPointsForToggleSmooth(BOOL MakeSmooth, BOOL WithConstrain)
02865 
02866     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02867     Created:    28/9/94
02868     Inputs:     MakeSmooth - TRUE if selected cusp points should be made smooth.
02869                              FALSE if selected smooth points should be made cusps.
02870                 WithConstrain - TRUE if the control points should not be moved when making
02871                                 smooth cusp
02872     Returns:    -
02873     Purpose:    Called by the click handlers for the smooth and cusp buttons.  Calls 
02874                 DoTogglePoint to do the work.
02875     SeeAlso:    OpToggleSmooth::DoTogglePoint
02876 ********************************************************************************************/
02877 void BezToolInfoBarOp::ScanPointsForToggleSmooth(BOOL MakeSmooth, BOOL WithConstrain)
02878 {
02879     OpToggleSmooth* pOpToggle = new OpToggleSmooth;
02880     if (!pOpToggle)
02881     {   // Inform the user that we are out of memory
02882         InformError();
02883     }
02884     else
02885     {   // Call the function that actually does something
02886         pOpToggle->DoTogglePoint(NULL, -1, NULL, MakeSmooth, WithConstrain);
02887     }
02888 }
02889 
02890 
02891 
02892 /********************************************************************************************
02893 
02894 >   BOOL BezToolInfoBarOp::ScanPointsForJoins(BOOL* AllSmooth, BOOL* AllCusp)
02895 
02896     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02897     Created:    28/9/94
02898     Inputs:     AllSmooth - pointer to a BOOL.
02899                 AllCusp - pointer to a BOOL.
02900     Returns:    *AllSmooth is set to TRUE if all selected points are smooth.
02901                 *AllCusp is set to TRUE if all selected points are not smooth.
02902                 FALSE if there are no selected objects, otherwise true
02903     Purpose:    Scan the selected points on all paths to see the type of the points.
02904 
02905 ********************************************************************************************/
02906 
02907 BOOL BezToolInfoBarOp::ScanPointsForJoins(BOOL* AllSmooth, BOOL* AllCusp)
02908 {
02909     ERROR2IF(pBezTool==NULL, FALSE, "No tool pointer");
02910 
02911     SelRange*   Selected = GetApplication()->FindSelection();
02912     Node*       pNode = Selected->FindFirst();
02913     BOOL        FoundSome = FALSE;
02914 
02915     *AllSmooth = TRUE;
02916     *AllCusp = TRUE;
02917     
02918     while (pNode != NULL)
02919     {
02920         NodePath* pSelected = pBezTool->FindEditablePath(pNode);
02921 
02922         if (pSelected)
02923         {
02924             Path*   ThisPath = &(pSelected->InkPath);
02925             INT32   UsedSlots = ThisPath->GetNumCoords();
02926             PathFlags*  Flags = ThisPath->GetFlagArray();
02927 
02928             for (INT32 i=0; i<UsedSlots; i++)
02929             {
02930                 if (Flags[i].IsSelected && Flags[i].IsEndPoint)
02931                 {
02932                     if (Flags[i].IsRotate)
02933                         *AllCusp = FALSE;
02934                     else
02935                         *AllSmooth = FALSE;
02936                     FoundSome = TRUE;
02937                 }
02938             }
02939         }
02940         pNode = Selected->FindNext(pNode);
02941     }
02942     if (!FoundSome)
02943     {
02944         *AllSmooth = FALSE;
02945         *AllCusp = FALSE;
02946         return FALSE;
02947     }
02948     return TRUE;
02949 }
02950 
02951 
02952 /********************************************************************************************
02953 
02954 >   BOOL BezierTool::CyclePathPoints(BOOL Fowards)
02955 
02956     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02957     Created:    19/10/94
02958     Inputs:     Fowards - TRUE if the point selection status are to move fowards, otherwise
02959                 they move backwards.
02960     Returns:    TRUE if all went well, FALSE if an error occured
02961     Purpose:    Performs the cycle the selected points task.  The selection status of each
02962                 endpoint on selected paths is applied to the next endpoint.
02963     Errors:     -
02964     SeeAlso:    -
02965 
02966 ********************************************************************************************/
02967 
02968 BOOL BezierTool::CyclePathPoints(BOOL Fowards)
02969 {   
02970     // Obtain the current selections and the first node in the selection
02971     SelRange*   Selected = GetApplication()->FindSelection();
02972     Node*       pNode = Selected->FindFirst();
02973     NodePath*   ThisPath;
02974 
02975     // Holds the coords of the last selected point we find on our travels,
02976     // we then call DocView::ScrolToShowWithMargin to ensure that this point is visible
02977     DocCoord LastSelectedPoint;
02978     BOOL LastSelectedPointValid = FALSE;    // does LastSelectedPoint hold a valid value?
02979 
02980     while (pNode != NULL)
02981     {   // we're only interested in NodePaths which have selected points
02982         NodePath* pSelected = FindEditablePath(pNode);
02983         if (pSelected)
02984         {
02985                 // for convenience, cast the pointer to a pointer to a NodePath
02986                 ThisPath = pSelected;
02987 
02988                 // First get pointers to the arrays
02989                 PathFlags*  Flags = ThisPath->InkPath.GetFlagArray();
02990                 PathVerb*   Verbs = ThisPath->InkPath.GetVerbArray();
02991                 DocCoord*   Coords = ThisPath->InkPath.GetCoordArray();
02992                 const INT32 NumCoords = ThisPath->InkPath.GetNumCoords();
02993             
02994             if (pSelected->InkPath.IsSubSelection())
02995             {   
02996                 // Render off the current selection blobs
02997                 ThisPath->InkPath.RenderPathSelectedControlBlobs(pNode->FindParentSpread());
02998 
02999                 if (Fowards)
03000                 {   
03001                     INT32 Current = 0;
03002                     INT32 Next = 0;
03003                     BOOL MoreEndpoints = TRUE;
03004                     BOOL PrevSelected = Flags[0].IsSelected;
03005 
03006                     while (MoreEndpoints)
03007                     {
03008                         // Find the next endpoint
03009                         MoreEndpoints = ThisPath->InkPath.FindNextEndPoint(&Next);  
03010                         if (MoreEndpoints && (Verbs[Next] & PT_CLOSEFIGURE))
03011                             MoreEndpoints = ThisPath->InkPath.FindNextEndPoint(&Next);
03012                         if (MoreEndpoints)
03013                         {
03014                             BOOL temp = Flags[Next].IsSelected;
03015                             Flags[Next].IsSelected = PrevSelected;
03016                             if (Flags[Next].IsSelected)
03017                             {
03018                             LastSelectedPoint = Coords[Next];
03019                             LastSelectedPointValid = TRUE;
03020                             }
03021                             PrevSelected= temp;
03022                             Current = Next;
03023                         }
03024                         else
03025                         {
03026                             Flags[0].IsSelected = PrevSelected;
03027                             if (Flags[0].IsSelected)
03028                             {
03029                                 LastSelectedPoint = Coords[0];
03030                                 LastSelectedPointValid = TRUE;
03031                             }
03032                         }
03033                     }
03034 
03035                     // Tidy up the selection state
03036                     ThisPath->InkPath.EnsureSelection(TRUE);
03037                 }
03038                 else                                                        
03039                 {
03040                     INT32 Current = NumCoords-1;
03041                     INT32 Next = Current;
03042                     BOOL MoreEndpoints = TRUE;
03043                     BOOL PrevSelected = Flags[Current].IsSelected;
03044                     BOOL ClosedPath = (Verbs[Next] & PT_CLOSEFIGURE);
03045 
03046                     while (MoreEndpoints)
03047                     {
03048                         // Find the previous endpoint
03049                         MoreEndpoints = ThisPath->InkPath.FindPrevEndPoint(&Next);  
03050                         if (MoreEndpoints && ClosedPath && (Verbs[Next] == PT_MOVETO))
03051                             MoreEndpoints = ThisPath->InkPath.FindPrevEndPoint(&Next);
03052                         if (MoreEndpoints)
03053                         {
03054                             BOOL temp = Flags[Next].IsSelected;
03055                             Flags[Next].IsSelected = PrevSelected;
03056                             if (Flags[Next].IsSelected)
03057                             {
03058                                 LastSelectedPoint = Coords[Next];
03059                                 LastSelectedPointValid = TRUE;
03060                             }
03061                             PrevSelected = temp;
03062                             Current = Next;
03063                         }
03064                         else
03065                         {
03066                             Flags[NumCoords-1].IsSelected = PrevSelected;
03067                             if (Flags[NumCoords-1].IsSelected)
03068                             {
03069                                 LastSelectedPoint = Coords[NumCoords-1];
03070                                 LastSelectedPointValid = TRUE;
03071                             }
03072                         }
03073                     }
03074 
03075                     // Tidy up the selection state
03076                     ThisPath->InkPath.EnsureSelection(FALSE);
03077                 }
03078 
03079             }
03080             // If no SubSelection
03081             else
03082             {
03083                 if (Fowards) 
03084                 {
03085                     Flags[0].IsSelected = TRUE;
03086                     ThisPath->InkPath.EnsureSelection(TRUE);
03087                 }
03088                 else
03089                     Flags[NumCoords-1].IsSelected = TRUE; 
03090                     ThisPath->InkPath.EnsureSelection(FALSE);
03091             }
03092     
03093             // Render on the new selection blobs
03094             ThisPath->InkPath.RenderPathSelectedControlBlobs(pNode->FindParentSpread());
03095         }
03096         pNode = Selected->FindNext(pNode);
03097     }
03098     DialogBarOp::SetSystemStateChanged();       
03099     if (LastSelectedPointValid)
03100     {
03101         DocView* pDocView = DocView::GetSelected();
03102         if (pDocView != NULL)
03103         {
03104             pDocView->ScrollToShowWithMargin(&LastSelectedPoint);
03105         }
03106     }
03107     return TRUE;
03108 }
03109 
03110 
03111 /********************************************************************************************
03112 
03113 >   BOOL BezierTool::HomePathPoints()
03114 
03115     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
03116     Created:    5/1/94
03117     Inputs:     -
03118     Returns:    TRUE if all went well, FALSE if an error occured
03119     Purpose:    Moves the selected endpoints back along the selected path(s) so that the first
03120                 point is selected.  The pattern of selection remains the same.
03121     Errors:     -
03122     SeeAlso:    BezierTool::EndPathPoints
03123 
03124 ********************************************************************************************/
03125 BOOL BezierTool::HomePathPoints()
03126 {   
03127     // Obtain the current selections and the first node in the selection
03128     SelRange*   Selected = GetApplication()->FindSelection();
03129     Node*       pNode = Selected->FindFirst();
03130     NodePath*   ThisPath;
03131 
03132     // Variable to hold the position of the last 'first point'. This is updated as we
03133     // loop through the selection until it holds the first point of the last line
03134     // in the selection with subselection.
03135     DocCoord LastFirstPoint;
03136     BOOL LastFirstPointValid = FALSE;
03137 
03138     while (pNode != NULL)
03139     {   // we're only interested in NodePaths which have selected points
03140         NodePath* pSelected = FindEditablePath(pNode);
03141         if (pSelected) 
03142         {
03143             // for convenience, cast the pointer to a pointer to a NodePath
03144             ThisPath = pSelected;
03145 
03146             // First get pointers to the arrays
03147             PathFlags*  Flags = ThisPath->InkPath.GetFlagArray();
03148             PathVerb*   Verbs = ThisPath->InkPath.GetVerbArray();
03149             DocCoord*   Coords = ThisPath->InkPath.GetCoordArray();
03150             INT32   NumCoords = ThisPath->InkPath.GetNumCoords()-1;
03151             // We only need to bother if the first endpoint is not selected!
03152             if (pSelected->InkPath.IsSubSelection())        
03153             {
03154                 if (!Flags[0].IsSelected)
03155                 {
03156                     // Render off the current selection blobs
03157                     ThisPath->InkPath.RenderPathSelectedControlBlobs(pNode->FindParentSpread());
03158 
03159                     // Find the first selected point
03160                     INT32 Offset = 0;
03161                     while ((Offset <= NumCoords) && !(Flags[Offset].IsSelected && Flags[Offset].IsEndPoint))
03162                     {
03163                         Offset ++;
03164                     }
03165 
03166                     ERROR3IF(Offset > NumCoords,"No selected endpoint found when there was one");
03167                 
03168                     // Now we can move the selection back to the start
03169                     BOOL MoreEndpoints = TRUE;
03170                     INT32 Current = 0;
03171 
03172                     while (MoreEndpoints)
03173                     {
03174                         Flags[Current].IsSelected = Flags[Offset].IsSelected;
03175                         MoreEndpoints = ThisPath->InkPath.FindNextEndPoint(&Offset);    
03176                         if (Verbs[Offset] & PT_CLOSEFIGURE)
03177                             MoreEndpoints = ThisPath->InkPath.FindNextEndPoint(&Offset);
03178                         if (MoreEndpoints)
03179                         {
03180                             ThisPath->InkPath.FindNextEndPoint(&Current);   
03181                             if (Verbs[Current] & PT_CLOSEFIGURE)
03182                             ThisPath->InkPath.FindNextEndPoint(&Current);   
03183                         }
03184                         else
03185                             Current ++;
03186                     }
03187 
03188                     // Unselect the remaining endpoints from Current onwards
03189                     while (Current <= NumCoords)
03190                     {
03191                         Flags[Current++].IsSelected = FALSE;
03192                     }
03193 
03194                     // Now fix up the selection status of the path
03195                     ThisPath->InkPath.EnsureSelection(TRUE);
03196 
03197                     // Render on the new selection blobs
03198                     ThisPath->InkPath.RenderPathSelectedControlBlobs(pNode->FindParentSpread());
03199                 }
03200 
03201                 // current pNode is selected and has a seubselection, so update FirstPointCoord to
03202                 // point to the first end point of this path
03203                 LastFirstPoint = Coords[0];
03204                 LastFirstPointValid = TRUE;
03205             }       
03206             else
03207             {
03208                 Flags [0].IsSelected = TRUE;
03209                 // Now fix up the selection status of the path
03210                 ThisPath->InkPath.EnsureSelection(TRUE);
03211                 // Render on the new selection blobs
03212                 ThisPath->InkPath.RenderPathSelectedControlBlobs(pNode->FindParentSpread());    
03213             }
03214         }
03215         pNode = Selected->FindNext(pNode);
03216     }
03217     DialogBarOp::SetSystemStateChanged();       
03218 
03219     // scroll to show the last first point (if it exists) at the coordinates we remembered earlier
03220     if (LastFirstPointValid)
03221     {
03222         DocView* pDocView = DocView::GetSelected();
03223         if (pDocView != NULL)
03224         {
03225             pDocView->ScrollToShowWithMargin(&LastFirstPoint);
03226         }
03227     }
03228 
03229     return TRUE;
03230 }
03231 
03232 /********************************************************************************************
03233 
03234 >   BOOL BezierTool::EndPathPoints()
03235 
03236     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
03237     Created:    5/1/94
03238     Inputs:     -
03239     Returns:    TRUE if all went well, FALSE if an error occured
03240     Purpose:    Moves the selected endpoints foward along the selected path(s) so that the last
03241                 point is selected.  The pattern of selection remains the same.
03242     Errors:     -
03243     SeeAlso:    BezierTool::HomePathPoints
03244 
03245 ********************************************************************************************/
03246 
03247 BOOL BezierTool::EndPathPoints()
03248 {   
03249     // Obtain the current selections and the first node in the selection
03250     SelRange*   Selected = GetApplication()->FindSelection();
03251     Node*       pNode = Selected->FindFirst();
03252     NodePath*   ThisPath;
03253 
03254     // Variable to hold the position of the last 'end point'. This is updated as we
03255     // loop through the selection until it holds the end point of the last line
03256     // in the selection with subselection.
03257     DocCoord LastEndPoint;
03258     BOOL LastEndPointValid = FALSE;
03259 
03260     while (pNode != NULL)
03261     {   // we're only interested in NodePaths which have selected points
03262             NodePath* pSelected = FindEditablePath(pNode);
03263             if (pSelected)
03264             {
03265                 // for convenience, cast the pointer to a pointer to a NodePath
03266                 ThisPath = pSelected;
03267 
03268                 // First get pointers to the arrays
03269                 PathFlags*  Flags = ThisPath->InkPath.GetFlagArray();
03270                 PathVerb*   Verbs = ThisPath->InkPath.GetVerbArray();
03271                 DocCoord*   Coords = ThisPath->InkPath.GetCoordArray();
03272                 INT32   NumCoords = ThisPath->InkPath.GetNumCoords()-1;
03273                 if (pSelected->InkPath.IsSubSelection() ) 
03274                 {
03275                     // We only need to bother if the last endpoint is not selected!
03276                     if (!Flags[NumCoords].IsSelected)
03277                     {
03278                         // Render off the current selection blobs
03279                         ThisPath->InkPath.RenderPathSelectedControlBlobs(pNode->FindParentSpread());
03280 
03281                         // Find the last selected point
03282                         INT32 Offset = NumCoords;
03283                         while ((Offset > -1) && !(Flags[Offset].IsSelected && Flags[Offset].IsEndPoint))
03284                     
03285                         {
03286                             Offset--;
03287                         }
03288 
03289                         ERROR3IF(Offset == -1,"No selected endpoint found when there was one");
03290 
03291                         // Now we can move the selection on to the end
03292                         INT32 Current = NumCoords;
03293                         BOOL MoreEndpoints = TRUE;
03294 
03295                         while (MoreEndpoints)
03296                         {
03297                             Flags[Current].IsSelected = Flags[Offset].IsSelected;
03298                             MoreEndpoints = ThisPath->InkPath.FindPrevEndPoint(&Offset);    
03299                             if (MoreEndpoints && (Verbs[Offset] & PT_CLOSEFIGURE))
03300                                 MoreEndpoints = ThisPath->InkPath.FindPrevEndPoint(&Offset);
03301                             if (MoreEndpoints)
03302                             {
03303                                 ThisPath->InkPath.FindPrevEndPoint(&Current);   
03304                                 if (Verbs[Current] & PT_CLOSEFIGURE)
03305                                     ThisPath->InkPath.FindPrevEndPoint(&Current);   
03306                             }   
03307                             else
03308                                 Current --;
03309                         }
03310 
03311                         // Unselect the remaining endpoints
03312                         while (Current > -1)
03313                         {
03314                             Flags[Current--].IsSelected = FALSE;
03315                         }
03316 
03317                         // Now fix up the selections so if the starts of subpaths are selected then so are the ends
03318                         ThisPath->InkPath.EnsureSelection(FALSE);
03319     
03320                         // Render on the new selection blobs
03321                         ThisPath->InkPath.RenderPathSelectedControlBlobs(pNode->FindParentSpread());
03322                     }
03323                 
03324                     // this path is in the selection, and has a subselection, so remember its end point
03325                     // position, as it may be the last end point
03326                     LastEndPoint = Coords[NumCoords];
03327                     LastEndPointValid = TRUE;
03328             }
03329 
03330             else
03331             {
03332                     Flags[NumCoords].IsSelected = TRUE;
03333                     // Now fix up the selections so if the starts of subpaths are selected then so are the ends
03334                     ThisPath->InkPath.EnsureSelection(FALSE);
03335                     // Render on the new selection blobs
03336                     ThisPath->InkPath.RenderPathSelectedControlBlobs(pNode->FindParentSpread());        
03337             }                               
03338         }
03339         pNode = Selected->FindNext(pNode);
03340     }
03341     DialogBarOp::SetSystemStateChanged();       
03342 
03343     // scroll to show the last end point (if it exists) at the coordinates we remembered earlier
03344     if (LastEndPointValid)
03345     {
03346         DocView* pDocView = DocView::GetSelected();
03347         if (pDocView != NULL)
03348         {
03349             pDocView->ScrollToShowWithMargin(&LastEndPoint);
03350         }
03351     }
03352 
03353     return TRUE;
03354 }
03355 
03356 
03357 
03358 /********************************************************************************************
03359 
03360 >   BezierTool::RetroSmoothChanging(double smooth)
03361 
03362     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
03363     Created:    11/11/94
03364     Inputs:     smooth = smoothness level to use
03365     Returns:
03366     Purpose:    Scan the selection for a path to smooth. If we find only one path object
03367                 selected then we should tell the retro smooth code to begin smoothing.
03368                 Arh!, actually we need to be able to keep track of the selected object?
03369                 possibly.... Surely its fast enough already to find the selected
03370                 objects, there must be some caching going on somewhere. Ok, best thing to
03371                 do is to make sure theres only one selected object and ignore the slider
03372                 change if there isn't
03373     
03374 ********************************************************************************************/
03375 
03376 void BezierTool::RetroSmoothChanging(double smooth)
03377 {
03378 
03379     if (!RetroFlag)
03380     {
03381         Node* pNode = OneNodePathSelected();
03382         if (pNode == NULL)
03383             return;
03384     
03385         Spread* pSpread = pNode->FindParentSpread();
03386         if (pSpread == NULL)
03387             return;
03388 
03389         RetroFlag = TRUE;
03390         pRetroNode = pNode;
03391         pRetroSpread = pSpread;
03392 
03393     }
03394 
03395     // ok, at this stage we should have a path to work with.
03396     pSmooth->Changing((NodePath*)pRetroNode, pRetroSpread, smooth);
03397 }
03398 
03399 
03400 
03401 /********************************************************************************************
03402 
03403 >   BezierTool::RetroSmoothFinished()
03404 
03405     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
03406     Created:    11/11/94
03407     Inputs:     
03408     Returns:
03409     Purpose:    Calls the retro smooth finalisation code to perform the completed
03410                 smoothing action
03411     
03412 ********************************************************************************************/
03413 
03414 void BezierTool::RetroSmoothFinished()
03415 {
03416     // ok, at this stage we should have a path to work with.
03417     if (RetroFlag)
03418     {
03419         pSmooth->Finished();
03420         RetroFlag = FALSE;
03421     }
03422 }
03423 
03424 
03425 /********************************************************************************************
03426 
03427 >   Node* BezierTool::OneNodePathSelected()
03428 
03429     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
03430     Created:    22/11/94
03431     Inputs:     -
03432     Returns:    a pointer to the node if there is a single node path object selected
03433                 NULL if no nodepath or more than one object is selected
03434     Purpose:    Checks the selection and determins whether exactly one nodepath object
03435                 is selected.
03436 
03437 ********************************************************************************************/
03438 
03439 Node* BezierTool::OneNodePathSelected()
03440 {
03441     // Find the selected range of objects
03442     SelRange* Selected = GetApplication()->FindSelection();
03443 
03444     Node* pNode = Selected->FindFirst();
03445     if (pNode == NULL)
03446         return NULL;
03447     Node* qNode = Selected->FindNext(pNode);
03448     if (qNode != NULL)
03449         return NULL;
03450 
03451     // ok there's only one selected object so what kind is it?
03452     if (pNode->GetRuntimeClass() != CC_RUNTIME_CLASS(NodePath))
03453         return NULL;
03454 
03455     return pNode;
03456 }
03457 
03458 
03459 /********************************************************************************************
03460 
03461     void BezierTool::ResetRetroSlider()
03462 
03463     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
03464     Created:    22/11/94
03465     Inputs:     -
03466     Returns:
03467     Purpose:    There has been a change in the selection so we need to update the retro
03468                 slider state
03469 
03470 ********************************************************************************************/
03471 
03472 void BezierTool::ResetRetroSlider()
03473 {
03474     Node* pNode = OneNodePathSelected();
03475     Path* pPath = NULL;
03476 
03477     if (pNode)
03478         pPath = &(((NodePath*)pNode)->InkPath);
03479     
03480     if (pPath && (!pPath->IsSubSelection()))
03481         pPath=NULL;
03482 
03483     UpdateRetroSlider(pPath); 
03484 }
03485 
03486 
03487 
03488 /********************************************************************************************
03489 
03490     void BezierTool::UpdateRetroSlider(Path* pPath)
03491 
03492     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
03493     Created:    22/11/94
03494     Inputs:     pPath = a path pointer, if NULL then the slider is set to zero
03495     Purpose:    Sets the retro smooth slider position for a given path.
03496 
03497 ********************************************************************************************/
03498 
03499 void BezierTool::UpdateRetroSlider(Path* pPath)
03500 {
03501     if (pSmooth && pPath)
03502     {
03503         double acc = pSmooth->ReturnCachedAccuracy(pPath);
03504         INT32 lacc = (INT32) (acc+0.5);
03505         RetroSmoothSet(lacc, TRUE);
03506     }
03507     else
03508         RetroSmoothSet(0, FALSE);
03509 }
03510 
03511 
03512 
03513 /********************************************************************************************
03514 
03515     void BezierTool::RetroSmoothSet(INT32 percent, BOOL Enabled)
03516 
03517     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
03518     Created:    11/11/94
03519     Inputs:     percent = the value to set the smooth slider to (0..100)
03520     Returns:
03521     Purpose:    Set the position of the retro smooth slider, given a percentage value.
03522                 The function will also update the percent text field.
03523 
03524 ********************************************************************************************/
03525 
03526 void BezierTool::RetroSmoothSet(INT32 percent, BOOL Enabled)
03527 {
03528     // Set the pip position
03529     if (pBezToolInfoBarOp)
03530     {
03531         if (pBezToolInfoBarOp->IsOpen())
03532         {
03533             pBezToolInfoBarOp->SetLongGadgetValue(_R(IDC_SMOOTHSLIDER), percent);
03534             
03535             // Set the percentage string
03536             TCHAR Str[32];
03537             String_32 jcf(_R(IDS_PERCENT_FORMAT));
03538             camSnprintf(Str, 31, jcf, (INT32) percent);
03539             String_32 PercentStr(Str);
03540             pBezToolInfoBarOp->SetStringGadgetValue(_R(IDC_SMOOTHPERCENT), PercentStr);
03541 
03542             pBezToolInfoBarOp->EnableGadget(_R(IDC_SMOOTHSLIDER), Enabled);
03543             pBezToolInfoBarOp->EnableGadget(_R(IDC_SMOOTHPERCENT), Enabled);
03544 
03545         }
03546     }
03547 }
03548 
03549 
03550 
03551 
03552 /********************************************************************************************
03553 
03554 >   void BezierTool::RetroSmoothInvalidate()
03555 
03556     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
03557     Created:    22/11/94
03558     Inputs:     -
03559     Returns:
03560     Purpose:    Invalidate the retro smooth cached information.
03561 
03562 ********************************************************************************************/
03563 
03564 void BezierTool::RetroSmoothInvalidate()
03565 {
03566     // set the slider to full
03567     RetroSmoothSet(100, TRUE);
03568     // and tell the reto smooth op.
03569     if (pSmooth != NULL)
03570         pSmooth->Invalidate();
03571 }
03572 
03573 
03574 
03575 /*****************************************************************************
03576 
03577 >   virtual BOOL BezierTool::GetStatusLineText(String_256* ptext, Spread* pSpread,
03578                                              DocCoord DocPos, ClickModifiers ClickMods);
03579 
03580     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
03581     Created:    08/12/94
03582     Inputs:     pSpread   - pointer to spread under mouse (else NULL)
03583                 DocPos    - position of mouse in doc (in spread coords)
03584                 ClickMods - mouse click modifiers
03585     Outputs:    ptext - text for status line
03586     Returns:    TRUE if outputting valid text
03587     Purpose:    generate up-to-date text for the status line (called on idles)
03588     Errors:     ERROR3 if ptext is NULL
03589 
03590 *****************************************************************************/
03591 
03592 BOOL BezierTool::GetStatusLineText(String_256* ptext, Spread* pSpread, DocCoord DocPos, ClickModifiers ClickMods)
03593 {
03594     ERROR2IF(ptext==NULL,FALSE,"ptext was NULL");
03595 
03596     *ptext = "";
03597 
03598     GenerateStatusLineText(ptext, pSpread, DocPos, ClickMods);
03599 
03600     return TRUE;
03601 }
03602 
03603 
03604 
03605 /*****************************************************************************
03606 
03607 >   void BezierTool::GenerateStatusLineText(String_256* ptext, Spread* pSpread,
03608                                              DocCoord DocPos, ClickModifiers ClickMods);
03609 
03610     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
03611     Created:    08/12/94
03612     Inputs:     pSpread   - pointer to spread under mouse (else NULL)
03613                 DocPos    - position of mouse in doc (in spread coords)
03614                 ClickMods - mouse click modifiers
03615     Outputs:    ptext - text for status line
03616     Returns:    -
03617     Purpose:    To find the string to display on the status line given the
03618                 current mouse position
03619     Errors:     -
03620 
03621 *****************************************************************************/
03622 
03623 void BezierTool::GenerateStatusLineText(String_256* ptext, Spread* pSpread, DocCoord coord, ClickModifiers mods)
03624 {
03625     // Call DetermineClickEffect to see what a click will do at this position
03626 
03627     INT32 PathPosition;         // Needed to receive a return, not used otherwise
03628     NodeRenderableInk* node;    // again, only temporary
03629     INT32 NumPts;               // Number of selected points
03630     INT32 NumPaths;             // Number of selected paths
03631     clickeffect WhatToDo = DetermineClickEffect(coord, pSpread, &node, &PathPosition, &NumPaths, &NumPts);
03632 
03633     switch (WhatToDo)
03634     {
03635     case AddSegment:
03636         if (CreateCurve)
03637             ptext->Load(_R(IDS_ADD_SEGMENT),Tool::GetModuleID(GetID()));
03638         else
03639             ptext->Load(_R(IDS_ADDLINESEGMENT),Tool::GetModuleID(GetID()));
03640         break;
03641     case NewPath:
03642         if (FloatingEndpoint)
03643         {
03644             if (CreateCurve)
03645             {
03646                 if (NumPts == 0)
03647                     ptext->Load(_R(IDS_CLICK_TO_MAKE_NEW),Tool::GetModuleID(GetID()));
03648                 else
03649                     ptext->Load(_R(IDS_CLICK_TO_MAKE_NEW),Tool::GetModuleID(GetID()));
03650             }
03651             else
03652             {
03653                 ptext->Load(_R(IDS_CLICKMAKENEWLINE),Tool::GetModuleID(GetID()));
03654             }
03655         }
03656         else
03657         {
03658             if (CreateCurve)
03659             {
03660                 if (NumPts == 0)
03661                     ptext->Load(_R(IDS_STARTNEWCURVENOSEL),Tool::GetModuleID(GetID()));
03662                 else
03663                     ptext->Load(_R(IDS_CREATE_NEW_PATH),Tool::GetModuleID(GetID()));
03664             }
03665             else
03666             {
03667                 if (NumPts == 0)
03668                     ptext->Load(_R(IDS_STARTNEWLINENOSEL),Tool::GetModuleID(GetID()));
03669                 else
03670                     ptext->Load(_R(IDS_STARTNEWLINE),Tool::GetModuleID(GetID()));
03671             }
03672         }
03673         break;
03674     case OnPoint:
03675         // There are various different combinations of selected/unselected, which require
03676         // different status line messages
03677         {
03678             NodePath* ThisPath = (NodePath*)node;
03679 //          PathVerb* Verbs = ThisPath->InkPath.GetVerbArray();
03680             PathFlags* Flags = ThisPath->InkPath.GetFlagArray();
03681             INT32 ptype = 0;
03682             if (Flags[PathPosition].IsSelected)
03683                 ptype |= 1;
03684             if (Flags[PathPosition].IsEndPoint)
03685                 ptype |= 2;
03686             if (Flags[PathPosition].IsRotate)
03687                 ptype |= 4;
03688 
03689             // Now ptype holds a number from 0 to 7 which completely describes what type of
03690             // point this is, so we can use that to select the correct string
03691             switch(ptype)
03692             {
03693                 case 0:     //Unselected, unsmoothed control point
03694                 case 4:     // UnSelected, smoothed control point 
03695                 case 1:     // Selected, unsmoothed control point
03696                 case 5:     // Selected, smoothed control point
03697                     ptext->Load(_R(IDS_SELCONTROL),Tool::GetModuleID(GetID()));
03698                     break;
03699                 case 2:     // UnSelected, unsmoothed end point
03700                     ptext->Load(_R(IDS_UNSELCUSP),Tool::GetModuleID(GetID()));
03701                     break;
03702                 case 3:     // Selected, unsmoothed end point
03703                     ptext->Load(_R(IDS_SELCUSP),Tool::GetModuleID(GetID()));
03704                     break;
03705                 case 6:     // UnSelected, smoothed end point
03706                     ptext->Load(_R(IDS_UNSELSMOOTH),Tool::GetModuleID(GetID()));
03707                     break;
03708                 case 7:     // Selected, smoothed end point
03709                     ptext->Load(_R(IDS_SELSMOOTH),Tool::GetModuleID(GetID()));
03710                     break;
03711             }
03712         }
03713         break;
03714     case ReshapeLine:
03715         ptext->Load(_R(IDS_RESHAPE_LINE),Tool::GetModuleID(GetID()));
03716         break;
03717     case ClosePath:
03718         ptext->Load(_R(IDS_CLOSEPATH),Tool::GetModuleID(GetID()));
03719         break;
03720     }
03721 }
03722 
03723 
03724 
03725 /*****************************************************************************
03726 
03727 >   void BezToolInfoBarOp::UpdateEditFieldsFromPath(Path*   pPath,
03728                                                     Spread* pSpread,
03729                                                     INT32   Index
03730                                                     BOOL    ForceUpdate,
03731                                                     BOOL    EnableData[]);
03732 
03733     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
03734     Created:    25/1/95
03735     Inputs:     pPath - points to the path to display the data from
03736                 pSpread - pointer to spread to use in corordinate display
03737                 Index - the index of the endpoint in the path being edited
03738                 ForceUpdate - TRUE if the changed gadgets should be immediatly redraw
03739                 EnableData - Points to an array of BOOLs to gadget enabling
03740                              Can be NULL
03741     Outputs:    Items in EnableData are set to FALSE if they should be greyed.
03742     Returns:    -
03743     Purpose:    Updates the contents of the edit fields in the infobar from
03744                 the positions in the path
03745     Errors:     -
03746 
03747 *****************************************************************************/
03748 void BezToolInfoBarOp::UpdateEditFieldsFromPath(Path* pPath, Spread* pSpread, INT32 Index, BOOL ForceUpdate,  BOOL EnableData[NumberIconIDs])
03749 {
03750     // Error Checks
03751     if ((WindowID == NULL) || (pPath == NULL) || (pSpread == NULL) || (pBezTool == NULL))
03752         return;
03753 
03754     INT32 LineAnglePrevIndex = GetPrevLineIndex(pPath, Index);
03755     INT32 LineAngleNextIndex = GetNextLineIndex(pPath, Index);
03756     if (LineAnglePrevIndex==-2 || LineAngleNextIndex==-2)
03757     {
03758         InformError();
03759         return;
03760     }
03761 
03762 //  PathVerb* Verbs = pPath->GetVerbArray();
03763     DocCoord* Coords = pPath->GetCoordArray();
03764 //  INT32 UsedSlots = pPath->GetNumCoords();
03765     String_8 NullString(_T(""));
03766     String_8 UpperString(_T(""));
03767     String_8 LowerString(_T(""));
03768 
03769     SetEditPosition(_R(IDC_PATH_EDIT_ENDPOINTX), _R(IDC_PATH_EDIT_ENDPOINTY), Coords[Index], pSpread, ForceUpdate);
03770 
03771     INT32 FirstIndex = pPath->FindPrevControlPoint(Index);
03772     INT32 SecondIndex = pPath->FindNextControlPoint(Index);
03773 
03774     if (FirstIndex != -1)
03775     {
03776         // It's a curve
03777         SetEditPosition(_R(IDC_PATH_EDIT_FIRSTX), _R(IDC_PATH_EDIT_FIRSTY), Coords[FirstIndex], CurrentSpread, ForceUpdate);
03778 
03779         // Set the field indicators to XY
03780         UpperString.Load(_R(IDS_LINE_X_FIELD),Tool::GetModuleID(pBezTool->GetID()));
03781         LowerString.Load(_R(IDS_LINE_Y_FIELD),Tool::GetModuleID(pBezTool->GetID()));
03782         // Set the bubble and status help on the fields
03783         SetGadgetHelp(_R(IDC_PATH_EDIT_FIRSTX), _R(IDBBL_PATH_PREVCONTROLPOINT), _R(IDS_PATH_PREVCONTROLPOINT_X)); 
03784         SetGadgetHelp(_R(IDC_PATH_EDIT_FIRSTY), _R(IDBBL_PATH_PREVCONTROLPOINT), _R(IDS_PATH_PREVCONTROLPOINT_Y)); 
03785         SetGadgetHelp(_R(IDC_PATH_BUMP_FIRSTX_LESS), _R(IDBBL_PATH_BUMPLOWER_X), _R(IDS_PATH_BUMPCOORDLOWER_X)); 
03786         SetGadgetHelp(_R(IDC_PATH_BUMP_FIRSTX_MORE), _R(IDBBL_PATH_BUMPHIGHER_X), _R(IDS_PATH_BUMPCOORDHIGHER_X)); 
03787         SetGadgetHelp(_R(IDC_PATH_BUMP_FIRSTY_MORE), _R(IDBBL_PATH_BUMPLOWER_Y), _R(IDS_PATH_BUMPCOORDLOWER_Y)); 
03788         SetGadgetHelp(_R(IDC_PATH_BUMP_FIRSTY_LESS), _R(IDBBL_PATH_BUMPHIGHER_Y), _R(IDS_PATH_BUMPCOORDHIGHER_Y)); 
03789     }
03790     else
03791     {
03792         // It's a line
03793         if (LineAnglePrevIndex!=-1)
03794         {
03795             const double Distance = Coords[Index].Distance(Coords[LineAnglePrevIndex]);
03796             const double Angle = GetAngle(Coords[Index], Coords[LineAnglePrevIndex])*(180/PI);
03797 
03798             SetEditLength(_R(IDC_PATH_EDIT_FIRSTX), (INT32)Distance, pSpread, ForceUpdate);
03799             String_16 strX = GetStringGadgetValue(_R(IDC_PATH_EDIT_FIRSTX));
03800             DimScale* pDimScale = DimScale::GetPtrDimScale((Node*) CurrentSpread);
03801             if (pDimScale != NULL)
03802                 pDimScale->ConvertToMillipoints(strX, &LastShownPrevLength); 
03803             SetDoubleGadgetValue(_R(IDC_PATH_EDIT_FIRSTY), Angle, FALSE);
03804 
03805             if ((Distance == 0.0) && (EnableData != NULL))
03806                 EnableData[8] = FALSE;
03807 
03808             if (ForceUpdate)
03809                 PaintGadgetNow(_R(IDC_PATH_EDIT_FIRSTY));
03810 
03811             // Set the field indicators to LA
03812             UpperString.Load(_R(IDS_LINE_L_FIELD),Tool::GetModuleID(pBezTool->GetID()));
03813             LowerString.Load(_R(IDS_LINE_A_FIELD),Tool::GetModuleID(pBezTool->GetID()));
03814             // Set the bubble and status help on the fields
03815             SetGadgetHelp(_R(IDC_PATH_EDIT_FIRSTX), _R(IDBBL_PATH_LINELENGTH), _R(IDS_PATH_PREVLINELENGTH)); 
03816             SetGadgetHelp(_R(IDC_PATH_EDIT_FIRSTY), _R(IDBBL_PATH_LINEANGLE), _R(IDS_PATH_PREVLINEANGLE)); 
03817             SetGadgetHelp(_R(IDC_PATH_BUMP_FIRSTX_LESS), _R(IDBBL_PATH_BUMPLOWER_X), _R(IDS_PATH_BUMPLINELENGTHDOWN)); 
03818             SetGadgetHelp(_R(IDC_PATH_BUMP_FIRSTX_MORE), _R(IDBBL_PATH_BUMPHIGHER_X), _R(IDS_PATH_BUMPLINELENGTHUP)); 
03819             SetGadgetHelp(_R(IDC_PATH_BUMP_FIRSTY_MORE), _R(IDBBL_PATH_BUMPLOWER_Y), _R(IDS_PATH_BUMPLINEANGLEDOWN)); 
03820             SetGadgetHelp(_R(IDC_PATH_BUMP_FIRSTY_LESS), _R(IDBBL_PATH_BUMPHIGHER_Y), _R(IDS_PATH_BUMPLINEANGLEUP)); 
03821         }
03822         else
03823         {
03824             SetStringGadgetValue(_R(IDC_PATH_EDIT_FIRSTX), NullString, FALSE);
03825             SetStringGadgetValue(_R(IDC_PATH_EDIT_FIRSTY), NullString, FALSE);
03826             if (EnableData != NULL)
03827             {
03828                 EnableData[8] = FALSE;
03829                 EnableData[9] = FALSE;
03830                 EnableData[10] = FALSE;
03831                 EnableData[11] = FALSE;
03832             }
03833             if (ForceUpdate)
03834             {
03835                 PaintGadgetNow(_R(IDC_PATH_EDIT_FIRSTX));
03836                 PaintGadgetNow(_R(IDC_PATH_EDIT_FIRSTY));
03837             }
03838 
03839             // Clear the field indicators
03840             UpperString.Empty();
03841             LowerString.Empty();
03842             SetGadgetHelp(_R(IDC_PATH_EDIT_FIRSTX), 0, 0); 
03843             SetGadgetHelp(_R(IDC_PATH_EDIT_FIRSTY), 0, 0); 
03844             SetGadgetHelp(_R(IDC_PATH_BUMP_FIRSTX_LESS), 0, 0); 
03845             SetGadgetHelp(_R(IDC_PATH_BUMP_FIRSTX_MORE), 0, 0); 
03846             SetGadgetHelp(_R(IDC_PATH_BUMP_FIRSTY_MORE), 0, 0); 
03847             SetGadgetHelp(_R(IDC_PATH_BUMP_FIRSTY_LESS), 0, 0); 
03848         }
03849     }
03850 
03851     // Redraw the left hand text indicators
03852     SetStringGadgetValue(_R(IDC_PATH_STATIC_FIRSTX), UpperString);
03853     SetStringGadgetValue(_R(IDC_PATH_STATIC_FIRSTY), LowerString);
03854     if (ForceUpdate)
03855     {
03856         PaintGadgetNow(_R(IDC_PATH_STATIC_FIRSTX));
03857         PaintGadgetNow(_R(IDC_PATH_STATIC_FIRSTY));
03858     }
03859 
03860     // Now do the right hand edit fields
03861     if (SecondIndex != -1)
03862     {
03863         SetEditPosition(_R(IDC_PATH_EDIT_SECONDX), _R(IDC_PATH_EDIT_SECONDY), Coords[SecondIndex], pSpread, ForceUpdate);
03864 
03865         // Set the field indicators to XY
03866         UpperString.Load(_R(IDS_LINE_X_FIELD),Tool::GetModuleID(pBezTool->GetID()));
03867         LowerString.Load(_R(IDS_LINE_Y_FIELD),Tool::GetModuleID(pBezTool->GetID()));
03868             // Set the bubble and status help on the fields
03869         SetGadgetHelp(_R(IDC_PATH_EDIT_SECONDX), _R(IDBBL_PATH_NEXTCONTROLPOINT), _R(IDS_PATH_NEXTCONTROLPOINT_X)); 
03870         SetGadgetHelp(_R(IDC_PATH_EDIT_SECONDY), _R(IDBBL_PATH_NEXTCONTROLPOINT), _R(IDS_PATH_NEXTCONTROLPOINT_Y)); 
03871         SetGadgetHelp(_R(IDC_PATH_BUMP_SECONDX_LESS), _R(IDBBL_PATH_BUMPLOWER_X), _R(IDS_PATH_BUMPCOORDLOWER_X)); 
03872         SetGadgetHelp(_R(IDC_PATH_BUMP_SECONDX_MORE), _R(IDBBL_PATH_BUMPHIGHER_X), _R(IDS_PATH_BUMPCOORDHIGHER_X)); 
03873         SetGadgetHelp(_R(IDC_PATH_BUMP_SECONDY_MORE), _R(IDBBL_PATH_BUMPLOWER_Y), _R(IDS_PATH_BUMPCOORDLOWER_Y)); 
03874         SetGadgetHelp(_R(IDC_PATH_BUMP_SECONDY_LESS), _R(IDBBL_PATH_BUMPHIGHER_Y), _R(IDS_PATH_BUMPCOORDHIGHER_Y)); 
03875     }
03876     else
03877     {
03878         // It's a line
03879         if (LineAngleNextIndex!=-1)
03880         {
03881             const double Distance = Coords[Index].Distance(Coords[LineAngleNextIndex]);
03882             const double Angle = GetAngle(Coords[Index], Coords[LineAngleNextIndex])*(180/PI);
03883 
03884             SetEditLength(_R(IDC_PATH_EDIT_SECONDX), (INT32)Distance, pSpread, ForceUpdate);
03885             SetDoubleGadgetValue(_R(IDC_PATH_EDIT_SECONDY), Angle, FALSE);
03886 
03887             String_16 strX = GetStringGadgetValue(_R(IDC_PATH_EDIT_SECONDX));
03888             DimScale* pDimScale = DimScale::GetPtrDimScale((Node*) CurrentSpread);
03889             if (pDimScale != NULL)
03890                 pDimScale->ConvertToMillipoints(strX, &LastShownNextLength); 
03891 
03892             if ((Distance == 0.0) && (EnableData != NULL))
03893                 EnableData[14] = FALSE;
03894 
03895             if (ForceUpdate)
03896                 PaintGadgetNow(_R(IDC_PATH_EDIT_SECONDY));
03897 
03898             // Set the field indicators to LA
03899             UpperString.Load(_R(IDS_LINE_L_FIELD),Tool::GetModuleID(pBezTool->GetID()));
03900             LowerString.Load(_R(IDS_LINE_A_FIELD),Tool::GetModuleID(pBezTool->GetID()));
03901             // Set the bubble and status help on the fields
03902             SetGadgetHelp(_R(IDC_PATH_EDIT_SECONDX), _R(IDBBL_PATH_LINELENGTH), _R(IDS_PATH_NEXTLINELENGTH)); 
03903             SetGadgetHelp(_R(IDC_PATH_EDIT_SECONDY), _R(IDBBL_PATH_LINEANGLE), _R(IDS_PATH_NEXTLINEANGLE)); 
03904             SetGadgetHelp(_R(IDC_PATH_BUMP_SECONDX_LESS), _R(IDBBL_PATH_BUMPLOWER_X), _R(IDS_PATH_BUMPLINELENGTHDOWN)); 
03905             SetGadgetHelp(_R(IDC_PATH_BUMP_SECONDX_MORE), _R(IDBBL_PATH_BUMPHIGHER_X), _R(IDS_PATH_BUMPLINELENGTHUP)); 
03906             SetGadgetHelp(_R(IDC_PATH_BUMP_SECONDY_MORE), _R(IDBBL_PATH_BUMPLOWER_Y), _R(IDS_PATH_BUMPLINEANGLEDOWN)); 
03907             SetGadgetHelp(_R(IDC_PATH_BUMP_SECONDY_LESS), _R(IDBBL_PATH_BUMPHIGHER_Y), _R(IDS_PATH_BUMPLINEANGLEUP)); 
03908         }
03909         else
03910         {
03911             SetStringGadgetValue(_R(IDC_PATH_EDIT_SECONDX), NullString, FALSE);
03912             SetStringGadgetValue(_R(IDC_PATH_EDIT_SECONDY), NullString, FALSE);
03913             if (EnableData != NULL)
03914             {
03915                 EnableData[14] = FALSE;
03916                 EnableData[15] = FALSE;
03917                 EnableData[16] = FALSE;
03918                 EnableData[17] = FALSE;
03919             }
03920             if (ForceUpdate)
03921             {
03922                 PaintGadgetNow(_R(IDC_PATH_EDIT_SECONDX));
03923                 PaintGadgetNow(_R(IDC_PATH_EDIT_SECONDY));
03924             }
03925 
03926             // Clear the field indicators
03927             UpperString.Empty();
03928             LowerString.Empty();
03929         }
03930     }
03931 
03932     // Redraw the right hand text indicators
03933     SetStringGadgetValue(_R(IDC_PATH_STATIC_SECONDX), UpperString);
03934     SetStringGadgetValue(_R(IDC_PATH_STATIC_SECONDY), LowerString);
03935     if (ForceUpdate)
03936     {
03937         PaintGadgetNow(_R(IDC_PATH_STATIC_SECONDX));
03938         PaintGadgetNow(_R(IDC_PATH_STATIC_SECONDY));
03939     }
03940 
03941     CurrentIndex = Index;
03942     CurrentSpread = pSpread;
03943 }
03944 
03945 
03946 
03947 /*****************************************************************************
03948 
03949 >   void BezToolInfoBarOp::HandleLAFieldCommit( CGadgetID LengthID,
03950                                                 CGadgetID AngleID,
03951                                                 CGadgetID CommitID,
03952                                                 DocCoord SelectedPoint,
03953                                                 DocCoord UnselectedPoint,
03954                                                 DocCoord* Result)
03955     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
03956     Created:    31/1/95
03957     Inputs:     LengthID - gadget ID of the field with the new segment length
03958                 AngleID - gagdet ID of the field with the new angle
03959                 CommitID - gagdet ID of the field that was commited
03960                 SelectedPoint - the coord of the selected endpoint
03961                 UnselectedPoint - the coord of the unselected endpoint
03962                 Result - pointer to the new position of the selected endpoint
03963     Outputs:    Result will hold the new location of the previous/next endpoint
03964     Returns:    -
03965     Purpose:    Common code for calculating the new position of the previous or
03966                 next line endpoint from the given endpoint.
03967     Errors:     -
03968 
03969 *****************************************************************************/
03970 BOOL BezToolInfoBarOp::HandleLAFieldCommit(CGadgetID LengthID, CGadgetID AngleID, CGadgetID CommitID, 
03971                     DocCoord SelectedPoint, DocCoord UnselectedPoint, DocCoord* Result)
03972 {
03973     // Set return value to sensible value
03974     *Result = SelectedPoint;
03975 
03976     // Exit now if there is no infobar
03977     if (WindowID == NULL)
03978         return TRUE;
03979 
03980     // if the commited ID was not one of the two fields then exit
03981     if ((CommitID!=LengthID) && (CommitID!=AngleID))
03982         return TRUE;
03983 
03984     DimScale* pDimScale = DimScale::GetPtrDimScale((Node*) CurrentSpread);
03985     ERROR2IF(pDimScale == NULL, FALSE, "NULL DimScalePtr");
03986 
03987     // See what the edit fields were showing before the edit
03988     String_256 OldLength;
03989     String_256 OldAngle;
03990     const INT32 OldLengthVal = (INT32)(SelectedPoint.Distance(UnselectedPoint));
03991     const double OldAngleVal = GetAngle(UnselectedPoint, SelectedPoint)*(180/PI);
03992     pDimScale->ConvertToUnits((INT32) OldLengthVal, &OldLength);
03993     if (!Convert::DoubleToString(OldAngleVal, &OldAngle))
03994         return FALSE;
03995 
03996     // Get the current edit field strings
03997     INT32 RequiredLength = 0;
03998     double RequiredAngle = 0;
03999     BOOL Valid = TRUE;
04000     String_16 NewLength;
04001     String_16 NewAngle;
04002     if (Valid)
04003         NewLength = GetStringGadgetValue(LengthID, &Valid);
04004     if (Valid)
04005         NewAngle = GetStringGadgetValue(AngleID, &Valid);
04006 
04007     // Get the required length
04008     if (Valid && (NewLength != OldLength))
04009         Valid = pDimScale->ConvertToMillipoints(NewLength, &RequiredLength); 
04010     else
04011         RequiredLength = OldLengthVal;
04012 
04013     // Get the required angle
04014     if (Valid && (NewAngle != OldAngle))
04015     {
04016         RequiredAngle = GetDoubleGadgetValue(AngleID, -360.0, 360.0, _R(IDE_INVALID_ANGLE), &Valid);
04017         if (Valid)
04018         {
04019             // Get the angle to lie between -180 and 180
04020             while (RequiredAngle < -180)
04021                 RequiredAngle += 360;
04022             while (RequiredAngle > 180)
04023                 RequiredAngle -= 360;
04024         }
04025 
04026         // Flip the angle around
04027         RequiredAngle -= 180;
04028         if (RequiredAngle < -180)
04029             RequiredAngle += 360;
04030     }
04031     else
04032         RequiredAngle = OldAngleVal;
04033 
04034 
04035     // Generate a new position if fields have been edited
04036     if (Valid && ((RequiredLength != OldLengthVal) || (RequiredAngle != OldAngleVal)))
04037     {
04038         DocCoord NewLoc(0,0);
04039         RequiredAngle = RequiredAngle * (PI/180);
04040 
04041         NewLoc.x = (INT32)(RequiredLength*cos(RequiredAngle));
04042         NewLoc.y = (INT32)(RequiredLength*sin(RequiredAngle));
04043         *Result = UnselectedPoint + NewLoc;
04044     }
04045 
04046     return TRUE;
04047 }
04048 
04049 
04050 
04051 /*****************************************************************************
04052 
04053 >   double BezToolInfoBarOp::GetAngle(DocCoord Origin, DocCoord Offset)
04054 
04055     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
04056     Created:    31/1/95
04057     Inputs:     Origin - the point to calculate the angle around
04058                 Offset - a point on the line to calculate the angle for
04059     Outputs:    -
04060     Returns:    The angle (in radians) from the positive x-axis and the line
04061     Purpose:    Common code for calculating the angle between two endpoints
04062     Errors:     -
04063 
04064 *****************************************************************************/
04065 double BezToolInfoBarOp::GetAngle(DocCoord Origin, DocCoord Offset)
04066 {
04067     const DocCoord Outer = Offset - Origin;
04068     double Angle = atan2((double)Outer.y, (double)Outer.x);
04069 
04070     if (Angle == HUGE_VAL)
04071         return 0.0;
04072     else
04073         return Angle;
04074 }
04075 
04076 
04077 
04078 /*****************************************************************************
04079 >   BOOL BezToolInfoBarOp::HandleCoordFieldCommit(UINT32 UpperID, UINT32 LowerID, INT32 Index, DocCoord* NewPos, BOOL* Valid)
04080 
04081     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
04082     Created:    11/7/95
04083     Inputs:     UpperID - the resource ID of the upper edit field (X pos)
04084                 LowerID - the resource IS of the lower edit field (Y pos)
04085                 Index - the index of the cordinate being edited.
04086     Outputs:    NewPos - DocCoord set to the new coordinate
04087                 Valid - TRUE/FALSE if the coord is valid (ie edit fields were ok)
04088     Returns:    TRUE/FALSE for success/failure
04089     Purpose:    Common code for handling a commit in a pait of edit fields that
04090                 contain a coordinate.  If the fields haven't been edited
04091                 then no change is required
04092     Errors:     -
04093 *****************************************************************************/
04094 BOOL BezToolInfoBarOp::HandleCoordFieldCommit(UINT32 UpperID, UINT32 LowerID, INT32 Index,
04095                                                                 DocCoord* NewPos, BOOL* Valid)
04096 {
04097     // Exit now if there is no infobar
04098     if (WindowID == NULL)
04099         return TRUE;
04100 
04101     // Check parameters and member variables
04102     ERROR2IF(((CurrentNodePath == NULL) || (CurrentInkPath == NULL)), FALSE, "HandleCoordFieldCommit found CurrentPath was NULL");
04103     ERROR2IF(&(CurrentNodePath->InkPath) != CurrentInkPath, FALSE, "HandleCoordFieldCommit found InkPath/NodePath mismatch");
04104     ERROR2IF(CurrentSpread == NULL, FALSE, "HandleCoordFieldCommit found CurrentSpread was NULL");
04105     ERROR2IF(Index < 0, FALSE, "-ve index supplied to HandleCoordFieldCommit");
04106     ERROR2IF(Index >= CurrentInkPath->GetNumCoords(), FALSE, "index supplied to HandleCoordFieldCommit was too large");
04107     
04108     DocCoord PreviousPos = CurrentInkPath->GetCoordArray()[Index]; 
04109     *NewPos = PreviousPos;
04110     BOOL FieldsValid = TRUE;
04111 
04112     // Work out what the fields showed before the edit
04113     String_256  OldXText;
04114     String_256  OldYText;
04115     if (!CurrentSpread->SpreadCoordToText(&OldXText, &OldYText, PreviousPos))
04116         return FALSE;
04117 
04118     // And what is actually in them now
04119     String_256  NewXText;
04120     String_256  NewYText;
04121     DocCoord NewPosition;
04122     NewXText = GetStringGadgetValue(UpperID, &FieldsValid);
04123     if (FieldsValid) 
04124         NewYText = GetStringGadgetValue(LowerID, &FieldsValid);
04125     if (FieldsValid)
04126         FieldsValid = CurrentSpread->TextToSpreadCoord(&NewPosition, &NewXText, &NewYText);
04127 
04128     // Update the coord
04129     if (FieldsValid)
04130     {
04131         if (OldXText != NewXText)
04132             NewPos->x = NewPosition.x;
04133         if (OldYText != NewYText)
04134             NewPos->y = NewPosition.y;
04135     }
04136 
04137     *Valid = FieldsValid;
04138 
04139     return TRUE;
04140 }
04141 
04142 BOOL BezToolInfoBarOp::CheckClassData ()
04143 {
04144     if ((CurrentNodePath) && (CurrentInkPath) && (CurrentSpread))
04145     {
04146         return (TRUE);
04147     }
04148     return (FALSE);
04149 }
04150 
04151 /*****************************************************************************
04152 >   MsgResult BezToolInfoBarOp::InfobarMessage(Msg* Message) 
04153 
04154     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
04155     Created:    9/8/95
04156     Inputs:     Msg - points to the message
04157     Outputs:    -
04158     Returns:    MsgResult code
04159     Purpose:    Deals with messages sent to the Shape editor tools infobar
04160     Errors:     -
04161     SeeAlso:    MsgResult
04162 *****************************************************************************/
04163 
04164 MsgResult BezToolInfoBarOp::InfobarMessage(Msg* Message) 
04165 {
04166     ERROR2IF(!(IS_OUR_DIALOG_MSG(Message)), FAIL, "Message not sent to the shape editor infobar");
04167     ERROR2IF(pBezTool==NULL, FAIL, "NULL tool pointer");
04168 
04169     DialogMsg* Msg = (DialogMsg*)Message;
04170 
04171     if (Msg->DlgMsg == DIM_CANCEL)
04172     {
04173         Close();
04174     }
04175     else if (Msg->DlgMsg == DIM_CREATE)
04176     {
04177         // Set the range of the slider control
04178         SetGadgetRange(_R(IDC_SMOOTHSLIDER), 0, 100, 1);
04179         SetGadgetBitmaps(_R(IDC_SMOOTHSLIDER), _R(IDB_QUALITYBASE), _R(IDB_QUALITYSLIDER));
04180         SetLongGadgetValue(_R(IDC_SMOOTHSLIDER), 0);
04181         
04182         // Set the percentage string
04183         TCHAR Str[32];
04184         String_32 jcf(_R(IDS_PERCENT_FORMAT));
04185         camSnprintf(Str, 31, jcf, (INT32) 0);
04186         String_32 PercentStr(Str);
04187         SetStringGadgetValue(_R(IDC_SMOOTHPERCENT), PercentStr);
04188 
04189         // Update all the buttons
04190         UpdateTextIndicator();  
04191         UpdateLineButton();
04192         UpdateCurveButton();
04193         UpdateReversePathsButton ();
04194         UpdateSmoothButton();
04195         UpdateCuspButton();
04196         UpdatePositionFields();
04197     }
04198     else if (Msg->DlgMsg == DIM_SELECTION_CHANGED)
04199     {
04200         if (CheckClassData())
04201         {
04202             if (!HandleEditFieldCommit(Msg->GadgetID))
04203                 InformError();
04204         }
04205     }
04206     else if ((Msg->DlgMsg == DIM_LFT_BN_CLICKED) || (Msg->DlgMsg == DIM_LFT_BN_UP))
04207     {
04208         //switch (Msg->GadgetID)
04209     
04210         if (Msg->GadgetID == _R(IDC_BTN_MAKELINE))
04211         {
04212             if (Msg->DlgMsg == DIM_LFT_BN_CLICKED)
04213                 HandleClickOnLineButton();
04214         }
04215         else if (Msg->GadgetID == _R(IDC_BTN_MAKECURVE))
04216         {
04217             if (Msg->DlgMsg == DIM_LFT_BN_CLICKED)
04218                 HandleClickOnCurveButton();
04219         }
04220         else if (Msg->GadgetID == _R(IDC_BTN_SMOOTHPOINT))
04221         {
04222             if (Msg->DlgMsg == DIM_LFT_BN_CLICKED)
04223                 HandleClickOnSmoothButton();
04224         }
04225         else if (Msg->GadgetID == _R(IDC_BTN_CUSPPOINT))
04226         {
04227             if (Msg->DlgMsg == DIM_LFT_BN_CLICKED)
04228                 HandleClickOnCuspButton(KeyPress::IsConstrainPressed());
04229         }
04230         else if (
04231             (Msg->GadgetID == _R(IDC_PATH_BUMP_FIRSTX_LESS)) ||
04232             (Msg->GadgetID == _R(IDC_PATH_BUMP_FIRSTX_MORE)) ||
04233             (Msg->GadgetID == _R(IDC_PATH_BUMP_FIRSTY_LESS)) ||
04234             (Msg->GadgetID == _R(IDC_PATH_BUMP_FIRSTY_MORE)) ||
04235             (Msg->GadgetID == _R(IDC_PATH_BUMP_ENDX_LESS)) ||
04236             (Msg->GadgetID == _R(IDC_PATH_BUMP_ENDX_MORE)) ||
04237             (Msg->GadgetID == _R(IDC_PATH_BUMP_ENDY_LESS)) ||
04238             (Msg->GadgetID == _R(IDC_PATH_BUMP_ENDY_MORE)) ||
04239             (Msg->GadgetID == _R(IDC_PATH_BUMP_SECONDX_LESS)) ||
04240             (Msg->GadgetID == _R(IDC_PATH_BUMP_SECONDX_MORE)) ||
04241             (Msg->GadgetID == _R(IDC_PATH_BUMP_SECONDY_LESS)) ||
04242             (Msg->GadgetID == _R(IDC_PATH_BUMP_SECONDY_MORE))
04243             )
04244         {
04245             if (Msg->DlgMsg == DIM_LFT_BN_CLICKED)
04246             {
04247                 if (!HandleBumpClick(Msg->GadgetID))
04248                     InformError();
04249             }
04250             else
04251             {
04252                 if (Msg->DlgMsg == DIM_LFT_BN_UP)
04253                 {
04254                     if (CheckClassData ())
04255                     {
04256                         // Pretend the user has pressed return in the field
04257                         BOOL ok = TRUE;
04258                         if ((Msg->GadgetID == _R(IDC_PATH_BUMP_FIRSTX_LESS)) || (Msg->GadgetID == _R(IDC_PATH_BUMP_FIRSTX_MORE)))
04259                         {
04260                             ok = HandleEditFieldCommit(_R(IDC_PATH_EDIT_FIRSTX));
04261                         }
04262                         else if ((Msg->GadgetID == _R(IDC_PATH_BUMP_FIRSTY_LESS)) || (Msg->GadgetID == _R(IDC_PATH_BUMP_FIRSTY_MORE)))
04263                         {
04264                             ok = HandleEditFieldCommit(_R(IDC_PATH_EDIT_FIRSTY));
04265                         }
04266                         else if ((Msg->GadgetID == _R(IDC_PATH_BUMP_ENDX_LESS)) || (Msg->GadgetID == _R(IDC_PATH_BUMP_ENDX_MORE)))
04267                         {   
04268                             ok = HandleEditFieldCommit(_R(IDC_PATH_EDIT_ENDPOINTX));
04269                         }
04270                         else if ((Msg->GadgetID == _R(IDC_PATH_BUMP_ENDY_LESS)) || (Msg->GadgetID == _R(IDC_PATH_BUMP_ENDY_MORE)))
04271                         {
04272                             ok = HandleEditFieldCommit(_R(IDC_PATH_EDIT_ENDPOINTY));
04273                         }
04274                         else if ((Msg->GadgetID == _R(IDC_PATH_BUMP_SECONDX_LESS)) || (Msg->GadgetID == _R(IDC_PATH_BUMP_SECONDX_MORE)))
04275                         {
04276                             ok = HandleEditFieldCommit(_R(IDC_PATH_EDIT_SECONDX));
04277                         }
04278                         else if ((Msg->GadgetID == _R(IDC_PATH_BUMP_SECONDY_LESS)) || (Msg->GadgetID == _R(IDC_PATH_BUMP_SECONDY_MORE)))
04279                         {
04280                             ok = HandleEditFieldCommit(_R(IDC_PATH_EDIT_SECONDY));
04281                         }
04282 
04283                         if (!ok)
04284                             InformError();
04285                     }
04286                 }
04287             }
04288         }
04289         else if (Msg->GadgetID == _R(IDC_BTN_BEZ_REVERSE_PATH))
04290         {
04291             if (Msg->DlgMsg == DIM_LFT_BN_CLICKED)
04292             {
04293                 HandleClickOnReversePathButton ();
04294             }
04295         }
04296 #ifdef ARROWHEADS
04297         else if (Msg->GadgetID == _R(IDC_BTN_BEZ_END_ARROW))
04298         {
04299             if (Msg->DlgMsg == DIM_LFT_BN_CLICKED)
04300             {
04301                 HandleClickOnEndArrowButton();
04302                 UpdateEndArrowButton();
04303             }
04304         }
04305         else if (Msg->GadgetID == _R(IDC_BTN_BEZ_START_ARROW))
04306         {
04307             if (Msg->DlgMsg == DIM_LFT_BN_CLICKED)
04308             {
04309                 HandleClickOnStartArrowButton();
04310                 UpdateStartArrowButton();
04311             }
04312         }
04313 #endif
04314     }
04315     else if (Msg->DlgMsg == DIM_SLIDER_POS_CHANGING)
04316     {
04317         // Messages to all the controls, handled individually
04318         if (Msg->GadgetID == _R(IDC_SMOOTHSLIDER))
04319         {
04320             // Find the current scrollers position
04321             TCHAR Str[32];
04322             BOOL Valid;
04323             INT32 Result = GetLongGadgetValue(_R(IDC_SMOOTHSLIDER), 0, 100, 0, &Valid);
04324 
04325             // Build the Percentage string and set it
04326             String_32 jcf(_R(IDS_PERCENT_FORMAT));
04327             camSnprintf(Str, 31, jcf, Result);
04328             String_32 PercentStr(Str);
04329             SetStringGadgetValue(_R(IDC_SMOOTHPERCENT), PercentStr);
04330 
04331             // Tell the bezier tool about the new smoothness
04332             if (Valid)
04333                 // Set the smoothness and resmooth the points if possible
04334                 pBezTool->RetroSmoothChanging(Result);
04335         }
04336     }
04337     else if (Msg->DlgMsg == DIM_SLIDER_POS_SET)
04338     {
04339         if (Msg->GadgetID == _R(IDC_SMOOTHSLIDER))
04340         {
04341             // Tell the tool that things are all finished with
04342             pBezTool->RetroSmoothFinished();
04343         }
04344     }
04345 
04346     return OK; 
04347 }
04348 
04349 
04350 
04351 /********************************************************************************************
04352 >   BOOL BezierTool::AutoClosePaths()
04353 
04354     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
04355     Created:    19/12/95
04356     Inputs:     -
04357     Outputs:    -
04358     Returns:    TRUE if all went well, FALSE if an error occured.
04359     Purpose:    Runs through all selected lines.  If the path is open and either of the ends
04360                 are selected then the path is closed.
04361     SeeAlso:    -
04362 ********************************************************************************************/
04363 BOOL BezierTool::AutoClosePaths()
04364 {
04365     OpState Calcium = OpCloseNodePaths::GetState(NULL, NULL);
04366 
04367     if (!Calcium.Greyed)
04368     {
04369         OpParam Param(CreateCurve, !CreateCusp);
04370         OpDescriptor* Apple = OpDescriptor::FindOpDescriptor(CC_RUNTIME_CLASS(OpCloseNodePaths));
04371         if (Apple != NULL)
04372             Apple->Invoke(&Param);
04373     }
04374 
04375     return TRUE;
04376 }
04377 
04378 
04379 
04380 /********************************************************************************************
04381 >   INT32 BezToolInfoBarOp::GetPrevLineIndex(Path* pPath, INT32 Index)
04382 
04383     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
04384     Created:    16/1/96
04385     Inputs:     pPath - points to the path
04386                 Index - the index of the current endpoint
04387     Outputs:    -
04388     Returns:    The index of the previous line endpoint, -1 if there isn't one, -2 for error
04389     Purpose:    Gets the index of the 'other' end of the line ending at the current index,
04390                 accounting for closed and sub-paths.
04391     SeeAlso:    INT32 BezToolInfoBarOp::GetNextLineIndex()
04392 ********************************************************************************************/
04393 INT32 BezToolInfoBarOp::GetPrevLineIndex(Path* pPath, INT32 Index)
04394 {
04395     if (pPath == NULL)
04396     {
04397         ERROR3("NULL path pointer");
04398         return -1;
04399     }
04400 
04401     // Get info on this subpath
04402     INT32 SubPathStart = Index;
04403     INT32 SubPathEnd = Index;
04404     pPath->FindStartOfSubPath(&SubPathStart);
04405     pPath->FindEndOfSubPath(&SubPathEnd);
04406     BOOL SubPathClosed = (pPath->GetVerbArray()[SubPathEnd] & PT_CLOSEFIGURE);
04407 
04408     // If the sub-path is closed and we're at the start then we must skip to the end
04409     if (SubPathClosed && (Index == SubPathStart) )
04410     {
04411         if ((pPath->GetVerbArray()[SubPathEnd] & ~PT_CLOSEFIGURE) == PT_LINETO)
04412             return SubPathEnd;
04413         else
04414             return -1;
04415     }
04416 
04417     // no worries about looping around ends of paths
04418     if ((Index > SubPathStart) && ((pPath->GetVerbArray()[CurrentIndex] & ~PT_CLOSEFIGURE) == PT_LINETO) )
04419         return Index-1;
04420     else
04421         return -1;
04422 }
04423 
04424 
04425 
04426 /********************************************************************************************
04427 >   INT32 BezToolInfoBarOp::GetNextLineIndex()
04428 
04429     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
04430     Created:    16/1/96
04431     Inputs:     pPath - points to the path
04432                 Index - the index of the current endpoint
04433     Outputs:    -
04434     Returns:    The index of the next line endpoint, -1 if there isn't one
04435     Purpose:    Gets the index of the 'other' end of the line starting at the current index,
04436                 accounting for closed and sub-paths.
04437     SeeAlso:    INT32 BezToolInfoBarOp::GetPrevLineIndex()
04438 ********************************************************************************************/
04439 INT32 BezToolInfoBarOp::GetNextLineIndex(Path* pPath, INT32 Index)
04440 {
04441     if (pPath == NULL)
04442     {
04443         ERROR3("NULL path pointer");
04444         return -1;
04445     }
04446 
04447     // Get info on this subpath
04448     INT32 SubPathStart = Index;
04449     INT32 SubPathEnd = Index;
04450     pPath->FindStartOfSubPath(&SubPathStart);
04451     pPath->FindEndOfSubPath(&SubPathEnd);
04452     BOOL SubPathClosed = (pPath->GetVerbArray()[SubPathEnd] & PT_CLOSEFIGURE);
04453 
04454     // If the sub-path is closed and we're at the end then we must skip to the start
04455     if (SubPathClosed && (Index == SubPathEnd) )
04456     {
04457         if ((pPath->GetVerbArray()[SubPathStart+1] & ~PT_CLOSEFIGURE) == PT_LINETO)
04458             return SubPathStart+1;
04459         else
04460             return -1;
04461     }
04462 
04463     // no worries about looping around ends of paths
04464     if ((Index < SubPathEnd) && ((pPath->GetVerbArray()[CurrentIndex+1] & ~PT_CLOSEFIGURE) == PT_LINETO) )
04465         return Index+1;
04466     else
04467         return -1;
04468 }
04469 
04470 
04471 
04472 /********************************************************************************************
04473 >   void BezierTool::RemoveFloater(DocCoord* FloatPos, Spread* FloatSpread, Document* pDoc)
04474 
04475     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
04476     Created:    23/1/96
04477     Inputs:     FloatPos - the current position of the floating endpoint
04478                 FloatSpread - the spread it's on
04479                 pDoc - points to document with the endpoint
04480     Outputs:    -
04481     Returns:    -
04482     Purpose:    Invokes the operation to remove the floating endpoint
04483     SeeAlso:    OpRemoveFloater
04484 ********************************************************************************************/
04485 void BezierTool::RemoveFloater(DocCoord* FloatPos, Spread* FloatSpread, Document* pDoc)
04486 {
04487     if (FloatingEndpoint)
04488     {   
04489         InsertFloaterParam Param(FloatPos, FloatSpread, this, pDoc);
04490         OpDescriptor* Apple = OpDescriptor::FindOpDescriptor(CC_RUNTIME_CLASS(OpRemoveFloater));
04491         if (Apple != NULL)
04492             Apple->Invoke(&Param);
04493         else
04494             InformError();
04495     }
04496     else
04497     {
04498         ERROR3("Attempted to remove floating endpoint but it wasn't there!");
04499     }
04500 }
04501 
04502 
04503 

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