blndtool.cpp

Go to the documentation of this file.
00001 // $Id: blndtool.cpp 1401 2006-07-03 11:17:52Z 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 of the blend tool
00099 
00100 /*
00101 */
00102 
00103 #include "camtypes.h"
00104 //#include "oilfiles.h"
00105 //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00106 //#include "selop.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00107 #include "csrstack.h"
00108 //#include "markn.h"
00109 //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00110 #include "nodepath.h"
00111 #include "progress.h"
00112 #include "nodeblnd.h"
00113 #include "nodebldr.h"
00114 //#include "simon.h"
00115 #include "blobs.h"
00116 //#include "blndres.h"
00117 //#include "bevres.h"
00118 #include "objchge.h"
00119 //#include "resource.h"
00120 //#include "will.h"
00121 #include "filltool.h"
00122 #include "bubbleid.h"
00123 //#include "becomea.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00124 #include "attrmap.h"
00125 #include "ndbldpth.h"
00126 #include "pathedit.h"
00127 #include "keypress.h"
00128 #include "vkextra.h"
00129 
00130 #include "blndtool.h"
00131 //#include "ezmodule.h"
00132 #include "opbevel.h"
00133 
00134 //#include "will2.h"
00135 #include "biasgdgt.h"
00136 #include "opcntr.h"
00137 //#include "cntrtool.h"
00138 #include "shapeops.h"
00139 #include "biasdlg.h"
00140 
00141 //#include "will.h"
00142 
00143 
00144 // for bevels & shadows
00145 #include "nbevcont.h"
00146 #include "nodecont.h"
00147 
00148 #include "nodecntr.h"
00149 #include "ncntrcnt.h"
00150 #include "opbevel.h"
00151 #include "layer.h"
00152 #include "ophist.h"
00153 
00154 DECLARE_SOURCE( "$Revision: 1401 $" );
00155                                                 
00156 CC_IMPLEMENT_MEMDUMP(BlendTool,Tool_v1)
00157 CC_IMPLEMENT_DYNCREATE(BlendInfoBarOp,InformationBarOp)
00158 CC_IMPLEMENT_MEMDUMP(BlendToolRef,CC_CLASS_MEMDUMP)
00159 CC_IMPLEMENT_DYNCREATE(OpBlendNodes,SelOperation)
00160 CC_IMPLEMENT_DYNCREATE(OpRemoveBlend,SelOperation)
00161 CC_IMPLEMENT_DYNCREATE(OpAddBlendPath,SelOperation)
00162 CC_IMPLEMENT_DYNCREATE(OpDetachBlendPath,SelOperation)
00163 CC_IMPLEMENT_DYNCREATE(OpChangeBlend,SelOperation)
00164 CC_IMPLEMENT_DYNCREATE(OpBlendOneToOne,OpChangeBlend)
00165 CC_IMPLEMENT_DYNCREATE(OpBlendAntialias,OpChangeBlend)
00166 CC_IMPLEMENT_DYNCREATE(OpBlendTangential,OpChangeBlend)
00167 CC_IMPLEMENT_DYNCREATE(OpChangeBlendSteps,SelOperation)
00168 CC_IMPLEMENT_DYNCREATE(ChangeBlendAction,Action)
00169 CC_IMPLEMENT_DYNCREATE(ChangeBlenderAction,Action)
00170 CC_IMPLEMENT_DYNCREATE(ChangeBlendStepsAction,Action)
00171 CC_IMPLEMENT_DYNCREATE(RemapBlendAction,Action)
00172 CC_IMPLEMENT_DYNCREATE(InitBlendersAction,Action)
00173 CC_IMPLEMENT_MEMDUMP(ChangeBlendOpParam,OpParam)
00174 CC_IMPLEMENT_MEMDUMP(ChangeBlenderOpParam,OpParam)
00175 CC_IMPLEMENT_DYNCREATE(InvalidateBoundsAction,Action)
00176 CC_IMPLEMENT_DYNCREATE(OpChangeBlendDistance, SelOperation)
00177 CC_IMPLEMENT_DYNCREATE(OpEditBlendEndObject, SelOperation)
00178 CC_IMPLEMENT_MEMDUMP(BlenderInfoItem,ListItem);
00179 
00180 
00181 
00182 // Must come after the last CC_IMPLEMENT.. macro
00183 #define new CAM_DEBUG_NEW     
00184 
00185 // These are still char* while we wait for resource technology to be developed for modules
00186 TCHAR* BlendTool::FamilyName    = _T("Blend Tools");
00187 TCHAR* BlendTool::ToolName      = _T("Blend Tool");
00188 TCHAR* BlendTool::Purpose       = _T("Blend manipulation");
00189 TCHAR* BlendTool::Author        = _T("Mark Neves");
00190 
00191 // Init those other useful static vars
00192 BlendInfoBarOp* BlendTool::pBlendInfoBarOp          = NULL;
00193 BlendToolRef*   BlendTool::pRefStart                = NULL;
00194 BlendToolRef*   BlendTool::pRefEnd                  = NULL;
00195 Cursor*         BlendTool::pcNormalCursor           = NULL;
00196 Cursor*         BlendTool::pcBlendableCursor        = NULL;
00197 Cursor*         BlendTool::pcBlendableBlobCursor    = NULL;
00198 Cursor*         BlendTool::pcBlendableRemapCursor   = NULL;
00199 Cursor*         BlendTool::pcCurrentCursor          = NULL;
00200 INT32           BlendTool::CurrentCursorID          = 0;
00201 UINT32          BlendTool::StatusID                 = _R(IDS_BLENDSTATUS_FINDSTART);
00202 
00203 //#define Swap(a,b)       { (a)^=(b), (b)^=(a), (a)^=(b); }
00204 
00205 #define SWAP(type,a,b) { type x=a; a=b; b=x; }
00206 
00207 /********************************************************************************************
00208 
00209 >   BlendTool::BlendTool()
00210 
00211     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00212     Created:    3/10/94
00213     Purpose:    Default Constructor.
00214                 Other initialisation is done in BlendTool::Init which is called by the Tool Manager
00215     SeeAlso:    BlendTool::Init
00216 
00217 ********************************************************************************************/
00218 
00219 BlendTool::BlendTool()
00220 {
00221     pcCurrentCursor = NULL;
00222 }
00223 
00224 /********************************************************************************************
00225 
00226 >   BlendTool::~BlendTool()
00227 
00228     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00229     Created:    3/10/94
00230     Purpose:    Destructor.
00231 
00232 ********************************************************************************************/
00233 
00234 BlendTool::~BlendTool()
00235 {
00236     if (pRefStart != NULL)
00237     {
00238         delete pRefStart;
00239         pRefStart = NULL;
00240     }
00241 
00242     if (pRefEnd != NULL)
00243     {
00244         delete pRefEnd;
00245         pRefEnd = NULL;
00246     }
00247 }
00248 
00249 
00250 /********************************************************************************************
00251 
00252 >   BOOL BlendTool::Init( INT32 Pass )
00253 
00254     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00255     Created:    3/10/94
00256     Returns:    FALSE if it does not want to be created, TRUE otherwise
00257     Purpose:    Used to check if the Tool was properly constructed
00258     SeeAlso:    BlendTool::BlendTool
00259 
00260 ********************************************************************************************/
00261 
00262 BOOL BlendTool::Init()
00263 {
00264     // Declare all your ops here and only succeed if all declarations succeed
00265 
00266     BOOL ok = ( OpBlendNodes::Declare()         && 
00267                 OpRemoveBlend::Declare()        && 
00268                 OpAddBlendPath::Declare()       && 
00269                 OpDetachBlendPath::Declare()    && 
00270                 OpChangeBlend::Declare()        && 
00271                 OpBlendOneToOne::Declare()      &&
00272                 OpBlendAntialias::Declare()     &&
00273                 OpChangeBlendSteps::Declare());
00274 
00275     if (!ok) return FALSE;
00276 
00277     // We need two BlendToolRef objects
00278     BlendTool::pRefStart = new BlendToolRef;
00279     BlendTool::pRefEnd   = new BlendToolRef;
00280 
00281     ok = (BlendTool::pRefStart != NULL && BlendTool::pRefEnd != NULL);
00282 
00283     // This section reads in the infobar definition and creates an instance of
00284     // BlendInfoBarOp.  Also pBlendInfoBarOp, the ptr to the tool's infobar, is set up
00285     // after the infobar is successfully read and created.
00286     if (ok)
00287     {
00288         pBlendInfoBarOp = new BlendInfoBarOp();
00289         ok = (pBlendInfoBarOp != NULL);
00290 #if 0
00291         CCResTextFile       file;               // Resource File
00292         BlendInfoBarOpCreate BarCreate;         // Object that creates BlendInfoBarOp objects
00293 
00294                 ok = file.open(_R(IDM_BLEND_BAR), _R(IDT_INFO_BAR_RES));        // Open resource
00295         if (ok) ok = DialogBarOp::ReadBarsFromFile(file,BarCreate); // Read and create info bar
00296         if (ok) file.close();                                       // Close resource
00297 
00298         ENSURE(ok,"Unable to load blendbar.ini from resource\n"); 
00299 
00300         if (ok)
00301         {
00302             // Info bar now exists.  Now get a pointer to it
00303             String_32 str = String_32(_R(IDS_BLNDTOOL_INFOBARNAME));
00304             DialogBarOp* pDialogBarOp = DialogBarOp::FindDialogBarOp(str);
00305 
00306                     ok = (pDialogBarOp != NULL);
00307             if (ok) ok = pDialogBarOp->IsKindOf(CC_RUNTIME_CLASS(BlendInfoBarOp));
00308             if (ok) pBlendInfoBarOp = (BlendInfoBarOp*)pDialogBarOp;
00309 
00310             ENSURE(ok,"Error finding the blend tool info bar");
00311         }
00312 #endif
00313     }
00314 
00315     return (ok);
00316 }
00317 
00318 
00319 /********************************************************************************************
00320 
00321 >   void BlendTool::Describe(void *InfoPtr)
00322 
00323     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00324     Created:    3/10/94
00325     Inputs:     InfoPtr -   A pointer to a tool info block. It is passed cast to void* as
00326                             the version of the tool is unknown at this point. Later versions 
00327                             of the Tool class may have more items in this block, that this 
00328                             tool will not use
00329     Outputs:    InfoPtr -   The structure pointed to by InfoPtr will have had all the info
00330                             that this version of the Tool knows about
00331     Purpose:    Allows the tool manager to extract information about the tool
00332 
00333 ********************************************************************************************/
00334 
00335 void BlendTool::Describe(void *InfoPtr)
00336 {
00337     // Cast structure into the latest one we understand.
00338     ToolInfo_v1 *Info = (ToolInfo_v1 *) InfoPtr;
00339 
00340     Info->InfoVersion = 1;
00341     
00342     Info->InterfaceVersion = GetToolInterfaceVersion();  // You should always have this line.
00343         
00344     // These are all arbitrary at present.
00345     Info->Version = 1;
00346     Info->ID      = GetID();
00347     Info->TextID  = _R(IDS_BLEND_TOOL);
00348 
00349     Info->Family  = FamilyName;
00350     Info->Name    = ToolName;
00351     Info->Purpose = Purpose;
00352     Info->Author  = Author;
00353 
00354     Info->BubbleID = _R(IDBBL_BLEND_TOOLBOX);
00355 }
00356 
00357 /********************************************************************************************
00358 
00359 >   virtual void BlendTool::SelectChange(BOOL isSelected)
00360 
00361     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00362     Created:    3/10/94
00363     Inputs:     isSelected  - TRUE  = tool has been selected
00364                             - FALSE = tool has been deselected
00365     Outputs:    -
00366     Returns:    -
00367     Purpose:    Starts up and closes down the blend tool
00368     Errors:     Debug warning if creating the cursor fails.
00369     SeeAlso:    -
00370 
00371 ********************************************************************************************/
00372 
00373 void BlendTool::SelectChange(BOOL isSelected)
00374 {
00375     if (isSelected)
00376     {
00377         if (!CreateCursors()) return;
00378         CurrentCursorID = CursorStack::GPush(pcNormalCursor, FALSE);        // Push cursor but don't display now
00379         pcCurrentCursor = pcNormalCursor;
00380 
00381         // Create and display the tool's info bar
00382         pBlendInfoBarOp->Create();
00383         m_EditEndObject = FALSE;
00384         // Which blobs do I want displayed
00385         BlobManager* BlobMgr = GetApplication()->GetBlobManager();
00386         if (BlobMgr != NULL)
00387         {
00388             // Decide which blobs we will display
00389             BlobStyle MyBlobs;
00390             
00391             MyBlobs.Object = TRUE;
00392             MyBlobs.Tiny = FALSE;
00393 
00394             BlobMgr->ToolInterest(MyBlobs);
00395         }
00396 
00397         Document* pDoc = Document::GetCurrent();
00398         if (pDoc != NULL)
00399             BlobMgr->RenderToolBlobsOn(this, pDoc->GetSelectedSpread(),NULL);
00400     }
00401     else
00402     {
00403         // Deselection - destroy the tool's cursors, if they exist.
00404         if (pcCurrentCursor != NULL)
00405         {
00406             CursorStack::GPop(CurrentCursorID);
00407             pcCurrentCursor = NULL;
00408             CurrentCursorID = 0;
00409         }
00410         DestroyCursors();
00411 
00412         // we need to close down any profile dialogs that are currently open ....
00413         pBlendInfoBarOp->CloseProfileDialog (pBlendInfoBarOp->m_BiasGainGadgetPosition);
00414         pBlendInfoBarOp->CloseProfileDialog (pBlendInfoBarOp->m_BiasGainGadgetAttribute);
00415         
00416         // BEFORE we do the next call !!!! Cause otherwise pBlendInfoBarOp will
00417         // have been "deleted", and the above will access violate!
00418 
00419         // ensure any tool object blobs are removed.
00420         BlobManager* BlobMgr = GetApplication()->GetBlobManager();
00421         if (BlobMgr != NULL)
00422         {
00423             BlobStyle bsRemoves;
00424             bsRemoves.ToolObject = TRUE;
00425             BlobMgr->RemoveInterest(bsRemoves);
00426         }
00427 
00428         // Remove the info bar from view by deleting the actual underlying window
00429         pBlendInfoBarOp->Delete();
00430 
00431         Document* pDoc = Document::GetCurrent();
00432         if (pDoc != NULL)
00433             BlobMgr->RenderToolBlobsOff(this, pDoc->GetSelectedSpread(),NULL);
00434     }
00435 }
00436 
00437 /********************************************************************************************
00438 
00439 >   BOOL BlendTool::CreateCursors()
00440 
00441     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00442     Created:    3/10/94
00443     Inputs:     -
00444     Outputs:    -
00445     Returns:    TRUE if all the blend tool cursors have been successfully created
00446     Purpose:    Creates all the blend tool cursors
00447     SeeAlso:    -
00448 
00449 ********************************************************************************************/
00450 
00451 BOOL BlendTool::CreateCursors()
00452 {
00453     // This tool has just been selected.  Create the cursors.
00454     pcNormalCursor          = new Cursor(this, _R(IDC_BLENDNORMALCURSOR));
00455     pcBlendableCursor       = new Cursor(this, _R(IDC_BLENDABLECURSOR));
00456     pcBlendableBlobCursor   = new Cursor(this, _R(IDC_BLENDABLEBLOBCURSOR));
00457     pcBlendableRemapCursor  = new Cursor(this, _R(IDC_BLENDABLEREMAPCURSOR));
00458 
00459     if ( pcNormalCursor         ==NULL || !pcNormalCursor->IsValid()            ||
00460          pcBlendableCursor      ==NULL || !pcBlendableCursor->IsValid()         ||
00461          pcBlendableBlobCursor  ==NULL || !pcBlendableBlobCursor->IsValid()     ||
00462          pcBlendableRemapCursor ==NULL || !pcBlendableRemapCursor->IsValid()
00463        )
00464     {
00465         DestroyCursors();
00466         return FALSE;
00467     }
00468     else
00469         return TRUE;
00470 }
00471 
00472 /********************************************************************************************
00473 
00474 >   void BlendTool::DestroyCursors()
00475 
00476     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00477     Created:    3/10/94
00478     Inputs:     -
00479     Outputs:    -
00480     Returns:    -
00481     Purpose:    Destroys all the blend tool cursors
00482     SeeAlso:    -
00483 
00484 ********************************************************************************************/
00485 
00486 void BlendTool::DestroyCursors()
00487 {
00488     if (pcNormalCursor          != NULL) delete pcNormalCursor;
00489     if (pcBlendableCursor       != NULL) delete pcBlendableCursor;
00490     if (pcBlendableBlobCursor   != NULL) delete pcBlendableBlobCursor;
00491     if (pcBlendableRemapCursor  != NULL) delete pcBlendableRemapCursor;
00492 }
00493 
00494 /********************************************************************************************
00495 
00496 >   BOOL BlendTool::OnKeyPress(KeyPress* pKeyPress)
00497 
00498     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00499     Created:    1/6/99
00500     Inputs:     pKeyPress - pointer to a keypress object
00501     Returns:    TRUE if it handled the keypress, FALSE otherwise
00502     Purpose:    To handle keypress events for the Blend Tool.
00503 
00504 ********************************************************************************************/
00505 
00506 BOOL BlendTool::OnKeyPress(KeyPress* pKeyPress)
00507 {
00508 #if defined(_DEBUG) && 0
00509     if (pKeyPress == NULL)
00510         return FALSE;
00511 
00512     if (pKeyPress->IsRepeat())
00513         return FALSE;
00514 
00515     if (pKeyPress->IsRelease())
00516         return FALSE;
00517 
00518     AFp BiasDelta = 0.0;
00519     AFp GainDelta = 0.0;
00520     BOOL Reset = FALSE;
00521     if (*pKeyPress == KeyPress(CAMKEY(Z)))  { BiasDelta = -0.1; TRACEUSER( "Markn", _T("Decrease Bias by 0.1\n"));}
00522     if (*pKeyPress == KeyPress(CAMKEY(X)))  { BiasDelta =  0.1; TRACEUSER( "Markn", _T("Increase Bias by 0.1\n"));}
00523     if (*pKeyPress == KeyPress(CAMKEY(N)))  { GainDelta = -0.1; TRACEUSER( "Markn", _T("Decrease Gain by 0.1\n"));}
00524     if (*pKeyPress == KeyPress(CAMKEY(M)))  { GainDelta =  0.1; TRACEUSER( "Markn", _T("Increase Gain by 0.1\n"));}
00525 
00526     if (*pKeyPress == KeyPress(CAMKEY(R)))  { Reset = TRUE;     TRACEUSER( "Markn", _T("Resetting Bias and Gain\n"));}
00527 
00528     SelRange* pSelRange = GetApplication()->FindSelection();
00529     Node* pNode = pSelRange->FindFirst();
00530     while (pNode)
00531     {
00532         if (IS_A(pNode,NodeBlend))
00533         {
00534             NodeBlend* pNodeBlend = (NodeBlend*)pNode;
00535 
00536             // This alters the Attribute profile, but can easily be modified to alter the Object profile if necessary
00537             CProfileBiasGain* pProfile = pNodeBlend->GetAttrProfile();
00538             if (pProfile)
00539             {
00540                 AFp Bias = pProfile->GetBias() + BiasDelta;
00541                 AFp Gain = pProfile->GetGain() + GainDelta;
00542                 if (Reset)
00543                     Bias = Gain = 0.0;
00544 
00545                 if (Bias < -0.9)    Bias = -0.9;
00546                 if (Bias >  0.9)    Bias =  0.9;
00547                 if (Gain < -0.9)    Gain = -0.9;
00548                 if (Gain >  0.9)    Gain =  0.9;
00549 
00550                 pProfile->SetBiasGain(Bias,Gain);
00551             }
00552         }
00553         // Now find the next selected node
00554         pNode = pSelRange->FindNext(pNode);
00555     }
00556 #endif // _DEBUG
00557 
00558     return FALSE;
00559 }
00560 
00561 /********************************************************************************************
00562 
00563 >   void BlendTool::OnClick( DocCoord PointerPos, ClickType Click, ClickModifiers ClickMods,
00564                         Spread* pSpread )
00565 
00566     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00567     Created:    3/10/94
00568     Inputs:     PointerPos  -   The DocCoord of the point where the mouse button was clicked
00569                 Click       -   Describes the type of click that was detected. 
00570                 ClickMods   -   Indicates which buttons caused the click and which modifers were
00571                                 pressed at the same time
00572                 pSpread     -   The spread in which the click happened
00573     Returns:    -
00574     Purpose:    To handle a Mouse Click event for the Blend Tool.  If the click is over the 
00575                 central blob of a blend on a path then start a EditEnd operation, otherwise
00576                 start a createblend operation.
00577     SeeAlso:    Tool::MouseClick; ClickType; ClickModifiers
00578 
00579 ********************************************************************************************/
00580 
00581 void BlendTool::OnClick( DocCoord PointerPos, ClickType Click, ClickModifiers ClickMods,
00582                         Spread* pSpread )
00583 {
00584     if (ClickMods.Menu) return;                         // Don't do anything if the user clicked the Menu button
00585 
00586     ERROR3IF_PF(pSpread==NULL,("pSpread is NULL"));
00587 
00588     if (Click == CLICKTYPE_DRAG)
00589     {
00590         // are we over the middle blob of a blend on a curve?
00591         BOOL MoveEndObject = EditBlendEnd(pSpread, PointerPos);
00592 
00593         // Diccon  9/99, do we wish to create a blend or move an object?
00594         if (!MoveEndObject)  // create a blend
00595         {
00596             UpdateRef(pRefStart,pSpread,PointerPos);
00597             UpdateRef(pRefEnd  ,pSpread,PointerPos);
00598             CheckNodeRemapping(pRefStart,pRefEnd);
00599             UpdateCursorAndStatus();
00600 
00601             if (pRefStart->pNode != NULL)
00602             {
00603                 // Start a drag
00604                 OpBlendNodes* pOpBlendNodes = new OpBlendNodes;
00605                 if (pOpBlendNodes != NULL)
00606                 {
00607                     // Start the drag operation and pass in the Anchor Point to the push operation
00608                     if  (!pOpBlendNodes->DoDrag(this))
00609                         delete pOpBlendNodes;
00610                 }
00611             }
00612         }
00613         else  // drag end object
00614         {   
00615             OpEditBlendEndObject* pEditEnd = new OpEditBlendEndObject(this);
00616 
00617             if (pEditEnd != NULL)
00618             {
00619                 StatusID = _R(IDS_BLENDSTATUS_MOVEEND);
00620                 DisplayStatusBarHelp(StatusID);
00621                 pEditEnd->DoDrag(PointerPos, pSpread);
00622             }
00623         }
00624     }
00625     
00626     // call the base class ....
00627     
00628     DragTool::OnClick (PointerPos, Click, ClickMods, pSpread);
00629 }
00630 
00631 /********************************************************************************************
00632 
00633 >   void BlendTool::OnMouseMove( DocCoord PointerPos,Spread* pSpread, ClickModifiers ClickMods)
00634 
00635     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00636     Created:    3/10/94
00637     Inputs:     PointerPos  -   The DocCoord of the point where the mouse has moved to
00638                 pSpread     -   The spread in which the move occurred
00639     Returns:    TRUE if it handled the Click, FALSE otherwise
00640     Purpose:    To handle a Mouse Move event for the Blend Tool.
00641     SeeAlso:    Tool::MouseClick; ClickType; ClickModifiers
00642 
00643 ********************************************************************************************/
00644 
00645 void BlendTool::OnMouseMove(DocCoord PointerPos,Spread* pSpread,ClickModifiers ClickMods)
00646 {
00647     ERROR3IF_PF(pSpread==NULL,("pSpread is NULL"));
00648 
00649     pRefEnd->Reset();
00650     UpdateRef(pRefStart,pSpread,PointerPos,FALSE);
00651     // Diccon added - check first to see if there is a hit on an edit end blob
00652     m_EditEndObject = EditBlendEndAndUpdateCursor(pSpread, PointerPos);
00653     // if not then check for new blend/remapping etc.
00654     if (m_EditEndObject == FALSE)
00655         UpdateCursorAndStatus();
00656 }
00657 
00658 
00659 /********************************************************************************************
00660 
00661 >   BOOL BlendTool::EditBlendEndAndUpdateCursor(Spread* pSpread, DocCoord PointerPos)
00662 
00663     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
00664     Created:    10/9/99
00665     Inputs:     PointerPos  -   The DocCoord of the point where the mouse has moved to
00666                 pSpread     -   The spread in which the move occurred
00667     Returns:    TRUE if we can edit and end object, FALSE otherwise
00668     Purpose:    To determine whether or not the pointer is the central blob of an end object
00669                 of a blend on a curve. If so then we change the cursor and set a flag that 
00670                 allows us to begin an OpEditBlendEndobject if the user clicks and drags.
00671     SeeAlso:    OnMouseMove, OnClick
00672 
00673 ********************************************************************************************/
00674 
00675 BOOL BlendTool::EditBlendEndAndUpdateCursor(Spread* pSpread, DocCoord PointerPos)
00676 {
00677     Cursor* pcNewCursor = pcNormalCursor;
00678 
00679     List BlendList;
00680     // make a list of selected nodes
00681     BOOL ok = BevelTools::BuildListOfSelectedNodes(&BlendList, CC_RUNTIME_CLASS(NodeBlend), FALSE);
00682     if (ok)
00683     {
00684         ok = FALSE;
00685         NodeListItem* pListItem = (NodeListItem*)BlendList.GetHead();
00686         while (pListItem != NULL)
00687         {
00688             
00689             NodeBlend* pNodeBlend = (NodeBlend*)pListItem->pNode;
00690             Node* pNode = NULL;
00691             // check to see if there is a hit
00692             ok = pNodeBlend->HitOnEndDragBlob(PointerPos, &pNode);
00693             if (ok)
00694             {
00695                 pcNewCursor = pcBlendableBlobCursor;
00696                 StatusID = _R(IDS_BLENDSTATUS_EDITENDS);                        
00697                 break;
00698             }
00699             pListItem = (NodeListItem*)BlendList.GetNext(pListItem);
00700         }
00701     }
00702     BlendList.DeleteAll();
00703 
00704     if (pcCurrentCursor != pcNewCursor)
00705     {
00706         // Current cursor has changed
00707         CursorStack::GSetTop(pcNewCursor, CurrentCursorID);
00708         pcCurrentCursor = pcNewCursor;
00709     }
00710     
00711     return ok;
00712 }
00713 
00714 
00715 /********************************************************************************************
00716 
00717 >   BOOL BlendTool::EditBlendEnd(Spread* pSpread, DocCoord PointerPos)
00718 
00719     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
00720     Created:    10/9/99
00721     Inputs:     PointerPos  -   The DocCoord of the point where the mouse has moved to
00722                 pSpread     -   The spread in which the move occurred
00723     Returns:    TRUE if we can edit and end object, FALSE otherwise
00724     Purpose:    To determine whether or not the pointer is the central blob of an end object
00725                 of a blend on a curve. If so then we change the cursor and set a flag that 
00726                 allows us to begin an OpEditBlendEndobject if the user clicks and drags.
00727     SeeAlso:    OnMouseMove, OnClick
00728 
00729 ********************************************************************************************/
00730 
00731 BOOL BlendTool::EditBlendEnd(Spread* pSpread, DocCoord PointerPos)
00732 {
00733     List BlendList;
00734     // make a list of selected nodes
00735     BOOL ok = BevelTools::BuildListOfSelectedNodes(&BlendList, CC_RUNTIME_CLASS(NodeBlend), FALSE);
00736     if (ok)
00737     {
00738         ok = FALSE;
00739         NodeListItem* pListItem = (NodeListItem*)BlendList.GetHead();
00740         while (pListItem != NULL)
00741         {
00742             
00743             NodeBlend* pNodeBlend = (NodeBlend*)pListItem->pNode;
00744             Node* pNode = NULL;
00745             // check to see if there is a hit
00746             ok = pNodeBlend->HitOnEndDragBlob(PointerPos, &pNode);
00747             if (ok)
00748                 break;
00749             pListItem = (NodeListItem*)BlendList.GetNext(pListItem);
00750         }
00751     }
00752     BlendList.DeleteAll();
00753 
00754     return ok;
00755 }
00756 
00757 
00758 
00759 /********************************************************************************************
00760 >   void BlendTool::UpdateRef(  BlendToolRef* pRef,
00761                                 Spread* pSpread, 
00762                                 DocCoord PointerPos,
00763                                 BOOL CheckNodeUnderPoint = TRUE)
00764 
00765     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00766     Created:    10/10/94
00767     Inputs:     pRef                = ptr to a reference struct to update
00768                 pSpread             = ptr to spread click occurred
00769                 PointerPos          = the DocCoord of the click
00770                 CheckNodeUnderPoint = TRUE to revert to click-detection search for underlying node
00771     Outputs:    The members of pRef are updated.
00772     Returns:    -
00773     Purpose:    This will update the blend tool ref depending on the current pointer pos.
00774                 If CheckNodeUnderPoint is TRUE, then the routine will revert to a click-detection search
00775                 to find out which node lies under the given point.  This is potentially very time-consuming.
00776     Errors:     -
00777     SeeAlso:    -
00778 ********************************************************************************************/
00779 
00780 void BlendTool::UpdateRef(BlendToolRef* pRef,Spread* pSpread, DocCoord PointerPos,BOOL CheckNodeUnderPoint)
00781 {
00782     ERROR3IF_PF(pRef   ==NULL,("pRef is NULL"));
00783     ERROR3IF_PF(pSpread==NULL,("pSpread is NULL"));
00784 
00785     // Reset the reference
00786     pRef->Reset();
00787 
00788     // See if point is over a blob of a selected path
00789     // if IsPointOverPathBlob() fails, it inits pNode to NULL and Index to -1
00790     if (!IsPointOverPathBlob(&PointerPos,pRef))
00791     {
00792         // Reset the reference in case above call altered it
00793         pRef->Reset();
00794 
00795         // See if point is over a blob is a selected blend
00796         // if IsPointOverBlendBlob() fails, it inits pNode & pNodeBlend to NULL and Index to -1
00797         if (!IsPointOverBlendBlob(&PointerPos,pRef))
00798         {
00799             // Reset the reference in case above call altered it
00800             pRef->Reset();
00801 
00802             if (CheckNodeUnderPoint)
00803             {
00804                 // See if we are over an object
00805                 NodeRenderableInk* pNodeUnderPoint = FindObject(pSpread,PointerPos);
00806 
00807                 if (pNodeUnderPoint != NULL)
00808                 {
00809                     BecomeA TestBecomeA(BECOMEA_TEST, CC_RUNTIME_CLASS(NodePath));
00810                     // We are over an object. Can we blend it?
00811                     if (pNodeUnderPoint->CanBecomeA(&TestBecomeA))
00812                     {
00813                         // We are over a NodePath or a node that can become a NodePath
00814                         pRef->pNode = pNodeUnderPoint;
00815                     }
00816                 }
00817             }
00818         }
00819     }
00820 
00821     // Set the spread and pointer pos members
00822     pRef->pSpread    = pSpread;
00823     pRef->PointerPos = PointerPos;
00824 }
00825 
00826 /********************************************************************************************
00827 >   void BlendTool::UpdateCursorAndStatus()
00828 
00829     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00830     Created:    10/10/94
00831     Inputs:     -
00832     Outputs:    -
00833     Returns:    -
00834     Purpose:    This will update the cursor and status line text depending on the data in the 
00835                 two blend tool references within BlendTool.
00836 
00837                 Copes with the following scenarios:
00838                     Pointer over an object
00839                     Dragging to another object
00840                     Pointer over a selected path's blob
00841                     Dragging to another blob in a selected path
00842                     Pointer over a blend blob
00843                     Dragging to a corresponding blend blob for remapping
00844 
00845     Errors:     -
00846     SeeAlso:    -
00847 ********************************************************************************************/
00848 
00849 void BlendTool::UpdateCursorAndStatus()
00850 {
00851     ERROR3IF_PF(pRefStart==NULL,("pRefStart is NULL"));
00852     ERROR3IF_PF(pRefEnd  ==NULL,("pRefEnd   is NULL"));
00853 
00854     // Default to standard pointer and status line text
00855     Cursor* pcNewCursor = pcNormalCursor;
00856     StatusID = _R(IDS_BLENDSTATUS_FINDSTART);   // StatusID is a member var
00857 
00858     if (pRefStart->RemapRef > 0)
00859     {
00860         // The pointer is either over a blend blob, or user started a drag over a blend blob
00861         pcNewCursor = pcBlendableBlobCursor;
00862         StatusID = _R(IDS_BLENDSTATUS_REMAPSTART);
00863 
00864         if (pRefStart->RemapRef == pRefEnd->RemapRef && pRefStart->AStartNode != pRefEnd->AStartNode)
00865         {
00866             // Dragged to a corresponding blob in the other blend
00867             StatusID = _R(IDS_BLENDSTATUS_REMAPEND);
00868             pcNewCursor = pcBlendableRemapCursor;
00869         }
00870     }
00871     else if (pRefStart->pNode != NULL)
00872     {
00873         if (pRefEnd->pSpread == NULL)
00874         {
00875             if (pRefStart->Index >= 0)
00876             {
00877                 // We are over a blob of a selected path
00878                 pcNewCursor = pcBlendableBlobCursor;
00879                 StatusID    = _R(IDS_BLENDSTATUS_OVERBLOB);
00880             }
00881             else
00882             {
00883                 // We are over a blendable node
00884                 pcNewCursor = pcBlendableCursor;
00885                 StatusID    = _R(IDS_BLENDSTATUS_FINDEND);
00886             }
00887         }
00888         else if (pRefStart->pSpread == pRefEnd->pSpread)
00889         {
00890             // Dragging, and start and end are in the same spread
00891             if (pRefStart->pNode != pRefEnd->pNode && pRefEnd->pNode != NULL)
00892             {
00893                 StatusID = _R(IDS_BLENDSTATUS_OVEREND);
00894 
00895                 if (pRefEnd->Index >= 0 && pRefStart->Index >= 0)
00896                     // We are over a blob of a selected path, for both start & end objects
00897                     // so display the remap cursor
00898                     pcNewCursor = pcBlendableRemapCursor;
00899                 else
00900                     // We are over a blendable node
00901                     pcNewCursor = pcBlendableCursor;
00902             }
00903             else
00904                 StatusID = _R(IDS_BLENDSTATUS_FINDEND);
00905         }
00906     }
00907 
00908     if (pcCurrentCursor != pcNewCursor)
00909     {
00910         // Current cursor has changed
00911         CursorStack::GSetTop(pcNewCursor, CurrentCursorID);
00912         pcCurrentCursor = pcNewCursor;
00913     }
00914 
00915     // Always update the status bar text
00916     DisplayStatusBarHelp(StatusID);
00917 }
00918 
00919 /********************************************************************************************
00920 
00921 >   virtual BOOL BlendTool::GetStatusLineText(  String_256* ptext, 
00922                                                 Spread* pSpread,
00923                                                 DocCoord DocPos, 
00924                                                 ClickModifiers ClickMods)
00925     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00926     Created:    19/12/94
00927     Inputs:     pStr        - ptr to string to place text in
00928                 pSpread     - ptr to the spread in question
00929                 DocPos      - position of mouse in doc (in spread coords)
00930                 ClickMods   - mouse click modifiers
00931     Outputs:    *pStr - text for status line
00932     Returns:    TRUE if outputting valid text
00933     Purpose:    generate up-to-date text for the status line (called on idles)
00934 
00935 ********************************************************************************************/
00936 
00937 BOOL BlendTool::GetStatusLineText(String_256* pStr,Spread* pSpread,DocCoord DocPos,ClickModifiers ClickMods)
00938 {
00939     *pStr = String_256(StatusID);
00940     return TRUE;
00941 }
00942 
00943 
00944 /********************************************************************************************
00945 
00946 >   void BlendTool::UpdateInfobar()
00947 
00948     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
00949     Created:    16/11/99
00950     Inputs:     -
00951     Outputs:    -
00952     Returns:    -
00953     Purpose:    Asks the infobar to update itself
00954     SeeAlso:    -
00955 
00956 ********************************************************************************************/
00957 
00958 void BlendTool::UpdateInfobar()
00959 {
00960     pBlendInfoBarOp->UpdateInfoBarState();
00961 }
00962 
00963 
00964 
00965 
00966 /********************************************************************************************
00967 
00968 >   BOOL BlendTool::IsPointOverPathBlob(DocCoord* pPointerPos,BlendToolRef* pRef)
00969 
00970     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00971     Created:    10/10/94
00972     Inputs:     pPointerPos = ptr to position of mouse in DocCoords
00973                 pRef        = ptr to blend tool ref to update
00974     Outputs:    Updates pRef->pNode and pRef->pIndex if a path blob was found.
00975                 Also, if blob found, *pPointerPos is snapped to coord of blob
00976     Returns:    TRUE if the coord is over a blob of a selected path
00977     Purpose:    Scans for selected paths.  If the coord is over a selected path's blob,
00978                 it returns TRUE and pRef->pNode points to the path, and pRef->Index contains
00979                 the element index of the blob.
00980                 Also, *pPointerPos is snapped to the coord of the centre of the blob, if found.
00981 
00982 ********************************************************************************************/
00983 
00984 BOOL BlendTool::IsPointOverPathBlob(DocCoord* pPointerPos,BlendToolRef* pRef)
00985 {
00986     if (pRef == NULL) return FALSE;
00987 
00988     // Find the selected range of objects
00989     SelRange* pSelRange = GetApplication()->FindSelection();
00990     Node* pNode = pSelRange->FindFirst();
00991 
00992     BOOL BlobFound = FALSE;
00993 
00994     // Scan the selection for NodePath objects
00995     while (!BlobFound && pNode != NULL && pNode->FindParent() != NULL)
00996     {
00997         // Only look at selected NodePaths that are NOT selected inside another node.
00998         if (IS_A(pNode,NodePath) && IS_A(pNode->FindParent(),Layer))
00999         {
01000             NodePath* pNodePath = (NodePath*)pNode;
01001 
01002             if (pNodePath->GetUnionBlobBoundingRect().ContainsCoord(*pPointerPos))
01003             {
01004                 // Get a pointer to the Path object within the NodePath
01005                 Path* pPath = &(pNodePath->InkPath);
01006 
01007                 // Is it over a blob? (Only check end points. Forget about control points)
01008                 BlobFound = pPath->FindNearestPoint(*pPointerPos,POINTFLAG_ENDPOINTS,&(pRef->Index));
01009 
01010                 // If a blob is found, store ptr to the node
01011                 if (BlobFound)
01012                 {
01013                     pRef->pNode = pNodePath;
01014                     pPath->SetPathPosition(pRef->Index);
01015                     *pPointerPos = pPath->GetCoord();
01016                 }
01017             }
01018         }
01019 
01020         // Now find the next selected node
01021         pNode = pSelRange->FindNext(pNode);
01022     }
01023 
01024     return BlobFound;
01025 }
01026 
01027 /********************************************************************************************
01028 
01029 >   BOOL BlendTool::IsPointOverBlendBlob(DocCoord* pPointerPos,NodeRenderableInk** ppNodePath,INT32* pIndex)
01030 
01031     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
01032     Created:    10/11/94
01033     Inputs:     pPointerPos = ptr to position of mouse in DocCoords
01034                 pRef        = ptr to blend tool ref to update
01035     Outputs:    Updates pRef->pNode pRef->pNodeBlend and pRef->Index if a blend blob was found.
01036                 Also, if found, *pPointerPos = centre of blob
01037     Returns:    TRUE if the coord is over a blob of a selected blend
01038     Purpose:    Scans for selected blends.  If the coord is over a selected blend's blob,
01039                 it returns TRUE and pRef->pNode points to the path, pRef->pNodeBlend points to the blend
01040                 containing the path, and *pIndex contains the element index of the blob.
01041                 Also, if found, *pPointerPos = centre of blob.
01042 
01043 ********************************************************************************************/
01044 
01045 BOOL BlendTool::IsPointOverBlendBlob(DocCoord* pPointerPos,BlendToolRef* pRef)
01046 {
01047     if (pRef == NULL) return FALSE;
01048 
01049     // Find the selected range of objects
01050     SelRange* pSelRange = GetApplication()->FindSelection();
01051     Node* pNode = pSelRange->FindFirst();
01052 
01053     BOOL BlobFound = FALSE;
01054 
01055     // Scan the selection for NodePath objects
01056     while (pNode != NULL && !BlobFound)
01057     {
01058         if (pNode->GetRuntimeClass() == CC_RUNTIME_CLASS(NodeBlend))
01059         {
01060             NodeBlend* pNodeBlend = (NodeBlend*)pNode;
01061 
01062             if (pNodeBlend->GetUnionBlobBoundingRect().ContainsCoord(*pPointerPos))
01063             {
01064                 BlobFound = pNodeBlend->IsPointOverBlob(pPointerPos,
01065                                                         &(pRef->pBlendPath),
01066                                                         &(pRef->Index),
01067                                                         &(pRef->AStartNode),
01068                                                         &(pRef->RemapRef));
01069 
01070                 if (BlobFound)
01071                 {
01072                     pRef->pNode      = pNodeBlend;
01073                     pRef->pNodeBlend = pNodeBlend;
01074 //                  *pPointerPos = pRef->pBlendPath->GetPathCoord(pRef->Index);
01075                 }
01076             }
01077         }
01078 
01079         // Now find the next selected node
01080         pNode = pSelRange->FindNext(pNode);
01081     }
01082 
01083     return BlobFound;
01084 }
01085 
01086 /********************************************************************************************
01087 
01088 >   void BlendTool::CheckNodeRemapping(BlendToolRef* pRefStart, BlendToolRef* pRefEnd)
01089 
01090     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
01091     Created:    6/12/94
01092     Inputs:     pRefStart = ptr to start ref
01093                 pRefEnd   = ptr to end ref
01094     Outputs:    Potentailly updates pRefStart and pRefEnd, so that if they represent a remapping of nodes,
01095                 they have the same remap reference
01096     Returns:    -
01097     Purpose:    This checks to see if the two references actually represent a node remapping within a blend.
01098                 In order to cope with multi-stage blends, both references have to be looked at the same time.
01099 
01100                 This is not the neatest way of doing it, but it's the quickest and safest method, given that
01101                 it has to work for the gamma release it a few days time.
01102 
01103 ********************************************************************************************/
01104 
01105 void BlendTool::CheckNodeRemapping(BlendToolRef* pRefStart, BlendToolRef* pRefEnd)
01106 {
01107     ERROR3IF(pRefStart == NULL,"pRefStart == NULL");
01108     ERROR3IF(pRefEnd   == NULL,"pRefEnd == NULL");
01109     if (pRefStart == NULL || pRefEnd == NULL)
01110         return;
01111 
01112     NodeBlend* pNodeBlend = pRefStart->pNodeBlend;
01113 
01114     if (pNodeBlend == NULL || pNodeBlend != pRefEnd->pNodeBlend)
01115         return;
01116 
01117     Node* pNode = pNodeBlend->FindFirstChild();
01118     while (pNode != NULL)
01119     {
01120         if (IS_A(pNode,NodeBlender))
01121         {
01122             NodeBlender* pNodeBlender = (NodeBlender*)pNode;
01123 
01124             BOOL StartFound = pNodeBlender->IsPointOverBlob(&(pRefStart->PointerPos),
01125                                                             &(pRefStart->pBlendPath),
01126                                                             &(pRefStart->Index),
01127                                                             &(pRefStart->AStartNode));
01128 
01129             BOOL EndFound   = pNodeBlender->IsPointOverBlob(&(pRefEnd  ->PointerPos),
01130                                                             &(pRefEnd  ->pBlendPath),
01131                                                             &(pRefEnd  ->Index),
01132                                                             &(pRefEnd  ->AStartNode));
01133 
01134             if (StartFound && EndFound && (pRefStart->AStartNode != pRefEnd->AStartNode))
01135             {
01136                 pRefStart->RemapRef = pNodeBlender->GetTag();
01137                 pRefEnd  ->RemapRef = pNodeBlender->GetTag();
01138                 return;
01139             }
01140         }
01141         pNode