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