cntrtool.cpp

Go to the documentation of this file.
00001 // $Id: cntrtool.cpp 1386 2006-06-28 17:49:55Z 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 "csrstack.h"
00106 //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00107 //#include "markn.h"
00108 //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00109 #include "nodepath.h"
00110 #include "progress.h"
00111 #include "nodeblnd.h"
00112 #include "nodebldr.h"
00113 //#include "simon.h"
00114 #include "blobs.h"
00115 //#include "blndres.h"
00116 #include "objchge.h"
00117 //#include "resource.h"
00118 //#include "barsdlgs.h"
00119 //#include "will.h"
00120 #include "filltool.h"
00121 #include "bubbleid.h"
00122 //#include "becomea.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00123 #include "attrmap.h"
00124 #include "ndbldpth.h"
00125 #include "pathedit.h"
00126 #include "keypress.h"
00127 #include "vkextra.h"
00128 #include "ezmodule.h"
00129 #include "opbevel.h"
00130 //#include "will2.h"
00131 //#include "biasres.h"
00132 #include "opcntr.h"
00133 #include "cntrtool.h"
00134 #include "ppbevel.h"
00135 #include "nodetxts.h"   // for TextStory.
00136 //#include "camvw.h"
00137 #include "layer.h"
00138 #include "ophist.h"
00139 // the resources headers
00140 //#include "cntres.h"
00141 #include "gclips.h"
00142 
00143 // for bevels & shadows
00144 #include "nodecont.h"
00145 #include "nodecntr.h"
00146 #include "ncntrcnt.h"
00147 //#include "rikdlg.h"
00148 #include "nodemold.h"
00149 #include "ndclpcnt.h"
00150 #include "biasdlg.h"
00151 #include "helpuser.h"
00152 //#include "xshelpid.h"
00153 //#include "helppath.h"
00154 #include "dragmgr.h"
00155 #include "lineattr.h"
00156 #include "effects_stack.h"
00157 #include "blndtool.h"
00158 #include "nbevcont.h"
00159 
00160 DECLARE_SOURCE( "$Revision: 1386 $" );
00161                                                 
00162 CC_IMPLEMENT_MEMDUMP(ContourTool,Tool_v1)
00163 CC_IMPLEMENT_DYNCREATE(ContourInfoBarOp,InformationBarOp)
00164 CC_IMPLEMENT_DYNCREATE(OpContourNodes,SelOperation)
00165 
00166 // Must come after the last CC_IMPLEMENT.. macro
00167 #define new CAM_DEBUG_NEW     
00168 
00169 // These are still char* while we wait for resource technology to be developed for modules
00170 TCHAR* ContourTool::FamilyName  = _T("Contour Tools");
00171 TCHAR* ContourTool::ToolName    = _T("Contour Tool");
00172 TCHAR* ContourTool::Purpose     = _T("Contour manipulation");
00173 TCHAR* ContourTool::Author      = _T("David Mc");
00174 
00175 // Init those other useful static vars
00176 ContourInfoBarOp*   ContourTool::pContourInfoBarOp          = NULL;
00177 BlendToolRef*   ContourTool::pRefStart              = NULL;
00178 BlendToolRef*   ContourTool::pRefEnd                = NULL;
00179 Cursor*         ContourTool::pcNormalCursor         = NULL;
00180 Cursor*         ContourTool::pcOverBlob             = NULL;
00181 Cursor*         ContourTool::pcCurrentCursor            = NULL;
00182 INT32           ContourTool::CurrentCursorID            = 0;
00183 UINT32          ContourTool::StatusID                   = _R(IDS_CONTOURDRAGHELP);
00184 
00185 OpContourNodes      *ContourTool::m_pOpContourNodes = NULL;
00186 
00187 // maximum & minimum values for the contour slider
00188 #define CONTOUR_DEPTH_MIN 10
00189 #define CONTOUR_DEPTH_MAX 250000
00190 
00191 //#define Swap(a,b)       { (a)^=(b), (b)^=(a), (a)^=(b); }
00192 
00193 #define SWAP(type,a,b) { type x=a; a=b; b=x; }
00194 
00196 // Flatness settings
00197 const INT32 ContourToolFlatness=2048;
00198 
00200 // Profile for the logarithmic width slider
00201 CProfileBiasGain SliderProfile((AFp)0.7, (AFp)0.0);
00202 
00203 
00204 /********************************************************************************************
00205 
00206 >   ContourTool::ContourTool()
00207 
00208     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00209     Created:    3/10/94
00210     Purpose:    Default Constructor.
00211                 Other initialisation is done in ContourTool::Init which is called by the Tool Manager
00212     SeeAlso:    ContourTool::Init
00213 
00214 ********************************************************************************************/
00215 
00216 ContourTool::ContourTool()
00217 {
00218     pcCurrentCursor = NULL;
00219     m_bDisableBlobRenderingFlag = FALSE;
00220 }
00221 
00222 /********************************************************************************************
00223 
00224 >   ContourTool::~ContourTool()
00225 
00226     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00227     Created:    3/10/94
00228     Purpose:    Destructor.
00229 
00230 ********************************************************************************************/
00231 
00232 ContourTool::~ContourTool()
00233 {
00234     if (pRefStart != NULL)
00235     {
00236         delete pRefStart;
00237         pRefStart = NULL;
00238     }
00239 
00240     if (pRefEnd != NULL)
00241     {
00242         delete pRefEnd;
00243         pRefEnd = NULL;
00244     }
00245 
00246     if (m_pOpContourNodes)
00247     {
00248         delete m_pOpContourNodes;
00249         m_pOpContourNodes = NULL;
00250     }
00251 }
00252 
00253 
00254 /********************************************************************************************
00255 
00256 >   BOOL ContourTool::Init( INT32 Pass )
00257 
00258     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00259     Created:    3/10/94
00260     Returns:    FALSE if it does not want to be created, TRUE otherwise
00261     Purpose:    Used to check if the Tool was properly constructed
00262     SeeAlso:    ContourTool::ContourTool
00263 
00264 ********************************************************************************************/
00265 
00266 BOOL ContourTool::Init()
00267 {
00268     // Declare all your ops here and only succeed if all declarations succeed
00269     BOOL ok =   OpContourNodes::Declare() &&
00270         OpChangeContourToInner::Declare() &&
00271         OpChangeContourToOuter::Declare() &&
00272         OpRemoveContour::Declare() &&
00273         OpChangeContourSteps::Declare() &&
00274         OpChangeContourColourType::Declare() &&
00275         OpChangeContourAttributeProfile::Declare() &&
00276         OpChangeContourObjectProfile::Declare() &&
00277         OpChangeContourStepDistance::Declare() &&
00278         OpToggleContourInsetPath::Declare();
00279     
00280     if (!ok) return FALSE;
00281 
00282     m_pOpContourNodes = new OpContourNodes;
00283 
00284     if (!m_pOpContourNodes)
00285         return FALSE;
00286 
00287     // We need two BlendToolRef objects
00288     ContourTool::pRefStart = new BlendToolRef;
00289     ContourTool::pRefEnd   = new BlendToolRef;
00290 
00291     ok = (ContourTool::pRefStart != NULL && ContourTool::pRefEnd != NULL);
00292 
00293     // This section reads in the infobar definition and creates an instance of
00294     // ContourInfoBarOp.  Also pContourInfoBarOp, the ptr to the tool's infobar, is set up
00295     // after the infobar is successfully read and created.
00296     if (ok)
00297     {
00298 #if 0       
00299         CCResTextFile       file;               // Resource File
00300         ContourInfoBarOpCreate BarCreate;           // Object that creates ContourInfoBarOp objects
00301 
00302 //              ok = file.open(_R(IDM_BLEND_BAR), _R(IDT_INFO_BAR_RES));        // Open resource
00303                 ok = file.open( _R(IDM_CONTOUR_BAR), _R(IDT_INFO_BAR_RES) );
00304         if (ok) ok = DialogBarOp::ReadBarsFromFile(file,BarCreate); // Read and create info bar
00305         if (ok) file.close();                                       // Close resource
00306 
00307         ENSURE(ok,"Unable to load blendbar.ini from resource\n"); 
00308 
00309         if (ok)
00310         {
00311             // Info bar now exists.  Now get a pointer to it
00312             String_32 str = String_32(_R(IDS_CONTOURTOOL_INFOBARNAME));
00313             DialogBarOp* pDialogBarOp = DialogBarOp::FindDialogBarOp(str);
00314 
00315                     ok = (pDialogBarOp != NULL);
00316             if (ok) ok = pDialogBarOp->IsKindOf(CC_RUNTIME_CLASS(ContourInfoBarOp));
00317             if (ok) pContourInfoBarOp = (ContourInfoBarOp*)pDialogBarOp;
00318 
00319             ENSURE(ok,"Error finding the blend tool info bar");
00320         }
00321 #endif
00322         pContourInfoBarOp = new ContourInfoBarOp();
00323         ok = (pContourInfoBarOp != NULL);
00324 
00325     }
00326 
00327     return (ok);
00328 }
00329 
00330 
00331 /********************************************************************************************
00332 
00333 >   void ContourTool::Describe(void *InfoPtr)
00334 
00335     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00336     Created:    3/10/94
00337     Inputs:     InfoPtr -   A pointer to a tool info block. It is passed cast to void* as
00338                             the version of the tool is unknown at this point. Later versions 
00339                             of the Tool class may have more items in this block, that this 
00340                             tool will not use
00341     Outputs:    InfoPtr -   The structure pointed to by InfoPtr will have had all the info
00342                             that this version of the Tool knows about
00343     Purpose:    Allows the tool manager to extract information about the tool
00344 
00345 ********************************************************************************************/
00346 
00347 void ContourTool::Describe(void *InfoPtr)
00348 {
00349     // Cast structure into the latest one we understand.
00350     ToolInfo_v1 *Info = (ToolInfo_v1 *) InfoPtr;
00351 
00352     Info->InfoVersion = 1;
00353     
00354     Info->InterfaceVersion = GetToolInterfaceVersion();  // You should always have this line.
00355         
00356     // These are all arbitrary at present.
00357     Info->Version = 1;
00358     Info->ID      = GetID();
00359     Info->TextID  = _R(IDS_CONTOUR_TOOL);
00360 
00361     Info->Family  = FamilyName;
00362     Info->Name    = ToolName;
00363     Info->Purpose = Purpose;
00364     Info->Author  = Author;
00365 
00366     Info->BubbleID = _R(IDBBL_CONTOUR_TOOLBOX);
00367 }
00368 
00369 /********************************************************************************************
00370 
00371 >   virtual void ContourTool::SelectChange(BOOL isSelected)
00372 
00373     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00374     Created:    3/10/94
00375     Inputs:     isSelected  - TRUE  = tool has been selected
00376                             - FALSE = tool has been deselected
00377     Outputs:    -
00378     Returns:    -
00379     Purpose:    Starts up and closes down the blend tool
00380     Errors:     Debug warning if creating the cursor fails.
00381     SeeAlso:    -
00382 
00383 ********************************************************************************************/
00384 
00385 void ContourTool::SelectChange(BOOL isSelected)
00386 {
00387 // Stub out this function if the tool isn't wanted
00388 #ifndef NO_ADVANCED_TOOLS
00389     if (isSelected)
00390     {
00391         if (!CreateCursors()) return;
00392         CurrentCursorID = CursorStack::GPush(pcNormalCursor, FALSE);        // Push cursor but don't display now
00393         pcCurrentCursor = pcNormalCursor;
00394 
00395         // Create and display the tool's info bar
00396         pContourInfoBarOp->Create();
00397 
00398         // Which blobs do I want displayed
00399         BlobManager* BlobMgr = GetApplication()->GetBlobManager();
00400         if (BlobMgr != NULL)
00401         {
00402             // Decide which blobs we will display
00403             BlobStyle MyBlobs;
00404             MyBlobs.Object = FALSE;
00405             MyBlobs.Tiny = TRUE;
00406             // Tell the blob manager
00407             BlobMgr->ToolInterest(MyBlobs);
00408         }
00409 
00410         Document* pDoc = Document::GetCurrent();
00411         SetupToolBlobs();
00412 
00413         if (pDoc != NULL)
00414             RenderToolBlobs(pDoc->GetSelectedSpread(),NULL);
00415 
00416         pContourInfoBarOp->m_pTool = this;
00417     }
00418     else
00419     {
00420         // Deselection - destroy the tool's cursors, if they exist.
00421         if (pcCurrentCursor != NULL)
00422         {
00423             CursorStack::GPop(CurrentCursorID);
00424             pcCurrentCursor = NULL;
00425             CurrentCursorID = 0;
00426         }
00427         DestroyCursors();
00428 
00429         pContourInfoBarOp->CloseProfileDialog (pContourInfoBarOp->m_BiasGainObjectGadget);
00430         pContourInfoBarOp->CloseProfileDialog (pContourInfoBarOp->m_BiasGainAttrGadget);
00431 
00432         // Remove the info bar from view by deleting the actual underlying window
00433         pContourInfoBarOp->Delete();
00434 
00435         // ensure any tool object blobs are removed.
00436         BlobManager* BlobMgr = GetApplication()->GetBlobManager();
00437         if (BlobMgr != NULL)
00438         {
00439             BlobStyle bsRemoves;
00440             bsRemoves.ToolObject = TRUE;
00441             BlobMgr->RemoveInterest(bsRemoves);
00442         }
00443 
00444         Document* pDoc = Document::GetCurrent();
00445         if (pDoc != NULL)
00446             RenderToolBlobs(pDoc->GetSelectedSpread(),NULL);
00447     }
00448 #endif  // NO_ADVANCED_TOOLS
00449 }
00450 
00451 /********************************************************************************************
00452 
00453 >   void ContourTool::SetupToolBlobs()
00454 
00455     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00456     Created:    3/10/94
00457     Inputs:     -
00458     Outputs:    -
00459     Returns:    
00460     Purpose:    Sets up the tool blobs for the current selection.
00461 
00462                 Karim 28/07/2000
00463                 Contour tool blobs are laid out like so relative to the selection:
00464 
00465                         2   4   3
00466 
00467                         6   *   7
00468 
00469                         0   5   1
00470 
00471                 with corner blobs positioned 1 pixel out from the bounds rect
00472                 and middle blobs positioned 2 pixels out from the bounds rect.
00473 
00474     SeeAlso:    -
00475 
00476 ********************************************************************************************/
00477 void ContourTool::SetupToolBlobs()
00478 {
00479     // reset the blob positions.
00480     for (INT32 i = 0 ; i < 8; i++)
00481     {
00482         m_BlobPoints[i].x = 0;
00483         m_BlobPoints[i].y = 0;
00484     }
00485 
00486     // give up if we there is no selection or we can't get the info we need.
00487     if (GetApplication()->FindSelection() == NULL)
00488         return;
00489 
00490     if (GetApplication()->FindSelection()->IsEmpty())
00491         return;
00492 
00493     BlobManager * pBlobMgr = GetApplication()->GetBlobManager();
00494     if (pBlobMgr == NULL)
00495         return;
00496 
00497     // figure out the blob offsets, in millipoints.
00498     INT32 BlobGap = 0;
00499     DocView* pDocView = DocView::GetSelected();
00500     if (pDocView != NULL)
00501         BlobGap = pDocView->GetScaledPixelWidth().MakeLong();
00502 
00503     // ok, get the bounds of the selection, including contours.
00504     SelRange Sel(*(GetApplication()->FindSelection()));
00505     DocRect drBounds;
00506     Node* pN = Sel.FindFirst();
00507     while (pN != NULL)
00508     {
00509         if (pN->IsBounded())
00510         {
00511             Node* pParent = pN->FindParent();
00512             while (pParent != NULL && pParent->IS_KIND_OF(NodeContourController))
00513             {
00514                 pN = pParent;
00515                 pParent = pN->FindParent();
00516             }
00517 
00518             drBounds = drBounds.Union( ((NodeRenderableBounded*)pN)->GetBoundingRect() );
00519         }
00520         pN = Sel.FindNext(pN);
00521     }
00522 
00523     // lets set up those blobs!
00524     DocCoord dc;
00525     drBounds.Inflate(BlobGap);
00526     INT32 BlobSize = pBlobMgr->GetBlobSize();
00527 
00528     dc.x = drBounds.lo.x - BlobSize;
00529     dc.y = drBounds.lo.y - BlobSize;
00530     m_BlobPoints[0] = dc;
00531 
00532     dc.x = drBounds.hi.x + BlobSize;
00533     dc.y = drBounds.lo.y - BlobSize;
00534     m_BlobPoints[1] = dc;
00535 
00536     dc.x = drBounds.lo.x - BlobSize;
00537     dc.y = drBounds.hi.y + BlobSize;
00538     m_BlobPoints[2] = dc;
00539 
00540     dc.x = drBounds.hi.x + BlobSize;
00541     dc.y = drBounds.hi.y + BlobSize;
00542     m_BlobPoints[3] = dc;
00543 
00544     dc.x = (drBounds.lo.x + drBounds.hi.x) / 2;
00545     dc.y = drBounds.hi.y + BlobSize + BlobGap;
00546     m_BlobPoints[4] = dc;
00547 
00548     dc.x = (drBounds.lo.x + drBounds.hi.x) / 2;
00549     dc.y = drBounds.lo.y - BlobSize - BlobGap;
00550     m_BlobPoints[5] = dc;
00551 
00552     dc.x = drBounds.lo.x - BlobSize - BlobGap;
00553     dc.y = (drBounds.lo.y + drBounds.hi.y) / 2;
00554     m_BlobPoints[6] = dc;
00555 
00556     dc.x = drBounds.hi.x + BlobSize + BlobGap;
00557     dc.y = (drBounds.lo.y + drBounds.hi.y) / 2;
00558     m_BlobPoints[7] = dc;
00559 }
00560 
00561 
00562 
00563 /********************************************************************************************
00564 
00565 >   BOOL ContourTool::CreateCursors()
00566 
00567     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00568     Created:    3/10/94
00569     Inputs:     -
00570     Outputs:    -
00571     Returns:    TRUE if all the blend tool cursors have been successfully created
00572     Purpose:    Creates all the blend tool cursors
00573     SeeAlso:    -
00574 
00575 ********************************************************************************************/
00576 
00577 BOOL ContourTool::CreateCursors()
00578 {
00579     // This tool has just been selected.  Create the cursors.
00580     pcNormalCursor          = new Cursor(this, _R(IDC_POINTER_CONTOUR));
00581     pcOverBlob              = new Cursor(this, _R(IDC_SELECT_CONTOUR));
00582     
00583     if ( pcNormalCursor         ==NULL || !pcNormalCursor->IsValid() ||
00584         pcOverBlob== NULL || !pcOverBlob->IsValid()
00585        )
00586     {
00587         DestroyCursors();
00588         return FALSE;
00589     }
00590     else
00591         return TRUE;
00592 }
00593 
00594 /********************************************************************************************
00595 
00596 >   BOOL ContourTool::IsPointOverBlob(DocCoord &Point, DocRect * pBlobRect)
00597 
00598     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00599     Created:    3/10/94
00600     Inputs:     A point in document coordinates, and the doc rect variable to
00601                 put the blob rect into
00602     Outputs:    -
00603     Returns:    TRUE if the point is over a blob, false otherwise
00604     Purpose:    Creates all the blend tool cursors
00605     SeeAlso:    -
00606 
00607 ********************************************************************************************/
00608 BOOL ContourTool::IsPointOverBlob(DocCoord &Point, DocRect * pBlobRect)
00609 {
00610     // check to see if I'm over a blob
00611     BlobManager * pBlobMgr = GetApplication()->GetBlobManager();
00612 
00613     INT32 BlobSize = 0;
00614 
00615     // Karim 18/09/2000
00616     // The contour blobs are about twice the size of normal blobs,
00617     // so I'm doubling the value of BlobSize when checking for them.
00618     if (pBlobMgr)
00619         BlobSize = pBlobMgr->GetBlobSize();
00620 //      BlobSize = pBlobMgr->GetBlobSize()/2;
00621 
00622     for (INT32 i = 0; i < 8; i++)
00623     {
00624         DocRect dr( m_BlobPoints[i].x - BlobSize,
00625                     m_BlobPoints[i].y - BlobSize,
00626                     m_BlobPoints[i].x + BlobSize,
00627                     m_BlobPoints[i].y + BlobSize);
00628 
00629         if (dr.ContainsCoord(Point))
00630         {
00631             if (pBlobRect)
00632                 *pBlobRect = dr;
00633             return TRUE;
00634         }
00635     }
00636 
00637     return FALSE;
00638 }
00639 
00640 /********************************************************************************************
00641 
00642 >   void ContourTool::DestroyCursors()
00643 
00644     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00645     Created:    3/10/94
00646     Inputs:     -
00647     Outputs:    -
00648     Returns:    -
00649     Purpose:    Destroys all the blend tool cursors
00650     SeeAlso:    -
00651 
00652 ********************************************************************************************/
00653 
00654 void ContourTool::DestroyCursors()
00655 {
00656     if (pcNormalCursor          != NULL) delete pcNormalCursor;
00657     if (pcOverBlob              != NULL) delete pcOverBlob;
00658 
00659     pcNormalCursor = NULL;
00660     pcOverBlob = NULL;
00661 }
00662 
00663 /********************************************************************************************
00664 
00665 >   BOOL ContourTool::OnKeyPress(KeyPress* pKeyPress)
00666 
00667     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00668     Created:    1/6/99
00669     Inputs:     pKeyPress - pointer to a keypress object
00670     Returns:    TRUE if it handled the keypress, FALSE otherwise
00671     Purpose:    To handle keypress events for the Blend Tool.
00672 
00673 ********************************************************************************************/
00674 
00675 BOOL ContourTool::OnKeyPress(KeyPress* pKeyPress)
00676 {
00677 #ifdef _DEBUG
00678     if (pKeyPress == NULL)
00679         return FALSE;
00680 
00681     if (pKeyPress->IsRepeat())
00682         return FALSE;
00683 
00684     if (pKeyPress->IsRelease())
00685         return FALSE;
00686 
00687     AFp BiasDelta = 0.0;
00688     AFp GainDelta = 0.0;
00689     BOOL Reset = FALSE;
00690     if (*pKeyPress == KeyPress(CAMKEY(Z)))  { BiasDelta = -0.1; TRACEUSER( "Markn", _T("Decrease Bias by 0.1\n"));}
00691     if (*pKeyPress == KeyPress(CAMKEY(X)))  { BiasDelta =  0.1; TRACEUSER( "Markn", _T("Increase Bias by 0.1\n"));}
00692     if (*pKeyPress == KeyPress(CAMKEY(N)))  { GainDelta = -0.1; TRACEUSER( "Markn", _T("Decrease Gain by 0.1\n"));}
00693     if (*pKeyPress == KeyPress(CAMKEY(M)))  { GainDelta =  0.1; TRACEUSER( "Markn", _T("Increase Gain by 0.1\n"));}
00694 
00695     if (*pKeyPress == KeyPress(CAMKEY(R)))  { Reset = TRUE;     TRACEUSER( "Markn", _T("Resetting Bias and Gain\n"));}
00696 
00697     SelRange* pSelRange = GetApplication()->FindSelection();
00698     Node* pNode = pSelRange->FindFirst();
00699     while (pNode)
00700     {
00701         if (IS_A(pNode,NodeBlend))
00702         {
00703             NodeBlend* pNodeBlend = (NodeBlend*)pNode;
00704 
00705             // This alters the Attribute profile, but can easily be modified to alter the Object profile if necessary
00706             CProfileBiasGain* pProfile = pNodeBlend->GetAttrProfile();
00707             if (pProfile)
00708             {
00709                 AFp Bias = pProfile->GetBias() + BiasDelta;
00710                 AFp Gain = pProfile->GetGain() + GainDelta;
00711                 if (Reset)
00712                     Bias = Gain = 0.0;
00713 
00714                 if (Bias < -0.9)    Bias = -0.9;
00715                 if (Bias >  0.9)    Bias =  0.9;
00716                 if (Gain < -0.9)    Gain = -0.9;
00717                 if (Gain >  0.9)    Gain =  0.9;
00718 
00719                 pProfile->SetBiasGain(Bias,Gain);
00720             }
00721         }
00722         // Now find the next selected node
00723         pNode = pSelRange->FindNext(pNode);
00724     }
00725 #endif // _DEBUG
00726 
00727     return FALSE;
00728 }
00729 
00730 /********************************************************************************************
00731 
00732 >   void ContourTool::OnClick( DocCoord PointerPos, ClickType Click, ClickModifiers ClickMods,
00733                         Spread* pSpread )
00734 
00735     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00736     Created:    3/10/94
00737     Inputs:     PointerPos  -   The DocCoord of the point where the mouse button was clicked
00738                 Click       -   Describes the type of click that was detected. 
00739                 ClickMods   -   Indicates which buttons caused the click and which modifers were
00740                                 pressed at the same time
00741                 pSpread     -   The spread in which the click happened
00742     Returns:    -
00743     Purpose:    To handle a Mouse Click event for the Blend Tool.
00744     SeeAlso:    Tool::MouseClick; ClickType; ClickModifiers
00745 
00746 ********************************************************************************************/
00747 
00748 void ContourTool::OnClick( DocCoord PointerPos, ClickType Click, ClickModifiers ClickMods,
00749                         Spread* pSpread )
00750 {
00751 // Stub out this function if the tool isn't wanted
00752 #ifndef NO_ADVANCED_TOOLS
00753     if (ClickMods.Menu) return;                         // Don't do anything if the user clicked the Menu button
00754 
00755     ERROR3IF_PF(pSpread==NULL,("pSpread is NULL"));
00756 
00757 //  NodeCompound * pCompound = NULL;
00758 
00759     DocRect BlobRect;
00760 
00761     switch (Click)
00762     {
00763         // if a drag was started, we alter or create a contour.
00764         case CLICKTYPE_DRAG:
00765         {
00766             if (m_pOpContourNodes)
00767             {
00768                 m_pOpContourNodes->DoDrag(this, pContourInfoBarOp, PointerPos,
00769                     IsPointOverBlob(PointerPos, &BlobRect), &BlobRect);
00770                 return;
00771             }
00772         }
00773         break;
00774 
00775         // we ignore all other click types, but note that they aren't drag-clicks.
00776         case CLICKTYPE_SINGLE:
00777         {
00778             // check for bevels existing
00779             List BevelList;
00780             BevelTools::BuildListOfSelectedNodes(&BevelList, CC_RUNTIME_CLASS(NodeBevelController));
00781             
00782             if (!BevelList.IsEmpty())
00783             {
00784                 BevelList.DeleteAll();
00785                 
00786                 DocRect BlobRect;
00787 
00788                 // disable the drag
00789                 if (IsPointOverBlob(PointerPos, &BlobRect))
00790                 {
00791                     DocView * pView = DocView::GetCurrent();
00792 
00793                     if (pView)
00794                         pView->EndDrag(NULL);               
00795                     
00796                     InformWarningBevelExistsInSelection();
00797                 }
00798             }
00799 
00800             // call the base class ....
00801 
00802             DragTool::OnClick (PointerPos, Click, ClickMods, pSpread);
00803         }
00804         break;
00805         
00806         default:
00807             // call the base class ....
00808             
00809             DragTool::OnClick (PointerPos, Click, ClickMods, pSpread);
00810         break;
00811     }
00812 #endif  // NO_ADVANCED_TOOLS
00813 }
00814 
00815 /********************************************************************************************
00816 
00817 >   void ContourTool::InformWarningBevelExistsInSelection()
00818 
00819     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00820     Created:    7/4/2000
00821     Purpose:    Brings up the associated warning dialog
00822     SeeAlso:    Tool::MouseClick; ClickType; ClickModifiers
00823 
00824 ********************************************************************************************/
00825 void ContourTool::InformWarningBevelExistsInSelection()
00826 {
00827     String_256 QueryString(_R(IDS_CANTCONTOURBEVEL));
00828 
00829     Error::SetError(0, QueryString, 0);             
00830     // The only way of bringing up a box with a string in it
00831     INT32 DlgResult = InformError(_R(IDS_CANTCONTOURBEVEL),
00832         _R(IDS_OK), _R(IDS_HELP));
00833                 
00834     if (DlgResult == 2)
00835     {
00836         HelpUserTopic(_R(IDS_HELPPATH_Message__Bevel_already_applied));
00837     }
00838 }
00839 
00840 
00841 /********************************************************************************************
00842 
00843 >   void ContourTool::OnMouseMove( DocCoord PointerPos,Spread* pSpread, ClickModifiers ClickMods)
00844 
00845     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00846     Created:    3/10/94
00847     Inputs:     PointerPos  -   The DocCoord of the point where the mouse has moved to
00848                 pSpread     -   The spread in which the move occurred
00849     Returns:    TRUE if it handled the Click, FALSE otherwise
00850     Purpose:    To handle a Mouse Move event for the Blend Tool.
00851     SeeAlso:    Tool::MouseClick; ClickType; ClickModifiers
00852 
00853 ********************************************************************************************/
00854 
00855 void ContourTool::OnMouseMove(DocCoord PointerPos,Spread* pSpread,ClickModifiers ClickMods)
00856 {
00857 // Stub out this function if the tool isn't wanted
00858 #ifndef NO_ADVANCED_TOOLS
00859     ERROR3IF_PF(pSpread==NULL,("pSpread is NULL"));
00860 
00861 //  BlobManager * pBlobMgr = GetApplication()->GetBlobManager();
00862 //  INT32 BlobSize = pBlobMgr->GetBlobSize();
00863 
00864     if (IsPointOverBlob(PointerPos))
00865     {
00866         // change the cursor
00867         if (pcCurrentCursor != pcOverBlob )
00868         {
00869             if (pcCurrentCursor)
00870             {
00871                 CursorStack::GPop(CurrentCursorID);
00872             }
00873             
00874             CurrentCursorID = CursorStack::GPush(pcOverBlob, TRUE);
00875             pcCurrentCursor = pcOverBlob;
00876             DisplayStatusBarHelp(_R(IDS_CONTOUROVERBLOBHELP));
00877         }
00878     }
00879     else
00880     {
00881         // change the cursor back
00882         if (pcCurrentCursor != pcNormalCursor )
00883         {
00884             if (pcCurrentCursor)
00885             {
00886                 CursorStack::GPop(CurrentCursorID);
00887             }
00888             
00889             CurrentCursorID = CursorStack::GPush(pcNormalCursor, TRUE);
00890             pcCurrentCursor = pcNormalCursor;
00891             DisplayStatusBarHelp(_R(IDS_CONTOURDRAGHELP));
00892         }
00893     }
00894 #endif  // NO_ADVANCED_TOOLS
00895 }
00896 
00897 /********************************************************************************************
00898 >   void ContourTool::UpdateRef(    BlendToolRef* pRef,
00899                                 Spread* pSpread, 
00900                                 DocCoord PointerPos,
00901                                 BOOL CheckNodeUnderPoint = TRUE)
00902 
00903     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00904     Created:    10/10/94
00905     Inputs:     pRef                = ptr to a reference struct to update
00906                 pSpread             = ptr to spread click occurred
00907                 PointerPos          = the DocCoord of the click
00908                 CheckNodeUnderPoint = TRUE to revert to click-detection search for underlying node
00909     Outputs:    The members of pRef are updated.
00910     Returns:    -
00911     Purpose:    This will update the blend tool ref depending on the current pointer pos.
00912                 If CheckNodeUnderPoint is TRUE, then the routine will revert to a click-detection search
00913                 to find out which node lies under the given point.  This is potentially very time-consuming.
00914     Errors:     -
00915     SeeAlso:    -
00916 ********************************************************************************************/
00917 
00918 void ContourTool::UpdateRef(BlendToolRef* pRef,Spread* pSpread, DocCoord PointerPos,BOOL CheckNodeUnderPoint)
00919 {
00920     ERROR3IF_PF(pRef   ==NULL,("pRef is NULL"));
00921     ERROR3IF_PF(pSpread==NULL,("pSpread is NULL"));
00922 
00923     // Set the spread and pointer pos members
00924     pRef->pSpread    = pSpread;
00925     pRef->PointerPos = PointerPos;
00926 }
00927 
00928 /********************************************************************************************
00929 >   void ContourTool::UpdateCursorAndStatus()
00930 
00931     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00932     Created:    10/10/94
00933     Inputs:     -
00934     Outputs:    -
00935     Returns:    -
00936     Purpose:    This will update the cursor and status line text depending on the data in the 
00937                 two blend tool references within ContourTool.
00938 
00939                 Copes with the following scenarios:
00940                     Pointer over an object
00941                     Dragging to another object
00942                     Pointer over a selected path's blob
00943                     Dragging to another blob in a selected path
00944                     Pointer over a blend blob
00945                     Dragging to a corresponding blend blob for remapping
00946 
00947     Errors:     -
00948     SeeAlso:    -
00949 ********************************************************************************************/
00950 
00951 void ContourTool::UpdateCursorAndStatus()
00952 {
00953     ERROR3IF_PF(pRefStart==NULL,("pRefStart is NULL"));
00954     ERROR3IF_PF(pRefEnd  ==NULL,("pRefEnd   is NULL"));
00955 
00956     DisplayStatusBarHelp(StatusID);
00957 }
00958 
00959 /********************************************************************************************
00960 
00961 >   virtual BOOL ContourTool::GetStatusLineText(    String_256* ptext, 
00962                                                 Spread* pSpread,
00963                                                 DocCoord DocPos, 
00964                                                 ClickModifiers ClickMods)
00965     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00966     Created:    19/12/94
00967     Inputs:     pStr        - ptr to string to place text in
00968                 pSpread     - ptr to the spread in question
00969                 DocPos      - position of mouse in doc (in spread coords)
00970                 ClickMods   - mouse click modifiers
00971     Outputs:    *pStr - text for status line
00972     Returns:    TRUE if outputting valid text
00973     Purpose:    generate up-to-date text for the status line (called on idles)
00974 
00975 ********************************************************************************************/
00976 
00977 BOOL ContourTool::GetStatusLineText(String_256* pStr,Spread* pSpread,DocCoord DocPos,ClickModifiers ClickMods)
00978 {
00979     if (StatusID != 0)
00980     {
00981         *pStr = String_256(StatusID);
00982     }
00983     else
00984     {
00985         *pStr = String_256(_R(IDS_CONTOURDRAGHELP));
00986     }
00987 
00988     return TRUE;
00989 }
00990 
00991 /********************************************************************************************
00992 
00993 >   BOOL ContourTool::IsPointOverPathBlob(DocCoord* pPointerPos,BlendToolRef* pRef)
00994 
00995     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00996     Created:    10/10/94
00997     Inputs:     pPointerPos = ptr to position of mouse in DocCoords
00998                 pRef        = ptr to blend tool ref to update
00999     Outputs:    Updates pRef->pNode and pRef->pIndex if a path blob was found.
01000                 Also, if blob found, *pPointerPos is snapped to coord of blob
01001     Returns:    TRUE if the coord is over a blob of a selected path
01002     Purpose:    Scans for selected paths.  If the coord is over a selected path's blob,
01003                 it returns TRUE and pRef->pNode points to the path, and pRef->Index contains
01004                 the element index of the blob.
01005                 Also, *pPointerPos is snapped to the coord of the centre of the blob, if found.
01006 
01007 ********************************************************************************************/
01008 
01009 BOOL ContourTool::IsPointOverPathBlob(DocCoord* pPointerPos,BlendToolRef* pRef)
01010 {
01011     if (pRef == NULL) return FALSE;
01012 
01013     // Find the selected range of objects
01014     SelRange* pSelRange = GetApplication()->FindSelection();
01015     Node* pNode = pSelRange->FindFirst();
01016 
01017     BOOL BlobFound = FALSE;
01018 
01019     // Scan the selection for NodePath objects
01020     while (!BlobFound && pNode != NULL && pNode->FindParent() != NULL)
01021     {
01022         // Only look at selected NodePaths that are NOT selected inside another node.
01023         if (IS_A(pNode,NodePath) && IS_A(pNode->FindParent(),Layer))
01024         {
01025             NodePath* pNodePath = (NodePath*)pNode;
01026 
01027             if (pNodePath->GetUnionBlobBoundingRect().ContainsCoord(*pPointerPos))
01028             {
01029                 // Get a pointer to the Path object within the NodePath
01030                 Path* pPath = &(pNodePath->InkPath);
01031 
01032                 // Is it over a blob? (Only check end points. Forget about control points)
01033                 BlobFound = pPath->FindNearestPoint(*pPointerPos,POINTFLAG_ENDPOINTS,&(pRef->Index));
01034 
01035                 // If a blob is found, store ptr to the node
01036                 if (BlobFound)
01037                 {
01038                     pRef->pNode = pNodePath;
01039                     pPath->SetPathPosition(pRef->Index);
01040                     *pPointerPos = pPath->GetCoord();
01041                 }
01042             }
01043         }
01044 
01045         // Now find the next selected node
01046         pNode = pSelRange->FindNext(pNode);
01047     }
01048 
01049     return BlobFound;
01050 }
01051 
01052 /********************************************************************************************
01053 
01054 >   BOOL ContourTool::IsPointOverBlendBlob(DocCoord* pPointerPos,NodeRenderableInk** ppNodePath,INT32* pIndex)
01055 
01056     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
01057     Created:    10/11/94
01058     Inputs:     pPointerPos = ptr to position of mouse in DocCoords
01059                 pRef        = ptr to blend tool ref to update
01060     Outputs:    Updates pRef->pNode pRef->pNodeBlend and pRef->Index if a blend blob was found.
01061                 Also, if found, *pPointerPos = centre of blob
01062     Returns:    TRUE if the coord is over a blob of a selected blend
01063     Purpose:    Scans for selected blends.  If the coord is over a selected blend's blob,
01064                 it returns TRUE and pRef->pNode points to the path, pRef->pNodeBlend points to the blend
01065                 containing the path, and *pIndex contains the element index of the blob.
01066                 Also, if found, *pPointerPos = centre of blob.
01067 
01068 ********************************************************************************************/
01069 
01070 BOOL ContourTool::IsPointOverBlendBlob(DocCoord* pPointerPos,BlendToolRef* pRef)
01071 {
01072     if (pRef == NULL) return FALSE;
01073 
01074     // Find the selected range of objects
01075     SelRange* pSelRange = GetApplication()->FindSelection();
01076     Node* pNode = pSelRange->FindFirst();
01077 
01078     BOOL BlobFound = FALSE;
01079 
01080     // Scan the selection for NodePath objects
01081     while (pNode != NULL && !BlobFound)
01082     {
01083         if (pNode->GetRuntimeClass() == CC_RUNTIME_CLASS(NodeBlend))
01084         {
01085             NodeBlend* pNodeBlend = (NodeBlend*)pNode;
01086 
01087             if (pNodeBlend->GetUnionBlobBoundingRect().ContainsCoord(*pPointerPos))
01088             {
01089                 BlobFound = pNodeBlend->IsPointOverBlob(pPointerPos,
01090                                                         &(pRef->pBlendPath),
01091                                                         &(pRef->Index),
01092                                                         &(pRef->AStartNode),
01093                                                         &(pRef->RemapRef));
01094 
01095                 if (BlobFound)
01096                 {
01097                     pRef->pNode      = pNodeBlend;
01098                     pRef->pNodeBlend = pNodeBlend;
01099 //                  *pPointerPos = pRef->pBlendPath->GetPathCoord(pRef->Index);
01100                 }
01101             }
01102         }
01103 
01104         // Now find the next selected node
01105         pNode = pSelRange->FindNext(pNode);
01106     }
01107 
01108     return BlobFound;
01109 }
01110 
01111 /********************************************************************************************
01112 
01113 >   void ContourTool::CheckNodeRemapping(BlendToolRef* pRefStart, BlendToolRef* pRefEnd)
01114 
01115     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
01116     Created:    6/12/94
01117     Inputs:     pRefStart = ptr to start ref
01118                 pRefEnd   = ptr to end ref
01119     Outputs:    Potentailly updates pRefStart and pRefEnd, so that if they represent a remapping of nodes,
01120                 they have the same remap reference
01121     Returns:    -
01122     Purpose:    This checks to see if the two references actually represent a node remapping within a blend.
01123                 In order to cope with multi-stage blends, both references have to be looked at the same time.
01124 
01125                 This is not the neatest way of doing it, but it's the quickest and safest method, given that
01126                 it has to work for the gamma release it a few days time.
01127 
01128 ********************************************************************************************/
01129 
01130 void ContourTool::CheckNodeRemapping(BlendToolRef* pRefStart, BlendToolRef* pRefEnd)
01131 {
01132     ERROR3IF(pRefStart == NULL,"pRefStart == NULL");
01133     ERROR3IF(pRefEnd   == NULL,"pRefEnd == NULL");
01134 }
01135 
01136 /********************************************************************************************
01137 >   NodeRenderableInk* ContourTool::FindObject(Spread* pSpread, DocCoord PointerPos)
01138 
01139     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
01140     Created:    7/10/94
01141     Inputs:     pSpread  = ptr to spread click occurred
01142                 PointerPos = the DocCoord of the click
01143     Outputs:    -
01144     Returns:    -
01145     Purpose:    Looks for a (possibly grouped) node(s) in the given spread at the
01146                 given mouse-click position.  Convenient shorthand for the hit-testing
01147                 functions.
01148     Errors:     -
01149     SeeAlso:    -
01150 ********************************************************************************************/
01151 
01152 NodeRenderableInk* ContourTool::FindObject(Spread* pSpread, DocCoord PointerPos)
01153 {
01154     return NodeRenderableInk::FindCompoundAtPoint(pSpread,PointerPos,NULL);
01155 }
01156 
01157 /********************************************************************************************
01158 
01159 >   void ContourTool::RenderToolBlobs(Spread* pSpread,DocRect* pDocRect)
01160 
01161     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
01162     Created:    28/07/2000
01163     Inputs:     pSpread     ptr to a spread.
01164                 pDocRect    ptr to DocRect of spread to render in.
01165     Returns:    -
01166     Purpose:    Handles the RenderToolBlobs method.
01167                 Renders the tool's blobs into the current doc view.
01168 
01169                 In case you were wondering, this is how contour blobs are arranged:
01170 
01171                     2   4   3
01172 
01173                     6   *   7
01174 
01175                     0   5   1
01176     SeeAlso:    
01177 
01178 ********************************************************************************************/
01179 
01180 void ContourTool::RenderToolBlobs(Spread* pSpread,DocRect* pDocRect)
01181 {
01182 //  BlobManager * pBlobMgr = GetApplication()->GetBlobManager();
01183 
01184     if (m_bDisableBlobRenderingFlag)
01185         return;
01186 
01187     // check the blobs to see if they're valid
01188     BOOL bZeroBlobs = TRUE;
01189 
01190     for (UINT32 i = 0 ; i < 8; i++)
01191     {
01192         if (m_BlobPoints[i].x != 0 ||
01193             m_BlobPoints[i].y != 0)
01194         {
01195             bZeroBlobs = FALSE;
01196             break;
01197         }
01198     }
01199 
01200     if (bZeroBlobs)
01201         return;
01202     
01203     DocView* pDocView = DocView::GetCurrent();
01204 
01205     // Get a render region on the spread.  We need to render EORd stuff on top of the current view
01206     RenderRegion* pRender = pDocView->RenderOnTop(pDocRect,pSpread,UnclippedEOR);
01207 
01208     while (pRender != NULL)
01209     {
01210         pRender->SaveContext();
01211 
01212         // left and right.
01213         pRender->DrawBitmapBlob(m_BlobPoints[6], _R(IDBMP_CONTOUR_LEFTRIGHT));
01214         pRender->DrawBitmapBlob(m_BlobPoints[7], _R(IDBMP_CONTOUR_LEFTRIGHT));
01215 
01216         // up and down.
01217         pRender->DrawBitmapBlob(m_BlobPoints[4], _R(IDBMP_CONTOUR_UPDOWN));
01218         pRender->DrawBitmapBlob(m_BlobPoints[5], _R(IDBMP_CONTOUR_UPDOWN));
01219 
01220         // acute accent (bl to tr).
01221         pRender->DrawBitmapBlob(m_BlobPoints[0], _R(IDBMP_CONTOUR_ACUTE));
01222         pRender->DrawBitmapBlob(m_BlobPoints[3], _R(IDBMP_CONTOUR_ACUTE));
01223 
01224         // grave accent (tl to br).
01225         pRender->DrawBitmapBlob(m_BlobPoints[2], _R(IDBMP_CONTOUR_GRAVE));
01226         pRender->DrawBitmapBlob(m_BlobPoints[1], _R(IDBMP_CONTOUR_GRAVE));
01227 
01228         pRender->RestoreContext();
01229 
01230         // get the next render region
01231         pRender = pDocView->GetNextOnTop(pDocRect);
01232     }
01233 }
01234 
01235 /********************************************************************************************
01236 
01237 >   static void ContourTool::DisplayStatusBarHelp(UINT32 StatusID)
01238 
01239     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
01240     Created:    3/10/94
01241     Inputs:     StatusID = ID of status help string
01242     Outputs:    -
01243     Returns:    -
01244     Purpose:    Displays the given status help string in the status bar
01245     SeeAlso:    -
01246 
01247 ********************************************************************************************/
01248 
01249 void ContourTool::DisplayStatusBarHelp(UINT32 StatusIDX)
01250 {
01251     String_256 StatusMsg("");
01252     StatusMsg.Load(StatusIDX);
01253     GetApplication()->UpdateStatusBarText(&StatusMsg);
01254     ContourTool::StatusID = StatusIDX;
01255 }
01256 
01257 /********************************************************************************************
01258 
01259 >   static BOOL ContourTool::IsCurrent()
01260 
01261     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
01262     Created:    4/11/94
01263     Inputs:     -
01264     Outputs:    -
01265     Returns:    TRUE if the blend tool is the current tool
01266     Purpose:    Is the blend tool the current one? Call this to find out.
01267     SeeAlso:    -
01268 
01269 ********************************************************************************************/
01270 
01271 BOOL ContourTool::IsCurrent()
01272 {
01273     return (Tool::GetCurrentID() == TOOLID_CONTOURTOOL);
01274 }
01275 
01276 /********************************************************************************************
01277 
01278 >   static INT32 ContourTool::CalculateContourWidth(DocRect &br, DocCoord &PointerPos)
01279 
01280     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01281     Created:    11/9/99
01282     Inputs:     The doc rect to calculate the width from, and the pointer position
01283     Outputs:    -
01284     Returns:    The width of the contour
01285     Purpose:    Calculates the contour width for a given rect
01286     SeeAlso:    -
01287 
01288 ********************************************************************************************/
01289 INT32 ContourTool::CalculateContourWidth(DocRect &br, DocCoord &PointerPos)
01290 {
01291     DocCoord Centre = br.Centre();
01292 
01293     INT32 Width = 0;
01294 
01295     INT32 OffsetX = 0;
01296     INT32 OffsetY = 0;
01297 
01298     double dOffsetX = 0;
01299     double dOffsetY = 0;
01300 
01301     // try to work out the width of the contour
01302     // first, are we dragging inside or outside the bounding rect ?
01303     if (!br.ContainsCoord(PointerPos))
01304     {
01305         // outside the bounding rect
01306         if (PointerPos.x < br.lo.x && PointerPos.y >= br.lo.y &&
01307             PointerPos.y <= br.hi.y)
01308         {
01309             Width = br.lo.x - PointerPos.x;
01310         }
01311         else if (PointerPos.x > br.hi.x && PointerPos.y >= br.lo.y &&
01312             PointerPos.y <= br.hi.y)
01313         {
01314             Width = PointerPos.x - br.hi.x;
01315         }
01316         else if (PointerPos.x >= br.lo.x && PointerPos.x <= br.hi.x &&
01317             PointerPos.y < br.lo.y)
01318         {
01319             Width = br.lo.y - PointerPos.y;
01320         }
01321         else if (PointerPos.x >= br.lo.x && PointerPos.x <= br.hi.x &&
01322             PointerPos.y > br.hi.y)
01323         {
01324             Width = PointerPos.y - br.hi.y;
01325         }
01326         else if (PointerPos.x < br.lo.x && PointerPos.y < br.lo.y)
01327         {
01328             OffsetX = br.lo.x - PointerPos.x;
01329             OffsetY = br.lo.y - PointerPos.y;
01330             
01331             if (OffsetX > OffsetY)
01332             {
01333                 Width = OffsetX;
01334             }
01335             else
01336             {
01337                 Width = OffsetY;
01338             }
01339         }
01340         else if (PointerPos.x > br.hi.x && PointerPos.y < br.lo.y)
01341         {
01342             OffsetX = PointerPos.x - br.hi.x ;
01343             OffsetY = br.lo.y - PointerPos.y;
01344             
01345             if (OffsetX > OffsetY)
01346             {
01347                 Width = OffsetX;
01348             }
01349             else
01350             {
01351                 Width = OffsetY;
01352             }
01353         }
01354         else if (PointerPos.x > br.hi.x && PointerPos.y > br.hi.y)
01355         {
01356             OffsetX = PointerPos.x - br.hi.x ;
01357             OffsetY = PointerPos.y - br.hi.y;
01358             
01359             if (OffsetX > OffsetY)
01360             {
01361                 Width = OffsetX;
01362             }
01363             else
01364             {
01365                 Width = OffsetY;
01366             }
01367         }
01368         else if (PointerPos.x < br.lo.x && PointerPos.y > br.hi.y)
01369         {
01370             OffsetX = br.lo.x - PointerPos.x ;
01371             OffsetY = PointerPos.y - br.hi.y;
01372             
01373             if (OffsetX > OffsetY)
01374             {
01375                 Width = OffsetX;
01376             }
01377             else
01378             {
01379                 Width = OffsetY;
01380             }
01381         }
01382         
01383         Width = -Width;
01384     }
01385     else
01386     {
01387         // inside the bounding rect then
01388 
01389         // work out which quadrant to access
01390         dOffsetX = ((double)(PointerPos.x - Centre.x));
01391         dOffsetY = ((double)(PointerPos.y - Centre.y));
01392 
01393         INT32 RWidth = br.Width()/2;
01394         INT32 RHeight = br.Height()/2;
01395 
01396         if (RWidth > RHeight)
01397         {
01398             INT32 DistX = RWidth - RHeight;
01399 
01400             if (PointerPos.x < Centre.x + DistX &&
01401                 PointerPos.x > Centre.x - DistX)
01402             {
01403                 if (dOffsetY > 0)
01404                 {
01405                     Width = br.hi.y - PointerPos.y;
01406                 }
01407                 else
01408                 {
01409                     Width = PointerPos.y - br.lo.y;
01410                 }
01411             }
01412             else if (PointerPos.x > Centre.x + DistX)
01413             {
01414                 dOffsetX -= (double)DistX;
01415 
01416                 if (dOffsetY > 0)
01417                 {
01418                     if (dOffsetX > dOffsetY)
01419                     {
01420                         Width = br.hi.x - PointerPos.x;
01421                     }
01422                     else
01423                     {
01424                         Width = br.hi.y - PointerPos.y;
01425                     }
01426                 }
01427                 else
01428                 {
01429                     if (dOffsetX > -dOffsetY)
01430                     {
01431                         Width = br.hi.x - PointerPos.x;
01432                     }
01433                     else
01434                     {
01435                         Width = PointerPos.y - br.lo.y;
01436                     }
01437                 }
01438             }
01439             else
01440             {
01441                 dOffsetX += (double)DistX;
01442                 dOffsetX = -dOffsetX;
01443 
01444                 if (dOffsetY > 0)
01445                 {
01446                     if (dOffsetX > dOffsetY)
01447                     {
01448                         Width = PointerPos.x - br.lo.x;
01449                     }
01450                     else
01451                     {
01452                         Width = br.hi.y - PointerPos.y;
01453                     }
01454                 }
01455                 else
01456                 {
01457                     if (dOffsetX > -dOffsetY)
01458                     {
01459                         Width = PointerPos.x - br.lo.x;
01460                     }
01461                     else
01462                     {
01463                         Width = PointerPos.y - br.lo.y;
01464                     }
01465                 }
01466             }
01467         }
01468         else
01469         {
01470             INT32 DistY = RHeight - RWidth;
01471 
01472             if (PointerPos.y < Centre.y + DistY &&
01473                 PointerPos.y > Centre.y - DistY)
01474             {
01475                 if (dOffsetX > 0)
01476                 {
01477                     Width = br.hi.x - PointerPos.x;
01478                 }
01479                 else
01480                 {
01481                     Width = PointerPos.x - br.lo.x;
01482                 }
01483             }
01484             else if (PointerPos.y > Centre.y + DistY)
01485             {
01486                 dOffsetY -= (double)DistY;
01487 
01488                 if (dOffsetX > 0)
01489                 {
01490                     if (dOffsetY > dOffsetX)
01491                     {
01492                         Width = br.hi.y - PointerPos.y;
01493                     }
01494                     else
01495                     {
01496                         Width = br.hi.x - PointerPos.x;
01497                     }
01498                 }
01499                 else
01500                 {
01501                     if (dOffsetY > -dOffsetX)
01502                     {
01503                         Width = br.hi.y - PointerPos.y;
01504                     }
01505                     else
01506                     {
01507                         Width = PointerPos.x - br.lo.x;
01508                     }
01509                 }
01510             }
01511             else
01512             {
01513                 dOffsetY += (double)DistY;
01514                 dOffsetY = -dOffsetY;
01515 
01516                 if (dOffsetX > 0)
01517                 {
01518                     if (dOffsetY > dOffsetX)
01519                     {
01520                         Width = PointerPos.y - br.lo.y;
01521                     }
01522                     else
01523                     {
01524                         Width = br.hi.x - PointerPos.x;
01525                     }
01526                 }
01527                 else
01528                 {
01529                     if (dOffsetY > -dOffsetX)
01530                     {
01531                         Width = PointerPos.y - br.lo.y;
01532                     }
01533                     else
01534                     {
01535                         Width = PointerPos.x - br.lo.x;
01536                     }
01537                 }
01538             }
01539         }
01540     }
01541 
01542     // check for max's and min's
01543     if (Width > 0)
01544     {
01545         if (Width > CONTOUR_DEPTH_MAX)
01546             Width = CONTOUR_DEPTH_MAX;
01547 
01548         if (Width < CONTOUR_DEPTH_MIN)
01549             Width = CONTOUR_DEPTH_MIN;
01550     }
01551     else
01552     {
01553         if (Width < -CONTOUR_DEPTH_MAX)
01554             Width = -CONTOUR_DEPTH_MAX;
01555 
01556         if (Width > -CONTOUR_DEPTH_MIN)
01557             Width = -CONTOUR_DEPTH_MIN;
01558     }
01559     
01560     return Width;
01561 }
01562 
01563 
01564 
01565 
01566 /********************************************************************************************
01567 
01568 >   static INT32 ContourTool::CalculateContourWidth(Node * pNode, DocCoord &PointerPos)
01569 
01570     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01571     Created:    11/9/99
01572     Inputs:     -
01573     Outputs:    -
01574     Returns:    The width of the contour
01575     Purpose:    Calculates the contour width for a given node given a pointer position
01576     SeeAlso:    -
01577 
01578 ********************************************************************************************/
01579 
01580 INT32 ContourTool::CalculateContourWidth(Node * pNode, DocCoord &PointerPos)
01581 {
01582     // get the bounding rect of the node to be contoured
01583     DocRect br = ((NodeRenderableBounded *)pNode)->GetBoundingRect();
01584 
01585     Node * pParent = pNode;
01586 
01587     BOOL bContourExists = FALSE;
01588 
01589     while (pParent && !bContourExists)
01590     {
01591         if (pParent->IsKindOf(CC_RUNTIME_CLASS(NodeContourController)))
01592         {
01593             pNode = pParent;
01594             bContourExists = TRUE;
01595 
01596             br = ((NodeContourController *)pNode)->GetInsideBoundingRect();
01597         }
01598 
01599         pParent = pParent->FindParent();
01600     }
01601 
01602     return CalculateContourWidth(br, PointerPos);
01603 
01604     
01605 }
01606 
01607 //----------------------------------------------
01608 //----------------------------------------------
01609 //----------------------------------------------
01610 //----------------------------------------------
01611 BOOL ContourInfoBarOp::CheckForBevels()
01612 {
01613     List BevelList;
01614     BevelTools::BuildListOfSelectedNodes(&BevelList, CC_RUNTIME_CLASS(NodeBevelController));
01615     
01616     if (!BevelList.IsEmpty())
01617     {
01618         BevelList.DeleteAll();
01619         return TRUE;
01620     }
01621 
01622     return FALSE;
01623 }
01624 
01625 /********************************************************************************************
01626 
01627 >   MsgResult ContourInfoBarOp::Message(Msg* Message) 
01628 
01629     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
01630     Created:    3/10/94  changed 13/9/99 Diccon Yamanaka
01631     Inputs:     Message = The message to handle
01632     Outputs:    -
01633     Returns:    -
01634     Purpose:    Blend info bar dialog message handler
01635     Errors:     -
01636     SeeAlso:    -
01637 
01638 ********************************************************************************************/
01639 
01640 MsgResult ContourInfoBarOp::Message(Msg* Message) 
01641 {
01642     CProfileBiasGain Profile;
01643 //  BOOL bMany = FALSE;
01644     UINT32 Width = 0;
01645     INT32 LWidth = 0;
01646     
01647     if (IS_OUR_DIALOG_MSG(Message))
01648     {
01649         DialogMsg* Msg = (DialogMsg*)Message;
01650 
01651         switch (Msg->DlgMsg)
01652         {
01653 
01654             case DIM_CREATE:
01655                 m_BetweenViews = FALSE;
01656                 // Initialise the infobar controls here
01657                 // This is sent when you create the infobar in your tool startup code
01658 //              m_BiasGainObjectGadget.LinkControlButton ( this, _R(IDC_CONTOUROBJECTBIASGAIN), _R(IDBBL_CONTOUROBJECTBIASGAIN), _R(IDS_CONTOUROBJECTBIASGAIN) );
01659 //              m_BiasGainAttrGadget.LinkControlButton ( this, _R(IDC_CONTOURATTRBIASGAIN), _R(IDBBL_CONTOURATTRBIASGAIN), _R(IDS_CONTOURATTRBIASGAIN) );
01660                 m_BiasGainObjectGadget.Init(this, _R(IDC_CONTOUROBJECTBIASGAIN), _R(IDBBL_CONTOUROBJECTBIASGAIN), _R(IDS_CONTOUROBJECTBIASGAIN));
01661                 m_BiasGainAttrGadget.Init(this, _R(IDC_CONTOURATTRBIASGAIN), _R(IDBBL_CONTOURATTRBIASGAIN), _R(IDS_CONTOURATTRBIASGAIN));
01662                 m_BiasGainAttrGadget.ToggleFillProfile ();
01663                 
01664                 DisallowInteractiveProfiles ();
01665                 
01666                 SetGadgetHelp(_R(IDC_BTN_CONTOURSTEPS), _R(IDBBL_CONTOURSTEPSEDIT), _R(IDS_CONTOURSTEPSEDIT));
01667 
01668                 // set the slider bitmap
01669                 SetGadgetBitmaps(_R(IDC_CONTOURSLIDER), _R(IDB_SLIDERBASE), _R(IDB_SLIDERSLIDER));
01670                 
01671                 // these two buttons different buttons for their selected and 
01672                 // SELECTED states, set them here.
01673                 UpdateState();
01674                 break;
01675 
01676             case DIM_LFT_BN_CLICKED:
01677             {
01678                 if (Msg->GadgetID == _R(IDC_CONTOUROBJECTBIASGAIN))
01679                     HandleProfileButtonClick (m_BiasGainObjectGadget, _R(IDC_CONTOUROBJECTBIASGAIN));
01680                 else if (Msg->GadgetID == _R(IDC_CONTOURATTRBIASGAIN))
01681                     HandleProfileButtonClick (m_BiasGainAttrGadget, _R(IDC_CONTOURATTRBIASGAIN));
01682                 else if (Msg->GadgetID == _R(IDC_REMOVECONTOUR))
01683                 {
01684                     OpDescriptor* pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_REMOVECONTOUR);
01685                     if (pOpDesc != NULL) pOpDesc->Invoke();
01686                 }
01687                 /* DY 13/9/99 buttons to select whether blend steps or blend distance
01688                     is to be edited in the edit box */
01689                 else if (Msg->GadgetID == _R(IDC_BTN_CONTOURDISTANCE))
01690                 {
01691                     // set the steps control    
01692                     SetLongGadgetValue(_R(IDC_BTN_CONTOURSTEPS), FALSE);
01693                     m_EditBlendSteps = FALSE;
01694                     UpdateState();      // tell the infobar something has changed
01695                 }
01696                 else if (Msg->GadgetID == _R(IDC_BTN_CONTOURSTEPS))
01697                 {   
01698                     // set the distance control
01699                     SetLongGadgetValue(_R(IDC_BTN_CONTOURDISTANCE), FALSE);
01700                     m_EditBlendSteps = TRUE;
01701                     UpdateState();  
01702                 }
01703                 else if (Msg->GadgetID == _R(IDC_BTN_CONTOUROUTER))
01704                 {
01705                     // deal with the button click
01706                     DealWithContourOuterClick();
01707                 }
01708                 else if (Msg->GadgetID == _R(IDC_BTN_CONTOURINNER))
01709                 {
01710                     DealWithContourInnerClick();
01711                 }
01712                 else if (Msg->GadgetID == _R(IDC_BTN_INSETPATH))
01713                 {
01714                     DealWithInsetPathClick();
01715                 }
01716                 else if (Msg->GadgetID == _R(IDC_CONTOURJOINTYPEMITRE))
01717                 {
01718                     if (Msg->DlgMsg == DIM_LFT_BN_CLICKED)
01719                     {
01720                         HandleJoinTypeMitreClicked();
01721                     }
01722                 }
01723                 else if (Msg->GadgetID == _R(IDC_CONTOURJOINTYPEROUND))
01724                 {
01725                     if (Msg->DlgMsg == DIM_LFT_BN_CLICKED)
01726                     {
01727                         HandleJoinTypeRoundClicked();
01728                     }
01729                 }
01730                 else if (Msg->GadgetID == _R(IDC_CONTOURJOINTYPEBEVEL))
01731                 {
01732                     if (Msg->DlgMsg == DIM_LFT_BN_CLICKED)
01733                     {
01734                         HandleJoinTypeBevelClicked();
01735                     }
01736                 }
01737             }
01738             break;
01739 
01740             case DIM_SLIDER_POS_SET:
01741             {
01742                 if (Msg->GadgetID == _R(IDC_CONTOURSLIDER))
01743                 {
01744                     // slider has been dropped therefore invoke the op
01745                     Width = GetSliderLogValue();
01746 
01747                     // get rid of the drag blobs
01748                     ContourTool::m_pOpContourNodes->RenderDragBlobs(m_LastDragWidth, 
01749                             Document::GetSelectedSpread(), TRUE);   
01750                     
01751                     m_bDragging = FALSE;
01752 
01753                     DealWithWidthChange(Width);
01754                 }
01755             }
01756             break;
01757             
01758             case DIM_SLIDER_POS_CHANGING:
01759 
01760                 // deal with the slider being dragged about
01761                 if (Msg->GadgetID == _R(IDC_CONTOURSLIDER))
01762                 {
01763                     // slider changed !
01764                     // get the slider value & put it into the edit box
01765                     Width = GetSliderLogValue();
01766 
01767                     SetSliderValue(Width, FALSE);
01768 
01769                     if (ContourTool::m_pOpContourNodes)
01770                     {
01771                         if(m_bOuterIsSelected)
01772                             Width = -Width;
01773 
01774                         if (!m_bDragging)
01775                         {
01776                             // set up the drag information for the blob rendering
01777                             ContourTool::m_pOpContourNodes->SetupDragInfo();
01778                             m_bDragging = TRUE;
01779                         }
01780                         else
01781                         {
01782                             // render drag blobs off
01783                             ContourTool::m_pOpContourNodes->RenderDragBlobs(m_LastDragWidth,Document::GetSelectedSpread());
01784                         }
01785 
01786                         // render drag blobs with the new width
01787                         ContourTool::m_pOpContourNodes->RenderDragBlobs(Width,Document::GetSelectedSpread());
01788 
01789                         // set the last drag width
01790                         m_LastDragWidth = Width;
01791                     }
01792                 }
01793                 break;
01794             
01795             case DIM_SELECTION_CHANGED:
01796             {
01797                 if (FALSE) {}
01798                 else if (Msg->GadgetID == _R(IDC_CONTOURSTEPS))
01799                 {
01800                     BOOL Valid = TRUE;
01801 
01802                     // get the correct number of steps depending on the buttons which are
01803                     // activated (either step width or number of steps)
01804                     if (GetBoolGadgetSelected(_R(IDC_BTN_CONTOURSTEPS)))
01805                     {                       
01806                         INT32 NumSteps = GetLongGadgetValue(_R(IDC_CONTOURSTEPS),0,999,_R(IDS_BLENDSTEPS_INVALID),&Valid);
01807 
01808                         if (NumSteps <= 0)
01809                             NumSteps = 1;
01810 
01811                         NumSteps --;
01812 
01813                         if (Valid)
01814                         {
01815                             OpDescriptor* pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_CHANGECONTOURSTEPS);
01816                             if (pOpDesc != NULL)
01817                             {
01818                                 OpParam Param(NumSteps,0);
01819                                 pOpDesc->Invoke(&Param);
01820                             }
01821                         }
01822                     }
01823                     else if (GetBoolGadgetSelected(_R(IDC_BTN_CONTOURDISTANCE)))
01824                     {
01825                         // convert to units
01826                         INT32 Dist = 0;
01827                         Valid = ConvertStringToValue(_R(IDC_CONTOURSTEPS), Dist);
01828 
01829                         // Make sure the value is suitable, ie. greater than a pixel or 750 MILLIPOINTS
01830                         if(Dist <= 0)
01831                         {
01832                             InformWarning(_R(IDS_CONTOUR_DISTANCE_WARNING));
01833                         }
01834                         else if(Valid)
01835                         {
01836                             OpDescriptor* pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_CHANGECONTOURSTEPDISTANCE);
01837                             if (pOpDesc != NULL)
01838                             {
01839                                 OpParam Param(Dist,0);
01840                                 pOpDesc->Invoke(&Param);
01841                             }
01842                         }               
01843                     }
01844                 }
01845                 else if (Msg->GadgetID == _R(IDC_EFFECT))
01846                 {
01847                     WORD Index;
01848                     GetValueIndex(_R(IDC_EFFECT),&Index); 
01849                     ColourBlendType ColBlendType = (ColourBlendType)Index;
01850 
01851                     if (ColBlendType <= COLOURBLEND_ALTRAINBOW)
01852                     {
01853                         OpDescriptor* pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_CHANGECONTOURCOLOURTYPE);
01854                         if (pOpDesc != NULL)
01855                         {
01856                             OpParam Param;
01857                             Param.Param1 = (UINT32)ColBlendType;
01858                             pOpDesc->Invoke(&Param);
01859                         }
01860                     }
01861                 }
01862                 else if (Msg->GadgetID == _R(IDC_CONTOURSLIDEREDIT))
01863                 {
01864                     // get the value from the editable control
01865                     BOOL Valid = ConvertStringToValue(_R(IDC_CONTOURSLIDEREDIT), LWidth);
01866 
01867                     // make sure that the specified contour width is a valid value.
01868                     // Make the lowest value a pixel or 750 MILLIPOINTs
01869                     if(LWidth <= 0)
01870                     {
01871                         InformWarning(_R(IDS_CONTOUR_WIDTH_WARNING));
01872                     }
01873                     else if(Valid)
01874                     {
01875                         // ok, deal with this change
01876                         if (LWidth > 0)
01877                         {
01878                             Width = (UINT32)LWidth;
01879 
01880                             DealWithWidthChange(Width);
01881                         }
01882                     }
01883                 }
01884             }
01885             break;
01886 
01887             default:
01888                 break;
01889 
01890         }
01891 
01892         if (( Msg->GadgetID == _R(IDC_CONTOUROBJECTBIASGAIN) ) || ( Msg->GadgetID == _R(IDC_CONTOURATTRBIASGAIN) ))
01893         {  
01894             ProfileSelectionChange( Msg, Msg->GadgetID );
01895         }
01896     }
01897     else if (MESSAGE_IS_A(Message, SelChangingMsg))
01898     {
01899         UpdateState();
01900         Spread* pSelected = Document::GetSelectedSpread();
01901 
01902         // render the tool blobs off
01903         if (m_pTool && pSelected && ContourTool::IsCurrent() && !m_BetweenViews)
01904         {
01905             BlobManager* BlobMgr = GetApplication()->GetBlobManager();
01906             ENSURE(BlobMgr, "Can't get BlobManager");
01907             BlobMgr->RenderToolBlobsOff(m_pTool, pSelected, NULL);
01908             m_pTool->SetupToolBlobs();
01909             BlobMgr->RenderToolBlobsOn(m_pTool, pSelected, NULL);
01910 
01911             TRACEUSER( "MarkH", _T("Selected Spread = 0x%x\n"),pSelected);
01912         
01913             HandleProfileSelChangingMsg (m_BiasGainObjectGadget, _R(IDC_CONTOUROBJECTBIASGAIN));
01914             HandleProfileSelChangingMsg (m_BiasGainAttrGadget, _R(IDC_CONTOURATTRBIASGAIN));
01915         }
01916     }
01917     else if (MESSAGE_IS_A(Message, DocViewMsg))
01918     {
01919         DocViewMsg* msg = (DocViewMsg*) Message;
01920 
01921         // Render the tool blobs off the old view just before it changes
01922         if ((msg->State == DocViewMsg::SELABOUTTOCHANGE || msg->State == DocViewMsg::SELCHANGED ) 
01923             && msg->pOldDocView && m_pTool && ContourTool::IsCurrent() && !GetApplication()->CamelotIsDying())
01924         {
01925             // Do additional checks here to avoid ensures!
01926             CCamView* pOilView = msg->pOldDocView->GetConnectionToOilView();
01927             BlobManager* BlobMgr = GetApplication()->GetBlobManager();
01928             ENSURE(BlobMgr, "Can't get BlobManager");
01929 
01930             PORTNOTE("other", "Removed IsScreenCamView test");
01931             if (pOilView!=NULL /*&& pOilView->IsScreenCamView()*/)
01932             {
01933                 // handle the view will/has changed msgs and do NOT process and selection changes msgs between times (sjk 22/11/00)
01934                 if (msg->State == DocViewMsg::SELABOUTTOCHANGE)
01935                 {
01936                     m_pTool->SetupToolBlobs();
01937                     BlobMgr->RenderToolBlobsOff(m_pTool, msg->pOldDocView->GetVisibleSpread(), NULL);
01938                     //TRACE( _T("View about to change\n"));
01939                     m_BetweenViews = TRUE;
01940                 }
01941                 else
01942                 {
01943                     m_pTool->SetupToolBlobs();
01944                     BlobMgr->RenderToolBlobsOn(m_pTool, msg->pOldDocView->GetVisibleSpread(), NULL);
01945                     //TRACE( _T("View has just changed\n"));
01946                     m_BetweenViews = FALSE;
01947                 }
01948             }
01949 
01950         }
01951     }
01952     else if (MESSAGE_IS_A(Message,OpMsg))   // Check for undo/redo
01953     {
01954         // update the gadgets
01955         UpdateState();
01956         BlobManager* BlobMgr = GetApplication()->GetBlobManager();
01957         ENSURE(BlobMgr, "Can't get BlobManager");
01958         
01959         if ( (((OpMsg*)Message)->MsgType == OpMsg::BEFORE_UNDO) ||
01960                                         (((OpMsg*)Message)->MsgType == OpMsg::BEFORE_REDO))
01961         {
01962             // render the tool blobs off
01963             if (m_pTool && Document::GetSelectedSpread() && ContourTool::IsCurrent())
01964             {
01965                 BlobMgr->RenderToolBlobsOff(m_pTool, Document::GetSelectedSpread(), NULL);
01966                 m_pTool->SetBlobRendering(FALSE);
01967             }
01968         }
01969         else if ( (((OpMsg*)Message)->MsgType == OpMsg::AFTER_UNDO) ||
01970                                         (((OpMsg*)Message)->MsgType == OpMsg::AFTER_REDO))
01971         {
01972             // render the tool blobs back on
01973             if (m_pTool && Document::GetSelectedSpread() && ContourTool::IsCurrent())
01974             {
01975                 m_pTool->SetBlobRendering(TRUE);
01976                 m_pTool->SetupToolBlobs();
01977                 BlobMgr->RenderToolBlobsOn(m_pTool, Document::GetSelectedSpread(), NULL);
01978             }
01979         }
01980     }
01981 
01982     // Pass the message on to the immediate base class
01983     return (InformationBarOp::Message(Message));
01984 }
01985 
01986 
01987 
01988 /********************************************************************************************
01989 
01990 >   void ContourInfoBarOp::HandleJoinTypeMitreClicked()
01991 
01992     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
01993     Created:    08 September 2000
01994 
01995     Outputs:    We ensure that all objects in the selection have a mitre join type.
01996 
01997     Purpose:    Handles the user clicking on the mitre join-type button on our infobar.
01998 
01999     Notes:      This method assumes that an object must have a join type;
02000                 ie  if none of the other join types apply to the selection,
02001                     then the whole selection must have this join type, so there
02002                     is no point in reapplying it in this case.
02003 
02004 ********************************************************************************************/
02005 void ContourInfoBarOp::HandleJoinTypeMitreClicked()
02006 {
02007     BOOL fRound = GetBoolGadgetSelected(_R(IDC_CONTOURJOINTYPEROUND));
02008     BOOL fBevel = GetBoolGadgetSelected(_R(IDC_CONTOURJOINTYPEBEVEL));
02009 
02010     // only bother changing to a mitre join type if any selected objects
02011     // use the other join types.
02012     if (fRound || fBevel)
02013         ChangeJoinType(MitreJoin);
02014     else
02015         SetBoolGadgetSelected(_R(IDC_CONTOURJOINTYPEMITRE), TRUE);
02016 }
02017 
02018 
02019 
02020 /********************************************************************************************
02021 
02022 >   void ContourInfoBarOp::HandleJoinTypeRoundClicked()
02023 
02024     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
02025     Created:    08 September 2000
02026 
02027     Outputs:    We ensure that all objects in the selection have a round join type.
02028 
02029     Purpose:    Handles the user clicking on the round join-type button on our infobar.
02030 
02031     Notes:      This method assumes that an object must have a join type;
02032                 ie  if none of the other join types apply to the selection,
02033                     then the whole selection must have this join type, so there
02034                     is no point in reapplying it in this case.
02035 
02036 ********************************************************************************************/
02037 void ContourInfoBarOp::HandleJoinTypeRoundClicked()
02038 {
02039     BOOL fMitre = GetBoolGadgetSelected(_R(IDC_CONTOURJOINTYPEMITRE));
02040     BOOL fBevel = GetBoolGadgetSelected(_R(IDC_CONTOURJOINTYPEBEVEL));
02041 
02042     // only bother changing to a mitre join type if any selected objects
02043     // use the other join types.
02044     if (fMitre || fBevel)
02045         ChangeJoinType(RoundJoin);
02046     else
02047         SetBoolGadgetSelected(_R(IDC_CONTOURJOINTYPEROUND), TRUE);
02048 }
02049 
02050 
02051 
02052 /********************************************************************************************
02053 
02054 >   void ContourInfoBarOp::HandleJoinTypeBevelClicked()
02055 
02056     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
02057     Created:    08 September 2000
02058 
02059     Outputs:    We ensure that all objects in the selection have a bevel join type.
02060 
02061     Purpose:    Handles the user clicking on the bevel join-type button on our infobar.
02062 
02063     Notes:      This method assumes that an object must have a join type;
02064                 ie  if none of the other join types apply to the selection,
02065                     then the whole selection must have this join type, so there
02066                     is no point in reapplying it in this case.
02067 
02068 ********************************************************************************************/
02069 void ContourInfoBarOp::HandleJoinTypeBevelClicked()
02070 {
02071     BOOL fRound = GetBoolGadgetSelected(_R(IDC_CONTOURJOINTYPEROUND));
02072     BOOL fMitre = GetBoolGadgetSelected(_R(IDC_CONTOURJOINTYPEMITRE));
02073 
02074     // only bother changing to a mitre join type if any selected objects
02075     // use the other join types.
02076     if (fRound || fMitre)
02077         ChangeJoinType(BevelledJoin);
02078     else
02079         SetBoolGadgetSelected(_R(IDC_CONTOURJOINTYPEBEVEL), TRUE);
02080 }
02081 
02082 
02083 
02084 /********************************************************************************************
02085 
02086 >   void ContourInfoBarOp::ChangeJoinType(JointType jt)
02087 
02088     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
02089     Created:    11 September 2000
02090     Inputs:     jt  the join type to apply to the selection.
02091 
02092     Outputs:    An attribute describing the given join type is applied to the selection.
02093 
02094     Purpose:    Apply the given join type to the selection.
02095 
02096     Errors:     Shows a warning if we run out of memory.
02097 
02098     See also:   AttributeManager::AttributeSelected().
02099 
02100 ********************************************************************************************/
02101 void ContourInfoBarOp::ChangeJoinType(JointType jt)
02102 {
02103     // Create the appropriate join type attribute.
02104     AttrJoinType* pJoinAttr = new AttrJoinType;
02105     if (pJoinAttr == NULL)
02106     {
02107         InformWarning(_R(IDS_UNDO_MEMORY_FAILURE), _R(IDS_OK));
02108         return;
02109     }
02110     pJoinAttr->Value.JoinType = jt;
02111 
02112     // Let the attribute manager apply it to the selection.
02113     AttributeManager::AttributeSelected(pJoinAttr);
02114 }
02115 
02116 
02117 
02118 /********************************************************************************************
02119 
02120 >   void ContourInfoBarOp::UpdateJoinTypeControls()
02121 
02122     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
02123     Created:    11 September 2000
02124 
02125     Outputs:    The join type buttons on the infobar are updated to reflect the join types
02126                 contained in the selection.
02127 
02128     Purpose:    Update our infobar join type buttons.
02129                 This method is currently called only in response to a SelChanging message.
02130 
02131 ********************************************************************************************/
02132 void ContourInfoBarOp::UpdateJoinTypeControls()
02133 {
02134 // DEBUG:
02135 //  TRACEUSER( "Karim", _T("ContourInfoBarOp::UpdateJoinTypeControls\n"));
02136 
02137     // the check for join types is possibly time-consuming, as we may need to scan the
02138     // selection if there are multiple join types in use on selected objects.
02139 
02140     // Determine if there is a common attribute.
02141     NodeAttribute* pAttr;
02142     SelRange::CommonAttribResult eResult;
02143     eResult = GetApplication()->
02144                  FindSelection()->
02145                      FindCommonAttribute(CC_RUNTIME_CLASS(AttrJoinType), &pAttr);
02146 
02147     // initialise all button-states to FALSE - they will be set to TRUE appropriately below.
02148     BOOL fMitre = FALSE;
02149     BOOL fRound = FALSE;
02150     BOOL fBevel = FALSE;
02151 
02152     switch (eResult)
02153     {
02154         // uncheck all buttons.
02155         case SelRange::ATTR_NONE:
02156             break;
02157 
02158         // only check the common join type.
02159         case SelRange::ATTR_COMMON:
02160             if (pAttr != NULL)
02161             {
02162                 fMitre = ( ((AttrJoinType*)pAttr)->Value.JoinType == MitreJoin );
02163                 fRound = ( ((AttrJoinType*)pAttr)->Value.JoinType == RoundJoin );
02164                 fBevel = ( ((AttrJoinType*)pAttr)->Value.JoinType == BevelledJoin );
02165             }
02166             break;
02167 
02168         // test the selection for all common line attr types.
02169         case SelRange::ATTR_MANY:
02170             {
02171                 // scan the selection, quitting as soon as all three states get set.
02172                 SelRange* pSel = GetApplication()->FindSelection();
02173                 for (Node*  pSelNode =  pSel->FindFirst();
02174                             pSelNode != NULL && !(fMitre && fRound && fBevel);
02175                             pSelNode =  pSel->FindNext(pSelNode))
02176                 {
02177                     if (!pSelNode->IsAnObject())
02178                         continue;
02179 
02180                     ((NodeRenderableInk*)pSelNode)->
02181                         FindAppliedAttribute(CC_RUNTIME_CLASS(AttrJoinType), &pAttr);
02182                     if (pAttr != NULL)
02183                     {
02184                         fMitre |= ( ((AttrJoinType*)pAttr)->Value.JoinType == MitreJoin );
02185                         fRound |= ( ((AttrJoinType*)pAttr)->Value.JoinType == RoundJoin );
02186                         fBevel |= ( ((AttrJoinType*)pAttr)->Value.JoinType == BevelledJoin );
02187                     }
02188                 }
02189             }
02190             break;
02191 
02192         // error - unrecognised return option from FindCommonAttribute().
02193         default:
02194             ERROR3("ContourInfoBarOp::UpdateJoinTypeControls; invalid result from FindCommonAttribute()");
02195             break;
02196     }
02197 
02198     SetBoolGadgetSelected(_R(IDC_CONTOURJOINTYPEMITRE), fMitre);
02199     SetBoolGadgetSelected(_R(IDC_CONTOURJOINTYPEROUND), fRound);
02200     SetBoolGadgetSelected(_R(IDC_CONTOURJOINTYPEBEVEL), fBevel);
02201 }
02202 
02203 
02204 
02205 /********************************************************************************************
02206 
02207 >   void ContourInfoBarOp::EnableDisableJoinTypeControls()
02208 
02209     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
02210     Created:    12 September 2000
02211     Purpose:    Enable/disable the join type buttons on the info bar,
02212                 according to the contents of the selection.
02213 
02214 ********************************************************************************************/
02215 void ContourInfoBarOp::EnableDisableJoinTypeControls()
02216 {
02217     // our join type buttons act only upon the selection.
02218     SelRange* pSel = GetApplication()->FindSelection();
02219     if (pSel == NULL || pSel->IsEmpty())
02220     {
02221         EnableGadget(_R(IDC_CONTOURJOINTYPEMITRE), FALSE);
02222         EnableGadget(_R(IDC_CONTOURJOINTYPEROUND), FALSE);
02223         EnableGadget(_R(IDC_CONTOURJOINTYPEBEVEL), FALSE);
02224     }
02225     else
02226     {
02227         EnableGadget(_R(IDC_CONTOURJOINTYPEMITRE), TRUE);
02228         EnableGadget(_R(IDC_CONTOURJOINTYPEROUND), TRUE);
02229         EnableGadget(_R(IDC_CONTOURJOINTYPEBEVEL), TRUE);
02230     }
02231 }
02232 
02233 
02234 
02235 /********************************************************************************************
02236 
02237 >   virtual CProfileBiasGain* GetProfileFromSelection(CGadgetID GadgetID, INT32* Index, BOOL* bAllSameType)
02238 
02239     Author:     Chris_Snook (Xara Group Ltd) <camelotdev@xara.com>
02240     Created:    19/1/2000
02241     Inputs:     The GadgetID of the CBiasGainGadget that we are dealing with.
02242     Outputs:    bMany - returned as TRUE if we have MANY profiles selected.
02243                 bAllSameType - returned as TRUE if objects within selection are all of the
02244                 same type.
02245     returns     Ptr to common CProfileBiasGain, or NULL if there is NOT one.
02246     Purpose:    See InformationBarOp::GetProfileFromSelection () for a description of this
02247                 function.
02248 
02249 *********************************************************************************************/
02250 
02251 CProfileBiasGain* ContourInfoBarOp::GetProfileFromSelection(CGadgetID GadgetID, BOOL* bMany, BOOL* bAllSameType)
02252 {
02253     BOOL ok = ((GadgetID == _R(IDC_CONTOUROBJECTBIASGAIN)) || (GadgetID == _R(IDC_CONTOURATTRBIASGAIN)));
02254 
02255     ERROR2IF(ok==FALSE, FALSE, "Invalid gadgetID passed");
02256 
02257     UINT32 TotalNumberSelected = (GetApplication()->FindSelection()->Count ());
02258 
02259     // get the list of all the shadows
02260     List ContourList;
02261     BevelTools::BuildListOfSelectedNodes(&ContourList, CC_RUNTIME_CLASS(NodeContourController));
02262 
02263     NodeContour* pFirstNodeCountour = NULL;
02264     CProfileBiasGain* pFirstProfile = NULL;
02265 
02266     /*if (ContourList.IsEmpty())
02267     {
02268         // the list is empty - er trick the code into closing the dialog
02269         *bAllSameType = FALSE;
02270         ContourList.DeleteAll();
02271         return (NULL);
02272     }*/
02273 
02274     if (TotalNumberSelected != (UINT32) ContourList.GetCount ())
02275     {
02276         // totals differ - so the user MUST have selected someother type of node as well
02277         *bAllSameType = FALSE;
02278         ContourList.DeleteAll();
02279         return (NULL);
02280     }
02281 
02282     NodeListItem * pItem = (NodeListItem *)ContourList.GetHead();
02283 
02284     // many flag
02285     *bMany = FALSE;
02286 
02287     while (pItem)
02288     {
02289         if (pFirstNodeCountour == NULL)
02290         {
02291             pFirstNodeCountour = (NodeContour*) pItem->pNode;
02292             
02293             if (GadgetID == _R(IDC_CONTOUROBJECTBIASGAIN))
02294             {
02295                 pFirstProfile = ((NodeContourController *)pItem->pNode)->GetObjectProfilePtr();
02296             }
02297             else if (GadgetID == _R(IDC_CONTOURATTRBIASGAIN))
02298             {
02299                 pFirstProfile = ((NodeContourController *)pItem->pNode)->GetAttrProfilePtr();
02300             }
02301         }
02302         else
02303         {
02304             if (GadgetID == _R(IDC_CONTOUROBJECTBIASGAIN))
02305             {
02306                 CProfileBiasGain* pOtherProfile = NULL;
02307 
02308                 pOtherProfile = ((NodeContourController *)pItem->pNode)->GetObjectProfilePtr();
02309 
02310                 if (pOtherProfile)
02311                 {
02312                     if (*pFirstProfile == *pOtherProfile)
02313                     {
02314                         // all ok
02315                     }
02316                     else
02317                     {
02318                         *bMany = TRUE;
02319                     }
02320                 }
02321             }
02322             else if (GadgetID == _R(IDC_CONTOURATTRBIASGAIN))
02323             {
02324                 CProfileBiasGain* pOtherProfile = NULL;
02325 
02326                 pOtherProfile = ((NodeContourController *)pItem->pNode)->GetAttrProfilePtr();
02327                 
02328                 if (pOtherProfile)
02329                 {
02330                     if (*pFirstProfile == *pOtherProfile)
02331                     {
02332                         // all ok
02333                     }
02334                     else
02335                     {
02336                         *bMany = TRUE;
02337                     }
02338                 }
02339             }
02340         }
02341 
02342         pItem = (NodeListItem *)ContourList.GetNext(pItem);
02343     }
02344 
02345     ContourList.DeleteAll();
02346     
02347     if (*bMany == TRUE)
02348     {
02349         return (NULL);
02350     }
02351     else
02352     {
02353         return (pFirstProfile);
02354     }
02355 }
02356 
02357 
02358 
02359 /********************************************************************************************
02360 
02361 >   void ContourInfoBarOp::ChangeProfile(CProfileBiasGain* Profile, CGadgetID GadgetID)
02362 
02363     Author:     Chris_Snook (Xara Group Ltd) <camelotdev@xara.com>
02364     Created:    19/1/2000
02365     Purpose:    See InformationBarOp::ChangeProfile () for an explanation of this function.
02366     See Also:   InformationBarOp::ChangeProfile ()
02367 
02368 *********************************************************************************************/
02369 
02370 void ContourInfoBarOp::ChangeProfile(CProfileBiasGain* Profile, CGadgetID GadgetID)
02371 {
02372     OpDescriptor* pOpDesc = NULL;
02373 
02374     BOOL FireOp = TRUE;
02375 
02376     // we only want to generate one bit of undo information - so decided whether
02377     // we have to fire the above op, or whether we just 'pump' the values into
02378     // our nodes (thereby nolonger generating infinite undo information) ....
02379 
02380     Operation* pLastOp = NULL;
02381 
02382     if (Profile->GetGeneratesInfiniteUndo ())   // only do if they didn't select a preset profile
02383     {
02384         pLastOp = Document::GetSelected()->GetOpHistory().FindLastOp();
02385     }
02386 
02387     if (GadgetID == _R(IDC_CONTOUROBJECTBIASGAIN))
02388     {
02389         if (pLastOp)
02390         {
02391             if (pLastOp->GetRuntimeClass() == CC_RUNTIME_CLASS(OpChangeContourObjectProfile))
02392             {
02393                 FireOp = FALSE;
02394             }
02395         }
02396             
02397         if (FireOp == TRUE)
02398         {
02399             pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_CHANGECONTOUROBJPROFILE);
02400         }
02401         else
02402         {   
02403             // we don't need/want any undo information - so just change the value ....
02404 
02405             ChangeContourObjectProfileAction Action;
02406             Action.ChangeObjectProfileWithNoUndo (*Profile);
02407         }
02408     }
02409     else if (GadgetID == _R(IDC_CONTOURATTRBIASGAIN))
02410     {
02411         if (pLastOp)
02412         {
02413             if (pLastOp->GetRuntimeClass() == CC_RUNTIME_CLASS(OpChangeContourAttributeProfile))
02414             {   
02415                 FireOp = FALSE;
02416             }
02417         }
02418 
02419         if (FireOp == TRUE)
02420         {
02421             pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_CHANGECONTOURATTRPROFILE);
02422         }
02423         else
02424         {
02425             ChangeContourAttributeProfileAction Action;
02426             Action.ChangeAttributeProfileWithNoUndo (*Profile);
02427         }
02428     }
02429     else        // some mistake surely !!!!
02430     {
02431         return;
02432     }
02433     
02434     if ((pOpDesc != NULL) && (FireOp == TRUE))
02435     {
02436         ChangeContourProfileParam Param (*Profile);
02437         pOpDesc->Invoke(&Param);
02438     }
02439 }
02440 
02441 
02442 
02443 /********************************************************************************************
02444 
02445 >   void ContourInfoBarOp::ChangeProfileOnIdle(CProfileBiasGain* Profile, CGadgetID GadgetID)
02446 
02447     Author:     Chris_Snook (Xara Group Ltd) <camelotdev@xara.com>
02448     Created:    9/8/2000
02449     Purpose:    See InformationBarOp::ChangeProfileOnIdle () for an explanation of this function.
02450     See Also:   InformationBarOp::ChangeProfileOnIdle ()
02451 
02452 *********************************************************************************************/
02453 
02454 void ContourInfoBarOp::ChangeProfileOnIdle(CProfileBiasGain* Profile, CGadgetID GadgetID)
02455 {
02456     if (GadgetID == _R(IDC_CONTOUROBJECTBIASGAIN))
02457     {
02458         // we don't need/want any undo information - so just change the value ....
02459         
02460         ChangeContourObjectProfileAction Action;
02461         Action.ChangeObjectProfileWithNoUndo (*Profile);
02462     }
02463     else if (GadgetID == _R(IDC_CONTOURATTRBIASGAIN))
02464     {
02465         // we don't need/want any undo information - so just change the value ....
02466         
02467         ChangeContourAttributeProfileAction Action;
02468         Action.ChangeAttributeProfileWithNoUndo (*Profile);
02469     }
02470     else
02471     {
02472         return;     // what planet are you on?
02473     }
02474 }
02475 
02476 
02477 
02478 /********************************************************************************************
02479 
02480 >   BOOL ContourInfoBarOp::SetSliderLogValue(MILLIPOINT Value)
02481 
02482     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com> Mc
02483     Created:    7/12/99
02484     Inputs:     The new width to set the slider to
02485     Outputs:    -
02486     Returns:    -
02487     Purpose:    Sets the position of the slider given the value
02488                 The slider is logarithmic - i.e. it uses the profile to set & retreive
02489                 its values
02490     Errors:     -
02491     SeeAlso:    -
02492 
02493 ********************************************************************************************/
02494 BOOL ContourInfoBarOp::SetSliderLogValue(MILLIPOINT Value)
02495 {
02496     // make the value appear between 0 and 1
02497     if (Value > CONTOUR_DEPTH_MAX)
02498         Value = CONTOUR_DEPTH_MAX;
02499 
02500     if (Value < CONTOUR_DEPTH_MIN)
02501         Value = CONTOUR_DEPTH_MIN;
02502 
02503     double dValue = Value - CONTOUR_DEPTH_MIN;
02504     dValue /= (double)(CONTOUR_DEPTH_MAX - CONTOUR_DEPTH_MIN);
02505 
02506     // change the profile
02507     double dSliderValue = SliderProfile.MapZeroToOne((AFp)(dValue));
02508 //  dSliderValue = 1.0 - dSliderValue;
02509         
02510     dSliderValue *= (double)CONTOUR_DEPTH_MAX;
02511     dSliderValue += CONTOUR_DEPTH_MIN;
02512 
02513     BOOL ok = SetGadgetRange(_R(IDC_CONTOURSLIDER), CONTOUR_DEPTH_MIN, CONTOUR_DEPTH_MAX);
02514 
02515     if (ok)
02516         ok = SetLongGadgetValue(_R(IDC_CONTOURSLIDER), (INT32)dSliderValue);
02517 
02518     return ok;
02519 }
02520 
02521 /********************************************************************************************
02522 
02523 >   MILLIPOINT ContourInfoBarOp::GetSliderLogValue()
02524 
02525     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com> Mc
02526     Created:    7/12/99
02527     Inputs:     -
02528     Outputs:    The slider width in millipoints
02529     Returns:    -
02530     Purpose:    Sets the position of the slider given the value
02531                 The slider is logarithmic - i.e. it uses the profile to set & retreive
02532                 its values
02533     Errors:     -
02534     SeeAlso:    -
02535 
02536 ********************************************************************************************/
02537 MILLIPOINT ContourInfoBarOp::GetSliderLogValue()
02538 {
02539     SetGadgetRange(_R(IDC_CONTOURSLIDER), CONTOUR_DEPTH_MIN, CONTOUR_DEPTH_MAX);
02540     MILLIPOINT Value = GetLongGadgetValue(_R(IDC_CONTOURSLIDER), CONTOUR_DEPTH_MIN,
02541         CONTOUR_DEPTH_MAX);
02542     
02543     // make the value appear between 0 and 1
02544     if (Value > CONTOUR_DEPTH_MAX)
02545         Value = CONTOUR_DEPTH_MAX;
02546 
02547     double dValue = Value - CONTOUR_DEPTH_MIN;
02548     dValue /= (double)(CONTOUR_DEPTH_MAX - CONTOUR_DEPTH_MIN);
02549 //  dValue = 1.0 - dValue;
02550 
02551     // change the profile
02552     SliderProfile.SetBias(-SliderProfile.GetBias());
02553     double dSliderValue = SliderProfile.MapZeroToOne((AFp)(dValue));
02554     SliderProfile.SetBias(-SliderProfile.GetBias());
02555         
02556     dSliderValue *= (double)(CONTOUR_DEPTH_MAX - CONTOUR_DEPTH_MIN);
02557     dSliderValue += CONTOUR_DEPTH_MIN;
02558 
02559     return (INT32)dSliderValue; 
02560 }
02561 
02562 
02563 /********************************************************************************************
02564 
02565 >   BOOL ContourInfoBarOp::ConvertValueToString(String_256 &In, const INT32 value, UnitType type, String_256 &unit)
02566 
02567     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
02568     Created:    09/12/96
02569     Inputs:     The value to convert to a string (In)
02570     Outputs:    -
02571     Returns:    -
02572     Purpose:    TRUE if the function was able to convert the INT32 to a string
02573 
02574 ********************************************************************************************/
02575 
02576 BOOL ContourInfoBarOp::ConvertValueToString(String_256 &In, const INT32 value, UnitType type)
02577 {
02578     BOOL Converted = FALSE; // True when a value has been converted
02579     
02580     Spread * CurrentSpread = Document::GetSelectedSpread();
02581 
02582     if (CurrentSpread)
02583     {
02584         // Get the dimension scaling object (units) associated with the given spread
02585         // and convert to its units.
02586         DimScale* pDimScale = DimScale::GetPtrDimScale((Node*) CurrentSpread);
02587         pDimScale->ConvertToUnits((INT32)value , &In);
02588         Converted = TRUE;
02589     }
02590 
02591     if (In.Length() == 0)
02592     {
02593         In = _T("0");
02594     }
02595 
02596     return Converted;
02597         
02598 }
02599 
02600 
02601 /********************************************************************************************
02602 
02603 >   BOOL ContourInfoBarOp::ConvertStringToValue(CGadgetID ID, INT32 &Value)
02604 
02605     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
02606     Created:    3/2/99
02607     Inputs:     The value to convert to a string (In)
02608     Outputs:    -
02609     Returns:    -
02610     Purpose:    TRUE if the function was able to convert the INT32 to a string
02611     See Also:   ShadowBarOp::ConvertStringToValue()
02612 
02613 ********************************************************************************************/
02614 
02615 BOOL ContourInfoBarOp::ConvertStringToValue(CGadgetID ID, INT32 &Value)
02616 {
02617     BOOL Valid = FALSE;
02618     
02619     DimScale *pDimScale = DimScale::GetPtrDimScale(Document::GetSelectedSpread());
02620     String_128 FieldContents = GetStringGadgetValue(ID, &Valid);
02621     Valid = pDimScale->ConvertToMillipoints(FieldContents, &Value);
02622 
02623     return Valid;
02624 }
02625 
02626 /********************************************************************************************
02627 
02628 >   void ContourInfoBarOp::UpdateState()
02629 
02630     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02631     Created:    4/11/94, changed 13/9/99 Diccon Yamanaka
02632     Purpose:    Overrides the empty UpdateState function provided by InformationBarOp
02633                 making a call to the function in DialogBarOp.
02634 
02635 *********************************************************************************************/
02636 
02637 void ContourInfoBarOp::UpdateState()
02638 {
02639     if(this == NULL)
02640         return;
02641 
02642     if(!this->HasWindow() || !m_bUpdateState || !ContourTool::IsCurrent())
02643         return ;
02644 
02645     DialogBarOp::UpdateState();     // This updates all controls that have OpDescs attached
02646 
02647 //  BOOL OneToOne = FALSE;
02648 //  BOOL Antialias = NodeBlend::GetDefaultAntialias();
02649     INT32 NumSteps = 0;
02650     UINT32 NumSelBlends = 0;
02651     ColourBlendType ColBlendType = COLOURBLEND_FADE;
02652     UINT32 StepDistance = 0;
02653     BOOL OnlyBlendsSelected         = TRUE;
02654 //  BOOL AllSelectedBlendsAreOnCurve= TRUE;
02655 //  BOOL AtLeastOneBlendIsOnCurve   = FALSE;
02656 //  BOOL Tangential                 = FALSE;
02657 //  BOOL EditSteps                  = TRUE;
02658 //  UINT32 NumBlendsOnCurve         = 0;
02659     CProfileBiasGain Profile;
02660 //  BOOL bMany                      = FALSE;
02661     UINT32 NumInsets = 0;
02662     UINT32 NumNonInsets = 0;
02663 
02664     // build a list of all contour nodes in the selection
02665     List ContourList;
02666     BevelTools::BuildListOfSelectedNodes(&ContourList, CC_RUNTIME_CLASS(NodeContourController),TRUE);
02667     NodeListItem * pItem = (NodeListItem *)ContourList.GetHead();
02668     Node* pNode = NULL;
02669     
02670     if (pItem)
02671         pNode = pItem->pNode;
02672 
02673     // booleans to say whether there are inner or outer (or both) contours in 
02674     // the selection
02675     BOOL bHasInner = FALSE;
02676     BOOL bHasOuter = TRUE;
02677     BOOL bManySteps = FALSE;
02678     BOOL bFirst = TRUE;
02679     UINT32 LastNumSteps = 0;
02680     BOOL bManyDistances = FALSE;
02681     UINT32 LastStepDistance = FALSE;
02682     UINT32 ThisWidth = 0;
02683     BOOL bManyWidths = FALSE;
02684     UINT32 ThisNumSteps = 0;
02685     UINT32 ThisStepDistance = 0;
02686 
02687     NodeContourController* pControl = NULL;
02688     Node* pParent = NULL;
02689 
02690     // We need to check for contours in Blends! If we have then we need to disable the Inset Path function!
02691     BOOL DoEnableInsetPaths = TRUE;
02692     
02693     // If there`s no contours in the list then we must also check to see if there any nodes selected
02694     // inside blends as the inset path function will crash if there is!
02695     if(pNode == NULL)
02696     {
02697         // Get the current selection
02698         SelRange* pSelection = GetApplication()->FindSelection();
02699         Node* pSelected = pSelection->FindFirst();
02700 
02701         while(pSelected && DoEnableInsetPaths)
02702         {
02703             pParent = pSelected->FindParent();
02704 
02705             while(pParent && pParent->IsCompound() && DoEnableInsetPaths)
02706             {
02707                 DoEnableInsetPaths = !pParent->IsABlend();
02708                 pParent = pParent->FindParent();
02709             }
02710 
02711             pSelected = pSelection->FindNext(pSelected);
02712         }
02713     }
02714 
02715     // run through the list, setting variables
02716     while (pNode != NULL)
02717     {
02718         if (pNode->IsAContourController())
02719         {
02720             pControl = (NodeContourController *)pNode;
02721 
02722             // Find out if this contour is sitting inside a blend
02723             if(DoEnableInsetPaths)
02724             {
02725                 pParent = pControl->FindParent();
02726                 while(pParent && pParent->IsCompound() && DoEnableInsetPaths)
02727                 {
02728                     DoEnableInsetPaths = !pParent->IsABlend();
02729                     pParent = pParent->FindParent();
02730                 }
02731             }
02732 
02733             NumSelBlends++;
02734             
02735             ThisNumSteps = pControl->GetNumberOfSteps();
02736             ColourBlendType ThisColBlendType = pControl->GetColourBlendType();
02737 
02738             if (pControl->GetInsetPathFlag())
02739                 NumInsets ++;
02740             else
02741                 NumNonInsets ++;
02742 
02743             ThisStepDistance = (UINT32)(abs(pControl->GetWidth()) / (pControl->GetNumberOfSteps() + 1));
02744 
02745             // check for many's
02746             if (bFirst)
02747             {
02748                 bFirst = FALSE;
02749                 LastNumSteps = ThisNumSteps;
02750                 LastStepDistance = ThisStepDistance;
02751                 ThisWidth = abs(pControl->GetWidth());
02752             }
02753             else
02754             {
02755                 if (LastNumSteps != ThisNumSteps)
02756                     bManySteps = TRUE;
02757 
02758                 if (LastStepDistance != ThisStepDistance)
02759                     bManyDistances = TRUE;
02760 
02761                 if (ThisWidth != (UINT32)abs(pControl->GetWidth()))
02762                     bManyWidths = TRUE;
02763             }
02764             
02765             NumSteps     = ThisNumSteps;
02766             ColBlendType = ThisColBlendType;
02767             StepDistance = ThisStepDistance;
02768             
02769             if (pControl->GetWidth() < 0)
02770                 bHasOuter = TRUE;
02771 
02772             if (pControl->GetWidth() > 0)
02773                 bHasInner = TRUE;
02774         }
02775         else
02776             OnlyBlendsSelected = FALSE;
02777         
02778         pItem = (NodeListItem *)ContourList.GetNext(pItem);
02779 
02780         if (pItem)
02781             pNode = pItem->pNode;
02782         else
02783             pNode = NULL;
02784     }
02785 
02786     ContourList.DeleteAll();
02787 
02788     BOOL HasBevels = CheckForBevels();
02789 
02790     if ((NumInsets == 0 || (NumInsets > 0 && NumNonInsets > 0) || (NumInsets == 0 && NumNonInsets == 0)) && !HasBevels)
02791     {
02792         EnableGadget(_R(IDC_BTN_CONTOURDISTANCE), NumSelBlends > 0);
02793         EnableGadget(_R(IDC_BTN_CONTOURSTEPS), NumSelBlends > 0);
02794         EnableGadget(_R(IDC_CONTOUROBJECTBIASGAIN), NumSelBlends > 0);
02795         EnableGadget(_R(IDC_CONTOURATTRBIASGAIN), NumSelBlends > 0);
02796         EnableGadget(_R(IDC_CONTOURSTEPS), NumSelBlends > 0);
02797 PORTNOTE("other", "_R(IDC_BTN_CONTOURANTIALIAS) removed as not in the resources")
02798 #ifndef EXCLUDE_FROM_XARALX
02799         EnableGadget(_R(IDC_BTN_CONTOURANTIALIAS),NumSelBlends > 0);
02800 #endif
02801         EnableGadget(_R(IDC_EFFECT), NumSelBlends > 0);
02802 
02803         if (NumSelBlends > 0)
02804         {
02805             EnableGadget(_R(IDC_BTN_INSETPATH), DoEnableInsetPaths);
02806             SetBoolGadgetSelected(_R(IDC_BTN_INSETPATH), m_bInsetPath);
02807         }
02808         else
02809         {
02810             SetBoolGadgetSelected(_R(IDC_BTN_INSETPATH), m_bInsetPath);
02811             EnableGadget(_R(IDC_BTN_INSETPATH), DoEnableInsetPaths);
02812         }
02813     }
02814     else
02815     {
02816         EnableGadget(_R(IDC_BTN_CONTOURDISTANCE), FALSE);
02817         EnableGadget(_R(IDC_BTN_CONTOURSTEPS), FALSE);
02818         EnableGadget(_R(IDC_CONTOUROBJECTBIASGAIN), FALSE);
02819         EnableGadget(_R(IDC_CONTOURATTRBIASGAIN), FALSE);
02820         EnableGadget(_R(IDC_CONTOURSTEPS), FALSE);
02821 PORTNOTE("other", "_R(IDC_BTN_CONTOURANTIALIAS) removed as not in the resources")
02822 #ifndef EXCLUDE_FROM_XARALX
02823         EnableGadget(_R(IDC_BTN_CONTOURANTIALIAS),FALSE);
02824 #endif
02825         EnableGadget(_R(IDC_EFFECT), FALSE);
02826         EnableGadget(_R(IDC_BTN_INSETPATH), DoEnableInsetPaths);
02827         SetBoolGadgetSelected(_R(IDC_BTN_INSETPATH), TRUE);
02828     }
02829 
02830     if (GetApplication()->FindSelection())
02831     {
02832         if (GetApplication()->FindSelection()->IsEmpty() || HasBevels)
02833         {
02834             EnableGadget(_R(IDC_CONTOURSLIDER), FALSE);
02835             EnableGadget(_R(IDC_CONTOURSLIDEREDIT), FALSE);
02836         }
02837         else
02838         {
02839             EnableGadget(_R(IDC_CONTOURSLIDER), TRUE);
02840             EnableGadget(_R(IDC_CONTOURSLIDEREDIT), TRUE);
02841         }
02842     }
02843     
02844     String_64 Str;
02845 
02846     DeleteAllValues(_R(IDC_EFFECT));
02847 
02848     Str.Load(_R(IDS_FILLTOOL_FADE));
02849     SetStringGadgetValue(_R(IDC_EFFECT),Str,FALSE, FEMENU_FADE);
02850     Str.Load(_R(IDS_FILLTOOL_RAINBOW));
02851     SetStringGadgetValue(_R(IDC_EFFECT),Str,FALSE, FEMENU_RAINBOW);
02852     Str.Load(_R(IDS_FILLTOOL_ALTRAINBOW));
02853     SetStringGadgetValue(_R(IDC_EFFECT),Str,TRUE, FEMENU_ALTRAINBOW);
02854 
02855     SetComboListLength(_R(IDC_EFFECT));
02856 
02857     if (NumSelBlends == 0)
02858     {
02859         Str = _T("");
02860         SetStringGadgetValue(_R(IDC_CONTOURSTEPS),Str);
02861     }
02862     else
02863     {
02864         if (GetBoolGadgetSelected(_R(IDC_BTN_CONTOURSTEPS)))
02865         {
02866             if (NumSteps >= 0 && !bManySteps)
02867             {
02868                 SetLongGadgetValue(_R(IDC_CONTOURSTEPS),NumSteps+1);
02869                 PaintGadgetNow(_R(IDC_CONTOURSTEPS));
02870             }
02871             else if (bManySteps)
02872             {
02873                 String_256 ManyString;
02874                 ManyString.Load(_R(IDS_MANY));
02875                 SetStringGadgetValue(_R(IDC_CONTOURSTEPS),ManyString);
02876                 PaintGadgetNow(_R(IDC_CONTOURSTEPS));
02877             }
02878         }
02879         else if (GetBoolGadgetSelected(_R(IDC_BTN_CONTOURDISTANCE)))
02880         {
02881             if (!bManyDistances)
02882             {
02883                 // do the conversion to whatever the document intends
02884                 String_256 DistString;
02885                 ConvertValueToString(DistString, StepDistance);
02886                 SetStringGadgetValue(_R(IDC_CONTOURSTEPS), DistString);
02887 
02888             }
02889             else
02890             {
02891                 String_256 ManyString;
02892                 ManyString.Load(_R(IDS_MANY));
02893                 SetStringGadgetValue(_R(IDC_CONTOURSTEPS),ManyString);
02894                 PaintGadgetNow(_R(IDC_CONTOURSTEPS));
02895             }
02896         }
02897 
02898         switch (ColBlendType)
02899         {
02900             case COLOURBLEND_FADE:          Str.Load(_R(IDS_FILLTOOL_FADE));        break;
02901             case COLOURBLEND_RAINBOW:       Str.Load(_R(IDS_FILLTOOL_RAINBOW));     break;
02902             case COLOURBLEND_ALTRAINBOW:    Str.Load(_R(IDS_FILLTOOL_ALTRAINBOW));  break;
02903             case COLOURBLEND_NONE:          Str.Load(_R(IDS_MANY));                 break;
02904 
02905             default:ERROR3("Unknown colour blend type"); Str.Load(_R(IDS_FILLTOOL_FADE)); break;
02906         }
02907         
02908         SetStringGadgetValue(_R(IDC_EFFECT),Str, FALSE, -1);
02909         
02910         // ensure immediate update
02911         PaintGadgetNow(_R(IDC_EFFECT));
02912     }
02913 
02914     // depress the profile buttons (or NOT) as the case may be ....
02915     if (NumSelBlends != GetApplication()->FindSelection()->Count())
02916     {
02917         EnableGadget (_R(IDC_CONTOUROBJECTBIASGAIN), FALSE);
02918         EnableGadget (_R(IDC_CONTOURATTRBIASGAIN), FALSE);
02919     }
02920 
02921     // depress either the edit steps or edit distance button
02922     if (m_EditBlendSteps == TRUE)
02923     {
02924         SetLongGadgetValue(_R(IDC_BTN_CONTOURSTEPS), TRUE);
02925         SetGadgetHelp(_R(IDC_CONTOURSTEPS), _R(IDBBL_CONTOURSTEPSEDITVALUE), _R(IDS_CONTOURSTEPSEDITVALUE));
02926     }
02927     else
02928     {
02929         SetLongGadgetValue(_R(IDC_BTN_CONTOURDISTANCE), TRUE);
02930         SetGadgetHelp(_R(IDC_CONTOURSTEPS), _R(IDBBL_CONTOURDISTANCEEDITVALUE), _R(IDS_CONTOURDISTANCEEDITVALUE));
02931     }
02932 
02933     // now, lets do the inner & outer buttons
02934     if (!bHasInner && bHasOuter)
02935     {
02936         EnableGadget(_R(IDC_BTN_CONTOUROUTER), TRUE);
02937         EnableGadget(_R(IDC_BTN_CONTOURINNER), TRUE);
02938         SetBoolGadgetSelected(_R(IDC_BTN_CONTOUROUTER), TRUE);
02939         SetBoolGadgetSelected(_R(IDC_BTN_CONTOURINNER), FALSE);
02940         m_bOuterIsSelected = TRUE;
02941         m_bInnerIsSelected = FALSE;
02942     }
02943     else if (bHasInner && !bHasOuter)
02944     {
02945         EnableGadget(_R(IDC_BTN_CONTOUROUTER), TRUE);
02946         EnableGadget(_R(IDC_BTN_CONTOURINNER), TRUE);
02947         SetBoolGadgetSelected(_R(IDC_BTN_CONTOUROUTER), FALSE);
02948         SetBoolGadgetSelected(_R(IDC_BTN_CONTOURINNER), TRUE);
02949         m_bOuterIsSelected = FALSE;
02950         m_bInnerIsSelected = TRUE;
02951     } 
02952     else if (bHasInner && bHasOuter)
02953     {
02954         EnableGadget(_R(IDC_BTN_CONTOUROUTER), TRUE);
02955         EnableGadget(_R(IDC_BTN_CONTOURINNER), TRUE);
02956         SetBoolGadgetSelected(_R(IDC_BTN_CONTOUROUTER), FALSE);
02957         SetBoolGadgetSelected(_R(IDC_BTN_CONTOURINNER), FALSE);
02958         m_bOuterIsSelected = FALSE;
02959         m_bInnerIsSelected = FALSE;
02960     }
02961     else if (!bHasInner && !bHasOuter)
02962     {
02963         EnableGadget(_R(IDC_BTN_CONTOUROUTER), FALSE);
02964         EnableGadget(_R(IDC_BTN_CONTOURINNER), FALSE);
02965         SetBoolGadgetSelected(_R(IDC_BTN_CONTOUROUTER), FALSE);
02966         SetBoolGadgetSelected(_R(IDC_BTN_CONTOURINNER), FALSE);
02967         m_bOuterIsSelected = FALSE;
02968         m_bInnerIsSelected = FALSE;
02969     }
02970 
02971     // Mark, do something similar here as well for _R(IDC_CONTOURJOINTYPEMITRE), ROUND, BEVEL....
02972     SetSliderValue(ThisWidth, bManyWidths); 
02973 
02974     // update the state of our join-type controls.
02975     UpdateJoinTypeControls();
02976     EnableDisableJoinTypeControls();
02977 
02978     UpdateGadgetHelp();
02979 }
02980 
02981 /********************************************************************************************
02982 
02983 >   void ContourInfoBarOp::UpdateGadgetHelp()
02984 
02985     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
02986     Created:    24/11/99
02987     Purpose:    Updates the help in this info bar
02988 
02989 *********************************************************************************************/
02990 void ContourInfoBarOp::UpdateGadgetHelp()
02991 {
02992 
02993 }
02994 
02995 
02996 
02997 /********************************************************************************************
02998 
02999 >   void ContourInfoBarOp::SetSliderValue(UINT32 Value, BOOL bMany)
03000 
03001     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
03002     Created:    24/11/99
03003     Purpose:    Sets the slider & edit controls in the info bar
03004 
03005 *********************************************************************************************/
03006 void ContourInfoBarOp::SetSliderValue(UINT32 Value, BOOL bMany)
03007 {
03008     // now do the slider
03009     SetGadgetRange(_R(IDC_CONTOURSLIDER), CONTOUR_DEPTH_MIN, CONTOUR_DEPTH_MAX);
03010 
03011     // slider goes from 0 on the right, so invert the width
03012 //  UINT32 SliderValue = (CONTOUR_DEPTH_MAX - (Value - CONTOUR_DEPTH_MIN));
03013     
03014     BOOL ok = SetSliderLogValue(Value);
03015 
03016     String_256 WidthStr;
03017     
03018     // now do the edit control
03019     if (bMany)
03020     {
03021         WidthStr.Load(_R(IDS_MANY));
03022 
03023         if (ok)
03024             ok = SetStringGadgetValue(_R(IDC_CONTOURSLIDEREDIT), WidthStr);
03025         
03026         if (ok)
03027         {
03028             PaintGadgetNow(_R(IDC_CONTOURSLIDER));
03029             PaintGadgetNow(_R(IDC_CONTOURSLIDEREDIT));
03030         }
03031     }
03032     else
03033     {
03034         // convert to whatever value we're dealing with
03035         if (ok)
03036             ok = ConvertValueToString(WidthStr, Value);
03037         
03038         if (ok)
03039             ok = SetStringGadgetValue(_R(IDC_CONTOURSLIDEREDIT), WidthStr);
03040 
03041         if (ok)
03042         {
03043             PaintGadgetNow(_R(IDC_CONTOURSLIDER));
03044             PaintGadgetNow(_R(IDC_CONTOURSLIDEREDIT));
03045         }
03046     }
03047 }
03048 
03049 
03050 
03051 /********************************************************************************************
03052 
03053 >   void ContourInfoBarOp::ChangeBitmapButtonState(CGadget GadgetID)
03054 
03055     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
03056     Created:    13/9/99
03057     Purpose:     utility used by ContourInfoBarOp::Message, if a button is depressed 
03058                 and clicked again then un-depress it  For some reason the bitmap button custom
03059                 control does not do this by itself..
03060 
03061 *********************************************************************************************/
03062 
03063 void ContourInfoBarOp::ChangeBitmapButtonState(CGadgetID GadgetID, BOOL* CurrentState)
03064 {
03065     /*BOOL test =*/  GetBoolGadgetSelected(GadgetID);
03066     if (!*CurrentState)
03067     {
03068         *CurrentState = TRUE;
03069     }
03070     else
03071     {
03072         *CurrentState = FALSE;
03073         SetLongGadgetValue(GadgetID, FALSE);
03074     }
03075     
03076 }
03077 
03078 /********************************************************************************************
03079 
03080 >   void ContourInfoBarOp::DealWithContourOuterClick()
03081 
03082     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
03083     Created:    6/9/99
03084     Purpose:    Deals with the contour outer button click message
03085 
03086 *********************************************************************************************/
03087 void ContourInfoBarOp::DealWithContourOuterClick()
03088 {
03089     // do nothing if the button is already selected
03090     if (m_bOuterIsSelected)
03091         return;
03092 
03093     // invoke the operation
03094     OpDescriptor* pOpDesc = NULL;
03095 
03096     pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_CHANGECONTOUR_OUTER);
03097 
03098     if (pOpDesc)
03099     {
03100         pOpDesc->Invoke();
03101     }                       
03102 }
03103 
03104 /********************************************************************************************
03105 
03106 >   void ContourInfoBarOp::DealWithInsetPathClick()
03107 
03108     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
03109     Created:    6/9/99
03110     Purpose:    Deals with the contour outer button click message
03111 
03112 *********************************************************************************************/
03113 void ContourInfoBarOp::DealWithInsetPathClick()
03114 {
03115     // do we have any contours at the moment ?
03116     List ContourList;
03117     BevelTools::BuildListOfSelectedNodes(&ContourList, CC_RUNTIME_CLASS(NodeContour));
03118 
03119     if (ContourList.IsEmpty())
03120     {
03121         // then just flip the flag
03122         if (m_bInsetPath)
03123         {
03124             m_bInsetPath = FALSE;
03125             SetBoolGadgetSelected(_R(IDC_BTN_INSETPATH), FALSE);
03126         }
03127         else
03128         {
03129             m_bInsetPath = TRUE;
03130             SetBoolGadgetSelected(_R(IDC_BTN_INSETPATH), TRUE);
03131         }
03132 
03133         return;
03134     }
03135 
03136     ContourList.DeleteAll();
03137         
03138     // invoke the operation
03139     OpDescriptor* pOpDesc = NULL;
03140 
03141     pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_TOGGLEINSETPATH);
03142 
03143     BOOL bSet = FALSE;
03144 
03145     if (GetBoolGadgetSelected(_R(IDC_BTN_INSETPATH)))
03146     {
03147         bSet = TRUE;
03148     }
03149     else
03150     {
03151         bSet = FALSE;
03152     }
03153 
03154     OpParam Param;
03155     Param.Param1 = bSet;
03156 
03157     if (pOpDesc)
03158     {
03159         pOpDesc->Invoke(&Param);
03160     }                       
03161 }
03162 
03163 
03164 /********************************************************************************************
03165 
03166 >   void ContourInfoBarOp::DealWithContourInnerClick()
03167 
03168     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
03169     Created:    6/9/99
03170     Purpose:    Deals with the contour inner button click message
03171 
03172 *********************************************************************************************/
03173 void ContourInfoBarOp::DealWithContourInnerClick()
03174 {
03175     // do nothing if the button is already selected
03176     if (m_bInnerIsSelected)
03177         return;
03178 
03179     // invoke the operation
03180     OpDescriptor* pOpDesc = NULL;
03181 
03182     pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_CHANGECONTOUR_INNER);
03183 
03184     if (pOpDesc)
03185     {
03186         pOpDesc->Invoke();
03187     }                       
03188 }
03189 
03190 /********************************************************************************************
03191 
03192 >   void ContourInfoBarOp::DealWithWidthChange(UINT32 Width)
03193 
03194     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
03195     Created:    6/9/99
03196     Purpose:    Deals with the contour width changes without changing the direction of
03197                 the contours
03198 
03199 *********************************************************************************************/
03200 void ContourInfoBarOp::DealWithWidthChange(UINT32 Width)
03201 {
03202     // make the list of nodes out of the selection
03203     List ContourList;
03204     BevelTools::BuildListOfSelectedNodes(&ContourList, CC_RUNTIME_CLASS(NodeContourController));
03205 
03206     if (!ContourList.IsEmpty())
03207     {   
03208         ChangeContourWidthParam Param(&ContourList, Width, TRUE);
03209         
03210         OpDescriptor * pOpDesc = OpDescriptor::FindOpDescriptor(CC_RUNTIME_CLASS(OpChangeContourWidth));
03211         
03212         if (pOpDesc)
03213         {
03214             pOpDesc->Invoke(&Param);
03215         }   
03216         
03217         ContourList.DeleteAll();
03218     }
03219     else
03220     {
03221         // build the creation list
03222         SelRange* pSel = GetApplication()->FindSelection();
03223 
03224         if (pSel)
03225         {
03226             if (!pSel->IsEmpty())
03227             {
03228                 EffectsStack* pStack = pSel->GetEffectsStack();
03229                 ENSURE(pStack, "Argh!");
03230                 Range* pSelList = pStack->GetBaseLevelRange();
03231                 ENSURE(pSelList, "Argh!");
03232 
03233                 Node* pNode = pSelList->FindFirst();
03234                 NodeListItem* pItem = NULL;
03235                 while (pNode)
03236                 {
03237                     pItem = new NodeListItem(pNode);
03238                     ContourList.AddTail(pItem);
03239                     
03240                     pNode = pSelList->FindNext(pNode);
03241                 }
03242 
03243                 CreateContourParam Param(&ContourList, 3, -Width, m_bInsetPath);
03244 
03245                 OpDescriptor * pOpDesc = OpDescriptor::FindOpDescriptor(CC_RUNTIME_CLASS(OpCreateContour));
03246         
03247                 if (pOpDesc)
03248                 {
03249                     pOpDesc->Invoke(&Param);
03250                 }   
03251         
03252                 ContourList.DeleteAll();
03253             }
03254         }
03255     }
03256 }
03257 
03258 
03260 //  OpContourNodes
03261 //
03262 // This operation is responsible for creating and editing
03263 
03264 
03265 
03266 /********************************************************************************************
03267 
03268 >   OpContourNodes::OpContourNodes()
03269 
03270     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03271     Created:    11/10/94
03272     Purpose:    Constructor. 
03273 
03274 ********************************************************************************************/
03275 
03276 OpContourNodes::OpContourNodes()
03277 {
03278     m_pBarOp = NULL;
03279 
03280     m_bHasDragged = FALSE;
03281 
03282     m_pPathList = NULL;
03283     m_NumPaths = 0;
03284     m_pSetList = NULL;
03285     m_NumSets = 0;
03286     m_pPathOuterList = NULL;
03287     m_pPathJoinTypeList = NULL;
03288     m_OriginalWidth = 0;
03289     m_StartDragWidth = 0;
03290     m_pTool = NULL;
03291     m_JoinType = MitreJoin;
03292 }
03293 
03294 /********************************************************************************************
03295 
03296 >   OpContourNodes::~OpContourNodes()
03297 
03298     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03299     Created:    11/10/94
03300     Purpose:    Destructor.
03301 
03302 ********************************************************************************************/
03303 
03304 OpContourNodes::~OpContourNodes()
03305 {
03306     if (m_pPathList)
03307     {
03308         delete [] m_pPathList;
03309     }   
03310 
03311     if (m_pSetList)
03312     {
03313         delete [] m_pSetList;
03314     }
03315 
03316     if (m_pPathOuterList)
03317     {
03318         delete [] m_pPathOuterList;
03319     }
03320 
03321     if (m_pPathJoinTypeList)
03322     {
03323         delete [] m_pPathJoinTypeList;
03324         m_pPathJoinTypeList = NULL;
03325     }
03326 }
03327 
03328 /********************************************************************************************
03329 
03330 >   BOOL OpContourNodes::SetupDragInfo()
03331     
03332     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
03333     Created:    2/12/99
03334     Inputs:     
03335     Outputs:    TRUE for success
03336     Returns:    -
03337     Purpose:    Sets up the drag information necessary to render the drag blobs
03338 
03339 ********************************************************************************************/
03340 BOOL OpContourNodes::SetupDragInfo()
03341 {
03342     Range Sel((*GetApplication()->FindSelection()));
03343 
03344     if (Sel.IsEmpty())
03345         return FALSE;
03346 
03347     RangeControl rg = Sel.GetRangeControlFlags();
03348     rg.PromoteToParent = TRUE;
03349     Sel.SetRangeControl(rg);
03350     
03351     // go through all nodes getting their inside bounding rects, and
03352     // building up the summed path
03353     CProfileBiasGain Profile;
03354 
03355     // find out how many paths we require
03356 
03357     Node * pNode = Sel.FindFirst();
03358 
03359     DocRect dr(0,0,0,0);
03360 
03361     // how many paths do we need ?
03362     m_NumPaths = 0;
03363 
03364     Node * pSubNode = NULL;
03365 
03366     // don't do needs parent nodes !!!
03367     // therefore, recurse through the subtree of every node in the selection
03368     // calling their can become a's individually
03369     m_NumSets = 0;
03370 
03371     // do we have any contour controller nodes in the selection ? if so,
03372     // we need to set a flag to tell ourselves to ignore all nodes which aren't contoured
03373     BOOL bContourNodesExist = FALSE;
03374 
03375     List ContourNodeList;
03376     BevelTools::BuildListOfSelectedNodes(&ContourNodeList, 
03377         CC_RUNTIME_CLASS(NodeContourController),
03378         TRUE);
03379 
03380     if (!ContourNodeList.IsEmpty())
03381     {
03382         bContourNodesExist = TRUE;
03383     }
03384 
03385     // do the flatness
03386     // get the current view's zoom factor
03387     DocView * pView = DocView::GetCurrent();
03388 
03389     double ZoomFactor = 1;
03390 
03391     BecomeA TestBecomeA(BECOMEA_TEST, CC_RUNTIME_CLASS(NodePath));
03392     TestBecomeA.ResetCount();
03393 
03394     if (pView)
03395     {
03396         ZoomFactor = 
03397             pView->GetScaledPixelWidth().MakeDouble() / pView->GetPixelWidth().MakeDouble();
03398     }
03399 
03400     // alter the flatness appropriately
03401     double dFlat = ContourToolFlatness;
03402 
03403     if (ZoomFactor < 1.0)   
03404         m_Flatness = (INT32)(dFlat * ZoomFactor);
03405     else
03406         m_Flatness = ContourToolFlatness;
03407 
03408     ContourNodeList.DeleteAll();
03409 
03410     Node* pLastController = NULL;
03411     Node* pParentController = NULL;
03412 
03413     // we do a depth first search of the selection, calling DoBecomeA on the appropriate nodes
03414     // we do this because
03415     // a) we need to neglect all 'needs parent' nodes, which means calling doBecomeA on
03416     // just the top level selected nodes is insufficient
03417     // we also count how many 'sets' we need
03418     // a set defines a range in the path list (i.e. between path 2 and path 6) of paths
03419     // which need to be merged together before rendering
03420     // in building this set, we need to take into account when the nodes switch from one
03421     // contour controller node to another, as this obviously indicates the start of a new set
03422     // if any contour controller nodes exist, then we must ignore all nodes which are in the
03423     // selection but not part of a contour node. This is because in this case, when dragging,
03424     // no new contour nodes are created - the existing ones widths are changed
03425 
03426     // MRH - We need to find the default or applied join type on the contour controller!
03427     m_JoinType = MitreJoin;
03428     AttrJoinType* pJoinType = NULL;
03429 
03430     while (pNode)
03431     {
03432         pSubNode = pNode->FindFirstDepthFirst();
03433 
03434         // set up the parent controller from this node
03435         if (bContourNodesExist)
03436         {
03437             pLastController = pSubNode->FindParent(CC_RUNTIME_CLASS(NodeContourController));
03438         }
03439         
03440         while (pSubNode)
03441         {
03442             if(IS_A(pSubNode,NodeContour))
03443             {
03444                 ((NodeRenderableInk *)pSubNode)->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrJoinType), (NodeAttribute **)(&pJoinType));
03445 
03446                 if (pJoinType)
03447                 {
03448                     m_JoinType = pJoinType->Value.JoinType;
03449                 }
03450             }
03451 
03452             TestBecomeA.ResetCount();
03453             if (NodeCanBeDragContoured(pSubNode, pNode, bContourNodesExist) &&
03454                 pSubNode->CanBecomeA(&TestBecomeA))
03455             {
03456                 if (!bContourNodesExist ||
03457                     pSubNode->FindParent(CC_RUNTIME_CLASS(NodeContourController)) != NULL)
03458                 {
03459                     m_NumPaths += TestBecomeA.GetCount();
03460                 }
03461 
03462                 // check for the number of sets needing to be increased - i.e. if
03463                 // the controller node of the new node is different to the last controller
03464                 if (bContourNodesExist)
03465                 {
03466                     pParentController = pSubNode->FindParent(CC_RUNTIME_CLASS(NodeContourController));
03467 
03468                     if (pParentController)
03469                     {
03470                         if (pParentController != pLastController &&
03471                             pLastController != NULL)
03472                         {
03473                             m_NumSets ++;
03474                         }
03475 
03476                         pLastController = pParentController;
03477                     }
03478                 }
03479             }
03480 
03481             pSubNode = pSubNode->FindNextDepthFirst(pNode);
03482         }
03483 
03484         m_NumSets ++;
03485 
03486         pNode = Sel.FindNext(pNode);
03487     }
03488 
03489     // increase the number of sets by 1
03490     m_NumSets++;
03491 
03492     ERROR2IF(m_NumPaths == 0, FALSE, "Nothing to contour");
03493 
03494     if (m_pPathList)
03495         delete [] m_pPathList;
03496 
03497     if (m_pSetList)
03498         delete [] m_pSetList;
03499 
03500     if (m_pPathOuterList)
03501         delete [] m_pPathOuterList;
03502 
03503     if (m_pPathJoinTypeList)
03504         delete [] m_pPathJoinTypeList;
03505 
03506     ALLOC_WITH_FAIL(m_pPathList, new Path[m_NumPaths], this);
03507 
03508     // each path has whether the object it came from was an inner or an outer contour
03509     // or not
03510     ALLOC_WITH_FAIL(m_pPathOuterList, new BOOL[m_NumPaths], this);
03511     ALLOC_WITH_FAIL(m_pPathJoinTypeList, new AttrJoinType*[m_NumPaths], this);
03512     UINT32 i;
03513     for (i = 0; i < m_NumPaths; i++)
03514     {
03515         m_pPathList[i].Initialise();
03516         m_pPathOuterList[i] = TRUE;
03517         m_pPathJoinTypeList[i] = NULL;
03518     }
03519 
03520     ALLOC_WITH_FAIL(m_pSetList, new UINT32[m_NumSets], this);
03521 
03522     // Make sure we have a valid pOurDoc as Contouring moulds with bitmap fills go bang!
03523     pOurDoc = Document::GetCurrent();
03524 
03525     // sum all paths together in the range
03526     ContourDragBecomeA MyBecomeA(BECOMEA_PASSBACK, CC_RUNTIME_CLASS(NodePath), this, FALSE,
03527         m_pPathList, m_NumPaths);
03528     MyBecomeA.ResetCount();
03529 
03530     pNode = Sel.FindFirst();
03531 
03532     // don't do needs parent nodes !!!
03533     // therefore, recurse through the subtree of every node in the selection
03534     // calling their do become a's individually
03535 
03536     NodeContourController * pControl = NULL;
03537 
03538     AttrJoinType * pAttrJoin = NULL;
03539     Node * pNodePath = NULL;
03540 
03541     UINT32 SetCount = 0;
03542 //  UINT32 ObjectCount = 0;
03543 
03544     while (pNode)
03545     {
03546         pSubNode = pNode->FindFirstDepthFirst();
03547 
03548         // set up the parent controller from this node
03549         if (bContourNodesExist)
03550         {
03551             pLastController = pSubNode->FindParent(CC_RUNTIME_CLASS(NodeContourController));
03552         }
03553 
03554         // start a new set
03555         m_pSetList[SetCount] = MyBecomeA.GetCount();
03556         SetCount ++;
03557 
03558         ERROR3IF(SetCount >= m_NumSets, "Number of sets doesn't match");        
03559 
03560         while (pSubNode)
03561         {
03562             if (NodeCanBeDragContoured(pSubNode, pNode, bContourNodesExist) &&
03563                 pSubNode->CanBecomeA(&TestBecomeA))
03564             {
03565                 // check for the number of sets needing to be increased - i.e. if
03566                 // the controller node of the new node is different to the last controller
03567                 if (bContourNodesExist)
03568                 {
03569                     pParentController = pSubNode->FindParent(CC_RUNTIME_CLASS(NodeContourController));
03570 
03571                     if (pParentController)
03572                     {
03573                         if (pParentController != pLastController &&
03574                             pLastController != NULL)
03575                         {
03576                             // start a new set
03577                             m_pSetList[SetCount] = MyBecomeA.GetCount();
03578                             SetCount ++;
03579                         }
03580 
03581                         pLastController = pParentController;
03582                     }
03583                 }
03584                 
03585                 // do the become a
03586                 if (!bContourNodesExist ||
03587                     pSubNode->FindParent(CC_RUNTIME_CLASS(NodeContourController)) != NULL)
03588                 {
03589                     // get the start path count
03590                     UINT32 StartCount = MyBecomeA.GetCount();
03591                     
03592                     // find out if we have a parent contour controller node or not
03593                     pControl = 
03594                         (NodeContourController *)pSubNode->FindParent(CC_RUNTIME_CLASS(NodeContourController));
03595 
03596                     // do the become A
03597                     if (!pSubNode->IsNodePath())
03598                     {
03599                         pSubNode->DoBecomeA(&MyBecomeA);
03600                     }
03601                     else
03602                     {
03603                         // make the node out of the path first
03604                         pNodePath = ((NodePath *)pSubNode)->MakeNodePathFromAttributes(m_Flatness);
03605 
03606                         pNodePath->DoBecomeA(&MyBecomeA);
03607 
03608                         delete pNodePath;
03609                         pNodePath = NULL;
03610                     }
03611                     
03612                     // find out if my parent controller is an inner or an outer,
03613                     // and store the value for each path which has just been created
03614                     if (pControl)
03615                     {
03616                         // find the applied join type attribute for this particular series of
03617                         if (pControl->GetContourNode())
03618                         {
03619                             pControl->GetContourNode()->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrJoinType),
03620                                 (NodeAttribute **)(&pAttrJoin));
03621                         }
03622                         
03623                         for (UINT32 x = StartCount; x < MyBecomeA.GetCount(); x++)
03624                         {
03625                             if (pControl->GetWidth() > 0)
03626                             {
03627                                 m_pPathOuterList[x] = FALSE;
03628                             }
03629                             
03630                             m_pPathJoinTypeList[x] = pAttrJoin;                 
03631                         }
03632                     }
03633                     else
03634                     {
03635                         if (pSubNode->IsAnObject())
03636                         {
03637                             // find the selected parent of this sub node (if one exists !)
03638                             Node * pSubNodeParentStep = pSubNode->FindParent();
03639                             Node * pSubNodeParent     = NULL;
03640                             pAttrJoin = NULL;
03641 
03642                             while (pSubNodeParentStep)
03643                             {
03644                                 if (pSubNodeParentStep->IsAnObject() &&
03645                                     pSubNodeParentStep->IsSelected())
03646                                 {
03647                                     pSubNodeParent = pSubNodeParentStep;
03648                                 }
03649 
03650                                 pSubNodeParentStep = pSubNodeParentStep->FindParent();
03651                             }
03652                             
03653                             if (pSubNodeParent)
03654                             {
03655                                 ((NodeRenderableInk *)pSubNodeParent)->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrJoinType),
03656                                     (NodeAttribute **)(&pAttrJoin));
03657                             }
03658                             else if (pSubNode->IsAnObject())
03659                             {
03660                                 ((NodeRenderableInk *)pSubNode)->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrJoinType),
03661                                     (NodeAttribute **)(&pAttrJoin));
03662                             }
03663 
03664                             for (UINT32 x = StartCount; x < MyBecomeA.GetCount(); x++)
03665                             {
03666                                 m_pPathJoinTypeList[x] = pAttrJoin;                 
03667                             }
03668                         }
03669                     }
03670                 }
03671             }
03672 
03673             pSubNode = pSubNode->FindNextDepthFirst(pNode);
03674         }
03675 
03676         pNode = Sel.FindNext(pNode);
03677     }
03678 
03679     // put the last set node in
03680     m_pSetList[SetCount] = MyBecomeA.GetCount();
03681 
03682     m_SelRect.lo.x = 0;
03683     m_SelRect.lo.y = 0;
03684     m_SelRect.hi.x = 0;
03685     m_SelRect.hi.y = 0;
03686 
03687     INT32 BlobSize = 0;
03688     BlobManager * pBlobMgr = GetApplication()->GetBlobManager();
03689     if (pBlobMgr)
03690         BlobSize = pBlobMgr->GetBlobSize();
03691 
03692     // calculate the rect from the paths
03693     for (i = 0 ; i < m_NumPaths; i++)
03694     {
03695         // Karim 19/04/2000
03696         // don't complain if a path has less than two coords, just ignore it.
03697         if (m_pPathList[i].GetNumCoords() < 2)
03698         {
03699 //          ERROR3("Less than 2 points in path");
03700         }
03701         else
03702         {
03703 //          m_SelRect = m_SelRect.Union(m_pPathList[i].GetBoundingRect()); MRH 19/5/00
03704             m_pPathList[i].GetTrueBoundingRect(&dr);
03705             m_SelRect = m_SelRect.Union(dr);
03706         }
03707     }
03708 
03709     // increase the size of the selection rect to take into account the blobs
03710     m_SelRect.Inflate(BlobSize);
03711     
03712     return TRUE;
03713 }
03714 
03715 
03716 
03717 /********************************************************************************************
03718 
03719 >   BOOL OpContourNodes::NodeCanBeDragContoured(Node* pSubNode,
03720                                                 Node* pNode,
03721                                                 BOOL bContourNodesExist)
03722     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
03723     Created:    10/05/2000
03724     Inputs:     pSubNode    the node which we are considering.
03725                 pNode       the node on which the drag is actually occurring.
03726                             note that this should be an ancestor of pSubNode in the tree.
03727                 bContourNodesExist  TRUE if we're editting existing contours,
03728                                     FALSE if we're creating new contours.
03729 
03730     Returns:    TRUE if pSubNode should have its outline taken into account when the user
03731                 creates or changes a contour by dragging.
03732                 FALSE if not.
03733 
03734     Purpose:    Helper function for SetupDragInfo(), which encapsulates a test for whether
03735                 a node's outline should be considered when dragging contours.
03736                 There are a number of rules for exactly which nodes should be considered;
03737                 examine the body of this function for details.
03738     Errors:     ERROR3 in DEBUG if any parameters are NULL.
03739                 *Will* bomb out with AV's if NULL parameters are passed, so don't!
03740     See also:   SetupDragInfo()
03741 
03742 ********************************************************************************************/
03743 BOOL OpContourNodes::NodeCanBeDragContoured(Node* pSubNode,
03744                                             Node* pNode,
03745                                             BOOL bContourNodesExist)
03746 {
03747     // subnode must not require a parent node to exist.
03748     BOOL    ok = !pSubNode->NeedsParent(NULL);
03749 
03750     // subnode must be a NodeRenderableInk.
03751     if (ok) ok = pSubNode->IsAnObject();
03752 
03753     // subnode must not be any compound node other than the exceptions listed here.
03754     if (ok) ok = !pSubNode->IsCompound() || pSubNode->IsABaseTextClass() ||
03755                     pSubNode->IS_KIND_OF(NodeMould) || pSubNode->IsANodeClipViewController();
03756 
03757     // subnode must not reside within a NodeMould.
03758     if (ok) ok = (pSubNode->FindParent(CC_RUNTIME_CLASS(NodeMould)) == NULL);
03759 
03760     // ClipView tests.
03761     if (ok)
03762     {
03763         // if contours do exist, then subnode must not reside within a ClipView group
03764         // which has, or whose parents have, a contour currently applied, and which is,
03765         // or lies beneath, the contour's object node.
03766         if (bContourNodesExist)
03767         {
03768             Node* pNCC = pSubNode->FindParent(CC_RUNTIME_CLASS(NodeContourController));
03769             if (pNCC != NULL)
03770             {
03771                 Node* pNodeTest = pNCC;
03772                 while (pNodeTest != pNode && pNodeTest != NULL)
03773                     pNodeTest = pNodeTest->FindParent();
03774                 if (pNodeTest != NULL)
03775                 {
03776                     // pNCC is, or is a child of, pNode.
03777                     // now we just need to find out whether pSubNode and pNCC
03778                     // have a NCVC in between.
03779                     ok = !pSubNode->IsFamily(CC_RUNTIME_CLASS(NodeClipViewController), pNCC);
03780                 }
03781             }
03782         }
03783 
03784         // if no contours exist yet, then subnode must not reside within a ClipView group
03785         // which either is, or lies beneath, the contour's object node.
03786         else
03787             ok = !pSubNode->IsFamily(CC_RUNTIME_CLASS(NodeClipViewController), pNode);
03788     }
03789 
03790     return ok;
03791 
03792 /*
03793  *  This comment holds the unexpanded version of the above tests,
03794  *  apart from the ClipView test for when contours do exist.
03795  *
03796     return !pSubNode->NeedsParent(NULL) && 
03797             (!pSubNode->IsCompound() || pSubNode->IsABaseTextClass() || 
03798             pSubNode->IS_KIND_OF(NodeMould) || pSubNode->IsANodeClipViewController()) && 
03799             pSubNode->IsAnObject() &&
03800             pSubNode->FindParent(CC_RUNTIME_CLASS(NodeMould)) == NULL &&
03801             (bContourNodesExist || 
03802             !pSubNode->IsFamily(CC_RUNTIME_CLASS(NodeClipViewController), pNode))
03803             ;
03804 */
03805 }
03806 
03807 
03808 
03809 /********************************************************************************************
03810 
03811 >   BOOL OpContourNodes::DoDrag(ContourTool * pTool, ContourInfoBarOp * pBar, DocCoord &PointerPos,
03812                                 BOOL bDragOnBlob, DocRect * pBlobRect)
03813     
03814     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
03815     Created:    2/12/99
03816     Inputs:     The info bar starting the drag, and the pointer position when the drag is 
03817                 started.
03818                 bDragOnBlob = TRUE if the drag started on a tool blob
03819                 pBlobRect   = pointer to the rect of the blob which has been clicked on
03820     Outputs:    -
03821     Returns:    -
03822     Purpose:    This starts a drag that may lead to a contour.
03823                 The DragFinished() method will do the hard work of contouring if it can be done.
03824 
03825 ********************************************************************************************/
03826 
03827 BOOL OpContourNodes::DoDrag(ContourTool * pTool, ContourInfoBarOp * pBar, DocCoord &PointerPos, BOOL bDragOnBlob,
03828                             DocRect *pBlobRect)
03829 {
03830     // snap the pointer position
03831     DocView * pView = DocView::GetCurrent();
03832     Spread * pSpread = Document::GetSelectedSpread();
03833 
03834     if (pView && pSpread)
03835     {
03836         pView->Snap(pSpread, &PointerPos, FALSE, TRUE);
03837     }
03838     
03839     // if there's no selection then forget it !
03840     if (!GetApplication()->FindSelection())
03841         return FALSE;
03842 
03844     //
03845     // This added by Karim MacDonald 17/12/1999, to fix a bug, whereby starting a contour
03846     // drag whilst a text char was sub-selected, then dragging with selector tool, caused
03847     // and error.
03848     //
03849 
03850     // if the drag was started on a blob, ensure any text in the selection is valid for this.
03851     if (bDragOnBlob)
03852         GetApplication()->FindSelection()->MakePartialSelectionWhole();
03853 
03854     // otherwise quit - can't do this drag unless it's from a blob.
03855     else
03856         return FALSE;
03857     //
03859 
03860     m_pBarOp = pBar;
03861     m_pTool  = pTool;
03862     m_bHasDragged = FALSE;
03863     m_LastPointerPos = PointerPos;
03864     m_ThisPointerPos = PointerPos;  
03865 
03866     if (pBlobRect)
03867         m_BlobRect = *pBlobRect;
03868     else
03869         m_BlobRect = DocRect(0,0,0,0);
03870     
03871     m_bDragStartedOnBlob = bDragOnBlob;
03872 
03873     // decide what the start value for the drag will be
03874     List ContourList;
03875     BevelTools::BuildListOfSelectedNodes(&ContourList, CC_RUNTIME_CLASS(NodeContourController));
03876 
03877     if (ContourList.IsEmpty())
03878     {
03879         m_OriginalWidth = 0;
03880     }
03881     else
03882     {
03883         NodeListItem * pItem = (NodeListItem *)ContourList.GetHead();
03884 
03885         m_OriginalWidth = ((NodeContourController *)(pItem->pNode))->GetWidth();
03886     }
03887 
03888     ContourList.DeleteAll();
03889 
03890     StartDrag( DRAGTYPE_AUTOSCROLL );   
03891 
03892     if (bDragOnBlob)
03893     {
03894         SetupDragInfo();
03895 
03896         if (pBlobRect)
03897         {
03898             DocCoord BlobCentre(pBlobRect->Centre());
03899     
03900             // calculate the width
03901             INT32 Width = ContourTool::CalculateContourWidth(m_SelRect, 
03902                 BlobCentre);
03903 
03904             m_StartDragWidth = Width;
03905 
03906             m_LastPointerPos = BlobCentre;
03907             m_ThisPointerPos = BlobCentre;
03908         }
03909     }
03910     
03911     // Tell the Dragging system that we need drags to happen
03912     return TRUE;
03913 }
03914 
03915 
03916 
03917 
03918 /********************************************************************************************
03919 
03920 >   void OpContourNodes::DragPointerMove( DocCoord PointerPos, ClickModifiers ClickMods, 
03921                                    Spread* pSpread, BOOL bSolidDrag)
03922     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
03923     Created:    25/11/99
03924     Inputs:     PointerPos - The current position of the mouse in Doc Coords
03925                 ClickMods  - Which key modifiers are being pressed
03926                 pSpread    - The spread that the mouse pointer is over
03927     Purpose:    Takes the pointer position and calculates the new dragged outline of the EORd
03928                 bounding box
03929     SeeAlso:    ClickModifiers
03930 
03931 ********************************************************************************************/
03932 
03933 void OpContourNodes::DragPointerMove( DocCoord PointerPos, ClickModifiers ClickMods, 
03934                                    Spread* pSpread, BOOL bSolidDrag)
03935 {
03936     ERROR3IF(!m_pBarOp, "No info bar op");
03937 
03938     // snap the pointer position
03939     DocView *pView = DocView::GetCurrent();
03940 
03941     if (pView && pSpread)
03942     {
03943         pView->Snap(pSpread, &PointerPos, FALSE, TRUE);
03944     }
03945 /*
03946     // check the pointer pos for it being over the blob
03947     AlterPointerPosToAccountForBlob(&PointerPos);
03948 */
03949     if (PointerPos == m_LastPointerPos)
03950     {
03951         return;
03952     }
03953 
03954     if (!m_bDragStartedOnBlob)
03955     {
03956         m_bHasDragged = TRUE;
03957         return;
03958     }
03959 
03960     // render the drag blobs off first
03961     if (m_bHasDragged)
03962         RenderDragBlobs(m_SelRect, pSpread, bSolidDrag);
03963 
03964     m_bHasDragged = TRUE;
03965     
03966     m_LastPointerPos = PointerPos;
03967     m_ThisPointerPos = PointerPos;  
03968 
03969     ContourTool::DisplayStatusBarHelp(_R(IDS_CONTOURDRAGHELP));
03970     
03971     INT32 Width = (ContourTool::CalculateContourWidth(m_SelRect, PointerPos) - m_StartDragWidth) 
03972         + m_OriginalWidth;
03973 
03974     RenderDragBlobs(Width, pSpread);
03975 
03976     if (Width < 0)
03977     {
03978         // outer contour
03979         m_pBarOp->SetBoolGadgetSelected(_R(IDC_BTN_CONTOUROUTER), TRUE);
03980         m_pBarOp->SetBoolGadgetSelected(_R(IDC_BTN_CONTOURINNER), FALSE);
03981     }
03982     else
03983     {
03984         // inner contour
03985         m_pBarOp->SetBoolGadgetSelected(_R(IDC_BTN_CONTOUROUTER), FALSE);
03986         m_pBarOp->SetBoolGadgetSelected(_R(IDC_BTN_CONTOURINNER), TRUE);
03987     }
03988 
03989     m_pBarOp->PaintGadgetNow(_R(IDC_BTN_CONTOURINNER));
03990     m_pBarOp->PaintGadgetNow(_R(IDC_BTN_CONTOUROUTER));
03991 
03992     m_pBarOp->SetSliderValue(abs(Width), FALSE);
03993 }
03994 
03995 
03996 
03997 /********************************************************************************************
03998 
03999 >   void OpContourNodes::DragFinished( DocCoord PointerPos, ClickModifiers ClickMods, 
04000                                 Spread* pSpread, BOOL Success, BOOL bSolidDrag)
04001 
04002     
04003     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
04004     Created:    11/10/94
04005     Inputs:     PointerPos - The position of the mouse at the end of the drag
04006                 ClickMods - the key modifiers being pressed
04007                 pSpread - The spread that the drag finished on
04008                 Success - TRUE if the drag was terminated properly, FALSE if it
04009                 was ended with the escape key being pressed
04010     Purpose:    Ends the drag.
04011                 Either creates a new grid or resizes GridClicked depending on the state of affairs
04012                 when the drag started
04013     SeeAlso:    ClickModifiers
04014 
04015 ********************************************************************************************/
04016 
04017 void OpContourNodes::DragFinished( DocCoord PointerPos, ClickModifiers ClickMods, 
04018                                 Spread* pSpread, BOOL Success, BOOL bSolidDrag)
04019 {
04020     // snap the pointer position
04021     DocView * pView = DocView::GetCurrent();
04022     
04023     if (pView && pSpread)
04024     {
04025         pView->Snap(pSpread, &PointerPos, FALSE, TRUE);
04026     }
04027     
04028     ContourTool * pTool = (ContourTool *)Tool::GetCurrent();
04029     
04030     // End the drag and the op
04031 //  BlobManager * pBlobMgr = GetApplication()->GetBlobManager();
04032 
04033     EndDrag();
04034 
04035     // added 20/12/1999 by Karim MacDonald.
04036     // if the drag did not start on a blob, we quit now.
04037     if (!m_bDragStartedOnBlob)
04038         return;
04039 
04040     // calculate the contour width & set the infobar
04041     INT32 Width = (ContourTool::CalculateContourWidth(m_SelRect, PointerPos) - m_StartDragWidth) 
04042         + m_OriginalWidth;
04043 
04044     // if we've not dragged, then do nothing
04045     if (!m_bHasDragged)
04046         return;
04047 
04048     if (m_pBarOp)
04049         m_pBarOp->UpdateState();
04050 
04051     m_bHasDragged = FALSE;
04052 
04053     if (!pSpread)
04054         pSpread = Document::GetSelectedSpread();
04055 
04056     // render the drag blobs off
04057     RenderDragBlobs(m_SelRect, pSpread, bSolidDrag);    
04058 
04059     // create the contour, or change its width
04060     if (Success)
04061     {
04062         if (pTool)
04063         {
04064             pTool->RenderToolBlobs(pSpread, NULL);
04065             pTool->SetBlobRendering(FALSE);
04066         }
04067         
04068         List ContourList;
04069         BevelTools::BuildListOfSelectedNodes(&ContourList, CC_RUNTIME_CLASS(NodeContourController));
04070 
04071         if (ContourList.IsEmpty())
04072         {
04073             // make a list of the range, and then invoke the op
04074             EffectsStack* pStack = GetApplication()->FindSelection()->GetEffectsStack();
04075             ENSURE(pStack, "Argh!");
04076             Range* pSelList = pStack->GetBaseLevelRange();
04077             ENSURE(pSelList, "Argh!");
04078 
04079             Node* pNode = pSelList->FindFirst();
04080             NodeListItem* pItem = NULL;
04081             while (pNode)
04082             {
04083                 pItem = new NodeListItem(pNode);
04084                 ContourList.AddTail(pItem);
04085                 
04086                 pNode = pSelList->FindNext(pNode);
04087             }
04088 
04089             CreateContourParam Param(&ContourList, 3, Width, m_pBarOp->GetInsetPathFlag());
04090             
04091             OpDescriptor* pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_CREATECONTOUR);
04092 
04093             if (pOpDesc)
04094                 pOpDesc->Invoke(&Param);
04095 
04096             ContourList.DeleteAll();
04097             delete pSelList;
04098         }
04099         else
04100         {
04101             ChangeContourWidthParam Param(&ContourList, Width, FALSE);
04102 
04103             OpDescriptor* pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_CHANGECONTOURWIDTH);
04104 
04105             if (pOpDesc)
04106                 pOpDesc->Invoke(&Param);
04107 
04108             ContourList.DeleteAll();
04109         }
04110 
04111         // put the tool blobs back on
04112         if (pTool)
04113         {
04114             pTool->SetBlobRendering(TRUE);
04115             pTool->SetupToolBlobs();
04116             pTool->RenderToolBlobs(pSpread, NULL);
04117         }
04118     }
04119 
04120     delete [] m_pPathList;
04121     m_pPathList = NULL;
04122 
04123     delete [] m_pSetList;
04124     m_pSetList = NULL;
04125 
04126     delete [] m_pPathOuterList;
04127     m_pPathOuterList = NULL;
04128 
04129     delete [] m_pPathJoinTypeList;
04130     m_pPathJoinTypeList = NULL;
04131 }
04132 
04133 /********************************************************************************************
04134 
04135 >   void OpContourNodes::AlterPointerPosToAccountForBlob(DocCoord * pPoint)
04136 
04137     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
04138     Created:    11/10/94
04139     Inputs:     The point to change
04140     Purpose:    Ensures that dragging back onto a blob returns you to the original
04141                 contour value
04142 
04143 ********************************************************************************************/
04144 void OpContourNodes::AlterPointerPosToAccountForBlob(DocCoord * pPoint)
04145 {
04146     DocCoord PPos;
04147 
04148     DocCoord Offset;
04149 
04150     double Scale = 0;
04151 
04152     BlobManager * pBlobMgr = GetApplication()->GetBlobManager();
04153 
04154     double BlobSize = 0;
04155 
04156     if (pBlobMgr)
04157         BlobSize = pBlobMgr->GetBlobSize()/2;
04158     else
04159         return;
04160     
04161     if (m_BlobRect.IsValid())
04162     {
04163         DocCoord Centre = m_BlobRect.Centre();
04164             
04165         if (m_BlobRect.ContainsCoord(*pPoint))
04166         {
04167             
04168             pPoint->x = Centre.x;
04169             pPoint->y = Centre.y;
04170         }
04171         else
04172         {
04173             // move the pointer appropriately so that the blob is always
04174             // the same value
04175             
04176             // first find the point which is the intersection between the line from
04177             // the centre of the blob to the point on the outside of the blob
04178             PPos.x = pPoint->x - Centre.x;
04179             PPos.y = pPoint->y - Centre.y;
04180 
04181             if (abs(PPos.x) > abs(PPos.y))
04182             {
04183                 Scale = ((double)abs(PPos.y)) / ((double)abs(PPos.x));
04184                 Scale *= BlobSize;
04185 
04186                 if (PPos.x > 0)
04187                 {
04188                     Offset.x = (INT32)BlobSize;
04189                 }
04190                 else
04191                 {
04192                     Offset.x = (INT32)-BlobSize;
04193                 }
04194 
04195                 if (PPos.y > 0)
04196                 {
04197                     Offset.y = (INT32)Scale;
04198                 }
04199                 else
04200                 {
04201                     Offset.y = (INT32)-Scale;
04202                 }
04203             }
04204             else
04205             {
04206                 Scale = ((double)PPos.x) / ((double)PPos.y);
04207                 Scale *= BlobSize;
04208 
04209                 if (PPos.y > 0)
04210                 {
04211                     Offset.y = (INT32)BlobSize;
04212                 }
04213                 else
04214                 {
04215                     Offset.y = (INT32)-BlobSize;
04216                 }
04217 
04218                 if (PPos.x > 0)
04219                 {
04220                     Offset.x = (INT32)Scale;
04221                 }
04222                 else
04223                 {
04224                     Offset.x = (INT32)-Scale;
04225                 }
04226             }
04227 
04228             pPoint->x -= Offset.x;
04229             pPoint->y -= Offset.y;
04230         }
04231     }
04232 }
04233 
04234 
04235 /********************************************************************************************
04236 
04237 >   void OpContourNodes::RenderDragBlobs(MILLIPOINT Width,Spread* pSpread, BOOL bKeepDirections)
04238 
04239     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com> H - rewritten Dave Mc code 06/06/00
04240     Created:    11/10/94
04241     Inputs:     Width   - The width of the contour to render the drag blobs of
04242                 pSpread - The spread that the drawing will happen on
04243                 bKeepDirections - Whether to maintian each set's direction (either inner
04244                                   or outer) or to render the blobs with the signed value
04245                                   passed in.
04246     Purpose:    Draws an EORed outline distance width away
04247 
04248 ********************************************************************************************/
04249 void OpContourNodes::RenderDragBlobs(MILLIPOINT Width,Spread* pSpread, BOOL bKeepDirections)
04250 {
04251     bool Outer = false;
04252     if(Width < 0)
04253         Outer = true;
04254 
04255     if (bKeepDirections)
04256         Width = abs(Width);
04257 
04258     if (/*m_pPathJoinTypeList == NULL || */m_pPathList == NULL || m_pSetList == NULL)
04259     {
04260         ERROR3("No list defined");
04261         return;
04262     }
04263 
04264     // Allocate Two Path Arrays to hold the outline information
04265     // SetPathList holds the all the paths inside compound nodes
04266     Path * pSetPathList = NULL;
04267     ALLOC_WITH_FAIL(pSetPathList, new Path[m_NumSets-1], this);
04268 
04269     // DrawPAthList holds the path that has been expanded by the width
04270     Path * pDrawPathList = NULL;
04271     ALLOC_WITH_FAIL(pDrawPathList, new Path[m_NumPaths], this);
04272 
04273     // Two local paths to hold intermediate path steps
04274     Path CopyPath;
04275     Path TempPath;
04276     CopyPath.Initialise();
04277     TempPath.Initialise();
04278 
04279     DocRect DrawBounds(0,0,0,0);
04280     DocRect dri(0,0,0,0);
04281     DocRect drj(0,0,0,0);
04282     UINT32 j = 0;
04283 
04284     // Set the flatness to be 1500 (2 pixels) at 96dpi
04285     INT32 Flatness = 1500;
04286     DocView * pView = DocView::GetCurrent();
04287     if (pView)
04288     {
04289         double ZoomFactor = pView->GetScaledPixelWidth().MakeDouble() / pView->GetPixelWidth().MakeDouble();
04290         Flatness = (INT32)(ZoomFactor * Flatness);
04291         if(Flatness <= 1)
04292         {
04293             Flatness = 2;
04294         }
04295     }
04296 
04297     //  Get the current Join Style
04298     // do the default join type for the document
04299 //  AttrJoinType * pDefaultJoinType = NULL;
04300     JointType DefaultJT = m_JoinType;
04301     JoinStyles JoinS = (DefaultJT==MitreJoin) ? JOIN_MITER : (DefaultJT==RoundJoin) ? JOIN_ROUND : JOIN_BEVEL;
04302     UINT32 i;
04303     // Loop trough the path list expanding them and saving the result into the DrawPathList
04304     for (i = 0 ; i < m_NumPaths; i++)
04305     {
04306         pDrawPathList[i].Initialise();
04307 
04308         // Make sure we use the correct function by checking to see if the path is closed or not!
04309         CapStyles CapS = CAPS_ROUND;
04310         BOOL IsPathClosed = m_pPathList[i].IsClosed();
04311         m_pPathList[i].InitializeContourValues(abs(Width)*2,JoinS,Outer,Flatness,IsPathClosed,IsPathClosed,CapS);
04312         m_pPathList[i].GetContourForStep(&pDrawPathList[i],1.0);
04313     }
04314 
04315     // merge all the paths in each set and keep an overall bounding rect going
04316     for (i = 0 ; i < m_NumSets - 1; i++)
04317     {
04318         pSetPathList[i].Initialise();
04319         pSetPathList[i].GetTrueBoundingRect(&dri);
04320         
04321         for (j = m_pSetList[i]; j < m_pSetList[i+1]; j++)
04322         {
04323             pDrawPathList[j].GetTrueBoundingRect(&drj);
04324             pSetPathList[i].MergeTwoPaths(pDrawPathList[j]);
04325         }
04326 
04327         if(m_NumSets > 1)
04328         {
04329             CopyPath.ClearPath(FALSE);
04330             CopyPath.CloneFrom(pSetPathList[i]);
04331             pSetPathList[i].ClearPath(FALSE);
04332             TempPath.ClipPathToPath(CopyPath,&pSetPathList[i],7|(1<<4),50,m_Flatness,m_Flatness);
04333         }
04334 
04335         pSetPathList[i].IsFilled = TRUE;
04336         pSetPathList[i].IsStroked = FALSE;
04337 
04338         DrawBounds = DrawBounds.Union(dri);
04339     }
04340 
04341     // increase the bounds slightly - DaveMc ?
04342     DrawBounds.Inflate(750);
04343 
04344     // Get the render region required
04345     RenderRegion* pRegion = DocView::RenderOnTop( NULL, pSpread, UnclippedEOR );
04346 
04347     while ( pRegion != NULL )
04348     {
04349         // Set the line colour and Draw the rect
04350         pRegion->SaveContext();
04351 
04352         pRegion->SetLineWidth(0);
04353         
04354         pRegion->SetWindingRule(PositiveWinding);
04355         
04356         pRegion->SetLineColour(COLOUR_RED);
04357         pRegion->SetFillColour(COLOUR_NONE);
04358 
04359         for (i = 0 ; i < m_NumSets-1; i++)
04360         {
04361             if (pSetPathList[i].GetNumCoords()>0)
04362                 pRegion->DrawPath(&(pSetPathList[i]));
04363         }
04364         
04365         // Get the Next render region
04366         pRegion->RestoreContext();
04367         pRegion = DocView::GetNextOnTop( NULL );
04368     }
04369 
04370     // tidy up and finish!
04371     delete [] pSetPathList;
04372     delete [] pDrawPathList;
04373 }
04374 
04375 
04376 /********************************************************************************************
04377 
04378 >   void OpContourNodes::RenderDragBlobs(DocRect Rect,Spread* pSpread, BOOL bSolidDrag)
04379 
04380     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
04381     Created:    11/10/94
04382     Inputs:     Rect    - The region that needs the blobs to be drawn
04383                 pSpread - The spread that the drawing will happen on
04384     Purpose:    Draws an EORed rectangle defined by AnchorPoint and DragPoint
04385 
04386 ********************************************************************************************/
04387 
04388 void OpContourNodes::RenderDragBlobs(DocRect Rect,Spread* pSpread, BOOL bSolidDrag)
04389 {
04390     if (m_pPathList == NULL || m_NumPaths == 0)
04391     {
04392         return;
04393     }
04394     
04395     // set up the bounds
04396     INT32 Width = (ContourTool::CalculateContourWidth(m_SelRect, m_ThisPointerPos) - m_StartDragWidth) 
04397         + m_OriginalWidth;
04398 
04399     RenderDragBlobs(Width, pSpread);    
04400 }
04401 
04402 /********************************************************************************************
04403 
04404 >   BOOL OpContourNodes::Declare()
04405 
04406     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
04407     Created:    11/10/94
04408     Returns:    TRUE if all went OK, FALSE otherwise
04409     Purpose:    Adds the operation to the list of all known operations
04410 
04411 ********************************************************************************************/
04412 
04413 BOOL OpContourNodes::Declare()
04414 {
04415     return (RegisterOpDescriptor(
04416                                 0, 
04417                                 _R(IDS_CONTOUR_TOOL),
04418                                 CC_RUNTIME_CLASS(OpContourNodes), 
04419                                 OPTOKEN_CONTOURNODES,
04420                                 OpContourNodes::GetState,
04421                                 0,          /* help ID */
04422                                 _R(IDBBL_NOOP), /* bubble ID */
04423                                 0           /* bitmap ID */
04424                                 ));
04425 }
04426 
04427 
04428 /********************************************************************************************
04429 
04430 >   static OpState OpContourNodes::GetState(String_256* Description, OpDescriptor*)
04431 
04432     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
04433     Created:    11/10/94
04434     Outputs:    Description - GetState fills this string with an approriate description
04435                 of the current state of the push tool
04436     Returns:    The state of the operation, so that menu items (ticks and greying can be
04437                 done properly
04438     Purpose:    Find out the state of the operation at the specific time
04439 
04440 ********************************************************************************************/
04441 
04442 OpState OpContourNodes::GetState(String_256* Description, OpDescriptor*)
04443 {
04444     OpState State;
04445     
04446     return State;
04447 }
04448 
04449 /********************************************************************************************
04450 
04451 >   virtual void OpContourNodes::GetOpName(String_256* OpName) 
04452 
04453     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
04454     Created:    11/10/94
04455     Inputs:     OpName = ptr to str to place op name in
04456     Outputs:    The undo string for the operation
04457     Returns:    
04458     Purpose:    The GetOpName fn is overridden so that we return back a description 
04459                 appropriate to the type of attribute that the operation applies. 
04460     Errors:     -
04461     SeeAlso:    -
04462 
04463 ********************************************************************************************/
04464 
04465 void OpContourNodes::GetOpName(String_256* OpName) 
04466 { 
04467     *OpName = String_256(_R(IDS_CREATECONTOUROP));
04468 }  
04469 
04470 /********************************************************************************************
04471 
04472 >   BOOL ContourDragBecomeA::PassBack(NodeRenderableInk* pNewNode,
04473         NodeRenderableInk* pCreatedByNode,
04474         CCAttrMap* pAttrMap);
04475 
04476     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
04477     Created:    2/12/99
04478     Inputs:     See base class
04479     Outputs:    TRUE for success
04480     Returns:    
04481     Purpose:    Passback function used to sum all paths together
04482     Errors:     -
04483     SeeAlso:    -
04484 
04485 ********************************************************************************************/
04486 BOOL ContourDragBecomeA::PassBack(NodeRenderableInk* pNewNode,
04487         NodeRenderableInk* pCreatedByNode,
04488         CCAttrMap* pAttrMap)
04489 {
04490     if (m_Count >= m_NumPaths)
04491     {
04492         ERROR3("Number of paths doesn't match");
04493         return FALSE;
04494     }
04495     
04496     if (!pNewNode || !pNewNode->IsNodePath() || ((NodePath *)pNewNode)->InkPath.GetNumCoords() < 2)
04497         return FALSE;
04498 
04499     if (pCreatedByNode)
04500     {
04501         // only do non-needs parent nodes
04502         if (!pCreatedByNode->NeedsParent(NULL))
04503         {
04504             BOOL PathIsClosed = ((NodePath*)pNewNode)->InkPath.IsClosed();
04505             // check for an open path
04506             if ( !PathIsClosed )
04507             {       
04508                 // make a new node path
04509                 NodePath * pClosedPathNode = 
04510                     ((NodePath *)pNewNode)->MakeNodePathFromAttributes(ContourToolFlatness, pAttrMap, FALSE, m_IncludeLineWidths);
04511 
04512                 m_pPathList[m_Count].ClearPath(FALSE);
04513                 m_pPathList[m_Count].CloneFrom(((NodePath *)pNewNode)->InkPath);
04514 
04515                 // delete the node
04516                 pNewNode->DeleteChildren(pNewNode->FindFirstChild());
04517                 
04518                 delete pNewNode;
04519                 pNewNode = pClosedPathNode;
04520             }
04521 
04522             NodePath* pStrokedPath = pCreatedByNode->GetVariableWidthStrokePath();
04523             BOOL bStroke = FALSE;
04524             
04525             if (pStrokedPath != NULL)
04526             {
04527                 delete pNewNode;
04528                 pNewNode = pStrokedPath;
04529                 pStrokedPath = NULL;
04530                 bStroke = TRUE;
04531             }
04532 
04533             // Karim 30/06/2000
04534             // the path-clipping code cannot cope with a straight line (two handles),
04535             // so we need to explicitly check for this case.
04536             if ( ((NodePath *)pNewNode)->InkPath.GetNumCoords() == 2 )
04537             {
04538                 m_pPathList[m_Count].ClearPath(FALSE);
04539                 m_pPathList[m_Count].CloneFrom(((NodePath *)pNewNode)->InkPath);
04540             }
04541             else
04542             {
04543                 // clip the path to ensure its validity
04544                 if (!bStroke)
04545                 {
04546                     Path BlankPath;
04547                     BlankPath.Initialise();
04548                     BlankPath.ClipPathToPath(((NodePath *)pNewNode)->InkPath, &(m_pPathList[m_Count]), 3);
04549                 }
04550             }
04551 
04552             // has the clipping failed ? if so, just clone.
04553             if (m_pPathList[m_Count].GetNumCoords() < 2)
04554             {
04555                 m_pPathList[m_Count].ClearPath(FALSE);
04556                 m_pPathList[m_Count].CloneFrom(((NodePath *)pNewNode)->InkPath);
04557             }
04558             
04559             // take the path from the node, and add it to my path
04560             m_pPathList[m_Count++].TryToClose();
04561         }
04562     }
04563     
04564     // delete the node
04565     Node *pChild = pNewNode->FindFirstChild();
04566     Node *pNextNode = NULL;
04567 
04568     while (pChild)
04569     {
04570         pNextNode = pChild->FindNext();
04571         pChild->UnlinkNodeFromTree();
04572         
04573         delete pChild;
04574         pChild = pNextNode;
04575     }
04576     
04577     if (pAttrMap)
04578     {
04579         pAttrMap->DeleteAttributes();
04580         delete pAttrMap;
04581     }
04582 
04583     if(pNewNode)
04584         delete pNewNode;
04585     
04586     return TRUE;
04587 }
04588 
04589 
04590 /*
04591 IMPLEMENT_SIMPLE_MODULE( ContourModule, MODULEID_BLEND, ContourTool,
04592                             "Contour Tool", "To contour objects", "DavidMc" );
04593                             */

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