penedit.cpp

Go to the documentation of this file.
00001 // $Id: penedit.cpp 1282 2006-06-09 09:46:49Z alex $
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 pen tools edit code. Contains all operations provided for the 
00099 // pen tool
00100 // Created by Mike on 25/9/94
00101 
00102 #include "camtypes.h"
00103 //#include "resource.h"
00104 #include "penedit.h"
00105 //#include "mike.h"
00106 #include "osrndrgn.h"
00107 //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00108 #include "nodepath.h"
00109 //#include "document.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00110 //#include "ops.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00111 //#include "selop.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00112 #include "progress.h"
00113 //#include "attrmgr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00114 #include "pathedit.h"
00115 //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00116 //#include "stockcol.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00117 //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00118 
00119 CC_IMPLEMENT_DYNCREATE( OpPenHandles, Operation )
00120 CC_IMPLEMENT_DYNCREATE( OpPenDragBlobs, OpPenHandles )
00121 CC_IMPLEMENT_DYNCREATE( OpPenCreateInternal, OpPenDragBlobs )
00122 CC_IMPLEMENT_DYNCREATE( OpPenEditInternal, OpPenDragBlobs )
00123 CC_IMPLEMENT_DYNCREATE( OpPenEditPath, OpPenHandles )
00124 CC_IMPLEMENT_DYNCREATE( OpPenCreatePath, OpPenEditPath )
00125 CC_IMPLEMENT_DYNCREATE( OpPenAddElement, OpPenEditPath )
00126 CC_IMPLEMENT_DYNCREATE( OpPenClosePath, OpPenEditPath )
00127 
00128 CC_IMPLEMENT_DYNCREATE( OpAddPath, SelOperation )
00129 CC_IMPLEMENT_DYNCREATE( OpAddNewPath, OpAddPath )
00130 CC_IMPLEMENT_DYNCREATE( OpAddPathToPath, OpAddPath )
00131 CC_IMPLEMENT_DYNCREATE( OpClosePathWithPath, OpAddPath )
00132 
00133 
00134 #define new CAM_DEBUG_NEW
00135 
00136 /*
00137 */
00138 
00139 
00140 /********************************************************************************************
00141 
00142 >  HandleFlags::HandleFlags(BOOL rTrackEnd = TRUE,
00143                             BOOL rGhostEnd = TRUE,
00144                             BOOL TrackPtMoves = TRUE,
00145                             BOOL TrackPtSpins = FALSE,
00146                             BOOL GhostPtMoves = TRUE
00147                             BOOL GhostPtSpins = FALSE);
00148 
00149     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00150     Created:    25/9/94
00151     Purpose:    constructor for handle flags class
00152     SeeAlso:    -
00153 
00154 ********************************************************************************************/
00155 
00156 HandleFlags::HandleFlags(   BOOL rTrackEnd,
00157                             BOOL rGhostEnd,
00158                             BOOL TrackPtMoves,
00159                             BOOL GhostPtMoves,
00160                             BOOL TrackPtSpins,
00161                             BOOL GhostPtSpins)
00162 {
00163     // Set up some flags for rendering handles correctly
00164     RenderTrackEnd = rTrackEnd;
00165     RenderGhostEnd = rGhostEnd;
00166     TrackPointMoves = TrackPtMoves;
00167     GhostPointMoves = GhostPtMoves;
00168     TrackPointSpins = TrackPtSpins;
00169     GhostPointSpins = GhostPtSpins;
00170 }
00171 
00172 
00173 /********************************************************************************************
00174 
00175 >  WobbleFlags::WobbleFlags(BOOL pSister  = FALSE,
00176                             BOOL pBrother = TRUE,
00177                             BOOL nBrother = FALSE,
00178                             BOOL nSister  = FALSE)
00179     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00180     Created:    25/9/94
00181     Purpose:    constructor for wobble flags class
00182     SeeAlso:    -
00183 
00184 ********************************************************************************************/
00185 
00186 WobbleFlags::WobbleFlags(   BOOL pSister,
00187                             BOOL pBrother,
00188                             BOOL nBrother, 
00189                             BOOL nSister)
00190 {
00191     // Set up some flags for wobbling a curves control points correctly
00192     PrevSister  = pSister;
00193     PrevBrother = pBrother;
00194     NextBrother = nBrother;
00195     NextSister  = nSister;
00196 };
00197 
00198 /********************************************************************************************
00199 
00200     ControlPts::ControlPts() / ~ControlPts()
00201 
00202     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00203     Created:    25/9/94
00204     Purpose:    Control points class constructor and destructor
00205     SeeAlso:    -
00206 
00207 ********************************************************************************************/
00208 
00209 ControlPts::ControlPts()
00210 {
00211     pHndSpread = NULL;
00212 }
00213 
00214 ControlPts::~ControlPts()
00215 {
00216 }
00217 
00218 
00219 /********************************************************************************************
00220 
00221 >   OpPenHandles::OpPenHandles()
00222 
00223     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00224     Created:    25/9/94
00225     Purpose:    OpPenEorHandles constructor
00226     SeeAlso:    -
00227 
00228 ********************************************************************************************/
00229 
00230 OpPenHandles::OpPenHandles()
00231 {
00232     // default constructor (do nothing)
00233 }
00234 
00235 
00236 /********************************************************************************************
00237 
00238 >   DocCoord OpPenHandles::GetTrackHandle() const;
00239 
00240     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00241     Created:    25/9/94
00242     Inputs:     -
00243     Outputs:    The current track handle coordinate
00244     Purpose:    Return the private track handle coordinate for reference by none
00245                 inclass code.
00246 
00247 ********************************************************************************************/
00248 
00249 DocCoord OpPenHandles::GetTrackHandle() const
00250 {
00251     return TrackEnd;
00252 }
00253 
00254 DocCoord OpPenHandles::GetGhostHandle() const
00255 {
00256     return GhostEnd;
00257 }
00258 
00259 DocCoord OpPenHandles::GetMidHandle() const
00260 {
00261     return MidPoint;
00262 }
00263 
00264 
00265 /********************************************************************************************
00266 
00267 >   DocCoord OpPenHandles::CalcGhostEnd(DocCoord start, DocCoord end) const;
00268 
00269     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00270     Created:    25/9/94
00271     Inputs:     start   = point 1
00272                 end     = point 2
00273     Returns:    ghost doc coordinate
00274     Purpose:    Calculate a ghost end coordinate given two input coordinates.
00275                 ghost = point1 - (point2 - point1) 
00276 
00277 ********************************************************************************************/
00278 
00279 DocCoord OpPenHandles::CalcGhostEnd(DocCoord start, DocCoord end) const
00280 {
00281     DocCoord ghost;
00282     ghost.x = start.x - (end.x - start.x);
00283     ghost.y = start.y - (end.y - start.y);
00284 
00285     return ghost;
00286 }
00287 
00288 
00289 
00290 /********************************************************************************************
00291 
00292 >   DocRect OpPenHandles::GetHandlesRect()
00293 
00294     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00295     Created:    26/9/94
00296     Returns:    DocRect, 
00297     Purpose:    Calculate the bounding rect of the drag blobs.
00298 
00299 ********************************************************************************************/
00300 
00301 DocRect OpPenHandles::GetHandlesRect()
00302 {
00303     // just set it to be an empty rectangle
00304     DocRect BoundRect (0,0,0,0);
00305     DocView* pDocView = DocView::GetSelected();
00306     ERROR2IF(pDocView == NULL, BoundRect, 
00307              "No selected DocView in OpPenHandles::GetHandlesRect()");
00308 
00309     // Find the Rect around the drag blobs if there is any, otherwise return that bbox
00310     // of the start pos.
00311 
00312     OSRenderRegion::GetBlobRect(pDocView->GetViewScale(), TrackEnd, BT_SELECTEDLARGEST, &BoundRect);
00313     DocRect BlobRect;
00314     OSRenderRegion::GetBlobRect(pDocView->GetViewScale(), GhostEnd, BT_SELECTEDLARGEST, &BlobRect);
00315     BoundRect = BoundRect.Union(BlobRect);
00316     return BoundRect;
00317 }
00318 
00319 
00320 
00321 /********************************************************************************************
00322 
00323 >   void OpPenHandles::RecalcHandles(DocCoord DragPos, BOOL constrain)
00324 
00325     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00326     Created:    26/9/94
00327     Inputs:     DragPos     = New position to take as the drag position
00328                 constrain   = TRUE the constrain the handle positions
00329     Purpose:    Recalc handles, given a new drag position which is generally following the
00330                 mouse pointer, will work out the relative positions of the drag handles.
00331                 The drag handles can be made to follow the drag position absolutely, or
00332                 reletively dependent on their start positions.
00333 
00334 ********************************************************************************************/
00335 
00336 void OpPenHandles::RecalcHandles(DocCoord DragPos, BOOL constrain)
00337 {
00338 
00339     // Recalculate the position of the handle which is following the
00340     // mouse.
00341 
00342     DocCoord Constrain = DragPos;
00343     if (constrain)
00344         DocView::ConstrainToAngle(MidPoint, &Constrain);
00345 
00346     if (Hflags.TrackPointMoves)
00347             TrackEnd=Constrain;
00348     else if (Hflags.TrackPointSpins)
00349     {
00350         double Glen = TrackEnd.Distance(MidPoint);
00351         double Dlen = Constrain.Distance(MidPoint);
00352         DocCoord direction;
00353         direction.x = Constrain.x - MidPoint.x;
00354         direction.y = Constrain.y - MidPoint.y;
00355         if (Dlen!=0)
00356         {
00357             double ndx = direction.x / Dlen;
00358             double ndy = direction.y / Dlen;
00359             TrackEnd.x = MidPoint.x + (INT32)(Glen*ndx);
00360             TrackEnd.y = MidPoint.y + (INT32)(Glen*ndy);
00361         }
00362     }
00363 
00364     // Recalc the position of the handle opposite, the track handle accross
00365     // the midpoint.
00366 
00367     if (Hflags.GhostPointMoves)
00368     {
00369         GhostEnd.x = MidPoint.x - (Constrain.x - MidPoint.x);
00370         GhostEnd.y = MidPoint.y - (Constrain.y - MidPoint.y);
00371     }
00372     else if (Hflags.GhostPointSpins)
00373     {
00374         // if we're spinning, the ghost point will remain a set distance
00375         // from the midpoint, but just spin around coincident with
00376         // the drag pos.
00377         double Glen = GhostEnd.Distance(MidPoint);
00378         double Dlen = Constrain.Distance(MidPoint);
00379         DocCoord direction;
00380         direction.x = Constrain.x - MidPoint.x;
00381         direction.y = Constrain.y - MidPoint.y;
00382         if (Dlen!=0)
00383         {
00384             double ndx = direction.x / Dlen;
00385             double ndy = direction.y / Dlen;
00386             GhostEnd.x = MidPoint.x - (INT32)(Glen*ndx);
00387             GhostEnd.y = MidPoint.y - (INT32)(Glen*ndy);
00388         }
00389     }
00390 
00391 }
00392 
00393 
00394 
00395 /********************************************************************************************
00396 
00397 >   void OpPenHandles::RenderHandles()
00398 
00399     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00400     Created:    26/9/94
00401     Purpose:    Render some drag control handles as the mouse moves around. These will
00402                 spin around an anchor point.
00403 
00404 ********************************************************************************************/
00405 
00406 void OpPenHandles::RenderHandles()
00407 {
00408     DocRect Rect = GetHandlesRect();
00409     RenderRegion* pRegion = DocView::RenderOnTop( &Rect, StartSpread, ClippedEOR );
00410     while ( pRegion )
00411     {
00412         UpdateHandles(pRegion);
00413         pRegion = DocView::GetNextOnTop(NULL);
00414     }   
00415 }
00416 
00417 
00418 /********************************************************************************************
00419 
00420 >   void OpPenHandles::UpdateHandles( RenderRegion* pRegion )
00421 
00422     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00423     Created:    30/9/94
00424     Inputs:     pRegion =   Pointer to a region to draw into
00425     Purpose:    Render some drag control handles as the mouse moves around. These will
00426                 spin around an anchor point. Their appearence and positioning is set
00427                 by the flags variable hflags.
00428     SeeAlso:    Hflags
00429 
00430 
00431 ********************************************************************************************/
00432 
00433 void OpPenHandles::UpdateHandles( RenderRegion* pRegion )
00434 {
00435     pRegion->SetLineColour(COLOUR_BEZIERBLOB);
00436     pRegion->SetFillColour(COLOUR_TRANS);
00437     pRegion->DrawBlob(MidPoint, BT_SELECTED);
00438 
00439     if (TrackEnd != MidPoint)
00440     {
00441         pRegion->SetLineColour(COLOUR_BEZIERLINE);
00442         pRegion->SetFillColour(COLOUR_TRANS);
00443 
00444         if (Hflags.RenderTrackEnd && Hflags.RenderGhostEnd)
00445             pRegion->DrawLine(TrackEnd, GhostEnd);
00446         else
00447         {
00448             if (Hflags.RenderTrackEnd)
00449                 pRegion->DrawLine(TrackEnd,MidPoint);
00450             if (Hflags.RenderGhostEnd)
00451                 pRegion->DrawLine(GhostEnd,MidPoint);
00452         }
00453 
00454         pRegion -> SetLineColour(COLOUR_TRANS);
00455         pRegion -> SetFillColour(COLOUR_UNSELECTEDBLOB);
00456 
00457         if (Hflags.RenderTrackEnd)
00458             pRegion -> DrawBlob(TrackEnd, BT_UNSELECTED);
00459         if (Hflags.RenderGhostEnd)
00460             pRegion -> DrawBlob(GhostEnd, BT_UNSELECTED);
00461 
00462     }
00463 }
00464 
00465 
00466 /********************************************************************************************
00467 
00468 >   void OpPenHandles::SetHandleEnds(DocCoord ClickPos, DocCoord DragPos)
00469 
00470     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00471     Created:    26/9/94
00472     Purpose:    
00473 
00474 ********************************************************************************************/
00475 
00476 void OpPenHandles::SetHandleEnds(DocCoord ClickPos, DocCoord DragPos, DocCoord GhostPos)
00477 {
00478     MidPoint = ClickPos;
00479     TrackEnd = DragPos;
00480     GhostEnd = GhostPos;
00481 }
00482 
00483 
00484 
00485 /********************************************************************************************
00486 
00487 >   OpPenHandles::SetDragHandles(   HandleFlags HandFlags,
00488                                     DocCoord Click,
00489                                     DocCoord Drag,
00490                                     DocCoord Ghost,
00491                                     Spread* pSpread)
00492 
00493     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00494     Created:    25/9/94
00495     Purpose:    Set up the initial positions of the drag handles. Note, the handles
00496                 contain their own coordinates of where they think they are. These are
00497                 separate from the drag mouse position. This allows the handles to represent
00498                 a relation to the drag point and not an absolute position.
00499     SeeAlso:    -
00500 
00501 ********************************************************************************************/
00502 
00503 void OpPenHandles::SetDragHandles(  HandleFlags HandFlags,
00504                                     DocCoord Click,
00505                                     DocCoord Drag,
00506                                     DocCoord Ghost,
00507                                     Spread* pSpread)
00508 {
00509     StartMousePos = Click;
00510     CurrentMousePos = Click;
00511     StartSpread = pSpread;
00512     Hflags = HandFlags;
00513     SetHandleEnds(Click, Drag, Ghost);
00514 }
00515 
00516 
00517 /********************************************************************************************
00518 
00519 >   void OpPenHandles::ChangeTrackHandle(DocCoord newpos, BOOL constrain)
00520 
00521     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00522     Created:    26/9/94
00523     Purpose:    
00524 
00525 ********************************************************************************************/
00526 
00527 void OpPenHandles::ChangeTrackHandle(DocCoord newpos, BOOL constrain)
00528 {
00529     CurrentMousePos = newpos;
00530     RecalcHandles(newpos, constrain);
00531 }
00532 
00533 
00534 
00535 /********************************************************************************************
00536 
00537 >   void OpPenHandles::HasMouseMoved(DocCoord& OriginalPoint, DocCoord& PointerPos, double threshold=2)
00538 
00539     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00540     Created:    26/9/94
00541     Inputs      OriginalPoint = usually the last mouse position recorded
00542                 PointerPos    = usually the current mouse position to check
00543                 threshold     = a value to multiply the scaled pixel size for the view by.
00544     Purpose:    Checks to see whether two coordinates are the same. The current test also
00545                 checks for movement of less than two pixels in the scaled view. If the mouse
00546                 has moved less than threshold*ScaledPixelSize then the points are considered
00547                 equivalent.
00548 
00549 ********************************************************************************************/
00550 
00551 BOOL OpPenHandles::HasMouseMoved(DocCoord& OriginalPoint, DocCoord& PointerPos, double threshold)
00552 {
00553     if (OriginalPoint==PointerPos)
00554         return FALSE;
00555 
00556     INT32 dist = (INT32)(OriginalPoint.Distance(PointerPos)+0.5);
00557     
00558     FIXED16 ScaledPixelWidth,
00559             ScaledPixelHeight;
00560     GetWorkingView()->GetScaledPixelSize(&ScaledPixelWidth, &ScaledPixelHeight);
00561 
00562     INT32 PixWidth  = ScaledPixelWidth.MakeLong();
00563     INT32 PixHeight = ScaledPixelHeight.MakeLong();
00564 
00565     PixWidth    = (INT32)(PixWidth*threshold+0.5);
00566     PixHeight   = (INT32)(PixHeight*threshold+0.5);
00567 
00568     INT32 range;
00569     (PixWidth<PixHeight) ? (range=PixHeight) : (range=PixWidth);
00570 
00571     return (dist>range);
00572 }   
00573 
00574 
00575 
00576 /********************************************************************************************
00577 
00578 >   void OpPenHandles::RenderTestRect( DocRect Rect, Spread* pSpread, StockColour colour )
00579 
00580     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00581     Created:    29/9/94
00582     Purpose:    Render a black rectangle. Useful for checking render regions and clipping
00583 
00584 ********************************************************************************************/
00585 
00586 void OpPenHandles::RenderTestRect( DocRect Rect, Spread* pSpread, StockColour colour )
00587 {
00588     if (Error::IsUserName("Mike"))
00589     {
00590         TRACE( _T("RectLox=%d\n"),Rect.lo.x);
00591         TRACE( _T("RectLoy=%d\n"),Rect.lo.y);
00592         TRACE( _T("RectHix=%d\n"),Rect.hi.x);
00593         TRACE( _T("RectHiy=%d\n\n"),Rect.hi.y);
00594     }
00595 
00596     RenderRegion* pRegion = DocView::RenderOnTop( &Rect, pSpread, ClippedEOR );
00597     while ( pRegion )
00598     {
00599         pRegion -> SetFillColour(colour);
00600         pRegion -> DrawRect(&Rect);
00601         pRegion = DocView::GetNextOnTop(NULL);
00602     }
00603 }
00604 
00605 /********************************************************************************************
00606 
00607 >   OpPenDragBlobs::OpPenDragBolbs()
00608 
00609     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00610     Created:    25/9/94
00611     Purpose:    OpPenDragBlobs constructor
00612     SeeAlso:    -
00613 
00614 ********************************************************************************************/
00615 
00616 OpPenDragBlobs::OpPenDragBlobs()
00617 {
00618 }
00619 
00620 
00621 /********************************************************************************************
00622 
00623 >   BOOL OpPenDragBlobs::DoPenDragBlobs()
00624 
00625     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00626     Created:    26/9/94
00627     Inputs:     tptr    =   pointer to calling tool.
00628     Outputs:    -
00629     Returns:    TRUE if able to start the drag, FALSE if not
00630     Purpose:    
00631     Errors:     failandexecute will be called if the operation fails in some way, most
00632                 likely when no memory is available. 
00633 
00634 ********************************************************************************************/
00635     
00636 BOOL OpPenDragBlobs::DoPenDragBlobs()
00637 {   
00638     // And tell the Dragging system that we need drags to happen
00639     DocRect HandlesRect = GetHandlesRect();
00640     return StartDrag(DRAGTYPE_AUTOSCROLL, &HandlesRect, &CurrentMousePos);
00641 }
00642 
00643 
00644 
00645 /********************************************************************************************
00646 
00647 >   void OpPenDragBlobs::DragPointerMove(   DocCoord PointerPos,
00648                                             ClickModifiers ClickMods,
00649                                             Spread* pSpread,
00650                                             BOOL bSolidDrag)
00651 
00652     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00653     Created:    26/9/94
00654     Inputs:     PointerPos - The current position of the mouse in Doc Coords
00655                 ClickMods - Which key modifiers are being pressed
00656     Purpose:    This is called every time the mouse moves, during a drag.
00657     SeeAlso:    ClickModifiers
00658 
00659 ********************************************************************************************/
00660 
00661 void OpPenDragBlobs::DragPointerMove(DocCoord PointerPos, 
00662                                      ClickModifiers ClickMods,
00663                                      Spread* pSpread,
00664                                      BOOL bSolidDrag)
00665 {
00666     // If drag has moved onto a different spread, convert the coord to be relative to the
00667     // original spread.
00668     if (pSpread != StartSpread)
00669         PointerPos = MakeRelativeToSpread(StartSpread, pSpread, PointerPos);
00670 
00671     // Make sure we get the grid snapping involved
00672     DocCoord SnapPos = PointerPos;
00673     DocView::SnapCurrent(pSpread,&SnapPos);
00674 
00675     // Rub out the old EORed version of the handles, and stick some new ones on.
00676     RenderHandles();
00677     ChangeTrackHandle(SnapPos, ClickMods.Constrain);
00678     RenderHandles();
00679 }
00680 
00681 
00682 
00683 /********************************************************************************************
00684 
00685 >   void OpPenDragBlobs::DragFinished( DocCoord PointerPos, ClickModifiers ClickMods,
00686                                             Spread* pSpread, BOOL Success, BOOL bSolidDrag)
00687 
00688     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00689     Created:    26/9/94
00690     Inputs:     PointerPos - The position of the mouse at the end of the drag
00691                 ClickMods - the key modifiers being pressed
00692                 Success - TRUE if the drag was terminated properly, FALSE if it
00693                 was ended with the escape key being pressed
00694     Purpose:    This is called when a drag operation finishes.
00695     SeeAlso:    ClickModifiers
00696 
00697 ********************************************************************************************/
00698 
00699 void OpPenDragBlobs::DragFinished( DocCoord PointerPos, ClickModifiers ClickMods, 
00700                                         Spread* pSpread, BOOL Success, BOOL bSolidDrag)
00701 {
00702     // terminate the drag and operation
00703     EndDrag();      
00704 }
00705 
00706 
00707 
00708 /********************************************************************************************
00709 
00710 >   virtual void OpPenDragBlobs::RenderDragBlobs( DocRect Rect, Spread* pSpread, BOOL bSolidDrag )
00711 
00712     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00713     Created:    29/9/94
00714     Purpose:    This function is called to render our drag blobs during scrolling.
00715 
00716 ********************************************************************************************/
00717 
00718 void OpPenDragBlobs::RenderDragBlobs( DocRect Rect, Spread* pSpread, BOOL bSolidDrag )
00719 {
00720     if (StartSpread!=pSpread) return;
00721     RenderRegion* pRegion = DocView::RenderOnTop( &Rect, pSpread, ClippedEOR );
00722     while ( pRegion )
00723     {
00724         UpdateHandles(pRegion);
00725         pRegion = DocView::GetNextOnTop(NULL);
00726     }
00727 }
00728 
00729 
00730 
00731 
00732 /********************************************************************************************
00733 
00734 >   OpPenCreateInternal::OpPenCreateInternal()
00735 
00736     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00737     Created:    25/9/94
00738     Purpose:    OpPenCreateInternal constructor
00739     SeeAlso:    -
00740 
00741 ********************************************************************************************/
00742 
00743 OpPenCreateInternal::OpPenCreateInternal()
00744 {
00745 }
00746 
00747 
00748 
00749 /********************************************************************************************
00750 
00751 >   BOOL OpPenCreateInternal::Init()
00752 
00753     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00754     Created:    25/9/94
00755     Inputs:     -
00756     Outputs:    -
00757     Returns:    TRUE if the operation could be successfully initialised 
00758                 FALSE if no more memory could be allocated 
00759                 
00760     Purpose:    OpDeletePoints initialiser method
00761     Errors:     ERROR will be called if there was insufficient memory to allocate the 
00762                 operation.
00763     SeeAlso:    -
00764 
00765 ********************************************************************************************/
00766 
00767 BOOL OpPenCreateInternal::Init()
00768 {
00769     return (RegisterOpDescriptor(0,                                     // tool ID
00770                                 _R(IDS_PENCREATEINTERNALOP),                // string resource ID
00771                                 CC_RUNTIME_CLASS(OpPenCreateInternal),  // runtime class for Op
00772                                 OPTOKEN_PENCREATEINTERNAL,              // Ptr to token string
00773                                 OpPenCreateInternal::GetState,          // GetState function
00774                                 0,                                      // help ID = 0
00775                                 _R(IDBBL_PENCREATEINTERNALOP),              // bubble help ID = 0
00776                                 0                                       // resource ID = 0
00777                                 )); 
00778 
00779 }               
00780 
00781 
00782 /********************************************************************************************
00783 
00784 >   OpState OpPenCreateInternal::GetState(String_256*, OpDescriptor*)
00785 
00786     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00787     Created:    26/9/94
00788     Inputs:     -
00789     Outputs:    -
00790     Returns:    The state of the OpPenCreateInternal
00791     Purpose:    For finding the OpPenCreateInternal's state. 
00792     Errors:     -
00793     SeeAlso:    -
00794 
00795 ********************************************************************************************/
00796 
00797 OpState OpPenCreateInternal::GetState(String_256* UIDescription, OpDescriptor*)
00798 {
00799     OpState OpSt;
00800     return OpSt;   
00801 }
00802 
00803 
00804 
00805 
00806 
00807 
00808 
00809 /********************************************************************************************
00810 
00811 >   void OpPenCreateInternal::DoPenCreateInternal(  DocCoord Anchor, 
00812                                                     Spread *pSpread, 
00813                                                     ControlPts* pHandles)
00814 
00815     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00816     Created:    26/9/94
00817     Inputs:     Anchor  =   position of first click
00818                 pSpread =   pointer to spread where first click occured
00819                 pHandles=   pointer to handles block
00820     Outputs:    -
00821     Returns:    -
00822     Purpose:    
00823     Errors:     failandexecute will be called if the operation fails in some way, most
00824                 likely when no memory is available. 
00825 
00826 ********************************************************************************************/
00827     
00828 void OpPenCreateInternal::DoPenCreateInternal(DocCoord Anchor, Spread* pSpread, ControlPts* pHandles)
00829 {   
00830     // initialise the drag handles into the default mode.
00831 
00832     pUserHandles = pHandles;
00833     HandleFlags Flags;
00834 
00835     // Make sure we get the grid snapping involved
00836     DocCoord SnapPos = Anchor;
00837     DocView::SnapCurrent(pSpread,&SnapPos);
00838 
00839     SetDragHandles(Flags, SnapPos, SnapPos, SnapPos, pSpread);
00840 
00841     // call the base classes drag init function
00842     if (!DoPenDragBlobs())
00843     {
00844         FailAndExecute();
00845         End();
00846         return;
00847     }
00848 
00849     // Render the current drag blobs where necessary
00850     RenderHandles();
00851 }
00852 
00853 
00854 
00855 
00856 /********************************************************************************************
00857 
00858 >   void OpPenCreateInternal::DragFinished( DocCoord PointerPos, ClickModifiers ClickMods,
00859                                             Spread* pSpread, BOOL Success, BOOL bSolidDrag)
00860 
00861     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00862     Created:    26/9/94
00863     Inputs:     PointerPos - The position of the mouse at the end of the drag
00864                 ClickMods - the key modifiers being pressed
00865                 Success - TRUE if the drag was terminated properly, FALSE if it
00866                 was ended with the escape key being pressed
00867     Purpose:    This is called when a drag operation finishes.
00868     SeeAlso:    ClickModifiers
00869 
00870 ********************************************************************************************/
00871 
00872 void OpPenCreateInternal::DragFinished( DocCoord PointerPos, ClickModifiers ClickMods, 
00873                                         Spread* pSpread, BOOL Success, BOOL bSolidDrag)
00874 {
00875     // Rub out the old EORed version of the handles
00876     RenderHandles();
00877 
00878     // inform the base class to stop dragging
00879     OpPenDragBlobs::DragFinished( PointerPos, ClickMods, pSpread, Success, bSolidDrag);
00880 
00881     // inform the pen tool that a drag has come to an end
00882     if (Success)
00883     {
00884         pUserHandles->HndClick = GetMidHandle();
00885         pUserHandles->HndDrag = GetTrackHandle();
00886         pUserHandles->pHndSpread = StartSpread;
00887     }
00888     else
00889         FailAndExecute();
00890     // End the operation
00891     End();
00892 }
00893 
00894 
00895 
00896 
00897 /********************************************************************************************
00898 
00899 >   OpPenEditInternal::OpPenEditInternal()
00900 
00901     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00902     Created:    25/9/94
00903     Purpose:    OpPenEditInternal constructor
00904     SeeAlso:    -
00905 
00906 ********************************************************************************************/
00907 
00908 OpPenEditInternal::OpPenEditInternal()
00909 {
00910 }
00911 
00912 
00913 /********************************************************************************************
00914 
00915 >   BOOL OpPenEditInternal::Init()
00916 
00917     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00918     Created:    25/9/94
00919     Inputs:     -
00920     Outputs:    -
00921     Returns:    TRUE if the operation could be successfully initialised 
00922                 FALSE if no more memory could be allocated 
00923                 
00924     Purpose:    OpPenEditInternal initialiser method
00925     Errors:     ERROR will be called if there was insufficient memory to allocate the 
00926                 operation.
00927     SeeAlso:    -
00928 
00929 ********************************************************************************************/
00930 
00931 BOOL OpPenEditInternal::Init()
00932 {
00933     return (RegisterOpDescriptor(0,                                     // tool ID
00934                                 _R(IDS_PENEDITINTERNALOP),                  // string resource ID
00935                                 CC_RUNTIME_CLASS(OpPenEditInternal),    // runtime class for Op
00936                                 OPTOKEN_PENEDITINTERNAL,                // Ptr to token string
00937                                 OpPenEditInternal::GetState,            // GetState function
00938                                 0,                                      // help ID = 0
00939                                 _R(IDBBL_PENEDITINTERNALOP),                // bubble help ID = 0
00940                                 0                                       // resource ID = 0
00941                                 )); 
00942 
00943 }               
00944 
00945 
00946 /********************************************************************************************
00947 
00948 >   OpState OpPenEditInternal::GetState(String_256*, OpDescriptor*)
00949 
00950     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00951     Created:    26/9/94
00952     Inputs:     -
00953     Outputs:    -
00954     Returns:    The state of the OpPenEditInternal
00955     Purpose:    For finding the OpPenEditInternal's state. 
00956     Errors:     -
00957     SeeAlso:    -
00958 
00959 ********************************************************************************************/
00960 
00961 OpState OpPenEditInternal::GetState(String_256* UIDescription, OpDescriptor*)
00962 {
00963     OpState OpSt;
00964     return OpSt;   
00965 }
00966 
00967 
00968 
00969 /********************************************************************************************
00970 
00971 >   void OpPenEditInternal::DoPenEditInternal(ControlPts* pHandles)
00972 
00973     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00974     Created:    26/9/94
00975     Inputs:     pHandles    = Pointer to a handles block containing click pos, drag pos
00976                               and spread ptr
00977     Outputs:    -
00978     Returns:    -
00979     Purpose:    
00980     Errors:     failandexecute will be called if the operation fails in some way, most
00981                 likely when no memory is available. 
00982 
00983 ********************************************************************************************/
00984     
00985 void OpPenEditInternal::DoPenEditInternal(ControlPts* pHandles)
00986 {   
00987 
00988     // Set the internal state
00989     pUserHandles = pHandles;
00990 
00991     HandleFlags Flags;
00992     DocCoord ghst = CalcGhostEnd(pHandles->HndClick, pHandles->HndDrag);
00993     SetDragHandles(Flags, pHandles->HndClick, pHandles->HndDrag, ghst, pHandles->pHndSpread);
00994 
00995     // call the base classes drag init function
00996     if (!DoPenDragBlobs())
00997     {
00998         FailAndExecute();
00999         End();
01000         return;
01001     }
01002 }
01003 
01004 
01005 /********************************************************************************************
01006 
01007 >   void OpPenEditInternal::DragFinished( DocCoord PointerPos, ClickModifiers ClickMods,
01008                                             Spread* pSpread, BOOL Success, BOOL bSolidDrag)
01009 
01010     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01011     Created:    26/9/94
01012     Inputs:     PointerPos - The position of the mouse at the end of the drag
01013                 ClickMods - the key modifiers being pressed
01014                 Success - TRUE if the drag was terminated properly, FALSE if it
01015                 was ended with the escape key being pressed
01016     Purpose:    This is called when a drag operation finishes.
01017     SeeAlso:    ClickModifiers
01018 
01019 ********************************************************************************************/
01020 
01021 void OpPenEditInternal::DragFinished( DocCoord PointerPos, ClickModifiers ClickMods, 
01022                                         Spread* pSpread, BOOL Success, BOOL bSolidDrag)
01023 {
01024     // Rub out the old EORed version of the handles
01025     RenderHandles();
01026 
01027     // inform the base class to stop dragging
01028     OpPenDragBlobs::DragFinished( PointerPos, ClickMods, pSpread, Success, bSolidDrag);
01029 
01030     // inform the pen tool that a drag has come to an end
01031     if (Success)
01032     {
01033         pUserHandles->HndClick  = GetMidHandle();
01034         pUserHandles->HndDrag   = GetTrackHandle();
01035         pUserHandles->pHndSpread = StartSpread;
01036     }
01037     else
01038         FailAndExecute();
01039 
01040     // terminate the op
01041     End();      
01042 }
01043 
01044 
01045 
01046 
01047 /********************************************************************************************
01048 
01049 >   OpPenEditPath::OpPenEditPath()
01050 
01051     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01052     Created:    29/9/94
01053     Purpose:    OpPenEditPath constructor
01054     SeeAlso:    -
01055 
01056 ********************************************************************************************/
01057 
01058 OpPenEditPath::OpPenEditPath()
01059 {
01060 }
01061 
01062 /********************************************************************************************
01063 
01064 >   OpPenEditPath::SetWobbleIndex(WobbleFlags wibble, const INT32 index)
01065 
01066     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01067     Created:    29/9/94
01068     Inputs:     wibble  = wobble flags indicating which control handles will follow those
01069                           of the drag handles.
01070                 index   = the index into our edit curve of the endpoint handle whose brother
01071                           and sister handles are being dragged around.
01072     Outputs:    -
01073     Purpose:    Set the control handle index of our edit curve which will follow the
01074                 dragging control handles.
01075     SeeAlso:    -
01076 
01077 ********************************************************************************************/
01078 
01079 void OpPenEditPath::SetWobbleIndex(WobbleFlags wibble, const INT32 index)
01080 {
01081     Wobble = wibble;
01082     WobbleIndex = index;
01083 }
01084 
01085 
01086         
01087 /********************************************************************************************
01088 
01089 >   BOOL OpPenEditPath::DoPenDragPath(Path* pEditPath)
01090 
01091     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01092     Created:    29/9/94
01093     Inputs:     pEditPath = pointer a path to begin editing.
01094     Returns:    TRUE if able to start the drag, FALSE otherwise
01095     Returns:    -
01096     Purpose:    
01097 
01098 ********************************************************************************************/
01099     
01100 BOOL OpPenEditPath::DoPenDragPath()
01101 {   
01102     // Tell the Dragging system that we need drags to happen
01103     DocRect HandlesRect = GetBoundingRect();
01104     return StartDrag(DRAGTYPE_AUTOSCROLL, &HandlesRect, &CurrentMousePos);
01105 }
01106 
01107 
01108 
01109 /********************************************************************************************
01110 
01111 >   void OpPenEditPath::WobbleCoords
01112 
01113     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01114     Created:    11/10/94
01115     Inputs:     
01116     Purpose:    Moves the control handles around dependent on the classes wobble flags
01117 
01118 ********************************************************************************************/
01119 
01120 void OpPenEditPath::WobbleCoords()
01121 {
01122     DocCoord* Coords = pEditPath->GetCoordArray();
01123 
01124     if (Wobble.PrevBrother)
01125         Coords[WobbleIndex-1] = GetGhostHandle();
01126 
01127     if (Wobble.PrevSister)
01128     {
01129         Coords[WobbleIndex-2].x = (2*Coords[WobbleIndex-3].x + Coords[WobbleIndex-1].x)/3;
01130         Coords[WobbleIndex-2].y = (2*Coords[WobbleIndex-3].y + Coords[WobbleIndex-1].y)/3;
01131     }
01132 
01133     if (Wobble.NextBrother)
01134         Coords[WobbleIndex+1] = GetTrackHandle();
01135 
01136     if (Wobble.NextSister)
01137     {
01138         Coords[WobbleIndex+2].x = (Coords[WobbleIndex+1].x + 2*Coords[WobbleIndex+3].x)/3;
01139         Coords[WobbleIndex+2].y = (Coords[WobbleIndex+1].y + 2*Coords[WobbleIndex+3].y)/3;
01140     }
01141 }
01142 
01143 
01144 /********************************************************************************************
01145 
01146 >   void OpPenEditPath::DragPointerMove(DocCoord PointerPos, 
01147                                         ClickModifiers ClickMods,
01148                                         Spread* pSpread, BOOL bSolidDrag)
01149 
01150     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01151     Created:    26/9/94
01152     Inputs:     PointerPos - The current position of the mouse in Doc Coords
01153                 ClickMods - Which key modifiers are being pressed
01154     Purpose:    This is called every time the mouse moves, during a drag.
01155     SeeAlso:    ClickModifiers
01156 
01157 ********************************************************************************************/
01158 
01159 void OpPenEditPath::DragPointerMove(DocCoord PointerPos,
01160                                     ClickModifiers ClickMods,
01161                                     Spread* pSpread, BOOL bSolidDrag)
01162 {
01163     // If drag has moved onto a different spread, convert the coord to be relative to the
01164     // original spread.
01165     if (pSpread != StartSpread)
01166         PointerPos = MakeRelativeToSpread(StartSpread, pSpread, PointerPos);
01167 
01168     // Make sure we get the grid snapping involved
01169     DocCoord SnapPos = PointerPos;
01170     DocView::SnapCurrent(pSpread,&SnapPos);
01171 
01172     if (CurrentMousePos != SnapPos)
01173     {
01174 
01175         // Rub out the old EORed version of the path and stick a new one on
01176         DocRect Rect = OpPenEditPath::GetBoundingRect();
01177         RenderDragBlobs(Rect, StartSpread, bSolidDrag);
01178 
01179         Rect.IncludePoint(SnapPos);
01180 
01181         // Alter the curve drag handles
01182         ChangeTrackHandle(SnapPos, ClickMods.Constrain);
01183         
01184         // make sure we wobble all the coordinates around this control point
01185         // correctly.
01186         WobbleCoords();
01187 
01188         // Now render the blobs back on again
01189         Rect = Rect.Union(OpPenEditPath::GetBoundingRect());
01190         RenderDragBlobs(Rect, StartSpread, bSolidDrag);
01191     }
01192 
01193 }
01194 
01195 
01196 
01197 /********************************************************************************************
01198 
01199 >   DocRect OpPenEditPath::GetBoundingRect()
01200 
01201     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01202     Created:    30/9/94
01203     Inputs:     -
01204     Purpose:    Find the bounding box of the edit path and the drag handles.
01205 
01206 ********************************************************************************************/
01207 
01208 DocRect OpPenEditPath::GetBoundingRect()
01209 {
01210 
01211     DocRect Rect;   
01212     DocView* pDocView = DocView::GetSelected();
01213     ENSURE( pDocView != NULL, "There was no selected docview when getting the bounding box" );
01214 
01215     if ( pDocView != NULL )
01216     {
01217         DocRect BlobRect;
01218         DocCoord Coord;
01219         Rect = pEditPath->GetBoundingRect();
01220         Rect = Rect.Union(OpPenHandles::GetHandlesRect());
01221         // inflate by 1/4 of an inch to avoid eor problems
01222         Rect.Inflate(72000>>2);
01223     }
01224 
01225     return Rect;
01226 }
01227 
01228 
01229 /********************************************************************************************
01230 
01231 >   DocRect OpPenEditPath::ConvertToLine(const INT32 index)
01232 
01233     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01234     Created:    30/9/94
01235     Inputs:     index   = index of element to remove from.
01236     Purpose:    Convert the currently edited curve element into a line
01237 
01238 ********************************************************************************************/
01239 
01240 void OpPenEditPath::ConvertToLine(const INT32 index)
01241 {
01242     // remove the curve element, then stick a none smooth line
01243     // segment in there.
01244 
01245     pEditPath->DeleteFromElement(index);
01246     PathFlags tempflags;
01247     tempflags.IsSelected = TRUE;
01248     pEditPath->InsertLineTo(GetMidHandle(), &tempflags);
01249 }
01250 
01251 
01252 
01253 /********************************************************************************************
01254 
01255 >   void OpPenEditPath::RemoveRotateEnd(const INT32 index)
01256 
01257     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01258     Created:    30/9/94
01259     Inputs:     index   = index of bezierto element
01260     Purpose:    Remove any rotate bits from a bezier element, starting at index
01261     
01262 ********************************************************************************************/
01263 
01264 void OpPenEditPath::RemoveRotateEnd(const INT32 index)
01265 {
01266     // Remove all rotate values from the end curve element
01267     PathFlags* Flags = pEditPath->GetFlagArray();
01268     Flags[index].IsRotate = FALSE;
01269     Flags[index+1].IsRotate = FALSE;
01270     Flags[index+2].IsRotate = FALSE;
01271 }
01272 
01273 
01274 /********************************************************************************************
01275 
01276 >   void OpPenEditPath::InsertBezier(DocCoord pt1, DocCoord pt2, DocCoord pt3)
01277 
01278     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01279     Created:    30/9/94
01280     Inputs:     pt1,pt2,pt3 = coords of control points for bezier
01281     Purpose:    Add a specific curve at the end of the path
01282     
01283 ********************************************************************************************/
01284 
01285 BOOL OpPenEditPath::InsertBezier(DocCoord pt1, DocCoord pt2, DocCoord pt3)
01286 {
01287     // insert a particular type of curve
01288     PathFlags tempflags;
01289     tempflags.IsRotate = TRUE;
01290     BOOL done = pEditPath->InsertCurveTo(pt1, pt2, pt3, &tempflags);
01291 
01292     if (done)
01293     {
01294         INT32 npts = pEditPath->GetNumCoords();
01295         PathFlags* Flags = pEditPath->GetFlagArray();
01296         Flags[npts-1].IsSelected = TRUE;
01297     }
01298     return done;
01299 }
01300 
01301 
01302 
01303 /********************************************************************************************
01304 
01305 >   void OpPenEditPath::InsertCurvedLine(DocCoord pt1, DocCoord pt2, DocCoord pt3)
01306 
01307     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01308     Created:    4/10/94
01309     Inputs:     pt1,pt2 = coords of the end control points for the bezier
01310     Purpose:    Add a specific curve at the end of the path. The curve is made of colinear
01311                 points
01312     
01313 ********************************************************************************************/
01314 
01315 BOOL OpPenEditPath::InsertCurvedLine(DocCoord pt1, DocCoord pt2)
01316 {
01317 
01318     BOOL ok = pEditPath->InsertMoveTo(pt1, NULL);
01319     if (ok)
01320     {
01321         DocCoord Cpt1;
01322         Cpt1.x = (2*pt1.x + pt2.x)/3;
01323         Cpt1.y = (2*pt1.y + pt2.y)/3;
01324 
01325         DocCoord Cpt2;
01326         Cpt2.x = (pt1.x + 2*pt2.x)/3;
01327         Cpt2.y = (pt1.y + 2*pt2.y)/3;
01328 
01329         ok = InsertBezier(Cpt1, Cpt2, pt2);
01330 
01331     }
01332     return ok;
01333 } 
01334 
01335 
01336 /********************************************************************************************
01337 
01338 >   BOOL OpPenEditPath::InsertGhostedBezier(DocCoord pt1, DocCoord pt2, DocCoord pt3)
01339 
01340     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01341     Created:    4/10/94
01342     Inputs:     pt1,pt2 = coords of the end control points of a previous bezier
01343                 pt3     = coordinate for new end point of inserted bezier
01344     Purpose:    Add a specific curve at the end of the path.
01345                 The curve control points are made up of 
01346                 { pt2, Ghost(pt1,pt2), LinIntpl(Ghost,pt3), pt3 }
01347     
01348 ********************************************************************************************/
01349 
01350 BOOL OpPenEditPath::InsertGhostedBezier(DocCoord pt1, DocCoord pt2, DocCoord pt3)
01351 {
01352     BOOL ok = pEditPath->InsertMoveTo(pt2, NULL);
01353     if (ok)
01354     {
01355         DocCoord Cpt1;
01356         Cpt1.x = pt2.x - (pt1.x - pt2.x);
01357         Cpt1.y = pt2.y - (pt1.y - pt2.y);
01358 
01359         DocCoord Cpt2;
01360         Cpt2.x = (Cpt1.x + 2*pt3.x)/3;
01361         Cpt2.y = (Cpt1.y + 2*pt3.y)/3;
01362 
01363         ok = InsertBezier(Cpt1, Cpt2, pt3);
01364     }
01365     return ok;
01366 }
01367 
01368 
01369 
01370 /********************************************************************************************
01371 
01372 >   BOOL OpPenEditPath::ConvertPathEnd(Path* pDocPath)
01373 
01374     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01375     Created:    7/10/94
01376     Inputs:     -
01377     Purpose:    Common code to convert an edited element to a line, or remove its
01378                 rotation bits. This should happen when the user simply clicks and does
01379                 not perform a drag. The built curve element should be replaced by either
01380                 a none smoothed curve or a line.
01381 
01382 ********************************************************************************************/
01383 
01384 BOOL OpPenEditPath::ConvertPathEnd(Path* pDocPath)
01385 {
01386     BOOL converted = FALSE;
01387 
01388     // set the path position and read the coordinate data
01389     INT32 LastEl    = pDocPath->GetNumCoords();
01390     pDocPath->SetPathPosition(LastEl-1);
01391 
01392     DocCoord  CtrlPtC = pDocPath->GetCoord();
01393     PathVerb  CtrlPtV = (pDocPath->GetVerb()) & ~PT_CLOSEFIGURE;
01394     PathFlags CtrlPtF = pDocPath->GetFlags();
01395 
01396     // now create an element dependent on the control point type
01397 
01398     switch (CtrlPtV)
01399     {
01400         case PT_BEZIERTO:
01401             if (CtrlPtF.IsRotate)
01402                 RemoveRotateEnd(1);
01403             else
01404                 ConvertToLine(1);
01405                 converted=TRUE;
01406             break;
01407 
01408         case PT_LINETO:
01409             ConvertToLine(1);
01410             converted=TRUE;
01411             break;
01412     }
01413     return converted;
01414 }
01415 
01416 
01417 
01418 /********************************************************************************************
01419 
01420 >   void OpPenEditPath::DragFinished( DocCoord PointerPos, ClickModifiers ClickMods,
01421                                             Spread* pSpread, BOOL Success, BOOL bSolidDrag)
01422 
01423     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01424     Created:    26/9/94
01425     Inputs:     PointerPos  - The position of the mouse at the end of the drag
01426                 ClickMods   - the key modifiers being pressed
01427                 Success     - TRUE if the drag was terminated properly, FALSE if it
01428                 was ended with the escape key being pressed
01429     Purpose:    This is called when a drag operation finishes.
01430     SeeAlso:    ClickModifiers
01431 
01432 ********************************************************************************************/
01433 
01434 void OpPenEditPath::DragFinished( DocCoord PointerPos, ClickModifiers ClickMods, 
01435                                   Spread* pSpread, BOOL Success, BOOL bSolidDrag)
01436 {
01437     // terminate the drag and operation
01438     EndDrag();      
01439 }
01440 
01441 
01442 
01443 /********************************************************************************************
01444 
01445 >   void OpPenEditPath::RenderDragBlobs( DocRect Rect, Spread* pSpread, BOOL bSolidDrag )
01446 
01447     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01448     Created:    29/9/94
01449     Purpose:    Render some drag control handles as the mouse moves around. These will
01450                 spin around an anchor point.
01451 
01452 ********************************************************************************************/
01453 
01454 void OpPenEditPath::RenderDragBlobs( DocRect Rect, Spread* pSpread, BOOL bSolidDrag )
01455 {
01456 
01457     RenderRegion* pRegion = DocView::RenderOnTop( &Rect, StartSpread, ClippedEOR );
01458     while ( pRegion )
01459     {
01460         // Get the edit path to render itself
01461         pRegion -> SetLineColour(COLOUR_XOREDIT);
01462         pRegion -> SetLineWidth(0);
01463         pRegion -> DrawPath(pEditPath);
01464 
01465         // Render the drag handles on top
01466         OpPenHandles::UpdateHandles(pRegion);
01467 
01468         pRegion = DocView::GetNextOnTop(NULL);
01469     }
01470 }
01471 
01472 
01473 
01474 
01475 /********************************************************************************************
01476 
01477 >   OpPenCreatePath::OpPenCreatePath()
01478 
01479     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01480     Created:    29/9/94
01481     Purpose:    OpPenCreatePath constructor
01482     SeeAlso:    -
01483 
01484 ********************************************************************************************/
01485 
01486 OpPenCreatePath::OpPenCreatePath()
01487 {
01488     // Dummy constructor
01489 }
01490 
01491 
01492 /********************************************************************************************
01493 
01494 >   BOOL OpPenCreatePath::Init()
01495 
01496     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01497     Created:    29/9/94
01498     Inputs:     -
01499     Outputs:    -
01500     Returns:    TRUE if the operation could be successfully initialised 
01501                 FALSE if no more memory could be allocated 
01502                 
01503     Purpose:    OpPenCreatePath initialiser method
01504     Errors:     ERROR will be called if there was insufficient memory to allocate the 
01505                 operation.
01506     SeeAlso:    -
01507 
01508 ********************************************************************************************/
01509 
01510 BOOL OpPenCreatePath::Init()
01511 {
01512     return (RegisterOpDescriptor(0,                                     // tool ID
01513                                 _R(IDS_PENCREATEPATHOP),                    // string resource ID
01514                                 CC_RUNTIME_CLASS(OpPenCreatePath),      // runtime class for Op
01515                                 OPTOKEN_PENCREATEPATH,                  // Ptr to token string
01516                                 OpPenCreatePath::GetState,              // GetState function
01517                                 0,                                      // help ID = 0
01518                                 _R(IDBBL_PENCREATEPATHOP),                  // bubble help ID = 0
01519                                 0                                       // resource ID = 0
01520                                 )); 
01521 
01522 }               
01523 
01524 
01525 
01526 /********************************************************************************************
01527 
01528 >   OpState OpPenCreatePath::GetState(String_256*, OpDescriptor*)
01529 
01530     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01531     Created:    29/9/94
01532     Inputs:     -
01533     Outputs:    -
01534     Returns:    The state of the OpPenCreatePath
01535     Purpose:    For finding the OpPenCreatePath's state. 
01536     Errors:     -
01537     SeeAlso:    -
01538 
01539 ********************************************************************************************/
01540                 
01541 OpState OpPenCreatePath::GetState(String_256*, OpDescriptor*)
01542 {
01543     OpState OpSt;
01544     // Always enabled at the moment.
01545     return OpSt;
01546 }
01547 
01548 
01549 /********************************************************************************************
01550 
01551 >   void OpPenCreatePath::DoPenCreatePath(ControlPts* pHandles,
01552                                           DocCoord ClickPoint,
01553                                           Spread *pClickSpread, 
01554                                           Path* pEditPath)
01555 
01556     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01557     Created:    29/9/94
01558     Inputs:     pHandles    =   pointer to a handles block containing drag handle info
01559                 ClickPoint  =   the last mouse click position
01560                 pClickSpread=   pointer to spread where the click occured
01561                 pEditPath   =   pointer to path to edit
01562                 
01563     Outputs:    pEditPath   =   Updated to contain a single path element, either
01564                                 a curve or line dependent on whether the operation
01565                                 performs a drag.
01566     Returns:    -
01567     Purpose:    Creates a path element when the user clicks or drags at a particular point.
01568                 The edit path will contain the necessary element when the operation has
01569                 finished. If the operation is not a success, the edit path will contain
01570                 nothing.
01571     Errors:     failandexecute will be called if the operation fails in some way, most
01572                 likely when no memory is available. 
01573 
01574 ********************************************************************************************/
01575     
01576 void OpPenCreatePath::DoPenCreatePath(  ControlPts* pHandles,
01577                                         DocCoord ClickPoint,
01578                                         Spread *pClickSpread, 
01579                                         Path* pPath)
01580 {   
01581     // ok, we always create a curve element in the path and descover whether
01582     // the points all lie in a straight line at the end of the drag. If so, we
01583     // will alter the path element back to a line at that stage.
01584 
01585     BOOL ok;
01586     
01587     pEditPath = pPath;
01588     
01589     Handle0 = pHandles->HndClick;
01590     Handle1 = pHandles->HndDrag;
01591 
01592     // make the new click point relative to our handle coordinates.
01593     if (pHandles->pHndSpread != pClickSpread)
01594         ClickPoint = MakeRelativeToSpread(pHandles->pHndSpread, pClickSpread, ClickPoint);
01595 
01596     // make sure we get the grid snapping involved
01597     DocCoord SnapPos = ClickPoint;
01598     DocView::SnapCurrent(pClickSpread,&SnapPos);
01599 
01600     // create the first element in the path
01601     ok = (pEditPath->InsertMoveTo(Handle0, NULL));
01602 
01603     if (ok)
01604     {
01605         DocCoord Cpt1 = Handle1;
01606         if (Handle0 == Handle1)
01607         {
01608             Cpt1.x = (2*Handle0.x + SnapPos.x)/3;
01609             Cpt1.y = (2*Handle0.y + SnapPos.y)/3;
01610         }
01611         DocCoord Cpt2;
01612         Cpt2.x = (Handle1.x + 2*SnapPos.x)/3;
01613         Cpt2.y = (Handle1.y + 2*SnapPos.y)/3;
01614 
01615         ok = InsertBezier(Cpt1, Cpt2, SnapPos);
01616     }
01617     
01618     if (ok)
01619     {
01620         // Set info about the click point and spread
01621         HandleFlags hFlags;
01622         SetDragHandles(hFlags, SnapPos, SnapPos, SnapPos, pClickSpread);
01623             
01624         // Set the edit control to tell the drag code which 
01625         // point to overwrite 
01626         WobbleFlags wFlags;
01627         SetWobbleIndex(wFlags, 3);
01628 
01629         // Render the first eor version on
01630         DocRect Rect = GetBoundingRect();
01631         RenderDragBlobs(Rect, StartSpread, FALSE);
01632         
01633         // Now start the drag op on this path
01634         ok = OpPenEditPath::DoPenDragPath();
01635     }
01636 
01637     if (!ok)
01638     {
01639         // failed to perform create so lets tidy up the path and exit
01640         pEditPath->ClearPath();
01641         FailAndExecute();
01642         End();
01643     }
01644 }   
01645 
01646 
01647 
01648 
01649 /********************************************************************************************
01650 
01651 >   void OpPenCreatePath::DragFinished( DocCoord PointerPos, ClickModifiers ClickMods,
01652                                         Spread* pSpread, BOOL Success, BOOL bSolidDrag)
01653 
01654     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01655     Created:    26/9/94
01656     Inputs:     PointerPos  - The position of the mouse at the end of the drag
01657                 ClickMods   - the key modifiers being pressed
01658                 Success     - TRUE if the drag was terminated properly, FALSE if it
01659                 was ended with the escape key being pressed
01660     Purpose:    This is called when a drag operation finishes.
01661     SeeAlso:    ClickModifiers
01662 
01663 ********************************************************************************************/
01664 
01665 void OpPenCreatePath::DragFinished( DocCoord PointerPos, ClickModifiers ClickMods, 
01666                                     Spread* pSpread, BOOL Success, BOOL bSolidDrag)
01667 {
01668     // inform the base class to stop dragging
01669     OpPenEditPath::DragFinished( PointerPos, ClickMods, pSpread, Success, bSolidDrag);
01670 
01671     // Rub out the old EORed version of the path
01672     DocRect Rect = GetBoundingRect();
01673     RenderDragBlobs( Rect, StartSpread, bSolidDrag);
01674 
01675     if (Success)
01676     {
01677         if (!HasMouseMoved(StartMousePos,CurrentMousePos))
01678         {
01679             // nothing moved so check to see if we can convert to a line
01680             if (Handle0 == Handle1)
01681                 ConvertToLine(1);           // delete the curve element and create a line!
01682             else
01683                 RemoveRotateEnd(1);         // make sure the end has no smooth bits set
01684         }
01685     }
01686     else
01687         FailAndExecute();
01688 
01689     End();
01690 }
01691 
01692 
01693 
01694 
01695 
01696 /********************************************************************************************
01697 
01698 >   OpPenAddElement::OpPenAddElement()
01699 
01700     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01701     Created:    30/9/94
01702     Purpose:    OpPenAddElement constructor.
01703     SeeAlso:    -
01704 
01705 ********************************************************************************************/
01706 
01707 OpPenAddElement::OpPenAddElement()
01708 {
01709 }
01710 
01711 
01712 
01713 /********************************************************************************************
01714 
01715 >   BOOL OpPenAddElement::Init()
01716 
01717     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01718     Created:    30/9/94
01719     Inputs:     -
01720     Outputs:    -
01721     Returns:    TRUE if the operation could be successfully initialised 
01722                 FALSE if no more memory could be allocated 
01723                 
01724     Purpose:    OpPenAddElement initialiser method
01725     Errors:     ERROR will be called if there was insufficient memory to allocate the 
01726                 operation.
01727     SeeAlso:    -
01728 
01729 ********************************************************************************************/
01730 
01731 BOOL OpPenAddElement::Init()
01732 {
01733     return (RegisterOpDescriptor(0,                                     // tool ID
01734                                 _R(IDS_PENADDELEMENTOP),                    // string resource ID
01735                                 CC_RUNTIME_CLASS(OpPenAddElement),      // runtime class for Op
01736                                 OPTOKEN_PENADDELEMENT,                  // Ptr to token string
01737                                 OpPenAddElement::GetState,              // GetState function
01738                                 0,                                      // help ID = 0
01739                                 _R(IDBBL_PENADDELEMENTOP),                  // bubble help ID = 0
01740                                 0                                       // resource ID = 0
01741                                 )); 
01742 
01743 }               
01744 
01745 
01746 
01747 
01748 /********************************************************************************************
01749 
01750 >   OpState OpPenAddElement::GetState(String_256*, OpDescriptor*)
01751 
01752     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01753     Created:    30/9/94
01754     Inputs:     -
01755     Outputs:    -
01756     Returns:    The state of the OpPenAddElement
01757     Purpose:    For finding the OpPenAddElement's state. 
01758     Errors:     -
01759     SeeAlso:    -
01760 
01761 ********************************************************************************************/
01762                 
01763 OpState OpPenAddElement::GetState(String_256*, OpDescriptor*)
01764 {
01765     OpState OpSt;
01766     // Always enabled at the moment.
01767     return OpSt;
01768 }
01769 
01770 
01771 
01772 /********************************************************************************************
01773 
01774 >   void OpPenAddElement::DoPenAddElement(  NodePath* pNode,
01775                                             INT32 Attach,
01776                                             DocCoord ClickPoint,
01777                                             Spread *pClickSpread, 
01778                                             Path* pPath)
01779 
01780     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01781     Created:    30/9/94
01782 
01783     Inputs:     pNode       =   pointer to path in document to be edited
01784                 Attach      =   index of coordinate to attach new element to
01785                 ClickPoint  =   position of click over document.
01786                 pClickSpread=   pointer to spread where first click occured
01787                 pEditPath   =   pointer to path to edit
01788                 
01789     Outputs:    -
01790     Returns:    -
01791     Purpose:    This function starts of the dragging of a new path element which will be
01792                 added to the path held within pNode. The add position is taken as the
01793                 paths CurrentPosition variable.
01794 
01795     Errors:     failandexecute will be called if the operation fails in some way, most
01796                 likely when no memory is available. 
01797 
01798 ********************************************************************************************/
01799     
01800 void OpPenAddElement::DoPenAddElement(  NodePath* pNode,
01801                                         INT32 Attach,
01802                                         DocCoord ClickPoint,
01803                                         Spread *pClickSpread, 
01804                                         Path* pPath)
01805 {   
01806     // right we need to build a new path element, whose start coordinate and control is
01807     // determined by the end element of the passed docpath.
01808     // here are the rules for element addition
01809     // (1)  A curve is always added, and possibly converted to a line at the end
01810     //      of the drag
01811     // (2)  The curve elements smoothness is controled by the previous path elements
01812     //      smoothness. ie if we are adding to a smooth end point then the element
01813     //      we create will form a smooth join.
01814     // (3)  If the previous element is a line, we will create either a none smooth
01815     //      curve or another line, dependent on the drag.
01816 
01817     BOOL ok = FALSE;
01818     pEditPath = pPath;
01819     pEditNode = pNode;
01820     EditIndex = Attach;
01821 
01822     // Make sure the click point is in the correct coordinate system
01823     Spread* pPathSpread = pNode->FindParentSpread();
01824     if (pPathSpread != pClickSpread)
01825         ClickPoint = MakeRelativeToSpread(pPathSpread, pClickSpread, ClickPoint);
01826 
01827     // Get some interresting details about the path...
01828     Path* pDocPath = &(pNode->InkPath);
01829     INT32 ncoords = pDocPath->GetNumCoords();
01830 
01831     pDocPath->SetPathPosition(EditIndex);
01832 
01833     // Ensure things are in range and sensible
01834     if (ncoords<=0)
01835     {
01836         FailAndExecute();
01837         End();
01838         return;
01839     }
01840 
01841     // Read the attach point control details
01842     DocCoord  AttachC = pDocPath->GetCoord();
01843     PathVerb  AttachV = pDocPath->GetVerb();
01844     PathFlags AttachF = pDocPath->GetFlags();
01845 
01846     WobbleFlags wFlags;
01847 
01848     // Make sure we get the grid snapping involved
01849     DocCoord SnapPos = ClickPoint;
01850     DocView::SnapCurrent(pClickSpread,&SnapPos);
01851 
01852     // now create an element dependent on the control point type
01853 
01854     switch (AttachV)
01855     {
01856         case PT_MOVETO:
01857         // Nasty option to control adding paths to the begining of the path
01858         {
01859             pDocPath->SetPathPosition(EditIndex+1);
01860             DocCoord NextC = pDocPath->GetCoord();
01861             PathFlags NextF = pDocPath->GetFlags();
01862             
01863             if (NextF.IsRotate)
01864                 ok = InsertGhostedBezier(NextC, AttachC, SnapPos);
01865             else
01866             {
01867                 wFlags.PrevSister = TRUE;
01868                 ok = InsertCurvedLine(AttachC, SnapPos);
01869             }
01870         } 
01871         break;
01872 
01873 
01874         case PT_BEZIERTO:
01875         {
01876             pDocPath->SetPathPosition(EditIndex-1);
01877             DocCoord PrevC = pDocPath->GetCoord();
01878 
01879             if (AttachF.IsRotate)
01880                 ok = InsertGhostedBezier(PrevC, AttachC, SnapPos);
01881             else
01882             {
01883                 wFlags.PrevSister = TRUE;
01884                 ok = InsertCurvedLine(AttachC, SnapPos);
01885             }
01886         }
01887         break;
01888 
01889 
01890         case PT_LINETO:
01891         {
01892             wFlags.PrevSister = TRUE;
01893             ok = InsertCurvedLine(AttachC, SnapPos);
01894         }
01895         break;
01896     }
01897 
01898     if (ok)
01899     {
01900         // if we've created the element, set the wobble flags
01901         SetWobbleIndex(wFlags,3);
01902 
01903         // Save info about the click point and spread
01904         HandleFlags hFlags;
01905         SetDragHandles(hFlags, SnapPos, SnapPos, SnapPos, pClickSpread);
01906 
01907         // Render the first eor version on
01908         DocRect Rect = GetBoundingRect();
01909         RenderDragBlobs(Rect, StartSpread, FALSE);
01910 
01911         // now attempt to start the drag        
01912         ok = OpPenEditPath::DoPenDragPath();
01913     }
01914 
01915     if (!ok)
01916     {
01917         // failed to perform create so lets tidy up the path and exit
01918         pEditPath->ClearPath();
01919         FailAndExecute();
01920         End();
01921     }
01922 }   
01923 
01924 
01925 
01926 
01927 /********************************************************************************************
01928 
01929 >   void OpPenAddElement::DragFinished( DocCoord PointerPos, ClickModifiers ClickMods,
01930                                         Spread* pSpread, BOOL Success, BOOL bSolidDrag)
01931 
01932     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01933     Created:    26/9/94
01934     Inputs:     PointerPos - The position of the mouse at the end of the drag
01935                 ClickMods - the key modifiers being pressed
01936                 Success - TRUE if the drag was terminated properly, FALSE if it
01937                 was ended with the escape key being pressed
01938     Purpose:    This is called when a drag operation finishes.
01939     SeeAlso:    ClickModifiers
01940 
01941 ********************************************************************************************/
01942 
01943 void OpPenAddElement::DragFinished( DocCoord PointerPos, ClickModifiers ClickMods, 
01944                                     Spread* pSpread, BOOL Success, BOOL bSolidDrag)
01945 {
01946     // inform the base class to stop dragging
01947     OpPenEditPath::DragFinished( PointerPos, ClickMods, pSpread, Success, bSolidDrag);
01948 
01949     // Rub out the old EORed version of the path
01950     DocRect Rect = GetBoundingRect();
01951     RenderDragBlobs( Rect, StartSpread, bSolidDrag);
01952 
01953     if (Success)
01954     {
01955         // if the mouse hasn't moved make the curve into a path
01956         if (!HasMouseMoved(StartMousePos,CurrentMousePos))
01957             ConvertPathEnd(&(pEditNode->InkPath));
01958 
01959         // if we're adding to the start of a subpath, reverse our edited path
01960         PathVerb* verbs = pEditNode->InkPath.GetVerbArray();
01961         if (verbs[EditIndex] == PT_MOVETO)
01962             pEditPath->Reverse();
01963     }
01964     else
01965         FailAndExecute();
01966 
01967     // Once done remove the edit path
01968     End();
01969 }
01970 
01971 
01972 
01973 /********************************************************************************************
01974 
01975 >   OpPenClosePath::OpPenClosePath()
01976 
01977     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01978     Created:    6/10/94
01979     Purpose:    OpPenClosePath constructor.
01980     SeeAlso:    -
01981 
01982 ********************************************************************************************/
01983 
01984 OpPenClosePath::OpPenClosePath()
01985 {
01986     NumElements = 0;
01987     FirstDrag = TRUE;
01988 }
01989 
01990 
01991 
01992 /********************************************************************************************
01993 
01994 >   BOOL OpPenClosePath::Init()
01995 
01996     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01997     Created:    6/10/94
01998     Inputs:     -
01999     Outputs:    -
02000     Returns:    TRUE if the operation could be successfully initialised 
02001                 FALSE if no more memory could be allocated 
02002                 
02003     Purpose:    OpPenClosePath initialiser method
02004     Errors:     ERROR will be called if there was insufficient memory to allocate the 
02005                 operation.
02006     SeeAlso:    -
02007 
02008 ********************************************************************************************/
02009 
02010 BOOL OpPenClosePath::Init()
02011 {
02012     return (RegisterOpDescriptor(0,                                     // tool ID
02013                                 _R(IDS_PENCLOSEPATHOP),                     // string resource ID
02014                                 CC_RUNTIME_CLASS(OpPenClosePath),       // runtime class for Op
02015                                 OPTOKEN_PENCLOSEPATH,                   // Ptr to token string
02016                                 OpPenClosePath::GetState,               // GetState function
02017                                 0,                                      // help ID = 0
02018                                 _R(IDBBL_PENCLOSEPATHOP),                   // bubble help ID = 0
02019                                 0                                       // resource ID = 0
02020                                 )); 
02021 
02022 }               
02023 
02024 
02025 
02026 
02027 /********************************************************************************************
02028 
02029 >   OpState OpPenClosePath::GetState(String_256*, OpDescriptor*)
02030 
02031     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
02032     Created:    6/10/94
02033     Inputs:     -
02034     Outputs:    -
02035     Returns:    The state of the OpPenClosePath
02036     Purpose:    For finding the OpPenClosePath's state. 
02037     Errors:     -
02038     SeeAlso:    -
02039 
02040 ********************************************************************************************/
02041                 
02042 OpState OpPenClosePath::GetState(String_256*, OpDescriptor*)
02043 {
02044     OpState OpSt;
02045     // Always enabled at the moment.
02046     return OpSt;
02047 }
02048 
02049 
02050 
02051 /********************************************************************************************
02052 
02053 >   void OpPenClosePath::DoPenClosePath(NodePath* pNode,
02054                                         INT32 ClickIndex,
02055                                         Spread *pSpread,
02056                                         Path* pPath)
02057 
02058     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
02059     Created:    6/10/94
02060     Inputs:     pNode       =   pointer to nodepath in document to be edited
02061                 ClickIndex  =   index of element where click occured.
02062                 pSpread     =   pointer to spread where first click occured
02063                 pPath       =   pointer to path to edit
02064                 
02065     Outputs:    -
02066     Returns:    -
02067     Purpose:    
02068     Errors:     failandexecute will be called if the operation fails in some way, most
02069                 likely when no memory is available. 
02070 
02071 ********************************************************************************************/
02072     
02073 void OpPenClosePath::DoPenClosePath(NodePath* pNode,
02074                                     INT32 ClickIndex,
02075                                     Spread *pSpread,
02076                                     Path* pPath)
02077 {   
02078 
02079     BOOL ok = FALSE;
02080     pEditPath = pPath;
02081     pEditNode = pNode;
02082     EditIndex = ClickIndex;
02083 
02084     Path* pDocPath = &(pNode->InkPath);
02085 
02086     // find the extreems of this sub path. 
02087     INT32 lasti = ClickIndex;
02088     INT32 firsti = ClickIndex;
02089     pDocPath->FindEndElOfSubPath(&lasti);
02090     pDocPath->FindStartOfSubPath(&firsti);
02091 
02092     // ok the click could have occured over the begining of a subpath
02093     // or over the end of a subpath. The verb at ClickIndex should tell
02094     // us this.
02095 
02096     if ((lasti<=0) || (lasti<=firsti))
02097     {
02098         FailAndExecute();
02099         End();
02100         return;
02101     }
02102 
02103     OnMoveto = (ClickIndex == firsti);
02104 
02105     // Get the arrays for the destination path
02106     PathFlags* Flags  = pDocPath->GetFlagArray();
02107     PathVerb*  Verbs  = pDocPath->GetVerbArray();
02108     DocCoord*  Coords = pDocPath->GetCoordArray();
02109 
02110     DocCoord* EditCoords = pEditPath->GetCoordArray();
02111 
02112     // Read the final control point details
02113     PathVerb  LastV  = Verbs[lasti] & ~PT_CLOSEFIGURE;
02114     PathFlags LastF  = Flags[lasti];
02115 
02116     PathVerb  SecondV = Verbs[firsti+1] & ~PT_CLOSEFIGURE;
02117     PathFlags SecondF = Flags[firsti+1];
02118 
02119     // make sure the first index is sensible, ie offsets to a moveto
02120     if (Verbs[firsti] != PT_MOVETO)
02121     {
02122         FailAndExecute();
02123         End();
02124         return;
02125     }
02126 
02127     // dont render the track end until the pointer moves
02128     FirstHFlags.RenderTrackEnd = FALSE;
02129 
02130     // now create an element dependent on the control point type
02131 
02132     if (OnMoveto)
02133     {
02134         switch (LastV)
02135         {
02136             case PT_BEZIERTO:
02137                 if (LastF.IsRotate)
02138                     ok = InsertGhostedBezier(Coords[lasti-1], Coords[lasti], Coords[firsti]);
02139                 else
02140                 {
02141                     FirstWFlags.PrevSister = TRUE;
02142                     ok = InsertCurvedLine(Coords[lasti], Coords[firsti]);
02143                 }
02144                 NumElements++;
02145                 break;
02146         
02147             case PT_LINETO:
02148                 FirstWFlags.PrevSister = TRUE;
02149                 ok = InsertCurvedLine(Coords[lasti], Coords[firsti]);
02150                 NumElements++;
02151                 break;
02152 
02153             default:
02154                 FailAndExecute();
02155                 End();
02156                 return;
02157                 break;
02158         }
02159         if (ok)
02160             SetDragHandles(FirstHFlags, Coords[firsti], Coords[firsti], EditCoords[2], pSpread);
02161     }
02162     else
02163     {
02164         switch (SecondV)
02165         {
02166             case PT_BEZIERTO:
02167                 if (SecondF.IsRotate)
02168                     ok = InsertGhostedBezier(Coords[firsti+1], Coords[firsti], Coords[lasti]);
02169                 else
02170                 {
02171                     FirstWFlags.PrevSister = TRUE;
02172                     ok = InsertCurvedLine(Coords[firsti], Coords[lasti]);
02173                 }
02174                 NumElements++;
02175                 break;
02176         
02177             case PT_LINETO:
02178                 FirstWFlags.PrevSister = TRUE;
02179                 ok = InsertCurvedLine(Coords[firsti], Coords[lasti]);
02180                 NumElements++;
02181                 break;
02182 
02183             default:
02184                 FailAndExecute();
02185                 End();
02186                 return;
02187                 break;
02188         }
02189         if (ok)
02190             SetDragHandles(FirstHFlags, Coords[lasti], Coords[lasti], EditCoords[2], pSpread);
02191     }
02192 
02193     
02194     if (ok)
02195     {
02196         SetWobbleIndex(FirstWFlags,3);
02197         // Render the first eor version on
02198         DocRect Rect = GetBoundingRect();
02199         RenderDragBlobs(Rect, StartSpread, FALSE);
02200         // Now start the drag op on this path
02201         ok = OpPenEditPath::DoPenDragPath();
02202     }
02203 
02204     if (!ok) 
02205     {
02206         // failed to perform create so lets tidy up the path and exit
02207         pEditPath->ClearPath();
02208         FailAndExecute();
02209         End();
02210     }
02211 }   
02212 
02213 
02214 
02215 /********************************************************************************************
02216 
02217 >   BOOL OpPenClosePath::AddFirstElement()
02218 
02219     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
02220     Created:    11/10/94
02221     Inputs:
02222     Purpose:    This routine adds another element to the eor path. When closing a path
02223                 the user can either click or drag on the start point of a path. Now, we
02224                 dont really want to render what will become a smoothed start curve along
02225                 with the close element, if just a click has occured. So we wait for the
02226                 first DragPointerMove call and use this function to create the second
02227                 element. This basically looks much neater on screen.
02228     
02229 ********************************************************************************************/
02230 
02231 BOOL OpPenClosePath::AddFirstElement(Spread* pSpread)
02232 {
02233 
02234     // Calculate the first control point on the forward bezier.
02235     // This is calculated from the last element inserted into the
02236     // edit curve, ie the last control points ghost point.
02237 
02238     Path* pDocPath = &(pEditNode->InkPath);
02239 
02240     // Get the arrays for this path
02241     PathFlags* Flags  = pDocPath->GetFlagArray();
02242     PathVerb*  Verbs  = pDocPath->GetVerbArray();
02243     DocCoord*  Coords = pDocPath->GetCoordArray();
02244 
02245     DocCoord* EditCoords = pEditPath->GetCoordArray();
02246 
02247     INT32 Associate = EditIndex;
02248     if (OnMoveto)
02249         Associate++;
02250 
02251     PathVerb AssociateV = Verbs[Associate] & ~PT_CLOSEFIGURE;
02252 
02253     DocCoord Control;
02254     Control.x = EditCoords[3].x - (EditCoords[2].x - EditCoords[3].x);
02255     Control.y = EditCoords[3].y - (EditCoords[2].y - EditCoords[3].y);
02256 
02257     BOOL ok = TRUE;
02258             
02259     switch (AssociateV)
02260     {
02261         case PT_BEZIERTO:
02262         {
02263             FirstHFlags.RenderTrackEnd = TRUE;
02264             FirstHFlags.TrackPointMoves = FALSE;
02265             FirstHFlags.TrackPointSpins = TRUE;
02266             FirstWFlags.NextBrother = TRUE;
02267 
02268             PathFlags tempflags = Flags[Associate];
02269             if (OnMoveto)
02270                 ok = pEditPath->InsertCurveTo(Control, Coords[Associate+1], Coords[Associate+2], &tempflags);
02271             else
02272                 ok = pEditPath->InsertCurveTo(Control, Coords[Associate-2], Coords[Associate-3], &tempflags);
02273 
02274             NumElements++;
02275         }
02276         break;
02277 
02278         case PT_LINETO:
02279         {
02280             FirstHFlags.RenderTrackEnd = FALSE;
02281             FirstHFlags.TrackPointMoves = FALSE;
02282             FirstHFlags.TrackPointSpins = FALSE;
02283         }
02284         break;
02285     }
02286 
02287     // if we've created the element, set the wobble flags
02288     if (ok) 
02289     {
02290         SetWobbleIndex(FirstWFlags,3);
02291         if (OnMoveto)
02292             SetDragHandles(FirstHFlags, Coords[EditIndex], Coords[EditIndex+1], EditCoords[2], pSpread);
02293         else
02294             SetDragHandles(FirstHFlags, Coords[EditIndex], Coords[EditIndex-1], EditCoords[2], pSpread);
02295     }
02296 
02297     return ok;
02298 }
02299 
02300 
02301 
02302 
02303 /********************************************************************************************
02304 
02305 >   void OpPenClosePath::DragPointerMove(DocCoord PointerPos, 
02306                                         ClickModifiers ClickMods,
02307                                         Spread* pSpread, BOOL bSolidDrag)
02308 
02309     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
02310     Created:    26/9/94
02311     Inputs:     PointerPos - The current position of the mouse in Doc Coords
02312                 ClickMods - Which key modifiers are being pressed
02313     Purpose:    This is called every time the mouse moves, during a drag.
02314     SeeAlso:    ClickModifiers
02315 
02316 ********************************************************************************************/
02317 
02318 void OpPenClosePath::DragPointerMove(DocCoord PointerPos, 
02319                                     ClickModifiers ClickMods,
02320                                     Spread* pSpread, BOOL bSolidDrag)
02321 {
02322 
02323     // If drag has moved onto a different spread, convert the coord to be relative to the
02324     // original spread.
02325     if (pSpread != StartSpread)
02326         PointerPos = MakeRelativeToSpread(StartSpread, pSpread, PointerPos);
02327 
02328     // Make sure we get the grid snapping involved
02329     DocCoord SnapPos = PointerPos;
02330     DocView::SnapCurrent(pSpread,&SnapPos);
02331 
02332     if (FirstDrag)
02333     {
02334 
02335         // Rub out the old EORed version of the path and stick a new one on
02336         DocRect Rect = GetBoundingRect();
02337         RenderDragBlobs(Rect, StartSpread, bSolidDrag);
02338 
02339         if (!AddFirstElement(pSpread))
02340         {
02341             pEditPath->ClearPath();
02342             EndDrag();
02343             FailAndExecute();
02344             End();
02345             return;
02346         }
02347 
02348         Rect.IncludePoint(SnapPos);
02349 
02350         // Alter the curve drag handles
02351         ChangeTrackHandle(SnapPos, ClickMods.Constrain);
02352         
02353         // make sure we wobble all the coordinates around this control point
02354         // correctly.
02355         WobbleCoords();
02356 
02357         // Now render the blobs back on again
02358         Rect = Rect.Union(GetBoundingRect());
02359         RenderDragBlobs(Rect, StartSpread, bSolidDrag);
02360 
02361     }
02362     else
02363     {
02364         OpPenEditPath::DragPointerMove(SnapPos, ClickMods, pSpread, bSolidDrag);
02365     } 
02366 
02367     FirstDrag = FALSE;
02368 }
02369 
02370 
02371 
02372 
02373 /********************************************************************************************
02374 
02375 >   void OpPenClosePath::DragFinished( DocCoord PointerPos, ClickModifiers ClickMods,
02376                                         Spread* pSpread, BOOL Success, BOOL bSolidDrag)
02377 
02378     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
02379     Created:    26/9/94
02380     Inputs:     PointerPos - The position of the mouse at the end of the drag
02381                 ClickMods - the key modifiers being pressed
02382                 Success - TRUE if the drag was terminated properly, FALSE if it
02383                 was ended with the escape key being pressed
02384     Purpose:    This is called when a drag operation finishes.
02385     SeeAlso:    ClickModifiers
02386 
02387 ********************************************************************************************/
02388 
02389 void OpPenClosePath::DragFinished( DocCoord PointerPos, ClickModifiers ClickMods, 
02390                                     Spread* pSpread, BOOL Success, BOOL bSolidDrag)
02391 {
02392     // inform the base class to stop dragging
02393     OpPenEditPath::DragFinished( PointerPos, ClickMods, pSpread, Success, bSolidDrag);
02394 
02395     // Rub out the old EORed version of the path
02396     DocRect Rect = GetBoundingRect();
02397     RenderDragBlobs( Rect, StartSpread, bSolidDrag);
02398 
02399     if (Success)
02400     {
02401         if (!HasMouseMoved(StartMousePos,CurrentMousePos))
02402         {
02403             // if the mouse hasn't moved then we haven't edited the
02404             // first element in the path being closed, so bin our replacement
02405             // element.
02406             if (NumElements == 2)
02407             {
02408                 INT32 last = 0;
02409                 pEditPath->FindEndOfSubPath(&last);
02410                 pEditPath->DeleteFromElement(last);             
02411                 NumElements--;
02412             }
02413             ConvertPathEnd(&(pEditNode->InkPath));
02414         }
02415     }
02416     else
02417         FailAndExecute();
02418 
02419     End();
02420 }
02421 
02422 
02423 
02424 /********************************************************************************************
02425 
02426 >   OpAddPath::OpAddPath()
02427 
02428     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
02429     Created:    6/10/94
02430     Purpose:    OpAddPath constructor.
02431     SeeAlso:    -
02432 
02433 ********************************************************************************************/
02434 
02435 OpAddPath::OpAddPath()
02436 {
02437     // dummy constructor for the moment.
02438 }
02439 
02440 
02441 
02442 /********************************************************************************************
02443 
02444 >   void OpAddPath::AugmentPathWithPath(Path* SourcePath, NodePath* DestinNode, INT32 after) 
02445 
02446     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
02447     Created:    3/10/94
02448     Inputs:     SourcePath
02449                 DestinNode
02450                 pNode       = Pointer to nodepath in document to add element to
02451                 pElement    = Pointer to path element to add
02452                 after       = offset of verb (line, move, curve) after which to add element 
02453 
02454     Outputs:    -
02455     Purpose:    This operation is called to update an existing path. If successful it will
02456                 create a new node, copy the contents of pElement into it and apply the
02457                 current attributes to it.
02458 
02459 ********************************************************************************************/
02460 
02461 
02462 ExecuteType OpAddPath::AugmentPathWithPath(Path* pSourcePath, NodePath* pDestinNode, INT32 after)
02463 {
02464 #ifndef STANDALONE
02465 
02466     // Get a pointer to the path object (makes reading easier)
02467     Path* pDestPath = &(pDestinNode->InkPath);
02468 
02469     PathFlags* Flags = pDestPath->GetFlagArray();
02470     PathVerb* Verbs = pDestPath->GetVerbArray();
02471     DocCoord* Coords = pDestPath->GetCoordArray();
02472 
02473     PathVerb AfterVerb = Verbs[after] & ~PT_CLOSEFIGURE;
02474 
02475     // Set the insert position (v important for path->makespace func)
02476     pDestPath->SetPathPosition(after+1);
02477 
02478     // calc how many coords in the insert path
02479     INT32 NumInSource = pSourcePath->GetNumCoords();
02480 
02481     // If we're undoing, create an action for this insert
02482     Action* UnAction;               // pointer to action that might be created
02483     ActionCode Act;                 // Action code that might be used
02484 
02485     // Remove selection from around this handle
02486     Act = DeselectHandle(pDestinNode, after);
02487     if (Act == AC_FAIL)
02488         return ExeInclusive;
02489 
02490     // if inserting after a moveto, we change the moveto.
02491     if (AfterVerb == PT_MOVETO)
02492     {
02493         // alter the coordinate of the move to
02494         Act = ModifyElementAction::Init(this, 
02495                                         &UndoActions,
02496                                         Verbs[after],
02497                                         Flags[after],
02498                                         Coords[after],
02499                                         after,
02500                                         pDestinNode,
02501                                         (Action**)&UnAction);
02502 
02503         if (Act == AC_FAIL)
02504             return ExeInclusive;
02505 
02506         DocCoord* SCoords = pSourcePath->GetCoordArray();
02507         Coords[after] = SCoords[0];
02508         Flags[after].IsSelected = TRUE;
02509 
02510     }
02511 
02512     // record a record for the undo. we're inserting elements so we'll want to delete them during undo      
02513     Act = RemovePathElementAction::Init(this, &UndoActions, NumInSource-1, after+1, (Action**)(&UnAction));
02514     if (Act == AC_FAIL)
02515         return ExeInclusive;
02516 
02517     // record the path pointer where the remove will take place. (Why isn't this part of the above?)
02518     if (Act == AC_OK)
02519         ((RemovePathElementAction*)UnAction)->RecordPath(pDestinNode);
02520 
02521     // copy the necessary data into the destination path. return fail if unable to do so
02522     if (!pDestPath->MergeSectionFrom(after+1, *pSourcePath, 1, NumInSource-1))
02523         return ExeExclusive;
02524 
02525 #endif
02526     return ExeNone;
02527                                                                      
02528 }
02529 
02530 
02531 
02532 /********************************************************************************************
02533 
02534 >   void OpAddPath::DeselectPoint(NodePath* pDestNode, INT32 after) 
02535 
02536     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
02537     Created:    3/10/94
02538     Inputs:     pDestinNode = pointer to a node whose path contains a point we will deselect
02539                 position    = offset of verb (line, move, curve) which will be deselected 
02540     Outputs:    -
02541     Purpose:
02542 
02543 ********************************************************************************************/
02544 
02545 ActionCode OpAddPath::DeselectPoint(NodePath* pDestNode, INT32 position)
02546 {
02547 #ifndef STANDALONE
02548 
02549     Action* UnAction;               // pointer to action that might be created
02550     ActionCode Act;                 // Action code that might be used
02551 
02552     PathFlags* Flags = pDestNode->InkPath.GetFlagArray();
02553 
02554     Act = ModifyFlagsAction::Init(this, &UndoActions, Flags[position], position, pDestNode, (Action**)(&UnAction));
02555     if (Act != AC_FAIL)
02556         Flags[position].IsSelected = FALSE;
02557 
02558     return Act;
02559 
02560 #else
02561     return AC_OK;
02562 #endif
02563 }
02564 
02565 
02566 
02567 /********************************************************************************************
02568 
02569 >   void OpAddPath::DeselectHandle(NodePath* pDestNode, INT32 after) 
02570 
02571     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
02572     Created:    3/10/94
02573     Inputs:     pDestinNode = pointer to a node whose path contains a point we will deselect
02574                 after       = offset of verb (line, move, curve) which will be deselected  
02575     Outputs:    -
02576     Purpose:    
02577 
02578 ********************************************************************************************/
02579 
02580 ActionCode OpAddPath::DeselectHandle(NodePath* pDestinNode, INT32 after)
02581 {
02582 #ifndef STANDALONE
02583 
02584     ActionCode Act;                 // Action code that might be used
02585 
02586     // Remove the selected end bit
02587     Act = DeselectPoint(pDestinNode, after);
02588     if (Act == AC_FAIL)
02589         return Act;
02590 
02591     INT32 lasti = pDestinNode->InkPath.GetNumCoords() -1;
02592 
02593     // Now check and remove any other selection bits around the handle
02594     PathVerb* Verbs = pDestinNode->InkPath.GetVerbArray();
02595     PathVerb AfterVerb = Verbs[after] & ~PT_CLOSEFIGURE;
02596 
02597     switch (AfterVerb)
02598     {
02599         case PT_MOVETO:
02600             if (after < lasti)
02601             {
02602                 PathVerb NextVerb = Verbs[after+1] & ~PT_CLOSEFIGURE;
02603                 if (NextVerb == PT_BEZIERTO)
02604                     Act = DeselectPoint(pDestinNode,after+1);
02605             }
02606         break;
02607 
02608         case PT_BEZIERTO:
02609             if (after>0)
02610                 Act = DeselectPoint(pDestinNode,after-1);
02611             break;
02612     }
02613 
02614     return Act;
02615 
02616 #else
02617     return AC_OK;
02618 #endif
02619 }
02620 
02621 
02622 
02623 
02624 
02625 /********************************************************************************************
02626 
02627 >   OpAddNewPath::OpAddNewPath()
02628 
02629     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
02630     Created:    30/9/94
02631     Purpose:    OpAddNewPath constructor
02632     SeeAlso:    -
02633 
02634 ********************************************************************************************/
02635 
02636 OpAddNewPath::OpAddNewPath()
02637 {
02638 }
02639 
02640 
02641 
02642 /********************************************************************************************
02643 
02644 >   BOOL OpAddNewPath::Init()
02645 
02646     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
02647     Created:    30/9/94
02648     Inputs:     -
02649     Outputs:    -
02650     Returns:    TRUE if the operation could be successfully initialised 
02651                 FALSE if no more memory could be allocated 
02652                 
02653     Purpose:    OpAddNewPath initialiser method
02654     Errors:     ERROR will be called if there was insufficient memory to allocate the 
02655                 operation.
02656     SeeAlso:    -
02657 
02658 ********************************************************************************************/
02659 
02660 BOOL OpAddNewPath::Init()
02661 {
02662     return (RegisterOpDescriptor(0,                                     // tool ID
02663                                 _R(IDS_ADDNEWPATHOP),                       // string resource ID
02664                                 CC_RUNTIME_CLASS(OpAddNewPath),         // runtime class for Op
02665                                 OPTOKEN_ADDNEWPATH,                     // Ptr to token string
02666                                 OpAddNewPath::GetState,                 // GetState function
02667                                 0,                                      // help ID = 0
02668                                 _R(IDBBL_ADDNEWPATHOP),                     // bubble help ID = 0
02669                                 0                                       // resource ID = 0
02670                                 )); 
02671 
02672 }               
02673 
02674 
02675 /********************************************************************************************
02676 
02677 >   OpState OpAddNewPath::GetState(String_256*, OpDescriptor*)
02678 
02679     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
02680     Created:    30/9/94
02681     Inputs:     -
02682     Outputs:    -
02683     Returns:    The state of the OpAddNewPath
02684     Purpose:    For finding the OpAddNewPath's state. 
02685     Errors:     -
02686     SeeAlso:    -
02687 
02688 ********************************************************************************************/
02689 
02690 OpState OpAddNewPath::GetState(String_256* UIDescription, OpDescriptor*)
02691 {
02692     OpState OpSt;
02693     return OpSt;   
02694 }
02695 
02696 
02697 /********************************************************************************************
02698 
02699 >   void OpAddNewPath::GetOpName(String_256* OpName)
02700 
02701     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
02702     Created:    30/9/94
02703     Inputs:     -
02704     Outputs:    The undo string for the operation
02705     Returns:    
02706     Purpose:    The GetOpName fn is overridden so that we return back a description
02707                 appropriate to the type of attribute that the operation applies.
02708                     
02709     Errors:     -
02710     SeeAlso:    -
02711 
02712 ********************************************************************************************/
02713 
02714 void OpAddNewPath::GetOpName(String_256* OpName)
02715 {
02716     *OpName = String_256(_R(IDS_UNDO_ADDNEWPATHOP));
02717 }
02718 
02719 
02720 
02721 
02722 
02723 /********************************************************************************************
02724 
02725 >   void OpAddNewPath::DoAddNewPath( Path* pAddPath, Spread* pSpread )
02726 
02727     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
02728     Created:    30/9/94
02729     Inputs:     pAddPath    = Pointer to a path to add to the document
02730                 pSpread     = Pointer to the spread to add the path to.
02731     Outputs:    -
02732     Purpose:    This operation is called to add a path to the tree. If successfull it will
02733                 create a new node, copy the contents of pAddPath into it and apply the
02734                 current attributes to it.
02735 
02736 ********************************************************************************************/
02737 
02738 void OpAddNewPath::DoAddNewPath(Path* pAddPath, Spread* pSpread)
02739 {
02740     BeginSlowJob();
02741 
02742     if (!DoStartSelOp(FALSE,TRUE))
02743     {
02744         FailAndExecute(); End(); return;
02745     }
02746     
02747     // We had better copy the path back over the original and re-calc the bounding box
02748     DocView* pDocView = DocView::GetSelected();
02749     ERROR2IF( pDocView == NULL, (void)0, "There was no selected DocView when editing a path" );
02750 
02751     // Create a path to hold the data
02752     NodePath* NewPath = new NodePath;
02753     if (!NewPath)
02754     {
02755         FailAndExecute(); End(); return;
02756     }
02757 
02758     if (!NewPath->SetUpPath(24, 12))
02759     {
02760         InformError( _R(IDS_OUT_OF_MEMORY), _R(IDS_OK) );
02761         delete NewPath;
02762         FailAndExecute(); End(); return;
02763     }
02764     
02765     // Copy the data from the edit path to the new path
02766     if (!NewPath->InkPath.CopyPathDataFrom(pAddPath))
02767     {
02768         NewPath->CascadeDelete();
02769         delete NewPath;
02770         FailAndExecute(); End(); return;
02771     }
02772     
02773     // Apply attributes to the new node
02774     Document* pDoc = GetWorkingDoc();
02775     if (pDoc!=NULL)
02776     {
02777         // Apply the current attributes to the path
02778         if (!(pDoc->GetAttributeMgr().ApplyCurrentAttribsToNode((NodeRenderableInk*)NewPath)))
02779         {
02780             NewPath->CascadeDelete();
02781             delete NewPath;
02782             FailAndExecute(); End(); return;
02783         }
02784     }
02785     else
02786     {
02787         NewPath->CascadeDelete();
02788         delete NewPath;
02789         FailAndExecute(); End(); return;
02790     }
02791 
02792     if (!DoInsertNewNode(NewPath, pSpread, TRUE))
02793     {
02794         NewPath->CascadeDelete();
02795         delete NewPath;
02796         FailAndExecute(); End(); return;
02797     }
02798 
02799     // Reselect the last endpoint in the new path
02800     NewPath->InkPath.GetFlagArray()[NewPath->InkPath.GetNumCoords()-1].IsSelected = TRUE;
02801 
02802     // terminate the op
02803     End();
02804         
02805 }
02806 
02807 
02808 
02809 /********************************************************************************************
02810 
02811 >   OpAddPathToPath::OpAddPathToPath()
02812 
02813     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
02814     Created:    3/10/94
02815     Purpose:    OpAddPathToPath constructor
02816     SeeAlso:    -
02817 
02818 ********************************************************************************************/
02819 
02820 OpAddPathToPath::OpAddPathToPath()
02821 {
02822 }
02823 
02824 
02825 
02826 /********************************************************************************************
02827 
02828 >   BOOL OpAddPathToPath::Init()
02829 
02830     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
02831     Created:    3/10/94
02832     Inputs:     -
02833     Outputs:    -
02834     Returns:    TRUE if the operation could be successfully initialised 
02835                 FALSE if no more memory could be allocated 
02836                 
02837     Purpose:    OpAddPathToPath initialiser method
02838     Errors:     ERROR will be called if there was insufficient memory to allocate the 
02839                 operation.
02840     SeeAlso:    -
02841 
02842 ********************************************************************************************/
02843 
02844 BOOL OpAddPathToPath::Init()
02845 {
02846     return (RegisterOpDescriptor(0,                                     // tool ID
02847                                 _R(IDS_ADDPATHTOPATHOP),                    // string resource ID
02848                                 CC_RUNTIME_CLASS(OpAddPathToPath),      // runtime class for Op
02849                                 OPTOKEN_ADDPATHTOPATH,                  // Ptr to token string
02850                                 OpAddPathToPath::GetState,              // GetState function
02851                                 0,                                      // help ID = 0
02852                                 _R(IDBBL_ADDPATHTOPATHOP),                  // bubble help ID = 0
02853                                 0                                       // resource ID = 0
02854                                 )); 
02855 
02856 }               
02857 
02858 
02859 /********************************************************************************************
02860 
02861 >   OpState OpAddPathToPath::GetState(String_256*, OpDescriptor*)
02862 
02863     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
02864     Created:    3/10/94
02865     Inputs:     -
02866     Outputs:    -
02867     Returns:    The state of the OpAddPathToPath
02868     Purpose:    For finding the OpAddPathToPath's state. 
02869     Errors:     -
02870     SeeAlso:    -
02871 
02872 ********************************************************************************************/
02873 
02874 OpState OpAddPathToPath::GetState(String_256* UIDescription, OpDescriptor*)
02875 {
02876     OpState OpSt;
02877     return OpSt;   
02878 }
02879 
02880 
02881 /********************************************************************************************
02882 
02883 >   void OpAddPathToPath::GetOpName(String_256* OpName)
02884 
02885     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
02886     Created:    3/10/94
02887     Inputs:     -
02888     Outputs:    The undo string for the operation
02889     Returns:    
02890     Purpose:    The GetOpName fn is overridden so that we return back a description
02891                 appropriate to the type of attribute that the operation applies.
02892                     
02893     Errors:     -
02894     SeeAlso:    -
02895 
02896 ********************************************************************************************/
02897 
02898 void OpAddPathToPath::GetOpName(String_256* OpName)
02899 {
02900     *OpName = String_256(_R(IDS_UNDO_ADDPATHTOPATHOP));
02901 }
02902 
02903 
02904 
02905 
02906 
02907 /********************************************************************************************
02908 
02909 >   void OpAddPathToPath::DoAddPathToPath( NodePath* pNode, Path* pElement, INT32 index )
02910 
02911     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
02912     Created:    3/10/94
02913     Inputs:     pNode       = Pointer to nodepath in document to add element to
02914                 pElement    = Pointer to path element to add
02915                 index       = index within path at which to add the new elements
02916 
02917     Outputs:    -
02918     Purpose:    This operation is called to update an existing path. If successful it will
02919                 create a new node, copy the contents of pElement into it and apply the
02920                 current attributes to it.
02921 
02922 ********************************************************************************************/
02923 
02924 void OpAddPathToPath::DoAddPathToPath(NodePath* pNode, Path* pElement, INT32 index)
02925 {
02926 #ifndef STANDALONE
02927 
02928     BeginSlowJob();
02929 
02930     DoStartSelOp(TRUE,TRUE);
02931     
02932     DocView* pDocView = DocView::GetSelected();
02933     ERROR2IF( pDocView == NULL, (void)0, "There was no selected docview when augmenting a path" );
02934 
02935     // Save the bounds of the path for undo/redo
02936     if (RecalcBoundsAction::DoRecalc(this, &UndoActions, pNode) == AC_FAIL)
02937     {
02938         FailAndExecute();
02939         End();
02940         return;
02941     }
02942 
02943     // Go and copy the edited path to the end of the original
02944 
02945     ExecuteType Exe = AugmentPathWithPath(pElement, pNode, index);
02946     if (Exe != ExeNone)
02947     {
02948         InformError( _R(IDS_OUT_OF_MEMORY), _R(IDS_OK) );
02949         if (Exe == ExeInclusive)
02950             FailAndExecute();
02951         if (Exe == ExeExclusive)
02952             FailAndExecuteAllButLast();
02953         End();
02954         return;
02955     }   
02956 
02957     // Recalculate the path's bounding box
02958     pNode->InvalidateBoundingRect();
02959     
02960     // tell the world that something in the selection has changed 
02961     // so that selection bounds are updated
02962     GetApplication()->FindSelection()->Update(TRUE);
02963 
02964     // record new bounds action undo/redo
02965     if (RecordBoundsAction::DoRecord(this, &UndoActions, pNode) == AC_FAIL)
02966     {
02967         FailAndExecute();
02968         End();
02969         return;
02970     }
02971 
02972 #endif
02973     End();
02974 }
02975 
02976 
02977 
02978 
02979 
02980 
02981 /********************************************************************************************
02982 
02983 >   OpClosePathWithPath::OpClosePathWithPath()
02984 
02985     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
02986     Created:    3/10/94
02987     Purpose:    OpClosePathWithPath constructor
02988     SeeAlso:    -
02989 
02990 ********************************************************************************************/
02991 
02992 OpClosePathWithPath::OpClosePathWithPath()
02993 {
02994 }
02995 
02996 
02997 
02998 /********************************************************************************************
02999 
03000 >   BOOL OpClosePathWithPath::Init()
03001 
03002     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
03003     Created:    3/10/94
03004     Inputs:     -
03005     Outputs:    -
03006     Returns:    TRUE if the operation could be successfully initialised 
03007                 FALSE if no more memory could be allocated 
03008                 
03009     Purpose:    OpClosePathWithPath initialiser method
03010     Errors:     ERROR will be called if there was insufficient memory to allocate the 
03011                 operation.
03012     SeeAlso:    -
03013 
03014 ********************************************************************************************/
03015 
03016 BOOL OpClosePathWithPath::Init()
03017 {
03018     return (RegisterOpDescriptor(0,                                     // tool ID
03019                                 _R(IDS_CLOSEPATHWITHPATHOP),                // string resource ID
03020                                 CC_RUNTIME_CLASS(OpClosePathWithPath),  // runtime class for Op
03021                                 OPTOKEN_CLOSEPATHWITHPATH,              // Ptr to token string
03022                                 OpClosePathWithPath::GetState,          // GetState function
03023                                 0,                                      // help ID = 0
03024                                 _R(IDBBL_CLOSEPATHWITHPATHOP),              // bubble help ID = 0
03025                                 0                                       // resource ID = 0
03026                                 )); 
03027 
03028 }               
03029 
03030 
03031 /********************************************************************************************
03032 
03033 >   OpState OpClosePathWithPath::GetState(String_256*, OpDescriptor*)
03034 
03035     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
03036     Created:    3/10/94
03037     Inputs:     -
03038     Outputs:    -
03039     Returns:    The state of the OpClosePathWithPath
03040     Purpose:    For finding the OpClosePathWithPath's state. 
03041     Errors:     -
03042     SeeAlso:    -
03043 
03044 ********************************************************************************************/
03045 
03046 OpState OpClosePathWithPath::GetState(String_256* UIDescription, OpDescriptor*)
03047 {
03048     OpState OpSt;
03049     return OpSt;   
03050 }
03051 
03052 
03053 /********************************************************************************************
03054 
03055 >   void OpClosePathWithPath::GetOpName(String_256* OpName)
03056 
03057     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
03058     Created:    3/10/94
03059     Inputs:     -
03060     Outputs:    The undo string for the operation
03061     Returns:    
03062     Purpose:    The GetOpName fn is overridden so that we return back a description
03063                 appropriate to the type of attribute that the operation applies.
03064                     
03065     Errors:     -
03066     SeeAlso:    -
03067 
03068 ********************************************************************************************/
03069 
03070 void OpClosePathWithPath::GetOpName(String_256* OpName)
03071 {
03072     *OpName = String_256(_R(IDS_UNDO_CLOSEPATHWITHPATHOP));
03073 }
03074 
03075 
03076 
03077 
03078 
03079 /********************************************************************************************
03080 
03081 >   void OpClosePathWithPath::DoClosePathWithPath( NodePath* pNode, Path* pEditPath, INT32 index )
03082 
03083     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
03084     Created:    3/10/94
03085     Inputs:     pNode       = Pointer to nodepath in document to add element to
03086                 pEditPath   = Pointer to path element to add
03087                 index       = offset of subpath end in destin path. The end can either be
03088                               the moveto of the subpath or the index of the final coordinate
03089                               in the subpath.
03090                 
03091     Outputs:    -
03092     Purpose:    This op takes as parameters a destination path being affected, and a source 
03093                 path which may be the result of a drag edit operation. 
03094                 The idea is that the op will take the components of the source path and add
03095                 them in a deterministic way to the destination path, resulting in a closed
03096                 destination path.
03097                 The source path is assumed to have either 1 or 2 elements in it. The first
03098                 element will be added to the end of the destination path and act as a
03099                 closing piece. The second element of the source path will affect the
03100                 first element in the destination path. If both elements match in type, ie
03101                 are two curves, the none end control handles of the source will be copied 
03102                 to the destination.
03103                 This op as can be seen is rather specific, and is used by the pen tool to
03104                 close an open path. Imagine clicking on the first control point of a path
03105                 and draging out control handles. The handles will affect the previous and
03106                 next elements of the path. The previous being a close element and the next
03107                 being the first element of the path. Hence the need for this strange op.
03108     
03109 ********************************************************************************************/
03110 
03111 void OpClosePathWithPath::DoClosePathWithPath(NodePath* pDestinNode, Path* pEditPath, INT32 index)
03112 {
03113 #ifndef STANDALONE
03114 
03115     BeginSlowJob();
03116 
03117     DoStartSelOp(TRUE,TRUE);
03118     
03119     DocView* pDocView = DocView::GetSelected();
03120     ERROR2IF( pDocView == NULL, (void)0, "There was no selected doc view when closing a path" );
03121 
03122     // Save the bounds of the path for undo/redo
03123     if (RecalcBoundsAction::DoRecalc(this, &UndoActions, pDestinNode) == AC_FAIL)
03124     {
03125         FailAndExecute();
03126         End();
03127         return;
03128     }
03129 
03130     // go and count the number of elements given to us in the edit path
03131     INT32 ind = 0;
03132     INT32 i;
03133     for (i=0; pEditPath->FindNext(&ind); i++);
03134     ENSURE(i<3, "There are two many elements in the edit path when closing a path" );
03135 
03136     if (i<1 || i>2)
03137     {
03138         FailAndExecute();
03139         End();
03140         return;
03141     }
03142 
03143     // get a pointer to the destination path
03144     Path* pDestinPath = &(pDestinNode->InkPath);
03145 
03146     // Get the arrays for these paths
03147     PathFlags* SFlags  = pEditPath->GetFlagArray();
03148     PathVerb*  SVerbs  = pEditPath->GetVerbArray();
03149     PathVerb*  DVerbs  = pDestinPath->GetVerbArray();
03150 
03151     BOOL OnMove = (DVerbs[index] == PT_MOVETO);
03152 
03153     INT32 SEnd = 0;
03154     pEditPath->FindEndOfSubPath(&SEnd);
03155     
03156     if (i==2)
03157     {
03158         // if we have two elements in the path, go and affect the first element in
03159         // the destin path with the data held in the second element of the edit path
03160         // Er, this could be the other way around so watch out!
03161 
03162         INT32 First = index;
03163         INT32 Second;
03164 
03165         if (OnMove)
03166         {
03167             First++;
03168             Second = First+1;
03169         }
03170         else
03171         {
03172             First--;
03173             Second = First-1;
03174         }
03175 
03176         // If the elements match in type, affect the destination
03177         if ((SVerbs[SEnd] == DVerbs[First]) && (DVerbs[First] == PT_BEZIERTO))
03178         {
03179             
03180             DocCoord*  SCoords = pEditPath->GetCoordArray();
03181             DocCoord*  DCoords = pDestinPath->GetCoordArray();
03182             PathFlags* DFlags  = pDestinPath->GetFlagArray();
03183 
03184             ModifyElementAction* UnAction;
03185             ActionCode Act;
03186 
03187             // Alter the moveto's flags
03188             Act = ModifyElementAction::Init(this, 
03189                                             &UndoActions,
03190                                             DVerbs[index],
03191                                             DFlags[index],
03192                                             DCoords[index],
03193                                             index,
03194                                             pDestinNode,
03195                                             (Action**)&UnAction);
03196             if (Act == AC_FAIL)
03197             {   
03198                 FailAndExecute();
03199                 End();
03200                 return;
03201             }
03202             DFlags[index].IsRotate = TRUE;
03203 
03204             // Move the first control point
03205             Act = ModifyElementAction::Init(this, 
03206                                             &UndoActions,
03207                                             DVerbs[First],
03208                                             DFlags[First],
03209                                             DCoords[First],
03210                                             First,
03211                                             pDestinNode,
03212                                             (Action**)&UnAction);
03213             if (Act == AC_FAIL)
03214             {   
03215                 FailAndExecute();
03216                 End();
03217                 return;
03218             }
03219             DCoords[First] = SCoords[SEnd];
03220             DFlags[First].IsRotate = TRUE;
03221 
03222             // Move the second control point
03223             Act = ModifyElementAction::Init(this, 
03224                                             &UndoActions,
03225                                             DVerbs[Second],
03226                                             DFlags[Second],
03227                                             DCoords[Second],
03228                                             Second,
03229                                             pDestinNode,
03230                                             (Action**)&UnAction);
03231             if (Act == AC_FAIL)
03232             {   
03233                 FailAndExecute();
03234                 End();
03235                 return;
03236             }
03237             DCoords[Second] = SCoords[SEnd+1];
03238             DFlags[Second].IsRotate = TRUE;
03239         }
03240 
03241         // now bin the last element of the path
03242         pEditPath->DeleteFromElement(SEnd);
03243     }
03244 
03245     INT32 DStart = index;
03246     INT32 DEnd = index;
03247     pDestinPath->FindStartOfSubPath(&DStart);
03248     pDestinPath->FindEndElOfSubPath(&DEnd);
03249 
03250     if (!OnMove)
03251     {
03252         // if OnMove is false ie someone is closing a curve from the
03253         // selected moveto backwards, then we need to deselect the moveto
03254         // element and reverse the path.
03255 
03256         ActionCode Act = DeselectHandle(pDestinNode, DStart);
03257         if (Act == AC_FAIL)
03258         {   
03259             FailAndExecute();
03260             End();
03261             return;
03262         }
03263         pEditPath->Reverse();
03264     }
03265 
03266     // set a close figure on the element we're about to add and bin any selection
03267     SEnd = (pEditPath->GetNumCoords())-1;
03268     SVerbs[SEnd] = SVerbs[SEnd] | PT_CLOSEFIGURE;
03269     SFlags[SEnd].IsSelected = FALSE;
03270     
03271     // now set the filled bit on the path to make sure it really does
03272     // get filled once closed.
03273     ModifyFilledAction* pAction;
03274 
03275     if (ModifyFilledAction::Init(this, &UndoActions, TRUE, FALSE, pDestinNode, (Action**)(&pAction))== AC_FAIL)
03276     {   
03277         FailAndExecute();
03278         End();
03279         return;
03280     }
03281 
03282     pDestinNode->InkPath.IsFilled = TRUE;
03283 
03284     // Go and copy the edited path to the end of the original
03285     ExecuteType Exe = AugmentPathWithPath(pEditPath, pDestinNode, DEnd);
03286     if (Exe != ExeNone)
03287     {
03288         InformError( _R(IDS_OUT_OF_MEMORY), _R(IDS_OK) );
03289         if (Exe == ExeInclusive)
03290             FailAndExecute();
03291         if (Exe == ExeExclusive)
03292             FailAndExecuteAllButLast();
03293         End();
03294         return;
03295     }   
03296 
03297     // Recalculate the path's bounding box
03298     pDestinNode->InvalidateBoundingRect();
03299     
03300     // tell the world that something in the selection has changed 
03301     // so that selection bounds are updated
03302     GetApplication()->FindSelection()->Update(TRUE);
03303 
03304     // record new bounds action undo/redo
03305     if (RecordBoundsAction::DoRecord(this, &UndoActions, pDestinNode) == AC_FAIL)
03306     {
03307         FailAndExecute();
03308         End();
03309         return;
03310     }
03311 
03312 #endif
03313 
03314     End();
03315 }
03316 

Generated on Sat Nov 10 03:46:30 2007 for Camelot by  doxygen 1.4.4