00001 // $Id: opbreak.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 // Implementation file for operation to break paths at selected points 00099 00100 /* 00101 */ 00102 00103 #include "camtypes.h" 00104 //#include "mike.h" 00105 //#include "ops.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00106 #include "opbreak.h" 00107 //#include "document.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00108 //#include "simon.h" 00109 //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00110 #include "nodepath.h" 00111 #include "ndtxtpth.h" 00112 //#include "resource.h" // _R(IDS_OK) _R(IDS_OUTOFMEMORY) 00113 //#include "barsdlgs.h" 00114 //#include "jim.h" 00115 //#include "attrmgr.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00116 //#include "nodeattr.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00117 #include "objchge.h" 00118 #include "ndbldpth.h" 00119 #include "ophist.h" 00120 00121 DECLARE_SOURCE("$Revision: 1282 $"); 00122 CC_IMPLEMENT_DYNCREATE( OpBreakAtPoints, SelOperation ) 00123 00124 #define new CAM_DEBUG_NEW 00125 00126 /* things to remember 00127 00128 (2) The GetState function is exactly the same as DeletePoints::GetState so 00129 we might be able to merge the two. 00130 (4) Force DoStartSelOp to return FALSE to make sure we recover correctly. 00131 (5) something has to be done about ExtraInfo 00132 ie in RotatePathLeft() 00133 if (ExtraInfo != NULL) 00134 ExtraInfo->ShiftDownExtraInfo(StartSlot, NumSlots, SlotsToMove); 00135 Ask Will! 00136 (6) test both parts to rotatepathleft() by causing the tempbuff memory claim to fail 00137 00138 */ 00139 00140 /******************************************************************************************** 00141 00142 > OpBreakAtPoints::OpBreakPoints() 00143 00144 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 00145 Created: 30/8/94 00146 Purpose: OpBreakAtPoints constructor 00147 SeeAlso: - 00148 00149 ********************************************************************************************/ 00150 00151 OpBreakAtPoints::OpBreakAtPoints() 00152 { 00153 // Dummy constructor 00154 } 00155 00156 00157 /******************************************************************************************** 00158 00159 > BOOL OpBreakAtPoints::Init() 00160 00161 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 00162 Created: 26/7/94 00163 Inputs: - 00164 Outputs: - 00165 Returns: TRUE if the operation could be successfully initialised 00166 FALSE if no more memory could be allocated 00167 00168 Purpose: OpDeletePoints initialiser method 00169 Errors: ERROR will be called if there was insufficient memory to allocate the 00170 operation. 00171 SeeAlso: - 00172 00173 ********************************************************************************************/ 00174 00175 BOOL OpBreakAtPoints::Init() 00176 { 00177 return (RegisterOpDescriptor(0, // tool ID 00178 _R(IDS_BREAKATPOINTSOP), // string resource ID 00179 CC_RUNTIME_CLASS(OpBreakAtPoints), // runtime class for Op 00180 OPTOKEN_BREAKATPOINTS, // Ptr to token string 00181 OpBreakAtPoints::GetState, // GetState function 00182 0, // help ID 00183 _R(IDBBL_BREAKATPOINTSOP), // bubble help ID 00184 _R(IDD_BARCONTROLSTORE), // resource ID 00185 _R(IDC_BTN_BREAKATPOINTSOP), // control ID 00186 SYSTEMBAR_EDIT, // Group bar ID 00187 TRUE, // Receive messages 00188 FALSE, // Smart 00189 FALSE, // Clean 00190 0, // One open Instance ID 00191 0 00192 00193 )); 00194 00195 } 00196 00197 00198 00199 /******************************************************************************************** 00200 00201 > OpState OpBreakAtPoints::GetState(String_256*, OpDescriptor*) 00202 00203 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 00204 Created: 30/8/94 00205 Inputs: - 00206 Outputs: - 00207 Returns: The state of the OpBreakAtPoints 00208 Purpose: For finding the OpBreakAtPoints's state. 00209 Errors: - 00210 SeeAlso: - 00211 00212 ********************************************************************************************/ 00213 00214 OpState OpBreakAtPoints::GetState(String_256* UIDescription, OpDescriptor*) 00215 { 00216 00217 OpState OpSt; 00218 String_256 DisableReason; 00219 00220 OpSt.Greyed = FALSE; 00221 BOOL FoundSelected = FALSE; 00222 00223 // Go through the selection until we find a selected point 00224 00225 SelRange* Selected = GetApplication()->FindSelection(); 00226 Node* pNode = Selected->FindFirst(); 00227 00228 while (pNode) 00229 { 00230 if (IS_A(pNode,NodePath) || IS_A(pNode,NodeBlendPath)) 00231 { 00232 NodePath* pNodePath = (NodePath*)pNode; 00233 INT32 NumSplinters = pNodePath->InkPath.NumSplinters(); 00234 00235 if (NumSplinters > 0) 00236 { 00237 // We need to ask the effected nodes if they (and their parents) can handle this node being replaced 00238 ObjChangeFlags cFlags; 00239 00240 if (NumSplinters > 1) 00241 cFlags.MultiReplaceNode = TRUE; // Node will be replaced with more than one node. 00242 else 00243 cFlags.ReplaceNode = TRUE; // Node will be replaced with one node only. 00244 00245 String_32 optokenstring(OPTOKEN_BREAKATPOINTS); 00246 ObjChangeParamWithToken ObjChange(OBJCHANGE_STARTING,cFlags,pNodePath,NULL,&optokenstring); 00247 00248 // Will the node allow this op to happen? 00249 if (pNodePath->AllowOp(&ObjChange,FALSE)) 00250 { 00251 FoundSelected = TRUE; 00252 break; 00253 } 00254 } 00255 } 00256 pNode = Selected->FindNext(pNode); 00257 } 00258 00259 // The operation is disabled if there are no complex paths selected 00260 00261 if (!FoundSelected) 00262 { 00263 OpSt.Greyed = TRUE; 00264 DisableReason = String_256(_R(IDS_NEEDS_SELECTED_POINT)); 00265 *UIDescription = DisableReason; 00266 } 00267 00268 return(OpSt); 00269 } 00270 00271 00272 /******************************************************************************************** 00273 00274 > void OpBreakAtPoints::GetOpName(String_256* OpName) 00275 00276 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 00277 Created: 9/9/94 00278 Inputs: - 00279 Outputs: The undo string for the operation 00280 Returns: 00281 Purpose: The GetOpName fn is overridden so that we return back a description 00282 appropriate to the type of attribute that the operation applies. 00283 00284 Errors: - 00285 SeeAlso: - 00286 00287 ********************************************************************************************/ 00288 00289 void OpBreakAtPoints::GetOpName(String_256* OpName) 00290 { 00291 *OpName = String_256(_R(IDS_UNDO_BREAKATPOINTSOP)); 00292 } 00293 00294 00295 00296 00297 00298 00299 00300 /******************************************************************************************** 00301 00302 > void OpBreakAtPoints::Do(OpDescriptor*) 00303 00304 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 00305 Created: 30/8/94 00306 Inputs: pointer to an OpDescriptor (unused) 00307 Outputs: - 00308 Returns: - 00309 Purpose: This operation works on the selected objects in the current document. 00310 It will scan through all path objects in the selection and if any contain 00311 selected points, break the path at those points, producing a number of 00312 output paths. These paths will be connected on the next sibling to the 00313 original. All attributes will be distributed to the output siblings. 00314 Errors: failandexecute will be called if the operation fails in some way, most 00315 likely when no memory is available. 00316 00317 ********************************************************************************************/ 00318 00319 void OpBreakAtPoints::Do(OpDescriptor*) 00320 { 00321 00322 // Obtain the current selections 00323 SelRange* Selected = GetApplication()->FindSelection(); 00324 NodePath* pSplitNode; 00325 00326 // Now, because we're going to be doing mad things to the selection, we have to make a list 00327 // of all the selected nodes, so that adding nodes into the tree won't confuse us 00328 00329 List* NodeList = Selected->MakeListOfNodes(); 00330 NodeListItem* CurItem = (NodeListItem*)(NodeList->GetHead()); 00331 00332 if (!CurItem) 00333 goto FailAndDeleteList; 00334 00335 if (!DoStartSelOp(TRUE,TRUE)) 00336 goto FailAndDeleteList; 00337 00338 while (CurItem) 00339 { 00340 // get a pointer to the NodePath 00341 NodePath* pThisNode = (NodePath*)(CurItem->pNode); 00342 00343 // Only interested in NodePaths that have a sub selection, and that will allow the op to happen 00344 if ((IS_A(pThisNode,NodePath) || IS_A(pThisNode,NodeBlendPath)) && pThisNode->InkPath.IsSubSelection()) 00345 { 00346 // Find out how many nodes this op will reproduce 00347 INT32 NumSplinters = pThisNode->InkPath.NumSplinters(); 00348 BOOL DoThisNode = FALSE; 00349 00350 if (NumSplinters > 0) 00351 { 00352 // We need to ask the effected nodes if they (and their parents) can handle this node being replaced 00353 ObjChangeFlags cFlags; 00354 00355 if (NumSplinters > 1) 00356 cFlags.MultiReplaceNode = TRUE; // Node will be replaced with more than one node. 00357 else 00358 cFlags.ReplaceNode = TRUE; // Node will be replaced with one node only. 00359 00360 ObjChangeParam ObjChange(OBJCHANGE_STARTING,cFlags,NULL,this); 00361 DoThisNode = pThisNode->AllowOp(&ObjChange); 00362 } 00363 00364 if (DoThisNode) 00365 { 00366 00367 BOOL ok; 00368 Node* pnode; 00369 00370 // Copy the nodepath and all its children, without placing the copy in the tree 00371 CALL_WITH_FAIL(pThisNode->NodeCopy(&pnode), this, ok); 00372 if (!ok) goto DeleteList; 00373 pSplitNode = (NodePath*)pnode; 00374 00375 // remove the fill from this path as we're about to open it 00376 pSplitNode->InkPath.IsFilled = FALSE; 00377 00378 // Now stick the new path into the tree 00379 CALL_WITH_FAIL 00380 ( 00381 DoInsertNewNode(pSplitNode, pThisNode, NEXT, TRUE, FALSE), 00382 this,ok 00383 ); 00384 00385 if (!ok) 00386 goto DeleteListAndPath; 00387 00388 // Now breakup this copy of the path where necessary 00389 Path* pChildPath; 00390 INT32 split; 00391 00392 do 00393 { 00394 // Create a new path, ready for split 00395 ALLOC_WITH_FAIL(pChildPath, new Path, this); 00396 if (!pChildPath) 00397 goto DeleteList; 00398 00399 // Now split the path, possibly into two pieces. 00400 split = pSplitNode->InkPath.BreakInTwo(pChildPath); 00401 00402 if (split==-1) 00403 { 00404 InformError(_R(IDS_OUT_OF_MEMORY), _R(IDS_OK)); 00405 delete pChildPath; 00406 goto FailAndDeleteList; 00407 } 00408 00409 /* Karim 05/12/2000 00410 No longer required - see code addition at the bottom of this loop. 00411 00412 if (split==1) 00413 { 00414 delete pChildPath; 00415 continue; 00416 } 00417 00418 */ 00419 00420 if (split>1) 00421 { 00422 00423 // update the split paths bounding rectangle 00424 pSplitNode->InvalidateBoundingRect(); 00425 00426 // Create a new nodepath. 00427 NodePath* pChildNode; 00428 ALLOC_WITH_FAIL(pChildNode, new NodePath(), this); 00429 if (!pChildNode) 00430 { 00431 delete pChildPath; 00432 goto DeleteList; 00433 } 00434 00435 // make room for the new path in the node path. 00436 CALL_WITH_FAIL 00437 ( 00438 pChildNode->SetUpPath(pChildPath->GetNumCoords(),12), 00439 this,ok 00440 ); 00441 00442 if (!ok) 00443 { 00444 delete pChildNode; 00445 delete pChildPath; 00446 goto DeleteList; 00447 } 00448 00449 // now copy the path data in there. 00450 pChildNode->InkPath.CopyPathDataFrom(pChildPath); 00451 delete pChildPath; 00452 00453 // Clear the selection flag from the first element in both the split 00454 // and child paths. All others apart from the last will be unselected 00455 // by definition. Also select the last element in the child 00456 (pSplitNode->InkPath.GetFlagArray())[0].IsSelected = FALSE; 00457 (pChildNode->InkPath.GetFlagArray())[0].IsSelected = FALSE; 00458 (pChildNode->InkPath.GetFlagArray())[(pChildNode->InkPath.GetNumCoords()-1)].IsSelected = TRUE; 00459 pChildNode->InkPath.IsFilled = FALSE; 00460 00461 // now, copy all attributes from the parent split to the child split 00462 Node* pAttr = pSplitNode->FindFirstChild(); 00463 while (pAttr != NULL) 00464 { 00465 if (pAttr->IsKindOf(CC_RUNTIME_CLASS(NodeAttribute))) 00466 { 00467 Node* pAttrCopy; 00468 CALL_WITH_FAIL(pAttr->NodeCopy(&pAttrCopy), this,ok); 00469 if (!ok) 00470 { 00471 pChildNode->CascadeDelete(); 00472 delete pChildNode; 00473 goto DeleteList; 00474 } 00475 pAttrCopy->AttachNode(pChildNode, FIRSTCHILD); 00476 } 00477 pAttr = pAttr->FindNext(); 00478 } 00479 00480 for (INT32 loop = 0; loop < pChildNode->InkPath.GetNumCoords(); loop ++) 00481 { 00482 pChildNode->InkPath.GetVerbArray()[loop] = pChildNode->InkPath.GetVerbArray()[loop] & ~PT_CLOSEFIGURE; 00483 } 00484 00485 // Now stick the new path into the tree 00486 CALL_WITH_FAIL 00487 ( 00488 DoInsertNewNode(pChildNode, pSplitNode, NEXT, TRUE, FALSE), 00489 this,ok 00490 ); 00491 00492 if (!ok) 00493 { 00494 pChildNode->CascadeDelete(); 00495 delete pChildNode; 00496 goto DeleteList; 00497 } 00498 00499 pSplitNode = pChildNode; 00500 } 00501 00502 // Karim 05/12/2000 00503 // Fix for memory leak. 00504 else 00505 { 00506 delete pChildPath; 00507 } 00508 } 00509 while (split); 00510 00511 // Clear out any remaining closefigures on the last bit of the path 00512 for (INT32 loop = 0; loop < pSplitNode->InkPath.GetNumCoords(); loop ++) 00513 { 00514 pSplitNode->InkPath.GetVerbArray()[loop] = pSplitNode->InkPath.GetVerbArray()[loop] & ~PT_CLOSEFIGURE; 00515 } 00516 00517 // Now we've broken up this path, let's hide it 00518 CALL_WITH_FAIL(DoHideNode(pThisNode,TRUE), this, ok) 00519 if (!ok) goto DeleteList; 00520 } 00521 } 00522 CurItem = (NodeListItem*)(NodeList->GetNext(CurItem)); 00523 } 00524 00525 // Update all the parents of this node 00526 { 00527 ObjChangeFlags cFlags; 00528 ObjChangeParam ObjChange(OBJCHANGE_FINISHED,cFlags,NULL,this); 00529 if (!UpdateChangedNodes(&ObjChange)) 00530 goto FailAndDeleteList; 00531 } 00532 00533 End(); 00534 00535 // delete the nodelist (and all the list items) 00536 while (!NodeList->IsEmpty()) 00537 delete (NodeListItem*)(NodeList->RemoveHead()); 00538 delete NodeList; 00539 return; 00540 00541 00542 FailAndDeleteList: 00543 while (!NodeList->IsEmpty()) 00544 delete (NodeListItem*)(NodeList->RemoveHead()); 00545 delete NodeList; 00546 FailAndExecute(); 00547 End(); 00548 return; 00549 00550 DeleteListAndPath: 00551 pSplitNode->CascadeDelete(); 00552 delete pSplitNode; 00553 00554 DeleteList: 00555 while (!NodeList->IsEmpty()) 00556 delete (NodeListItem*)(NodeList->RemoveHead()); 00557 delete NodeList; 00558 End(); 00559 return; 00560 00561 } 00562 00563